source: rtems/bsps/powerpc/mpc55xxevb/net/smsc9218i.c @ cb68253

5
Last change on this file since cb68253 was cb68253, checked in by Sebastian Huber <sebastian.huber@…>, on 09/07/18 at 04:19:02

network: Use kernel/user space header files

Add and use <machine/rtems-bsd-kernel-space.h> and
<machine/rtems-bsd-user-space.h> similar to the libbsd to avoid command
line defines and defines scattered throught the code base.

Simplify cpukit/libnetworking/Makefile.am.

Update #3375.

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