source: rtems/c/src/lib/libbsp/arm/tms570/console/tms570-sci.c @ 3e1196d

5
Last change on this file since 3e1196d was 3e1196d, checked in by Premysl Houdek <kom541000@…>, on Nov 18, 2015 at 5:45:32 PM

bsp/tms570: unite code duplication in pinmux and clean SCI close per review remarks.

Signed-off-by: Premysl Houdek <kom541000@…>

  • Property mode set to 100644
File size: 14.6 KB
Line 
1/**
2 * @file tms570-sci.c
3 *
4 * @ingroup tms570
5 *
6 * @brief Serial communication interface (SCI) functions definitions.
7 */
8
9/*
10 * Copyright (c) 2014 Premysl Houdek <kom541000@gmail.com>
11 *
12 * Google Summer of Code 2014 at
13 * Czech Technical University in Prague
14 * Zikova 1903/4
15 * 166 36 Praha 6
16 * Czech Republic
17 *
18 * Based on LPC24xx and LPC1768 BSP
19 * by embedded brains GmbH and others
20 *
21 * The license and distribution terms for this file may be
22 * found in the file LICENSE in this distribution or at
23 * http://www.rtems.org/license/LICENSE.
24 */
25
26#include <bspopts.h>
27#include <termios.h>
28#include <rtems/termiostypes.h>
29#include <bsp/tms570-sci.h>
30#include <bsp/tms570-sci-driver.h>
31#include <rtems/console.h>
32#include <bsp.h>
33#include <bsp/fatal.h>
34#include <bsp/irq.h>
35
36#define TMS570_SCI_BUFFER_SIZE 1
37
38/**
39 * @brief Table including all serial drivers
40 *
41 * Definitions of all serial drivers
42 */
43tms570_sci_context driver_context_table[] = {
44  {
45    .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI1"),
46    .device_name = "/dev/console",
47    /* TMS570 UART peripheral use subset of LIN registers which are equivalent
48     * to SCI ones
49     */
50    .regs = (volatile tms570_sci_t *) &TMS570_LIN,
51    .irq = TMS570_IRQ_SCI_LEVEL_0,
52  },
53  {
54    .base = RTEMS_TERMIOS_DEVICE_CONTEXT_INITIALIZER("TMS570 SCI2"),
55    .device_name = "/dev/ttyS1",
56    .regs = &TMS570_SCI,
57    .irq = TMS570_IRQ_SCI2_LEVEL_0,
58  }
59};
60
61/**
62 * @brief Serial drivers init function
63 *
64 * Initialize all serial drivers specified in driver_context_table
65 *
66 * @param[in] major
67 * @param[in] minor
68 * @param[in] arg
69 * @retval RTEMS_SUCCESSFUL Initialization completed
70 */
71rtems_device_driver console_initialize(
72  rtems_device_major_number  major,
73  rtems_device_minor_number  minor,
74  void                      *arg
75)
76{
77  rtems_status_code sc;
78#if CONSOLE_USE_INTERRUPTS
79  const rtems_termios_device_handler *handler = &tms570_sci_handler_interrupt;
80#else
81  const rtems_termios_device_handler *handler = &tms570_sci_handler_polled;
82#endif
83
84  /*
85   * Initialize the Termios infrastructure.  If Termios has already
86   * been initialized by another device driver, then this call will
87   * have no effect.
88   */
89  rtems_termios_initialize();
90
91  /* Initialize each device */
92  for (
93    minor = 0;
94    minor < RTEMS_ARRAY_SIZE(driver_context_table);
95    ++minor
96  ) {
97    tms570_sci_context *ctx = &driver_context_table[minor];
98
99    /*
100     * Install this device in the file system and Termios.  In order
101     * to use the console (i.e. being able to do printf, scanf etc.
102     * on stdin, stdout and stderr), one device must be registered as
103     * "/dev/console" (CONSOLE_DEVICE_NAME).
104     */
105    sc = rtems_termios_device_install(
106        ctx->device_name,
107        major,
108        minor,
109        handler,
110        NULL,
111        &ctx->base
112    );
113    if ( sc != RTEMS_SUCCESSFUL ) {
114      bsp_fatal(BSP_FATAL_CONSOLE_NO_DEV);
115    }
116  }
117  return RTEMS_SUCCESSFUL;
118}
119
120/**
121 * @brief Reads chars from HW
122 *
123 * Reads chars from HW peripheral specified in driver context.
124 * TMS570 does not have HW buffer for serial line so this function can
125 * return only 0 or 1 char
126 *
127 * @param[in] ctx context of the driver
128 * @param[out] buf read data buffer
129 * @param[in] N size of buffer
130 * @retval x Number of read chars from peripherals
131 */
132static int tms570_sci_read_received_chars(
133  tms570_sci_context * ctx,
134  char * buf,
135  int N)
136{
137  if ( N < 1 ) {
138    return 0;
139  }
140  if ( ctx->regs->RD != 0 ) {
141     buf[0] = ctx->regs->RD;
142    return 1;
143  }
144  return 0;
145}
146
147/**
148 * @brief Enables RX interrupt
149 *
150 * Enables RX interrupt source of SCI peripheral
151 * specified in the driver context.
152 *
153 * @param[in] ctx context of the driver
154 * @retval Void
155 */
156static void tms570_sci_enable_interrupts(tms570_sci_context * ctx)
157{
158  ctx->regs->SETINT = TMS570_SCI_SETINT_SET_RX_INT;
159}
160
161/**
162 * @brief Disables RX interrupt
163 *
164 * Disables RX interrupt source of SCI peripheral specified in the driver
165 * context.
166 *
167 * @param[in] ctx context of the driver
168 * @retval Void
169 */
170static void tms570_sci_disable_interrupts(tms570_sci_context * ctx)
171{
172  ctx->regs->CLEARINT = TMS570_SCI_CLEARINT_CLR_RX_INT;
173}
174
175/**
176 * @brief Check whether driver has put char in HW
177 *
178 * Check whether driver has put char in HW.
179 * This information is read from the driver context not from a peripheral.
180 * TMS570 does not have write data buffer asociated with SCI
181 * so the return can be only 0 or 1.
182 *
183 * @param[in] ctx context of the driver
184 * @retval x
185 */
186static int tms570_sci_transmitted_chars(tms570_sci_context * ctx)
187{
188  int ret;
189
190  ret = ctx->tx_chars_in_hw;
191  if ( ret == 1 ) {
192    ctx->tx_chars_in_hw = 0;
193    return 1;
194  }
195  return ret;
196}
197
198/**
199 * @brief Set attributes of the HW peripheral
200 *
201 * Sets attributes of the HW peripheral (parity, baud rate, etc.)
202 *
203 * @param[in] base context of the driver
204 * @param[in] t termios driver
205 * @retval true peripheral setting is changed
206 */
207static bool tms570_sci_set_attributes(
208  rtems_termios_device_context *base,
209  const struct termios *t
210)
211{
212  tms570_sci_context *ctx = (tms570_sci_context *) base;
213  rtems_interrupt_lock_context lock_context;
214  int32_t bauddiv;
215  int32_t baudrate;
216
217  rtems_termios_device_lock_acquire(base, &lock_context);
218
219  ctx->regs->GCR1 &= ~( TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
220                        TMS570_SCI_GCR1_RXENA );
221
222  ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_STOP;    /*one stop bit*/
223  ctx->regs->FORMAT = TMS570_SCI_FORMAT_CHAR(0x7);
224
225  switch ( t->c_cflag & ( PARENB|PARODD ) ) {
226    case ( PARENB|PARODD ):
227      /* Odd parity */
228      ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY;
229      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
230      break;
231
232    case PARENB:
233      /* Even parity */
234      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY;
235      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
236      break;
237
238    default:
239    case 0:
240    case PARODD:
241      /* No Parity */
242      ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY_ENA;
243  }
244
245  /* Baud rate */
246  baudrate = rtems_termios_baud_to_number(cfgetospeed(t));
247  baudrate *= 2 * 16;
248  bauddiv = (BSP_PLL_OUT_CLOCK + baudrate / 2) / baudrate;
249  ctx->regs->BRS = bauddiv;
250
251  ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
252                     TMS570_SCI_GCR1_RXENA;
253
254  rtems_termios_device_lock_release(base, &lock_context);
255
256  return true;
257}
258
259/**
260 * @brief sci interrupt handler
261 *
262 * Handler checks which interrupt occured and provides nessesary maintenance
263 * dequeue characters in termios driver whether character is send succesfully
264 * enqueue characters in termios driver whether character is recieved
265 *
266 * @param[in] arg rtems_termios_tty
267 * @retval Void
268 */
269static void tms570_sci_interrupt_handler(void * arg)
270{
271  rtems_termios_tty *tty = arg;
272  tms570_sci_context *ctx = rtems_termios_get_device_context(tty);
273  char buf[TMS570_SCI_BUFFER_SIZE];
274  size_t n;
275
276  /*
277   * Check if we have received something.
278   */
279   if ( (ctx->regs->FLR & TMS570_SCI_FLR_RXRDY ) == TMS570_SCI_FLR_RXRDY ) {
280      n = tms570_sci_read_received_chars(ctx, buf, TMS570_SCI_BUFFER_SIZE);
281      if ( n > 0 ) {
282        /* Hand the data over to the Termios infrastructure */
283        rtems_termios_enqueue_raw_characters(tty, buf, n);
284      }
285    }
286  /*
287   * Check if we have something transmitted.
288   */
289  if ( (ctx->regs->FLR & TMS570_SCI_FLR_TXRDY ) == TMS570_SCI_FLR_TXRDY ) {
290    n = tms570_sci_transmitted_chars(ctx);
291    if ( n > 0 ) {
292      /*
293       * Notify Termios that we have transmitted some characters.  It
294       * will call now the interrupt write function if more characters
295       * are ready for transmission.
296       */
297      rtems_termios_dequeue_characters(tty, n);
298    }
299  }
300}
301
302/**
303 * @brief sci write function called from interrupt
304 *
305 * Nonblocking write function. Writes characters to HW peripheral
306 * TMS570 does not have write data buffer asociated with SCI
307 * so only one character can be written.
308 *
309 * @param[in] base context of the driver
310 * @param[in] buf buffer of characters pending to send
311 * @param[in] len size of the buffer
312 * @retval Void
313 */
314static void tms570_sci_interrupt_write(
315  rtems_termios_device_context *base,
316  const char *buf,
317  size_t len
318)
319{
320  tms570_sci_context *ctx = (tms570_sci_context *) base;
321
322  if ( len > 0 ) {
323    /* start UART TX, this will result in an interrupt when done */
324    ctx->regs->TD = *buf;
325    /* character written - raise count*/
326    ctx->tx_chars_in_hw = 1;
327    /* Enable TX interrupt (interrupt is edge-triggered) */
328    ctx->regs->SETINT = (1<<8);
329
330  } else {
331    /* No more to send, disable TX interrupts */
332    ctx->regs->CLEARINT = (1<<8);
333    /* Tell close that we sent everything */
334  }
335}
336
337/**
338 * @brief sci write function
339 *
340 * Blocking write function. Waits until HW peripheral is ready and then writes
341 * character to HW peripheral. Writes all characters in the buffer.
342 *
343 * @param[in] base context of the driver
344 * @param[in] buf buffer of characters pending to send
345 * @param[in] len size of the buffer
346 * @retval Void
347 */
348static void tms570_sci_poll_write(
349  rtems_termios_device_context *base,
350  const char *buf,
351  size_t n
352)
353{
354  tms570_sci_context *ctx = (tms570_sci_context *) base;
355  size_t i;
356
357  /* Write */
358
359  for ( i = 0; i < n; ++i ) {
360    while ( (ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0) {
361      ;
362    }
363    ctx->regs->TD = buf[i];
364  }
365}
366
367/**
368 * @brief See if there is recieved charakter to read
369 *
370 * read the RX flag from peripheral specified in context
371 *
372 * @param[in] ctx context of the driver
373 * @retval 0 No character to read
374 * @retval x Character ready to read
375 */
376static int TMS570_sci_can_read_char(
377  tms570_sci_context * ctx
378)
379{
380  return ctx->regs->FLR & TMS570_SCI_FLR_RXRDY;
381}
382
383/**
384 * @brief reads character from peripheral
385 *
386 * reads the recieved character from peripheral specified in context
387 *
388 * @param[in] ctx context of the driver
389 * @retval x Character
390 */
391static char TMS570_sci_read_char(
392  tms570_sci_context * ctx
393)
394{
395  return ctx->regs->RD;
396}
397
398/**
399 * @brief sci read function
400 *
401 * check if there is recieved character to be read and reads it.
402 *
403 * @param[in] base context of the driver
404 * @retval -1 No character to be read
405 * @retval x Read character
406 */
407static int tms570_sci_poll_read(rtems_termios_device_context *base)
408{
409  tms570_sci_context *ctx = (tms570_sci_context *) base;
410
411  /* Check if a character is available */
412  if ( TMS570_sci_can_read_char(ctx) ) {
413    return TMS570_sci_read_char(ctx);
414  } else {
415    return -1;
416  }
417}
418
419/**
420 * @brief initialization of the driver
421 *
422 * initialization of the HW peripheral specified in contex of the driver.
423 * This function is called only once when opening the driver.
424 *
425 * @param[in] tty Termios control
426 * @param[in] ctx context of the driver
427 * @param[in] term Termios attributes
428 * @param[in] args
429 * @retval false Error occured during initialization
430 * @retval true Driver is open and ready
431 */
432static bool tms570_sci_poll_first_open(
433  rtems_termios_tty             *tty,
434  rtems_termios_device_context  *ctx,
435  struct termios                *term,
436  rtems_libio_open_close_args_t *args
437)
438{
439  bool ok;
440
441  rtems_termios_set_best_baud(term, TMS570_SCI_BAUD_RATE);
442  ok = tms570_sci_set_attributes(ctx, term);
443  if ( !ok ) {
444    return false;
445  }
446  return true;
447}
448
449/**
450 * @brief initialization of the interrupt driven driver
451 *
452 * calls tms570_sci_poll_first_open function.
453 * install and enables interrupts.
454 *
455 * @param[in] tty Termios control
456 * @param[in] base context of the driver
457 * @param[in] args
458 * @retval false Error occured during initialization
459 * @retval true Driver is open and ready
460 */
461static bool tms570_sci_interrupt_first_open(
462  rtems_termios_tty             *tty,
463  rtems_termios_device_context  *base,
464  struct termios                *term,
465  rtems_libio_open_close_args_t *args
466)
467{
468  tms570_sci_context *ctx = (tms570_sci_context *) base;
469  rtems_status_code sc;
470  bool ret;
471
472  ret = tms570_sci_poll_first_open(tty, base, term, args);
473  if ( ret == false ) {
474    return false;
475  }
476  ctx->regs->SETINTLVL = 0;
477  /* Register Interrupt handler */
478  sc = rtems_interrupt_handler_install(ctx->irq,
479      ctx->device_name,
480      RTEMS_INTERRUPT_SHARED,
481      tms570_sci_interrupt_handler,
482      tty
483  );
484  if ( sc != RTEMS_SUCCESSFUL ) {
485    return false;
486  }
487  tms570_sci_enable_interrupts(ctx);
488  return true;
489}
490
491/**
492 * @brief closes sci peripheral
493 *
494 * @param[in] tty Termios control
495 * @param[in] base context of the driver
496 * @param[in] args
497 * @retval false Error occured during initialization
498 * @retval true Driver is open and ready
499 */
500static void tms570_sci_poll_last_close(
501  rtems_termios_tty             *tty,
502  rtems_termios_device_context  *base,
503  rtems_libio_open_close_args_t *args
504)
505{
506  ;
507}
508
509/**
510 * @brief closes sci peripheral of interrupt driven driver
511 *
512 * calls tms570_sci_poll_last_close and disables interrupts
513 *
514 * @param[in] tty Termios control
515 * @param[in] base context of the driver
516 * @param[in] args
517 * @retval false Error occured during initialization
518 * @retval true Driver is open and ready
519 */
520static void tms570_sci_interrupt_last_close(
521  rtems_termios_tty             *tty,
522  rtems_termios_device_context  *base,
523  rtems_libio_open_close_args_t *args
524)
525{
526  tms570_sci_context *ctx = (tms570_sci_context *) base;
527  rtems_interrupt_lock_context lock_context;
528  rtems_interval tw;
529  int32_t baudrate;
530
531  /* Turn off RX interrupts */
532  rtems_termios_device_lock_acquire(base, &lock_context);
533  tms570_sci_disable_interrupts(ctx);
534  rtems_termios_device_lock_release(base, &lock_context);
535
536  tw = rtems_clock_get_ticks_per_second();
537  baudrate = rtems_termios_baud_to_number(cfgetospeed(&tty->termios));
538  tw = tw * 10 / baudrate + 1;
539  while ( ( ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0 ) {
540     rtems_task_wake_after(tw);
541  }
542
543  /* uninstall ISR */
544  rtems_interrupt_handler_remove(ctx->irq, tms570_sci_interrupt_handler, tty);
545
546  tms570_sci_poll_last_close(tty, base, args);
547}
548
549/**
550 * @brief Struct containing definitions of polled driver functions.
551 *
552 * Encapsulates polled driver functions.
553 * Use of this table is determited by not defining TMS570_USE_INTERRUPTS
554 */
555const rtems_termios_device_handler tms570_sci_handler_polled = {
556  .first_open = tms570_sci_poll_first_open,
557  .last_close = tms570_sci_poll_last_close,
558  .poll_read = tms570_sci_poll_read,
559  .write = tms570_sci_poll_write,
560  .set_attributes = tms570_sci_set_attributes,
561  .mode = TERMIOS_POLLED
562};
563
564/**
565 * @brief Struct containing definitions of interrupt driven driver functions.
566 *
567 * Encapsulates interrupt driven driver functions.
568 * Use of this table is determited by defining TMS570_USE_INTERRUPTS
569 */
570const rtems_termios_device_handler tms570_sci_handler_interrupt  = {
571  .first_open = tms570_sci_interrupt_first_open,
572  .last_close = tms570_sci_interrupt_last_close,
573  .poll_read = NULL,
574  .write = tms570_sci_interrupt_write,
575  .set_attributes = tms570_sci_set_attributes,
576  .mode = TERMIOS_IRQ_DRIVEN
577};
Note: See TracBrowser for help on using the repository browser.