~ubuntu-branches/ubuntu/trusty/linux-armadaxp/trusty

« back to all changes in this revision

Viewing changes to drivers/net/ethernet/freescale/fsl_pq_mdio.c

  • Committer: Package Import Robot
  • Author(s): Michael Casadevall, Bryan Wu, Dann Frazier, Michael Casadeall
  • Date: 2012-03-10 15:00:54 UTC
  • mfrom: (1.1.1)
  • Revision ID: package-import@ubuntu.com-20120310150054-flugb39zon8vvgwe
Tags: 3.2.0-1600.1
[ Bryan Wu ]
* UBUNTU: import debian/debian.env and debian.armadaxp

[ Dann Frazier ]
* ARM: Armada XP: remove trailing '/' in dirnames in mvRules.mk

[ Michael Casadeall ]
* tools: add some tools for Marvell Armada XP processor
* kernel: timer tick hacking from Marvell
* kernel: Sheeva Errata: add delay on Sheeva when powering down
* net: add Marvell NFP netfilter
* net: socket and skb modifications made by Marvell
* miscdevice: add minor IDs for some Marvell Armada drivers
* fs: introduce memory pool for splice()
* video: EDID detection updates from Marvell Armada XP patchset
* video: backlight: add Marvell Dove LCD backlight driver
* video: display: add THS8200 display driver
* video: framebuffer: add Marvell Dove and Armada XP processor onchip LCD controller driver
* usbtest: add Interrupt transfer testing by Marvell Armada XP code
* usb: ehci: add support for Marvell EHCI controler
* tty/serial: 8250: add support for Marvell Armada XP processor and DeviceTree work
* rtc: add support for Marvell Armada XP onchip RTC controller
* net: pppoe: add Marvell ethernet NFP hook in PPPoE networking driver
* mtd: nand: add support for Marvell Armada XP Nand Flash Controller
* mtd: maps: add Marvell Armada XP specific map driver
* mmc: add support for Marvell Armada XP MMC/SD host controller
* i2c: add support for Marvell Armada XP onchip i2c bus controller
* hwmon: add Kconfig option for Armada XP onchip thermal sensor driver
* dmaengine: add Net DMA support for splice and update Marvell XOR DMA engine driver
* ata: add support for Marvell Armada XP SATA controller and update some quirks
* ARM: add Marvell Armada XP machine to mach-types
* ARM: oprofile: add support for Marvell PJ4B core
* ARM: mm: more ARMv6 switches for Marvell Armada XP
* ARM: remove static declaration to allow compilation
* ARM: alignment access fault trick
* ARM: mm: skip some fault fixing when run on NONE SMP ARMv6 mode during early abort event
* ARM: mm: add Marvell Sheeva CPU Architecture for PJ4B
* ARM: introduce optimized copy operation for Marvell Armada XP
* ARM: SAUCE: hardware breakpoint trick for Marvell Armada XP
* ARM: big endian and little endian tricks for Marvell Armada XP
* ARM: SAUCE: Add Marvell Armada XP build rules to arch/arm/kernel/Makefile
* ARM: vfp: add special handling for Marvell Armada XP
* ARM: add support for Marvell U-Boot
* ARM: add mv_controller_num for ARM PCI drivers
* ARM: add support for local PMUs, general SMP tweaks and cache flushing
* ARM: add Marvell device identifies in glue-proc.h
* ARM: add IPC driver support for Marvell platforms
* ARM: add DMA mapping for Marvell platforms
* ARM: add Sheeva errata and PJ4B code for booting
* ARM: update Kconfig and Makefile to include Marvell Armada XP platforms
* ARM: Armada XP: import LSP from Marvell for Armada XP 3.2 kernel enablement

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Freescale PowerQUICC Ethernet Driver -- MIIM bus implementation
 
3
 * Provides Bus interface for MIIM regs
 
4
 *
 
5
 * Author: Andy Fleming <afleming@freescale.com>
 
6
 * Modifier: Sandeep Gopalpet <sandeep.kumar@freescale.com>
 
7
 *
 
8
 * Copyright 2002-2004, 2008-2009 Freescale Semiconductor, Inc.
 
9
 *
 
10
 * Based on gianfar_mii.c and ucc_geth_mii.c (Li Yang, Kim Phillips)
 
11
 *
 
12
 * This program is free software; you can redistribute  it and/or modify it
 
13
 * under  the terms of  the GNU General  Public License as published by the
 
14
 * Free Software Foundation;  either version 2 of the  License, or (at your
 
15
 * option) any later version.
 
16
 *
 
17
 */
 
