source: rtems/c/src/lib/libbsp/arm/tms570/console/tms570-sci.c @ 55e0be36

Last change on this file since 55e0be36 was 55e0be36, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 16, 2016 at 10:58:06 AM

termios: Use IMFS nodes for new Termios devices

This makes the new Termios devices independent of device major/minor
numbers. It enables BSP independent Termios device drivers which may
reside in the cpukit domain. These drivers require an IMFS and do not
work with the device file system. However, the device file system
should go away in the future.

  • Property mode set to 100644
File size: 16.7 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    uint32_t rx_pin = 1 << 1;
99    uint32_t tx_pin = 1 << 2;
100
101    /* Resec SCI peripheral */
102    ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 0;
103    ctx->regs->GCR0 = TMS570_SCI_GCR0_RESET * 1;
104
105    /* Clear all interrupt sources */
106    ctx->regs->CLEARINT = 0xffffffff;
107
108    /* Map all interrupts to SCI INT0 line */
109    ctx->regs->CLEARINTLVL = 0xffffffff;
110
111    ctx->regs->GCR1 = TMS570_SCI_GCR1_TXENA * 0 |
112                      TMS570_SCI_GCR1_RXENA * 0 |
113                      TMS570_SCI_GCR1_CONT * 0 | /* continue operation when debugged */
114                      TMS570_SCI_GCR1_LOOP_BACK * 0 |
115                      TMS570_SCI_GCR1_POWERDOWN * 0 |
116                      TMS570_SCI_GCR1_SLEEP * 0 |
117                      TMS570_SCI_GCR1_SWnRST * 0 | /* reset state */
118                      TMS570_SCI_GCR1_CLOCK * 1 | /* internal clock */
119                      TMS570_SCI_GCR1_TIMING_MODE * 1 |
120                      TMS570_SCI_GCR1_COMM_MODE * 0;
121
122    /* Setup connection of SCI peripheral Rx and Tx  pins */
123    ctx->regs->PIO0 = rx_pin * 1 | tx_pin * 1; /* Rx and Tx pins are not GPIO */
124    ctx->regs->PIO3 = rx_pin * 0 | tx_pin * 0; /* Default output low  */
125    ctx->regs->PIO1 = rx_pin * 0 | tx_pin * 0; /* Input when not used by SCI */
126    ctx->regs->PIO6 = rx_pin * 0 | tx_pin * 0; /* No open drain */
127    ctx->regs->PIO7 = rx_pin * 0 | tx_pin * 0; /* Pull-up/down enabled */
128    ctx->regs->PIO8 = rx_pin * 1 | tx_pin * 1; /* Select pull-up */
129
130    /* Bring device out of software reset */
131    ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST;
132
133    /*
134     * Install this device in the file system and Termios.  In order
135     * to use the console (i.e. being able to do printf, scanf etc.
136     * on stdin, stdout and stderr), one device must be registered as
137     * "/dev/console" (CONSOLE_DEVICE_NAME).
138     */
139    sc = rtems_termios_device_install(
140        ctx->device_name,
141        handler,
142        NULL,
143        &ctx->base
144    );
145    if ( sc != RTEMS_SUCCESSFUL ) {
146      bsp_fatal(BSP_FATAL_CONSOLE_NO_DEV);
147    }
148  }
149  return RTEMS_SUCCESSFUL;
150}
151
152/**
153 * @brief Reads chars from HW
154 *
155 * Reads chars from HW peripheral specified in driver context.
156 * TMS570 does not have HW buffer for serial line so this function can
157 * return only 0 or 1 char
158 *
159 * @param[in] ctx context of the driver
160 * @param[out] buf read data buffer
161 * @param[in] N size of buffer
162 * @retval x Number of read chars from peripherals
163 */
164static int tms570_sci_read_received_chars(
165  tms570_sci_context * ctx,
166  char * buf,
167  int N)
168{
169  if ( N < 1 ) {
170    return 0;
171  }
172  if ( ctx->regs->RD != 0 ) {
173     buf[0] = ctx->regs->RD;
174    return 1;
175  }
176  return 0;
177}
178
179/**
180 * @brief Enables RX interrupt
181 *
182 * Enables RX interrupt source of SCI peripheral
183 * specified in the driver context.
184 *
185 * @param[in] ctx context of the driver
186 * @retval Void
187 */
188static void tms570_sci_enable_interrupts(tms570_sci_context * ctx)
189{
190  ctx->regs->SETINT = TMS570_SCI_SETINT_SET_RX_INT;
191}
192
193/**
194 * @brief Disables RX interrupt
195 *
196 * Disables RX interrupt source of SCI peripheral specified in the driver
197 * context.
198 *
199 * @param[in] ctx context of the driver
200 * @retval Void
201 */
202static void tms570_sci_disable_interrupts(tms570_sci_context * ctx)
203{
204  ctx->regs->CLEARINT = TMS570_SCI_CLEARINT_CLR_RX_INT;
205}
206
207/**
208 * @brief Check whether driver has put char in HW
209 *
210 * Check whether driver has put char in HW.
211 * This information is read from the driver context not from a peripheral.
212 * TMS570 does not have write data buffer asociated with SCI
213 * so the return can be only 0 or 1.
214 *
215 * @param[in] ctx context of the driver
216 * @retval x
217 */
218static int tms570_sci_transmitted_chars(tms570_sci_context * ctx)
219{
220  int ret;
221
222  ret = ctx->tx_chars_in_hw;
223  if ( ret == 1 ) {
224    ctx->tx_chars_in_hw = 0;
225    return 1;
226  }
227  return ret;
228}
229
230/**
231 * @brief Set attributes of the HW peripheral
232 *
233 * Sets attributes of the HW peripheral (parity, baud rate, etc.)
234 *
235 * @param[in] base context of the driver
236 * @param[in] t termios driver
237 * @retval true peripheral setting is changed
238 */
239static bool tms570_sci_set_attributes(
240  rtems_termios_device_context *base,
241  const struct termios *t
242)
243{
244  tms570_sci_context *ctx = (tms570_sci_context *) base;
245  rtems_interrupt_lock_context lock_context;
246  int32_t bauddiv;
247  int32_t baudrate;
248  uint32_t flr_tx_ready = TMS570_SCI_FLR_TX_EMPTY | TMS570_SCI_FLR_TX_EMPTY;
249
250  /* Baud rate */
251  baudrate = rtems_termios_baud_to_number(cfgetospeed(t));
252
253  rtems_termios_device_lock_acquire(base, &lock_context);
254
255  while ( (ctx->regs->GCR1 & TMS570_SCI_GCR1_TXENA) &&
256          (ctx->regs->FLR & flr_tx_ready) != flr_tx_ready) {
257    /*
258     * There are pending characters in the hardware,
259     * change in the middle of the character Tx leads
260     * to disturb of the character and SCI engine
261     */
262    rtems_interval tw;
263
264    rtems_termios_device_lock_release(base, &lock_context);
265
266    tw = rtems_clock_get_ticks_per_second();
267    tw = tw * 5 / baudrate + 1;
268    rtems_task_wake_after( tw );
269
270    rtems_termios_device_lock_acquire(base, &lock_context);
271  }
272
273  ctx->regs->GCR1 &= ~( TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
274                        TMS570_SCI_GCR1_RXENA );
275
276  ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_STOP;    /*one stop bit*/
277  ctx->regs->FORMAT = TMS570_SCI_FORMAT_CHAR(0x7);
278
279  switch ( t->c_cflag & ( PARENB|PARODD ) ) {
280    case ( PARENB|PARODD ):
281      /* Odd parity */
282      ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY;
283      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
284      break;
285
286    case PARENB:
287      /* Even parity */
288      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY;
289      ctx->regs->GCR1 |= TMS570_SCI_GCR1_PARITY_ENA;
290      break;
291
292    default:
293    case 0:
294    case PARODD:
295      /* No Parity */
296      ctx->regs->GCR1 &= ~TMS570_SCI_GCR1_PARITY_ENA;
297  }
298
299  /* Apply baudrate to the hardware */
300  baudrate *= 2 * 16;
301  bauddiv = (BSP_PLL_OUT_CLOCK + baudrate / 2) / baudrate;
302  ctx->regs->BRS = bauddiv;
303
304  ctx->regs->GCR1 |= TMS570_SCI_GCR1_SWnRST | TMS570_SCI_GCR1_TXENA |
305                     TMS570_SCI_GCR1_RXENA;
306
307  rtems_termios_device_lock_release(base, &lock_context);
308
309  return true;
310}
311
312/**
313 * @brief sci interrupt handler
314 *
315 * Handler checks which interrupt occured and provides nessesary maintenance
316 * dequeue characters in termios driver whether character is send succesfully
317 * enqueue characters in termios driver whether character is recieved
318 *
319 * @param[in] arg rtems_termios_tty
320 * @retval Void
321 */
322static void tms570_sci_interrupt_handler(void * arg)
323{
324  rtems_termios_tty *tty = arg;
325  tms570_sci_context *ctx = rtems_termios_get_device_context(tty);
326  char buf[TMS570_SCI_BUFFER_SIZE];
327  size_t n;
328
329  /*
330   * Check if we have received something.
331   */
332   if ( (ctx->regs->FLR & TMS570_SCI_FLR_RXRDY ) == TMS570_SCI_FLR_RXRDY ) {
333      n = tms570_sci_read_received_chars(ctx, buf, TMS570_SCI_BUFFER_SIZE);
334      if ( n > 0 ) {
335        /* Hand the data over to the Termios infrastructure */
336        rtems_termios_enqueue_raw_characters(tty, buf, n);
337      }
338    }
339  /*
340   * Check if we have something transmitted.
341   */
342  if ( (ctx->regs->FLR & TMS570_SCI_FLR_TXRDY ) == TMS570_SCI_FLR_TXRDY ) {
343    n = tms570_sci_transmitted_chars(ctx);
344    if ( n > 0 ) {
345      /*
346       * Notify Termios that we have transmitted some characters.  It
347       * will call now the interrupt write function if more characters
348       * are ready for transmission.
349       */
350      rtems_termios_dequeue_characters(tty, n);
351    }
352  }
353}
354
355/**
356 * @brief sci write function called from interrupt
357 *
358 * Nonblocking write function. Writes characters to HW peripheral
359 * TMS570 does not have write data buffer asociated with SCI
360 * so only one character can be written.
361 *
362 * @param[in] base context of the driver
363 * @param[in] buf buffer of characters pending to send
364 * @param[in] len size of the buffer
365 * @retval Void
366 */
367static void tms570_sci_interrupt_write(
368  rtems_termios_device_context *base,
369  const char *buf,
370  size_t len
371)
372{
373  tms570_sci_context *ctx = (tms570_sci_context *) base;
374
375  if ( len > 0 ) {
376    /* start UART TX, this will result in an interrupt when done */
377    ctx->regs->TD = *buf;
378    /* character written - raise count*/
379    ctx->tx_chars_in_hw = 1;
380    /* Enable TX interrupt (interrupt is edge-triggered) */
381    ctx->regs->SETINT = (1<<8);
382
383  } else {
384    /* No more to send, disable TX interrupts */
385    ctx->regs->CLEARINT = (1<<8);
386    /* Tell close that we sent everything */
387  }
388}
389
390/**
391 * @brief sci write function
392 *
393 * Blocking write function. Waits until HW peripheral is ready and then writes
394 * character to HW peripheral. Writes all characters in the buffer.
395 *
396 * @param[in] base context of the driver
397 * @param[in] buf buffer of characters pending to send
398 * @param[in] len size of the buffer
399 * @retval Void
400 */
401static void tms570_sci_poll_write(
402  rtems_termios_device_context *base,
403  const char *buf,
404  size_t n
405)
406{
407  tms570_sci_context *ctx = (tms570_sci_context *) base;
408  size_t i;
409
410  /* Write */
411
412  for ( i = 0; i < n; ++i ) {
413    while ( (ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0) {
414      ;
415    }
416    ctx->regs->TD = buf[i];
417  }
418}
419
420/**
421 * @brief See if there is recieved charakter to read
422 *
423 * read the RX flag from peripheral specified in context
424 *
425 * @param[in] ctx context of the driver
426 * @retval 0 No character to read
427 * @retval x Character ready to read
428 */
429static int TMS570_sci_can_read_char(
430  tms570_sci_context * ctx
431)
432{
433  return ctx->regs->FLR & TMS570_SCI_FLR_RXRDY;
434}
435
436/**
437 * @brief reads character from peripheral
438 *
439 * reads the recieved character from peripheral specified in context
440 *
441 * @param[in] ctx context of the driver
442 * @retval x Character
443 */
444static char TMS570_sci_read_char(
445  tms570_sci_context * ctx
446)
447{
448  return ctx->regs->RD;
449}
450
451/**
452 * @brief sci read function
453 *
454 * check if there is recieved character to be read and reads it.
455 *
456 * @param[in] base context of the driver
457 * @retval -1 No character to be read
458 * @retval x Read character
459 */
460static int tms570_sci_poll_read(rtems_termios_device_context *base)
461{
462  tms570_sci_context *ctx = (tms570_sci_context *) base;
463
464  /* Check if a character is available */
465  if ( TMS570_sci_can_read_char(ctx) ) {
466    return TMS570_sci_read_char(ctx);
467  } else {
468    return -1;
469  }
470}
471
472/**
473 * @brief initialization of the driver
474 *
475 * initialization of the HW peripheral specified in contex of the driver.
476 * This function is called only once when opening the driver.
477 *
478 * @param[in] tty Termios control
479 * @param[in] ctx context of the driver
480 * @param[in] term Termios attributes
481 * @param[in] args
482 * @retval false Error occured during initialization
483 * @retval true Driver is open and ready
484 */
485static bool tms570_sci_poll_first_open(
486  rtems_termios_tty             *tty,
487  rtems_termios_device_context  *ctx,
488  struct termios                *term,
489  rtems_libio_open_close_args_t *args
490)
491{
492  bool ok;
493
494  rtems_termios_set_best_baud(term, TMS570_SCI_BAUD_RATE);
495  ok = tms570_sci_set_attributes(ctx, term);
496  if ( !ok ) {
497    return false;
498  }
499  return true;
500}
501
502/**
503 * @brief initialization of the interrupt driven driver
504 *
505 * calls tms570_sci_poll_first_open function.
506 * install and enables interrupts.
507 *
508 * @param[in] tty Termios control
509 * @param[in] base context of the driver
510 * @param[in] args
511 * @retval false Error occured during initialization
512 * @retval true Driver is open and ready
513 */
514static bool tms570_sci_interrupt_first_open(
515  rtems_termios_tty             *tty,
516  rtems_termios_device_context  *base,
517  struct termios                *term,
518  rtems_libio_open_close_args_t *args
519)
520{
521  tms570_sci_context *ctx = (tms570_sci_context *) base;
522  rtems_status_code sc;
523  bool ret;
524
525  ret = tms570_sci_poll_first_open(tty, base, term, args);
526  if ( ret == false ) {
527    return false;
528  }
529
530  /* Register Interrupt handler */
531  sc = rtems_interrupt_handler_install(ctx->irq,
532      ctx->device_name,
533      RTEMS_INTERRUPT_SHARED,
534      tms570_sci_interrupt_handler,
535      tty
536  );
537  if ( sc != RTEMS_SUCCESSFUL ) {
538    return false;
539  }
540  tms570_sci_enable_interrupts(ctx);
541  return true;
542}
543
544/**
545 * @brief closes sci peripheral
546 *
547 * @param[in] tty Termios control
548 * @param[in] base context of the driver
549 * @param[in] args
550 * @retval false Error occured during initialization
551 * @retval true Driver is open and ready
552 */
553static void tms570_sci_poll_last_close(
554  rtems_termios_tty             *tty,
555  rtems_termios_device_context  *base,
556  rtems_libio_open_close_args_t *args
557)
558{
559  ;
560}
561
562/**
563 * @brief closes sci peripheral of interrupt driven driver
564 *
565 * calls tms570_sci_poll_last_close and disables interrupts
566 *
567 * @param[in] tty Termios control
568 * @param[in] base context of the driver
569 * @param[in] args
570 * @retval false Error occured during initialization
571 * @retval true Driver is open and ready
572 */
573static void tms570_sci_interrupt_last_close(
574  rtems_termios_tty             *tty,
575  rtems_termios_device_context  *base,
576  rtems_libio_open_close_args_t *args
577)
578{
579  tms570_sci_context *ctx = (tms570_sci_context *) base;
580  rtems_interrupt_lock_context lock_context;
581  rtems_interval tw;
582  int32_t baudrate;
583
584  /* Turn off RX interrupts */
585  rtems_termios_device_lock_acquire(base, &lock_context);
586  tms570_sci_disable_interrupts(ctx);
587  rtems_termios_device_lock_release(base, &lock_context);
588
589  tw = rtems_clock_get_ticks_per_second();
590  baudrate = rtems_termios_baud_to_number(cfgetospeed(&tty->termios));
591  tw = tw * 10 / baudrate + 1;
592  while ( ( ctx->regs->FLR & TMS570_SCI_FLR_TX_EMPTY ) == 0 ) {
593     rtems_task_wake_after(tw);
594  }
595
596  /* uninstall ISR */
597  rtems_interrupt_handler_remove(ctx->irq, tms570_sci_interrupt_handler, tty);
598
599  tms570_sci_poll_last_close(tty, base, args);
600}
601
602/**
603 * @brief Struct containing definitions of polled driver functions.
604 *
605 * Encapsulates polled driver functions.
606 * Use of this table is determited by not defining TMS570_USE_INTERRUPTS
607 */
608const rtems_termios_device_handler tms570_sci_handler_polled = {
609  .first_open = tms570_sci_poll_first_open,
610  .last_close = tms570_sci_poll_last_close,
611  .poll_read = tms570_sci_poll_read,
612  .write = tms570_sci_poll_write,
613  .set_attributes = tms570_sci_set_attributes,
614  .mode = TERMIOS_POLLED
615};
616
617/**
618 * @brief Struct containing definitions of interrupt driven driver functions.
619 *
620 * Encapsulates interrupt driven driver functions.
621 * Use of this table is determited by defining TMS570_USE_INTERRUPTS
622 */
623const rtems_termios_device_handler tms570_sci_handler_interrupt  = {
624  .first_open = tms570_sci_interrupt_first_open,
625  .last_close = tms570_sci_interrupt_last_close,
626  .poll_read = NULL,
627  .write = tms570_sci_interrupt_write,
628  .set_attributes = tms570_sci_set_attributes,
629  .mode = TERMIOS_IRQ_DRIVEN
630};
Note: See TracBrowser for help on using the repository browser.