Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Driver for the Texas Instruments DP83822 PHY
  4 *
  5 * Copyright (C) 2017 Texas Instruments Inc.
  6 */
  7
  8#include <linux/ethtool.h>
  9#include <linux/etherdevice.h>
 10#include <linux/kernel.h>
 11#include <linux/mii.h>
 12#include <linux/module.h>
 13#include <linux/of.h>
 14#include <linux/phy.h>
 15#include <linux/netdevice.h>
 16
 17#define DP83822_PHY_ID	        0x2000a240
 18#define DP83825I_PHY_ID		0x2000a150
 19
 20#define DP83822_DEVADDR		0x1f
 21
 22#define MII_DP83822_PHYSCR	0x11
 23#define MII_DP83822_MISR1	0x12
 24#define MII_DP83822_MISR2	0x13
 25#define MII_DP83822_RESET_CTRL	0x1f
 26
 27#define DP83822_HW_RESET	BIT(15)
 28#define DP83822_SW_RESET	BIT(14)
 29
 30/* PHYSCR Register Fields */
 31#define DP83822_PHYSCR_INT_OE		BIT(0) /* Interrupt Output Enable */
 32#define DP83822_PHYSCR_INTEN		BIT(1) /* Interrupt Enable */
 33
 34/* MISR1 bits */
 35#define DP83822_RX_ERR_HF_INT_EN	BIT(0)
 36#define DP83822_FALSE_CARRIER_HF_INT_EN	BIT(1)
 37#define DP83822_ANEG_COMPLETE_INT_EN	BIT(2)
 38#define DP83822_DUP_MODE_CHANGE_INT_EN	BIT(3)
 39#define DP83822_SPEED_CHANGED_INT_EN	BIT(4)
 40#define DP83822_LINK_STAT_INT_EN	BIT(5)
 41#define DP83822_ENERGY_DET_INT_EN	BIT(6)
 42#define DP83822_LINK_QUAL_INT_EN	BIT(7)
 43
 44/* MISR2 bits */
 45#define DP83822_JABBER_DET_INT_EN	BIT(0)
 46#define DP83822_WOL_PKT_INT_EN		BIT(1)
 47#define DP83822_SLEEP_MODE_INT_EN	BIT(2)
 48#define DP83822_MDI_XOVER_INT_EN	BIT(3)
 49#define DP83822_LB_FIFO_INT_EN		BIT(4)
 50#define DP83822_PAGE_RX_INT_EN		BIT(5)
 51#define DP83822_ANEG_ERR_INT_EN		BIT(6)
 52#define DP83822_EEE_ERROR_CHANGE_INT_EN	BIT(7)
 53
 54/* INT_STAT1 bits */
 55#define DP83822_WOL_INT_EN	BIT(4)
 56#define DP83822_WOL_INT_STAT	BIT(12)
 57
 58#define MII_DP83822_RXSOP1	0x04a5
 59#define	MII_DP83822_RXSOP2	0x04a6
 60#define	MII_DP83822_RXSOP3	0x04a7
 61
 62/* WoL Registers */
 63#define	MII_DP83822_WOL_CFG	0x04a0
 64#define	MII_DP83822_WOL_STAT	0x04a1
 65#define	MII_DP83822_WOL_DA1	0x04a2
 66#define	MII_DP83822_WOL_DA2	0x04a3
 67#define	MII_DP83822_WOL_DA3	0x04a4
 68
 69/* WoL bits */
 70#define DP83822_WOL_MAGIC_EN	BIT(0)
 71#define DP83822_WOL_SECURE_ON	BIT(5)
 72#define DP83822_WOL_EN		BIT(7)
 73#define DP83822_WOL_INDICATION_SEL BIT(8)
 74#define DP83822_WOL_CLR_INDICATION BIT(11)
 75
 76static int dp83822_ack_interrupt(struct phy_device *phydev)
 77{
 78	int err;
 79
 80	err = phy_read(phydev, MII_DP83822_MISR1);
 81	if (err < 0)
 82		return err;
 83
 84	err = phy_read(phydev, MII_DP83822_MISR2);
 85	if (err < 0)
 86		return err;
 87
 88	return 0;
 89}
 90
 91static int dp83822_set_wol(struct phy_device *phydev,
 92			   struct ethtool_wolinfo *wol)
 93{
 94	struct net_device *ndev = phydev->attached_dev;
 95	u16 value;
 96	const u8 *mac;
 97
 98	if (wol->wolopts & (WAKE_MAGIC | WAKE_MAGICSECURE)) {
 99		mac = (const u8 *)ndev->dev_addr;
100
101		if (!is_valid_ether_addr(mac))
102			return -EINVAL;
103
104		/* MAC addresses start with byte 5, but stored in mac[0].
105		 * 822 PHYs store bytes 4|5, 2|3, 0|1
106		 */
107		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA1,
108			      (mac[1] << 8) | mac[0]);
109		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA2,
110			      (mac[3] << 8) | mac[2]);
111		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_DA3,
112			      (mac[5] << 8) | mac[4]);
113
114		value = phy_read_mmd(phydev, DP83822_DEVADDR,
115				     MII_DP83822_WOL_CFG);
116		if (wol->wolopts & WAKE_MAGIC)
117			value |= DP83822_WOL_MAGIC_EN;
118		else
119			value &= ~DP83822_WOL_MAGIC_EN;
120
121		if (wol->wolopts & WAKE_MAGICSECURE) {
122			phy_write_mmd(phydev, DP83822_DEVADDR,
123				      MII_DP83822_RXSOP1,
124				      (wol->sopass[1] << 8) | wol->sopass[0]);
125			phy_write_mmd(phydev, DP83822_DEVADDR,
126				      MII_DP83822_RXSOP2,
127				      (wol->sopass[3] << 8) | wol->sopass[2]);
128			phy_write_mmd(phydev, DP83822_DEVADDR,
129				      MII_DP83822_RXSOP3,
130				      (wol->sopass[5] << 8) | wol->sopass[4]);
131			value |= DP83822_WOL_SECURE_ON;
132		} else {
133			value &= ~DP83822_WOL_SECURE_ON;
134		}
135
136		value |= (DP83822_WOL_EN | DP83822_WOL_INDICATION_SEL |
137			  DP83822_WOL_CLR_INDICATION);
138		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
139			      value);
140	} else {
141		value = phy_read_mmd(phydev, DP83822_DEVADDR,
142				     MII_DP83822_WOL_CFG);
143		value &= ~DP83822_WOL_EN;
144		phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
145			      value);
146	}
147
148	return 0;
149}
150
151static void dp83822_get_wol(struct phy_device *phydev,
152			    struct ethtool_wolinfo *wol)
153{
154	int value;
155	u16 sopass_val;
156
157	wol->supported = (WAKE_MAGIC | WAKE_MAGICSECURE);
158	wol->wolopts = 0;
159
160	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
161
162	if (value & DP83822_WOL_MAGIC_EN)
163		wol->wolopts |= WAKE_MAGIC;
164
165	if (value & DP83822_WOL_SECURE_ON) {
166		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
167					  MII_DP83822_RXSOP1);
168		wol->sopass[0] = (sopass_val & 0xff);
169		wol->sopass[1] = (sopass_val >> 8);
170
171		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
172					  MII_DP83822_RXSOP2);
173		wol->sopass[2] = (sopass_val & 0xff);
174		wol->sopass[3] = (sopass_val >> 8);
175
176		sopass_val = phy_read_mmd(phydev, DP83822_DEVADDR,
177					  MII_DP83822_RXSOP3);
178		wol->sopass[4] = (sopass_val & 0xff);
179		wol->sopass[5] = (sopass_val >> 8);
180
181		wol->wolopts |= WAKE_MAGICSECURE;
182	}
183
184	/* WoL is not enabled so set wolopts to 0 */
185	if (!(value & DP83822_WOL_EN))
186		wol->wolopts = 0;
187}
188
189static int dp83822_config_intr(struct phy_device *phydev)
190{
191	int misr_status;
192	int physcr_status;
193	int err;
194
195	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
196		misr_status = phy_read(phydev, MII_DP83822_MISR1);
197		if (misr_status < 0)
198			return misr_status;
199
200		misr_status |= (DP83822_RX_ERR_HF_INT_EN |
201				DP83822_FALSE_CARRIER_HF_INT_EN |
202				DP83822_ANEG_COMPLETE_INT_EN |
203				DP83822_DUP_MODE_CHANGE_INT_EN |
204				DP83822_SPEED_CHANGED_INT_EN |
205				DP83822_LINK_STAT_INT_EN |
206				DP83822_ENERGY_DET_INT_EN |
207				DP83822_LINK_QUAL_INT_EN);
208
209		err = phy_write(phydev, MII_DP83822_MISR1, misr_status);
210		if (err < 0)
211			return err;
212
213		misr_status = phy_read(phydev, MII_DP83822_MISR2);
214		if (misr_status < 0)
215			return misr_status;
216
217		misr_status |= (DP83822_JABBER_DET_INT_EN |
218				DP83822_WOL_PKT_INT_EN |
219				DP83822_SLEEP_MODE_INT_EN |
220				DP83822_MDI_XOVER_INT_EN |
221				DP83822_LB_FIFO_INT_EN |
222				DP83822_PAGE_RX_INT_EN |
223				DP83822_ANEG_ERR_INT_EN |
224				DP83822_EEE_ERROR_CHANGE_INT_EN);
225
226		err = phy_write(phydev, MII_DP83822_MISR2, misr_status);
227		if (err < 0)
228			return err;
229
230		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
231		if (physcr_status < 0)
232			return physcr_status;
233
234		physcr_status |= DP83822_PHYSCR_INT_OE | DP83822_PHYSCR_INTEN;
235
236	} else {
237		err = phy_write(phydev, MII_DP83822_MISR1, 0);
238		if (err < 0)
239			return err;
240
241		err = phy_write(phydev, MII_DP83822_MISR1, 0);
242		if (err < 0)
243			return err;
244
245		physcr_status = phy_read(phydev, MII_DP83822_PHYSCR);
246		if (physcr_status < 0)
247			return physcr_status;
248
249		physcr_status &= ~DP83822_PHYSCR_INTEN;
250	}
251
252	return phy_write(phydev, MII_DP83822_PHYSCR, physcr_status);
253}
254
255static int dp83822_config_init(struct phy_device *phydev)
256{
257	int value;
258
259	value = DP83822_WOL_MAGIC_EN | DP83822_WOL_SECURE_ON | DP83822_WOL_EN;
260
261	return phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG,
262	      value);
263}
264
265static int dp83822_phy_reset(struct phy_device *phydev)
266{
267	int err;
268
269	err = phy_write(phydev, MII_DP83822_RESET_CTRL, DP83822_HW_RESET);
270	if (err < 0)
271		return err;
272
273	dp83822_config_init(phydev);
274
275	return 0;
276}
277
278static int dp83822_suspend(struct phy_device *phydev)
279{
280	int value;
281
282	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
283
284	if (!(value & DP83822_WOL_EN))
285		genphy_suspend(phydev);
286
287	return 0;
288}
289
290static int dp83822_resume(struct phy_device *phydev)
291{
292	int value;
293
294	genphy_resume(phydev);
295
296	value = phy_read_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG);
297
298	phy_write_mmd(phydev, DP83822_DEVADDR, MII_DP83822_WOL_CFG, value |
299		      DP83822_WOL_CLR_INDICATION);
300
301	return 0;
302}
303
304#define DP83822_PHY_DRIVER(_id, _name)				\
305	{							\
306		PHY_ID_MATCH_MODEL(_id),			\
307		.name		= (_name),			\
308		/* PHY_BASIC_FEATURES */			\
309		.soft_reset	= dp83822_phy_reset,		\
310		.config_init	= dp83822_config_init,		\
311		.get_wol = dp83822_get_wol,			\
312		.set_wol = dp83822_set_wol,			\
313		.ack_interrupt = dp83822_ack_interrupt,		\
314		.config_intr = dp83822_config_intr,		\
315		.suspend = dp83822_suspend,			\
316		.resume = dp83822_resume,			\
317	}
318
319static struct phy_driver dp83822_driver[] = {
320	DP83822_PHY_DRIVER(DP83822_PHY_ID, "TI DP83822"),
321	DP83822_PHY_DRIVER(DP83825I_PHY_ID, "TI DP83825I"),
322};
323module_phy_driver(dp83822_driver);
324
325static struct mdio_device_id __maybe_unused dp83822_tbl[] = {
326	{ DP83822_PHY_ID, 0xfffffff0 },
327	{ DP83825I_PHY_ID, 0xfffffff0 },
328	{ },
329};
330MODULE_DEVICE_TABLE(mdio, dp83822_tbl);
331
332MODULE_DESCRIPTION("Texas Instruments DP83822 PHY driver");
333MODULE_AUTHOR("Dan Murphy <dmurphy@ti.com");
334MODULE_LICENSE("GPL v2");