diff options
Diffstat (limited to 'drivers/net/phy/mdio-mux-bcm-iproc.c')
-rw-r--r-- | drivers/net/phy/mdio-mux-bcm-iproc.c | 108 |
1 files changed, 97 insertions, 11 deletions
diff --git a/drivers/net/phy/mdio-mux-bcm-iproc.c b/drivers/net/phy/mdio-mux-bcm-iproc.c index 0c5b68e7da51..c017486e9b86 100644 --- a/drivers/net/phy/mdio-mux-bcm-iproc.c +++ b/drivers/net/phy/mdio-mux-bcm-iproc.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * version 2 (GPLv2) along with this source code. */ - +#include <linux/clk.h> #include <linux/platform_device.h> #include <linux/device.h> #include <linux/of_mdio.h> @@ -22,7 +22,14 @@ #include <linux/mdio-mux.h> #include <linux/delay.h> -#define MDIO_PARAM_OFFSET 0x00 +#define MDIO_RATE_ADJ_EXT_OFFSET 0x000 +#define MDIO_RATE_ADJ_INT_OFFSET 0x004 +#define MDIO_RATE_ADJ_DIVIDENT_SHIFT 16 + +#define MDIO_SCAN_CTRL_OFFSET 0x008 +#define MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR 28 + +#define MDIO_PARAM_OFFSET 0x23c #define MDIO_PARAM_MIIM_CYCLE 29 #define MDIO_PARAM_INTERNAL_SEL 25 #define MDIO_PARAM_BUS_ID 22 @@ -30,27 +37,56 @@ #define MDIO_PARAM_PHY_ID 16 #define MDIO_PARAM_PHY_DATA 0 -#define MDIO_READ_OFFSET 0x04 +#define MDIO_READ_OFFSET 0x240 #define MDIO_READ_DATA_MASK 0xffff -#define MDIO_ADDR_OFFSET 0x08 +#define MDIO_ADDR_OFFSET 0x244 -#define MDIO_CTRL_OFFSET 0x0C +#define MDIO_CTRL_OFFSET 0x248 #define MDIO_CTRL_WRITE_OP 0x1 #define MDIO_CTRL_READ_OP 0x2 -#define MDIO_STAT_OFFSET 0x10 +#define MDIO_STAT_OFFSET 0x24c #define MDIO_STAT_DONE 1 #define BUS_MAX_ADDR 32 #define EXT_BUS_START_ADDR 16 +#define MDIO_REG_ADDR_SPACE_SIZE 0x250 + +#define MDIO_OPERATING_FREQUENCY 11000000 +#define MDIO_RATE_ADJ_DIVIDENT 1 + struct iproc_mdiomux_desc { void *mux_handle; void __iomem *base; struct device *dev; struct mii_bus *mii_bus; + struct clk *core_clk; }; +static void mdio_mux_iproc_config(struct iproc_mdiomux_desc *md) +{ + u32 divisor; + u32 val; + + /* Disable external mdio master access */ + val = readl(md->base + MDIO_SCAN_CTRL_OFFSET); + val |= BIT(MDIO_SCAN_CTRL_OVRIDE_EXT_MSTR); + writel(val, md->base + MDIO_SCAN_CTRL_OFFSET); + + if (md->core_clk) { + /* use rate adjust regs to derrive the mdio's operating + * frequency from the specified core clock + */ + divisor = clk_get_rate(md->core_clk) / MDIO_OPERATING_FREQUENCY; + divisor = divisor / (MDIO_RATE_ADJ_DIVIDENT + 1); + val = divisor; + val |= MDIO_RATE_ADJ_DIVIDENT << MDIO_RATE_ADJ_DIVIDENT_SHIFT; + writel(val, md->base + MDIO_RATE_ADJ_EXT_OFFSET); + writel(val, md->base + MDIO_RATE_ADJ_INT_OFFSET); + } +} + static int iproc_mdio_wait_for_idle(void __iomem *base, bool result) { unsigned int timeout = 1000; /* loop for 1s */ @@ -169,18 +205,39 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev) md->dev = &pdev->dev; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res->start & 0xfff) { + /* For backward compatibility in case the + * base address is specified with an offset. + */ + dev_info(&pdev->dev, "fix base address in dt-blob\n"); + res->start &= ~0xfff; + res->end = res->start + MDIO_REG_ADDR_SPACE_SIZE - 1; + } md->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(md->base)) { dev_err(&pdev->dev, "failed to ioremap register\n"); return PTR_ERR(md->base); } - md->mii_bus = mdiobus_alloc(); + md->mii_bus = devm_mdiobus_alloc(&pdev->dev); if (!md->mii_bus) { dev_err(&pdev->dev, "mdiomux bus alloc failed\n"); return -ENOMEM; } + md->core_clk = devm_clk_get(&pdev->dev, NULL); + if (md->core_clk == ERR_PTR(-ENOENT) || + md->core_clk == ERR_PTR(-EINVAL)) + md->core_clk = NULL; + else if (IS_ERR(md->core_clk)) + return PTR_ERR(md->core_clk); + + rc = clk_prepare_enable(md->core_clk); + if (rc) { + dev_err(&pdev->dev, "failed to enable core clk\n"); + return rc; + } + bus = md->mii_bus; bus->priv = md; bus->name = "iProc MDIO mux bus"; @@ -194,7 +251,7 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev) rc = mdiobus_register(bus); if (rc) { dev_err(&pdev->dev, "mdiomux registration failed\n"); - goto out; + goto out_clk; } platform_set_drvdata(pdev, md); @@ -206,13 +263,15 @@ static int mdio_mux_iproc_probe(struct platform_device *pdev) goto out_register; } + mdio_mux_iproc_config(md); + dev_info(md->dev, "iProc mdiomux registered\n"); return 0; out_register: mdiobus_unregister(bus); -out: - mdiobus_free(bus); +out_clk: + clk_disable_unprepare(md->core_clk); return rc; } @@ -222,11 +281,37 @@ static int mdio_mux_iproc_remove(struct platform_device *pdev) mdio_mux_uninit(md->mux_handle); mdiobus_unregister(md->mii_bus); - mdiobus_free(md->mii_bus); + clk_disable_unprepare(md->core_clk); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mdio_mux_iproc_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev); + + clk_disable_unprepare(md->core_clk); return 0; } +static int mdio_mux_iproc_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iproc_mdiomux_desc *md = platform_get_drvdata(pdev); + + clk_prepare_enable(md->core_clk); + mdio_mux_iproc_config(md); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mdio_mux_iproc_pm_ops, + mdio_mux_iproc_suspend, mdio_mux_iproc_resume); + static const struct of_device_id mdio_mux_iproc_match[] = { { .compatible = "brcm,mdio-mux-iproc", @@ -239,6 +324,7 @@ static struct platform_driver mdiomux_iproc_driver = { .driver = { .name = "mdio-mux-iproc", .of_match_table = mdio_mux_iproc_match, + .pm = &mdio_mux_iproc_pm_ops, }, .probe = mdio_mux_iproc_probe, .remove = mdio_mux_iproc_remove, |