net: phy: add generic UniMAC MDIO bus driver
Add a generic UniMAC MDIO bus driver and its Device Tree binding, which can be used by the BCMGENET driver as-is, and the upcoming Starfighter 2 Ethernet switch MDIO bus controller. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
3e8a72d1da
Коммит
2ba1b163c9
|
@ -0,0 +1,39 @@
|
|||
* Broadcom UniMAC MDIO bus controller
|
||||
|
||||
Required properties:
|
||||
- compatible: should one from "brcm,genet-mdio-v1", "brcm,genet-mdio-v2",
|
||||
"brcm,genet-mdio-v3", "brcm,genet-mdio-v4" or "brcm,unimac-mdio"
|
||||
- reg: address and length of the regsiter set for the device, first one is the
|
||||
base register, and the second one is optional and for indirect accesses to
|
||||
larger than 16-bits MDIO transactions
|
||||
- reg-names: name(s) of the register must be "mdio" and optional "mdio_indir_rw"
|
||||
- #size-cells: must be 1
|
||||
- #address-cells: must be 0
|
||||
|
||||
Optional properties:
|
||||
- interrupts: must be one if the interrupt is shared with the Ethernet MAC or
|
||||
Ethernet switch this MDIO block is integrated from, or must be two, if there
|
||||
are two separate interrupts, first one must be "mdio done" and second must be
|
||||
for "mdio error"
|
||||
- interrupt-names: must be "mdio_done_error" when there is a share interrupt fed
|
||||
to this hardware block, or must be "mdio_done" for the first interrupt and
|
||||
"mdio_error" for the second when there are separate interrupts
|
||||
|
||||
Child nodes of this MDIO bus controller node are standard Ethernet PHY device
|
||||
nodes as described in Documentation/devicetree/bindings/net/phy.txt
|
||||
|
||||
Example:
|
||||
|
||||
mdio@403c0 {
|
||||
compatible = "brcm,unimac-mdio";
|
||||
reg = <0x403c0 0x8 0x40300 0x18>;
|
||||
reg-names = "mdio", "mdio_indir_rw";
|
||||
#size-cells = <1>;
|
||||
#address-cells = <0>;
|
||||
|
||||
...
|
||||
phy@0 {
|
||||
compatible = "ethernet-phy-ieee802.3-c22";
|
||||
reg = <0>;
|
||||
};
|
||||
};
|
|
@ -205,6 +205,14 @@ config MDIO_BUS_MUX_MMIOREG
|
|||
|
||||
Currently, only 8-bit registers are supported.
|
||||
|
||||
config MDIO_BCM_UNIMAC
|
||||
tristate "Broadcom UniMAC MDIO bus controller"
|
||||
help
|
||||
This module provides a driver for the Broadcom UniMAC MDIO busses.
|
||||
This hardware can be found in the Broadcom GENET Ethernet MAC
|
||||
controllers as well as some Broadcom Ethernet switches such as the
|
||||
Starfighter 2 switches.
|
||||
|
||||
endif # PHYLIB
|
||||
|
||||
config MICREL_KS8995MA
|
||||
|
|
|
@ -34,3 +34,4 @@ obj-$(CONFIG_MDIO_BUS_MUX_MMIOREG) += mdio-mux-mmioreg.o
|
|||
obj-$(CONFIG_MDIO_SUN4I) += mdio-sun4i.o
|
||||
obj-$(CONFIG_MDIO_MOXART) += mdio-moxart.o
|
||||
obj-$(CONFIG_AMD_XGBE_PHY) += amd-xgbe-phy.o
|
||||
obj-$(CONFIG_MDIO_BCM_UNIMAC) += mdio-bcm-unimac.o
|
||||
|
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Broadcom UniMAC MDIO bus controller driver
|
||||
*
|
||||
* Copyright (C) 2014, Broadcom Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_mdio.h>
|
||||
|
||||
#define MDIO_CMD 0x00
|
||||
#define MDIO_START_BUSY (1 << 29)
|
||||
#define MDIO_READ_FAIL (1 << 28)
|
||||
#define MDIO_RD (2 << 26)
|
||||
#define MDIO_WR (1 << 26)
|
||||
#define MDIO_PMD_SHIFT 21
|
||||
#define MDIO_PMD_MASK 0x1F
|
||||
#define MDIO_REG_SHIFT 16
|
||||
#define MDIO_REG_MASK 0x1F
|
||||
|
||||
#define MDIO_CFG 0x04
|
||||
#define MDIO_C22 (1 << 0)
|
||||
#define MDIO_C45 0
|
||||
#define MDIO_CLK_DIV_SHIFT 4
|
||||
#define MDIO_CLK_DIV_MASK 0x3F
|
||||
#define MDIO_SUPP_PREAMBLE (1 << 12)
|
||||
|
||||
struct unimac_mdio_priv {
|
||||
struct mii_bus *mii_bus;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
static inline void unimac_mdio_start(struct unimac_mdio_priv *priv)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = __raw_readl(priv->base + MDIO_CMD);
|
||||
reg |= MDIO_START_BUSY;
|
||||
__raw_writel(reg, priv->base + MDIO_CMD);
|
||||
}
|
||||
|
||||
static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv)
|
||||
{
|
||||
return __raw_readl(priv->base + MDIO_CMD) & MDIO_START_BUSY;
|
||||
}
|
||||
|
||||
static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
|
||||
{
|
||||
struct unimac_mdio_priv *priv = bus->priv;
|
||||
unsigned int timeout = 1000;
|
||||
u32 cmd;
|
||||
|
||||
/* Prepare the read operation */
|
||||
cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
|
||||
__raw_writel(cmd, priv->base + MDIO_CMD);
|
||||
|
||||
/* Start MDIO transaction */
|
||||
unimac_mdio_start(priv);
|
||||
|
||||
do {
|
||||
if (!unimac_mdio_busy(priv))
|
||||
break;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
} while (timeout--);
|
||||
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
cmd = __raw_readl(priv->base + MDIO_CMD);
|
||||
if (cmd & MDIO_READ_FAIL)
|
||||
return -EIO;
|
||||
|
||||
return cmd & 0xffff;
|
||||
}
|
||||
|
||||
static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
|
||||
int reg, u16 val)
|
||||
{
|
||||
struct unimac_mdio_priv *priv = bus->priv;
|
||||
unsigned int timeout = 1000;
|
||||
u32 cmd;
|
||||
|
||||
/* Prepare the write operation */
|
||||
cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
|
||||
(reg << MDIO_REG_SHIFT) | (0xffff & val);
|
||||
__raw_writel(cmd, priv->base + MDIO_CMD);
|
||||
|
||||
unimac_mdio_start(priv);
|
||||
|
||||
do {
|
||||
if (!unimac_mdio_busy(priv))
|
||||
break;
|
||||
|
||||
usleep_range(1000, 2000);
|
||||
} while (timeout--);
|
||||
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int unimac_mdio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct unimac_mdio_priv *priv;
|
||||
struct device_node *np;
|
||||
struct mii_bus *bus;
|
||||
struct resource *r;
|
||||
int ret;
|
||||
|
||||
np = pdev->dev.of_node;
|
||||
|
||||
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
||||
/* Just ioremap, as this MDIO block is usually integrated into an
|
||||
* Ethernet MAC controller register range
|
||||
*/
|
||||
priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
|
||||
if (!priv->base) {
|
||||
dev_err(&pdev->dev, "failed to remap register\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
priv->mii_bus = mdiobus_alloc();
|
||||
if (!priv->mii_bus)
|
||||
return -ENOMEM;
|
||||
|
||||
bus = priv->mii_bus;
|
||||
bus->priv = priv;
|
||||
bus->name = "unimac MII bus";
|
||||
bus->parent = &pdev->dev;
|
||||
bus->read = unimac_mdio_read;
|
||||
bus->write = unimac_mdio_write;
|
||||
snprintf(bus->id, MII_BUS_ID_SIZE, "%s", pdev->name);
|
||||
|
||||
bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
|
||||
if (!bus->irq) {
|
||||
ret = -ENOMEM;
|
||||
goto out_mdio_free;
|
||||
}
|
||||
|
||||
ret = of_mdiobus_register(bus, np);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "MDIO bus registration failed\n");
|
||||
goto out_mdio_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus at 0x%p\n", priv->base);
|
||||
|
||||
return 0;
|
||||
|
||||
out_mdio_irq:
|
||||
kfree(bus->irq);
|
||||
out_mdio_free:
|
||||
mdiobus_free(bus);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int unimac_mdio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
mdiobus_unregister(priv->mii_bus);
|
||||
kfree(priv->mii_bus->irq);
|
||||
mdiobus_free(priv->mii_bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id unimac_mdio_ids[] = {
|
||||
{ .compatible = "brcm,genet-mdio-v4", },
|
||||
{ .compatible = "brcm,genet-mdio-v3", },
|
||||
{ .compatible = "brcm,genet-mdio-v2", },
|
||||
{ .compatible = "brcm,genet-mdio-v1", },
|
||||
{ .compatible = "brcm,unimac-mdio", },
|
||||
};
|
||||
|
||||
static struct platform_driver unimac_mdio_driver = {
|
||||
.driver = {
|
||||
.name = "unimac-mdio",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = unimac_mdio_ids,
|
||||
},
|
||||
.probe = unimac_mdio_probe,
|
||||
.remove = unimac_mdio_remove,
|
||||
};
|
||||
module_platform_driver(unimac_mdio_driver);
|
||||
|
||||
MODULE_AUTHOR("Broadcom Corporation");
|
||||
MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:unimac-mdio");
|
Загрузка…
Ссылка в новой задаче