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