diff options
-rw-r--r-- | board/cr50/board.h | 7 | ||||
-rw-r--r-- | chip/g/build.mk | 2 | ||||
-rw-r--r-- | chip/g/clock.c | 2 | ||||
-rw-r--r-- | chip/g/uart.c | 81 | ||||
-rw-r--r-- | chip/g/uartn.c | 137 | ||||
-rw-r--r-- | chip/g/uartn.h | 77 |
6 files changed, 244 insertions, 62 deletions
diff --git a/board/cr50/board.h b/board/cr50/board.h index d7cface787..9fde91dca2 100644 --- a/board/cr50/board.h +++ b/board/cr50/board.h @@ -85,6 +85,13 @@ enum usb_strings { #define USB_EP_HID 2 #define USB_EP_COUNT 3 +/* UART indexes (use define rather than enum to expand them) */ +#define UART_CR50 0 +#define UART_AP 1 +#define UART_EC 2 + +#define UARTN UART_CR50 + /* * This would be a low hanging fruit if there is a need to reduce memory * footprint. Having a large buffer helps not to drop debug outputs generated diff --git a/chip/g/build.mk b/chip/g/build.mk index d6df5d228c..a98f2e3e8f 100644 --- a/chip/g/build.mk +++ b/chip/g/build.mk @@ -23,6 +23,7 @@ ifeq ($(CONFIG_POLLING_UART),y) chip-y += polling_uart.o else chip-y += uart.o +chip-y += uartn.o endif chip-$(CONFIG_DCRYPTO)+= dcrypto/aes.o @@ -66,6 +67,7 @@ custom-ro_objs-y += chip/g/pmu.o custom-ro_objs-y += chip/g/system.o custom-ro_objs-y += chip/g/trng.o custom-ro_objs-y += chip/g/uart.o +custom-ro_objs-y += chip/g/uartn.o custom-ro_objs-y += common/printf.o custom-ro_objs-y += common/util.o custom-ro_objs-y += core/cortex-m/init.o diff --git a/chip/g/clock.c b/chip/g/clock.c index 98d26c5fdb..8903b9b5b0 100644 --- a/chip/g/clock.c +++ b/chip/g/clock.c @@ -23,6 +23,8 @@ void clock_enable_module(enum module_id module, int enable) switch (module) { case MODULE_UART: clock_func(PERIPH_UART0); + clock_func(PERIPH_UART1); + clock_func(PERIPH_UART2); break; case MODULE_I2C: clock_func(PERIPH_I2C0); diff --git a/chip/g/uart.c b/chip/g/uart.c index f7219d5d4c..0e1534f1db 100644 --- a/chip/g/uart.c +++ b/chip/g/uart.c @@ -10,12 +10,17 @@ #include "system.h" #include "task.h" #include "uart.h" +#include "uartn.h" #include "util.h" static int done_uart_init_yet; #define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \ defined(SECTION_IS_RO))) +#ifndef UARTN +#define UARTN 0 +#endif + int uart_init_done(void) { return done_uart_init_yet; @@ -23,89 +28,56 @@ int uart_init_done(void) void uart_tx_start(void) { - if (!uart_init_done()) - return; - - /* If interrupt is already enabled, nothing to do */ - if (GR_UART_ICTRL(0) & GC_UART_ICTRL_TX_MASK) - 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. - */ - /* TODO(crosbug.com/p/33819): Do we need this hack here? Find out. */ - REG_WRITE_MLV(GR_UART_ICTRL(0), GC_UART_ICTRL_TX_MASK, - GC_UART_ICTRL_TX_LSB, 1); - task_trigger_irq(GC_IRQNUM_UART0_TXINT); + uartn_tx_start(UARTN); } void uart_tx_stop(void) { - /* Disable the TX interrupt */ - REG_WRITE_MLV(GR_UART_ICTRL(0), GC_UART_ICTRL_TX_MASK, - GC_UART_ICTRL_TX_LSB, 0); + uartn_tx_stop(UARTN); - /* Re-allow deep sleep */ - enable_sleep(SLEEP_MASK_UART); } int uart_tx_in_progress(void) { - /* Transmit is in progress unless the TX FIFO is empty and idle. */ - return !(GR_UART_STATE(0) & (GC_UART_STATE_TXIDLE_MASK | - GC_UART_STATE_TXEMPTY_MASK)); + return uartn_tx_in_progress(UARTN); } void uart_tx_flush(void) { - /* Wait until TX FIFO is idle. */ - while (uart_tx_in_progress()) - ; + uartn_tx_flush(UARTN); } int uart_tx_ready(void) { /* True if the TX buffer is not completely full */ - return !(GR_UART_STATE(0) & GC_UART_STATE_TX_MASK); + return uartn_tx_ready(UARTN); } int uart_rx_available(void) { /* True if the RX buffer is not completely empty. */ - return !(GR_UART_STATE(0) & GC_UART_STATE_RXEMPTY_MASK); + return uartn_rx_available(UARTN); } void uart_write_char(char c) { - /* Wait for space in transmit FIFO. */ - while (!uart_tx_ready()) - ; - - GR_UART_WDATA(0) = c; + uartn_write_char(UARTN, c); } int uart_read_char(void) { - return GR_UART_RDATA(0); + return uartn_read_char(UARTN); } #if USE_UART_INTERRUPTS void uart_disable_interrupt(void) { - task_disable_irq(GC_IRQNUM_UART0_TXINT); - task_disable_irq(GC_IRQNUM_UART0_RXINT); + uartn_disable_interrupt(UARTN); } void uart_enable_interrupt(void) { - task_enable_irq(GC_IRQNUM_UART0_TXINT); - task_enable_irq(GC_IRQNUM_UART0_RXINT); + uartn_enable_interrupt(UARTN); } /** @@ -114,7 +86,7 @@ void uart_enable_interrupt(void) void uart_ec_tx_interrupt(void) { /* Clear transmit interrupt status */ - GR_UART_ISTATECLR(0) = GC_UART_ISTATECLR_TX_MASK; + GR_UART_ISTATECLR(UARTN) = GC_UART_ISTATECLR_TX_MASK; /* Fill output FIFO */ uart_process_output(); @@ -124,7 +96,7 @@ DECLARE_IRQ(GC_IRQNUM_UART0_TXINT, uart_ec_tx_interrupt, 1); void uart_ec_rx_interrupt(void) { /* Clear receive interrupt status */ - GR_UART_ISTATECLR(0) = GC_UART_ISTATECLR_RX_MASK; + GR_UART_ISTATECLR(UARTN) = GC_UART_ISTATECLR_RX_MASK; /* Read input FIFO until empty */ uart_process_input(); @@ -134,25 +106,10 @@ DECLARE_IRQ(GC_IRQNUM_UART0_RXINT, uart_ec_rx_interrupt, 1); void uart_init(void) { - long long setting = (16 * (1 << UART_NCO_WIDTH) * - (long long)CONFIG_UART_BAUD_RATE / PCLK_FREQ); - - /* turn on uart clock */ clock_enable_module(MODULE_UART, 1); - /* set frequency */ - GR_UART_NCO(0) = setting; - - /* Interrupt when RX fifo has anything, when TX fifo <= half empty */ - /* Also reset (clear) both FIFOs */ - GR_UART_FIFO(0) = 0x63; - - /* TX enable, RX enable, HW flow control disabled, no loopback */ - GR_UART_CTRL(0) = 0x03; - - /* enable RX interrupts in block */ - /* Note: doesn't do anything unless turned on in NVIC */ - GR_UART_ICTRL(0) = 0x02; + /* Initialize the Cr50 UART */ + uartn_init(UARTN); #if USE_UART_INTERRUPTS /* Enable interrupts for UART0 only */ diff --git a/chip/g/uartn.c b/chip/g/uartn.c new file mode 100644 index 0000000000..343f5dc981 --- /dev/null +++ b/chip/g/uartn.c @@ -0,0 +1,137 @@ +/* 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. + */ + +#include "clock.h" +#include "common.h" +#include "gpio.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "uart.h" +#include "util.h" + +struct uartn_interrupts { + int tx_int; + int rx_int; +}; +static struct uartn_interrupts interrupt[] = { + {GC_IRQNUM_UART0_TXINT, GC_IRQNUM_UART0_RXINT}, + {GC_IRQNUM_UART1_TXINT, GC_IRQNUM_UART1_RXINT}, + {GC_IRQNUM_UART2_TXINT, GC_IRQNUM_UART2_RXINT}, +}; + + +void uartn_tx_start(int uart) +{ + if (!uart_init_done()) + return; + + /* If interrupt is already enabled, nothing to do */ + if (GR_UART_ICTRL(uart) & GC_UART_ICTRL_TX_MASK) + 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. + */ + /* TODO(crosbug.com/p/33819): Do we need this hack here? Find out. */ + REG_WRITE_MLV(GR_UART_ICTRL(uart), GC_UART_ICTRL_TX_MASK, + GC_UART_ICTRL_TX_LSB, 1); + task_trigger_irq(interrupt[uart].tx_int); +} + +void uartn_tx_stop(int uart) +{ + /* Disable the TX interrupt */ + REG_WRITE_MLV(GR_UART_ICTRL(uart), GC_UART_ICTRL_TX_MASK, + GC_UART_ICTRL_TX_LSB, 0); + + /* Re-allow deep sleep */ + enable_sleep(SLEEP_MASK_UART); +} + +int uartn_tx_in_progress(int uart) +{ + /* Transmit is in progress unless the TX FIFO is empty and idle. */ + return !(GR_UART_STATE(uart) & (GC_UART_STATE_TXIDLE_MASK | + GC_UART_STATE_TXEMPTY_MASK)); +} + +void uartn_tx_flush(int uart) +{ + /* Wait until TX FIFO is idle. */ + while (uartn_tx_in_progress(uart)) + ; +} + +int uartn_tx_ready(int uart) +{ + /* True if the TX buffer is not completely full */ + return !(GR_UART_STATE(uart) & GC_UART_STATE_TX_MASK); +} + +int uartn_rx_available(int uart) +{ + /* True if the RX buffer is not completely empty. */ + return !(GR_UART_STATE(uart) & GC_UART_STATE_RXEMPTY_MASK); +} + +void uartn_write_char(int uart, char c) +{ + /* Wait for space in transmit FIFO. */ + while (!uartn_tx_ready(uart)) + ; + + GR_UART_WDATA(uart) = c; +} + +int uartn_read_char(int uart) +{ + return GR_UART_RDATA(uart); +} + +void uartn_disable_interrupt(int uart) +{ + task_disable_irq(interrupt[uart].tx_int); + task_disable_irq(interrupt[uart].rx_int); +} + +void uartn_enable_interrupt(int uart) +{ + task_enable_irq(interrupt[uart].tx_int); + task_enable_irq(interrupt[uart].rx_int); +} + + +void uartn_init(int uart) +{ + long long setting = (16 * (1 << UART_NCO_WIDTH) * + (long long)CONFIG_UART_BAUD_RATE / PCLK_FREQ); + + /* set frequency */ + GR_UART_NCO(uart) = setting; + + /* + * Interrupt when RX fifo has anything, when TX fifo <= half + * empty and reset (clear) both FIFOs + */ + GR_UART_FIFO(uart) = 0x63; + + /* + * TX enable, RX enable, HW flow control disabled, no + * loopback + */ + GR_UART_CTRL(uart) = 0x03; + + /* enable RX interrupts in block */ + /* Note: doesn't do anything unless turned on in NVIC */ + GR_UART_ICTRL(uart) = 0x02; + +} diff --git a/chip/g/uartn.h b/chip/g/uartn.h new file mode 100644 index 0000000000..12d0f53bff --- /dev/null +++ b/chip/g/uartn.h @@ -0,0 +1,77 @@ +/* Copyright 2016 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. + */ + +#ifndef __CROS_EC_UARTN_H +#define __CROS_EC_UARTN_H + +#include "common.h" +#include "gpio.h" + +/** + * Initialize the UART module. + */ +void uartn_init(int uart); + +/** + * Flush the transmit FIFO. + */ +void uartn_tx_flush(int uart); + +/** + * Return non-zero if there is room to transmit a character immediately. + */ +int uartn_tx_ready(int uart); + +/** + * Return non-zero if a transmit is in progress. + */ +int uartn_tx_in_progress(int uart); + +/* + * Return non-zero if the UART has a character available to read. + */ +int uartn_rx_available(int uart); + +/** + * Send a character to the UART data register. + * + * If the transmit FIFO is full, blocks until there is space. + * + * @param c Character to send. + */ +void uartn_write_char(int uart, char c); + +/** + * Read one char from the UART data register. + * + * @return The character read. + */ +int uartn_read_char(int uart); + +/** + * Disable all UART related IRQs. + * + * Used to avoid concurrent accesses on UART management variables. + */ +void uartn_disable_interrupt(int uart); + +/** + * Re-enable UART IRQs. + */ +void uartn_enable_interrupt(int uart); + +/** + * Re-enable the UART transmit interrupt. + * + * This also forces triggering a UART interrupt, if the transmit interrupt was + * disabled. + */ +void uartn_tx_start(int uart); + +/** + * Disable the UART transmit interrupt. + */ +void uartn_tx_stop(int uart); +#endif /* __CROS_EC_UARTN_H */ |