Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rdma/rdma
[linux-2.6-microblaze.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         if (regnum & MII_ADDR_C45)
106                 return -EOPNOTSUPP;
107
108         ret = mscc_miim_wait_pending(bus);
109         if (ret)
110                 goto out;
111
112         ret = regmap_write(miim->regs,
113                            MSCC_MIIM_REG_CMD + miim->mii_status_offset,
114                            MSCC_MIIM_CMD_VLD |
115                            (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
116                            (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
117                            MSCC_MIIM_CMD_OPR_READ);
118
119         if (ret < 0) {
120                 WARN_ONCE(1, "mscc miim write cmd reg error %d\n", ret);
121                 goto out;
122         }
123
124         ret = mscc_miim_wait_ready(bus);
125         if (ret)
126                 goto out;
127
128         ret = regmap_read(miim->regs,
129                           MSCC_MIIM_REG_DATA + miim->mii_status_offset, &val);
130         if (ret < 0) {
131                 WARN_ONCE(1, "mscc miim read data reg error %d\n", ret);
132                 goto out;
133         }
134
135         if (val & MSCC_MIIM_DATA_ERROR) {
136                 ret = -EIO;
137                 goto out;
138         }
139
140         ret = val & 0xFFFF;
141 out:
142         return ret;
143 }
144
145 static int mscc_miim_write(struct mii_bus *bus, int mii_id,
146                            int regnum, u16 value)
147 {
148         struct mscc_miim_dev *miim = bus->priv;
149         int ret;
150
151         if (regnum & MII_ADDR_C45)
152                 return -EOPNOTSUPP;
153
154         ret = mscc_miim_wait_pending(bus);
155         if (ret < 0)
156                 goto out;
157
158         ret = regmap_write(miim->regs,
159                            MSCC_MIIM_REG_CMD + miim->mii_status_offset,
160                            MSCC_MIIM_CMD_VLD |
161                            (mii_id << MSCC_MIIM_CMD_PHYAD_SHIFT) |
162                            (regnum << MSCC_MIIM_CMD_REGAD_SHIFT) |
163                            (value << MSCC_MIIM_CMD_WRDATA_SHIFT) |
164                            MSCC_MIIM_CMD_OPR_WRITE);
165
166         if (ret < 0)
167                 WARN_ONCE(1, "mscc miim write error %d\n", ret);
168 out:
169         return ret;
170 }
171
172 static int mscc_miim_reset(struct mii_bus *bus)
173 {
174         struct mscc_miim_dev *miim = bus->priv;
175         unsigned int offset, bits;
176         int ret;
177
178         if (!miim->phy_regs)
179                 return 0;
180
181         offset = miim->info->phy_reset_offset;
182         bits = miim->info->phy_reset_bits;
183
184         ret = regmap_update_bits(miim->phy_regs, offset, bits, 0);
185         if (ret < 0) {
186                 WARN_ONCE(1, "mscc reset set error %d\n", ret);
187                 return ret;
188         }
189
190         ret = regmap_update_bits(miim->phy_regs, offset, bits, bits);
191         if (ret < 0) {
192                 WARN_ONCE(1, "mscc reset clear error %d\n", ret);
193                 return ret;
194         }
195
196         mdelay(500);
197
198         return 0;
199 }
200
201 static const struct regmap_config mscc_miim_regmap_config = {
202         .reg_bits       = 32,
203         .val_bits       = 32,
204         .reg_stride     = 4,
205 };
206
207 static const struct regmap_config mscc_miim_phy_regmap_config = {
208         .reg_bits       = 32,
209         .val_bits       = 32,
210         .reg_stride     = 4,
211         .name           = "phy",
212 };
213
214 int mscc_miim_setup(struct device *dev, struct mii_bus **pbus, const char *name,
215                     struct regmap *mii_regmap, int status_offset)
216 {
217         struct mscc_miim_dev *miim;
218         struct mii_bus *bus;
219
220         bus = devm_mdiobus_alloc_size(dev, sizeof(*miim));
221         if (!bus)
222                 return -ENOMEM;
223
224         bus->name = name;
225         bus->read = mscc_miim_read;
226         bus->write = mscc_miim_write;
227         bus->reset = mscc_miim_reset;
228         snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
229         bus->parent = dev;
230
231         miim = bus->priv;
232
233         *pbus = bus;
234
235         miim->regs = mii_regmap;
236         miim->mii_status_offset = status_offset;
237
238         *pbus = bus;
239
240         return 0;
241 }
242 EXPORT_SYMBOL(mscc_miim_setup);
243
244 static int mscc_miim_probe(struct platform_device *pdev)
245 {
246         struct regmap *mii_regmap, *phy_regmap = NULL;
247         void __iomem *regs, *phy_regs;
248         struct mscc_miim_dev *miim;
249         struct resource *res;
250         struct mii_bus *bus;
251         int ret;
252
253         regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
254         if (IS_ERR(regs)) {
255                 dev_err(&pdev->dev, "Unable to map MIIM registers\n");
256                 return PTR_ERR(regs);
257         }
258
259         mii_regmap = devm_regmap_init_mmio(&pdev->dev, regs,
260                                            &mscc_miim_regmap_config);
261
262         if (IS_ERR(mii_regmap)) {
263                 dev_err(&pdev->dev, "Unable to create MIIM regmap\n");
264                 return PTR_ERR(mii_regmap);
265         }
266
267         /* This resource is optional */
268         res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
269         if (res) {
270                 phy_regs = devm_ioremap_resource(&pdev->dev, res);
271                 if (IS_ERR(phy_regs)) {
272                         dev_err(&pdev->dev, "Unable to map internal phy registers\n");
273                         return PTR_ERR(phy_regs);
274                 }
275
276                 phy_regmap = devm_regmap_init_mmio(&pdev->dev, phy_regs,
277                                                    &mscc_miim_phy_regmap_config);
278                 if (IS_ERR(phy_regmap)) {
279                         dev_err(&pdev->dev, "Unable to create phy register regmap\n");
280                         return PTR_ERR(phy_regmap);
281                 }
282         }
283
284         ret = mscc_miim_setup(&pdev->dev, &bus, "mscc_miim", mii_regmap, 0);
285         if (ret < 0) {
286                 dev_err(&pdev->dev, "Unable to setup the MDIO bus\n");
287                 return ret;
288         }
289
290         miim = bus->priv;
291         miim->phy_regs = phy_regmap;
292
293         miim->info = device_get_match_data(&pdev->dev);
294         if (!miim->info)
295                 return -EINVAL;
296
297         ret = of_mdiobus_register(bus, pdev->dev.of_node);
298         if (ret < 0) {
299                 dev_err(&pdev->dev, "Cannot register MDIO bus (%d)\n", ret);
300                 return ret;
301         }
302
303         platform_set_drvdata(pdev, bus);
304
305         return 0;
306 }
307
308 static int mscc_miim_remove(struct platform_device *pdev)
309 {
310         struct mii_bus *bus = platform_get_drvdata(pdev);
311
312         mdiobus_unregister(bus);
313
314         return 0;
315 }
316
317 static const struct mscc_miim_info mscc_ocelot_miim_info = {
318         .phy_reset_offset = MSCC_PHY_REG_PHY_CFG,
319         .phy_reset_bits = PHY_CFG_PHY_ENA | PHY_CFG_PHY_COMMON_RESET |
320                           PHY_CFG_PHY_RESET,
321 };
322
323 static const struct mscc_miim_info microchip_lan966x_miim_info = {
324         .phy_reset_offset = LAN966X_CUPHY_COMMON_CFG,
325         .phy_reset_bits = CUPHY_COMMON_CFG_RESET_N,
326 };
327
328 static const struct of_device_id mscc_miim_match[] = {
329         {
330                 .compatible = "mscc,ocelot-miim",
331                 .data = &mscc_ocelot_miim_info
332         }, {
333                 .compatible = "microchip,lan966x-miim",
334                 .data = &microchip_lan966x_miim_info
335         },
336         { }
337 };
338 MODULE_DEVICE_TABLE(of, mscc_miim_match);
339
340 static struct platform_driver mscc_miim_driver = {
341         .probe = mscc_miim_probe,
342         .remove = mscc_miim_remove,
343         .driver = {
344                 .name = "mscc-miim",
345                 .of_match_table = mscc_miim_match,
346         },
347 };
348
349 module_platform_driver(mscc_miim_driver);
350
351 MODULE_DESCRIPTION("Microsemi MIIM driver");
352 MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@bootlin.com>");
353 MODULE_LICENSE("Dual MIT/GPL");