OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rtems/] [c/] [src/] [libchip/] [serial/] [ns16550.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 158 chris
/*
2
 *  This file contains the TTY driver for the National Semiconductor NS16550.
3
 *
4
 *  This part is widely cloned and second sourced.  It is found in a number
5
 *  of "Super IO" controllers.
6
 *
7
 *  COPYRIGHT (c) 1998 by Radstone Technology
8
 *
9
 *
10
 * THIS FILE IS PROVIDED TO YOU, THE USER, "AS IS", WITHOUT WARRANTY OF ANY
11
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
12
 * IMPLIED WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK
13
 * AS TO THE QUALITY AND PERFORMANCE OF ALL CODE IN THIS FILE IS WITH YOU.
14
 *
15
 * You are hereby granted permission to use, copy, modify, and distribute
16
 * this file, provided that this notice, plus the above copyright notice
17
 * and disclaimer, appears in all copies. Radstone Technology will provide
18
 * no support for this code.
19
 *
20
 *  This driver uses the termios pseudo driver.
21
 */
22
 
23
#include <rtems.h>
24
#include <rtems/libio.h>
25
#include <stdlib.h>
26
#include <ringbuf.h>
27
 
28
#include <libchip/serial.h>
29
#include "ns16550_p.h"
30
#include "sersupp.h"
31
 
32
/*
33
 * Flow control is only supported when using interrupts
34
 */
35
 
36
console_flow ns16550_flow_RTSCTS = {
37
  ns16550_negate_RTS,             /* deviceStopRemoteTx */
38
  ns16550_assert_RTS              /* deviceStartRemoteTx */
39
};
40
 
41
console_flow ns16550_flow_DTRCTS = {
42
  ns16550_negate_DTR,             /* deviceStopRemoteTx */
43
  ns16550_assert_DTR              /* deviceStartRemoteTx */
44
};
45
 
46
console_fns ns16550_fns = {
47
  libchip_serial_default_probe,   /* deviceProbe */
48
  ns16550_open,                   /* deviceFirstOpen */
49
  NULL,                           /* deviceLastClose */
50
  NULL,                           /* deviceRead */
51
  ns16550_write_support_int,      /* deviceWrite */
52
  ns16550_initialize_interrupts,  /* deviceInitialize */
53
  ns16550_write_polled,           /* deviceWritePolled */
54
  ns16550_set_attributes,         /* deviceSetAttributes */
55
  TRUE                            /* deviceOutputUsesInterrupts */
56
};
57
 
58
console_fns ns16550_fns_polled = {
59
  libchip_serial_default_probe,        /* deviceProbe */
60
  ns16550_open,                        /* deviceFirstOpen */
61
  ns16550_close,                       /* deviceLastClose */
62
  ns16550_inbyte_nonblocking_polled,   /* deviceRead */
63
  ns16550_write_support_polled,        /* deviceWrite */
64
  ns16550_init,                        /* deviceInitialize */
65
  ns16550_write_polled,                /* deviceWritePolled */
66
  ns16550_set_attributes,              /* deviceSetAttributes */
67
  FALSE                                /* deviceOutputUsesInterrupts */
68
};
69
 
70
extern void set_vector( rtems_isr_entry, rtems_vector_number, int );
71
 
72
/*
73
 *  ns16550_init
74
 */
75
 
76
NS16550_STATIC void ns16550_init(int minor)
77
{
78
  unsigned32              pNS16550;
79
  unsigned8               ucTrash;
80
  unsigned8               ucDataByte;
81
  unsigned32              ulBaudDivisor;
82
  ns16550_context        *pns16550Context;
83
  setRegister_f           setReg;
84
  getRegister_f           getReg;
85
 
86
  pns16550Context=(ns16550_context *)malloc(sizeof(ns16550_context));
87
 
88
  Console_Port_Data[minor].pDeviceContext=(void *)pns16550Context;
89
  pns16550Context->ucModemCtrl=SP_MODEM_IRQ;
90
 
91
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
92
  setReg   = Console_Port_Tbl[minor].setRegister;
93
  getReg   = Console_Port_Tbl[minor].getRegister;
94
 
95
  /* Clear the divisor latch, clear all interrupt enables,
96
   * and reset and
97
   * disable the FIFO's.
98
   */
99
 
100
  (*setReg)(pNS16550, NS16550_LINE_CONTROL, 0x0);
101
  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
102
 
103
  /* Set the divisor latch and set the baud rate. */
104
 
105
  ulBaudDivisor = NS16550_Baud(
106
    (unsigned32) Console_Port_Tbl[minor].ulClock,
107
    (unsigned32) Console_Port_Tbl[minor].pDeviceParams
108
  );
109
  ucDataByte = SP_LINE_DLAB;
110
  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
111
 
112
  /* XXX */
113
  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
114
  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
115
 
116
  /* Clear the divisor latch and set the character size to eight bits */
117
  /* with one stop bit and no parity checking. */
118
  ucDataByte = EIGHT_BITS;
119
  (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucDataByte);
120
 
121
  /* Enable and reset transmit and receive FIFOs. TJA     */
122
  ucDataByte = SP_FIFO_ENABLE;
123
  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
124
 
125
  ucDataByte = SP_FIFO_ENABLE | SP_FIFO_RXRST | SP_FIFO_TXRST;
126
  (*setReg)(pNS16550, NS16550_FIFO_CONTROL, ucDataByte);
127
 
128
  ns16550_enable_interrupts(minor, NS16550_DISABLE_ALL_INTR);
129
 
130
  /* Set data terminal ready. */
131
  /* And open interrupt tristate line */
132
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
133
 
134
  ucTrash = (*getReg)(pNS16550, NS16550_LINE_STATUS );
135
  ucTrash = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER );
136
}
137
 
