| /* Copyright (c) 2014 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| /* UART module for Chrome EC */ |
| |
| #include "clock.h" |
| #include "common.h" |
| #include "console.h" |
| #include "gpio.h" |
| #include "lpc.h" |
| #include "registers.h" |
| #include "clock_chip.h" |
| #include "system.h" |
| #include "task.h" |
| #include "uart.h" |
| #include "util.h" |
| |
| static int init_done; |
| |
| int uart_init_done(void) |
| { |
| return init_done; |
| } |
| |
| void uart_tx_start(void) |
| { |
| if (IS_BIT_SET(NPCX_WKEN(1, 1), 0)) { |
| /* disable MIWU*/ |
| CLEAR_BIT(NPCX_WKEN(1, 1), 0); |
| /* go back to original setting */ |
| task_enable_irq(NPCX_IRQ_WKINTB_1); |
| /* Go back CR_SIN*/ |
| SET_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); |
| /* enable uart again from MIWU mode */ |
| task_enable_irq(NPCX_IRQ_UART); |
| } |
| |
| /* If interrupt is already enabled, nothing to do */ |
| if (NPCX_UICTRL & 0x20) |
| return; |
| |
| /* Do not allow deep sleep while transmit in progress */ |
| disable_sleep(SLEEP_MASK_UART); |
| |
| /* |
| * Re-enable the transmit interrupt, then forcibly trigger the |
| * interrupt. This works around a hardware problem with the |
| * UART where the FIFO only triggers the interrupt when its |
| * threshold is _crossed_, not just met. |
| */ |
| NPCX_UICTRL |= 0x20; |
| |
| task_trigger_irq(NPCX_IRQ_UART); |
| } |
| |
| void uart_tx_stop(void) /* Disable TX interrupt */ |
| { |
| NPCX_UICTRL &= ~0x20; |
| |
| /* Re-allow deep sleep */ |
| enable_sleep(SLEEP_MASK_UART); |
| } |
| |
| void uart_tx_flush(void) |
| { |
| /* Wait for transmit FIFO empty */ |
| while (!(NPCX_UICTRL & 0x01)) |
| ; |
| } |
| |
| int uart_tx_ready(void) |
| { |
| return NPCX_UICTRL & 0x01; /*if TX FIFO is empty return 1*/ |
| } |
| |
| int uart_tx_in_progress(void) |
| { |
| /* Transmit is in progress if the TX busy bit is set. */ |
| return NPCX_USTAT & 0x40; /*BUSY bit , if busy return 1*/ |
| } |
| |
| int uart_rx_available(void) |
| { |
| uint8_t ctrl = NPCX_UICTRL; |
| #ifdef CONFIG_LOW_POWER_IDLE |
| /* |
| * Activity seen on UART RX pin while UART was disabled for deep sleep. |
| * The console won't see that character because the UART is disabled, |
| * so we need to inform the clock module of UART activity ourselves. |
| */ |
| if (ctrl & 0x02) |
| clock_refresh_console_in_use(); |
| #endif |
| return ctrl & 0x02; /* If RX FIFO is empty return '0'*/ |
| } |
| |
| void uart_write_char(char c) |
| { |
| /* Wait for space in transmit FIFO. */ |
| while (!uart_tx_ready()) |
| ; |
| |
| NPCX_UTBUF = c; |
| } |
| |
| int uart_read_char(void) |
| { |
| return NPCX_URBUF; |
| } |
| |
| static void uart_clear_rx_fifo(int channel) |
| { |
| int scratch __attribute__ ((unused)); |
| if (channel == 0) { /* suppose '0' is EC UART*/ |
| /*if '1' that mean have a RX data on the FIFO register*/ |
| while ((NPCX_UICTRL & 0x02)) |
| scratch = NPCX_URBUF; |
| } |
| } |
| |
| void uart_disable_interrupt(void) |
| { |
| task_disable_irq(NPCX_IRQ_UART); |
| } |
| |
| void uart_enable_interrupt(void) |
| { |
| task_enable_irq(NPCX_IRQ_UART); |
| } |
| |
| /** |
| * Interrupt handler for UART0 |
| */ |
| void uart_ec_interrupt(void) |
| { |
| /* Read input FIFO until empty, then fill output FIFO */ |
| uart_process_input(); |
| uart_process_output(); |
| } |
| DECLARE_IRQ(NPCX_IRQ_UART, uart_ec_interrupt, 1); |
| |
| |
| static void uart_config(void) |
| { |
| uint32_t div, opt_dev, min_deviation, clk, calc_baudrate, deviation; |
| uint8_t prescalar, opt_prescalar, i; |
| /* Enable the port */ |
| /* Configure pins from GPIOs to CR_UART */ |
| gpio_config_module(MODULE_UART, 1); |
| |
| /* Calculated UART baudrate , clock source from APB2 */ |
| opt_prescalar = opt_dev = 0; |
| prescalar = 10; |
| min_deviation = 0xFFFFFFFF; |
| clk = clock_get_apb2_freq(); |
| for (i = 1; i < 31; i++) { |
| div = (clk * 10) / (16 * CONFIG_UART_BAUD_RATE * prescalar); |
| if (div != 0) { |
| calc_baudrate = (clk * 10) / (16 * div * prescalar); |
| deviation = (calc_baudrate > CONFIG_UART_BAUD_RATE) ? |
| (calc_baudrate - CONFIG_UART_BAUD_RATE) : |
| (CONFIG_UART_BAUD_RATE - calc_baudrate); |
| if (deviation < min_deviation) { |
| min_deviation = deviation; |
| opt_prescalar = i; |
| opt_dev = div; |
| } |
| } |
| prescalar += 5; |
| } |
| opt_dev--; |
| NPCX_UPSR = ((opt_prescalar<<3) & 0xF8) | ((opt_dev >> 8) & 0x7); |
| NPCX_UBAUD = (uint8_t)opt_dev; |
| /* |
| * 8-N-1, FIFO enabled. Must be done after setting |
| * the divisor for the new divisor to take effect. |
| */ |
| NPCX_UFRS = 0x00; |
| NPCX_UICTRL = 0x40; /* receive int enable only */ |
| } |
| |
| void uart_init(void) |
| { |
| uint32_t mask = 0; |
| |
| /* |
| * Enable UART0 in run, sleep, and deep sleep modes. Enable the Host |
| * UART in run and sleep modes. |
| */ |
| mask = 0x10; /* bit 4 */ |
| clock_enable_peripheral(CGC_OFFSET_UART, mask, CGC_MODE_ALL); |
| |
| /* Set pin-mask for UART */ |
| SET_BIT(NPCX_DEVALT(0x0A), NPCX_DEVALTA_UART_SL); |
| gpio_config_module(MODULE_UART, 1); |
| |
| /* Configure UARTs (identically) */ |
| uart_config(); |
| |
| /* |
| * Enable interrupts for UART0 only. Host UART will have to wait |
| * until the LPC bus is initialized. |
| */ |
| uart_clear_rx_fifo(0); |
| task_enable_irq(NPCX_IRQ_UART); |
| |
| init_done = 1; |
| } |