c483ba67c21f193747a539df87ea23002150b944
[platform/kernel/linux-starfive.git] / drivers / net / mdio / mdio-mscc-miim.c
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /*
3  * Driver for the MDIO interface of Microsemi network switches.
4  *
5  * Author: Alexandre Belloni <alexandre.belloni@bootlin.com>
6  * Copyright (c) 2017 Microsemi Corporation
7  */
8
9 #include <linux/bitops.h>
10 #include <linux/io.h>
11 #include <linux/iopoll.h>
12 #include <linux/kernel.h>
13 #include <linux/mdio/mdio-mscc-miim.h>
14 #include <linux/module.h>
15 #include <linux/of_mdio.h>
16 #include <linux/phy.h>
17 #include <linux/platform_device.h>
18 #include <linux/property.h>
19 #include <linux/regmap.h>
20
21 #define MSCC_MIIM_REG_STATUS            0x0
22 #define         MSCC_MIIM_STATUS_STAT_PENDING   BIT(2)
23 #define         MSCC_MIIM_STATUS_STAT_BUSY      BIT(3)
24 #define MSCC_MIIM_REG_CMD               0x8
25 #define         MSCC_MIIM_CMD_OPR_WRITE         BIT(1)
26 #define         MSCC_MIIM_CMD_OPR_READ          BIT(2)
27 #define         MSCC_MIIM_CMD_WRDATA_SHIFT      4
28 #define         MSCC_MIIM_CMD_REGAD_SHIFT       20
29 #define         MSCC_MIIM_CMD_PHYAD_SHIFT       25
30 #define         MSCC_MIIM_CMD_VLD               BIT(31)
31 #define MSCC_MIIM_REG_DATA              0xC
32 #define         MSCC_MIIM_DATA_ERROR            (BIT(16) | BIT(17))
33
34 #define MSCC_PHY_REG_PHY_CFG    0x0
35 #define         PHY_CFG_PHY_ENA         (BIT(0) | BIT(1) | BIT(2) | BIT(3))
36 #define         PHY_CFG_PHY_COMMON_RESET BIT(4)
37 #define         PHY_CFG_PHY_RESET       (BIT(5) | BIT(6) | BIT(7) | BIT(8))
38 #define MSCC_PHY_REG_PHY_STATUS 0x4
39
40 #define LAN966X_CUPHY_COMMON_CFG        0x0
41 #define         CUPHY_COMMON_CFG_RESET_N        BIT(0)
42
43 struct mscc_miim_info {
44         unsigned int phy_reset_offset;
45         unsigned int phy_reset_bits;
46 };
47
48 struct mscc_miim_dev {
49         struct regmap *regs;
50         int mii_status_offset;
51         struct regmap *phy_regs;
52         const struct mscc_miim_info *info;
53 };
54
55 /* When high resolution timers aren't built-in: we can't use usleep_range() as
56  * we would sleep way too long. Use udelay() instead.
57  */
58 #define mscc_readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us)\
59 ({                                                                        \
60         if (!IS_ENABLED(CONFIG_HIGH_RES_TIMERS))                          \
61                 readx_poll_timeout_atomic(op, addr, val, cond, delay_us,  \
62                                           timeout_us);                    \
63         readx_poll_timeout(op, addr, val, cond, delay_us, timeout_us);    \
64 })
65
66 static int mscc_miim_status(struct mii_bus *bus)
67 {
68         struct mscc_miim_dev *miim = bus->priv;
69         int val, ret;
70
71         ret = regmap_read(miim->regs,
72                           MSCC_MIIM_REG_STATUS + miim->mii_status_offset, &val);
73         if (ret < 0) {
74                 WARN_ONCE(1, "mscc miim status read error %d\n", ret);
75                 return ret;
76         }
77
78         return val;
79 }
80
81 static int mscc_miim_wait_ready(struct mii_bus *bus)
82 {
83         u32 val;
84
85         return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
86                                        !(val & MSCC_MIIM_STATUS_STAT_BUSY), 50,
87                                        10000);
88 }
89
90 static int mscc_miim_wait_pending(struct mii_bus *bus)
91 {
92         u32 val;
93
94         return mscc_readx_poll_timeout(mscc_miim_status, bus, val,
95                                        !(val & MSCC_MIIM_STATUS_STAT_PENDING),
96                                        50, 10000);
97 }
98
99 static int mscc_miim_read(struct mii_bus *bus, int mii_id, int regnum)
100 {
101         struct mscc_miim_dev *miim = bus->priv;
102         u32 val;
103         int ret;
104
105         ret = mscc_miim_wait_pending(bus);
106         if (ret)
107                 goto out;
108
109         ret = regmap_write(miim->regs,
110                            MSCC_MIIM_REG_CMD + miim->mii_status_offset,
111                            MSCC_MIIM_CMD_VLD |
112                            (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
113                            (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
114                            MSCC_MIIM_CMD_OPR_READ);
115
116         if (ret < 0) {
117                 WARN_ONCE(1, "mscc miim write cmd reg error %d\n", ret);
118                 goto out;
119         }
120
121         ret = mscc_miim_wait_ready(bus);
122         if (ret)
123                 goto out;
124
125         ret = regmap_read(miim->regs,
126                           MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val);
127         if (ret < 0) {
128                 WARN_ONCE(1, "mscc miim read data reg error %d\n", ret);
129                 goto out;
130         }
131
132         if (val & MSCC_MIIM_DATA_ERROR) {
133                 ret = -EIO;
134                 goto out;
135         }
136
137         ret = val & 0xFFFF;
138 out:
139         return ret;
140 }
141
142 static int mscc_miim_write(struct mii_bus *bus, int mii_id,
143                            int regnum, u16 value)
144 {
145         struct mscc_miim_dev *miim = bus->priv;
146         int ret;
147
148         ret = mscc_miim_wait_pending(bus);
149         if (ret < 0)
150                 goto out;
151
152         ret = regmap_write(miim->regs,
153                            MSCC_MIIM_REG_CMD + miim->mii_status_offset,
154                            MSCC_MIIM_CMD_VLD |
155                            (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
156                            (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
157                            (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
158                            MSCC_MIIM_CMD_OPR_WRITE);
159
160         if (ret < 0)
161                 WARN_ONCE(1, "mscc miim write error %d\n", ret);
162 out:
163         return ret;
164 }
165
166 static int mscc_miim_reset(struct mii_bus *bus)
167 {
168         struct mscc_miim_dev *miim = bus->priv;
169         unsigned int offset, bits;
170         int ret;
171
172         if (!miim->phy_regs)
173                 return 0;
174
175         offset = miim->info->phy_reset_offset;
176         bits = miim->info->phy_reset_bits;
177
178         ret = regmap_update_bits(miim->phy_regs, offset, bits, 0);
179         if (ret < 0) {
180                 WARN_ONCE(1, "mscc reset set error %d\n", ret);
181                 return ret;
182         }
183
184         ret = regmap_update_bits(miim->phy_regs, offset, bits, bits);
185         if (ret < 0) {
186                 WARN_ONCE(1, "mscc reset clear error %d\n", ret);
187                 return ret;
188         }
189
190         mdelay(500);
191
192         return 0;
193 }
194
195 static const struct regmap_config mscc_miim_regmap_config = {
196         .reg_bits       = 32,
197         .val_bits       = 32,
198         .reg_stride     = 4,
199 };
200
201 static const struct regmap_config mscc_miim_phy_regmap_config = {
202         .reg_bits       = 32,
203         .val_bits       = 32,
204         .reg_stride     = 4,
205         .name           = "phy",
206 };
207
208 int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
209                     struct regmap *mii_regmap, int status_offset)
210 {
211         struct mscc_miim_dev *miim;
212         struct mii_bus *bus;
213
214         bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
215         if (!bus)
216                 return -ENOMEM;
217
218         bus->name = name;
219         bus->read = mscc_miim_read;
220         bus->write = mscc_miim_write;
221         bus->reset = mscc_miim_reset;
222         snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
223         bus->parent = dev;
224
225         miim = bus->priv;
226
227         *pbus = bus;
228
229         miim->regs = mii_regmap;
230         miim->mii_status_offset = status_offset;
231
232         *pbus = bus;
233
234         return 0;
235 }
236 EXPORT_SYMBOL(mscc_miim_setup);
237
238 static int mscc_miim_probe(struct platform_device *pdev)
239 {
240         struct regmap *mii_regmap, *phy_regmap = NULL;
241         void __iomem *regs, *phy_regs;
242         struct mscc_miim_dev *miim;
243         struct resource *res;
244         struct mii_bus *bus;
245         int ret;
246
247         regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
248         if (IS_ERR(regs)) {
249                 dev_err(&pdev->dev, "Unable to map MIIM registers\n");
250                 return PTR_ERR(regs);
251         }
252
253         mii_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
254                                            &mscc_miim_regmap_config);
255
256         if (IS_ERR(mii_regmap)) {
257                 dev_err(&pdev->dev, "Unable to create MIIM regmap\n");
258                 return PTR_ERR(mii_regmap);
259         }
260
261         /* This resource is optional */
262         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
263         if (res) {
264                 phy_regs = devm_ioremap_resource(&pdev->dev, res);
265                 if (IS_ERR(phy_regs)) {
266                         dev_err(&pdev->dev, "Unable to map internal phy registers\n");
267                         return PTR_ERR(phy_regs);
268                 }
269
270                 phy_regmap = devm_regmap_init_mmio(&pdev->dev, phy_regs,
271                                                    &mscc_miim_phy_regmap_config);
272                 if (IS_ERR(phy_regmap)) {
273                         dev_err(&pdev->dev, "Unable to create phy register regmap\n");
274                         return PTR_ERR(phy_regmap);
275                 }
276         }
277
278         ret = mscc_miim_setup(&pdev->dev, &bus, "mscc_miim", mii_regmap, 0);
279         if (ret < 0) {
280                 dev_err(&pdev->dev, "Unable to setup the MDIO bus\n");
281                 return ret;
282         }
283
284         miim = bus->priv;
285         miim->phy_regs = phy_regmap;
286
287         miim->info = device_get_match_data(&pdev->dev);
288         if (!miim->info)
289                 return -EINVAL;
290
291         ret = of_mdiobus_register(bus, pdev->dev.of_node);
292         if (ret < 0) {
293                 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
294                 return ret;
295         }
296
297         platform_set_drvdata(pdev, bus);
298
299         return 0;
300 }
301
302 static int mscc_miim_remove(struct platform_device *pdev)
303 {
304         struct mii_bus *bus = platform_get_drvdata(pdev);
305
306         mdiobus_unregister(bus);
307
308         return 0;
309 }
310
311 static const struct mscc_miim_info mscc_ocelot_miim_info = {
312         .phy_reset_offset = MSCC_PHY_REG_PHY_CFG,
313         .phy_reset_bits = PHY_CFG_PHY_ENA | PHY_CFG_PHY_COMMON_RESET |
314                           PHY_CFG_PHY_RESET,
315 };
316
317 static const struct mscc_miim_info microchip_lan966x_miim_info = {
318         .phy_reset_offset = LAN966X_CUPHY_COMMON_CFG,
319         .phy_reset_bits = CUPHY_COMMON_CFG_RESET_N,
320 };
321
322 static const struct of_device_id mscc_miim_match[] = {
323         {
324                 .compatible = "mscc,ocelot-miim",
325                 .data = &mscc_ocelot_miim_info
326         }, {
327                 .compatible = "microchip,lan966x-miim",
328                 .data = &microchip_lan966x_miim_info
329         },
330         { }
331 };
332 MODULE_DEVICE_TABLE(of, mscc_miim_match);
333
334 static struct platform_driver mscc_miim_driver = {
335         .probe = mscc_miim_probe,
336         .remove = mscc_miim_remove,
337         .driver = {
338                 .name = "mscc-miim",
339                 .of_match_table = mscc_miim_match,
340         },
341 };
342
343 module_platform_driver(mscc_miim_driver);
344
345 MODULE_DESCRIPTION("Microsemi MIIM driver");
346 MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
347 MODULE_LICENSE("Dual MIT/GPL");