138
/*
139
 *  ns16550_open
140
 */
141
 
142
NS16550_STATIC int ns16550_open(
143
  int      major,
144
  int      minor,
145
  void    * arg
146
)
147
{
148
  /*
149
   * Assert DTR
150
   */
151
 
152
  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
153
    ns16550_assert_DTR(minor);
154
  }
155
 
156
  return(RTEMS_SUCCESSFUL);
157
}
158
 
159
/*
160
 *  ns16550_close
161
 */
162
 
163
NS16550_STATIC int ns16550_close(
164
  int      major,
165
  int      minor,
166
  void    * arg
167
)
168
{
169
  /*
170
   * Negate DTR
171
   */
172
  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_DTRCTS) {
173
    ns16550_negate_DTR(minor);
174
  }
175
 
176
  return(RTEMS_SUCCESSFUL);
177
}
178
 
179
/*
180
 *  ns16550_write_polled
181
 */
182
 
183
NS16550_STATIC void ns16550_write_polled(
184
  int   minor,
185
  char  cChar
186
)
187
{
188
  unsigned32              pNS16550;
189
  unsigned char           ucLineStatus;
190
  int                     iTimeout;
191
  getRegister_f           getReg;
192
  setRegister_f           setReg;
193
 
194
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
195
  getReg   = Console_Port_Tbl[minor].getRegister;
196
  setReg   = Console_Port_Tbl[minor].setRegister;
197
 
198
  /*
199
   * wait for transmitter holding register to be empty
200
   */
201
  iTimeout=1000;
202
  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
203
  while ((ucLineStatus & SP_LSR_THOLD) == 0) {
204
    /*
205
     * Yield while we wait
206
     */
207
#if 0
208
     if(_System_state_Is_up(_System_state_Get())) {
209
       rtems_task_wake_after(RTEMS_YIELD_PROCESSOR);
210
     }
211
#endif
212
     ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
213
     if(!--iTimeout) {
214
       break;
215
     }
216
  }
217
 
218
  /*
219
   * transmit character
220
   */
221
  (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, cChar);
222
}
223
 
224
/*
225
 * These routines provide control of the RTS and DTR lines
226
 */
227
 
228
/*
229
 *  ns16550_assert_RTS
230
 */
231
 
232
NS16550_STATIC int ns16550_assert_RTS(int minor)
233
{
234
  unsigned32              pNS16550;
235
  unsigned32              Irql;
236
  ns16550_context        *pns16550Context;
237
  setRegister_f           setReg;
238
 
239
  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
240
 
241
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
242
  setReg   = Console_Port_Tbl[minor].setRegister;
243
 
244
  /*
245
   * Assert RTS
246
   */
247
  rtems_interrupt_disable(Irql);
248
  pns16550Context->ucModemCtrl|=SP_MODEM_RTS;
249
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
250
  rtems_interrupt_enable(Irql);
251
  return 0;
252
}
253
 
