net: phy: at803x: Allow specifying the RGMII RX clock delay via phy mode
[deliverable/linux.git] / drivers / net / phy / at803x.c
CommitLineData
0ca7111a
MU
1/*
2 * drivers/net/phy/at803x.c
3 *
4 * Driver for Atheros 803x PHY
5 *
6 * Author: Matus Ujhelyi <ujhelyi.m@gmail.com>
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 */
13
14#include <linux/phy.h>
15#include <linux/module.h>
16#include <linux/string.h>
17#include <linux/netdevice.h>
18#include <linux/etherdevice.h>
13a56b44
DM
19#include <linux/of_gpio.h>
20#include <linux/gpio/consumer.h>
0ca7111a
MU
21
22#define AT803X_INTR_ENABLE 0x12
23#define AT803X_INTR_STATUS 0x13
13a56b44
DM
24#define AT803X_SMART_SPEED 0x14
25#define AT803X_LED_CONTROL 0x18
0ca7111a
MU
26#define AT803X_WOL_ENABLE 0x01
27#define AT803X_DEVICE_ADDR 0x03
28#define AT803X_LOC_MAC_ADDR_0_15_OFFSET 0x804C
29#define AT803X_LOC_MAC_ADDR_16_31_OFFSET 0x804B
30#define AT803X_LOC_MAC_ADDR_32_47_OFFSET 0x804A
31#define AT803X_MMD_ACCESS_CONTROL 0x0D
32#define AT803X_MMD_ACCESS_CONTROL_DATA 0x0E
33#define AT803X_FUNC_DATA 0x4003
77a99394
ZQ
34#define AT803X_INER 0x0012
35#define AT803X_INER_INIT 0xec00
36#define AT803X_INSR 0x0013
1ca6d1b1
M
37#define AT803X_DEBUG_ADDR 0x1D
38#define AT803X_DEBUG_DATA 0x1E
2e5f9f28
MB
39#define AT803X_DEBUG_REG_0 0x00
40#define AT803X_DEBUG_RX_CLK_DLY_EN BIT(15)
41#define AT803X_DEBUG_REG_5 0x05
42#define AT803X_DEBUG_TX_CLK_DLY_EN BIT(8)
0ca7111a 43
bd8ca17f
DM
44#define ATH8030_PHY_ID 0x004dd076
45#define ATH8031_PHY_ID 0x004dd074
46#define ATH8035_PHY_ID 0x004dd072
47
0ca7111a
MU
48MODULE_DESCRIPTION("Atheros 803x PHY driver");
49MODULE_AUTHOR("Matus Ujhelyi");
50MODULE_LICENSE("GPL");
51
13a56b44
DM
52struct at803x_priv {
53 bool phy_reset:1;
54 struct gpio_desc *gpiod_reset;
55};
56
57struct at803x_context {
58 u16 bmcr;
59 u16 advertise;
60 u16 control1000;
61 u16 int_enable;
62 u16 smart_speed;
63 u16 led_control;
64};
65
2e5f9f28
MB
66static int at803x_debug_reg_read(struct phy_device *phydev, u16 reg)
67{
68 int ret;
69
70 ret = phy_write(phydev, AT803X_DEBUG_ADDR, reg);
71 if (ret < 0)
72 return ret;
73
74 return phy_read(phydev, AT803X_DEBUG_DATA);
75}
76
77static int at803x_debug_reg_mask(struct phy_device *phydev, u16 reg,
78 u16 clear, u16 set)
79{
80 u16 val;
81 int ret;
82
83 ret = at803x_debug_reg_read(phydev, reg);
84 if (ret < 0)
85 return ret;
86
87 val = ret & 0xffff;
88 val &= ~clear;
89 val |= set;
90
91 return phy_write(phydev, AT803X_DEBUG_DATA, val);
92}
93
94static inline int at803x_enable_rx_delay(struct phy_device *phydev)
95{
96 return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_0, 0,
97 AT803X_DEBUG_RX_CLK_DLY_EN);
98}
99
100static inline int at803x_enable_tx_delay(struct phy_device *phydev)
101{
102 return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_5, 0,
103 AT803X_DEBUG_TX_CLK_DLY_EN);
104}
105
13a56b44
DM
106/* save relevant PHY registers to private copy */
107static void at803x_context_save(struct phy_device *phydev,
108 struct at803x_context *context)
109{
110 context->bmcr = phy_read(phydev, MII_BMCR);
111 context->advertise = phy_read(phydev, MII_ADVERTISE);
112 context->control1000 = phy_read(phydev, MII_CTRL1000);
113 context->int_enable = phy_read(phydev, AT803X_INTR_ENABLE);
114 context->smart_speed = phy_read(phydev, AT803X_SMART_SPEED);
115 context->led_control = phy_read(phydev, AT803X_LED_CONTROL);
116}
117
118/* restore relevant PHY registers from private copy */
119static void at803x_context_restore(struct phy_device *phydev,
120 const struct at803x_context *context)
121{
122 phy_write(phydev, MII_BMCR, context->bmcr);
123 phy_write(phydev, MII_ADVERTISE, context->advertise);
124 phy_write(phydev, MII_CTRL1000, context->control1000);
125 phy_write(phydev, AT803X_INTR_ENABLE, context->int_enable);
126 phy_write(phydev, AT803X_SMART_SPEED, context->smart_speed);
127 phy_write(phydev, AT803X_LED_CONTROL, context->led_control);
128}
129
ea13c9ee
M
130static int at803x_set_wol(struct phy_device *phydev,
131 struct ethtool_wolinfo *wol)
0ca7111a
MU
132{
133 struct net_device *ndev = phydev->attached_dev;
134 const u8 *mac;
ea13c9ee
M
135 int ret;
136 u32 value;
0ca7111a
MU
137 unsigned int i, offsets[] = {
138 AT803X_LOC_MAC_ADDR_32_47_OFFSET,
139 AT803X_LOC_MAC_ADDR_16_31_OFFSET,
140 AT803X_LOC_MAC_ADDR_0_15_OFFSET,
141 };
142
143 if (!ndev)
ea13c9ee 144 return -ENODEV;
0ca7111a 145
ea13c9ee
M
146 if (wol->wolopts & WAKE_MAGIC) {
147 mac = (const u8 *) ndev->dev_addr;
0ca7111a 148
ea13c9ee
M
149 if (!is_valid_ether_addr(mac))
150 return -EFAULT;
0ca7111a 151
ea13c9ee
M
152 for (i = 0; i < 3; i++) {
153 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
0ca7111a 154 AT803X_DEVICE_ADDR);
ea13c9ee 155 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
0ca7111a 156 offsets[i]);
ea13c9ee 157 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL,
0ca7111a 158 AT803X_FUNC_DATA);
ea13c9ee 159 phy_write(phydev, AT803X_MMD_ACCESS_CONTROL_DATA,
0ca7111a 160 mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
ea13c9ee
M
161 }
162
163 value = phy_read(phydev, AT803X_INTR_ENABLE);
164 value |= AT803X_WOL_ENABLE;
165 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
166 if (ret)
167 return ret;
168 value = phy_read(phydev, AT803X_INTR_STATUS);
169 } else {
170 value = phy_read(phydev, AT803X_INTR_ENABLE);
171 value &= (~AT803X_WOL_ENABLE);
172 ret = phy_write(phydev, AT803X_INTR_ENABLE, value);
173 if (ret)
174 return ret;
175 value = phy_read(phydev, AT803X_INTR_STATUS);
0ca7111a 176 }
ea13c9ee
M
177
178 return ret;
179}
180
181static void at803x_get_wol(struct phy_device *phydev,
182 struct ethtool_wolinfo *wol)
183{
184 u32 value;
185
186 wol->supported = WAKE_MAGIC;
187 wol->wolopts = 0;
188
189 value = phy_read(phydev, AT803X_INTR_ENABLE);
190 if (value & AT803X_WOL_ENABLE)
191 wol->wolopts |= WAKE_MAGIC;
0ca7111a
MU
192}
193
6229ed1f
DM
194static int at803x_suspend(struct phy_device *phydev)
195{
196 int value;
197 int wol_enabled;
198
199 mutex_lock(&phydev->lock);
200
201 value = phy_read(phydev, AT803X_INTR_ENABLE);
202 wol_enabled = value & AT803X_WOL_ENABLE;
203
204 value = phy_read(phydev, MII_BMCR);
205
206 if (wol_enabled)
207 value |= BMCR_ISOLATE;
208 else
209 value |= BMCR_PDOWN;
210
211 phy_write(phydev, MII_BMCR, value);
212
213 mutex_unlock(&phydev->lock);
214
215 return 0;
216}
217
218static int at803x_resume(struct phy_device *phydev)
219{
220 int value;
221
222 mutex_lock(&phydev->lock);
223
224 value = phy_read(phydev, MII_BMCR);
225 value &= ~(BMCR_PDOWN | BMCR_ISOLATE);
226 phy_write(phydev, MII_BMCR, value);
227
228 mutex_unlock(&phydev->lock);
229
230 return 0;
231}
232
13a56b44
DM
233static int at803x_probe(struct phy_device *phydev)
234{
e5a03bfd 235 struct device *dev = &phydev->mdio.dev;
13a56b44 236 struct at803x_priv *priv;
687908c2 237 struct gpio_desc *gpiod_reset;
13a56b44 238
8f2877ca 239 priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
13a56b44
DM
240 if (!priv)
241 return -ENOMEM;
242
687908c2
UKK
243 gpiod_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
244 if (IS_ERR(gpiod_reset))
245 return PTR_ERR(gpiod_reset);
246
247 priv->gpiod_reset = gpiod_reset;
13a56b44
DM
248
249 phydev->priv = priv;
250
251 return 0;
252}
253
0ca7111a
MU
254static int at803x_config_init(struct phy_device *phydev)
255{
1ca6d1b1 256 int ret;
0ca7111a 257
6ff01dbb
DM
258 ret = genphy_config_init(phydev);
259 if (ret < 0)
260 return ret;
0ca7111a 261
2e5f9f28
MB
262 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID ||
263 phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
264 ret = at803x_enable_rx_delay(phydev);
265 if (ret < 0)
1ca6d1b1 266 return ret;
2e5f9f28
MB
267 }
268
269 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID ||
270 phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) {
271 ret = at803x_enable_tx_delay(phydev);
272 if (ret < 0)
1ca6d1b1
M
273 return ret;
274 }
275
0ca7111a
MU
276 return 0;
277}
278
77a99394
ZQ
279static int at803x_ack_interrupt(struct phy_device *phydev)
280{
281 int err;
282
283 err = phy_read(phydev, AT803X_INSR);
284
285 return (err < 0) ? err : 0;
286}
287
288static int at803x_config_intr(struct phy_device *phydev)
289{
290 int err;
291 int value;
292
293 value = phy_read(phydev, AT803X_INER);
294
295 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
296 err = phy_write(phydev, AT803X_INER,
297 value | AT803X_INER_INIT);
298 else
299 err = phy_write(phydev, AT803X_INER, 0);
300
301 return err;
302}
303
13a56b44
DM
304static void at803x_link_change_notify(struct phy_device *phydev)
305{
306 struct at803x_priv *priv = phydev->priv;
307
308 /*
309 * Conduct a hardware reset for AT8030 every time a link loss is
310 * signalled. This is necessary to circumvent a hardware bug that
311 * occurs when the cable is unplugged while TX packets are pending
312 * in the FIFO. In such cases, the FIFO enters an error mode it
313 * cannot recover from by software.
314 */
315 if (phydev->drv->phy_id == ATH8030_PHY_ID) {
316 if (phydev->state == PHY_NOLINK) {
317 if (priv->gpiod_reset && !priv->phy_reset) {
318 struct at803x_context context;
319
320 at803x_context_save(phydev, &context);
321
322 gpiod_set_value(priv->gpiod_reset, 0);
323 msleep(1);
324 gpiod_set_value(priv->gpiod_reset, 1);
325 msleep(1);
326
327 at803x_context_restore(phydev, &context);
328
72ba48be
AL
329 phydev_dbg(phydev, "%s(): phy was reset\n",
330 __func__);
13a56b44
DM
331 priv->phy_reset = true;
332 }
333 } else {
334 priv->phy_reset = false;
335 }
336 }
337}
338
317420ab
M
339static struct phy_driver at803x_driver[] = {
340{
341 /* ATHEROS 8035 */
13a56b44
DM
342 .phy_id = ATH8035_PHY_ID,
343 .name = "Atheros 8035 ethernet",
344 .phy_id_mask = 0xffffffef,
345 .probe = at803x_probe,
346 .config_init = at803x_config_init,
347 .link_change_notify = at803x_link_change_notify,
348 .set_wol = at803x_set_wol,
349 .get_wol = at803x_get_wol,
350 .suspend = at803x_suspend,
351 .resume = at803x_resume,
352 .features = PHY_GBIT_FEATURES,
353 .flags = PHY_HAS_INTERRUPT,
354 .config_aneg = genphy_config_aneg,
355 .read_status = genphy_read_status,
0eae5982
MR
356 .ack_interrupt = at803x_ack_interrupt,
357 .config_intr = at803x_config_intr,
317420ab
M
358}, {
359 /* ATHEROS 8030 */
13a56b44
DM
360 .phy_id = ATH8030_PHY_ID,
361 .name = "Atheros 8030 ethernet",
362 .phy_id_mask = 0xffffffef,
363 .probe = at803x_probe,
364 .config_init = at803x_config_init,
365 .link_change_notify = at803x_link_change_notify,
366 .set_wol = at803x_set_wol,
367 .get_wol = at803x_get_wol,
368 .suspend = at803x_suspend,
369 .resume = at803x_resume,
e15bb4c6 370 .features = PHY_BASIC_FEATURES,
13a56b44
DM
371 .flags = PHY_HAS_INTERRUPT,
372 .config_aneg = genphy_config_aneg,
373 .read_status = genphy_read_status,
0eae5982
MR
374 .ack_interrupt = at803x_ack_interrupt,
375 .config_intr = at803x_config_intr,
05d7cce8
M
376}, {
377 /* ATHEROS 8031 */
13a56b44
DM
378 .phy_id = ATH8031_PHY_ID,
379 .name = "Atheros 8031 ethernet",
380 .phy_id_mask = 0xffffffef,
381 .probe = at803x_probe,
382 .config_init = at803x_config_init,
383 .link_change_notify = at803x_link_change_notify,
384 .set_wol = at803x_set_wol,
385 .get_wol = at803x_get_wol,
386 .suspend = at803x_suspend,
387 .resume = at803x_resume,
388 .features = PHY_GBIT_FEATURES,
389 .flags = PHY_HAS_INTERRUPT,
390 .config_aneg = genphy_config_aneg,
391 .read_status = genphy_read_status,
392 .ack_interrupt = &at803x_ack_interrupt,
393 .config_intr = &at803x_config_intr,
317420ab 394} };
0ca7111a 395
50fd7150 396module_phy_driver(at803x_driver);
0ca7111a
MU
397
398static struct mdio_device_id __maybe_unused atheros_tbl[] = {
bd8ca17f
DM
399 { ATH8030_PHY_ID, 0xffffffef },
400 { ATH8031_PHY_ID, 0xffffffef },
401 { ATH8035_PHY_ID, 0xffffffef },
0ca7111a
MU
402 { }
403};
404
405MODULE_DEVICE_TABLE(mdio, atheros_tbl);
This page took 0.229367 seconds and 5 git commands to generate.