]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/net/phy/fixed.c
Merge branch 'for-3.17-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/tj...
[karo-tx-linux.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 #include <linux/of.h>
25
26 #define MII_REGS_NUM 29
27
28 struct fixed_mdio_bus {
29         int irqs[PHY_MAX_ADDR];
30         struct mii_bus *mii_bus;
31         struct list_head phys;
32 };
33
34 struct fixed_phy {
35         int addr;
36         u16 regs[MII_REGS_NUM];
37         struct phy_device *phydev;
38         struct fixed_phy_status status;
39         int (*link_update)(struct net_device *, struct fixed_phy_status *);
40         struct list_head node;
41 };
42
43 static struct platform_device *pdev;
44 static struct fixed_mdio_bus platform_fmb = {
45         .phys = LIST_HEAD_INIT(platform_fmb.phys),
46 };
47
48 static int fixed_phy_update_regs(struct fixed_phy *fp)
49 {
50         u16 bmsr = BMSR_ANEGCAPABLE;
51         u16 bmcr = 0;
52         u16 lpagb = 0;
53         u16 lpa = 0;
54
55         if (fp->status.duplex) {
56                 bmcr |= BMCR_FULLDPLX;
57
58                 switch (fp->status.speed) {
59                 case 1000:
60                         bmsr |= BMSR_ESTATEN;
61                         bmcr |= BMCR_SPEED1000;
62                         lpagb |= LPA_1000FULL;
63                         break;
64                 case 100:
65                         bmsr |= BMSR_100FULL;
66                         bmcr |= BMCR_SPEED100;
67                         lpa |= LPA_100FULL;
68                         break;
69                 case 10:
70                         bmsr |= BMSR_10FULL;
71                         lpa |= LPA_10FULL;
72                         break;
73                 default:
74                         pr_warn("fixed phy: unknown speed\n");
75                         return -EINVAL;
76                 }
77         } else {
78                 switch (fp->status.speed) {
79                 case 1000:
80                         bmsr |= BMSR_ESTATEN;
81                         bmcr |= BMCR_SPEED1000;
82                         lpagb |= LPA_1000HALF;
83                         break;
84                 case 100:
85                         bmsr |= BMSR_100HALF;
86                         bmcr |= BMCR_SPEED100;
87                         lpa |= LPA_100HALF;
88                         break;
89                 case 10:
90                         bmsr |= BMSR_10HALF;
91                         lpa |= LPA_10HALF;
92                         break;
93                 default:
94                         pr_warn("fixed phy: unknown speed\n");
95                         return -EINVAL;
96                 }
97         }
98
99         if (fp->status.link)
100                 bmsr |= BMSR_LSTATUS | BMSR_ANEGCOMPLETE;
101
102         if (fp->status.pause)
103                 lpa |= LPA_PAUSE_CAP;
104
105         if (fp->status.asym_pause)
106                 lpa |= LPA_PAUSE_ASYM;
107
108         fp->regs[MII_PHYSID1] = 0;
109         fp->regs[MII_PHYSID2] = 0;
110
111         fp->regs[MII_BMSR] = bmsr;
112         fp->regs[MII_BMCR] = bmcr;
113         fp->regs[MII_LPA] = lpa;
114         fp->regs[MII_STAT1000] = lpagb;
115
116         return 0;
117 }
118
119 static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
120 {
121         struct fixed_mdio_bus *fmb = bus->priv;
122         struct fixed_phy *fp;
123
124         if (reg_num >= MII_REGS_NUM)
125                 return -1;
126
127         list_for_each_entry(fp, &fmb->phys, node) {
128                 if (fp->addr == phy_addr) {
129                         /* Issue callback if user registered it. */
130                         if (fp->link_update) {
131                                 fp->link_update(fp->phydev->attached_dev,
132                                                 &fp->status);
133                                 fixed_phy_update_regs(fp);
134                         }
135                         return fp->regs[reg_num];
136                 }
137         }
138
139         return 0xFFFF;
140 }
141
142 static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
143                             u16 val)
144 {
145         return 0;
146 }
147
148 /*
149  * If something weird is required to be done with link/speed,
150  * network driver is able to assign a function to implement this.
151  * May be useful for PHY's that need to be software-driven.
152  */
153 int fixed_phy_set_link_update(struct phy_device *phydev,
154                               int (*link_update)(struct net_device *,
155                                                  struct fixed_phy_status *))
156 {
157         struct fixed_mdio_bus *fmb = &platform_fmb;
158         struct fixed_phy *fp;
159
160         if (!link_update || !phydev || !phydev->bus)
161                 return -EINVAL;
162
163         list_for_each_entry(fp, &fmb->phys, node) {
164                 if (fp->addr == phydev->addr) {
165                         fp->link_update = link_update;
166                         fp->phydev = phydev;
167                         return 0;
168                 }
169         }
170
171         return -ENOENT;
172 }
173 EXPORT_SYMBOL_GPL(fixed_phy_set_link_update);
174
175 int fixed_phy_add(unsigned int irq, int phy_addr,
176                   struct fixed_phy_status *status)
177 {
178         int ret;
179         struct fixed_mdio_bus *fmb = &platform_fmb;
180         struct fixed_phy *fp;
181
182         fp = kzalloc(sizeof(*fp), GFP_KERNEL);
183         if (!fp)
184                 return -ENOMEM;
185
186         memset(fp->regs, 0xFF,  sizeof(fp->regs[0]) * MII_REGS_NUM);
187
188         fmb->irqs[phy_addr] = irq;
189
190         fp->addr = phy_addr;
191         fp->status = *status;
192
193         ret = fixed_phy_update_regs(fp);
194         if (ret)
195                 goto err_regs;
196
197         list_add_tail(&fp->node, &fmb->phys);
198
199         return 0;
200
201 err_regs:
202         kfree(fp);
203         return ret;
204 }
205 EXPORT_SYMBOL_GPL(fixed_phy_add);
206
207 void fixed_phy_del(int phy_addr)
208 {
209         struct fixed_mdio_bus *fmb = &platform_fmb;
210         struct fixed_phy *fp, *tmp;
211
212         list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
213                 if (fp->addr == phy_addr) {
214                         list_del(&fp->node);
215                         kfree(fp);
216                         return;
217                 }
218         }
219 }
220 EXPORT_SYMBOL_GPL(fixed_phy_del);
221
222 static int phy_fixed_addr;
223 static DEFINE_SPINLOCK(phy_fixed_addr_lock);
224
225 int fixed_phy_register(unsigned int irq,
226                        struct fixed_phy_status *status,
227                        struct device_node *np)
228 {
229         struct fixed_mdio_bus *fmb = &platform_fmb;
230         struct phy_device *phy;
231         int phy_addr;
232         int ret;
233
234         /* Get the next available PHY address, up to PHY_MAX_ADDR */
235         spin_lock(&phy_fixed_addr_lock);
236         if (phy_fixed_addr == PHY_MAX_ADDR) {
237                 spin_unlock(&phy_fixed_addr_lock);
238                 return -ENOSPC;
239         }
240         phy_addr = phy_fixed_addr++;
241         spin_unlock(&phy_fixed_addr_lock);
242
243         ret = fixed_phy_add(PHY_POLL, phy_addr, status);
244         if (ret < 0)
245                 return ret;
246
247         phy = get_phy_device(fmb->mii_bus, phy_addr, false);
248         if (!phy || IS_ERR(phy)) {
249                 fixed_phy_del(phy_addr);
250                 return -EINVAL;
251         }
252
253         of_node_get(np);
254         phy->dev.of_node = np;
255
256         ret = phy_device_register(phy);
257         if (ret) {
258                 phy_device_free(phy);
259                 of_node_put(np);
260                 fixed_phy_del(phy_addr);
261                 return ret;
262         }
263
264         return 0;
265 }
266
267 static int __init fixed_mdio_bus_init(void)
268 {
269         struct fixed_mdio_bus *fmb = &platform_fmb;
270         int ret;
271
272         pdev = platform_device_register_simple("Fixed MDIO bus", 0, NULL, 0);
273         if (IS_ERR(pdev)) {
274                 ret = PTR_ERR(pdev);
275                 goto err_pdev;
276         }
277
278         fmb->mii_bus = mdiobus_alloc();
279         if (fmb->mii_bus == NULL) {
280                 ret = -ENOMEM;
281                 goto err_mdiobus_reg;
282         }
283
284         snprintf(fmb->mii_bus->id, MII_BUS_ID_SIZE, "fixed-0");
285         fmb->mii_bus->name = "Fixed MDIO Bus";
286         fmb->mii_bus->priv = fmb;
287         fmb->mii_bus->parent = &pdev->dev;
288         fmb->mii_bus->read = &fixed_mdio_read;
289         fmb->mii_bus->write = &fixed_mdio_write;
290         fmb->mii_bus->irq = fmb->irqs;
291
292         ret = mdiobus_register(fmb->mii_bus);
293         if (ret)
294                 goto err_mdiobus_alloc;
295
296         return 0;
297
298 err_mdiobus_alloc:
299         mdiobus_free(fmb->mii_bus);
300 err_mdiobus_reg:
301         platform_device_unregister(pdev);
302 err_pdev:
303         return ret;
304 }
305 module_init(fixed_mdio_bus_init);
306
307 static void __exit fixed_mdio_bus_exit(void)
308 {
309         struct fixed_mdio_bus *fmb = &platform_fmb;
310         struct fixed_phy *fp, *tmp;
311
312         mdiobus_unregister(fmb->mii_bus);
313         mdiobus_free(fmb->mii_bus);
314         platform_device_unregister(pdev);
315
316         list_for_each_entry_safe(fp, tmp, &fmb->phys, node) {
317                 list_del(&fp->node);
318                 kfree(fp);
319         }
320 }
321 module_exit(fixed_mdio_bus_exit);
322
323 MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)");
324 MODULE_AUTHOR("Vitaly Bordug");
325 MODULE_LICENSE("GPL");