162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This is a new flat driver which is based on the original emac_lite 562306a36Sopenharmony_ci * driver from John Williams <john.williams@xilinx.com>. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (c) 2007 - 2013 Xilinx, Inc. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/platform_device.h> 1262306a36Sopenharmony_ci#include <linux/uaccess.h> 1362306a36Sopenharmony_ci#include <linux/netdevice.h> 1462306a36Sopenharmony_ci#include <linux/etherdevice.h> 1562306a36Sopenharmony_ci#include <linux/skbuff.h> 1662306a36Sopenharmony_ci#include <linux/ethtool.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <linux/slab.h> 1962306a36Sopenharmony_ci#include <linux/of.h> 2062306a36Sopenharmony_ci#include <linux/of_address.h> 2162306a36Sopenharmony_ci#include <linux/of_mdio.h> 2262306a36Sopenharmony_ci#include <linux/of_net.h> 2362306a36Sopenharmony_ci#include <linux/phy.h> 2462306a36Sopenharmony_ci#include <linux/interrupt.h> 2562306a36Sopenharmony_ci#include <linux/iopoll.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define DRIVER_NAME "xilinx_emaclite" 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci/* Register offsets for the EmacLite Core */ 3062306a36Sopenharmony_ci#define XEL_TXBUFF_OFFSET 0x0 /* Transmit Buffer */ 3162306a36Sopenharmony_ci#define XEL_MDIOADDR_OFFSET 0x07E4 /* MDIO Address Register */ 3262306a36Sopenharmony_ci#define XEL_MDIOWR_OFFSET 0x07E8 /* MDIO Write Data Register */ 3362306a36Sopenharmony_ci#define XEL_MDIORD_OFFSET 0x07EC /* MDIO Read Data Register */ 3462306a36Sopenharmony_ci#define XEL_MDIOCTRL_OFFSET 0x07F0 /* MDIO Control Register */ 3562306a36Sopenharmony_ci#define XEL_GIER_OFFSET 0x07F8 /* GIE Register */ 3662306a36Sopenharmony_ci#define XEL_TSR_OFFSET 0x07FC /* Tx status */ 3762306a36Sopenharmony_ci#define XEL_TPLR_OFFSET 0x07F4 /* Tx packet length */ 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define XEL_RXBUFF_OFFSET 0x1000 /* Receive Buffer */ 4062306a36Sopenharmony_ci#define XEL_RPLR_OFFSET 0x100C /* Rx packet length */ 4162306a36Sopenharmony_ci#define XEL_RSR_OFFSET 0x17FC /* Rx status */ 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define XEL_BUFFER_OFFSET 0x0800 /* Next Tx/Rx buffer's offset */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* MDIO Address Register Bit Masks */ 4662306a36Sopenharmony_ci#define XEL_MDIOADDR_REGADR_MASK 0x0000001F /* Register Address */ 4762306a36Sopenharmony_ci#define XEL_MDIOADDR_PHYADR_MASK 0x000003E0 /* PHY Address */ 4862306a36Sopenharmony_ci#define XEL_MDIOADDR_PHYADR_SHIFT 5 4962306a36Sopenharmony_ci#define XEL_MDIOADDR_OP_MASK 0x00000400 /* RD/WR Operation */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci/* MDIO Write Data Register Bit Masks */ 5262306a36Sopenharmony_ci#define XEL_MDIOWR_WRDATA_MASK 0x0000FFFF /* Data to be Written */ 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* MDIO Read Data Register Bit Masks */ 5562306a36Sopenharmony_ci#define XEL_MDIORD_RDDATA_MASK 0x0000FFFF /* Data to be Read */ 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* MDIO Control Register Bit Masks */ 5862306a36Sopenharmony_ci#define XEL_MDIOCTRL_MDIOSTS_MASK 0x00000001 /* MDIO Status Mask */ 5962306a36Sopenharmony_ci#define XEL_MDIOCTRL_MDIOEN_MASK 0x00000008 /* MDIO Enable */ 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/* Global Interrupt Enable Register (GIER) Bit Masks */ 6262306a36Sopenharmony_ci#define XEL_GIER_GIE_MASK 0x80000000 /* Global Enable */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Transmit Status Register (TSR) Bit Masks */ 6562306a36Sopenharmony_ci#define XEL_TSR_XMIT_BUSY_MASK 0x00000001 /* Tx complete */ 6662306a36Sopenharmony_ci#define XEL_TSR_PROGRAM_MASK 0x00000002 /* Program the MAC address */ 6762306a36Sopenharmony_ci#define XEL_TSR_XMIT_IE_MASK 0x00000008 /* Tx interrupt enable bit */ 6862306a36Sopenharmony_ci#define XEL_TSR_XMIT_ACTIVE_MASK 0x80000000 /* Buffer is active, SW bit 6962306a36Sopenharmony_ci * only. This is not documented 7062306a36Sopenharmony_ci * in the HW spec 7162306a36Sopenharmony_ci */ 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci/* Define for programming the MAC address into the EmacLite */ 7462306a36Sopenharmony_ci#define XEL_TSR_PROG_MAC_ADDR (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_PROGRAM_MASK) 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* Receive Status Register (RSR) */ 7762306a36Sopenharmony_ci#define XEL_RSR_RECV_DONE_MASK 0x00000001 /* Rx complete */ 7862306a36Sopenharmony_ci#define XEL_RSR_RECV_IE_MASK 0x00000008 /* Rx interrupt enable bit */ 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Transmit Packet Length Register (TPLR) */ 8162306a36Sopenharmony_ci#define XEL_TPLR_LENGTH_MASK 0x0000FFFF /* Tx packet length */ 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/* Receive Packet Length Register (RPLR) */ 8462306a36Sopenharmony_ci#define XEL_RPLR_LENGTH_MASK 0x0000FFFF /* Rx packet length */ 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define XEL_HEADER_OFFSET 12 /* Offset to length field */ 8762306a36Sopenharmony_ci#define XEL_HEADER_SHIFT 16 /* Shift value for length */ 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci/* General Ethernet Definitions */ 9062306a36Sopenharmony_ci#define XEL_ARP_PACKET_SIZE 28 /* Max ARP packet size */ 9162306a36Sopenharmony_ci#define XEL_HEADER_IP_LENGTH_OFFSET 16 /* IP Length Offset */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define TX_TIMEOUT (60 * HZ) /* Tx timeout is 60 seconds. */ 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 9662306a36Sopenharmony_ci#define xemaclite_readl ioread32be 9762306a36Sopenharmony_ci#define xemaclite_writel iowrite32be 9862306a36Sopenharmony_ci#else 9962306a36Sopenharmony_ci#define xemaclite_readl ioread32 10062306a36Sopenharmony_ci#define xemaclite_writel iowrite32 10162306a36Sopenharmony_ci#endif 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/** 10462306a36Sopenharmony_ci * struct net_local - Our private per device data 10562306a36Sopenharmony_ci * @ndev: instance of the network device 10662306a36Sopenharmony_ci * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW 10762306a36Sopenharmony_ci * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW 10862306a36Sopenharmony_ci * @next_tx_buf_to_use: next Tx buffer to write to 10962306a36Sopenharmony_ci * @next_rx_buf_to_use: next Rx buffer to read from 11062306a36Sopenharmony_ci * @base_addr: base address of the Emaclite device 11162306a36Sopenharmony_ci * @reset_lock: lock to serialize xmit and tx_timeout execution 11262306a36Sopenharmony_ci * @deferred_skb: holds an skb (for transmission at a later time) when the 11362306a36Sopenharmony_ci * Tx buffer is not free 11462306a36Sopenharmony_ci * @phy_dev: pointer to the PHY device 11562306a36Sopenharmony_ci * @phy_node: pointer to the PHY device node 11662306a36Sopenharmony_ci * @mii_bus: pointer to the MII bus 11762306a36Sopenharmony_ci * @last_link: last link status 11862306a36Sopenharmony_ci */ 11962306a36Sopenharmony_cistruct net_local { 12062306a36Sopenharmony_ci struct net_device *ndev; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci bool tx_ping_pong; 12362306a36Sopenharmony_ci bool rx_ping_pong; 12462306a36Sopenharmony_ci u32 next_tx_buf_to_use; 12562306a36Sopenharmony_ci u32 next_rx_buf_to_use; 12662306a36Sopenharmony_ci void __iomem *base_addr; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci spinlock_t reset_lock; /* serialize xmit and tx_timeout execution */ 12962306a36Sopenharmony_ci struct sk_buff *deferred_skb; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci struct phy_device *phy_dev; 13262306a36Sopenharmony_ci struct device_node *phy_node; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci struct mii_bus *mii_bus; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci int last_link; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/*************************/ 14062306a36Sopenharmony_ci/* EmacLite driver calls */ 14162306a36Sopenharmony_ci/*************************/ 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/** 14462306a36Sopenharmony_ci * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device 14562306a36Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 14662306a36Sopenharmony_ci * 14762306a36Sopenharmony_ci * This function enables the Tx and Rx interrupts for the Emaclite device along 14862306a36Sopenharmony_ci * with the Global Interrupt Enable. 14962306a36Sopenharmony_ci */ 15062306a36Sopenharmony_cistatic void xemaclite_enable_interrupts(struct net_local *drvdata) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci u32 reg_data; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* Enable the Tx interrupts for the first Buffer */ 15562306a36Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); 15662306a36Sopenharmony_ci xemaclite_writel(reg_data | XEL_TSR_XMIT_IE_MASK, 15762306a36Sopenharmony_ci drvdata->base_addr + XEL_TSR_OFFSET); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci /* Enable the Rx interrupts for the first buffer */ 16062306a36Sopenharmony_ci xemaclite_writel(XEL_RSR_RECV_IE_MASK, drvdata->base_addr + XEL_RSR_OFFSET); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* Enable the Global Interrupt Enable */ 16362306a36Sopenharmony_ci xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); 16462306a36Sopenharmony_ci} 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/** 16762306a36Sopenharmony_ci * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device 16862306a36Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 16962306a36Sopenharmony_ci * 17062306a36Sopenharmony_ci * This function disables the Tx and Rx interrupts for the Emaclite device, 17162306a36Sopenharmony_ci * along with the Global Interrupt Enable. 17262306a36Sopenharmony_ci */ 17362306a36Sopenharmony_cistatic void xemaclite_disable_interrupts(struct net_local *drvdata) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci u32 reg_data; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Disable the Global Interrupt Enable */ 17862306a36Sopenharmony_ci xemaclite_writel(XEL_GIER_GIE_MASK, drvdata->base_addr + XEL_GIER_OFFSET); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Disable the Tx interrupts for the first buffer */ 18162306a36Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_TSR_OFFSET); 18262306a36Sopenharmony_ci xemaclite_writel(reg_data & (~XEL_TSR_XMIT_IE_MASK), 18362306a36Sopenharmony_ci drvdata->base_addr + XEL_TSR_OFFSET); 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* Disable the Rx interrupts for the first buffer */ 18662306a36Sopenharmony_ci reg_data = xemaclite_readl(drvdata->base_addr + XEL_RSR_OFFSET); 18762306a36Sopenharmony_ci xemaclite_writel(reg_data & (~XEL_RSR_RECV_IE_MASK), 18862306a36Sopenharmony_ci drvdata->base_addr + XEL_RSR_OFFSET); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci/** 19262306a36Sopenharmony_ci * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address 19362306a36Sopenharmony_ci * @src_ptr: Void pointer to the 16-bit aligned source address 19462306a36Sopenharmony_ci * @dest_ptr: Pointer to the 32-bit aligned destination address 19562306a36Sopenharmony_ci * @length: Number bytes to write from source to destination 19662306a36Sopenharmony_ci * 19762306a36Sopenharmony_ci * This function writes data from a 16-bit aligned buffer to a 32-bit aligned 19862306a36Sopenharmony_ci * address in the EmacLite device. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void xemaclite_aligned_write(const void *src_ptr, u32 *dest_ptr, 20162306a36Sopenharmony_ci unsigned int length) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci const u16 *from_u16_ptr; 20462306a36Sopenharmony_ci u32 align_buffer; 20562306a36Sopenharmony_ci u32 *to_u32_ptr; 20662306a36Sopenharmony_ci u16 *to_u16_ptr; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci to_u32_ptr = dest_ptr; 20962306a36Sopenharmony_ci from_u16_ptr = src_ptr; 21062306a36Sopenharmony_ci align_buffer = 0; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (; length > 3; length -= 4) { 21362306a36Sopenharmony_ci to_u16_ptr = (u16 *)&align_buffer; 21462306a36Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 21562306a36Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci /* This barrier resolves occasional issues seen around 21862306a36Sopenharmony_ci * cases where the data is not properly flushed out 21962306a36Sopenharmony_ci * from the processor store buffers to the destination 22062306a36Sopenharmony_ci * memory locations. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci wmb(); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Output a word */ 22562306a36Sopenharmony_ci *to_u32_ptr++ = align_buffer; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci if (length) { 22862306a36Sopenharmony_ci u8 *from_u8_ptr, *to_u8_ptr; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Set up to output the remaining data */ 23162306a36Sopenharmony_ci align_buffer = 0; 23262306a36Sopenharmony_ci to_u8_ptr = (u8 *)&align_buffer; 23362306a36Sopenharmony_ci from_u8_ptr = (u8 *)from_u16_ptr; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci /* Output the remaining data */ 23662306a36Sopenharmony_ci for (; length > 0; length--) 23762306a36Sopenharmony_ci *to_u8_ptr++ = *from_u8_ptr++; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci /* This barrier resolves occasional issues seen around 24062306a36Sopenharmony_ci * cases where the data is not properly flushed out 24162306a36Sopenharmony_ci * from the processor store buffers to the destination 24262306a36Sopenharmony_ci * memory locations. 24362306a36Sopenharmony_ci */ 24462306a36Sopenharmony_ci wmb(); 24562306a36Sopenharmony_ci *to_u32_ptr = align_buffer; 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/** 25062306a36Sopenharmony_ci * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer 25162306a36Sopenharmony_ci * @src_ptr: Pointer to the 32-bit aligned source address 25262306a36Sopenharmony_ci * @dest_ptr: Pointer to the 16-bit aligned destination address 25362306a36Sopenharmony_ci * @length: Number bytes to read from source to destination 25462306a36Sopenharmony_ci * 25562306a36Sopenharmony_ci * This function reads data from a 32-bit aligned address in the EmacLite device 25662306a36Sopenharmony_ci * to a 16-bit aligned buffer. 25762306a36Sopenharmony_ci */ 25862306a36Sopenharmony_cistatic void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, 25962306a36Sopenharmony_ci unsigned int length) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci u16 *to_u16_ptr, *from_u16_ptr; 26262306a36Sopenharmony_ci u32 *from_u32_ptr; 26362306a36Sopenharmony_ci u32 align_buffer; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci from_u32_ptr = src_ptr; 26662306a36Sopenharmony_ci to_u16_ptr = (u16 *)dest_ptr; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci for (; length > 3; length -= 4) { 26962306a36Sopenharmony_ci /* Copy each word into the temporary buffer */ 27062306a36Sopenharmony_ci align_buffer = *from_u32_ptr++; 27162306a36Sopenharmony_ci from_u16_ptr = (u16 *)&align_buffer; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Read data from source */ 27462306a36Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 27562306a36Sopenharmony_ci *to_u16_ptr++ = *from_u16_ptr++; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (length) { 27962306a36Sopenharmony_ci u8 *to_u8_ptr, *from_u8_ptr; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci /* Set up to read the remaining data */ 28262306a36Sopenharmony_ci to_u8_ptr = (u8 *)to_u16_ptr; 28362306a36Sopenharmony_ci align_buffer = *from_u32_ptr++; 28462306a36Sopenharmony_ci from_u8_ptr = (u8 *)&align_buffer; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Read the remaining data */ 28762306a36Sopenharmony_ci for (; length > 0; length--) 28862306a36Sopenharmony_ci *to_u8_ptr = *from_u8_ptr; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/** 29362306a36Sopenharmony_ci * xemaclite_send_data - Send an Ethernet frame 29462306a36Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 29562306a36Sopenharmony_ci * @data: Pointer to the data to be sent 29662306a36Sopenharmony_ci * @byte_count: Total frame size, including header 29762306a36Sopenharmony_ci * 29862306a36Sopenharmony_ci * This function checks if the Tx buffer of the Emaclite device is free to send 29962306a36Sopenharmony_ci * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it 30062306a36Sopenharmony_ci * returns an error. 30162306a36Sopenharmony_ci * 30262306a36Sopenharmony_ci * Return: 0 upon success or -1 if the buffer(s) are full. 30362306a36Sopenharmony_ci * 30462306a36Sopenharmony_ci * Note: The maximum Tx packet size can not be more than Ethernet header 30562306a36Sopenharmony_ci * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. 30662306a36Sopenharmony_ci */ 30762306a36Sopenharmony_cistatic int xemaclite_send_data(struct net_local *drvdata, u8 *data, 30862306a36Sopenharmony_ci unsigned int byte_count) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u32 reg_data; 31162306a36Sopenharmony_ci void __iomem *addr; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Determine the expected Tx buffer address */ 31462306a36Sopenharmony_ci addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci /* If the length is too large, truncate it */ 31762306a36Sopenharmony_ci if (byte_count > ETH_FRAME_LEN) 31862306a36Sopenharmony_ci byte_count = ETH_FRAME_LEN; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* Check if the expected buffer is available */ 32162306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 32262306a36Sopenharmony_ci if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | 32362306a36Sopenharmony_ci XEL_TSR_XMIT_ACTIVE_MASK)) == 0) { 32462306a36Sopenharmony_ci /* Switch to next buffer if configured */ 32562306a36Sopenharmony_ci if (drvdata->tx_ping_pong != 0) 32662306a36Sopenharmony_ci drvdata->next_tx_buf_to_use ^= XEL_BUFFER_OFFSET; 32762306a36Sopenharmony_ci } else if (drvdata->tx_ping_pong != 0) { 32862306a36Sopenharmony_ci /* If the expected buffer is full, try the other buffer, 32962306a36Sopenharmony_ci * if it is configured in HW 33062306a36Sopenharmony_ci */ 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci addr = (void __iomem __force *)((uintptr_t __force)addr ^ 33362306a36Sopenharmony_ci XEL_BUFFER_OFFSET); 33462306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if ((reg_data & (XEL_TSR_XMIT_BUSY_MASK | 33762306a36Sopenharmony_ci XEL_TSR_XMIT_ACTIVE_MASK)) != 0) 33862306a36Sopenharmony_ci return -1; /* Buffers were full, return failure */ 33962306a36Sopenharmony_ci } else { 34062306a36Sopenharmony_ci return -1; /* Buffer was full, return failure */ 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Write the frame to the buffer */ 34462306a36Sopenharmony_ci xemaclite_aligned_write(data, (u32 __force *)addr, byte_count); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci xemaclite_writel((byte_count & XEL_TPLR_LENGTH_MASK), 34762306a36Sopenharmony_ci addr + XEL_TPLR_OFFSET); 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ci /* Update the Tx Status Register to indicate that there is a 35062306a36Sopenharmony_ci * frame to send. Set the XEL_TSR_XMIT_ACTIVE_MASK flag which 35162306a36Sopenharmony_ci * is used by the interrupt handler to check whether a frame 35262306a36Sopenharmony_ci * has been transmitted 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 35562306a36Sopenharmony_ci reg_data |= (XEL_TSR_XMIT_BUSY_MASK | XEL_TSR_XMIT_ACTIVE_MASK); 35662306a36Sopenharmony_ci xemaclite_writel(reg_data, addr + XEL_TSR_OFFSET); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci/** 36262306a36Sopenharmony_ci * xemaclite_recv_data - Receive a frame 36362306a36Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 36462306a36Sopenharmony_ci * @data: Address where the data is to be received 36562306a36Sopenharmony_ci * @maxlen: Maximum supported ethernet packet length 36662306a36Sopenharmony_ci * 36762306a36Sopenharmony_ci * This function is intended to be called from the interrupt context or 36862306a36Sopenharmony_ci * with a wrapper which waits for the receive frame to be available. 36962306a36Sopenharmony_ci * 37062306a36Sopenharmony_ci * Return: Total number of bytes received 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci void __iomem *addr; 37562306a36Sopenharmony_ci u16 length, proto_type; 37662306a36Sopenharmony_ci u32 reg_data; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Determine the expected buffer address */ 37962306a36Sopenharmony_ci addr = (drvdata->base_addr + drvdata->next_rx_buf_to_use); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci /* Verify which buffer has valid data */ 38262306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if ((reg_data & XEL_RSR_RECV_DONE_MASK) == XEL_RSR_RECV_DONE_MASK) { 38562306a36Sopenharmony_ci if (drvdata->rx_ping_pong != 0) 38662306a36Sopenharmony_ci drvdata->next_rx_buf_to_use ^= XEL_BUFFER_OFFSET; 38762306a36Sopenharmony_ci } else { 38862306a36Sopenharmony_ci /* The instance is out of sync, try other buffer if other 38962306a36Sopenharmony_ci * buffer is configured, return 0 otherwise. If the instance is 39062306a36Sopenharmony_ci * out of sync, do not update the 'next_rx_buf_to_use' since it 39162306a36Sopenharmony_ci * will correct on subsequent calls 39262306a36Sopenharmony_ci */ 39362306a36Sopenharmony_ci if (drvdata->rx_ping_pong != 0) 39462306a36Sopenharmony_ci addr = (void __iomem __force *) 39562306a36Sopenharmony_ci ((uintptr_t __force)addr ^ 39662306a36Sopenharmony_ci XEL_BUFFER_OFFSET); 39762306a36Sopenharmony_ci else 39862306a36Sopenharmony_ci return 0; /* No data was available */ 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Verify that buffer has valid data */ 40162306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 40262306a36Sopenharmony_ci if ((reg_data & XEL_RSR_RECV_DONE_MASK) != 40362306a36Sopenharmony_ci XEL_RSR_RECV_DONE_MASK) 40462306a36Sopenharmony_ci return 0; /* No data was available */ 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Get the protocol type of the ethernet frame that arrived 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci proto_type = ((ntohl(xemaclite_readl(addr + XEL_HEADER_OFFSET + 41062306a36Sopenharmony_ci XEL_RXBUFF_OFFSET)) >> XEL_HEADER_SHIFT) & 41162306a36Sopenharmony_ci XEL_RPLR_LENGTH_MASK); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Check if received ethernet frame is a raw ethernet frame 41462306a36Sopenharmony_ci * or an IP packet or an ARP packet 41562306a36Sopenharmony_ci */ 41662306a36Sopenharmony_ci if (proto_type > ETH_DATA_LEN) { 41762306a36Sopenharmony_ci if (proto_type == ETH_P_IP) { 41862306a36Sopenharmony_ci length = ((ntohl(xemaclite_readl(addr + 41962306a36Sopenharmony_ci XEL_HEADER_IP_LENGTH_OFFSET + 42062306a36Sopenharmony_ci XEL_RXBUFF_OFFSET)) >> 42162306a36Sopenharmony_ci XEL_HEADER_SHIFT) & 42262306a36Sopenharmony_ci XEL_RPLR_LENGTH_MASK); 42362306a36Sopenharmony_ci length = min_t(u16, length, ETH_DATA_LEN); 42462306a36Sopenharmony_ci length += ETH_HLEN + ETH_FCS_LEN; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci } else if (proto_type == ETH_P_ARP) { 42762306a36Sopenharmony_ci length = XEL_ARP_PACKET_SIZE + ETH_HLEN + ETH_FCS_LEN; 42862306a36Sopenharmony_ci } else { 42962306a36Sopenharmony_ci /* Field contains type other than IP or ARP, use max 43062306a36Sopenharmony_ci * frame size and let user parse it 43162306a36Sopenharmony_ci */ 43262306a36Sopenharmony_ci length = ETH_FRAME_LEN + ETH_FCS_LEN; 43362306a36Sopenharmony_ci } 43462306a36Sopenharmony_ci } else { 43562306a36Sopenharmony_ci /* Use the length in the frame, plus the header and trailer */ 43662306a36Sopenharmony_ci length = proto_type + ETH_HLEN + ETH_FCS_LEN; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (WARN_ON(length > maxlen)) 44062306a36Sopenharmony_ci length = maxlen; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Read from the EmacLite device */ 44362306a36Sopenharmony_ci xemaclite_aligned_read((u32 __force *)(addr + XEL_RXBUFF_OFFSET), 44462306a36Sopenharmony_ci data, length); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci /* Acknowledge the frame */ 44762306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_RSR_OFFSET); 44862306a36Sopenharmony_ci reg_data &= ~XEL_RSR_RECV_DONE_MASK; 44962306a36Sopenharmony_ci xemaclite_writel(reg_data, addr + XEL_RSR_OFFSET); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return length; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/** 45562306a36Sopenharmony_ci * xemaclite_update_address - Update the MAC address in the device 45662306a36Sopenharmony_ci * @drvdata: Pointer to the Emaclite device private data 45762306a36Sopenharmony_ci * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) 45862306a36Sopenharmony_ci * 45962306a36Sopenharmony_ci * Tx must be idle and Rx should be idle for deterministic results. 46062306a36Sopenharmony_ci * It is recommended that this function should be called after the 46162306a36Sopenharmony_ci * initialization and before transmission of any packets from the device. 46262306a36Sopenharmony_ci * The MAC address can be programmed using any of the two transmit 46362306a36Sopenharmony_ci * buffers (if configured). 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_cistatic void xemaclite_update_address(struct net_local *drvdata, 46662306a36Sopenharmony_ci const u8 *address_ptr) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci void __iomem *addr; 46962306a36Sopenharmony_ci u32 reg_data; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Determine the expected Tx buffer address */ 47262306a36Sopenharmony_ci addr = drvdata->base_addr + drvdata->next_tx_buf_to_use; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci xemaclite_aligned_write(address_ptr, (u32 __force *)addr, ETH_ALEN); 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci xemaclite_writel(ETH_ALEN, addr + XEL_TPLR_OFFSET); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci /* Update the MAC address in the EmacLite */ 47962306a36Sopenharmony_ci reg_data = xemaclite_readl(addr + XEL_TSR_OFFSET); 48062306a36Sopenharmony_ci xemaclite_writel(reg_data | XEL_TSR_PROG_MAC_ADDR, addr + XEL_TSR_OFFSET); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci /* Wait for EmacLite to finish with the MAC address update */ 48362306a36Sopenharmony_ci while ((xemaclite_readl(addr + XEL_TSR_OFFSET) & 48462306a36Sopenharmony_ci XEL_TSR_PROG_MAC_ADDR) != 0) 48562306a36Sopenharmony_ci ; 48662306a36Sopenharmony_ci} 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * xemaclite_set_mac_address - Set the MAC address for this device 49062306a36Sopenharmony_ci * @dev: Pointer to the network device instance 49162306a36Sopenharmony_ci * @address: Void pointer to the sockaddr structure 49262306a36Sopenharmony_ci * 49362306a36Sopenharmony_ci * This function copies the HW address from the sockaddr structure to the 49462306a36Sopenharmony_ci * net_device structure and updates the address in HW. 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * Return: Error if the net device is busy or 0 if the addr is set 49762306a36Sopenharmony_ci * successfully 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_cistatic int xemaclite_set_mac_address(struct net_device *dev, void *address) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 50262306a36Sopenharmony_ci struct sockaddr *addr = address; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (netif_running(dev)) 50562306a36Sopenharmony_ci return -EBUSY; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 50862306a36Sopenharmony_ci xemaclite_update_address(lp, dev->dev_addr); 50962306a36Sopenharmony_ci return 0; 51062306a36Sopenharmony_ci} 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci/** 51362306a36Sopenharmony_ci * xemaclite_tx_timeout - Callback for Tx Timeout 51462306a36Sopenharmony_ci * @dev: Pointer to the network device 51562306a36Sopenharmony_ci * @txqueue: Unused 51662306a36Sopenharmony_ci * 51762306a36Sopenharmony_ci * This function is called when Tx time out occurs for Emaclite device. 51862306a36Sopenharmony_ci */ 51962306a36Sopenharmony_cistatic void xemaclite_tx_timeout(struct net_device *dev, unsigned int txqueue) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 52262306a36Sopenharmony_ci unsigned long flags; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci dev_err(&lp->ndev->dev, "Exceeded transmit timeout of %lu ms\n", 52562306a36Sopenharmony_ci TX_TIMEOUT * 1000UL / HZ); 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci dev->stats.tx_errors++; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Reset the device */ 53062306a36Sopenharmony_ci spin_lock_irqsave(&lp->reset_lock, flags); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci /* Shouldn't really be necessary, but shouldn't hurt */ 53362306a36Sopenharmony_ci netif_stop_queue(dev); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci xemaclite_disable_interrupts(lp); 53662306a36Sopenharmony_ci xemaclite_enable_interrupts(lp); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci if (lp->deferred_skb) { 53962306a36Sopenharmony_ci dev_kfree_skb_irq(lp->deferred_skb); 54062306a36Sopenharmony_ci lp->deferred_skb = NULL; 54162306a36Sopenharmony_ci dev->stats.tx_errors++; 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* To exclude tx timeout */ 54562306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci /* We're all ready to go. Start the queue */ 54862306a36Sopenharmony_ci netif_wake_queue(dev); 54962306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci/**********************/ 55362306a36Sopenharmony_ci/* Interrupt Handlers */ 55462306a36Sopenharmony_ci/**********************/ 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci/** 55762306a36Sopenharmony_ci * xemaclite_tx_handler - Interrupt handler for frames sent 55862306a36Sopenharmony_ci * @dev: Pointer to the network device 55962306a36Sopenharmony_ci * 56062306a36Sopenharmony_ci * This function updates the number of packets transmitted and handles the 56162306a36Sopenharmony_ci * deferred skb, if there is one. 56262306a36Sopenharmony_ci */ 56362306a36Sopenharmony_cistatic void xemaclite_tx_handler(struct net_device *dev) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci dev->stats.tx_packets++; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (!lp->deferred_skb) 57062306a36Sopenharmony_ci return; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci if (xemaclite_send_data(lp, (u8 *)lp->deferred_skb->data, 57362306a36Sopenharmony_ci lp->deferred_skb->len)) 57462306a36Sopenharmony_ci return; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci dev->stats.tx_bytes += lp->deferred_skb->len; 57762306a36Sopenharmony_ci dev_consume_skb_irq(lp->deferred_skb); 57862306a36Sopenharmony_ci lp->deferred_skb = NULL; 57962306a36Sopenharmony_ci netif_trans_update(dev); /* prevent tx timeout */ 58062306a36Sopenharmony_ci netif_wake_queue(dev); 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/** 58462306a36Sopenharmony_ci * xemaclite_rx_handler- Interrupt handler for frames received 58562306a36Sopenharmony_ci * @dev: Pointer to the network device 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * This function allocates memory for a socket buffer, fills it with data 58862306a36Sopenharmony_ci * received and hands it over to the TCP/IP stack. 58962306a36Sopenharmony_ci */ 59062306a36Sopenharmony_cistatic void xemaclite_rx_handler(struct net_device *dev) 59162306a36Sopenharmony_ci{ 59262306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 59362306a36Sopenharmony_ci struct sk_buff *skb; 59462306a36Sopenharmony_ci u32 len; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci len = ETH_FRAME_LEN + ETH_FCS_LEN; 59762306a36Sopenharmony_ci skb = netdev_alloc_skb(dev, len + NET_IP_ALIGN); 59862306a36Sopenharmony_ci if (!skb) { 59962306a36Sopenharmony_ci /* Couldn't get memory. */ 60062306a36Sopenharmony_ci dev->stats.rx_dropped++; 60162306a36Sopenharmony_ci dev_err(&lp->ndev->dev, "Could not allocate receive buffer\n"); 60262306a36Sopenharmony_ci return; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci skb_reserve(skb, NET_IP_ALIGN); 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci len = xemaclite_recv_data(lp, (u8 *)skb->data, len); 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci if (!len) { 61062306a36Sopenharmony_ci dev->stats.rx_errors++; 61162306a36Sopenharmony_ci dev_kfree_skb_irq(skb); 61262306a36Sopenharmony_ci return; 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci skb_put(skb, len); /* Tell the skb how much data we got */ 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 61862306a36Sopenharmony_ci skb_checksum_none_assert(skb); 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci dev->stats.rx_packets++; 62162306a36Sopenharmony_ci dev->stats.rx_bytes += len; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci if (!skb_defer_rx_timestamp(skb)) 62462306a36Sopenharmony_ci netif_rx(skb); /* Send the packet upstream */ 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci/** 62862306a36Sopenharmony_ci * xemaclite_interrupt - Interrupt handler for this driver 62962306a36Sopenharmony_ci * @irq: Irq of the Emaclite device 63062306a36Sopenharmony_ci * @dev_id: Void pointer to the network device instance used as callback 63162306a36Sopenharmony_ci * reference 63262306a36Sopenharmony_ci * 63362306a36Sopenharmony_ci * Return: IRQ_HANDLED 63462306a36Sopenharmony_ci * 63562306a36Sopenharmony_ci * This function handles the Tx and Rx interrupts of the EmacLite device. 63662306a36Sopenharmony_ci */ 63762306a36Sopenharmony_cistatic irqreturn_t xemaclite_interrupt(int irq, void *dev_id) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci bool tx_complete = false; 64062306a36Sopenharmony_ci struct net_device *dev = dev_id; 64162306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 64262306a36Sopenharmony_ci void __iomem *base_addr = lp->base_addr; 64362306a36Sopenharmony_ci u32 tx_status; 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci /* Check if there is Rx Data available */ 64662306a36Sopenharmony_ci if ((xemaclite_readl(base_addr + XEL_RSR_OFFSET) & 64762306a36Sopenharmony_ci XEL_RSR_RECV_DONE_MASK) || 64862306a36Sopenharmony_ci (xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_RSR_OFFSET) 64962306a36Sopenharmony_ci & XEL_RSR_RECV_DONE_MASK)) 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci xemaclite_rx_handler(dev); 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci /* Check if the Transmission for the first buffer is completed */ 65462306a36Sopenharmony_ci tx_status = xemaclite_readl(base_addr + XEL_TSR_OFFSET); 65562306a36Sopenharmony_ci if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && 65662306a36Sopenharmony_ci (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { 65762306a36Sopenharmony_ci tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; 65862306a36Sopenharmony_ci xemaclite_writel(tx_status, base_addr + XEL_TSR_OFFSET); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci tx_complete = true; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci /* Check if the Transmission for the second buffer is completed */ 66462306a36Sopenharmony_ci tx_status = xemaclite_readl(base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); 66562306a36Sopenharmony_ci if (((tx_status & XEL_TSR_XMIT_BUSY_MASK) == 0) && 66662306a36Sopenharmony_ci (tx_status & XEL_TSR_XMIT_ACTIVE_MASK) != 0) { 66762306a36Sopenharmony_ci tx_status &= ~XEL_TSR_XMIT_ACTIVE_MASK; 66862306a36Sopenharmony_ci xemaclite_writel(tx_status, base_addr + XEL_BUFFER_OFFSET + 66962306a36Sopenharmony_ci XEL_TSR_OFFSET); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci tx_complete = true; 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci /* If there was a Tx interrupt, call the Tx Handler */ 67562306a36Sopenharmony_ci if (tx_complete != 0) 67662306a36Sopenharmony_ci xemaclite_tx_handler(dev); 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci return IRQ_HANDLED; 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/**********************/ 68262306a36Sopenharmony_ci/* MDIO Bus functions */ 68362306a36Sopenharmony_ci/**********************/ 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci/** 68662306a36Sopenharmony_ci * xemaclite_mdio_wait - Wait for the MDIO to be ready to use 68762306a36Sopenharmony_ci * @lp: Pointer to the Emaclite device private data 68862306a36Sopenharmony_ci * 68962306a36Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 69062306a36Sopenharmony_ci * request. 69162306a36Sopenharmony_ci * 69262306a36Sopenharmony_ci * Return: 0 for success or ETIMEDOUT for a timeout 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic int xemaclite_mdio_wait(struct net_local *lp) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci u32 val; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* wait for the MDIO interface to not be busy or timeout 70062306a36Sopenharmony_ci * after some time. 70162306a36Sopenharmony_ci */ 70262306a36Sopenharmony_ci return readx_poll_timeout(xemaclite_readl, 70362306a36Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET, 70462306a36Sopenharmony_ci val, !(val & XEL_MDIOCTRL_MDIOSTS_MASK), 70562306a36Sopenharmony_ci 1000, 20000); 70662306a36Sopenharmony_ci} 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci/** 70962306a36Sopenharmony_ci * xemaclite_mdio_read - Read from a given MII management register 71062306a36Sopenharmony_ci * @bus: the mii_bus struct 71162306a36Sopenharmony_ci * @phy_id: the phy address 71262306a36Sopenharmony_ci * @reg: register number to read from 71362306a36Sopenharmony_ci * 71462306a36Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 71562306a36Sopenharmony_ci * request and then writes the phy address to the MDIO Address register 71662306a36Sopenharmony_ci * and reads data from MDIO Read Data register, when its available. 71762306a36Sopenharmony_ci * 71862306a36Sopenharmony_ci * Return: Value read from the MII management register 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_cistatic int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) 72162306a36Sopenharmony_ci{ 72262306a36Sopenharmony_ci struct net_local *lp = bus->priv; 72362306a36Sopenharmony_ci u32 ctrl_reg; 72462306a36Sopenharmony_ci u32 rc; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 72762306a36Sopenharmony_ci return -ETIMEDOUT; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci /* Write the PHY address, register number and set the OP bit in the 73062306a36Sopenharmony_ci * MDIO Address register. Set the Status bit in the MDIO Control 73162306a36Sopenharmony_ci * register to start a MDIO read transaction. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); 73462306a36Sopenharmony_ci xemaclite_writel(XEL_MDIOADDR_OP_MASK | 73562306a36Sopenharmony_ci ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), 73662306a36Sopenharmony_ci lp->base_addr + XEL_MDIOADDR_OFFSET); 73762306a36Sopenharmony_ci xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, 73862306a36Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 74162306a36Sopenharmony_ci return -ETIMEDOUT; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci rc = xemaclite_readl(lp->base_addr + XEL_MDIORD_OFFSET); 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci dev_dbg(&lp->ndev->dev, 74662306a36Sopenharmony_ci "%s(phy_id=%i, reg=%x) == %x\n", __func__, 74762306a36Sopenharmony_ci phy_id, reg, rc); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci return rc; 75062306a36Sopenharmony_ci} 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci/** 75362306a36Sopenharmony_ci * xemaclite_mdio_write - Write to a given MII management register 75462306a36Sopenharmony_ci * @bus: the mii_bus struct 75562306a36Sopenharmony_ci * @phy_id: the phy address 75662306a36Sopenharmony_ci * @reg: register number to write to 75762306a36Sopenharmony_ci * @val: value to write to the register number specified by reg 75862306a36Sopenharmony_ci * 75962306a36Sopenharmony_ci * This function waits till the device is ready to accept a new MDIO 76062306a36Sopenharmony_ci * request and then writes the val to the MDIO Write Data register. 76162306a36Sopenharmony_ci * 76262306a36Sopenharmony_ci * Return: 0 upon success or a negative error upon failure 76362306a36Sopenharmony_ci */ 76462306a36Sopenharmony_cistatic int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, 76562306a36Sopenharmony_ci u16 val) 76662306a36Sopenharmony_ci{ 76762306a36Sopenharmony_ci struct net_local *lp = bus->priv; 76862306a36Sopenharmony_ci u32 ctrl_reg; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci dev_dbg(&lp->ndev->dev, 77162306a36Sopenharmony_ci "%s(phy_id=%i, reg=%x, val=%x)\n", __func__, 77262306a36Sopenharmony_ci phy_id, reg, val); 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (xemaclite_mdio_wait(lp)) 77562306a36Sopenharmony_ci return -ETIMEDOUT; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci /* Write the PHY address, register number and clear the OP bit in the 77862306a36Sopenharmony_ci * MDIO Address register and then write the value into the MDIO Write 77962306a36Sopenharmony_ci * Data register. Finally, set the Status bit in the MDIO Control 78062306a36Sopenharmony_ci * register to start a MDIO write transaction. 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci ctrl_reg = xemaclite_readl(lp->base_addr + XEL_MDIOCTRL_OFFSET); 78362306a36Sopenharmony_ci xemaclite_writel(~XEL_MDIOADDR_OP_MASK & 78462306a36Sopenharmony_ci ((phy_id << XEL_MDIOADDR_PHYADR_SHIFT) | reg), 78562306a36Sopenharmony_ci lp->base_addr + XEL_MDIOADDR_OFFSET); 78662306a36Sopenharmony_ci xemaclite_writel(val, lp->base_addr + XEL_MDIOWR_OFFSET); 78762306a36Sopenharmony_ci xemaclite_writel(ctrl_reg | XEL_MDIOCTRL_MDIOSTS_MASK, 78862306a36Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return 0; 79162306a36Sopenharmony_ci} 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci/** 79462306a36Sopenharmony_ci * xemaclite_mdio_setup - Register mii_bus for the Emaclite device 79562306a36Sopenharmony_ci * @lp: Pointer to the Emaclite device private data 79662306a36Sopenharmony_ci * @dev: Pointer to OF device structure 79762306a36Sopenharmony_ci * 79862306a36Sopenharmony_ci * This function enables MDIO bus in the Emaclite device and registers a 79962306a36Sopenharmony_ci * mii_bus. 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * Return: 0 upon success or a negative error upon failure 80262306a36Sopenharmony_ci */ 80362306a36Sopenharmony_cistatic int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct mii_bus *bus; 80662306a36Sopenharmony_ci struct resource res; 80762306a36Sopenharmony_ci struct device_node *np = of_get_parent(lp->phy_node); 80862306a36Sopenharmony_ci struct device_node *npp; 80962306a36Sopenharmony_ci int rc, ret; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci /* Don't register the MDIO bus if the phy_node or its parent node 81262306a36Sopenharmony_ci * can't be found. 81362306a36Sopenharmony_ci */ 81462306a36Sopenharmony_ci if (!np) { 81562306a36Sopenharmony_ci dev_err(dev, "Failed to register mdio bus.\n"); 81662306a36Sopenharmony_ci return -ENODEV; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci npp = of_get_parent(np); 81962306a36Sopenharmony_ci ret = of_address_to_resource(npp, 0, &res); 82062306a36Sopenharmony_ci of_node_put(npp); 82162306a36Sopenharmony_ci if (ret) { 82262306a36Sopenharmony_ci dev_err(dev, "%s resource error!\n", 82362306a36Sopenharmony_ci dev->of_node->full_name); 82462306a36Sopenharmony_ci of_node_put(np); 82562306a36Sopenharmony_ci return ret; 82662306a36Sopenharmony_ci } 82762306a36Sopenharmony_ci if (lp->ndev->mem_start != res.start) { 82862306a36Sopenharmony_ci struct phy_device *phydev; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci phydev = of_phy_find_device(lp->phy_node); 83162306a36Sopenharmony_ci if (!phydev) 83262306a36Sopenharmony_ci dev_info(dev, 83362306a36Sopenharmony_ci "MDIO of the phy is not registered yet\n"); 83462306a36Sopenharmony_ci else 83562306a36Sopenharmony_ci put_device(&phydev->mdio.dev); 83662306a36Sopenharmony_ci of_node_put(np); 83762306a36Sopenharmony_ci return 0; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci /* Enable the MDIO bus by asserting the enable bit in MDIO Control 84162306a36Sopenharmony_ci * register. 84262306a36Sopenharmony_ci */ 84362306a36Sopenharmony_ci xemaclite_writel(XEL_MDIOCTRL_MDIOEN_MASK, 84462306a36Sopenharmony_ci lp->base_addr + XEL_MDIOCTRL_OFFSET); 84562306a36Sopenharmony_ci 84662306a36Sopenharmony_ci bus = mdiobus_alloc(); 84762306a36Sopenharmony_ci if (!bus) { 84862306a36Sopenharmony_ci dev_err(dev, "Failed to allocate mdiobus\n"); 84962306a36Sopenharmony_ci of_node_put(np); 85062306a36Sopenharmony_ci return -ENOMEM; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", 85462306a36Sopenharmony_ci (unsigned long long)res.start); 85562306a36Sopenharmony_ci bus->priv = lp; 85662306a36Sopenharmony_ci bus->name = "Xilinx Emaclite MDIO"; 85762306a36Sopenharmony_ci bus->read = xemaclite_mdio_read; 85862306a36Sopenharmony_ci bus->write = xemaclite_mdio_write; 85962306a36Sopenharmony_ci bus->parent = dev; 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci rc = of_mdiobus_register(bus, np); 86262306a36Sopenharmony_ci of_node_put(np); 86362306a36Sopenharmony_ci if (rc) { 86462306a36Sopenharmony_ci dev_err(dev, "Failed to register mdio bus.\n"); 86562306a36Sopenharmony_ci goto err_register; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci lp->mii_bus = bus; 86962306a36Sopenharmony_ci 87062306a36Sopenharmony_ci return 0; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_cierr_register: 87362306a36Sopenharmony_ci mdiobus_free(bus); 87462306a36Sopenharmony_ci return rc; 87562306a36Sopenharmony_ci} 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci/** 87862306a36Sopenharmony_ci * xemaclite_adjust_link - Link state callback for the Emaclite device 87962306a36Sopenharmony_ci * @ndev: pointer to net_device struct 88062306a36Sopenharmony_ci * 88162306a36Sopenharmony_ci * There's nothing in the Emaclite device to be configured when the link 88262306a36Sopenharmony_ci * state changes. We just print the status. 88362306a36Sopenharmony_ci */ 88462306a36Sopenharmony_cistatic void xemaclite_adjust_link(struct net_device *ndev) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci struct net_local *lp = netdev_priv(ndev); 88762306a36Sopenharmony_ci struct phy_device *phy = lp->phy_dev; 88862306a36Sopenharmony_ci int link_state; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci /* hash together the state values to decide if something has changed */ 89162306a36Sopenharmony_ci link_state = phy->speed | (phy->duplex << 1) | phy->link; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (lp->last_link != link_state) { 89462306a36Sopenharmony_ci lp->last_link = link_state; 89562306a36Sopenharmony_ci phy_print_status(phy); 89662306a36Sopenharmony_ci } 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci/** 90062306a36Sopenharmony_ci * xemaclite_open - Open the network device 90162306a36Sopenharmony_ci * @dev: Pointer to the network device 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * This function sets the MAC address, requests an IRQ and enables interrupts 90462306a36Sopenharmony_ci * for the Emaclite device and starts the Tx queue. 90562306a36Sopenharmony_ci * It also connects to the phy device, if MDIO is included in Emaclite device. 90662306a36Sopenharmony_ci * 90762306a36Sopenharmony_ci * Return: 0 on success. -ENODEV, if PHY cannot be connected. 90862306a36Sopenharmony_ci * Non-zero error value on failure. 90962306a36Sopenharmony_ci */ 91062306a36Sopenharmony_cistatic int xemaclite_open(struct net_device *dev) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 91362306a36Sopenharmony_ci int retval; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci /* Just to be safe, stop the device first */ 91662306a36Sopenharmony_ci xemaclite_disable_interrupts(lp); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (lp->phy_node) { 91962306a36Sopenharmony_ci lp->phy_dev = of_phy_connect(lp->ndev, lp->phy_node, 92062306a36Sopenharmony_ci xemaclite_adjust_link, 0, 92162306a36Sopenharmony_ci PHY_INTERFACE_MODE_MII); 92262306a36Sopenharmony_ci if (!lp->phy_dev) { 92362306a36Sopenharmony_ci dev_err(&lp->ndev->dev, "of_phy_connect() failed\n"); 92462306a36Sopenharmony_ci return -ENODEV; 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci /* EmacLite doesn't support giga-bit speeds */ 92862306a36Sopenharmony_ci phy_set_max_speed(lp->phy_dev, SPEED_100); 92962306a36Sopenharmony_ci phy_start(lp->phy_dev); 93062306a36Sopenharmony_ci } 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci /* Set the MAC address each time opened */ 93362306a36Sopenharmony_ci xemaclite_update_address(lp, dev->dev_addr); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci /* Grab the IRQ */ 93662306a36Sopenharmony_ci retval = request_irq(dev->irq, xemaclite_interrupt, 0, dev->name, dev); 93762306a36Sopenharmony_ci if (retval) { 93862306a36Sopenharmony_ci dev_err(&lp->ndev->dev, "Could not allocate interrupt %d\n", 93962306a36Sopenharmony_ci dev->irq); 94062306a36Sopenharmony_ci if (lp->phy_dev) 94162306a36Sopenharmony_ci phy_disconnect(lp->phy_dev); 94262306a36Sopenharmony_ci lp->phy_dev = NULL; 94362306a36Sopenharmony_ci 94462306a36Sopenharmony_ci return retval; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* Enable Interrupts */ 94862306a36Sopenharmony_ci xemaclite_enable_interrupts(lp); 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci /* We're ready to go */ 95162306a36Sopenharmony_ci netif_start_queue(dev); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci return 0; 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci/** 95762306a36Sopenharmony_ci * xemaclite_close - Close the network device 95862306a36Sopenharmony_ci * @dev: Pointer to the network device 95962306a36Sopenharmony_ci * 96062306a36Sopenharmony_ci * This function stops the Tx queue, disables interrupts and frees the IRQ for 96162306a36Sopenharmony_ci * the Emaclite device. 96262306a36Sopenharmony_ci * It also disconnects the phy device associated with the Emaclite device. 96362306a36Sopenharmony_ci * 96462306a36Sopenharmony_ci * Return: 0, always. 96562306a36Sopenharmony_ci */ 96662306a36Sopenharmony_cistatic int xemaclite_close(struct net_device *dev) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci netif_stop_queue(dev); 97162306a36Sopenharmony_ci xemaclite_disable_interrupts(lp); 97262306a36Sopenharmony_ci free_irq(dev->irq, dev); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci if (lp->phy_dev) 97562306a36Sopenharmony_ci phy_disconnect(lp->phy_dev); 97662306a36Sopenharmony_ci lp->phy_dev = NULL; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci return 0; 97962306a36Sopenharmony_ci} 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci/** 98262306a36Sopenharmony_ci * xemaclite_send - Transmit a frame 98362306a36Sopenharmony_ci * @orig_skb: Pointer to the socket buffer to be transmitted 98462306a36Sopenharmony_ci * @dev: Pointer to the network device 98562306a36Sopenharmony_ci * 98662306a36Sopenharmony_ci * This function checks if the Tx buffer of the Emaclite device is free to send 98762306a36Sopenharmony_ci * data. If so, it fills the Tx buffer with data from socket buffer data, 98862306a36Sopenharmony_ci * updates the stats and frees the socket buffer. The Tx completion is signaled 98962306a36Sopenharmony_ci * by an interrupt. If the Tx buffer isn't free, then the socket buffer is 99062306a36Sopenharmony_ci * deferred and the Tx queue is stopped so that the deferred socket buffer can 99162306a36Sopenharmony_ci * be transmitted when the Emaclite device is free to transmit data. 99262306a36Sopenharmony_ci * 99362306a36Sopenharmony_ci * Return: NETDEV_TX_OK, always. 99462306a36Sopenharmony_ci */ 99562306a36Sopenharmony_cistatic netdev_tx_t 99662306a36Sopenharmony_cixemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci struct net_local *lp = netdev_priv(dev); 99962306a36Sopenharmony_ci struct sk_buff *new_skb; 100062306a36Sopenharmony_ci unsigned int len; 100162306a36Sopenharmony_ci unsigned long flags; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci len = orig_skb->len; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci new_skb = orig_skb; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci spin_lock_irqsave(&lp->reset_lock, flags); 100862306a36Sopenharmony_ci if (xemaclite_send_data(lp, (u8 *)new_skb->data, len) != 0) { 100962306a36Sopenharmony_ci /* If the Emaclite Tx buffer is busy, stop the Tx queue and 101062306a36Sopenharmony_ci * defer the skb for transmission during the ISR, after the 101162306a36Sopenharmony_ci * current transmission is complete 101262306a36Sopenharmony_ci */ 101362306a36Sopenharmony_ci netif_stop_queue(dev); 101462306a36Sopenharmony_ci lp->deferred_skb = new_skb; 101562306a36Sopenharmony_ci /* Take the time stamp now, since we can't do this in an ISR. */ 101662306a36Sopenharmony_ci skb_tx_timestamp(new_skb); 101762306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 101862306a36Sopenharmony_ci return NETDEV_TX_OK; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci spin_unlock_irqrestore(&lp->reset_lock, flags); 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci skb_tx_timestamp(new_skb); 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_ci dev->stats.tx_bytes += len; 102562306a36Sopenharmony_ci dev_consume_skb_any(new_skb); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return NETDEV_TX_OK; 102862306a36Sopenharmony_ci} 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci/** 103162306a36Sopenharmony_ci * get_bool - Get a parameter from the OF device 103262306a36Sopenharmony_ci * @ofdev: Pointer to OF device structure 103362306a36Sopenharmony_ci * @s: Property to be retrieved 103462306a36Sopenharmony_ci * 103562306a36Sopenharmony_ci * This function looks for a property in the device node and returns the value 103662306a36Sopenharmony_ci * of the property if its found or 0 if the property is not found. 103762306a36Sopenharmony_ci * 103862306a36Sopenharmony_ci * Return: Value of the parameter if the parameter is found, or 0 otherwise 103962306a36Sopenharmony_ci */ 104062306a36Sopenharmony_cistatic bool get_bool(struct platform_device *ofdev, const char *s) 104162306a36Sopenharmony_ci{ 104262306a36Sopenharmony_ci u32 *p = (u32 *)of_get_property(ofdev->dev.of_node, s, NULL); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (!p) { 104562306a36Sopenharmony_ci dev_warn(&ofdev->dev, "Parameter %s not found, defaulting to false\n", s); 104662306a36Sopenharmony_ci return false; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci return (bool)*p; 105062306a36Sopenharmony_ci} 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci/** 105362306a36Sopenharmony_ci * xemaclite_ethtools_get_drvinfo - Get various Axi Emac Lite driver info 105462306a36Sopenharmony_ci * @ndev: Pointer to net_device structure 105562306a36Sopenharmony_ci * @ed: Pointer to ethtool_drvinfo structure 105662306a36Sopenharmony_ci * 105762306a36Sopenharmony_ci * This implements ethtool command for getting the driver information. 105862306a36Sopenharmony_ci * Issue "ethtool -i ethX" under linux prompt to execute this function. 105962306a36Sopenharmony_ci */ 106062306a36Sopenharmony_cistatic void xemaclite_ethtools_get_drvinfo(struct net_device *ndev, 106162306a36Sopenharmony_ci struct ethtool_drvinfo *ed) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci strscpy(ed->driver, DRIVER_NAME, sizeof(ed->driver)); 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic const struct ethtool_ops xemaclite_ethtool_ops = { 106762306a36Sopenharmony_ci .get_drvinfo = xemaclite_ethtools_get_drvinfo, 106862306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 106962306a36Sopenharmony_ci .get_link_ksettings = phy_ethtool_get_link_ksettings, 107062306a36Sopenharmony_ci .set_link_ksettings = phy_ethtool_set_link_ksettings, 107162306a36Sopenharmony_ci}; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic const struct net_device_ops xemaclite_netdev_ops; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci/** 107662306a36Sopenharmony_ci * xemaclite_of_probe - Probe method for the Emaclite device. 107762306a36Sopenharmony_ci * @ofdev: Pointer to OF device structure 107862306a36Sopenharmony_ci * 107962306a36Sopenharmony_ci * This function probes for the Emaclite device in the device tree. 108062306a36Sopenharmony_ci * It initializes the driver data structure and the hardware, sets the MAC 108162306a36Sopenharmony_ci * address and registers the network device. 108262306a36Sopenharmony_ci * It also registers a mii_bus for the Emaclite device, if MDIO is included 108362306a36Sopenharmony_ci * in the device. 108462306a36Sopenharmony_ci * 108562306a36Sopenharmony_ci * Return: 0, if the driver is bound to the Emaclite device, or 108662306a36Sopenharmony_ci * a negative error if there is failure. 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_cistatic int xemaclite_of_probe(struct platform_device *ofdev) 108962306a36Sopenharmony_ci{ 109062306a36Sopenharmony_ci struct resource *res; 109162306a36Sopenharmony_ci struct net_device *ndev = NULL; 109262306a36Sopenharmony_ci struct net_local *lp = NULL; 109362306a36Sopenharmony_ci struct device *dev = &ofdev->dev; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci int rc = 0; 109662306a36Sopenharmony_ci 109762306a36Sopenharmony_ci dev_info(dev, "Device Tree Probing\n"); 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci /* Create an ethernet device instance */ 110062306a36Sopenharmony_ci ndev = alloc_etherdev(sizeof(struct net_local)); 110162306a36Sopenharmony_ci if (!ndev) 110262306a36Sopenharmony_ci return -ENOMEM; 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci dev_set_drvdata(dev, ndev); 110562306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, &ofdev->dev); 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci lp = netdev_priv(ndev); 110862306a36Sopenharmony_ci lp->ndev = ndev; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci /* Get IRQ for the device */ 111162306a36Sopenharmony_ci rc = platform_get_irq(ofdev, 0); 111262306a36Sopenharmony_ci if (rc < 0) 111362306a36Sopenharmony_ci goto error; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci ndev->irq = rc; 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_ci res = platform_get_resource(ofdev, IORESOURCE_MEM, 0); 111862306a36Sopenharmony_ci lp->base_addr = devm_ioremap_resource(&ofdev->dev, res); 111962306a36Sopenharmony_ci if (IS_ERR(lp->base_addr)) { 112062306a36Sopenharmony_ci rc = PTR_ERR(lp->base_addr); 112162306a36Sopenharmony_ci goto error; 112262306a36Sopenharmony_ci } 112362306a36Sopenharmony_ci 112462306a36Sopenharmony_ci ndev->mem_start = res->start; 112562306a36Sopenharmony_ci ndev->mem_end = res->end; 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci spin_lock_init(&lp->reset_lock); 112862306a36Sopenharmony_ci lp->next_tx_buf_to_use = 0x0; 112962306a36Sopenharmony_ci lp->next_rx_buf_to_use = 0x0; 113062306a36Sopenharmony_ci lp->tx_ping_pong = get_bool(ofdev, "xlnx,tx-ping-pong"); 113162306a36Sopenharmony_ci lp->rx_ping_pong = get_bool(ofdev, "xlnx,rx-ping-pong"); 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci rc = of_get_ethdev_address(ofdev->dev.of_node, ndev); 113462306a36Sopenharmony_ci if (rc) { 113562306a36Sopenharmony_ci dev_warn(dev, "No MAC address found, using random\n"); 113662306a36Sopenharmony_ci eth_hw_addr_random(ndev); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci /* Clear the Tx CSR's in case this is a restart */ 114062306a36Sopenharmony_ci xemaclite_writel(0, lp->base_addr + XEL_TSR_OFFSET); 114162306a36Sopenharmony_ci xemaclite_writel(0, lp->base_addr + XEL_BUFFER_OFFSET + XEL_TSR_OFFSET); 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci /* Set the MAC address in the EmacLite device */ 114462306a36Sopenharmony_ci xemaclite_update_address(lp, ndev->dev_addr); 114562306a36Sopenharmony_ci 114662306a36Sopenharmony_ci lp->phy_node = of_parse_phandle(ofdev->dev.of_node, "phy-handle", 0); 114762306a36Sopenharmony_ci xemaclite_mdio_setup(lp, &ofdev->dev); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci dev_info(dev, "MAC address is now %pM\n", ndev->dev_addr); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci ndev->netdev_ops = &xemaclite_netdev_ops; 115262306a36Sopenharmony_ci ndev->ethtool_ops = &xemaclite_ethtool_ops; 115362306a36Sopenharmony_ci ndev->flags &= ~IFF_MULTICAST; 115462306a36Sopenharmony_ci ndev->watchdog_timeo = TX_TIMEOUT; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci /* Finally, register the device */ 115762306a36Sopenharmony_ci rc = register_netdev(ndev); 115862306a36Sopenharmony_ci if (rc) { 115962306a36Sopenharmony_ci dev_err(dev, 116062306a36Sopenharmony_ci "Cannot register network device, aborting\n"); 116162306a36Sopenharmony_ci goto put_node; 116262306a36Sopenharmony_ci } 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci dev_info(dev, 116562306a36Sopenharmony_ci "Xilinx EmacLite at 0x%08lX mapped to 0x%p, irq=%d\n", 116662306a36Sopenharmony_ci (unsigned long __force)ndev->mem_start, lp->base_addr, ndev->irq); 116762306a36Sopenharmony_ci return 0; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ciput_node: 117062306a36Sopenharmony_ci of_node_put(lp->phy_node); 117162306a36Sopenharmony_cierror: 117262306a36Sopenharmony_ci free_netdev(ndev); 117362306a36Sopenharmony_ci return rc; 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci/** 117762306a36Sopenharmony_ci * xemaclite_of_remove - Unbind the driver from the Emaclite device. 117862306a36Sopenharmony_ci * @of_dev: Pointer to OF device structure 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * This function is called if a device is physically removed from the system or 118162306a36Sopenharmony_ci * if the driver module is being unloaded. It frees any resources allocated to 118262306a36Sopenharmony_ci * the device. 118362306a36Sopenharmony_ci * 118462306a36Sopenharmony_ci * Return: 0, always. 118562306a36Sopenharmony_ci */ 118662306a36Sopenharmony_cistatic int xemaclite_of_remove(struct platform_device *of_dev) 118762306a36Sopenharmony_ci{ 118862306a36Sopenharmony_ci struct net_device *ndev = platform_get_drvdata(of_dev); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci struct net_local *lp = netdev_priv(ndev); 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_ci /* Un-register the mii_bus, if configured */ 119362306a36Sopenharmony_ci if (lp->mii_bus) { 119462306a36Sopenharmony_ci mdiobus_unregister(lp->mii_bus); 119562306a36Sopenharmony_ci mdiobus_free(lp->mii_bus); 119662306a36Sopenharmony_ci lp->mii_bus = NULL; 119762306a36Sopenharmony_ci } 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci unregister_netdev(ndev); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci of_node_put(lp->phy_node); 120262306a36Sopenharmony_ci lp->phy_node = NULL; 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci free_netdev(ndev); 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci return 0; 120762306a36Sopenharmony_ci} 120862306a36Sopenharmony_ci 120962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 121062306a36Sopenharmony_cistatic void 121162306a36Sopenharmony_cixemaclite_poll_controller(struct net_device *ndev) 121262306a36Sopenharmony_ci{ 121362306a36Sopenharmony_ci disable_irq(ndev->irq); 121462306a36Sopenharmony_ci xemaclite_interrupt(ndev->irq, ndev); 121562306a36Sopenharmony_ci enable_irq(ndev->irq); 121662306a36Sopenharmony_ci} 121762306a36Sopenharmony_ci#endif 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci/* Ioctl MII Interface */ 122062306a36Sopenharmony_cistatic int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) 122162306a36Sopenharmony_ci{ 122262306a36Sopenharmony_ci if (!dev->phydev || !netif_running(dev)) 122362306a36Sopenharmony_ci return -EINVAL; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci switch (cmd) { 122662306a36Sopenharmony_ci case SIOCGMIIPHY: 122762306a36Sopenharmony_ci case SIOCGMIIREG: 122862306a36Sopenharmony_ci case SIOCSMIIREG: 122962306a36Sopenharmony_ci return phy_mii_ioctl(dev->phydev, rq, cmd); 123062306a36Sopenharmony_ci default: 123162306a36Sopenharmony_ci return -EOPNOTSUPP; 123262306a36Sopenharmony_ci } 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_cistatic const struct net_device_ops xemaclite_netdev_ops = { 123662306a36Sopenharmony_ci .ndo_open = xemaclite_open, 123762306a36Sopenharmony_ci .ndo_stop = xemaclite_close, 123862306a36Sopenharmony_ci .ndo_start_xmit = xemaclite_send, 123962306a36Sopenharmony_ci .ndo_set_mac_address = xemaclite_set_mac_address, 124062306a36Sopenharmony_ci .ndo_tx_timeout = xemaclite_tx_timeout, 124162306a36Sopenharmony_ci .ndo_eth_ioctl = xemaclite_ioctl, 124262306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 124362306a36Sopenharmony_ci .ndo_poll_controller = xemaclite_poll_controller, 124462306a36Sopenharmony_ci#endif 124562306a36Sopenharmony_ci}; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_ci/* Match table for OF platform binding */ 124862306a36Sopenharmony_cistatic const struct of_device_id xemaclite_of_match[] = { 124962306a36Sopenharmony_ci { .compatible = "xlnx,opb-ethernetlite-1.01.a", }, 125062306a36Sopenharmony_ci { .compatible = "xlnx,opb-ethernetlite-1.01.b", }, 125162306a36Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-1.00.a", }, 125262306a36Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-2.00.a", }, 125362306a36Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-2.01.a", }, 125462306a36Sopenharmony_ci { .compatible = "xlnx,xps-ethernetlite-3.00.a", }, 125562306a36Sopenharmony_ci { /* end of list */ }, 125662306a36Sopenharmony_ci}; 125762306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, xemaclite_of_match); 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_cistatic struct platform_driver xemaclite_of_driver = { 126062306a36Sopenharmony_ci .driver = { 126162306a36Sopenharmony_ci .name = DRIVER_NAME, 126262306a36Sopenharmony_ci .of_match_table = xemaclite_of_match, 126362306a36Sopenharmony_ci }, 126462306a36Sopenharmony_ci .probe = xemaclite_of_probe, 126562306a36Sopenharmony_ci .remove = xemaclite_of_remove, 126662306a36Sopenharmony_ci}; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cimodule_platform_driver(xemaclite_of_driver); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ciMODULE_AUTHOR("Xilinx, Inc."); 127162306a36Sopenharmony_ciMODULE_DESCRIPTION("Xilinx Ethernet MAC Lite driver"); 127262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1273