brintos

brintos / linux-shallow public Read only

0
0
Text · 4.8 KiB · 5a2d26c Raw
220 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2015 Broadcom Corporation4 */5 6#include <linux/delay.h>7#include <linux/io.h>8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/of.h>11#include <linux/of_platform.h>12#include <linux/of_mdio.h>13#include <linux/phy.h>14#include <linux/platform_device.h>15#include <linux/sched.h>16 17#define IPROC_GPHY_MDCDIV    0x1a18 19#define MII_CTRL_OFFSET      0x00020 21#define MII_CTRL_DIV_SHIFT   022#define MII_CTRL_PRE_SHIFT   723#define MII_CTRL_BUSY_SHIFT  824 25#define MII_DATA_OFFSET      0x00426#define MII_DATA_MASK        0xffff27#define MII_DATA_TA_SHIFT    1628#define MII_DATA_TA_VAL      229#define MII_DATA_RA_SHIFT    1830#define MII_DATA_PA_SHIFT    2331#define MII_DATA_OP_SHIFT    2832#define MII_DATA_OP_WRITE    133#define MII_DATA_OP_READ     234#define MII_DATA_SB_SHIFT    3035 36struct iproc_mdio_priv {37	struct mii_bus *mii_bus;38	void __iomem *base;39};40 41static inline int iproc_mdio_wait_for_idle(void __iomem *base)42{43	u32 val;44	unsigned int timeout = 1000; /* loop for 1s */45 46	do {47		val = readl(base + MII_CTRL_OFFSET);48		if ((val & BIT(MII_CTRL_BUSY_SHIFT)) == 0)49			return 0;50 51		usleep_range(1000, 2000);52	} while (timeout--);53 54	return -ETIMEDOUT;55}56 57static inline void iproc_mdio_config_clk(void __iomem *base)58{59	u32 val;60 61	val = (IPROC_GPHY_MDCDIV << MII_CTRL_DIV_SHIFT) |62		  BIT(MII_CTRL_PRE_SHIFT);63	writel(val, base + MII_CTRL_OFFSET);64}65 66static int iproc_mdio_read(struct mii_bus *bus, int phy_id, int reg)67{68	struct iproc_mdio_priv *priv = bus->priv;69	u32 cmd;70	int rc;71 72	rc = iproc_mdio_wait_for_idle(priv->base);73	if (rc)74		return rc;75 76	/* Prepare the read operation */77	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |78		(reg << MII_DATA_RA_SHIFT) |79		(phy_id << MII_DATA_PA_SHIFT) |80		BIT(MII_DATA_SB_SHIFT) |81		(MII_DATA_OP_READ << MII_DATA_OP_SHIFT);82 83	writel(cmd, priv->base + MII_DATA_OFFSET);84 85	rc = iproc_mdio_wait_for_idle(priv->base);86	if (rc)87		return rc;88 89	cmd = readl(priv->base + MII_DATA_OFFSET) & MII_DATA_MASK;90 91	return cmd;92}93 94static int iproc_mdio_write(struct mii_bus *bus, int phy_id,95			    int reg, u16 val)96{97	struct iproc_mdio_priv *priv = bus->priv;98	u32 cmd;99	int rc;100 101	rc = iproc_mdio_wait_for_idle(priv->base);102	if (rc)103		return rc;104 105	/* Prepare the write operation */106	cmd = (MII_DATA_TA_VAL << MII_DATA_TA_SHIFT) |107		(reg << MII_DATA_RA_SHIFT) |108		(phy_id << MII_DATA_PA_SHIFT) |109		BIT(MII_DATA_SB_SHIFT) |110		(MII_DATA_OP_WRITE << MII_DATA_OP_SHIFT) |111		((u32)(val) & MII_DATA_MASK);112 113	writel(cmd, priv->base + MII_DATA_OFFSET);114 115	rc = iproc_mdio_wait_for_idle(priv->base);116	if (rc)117		return rc;118 119	return 0;120}121 122static int iproc_mdio_probe(struct platform_device *pdev)123{124	struct iproc_mdio_priv *priv;125	struct mii_bus *bus;126	int rc;127 128	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);129	if (!priv)130		return -ENOMEM;131 132	priv->base = devm_platform_ioremap_resource(pdev, 0);133	if (IS_ERR(priv->base)) {134		dev_err(&pdev->dev, "failed to ioremap register\n");135		return PTR_ERR(priv->base);136	}137 138	priv->mii_bus = mdiobus_alloc();139	if (!priv->mii_bus) {140		dev_err(&pdev->dev, "MDIO bus alloc failed\n");141		return -ENOMEM;142	}143 144	bus = priv->mii_bus;145	bus->priv = priv;146	bus->name = "iProc MDIO bus";147	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);148	bus->parent = &pdev->dev;149	bus->read = iproc_mdio_read;150	bus->write = iproc_mdio_write;151 152	iproc_mdio_config_clk(priv->base);153 154	rc = of_mdiobus_register(bus, pdev->dev.of_node);155	if (rc) {156		dev_err(&pdev->dev, "MDIO bus registration failed\n");157		goto err_iproc_mdio;158	}159 160	platform_set_drvdata(pdev, priv);161 162	dev_info(&pdev->dev, "Broadcom iProc MDIO bus registered\n");163 164	return 0;165 166err_iproc_mdio:167	mdiobus_free(bus);168	return rc;169}170 171static void iproc_mdio_remove(struct platform_device *pdev)172{173	struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);174 175	mdiobus_unregister(priv->mii_bus);176	mdiobus_free(priv->mii_bus);177}178 179#ifdef CONFIG_PM_SLEEP180static int iproc_mdio_resume(struct device *dev)181{182	struct platform_device *pdev = to_platform_device(dev);183	struct iproc_mdio_priv *priv = platform_get_drvdata(pdev);184 185	/* restore the mii clock configuration */186	iproc_mdio_config_clk(priv->base);187 188	return 0;189}190 191static const struct dev_pm_ops iproc_mdio_pm_ops = {192	.resume = iproc_mdio_resume193};194#endif /* CONFIG_PM_SLEEP */195 196static const struct of_device_id iproc_mdio_of_match[] = {197	{ .compatible = "brcm,iproc-mdio", },198	{ /* sentinel */ },199};200MODULE_DEVICE_TABLE(of, iproc_mdio_of_match);201 202static struct platform_driver iproc_mdio_driver = {203	.driver = {204		.name = "iproc-mdio",205		.of_match_table = iproc_mdio_of_match,206#ifdef CONFIG_PM_SLEEP207		.pm = &iproc_mdio_pm_ops,208#endif209	},210	.probe = iproc_mdio_probe,211	.remove_new = iproc_mdio_remove,212};213 214module_platform_driver(iproc_mdio_driver);215 216MODULE_AUTHOR("Broadcom Corporation");217MODULE_DESCRIPTION("Broadcom iProc MDIO bus controller");218MODULE_LICENSE("GPL v2");219MODULE_ALIAS("platform:iproc-mdio");220