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

4.104.115
Last change on this file since 9eac014 was 60e5832, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 10/21/09 at 13:24:35

interrupt handler type change

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