]> asedeno.scripts.mit.edu Git - linux.git/blob - drivers/net/phy/mdio-bcm-unimac.c
Merge tag 'firewire-update' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394...
[linux.git] / drivers / net / phy / mdio-bcm-unimac.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Broadcom UniMAC MDIO bus controller driver
4  *
5  * Copyright (C) 2014-2017 Broadcom
6  */
7
8 #include <linux/kernel.h>
9 #include <linux/phy.h>
10 #include <linux/platform_device.h>
11 #include <linux/sched.h>
12 #include <linux/module.h>
13 #include <linux/io.h>
14 #include <linux/delay.h>
15 #include <linux/clk.h>
16
17 #include <linux/of.h>
18 #include <linux/of_platform.h>
19 #include <linux/of_mdio.h>
20
21 #include <linux/platform_data/mdio-bcm-unimac.h>
22
23 #define MDIO_CMD                0x00
24 #define  MDIO_START_BUSY        (1 << 29)
25 #define  MDIO_READ_FAIL         (1 << 28)
26 #define  MDIO_RD                (2 << 26)
27 #define  MDIO_WR                (1 << 26)
28 #define  MDIO_PMD_SHIFT         21
29 #define  MDIO_PMD_MASK          0x1F
30 #define  MDIO_REG_SHIFT         16
31 #define  MDIO_REG_MASK          0x1F
32
33 #define MDIO_CFG                0x04
34 #define  MDIO_C22               (1 << 0)
35 #define  MDIO_C45               0
36 #define  MDIO_CLK_DIV_SHIFT     4
37 #define  MDIO_CLK_DIV_MASK      0x3F
38 #define  MDIO_SUPP_PREAMBLE     (1 << 12)
39
40 struct unimac_mdio_priv {
41         struct mii_bus          *mii_bus;
42         void __iomem            *base;
43         int (*wait_func)        (void *wait_func_data);
44         void                    *wait_func_data;
45         struct clk              *clk;
46         u32                     clk_freq;
47 };
48
49 static inline u32 unimac_mdio_readl(struct unimac_mdio_priv *priv, u32 offset)
50 {
51         /* MIPS chips strapped for BE will automagically configure the
52          * peripheral registers for CPU-native byte order.
53          */
54         if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
55                 return __raw_readl(priv->base + offset);
56         else
57                 return readl_relaxed(priv->base + offset);
58 }
59
60 static inline void unimac_mdio_writel(struct unimac_mdio_priv *priv, u32 val,
61                                       u32 offset)
62 {
63         if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
64                 __raw_writel(val, priv->base + offset);
65         else
66                 writel_relaxed(val, priv->base + offset);
67 }
68
69 static inline void unimac_mdio_start(struct unimac_mdio_priv *priv)
70 {
71         u32 reg;
72
73         reg = unimac_mdio_readl(priv, MDIO_CMD);
74         reg |= MDIO_START_BUSY;
75         unimac_mdio_writel(priv, reg, MDIO_CMD);
76 }
77
78 static inline unsigned int unimac_mdio_busy(struct unimac_mdio_priv *priv)
79 {
80         return unimac_mdio_readl(priv, MDIO_CMD) & MDIO_START_BUSY;
81 }
82
83 static int unimac_mdio_poll(void *wait_func_data)
84 {
85         struct unimac_mdio_priv *priv = wait_func_data;
86         unsigned int timeout = 1000;
87
88         do {
89                 if (!unimac_mdio_busy(priv))
90                         return 0;
91
92                 usleep_range(1000, 2000);
93         } while (--timeout);
94
95         return -ETIMEDOUT;
96 }
97
98 static int unimac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
99 {
100         struct unimac_mdio_priv *priv = bus->priv;
101         int ret;
102         u32 cmd;
103
104         /* Prepare the read operation */
105         cmd = MDIO_RD | (phy_id << MDIO_PMD_SHIFT) | (reg << MDIO_REG_SHIFT);
106         unimac_mdio_writel(priv, cmd, MDIO_CMD);
107
108         /* Start MDIO transaction */
109         unimac_mdio_start(priv);
110
111         ret = priv->wait_func(priv->wait_func_data);
112         if (ret)
113                 return ret;
114
115         cmd = unimac_mdio_readl(priv, MDIO_CMD);
116
117         /* Some broken devices are known not to release the line during
118          * turn-around, e.g: Broadcom BCM53125 external switches, so check for
119          * that condition here and ignore the MDIO controller read failure
120          * indication.
121          */
122         if (!(bus->phy_ignore_ta_mask & 1 << phy_id) && (cmd & MDIO_READ_FAIL))
123                 return -EIO;
124
125         return cmd & 0xffff;
126 }
127
128 static int unimac_mdio_write(struct mii_bus *bus, int phy_id,
129                              int reg, u16 val)
130 {
131         struct unimac_mdio_priv *priv = bus->priv;
132         u32 cmd;
133
134         /* Prepare the write operation */
135         cmd = MDIO_WR | (phy_id << MDIO_PMD_SHIFT) |
136                 (reg << MDIO_REG_SHIFT) | (0xffff & val);
137         unimac_mdio_writel(priv, cmd, MDIO_CMD);
138
139         unimac_mdio_start(priv);
140
141         return priv->wait_func(priv->wait_func_data);
142 }
143
144 /* Workaround for integrated BCM7xxx Gigabit PHYs which have a problem with
145  * their internal MDIO management controller making them fail to successfully
146  * be read from or written to for the first transaction.  We insert a dummy
147  * BMSR read here to make sure that phy_get_device() and get_phy_id() can
148  * correctly read the PHY MII_PHYSID1/2 registers and successfully register a
149  * PHY device for this peripheral.
150  *
151  * Once the PHY driver is registered, we can workaround subsequent reads from
152  * there (e.g: during system-wide power management).
153  *
154  * bus->reset is invoked before mdiobus_scan during mdiobus_register and is
155  * therefore the right location to stick that workaround. Since we do not want
156  * to read from non-existing PHYs, we either use bus->phy_mask or do a manual
157  * Device Tree scan to limit the search area.
158  */
159 static int unimac_mdio_reset(struct mii_bus *bus)
160 {
161         struct device_node *np = bus->dev.of_node;
162         struct device_node *child;
163         u32 read_mask = 0;
164         int addr;
165
166         if (!np) {
167                 read_mask = ~bus->phy_mask;
168         } else {
169                 for_each_available_child_of_node(np, child) {
170                         addr = of_mdio_parse_addr(&bus->dev, child);
171                         if (addr < 0)
172                                 continue;
173
174                         read_mask |= 1 << addr;
175                 }
176         }
177
178         for (addr = 0; addr < PHY_MAX_ADDR; addr++) {
179                 if (read_mask & 1 << addr) {
180                         dev_dbg(&bus->dev, "Workaround for PHY @ %d\n", addr);
181                         mdiobus_read(bus, addr, MII_BMSR);
182                 }
183         }
184
185         return 0;
186 }
187
188 static void unimac_mdio_clk_set(struct unimac_mdio_priv *priv)
189 {
190         unsigned long rate;
191         u32 reg, div;
192
193         /* Keep the hardware default values */
194         if (!priv->clk_freq)
195                 return;
196
197         if (!priv->clk)
198                 rate = 250000000;
199         else
200                 rate = clk_get_rate(priv->clk);
201
202         div = (rate / (2 * priv->clk_freq)) - 1;
203         if (div & ~MDIO_CLK_DIV_MASK) {
204                 pr_warn("Incorrect MDIO clock frequency, ignoring\n");
205                 return;
206         }
207
208         /* The MDIO clock is the reference clock (typicaly 250Mhz) divided by
209          * 2 x (MDIO_CLK_DIV + 1)
210          */
211         reg = unimac_mdio_readl(priv, MDIO_CFG);
212         reg &= ~(MDIO_CLK_DIV_MASK << MDIO_CLK_DIV_SHIFT);
213         reg |= div << MDIO_CLK_DIV_SHIFT;
214         unimac_mdio_writel(priv, reg, MDIO_CFG);
215 }
216
217 static int unimac_mdio_probe(struct platform_device *pdev)
218 {
219         struct unimac_mdio_pdata *pdata = pdev->dev.platform_data;
220         struct unimac_mdio_priv *priv;
221         struct device_node *np;
222         struct mii_bus *bus;
223         struct resource *r;
224         int ret;
225
226         np = pdev->dev.of_node;
227
228         priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
229         if (!priv)
230                 return -ENOMEM;
231
232         r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
233         if (!r)
234                 return -EINVAL;
235
236         /* Just ioremap, as this MDIO block is usually integrated into an
237          * Ethernet MAC controller register range
238          */
239         priv->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
240         if (!priv->base) {
241                 dev_err(&pdev->dev, "failed to remap register\n");
242                 return -ENOMEM;
243         }
244
245         priv->clk = devm_clk_get(&pdev->dev, NULL);
246         if (PTR_ERR(priv->clk) == -EPROBE_DEFER)
247                 return PTR_ERR(priv->clk);
248         else
249                 priv->clk = NULL;
250
251         ret = clk_prepare_enable(priv->clk);
252         if (ret)
253                 return ret;
254
255         if (of_property_read_u32(np, "clock-frequency", &priv->clk_freq))
256                 priv->clk_freq = 0;
257
258         unimac_mdio_clk_set(priv);
259
260         priv->mii_bus = mdiobus_alloc();
261         if (!priv->mii_bus) {
262                 ret = -ENOMEM;
263                 goto out_clk_disable;
264         }
265
266         bus = priv->mii_bus;
267         bus->priv = priv;
268         if (pdata) {
269                 bus->name = pdata->bus_name;
270                 priv->wait_func = pdata->wait_func;
271                 priv->wait_func_data = pdata->wait_func_data;
272                 bus->phy_mask = ~pdata->phy_mask;
273         } else {
274                 bus->name = "unimac MII bus";
275                 priv->wait_func_data = priv;
276                 priv->wait_func = unimac_mdio_poll;
277         }
278         bus->parent = &pdev->dev;
279         bus->read = unimac_mdio_read;
280         bus->write = unimac_mdio_write;
281         bus->reset = unimac_mdio_reset;
282         snprintf(bus->id, MII_BUS_ID_SIZE, "%s-%d", pdev->name, pdev->id);
283
284         ret = of_mdiobus_register(bus, np);
285         if (ret) {
286                 dev_err(&pdev->dev, "MDIO bus registration failed\n");
287                 goto out_mdio_free;
288         }
289
290         platform_set_drvdata(pdev, priv);
291
292         dev_info(&pdev->dev, "Broadcom UniMAC MDIO bus\n");
293
294         return 0;
295
296 out_mdio_free:
297         mdiobus_free(bus);
298 out_clk_disable:
299         clk_disable_unprepare(priv->clk);
300         return ret;
301 }
302
303 static int unimac_mdio_remove(struct platform_device *pdev)
304 {
305         struct unimac_mdio_priv *priv = platform_get_drvdata(pdev);
306
307         mdiobus_unregister(priv->mii_bus);
308         mdiobus_free(priv->mii_bus);
309         clk_disable_unprepare(priv->clk);
310
311         return 0;
312 }
313
314 static int __maybe_unused unimac_mdio_suspend(struct device *d)
315 {
316         struct unimac_mdio_priv *priv = dev_get_drvdata(d);
317
318         clk_disable_unprepare(priv->clk);
319
320         return 0;
321 }
322
323 static int __maybe_unused unimac_mdio_resume(struct device *d)
324 {
325         struct unimac_mdio_priv *priv = dev_get_drvdata(d);
326         int ret;
327
328         ret = clk_prepare_enable(priv->clk);
329         if (ret)
330                 return ret;
331
332         unimac_mdio_clk_set(priv);
333
334         return 0;
335 }
336
337 static SIMPLE_DEV_PM_OPS(unimac_mdio_pm_ops,
338                          unimac_mdio_suspend, unimac_mdio_resume);
339
340 static const struct of_device_id unimac_mdio_ids[] = {
341         { .compatible = "brcm,genet-mdio-v5", },
342         { .compatible = "brcm,genet-mdio-v4", },
343         { .compatible = "brcm,genet-mdio-v3", },
344         { .compatible = "brcm,genet-mdio-v2", },
345         { .compatible = "brcm,genet-mdio-v1", },
346         { .compatible = "brcm,unimac-mdio", },
347         { /* sentinel */ },
348 };
349 MODULE_DEVICE_TABLE(of, unimac_mdio_ids);
350
351 static struct platform_driver unimac_mdio_driver = {
352         .driver = {
353                 .name = UNIMAC_MDIO_DRV_NAME,
354                 .of_match_table = unimac_mdio_ids,
355                 .pm = &unimac_mdio_pm_ops,
356         },
357         .probe  = unimac_mdio_probe,
358         .remove = unimac_mdio_remove,
359 };
360 module_platform_driver(unimac_mdio_driver);
361
362 MODULE_AUTHOR("Broadcom Corporation");
363 MODULE_DESCRIPTION("Broadcom UniMAC MDIO bus controller");
364 MODULE_LICENSE("GPL");
365 MODULE_ALIAS("platform:" UNIMAC_MDIO_DRV_NAME);