Commit 6539c44d08ac2eea693b6163135169b9c8c18bb1
1 parent
772801ef75
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
net: Allow FIXED_PHY to be modular.
Otherwise we get things like: warning: (NET_DSA_BCM_SF2 && BCMGENET && SYSTEMPORT) selects FIXED_PHY which has unmet direct dependencies (NETDEVICES && PHYLIB=y) In order to make this work we have to rename fixed.c to fixed_phy.c because the regulator drivers already have a module named "fixed.o". Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 5 changed files with 341 additions and 341 deletions Side-by-side Diff
drivers/net/phy/Kconfig
... | ... | @@ -119,8 +119,8 @@ |
119 | 119 | Supports the KSZ9021, VSC8201, KS8001 PHYs. |
120 | 120 | |
121 | 121 | config FIXED_PHY |
122 | - bool "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" | |
123 | - depends on PHYLIB=y | |
122 | + tristate "Driver for MDIO Bus/PHY emulation with fixed speed/link PHYs" | |
123 | + depends on PHYLIB | |
124 | 124 | ---help--- |
125 | 125 | Adds the platform "fixed" MDIO Bus to cover the boards that use |
126 | 126 | PHYs that are not connected to the real MDIO bus. |
drivers/net/phy/Makefile
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | obj-$(CONFIG_ICPLUS_PHY) += icplus.o |
18 | 18 | obj-$(CONFIG_REALTEK_PHY) += realtek.o |
19 | 19 | obj-$(CONFIG_LSI_ET1011C_PHY) += et1011c.o |
20 | -obj-$(CONFIG_FIXED_PHY) += fixed.o | |
20 | +obj-$(CONFIG_FIXED_PHY) += fixed_phy.o | |
21 | 21 | obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o |
22 | 22 | obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o |
23 | 23 | obj-$(CONFIG_NATIONAL_PHY) += national.o |
drivers/net/phy/fixed.c
1 | -/* | |
2 | - * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) | |
3 | - * | |
4 | - * Author: Vitaly Bordug <vbordug@ru.mvista.com> | |
5 | - * Anton Vorontsov <avorontsov@ru.mvista.com> | |
6 | - * | |
7 | - * Copyright (c) 2006-2007 MontaVista Software, Inc. | |
8 | - * | |
9 | - * This program is free software; you can redistribute it and/or modify it | |
10 | - * under the terms of the GNU General Public License as published by the | |
11 | - * Free Software Foundation; either version 2 of the License, or (at your | |
12 | - * option) any later version. | |
13 | - */ | |
14 | - | |
15 | -#include <linux/kernel.h> | |
16 | -#include <linux/module.h> | |
17 | -#include <linux/platform_device.h> | |
18 | -#include <linux/list.h> | |
19 | -#include <linux/mii.h> | |
20 | -#include <linux/phy.h> | |
21 | -#include <linux/phy_fixed.h> | |
22 | -#include <linux/err.h> | |
23 | -#include <linux/slab.h> | |
24 | -#include <linux/of.h> | |
25 | - | |
26 | -#define MII_REGS_NUM 29 | |
27 | - | |
28 | -struct fixed_mdio_bus { | |
29 | - int irqs[PHY_MAX_ADDR]; | |
30 | - struct mii_bus *mii_bus; | |
31 | - struct list_head phys; | |
32 | -}; | |
33 | - | |
34 | -struct fixed_phy { | |
35 | - int addr; | |
36 | - u16 regs[MII_REGS_NUM]; | |
37 | - struct phy_device *phydev; | |
38 | - struct fixed_phy_status status; | |
39 | - int (*link_update)(struct net_device *, struct fixed_phy_status *); | |
40 | - struct list_head node; | |
41 | -}; | |
42 | - | |
43 | -static struct platform_device *pdev; | |
44 | -static struct fixed_mdio_bus platform_fmb = { | |
45 | - .phys = LIST_HEAD_INIT(platform_fmb.phys), | |
46 | -}; | |
47 | - | |
48 | -static int fixed_phy_update_regs(struct fixed_phy *fp) | |
49 | -{ | |
50 | - u16 bmsr = BMSR_ANEGCAPABLE; | |
51 | - u16 bmcr = 0; | |
52 | - u16 lpagb = 0; | |
53 | - u16 lpa = 0; | |
54 | - | |
55 | - if (fp->status.duplex) { | |
56 | - bmcr |= BMCR_FULLDPLX; | |
57 | - | |
58 | - switch (fp->status.speed) { | |
59 | - case 1000: | |
60 | - bmsr |= BMSR_ESTATEN; | |
61 | - bmcr |= BMCR_SPEED1000; | |
62 | - lpagb |= LPA_1000FULL; | |
63 | - break; | |
64 | - case 100: | |
65 | - bmsr |= BMSR_100FULL; | |
66 | - bmcr |= BMCR_SPEED100; | |
67 | - lpa |= LPA_100FULL; | |
68 | - break; | |
69 | - case 10: | |
70 | - bmsr |= BMSR_10FULL; | |
71 | - lpa |= LPA_10FULL; | |
72 | - break; | |
73 | - default: | |
74 | - pr_warn("fixed phy: unknown speed\n"); | |
75 | - return -EINVAL; | |
76 | - } | |
77 | - } else { | |
78 | - switch (fp->status.speed) { | |
79 | - case 1000: | |
80 | - bmsr |= BMSR_ESTATEN; | |
81 | - bmcr |= BMCR_SPEED1000; | |
82 | - lpagb |= LPA_1000HALF; | |
83 | - break; | |
84 | - case 100: | |
85 | - bmsr |= BMSR_100HALF; | |
86 | - bmcr |= BMCR_SPEED100; | |
87 | - lpa |= LPA_100HALF; | |
88 | - break; | |
89 | - case 10: | |
90 | - bmsr |= BMSR_10HALF; | |
91 | - lpa |= LPA_10HALF; | |
92 | - break; | |
93 | - default: | |
94 | - pr_warn("fixed phy: unknown speed\n"); | |
95 | - return -EINVAL; | |
96 | - } | |
97 | - } | |
98 | - | |
99 | - if (fp->status.link) | |
100 | - bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; | |
101 | - | |
102 | - if (fp->status.pause) | |
103 | - lpa |= LPA_PAUSE_CAP; | |
104 | - | |
105 | - if (fp->status.asym_pause) | |
106 | - lpa |= LPA_PAUSE_ASYM; | |
107 | - | |
108 | - fp->regs[MII_PHYSID1] = 0; | |
109 | - fp->regs[MII_PHYSID2] = 0; | |
110 | - | |
111 | - fp->regs[MII_BMSR] = bmsr; | |
112 | - fp->regs[MII_BMCR] = bmcr; | |
113 | - fp->regs[MII_LPA] = lpa; | |
114 | - fp->regs[MII_STAT1000] = lpagb; | |
115 | - | |
116 | - return 0; | |
117 | -} | |
118 | - | |
119 | -static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) | |
120 | -{ | |
121 | - struct fixed_mdio_bus *fmb = bus->priv; | |
122 | - struct fixed_phy *fp; | |
123 | - | |
124 | - if (reg_num >= MII_REGS_NUM) | |
125 | - return -1; | |
126 | - | |
127 | - /* We do not support emulating Clause 45 over Clause 22 register reads | |
128 | - * return an error instead of bogus data. | |
129 | - */ | |
130 | - switch (reg_num) { | |
131 | - case MII_MMD_CTRL: | |
132 | - case MII_MMD_DATA: | |
133 | - return -1; | |
134 | - default: | |
135 | - break; | |
136 | - } | |
137 | - | |
138 | - list_for_each_entry(fp, &fmb->phys, node) { | |
139 | - if (fp->addr == phy_addr) { | |
140 | - /* Issue callback if user registered it. */ | |
141 | - if (fp->link_update) { | |
142 | - fp->link_update(fp->phydev->attached_dev, | |
143 | - &fp->status); | |
144 | - fixed_phy_update_regs(fp); | |
145 | - } | |
146 | - return fp->regs[reg_num]; | |
147 | - } | |
148 | - } | |
149 | - | |
150 | - return 0xFFFF; | |
151 | -} | |
152 | - | |
153 | -static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, | |
154 | - u16 val) | |
155 | -{ | |
156 | - return 0; | |
157 | -} | |
158 | - | |
159 | -/* | |
160 | - * If something weird is required to be done with link/speed, | |
161 | - * network driver is able to assign a function to implement this. | |
162 | - * May be useful for PHY's that need to be software-driven. | |
163 | - */ | |
164 | -int fixed_phy_set_link_update(struct phy_device *phydev, | |
165 | - int (*link_update)(struct net_device *, | |
166 | - struct fixed_phy_status *)) | |
167 | -{ | |
168 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
169 | - struct fixed_phy *fp; | |
170 | - | |
171 | - if (!link_update || !phydev || !phydev->bus) | |
172 | - return -EINVAL; | |
173 | - | |
174 | - list_for_each_entry(fp, &fmb->phys, node) { | |
175 | - if (fp->addr == phydev->addr) { | |
176 | - fp->link_update = link_update; | |
177 | - fp->phydev = phydev; | |
178 | - return 0; | |
179 | - } | |
180 | - } | |
181 | - | |
182 | - return -ENOENT; | |
183 | -} | |
184 | -EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); | |
185 | - | |
186 | -int fixed_phy_add(unsigned int irq, int phy_addr, | |
187 | - struct fixed_phy_status *status) | |
188 | -{ | |
189 | - int ret; | |
190 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
191 | - struct fixed_phy *fp; | |
192 | - | |
193 | - fp = kzalloc(sizeof(*fp), GFP_KERNEL); | |
194 | - if (!fp) | |
195 | - return -ENOMEM; | |
196 | - | |
197 | - memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); | |
198 | - | |
199 | - fmb->irqs[phy_addr] = irq; | |
200 | - | |
201 | - fp->addr = phy_addr; | |
202 | - fp->status = *status; | |
203 | - | |
204 | - ret = fixed_phy_update_regs(fp); | |
205 | - if (ret) | |
206 | - goto err_regs; | |
207 | - | |
208 | - list_add_tail(&fp->node, &fmb->phys); | |
209 | - | |
210 | - return 0; | |
211 | - | |
212 | -err_regs: | |
213 | - kfree(fp); | |
214 | - return ret; | |
215 | -} | |
216 | -EXPORT_SYMBOL_GPL(fixed_phy_add); | |
217 | - | |
218 | -void fixed_phy_del(int phy_addr) | |
219 | -{ | |
220 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
221 | - struct fixed_phy *fp, *tmp; | |
222 | - | |
223 | - list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { | |
224 | - if (fp->addr == phy_addr) { | |
225 | - list_del(&fp->node); | |
226 | - kfree(fp); | |
227 | - return; | |
228 | - } | |
229 | - } | |
230 | -} | |
231 | -EXPORT_SYMBOL_GPL(fixed_phy_del); | |
232 | - | |
233 | -static int phy_fixed_addr; | |
234 | -static DEFINE_SPINLOCK(phy_fixed_addr_lock); | |
235 | - | |
236 | -struct phy_device *fixed_phy_register(unsigned int irq, | |
237 | - struct fixed_phy_status *status, | |
238 | - struct device_node *np) | |
239 | -{ | |
240 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
241 | - struct phy_device *phy; | |
242 | - int phy_addr; | |
243 | - int ret; | |
244 | - | |
245 | - /* Get the next available PHY address, up to PHY_MAX_ADDR */ | |
246 | - spin_lock(&phy_fixed_addr_lock); | |
247 | - if (phy_fixed_addr == PHY_MAX_ADDR) { | |
248 | - spin_unlock(&phy_fixed_addr_lock); | |
249 | - return ERR_PTR(-ENOSPC); | |
250 | - } | |
251 | - phy_addr = phy_fixed_addr++; | |
252 | - spin_unlock(&phy_fixed_addr_lock); | |
253 | - | |
254 | - ret = fixed_phy_add(PHY_POLL, phy_addr, status); | |
255 | - if (ret < 0) | |
256 | - return ERR_PTR(ret); | |
257 | - | |
258 | - phy = get_phy_device(fmb->mii_bus, phy_addr, false); | |
259 | - if (!phy || IS_ERR(phy)) { | |
260 | - fixed_phy_del(phy_addr); | |
261 | - return ERR_PTR(-EINVAL); | |
262 | - } | |
263 | - | |
264 | - of_node_get(np); | |
265 | - phy->dev.of_node = np; | |
266 | - | |
267 | - ret = phy_device_register(phy); | |
268 | - if (ret) { | |
269 | - phy_device_free(phy); | |
270 | - of_node_put(np); | |
271 | - fixed_phy_del(phy_addr); | |
272 | - return ERR_PTR(ret); | |
273 | - } | |
274 | - | |
275 | - return phy; | |
276 | -} | |
277 | -EXPORT_SYMBOL_GPL(fixed_phy_register); | |
278 | - | |
279 | -static int __init fixed_mdio_bus_init(void) | |
280 | -{ | |
281 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
282 | - int ret; | |
283 | - | |
284 | - pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); | |
285 | - if (IS_ERR(pdev)) { | |
286 | - ret = PTR_ERR(pdev); | |
287 | - goto err_pdev; | |
288 | - } | |
289 | - | |
290 | - fmb->mii_bus = mdiobus_alloc(); | |
291 | - if (fmb->mii_bus == NULL) { | |
292 | - ret = -ENOMEM; | |
293 | - goto err_mdiobus_reg; | |
294 | - } | |
295 | - | |
296 | - snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); | |
297 | - fmb->mii_bus->name = "Fixed MDIO Bus"; | |
298 | - fmb->mii_bus->priv = fmb; | |
299 | - fmb->mii_bus->parent = &pdev->dev; | |
300 | - fmb->mii_bus->read = &fixed_mdio_read; | |
301 | - fmb->mii_bus->write = &fixed_mdio_write; | |
302 | - fmb->mii_bus->irq = fmb->irqs; | |
303 | - | |
304 | - ret = mdiobus_register(fmb->mii_bus); | |
305 | - if (ret) | |
306 | - goto err_mdiobus_alloc; | |
307 | - | |
308 | - return 0; | |
309 | - | |
310 | -err_mdiobus_alloc: | |
311 | - mdiobus_free(fmb->mii_bus); | |
312 | -err_mdiobus_reg: | |
313 | - platform_device_unregister(pdev); | |
314 | -err_pdev: | |
315 | - return ret; | |
316 | -} | |
317 | -module_init(fixed_mdio_bus_init); | |
318 | - | |
319 | -static void __exit fixed_mdio_bus_exit(void) | |
320 | -{ | |
321 | - struct fixed_mdio_bus *fmb = &platform_fmb; | |
322 | - struct fixed_phy *fp, *tmp; | |
323 | - | |
324 | - mdiobus_unregister(fmb->mii_bus); | |
325 | - mdiobus_free(fmb->mii_bus); | |
326 | - platform_device_unregister(pdev); | |
327 | - | |
328 | - list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { | |
329 | - list_del(&fp->node); | |
330 | - kfree(fp); | |
331 | - } | |
332 | -} | |
333 | -module_exit(fixed_mdio_bus_exit); | |
334 | - | |
335 | -MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); | |
336 | -MODULE_AUTHOR("Vitaly Bordug"); | |
337 | -MODULE_LICENSE("GPL"); |
drivers/net/phy/fixed_phy.c
1 | +/* | |
2 | + * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) | |
3 | + * | |
4 | + * Author: Vitaly Bordug <vbordug@ru.mvista.com> | |
5 | + * Anton Vorontsov <avorontsov@ru.mvista.com> | |
6 | + * | |
7 | + * Copyright (c) 2006-2007 MontaVista Software, Inc. | |
8 | + * | |
9 | + * This program is free software; you can redistribute it and/or modify it | |
10 | + * under the terms of the GNU General Public License as published by the | |
11 | + * Free Software Foundation; either version 2 of the License, or (at your | |
12 | + * option) any later version. | |
13 | + */ | |
14 | + | |
15 | +#include <linux/kernel.h> | |
16 | +#include <linux/module.h> | |
17 | +#include <linux/platform_device.h> | |
18 | +#include <linux/list.h> | |
19 | +#include <linux/mii.h> | |
20 | +#include <linux/phy.h> | |
21 | +#include <linux/phy_fixed.h> | |
22 | +#include <linux/err.h> | |
23 | +#include <linux/slab.h> | |
24 | +#include <linux/of.h> | |
25 | + | |
26 | +#define MII_REGS_NUM 29 | |
27 | + | |
28 | +struct fixed_mdio_bus { | |
29 | + int irqs[PHY_MAX_ADDR]; | |
30 | + struct mii_bus *mii_bus; | |
31 | + struct list_head phys; | |
32 | +}; | |
33 | + | |
34 | +struct fixed_phy { | |
35 | + int addr; | |
36 | + u16 regs[MII_REGS_NUM]; | |
37 | + struct phy_device *phydev; | |
38 | + struct fixed_phy_status status; | |
39 | + int (*link_update)(struct net_device *, struct fixed_phy_status *); | |
40 | + struct list_head node; | |
41 | +}; | |
42 | + | |
43 | +static struct platform_device *pdev; | |
44 | +static struct fixed_mdio_bus platform_fmb = { | |
45 | + .phys = LIST_HEAD_INIT(platform_fmb.phys), | |
46 | +}; | |
47 | + | |
48 | +static int fixed_phy_update_regs(struct fixed_phy *fp) | |
49 | +{ | |
50 | + u16 bmsr = BMSR_ANEGCAPABLE; | |
51 | + u16 bmcr = 0; | |
52 | + u16 lpagb = 0; | |
53 | + u16 lpa = 0; | |
54 | + | |
55 | + if (fp->status.duplex) { | |
56 | + bmcr |= BMCR_FULLDPLX; | |
57 | + | |
58 | + switch (fp->status.speed) { | |
59 | + case 1000: | |
60 | + bmsr |= BMSR_ESTATEN; | |
61 | + bmcr |= BMCR_SPEED1000; | |
62 | + lpagb |= LPA_1000FULL; | |
63 | + break; | |
64 | + case 100: | |
65 | + bmsr |= BMSR_100FULL; | |
66 | + bmcr |= BMCR_SPEED100; | |
67 | + lpa |= LPA_100FULL; | |
68 | + break; | |
69 | + case 10: | |
70 | + bmsr |= BMSR_10FULL; | |
71 | + lpa |= LPA_10FULL; | |
72 | + break; | |
73 | + default: | |
74 | + pr_warn("fixed phy: unknown speed\n"); | |
75 | + return -EINVAL; | |
76 | + } | |
77 | + } else { | |
78 | + switch (fp->status.speed) { | |
79 | + case 1000: | |
80 | + bmsr |= BMSR_ESTATEN; | |
81 | + bmcr |= BMCR_SPEED1000; | |
82 | + lpagb |= LPA_1000HALF; | |
83 | + break; | |
84 | + case 100: | |
85 | + bmsr |= BMSR_100HALF; | |
86 | + bmcr |= BMCR_SPEED100; | |
87 | + lpa |= LPA_100HALF; | |
88 | + break; | |
89 | + case 10: | |
90 | + bmsr |= BMSR_10HALF; | |
91 | + lpa |= LPA_10HALF; | |
92 | + break; | |
93 | + default: | |
94 | + pr_warn("fixed phy: unknown speed\n"); | |
95 | + return -EINVAL; | |
96 | + } | |
97 | + } | |
98 | + | |
99 | + if (fp->status.link) | |
100 | + bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE; | |
101 | + | |
102 | + if (fp->status.pause) | |
103 | + lpa |= LPA_PAUSE_CAP; | |
104 | + | |
105 | + if (fp->status.asym_pause) | |
106 | + lpa |= LPA_PAUSE_ASYM; | |
107 | + | |
108 | + fp->regs[MII_PHYSID1] = 0; | |
109 | + fp->regs[MII_PHYSID2] = 0; | |
110 | + | |
111 | + fp->regs[MII_BMSR] = bmsr; | |
112 | + fp->regs[MII_BMCR] = bmcr; | |
113 | + fp->regs[MII_LPA] = lpa; | |
114 | + fp->regs[MII_STAT1000] = lpagb; | |
115 | + | |
116 | + return 0; | |
117 | +} | |
118 | + | |
119 | +static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) | |
120 | +{ | |
121 | + struct fixed_mdio_bus *fmb = bus->priv; | |
122 | + struct fixed_phy *fp; | |
123 | + | |
124 | + if (reg_num >= MII_REGS_NUM) | |
125 | + return -1; | |
126 | + | |
127 | + /* We do not support emulating Clause 45 over Clause 22 register reads | |
128 | + * return an error instead of bogus data. | |
129 | + */ | |
130 | + switch (reg_num) { | |
131 | + case MII_MMD_CTRL: | |
132 | + case MII_MMD_DATA: | |
133 | + return -1; | |
134 | + default: | |
135 | + break; | |
136 | + } | |
137 | + | |
138 | + list_for_each_entry(fp, &fmb->phys, node) { | |
139 | + if (fp->addr == phy_addr) { | |
140 | + /* Issue callback if user registered it. */ | |
141 | + if (fp->link_update) { | |
142 | + fp->link_update(fp->phydev->attached_dev, | |
143 | + &fp->status); | |
144 | + fixed_phy_update_regs(fp); | |
145 | + } | |
146 | + return fp->regs[reg_num]; | |
147 | + } | |
148 | + } | |
149 | + | |
150 | + return 0xFFFF; | |
151 | +} | |
152 | + | |
153 | +static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, | |
154 | + u16 val) | |
155 | +{ | |
156 | + return 0; | |
157 | +} | |
158 | + | |
159 | +/* | |
160 | + * If something weird is required to be done with link/speed, | |
161 | + * network driver is able to assign a function to implement this. | |
162 | + * May be useful for PHY's that need to be software-driven. | |
163 | + */ | |
164 | +int fixed_phy_set_link_update(struct phy_device *phydev, | |
165 | + int (*link_update)(struct net_device *, | |
166 | + struct fixed_phy_status *)) | |
167 | +{ | |
168 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
169 | + struct fixed_phy *fp; | |
170 | + | |
171 | + if (!link_update || !phydev || !phydev->bus) | |
172 | + return -EINVAL; | |
173 | + | |
174 | + list_for_each_entry(fp, &fmb->phys, node) { | |
175 | + if (fp->addr == phydev->addr) { | |
176 | + fp->link_update = link_update; | |
177 | + fp->phydev = phydev; | |
178 | + return 0; | |
179 | + } | |
180 | + } | |
181 | + | |
182 | + return -ENOENT; | |
183 | +} | |
184 | +EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); | |
185 | + | |
186 | +int fixed_phy_add(unsigned int irq, int phy_addr, | |
187 | + struct fixed_phy_status *status) | |
188 | +{ | |
189 | + int ret; | |
190 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
191 | + struct fixed_phy *fp; | |
192 | + | |
193 | + fp = kzalloc(sizeof(*fp), GFP_KERNEL); | |
194 | + if (!fp) | |
195 | + return -ENOMEM; | |
196 | + | |
197 | + memset(fp->regs, 0xFF, sizeof(fp->regs[0]) * MII_REGS_NUM); | |
198 | + | |
199 | + fmb->irqs[phy_addr] = irq; | |
200 | + | |
201 | + fp->addr = phy_addr; | |
202 | + fp->status = *status; | |
203 | + | |
204 | + ret = fixed_phy_update_regs(fp); | |
205 | + if (ret) | |
206 | + goto err_regs; | |
207 | + | |
208 | + list_add_tail(&fp->node, &fmb->phys); | |
209 | + | |
210 | + return 0; | |
211 | + | |
212 | +err_regs: | |
213 | + kfree(fp); | |
214 | + return ret; | |
215 | +} | |
216 | +EXPORT_SYMBOL_GPL(fixed_phy_add); | |
217 | + | |
218 | +void fixed_phy_del(int phy_addr) | |
219 | +{ | |
220 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
221 | + struct fixed_phy *fp, *tmp; | |
222 | + | |
223 | + list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { | |
224 | + if (fp->addr == phy_addr) { | |
225 | + list_del(&fp->node); | |
226 | + kfree(fp); | |
227 | + return; | |
228 | + } | |
229 | + } | |
230 | +} | |
231 | +EXPORT_SYMBOL_GPL(fixed_phy_del); | |
232 | + | |
233 | +static int phy_fixed_addr; | |
234 | +static DEFINE_SPINLOCK(phy_fixed_addr_lock); | |
235 | + | |
236 | +struct phy_device *fixed_phy_register(unsigned int irq, | |
237 | + struct fixed_phy_status *status, | |
238 | + struct device_node *np) | |
239 | +{ | |
240 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
241 | + struct phy_device *phy; | |
242 | + int phy_addr; | |
243 | + int ret; | |
244 | + | |
245 | + /* Get the next available PHY address, up to PHY_MAX_ADDR */ | |
246 | + spin_lock(&phy_fixed_addr_lock); | |
247 | + if (phy_fixed_addr == PHY_MAX_ADDR) { | |
248 | + spin_unlock(&phy_fixed_addr_lock); | |
249 | + return ERR_PTR(-ENOSPC); | |
250 | + } | |
251 | + phy_addr = phy_fixed_addr++; | |
252 | + spin_unlock(&phy_fixed_addr_lock); | |
253 | + | |
254 | + ret = fixed_phy_add(PHY_POLL, phy_addr, status); | |
255 | + if (ret < 0) | |
256 | + return ERR_PTR(ret); | |
257 | + | |
258 | + phy = get_phy_device(fmb->mii_bus, phy_addr, false); | |
259 | + if (!phy || IS_ERR(phy)) { | |
260 | + fixed_phy_del(phy_addr); | |
261 | + return ERR_PTR(-EINVAL); | |
262 | + } | |
263 | + | |
264 | + of_node_get(np); | |
265 | + phy->dev.of_node = np; | |
266 | + | |
267 | + ret = phy_device_register(phy); | |
268 | + if (ret) { | |
269 | + phy_device_free(phy); | |
270 | + of_node_put(np); | |
271 | + fixed_phy_del(phy_addr); | |
272 | + return ERR_PTR(ret); | |
273 | + } | |
274 | + | |
275 | + return phy; | |
276 | +} | |
277 | +EXPORT_SYMBOL_GPL(fixed_phy_register); | |
278 | + | |
279 | +static int __init fixed_mdio_bus_init(void) | |
280 | +{ | |
281 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
282 | + int ret; | |
283 | + | |
284 | + pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0); | |
285 | + if (IS_ERR(pdev)) { | |
286 | + ret = PTR_ERR(pdev); | |
287 | + goto err_pdev; | |
288 | + } | |
289 | + | |
290 | + fmb->mii_bus = mdiobus_alloc(); | |
291 | + if (fmb->mii_bus == NULL) { | |
292 | + ret = -ENOMEM; | |
293 | + goto err_mdiobus_reg; | |
294 | + } | |
295 | + | |
296 | + snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0"); | |
297 | + fmb->mii_bus->name = "Fixed MDIO Bus"; | |
298 | + fmb->mii_bus->priv = fmb; | |
299 | + fmb->mii_bus->parent = &pdev->dev; | |
300 | + fmb->mii_bus->read = &fixed_mdio_read; | |
301 | + fmb->mii_bus->write = &fixed_mdio_write; | |
302 | + fmb->mii_bus->irq = fmb->irqs; | |
303 | + | |
304 | + ret = mdiobus_register(fmb->mii_bus); | |
305 | + if (ret) | |
306 | + goto err_mdiobus_alloc; | |
307 | + | |
308 | + return 0; | |
309 | + | |
310 | +err_mdiobus_alloc: | |
311 | + mdiobus_free(fmb->mii_bus); | |
312 | +err_mdiobus_reg: | |
313 | + platform_device_unregister(pdev); | |
314 | +err_pdev: | |
315 | + return ret; | |
316 | +} | |
317 | +module_init(fixed_mdio_bus_init); | |
318 | + | |
319 | +static void __exit fixed_mdio_bus_exit(void) | |
320 | +{ | |
321 | + struct fixed_mdio_bus *fmb = &platform_fmb; | |
322 | + struct fixed_phy *fp, *tmp; | |
323 | + | |
324 | + mdiobus_unregister(fmb->mii_bus); | |
325 | + mdiobus_free(fmb->mii_bus); | |
326 | + platform_device_unregister(pdev); | |
327 | + | |
328 | + list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { | |
329 | + list_del(&fp->node); | |
330 | + kfree(fp); | |
331 | + } | |
332 | +} | |
333 | +module_exit(fixed_mdio_bus_exit); | |
334 | + | |
335 | +MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)"); | |
336 | +MODULE_AUTHOR("Vitaly Bordug"); | |
337 | +MODULE_LICENSE("GPL"); |
include/linux/phy_fixed.h