// SPDX-License-Identifier: GPL-2.0-or-later /* Xilinx EmacLite Linux driver for the Xilinx Ethernet MAC Lite device. * * This is a new flat driver which is based on the original emac_lite * driver from John Williams <[email protected]>. * * Copyright (c) 2007 - 2013 Xilinx, Inc. */ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/uaccess.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/skbuff.h> #include <linux/ethtool.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/phy.h> #include <linux/interrupt.h> #include <linux/iopoll.h> #define DRIVER_NAME … /* Register offsets for the EmacLite Core */ #define XEL_TXBUFF_OFFSET … #define XEL_MDIOADDR_OFFSET … #define XEL_MDIOWR_OFFSET … #define XEL_MDIORD_OFFSET … #define XEL_MDIOCTRL_OFFSET … #define XEL_GIER_OFFSET … #define XEL_TSR_OFFSET … #define XEL_TPLR_OFFSET … #define XEL_RXBUFF_OFFSET … #define XEL_RPLR_OFFSET … #define XEL_RSR_OFFSET … #define XEL_BUFFER_OFFSET … /* MDIO Address Register Bit Masks */ #define XEL_MDIOADDR_REGADR_MASK … #define XEL_MDIOADDR_PHYADR_MASK … #define XEL_MDIOADDR_PHYADR_SHIFT … #define XEL_MDIOADDR_OP_MASK … /* MDIO Write Data Register Bit Masks */ #define XEL_MDIOWR_WRDATA_MASK … /* MDIO Read Data Register Bit Masks */ #define XEL_MDIORD_RDDATA_MASK … /* MDIO Control Register Bit Masks */ #define XEL_MDIOCTRL_MDIOSTS_MASK … #define XEL_MDIOCTRL_MDIOEN_MASK … /* Global Interrupt Enable Register (GIER) Bit Masks */ #define XEL_GIER_GIE_MASK … /* Transmit Status Register (TSR) Bit Masks */ #define XEL_TSR_XMIT_BUSY_MASK … #define XEL_TSR_PROGRAM_MASK … #define XEL_TSR_XMIT_IE_MASK … #define XEL_TSR_XMIT_ACTIVE_MASK … /* Define for programming the MAC address into the EmacLite */ #define XEL_TSR_PROG_MAC_ADDR … /* Receive Status Register (RSR) */ #define XEL_RSR_RECV_DONE_MASK … #define XEL_RSR_RECV_IE_MASK … /* Transmit Packet Length Register (TPLR) */ #define XEL_TPLR_LENGTH_MASK … /* Receive Packet Length Register (RPLR) */ #define XEL_RPLR_LENGTH_MASK … #define XEL_HEADER_OFFSET … #define XEL_HEADER_SHIFT … /* General Ethernet Definitions */ #define XEL_ARP_PACKET_SIZE … #define XEL_HEADER_IP_LENGTH_OFFSET … #define TX_TIMEOUT … #ifdef __BIG_ENDIAN #define xemaclite_readl … #define xemaclite_writel … #else #define xemaclite_readl … #define xemaclite_writel … #endif /** * struct net_local - Our private per device data * @ndev: instance of the network device * @tx_ping_pong: indicates whether Tx Pong buffer is configured in HW * @rx_ping_pong: indicates whether Rx Pong buffer is configured in HW * @next_tx_buf_to_use: next Tx buffer to write to * @next_rx_buf_to_use: next Rx buffer to read from * @base_addr: base address of the Emaclite device * @reset_lock: lock to serialize xmit and tx_timeout execution * @deferred_skb: holds an skb (for transmission at a later time) when the * Tx buffer is not free * @phy_dev: pointer to the PHY device * @phy_node: pointer to the PHY device node * @mii_bus: pointer to the MII bus * @last_link: last link status */ struct net_local { … }; /*************************/ /* EmacLite driver calls */ /*************************/ /** * xemaclite_enable_interrupts - Enable the interrupts for the EmacLite device * @drvdata: Pointer to the Emaclite device private data * * This function enables the Tx and Rx interrupts for the Emaclite device along * with the Global Interrupt Enable. */ static void xemaclite_enable_interrupts(struct net_local *drvdata) { … } /** * xemaclite_disable_interrupts - Disable the interrupts for the EmacLite device * @drvdata: Pointer to the Emaclite device private data * * This function disables the Tx and Rx interrupts for the Emaclite device, * along with the Global Interrupt Enable. */ static void xemaclite_disable_interrupts(struct net_local *drvdata) { … } /** * xemaclite_aligned_write - Write from 16-bit aligned to 32-bit aligned address * @src_ptr: Void pointer to the 16-bit aligned source address * @dest_ptr: Pointer to the 32-bit aligned destination address * @length: Number bytes to write from source to destination * * This function writes data from a 16-bit aligned buffer to a 32-bit aligned * address in the EmacLite device. */ static void xemaclite_aligned_write(const void *src_ptr, u32 *dest_ptr, unsigned int length) { … } /** * xemaclite_aligned_read - Read from 32-bit aligned to 16-bit aligned buffer * @src_ptr: Pointer to the 32-bit aligned source address * @dest_ptr: Pointer to the 16-bit aligned destination address * @length: Number bytes to read from source to destination * * This function reads data from a 32-bit aligned address in the EmacLite device * to a 16-bit aligned buffer. */ static void xemaclite_aligned_read(u32 *src_ptr, u8 *dest_ptr, unsigned int length) { … } /** * xemaclite_send_data - Send an Ethernet frame * @drvdata: Pointer to the Emaclite device private data * @data: Pointer to the data to be sent * @byte_count: Total frame size, including header * * This function checks if the Tx buffer of the Emaclite device is free to send * data. If so, it fills the Tx buffer with data for transmission. Otherwise, it * returns an error. * * Return: 0 upon success or -1 if the buffer(s) are full. * * Note: The maximum Tx packet size can not be more than Ethernet header * (14 Bytes) + Maximum MTU (1500 bytes). This is excluding FCS. */ static int xemaclite_send_data(struct net_local *drvdata, u8 *data, unsigned int byte_count) { … } /** * xemaclite_recv_data - Receive a frame * @drvdata: Pointer to the Emaclite device private data * @data: Address where the data is to be received * @maxlen: Maximum supported ethernet packet length * * This function is intended to be called from the interrupt context or * with a wrapper which waits for the receive frame to be available. * * Return: Total number of bytes received */ static u16 xemaclite_recv_data(struct net_local *drvdata, u8 *data, int maxlen) { … } /** * xemaclite_update_address - Update the MAC address in the device * @drvdata: Pointer to the Emaclite device private data * @address_ptr:Pointer to the MAC address (MAC address is a 48-bit value) * * Tx must be idle and Rx should be idle for deterministic results. * It is recommended that this function should be called after the * initialization and before transmission of any packets from the device. * The MAC address can be programmed using any of the two transmit * buffers (if configured). */ static void xemaclite_update_address(struct net_local *drvdata, const u8 *address_ptr) { … } /** * xemaclite_set_mac_address - Set the MAC address for this device * @dev: Pointer to the network device instance * @address: Void pointer to the sockaddr structure * * This function copies the HW address from the sockaddr structure to the * net_device structure and updates the address in HW. * * Return: Error if the net device is busy or 0 if the addr is set * successfully */ static int xemaclite_set_mac_address(struct net_device *dev, void *address) { … } /** * xemaclite_tx_timeout - Callback for Tx Timeout * @dev: Pointer to the network device * @txqueue: Unused * * This function is called when Tx time out occurs for Emaclite device. */ static void xemaclite_tx_timeout(struct net_device *dev, unsigned int txqueue) { … } /**********************/ /* Interrupt Handlers */ /**********************/ /** * xemaclite_tx_handler - Interrupt handler for frames sent * @dev: Pointer to the network device * * This function updates the number of packets transmitted and handles the * deferred skb, if there is one. */ static void xemaclite_tx_handler(struct net_device *dev) { … } /** * xemaclite_rx_handler- Interrupt handler for frames received * @dev: Pointer to the network device * * This function allocates memory for a socket buffer, fills it with data * received and hands it over to the TCP/IP stack. */ static void xemaclite_rx_handler(struct net_device *dev) { … } /** * xemaclite_interrupt - Interrupt handler for this driver * @irq: Irq of the Emaclite device * @dev_id: Void pointer to the network device instance used as callback * reference * * Return: IRQ_HANDLED * * This function handles the Tx and Rx interrupts of the EmacLite device. */ static irqreturn_t xemaclite_interrupt(int irq, void *dev_id) { … } /**********************/ /* MDIO Bus functions */ /**********************/ /** * xemaclite_mdio_wait - Wait for the MDIO to be ready to use * @lp: Pointer to the Emaclite device private data * * This function waits till the device is ready to accept a new MDIO * request. * * Return: 0 for success or ETIMEDOUT for a timeout */ static int xemaclite_mdio_wait(struct net_local *lp) { … } /** * xemaclite_mdio_read - Read from a given MII management register * @bus: the mii_bus struct * @phy_id: the phy address * @reg: register number to read from * * This function waits till the device is ready to accept a new MDIO * request and then writes the phy address to the MDIO Address register * and reads data from MDIO Read Data register, when its available. * * Return: Value read from the MII management register */ static int xemaclite_mdio_read(struct mii_bus *bus, int phy_id, int reg) { … } /** * xemaclite_mdio_write - Write to a given MII management register * @bus: the mii_bus struct * @phy_id: the phy address * @reg: register number to write to * @val: value to write to the register number specified by reg * * This function waits till the device is ready to accept a new MDIO * request and then writes the val to the MDIO Write Data register. * * Return: 0 upon success or a negative error upon failure */ static int xemaclite_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) { … } /** * xemaclite_mdio_setup - Register mii_bus for the Emaclite device * @lp: Pointer to the Emaclite device private data * @dev: Pointer to OF device structure * * This function enables MDIO bus in the Emaclite device and registers a * mii_bus. * * Return: 0 upon success or a negative error upon failure */ static int xemaclite_mdio_setup(struct net_local *lp, struct device *dev) { … } /** * xemaclite_adjust_link - Link state callback for the Emaclite device * @ndev: pointer to net_device struct * * There's nothing in the Emaclite device to be configured when the link * state changes. We just print the status. */ static void xemaclite_adjust_link(struct net_device *ndev) { … } /** * xemaclite_open - Open the network device * @dev: Pointer to the network device * * This function sets the MAC address, requests an IRQ and enables interrupts * for the Emaclite device and starts the Tx queue. * It also connects to the phy device, if MDIO is included in Emaclite device. * * Return: 0 on success. -ENODEV, if PHY cannot be connected. * Non-zero error value on failure. */ static int xemaclite_open(struct net_device *dev) { … } /** * xemaclite_close - Close the network device * @dev: Pointer to the network device * * This function stops the Tx queue, disables interrupts and frees the IRQ for * the Emaclite device. * It also disconnects the phy device associated with the Emaclite device. * * Return: 0, always. */ static int xemaclite_close(struct net_device *dev) { … } /** * xemaclite_send - Transmit a frame * @orig_skb: Pointer to the socket buffer to be transmitted * @dev: Pointer to the network device * * This function checks if the Tx buffer of the Emaclite device is free to send * data. If so, it fills the Tx buffer with data from socket buffer data, * updates the stats and frees the socket buffer. The Tx completion is signaled * by an interrupt. If the Tx buffer isn't free, then the socket buffer is * deferred and the Tx queue is stopped so that the deferred socket buffer can * be transmitted when the Emaclite device is free to transmit data. * * Return: NETDEV_TX_OK, always. */ static netdev_tx_t xemaclite_send(struct sk_buff *orig_skb, struct net_device *dev) { … } /** * get_bool - Get a parameter from the OF device * @ofdev: Pointer to OF device structure * @s: Property to be retrieved * * This function looks for a property in the device node and returns the value * of the property if its found or 0 if the property is not found. * * Return: Value of the parameter if the parameter is found, or 0 otherwise */ static bool get_bool(struct platform_device *ofdev, const char *s) { … } /** * xemaclite_ethtools_get_drvinfo - Get various Axi Emac Lite driver info * @ndev: Pointer to net_device structure * @ed: Pointer to ethtool_drvinfo structure * * This implements ethtool command for getting the driver information. * Issue "ethtool -i ethX" under linux prompt to execute this function. */ static void xemaclite_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { … } static const struct ethtool_ops xemaclite_ethtool_ops = …; static const struct net_device_ops xemaclite_netdev_ops; /** * xemaclite_of_probe - Probe method for the Emaclite device. * @ofdev: Pointer to OF device structure * * This function probes for the Emaclite device in the device tree. * It initializes the driver data structure and the hardware, sets the MAC * address and registers the network device. * It also registers a mii_bus for the Emaclite device, if MDIO is included * in the device. * * Return: 0, if the driver is bound to the Emaclite device, or * a negative error if there is failure. */ static int xemaclite_of_probe(struct platform_device *ofdev) { … } /** * xemaclite_of_remove - Unbind the driver from the Emaclite device. * @of_dev: Pointer to OF device structure * * This function is called if a device is physically removed from the system or * if the driver module is being unloaded. It frees any resources allocated to * the device. */ static void xemaclite_of_remove(struct platform_device *of_dev) { … } #ifdef CONFIG_NET_POLL_CONTROLLER static void xemaclite_poll_controller(struct net_device *ndev) { … } #endif /* Ioctl MII Interface */ static int xemaclite_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { … } static const struct net_device_ops xemaclite_netdev_ops = …; /* Match table for OF platform binding */ static const struct of_device_id xemaclite_of_match[] = …; MODULE_DEVICE_TABLE(of, xemaclite_of_match); static struct platform_driver xemaclite_of_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;