// SPDX-License-Identifier: GPL-2.0-only /* * Xilinx Axi Ethernet device driver * * Copyright (c) 2008 Nissin Systems Co., Ltd., Yoshio Kashiwagi * Copyright (c) 2005-2008 DLA Systems, David H. Lynch Jr. <[email protected]> * Copyright (c) 2008-2009 Secret Lab Technologies Ltd. * Copyright (c) 2010 - 2011 Michal Simek <[email protected]> * Copyright (c) 2010 - 2011 PetaLogix * Copyright (c) 2019 - 2022 Calian Advanced Technologies * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. * * This is a driver for the Xilinx Axi Ethernet which is used in the Virtex6 * and Spartan6. * * TODO: * - Add Axi Fifo support. * - Factor out Axi DMA code into separate driver. * - Test and fix basic multicast filtering. * - Add support for extended multicast filtering. * - Test basic VLAN support. * - Add support for extended VLAN support. */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/etherdevice.h> #include <linux/module.h> #include <linux/netdevice.h> #include <linux/of.h> #include <linux/of_mdio.h> #include <linux/of_net.h> #include <linux/of_irq.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/skbuff.h> #include <linux/math64.h> #include <linux/phy.h> #include <linux/mii.h> #include <linux/ethtool.h> #include <linux/dmaengine.h> #include <linux/dma-mapping.h> #include <linux/dma/xilinx_dma.h> #include <linux/circ_buf.h> #include <net/netdev_queues.h> #include "xilinx_axienet.h" /* Descriptors defines for Tx and Rx DMA */ #define TX_BD_NUM_DEFAULT … #define RX_BD_NUM_DEFAULT … #define TX_BD_NUM_MIN … #define TX_BD_NUM_MAX … #define RX_BD_NUM_MAX … #define DMA_NUM_APP_WORDS … #define LEN_APP … #define RX_BUF_NUM_DEFAULT … /* Must be shorter than length of ethtool_drvinfo.driver field to fit */ #define DRIVER_NAME … #define DRIVER_DESCRIPTION … #define DRIVER_VERSION … #define AXIENET_REGS_N … static void axienet_rx_submit_desc(struct net_device *ndev); /* Match table for of_platform binding */ static const struct of_device_id axienet_of_match[] = …; MODULE_DEVICE_TABLE(of, axienet_of_match); /* Option table for setting up Axi Ethernet hardware options */ static struct axienet_option axienet_options[] = …; static struct skbuf_dma_descriptor *axienet_get_rx_desc(struct axienet_local *lp, int i) { … } static struct skbuf_dma_descriptor *axienet_get_tx_desc(struct axienet_local *lp, int i) { … } /** * axienet_dma_in32 - Memory mapped Axi DMA register read * @lp: Pointer to axienet local structure * @reg: Address offset from the base address of the Axi DMA core * * Return: The contents of the Axi DMA register * * This function returns the contents of the corresponding Axi DMA register. */ static inline u32 axienet_dma_in32(struct axienet_local *lp, off_t reg) { … } static void desc_set_phys_addr(struct axienet_local *lp, dma_addr_t addr, struct axidma_bd *desc) { … } static dma_addr_t desc_get_phys_addr(struct axienet_local *lp, struct axidma_bd *desc) { … } /** * axienet_dma_bd_release - Release buffer descriptor rings * @ndev: Pointer to the net_device structure * * This function is used to release the descriptors allocated in * axienet_dma_bd_init. axienet_dma_bd_release is called when Axi Ethernet * driver stop api is called. */ static void axienet_dma_bd_release(struct net_device *ndev) { … } /** * axienet_usec_to_timer - Calculate IRQ delay timer value * @lp: Pointer to the axienet_local structure * @coalesce_usec: Microseconds to convert into timer value */ static u32 axienet_usec_to_timer(struct axienet_local *lp, u32 coalesce_usec) { … } /** * axienet_dma_start - Set up DMA registers and start DMA operation * @lp: Pointer to the axienet_local structure */ static void axienet_dma_start(struct axienet_local *lp) { … } /** * axienet_dma_bd_init - Setup buffer descriptor rings for Axi DMA * @ndev: Pointer to the net_device structure * * Return: 0, on success -ENOMEM, on failure * * This function is called to initialize the Rx and Tx DMA descriptor * rings. This initializes the descriptors with required default values * and is called when Axi Ethernet driver reset is called. */ static int axienet_dma_bd_init(struct net_device *ndev) { … } /** * axienet_set_mac_address - Write the MAC address * @ndev: Pointer to the net_device structure * @address: 6 byte Address to be written as MAC address * * This function is called to initialize the MAC address of the Axi Ethernet * core. It writes to the UAW0 and UAW1 registers of the core. */ static void axienet_set_mac_address(struct net_device *ndev, const void *address) { … } /** * netdev_set_mac_address - Write the MAC address (from outside the driver) * @ndev: Pointer to the net_device structure * @p: 6 byte Address to be written as MAC address * * Return: 0 for all conditions. Presently, there is no failure case. * * This function is called to initialize the MAC address of the Axi Ethernet * core. It calls the core specific axienet_set_mac_address. This is the * function that goes into net_device_ops structure entry ndo_set_mac_address. */ static int netdev_set_mac_address(struct net_device *ndev, void *p) { … } /** * axienet_set_multicast_list - Prepare the multicast table * @ndev: Pointer to the net_device structure * * This function is called to initialize the multicast table during * initialization. The Axi Ethernet basic multicast support has a four-entry * multicast table which is initialized here. Additionally this function * goes into the net_device_ops structure entry ndo_set_multicast_list. This * means whenever the multicast table entries need to be updated this * function gets called. */ static void axienet_set_multicast_list(struct net_device *ndev) { … } /** * axienet_setoptions - Set an Axi Ethernet option * @ndev: Pointer to the net_device structure * @options: Option to be enabled/disabled * * The Axi Ethernet core has multiple features which can be selectively turned * on or off. The typical options could be jumbo frame option, basic VLAN * option, promiscuous mode option etc. This function is used to set or clear * these options in the Axi Ethernet hardware. This is done through * axienet_option structure . */ static void axienet_setoptions(struct net_device *ndev, u32 options) { … } static int __axienet_device_reset(struct axienet_local *lp) { … } /** * axienet_dma_stop - Stop DMA operation * @lp: Pointer to the axienet_local structure */ static void axienet_dma_stop(struct axienet_local *lp) { … } /** * axienet_device_reset - Reset and initialize the Axi Ethernet hardware. * @ndev: Pointer to the net_device structure * * This function is called to reset and initialize the Axi Ethernet core. This * is typically called during initialization. It does a reset of the Axi DMA * Rx/Tx channels and initializes the Axi DMA BDs. Since Axi DMA reset lines * are connected to Axi Ethernet reset lines, this in turn resets the Axi * Ethernet core. No separate hardware reset is done for the Axi Ethernet * core. * Returns 0 on success or a negative error number otherwise. */ static int axienet_device_reset(struct net_device *ndev) { … } /** * axienet_free_tx_chain - Clean up a series of linked TX descriptors. * @lp: Pointer to the axienet_local structure * @first_bd: Index of first descriptor to clean up * @nr_bds: Max number of descriptors to clean up * @force: Whether to clean descriptors even if not complete * @sizep: Pointer to a u32 filled with the total sum of all bytes * in all cleaned-up descriptors. Ignored if NULL. * @budget: NAPI budget (use 0 when not called from NAPI poll) * * Would either be called after a successful transmit operation, or after * there was an error when setting up the chain. * Returns the number of descriptors handled. */ static int axienet_free_tx_chain(struct axienet_local *lp, u32 first_bd, int nr_bds, bool force, u32 *sizep, int budget) { … } /** * axienet_check_tx_bd_space - Checks if a BD/group of BDs are currently busy * @lp: Pointer to the axienet_local structure * @num_frag: The number of BDs to check for * * Return: 0, on success * NETDEV_TX_BUSY, if any of the descriptors are not free * * This function is invoked before BDs are allocated and transmission starts. * This function returns 0 if a BD or group of BDs can be allocated for * transmission. If the BD or any of the BDs are not free the function * returns a busy status. */ static inline int axienet_check_tx_bd_space(struct axienet_local *lp, int num_frag) { … } /** * axienet_dma_tx_cb - DMA engine callback for TX channel. * @data: Pointer to the axienet_local structure. * @result: error reporting through dmaengine_result. * This function is called by dmaengine driver for TX channel to notify * that the transmit is done. */ static void axienet_dma_tx_cb(void *data, const struct dmaengine_result *result) { … } /** * axienet_start_xmit_dmaengine - Starts the transmission. * @skb: sk_buff pointer that contains data to be Txed. * @ndev: Pointer to net_device structure. * * Return: NETDEV_TX_OK on success or any non space errors. * NETDEV_TX_BUSY when free element in TX skb ring buffer * is not available. * * This function is invoked to initiate transmission. The * function sets the skbs, register dma callback API and submit * the dma transaction. * Additionally if checksum offloading is supported, * it populates AXI Stream Control fields with appropriate values. */ static netdev_tx_t axienet_start_xmit_dmaengine(struct sk_buff *skb, struct net_device *ndev) { … } /** * axienet_tx_poll - Invoked once a transmit is completed by the * Axi DMA Tx channel. * @napi: Pointer to NAPI structure. * @budget: Max number of TX packets to process. * * Return: Number of TX packets processed. * * This function is invoked from the NAPI processing to notify the completion * of transmit operation. It clears fields in the corresponding Tx BDs and * unmaps the corresponding buffer so that CPU can regain ownership of the * buffer. It finally invokes "netif_wake_queue" to restart transmission if * required. */ static int axienet_tx_poll(struct napi_struct *napi, int budget) { … } /** * axienet_start_xmit - Starts the transmission. * @skb: sk_buff pointer that contains data to be Txed. * @ndev: Pointer to net_device structure. * * Return: NETDEV_TX_OK, on success * NETDEV_TX_BUSY, if any of the descriptors are not free * * This function is invoked from upper layers to initiate transmission. The * function uses the next available free BDs and populates their fields to * start the transmission. Additionally if checksum offloading is supported, * it populates AXI Stream Control fields with appropriate values. */ static netdev_tx_t axienet_start_xmit(struct sk_buff *skb, struct net_device *ndev) { … } /** * axienet_dma_rx_cb - DMA engine callback for RX channel. * @data: Pointer to the skbuf_dma_descriptor structure. * @result: error reporting through dmaengine_result. * This function is called by dmaengine driver for RX channel to notify * that the packet is received. */ static void axienet_dma_rx_cb(void *data, const struct dmaengine_result *result) { … } /** * axienet_rx_poll - Triggered by RX ISR to complete the BD processing. * @napi: Pointer to NAPI structure. * @budget: Max number of RX packets to process. * * Return: Number of RX packets processed. */ static int axienet_rx_poll(struct napi_struct *napi, int budget) { … } /** * axienet_tx_irq - Tx Done Isr. * @irq: irq number * @_ndev: net_device pointer * * Return: IRQ_HANDLED if device generated a TX interrupt, IRQ_NONE otherwise. * * This is the Axi DMA Tx done Isr. It invokes NAPI polling to complete the * TX BD processing. */ static irqreturn_t axienet_tx_irq(int irq, void *_ndev) { … } /** * axienet_rx_irq - Rx Isr. * @irq: irq number * @_ndev: net_device pointer * * Return: IRQ_HANDLED if device generated a RX interrupt, IRQ_NONE otherwise. * * This is the Axi DMA Rx Isr. It invokes NAPI polling to complete the RX BD * processing. */ static irqreturn_t axienet_rx_irq(int irq, void *_ndev) { … } /** * axienet_eth_irq - Ethernet core Isr. * @irq: irq number * @_ndev: net_device pointer * * Return: IRQ_HANDLED if device generated a core interrupt, IRQ_NONE otherwise. * * Handle miscellaneous conditions indicated by Ethernet core IRQ. */ static irqreturn_t axienet_eth_irq(int irq, void *_ndev) { … } static void axienet_dma_err_handler(struct work_struct *work); /** * axienet_rx_submit_desc - Submit the rx descriptors to dmaengine. * allocate skbuff, map the scatterlist and obtain a descriptor * and then add the callback information and submit descriptor. * * @ndev: net_device pointer * */ static void axienet_rx_submit_desc(struct net_device *ndev) { … } /** * axienet_init_dmaengine - init the dmaengine code. * @ndev: Pointer to net_device structure * * Return: 0, on success. * non-zero error value on failure * * This is the dmaengine initialization code. */ static int axienet_init_dmaengine(struct net_device *ndev) { … } /** * axienet_init_legacy_dma - init the dma legacy code. * @ndev: Pointer to net_device structure * * Return: 0, on success. * non-zero error value on failure * * This is the dma initialization code. It also allocates interrupt * service routines, enables the interrupt lines and ISR handling. * */ static int axienet_init_legacy_dma(struct net_device *ndev) { … } /** * axienet_open - Driver open routine. * @ndev: Pointer to net_device structure * * Return: 0, on success. * non-zero error value on failure * * This is the driver open routine. It calls phylink_start to start the * PHY device. * It also allocates interrupt service routines, enables the interrupt lines * and ISR handling. Axi Ethernet core is reset through Axi DMA core. Buffer * descriptors are initialized. */ static int axienet_open(struct net_device *ndev) { … } /** * axienet_stop - Driver stop routine. * @ndev: Pointer to net_device structure * * Return: 0, on success. * * This is the driver stop routine. It calls phylink_disconnect to stop the PHY * device. It also removes the interrupt handlers and disables the interrupts. * The Axi DMA Tx/Rx BDs are released. */ static int axienet_stop(struct net_device *ndev) { … } /** * axienet_change_mtu - Driver change mtu routine. * @ndev: Pointer to net_device structure * @new_mtu: New mtu value to be applied * * Return: Always returns 0 (success). * * This is the change mtu driver routine. It checks if the Axi Ethernet * hardware supports jumbo frames before changing the mtu. This can be * called only when the device is not up. */ static int axienet_change_mtu(struct net_device *ndev, int new_mtu) { … } #ifdef CONFIG_NET_POLL_CONTROLLER /** * axienet_poll_controller - Axi Ethernet poll mechanism. * @ndev: Pointer to net_device structure * * This implements Rx/Tx ISR poll mechanisms. The interrupts are disabled prior * to polling the ISRs and are enabled back after the polling is done. */ static void axienet_poll_controller(struct net_device *ndev) { … } #endif static int axienet_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { … } static void axienet_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { … } static const struct net_device_ops axienet_netdev_ops = …; static const struct net_device_ops axienet_netdev_dmaengine_ops = …; /** * axienet_ethtools_get_drvinfo - Get various Axi Ethernet driver information. * @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 axienet_ethtools_get_drvinfo(struct net_device *ndev, struct ethtool_drvinfo *ed) { … } /** * axienet_ethtools_get_regs_len - Get the total regs length present in the * AxiEthernet core. * @ndev: Pointer to net_device structure * * This implements ethtool command for getting the total register length * information. * * Return: the total regs length */ static int axienet_ethtools_get_regs_len(struct net_device *ndev) { … } /** * axienet_ethtools_get_regs - Dump the contents of all registers present * in AxiEthernet core. * @ndev: Pointer to net_device structure * @regs: Pointer to ethtool_regs structure * @ret: Void pointer used to return the contents of the registers. * * This implements ethtool command for getting the Axi Ethernet register dump. * Issue "ethtool -d ethX" to execute this function. */ static void axienet_ethtools_get_regs(struct net_device *ndev, struct ethtool_regs *regs, void *ret) { … } static void axienet_ethtools_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ering, struct kernel_ethtool_ringparam *kernel_ering, struct netlink_ext_ack *extack) { … } static int axienet_ethtools_set_ringparam(struct net_device *ndev, struct ethtool_ringparam *ering, struct kernel_ethtool_ringparam *kernel_ering, struct netlink_ext_ack *extack) { … } /** * axienet_ethtools_get_pauseparam - Get the pause parameter setting for * Tx and Rx paths. * @ndev: Pointer to net_device structure * @epauseparm: Pointer to ethtool_pauseparam structure. * * This implements ethtool command for getting axi ethernet pause frame * setting. Issue "ethtool -a ethX" to execute this function. */ static void axienet_ethtools_get_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *epauseparm) { … } /** * axienet_ethtools_set_pauseparam - Set device pause parameter(flow control) * settings. * @ndev: Pointer to net_device structure * @epauseparm:Pointer to ethtool_pauseparam structure * * This implements ethtool command for enabling flow control on Rx and Tx * paths. Issue "ethtool -A ethX tx on|off" under linux prompt to execute this * function. * * Return: 0 on success, -EFAULT if device is running */ static int axienet_ethtools_set_pauseparam(struct net_device *ndev, struct ethtool_pauseparam *epauseparm) { … } /** * axienet_ethtools_get_coalesce - Get DMA interrupt coalescing count. * @ndev: Pointer to net_device structure * @ecoalesce: Pointer to ethtool_coalesce structure * @kernel_coal: ethtool CQE mode setting structure * @extack: extack for reporting error messages * * This implements ethtool command for getting the DMA interrupt coalescing * count on Tx and Rx paths. Issue "ethtool -c ethX" under linux prompt to * execute this function. * * Return: 0 always */ static int axienet_ethtools_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ecoalesce, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { … } /** * axienet_ethtools_set_coalesce - Set DMA interrupt coalescing count. * @ndev: Pointer to net_device structure * @ecoalesce: Pointer to ethtool_coalesce structure * @kernel_coal: ethtool CQE mode setting structure * @extack: extack for reporting error messages * * This implements ethtool command for setting the DMA interrupt coalescing * count on Tx and Rx paths. Issue "ethtool -C ethX rx-frames 5" under linux * prompt to execute this function. * * Return: 0, on success, Non-zero error value on failure. */ static int axienet_ethtools_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ecoalesce, struct kernel_ethtool_coalesce *kernel_coal, struct netlink_ext_ack *extack) { … } static int axienet_ethtools_get_link_ksettings(struct net_device *ndev, struct ethtool_link_ksettings *cmd) { … } static int axienet_ethtools_set_link_ksettings(struct net_device *ndev, const struct ethtool_link_ksettings *cmd) { … } static int axienet_ethtools_nway_reset(struct net_device *dev) { … } static const struct ethtool_ops axienet_ethtool_ops = …; static struct axienet_local *pcs_to_axienet_local(struct phylink_pcs *pcs) { … } static void axienet_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) { … } static void axienet_pcs_an_restart(struct phylink_pcs *pcs) { … } static int axienet_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode, phy_interface_t interface, const unsigned long *advertising, bool permit_pause_to_mac) { … } static const struct phylink_pcs_ops axienet_pcs_ops = …; static struct phylink_pcs *axienet_mac_select_pcs(struct phylink_config *config, phy_interface_t interface) { … } static void axienet_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { … } static void axienet_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { … } static void axienet_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, int speed, int duplex, bool tx_pause, bool rx_pause) { … } static const struct phylink_mac_ops axienet_phylink_ops = …; /** * axienet_dma_err_handler - Work queue task for Axi DMA Error * @work: pointer to work_struct * * Resets the Axi DMA and Axi Ethernet devices, and reconfigures the * Tx/Rx BDs. */ static void axienet_dma_err_handler(struct work_struct *work) { … } /** * axienet_probe - Axi Ethernet probe function. * @pdev: Pointer to platform device structure. * * Return: 0, on success * Non-zero error value on failure. * * This is the probe routine for Axi Ethernet driver. This is called before * any other driver routines are invoked. It allocates and sets up the Ethernet * device. Parses through device tree and populates fields of * axienet_local. It registers the Ethernet device. */ static int axienet_probe(struct platform_device *pdev) { … } static void axienet_remove(struct platform_device *pdev) { … } static void axienet_shutdown(struct platform_device *pdev) { … } static int axienet_suspend(struct device *dev) { … } static int axienet_resume(struct device *dev) { … } static DEFINE_SIMPLE_DEV_PM_OPS(axienet_pm_ops, axienet_suspend, axienet_resume); static struct platform_driver axienet_driver = …; module_platform_driver(…) …; MODULE_DESCRIPTION(…) …; MODULE_AUTHOR(…) …; MODULE_LICENSE(…) …;