Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0
  2// Copyright (C) 2018 Microchip Technology
  3
  4#include <linux/kernel.h>
  5#include <linux/module.h>
  6#include <linux/delay.h>
  7#include <linux/mii.h>
  8#include <linux/phy.h>
  9#include <linux/ethtool.h>
 10#include <linux/ethtool_netlink.h>
 11#include <linux/bitfield.h>
 12
 13#define PHY_ID_LAN87XX				0x0007c150
 14#define PHY_ID_LAN937X				0x0007c180
 15
 16/* External Register Control Register */
 17#define LAN87XX_EXT_REG_CTL                     (0x14)
 18#define LAN87XX_EXT_REG_CTL_RD_CTL              (0x1000)
 19#define LAN87XX_EXT_REG_CTL_WR_CTL              (0x0800)
 20#define LAN87XX_REG_BANK_SEL_MASK		GENMASK(10, 8)
 21#define LAN87XX_REG_ADDR_MASK			GENMASK(7, 0)
 22
 23/* External Register Read Data Register */
 24#define LAN87XX_EXT_REG_RD_DATA                 (0x15)
 25
 26/* External Register Write Data Register */
 27#define LAN87XX_EXT_REG_WR_DATA                 (0x16)
 28
 29/* Interrupt Source Register */
 30#define LAN87XX_INTERRUPT_SOURCE                (0x18)
 31#define LAN87XX_INTERRUPT_SOURCE_2              (0x08)
 32
 33/* Interrupt Mask Register */
 34#define LAN87XX_INTERRUPT_MASK                  (0x19)
 35#define LAN87XX_MASK_LINK_UP                    (0x0004)
 36#define LAN87XX_MASK_LINK_DOWN                  (0x0002)
 37
 38#define LAN87XX_INTERRUPT_MASK_2                (0x09)
 39#define LAN87XX_MASK_COMM_RDY			BIT(10)
 40
 41/* MISC Control 1 Register */
 42#define LAN87XX_CTRL_1                          (0x11)
 43#define LAN87XX_MASK_RGMII_TXC_DLY_EN           (0x4000)
 44#define LAN87XX_MASK_RGMII_RXC_DLY_EN           (0x2000)
 45
 46/* phyaccess nested types */
 47#define	PHYACC_ATTR_MODE_READ		0
 48#define	PHYACC_ATTR_MODE_WRITE		1
 49#define	PHYACC_ATTR_MODE_MODIFY		2
 50#define	PHYACC_ATTR_MODE_POLL		3
 51
 52#define	PHYACC_ATTR_BANK_SMI		0
 53#define	PHYACC_ATTR_BANK_MISC		1
 54#define	PHYACC_ATTR_BANK_PCS		2
 55#define	PHYACC_ATTR_BANK_AFE		3
 56#define	PHYACC_ATTR_BANK_DSP		4
 57#define	PHYACC_ATTR_BANK_MAX		7
 58
 59/* measurement defines */
 60#define	LAN87XX_CABLE_TEST_OK		0
 61#define	LAN87XX_CABLE_TEST_OPEN	1
 62#define	LAN87XX_CABLE_TEST_SAME_SHORT	2
 63
 64/* T1 Registers */
 65#define T1_AFE_PORT_CFG1_REG		0x0B
 66#define T1_POWER_DOWN_CONTROL_REG	0x1A
 67#define T1_SLV_FD_MULT_CFG_REG		0x18
 68#define T1_CDR_CFG_PRE_LOCK_REG		0x05
 69#define T1_CDR_CFG_POST_LOCK_REG	0x06
 70#define T1_LCK_STG2_MUFACT_CFG_REG	0x1A
 71#define T1_LCK_STG3_MUFACT_CFG_REG	0x1B
 72#define T1_POST_LCK_MUFACT_CFG_REG	0x1C
 73#define T1_TX_RX_FIFO_CFG_REG		0x02
 74#define T1_TX_LPF_FIR_CFG_REG		0x55
 75#define T1_COEF_CLK_PWR_DN_CFG		0x04
 76#define T1_COEF_RW_CTL_CFG		0x0D
 77#define T1_SQI_CONFIG_REG		0x2E
 78#define T1_SQI_CONFIG2_REG		0x4A
 79#define T1_DCQ_SQI_REG			0xC3
 80#define T1_DCQ_SQI_MSK			GENMASK(3, 1)
 81#define T1_MDIO_CONTROL2_REG		0x10
 82#define T1_INTERRUPT_SOURCE_REG		0x18
 83#define T1_INTERRUPT2_SOURCE_REG	0x08
 84#define T1_EQ_FD_STG1_FRZ_CFG		0x69
 85#define T1_EQ_FD_STG2_FRZ_CFG		0x6A
 86#define T1_EQ_FD_STG3_FRZ_CFG		0x6B
 87#define T1_EQ_FD_STG4_FRZ_CFG		0x6C
 88#define T1_EQ_WT_FD_LCK_FRZ_CFG		0x6D
 89#define T1_PST_EQ_LCK_STG1_FRZ_CFG	0x6E
 90
 91#define T1_MODE_STAT_REG		0x11
 92#define T1_LINK_UP_MSK			BIT(0)
 93
 94/* SQI defines */
 95#define LAN87XX_MAX_SQI			0x07
 96
 97#define DRIVER_AUTHOR	"Nisar Sayed <nisar.sayed@microchip.com>"
 98#define DRIVER_DESC	"Microchip LAN87XX/LAN937x T1 PHY driver"
 99
