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

4.115
Last change on this file since a830cb8 was a830cb8, checked in by Sebastian Huber <sebastian.huber@…>, on Oct 7, 2014 at 2:27:51 PM

termios: Separate flow control from normal handler

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