source: rtems/c/src/lib/libcpu/powerpc/mpc83xx/network/tsec.c @ 359e537

4.104.115
Last change on this file since 359e537 was 359e537, checked in by Ralf Corsepius <ralf.corsepius@…>, on 11/30/09 at 05:09:41

Whitespace removal.

  • Property mode set to 100644
File size: 70.7 KB
Line 
1/*===============================================================*\
2| Project: RTEMS support for MPC83xx                              |
3+-----------------------------------------------------------------+
4|                    Copyright (c) 2007                           |
5|                    Embedded Brains GmbH                         |
6|                    Obere Lagerstr. 30                           |
7|                    D-82178 Puchheim                             |
8|                    Germany                                      |
9|                    rtems@embedded-brains.de                     |
10+-----------------------------------------------------------------+
11| The license and distribution terms for this file may be         |
12| found in the file LICENSE in this distribution or at            |
13|                                                                 |
14| http://www.rtems.com/license/LICENSE.                           |
15|                                                                 |
16+-----------------------------------------------------------------+
17| this file contains the MPC83xx TSEC networking driver           |
18\*===============================================================*/
19/*
20 * this driver has the following HW assumptions:
21 * - PHY for TSEC1 uses address 0
22 * - PHY for TSEC2 uses address 1
23 * - PHY uses GMII for 1000Base-T and MII for the rest of the modes
24 */
25#include <stdlib.h>
26#include <bsp.h>
27#include <bsp/irq.h>
28#include <mpc83xx/mpc83xx.h>
29#include <mpc83xx/tsec.h>
30#include <libcpu/spr.h>
31#include <rtems/error.h>
32#include <rtems/bspIo.h>
33#include <rtems/rtems_bsdnet.h>
34#include <rtems/rtems_mii_ioctl.h>
35#include <errno.h>
36
37#include <sys/param.h>
38#include <sys/socket.h>
39#include <sys/sockio.h>
40#include <sys/mbuf.h>
41#include <net/if.h>
42#include <net/if_arp.h>
43#include <netinet/in.h>
44#include <netinet/if_ether.h>
45#include <stdio.h>
46
47/* System Version Register */
48#define SVR 286
49SPR_RO( SVR)
50
51/* Processor Version Register */
52SPR_RO( PVR)
53
54#define CLREVENT_IN_IRQ
55
56#define TSEC_WATCHDOG_TIMEOUT 5 /* check media every 5 seconds */
57
58/*
59 * Device data
60 */
61struct mpc83xx_tsec_struct {
62  struct arpcom           arpcom;
63  int                     acceptBroadcast;
64
65  /*
66   * HW links: (filled from rtems_bsdnet_ifconfig
67   */
68  m83xxTSEC_Registers_t  *reg_ptr;    /* pointer to TSEC register block */
69  m83xxTSEC_Registers_t  *mdio_ptr;   /* pointer to TSEC register block which is responsible for MDIO communication */
70  int                    irq_num_tx;  /* tx irq number                  */
71  int                    irq_num_rx;  /* rx irq number                  */
72  int                    irq_num_err; /* error irq number               */
73
74  /*
75   * BD management
76   */
77  int                     rxBdCount;
78  int                     txBdCount;
79  PQBufferDescriptor_t    *Rx_Frst_BD;
80  PQBufferDescriptor_t    *Rx_Last_BD;
81  PQBufferDescriptor_t    *Rx_NxtUsed_BD; /* First BD, which is in Use */
82  PQBufferDescriptor_t    *Rx_NxtFill_BD; /* BD to be filled next      */
83  struct mbuf             **Rx_mBuf_Ptr;  /* Storage for mbufs         */
84
85  PQBufferDescriptor_t    *Tx_Frst_BD;
86  PQBufferDescriptor_t    *Tx_Last_BD;
87  PQBufferDescriptor_t    *Tx_NxtUsed_BD; /* First BD, which is in Use */
88  PQBufferDescriptor_t    *Tx_NxtFill_BD; /* BD to be filled next      */
89  struct mbuf             **Tx_mBuf_Ptr;  /* Storage for mbufs         */
90  /*
91   * Daemon IDs
92   */
93  rtems_id                rxDaemonTid;
94  rtems_id                txDaemonTid;
95
96  /*
97   * MDIO/Phy info
98   */
99  struct rtems_mdio_info mdio_info;
100  int phy_default;
101  int media_state; /* (last detected) state of media */
102  /*
103   * statistic counters Rx
104   */
105  unsigned long           rxInterrupts;
106  unsigned long           rxErrors;
107  /*
108   * statistic counters Tx
109   */
110  unsigned long           txInterrupts;
111  unsigned long           txErrors;
112  };
113
114static struct mpc83xx_tsec_struct tsec_driver[M83xx_TSEC_NIFACES];
115
116/*
117 * default numbers for buffers
118 */
119#define RX_BUF_COUNT 64
120#define TX_BUF_COUNT 64
121
122/*
123 * mask for all Tx interrupts
124 */
125#define M83xx_IEVENT_TXALL (M83xx_TSEC_IEVENT_GTSC      \
126                            | M83xx_TSEC_IEVENT_TXC     \
127                            /*| M83xx_TSEC_IEVENT_TXB*/ \
128                            | M83xx_TSEC_IEVENT_TXF )
129
130/*
131 * mask for all Rx interrupts
132 */
133#define M83xx_IEVENT_RXALL (M83xx_TSEC_IEVENT_RXC       \
134                            /* | M83xx_TSEC_IEVENT_RXB */       \
135                            | M83xx_TSEC_IEVENT_GRSC    \
136                            | M83xx_TSEC_IEVENT_RXF  )
137
138/*
139 * mask for all Error interrupts
140 */
141#define M83xx_IEVENT_ERRALL (M83xx_TSEC_IEVENT_BABR             \
142                            | M83xx_TSEC_IEVENT_BSY             \
143                            | M83xx_TSEC_IEVENT_EBERR           \
144                            | M83xx_TSEC_IEVENT_MSRO            \
145                            | M83xx_TSEC_IEVENT_BABT            \
146                            | M83xx_TSEC_IEVENT_TXE             \
147                            | M83xx_TSEC_IEVENT_LC              \
148                            | M83xx_TSEC_IEVENT_CRL_XDA         \
149                            | M83xx_TSEC_IEVENT_XFUN   )
150
151#define M83xx_TSEC_IMASK_SET(reg,mask,val) {    \
152  rtems_interrupt_level level;                  \
153                                                \
154  rtems_interrupt_disable(level);               \
155  (reg) = (((reg) & ~(mask)) |                  \
156           ((val) &  (mask)));                  \
157  rtems_interrupt_enable(level);                \
158}
159
160#define M83xx_TSEC_ALIGN_BUFFER(buf,align)              \
161  ((void *)( (((uint32_t)(buf))+(align)-1)              \
162             -(((uint32_t)(buf))+(align)-1)%align))
163
164/*
165 * RTEMS event used by interrupt handler to signal daemons.
166 * This must *not* be the same event used by the TCP/IP task synchronization.
167 */
168#define INTERRUPT_EVENT RTEMS_EVENT_1
169#define FATAL_INT_EVENT RTEMS_EVENT_3
170
171/*
172 * RTEMS event used to start transmit daemon.
173 * This must not be the same as INTERRUPT_EVENT.
174 */
175#define START_TRANSMIT_EVENT RTEMS_EVENT_2
176
177static int mpc83xx_tsec_ioctl
178(
179 struct ifnet *ifp,                    /* interface information            */
180 ioctl_command_t command,              /* ioctl command code               */
181 caddr_t data                          /* optional data                    */
182 );
183
184/*=========================================================================*\
185| Function:                                                                 |
186\*-------------------------------------------------------------------------*/
187static void mpc83xx_tsec_hwinit
188(
189/*-------------------------------------------------------------------------*\
190| Purpose:                                                                  |
191|   initialize hardware register                                            |
192+---------------------------------------------------------------------------+
193| Input Parameters:                                                         |
194\*-------------------------------------------------------------------------*/
195 struct mpc83xx_tsec_struct *sc        /* control structure                */
196)
197/*-------------------------------------------------------------------------*\
198| Return Value:                                                             |
199|    <none>                                                                 |
200\*=========================================================================*/
201{
202  m83xxTSEC_Registers_t  *reg_ptr = sc->reg_ptr; /* pointer to TSEC registers*/
203  uint8_t *mac_addr;
204  size_t i;
205
206  /* Clear interrupt mask and all pending events */
207  reg_ptr->imask = 0;
208  reg_ptr->ievent = 0xffffffff;
209
210  /*
211   * init ECNTL register
212   * - clear statistics counters
213   * - enable statistics
214   * NOTE: do not clear bits set in BSP init function
215   */
216  reg_ptr->ecntrl = ((reg_ptr->ecntrl & ~M83xx_TSEC_ECNTRL_AUTOZ)
217                     | M83xx_TSEC_ECNTRL_CLRCNT
218                     | M83xx_TSEC_ECNTRL_STEN
219                     | M83xx_TSEC_ECNTRL_R100M);
220
221  /*
222   * init DMA control register:
223   * - enable snooping
224   * - write BD status before interrupt request
225   * - do not poll TxBD, but wait for TSTAT[THLT] to be written
226   */
227  reg_ptr->dmactrl = (M83xx_TSEC_DMACTL_TDSEN
228                      | M83xx_TSEC_DMACTL_TBDSEN
229                      | M83xx_TSEC_DMACTL_WWR
230                      | M83xx_TSEC_DMACTL_WOP);
231
232  /*
233   * init Attribute register:
234   * - enable read snooping for data and BD
235   */
236  reg_ptr->attr = (M83xx_TSEC_ATTR_RDSEN
237                   | M83xx_TSEC_ATTR_RBDSEN);
238
239
240  reg_ptr->mrblr  = MCLBYTES-64; /* take care of buffer size lost
241                                  * due to alignment              */
242
243  /*
244   * init EDIS register: disable all error reportings
245   */
246  reg_ptr->edis = (M83xx_TSEC_EDIS_BSYDIS    |
247                   M83xx_TSEC_EDIS_EBERRDIS  |
248                   M83xx_TSEC_EDIS_TXEDIS    |
249                   M83xx_TSEC_EDIS_LCDIS     |
250                   M83xx_TSEC_EDIS_CRLXDADIS |
251                   M83xx_TSEC_EDIS_FUNDIS);
252  /*
253   * init minimum frame length register
254   */
255  reg_ptr->minflr = 64;
256  /*
257   * init maximum frame length register
258   */
259  reg_ptr->maxfrm = 1536;
260  /*
261   * define physical address of TBI
262   */
263  reg_ptr->tbipa = 0x1e;
264  /*
265   * init transmit interrupt coalescing register
266   */
267  reg_ptr->txic = (M83xx_TSEC_TXIC_ICEN
268                   | M83xx_TSEC_TXIC_ICFCT(2)
269                   | M83xx_TSEC_TXIC_ICTT(32));
270  /*
271   * init receive interrupt coalescing register
272   */
273#if 0
274  reg_ptr->rxic = (M83xx_TSEC_RXIC_ICEN
275                   | M83xx_TSEC_RXIC_ICFCT(2)
276                   | M83xx_TSEC_RXIC_ICTT(32));
277#else
278  reg_ptr->rxic = 0;
279#endif
280  /*
281   * init MACCFG1 register
282   */
283  reg_ptr->maccfg1 = (M83xx_TSEC_MACCFG1_RX_FLOW
284                      | M83xx_TSEC_MACCFG1_TX_FLOW);
285
286  /*
287   * init MACCFG2 register
288   */
289  reg_ptr->maccfg2 = ((reg_ptr->maccfg2 & M83xx_TSEC_MACCFG2_IFMODE_MSK)
290                      | M83xx_TSEC_MACCFG2_PRELEN( 7)
291                      | M83xx_TSEC_MACCFG2_FULLDUPLEX);
292
293  /*
294   * init station address register
295   */
296  mac_addr = sc->arpcom.ac_enaddr;
297
298  reg_ptr->macstnaddr[0] = ((mac_addr[5] << 24)
299                            | (mac_addr[4] << 16)
300                            | (mac_addr[3] <<  8)
301                            | (mac_addr[2] <<  0));
302  reg_ptr->macstnaddr[1] = ((mac_addr[1] << 24)
303                            | (mac_addr[0] << 16));
304  /*
305   * clear hash filters
306   */
307  for (i = 0;
308       i < sizeof(reg_ptr->iaddr)/sizeof(reg_ptr->iaddr[0]);
309       i++) {
310    reg_ptr->iaddr[i] = 0;
311  }
312  for (i = 0;
313       i < sizeof(reg_ptr->gaddr)/sizeof(reg_ptr->gaddr[0]);
314       i++) {
315    reg_ptr->gaddr[i] = 0;
316  }
317}
318
319/***************************************************************************\
320|  MII Management access functions                                          |
321\***************************************************************************/
322
323/*=========================================================================*\
324| Function:                                                                 |
325\*-------------------------------------------------------------------------*/
326static void mpc83xx_tsec_mdio_init
327(
328/*-------------------------------------------------------------------------*\
329| Purpose:                                                                  |
330|   initialize the MIIM interface                                           |
331+---------------------------------------------------------------------------+
332| Input Parameters:                                                         |
333\*-------------------------------------------------------------------------*/
334 struct mpc83xx_tsec_struct *sc        /* control structure                */
335)
336/*-------------------------------------------------------------------------*\
337| Return Value:                                                             |
338|    <none>                                                                 |
339\*=========================================================================*/
340{
341
342  /* Set TSEC registers for MDIO communication */
343
344  /*
345   * FIXME: Not clear if this works for all boards.
346   * Tested only on MPC8313ERDB.
347   */
348  sc->mdio_ptr = &mpc83xx.tsec [0];
349
350  /*
351   * set clock divider
352   */
353  sc->mdio_ptr->miimcfg = 3;
354}
355
356/*=========================================================================*\
357| Function:                                                                 |
358\*-------------------------------------------------------------------------*/
359int mpc83xx_tsec_mdio_read
360(
361/*-------------------------------------------------------------------------*\
362| Purpose:                                                                  |
363|   read register of a phy                                                  |
364+---------------------------------------------------------------------------+
365| Input Parameters:                                                         |
366\*-------------------------------------------------------------------------*/
367 int phy,                              /* PHY number to access or -1       */
368 void *uarg,                           /* unit argument                    */
369 unsigned reg,                         /* register address                 */
370 uint32_t *pval                        /* ptr to read buffer               */
371 )
372/*-------------------------------------------------------------------------*\
373| Return Value:                                                             |
374|    0, if ok, else error                                                   |
375\*=========================================================================*/
376{
377  struct mpc83xx_tsec_struct *sc = uarg;/* control structure                */
378
379  /* pointer to TSEC registers */
380  m83xxTSEC_Registers_t *reg_ptr = sc->mdio_ptr;
381
382  /*
383   * make sure we work with a valid phy
384   */
385  if (phy == -1) {
386    /*
387     * set default phy number: 0 for TSEC1, 1 for TSEC2
388     */
389    phy = sc->phy_default;
390  }
391  if ( (phy < 0) || (phy > 31)) {
392    /*
393     * invalid phy number
394     */
395    return EINVAL;
396  }
397  /*
398   * set PHY/reg address
399   */
400  reg_ptr->miimadd = (M83xx_TSEC_MIIMADD_PHY(phy)
401                      | M83xx_TSEC_MIIMADD_REGADDR(reg));
402  /*
403   * start read cycle
404   */
405  reg_ptr->miimcom = 0;
406  reg_ptr->miimcom = M83xx_TSEC_MIIMCOM_READ;
407
408  /*
409   * wait for cycle to terminate
410   */
411  do {
412    rtems_task_wake_after(2);
413  }  while (0 != (reg_ptr->miimind & M83xx_TSEC_MIIMIND_BUSY));
414  reg_ptr->miimcom = 0;
415  /*
416   * fetch read data, if available
417   */
418  if (pval != NULL) {
419    *pval = reg_ptr->miimstat;
420  }
421  return 0;
422}
423
424/*=========================================================================*\
425| Function:                                                                 |
426\*-------------------------------------------------------------------------*/
427int mpc83xx_tsec_mdio_write
428(
429/*-------------------------------------------------------------------------*\
430| Purpose:                                                                  |
431|   write register of a phy                                                 |
432+---------------------------------------------------------------------------+
433| Input Parameters:                                                         |
434\*-------------------------------------------------------------------------*/
435 int phy,                              /* PHY number to access or -1       */
436 void *uarg,                           /* unit argument                    */
437 unsigned reg,                         /* register address                 */
438 uint32_t val                          /* write value                      */
439 )
440/*-------------------------------------------------------------------------*\
441| Return Value:                                                             |
442|    0, if ok, else error                                                   |
443\*=========================================================================*/
444{
445  struct mpc83xx_tsec_struct *sc = uarg;/* control structure                */
446
447  /* pointer to TSEC registers */
448  m83xxTSEC_Registers_t *reg_ptr = sc->mdio_ptr;
449
450  /*
451   * make sure we work with a valid phy
452   */
453  if (phy == -1) {
454    /*
455     * set default phy number: 0 for TSEC1, 1 for TSEC2
456     */
457    phy = sc->phy_default;
458  }
459  if ( (phy < 0) || (phy > 31)) {
460    /*
461     * invalid phy number
462     */
463    return EINVAL;
464  }
465  /*
466   * set PHY/reg address
467   */
468  reg_ptr->miimadd = (M83xx_TSEC_MIIMADD_PHY(phy)
469                      | M83xx_TSEC_MIIMADD_REGADDR(reg));
470  /*
471   * start write cycle
472   */
473  reg_ptr->miimcon = val;
474
475  /*
476   * wait for cycle to terminate
477   */
478  do {
479    rtems_task_wake_after(2);
480  }  while (0 != (reg_ptr->miimind & M83xx_TSEC_MIIMIND_BUSY));
481  reg_ptr->miimcom = 0;
482  return 0;
483}
484
485
486/***************************************************************************\
487|  RX receive functions                                                     |
488\***************************************************************************/
489
490/*=========================================================================*\
491| Function:                                                                 |
492\*-------------------------------------------------------------------------*/
493static rtems_event_set mpc83xx_tsec_rx_wait_for_events
494(
495/*-------------------------------------------------------------------------*\
496| Purpose:                                                                  |
497|   handle all rx events                                                    |
498+---------------------------------------------------------------------------+
499| Input Parameters:                                                         |
500\*-------------------------------------------------------------------------*/
501 struct mpc83xx_tsec_struct *sc,       /* control structure                */
502 rtems_event_set event_mask            /* events to wait for               */
503)
504/*-------------------------------------------------------------------------*\
505| Return Value:                                                             |
506|    event set received                                                     |
507\*=========================================================================*/
508{
509 rtems_event_set events;            /* events received                     */
510 /*
511  * enable Rx interrupts, make sure this is not interrupted :-)
512  */
513 M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,M83xx_IEVENT_RXALL,~0);
514
515 /*
516  * wait for events to come in
517  */
518 rtems_bsdnet_event_receive(event_mask,
519                            RTEMS_EVENT_ANY | RTEMS_WAIT,
520                            RTEMS_NO_TIMEOUT,
521                            &events);
522 return events;
523}
524
525/*=========================================================================*\
526| Function:                                                                 |
527\*-------------------------------------------------------------------------*/
528static void mpc83xx_rxbd_alloc_clear
529(
530/*-------------------------------------------------------------------------*\
531| Purpose:                                                                  |
532|   allocate space for Rx BDs, clear them                                   |
533+---------------------------------------------------------------------------+
534| Input Parameters:                                                         |
535\*-------------------------------------------------------------------------*/
536 struct mpc83xx_tsec_struct *sc        /* control structure                */
537)
538/*-------------------------------------------------------------------------*\
539| Return Value:                                                             |
540|    <none>                                                                 |
541\*=========================================================================*/
542{
543  char *alloc_ptr;
544  PQBufferDescriptor_t *BD_ptr;
545  /*
546   * allocate proper space for Rx BDs
547   */
548  alloc_ptr = calloc((sc->rxBdCount+1),sizeof(PQBufferDescriptor_t));
549  if (alloc_ptr == NULL) {
550    rtems_panic("TSEC: cannot allocate space for Rx BDs");
551  }
552  alloc_ptr = (void *)((uint32_t )((alloc_ptr + (sizeof(PQBufferDescriptor_t)-1)))
553                       & ~(sizeof(PQBufferDescriptor_t)-1));
554  /*
555   * store pointers to certain positions in BD chain
556   */
557  sc->Rx_Last_BD = ((PQBufferDescriptor_t *)alloc_ptr)+sc->rxBdCount-1;
558  sc->Rx_Frst_BD = (PQBufferDescriptor_t *)alloc_ptr;
559  sc->Rx_NxtUsed_BD = sc->Rx_Frst_BD;
560  sc->Rx_NxtFill_BD = sc->Rx_Frst_BD;
561
562  /*
563   * clear all BDs
564   */
565  for (BD_ptr  = sc->Rx_Frst_BD;
566       BD_ptr <= sc->Rx_Last_BD;
567       BD_ptr++) {
568    BD_ptr->status = 0;
569  }
570  /*
571   * Init BD chain registers
572   */
573  sc->reg_ptr->rbase = (uint32_t) (sc->Rx_Frst_BD);
574}
575
576/*=========================================================================*\
577| Function:                                                                 |
578\*-------------------------------------------------------------------------*/
579static void mpc83xx_tsec_receive_packets
580(
581/*-------------------------------------------------------------------------*\
582| Purpose:                                                                  |
583|   process any received packets                                            |
584+---------------------------------------------------------------------------+
585| Input Parameters:                                                         |
586\*-------------------------------------------------------------------------*/
587 struct mpc83xx_tsec_struct *sc        /* control structure                */
588)
589/*-------------------------------------------------------------------------*\
590| Return Value:                                                             |
591|    <none>                                                                 |
592\*=========================================================================*/
593{
594  PQBufferDescriptor_t *BD_ptr;
595  struct mbuf *m,*n;
596  bool finished = false;
597  uint16_t status;
598  struct ether_header *eh;
599  int bd_idx;
600
601  BD_ptr = sc->Rx_NxtUsed_BD;
602
603  while ((0 == ((status = BD_ptr->status) & M83xx_BD_EMPTY)) &&
604         !finished &&
605         (BD_ptr->buffer != NULL)) {
606    /*
607     * get mbuf associated with BD
608     */
609    bd_idx = BD_ptr - sc->Rx_Frst_BD;
610    m = sc->Rx_mBuf_Ptr[bd_idx];
611    sc->Rx_mBuf_Ptr[bd_idx] = NULL;
612
613    /*
614     * Check that packet is valid
615     */
616    if ((status & (M83xx_BD_LAST |
617                   M83xx_BD_FIRST_IN_FRAME |
618                   M83xx_BD_LONG |
619                   M83xx_BD_NONALIGNED |
620                   M83xx_BD_CRC_ERROR |
621                   M83xx_BD_OVERRUN ))
622        == (M83xx_BD_LAST |
623            M83xx_BD_FIRST_IN_FRAME ) ) {
624      /*
625       * send mbuf of this buffer to ether_input()
626       */
627      m->m_len   = m->m_pkthdr.len = (BD_ptr->length
628                                    - sizeof(uint32_t)
629                                    - sizeof(struct ether_header));
630      eh         = mtod(m, struct ether_header *);
631      m->m_data += sizeof(struct ether_header);
632      ether_input(&sc->arpcom.ac_if,eh,m);
633    }
634    else {
635      /*
636       * throw away mbuf
637       */
638      MFREE(m,n);
639    }
640    /*
641     * mark buffer as non-allocated (for refill)
642     */
643    BD_ptr->buffer = NULL;
644    /*
645     * Advance BD_ptr to next BD
646     */
647    BD_ptr = ((BD_ptr == sc->Rx_Last_BD)
648             ? sc->Rx_Frst_BD
649             : BD_ptr+1);
650  }
651  sc->Rx_NxtUsed_BD = BD_ptr;
652}
653
654/*=========================================================================*\
655| Function:                                                                 |
656\*-------------------------------------------------------------------------*/
657static void mpc83xx_tsec_refill_rxbds
658(
659/*-------------------------------------------------------------------------*\
660| Purpose:                                                                  |
661|   link new buffers to rx BDs                                              |
662+---------------------------------------------------------------------------+
663| Input Parameters:                                                         |
664\*-------------------------------------------------------------------------*/
665 struct mpc83xx_tsec_struct *sc        /* control structure                */
666)
667/*-------------------------------------------------------------------------*\
668| Return Value:                                                             |
669|    <none>                                                                 |
670\*=========================================================================*/
671{
672  PQBufferDescriptor_t *BD_ptr;
673  struct mbuf *m,*n;
674  bool finished = false;
675  int bd_idx;
676
677  BD_ptr = sc->Rx_NxtFill_BD;
678  while ((BD_ptr->buffer == NULL) &&
679         !finished) {
680    /*
681     * get new mbuf and attach a cluster
682     */
683    MGETHDR(m,M_DONTWAIT,MT_DATA);
684    if (m != NULL) {
685      MCLGET(m,M_DONTWAIT);
686      if ((m->m_flags & M_EXT) == 0) {
687        MFREE(m,n);
688        m = NULL;
689      }
690    }
691    if (m == NULL) {
692      finished = true;
693    }
694    else {
695      bd_idx = BD_ptr - sc->Rx_Frst_BD;
696      sc->Rx_mBuf_Ptr[bd_idx] = m;
697
698      m->m_pkthdr.rcvif= &sc->arpcom.ac_if;
699      m->m_data        = M83xx_TSEC_ALIGN_BUFFER(m->m_ext.ext_buf,64);
700      BD_ptr->buffer   = m->m_data;
701      BD_ptr->length   = 0;
702      BD_ptr->status   = (M83xx_BD_EMPTY
703                          | M83xx_BD_INTERRUPT
704                          | ((BD_ptr == sc->Rx_Last_BD)
705                             ? M83xx_BD_WRAP
706                             : 0));
707      /*
708       * Advance BD_ptr to next BD
709       */
710      BD_ptr = ((BD_ptr == sc->Rx_Last_BD)
711                ? sc->Rx_Frst_BD
712                : BD_ptr+1);
713    }
714  }
715  sc->Rx_NxtFill_BD = BD_ptr;
716}
717
718/*=========================================================================*\
719| Function:                                                                 |
720\*-------------------------------------------------------------------------*/
721static void mpc83xx_tsec_rxDaemon
722(
723/*-------------------------------------------------------------------------*\
724| Purpose:                                                                  |
725|   handle all rx buffers and events                                        |
726+---------------------------------------------------------------------------+
727| Input Parameters:                                                         |
728\*-------------------------------------------------------------------------*/
729 void * arg                            /* argument, is sc structure ptr    */
730)
731/*-------------------------------------------------------------------------*\
732| Return Value:                                                             |
733|    <none>                                                                 |
734\*=========================================================================*/
735{
736  struct mpc83xx_tsec_struct *sc =
737    (struct mpc83xx_tsec_struct *)arg;
738  bool finished = false;
739  rtems_event_set events;
740#if !defined(CLREVENT_IN_IRQ)
741  uint32_t irq_events;
742#endif
743  /*
744   * enable Rx in MACCFG1 register
745   */
746  sc->reg_ptr->maccfg1 |= M83xx_TSEC_MACCFG1_RXEN;
747  while (!finished) {
748    /*
749     * fetch MBufs, associate them to RxBDs
750     */
751    mpc83xx_tsec_refill_rxbds(sc);
752    /*
753     * wait for events to come in
754     */
755    events = mpc83xx_tsec_rx_wait_for_events(sc,INTERRUPT_EVENT);
756#if !defined(CLREVENT_IN_IRQ)
757    /*
758     * clear any pending RX events
759     */
760    irq_events = sc->reg_ptr->ievent & M83xx_IEVENT_RXALL;
761    sc->reg_ptr->ievent = irq_events;
762#endif
763    /*
764     * fetch any completed buffers/packets received
765     * and stuff them into the TCP/IP Stack
766     */
767    mpc83xx_tsec_receive_packets(sc);
768  }
769  /*
770   * disable Rx in MACCFG1 register
771   */
772  sc->reg_ptr->maccfg1 &= ~M83xx_TSEC_MACCFG1_RXEN;
773  /*
774   * terminate daemon
775   */
776  sc->rxDaemonTid = 0;
777  rtems_task_delete(RTEMS_SELF);
778}
779
780/***************************************************************************\
781|  TX Transmit functions                                                    |
782\***************************************************************************/
783
784/*=========================================================================*\
785| Function:                                                                 |
786\*-------------------------------------------------------------------------*/
787static void mpc83xx_txbd_alloc_clear
788(
789/*-------------------------------------------------------------------------*\
790| Purpose:                                                                  |
791|   allocate space for Tx BDs, clear them                                   |
792+---------------------------------------------------------------------------+
793| Input Parameters:                                                         |
794\*-------------------------------------------------------------------------*/
795 struct mpc83xx_tsec_struct *sc        /* control structure                */
796)
797/*-------------------------------------------------------------------------*\
798| Return Value:                                                             |
799|    <none>                                                                 |
800\*=========================================================================*/
801{
802  char *alloc_ptr;
803  PQBufferDescriptor_t *BD_ptr;
804  /*
805   * allocate proper space for Tx BDs
806   */
807  alloc_ptr = calloc((sc->txBdCount+1),sizeof(PQBufferDescriptor_t));
808  if (alloc_ptr == NULL) {
809    rtems_panic("TSEC: cannot allocate space for Tx BDs");
810  }
811  alloc_ptr = (void *)((uint32_t )((alloc_ptr + (sizeof(PQBufferDescriptor_t)-1)))
812                       & ~(sizeof(PQBufferDescriptor_t)-1));
813  /*
814   * store pointers to certain positions in BD chain
815   */
816  sc->Tx_Last_BD = ((PQBufferDescriptor_t *)alloc_ptr)+sc->txBdCount-1;
817  sc->Tx_Frst_BD = (PQBufferDescriptor_t *)alloc_ptr;
818  sc->Tx_NxtUsed_BD = sc->Tx_Frst_BD;
819  sc->Tx_NxtFill_BD = sc->Tx_Frst_BD;
820
821  /*
822   * clear all BDs
823   */
824  for (BD_ptr  = sc->Tx_Frst_BD;
825       BD_ptr <= sc->Tx_Last_BD;
826       BD_ptr++) {
827    BD_ptr->status = 0;
828  }
829  /*
830   * Init BD chain registers
831   */
832  sc->reg_ptr->tbase = (uint32_t)(sc->Tx_Frst_BD);
833}
834
835/*=========================================================================*\
836| Function:                                                                 |
837\*-------------------------------------------------------------------------*/
838void mpc83xx_tsec_tx_start
839(
840/*-------------------------------------------------------------------------*\
841| Purpose:                                                                  |
842|   start transmission                                                      |
843+---------------------------------------------------------------------------+
844| Input Parameters:                                                         |
845\*-------------------------------------------------------------------------*/
846struct ifnet *ifp
847)
848/*-------------------------------------------------------------------------*\
849| Return Value:                                                             |
850|    <none>                                                                 |
851\*=========================================================================*/
852{
853  struct mpc83xx_tsec_struct *sc = ifp->if_softc;
854
855  ifp->if_flags |= IFF_OACTIVE;
856
857  rtems_event_send (sc->txDaemonTid, START_TRANSMIT_EVENT);
858}
859
860/*=========================================================================*\
861| Function:                                                                 |
862\*-------------------------------------------------------------------------*/
863static rtems_event_set mpc83xx_tsec_tx_wait_for_events
864(
865/*-------------------------------------------------------------------------*\
866| Purpose:                                                                  |
867|   handle all tx events                                                    |
868+---------------------------------------------------------------------------+
869| Input Parameters:                                                         |
870\*-------------------------------------------------------------------------*/
871 struct mpc83xx_tsec_struct *sc,       /* control structure                */
872 rtems_event_set event_mask            /* events to wait for               */
873)
874/*-------------------------------------------------------------------------*\
875| Return Value:                                                             |
876|    event set received                                                     |
877\*=========================================================================*/
878{
879 rtems_event_set events;            /* events received                     */
880 /*
881  * enable Tx interrupts, make sure this is not interrupted :-)
882  */
883 M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,M83xx_IEVENT_TXALL,~0);
884
885 /*
886  * wait for events to come in
887  */
888 rtems_bsdnet_event_receive(event_mask,
889                            RTEMS_EVENT_ANY | RTEMS_WAIT,
890                            RTEMS_NO_TIMEOUT,
891                            &events);
892 return events;
893}
894
895/*=========================================================================*\
896| Function:                                                                 |
897\*-------------------------------------------------------------------------*/
898static void mpc83xx_tsec_tx_retire
899(
900/*-------------------------------------------------------------------------*\
901| Purpose:                                                                  |
902|   handle all tx events                                                    |
903+---------------------------------------------------------------------------+
904| Input Parameters:                                                         |
905\*-------------------------------------------------------------------------*/
906 struct mpc83xx_tsec_struct *sc        /* control structure                */
907)
908/*-------------------------------------------------------------------------*\
909| Return Value:                                                             |
910|    <none>                                                                 |
911\*=========================================================================*/
912{
913  PQBufferDescriptor_t *RetBD;
914  RetBD = sc->Tx_NxtUsed_BD;
915  int bd_idx;
916  struct mbuf *m,*n;
917  /*
918   * check next BDs to be empty
919   */
920  while ((RetBD->buffer != NULL)                       /* BD is filled      */
921         && (0 == (RetBD->status & M83xx_BD_READY ))) {/* BD no longer ready*/
922
923    bd_idx = RetBD - sc->Tx_Frst_BD;
924    m = sc->Tx_mBuf_Ptr[bd_idx];
925    sc->Tx_mBuf_Ptr[bd_idx] = NULL;
926
927    MFREE(m,n);
928    RetBD->buffer = NULL;
929    /*
930     * Advance CurrBD to next BD
931     */
932    RetBD = ((RetBD == sc->Tx_Last_BD)
933             ? sc->Tx_Frst_BD
934             : RetBD+1);
935  }
936  sc->Tx_NxtUsed_BD = RetBD;
937}
938
939/*=========================================================================*\
940| Function:                                                                 |
941\*-------------------------------------------------------------------------*/
942static void mpc83xx_tsec_sendpacket
943(
944/*-------------------------------------------------------------------------*\
945| Purpose:                                                                  |
946|   handle all tx events                                                    |
947+---------------------------------------------------------------------------+
948| Input Parameters:                                                         |
949\*-------------------------------------------------------------------------*/
950 struct mpc83xx_tsec_struct *sc,       /* control structure                */
951 struct mbuf *m                        /* start of packet to send          */
952)
953/*-------------------------------------------------------------------------*\
954| Return Value:                                                             |
955|    <none>                                                                 |
956\*=========================================================================*/
957{
958  PQBufferDescriptor_t *FrstBD = NULL;
959  PQBufferDescriptor_t *CurrBD;
960  uint16_t status;
961  struct mbuf *l = NULL; /* ptr to last non-freed (non-empty) mbuf */
962  int bd_idx;
963  /*
964   * get next Tx BD
965   */
966  CurrBD = sc->Tx_NxtFill_BD;
967  while (m) {
968    if(m->m_len == 0) {
969      /*
970       * Just toss empty mbufs
971       */
972      struct mbuf *n;
973      MFREE(m, n);
974      m = n;
975      if(l != NULL) {
976        l->m_next = m;
977      }
978    }
979    else {
980      /*
981       * this mbuf is non-empty, so send it
982       */
983      /*
984       * Is CurrBD still in Use/not yet retired?
985       */
986      while (CurrBD->buffer != NULL) {
987        /*
988         * Then try to retire it
989         * and to return its mbuf
990         */
991        mpc83xx_tsec_tx_retire(sc);
992        if (CurrBD->buffer != NULL) {
993          /*
994           * Wait for anything to happen...
995           */
996          mpc83xx_tsec_tx_wait_for_events(sc,INTERRUPT_EVENT);
997        }
998      }
999      status = ((M83xx_BD_PAD_CRC | M83xx_BD_TX_CRC)
1000                | ((m->m_next == NULL)
1001                   ? M83xx_BD_LAST | M83xx_BD_INTERRUPT
1002                   : 0)
1003                | ((CurrBD == sc->Tx_Last_BD) ? M83xx_BD_WRAP : 0));
1004
1005      /*
1006       * link buffer to BD
1007       */
1008      CurrBD->buffer = mtod(m, void *);
1009      CurrBD->length = (uint32_t)m->m_len;
1010      l = m;       /* remember: we use this mbuf          */
1011
1012      bd_idx = CurrBD - sc->Tx_Frst_BD;
1013      sc->Tx_mBuf_Ptr[bd_idx] = m;
1014
1015      m = m->m_next; /* advance to next mbuf of this packet */
1016      /*
1017       * is this the first BD of the packet?
1018       * then don't set it to "READY" state,
1019       * and remember this BD position
1020       */
1021      if (FrstBD == NULL) {
1022        FrstBD = CurrBD;
1023      }
1024      else {
1025        status |= M83xx_BD_READY;
1026      }
1027      CurrBD->status = status;
1028      /*
1029       * Advance CurrBD to next BD
1030       */
1031      CurrBD = ((CurrBD == sc->Tx_Last_BD)
1032                ? sc->Tx_Frst_BD
1033                : CurrBD+1);
1034    }
1035  }
1036  /*
1037   * mbuf chain of this packet
1038   * has been translated
1039   * to BD chain, so set first BD ready now
1040   */
1041  if (FrstBD != NULL) {
1042    FrstBD->status |= M83xx_BD_READY;
1043  }
1044  sc->Tx_NxtFill_BD = CurrBD;
1045  /*
1046   * wake up transmitter (clear TSTAT[THLT])
1047   */
1048  sc->reg_ptr->tstat = M83xx_TSEC_TSTAT_THLT;
1049}
1050
1051/*=========================================================================*\
1052| Function:                                                                 |
1053\*-------------------------------------------------------------------------*/
1054static void mpc83xx_tsec_txDaemon
1055(
1056/*-------------------------------------------------------------------------*\
1057| Purpose:                                                                  |
1058|   handle all tx events                                                    |
1059+---------------------------------------------------------------------------+
1060| Input Parameters:                                                         |
1061\*-------------------------------------------------------------------------*/
1062 void * arg                            /* argument, is sc structure ptr    */
1063)
1064/*-------------------------------------------------------------------------*\
1065| Return Value:                                                             |
1066|    <none>                                                                 |
1067\*=========================================================================*/
1068{
1069  struct mpc83xx_tsec_struct *sc =
1070    (struct mpc83xx_tsec_struct *)arg;
1071  struct ifnet *ifp = &sc->arpcom.ac_if;
1072  struct mbuf *m;
1073  bool finished = false;
1074  rtems_event_set events;
1075#if !defined(CLREVENT_IN_IRQ)
1076  uint32_t irq_events;
1077#endif
1078
1079  /*
1080   * enable Tx in MACCFG1 register
1081   * FIXME: make this irq save
1082   */
1083  sc->reg_ptr->maccfg1 |= M83xx_TSEC_MACCFG1_TXEN;
1084  while (!finished) {
1085    /*
1086     * wait for events to come in
1087     */
1088    events = mpc83xx_tsec_tx_wait_for_events(sc,
1089                                             START_TRANSMIT_EVENT
1090                                             | INTERRUPT_EVENT);
1091#if !defined(CLREVENT_IN_IRQ)
1092    /*
1093     * clear any pending TX events
1094     */
1095    irq_events = sc->reg_ptr->ievent & M83xx_IEVENT_TXALL;
1096    sc->reg_ptr->ievent = irq_events;
1097#endif
1098    /*
1099     * retire any sent tx BDs
1100     */
1101    mpc83xx_tsec_tx_retire(sc);
1102    /*
1103     * Send packets till queue is empty
1104     */
1105    do {
1106      /*
1107       * Get the next mbuf chain to transmit.
1108       */
1109      IF_DEQUEUE(&ifp->if_snd, m);
1110
1111      if (m) {
1112        mpc83xx_tsec_sendpacket(sc,m);
1113      }
1114    } while (m != NULL);
1115
1116    ifp->if_flags &= ~IFF_OACTIVE;
1117  }
1118  /*
1119   * disable Tx in MACCFG1 register
1120   */
1121  sc->reg_ptr->maccfg1 &= ~M83xx_TSEC_MACCFG1_TXEN;
1122  /*
1123   * terminate daemon
1124   */
1125  sc->txDaemonTid = 0;
1126  rtems_task_delete(RTEMS_SELF);
1127}
1128
1129/***************************************************************************\
1130|  Interrupt handlers and management routines                               |
1131\***************************************************************************/
1132
1133/*=========================================================================*\
1134| Function:                                                                 |
1135\*-------------------------------------------------------------------------*/
1136static void mpc83xx_tsec_tx_irq_handler
1137(
1138/*-------------------------------------------------------------------------*\
1139| Purpose:                                                                  |
1140|   handle tx interrupts                                                    |
1141+---------------------------------------------------------------------------+
1142| Input Parameters:                                                         |
1143\*-------------------------------------------------------------------------*/
1144 rtems_irq_hdl_param handle            /* handle, is sc structure ptr      */
1145)
1146/*-------------------------------------------------------------------------*\
1147| Return Value:                                                             |
1148|    <none>                                                                 |
1149\*=========================================================================*/
1150{
1151  struct mpc83xx_tsec_struct *sc =
1152    (struct mpc83xx_tsec_struct *)handle;
1153#if defined(CLREVENT_IN_IRQ)
1154  uint32_t irq_events;
1155#endif
1156
1157  sc->txInterrupts++;
1158  /*
1159   * disable tx interrupts
1160   */
1161  M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,M83xx_IEVENT_TXALL,0);
1162
1163#if defined(CLREVENT_IN_IRQ)
1164  /*
1165   * clear any pending TX events
1166   */
1167  irq_events = sc->reg_ptr->ievent & M83xx_IEVENT_TXALL;
1168  sc->reg_ptr->ievent = irq_events;
1169#endif
1170  /*
1171   * wake up tx Daemon
1172   */
1173  rtems_event_send(sc->txDaemonTid, INTERRUPT_EVENT);
1174}
1175
1176/*=========================================================================*\
1177| Function:                                                                 |
1178\*-------------------------------------------------------------------------*/
1179static void mpc83xx_tsec_rx_irq_handler
1180(
1181/*-------------------------------------------------------------------------*\
1182| Purpose:                                                                  |
1183|   handle rx interrupts                                                    |
1184+---------------------------------------------------------------------------+
1185| Input Parameters:                                                         |
1186\*-------------------------------------------------------------------------*/
1187 rtems_irq_hdl_param handle            /* handle, is sc structure          */
1188)
1189/*-------------------------------------------------------------------------*\
1190| Return Value:                                                             |
1191|    <none>                                                                 |
1192\*=========================================================================*/
1193{
1194  struct mpc83xx_tsec_struct *sc =
1195    (struct mpc83xx_tsec_struct *)handle;
1196#if defined(CLREVENT_IN_IRQ)
1197  uint32_t irq_events;
1198#endif
1199
1200  sc->rxInterrupts++;
1201  /*
1202   * disable rx interrupts
1203   */
1204  M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,M83xx_IEVENT_RXALL,0);
1205#if defined(CLREVENT_IN_IRQ)
1206  /*
1207   * clear any pending RX events
1208   */
1209  irq_events = sc->reg_ptr->ievent & M83xx_IEVENT_RXALL;
1210  sc->reg_ptr->ievent = irq_events;
1211#endif
1212  /*
1213   * wake up rx Daemon<
1214   */
1215  rtems_event_send(sc->rxDaemonTid, INTERRUPT_EVENT);
1216}
1217
1218
1219/*=========================================================================*\
1220| Function:                                                                 |
1221\*-------------------------------------------------------------------------*/
1222static void mpc83xx_tsec_err_irq_handler
1223(
1224/*-------------------------------------------------------------------------*\
1225| Purpose:                                                                  |
1226|   handle error interrupts                                                 |
1227+---------------------------------------------------------------------------+
1228| Input Parameters:                                                         |
1229\*-------------------------------------------------------------------------*/
1230 rtems_irq_hdl_param handle            /* handle, is sc structure          */
1231)
1232/*-------------------------------------------------------------------------*\
1233| Return Value:                                                             |
1234|    <none>                                                                 |
1235\*=========================================================================*/
1236{
1237  struct mpc83xx_tsec_struct *sc =
1238    (struct mpc83xx_tsec_struct *)handle;
1239  /*
1240   * clear error events in IEVENT
1241   */
1242  sc->reg_ptr->ievent = M83xx_IEVENT_ERRALL;
1243  /*
1244   * has Rx been stopped? then restart it
1245   */
1246  if (0 != (sc->reg_ptr->rstat & M83xx_TSEC_RSTAT_QHLT)) {
1247    sc->rxErrors++;
1248    sc->reg_ptr->rstat = M83xx_TSEC_RSTAT_QHLT;
1249  }
1250  /*
1251   * has Tx been stopped? then restart it
1252   */
1253  if (0 != (sc->reg_ptr->tstat & M83xx_TSEC_TSTAT_THLT)) {
1254    sc->txErrors++;
1255    sc->reg_ptr->tstat = M83xx_TSEC_TSTAT_THLT;
1256  }
1257}
1258
1259
1260/*=========================================================================*\
1261| Function:                                                                 |
1262\*-------------------------------------------------------------------------*/
1263static uint32_t mpc83xx_tsec_irq_mask
1264(
1265/*-------------------------------------------------------------------------*\
1266| Purpose:                                                                  |
1267|   determine irq mask for given interrupt number                           |
1268+---------------------------------------------------------------------------+
1269| Input Parameters:                                                         |
1270\*-------------------------------------------------------------------------*/
1271 int irqnum,
1272 struct mpc83xx_tsec_struct *sc
1273)
1274/*-------------------------------------------------------------------------*\
1275| Return Value:                                                             |
1276|    interrupt mask (for ievent/imask register)                             |
1277\*=========================================================================*/
1278{
1279  return ((irqnum == sc->irq_num_tx)
1280          ? M83xx_IEVENT_TXALL
1281          : ((irqnum == sc->irq_num_rx)
1282             ? M83xx_IEVENT_RXALL
1283             : ((irqnum == sc->irq_num_err)
1284                ? M83xx_IEVENT_ERRALL
1285                : 0)));
1286}
1287/*=========================================================================*\
1288| Function:                                                                 |
1289\*-------------------------------------------------------------------------*/
1290static void mpc83xx_tsec_irq_on
1291(
1292/*-------------------------------------------------------------------------*\
1293| Purpose:                                                                  |
1294|   enable interrupts in TSEC mask register                              |
1295+---------------------------------------------------------------------------+
1296| Input Parameters:                                                         |
1297\*-------------------------------------------------------------------------*/
1298 const
1299 rtems_irq_connect_data *irq_conn_data   /* irq connect data                */
1300)
1301/*-------------------------------------------------------------------------*\
1302| Return Value:                                                             |
1303|    <none>                                                                 |
1304\*=========================================================================*/
1305{
1306  struct mpc83xx_tsec_struct *sc =
1307    (struct mpc83xx_tsec_struct *)(irq_conn_data->handle);
1308
1309  M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,
1310                       mpc83xx_tsec_irq_mask(irq_conn_data->name,sc),
1311                       ~0);
1312}
1313
1314/*=========================================================================*\
1315| Function:                                                                 |
1316\*-------------------------------------------------------------------------*/
1317static void mpc83xx_tsec_irq_off
1318(
1319/*-------------------------------------------------------------------------*\
1320| Purpose:                                                                  |
1321|   disable TX interrupts in TSEC mask register                             |
1322+---------------------------------------------------------------------------+
1323| Input Parameters:                                                         |
1324\*-------------------------------------------------------------------------*/
1325 const
1326 rtems_irq_connect_data *irq_conn_data   /* irq connect data                */
1327)
1328/*-------------------------------------------------------------------------*\
1329| Return Value:                                                             |
1330|    <none>                                                                 |
1331\*=========================================================================*/
1332{
1333  struct mpc83xx_tsec_struct *sc =
1334    (struct mpc83xx_tsec_struct *)irq_conn_data->handle;
1335
1336  M83xx_TSEC_IMASK_SET(sc->reg_ptr->imask,
1337                       mpc83xx_tsec_irq_mask(irq_conn_data->name,sc),
1338                       0);
1339}
1340
1341/*=========================================================================*\
1342| Function:                                                                 |
1343\*-------------------------------------------------------------------------*/
1344static int mpc83xx_tsec_irq_isOn
1345(
1346/*-------------------------------------------------------------------------*\
1347| Purpose:                                                                  |
1348|   check state of interrupts in TSEC mask register                         |
1349+---------------------------------------------------------------------------+
1350| Input Parameters:                                                         |
1351\*-------------------------------------------------------------------------*/
1352 const
1353 rtems_irq_connect_data *irq_conn_data  /* irq connect data                */
1354)
1355/*-------------------------------------------------------------------------*\
1356| Return Value:                                                             |
1357|    <none>                                                                 |
1358\*=========================================================================*/
1359{
1360  struct mpc83xx_tsec_struct *sc =
1361    (struct mpc83xx_tsec_struct *)irq_conn_data->handle;
1362
1363  return (0 != (sc->reg_ptr->imask
1364                & mpc83xx_tsec_irq_mask(irq_conn_data->name,sc)));
1365}
1366
1367/*=========================================================================*\
1368| Function:                                                                 |
1369\*-------------------------------------------------------------------------*/
1370static void mpc83xx_tsec_install_irq_handlers
1371(
1372/*-------------------------------------------------------------------------*\
1373| Purpose:                                                                  |
1374|   (un-)install the interrupt handlers                                     |
1375+---------------------------------------------------------------------------+
1376| Input Parameters:                                                         |
1377\*-------------------------------------------------------------------------*/
1378 struct mpc83xx_tsec_struct *sc,        /* ptr to control structure        */
1379 bool   install                         /* true: install, false: remove    */
1380)
1381/*-------------------------------------------------------------------------*\
1382| Return Value:                                                             |
1383|    <none>                                                                 |
1384\*=========================================================================*/
1385{
1386  size_t i;
1387
1388  rtems_irq_connect_data irq_conn_data[3] = {
1389    {
1390      sc->irq_num_tx,
1391      mpc83xx_tsec_tx_irq_handler, /* rtems_irq_hdl           */
1392      (rtems_irq_hdl_param)sc,     /* (rtems_irq_hdl_param)   */
1393      mpc83xx_tsec_irq_on,         /* (rtems_irq_enable)      */
1394      mpc83xx_tsec_irq_off,        /* (rtems_irq_disable)     */
1395      mpc83xx_tsec_irq_isOn        /* (rtems_irq_is_enabled)  */
1396    },{
1397      sc->irq_num_rx,
1398      mpc83xx_tsec_rx_irq_handler, /* rtems_irq_hdl           */
1399      (rtems_irq_hdl_param)sc,     /* (rtems_irq_hdl_param)   */
1400      mpc83xx_tsec_irq_on,         /* (rtems_irq_enable)      */
1401      mpc83xx_tsec_irq_off,        /* (rtems_irq_disable)     */
1402      mpc83xx_tsec_irq_isOn        /* (rtems_irq_is_enabled)  */
1403    },{
1404      sc->irq_num_err,
1405      mpc83xx_tsec_err_irq_handler, /* rtems_irq_hdl           */
1406      (rtems_irq_hdl_param)sc,      /* (rtems_irq_hdl_param)   */
1407      mpc83xx_tsec_irq_on,          /* (rtems_irq_enable)      */
1408      mpc83xx_tsec_irq_off,         /* (rtems_irq_disable)     */
1409      mpc83xx_tsec_irq_isOn         /* (rtems_irq_is_enabled)  */
1410    }
1411  };
1412
1413  /*
1414   * (un-)install handler for Tx/Rx/Error
1415   */
1416  for (i = 0;
1417       i < sizeof(irq_conn_data)/sizeof(irq_conn_data[0]);
1418       i++) {
1419    if (install) {
1420      if (!BSP_install_rtems_irq_handler (&irq_conn_data[i])) {
1421        rtems_panic("TSEC: cannot install IRQ handler");
1422      }
1423    }
1424    else {
1425      if (!BSP_remove_rtems_irq_handler (&irq_conn_data[i])) {
1426        rtems_panic("TSEC: cannot uninstall IRQ handler");
1427      }
1428    }
1429  }
1430}
1431
1432/***************************************************************************\
1433|  Initialization and interface routines                                    |
1434\***************************************************************************/
1435
1436/*=========================================================================*\
1437| Function:                                                                 |
1438\*-------------------------------------------------------------------------*/
1439static void mpc83xx_tsec_init
1440(
1441/*-------------------------------------------------------------------------*\
1442| Purpose:                                                                  |
1443|   initialize the driver and the hardware                                  |
1444+---------------------------------------------------------------------------+
1445| Input Parameters:                                                         |
1446\*-------------------------------------------------------------------------*/
1447 void *arg                              /* argument pointer, contains *sc  */
1448)
1449/*-------------------------------------------------------------------------*\
1450| Return Value:                                                             |
1451|    zero, if success                                                       |
1452\*=========================================================================*/
1453{
1454  struct mpc83xx_tsec_struct *sc = (struct mpc83xx_tsec_struct *)arg;
1455  struct ifnet *ifp = &sc->arpcom.ac_if;
1456  /*
1457   * check, whether device is not yet running
1458   */
1459  if (0 == sc->rxDaemonTid) {
1460    /*
1461     * allocate rx/tx BDs
1462     */
1463    mpc83xx_rxbd_alloc_clear(sc);
1464    mpc83xx_txbd_alloc_clear(sc);
1465    /*
1466     * allocate storage for mbuf ptrs
1467     */
1468    sc->Rx_mBuf_Ptr = calloc(sc->rxBdCount,sizeof(struct mbuf *));
1469    sc->Tx_mBuf_Ptr = calloc(sc->txBdCount,sizeof(struct mbuf *));
1470    if ((sc->Rx_mBuf_Ptr == NULL) ||
1471        (sc->Tx_mBuf_Ptr == NULL)) {
1472        rtems_panic("TSEC: cannot allocate buffers for mbuf management");
1473
1474    }
1475
1476    /*
1477     * initialize TSEC hardware:
1478     * - set interrupt coalescing to BDCount/8, Time of 8 frames
1479     * - enable DMA snooping
1480     */
1481    mpc83xx_tsec_hwinit(sc);
1482    /*
1483     * init access to phys
1484     */
1485    mpc83xx_tsec_mdio_init(sc);
1486    /*
1487     * Start driver tasks
1488     */
1489    sc->txDaemonTid = rtems_bsdnet_newproc("TStx",
1490                                           4096,
1491                                           mpc83xx_tsec_txDaemon,
1492                                           sc);
1493    sc->rxDaemonTid = rtems_bsdnet_newproc("TSrx", 4096,
1494                                           mpc83xx_tsec_rxDaemon,
1495                                           sc);
1496    /*
1497     * install interrupt handlers
1498     */
1499    mpc83xx_tsec_install_irq_handlers(sc,true);
1500  }
1501  /*
1502   * Set flags appropriately
1503   */
1504  if(ifp->if_flags & IFF_PROMISC) {
1505    sc->reg_ptr->rctrl |=  M83xx_TSEC_RCTRL_PROM;
1506  }
1507  else {
1508    sc->reg_ptr->rctrl &= ~M83xx_TSEC_RCTRL_PROM;
1509  }
1510
1511#if defined(HSC_CM01)
1512  /*
1513   * for HSC CM01: we need to configure the PHY to use maximum skew adjust
1514   */
1515
1516  mpc83xx_tsec_mdio_write(-1,sc,23,0x0100);
1517#endif
1518
1519  /*
1520   * init timer so the "watchdog function gets called periodically
1521   */
1522  ifp->if_timer    = 1;
1523  /*
1524   * Tell the world that we're running.
1525   */
1526  ifp->if_flags |= IFF_RUNNING;
1527}
1528
1529/*=========================================================================*\
1530| Function:                                                                 |
1531\*-------------------------------------------------------------------------*/
1532static void mpc83xx_tsec_off
1533(
1534/*-------------------------------------------------------------------------*\
1535| Purpose:                                                                  |
1536|   deinitialize the driver and the hardware                                |
1537+---------------------------------------------------------------------------+
1538| Input Parameters:                                                         |
1539\*-------------------------------------------------------------------------*/
1540 struct mpc83xx_tsec_struct *sc         /* ptr to control structure        */
1541)
1542/*-------------------------------------------------------------------------*\
1543| Return Value:                                                             |
1544|    <none>                                                                 |
1545\*=========================================================================*/
1546{
1547  /*
1548   * deinitialize driver?
1549   */
1550}
1551
1552/*=========================================================================*\
1553| Function:                                                                 |
1554\*-------------------------------------------------------------------------*/
1555static void mpc83xx_tsec_stats
1556(
1557/*-------------------------------------------------------------------------*\
1558| Purpose:                                                                  |
1559|   print statistics                                                        |
1560+---------------------------------------------------------------------------+
1561| Input Parameters:                                                         |
1562\*-------------------------------------------------------------------------*/
1563 struct mpc83xx_tsec_struct *sc         /* ptr to control structure        */
1564)
1565/*-------------------------------------------------------------------------*\
1566| Return Value:                                                             |
1567|    <none>                                                                 |
1568\*=========================================================================*/
1569{
1570  int media;
1571  int result;
1572  /*
1573   * fetch/print media info
1574   */
1575  media = IFM_MAKEWORD(0,0,0,sc->phy_default); /* fetch from default phy */
1576
1577  result = mpc83xx_tsec_ioctl(&(sc->arpcom.ac_if),
1578                              SIOCGIFMEDIA,
1579                              (caddr_t)&media);
1580  if (result == 0) {
1581    rtems_ifmedia2str(media,NULL,0);
1582    printf ("\n");
1583  }
1584#if 0 /* print all PHY registers */
1585  {
1586    int reg;
1587    uint32_t reg_val;
1588    printf("****** PHY register values****\n");
1589    for (reg = 0;reg <= 31;reg++) {
1590      mpc83xx_tsec_mdio_read(-1,sc,reg,&reg_val);
1591      printf("%02d:0x%04x%c",reg,reg_val,
1592             (((reg % 4) == 3) ? '\n' : ' '));
1593    }
1594  }
1595#endif
1596  /*
1597   * print some statistics
1598   */
1599  printf ("   Rx Interrupts:%-8lu",   sc->rxInterrupts);
1600  printf ("       Rx Errors:%-8lu",   sc->rxErrors);
1601  printf ("      Rx packets:%-8lu\n",
1602          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rpkt]);
1603  printf ("   Rx broadcasts:%-8lu",
1604          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rbca]);
1605  printf ("   Rx multicasts:%-8lu",
1606          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rmca]);
1607  printf ("           Giant:%-8lu\n",
1608          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rovr]);
1609  printf ("       Non-octet:%-8lu",
1610          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_raln]);
1611  printf ("         Bad CRC:%-8lu",
1612          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rfcs]);
1613  printf ("         Overrun:%-8lu\n",
1614          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_rdrp]);
1615
1616  printf ("   Tx Interrupts:%-8lu",   sc->txInterrupts);
1617  printf ("       Tx Errors:%-8lu",   sc->txErrors);
1618  printf ("      Tx packets:%-8lu\n",
1619          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_tpkt]);
1620  printf ("        Deferred:%-8lu",
1621          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_tdfr]);
1622  printf ("  Late Collision:%-8lu",
1623          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_tlcl]);
1624  printf ("Retransmit Limit:%-8lu\n",
1625          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_tedf]);
1626  printf ("        Underrun:%-8lu\n",
1627          sc->reg_ptr->rmon_mib[m83xx_tsec_rmon_tund]);
1628}
1629
1630/*=========================================================================*\
1631| Function:                                                                 |
1632\*-------------------------------------------------------------------------*/
1633static int mpc83xx_tsec_ioctl
1634(
1635/*-------------------------------------------------------------------------*\
1636| Purpose:                                                                  |
1637|   perform io control functions                                            |
1638+---------------------------------------------------------------------------+
1639| Input Parameters:                                                         |
1640\*-------------------------------------------------------------------------*/
1641 struct ifnet *ifp,                    /* interface information            */
1642 ioctl_command_t command,              /* ioctl command code               */
1643 caddr_t data                          /* optional data                    */
1644)
1645/*-------------------------------------------------------------------------*\
1646| Return Value:                                                             |
1647|    zero, if success                                                       |
1648\*=========================================================================*/
1649{
1650  struct mpc83xx_tsec_struct *sc = ifp->if_softc;
1651  int error = 0;
1652
1653  switch(command)  {
1654    /*
1655     * access PHY via MII
1656     */
1657  case SIOCGIFMEDIA:
1658  case SIOCSIFMEDIA:
1659    rtems_mii_ioctl (&(sc->mdio_info),sc,command,(void *)data);
1660    break;
1661  case SIOCGIFADDR:
1662  case SIOCSIFADDR:
1663    /*
1664     * pass through to general ether_ioctl
1665     */
1666    ether_ioctl(ifp, command, data);
1667    break;
1668
1669  case SIOCSIFFLAGS:
1670    /*
1671     * adjust active state
1672     */
1673    if (ifp->if_flags & IFF_RUNNING) {
1674      mpc83xx_tsec_off(sc);
1675    }
1676    if (ifp->if_flags & IFF_UP) {
1677      mpc83xx_tsec_init(sc);
1678    }
1679    break;
1680
1681  case SIO_RTEMS_SHOW_STATS:
1682    /*
1683     * show interface statistics
1684     */
1685    mpc83xx_tsec_stats(sc);
1686    break;
1687
1688    /*
1689     * All sorts of multicast commands need to be added here!
1690     */
1691  default:
1692    error = EINVAL;
1693    break;
1694  }
1695
1696  return error;
1697}
1698
1699/* #define DEBUG */
1700
1701/*=========================================================================*\
1702| Function:                                                                 |
1703\*-------------------------------------------------------------------------*/
1704int rtems_mpc83xx_tsec_mode_adapt
1705(
1706/*-------------------------------------------------------------------------*\
1707| Purpose:                                                                  |
1708|   init the PHY and adapt TSEC settings                                    |
1709+---------------------------------------------------------------------------+
1710| Input Parameters:                                                         |
1711\*-------------------------------------------------------------------------*/
1712 struct ifnet *ifp
1713)
1714/*-------------------------------------------------------------------------*\
1715| Return Value:                                                             |
1716|    0, if success                                                       |
1717\*=========================================================================*/
1718{
1719  int result = 0;
1720  struct mpc83xx_tsec_struct *sc = ifp->if_softc;
1721  int media = IFM_MAKEWORD( 0, 0, 0, sc->phy_default);
1722
1723#ifdef DEBUG
1724  printf("c");
1725#endif
1726  /*
1727   * fetch media status
1728   */
1729  result = mpc83xx_tsec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
1730  if (result != 0) {
1731    return result;
1732  }
1733#ifdef DEBUG
1734  printf("C");
1735#endif
1736  /*
1737   * status is unchanged? then do nothing
1738   */
1739  if (media == sc->media_state) {
1740    return 0;
1741  }
1742  /*
1743   * otherwise: for the first call, try to negotiate mode
1744   */
1745  if (sc->media_state == 0) {
1746    /*
1747     * set media status: set auto negotiation -> start auto-negotiation
1748     */
1749    media = IFM_MAKEWORD(0,IFM_AUTO,0,sc->phy_default);
1750    result = mpc83xx_tsec_ioctl(ifp,SIOCSIFMEDIA,(caddr_t)&media);
1751    if (result != 0) {
1752      return result;
1753    }
1754    /*
1755     * wait for auto-negotiation to terminate
1756     */
1757    do {
1758      media = IFM_MAKEWORD(0,0,0,sc->phy_default);
1759      result = mpc83xx_tsec_ioctl(ifp,SIOCGIFMEDIA,(caddr_t)&media);
1760      if (result != 0) {
1761        return result;
1762      }
1763    } while (IFM_NONE == IFM_SUBTYPE(media));
1764  }
1765
1766  /*
1767   * now set HW according to media results:
1768   */
1769  /*
1770   * if we are 1000MBit, then switch IF to byte mode
1771   */
1772  if (IFM_1000_T == IFM_SUBTYPE(media)) {
1773    sc->reg_ptr->maccfg2 =
1774      ((sc->reg_ptr->maccfg2 & ~M83xx_TSEC_MACCFG2_IFMODE_MSK)
1775       | M83xx_TSEC_MACCFG2_IFMODE_BYT);
1776  }
1777  else {
1778    sc->reg_ptr->maccfg2 =
1779      ((sc->reg_ptr->maccfg2 & ~M83xx_TSEC_MACCFG2_IFMODE_MSK)
1780       | M83xx_TSEC_MACCFG2_IFMODE_NIB);
1781  }
1782  /*
1783   * if we are 10MBit, then switch rate to 10M
1784   */
1785  if (IFM_10_T == IFM_SUBTYPE(media)) {
1786    sc->reg_ptr->ecntrl &= ~M83xx_TSEC_ECNTRL_R100M;
1787  }
1788  else {
1789    sc->reg_ptr->ecntrl |= M83xx_TSEC_ECNTRL_R100M;
1790  }
1791  /*
1792   * if we are half duplex then switch to half duplex
1793   */
1794  if (0 == (IFM_FDX & IFM_OPTIONS(media))) {
1795    sc->reg_ptr->maccfg2 &= ~M83xx_TSEC_MACCFG2_FULLDUPLEX;
1796  }
1797  else {
1798    sc->reg_ptr->maccfg2 |=  M83xx_TSEC_MACCFG2_FULLDUPLEX;
1799  }
1800  /*
1801   * store current media state for future compares
1802   */
1803  sc->media_state = media;
1804
1805  return 0;
1806}
1807
1808/*=========================================================================*\
1809| Function:                                                                 |
1810\*-------------------------------------------------------------------------*/
1811static void mpc83xx_tsec_watchdog
1812(
1813/*-------------------------------------------------------------------------*\
1814| Purpose:                                                                  |
1815|   periodically poll the PHY. if mode has changed,                         |
1816|  then adjust the TSEC settings                                            |
1817+---------------------------------------------------------------------------+
1818| Input Parameters:                                                         |
1819\*-------------------------------------------------------------------------*/
1820 struct ifnet *ifp
1821)
1822/*-------------------------------------------------------------------------*\
1823| Return Value:                                                             |
1824|    1, if success                                                       |
1825\*=========================================================================*/
1826{
1827  rtems_mpc83xx_tsec_mode_adapt(ifp);
1828  ifp->if_timer    = TSEC_WATCHDOG_TIMEOUT;
1829}
1830
1831/*=========================================================================*\
1832| Function:                                                                 |
1833\*-------------------------------------------------------------------------*/
1834static int mpc83xx_tsec_driver_attach
1835(
1836/*-------------------------------------------------------------------------*\
1837| Purpose:                                                                  |
1838|   attach the driver                                                       |
1839+---------------------------------------------------------------------------+
1840| Input Parameters:                                                         |
1841\*-------------------------------------------------------------------------*/
1842 struct rtems_bsdnet_ifconfig *config  /* interface configuration          */
1843)
1844/*-------------------------------------------------------------------------*\
1845| Return Value:                                                             |
1846|    1, if success                                                          |
1847\*=========================================================================*/
1848{
1849  struct mpc83xx_tsec_struct *sc;
1850  struct ifnet *ifp;
1851  int    unitNumber;
1852  char   *unitName;
1853  uint32_t svr = _read_SVR();
1854  uint32_t pvr = _read_PVR();
1855
1856 /*
1857  * Parse driver name
1858  */
1859  if((unitNumber = rtems_bsdnet_parse_driver_name(config, &unitName)) < 0) {
1860    return 0;
1861  }
1862
1863 /*
1864  * Is driver free?
1865  */
1866  if ((unitNumber <= 0) || (unitNumber > M83xx_TSEC_NIFACES)) {
1867
1868    printk ("Bad TSEC unit number.\n");
1869    return 0;
1870
1871    }
1872
1873  sc = &tsec_driver[unitNumber - 1];
1874  ifp = &sc->arpcom.ac_if;
1875  /*
1876   * add sc to config
1877   */
1878  config->drv_ctrl = sc;
1879
1880  if(ifp->if_softc != NULL) {
1881    printk ("Driver already in use.\n");
1882    return 0;
1883  }
1884
1885  /*
1886   * Process options
1887   */
1888  if(config->hardware_address) {
1889    memcpy(sc->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
1890  }
1891  else {
1892      rtems_panic("TSEC: No Ethernet address specified!\n");
1893  }
1894
1895  sc->rxBdCount       = (config->rbuf_count > 0) ? config->rbuf_count :  RX_BUF_COUNT;
1896  sc->txBdCount       = (config->xbuf_count > 0) ? config->xbuf_count :  TX_BUF_COUNT;
1897  sc->acceptBroadcast = !config->ignore_broadcast;
1898
1899  /* get pointer to TSEC register block */
1900  sc->reg_ptr         = &mpc83xx.tsec[unitNumber-1];
1901
1902  if (svr == 0x80b00010 && pvr == 0x80850010) {
1903    /*
1904     * This is a special case for MPC8313ERDB with silicon revision 1.  Look in
1905     * "MPC8313ECE Rev. 3, 3/2008" errata for "IPIC 1".
1906     */
1907    if (unitNumber == 1) {
1908      sc->irq_num_tx      = 37;
1909      sc->irq_num_rx      = 36;
1910      sc->irq_num_err     = 35;
1911    } else if (unitNumber == 2) {
1912      sc->irq_num_tx      = 34;
1913      sc->irq_num_rx      = 33;
1914      sc->irq_num_err     = 32;
1915    } else {
1916      return 0;
1917    }
1918  } else {
1919    /* get base interrupt number (for Tx irq, Rx=base+1,Err=base+2) */
1920    sc->irq_num_tx      = config->irno + 0;  /* tx  irq number from BSP */
1921    sc->irq_num_rx      = config->irno + 1;  /* rx  irq number from BSP */
1922    sc->irq_num_err     = config->irno + 2;  /* err irq number from BSP */
1923  }
1924
1925  if (config->irno  == 0) {
1926    rtems_panic("TSEC: interupt base number irno not defined");
1927  }
1928  /*
1929   * setup info about mdio interface
1930   */
1931  sc->mdio_info.mdio_r   = mpc83xx_tsec_mdio_read;
1932  sc->mdio_info.mdio_w   = mpc83xx_tsec_mdio_write;
1933  sc->mdio_info.has_gmii = 1; /* we support gigabit IF */
1934
1935  /*
1936   * XXX: Although most hardware builders will assign the PHY addresses
1937   * like this, this should be more configurable
1938   */
1939#ifdef MPC8313ERDB
1940  if (unitNumber == 2) {
1941          sc->phy_default = 4;
1942  } else {
1943          /* TODO */
1944          return 0;
1945  }
1946#else /* MPC8313ERDB */
1947  sc->phy_default = unitNumber-1;
1948#endif /* MPC8313ERDB */
1949
1950 /*
1951  * Set up network interface values
1952  */
1953  ifp->if_softc   = sc;
1954  ifp->if_unit    = unitNumber;
1955  ifp->if_name    = unitName;
1956  ifp->if_mtu     = (config->mtu > 0) ? config->mtu : ETHERMTU;
1957  ifp->if_init    = mpc83xx_tsec_init;
1958  ifp->if_ioctl   = mpc83xx_tsec_ioctl;
1959  ifp->if_start   = mpc83xx_tsec_tx_start;
1960  ifp->if_output  = ether_output;
1961  ifp->if_watchdog =  mpc83xx_tsec_watchdog; /* XXX: timer is set in "init" */
1962
1963  ifp->if_flags   = (config->ignore_broadcast) ? 0 : IFF_BROADCAST;
1964  /*ifp->if_flags   = IFF_BROADCAST | IFF_SIMPLEX;*/
1965
1966  if(ifp->if_snd.ifq_maxlen == 0) {
1967    ifp->if_snd.ifq_maxlen = ifqmaxlen;
1968  }
1969
1970  /*
1971   * Attach the interface
1972   */
1973  if_attach(ifp);
1974
1975  ether_ifattach(ifp);
1976
1977  return 1;
1978}
1979
1980/*=========================================================================*\
1981| Function:                                                                 |
1982\*-------------------------------------------------------------------------*/
1983int rtems_mpc83xx_tsec_driver_attach_detach
1984(
1985/*-------------------------------------------------------------------------*\
1986| Purpose:                                                                  |
1987|   attach or detach the driver                                             |
1988+---------------------------------------------------------------------------+
1989| Input Parameters:                                                         |
1990\*-------------------------------------------------------------------------*/
1991 struct rtems_bsdnet_ifconfig *config, /* interface configuration          */
1992 int attaching                         /* 0 = detach, else attach          */
1993)
1994/*-------------------------------------------------------------------------*\
1995| Return Value:                                                             |
1996|    1, if success                                                       |
1997\*=========================================================================*/
1998{
1999  if (attaching) {
2000    return mpc83xx_tsec_driver_attach(config);
2001  }
2002  else {
2003    return 0;
2004  }
2005}
2006
Note: See TracBrowser for help on using the repository browser.