source: rtems/c/src/lib/libbsp/arm/lpc32xx/console/hsu.c @ 6ec438e

4.11
Last change on this file since 6ec438e was 6ec438e, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 7, 2014 at 6:29:16 AM

libchip/serial: Add alternative NS16550 driver

Use the Termios device API.

  • Property mode set to 100644
File size: 4.7 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup arm_lpc32xx
5 *
6 * @brief High speed UART driver (14-clock).
7 */
8
9/*
10 * Copyright (c) 2010-2014 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Dornierstr. 4
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.org/license/LICENSE.
21 */
22
23#include <bsp.h>
24#include <bsp/lpc32xx.h>
25#include <bsp/irq.h>
26#include <bsp/hsu.h>
27
28#define HSU_FIFO_SIZE 64
29
30#define HSU_LEVEL_RX_MASK 0xffU
31#define HSU_LEVEL_TX_MASK 0xff00U
32#define HSU_LEVEL_TX_SHIFT 8
33
34#define HSU_RX_DATA_MASK 0xffU
35#define HSU_RX_EMPTY (1U << 8)
36#define HSU_RX_ERROR (1U << 9)
37#define HSU_RX_BREAK (1U << 10)
38
39#define HSU_IIR_TX (1U << 0)
40#define HSU_IIR_RX_TRIG (1U << 1)
41#define HSU_IIR_RX_TIMEOUT (1U << 2)
42
43#define HSU_CTRL_INTR_DISABLED 0x1280fU
44#define HSU_CTRL_RX_INTR_ENABLED 0x1284fU
45#define HSU_CTRL_RX_AND_TX_INTR_ENABLED 0x1286fU
46
47/* We are interested in RX timeout, RX trigger and TX trigger interrupts */
48#define HSU_IIR_MASK 0x7U
49
50bool lpc32xx_hsu_probe(rtems_termios_device_context *base)
51{
52  lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
53  volatile lpc32xx_hsu *hsu = ctx->hsu;
54
55  hsu->ctrl = HSU_CTRL_INTR_DISABLED;
56
57  /* Drain FIFOs */
58  while (hsu->level != 0) {
59    hsu->fifo;
60  }
61
62  return true;
63}
64
65static void lpc32xx_hsu_interrupt_handler(void *arg)
66{
67  rtems_termios_tty *tty = arg;
68  lpc32xx_hsu_context *ctx = rtems_termios_get_device_context(tty);
69  volatile lpc32xx_hsu *hsu = ctx->hsu;
70
71  /* Iterate until no more interrupts are pending */
72  do {
73    int rv = 0;
74    int i = 0;
75    char buf [HSU_FIFO_SIZE];
76
77    /* Enqueue received characters */
78    while (i < HSU_FIFO_SIZE) {
79      uint32_t in = hsu->fifo;
80
81      if ((in & HSU_RX_EMPTY) == 0) {
82        if ((in & HSU_RX_BREAK) == 0) {
83          buf [i] = in & HSU_RX_DATA_MASK;
84          ++i;
85        }
86      } else {
87        break;
88      }
89    }
90    rtems_termios_enqueue_raw_characters(tty, buf, i);
91
92    /* Dequeue transmitted characters */
93    rv = rtems_termios_dequeue_characters(tty, (int) ctx->chars_in_transmission);
94    if (rv == 0) {
95      /* Nothing to transmit */
96    }
97  } while ((hsu->iir & HSU_IIR_MASK) != 0);
98}
99
100static bool lpc32xx_hsu_first_open(
101  struct rtems_termios_tty *tty,
102  rtems_termios_device_context *base,
103  struct termios *term,
104  rtems_libio_open_close_args_t *args
105)
106{
107  lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
108  volatile lpc32xx_hsu *hsu = ctx->hsu;
109  rtems_status_code sc;
110  bool ok;
111
112  sc = rtems_interrupt_handler_install(
113    ctx->irq,
114    "HSU",
115    RTEMS_INTERRUPT_UNIQUE,
116    lpc32xx_hsu_interrupt_handler,
117    tty
118  );
119  ok = sc == RTEMS_SUCCESSFUL;
120
121  if (ok) {
122    rtems_termios_set_initial_baud(tty, ctx->initial_baud);
123    hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
124  }
125
126  return ok;
127}
128
129static void lpc32xx_hsu_last_close(
130  struct rtems_termios_tty *tty,
131  rtems_termios_device_context *base,
132  rtems_libio_open_close_args_t *args
133)
134{
135  lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
136  volatile lpc32xx_hsu *hsu = ctx->hsu;
137
138  hsu->ctrl = HSU_CTRL_INTR_DISABLED;
139
140  rtems_interrupt_handler_remove(
141    ctx->irq,
142    lpc32xx_hsu_interrupt_handler,
143    tty
144  );
145}
146
147static void lpc32xx_hsu_write(
148  rtems_termios_device_context *base,
149  const char *buf,
150  size_t len
151)
152{
153  lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
154  volatile lpc32xx_hsu *hsu = ctx->hsu;
155  size_t tx_level = (hsu->level & HSU_LEVEL_TX_MASK) >> HSU_LEVEL_TX_SHIFT;
156  size_t tx_free = HSU_FIFO_SIZE - tx_level;
157  size_t i = 0;
158  size_t out = len > tx_free ? tx_free : len;
159
160  for (i = 0; i < out; ++i) {
161    hsu->fifo = buf [i];
162  }
163
164  ctx->chars_in_transmission = out;
165
166  if (len > 0) {
167    hsu->ctrl = HSU_CTRL_RX_AND_TX_INTR_ENABLED;
168  } else {
169    hsu->ctrl = HSU_CTRL_RX_INTR_ENABLED;
170    hsu->iir = HSU_IIR_TX;
171  }
172}
173
174static bool lpc32xx_hsu_set_attributes(
175  rtems_termios_device_context *base,
176  const struct termios *term
177)
178{
179  lpc32xx_hsu_context *ctx = (lpc32xx_hsu_context *) base;
180  volatile lpc32xx_hsu *hsu = ctx->hsu;
181  int baud_flags = term->c_cflag & CBAUD;
182
183  if (baud_flags != 0) {
184    int32_t baud = rtems_termios_baud_to_number(baud_flags);
185
186    if (baud > 0) {
187      uint32_t baud_divisor = 14 * (uint32_t) baud;
188      uint32_t rate = LPC32XX_PERIPH_CLK / baud_divisor;
189      uint32_t remainder = LPC32XX_PERIPH_CLK - rate * baud_divisor;
190
191      if (2 * remainder >= baud_divisor) {
192        ++rate;
193      }
194
195      hsu->rate = rate - 1;
196    }
197  }
198
199  return true;
200}
201
202const rtems_termios_device_handler lpc32xx_hsu_fns = {
203  .first_open = lpc32xx_hsu_first_open,
204  .last_close = lpc32xx_hsu_last_close,
205  .write = lpc32xx_hsu_write,
206  .set_attributes = lpc32xx_hsu_set_attributes,
207  .mode = TERMIOS_IRQ_DRIVEN
208};
Note: See TracBrowser for help on using the repository browser.