254
/*
255
 *  ns16550_negate_RTS
256
 */
257
 
258
NS16550_STATIC int ns16550_negate_RTS(int minor)
259
{
260
  unsigned32              pNS16550;
261
  unsigned32              Irql;
262
  ns16550_context        *pns16550Context;
263
  setRegister_f           setReg;
264
 
265
  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
266
 
267
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
268
  setReg   = Console_Port_Tbl[minor].setRegister;
269
 
270
  /*
271
   * Negate RTS
272
   */
273
  rtems_interrupt_disable(Irql);
274
  pns16550Context->ucModemCtrl&=~SP_MODEM_RTS;
275
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
276
  rtems_interrupt_enable(Irql);
277
  return 0;
278
}
279
 
280
/*
281
 * These flow control routines utilise a connection from the local DTR
282
 * line to the remote CTS line
283
 */
284
 
285
/*
286
 *  ns16550_assert_DTR
287
 */
288
 
289
NS16550_STATIC int ns16550_assert_DTR(int minor)
290
{
291
  unsigned32              pNS16550;
292
  unsigned32              Irql;
293
  ns16550_context        *pns16550Context;
294
  setRegister_f           setReg;
295
 
296
  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
297
 
298
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
299
  setReg   = Console_Port_Tbl[minor].setRegister;
300
 
301
  /*
302
   * Assert DTR
303
   */
304
  rtems_interrupt_disable(Irql);
305
  pns16550Context->ucModemCtrl|=SP_MODEM_DTR;
306
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL, pns16550Context->ucModemCtrl);
307
  rtems_interrupt_enable(Irql);
308
  return 0;
309
}
310
 
311
/*
312
 *  ns16550_negate_DTR
313
 */
314
 
315
NS16550_STATIC int ns16550_negate_DTR(int minor)
316
{
317
  unsigned32              pNS16550;
318
  unsigned32              Irql;
319
  ns16550_context        *pns16550Context;
320
  setRegister_f           setReg;
321
 
322
  pns16550Context=(ns16550_context *) Console_Port_Data[minor].pDeviceContext;
323
 
324
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
325
  setReg   = Console_Port_Tbl[minor].setRegister;
326
 
327
  /*
328
   * Negate DTR
329
   */
330
  rtems_interrupt_disable(Irql);
331
  pns16550Context->ucModemCtrl&=~SP_MODEM_DTR;
332
  (*setReg)(pNS16550, NS16550_MODEM_CONTROL,pns16550Context->ucModemCtrl);
333
  rtems_interrupt_enable(Irql);
334
  return 0;
335
}
336
 
337
/*
338
 *  ns16550_set_attributes
339
 *
340
 *  This function sets the channel to reflect the requested termios
341
 *  port settings.
342
 */
343
 
344
NS16550_STATIC int ns16550_set_attributes(
345
  int                   minor,
346
  const struct termios *t
347
)
348
{
349
  unsigned32              pNS16550;
350
  unsigned32              ulBaudDivisor;
351
  unsigned8               ucLineControl;
352
  unsigned32              baud_requested;
353
  setRegister_f           setReg;
354
  getRegister_f           getReg;
355
  unsigned32              Irql;
356
 
357
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
358
  setReg   = Console_Port_Tbl[minor].setRegister;
359
  getReg   = Console_Port_Tbl[minor].getRegister;
360
 
361
  /*
362
   *  Calculate the baud rate divisor
363
   */
364
 
365
  baud_requested = t->c_cflag & CBAUD;
366
  if (!baud_requested)
367
    baud_requested = B9600;              /* default to 9600 baud */
368
 
369
  ulBaudDivisor = NS16550_Baud(
370
    (unsigned32) Console_Port_Tbl[minor].ulClock,
371
    termios_baud_to_number(baud_requested)
372
  );
373
 
374
  ucLineControl = 0;
375
 
376
  /*
377
   *  Parity
378
   */
379
 
380
  if (t->c_cflag & PARENB) {
381
    ucLineControl |= SP_LINE_PAR;
382
    if (!(t->c_cflag & PARODD))
383
      ucLineControl |= SP_LINE_ODD;
384
  }
385
 
386
  /*
387
   *  Character Size
388
   */
389
 
390
  if (t->c_cflag & CSIZE) {
391
    switch (t->c_cflag & CSIZE) {
392
      case CS5:  ucLineControl |= FIVE_BITS;  break;
393
      case CS6:  ucLineControl |= SIX_BITS;   break;
394
      case CS7:  ucLineControl |= SEVEN_BITS; break;
395
      case CS8:  ucLineControl |= EIGHT_BITS; break;
396
    }
397
  } else {
398
    ucLineControl |= EIGHT_BITS;               /* default to 9600,8,N,1 */
399
  }
400
 
401
  /*
402
   *  Stop Bits
403
   */
404
 
405
  if (t->c_cflag & CSTOPB) {
406
    ucLineControl |= SP_LINE_STOP;              /* 2 stop bits */
407
  } else {
408
    ;                                           /* 1 stop bit */
409
  }
410
 
411
  /*
412
   *  Now actually set the chip
413
   */
414
 
415
  rtems_interrupt_disable(Irql);
416
 
417
    /*
418
     *  Set the baud rate
419
     */
420
 
421
    (*setReg)(pNS16550, NS16550_LINE_CONTROL, SP_LINE_DLAB);
422
    /* XXX are these registers right? */
423
    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, ulBaudDivisor&0xff);
