source: rtems/c/src/lib/libbsp/arm/tms570/console/tms570-sci.c @ 0afac6a

4.11
Last change on this file since 0afac6a was cf4dfc1, checked in by Pavel Pisa <pisa@…>, on Feb 4, 2015 at 5:20:27 PM

arm/tms570: sci context has to be writable because it holds state variable.

The structure tms570_sci_context holds state variable
tx_chars_in_hw which holds if and how many characters
(in the optional FIFO support for some Ti SCIs) are submitted
into hardware.

When field is not writable then code breaks when RTEMS
is build for Flash area.

The problem found and analyzed by Martin Galvan from tallertechnologies.

Signed-off-by: Pavel Pisa <pisa@…>

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