blob: af433f226ef44fece932d588ef6346b67c0f5983 [file] [log] [blame]
Raju Lakkarajud50736a2016-08-05 17:54:21 +05301/*
2 * Driver for Microsemi VSC85xx PHYs
3 *
4 * Author: Nagaraju Lakkaraju
5 * License: Dual MIT/GPL
6 * Copyright (c) 2016 Microsemi Corporation
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/mdio.h>
12#include <linux/mii.h>
13#include <linux/phy.h>
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053014#include <linux/of.h>
Raju Lakkaraju0a55c122016-10-05 14:19:27 +053015#include <linux/netdevice.h>
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +053016#include <dt-bindings/net/mscc-phy-vsc8531.h>
Raju Lakkarajud50736a2016-08-05 17:54:21 +053017
18enum rgmii_rx_clock_delay {
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053019 RGMII_RX_CLK_DELAY_0_2_NS = 0,
20 RGMII_RX_CLK_DELAY_0_8_NS = 1,
21 RGMII_RX_CLK_DELAY_1_1_NS = 2,
22 RGMII_RX_CLK_DELAY_1_7_NS = 3,
23 RGMII_RX_CLK_DELAY_2_0_NS = 4,
24 RGMII_RX_CLK_DELAY_2_3_NS = 5,
25 RGMII_RX_CLK_DELAY_2_6_NS = 6,
26 RGMII_RX_CLK_DELAY_3_4_NS = 7
Raju Lakkarajud50736a2016-08-05 17:54:21 +053027};
28
Raju Lakkaraju1a211012016-09-19 15:33:54 +053029/* Microsemi VSC85xx PHY registers */
30/* IEEE 802. Std Registers */
Raju Lakkaraju233275e2016-11-29 15:16:48 +053031#define MSCC_PHY_BYPASS_CONTROL 18
32#define DISABLE_HP_AUTO_MDIX_MASK 0x0080
33#define DISABLE_PAIR_SWAP_CORR_MASK 0x0020
34#define DISABLE_POLARITY_CORR_MASK 0x0010
35
Raju Lakkaraju1a211012016-09-19 15:33:54 +053036#define MSCC_PHY_EXT_PHY_CNTL_1 23
37#define MAC_IF_SELECTION_MASK 0x1800
38#define MAC_IF_SELECTION_GMII 0
39#define MAC_IF_SELECTION_RMII 1
40#define MAC_IF_SELECTION_RGMII 2
41#define MAC_IF_SELECTION_POS 11
42#define FAR_END_LOOPBACK_MODE_MASK 0x0008
43
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053044#define MII_VSC85XX_INT_MASK 25
45#define MII_VSC85XX_INT_MASK_MASK 0xa000
Raju Lakkaraju0a55c122016-10-05 14:19:27 +053046#define MII_VSC85XX_INT_MASK_WOL 0x0040
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053047#define MII_VSC85XX_INT_STATUS 26
Raju Lakkarajud50736a2016-08-05 17:54:21 +053048
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053049#define MSCC_PHY_WOL_MAC_CONTROL 27
50#define EDGE_RATE_CNTL_POS 5
51#define EDGE_RATE_CNTL_MASK 0x00E0
52
Raju Lakkaraju233275e2016-11-29 15:16:48 +053053#define MSCC_PHY_DEV_AUX_CNTL 28
54#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
55
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +053056#define MSCC_PHY_LED_MODE_SEL 29
Quentin Schulz11bfdab2018-09-03 10:48:47 +020057#define LED_MODE_SEL_POS(x) ((x) * 4)
58#define LED_MODE_SEL_MASK(x) (GENMASK(3, 0) << LED_MODE_SEL_POS(x))
59#define LED_MODE_SEL(x, mode) (((mode) << LED_MODE_SEL_POS(x)) & LED_MODE_SEL_MASK(x))
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +053060
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053061#define MSCC_EXT_PAGE_ACCESS 31
62#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010063#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053064#define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
Raju Lakkarajud50736a2016-08-05 17:54:21 +053065
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010066/* Extended Page 1 Registers */
Raju Lakkaraju233275e2016-11-29 15:16:48 +053067#define MSCC_PHY_EXT_MODE_CNTL 19
68#define FORCE_MDI_CROSSOVER_MASK 0x000C
69#define FORCE_MDI_CROSSOVER_MDIX 0x000C
70#define FORCE_MDI_CROSSOVER_MDI 0x0008
71
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010072#define MSCC_PHY_ACTIPHY_CNTL 20
73#define DOWNSHIFT_CNTL_MASK 0x001C
74#define DOWNSHIFT_EN 0x0010
75#define DOWNSHIFT_CNTL_POS 2
76
Raju Lakkarajud50736a2016-08-05 17:54:21 +053077/* Extended Page 2 Registers */
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053078#define MSCC_PHY_RGMII_CNTL 20
79#define RGMII_RX_CLK_DELAY_MASK 0x0070
80#define RGMII_RX_CLK_DELAY_POS 4
Raju Lakkarajud50736a2016-08-05 17:54:21 +053081
Raju Lakkaraju0a55c122016-10-05 14:19:27 +053082#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
83#define MSCC_PHY_WOL_MID_MAC_ADDR 22
84#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
85#define MSCC_PHY_WOL_LOWER_PASSWD 24
86#define MSCC_PHY_WOL_MID_PASSWD 25
87#define MSCC_PHY_WOL_UPPER_PASSWD 26
88
89#define MSCC_PHY_WOL_MAC_CONTROL 27
90#define SECURE_ON_ENABLE 0x8000
91#define SECURE_ON_PASSWD_LEN_4 0x4000
92
Raju Lakkarajud50736a2016-08-05 17:54:21 +053093/* Microsemi PHY ID's */
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +020094#define PHY_ID_VSC8530 0x00070560
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053095#define PHY_ID_VSC8531 0x00070570
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +020096#define PHY_ID_VSC8540 0x00070760
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053097#define PHY_ID_VSC8541 0x00070770
Raju Lakkarajud50736a2016-08-05 17:54:21 +053098
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +020099#define MSCC_VDDMAC_1500 1500
100#define MSCC_VDDMAC_1800 1800
101#define MSCC_VDDMAC_2500 2500
102#define MSCC_VDDMAC_3300 3300
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530103
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100104#define DOWNSHIFT_COUNT_MAX 5
105
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200106#define MAX_LEDS 4
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530107struct vsc8531_private {
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200108 int rate_magic;
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200109 u8 leds_mode[MAX_LEDS];
110 u8 nleds;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530111};
112
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200113#ifdef CONFIG_OF_MDIO
114struct vsc8531_edge_rate_table {
115 u16 vddmac;
116 u8 slowdown[8];
117};
118
119static const struct vsc8531_edge_rate_table edge_table[] = {
120 {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} },
121 {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} },
122 {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} },
123 {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} },
124};
125#endif /* CONFIG_OF_MDIO */
126
Quentin Schulz86ff7362018-07-30 14:53:13 +0200127static int vsc85xx_phy_page_set(struct phy_device *phydev, u16 page)
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530128{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530129 int rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530130
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530131 rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
132 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530133}
134
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530135static int vsc85xx_led_cntl_set(struct phy_device *phydev,
136 u8 led_num,
137 u8 mode)
138{
139 int rc;
140 u16 reg_val;
141
142 mutex_lock(&phydev->lock);
143 reg_val = phy_read(phydev, MSCC_PHY_LED_MODE_SEL);
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200144 reg_val &= ~LED_MODE_SEL_MASK(led_num);
145 reg_val |= LED_MODE_SEL(led_num, (u16)mode);
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530146 rc = phy_write(phydev, MSCC_PHY_LED_MODE_SEL, reg_val);
147 mutex_unlock(&phydev->lock);
148
149 return rc;
150}
151
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530152static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix)
153{
154 u16 reg_val;
155
156 reg_val = phy_read(phydev, MSCC_PHY_DEV_AUX_CNTL);
157 if (reg_val & HP_AUTO_MDIX_X_OVER_IND_MASK)
158 *mdix = ETH_TP_MDI_X;
159 else
160 *mdix = ETH_TP_MDI;
161
162 return 0;
163}
164
165static int vsc85xx_mdix_set(struct phy_device *phydev, u8 mdix)
166{
167 int rc;
168 u16 reg_val;
169
170 reg_val = phy_read(phydev, MSCC_PHY_BYPASS_CONTROL);
171 if ((mdix == ETH_TP_MDI) || (mdix == ETH_TP_MDI_X)) {
172 reg_val |= (DISABLE_PAIR_SWAP_CORR_MASK |
173 DISABLE_POLARITY_CORR_MASK |
174 DISABLE_HP_AUTO_MDIX_MASK);
175 } else {
176 reg_val &= ~(DISABLE_PAIR_SWAP_CORR_MASK |
177 DISABLE_POLARITY_CORR_MASK |
178 DISABLE_HP_AUTO_MDIX_MASK);
179 }
180 rc = phy_write(phydev, MSCC_PHY_BYPASS_CONTROL, reg_val);
181 if (rc != 0)
182 return rc;
183
184 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
185 if (rc != 0)
186 return rc;
187
188 reg_val = phy_read(phydev, MSCC_PHY_EXT_MODE_CNTL);
189 reg_val &= ~(FORCE_MDI_CROSSOVER_MASK);
190 if (mdix == ETH_TP_MDI)
191 reg_val |= FORCE_MDI_CROSSOVER_MDI;
192 else if (mdix == ETH_TP_MDI_X)
193 reg_val |= FORCE_MDI_CROSSOVER_MDIX;
194 rc = phy_write(phydev, MSCC_PHY_EXT_MODE_CNTL, reg_val);
195 if (rc != 0)
196 return rc;
197
198 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
199 if (rc != 0)
200 return rc;
201
202 return genphy_restart_aneg(phydev);
203}
204
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100205static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
206{
207 int rc;
208 u16 reg_val;
209
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100210 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
211 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800212 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100213
214 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
215 reg_val &= DOWNSHIFT_CNTL_MASK;
216 if (!(reg_val & DOWNSHIFT_EN))
217 *count = DOWNSHIFT_DEV_DISABLE;
218 else
219 *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2;
220 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
221
Florian Fainelli4b652462016-11-22 13:55:31 -0800222out:
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100223 return rc;
224}
225
226static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count)
227{
228 int rc;
229 u16 reg_val;
230
231 if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) {
232 /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */
233 count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
234 } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) {
235 phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n");
236 return -ERANGE;
237 } else if (count) {
238 /* Downshift count is either 2,3,4 or 5 */
239 count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
240 }
241
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100242 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
243 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800244 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100245
246 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
247 reg_val &= ~(DOWNSHIFT_CNTL_MASK);
248 reg_val |= count;
249 rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val);
250 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800251 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100252
253 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
254
Florian Fainelli4b652462016-11-22 13:55:31 -0800255out:
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100256 return rc;
257}
258
Raju Lakkaraju0a55c122016-10-05 14:19:27 +0530259static int vsc85xx_wol_set(struct phy_device *phydev,
260 struct ethtool_wolinfo *wol)
261{
262 int rc;
263 u16 reg_val;
264 u8 i;
265 u16 pwd[3] = {0, 0, 0};
266 struct ethtool_wolinfo *wol_conf = wol;
267 u8 *mac_addr = phydev->attached_dev->dev_addr;
268
269 mutex_lock(&phydev->lock);
270 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
271 if (rc != 0)
272 goto out_unlock;
273
274 if (wol->wolopts & WAKE_MAGIC) {
275 /* Store the device address for the magic packet */
276 for (i = 0; i < ARRAY_SIZE(pwd); i++)
277 pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 |
278 mac_addr[5 - i * 2];
279 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, pwd[0]);
280 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, pwd[1]);
281 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, pwd[2]);
282 } else {
283 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, 0);
284 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, 0);
285 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, 0);
286 }
287
288 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
289 for (i = 0; i < ARRAY_SIZE(pwd); i++)
290 pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 |
291 wol_conf->sopass[5 - i * 2];
292 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, pwd[0]);
293 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, pwd[1]);
294 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, pwd[2]);
295 } else {
296 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, 0);
297 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, 0);
298 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, 0);
299 }
300
301 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
302 if (wol_conf->wolopts & WAKE_MAGICSECURE)
303 reg_val |= SECURE_ON_ENABLE;
304 else
305 reg_val &= ~SECURE_ON_ENABLE;
306 phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
307
308 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
309 if (rc != 0)
310 goto out_unlock;
311
312 if (wol->wolopts & WAKE_MAGIC) {
313 /* Enable the WOL interrupt */
314 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
315 reg_val |= MII_VSC85XX_INT_MASK_WOL;
316 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
317 if (rc != 0)
318 goto out_unlock;
319 } else {
320 /* Disable the WOL interrupt */
321 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
322 reg_val &= (~MII_VSC85XX_INT_MASK_WOL);
323 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
324 if (rc != 0)
325 goto out_unlock;
326 }
327 /* Clear WOL iterrupt status */
328 reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS);
329
330out_unlock:
331 mutex_unlock(&phydev->lock);
332
333 return rc;
334}
335
336static void vsc85xx_wol_get(struct phy_device *phydev,
337 struct ethtool_wolinfo *wol)
338{
339 int rc;
340 u16 reg_val;
341 u8 i;
342 u16 pwd[3] = {0, 0, 0};
343 struct ethtool_wolinfo *wol_conf = wol;
344
345 mutex_lock(&phydev->lock);
346 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
347 if (rc != 0)
348 goto out_unlock;
349
350 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
351 if (reg_val & SECURE_ON_ENABLE)
352 wol_conf->wolopts |= WAKE_MAGICSECURE;
353 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
354 pwd[0] = phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD);
355 pwd[1] = phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD);
356 pwd[2] = phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD);
357 for (i = 0; i < ARRAY_SIZE(pwd); i++) {
358 wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff;
359 wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00)
360 >> 8;
361 }
362 }
363
364 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
365
366out_unlock:
367 mutex_unlock(&phydev->lock);
368}
369
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200370#ifdef CONFIG_OF_MDIO
371static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530372{
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530373 u8 sd;
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200374 u16 vdd;
375 int rc, i, j;
376 struct device *dev = &phydev->mdio.dev;
377 struct device_node *of_node = dev->of_node;
378 u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown);
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530379
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200380 if (!of_node)
381 return -ENODEV;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530382
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200383 rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd);
384 if (rc != 0)
385 vdd = MSCC_VDDMAC_3300;
386
387 rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd);
388 if (rc != 0)
389 sd = 0;
390
391 for (i = 0; i < ARRAY_SIZE(edge_table); i++)
392 if (edge_table[i].vddmac == vdd)
393 for (j = 0; j < sd_array_size; j++)
394 if (edge_table[i].slowdown[j] == sd)
395 return (sd_array_size - j - 1);
396
397 return -EINVAL;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530398}
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530399
400static int vsc85xx_dt_led_mode_get(struct phy_device *phydev,
401 char *led,
402 u8 default_mode)
403{
404 struct device *dev = &phydev->mdio.dev;
405 struct device_node *of_node = dev->of_node;
406 u8 led_mode;
407 int err;
408
409 if (!of_node)
410 return -ENODEV;
411
412 led_mode = default_mode;
413 err = of_property_read_u8(of_node, led, &led_mode);
414 if (!err && (led_mode > 15 || led_mode == 7 || led_mode == 11)) {
415 phydev_err(phydev, "DT %s invalid\n", led);
416 return -EINVAL;
417 }
418
419 return led_mode;
420}
421
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200422#else
423static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
424{
425 return 0;
426}
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530427
428static int vsc85xx_dt_led_mode_get(struct phy_device *phydev,
429 char *led,
430 u8 default_mode)
431{
432 return default_mode;
433}
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200434#endif /* CONFIG_OF_MDIO */
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530435
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200436static int vsc85xx_dt_led_modes_get(struct phy_device *phydev, u8 *default_mode)
437{
438 struct vsc8531_private *priv = phydev->priv;
439 char led_dt_prop[19];
440 int i, ret;
441
442 for (i = 0; i < priv->nleds; i++) {
443 ret = sprintf(led_dt_prop, "vsc8531,led-%d-mode", i);
444 if (ret < 0)
445 return ret;
446
447 ret = vsc85xx_dt_led_mode_get(phydev, led_dt_prop,
448 default_mode[i]);
449 if (ret < 0)
450 return ret;
451 priv->leds_mode[i] = ret;
452 }
453
454 return 0;
455}
456
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200457static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530458{
459 int rc;
460 u16 reg_val;
461
462 mutex_lock(&phydev->lock);
463 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
464 if (rc != 0)
465 goto out_unlock;
466 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
467 reg_val &= ~(EDGE_RATE_CNTL_MASK);
468 reg_val |= (edge_rate << EDGE_RATE_CNTL_POS);
469 rc = phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
470 if (rc != 0)
471 goto out_unlock;
472 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
473
474out_unlock:
475 mutex_unlock(&phydev->lock);
476
477 return rc;
478}
479
Raju Lakkaraju1a211012016-09-19 15:33:54 +0530480static int vsc85xx_mac_if_set(struct phy_device *phydev,
481 phy_interface_t interface)
482{
483 int rc;
484 u16 reg_val;
485
486 mutex_lock(&phydev->lock);
487 reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
488 reg_val &= ~(MAC_IF_SELECTION_MASK);
489 switch (interface) {
490 case PHY_INTERFACE_MODE_RGMII:
491 reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
492 break;
493 case PHY_INTERFACE_MODE_RMII:
494 reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
495 break;
496 case PHY_INTERFACE_MODE_MII:
497 case PHY_INTERFACE_MODE_GMII:
498 reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
499 break;
500 default:
501 rc = -EINVAL;
502 goto out_unlock;
503 }
504 rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
505 if (rc != 0)
506 goto out_unlock;
507
508 rc = genphy_soft_reset(phydev);
509
510out_unlock:
511 mutex_unlock(&phydev->lock);
512
513 return rc;
514}
515
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530516static int vsc85xx_default_config(struct phy_device *phydev)
517{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530518 int rc;
519 u16 reg_val;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530520
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530521 phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530522 mutex_lock(&phydev->lock);
523 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
524 if (rc != 0)
525 goto out_unlock;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530526
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530527 reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
528 reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
529 reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
530 phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
531 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530532
533out_unlock:
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530534 mutex_unlock(&phydev->lock);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530535
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530536 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530537}
538
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100539static int vsc85xx_get_tunable(struct phy_device *phydev,
540 struct ethtool_tunable *tuna, void *data)
541{
542 switch (tuna->id) {
543 case ETHTOOL_PHY_DOWNSHIFT:
544 return vsc85xx_downshift_get(phydev, (u8 *)data);
545 default:
546 return -EINVAL;
547 }
548}
549
550static int vsc85xx_set_tunable(struct phy_device *phydev,
551 struct ethtool_tunable *tuna,
552 const void *data)
553{
554 switch (tuna->id) {
555 case ETHTOOL_PHY_DOWNSHIFT:
556 return vsc85xx_downshift_set(phydev, *(u8 *)data);
557 default:
558 return -EINVAL;
559 }
560}
561
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530562static int vsc85xx_config_init(struct phy_device *phydev)
563{
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200564 int rc, i;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530565 struct vsc8531_private *vsc8531 = phydev->priv;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530566
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530567 rc = vsc85xx_default_config(phydev);
568 if (rc)
569 return rc;
Raju Lakkaraju1a211012016-09-19 15:33:54 +0530570
571 rc = vsc85xx_mac_if_set(phydev, phydev->interface);
572 if (rc)
573 return rc;
574
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200575 rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530576 if (rc)
577 return rc;
578
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200579 for (i = 0; i < vsc8531->nleds; i++) {
580 rc = vsc85xx_led_cntl_set(phydev, i, vsc8531->leds_mode[i]);
581 if (rc)
582 return rc;
583 }
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530584
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530585 rc = genphy_config_init(phydev);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530586
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530587 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530588}
589
590static int vsc85xx_ack_interrupt(struct phy_device *phydev)
591{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530592 int rc = 0;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530593
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530594 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
595 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530596
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530597 return (rc < 0) ? rc : 0;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530598}
599
600static int vsc85xx_config_intr(struct phy_device *phydev)
601{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530602 int rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530603
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530604 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
605 rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
606 MII_VSC85XX_INT_MASK_MASK);
607 } else {
608 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
609 if (rc < 0)
610 return rc;
611 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
612 }
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530613
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530614 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530615}
616
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530617static int vsc85xx_config_aneg(struct phy_device *phydev)
618{
619 int rc;
620
621 rc = vsc85xx_mdix_set(phydev, phydev->mdix_ctrl);
622 if (rc < 0)
623 return rc;
624
625 return genphy_config_aneg(phydev);
626}
627
628static int vsc85xx_read_status(struct phy_device *phydev)
629{
630 int rc;
631
632 rc = vsc85xx_mdix_get(phydev, &phydev->mdix);
633 if (rc < 0)
634 return rc;
635
636 return genphy_read_status(phydev);
637}
638
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530639static int vsc85xx_probe(struct phy_device *phydev)
640{
641 struct vsc8531_private *vsc8531;
Raju Lakkaraju04d8a0a2017-02-07 19:10:26 +0530642 int rate_magic;
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200643 u8 default_mode[2] = {VSC8531_LINK_1000_ACTIVITY,
644 VSC8531_LINK_100_ACTIVITY};
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530645
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200646 rate_magic = vsc85xx_edge_rate_magic_get(phydev);
647 if (rate_magic < 0)
648 return rate_magic;
649
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530650 vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
651 if (!vsc8531)
652 return -ENOMEM;
653
654 phydev->priv = vsc8531;
655
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200656 vsc8531->rate_magic = rate_magic;
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200657 vsc8531->nleds = 2;
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200658
Quentin Schulz11bfdab2018-09-03 10:48:47 +0200659 return vsc85xx_dt_led_modes_get(phydev, default_mode);
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530660}
661
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530662/* Microsemi VSC85xx PHYs */
663static struct phy_driver vsc85xx_driver[] = {
664{
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200665 .phy_id = PHY_ID_VSC8530,
666 .name = "Microsemi FE VSC8530",
667 .phy_id_mask = 0xfffffff0,
668 .features = PHY_BASIC_FEATURES,
669 .flags = PHY_HAS_INTERRUPT,
670 .soft_reset = &genphy_soft_reset,
671 .config_init = &vsc85xx_config_init,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530672 .config_aneg = &vsc85xx_config_aneg,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200673 .aneg_done = &genphy_aneg_done,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530674 .read_status = &vsc85xx_read_status,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200675 .ack_interrupt = &vsc85xx_ack_interrupt,
676 .config_intr = &vsc85xx_config_intr,
677 .suspend = &genphy_suspend,
678 .resume = &genphy_resume,
679 .probe = &vsc85xx_probe,
680 .set_wol = &vsc85xx_wol_set,
681 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100682 .get_tunable = &vsc85xx_get_tunable,
683 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200684},
685{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530686 .phy_id = PHY_ID_VSC8531,
687 .name = "Microsemi VSC8531",
688 .phy_id_mask = 0xfffffff0,
689 .features = PHY_GBIT_FEATURES,
690 .flags = PHY_HAS_INTERRUPT,
691 .soft_reset = &genphy_soft_reset,
692 .config_init = &vsc85xx_config_init,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530693 .config_aneg = &vsc85xx_config_aneg,
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530694 .aneg_done = &genphy_aneg_done,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530695 .read_status = &vsc85xx_read_status,
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530696 .ack_interrupt = &vsc85xx_ack_interrupt,
697 .config_intr = &vsc85xx_config_intr,
698 .suspend = &genphy_suspend,
699 .resume = &genphy_resume,
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200700 .probe = &vsc85xx_probe,
701 .set_wol = &vsc85xx_wol_set,
702 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100703 .get_tunable = &vsc85xx_get_tunable,
704 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530705},
706{
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200707 .phy_id = PHY_ID_VSC8540,
708 .name = "Microsemi FE VSC8540 SyncE",
709 .phy_id_mask = 0xfffffff0,
710 .features = PHY_BASIC_FEATURES,
711 .flags = PHY_HAS_INTERRUPT,
712 .soft_reset = &genphy_soft_reset,
713 .config_init = &vsc85xx_config_init,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530714 .config_aneg = &vsc85xx_config_aneg,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200715 .aneg_done = &genphy_aneg_done,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530716 .read_status = &vsc85xx_read_status,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200717 .ack_interrupt = &vsc85xx_ack_interrupt,
718 .config_intr = &vsc85xx_config_intr,
719 .suspend = &genphy_suspend,
720 .resume = &genphy_resume,
721 .probe = &vsc85xx_probe,
722 .set_wol = &vsc85xx_wol_set,
723 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100724 .get_tunable = &vsc85xx_get_tunable,
725 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200726},
727{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530728 .phy_id = PHY_ID_VSC8541,
729 .name = "Microsemi VSC8541 SyncE",
730 .phy_id_mask = 0xfffffff0,
731 .features = PHY_GBIT_FEATURES,
732 .flags = PHY_HAS_INTERRUPT,
733 .soft_reset = &genphy_soft_reset,
734 .config_init = &vsc85xx_config_init,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530735 .config_aneg = &vsc85xx_config_aneg,
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530736 .aneg_done = &genphy_aneg_done,
Raju Lakkaraju233275e2016-11-29 15:16:48 +0530737 .read_status = &vsc85xx_read_status,
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530738 .ack_interrupt = &vsc85xx_ack_interrupt,
739 .config_intr = &vsc85xx_config_intr,
740 .suspend = &genphy_suspend,
741 .resume = &genphy_resume,
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200742 .probe = &vsc85xx_probe,
743 .set_wol = &vsc85xx_wol_set,
744 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100745 .get_tunable = &vsc85xx_get_tunable,
746 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530747}
748
749};
750
751module_phy_driver(vsc85xx_driver);
752
753static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200754 { PHY_ID_VSC8530, 0xfffffff0, },
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530755 { PHY_ID_VSC8531, 0xfffffff0, },
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200756 { PHY_ID_VSC8540, 0xfffffff0, },
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530757 { PHY_ID_VSC8541, 0xfffffff0, },
758 { }
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530759};
760
761MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
762
763MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
764MODULE_AUTHOR("Nagaraju Lakkaraju");
765MODULE_LICENSE("Dual MIT/GPL");