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

4.115
Last change on this file since ff7217b was 4407ee6, checked in by Premysl Houdek <kom541000@…>, on 08/20/14 at 15:24:23

BSP for TMS570LS31x Hercules Development Kit from TI (TMS570LS3137)

Included variants:

tms570ls3137_hdk_intram - place code and data into internal SRAM
tms570ls3137_hdk_sdram - place code into external SDRAM and data to SRAM
tms570ls3137_hdk - variant prepared for stand-alone RTEMS aplication

stored and running directly from flash. Not working yet.

Chip initialization code not included in BSP.
External startup generated by TI's HalCoGen? was used for
testing and debugging.

More information about TMS570 BSP can be found at

http://www.rtems.org/wiki/index.php/Tms570

Patch version 2

  • most of the formatting suggestion applied.
  • BSP converted to use clock shell
  • console driver "set attributes" tested. Baudrate change working

Patch version 3

  • more formatting changes.
  • removed leftover defines and test functions

Todo:

refactor header files (name register fields)

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