source: rtems/c/src/libchip/serial/ns16550.c @ 1301468

5
Last change on this file since 1301468 was 1301468, checked in by Sebastian Huber <sebastian.huber@…>, on 04/03/17 at 10:51:51

bsps: Fix baud settings

Update #2897.

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