9
0
Fork 0

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:
Sebastian Hesselbarth 2014-02-05 23:40:07 +01:00 committed by Sascha Hauer
parent 695060fb1a
commit 0066844fb1
3 changed files with 162 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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);