Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
[sfrench/cifs-2.6.git] / drivers / net / fec_mpc52xx_phy.c
1 /*
2  * Driver for the MPC5200 Fast Ethernet Controller - MDIO bus driver
3  *
4  * Copyright (C) 2007  Domen Puncer, Telargo, Inc.
5  * Copyright (C) 2008  Wolfram Sang, Pengutronix
6  *
7  * This file is licensed under the terms of the GNU General Public License
8  * version 2. This program is licensed "as is" without any warranty of any
9  * kind, whether express or implied.
10  */
11
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/netdevice.h>
15 #include <linux/phy.h>
16 #include <linux/of_platform.h>
17 #include <linux/slab.h>
18 #include <linux/of_mdio.h>
19 #include <asm/io.h>
20 #include <asm/mpc52xx.h>
21 #include "fec_mpc52xx.h"
22
23 struct mpc52xx_fec_mdio_priv {
24         struct mpc52xx_fec __iomem *regs;
25         int mdio_irqs[PHY_MAX_ADDR];
26 };
27
28 static int mpc52xx_fec_mdio_transfer(struct mii_bus *bus, int phy_id,
29                 int reg, u32 value)
30 {
31         struct mpc52xx_fec_mdio_priv *priv = bus->priv;
32         struct mpc52xx_fec __iomem *fec;
33         int tries = 3;
34
35         value |= (phy_id << FEC_MII_DATA_PA_SHIFT) & FEC_MII_DATA_PA_MSK;
36         value |= (reg << FEC_MII_DATA_RA_SHIFT) & FEC_MII_DATA_RA_MSK;
37
38         fec = priv->regs;
39         out_be32(&fec->ievent, FEC_IEVENT_MII);
40         out_be32(&priv->regs->mii_data, value);
41
42         /* wait for it to finish, this takes about 23 us on lite5200b */
43         while (!(in_be32(&fec->ievent) & FEC_IEVENT_MII) && --tries)
44                 msleep(1);
45
46         if (!tries)
47                 return -ETIMEDOUT;
48
49         return value & FEC_MII_DATA_OP_RD ?
50                 in_be32(&priv->regs->mii_data) & FEC_MII_DATA_DATAMSK : 0;
51 }
52
53 static int mpc52xx_fec_mdio_read(struct mii_bus *bus, int phy_id, int reg)
54 {
55         return mpc52xx_fec_mdio_transfer(bus, phy_id, reg, FEC_MII_READ_FRAME);
56 }
57
58 static int mpc52xx_fec_mdio_write(struct mii_bus *bus, int phy_id, int reg,
59                 u16 data)
60 {
61         return mpc52xx_fec_mdio_transfer(bus, phy_id, reg,
62                 data | FEC_MII_WRITE_FRAME);
63 }
64
65 static int mpc52xx_fec_mdio_probe(struct of_device *of,
66                 const struct of_device_id *match)
67 {
68         struct device *dev = &of->dev;
69         struct device_node *np = of->dev.of_node;
70         struct mii_bus *bus;
71         struct mpc52xx_fec_mdio_priv *priv;
72         struct resource res = {};
73         int err;
74         int i;
75
76         bus = mdiobus_alloc();
77         if (bus == NULL)
78                 return -ENOMEM;
79         priv = kzalloc(sizeof(*priv), GFP_KERNEL);
80         if (priv == NULL) {
81                 err = -ENOMEM;
82                 goto out_free;
83         }
84
85         bus->name = "mpc52xx MII bus";
86         bus->read = mpc52xx_fec_mdio_read;
87         bus->write = mpc52xx_fec_mdio_write;
88
89         /* setup irqs */
90         bus->irq = priv->mdio_irqs;
91
92         /* setup registers */
93         err = of_address_to_resource(np, 0, &res);
94         if (err)
95                 goto out_free;
96         priv->regs = ioremap(res.start, res.end - res.start + 1);
97         if (priv->regs == NULL) {
98                 err = -ENOMEM;
99                 goto out_free;
100         }
101
102         snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start);
103         bus->priv = priv;
104
105         bus->parent = dev;
106         dev_set_drvdata(dev, bus);
107
108         /* set MII speed */
109         out_be32(&priv->regs->mii_speed,
110                 ((mpc5xxx_get_bus_frequency(of->dev.of_node) >> 20) / 5) << 1);
111
112         err = of_mdiobus_register(bus, np);
113         if (err)
114                 goto out_unmap;
115
116         return 0;
117
118  out_unmap:
119         iounmap(priv->regs);
120  out_free:
121         for (i=0; i<PHY_MAX_ADDR; i++)
122                 if (bus->irq[i] != PHY_POLL)
123                         irq_dispose_mapping(bus->irq[i]);
124         kfree(bus->irq);
125         kfree(priv);
126         mdiobus_free(bus);
127
128         return err;
129 }
130
131 static int mpc52xx_fec_mdio_remove(struct of_device *of)
132 {
133         struct device *dev = &of->dev;
134         struct mii_bus *bus = dev_get_drvdata(dev);
135         struct mpc52xx_fec_mdio_priv *priv = bus->priv;
136         int i;
137
138         mdiobus_unregister(bus);
139         dev_set_drvdata(dev, NULL);
140
141         iounmap(priv->regs);
142         for (i=0; i<PHY_MAX_ADDR; i++)
143                 if (bus->irq[i] != PHY_POLL)
144                         irq_dispose_mapping(bus->irq[i]);
145         kfree(priv);
146         kfree(bus->irq);
147         mdiobus_free(bus);
148
149         return 0;
150 }
151
152
153 static struct of_device_id mpc52xx_fec_mdio_match[] = {
154         { .compatible = "fsl,mpc5200b-mdio", },
155         { .compatible = "fsl,mpc5200-mdio", },
156         { .compatible = "mpc5200b-fec-phy", },
157         {}
158 };
159 MODULE_DEVICE_TABLE(of, mpc52xx_fec_mdio_match);
160
161 struct of_platform_driver mpc52xx_fec_mdio_driver = {
162         .driver = {
163                 .name = "mpc5200b-fec-phy",
164                 .owner = THIS_MODULE,
165                 .of_match_table = mpc52xx_fec_mdio_match,
166         },
167         .probe = mpc52xx_fec_mdio_probe,
168         .remove = mpc52xx_fec_mdio_remove,
169 };
170
171 /* let fec driver call it, since this has to be registered before it */
172 EXPORT_SYMBOL_GPL(mpc52xx_fec_mdio_driver);
173
174
175 MODULE_LICENSE("Dual BSD/GPL");