source: rtems/bsps/powerpc/tqm8xx/console/console.c @ 53fb03fe

Last change on this file since 53fb03fe was 53fb03fe, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 12, 2018 at 8:24:56 AM

bsp/tqm8xx: Move rxBuf to channel descriptor

Update #3513.

  • Property mode set to 100644
File size: 27.6 KB
Line 
1/*===============================================================*\
2| Project: RTEMS TQM8xx BSP                                       |
3+-----------------------------------------------------------------+
4| This file has been adapted to MPC8xx by                         |
5|    Thomas Doerfler <Thomas.Doerfler@embedded-brains.de>         |
6|                    Copyright (c) 2008                           |
7|                    Embedded Brains GmbH                         |
8|                    Obere Lagerstr. 30                           |
9|                    D-82178 Puchheim                             |
10|                    Germany                                      |
11|                    rtems@embedded-brains.de                     |
12|                                                                 |
13| See the other copyright notice below for the original parts.    |
14+-----------------------------------------------------------------+
15| The license and distribution terms for this file may be         |
16| found in the file LICENSE in this distribution or at            |
17|                                                                 |
18| http://www.rtems.org/license/LICENSE.                           |
19|                                                                 |
20+-----------------------------------------------------------------+
21| this file contains the console driver                           |
22\*===============================================================*/
23/* derived from: */
24/*
25 *  SMC1/2 SCC1..4 raw console serial I/O.
26 *  adapted to work with up to 4 SCC and 2 SMC
27 *
28 *  This driver is an example of `TASK DRIVEN' `POLLING' or `INTERRUPT' I/O.
29 *
30 *  To run with interrupt-driven I/O, ensure m8xx_smc1_interrupt
31 *  is set before calling the initialization routine.
32 *
33 *  Author:
34 *    W. Eric Norum
35 *    Saskatchewan Accelerator Laboratory
36 *    University of Saskatchewan
37 *    Saskatoon, Saskatchewan, CANADA
38 *    eric@skatter.usask.ca
39 *
40 *  COPYRIGHT (c) 1989-1998.
41 *  On-Line Applications Research Corporation (OAR).
42 *  Copyright assigned to U.S. Government, 1994.
43 *
44 *  The license and distribution terms for this file may be
45 *  found in the file LICENSE in this distribution or at
46 *  http://www.OARcorp.com/rtems/license.html.
47 */
48
49#include <stdio.h>
50#include <stdlib.h>
51#include <termios.h>
52#include <unistd.h>
53
54#include <rtems.h>
55#include <rtems/console.h>
56#include <rtems/libio.h>
57#include <rtems/termiostypes.h>
58#include <rtems/bspIo.h>
59#include <rtems/error.h>
60
61#include <bsp.h>
62#include <mpc8xx.h>
63#include <bsp/irq.h>
64
65/*
66 * Interrupt-driven input buffer
67 */
68#define RXBUFSIZE       16
69
70#define M8xx_SICR_BRG1 (0)
71#define M8xx_SICR_BRG2 (1)
72#define M8xx_SICR_BRG3 (2)
73#define M8xx_SICR_BRG4 (3)
74
75#define M8xx_SICR_SCCRX_MSK(scc) ((  7) << (((scc))*8+3))
76#define M8xx_SICR_SCCRX(scc,clk) ((clk) << (((scc))*8+3))
77
78#define M8xx_SICR_SCCTX_MSK(scc) ((  7) << (((scc))*8+0))
79#define M8xx_SICR_SCCTX(scc,clk) ((clk) << (((scc))*8+0))
80
81#define M8xx_SIMODE_SMCCS(smc,clk) ((clk) << ((smc)*16+12))
82#define M8xx_SIMODE_SMCCS_MSK(smc) M8xx_SIMODE_SMCCS(smc,7)
83
84#define CONS_CHN_CNT 6
85#define CONS_CHN_SCC1 0
86#define CONS_CHN_SCC2 1
87#define CONS_CHN_SCC3 2
88#define CONS_CHN_SCC4 3
89#define CONS_CHN_SMC1 4
90#define CONS_CHN_SMC2 5
91#define CONS_CHN_NONE -1
92
93/*
94 * possible identifiers for bspopts.h: CONS_SxCy_MODE
95 */
96#define CONS_MODE_UNUSED -1
97#define CONS_MODE_POLLED TERMIOS_POLLED
98#define CONS_MODE_IRQ    TERMIOS_IRQ_DRIVEN
99
100#define CHN_IS_SCC(chan) ((chan) < CONS_CHN_SMC1)
101
102#define BRG_CNT 4
103
104#define MAX_IDL_DEFAULT 10
105#define DEVICEPREFIX "tty"
106
107/*
108 * I/O buffers and pointers to buffer descriptors
109 */
110#define SCC_RXBD_CNT 4
111#define SCC_TXBD_CNT 4
112typedef volatile char sccRxBuf_t[SCC_RXBD_CNT][RXBUFSIZE];
113
114/*
115 * Interrupt-driven callback
116 */
117static int m8xx_scc_mode[CONS_CHN_CNT];
118static void *sccttyp[CONS_CHN_CNT];
119typedef struct m8xx_console_chan_desc_s {
120  bool is_scc;                  /* true for SCC */
121  struct {
122    volatile m8xxSCCparms_t *sccp;
123    volatile m8xxSMCparms_t *smcp;
124  } parms;
125  struct {
126    volatile m8xxSCCRegisters_t *sccr;
127    volatile m8xxSMCRegisters_t *smcr;
128  } regs;
129  rtems_vector_number ivec_src;
130  int cr_chan_code;
131  int brg_used;
132  sccRxBuf_t *rxBuf;
133} m8xx_console_chan_desc_t;
134
135m8xx_console_chan_desc_t m8xx_console_chan_desc[CONS_CHN_CNT] = {
136  /* SCC1 */
137  { .is_scc = true,
138   .parms = {(m8xxSCCparms_t *)&(m8xx.scc1p),NULL},
139   .regs = {&(m8xx.scc1),NULL},
140   .ivec_src = BSP_CPM_IRQ_SCC1,
141   .cr_chan_code = M8xx_CR_CHAN_SCC1,
142   .brg_used = -1},
143  /* SCC2 */
144  { .is_scc = true,
145   .parms = {&(m8xx.scc2p),NULL},
146   .regs = {&(m8xx.scc2),NULL},
147   .ivec_src = BSP_CPM_IRQ_SCC2,
148   .cr_chan_code = M8xx_CR_CHAN_SCC2,
149   .brg_used = -1},
150  /* SCC3 */
151  { .is_scc = true,
152   .parms = {&(m8xx.scc3p),NULL},
153   .regs = {&(m8xx.scc3),NULL},
154   .ivec_src = BSP_CPM_IRQ_SCC3,
155   .cr_chan_code = M8xx_CR_CHAN_SCC3,
156   .brg_used = -1},
157  /* SCC4 */
158  { .is_scc = true,
159   .parms = {&(m8xx.scc4p),NULL},
160   .regs = {&(m8xx.scc4),NULL},
161   .ivec_src = BSP_CPM_IRQ_SCC4,
162   .cr_chan_code = M8xx_CR_CHAN_SCC4,
163   .brg_used = -1},
164  /* SMC1 */
165  { .is_scc = false,
166   .parms = {NULL,&(m8xx.smc1p)},
167   .regs = {NULL,&(m8xx.smc1)},
168   .ivec_src = BSP_CPM_IRQ_SMC1,
169   .cr_chan_code = M8xx_CR_CHAN_SMC1,
170   .brg_used = -1},
171  /* SMC2 */
172  { .is_scc = false,
173   .parms = {NULL,&(m8xx.smc2p)},
174   .regs = {NULL,&(m8xx.smc2)},
175   .ivec_src = BSP_CPM_IRQ_SMC2_OR_PIP,
176   .cr_chan_code = M8xx_CR_CHAN_SMC2,
177   .brg_used = -1}};
178
179#define CHN_PARAM_GET(chan,param)                       \
180  (m8xx_console_chan_desc[chan].is_scc                  \
181   ? m8xx_console_chan_desc[chan].parms.sccp->param     \
182   : m8xx_console_chan_desc[chan].parms.smcp->param)
183
184#define CHN_PARAM_SET(chan,param,value)                         \
185  do {if (m8xx_console_chan_desc[chan].is_scc)                  \
186      m8xx_console_chan_desc[chan].parms.sccp->param = value;   \
187    else                                                        \
188      m8xx_console_chan_desc[chan].parms.smcp->param = value;   \
189  } while (0)
190
191#define CHN_EVENT_GET(chan)                             \
192  (m8xx_console_chan_desc[chan].is_scc                  \
193   ? m8xx_console_chan_desc[chan].regs.sccr->scce       \
194   : m8xx_console_chan_desc[chan].regs.smcr->smce)
195
196#define CHN_EVENT_CLR(chan,mask)                                \
197  do {                                                          \
198    if (m8xx_console_chan_desc[chan].is_scc)                    \
199      m8xx_console_chan_desc[chan].regs.sccr->scce = (mask);    \
200    else                                                        \
201      m8xx_console_chan_desc[chan].regs.smcr->smce = (mask);    \
202  }while (0)
203
204#define CHN_MASK_GET(chan)                              \
205  (m8xx_console_chan_desc[chan].is_scc                  \
206   ? m8xx_console_chan_desc[chan].regs.sccr->sccm       \
207   : m8xx_console_chan_desc[chan].regs.smcr->smcm)
208
209#define CHN_MASK_SET(chan,mask)                                 \
210  do {                                                          \
211    if (m8xx_console_chan_desc[chan].is_scc)                    \
212      m8xx_console_chan_desc[chan].regs.sccr->sccm = (mask);    \
213    else                                                        \
214      m8xx_console_chan_desc[chan].regs.smcr->smcm = (mask);    \
215  }while (0)
216
217
218static volatile m8xxBufferDescriptor_t *sccFrstRxBd[CONS_CHN_CNT];
219static volatile m8xxBufferDescriptor_t *sccCurrRxBd[CONS_CHN_CNT];
220static volatile m8xxBufferDescriptor_t *sccFrstTxBd[CONS_CHN_CNT];
221static volatile m8xxBufferDescriptor_t *sccPrepTxBd[CONS_CHN_CNT];
222static volatile m8xxBufferDescriptor_t *sccDequTxBd[CONS_CHN_CNT];
223
224/*
225 * Compute baud-rate-generator configuration register value
226 */
227static uint32_t
228sccBRGval (int baud)
229{
230  int divisor;
231  int div16 = 0;
232
233  divisor = ((BSP_bus_frequency / 16) + (baud / 2)) / baud;
234  if (divisor > 4096) {
235    div16 = 1;
236    divisor = (divisor + 8) / 16;
237  }
238  return M8xx_BRG_EN | M8xx_BRG_EXTC_BRGCLK | ((divisor - 1) << 1) | div16;
239}
240
241typedef struct {
242  uint32_t reg_content;
243  int link_cnt;
244}brg_state_t;
245brg_state_t scc_brg_state[BRG_CNT];
246
247/*
248 * initialize brg_state
249 */
250static void sccBRGinit(void)
251{
252  int brg_idx;
253
254  for (brg_idx = 0;brg_idx < BRG_CNT;brg_idx++) {
255    scc_brg_state[brg_idx].reg_content = 0;
256    scc_brg_state[brg_idx].link_cnt    = 0;
257  }
258#ifndef MDE360
259  /*
260   * on ZEM40, init CLK4/5 inputs
261   */
262  m8xx.papar |=  ((1 << 11) | (1 << 12));
263  m8xx.padir &= ~((1 << 11) | (1 << 12));
264#endif
265}
266
267#if CONS_USE_EXT_CLK
268/*
269 * input clock frq for CPM clock inputs
270 */
271static uint32_t clkin_frq[2][4] = {
272#ifdef MDE360
273  {0,0,0,0},
274  {0,0,0,0}
275#else
276  {0,0,0,1843000},
277  {1843000,0,0,0}
278#endif
279};
280#endif
281
282/*
283 * allocate, set and connect baud rate generators
284 * FIXME: or clock input
285 * FIXME: set pin to be clock input
286 */
287
288static int sccBRGalloc(int chan,int baud)
289{
290  rtems_interrupt_level level;
291  m8xx_console_chan_desc_t *chan_desc = &(m8xx_console_chan_desc[chan]);
292  uint32_t reg_val;
293  int old_brg;
294  int new_brg = -1;
295  int brg_idx;
296#if CONS_USE_EXT_CLK
297  int clk_group;
298  int clk_sel;
299#endif
300
301  old_brg = chan_desc->brg_used;
302  /* compute brg register contents needed */
303  reg_val = sccBRGval(baud);
304
305#if CONS_EXT_CLK
306  /* search for clock input with this frq */
307  clk_group = ((chan == CONS_CHN_SCC3) ||
308               (chan == CONS_CHN_SCC4) ||
309               (chan == CONS_CHN_SMC2)) ? 1 : 0;
310
311  for (clk_sel = 0, new_brg = -1;
312       (clk_sel < 4) && (new_brg < 0);
313       clk_sel++) {
314    if (baud == (clkin_frq[clk_group][clk_sel] / 16)) {
315      new_brg = clk_sel + 4;
316    }
317  }
318#endif
319
320  rtems_interrupt_disable(level);
321
322  if (new_brg < 0) {
323    /* search for brg with this settings */
324    for (brg_idx = 0;
325         (new_brg < 0) && (brg_idx < BRG_CNT);
326         brg_idx++) {
327      if (scc_brg_state[brg_idx].reg_content == reg_val) {
328        new_brg = brg_idx;
329      }
330    }
331    /*
332     * if not found: check, whether brg currently in use
333     * is linked only from our channel
334     */
335    if ((new_brg < 0) &&
336        (old_brg >= 0) &&
337        (scc_brg_state[old_brg].link_cnt == 1)) {
338      new_brg = old_brg;
339    }
340    /* if not found: search for unused brg, set it  */
341    for (brg_idx = 0;
342         (new_brg < 0) && (brg_idx < BRG_CNT);
343         brg_idx++) {
344      if (scc_brg_state[brg_idx].link_cnt == 0) {
345        new_brg = brg_idx;
346      }
347    }
348  }
349
350  /* decrease old link count */
351  if ((old_brg >= 0) &&
352      (old_brg < 4)) {
353    scc_brg_state[old_brg].link_cnt--;
354  }
355  /* increase new brg link count, set brg */
356  if ((new_brg >= 0) &&
357      (new_brg < 4)) {
358    scc_brg_state[new_brg].link_cnt++;
359    scc_brg_state[new_brg].reg_content = reg_val;
360    (&m8xx.brgc1)[new_brg] = reg_val;
361  }
362  rtems_interrupt_enable(level);
363
364  /* connect to scc/smc */
365  if (new_brg >= 0) {
366    m8xx_console_chan_desc[chan].brg_used = new_brg;
367    /*
368     * Put SCC in NMSI mode, connect SCC to BRG or CLKx
369     */
370    if (m8xx_console_chan_desc[chan].is_scc) {
371      m8xx.sicr = ((m8xx.sicr & ~(M8xx_SICR_SCCRX_MSK(chan) |
372                                  M8xx_SICR_SCCTX_MSK(chan))) |
373                   M8xx_SICR_SCCRX(chan,new_brg)|
374                   M8xx_SICR_SCCTX(chan,new_brg));
375    }
376    else {
377      /* connect SMC to BRGx or CLKx... */
378      m8xx.simode = ((m8xx.simode & ~(M8xx_SIMODE_SMCCS_MSK(chan - CONS_CHN_SMC1)))|
379                     M8xx_SIMODE_SMCCS(chan - CONS_CHN_SMC1,new_brg));
380    }
381  }
382  return (new_brg < 0);
383}
384
385
386/*
387 * Hardware-dependent portion of tcsetattr().
388 */
389static int
390sccSetAttributes (int minor, const struct termios *t)
391{
392  int baud;
393
394  switch (t->c_ospeed) {
395  default:      baud = -1;      break;
396  case B50:     baud = 50;      break;
397  case B75:     baud = 75;      break;
398  case B110:    baud = 110;     break;
399  case B134:    baud = 134;     break;
400  case B150:    baud = 150;     break;
401  case B200:    baud = 200;     break;
402  case B300:    baud = 300;     break;
403  case B600:    baud = 600;     break;
404  case B1200:   baud = 1200;    break;
405  case B1800:   baud = 1800;    break;
406  case B2400:   baud = 2400;    break;
407  case B4800:   baud = 4800;    break;
408  case B9600:   baud = 9600;    break;
409  case B19200:  baud = 19200;   break;
410  case B38400:  baud = 38400;   break;
411  case B57600:  baud = 57600;   break;
412  case B115200: baud = 115200;  break;
413  case B230400: baud = 230400;  break;
414  case B460800: baud = 460800;  break;
415  }
416  return sccBRGalloc(minor,baud);
417  return 0;
418}
419
420/*
421 * Interrupt handler
422 */
423static rtems_isr
424sccInterruptHandler (void *arg)
425{
426  int chan = (int)arg;
427
428  /*
429   * Buffer received?
430   */
431  if (CHN_EVENT_GET(chan) & 0x1) {
432    /*
433     * clear SCC event flag
434     */
435    CHN_EVENT_CLR(chan,0x01);
436    /*
437     * process event
438     */
439    while ((sccCurrRxBd[chan]->status & M8xx_BD_EMPTY) == 0) {
440      if (sccttyp[chan] != NULL) {
441        rtems_cache_invalidate_multiple_data_lines((void *)sccCurrRxBd[chan]->buffer,
442                                                   sccCurrRxBd[chan]->length);
443        rtems_termios_enqueue_raw_characters (sccttyp[chan],
444                                              (char *)sccCurrRxBd[chan]->buffer,
445                                              sccCurrRxBd[chan]->length);
446      }
447      /*
448       * clear status
449       */
450      sccCurrRxBd[chan]->status =
451        (sccCurrRxBd[chan]->status
452         & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
453        | M8xx_BD_EMPTY;
454      /*
455       * advance to next BD
456       */
457      if ((sccCurrRxBd[chan]->status & M8xx_BD_WRAP) != 0) {
458        sccCurrRxBd[chan] = sccFrstRxBd[chan];
459      }
460      else {
461        sccCurrRxBd[chan]++;
462      }
463    }
464  }
465  /*
466   * Buffer transmitted?
467   */
468  if (CHN_EVENT_GET(chan) & 0x2) {
469    /*
470     * then clear interrupt event bit
471     */
472    CHN_EVENT_CLR(chan,0x2);
473    /*
474     * and signal successful transmit to termios
475     */
476    /*
477     * FIXME: multiple dequeue calls for multiple buffers
478     */
479    while((sccDequTxBd[chan] != sccPrepTxBd[chan]) &&
480          ((sccDequTxBd[chan]->status & M8xx_BD_READY) == 0)) {
481      if (sccttyp[chan] != NULL) {
482        rtems_termios_dequeue_characters (sccttyp[chan],
483                                          sccDequTxBd[chan]->length);
484      }
485      /*
486       * advance to next BD
487       */
488      if ((sccDequTxBd[chan]->status & M8xx_BD_WRAP) != 0) {
489        sccDequTxBd[chan] = sccFrstTxBd[chan];
490      }
491      else {
492        sccDequTxBd[chan]++;
493      }
494    }
495  }
496}
497
498static void
499mpc8xx_console_irq_on(int chan)
500{
501    CHN_MASK_SET(chan, 3);      /* Enable TX and RX interrupts */
502}
503
504static void
505sccInitialize (m8xx_console_chan_desc_t *cd, int chan)
506{
507  int i;
508  /*
509   * allocate buffers
510   * FIXME: use a cache-line size boundary alloc here
511   */
512  cd->rxBuf = rtems_cache_aligned_malloc(sizeof(*cd->rxBuf));
513  if (cd->rxBuf == NULL) {
514    rtems_panic("Cannot allocate console rx buffer\n");
515  }
516
517  /*
518   * Allocate buffer descriptors
519   */
520  sccCurrRxBd[chan] =
521    sccFrstRxBd[chan] = m8xx_bd_allocate(SCC_RXBD_CNT);
522  sccPrepTxBd[chan] =
523    sccDequTxBd[chan] =
524    sccFrstTxBd[chan] = m8xx_bd_allocate(SCC_TXBD_CNT);
525  switch(chan) {
526  case CONS_CHN_SCC1:
527    /*
528     * Configure port A pins to enable TXD1 and RXD1 pins
529     * FIXME: add setup for modem control lines....
530     */
531    m8xx.papar |=  0x03;
532    m8xx.padir &= ~0x03;
533
534    /*
535     * Configure port C pins to enable RTS1 pins (static active low)
536     */
537    m8xx.pcpar &= ~0x01;
538    m8xx.pcso  &= ~0x01;
539    m8xx.pcdir |=  0x01;
540    m8xx.pcdat &= ~0x01;
541    break;
542  case CONS_CHN_SCC2:
543    /*
544     * Configure port A pins to enable TXD2 and RXD2 pins
545     * FIXME: add setup for modem control lines....
546     */
547    m8xx.papar |=  0x0C;
548    m8xx.padir &= ~0x0C;
549
550    /*
551     * Configure port C pins to enable RTS2 pins (static active low)
552     */
553    m8xx.pcpar &= ~0x02;
554    m8xx.pcso  &= ~0x02;
555    m8xx.pcdir |=  0x02;
556    m8xx.pcdat &= ~0x02;
557    break;
558  case CONS_CHN_SCC3:
559    /*
560     * Configure port A pins to enable TXD3 and RXD3 pins
561     * FIXME: add setup for modem control lines....
562     */
563    m8xx.papar |=  0x30;
564    m8xx.padir &= ~0x30;
565
566    /*
567     * Configure port C pins to enable RTS3 (static active low)
568     */
569    m8xx.pcpar &= ~0x04;
570    m8xx.pcso  &= ~0x04;
571    m8xx.pcdir |=  0x04;
572    m8xx.pcdat &= ~0x04;
573    break;
574  case CONS_CHN_SCC4:
575    /*
576     * Configure port A pins to enable TXD4 and RXD4 pins
577     * FIXME: add setup for modem control lines....
578     */
579    m8xx.papar |=  0xC0;
580    m8xx.padir &= ~0xC0;
581
582    /*
583     * Configure port C pins to enable RTS4 pins (static active low)
584     */
585    m8xx.pcpar &= ~0x08;
586    m8xx.pcso  &= ~0x08;
587    m8xx.pcdir |=  0x08;
588    m8xx.pcdat &= ~0x08;
589    break;
590  case CONS_CHN_SMC1:
591    /*
592     * Configure port B pins to enable SMTXD1 and SMRXD1 pins
593     */
594    m8xx.pbpar |=  0xC0;
595    m8xx.pbdir &= ~0xC0;
596    break;
597  case CONS_CHN_SMC2:
598    /*
599     * Configure port B pins to enable SMTXD2 and SMRXD2 pins
600     */
601    m8xx.pbpar |=  0xC00;
602    m8xx.pbdir &= ~0xC00;
603    break;
604  }
605  /*
606   * allocate and connect BRG
607   */
608  sccBRGalloc(chan,9600);
609
610
611  /*
612   * Set up SCCx parameter RAM common to all protocols
613   */
614  CHN_PARAM_SET(chan,rbase,(char *)sccFrstRxBd[chan] - (char *)&m8xx);
615  CHN_PARAM_SET(chan,tbase,(char *)sccFrstTxBd[chan] - (char *)&m8xx);
616  CHN_PARAM_SET(chan,rfcr ,M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0));
617  CHN_PARAM_SET(chan,tfcr ,M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0));
618  if (m8xx_scc_mode[chan] != TERMIOS_POLLED)
619    CHN_PARAM_SET(chan,mrblr,RXBUFSIZE);
620  else
621    CHN_PARAM_SET(chan,mrblr,1);
622
623  /*
624   * Set up SCCx parameter RAM UART-specific parameters
625   */
626  CHN_PARAM_SET(chan,un.uart.max_idl ,MAX_IDL_DEFAULT);
627  CHN_PARAM_SET(chan,un.uart.brkln   ,0);
628  CHN_PARAM_SET(chan,un.uart.brkec   ,0);
629  CHN_PARAM_SET(chan,un.uart.brkcr   ,0);
630  if (m8xx_console_chan_desc[chan].is_scc) {
631    m8xx_console_chan_desc[chan].parms.sccp->un.uart.character[0]=0x8000; /* no char filter */
632    m8xx_console_chan_desc[chan].parms.sccp->un.uart.rccm=0x80FF; /* control character mask */
633  }
634
635  /*
636   * Set up the Receive Buffer Descriptors
637   */
638  for (i = 0;i < SCC_RXBD_CNT;i++) {
639    sccFrstRxBd[chan][i].status = M8xx_BD_EMPTY | M8xx_BD_INTERRUPT;
640    if (i == SCC_RXBD_CNT-1) {
641      sccFrstRxBd[chan][i].status |= M8xx_BD_WRAP;
642    }
643    sccFrstRxBd[chan][i].length = 0;
644    sccFrstRxBd[chan][i].buffer = (*cd->rxBuf)[i];
645  }
646  /*
647   * Setup the Transmit Buffer Descriptor
648   */
649  for (i = 0;i < SCC_TXBD_CNT;i++) {
650    sccFrstTxBd[chan][i].status = M8xx_BD_INTERRUPT;
651    if (i == SCC_TXBD_CNT-1) {
652      sccFrstTxBd[chan][i].status |= M8xx_BD_WRAP;
653    }
654    sccFrstTxBd[chan][i].length = 0;
655    sccFrstTxBd[chan][i].buffer = NULL;
656  }
657
658  /*
659   * Set up SCC general and protocol-specific mode registers
660   */
661  CHN_EVENT_CLR(chan,~0);       /* Clear any pending events */
662  CHN_MASK_SET(chan,0);         /* Mask all interrupt/event sources */
663
664  if (m8xx_console_chan_desc[chan].is_scc) {
665    m8xx_console_chan_desc[chan].regs.sccr->psmr = 0xb000; /* 8N1, CTS flow control */
666    m8xx_console_chan_desc[chan].regs.sccr->gsmr_h = 0x00000000;
667    m8xx_console_chan_desc[chan].regs.sccr->gsmr_l = 0x00028004; /* UART mode */
668  }
669  else {
670    m8xx_console_chan_desc[chan].regs.smcr->smcmr = 0x4820;
671  }
672  /*
673   * Send "Init parameters" command
674   */
675  m8xx_cp_execute_cmd(M8xx_CR_OP_INIT_RX_TX
676                      | m8xx_console_chan_desc[chan].cr_chan_code);
677
678  /*
679   * Enable receiver and transmitter
680   */
681  if (m8xx_console_chan_desc[chan].is_scc) {
682    m8xx_console_chan_desc[chan].regs.sccr->gsmr_l |= 0x00000030;
683  }
684  else {
685    m8xx_console_chan_desc[chan].regs.smcr->smcmr |= 0x0003;
686  }
687
688  if (m8xx_scc_mode[chan] != TERMIOS_POLLED) {
689    rtems_status_code sc;
690
691    sc = rtems_interrupt_handler_install(
692      m8xx_console_chan_desc[chan].ivec_src,
693      "SCC",
694      RTEMS_INTERRUPT_UNIQUE,
695      sccInterruptHandler,
696      (void *)chan
697    );
698    if (sc != RTEMS_SUCCESSFUL) {
699      rtems_panic("console: cannot install IRQ handler");
700    }
701    mpc8xx_console_irq_on(chan);
702  }
703}
704
705/*
706 * polled scc read function
707 */
708static int
709sccPollRead (int minor)
710{
711  int c = -1;
712  int chan = minor;
713
714  while(1) {
715    if ((sccCurrRxBd[chan]->status & M8xx_BD_EMPTY) != 0) {
716      return -1;
717    }
718
719    if (0 == (sccCurrRxBd[chan]->status & (M8xx_BD_OVERRUN
720                                           | M8xx_BD_PARITY_ERROR
721                                           | M8xx_BD_FRAMING_ERROR
722                                           | M8xx_BD_BREAK
723                                           | M8xx_BD_IDLE))) {
724      /* character received and no error detected */
725      rtems_cache_invalidate_multiple_data_lines((void *)sccCurrRxBd[chan]->buffer,
726                                                 sccCurrRxBd[chan]->length);
727      c = (unsigned)*((char *)sccCurrRxBd[chan]->buffer);
728      /*
729       * clear status
730       */
731    }
732    sccCurrRxBd[chan]->status =
733      (sccCurrRxBd[chan]->status
734       & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
735      | M8xx_BD_EMPTY;
736    /*
737     * advance to next BD
738     */
739    if ((sccCurrRxBd[chan]->status & M8xx_BD_WRAP) != 0) {
740      sccCurrRxBd[chan] = sccFrstRxBd[chan];
741    }
742    else {
743      sccCurrRxBd[chan]++;
744    }
745    if (c >= 0) {
746      return c;
747    }
748  }
749}
750
751
752/*
753 * Device-dependent write routine
754 * Interrupt-driven devices:
755 *      Begin transmission of as many characters as possible (minimum is 1).
756 * Polling devices:
757 *      Transmit all characters.
758 */
759static ssize_t
760sccInterruptWrite (int minor, const char *buf, size_t len)
761{
762  if (len > 0) {
763    int chan = minor;
764
765    if ((sccPrepTxBd[chan]->status & M8xx_BD_READY) == 0) {
766      sccPrepTxBd[chan]->buffer = (char *)buf;
767      sccPrepTxBd[chan]->length = len;
768      rtems_cache_flush_multiple_data_lines((const void *)buf,len);
769      /*
770       * clear status, set ready bit
771       */
772      sccPrepTxBd[chan]->status =
773        (sccPrepTxBd[chan]->status
774         & M8xx_BD_WRAP)
775        | M8xx_BD_READY | M8xx_BD_INTERRUPT;
776      if ((sccPrepTxBd[chan]->status & M8xx_BD_WRAP) != 0) {
777        sccPrepTxBd[chan] = sccFrstTxBd[chan];
778      }
779      else {
780        sccPrepTxBd[chan]++;
781      }
782    }
783  }
784
785  return 0;
786}
787
788static ssize_t
789sccPollWrite (int minor, const char *buf, size_t len)
790{
791  static char txBuf[CONS_CHN_CNT][SCC_TXBD_CNT];
792  int chan = minor;
793  int bd_used;
794  size_t retval = len;
795
796  while (len--) {
797    while (sccPrepTxBd[chan]->status & M8xx_BD_READY)
798      continue;
799    bd_used = sccPrepTxBd[chan]-sccFrstTxBd[chan];
800    txBuf[chan][bd_used] = *buf++;
801      rtems_cache_flush_multiple_data_lines((const void *)&txBuf[chan][bd_used],
802                                            sizeof(txBuf[chan][bd_used]));
803    sccPrepTxBd[chan]->buffer = &(txBuf[chan][bd_used]);
804    sccPrepTxBd[chan]->length = 1;
805    sccPrepTxBd[chan]->status =
806      (sccPrepTxBd[chan]->status
807       & M8xx_BD_WRAP)
808      | M8xx_BD_READY;
809    if ((sccPrepTxBd[chan]->status & M8xx_BD_WRAP) != 0) {
810      sccPrepTxBd[chan] = sccFrstTxBd[chan];
811    }
812    else {
813      sccPrepTxBd[chan]++;
814    }
815  }
816  return retval;
817}
818
819/*
820 * printk basic support
821 */
822int BSP_output_chan = CONS_CHN_NONE; /* channel used for printk operation */
823
824static void console_debug_putc_onlcr(const char c)
825{
826  rtems_interrupt_level irq_level;
827
828  if (BSP_output_chan != CONS_CHN_NONE) {
829    rtems_interrupt_disable(irq_level);
830
831    sccPollWrite (BSP_output_chan,&c,1);
832    rtems_interrupt_enable(irq_level);
833  }
834}
835
836BSP_output_char_function_type     BSP_output_char = console_debug_putc_onlcr;
837BSP_polling_getchar_function_type BSP_poll_char = NULL;
838
839
840/*
841***************
842* BOILERPLATE *
843***************
844*/
845
846struct {
847  rtems_device_minor_number minor;
848  int driver_mode;
849} channel_list[] = {
850  {CONS_CHN_SMC1,CONS_SMC1_MODE},
851  {CONS_CHN_SMC2,CONS_SMC2_MODE},
852  {CONS_CHN_SCC1,CONS_SCC1_MODE},
853  {CONS_CHN_SCC2,CONS_SCC2_MODE},
854  {CONS_CHN_SCC3,CONS_SCC3_MODE},
855  {CONS_CHN_SCC4,CONS_SCC4_MODE}
856};
857
858
859/*
860 * Initialize and register the device
861 */
862rtems_device_driver console_initialize(rtems_device_major_number  major,
863                                       rtems_device_minor_number  minor,/* ignored */
864                                       void                      *arg
865                                       )
866{
867  rtems_status_code status = RTEMS_SUCCESSFUL;
868  int chan,entry,ttynum;
869  char tty_name[] = "/dev/tty00";
870
871  /*
872   * Set up TERMIOS
873   */
874  rtems_termios_initialize ();
875  /*
876   * init BRG allocataion
877   */
878  sccBRGinit();
879  ttynum = 0;
880  for (entry = 0;
881       (entry < sizeof(channel_list)/sizeof(channel_list[0]))
882         && (status == RTEMS_SUCCESSFUL);
883       entry++) {
884    if (channel_list[entry].driver_mode != CONS_MODE_UNUSED) {
885      /*
886       * Do device-specific initialization
887       */
888      chan = channel_list[entry].minor;
889      m8xx_scc_mode[chan] = channel_list[entry].driver_mode;
890      sccInitialize (&m8xx_console_chan_desc[chan], chan);
891
892      /*
893       * build device name
894       */
895      tty_name[sizeof(tty_name)-2] = '0'+ttynum;
896      ttynum++;
897      /*
898       * Register the device
899       */
900      status = rtems_io_register_name (tty_name,
901                                       major,
902                                       channel_list[entry].minor);
903      if (status != RTEMS_SUCCESSFUL) {
904        rtems_fatal_error_occurred (status);
905      }
906    }
907  }
908  /*
909   * register /dev/console
910   */
911  status = rtems_io_register_name ("/dev/console",
912                                   major,
913                                   CONSOLE_CHN);
914  if (status != RTEMS_SUCCESSFUL) {
915    rtems_fatal_error_occurred (status);
916  }
917  /*
918   * enable printk support
919   */
920  BSP_output_chan = PRINTK_CHN;
921
922  return RTEMS_SUCCESSFUL;
923}
924
925/*
926 * Open the device
927 */
928rtems_device_driver console_open(
929                                 rtems_device_major_number major,
930                                 rtems_device_minor_number minor,
931                                 void                    * arg
932                                 )
933{
934  rtems_status_code status;
935  int chan = minor;
936  rtems_libio_open_close_args_t *args = (rtems_libio_open_close_args_t *)arg;
937  static const rtems_termios_callbacks interruptCallbacks = {
938    NULL,               /* firstOpen */
939    NULL,               /* lastClose */
940    NULL,               /* pollRead */
941    sccInterruptWrite,  /* write */
942    sccSetAttributes,   /* setAttributes */
943    NULL,               /* stopRemoteTx */
944    NULL,               /* startRemoteTx */
945    TERMIOS_IRQ_DRIVEN  /* outputUsesInterrupts */
946  };
947  static const rtems_termios_callbacks pollCallbacks = {
948    NULL,               /* firstOpen */
949    NULL,               /* lastClose */
950    sccPollRead,        /* pollRead */
951    sccPollWrite,       /* write */
952    sccSetAttributes,   /* setAttributes */
953    NULL,               /* stopRemoteTx */
954    NULL,               /* startRemoteTx */
955    0                   /* outputUsesInterrupts */
956  };
957
958  if (m8xx_scc_mode[chan] == TERMIOS_IRQ_DRIVEN) {
959    status = rtems_termios_open (major, minor, arg, &interruptCallbacks);
960    sccttyp[chan] = args->iop->data1;
961  }
962  else {
963    status = rtems_termios_open (major, minor, arg, &pollCallbacks);
964    sccttyp[chan] = args->iop->data1;
965  }
966  return status;
967}
968
969/*
970 * Close the device
971 */
972rtems_device_driver console_close(
973                                  rtems_device_major_number major,
974                                  rtems_device_minor_number minor,
975                                  void                    * arg
976                                  )
977{
978  rtems_status_code rc;
979
980  rc = rtems_termios_close (arg);
981  sccttyp[minor] = NULL;
982
983  return rc;
984
985}
986
987/*
988 * Read from the device
989 */
990rtems_device_driver console_read(
991                                 rtems_device_major_number major,
992                                 rtems_device_minor_number minor,
993                                 void                    * arg
994                                 )
995{
996  return rtems_termios_read (arg);
997}
998
999/*
1000 * Write to the device
1001 */
1002rtems_device_driver console_write(
1003                                  rtems_device_major_number major,
1004                                  rtems_device_minor_number minor,
1005                                  void                    * arg
1006                                  )
1007{
1008  return rtems_termios_write (arg);
1009}
1010
1011#if 0
1012static int scc_io_set_trm_char(rtems_device_minor_number minor,
1013                               rtems_libio_ioctl_args_t *ioa)
1014{
1015  rtems_status_code rc                = RTEMS_SUCCESSFUL;
1016  con360_io_trm_char_t *trm_char_info = ioa->buffer;
1017
1018  /*
1019   * check, that parameter is non-NULL
1020   */
1021  if ((rc == RTEMS_SUCCESSFUL) &&
1022      (trm_char_info == NULL)) {
1023    rc = RTEMS_INVALID_ADDRESS;
1024  }
1025  /*
1026   * transfer max_idl
1027   */
1028  if (rc == RTEMS_SUCCESSFUL) {
1029    if (trm_char_info->max_idl >= 0x10000) {
1030      rc = RTEMS_INVALID_NUMBER;
1031    }
1032    else if (trm_char_info->max_idl > 0) {
1033      CHN_PARAM_SET(minor,un.uart.max_idl ,trm_char_info->max_idl);
1034    }
1035    else if (trm_char_info->max_idl == 0) {
1036      CHN_PARAM_SET(minor,un.uart.max_idl ,MAX_IDL_DEFAULT);
1037    }
1038  }
1039  /*
1040   * transfer characters
1041   */
1042  if (rc == RTEMS_SUCCESSFUL) {
1043    if (trm_char_info->char_cnt > CON8XX_TRM_CHAR_CNT) {
1044      rc = RTEMS_TOO_MANY;
1045    }
1046    else if (trm_char_info->char_cnt >= 0) {
1047      /*
1048       * check, whether device is a SCC
1049       */
1050      if ((rc == RTEMS_SUCCESSFUL) &&
1051          !m8xx_console_chan_desc[minor].is_scc) {
1052        rc = RTEMS_UNSATISFIED;
1053      }
1054      else {
1055        int idx = 0;
1056        for(idx = 0;idx < trm_char_info->char_cnt;idx++) {
1057          m8xx_console_chan_desc[minor].parms.sccp->un.uart.character[idx] =
1058            trm_char_info->character[idx] & 0x00ff;
1059        }
1060        if (trm_char_info->char_cnt < CON8XX_TRM_CHAR_CNT) {
1061          m8xx_console_chan_desc[minor].parms.sccp
1062            ->un.uart.character[trm_char_info->char_cnt] = 0x8000;
1063        }
1064      }
1065    }
1066  }
1067
1068  return rc;
1069}
1070#endif
1071
1072/*
1073 * Handle ioctl request.
1074 */
1075rtems_device_driver console_control(
1076                                    rtems_device_major_number major,
1077                                    rtems_device_minor_number minor,
1078                                    void                    * arg
1079                                    )
1080{
1081  rtems_libio_ioctl_args_t *ioa=arg;
1082
1083  switch (ioa->command) {
1084#if 0
1085  case CON8XX_IO_SET_TRM_CHAR:
1086    return scc_io_set_trm_char(minor, ioa);
1087#endif
1088  default:
1089    return rtems_termios_ioctl (arg);
1090    break;
1091  }
1092}
1093
Note: See TracBrowser for help on using the repository browser.