diff options
Diffstat (limited to 'drivers/net/gianfar_mii.c')
-rw-r--r-- | drivers/net/gianfar_mii.c | 212 |
1 files changed, 144 insertions, 68 deletions
diff --git a/drivers/net/gianfar_mii.c b/drivers/net/gianfar_mii.c index 0e2595d24933..f3706e415b45 100644 --- a/drivers/net/gianfar_mii.c +++ b/drivers/net/gianfar_mii.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/crc32.h> | 34 | #include <linux/crc32.h> |
35 | #include <linux/mii.h> | 35 | #include <linux/mii.h> |
36 | #include <linux/phy.h> | 36 | #include <linux/phy.h> |
37 | #include <linux/of.h> | ||
38 | #include <linux/of_platform.h> | ||
37 | 39 | ||
38 | #include <asm/io.h> | 40 | #include <asm/io.h> |
39 | #include <asm/irq.h> | 41 | #include <asm/irq.h> |
@@ -150,19 +152,83 @@ static int gfar_mdio_reset(struct mii_bus *bus) | |||
150 | return 0; | 152 | return 0; |
151 | } | 153 | } |
152 | 154 | ||
155 | /* Allocate an array which provides irq #s for each PHY on the given bus */ | ||
156 | static int *create_irq_map(struct device_node *np) | ||
157 | { | ||
158 | int *irqs; | ||
159 | int i; | ||
160 | struct device_node *child = NULL; | ||
161 | |||
162 | irqs = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL); | ||
163 | |||
164 | if (!irqs) | ||
165 | return NULL; | ||
166 | |||
167 | for (i = 0; i < PHY_MAX_ADDR; i++) | ||
168 | irqs[i] = PHY_POLL; | ||
169 | |||
170 | while ((child = of_get_next_child(np, child)) != NULL) { | ||
171 | int irq = irq_of_parse_and_map(child, 0); | ||
172 | const u32 *id; | ||
173 | |||
174 | if (irq == NO_IRQ) | ||
175 | continue; | ||
176 | |||
177 | id = of_get_property(child, "reg", NULL); | ||
178 | |||
179 | if (!id) | ||
180 | continue; | ||
181 | |||
182 | if (*id < PHY_MAX_ADDR && *id >= 0) | ||
183 | irqs[*id] = irq; | ||
184 | else | ||
185 | printk(KERN_WARNING "%s: " | ||
186 | "%d is not a valid PHY address\n", | ||
187 | np->full_name, *id); | ||
188 | } | ||
189 | |||
190 | return irqs; | ||
191 | } | ||
192 | |||
193 | |||
194 | void gfar_mdio_bus_name(char *name, struct device_node *np) | ||
195 | { | ||
196 | const u32 *reg; | ||
197 | |||
198 | reg = of_get_property(np, "reg", NULL); | ||
153 | 199 | ||
154 | static int gfar_mdio_probe(struct device *dev) | 200 | snprintf(name, MII_BUS_ID_SIZE, "%s@%x", np->name, reg ? *reg : 0); |
201 | } | ||
202 | |||
203 | /* Scan the bus in reverse, looking for an empty spot */ | ||
204 | static int gfar_mdio_find_free(struct mii_bus *new_bus) | ||
205 | { | ||
206 | int i; | ||
207 | |||
208 | for (i = PHY_MAX_ADDR; i > 0; i--) { | ||
209 | u32 phy_id; | ||
210 | |||
211 | if (get_phy_id(new_bus, i, &phy_id)) | ||
212 | return -1; | ||
213 | |||
214 | if (phy_id == 0xffffffff) | ||
215 | break; | ||
216 | } | ||
217 | |||
218 | return i; | ||
219 | } | ||
220 | |||
221 | static int gfar_mdio_probe(struct of_device *ofdev, | ||
222 | const struct of_device_id *match) | ||
155 | { | 223 | { |
156 | struct platform_device *pdev = to_platform_device(dev); | ||
157 | struct gianfar_mdio_data *pdata; | ||
158 | struct gfar_mii __iomem *regs; | 224 | struct gfar_mii __iomem *regs; |
159 | struct gfar __iomem *enet_regs; | 225 | struct gfar __iomem *enet_regs; |
160 | struct mii_bus *new_bus; | 226 | struct mii_bus *new_bus; |
161 | struct resource *r; | 227 | int err = 0; |
162 | int i, err = 0; | 228 | u64 addr, size; |
163 | 229 | struct device_node *np = ofdev->node; | |
164 | if (NULL == dev) | 230 | struct device_node *tbi; |
165 | return -EINVAL; | 231 | int tbiaddr = -1; |
166 | 232 | ||
167 | new_bus = mdiobus_alloc(); | 233 | new_bus = mdiobus_alloc(); |
168 | if (NULL == new_bus) | 234 | if (NULL == new_bus) |
@@ -172,31 +238,28 @@ static int gfar_mdio_probe(struct device *dev) | |||
172 | new_bus->read = &gfar_mdio_read, | 238 | new_bus->read = &gfar_mdio_read, |
173 | new_bus->write = &gfar_mdio_write, | 239 | new_bus->write = &gfar_mdio_write, |
174 | new_bus->reset = &gfar_mdio_reset, | 240 | new_bus->reset = &gfar_mdio_reset, |
175 | snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", pdev->id); | 241 | gfar_mdio_bus_name(new_bus->id, np); |
176 | |||
177 | pdata = (struct gianfar_mdio_data *)pdev->dev.platform_data; | ||
178 | |||
179 | if (NULL == pdata) { | ||
180 | printk(KERN_ERR "gfar mdio %d: Missing platform data!\n", pdev->id); | ||
181 | return -ENODEV; | ||
182 | } | ||
183 | |||
184 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
185 | 242 | ||
186 | /* Set the PHY base address */ | 243 | /* Set the PHY base address */ |
187 | regs = ioremap(r->start, sizeof (struct gfar_mii)); | 244 | addr = of_translate_address(np, of_get_address(np, 0, &size, NULL)); |
245 | regs = ioremap(addr, size); | ||
188 | 246 | ||
189 | if (NULL == regs) { | 247 | if (NULL == regs) { |
190 | err = -ENOMEM; | 248 | err = -ENOMEM; |
191 | goto reg_map_fail; | 249 | goto err_free_bus; |
192 | } | 250 | } |
193 | 251 | ||
194 | new_bus->priv = (void __force *)regs; | 252 | new_bus->priv = (void __force *)regs; |
195 | 253 | ||
196 | new_bus->irq = pdata->irq; | 254 | new_bus->irq = create_irq_map(np); |
255 | |||
256 | if (new_bus->irq == NULL) { | ||
257 | err = -ENOMEM; | ||
258 | goto err_unmap_regs; | ||
259 | } | ||
197 | 260 | ||
198 | new_bus->parent = dev; | 261 | new_bus->parent = &ofdev->dev; |
199 | dev_set_drvdata(dev, new_bus); | 262 | dev_set_drvdata(&ofdev->dev, new_bus); |
200 | 263 | ||
201 | /* | 264 | /* |
202 | * This is mildly evil, but so is our hardware for doing this. | 265 | * This is mildly evil, but so is our hardware for doing this. |
@@ -206,96 +269,109 @@ static int gfar_mdio_probe(struct device *dev) | |||
206 | enet_regs = (struct gfar __iomem *) | 269 | enet_regs = (struct gfar __iomem *) |
207 | ((char *)regs - offsetof(struct gfar, gfar_mii_regs)); | 270 | ((char *)regs - offsetof(struct gfar, gfar_mii_regs)); |
208 | 271 | ||
209 | /* Scan the bus, looking for an empty spot for TBIPA */ | 272 | for_each_child_of_node(np, tbi) { |
210 | gfar_write(&enet_regs->tbipa, 0); | 273 | if (!strncmp(tbi->type, "tbi-phy", 8)) |
211 | for (i = PHY_MAX_ADDR; i > 0; i--) { | 274 | break; |
212 | u32 phy_id; | 275 | } |
213 | 276 | ||
214 | err = get_phy_id(new_bus, i, &phy_id); | 277 | if (tbi) { |
215 | if (err) | 278 | const u32 *prop = of_get_property(tbi, "reg", NULL); |
216 | goto bus_register_fail; | ||
217 | 279 | ||
218 | if (phy_id == 0xffffffff) | 280 | if (prop) |
219 | break; | 281 | tbiaddr = *prop; |
220 | } | 282 | } |
221 | 283 | ||
222 | /* The bus is full. We don't support using 31 PHYs, sorry */ | 284 | if (tbiaddr == -1) { |
223 | if (i == 0) { | 285 | gfar_write(&enet_regs->tbipa, 0); |
286 | |||
287 | tbiaddr = gfar_mdio_find_free(new_bus); | ||
288 | } | ||
289 | |||
290 | /* | ||
291 | * We define TBIPA at 0 to be illegal, opting to fail for boards that | ||
292 | * have PHYs at 1-31, rather than change tbipa and rescan. | ||
293 | */ | ||
294 | if (tbiaddr == 0) { | ||
224 | err = -EBUSY; | 295 | err = -EBUSY; |
225 | 296 | ||
226 | goto bus_register_fail; | 297 | goto err_free_irqs; |
227 | } | 298 | } |
228 | 299 | ||
229 | gfar_write(&enet_regs->tbipa, i); | 300 | gfar_write(&enet_regs->tbipa, tbiaddr); |
301 | |||
302 | /* | ||
303 | * The TBIPHY-only buses will find PHYs at every address, | ||
304 | * so we mask them all but the TBI | ||
305 | */ | ||
306 | if (!of_device_is_compatible(np, "fsl,gianfar-mdio")) | ||
307 | new_bus->phy_mask = ~(1 << tbiaddr); | ||
230 | 308 | ||
231 | err = mdiobus_register(new_bus); | 309 | err = mdiobus_register(new_bus); |
232 | 310 | ||
233 | if (0 != err) { | 311 | if (err != 0) { |
234 | printk (KERN_ERR "%s: Cannot register as MDIO bus\n", | 312 | printk (KERN_ERR "%s: Cannot register as MDIO bus\n", |
235 | new_bus->name); | 313 | new_bus->name); |
236 | goto bus_register_fail; | 314 | goto err_free_irqs; |
237 | } | 315 | } |
238 | 316 | ||
239 | return 0; | 317 | return 0; |
240 | 318 | ||
241 | bus_register_fail: | 319 | err_free_irqs: |
320 | kfree(new_bus->irq); | ||
321 | err_unmap_regs: | ||
242 | iounmap(regs); | 322 | iounmap(regs); |
243 | reg_map_fail: | 323 | err_free_bus: |
244 | mdiobus_free(new_bus); | 324 | mdiobus_free(new_bus); |
245 | 325 | ||
246 | return err; | 326 | return err; |
247 | } | 327 | } |
248 | 328 | ||
249 | 329 | ||
250 | static int gfar_mdio_remove(struct device *dev) | 330 | static int gfar_mdio_remove(struct of_device *ofdev) |
251 | { | 331 | { |
252 | struct mii_bus *bus = dev_get_drvdata(dev); | 332 | struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); |
253 | 333 | ||
254 | mdiobus_unregister(bus); | 334 | mdiobus_unregister(bus); |
255 | 335 | ||
256 | dev_set_drvdata(dev, NULL); | 336 | dev_set_drvdata(&ofdev->dev, NULL); |
257 | 337 | ||
258 | iounmap((void __iomem *)bus->priv); | 338 | iounmap((void __iomem *)bus->priv); |
259 | bus->priv = NULL; | 339 | bus->priv = NULL; |
340 | kfree(bus->irq); | ||
260 | mdiobus_free(bus); | 341 | mdiobus_free(bus); |
261 | 342 | ||
262 | return 0; | 343 | return 0; |
263 | } | 344 | } |
264 | 345 | ||
265 | static struct device_driver gianfar_mdio_driver = { | 346 | static struct of_device_id gfar_mdio_match[] = |
347 | { | ||
348 | { | ||
349 | .compatible = "fsl,gianfar-mdio", | ||
350 | }, | ||
351 | { | ||
352 | .compatible = "fsl,gianfar-tbi", | ||
353 | }, | ||
354 | { | ||
355 | .type = "mdio", | ||
356 | .compatible = "gianfar", | ||
357 | }, | ||
358 | {}, | ||
359 | }; | ||
360 | |||
361 | static struct of_platform_driver gianfar_mdio_driver = { | ||
266 | .name = "fsl-gianfar_mdio", | 362 | .name = "fsl-gianfar_mdio", |
267 | .bus = &platform_bus_type, | 363 | .match_table = gfar_mdio_match, |
364 | |||
268 | .probe = gfar_mdio_probe, | 365 | .probe = gfar_mdio_probe, |
269 | .remove = gfar_mdio_remove, | 366 | .remove = gfar_mdio_remove, |
270 | }; | 367 | }; |
271 | 368 | ||
272 | static int match_mdio_bus(struct device *dev, void *data) | ||
273 | { | ||
274 | const struct gfar_private *priv = data; | ||
275 | const struct platform_device *pdev = to_platform_device(dev); | ||
276 | |||
277 | return !strcmp(pdev->name, gianfar_mdio_driver.name) && | ||
278 | pdev->id == priv->einfo->mdio_bus; | ||
279 | } | ||
280 | |||
281 | /* Given a gfar_priv structure, find the mii_bus controlled by this device (not | ||
282 | * necessarily the same as the bus the gfar's PHY is on), if one exists. | ||
283 | * Normally only the first gianfar controls a mii_bus. */ | ||
284 | struct mii_bus *gfar_get_miibus(const struct gfar_private *priv) | ||
285 | { | ||
286 | /*const*/ struct device *d; | ||
287 | |||
288 | d = bus_find_device(gianfar_mdio_driver.bus, NULL, (void *)priv, | ||
289 | match_mdio_bus); | ||
290 | return d ? dev_get_drvdata(d) : NULL; | ||
291 | } | ||
292 | |||
293 | int __init gfar_mdio_init(void) | 369 | int __init gfar_mdio_init(void) |
294 | { | 370 | { |
295 | return driver_register(&gianfar_mdio_driver); | 371 | return of_register_platform_driver(&gianfar_mdio_driver); |
296 | } | 372 | } |
297 | 373 | ||
298 | void gfar_mdio_exit(void) | 374 | void gfar_mdio_exit(void) |
299 | { | 375 | { |
300 | driver_unregister(&gianfar_mdio_driver); | 376 | of_unregister_platform_driver(&gianfar_mdio_driver); |
301 | } | 377 | } |