Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

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