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

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
......@@ -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>;
......
......@@ -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.
......
......@@ -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 */
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment