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