424
    (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, (ulBaudDivisor>>8)&0xff);
425
 
426
    /*
427
     *  Now write the line control
428
     */
429
    (*setReg)(pNS16550, NS16550_LINE_CONTROL, ucLineControl );
430
 
431
  rtems_interrupt_enable(Irql);
432
 
433
  return 0;
434
}
435
 
436
/*
437
 *  ns16550_process
438
 *
439
 *  This routine is the console interrupt handler for A port.
440
 */
441
 
442
NS16550_STATIC void ns16550_process(
443
        int             minor
444
)
445
{
446
  unsigned32              pNS16550;
447
  volatile unsigned8      ucLineStatus;
448
  volatile unsigned8      ucInterruptId;
449
  unsigned char           cChar;
450
  getRegister_f           getReg;
451
  setRegister_f           setReg;
452
 
453
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
454
  getReg   = Console_Port_Tbl[minor].getRegister;
455
  setReg   = Console_Port_Tbl[minor].setRegister;
456
 
457
  do {
458
    /*
459
     * Deal with any received characters
460
     */
461
    while(TRUE) {
462
      ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
463
      if(~ucLineStatus & SP_LSR_RDY) {
464
        break;
465
      }
466
      cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
467
      rtems_termios_enqueue_raw_characters(
468
        Console_Port_Data[minor].termios_data,
469
        &cChar,
470
        1
471
      );
472
    }
473
 
474
    /*
475
     *  TX all the characters we can
476
     */
477
 
478
    while(TRUE) {
479
        ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
480
        if(~ucLineStatus & SP_LSR_THOLD) {
481
          /*
482
           * We'll get another interrupt when
483
           * the transmitter holding reg. becomes
484
           * free again
485
           */
486
          break;
487
        }
488
 
489
#if 0
490
        /* XXX flow control not completely supported in libchip */
491
 
492
        if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
493
          ns16550_negate_RTS(minor);
494
        }
495
#endif
496
 
497
    rtems_termios_dequeue_characters(Console_Port_Data[minor].termios_data, 1);
498
    if (rtems_termios_dequeue_characters(
499
         Console_Port_Data[minor].termios_data, 1)) {
500
        if (Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
501
          ns16550_negate_RTS(minor);
502
        }
503
        Console_Port_Data[minor].bActive = FALSE;
504
        ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR_EXCEPT_TX);
505
        break;
506
      }
507
 
508
      ucInterruptId = (*getReg)(pNS16550, NS16550_INTERRUPT_ID);
509
    }
510
  } while((ucInterruptId&0xf)!=0x1);
511
}
512
 
513
/*
514
 *  ns16550_isr
515
 */
516
 
517
NS16550_STATIC rtems_isr ns16550_isr(
518
  rtems_vector_number vector
519
)
520
{
521
  int     minor;
522
 
523
  for(minor=0;minor<Console_Port_Count;minor++) {
524
    if(Console_Port_Tbl[minor].ulIntVector == vector &&
525
       Console_Port_Tbl[minor].deviceType == SERIAL_NS16550 ) {
526
      ns16550_process(minor);
527
    }
528
  }
529
}
530
 
