source: rtems/bsps/powerpc/tqm8xx/console/console.c @ d22147e

Last change on this file since d22147e was d22147e, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 12, 2018 at 9:47:19 AM

bsp/tqm8xx: Convert console to new Termios API

Update #3513.

  • Property mode set to 100644
File size: 23.1 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 <unistd.h>
50
51#include <rtems.h>
52#include <rtems/console.h>
53#include <rtems/termiostypes.h>
54#include <rtems/bspIo.h>
55#include <rtems/error.h>
56
57#include <bsp.h>
58#include <mpc8xx.h>
59#include <bsp/irq.h>
60
61/*
62 * Interrupt-driven input buffer
63 */
64#define RXBUFSIZE       16
65
66#define M8xx_SICR_BRG1 (0)
67#define M8xx_SICR_BRG2 (1)
68#define M8xx_SICR_BRG3 (2)
69#define M8xx_SICR_BRG4 (3)
70
71#define M8xx_SICR_SCCRX_MSK(scc) ((  7) << (((scc))*8+3))
72#define M8xx_SICR_SCCRX(scc,clk) ((clk) << (((scc))*8+3))
73
74#define M8xx_SICR_SCCTX_MSK(scc) ((  7) << (((scc))*8+0))
75#define M8xx_SICR_SCCTX(scc,clk) ((clk) << (((scc))*8+0))
76
77#define M8xx_SIMODE_SMCCS(smc,clk) ((clk) << ((smc)*16+12))
78#define M8xx_SIMODE_SMCCS_MSK(smc) M8xx_SIMODE_SMCCS(smc,7)
79
80#define CONS_CHN_CNT 6
81#define CONS_CHN_SCC1 0
82#define CONS_CHN_SCC2 1
83#define CONS_CHN_SCC3 2
84#define CONS_CHN_SCC4 3
85#define CONS_CHN_SMC1 4
86#define CONS_CHN_SMC2 5
87#define CONS_CHN_NONE -1
88
89/*
90 * possible identifiers for bspopts.h: CONS_SxCy_MODE
91 */
92#define CONS_MODE_UNUSED -1
93#define CONS_MODE_POLLED TERMIOS_POLLED
94#define CONS_MODE_IRQ    TERMIOS_IRQ_DRIVEN
95
96#define CHN_IS_SCC(chan) ((chan) < CONS_CHN_SMC1)
97
98#define BRG_CNT 4
99
100#define MAX_IDL_DEFAULT 10
101#define DEVICEPREFIX "tty"
102
103/*
104 * I/O buffers and pointers to buffer descriptors
105 */
106#define SCC_RXBD_CNT 4
107#define SCC_TXBD_CNT 4
108typedef volatile char sccRxBuf_t[SCC_RXBD_CNT][RXBUFSIZE];
109
110/*
111 * Interrupt-driven callback
112 */
113typedef struct m8xx_console_chan_desc_s {
114  rtems_termios_device_context base;
115  volatile m8xxBufferDescriptor_t *sccFrstRxBd;
116  volatile m8xxBufferDescriptor_t *sccCurrRxBd;
117  volatile m8xxBufferDescriptor_t *sccFrstTxBd;
118  volatile m8xxBufferDescriptor_t *sccPrepTxBd;
119  volatile m8xxBufferDescriptor_t *sccDequTxBd;
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  int chan;
130  rtems_termios_device_mode mode;
131  rtems_vector_number ivec_src;
132  int cr_chan_code;
133  int brg_used;
134  sccRxBuf_t *rxBuf;
135} m8xx_console_chan_desc_t;
136
137m8xx_console_chan_desc_t m8xx_console_chan_desc[CONS_CHN_CNT] = {
138  /* SCC1 */
139  { .is_scc = true,
140   .parms = {(m8xxSCCparms_t *)&(m8xx.scc1p),NULL},
141   .regs = {&(m8xx.scc1),NULL},
142   .chan = CONS_CHN_SCC1,
143   .ivec_src = BSP_CPM_IRQ_SCC1,
144   .cr_chan_code = M8xx_CR_CHAN_SCC1,
145   .brg_used = -1},
146  /* SCC2 */
147  { .is_scc = true,
148   .parms = {&(m8xx.scc2p),NULL},
149   .regs = {&(m8xx.scc2),NULL},
150   .chan = CONS_CHN_SCC2,
151   .ivec_src = BSP_CPM_IRQ_SCC2,
152   .cr_chan_code = M8xx_CR_CHAN_SCC2,
153   .brg_used = -1},
154  /* SCC3 */
155  { .is_scc = true,
156   .parms = {&(m8xx.scc3p),NULL},
157   .regs = {&(m8xx.scc3),NULL},
158   .chan = CONS_CHN_SCC3,
159   .ivec_src = BSP_CPM_IRQ_SCC3,
160   .cr_chan_code = M8xx_CR_CHAN_SCC3,
161   .brg_used = -1},
162  /* SCC4 */
163  { .is_scc = true,
164   .parms = {&(m8xx.scc4p),NULL},
165   .regs = {&(m8xx.scc4),NULL},
166   .chan = CONS_CHN_SCC4,
167   .ivec_src = BSP_CPM_IRQ_SCC4,
168   .cr_chan_code = M8xx_CR_CHAN_SCC4,
169   .brg_used = -1},
170  /* SMC1 */
171  { .is_scc = false,
172   .parms = {NULL,&(m8xx.smc1p)},
173   .regs = {NULL,&(m8xx.smc1)},
174   .chan = CONS_CHN_SMC1,
175   .ivec_src = BSP_CPM_IRQ_SMC1,
176   .cr_chan_code = M8xx_CR_CHAN_SMC1,
177   .brg_used = -1},
178  /* SMC2 */
179  { .is_scc = false,
180   .parms = {NULL,&(m8xx.smc2p)},
181   .regs = {NULL,&(m8xx.smc2)},
182   .chan = CONS_CHN_SMC2,
183   .ivec_src = BSP_CPM_IRQ_SMC2_OR_PIP,
184   .cr_chan_code = M8xx_CR_CHAN_SMC2,
185   .brg_used = -1}};
186
187#define CHN_PARAM_GET(cd,param) \
188  (cd->is_scc                   \
189   ? cd->parms.sccp->param      \
190   : cd->parms.smcp->param)
191
192#define CHN_PARAM_SET(cd,param,value)   \
193  do {if (cd->is_scc)                   \
194      cd->parms.sccp->param = value;    \
195    else                                \
196      cd->parms.smcp->param = value;    \
197  } while (0)
198
199#define CHN_EVENT_GET(cd)       \
200  (cd->is_scc                   \
201   ? cd->regs.sccr->scce        \
202   : cd->regs.smcr->smce)
203
204#define CHN_EVENT_CLR(cd,mask)          \
205  do {                                  \
206    if (cd->is_scc)                     \
207      cd->regs.sccr->scce = (mask);     \
208    else                                \
209      cd->regs.smcr->smce = (mask);     \
210  }while (0)
211
212#define CHN_MASK_GET(cd)        \
213  (cd->is_scc                   \
214   ? cd->regs.sccr->sccm        \
215   : cd->regs.smcr->smcm)
216
217#define CHN_MASK_SET(cd,mask)           \
218  do {                                  \
219    if (cd->is_scc)                     \
220      cd->regs.sccr->sccm = (mask);     \
221    else                                \
222      cd->regs.smcr->smcm = (mask);     \
223  }while (0)
224
225/*
226 * Compute baud-rate-generator configuration register value
227 */
228static uint32_t
229sccBRGval (int baud)
230{
231  int divisor;
232  int div16 = 0;
233
234  divisor = ((BSP_bus_frequency / 16) + (baud / 2)) / baud;
235  if (divisor > 4096) {
236    div16 = 1;
237    divisor = (divisor + 8) / 16;
238  }
239  return M8xx_BRG_EN | M8xx_BRG_EXTC_BRGCLK | ((divisor - 1) << 1) | div16;
240}
241
242typedef struct {
243  uint32_t reg_content;
244  int link_cnt;
245}brg_state_t;
246brg_state_t scc_brg_state[BRG_CNT];
247
248/*
249 * initialize brg_state
250 */
251static void sccBRGinit(void)
252{
253  int brg_idx;
254
255  for (brg_idx = 0;brg_idx < BRG_CNT;brg_idx++) {
256    scc_brg_state[brg_idx].reg_content = 0;
257    scc_brg_state[brg_idx].link_cnt    = 0;
258  }
259#ifndef MDE360
260  /*
261   * on ZEM40, init CLK4/5 inputs
262   */
263  m8xx.papar |=  ((1 << 11) | (1 << 12));
264  m8xx.padir &= ~((1 << 11) | (1 << 12));
265#endif
266}
267
268#if CONS_USE_EXT_CLK
269/*
270 * input clock frq for CPM clock inputs
271 */
272static uint32_t clkin_frq[2][4] = {
273#ifdef MDE360
274  {0,0,0,0},
275  {0,0,0,0}
276#else
277  {0,0,0,1843000},
278  {1843000,0,0,0}
279#endif
280};
281#endif
282
283/*
284 * allocate, set and connect baud rate generators
285 * FIXME: or clock input
286 * FIXME: set pin to be clock input
287 */
288
289static bool sccBRGalloc(m8xx_console_chan_desc_t *cd,int baud)
290{
291  rtems_interrupt_level level;
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 = cd->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    cd->brg_used = new_brg;
367    /*
368     * Put SCC in NMSI mode, connect SCC to BRG or CLKx
369     */
370    if (cd->is_scc) {
371      m8xx.sicr = ((m8xx.sicr & ~(M8xx_SICR_SCCRX_MSK(cd->chan) |
372                                  M8xx_SICR_SCCTX_MSK(cd->chan))) |
373                   M8xx_SICR_SCCRX(cd->chan,new_brg)|
374                   M8xx_SICR_SCCTX(cd->chan,new_brg));
375    }
376    else {
377      /* connect SMC to BRGx or CLKx... */
378      m8xx.simode = ((m8xx.simode & ~(M8xx_SIMODE_SMCCS_MSK(cd->chan - CONS_CHN_SMC1)))|
379                     M8xx_SIMODE_SMCCS(cd->chan - CONS_CHN_SMC1,new_brg));
380    }
381
382    return true;
383  } else {
384    return false;
385  }
386}
387
388
389/*
390 * Hardware-dependent portion of tcsetattr().
391 */
392static bool
393sccSetAttributes (rtems_termios_device_context *base, const struct termios *t)
394{
395  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
396  speed_t speed;
397  rtems_termios_baud_t baud;
398
399  speed = cfgetispeed(t);
400  if (speed == B0) {
401        speed = cfgetospeed(t);
402  }
403
404  baud = rtems_termios_baud_to_number(speed);
405  if (baud == 0) {
406    return false;
407  }
408
409  return sccBRGalloc(cd,baud);
410}
411
412/*
413 * Interrupt handler
414 */
415static rtems_isr
416sccInterruptHandler (void *arg)
417{
418  rtems_termios_tty *tty = arg;
419  m8xx_console_chan_desc_t *cd = rtems_termios_get_device_context(tty);
420
421  /*
422   * Buffer received?
423   */
424  if (CHN_EVENT_GET(cd) & 0x1) {
425    /*
426     * clear SCC event flag
427     */
428    CHN_EVENT_CLR(cd,0x01);
429    /*
430     * process event
431     */
432    while ((cd->sccCurrRxBd->status & M8xx_BD_EMPTY) == 0) {
433      rtems_cache_invalidate_multiple_data_lines((void *)cd->sccCurrRxBd->buffer,
434                                                  cd->sccCurrRxBd->length);
435      rtems_termios_enqueue_raw_characters (tty,
436                                            (char *)cd->sccCurrRxBd->buffer,
437                                            cd->sccCurrRxBd->length);
438      /*
439       * clear status
440       */
441      cd->sccCurrRxBd->status =
442        (cd->sccCurrRxBd->status
443         & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
444        | M8xx_BD_EMPTY;
445      /*
446       * advance to next BD
447       */
448      if ((cd->sccCurrRxBd->status & M8xx_BD_WRAP) != 0) {
449        cd->sccCurrRxBd = cd->sccFrstRxBd;
450      }
451      else {
452        cd->sccCurrRxBd++;
453      }
454    }
455  }
456  /*
457   * Buffer transmitted?
458   */
459  if (CHN_EVENT_GET(cd) & 0x2) {
460    /*
461     * then clear interrupt event bit
462     */
463    CHN_EVENT_CLR(cd,0x2);
464    /*
465     * and signal successful transmit to termios
466     */
467    /*
468     * FIXME: multiple dequeue calls for multiple buffers
469     */
470    while((cd->sccDequTxBd != cd->sccPrepTxBd) &&
471          ((cd->sccDequTxBd->status & M8xx_BD_READY) == 0)) {
472      rtems_termios_dequeue_characters (tty, cd->sccDequTxBd->length);
473      /*
474       * advance to next BD
475       */
476      if ((cd->sccDequTxBd->status & M8xx_BD_WRAP) != 0) {
477        cd->sccDequTxBd = cd->sccFrstTxBd;
478      }
479      else {
480        cd->sccDequTxBd++;
481      }
482    }
483  }
484}
485
486static void
487mpc8xx_console_irq_on(m8xx_console_chan_desc_t *cd)
488{
489    CHN_MASK_SET(cd, 3);        /* Enable TX and RX interrupts */
490}
491
492static void
493sccInitialize (m8xx_console_chan_desc_t *cd)
494{
495  int i;
496  /*
497   * allocate buffers
498   * FIXME: use a cache-line size boundary alloc here
499   */
500  cd->rxBuf = rtems_cache_aligned_malloc(sizeof(*cd->rxBuf));
501  if (cd->rxBuf == NULL) {
502    rtems_panic("Cannot allocate console rx buffer\n");
503  }
504
505  /*
506   * Allocate buffer descriptors
507   */
508  cd->sccCurrRxBd =
509    cd->sccFrstRxBd = m8xx_bd_allocate(SCC_RXBD_CNT);
510  cd->sccPrepTxBd =
511    cd->sccDequTxBd =
512    cd->sccFrstTxBd = m8xx_bd_allocate(SCC_TXBD_CNT);
513  switch(cd->chan) {
514  case CONS_CHN_SCC1:
515    /*
516     * Configure port A pins to enable TXD1 and RXD1 pins
517     * FIXME: add setup for modem control lines....
518     */
519    m8xx.papar |=  0x03;
520    m8xx.padir &= ~0x03;
521
522    /*
523     * Configure port C pins to enable RTS1 pins (static active low)
524     */
525    m8xx.pcpar &= ~0x01;
526    m8xx.pcso  &= ~0x01;
527    m8xx.pcdir |=  0x01;
528    m8xx.pcdat &= ~0x01;
529    break;
530  case CONS_CHN_SCC2:
531    /*
532     * Configure port A pins to enable TXD2 and RXD2 pins
533     * FIXME: add setup for modem control lines....
534     */
535    m8xx.papar |=  0x0C;
536    m8xx.padir &= ~0x0C;
537
538    /*
539     * Configure port C pins to enable RTS2 pins (static active low)
540     */
541    m8xx.pcpar &= ~0x02;
542    m8xx.pcso  &= ~0x02;
543    m8xx.pcdir |=  0x02;
544    m8xx.pcdat &= ~0x02;
545    break;
546  case CONS_CHN_SCC3:
547    /*
548     * Configure port A pins to enable TXD3 and RXD3 pins
549     * FIXME: add setup for modem control lines....
550     */
551    m8xx.papar |=  0x30;
552    m8xx.padir &= ~0x30;
553
554    /*
555     * Configure port C pins to enable RTS3 (static active low)
556     */
557    m8xx.pcpar &= ~0x04;
558    m8xx.pcso  &= ~0x04;
559    m8xx.pcdir |=  0x04;
560    m8xx.pcdat &= ~0x04;
561    break;
562  case CONS_CHN_SCC4:
563    /*
564     * Configure port A pins to enable TXD4 and RXD4 pins
565     * FIXME: add setup for modem control lines....
566     */
567    m8xx.papar |=  0xC0;
568    m8xx.padir &= ~0xC0;
569
570    /*
571     * Configure port C pins to enable RTS4 pins (static active low)
572     */
573    m8xx.pcpar &= ~0x08;
574    m8xx.pcso  &= ~0x08;
575    m8xx.pcdir |=  0x08;
576    m8xx.pcdat &= ~0x08;
577    break;
578  case CONS_CHN_SMC1:
579    /*
580     * Configure port B pins to enable SMTXD1 and SMRXD1 pins
581     */
582    m8xx.pbpar |=  0xC0;
583    m8xx.pbdir &= ~0xC0;
584    break;
585  case CONS_CHN_SMC2:
586    /*
587     * Configure port B pins to enable SMTXD2 and SMRXD2 pins
588     */
589    m8xx.pbpar |=  0xC00;
590    m8xx.pbdir &= ~0xC00;
591    break;
592  }
593  /*
594   * allocate and connect BRG
595   */
596  sccBRGalloc(cd,9600);
597
598
599  /*
600   * Set up SCCx parameter RAM common to all protocols
601   */
602  CHN_PARAM_SET(cd,rbase,(char *)cd->sccFrstRxBd - (char *)&m8xx);
603  CHN_PARAM_SET(cd,tbase,(char *)cd->sccFrstTxBd - (char *)&m8xx);
604  CHN_PARAM_SET(cd,rfcr ,M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0));
605  CHN_PARAM_SET(cd,tfcr ,M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0));
606  if (cd->mode != TERMIOS_POLLED)
607    CHN_PARAM_SET(cd,mrblr,RXBUFSIZE);
608  else
609    CHN_PARAM_SET(cd,mrblr,1);
610
611  /*
612   * Set up SCCx parameter RAM UART-specific parameters
613   */
614  CHN_PARAM_SET(cd,un.uart.max_idl ,MAX_IDL_DEFAULT);
615  CHN_PARAM_SET(cd,un.uart.brkln   ,0);
616  CHN_PARAM_SET(cd,un.uart.brkec   ,0);
617  CHN_PARAM_SET(cd,un.uart.brkcr   ,0);
618  if (cd->is_scc) {
619    cd->parms.sccp->un.uart.character[0]=0x8000; /* no char filter */
620    cd->parms.sccp->un.uart.rccm=0x80FF; /* control character mask */
621  }
622
623  /*
624   * Set up the Receive Buffer Descriptors
625   */
626  for (i = 0;i < SCC_RXBD_CNT;i++) {
627    cd->sccFrstRxBd[i].status = M8xx_BD_EMPTY | M8xx_BD_INTERRUPT;
628    if (i == SCC_RXBD_CNT-1) {
629      cd->sccFrstRxBd[i].status |= M8xx_BD_WRAP;
630    }
631    cd->sccFrstRxBd[i].length = 0;
632    cd->sccFrstRxBd[i].buffer = (*cd->rxBuf)[i];
633  }
634  /*
635   * Setup the Transmit Buffer Descriptor
636   */
637  for (i = 0;i < SCC_TXBD_CNT;i++) {
638    cd->sccFrstTxBd[i].status = M8xx_BD_INTERRUPT;
639    if (i == SCC_TXBD_CNT-1) {
640      cd->sccFrstTxBd[i].status |= M8xx_BD_WRAP;
641    }
642    cd->sccFrstTxBd[i].length = 0;
643    cd->sccFrstTxBd[i].buffer = NULL;
644  }
645
646  /*
647   * Set up SCC general and protocol-specific mode registers
648   */
649  CHN_EVENT_CLR(cd,~0); /* Clear any pending events */
650  CHN_MASK_SET(cd,0);           /* Mask all interrupt/event sources */
651
652  if (cd->is_scc) {
653    cd->regs.sccr->psmr = 0xb000; /* 8N1, CTS flow control */
654    cd->regs.sccr->gsmr_h = 0x00000000;
655    cd->regs.sccr->gsmr_l = 0x00028004; /* UART mode */
656  }
657  else {
658    cd->regs.smcr->smcmr = 0x4820;
659  }
660  /*
661   * Send "Init parameters" command
662   */
663  m8xx_cp_execute_cmd(M8xx_CR_OP_INIT_RX_TX | cd->cr_chan_code);
664
665  /*
666   * Enable receiver and transmitter
667   */
668  if (cd->is_scc) {
669    cd->regs.sccr->gsmr_l |= 0x00000030;
670  }
671  else {
672    cd->regs.smcr->smcmr |= 0x0003;
673  }
674}
675
676/*
677 * polled scc read function
678 */
679static int
680sccPollRead (rtems_termios_device_context *base)
681{
682  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
683  int c = -1;
684
685  while(1) {
686    if ((cd->sccCurrRxBd->status & M8xx_BD_EMPTY) != 0) {
687      return -1;
688    }
689
690    if (0 == (cd->sccCurrRxBd->status & (M8xx_BD_OVERRUN
691                                           | M8xx_BD_PARITY_ERROR
692                                           | M8xx_BD_FRAMING_ERROR
693                                           | M8xx_BD_BREAK
694                                           | M8xx_BD_IDLE))) {
695      /* character received and no error detected */
696      rtems_cache_invalidate_multiple_data_lines((void *)cd->sccCurrRxBd->buffer,
697                                                 cd->sccCurrRxBd->length);
698      c = (unsigned)*((char *)cd->sccCurrRxBd->buffer);
699      /*
700       * clear status
701       */
702    }
703    cd->sccCurrRxBd->status =
704      (cd->sccCurrRxBd->status
705       & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
706      | M8xx_BD_EMPTY;
707    /*
708     * advance to next BD
709     */
710    if ((cd->sccCurrRxBd->status & M8xx_BD_WRAP) != 0) {
711      cd->sccCurrRxBd = cd->sccFrstRxBd;
712    }
713    else {
714      cd->sccCurrRxBd++;
715    }
716    if (c >= 0) {
717      return c;
718    }
719  }
720}
721
722
723/*
724 * Device-dependent write routine
725 * Interrupt-driven devices:
726 *      Begin transmission of as many characters as possible (minimum is 1).
727 * Polling devices:
728 *      Transmit all characters.
729 */
730static void
731sccInterruptWrite (rtems_termios_device_context *base, const char *buf, size_t len)
732{
733  if (len > 0) {
734    m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
735    if ((cd->sccPrepTxBd->status & M8xx_BD_READY) == 0) {
736      cd->sccPrepTxBd->buffer = (char *)buf;
737      cd->sccPrepTxBd->length = len;
738      rtems_cache_flush_multiple_data_lines((const void *)buf,len);
739      /*
740       * clear status, set ready bit
741       */
742      cd->sccPrepTxBd->status =
743        (cd->sccPrepTxBd->status
744         & M8xx_BD_WRAP)
745        | M8xx_BD_READY | M8xx_BD_INTERRUPT;
746      if ((cd->sccPrepTxBd->status & M8xx_BD_WRAP) != 0) {
747        cd->sccPrepTxBd = cd->sccFrstTxBd;
748      }
749      else {
750        cd->sccPrepTxBd++;
751      }
752    }
753  }
754}
755
756static void
757sccPollWrite (rtems_termios_device_context *base, const char *buf, size_t len)
758{
759  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
760  static char txBuf[CONS_CHN_CNT][SCC_TXBD_CNT];
761  int chan = cd->chan;
762  int bd_used;
763
764  while (len--) {
765    while (cd->sccPrepTxBd->status & M8xx_BD_READY)
766      continue;
767    bd_used = cd->sccPrepTxBd - cd->sccFrstTxBd;
768    txBuf[chan][bd_used] = *buf++;
769      rtems_cache_flush_multiple_data_lines((const void *)&txBuf[chan][bd_used],
770                                            sizeof(txBuf[chan][bd_used]));
771    cd->sccPrepTxBd->buffer = &(txBuf[chan][bd_used]);
772    cd->sccPrepTxBd->length = 1;
773    cd->sccPrepTxBd->status =
774      (cd->sccPrepTxBd->status
775       & M8xx_BD_WRAP)
776      | M8xx_BD_READY;
777    if ((cd->sccPrepTxBd->status & M8xx_BD_WRAP) != 0) {
778      cd->sccPrepTxBd = cd->sccFrstTxBd;
779    }
780    else {
781      cd->sccPrepTxBd++;
782    }
783  }
784}
785
786/*
787 * printk basic support
788 */
789int BSP_output_chan = CONS_CHN_NONE; /* channel used for printk operation */
790
791static void console_debug_putc_onlcr(const char c)
792{
793  rtems_interrupt_level irq_level;
794
795  if (BSP_output_chan != CONS_CHN_NONE) {
796    rtems_interrupt_disable(irq_level);
797
798    sccPollWrite (&m8xx_console_chan_desc[BSP_output_chan].base,&c,1);
799    rtems_interrupt_enable(irq_level);
800  }
801}
802
803BSP_output_char_function_type     BSP_output_char = console_debug_putc_onlcr;
804BSP_polling_getchar_function_type BSP_poll_char = NULL;
805
806
807/*
808***************
809* BOILERPLATE *
810***************
811*/
812
813struct {
814  rtems_device_minor_number minor;
815  int driver_mode;
816} channel_list[] = {
817  {CONS_CHN_SMC1,CONS_SMC1_MODE},
818  {CONS_CHN_SMC2,CONS_SMC2_MODE},
819  {CONS_CHN_SCC1,CONS_SCC1_MODE},
820  {CONS_CHN_SCC2,CONS_SCC2_MODE},
821  {CONS_CHN_SCC3,CONS_SCC3_MODE},
822  {CONS_CHN_SCC4,CONS_SCC4_MODE}
823};
824
825static bool m8xx_console_first_open(
826  rtems_termios_tty *tty,
827  rtems_termios_device_context *base,
828  struct termios *term,
829  rtems_libio_open_close_args_t *args
830)
831{
832  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
833
834  if (cd->mode == TERMIOS_IRQ_DRIVEN) {
835    rtems_status_code sc;
836
837    sc = rtems_interrupt_handler_install(
838      cd->ivec_src,
839      "SCC",
840      RTEMS_INTERRUPT_UNIQUE,
841      sccInterruptHandler,
842      tty
843    );
844    if (sc != RTEMS_SUCCESSFUL) {
845      return false;
846    }
847
848    mpc8xx_console_irq_on(cd);
849  }
850
851  return true;
852}
853
854static const rtems_termios_device_handler m8xx_console_handler_polled = {
855  .first_open = m8xx_console_first_open,
856  .set_attributes = sccSetAttributes,
857  .write = sccPollWrite,
858  .poll_read = sccPollRead,
859  .mode = TERMIOS_POLLED
860};
861
862static const rtems_termios_device_handler m8xx_console_handler_irq_driven = {
863  .first_open = m8xx_console_first_open,
864  .set_attributes = sccSetAttributes,
865  .write = sccInterruptWrite,
866  .mode = TERMIOS_IRQ_DRIVEN
867};
868
869/*
870 * Initialize and register the device
871 */
872rtems_device_driver console_initialize(rtems_device_major_number  major,
873                                       rtems_device_minor_number  minor,/* ignored */
874                                       void                      *arg
875                                       )
876{
877  rtems_status_code status = RTEMS_SUCCESSFUL;
878  int entry,ttynum;
879  char tty_name[] = "/dev/tty00";
880
881  /*
882   * Set up TERMIOS
883   */
884  rtems_termios_initialize ();
885  /*
886   * init BRG allocataion
887   */
888  sccBRGinit();
889  ttynum = 0;
890  for (entry = 0;
891       (entry < sizeof(channel_list)/sizeof(channel_list[0]))
892         && (status == RTEMS_SUCCESSFUL);
893       entry++) {
894    if (channel_list[entry].driver_mode != CONS_MODE_UNUSED) {
895      m8xx_console_chan_desc_t *cd =
896        &m8xx_console_chan_desc[channel_list[entry].minor];
897      /*
898       * Do device-specific initialization
899       */
900      cd->mode = channel_list[entry].driver_mode;
901      sccInitialize (cd);
902
903      /*
904       * build device name
905       */
906      tty_name[sizeof(tty_name)-2] = '0'+ttynum;
907      ttynum++;
908      /*
909       * Register the device
910       */
911      status = rtems_termios_device_install(
912        tty_name,
913        cd->mode == TERMIOS_IRQ_DRIVEN ?
914          &m8xx_console_handler_irq_driven : &m8xx_console_handler_polled,
915        NULL,
916        &cd->base
917      );
918      if (status != RTEMS_SUCCESSFUL) {
919        rtems_fatal_error_occurred (status);
920      }
921
922      if (cd->chan == CONSOLE_CHN) {
923        int rv;
924
925        rv = link(tty_name, CONSOLE_DEVICE_NAME);
926        if (rv != 0) {
927          rtems_fatal_error_occurred (RTEMS_IO_ERROR);
928        }
929      }
930    }
931  }
932
933  /*
934   * enable printk support
935   */
936  BSP_output_chan = PRINTK_CHN;
937
938  return RTEMS_SUCCESSFUL;
939}
Note: See TracBrowser for help on using the repository browser.