source: rtems/c/src/lib/libbsp/powerpc/mpc55xxevb/network/smsc9218i.c @ d922dab

4.115
Last change on this file since d922dab was d922dab, checked in by Sebastian Huber <sebastian.huber@…>, on 11/08/11 at 10:13:32

2011-11-08 Sebastian Huber <sebastian.huber@…>

  • make/custom/mpc5566evb_spe.cfg, make/custom/mpc5674fevb_spe.cfg, startup/linkcmds.mpc5566evb_spe, startup/linkcmds.mpc5674fevb_spe, startup/reset.c: New files.
  • Makefile.am, preinstall.am: Reflect changes above.
  • startup/early-init.c: Added missing section attribute.
  • configure.ac, network/smsc9218i.c: New BSP options SMSC9218I_EDMA_RX_CHANNEL and SMSC9218I_EDMA_TX_CHANNEL. Enable reset at cleanup.
  • Property mode set to 100644
File size: 47.7 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup mpc55xx
5 *
6 * @brief SMSC - LAN9218i
7 */
8
9/*
10 * Copyright (c) 2009-2011 embedded brains GmbH.  All rights reserved.
11 *
12 *  embedded brains GmbH
13 *  Obere Lagerstr. 30
14 *  82178 Puchheim
15 *  Germany
16 *  <rtems@embedded-brains.de>
17 *
18 * The license and distribution terms for this file may be
19 * found in the file LICENSE in this distribution or at
20 * http://www.rtems.com/license/LICENSE.
21 *
22 * $Id$
23 */
24
25#define __INSIDE_RTEMS_BSD_TCPIP_STACK__ 1
26#define __BSD_VISIBLE 1
27
28#include <mpc55xx/regs.h>
29
30#include <errno.h>
31#include <assert.h>
32#include <stdlib.h>
33#include <stdio.h>
34#include <string.h>
35#include <inttypes.h>
36
37#include <rtems.h>
38#include <rtems/rtems_bsdnet.h>
39#include <rtems/rtems_mii_ioctl.h>
40
41#include <sys/param.h>
42#include <sys/socket.h>
43#include <sys/sockio.h>
44#include <sys/mbuf.h>
45
46#include <net/if.h>
47#include <net/if_arp.h>
48#include <netinet/in.h>
49#include <netinet/if_ether.h>
50#include <netinet/in_systm.h>
51#include <netinet/ip.h>
52
53#include <mpc55xx/edma.h>
54
55#include <bsp.h>
56#include <bsp/irq.h>
57#include <bsp/utility.h>
58#include <libcpu/powerpc-utility.h>
59#include <bsp/smsc9218i.h>
60
61#if MCLBYTES != 2048
62  #warning "unexpected MCLBYTES value"
63#endif
64
65#define ASSERT_SC(sc) assert((sc) == RTEMS_SUCCESSFUL)
66
67#define SMSC9218I_EVENT_TX RTEMS_EVENT_1
68
69#define SMSC9218I_EVENT_TX_START RTEMS_EVENT_2
70
71#define SMSC9218I_EVENT_TX_ERROR RTEMS_EVENT_3
72
73#define SMSC9218I_EVENT_RX RTEMS_EVENT_4
74
75#define SMSC9218I_EVENT_RX_ERROR RTEMS_EVENT_5
76
77#define SMSC9218I_EVENT_PHY RTEMS_EVENT_6
78
79#define SMSC9218I_EVENT_DMA RTEMS_EVENT_7
80
81#define SMSC9218I_EVENT_DMA_ERROR RTEMS_EVENT_8
82
83/* Adjust by two bytes for proper IP header alignment */
84#define SMSC9218I_RX_DATA_OFFSET 2
85
86#define SMSC9218I_RX_JOBS 32
87
88#define SMSC9218I_TX_JOBS 64
89
90/* Maximum number of fragments per frame, see manual section 3.11.3.2 */
91#define SMSC9218I_TX_FRAGMENT_MAX 86
92
93#if SMSC9218I_TX_JOBS > SMSC9218I_TX_FRAGMENT_MAX
94  #error "too many TX jobs"
95#endif
96
97#define SMSC9218I_IRQ_CFG_GLOBAL_ENABLE \
98  (SMSC9218I_IRQ_CFG_IRQ_EN | SMSC9218I_IRQ_CFG_IRQ_TYPE)
99
100#define SMSC9218I_IRQ_CFG_GLOBAL_DISABLE SMSC9218I_IRQ_CFG_IRQ_TYPE
101
102#define SMSC9218I_EDMA_RX_TCD_CDF 0x10004
103
104#define SMSC9218I_EDMA_RX_TCD_BMF 0x10003
105
106#define SMSC9218I_TCD_BMF_LINK 0x10011
107
108#define SMSC9218I_TCD_BMF_LAST 0x10003
109
110#define SMSC9218I_TCD_BMF_CLEAR 0x10000
111
112#define SMSC9218I_ERROR_INTERRUPTS \
113  (SMSC9218I_INT_TXSO \
114     | SMSC9218I_INT_RWT \
115     | SMSC9218I_INT_RXE \
116     | SMSC9218I_INT_TXE)
117
118#define SMSC9218I_UNLIKELY(x) __builtin_expect((x), 0)
119
120#ifdef DEBUG
121  #define SMSC9218I_PRINTF(...) printf(__VA_ARGS__)
122  #define SMSC9218I_PRINTK(...) printk(__VA_ARGS__)
123#else
124  #define SMSC9218I_PRINTF(...)
125  #define SMSC9218I_PRINTK(...)
126#endif
127
128typedef enum {
129  SMSC9218I_NOT_INITIALIZED,
130  SMSC9218I_CONFIGURED,
131  SMSC9218I_STARTED,
132  SMSC9218I_RUNNING
133} smsc9218i_state;
134
135typedef struct {
136  struct arpcom arpcom;
137  struct rtems_mdio_info mdio;
138  smsc9218i_state state;
139  rtems_id receive_task;
140  rtems_id transmit_task;
141  mpc55xx_edma_channel_entry edma_receive;
142  mpc55xx_edma_channel_entry edma_transmit;
143  unsigned phy_interrupts;
144  unsigned received_frames;
145  unsigned receiver_errors;
146  unsigned receive_interrupts;
147  unsigned receive_dma_interrupts;
148  unsigned receive_too_long_errors;
149  unsigned receive_collision_errors;
150  unsigned receive_crc_errors;
151  unsigned receive_dma_errors;
152  unsigned receive_drop;
153  unsigned receive_watchdog_timeouts;
154  unsigned transmitted_frames;
155  unsigned transmitter_errors;
156  unsigned transmit_interrupts;
157  unsigned transmit_dma_interrupts;
158  unsigned transmit_status_overflows;
159  unsigned transmit_frame_errors;
160  unsigned transmit_dma_errors;
161} smsc9218i_driver_entry;
162
163typedef struct {
164  uint32_t a;
165  uint32_t b;
166} smsc9218i_transmit_command;
167
168typedef struct {
169  struct tcd_t command_tcd_table [SMSC9218I_TX_JOBS];
170  struct tcd_t data_tcd_table [SMSC9218I_TX_JOBS];
171  smsc9218i_transmit_command command_table [SMSC9218I_TX_JOBS];
172  struct mbuf *fragment_table [SMSC9218I_TX_JOBS];
173  struct mbuf *frame;
174  struct mbuf *next_fragment;
175  int empty_index;
176  int transfer_index;
177  int transfer_last_index;
178  int todo_index;
179  int empty;
180  int transfer;
181  int todo;
182  uint32_t frame_length;
183  uint32_t command_b;
184  uint16_t tag;
185  bool done;
186  unsigned frame_compact_count;
187} smsc9218i_transmit_job_control;
188
189typedef struct {
190  struct tcd_t tcd_table [SMSC9218I_RX_JOBS];
191  struct mbuf *mbuf_table [SMSC9218I_RX_JOBS];
192  int consume;
193  int done;
194  int produce;
195} smsc9218i_receive_job_control;
196
197static smsc9218i_receive_job_control smsc_rx_jc __attribute__((aligned (32)));
198
199static void smsc9218i_transmit_dma_done(
200  mpc55xx_edma_channel_entry *channel_entry,
201  uint32_t error_status
202);
203
204static void smsc9218i_receive_dma_done(
205  mpc55xx_edma_channel_entry *e,
206  uint32_t error_status
207);
208
209static smsc9218i_driver_entry smsc9218i_driver_data = {
210  .state = SMSC9218I_NOT_INITIALIZED,
211  .receive_task = RTEMS_ID_NONE,
212  .transmit_task = RTEMS_ID_NONE,
213  .edma_receive = {
214    .channel = SMSC9218I_EDMA_RX_CHANNEL,
215    .done = smsc9218i_receive_dma_done,
216    .id = RTEMS_ID_NONE
217  },
218  .edma_transmit = {
219    .channel = SMSC9218I_EDMA_TX_CHANNEL,
220    .done = smsc9218i_transmit_dma_done,
221    .id = RTEMS_ID_NONE
222  }
223};
224
225static void smsc9218i_mac_wait(volatile smsc9218i_registers *regs)
226{
227  while ((regs->mac_csr_cmd & SMSC9218I_MAC_CSR_CMD_BUSY) != 0) {
228    /* Wait */
229  }
230}
231
232static void smsc9218i_mac_write(
233  volatile smsc9218i_registers *regs,
234  uint32_t address,
235  uint32_t data
236)
237{
238  smsc9218i_mac_wait(regs);
239  regs->mac_csr_data = SMSC9218I_SWAP(data);
240  regs->mac_csr_cmd = SMSC9218I_MAC_CSR_CMD_BUSY
241    | SMSC9218I_MAC_CSR_CMD_ADDR(address);
242  smsc9218i_mac_wait(regs);
243}
244
245static uint32_t smsc9218i_mac_read(
246  volatile smsc9218i_registers *regs,
247  uint32_t address
248)
249{
250  uint32_t mac_csr_data = 0;
251
252  smsc9218i_mac_wait(regs);
253  regs->mac_csr_cmd = SMSC9218I_MAC_CSR_CMD_BUSY
254    | SMSC9218I_MAC_CSR_CMD_READ
255    | SMSC9218I_MAC_CSR_CMD_ADDR(address);
256  smsc9218i_mac_wait(regs);
257  mac_csr_data = regs->mac_csr_data;
258
259  return SMSC9218I_SWAP(mac_csr_data);
260}
261
262static void smsc9218i_phy_wait(volatile smsc9218i_registers *regs)
263{
264  uint32_t mac_mii_acc = 0;
265
266  do {
267    mac_mii_acc = smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_ACC);
268  } while ((mac_mii_acc & SMSC9218I_MAC_MII_ACC_BUSY) != 0);
269}
270
271static void smsc9218i_phy_write(
272  volatile smsc9218i_registers *regs,
273  uint32_t address,
274  uint32_t data
275)
276{
277  smsc9218i_phy_wait(regs);
278  smsc9218i_mac_write(regs, SMSC9218I_MAC_MII_DATA, data);
279  smsc9218i_mac_write(
280    regs,
281    SMSC9218I_MAC_MII_ACC,
282    SMSC9218I_MAC_MII_ACC_PHY_DEFAULT
283      | SMSC9218I_MAC_MII_ACC_BUSY
284      | SMSC9218I_MAC_MII_ACC_WRITE
285      | SMSC9218I_MAC_MII_ACC_ADDR(address)
286  );
287  smsc9218i_phy_wait(regs);
288}
289
290static uint32_t smsc9218i_phy_read(
291  volatile smsc9218i_registers *regs,
292  uint32_t address
293)
294{
295  smsc9218i_phy_wait(regs);
296  smsc9218i_mac_write(
297    regs,
298    SMSC9218I_MAC_MII_ACC,
299    SMSC9218I_MAC_MII_ACC_PHY_DEFAULT
300      | SMSC9218I_MAC_MII_ACC_BUSY
301      | SMSC9218I_MAC_MII_ACC_ADDR(address)
302  );
303  smsc9218i_phy_wait(regs);
304  return smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_DATA);
305}
306
307static void smsc9218i_enable_promiscous_mode(
308  volatile smsc9218i_registers *regs,
309  bool enable
310)
311{
312  uint32_t flags = SMSC9218I_MAC_CR_RXALL | SMSC9218I_MAC_CR_PRMS;
313  uint32_t mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR);
314
315  if (enable) {
316    mac_cr |= flags;
317  } else {
318    mac_cr &= ~flags;
319  }
320
321  smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr);
322}
323
324#if defined(DEBUG)
325static void smsc9218i_register_dump(volatile smsc9218i_registers *regs)
326{
327  uint32_t reg = 0;
328
329  reg = regs->id_rev;
330  printf(
331    "id_rev: 0x%08" PRIx32 " (ID = 0x%04" PRIx32
332      ", revision = 0x%04" PRIx32 ")\n",
333    SMSC9218I_SWAP(reg),
334    SMSC9218I_ID_REV_GET_ID(reg),
335    SMSC9218I_ID_REV_GET_REV(reg)
336  );
337
338  reg = regs->irq_cfg;
339  printf("irq_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
340
341  reg = regs->int_sts;
342  printf("int_sts: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
343
344  reg = regs->int_en;
345  printf("int_en: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
346
347  reg = regs->byte_test;
348  printf("byte_test: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
349
350  reg = regs->fifo_int;
351  printf("fifo_int: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
352
353  reg = regs->rx_cfg;
354  printf("rx_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
355
356  reg = regs->tx_cfg;
357  printf("tx_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
358
359  reg = regs->hw_cfg;
360  printf("hw_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
361
362  reg = regs->rx_dp_ctl;
363  printf("rx_dp_ctl: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
364
365  reg = regs->rx_fifo_inf;
366  printf(
367    "rx_fifo_inf: 0x%08" PRIx32 " (status used = %" PRIu32
368      ", data used = %" PRIu32 ")\n",
369    SMSC9218I_SWAP(reg),
370    SMSC9218I_RX_FIFO_INF_GET_SUSED(reg),
371    SMSC9218I_RX_FIFO_INF_GET_DUSED(reg)
372  );
373
374  reg = regs->tx_fifo_inf;
375  printf(
376    "tx_fifo_inf: 0x%08" PRIx32 " (status unused = %" PRIu32
377      ", free = %" PRIu32 ")\n",
378    SMSC9218I_SWAP(reg),
379    SMSC9218I_TX_FIFO_INF_GET_SUSED(reg),
380    SMSC9218I_TX_FIFO_INF_GET_FREE(reg)
381  );
382
383  reg = regs->pmt_ctrl;
384  printf("pmt_ctrl: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
385
386  reg = regs->gpio_cfg;
387  printf("gpio_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
388
389  reg = regs->gpt_cfg;
390  printf("gpt_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
391
392  reg = regs->gpt_cnt;
393  printf("gpt_cnt: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
394
395  reg = regs->word_swap;
396  printf("word_swap: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
397
398  reg = regs->free_run;
399  printf("free_run: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
400
401  reg = regs->rx_drop;
402  printf("rx_drop: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
403
404  reg = regs->afc_cfg;
405  printf("afc_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg));
406
407  printf("mac: cr: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_CR));
408  printf("mac: addrh: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRH));
409  printf("mac: addrl: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRL));
410  printf("mac: hashh: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_HASHH));
411  printf("mac: hashl: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_HASHL));
412  printf("mac: mii_acc: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_ACC));
413  printf("mac: mii_data: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_DATA));
414  printf("mac: flow: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_FLOW));
415  printf("mac: vlan1: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_VLAN1));
416  printf("mac: vlan2: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_VLAN2));
417  printf("mac: wuff: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_WUFF));
418  printf("mac: wucsr: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_WUCSR));
419
420  printf("phy: bcr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_BMCR));
421  printf("phy: bsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_BMSR));
422  printf("phy: id1: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_PHYIDR1));
423  printf("phy: id2: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_PHYIDR2));
424  printf("phy: anar: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANAR));
425  printf("phy: anlpar: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANLPAR));
426  printf("phy: anexpr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANER));
427  printf("phy: mcsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_MCSR));
428  printf("phy: spmodes: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_SPMODES));
429  printf("phy: cisr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_CSIR));
430  printf("phy: isr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_ISR));
431  printf("phy: imr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_IMR));
432  printf("phy: physcsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_PHYSCSR));
433}
434#endif
435
436static void smsc9218i_flush_tcd(struct tcd_t *tcd)
437{
438  ppc_data_cache_block_store(tcd);
439}
440
441static uint32_t smsc9218i_align_up(uint32_t val)
442{
443  return 4U + ((val - 1U) & ~0x3U);
444}
445
446static void smsc9218i_discard_frame(
447  smsc9218i_driver_entry *e,
448  volatile smsc9218i_registers *regs,
449  uint32_t rx_fifo_status,
450  uint32_t frame_length,
451  uint32_t data_length
452)
453{
454  /* Update error counters */
455  if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_TOO_LONG) != 0) {
456    ++e->receive_too_long_errors;
457  }
458  if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_COLLISION) != 0) {
459    ++e->receive_collision_errors;
460  }
461  if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_CRC) != 0) {
462    ++e->receive_crc_errors;
463  }
464
465  /* Discard frame */
466  if (frame_length > 16) {
467    /* Fast forward */
468    regs->rx_dp_ctl = SMSC9218I_RX_DP_CTRL_FFWD;
469
470    while ((regs->rx_dp_ctl & SMSC9218I_RX_DP_CTRL_FFWD) != 0) {
471      /* Wait */
472    }
473  } else {
474    uint32_t len = data_length / 4;
475    uint32_t i = 0;
476
477    /* Discard data */
478    for (i = 0; i < len; ++i) {
479      regs->rx_fifo_data;
480    }
481  }
482}
483
484static void smsc9218i_setup_receive_dma(
485  smsc9218i_driver_entry *e,
486  volatile smsc9218i_registers *regs,
487  smsc9218i_receive_job_control *jc
488)
489{
490  int c = jc->consume;
491  int p = jc->produce;
492  int np = (p + 1) % SMSC9218I_RX_JOBS;
493  struct tcd_t *first = &jc->tcd_table [p];
494  struct tcd_t *last = NULL;
495
496  while (np != c) {
497    uint32_t rx_fifo_inf = 0;
498    uint32_t status_used = 0;
499
500    /* Clear FIFO level status */
501    regs->int_sts = SMSC9218I_INT_RSFL;
502
503    /* Next FIFO status */
504    rx_fifo_inf = regs->rx_fifo_inf;
505    status_used = SMSC9218I_RX_FIFO_INF_GET_SUSED(rx_fifo_inf);
506
507    if (status_used > 0) {
508      uint32_t status = regs->rx_fifo_status;
509      uint32_t frame_length = SMSC9218I_RX_STS_GET_LENGTH(status);
510      uint32_t data_length = smsc9218i_align_up(
511        SMSC9218I_RX_DATA_OFFSET + frame_length
512      );
513      bool frame_ok = (status & SMSC9218I_RX_STS_ERROR) == 0;
514
515      if (frame_ok) {
516        struct mbuf *m = jc->mbuf_table [p];
517        int mbuf_length = (int) frame_length - ETHER_HDR_LEN - ETHER_CRC_LEN;
518        struct tcd_t *current = &jc->tcd_table [p];
519
520        m->m_len = mbuf_length;
521        m->m_pkthdr.len = mbuf_length;
522
523        current->NBYTES = data_length;
524        smsc9218i_flush_tcd(current);
525
526        last = current;
527        p = np;
528        np = (p + 1) % SMSC9218I_RX_JOBS;
529      } else {
530        smsc9218i_discard_frame(e, regs, status, frame_length, data_length);
531      }
532    } else {
533      break;
534    }
535  }
536
537  jc->produce = p;
538
539  if (last != NULL) {
540    volatile struct tcd_t *channel = &EDMA.TCD [e->edma_receive.channel];
541
542    /* Setup last TCD */
543    last->BMF.R = SMSC9218I_TCD_BMF_LAST;
544    smsc9218i_flush_tcd(last);
545    ppc_synchronize_data();
546
547    /* Start eDMA transfer */
548    channel->SADDR = first->SADDR;
549    channel->SDF.R = first->SDF.R;
550    channel->NBYTES = first->NBYTES;
551    channel->SLAST = first->SLAST;
552    channel->DADDR = first->DADDR;
553    channel->CDF.R = first->CDF.R;
554    channel->DLAST_SGA = first->DLAST_SGA;
555    channel->BMF.R = SMSC9218I_TCD_BMF_CLEAR;
556    channel->BMF.R = first->BMF.R;
557  }
558}
559
560static void smsc9218i_receive_dma_done(
561  mpc55xx_edma_channel_entry *channel_entry,
562  uint32_t error_status
563)
564{
565  rtems_status_code sc = RTEMS_SUCCESSFUL;
566  smsc9218i_driver_entry *e = &smsc9218i_driver_data;
567  volatile smsc9218i_registers *const regs = smsc9218i;
568  smsc9218i_receive_job_control *jc = &smsc_rx_jc;
569
570  SMSC9218I_PRINTK(
571    "edma: id = 0x%08x, error status = 0x%08x\n",
572    channel_entry->id,
573    error_status
574  );
575
576  ++e->receive_dma_interrupts;
577  if (SMSC9218I_UNLIKELY(error_status != 0)) {
578    ++e->receive_dma_errors;
579  }
580
581  sc = rtems_event_send(channel_entry->id, SMSC9218I_EVENT_DMA);
582  ASSERT_SC(sc);
583
584  jc->done = jc->produce;
585
586  smsc9218i_setup_receive_dma(e, regs, jc);
587}
588
589static void smsc9218i_transmit_dma_done(
590  mpc55xx_edma_channel_entry *channel_entry,
591  uint32_t error_status
592)
593{
594  rtems_status_code sc = RTEMS_SUCCESSFUL;
595  smsc9218i_driver_entry *e = &smsc9218i_driver_data;
596  rtems_event_set event = error_status == 0 ?
597    SMSC9218I_EVENT_DMA : SMSC9218I_EVENT_DMA_ERROR;
598
599  SMSC9218I_PRINTK(
600    "edma: id = 0x%08x, error status = 0x%08x\n",
601    channel_entry->id,
602    error_status
603  );
604
605  ++e->transmit_dma_interrupts;
606
607  sc = rtems_event_send(channel_entry->id, event);
608  ASSERT_SC(sc);
609}
610
611static void smsc9218i_interrupt_handler(void *arg)
612{
613  rtems_status_code sc = RTEMS_SUCCESSFUL;
614  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
615  volatile smsc9218i_registers *const regs = smsc9218i;
616  uint32_t int_en = regs->int_en;
617  uint32_t int_sts = regs->int_sts & int_en;
618  #ifdef DEBUG
619    uint32_t irq_cfg = regs->irq_cfg;
620  #endif
621
622  /* Get interrupt status */
623  SMSC9218I_PRINTK(
624    "interrupt: irq_cfg = 0x%08x, int_sts = 0x%08x, int_en = 0x%08x\n",
625    SMSC9218I_SWAP(irq_cfg),
626    SMSC9218I_SWAP(int_sts),
627    SMSC9218I_SWAP(int_en)
628  );
629
630  /* Disable module interrupts */
631  regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_DISABLE;
632
633  /* Clear external interrupt status */
634  SIU.EISR.R = 1;
635
636  /* Clear interrupts */
637  regs->int_sts = int_sts;
638
639  /* Error interrupts */
640  if (SMSC9218I_UNLIKELY((int_sts & SMSC9218I_ERROR_INTERRUPTS) != 0)) {
641    if ((int_sts & SMSC9218I_INT_TXSO) != 0) {
642      ++e->transmit_status_overflows;
643    }
644    if ((int_sts & SMSC9218I_INT_RWT) != 0) {
645      ++e->receive_watchdog_timeouts;
646    }
647    if ((int_sts & SMSC9218I_INT_RXE) != 0) {
648      ++e->receiver_errors;
649    }
650    if ((int_sts & SMSC9218I_INT_TXE) != 0) {
651      ++e->transmitter_errors;
652    }
653  }
654
655  /* Check receive interrupts */
656  if ((int_sts & SMSC9218I_INT_RSFL) != 0) {
657    smsc9218i_receive_job_control *jc = &smsc_rx_jc;
658
659    int_en &= ~SMSC9218I_INT_RSFL;
660    ++e->receive_interrupts;
661    smsc9218i_setup_receive_dma(e, regs, jc);
662  }
663
664  /* Check PHY interrupts */
665  if (SMSC9218I_UNLIKELY((int_sts & SMSC9218I_INT_PHY) != 0)) {
666    SMSC9218I_PRINTK("interrupt: phy\n");
667    int_en &= ~SMSC9218I_INT_PHY;
668    ++e->phy_interrupts;
669    sc = rtems_event_send(e->receive_task, SMSC9218I_EVENT_PHY);
670    ASSERT_SC(sc);
671  }
672
673  /* Check transmit interrupts */
674  if ((int_sts & SMSC9218I_INT_TDFA) != 0) {
675    SMSC9218I_PRINTK("interrupt: transmit\n");
676    int_en &= ~SMSC9218I_INT_TDFA;
677    ++e->transmit_interrupts;
678    sc = rtems_event_send(e->transmit_task, SMSC9218I_EVENT_TX);
679    ASSERT_SC(sc);
680  }
681
682  /* Update interrupt enable */
683  regs->int_en = int_en;
684
685  /* Enable module interrupts */
686  regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_ENABLE;
687}
688
689static void smsc9218i_enable_transmit_interrupts(
690  volatile smsc9218i_registers *regs
691)
692{
693  rtems_interrupt_level level;
694
695  rtems_interrupt_disable(level);
696  regs->int_en |= SMSC9218I_INT_TDFA;
697  rtems_interrupt_enable(level);
698}
699
700static void smsc9218i_enable_phy_interrupts(
701  volatile smsc9218i_registers *regs
702)
703{
704  rtems_interrupt_level level;
705
706  rtems_interrupt_disable(level);
707  regs->int_en |= SMSC9218I_INT_PHY;
708  rtems_interrupt_enable(level);
709}
710
711static void smsc9218i_phy_clear_interrupts(
712  volatile smsc9218i_registers *regs
713)
714{
715  smsc9218i_phy_read(regs, SMSC9218I_PHY_ISR);
716}
717
718static bool smsc9218i_media_status(smsc9218i_driver_entry *e, int *media)
719{
720  struct ifnet *ifp = &e->arpcom.ac_if;
721
722  *media = IFM_MAKEWORD(0, 0, 0, SMSC9218I_MAC_MII_ACC_PHY_DEFAULT);
723
724  return (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t) media) == 0;
725}
726
727static void smsc9218i_media_status_change(
728  smsc9218i_driver_entry *e,
729  volatile smsc9218i_registers *regs
730)
731{
732  int media = 0;
733  bool media_ok = false;
734  uint32_t mac_cr = 0;
735
736  smsc9218i_phy_clear_interrupts(regs);
737  smsc9218i_enable_phy_interrupts(regs);
738
739  media_ok = smsc9218i_media_status(e, &media);
740  mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR);
741  if (media_ok && (IFM_OPTIONS(media) & IFM_FDX) == 0) {
742    mac_cr &= ~SMSC9218I_MAC_CR_FDPX;
743  } else {
744    mac_cr |= SMSC9218I_MAC_CR_FDPX;
745  }
746  smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr);
747}
748
749static void smsc9218i_new_mbuf(
750  struct ifnet *ifp,
751  smsc9218i_receive_job_control *jc,
752  int i
753)
754{
755  struct mbuf *m = m_gethdr(M_WAIT, MT_DATA);
756  struct tcd_t *tcd = &jc->tcd_table [i];
757  char *data = NULL;
758
759  m->m_pkthdr.rcvif = ifp;
760  MCLGET(m, M_WAIT);
761
762  data = mtod(m, char *);
763  m->m_data = data + SMSC9218I_RX_DATA_OFFSET + ETHER_HDR_LEN;
764
765  jc->mbuf_table [i] = m;
766
767  tcd->DADDR = (uint32_t) data;
768  tcd->BMF.R = SMSC9218I_TCD_BMF_LINK;
769
770  /* FIXME: This is maybe a problem in case of a lot of small frames */
771  rtems_cache_invalidate_multiple_data_lines(
772    data,
773    SMSC9218I_RX_DATA_OFFSET + ETHER_HDR_LEN + ETHERMTU + ETHER_CRC_LEN
774  );
775}
776
777static void smsc9218i_init_receive_jobs(
778  smsc9218i_driver_entry *e,
779  volatile smsc9218i_registers *regs,
780  smsc9218i_receive_job_control *jc
781)
782{
783  rtems_status_code sc = RTEMS_SUCCESSFUL;
784  struct ifnet *ifp = &e->arpcom.ac_if;
785  int i = 0;
786
787  /* Obtain receive eDMA channel */
788  e->edma_receive.id = e->receive_task;
789  sc = mpc55xx_edma_obtain_channel(
790    &e->edma_receive,
791    MPC55XX_INTC_DEFAULT_PRIORITY
792  );
793  ASSERT_SC(sc);
794
795  for (i = 0; i < SMSC9218I_RX_JOBS; ++i) {
796    struct tcd_t *tcd = &jc->tcd_table [i];
797    struct tcd_t *next_tcd = &jc->tcd_table [(i + 1) % SMSC9218I_RX_JOBS];
798
799    tcd->SADDR = (uint32_t) &regs->rx_fifo_data;
800    tcd->SDF.B.SSIZE = 0x2;
801    tcd->SDF.B.DSIZE = 0x2;
802    tcd->CDF.B.CITER = 1;
803    tcd->CDF.B.DOFF = 4;
804    tcd->DLAST_SGA = (int32_t) next_tcd;
805
806    smsc9218i_new_mbuf(ifp, jc, i);
807  }
808}
809
810static void smsc9218i_ether_input(
811  smsc9218i_driver_entry *e,
812  volatile smsc9218i_registers *regs,
813  smsc9218i_receive_job_control *jc
814)
815{
816  rtems_interrupt_level level;
817  struct ifnet *ifp = &e->arpcom.ac_if;
818  int c = jc->consume;
819  int d = jc->done;
820
821  while (c != d) {
822    struct mbuf *m = jc->mbuf_table [c];
823    struct ether_header *eh = (struct ether_header *)
824      (mtod(m, char *) - ETHER_HDR_LEN);
825
826    ++e->received_frames;
827    ether_input(ifp, eh, m);
828    smsc9218i_new_mbuf(ifp, jc, c);
829
830    c = (c + 1) % SMSC9218I_RX_JOBS;
831  }
832
833  jc->consume = c;
834
835  rtems_interrupt_disable(level);
836  /* Enabling the receive interrupts while the DMA is active leads to chaos */
837  if (c == jc->produce) {
838    regs->int_en |= SMSC9218I_INT_RSFL;
839  }
840  rtems_interrupt_enable(level);
841}
842
843static void smsc9218i_receive_task(void *arg)
844{
845  rtems_status_code sc = RTEMS_SUCCESSFUL;
846  rtems_interrupt_level level;
847  smsc9218i_receive_job_control *jc = &smsc_rx_jc;
848  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
849  volatile smsc9218i_registers *const regs = smsc9218i;
850  uint32_t mac_cr = 0;
851
852  smsc9218i_init_receive_jobs(e, regs, jc);
853
854  /* Configure receiver */
855  regs->rx_cfg = SMSC9218I_RX_CFG_END_ALIGN_4
856    | SMSC9218I_RX_CFG_DOFF(SMSC9218I_RX_DATA_OFFSET);
857
858  /* Enable MAC receiver */
859  mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR);
860  mac_cr |= SMSC9218I_MAC_CR_RXEN;
861  smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr);
862
863  /* Enable receive interrupts */
864  rtems_interrupt_disable(level);
865  regs->int_en |= SMSC9218I_INT_RSFL;
866  rtems_interrupt_enable(level);
867
868  /* Enable PHY interrupts */
869  smsc9218i_phy_write(
870    regs,
871    SMSC9218I_PHY_IMR,
872    SMSC9218I_PHY_IMR_INT4 | SMSC9218I_PHY_IMR_INT6
873  );
874  smsc9218i_enable_phy_interrupts(regs);
875
876  while (true) {
877    rtems_event_set events;
878
879    sc = rtems_bsdnet_event_receive(
880      SMSC9218I_EVENT_DMA | SMSC9218I_EVENT_PHY,
881      RTEMS_EVENT_ANY | RTEMS_WAIT,
882      RTEMS_NO_TIMEOUT,
883      &events
884    );
885    ASSERT_SC(sc);
886
887    if ((events & SMSC9218I_EVENT_DMA) != 0) {
888      smsc9218i_ether_input(e, regs, jc);
889    }
890
891    if ((events & SMSC9218I_EVENT_PHY) != 0) {
892      smsc9218i_media_status_change(e, regs);
893    }
894  }
895}
896
897#if defined(DEBUG)
898static void smsc9218i_transmit_job_dump(
899  smsc9218i_transmit_job_control *jc,
900  const char *msg
901)
902{
903  char out [SMSC9218I_TX_JOBS + 1];
904  int c = 0;
905  int s = 0;
906
907  out [SMSC9218I_TX_JOBS] = '\0';
908
909  memset(out, '?', SMSC9218I_TX_JOBS);
910
911  c = jc->todo_index;
912  s = jc->empty;
913  while (s > 0) {
914    out [c] = 'E';
915    --s;
916    c = (c + 1) % SMSC9218I_TX_JOBS;
917  }
918
919  c = jc->transfer_index;
920  s = jc->todo;
921  while (s > 0) {
922    out [c] = 'T';
923    --s;
924    c = (c + 1) % SMSC9218I_TX_JOBS;
925  }
926
927  c = jc->empty_index;
928  s = jc->transfer;
929  while (s > 0) {
930    out [c] = 'D';
931    --s;
932    c = (c + 1) % SMSC9218I_TX_JOBS;
933  }
934
935  printf(
936    "tx: %s: %02u:%02u:%02u %s\n",
937    msg,
938    jc->empty,
939    jc->todo,
940    jc->transfer,
941    out
942  );
943}
944#endif /* defined(DEBUG) */
945
946static struct mbuf *smsc9218i_compact_frame(
947  smsc9218i_transmit_job_control *jc,
948  uint32_t frame_length
949)
950{
951  struct mbuf *old_m = jc->frame;
952  struct mbuf *new_m = m_gethdr(M_WAIT, MT_DATA);
953  char *data = NULL;
954
955  ++jc->frame_compact_count;
956
957  MCLGET(new_m, M_WAIT);
958  data = mtod(new_m, char *);
959
960  new_m->m_len = (int) frame_length;
961  new_m->m_pkthdr.len = (int) frame_length;
962
963  while (old_m != NULL) {
964    size_t len = (size_t) old_m->m_len;
965    memcpy(data, mtod(old_m, void *), len);
966    data += len;
967    old_m = m_free(old_m);
968  }
969
970  jc->frame = new_m;
971
972  return new_m;
973}
974
975static struct mbuf *smsc9218i_next_transmit_fragment(
976  struct ifnet *ifp,
977  smsc9218i_transmit_job_control *jc
978)
979{
980  struct mbuf *m = jc->next_fragment;
981
982  if (m != NULL) {
983    /* Next fragment is from the current frame */
984    jc->next_fragment = m->m_next;
985  } else {
986    /* Dequeue first fragment of the next frame */
987    IF_DEQUEUE(&ifp->if_snd, m);
988
989    /* Update frame head */
990    jc->frame = m;
991
992    if (m != NULL) {
993      struct mbuf *n = m;
994      struct mbuf *p = NULL;
995      uint32_t frame_length = 0;
996      unsigned fragments = 0;
997      bool tiny = false;
998
999      /* Calculate frame length and fragment number */
1000      do {
1001        int len = n->m_len;
1002
1003        if (len > 0) {
1004          ++fragments;
1005          frame_length += (uint32_t) len;
1006          tiny = tiny || len < 4;
1007
1008          /* Next fragment */
1009          p = n;
1010          n = n->m_next;
1011        } else {
1012          /* Discard empty fragment and get next */
1013          n = m_free(n);
1014
1015          /* Remove fragment from list */
1016          if (p != NULL) {
1017            p->m_next = n;
1018          } else {
1019            jc->frame = n;
1020          }
1021        }
1022      } while (n != NULL);
1023
1024      if (SMSC9218I_UNLIKELY(tiny || fragments > SMSC9218I_TX_JOBS)) {
1025        fragments = 1;
1026        m = smsc9218i_compact_frame(jc, frame_length);
1027      }
1028
1029      /* Set frame length */
1030      jc->frame_length = frame_length;
1031
1032      /* Update next fragment */
1033      jc->next_fragment = jc->frame->m_next;
1034
1035      /* Update tag */
1036      ++jc->tag;
1037
1038      /* Command B */
1039      jc->command_b = ((uint32_t) SMSC9218I_TX_B_TAG(jc->tag))
1040        | SMSC9218I_TX_B_FRAME_LENGTH(jc->frame_length);
1041
1042      SMSC9218I_PRINTF(
1043        "tx: next frame: tag = %i, frame length = %" PRIu32
1044          ", fragments = %u\n",
1045        jc->tag,
1046        frame_length,
1047        fragments
1048      );
1049    } else {
1050      /* The transmit queue is empty, we have no next fragment */
1051      jc->next_fragment = NULL;
1052
1053      /* Interface is now inactive */
1054      ifp->if_flags &= ~IFF_OACTIVE;
1055
1056      /* Transmit task may wait for events */
1057      jc->done = true;
1058
1059      SMSC9218I_PRINTF("tx: inactive\n");
1060    }
1061  }
1062
1063  return m;
1064}
1065
1066static void smsc9218i_transmit_create_jobs(
1067  smsc9218i_transmit_job_control *jc,
1068  volatile smsc9218i_registers *const regs,
1069  struct ifnet *ifp
1070)
1071{
1072  int n = jc->empty;
1073
1074  if (n > 0) {
1075    int c = jc->todo_index;
1076    int i = 0;
1077
1078    #ifdef DEBUG
1079      smsc9218i_transmit_job_dump(jc, "create");
1080    #endif
1081
1082    for (i = 0; i < n; ++i) {
1083      struct mbuf *m = smsc9218i_next_transmit_fragment(ifp, jc);
1084
1085      if (m != NULL) {
1086        uint32_t first = m == jc->frame ? SMSC9218I_TX_A_FIRST : 0;
1087        uint32_t last = m->m_next == NULL ? SMSC9218I_TX_A_LAST : 0;
1088        uint32_t fragment_length = (uint32_t) m->m_len;
1089        uint32_t fragment_misalign = (uint32_t) (mtod(m, uintptr_t) % 4);
1090        uint32_t data_length = fragment_misalign + fragment_length;
1091        uint32_t data_misalign = data_length % 4;
1092        uint32_t data = (uint32_t) (mtod(m, char *) - fragment_misalign);
1093
1094        /* Align data length on four byte boundary */
1095        if (data_misalign > 0) {
1096          data_length += 4 - data_misalign;
1097        }
1098
1099        /* Cache flush for fragment data */
1100        rtems_cache_flush_multiple_data_lines(
1101          (const void *) data,
1102          data_length
1103        );
1104
1105        /* Remember fragement */
1106        jc->fragment_table [c] = m;
1107
1108        /* Command A and B */
1109        jc->command_table [c].a = SMSC9218I_TX_A_END_ALIGN_4
1110          | SMSC9218I_TX_A_DOFF(fragment_misalign)
1111          | SMSC9218I_TX_A_FRAGMENT_LENGTH(fragment_length)
1112          | first
1113          | last;
1114        jc->command_table [c].b = jc->command_b;
1115
1116        /* Data TCD */
1117        jc->data_tcd_table [c].SADDR = data;
1118        jc->data_tcd_table [c].NBYTES = data_length;
1119
1120        SMSC9218I_PRINTF("tx: create: index = %u\n", c);
1121      } else {
1122        /* Nothing to do */
1123        break;
1124      }
1125
1126      c = (c + 1) % SMSC9218I_TX_JOBS;
1127    }
1128
1129    /* Increment jobs to do */
1130    jc->todo += i;
1131
1132    /* Decrement empty count */
1133    jc->empty -= i;
1134
1135    /* Update todo index */
1136    jc->todo_index = c;
1137
1138    #ifdef DEBUG
1139      smsc9218i_transmit_job_dump(jc, "create done");
1140    #endif
1141  } else {
1142    /* Transmit task may wait for events */
1143    jc->done = true;
1144  }
1145}
1146
1147static void smsc9218i_transmit_do_jobs(
1148  smsc9218i_transmit_job_control *jc,
1149  volatile smsc9218i_registers *const regs,
1150  smsc9218i_driver_entry *e
1151)
1152{
1153  if (jc->transfer == 0) {
1154    uint32_t tx_fifo_inf = regs->tx_fifo_inf;
1155    uint32_t data_free = SMSC9218I_TX_FIFO_INF_GET_FREE(tx_fifo_inf);
1156    int c = jc->transfer_index;
1157    int last_index = c;
1158    int i = 0;
1159    int n = jc->todo;
1160
1161    #ifdef DEBUG
1162      smsc9218i_transmit_job_dump(jc, "transfer");
1163    #endif
1164
1165    for (i = 0; i < n; ++i) {
1166      struct tcd_t *tcd = &jc->data_tcd_table [c];
1167      uint32_t data_length = tcd->NBYTES + 14;
1168
1169      if (data_length <= data_free) {
1170        /* Reduce free FIFO space */
1171        data_free -= data_length;
1172
1173        /* Index of last TCD */
1174        last_index = c;
1175
1176        /* Cache flush for data TCD */
1177        smsc9218i_flush_tcd(tcd);
1178      } else {
1179        break;
1180      }
1181
1182      c = (c + 1) % SMSC9218I_TX_JOBS;
1183    }
1184
1185    if (i > 0) {
1186      volatile struct tcd_t *channel = &EDMA.TCD [e->edma_transmit.channel];
1187      struct tcd_t *start = &jc->command_tcd_table [jc->transfer_index];
1188      struct tcd_t *last = &jc->data_tcd_table [last_index];
1189
1190      /* New transfer index */
1191      jc->transfer_index = c;
1192
1193      /* New last transfer index */
1194      jc->transfer_last_index = last_index;
1195
1196      /* Set jobs in transfer */
1197      jc->transfer = i;
1198
1199      /* Decrement jobs to do */
1200      jc->todo -= i;
1201
1202      /* Cache flush for command table */
1203      rtems_cache_flush_multiple_data_lines(
1204        jc->command_table,
1205        sizeof(jc->command_table)
1206      );
1207
1208      /* Enable interrupt for last data TCD */
1209      last->BMF.R = SMSC9218I_TCD_BMF_LAST;
1210      smsc9218i_flush_tcd(last);
1211      ppc_synchronize_data();
1212
1213      /* Start eDMA transfer */
1214      channel->SADDR = start->SADDR;
1215      channel->SDF.R = start->SDF.R;
1216      channel->NBYTES = start->NBYTES;
1217      channel->SLAST = start->SLAST;
1218      channel->DADDR = start->DADDR;
1219      channel->CDF.R = start->CDF.R;
1220      channel->DLAST_SGA = start->DLAST_SGA;
1221      channel->BMF.R = SMSC9218I_TCD_BMF_CLEAR;
1222      channel->BMF.R = start->BMF.R;
1223
1224      /* Transmit task may wait for events */
1225      jc->done = true;
1226    } else if (n > 0) {
1227      /* We need to wait until the FIFO has more free space */
1228      smsc9218i_enable_transmit_interrupts(regs);
1229
1230      /* Transmit task may wait for events */
1231      jc->done = true;
1232    }
1233
1234    #ifdef DEBUG
1235      smsc9218i_transmit_job_dump(jc, "transfer done");
1236    #endif
1237  }
1238}
1239
1240static void smsc9218i_transmit_finish_jobs(
1241  smsc9218i_transmit_job_control *jc,
1242  volatile smsc9218i_registers *const regs,
1243  smsc9218i_driver_entry *e,
1244  rtems_event_set events
1245)
1246{
1247  uint32_t tx_fifo_inf = regs->tx_fifo_inf;
1248  uint32_t status_used = SMSC9218I_TX_FIFO_INF_GET_SUSED(tx_fifo_inf);
1249  uint32_t s = 0;
1250  int n = jc->transfer;
1251
1252  for (s = 0; s < status_used; ++s) {
1253    uint32_t tx_fifo_status = regs->tx_fifo_status;
1254
1255    if ((tx_fifo_status & SMSC9218I_TX_STS_ERROR) == 0) {
1256      ++e->transmitted_frames;
1257    } else {
1258      ++e->transmit_frame_errors;
1259    }
1260
1261    SMSC9218I_PRINTF(
1262      "tx: transmit status: tag = %" PRIu32 ", status = 0x%08" PRIx32 "\n",
1263      SMSC9218I_TX_STS_GET_TAG(tx_fifo_status),
1264      SMSC9218I_SWAP(tx_fifo_status)
1265    );
1266  }
1267
1268  if (
1269    (events & (SMSC9218I_EVENT_DMA | SMSC9218I_EVENT_DMA_ERROR)) != 0
1270      && n > 0
1271  ) {
1272    int c = jc->empty_index;
1273    int i = 0;
1274
1275    #ifdef DEBUG
1276      smsc9218i_transmit_job_dump(jc, "finish");
1277    #endif
1278
1279    if ((events & SMSC9218I_EVENT_DMA_ERROR) != 0) {
1280      ++e->transmit_dma_errors;
1281    }
1282
1283    /* Restore last data TCD */
1284    jc->data_tcd_table [jc->transfer_last_index].BMF.R =
1285      SMSC9218I_TCD_BMF_LINK;
1286
1287    for (i = 0; i < n; ++i) {
1288      /* Free fragment buffer */
1289      m_free(jc->fragment_table [c]);
1290
1291      c = (c + 1) % SMSC9218I_TX_JOBS;
1292    }
1293
1294    /* Increment empty count */
1295    jc->empty += n;
1296
1297    /* New empty index */
1298    jc->empty_index = jc->transfer_index;
1299
1300    /* Clear jobs in transfer */
1301    jc->transfer = 0;
1302
1303    /* Update empty index */
1304    jc->empty_index = c;
1305
1306    #ifdef DEBUG
1307      smsc9218i_transmit_job_dump(jc, "finish done");
1308    #endif
1309  }
1310}
1311
1312/* FIXME */
1313static smsc9218i_transmit_job_control smsc_tx_jc __attribute__((aligned (32))) = {
1314  .frame = NULL,
1315  .next_fragment = NULL,
1316  .frame_length = 0,
1317  .tag = 0,
1318  .command_b = 0,
1319  .done = false,
1320  .empty_index = 0,
1321  .transfer_index = 0,
1322  .todo_index = 0,
1323  .empty = SMSC9218I_TX_JOBS,
1324  .transfer = 0,
1325  .todo = 0
1326};
1327
1328static void smsc9218i_transmit_task(void *arg)
1329{
1330  rtems_status_code sc = RTEMS_SUCCESSFUL;
1331  rtems_event_set events = 0;
1332  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
1333  struct ifnet *ifp = &e->arpcom.ac_if;
1334  volatile smsc9218i_registers *const regs = smsc9218i;
1335  uint32_t mac_cr = 0;
1336  smsc9218i_transmit_job_control *jc = &smsc_tx_jc;
1337  unsigned i = 0;
1338
1339  SMSC9218I_PRINTF("%s\n", __func__);
1340
1341  /* Obtain transmit eDMA channel */
1342  e->edma_transmit.id = e->transmit_task;
1343  sc = mpc55xx_edma_obtain_channel(
1344    &e->edma_transmit,
1345    MPC55XX_INTC_DEFAULT_PRIORITY
1346  );
1347  ASSERT_SC(sc);
1348
1349  /* Setup transmit eDMA descriptors */
1350  for (i = 0; i < SMSC9218I_TX_JOBS; ++i) {
1351    unsigned ii = (i + 1) % SMSC9218I_TX_JOBS;
1352    struct tcd_t tcd = EDMA_TCD_DEFAULT;
1353    struct tcd_t *command_tcd = &jc->command_tcd_table [i];
1354    struct tcd_t *data_tcd = &jc->data_tcd_table [i];
1355    struct tcd_t *next_command_tcd = &jc->command_tcd_table [ii];
1356
1357    tcd.SDF.B.SSIZE = 2;
1358    tcd.SDF.B.SOFF = 4;
1359    tcd.SDF.B.DSIZE = 2;
1360    tcd.CDF.B.CITER = 1;
1361    tcd.BMF.R = SMSC9218I_TCD_BMF_LINK;
1362    tcd.DADDR = (uint32_t) &regs->tx_fifo_data;
1363
1364    tcd.DLAST_SGA = (int32_t) next_command_tcd;
1365    *data_tcd = tcd;
1366
1367    tcd.SADDR = (uint32_t) &jc->command_table [i].a;
1368    tcd.NBYTES = 8;
1369    tcd.DLAST_SGA = (int32_t) data_tcd;
1370    *command_tcd = tcd;
1371  }
1372
1373  /*
1374   * Cache flush for command TCD.  The content of the command TCD remains
1375   * invariant.
1376   */
1377  rtems_cache_flush_multiple_data_lines(
1378    jc->command_tcd_table,
1379    sizeof(jc->command_tcd_table)
1380  );
1381
1382  /* Configure transmitter */
1383  regs->tx_cfg = SMSC9218I_TX_CFG_SAO | SMSC9218I_TX_CFG_ON;
1384
1385  /* Enable MAC transmitter */
1386  mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR) | SMSC9218I_MAC_CR_TXEN;
1387  smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr);
1388
1389  /* Main event loop */
1390  while (true) {
1391    /* Wait for events */
1392    sc = rtems_bsdnet_event_receive(
1393      SMSC9218I_EVENT_TX
1394        | SMSC9218I_EVENT_TX_START
1395        | SMSC9218I_EVENT_DMA
1396        | SMSC9218I_EVENT_DMA_ERROR,
1397      RTEMS_EVENT_ANY | RTEMS_WAIT,
1398      RTEMS_NO_TIMEOUT,
1399      &events
1400    );
1401    ASSERT_SC(sc);
1402
1403    SMSC9218I_PRINTF("tx: wake up: events = 0x%08" PRIx32 "\n", events);
1404
1405    do {
1406      jc->done = false;
1407      smsc9218i_transmit_finish_jobs(jc, regs, e, events);
1408      smsc9218i_transmit_create_jobs(jc, regs, ifp);
1409      smsc9218i_transmit_do_jobs(jc, regs, e);
1410    } while (!jc->done);
1411
1412    SMSC9218I_PRINTF("tx: done\n");
1413  }
1414
1415  /* Release network semaphore */
1416  rtems_bsdnet_semaphore_release();
1417
1418  /* Terminate self */
1419  (void) rtems_task_delete(RTEMS_SELF);
1420}
1421
1422#if defined(DEBUG)
1423static void smsc9218i_test_macros(void)
1424{
1425  unsigned i = 0;
1426  unsigned byte_test = 0x87654321U;
1427  unsigned val8 = 0xa5;
1428  unsigned val16 = 0xa55a;
1429  int r = 0;
1430
1431  r = SMSC9218I_SWAP(SMSC9218I_BYTE_TEST) == byte_test;
1432  printf("[%i] SMSC9218I_SWAP\n", r);
1433
1434  for (i = 0; i < 32; ++i) {
1435    r = SMSC9218I_SWAP(SMSC9218I_FLAG(i)) == (1U << i);
1436    printf("[%i] flag: %u\n", r, i);
1437  }
1438
1439  for (i = 0; i < 32; i += 8) {
1440    r = SMSC9218I_SWAP(SMSC9218I_FIELD_8(val8, i)) == (val8 << i);
1441    printf("[%i] field 8: %u\n", r, i);
1442  }
1443
1444  for (i = 0; i < 32; i += 16) {
1445    r = SMSC9218I_SWAP(SMSC9218I_FIELD_16(val16, i)) == (val16 << i);
1446    printf("[%i] field 16: %u\n", r, i);
1447  }
1448
1449  for (i = 0; i < 32; i += 8) {
1450    r = SMSC9218I_GET_FIELD_8(SMSC9218I_BYTE_TEST, i)
1451      == ((byte_test >> i) & 0xffU);
1452    printf("[%i] get field 8: %u\n", r, i);
1453  }
1454
1455  for (i = 0; i < 32; i += 16) {
1456    r = SMSC9218I_GET_FIELD_16(SMSC9218I_BYTE_TEST, i)
1457      == ((byte_test >> i) & 0xffffU);
1458    printf("[%i] get field 16: %u\n", r, i);
1459  }
1460}
1461#endif
1462
1463static void smsc9218i_wait_for_eeprom_access(volatile smsc9218i_registers *regs)
1464{
1465  while ((regs->e2p_cmd & SMSC9218I_E2P_CMD_EPC_BUSY) != 0) {
1466    /* Wait */
1467  }
1468}
1469
1470static void smsc9218i_set_mac_address(
1471  volatile smsc9218i_registers *regs,
1472  unsigned char address [6]
1473)
1474{
1475  smsc9218i_mac_write(
1476    regs,
1477    SMSC9218I_MAC_ADDRL,
1478    ((uint32_t) address [3] << 24) | ((uint32_t) address [2] << 16)
1479      | ((uint32_t) address [1] << 8) | (uint32_t) address [0]
1480  );
1481  smsc9218i_mac_write(
1482    regs,
1483    SMSC9218I_MAC_ADDRH,
1484    ((uint32_t) address [5] << 8) | (uint32_t) address [4]
1485  );
1486}
1487
1488#if defined(DEBUG)
1489static void smsc9218i_mac_address_dump(volatile smsc9218i_registers *regs)
1490{
1491  uint32_t low = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRL);
1492  uint32_t high = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRH);
1493
1494  printf(
1495    "MAC address: %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
1496      ":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
1497    low & 0xff,
1498    (low >> 8) & 0xff,
1499    (low >> 16) & 0xff,
1500    (low >> 24) & 0xff,
1501    high & 0xff,
1502    (high >> 8) & 0xff
1503  );
1504}
1505#endif
1506
1507static void smsc9218i_interrupt_init(
1508  smsc9218i_driver_entry *e,
1509  volatile smsc9218i_registers *regs
1510)
1511{
1512  rtems_status_code sc = RTEMS_SUCCESSFUL;
1513  union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS;
1514  union SIU_DIRER_tag direr = MPC55XX_ZERO_FLAGS;
1515  union SIU_DIRSR_tag dirsr = MPC55XX_ZERO_FLAGS;
1516  union SIU_ORER_tag orer = MPC55XX_ZERO_FLAGS;
1517  union SIU_IREER_tag ireer = MPC55XX_ZERO_FLAGS;
1518  union SIU_IFEER_tag ifeer = MPC55XX_ZERO_FLAGS;
1519  union SIU_IDFR_tag idfr = MPC55XX_ZERO_FLAGS;
1520  rtems_interrupt_level level;
1521
1522  /* Configure IRQ input pin */
1523  pcr.B.PA = 2;
1524  pcr.B.OBE = 0;
1525  pcr.B.IBE = 1;
1526#if MPC55XX_CHIP_TYPE / 10 != 551
1527  pcr.B.DSC = 0;
1528#endif
1529  pcr.B.ODE = 0;
1530  pcr.B.HYS = 0;
1531  pcr.B.SRC = 3;
1532  pcr.B.WPE = 0;
1533  pcr.B.WPS = 1;
1534  SIU.PCR [193].R = pcr.R;
1535
1536  /* DMA/Interrupt Request Select */
1537  rtems_interrupt_disable(level);
1538  dirsr.R = SIU.DIRSR.R;
1539#if MPC55XX_CHIP_TYPE / 10 != 551
1540  dirsr.B.DIRS0 = 0;
1541#endif
1542  SIU.DIRSR.R = dirsr.R;
1543  rtems_interrupt_enable(level);
1544
1545  /* Overrun Request Enable */
1546  rtems_interrupt_disable(level);
1547  orer.R = SIU.ORER.R;
1548  orer.B.ORE0 = 0;
1549  SIU.ORER.R = orer.R;
1550  rtems_interrupt_enable(level);
1551
1552  /* IRQ Rising-Edge Enable */
1553  rtems_interrupt_disable(level);
1554  ireer.R = SIU.IREER.R;
1555  ireer.B.IREE0 = 0;
1556  SIU.IREER.R = ireer.R;
1557  rtems_interrupt_enable(level);
1558
1559  /* IRQ Falling-Edge Enable */
1560  rtems_interrupt_disable(level);
1561  ifeer.R = SIU.IFEER.R;
1562  ifeer.B.IFEE0 = 1;
1563  SIU.IFEER.R = ifeer.R;
1564  rtems_interrupt_enable(level);
1565
1566  /* IRQ Digital Filter */
1567  rtems_interrupt_disable(level);
1568  idfr.R = SIU.IDFR.R;
1569  idfr.B.DFL = 0;
1570  SIU.IDFR.R = idfr.R;
1571  rtems_interrupt_enable(level);
1572
1573  /* Clear external interrupt status */
1574  SIU.EISR.R = 1;
1575
1576  /* DMA/Interrupt Request Enable */
1577  rtems_interrupt_disable(level);
1578  direr.R = SIU.DIRER.R;
1579  direr.B.EIRE0 = 1;
1580  SIU.DIRER.R = direr.R;
1581  rtems_interrupt_enable(level);
1582
1583  /* Install interrupt handler */
1584  sc = rtems_interrupt_handler_install(
1585    MPC55XX_IRQ_SIU_EXTERNAL_0,
1586    "SMSC9218i",
1587    RTEMS_INTERRUPT_UNIQUE,
1588    smsc9218i_interrupt_handler,
1589    e
1590  );
1591  ASSERT_SC(sc);
1592
1593  /* Enable interrupts and use push-pull driver (active low) */
1594  regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_ENABLE;
1595
1596  /* Enable error interrupts */
1597  regs->int_en = SMSC9218I_ERROR_INTERRUPTS;
1598}
1599
1600static void smsc9218i_reset_signal(bool signal)
1601{
1602  SIU.GPDO [186].R = signal ? 1 : 0;
1603}
1604
1605static void smsc9218i_reset_signal_init(void)
1606{
1607  union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS;
1608
1609  smsc9218i_reset_signal(true);
1610
1611  pcr.B.PA = 0;
1612  pcr.B.OBE = 1;
1613  pcr.B.IBE = 0;
1614#if MPC55XX_CHIP_TYPE / 10 != 551
1615  pcr.B.DSC = 0;
1616#endif
1617  pcr.B.ODE = 0;
1618  pcr.B.HYS = 0;
1619  pcr.B.SRC = 3;
1620  pcr.B.WPE = 1;
1621  pcr.B.WPS = 1;
1622
1623  SIU.PCR [186].R = pcr.R;
1624}
1625
1626static void smsc9218i_hardware_reset(volatile smsc9218i_registers *regs)
1627{
1628  smsc9218i_reset_signal_init();
1629  smsc9218i_reset_signal(false);
1630  rtems_bsp_delay(200);
1631  smsc9218i_reset_signal(true);
1632
1633  while ((regs->pmt_ctrl & SMSC9218I_PMT_CTRL_READY) == 0) {
1634    /* Wait */
1635  }
1636}
1637
1638static void smsc9218i_interface_init(void *arg)
1639{
1640  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
1641  struct ifnet *ifp = &e->arpcom.ac_if;
1642  volatile smsc9218i_registers *const regs = smsc9218i;
1643
1644  SMSC9218I_PRINTF("%s\n", __func__);
1645
1646  if (e->state == SMSC9218I_CONFIGURED) {
1647    smsc9218i_hardware_reset(regs);
1648
1649#if defined(DEBUG)
1650    /* Register dump */
1651    smsc9218i_register_dump(regs);
1652#endif
1653
1654    /* Set hardware configuration */
1655    regs->hw_cfg = SMSC9218I_HW_CFG_MBO | SMSC9218I_HW_CFG_TX_FIF_SZ(5);
1656
1657    /* MAC address */
1658    smsc9218i_wait_for_eeprom_access(regs);
1659    smsc9218i_set_mac_address(regs, e->arpcom.ac_enaddr);
1660#if defined(DEBUG)
1661    smsc9218i_mac_address_dump(regs);
1662#endif
1663
1664    /* Auto-negotiation advertisment */
1665    smsc9218i_phy_write(
1666      regs,
1667      MII_ANAR,
1668      ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA
1669    );
1670
1671    /* Initialize interrupts */
1672    smsc9218i_interrupt_init(e, regs);
1673
1674    /* Set MAC control */
1675    smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, SMSC9218I_MAC_CR_FDPX);
1676
1677    /* Set FIFO interrupts */
1678    regs->fifo_int = SMSC9218I_FIFO_INT_TDAL(32);
1679
1680    /* Clear receive drop counter */
1681    regs->rx_drop;
1682
1683    /* Start receive task */
1684    if (e->receive_task == RTEMS_ID_NONE) {
1685      e->receive_task = rtems_bsdnet_newproc(
1686        "ntrx",
1687        4096,
1688        smsc9218i_receive_task,
1689        e
1690      );
1691    }
1692
1693    /* Start transmit task */
1694    if (e->transmit_task == RTEMS_ID_NONE) {
1695      e->transmit_task = rtems_bsdnet_newproc(
1696        "nttx",
1697        4096,
1698        smsc9218i_transmit_task,
1699        e
1700      );
1701    }
1702
1703    /* Change state */
1704    if (e->receive_task != RTEMS_ID_NONE
1705      && e->transmit_task != RTEMS_ID_NONE) {
1706      e->state = SMSC9218I_STARTED;
1707    }
1708  }
1709
1710  if (e->state == SMSC9218I_STARTED) {
1711    /* Enable promiscous mode */
1712    smsc9218i_enable_promiscous_mode(
1713      regs,
1714      (ifp->if_flags & IFF_PROMISC) != 0
1715    );
1716
1717    /* Set interface to running state */
1718    ifp->if_flags |= IFF_RUNNING;
1719
1720    /* Change state */
1721    e->state = SMSC9218I_RUNNING;
1722  }
1723}
1724
1725static int smsc9218i_mdio_read(
1726  int phy,
1727  void *arg,
1728  unsigned phy_reg,
1729  uint32_t *val
1730)
1731{
1732  volatile smsc9218i_registers *const regs = smsc9218i;
1733
1734  *val = smsc9218i_phy_read(regs, phy_reg);
1735
1736  return 0;
1737}
1738
1739static int smsc9218i_mdio_write(
1740  int phy,
1741  void *arg,
1742  unsigned phy_reg,
1743  uint32_t data
1744)
1745{
1746  volatile smsc9218i_registers *const regs = smsc9218i;
1747
1748  smsc9218i_phy_write(regs, phy_reg, data);
1749
1750  return 0;
1751}
1752
1753static void smsc9218i_interface_stats(smsc9218i_driver_entry *e)
1754{
1755  volatile smsc9218i_registers *const regs = smsc9218i;
1756  smsc9218i_transmit_job_control *jc = &smsc_tx_jc;
1757  int media = 0;
1758  bool media_ok = smsc9218i_media_status(e, &media);
1759
1760  if (media_ok) {
1761    rtems_ifmedia2str(media, NULL, 0);
1762    printf ("\n");
1763  } else {
1764    printf ("PHY communication error\n");
1765  }
1766
1767  e->receive_drop += SMSC9218I_SWAP(regs->rx_drop);
1768
1769  printf("PHY interrupts:            %u\n", e->phy_interrupts);
1770  printf("received frames:           %u\n", e->received_frames);
1771  printf("receiver errors:           %u\n", e->receiver_errors);
1772  printf("receive interrupts:        %u\n", e->receive_interrupts);
1773  printf("receive DMA interrupts:    %u\n", e->receive_dma_interrupts);
1774  printf("receive to long errors:    %u\n", e->receive_too_long_errors);
1775  printf("receive collision errors:  %u\n", e->receive_collision_errors);
1776  printf("receive CRC errors:        %u\n", e->receive_crc_errors);
1777  printf("receive DMA errors:        %u\n", e->receive_dma_errors);
1778  printf("receive drops:             %u\n", e->receive_drop);
1779  printf("receive watchdog timeouts: %u\n", e->receive_watchdog_timeouts);
1780  printf("transmitted frames:        %u\n", e->transmitted_frames);
1781  printf("transmitter errors:        %u\n", e->transmitter_errors);
1782  printf("transmit interrupts:       %u\n", e->transmit_interrupts);
1783  printf("transmit DMA interrupts:   %u\n", e->transmit_dma_interrupts);
1784  printf("transmit status overflows: %u\n", e->transmit_status_overflows);
1785  printf("transmit frame errors:     %u\n", e->transmit_frame_errors);
1786  printf("transmit DMA errors:       %u\n", e->transmit_dma_errors);
1787  printf("frame compact count:       %u\n", jc->frame_compact_count);
1788}
1789
1790static int smsc9218i_interface_ioctl(
1791  struct ifnet *ifp,
1792  ioctl_command_t command,
1793  caddr_t data
1794) {
1795  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc;
1796  int rv = 0;
1797
1798  SMSC9218I_PRINTF("%s\n", __func__);
1799
1800  switch (command)  {
1801    case SIOCGIFMEDIA:
1802    case SIOCSIFMEDIA:
1803      rtems_mii_ioctl(&e->mdio, e, command, (int *) data);
1804      break;
1805    case SIOCGIFADDR:
1806    case SIOCSIFADDR:
1807      ether_ioctl(ifp, command, data);
1808      break;
1809    case SIOCSIFFLAGS:
1810      if (ifp->if_flags & IFF_RUNNING) {
1811        /* TODO: off */
1812      }
1813      if (ifp->if_flags & IFF_UP) {
1814        ifp->if_flags |= IFF_RUNNING;
1815        /* TODO: init */
1816      }
1817      break;
1818    case SIO_RTEMS_SHOW_STATS:
1819      smsc9218i_interface_stats(e);
1820      break;
1821    default:
1822      rv = EINVAL;
1823      break;
1824  }
1825
1826  return rv;
1827}
1828
1829static void smsc9218i_interface_start(struct ifnet *ifp)
1830{
1831  rtems_status_code sc = RTEMS_SUCCESSFUL;
1832  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc;
1833
1834  /* Interface is now active */
1835  ifp->if_flags |= IFF_OACTIVE;
1836
1837  sc = rtems_event_send(e->transmit_task, SMSC9218I_EVENT_TX_START);
1838  ASSERT_SC(sc);
1839}
1840
1841static void smsc9218i_interface_watchdog(struct ifnet *ifp)
1842{
1843  SMSC9218I_PRINTF("%s\n", __func__);
1844}
1845
1846static void smsc9218i_attach(struct rtems_bsdnet_ifconfig *config)
1847{
1848  smsc9218i_driver_entry *e = &smsc9218i_driver_data;
1849  struct ifnet *ifp = &e->arpcom.ac_if;
1850  char *unit_name = NULL;
1851  int unit_number = rtems_bsdnet_parse_driver_name(config, &unit_name);
1852
1853  /* Check parameter */
1854  assert(unit_number == 0);
1855  assert(config->hardware_address != NULL);
1856  assert(e->state == SMSC9218I_NOT_INITIALIZED);
1857
1858  /* Interrupt number */
1859  config->irno = MPC55XX_IRQ_SIU_EXTERNAL_0;
1860
1861  /* Device control */
1862  config->drv_ctrl = e;
1863
1864  /* Receive unit number */
1865  config->rbuf_count = 0;
1866
1867  /* Transmit unit number */
1868  config->xbuf_count = 0;
1869
1870  /* Copy MAC address */
1871  memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
1872
1873  /* Set interface data */
1874  ifp->if_softc = e;
1875  ifp->if_unit = (short) unit_number;
1876  ifp->if_name = unit_name;
1877  ifp->if_mtu = config->mtu > 0 ? (u_long) config->mtu : ETHERMTU;
1878  ifp->if_init = smsc9218i_interface_init;
1879  ifp->if_ioctl = smsc9218i_interface_ioctl;
1880  ifp->if_start = smsc9218i_interface_start;
1881  ifp->if_output = ether_output;
1882  ifp->if_watchdog = smsc9218i_interface_watchdog;
1883  ifp->if_flags = config->ignore_broadcast ? 0 : IFF_BROADCAST;
1884  ifp->if_snd.ifq_maxlen = ifqmaxlen;
1885  ifp->if_timer = 0;
1886
1887  /* MDIO */
1888  e->mdio.mdio_r = smsc9218i_mdio_read;
1889  e->mdio.mdio_w = smsc9218i_mdio_write;
1890
1891  /* Change status */
1892  e->state = SMSC9218I_CONFIGURED;
1893
1894  /* Attach the interface */
1895  if_attach(ifp);
1896  ether_ifattach(ifp);
1897}
1898
1899int smsc9218i_attach_detach(
1900  struct rtems_bsdnet_ifconfig *config,
1901  int attaching
1902) {
1903  if (attaching) {
1904    smsc9218i_attach(config);
1905  } else {
1906    /* TODO */
1907  }
1908
1909  /* FIXME: Return value */
1910  return 0;
1911}
Note: See TracBrowser for help on using the repository browser.