Root/target/linux/ar71xx/files/drivers/net/ethernet/atheros/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->switch_data) {
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->switch_data)
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        netdev_err(dev, "invalid speed specified\n");
91        ret = -EINVAL;
92        break;
93    }
94
95    netdev_dbg(dev, "using fixed link parameters\n");
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        netdev_err(dev, "no PHY found with phy_mask=%08x\n",
129               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        netdev_err(dev, "could not connect to PHY at %s\n",
139               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    netdev_info(dev, "connected to PHY at %s [uid=%08x, driver=%s]\n",
152            dev_name(&phydev->dev), phydev->phy_id, phydev->drv->name);
153
154    ag->link = 0;
155    ag->speed = 0;
156    ag->duplex = -1;
157
158    return ret;
159}
160
161static int dev_is_class(struct device *dev, void *class)
162{
163    if (dev->class != NULL && !strcmp(dev->class->name, class))
164        return 1;
165
166    return 0;
167}
168
169static struct device *dev_find_class(struct device *parent, char *class)
170{
171    if (dev_is_class(parent, class)) {
172        get_device(parent);
173        return parent;
174    }
175
176    return device_find_child(parent, class, dev_is_class);
177}
178
179static struct mii_bus *dev_to_mii_bus(struct device *dev)
180{
181    struct device *d;
182
183    d = dev_find_class(dev, "mdio_bus");
184    if (d != NULL) {
185        struct mii_bus *bus;
186
187        bus = to_mii_bus(d);
188        put_device(d);
189
190        return bus;
191    }
192
193    return NULL;
194}
195
196int __devinit ag71xx_phy_connect(struct ag71xx *ag)
197{
198    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
199
200    if (pdata->mii_bus_dev == NULL ||
201        pdata->mii_bus_dev->bus == NULL )
202        return ag71xx_phy_connect_fixed(ag);
203
204    ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev);
205    if (ag->mii_bus == NULL) {
206        netdev_err(ag->dev, "unable to find MII bus on device '%s'\n",
207               dev_name(pdata->mii_bus_dev));
208        return -ENODEV;
209    }
210
211    /* Reset the mdio bus explicitly */
212    if (ag->mii_bus->reset) {
213        mutex_lock(&ag->mii_bus->mdio_lock);
214        ag->mii_bus->reset(ag->mii_bus);
215        mutex_unlock(&ag->mii_bus->mdio_lock);
216    }
217
218    if (pdata->switch_data)
219        return ag71xx_ar7240_init(ag);
220
221    if (pdata->phy_mask)
222        return ag71xx_phy_connect_multi(ag);
223
224    return ag71xx_phy_connect_fixed(ag);
225}
226
227void ag71xx_phy_disconnect(struct ag71xx *ag)
228{
229    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
230
231    if (pdata->switch_data)
232        ag71xx_ar7240_cleanup(ag);
233    else if (ag->phy_dev)
234        phy_disconnect(ag->phy_dev);
235}
236

Archive Download this file



interactive