source: rtems/bsps/powerpc/tqm8xx/console/console.c @ 68920e7f

Last change on this file since 68920e7f was 68920e7f, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 12, 2018 at 8:43:02 AM

bsp/tqm8xx: Move DMA support to channel descriptor

Update #3513.

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