//#define MULTICORE_DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define CARD_NAME "mc_eth" #define CARD_VERSION "NVCOM-01" #define MODULE_NAME "mc_eth" #define DRIVER_VERSION "1.01" #define PROC_FILENAME "eth_stat" #define MC_MSG_DEFAULT (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK) #define ETH_MTU 1500 // Максимальный размер пакета #define ETH_HEADER_SIZE 14 // Размер заголовка пакета #define ETH_CRC_SIZE 4 // Размер CRC struct mc_eth { #ifdef CONFIG_MULTICORE_ETH_NAPI struct napi_struct napi; void __iomem *irq_mask_regs; #endif // Указатель на область регистров EMAC void __iomem *eth_regs; // Указатель на область регистров RX DMA descriptors EMAC void __iomem *dma_rx_desc_regs; // Указатель на область регистров TX DMA descriptors EMAC void __iomem *dma_tx_desc_regs; // Указатель на область регистров RX DMA data EMAC void __iomem *dma_rx_regs; // Указатель на область регистров TX DMA data EMAC void __iomem *dma_tx_regs; // Прерывание по приёму пакета int rx_irq; // Прерывание по выдаче пакета int tx_irq; struct net_device *ndev; // Указатель на сетевое устройство struct sk_buff *pending_tx_skb; // Следующий передавамый пакет unsigned phy; // Адрес внешнего PHY void *tx_descbuf; // Буфер дескрипторов на передачу unsigned tx_descbuf_phys; // Его физический адрес void *txbuf; // Буфер выдачи unsigned txbuf_phys; // Его физический адрес void *rx_descbuf; // Буфер дескрипторов на прием unsigned rx_descbuf_phys; // Его физический адрес void *rxbuf; // Буфер прием unsigned rxbuf_phys; // Его физический адрес unsigned rxDescr1; unsigned rxDescr2; int msg_level; // для ethtool // Статистика, печатаемая через /proc struct _proc_stat { int rx_lengths [16]; // Гистограмма длина выдаваемых пакетов int tx_lengths [16]; // Гистограмма длина принимаемых пакетов } proc; }; #ifdef CONFIG_MULTICORE_ETH_NAPI /// mask RX interrupt source static void _MSK_RXIRQ(struct mc_eth *u, unsigned en) { u32 val = __raw_readl(u->irq_mask_regs + 0x020); if (en) { val |= (1 << 16); /// unmask int } else { val &= (0xFFFFFFFF ^ (1 << 16)); /// mask int } __raw_writel(val, u->irq_mask_regs + 0x020); } #endif // // Функции записи в регистры EMAC и DMA EMAC // static inline u32 _R(struct mc_eth *u, unsigned idx) { return __raw_readl(u->eth_regs + idx); } static inline void _W(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->eth_regs + idx); } static inline u32 _RRX(struct mc_eth *u, unsigned idx) { return __raw_readl(u->dma_rx_regs + idx); } static inline u32 _RRX_DESC(struct mc_eth *u, unsigned idx) { return __raw_readl(u->dma_rx_desc_regs + idx); } static inline void _WRX(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_rx_regs + idx); } static inline void _WRX_DESC(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_rx_desc_regs + idx); } static inline u32 _RTX(struct mc_eth *u, unsigned idx) { return __raw_readl(u->dma_tx_regs + idx); } static inline u32 _RTX_DESC(struct mc_eth *u, unsigned idx) { return __raw_readl(u->dma_tx_desc_regs + idx); } static inline void _WTX(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_tx_regs + idx); } static inline void _WTX_DESC(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_tx_desc_regs + idx); } // // Запись регистра PHY // static void mc_phy_write (struct mc_eth *u, unsigned address, unsigned data) { unsigned i, status; // Команда PHY _W(u, RG_MD_CONTROL, F_OP_WRITE | F_DATA(data) | F_PHY(u->phy) | F_REG(address)); // Ожидаем завершения операции for (i=0; i<100000; ++i) { status = _R(u, RG_MD_STATUS); if (! (status & F_MD_BUSY)) break; } PDEBUGG("mc_phy_write(%d, 0x%02x, 0x%04x) ", u->phy, address, data); if (status & F_MD_BUSY) PDEBUG("TIMEOUT\n"); } // // Чтение регистра PHY // static unsigned mc_phy_read (struct mc_eth *u, unsigned address) { unsigned status, i; // Команда PHY _W(u, RG_MD_CONTROL, F_OP_READ | F_PHY(u->phy) | F_REG(address)); // Ожидаем завершения операции for (i=0; i<100000; ++i) { status = _R(u, RG_MD_STATUS); if (! (status & F_MD_BUSY)) break; } PDEBUGG("mc_phy_read(%d, 0x%02x) ", u->phy, address); if (status & F_MD_BUSY) { PDEBUG("TIMEOUT\n"); } else { PDEBUGG("returned 0x%04x\n", status & F_DATA_MASK); } return status & F_DATA_MASK; } // // Установка MAC-адреса // static int eth_set_mac_addr (struct net_device *dev, void *addr) { struct mc_eth *u = netdev_priv(dev); PDEBUGG("=== eth_set_mac_addr\n"); #ifdef DEBUG_MULTICORE u8 *p = (u8*) addr; int i; for (i = 0; i < 8; i++) printk (KERN_DEBUG "addr[%d] = %02X\n", i, p[i]); #endif memcpy(dev->dev_addr, addr, dev->addr_len); _W(u, RG_UC_ADDR1, dev->dev_addr[4] | (dev->dev_addr[5] << 8)); _W(u, RG_UC_ADDR2, dev->dev_addr[0] | (dev->dev_addr[1] << 8) | (dev->dev_addr[2] << 16)| (dev->dev_addr[3] << 24)); PDEBUGG ("UCADDR=%02x:%08x\n", _R(u, RG_UC_ADDR1), _R(u, RG_UC_ADDR2)); return 0; } // // Смена MTU // static int mc_change_mtu(struct net_device *dev, int new_mtu) { struct mc_eth *u = netdev_priv(dev); PDEBUGG("=== eth_change_mtu\n"); _W(u, RG_LEN_BORDER_RX, (new_mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE)); dev->mtu = new_mtu; return 0; } // // Выдача пакета с помощью DMA // static void chip_write_txfifo (struct mc_eth *u, unsigned physaddr, unsigned nbytes) { unsigned count; unsigned descr1; unsigned descr2; unsigned *txDescrBuff; unsigned csr_dsc; unsigned csr; PDEBUG ("write_txfifo %08x, %d bytes\n", physaddr, nbytes); descr1 = 0; descr2 = (nbytes & 0x7FF); txDescrBuff = ((unsigned *) u->tx_descbuf); // Записываем дескрипторы в буфер txDescrBuff[0] = descr2; txDescrBuff[1] = descr1; // Инициализируем канал ДМА дескрипторов csr_dsc = F_DMA_WCX (0); _WTX_DESC(u, RG_DMA_IR, u->tx_descbuf_phys); _WTX_DESC(u, RG_DMA_CP, 0); _WTX_DESC(u, RG_DMA_CSR, csr_dsc); // Инициализируем и запускаем канал ДМА данных csr = (F_DMA_WN(15) | F_DMA_WCX (((nbytes + 7) >> 3) - 1)); _WTX(u, RG_DMA_IR, physaddr); _WTX(u, RG_DMA_CP, 0); _WTX(u, RG_DMA_CSR, csr | F_DMA_RUN); _WTX_DESC(u, RG_DMA_CSR, (csr_dsc | F_DMA_RUN)); for (count=100000; count>0; count--) { csr_dsc = _RTX_DESC(u, RG_DMA_CSR); csr = _RTX(u, RG_DMA_CSR); if (! ((csr & F_DMA_RUN) || (csr_dsc & F_DMA_RUN)) ) break; PDEBUG ("~"); } if (count == 0) { PDEBUG ("eth: TX DMA failed, CSR=%08x\tCSR_DESC=%08x\n", csr, csr_dsc); _WTX(u, RG_DMA_CSR, 0); _WTX_DESC(u, RG_DMA_CSR, 0); } else { PDEBUG ("eth: TX DMA succseed, CSR=%08x\tCSR_DESC=%08x\n", csr, csr_dsc); } } // // Приём пакета с помощью DMA // static void chip_read_rxfifo (struct mc_eth *u, unsigned physaddr, unsigned nwrds) { unsigned count; unsigned csr, csr_desc; PDEBUG("chip_read_rxfifo\n"); csr_desc = _RRX_DESC(u, RG_DMA_CSR); csr = (F_DMA_WN(15) | F_DMA_WCX (nwrds)); PDEBUGG ("(r%d)\n", nwrds); _WRX(u, RG_DMA_IR, physaddr); _WRX(u, RG_DMA_CP, 0); _WRX(u, RG_DMA_CSR, csr | F_DMA_RUN); _WRX_DESC(u, RG_DMA_IR, u->rx_descbuf_phys); _WRX_DESC(u, RG_DMA_CP, 0); _WRX_DESC(u, RG_DMA_CSR, csr_desc | F_DMA_RUN); csr = _RRX(u, RG_DMA_CSR); while (csr & F_DMA_RUN) { csr = _RRX(u, RG_DMA_CSR); } csr_desc = _RRX_DESC(u, RG_DMA_CSR); while ((csr_desc & F_DMA_RUN) && ((csr_desc & 0xFFFF0000) != 0xFFFF0000)) { csr_desc = _RRX_DESC(u, RG_DMA_CSR); csr = _RRX(u, RG_DMA_CSR); if (((csr & F_DMA_RUN) == 0) && ((csr_desc & F_DMA_RUN) != 0)) { _WRX(u, RG_DMA_CSR, (0 | F_DMA_RUN)); for (count=1000; count>0; count--) { csr = _RRX(u, RG_DMA_CSR); if (! (csr & F_DMA_RUN)) { PDEBUG (">>>> csr_desc = %#08x\tcsr = %#08x\n", csr_desc, csr); break; } } } } } // // Выборка принятого пакета из буфера контроллера и постановка // его в FIFO драйвера // static int eth_receive_frame (struct net_device *dev, unsigned frames, unsigned wrds) { struct mc_eth *u = netdev_priv(dev); unsigned len; struct sk_buff *skb; unsigned char *data; unsigned char *src; unsigned j; unsigned *rxDescrBuff; unsigned cc_rx; PDEBUG("eth_receive_frame:\n"); PDEBUG("frames = %d\twrds = %d\n", frames, wrds); rxDescrBuff = ((unsigned*) u->rx_descbuf); _WRX_DESC(u, RG_DMA_CSR, F_DMA_WCX ((frames - 1))); chip_read_rxfifo(u, u->rxbuf_phys, wrds + 1); src = ((unsigned char*) u->rxbuf); PDEBUG ("eth_receive_frame: \n"); for (j = 0; j < frames; j++) { u->rxDescr1 = rxDescrBuff[(2*j)+1]; u->rxDescr2 = rxDescrBuff[(2*j)]; cc_rx = ((u->rxDescr2 & 0xF000) >> 12); if (! cc_rx == F_CC_RX_OK) { PDEBUG ("eth_receive_frame: failed, rx_descriptor2 = %#08x CC = %#08x\n", u->rxDescr2, cc_rx); ++dev->stats.rx_errors; } else { PDEBUG("eth_receive_frame: frames = %d j = %d descr1 = %#08x,\tdescr2= %#08x\n", frames, j, u->rxDescr1, u->rxDescr2); len = u->rxDescr2 & 0x7FF; if (len < 4 || len > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { PDEBUG ("eth_receive_frame: bad length %d bytes,\trx_status=%#08x,\tdescr2=%#08x\n", len, rx_status, u->rxDescr2); ++dev->stats.rx_length_errors; //return 0; } else { PDEBUG("eth_receive_frame: ok, rx_status=%#08x, rxb_csr=%#08x, length %d bytes\n", rx_status, rxb_csr, len); if (u->rxDescr2 & ((1 << 22) | (1 << 23))) dev->stats.multicast++; ///попробуем сделать выравнивание на 16 байт **** skb = dev_alloc_skb((len + 16 + 0xF) & ~(0xF)); if (unlikely(skb == NULL)) { printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", dev->name); //dev->stats.rx_dropped++; dev->stats.rx_dropped += (frames - j); return 0; } // Выравниваем заголовок IP на байта. skb_reserve(skb, 8); data = skb_put(skb, len); memcpy(data, src, len); src += len; if (len % 8) src += 8 - (len % 8); PDEBUG ("eth_receive_frame: %02x-%02x-%02x-%02x-%02x-%02x-%02x-" "%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], data[8], data[9], data[10], data[11], data[12], data[13]); skb->protocol = eth_type_trans(skb, dev); netif_rx(skb); dev->stats.rx_packets++; dev->stats.rx_bytes += len; // Накопление статистики для построения гистограмм через /proc u->proc.rx_lengths [len / 100]++; } } } return (frames - j); } // // Отправка пакета с помощью DMA // static void chip_transmit_packet (struct sk_buff *skb, struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); unsigned len = skb->len; PDEBUG ("---- chip_transmit_packet\n"); memcpy(u->txbuf, skb->data, skb->len); if (len < 60) { len = 60; PDEBUGG ("txzero %08x, %d bytes\n", (unsigned)(u->txbuf + skb->len), len - skb->len); memset (u->txbuf + skb->len, 0, len - skb->len); } PDEBUG ("chip_transmit_packet: %02x-%02x-%02x-%02x-%02x-%02x-%02x-" "%02x-%02x-%02x-%02x-%02x-%02x-%02x\n", ((unsigned char*)u->txbuf)[0], ((unsigned char*)u->txbuf)[1], ((unsigned char*)u->txbuf)[2], ((unsigned char*)u->txbuf)[3], ((unsigned char*)u->txbuf)[4], ((unsigned char*)u->txbuf)[5], ((unsigned char*)u->txbuf)[6], ((unsigned char*)u->txbuf)[7], ((unsigned char*)u->txbuf)[8], ((unsigned char*)u->txbuf)[9], ((unsigned char*)u->txbuf)[10], ((unsigned char*)u->txbuf)[11], ((unsigned char*)u->txbuf)[12], ((unsigned char*)u->txbuf)[13]); chip_write_txfifo (u, u->txbuf_phys, len); PDEBUGG ("!"); PDEBUGG ("@"); dev->trans_start = jiffies; dev->stats.tx_packets++; dev->stats.tx_bytes += len; // Накопление статистики для построения гистограмм через /proc u->proc.tx_lengths [len / 100]++; PDEBUGG ("tx%d", len); } #ifdef CONFIG_MULTICORE_ETH_NAPI static void eth_enable_rx_irq(struct mc_eth *u) { // _W(u, RG_CONTROL, _R(u, RG_CONTROL) | F_IRQ_RX_DONE); // TODO _MSK_RXIRQ(u, 1); } static void eth_disable_rx_irq(struct mc_eth *u) { // _W(u, RG_CONTROL, _R(u, RG_CONTROL) & ~F_IRQ_RX_DONE); // TODO _MSK_RXIRQ(u, 0); } static int eth_poll(struct napi_struct *napi, int budget) { struct mc_eth *u = container_of(napi, struct mc_eth, napi); int work_done = 0, nframes; unsigned rxb_csr; unsigned mac_csr; unsigned words; PDEBUG ("eth_poll, budget = %d\n", budget); while (work_done < budget) { mac_csr = _R(u, RG_MAC_CSR); while (mac_csr & F_RXM_BUSY) { // wait for receiving finished PDEBUG ("mac_csr = %08x\n", mac_csr); mac_csr = _R(u, RG_MAC_CSR); } _W(u, RG_INT_CSR, ((F_RX_INT | F_RX_ERROR_INT))); rxb_csr = _R(u, RG_RXB_CSR); nframes = F_RX_FRAME_NUM(rxb_csr); words = F_RX_WORD_NUM(rxb_csr); if (nframes == 0) { eth_enable_rx_irq(u); napi_complete(napi); return work_done; } //work_done += eth_receive_frame (u->pdev, nframes, words); work_done += eth_receive_frame (u->ndev, nframes, words); } PDEBUG ("eth_poll, work_done = %d\n", work_done); return work_done; } #endif // // Обработка прерывания по приёму пакета // static irqreturn_t handle_receive_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct mc_eth *u = netdev_priv(dev); unsigned rx_status; unsigned int_csr; unsigned rxb_csr; unsigned nframes; unsigned words; unsigned mac_csr; #ifdef CONFIG_MULTICORE_ETH_NAPI PDEBUGG ("mask rx irq\n"); eth_disable_rx_irq(u); #endif PDEBUG ("eth receive interrupt\n"); mac_csr = _R(u, RG_MAC_CSR); while (mac_csr & F_RXM_BUSY) { // wait for receiving finished PDEBUG ("mac_csr = %08x\n", mac_csr); mac_csr = _R(u, RG_MAC_CSR); } rx_status = _R(u, RG_RX_STATUS); PDEBUG ("eth rx irq: RX_STATUS = %08x\n", rx_status); for (;;) { int_csr = _R(u, RG_INT_CSR); PDEBUG ("eth rx irq: INT_CSR = %08x\n", int_csr); if (! (int_csr & (F_RX_INT | F_RX_ERROR_INT))) { return IRQ_HANDLED; } _W(u, RG_INT_CSR, (int_csr & (F_RX_INT | F_RX_ERROR_INT))); rxb_csr = _R(u, RG_RXB_CSR); #ifdef CONFIG_MULTICORE_ETH_NAPI if (! (rxb_csr & F_EMPTY)) { PDEBUG ("there are packets in RX FIFO!\n"); if (napi_schedule_prep(&u->napi)) __napi_schedule(&u->napi); } else { PDEBUGG ("RX FIFO empty\n"); eth_enable_rx_irq(u); } return IRQ_HANDLED; #else // Выборка всех принятых пакетов while (! (rxb_csr & F_EMPTY)) { PDEBUG ("eth rx irq: RXB_CSR = %08x\n", rxb_csr); nframes = F_RX_FRAME_NUM(rxb_csr); words = F_RX_WORD_NUM(rxb_csr); if ((nframes <= 0) || (eth_receive_frame (dev, nframes, words) <= 0)) { return IRQ_HANDLED; } rxb_csr = _R(u, RG_RXB_CSR); } #endif } return IRQ_HANDLED; } // // Обработка прерывания по выдаче пакета // static irqreturn_t handle_transmit_interrupt(int irq, void *dev_id) { struct net_device *dev = dev_id; struct mc_eth *u = netdev_priv(dev); unsigned txmBusy = ((_R(u, RG_MAC_CSR) & F_TXM_BUSY) != 0)? 1 : 0; unsigned int_csr = _R(u, RG_INT_CSR); unsigned tx_status = _R(u, RG_TX_STATUS); PDEBUG ("transmit interrupt\n"); _W(u, RG_INT_CSR, (int_csr & (F_TX_INT | F_TX_ERROR_INT))); if (txmBusy) { // Передатчик ещё в процессе передачи - такого быть не должно PDEBUG ("eth tx irq: F_TXM_BUSY, MAC_CSR = %08x\n", (_R(u, RG_MAC_CSR) & F_TXM_BUSY)); return IRQ_HANDLED; } if (tx_status & (F_ONCOL | F_CC_TX_LTCOLER)) { printk("eth tx irq: tx collision\n"); ++dev->stats.collisions; } if (u->pending_tx_skb) { PDEBUG ("Transmitting pending skb in interrupt handler\n"); chip_transmit_packet (u->pending_tx_skb, dev); dev_kfree_skb_irq(u->pending_tx_skb); netif_wake_queue(dev); u->pending_tx_skb = 0; } return IRQ_HANDLED; } // // Запуск выдачи пакета // netdev_tx_t eth_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); PDEBUGG ("eth_start_xmit: transmit %d bytes\n", skb->len); if (! (mc_phy_read (u, PHY_STS) & PHY_STS_LINK)) { printk ("TX ERROR: NO CARRIER\n"); ++dev->stats.tx_carrier_errors; dev_kfree_skb(skb); return NETDEV_TX_OK; } // Если нет несущей, то выходим if (skb->len < 4 || skb->len > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { PDEBUG ("TX ERROR: skb->len = %d, dev->mtu = %d\n", skb->len, dev->mtu); printk ("TX ERROR: skb->len = %d, dev->mtu = %d\n", skb->len, dev->mtu); ++dev->stats.tx_errors; dev_kfree_skb(skb); return NETDEV_TX_OK; } disable_irq (u->tx_irq); if (! (_R(u, RG_MAC_CSR) & F_TXM_BUSY)) { PDEBUGG ("eth_start_xmit: transmit now"); chip_transmit_packet (skb, dev); dev_kfree_skb(skb); PDEBUG("eth_start_xmit: INT_CSR = %08x\n", _R(u, RG_INT_CSR)); } else { // Передатчик занят, останавливаем очередь драйвера PDEBUG ("eth_start_xmit: stop, wait for interrupt"); u->pending_tx_skb = skb; netif_stop_queue(dev); } enable_irq (u->tx_irq); return NETDEV_TX_OK; } extern u_int32_t mips_hpt_frequency; // // Инициализация EMAC // static int eth_init (struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); unsigned id, retry = 0; int count; int tmp; unsigned rxb_csr; PDEBUGG("=== eth_init\n"); // Включение тактовой частоты EMAC MC_CLKEN |= (MC_CLKEN_EMAC0 /*| MC_CLKEN_EMAC1 */); udelay (10); // Программный сброс контроллера PHY _W(u, RG_MAC_CSR, (_R(u, RG_MAC_CSR)&(~F_EN_MAC))); udelay (10); _W(u, RG_MAC_CSR, (_R(u, RG_MAC_CSR)|F_EN_MAC)); // Тактовый сигнал MDC не должен превышать 2.5 МГц _W(u, RG_MD_MODE, (F_DIVIDER (mips_hpt_frequency / 2000000))); //tmp = _R(u, RG_MD_MODE); TODO //printk("tmp = 0x%08x\n", tmp); // Сброс PHY _W(u, RG_MD_MODE, (_R(u,RG_MD_MODE)|F_RST_MD)); for (count=10000; count>0; count--) if (! (mc_phy_read (u, PHY_CTL) & PHY_CTL_RST)) break; if (count == 0) printk ("eth_init: PHY reset failed\n"); // Поиск адреса PHY dev->base_addr = 31; for (;;) { id = mc_phy_read (u, PHY_ID1) << 16 | mc_phy_read (u, PHY_ID2); printk("id = 0x%08x\n", id); if (id != 0 && id != 0xffffffff) break; if (u->phy > 0) u->phy--; else { u->phy = 31; retry++; if (retry > 3) { printk ("eth_init: no PHY detected\n"); return -ENODEV; } } } printk ("eth_init: transceiver `%s' detected at address %d\n", ((id & PHY_ID_MASK) == PHY_ID2_NUMB) ? "LXT971A" : "Unknown", u->phy); mc_phy_write (u, PHY_CTL, (PHY_CTL_DPLX | PHY_CTL_ANEG_EN)); /// full duplex | autoneg enable // Автоподстройка (Auto-Negotiation) mc_phy_write (u, PHY_ADVRT, PHY_ADVRT_CSMA | PHY_ADVRT_10 | PHY_ADVRT_10_FDX | PHY_ADVRT_100 | PHY_ADVRT_100_FDX); tmp = mc_phy_read(u, PHY_CTL); PDEBUGG("PHY_CTL = 0x%08x\n", tmp); mc_phy_write (u, PHY_CTL, (tmp |(PHY_CTL_COLTST | PHY_CTL_ANEG_RST))); /// full duplex | collision test | autoneg enable | autoneg restart //_W(u, RG_MODE_CSR, (F_MODE_ETHERNET | F_LOOPBACK)); // Сброс буфера передатчика _W(u, RG_TXB_CSR, F_CLR_TXB); // Сброс буфера приемника _W(u, RG_RXB_CSR, F_CLR_RXB); udelay (10); rxb_csr = _R(u, RG_RXB_CSR); // Общие режимы _W(u, RG_MAC_CSR,(F_EN_MAC | F_FULLD)); // enable emac, full duplex PDEBUGG("MAC_CSR: 0x%08x\n", _R(u, RG_MAC_CSR)); // Режимы обработки коллизии _W(u, RG_IFS_COLL_MODE, (F_ATTEMPT_NUM(15) | F_CW_EN | F_COLL_WIN(64) | F_JAMB(0xC3) | F_IFS(24))); // Режимы приёма _W(u, RG_RX_CTR, (/*F_ALL_EN |*/ F_BC_EN | /*F_MC_EN | F_MCHT_EN |*/ F_UC_EN)); // Свой адрес dev->addr_assign_type = NET_ADDR_RANDOM; random_ether_addr(dev->perm_addr); eth_set_mac_addr(dev, dev->perm_addr); // Максимальный размер кадра _W(u, RG_LEN_BORDER_RX, (ETH_MTU + ETH_HEADER_SIZE + ETH_CRC_SIZE)); // TODO //tmp = mc_phy_read (u, PHY_STS); //printk("PHY_STS: 0x%08x\n", tmp); PDEBUG("=== eth_init OK\n"); return 0; } static int eth_open (struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); int ret; PDEBUGG("=== eth_open\n"); if (netif_queue_stopped(dev)) { netif_wake_queue(dev); } else { netif_start_queue(dev); } #ifdef CONFIG_MULTICORE_ETH_NAPI napi_enable(&u->napi); #endif u->pending_tx_skb = 0; memset (&u->proc, 0, sizeof (struct _proc_stat)); ret = request_irq(u->rx_irq, handle_receive_interrupt, 0, dev->name, dev); if (ret) return -EFAULT; ret = request_irq(u->tx_irq, handle_transmit_interrupt, 0, dev->name, dev); if (ret) { free_irq(u->rx_irq, dev); return -EFAULT; } _W(u, RG_RXB_CSR, (_R(u, RG_RXB_CSR) | F_CLR_RXB)); _W(u, RG_MAC_CSR,(_R(u, RG_MAC_CSR) | F_EN_RX | F_EN_TX | F_FULLD)); // enable RX, enable TX PDEBUG("=== eth_open OK\n"); return 0; } static int eth_close (struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); PDEBUGG("=== eth_close\n"); #ifdef CONFIG_MULTICORE_ETH_NAPI napi_disable(&u->napi); //TODO #endif free_irq(u->tx_irq, dev); free_irq(u->rx_irq, dev); if (!netif_queue_stopped(dev)) netif_stop_queue(dev); return 0; } static void mc_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) { strcpy(info->driver, MODULE_NAME); strcpy(info->version, DRIVER_VERSION); strcpy(info->fw_version, CARD_VERSION); strcpy(info->bus_info, "Elvees AMBA"); } static int mc_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct mc_eth *u = netdev_priv(dev); unsigned phy_ctl; memset(cmd, 0, sizeof(*cmd)); cmd->phy_address = u->phy; cmd->supported = SUPPORTED_Autoneg | SUPPORTED_10baseT_Half | SUPPORTED_10baseT_Full | SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full | SUPPORTED_MII; cmd->advertising = ADVERTISED_Autoneg | ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | ADVERTISED_MII; cmd->autoneg = (mc_phy_read (u, PHY_CTL) & PHY_CTL_ANEG_EN) ? (1) : (0); cmd->port = PORT_MII; cmd->transceiver = XCVR_EXTERNAL; cmd->duplex = DUPLEX_HALF; cmd->speed = SPEED_10; phy_ctl = mc_phy_read(u, PHY_CTL); if (phy_ctl & PHY_CTL_DPLX) cmd->duplex = DUPLEX_FULL; if (phy_ctl & PHY_CTL_SPEED_100) cmd->speed = SPEED_100; return 0; } static u32 mc_get_msglevel(struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); return u->msg_level; } static void mc_set_msglevel(struct net_device *dev, u32 value) { struct mc_eth *u = netdev_priv(dev); u->msg_level = value; } static const struct ethtool_ops mc_ethtool_ops = { .get_drvinfo = mc_get_drvinfo, .get_link = ethtool_op_get_link, .get_msglevel = mc_get_msglevel, .set_msglevel = mc_set_msglevel, .get_settings = mc_get_settings, }; static const struct net_device_ops netdev_ops = { .ndo_init = eth_init, .ndo_open = eth_open, .ndo_stop = eth_close, .ndo_start_xmit = eth_start_xmit, //.ndo_tx_timeout = eth_timeout, //.ndo_set_multicast_list = eth_set_multicast_list, .ndo_change_mtu = mc_change_mtu, .ndo_set_mac_address = eth_set_mac_addr, }; static int mc_eth_probe(struct platform_device *pdev) { struct net_device *ndev; struct resource *r; struct mc_eth *u; int ret; PDEBUG ("=== eth_module_init\n"); ndev = alloc_etherdev(sizeof(struct mc_eth)); if (!ndev) { printk("%s: could not allocate device.\n", CARD_NAME); ret = -ENOMEM; goto err_get_res; } ndev->dma = (unsigned char)-1; ndev->netdev_ops = &netdev_ops; ndev->ethtool_ops = &mc_ethtool_ops; ether_setup(ndev); ndev->mtu = ETH_MTU; u = netdev_priv(ndev); u->ndev = ndev; r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } u->eth_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->eth_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txdma_desc"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } PDEBUG("txdma_desc: r = 0x%08x\n", ((unsigned) r)); u->dma_tx_desc_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_tx_desc_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "txdma_data"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } PDEBUG("txdma_data: r = 0x%08x\n", ((unsigned) r)); u->dma_tx_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_tx_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxdma_desc"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } PDEBUG("rxdma_desc: r = 0x%08x\n", ((unsigned) r)); u->dma_rx_desc_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_rx_desc_regs)) { ret = -ENOMEM; goto err_get_res; } r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rxdma_data"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } PDEBUG("rxdma_data: r = 0x%08x\n", ((unsigned) r)); u->dma_rx_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->dma_rx_regs)) { ret = -ENOMEM; goto err_get_res; } PDEBUG("Registers at: %p, RX_DMA_DESC: %p, RX_DMA: %p, TX_DMA_DESC: %p, TX_DMA: %p\n", u->eth_regs, u->dma_rx_desc_regs, u->dma_rx_regs, u->dma_tx_desc_regs, u->dma_tx_regs); u->tx_irq = platform_get_irq_byname(pdev, "txframe_irq"); if (u->tx_irq < 0) { printk("mc_eth_probe: error txframe_irq\n"); ret = -EINVAL; goto err_get_res; } u->rx_irq = platform_get_irq_byname(pdev, "rxframe_irq"); if (u->rx_irq < 0) { printk("mc_eth_probe: error rxframe_irq\n"); ret = -EINVAL; goto err_get_res; } ndev->irq = u->rx_irq; #ifdef CONFIG_MULTICORE_ETH_TXBUF_CRAM u->txbuf = (unsigned char *) CONFIG_MULTICORE_ETH_TXBUF_ADDR; u->txbuf_phys = CONFIG_MULTICORE_ETH_TXBUF_ADDR & 0x1FFFFFFF; u->tx_descbuf = (unsigned char *) (CONFIG_MULTICORE_ETH_TXBUF_ADDR + CONFIG_MULTICORE_ETH_TXBUF_SIZE); u->tx_descbuf_phys = (CONFIG_MULTICORE_ETH_TXBUF_ADDR + CONFIG_MULTICORE_ETH_TXBUF_SIZE) & 0x1FFFFFFF; u->rx_descbuf = (unsigned char *) (CONFIG_MULTICORE_ETH_TXBUF_ADDR + (2 * CONFIG_MULTICORE_ETH_TXBUF_SIZE)); u->rx_descbuf_phys = (CONFIG_MULTICORE_ETH_TXBUF_ADDR + (2 * CONFIG_MULTICORE_ETH_TXBUF_SIZE)) & 0x1FFFFFFF; #else u->txbuf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_ETH_TXBUF_SIZE, &u->txbuf_phys, GFP_KERNEL); if (!u->txbuf) { ret = -ENOMEM; goto err_get_res; } PDEBUG("txbuf @ %p, phys_addr = %08X\n", u->txbuf, u->txbuf_phys); u->tx_descbuf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_ETH_TXBUF_SIZE, &u->tx_descbuf_phys, GFP_KERNEL); if (!u->tx_descbuf) { ret = -ENOMEM; goto err_get_res; } PDEBUG("tx_descbuf @ %p, txdescbuf phys_addr = %08X\n", u->tx_descbuf, u->tx_descbuf_phys); u->rx_descbuf = dmam_alloc_coherent(&pdev->dev, CONFIG_MULTICORE_ETH_TXBUF_SIZE, &u->rx_descbuf_phys, GFP_KERNEL); if (!u->rx_descbuf) { ret = -ENOMEM; goto err_get_res; } PDEBUG("rx_descbuf @ %p, rx_descbuf phys_addr = %08X\n", u->rx_descbuf, u->rx_descbuf_phys); u->rxbuf = dmam_alloc_coherent(&pdev->dev, (2*CONFIG_MULTICORE_ETH_TXBUF_SIZE), &u->rxbuf_phys, GFP_KERNEL); if (!u->rxbuf) { ret = -ENOMEM; goto err_get_res; } PDEBUG("rx_descbuf @ %p, rx_descbuf phys_addr = %08X\n", u->rx_descbuf, u->rx_descbuf_phys); #endif #ifdef CONFIG_MULTICORE_ETH_NAPI r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sys_regs"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } PDEBUG("sys_regs: r = 0x%08x\n", ((unsigned) r)); u->irq_mask_regs = devm_ioremap_resource(&pdev->dev, r); if (IS_ERR(u->irq_mask_regs)) { ret = -ENOMEM; goto err_get_res; } // Запуск NAPI netif_napi_add(ndev, &u->napi, eth_poll, 64); #endif u->msg_level = netif_msg_init(-1, MC_MSG_DEFAULT); ret = register_netdev(ndev); if (ret) { goto err_register; } //create_proc_read_entry (PROC_FILENAME, 0 /* default mode */, // kshubin // NULL /* parent dir */, read_proc, ndev /* client data */); //proc_create_data(PROC_FILENAME, 0 /* default mode */, NULL /* parent dir */, platform_set_drvdata(pdev, ndev); printk("mc_eth: controller found\n"); return 0; err_register: #ifdef CONFIG_MULTICORE_ETH_NAPI netif_napi_del(&u->napi); #endif free_netdev(ndev); err_get_res: return ret; } static int mc_eth_remove(struct platform_device *pdev) { struct net_device *ndev = platform_get_drvdata(pdev); eth_close(ndev); remove_proc_entry(PROC_FILENAME, NULL /* parent dir */); return 0; } static struct platform_driver mc_eth_driver = { .driver = { .name = CARD_NAME, .owner = THIS_MODULE, }, .probe = mc_eth_probe, .remove = mc_eth_remove, }; module_platform_driver(mc_eth_driver); MODULE_DESCRIPTION("Multicore Ethernet Controller driver"); MODULE_AUTHOR("Dmitry Podkhvatilin"); MODULE_LICENSE("GPL");