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

5
Last change on this file since 5857e83c was 5857e83c, checked in by G S Niteesh <gsnb.gn@…>, on 02/09/20 at 19:21:43

libchip/ns16550: Allow user calculate baud divisor

This patch will allow the user to pass a function to calculate
the baud divisor.
This will allow for more flexibility, since for some BSPs
like raspberrypi, the calculation of baud divisor is different
from what is in the current driver.

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