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