18
 
 
19
#include <linux/kernel.h>
 
20
#include <linux/string.h>
 
21
#include <linux/errno.h>
 
22
#include <linux/unistd.h>
 
23
#include <linux/slab.h>
 
24
#include <linux/interrupt.h>
 
25
#include <linux/init.h>
 
26
#include <linux/delay.h>
 
27
#include <linux/netdevice.h>
 
28
#include <linux/etherdevice.h>
 
29
#include <linux/skbuff.h>
 
30
#include <linux/spinlock.h>
 
31
#include <linux/mm.h>
 
32
#include <linux/module.h>
 
33
#include <linux/platform_device.h>
 
34
#include <linux/crc32.h>
 
35
#include <linux/mii.h>
 
36
#include <linux/phy.h>
 
37
#include <linux/of.h>
 
38
#include <linux/of_address.h>
 
39
#include <linux/of_mdio.h>
 
40
#include <linux/of_platform.h>
 
41
 
 
42
#include <asm/io.h>
 
43
#include <asm/irq.h>
 
44
#include <asm/uaccess.h>
 
45
#include <asm/ucc.h>
 
46
 
 
47
#include "gianfar.h"
 
48
#include "fsl_pq_mdio.h"
 
49
 
 
50
struct fsl_pq_mdio_priv {
 
51
        void __iomem *map;
 
52
        struct fsl_pq_mdio __iomem *regs;
 
53
};
 
54
 
 
55
/*
 
56
 * Write value to the PHY at mii_id at register regnum,
 
57
 * on the bus attached to the local interface, which may be different from the
 
58
 * generic mdio bus (tied to a single interface), waiting until the write is
 
59
 * done before returning. This is helpful in programming interfaces like
 
60
 * the TBI which control interfaces like onchip SERDES and are always tied to
 
61
 * the local mdio pins, which may not be the same as system mdio bus, used for
 
62
 * controlling the external PHYs, for example.
 
63
 */
 
64
int fsl_pq_local_mdio_write(struct fsl_pq_mdio __iomem *regs, int mii_id,
 
65
                int regnum, u16 value)
 
66
{
 
67
        /* Set the PHY address and the register address we want to write */
 
68
        out_be32(&regs->miimadd, (mii_id << 8) | regnum);
 
69
 
 
70
        /* Write out the value we want */
 
71
        out_be32(&regs->miimcon, value);
 
72
 
 
73
        /* Wait for the transaction to finish */
 
74
        while (in_be32(&regs->miimind) & MIIMIND_BUSY)
 
75
                cpu_relax();
 
76
 
 
77
        return 0;
 
78
}
 
79
 
 
80
/*
 
81
 * Read the bus for PHY at addr mii_id, register regnum, and
 
82
 * return the value.  Clears miimcom first.  All PHY operation
 
83
 * done on the bus attached to the local interface,
 
84
 * which may be different from the generic mdio bus
 
85
 * This is helpful in programming interfaces like
 
86
 * the TBI which, in turn, control interfaces like onchip SERDES
 
87
 * and are always tied to the local mdio pins, which may not be the
 
88
 * same as system mdio bus, used for controlling the external PHYs, for eg.
 
89
 */
 
90
int fsl_pq_local_mdio_read(struct fsl_pq_mdio __iomem *regs,
 
91
                int mii_id, int regnum)
 
92
{
 
93
        u16 value;
 
94
 
 
95
        /* Set the PHY address and the register address we want to read */
 
96
        out_be32(&regs->miimadd, (mii_id << 8) | regnum);
 
97
 
 
98
        /* Clear miimcom, and then initiate a read */
 
99
        out_be32(&regs->miimcom, 0);
 
100
        out_be32(&regs->miimcom, MII_READ_COMMAND);
 
101
 
 
102
        /* Wait for the transaction to finish */
 
103
        while (in_be32(&regs->miimind) & (MIIMIND_NOTVALID | MIIMIND_BUSY))
 
104
                cpu_relax();
 
105
 
 
106
        /* Grab the value of the register from miimstat */
 
107
        value = in_be32(&regs->miimstat);
 
108
 
 
109
        return value;
 
110
}
 
111
 
 
112
static struct fsl_pq_mdio __iomem *fsl_pq_mdio_get_regs(struct mii_bus *bus)
 
113
{
 
114
        struct fsl_pq_mdio_priv *priv = bus->priv;
 
115
 
 
116
        return priv->regs;
 
117
}
 
118
 
 
119
/*
 
120
 * Write value to the PHY at mii_id at register regnum,
 
121
 * on the bus, waiting until the write is done before returning.
 
122
 */
 
