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

4.115
Last change on this file since 7f23ead was 20e1e769, checked in by Sebastian Huber <sebastian.huber@…>, on 10/31/12 at 16:03:06

bsp/mpc55xx: SMSC9218i avoid mbuf migration

The receive task will only hand over a mbuf if it gets a new one
immediately. This avoids mbuf migration out of the receive task in case
of overload.

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