source: rtems/c/src/libchip/serial/ns16550.c @ 6ec438e

4.115
Last change on this file since 6ec438e was 6ec438e, checked in by Sebastian Huber <sebastian.huber@…>, on 10/07/14 at 06:29:16

libchip/serial: Add alternative NS16550 driver

Use the Termios device API.

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