123
int fsl_pq_mdio_write(struct mii_bus *bus, int mii_id, int regnum, u16 value)
 
124
{
 
125
        struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
 
126
 
 
127
        /* Write to the local MII regs */
 
128
        return fsl_pq_local_mdio_write(regs, mii_id, regnum, value);
 
129
}
 
130
 
 
131
/*
 
132
 * Read the bus for PHY at addr mii_id, register regnum, and
 
133
 * return the value.  Clears miimcom first.
 
134
 */
 
135
int fsl_pq_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
 
136
{
 
137
        struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
 
138
 
 
139
        /* Read the local MII regs */
 
140
        return fsl_pq_local_mdio_read(regs, mii_id, regnum);
 
141
}
 
142
 
 
143
/* Reset the MIIM registers, and wait for the bus to free */
 
144
static int fsl_pq_mdio_reset(struct mii_bus *bus)
 
145
{
 
146
        struct fsl_pq_mdio __iomem *regs = fsl_pq_mdio_get_regs(bus);
 
147
        int timeout = PHY_INIT_TIMEOUT;
 
148
 
 
149
        mutex_lock(&bus->mdio_lock);
 
150
 
 
151
        /* Reset the management interface */
 
152
        out_be32(&regs->miimcfg, MIIMCFG_RESET);
 
153
 
 
154
        /* Setup the MII Mgmt clock speed */
 
155
        out_be32(&regs->miimcfg, MIIMCFG_INIT_VALUE);
 
156
 
 
157
        /* Wait until the bus is free */
 
158
        while ((in_be32(&regs->miimind) & MIIMIND_BUSY) && timeout--)
 
159
                cpu_relax();
 
160
 
 
161
        mutex_unlock(&bus->mdio_lock);
 
162
 
 
163
        if (timeout < 0) {
 
164
                printk(KERN_ERR "%s: The MII Bus is stuck!\n",
 
165
                                bus->name);
 
166
                return -EBUSY;
 
167
        }
 
168
 
 
169
        return 0;
 
170
}
 
171
 
 
172
void fsl_pq_mdio_bus_name(char *name, struct device_node *np)
 
173
{
 
174
        const u32 *addr;
 
175
        u64 taddr = OF_BAD_ADDR;
 
176
 
 
177
        addr = of_get_address(np, 0, NULL, NULL);
 
178
        if (addr)
 
179
                taddr = of_translate_address(np, addr);
 
180
 
 
181
        snprintf(name, MII_BUS_ID_SIZE, "%s@%llx", np->name,
 
182
                (unsigned long long)taddr);
 
183
}
 
184
EXPORT_SYMBOL_GPL(fsl_pq_mdio_bus_name);
 
185
 
 
186
 
 
187
static u32 __iomem *get_gfar_tbipa(struct fsl_pq_mdio __iomem *regs, struct device_node *np)
 
188
{
 
189
#if defined(CONFIG_GIANFAR) || defined(CONFIG_GIANFAR_MODULE)
 
190
        struct gfar __iomem *enet_regs;
 
191
 
 
192
        /*
 
193
         * This is mildly evil, but so is our hardware for doing this.
 
194
         * Also, we have to cast back to struct gfar because of
 
195
         * definition weirdness done in gianfar.h.
 
196
         */
 
197
        if(of_device_is_compatible(np, "fsl,gianfar-mdio") ||
 
198
                of_device_is_compatible(np, "fsl,gianfar-tbi") ||
 
199
                of_device_is_compatible(np, "gianfar")) {
 
200
                enet_regs = (struct gfar __iomem *)regs;
 
201
                return &enet_regs->tbipa;
 
202
        } else if (of_device_is_compatible(np, "fsl,etsec2-mdio") ||
 
203
                        of_device_is_compatible(np, "fsl,etsec2-tbi")) {
 
204
                return of_iomap(np, 1);
 
205
        }
 
206
#endif
 
207
        return NULL;
 
208
}
 
209
 
 
210
 
 
211
static int get_ucc_id_for_range(u64 start, u64 end, u32 *ucc_id)
 
