net: phy: add mvebu mdio bus driver
This adds a driver for the mdio bus found on Marvell Orion SoCs as part of the GBE device and Marvell Armada 370/XP as part of Neta eth device. Both drivers can share this code, so make it available independently. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Signed-off-by: Michael Grzeschik <mgr@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
This commit is contained in:
parent
695060fb1a
commit
0066844fb1
|
@ -33,6 +33,14 @@ config SMSC_PHY
|
|||
---help---
|
||||
Currently supports the LAN83C185, LAN8187 and LAN8700 PHYs
|
||||
|
||||
comment "MII bus device drivers"
|
||||
|
||||
config MDIO_MVEBU
|
||||
bool "Driver for MVEBU SoC MDIO bus"
|
||||
depends on ARCH_MVEBU
|
||||
---help---
|
||||
Driver for the MDIO bus found on Marvell EBU SoCs.
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -4,3 +4,5 @@ obj-$(CONFIG_LXT_PHY) += lxt.o
|
|||
obj-$(CONFIG_MICREL_PHY) += micrel.o
|
||||
obj-$(CONFIG_NATIONAL_PHY) += national.o
|
||||
obj-$(CONFIG_SMSC_PHY) += smsc.o
|
||||
|
||||
obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Marvell MVEBU SoC MDIO interface driver
|
||||
*
|
||||
* (C) Copyright 2014
|
||||
* Pengutronix, Michael Grzeschik <mgr@pengutronix.de>
|
||||
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
|
||||
*
|
||||
* based on mvmdio driver from Linux
|
||||
* Copyright (C) 2012 Marvell
|
||||
* Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
|
||||
*
|
||||
* Since the MDIO interface of Marvell network interfaces is shared
|
||||
* between all network interfaces, having a single driver allows to
|
||||
* handle concurrent accesses properly (you may have four Ethernet
|
||||
* ports, but they in fact share the same SMI interface to access
|
||||
* the MDIO bus). This driver is currently used by the mvneta and
|
||||
* mv643xx_eth drivers.
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <clock.h>
|
||||
#include <common.h>
|
||||
#include <driver.h>
|
||||
#include <init.h>
|
||||
#include <io.h>
|
||||
#include <of.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
#define SMI_DATA_SHIFT 0
|
||||
#define SMI_PHY_ADDR_SHIFT 16
|
||||
#define SMI_PHY_REG_SHIFT 21
|
||||
#define SMI_READ_OPERATION BIT(26)
|
||||
#define SMI_WRITE_OPERATION 0
|
||||
#define SMI_READ_VALID BIT(27)
|
||||
#define SMI_BUSY BIT(28)
|
||||
#define ERR_INT_CAUSE 0x007C
|
||||
#define ERR_INT_SMI_DONE BIT(4)
|
||||
#define ERR_INT_MASK BIT(7)
|
||||
|
||||
struct mdio_priv {
|
||||
struct mii_bus miibus;
|
||||
void __iomem *regs;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
#define SMI_POLL_TIMEOUT (10 * MSECOND)
|
||||
|
||||
static int mvebu_mdio_wait_ready(struct mdio_priv *priv)
|
||||
{
|
||||
int ret = wait_on_timeout(SMI_POLL_TIMEOUT,
|
||||
(readl(priv->regs) & SMI_BUSY) == 0);
|
||||
|
||||
if (ret)
|
||||
dev_err(&priv->miibus.dev, "timeout, SMI busy for too long\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mvebu_mdio_read(struct mii_bus *bus, int addr, int reg)
|
||||
{
|
||||
struct mdio_priv *priv = bus->priv;
|
||||
u32 smi;
|
||||
int ret;
|
||||
|
||||
ret = mvebu_mdio_wait_ready(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
smi = SMI_READ_OPERATION;
|
||||
smi |= (addr << SMI_PHY_ADDR_SHIFT) | (reg << SMI_PHY_REG_SHIFT);
|
||||
writel(smi, priv->regs);
|
||||
|
||||
ret = mvebu_mdio_wait_ready(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
smi = readl(priv->regs);
|
||||
if ((smi & SMI_READ_VALID) == 0) {
|
||||
dev_err(&bus->dev, "SMI bus read not valid\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return smi & 0xFFFF;
|
||||
}
|
||||
|
||||
static int mvebu_mdio_write(struct mii_bus *bus, int addr, int reg, u16 data)
|
||||
{
|
||||
struct mdio_priv *priv = bus->priv;
|
||||
u32 smi;
|
||||
int ret;
|
||||
|
||||
ret = mvebu_mdio_wait_ready(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
smi = SMI_WRITE_OPERATION;
|
||||
smi |= (addr << SMI_PHY_ADDR_SHIFT) | (reg << SMI_PHY_REG_SHIFT);
|
||||
smi |= data << SMI_DATA_SHIFT;
|
||||
writel(smi, priv->regs);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_mdio_probe(struct device_d *dev)
|
||||
{
|
||||
struct mdio_priv *priv;
|
||||
|
||||
priv = xzalloc(sizeof(*priv));
|
||||
dev->priv = priv;
|
||||
|
||||
priv->regs = dev_get_mem_region(dev, 0);
|
||||
if (!priv->regs)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->clk = clk_get(dev, NULL);
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_enable(priv->clk);
|
||||
|
||||
priv->miibus.priv = priv;
|
||||
priv->miibus.parent = dev;
|
||||
priv->miibus.read = mvebu_mdio_read;
|
||||
priv->miibus.write = mvebu_mdio_write;
|
||||
|
||||
return mdiobus_register(&priv->miibus);
|
||||
}
|
||||
|
||||
static void mvebu_mdio_remove(struct device_d *dev)
|
||||
{
|
||||
struct mdio_priv *priv = dev->priv;
|
||||
|
||||
mdiobus_unregister(&priv->miibus);
|
||||
|
||||
if (!IS_ERR(priv->clk))
|
||||
clk_disable(priv->clk);
|
||||
}
|
||||
|
||||
static struct of_device_id mvebu_mdio_dt_ids[] = {
|
||||
{ .compatible = "marvell,orion-mdio" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct driver_d mvebu_mdio_driver = {
|
||||
.name = "mvebu-mdio",
|
||||
.probe = mvebu_mdio_probe,
|
||||
.remove = mvebu_mdio_remove,
|
||||
.of_compatible = DRV_OF_COMPAT(mvebu_mdio_dt_ids),
|
||||
};
|
||||
device_platform_driver(mvebu_mdio_driver);
|
Loading…
Reference in New Issue