summaryrefslogtreecommitdiffstats
path: root/bsps/arm/lpc32xx/console/hsu.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsps/arm/lpc32xx/console/hsu.c')
-rw-r--r--bsps/arm/lpc32xx/console/hsu.c208
1 files changed, 208 insertions, 0 deletions
diff --git a/bsps/arm/lpc32xx/console/hsu.c b/bsps/arm/lpc32xx/console/hsu.c
new file mode 100644
index 0000000000..b2044e0753
--- /dev/null
+++ b/bsps/arm/lpc32xx/console/hsu.c
@@ -0,0 +1,208 @@
+/**
+ * @file
+ *
+ * @ingroup arm_lpc32xx
+ *
+ * @brief High speed UART driver (14-clock).
+ */
+
+/*
+ * Copyright (c) 2010-2014 embedded brains GmbH. All rights reserved.
+ *
+ * embedded brains GmbH
+ * Dornierstr. 4
+ * 82178 Puchheim
+ * Germany
+ * <rtems@embedded-brains.de>
+ *
+ * The license and distribution terms for this file may be
+ * found in the file LICENSE in this distribution or at
+ * http://www.rtems.org/license/LICENSE.
+ */
+
+#include <bsp.h>
+#include <bsp/lpc32xx.h>
+#include <bsp/irq.h>
+#include <bsp/hsu.h>
+
+#define HSU_FIFO_SIZE 64
+
+#define HSU_LEVEL_RX_MASK 0xffU
+#define HSU_LEVEL_TX_MASK 0xff00U
+#define HSU_LEVEL_TX_SHIFT 8
+
+#define HSU_RX_DATA_MASK 0xffU
+#define HSU_RX_EMPTY (1U << 8)
+#define HSU_RX_ERROR (1U << 9)
+#define HSU_RX_BREAK (1U << 10)
+
+#define HSU_IIR_TX (1U << 0)
+#define HSU_IIR_RX_TRIG (1U << 1)
+#define HSU_IIR_RX_TIMEOUT (1U << 2)
+
+#define HSU_CTRL_INTR_DISABLED 0x1280fU
+#define HSU_CTRL_RX_INTR_ENABLED 0x1284fU
+#define HSU_CTRL_RX_AND_TX_INTR_ENABLED 0x1286fU
+
+/* We are interested in RX timeout, RX trigger and TX trigger interrupts */
+#define HSU_IIR_MASK 0x7U
+
+bool lpc32xx_hsu_probe(rtems_termios_device_context *base)
+{
+ lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+
+ hsu->ctrl = HSU_CTRL_INTR_DISABLED;
+
+ /* Drain FIFOs */
+ while (hsu->level != 0) {
+ hsu->fifo;
+ }
+
+ return true;
+}
+
+static void lpc32xx_hsu_interrupt_handler(void *arg)
+{
+ rtems_termios_tty *tty = arg;
+ lpc32xx_hsu_context *ctx = rtems_termios_get_device_context(tty);
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+
+ /* Iterate until no more interrupts are pending */
+ do {
+ int rv = 0;
+ int i = 0;
+ char buf [HSU_FIFO_SIZE];
+
+ /* Enqueue received characters */
+ while (i < HSU_FIFO_SIZE) {
+ uint32_t in = hsu->fifo;
+
+ if ((in & HSU_RX_EMPTY) == 0) {
+ if ((in & HSU_RX_BREAK) == 0) {
+ buf [i] = in & HSU_RX_DATA_MASK;
+ ++i;
+ }
+ } else {
+ break;
+ }
+ }
+ rtems_termios_enqueue_raw_characters(tty, buf, i);
+
+ /* Dequeue transmitted characters */
+ rv = rtems_termios_dequeue_characters(tty, (int) ctx->chars_in_transmission);
+ if (rv == 0) {
+ /* Nothing to transmit */
+ }
+ } while ((hsu->iir & HSU_IIR_MASK) != 0);
+}
+
+static bool lpc32xx_hsu_first_open(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ struct termios *term,
+ rtems_libio_open_close_args_t *args
+)
+{
+ lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+ rtems_status_code sc;
+ bool ok;
+
+ sc = rtems_interrupt_handler_install(
+ ctx->irq,
+ "HSU",
+ RTEMS_INTERRUPT_UNIQUE,
+ lpc32xx_hsu_interrupt_handler,
+ tty
+ );
+ ok = sc == RTEMS_SUCCESSFUL;
+
+ if (ok) {
+ rtems_termios_set_initial_baud(tty, ctx->initial_baud);
+ hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
+ }
+
+ return ok;
+}
+
+static void lpc32xx_hsu_last_close(
+ struct rtems_termios_tty *tty,
+ rtems_termios_device_context *base,
+ rtems_libio_open_close_args_t *args
+)
+{
+ lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+
+ hsu->ctrl = HSU_CTRL_INTR_DISABLED;
+
+ rtems_interrupt_handler_remove(
+ ctx->irq,
+ lpc32xx_hsu_interrupt_handler,
+ tty
+ );
+}
+
+static void lpc32xx_hsu_write(
+ rtems_termios_device_context *base,
+ const char *buf,
+ size_t len
+)
+{
+ lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+ size_t tx_level = (hsu->level & HSU_LEVEL_TX_MASK) >> HSU_LEVEL_TX_SHIFT;
+ size_t tx_free = HSU_FIFO_SIZE - tx_level;
+ size_t i = 0;
+ size_t out = len > tx_free ? tx_free : len;
+
+ for (i = 0; i < out; ++i) {
+ hsu->fifo = buf [i];
+ }
+
+ ctx->chars_in_transmission = out;
+
+ if (len > 0) {
+ hsu->ctrl = HSU_CTRL_RX_AND_TX_INTR_ENABLED;
+ } else {
+ hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
+ hsu->iir = HSU_IIR_TX;
+ }
+}
+
+static bool lpc32xx_hsu_set_attributes(
+ rtems_termios_device_context *base,
+ const struct termios *term
+)
+{
+ lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
+ volatile lpc32xx_hsu *hsu = ctx->hsu;
+ int baud_flags = term->c_ospeed;
+
+ if (baud_flags != 0) {
+ int32_t baud = rtems_termios_baud_to_number(baud_flags);
+
+ if (baud > 0) {
+ uint32_t baud_divisor = 14 * (uint32_t) baud;
+ uint32_t rate = LPC32XX_PERIPH_CLK / baud_divisor;
+ uint32_t remainder = LPC32XX_PERIPH_CLK - rate * baud_divisor;
+
+ if (2 * remainder >= baud_divisor) {
+ ++rate;
+ }
+
+ hsu->rate = rate - 1;
+ }
+ }
+
+ return true;
+}
+
+const rtems_termios_device_handler lpc32xx_hsu_fns = {
+ .first_open = lpc32xx_hsu_first_open,
+ .last_close = lpc32xx_hsu_last_close,
+ .write = lpc32xx_hsu_write,
+ .set_attributes = lpc32xx_hsu_set_attributes,
+ .mode = TERMIOS_IRQ_DRIVEN
+};