18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * linux/drivers/net/ethernet/ethoc.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Avionic Design Development GmbH 68c2ecf20Sopenharmony_ci * Copyright (C) 2008-2009 Avionic Design GmbH 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Written by Thierry Reding <thierry.reding@avionic-design.de> 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 128c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 138c2ecf20Sopenharmony_ci#include <linux/clk.h> 148c2ecf20Sopenharmony_ci#include <linux/crc32.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/io.h> 178c2ecf20Sopenharmony_ci#include <linux/mii.h> 188c2ecf20Sopenharmony_ci#include <linux/phy.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/sched.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/of_net.h> 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci#include <net/ethoc.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic int buffer_size = 0x8000; /* 32 KBytes */ 288c2ecf20Sopenharmony_cimodule_param(buffer_size, int, 0); 298c2ecf20Sopenharmony_ciMODULE_PARM_DESC(buffer_size, "DMA buffer allocation size"); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* register offsets */ 328c2ecf20Sopenharmony_ci#define MODER 0x00 338c2ecf20Sopenharmony_ci#define INT_SOURCE 0x04 348c2ecf20Sopenharmony_ci#define INT_MASK 0x08 358c2ecf20Sopenharmony_ci#define IPGT 0x0c 368c2ecf20Sopenharmony_ci#define IPGR1 0x10 378c2ecf20Sopenharmony_ci#define IPGR2 0x14 388c2ecf20Sopenharmony_ci#define PACKETLEN 0x18 398c2ecf20Sopenharmony_ci#define COLLCONF 0x1c 408c2ecf20Sopenharmony_ci#define TX_BD_NUM 0x20 418c2ecf20Sopenharmony_ci#define CTRLMODER 0x24 428c2ecf20Sopenharmony_ci#define MIIMODER 0x28 438c2ecf20Sopenharmony_ci#define MIICOMMAND 0x2c 448c2ecf20Sopenharmony_ci#define MIIADDRESS 0x30 458c2ecf20Sopenharmony_ci#define MIITX_DATA 0x34 468c2ecf20Sopenharmony_ci#define MIIRX_DATA 0x38 478c2ecf20Sopenharmony_ci#define MIISTATUS 0x3c 488c2ecf20Sopenharmony_ci#define MAC_ADDR0 0x40 498c2ecf20Sopenharmony_ci#define MAC_ADDR1 0x44 508c2ecf20Sopenharmony_ci#define ETH_HASH0 0x48 518c2ecf20Sopenharmony_ci#define ETH_HASH1 0x4c 528c2ecf20Sopenharmony_ci#define ETH_TXCTRL 0x50 538c2ecf20Sopenharmony_ci#define ETH_END 0x54 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* mode register */ 568c2ecf20Sopenharmony_ci#define MODER_RXEN (1 << 0) /* receive enable */ 578c2ecf20Sopenharmony_ci#define MODER_TXEN (1 << 1) /* transmit enable */ 588c2ecf20Sopenharmony_ci#define MODER_NOPRE (1 << 2) /* no preamble */ 598c2ecf20Sopenharmony_ci#define MODER_BRO (1 << 3) /* broadcast address */ 608c2ecf20Sopenharmony_ci#define MODER_IAM (1 << 4) /* individual address mode */ 618c2ecf20Sopenharmony_ci#define MODER_PRO (1 << 5) /* promiscuous mode */ 628c2ecf20Sopenharmony_ci#define MODER_IFG (1 << 6) /* interframe gap for incoming frames */ 638c2ecf20Sopenharmony_ci#define MODER_LOOP (1 << 7) /* loopback */ 648c2ecf20Sopenharmony_ci#define MODER_NBO (1 << 8) /* no back-off */ 658c2ecf20Sopenharmony_ci#define MODER_EDE (1 << 9) /* excess defer enable */ 668c2ecf20Sopenharmony_ci#define MODER_FULLD (1 << 10) /* full duplex */ 678c2ecf20Sopenharmony_ci#define MODER_RESET (1 << 11) /* FIXME: reset (undocumented) */ 688c2ecf20Sopenharmony_ci#define MODER_DCRC (1 << 12) /* delayed CRC enable */ 698c2ecf20Sopenharmony_ci#define MODER_CRC (1 << 13) /* CRC enable */ 708c2ecf20Sopenharmony_ci#define MODER_HUGE (1 << 14) /* huge packets enable */ 718c2ecf20Sopenharmony_ci#define MODER_PAD (1 << 15) /* padding enabled */ 728c2ecf20Sopenharmony_ci#define MODER_RSM (1 << 16) /* receive small packets */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* interrupt source and mask registers */ 758c2ecf20Sopenharmony_ci#define INT_MASK_TXF (1 << 0) /* transmit frame */ 768c2ecf20Sopenharmony_ci#define INT_MASK_TXE (1 << 1) /* transmit error */ 778c2ecf20Sopenharmony_ci#define INT_MASK_RXF (1 << 2) /* receive frame */ 788c2ecf20Sopenharmony_ci#define INT_MASK_RXE (1 << 3) /* receive error */ 798c2ecf20Sopenharmony_ci#define INT_MASK_BUSY (1 << 4) 808c2ecf20Sopenharmony_ci#define INT_MASK_TXC (1 << 5) /* transmit control frame */ 818c2ecf20Sopenharmony_ci#define INT_MASK_RXC (1 << 6) /* receive control frame */ 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define INT_MASK_TX (INT_MASK_TXF | INT_MASK_TXE) 848c2ecf20Sopenharmony_ci#define INT_MASK_RX (INT_MASK_RXF | INT_MASK_RXE) 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci#define INT_MASK_ALL ( \ 878c2ecf20Sopenharmony_ci INT_MASK_TXF | INT_MASK_TXE | \ 888c2ecf20Sopenharmony_ci INT_MASK_RXF | INT_MASK_RXE | \ 898c2ecf20Sopenharmony_ci INT_MASK_TXC | INT_MASK_RXC | \ 908c2ecf20Sopenharmony_ci INT_MASK_BUSY \ 918c2ecf20Sopenharmony_ci ) 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/* packet length register */ 948c2ecf20Sopenharmony_ci#define PACKETLEN_MIN(min) (((min) & 0xffff) << 16) 958c2ecf20Sopenharmony_ci#define PACKETLEN_MAX(max) (((max) & 0xffff) << 0) 968c2ecf20Sopenharmony_ci#define PACKETLEN_MIN_MAX(min, max) (PACKETLEN_MIN(min) | \ 978c2ecf20Sopenharmony_ci PACKETLEN_MAX(max)) 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* transmit buffer number register */ 1008c2ecf20Sopenharmony_ci#define TX_BD_NUM_VAL(x) (((x) <= 0x80) ? (x) : 0x80) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* control module mode register */ 1038c2ecf20Sopenharmony_ci#define CTRLMODER_PASSALL (1 << 0) /* pass all receive frames */ 1048c2ecf20Sopenharmony_ci#define CTRLMODER_RXFLOW (1 << 1) /* receive control flow */ 1058c2ecf20Sopenharmony_ci#define CTRLMODER_TXFLOW (1 << 2) /* transmit control flow */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/* MII mode register */ 1088c2ecf20Sopenharmony_ci#define MIIMODER_CLKDIV(x) ((x) & 0xfe) /* needs to be an even number */ 1098c2ecf20Sopenharmony_ci#define MIIMODER_NOPRE (1 << 8) /* no preamble */ 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* MII command register */ 1128c2ecf20Sopenharmony_ci#define MIICOMMAND_SCAN (1 << 0) /* scan status */ 1138c2ecf20Sopenharmony_ci#define MIICOMMAND_READ (1 << 1) /* read status */ 1148c2ecf20Sopenharmony_ci#define MIICOMMAND_WRITE (1 << 2) /* write control data */ 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/* MII address register */ 1178c2ecf20Sopenharmony_ci#define MIIADDRESS_FIAD(x) (((x) & 0x1f) << 0) 1188c2ecf20Sopenharmony_ci#define MIIADDRESS_RGAD(x) (((x) & 0x1f) << 8) 1198c2ecf20Sopenharmony_ci#define MIIADDRESS_ADDR(phy, reg) (MIIADDRESS_FIAD(phy) | \ 1208c2ecf20Sopenharmony_ci MIIADDRESS_RGAD(reg)) 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/* MII transmit data register */ 1238c2ecf20Sopenharmony_ci#define MIITX_DATA_VAL(x) ((x) & 0xffff) 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/* MII receive data register */ 1268c2ecf20Sopenharmony_ci#define MIIRX_DATA_VAL(x) ((x) & 0xffff) 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci/* MII status register */ 1298c2ecf20Sopenharmony_ci#define MIISTATUS_LINKFAIL (1 << 0) 1308c2ecf20Sopenharmony_ci#define MIISTATUS_BUSY (1 << 1) 1318c2ecf20Sopenharmony_ci#define MIISTATUS_INVALID (1 << 2) 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* TX buffer descriptor */ 1348c2ecf20Sopenharmony_ci#define TX_BD_CS (1 << 0) /* carrier sense lost */ 1358c2ecf20Sopenharmony_ci#define TX_BD_DF (1 << 1) /* defer indication */ 1368c2ecf20Sopenharmony_ci#define TX_BD_LC (1 << 2) /* late collision */ 1378c2ecf20Sopenharmony_ci#define TX_BD_RL (1 << 3) /* retransmission limit */ 1388c2ecf20Sopenharmony_ci#define TX_BD_RETRY_MASK (0x00f0) 1398c2ecf20Sopenharmony_ci#define TX_BD_RETRY(x) (((x) & 0x00f0) >> 4) 1408c2ecf20Sopenharmony_ci#define TX_BD_UR (1 << 8) /* transmitter underrun */ 1418c2ecf20Sopenharmony_ci#define TX_BD_CRC (1 << 11) /* TX CRC enable */ 1428c2ecf20Sopenharmony_ci#define TX_BD_PAD (1 << 12) /* pad enable for short packets */ 1438c2ecf20Sopenharmony_ci#define TX_BD_WRAP (1 << 13) 1448c2ecf20Sopenharmony_ci#define TX_BD_IRQ (1 << 14) /* interrupt request enable */ 1458c2ecf20Sopenharmony_ci#define TX_BD_READY (1 << 15) /* TX buffer ready */ 1468c2ecf20Sopenharmony_ci#define TX_BD_LEN(x) (((x) & 0xffff) << 16) 1478c2ecf20Sopenharmony_ci#define TX_BD_LEN_MASK (0xffff << 16) 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci#define TX_BD_STATS (TX_BD_CS | TX_BD_DF | TX_BD_LC | \ 1508c2ecf20Sopenharmony_ci TX_BD_RL | TX_BD_RETRY_MASK | TX_BD_UR) 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/* RX buffer descriptor */ 1538c2ecf20Sopenharmony_ci#define RX_BD_LC (1 << 0) /* late collision */ 1548c2ecf20Sopenharmony_ci#define RX_BD_CRC (1 << 1) /* RX CRC error */ 1558c2ecf20Sopenharmony_ci#define RX_BD_SF (1 << 2) /* short frame */ 1568c2ecf20Sopenharmony_ci#define RX_BD_TL (1 << 3) /* too long */ 1578c2ecf20Sopenharmony_ci#define RX_BD_DN (1 << 4) /* dribble nibble */ 1588c2ecf20Sopenharmony_ci#define RX_BD_IS (1 << 5) /* invalid symbol */ 1598c2ecf20Sopenharmony_ci#define RX_BD_OR (1 << 6) /* receiver overrun */ 1608c2ecf20Sopenharmony_ci#define RX_BD_MISS (1 << 7) 1618c2ecf20Sopenharmony_ci#define RX_BD_CF (1 << 8) /* control frame */ 1628c2ecf20Sopenharmony_ci#define RX_BD_WRAP (1 << 13) 1638c2ecf20Sopenharmony_ci#define RX_BD_IRQ (1 << 14) /* interrupt request enable */ 1648c2ecf20Sopenharmony_ci#define RX_BD_EMPTY (1 << 15) 1658c2ecf20Sopenharmony_ci#define RX_BD_LEN(x) (((x) & 0xffff) << 16) 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci#define RX_BD_STATS (RX_BD_LC | RX_BD_CRC | RX_BD_SF | RX_BD_TL | \ 1688c2ecf20Sopenharmony_ci RX_BD_DN | RX_BD_IS | RX_BD_OR | RX_BD_MISS) 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci#define ETHOC_BUFSIZ 1536 1718c2ecf20Sopenharmony_ci#define ETHOC_ZLEN 64 1728c2ecf20Sopenharmony_ci#define ETHOC_BD_BASE 0x400 1738c2ecf20Sopenharmony_ci#define ETHOC_TIMEOUT (HZ / 2) 1748c2ecf20Sopenharmony_ci#define ETHOC_MII_TIMEOUT (1 + (HZ / 5)) 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci/** 1778c2ecf20Sopenharmony_ci * struct ethoc - driver-private device structure 1788c2ecf20Sopenharmony_ci * @iobase: pointer to I/O memory region 1798c2ecf20Sopenharmony_ci * @membase: pointer to buffer memory region 1808c2ecf20Sopenharmony_ci * @big_endian: just big or little (endian) 1818c2ecf20Sopenharmony_ci * @num_bd: number of buffer descriptors 1828c2ecf20Sopenharmony_ci * @num_tx: number of send buffers 1838c2ecf20Sopenharmony_ci * @cur_tx: last send buffer written 1848c2ecf20Sopenharmony_ci * @dty_tx: last buffer actually sent 1858c2ecf20Sopenharmony_ci * @num_rx: number of receive buffers 1868c2ecf20Sopenharmony_ci * @cur_rx: current receive buffer 1878c2ecf20Sopenharmony_ci * @vma: pointer to array of virtual memory addresses for buffers 1888c2ecf20Sopenharmony_ci * @netdev: pointer to network device structure 1898c2ecf20Sopenharmony_ci * @napi: NAPI structure 1908c2ecf20Sopenharmony_ci * @msg_enable: device state flags 1918c2ecf20Sopenharmony_ci * @lock: device lock 1928c2ecf20Sopenharmony_ci * @mdio: MDIO bus for PHY access 1938c2ecf20Sopenharmony_ci * @clk: clock 1948c2ecf20Sopenharmony_ci * @phy_id: address of attached PHY 1958c2ecf20Sopenharmony_ci * @old_link: previous link info 1968c2ecf20Sopenharmony_ci * @old_duplex: previous duplex info 1978c2ecf20Sopenharmony_ci */ 1988c2ecf20Sopenharmony_cistruct ethoc { 1998c2ecf20Sopenharmony_ci void __iomem *iobase; 2008c2ecf20Sopenharmony_ci void __iomem *membase; 2018c2ecf20Sopenharmony_ci bool big_endian; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci unsigned int num_bd; 2048c2ecf20Sopenharmony_ci unsigned int num_tx; 2058c2ecf20Sopenharmony_ci unsigned int cur_tx; 2068c2ecf20Sopenharmony_ci unsigned int dty_tx; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci unsigned int num_rx; 2098c2ecf20Sopenharmony_ci unsigned int cur_rx; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci void **vma; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci struct net_device *netdev; 2148c2ecf20Sopenharmony_ci struct napi_struct napi; 2158c2ecf20Sopenharmony_ci u32 msg_enable; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spinlock_t lock; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci struct mii_bus *mdio; 2208c2ecf20Sopenharmony_ci struct clk *clk; 2218c2ecf20Sopenharmony_ci s8 phy_id; 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci int old_link; 2248c2ecf20Sopenharmony_ci int old_duplex; 2258c2ecf20Sopenharmony_ci}; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci/** 2288c2ecf20Sopenharmony_ci * struct ethoc_bd - buffer descriptor 2298c2ecf20Sopenharmony_ci * @stat: buffer statistics 2308c2ecf20Sopenharmony_ci * @addr: physical memory address 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_cistruct ethoc_bd { 2338c2ecf20Sopenharmony_ci u32 stat; 2348c2ecf20Sopenharmony_ci u32 addr; 2358c2ecf20Sopenharmony_ci}; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_cistatic inline u32 ethoc_read(struct ethoc *dev, loff_t offset) 2388c2ecf20Sopenharmony_ci{ 2398c2ecf20Sopenharmony_ci if (dev->big_endian) 2408c2ecf20Sopenharmony_ci return ioread32be(dev->iobase + offset); 2418c2ecf20Sopenharmony_ci else 2428c2ecf20Sopenharmony_ci return ioread32(dev->iobase + offset); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic inline void ethoc_write(struct ethoc *dev, loff_t offset, u32 data) 2468c2ecf20Sopenharmony_ci{ 2478c2ecf20Sopenharmony_ci if (dev->big_endian) 2488c2ecf20Sopenharmony_ci iowrite32be(data, dev->iobase + offset); 2498c2ecf20Sopenharmony_ci else 2508c2ecf20Sopenharmony_ci iowrite32(data, dev->iobase + offset); 2518c2ecf20Sopenharmony_ci} 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic inline void ethoc_read_bd(struct ethoc *dev, int index, 2548c2ecf20Sopenharmony_ci struct ethoc_bd *bd) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); 2578c2ecf20Sopenharmony_ci bd->stat = ethoc_read(dev, offset + 0); 2588c2ecf20Sopenharmony_ci bd->addr = ethoc_read(dev, offset + 4); 2598c2ecf20Sopenharmony_ci} 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_cistatic inline void ethoc_write_bd(struct ethoc *dev, int index, 2628c2ecf20Sopenharmony_ci const struct ethoc_bd *bd) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci loff_t offset = ETHOC_BD_BASE + (index * sizeof(struct ethoc_bd)); 2658c2ecf20Sopenharmony_ci ethoc_write(dev, offset + 0, bd->stat); 2668c2ecf20Sopenharmony_ci ethoc_write(dev, offset + 4, bd->addr); 2678c2ecf20Sopenharmony_ci} 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic inline void ethoc_enable_irq(struct ethoc *dev, u32 mask) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci u32 imask = ethoc_read(dev, INT_MASK); 2728c2ecf20Sopenharmony_ci imask |= mask; 2738c2ecf20Sopenharmony_ci ethoc_write(dev, INT_MASK, imask); 2748c2ecf20Sopenharmony_ci} 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_cistatic inline void ethoc_disable_irq(struct ethoc *dev, u32 mask) 2778c2ecf20Sopenharmony_ci{ 2788c2ecf20Sopenharmony_ci u32 imask = ethoc_read(dev, INT_MASK); 2798c2ecf20Sopenharmony_ci imask &= ~mask; 2808c2ecf20Sopenharmony_ci ethoc_write(dev, INT_MASK, imask); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_cistatic inline void ethoc_ack_irq(struct ethoc *dev, u32 mask) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci ethoc_write(dev, INT_SOURCE, mask); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic inline void ethoc_enable_rx_and_tx(struct ethoc *dev) 2898c2ecf20Sopenharmony_ci{ 2908c2ecf20Sopenharmony_ci u32 mode = ethoc_read(dev, MODER); 2918c2ecf20Sopenharmony_ci mode |= MODER_RXEN | MODER_TXEN; 2928c2ecf20Sopenharmony_ci ethoc_write(dev, MODER, mode); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic inline void ethoc_disable_rx_and_tx(struct ethoc *dev) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci u32 mode = ethoc_read(dev, MODER); 2988c2ecf20Sopenharmony_ci mode &= ~(MODER_RXEN | MODER_TXEN); 2998c2ecf20Sopenharmony_ci ethoc_write(dev, MODER, mode); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic int ethoc_init_ring(struct ethoc *dev, unsigned long mem_start) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct ethoc_bd bd; 3058c2ecf20Sopenharmony_ci int i; 3068c2ecf20Sopenharmony_ci void *vma; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci dev->cur_tx = 0; 3098c2ecf20Sopenharmony_ci dev->dty_tx = 0; 3108c2ecf20Sopenharmony_ci dev->cur_rx = 0; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci ethoc_write(dev, TX_BD_NUM, dev->num_tx); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* setup transmission buffers */ 3158c2ecf20Sopenharmony_ci bd.addr = mem_start; 3168c2ecf20Sopenharmony_ci bd.stat = TX_BD_IRQ | TX_BD_CRC; 3178c2ecf20Sopenharmony_ci vma = dev->membase; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_tx; i++) { 3208c2ecf20Sopenharmony_ci if (i == dev->num_tx - 1) 3218c2ecf20Sopenharmony_ci bd.stat |= TX_BD_WRAP; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci ethoc_write_bd(dev, i, &bd); 3248c2ecf20Sopenharmony_ci bd.addr += ETHOC_BUFSIZ; 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci dev->vma[i] = vma; 3278c2ecf20Sopenharmony_ci vma += ETHOC_BUFSIZ; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci bd.stat = RX_BD_EMPTY | RX_BD_IRQ; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci for (i = 0; i < dev->num_rx; i++) { 3338c2ecf20Sopenharmony_ci if (i == dev->num_rx - 1) 3348c2ecf20Sopenharmony_ci bd.stat |= RX_BD_WRAP; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci ethoc_write_bd(dev, dev->num_tx + i, &bd); 3378c2ecf20Sopenharmony_ci bd.addr += ETHOC_BUFSIZ; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci dev->vma[dev->num_tx + i] = vma; 3408c2ecf20Sopenharmony_ci vma += ETHOC_BUFSIZ; 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic int ethoc_reset(struct ethoc *dev) 3478c2ecf20Sopenharmony_ci{ 3488c2ecf20Sopenharmony_ci u32 mode; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* TODO: reset controller? */ 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ethoc_disable_rx_and_tx(dev); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* TODO: setup registers */ 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci /* enable FCS generation and automatic padding */ 3578c2ecf20Sopenharmony_ci mode = ethoc_read(dev, MODER); 3588c2ecf20Sopenharmony_ci mode |= MODER_CRC | MODER_PAD; 3598c2ecf20Sopenharmony_ci ethoc_write(dev, MODER, mode); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* set full-duplex mode */ 3628c2ecf20Sopenharmony_ci mode = ethoc_read(dev, MODER); 3638c2ecf20Sopenharmony_ci mode |= MODER_FULLD; 3648c2ecf20Sopenharmony_ci ethoc_write(dev, MODER, mode); 3658c2ecf20Sopenharmony_ci ethoc_write(dev, IPGT, 0x15); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ethoc_ack_irq(dev, INT_MASK_ALL); 3688c2ecf20Sopenharmony_ci ethoc_enable_irq(dev, INT_MASK_ALL); 3698c2ecf20Sopenharmony_ci ethoc_enable_rx_and_tx(dev); 3708c2ecf20Sopenharmony_ci return 0; 3718c2ecf20Sopenharmony_ci} 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_cistatic unsigned int ethoc_update_rx_stats(struct ethoc *dev, 3748c2ecf20Sopenharmony_ci struct ethoc_bd *bd) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct net_device *netdev = dev->netdev; 3778c2ecf20Sopenharmony_ci unsigned int ret = 0; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_TL) { 3808c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: frame too long\n"); 3818c2ecf20Sopenharmony_ci netdev->stats.rx_length_errors++; 3828c2ecf20Sopenharmony_ci ret++; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_SF) { 3868c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: frame too short\n"); 3878c2ecf20Sopenharmony_ci netdev->stats.rx_length_errors++; 3888c2ecf20Sopenharmony_ci ret++; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_DN) { 3928c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: dribble nibble\n"); 3938c2ecf20Sopenharmony_ci netdev->stats.rx_frame_errors++; 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_CRC) { 3978c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: wrong CRC\n"); 3988c2ecf20Sopenharmony_ci netdev->stats.rx_crc_errors++; 3998c2ecf20Sopenharmony_ci ret++; 4008c2ecf20Sopenharmony_ci } 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_OR) { 4038c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: overrun\n"); 4048c2ecf20Sopenharmony_ci netdev->stats.rx_over_errors++; 4058c2ecf20Sopenharmony_ci ret++; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_MISS) 4098c2ecf20Sopenharmony_ci netdev->stats.rx_missed_errors++; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (bd->stat & RX_BD_LC) { 4128c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "RX: late collision\n"); 4138c2ecf20Sopenharmony_ci netdev->stats.collisions++; 4148c2ecf20Sopenharmony_ci ret++; 4158c2ecf20Sopenharmony_ci } 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ret; 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic int ethoc_rx(struct net_device *dev, int limit) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 4238c2ecf20Sopenharmony_ci int count; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci for (count = 0; count < limit; ++count) { 4268c2ecf20Sopenharmony_ci unsigned int entry; 4278c2ecf20Sopenharmony_ci struct ethoc_bd bd; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci entry = priv->num_tx + priv->cur_rx; 4308c2ecf20Sopenharmony_ci ethoc_read_bd(priv, entry, &bd); 4318c2ecf20Sopenharmony_ci if (bd.stat & RX_BD_EMPTY) { 4328c2ecf20Sopenharmony_ci ethoc_ack_irq(priv, INT_MASK_RX); 4338c2ecf20Sopenharmony_ci /* If packet (interrupt) came in between checking 4348c2ecf20Sopenharmony_ci * BD_EMTPY and clearing the interrupt source, then we 4358c2ecf20Sopenharmony_ci * risk missing the packet as the RX interrupt won't 4368c2ecf20Sopenharmony_ci * trigger right away when we reenable it; hence, check 4378c2ecf20Sopenharmony_ci * BD_EMTPY here again to make sure there isn't such a 4388c2ecf20Sopenharmony_ci * packet waiting for us... 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci ethoc_read_bd(priv, entry, &bd); 4418c2ecf20Sopenharmony_ci if (bd.stat & RX_BD_EMPTY) 4428c2ecf20Sopenharmony_ci break; 4438c2ecf20Sopenharmony_ci } 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (ethoc_update_rx_stats(priv, &bd) == 0) { 4468c2ecf20Sopenharmony_ci int size = bd.stat >> 16; 4478c2ecf20Sopenharmony_ci struct sk_buff *skb; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci size -= 4; /* strip the CRC */ 4508c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(dev, size); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (likely(skb)) { 4538c2ecf20Sopenharmony_ci void *src = priv->vma[entry]; 4548c2ecf20Sopenharmony_ci memcpy_fromio(skb_put(skb, size), src, size); 4558c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 4568c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 4578c2ecf20Sopenharmony_ci dev->stats.rx_bytes += size; 4588c2ecf20Sopenharmony_ci netif_receive_skb(skb); 4598c2ecf20Sopenharmony_ci } else { 4608c2ecf20Sopenharmony_ci if (net_ratelimit()) 4618c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 4628c2ecf20Sopenharmony_ci "low on memory - packet dropped\n"); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 4658c2ecf20Sopenharmony_ci break; 4668c2ecf20Sopenharmony_ci } 4678c2ecf20Sopenharmony_ci } 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci /* clear the buffer descriptor so it can be reused */ 4708c2ecf20Sopenharmony_ci bd.stat &= ~RX_BD_STATS; 4718c2ecf20Sopenharmony_ci bd.stat |= RX_BD_EMPTY; 4728c2ecf20Sopenharmony_ci ethoc_write_bd(priv, entry, &bd); 4738c2ecf20Sopenharmony_ci if (++priv->cur_rx == priv->num_rx) 4748c2ecf20Sopenharmony_ci priv->cur_rx = 0; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return count; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic void ethoc_update_tx_stats(struct ethoc *dev, struct ethoc_bd *bd) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct net_device *netdev = dev->netdev; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci if (bd->stat & TX_BD_LC) { 4858c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "TX: late collision\n"); 4868c2ecf20Sopenharmony_ci netdev->stats.tx_window_errors++; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if (bd->stat & TX_BD_RL) { 4908c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "TX: retransmit limit\n"); 4918c2ecf20Sopenharmony_ci netdev->stats.tx_aborted_errors++; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci if (bd->stat & TX_BD_UR) { 4958c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "TX: underrun\n"); 4968c2ecf20Sopenharmony_ci netdev->stats.tx_fifo_errors++; 4978c2ecf20Sopenharmony_ci } 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci if (bd->stat & TX_BD_CS) { 5008c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "TX: carrier sense lost\n"); 5018c2ecf20Sopenharmony_ci netdev->stats.tx_carrier_errors++; 5028c2ecf20Sopenharmony_ci } 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (bd->stat & TX_BD_STATS) 5058c2ecf20Sopenharmony_ci netdev->stats.tx_errors++; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci netdev->stats.collisions += (bd->stat >> 4) & 0xf; 5088c2ecf20Sopenharmony_ci netdev->stats.tx_bytes += bd->stat >> 16; 5098c2ecf20Sopenharmony_ci netdev->stats.tx_packets++; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_cistatic int ethoc_tx(struct net_device *dev, int limit) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 5158c2ecf20Sopenharmony_ci int count; 5168c2ecf20Sopenharmony_ci struct ethoc_bd bd; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci for (count = 0; count < limit; ++count) { 5198c2ecf20Sopenharmony_ci unsigned int entry; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci entry = priv->dty_tx & (priv->num_tx-1); 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci ethoc_read_bd(priv, entry, &bd); 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (bd.stat & TX_BD_READY || (priv->dty_tx == priv->cur_tx)) { 5268c2ecf20Sopenharmony_ci ethoc_ack_irq(priv, INT_MASK_TX); 5278c2ecf20Sopenharmony_ci /* If interrupt came in between reading in the BD 5288c2ecf20Sopenharmony_ci * and clearing the interrupt source, then we risk 5298c2ecf20Sopenharmony_ci * missing the event as the TX interrupt won't trigger 5308c2ecf20Sopenharmony_ci * right away when we reenable it; hence, check 5318c2ecf20Sopenharmony_ci * BD_EMPTY here again to make sure there isn't such an 5328c2ecf20Sopenharmony_ci * event pending... 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci ethoc_read_bd(priv, entry, &bd); 5358c2ecf20Sopenharmony_ci if (bd.stat & TX_BD_READY || 5368c2ecf20Sopenharmony_ci (priv->dty_tx == priv->cur_tx)) 5378c2ecf20Sopenharmony_ci break; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci ethoc_update_tx_stats(priv, &bd); 5418c2ecf20Sopenharmony_ci priv->dty_tx++; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci if ((priv->cur_tx - priv->dty_tx) <= (priv->num_tx / 2)) 5458c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci return count; 5488c2ecf20Sopenharmony_ci} 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_cistatic irqreturn_t ethoc_interrupt(int irq, void *dev_id) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 5538c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 5548c2ecf20Sopenharmony_ci u32 pending; 5558c2ecf20Sopenharmony_ci u32 mask; 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci /* Figure out what triggered the interrupt... 5588c2ecf20Sopenharmony_ci * The tricky bit here is that the interrupt source bits get 5598c2ecf20Sopenharmony_ci * set in INT_SOURCE for an event regardless of whether that 5608c2ecf20Sopenharmony_ci * event is masked or not. Thus, in order to figure out what 5618c2ecf20Sopenharmony_ci * triggered the interrupt, we need to remove the sources 5628c2ecf20Sopenharmony_ci * for all events that are currently masked. This behaviour 5638c2ecf20Sopenharmony_ci * is not particularly well documented but reasonable... 5648c2ecf20Sopenharmony_ci */ 5658c2ecf20Sopenharmony_ci mask = ethoc_read(priv, INT_MASK); 5668c2ecf20Sopenharmony_ci pending = ethoc_read(priv, INT_SOURCE); 5678c2ecf20Sopenharmony_ci pending &= mask; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci if (unlikely(pending == 0)) 5708c2ecf20Sopenharmony_ci return IRQ_NONE; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci ethoc_ack_irq(priv, pending); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* We always handle the dropped packet interrupt */ 5758c2ecf20Sopenharmony_ci if (pending & INT_MASK_BUSY) { 5768c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "packet dropped\n"); 5778c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 5788c2ecf20Sopenharmony_ci } 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* Handle receive/transmit event by switching to polling */ 5818c2ecf20Sopenharmony_ci if (pending & (INT_MASK_TX | INT_MASK_RX)) { 5828c2ecf20Sopenharmony_ci ethoc_disable_irq(priv, INT_MASK_TX | INT_MASK_RX); 5838c2ecf20Sopenharmony_ci napi_schedule(&priv->napi); 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5878c2ecf20Sopenharmony_ci} 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_cistatic int ethoc_get_mac_address(struct net_device *dev, void *addr) 5908c2ecf20Sopenharmony_ci{ 5918c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 5928c2ecf20Sopenharmony_ci u8 *mac = (u8 *)addr; 5938c2ecf20Sopenharmony_ci u32 reg; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci reg = ethoc_read(priv, MAC_ADDR0); 5968c2ecf20Sopenharmony_ci mac[2] = (reg >> 24) & 0xff; 5978c2ecf20Sopenharmony_ci mac[3] = (reg >> 16) & 0xff; 5988c2ecf20Sopenharmony_ci mac[4] = (reg >> 8) & 0xff; 5998c2ecf20Sopenharmony_ci mac[5] = (reg >> 0) & 0xff; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci reg = ethoc_read(priv, MAC_ADDR1); 6028c2ecf20Sopenharmony_ci mac[0] = (reg >> 8) & 0xff; 6038c2ecf20Sopenharmony_ci mac[1] = (reg >> 0) & 0xff; 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_cistatic int ethoc_poll(struct napi_struct *napi, int budget) 6098c2ecf20Sopenharmony_ci{ 6108c2ecf20Sopenharmony_ci struct ethoc *priv = container_of(napi, struct ethoc, napi); 6118c2ecf20Sopenharmony_ci int rx_work_done = 0; 6128c2ecf20Sopenharmony_ci int tx_work_done = 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci rx_work_done = ethoc_rx(priv->netdev, budget); 6158c2ecf20Sopenharmony_ci tx_work_done = ethoc_tx(priv->netdev, budget); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci if (rx_work_done < budget && tx_work_done < budget) { 6188c2ecf20Sopenharmony_ci napi_complete_done(napi, rx_work_done); 6198c2ecf20Sopenharmony_ci ethoc_enable_irq(priv, INT_MASK_TX | INT_MASK_RX); 6208c2ecf20Sopenharmony_ci } 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci return rx_work_done; 6238c2ecf20Sopenharmony_ci} 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_cistatic int ethoc_mdio_read(struct mii_bus *bus, int phy, int reg) 6268c2ecf20Sopenharmony_ci{ 6278c2ecf20Sopenharmony_ci struct ethoc *priv = bus->priv; 6288c2ecf20Sopenharmony_ci int i; 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg)); 6318c2ecf20Sopenharmony_ci ethoc_write(priv, MIICOMMAND, MIICOMMAND_READ); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 6348c2ecf20Sopenharmony_ci u32 status = ethoc_read(priv, MIISTATUS); 6358c2ecf20Sopenharmony_ci if (!(status & MIISTATUS_BUSY)) { 6368c2ecf20Sopenharmony_ci u32 data = ethoc_read(priv, MIIRX_DATA); 6378c2ecf20Sopenharmony_ci /* reset MII command register */ 6388c2ecf20Sopenharmony_ci ethoc_write(priv, MIICOMMAND, 0); 6398c2ecf20Sopenharmony_ci return data; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci usleep_range(100, 200); 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci return -EBUSY; 6458c2ecf20Sopenharmony_ci} 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cistatic int ethoc_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val) 6488c2ecf20Sopenharmony_ci{ 6498c2ecf20Sopenharmony_ci struct ethoc *priv = bus->priv; 6508c2ecf20Sopenharmony_ci int i; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci ethoc_write(priv, MIIADDRESS, MIIADDRESS_ADDR(phy, reg)); 6538c2ecf20Sopenharmony_ci ethoc_write(priv, MIITX_DATA, val); 6548c2ecf20Sopenharmony_ci ethoc_write(priv, MIICOMMAND, MIICOMMAND_WRITE); 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci for (i = 0; i < 5; i++) { 6578c2ecf20Sopenharmony_ci u32 stat = ethoc_read(priv, MIISTATUS); 6588c2ecf20Sopenharmony_ci if (!(stat & MIISTATUS_BUSY)) { 6598c2ecf20Sopenharmony_ci /* reset MII command register */ 6608c2ecf20Sopenharmony_ci ethoc_write(priv, MIICOMMAND, 0); 6618c2ecf20Sopenharmony_ci return 0; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci usleep_range(100, 200); 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci return -EBUSY; 6678c2ecf20Sopenharmony_ci} 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistatic void ethoc_mdio_poll(struct net_device *dev) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 6728c2ecf20Sopenharmony_ci struct phy_device *phydev = dev->phydev; 6738c2ecf20Sopenharmony_ci bool changed = false; 6748c2ecf20Sopenharmony_ci u32 mode; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (priv->old_link != phydev->link) { 6778c2ecf20Sopenharmony_ci changed = true; 6788c2ecf20Sopenharmony_ci priv->old_link = phydev->link; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci if (priv->old_duplex != phydev->duplex) { 6828c2ecf20Sopenharmony_ci changed = true; 6838c2ecf20Sopenharmony_ci priv->old_duplex = phydev->duplex; 6848c2ecf20Sopenharmony_ci } 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci if (!changed) 6878c2ecf20Sopenharmony_ci return; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci mode = ethoc_read(priv, MODER); 6908c2ecf20Sopenharmony_ci if (phydev->duplex == DUPLEX_FULL) 6918c2ecf20Sopenharmony_ci mode |= MODER_FULLD; 6928c2ecf20Sopenharmony_ci else 6938c2ecf20Sopenharmony_ci mode &= ~MODER_FULLD; 6948c2ecf20Sopenharmony_ci ethoc_write(priv, MODER, mode); 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci phy_print_status(phydev); 6978c2ecf20Sopenharmony_ci} 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_cistatic int ethoc_mdio_probe(struct net_device *dev) 7008c2ecf20Sopenharmony_ci{ 7018c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 7028c2ecf20Sopenharmony_ci struct phy_device *phy; 7038c2ecf20Sopenharmony_ci int err; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci if (priv->phy_id != -1) 7068c2ecf20Sopenharmony_ci phy = mdiobus_get_phy(priv->mdio, priv->phy_id); 7078c2ecf20Sopenharmony_ci else 7088c2ecf20Sopenharmony_ci phy = phy_find_first(priv->mdio); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (!phy) { 7118c2ecf20Sopenharmony_ci dev_err(&dev->dev, "no PHY found\n"); 7128c2ecf20Sopenharmony_ci return -ENXIO; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci priv->old_duplex = -1; 7168c2ecf20Sopenharmony_ci priv->old_link = -1; 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci err = phy_connect_direct(dev, phy, ethoc_mdio_poll, 7198c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_GMII); 7208c2ecf20Sopenharmony_ci if (err) { 7218c2ecf20Sopenharmony_ci dev_err(&dev->dev, "could not attach to PHY\n"); 7228c2ecf20Sopenharmony_ci return err; 7238c2ecf20Sopenharmony_ci } 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci phy_set_max_speed(phy, SPEED_100); 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci return 0; 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic int ethoc_open(struct net_device *dev) 7318c2ecf20Sopenharmony_ci{ 7328c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 7338c2ecf20Sopenharmony_ci int ret; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci ret = request_irq(dev->irq, ethoc_interrupt, IRQF_SHARED, 7368c2ecf20Sopenharmony_ci dev->name, dev); 7378c2ecf20Sopenharmony_ci if (ret) 7388c2ecf20Sopenharmony_ci return ret; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci napi_enable(&priv->napi); 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci ethoc_init_ring(priv, dev->mem_start); 7438c2ecf20Sopenharmony_ci ethoc_reset(priv); 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci if (netif_queue_stopped(dev)) { 7468c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, " resuming queue\n"); 7478c2ecf20Sopenharmony_ci netif_wake_queue(dev); 7488c2ecf20Sopenharmony_ci } else { 7498c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, " starting queue\n"); 7508c2ecf20Sopenharmony_ci netif_start_queue(dev); 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci priv->old_link = -1; 7548c2ecf20Sopenharmony_ci priv->old_duplex = -1; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci phy_start(dev->phydev); 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (netif_msg_ifup(priv)) { 7598c2ecf20Sopenharmony_ci dev_info(&dev->dev, "I/O: %08lx Memory: %08lx-%08lx\n", 7608c2ecf20Sopenharmony_ci dev->base_addr, dev->mem_start, dev->mem_end); 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return 0; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic int ethoc_stop(struct net_device *dev) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_ci napi_disable(&priv->napi); 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci if (dev->phydev) 7738c2ecf20Sopenharmony_ci phy_stop(dev->phydev); 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci ethoc_disable_rx_and_tx(priv); 7768c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci if (!netif_queue_stopped(dev)) 7798c2ecf20Sopenharmony_ci netif_stop_queue(dev); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return 0; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic int ethoc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 7878c2ecf20Sopenharmony_ci struct mii_ioctl_data *mdio = if_mii(ifr); 7888c2ecf20Sopenharmony_ci struct phy_device *phy = NULL; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (!netif_running(dev)) 7918c2ecf20Sopenharmony_ci return -EINVAL; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci if (cmd != SIOCGMIIPHY) { 7948c2ecf20Sopenharmony_ci if (mdio->phy_id >= PHY_MAX_ADDR) 7958c2ecf20Sopenharmony_ci return -ERANGE; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci phy = mdiobus_get_phy(priv->mdio, mdio->phy_id); 7988c2ecf20Sopenharmony_ci if (!phy) 7998c2ecf20Sopenharmony_ci return -ENODEV; 8008c2ecf20Sopenharmony_ci } else { 8018c2ecf20Sopenharmony_ci phy = dev->phydev; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci 8048c2ecf20Sopenharmony_ci return phy_mii_ioctl(phy, ifr, cmd); 8058c2ecf20Sopenharmony_ci} 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_cistatic void ethoc_do_set_mac_address(struct net_device *dev) 8088c2ecf20Sopenharmony_ci{ 8098c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 8108c2ecf20Sopenharmony_ci unsigned char *mac = dev->dev_addr; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci ethoc_write(priv, MAC_ADDR0, (mac[2] << 24) | (mac[3] << 16) | 8138c2ecf20Sopenharmony_ci (mac[4] << 8) | (mac[5] << 0)); 8148c2ecf20Sopenharmony_ci ethoc_write(priv, MAC_ADDR1, (mac[0] << 8) | (mac[1] << 0)); 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_cistatic int ethoc_set_mac_address(struct net_device *dev, void *p) 8188c2ecf20Sopenharmony_ci{ 8198c2ecf20Sopenharmony_ci const struct sockaddr *addr = p; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 8228c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 8238c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN); 8248c2ecf20Sopenharmony_ci ethoc_do_set_mac_address(dev); 8258c2ecf20Sopenharmony_ci return 0; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void ethoc_set_multicast_list(struct net_device *dev) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 8318c2ecf20Sopenharmony_ci u32 mode = ethoc_read(priv, MODER); 8328c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 8338c2ecf20Sopenharmony_ci u32 hash[2] = { 0, 0 }; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci /* set loopback mode if requested */ 8368c2ecf20Sopenharmony_ci if (dev->flags & IFF_LOOPBACK) 8378c2ecf20Sopenharmony_ci mode |= MODER_LOOP; 8388c2ecf20Sopenharmony_ci else 8398c2ecf20Sopenharmony_ci mode &= ~MODER_LOOP; 8408c2ecf20Sopenharmony_ci 8418c2ecf20Sopenharmony_ci /* receive broadcast frames if requested */ 8428c2ecf20Sopenharmony_ci if (dev->flags & IFF_BROADCAST) 8438c2ecf20Sopenharmony_ci mode &= ~MODER_BRO; 8448c2ecf20Sopenharmony_ci else 8458c2ecf20Sopenharmony_ci mode |= MODER_BRO; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci /* enable promiscuous mode if requested */ 8488c2ecf20Sopenharmony_ci if (dev->flags & IFF_PROMISC) 8498c2ecf20Sopenharmony_ci mode |= MODER_PRO; 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci mode &= ~MODER_PRO; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci ethoc_write(priv, MODER, mode); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci /* receive multicast frames */ 8568c2ecf20Sopenharmony_ci if (dev->flags & IFF_ALLMULTI) { 8578c2ecf20Sopenharmony_ci hash[0] = 0xffffffff; 8588c2ecf20Sopenharmony_ci hash[1] = 0xffffffff; 8598c2ecf20Sopenharmony_ci } else { 8608c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) { 8618c2ecf20Sopenharmony_ci u32 crc = ether_crc(ETH_ALEN, ha->addr); 8628c2ecf20Sopenharmony_ci int bit = (crc >> 26) & 0x3f; 8638c2ecf20Sopenharmony_ci hash[bit >> 5] |= 1 << (bit & 0x1f); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci } 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci ethoc_write(priv, ETH_HASH0, hash[0]); 8688c2ecf20Sopenharmony_ci ethoc_write(priv, ETH_HASH1, hash[1]); 8698c2ecf20Sopenharmony_ci} 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_cistatic int ethoc_change_mtu(struct net_device *dev, int new_mtu) 8728c2ecf20Sopenharmony_ci{ 8738c2ecf20Sopenharmony_ci return -ENOSYS; 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_cistatic void ethoc_tx_timeout(struct net_device *dev, unsigned int txqueue) 8778c2ecf20Sopenharmony_ci{ 8788c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 8798c2ecf20Sopenharmony_ci u32 pending = ethoc_read(priv, INT_SOURCE); 8808c2ecf20Sopenharmony_ci if (likely(pending)) 8818c2ecf20Sopenharmony_ci ethoc_interrupt(dev->irq, dev); 8828c2ecf20Sopenharmony_ci} 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic netdev_tx_t ethoc_start_xmit(struct sk_buff *skb, struct net_device *dev) 8858c2ecf20Sopenharmony_ci{ 8868c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 8878c2ecf20Sopenharmony_ci struct ethoc_bd bd; 8888c2ecf20Sopenharmony_ci unsigned int entry; 8898c2ecf20Sopenharmony_ci void *dest; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (skb_put_padto(skb, ETHOC_ZLEN)) { 8928c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8938c2ecf20Sopenharmony_ci goto out_no_free; 8948c2ecf20Sopenharmony_ci } 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci if (unlikely(skb->len > ETHOC_BUFSIZ)) { 8978c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 8988c2ecf20Sopenharmony_ci goto out; 8998c2ecf20Sopenharmony_ci } 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ci entry = priv->cur_tx % priv->num_tx; 9028c2ecf20Sopenharmony_ci spin_lock_irq(&priv->lock); 9038c2ecf20Sopenharmony_ci priv->cur_tx++; 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_ci ethoc_read_bd(priv, entry, &bd); 9068c2ecf20Sopenharmony_ci if (unlikely(skb->len < ETHOC_ZLEN)) 9078c2ecf20Sopenharmony_ci bd.stat |= TX_BD_PAD; 9088c2ecf20Sopenharmony_ci else 9098c2ecf20Sopenharmony_ci bd.stat &= ~TX_BD_PAD; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci dest = priv->vma[entry]; 9128c2ecf20Sopenharmony_ci memcpy_toio(dest, skb->data, skb->len); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci bd.stat &= ~(TX_BD_STATS | TX_BD_LEN_MASK); 9158c2ecf20Sopenharmony_ci bd.stat |= TX_BD_LEN(skb->len); 9168c2ecf20Sopenharmony_ci ethoc_write_bd(priv, entry, &bd); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci bd.stat |= TX_BD_READY; 9198c2ecf20Sopenharmony_ci ethoc_write_bd(priv, entry, &bd); 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (priv->cur_tx == (priv->dty_tx + priv->num_tx)) { 9228c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "stopping queue\n"); 9238c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9248c2ecf20Sopenharmony_ci } 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci spin_unlock_irq(&priv->lock); 9278c2ecf20Sopenharmony_ci skb_tx_timestamp(skb); 9288c2ecf20Sopenharmony_ciout: 9298c2ecf20Sopenharmony_ci dev_kfree_skb(skb); 9308c2ecf20Sopenharmony_ciout_no_free: 9318c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_cistatic int ethoc_get_regs_len(struct net_device *netdev) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci return ETH_END; 9378c2ecf20Sopenharmony_ci} 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_cistatic void ethoc_get_regs(struct net_device *dev, struct ethtool_regs *regs, 9408c2ecf20Sopenharmony_ci void *p) 9418c2ecf20Sopenharmony_ci{ 9428c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 9438c2ecf20Sopenharmony_ci u32 *regs_buff = p; 9448c2ecf20Sopenharmony_ci unsigned i; 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci regs->version = 0; 9478c2ecf20Sopenharmony_ci for (i = 0; i < ETH_END / sizeof(u32); ++i) 9488c2ecf20Sopenharmony_ci regs_buff[i] = ethoc_read(priv, i * sizeof(u32)); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic void ethoc_get_ringparam(struct net_device *dev, 9528c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 9538c2ecf20Sopenharmony_ci{ 9548c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci ring->rx_max_pending = priv->num_bd - 1; 9578c2ecf20Sopenharmony_ci ring->rx_mini_max_pending = 0; 9588c2ecf20Sopenharmony_ci ring->rx_jumbo_max_pending = 0; 9598c2ecf20Sopenharmony_ci ring->tx_max_pending = priv->num_bd - 1; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci ring->rx_pending = priv->num_rx; 9628c2ecf20Sopenharmony_ci ring->rx_mini_pending = 0; 9638c2ecf20Sopenharmony_ci ring->rx_jumbo_pending = 0; 9648c2ecf20Sopenharmony_ci ring->tx_pending = priv->num_tx; 9658c2ecf20Sopenharmony_ci} 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_cistatic int ethoc_set_ringparam(struct net_device *dev, 9688c2ecf20Sopenharmony_ci struct ethtool_ringparam *ring) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(dev); 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci if (ring->tx_pending < 1 || ring->rx_pending < 1 || 9738c2ecf20Sopenharmony_ci ring->tx_pending + ring->rx_pending > priv->num_bd) 9748c2ecf20Sopenharmony_ci return -EINVAL; 9758c2ecf20Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending) 9768c2ecf20Sopenharmony_ci return -EINVAL; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (netif_running(dev)) { 9798c2ecf20Sopenharmony_ci netif_tx_disable(dev); 9808c2ecf20Sopenharmony_ci ethoc_disable_rx_and_tx(priv); 9818c2ecf20Sopenharmony_ci ethoc_disable_irq(priv, INT_MASK_TX | INT_MASK_RX); 9828c2ecf20Sopenharmony_ci synchronize_irq(dev->irq); 9838c2ecf20Sopenharmony_ci } 9848c2ecf20Sopenharmony_ci 9858c2ecf20Sopenharmony_ci priv->num_tx = rounddown_pow_of_two(ring->tx_pending); 9868c2ecf20Sopenharmony_ci priv->num_rx = ring->rx_pending; 9878c2ecf20Sopenharmony_ci ethoc_init_ring(priv, dev->mem_start); 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (netif_running(dev)) { 9908c2ecf20Sopenharmony_ci ethoc_enable_irq(priv, INT_MASK_TX | INT_MASK_RX); 9918c2ecf20Sopenharmony_ci ethoc_enable_rx_and_tx(priv); 9928c2ecf20Sopenharmony_ci netif_wake_queue(dev); 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci return 0; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic const struct ethtool_ops ethoc_ethtool_ops = { 9988c2ecf20Sopenharmony_ci .get_regs_len = ethoc_get_regs_len, 9998c2ecf20Sopenharmony_ci .get_regs = ethoc_get_regs, 10008c2ecf20Sopenharmony_ci .nway_reset = phy_ethtool_nway_reset, 10018c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 10028c2ecf20Sopenharmony_ci .get_ringparam = ethoc_get_ringparam, 10038c2ecf20Sopenharmony_ci .set_ringparam = ethoc_set_ringparam, 10048c2ecf20Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 10058c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 10068c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 10078c2ecf20Sopenharmony_ci}; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_cistatic const struct net_device_ops ethoc_netdev_ops = { 10108c2ecf20Sopenharmony_ci .ndo_open = ethoc_open, 10118c2ecf20Sopenharmony_ci .ndo_stop = ethoc_stop, 10128c2ecf20Sopenharmony_ci .ndo_do_ioctl = ethoc_ioctl, 10138c2ecf20Sopenharmony_ci .ndo_set_mac_address = ethoc_set_mac_address, 10148c2ecf20Sopenharmony_ci .ndo_set_rx_mode = ethoc_set_multicast_list, 10158c2ecf20Sopenharmony_ci .ndo_change_mtu = ethoc_change_mtu, 10168c2ecf20Sopenharmony_ci .ndo_tx_timeout = ethoc_tx_timeout, 10178c2ecf20Sopenharmony_ci .ndo_start_xmit = ethoc_start_xmit, 10188c2ecf20Sopenharmony_ci}; 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci/** 10218c2ecf20Sopenharmony_ci * ethoc_probe - initialize OpenCores ethernet MAC 10228c2ecf20Sopenharmony_ci * @pdev: platform device 10238c2ecf20Sopenharmony_ci */ 10248c2ecf20Sopenharmony_cistatic int ethoc_probe(struct platform_device *pdev) 10258c2ecf20Sopenharmony_ci{ 10268c2ecf20Sopenharmony_ci struct net_device *netdev = NULL; 10278c2ecf20Sopenharmony_ci struct resource *res = NULL; 10288c2ecf20Sopenharmony_ci struct resource *mmio = NULL; 10298c2ecf20Sopenharmony_ci struct resource *mem = NULL; 10308c2ecf20Sopenharmony_ci struct ethoc *priv = NULL; 10318c2ecf20Sopenharmony_ci int num_bd; 10328c2ecf20Sopenharmony_ci int ret = 0; 10338c2ecf20Sopenharmony_ci struct ethoc_platform_data *pdata = dev_get_platdata(&pdev->dev); 10348c2ecf20Sopenharmony_ci u32 eth_clkfreq = pdata ? pdata->eth_clkfreq : 0; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci /* allocate networking device */ 10378c2ecf20Sopenharmony_ci netdev = alloc_etherdev(sizeof(struct ethoc)); 10388c2ecf20Sopenharmony_ci if (!netdev) { 10398c2ecf20Sopenharmony_ci ret = -ENOMEM; 10408c2ecf20Sopenharmony_ci goto out; 10418c2ecf20Sopenharmony_ci } 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 10448c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, netdev); 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* obtain I/O memory space */ 10478c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 10488c2ecf20Sopenharmony_ci if (!res) { 10498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot obtain I/O memory space\n"); 10508c2ecf20Sopenharmony_ci ret = -ENXIO; 10518c2ecf20Sopenharmony_ci goto free; 10528c2ecf20Sopenharmony_ci } 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci mmio = devm_request_mem_region(&pdev->dev, res->start, 10558c2ecf20Sopenharmony_ci resource_size(res), res->name); 10568c2ecf20Sopenharmony_ci if (!mmio) { 10578c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot request I/O memory space\n"); 10588c2ecf20Sopenharmony_ci ret = -ENXIO; 10598c2ecf20Sopenharmony_ci goto free; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci netdev->base_addr = mmio->start; 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci /* obtain buffer memory space */ 10658c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_MEM, 1); 10668c2ecf20Sopenharmony_ci if (res) { 10678c2ecf20Sopenharmony_ci mem = devm_request_mem_region(&pdev->dev, res->start, 10688c2ecf20Sopenharmony_ci resource_size(res), res->name); 10698c2ecf20Sopenharmony_ci if (!mem) { 10708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot request memory space\n"); 10718c2ecf20Sopenharmony_ci ret = -ENXIO; 10728c2ecf20Sopenharmony_ci goto free; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci netdev->mem_start = mem->start; 10768c2ecf20Sopenharmony_ci netdev->mem_end = mem->end; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci /* obtain device IRQ number */ 10818c2ecf20Sopenharmony_ci res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); 10828c2ecf20Sopenharmony_ci if (!res) { 10838c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot obtain IRQ\n"); 10848c2ecf20Sopenharmony_ci ret = -ENXIO; 10858c2ecf20Sopenharmony_ci goto free; 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci netdev->irq = res->start; 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci /* setup driver-private data */ 10918c2ecf20Sopenharmony_ci priv = netdev_priv(netdev); 10928c2ecf20Sopenharmony_ci priv->netdev = netdev; 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci priv->iobase = devm_ioremap(&pdev->dev, netdev->base_addr, 10958c2ecf20Sopenharmony_ci resource_size(mmio)); 10968c2ecf20Sopenharmony_ci if (!priv->iobase) { 10978c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot remap I/O memory space\n"); 10988c2ecf20Sopenharmony_ci ret = -ENXIO; 10998c2ecf20Sopenharmony_ci goto free; 11008c2ecf20Sopenharmony_ci } 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci if (netdev->mem_end) { 11038c2ecf20Sopenharmony_ci priv->membase = devm_ioremap(&pdev->dev, 11048c2ecf20Sopenharmony_ci netdev->mem_start, resource_size(mem)); 11058c2ecf20Sopenharmony_ci if (!priv->membase) { 11068c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot remap memory space\n"); 11078c2ecf20Sopenharmony_ci ret = -ENXIO; 11088c2ecf20Sopenharmony_ci goto free; 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci } else { 11118c2ecf20Sopenharmony_ci /* Allocate buffer memory */ 11128c2ecf20Sopenharmony_ci priv->membase = dmam_alloc_coherent(&pdev->dev, 11138c2ecf20Sopenharmony_ci buffer_size, (void *)&netdev->mem_start, 11148c2ecf20Sopenharmony_ci GFP_KERNEL); 11158c2ecf20Sopenharmony_ci if (!priv->membase) { 11168c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot allocate %dB buffer\n", 11178c2ecf20Sopenharmony_ci buffer_size); 11188c2ecf20Sopenharmony_ci ret = -ENOMEM; 11198c2ecf20Sopenharmony_ci goto free; 11208c2ecf20Sopenharmony_ci } 11218c2ecf20Sopenharmony_ci netdev->mem_end = netdev->mem_start + buffer_size; 11228c2ecf20Sopenharmony_ci } 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci priv->big_endian = pdata ? pdata->big_endian : 11258c2ecf20Sopenharmony_ci of_device_is_big_endian(pdev->dev.of_node); 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* calculate the number of TX/RX buffers, maximum 128 supported */ 11288c2ecf20Sopenharmony_ci num_bd = min_t(unsigned int, 11298c2ecf20Sopenharmony_ci 128, (netdev->mem_end - netdev->mem_start + 1) / ETHOC_BUFSIZ); 11308c2ecf20Sopenharmony_ci if (num_bd < 4) { 11318c2ecf20Sopenharmony_ci ret = -ENODEV; 11328c2ecf20Sopenharmony_ci goto free; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci priv->num_bd = num_bd; 11358c2ecf20Sopenharmony_ci /* num_tx must be a power of two */ 11368c2ecf20Sopenharmony_ci priv->num_tx = rounddown_pow_of_two(num_bd >> 1); 11378c2ecf20Sopenharmony_ci priv->num_rx = num_bd - priv->num_tx; 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "ethoc: num_tx: %d num_rx: %d\n", 11408c2ecf20Sopenharmony_ci priv->num_tx, priv->num_rx); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci priv->vma = devm_kcalloc(&pdev->dev, num_bd, sizeof(void *), 11438c2ecf20Sopenharmony_ci GFP_KERNEL); 11448c2ecf20Sopenharmony_ci if (!priv->vma) { 11458c2ecf20Sopenharmony_ci ret = -ENOMEM; 11468c2ecf20Sopenharmony_ci goto free; 11478c2ecf20Sopenharmony_ci } 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci /* Allow the platform setup code to pass in a MAC address. */ 11508c2ecf20Sopenharmony_ci if (pdata) { 11518c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, pdata->hwaddr); 11528c2ecf20Sopenharmony_ci priv->phy_id = pdata->phy_id; 11538c2ecf20Sopenharmony_ci } else { 11548c2ecf20Sopenharmony_ci const void *mac; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci mac = of_get_mac_address(pdev->dev.of_node); 11578c2ecf20Sopenharmony_ci if (!IS_ERR(mac)) 11588c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, mac); 11598c2ecf20Sopenharmony_ci priv->phy_id = -1; 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Check that the given MAC address is valid. If it isn't, read the 11638c2ecf20Sopenharmony_ci * current MAC from the controller. 11648c2ecf20Sopenharmony_ci */ 11658c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) 11668c2ecf20Sopenharmony_ci ethoc_get_mac_address(netdev, netdev->dev_addr); 11678c2ecf20Sopenharmony_ci 11688c2ecf20Sopenharmony_ci /* Check the MAC again for validity, if it still isn't choose and 11698c2ecf20Sopenharmony_ci * program a random one. 11708c2ecf20Sopenharmony_ci */ 11718c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(netdev->dev_addr)) 11728c2ecf20Sopenharmony_ci eth_hw_addr_random(netdev); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci ethoc_do_set_mac_address(netdev); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* Allow the platform setup code to adjust MII management bus clock. */ 11778c2ecf20Sopenharmony_ci if (!eth_clkfreq) { 11788c2ecf20Sopenharmony_ci struct clk *clk = devm_clk_get(&pdev->dev, NULL); 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci if (!IS_ERR(clk)) { 11818c2ecf20Sopenharmony_ci priv->clk = clk; 11828c2ecf20Sopenharmony_ci clk_prepare_enable(clk); 11838c2ecf20Sopenharmony_ci eth_clkfreq = clk_get_rate(clk); 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci if (eth_clkfreq) { 11878c2ecf20Sopenharmony_ci u32 clkdiv = MIIMODER_CLKDIV(eth_clkfreq / 2500000 + 1); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (!clkdiv) 11908c2ecf20Sopenharmony_ci clkdiv = 2; 11918c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "setting MII clkdiv to %u\n", clkdiv); 11928c2ecf20Sopenharmony_ci ethoc_write(priv, MIIMODER, 11938c2ecf20Sopenharmony_ci (ethoc_read(priv, MIIMODER) & MIIMODER_NOPRE) | 11948c2ecf20Sopenharmony_ci clkdiv); 11958c2ecf20Sopenharmony_ci } 11968c2ecf20Sopenharmony_ci 11978c2ecf20Sopenharmony_ci /* register MII bus */ 11988c2ecf20Sopenharmony_ci priv->mdio = mdiobus_alloc(); 11998c2ecf20Sopenharmony_ci if (!priv->mdio) { 12008c2ecf20Sopenharmony_ci ret = -ENOMEM; 12018c2ecf20Sopenharmony_ci goto free2; 12028c2ecf20Sopenharmony_ci } 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci priv->mdio->name = "ethoc-mdio"; 12058c2ecf20Sopenharmony_ci snprintf(priv->mdio->id, MII_BUS_ID_SIZE, "%s-%d", 12068c2ecf20Sopenharmony_ci priv->mdio->name, pdev->id); 12078c2ecf20Sopenharmony_ci priv->mdio->read = ethoc_mdio_read; 12088c2ecf20Sopenharmony_ci priv->mdio->write = ethoc_mdio_write; 12098c2ecf20Sopenharmony_ci priv->mdio->priv = priv; 12108c2ecf20Sopenharmony_ci 12118c2ecf20Sopenharmony_ci ret = mdiobus_register(priv->mdio); 12128c2ecf20Sopenharmony_ci if (ret) { 12138c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "failed to register MDIO bus\n"); 12148c2ecf20Sopenharmony_ci goto free3; 12158c2ecf20Sopenharmony_ci } 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_ci ret = ethoc_mdio_probe(netdev); 12188c2ecf20Sopenharmony_ci if (ret) { 12198c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "failed to probe MDIO bus\n"); 12208c2ecf20Sopenharmony_ci goto error; 12218c2ecf20Sopenharmony_ci } 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* setup the net_device structure */ 12248c2ecf20Sopenharmony_ci netdev->netdev_ops = ðoc_netdev_ops; 12258c2ecf20Sopenharmony_ci netdev->watchdog_timeo = ETHOC_TIMEOUT; 12268c2ecf20Sopenharmony_ci netdev->features |= 0; 12278c2ecf20Sopenharmony_ci netdev->ethtool_ops = ðoc_ethtool_ops; 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci /* setup NAPI */ 12308c2ecf20Sopenharmony_ci netif_napi_add(netdev, &priv->napi, ethoc_poll, 64); 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci spin_lock_init(&priv->lock); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci ret = register_netdev(netdev); 12358c2ecf20Sopenharmony_ci if (ret < 0) { 12368c2ecf20Sopenharmony_ci dev_err(&netdev->dev, "failed to register interface\n"); 12378c2ecf20Sopenharmony_ci goto error2; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci goto out; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_cierror2: 12438c2ecf20Sopenharmony_ci netif_napi_del(&priv->napi); 12448c2ecf20Sopenharmony_cierror: 12458c2ecf20Sopenharmony_ci mdiobus_unregister(priv->mdio); 12468c2ecf20Sopenharmony_cifree3: 12478c2ecf20Sopenharmony_ci mdiobus_free(priv->mdio); 12488c2ecf20Sopenharmony_cifree2: 12498c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 12508c2ecf20Sopenharmony_cifree: 12518c2ecf20Sopenharmony_ci free_netdev(netdev); 12528c2ecf20Sopenharmony_ciout: 12538c2ecf20Sopenharmony_ci return ret; 12548c2ecf20Sopenharmony_ci} 12558c2ecf20Sopenharmony_ci 12568c2ecf20Sopenharmony_ci/** 12578c2ecf20Sopenharmony_ci * ethoc_remove - shutdown OpenCores ethernet MAC 12588c2ecf20Sopenharmony_ci * @pdev: platform device 12598c2ecf20Sopenharmony_ci */ 12608c2ecf20Sopenharmony_cistatic int ethoc_remove(struct platform_device *pdev) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct net_device *netdev = platform_get_drvdata(pdev); 12638c2ecf20Sopenharmony_ci struct ethoc *priv = netdev_priv(netdev); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci if (netdev) { 12668c2ecf20Sopenharmony_ci netif_napi_del(&priv->napi); 12678c2ecf20Sopenharmony_ci phy_disconnect(netdev->phydev); 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci if (priv->mdio) { 12708c2ecf20Sopenharmony_ci mdiobus_unregister(priv->mdio); 12718c2ecf20Sopenharmony_ci mdiobus_free(priv->mdio); 12728c2ecf20Sopenharmony_ci } 12738c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 12748c2ecf20Sopenharmony_ci unregister_netdev(netdev); 12758c2ecf20Sopenharmony_ci free_netdev(netdev); 12768c2ecf20Sopenharmony_ci } 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_ci return 0; 12798c2ecf20Sopenharmony_ci} 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 12828c2ecf20Sopenharmony_cistatic int ethoc_suspend(struct platform_device *pdev, pm_message_t state) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci return -ENOSYS; 12858c2ecf20Sopenharmony_ci} 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_cistatic int ethoc_resume(struct platform_device *pdev) 12888c2ecf20Sopenharmony_ci{ 12898c2ecf20Sopenharmony_ci return -ENOSYS; 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ci#else 12928c2ecf20Sopenharmony_ci# define ethoc_suspend NULL 12938c2ecf20Sopenharmony_ci# define ethoc_resume NULL 12948c2ecf20Sopenharmony_ci#endif 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic const struct of_device_id ethoc_match[] = { 12978c2ecf20Sopenharmony_ci { .compatible = "opencores,ethoc", }, 12988c2ecf20Sopenharmony_ci {}, 12998c2ecf20Sopenharmony_ci}; 13008c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ethoc_match); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_cistatic struct platform_driver ethoc_driver = { 13038c2ecf20Sopenharmony_ci .probe = ethoc_probe, 13048c2ecf20Sopenharmony_ci .remove = ethoc_remove, 13058c2ecf20Sopenharmony_ci .suspend = ethoc_suspend, 13068c2ecf20Sopenharmony_ci .resume = ethoc_resume, 13078c2ecf20Sopenharmony_ci .driver = { 13088c2ecf20Sopenharmony_ci .name = "ethoc", 13098c2ecf20Sopenharmony_ci .of_match_table = ethoc_match, 13108c2ecf20Sopenharmony_ci }, 13118c2ecf20Sopenharmony_ci}; 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_cimodule_platform_driver(ethoc_driver); 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thierry Reding <thierry.reding@avionic-design.de>"); 13168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OpenCores Ethernet MAC driver"); 13178c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 13188c2ecf20Sopenharmony_ci 1319