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