18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This is a new flat driver which is based on the original emac_lite 68c2ecf20Sopenharmony_ci * driver from John Williams <john.williams@xilinx.com>. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * 2007 - 2013 (c) Xilinx, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 138c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 148c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 158c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 168c2ecf20Sopenharmony_ci#include <linux/ethtool.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/of_address.h> 208c2ecf20Sopenharmony_ci#include <linux/of_device.h> 218c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 228c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 238c2ecf20Sopenharmony_ci#include <linux/of_net.h> 248c2ecf20Sopenharmony_ci#include <linux/phy.h> 258c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 268c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci#define DRIVER_NAME "xilinx_emaclite" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* Register offsets for the EmacLite Core */ 318c2ecf20Sopenharmony_ci#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ 328c2ecf20Sopenharmony_ci#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ 338c2ecf20Sopenharmony_ci#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ 348c2ecf20Sopenharmony_ci#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ 358c2ecf20Sopenharmony_ci#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ 368c2ecf20Sopenharmony_ci#define XEL_GIER_OFFSET 0x07F8 /* GIE Register */ 378c2ecf20Sopenharmony_ci#define XEL_TSR_OFFSET 0x07FC /* Tx status */ 388c2ecf20Sopenharmony_ci#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ 418c2ecf20Sopenharmony_ci#define XEL_RPLR_OFFSET 0x100C /* Rx packet length */ 428c2ecf20Sopenharmony_ci#define XEL_RSR_OFFSET 0x17FC /* Rx status */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/* MDIO Address Register Bit Masks */ 478c2ecf20Sopenharmony_ci#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ 488c2ecf20Sopenharmony_ci#define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ 498c2ecf20Sopenharmony_ci#define XEL_MDIOADDR_PHYADR_SHIFT 5 508c2ecf20Sopenharmony_ci#define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* MDIO Write Data Register Bit Masks */ 538c2ecf20Sopenharmony_ci#define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* MDIO Read Data Register Bit Masks */ 568c2ecf20Sopenharmony_ci#define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* MDIO Control Register Bit Masks */ 598c2ecf20Sopenharmony_ci#define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ 608c2ecf20Sopenharmony_ci#define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* Global Interrupt Enable Register (GIER) Bit Masks */ 638c2ecf20Sopenharmony_ci#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Transmit Status Register (TSR) Bit Masks */ 668c2ecf20Sopenharmony_ci#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ 678c2ecf20Sopenharmony_ci#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ 688c2ecf20Sopenharmony_ci#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ 698c2ecf20Sopenharmony_ci#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit 708c2ecf20Sopenharmony_ci * only. This is not documented 718c2ecf20Sopenharmony_ci * in the HW spec 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* Define for programming the MAC address into the EmacLite */ 758c2ecf20Sopenharmony_ci#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* Receive Status Register (RSR) */ 788c2ecf20Sopenharmony_ci#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ 798c2ecf20Sopenharmony_ci#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/* Transmit Packet Length Register (TPLR) */ 828c2ecf20Sopenharmony_ci#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* Receive Packet Length Register (RPLR) */ 858c2ecf20Sopenharmony_ci#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci#define XEL_HEADER_OFFSET 12 /* Offset to length field */ 888c2ecf20Sopenharmony_ci#define XEL_HEADER_SHIFT 16 /* Shift value for length */ 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci/* General Ethernet Definitions */ 918c2ecf20Sopenharmony_ci#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ 928c2ecf20Sopenharmony_ci#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci#define TX_TIMEOUT (60 * HZ) /* Tx timeout is 60 seconds. */ 978c2ecf20Sopenharmony_ci#define ALIGNMENT 4 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci/* BUFFER_ALIGN(adr) calculates the number of bytes to the next alignment. */ 1008c2ecf20Sopenharmony_ci#define BUFFER_ALIGN(adr) ((ALIGNMENT - ((u32)adr)) % ALIGNMENT) 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN 1038c2ecf20Sopenharmony_ci#define xemaclite_readl ioread32be 1048c2ecf20Sopenharmony_ci#define xemaclite_writel iowrite32be 1058c2ecf20Sopenharmony_ci#else 1068c2ecf20Sopenharmony_ci#define xemaclite_readl ioread32 1078c2ecf20Sopenharmony_ci#define xemaclite_writel iowrite32 1088c2ecf20Sopenharmony_ci#endif 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci/** 1118c2ecf20Sopenharmony_ci * struct net_local - Our private per device data 1128c2ecf20Sopenharmony_ci * @ndev: instance of the network device 1138c2ecf20Sopenharmony_ci * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW 1148c2ecf20Sopenharmony_ci * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW 1158c2ecf20Sopenharmony_ci * @next_tx_buf_to_use: next Tx buffer to write to 1168c2ecf20Sopenharmony_ci * @next_rx_buf_to_use: next Rx buffer to read from 1178c2ecf20Sopenharmony_ci * @base_addr: base address of the Emaclite device 1188c2ecf20Sopenharmony_ci * @reset_lock: lock used for synchronization 1198c2ecf20Sopenharmony_ci * @deferred_skb: holds an skb (for transmission at a later time) when the 1208c2ecf20Sopenharmony_ci * Tx buffer is not free 1218c2ecf20Sopenharmony_ci * @phy_dev: pointer to the PHY device 1228c2ecf20Sopenharmony_ci * @phy_node: pointer to the PHY device node 1238c2ecf20Sopenharmony_ci * @mii_bus: pointer to the MII bus 1248c2ecf20Sopenharmony_ci * @last_link: last link status 1258c2ecf20Sopenharmony_ci */ 1268c2ecf20Sopenharmony_cistruct net_local { 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci struct net_device *ndev; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci bool tx_ping_pong; 1318c2ecf20Sopenharmony_ci bool rx_ping_pong; 1328c2ecf20Sopenharmony_ci u32 next_tx_buf_to_use; 1338c2ecf20Sopenharmony_ci u32 next_rx_buf_to_use; 1348c2ecf20Sopenharmony_ci void __iomem *base_addr; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci spinlock_t reset_lock; 1378c2ecf20Sopenharmony_ci struct sk_buff *deferred_skb; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci struct phy_device *phy_dev; 1408c2ecf20Sopenharmony_ci struct device_node *phy_node; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci struct mii_bus *mii_bus; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci int last_link; 1458c2ecf20Sopenharmony_ci}; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/*************************/ 1498c2ecf20Sopenharmony_ci/* EmacLite driver calls */ 1508c2ecf20Sopenharmony_ci/*************************/ 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci/** 1538c2ecf20Sopenharmony_ci * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device 1548c2ecf20Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 1558c2ecf20Sopenharmony_ci * 1568c2ecf20Sopenharmony_ci * This function enables the Tx and Rx interrupts for the Emaclite device along 1578c2ecf20Sopenharmony_ci * with the Global Interrupt Enable. 1588c2ecf20Sopenharmony_ci */ 1598c2ecf20Sopenharmony_cistatic void xemaclite_enable_interrupts(struct net_local *drvdata) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci u32 reg_data; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci /* Enable the Tx interrupts for the first Buffer */ 1648c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); 1658c2ecf20Sopenharmony_ci xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK, 1668c2ecf20Sopenharmony_ci drvdata->base_addr + XEL_TSR_OFFSET); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Enable the Rx interrupts for the first buffer */ 1698c2ecf20Sopenharmony_ci xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Enable the Global Interrupt Enable */ 1728c2ecf20Sopenharmony_ci xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci/** 1768c2ecf20Sopenharmony_ci * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device 1778c2ecf20Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 1788c2ecf20Sopenharmony_ci * 1798c2ecf20Sopenharmony_ci * This function disables the Tx and Rx interrupts for the Emaclite device, 1808c2ecf20Sopenharmony_ci * along with the Global Interrupt Enable. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_cistatic void xemaclite_disable_interrupts(struct net_local *drvdata) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci u32 reg_data; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Disable the Global Interrupt Enable */ 1878c2ecf20Sopenharmony_ci xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* Disable the Tx interrupts for the first buffer */ 1908c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); 1918c2ecf20Sopenharmony_ci xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), 1928c2ecf20Sopenharmony_ci drvdata->base_addr + XEL_TSR_OFFSET); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci /* Disable the Rx interrupts for the first buffer */ 1958c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET); 1968c2ecf20Sopenharmony_ci xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), 1978c2ecf20Sopenharmony_ci drvdata->base_addr + XEL_RSR_OFFSET); 1988c2ecf20Sopenharmony_ci} 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci/** 2018c2ecf20Sopenharmony_ci * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address 2028c2ecf20Sopenharmony_ci * @src_ptr: Void pointer to the 16-bit aligned source address 2038c2ecf20Sopenharmony_ci * @dest_ptr: Pointer to the 32-bit aligned destination address 2048c2ecf20Sopenharmony_ci * @length: Number bytes to write from source to destination 2058c2ecf20Sopenharmony_ci * 2068c2ecf20Sopenharmony_ci * This function writes data from a 16-bit aligned buffer to a 32-bit aligned 2078c2ecf20Sopenharmony_ci * address in the EmacLite device. 2088c2ecf20Sopenharmony_ci */ 2098c2ecf20Sopenharmony_cistatic void xemaclite_aligned_write(void *src_ptr, u32 *dest_ptr, 2108c2ecf20Sopenharmony_ci unsigned length) 2118c2ecf20Sopenharmony_ci{ 2128c2ecf20Sopenharmony_ci u32 align_buffer; 2138c2ecf20Sopenharmony_ci u32 *to_u32_ptr; 2148c2ecf20Sopenharmony_ci u16 *from_u16_ptr, *to_u16_ptr; 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci to_u32_ptr = dest_ptr; 2178c2ecf20Sopenharmony_ci from_u16_ptr = src_ptr; 2188c2ecf20Sopenharmony_ci align_buffer = 0; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci for (; length > 3; length -= 4) { 2218c2ecf20Sopenharmony_ci to_u16_ptr = (u16 *)&align_buffer; 2228c2ecf20Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 2238c2ecf20Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* This barrier resolves occasional issues seen around 2268c2ecf20Sopenharmony_ci * cases where the data is not properly flushed out 2278c2ecf20Sopenharmony_ci * from the processor store buffers to the destination 2288c2ecf20Sopenharmony_ci * memory locations. 2298c2ecf20Sopenharmony_ci */ 2308c2ecf20Sopenharmony_ci wmb(); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci /* Output a word */ 2338c2ecf20Sopenharmony_ci *to_u32_ptr++ = align_buffer; 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci if (length) { 2368c2ecf20Sopenharmony_ci u8 *from_u8_ptr, *to_u8_ptr; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci /* Set up to output the remaining data */ 2398c2ecf20Sopenharmony_ci align_buffer = 0; 2408c2ecf20Sopenharmony_ci to_u8_ptr = (u8 *)&align_buffer; 2418c2ecf20Sopenharmony_ci from_u8_ptr = (u8 *)from_u16_ptr; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci /* Output the remaining data */ 2448c2ecf20Sopenharmony_ci for (; length > 0; length--) 2458c2ecf20Sopenharmony_ci *to_u8_ptr++ = *from_u8_ptr++; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci /* This barrier resolves occasional issues seen around 2488c2ecf20Sopenharmony_ci * cases where the data is not properly flushed out 2498c2ecf20Sopenharmony_ci * from the processor store buffers to the destination 2508c2ecf20Sopenharmony_ci * memory locations. 2518c2ecf20Sopenharmony_ci */ 2528c2ecf20Sopenharmony_ci wmb(); 2538c2ecf20Sopenharmony_ci *to_u32_ptr = align_buffer; 2548c2ecf20Sopenharmony_ci } 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci/** 2588c2ecf20Sopenharmony_ci * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer 2598c2ecf20Sopenharmony_ci * @src_ptr: Pointer to the 32-bit aligned source address 2608c2ecf20Sopenharmony_ci * @dest_ptr: Pointer to the 16-bit aligned destination address 2618c2ecf20Sopenharmony_ci * @length: Number bytes to read from source to destination 2628c2ecf20Sopenharmony_ci * 2638c2ecf20Sopenharmony_ci * This function reads data from a 32-bit aligned address in the EmacLite device 2648c2ecf20Sopenharmony_ci * to a 16-bit aligned buffer. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_cistatic void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, 2678c2ecf20Sopenharmony_ci unsigned length) 2688c2ecf20Sopenharmony_ci{ 2698c2ecf20Sopenharmony_ci u16 *to_u16_ptr, *from_u16_ptr; 2708c2ecf20Sopenharmony_ci u32 *from_u32_ptr; 2718c2ecf20Sopenharmony_ci u32 align_buffer; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci from_u32_ptr = src_ptr; 2748c2ecf20Sopenharmony_ci to_u16_ptr = (u16 *)dest_ptr; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci for (; length > 3; length -= 4) { 2778c2ecf20Sopenharmony_ci /* Copy each word into the temporary buffer */ 2788c2ecf20Sopenharmony_ci align_buffer = *from_u32_ptr++; 2798c2ecf20Sopenharmony_ci from_u16_ptr = (u16 *)&align_buffer; 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* Read data from source */ 2828c2ecf20Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 2838c2ecf20Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci if (length) { 2878c2ecf20Sopenharmony_ci u8 *to_u8_ptr, *from_u8_ptr; 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci /* Set up to read the remaining data */ 2908c2ecf20Sopenharmony_ci to_u8_ptr = (u8 *)to_u16_ptr; 2918c2ecf20Sopenharmony_ci align_buffer = *from_u32_ptr++; 2928c2ecf20Sopenharmony_ci from_u8_ptr = (u8 *)&align_buffer; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Read the remaining data */ 2958c2ecf20Sopenharmony_ci for (; length > 0; length--) 2968c2ecf20Sopenharmony_ci *to_u8_ptr = *from_u8_ptr; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci/** 3018c2ecf20Sopenharmony_ci * xemaclite_send_data - Send an Ethernet frame 3028c2ecf20Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 3038c2ecf20Sopenharmony_ci * @data: Pointer to the data to be sent 3048c2ecf20Sopenharmony_ci * @byte_count: Total frame size, including header 3058c2ecf20Sopenharmony_ci * 3068c2ecf20Sopenharmony_ci * This function checks if the Tx buffer of the Emaclite device is free to send 3078c2ecf20Sopenharmony_ci * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it 3088c2ecf20Sopenharmony_ci * returns an error. 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Return: 0 upon success or -1 if the buffer(s) are full. 3118c2ecf20Sopenharmony_ci * 3128c2ecf20Sopenharmony_ci * Note: The maximum Tx packet size can not be more than Ethernet header 3138c2ecf20Sopenharmony_ci * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_cistatic int xemaclite_send_data(struct net_local *drvdata, u8 *data, 3168c2ecf20Sopenharmony_ci unsigned int byte_count) 3178c2ecf20Sopenharmony_ci{ 3188c2ecf20Sopenharmony_ci u32 reg_data; 3198c2ecf20Sopenharmony_ci void __iomem *addr; 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci /* Determine the expected Tx buffer address */ 3228c2ecf20Sopenharmony_ci addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* If the length is too large, truncate it */ 3258c2ecf20Sopenharmony_ci if (byte_count > ETH_FRAME_LEN) 3268c2ecf20Sopenharmony_ci byte_count = ETH_FRAME_LEN; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci /* Check if the expected buffer is available */ 3298c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 3308c2ecf20Sopenharmony_ci if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | 3318c2ecf20Sopenharmony_ci XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci /* Switch to next buffer if configured */ 3348c2ecf20Sopenharmony_ci if (drvdata->tx_ping_pong != 0) 3358c2ecf20Sopenharmony_ci drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET; 3368c2ecf20Sopenharmony_ci } else if (drvdata->tx_ping_pong != 0) { 3378c2ecf20Sopenharmony_ci /* If the expected buffer is full, try the other buffer, 3388c2ecf20Sopenharmony_ci * if it is configured in HW 3398c2ecf20Sopenharmony_ci */ 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci addr = (void __iomem __force *)((u32 __force)addr ^ 3428c2ecf20Sopenharmony_ci XEL_BUFFER_OFFSET); 3438c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | 3468c2ecf20Sopenharmony_ci XEL_TSR_XMIT_ACTIVE_MASK)) != 0) 3478c2ecf20Sopenharmony_ci return -1; /* Buffers were full, return failure */ 3488c2ecf20Sopenharmony_ci } else 3498c2ecf20Sopenharmony_ci return -1; /* Buffer was full, return failure */ 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci /* Write the frame to the buffer */ 3528c2ecf20Sopenharmony_ci xemaclite_aligned_write(data, (u32 __force *)addr, byte_count); 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK), 3558c2ecf20Sopenharmony_ci addr + XEL_TPLR_OFFSET); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Update the Tx Status Register to indicate that there is a 3588c2ecf20Sopenharmony_ci * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which 3598c2ecf20Sopenharmony_ci * is used by the interrupt handler to check whether a frame 3608c2ecf20Sopenharmony_ci * has been transmitted 3618c2ecf20Sopenharmony_ci */ 3628c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 3638c2ecf20Sopenharmony_ci reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); 3648c2ecf20Sopenharmony_ci xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/** 3708c2ecf20Sopenharmony_ci * xemaclite_recv_data - Receive a frame 3718c2ecf20Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 3728c2ecf20Sopenharmony_ci * @data: Address where the data is to be received 3738c2ecf20Sopenharmony_ci * @maxlen: Maximum supported ethernet packet length 3748c2ecf20Sopenharmony_ci * 3758c2ecf20Sopenharmony_ci * This function is intended to be called from the interrupt context or 3768c2ecf20Sopenharmony_ci * with a wrapper which waits for the receive frame to be available. 3778c2ecf20Sopenharmony_ci * 3788c2ecf20Sopenharmony_ci * Return: Total number of bytes received 3798c2ecf20Sopenharmony_ci */ 3808c2ecf20Sopenharmony_cistatic u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen) 3818c2ecf20Sopenharmony_ci{ 3828c2ecf20Sopenharmony_ci void __iomem *addr; 3838c2ecf20Sopenharmony_ci u16 length, proto_type; 3848c2ecf20Sopenharmony_ci u32 reg_data; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Determine the expected buffer address */ 3878c2ecf20Sopenharmony_ci addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Verify which buffer has valid data */ 3908c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { 3938c2ecf20Sopenharmony_ci if (drvdata->rx_ping_pong != 0) 3948c2ecf20Sopenharmony_ci drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET; 3958c2ecf20Sopenharmony_ci } else { 3968c2ecf20Sopenharmony_ci /* The instance is out of sync, try other buffer if other 3978c2ecf20Sopenharmony_ci * buffer is configured, return 0 otherwise. If the instance is 3988c2ecf20Sopenharmony_ci * out of sync, do not update the 'next_rx_buf_to_use' since it 3998c2ecf20Sopenharmony_ci * will correct on subsequent calls 4008c2ecf20Sopenharmony_ci */ 4018c2ecf20Sopenharmony_ci if (drvdata->rx_ping_pong != 0) 4028c2ecf20Sopenharmony_ci addr = (void __iomem __force *)((u32 __force)addr ^ 4038c2ecf20Sopenharmony_ci XEL_BUFFER_OFFSET); 4048c2ecf20Sopenharmony_ci else 4058c2ecf20Sopenharmony_ci return 0; /* No data was available */ 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* Verify that buffer has valid data */ 4088c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 4098c2ecf20Sopenharmony_ci if ((reg_data & XEL_RSR_RECV_DONE_MASK) != 4108c2ecf20Sopenharmony_ci XEL_RSR_RECV_DONE_MASK) 4118c2ecf20Sopenharmony_ci return 0; /* No data was available */ 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* Get the protocol type of the ethernet frame that arrived 4158c2ecf20Sopenharmony_ci */ 4168c2ecf20Sopenharmony_ci proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET + 4178c2ecf20Sopenharmony_ci XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & 4188c2ecf20Sopenharmony_ci XEL_RPLR_LENGTH_MASK); 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci /* Check if received ethernet frame is a raw ethernet frame 4218c2ecf20Sopenharmony_ci * or an IP packet or an ARP packet 4228c2ecf20Sopenharmony_ci */ 4238c2ecf20Sopenharmony_ci if (proto_type > ETH_DATA_LEN) { 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (proto_type == ETH_P_IP) { 4268c2ecf20Sopenharmony_ci length = ((ntohl(xemaclite_readl(addr + 4278c2ecf20Sopenharmony_ci XEL_HEADER_IP_LENGTH_OFFSET + 4288c2ecf20Sopenharmony_ci XEL_RXBUFF_OFFSET)) >> 4298c2ecf20Sopenharmony_ci XEL_HEADER_SHIFT) & 4308c2ecf20Sopenharmony_ci XEL_RPLR_LENGTH_MASK); 4318c2ecf20Sopenharmony_ci length = min_t(u16, length, ETH_DATA_LEN); 4328c2ecf20Sopenharmony_ci length += ETH_HLEN + ETH_FCS_LEN; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci } else if (proto_type == ETH_P_ARP) 4358c2ecf20Sopenharmony_ci length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN; 4368c2ecf20Sopenharmony_ci else 4378c2ecf20Sopenharmony_ci /* Field contains type other than IP or ARP, use max 4388c2ecf20Sopenharmony_ci * frame size and let user parse it 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_ci length = ETH_FRAME_LEN + ETH_FCS_LEN; 4418c2ecf20Sopenharmony_ci } else 4428c2ecf20Sopenharmony_ci /* Use the length in the frame, plus the header and trailer */ 4438c2ecf20Sopenharmony_ci length = proto_type + ETH_HLEN + ETH_FCS_LEN; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci if (WARN_ON(length > maxlen)) 4468c2ecf20Sopenharmony_ci length = maxlen; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci /* Read from the EmacLite device */ 4498c2ecf20Sopenharmony_ci xemaclite_aligned_read((u32 __force *)(addr + XEL_RXBUFF_OFFSET), 4508c2ecf20Sopenharmony_ci data, length); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* Acknowledge the frame */ 4538c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 4548c2ecf20Sopenharmony_ci reg_data &= ~XEL_RSR_RECV_DONE_MASK; 4558c2ecf20Sopenharmony_ci xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci return length; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci/** 4618c2ecf20Sopenharmony_ci * xemaclite_update_address - Update the MAC address in the device 4628c2ecf20Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 4638c2ecf20Sopenharmony_ci * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) 4648c2ecf20Sopenharmony_ci * 4658c2ecf20Sopenharmony_ci * Tx must be idle and Rx should be idle for deterministic results. 4668c2ecf20Sopenharmony_ci * It is recommended that this function should be called after the 4678c2ecf20Sopenharmony_ci * initialization and before transmission of any packets from the device. 4688c2ecf20Sopenharmony_ci * The MAC address can be programmed using any of the two transmit 4698c2ecf20Sopenharmony_ci * buffers (if configured). 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic void xemaclite_update_address(struct net_local *drvdata, 4728c2ecf20Sopenharmony_ci u8 *address_ptr) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci void __iomem *addr; 4758c2ecf20Sopenharmony_ci u32 reg_data; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Determine the expected Tx buffer address */ 4788c2ecf20Sopenharmony_ci addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci xemaclite_aligned_write(address_ptr, (u32 __force *)addr, ETH_ALEN); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* Update the MAC address in the EmacLite */ 4858c2ecf20Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 4868c2ecf20Sopenharmony_ci xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci /* Wait for EmacLite to finish with the MAC address update */ 4898c2ecf20Sopenharmony_ci while ((xemaclite_readl(addr + XEL_TSR_OFFSET) & 4908c2ecf20Sopenharmony_ci XEL_TSR_PROG_MAC_ADDR) != 0) 4918c2ecf20Sopenharmony_ci ; 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci/** 4958c2ecf20Sopenharmony_ci * xemaclite_set_mac_address - Set the MAC address for this device 4968c2ecf20Sopenharmony_ci * @dev: Pointer to the network device instance 4978c2ecf20Sopenharmony_ci * @address: Void pointer to the sockaddr structure 4988c2ecf20Sopenharmony_ci * 4998c2ecf20Sopenharmony_ci * This function copies the HW address from the sockaddr strucutre to the 5008c2ecf20Sopenharmony_ci * net_device structure and updates the address in HW. 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * Return: Error if the net device is busy or 0 if the addr is set 5038c2ecf20Sopenharmony_ci * successfully 5048c2ecf20Sopenharmony_ci */ 5058c2ecf20Sopenharmony_cistatic int xemaclite_set_mac_address(struct net_device *dev, void *address) 5068c2ecf20Sopenharmony_ci{ 5078c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 5088c2ecf20Sopenharmony_ci struct sockaddr *addr = address; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (netif_running(dev)) 5118c2ecf20Sopenharmony_ci return -EBUSY; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 5148c2ecf20Sopenharmony_ci xemaclite_update_address(lp, dev->dev_addr); 5158c2ecf20Sopenharmony_ci return 0; 5168c2ecf20Sopenharmony_ci} 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci/** 5198c2ecf20Sopenharmony_ci * xemaclite_tx_timeout - Callback for Tx Timeout 5208c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * This function is called when Tx time out occurs for Emaclite device. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic void xemaclite_tx_timeout(struct net_device *dev, unsigned int txqueue) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 5278c2ecf20Sopenharmony_ci unsigned long flags; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n", 5308c2ecf20Sopenharmony_ci TX_TIMEOUT * 1000UL / HZ); 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Reset the device */ 5358c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->reset_lock, flags); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci /* Shouldn't really be necessary, but shouldn't hurt */ 5388c2ecf20Sopenharmony_ci netif_stop_queue(dev); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci xemaclite_disable_interrupts(lp); 5418c2ecf20Sopenharmony_ci xemaclite_enable_interrupts(lp); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci if (lp->deferred_skb) { 5448c2ecf20Sopenharmony_ci dev_kfree_skb_irq(lp->deferred_skb); 5458c2ecf20Sopenharmony_ci lp->deferred_skb = NULL; 5468c2ecf20Sopenharmony_ci dev->stats.tx_errors++; 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* To exclude tx timeout */ 5508c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci /* We're all ready to go. Start the queue */ 5538c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci/**********************/ 5588c2ecf20Sopenharmony_ci/* Interrupt Handlers */ 5598c2ecf20Sopenharmony_ci/**********************/ 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci/** 5628c2ecf20Sopenharmony_ci * xemaclite_tx_handler - Interrupt handler for frames sent 5638c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * This function updates the number of packets transmitted and handles the 5668c2ecf20Sopenharmony_ci * deferred skb, if there is one. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_cistatic void xemaclite_tx_handler(struct net_device *dev) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci dev->stats.tx_packets++; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci if (!lp->deferred_skb) 5758c2ecf20Sopenharmony_ci return; 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (xemaclite_send_data(lp, (u8 *)lp->deferred_skb->data, 5788c2ecf20Sopenharmony_ci lp->deferred_skb->len)) 5798c2ecf20Sopenharmony_ci return; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci dev->stats.tx_bytes += lp->deferred_skb->len; 5828c2ecf20Sopenharmony_ci dev_consume_skb_irq(lp->deferred_skb); 5838c2ecf20Sopenharmony_ci lp->deferred_skb = NULL; 5848c2ecf20Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 5858c2ecf20Sopenharmony_ci netif_wake_queue(dev); 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci/** 5898c2ecf20Sopenharmony_ci * xemaclite_rx_handler- Interrupt handler for frames received 5908c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 5918c2ecf20Sopenharmony_ci * 5928c2ecf20Sopenharmony_ci * This function allocates memory for a socket buffer, fills it with data 5938c2ecf20Sopenharmony_ci * received and hands it over to the TCP/IP stack. 5948c2ecf20Sopenharmony_ci */ 5958c2ecf20Sopenharmony_cistatic void xemaclite_rx_handler(struct net_device *dev) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 5988c2ecf20Sopenharmony_ci struct sk_buff *skb; 5998c2ecf20Sopenharmony_ci unsigned int align; 6008c2ecf20Sopenharmony_ci u32 len; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci len = ETH_FRAME_LEN + ETH_FCS_LEN; 6038c2ecf20Sopenharmony_ci skb = netdev_alloc_skb(dev, len + ALIGNMENT); 6048c2ecf20Sopenharmony_ci if (!skb) { 6058c2ecf20Sopenharmony_ci /* Couldn't get memory. */ 6068c2ecf20Sopenharmony_ci dev->stats.rx_dropped++; 6078c2ecf20Sopenharmony_ci dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n"); 6088c2ecf20Sopenharmony_ci return; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* A new skb should have the data halfword aligned, but this code is 6128c2ecf20Sopenharmony_ci * here just in case that isn't true. Calculate how many 6138c2ecf20Sopenharmony_ci * bytes we should reserve to get the data to start on a word 6148c2ecf20Sopenharmony_ci * boundary 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_ci align = BUFFER_ALIGN(skb->data); 6178c2ecf20Sopenharmony_ci if (align) 6188c2ecf20Sopenharmony_ci skb_reserve(skb, align); 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci skb_reserve(skb, 2); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci len = xemaclite_recv_data(lp, (u8 *)skb->data, len); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci if (!len) { 6258c2ecf20Sopenharmony_ci dev->stats.rx_errors++; 6268c2ecf20Sopenharmony_ci dev_kfree_skb_irq(skb); 6278c2ecf20Sopenharmony_ci return; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci skb_put(skb, len); /* Tell the skb how much data we got */ 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 6338c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci dev->stats.rx_packets++; 6368c2ecf20Sopenharmony_ci dev->stats.rx_bytes += len; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!skb_defer_rx_timestamp(skb)) 6398c2ecf20Sopenharmony_ci netif_rx(skb); /* Send the packet upstream */ 6408c2ecf20Sopenharmony_ci} 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/** 6438c2ecf20Sopenharmony_ci * xemaclite_interrupt - Interrupt handler for this driver 6448c2ecf20Sopenharmony_ci * @irq: Irq of the Emaclite device 6458c2ecf20Sopenharmony_ci * @dev_id: Void pointer to the network device instance used as callback 6468c2ecf20Sopenharmony_ci * reference 6478c2ecf20Sopenharmony_ci * 6488c2ecf20Sopenharmony_ci * Return: IRQ_HANDLED 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci * This function handles the Tx and Rx interrupts of the EmacLite device. 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_cistatic irqreturn_t xemaclite_interrupt(int irq, void *dev_id) 6538c2ecf20Sopenharmony_ci{ 6548c2ecf20Sopenharmony_ci bool tx_complete = false; 6558c2ecf20Sopenharmony_ci struct net_device *dev = dev_id; 6568c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 6578c2ecf20Sopenharmony_ci void __iomem *base_addr = lp->base_addr; 6588c2ecf20Sopenharmony_ci u32 tx_status; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci /* Check if there is Rx Data available */ 6618c2ecf20Sopenharmony_ci if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) & 6628c2ecf20Sopenharmony_ci XEL_RSR_RECV_DONE_MASK) || 6638c2ecf20Sopenharmony_ci (xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) 6648c2ecf20Sopenharmony_ci & XEL_RSR_RECV_DONE_MASK)) 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci xemaclite_rx_handler(dev); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* Check if the Transmission for the first buffer is completed */ 6698c2ecf20Sopenharmony_ci tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET); 6708c2ecf20Sopenharmony_ci if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && 6718c2ecf20Sopenharmony_ci (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; 6748c2ecf20Sopenharmony_ci xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci tx_complete = true; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci /* Check if the Transmission for the second buffer is completed */ 6808c2ecf20Sopenharmony_ci tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); 6818c2ecf20Sopenharmony_ci if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && 6828c2ecf20Sopenharmony_ci (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; 6858c2ecf20Sopenharmony_ci xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + 6868c2ecf20Sopenharmony_ci XEL_TSR_OFFSET); 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci tx_complete = true; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* If there was a Tx interrupt, call the Tx Handler */ 6928c2ecf20Sopenharmony_ci if (tx_complete != 0) 6938c2ecf20Sopenharmony_ci xemaclite_tx_handler(dev); 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6968c2ecf20Sopenharmony_ci} 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci/**********************/ 6998c2ecf20Sopenharmony_ci/* MDIO Bus functions */ 7008c2ecf20Sopenharmony_ci/**********************/ 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci/** 7038c2ecf20Sopenharmony_ci * xemaclite_mdio_wait - Wait for the MDIO to be ready to use 7048c2ecf20Sopenharmony_ci * @lp: Pointer to the Emaclite device private data 7058c2ecf20Sopenharmony_ci * 7068c2ecf20Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 7078c2ecf20Sopenharmony_ci * request. 7088c2ecf20Sopenharmony_ci * 7098c2ecf20Sopenharmony_ci * Return: 0 for success or ETIMEDOUT for a timeout 7108c2ecf20Sopenharmony_ci */ 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_cistatic int xemaclite_mdio_wait(struct net_local *lp) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci u32 val; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci /* wait for the MDIO interface to not be busy or timeout 7178c2ecf20Sopenharmony_ci * after some time. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci return readx_poll_timeout(xemaclite_readl, 7208c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET, 7218c2ecf20Sopenharmony_ci val, !(val & XEL_MDIOCTRL_MDIOSTS_MASK), 7228c2ecf20Sopenharmony_ci 1000, 20000); 7238c2ecf20Sopenharmony_ci} 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci/** 7268c2ecf20Sopenharmony_ci * xemaclite_mdio_read - Read from a given MII management register 7278c2ecf20Sopenharmony_ci * @bus: the mii_bus struct 7288c2ecf20Sopenharmony_ci * @phy_id: the phy address 7298c2ecf20Sopenharmony_ci * @reg: register number to read from 7308c2ecf20Sopenharmony_ci * 7318c2ecf20Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 7328c2ecf20Sopenharmony_ci * request and then writes the phy address to the MDIO Address register 7338c2ecf20Sopenharmony_ci * and reads data from MDIO Read Data register, when its available. 7348c2ecf20Sopenharmony_ci * 7358c2ecf20Sopenharmony_ci * Return: Value read from the MII management register 7368c2ecf20Sopenharmony_ci */ 7378c2ecf20Sopenharmony_cistatic int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci struct net_local *lp = bus->priv; 7408c2ecf20Sopenharmony_ci u32 ctrl_reg; 7418c2ecf20Sopenharmony_ci u32 rc; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 7448c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci /* Write the PHY address, register number and set the OP bit in the 7478c2ecf20Sopenharmony_ci * MDIO Address register. Set the Status bit in the MDIO Control 7488c2ecf20Sopenharmony_ci * register to start a MDIO read transaction. 7498c2ecf20Sopenharmony_ci */ 7508c2ecf20Sopenharmony_ci ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); 7518c2ecf20Sopenharmony_ci xemaclite_writel(XEL_MDIOADDR_OP_MASK | 7528c2ecf20Sopenharmony_ci ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), 7538c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOADDR_OFFSET); 7548c2ecf20Sopenharmony_ci xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, 7558c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 7588c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET); 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_ci dev_dbg(&lp->ndev->dev, 7638c2ecf20Sopenharmony_ci "%s(phy_id=%i, reg=%x) == %x\n", __func__, 7648c2ecf20Sopenharmony_ci phy_id, reg, rc); 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci return rc; 7678c2ecf20Sopenharmony_ci} 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/** 7708c2ecf20Sopenharmony_ci * xemaclite_mdio_write - Write to a given MII management register 7718c2ecf20Sopenharmony_ci * @bus: the mii_bus struct 7728c2ecf20Sopenharmony_ci * @phy_id: the phy address 7738c2ecf20Sopenharmony_ci * @reg: register number to write to 7748c2ecf20Sopenharmony_ci * @val: value to write to the register number specified by reg 7758c2ecf20Sopenharmony_ci * 7768c2ecf20Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 7778c2ecf20Sopenharmony_ci * request and then writes the val to the MDIO Write Data register. 7788c2ecf20Sopenharmony_ci * 7798c2ecf20Sopenharmony_ci * Return: 0 upon success or a negative error upon failure 7808c2ecf20Sopenharmony_ci */ 7818c2ecf20Sopenharmony_cistatic int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, 7828c2ecf20Sopenharmony_ci u16 val) 7838c2ecf20Sopenharmony_ci{ 7848c2ecf20Sopenharmony_ci struct net_local *lp = bus->priv; 7858c2ecf20Sopenharmony_ci u32 ctrl_reg; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci dev_dbg(&lp->ndev->dev, 7888c2ecf20Sopenharmony_ci "%s(phy_id=%i, reg=%x, val=%x)\n", __func__, 7898c2ecf20Sopenharmony_ci phy_id, reg, val); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 7928c2ecf20Sopenharmony_ci return -ETIMEDOUT; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci /* Write the PHY address, register number and clear the OP bit in the 7958c2ecf20Sopenharmony_ci * MDIO Address register and then write the value into the MDIO Write 7968c2ecf20Sopenharmony_ci * Data register. Finally, set the Status bit in the MDIO Control 7978c2ecf20Sopenharmony_ci * register to start a MDIO write transaction. 7988c2ecf20Sopenharmony_ci */ 7998c2ecf20Sopenharmony_ci ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); 8008c2ecf20Sopenharmony_ci xemaclite_writel(~XEL_MDIOADDR_OP_MASK & 8018c2ecf20Sopenharmony_ci ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), 8028c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOADDR_OFFSET); 8038c2ecf20Sopenharmony_ci xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); 8048c2ecf20Sopenharmony_ci xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, 8058c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci return 0; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci/** 8118c2ecf20Sopenharmony_ci * xemaclite_mdio_setup - Register mii_bus for the Emaclite device 8128c2ecf20Sopenharmony_ci * @lp: Pointer to the Emaclite device private data 8138c2ecf20Sopenharmony_ci * @dev: Pointer to OF device structure 8148c2ecf20Sopenharmony_ci * 8158c2ecf20Sopenharmony_ci * This function enables MDIO bus in the Emaclite device and registers a 8168c2ecf20Sopenharmony_ci * mii_bus. 8178c2ecf20Sopenharmony_ci * 8188c2ecf20Sopenharmony_ci * Return: 0 upon success or a negative error upon failure 8198c2ecf20Sopenharmony_ci */ 8208c2ecf20Sopenharmony_cistatic int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) 8218c2ecf20Sopenharmony_ci{ 8228c2ecf20Sopenharmony_ci struct mii_bus *bus; 8238c2ecf20Sopenharmony_ci struct resource res; 8248c2ecf20Sopenharmony_ci struct device_node *np = of_get_parent(lp->phy_node); 8258c2ecf20Sopenharmony_ci struct device_node *npp; 8268c2ecf20Sopenharmony_ci int rc, ret; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci /* Don't register the MDIO bus if the phy_node or its parent node 8298c2ecf20Sopenharmony_ci * can't be found. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_ci if (!np) { 8328c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register mdio bus.\n"); 8338c2ecf20Sopenharmony_ci return -ENODEV; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci npp = of_get_parent(np); 8368c2ecf20Sopenharmony_ci ret = of_address_to_resource(npp, 0, &res); 8378c2ecf20Sopenharmony_ci of_node_put(npp); 8388c2ecf20Sopenharmony_ci if (ret) { 8398c2ecf20Sopenharmony_ci dev_err(dev, "%s resource error!\n", 8408c2ecf20Sopenharmony_ci dev->of_node->full_name); 8418c2ecf20Sopenharmony_ci of_node_put(np); 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci if (lp->ndev->mem_start != res.start) { 8458c2ecf20Sopenharmony_ci struct phy_device *phydev; 8468c2ecf20Sopenharmony_ci phydev = of_phy_find_device(lp->phy_node); 8478c2ecf20Sopenharmony_ci if (!phydev) 8488c2ecf20Sopenharmony_ci dev_info(dev, 8498c2ecf20Sopenharmony_ci "MDIO of the phy is not registered yet\n"); 8508c2ecf20Sopenharmony_ci else 8518c2ecf20Sopenharmony_ci put_device(&phydev->mdio.dev); 8528c2ecf20Sopenharmony_ci of_node_put(np); 8538c2ecf20Sopenharmony_ci return 0; 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci 8568c2ecf20Sopenharmony_ci /* Enable the MDIO bus by asserting the enable bit in MDIO Control 8578c2ecf20Sopenharmony_ci * register. 8588c2ecf20Sopenharmony_ci */ 8598c2ecf20Sopenharmony_ci xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK, 8608c2ecf20Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci bus = mdiobus_alloc(); 8638c2ecf20Sopenharmony_ci if (!bus) { 8648c2ecf20Sopenharmony_ci dev_err(dev, "Failed to allocate mdiobus\n"); 8658c2ecf20Sopenharmony_ci of_node_put(np); 8668c2ecf20Sopenharmony_ci return -ENOMEM; 8678c2ecf20Sopenharmony_ci } 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", 8708c2ecf20Sopenharmony_ci (unsigned long long)res.start); 8718c2ecf20Sopenharmony_ci bus->priv = lp; 8728c2ecf20Sopenharmony_ci bus->name = "Xilinx Emaclite MDIO"; 8738c2ecf20Sopenharmony_ci bus->read = xemaclite_mdio_read; 8748c2ecf20Sopenharmony_ci bus->write = xemaclite_mdio_write; 8758c2ecf20Sopenharmony_ci bus->parent = dev; 8768c2ecf20Sopenharmony_ci 8778c2ecf20Sopenharmony_ci rc = of_mdiobus_register(bus, np); 8788c2ecf20Sopenharmony_ci of_node_put(np); 8798c2ecf20Sopenharmony_ci if (rc) { 8808c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register mdio bus.\n"); 8818c2ecf20Sopenharmony_ci goto err_register; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci lp->mii_bus = bus; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci return 0; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_cierr_register: 8898c2ecf20Sopenharmony_ci mdiobus_free(bus); 8908c2ecf20Sopenharmony_ci return rc; 8918c2ecf20Sopenharmony_ci} 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci/** 8948c2ecf20Sopenharmony_ci * xemaclite_adjust_link - Link state callback for the Emaclite device 8958c2ecf20Sopenharmony_ci * @ndev: pointer to net_device struct 8968c2ecf20Sopenharmony_ci * 8978c2ecf20Sopenharmony_ci * There's nothing in the Emaclite device to be configured when the link 8988c2ecf20Sopenharmony_ci * state changes. We just print the status. 8998c2ecf20Sopenharmony_ci */ 9008c2ecf20Sopenharmony_cistatic void xemaclite_adjust_link(struct net_device *ndev) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(ndev); 9038c2ecf20Sopenharmony_ci struct phy_device *phy = lp->phy_dev; 9048c2ecf20Sopenharmony_ci int link_state; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci /* hash together the state values to decide if something has changed */ 9078c2ecf20Sopenharmony_ci link_state = phy->speed | (phy->duplex << 1) | phy->link; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci if (lp->last_link != link_state) { 9108c2ecf20Sopenharmony_ci lp->last_link = link_state; 9118c2ecf20Sopenharmony_ci phy_print_status(phy); 9128c2ecf20Sopenharmony_ci } 9138c2ecf20Sopenharmony_ci} 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci/** 9168c2ecf20Sopenharmony_ci * xemaclite_open - Open the network device 9178c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 9188c2ecf20Sopenharmony_ci * 9198c2ecf20Sopenharmony_ci * This function sets the MAC address, requests an IRQ and enables interrupts 9208c2ecf20Sopenharmony_ci * for the Emaclite device and starts the Tx queue. 9218c2ecf20Sopenharmony_ci * It also connects to the phy device, if MDIO is included in Emaclite device. 9228c2ecf20Sopenharmony_ci * 9238c2ecf20Sopenharmony_ci * Return: 0 on success. -ENODEV, if PHY cannot be connected. 9248c2ecf20Sopenharmony_ci * Non-zero error value on failure. 9258c2ecf20Sopenharmony_ci */ 9268c2ecf20Sopenharmony_cistatic int xemaclite_open(struct net_device *dev) 9278c2ecf20Sopenharmony_ci{ 9288c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 9298c2ecf20Sopenharmony_ci int retval; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci /* Just to be safe, stop the device first */ 9328c2ecf20Sopenharmony_ci xemaclite_disable_interrupts(lp); 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_ci if (lp->phy_node) { 9358c2ecf20Sopenharmony_ci lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, 9368c2ecf20Sopenharmony_ci xemaclite_adjust_link, 0, 9378c2ecf20Sopenharmony_ci PHY_INTERFACE_MODE_MII); 9388c2ecf20Sopenharmony_ci if (!lp->phy_dev) { 9398c2ecf20Sopenharmony_ci dev_err(&lp->ndev->dev, "of_phy_connect() failed\n"); 9408c2ecf20Sopenharmony_ci return -ENODEV; 9418c2ecf20Sopenharmony_ci } 9428c2ecf20Sopenharmony_ci 9438c2ecf20Sopenharmony_ci /* EmacLite doesn't support giga-bit speeds */ 9448c2ecf20Sopenharmony_ci phy_set_max_speed(lp->phy_dev, SPEED_100); 9458c2ecf20Sopenharmony_ci phy_start(lp->phy_dev); 9468c2ecf20Sopenharmony_ci } 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci /* Set the MAC address each time opened */ 9498c2ecf20Sopenharmony_ci xemaclite_update_address(lp, dev->dev_addr); 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_ci /* Grab the IRQ */ 9528c2ecf20Sopenharmony_ci retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); 9538c2ecf20Sopenharmony_ci if (retval) { 9548c2ecf20Sopenharmony_ci dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", 9558c2ecf20Sopenharmony_ci dev->irq); 9568c2ecf20Sopenharmony_ci if (lp->phy_dev) 9578c2ecf20Sopenharmony_ci phy_disconnect(lp->phy_dev); 9588c2ecf20Sopenharmony_ci lp->phy_dev = NULL; 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci return retval; 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci /* Enable Interrupts */ 9648c2ecf20Sopenharmony_ci xemaclite_enable_interrupts(lp); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci /* We're ready to go */ 9678c2ecf20Sopenharmony_ci netif_start_queue(dev); 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci return 0; 9708c2ecf20Sopenharmony_ci} 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci/** 9738c2ecf20Sopenharmony_ci * xemaclite_close - Close the network device 9748c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 9758c2ecf20Sopenharmony_ci * 9768c2ecf20Sopenharmony_ci * This function stops the Tx queue, disables interrupts and frees the IRQ for 9778c2ecf20Sopenharmony_ci * the Emaclite device. 9788c2ecf20Sopenharmony_ci * It also disconnects the phy device associated with the Emaclite device. 9798c2ecf20Sopenharmony_ci * 9808c2ecf20Sopenharmony_ci * Return: 0, always. 9818c2ecf20Sopenharmony_ci */ 9828c2ecf20Sopenharmony_cistatic int xemaclite_close(struct net_device *dev) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci netif_stop_queue(dev); 9878c2ecf20Sopenharmony_ci xemaclite_disable_interrupts(lp); 9888c2ecf20Sopenharmony_ci free_irq(dev->irq, dev); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (lp->phy_dev) 9918c2ecf20Sopenharmony_ci phy_disconnect(lp->phy_dev); 9928c2ecf20Sopenharmony_ci lp->phy_dev = NULL; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci return 0; 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci/** 9988c2ecf20Sopenharmony_ci * xemaclite_send - Transmit a frame 9998c2ecf20Sopenharmony_ci * @orig_skb: Pointer to the socket buffer to be transmitted 10008c2ecf20Sopenharmony_ci * @dev: Pointer to the network device 10018c2ecf20Sopenharmony_ci * 10028c2ecf20Sopenharmony_ci * This function checks if the Tx buffer of the Emaclite device is free to send 10038c2ecf20Sopenharmony_ci * data. If so, it fills the Tx buffer with data from socket buffer data, 10048c2ecf20Sopenharmony_ci * updates the stats and frees the socket buffer. The Tx completion is signaled 10058c2ecf20Sopenharmony_ci * by an interrupt. If the Tx buffer isn't free, then the socket buffer is 10068c2ecf20Sopenharmony_ci * deferred and the Tx queue is stopped so that the deferred socket buffer can 10078c2ecf20Sopenharmony_ci * be transmitted when the Emaclite device is free to transmit data. 10088c2ecf20Sopenharmony_ci * 10098c2ecf20Sopenharmony_ci * Return: NETDEV_TX_OK, always. 10108c2ecf20Sopenharmony_ci */ 10118c2ecf20Sopenharmony_cistatic netdev_tx_t 10128c2ecf20Sopenharmony_cixemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) 10138c2ecf20Sopenharmony_ci{ 10148c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 10158c2ecf20Sopenharmony_ci struct sk_buff *new_skb; 10168c2ecf20Sopenharmony_ci unsigned int len; 10178c2ecf20Sopenharmony_ci unsigned long flags; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci len = orig_skb->len; 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci new_skb = orig_skb; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci spin_lock_irqsave(&lp->reset_lock, flags); 10248c2ecf20Sopenharmony_ci if (xemaclite_send_data(lp, (u8 *)new_skb->data, len) != 0) { 10258c2ecf20Sopenharmony_ci /* If the Emaclite Tx buffer is busy, stop the Tx queue and 10268c2ecf20Sopenharmony_ci * defer the skb for transmission during the ISR, after the 10278c2ecf20Sopenharmony_ci * current transmission is complete 10288c2ecf20Sopenharmony_ci */ 10298c2ecf20Sopenharmony_ci netif_stop_queue(dev); 10308c2ecf20Sopenharmony_ci lp->deferred_skb = new_skb; 10318c2ecf20Sopenharmony_ci /* Take the time stamp now, since we can't do this in an ISR. */ 10328c2ecf20Sopenharmony_ci skb_tx_timestamp(new_skb); 10338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 10348c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10358c2ecf20Sopenharmony_ci } 10368c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 10378c2ecf20Sopenharmony_ci 10388c2ecf20Sopenharmony_ci skb_tx_timestamp(new_skb); 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_ci dev->stats.tx_bytes += len; 10418c2ecf20Sopenharmony_ci dev_consume_skb_any(new_skb); 10428c2ecf20Sopenharmony_ci 10438c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 10448c2ecf20Sopenharmony_ci} 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci/** 10478c2ecf20Sopenharmony_ci * get_bool - Get a parameter from the OF device 10488c2ecf20Sopenharmony_ci * @ofdev: Pointer to OF device structure 10498c2ecf20Sopenharmony_ci * @s: Property to be retrieved 10508c2ecf20Sopenharmony_ci * 10518c2ecf20Sopenharmony_ci * This function looks for a property in the device node and returns the value 10528c2ecf20Sopenharmony_ci * of the property if its found or 0 if the property is not found. 10538c2ecf20Sopenharmony_ci * 10548c2ecf20Sopenharmony_ci * Return: Value of the parameter if the parameter is found, or 0 otherwise 10558c2ecf20Sopenharmony_ci */ 10568c2ecf20Sopenharmony_cistatic bool get_bool(struct platform_device *ofdev, const char *s) 10578c2ecf20Sopenharmony_ci{ 10588c2ecf20Sopenharmony_ci u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL); 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (!p) { 10618c2ecf20Sopenharmony_ci dev_warn(&ofdev->dev, "Parameter %s not found, defaulting to false\n", s); 10628c2ecf20Sopenharmony_ci return false; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci return (bool)*p; 10668c2ecf20Sopenharmony_ci} 10678c2ecf20Sopenharmony_ci 10688c2ecf20Sopenharmony_ci/** 10698c2ecf20Sopenharmony_ci * xemaclite_ethtools_get_drvinfo - Get various Axi Emac Lite driver info 10708c2ecf20Sopenharmony_ci * @ndev: Pointer to net_device structure 10718c2ecf20Sopenharmony_ci * @ed: Pointer to ethtool_drvinfo structure 10728c2ecf20Sopenharmony_ci * 10738c2ecf20Sopenharmony_ci * This implements ethtool command for getting the driver information. 10748c2ecf20Sopenharmony_ci * Issue "ethtool -i ethX" under linux prompt to execute this function. 10758c2ecf20Sopenharmony_ci */ 10768c2ecf20Sopenharmony_cistatic void xemaclite_ethtools_get_drvinfo(struct net_device *ndev, 10778c2ecf20Sopenharmony_ci struct ethtool_drvinfo *ed) 10788c2ecf20Sopenharmony_ci{ 10798c2ecf20Sopenharmony_ci strlcpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); 10808c2ecf20Sopenharmony_ci} 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_cistatic const struct ethtool_ops xemaclite_ethtool_ops = { 10838c2ecf20Sopenharmony_ci .get_drvinfo = xemaclite_ethtools_get_drvinfo, 10848c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 10858c2ecf20Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 10868c2ecf20Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 10878c2ecf20Sopenharmony_ci}; 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_cistatic const struct net_device_ops xemaclite_netdev_ops; 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci/** 10928c2ecf20Sopenharmony_ci * xemaclite_of_probe - Probe method for the Emaclite device. 10938c2ecf20Sopenharmony_ci * @ofdev: Pointer to OF device structure 10948c2ecf20Sopenharmony_ci * 10958c2ecf20Sopenharmony_ci * This function probes for the Emaclite device in the device tree. 10968c2ecf20Sopenharmony_ci * It initializes the driver data structure and the hardware, sets the MAC 10978c2ecf20Sopenharmony_ci * address and registers the network device. 10988c2ecf20Sopenharmony_ci * It also registers a mii_bus for the Emaclite device, if MDIO is included 10998c2ecf20Sopenharmony_ci * in the device. 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * Return: 0, if the driver is bound to the Emaclite device, or 11028c2ecf20Sopenharmony_ci * a negative error if there is failure. 11038c2ecf20Sopenharmony_ci */ 11048c2ecf20Sopenharmony_cistatic int xemaclite_of_probe(struct platform_device *ofdev) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci struct resource *res; 11078c2ecf20Sopenharmony_ci struct net_device *ndev = NULL; 11088c2ecf20Sopenharmony_ci struct net_local *lp = NULL; 11098c2ecf20Sopenharmony_ci struct device *dev = &ofdev->dev; 11108c2ecf20Sopenharmony_ci const void *mac_address; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci int rc = 0; 11138c2ecf20Sopenharmony_ci 11148c2ecf20Sopenharmony_ci dev_info(dev, "Device Tree Probing\n"); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* Create an ethernet device instance */ 11178c2ecf20Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct net_local)); 11188c2ecf20Sopenharmony_ci if (!ndev) 11198c2ecf20Sopenharmony_ci return -ENOMEM; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ndev); 11228c2ecf20Sopenharmony_ci SET_NETDEV_DEV(ndev, &ofdev->dev); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci lp = netdev_priv(ndev); 11258c2ecf20Sopenharmony_ci lp->ndev = ndev; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci /* Get IRQ for the device */ 11288c2ecf20Sopenharmony_ci res = platform_get_resource(ofdev, IORESOURCE_IRQ, 0); 11298c2ecf20Sopenharmony_ci if (!res) { 11308c2ecf20Sopenharmony_ci dev_err(dev, "no IRQ found\n"); 11318c2ecf20Sopenharmony_ci rc = -ENXIO; 11328c2ecf20Sopenharmony_ci goto error; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci ndev->irq = res->start; 11368c2ecf20Sopenharmony_ci 11378c2ecf20Sopenharmony_ci res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); 11388c2ecf20Sopenharmony_ci lp->base_addr = devm_ioremap_resource(&ofdev->dev, res); 11398c2ecf20Sopenharmony_ci if (IS_ERR(lp->base_addr)) { 11408c2ecf20Sopenharmony_ci rc = PTR_ERR(lp->base_addr); 11418c2ecf20Sopenharmony_ci goto error; 11428c2ecf20Sopenharmony_ci } 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci ndev->mem_start = res->start; 11458c2ecf20Sopenharmony_ci ndev->mem_end = res->end; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci spin_lock_init(&lp->reset_lock); 11488c2ecf20Sopenharmony_ci lp->next_tx_buf_to_use = 0x0; 11498c2ecf20Sopenharmony_ci lp->next_rx_buf_to_use = 0x0; 11508c2ecf20Sopenharmony_ci lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong"); 11518c2ecf20Sopenharmony_ci lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong"); 11528c2ecf20Sopenharmony_ci mac_address = of_get_mac_address(ofdev->dev.of_node); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci if (!IS_ERR(mac_address)) { 11558c2ecf20Sopenharmony_ci /* Set the MAC address. */ 11568c2ecf20Sopenharmony_ci ether_addr_copy(ndev->dev_addr, mac_address); 11578c2ecf20Sopenharmony_ci } else { 11588c2ecf20Sopenharmony_ci dev_warn(dev, "No MAC address found, using random\n"); 11598c2ecf20Sopenharmony_ci eth_hw_addr_random(ndev); 11608c2ecf20Sopenharmony_ci } 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci /* Clear the Tx CSR's in case this is a restart */ 11638c2ecf20Sopenharmony_ci xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET); 11648c2ecf20Sopenharmony_ci xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci /* Set the MAC address in the EmacLite device */ 11678c2ecf20Sopenharmony_ci xemaclite_update_address(lp, ndev->dev_addr); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); 11708c2ecf20Sopenharmony_ci xemaclite_mdio_setup(lp, &ofdev->dev); 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci ndev->netdev_ops = &xemaclite_netdev_ops; 11758c2ecf20Sopenharmony_ci ndev->ethtool_ops = &xemaclite_ethtool_ops; 11768c2ecf20Sopenharmony_ci ndev->flags &= ~IFF_MULTICAST; 11778c2ecf20Sopenharmony_ci ndev->watchdog_timeo = TX_TIMEOUT; 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci /* Finally, register the device */ 11808c2ecf20Sopenharmony_ci rc = register_netdev(ndev); 11818c2ecf20Sopenharmony_ci if (rc) { 11828c2ecf20Sopenharmony_ci dev_err(dev, 11838c2ecf20Sopenharmony_ci "Cannot register network device, aborting\n"); 11848c2ecf20Sopenharmony_ci goto put_node; 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci dev_info(dev, 11888c2ecf20Sopenharmony_ci "Xilinx EmacLite at 0x%08X mapped to 0x%p, irq=%d\n", 11898c2ecf20Sopenharmony_ci (unsigned int __force)ndev->mem_start, lp->base_addr, ndev->irq); 11908c2ecf20Sopenharmony_ci return 0; 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ciput_node: 11938c2ecf20Sopenharmony_ci of_node_put(lp->phy_node); 11948c2ecf20Sopenharmony_cierror: 11958c2ecf20Sopenharmony_ci free_netdev(ndev); 11968c2ecf20Sopenharmony_ci return rc; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci/** 12008c2ecf20Sopenharmony_ci * xemaclite_of_remove - Unbind the driver from the Emaclite device. 12018c2ecf20Sopenharmony_ci * @of_dev: Pointer to OF device structure 12028c2ecf20Sopenharmony_ci * 12038c2ecf20Sopenharmony_ci * This function is called if a device is physically removed from the system or 12048c2ecf20Sopenharmony_ci * if the driver module is being unloaded. It frees any resources allocated to 12058c2ecf20Sopenharmony_ci * the device. 12068c2ecf20Sopenharmony_ci * 12078c2ecf20Sopenharmony_ci * Return: 0, always. 12088c2ecf20Sopenharmony_ci */ 12098c2ecf20Sopenharmony_cistatic int xemaclite_of_remove(struct platform_device *of_dev) 12108c2ecf20Sopenharmony_ci{ 12118c2ecf20Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(of_dev); 12128c2ecf20Sopenharmony_ci 12138c2ecf20Sopenharmony_ci struct net_local *lp = netdev_priv(ndev); 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* Un-register the mii_bus, if configured */ 12168c2ecf20Sopenharmony_ci if (lp->mii_bus) { 12178c2ecf20Sopenharmony_ci mdiobus_unregister(lp->mii_bus); 12188c2ecf20Sopenharmony_ci mdiobus_free(lp->mii_bus); 12198c2ecf20Sopenharmony_ci lp->mii_bus = NULL; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci unregister_netdev(ndev); 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci of_node_put(lp->phy_node); 12258c2ecf20Sopenharmony_ci lp->phy_node = NULL; 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci free_netdev(ndev); 12288c2ecf20Sopenharmony_ci 12298c2ecf20Sopenharmony_ci return 0; 12308c2ecf20Sopenharmony_ci} 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 12338c2ecf20Sopenharmony_cistatic void 12348c2ecf20Sopenharmony_cixemaclite_poll_controller(struct net_device *ndev) 12358c2ecf20Sopenharmony_ci{ 12368c2ecf20Sopenharmony_ci disable_irq(ndev->irq); 12378c2ecf20Sopenharmony_ci xemaclite_interrupt(ndev->irq, ndev); 12388c2ecf20Sopenharmony_ci enable_irq(ndev->irq); 12398c2ecf20Sopenharmony_ci} 12408c2ecf20Sopenharmony_ci#endif 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci/* Ioctl MII Interface */ 12438c2ecf20Sopenharmony_cistatic int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci if (!dev->phydev || !netif_running(dev)) 12468c2ecf20Sopenharmony_ci return -EINVAL; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci switch (cmd) { 12498c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 12508c2ecf20Sopenharmony_ci case SIOCGMIIREG: 12518c2ecf20Sopenharmony_ci case SIOCSMIIREG: 12528c2ecf20Sopenharmony_ci return phy_mii_ioctl(dev->phydev, rq, cmd); 12538c2ecf20Sopenharmony_ci default: 12548c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci} 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_cistatic const struct net_device_ops xemaclite_netdev_ops = { 12598c2ecf20Sopenharmony_ci .ndo_open = xemaclite_open, 12608c2ecf20Sopenharmony_ci .ndo_stop = xemaclite_close, 12618c2ecf20Sopenharmony_ci .ndo_start_xmit = xemaclite_send, 12628c2ecf20Sopenharmony_ci .ndo_set_mac_address = xemaclite_set_mac_address, 12638c2ecf20Sopenharmony_ci .ndo_tx_timeout = xemaclite_tx_timeout, 12648c2ecf20Sopenharmony_ci .ndo_do_ioctl = xemaclite_ioctl, 12658c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 12668c2ecf20Sopenharmony_ci .ndo_poll_controller = xemaclite_poll_controller, 12678c2ecf20Sopenharmony_ci#endif 12688c2ecf20Sopenharmony_ci}; 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci/* Match table for OF platform binding */ 12718c2ecf20Sopenharmony_cistatic const struct of_device_id xemaclite_of_match[] = { 12728c2ecf20Sopenharmony_ci { .compatible = "xlnx,opb-ethernetlite-1.01.a", }, 12738c2ecf20Sopenharmony_ci { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, 12748c2ecf20Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, 12758c2ecf20Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, 12768c2ecf20Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, 12778c2ecf20Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, 12788c2ecf20Sopenharmony_ci { /* end of list */ }, 12798c2ecf20Sopenharmony_ci}; 12808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, xemaclite_of_match); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_cistatic struct platform_driver xemaclite_of_driver = { 12838c2ecf20Sopenharmony_ci .driver = { 12848c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 12858c2ecf20Sopenharmony_ci .of_match_table = xemaclite_of_match, 12868c2ecf20Sopenharmony_ci }, 12878c2ecf20Sopenharmony_ci .probe = xemaclite_of_probe, 12888c2ecf20Sopenharmony_ci .remove = xemaclite_of_remove, 12898c2ecf20Sopenharmony_ci}; 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_cimodule_platform_driver(xemaclite_of_driver); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc."); 12948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver"); 12958c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1296