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

4.104.115
Last change on this file since 2067679 was d374492, checked in by Thomas Doerfler <Thomas.Doerfler@…>, on 07/21/09 at 08:38:04

Update for MPC55XX changes

  • Property mode set to 100644
File size: 42.8 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(
407  rtems_vector_number vector,
408  void *arg
409)
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
731static void smsc9218i_transmit_job_dump(
732  smsc9218i_transmit_job_control *jc,
733  const char *msg
734)
735{
736  char out [SMSC9218I_TX_JOBS + 1];
737  unsigned c = 0;
738  unsigned s = 0;
739
740  out [SMSC9218I_TX_JOBS] = '\0';
741
742  memset(out, '?', SMSC9218I_TX_JOBS);
743
744  c = jc->todo_index;
745  s = jc->empty;
746  while (s > 0) {
747    out [c] = 'E';
748    --s;
749    if (c < SMSC9218I_TX_JOBS_MAX) {
750      ++c;
751    } else {
752      c = 0;
753    }
754  }
755
756  c = jc->transfer_index;
757  s = jc->todo;
758  while (s > 0) {
759    out [c] = 'T';
760    --s;
761    if (c < SMSC9218I_TX_JOBS_MAX) {
762      ++c;
763    } else {
764      c = 0;
765    }
766  }
767
768  c = jc->empty_index;
769  s = jc->transfer;
770  while (s > 0) {
771    out [c] = 'D';
772    --s;
773    if (c < SMSC9218I_TX_JOBS_MAX) {
774      ++c;
775    } else {
776      c = 0;
777    }
778  }
779
780  printf(
781    "tx: %s: %02u:%02u:%02u %s\n",
782    msg,
783    jc->empty,
784    jc->todo,
785    jc->transfer,
786    out
787  );
788}
789
790static struct mbuf *smsc9218i_next_transmit_fragment(
791  struct ifnet *ifp,
792  smsc9218i_transmit_job_control *jc
793)
794{
795  struct mbuf *m = jc->next_fragment;
796
797  if (m != NULL) {
798    /* Next fragment is from the current frame */
799    jc->next_fragment = m->m_next;
800  } else {
801    /* Dequeue first fragment of the next frame */
802    IF_DEQUEUE(&ifp->if_snd, m);
803
804    /* Update frame head */
805    jc->frame = m;
806
807    if (m != NULL) {
808      struct mbuf *n = m;
809      struct mbuf *p = NULL;
810      uint32_t frame_length = 0;
811      unsigned fragments = 0;
812
813      /* Calculate frame length and fragment number */
814      do {
815        int len = n->m_len;
816
817        if (len > 0) {
818          ++fragments;
819          frame_length += (uint32_t) len;
820
821          if (len < 4) {
822            printf("FIXME\n");
823          }
824
825          /* Next fragment */
826          p = n;
827          n = n->m_next;
828        } else {
829          /* Discard empty fragment and get next */
830          n = m_free(n);
831
832          /* Remove fragment from list */
833          if (p != NULL) {
834            p->m_next = n;
835          } else {
836            jc->frame = n;
837          }
838        }
839      } while (n != NULL);
840
841      if (fragments > SMSC9218I_TX_FRAGMENT_MAX) {
842        printf("FIXME\n");
843      }
844
845      /* Set frame length */
846      jc->frame_length = frame_length;
847
848      /* Update next fragment */
849      jc->next_fragment = jc->frame->m_next;
850
851      /* Update tag */
852      ++jc->tag;
853
854      /* Command B */
855      jc->command_b = SMSC9218I_TX_B_TAG(jc->tag)
856        | SMSC9218I_TX_B_FRAME_LENGTH(jc->frame_length);
857
858      SMSC9218I_PRINTF(
859        "tx: next frame: tag = %u, frame length = %" PRIu32
860          ", fragments = %u\n",
861        (unsigned) jc->tag,
862        frame_length,
863        fragments
864      );
865    } else {
866      /* The transmit queue is empty, we have no next fragment */
867      jc->next_fragment = NULL;
868
869      /* Interface is now inactive */
870      ifp->if_flags = CLEAR_FLAG(ifp->if_flags, IFF_OACTIVE);
871
872      /* Transmit task may wait for events */
873      jc->done = true;
874
875      SMSC9218I_PRINTF("tx: inactive\n");
876    }
877  }
878
879  return m;
880}
881
882static void smsc9218i_transmit_create_jobs(
883  smsc9218i_transmit_job_control *jc,
884  volatile smsc9218i_registers *const regs,
885  struct ifnet *ifp
886)
887{
888  unsigned n = jc->empty;
889
890  if (n > 0) {
891    unsigned c = jc->todo_index;
892    unsigned i = 0;
893
894    #ifdef DEBUG
895      smsc9218i_transmit_job_dump(jc, "create");
896    #endif
897
898    for (i = 0; i < n; ++i) {
899      struct mbuf *m = smsc9218i_next_transmit_fragment(ifp, jc);
900
901      if (m != NULL) {
902        uint32_t first = m == jc->frame ? SMSC9218I_TX_A_FIRST : 0;
903        uint32_t last = m->m_next == NULL ? SMSC9218I_TX_A_LAST : 0;
904        uint32_t fragment_length = (uint32_t) m->m_len;
905        uint32_t fragment_misalign = (uint32_t) (mtod(m, uintptr_t) % 4);
906        uint32_t data_length = fragment_misalign + fragment_length;
907        uint32_t data_misalign = data_length % 4;
908        uint32_t data = (uint32_t) (mtod(m, char *) - fragment_misalign);
909
910        /* Align data length on four byte boundary */
911        if (data_misalign > 0) {
912          data_length += 4 - data_misalign;
913        }
914
915        /* Cache flush for fragment data */
916        rtems_cache_flush_multiple_data_lines(
917          (const void *) data,
918          data_length
919        );
920
921        asm volatile ( "sync");
922
923        /* Remember fragement */
924        jc->fragment_table [c] = m;
925
926        /* Command A and B */
927        jc->command_table [c].a = SMSC9218I_TX_A_END_ALIGN_4
928          | SMSC9218I_TX_A_DOFF(fragment_misalign)
929          | SMSC9218I_TX_A_FRAGMENT_LENGTH(fragment_length)
930          | first
931          | last;
932        jc->command_table [c].b = jc->command_b;
933
934        /* Data TCD */
935        jc->data_tcd_table [c].SADDR = data;
936        jc->data_tcd_table [c].NBYTES = data_length;
937
938        SMSC9218I_PRINTF("tx: create: index = %u\n", c);
939      } else {
940        /* Nothing to do */
941        break;
942      }
943
944      if (c < SMSC9218I_TX_JOBS_MAX) {
945        ++c;
946      } else {
947        c = 0;
948      }
949    }
950
951    /* Increment jobs to do */
952    jc->todo += i;
953
954    /* Decrement empty count */
955    jc->empty -= i;
956
957    /* Update todo index */
958    jc->todo_index = c;
959
960    #ifdef DEBUG
961      smsc9218i_transmit_job_dump(jc, "create done");
962    #endif
963  } else {
964    /* Transmit task may wait for events */
965    jc->done = true;
966  }
967}
968
969static void smsc9218i_transmit_do_jobs(
970  smsc9218i_transmit_job_control *jc,
971  volatile smsc9218i_registers *const regs,
972  smsc9218i_driver_entry *e
973)
974{
975  if (jc->transfer == 0) {
976    uint32_t tx_fifo_inf = regs->tx_fifo_inf;
977    uint32_t data_free = SMSC9218I_TX_FIFO_INF_GET_FREE(tx_fifo_inf);
978    unsigned c = jc->transfer_index;
979    unsigned last_index = c;
980    unsigned i = 0;
981    unsigned n = jc->todo;
982    struct tcd_t *p = NULL;
983
984    #ifdef DEBUG
985      smsc9218i_transmit_job_dump(jc, "transfer");
986    #endif
987
988    for (i = 0; i < n; ++i) {
989      struct tcd_t *tcd = &jc->data_tcd_table [c];
990      uint32_t data_length = tcd->NBYTES;
991
992      if (data_length <= data_free) {
993        /* Reduce free FIFO space */
994        data_free -= data_length;
995
996        /* Index of last TCD */
997        last_index = c;
998
999        /* Cache flush for previous data TCD */
1000        if (p != NULL) {
1001          rtems_cache_flush_multiple_data_lines(p, sizeof(*p));
1002        }
1003      } else {
1004        break;
1005      }
1006
1007      p = tcd;
1008      if (c < SMSC9218I_TX_JOBS_MAX) {
1009        ++c;
1010      } else {
1011        c = 0;
1012      }
1013    }
1014
1015    if (i > 0) {
1016      volatile struct tcd_t *channel = &EDMA.TCD [e->edma_transmit.channel];
1017      struct tcd_t *start = &jc->command_tcd_table [jc->transfer_index];
1018      struct tcd_t *last = &jc->data_tcd_table [last_index];
1019
1020      /* New transfer index */
1021      jc->transfer_index = c;
1022
1023      /* New last transfer index */
1024      jc->transfer_last_index = last_index;
1025
1026      /* Set jobs in transfer */
1027      jc->transfer = i;
1028
1029      /* Decrement jobs to do */
1030      jc->todo -= i;
1031
1032      /* Cache flush for command table */
1033      rtems_cache_flush_multiple_data_lines(
1034        jc->command_table,
1035        sizeof(jc->command_table)
1036      );
1037
1038      /* Enable interrupt for last data TCD */
1039      last->BMF.R = SMSC9218I_EDMA_TX_TCD_BMF_INTERRUPT;
1040
1041      /* Cache flush for last data TCD */
1042      rtems_cache_flush_multiple_data_lines(last, sizeof(*last));
1043
1044      /* Start eDMA transfer */
1045      channel->SADDR = start->SADDR;
1046      channel->SDF.R = start->SDF.R;
1047      channel->NBYTES = start->NBYTES;
1048      channel->SLAST = start->SLAST;
1049      channel->DADDR = start->DADDR;
1050      channel->CDF.R = start->CDF.R;
1051      channel->DLAST_SGA = start->DLAST_SGA;
1052      channel->BMF.R = SMSC9218I_EDMA_TX_TCD_BMF_CLEAR;
1053      channel->BMF.R = start->BMF.R;
1054
1055      /* Transmit task may wait for events */
1056      jc->done = true;
1057    } else if (n > 0) {
1058      /* We need to wait until the FIFO has more free space */
1059      smsc9218i_enable_transmit_interrupts(regs);
1060
1061      /* Transmit task may wait for events */
1062      jc->done = true;
1063    }
1064
1065    #ifdef DEBUG
1066      smsc9218i_transmit_job_dump(jc, "transfer done");
1067    #endif
1068  }
1069}
1070
1071static void smsc9218i_transmit_finish_jobs(
1072  smsc9218i_transmit_job_control *jc,
1073  volatile smsc9218i_registers *const regs,
1074  smsc9218i_driver_entry *e,
1075  rtems_event_set events
1076)
1077{
1078  uint32_t tx_fifo_inf = regs->tx_fifo_inf;
1079  uint32_t status_used = SMSC9218I_TX_FIFO_INF_GET_SUSED(tx_fifo_inf);
1080  uint32_t s = 0;
1081  unsigned n = jc->transfer;
1082
1083  for (s = 0; s < status_used; ++s) {
1084    uint32_t tx_fifo_status = regs->tx_fifo_status;
1085
1086    if (IS_FLAG_CLEARED(tx_fifo_status, SMSC9218I_TX_STS_ERROR)) {
1087      ++e->transmitted_frames;
1088    } else {
1089      ++e->transmit_errors;
1090    }
1091
1092    SMSC9218I_PRINTF(
1093      "tx: transmit status: tag = %" PRIu32 ", status = 0x%08" PRIx32 "\n",
1094      SMSC9218I_TX_STS_GET_TAG(tx_fifo_status),
1095      SMSC9218I_SWAP(tx_fifo_status)
1096    );
1097  }
1098
1099  if (
1100    IS_ANY_FLAG_SET(events, SMSC9218I_EVENT_EDMA | SMSC9218I_EVENT_EDMA_ERROR)
1101      && n > 0
1102  ) {
1103    unsigned c = jc->empty_index;
1104    unsigned i = 0;
1105
1106    #ifdef DEBUG
1107      smsc9218i_transmit_job_dump(jc, "finish");
1108    #endif
1109
1110    if (IS_FLAG_SET(events, SMSC9218I_EVENT_EDMA_ERROR)) {
1111      ++e->transmit_edma_errors;
1112    }
1113
1114    /* Restore last data TCD */
1115    jc->data_tcd_table [jc->transfer_last_index].BMF.R =
1116      SMSC9218I_EDMA_TX_TCD_BMF_LINK;
1117
1118    for (i = 0; i < n; ++i) {
1119      /* Free fragment buffer */
1120      m_free(jc->fragment_table [c]);
1121
1122      if (c < SMSC9218I_TX_JOBS_MAX) {
1123        ++c;
1124      } else {
1125        c = 0;
1126      }
1127    }
1128
1129    /* Increment empty count */
1130    jc->empty += n;
1131
1132    /* New empty index */
1133    jc->empty_index = jc->transfer_index;
1134
1135    /* Clear jobs in transfer */
1136    jc->transfer = 0;
1137
1138    /* Update empty index */
1139    jc->empty_index = c;
1140
1141    #ifdef DEBUG
1142      smsc9218i_transmit_job_dump(jc, "finish done");
1143    #endif
1144  }
1145}
1146
1147/* FIXME */
1148static smsc9218i_transmit_job_control smsc_jc  __attribute__ ((aligned (32))) = {
1149  .frame = NULL,
1150  .next_fragment = NULL,
1151  .frame_length = 0,
1152  .tag = 0,
1153  .command_b = 0,
1154  .done = false,
1155  .empty_index = 0,
1156  .transfer_index = 0,
1157  .todo_index = 0,
1158  .empty = SMSC9218I_TX_JOBS,
1159  .transfer = 0,
1160  .todo = 0
1161};
1162
1163static void smsc9218i_transmit_task(void *arg)
1164{
1165  rtems_status_code sc = RTEMS_SUCCESSFUL;
1166  rtems_event_set events = 0;
1167  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
1168  struct ifnet *ifp = &e->arpcom.ac_if;
1169  volatile smsc9218i_registers *const regs = smsc9218i;
1170  uint32_t mac_cr = 0;
1171  smsc9218i_transmit_job_control *jc = &smsc_jc;
1172  unsigned i = 0;
1173
1174  SMSC9218I_PRINTF("%s\n", __func__);
1175
1176  /* Obtain transmit eDMA channel */
1177  e->edma_transmit.id = e->transmit_task;
1178  sc = mpc55xx_edma_obtain_channel(&e->edma_transmit);
1179  RTEMS_CLEANUP_SC(sc, cleanup, "obtain transmit eDMA channel");
1180
1181  /* Setup transmit eDMA descriptors */
1182  for (i = 0; i < SMSC9218I_TX_JOBS; ++i) {
1183    unsigned ii = i < SMSC9218I_TX_JOBS_MAX ? i + 1 : 0;
1184    struct tcd_t tcd = EDMA_TCD_DEFAULT;
1185    struct tcd_t *command_tcd = &jc->command_tcd_table [i];
1186    struct tcd_t *data_tcd = &jc->data_tcd_table [i];
1187    struct tcd_t *next_command_tcd = &jc->command_tcd_table [ii];
1188
1189    tcd.SDF.B.SSIZE = 2;
1190    tcd.SDF.B.SOFF = 4;
1191    tcd.SDF.B.DSIZE = 2;
1192    tcd.CDF.B.CITER = 1;
1193    tcd.BMF.R = SMSC9218I_EDMA_TX_TCD_BMF_LINK;
1194    tcd.DADDR = (uint32_t) &regs->tx_fifo_data;
1195
1196    tcd.DLAST_SGA = (uint32_t) next_command_tcd;
1197    *data_tcd = tcd;
1198
1199    tcd.SADDR = (uint32_t) &jc->command_table [i].a;
1200    tcd.NBYTES = 8;
1201    tcd.DLAST_SGA = (uint32_t) data_tcd;
1202    *command_tcd = tcd;
1203  }
1204
1205  /*
1206   * Cache flush for command TCD.  The content of the command TCD remains
1207   * invariant.
1208   */
1209  rtems_cache_flush_multiple_data_lines(
1210    jc->command_tcd_table,
1211    sizeof(jc->command_tcd_table)
1212  );
1213
1214  /* Configure transmitter */
1215  regs->tx_cfg = SMSC9218I_TX_CFG_SAO | SMSC9218I_TX_CFG_ON;
1216
1217  /* Enable MAC transmitter */
1218  mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR) | SMSC9218I_MAC_CR_TXEN;
1219  smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr);
1220
1221  /* Main event loop */
1222  while (true) {
1223    /* Wait for events */
1224    sc = rtems_bsdnet_event_receive(
1225      SMSC9218I_EVENT_TX
1226        | SMSC9218I_EVENT_TX_START
1227        | SMSC9218I_EVENT_EDMA
1228        | SMSC9218I_EVENT_EDMA_ERROR,
1229      RTEMS_EVENT_ANY | RTEMS_WAIT,
1230      RTEMS_NO_TIMEOUT,
1231      &events
1232    );
1233    RTEMS_CLEANUP_SC(sc, cleanup, "wait for events");
1234
1235    SMSC9218I_PRINTF("tx: wake up: events = 0x%08" PRIx32 "\n", events);
1236
1237    do {
1238      jc->done = false;
1239      smsc9218i_transmit_finish_jobs(jc, regs, e, events);
1240      smsc9218i_transmit_create_jobs(jc, regs, ifp);
1241      smsc9218i_transmit_do_jobs(jc, regs, e);
1242    } while (!jc->done);
1243
1244    SMSC9218I_PRINTF("tx: done\n");
1245  }
1246
1247cleanup:
1248
1249  /* Release network semaphore */
1250  rtems_bsdnet_semaphore_release();
1251
1252  /* Terminate self */
1253  (void) rtems_task_delete(RTEMS_SELF);
1254}
1255
1256static void smsc9218i_test_macros(void)
1257{
1258  unsigned i = 0;
1259  unsigned byte_test = 0x87654321U;
1260  unsigned val8 = 0xa5;
1261  unsigned val16 = 0xa55a;
1262  int r = 0;
1263
1264  r = SMSC9218I_SWAP(SMSC9218I_BYTE_TEST) == byte_test;
1265  printf("[%i] SMSC9218I_SWAP\n", r);
1266
1267  for (i = 0; i < 32; ++i) {
1268    r = SMSC9218I_SWAP(SMSC9218I_FLAG(i)) == (1U << i);
1269    printf("[%i] flag: %u\n", r, i);
1270  }
1271
1272  for (i = 0; i < 32; i += 8) {
1273    r = SMSC9218I_SWAP(SMSC9218I_FIELD_8(val8, i)) == (val8 << i);
1274    printf("[%i] field 8: %u\n", r, i);
1275  }
1276
1277  for (i = 0; i < 32; i += 16) {
1278    r = SMSC9218I_SWAP(SMSC9218I_FIELD_16(val16, i)) == (val16 << i);
1279    printf("[%i] field 16: %u\n", r, i);
1280  }
1281
1282  for (i = 0; i < 32; i += 8) {
1283    r = SMSC9218I_GET_FIELD_8(SMSC9218I_BYTE_TEST, i)
1284      == ((byte_test >> i) & 0xffU);
1285    printf("[%i] get field 8: %u\n", r, i);
1286  }
1287
1288  for (i = 0; i < 32; i += 16) {
1289    r = SMSC9218I_GET_FIELD_16(SMSC9218I_BYTE_TEST, i)
1290      == ((byte_test >> i) & 0xffffU);
1291    printf("[%i] get field 16: %u\n", r, i);
1292  }
1293}
1294
1295static void smsc9218i_set_mac_address(
1296  volatile smsc9218i_registers *regs,
1297  unsigned char address [6]
1298)
1299{
1300  smsc9218i_mac_write(
1301    regs,
1302    SMSC9218I_MAC_ADDRL,
1303    ((uint32_t) address [3] << 24) | ((uint32_t) address [2] << 16)
1304      | ((uint32_t) address [1] << 8) | (uint32_t) address [0]
1305  );
1306  smsc9218i_mac_write(
1307    regs,
1308    SMSC9218I_MAC_ADDRH,
1309    ((uint32_t) address [5] << 8) | (uint32_t) address [4]
1310  );
1311}
1312
1313static void smsc9218i_mac_address_dump(volatile smsc9218i_registers *regs)
1314{
1315  uint32_t low = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRL);
1316  uint32_t high = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRH);
1317
1318  printf(
1319    "MAC address: %02" PRIx32 ":%02" PRIx32 ":%02" PRIx32
1320      ":%02" PRIx32 ":%02" PRIx32 ":%02" PRIx32 "\n",
1321    low & 0xff,
1322    (low >> 8) & 0xff,
1323    (low >> 16) & 0xff,
1324    (low >> 24) & 0xff,
1325    high & 0xff,
1326    (high >> 8) & 0xff
1327  );
1328}
1329
1330static void smsc9218i_interrupt_init(
1331  smsc9218i_driver_entry *e,
1332  volatile smsc9218i_registers *regs
1333)
1334{
1335  rtems_status_code sc = RTEMS_SUCCESSFUL;
1336  union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS;
1337  union SIU_DIRER_tag direr = MPC55XX_ZERO_FLAGS;
1338  union SIU_DIRSR_tag dirsr = MPC55XX_ZERO_FLAGS;
1339  union SIU_ORER_tag orer = MPC55XX_ZERO_FLAGS;
1340  union SIU_IREER_tag ireer = MPC55XX_ZERO_FLAGS;
1341  union SIU_IFEER_tag ifeer = MPC55XX_ZERO_FLAGS;
1342  union SIU_IDFR_tag idfr = MPC55XX_ZERO_FLAGS;
1343  rtems_interrupt_level level;
1344
1345  /* Configure IRQ input pin */
1346  pcr.B.PA = 2;
1347  pcr.B.OBE = 0;
1348  pcr.B.IBE = 1;
1349  pcr.B.DSC = 0;
1350  pcr.B.ODE = 0;
1351  pcr.B.HYS = 0;
1352  pcr.B.SRC = 3;
1353  pcr.B.WPE = 0;
1354  pcr.B.WPS = 1;
1355  SIU.PCR [193].R = pcr.R;
1356
1357  /* DMA/Interrupt Request Select */
1358  rtems_interrupt_disable(level);
1359  dirsr.R = SIU.DIRSR.R;
1360  dirsr.B.DIRS0 = 0;
1361  SIU.DIRSR.R = dirsr.R;
1362  rtems_interrupt_enable(level);
1363
1364  /* Overrun Request Enable */
1365  rtems_interrupt_disable(level);
1366  orer.R = SIU.ORER.R;
1367  orer.B.ORE0 = 0;
1368  SIU.ORER.R = orer.R;
1369  rtems_interrupt_enable(level);
1370
1371  /* IRQ Rising-Edge Enable */
1372  rtems_interrupt_disable(level);
1373  ireer.R = SIU.IREER.R;
1374  ireer.B.IREE0 = 0;
1375  SIU.IREER.R = ireer.R;
1376  rtems_interrupt_enable(level);
1377
1378  /* IRQ Falling-Edge Enable */
1379  rtems_interrupt_disable(level);
1380  ifeer.R = SIU.IFEER.R;
1381  ifeer.B.IFEE0 = 1;
1382  SIU.IFEER.R = ifeer.R;
1383  rtems_interrupt_enable(level);
1384
1385  /* IRQ Digital Filter */
1386  rtems_interrupt_disable(level);
1387  idfr.R = SIU.IDFR.R;
1388  idfr.B.DFL = 0;
1389  SIU.IDFR.R = idfr.R;
1390  rtems_interrupt_enable(level);
1391
1392  /* Clear external interrupt status */
1393  SIU.EISR.R = 1;
1394
1395  /* DMA/Interrupt Request Enable */
1396  rtems_interrupt_disable(level);
1397  direr.R = SIU.DIRER.R;
1398  direr.B.EIRE0 = 1;
1399  SIU.DIRER.R = direr.R;
1400  rtems_interrupt_enable(level);
1401
1402  /* Install interrupt handler */
1403  sc = rtems_interrupt_handler_install(
1404    MPC55XX_IRQ_SIU_EXTERNAL_0,
1405    "SMSC9218i",
1406    RTEMS_INTERRUPT_UNIQUE,
1407    smsc9218i_interrupt_handler,
1408    e
1409  );
1410  RTEMS_SYSLOG_ERROR_SC(sc, "install interrupt handler\n");
1411
1412  /* Enable interrupts and use push-pull driver (active low) */
1413  regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_ENABLE;
1414}
1415
1416static void smsc9218i_reset_signal(bool signal)
1417{
1418  SIU.GPDO [186].R = signal ? 1 : 0;
1419}
1420
1421static void smsc9218i_reset_signal_init(void)
1422{
1423  union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS;
1424
1425  smsc9218i_reset_signal(true);
1426
1427  pcr.B.PA = 0;
1428  pcr.B.OBE = 1;
1429  pcr.B.IBE = 0;
1430  pcr.B.DSC = 0;
1431  pcr.B.ODE = 0;
1432  pcr.B.HYS = 0;
1433  pcr.B.SRC = 3;
1434  pcr.B.WPE = 1;
1435  pcr.B.WPS = 1;
1436
1437  SIU.PCR [186].R = pcr.R;
1438}
1439
1440static void smsc9218i_interface_init(void *arg)
1441{
1442  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg;
1443  struct ifnet *ifp = &e->arpcom.ac_if;
1444  volatile smsc9218i_registers *const regs = smsc9218i;
1445
1446  SMSC9218I_PRINTF("%s\n", __func__);
1447
1448  if (e->state == SMSC9218I_CONFIGURED) {
1449    /* Hardware reset */
1450    smsc9218i_reset_signal_init();
1451    smsc9218i_reset_signal(false);
1452    rtems_bsp_delay(200);
1453    smsc9218i_reset_signal(true);
1454
1455    /* Register dump */
1456    smsc9218i_register_dump(regs);
1457
1458    /* Set hardware configuration */
1459    regs->hw_cfg = SMSC9218I_HW_CFG_MBO | SMSC9218I_HW_CFG_TX_FIF_SZ(5);
1460
1461    /* MAC address */
1462    smsc9218i_set_mac_address(regs, e->arpcom.ac_enaddr);
1463    smsc9218i_mac_address_dump(regs);
1464
1465    /* Initialize interrupts */
1466    smsc9218i_interrupt_init(e, regs);
1467
1468    /* Set MAC control */
1469    smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, SMSC9218I_MAC_CR_FDPX);
1470
1471    /* Set FIFO interrupts */
1472    regs->fifo_int = SMSC9218I_FIFO_INT_TDAL(32);
1473
1474    /* Start receive task */
1475    if (e->receive_task == RTEMS_ID_NONE) {
1476      e->receive_task = rtems_bsdnet_newproc(
1477        "ntrx",
1478        4096,
1479        smsc9218i_receive_task,
1480        e
1481      );
1482    }
1483
1484    /* Start transmit task */
1485    if (e->transmit_task == RTEMS_ID_NONE) {
1486      e->transmit_task = rtems_bsdnet_newproc(
1487        "nttx",
1488        4096,
1489        smsc9218i_transmit_task,
1490        e
1491      );
1492    }
1493
1494    /* Change state */
1495    if (e->receive_task != RTEMS_ID_NONE
1496      && e->transmit_task != RTEMS_ID_NONE) {
1497      e->state = SMSC9218I_STARTED;
1498    }
1499  }
1500
1501  if (e->state == SMSC9218I_STARTED) {
1502    /* Enable promiscous mode */
1503    smsc9218i_enable_promiscous_mode(
1504      regs,
1505      IS_FLAG_SET(ifp->if_flags, IFF_PROMISC)
1506    );
1507
1508    /* Set interface to running state */
1509    ifp->if_flags = SET_FLAG(ifp->if_flags, IFF_RUNNING);
1510
1511    /* Change state */
1512    e->state = SMSC9218I_RUNNING;
1513  }
1514}
1515
1516static void smsc9218i_interface_stats(const smsc9218i_driver_entry *e)
1517{
1518  printf("received frames:          %u\n", e->received_frames);
1519  printf("receive interrupts:       %u\n", e->receive_interrupts);
1520  printf("transmitted frames:       %u\n", e->transmitted_frames);
1521  printf("transmit interrupts:      %u\n", e->transmit_interrupts);
1522  printf("receive to long errors:   %u\n", e->receive_too_long_errors);
1523  printf("receive collision errors: %u\n", e->receive_collision_errors);
1524  printf("receive CRC errors:       %u\n", e->receive_crc_errors);
1525  printf("receive eDMA errors:      %u\n", e->receive_edma_errors);
1526  printf("transmit errors:          %u\n", e->transmit_errors);
1527  printf("transmit eDMA errors:     %u\n", e->transmit_edma_errors);
1528}
1529
1530static int smsc9218i_interface_ioctl(
1531  struct ifnet *ifp,
1532  ioctl_command_t command,
1533  caddr_t data
1534) {
1535  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc;
1536  int rv = 0;
1537
1538  SMSC9218I_PRINTF("%s\n", __func__);
1539
1540  switch (command)  {
1541    case SIOCGIFMEDIA:
1542    case SIOCSIFMEDIA:
1543      rtems_mii_ioctl(&e->mdio_info, e, (int) command, (int *) data);
1544      break;
1545    case SIOCGIFADDR:
1546    case SIOCSIFADDR:
1547      ether_ioctl(ifp, command, data);
1548      break;
1549    case SIOCSIFFLAGS:
1550      if (ifp->if_flags & IFF_RUNNING) {
1551        /* TODO: off */
1552      }
1553      if (ifp->if_flags & IFF_UP) {
1554        ifp->if_flags = SET_FLAG(ifp->if_flags, IFF_RUNNING);
1555        /* TODO: init */
1556      }
1557      break;
1558    case SIO_RTEMS_SHOW_STATS:
1559      smsc9218i_interface_stats(e);
1560      break;
1561    default:
1562      rv = EINVAL;
1563      break;
1564  }
1565
1566  return rv;
1567}
1568
1569static void smsc9218i_interface_start(struct ifnet *ifp)
1570{
1571  rtems_status_code sc = RTEMS_SUCCESSFUL;
1572  smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc;
1573
1574  /* Interface is now active */
1575  ifp->if_flags = SET_FLAG(ifp->if_flags, IFF_OACTIVE);
1576
1577  sc = rtems_event_send(e->transmit_task, SMSC9218I_EVENT_TX_START);
1578  RTEMS_SYSLOG_ERROR_SC(sc, "send transmit start event");
1579}
1580
1581static void smsc9218i_interface_watchdog(struct ifnet *ifp)
1582{
1583  SMSC9218I_PRINTF("%s\n", __func__);
1584}
1585
1586static int smsc9218i_attach(struct rtems_bsdnet_ifconfig *config)
1587{
1588  smsc9218i_driver_entry *e = &smsc9218i_driver_data;
1589  struct ifnet *ifp = &e->arpcom.ac_if;
1590  char *unit_name = NULL;
1591  int unit_number = rtems_bsdnet_parse_driver_name(config, &unit_name);
1592
1593  /* Check parameter */
1594  if (unit_number < 0) {
1595    RTEMS_SYSLOG_ERROR("parse error for interface name\n");
1596    return 0;
1597  }
1598  if (unit_number != 0) {
1599    RTEMS_DO_CLEANUP(smsc9218i_attach_cleanup, "unexpected unit number");
1600  }
1601  if (config->hardware_address == NULL) {
1602    RTEMS_DO_CLEANUP(smsc9218i_attach_cleanup, "MAC address missing");
1603  }
1604  if (e->state != SMSC9218I_NOT_INITIALIZED) {
1605    RTEMS_DO_CLEANUP(smsc9218i_attach_cleanup, "already attached");
1606  }
1607
1608  /* Interrupt number */
1609  config->irno = MPC55XX_IRQ_SIU_EXTERNAL_0;
1610
1611  /* Device control */
1612  config->drv_ctrl = e;
1613
1614  /* Receive unit number */
1615  config->rbuf_count = 0;
1616
1617  /* Transmit unit number */
1618  config->xbuf_count = 0;
1619
1620  /* Copy MAC address */
1621  memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN);
1622
1623  /* Set interface data */
1624  ifp->if_softc = e;
1625  ifp->if_unit = (short) unit_number;
1626  ifp->if_name = unit_name;
1627  ifp->if_mtu = config->mtu > 0 ? (u_long) config->mtu : ETHERMTU;
1628  ifp->if_init = smsc9218i_interface_init;
1629  ifp->if_ioctl = smsc9218i_interface_ioctl;
1630  ifp->if_start = smsc9218i_interface_start;
1631  ifp->if_output = ether_output;
1632  ifp->if_watchdog = smsc9218i_interface_watchdog;
1633  ifp->if_flags = config->ignore_broadcast ? 0 : IFF_BROADCAST;
1634  ifp->if_snd.ifq_maxlen = ifqmaxlen;
1635  ifp->if_timer = 0;
1636
1637  /* Change status */
1638  e->state = SMSC9218I_CONFIGURED;
1639
1640  /* Attach the interface */
1641  if_attach(ifp);
1642  ether_ifattach(ifp);
1643
1644  return 1;
1645
1646smsc9218i_attach_cleanup:
1647
1648  /* FIXME: Type */
1649  free(unit_name, (int) 0xdeadbeef);
1650
1651  return 0;
1652}
1653
1654int smsc9218i_attach_detach(
1655  struct rtems_bsdnet_ifconfig *config,
1656  int attaching
1657) {
1658  /* FIXME: Return value */
1659
1660  if (attaching) {
1661    return smsc9218i_attach(config);
1662  } else {
1663    /* TODO */
1664    return 0;
1665  }
1666}
Note: See TracBrowser for help on using the repository browser.