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 {
52        if (pdata->has_ar7240_switch)
53            ag71xx_ar7240_start(ag);
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
63    if (ag->phy_dev) {
64        phy_stop(ag->phy_dev);
65    } else {
66        if (pdata->has_ar7240_switch)
67            ag71xx_ar7240_stop(ag);
68        ag->link = 0;
69        ag71xx_link_adjust(ag);
70    }
71}
72
73static int ag71xx_phy_connect_fixed(struct ag71xx *ag)
74{
75    struct net_device *dev = ag->dev;
76    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
77    int ret = 0;
78
79    /* use fixed settings */
80    switch (pdata->speed) {
81    case SPEED_10:
82    case SPEED_100:
83    case SPEED_1000:
84        break;
85    default:
86        printk(KERN_ERR "%s: invalid speed specified\n", dev->name);
87        ret = -EINVAL;
88        break;
89    }
90
91    printk(KERN_DEBUG "%s: using fixed link parameters\n", dev->name);
92
93    ag->duplex = pdata->duplex;
94    ag->speed = pdata->speed;
95
96    return ret;
97}
98
99static int ag71xx_phy_connect_multi(struct ag71xx *ag)
100{
101    struct net_device *dev = ag->dev;
102    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
103    struct phy_device *phydev = NULL;
104    int phy_addr;
105    int ret = 0;
106
107    for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) {
108        if (!(pdata->phy_mask & (1 << phy_addr)))
109            continue;
110
111        if (ag->mii_bus->phy_map[phy_addr] == NULL)
112            continue;
113
114        DBG("%s: PHY found at %s, uid=%08x\n",
115            dev->name,
116            dev_name(&ag->mii_bus->phy_map[phy_addr]->dev),
117            ag->mii_bus->phy_map[phy_addr]->phy_id);
118
119        if (phydev == NULL)
120            phydev = ag->mii_bus->phy_map[phy_addr];
121    }
122
123    if (!phydev) {
124        printk(KERN_ERR "%s: no PHY found with phy_mask=%08x\n",
125            dev->name, pdata->phy_mask);
126        return -ENODEV;
127    }
128
129    ag->phy_dev = phy_connect(dev, dev_name(&phydev->dev),
130                  &ag71xx_phy_link_adjust, 0,
131                  pdata->phy_if_mode);
132
133    if (IS_ERR(ag->phy_dev)) {
134        printk(KERN_ERR "%s: could not connect to PHY at %s\n",
135            dev->name, dev_name(&phydev->dev));
136        return PTR_ERR(ag->phy_dev);
137    }
138
139    /* mask with MAC supported features */
140    if (pdata->has_gbit)
141        phydev->supported &= PHY_GBIT_FEATURES;
142    else
143        phydev->supported &= PHY_BASIC_FEATURES;
144
145    phydev->advertising = phydev->supported;
146
147    printk(KERN_DEBUG "%s: connected to PHY at %s [uid=%08x, driver=%s]\n",
148        dev->name, dev_name(&phydev->dev),
149        phydev->phy_id, phydev->drv->name);
150
151    ag->link = 0;
152    ag->speed = 0;
153    ag->duplex = -1;
154
155    return ret;
156}
157
158static int dev_is_class(struct device *dev, void *class)
159{
160    if (dev->class != NULL && !strcmp(dev->class->name, class))
161        return 1;
162
163    return 0;
164}
165
166static struct device *dev_find_class(struct device *parent, char *class)
167{
168    if (dev_is_class(parent, class)) {
169        get_device(parent);
170        return parent;
171    }
172
173    return device_find_child(parent, class, dev_is_class);
174}
175
176static struct mii_bus *dev_to_mii_bus(struct device *dev)
177{
178    struct device *d;
179
180    d = dev_find_class(dev, "mdio_bus");
181    if (d != NULL) {
182        struct mii_bus *bus;
183
184        bus = to_mii_bus(d);
185        put_device(d);
186
187        return bus;
188    }
189
190    return NULL;
191}
192
193int __devinit ag71xx_phy_connect(struct ag71xx *ag)
194{
195    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
196
197    ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev);
198    if (ag->mii_bus == NULL) {
199        printk(KERN_ERR "%s: unable to find MII bus on device '%s'\n",
200            ag->dev->name, dev_name(pdata->mii_bus_dev));
201        return -ENODEV;
202    }
203
204    /* Reset the mdio bus explicitly */
205    if (ag->mii_bus->reset) {
206        mutex_lock(&ag->mii_bus->mdio_lock);
207        ag->mii_bus->reset(ag->mii_bus);
208        mutex_unlock(&ag->mii_bus->mdio_lock);
209    }
210
211    if (pdata->has_ar7240_switch)
212        return ag71xx_ar7240_init(ag);
213
214    if (pdata->phy_mask)
215        return ag71xx_phy_connect_multi(ag);
216
217    return ag71xx_phy_connect_fixed(ag);
218}
219
220void __devexit ag71xx_phy_disconnect(struct ag71xx *ag)
221{
222    struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag);
223
224    if (pdata->has_ar7240_switch)
225        ag71xx_ar7240_cleanup(ag);
226    else if (ag->phy_dev)
227        phy_disconnect(ag->phy_dev);
228}
229

Archive Download this file



interactive