Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96524ea4 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'Xilinx-axienet-driver-updates'



Robert Hancock says:

====================
Xilinx axienet driver updates (v5)

This is a series of enhancements and bug fixes in order to get the mainline
version of this driver into a more generally usable state, including on
x86 or ARM platforms. It also converts the driver to use the phylink API
in order to provide support for SFP modules.

Changes since v4:
-Use reverse christmas tree variable order
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 40ae2550 f5203a3d
Loading
Loading
Loading
Loading
+25 −4
Original line number Diff line number Diff line
@@ -17,8 +17,15 @@ For more details about mdio please refer phy.txt file in the same directory.
Required properties:
- compatible	: Must be one of "xlnx,axi-ethernet-1.00.a",
		  "xlnx,axi-ethernet-1.01.a", "xlnx,axi-ethernet-2.01.a"
- reg		: Address and length of the IO space.
- interrupts	: Should be a list of two interrupt, TX and RX.
- reg		: Address and length of the IO space, as well as the address
                  and length of the AXI DMA controller IO space, unless
                  axistream-connected is specified, in which case the reg
                  attribute of the node referenced by it is used.
- interrupts	: Should be a list of 2 or 3 interrupts: TX DMA, RX DMA,
		  and optionally Ethernet core. If axistream-connected is
		  specified, the TX/RX DMA interrupts should be on that node
		  instead, and only the Ethernet core interrupt is optionally
		  specified here.
- phy-handle	: Should point to the external phy device.
		  See ethernet.txt file in the same directory.
- xlnx,rxmem	: Set to allocated memory buffer for Rx/Tx in the hardware
@@ -31,15 +38,29 @@ Optional properties:
		  1 to enable partial TX checksum offload,
		  2 to enable full TX checksum offload
- xlnx,rxcsum	: Same values as xlnx,txcsum but for RX checksum offload
- clocks	: AXI bus clock for the device. Refer to common clock bindings.
		  Used to calculate MDIO clock divisor. If not specified, it is
		  auto-detected from the CPU clock (but only on platforms where
		  this is possible). New device trees should specify this - the
		  auto detection is only for backward compatibility.
- axistream-connected: Reference to another node which contains the resources
		       for the AXI DMA controller used by this device.
		       If this is specified, the DMA-related resources from that
		       device (DMA registers and DMA TX/RX interrupts) rather
		       than this one will be used.
 - mdio		: Child node for MDIO bus. Must be defined if PHY access is
		  required through the core's MDIO interface (i.e. always,
		  unless the PHY is accessed through a different bus).

