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

4.115
Last change on this file since a762dc2 was a762dc2, checked in by Sebastian Huber <sebastian.huber@…>, on 01/23/12 at 10:19:22

Support for MPC5643L.

Rework of the start sequence to reduce the amount assembler code and to
support configuration tables which may be provided by the application.

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