100struct access_ereg_val {
101	u8  mode;
102	u8  bank;
103	u8  offset;
104	u16 val;
105	u16 mask;
106};
107
108static int lan937x_dsp_workaround(struct phy_device *phydev, u16 ereg, u8 bank)
109{
110	u8 prev_bank;
111	int rc = 0;
112	u16 val;
113
114	mutex_lock(&phydev->lock);
115	/* Read previous selected bank */
116	rc = phy_read(phydev, LAN87XX_EXT_REG_CTL);
117	if (rc < 0)
118		goto out_unlock;
119
120	/* store the prev_bank */
121	prev_bank = FIELD_GET(LAN87XX_REG_BANK_SEL_MASK, rc);
122
123	if (bank != prev_bank && bank == PHYACC_ATTR_BANK_DSP) {
124		val = ereg & ~LAN87XX_REG_ADDR_MASK;
125
126		val &= ~LAN87XX_EXT_REG_CTL_WR_CTL;
127		val |= LAN87XX_EXT_REG_CTL_RD_CTL;
128
129		/* access twice for DSP bank change,dummy access */
130		rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, val);
131	}
132
133out_unlock:
134	mutex_unlock(&phydev->lock);
135
136	return rc;
137}
138
139static int access_ereg(struct phy_device *phydev, u8 mode, u8 bank,
140		       u8 offset, u16 val)
141{
142	u16 ereg = 0;
143	int rc = 0;
144
145	if (mode > PHYACC_ATTR_MODE_WRITE || bank > PHYACC_ATTR_BANK_MAX)
146		return -EINVAL;
147
148	if (bank == PHYACC_ATTR_BANK_SMI) {
149		if (mode == PHYACC_ATTR_MODE_WRITE)
150			rc = phy_write(phydev, offset, val);
151		else
152			rc = phy_read(phydev, offset);
153		return rc;
154	}
155
156	if (mode == PHYACC_ATTR_MODE_WRITE) {
157		ereg = LAN87XX_EXT_REG_CTL_WR_CTL;
158		rc = phy_write(phydev, LAN87XX_EXT_REG_WR_DATA, val);
159		if (rc < 0)
160			return rc;
161	} else {
162		ereg = LAN87XX_EXT_REG_CTL_RD_CTL;
163	}
164
165	ereg |= (bank << 8) | offset;
166
167	/* DSP bank access workaround for lan937x */
168	if (phydev->phy_id == PHY_ID_LAN937X) {
169		rc = lan937x_dsp_workaround(phydev, ereg, bank);
170		if (rc < 0)
171			return rc;
172	}
173
174	rc = phy_write(phydev, LAN87XX_EXT_REG_CTL, ereg);
175	if (rc < 0)
176		return rc;
177
178	if (mode == PHYACC_ATTR_MODE_READ)
179		rc = phy_read(phydev, LAN87XX_EXT_REG_RD_DATA);
180
181	return rc;
182}
183
184static int access_ereg_modify_changed(struct phy_device *phydev,
185				      u8 bank, u8 offset, u16 val, u16 mask)
186{
187	int new = 0, rc = 0;
188
189	if (bank > PHYACC_ATTR_BANK_MAX)
190		return -EINVAL;
191
192	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, bank, offset, val);
193	if (rc < 0)
194		return rc;
195
196	new = val | (rc & (mask ^ 0xFFFF));
197	rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE, bank, offset, new);
198
199	return rc;
200}
201
202static int access_smi_poll_timeout(struct phy_device *phydev,
203				   u8 offset, u16 mask, u16 clr)
204{
205	int val;
206
207	return phy_read_poll_timeout(phydev, offset, val, (val & mask) == clr,
208				     150, 30000, true);
209}
210
211static int lan87xx_config_rgmii_delay(struct phy_device *phydev)
212{
213	int rc;
214
215	if (!phy_interface_is_rgmii(phydev))
216		return 0;
217
218	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
219			 PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, 0);
220	if (rc < 0)
221		return rc;
222
223	switch (phydev->interface) {
224	case PHY_INTERFACE_MODE_RGMII:
225		rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN;
226		rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN;
227		break;
228	case PHY_INTERFACE_MODE_RGMII_ID:
229		rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN;
230		rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN;
231		break;
232	case PHY_INTERFACE_MODE_RGMII_RXID:
233		rc &= ~LAN87XX_MASK_RGMII_TXC_DLY_EN;
234		rc |= LAN87XX_MASK_RGMII_RXC_DLY_EN;
235		break;
236	case PHY_INTERFACE_MODE_RGMII_TXID:
237		rc |= LAN87XX_MASK_RGMII_TXC_DLY_EN;
238		rc &= ~LAN87XX_MASK_RGMII_RXC_DLY_EN;
239		break;
240	default:
241		return 0;
242	}
243
244	return access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
245			   PHYACC_ATTR_BANK_MISC, LAN87XX_CTRL_1, rc);
246}
247
248static int lan87xx_phy_init(struct phy_device *phydev)
249{
250	static const struct access_ereg_val init[] = {
251		/* TXPD/TXAMP6 Configs */
252		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE,
253		  T1_AFE_PORT_CFG1_REG,       0x002D,  0 },
254		/* HW_Init Hi and Force_ED */
255		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI,
256		  T1_POWER_DOWN_CONTROL_REG,  0x0308,  0 },
257		/* Equalizer Full Duplex Freeze - T1 Slave */
258		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
259		  T1_EQ_FD_STG1_FRZ_CFG,     0x0002,  0 },
260		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
261		  T1_EQ_FD_STG2_FRZ_CFG,     0x0002,  0 },
262		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
263		  T1_EQ_FD_STG3_FRZ_CFG,     0x0002,  0 },
264		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
265		  T1_EQ_FD_STG4_FRZ_CFG,     0x0002,  0 },
266		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
267		  T1_EQ_WT_FD_LCK_FRZ_CFG,    0x0002,  0 },
268		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
269		  T1_PST_EQ_LCK_STG1_FRZ_CFG, 0x0002,  0 },
270		/* Slave Full Duplex Multi Configs */
271		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
272		  T1_SLV_FD_MULT_CFG_REG,     0x0D53,  0 },
273		/* CDR Pre and Post Lock Configs */
274		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
275		  T1_CDR_CFG_PRE_LOCK_REG,    0x0AB2,  0 },
276		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
277		  T1_CDR_CFG_POST_LOCK_REG,   0x0AB3,  0 },
278		/* Lock Stage 2-3 Multi Factor Config */
279		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
280		  T1_LCK_STG2_MUFACT_CFG_REG, 0x0AEA,  0 },
281		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
282		  T1_LCK_STG3_MUFACT_CFG_REG, 0x0AEB,  0 },
283		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
284		  T1_POST_LCK_MUFACT_CFG_REG, 0x0AEB,  0 },
285		/* Pointer delay */
286		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
287		  T1_TX_RX_FIFO_CFG_REG, 0x1C00, 0 },
288		/* Tx iir edits */
289		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
290		  T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 },
291		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
292		  T1_TX_LPF_FIR_CFG_REG, 0x1861, 0 },
293		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
294		  T1_TX_LPF_FIR_CFG_REG, 0x1061, 0 },
295		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
296		  T1_TX_LPF_FIR_CFG_REG, 0x1922, 0 },
297		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
298		  T1_TX_LPF_FIR_CFG_REG, 0x1122, 0 },
299		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
300		  T1_TX_LPF_FIR_CFG_REG, 0x1983, 0 },
301		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
302		  T1_TX_LPF_FIR_CFG_REG, 0x1183, 0 },
303		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
304		  T1_TX_LPF_FIR_CFG_REG, 0x1944, 0 },
305		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
306		  T1_TX_LPF_FIR_CFG_REG, 0x1144, 0 },
307		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
308		  T1_TX_LPF_FIR_CFG_REG, 0x18c5, 0 },
309		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
310		  T1_TX_LPF_FIR_CFG_REG, 0x10c5, 0 },
311		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
312		  T1_TX_LPF_FIR_CFG_REG, 0x1846, 0 },
313		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
314		  T1_TX_LPF_FIR_CFG_REG, 0x1046, 0 },
315		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
316		  T1_TX_LPF_FIR_CFG_REG, 0x1807, 0 },
317		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
318		  T1_TX_LPF_FIR_CFG_REG, 0x1007, 0 },
319		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
320		  T1_TX_LPF_FIR_CFG_REG, 0x1808, 0 },
321		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
322		  T1_TX_LPF_FIR_CFG_REG, 0x1008, 0 },
323		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
324		  T1_TX_LPF_FIR_CFG_REG, 0x1809, 0 },
325		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
326		  T1_TX_LPF_FIR_CFG_REG, 0x1009, 0 },
327		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
328		  T1_TX_LPF_FIR_CFG_REG, 0x180A, 0 },
329		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
330		  T1_TX_LPF_FIR_CFG_REG, 0x100A, 0 },
331		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
332		  T1_TX_LPF_FIR_CFG_REG, 0x180B, 0 },
333		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
334		  T1_TX_LPF_FIR_CFG_REG, 0x100B, 0 },
335		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
336		  T1_TX_LPF_FIR_CFG_REG, 0x180C, 0 },
337		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
338		  T1_TX_LPF_FIR_CFG_REG, 0x100C, 0 },
339		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
340		  T1_TX_LPF_FIR_CFG_REG, 0x180D, 0 },
341		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
342		  T1_TX_LPF_FIR_CFG_REG, 0x100D, 0 },
343		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
344		  T1_TX_LPF_FIR_CFG_REG, 0x180E, 0 },
345		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
346		  T1_TX_LPF_FIR_CFG_REG, 0x100E, 0 },
347		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
348		  T1_TX_LPF_FIR_CFG_REG, 0x180F, 0 },
349		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
350		  T1_TX_LPF_FIR_CFG_REG, 0x100F, 0 },
351		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
352		  T1_TX_LPF_FIR_CFG_REG, 0x1810, 0 },
353		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
354		  T1_TX_LPF_FIR_CFG_REG, 0x1010, 0 },
355		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
356		  T1_TX_LPF_FIR_CFG_REG, 0x1811, 0 },
357		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
358		  T1_TX_LPF_FIR_CFG_REG, 0x1011, 0 },
359		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
360		  T1_TX_LPF_FIR_CFG_REG, 0x1000, 0 },
361		/* Setup SQI measurement */
362		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
363		  T1_COEF_CLK_PWR_DN_CFG,	0x16d6, 0 },
364		/* SQI enable */
365		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
366		  T1_SQI_CONFIG_REG,		0x9572, 0 },
367		/* SQI select mode 5 */
368		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
369		  T1_SQI_CONFIG2_REG,		0x0001, 0 },
370		/* Throws the first SQI reading */
371		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP,
372		  T1_COEF_RW_CTL_CFG,		0x0301,	0 },
373		{ PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_DSP,
374		  T1_DCQ_SQI_REG,		0,	0 },
375		/* Flag LPS and WUR as idle errors */
376		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI,
377		  T1_MDIO_CONTROL2_REG,		0x0014, 0 },
378		/* HW_Init toggle, undo force ED, TXPD off */
379		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI,
380		  T1_POWER_DOWN_CONTROL_REG,	0x0200, 0 },
381		/* Reset PCS to trigger hardware initialization */
382		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI,
383		  T1_MDIO_CONTROL2_REG,		0x0094, 0 },
384		/* Poll till Hardware is initialized */
385		{ PHYACC_ATTR_MODE_POLL, PHYACC_ATTR_BANK_SMI,
386		  T1_MDIO_CONTROL2_REG,		0x0080, 0 },
387		/* Tx AMP - 0x06  */
388		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_AFE,
389		  T1_AFE_PORT_CFG1_REG,		0x000C, 0 },
390		/* Read INTERRUPT_SOURCE Register */
391		{ PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI,
392		  T1_INTERRUPT_SOURCE_REG,	0,	0 },
393		/* Read INTERRUPT_SOURCE Register */
394		{ PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_MISC,
395		  T1_INTERRUPT2_SOURCE_REG,	0,	0 },
396		/* HW_Init Hi */
397		{ PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_SMI,
398		  T1_POWER_DOWN_CONTROL_REG,	0x0300, 0 },
399	};
400	int rc, i;
401
402	/* phy Soft reset */
403	rc = genphy_soft_reset(phydev);
404	if (rc < 0)
405		return rc;
406
407	/* PHY Initialization */
408	for (i = 0; i < ARRAY_SIZE(init); i++) {
409		if (init[i].mode == PHYACC_ATTR_MODE_POLL &&
410		    init[i].bank == PHYACC_ATTR_BANK_SMI) {
411			rc = access_smi_poll_timeout(phydev,
412						     init[i].offset,
413						     init[i].val,
414						     init[i].mask);
415		} else {
416			rc = access_ereg(phydev, init[i].mode, init[i].bank,
417					 init[i].offset, init[i].val);
418		}
419		if (rc < 0)
420			return rc;
421	}
422
423	return lan87xx_config_rgmii_delay(phydev);
424}
425
426static int lan87xx_phy_config_intr(struct phy_device *phydev)
427{
428	int rc, val = 0;
429
430	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
431		/* clear all interrupt */
432		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
433		if (rc < 0)
434			return rc;
435
436		rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
437		if (rc < 0)
438			return rc;
439
440		rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
441				 PHYACC_ATTR_BANK_MISC,
442				 LAN87XX_INTERRUPT_MASK_2, val);
443		if (rc < 0)
444			return rc;
445
446		rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
447				 PHYACC_ATTR_BANK_MISC,
448				 LAN87XX_INTERRUPT_SOURCE_2, 0);
449		if (rc < 0)
450			return rc;
451
452		/* enable link down and comm ready interrupt */
453		val = LAN87XX_MASK_LINK_DOWN;
454		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
455		if (rc < 0)
456			return rc;
457
458		val = LAN87XX_MASK_COMM_RDY;
459		rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
460				 PHYACC_ATTR_BANK_MISC,
461				 LAN87XX_INTERRUPT_MASK_2, val);
462	} else {
463		rc = phy_write(phydev, LAN87XX_INTERRUPT_MASK, val);
464		if (rc < 0)
465			return rc;
466
467		rc = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
468		if (rc < 0)
469			return rc;
470
471		rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
472				 PHYACC_ATTR_BANK_MISC,
473				 LAN87XX_INTERRUPT_MASK_2, val);
474		if (rc < 0)
475			return rc;
476
477		rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
478				 PHYACC_ATTR_BANK_MISC,
479				 LAN87XX_INTERRUPT_SOURCE_2, 0);
480	}
481
482	return rc < 0 ? rc : 0;
483}
484
485static irqreturn_t lan87xx_handle_interrupt(struct phy_device *phydev)
486{
487	int irq_status;
488
489	irq_status  = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
490				  PHYACC_ATTR_BANK_MISC,
491				  LAN87XX_INTERRUPT_SOURCE_2, 0);
492	if (irq_status < 0) {
493		phy_error(phydev);
494		return IRQ_NONE;
495	}
496
497	irq_status = phy_read(phydev, LAN87XX_INTERRUPT_SOURCE);
498	if (irq_status < 0) {
499		phy_error(phydev);
500		return IRQ_NONE;
501	}
502
503	if (irq_status == 0)
504		return IRQ_NONE;
505
506	phy_trigger_machine(phydev);
507
508	return IRQ_HANDLED;
509}
510
511static int lan87xx_config_init(struct phy_device *phydev)
512{
513	int rc = lan87xx_phy_init(phydev);
514
515	return rc < 0 ? rc : 0;
516}
517
518static int microchip_cable_test_start_common(struct phy_device *phydev)
519{
520	int bmcr, bmsr, ret;
521
522	/* If auto-negotiation is enabled, but not complete, the cable
523	 * test never completes. So disable auto-neg.
524	 */
525	bmcr = phy_read(phydev, MII_BMCR);
526	if (bmcr < 0)
527		return bmcr;
528
529	bmsr = phy_read(phydev, MII_BMSR);
530
531	if (bmsr < 0)
532		return bmsr;
533
534	if (bmcr & BMCR_ANENABLE) {
535		ret =  phy_modify(phydev, MII_BMCR, BMCR_ANENABLE, 0);
536		if (ret < 0)
537			return ret;
538		ret = genphy_soft_reset(phydev);
539		if (ret < 0)
540			return ret;
541	}
542
543	/* If the link is up, allow it some time to go down */
544	if (bmsr & BMSR_LSTATUS)
545		msleep(1500);
546
547	return 0;
548}
549
550static int lan87xx_cable_test_start(struct phy_device *phydev)
551{
552	static const struct access_ereg_val cable_test[] = {
553		/* min wait */
554		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 93,
555		 0, 0},
556		/* max wait */
557		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94,
558		 10, 0},
559		/* pulse cycle */
560		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 95,
561		 90, 0},
562		/* cable diag thresh */
563		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 92,
564		 60, 0},
565		/* max gain */
566		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 79,
567		 31, 0},
568		/* clock align for each iteration */
569		{PHYACC_ATTR_MODE_MODIFY, PHYACC_ATTR_BANK_DSP, 55,
570		 0, 0x0038},
571		/* max cycle wait config */
572		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 94,
573		 70, 0},
574		/* start cable diag*/
575		{PHYACC_ATTR_MODE_WRITE, PHYACC_ATTR_BANK_DSP, 90,
576		 1, 0},
577	};
578	int rc, i;
579
580	rc = microchip_cable_test_start_common(phydev);
581	if (rc < 0)
582		return rc;
583
584	/* start cable diag */
585	/* check if part is alive - if not, return diagnostic error */
586	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI,
587			 0x00, 0);
588	if (rc < 0)
589		return rc;
590
591	/* master/slave specific configs */
592	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_SMI,
593			 0x0A, 0);
594	if (rc < 0)
595		return rc;
596
597	if ((rc & 0x4000) != 0x4000) {
598		/* DUT is Slave */
599		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_AFE,
600						0x0E, 0x5, 0x7);
601		if (rc < 0)
602			return rc;
603		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
604						0x1A, 0x8, 0x8);
605		if (rc < 0)
606			return rc;
607	} else {
608		/* DUT is Master */
609		rc = access_ereg_modify_changed(phydev, PHYACC_ATTR_BANK_SMI,
610						0x10, 0x8, 0x40);
611		if (rc < 0)
612			return rc;
613	}
614
615	for (i = 0; i < ARRAY_SIZE(cable_test); i++) {
616		if (cable_test[i].mode == PHYACC_ATTR_MODE_MODIFY) {
617			rc = access_ereg_modify_changed(phydev,
618							cable_test[i].bank,
619							cable_test[i].offset,
620							cable_test[i].val,
621							cable_test[i].mask);
622			/* wait 50ms */
623			msleep(50);
624		} else {
625			rc = access_ereg(phydev, cable_test[i].mode,
626					 cable_test[i].bank,
627					 cable_test[i].offset,
628					 cable_test[i].val);
629		}
630		if (rc < 0)
631			return rc;
632	}
633	/* cable diag started */
634
635	return 0;
636}
637
638static int lan87xx_cable_test_report_trans(u32 result)
639{
640	switch (result) {
641	case LAN87XX_CABLE_TEST_OK:
642		return ETHTOOL_A_CABLE_RESULT_CODE_OK;
643	case LAN87XX_CABLE_TEST_OPEN:
644		return ETHTOOL_A_CABLE_RESULT_CODE_OPEN;
645	case LAN87XX_CABLE_TEST_SAME_SHORT:
646		return ETHTOOL_A_CABLE_RESULT_CODE_SAME_SHORT;
647	default:
648		/* DIAGNOSTIC_ERROR */
649		return ETHTOOL_A_CABLE_RESULT_CODE_UNSPEC;
650	}
651}
652
653static int lan87xx_cable_test_report(struct phy_device *phydev)
654{
655	int pos_peak_cycle = 0, pos_peak_in_phases = 0, pos_peak_phase = 0;
656	int neg_peak_cycle = 0, neg_peak_in_phases = 0, neg_peak_phase = 0;
657	int noise_margin = 20, time_margin = 89, jitter_var = 30;
658	int min_time_diff = 96, max_time_diff = 96 + time_margin;
659	bool fault = false, check_a = false, check_b = false;
660	int gain_idx = 0, pos_peak = 0, neg_peak = 0;
661	int pos_peak_time = 0, neg_peak_time = 0;
662	int pos_peak_in_phases_hybrid = 0;
663	int detect = -1;
664
665	gain_idx = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
666			       PHYACC_ATTR_BANK_DSP, 151, 0);
667	/* read non-hybrid results */
668	pos_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
669			       PHYACC_ATTR_BANK_DSP, 153, 0);
670	neg_peak = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
671			       PHYACC_ATTR_BANK_DSP, 154, 0);
672	pos_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
673				    PHYACC_ATTR_BANK_DSP, 156, 0);
674	neg_peak_time = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
675				    PHYACC_ATTR_BANK_DSP, 157, 0);
676
677	pos_peak_cycle = (pos_peak_time >> 7) & 0x7F;
678	/* calculate non-hybrid values */
679	pos_peak_phase = pos_peak_time & 0x7F;
680	pos_peak_in_phases = (pos_peak_cycle * 96) + pos_peak_phase;
681	neg_peak_cycle = (neg_peak_time >> 7) & 0x7F;
682	neg_peak_phase = neg_peak_time & 0x7F;
683	neg_peak_in_phases = (neg_peak_cycle * 96) + neg_peak_phase;
684
685	/* process values */
686	check_a =
687		((pos_peak_in_phases - neg_peak_in_phases) >= min_time_diff) &&
688		((pos_peak_in_phases - neg_peak_in_phases) < max_time_diff) &&
689		pos_peak_in_phases_hybrid < pos_peak_in_phases &&
690		(pos_peak_in_phases_hybrid < (neg_peak_in_phases + jitter_var));
691	check_b =
692		((neg_peak_in_phases - pos_peak_in_phases) >= min_time_diff) &&
693		((neg_peak_in_phases - pos_peak_in_phases) < max_time_diff) &&
694		pos_peak_in_phases_hybrid < neg_peak_in_phases &&
695		(pos_peak_in_phases_hybrid < (pos_peak_in_phases + jitter_var));
696
697	if (pos_peak_in_phases > neg_peak_in_phases && check_a)
698		detect = 2;
699	else if ((neg_peak_in_phases > pos_peak_in_phases) && check_b)
700		detect = 1;
701
702	if (pos_peak > noise_margin && neg_peak > noise_margin &&
703	    gain_idx >= 0) {
704		if (detect == 1 || detect == 2)
705			fault = true;
706	}
707
708	if (!fault)
709		detect = 0;
710
711	ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_A,
712				lan87xx_cable_test_report_trans(detect));
713
714	return 0;
715}
716
717static int lan87xx_cable_test_get_status(struct phy_device *phydev,
718					 bool *finished)
719{
720	int rc = 0;
721
722	*finished = false;
723
724	/* check if cable diag was finished */
725	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ, PHYACC_ATTR_BANK_DSP,
726			 90, 0);
727	if (rc < 0)
728		return rc;
729
730	if ((rc & 2) == 2) {
731		/* stop cable diag*/
732		rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
733				 PHYACC_ATTR_BANK_DSP,
734				 90, 0);
735		if (rc < 0)
736			return rc;
737
738		*finished = true;
739
740		return lan87xx_cable_test_report(phydev);
741	}
742
743	return 0;
744}
745
746static int lan87xx_read_status(struct phy_device *phydev)
747{
748	int rc = 0;
749
750	rc = phy_read(phydev, T1_MODE_STAT_REG);
751	if (rc < 0)
752		return rc;
753
754	if (rc & T1_LINK_UP_MSK)
755		phydev->link = 1;
756	else
757		phydev->link = 0;
758
759	phydev->speed = SPEED_UNKNOWN;
760	phydev->duplex = DUPLEX_UNKNOWN;
761	phydev->pause = 0;
762	phydev->asym_pause = 0;
763
764	rc = genphy_read_master_slave(phydev);
765	if (rc < 0)
766		return rc;
767
768	rc = genphy_read_status_fixed(phydev);
769	if (rc < 0)
770		return rc;
771
772	return rc;
773}
774
775static int lan87xx_config_aneg(struct phy_device *phydev)
776{
777	u16 ctl = 0;
778
779	switch (phydev->master_slave_set) {
780	case MASTER_SLAVE_CFG_MASTER_FORCE:
781		ctl |= CTL1000_AS_MASTER;
782		break;
783	case MASTER_SLAVE_CFG_SLAVE_FORCE:
784		break;
785	case MASTER_SLAVE_CFG_UNKNOWN:
786	case MASTER_SLAVE_CFG_UNSUPPORTED:
787		return 0;
788	default:
789		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
790		return -EOPNOTSUPP;
791	}
792
793	return phy_modify_changed(phydev, MII_CTRL1000, CTL1000_AS_MASTER, ctl);
794}
795
796static int lan87xx_get_sqi(struct phy_device *phydev)
797{
798	u8 sqi_value = 0;
799	int rc;
800
801	rc = access_ereg(phydev, PHYACC_ATTR_MODE_WRITE,
802			 PHYACC_ATTR_BANK_DSP, T1_COEF_RW_CTL_CFG, 0x0301);
803	if (rc < 0)
804		return rc;
805
806	rc = access_ereg(phydev, PHYACC_ATTR_MODE_READ,
807			 PHYACC_ATTR_BANK_DSP, T1_DCQ_SQI_REG, 0x0);
808	if (rc < 0)
809		return rc;
810
811	sqi_value = FIELD_GET(T1_DCQ_SQI_MSK, rc);
812
813	return sqi_value;
814}
815
816static int lan87xx_get_sqi_max(struct phy_device *phydev)
817{
818	return LAN87XX_MAX_SQI;
819}
820
821static struct phy_driver microchip_t1_phy_driver[] = {
822	{
823		PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX),
824		.name           = "Microchip LAN87xx T1",
825		.flags          = PHY_POLL_CABLE_TEST,
826		.features       = PHY_BASIC_T1_FEATURES,
827		.config_init	= lan87xx_config_init,
828		.config_intr    = lan87xx_phy_config_intr,
829		.handle_interrupt = lan87xx_handle_interrupt,
830		.suspend        = genphy_suspend,
831		.resume         = genphy_resume,
832		.config_aneg    = lan87xx_config_aneg,
833		.read_status	= lan87xx_read_status,
834		.get_sqi	= lan87xx_get_sqi,
835		.get_sqi_max	= lan87xx_get_sqi_max,
836		.cable_test_start = lan87xx_cable_test_start,
837		.cable_test_get_status = lan87xx_cable_test_get_status,
838	},
839	{
840		PHY_ID_MATCH_MODEL(PHY_ID_LAN937X),
841		.name		= "Microchip LAN937x T1",
842		.flags          = PHY_POLL_CABLE_TEST,
843		.features	= PHY_BASIC_T1_FEATURES,
844		.config_init	= lan87xx_config_init,
845		.config_intr    = lan87xx_phy_config_intr,
846		.handle_interrupt = lan87xx_handle_interrupt,
847		.suspend	= genphy_suspend,
848		.resume		= genphy_resume,
849		.config_aneg    = lan87xx_config_aneg,
850		.read_status	= lan87xx_read_status,
851		.get_sqi	= lan87xx_get_sqi,
852		.get_sqi_max	= lan87xx_get_sqi_max,
853		.cable_test_start = lan87xx_cable_test_start,
854		.cable_test_get_status = lan87xx_cable_test_get_status,
855	}
856};
857
858module_phy_driver(microchip_t1_phy_driver);
859
860static struct mdio_device_id __maybe_unused microchip_t1_tbl[] = {
861	{ PHY_ID_MATCH_MODEL(PHY_ID_LAN87XX) },
862	{ PHY_ID_MATCH_MODEL(PHY_ID_LAN937X) },
863	{ }
864};
865
866MODULE_DEVICE_TABLE(mdio, microchip_t1_tbl);
867
868MODULE_AUTHOR(DRIVER_AUTHOR);
869MODULE_DESCRIPTION(DRIVER_DESC);
870MODULE_LICENSE("GPL");