source: rtems/c/src/lib/libbsp/arm/xilinx-zynq/console/zynq-uart.c @ 11f0d52

5
Last change on this file since 11f0d52 was 11f0d52, checked in by Sebastian Huber <sebastian.huber@…>, on Feb 22, 2017 at 8:01:40 AM

bsp/xilinx-zynq: Add interrupt support to UART

  • Property mode set to 100644
File size: 7.8 KB
Line 
1/*
2 * Copyright (c) 2013, 2017 embedded brains GmbH.  All rights reserved.
3 *
4 *  embedded brains GmbH
5 *  Dornierstr. 4
6 *  82178 Puchheim
7 *  Germany
8 *  <info@embedded-brains.de>
9 *
10 * The license and distribution terms for this file may be
11 * found in the file LICENSE in this distribution or at
12 * http://www.rtems.org/license/LICENSE.
13 */
14
15#include <bsp/zynq-uart.h>
16#include <bsp/zynq-uart-regs.h>
17#include <bsp/irq.h>
18
19#include <bspopts.h>
20
21/*
22 * Make weak and let the user override.
23 */
24uint32_t zynq_uart_input_clock(void) __attribute__ ((weak));
25
26uint32_t zynq_uart_input_clock(void)
27{
28  return ZYNQ_CLOCK_UART;
29}
30
31static int zynq_cal_baud_rate(uint32_t  baudrate,
32                              uint32_t* brgr,
33                              uint32_t* bauddiv,
34                              uint32_t  modereg)
35{
36  uint32_t brgr_value;    /* Calculated value for baud rate generator */
37  uint32_t calcbaudrate;  /* Calculated baud rate */
38  uint32_t bauderror;     /* Diff between calculated and requested baud rate */
39  uint32_t best_error = 0xFFFFFFFF;
40  uint32_t percenterror;
41  uint32_t bdiv;
42  uint32_t inputclk = zynq_uart_input_clock();
43
44  /*
45   * Make sure the baud rate is not impossilby large.
46   * Fastest possible baud rate is Input Clock / 2.
47   */
48  if ((baudrate * 2) > inputclk) {
49    return -1;
50  }
51  /*
52   * Check whether the input clock is divided by 8
53   */
54  if(modereg & ZYNQ_UART_MODE_CLKS) {
55    inputclk = inputclk / 8;
56  }
57
58  /*
59   * Determine the Baud divider. It can be 4to 254.
60   * Loop through all possible combinations
61   */
62  for (bdiv = 4; bdiv < 255; bdiv++) {
63
64    /*
65     * Calculate the value for BRGR register
66     */
67    brgr_value = inputclk / (baudrate * (bdiv + 1));
68
69    /*
70     * Calculate the baud rate from the BRGR value
71     */
72    calcbaudrate = inputclk/ (brgr_value * (bdiv + 1));
73
74    /*
75     * Avoid unsigned integer underflow
76     */
77    if (baudrate > calcbaudrate) {
78      bauderror = baudrate - calcbaudrate;
79    }
80    else {
81      bauderror = calcbaudrate - baudrate;
82    }
83
84    /*
85     * Find the calculated baud rate closest to requested baud rate.
86     */
87    if (best_error > bauderror) {
88      *brgr = brgr_value;
89      *bauddiv = bdiv;
90      best_error = bauderror;
91    }
92  }
93
94  /*
95   * Make sure the best error is not too large.
96   */
97  percenterror = (best_error * 100) / baudrate;
98#define XUARTPS_MAX_BAUD_ERROR_RATE              3      /* max % error allowed */
99  if (XUARTPS_MAX_BAUD_ERROR_RATE < percenterror) {
100    return -1;
101  }
102
103  return 0;
104}
105
106void zynq_uart_initialize(rtems_termios_device_context *base)
107{
108  zynq_uart_context *ctx = (zynq_uart_context *) base;
109  volatile zynq_uart *regs = ctx->regs;
110  uint32_t brgr = 0x3e;
111  uint32_t bauddiv = 0x6;
112
113  zynq_cal_baud_rate(ZYNQ_UART_DEFAULT_BAUD, &brgr, &bauddiv, regs->mode);
114
115  regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN);
116  regs->control = ZYNQ_UART_CONTROL_RXDIS
117    | ZYNQ_UART_CONTROL_TXDIS
118    | ZYNQ_UART_CONTROL_RXRES
119    | ZYNQ_UART_CONTROL_TXRES;
120  regs->mode = ZYNQ_UART_MODE_CHMODE(ZYNQ_UART_MODE_CHMODE_NORMAL)
121    | ZYNQ_UART_MODE_PAR(ZYNQ_UART_MODE_PAR_NONE)
122    | ZYNQ_UART_MODE_CHRL(ZYNQ_UART_MODE_CHRL_8);
123  regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr);
124  regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv);
125  regs->rx_fifo_trg_lvl = ZYNQ_UART_RX_FIFO_TRG_LVL_RTRIG(0);
126  regs->rx_timeout = ZYNQ_UART_RX_TIMEOUT_RTO(0);
127  regs->control = ZYNQ_UART_CONTROL_RXEN
128    | ZYNQ_UART_CONTROL_TXEN
129    | ZYNQ_UART_CONTROL_RSTTO;
130}
131
132#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
133static void zynq_uart_interrupt(void *arg)
134{
135  rtems_termios_tty *tty = arg;
136  zynq_uart_context *ctx = rtems_termios_get_device_context(tty);
137  volatile zynq_uart *regs = ctx->regs;
138  uint32_t channel_sts;
139
140  if ((regs->irq_sts & (ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG)) != 0) {
141    regs->irq_sts = ZYNQ_UART_TIMEOUT | ZYNQ_UART_RTRIG;
142
143    do {
144      char c = (char) ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo);
145
146      rtems_termios_enqueue_raw_characters(tty, &c, 1);
147
148      channel_sts = regs->channel_sts;
149    } while ((channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) == 0);
150  } else {
151    channel_sts = regs->channel_sts;
152  }
153
154  if (ctx->transmitting && (channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) != 0) {
155    rtems_termios_dequeue_characters(tty, 1);
156  }
157}
158#endif
159
160static bool zynq_uart_first_open(
161  rtems_termios_tty *tty,
162  rtems_termios_device_context *base,
163  struct termios *term,
164  rtems_libio_open_close_args_t *args
165)
166{
167#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
168  zynq_uart_context *ctx = (zynq_uart_context *) base;
169  volatile zynq_uart *regs = ctx->regs;
170  rtems_status_code sc;
171#endif
172
173  rtems_termios_set_initial_baud(tty, ZYNQ_UART_DEFAULT_BAUD);
174  zynq_uart_initialize(base);
175
176#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
177  regs->rx_timeout = 32;
178  regs->rx_fifo_trg_lvl = ZYNQ_UART_FIFO_DEPTH / 2;
179  regs->irq_dis = 0xffffffff;
180  regs->irq_sts = 0xffffffff;
181  regs->irq_en = ZYNQ_UART_RTRIG | ZYNQ_UART_TIMEOUT;
182  sc = rtems_interrupt_handler_install(
183    ctx->irq,
184    "UART",
185    RTEMS_INTERRUPT_SHARED,
186    zynq_uart_interrupt,
187    tty
188  );
189  if (sc != RTEMS_SUCCESSFUL) {
190    return false;
191  }
192#endif
193
194  return true;
195}
196
197#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
198static void zynq_uart_last_close(
199  rtems_termios_tty *tty,
200  rtems_termios_device_context *base,
201  rtems_libio_open_close_args_t *args
202)
203{
204  zynq_uart_context *ctx = (zynq_uart_context *) base;
205
206  rtems_interrupt_handler_remove(ctx->irq, zynq_uart_interrupt, tty);
207}
208#endif
209
210int zynq_uart_read_polled(rtems_termios_device_context *base)
211{
212  zynq_uart_context *ctx = (zynq_uart_context *) base;
213  volatile zynq_uart *regs = ctx->regs;
214
215  if ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_REMPTY) != 0) {
216    return -1;
217  } else {
218    return ZYNQ_UART_TX_RX_FIFO_FIFO_GET(regs->tx_rx_fifo);
219  }
220}
221
222void zynq_uart_write_polled(
223  rtems_termios_device_context *base,
224  char c
225)
226{
227  zynq_uart_context *ctx = (zynq_uart_context *) base;
228  volatile zynq_uart *regs = ctx->regs;
229
230  while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TFUL) != 0) {
231    /* Wait */
232  }
233
234  regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(c);
235}
236
237static void zynq_uart_write_support(
238  rtems_termios_device_context *base,
239  const char *buf,
240  size_t len
241)
242{
243#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
244  zynq_uart_context *ctx = (zynq_uart_context *) base;
245  volatile zynq_uart *regs = ctx->regs;
246
247  if (len > 0) {
248    ctx->transmitting = true;
249    regs->irq_sts = ZYNQ_UART_TEMPTY;
250    regs->irq_en = ZYNQ_UART_TEMPTY;
251    regs->tx_rx_fifo = ZYNQ_UART_TX_RX_FIFO_FIFO(buf[0]);
252  } else {
253    ctx->transmitting = false;
254    regs->irq_dis = ZYNQ_UART_TEMPTY;
255  }
256#else
257  ssize_t i;
258
259  for (i = 0; i < len; ++i) {
260    zynq_uart_write_polled(base, buf[i]);
261  }
262#endif
263}
264
265static bool zynq_uart_set_attributes(
266  rtems_termios_device_context *context,
267  const struct termios *term
268)
269{
270#if 0
271  volatile zynq_uart *regs = zynq_uart_get_regs(minor);
272  uint32_t brgr = 0;
273  uint32_t bauddiv = 0;
274  int rc;
275
276  rc = zynq_cal_baud_rate(115200, &brgr, &bauddiv, regs->mode);
277  if (rc != 0)
278    return rc;
279
280  regs->control &= ~(ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN);
281  regs->baud_rate_gen = ZYNQ_UART_BAUD_RATE_GEN_CD(brgr);
282  regs->baud_rate_div = ZYNQ_UART_BAUD_RATE_DIV_BDIV(bauddiv);
283  regs->control |= ZYNQ_UART_CONTROL_RXEN | ZYNQ_UART_CONTROL_TXEN;
284
285  return true;
286#else
287  return false;
288#endif
289}
290
291const rtems_termios_device_handler zynq_uart_handler = {
292  .first_open = zynq_uart_first_open,
293  .set_attributes = zynq_uart_set_attributes,
294  .write = zynq_uart_write_support,
295#ifdef ZYNQ_CONSOLE_USE_INTERRUPTS
296  .last_close = zynq_uart_last_close,
297  .mode = TERMIOS_IRQ_DRIVEN
298#else
299  .poll_read = zynq_uart_read_polled,
300  .mode = TERMIOS_POLLED
301#endif
302};
303
304void zynq_uart_reset_tx_flush(zynq_uart_context *ctx)
305{
306  volatile zynq_uart *regs = ctx->regs;
307  int                 c = 4;
308
309  while (c-- > 0)
310    zynq_uart_write_polled(&ctx->base, '\r');
311
312  while ((regs->channel_sts & ZYNQ_UART_CHANNEL_STS_TEMPTY) == 0) {
313    /* Wait */
314  }
315}
Note: See TracBrowser for help on using the repository browser.