Example:
	axi_ethernet_eth: ethernet@40c00000 {
		compatible = "xlnx,axi-ethernet-1.00.a";
		device_type = "network";
		interrupt-parent = <&microblaze_0_axi_intc>;
		interrupts = <2 0>;
		interrupts = <2 0 1>;
		clocks = <&axi_clk>;
		phy-mode = "mii";
		reg = <0x40c00000 0x40000>;
		reg = <0x40c00000 0x40000 0x50c00000 0x40000>;
		xlnx,rxcsum = <0x2>;
		xlnx,rxmem = <0x800>;
		xlnx,txcsum = <0x2>;
+3 −3
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
config NET_VENDOR_XILINX
	bool "Xilinx devices"
	default y
	depends on PPC || PPC32 || MICROBLAZE || ARCH_ZYNQ || MIPS || X86 || COMPILE_TEST
	depends on PPC || PPC32 || MICROBLAZE || ARCH_ZYNQ || MIPS || X86 || ARM || COMPILE_TEST
	---help---
	  If you have a network (Ethernet) card belonging to this class, say Y.

@@ -26,8 +26,8 @@ config XILINX_EMACLITE

config XILINX_AXI_EMAC
	tristate "Xilinx 10/100/1000 AXI Ethernet support"
	depends on MICROBLAZE
	select PHYLIB
	depends on MICROBLAZE || X86 || ARM || COMPILE_TEST
	select PHYLINK
	---help---
	  This driver supports the 10/100/1000 Ethernet from Xilinx for the
	  AXI bus interface used in Xilinx Virtex FPGAs.
+22 −13
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/if_vlan.h>
#include <linux/phylink.h>

/* Packet size info */
#define XAE_HDR_SIZE			14 /* Size of Ethernet header */
@@ -83,6 +84,8 @@
#define XAXIDMA_CR_RUNSTOP_MASK	0x00000001 /* Start/stop DMA channel */
#define XAXIDMA_CR_RESET_MASK	0x00000004 /* Reset DMA engine */

#define XAXIDMA_SR_HALT_MASK	0x00000001 /* Indicates DMA channel halted */

#define XAXIDMA_BD_NDESC_OFFSET		0x00 /* Next descriptor pointer */
#define XAXIDMA_BD_BUFA_OFFSET		0x08 /* Buffer address */
#define XAXIDMA_BD_CTRL_LEN_OFFSET	0x18 /* Control/buffer length */
@@ -356,9 +359,6 @@
 * @app2:         MM2S/S2MM User Application Field 2.
 * @app3:         MM2S/S2MM User Application Field 3.
 * @app4:         MM2S/S2MM User Application Field 4.
 * @sw_id_offset: MM2S/S2MM Sw ID
 * @reserved5:    Reserved and not used
 * @reserved6:    Reserved and not used
 */
struct axidma_bd {
	u32 next;	/* Physical address of next buffer descriptor */
@@ -373,11 +373,9 @@ struct axidma_bd {
	u32 app1;	/* TX start << 16 | insert */
	u32 app2;	/* TX csum seed */
	u32 app3;
	u32 app4;
	u32 sw_id_offset;
	u32 reserved5;
	u32 reserved6;
};
	u32 app4;   /* Last field used by HW */
	struct sk_buff *skb;
} __aligned(XAXIDMA_BD_MINIMUM_ALIGNMENT);

/**
 * struct axienet_local - axienet private per device data
@@ -385,6 +383,7 @@ struct axidma_bd {
 * @dev:	Pointer to device structure
 * @phy_node:	Pointer to device node structure
 * @mii_bus:	Pointer to MII bus structure
 * @regs_start: Resource start for axienet device addresses
 * @regs:	Base address for the axienet_local device address space
 * @dma_regs:	Base address for the axidma device address space
 * @dma_err_tasklet: Tasklet structure to process Axi DMA errors
@@ -422,10 +421,17 @@ struct axienet_local {
	/* Connection to PHY device */
	struct device_node *phy_node;

	struct phylink *phylink;
	struct phylink_config phylink_config;

	/* Clock for AXI bus */
	struct clk *clk;

	/* MDIO bus data */
	struct mii_bus *mii_bus;	/* MII bus reference */

	/* IO registers, dma functions and IRQs */
	resource_size_t regs_start;
	void __iomem *regs;
	void __iomem *dma_regs;

@@ -433,17 +439,19 @@ struct axienet_local {

	int tx_irq;
	int rx_irq;
	int eth_irq;
	phy_interface_t phy_mode;

	u32 options;			/* Current options word */
	u32 last_link;
	u32 features;

	/* Buffer descriptors */
	struct axidma_bd *tx_bd_v;
	dma_addr_t tx_bd_p;
	u32 tx_bd_num;
	struct axidma_bd *rx_bd_v;
	dma_addr_t rx_bd_p;
	u32 rx_bd_num;
	u32 tx_bd_ci;
	u32 tx_bd_tail;
	u32 rx_bd_ci;
@@ -481,7 +489,7 @@ struct axienet_option {
 */
static inline u32 axienet_ior(struct axienet_local *lp, off_t offset)
{
	return in_be32(lp->regs + offset);
	return ioread32(lp->regs + offset);
}

static inline u32 axinet_ior_read_mcr(struct axienet_local *lp)
@@ -501,12 +509,13 @@ static inline u32 axinet_ior_read_mcr(struct axienet_local *lp)
static inline void axienet_iow(struct axienet_local *lp, off_t offset,
			       u32 value)
{
	out_be32((lp->regs + offset), value);
	iowrite32(value, lp->regs + offset);
}

/* Function prototypes visible in xilinx_axienet_mdio.c for other files */
int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np);
int axienet_mdio_wait_until_ready(struct axienet_local *lp);
int axienet_mdio_enable(struct axienet_local *lp);
void axienet_mdio_disable(struct axienet_local *lp);
int axienet_mdio_setup(struct axienet_local *lp);
void axienet_mdio_teardown(struct axienet_local *lp);

#endif /* XILINX_AXI_ENET_H */
+473 −204

File changed.

Preview size limit exceeded, changes collapsed.

+70 −41
Original line number Diff line number Diff line
@@ -5,9 +5,11 @@
 * Copyright (c) 2009 Secret Lab Technologies, Ltd.
 * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu>
 * Copyright (c) 2010 - 2011 PetaLogix
 * Copyright (c) 2019 SED Systems, a division of Calian Ltd.
 * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved.
 */

#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of_mdio.h>
#include <linux/jiffies.h>
@@ -16,10 +18,10 @@
#include "xilinx_axienet.h"

#define MAX_MDIO_FREQ		2500000 /* 2.5 MHz */
#define DEFAULT_CLOCK_DIVISOR	XAE_MDIO_DIV_DFT
#define DEFAULT_HOST_CLOCK	150000000 /* 150 MHz */

/* Wait till MDIO interface is ready to accept a new transaction.*/
int axienet_mdio_wait_until_ready(struct axienet_local *lp)
static int axienet_mdio_wait_until_ready(struct axienet_local *lp)
{
	u32 val;

@@ -112,24 +114,43 @@ static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg,
}

/**
 * axienet_mdio_setup - MDIO setup function
 * axienet_mdio_enable - MDIO hardware setup function
 * @lp:		Pointer to axienet local data structure.
 * @np:		Pointer to device node
 *
 * Return:	0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
 *		mdiobus_alloc (to allocate memory for mii bus structure) fails.
 * Return:	0 on success, -ETIMEDOUT on a timeout.
 *
 * Sets up the MDIO interface by initializing the MDIO clock and enabling the
 * MDIO interface in hardware. Register the MDIO interface.
 * MDIO interface in hardware.
 **/
int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
int axienet_mdio_enable(struct axienet_local *lp)
{
	int ret;
	u32 clk_div, host_clock;
	struct mii_bus *bus;
	struct resource res;

	if (lp->clk) {
		host_clock = clk_get_rate(lp->clk);
	} else {
		struct device_node *np1;

		/* Legacy fallback: detect CPU clock frequency and use as AXI
		 * bus clock frequency. This only works on certain platforms.
		 */
		np1 = of_find_node_by_name(NULL, "cpu");
		if (!np1) {
			netdev_warn(lp->ndev, "Could not find CPU device node.\n");
			host_clock = DEFAULT_HOST_CLOCK;
		} else {
			int ret = of_property_read_u32(np1, "clock-frequency",
						       &host_clock);
			if (ret) {
				netdev_warn(lp->ndev, "CPU clock-frequency property not found.\n");
				host_clock = DEFAULT_HOST_CLOCK;
			}
			of_node_put(np1);
		}
		netdev_info(lp->ndev, "Setting assumed host clock to %u\n",
			    host_clock);
	}

	/* clk_div can be calculated by deriving it from the equation:
	 * fMDIO = fHOST / ((1 + clk_div) * 2)
	 *
@@ -155,25 +176,6 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
	 * "clock-frequency" from the CPU
	 */

	np1 = of_find_node_by_name(NULL, "cpu");
	if (!np1) {
		netdev_warn(lp->ndev, "Could not find CPU device node.\n");
		netdev_warn(lp->ndev,
			    "Setting MDIO clock divisor to default %d\n",
			    DEFAULT_CLOCK_DIVISOR);
		clk_div = DEFAULT_CLOCK_DIVISOR;
		goto issue;
	}
	if (of_property_read_u32(np1, "clock-frequency", &host_clock)) {
		netdev_warn(lp->ndev, "clock-frequency property not found.\n");
		netdev_warn(lp->ndev,
			    "Setting MDIO clock divisor to default %d\n",
			    DEFAULT_CLOCK_DIVISOR);
		clk_div = DEFAULT_CLOCK_DIVISOR;
		of_node_put(np1);
		goto issue;
	}

	clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1;
	/* If there is any remainder from the division of
	 * fHOST / (MAX_MDIO_FREQ * 2), then we need to add
@@ -186,12 +188,39 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
		   "Setting MDIO clock divisor to %u/%u Hz host clock.\n",
		   clk_div, host_clock);

	of_node_put(np1);
issue:
	axienet_iow(lp, XAE_MDIO_MC_OFFSET,
		    (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK));
	axienet_iow(lp, XAE_MDIO_MC_OFFSET, clk_div | XAE_MDIO_MC_MDIOEN_MASK);

	ret = axienet_mdio_wait_until_ready(lp);
	return axienet_mdio_wait_until_ready(lp);
}

/**
 * axienet_mdio_disable - MDIO hardware disable function
 * @lp:		Pointer to axienet local data structure.
 *
 * Disable the MDIO interface in hardware.
 **/
void axienet_mdio_disable(struct axienet_local *lp)
{
	axienet_iow(lp, XAE_MDIO_MC_OFFSET, 0);
}

/**
 * axienet_mdio_setup - MDIO setup function
 * @lp:		Pointer to axienet local data structure.
 *
 * Return:	0 on success, -ETIMEDOUT on a timeout, -ENOMEM when
 *		mdiobus_alloc (to allocate memory for mii bus structure) fails.
 *
 * Sets up the MDIO interface by initializing the MDIO clock and enabling the
 * MDIO interface in hardware. Register the MDIO interface.
 **/
int axienet_mdio_setup(struct axienet_local *lp)
{
	struct device_node *mdio_node;
	struct mii_bus *bus;
	int ret;

	ret = axienet_mdio_enable(lp);
	if (ret < 0)
		return ret;

@@ -199,10 +228,8 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
	if (!bus)
		return -ENOMEM;

	np1 = of_get_parent(lp->phy_node);
	of_address_to_resource(np1, 0, &res);
	snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
		 (unsigned long long) res.start);
	snprintf(bus->id, MII_BUS_ID_SIZE, "axienet-%.8llx",
		 (unsigned long long)lp->regs_start);

	bus->priv = lp;
	bus->name = "Xilinx Axi Ethernet MDIO";
@@ -211,7 +238,9 @@ int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np)
	bus->parent = lp->dev;
	lp->mii_bus = bus;

	ret = of_mdiobus_register(bus, np1);
	mdio_node = of_get_child_by_name(lp->dev->of_node, "mdio");
	ret = of_mdiobus_register(bus, mdio_node);
	of_node_put(mdio_node);
	if (ret) {
		mdiobus_free(bus);
		lp->mii_bus = NULL;