Root/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c

1/*
2 * Atheros AR71xx built-in ethernet mac driver
3 *
4 * Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
5 * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
6 *
7 * Based on Atheros' AG7100 driver
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 version 2 as published
11 * by the Free Software Foundation.
12 */
13
14#include "ag71xx.h"
15
16static void ag71xx_phy_link_adjust(struct net_device *dev)
17{
18    struct ag71xx *ag = netdev_priv(dev);
19    struct phy_device *phydev = ag->phy_dev;
20    unsigned long flags;
21    int status_change = 0;
22
23    spin_lock_irqsave(&ag->lock, flags);
24
25    if (phydev->link) {
26        if (ag->duplex != phydev->duplex
27            || ag->speed != phydev->speed) {
28            status_change = 1;
29        }
30    }
31
32    if (phydev->link != ag->link)
33        status_change = 1;
34
35    ag->link = phydev->link;
36    ag->duplex = phydev->duplex;
37    ag->speed = phydev->speed;
38
39    if (status_change)
40        ag71xx_link_adjust(ag);
41
42    spin_unlock_irqrestore(&ag->lock, flags);
43}
44
45void ag71xx_phy_start(struct ag71xx *ag)
46{
47    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
48
49    if (ag->phy_dev) {
50        phy_start(ag->phy_dev);
51    } else if (pdata->has_ar7240_switch) {
52        ag71xx_ar7240_start(ag);
53    } else {
54        ag->link = 1;
55        ag71xx_link_adjust(ag);
56    }
57}
58
59void ag71xx_phy_stop(struct ag71xx *ag)
60{
61    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
62    unsigned long flags;
63
64    if (ag->phy_dev)
65        phy_stop(ag->phy_dev);
66    else if (pdata->has_ar7240_switch)
67            ag71xx_ar7240_stop(ag);
68
69    spin_lock_irqsave(&ag->lock, flags);
70    if (ag->link) {
71        ag->link = 0;
72        ag71xx_link_adjust(ag);
73    }
74    spin_unlock_irqrestore(&ag->lock, flags);
75}
76
77static int ag71xx_phy_connect_fixed(struct ag71xx *ag)
78{
79    struct net_device *dev = ag->dev;
80    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
81    int ret = 0;
82
83    /* use fixed settings */
84    switch (pdata->speed) {
85    case SPEED_10:
86    case SPEED_100:
87    case SPEED_1000:
88        break;
89    default:
90        printk(KERN_ERR "%s: invalid speed specified\n", dev->name);
91        ret = -EINVAL;
92        break;
93    }
94
95    printk(KERN_DEBUG "%s: using fixed link parameters\n", dev->name);
96
97    ag->duplex = pdata->duplex;
98    ag->speed = pdata->speed;
99
100    return ret;
101}
102
103static int ag71xx_phy_connect_multi(struct ag71xx *ag)
104{
105    struct net_device *dev = ag->dev;
106    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
107    struct phy_device *phydev = NULL;
108    int phy_addr;
109    int ret = 0;
110
111    for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
112        if (!(pdata->phy_mask & (1 << phy_addr)))
113            continue;
114
115        if (ag->mii_bus->phy_map[phy_addr] == NULL)
116            continue;
117
118        DBG("%s: PHY found at %s, uid=%08x\n",
119            dev->name,
120            dev_name(&ag->mii_bus->phy_map[phy_addr]->dev),
121            ag->mii_bus->phy_map[phy_addr]->phy_id);
122
123        if (phydev == NULL)
124            phydev = ag->mii_bus->phy_map[phy_addr];
125    }
126
127    if (!phydev) {
128        printk(KERN_ERR "%s: no PHY found with phy_mask=%08x\n",
129            dev->name, pdata->phy_mask);
130        return -ENODEV;
131    }
132
133    ag->phy_dev = phy_connect(dev, dev_name(&phydev->dev),
134                  &ag71xx_phy_link_adjust, 0,
135                  pdata->phy_if_mode);
136
137    if (IS_ERR(ag->phy_dev)) {
138        printk(KERN_ERR "%s: could not connect to PHY at %s\n",
139            dev->name, dev_name(&phydev->dev));
140        return PTR_ERR(ag->phy_dev);
141    }
142
143    /* mask with MAC supported features */
144    if (pdata->has_gbit)
145        phydev->supported &= PHY_GBIT_FEATURES;
146    else
147        phydev->supported &= PHY_BASIC_FEATURES;
148
149    phydev->advertising = phydev->supported;
150
151    printk(KERN_DEBUG "%s: connected to PHY at %s [uid=%08x, driver=%s]\n",
152        dev->name, dev_name(&phydev->dev),
153        phydev->phy_id, phydev->drv->name);
154
155    ag->link = 0;
156    ag->speed = 0;
157    ag->duplex = -1;
158
159    return ret;
160}
161
162static int dev_is_class(struct device *dev, void *class)
163{
164    if (dev->class != NULL && !strcmp(dev->class->name, class))
165        return 1;
166
167    return 0;
168}
169
170static struct device *dev_find_class(struct device *parent, char *class)
171{
172    if (dev_is_class(parent, class)) {
173        get_device(parent);
174        return parent;
175    }
176
177    return device_find_child(parent, class, dev_is_class);
178}
179
180static struct mii_bus *dev_to_mii_bus(struct device *dev)
181{
182    struct device *d;
183
184    d = dev_find_class(dev, "mdio_bus");
185    if (d != NULL) {
186        struct mii_bus *bus;
187
188        bus = to_mii_bus(d);
189        put_device(d);
190
191        return bus;
192    }
193
194    return NULL;
195}
196
197int __devinit ag71xx_phy_connect(struct ag71xx *ag)
198{
199    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
200
201    ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev);
202    if (ag->mii_bus == NULL) {
203        printk(KERN_ERR "%s: unable to find MII bus on device '%s'\n",
204            ag->dev->name, dev_name(pdata->mii_bus_dev));
205        return -ENODEV;
206    }
207
208    /* Reset the mdio bus explicitly */
209    if (ag->mii_bus->reset) {
210        mutex_lock(&ag->mii_bus->mdio_lock);
211        ag->mii_bus->reset(ag->mii_bus);
212        mutex_unlock(&ag->mii_bus->mdio_lock);
213    }
214
215    if (pdata->has_ar7240_switch)
216        return ag71xx_ar7240_init(ag);
217
218    if (pdata->phy_mask)
219        return ag71xx_phy_connect_multi(ag);
220
221    return ag71xx_phy_connect_fixed(ag);
222}
223
224void ag71xx_phy_disconnect(struct ag71xx *ag)
225{
226    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
227
228    if (pdata->has_ar7240_switch)
229        ag71xx_ar7240_cleanup(ag);
230    else if (ag->phy_dev)
231        phy_disconnect(ag->phy_dev);
232}
233

Archive Download this file



interactive