Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
fixed.c
Go to the documentation of this file.
1 /*
2  * Fixed MDIO bus (MDIO bus emulation with fixed PHYs)
3  *
4  * Author: Vitaly Bordug <[email protected]>
5  * Anton Vorontsov <[email protected]>
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 
29  struct mii_bus *mii_bus;
30  struct list_head phys;
31 };
32 
33 struct fixed_phy {
34  int id;
36  struct phy_device *phydev;
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  pr_warn("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  pr_warn("fixed phy: unknown speed\n");
94  return -EINVAL;
95  }
96  }
97 
98  if (fp->status.link)
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  */
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 }
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 }
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, "fixed-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:
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 
252  mdiobus_free(fmb->mii_bus);
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");