diff options
-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))) |