diff options
author | Anton Staaf <robotboy@chromium.org> | 2014-07-23 14:25:35 -0700 |
---|---|---|
committer | chrome-internal-fetch <chrome-internal-fetch@google.com> | 2014-09-23 00:04:56 +0000 |
commit | 0a147973bb6a16714925cc7d701b9b6cffc73cb2 (patch) | |
tree | 94e385317bdff8ccc5d06082429b50d62ae7a2d5 | |
parent | ccb45ff8a2285fbee6d87c02789dc7b00250dc82 (diff) | |
download | chrome-ec-0a147973bb6a16714925cc7d701b9b6cffc73cb2.tar.gz |
stm32-USART: Add generic stream based usart driver
This driver can be used to access multiple usarts using an abstract
stream interface. The stream interface can also be used in drivers
for the host interface and USB console interface, providing a
consistent API across all character stream style IO.
Signed-off-by: Anton Staaf <robotboy@chromium.org>
BRANCH=None
BUG=None
TEST=make buildall -j
Change-Id: Icf567f0b0fa4eb0e9ad4cdb0be8edc31c937a7de
Reviewed-on: https://chromium-review.googlesource.com/209671
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Tested-by: Anton Staaf <robotboy@chromium.org>
Commit-Queue: Anton Staaf <robotboy@chromium.org>
-rw-r--r-- | chip/stm32/build.mk | 1 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f.c | 114 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f.h | 20 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f0.c | 146 | ||||
-rw-r--r-- | chip/stm32/usart-stm32f0.h | 21 | ||||
-rw-r--r-- | chip/stm32/usart-stm32l.c | 117 | ||||
-rw-r--r-- | chip/stm32/usart-stm32l.h | 20 | ||||
-rw-r--r-- | chip/stm32/usart.c | 201 | ||||
-rw-r--r-- | chip/stm32/usart.h | 185 | ||||
-rw-r--r-- | include/config.h | 13 | ||||
-rw-r--r-- | include/module_id.h | 1 | ||||
-rw-r--r-- | include/util.h | 8 |
12 files changed, 847 insertions, 0 deletions
diff --git a/chip/stm32/build.mk b/chip/stm32/build.mk index d4fe146197..8c6b0cd2e6 100644 --- a/chip/stm32/build.mk +++ b/chip/stm32/build.mk @@ -30,6 +30,7 @@ chip-$(CONFIG_SPI_MASTER_PORT)+=spi_master.o chip-$(CONFIG_COMMON_GPIO)+=gpio.o gpio-$(CHIP_FAMILY).o chip-$(CONFIG_COMMON_TIMER)+=hwtimer$(TIMER_TYPE).o chip-$(CONFIG_I2C)+=i2c-$(CHIP_FAMILY).o i2c.o +chip-$(CONFIG_STREAM_USART)+=usart.o usart-$(CHIP_FAMILY).o chip-$(CONFIG_WATCHDOG)+=watchdog.o chip-$(HAS_TASK_CONSOLE)+=uart.o chip-$(HAS_TASK_KEYSCAN)+=keyboard_raw.o diff --git a/chip/stm32/usart-stm32f.c b/chip/stm32/usart-stm32f.c new file mode 100644 index 0000000000..918b98b96e --- /dev/null +++ b/chip/stm32/usart-stm32f.c @@ -0,0 +1,114 @@ +/* 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 "usart-stm32f.h" + +#include "common.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" + +/* + * This configs array stores the currently active usart_config structure for + * each USART, an entry will be NULL if no USART driver is initialized for the + * corresponding hardware instance. + */ +static struct usart_config const *configs[STM32_USARTS_MAX]; + +static void usart_variant_enable(struct usart_config const *config) +{ + /* + * Make sure we register this config before enabling the HW. + * If we did it the other way around the FREQ_CHANGE hook could be + * called before we update the configs array and we would miss the + * clock frequency change event, leaving our baud rate divisor wrong. + */ + configs[config->hw->index] = config; + + usart_set_baud_f0_l(config); + + task_enable_irq(config->hw->irq); +} + +static void usart_variant_disable(struct usart_config const *config) +{ + task_disable_irq(config->hw->irq); + + configs[config->hw->index] = NULL; +} + +static usart_hw_ops const struct usart_variant_hw_ops = { + .enable = usart_variant_enable, + .disable = usart_variant_disable, +}; + +static void freq_change(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(configs); ++i) + if (configs[i]) + usart_set_baud_f(configs[i]); +} + +DECLARE_HOOK(HOOK_FREQ_CHANGE, freq_change, HOOK_PRIO_DEFAULT); + +/* + * USART interrupt bindings. These functions can not be defined as static or + * they will be removed by the linker because of the way that DECLARE_IRQ works. + */ +#if defined(CONFIG_STREAM_USART1) +struct usart_hw_config const usart1_hw = { + .index = 0, + .base = STM32_USART1_BASE, + .irq = STM32_IRQ_USART1, + .clock_register = &STM32_RCC_APB2ENR, + .clock_enable = STM32_RCC_PB2_USART1, + .ops = &usart_variant_hw_ops, +}; + +void usart1_interrupt(void) +{ + usart_interrupt(configs[0]); +} + +DECLARE_IRQ(STM32_IRQ_USART1, usart1_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART2) +struct usart_hw_config const usart2_hw = { + .index = 1, + .base = STM32_USART2_BASE, + .irq = STM32_IRQ_USART2, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART2, + .ops = &usart_variant_hw_ops, +}; + +void usart2_interrupt(void) +{ + usart_interrupt(configs[1]); +} + +DECLARE_IRQ(STM32_IRQ_USART2, usart2_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART3) +struct usart_hw_config const usart3_hw = { + .index = 2, + .base = STM32_USART3_BASE, + .irq = STM32_IRQ_USART3_4, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART3, + .ops = &usart_variant_hw_ops, +}; + +void usart3_interrupt(void) +{ + usart_interrupt(configs[2]); +} + +DECLARE_IRQ(STM32_IRQ_USART3, usart3_interrupt, 2); +#endif diff --git a/chip/stm32/usart-stm32f.h b/chip/stm32/usart-stm32f.h new file mode 100644 index 0000000000..e576c007b3 --- /dev/null +++ b/chip/stm32/usart-stm32f.h @@ -0,0 +1,20 @@ +/* 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. + */ +#ifndef CHIP_STM32_USART_STM32F_H +#define CHIP_STM32_USART_STM32F_H + +#include "usart.h" + +#define STM32_USARTS_MAX 3 + +/* + * The STM32F series can have as many as three UARTS. These are the HW configs + * for those UARTS. They can be used to initialize STM32 generic UART configs. + */ +extern struct usart_hw_config const usart1_hw; +extern struct usart_hw_config const usart2_hw; +extern struct usart_hw_config const usart3_hw; + +#endif /* CHIP_STM32_USART_STM32F_H */ diff --git a/chip/stm32/usart-stm32f0.c b/chip/stm32/usart-stm32f0.c new file mode 100644 index 0000000000..d0688d4d9e --- /dev/null +++ b/chip/stm32/usart-stm32f0.c @@ -0,0 +1,146 @@ +/* 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 "usart-stm32f0.h" + +#include "common.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" + +/* + * This configs array stores the currently active usart_config structure for + * each USART, an entry will be NULL if no USART driver is initialized for the + * corresponding hardware instance. + */ +static struct usart_config const *configs[STM32_USARTS_MAX]; + +static void usart_variant_enable(struct usart_config const *config) +{ + /* + * Make sure we register this config before enabling the HW. + * If we did it the other way around the FREQ_CHANGE hook could be + * called before we update the configs array and we would miss the + * clock frequency change event, leaving our baud rate divisor wrong. + */ + configs[config->hw->index] = config; + + usart_set_baud_f0_l(config); + + task_enable_irq(config->hw->irq); +} + +static void usart_variant_disable(struct usart_config const *config) +{ + int index = config->hw->index; + + /* + * Only disable the shared interupt for USART3/4 if both USARTs are + * now disabled. + */ + if ((index == 0) || + (index == 1) || + (index == 2 && configs[3] == NULL) || + (index == 3 && configs[2] == NULL)) + task_disable_irq(config->hw->irq); + + configs[index] = NULL; +} + +static struct usart_hw_ops const usart_variant_hw_ops = { + .enable = usart_variant_enable, + .disable = usart_variant_disable, +}; + +static void freq_change(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(configs); ++i) + if (configs[i]) + usart_set_baud_f0_l(configs[i]); +} + +DECLARE_HOOK(HOOK_FREQ_CHANGE, freq_change, HOOK_PRIO_DEFAULT); + +/* + * USART interrupt bindings. These functions can not be defined as static or + * they will be removed by the linker because of the way that DECLARE_IRQ works. + */ +#if defined(CONFIG_STREAM_USART1) +struct usart_hw_config const usart1_hw = { + .index = 0, + .base = STM32_USART1_BASE, + .irq = STM32_IRQ_USART1, + .clock_register = &STM32_RCC_APB2ENR, + .clock_enable = STM32_RCC_PB2_USART1, + .ops = &usart_variant_hw_ops, +}; + +void usart1_interrupt(void) +{ + usart_interrupt(configs[0]); +} + +DECLARE_IRQ(STM32_IRQ_USART1, usart1_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART2) +struct usart_hw_config const usart2_hw = { + .index = 1, + .base = STM32_USART2_BASE, + .irq = STM32_IRQ_USART2, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART2, + .ops = &usart_variant_hw_ops, +}; + +void usart2_interrupt(void) +{ + usart_interrupt(configs[1]); +} + +DECLARE_IRQ(STM32_IRQ_USART2, usart2_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART3) +struct usart_hw_config const usart3_hw = { + .index = 2, + .base = STM32_USART3_BASE, + .irq = STM32_IRQ_USART3_4, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART3, + .ops = &usart_variant_hw_ops, +}; +#endif + +#if defined(CONFIG_STREAM_USART4) +struct usart_hw_config const usart4_hw = { + .index = 3, + .base = STM32_USART4_BASE, + .irq = STM32_IRQ_USART3_4, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART4, + .ops = &usart_variant_hw_ops, +}; +#endif + +#if defined(CONFIG_STREAM_USART3) || defined(CONFIG_STREAM_USART4) +void usart3_4_interrupt(void) +{ + /* + * This interrupt handler could be called with one of these configs + * not initialized, so we need to check here and only call the generic + * USART interrupt handler for initialized configs. + */ + if (configs[2]) + usart_interrupt(configs[2]); + + if (configs[3]) + usart_interrupt(configs[3]); +} + +DECLARE_IRQ(STM32_IRQ_USART3_4, usart3_4_interrupt, 2); +#endif diff --git a/chip/stm32/usart-stm32f0.h b/chip/stm32/usart-stm32f0.h new file mode 100644 index 0000000000..f87d0dc590 --- /dev/null +++ b/chip/stm32/usart-stm32f0.h @@ -0,0 +1,21 @@ +/* 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. + */ +#ifndef CHIP_STM32_USART_STM32F0_H +#define CHIP_STM32_USART_STM32F0_H + +#include "usart.h" + +#define STM32_USARTS_MAX 4 + +/* + * The STM32F0 series can have as many as four UARTS. These are the HW configs + * for those UARTS. They can be used to initialize STM32 generic UART configs. + */ +extern struct usart_hw_config const usart1_hw; +extern struct usart_hw_config const usart2_hw; +extern struct usart_hw_config const usart3_hw; +extern struct usart_hw_config const usart4_hw; + +#endif /* CHIP_STM32_USART_STM32F0_H */ diff --git a/chip/stm32/usart-stm32l.c b/chip/stm32/usart-stm32l.c new file mode 100644 index 0000000000..6725df3c14 --- /dev/null +++ b/chip/stm32/usart-stm32l.c @@ -0,0 +1,117 @@ +/* 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 "usart-stm32l.h" + +#include "common.h" +#include "hooks.h" +#include "registers.h" +#include "task.h" +#include "util.h" + +/* + * This configs array stores the currently active usart_config structure for + * each USART, an entry will be NULL if no USART driver is initialized for the + * corresponding hardware instance. + */ +static struct usart_config const *configs[STM32_USARTS_MAX]; + +static void usart_variant_enable(struct usart_config const *config) +{ + /* Use single-bit sampling */ + STM32_USART_CR3(config->hw->base) |= STM32_USART_CR3_ONEBIT; + + /* + * Make sure we register this config before enabling the HW. + * If we did it the other way around the FREQ_CHANGE hook could be + * called before we update the configs array and we would miss the + * clock frequency change event, leaving our baud rate divisor wrong. + */ + configs[config->hw->index] = config; + + usart_set_baud_f0_l(config); + + task_enable_irq(config->hw->irq); +} + +static void usart_variant_disable(struct usart_config const *config) +{ + task_disable_irq(config->hw->irq); + + configs[config->hw->index] = NULL; +} + +static struct usart_hw_ops const usart_variant_hw_ops = { + .enable = usart_variant_enable, + .disable = usart_variant_disable, +}; + +static void freq_change(void) +{ + size_t i; + + for (i = 0; i < ARRAY_SIZE(configs); ++i) + if (configs[i]) + usart_set_baud_f0_l(configs[i]); +} + +DECLARE_HOOK(HOOK_FREQ_CHANGE, freq_change, HOOK_PRIO_DEFAULT); + +/* + * USART interrupt bindings. These functions can not be defined as static or + * they will be removed by the linker because of the way that DECLARE_IRQ works. + */ +#if defined(CONFIG_STREAM_USART1) +struct usart_hw_config const usart1_hw = { + .index = 0, + .base = STM32_USART1_BASE, + .irq = STM32_IRQ_USART1, + .clock_register = &STM32_RCC_APB2ENR, + .clock_enable = STM32_RCC_PB2_USART1, + .ops = &usart_variant_hw_ops, +}; + +void usart1_interrupt(void) +{ + usart_interrupt(configs[0]); +} + +DECLARE_IRQ(STM32_IRQ_USART1, usart1_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART2) +struct usart_hw_config const usart2_hw = { + .index = 1, + .base = STM32_USART2_BASE, + .irq = STM32_IRQ_USART2, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART2, + .ops = &usart_variant_hw_ops, +}; + +void usart2_interrupt(void) +{ + usart_interrupt(configs[1]); +} + +DECLARE_IRQ(STM32_IRQ_USART2, usart2_interrupt, 2); +#endif + +#if defined(CONFIG_STREAM_USART3) +struct usart_hw_config const usart3_hw = { + .index = 2, + .base = STM32_USART3_BASE, + .irq = STM32_IRQ_USART3, + .clock_register = &STM32_RCC_APB1ENR, + .clock_enable = STM32_RCC_PB1_USART3, + .ops = &usart_variant_hw_ops, +}; + +void usart3_interrupt(void) +{ + usart_interrupt(configs[2]); +} + +DECLARE_IRQ(STM32_IRQ_USART3, usart3_interrupt, 2); +#endif diff --git a/chip/stm32/usart-stm32l.h b/chip/stm32/usart-stm32l.h new file mode 100644 index 0000000000..2011a6517d --- /dev/null +++ b/chip/stm32/usart-stm32l.h @@ -0,0 +1,20 @@ +/* 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. + */ +#ifndef CHIP_STM32_USART_STM32L_H +#define CHIP_STM32_USART_STM32L_H + +#include "usart.h" + +#define STM32_USARTS_MAX 3 + +/* + * The STM32L series can have as many as three UARTS. These are the HW configs + * for those UARTS. They can be used to initialize STM32 generic UART configs. + */ +extern struct usart_hw_config const usart1_hw; +extern struct usart_hw_config const usart2_hw; +extern struct usart_hw_config const usart3_hw; + +#endif /* CHIP_STM32_USART_STM32L_H */ diff --git a/chip/stm32/usart.c b/chip/stm32/usart.c new file mode 100644 index 0000000000..8affbebf08 --- /dev/null +++ b/chip/stm32/usart.c @@ -0,0 +1,201 @@ +/* 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. + */ + +/* USART driver for Chrome EC */ + +#include "atomic.h" +#include "clock.h" +#include "common.h" +#include "gpio.h" +#include "registers.h" +#include "system.h" +#include "task.h" +#include "usart.h" +#include "util.h" + +static size_t usart_read(struct in_stream const *stream, + uint8_t *buffer, + size_t count) +{ + struct usart_config const *config = + DOWNCAST(stream, struct usart_config, in); + + return QUEUE_REMOVE_UNITS(&config->rx, buffer, count); +} + +static size_t usart_write(struct out_stream const *stream, + uint8_t const *buffer, + size_t count) +{ + struct usart_config const *config = + DOWNCAST(stream, struct usart_config, out); + + size_t wrote = QUEUE_ADD_UNITS(&config->tx, buffer, count); + + /* + * Enable USART interrupt. This causes the USART interrupt handler to + * start fetching from the TX queue if it wasn't already. + */ + if (wrote) + STM32_USART_CR1(config->hw->base) |= STM32_USART_CR1_TXEIE; + + return wrote; +} + +static void usart_flush(struct out_stream const *stream) +{ + struct usart_config const *config = + DOWNCAST(stream, struct usart_config, out); + + while (queue_count(&config->tx)) + ; +} + +struct in_stream_ops const usart_in_stream_ops = { + .read = usart_read, +}; + +struct out_stream_ops const usart_out_stream_ops = { + .write = usart_write, + .flush = usart_flush, +}; + +void usart_init(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + + queue_init(&config->tx); + queue_init(&config->rx); + + /* + * Enable clock to USART, this must be done first, before attempting + * to configure the USART. + */ + *(config->hw->clock_register) |= config->hw->clock_enable; + + /* + * Switch all GPIOs assigned to the USART module over to their USART + * alternate functions. + */ + gpio_config_module(MODULE_USART, 1); + + /* + * 8N1, 16 samples per bit, enable TX and RX (and associated RX + * interrupt) DMA, error interrupts, and special modes disabled. + */ + STM32_USART_CR1(base) = (STM32_USART_CR1_TE | + STM32_USART_CR1_RE | + STM32_USART_CR1_RXNEIE); + STM32_USART_CR2(base) = 0x0000; + STM32_USART_CR3(base) = 0x0000; + + /* + * Enable the variant specific HW. + */ + config->hw->ops->enable(config); + + /* + * Enable the USART, this must be done last since most of the + * configuration bits require that the USART be disabled for writes to + * succeed. + */ + STM32_USART_CR1(base) |= STM32_USART_CR1_UE; +} + +void usart_shutdown(struct usart_config const *config) +{ + STM32_USART_CR1(config->hw->base) &= ~STM32_USART_CR1_UE; + + config->hw->ops->disable(config); +} + +void usart_set_baud_f0_l(struct usart_config const *config) +{ + int div = DIV_ROUND_NEAREST(clock_get_freq(), config->baud); + intptr_t base = config->hw->base; + + if (div / 16 > 0) { + /* + * CPU clock is high enough to support x16 oversampling. + * BRR = (div mantissa)<<4 | (4-bit div fraction) + */ + STM32_USART_CR1(base) &= ~STM32_USART_CR1_OVER8; + STM32_USART_BRR(base) = div; + } else { + /* + * CPU clock is low; use x8 oversampling. + * BRR = (div mantissa)<<4 | (3-bit div fraction) + */ + STM32_USART_BRR(base) = ((div / 8) << 4) | (div & 7); + STM32_USART_CR1(base) |= STM32_USART_CR1_OVER8; + } +} + +void usart_set_baud_f(struct usart_config const *config) +{ + int div = DIV_ROUND_NEAREST(clock_get_freq(), config->baud); + + /* STM32F only supports x16 oversampling */ + STM32_USART_BRR(config->hw->base) = div; +} + +static void usart_interrupt_tx(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + uint8_t byte; + + if (queue_remove_unit(&config->tx, &byte)) { + STM32_USART_TDR(base) = byte; + + out_stream_ready(&config->out); + + /* + * Make sure the TXE interrupt is enabled and that we won't go + * into deep sleep. This invocation of the USART interrupt + * handler may have been manually triggered to start + * transmission. + */ + disable_sleep(SLEEP_MASK_UART); + + STM32_USART_CR1(base) |= STM32_USART_CR1_TXEIE; + } else { + /* + * The TX queue is empty, disable the TXE interrupt and enable + * deep sleep mode. The TXE interrupt will remain disabled + * until a write call happens. + */ + enable_sleep(SLEEP_MASK_UART); + + STM32_USART_CR1(base) &= ~STM32_USART_CR1_TXEIE; + } +} + +static void usart_interrupt_rx(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + uint8_t byte = STM32_USART_RDR(base); + uint32_t dropped = 1 - queue_add_unit(&config->rx, &byte); + + atomic_add((uint32_t *) &config->state->rx_dropped, dropped); + + /* + * Wake up whoever is listening on the other end of the queue. The + * queue_add_units call above may have failed due to a full queue, but + * it doesn't really matter to the ready callback because there will be + * something in the queue to consume either way. + */ + in_stream_ready(&config->in); +} + +void usart_interrupt(struct usart_config const *config) +{ + intptr_t base = config->hw->base; + + if (STM32_USART_SR(base) & STM32_USART_SR_TXE) + usart_interrupt_tx(config); + + if (STM32_USART_SR(base) & STM32_USART_SR_RXNE) + usart_interrupt_rx(config); +} diff --git a/chip/stm32/usart.h b/chip/stm32/usart.h new file mode 100644 index 0000000000..8fff29288c --- /dev/null +++ b/chip/stm32/usart.h @@ -0,0 +1,185 @@ +/* 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. + */ +#ifndef CHIP_STM32_USART_H +#define CHIP_STM32_USART_H + +/* STM32 USART driver for Chrome EC */ + +#include "common.h" +#include "in_stream.h" +#include "out_stream.h" +#include "queue.h" + +#include <stdint.h> + +/* + * Per-USART state stored in RAM. This structure will be zero initialized by + * BSS init. Most importantly, irq_lock will be zero, ensuring that shared + * interrupts don't cause problems. + */ +struct usart_state { + struct queue_state rx; + struct queue_state tx; + + uint32_t rx_dropped; +}; + +struct usart_config; + +struct usart_hw_ops { + /* + * The generic USART initialization code calls this function to allow + * the variant HW specific code to perform any initialization. This + * function is called before the USART is enabled, and should among + * other things enable the USARTs interrupt. + */ + void (*enable)(struct usart_config const *config); + + /* + */ + void (*disable)(struct usart_config const *config); +}; + +/* + * Per-USART hardware configuration stored in flash. Instances of this + * structure are provided by each variants driver, one per physical USART. + */ +struct usart_hw_config { + int index; + intptr_t base; + int irq; + + uint32_t volatile *clock_register; + uint32_t clock_enable; + + struct usart_hw_ops const *ops; +}; + +/* + * Compile time Per-USART configuration stored in flash. Instances of this + * structure are provided by the user of the USART. This structure binds + * together all information required to operate a USART. + */ +struct usart_config { + /* + * Pointer to USART HW configuration. There is one HW configuration + * per physical USART. + */ + struct usart_hw_config const *hw; + + /* + * Pointer to USART state structure. The state structure maintains per + * USART information (head and tail pointers for the queues for + * instance). + */ + struct usart_state volatile *state; + + /* + * Baud rate for USART. + */ + int baud; + + /* + * TX and RX queue configs. The state for the queue is stored + * separately in the usart_state structure. + */ + struct queue rx; + struct queue tx; + + /* + * In and Out streams, these contain pointers to the virtual function + * tables that implement in and out streams. They can be used by any + * code that wants to read or write to a stream interface. + */ + struct in_stream in; + struct out_stream out; +}; + +/* + * These function tables are defined by the USART driver and are used to + * initialize the in and out streams in the usart_config. + */ +extern struct in_stream_ops const usart_in_stream_ops; +extern struct out_stream_ops const usart_out_stream_ops; + +/* + * Convenience macro for defining USARTs and their associated state and buffers. + * NAME is used to construct the names of the queue buffers, usart_state struct, + * and usart_config struct, the latter is just called NAME. RX_SIZE and TX_SIZE + * are the size in bytes of the RX and TX buffers respectively. RX_READY and + * TX_READY are the callback functions for the in and out streams. The USART + * baud rate is specified by the BAUD parameter. + * + * If you want to share a queue with other code, you can manually initialize a + * usart_config to use the shared queue, or you can use this macro and then get + * the queue buffers as <NAME>_tx_buffer, and <NAME>_rx_buffer. + */ +#define USART_CONFIG(NAME, \ + HW, \ + BAUD, \ + RX_SIZE, \ + TX_SIZE, \ + RX_READY, \ + TX_READY) \ + static uint8_t CONCAT2(NAME, _tx_buffer)[TX_SIZE]; \ + static uint8_t CONCAT2(NAME, _rx_buffer)[RX_SIZE]; \ + \ + static struct usart_state CONCAT2(NAME, _state); \ + struct usart_config const NAME = { \ + .hw = &HW, \ + .state = &CONCAT2(NAME, _state), \ + .baud = BAUD, \ + .rx = { \ + .state = &CONCAT2(NAME, _state.rx), \ + .buffer_units = RX_SIZE, \ + .unit_bytes = 1, \ + .buffer = CONCAT2(NAME, _rx_buffer), \ + }, \ + .tx = { \ + .state = &CONCAT2(NAME, _state.tx), \ + .buffer_units = TX_SIZE, \ + .unit_bytes = 1, \ + .buffer = CONCAT2(NAME, _tx_buffer), \ + }, \ + .in = { \ + .ready = RX_READY, \ + .ops = &usart_in_stream_ops, \ + }, \ + .out = { \ + .ready = TX_READY, \ + .ops = &usart_out_stream_ops, \ + }, \ + }; + +/* + * Initialize the given USART. Once init is finished the USART streams are + * available for operating on, and the stream ready callbacks could be called + * at any time. + */ +void usart_init(struct usart_config const *config); + +/* + * Shutdown the given USART. + */ +void usart_shutdown(struct usart_config const *config); + +/* + * Handle a USART interrupt. The per-variant USART code creates bindings + * for the variants interrupts to call this generic USART interrupt handler + * with the appropriate usart_config. + * + * This function could also be called manually to poll the USART hardware. + */ +void usart_interrupt(struct usart_config const *config); + +/* + * These are HW specific baud rate calculation and setting functions that the + * peripheral variant code uses during initialization and clock frequency + * change. + */ +void usart_set_baud_f0_l(struct usart_config const *config); +void usart_set_baud_f(struct usart_config const *config); + +#endif /* CHIP_STM32_USART_H */ diff --git a/include/config.h b/include/config.h index 703297ba6c..5ae76fa5dd 100644 --- a/include/config.h +++ b/include/config.h @@ -912,6 +912,19 @@ #undef CONFIG_STREAM /*****************************************************************************/ +/* USART stream config */ +#undef CONFIG_STREAM_USART + +/* + * Each USART stream can be individually enabled and accessible using the + * stream interface provided in the usart_config struct. + */ +#undef CONFIG_STREAM_USART1 +#undef CONFIG_STREAM_USART2 +#undef CONFIG_STREAM_USART3 +#undef CONFIG_STREAM_USART4 + +/*****************************************************************************/ /* UART config */ /* Baud rate for UARTs */ diff --git a/include/module_id.h b/include/module_id.h index 640ad99cc4..55b05be8b3 100644 --- a/include/module_id.h +++ b/include/module_id.h @@ -40,6 +40,7 @@ enum module_id { MODULE_TASK, MODULE_THERMAL, MODULE_UART, + MODULE_USART, MODULE_USB, MODULE_USB_DEBUG, MODULE_USB_PD, diff --git a/include/util.h b/include/util.h index c012470a47..8b1e51e2c5 100644 --- a/include/util.h +++ b/include/util.h @@ -56,6 +56,14 @@ #define NULL ((void *)0) #endif +/* + * Convert a pointer to a base struct into a pointer to the struct that + * contains the base struct. This requires knowing where in the contained + * struct the base struct resides, this is the member parameter to downcast. + */ +#define DOWNCAST(pointer, type, member) \ + ((type *)(((uint8_t *) pointer) - offsetof(type, member))) + /* True of x is a power of two */ #define POWER_OF_TWO(x) (x && !(x & (x - 1))) |