source: rtems/bsps/arm/tms570/console/tms570-sci.c @ 5247a214

5
Last change on this file since 5247a214 was 5247a214, checked in by Pavel Pisa <pisa@…>, on 11/14/18 at 22:48:56

bsp/tms570: correct baudrate calculation.

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