16
16
#include <linux/string.h>
17
17
#include <linux/netdevice.h>
18
18
#include <linux/etherdevice.h>
19
+ #include <linux/of_gpio.h>
20
+ #include <linux/gpio/consumer.h>
19
21
20
22
#define AT803X_INTR_ENABLE 0x12
21
23
#define AT803X_INTR_STATUS 0x13
24
+ #define AT803X_SMART_SPEED 0x14
25
+ #define AT803X_LED_CONTROL 0x18
22
26
#define AT803X_WOL_ENABLE 0x01
23
27
#define AT803X_DEVICE_ADDR 0x03
24
28
#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
@@ -43,6 +47,44 @@ MODULE_DESCRIPTION("Atheros 803x PHY driver");
43
47
MODULE_AUTHOR ("Matus Ujhelyi" );
44
48
MODULE_LICENSE ("GPL" );
45
49
50
+ struct at803x_priv {
51
+ bool phy_reset :1 ;
52
+ struct gpio_desc * gpiod_reset ;
53
+ };
54
+
55
+ struct at803x_context {
56
+ u16 bmcr ;
57
+ u16 advertise ;
58
+ u16 control1000 ;
59
+ u16 int_enable ;
60
+ u16 smart_speed ;
61
+ u16 led_control ;
62
+ };
63
+
64
+ /* save relevant PHY registers to private copy */
65
+ static void at803x_context_save (struct phy_device * phydev ,
66
+ struct at803x_context * context )
67
+ {
68
+ context -> bmcr = phy_read (phydev , MII_BMCR );
69
+ context -> advertise = phy_read (phydev , MII_ADVERTISE );
70
+ context -> control1000 = phy_read (phydev , MII_CTRL1000 );
71
+ context -> int_enable = phy_read (phydev , AT803X_INTR_ENABLE );
72
+ context -> smart_speed = phy_read (phydev , AT803X_SMART_SPEED );
73
+ context -> led_control = phy_read (phydev , AT803X_LED_CONTROL );
74
+ }
75
+
76
+ /* restore relevant PHY registers from private copy */
77
+ static void at803x_context_restore (struct phy_device * phydev ,
78
+ const struct at803x_context * context )
79
+ {
80
+ phy_write (phydev , MII_BMCR , context -> bmcr );
81
+ phy_write (phydev , MII_ADVERTISE , context -> advertise );
82
+ phy_write (phydev , MII_CTRL1000 , context -> control1000 );
83
+ phy_write (phydev , AT803X_INTR_ENABLE , context -> int_enable );
84
+ phy_write (phydev , AT803X_SMART_SPEED , context -> smart_speed );
85
+ phy_write (phydev , AT803X_LED_CONTROL , context -> led_control );
86
+ }
87
+
46
88
static int at803x_set_wol (struct phy_device * phydev ,
47
89
struct ethtool_wolinfo * wol )
48
90
{
@@ -146,6 +188,26 @@ static int at803x_resume(struct phy_device *phydev)
146
188
return 0 ;
147
189
}
148
190
191
+ static int at803x_probe (struct phy_device * phydev )
192
+ {
193
+ struct device * dev = & phydev -> dev ;
194
+ struct at803x_priv * priv ;
195
+
196
+ priv = devm_kzalloc (dev , sizeof (priv ), GFP_KERNEL );
197
+ if (!priv )
198
+ return - ENOMEM ;
199
+
200
+ priv -> gpiod_reset = devm_gpiod_get (dev , "reset" );
201
+ if (IS_ERR (priv -> gpiod_reset ))
202
+ priv -> gpiod_reset = NULL ;
203
+ else
204
+ gpiod_direction_output (priv -> gpiod_reset , 1 );
205
+
206
+ phydev -> priv = priv ;
207
+
208
+ return 0 ;
209
+ }
210
+
149
211
static int at803x_config_init (struct phy_device * phydev )
150
212
{
151
213
int ret ;
@@ -193,58 +255,99 @@ static int at803x_config_intr(struct phy_device *phydev)
193
255
return err ;
194
256
}
195
257
258
+ static void at803x_link_change_notify (struct phy_device * phydev )
259
+ {
260
+ struct at803x_priv * priv = phydev -> priv ;
261
+
262
+ /*
263
+ * Conduct a hardware reset for AT8030 every time a link loss is
264
+ * signalled. This is necessary to circumvent a hardware bug that
265
+ * occurs when the cable is unplugged while TX packets are pending
266
+ * in the FIFO. In such cases, the FIFO enters an error mode it
267
+ * cannot recover from by software.
268
+ */
269
+ if (phydev -> drv -> phy_id == ATH8030_PHY_ID ) {
270
+ if (phydev -> state == PHY_NOLINK ) {
271
+ if (priv -> gpiod_reset && !priv -> phy_reset ) {
272
+ struct at803x_context context ;
273
+
274
+ at803x_context_save (phydev , & context );
275
+
276
+ gpiod_set_value (priv -> gpiod_reset , 0 );
277
+ msleep (1 );
278
+ gpiod_set_value (priv -> gpiod_reset , 1 );
279
+ msleep (1 );
280
+
281
+ at803x_context_restore (phydev , & context );
282
+
283
+ dev_dbg (& phydev -> dev , "%s(): phy was reset\n" ,
284
+ __func__ );
285
+ priv -> phy_reset = true;
286
+ }
287
+ } else {
288
+ priv -> phy_reset = false;
289
+ }
290
+ }
291
+ }
292
+
196
293
static struct phy_driver at803x_driver [] = {
197
294
{
198
295
/* ATHEROS 8035 */
199
- .phy_id = ATH8035_PHY_ID ,
200
- .name = "Atheros 8035 ethernet" ,
201
- .phy_id_mask = 0xffffffef ,
202
- .config_init = at803x_config_init ,
203
- .set_wol = at803x_set_wol ,
204
- .get_wol = at803x_get_wol ,
205
- .suspend = at803x_suspend ,
206
- .resume = at803x_resume ,
207
- .features = PHY_GBIT_FEATURES ,
208
- .flags = PHY_HAS_INTERRUPT ,
209
- .config_aneg = genphy_config_aneg ,
210
- .read_status = genphy_read_status ,
211
- .driver = {
296
+ .phy_id = ATH8035_PHY_ID ,
297
+ .name = "Atheros 8035 ethernet" ,
298
+ .phy_id_mask = 0xffffffef ,
299
+ .probe = at803x_probe ,
300
+ .config_init = at803x_config_init ,
301
+ .link_change_notify = at803x_link_change_notify ,
302
+ .set_wol = at803x_set_wol ,
303
+ .get_wol = at803x_get_wol ,
304
+ .suspend = at803x_suspend ,
305
+ .resume = at803x_resume ,
306
+ .features = PHY_GBIT_FEATURES ,
307
+ .flags = PHY_HAS_INTERRUPT ,
308
+ .config_aneg = genphy_config_aneg ,
309
+ .read_status = genphy_read_status ,
310
+ .driver = {
212
311
.owner = THIS_MODULE ,
213
312
},
214
313
}, {
215
314
/* ATHEROS 8030 */
216
- .phy_id = ATH8030_PHY_ID ,
217
- .name = "Atheros 8030 ethernet" ,
218
- .phy_id_mask = 0xffffffef ,
219
- .config_init = at803x_config_init ,
220
- .set_wol = at803x_set_wol ,
221
- .get_wol = at803x_get_wol ,
222
- .suspend = at803x_suspend ,
223
- .resume = at803x_resume ,
224
- .features = PHY_GBIT_FEATURES ,
225
- .flags = PHY_HAS_INTERRUPT ,
226
- .config_aneg = genphy_config_aneg ,
227
- .read_status = genphy_read_status ,
228
- .driver = {
315
+ .phy_id = ATH8030_PHY_ID ,
316
+ .name = "Atheros 8030 ethernet" ,
317
+ .phy_id_mask = 0xffffffef ,
318
+ .probe = at803x_probe ,
319
+ .config_init = at803x_config_init ,
320
+ .link_change_notify = at803x_link_change_notify ,
321
+ .set_wol = at803x_set_wol ,
322
+ .get_wol = at803x_get_wol ,
323
+ .suspend = at803x_suspend ,
324
+ .resume = at803x_resume ,
325
+ .features = PHY_GBIT_FEATURES ,
326
+ .flags = PHY_HAS_INTERRUPT ,
327
+ .config_aneg = genphy_config_aneg ,
328
+ .read_status = genphy_read_status ,
329
+ .driver = {
229
330
.owner = THIS_MODULE ,
230
331
},
231
332
}, {
232
333
/* ATHEROS 8031 */
233
- .phy_id = ATH8031_PHY_ID ,
234
- .name = "Atheros 8031 ethernet" ,
235
- .phy_id_mask = 0xffffffef ,
236
- .config_init = at803x_config_init ,
237
- .set_wol = at803x_set_wol ,
238
- .get_wol = at803x_get_wol ,
239
- .suspend = at803x_suspend ,
240
- .resume = at803x_resume ,
241
- .features = PHY_GBIT_FEATURES ,
242
- .flags = PHY_HAS_INTERRUPT ,
243
- .config_aneg = genphy_config_aneg ,
244
- .read_status = genphy_read_status ,
245
- .ack_interrupt = & at803x_ack_interrupt ,
246
- .config_intr = & at803x_config_intr ,
247
- .driver = {
334
+ .phy_id = ATH8031_PHY_ID ,
335
+ .name = "Atheros 8031 ethernet" ,
336
+ .phy_id_mask = 0xffffffef ,
337
+ .probe = at803x_probe ,
338
+ .config_init = at803x_config_init ,
339
+ .link_change_notify = at803x_link_change_notify ,
340
+ .set_wol = at803x_set_wol ,
341
+ .get_wol = at803x_get_wol ,
342
+ .suspend = at803x_suspend ,
343
+ .resume = at803x_resume ,
344
+ .features = PHY_GBIT_FEATURES ,
345
+ .flags = PHY_HAS_INTERRUPT ,
346
+ .config_aneg = genphy_config_aneg ,
347
+ .read_status = genphy_read_status ,
348
+ .ack_interrupt = & at803x_ack_interrupt ,
349
+ .config_intr = & at803x_config_intr ,
350
+ .driver = {
248
351
.owner = THIS_MODULE ,
249
352
},
250
353
} };
0 commit comments