mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 18:11:56 +00:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
96524ea4be
@ -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 = <µblaze_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 */
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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,23 +114,42 @@ 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;
|
||||
struct device_node *np1;
|
||||
|
||||
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 @@ issue:
|
||||
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 @@ issue:
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user