source: trunk/target/linux/ar71xx/files/drivers/net/ag71xx/ag71xx_phy.c @ 23575

Last change on this file since 23575 was 23575, checked in by nbd, 6 years ago

ar71xx: fix more section mismatches

  • Property svn:eol-style set to native
File size: 4.9 KB
Line 
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}
Note: See TracBrowser for help on using the repository browser.