diff options
author | Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | 2008-11-13 19:24:34 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-11-16 21:59:45 -0500 |
commit | f004f3ea34209d8b836426b26ade3dc502631b18 (patch) | |
tree | bf58002618bc3dd98db2416bb20cbeb016ecdd23 /drivers | |
parent | 72af187f216ed83c77ca3e6f22dfc9caf72e9347 (diff) |
phylib: make mdio-gpio work without OF (v4)
make mdio-gpio work with non OpenFirmware gpio implementation.
Aditional changes to mdio-gpio:
- use gpio_request() and gpio_free()
- place irq[] array in struct mdio_gpio_info
- add module description, author and license
- add note about compiling this driver as module
- rename mdc and mdio function (were ugly names)
- change MII to MDIO in bus name
- add __init __exit to module (un)loading functions
- probe fails if no phys added to the bus
- kzalloc bitbang with sizeof(*bitbang)
Changes since v3:
- keep bus naming "%x" to be compatible with existing drivers.
Changes since v2:
- more #ifdefs reduction
- platform driver will be registered on OF platforms also
- unified platform and OF bus_id to phy%i
Changes since v1:
- removed NO_IRQ
- reduced #idefs
Laurent, please test this driver under OF.
Signed-off-by: Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/net/phy/Kconfig | 5 | ||||
-rw-r--r-- | drivers/net/phy/mdio-gpio.c | 232 |
2 files changed, 166 insertions, 71 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 031807751991..c4c5a2f8ec74 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig | |||
@@ -86,8 +86,11 @@ config MDIO_BITBANG | |||
86 | 86 | ||
87 | config MDIO_GPIO | 87 | config MDIO_GPIO |
88 | tristate "Support for GPIO lib-based bitbanged MDIO buses" | 88 | tristate "Support for GPIO lib-based bitbanged MDIO buses" |
89 | depends on MDIO_BITBANG && OF_GPIO | 89 | depends on MDIO_BITBANG && GENERIC_GPIO |
90 | ---help--- | 90 | ---help--- |
91 | Supports GPIO lib-based MDIO busses. | 91 | Supports GPIO lib-based MDIO busses. |
92 | 92 | ||
93 | To compile this driver as a module, choose M here: the module | ||
94 | will be called mdio-gpio. | ||
95 | |||
93 | endif # PHYLIB | 96 | endif # PHYLIB |
diff --git a/drivers/net/phy/mdio-gpio.c b/drivers/net/phy/mdio-gpio.c index 2ff97754e574..a439ebeb4319 100644 --- a/drivers/net/phy/mdio-gpio.c +++ b/drivers/net/phy/mdio-gpio.c | |||
@@ -1,9 +1,12 @@ | |||
1 | /* | 1 | /* |
2 | * OpenFirmware GPIO based MDIO bitbang driver. | 2 | * GPIO based MDIO bitbang driver. |
3 | * Supports OpenFirmware. | ||
3 | * | 4 | * |
4 | * Copyright (c) 2008 CSE Semaphore Belgium. | 5 | * Copyright (c) 2008 CSE Semaphore Belgium. |
5 | * by Laurent Pinchart <laurentp@cse-semaphore.com> | 6 | * by Laurent Pinchart <laurentp@cse-semaphore.com> |
6 | * | 7 | * |
8 | * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | ||
9 | * | ||
7 | * Based on earlier work by | 10 | * Based on earlier work by |
8 | * | 11 | * |
9 | * Copyright (c) 2003 Intracom S.A. | 12 | * Copyright (c) 2003 Intracom S.A. |
@@ -21,9 +24,14 @@ | |||
21 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
22 | #include <linux/init.h> | 25 | #include <linux/init.h> |
23 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
24 | #include <linux/mdio-bitbang.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/gpio.h> | ||
29 | #include <linux/mdio-gpio.h> | ||
30 | |||
31 | #ifdef CONFIG_OF_GPIO | ||
25 | #include <linux/of_gpio.h> | 32 | #include <linux/of_gpio.h> |
26 | #include <linux/of_platform.h> | 33 | #include <linux/of_platform.h> |
34 | #endif | ||
27 | 35 | ||
28 | struct mdio_gpio_info { | 36 | struct mdio_gpio_info { |
29 | struct mdiobb_ctrl ctrl; | 37 | struct mdiobb_ctrl ctrl; |
@@ -41,7 +49,7 @@ static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) | |||
41 | gpio_direction_input(bitbang->mdio); | 49 | gpio_direction_input(bitbang->mdio); |
42 | } | 50 | } |
43 | 51 | ||
44 | static int mdio_read(struct mdiobb_ctrl *ctrl) | 52 | static int mdio_get(struct mdiobb_ctrl *ctrl) |
45 | { | 53 | { |
46 | struct mdio_gpio_info *bitbang = | 54 | struct mdio_gpio_info *bitbang = |
47 | container_of(ctrl, struct mdio_gpio_info, ctrl); | 55 | container_of(ctrl, struct mdio_gpio_info, ctrl); |
@@ -49,7 +57,7 @@ static int mdio_read(struct mdiobb_ctrl *ctrl) | |||
49 | return gpio_get_value(bitbang->mdio); | 57 | return gpio_get_value(bitbang->mdio); |
50 | } | 58 | } |
51 | 59 | ||
52 | static void mdio(struct mdiobb_ctrl *ctrl, int what) | 60 | static void mdio_set(struct mdiobb_ctrl *ctrl, int what) |
53 | { | 61 | { |
54 | struct mdio_gpio_info *bitbang = | 62 | struct mdio_gpio_info *bitbang = |
55 | container_of(ctrl, struct mdio_gpio_info, ctrl); | 63 | container_of(ctrl, struct mdio_gpio_info, ctrl); |
@@ -57,7 +65,7 @@ static void mdio(struct mdiobb_ctrl *ctrl, int what) | |||
57 | gpio_set_value(bitbang->mdio, what); | 65 | gpio_set_value(bitbang->mdio, what); |
58 | } | 66 | } |
59 | 67 | ||
60 | static void mdc(struct mdiobb_ctrl *ctrl, int what) | 68 | static void mdc_set(struct mdiobb_ctrl *ctrl, int what) |
61 | { | 69 | { |
62 | struct mdio_gpio_info *bitbang = | 70 | struct mdio_gpio_info *bitbang = |
63 | container_of(ctrl, struct mdio_gpio_info, ctrl); | 71 | container_of(ctrl, struct mdio_gpio_info, ctrl); |
@@ -67,93 +75,69 @@ static void mdc(struct mdiobb_ctrl *ctrl, int what) | |||
67 | 75 | ||
68 | static struct mdiobb_ops mdio_gpio_ops = { | 76 | static struct mdiobb_ops mdio_gpio_ops = { |
69 | .owner = THIS_MODULE, | 77 | .owner = THIS_MODULE, |
70 | .set_mdc = mdc, | 78 | .set_mdc = mdc_set, |
71 | .set_mdio_dir = mdio_dir, | 79 | .set_mdio_dir = mdio_dir, |
72 | .set_mdio_data = mdio, | 80 | .set_mdio_data = mdio_set, |
73 | .get_mdio_data = mdio_read, | 81 | .get_mdio_data = mdio_get, |
74 | }; | 82 | }; |
75 | 83 | ||
76 | static int __devinit mdio_ofgpio_bitbang_init(struct mii_bus *bus, | 84 | static int __devinit mdio_gpio_bus_init(struct device *dev, |
77 | struct device_node *np) | 85 | struct mdio_gpio_platform_data *pdata, |
78 | { | 86 | int bus_id) |
79 | struct mdio_gpio_info *bitbang = bus->priv; | ||
80 | |||
81 | bitbang->mdc = of_get_gpio(np, 0); | ||
82 | bitbang->mdio = of_get_gpio(np, 1); | ||
83 | |||
84 | if (bitbang->mdc < 0 || bitbang->mdio < 0) | ||
85 | return -ENODEV; | ||
86 | |||
87 | snprintf(bus->id, MII_BUS_ID_SIZE, "%x", bitbang->mdc); | ||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) | ||
92 | { | 87 | { |
93 | const u32 *data; | ||
94 | int len, id, irq; | ||
95 | |||
96 | data = of_get_property(np, "reg", &len); | ||
97 | if (!data || len != 4) | ||
98 | return; | ||
99 | |||
100 | id = *data; | ||
101 | bus->phy_mask &= ~(1 << id); | ||
102 | |||
103 | irq = of_irq_to_resource(np, 0, NULL); | ||
104 | if (irq != NO_IRQ) | ||
105 | bus->irq[id] = irq; | ||
106 | } | ||
107 | |||
108 | static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, | ||
109 | const struct of_device_id *match) | ||
110 | { | ||
111 | struct device_node *np = NULL; | ||
112 | struct mii_bus *new_bus; | 88 | struct mii_bus *new_bus; |
113 | struct mdio_gpio_info *bitbang; | 89 | struct mdio_gpio_info *bitbang; |
114 | int ret = -ENOMEM; | 90 | int ret = -ENOMEM; |
115 | int i; | 91 | int i; |
116 | 92 | ||
117 | bitbang = kzalloc(sizeof(struct mdio_gpio_info), GFP_KERNEL); | 93 | bitbang = kzalloc(sizeof(*bitbang), GFP_KERNEL); |
118 | if (!bitbang) | 94 | if (!bitbang) |
119 | goto out; | 95 | goto out; |
120 | 96 | ||
121 | bitbang->ctrl.ops = &mdio_gpio_ops; | 97 | bitbang->ctrl.ops = &mdio_gpio_ops; |
98 | bitbang->mdc = pdata->mdc; | ||
99 | bitbang->mdio = pdata->mdio; | ||
122 | 100 | ||
123 | new_bus = alloc_mdio_bitbang(&bitbang->ctrl); | 101 | new_bus = alloc_mdio_bitbang(&bitbang->ctrl); |
124 | if (!new_bus) | 102 | if (!new_bus) |
125 | goto out_free_bitbang; | 103 | goto out_free_bitbang; |
126 | 104 | ||
127 | new_bus->name = "GPIO Bitbanged MII", | 105 | new_bus->name = "GPIO Bitbanged MDIO", |
128 | 106 | ||
129 | ret = mdio_ofgpio_bitbang_init(new_bus, ofdev->node); | 107 | ret = -ENODEV; |
130 | if (ret) | ||
131 | goto out_free_bus; | ||
132 | 108 | ||
133 | new_bus->phy_mask = ~0; | 109 | new_bus->phy_mask = pdata->phy_mask; |
134 | new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); | 110 | new_bus->irq = pdata->irqs; |
135 | if (!new_bus->irq) | 111 | new_bus->parent = dev; |
112 | |||
113 | if (new_bus->phy_mask == ~0) | ||
136 | goto out_free_bus; | 114 | goto out_free_bus; |
137 | 115 | ||
138 | for (i = 0; i < PHY_MAX_ADDR; i++) | 116 | for (i = 0; i < PHY_MAX_ADDR; i++) |
139 | new_bus->irq[i] = -1; | 117 | if (!new_bus->irq[i]) |
118 | new_bus->irq[i] = PHY_POLL; | ||
140 | 119 | ||
141 | while ((np = of_get_next_child(ofdev->node, np))) | 120 | snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", bus_id); |
142 | if (!strcmp(np->type, "ethernet-phy")) | 121 | |
143 | add_phy(new_bus, np); | 122 | if (gpio_request(bitbang->mdc, "mdc")) |
123 | goto out_free_bus; | ||
144 | 124 | ||
145 | new_bus->parent = &ofdev->dev; | 125 | if (gpio_request(bitbang->mdio, "mdio")) |
146 | dev_set_drvdata(&ofdev->dev, new_bus); | 126 | goto out_free_mdc; |
127 | |||
128 | dev_set_drvdata(dev, new_bus); | ||
147 | 129 | ||
148 | ret = mdiobus_register(new_bus); | 130 | ret = mdiobus_register(new_bus); |
149 | if (ret) | 131 | if (ret) |
150 | goto out_free_irqs; | 132 | goto out_free_all; |
151 | 133 | ||
152 | return 0; | 134 | return 0; |
153 | 135 | ||
154 | out_free_irqs: | 136 | out_free_all: |
155 | dev_set_drvdata(&ofdev->dev, NULL); | 137 | dev_set_drvdata(dev, NULL); |
156 | kfree(new_bus->irq); | 138 | gpio_free(bitbang->mdio); |
139 | out_free_mdc: | ||
140 | gpio_free(bitbang->mdc); | ||
157 | out_free_bus: | 141 | out_free_bus: |
158 | free_mdio_bitbang(new_bus); | 142 | free_mdio_bitbang(new_bus); |
159 | out_free_bitbang: | 143 | out_free_bitbang: |
@@ -162,16 +146,86 @@ out: | |||
162 | return ret; | 146 | return ret; |
163 | } | 147 | } |
164 | 148 | ||
165 | static int mdio_ofgpio_remove(struct of_device *ofdev) | 149 | static void __devexit mdio_gpio_bus_destroy(struct device *dev) |
166 | { | 150 | { |
167 | struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); | 151 | struct mii_bus *bus = dev_get_drvdata(dev); |
168 | struct mdio_gpio_info *bitbang = bus->priv; | 152 | struct mdio_gpio_info *bitbang = bus->priv; |
169 | 153 | ||
170 | mdiobus_unregister(bus); | 154 | mdiobus_unregister(bus); |
171 | kfree(bus->irq); | ||
172 | free_mdio_bitbang(bus); | 155 | free_mdio_bitbang(bus); |
173 | dev_set_drvdata(&ofdev->dev, NULL); | 156 | dev_set_drvdata(dev, NULL); |
157 | gpio_free(bitbang->mdc); | ||
158 | gpio_free(bitbang->mdio); | ||
174 | kfree(bitbang); | 159 | kfree(bitbang); |
160 | } | ||
161 | |||
162 | static int __devinit mdio_gpio_probe(struct platform_device *pdev) | ||
163 | { | ||
164 | struct mdio_gpio_platform_data *pdata = pdev->dev.platform_data; | ||
165 | |||
166 | if (!pdata) | ||
167 | return -ENODEV; | ||
168 | |||
169 | return mdio_gpio_bus_init(&pdev->dev, pdata, pdev->id); | ||
170 | } | ||
171 | |||
172 | static int __devexit mdio_gpio_remove(struct platform_device *pdev) | ||
173 | { | ||
174 | mdio_gpio_bus_destroy(&pdev->dev); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | #ifdef CONFIG_OF_GPIO | ||
180 | static void __devinit add_phy(struct mdio_gpio_platform_data *pdata, | ||
181 | struct device_node *np) | ||
182 | { | ||
183 | const u32 *data; | ||
184 | int len, id, irq; | ||
185 | |||
186 | data = of_get_property(np, "reg", &len); | ||
187 | if (!data || len != 4) | ||
188 | return; | ||
189 | |||
190 | id = *data; | ||
191 | pdata->phy_mask &= ~(1 << id); | ||
192 | |||
193 | irq = of_irq_to_resource(np, 0, NULL); | ||
194 | if (irq) | ||
195 | pdata->irqs[id] = irq; | ||
196 | } | ||
197 | |||
198 | static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, | ||
199 | const struct of_device_id *match) | ||
200 | { | ||
201 | struct device_node *np = NULL; | ||
202 | struct mdio_gpio_platform_data *pdata; | ||
203 | |||
204 | pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); | ||
205 | if (!pdata) | ||
206 | return -ENOMEM; | ||
207 | |||
208 | pdata->mdc = of_get_gpio(ofdev->node, 0); | ||
209 | pdata->mdio = of_get_gpio(ofdev->node, 1); | ||
210 | |||
211 | if (pdata->mdc < 0 || pdata->mdio < 0) | ||
212 | goto out_free; | ||
213 | |||
214 | while ((np = of_get_next_child(ofdev->node, np))) | ||
215 | if (!strcmp(np->type, "ethernet-phy")) | ||
216 | add_phy(pdata, np); | ||
217 | |||
218 | return mdio_gpio_bus_init(&ofdev->dev, pdata, pdata->mdc); | ||
219 | |||
220 | out_free: | ||
221 | kfree(pdata); | ||
222 | return -ENODEV; | ||
223 | } | ||
224 | |||
225 | static int __devexit mdio_ofgpio_remove(struct of_device *ofdev) | ||
226 | { | ||
227 | mdio_gpio_bus_destroy(&ofdev->dev); | ||
228 | kfree(ofdev->dev.platform_data); | ||
175 | 229 | ||
176 | return 0; | 230 | return 0; |
177 | } | 231 | } |
@@ -187,18 +241,56 @@ static struct of_platform_driver mdio_ofgpio_driver = { | |||
187 | .name = "mdio-gpio", | 241 | .name = "mdio-gpio", |
188 | .match_table = mdio_ofgpio_match, | 242 | .match_table = mdio_ofgpio_match, |
189 | .probe = mdio_ofgpio_probe, | 243 | .probe = mdio_ofgpio_probe, |
190 | .remove = mdio_ofgpio_remove, | 244 | .remove = __devexit_p(mdio_ofgpio_remove), |
191 | }; | 245 | }; |
192 | 246 | ||
193 | static int mdio_ofgpio_init(void) | 247 | static inline int __init mdio_ofgpio_init(void) |
194 | { | 248 | { |
195 | return of_register_platform_driver(&mdio_ofgpio_driver); | 249 | return of_register_platform_driver(&mdio_ofgpio_driver); |
196 | } | 250 | } |
197 | 251 | ||
198 | static void mdio_ofgpio_exit(void) | 252 | static inline void __exit mdio_ofgpio_exit(void) |
199 | { | 253 | { |
200 | of_unregister_platform_driver(&mdio_ofgpio_driver); | 254 | of_unregister_platform_driver(&mdio_ofgpio_driver); |
201 | } | 255 | } |
256 | #else | ||
257 | static inline int __init mdio_ofgpio_init(void) { return 0; } | ||
258 | static inline void __exit mdio_ofgpio_exit(void) { } | ||
259 | #endif /* CONFIG_OF_GPIO */ | ||
260 | |||
261 | static struct platform_driver mdio_gpio_driver = { | ||
262 | .probe = mdio_gpio_probe, | ||
263 | .remove = __devexit_p(mdio_gpio_remove), | ||
264 | .driver = { | ||
265 | .name = "mdio-gpio", | ||
266 | .owner = THIS_MODULE, | ||
267 | }, | ||
268 | }; | ||
269 | |||
270 | static int __init mdio_gpio_init(void) | ||
271 | { | ||
272 | int ret; | ||
273 | |||
274 | ret = mdio_ofgpio_init(); | ||
275 | if (ret) | ||
276 | return ret; | ||
277 | |||
278 | ret = platform_driver_register(&mdio_gpio_driver); | ||
279 | if (ret) | ||
280 | mdio_ofgpio_exit(); | ||
281 | |||
282 | return ret; | ||
283 | } | ||
284 | module_init(mdio_gpio_init); | ||
285 | |||
286 | static void __exit mdio_gpio_exit(void) | ||
287 | { | ||
288 | platform_driver_unregister(&mdio_gpio_driver); | ||
289 | mdio_ofgpio_exit(); | ||
290 | } | ||
291 | module_exit(mdio_gpio_exit); | ||
202 | 292 | ||
203 | module_init(mdio_ofgpio_init); | 293 | MODULE_ALIAS("platform:mdio-gpio"); |
204 | module_exit(mdio_ofgpio_exit); | 294 | MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas"); |
295 | MODULE_LICENSE("GPL"); | ||
296 | MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO"); | ||