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

Last change on this file was 688e101a, checked in by Sebastian Huber <sebastian.huber@…>, on Sep 14, 2018 at 5:30:46 AM

bsp/tqm8xx: Fix polled vs. interrupt output

Update #3513.

  • Property mode set to 100644
File size: 23.2 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 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  uint16_t status;
421  uint16_t length;
422  void *buffer;
423
424  /*
425   * Buffer received?
426   */
427  if (CHN_EVENT_GET(cd) & 0x1) {
428    /*
429     * clear SCC event flag
430     */
431    CHN_EVENT_CLR(cd,0x01);
432    /*
433     * process event
434     */
435    while (true) {
436      status = cd->sccCurrRxBd->status;
437
438      if ((cd->sccCurrRxBd->status & M8xx_BD_EMPTY) != 0) {
439        break;
440      }
441
442      buffer = cd->sccCurrRxBd->buffer;
443      length = cd->sccCurrRxBd->length;
444      rtems_cache_invalidate_multiple_data_lines(buffer, length);
445      rtems_termios_enqueue_raw_characters (tty, buffer, length);
446
447      /*
448       * clear status
449       */
450      cd->sccCurrRxBd->status = (status & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
451        | M8xx_BD_EMPTY;
452      /*
453       * advance to next BD
454       */
455      if ((status & M8xx_BD_WRAP) != 0) {
456        cd->sccCurrRxBd = cd->sccFrstRxBd;
457      } else {
458        cd->sccCurrRxBd++;
459      }
460    }
461  }
462  /*
463   * Buffer transmitted?
464   */
465  if (CHN_EVENT_GET(cd) & 0x2) {
466    /*
467     * then clear interrupt event bit
468     */
469    CHN_EVENT_CLR(cd,0x2);
470    /*
471     * and signal successful transmit to termios
472     */
473    /*
474     * FIXME: multiple dequeue calls for multiple buffers
475     */
476    while (cd->sccDequTxBd != cd->sccPrepTxBd) {
477      status = cd->sccDequTxBd->status;
478
479      if ((status & M8xx_BD_READY) != 0) {
480        break;
481      }
482
483      if ((status & M8xx_BD_INTERRUPT) != 0) {
484        rtems_termios_dequeue_characters (tty, cd->sccDequTxBd->length);
485      }
486
487      /*
488       * advance to next BD
489       */
490      if ((status & M8xx_BD_WRAP) != 0) {
491        cd->sccDequTxBd = cd->sccFrstTxBd;
492      } else {
493        cd->sccDequTxBd++;
494      }
495    }
496  }
497}
498
499static void
500mpc8xx_console_irq_on(m8xx_console_chan_desc_t *cd)
501{
502    CHN_MASK_SET(cd, 3);        /* Enable TX and RX interrupts */
503}
504
505static void
506sccInitialize (m8xx_console_chan_desc_t *cd)
507{
508  int i;
509  /*
510   * allocate buffers
511   * FIXME: use a cache-line size boundary alloc here
512   */
513  cd->rxBuf = rtems_cache_aligned_malloc(sizeof(*cd->rxBuf));
514  if (cd->rxBuf == NULL) {
515    rtems_panic("Cannot allocate console rx buffer\n");
516  }
517
518  /*
519   * Allocate buffer descriptors
520   */
521  cd->sccCurrRxBd =
522    cd->sccFrstRxBd = m8xx_bd_allocate(SCC_RXBD_CNT);
523  cd->sccPrepTxBd =
524    cd->sccDequTxBd =
525    cd->sccFrstTxBd = m8xx_bd_allocate(SCC_TXBD_CNT);
526  switch(cd->chan) {
527  case CONS_CHN_SCC1:
528    /*
529     * Configure port A pins to enable TXD1 and RXD1 pins
530     * FIXME: add setup for modem control lines....
531     */
532    m8xx.papar |=  0x03;
533    m8xx.padir &= ~0x03;
534
535    /*
536     * Configure port C pins to enable RTS1 pins (static active low)
537     */
538    m8xx.pcpar &= ~0x01;
539    m8xx.pcso  &= ~0x01;
540    m8xx.pcdir |=  0x01;
541    m8xx.pcdat &= ~0x01;
542    break;
543  case CONS_CHN_SCC2:
544    /*
545     * Configure port A pins to enable TXD2 and RXD2 pins
546     * FIXME: add setup for modem control lines....
547     */
548    m8xx.papar |=  0x0C;
549    m8xx.padir &= ~0x0C;
550
551    /*
552     * Configure port C pins to enable RTS2 pins (static active low)
553     */
554    m8xx.pcpar &= ~0x02;
555    m8xx.pcso  &= ~0x02;
556    m8xx.pcdir |=  0x02;
557    m8xx.pcdat &= ~0x02;
558    break;
559  case CONS_CHN_SCC3:
560    /*
561     * Configure port A pins to enable TXD3 and RXD3 pins
562     * FIXME: add setup for modem control lines....
563     */
564    m8xx.papar |=  0x30;
565    m8xx.padir &= ~0x30;
566
567    /*
568     * Configure port C pins to enable RTS3 (static active low)
569     */
570    m8xx.pcpar &= ~0x04;
571    m8xx.pcso  &= ~0x04;
572    m8xx.pcdir |=  0x04;
573    m8xx.pcdat &= ~0x04;
574    break;
575  case CONS_CHN_SCC4:
576    /*
577     * Configure port A pins to enable TXD4 and RXD4 pins
578     * FIXME: add setup for modem control lines....
579     */
580    m8xx.papar |=  0xC0;
581    m8xx.padir &= ~0xC0;
582
583    /*
584     * Configure port C pins to enable RTS4 pins (static active low)
585     */
586    m8xx.pcpar &= ~0x08;
587    m8xx.pcso  &= ~0x08;
588    m8xx.pcdir |=  0x08;
589    m8xx.pcdat &= ~0x08;
590    break;
591  case CONS_CHN_SMC1:
592    /*
593     * Configure port B pins to enable SMTXD1 and SMRXD1 pins
594     */
595    m8xx.pbpar |=  0xC0;
596    m8xx.pbdir &= ~0xC0;
597    break;
598  case CONS_CHN_SMC2:
599    /*
600     * Configure port B pins to enable SMTXD2 and SMRXD2 pins
601     */
602    m8xx.pbpar |=  0xC00;
603    m8xx.pbdir &= ~0xC00;
604    break;
605  }
606  /*
607   * allocate and connect BRG
608   */
609  sccBRGalloc(cd,9600);
610
611
612  /*
613   * Set up SCCx parameter RAM common to all protocols
614   */
615  CHN_PARAM_SET(cd,rbase,(char *)cd->sccFrstRxBd - (char *)&m8xx);
616  CHN_PARAM_SET(cd,tbase,(char *)cd->sccFrstTxBd - (char *)&m8xx);
617  CHN_PARAM_SET(cd,rfcr ,M8xx_RFCR_MOT | M8xx_RFCR_DMA_SPACE(0));
618  CHN_PARAM_SET(cd,tfcr ,M8xx_TFCR_MOT | M8xx_TFCR_DMA_SPACE(0));
619  if (cd->mode != TERMIOS_POLLED)
620    CHN_PARAM_SET(cd,mrblr,RXBUFSIZE);
621  else
622    CHN_PARAM_SET(cd,mrblr,1);
623
624  /*
625   * Set up SCCx parameter RAM UART-specific parameters
626   */
627  CHN_PARAM_SET(cd,un.uart.max_idl ,MAX_IDL_DEFAULT);
628  CHN_PARAM_SET(cd,un.uart.brkln   ,0);
629  CHN_PARAM_SET(cd,un.uart.brkec   ,0);
630  CHN_PARAM_SET(cd,un.uart.brkcr   ,0);
631  if (cd->is_scc) {
632    cd->parms.sccp->un.uart.character[0]=0x8000; /* no char filter */
633    cd->parms.sccp->un.uart.rccm=0x80FF; /* control character mask */
634  }
635
636  /*
637   * Set up the Receive Buffer Descriptors
638   */
639  for (i = 0;i < SCC_RXBD_CNT;i++) {
640    cd->sccFrstRxBd[i].status = M8xx_BD_EMPTY | M8xx_BD_INTERRUPT;
641    if (i == SCC_RXBD_CNT-1) {
642      cd->sccFrstRxBd[i].status |= M8xx_BD_WRAP;
643    }
644    cd->sccFrstRxBd[i].length = 0;
645    cd->sccFrstRxBd[i].buffer = (*cd->rxBuf)[i];
646  }
647  /*
648   * Setup the Transmit Buffer Descriptor
649   */
650  for (i = 0;i < SCC_TXBD_CNT;i++) {
651    cd->sccFrstTxBd[i].status = M8xx_BD_INTERRUPT;
652    if (i == SCC_TXBD_CNT-1) {
653      cd->sccFrstTxBd[i].status |= M8xx_BD_WRAP;
654    }
655    cd->sccFrstTxBd[i].length = 0;
656    cd->sccFrstTxBd[i].buffer = NULL;
657  }
658
659  /*
660   * Set up SCC general and protocol-specific mode registers
661   */
662  CHN_EVENT_CLR(cd,~0); /* Clear any pending events */
663  CHN_MASK_SET(cd,0);           /* Mask all interrupt/event sources */
664
665  if (cd->is_scc) {
666    cd->regs.sccr->psmr = 0xb000; /* 8N1, CTS flow control */
667    cd->regs.sccr->gsmr_h = 0x00000000;
668    cd->regs.sccr->gsmr_l = 0x00028004; /* UART mode */
669  }
670  else {
671    cd->regs.smcr->smcmr = 0x4820;
672  }
673  /*
674   * Send "Init parameters" command
675   */
676  m8xx_cp_execute_cmd(M8xx_CR_OP_INIT_RX_TX | cd->cr_chan_code);
677
678  /*
679   * Enable receiver and transmitter
680   */
681  if (cd->is_scc) {
682    cd->regs.sccr->gsmr_l |= 0x00000030;
683  }
684  else {
685    cd->regs.smcr->smcmr |= 0x0003;
686  }
687}
688
689/*
690 * polled scc read function
691 */
692static int
693sccPollRead (rtems_termios_device_context *base)
694{
695  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
696  int c = -1;
697
698  while(1) {
699    if ((cd->sccCurrRxBd->status & M8xx_BD_EMPTY) != 0) {
700      return -1;
701    }
702
703    if (0 == (cd->sccCurrRxBd->status & (M8xx_BD_OVERRUN
704                                           | M8xx_BD_PARITY_ERROR
705                                           | M8xx_BD_FRAMING_ERROR
706                                           | M8xx_BD_BREAK
707                                           | M8xx_BD_IDLE))) {
708      /* character received and no error detected */
709      rtems_cache_invalidate_multiple_data_lines((void *)cd->sccCurrRxBd->buffer,
710                                                 cd->sccCurrRxBd->length);
711      c = (unsigned)*((char *)cd->sccCurrRxBd->buffer);
712      /*
713       * clear status
714       */
715    }
716    cd->sccCurrRxBd->status =
717      (cd->sccCurrRxBd->status
718       & (M8xx_BD_WRAP | M8xx_BD_INTERRUPT))
719      | M8xx_BD_EMPTY;
720    /*
721     * advance to next BD
722     */
723    if ((cd->sccCurrRxBd->status & M8xx_BD_WRAP) != 0) {
724      cd->sccCurrRxBd = cd->sccFrstRxBd;
725    }
726    else {
727      cd->sccCurrRxBd++;
728    }
729    if (c >= 0) {
730      return c;
731    }
732  }
733}
734
735
736/*
737 * Device-dependent write routine
738 * Interrupt-driven devices:
739 *      Begin transmission of as many characters as possible (minimum is 1).
740 * Polling devices:
741 *      Transmit all characters.
742 */
743static void
744sccInterruptWrite (rtems_termios_device_context *base, const char *buf, size_t len)
745{
746  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
747
748  if (len > 0) {
749    uint16_t status = cd->sccPrepTxBd->status;
750
751    if ((status & M8xx_BD_READY) == 0) {
752      cd->sccPrepTxBd->buffer = RTEMS_DECONST(char*, buf);
753      cd->sccPrepTxBd->length = len;
754      rtems_cache_flush_multiple_data_lines(buf, len);
755
756      /*
757       * clear status, set ready bit
758       */
759      cd->sccPrepTxBd->status = (status & M8xx_BD_WRAP)
760        | M8xx_BD_READY | M8xx_BD_INTERRUPT;
761
762      if ((status & M8xx_BD_WRAP) != 0) {
763        cd->sccPrepTxBd = cd->sccFrstTxBd;
764      } else {
765        cd->sccPrepTxBd++;
766      }
767    }
768  }
769}
770
771static void
772sccPollWrite (rtems_termios_device_context *base, const char *buf, size_t len)
773{
774  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
775  volatile m8xxBufferDescriptor_t *bd = NULL;
776
777  rtems_cache_flush_multiple_data_lines (buf, len);
778
779  while (bd == NULL) {
780    rtems_interrupt_level level;
781    uint16_t status;
782
783    rtems_interrupt_disable(level);
784
785    bd = cd->sccPrepTxBd;
786    status = bd->status;
787
788    if ((status & M8xx_BD_READY) == 0) {
789      bd->buffer = RTEMS_DECONST (char *, buf);
790      bd->length = len;
791      bd->status = (status & M8xx_BD_WRAP) | M8xx_BD_READY;
792
793      if ((status & M8xx_BD_WRAP) != 0) {
794        cd->sccPrepTxBd = cd->sccFrstTxBd;
795      } else {
796        cd->sccPrepTxBd++;
797      }
798    } else {
799      bd = NULL;
800    }
801
802    rtems_interrupt_enable(level);
803  }
804
805  while ((bd->status & M8xx_BD_READY) != 0) {
806    /* Wait */
807  }
808}
809
810/*
811 * printk basic support
812 */
813int BSP_output_chan = CONS_CHN_NONE; /* channel used for printk operation */
814
815static void console_debug_putc_onlcr(const char c)
816{
817  rtems_interrupt_level irq_level;
818
819  if (BSP_output_chan != CONS_CHN_NONE) {
820    rtems_interrupt_disable(irq_level);
821
822    sccPollWrite (&m8xx_console_chan_desc[BSP_output_chan].base,&c,1);
823    rtems_interrupt_enable(irq_level);
824  }
825}
826
827BSP_output_char_function_type     BSP_output_char = console_debug_putc_onlcr;
828BSP_polling_getchar_function_type BSP_poll_char = NULL;
829
830
831/*
832***************
833* BOILERPLATE *
834***************
835*/
836
837struct {
838  rtems_device_minor_number minor;
839  int driver_mode;
840} channel_list[] = {
841  {CONS_CHN_SMC1,CONS_SMC1_MODE},
842  {CONS_CHN_SMC2,CONS_SMC2_MODE},
843  {CONS_CHN_SCC1,CONS_SCC1_MODE},
844  {CONS_CHN_SCC2,CONS_SCC2_MODE},
845  {CONS_CHN_SCC3,CONS_SCC3_MODE},
846  {CONS_CHN_SCC4,CONS_SCC4_MODE}
847};
848
849static bool m8xx_console_first_open(
850  rtems_termios_tty *tty,
851  rtems_termios_device_context *base,
852  struct termios *term,
853  rtems_libio_open_close_args_t *args
854)
855{
856  m8xx_console_chan_desc_t *cd = (m8xx_console_chan_desc_t *)base;
857
858  if (cd->mode == TERMIOS_IRQ_DRIVEN) {
859    rtems_status_code sc;
860
861    sc = rtems_interrupt_handler_install(
862      cd->ivec_src,
863      "SCC",
864      RTEMS_INTERRUPT_UNIQUE,
865      sccInterruptHandler,
866      tty
867    );
868    if (sc != RTEMS_SUCCESSFUL) {
869      return false;
870    }
871
872    mpc8xx_console_irq_on(cd);
873  }
874
875  return true;
876}
877
878static const rtems_termios_device_handler m8xx_console_handler_polled = {
879  .first_open = m8xx_console_first_open,
880  .set_attributes = sccSetAttributes,
881  .write = sccPollWrite,
882  .poll_read = sccPollRead,
883  .mode = TERMIOS_POLLED
884};
885
886static const rtems_termios_device_handler m8xx_console_handler_irq_driven = {
887  .first_open = m8xx_console_first_open,
888  .set_attributes = sccSetAttributes,
889  .write = sccInterruptWrite,
890  .mode = TERMIOS_IRQ_DRIVEN
891};
892
893/*
894 * Initialize and register the device
895 */
896rtems_device_driver console_initialize(rtems_device_major_number  major,
897                                       rtems_device_minor_number  minor,/* ignored */
898                                       void                      *arg
899                                       )
900{
901  rtems_status_code status = RTEMS_SUCCESSFUL;
902  int entry,ttynum;
903  char tty_name[] = "/dev/tty00";
904
905  /*
906   * Set up TERMIOS
907   */
908  rtems_termios_initialize ();
909  /*
910   * init BRG allocataion
911   */
912  sccBRGinit();
913  ttynum = 0;
914  for (entry = 0;
915       (entry < sizeof(channel_list)/sizeof(channel_list[0]))
916         && (status == RTEMS_SUCCESSFUL);
917       entry++) {
918    if (channel_list[entry].driver_mode != CONS_MODE_UNUSED) {
919      m8xx_console_chan_desc_t *cd =
920        &m8xx_console_chan_desc[channel_list[entry].minor];
921      /*
922       * Do device-specific initialization
923       */
924      cd->mode = channel_list[entry].driver_mode;
925      sccInitialize (cd);
926
927      /*
928       * build device name
929       */
930      tty_name[sizeof(tty_name)-2] = '0'+ttynum;
931      ttynum++;
932      /*
933       * Register the device
934       */
935      status = rtems_termios_device_install(
936        tty_name,
937        cd->mode == TERMIOS_IRQ_DRIVEN ?
938          &m8xx_console_handler_irq_driven : &m8xx_console_handler_polled,
939        NULL,
940        &cd->base
941      );
942      if (status != RTEMS_SUCCESSFUL) {
943        rtems_fatal_error_occurred (status);
944      }
945
946      if (cd->chan == CONSOLE_CHN) {
947        int rv;
948
949        rv = link(tty_name, CONSOLE_DEVICE_NAME);
950        if (rv != 0) {
951          rtems_fatal_error_occurred (RTEMS_IO_ERROR);
952        }
953      }
954    }
955  }
956
957  /*
958   * enable printk support
959   */
960  BSP_output_chan = PRINTK_CHN;
961
962  return RTEMS_SUCCESSFUL;
963}
Note: See TracBrowser for help on using the repository browser.