212
{
 
213
#if defined(CONFIG_UCC_GETH) || defined(CONFIG_UCC_GETH_MODULE)
 
214
        struct device_node *np = NULL;
 
215
        int err = 0;
 
216
 
 
217
        for_each_compatible_node(np, NULL, "ucc_geth") {
 
218
                struct resource tempres;
 
219
 
 
220
                err = of_address_to_resource(np, 0, &tempres);
 
221
                if (err)
 
222
                        continue;
 
223
 
 
224
                /* if our mdio regs fall within this UCC regs range */
 
225
                if ((start >= tempres.start) && (end <= tempres.end)) {
 
226
                        /* Find the id of the UCC */
 
227
                        const u32 *id;
 
228
 
 
229
                        id = of_get_property(np, "cell-index", NULL);
 
230
                        if (!id) {
 
231
                                id = of_get_property(np, "device-id", NULL);
 
232
                                if (!id)
 
233
                                        continue;
 
234
                        }
 
235
 
 
236
                        *ucc_id = *id;
 
237
 
 
238
                        return 0;
 
239
                }
 
240
        }
 
241
 
 
242
        if (err)
 
243
                return err;
 
244
        else
 
245
                return -EINVAL;
 
246
#else
 
247
        return -ENODEV;
 
248
#endif
 
249
}
 
250
 
 
251
static int fsl_pq_mdio_probe(struct platform_device *ofdev)
 
252
{
 
253
        struct device_node *np = ofdev->dev.of_node;
 
254
        struct device_node *tbi;
 
255
        struct fsl_pq_mdio_priv *priv;
 
256
        struct fsl_pq_mdio __iomem *regs = NULL;
 
257
        void __iomem *map;
 
258
        u32 __iomem *tbipa;
 
259
        struct mii_bus *new_bus;
 
260
        int tbiaddr = -1;
 
261
        const u32 *addrp;
 
262
        u64 addr = 0, size = 0;
 
263
        int err;
 
264
 
 
265
        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 
266
        if (!priv)
 
267
                return -ENOMEM;
 
268
 
 
269
        new_bus = mdiobus_alloc();
 
270
        if (!new_bus) {
 
271
                err = -ENOMEM;
 
272
                goto err_free_priv;
 
273
        }
 
274
 
 
275
        new_bus->name = "Freescale PowerQUICC MII Bus",
 
276
        new_bus->read = &fsl_pq_mdio_read,
 
277
        new_bus->write = &fsl_pq_mdio_write,
 
278
        new_bus->reset = &fsl_pq_mdio_reset,
 
279
        new_bus->priv = priv;
 
280
        fsl_pq_mdio_bus_name(new_bus->id, np);
 
281
 
 
282
        addrp = of_get_address(np, 0, &size, NULL);
 
283
        if (!addrp) {
 
284
                err = -EINVAL;
 
285
                goto err_free_bus;
 
286
        }
 
287
 
 
288
        /* Set the PHY base address */
 
289
        addr = of_translate_address(np, addrp);
 
290
        if (addr == OF_BAD_ADDR) {
 
291
                err = -EINVAL;
 
292
                goto err_free_bus;
 
293
        }
 
294
 
 
295
        map = ioremap(addr, size);
 
296
        if (!map) {
 
297
                err = -ENOMEM;
 
298
                goto err_free_bus;
 
299
        }
 
300
        priv->map = map;
 
301
 
 
302
        if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
 
303
                        of_device_is_compatible(np, "fsl,gianfar-tbi") ||
 
304
                        of_device_is_compatible(np, "fsl,ucc-mdio") ||
 
305
                        of_device_is_compatible(np, "ucc_geth_phy"))
 
306
                map -= offsetof(struct fsl_pq_mdio, miimcfg);
 
307
        regs = map;
 
308
        priv->regs = regs;
 
309
 
 
310
        new_bus->irq = kcalloc(PHY_MAX_ADDR, sizeof(int), GFP_KERNEL);
 
311
 
 
312
        if (NULL == new_bus->irq) {
 
313
                err = -ENOMEM;
 
314
                goto err_unmap_regs;
 
315
        }
 
316
 
 
317
        new_bus->parent = &ofdev->dev;
 
318
        dev_set_drvdata(&ofdev->dev, new_bus);
 
319
 
 
320
        if (of_device_is_compatible(np, "fsl,gianfar-mdio") ||
 
321
                        of_device_is_compatible(np, "fsl,gianfar-tbi") ||
 
322
                        of_device_is_compatible(np, "fsl,etsec2-mdio") ||
 
323
                        of_device_is_compatible(np, "fsl,etsec2-tbi") ||
 
324
                        of_device_is_compatible(np, "gianfar")) {
 
325
                tbipa = get_gfar_tbipa(regs, np);
 
326
                if (!tbipa) {
 
327
                        err = -EINVAL;
 
328
                        goto err_free_irqs;
 
329
                }
 
330
        } else if (of_device_is_compatible(np, "fsl,ucc-mdio") ||
 
331
                        of_device_is_compatible(np, "ucc_geth_phy")) {
 
332
                u32 id;
 
333
                static u32 mii_mng_master;
 
334
 
 
335
                tbipa = &regs->utbipar;
 
336
 
 
337
                if ((err = get_ucc_id_for_range(addr, addr + size, &id)))
 
338
                        goto err_free_irqs;
 
339
 
 
340
                if (!mii_mng_master) {
 
341
                        mii_mng_master = id;
 
342
                        ucc_set_qe_mux_mii_mng(id - 1);
 
343
                }
 
344
        } else {
 
345
                err = -ENODEV;
 
346
                goto err_free_irqs;
 
347
        }
 