531
/*
532
 *  ns16550_enable_interrupts
533
 *
534
 *  This routine initializes the port to have the specified interrupts masked.
535
 */
536
 
537
NS16550_STATIC void ns16550_enable_interrupts(
538
  int minor,
539
  int mask
540
)
541
{
542
  unsigned32     pNS16550;
543
  setRegister_f  setReg;
544
 
545
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
546
  setReg   = Console_Port_Tbl[minor].setRegister;
547
 
548
  (*setReg)(pNS16550, NS16550_INTERRUPT_ENABLE, mask);
549
}
550
 
551
/*
552
 *  ns16550_initialize_interrupts
553
 *
554
 *  This routine initializes the port to operate in interrupt driver mode.
555
 */
556
 
557
NS16550_STATIC void ns16550_initialize_interrupts(int minor)
558
{
559
  ns16550_init(minor);
560
 
561
  Console_Port_Data[minor].bActive = FALSE;
562
 
563
  set_vector(ns16550_isr, Console_Port_Tbl[minor].ulIntVector, 1);
564
 
565
  ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
566
}
567
 
568
/*
569
 *  ns16550_write_support_int
570
 *
571
 *  Console Termios output entry point.
572
 */
573
 
574
NS16550_STATIC int ns16550_write_support_int(
575
  int   minor,
576
  const char *buf,
577
  int   len
578
)
579
{
580
  unsigned32     Irql;
581
  unsigned32     pNS16550;
582
  setRegister_f  setReg;
583
 
584
  setReg   = Console_Port_Tbl[minor].setRegister;
585
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
586
 
587
  /*
588
   *  We are using interrupt driven output and termios only sends us
589
   *  one character at a time.
590
   */
591
 
592
  if ( !len )
593
    return 0;
594
 
595
  if(Console_Port_Tbl[minor].pDeviceFlow != &ns16550_flow_RTSCTS) {
596
    ns16550_assert_RTS(minor);
597
  }
598
 
599
  rtems_interrupt_disable(Irql);
600
    if ( Console_Port_Data[minor].bActive == FALSE) {
601
      Console_Port_Data[minor].bActive = TRUE;
602
      ns16550_enable_interrupts(minor, NS16550_ENABLE_ALL_INTR);
603
    }
604
    (*setReg)(pNS16550, NS16550_TRANSMIT_BUFFER, *buf);
605
  rtems_interrupt_enable(Irql);
606
 
607
  return 1;
608
}
609
 
610
/*
611
 *  ns16550_write_support_polled
612
 *
613
 *  Console Termios output entry point.
614
 *
615
 */
616
 
617
NS16550_STATIC int ns16550_write_support_polled(
618
  int         minor,
619
  const char *buf,
620
  int         len
621
)
622
{
623
  int nwrite = 0;
624
 
625
  /*
626
   * poll each byte in the string out of the port.
627
   */
628
  while (nwrite < len) {
629
    /*
630
     * transmit character
631
     */
632
    ns16550_write_polled(minor, *buf++);
633
    nwrite++;
634
  }
635
 
636
  /*
637
   * return the number of bytes written.
638
   */
639
  return nwrite;
640
}
641
 
642
/*
643
 *  ns16550_inbyte_nonblocking_polled
644
 *
645
 *  Console Termios polling input entry point.
646
 */
647
 
648
NS16550_STATIC int ns16550_inbyte_nonblocking_polled(
649
  int minor
650
)
651
{
652
  unsigned32           pNS16550;
653
  unsigned char        ucLineStatus;
654
  char                 cChar;
655
  getRegister_f        getReg;
656
 
657
  pNS16550 = Console_Port_Tbl[minor].ulCtrlPort1;
658
  getReg   = Console_Port_Tbl[minor].getRegister;
659
 
660
  ucLineStatus = (*getReg)(pNS16550, NS16550_LINE_STATUS);
661
  if(ucLineStatus & SP_LSR_RDY) {
662
    cChar = (*getReg)(pNS16550, NS16550_RECEIVE_BUFFER);
663
    return (int)cChar;
664
  } else {
665
    return -1;
666
  }
667
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.