summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAseda Aboagye <aaboagye@google.com>2017-05-11 11:35:54 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-06-05 14:49:09 -0700
commit7dab0e853caf04a3c8d5d1f7b795a23f8b6142d9 (patch)
tree431eb724321c53d66d4d3de0349a955ebe2d4bb3
parent2ac6afd3e68bde5b50ddc9d35f832112c56f22ab (diff)
downloadchrome-ec-7dab0e853caf04a3c8d5d1f7b795a23f8b6142d9.tar.gz
chip: g: Add support for UART bit banging.
The UART block on the g chip has no functionality to adjust the parity. Unfortunately, this feature is needed for certain applications. This commit adds a UART bit bang driver with support for configuring the baud rate and parity. It currently only supports 8 data bits. BUG=b:35648297 BRANCH=cr50 TEST=make -j buildall TEST=With some other patches, successfully flash rowan EC at 9600 baud. Change-Id: I86a160c0960e46b3a8bb1057518f625aefb7d81f Signed-off-by: Aseda Aboagye <aaboagye@google.com> Reviewed-on: https://chromium-review.googlesource.com/503473 Commit-Ready: Aseda Aboagye <aaboagye@chromium.org> Tested-by: Aseda Aboagye <aaboagye@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--chip/g/build.mk3
-rw-r--r--chip/g/uart_bitbang.c390
-rw-r--r--chip/g/uart_bitbang.h112
-rw-r--r--chip/g/uartn.c66
-rw-r--r--include/config.h3
5 files changed, 570 insertions, 4 deletions
diff --git a/chip/g/build.mk b/chip/g/build.mk
index d425b4c09e..997a3965bb 100644
--- a/chip/g/build.mk
+++ b/chip/g/build.mk
@@ -26,7 +26,8 @@ chip-y += polling_uart.o
else
chip-y += uart.o
chip-y += uartn.o
-endif
+chip-$(CONFIG_UART_BITBANG)+= uart_bitbang.o
+endif # undef CONFIG_POLLING_UART
chip-$(CONFIG_DCRYPTO)+= crypto_api.o
diff --git a/chip/g/uart_bitbang.c b/chip/g/uart_bitbang.c
new file mode 100644
index 0000000000..54dcf0d7e0
--- /dev/null
+++ b/chip/g/uart_bitbang.c
@@ -0,0 +1,390 @@
+/* Copyright 2017 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 "common.h"
+#include "compile_time_macros.h"
+#include "console.h"
+#include "gpio.h"
+#include "pmu.h"
+#include "registers.h"
+#include "task.h"
+#include "timer.h"
+#include "uart_bitbang.h"
+#include "uartn.h"
+
+#define CPRINTF(format, args...) cprintf(CC_SYSTEM, format, ##args)
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ##args)
+
+/* Support the "standard" baud rates. */
+#define IS_BAUD_RATE_SUPPORTED(rate) \
+ ((rate == 1200) || (rate == 2400) || (rate == 4800) || (rate == 9600) \
+ || (rate == 19200) || (rate == 38400) || (rate == 57600) || \
+ (rate == 115200))
+
+#define RX_BUF_SIZE 8
+#define DISCARD_LOG 8
+
+#define BUF_NEXT(idx) ((idx+1) % RX_BUF_SIZE)
+
+#define TIMEUS_CLK_FREQ 24 /* units: MHz */
+
+/* Bitmask of UARTs with bit banging enabled. */
+static uint8_t bitbang_enabled;
+/* TODO(aaboagye): just a hack for now. */
+static int rx_buf[RX_BUF_SIZE];
+static int parity_err_discard[DISCARD_LOG];
+static int parity_discard_idx;
+static int stop_bit_discard[DISCARD_LOG];
+static int stop_bit_discard_idx;
+
+/* debug counters */
+static int read_char_cnt;
+static int rx_buff_inserted_cnt;
+static int rx_buff_rx_char_cnt;
+static int stop_bit_err_cnt;
+static int parity_err_cnt;
+
+/* Current bitbang context */
+static int tx_pin;
+static int rx_pin;
+static uint32_t bit_period_ticks;
+static uint8_t set_parity;
+
+static int is_uart_allowed(int uart)
+{
+ return uart == bitbang_config.uart;
+}
+
+int uart_bitbang_is_enabled(int uart)
+{
+ return (is_uart_allowed(uart) && !!bitbang_enabled);
+}
+
+int uart_bitbang_enable(int uart, int baud_rate, int parity)
+{
+ /* We only want to bit bang 1 UART at a time. */
+ if (bitbang_enabled)
+ return EC_ERROR_BUSY;
+
+ if (!is_uart_allowed(uart)) {
+ CPRINTF("bit bang config not found for UART%d", uart);
+ return EC_ERROR_INVAL;
+ }
+
+ /* Check desired properties. */
+ if (!IS_BAUD_RATE_SUPPORTED(baud_rate)) {
+ CPRINTF("Err: invalid baud rate (%d)", baud_rate);
+ return EC_ERROR_INVAL;
+ }
+ bitbang_config.baud_rate = baud_rate;
+
+ switch (parity) {
+ case 0:
+ case 1:
+ case 2:
+ break;
+
+ default:
+ CPRINTF("Err: invalid parity '%d'. (0:N, 1:O, 2:E)", parity);
+ return EC_ERROR_INVAL;
+ };
+ bitbang_config.htp.parity = parity;
+
+ /* Select the GPIOs instead of the UART block. */
+ uartn_tx_disconnect(bitbang_config.uart);
+ REG32(bitbang_config.tx_pinmux_reg) =
+ bitbang_config.tx_pinmux_regval;
+ gpio_set_flags(bitbang_config.tx_gpio, GPIO_OUT_HIGH);
+ REG32(bitbang_config.rx_pinmux_reg) =
+ bitbang_config.rx_pinmux_regval;
+ gpio_set_flags(bitbang_config.rx_gpio, GPIO_INPUT);
+
+ /*
+ * Ungate the microsecond timer so that we can use it. This is needed
+ * for accurate framing if using faster baud rates.
+ */
+ pmu_clock_en(PERIPH_TIMEUS);
+ GR_TIMEUS_EN(0) = 0;
+ GR_TIMEUS_MAXVAL(0) = 0xFFFFFFFF;
+ GR_TIMEUS_EN(0) = 1;
+
+ /* Save context information. */
+ tx_pin = bitbang_config.tx_gpio;
+ rx_pin = bitbang_config.rx_gpio;
+ bit_period_ticks = TIMEUS_CLK_FREQ *
+ ((1 * SECOND) / bitbang_config.baud_rate);
+ set_parity = bitbang_config.htp.parity;
+
+ /* Register the function pointers. */
+ uartn_funcs[uart]._rx_available = _uart_bitbang_rx_available;
+ uartn_funcs[uart]._write_char = _uart_bitbang_write_char;
+ uartn_funcs[uart]._read_char = _uart_bitbang_read_char;
+
+ bitbang_enabled = 1;
+ gpio_enable_interrupt(bitbang_config.rx_gpio);
+ ccprintf("successfully enabled\n");
+ return EC_SUCCESS;
+}
+
+int uart_bitbang_disable(int uart)
+{
+ if (!uart_bitbang_is_enabled(uart))
+ return EC_SUCCESS;
+
+ /*
+ * This is safe because if the UART was not specified in the config, we
+ * would have already returned.
+ */
+ bitbang_enabled = 0;
+ gpio_reset(bitbang_config.tx_gpio);
+ gpio_reset(bitbang_config.rx_gpio);
+
+ /* Unregister the function pointers. */
+ uartn_funcs[uart]._rx_available = _uartn_rx_available;
+ uartn_funcs[uart]._write_char = _uartn_write_char;
+ uartn_funcs[uart]._read_char = _uartn_read_char;
+
+ /* Gate the microsecond timer since we're done with it. */
+ pmu_clock_dis(PERIPH_TIMEUS);
+
+ /* Reconnect the GPIO to the UART block. */
+ gpio_disable_interrupt(bitbang_config.rx_gpio);
+ uartn_tx_connect(uart);
+ return EC_SUCCESS;
+}
+
+static void wait_ticks(uint32_t ticks)
+{
+ uint32_t t0 = GR_TIMEUS_CUR_MAJOR(0);
+
+ while ((GR_TIMEUS_CUR_MAJOR(0) - t0) < ticks)
+ ;
+}
+
+void uart_bitbang_write_char(int uart, char c)
+{
+ int val;
+ int ones;
+ int i;
+
+ if (!uart_bitbang_is_enabled(uart))
+ return;
+
+ interrupt_disable();
+
+ /* Start bit. */
+ gpio_set_level(tx_pin, 0);
+ wait_ticks(bit_period_ticks);
+
+ /* 8 data bits. */
+ ones = 0;
+ for (i = 0; i < 8; i++) {
+ val = (c & (1 << i));
+ /* Count 1's in order to handle parity bit. */
+ if (val)
+ ones++;
+ gpio_set_level(tx_pin, val);
+ wait_ticks(bit_period_ticks);
+ }
+
+ /* Optional parity. */
+ switch (set_parity) {
+ case 1: /* odd parity */
+ if (ones & 0x1)
+ gpio_set_level(tx_pin, 0);
+ else
+ gpio_set_level(tx_pin, 1);
+ wait_ticks(bit_period_ticks);
+ break;
+
+ case 2: /* even parity */
+ if (ones & 0x1)
+ gpio_set_level(tx_pin, 1);
+ else
+ gpio_set_level(tx_pin, 0);
+ wait_ticks(bit_period_ticks);
+ break;
+
+ case 0: /* no parity */
+ default:
+ break;
+ };
+
+ /* 1 stop bit. */
+ gpio_set_level(tx_pin, 1);
+ wait_ticks(bit_period_ticks);
+ interrupt_enable();
+}
+
+int uart_bitbang_receive_char(int uart)
+{
+ uint8_t rx_char;
+ int i;
+ int rv;
+ int ones;
+ int parity_bit;
+ int stop_bit;
+ uint8_t head;
+ uint8_t tail;
+
+ /* Disable interrupts so that we aren't interrupted. */
+ interrupt_disable();
+ rx_buff_rx_char_cnt++;
+ rv = EC_SUCCESS;
+
+ rx_char = 0;
+
+ /* Wait 1 bit period for the start bit. */
+ wait_ticks(bit_period_ticks);
+
+ /* 8 data bits. */
+ ones = 0;
+ for (i = 0; i < 8; i++) {
+ if (gpio_get_level(rx_pin)) {
+ ones++;
+ rx_char |= (1 << i);
+ }
+ wait_ticks(bit_period_ticks);
+ }
+
+ /* optional parity or stop bit. */
+ parity_bit = gpio_get_level(rx_pin);
+ if (set_parity) {
+ wait_ticks(bit_period_ticks);
+ stop_bit = gpio_get_level(rx_pin);
+ } else {
+ /* If there's no parity, that _was_ the stop bit. */
+ stop_bit = parity_bit;
+ }
+
+ /* Check the parity if necessary. */
+ switch (set_parity) {
+ case 2: /* even parity */
+ if (ones & 0x1)
+ rv = parity_bit ? EC_SUCCESS : EC_ERROR_CRC;
+ else
+ rv = parity_bit ? EC_ERROR_CRC : EC_SUCCESS;
+ break;
+
+ case 1: /* odd parity */
+ if (ones & 0x1)
+ rv = parity_bit ? EC_ERROR_CRC : EC_SUCCESS;
+ else
+ rv = parity_bit ? EC_SUCCESS : EC_ERROR_CRC;
+ break;
+
+ case 0:
+ default:
+ break;
+ }
+
+ if (rv != EC_SUCCESS) {
+ parity_err_cnt++;
+ parity_err_discard[parity_discard_idx] = rx_char;
+ parity_discard_idx = (parity_discard_idx + 1) % DISCARD_LOG;
+ }
+
+ /* Check that the stop bit is valid. */
+ if (stop_bit != 1) {
+ rv = EC_ERROR_CRC;
+ stop_bit_err_cnt++;
+ stop_bit_discard[stop_bit_discard_idx] = rx_char;
+ stop_bit_discard_idx = (stop_bit_discard_idx + 1) % DISCARD_LOG;
+ }
+
+ if (rv != EC_SUCCESS) {
+ interrupt_enable();
+ return rv;
+ }
+
+ /* Place the received char in the RX buffer. */
+ head = bitbang_config.htp.head;
+ tail = bitbang_config.htp.tail;
+ if (BUF_NEXT(tail) != head) {
+ rx_buf[tail] = rx_char;
+ bitbang_config.htp.tail = BUF_NEXT(tail);
+ rx_buff_inserted_cnt++;
+ }
+
+ interrupt_enable();
+ return EC_SUCCESS;
+}
+
+int uart_bitbang_read_char(int uart)
+{
+ int c;
+ uint8_t head;
+
+ if (!is_uart_allowed(uart))
+ return 0;
+
+ head = bitbang_config.htp.head;
+ c = rx_buf[head];
+
+ if (head != bitbang_config.htp.tail)
+ bitbang_config.htp.head = BUF_NEXT(head);
+
+ read_char_cnt++;
+ return c;
+}
+
+int uart_bitbang_is_char_available(int uart)
+{
+ if (!is_uart_allowed(uart))
+ return 0;
+
+ return bitbang_config.htp.head != bitbang_config.htp.tail;
+}
+
+static int command_bitbang_dump_stats(int argc, char **argv)
+{
+ int i;
+
+ if (argc == 2) {
+ /* Clear the counters. */
+ if (!strncasecmp(argv[1], "clear", 5)) {
+ parity_err_cnt = 0;
+ stop_bit_err_cnt = 0;
+ rx_buff_rx_char_cnt = 0;
+ read_char_cnt = 0;
+ rx_buff_inserted_cnt = 0;
+ return EC_SUCCESS;
+ }
+ return EC_ERROR_PARAM1;
+ }
+
+ ccprintf("Errors:\n");
+ ccprintf("%d Parity Errors\n", parity_err_cnt);
+ ccprintf("%d Stop Bit Errors\n", stop_bit_err_cnt);
+ ccprintf("Buffer info\n");
+ ccprintf("%d received\n", rx_buff_rx_char_cnt);
+ ccprintf("%d chars inserted\n", rx_buff_inserted_cnt);
+ ccprintf("%d chars read\n", read_char_cnt);
+ ccprintf("Contents\n");
+ ccprintf("[");
+ for (i = 0; i < RX_BUF_SIZE; i++)
+ ccprintf(" %02x ", rx_buf[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("head: %d\ntail: %d\n",
+ bitbang_config.htp.head,
+ bitbang_config.htp.tail);
+ ccprintf("Discards\nparity: ");
+ ccprintf("[");
+ for (i = 0; i < DISCARD_LOG; i++)
+ ccprintf(" %02x ", parity_err_discard[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("idx: %d\n", parity_discard_idx);
+ ccprintf("stop bit: ");
+ ccprintf("[");
+ for (i = 0; i < DISCARD_LOG; i++)
+ ccprintf(" %02x ", stop_bit_discard[i] & 0xFF);
+ ccprintf("]\n");
+ ccprintf("idx: %d\n", stop_bit_discard_idx);
+ cflush();
+ return EC_SUCCESS;
+}
+DECLARE_CONSOLE_COMMAND(bbstats, command_bitbang_dump_stats,
+ "",
+ "dumps bitbang stats");
diff --git a/chip/g/uart_bitbang.h b/chip/g/uart_bitbang.h
new file mode 100644
index 0000000000..f29c35dd34
--- /dev/null
+++ b/chip/g/uart_bitbang.h
@@ -0,0 +1,112 @@
+/* Copyright 2017 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_CHIP_G_UART_BITBANG_H
+#define __CROS_EC_CHIP_G_UART_BITBANG_H
+
+/* UART Bit Banging */
+
+#include "common.h"
+#include "gpio.h"
+
+/* These are functions that we'll have to replace. */
+struct uartn_function_ptrs {
+ int (*_rx_available)(int uart);
+ void (*_write_char)(int uart, char c);
+ int (*_read_char)(int uart);
+};
+
+/*
+ * And these are the function definitions. The functions live in
+ * chip/g/uartn.c.
+ */
+extern int _uartn_rx_available(int uart);
+extern void _uartn_write_char(int uart, char c);
+extern int _uartn_read_char(int uart);
+extern int _uart_bitbang_rx_available(int uart);
+extern void _uart_bitbang_write_char(int uart, char c);
+extern int _uart_bitbang_read_char(int uart);
+extern struct uartn_function_ptrs uartn_funcs[];
+
+struct uart_bitbang_properties {
+ enum gpio_signal tx_gpio;
+ enum gpio_signal rx_gpio;
+ uint32_t tx_pinmux_reg;
+ uint32_t tx_pinmux_regval;
+ uint32_t rx_pinmux_reg;
+ uint32_t rx_pinmux_regval;
+ int baud_rate;
+ uint8_t uart;
+ struct {
+ unsigned int head : 3;
+ unsigned int tail : 3;
+ unsigned int parity : 2;
+ } htp __packed;
+};
+
+/* In order to bitbang a UART, a board must define a bitbang_config. */
+extern struct uart_bitbang_properties bitbang_config;
+
+/**
+ * Enable bit banging mode for a UART.
+ *
+ * @param uart: Index of UART to enable bit banging mode.
+ * @param baud_rate: desired baud rate.
+ * @param parity: 0: no parity, 1: odd parity, 2: even parity.
+ *
+ * @returns EC_SUCCESS on success, otherwise an error.
+ */
+int uart_bitbang_enable(int uart, int baud_rate, int parity);
+
+/**
+ * Disable bit banging mode for a UART.
+ *
+ * @param uart: Index of UART to disable bit banging mode.
+ */
+int uart_bitbang_disable(int uart);
+
+/**
+ * Returns 1 if bit banging mode is enabled for the UART.
+ *
+ * @param uart: Index of UART to query.
+ */
+int uart_bitbang_is_enabled(int uart);
+
+/**
+ * TX a character on a UART configured for bit banging mode.
+ *
+ * @param uart: Index of UART to use.
+ * @param c: Character to send out.
+ */
+void uart_bitbang_write_char(int uart, char c);
+
+/**
+ * Sample the RX line on a UART configured for bit banging mode.
+ *
+ * This is called when a falling edge is seen on the RX line and will attempt to
+ * receive a character. Incoming data with framing errors or parity errors will
+ * be discarded.
+ *
+ * @param uart: Index of UART to use.
+ * @returns EC_SUCCESS if a character was successfully received, EC_ERROR_CRC if
+ * there was a framing or parity issue.
+ */
+int uart_bitbang_receive_char(int uart);
+
+/**
+ * Returns 1 if there are characters available for consumption, otherwise 0.
+ *
+ * @param uart: Index of UART to check.
+ */
+int uart_bitbang_is_char_available(int uart);
+
+/**
+ * Retrieve a character from the bit bang RX buffer.
+ *
+ * @param uart: Index of UART to use.
+ */
+int uart_bitbang_read_char(int uart);
+
+#endif /* __CROS_EC_CHIP_G_UART_BITBANG_H */
diff --git a/chip/g/uartn.c b/chip/g/uartn.c
index 369ec17a59..6140e8cff4 100644
--- a/chip/g/uartn.c
+++ b/chip/g/uartn.c
@@ -10,6 +10,7 @@
#include "system.h"
#include "task.h"
#include "uart.h"
+#include "uart_bitbang.h"
#include "util.h"
#define USE_UART_INTERRUPTS (!(defined(CONFIG_CUSTOMIZED_RO) && \
@@ -25,6 +26,26 @@ static struct uartn_interrupts interrupt[] = {
{GC_IRQNUM_UART2_TXINT, GC_IRQNUM_UART2_RXINT},
};
+struct uartn_function_ptrs uartn_funcs[3] = {
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+
+ {
+ _uartn_rx_available,
+ _uartn_write_char,
+ _uartn_read_char,
+ },
+};
+
void uartn_tx_start(int uart)
{
if (!uart_init_done())
@@ -92,13 +113,18 @@ int uartn_tx_ready(int uart)
return !(GR_UART_STATE(uart) & GC_UART_STATE_TX_MASK);
}
-int uartn_rx_available(int uart)
+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)
+int uartn_rx_available(int uart)
+{
+ return uartn_funcs[uart]._rx_available(uart);
+}
+
+void _uartn_write_char(int uart, char c)
{
/* Wait for space in transmit FIFO. */
while (!uartn_tx_ready(uart))
@@ -107,11 +133,45 @@ void uartn_write_char(int uart, char c)
GR_UART_WDATA(uart) = c;
}
-int uartn_read_char(int uart)
+void uartn_write_char(int uart, char c)
+{
+ uartn_funcs[uart]._write_char(uart, c);
+}
+
+int _uartn_read_char(int uart)
{
return GR_UART_RDATA(uart);
}
+int uartn_read_char(int uart)
+{
+ return uartn_funcs[uart]._read_char(uart);
+}
+
+#ifdef CONFIG_UART_BITBANG
+int _uart_bitbang_rx_available(int uart)
+{
+ if (uart_bitbang_is_enabled(uart))
+ return uart_bitbang_is_char_available(uart);
+
+ return 0;
+}
+
+void _uart_bitbang_write_char(int uart, char c)
+{
+ if (uart_bitbang_is_enabled(uart))
+ uart_bitbang_write_char(uart, c);
+}
+
+int _uart_bitbang_read_char(int uart)
+{
+ if (uart_bitbang_is_enabled(uart))
+ return uart_bitbang_read_char(uart);
+
+ return 0;
+}
+#endif /* defined(CONFIG_UART_BITBANG) */
+
void uartn_disable_interrupt(int uart)
{
task_disable_irq(interrupt[uart].tx_int);
diff --git a/include/config.h b/include/config.h
index f628673bd1..5174c655e4 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2215,6 +2215,9 @@
/* Baud rate for UARTs */
#define CONFIG_UART_BAUD_RATE 115200
+/* Allow bit banging of a UARTs pins and bypassing the UART block. */
+#undef CONFIG_UART_BITBANG
+
/* UART index (number) for EC console */
#undef CONFIG_UART_CONSOLE