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

5
Last change on this file since 031df391 was 031df391, checked in by Sebastian Huber <sebastian.huber@…>, on 04/23/18 at 07:53:31

bsps: Move legacy network drivers to bsps

This patch is a part of the BSP source reorganization.

Update #3285.

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