OpenWrt – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 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 | |||
16 | static 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 | |||
45 | void 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->mii_bus_dev && pdata->switch_data) { |
||
52 | ag71xx_ar7240_start(ag); |
||
53 | } else { |
||
54 | ag->link = 1; |
||
55 | ag71xx_link_adjust(ag); |
||
56 | } |
||
57 | } |
||
58 | |||
59 | void 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->mii_bus_dev && 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 | |||
77 | static int ag71xx_phy_connect_fixed(struct ag71xx *ag) |
||
78 | { |
||
79 | struct device *dev = &ag->pdev->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 | dev_err(dev, "invalid speed specified\n"); |
||
91 | ret = -EINVAL; |
||
92 | break; |
||
93 | } |
||
94 | |||
95 | dev_dbg(dev, "using fixed link parameters\n"); |
||
96 | |||
97 | ag->duplex = pdata->duplex; |
||
98 | ag->speed = pdata->speed; |
||
99 | |||
100 | return ret; |
||
101 | } |
||
102 | |||
103 | static int ag71xx_phy_connect_multi(struct ag71xx *ag) |
||
104 | { |
||
105 | struct device *dev = &ag->pdev->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 LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) |
||
116 | if (ag->mii_bus->phy_map[phy_addr] == NULL) |
||
117 | continue; |
||
118 | |||
119 | DBG("%s: PHY found at %s, uid=%08x\n", |
||
120 | dev_name(dev), |
||
121 | dev_name(&ag->mii_bus->phy_map[phy_addr]->dev), |
||
122 | ag->mii_bus->phy_map[phy_addr]->phy_id); |
||
123 | |||
124 | if (phydev == NULL) |
||
125 | phydev = ag->mii_bus->phy_map[phy_addr]; |
||
126 | #else |
||
127 | if (ag->mii_bus->mdio_map[phy_addr] == NULL) |
||
128 | continue; |
||
129 | |||
130 | DBG("%s: PHY found at %s, uid=%08x\n", |
||
131 | dev_name(dev), |
||
132 | dev_name(&ag->mii_bus->mdio_map[phy_addr]->dev), |
||
133 | ag->mii_bus->mdio_map[phy_addr]->phy_id); |
||
134 | |||
135 | if (phydev == NULL) |
||
136 | phydev = mdiobus_get_phy(ag->mii_bus, phy_addr); |
||
137 | #endif |
||
138 | } |
||
139 | |||
140 | if (!phydev) { |
||
141 | dev_err(dev, "no PHY found with phy_mask=%08x\n", |
||
142 | pdata->phy_mask); |
||
143 | return -ENODEV; |
||
144 | } |
||
145 | |||
146 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) |
||
147 | ag->phy_dev = phy_connect(ag->dev, dev_name(&phydev->dev), |
||
148 | #else |
||
149 | ag->phy_dev = phy_connect(ag->dev, phydev_name(phydev), |
||
150 | #endif |
||
151 | &ag71xx_phy_link_adjust, |
||
152 | pdata->phy_if_mode); |
||
153 | |||
154 | if (IS_ERR(ag->phy_dev)) { |
||
155 | dev_err(dev, "could not connect to PHY at %s\n", |
||
156 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) |
||
157 | dev_name(&phydev->dev)); |
||
158 | #else |
||
159 | phydev_name(phydev)); |
||
160 | #endif |
||
161 | return PTR_ERR(ag->phy_dev); |
||
162 | } |
||
163 | |||
164 | /* mask with MAC supported features */ |
||
165 | if (pdata->has_gbit) |
||
166 | phydev->supported &= PHY_GBIT_FEATURES; |
||
167 | else |
||
168 | phydev->supported &= PHY_BASIC_FEATURES; |
||
169 | |||
170 | phydev->advertising = phydev->supported; |
||
171 | |||
172 | dev_info(dev, "connected to PHY at %s [uid=%08x, driver=%s]\n", |
||
173 | #if LINUX_VERSION_CODE < KERNEL_VERSION(4,5,0) |
||
174 | dev_name(&phydev->dev), |
||
175 | #else |
||
176 | phydev_name(phydev), |
||
177 | #endif |
||
178 | phydev->phy_id, phydev->drv->name); |
||
179 | |||
180 | ag->link = 0; |
||
181 | ag->speed = 0; |
||
182 | ag->duplex = -1; |
||
183 | |||
184 | return ret; |
||
185 | } |
||
186 | |||
187 | static int dev_is_class(struct device *dev, void *class) |
||
188 | { |
||
189 | if (dev->class != NULL && !strcmp(dev->class->name, class)) |
||
190 | return 1; |
||
191 | |||
192 | return 0; |
||
193 | } |
||
194 | |||
195 | static struct device *dev_find_class(struct device *parent, char *class) |
||
196 | { |
||
197 | if (dev_is_class(parent, class)) { |
||
198 | get_device(parent); |
||
199 | return parent; |
||
200 | } |
||
201 | |||
202 | return device_find_child(parent, class, dev_is_class); |
||
203 | } |
||
204 | |||
205 | static struct mii_bus *dev_to_mii_bus(struct device *dev) |
||
206 | { |
||
207 | struct device *d; |
||
208 | |||
209 | d = dev_find_class(dev, "mdio_bus"); |
||
210 | if (d != NULL) { |
||
211 | struct mii_bus *bus; |
||
212 | |||
213 | bus = to_mii_bus(d); |
||
214 | put_device(d); |
||
215 | |||
216 | return bus; |
||
217 | } |
||
218 | |||
219 | return NULL; |
||
220 | } |
||
221 | |||
222 | int ag71xx_phy_connect(struct ag71xx *ag) |
||
223 | { |
||
224 | struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
||
225 | |||
226 | if (pdata->mii_bus_dev == NULL || |
||
227 | pdata->mii_bus_dev->bus == NULL ) |
||
228 | return ag71xx_phy_connect_fixed(ag); |
||
229 | |||
230 | ag->mii_bus = dev_to_mii_bus(pdata->mii_bus_dev); |
||
231 | if (ag->mii_bus == NULL) { |
||
232 | dev_err(&ag->pdev->dev, "unable to find MII bus on device '%s'\n", |
||
233 | dev_name(pdata->mii_bus_dev)); |
||
234 | return -ENODEV; |
||
235 | } |
||
236 | |||
237 | /* Reset the mdio bus explicitly */ |
||
238 | if (ag->mii_bus->reset) { |
||
239 | mutex_lock(&ag->mii_bus->mdio_lock); |
||
240 | ag->mii_bus->reset(ag->mii_bus); |
||
241 | mutex_unlock(&ag->mii_bus->mdio_lock); |
||
242 | } |
||
243 | |||
244 | if (pdata->switch_data) |
||
245 | return ag71xx_ar7240_init(ag); |
||
246 | |||
247 | if (pdata->phy_mask) |
||
248 | return ag71xx_phy_connect_multi(ag); |
||
249 | |||
250 | return ag71xx_phy_connect_fixed(ag); |
||
251 | } |
||
252 | |||
253 | void ag71xx_phy_disconnect(struct ag71xx *ag) |
||
254 | { |
||
255 | struct ag71xx_platform_data *pdata = ag71xx_get_pdata(ag); |
||
256 | |||
257 | if (pdata->switch_data) |
||
258 | ag71xx_ar7240_cleanup(ag); |
||
259 | else if (ag->phy_dev) |
||
260 | phy_disconnect(ag->phy_dev); |
||
261 | } |