Merge branch 'for-linus' of master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6] / 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
23 #define MII_REGS_NUM 29
24
25 struct fixed_mdio_bus {
26         int irqs[PHY_MAX_ADDR];
27         struct mii_bus *mii_bus;
28         struct list_head phys;
29 };
30
31 struct fixed_phy {
32         int id;
33         u16 regs[MII_REGS_NUM];
34         struct phy_device *phydev;
35         struct fixed_phy_status status;
36         int (*link_update)(struct net_device *, struct fixed_phy_status *);
37         struct list_head node;
38 };
39
40 static struct platform_device *pdev;
41 static struct fixed_mdio_bus platform_fmb = {
42         .phys = LIST_HEAD_INIT(platform_fmb.phys),
43 };
44
45 static int fixed_phy_update_regs(struct fixed_phy *fp)
46 {
47         u16 bmsr = BMSR_ANEGCAPABLE;
48         u16 bmcr = 0;
49         u16 lpagb = 0;
50         u16 lpa = 0;
51
52         if (fp->status.duplex) {
53                 bmcr |= BMCR_FULLDPLX;
54
55                 switch (fp->status.speed) {
56                 case 1000:
57                         bmsr |= BMSR_ESTATEN;
58                         bmcr |= BMCR_SPEED1000;
59                         lpagb |= LPA_1000FULL;
60                         break;
61                 case 100:
62                         bmsr |= BMSR_100FULL;
63                         bmcr |= BMCR_SPEED100;
64                         lpa |= LPA_100FULL;
65                         break;
66                 case 10:
67                         bmsr |= BMSR_10FULL;
68                         lpa |= LPA_10FULL;
69                         break;
70                 default:
71                         printk(KERN_WARNING "fixed phy: unknown speed\n");
72                         return -EINVAL;
73                 }
74         } else {
75                 switch (fp->status.speed) {
76                 case 1000:
77                         bmsr |= BMSR_ESTATEN;
78                         bmcr |= BMCR_SPEED1000;
79                         lpagb |= LPA_1000HALF;
80                         break;
81                 case 100:
82                         bmsr |= BMSR_100HALF;
83                         bmcr |= BMCR_SPEED100;
84                         lpa |= LPA_100HALF;
85                         break;
86                 case 10:
87                         bmsr |= BMSR_10HALF;
88                         lpa |= LPA_10HALF;
89                         break;
90                 default:
91                         printk(KERN_WARNING "fixed phy: unknown speed\n");
92                         return -EINVAL;
93                 }
94         }
95
96         if (fp->status.link)
97                 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
98
99         if (fp->status.pause)
100                 lpa |= LPA_PAUSE_CAP;
101
102         if (fp->status.asym_pause)
103                 lpa |= LPA_PAUSE_ASYM;
104
105         fp->regs[MII_PHYSID1] = fp->id >> 16;
106         fp->regs[MII_PHYSID2] = fp->id;
107
108         fp->regs[MII_BMSR] = bmsr;
109         fp->regs[MII_BMCR] = bmcr;
110         fp->regs[MII_LPA] = lpa;
111         fp->regs[MII_STAT1000] = lpagb;
112
113         return 0;
114 }
115
116 static int fixed_mdio_read(struct mii_bus *bus, int phy_id, int reg_num)
117 {
118         struct fixed_mdio_bus *fmb = bus->priv;
119         struct fixed_phy *fp;
120
121         if (reg_num >= MII_REGS_NUM)
122                 return -1;
123
124         list_for_each_entry(fp, &fmb->phys, node) {
125                 if (fp->id == phy_id) {
126                         /* Issue callback if user registered it. */
127                         if (fp->link_update) {
128                                 fp->link_update(fp->phydev->attached_dev,
129                                                 &fp->status);
130                                 fixed_phy_update_regs(fp);
131                         }
132                         return fp->regs[reg_num];
133                 }
134         }
135
136         return 0xFFFF;
137 }
138
139 static int fixed_mdio_write(struct mii_bus *bus, int phy_id, int reg_num,
140                             u16 val)
141 {
142         return 0;
143 }
144
145 /*
146  * If something weird is required to be done with link/speed,
147  * network driver is able to assign a function to implement this.
148  * May be useful for PHY's that need to be software-driven.
149  */
150 int fixed_phy_set_link_update(struct phy_device *phydev,
151                               int (*link_update)(struct net_device *,
152                                                  struct fixed_phy_status *))
153 {
154         struct fixed_mdio_bus *fmb = &platform_fmb;
155         struct fixed_phy *fp;
156
157         if (!link_update || !phydev || !phydev->bus)
158                 return -EINVAL;
159
160         list_for_each_entry(fp, &fmb->phys, node) {
161                 if (fp->id == phydev->phy_id) {
162                         fp->link_update = link_update;
163                         fp->phydev = phydev;
164                         return 0;
165                 }
166         }
167
168         return -ENOENT;
169 }
170 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
171
172 int fixed_phy_add(unsigned int irq, int phy_id,
173                   struct fixed_phy_status *status)
174 {
175         int ret;
176         struct fixed_mdio_bus *fmb = &platform_fmb;
177         struct fixed_phy *fp;
178
179         fp = kzalloc(sizeof(*fp), GFP_KERNEL);
180         if (!fp)
181                 return -ENOMEM;
182
183         memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
184
185         fmb->irqs[phy_id] = irq;
186
187         fp->id = phy_id;
188         fp->status = *status;
189
190         ret = fixed_phy_update_regs(fp);
191         if (ret)
192                 goto err_regs;
193
194         list_add_tail(&fp->node, &fmb->phys);
195
196         return 0;
197
198 err_regs:
199         kfree(fp);
200         return ret;
201 }
202 EXPORT_SYMBOL_GPL(fixed_phy_add);
203
204 static int __init fixed_mdio_bus_init(void)
205 {
206         struct fixed_mdio_bus *fmb = &platform_fmb;
207         int ret;
208
209         pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
210         if (!pdev) {
211                 ret = -ENOMEM;
212                 goto err_pdev;
213         }
214
215         fmb->mii_bus = mdiobus_alloc();
216         if (fmb->mii_bus == NULL) {
217                 ret = -ENOMEM;
218                 goto err_mdiobus_reg;
219         }
220
221         snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "0");
222         fmb->mii_bus->name = "Fixed MDIO Bus";
223         fmb->mii_bus->priv = fmb;
224         fmb->mii_bus->parent = &pdev->dev;
225         fmb->mii_bus->read = &fixed_mdio_read;
226         fmb->mii_bus->write = &fixed_mdio_write;
227         fmb->mii_bus->irq = fmb->irqs;
228
229         ret = mdiobus_register(fmb->mii_bus);
230         if (ret)
231                 goto err_mdiobus_alloc;
232
233         return 0;
234
235 err_mdiobus_alloc:
236         mdiobus_free(fmb->mii_bus);
237 err_mdiobus_reg:
238         platform_device_unregister(pdev);
239 err_pdev:
240         return ret;
241 }
242 module_init(fixed_mdio_bus_init);
243
244 static void __exit fixed_mdio_bus_exit(void)
245 {
246         struct fixed_mdio_bus *fmb = &platform_fmb;
247         struct fixed_phy *fp, *tmp;
248
249         mdiobus_unregister(fmb->mii_bus);
250         mdiobus_free(fmb->mii_bus);
251         platform_device_unregister(pdev);
252
253         list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
254                 list_del(&fp->node);
255                 kfree(fp);
256         }
257 }
258 module_exit(fixed_mdio_bus_exit);
259
260 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
261 MODULE_AUTHOR("Vitaly Bordug");
262 MODULE_LICENSE("GPL");