summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/build.mk1
-rw-r--r--chip/stm32/usart-stm32f.c114
-rw-r--r--chip/stm32/usart-stm32f.h20
-rw-r--r--chip/stm32/usart-stm32f0.c146
-rw-r--r--chip/stm32/usart-stm32f0.h21
-rw-r--r--chip/stm32/usart-stm32l.c117
-rw-r--r--chip/stm32/usart-stm32l.h20
-rw-r--r--chip/stm32/usart.c201
-rw-r--r--chip/stm32/usart.h185
-rw-r--r--include/config.h13
-rw-r--r--include/module_id.h1
-rw-r--r--include/util.h8
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)))