xref: /openbmc/linux/drivers/net/phy/intel-xway.c (revision be393dd6)
1a2443fd1SAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2112b558dSHauke Mehrtens /*
3112b558dSHauke Mehrtens  * Copyright (C) 2012 Daniel Schwierzeck <daniel.schwierzeck@googlemail.com>
4112b558dSHauke Mehrtens  * Copyright (C) 2016 Hauke Mehrtens <hauke@hauke-m.de>
5112b558dSHauke Mehrtens  */
6112b558dSHauke Mehrtens 
7112b558dSHauke Mehrtens #include <linux/mdio.h>
8112b558dSHauke Mehrtens #include <linux/module.h>
9112b558dSHauke Mehrtens #include <linux/phy.h>
10112b558dSHauke Mehrtens #include <linux/of.h>
11*be393dd6SMartin Schiller #include <linux/bitfield.h>
12112b558dSHauke Mehrtens 
13*be393dd6SMartin Schiller #define XWAY_MDIO_MIICTRL		0x17	/* mii control */
14112b558dSHauke Mehrtens #define XWAY_MDIO_IMASK			0x19	/* interrupt mask */
15112b558dSHauke Mehrtens #define XWAY_MDIO_ISTAT			0x1A	/* interrupt status */
16357a07c2SMartin Schiller #define XWAY_MDIO_LED			0x1B	/* led control */
17357a07c2SMartin Schiller 
18*be393dd6SMartin Schiller #define XWAY_MDIO_MIICTRL_RXSKEW_MASK	GENMASK(14, 12)
19*be393dd6SMartin Schiller #define XWAY_MDIO_MIICTRL_TXSKEW_MASK	GENMASK(10, 8)
20*be393dd6SMartin Schiller 
21357a07c2SMartin Schiller /* bit 15:12 are reserved */
22357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED3_EN		BIT(11)	/* Enable the integrated function of LED3 */
23357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED2_EN		BIT(10)	/* Enable the integrated function of LED2 */
24357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED1_EN		BIT(9)	/* Enable the integrated function of LED1 */
25357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED0_EN		BIT(8)	/* Enable the integrated function of LED0 */
26357a07c2SMartin Schiller /* bit 7:4 are reserved */
27357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED3_DA		BIT(3)	/* Direct Access to LED3 */
28357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED2_DA		BIT(2)	/* Direct Access to LED2 */
29357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED1_DA		BIT(1)	/* Direct Access to LED1 */
30357a07c2SMartin Schiller #define XWAY_MDIO_LED_LED0_DA		BIT(0)	/* Direct Access to LED0 */
31112b558dSHauke Mehrtens 
32112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_WOL		BIT(15)	/* Wake-On-LAN */
33112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_MSRE		BIT(14)
34112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_NPRX		BIT(13)
35112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_NPTX		BIT(12)
36112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_ANE		BIT(11)	/* Auto-Neg error */
37112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_ANC		BIT(10)	/* Auto-Neg complete */
38112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_ADSC		BIT(5)	/* Link auto-downspeed detect */
39112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_MPIPC		BIT(4)
40112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_MDIXC		BIT(3)
41112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_DXMC		BIT(2)	/* Duplex mode change */
42112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_LSPC		BIT(1)	/* Link speed change */
43112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_LSTC		BIT(0)	/* Link state change */
44112b558dSHauke Mehrtens #define XWAY_MDIO_INIT_MASK		(XWAY_MDIO_INIT_LSTC | \
45112b558dSHauke Mehrtens 					 XWAY_MDIO_INIT_ADSC)
46112b558dSHauke Mehrtens 
47112b558dSHauke Mehrtens #define ADVERTISED_MPD			BIT(10)	/* Multi-port device */
48112b558dSHauke Mehrtens 
49112b558dSHauke Mehrtens /* LED Configuration */
50112b558dSHauke Mehrtens #define XWAY_MMD_LEDCH			0x01E0
51112b558dSHauke Mehrtens /* Inverse of SCAN Function */
52112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_NONE	0x0000
53112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_LINK	0x0001
54112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_PDOWN	0x0002
55112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_EEE	0x0003
56112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_ANEG	0x0004
57112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_ABIST	0x0005
58112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_CDIAG	0x0006
59112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_NACS_TEST	0x0007
60112b558dSHauke Mehrtens /* Slow Blink Frequency */
61112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SBF_F02HZ	0x0000
62112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SBF_F04HZ	0x0010
63112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SBF_F08HZ	0x0020
64112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SBF_F16HZ	0x0030
65112b558dSHauke Mehrtens /* Fast Blink Frequency */
66112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_FBF_F02HZ	0x0000
67112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_FBF_F04HZ	0x0040
68112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_FBF_F08HZ	0x0080
69112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_FBF_F16HZ	0x00C0
70112b558dSHauke Mehrtens /* LED Configuration */
71112b558dSHauke Mehrtens #define XWAY_MMD_LEDCL			0x01E1
72112b558dSHauke Mehrtens /* Complex Blinking Configuration */
73112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_NONE	0x0000
74112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_LINK	0x0001
75112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_PDOWN	0x0002
76112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_EEE	0x0003
77112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_ANEG	0x0004
78112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_ABIST	0x0005
79112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_CDIAG	0x0006
80112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_CBLINK_TEST	0x0007
81112b558dSHauke Mehrtens /* Complex SCAN Configuration */
82112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_NONE	0x0000
83112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_LINK	0x0010
84112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_PDOWN	0x0020
85112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_EEE	0x0030
86112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_ANEG	0x0040
87112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_ABIST	0x0050
88112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_CDIAG	0x0060
89112b558dSHauke Mehrtens #define  XWAY_MMD_LEDCH_SCAN_TEST	0x0070
90112b558dSHauke Mehrtens /* Configuration for LED Pin x */
91112b558dSHauke Mehrtens #define XWAY_MMD_LED0H			0x01E2
92112b558dSHauke Mehrtens /* Fast Blinking Configuration */
93112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_MASK	0x000F
94112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_NONE	0x0000
95112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK10	0x0001
96112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK100	0x0002
97112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK10X	0x0003
98112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK1000	0x0004
99112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK10_0	0x0005
100112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK100X	0x0006
101112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_LINK10XX	0x0007
102112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_PDOWN	0x0008
103112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_EEE	0x0009
104112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_ANEG	0x000A
105112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_ABIST	0x000B
106112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_BLINKF_CDIAG	0x000C
107112b558dSHauke Mehrtens /* Constant On Configuration */
108112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_MASK	0x00F0
109112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_NONE	0x0000
110112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK10	0x0010
111112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK100	0x0020
112112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK10X	0x0030
113112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK1000	0x0040
114112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK10_0	0x0050
115112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK100X	0x0060
116112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_LINK10XX	0x0070
117112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_PDOWN	0x0080
118112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_EEE		0x0090
119112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_ANEG	0x00A0
120112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_ABIST	0x00B0
121112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_CDIAG	0x00C0
122112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_COPPER	0x00D0
123112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxH_CON_FIBER	0x00E0
124112b558dSHauke Mehrtens /* Configuration for LED Pin x */
125112b558dSHauke Mehrtens #define XWAY_MMD_LED0L			0x01E3
126112b558dSHauke Mehrtens /* Pulsing Configuration */
127112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_PULSE_MASK	0x000F
128112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_PULSE_NONE	0x0000
129112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_PULSE_TXACT	0x0001
130112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_PULSE_RXACT	0x0002
131112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_PULSE_COL	0x0004
132112b558dSHauke Mehrtens /* Slow Blinking Configuration */
133112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_MASK	0x00F0
134112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_NONE	0x0000
135112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK10	0x0010
136112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK100	0x0020
137112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK10X	0x0030
138112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK1000	0x0040
139112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK10_0	0x0050
140112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK100X	0x0060
141112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_LINK10XX	0x0070
142112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_PDOWN	0x0080
143112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_EEE	0x0090
144112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_ANEG	0x00A0
145112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_ABIST	0x00B0
146112b558dSHauke Mehrtens #define  XWAY_MMD_LEDxL_BLINKS_CDIAG	0x00C0
147112b558dSHauke Mehrtens #define XWAY_MMD_LED1H			0x01E4
148112b558dSHauke Mehrtens #define XWAY_MMD_LED1L			0x01E5
149112b558dSHauke Mehrtens #define XWAY_MMD_LED2H			0x01E6
150112b558dSHauke Mehrtens #define XWAY_MMD_LED2L			0x01E7
151112b558dSHauke Mehrtens #define XWAY_MMD_LED3H			0x01E8
152112b558dSHauke Mehrtens #define XWAY_MMD_LED3L			0x01E9
153112b558dSHauke Mehrtens 
154112b558dSHauke Mehrtens #define PHY_ID_PHY11G_1_3		0x030260D1
155112b558dSHauke Mehrtens #define PHY_ID_PHY22F_1_3		0x030260E1
156112b558dSHauke Mehrtens #define PHY_ID_PHY11G_1_4		0xD565A400
157112b558dSHauke Mehrtens #define PHY_ID_PHY22F_1_4		0xD565A410
158112b558dSHauke Mehrtens #define PHY_ID_PHY11G_1_5		0xD565A401
159112b558dSHauke Mehrtens #define PHY_ID_PHY22F_1_5		0xD565A411
160f452518cSMathias Kresin #define PHY_ID_PHY11G_VR9_1_1		0xD565A408
161f452518cSMathias Kresin #define PHY_ID_PHY22F_VR9_1_1		0xD565A418
1625b73d995SMathias Kresin #define PHY_ID_PHY11G_VR9_1_2		0xD565A409
1635b73d995SMathias Kresin #define PHY_ID_PHY22F_VR9_1_2		0xD565A419
164112b558dSHauke Mehrtens 
165*be393dd6SMartin Schiller static const int xway_internal_delay[] = {0, 500, 1000, 1500, 2000, 2500,
166*be393dd6SMartin Schiller 					 3000, 3500};
167*be393dd6SMartin Schiller 
xway_gphy_rgmii_init(struct phy_device * phydev)168*be393dd6SMartin Schiller static int xway_gphy_rgmii_init(struct phy_device *phydev)
169*be393dd6SMartin Schiller {
170*be393dd6SMartin Schiller 	struct device *dev = &phydev->mdio.dev;
171*be393dd6SMartin Schiller 	unsigned int delay_size = ARRAY_SIZE(xway_internal_delay);
172*be393dd6SMartin Schiller 	s32 int_delay;
173*be393dd6SMartin Schiller 	int val = 0;
174*be393dd6SMartin Schiller 
175*be393dd6SMartin Schiller 	if (!phy_interface_is_rgmii(phydev))
176*be393dd6SMartin Schiller 		return 0;
177*be393dd6SMartin Schiller 
178*be393dd6SMartin Schiller 	/* Existing behavior was to use default pin strapping delay in rgmii
179*be393dd6SMartin Schiller 	 * mode, but rgmii should have meant no delay.  Warn existing users,
180*be393dd6SMartin Schiller 	 * but do not change anything at the moment.
181*be393dd6SMartin Schiller 	 */
182*be393dd6SMartin Schiller 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII) {
183*be393dd6SMartin Schiller 		u16 txskew, rxskew;
184*be393dd6SMartin Schiller 
185*be393dd6SMartin Schiller 		val = phy_read(phydev, XWAY_MDIO_MIICTRL);
186*be393dd6SMartin Schiller 		if (val < 0)
187*be393dd6SMartin Schiller 			return val;
188*be393dd6SMartin Schiller 
189*be393dd6SMartin Schiller 		txskew = FIELD_GET(XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
190*be393dd6SMartin Schiller 		rxskew = FIELD_GET(XWAY_MDIO_MIICTRL_RXSKEW_MASK, val);
191*be393dd6SMartin Schiller 
192*be393dd6SMartin Schiller 		if (txskew > 0 || rxskew > 0)
193*be393dd6SMartin Schiller 			phydev_warn(phydev,
194*be393dd6SMartin Schiller 				    "PHY has delays (e.g. via pin strapping), but phy-mode = 'rgmii'\n"
195*be393dd6SMartin Schiller 				    "Should be 'rgmii-id' to use internal delays txskew:%d ps rxskew:%d ps\n",
196*be393dd6SMartin Schiller 				    xway_internal_delay[txskew],
197*be393dd6SMartin Schiller 				    xway_internal_delay[rxskew]);
198*be393dd6SMartin Schiller 		return 0;
199*be393dd6SMartin Schiller 	}
200*be393dd6SMartin Schiller 
201*be393dd6SMartin Schiller 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
202*be393dd6SMartin Schiller 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) {
203*be393dd6SMartin Schiller 		int_delay = phy_get_internal_delay(phydev, dev,
204*be393dd6SMartin Schiller 						   xway_internal_delay,
205*be393dd6SMartin Schiller 						   delay_size, true);
206*be393dd6SMartin Schiller 
207*be393dd6SMartin Schiller 		/* if rx-internal-delay-ps is missing, use default of 2.0 ns */
208*be393dd6SMartin Schiller 		if (int_delay < 0)
209*be393dd6SMartin Schiller 			int_delay = 4; /* 2000 ps */
210*be393dd6SMartin Schiller 
211*be393dd6SMartin Schiller 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_RXSKEW_MASK, int_delay);
212*be393dd6SMartin Schiller 	}
213*be393dd6SMartin Schiller 
214*be393dd6SMartin Schiller 	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
215*be393dd6SMartin Schiller 	    phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) {
216*be393dd6SMartin Schiller 		int_delay = phy_get_internal_delay(phydev, dev,
217*be393dd6SMartin Schiller 						   xway_internal_delay,
218*be393dd6SMartin Schiller 						   delay_size, false);
219*be393dd6SMartin Schiller 
220*be393dd6SMartin Schiller 		/* if tx-internal-delay-ps is missing, use default of 2.0 ns */
221*be393dd6SMartin Schiller 		if (int_delay < 0)
222*be393dd6SMartin Schiller 			int_delay = 4; /* 2000 ps */
223*be393dd6SMartin Schiller 
224*be393dd6SMartin Schiller 		val |= FIELD_PREP(XWAY_MDIO_MIICTRL_TXSKEW_MASK, int_delay);
225*be393dd6SMartin Schiller 	}
226*be393dd6SMartin Schiller 
227*be393dd6SMartin Schiller 	return phy_modify(phydev, XWAY_MDIO_MIICTRL,
228*be393dd6SMartin Schiller 			  XWAY_MDIO_MIICTRL_RXSKEW_MASK |
229*be393dd6SMartin Schiller 			  XWAY_MDIO_MIICTRL_TXSKEW_MASK, val);
230*be393dd6SMartin Schiller }
231*be393dd6SMartin Schiller 
xway_gphy_config_init(struct phy_device * phydev)232112b558dSHauke Mehrtens static int xway_gphy_config_init(struct phy_device *phydev)
233112b558dSHauke Mehrtens {
234112b558dSHauke Mehrtens 	int err;
235112b558dSHauke Mehrtens 	u32 ledxh;
236112b558dSHauke Mehrtens 	u32 ledxl;
237112b558dSHauke Mehrtens 
238112b558dSHauke Mehrtens 	/* Mask all interrupts */
239112b558dSHauke Mehrtens 	err = phy_write(phydev, XWAY_MDIO_IMASK, 0);
240112b558dSHauke Mehrtens 	if (err)
241112b558dSHauke Mehrtens 		return err;
242112b558dSHauke Mehrtens 
243112b558dSHauke Mehrtens 	/* Clear all pending interrupts */
244112b558dSHauke Mehrtens 	phy_read(phydev, XWAY_MDIO_ISTAT);
245112b558dSHauke Mehrtens 
246357a07c2SMartin Schiller 	/* Ensure that integrated led function is enabled for all leds */
247357a07c2SMartin Schiller 	err = phy_write(phydev, XWAY_MDIO_LED,
248357a07c2SMartin Schiller 			XWAY_MDIO_LED_LED0_EN |
249357a07c2SMartin Schiller 			XWAY_MDIO_LED_LED1_EN |
250357a07c2SMartin Schiller 			XWAY_MDIO_LED_LED2_EN |
251357a07c2SMartin Schiller 			XWAY_MDIO_LED_LED3_EN);
252357a07c2SMartin Schiller 	if (err)
253357a07c2SMartin Schiller 		return err;
254357a07c2SMartin Schiller 
255a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCH,
256112b558dSHauke Mehrtens 		      XWAY_MMD_LEDCH_NACS_NONE |
257112b558dSHauke Mehrtens 		      XWAY_MMD_LEDCH_SBF_F02HZ |
258112b558dSHauke Mehrtens 		      XWAY_MMD_LEDCH_FBF_F16HZ);
259a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LEDCL,
260112b558dSHauke Mehrtens 		      XWAY_MMD_LEDCH_CBLINK_NONE |
261112b558dSHauke Mehrtens 		      XWAY_MMD_LEDCH_SCAN_NONE);
262112b558dSHauke Mehrtens 
263112b558dSHauke Mehrtens 	/**
264112b558dSHauke Mehrtens 	 * In most cases only one LED is connected to this phy, so
265112b558dSHauke Mehrtens 	 * configure them all to constant on and pulse mode. LED3 is
266112b558dSHauke Mehrtens 	 * only available in some packages, leave it in its reset
267112b558dSHauke Mehrtens 	 * configuration.
268112b558dSHauke Mehrtens 	 */
269112b558dSHauke Mehrtens 	ledxh = XWAY_MMD_LEDxH_BLINKF_NONE | XWAY_MMD_LEDxH_CON_LINK10XX;
270112b558dSHauke Mehrtens 	ledxl = XWAY_MMD_LEDxL_PULSE_TXACT | XWAY_MMD_LEDxL_PULSE_RXACT |
271112b558dSHauke Mehrtens 		XWAY_MMD_LEDxL_BLINKS_NONE;
272a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0H, ledxh);
273a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED0L, ledxl);
274a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1H, ledxh);
275a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED1L, ledxl);
276a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2H, ledxh);
277a6d99fcdSRussell King 	phy_write_mmd(phydev, MDIO_MMD_VEND2, XWAY_MMD_LED2L, ledxl);
278112b558dSHauke Mehrtens 
279*be393dd6SMartin Schiller 	err = xway_gphy_rgmii_init(phydev);
280*be393dd6SMartin Schiller 	if (err)
281*be393dd6SMartin Schiller 		return err;
282*be393dd6SMartin Schiller 
283112b558dSHauke Mehrtens 	return 0;
284112b558dSHauke Mehrtens }
285112b558dSHauke Mehrtens 
xway_gphy14_config_aneg(struct phy_device * phydev)286112b558dSHauke Mehrtens static int xway_gphy14_config_aneg(struct phy_device *phydev)
287112b558dSHauke Mehrtens {
288112b558dSHauke Mehrtens 	int reg, err;
289112b558dSHauke Mehrtens 
290112b558dSHauke Mehrtens 	/* Advertise as multi-port device, see IEEE802.3-2002 40.5.1.1 */
291112b558dSHauke Mehrtens 	/* This is a workaround for an errata in rev < 1.5 devices */
292112b558dSHauke Mehrtens 	reg = phy_read(phydev, MII_CTRL1000);
293112b558dSHauke Mehrtens 	reg |= ADVERTISED_MPD;
294112b558dSHauke Mehrtens 	err = phy_write(phydev, MII_CTRL1000, reg);
295112b558dSHauke Mehrtens 	if (err)
296112b558dSHauke Mehrtens 		return err;
297112b558dSHauke Mehrtens 
298112b558dSHauke Mehrtens 	return genphy_config_aneg(phydev);
299112b558dSHauke Mehrtens }
300112b558dSHauke Mehrtens 
xway_gphy_ack_interrupt(struct phy_device * phydev)301112b558dSHauke Mehrtens static int xway_gphy_ack_interrupt(struct phy_device *phydev)
302112b558dSHauke Mehrtens {
303112b558dSHauke Mehrtens 	int reg;
304112b558dSHauke Mehrtens 
305112b558dSHauke Mehrtens 	reg = phy_read(phydev, XWAY_MDIO_ISTAT);
306112b558dSHauke Mehrtens 	return (reg < 0) ? reg : 0;
307112b558dSHauke Mehrtens }
308112b558dSHauke Mehrtens 
xway_gphy_config_intr(struct phy_device * phydev)309112b558dSHauke Mehrtens static int xway_gphy_config_intr(struct phy_device *phydev)
310112b558dSHauke Mehrtens {
311112b558dSHauke Mehrtens 	u16 mask = 0;
31216c9709aSIoana Ciornei 	int err;
313112b558dSHauke Mehrtens 
31416c9709aSIoana Ciornei 	if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
31516c9709aSIoana Ciornei 		err = xway_gphy_ack_interrupt(phydev);
31616c9709aSIoana Ciornei 		if (err)
31716c9709aSIoana Ciornei 			return err;
31816c9709aSIoana Ciornei 
319112b558dSHauke Mehrtens 		mask = XWAY_MDIO_INIT_MASK;
32016c9709aSIoana Ciornei 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
32116c9709aSIoana Ciornei 	} else {
32216c9709aSIoana Ciornei 		err = phy_write(phydev, XWAY_MDIO_IMASK, mask);
32316c9709aSIoana Ciornei 		if (err)
32416c9709aSIoana Ciornei 			return err;
325112b558dSHauke Mehrtens 
32616c9709aSIoana Ciornei 		err = xway_gphy_ack_interrupt(phydev);
32716c9709aSIoana Ciornei 	}
32816c9709aSIoana Ciornei 
32916c9709aSIoana Ciornei 	return err;
330112b558dSHauke Mehrtens }
331112b558dSHauke Mehrtens 
xway_gphy_handle_interrupt(struct phy_device * phydev)3321566db04SIoana Ciornei static irqreturn_t xway_gphy_handle_interrupt(struct phy_device *phydev)
3331566db04SIoana Ciornei {
3341566db04SIoana Ciornei 	int irq_status;
3351566db04SIoana Ciornei 
3361566db04SIoana Ciornei 	irq_status = phy_read(phydev, XWAY_MDIO_ISTAT);
3371566db04SIoana Ciornei 	if (irq_status < 0) {
3381566db04SIoana Ciornei 		phy_error(phydev);
3391566db04SIoana Ciornei 		return IRQ_NONE;
3401566db04SIoana Ciornei 	}
3411566db04SIoana Ciornei 
3421566db04SIoana Ciornei 	if (!(irq_status & XWAY_MDIO_INIT_MASK))
3431566db04SIoana Ciornei 		return IRQ_NONE;
3441566db04SIoana Ciornei 
3451566db04SIoana Ciornei 	phy_trigger_machine(phydev);
3461566db04SIoana Ciornei 
3471566db04SIoana Ciornei 	return IRQ_HANDLED;
3481566db04SIoana Ciornei }
3491566db04SIoana Ciornei 
350112b558dSHauke Mehrtens static struct phy_driver xway_gphy[] = {
351112b558dSHauke Mehrtens 	{
352112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY11G_1_3,
353112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
354112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.3",
355dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
356112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
357112b558dSHauke Mehrtens 		.config_aneg	= xway_gphy14_config_aneg,
3581566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
359112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
360112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
361112b558dSHauke Mehrtens 		.resume		= genphy_resume,
362112b558dSHauke Mehrtens 	}, {
363112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY22F_1_3,
364112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
365112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.3",
366dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
367112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
368112b558dSHauke Mehrtens 		.config_aneg	= xway_gphy14_config_aneg,
3691566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
370112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
371112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
372112b558dSHauke Mehrtens 		.resume		= genphy_resume,
373112b558dSHauke Mehrtens 	}, {
374112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY11G_1_4,
375112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
376112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.4",
377dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
378112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
379112b558dSHauke Mehrtens 		.config_aneg	= xway_gphy14_config_aneg,
3801566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
381112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
382112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
383112b558dSHauke Mehrtens 		.resume		= genphy_resume,
384112b558dSHauke Mehrtens 	}, {
385112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY22F_1_4,
386112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
387112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.4",
388dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
389112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
390112b558dSHauke Mehrtens 		.config_aneg	= xway_gphy14_config_aneg,
3911566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
392112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
393112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
394112b558dSHauke Mehrtens 		.resume		= genphy_resume,
395112b558dSHauke Mehrtens 	}, {
396112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY11G_1_5,
397112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
398112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY11G (PEF 7071/PEF 7072) v1.5 / v1.6",
399dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
400112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
4011566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
402112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
403112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
404112b558dSHauke Mehrtens 		.resume		= genphy_resume,
405112b558dSHauke Mehrtens 	}, {
406112b558dSHauke Mehrtens 		.phy_id		= PHY_ID_PHY22F_1_5,
407112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
408112b558dSHauke Mehrtens 		.name		= "Intel XWAY PHY22F (PEF 7061) v1.5 / v1.6",
409dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
410112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
4111566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
412112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
413112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
414112b558dSHauke Mehrtens 		.resume		= genphy_resume,
415112b558dSHauke Mehrtens 	}, {
416f452518cSMathias Kresin 		.phy_id		= PHY_ID_PHY11G_VR9_1_1,
417f452518cSMathias Kresin 		.phy_id_mask	= 0xffffffff,
418f452518cSMathias Kresin 		.name		= "Intel XWAY PHY11G (xRX v1.1 integrated)",
419dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
420f452518cSMathias Kresin 		.config_init	= xway_gphy_config_init,
4211566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
422f452518cSMathias Kresin 		.config_intr	= xway_gphy_config_intr,
423f452518cSMathias Kresin 		.suspend	= genphy_suspend,
424f452518cSMathias Kresin 		.resume		= genphy_resume,
425f452518cSMathias Kresin 	}, {
426f452518cSMathias Kresin 		.phy_id		= PHY_ID_PHY22F_VR9_1_1,
427f452518cSMathias Kresin 		.phy_id_mask	= 0xffffffff,
428f452518cSMathias Kresin 		.name		= "Intel XWAY PHY22F (xRX v1.1 integrated)",
429dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
430f452518cSMathias Kresin 		.config_init	= xway_gphy_config_init,
4311566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
432f452518cSMathias Kresin 		.config_intr	= xway_gphy_config_intr,
433f452518cSMathias Kresin 		.suspend	= genphy_suspend,
434f452518cSMathias Kresin 		.resume		= genphy_resume,
435f452518cSMathias Kresin 	}, {
4365b73d995SMathias Kresin 		.phy_id		= PHY_ID_PHY11G_VR9_1_2,
437112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
4385b73d995SMathias Kresin 		.name		= "Intel XWAY PHY11G (xRX v1.2 integrated)",
439dcdecdcfSHeiner Kallweit 		/* PHY_GBIT_FEATURES */
440112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
4411566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
442112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
443112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
444112b558dSHauke Mehrtens 		.resume		= genphy_resume,
445112b558dSHauke Mehrtens 	}, {
4465b73d995SMathias Kresin 		.phy_id		= PHY_ID_PHY22F_VR9_1_2,
447112b558dSHauke Mehrtens 		.phy_id_mask	= 0xffffffff,
4485b73d995SMathias Kresin 		.name		= "Intel XWAY PHY22F (xRX v1.2 integrated)",
449dcdecdcfSHeiner Kallweit 		/* PHY_BASIC_FEATURES */
450112b558dSHauke Mehrtens 		.config_init	= xway_gphy_config_init,
4511566db04SIoana Ciornei 		.handle_interrupt = xway_gphy_handle_interrupt,
452112b558dSHauke Mehrtens 		.config_intr	= xway_gphy_config_intr,
453112b558dSHauke Mehrtens 		.suspend	= genphy_suspend,
454112b558dSHauke Mehrtens 		.resume		= genphy_resume,
455112b558dSHauke Mehrtens 	},
456112b558dSHauke Mehrtens };
457112b558dSHauke Mehrtens module_phy_driver(xway_gphy);
458112b558dSHauke Mehrtens 
459112b558dSHauke Mehrtens static struct mdio_device_id __maybe_unused xway_gphy_tbl[] = {
460112b558dSHauke Mehrtens 	{ PHY_ID_PHY11G_1_3, 0xffffffff },
461112b558dSHauke Mehrtens 	{ PHY_ID_PHY22F_1_3, 0xffffffff },
462112b558dSHauke Mehrtens 	{ PHY_ID_PHY11G_1_4, 0xffffffff },
463112b558dSHauke Mehrtens 	{ PHY_ID_PHY22F_1_4, 0xffffffff },
464112b558dSHauke Mehrtens 	{ PHY_ID_PHY11G_1_5, 0xffffffff },
465112b558dSHauke Mehrtens 	{ PHY_ID_PHY22F_1_5, 0xffffffff },
466f452518cSMathias Kresin 	{ PHY_ID_PHY11G_VR9_1_1, 0xffffffff },
467f452518cSMathias Kresin 	{ PHY_ID_PHY22F_VR9_1_1, 0xffffffff },
4685b73d995SMathias Kresin 	{ PHY_ID_PHY11G_VR9_1_2, 0xffffffff },
4695b73d995SMathias Kresin 	{ PHY_ID_PHY22F_VR9_1_2, 0xffffffff },
470112b558dSHauke Mehrtens 	{ }
471112b558dSHauke Mehrtens };
472112b558dSHauke Mehrtens MODULE_DEVICE_TABLE(mdio, xway_gphy_tbl);
473112b558dSHauke Mehrtens 
474112b558dSHauke Mehrtens MODULE_DESCRIPTION("Intel XWAY PHY driver");
475112b558dSHauke Mehrtens MODULE_LICENSE("GPL");
476