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