//#define MULTICORE_DEBUG 1 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_MC30SF6) || defined(CONFIG_MC0428) #define LAN8710 #include #else #include #endif #include #include #define CARD_NAME "mc_eth" #define CARD_VERSION "NVCOM-01" #define MODULE_NAME "mc_eth" #define DRIVER_VERSION "1.0" #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; #endif // Указатель на область регистров EMAC void __iomem *eth_regs; // Указатель на область регистров RX DMA EMAC void __iomem *dma_rx_regs; // Указатель на область регистров TX DMA 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 *txbuf; // Буфер выдачи unsigned txbuf_phys; // Его физический адрес int msg_level; // для ethtool // Статистика, печатаемая через /proc struct _proc_stat { int rx_lengths [16]; // Гистограмма длина выдаваемых пакетов int tx_lengths [16]; // Гистограмма длина принимаемых пакетов } proc; }; // // Функции записи в регистры 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 void _WRX(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_rx_regs + idx); } static inline u32 _RTX(struct mc_eth *u, unsigned idx) { return __raw_readl(u->dma_tx_regs + idx); } static inline void _WTX(struct mc_eth *u, unsigned idx, u32 val) { __raw_writel(val, u->dma_tx_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); #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_UCADDR_L, dev->dev_addr[0] | (dev->dev_addr[1] << 8) | (dev->dev_addr[2] << 16)| (dev->dev_addr[3] << 24)); _W(u, RG_UCADDR_H, dev->dev_addr[4] | (dev->dev_addr[5] << 8)); PDEBUGG ("UCADDR=%02x:%08x\n", _R(u, RG_UCADDR_H), _R(u, RG_UCADDR_L)); return 0; } // // Смена MTU // static int mc_change_mtu(struct net_device *dev, int new_mtu) { struct mc_eth *u = netdev_priv(dev); _W(u, RG_RX_FR_MAXSIZE, 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 csr = F_DMA_WN(15) | #if defined(CONFIG_NVCOM01M)||defined(CONFIG_MC30SF6)||defined(CONFIG_MC0428) F_DMA_WCX (nbytes - 1); #else F_DMA_WCX (((nbytes + 7) >> 3) - 1); #endif _WTX(u, RG_DMA_IR, physaddr); //_WTX(u, RG_DMA_CSR, csr); PDEBUG ("write_txfifo %08x, %d bytes\n", physaddr, nbytes); _WTX(u, RG_DMA_CSR, csr | F_DMA_RUN); for (count=100000; count>0; count--) { csr = _RTX(u, RG_DMA_CSR); if (! (csr & F_DMA_RUN)) break; PDEBUG ("~"); } if (count == 0) { PDEBUG ("eth: TX DMA failed, CSR=%08x\n", csr); _WTX(u, RG_DMA_CSR, 0); } } // // Приём пакета с помощью DMA // static void chip_read_rxfifo (struct mc_eth *u, unsigned physaddr, unsigned nbytes) { unsigned count; unsigned csr = F_DMA_WN(15) | #if defined(CONFIG_NVCOM01M)||defined(CONFIG_MC30SF6)||defined(CONFIG_MC0428) F_DMA_WCX (nbytes - 1); #else F_DMA_WCX (((nbytes + 7) >> 3) - 1); #endif _WRX(u, RG_DMA_IR, physaddr); //_WRX(u, RG_DMA_CSR, csr); PDEBUGG ("(r%d) ", nbytes); _WRX(u, RG_DMA_CSR, csr | F_DMA_RUN); for (count=100000; count>0; count--) { csr = _RRX(u, RG_DMA_CSR); if (! (csr & F_DMA_RUN)) break; } if (count == 0) { PDEBUG ("eth: RX DMA failed, CSR=%08x\n", csr); _WRX(u, RG_DMA_CSR, 0); } } // // Выборка принятого пакета из буфера контроллера и постановка // его в FIFO драйвера // static int eth_receive_frame (struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); unsigned len; struct sk_buff *skb; unsigned char *data; unsigned frame_status = _R(u, RG_RX_FRAME_STATUS_FIFO); if (! (frame_status & F_OK)) { PDEBUG ("eth_receive_frame: failed, frame_status=%#08x\n", frame_status); ++dev->stats.rx_errors; return 0; } len = F_LEN (frame_status); if (len < 4 || len > dev->mtu + ETH_HEADER_SIZE + ETH_CRC_SIZE) { PDEBUG ("eth_receive_frame: bad length %d bytes, " "frame_status=%#08x\n", len, frame_status); ++dev->stats.rx_length_errors; return 0; } PDEBUG("eth_receive_frame: ok, frame_status=%#08x, length %d bytes\n", frame_status, len); if (frame_status & (F_MCM | F_MCHT)) dev->stats.multicast++; #if defined(CONFIG_MC30SF6) || defined(CONFIG_MC0428) ///попробуем сделать выравнивание на 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++; return 0; } // Выравниваем заголовок IP на байта. skb_reserve(skb, 8); #else // Размер пакета равен (len + 2) с выравниванием на 4 байта. // "+2", потому что мы выравниваем заголовок IP на 4 байта // (см. функцию skb_reserve() ниже) skb = dev_alloc_skb((len + 2 + 3) & ~3); if (unlikely(skb == NULL)) { printk(KERN_NOTICE "%s: Low memory, packet dropped.\n", dev->name); dev->stats.rx_dropped++; return 0; } // Выравниваем заголовок IP на байта. skb_reserve(skb, 2); #endif data = skb_put(skb, len); chip_read_rxfifo(u, mips_virt_to_phys ((unsigned) data), len); 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 1; } #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); } static void eth_disable_rx_irq(struct mc_eth *u) { _W(u, RG_CONTROL, _R(u, RG_CONTROL) & ~F_IRQ_RX_DONE); } 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, i; PDEBUG ("eth_poll, budget = %d\n", budget); while (work_done < budget) { nframes = F_NUM_FR(_R(u, RG_STATUS_RX)); _W(u, RG_STATUS_RX, 0); if (nframes == 0) { eth_enable_rx_irq(u); napi_complete(napi); return work_done; } for (i = 0; i < nframes; ++i) work_done += eth_receive_frame (u->pdev); } 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); #ifdef CONFIG_MULTICORE_ETH_NAPI PDEBUGG ("mask rx irq\n"); eth_disable_rx_irq(u); #else unsigned nframes; #endif for (;;) { unsigned status_rx = _R(u, RG_STATUS_RX); PDEBUG ("eth rx irq: STATUS_RX = %08x\n", status_rx); if (status_rx & (F_STATUS_OVF | F_FIFO_OVF)) { // Есть переполнение PDEBUG ("RX overflow!\n"); if (F_NUM_MISSED (status_rx)) dev->stats.rx_over_errors += F_NUM_MISSED (status_rx); else dev->stats.rx_over_errors++; _W(u, RG_STATUS_RX, 0); } #ifdef CONFIG_MULTICORE_ETH_NAPI if (status_rx & F_RX_DONE) { PDEBUG ("RX_DONE status set!\n"); if (napi_schedule_prep(&u->napi)) __napi_schedule(&u->napi); } else { PDEBUGG ("!RX_DONE\n"); _W(u, RG_STATUS_RX, 0); eth_enable_rx_irq(u); } return IRQ_HANDLED; #else if (! (status_rx & F_RX_DONE)) { return IRQ_HANDLED; } _W(u, RG_STATUS_RX, 0); // Выборка всех принятых пакетов nframes = F_NUM_FR (status_rx); while (nframes-- > 0) { if( eth_receive_frame (dev) < 1) { return IRQ_HANDLED; } } #endif } } // // Отправка пакета с помощью 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; 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 ("!"); _W(u, RG_TX_FRAME_CONTROL, F_DISENCAPFR | F_DISPAD | F_LENGTH(len) |F_TX_REQ); 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); } // // Обработка прерывания по выдаче пакета // 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 status_tx = _R(u, RG_STATUS_TX); PDEBUG ("transmit interrupt\n"); if (status_tx & F_ONTX_REQ) { // Передатчик ещё в процессе передачи - такого быть не должно PDEBUG ("eth tx irq: ONTX_REQ, STATUS_TX = %08x\n", status_tx); return IRQ_HANDLED; } _W(u, RG_STATUS_TX, 0); if (status_tx & (F_ONCOL | F_LATE_COLL)) { ++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)) { ++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); ++dev->stats.tx_errors; dev_kfree_skb(skb); return NETDEV_TX_OK; } disable_irq (u->tx_irq); if (! (_R(u, RG_STATUS_TX) & F_ONTX_REQ)) { PDEBUGG ("eth_start_xmit: transmit now"); chip_transmit_packet (skb, dev); dev_kfree_skb(skb); } 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; // Включение тактовой частоты EMAC MC_CLKEN |= MC_CLKEN_EMAC; udelay (10); #if defined(LAN8710) // Тактовый сигнал MDC не должен превышать 2.5 МГц _W(u, RG_MD_MODE, F_DIVIDER (mips_hpt_frequency / 2000000)); #endif // Поиск адреса PHY dev->base_addr = 31; for (;;) { id = mc_phy_read (u, PHY_ID1) << 16 | mc_phy_read (u, PHY_ID2); 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; } } } #if defined(LAN8710) printk ("eth_init: transceiver `%s' detected at address %d\n", ((id & PHY_ID_MASK) == PHY_ID2_NUMB) ? "LAN8710A" : "Unknown", u->phy); #else printk ("eth_init: transceiver `%s' detected at address %d\n", ((id & PHY_ID_MASK) == PHY_ID_KS8721BL) ? "KS8721" : "Unknown", u->phy); #endif // Сброс PHY mc_phy_write (u, PHY_CTL, PHY_CTL_RST); 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"); #if defined(LAN8710) mc_phy_write (u, PHY_CTL, ((1<<8)| 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); mc_phy_write (u, PHY_CTL, ((1<<8)|(1<<7)| PHY_CTL_ANEG_EN | PHY_CTL_ANEG_RST)); /// full duplex | collision test | autoneg enable | autoneg restart #else mc_phy_write (u, PHY_EXTCTL, PHY_EXTCTL_JABBER); // Автоподстройка (Auto-Negotiation) mc_phy_write (u, PHY_ADVRT, PHY_ADVRT_CSMA | PHY_ADVRT_10_HDX | PHY_ADVRT_10_FDX | PHY_ADVRT_100_HDX | PHY_ADVRT_100_FDX); mc_phy_write (u, PHY_CTL, PHY_CTL_ANEG_EN | PHY_CTL_ANEG_RST); #endif // Сброс приёмника и передатчика _W(u, RG_CONTROL, F_CP_TX | F_RST_TX | F_CP_RX | F_RST_RX); udelay (10); PDEBUGG("MAC_CONTROL: 0x%08x\n", _R(u, RG_CONTROL)); /* #if defined(LAN8710) // Общие режимы _W(u, RG_CONTROL, F_FULLD | // дуплексный режим F_EN_TX | // разрешение передачи F_EN_TX_DMA | // разрешение передающего DMА F_EN_RX | // разрешение приема //F_IRQ_TX_DONE | // прерывание от передачи F_IRQ_RX_DONE | // прерывание по приёму //F_IRQ_RX_OVF // прерывание по переполнению 0x00); PDEBUGG("MAC_CONTROL: 0x%08x\n", _R(u, RG_CONTROL)); #else */ // Общие режимы _W(u, RG_CONTROL, F_FULLD | // дуплексный режим F_EN_TX | // разрешение передачи F_EN_TX_DMA | // разрешение передающего DMА F_EN_RX | // разрешение приема F_IRQ_TX_DONE | // прерывание от передачи F_IRQ_RX_DONE | // прерывание по приёму F_IRQ_RX_OVF); // прерывание по переполнению PDEBUGG("MAC_CONTROL: 0x%08x\n", _R(u, RG_CONTROL)); /* #endif */ // Режимы приёма _W(u, RG_RX_FRAME_CONTROL, F_DIS_RCV_FCS | // не сохранять контрольную сумму F_ACC_TOOSHORT | // прием коротких кадров F_DIS_TOOLONG | // отбрасывание слишком длинных кадров F_DIS_FCSCHERR | // отбрасывание кадров с ошибкой CRC F_DIS_LENGTHERR); // отбрасывание кадров с ошибкой длины PDEBUGG("RX_FRAME_CONTROL: 0x%08x\n", _R(u, RG_RX_FRAME_CONTROL)); // Режимы передачи: // запрет формирования кадра в блоке передачи _W(u, RG_TX_FRAME_CONTROL, F_DISENCAPFR); PDEBUGG("TX_FRAME_CONTROL: 0x%08x\n", _R(u, RG_TX_FRAME_CONTROL)); // Режимы обработки коллизии _W(u, RG_IFS_COLL_MODE, F_ATTEMPT_NUM(15) | F_EN_CW | F_COLL_WIN(64) | F_JAMB(0xC3) | F_IFS(24)); #if !defined(LAN8710) // Тактовый сигнал MDC не должен превышать 2.5 МГц _W(u, RG_MD_MODE, F_DIVIDER (mips_hpt_frequency / 2000000)); #endif // Свой адрес dev->addr_assign_type = NET_ADDR_RANDOM; random_ether_addr(dev->perm_addr); eth_set_mac_addr(dev, dev->perm_addr); // Максимальный размер кадра _W(u, RG_RX_FR_MAXSIZE, ETH_MTU + ETH_HEADER_SIZE + ETH_CRC_SIZE); 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; } return 0; } static int eth_close (struct net_device *dev) { struct mc_eth *u = netdev_priv(dev); #ifdef CONFIG_MULTICORE_ETH_NAPI napi_disable(&u->napi); #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_hcdspeed; 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; #if defined(LAN8710) phy_hcdspeed = mc_phy_read (u, PHY_SCS); phy_hcdspeed &= PHY_SCS_HCDSPD_MSK; switch (phy_hcdspeed) { case PHY_SCS_HCDSPD_10HD: /* 10Base-T half duplex */ cmd->duplex = DUPLEX_HALF; cmd->speed = SPEED_10; break; case PHY_SCS_HCDSPD_10FD: /* 10Base-T full duplex */ cmd->duplex = DUPLEX_FULL; cmd->speed = SPEED_10; break; case PHY_SCS_HCDSPD_100HD: /* 100Base-TX half duplex */ cmd->duplex = DUPLEX_HALF; cmd->speed = SPEED_100; break; case PHY_SCS_HCDSPD_100FD: /* 100Base-TX full duplex */ cmd->duplex = DUPLEX_FULL; cmd->speed = SPEED_100; break; default: return 0; } #else switch (mc_phy_read (u, PHY_EXTCTL) & PHY_EXTCTL_MODE_MASK) { case PHY_EXTCTL_MODE_10_HDX: /* 10Base-T half duplex */ cmd->duplex = DUPLEX_HALF; cmd->speed = SPEED_10; break; case PHY_EXTCTL_MODE_100_HDX: /* 100Base-TX half duplex */ cmd->duplex = DUPLEX_HALF; cmd->speed = SPEED_100; break; case PHY_EXTCTL_MODE_10_FDX: /* 10Base-T full duplex */ cmd->duplex = DUPLEX_FULL; cmd->speed = SPEED_10; break; case PHY_EXTCTL_MODE_100_FDX: /* 100Base-TX full duplex */ cmd->duplex = DUPLEX_FULL; cmd->speed = SPEED_100; break; default: return 0; } #endif 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 int print_histogram (char *buf, int lengths [], int overall) { int len = 0, i, j; int nb_of_asterics; for (i = 0; i < 16; ++i) { nb_of_asterics = lengths [i] * 30 / overall; len += sprintf (buf + len, "len < %4d: ", (i + 1) * 100); for (j = 0; j < nb_of_asterics; ++j) *(buf + len++) = '*'; len += sprintf (buf + len, " (%d)\n", lengths [i]); } return len; } static int read_proc (char *buf, char **start, off_t offset, int count, int *eof, void *data) { struct net_device *dev = (struct net_device *) data; struct mc_eth *u = netdev_priv(dev); int len = 0; len += sprintf(buf+len, "=========================================\n"); len += sprintf(buf+len, "Histogram for received packets length:\n"); len += print_histogram(buf+len, u->proc.rx_lengths, dev->stats.rx_packets); len += sprintf(buf+len, "=========================================\n"); len += sprintf(buf+len, "Histogram for transmitted packets length:\n"); len += print_histogram(buf+len, u->proc.tx_lengths, dev->stats.tx_packets); *eof = 1; return len; } /* static int read_proc(struct seq_file *m, void* v) { struct net_device *dev = m->private; struct mc_eth *u = netdev_priv(dev); int i; int nb_of_asterics; int j; seq_printf(m, "=========================================\n"); seq_printf(m, "Histogram for received packets length:\n"); seq_printf(m, "=========================================\n"); seq_printf(m, "Histogram for transmitted packets length:\n"); for (i = 0; i < 16; ++i) { nb_of_asterics = u->proc.tx_lengths[i] * 30 / dev->stats.tx_packets; seq_printf(m, "len < %4d: ", (i + 1) * 100); for (j = 0; j < nb_of_asterics; ++j) seq_printf(m, "*"); seq_printf(m, " (%d)\n", u->proc.tx_lengths[i]); } return 0; } */ 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, #ifdef CONFIG_NET_POLL_CONTROLLER //.ndo_poll_controller = eth_poll_controller, #endif }; 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"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } 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"); if (r == NULL) { ret = -ENODEV; goto err_get_res; } 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: %p, TX_DMA: %p\n", u->eth_regs, u->dma_rx_regs, u->dma_tx_regs); u->tx_irq = platform_get_irq_byname(pdev, "txframe_irq"); if (u->tx_irq < 0) { ret = -EINVAL; goto err_get_res; } u->rx_irq = platform_get_irq_byname(pdev, "rxframe_irq"); if (u->rx_irq < 0) { 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; #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); #endif #ifdef CONFIG_MULTICORE_ETH_NAPI // Запуск 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; } /// TODO! //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");