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

4.104.115
Last change on this file since 4f5d1c9f was 4f5d1c9f, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 03/27/10 at 15:00:43

removed warnings

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