Merge branch 'mv643xx_eth'
Florian Fainelli says: ==================== This patch converts the mv643xx_eth driver to use the mvmdio MDIO bus driver instead of rolling its own implementation. As a result, all users of this mv643xx_eth driver are converted to register an "orion-mdio" platform_device. The mvmdio driver is also updated to support an interrupt line which reports SMI error/completion, and to allow traditionnal platform device registration instead of just device tree. David, I think it makes sense for you to merge all of this, since we do not want the architecture files to be desynchronized from the mv643xx_eth to avoid runtime breakage. The potential for merge conflicts should be very small. ==================== Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com> Tested-by: Jason Cooper <jason@lakedaemon.net> Acked-by: Jason Cooper <jason@lakedaemon.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
9b924dbd5e
@ -9,6 +9,9 @@ Required properties:
|
||||
- compatible: "marvell,orion-mdio"
|
||||
- reg: address and length of the SMI register
|
||||
|
||||
Optional properties:
|
||||
- interrupts: interrupt line number for the SMI error/done interrupt
|
||||
|
||||
The child nodes of the MDIO driver are the individual PHY devices
|
||||
connected to this MDIO bus. They must have a "reg" property given the
|
||||
PHY address on the MDIO bus.
|
||||
|
@ -238,6 +238,7 @@ static __init void ge_complete(
|
||||
struct mv643xx_eth_shared_platform_data *orion_ge_shared_data,
|
||||
struct resource *orion_ge_resource, unsigned long irq,
|
||||
struct platform_device *orion_ge_shared,
|
||||
struct platform_device *orion_ge_mvmdio,
|
||||
struct mv643xx_eth_platform_data *eth_data,
|
||||
struct platform_device *orion_ge)
|
||||
{
|
||||
@ -247,6 +248,8 @@ static __init void ge_complete(
|
||||
orion_ge->dev.platform_data = eth_data;
|
||||
|
||||
platform_device_register(orion_ge_shared);
|
||||
if (orion_ge_mvmdio)
|
||||
platform_device_register(orion_ge_mvmdio);
|
||||
platform_device_register(orion_ge);
|
||||
}
|
||||
|
||||
@ -258,8 +261,6 @@ struct mv643xx_eth_shared_platform_data orion_ge00_shared_data;
|
||||
static struct resource orion_ge00_shared_resources[] = {
|
||||
{
|
||||
.name = "ge00 base",
|
||||
}, {
|
||||
.name = "ge00 err irq",
|
||||
},
|
||||
};
|
||||
|
||||
@ -271,6 +272,19 @@ static struct platform_device orion_ge00_shared = {
|
||||
},
|
||||
};
|
||||
|
||||
static struct resource orion_ge_mvmdio_resources[] = {
|
||||
{
|
||||
.name = "ge00 mvmdio base",
|
||||
}, {
|
||||
.name = "ge00 mvmdio err irq",
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge_mvmdio = {
|
||||
.name = "orion-mdio",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
static struct resource orion_ge00_resources[] = {
|
||||
{
|
||||
.name = "ge00 irq",
|
||||
@ -295,26 +309,25 @@ void __init orion_ge00_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned int tx_csum_limit)
|
||||
{
|
||||
fill_resources(&orion_ge00_shared, orion_ge00_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
fill_resources(&orion_ge_mvmdio, orion_ge_mvmdio_resources,
|
||||
mapbase + 0x2004, 0x84 - 1, irq_err);
|
||||
orion_ge00_shared_data.tx_csum_limit = tx_csum_limit;
|
||||
ge_complete(&orion_ge00_shared_data,
|
||||
orion_ge00_resources, irq, &orion_ge00_shared,
|
||||
&orion_ge_mvmdio,
|
||||
eth_data, &orion_ge00);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE01
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge01_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge01_shared_data;
|
||||
|
||||
static struct resource orion_ge01_shared_resources[] = {
|
||||
{
|
||||
.name = "ge01 base",
|
||||
}, {
|
||||
.name = "ge01 err irq",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge01_shared = {
|
||||
@ -349,26 +362,23 @@ void __init orion_ge01_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned int tx_csum_limit)
|
||||
{
|
||||
fill_resources(&orion_ge01_shared, orion_ge01_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
orion_ge01_shared_data.tx_csum_limit = tx_csum_limit;
|
||||
ge_complete(&orion_ge01_shared_data,
|
||||
orion_ge01_resources, irq, &orion_ge01_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge01);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE10
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge10_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge10_shared_data;
|
||||
|
||||
static struct resource orion_ge10_shared_resources[] = {
|
||||
{
|
||||
.name = "ge10 base",
|
||||
}, {
|
||||
.name = "ge10 err irq",
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
static struct platform_device orion_ge10_shared = {
|
||||
@ -402,24 +412,21 @@ void __init orion_ge10_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned long irq_err)
|
||||
{
|
||||
fill_resources(&orion_ge10_shared, orion_ge10_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
ge_complete(&orion_ge10_shared_data,
|
||||
orion_ge10_resources, irq, &orion_ge10_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge10);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
* GE11
|
||||
****************************************************************************/
|
||||
struct mv643xx_eth_shared_platform_data orion_ge11_shared_data = {
|
||||
.shared_smi = &orion_ge00_shared,
|
||||
};
|
||||
struct mv643xx_eth_shared_platform_data orion_ge11_shared_data;
|
||||
|
||||
static struct resource orion_ge11_shared_resources[] = {
|
||||
{
|
||||
.name = "ge11 base",
|
||||
}, {
|
||||
.name = "ge11 err irq",
|
||||
},
|
||||
};
|
||||
|
||||
@ -454,9 +461,10 @@ void __init orion_ge11_init(struct mv643xx_eth_platform_data *eth_data,
|
||||
unsigned long irq_err)
|
||||
{
|
||||
fill_resources(&orion_ge11_shared, orion_ge11_shared_resources,
|
||||
mapbase + 0x2000, SZ_16K - 1, irq_err);
|
||||
mapbase + 0x2000, SZ_16K - 1, NO_IRQ);
|
||||
ge_complete(&orion_ge11_shared_data,
|
||||
orion_ge11_resources, irq, &orion_ge11_shared,
|
||||
NULL,
|
||||
eth_data, &orion_ge11);
|
||||
}
|
||||
|
||||
|
@ -47,6 +47,25 @@ static struct platform_device mv643xx_eth_shared_device = {
|
||||
.resource = mv643xx_eth_shared_resources,
|
||||
};
|
||||
|
||||
/*
|
||||
* The orion mdio driver only covers shared + 0x4 up to shared + 0x84 - 1
|
||||
*/
|
||||
static struct resource mv643xx_eth_mvmdio_resources[] = {
|
||||
[0] = {
|
||||
.name = "ethernet mdio base",
|
||||
.start = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x4,
|
||||
.end = 0xf1000000 + MV643XX_ETH_SHARED_REGS + 0x83,
|
||||
.flags = IORESOURCE_MEM,
|
||||
},
|
||||
};
|
||||
|
||||
static struct platform_device mv643xx_eth_mvmdio_device = {
|
||||
.name = "orion-mdio",
|
||||
.id = -1,
|
||||
.num_resources = ARRAY_SIZE(mv643xx_eth_mvmdio_resources),
|
||||
.resource = mv643xx_eth_shared_resources,
|
||||
};
|
||||
|
||||
static struct resource mv643xx_eth_port1_resources[] = {
|
||||
[0] = {
|
||||
.name = "eth port1 irq",
|
||||
@ -82,6 +101,7 @@ static struct platform_device eth_port1_device = {
|
||||
|
||||
static struct platform_device *mv643xx_eth_pd_devs[] __initdata = {
|
||||
&mv643xx_eth_shared_device,
|
||||
&mv643xx_eth_mvmdio_device,
|
||||
ð_port1_device,
|
||||
};
|
||||
|
||||
|
@ -214,15 +214,27 @@ static struct platform_device * __init mv64x60_eth_register_shared_pdev(
|
||||
struct device_node *np, int id)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
struct resource r[1];
|
||||
struct resource r[2];
|
||||
int err;
|
||||
|
||||
err = of_address_to_resource(np, 0, &r[0]);
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
/* register an orion mdio bus driver */
|
||||
r[1].start = r[0].start + 0x4;
|
||||
r[1].end = r[0].start + 0x84 - 1;
|
||||
r[1].flags = IORESOURCE_MEM;
|
||||
|
||||
if (id == 0) {
|
||||
pdev = platform_device_register_simple("orion-mdio", -1, &r[1], 1);
|
||||
if (!pdev)
|
||||
return pdev;
|
||||
}
|
||||
|
||||
pdev = platform_device_register_simple(MV643XX_ETH_SHARED_NAME, id,
|
||||
r, 1);
|
||||
&r[0], 1);
|
||||
|
||||
return pdev;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ config MV643XX_ETH
|
||||
depends on (MV64X60 || PPC32 || PLAT_ORION) && INET
|
||||
select INET_LRO
|
||||
select PHYLIB
|
||||
select MVMDIO
|
||||
---help---
|
||||
This driver supports the gigabit ethernet MACs in the
|
||||
Marvell Discovery PPC/MIPS chipset family (MV643XX) and
|
||||
@ -38,9 +39,7 @@ config MVMDIO
|
||||
interface units of the Marvell EBU SoCs (Kirkwood, Orion5x,
|
||||
Dove, Armada 370 and Armada XP).
|
||||
|
||||
For now, this driver is only needed for the MVNETA driver
|
||||
(used on Armada 370 and XP), but it could be used in the
|
||||
future by the MV643XX_ETH driver.
|
||||
This driver is used by the MV643XX_ETH and MVNETA drivers.
|
||||
|
||||
config MVNETA
|
||||
tristate "Marvell Armada 370/XP network interface support"
|
||||
|
@ -2,8 +2,8 @@
|
||||
# Makefile for the Marvell device drivers.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
|
||||
obj-$(CONFIG_MVMDIO) += mvmdio.o
|
||||
obj-$(CONFIG_MV643XX_ETH) += mv643xx_eth.o
|
||||
obj-$(CONFIG_MVNETA) += mvneta.o
|
||||
obj-$(CONFIG_PXA168_ETH) += pxa168_eth.o
|
||||
obj-$(CONFIG_SKGE) += skge.o
|
||||
|
@ -69,14 +69,6 @@ static char mv643xx_eth_driver_version[] = "1.4";
|
||||
* Registers shared between all ports.
|
||||
*/
|
||||
#define PHY_ADDR 0x0000
|
||||
#define SMI_REG 0x0004
|
||||
#define SMI_BUSY 0x10000000
|
||||
#define SMI_READ_VALID 0x08000000
|
||||
#define SMI_OPCODE_READ 0x04000000
|
||||
#define SMI_OPCODE_WRITE 0x00000000
|
||||
#define ERR_INT_CAUSE 0x0080
|
||||
#define ERR_INT_SMI_DONE 0x00000010
|
||||
#define ERR_INT_MASK 0x0084
|
||||
#define WINDOW_BASE(w) (0x0200 + ((w) << 3))
|
||||
#define WINDOW_SIZE(w) (0x0204 + ((w) << 3))
|
||||
#define WINDOW_REMAP_HIGH(w) (0x0280 + ((w) << 2))
|
||||
@ -265,25 +257,6 @@ struct mv643xx_eth_shared_private {
|
||||
*/
|
||||
void __iomem *base;
|
||||
|
||||
/*
|
||||
* Points at the right SMI instance to use.
|
||||
*/
|
||||
struct mv643xx_eth_shared_private *smi;
|
||||
|
||||
/*
|
||||
* Provides access to local SMI interface.
|
||||
*/
|
||||
struct mii_bus *smi_bus;
|
||||
|
||||
/*
|
||||
* If we have access to the error interrupt pin (which is
|
||||
* somewhat misnamed as it not only reflects internal errors
|
||||
* but also reflects SMI completion), use that to wait for
|
||||
* SMI access completion instead of polling the SMI busy bit.
|
||||
*/
|
||||
int err_interrupt;
|
||||
wait_queue_head_t smi_busy_wait;
|
||||
|
||||
/*
|
||||
* Per-port MBUS window access register value.
|
||||
*/
|
||||
@ -1122,97 +1095,6 @@ out_write:
|
||||
wrlp(mp, PORT_SERIAL_CONTROL, pscr);
|
||||
}
|
||||
|
||||
static irqreturn_t mv643xx_eth_err_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct mv643xx_eth_shared_private *msp = dev_id;
|
||||
|
||||
if (readl(msp->base + ERR_INT_CAUSE) & ERR_INT_SMI_DONE) {
|
||||
writel(~ERR_INT_SMI_DONE, msp->base + ERR_INT_CAUSE);
|
||||
wake_up(&msp->smi_busy_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int smi_is_done(struct mv643xx_eth_shared_private *msp)
|
||||
{
|
||||
return !(readl(msp->base + SMI_REG) & SMI_BUSY);
|
||||
}
|
||||
|
||||
static int smi_wait_ready(struct mv643xx_eth_shared_private *msp)
|
||||
{
|
||||
if (msp->err_interrupt == NO_IRQ) {
|
||||
int i;
|
||||
|
||||
for (i = 0; !smi_is_done(msp); i++) {
|
||||
if (i == 10)
|
||||
return -ETIMEDOUT;
|
||||
msleep(10);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!smi_is_done(msp)) {
|
||||
wait_event_timeout(msp->smi_busy_wait, smi_is_done(msp),
|
||||
msecs_to_jiffies(100));
|
||||
if (!smi_is_done(msp))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smi_bus_read(struct mii_bus *bus, int addr, int reg)
|
||||
{
|
||||
struct mv643xx_eth_shared_private *msp = bus->priv;
|
||||
void __iomem *smi_reg = msp->base + SMI_REG;
|
||||
int ret;
|
||||
|
||||
if (smi_wait_ready(msp)) {
|
||||
pr_warn("SMI bus busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
writel(SMI_OPCODE_READ | (reg << 21) | (addr << 16), smi_reg);
|
||||
|
||||
if (smi_wait_ready(msp)) {
|
||||
pr_warn("SMI bus busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
ret = readl(smi_reg);
|
||||
if (!(ret & SMI_READ_VALID)) {
|
||||
pr_warn("SMI bus read not valid\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return ret & 0xffff;
|
||||
}
|
||||
|
||||
static int smi_bus_write(struct mii_bus *bus, int addr, int reg, u16 val)
|
||||
{
|
||||
struct mv643xx_eth_shared_private *msp = bus->priv;
|
||||
void __iomem *smi_reg = msp->base + SMI_REG;
|
||||
|
||||
if (smi_wait_ready(msp)) {
|
||||
pr_warn("SMI bus busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
writel(SMI_OPCODE_WRITE | (reg << 21) |
|
||||
(addr << 16) | (val & 0xffff), smi_reg);
|
||||
|
||||
if (smi_wait_ready(msp)) {
|
||||
pr_warn("SMI bus busy timeout\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* statistics ***************************************************************/
|
||||
static struct net_device_stats *mv643xx_eth_get_stats(struct net_device *dev)
|
||||
{
|
||||
@ -2687,47 +2569,6 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
|
||||
if (msp->base == NULL)
|
||||
goto out_free;
|
||||
|
||||
/*
|
||||
* Set up and register SMI bus.
|
||||
*/
|
||||
if (pd == NULL || pd->shared_smi == NULL) {
|
||||
msp->smi_bus = mdiobus_alloc();
|
||||
if (msp->smi_bus == NULL)
|
||||
goto out_unmap;
|
||||
|
||||
msp->smi_bus->priv = msp;
|
||||
msp->smi_bus->name = "mv643xx_eth smi";
|
||||
msp->smi_bus->read = smi_bus_read;
|
||||
msp->smi_bus->write = smi_bus_write,
|
||||
snprintf(msp->smi_bus->id, MII_BUS_ID_SIZE, "%s-%d",
|
||||
pdev->name, pdev->id);
|
||||
msp->smi_bus->parent = &pdev->dev;
|
||||
msp->smi_bus->phy_mask = 0xffffffff;
|
||||
if (mdiobus_register(msp->smi_bus) < 0)
|
||||
goto out_free_mii_bus;
|
||||
msp->smi = msp;
|
||||
} else {
|
||||
msp->smi = platform_get_drvdata(pd->shared_smi);
|
||||
}
|
||||
|
||||
msp->err_interrupt = NO_IRQ;
|
||||
init_waitqueue_head(&msp->smi_busy_wait);
|
||||
|
||||
/*
|
||||
* Check whether the error interrupt is hooked up.
|
||||
*/
|
||||
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
if (res != NULL) {
|
||||
int err;
|
||||
|
||||
err = request_irq(res->start, mv643xx_eth_err_irq,
|
||||
IRQF_SHARED, "mv643xx_eth", msp);
|
||||
if (!err) {
|
||||
writel(ERR_INT_SMI_DONE, msp->base + ERR_INT_MASK);
|
||||
msp->err_interrupt = res->start;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (Re-)program MBUS remapping windows if we are asked to.
|
||||
*/
|
||||
@ -2743,10 +2584,6 @@ static int mv643xx_eth_shared_probe(struct platform_device *pdev)
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_mii_bus:
|
||||
mdiobus_free(msp->smi_bus);
|
||||
out_unmap:
|
||||
iounmap(msp->base);
|
||||
out_free:
|
||||
kfree(msp);
|
||||
out:
|
||||
@ -2756,14 +2593,7 @@ out:
|
||||
static int mv643xx_eth_shared_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mv643xx_eth_shared_private *msp = platform_get_drvdata(pdev);
|
||||
struct mv643xx_eth_shared_platform_data *pd = pdev->dev.platform_data;
|
||||
|
||||
if (pd == NULL || pd->shared_smi == NULL) {
|
||||
mdiobus_unregister(msp->smi_bus);
|
||||
mdiobus_free(msp->smi_bus);
|
||||
}
|
||||
if (msp->err_interrupt != NO_IRQ)
|
||||
free_irq(msp->err_interrupt, msp);
|
||||
iounmap(msp->base);
|
||||
kfree(msp);
|
||||
|
||||
@ -2826,14 +2656,21 @@ static void set_params(struct mv643xx_eth_private *mp,
|
||||
mp->txq_count = pd->tx_queue_count ? : 1;
|
||||
}
|
||||
|
||||
static void mv643xx_eth_adjust_link(struct net_device *dev)
|
||||
{
|
||||
struct mv643xx_eth_private *mp = netdev_priv(dev);
|
||||
|
||||
mv643xx_adjust_pscr(mp);
|
||||
}
|
||||
|
||||
static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
|
||||
int phy_addr)
|
||||
{
|
||||
struct mii_bus *bus = mp->shared->smi->smi_bus;
|
||||
struct phy_device *phydev;
|
||||
int start;
|
||||
int num;
|
||||
int i;
|
||||
char phy_id[MII_BUS_ID_SIZE + 3];
|
||||
|
||||
if (phy_addr == MV643XX_ETH_PHY_ADDR_DEFAULT) {
|
||||
start = phy_addr_get(mp) & 0x1f;
|
||||
@ -2843,17 +2680,19 @@ static struct phy_device *phy_scan(struct mv643xx_eth_private *mp,
|
||||
num = 1;
|
||||
}
|
||||
|
||||
/* Attempt to connect to the PHY using orion-mdio */
|
||||
phydev = NULL;
|
||||
for (i = 0; i < num; i++) {
|
||||
int addr = (start + i) & 0x1f;
|
||||
|
||||
if (bus->phy_map[addr] == NULL)
|
||||
mdiobus_scan(bus, addr);
|
||||
snprintf(phy_id, sizeof(phy_id), PHY_ID_FMT,
|
||||
"orion-mdio-mii", addr);
|
||||
|
||||
if (phydev == NULL) {
|
||||
phydev = bus->phy_map[addr];
|
||||
if (phydev != NULL)
|
||||
phydev = phy_connect(mp->dev, phy_id, mv643xx_eth_adjust_link,
|
||||
PHY_INTERFACE_MODE_GMII);
|
||||
if (!IS_ERR(phydev)) {
|
||||
phy_addr_set(mp, addr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2866,8 +2705,6 @@ static void phy_init(struct mv643xx_eth_private *mp, int speed, int duplex)
|
||||
|
||||
phy_reset(mp);
|
||||
|
||||
phy_attach(mp->dev, dev_name(&phy->dev), PHY_INTERFACE_MODE_GMII);
|
||||
|
||||
if (speed == 0) {
|
||||
phy->autoneg = AUTONEG_ENABLE;
|
||||
phy->speed = 0;
|
||||
|
@ -24,10 +24,13 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of_mdio.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#define MVMDIO_SMI_DATA_SHIFT 0
|
||||
#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
|
||||
@ -36,34 +39,59 @@
|
||||
#define MVMDIO_SMI_WRITE_OPERATION 0
|
||||
#define MVMDIO_SMI_READ_VALID BIT(27)
|
||||
#define MVMDIO_SMI_BUSY BIT(28)
|
||||
#define MVMDIO_ERR_INT_CAUSE 0x007C
|
||||
#define MVMDIO_ERR_INT_SMI_DONE 0x00000010
|
||||
#define MVMDIO_ERR_INT_MASK 0x0080
|
||||
|
||||
struct orion_mdio_dev {
|
||||
struct mutex lock;
|
||||
void __iomem *smireg;
|
||||
void __iomem *regs;
|
||||
/*
|
||||
* If we have access to the error interrupt pin (which is
|
||||
* somewhat misnamed as it not only reflects internal errors
|
||||
* but also reflects SMI completion), use that to wait for
|
||||
* SMI access completion instead of polling the SMI busy bit.
|
||||
*/
|
||||
int err_interrupt;
|
||||
wait_queue_head_t smi_busy_wait;
|
||||
};
|
||||
|
||||
static int orion_mdio_smi_is_done(struct orion_mdio_dev *dev)
|
||||
{
|
||||
return !(readl(dev->regs) & MVMDIO_SMI_BUSY);
|
||||
}
|
||||
|
||||
/* Wait for the SMI unit to be ready for another operation
|
||||
*/
|
||||
static int orion_mdio_wait_ready(struct mii_bus *bus)
|
||||
{
|
||||
struct orion_mdio_dev *dev = bus->priv;
|
||||
int count;
|
||||
u32 val;
|
||||
|
||||
if (dev->err_interrupt <= 0) {
|
||||
count = 0;
|
||||
while (1) {
|
||||
val = readl(dev->smireg);
|
||||
if (!(val & MVMDIO_SMI_BUSY))
|
||||
if (orion_mdio_smi_is_done(dev))
|
||||
break;
|
||||
|
||||
if (count > 100) {
|
||||
dev_err(bus->parent, "Timeout: SMI busy for too long\n");
|
||||
dev_err(bus->parent,
|
||||
"Timeout: SMI busy for too long\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
udelay(10);
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
if (!orion_mdio_smi_is_done(dev)) {
|
||||
wait_event_timeout(dev->smi_busy_wait,
|
||||
orion_mdio_smi_is_done(dev),
|
||||
msecs_to_jiffies(100));
|
||||
if (!orion_mdio_smi_is_done(dev))
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -87,12 +115,12 @@ static int orion_mdio_read(struct mii_bus *bus, int mii_id,
|
||||
writel(((mii_id << MVMDIO_SMI_PHY_ADDR_SHIFT) |
|
||||
(regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
|
||||
MVMDIO_SMI_READ_OPERATION),
|
||||
dev->smireg);
|
||||
dev->regs);
|
||||
|
||||
/* Wait for the value to become available */
|
||||
count = 0;
|
||||
while (1) {
|
||||
val = readl(dev->smireg);
|
||||
val = readl(dev->regs);
|
||||
if (val & MVMDIO_SMI_READ_VALID)
|
||||
break;
|
||||
|
||||
@ -129,7 +157,7 @@ static int orion_mdio_write(struct mii_bus *bus, int mii_id,
|
||||
(regnum << MVMDIO_SMI_PHY_REG_SHIFT) |
|
||||
MVMDIO_SMI_WRITE_OPERATION |
|
||||
(value << MVMDIO_SMI_DATA_SHIFT)),
|
||||
dev->smireg);
|
||||
dev->regs);
|
||||
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
@ -141,13 +169,34 @@ static int orion_mdio_reset(struct mii_bus *bus)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t orion_mdio_err_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct orion_mdio_dev *dev = dev_id;
|
||||
|
||||
if (readl(dev->regs + MVMDIO_ERR_INT_CAUSE) &
|
||||
MVMDIO_ERR_INT_SMI_DONE) {
|
||||
writel(~MVMDIO_ERR_INT_SMI_DONE,
|
||||
dev->regs + MVMDIO_ERR_INT_CAUSE);
|
||||
wake_up(&dev->smi_busy_wait);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int orion_mdio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct resource *r;
|
||||
struct mii_bus *bus;
|
||||
struct orion_mdio_dev *dev;
|
||||
int i, ret;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!r) {
|
||||
dev_err(&pdev->dev, "No SMI register address given\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
bus = mdiobus_alloc_size(sizeof(struct orion_mdio_dev));
|
||||
if (!bus) {
|
||||
dev_err(&pdev->dev, "Cannot allocate MDIO bus\n");
|
||||
@ -172,33 +221,54 @@ static int orion_mdio_probe(struct platform_device *pdev)
|
||||
bus->irq[i] = PHY_POLL;
|
||||
|
||||
dev = bus->priv;
|
||||
dev->smireg = of_iomap(pdev->dev.of_node, 0);
|
||||
if (!dev->smireg) {
|
||||
dev_err(&pdev->dev, "No SMI register address given in DT\n");
|
||||
kfree(bus->irq);
|
||||
mdiobus_free(bus);
|
||||
return -ENODEV;
|
||||
dev->regs = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (!dev->regs) {
|
||||
dev_err(&pdev->dev, "Unable to remap SMI register\n");
|
||||
ret = -ENODEV;
|
||||
goto out_mdio;
|
||||
}
|
||||
|
||||
init_waitqueue_head(&dev->smi_busy_wait);
|
||||
|
||||
dev->err_interrupt = platform_get_irq(pdev, 0);
|
||||
if (dev->err_interrupt != -ENXIO) {
|
||||
ret = devm_request_irq(&pdev->dev, dev->err_interrupt,
|
||||
orion_mdio_err_irq,
|
||||
IRQF_SHARED, pdev->name, dev);
|
||||
if (ret)
|
||||
goto out_mdio;
|
||||
|
||||
writel(MVMDIO_ERR_INT_SMI_DONE,
|
||||
dev->regs + MVMDIO_ERR_INT_MASK);
|
||||
}
|
||||
|
||||
mutex_init(&dev->lock);
|
||||
|
||||
ret = of_mdiobus_register(bus, np);
|
||||
if (pdev->dev.of_node)
|
||||
ret = of_mdiobus_register(bus, pdev->dev.of_node);
|
||||
else
|
||||
ret = mdiobus_register(bus);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
|
||||
iounmap(dev->smireg);
|
||||
kfree(bus->irq);
|
||||
mdiobus_free(bus);
|
||||
return ret;
|
||||
goto out_mdio;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, bus);
|
||||
|
||||
return 0;
|
||||
|
||||
out_mdio:
|
||||
kfree(bus->irq);
|
||||
mdiobus_free(bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int orion_mdio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mii_bus *bus = platform_get_drvdata(pdev);
|
||||
struct orion_mdio_dev *dev = bus->priv;
|
||||
|
||||
writel(0, dev->regs + MVMDIO_ERR_INT_MASK);
|
||||
mdiobus_unregister(bus);
|
||||
kfree(bus->irq);
|
||||
mdiobus_free(bus);
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
struct mv643xx_eth_shared_platform_data {
|
||||
struct mbus_dram_target_info *dram;
|
||||
struct platform_device *shared_smi;
|
||||
/*
|
||||
* Max packet size for Tx IP/Layer 4 checksum, when set to 0, default
|
||||
* limit of 9KiB will be used.
|
||||
|
Loading…
Reference in New Issue
Block a user