348
 
 
349
        for_each_child_of_node(np, tbi) {
 
350
                if (!strncmp(tbi->type, "tbi-phy", 8))
 
351
                        break;
 
352
        }
 
353
 
 
354
        if (tbi) {
 
355
                const u32 *prop = of_get_property(tbi, "reg", NULL);
 
356
 
 
357
                if (prop)
 
358
                        tbiaddr = *prop;
 
359
        }
 
360
 
 
361
        if (tbiaddr == -1) {
 
362
                err = -EBUSY;
 
363
 
 
364
                goto err_free_irqs;
 
365
        }
 
366
 
 
367
        out_be32(tbipa, tbiaddr);
 
368
 
 
369
        err = of_mdiobus_register(new_bus, np);
 
370
        if (err) {
 
371
                printk (KERN_ERR "%s: Cannot register as MDIO bus\n",
 
372
                                new_bus->name);
 
373
                goto err_free_irqs;
 
374
        }
 
375
 
 
376
        return 0;
 
377
 
 
378
err_free_irqs:
 
379
        kfree(new_bus->irq);
 
380
err_unmap_regs:
 
381
        iounmap(priv->map);
 
382
err_free_bus:
 
383
        kfree(new_bus);
 
384
err_free_priv:
 
385
        kfree(priv);
 
386
        return err;
 
387
}
 
388
 
 
389
 
 
390
static int fsl_pq_mdio_remove(struct platform_device *ofdev)
 
391
{
 
392
        struct device *device = &ofdev->dev;
 
393
        struct mii_bus *bus = dev_get_drvdata(device);
 
394
        struct fsl_pq_mdio_priv *priv = bus->priv;
 
395
 
 
396
        mdiobus_unregister(bus);
 
397
 
 
398
        dev_set_drvdata(device, NULL);
 
399
 
 
400
        iounmap(priv->map);
 
401
        bus->priv = NULL;
 
402
        mdiobus_free(bus);
 
403
        kfree(priv);
 
404
 
 
405
        return 0;
 
406
}
 
407
 
 
408
static struct of_device_id fsl_pq_mdio_match[] = {
 
409
        {
 
410
                .type = "mdio",
 
411
                .compatible = "ucc_geth_phy",
 
412
        },
 
413
        {
 
414
                .type = "mdio",
 
415
                .compatible = "gianfar",
 
416
        },
 
417
        {
 
418
                .compatible = "fsl,ucc-mdio",
 
419
        },
 
420
        {
 
421
                .compatible = "fsl,gianfar-tbi",
 
422
        },
 
423
        {
 
424
                .compatible = "fsl,gianfar-mdio",
 
425
        },
 
426
        {
 
427
                .compatible = "fsl,etsec2-tbi",
 
428
        },
 
429
        {
 
430
                .compatible = "fsl,etsec2-mdio",
 
431
        },
 
432
        {},
 
433
};
 
434
MODULE_DEVICE_TABLE(of, fsl_pq_mdio_match);
 
435
 
 
436
static struct platform_driver fsl_pq_mdio_driver = {
 
437
        .driver = {
 
438
                .name = "fsl-pq_mdio",
 
439
                .owner = THIS_MODULE,
 
440
                .of_match_table = fsl_pq_mdio_match,
 
441
        },
 
442
        .probe = fsl_pq_mdio_probe,
 
443
        .remove = fsl_pq_mdio_remove,
 
444
};
 
445
 
 
446
int __init fsl_pq_mdio_init(void)
 
447
{
 
448
        return platform_driver_register(&fsl_pq_mdio_driver);
 
449
}
 
450
module_init(fsl_pq_mdio_init);
 
451
 
 
452
void fsl_pq_mdio_exit(void)
 
453
{
 
454
        platform_driver_unregister(&fsl_pq_mdio_driver);
 
455
}
 
456
module_exit(fsl_pq_mdio_exit);
 
457
MODULE_LICENSE("GPL");