source: rtems/bsps/shared/dev/serial/ns16550-context.c @ a7cd4b73

5
Last change on this file since a7cd4b73 was a7cd4b73, checked in by Sebastian Huber <sebastian.huber@…>, on 07/31/18 at 09:38:56

serial/ns16550: Precision clock synthesizer

Set the FIFO control register while DLAB == 1 in the line control
register. At least on the QorIQ T4240 the driver still works with the
re-ordered FIFO control register access.

  • Property mode set to 100644
File size: 22.0 KB
Line 
1/**
2 *  @file
3 * 
4 *  This file contains the TTY driver for the National Semiconductor NS16550.
5 *
6 *  This part is widely cloned and second sourced.  It is found in a number
7 *  of "Super IO" controllers.
8 *
9 *  This driver uses the termios pseudo driver.
10 */
11
12/*
13 *  COPYRIGHT (c) 1998 by Radstone Technology
14 *
15 *  THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
16 *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
17 *  IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
18 *  AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
19 *
20 *  You are hereby granted permission to use, copy, modify, and distribute
21 *  this file, provided that this notice, plus the above copyright notice
22 *  and disclaimer, appears in all copies. Radstone Technology will provide
23 *  no support for this code.
24 *
25 *  COPYRIGHT (c) 1989-2012.
26 *  On-Line Applications Research Corporation (OAR).
27 *
28 *  The license and distribution terms for this file may be
29 *  found in the file LICENSE in this distribution or at
30 *  http://www.rtems.org/license/LICENSE.
31 */
32
33#include <stdlib.h>
34
35#include <rtems/bspIo.h>
36
37#include <bsp.h>
38
39#include <libchip/ns16550.h>
40#include <libchip/ns16550_p.h>
41
42#if defined(BSP_FEATURE_IRQ_EXTENSION)
43  #include <bsp/irq.h>
44#elif defined(BSP_FEATURE_IRQ_LEGACY)
45  #include <bsp/irq.h>
46#elif defined(__PPC__) || defined(__i386__)
47  #include <bsp/irq.h>
48  #define BSP_FEATURE_IRQ_LEGACY
49  #ifdef BSP_SHARED_HANDLER_SUPPORT
50    #define BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
51  #endif
52#endif
53
54static uint32_t NS16550_GetBaudDivisor(ns16550_context *ctx, uint32_t baud)
55{
56  uint32_t clock;
57  uint32_t baudDivisor;
58  uint32_t err;
59  uint32_t actual;
60  uint32_t newErr;
61
62  if (ctx->clock != 0) {
63    clock = ctx->clock;
64  } else {
65    clock = 115200;
66  }
67
68  baudDivisor = clock / (baud * 16);
69
70  if (ctx->has_precision_clock_synthesizer) {
71    uint32_t i;
72
73    err = baud;
74    baudDivisor = 0x0001ffff;
75
76    for (i = 2; i <= 0x10000; i *= 2) {
77      uint32_t fout;
78      uint32_t fin;
79
80      fin = i - 1;
81      fout = (baud * fin * 16) / clock;
82      actual = (clock * fout) / (16 * fin);
83      newErr = actual > baud ? actual - baud : baud - actual;
84
85      if (newErr < err) {
86        err = newErr;
87        baudDivisor = fin | (fout << 16);
88      }
89    }
90  } else if (ctx->has_fractional_divider_register) {
91    uint32_t fractionalDivider = 0x10;
92    uint32_t mulVal;
93    uint32_t divAddVal;
94
95    err = baud;
96    clock /= 16 * baudDivisor;
97
98    for (mulVal = 1; mulVal < 16; ++mulVal) {
99      for (divAddVal = 0; divAddVal < mulVal; ++divAddVal) {
100        actual = (mulVal * clock) / (mulVal + divAddVal);
101        newErr = actual > baud ? actual - baud : baud - actual;
102
103        if (newErr < err) {
104          err = newErr;
105          fractionalDivider = (mulVal << 4) | divAddVal;
106        }
107      }
108    }
109
110    (*ctx->set_reg)(
111      ctx->port,
112      NS16550_FRACTIONAL_DIVIDER,
113      fractionalDivider
114    );
115  }
116
117  return baudDivisor;
118}
119
120/*
121 *  ns16550_enable_interrupts
122 *
123 *  This routine initializes the port to have the specified interrupts masked.
124 */
125static void ns16550_enable_interrupts(
126  ns16550_context *ctx,
127  int              mask
128)
129{
130  (*ctx->set_reg)(ctx->port, NS16550_INTERRUPT_ENABLE, mask);
131}
132
133static void ns16550_clear_and_set_interrupts(
134  ns16550_context *ctx,
135  uint8_t          clear,
136  uint8_t          set
137)
138{
139  rtems_interrupt_lock_context lock_context;
140  ns16550_get_reg get_reg = ctx->get_reg;
141  ns16550_set_reg set_reg = ctx->set_reg;
142  uintptr_t port = ctx->port;
143  uint8_t val;
144
145  rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
146  val = (*get_reg)(port, NS16550_INTERRUPT_ENABLE);
147  val &= ~clear;
148  val |= set;
149  (*set_reg)(port, NS16550_INTERRUPT_ENABLE, val);
150  rtems_termios_device_lock_release(&ctx->base, &lock_context);
151}
152
153/*
154 *  ns16550_probe
155 */
156
157bool ns16550_probe(rtems_termios_device_context *base)
158{
159  ns16550_context        *ctx = (ns16550_context *) base;
160  uintptr_t               pNS16550;
161  uint8_t                 ucDataByte;
162  uint32_t                ulBaudDivisor;
163  ns16550_set_reg         setReg;
164  ns16550_get_reg         getReg;
165
166  ctx->modem_control = SP_MODEM_IRQ;
167
168  pNS16550 = ctx->port;   
169  setReg   = ctx->set_reg;
170  getReg   = ctx->get_reg;
171
172  /* Clear the divisor latch, clear all interrupt enables,
173   * and reset and
174   * disable the FIFO's.
175   */
176
177  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
178  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR );
179
180  /* Set the divisor latch and set the baud rate. */
181
182  ulBaudDivisor = NS16550_GetBaudDivisor(ctx, ctx->initial_baud);
183  ctx->baud_divisor = ulBaudDivisor;
184  ucDataByte = SP_LINE_DLAB;
185  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
186
187  /* XXX */
188  (*setReg)(pNS16550,NS16550_DIVISOR_LATCH_L,(uint8_t)(ulBaudDivisor & 0xffU));
189  (*setReg)(
190    pNS16550,NS16550_DIVISOR_LATCH_M,
191    (uint8_t)(( ulBaudDivisor >> 8 ) & 0xffU )
192  );
193
194  /* Enable and reset transmit and receive FIFOs. TJA     */
195  ucDataByte = SP_FIFO_ENABLE;
196  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
197
198  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
199
200  if (ctx->has_precision_clock_synthesizer) {
201    /*
202     * Enable precision clock synthesizer.  This must be done with DLAB == 1 in
203     * the line control register.
204     */
205    ucDataByte |= 0x10;
206  }
207
208  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
209
210  /* Clear the divisor latch and set the character size to eight bits */
211  /* with one stop bit and no parity checking. */
212  ucDataByte = EIGHT_BITS;
213  ctx->line_control = ucDataByte;
214
215  if (ctx->has_precision_clock_synthesizer) {
216    (*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 24));
217    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte );
218    (*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 16));
219  } else {
220    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
221  }
222
223  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
224
225  /* Set data terminal ready. */
226  /* And open interrupt tristate line */
227  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,ctx->modem_control);
228
229  (*getReg)(pNS16550, NS16550_LINE_STATUS );
230  (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
231
232  return true;
233}
234
235static size_t ns16550_write_to_fifo(
236  const ns16550_context *ctx,
237  const char *buf,
238  size_t len
239)
240{
241  uintptr_t port = ctx->port;
242  ns16550_set_reg set = ctx->set_reg;
243  size_t out = len > SP_FIFO_SIZE ? SP_FIFO_SIZE : len;
244  size_t i;
245
246  for (i = 0; i < out; ++i) {
247    (*set)(port, NS16550_TRANSMIT_BUFFER, buf[i]);
248  }
249
250  return out;
251}
252
253/**
254 * @brief Process interrupt.
255 */
256static void ns16550_isr(void *arg)
257{
258  rtems_termios_tty *tty = arg;
259  ns16550_context *ctx = rtems_termios_get_device_context(tty);
260  uintptr_t port = ctx->port;
261  ns16550_get_reg get = ctx->get_reg;
262  int i = 0;
263  char buf [SP_FIFO_SIZE];
264
265  /* Iterate until no more interrupts are pending */
266  do {
267    /* Fetch received characters */
268    for (i = 0; i < SP_FIFO_SIZE; ++i) {
269      if ((get( port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
270        buf [i] = (char) get(port, NS16550_RECEIVE_BUFFER);
271      } else {
272        break;
273      }
274    }
275
276    /* Enqueue fetched characters */
277    rtems_termios_enqueue_raw_characters(tty, buf, i);
278
279    /* Do transmit */
280    if (ctx->out_total > 0
281        && (get(port, NS16550_LINE_STATUS) & SP_LSR_THOLD) != 0) {
282      size_t current = ctx->out_current;
283
284      ctx->out_buf += current;
285      ctx->out_remaining -= current;
286
287      if (ctx->out_remaining > 0) {
288        ctx->out_current =
289          ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
290      } else {
291        rtems_termios_dequeue_characters(tty, ctx->out_total);
292      }
293    }
294  } while ((get( port, NS16550_INTERRUPT_ID) & SP_IID_0) == 0);
295}
296
297static void ns16550_isr_task(void *arg)
298{
299  rtems_termios_tty *tty = arg;
300  ns16550_context *ctx = rtems_termios_get_device_context(tty);
301  uint8_t status = (*ctx->get_reg)(ctx->port, NS16550_LINE_STATUS);
302
303  if ((status & SP_LSR_RDY) != 0) {
304    ns16550_clear_and_set_interrupts(ctx, SP_INT_RX_ENABLE, 0);
305    rtems_termios_rxirq_occured(tty);
306  }
307
308  if (ctx->out_total > 0 && (status & SP_LSR_THOLD) != 0) {
309    size_t current = ctx->out_current;
310
311    ctx->out_buf += current;
312    ctx->out_remaining -= current;
313
314    if (ctx->out_remaining > 0) {
315      ctx->out_current =
316        ns16550_write_to_fifo(ctx, ctx->out_buf, ctx->out_remaining);
317    } else {
318      size_t done = ctx->out_total;
319
320      ctx->out_total = 0;
321      ns16550_clear_and_set_interrupts(ctx, SP_INT_TX_ENABLE, 0);
322      rtems_termios_dequeue_characters(tty, done);
323    }
324  }
325}
326
327static int ns16550_read_task(rtems_termios_device_context *base)
328{
329  ns16550_context *ctx = (ns16550_context *) base;
330  uintptr_t port = ctx->port;
331  ns16550_get_reg get = ctx->get_reg;
332  char buf[SP_FIFO_SIZE];
333  int i;
334
335  for (i = 0; i < SP_FIFO_SIZE; ++i) {
336    if ((get(port, NS16550_LINE_STATUS) & SP_LSR_RDY) != 0) {
337      buf[i] = (char) get(port, NS16550_RECEIVE_BUFFER);
338    } else {
339      break;
340    }
341  }
342
343  rtems_termios_enqueue_raw_characters(ctx->tty, buf, i);
344  ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_RX_ENABLE);
345
346  return -1;
347}
348
349/*
350 *  ns16550_initialize_interrupts
351 *
352 *  This routine initializes the port to operate in interrupt driver mode.
353 */
354static void ns16550_initialize_interrupts(
355  struct rtems_termios_tty *tty,
356  ns16550_context          *ctx,
357  void                    (*isr)(void *)
358)
359{
360  #ifdef BSP_FEATURE_IRQ_EXTENSION
361    {
362      rtems_status_code sc = RTEMS_SUCCESSFUL;
363      sc = rtems_interrupt_handler_install(
364        ctx->irq,
365        "NS16550",
366        RTEMS_INTERRUPT_SHARED,
367        isr,
368        tty
369      );
370      if (sc != RTEMS_SUCCESSFUL) {
371        /* FIXME */
372        printk( "%s: Error: Install interrupt handler\n", __func__);
373        rtems_fatal_error_occurred( 0xdeadbeef);
374      }
375    }
376  #elif defined(BSP_FEATURE_IRQ_LEGACY)
377    {
378      int rv = 0;
379      #ifdef BSP_FEATURE_IRQ_LEGACY_SHARED_HANDLER_SUPPORT
380        rtems_irq_connect_data cd = {
381          ctx->irq,
382          isr,
383          tty,
384          NULL,
385          NULL,
386          NULL,
387          NULL
388        };
389        rv = BSP_install_rtems_shared_irq_handler( &cd);
390      #else
391        rtems_irq_connect_data cd = {
392          ctx->irq,
393          isr,
394          tty,
395          NULL,
396          NULL,
397          NULL
398        };
399        rv = BSP_install_rtems_irq_handler( &cd);
400      #endif
401      if (rv == 0) {
402        /* FIXME */
403        printk( "%s: Error: Install interrupt handler\n", __func__);
404        rtems_fatal_error_occurred( 0xdeadbeef);
405      }
406    }
407  #endif
408}
409
410/*
411 *  ns16550_open
412 */
413
414static bool ns16550_open(
415  struct rtems_termios_tty      *tty,
416  rtems_termios_device_context  *base,
417  struct termios                *term,
418  rtems_libio_open_close_args_t *args
419)
420{
421  ns16550_context *ctx = (ns16550_context *) base;
422
423  ctx->tty = tty;
424
425  /* Set initial baud */
426  rtems_termios_set_initial_baud(tty, ctx->initial_baud);
427
428  if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
429    ns16550_initialize_interrupts(tty, ctx, ns16550_isr);
430    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
431  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
432    ns16550_initialize_interrupts(tty, ctx, ns16550_isr_task);
433    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
434  }
435
436  return true;
437}
438
439static void ns16550_cleanup_interrupts(
440  struct rtems_termios_tty *tty,
441  ns16550_context          *ctx,
442  void                    (*isr)(void *)
443)
444{
445  #if defined(BSP_FEATURE_IRQ_EXTENSION)
446    rtems_status_code sc = RTEMS_SUCCESSFUL;
447    sc = rtems_interrupt_handler_remove(
448      ctx->irq,
449      isr,
450      tty
451    );
452    if (sc != RTEMS_SUCCESSFUL) {
453      /* FIXME */
454      printk("%s: Error: Remove interrupt handler\n", __func__);
455      rtems_fatal_error_occurred(0xdeadbeef);
456    }
457  #elif defined(BSP_FEATURE_IRQ_LEGACY)
458    int rv = 0;
459    rtems_irq_connect_data cd = {
460      .name = ctx->irq,
461      .hdl = isr,
462      .handle = tty
463    };
464    rv = BSP_remove_rtems_irq_handler(&cd);
465    if (rv == 0) {
466      /* FIXME */
467      printk("%s: Error: Remove interrupt handler\n", __func__);
468      rtems_fatal_error_occurred(0xdeadbeef);
469    }
470  #endif
471}
472
473/*
474 *  ns16550_close
475 */
476
477static void ns16550_close(
478  struct rtems_termios_tty      *tty,
479  rtems_termios_device_context  *base,
480  rtems_libio_open_close_args_t *args
481)
482{
483  ns16550_context *ctx = (ns16550_context *) base;
484
485  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
486
487  if (tty->handler.mode == TERMIOS_IRQ_DRIVEN) {
488    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr);
489  } else if (tty->handler.mode == TERMIOS_TASK_DRIVEN) {
490    ns16550_cleanup_interrupts(tty, ctx, ns16550_isr_task);
491  }
492}
493
494/**
495 * @brief Polled write for NS16550.
496 */
497void ns16550_polled_putchar(rtems_termios_device_context *base, char out)
498{
499  ns16550_context *ctx = (ns16550_context *) base;
500  uintptr_t port = ctx->port;
501  ns16550_get_reg get = ctx->get_reg;
502  ns16550_set_reg set = ctx->set_reg;
503  uint32_t status = 0;
504  rtems_interrupt_lock_context lock_context;
505
506  /* Save port interrupt mask */
507  uint32_t interrupt_mask = get( port, NS16550_INTERRUPT_ENABLE);
508
509  /* Disable port interrupts */
510  ns16550_enable_interrupts(ctx, NS16550_DISABLE_ALL_INTR);
511
512  while (true) {
513    /* Try to transmit the character in a critical section */
514    rtems_termios_device_lock_acquire(&ctx->base, &lock_context);
515
516    /* Read the transmitter holding register and check it */
517    status = get( port, NS16550_LINE_STATUS);
518    if ((status & SP_LSR_THOLD) != 0) {
519      /* Transmit character */
520      set( port, NS16550_TRANSMIT_BUFFER, out);
521
522      /* Finished */
523      rtems_termios_device_lock_release(&ctx->base, &lock_context);
524      break;
525    } else {
526      rtems_termios_device_lock_release(&ctx->base, &lock_context);
527    }
528
529    /* Wait for transmitter holding register to be empty */
530    do {
531      status = get( port, NS16550_LINE_STATUS);
532    } while ((status & SP_LSR_THOLD) == 0);
533  }
534
535  /* Restore port interrupt mask */
536  set( port, NS16550_INTERRUPT_ENABLE, interrupt_mask);
537}
538
539/*
540 * These routines provide control of the RTS and DTR lines
541 */
542
543/*
544 *  ns16550_assert_RTS
545 */
546
547static void ns16550_assert_RTS(rtems_termios_device_context *base)
548{
549  ns16550_context             *ctx = (ns16550_context *) base;
550  rtems_interrupt_lock_context lock_context;
551
552  /*
553   * Assert RTS
554   */
555  rtems_termios_device_lock_acquire(base, &lock_context);
556  ctx->modem_control |= SP_MODEM_RTS;
557  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
558  rtems_termios_device_lock_release(base, &lock_context);
559}
560
561/*
562 *  ns16550_negate_RTS
563 */
564
565static void ns16550_negate_RTS(rtems_termios_device_context *base)
566{
567  ns16550_context             *ctx = (ns16550_context *) base;
568  rtems_interrupt_lock_context lock_context;
569
570  /*
571   * Negate RTS
572   */
573  rtems_termios_device_lock_acquire(base, &lock_context);
574  ctx->modem_control &= ~SP_MODEM_RTS;
575  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
576  rtems_termios_device_lock_release(base, &lock_context);
577}
578
579/*
580 * These flow control routines utilise a connection from the local DTR
581 * line to the remote CTS line
582 */
583
584/*
585 *  ns16550_assert_DTR
586 */
587
588static void ns16550_assert_DTR(rtems_termios_device_context *base)
589{
590  ns16550_context             *ctx = (ns16550_context *) base;
591  rtems_interrupt_lock_context lock_context;
592
593  /*
594   * Assert DTR
595   */
596  rtems_termios_device_lock_acquire(base, &lock_context);
597  ctx->modem_control |= SP_MODEM_DTR;
598  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL, ctx->modem_control);
599  rtems_termios_device_lock_release(base, &lock_context);
600}
601
602/*
603 *  ns16550_negate_DTR
604 */
605
606static void ns16550_negate_DTR(rtems_termios_device_context *base)
607{
608  ns16550_context             *ctx = (ns16550_context *) base;
609  rtems_interrupt_lock_context lock_context;
610
611  /*
612   * Negate DTR
613   */
614  rtems_termios_device_lock_acquire(base, &lock_context);
615  ctx->modem_control &=~SP_MODEM_DTR;
616  (*ctx->set_reg)(ctx->port, NS16550_MODEM_CONTROL,ctx->modem_control);
617  rtems_termios_device_lock_release(base, &lock_context);
618}
619
620/*
621 *  ns16550_set_attributes
622 *
623 *  This function sets the channel to reflect the requested termios
624 *  port settings.
625 */
626
627static bool ns16550_set_attributes(
628  rtems_termios_device_context *base,
629  const struct termios         *t
630)
631{
632  ns16550_context        *ctx = (ns16550_context *) base;
633  uint32_t                pNS16550;
634  uint32_t                ulBaudDivisor;
635  uint8_t                 ucLineControl;
636  uint32_t                baud_requested;
637  ns16550_set_reg         setReg;
638
639  pNS16550 = ctx->port;
640  setReg   = ctx->set_reg;
641
642  /*
643   *  Calculate the baud rate divisor
644   *
645   *  Assert ensures there is no division by 0.
646   */
647
648  baud_requested = rtems_termios_baud_to_number(t->c_ospeed);
649  _Assert( baud_requested != 0 );
650
651  ulBaudDivisor = NS16550_GetBaudDivisor(ctx, baud_requested);
652
653  ucLineControl = 0;
654
655  /*
656   *  Parity
657   */
658
659  if (t->c_cflag & PARENB) {
660    ucLineControl |= SP_LINE_PAR;
661    if (!(t->c_cflag & PARODD))
662      ucLineControl |= SP_LINE_ODD;
663  }
664
665  /*
666   *  Character Size
667   */
668
669  if (t->c_cflag & CSIZE) {
670    switch (t->c_cflag & CSIZE) {
671      case CS5:  ucLineControl |= FIVE_BITS;  break;
672      case CS6:  ucLineControl |= SIX_BITS;   break;
673      case CS7:  ucLineControl |= SEVEN_BITS; break;
674      case CS8:  ucLineControl |= EIGHT_BITS; break;
675    }
676  } else {
677    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
678  }
679
680  /*
681   *  Stop Bits
682   */
683
684  if (t->c_cflag & CSTOPB) {
685    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
686  } else {
687    ;                                           /* 1 stop bit */
688  }
689
690  /*
691   *  Now actually set the chip
692   */
693
694  if (ulBaudDivisor != ctx->baud_divisor || ucLineControl != ctx->line_control) {
695    rtems_interrupt_lock_context lock_context;
696
697    ctx->baud_divisor = ulBaudDivisor;
698    ctx->line_control = ucLineControl;
699
700    rtems_termios_device_lock_acquire(base, &lock_context);
701
702    /*
703     *  Set the baud rate
704     *
705     *  NOTE: When the Divisor Latch Access Bit (DLAB) is set to 1,
706     *        the transmit buffer and interrupt enable registers
707     *        turn into the LSB and MSB divisor latch registers.
708     */
709
710    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
711    (*setReg)(pNS16550, NS16550_DIVISOR_LATCH_L, ulBaudDivisor&0xff);
712    (*setReg)(pNS16550, NS16550_DIVISOR_LATCH_M, (ulBaudDivisor>>8)&0xff);
713
714    /*
715     *  Now write the line control
716     */
717    if (ctx->has_precision_clock_synthesizer) {
718      (*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 24));
719      (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
720      (*setReg)(pNS16550, NS16550_SCRATCH_PAD, (uint8_t)(ulBaudDivisor >> 16));
721    } else {
722      (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
723    }
724
725    rtems_termios_device_lock_release(base, &lock_context);
726  }
727
728  return true;
729}
730
731/**
732 * @brief Transmits up to @a len characters from @a buf.
733 *
734 * This routine is invoked either from task context with disabled interrupts to
735 * start a new transmission process with exactly one character in case of an
736 * idle output state or from the interrupt handler to refill the transmitter.
737 *
738 * Returns always zero.
739 */
740static void ns16550_write_support_int(
741  rtems_termios_device_context *base,
742  const char                   *buf,
743  size_t                        len
744)
745{
746  ns16550_context *ctx = (ns16550_context *) base;
747
748  ctx->out_total = len;
749
750  if (len > 0) {
751    ctx->out_remaining = len;
752    ctx->out_buf = buf;
753    ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
754
755    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR);
756  } else {
757    ns16550_enable_interrupts(ctx, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
758  }
759}
760
761static void ns16550_write_support_task(
762  rtems_termios_device_context *base,
763  const char                   *buf,
764  size_t                        len
765)
766{
767  ns16550_context *ctx = (ns16550_context *) base;
768
769  ctx->out_total = len;
770
771  if (len > 0) {
772    ctx->out_remaining = len;
773    ctx->out_buf = buf;
774    ctx->out_current = ns16550_write_to_fifo(ctx, buf, len);
775
776    ns16550_clear_and_set_interrupts(ctx, 0, SP_INT_TX_ENABLE);
777  }
778}
779
780/*
781 *  ns16550_write_support_polled
782 *
783 *  Console Termios output entry point.
784 *
785 */
786
787static void ns16550_write_support_polled(
788  rtems_termios_device_context *base,
789  const char                   *buf,
790  size_t                        len
791)
792{
793  size_t nwrite = 0;
794
795  /*
796   * poll each byte in the string out of the port.
797   */
798  while (nwrite < len) {
799    /*
800     * transmit character
801     */
802    ns16550_polled_putchar(base, *buf++);
803    nwrite++;
804  }
805}
806
807/*
808 *  Debug gets() support
809 */
810int ns16550_polled_getchar(rtems_termios_device_context *base)
811{
812  ns16550_context     *ctx = (ns16550_context *) base;
813  uint32_t             pNS16550;
814  unsigned char        ucLineStatus;
815  uint8_t              cChar;
816  ns16550_get_reg      getReg;
817
818  pNS16550 = ctx->port;
819  getReg   = ctx->get_reg;
820
821  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
822  if (ucLineStatus & SP_LSR_RDY) {
823    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
824    return (int)cChar;
825  }
826  return -1;
827}
828
829/*
830 * Flow control is only supported when using interrupts
831 */
832
833const rtems_termios_device_flow ns16550_flow_rtscts = {
834  .stop_remote_tx = ns16550_negate_RTS,
835  .start_remote_tx = ns16550_assert_RTS
836};
837
838const rtems_termios_device_flow ns16550_flow_dtrcts = {
839  .stop_remote_tx = ns16550_negate_DTR,
840  .start_remote_tx = ns16550_assert_DTR
841};
842
843const rtems_termios_device_handler ns16550_handler_interrupt = {
844  .first_open = ns16550_open,
845  .last_close = ns16550_close,
846  .poll_read = NULL,
847  .write = ns16550_write_support_int,
848  .set_attributes = ns16550_set_attributes,
849  .mode = TERMIOS_IRQ_DRIVEN
850};
851
852const rtems_termios_device_handler ns16550_handler_polled = {
853  .first_open = ns16550_open,
854  .last_close = ns16550_close,
855  .poll_read = ns16550_polled_getchar,
856  .write = ns16550_write_support_polled,
857  .set_attributes = ns16550_set_attributes,
858  .mode = TERMIOS_POLLED
859};
860
861const rtems_termios_device_handler ns16550_handler_task = {
862  .first_open = ns16550_open,
863  .last_close = ns16550_close,
864  .poll_read = ns16550_read_task,
865  .write = ns16550_write_support_task,
866  .set_attributes = ns16550_set_attributes,
867  .mode = TERMIOS_TASK_DRIVEN
868};
Note: See TracBrowser for help on using the repository browser.