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