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

Archive Download this file



interactive