diff options
Diffstat (limited to 'bsps/powerpc/mpc55xxevb/net/smsc9218i.c')
-rw-r--r-- | bsps/powerpc/mpc55xxevb/net/smsc9218i.c | 2124 |
1 files changed, 0 insertions, 2124 deletions
diff --git a/bsps/powerpc/mpc55xxevb/net/smsc9218i.c b/bsps/powerpc/mpc55xxevb/net/smsc9218i.c deleted file mode 100644 index d88aa8ae39..0000000000 --- a/bsps/powerpc/mpc55xxevb/net/smsc9218i.c +++ /dev/null @@ -1,2124 +0,0 @@ -/** - * @file - * - * @ingroup RTEMSBSPsPowerPCMPC55XX - * - * @brief SMSC - LAN9218i - */ - -/* - * Copyright (c) 2009-2012 embedded brains GmbH. All rights reserved. - * - * embedded brains GmbH - * Obere Lagerstr. 30 - * 82178 Puchheim - * Germany - * <rtems@embedded-brains.de> - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.org/license/LICENSE. - */ - -#include <rtems.h> - -#include <mpc55xx/regs.h> - -#if defined(RTEMS_NETWORKING) && defined(MPC55XX_HAS_SIU) - -#include <machine/rtems-bsd-kernel-space.h> - -#include <errno.h> -#include <assert.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <inttypes.h> - -#include <rtems/rtems_bsdnet.h> -#include <rtems/rtems_mii_ioctl.h> - -#include <sys/param.h> -#include <sys/socket.h> -#include <sys/sockio.h> -#include <sys/mbuf.h> - -#include <net/if.h> -#include <net/if_arp.h> -#include <netinet/in.h> -#include <netinet/if_ether.h> -#include <netinet/in_systm.h> -#include <netinet/ip.h> - -#include <mpc55xx/edma.h> - -#include <bsp.h> -#include <bsp/irq.h> -#include <bsp/utility.h> -#include <libcpu/powerpc-utility.h> -#include <bsp/smsc9218i.h> - -#if MCLBYTES != 2048 - #warning "unexpected MCLBYTES value" -#endif - -#define ASSERT_SC(sc) assert((sc) == RTEMS_SUCCESSFUL) - -#define SMSC9218I_EVENT_TX RTEMS_EVENT_1 - -#define SMSC9218I_EVENT_TX_START RTEMS_EVENT_2 - -#define SMSC9218I_EVENT_TX_ERROR RTEMS_EVENT_3 - -#define SMSC9218I_EVENT_RX RTEMS_EVENT_4 - -#define SMSC9218I_EVENT_RX_ERROR RTEMS_EVENT_5 - -#define SMSC9218I_EVENT_PHY RTEMS_EVENT_6 - -#define SMSC9218I_EVENT_DMA RTEMS_EVENT_7 - -#define SMSC9218I_EVENT_DMA_ERROR RTEMS_EVENT_8 - -/* Adjust by two bytes for proper IP header alignment */ -#define SMSC9218I_RX_DATA_OFFSET 2 - -#define SMSC9218I_RX_JOBS 32 - -#define SMSC9218I_TX_JOBS 64 - -/* Maximum number of fragments per frame, see manual section 3.11.3.2 */ -#define SMSC9218I_TX_FRAGMENT_MAX 86 - -#if SMSC9218I_TX_JOBS > SMSC9218I_TX_FRAGMENT_MAX - #error "too many TX jobs" -#endif - -#define SMSC9218I_IRQ_CFG_GLOBAL_ENABLE \ - (SMSC9218I_IRQ_CFG_IRQ_EN | SMSC9218I_IRQ_CFG_IRQ_TYPE) - -#define SMSC9218I_IRQ_CFG_GLOBAL_DISABLE SMSC9218I_IRQ_CFG_IRQ_TYPE - -#define SMSC9218I_EDMA_RX_TCD_CDF 0x10004 - -#define SMSC9218I_EDMA_RX_TCD_BMF 0x10003 - -#define SMSC9218I_TCD_BMF_LINK 0x10011 - -#define SMSC9218I_TCD_BMF_LAST 0x10003 - -#define SMSC9218I_TCD_BMF_CLEAR 0x10000 - -#define SMSC9218I_ERROR_INTERRUPTS \ - (SMSC9218I_INT_TXSO \ - | SMSC9218I_INT_RWT \ - | SMSC9218I_INT_RXE \ - | SMSC9218I_INT_TXE) - -#define SMSC9218I_UNLIKELY(x) __builtin_expect((x), 0) - -#ifdef DEBUG - #define SMSC9218I_PRINTF(...) printf(__VA_ARGS__) - #define SMSC9218I_PRINTK(...) printk(__VA_ARGS__) -#else - #define SMSC9218I_PRINTF(...) - #define SMSC9218I_PRINTK(...) -#endif - -typedef enum { - SMSC9218I_NOT_INITIALIZED, - SMSC9218I_CONFIGURED, - SMSC9218I_STARTED, - SMSC9218I_RUNNING -} smsc9218i_state; - -static const char *const state_to_string [] = { - "NOT INITIALIZED", - "CONFIGURED", - "STARTED", - "RUNNING" -}; - -typedef struct { - struct arpcom arpcom; - struct rtems_mdio_info mdio; - smsc9218i_state state; - rtems_id receive_task; - rtems_id transmit_task; - edma_channel_context edma_receive; - edma_channel_context edma_transmit; - unsigned phy_interrupts; - unsigned received_frames; - unsigned receiver_errors; - unsigned receive_interrupts; - unsigned receive_dma_interrupts; - unsigned receive_too_long_errors; - unsigned receive_collision_errors; - unsigned receive_crc_errors; - unsigned receive_dma_errors; - unsigned receive_drop; - unsigned receive_watchdog_timeouts; - unsigned transmitted_frames; - unsigned transmitter_errors; - unsigned transmit_interrupts; - unsigned transmit_dma_interrupts; - unsigned transmit_status_overflows; - unsigned transmit_frame_errors; - unsigned transmit_dma_errors; -} smsc9218i_driver_entry; - -typedef struct { - uint32_t a; - uint32_t b; -} smsc9218i_transmit_command; - -typedef struct { - struct tcd_t command_tcd_table [SMSC9218I_TX_JOBS]; - struct tcd_t data_tcd_table [SMSC9218I_TX_JOBS]; - smsc9218i_transmit_command command_table [SMSC9218I_TX_JOBS]; - struct mbuf *fragment_table [SMSC9218I_TX_JOBS]; - struct mbuf *frame; - struct mbuf *next_fragment; - int empty_index; - int transfer_index; - int transfer_last_index; - int todo_index; - int empty; - int transfer; - int todo; - uint32_t frame_length; - uint32_t command_b; - uint16_t tag; - bool done; - unsigned frame_compact_count; -} smsc9218i_transmit_job_control; - -typedef struct { - struct tcd_t tcd_table [SMSC9218I_RX_JOBS]; - struct mbuf *mbuf_table [SMSC9218I_RX_JOBS]; - int consume; - int done; - int produce; -} smsc9218i_receive_job_control; - -static smsc9218i_receive_job_control smsc_rx_jc __attribute__((aligned (32))); - -static void smsc9218i_transmit_dma_done( - edma_channel_context *ctx, - uint32_t error_status -); - -static void smsc9218i_receive_dma_done( - edma_channel_context *ctx, - uint32_t error_status -); - -static smsc9218i_driver_entry smsc9218i_driver_data = { - .state = SMSC9218I_NOT_INITIALIZED, - .receive_task = RTEMS_ID_NONE, - .transmit_task = RTEMS_ID_NONE, - .edma_receive = { - .edma_tcd = EDMA_TCD_BY_CHANNEL_INDEX(SMSC9218I_EDMA_RX_CHANNEL), - .done = smsc9218i_receive_dma_done - }, - .edma_transmit = { - .edma_tcd = EDMA_TCD_BY_CHANNEL_INDEX(SMSC9218I_EDMA_TX_CHANNEL), - .done = smsc9218i_transmit_dma_done - } -}; - -static rtems_interval smsc9218i_timeout_init(void) -{ - return rtems_clock_get_ticks_since_boot(); -} - -static bool smsc9218i_timeout_not_expired(rtems_interval start) -{ - rtems_interval elapsed = rtems_clock_get_ticks_since_boot() - start; - - return elapsed < rtems_clock_get_ticks_per_second(); -} - -static bool smsc9218i_mac_wait(volatile smsc9218i_registers *regs) -{ - rtems_interval start = smsc9218i_timeout_init(); - bool busy; - - while ( - (busy = (regs->mac_csr_cmd & SMSC9218I_MAC_CSR_CMD_BUSY) != 0) - && smsc9218i_timeout_not_expired(start) - ) { - /* Wait */ - } - - return !busy; -} - -static bool smsc9218i_mac_write( - volatile smsc9218i_registers *regs, - uint32_t address, - uint32_t data -) -{ - bool ok = smsc9218i_mac_wait(regs); - - if (ok) { - regs->mac_csr_data = SMSC9218I_SWAP(data); - regs->mac_csr_cmd = SMSC9218I_MAC_CSR_CMD_BUSY - | SMSC9218I_MAC_CSR_CMD_ADDR(address); - ok = smsc9218i_mac_wait(regs); - } - - return ok; -} - -static uint32_t smsc9218i_mac_read( - volatile smsc9218i_registers *regs, - uint32_t address, - bool *ok_ptr -) -{ - uint32_t mac_csr_data = 0; - bool ok = smsc9218i_mac_wait(regs); - - if (ok) { - regs->mac_csr_cmd = SMSC9218I_MAC_CSR_CMD_BUSY - | SMSC9218I_MAC_CSR_CMD_READ - | SMSC9218I_MAC_CSR_CMD_ADDR(address); - ok = smsc9218i_mac_wait(regs); - - if (ok) { - mac_csr_data = regs->mac_csr_data; - } - } - - if (ok_ptr != NULL) { - *ok_ptr = ok; - } - - return SMSC9218I_SWAP(mac_csr_data); -} - -static bool smsc9218i_phy_wait(volatile smsc9218i_registers *regs) -{ - rtems_interval start = smsc9218i_timeout_init(); - uint32_t mac_mii_acc; - bool busy; - - do { - mac_mii_acc = smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_ACC, NULL); - } while ( - (busy = (mac_mii_acc & SMSC9218I_MAC_MII_ACC_BUSY) != 0) - && smsc9218i_timeout_not_expired(start) - ); - - return !busy; -} - -static bool smsc9218i_phy_write( - volatile smsc9218i_registers *regs, - uint32_t address, - uint32_t data -) -{ - bool ok = smsc9218i_phy_wait(regs); - - if (ok) { - ok = smsc9218i_mac_write(regs, SMSC9218I_MAC_MII_DATA, data); - - if (ok) { - ok = smsc9218i_mac_write( - regs, - SMSC9218I_MAC_MII_ACC, - SMSC9218I_MAC_MII_ACC_PHY_DEFAULT - | SMSC9218I_MAC_MII_ACC_BUSY - | SMSC9218I_MAC_MII_ACC_WRITE - | SMSC9218I_MAC_MII_ACC_ADDR(address) - ); - - if (ok) { - ok = smsc9218i_phy_wait(regs); - } - } - } - - return ok; -} - -static uint32_t smsc9218i_phy_read( - volatile smsc9218i_registers *regs, - uint32_t address -) -{ - smsc9218i_phy_wait(regs); - smsc9218i_mac_write( - regs, - SMSC9218I_MAC_MII_ACC, - SMSC9218I_MAC_MII_ACC_PHY_DEFAULT - | SMSC9218I_MAC_MII_ACC_BUSY - | SMSC9218I_MAC_MII_ACC_ADDR(address) - ); - smsc9218i_phy_wait(regs); - return smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_DATA, NULL); -} - -static bool smsc9218i_enable_promiscous_mode( - volatile smsc9218i_registers *regs, - bool enable -) -{ - bool ok; - uint32_t mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR, &ok); - - if (ok) { - uint32_t flags = SMSC9218I_MAC_CR_RXALL | SMSC9218I_MAC_CR_PRMS; - - if (enable) { - mac_cr |= flags; - } else { - mac_cr &= ~flags; - } - - ok = smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr); - } - - return ok; -} - -#if defined(DEBUG) -static void smsc9218i_register_dump(volatile smsc9218i_registers *regs) -{ - uint32_t reg = 0; - - reg = regs->id_rev; - printf( - "id_rev: 0x%08" PRIx32 " (ID = 0x%04" PRIx32 - ", revision = 0x%04" PRIx32 ")\n", - SMSC9218I_SWAP(reg), - SMSC9218I_ID_REV_GET_ID(reg), - SMSC9218I_ID_REV_GET_REV(reg) - ); - - reg = regs->irq_cfg; - printf("irq_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->int_sts; - printf("int_sts: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->int_en; - printf("int_en: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->byte_test; - printf("byte_test: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->fifo_int; - printf("fifo_int: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->rx_cfg; - printf("rx_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->tx_cfg; - printf("tx_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->hw_cfg; - printf("hw_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->rx_dp_ctl; - printf("rx_dp_ctl: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->rx_fifo_inf; - printf( - "rx_fifo_inf: 0x%08" PRIx32 " (status used = %" PRIu32 - ", data used = %" PRIu32 ")\n", - SMSC9218I_SWAP(reg), - SMSC9218I_RX_FIFO_INF_GET_SUSED(reg), - SMSC9218I_RX_FIFO_INF_GET_DUSED(reg) - ); - - reg = regs->tx_fifo_inf; - printf( - "tx_fifo_inf: 0x%08" PRIx32 " (status unused = %" PRIu32 - ", free = %" PRIu32 ")\n", - SMSC9218I_SWAP(reg), - SMSC9218I_TX_FIFO_INF_GET_SUSED(reg), - SMSC9218I_TX_FIFO_INF_GET_FREE(reg) - ); - - reg = regs->pmt_ctrl; - printf("pmt_ctrl: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->gpio_cfg; - printf("gpio_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->gpt_cfg; - printf("gpt_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->gpt_cnt; - printf("gpt_cnt: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->word_swap; - printf("word_swap: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->free_run; - printf("free_run: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->rx_drop; - printf("rx_drop: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - reg = regs->afc_cfg; - printf("afc_cfg: 0x%08" PRIx32 "\n", SMSC9218I_SWAP(reg)); - - printf("mac: cr: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_CR, NULL)); - printf("mac: addrh: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRH, NULL)); - printf("mac: addrl: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRL, NULL)); - printf("mac: hashh: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_HASHH, NULL)); - printf("mac: hashl: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_HASHL, NULL)); - printf("mac: mii_acc: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_ACC, NULL)); - printf("mac: mii_data: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_MII_DATA, NULL)); - printf("mac: flow: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_FLOW, NULL)); - printf("mac: vlan1: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_VLAN1, NULL)); - printf("mac: vlan2: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_VLAN2, NULL)); - printf("mac: wuff: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_WUFF, NULL)); - printf("mac: wucsr: 0x%08" PRIx32 "\n", smsc9218i_mac_read(regs, SMSC9218I_MAC_WUCSR, NULL)); - - printf("phy: bcr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_BMCR)); - printf("phy: bsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_BMSR)); - printf("phy: id1: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_PHYIDR1)); - printf("phy: id2: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_PHYIDR2)); - printf("phy: anar: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANAR)); - printf("phy: anlpar: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANLPAR)); - printf("phy: anexpr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, MII_ANER)); - printf("phy: mcsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_MCSR)); - printf("phy: spmodes: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_SPMODES)); - printf("phy: cisr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_CSIR)); - printf("phy: isr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_ISR)); - printf("phy: imr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_IMR)); - printf("phy: physcsr: 0x%08" PRIx32 "\n", smsc9218i_phy_read(regs, SMSC9218I_PHY_PHYSCSR)); -} -#endif - -static void smsc9218i_flush_tcd(struct tcd_t *tcd) -{ - ppc_data_cache_block_store(tcd); -} - -static uint32_t smsc9218i_align_up(uint32_t val) -{ - return 4U + ((val - 1U) & ~0x3U); -} - -static void smsc9218i_discard_frame( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs, - uint32_t rx_fifo_status, - uint32_t frame_length, - uint32_t data_length -) -{ - /* Update error counters */ - if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_TOO_LONG) != 0) { - ++e->receive_too_long_errors; - } - if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_COLLISION) != 0) { - ++e->receive_collision_errors; - } - if ((rx_fifo_status & SMSC9218I_RX_STS_ERROR_CRC) != 0) { - ++e->receive_crc_errors; - } - - /* Discard frame */ - if (frame_length > 16) { - /* Fast forward */ - regs->rx_dp_ctl = SMSC9218I_RX_DP_CTRL_FFWD; - - while ((regs->rx_dp_ctl & SMSC9218I_RX_DP_CTRL_FFWD) != 0) { - /* Wait */ - } - } else { - uint32_t len = data_length / 4; - uint32_t i = 0; - - /* Discard data */ - for (i = 0; i < len; ++i) { - regs->rx_fifo_data; - } - } -} - -static void smsc9218i_setup_receive_dma( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs, - smsc9218i_receive_job_control *jc -) -{ - int c = jc->consume; - int p = jc->produce; - int np = (p + 1) % SMSC9218I_RX_JOBS; - struct tcd_t *first = &jc->tcd_table [p]; - struct tcd_t *last = NULL; - - while (np != c) { - uint32_t rx_fifo_inf = 0; - uint32_t status_used = 0; - - /* Clear FIFO level status */ - regs->int_sts = SMSC9218I_INT_RSFL; - - /* Next FIFO status */ - rx_fifo_inf = regs->rx_fifo_inf; - status_used = SMSC9218I_RX_FIFO_INF_GET_SUSED(rx_fifo_inf); - - if (status_used > 0) { - uint32_t status = regs->rx_fifo_status; - uint32_t frame_length = SMSC9218I_RX_STS_GET_LENGTH(status); - uint32_t data_length = smsc9218i_align_up( - SMSC9218I_RX_DATA_OFFSET + frame_length - ); - bool frame_ok = (status & SMSC9218I_RX_STS_ERROR) == 0; - - if (frame_ok) { - struct mbuf *m = jc->mbuf_table [p]; - int mbuf_length = (int) frame_length - ETHER_HDR_LEN - ETHER_CRC_LEN; - struct tcd_t *current = &jc->tcd_table [p]; - - m->m_len = mbuf_length; - m->m_pkthdr.len = mbuf_length; - - current->NBYTES = data_length; - smsc9218i_flush_tcd(current); - - last = current; - p = np; - np = (p + 1) % SMSC9218I_RX_JOBS; - } else { - smsc9218i_discard_frame(e, regs, status, frame_length, data_length); - } - } else { - break; - } - } - - jc->produce = p; - - if (last != NULL) { - volatile struct tcd_t *channel = e->edma_receive.edma_tcd; - - /* Setup last TCD */ - last->BMF.R = SMSC9218I_TCD_BMF_LAST; - smsc9218i_flush_tcd(last); - ppc_synchronize_data(); - - /* Start eDMA transfer */ - channel->SADDR = first->SADDR; - channel->SDF.R = first->SDF.R; - channel->NBYTES = first->NBYTES; - channel->SLAST = first->SLAST; - channel->DADDR = first->DADDR; - channel->CDF.R = first->CDF.R; - channel->DLAST_SGA = first->DLAST_SGA; - channel->BMF.R = SMSC9218I_TCD_BMF_CLEAR; - channel->BMF.R = first->BMF.R; - } -} - -static void smsc9218i_receive_dma_done( - edma_channel_context *ctx, - uint32_t error_status -) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - smsc9218i_driver_entry *e = &smsc9218i_driver_data; - smsc9218i_receive_job_control *jc = &smsc_rx_jc; - - SMSC9218I_PRINTK( - "edma: id = 0x%08x, error status = 0x%08x\n", - channel_entry->id, - error_status - ); - - ++e->receive_dma_interrupts; - if (SMSC9218I_UNLIKELY(error_status != 0)) { - ++e->receive_dma_errors; - } - - sc = rtems_bsdnet_event_send(e->receive_task, SMSC9218I_EVENT_DMA); - ASSERT_SC(sc); - - jc->done = jc->produce; -} - -static void smsc9218i_transmit_dma_done( - edma_channel_context *ctx, - uint32_t error_status -) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - smsc9218i_driver_entry *e = &smsc9218i_driver_data; - rtems_event_set event = error_status == 0 ? - SMSC9218I_EVENT_DMA : SMSC9218I_EVENT_DMA_ERROR; - - SMSC9218I_PRINTK( - "edma: id = 0x%08x, error status = 0x%08x\n", - channel_entry->id, - error_status - ); - - ++e->transmit_dma_interrupts; - - sc = rtems_bsdnet_event_send(e->transmit_task, event); - ASSERT_SC(sc); -} - -static void smsc9218i_interrupt_handler(void *arg) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg; - volatile smsc9218i_registers *const regs = smsc9218i; - uint32_t int_en = regs->int_en; - uint32_t int_sts = regs->int_sts & int_en; - #ifdef DEBUG - uint32_t irq_cfg = regs->irq_cfg; - #endif - - /* Get interrupt status */ - SMSC9218I_PRINTK( - "interrupt: irq_cfg = 0x%08x, int_sts = 0x%08x, int_en = 0x%08x\n", - SMSC9218I_SWAP(irq_cfg), - SMSC9218I_SWAP(int_sts), - SMSC9218I_SWAP(int_en) - ); - - /* Disable module interrupts */ - regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_DISABLE; - - /* Clear external interrupt status */ - SIU.EISR.R = 1; - - /* Clear interrupts */ - regs->int_sts = int_sts; - - /* Error interrupts */ - if (SMSC9218I_UNLIKELY((int_sts & SMSC9218I_ERROR_INTERRUPTS) != 0)) { - if ((int_sts & SMSC9218I_INT_TXSO) != 0) { - ++e->transmit_status_overflows; - } - if ((int_sts & SMSC9218I_INT_RWT) != 0) { - ++e->receive_watchdog_timeouts; - } - if ((int_sts & SMSC9218I_INT_RXE) != 0) { - ++e->receiver_errors; - } - if ((int_sts & SMSC9218I_INT_TXE) != 0) { - ++e->transmitter_errors; - } - } - - /* Check receive interrupts */ - if ((int_sts & SMSC9218I_INT_RSFL) != 0) { - int_en &= ~SMSC9218I_INT_RSFL; - ++e->receive_interrupts; - - sc = rtems_bsdnet_event_send(e->receive_task, SMSC9218I_EVENT_RX); - ASSERT_SC(sc); - } - - /* Check PHY interrupts */ - if (SMSC9218I_UNLIKELY((int_sts & SMSC9218I_INT_PHY) != 0)) { - SMSC9218I_PRINTK("interrupt: phy\n"); - int_en &= ~SMSC9218I_INT_PHY; - ++e->phy_interrupts; - sc = rtems_bsdnet_event_send(e->receive_task, SMSC9218I_EVENT_PHY); - ASSERT_SC(sc); - } - - /* Check transmit interrupts */ - if ((int_sts & SMSC9218I_INT_TDFA) != 0) { - SMSC9218I_PRINTK("interrupt: transmit\n"); - int_en &= ~SMSC9218I_INT_TDFA; - ++e->transmit_interrupts; - sc = rtems_bsdnet_event_send(e->transmit_task, SMSC9218I_EVENT_TX); - ASSERT_SC(sc); - } - - /* Update interrupt enable */ - regs->int_en = int_en; - - /* Enable module interrupts */ - regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_ENABLE; -} - -static void smsc9218i_enable_transmit_interrupts( - volatile smsc9218i_registers *regs -) -{ - rtems_interrupt_level level; - - rtems_interrupt_disable(level); - regs->int_en |= SMSC9218I_INT_TDFA; - rtems_interrupt_enable(level); -} - -static void smsc9218i_enable_phy_interrupts( - volatile smsc9218i_registers *regs -) -{ - rtems_interrupt_level level; - - rtems_interrupt_disable(level); - regs->int_en |= SMSC9218I_INT_PHY; - rtems_interrupt_enable(level); -} - -static void smsc9218i_phy_clear_interrupts( - volatile smsc9218i_registers *regs -) -{ - smsc9218i_phy_read(regs, SMSC9218I_PHY_ISR); -} - -static bool smsc9218i_media_status(smsc9218i_driver_entry *e, int *media) -{ - struct ifnet *ifp = &e->arpcom.ac_if; - - *media = IFM_MAKEWORD(0, 0, 0, SMSC9218I_MAC_MII_ACC_PHY_DEFAULT); - - return (*ifp->if_ioctl)(ifp, SIOCGIFMEDIA, (caddr_t) media) == 0; -} - -static void smsc9218i_media_status_change( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs -) -{ - int media = 0; - bool media_ok = false; - uint32_t mac_cr = 0; - - smsc9218i_phy_clear_interrupts(regs); - smsc9218i_enable_phy_interrupts(regs); - - media_ok = smsc9218i_media_status(e, &media); - mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR, NULL); - if (media_ok && (IFM_OPTIONS(media) & IFM_FDX) == 0) { - mac_cr &= ~SMSC9218I_MAC_CR_FDPX; - } else { - mac_cr |= SMSC9218I_MAC_CR_FDPX; - } - smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr); -} - -static bool smsc9218i_new_mbuf( - struct ifnet *ifp, - smsc9218i_receive_job_control *jc, - int i, - struct mbuf *old_m -) -{ - bool ok = false; - int wait = old_m != NULL ? M_DONTWAIT : M_WAIT; - struct mbuf *new_m = m_gethdr(wait, MT_DATA); - struct tcd_t *tcd = &jc->tcd_table [i]; - char *data = NULL; - - if (new_m != NULL ) { - new_m->m_pkthdr.rcvif = ifp; - MCLGET(new_m, wait); - - if ((new_m->m_flags & M_EXT) != 0) { - ok = true; - } else { - m_free(new_m); - new_m = old_m; - } - } else { - new_m = old_m; - } - - data = mtod(new_m, char *); - new_m->m_data = data + SMSC9218I_RX_DATA_OFFSET + ETHER_HDR_LEN; - - jc->mbuf_table [i] = new_m; - - tcd->DADDR = (uint32_t) data; - tcd->BMF.R = SMSC9218I_TCD_BMF_LINK; - - /* FIXME: This is maybe a problem in case of a lot of small frames */ - rtems_cache_invalidate_multiple_data_lines( - data, - SMSC9218I_RX_DATA_OFFSET + ETHER_HDR_LEN + ETHERMTU + ETHER_CRC_LEN - ); - - return ok; -} - -static void smsc9218i_init_receive_jobs( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs, - volatile smsc9218i_registers *regs_dma, - smsc9218i_receive_job_control *jc -) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - struct ifnet *ifp = &e->arpcom.ac_if; - int i = 0; - - /* Obtain receive eDMA channel */ - sc = mpc55xx_edma_obtain_channel( - &e->edma_receive, - MPC55XX_INTC_DEFAULT_PRIORITY - ); - ASSERT_SC(sc); - - for (i = 0; i < SMSC9218I_RX_JOBS; ++i) { - struct tcd_t *tcd = &jc->tcd_table [i]; - struct tcd_t *next_tcd = &jc->tcd_table [(i + 1) % SMSC9218I_RX_JOBS]; - - tcd->SADDR = (uint32_t) ®s_dma->rx_fifo_data; - tcd->SDF.B.SSIZE = 0x2; - tcd->SDF.B.DSIZE = 0x2; - tcd->CDF.B.CITER = 1; - tcd->CDF.B.DOFF = 4; - tcd->DLAST_SGA = (int32_t) next_tcd; - - smsc9218i_new_mbuf(ifp, jc, i, NULL); - } -} - -static void smsc9218i_ether_input( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs, - smsc9218i_receive_job_control *jc -) -{ - rtems_interrupt_level level; - struct ifnet *ifp = &e->arpcom.ac_if; - int c = jc->consume; - int d = jc->done; - - while (c != d) { - struct mbuf *m = jc->mbuf_table [c]; - struct ether_header *eh = (struct ether_header *) - (mtod(m, char *) - ETHER_HDR_LEN); - - ++e->received_frames; - if (smsc9218i_new_mbuf(ifp, jc, c, m)) { - ether_input(ifp, eh, m); - } - - c = (c + 1) % SMSC9218I_RX_JOBS; - } - - jc->consume = c; - - rtems_interrupt_disable(level); - /* Enabling the receive interrupts while the DMA is active leads to chaos */ - if (c == jc->produce) { - regs->int_en |= SMSC9218I_INT_RSFL; - } - rtems_interrupt_enable(level); -} - -static void smsc9218i_receive_task(void *arg) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - rtems_interrupt_level level; - smsc9218i_receive_job_control *jc = &smsc_rx_jc; - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg; - volatile smsc9218i_registers *const regs = smsc9218i; - volatile smsc9218i_registers *const regs_dma = smsc9218i_dma; - uint32_t mac_cr = 0; - - smsc9218i_init_receive_jobs(e, regs, regs_dma, jc); - - /* Configure receiver */ - regs->rx_cfg = SMSC9218I_RX_CFG_END_ALIGN_4 - | SMSC9218I_RX_CFG_DOFF(SMSC9218I_RX_DATA_OFFSET); - - /* Enable MAC receiver */ - mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR, NULL); - mac_cr |= SMSC9218I_MAC_CR_RXEN; - smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr); - - /* Enable receive interrupts */ - rtems_interrupt_disable(level); - regs->int_en |= SMSC9218I_INT_RSFL; - rtems_interrupt_enable(level); - - /* Enable PHY interrupts */ - smsc9218i_phy_write( - regs, - SMSC9218I_PHY_IMR, - SMSC9218I_PHY_IMR_INT4 | SMSC9218I_PHY_IMR_INT6 - ); - smsc9218i_enable_phy_interrupts(regs); - - while (true) { - rtems_event_set events; - - sc = rtems_bsdnet_event_receive( - SMSC9218I_EVENT_DMA | SMSC9218I_EVENT_PHY | SMSC9218I_EVENT_RX, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, - &events - ); - ASSERT_SC(sc); - - if ((events & (SMSC9218I_EVENT_RX | SMSC9218I_EVENT_DMA)) != 0) { - smsc9218i_setup_receive_dma(e, regs, jc); - } - - if ((events & SMSC9218I_EVENT_DMA) != 0) { - smsc9218i_ether_input(e, regs, jc); - } - - if ((events & SMSC9218I_EVENT_PHY) != 0) { - smsc9218i_media_status_change(e, regs); - } - } -} - -#if defined(DEBUG) -static void smsc9218i_transmit_job_dump( - smsc9218i_transmit_job_control *jc, - const char *msg -) -{ - char out [SMSC9218I_TX_JOBS + 1]; - int c = 0; - int s = 0; - - out [SMSC9218I_TX_JOBS] = '\0'; - - memset(out, '?', SMSC9218I_TX_JOBS); - - c = jc->todo_index; - s = jc->empty; - while (s > 0) { - out [c] = 'E'; - --s; - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - c = jc->transfer_index; - s = jc->todo; - while (s > 0) { - out [c] = 'T'; - --s; - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - c = jc->empty_index; - s = jc->transfer; - while (s > 0) { - out [c] = 'D'; - --s; - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - printf( - "tx: %s: %02u:%02u:%02u %s\n", - msg, - jc->empty, - jc->todo, - jc->transfer, - out - ); -} -#endif /* defined(DEBUG) */ - -static struct mbuf *smsc9218i_compact_frame( - smsc9218i_transmit_job_control *jc, - uint32_t frame_length -) -{ - struct mbuf *old_m = jc->frame; - struct mbuf *new_m = m_gethdr(M_WAIT, MT_DATA); - char *data = NULL; - - ++jc->frame_compact_count; - - MCLGET(new_m, M_WAIT); - data = mtod(new_m, char *); - - new_m->m_len = (int) frame_length; - new_m->m_pkthdr.len = (int) frame_length; - - while (old_m != NULL) { - size_t len = (size_t) old_m->m_len; - memcpy(data, mtod(old_m, void *), len); - data += len; - old_m = m_free(old_m); - } - - jc->frame = new_m; - - return new_m; -} - -static struct mbuf *smsc9218i_next_transmit_fragment( - struct ifnet *ifp, - smsc9218i_transmit_job_control *jc -) -{ - struct mbuf *m = jc->next_fragment; - - if (m != NULL) { - /* Next fragment is from the current frame */ - jc->next_fragment = m->m_next; - } else { - /* Dequeue first fragment of the next frame */ - IF_DEQUEUE(&ifp->if_snd, m); - - /* Update frame head */ - jc->frame = m; - - if (m != NULL) { - struct mbuf *n = m; - struct mbuf *p = NULL; - uint32_t frame_length = 0; - unsigned fragments = 0; - bool tiny = false; - - /* Calculate frame length and fragment number */ - do { - int len = n->m_len; - - if (len > 0) { - ++fragments; - frame_length += (uint32_t) len; - tiny = tiny || len < 4; - - /* Next fragment */ - p = n; - n = n->m_next; - } else { - /* Discard empty fragment and get next */ - n = m_free(n); - - /* Remove fragment from list */ - if (p != NULL) { - p->m_next = n; - } else { - jc->frame = n; - } - } - } while (n != NULL); - - if (SMSC9218I_UNLIKELY(tiny || fragments > SMSC9218I_TX_JOBS)) { - fragments = 1; - m = smsc9218i_compact_frame(jc, frame_length); - } - - /* Set frame length */ - jc->frame_length = frame_length; - - /* Update next fragment */ - jc->next_fragment = jc->frame->m_next; - - /* Update tag */ - ++jc->tag; - - /* Command B */ - jc->command_b = ((uint32_t) SMSC9218I_TX_B_TAG(jc->tag)) - | SMSC9218I_TX_B_FRAME_LENGTH(jc->frame_length); - - SMSC9218I_PRINTF( - "tx: next frame: tag = %i, frame length = %" PRIu32 - ", fragments = %u\n", - jc->tag, - frame_length, - fragments - ); - } else { - /* The transmit queue is empty, we have no next fragment */ - jc->next_fragment = NULL; - - /* Interface is now inactive */ - ifp->if_flags &= ~IFF_OACTIVE; - - /* Transmit task may wait for events */ - jc->done = true; - - SMSC9218I_PRINTF("tx: inactive\n"); - } - } - - return m; -} - -static void smsc9218i_transmit_create_jobs( - smsc9218i_transmit_job_control *jc, - volatile smsc9218i_registers *const regs, - struct ifnet *ifp -) -{ - int n = jc->empty; - - if (n > 0) { - int c = jc->todo_index; - int i = 0; - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "create"); - #endif - - for (i = 0; i < n; ++i) { - struct mbuf *m = smsc9218i_next_transmit_fragment(ifp, jc); - - if (m != NULL) { - uint32_t first = m == jc->frame ? SMSC9218I_TX_A_FIRST : 0; - uint32_t last = m->m_next == NULL ? SMSC9218I_TX_A_LAST : 0; - uint32_t fragment_length = (uint32_t) m->m_len; - uint32_t fragment_misalign = (uint32_t) (mtod(m, uintptr_t) % 4); - uint32_t data_length = fragment_misalign + fragment_length; - uint32_t data_misalign = data_length % 4; - uint32_t data = (uint32_t) (mtod(m, char *) - fragment_misalign); - - /* Align data length on four byte boundary */ - if (data_misalign > 0) { - data_length += 4 - data_misalign; - } - - /* Cache flush for fragment data */ - rtems_cache_flush_multiple_data_lines( - (const void *) data, - data_length - ); - - /* Remember fragement */ - jc->fragment_table [c] = m; - - /* Command A and B */ - jc->command_table [c].a = SMSC9218I_TX_A_END_ALIGN_4 - | SMSC9218I_TX_A_DOFF(fragment_misalign) - | SMSC9218I_TX_A_FRAGMENT_LENGTH(fragment_length) - | first - | last; - jc->command_table [c].b = jc->command_b; - - /* Data TCD */ - jc->data_tcd_table [c].SADDR = data; - jc->data_tcd_table [c].NBYTES = data_length; - - SMSC9218I_PRINTF("tx: create: index = %u\n", c); - } else { - /* Nothing to do */ - break; - } - - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - /* Increment jobs to do */ - jc->todo += i; - - /* Decrement empty count */ - jc->empty -= i; - - /* Update todo index */ - jc->todo_index = c; - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "create done"); - #endif - } else { - /* Transmit task may wait for events */ - jc->done = true; - } -} - -static void smsc9218i_transmit_do_jobs( - smsc9218i_transmit_job_control *jc, - volatile smsc9218i_registers *const regs, - smsc9218i_driver_entry *e -) -{ - if (jc->transfer == 0) { - uint32_t tx_fifo_inf = regs->tx_fifo_inf; - uint32_t data_free = SMSC9218I_TX_FIFO_INF_GET_FREE(tx_fifo_inf); - int c = jc->transfer_index; - int last_index = c; - int i = 0; - int n = jc->todo; - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "transfer"); - #endif - - for (i = 0; i < n; ++i) { - struct tcd_t *tcd = &jc->data_tcd_table [c]; - uint32_t data_length = tcd->NBYTES + 14; - - if (data_length <= data_free) { - /* Reduce free FIFO space */ - data_free -= data_length; - - /* Index of last TCD */ - last_index = c; - - /* Cache flush for data TCD */ - smsc9218i_flush_tcd(tcd); - } else { - break; - } - - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - if (i > 0) { - volatile struct tcd_t *channel = e->edma_transmit.edma_tcd; - struct tcd_t *start = &jc->command_tcd_table [jc->transfer_index]; - struct tcd_t *last = &jc->data_tcd_table [last_index]; - - /* New transfer index */ - jc->transfer_index = c; - - /* New last transfer index */ - jc->transfer_last_index = last_index; - - /* Set jobs in transfer */ - jc->transfer = i; - - /* Decrement jobs to do */ - jc->todo -= i; - - /* Cache flush for command table */ - rtems_cache_flush_multiple_data_lines( - jc->command_table, - sizeof(jc->command_table) - ); - - /* Enable interrupt for last data TCD */ - last->BMF.R = SMSC9218I_TCD_BMF_LAST; - smsc9218i_flush_tcd(last); - ppc_synchronize_data(); - - /* Start eDMA transfer */ - channel->SADDR = start->SADDR; - channel->SDF.R = start->SDF.R; - channel->NBYTES = start->NBYTES; - channel->SLAST = start->SLAST; - channel->DADDR = start->DADDR; - channel->CDF.R = start->CDF.R; - channel->DLAST_SGA = start->DLAST_SGA; - channel->BMF.R = SMSC9218I_TCD_BMF_CLEAR; - channel->BMF.R = start->BMF.R; - - /* Transmit task may wait for events */ - jc->done = true; - } else if (n > 0) { - /* We need to wait until the FIFO has more free space */ - smsc9218i_enable_transmit_interrupts(regs); - - /* Transmit task may wait for events */ - jc->done = true; - } - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "transfer done"); - #endif - } -} - -static void smsc9218i_transmit_finish_jobs( - smsc9218i_transmit_job_control *jc, - volatile smsc9218i_registers *const regs, - smsc9218i_driver_entry *e, - rtems_event_set events -) -{ - uint32_t tx_fifo_inf = regs->tx_fifo_inf; - uint32_t status_used = SMSC9218I_TX_FIFO_INF_GET_SUSED(tx_fifo_inf); - uint32_t s = 0; - int n = jc->transfer; - - for (s = 0; s < status_used; ++s) { - uint32_t tx_fifo_status = regs->tx_fifo_status; - - if ((tx_fifo_status & SMSC9218I_TX_STS_ERROR) == 0) { - ++e->transmitted_frames; - } else { - ++e->transmit_frame_errors; - } - - SMSC9218I_PRINTF( - "tx: transmit status: tag = %" PRIu32 ", status = 0x%08" PRIx32 "\n", - SMSC9218I_TX_STS_GET_TAG(tx_fifo_status), - SMSC9218I_SWAP(tx_fifo_status) - ); - } - - if ( - (events & (SMSC9218I_EVENT_DMA | SMSC9218I_EVENT_DMA_ERROR)) != 0 - && n > 0 - ) { - int c = jc->empty_index; - int i = 0; - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "finish"); - #endif - - if ((events & SMSC9218I_EVENT_DMA_ERROR) != 0) { - ++e->transmit_dma_errors; - } - - /* Restore last data TCD */ - jc->data_tcd_table [jc->transfer_last_index].BMF.R = - SMSC9218I_TCD_BMF_LINK; - - for (i = 0; i < n; ++i) { - /* Free fragment buffer */ - m_free(jc->fragment_table [c]); - - c = (c + 1) % SMSC9218I_TX_JOBS; - } - - /* Increment empty count */ - jc->empty += n; - - /* New empty index */ - jc->empty_index = jc->transfer_index; - - /* Clear jobs in transfer */ - jc->transfer = 0; - - /* Update empty index */ - jc->empty_index = c; - - #ifdef DEBUG - smsc9218i_transmit_job_dump(jc, "finish done"); - #endif - } -} - -/* FIXME */ -static smsc9218i_transmit_job_control smsc_tx_jc __attribute__((aligned (32))) = { - .frame = NULL, - .next_fragment = NULL, - .frame_length = 0, - .tag = 0, - .command_b = 0, - .done = false, - .empty_index = 0, - .transfer_index = 0, - .todo_index = 0, - .empty = SMSC9218I_TX_JOBS, - .transfer = 0, - .todo = 0 -}; - -static void smsc9218i_transmit_task(void *arg) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - rtems_event_set events = 0; - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg; - struct ifnet *ifp = &e->arpcom.ac_if; - volatile smsc9218i_registers *const regs = smsc9218i; - volatile smsc9218i_registers *const regs_dma = smsc9218i_dma; - uint32_t mac_cr = 0; - smsc9218i_transmit_job_control *jc = &smsc_tx_jc; - unsigned i = 0; - - SMSC9218I_PRINTF("%s\n", __func__); - - /* Obtain transmit eDMA channel */ - sc = mpc55xx_edma_obtain_channel( - &e->edma_transmit, - MPC55XX_INTC_DEFAULT_PRIORITY - ); - ASSERT_SC(sc); - - /* Setup transmit eDMA descriptors */ - for (i = 0; i < SMSC9218I_TX_JOBS; ++i) { - unsigned ii = (i + 1) % SMSC9218I_TX_JOBS; - struct tcd_t tcd = EDMA_TCD_DEFAULT; - struct tcd_t *command_tcd = &jc->command_tcd_table [i]; - struct tcd_t *data_tcd = &jc->data_tcd_table [i]; - struct tcd_t *next_command_tcd = &jc->command_tcd_table [ii]; - - tcd.SDF.B.SSIZE = 2; - tcd.SDF.B.SOFF = 4; - tcd.SDF.B.DSIZE = 2; - tcd.CDF.B.CITER = 1; - tcd.BMF.R = SMSC9218I_TCD_BMF_LINK; - - tcd.DADDR = (uint32_t) ®s_dma->tx_fifo_data; - tcd.DLAST_SGA = (int32_t) next_command_tcd; - *data_tcd = tcd; - - tcd.DADDR = (uint32_t) ®s->tx_fifo_data; - tcd.SADDR = (uint32_t) &jc->command_table [i].a; - tcd.NBYTES = 8; - tcd.DLAST_SGA = (int32_t) data_tcd; - *command_tcd = tcd; - } - - /* - * Cache flush for command TCD. The content of the command TCD remains - * invariant. - */ - rtems_cache_flush_multiple_data_lines( - jc->command_tcd_table, - sizeof(jc->command_tcd_table) - ); - - /* Configure transmitter */ - regs->tx_cfg = SMSC9218I_TX_CFG_SAO | SMSC9218I_TX_CFG_ON; - - /* Enable MAC transmitter */ - mac_cr = smsc9218i_mac_read(regs, SMSC9218I_MAC_CR, NULL) | SMSC9218I_MAC_CR_TXEN; - smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, mac_cr); - - /* Main event loop */ - while (true) { - /* Wait for events */ - sc = rtems_bsdnet_event_receive( - SMSC9218I_EVENT_TX - | SMSC9218I_EVENT_TX_START - | SMSC9218I_EVENT_DMA - | SMSC9218I_EVENT_DMA_ERROR, - RTEMS_EVENT_ANY | RTEMS_WAIT, - RTEMS_NO_TIMEOUT, - &events - ); - ASSERT_SC(sc); - - SMSC9218I_PRINTF("tx: wake up: events = 0x%08" PRIx32 "\n", events); - - do { - jc->done = false; - smsc9218i_transmit_finish_jobs(jc, regs, e, events); - smsc9218i_transmit_create_jobs(jc, regs, ifp); - smsc9218i_transmit_do_jobs(jc, regs, e); - } while (!jc->done); - - SMSC9218I_PRINTF("tx: done\n"); - } - - /* Release network semaphore */ - rtems_bsdnet_semaphore_release(); - - /* Terminate self */ - rtems_task_exit(); -} - -#if defined(DEBUG) -static void smsc9218i_test_macros(void) -{ - unsigned i = 0; - unsigned byte_test = 0x87654321U; - unsigned val8 = 0xa5; - unsigned val16 = 0xa55a; - int r = 0; - - r = SMSC9218I_SWAP(SMSC9218I_BYTE_TEST) == byte_test; - printf("[%i] SMSC9218I_SWAP\n", r); - - for (i = 0; i < 32; ++i) { - r = SMSC9218I_SWAP(SMSC9218I_FLAG(i)) == (1U << i); - printf("[%i] flag: %u\n", r, i); - } - - for (i = 0; i < 32; i += 8) { - r = SMSC9218I_SWAP(SMSC9218I_FIELD_8(val8, i)) == (val8 << i); - printf("[%i] field 8: %u\n", r, i); - } - - for (i = 0; i < 32; i += 16) { - r = SMSC9218I_SWAP(SMSC9218I_FIELD_16(val16, i)) == (val16 << i); - printf("[%i] field 16: %u\n", r, i); - } - - for (i = 0; i < 32; i += 8) { - r = SMSC9218I_GET_FIELD_8(SMSC9218I_BYTE_TEST, i) - == ((byte_test >> i) & 0xffU); - printf("[%i] get field 8: %u\n", r, i); - } - - for (i = 0; i < 32; i += 16) { - r = SMSC9218I_GET_FIELD_16(SMSC9218I_BYTE_TEST, i) - == ((byte_test >> i) & 0xffffU); - printf("[%i] get field 16: %u\n", r, i); - } -} -#endif - -static bool smsc9218i_wait_for_eeprom_access( - volatile smsc9218i_registers *regs -) -{ - rtems_interval start = smsc9218i_timeout_init(); - bool busy; - - while ( - (busy = (regs->e2p_cmd & SMSC9218I_E2P_CMD_EPC_BUSY) != 0) - && smsc9218i_timeout_not_expired(start) - ) { - /* Wait */ - } - - return !busy; -} - -static bool smsc9218i_get_mac_address( - volatile smsc9218i_registers *regs, - uint8_t address [6] -) -{ - bool ok = false; - - uint32_t low = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRL, &ok); - address [0] = (uint8_t) low; - address [1] = (uint8_t) (low >> 8) & 0xff; - address [2] = (uint8_t) (low >> 16); - address [3] = (uint8_t) (low >> 24); - - if (ok) { - uint32_t high = smsc9218i_mac_read(regs, SMSC9218I_MAC_ADDRH, &ok); - address [4] = (uint8_t) high; - address [5] = (uint8_t) (high >> 8); - } - - return ok; -} - -static bool smsc9218i_set_mac_address( - volatile smsc9218i_registers *regs, - const uint8_t address [6] -) -{ - bool ok = smsc9218i_mac_write( - regs, - SMSC9218I_MAC_ADDRL, - ((uint32_t) address [3] << 24) | ((uint32_t) address [2] << 16) - | ((uint32_t) address [1] << 8) | (uint32_t) address [0] - ); - - if (ok) { - ok = smsc9218i_mac_write( - regs, - SMSC9218I_MAC_ADDRH, - ((uint32_t) address [5] << 8) | (uint32_t) address [4] - ); - } - - return ok; -} - -/* Sometimes the write of the MAC address was not reliable */ -static bool smsc9218i_set_and_verify_mac_address( - volatile smsc9218i_registers *regs, - const uint8_t address [6] -) -{ - bool ok = true; - int i; - - for (i = 0; ok && i < 3; ++i) { - ok = smsc9218i_set_mac_address(regs, address); - - if (ok) { - uint8_t actual_address [6]; - - ok = smsc9218i_get_mac_address(regs, actual_address) - && memcmp(address, actual_address, sizeof(actual_address)) == 0; - } - } - - return ok; -} - -#if defined(DEBUG) -static void smsc9218i_mac_address_dump(volatile smsc9218i_registers *regs) -{ - uint8_t mac_address [6]; - bool ok = smsc9218i_get_mac_address(regs, mac_address); - - if (ok) { - printf( - "MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", - mac_address [0], - mac_address [1], - mac_address [2], - mac_address [3], - mac_address [4], - mac_address [5] - ); - } else { - printf("cannot read MAC address\n"); - } -} -#endif - -static void smsc9218i_interrupt_init( - smsc9218i_driver_entry *e, - volatile smsc9218i_registers *regs -) -{ -#ifdef SMSC9218I_IRQ_PIN - rtems_status_code sc = RTEMS_SUCCESSFUL; - union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS; - union SIU_DIRER_tag direr = MPC55XX_ZERO_FLAGS; - union SIU_DIRSR_tag dirsr = MPC55XX_ZERO_FLAGS; - union SIU_ORER_tag orer = MPC55XX_ZERO_FLAGS; - union SIU_IREER_tag ireer = MPC55XX_ZERO_FLAGS; - union SIU_IFEER_tag ifeer = MPC55XX_ZERO_FLAGS; - union SIU_IDFR_tag idfr = MPC55XX_ZERO_FLAGS; - rtems_interrupt_level level; - - /* Configure IRQ input pin */ - pcr.B.PA = 2; - pcr.B.OBE = 0; - pcr.B.IBE = 1; -#if MPC55XX_CHIP_FAMILY != 551 - pcr.B.DSC = 0; -#endif - pcr.B.ODE = 0; - pcr.B.HYS = 0; - pcr.B.SRC = 0; - pcr.B.WPE = 0; - pcr.B.WPS = 1; - SIU.PCR [SMSC9218I_IRQ_PIN].R = pcr.R; - - /* DMA/Interrupt Request Select */ - rtems_interrupt_disable(level); - dirsr.R = SIU.DIRSR.R; -#if MPC55XX_CHIP_FAMILY != 551 - dirsr.B.DIRS0 = 0; -#endif - SIU.DIRSR.R = dirsr.R; - rtems_interrupt_enable(level); - - /* Overrun Request Enable */ - rtems_interrupt_disable(level); - orer.R = SIU.ORER.R; - orer.B.ORE0 = 0; - SIU.ORER.R = orer.R; - rtems_interrupt_enable(level); - - /* IRQ Rising-Edge Enable */ - rtems_interrupt_disable(level); - ireer.R = SIU.IREER.R; - ireer.B.IREE0 = 0; - SIU.IREER.R = ireer.R; - rtems_interrupt_enable(level); - - /* IRQ Falling-Edge Enable */ - rtems_interrupt_disable(level); - ifeer.R = SIU.IFEER.R; - ifeer.B.IFEE0 = 1; - SIU.IFEER.R = ifeer.R; - rtems_interrupt_enable(level); - - /* IRQ Digital Filter */ - rtems_interrupt_disable(level); - idfr.R = SIU.IDFR.R; - idfr.B.DFL = 0; - SIU.IDFR.R = idfr.R; - rtems_interrupt_enable(level); - - /* Clear external interrupt status */ - SIU.EISR.R = 1; - - /* DMA/Interrupt Request Enable */ - rtems_interrupt_disable(level); - direr.R = SIU.DIRER.R; - direr.B.EIRE0 = 1; - SIU.DIRER.R = direr.R; - rtems_interrupt_enable(level); - - /* Install interrupt handler */ - sc = rtems_interrupt_handler_install( - MPC55XX_IRQ_SIU_EXTERNAL_0, - "SMSC9218i", - RTEMS_INTERRUPT_UNIQUE, - smsc9218i_interrupt_handler, - e - ); - ASSERT_SC(sc); - - /* Enable interrupts and use push-pull driver (active low) */ - regs->irq_cfg = SMSC9218I_IRQ_CFG_GLOBAL_ENABLE; - - /* Enable error interrupts */ - regs->int_en = SMSC9218I_ERROR_INTERRUPTS; -#endif -} - -static void smsc9218i_reset_signal(bool signal) -{ -#ifdef SMSC9218I_RESET_PIN - SIU.GPDO [SMSC9218I_RESET_PIN].R = signal ? 1 : 0; -#endif -} - -static void smsc9218i_reset_signal_init(void) -{ -#ifdef SMSC9218I_RESET_PIN - union SIU_PCR_tag pcr = MPC55XX_ZERO_FLAGS; - - smsc9218i_reset_signal(true); - - pcr.B.PA = 0; - pcr.B.OBE = 1; - pcr.B.IBE = 0; -#if MPC55XX_CHIP_FAMILY != 551 - pcr.B.DSC = 0; -#endif - pcr.B.ODE = 0; - pcr.B.HYS = 0; - pcr.B.SRC = 0; - pcr.B.WPE = 1; - pcr.B.WPS = 1; - - SIU.PCR [SMSC9218I_RESET_PIN].R = pcr.R; -#endif -} - -static bool smsc9218i_hardware_reset(volatile smsc9218i_registers *regs) -{ - rtems_interval start = smsc9218i_timeout_init(); - bool not_ready; - - smsc9218i_reset_signal_init(); - smsc9218i_reset_signal(false); - rtems_bsp_delay(200); - smsc9218i_reset_signal(true); - - while ( - (not_ready = (regs->pmt_ctrl & SMSC9218I_PMT_CTRL_READY) == 0) - && smsc9218i_timeout_not_expired(start) - ) { - /* Wait */ - } - - return !not_ready; -} - -static void smsc9218i_interface_init(void *arg) -{ - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) arg; - struct ifnet *ifp = &e->arpcom.ac_if; - volatile smsc9218i_registers *const regs = smsc9218i; - bool ok = true; - - SMSC9218I_PRINTF("%s\n", __func__); - - if (e->state == SMSC9218I_CONFIGURED) { - ok = smsc9218i_hardware_reset(regs); - if (ok) { - e->state = SMSC9218I_NOT_INITIALIZED; - } - } - - if (e->state == SMSC9218I_NOT_INITIALIZED) { -#if defined(DEBUG) - smsc9218i_register_dump(regs); -#endif - - /* Set hardware configuration */ - regs->hw_cfg = SMSC9218I_HW_CFG_MBO | SMSC9218I_HW_CFG_TX_FIF_SZ(5); - - ok = smsc9218i_wait_for_eeprom_access(regs); - - if (ok) { - ok = smsc9218i_set_and_verify_mac_address(regs, e->arpcom.ac_enaddr); - - if (ok) { -#if defined(DEBUG) - smsc9218i_mac_address_dump(regs); -#endif - - /* Auto-negotiation advertisment */ - ok = smsc9218i_phy_write( - regs, - MII_ANAR, - ANAR_TX_FD | ANAR_TX | ANAR_10_FD | ANAR_10 | ANAR_CSMA - ); - - if (ok) { -#ifdef SMSC9218I_ENABLE_LED_OUTPUTS - regs->gpio_cfg = SMSC9218I_HW_CFG_LED_1 - | SMSC9218I_HW_CFG_LED_2 - | SMSC9218I_HW_CFG_LED_3; -#endif - - smsc9218i_interrupt_init(e, regs); - - /* Set MAC control */ - ok = smsc9218i_mac_write(regs, SMSC9218I_MAC_CR, SMSC9218I_MAC_CR_FDPX); - if (ok) { - /* Set FIFO interrupts */ - regs->fifo_int = SMSC9218I_FIFO_INT_TDAL(32); - - /* Clear receive drop counter */ - regs->rx_drop; - - /* Start receive task */ - if (e->receive_task == RTEMS_ID_NONE) { - e->receive_task = rtems_bsdnet_newproc( - "ntrx", - 4096, - smsc9218i_receive_task, - e - ); - } - - /* Start transmit task */ - if (e->transmit_task == RTEMS_ID_NONE) { - e->transmit_task = rtems_bsdnet_newproc( - "nttx", - 4096, - smsc9218i_transmit_task, - e - ); - } - - /* Change state */ - if (e->receive_task != RTEMS_ID_NONE - && e->transmit_task != RTEMS_ID_NONE) { - e->state = SMSC9218I_STARTED; - } - } - } - } - } - } - - if (e->state == SMSC9218I_STARTED) { - /* Enable promiscous mode */ - ok = smsc9218i_enable_promiscous_mode( - regs, - (ifp->if_flags & IFF_PROMISC) != 0 - ); - - if (ok) { - /* Set interface to running state */ - ifp->if_flags |= IFF_RUNNING; - - /* Change state */ - e->state = SMSC9218I_RUNNING; - } - } -} - -static int smsc9218i_mdio_read( - int phy, - void *arg, - unsigned phy_reg, - uint32_t *val -) -{ - volatile smsc9218i_registers *const regs = smsc9218i; - - *val = smsc9218i_phy_read(regs, phy_reg); - - return 0; -} - -static int smsc9218i_mdio_write( - int phy, - void *arg, - unsigned phy_reg, - uint32_t data -) -{ - volatile smsc9218i_registers *const regs = smsc9218i; - - smsc9218i_phy_write(regs, phy_reg, data); - - return 0; -} - -static void smsc9218i_interface_stats(smsc9218i_driver_entry *e) -{ - volatile smsc9218i_registers *const regs = smsc9218i; - smsc9218i_transmit_job_control *jc = &smsc_tx_jc; - int media = 0; - bool media_ok = smsc9218i_media_status(e, &media); - - if (media_ok) { - rtems_ifmedia2str(media, NULL, 0); - printf ("\n"); - } else { - printf ("PHY communication error\n"); - } - - e->receive_drop += SMSC9218I_SWAP(regs->rx_drop); - - printf("PHY interrupts: %u\n", e->phy_interrupts); - printf("received frames: %u\n", e->received_frames); - printf("receiver errors: %u\n", e->receiver_errors); - printf("receive interrupts: %u\n", e->receive_interrupts); - printf("receive DMA interrupts: %u\n", e->receive_dma_interrupts); - printf("receive to long errors: %u\n", e->receive_too_long_errors); - printf("receive collision errors: %u\n", e->receive_collision_errors); - printf("receive CRC errors: %u\n", e->receive_crc_errors); - printf("receive DMA errors: %u\n", e->receive_dma_errors); - printf("receive drops: %u\n", e->receive_drop); - printf("receive watchdog timeouts: %u\n", e->receive_watchdog_timeouts); - printf("transmitted frames: %u\n", e->transmitted_frames); - printf("transmitter errors: %u\n", e->transmitter_errors); - printf("transmit interrupts: %u\n", e->transmit_interrupts); - printf("transmit DMA interrupts: %u\n", e->transmit_dma_interrupts); - printf("transmit status overflows: %u\n", e->transmit_status_overflows); - printf("transmit frame errors: %u\n", e->transmit_frame_errors); - printf("transmit DMA errors: %u\n", e->transmit_dma_errors); - printf("frame compact count: %u\n", jc->frame_compact_count); - printf("interface state: %s\n", state_to_string [e->state]); -} - -static void smsc9218i_interface_off(struct ifnet *ifp) -{ - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc; - rtems_status_code sc = RTEMS_SUCCESSFUL; - - sc = rtems_task_suspend(e->receive_task); - ASSERT_SC(sc); - - sc = rtems_task_suspend(e->transmit_task); - ASSERT_SC(sc); - - /* remove interrupt handler */ - sc = rtems_interrupt_handler_remove( - MPC55XX_IRQ_SIU_EXTERNAL_0, - smsc9218i_interrupt_handler, - e - ); - ASSERT_SC(sc); - - mpc55xx_edma_release_channel( - &e->edma_receive - ); - - mpc55xx_edma_release_channel( - &e->edma_transmit - ); - - /* set in reset state */ - smsc9218i_reset_signal(false); -} - -static int smsc9218i_interface_ioctl( - struct ifnet *ifp, - ioctl_command_t command, - caddr_t data -) { - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc; - int rv = 0; - - SMSC9218I_PRINTF("%s\n", __func__); - - switch (command) { - case SIOCGIFMEDIA: - case SIOCSIFMEDIA: - rtems_mii_ioctl(&e->mdio, e, command, (int *) data); - break; - case SIOCGIFADDR: - case SIOCSIFADDR: - ether_ioctl(ifp, command, data); - break; - case SIOCSIFFLAGS: - if (ifp->if_flags & IFF_UP) { - /* TODO: init */ - } else { - smsc9218i_interface_off(ifp); - } - break; - case SIO_RTEMS_SHOW_STATS: - smsc9218i_interface_stats(e); - break; - default: - rv = EINVAL; - break; - } - - return rv; -} - -static void smsc9218i_interface_start(struct ifnet *ifp) -{ - rtems_status_code sc = RTEMS_SUCCESSFUL; - smsc9218i_driver_entry *e = (smsc9218i_driver_entry *) ifp->if_softc; - - /* Interface is now active */ - ifp->if_flags |= IFF_OACTIVE; - - sc = rtems_bsdnet_event_send(e->transmit_task, SMSC9218I_EVENT_TX_START); - ASSERT_SC(sc); -} - -static void smsc9218i_interface_watchdog(struct ifnet *ifp) -{ - SMSC9218I_PRINTF("%s\n", __func__); -} - -static void smsc9218i_attach(struct rtems_bsdnet_ifconfig *config) -{ - smsc9218i_driver_entry *e = &smsc9218i_driver_data; - struct ifnet *ifp = &e->arpcom.ac_if; - char *unit_name = NULL; - int unit_number = rtems_bsdnet_parse_driver_name(config, &unit_name); - - /* Check parameter */ - assert(unit_number == 0); - assert(config->hardware_address != NULL); - assert(e->state == SMSC9218I_NOT_INITIALIZED); - - /* Interrupt number */ - config->irno = MPC55XX_IRQ_SIU_EXTERNAL_0; - - /* Device control */ - config->drv_ctrl = e; - - /* Receive unit number */ - config->rbuf_count = 0; - - /* Transmit unit number */ - config->xbuf_count = 0; - - /* Copy MAC address */ - memcpy(e->arpcom.ac_enaddr, config->hardware_address, ETHER_ADDR_LEN); - - /* Set interface data */ - ifp->if_softc = e; - ifp->if_unit = (short) unit_number; - ifp->if_name = unit_name; - ifp->if_mtu = config->mtu > 0 ? (u_long) config->mtu : ETHERMTU; - ifp->if_init = smsc9218i_interface_init; - ifp->if_ioctl = smsc9218i_interface_ioctl; - ifp->if_start = smsc9218i_interface_start; - ifp->if_output = ether_output; - ifp->if_watchdog = smsc9218i_interface_watchdog; - ifp->if_flags = config->ignore_broadcast ? 0 : IFF_BROADCAST; - ifp->if_snd.ifq_maxlen = ifqmaxlen; - ifp->if_timer = 0; - - /* MDIO */ - e->mdio.mdio_r = smsc9218i_mdio_read; - e->mdio.mdio_w = smsc9218i_mdio_write; - - /* Change status */ - e->state = SMSC9218I_CONFIGURED; - - /* Attach the interface */ - if_attach(ifp); - ether_ifattach(ifp); -} - -int smsc9218i_attach_detach( - struct rtems_bsdnet_ifconfig *config, - int attaching -) { - if (attaching) { - smsc9218i_attach(config); - } else { - /* TODO */ - } - - /* FIXME: Return value */ - return 0; -} - -#endif /* defined(RTEMS_NETWORKING) && defined(MPC55XX_HAS_SIU) */ |