blob: 7a3740c7bf6d128f34b8e5ae3b2c6e6d96b162a5 [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 Lakkarajud50736a2016-08-05 17:54:21 +053016
17enum rgmii_rx_clock_delay {
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053018 RGMII_RX_CLK_DELAY_0_2_NS = 0,
19 RGMII_RX_CLK_DELAY_0_8_NS = 1,
20 RGMII_RX_CLK_DELAY_1_1_NS = 2,
21 RGMII_RX_CLK_DELAY_1_7_NS = 3,
22 RGMII_RX_CLK_DELAY_2_0_NS = 4,
23 RGMII_RX_CLK_DELAY_2_3_NS = 5,
24 RGMII_RX_CLK_DELAY_2_6_NS = 6,
25 RGMII_RX_CLK_DELAY_3_4_NS = 7
Raju Lakkarajud50736a2016-08-05 17:54:21 +053026};
27
Raju Lakkaraju1a211012016-09-19 15:33:54 +053028/* Microsemi VSC85xx PHY registers */
29/* IEEE 802. Std Registers */
30#define MSCC_PHY_EXT_PHY_CNTL_1 23
31#define MAC_IF_SELECTION_MASK 0x1800
32#define MAC_IF_SELECTION_GMII 0
33#define MAC_IF_SELECTION_RMII 1
34#define MAC_IF_SELECTION_RGMII 2
35#define MAC_IF_SELECTION_POS 11
36#define FAR_END_LOOPBACK_MODE_MASK 0x0008
37
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053038#define MII_VSC85XX_INT_MASK 25
39#define MII_VSC85XX_INT_MASK_MASK 0xa000
Raju Lakkaraju0a55c122016-10-05 14:19:27 +053040#define MII_VSC85XX_INT_MASK_WOL 0x0040
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053041#define MII_VSC85XX_INT_STATUS 26
Raju Lakkarajud50736a2016-08-05 17:54:21 +053042
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053043#define MSCC_PHY_WOL_MAC_CONTROL 27
44#define EDGE_RATE_CNTL_POS 5
45#define EDGE_RATE_CNTL_MASK 0x00E0
46
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053047#define MSCC_EXT_PAGE_ACCESS 31
48#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010049#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053050#define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
Raju Lakkarajud50736a2016-08-05 17:54:21 +053051
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010052/* Extended Page 1 Registers */
53#define MSCC_PHY_ACTIPHY_CNTL 20
54#define DOWNSHIFT_CNTL_MASK 0x001C
55#define DOWNSHIFT_EN 0x0010
56#define DOWNSHIFT_CNTL_POS 2
57
Raju Lakkarajud50736a2016-08-05 17:54:21 +053058/* Extended Page 2 Registers */
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053059#define MSCC_PHY_RGMII_CNTL 20
60#define RGMII_RX_CLK_DELAY_MASK 0x0070
61#define RGMII_RX_CLK_DELAY_POS 4
Raju Lakkarajud50736a2016-08-05 17:54:21 +053062
Raju Lakkaraju0a55c122016-10-05 14:19:27 +053063#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
64#define MSCC_PHY_WOL_MID_MAC_ADDR 22
65#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
66#define MSCC_PHY_WOL_LOWER_PASSWD 24
67#define MSCC_PHY_WOL_MID_PASSWD 25
68#define MSCC_PHY_WOL_UPPER_PASSWD 26
69
70#define MSCC_PHY_WOL_MAC_CONTROL 27
71#define SECURE_ON_ENABLE 0x8000
72#define SECURE_ON_PASSWD_LEN_4 0x4000
73
Raju Lakkarajud50736a2016-08-05 17:54:21 +053074/* Microsemi PHY ID's */
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +020075#define PHY_ID_VSC8530 0x00070560
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053076#define PHY_ID_VSC8531 0x00070570
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +020077#define PHY_ID_VSC8540 0x00070760
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +053078#define PHY_ID_VSC8541 0x00070770
Raju Lakkarajud50736a2016-08-05 17:54:21 +053079
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +020080#define MSCC_VDDMAC_1500 1500
81#define MSCC_VDDMAC_1800 1800
82#define MSCC_VDDMAC_2500 2500
83#define MSCC_VDDMAC_3300 3300
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053084
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +010085#define DOWNSHIFT_COUNT_MAX 5
86
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053087struct vsc8531_private {
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +020088 int rate_magic;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +053089};
90
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +020091#ifdef CONFIG_OF_MDIO
92struct vsc8531_edge_rate_table {
93 u16 vddmac;
94 u8 slowdown[8];
95};
96
97static const struct vsc8531_edge_rate_table edge_table[] = {
98 {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} },
99 {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} },
100 {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} },
101 {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} },
102};
103#endif /* CONFIG_OF_MDIO */
104
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530105static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
106{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530107 int rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530108
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530109 rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
110 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530111}
112
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100113static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
114{
115 int rc;
116 u16 reg_val;
117
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100118 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
119 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800120 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100121
122 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
123 reg_val &= DOWNSHIFT_CNTL_MASK;
124 if (!(reg_val & DOWNSHIFT_EN))
125 *count = DOWNSHIFT_DEV_DISABLE;
126 else
127 *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2;
128 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
129
Florian Fainelli4b652462016-11-22 13:55:31 -0800130out:
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100131 return rc;
132}
133
134static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count)
135{
136 int rc;
137 u16 reg_val;
138
139 if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) {
140 /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */
141 count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
142 } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) {
143 phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n");
144 return -ERANGE;
145 } else if (count) {
146 /* Downshift count is either 2,3,4 or 5 */
147 count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
148 }
149
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100150 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
151 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800152 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100153
154 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
155 reg_val &= ~(DOWNSHIFT_CNTL_MASK);
156 reg_val |= count;
157 rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val);
158 if (rc != 0)
Florian Fainelli4b652462016-11-22 13:55:31 -0800159 goto out;
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100160
161 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
162
Florian Fainelli4b652462016-11-22 13:55:31 -0800163out:
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100164 return rc;
165}
166
Raju Lakkaraju0a55c122016-10-05 14:19:27 +0530167static int vsc85xx_wol_set(struct phy_device *phydev,
168 struct ethtool_wolinfo *wol)
169{
170 int rc;
171 u16 reg_val;
172 u8 i;
173 u16 pwd[3] = {0, 0, 0};
174 struct ethtool_wolinfo *wol_conf = wol;
175 u8 *mac_addr = phydev->attached_dev->dev_addr;
176
177 mutex_lock(&phydev->lock);
178 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
179 if (rc != 0)
180 goto out_unlock;
181
182 if (wol->wolopts & WAKE_MAGIC) {
183 /* Store the device address for the magic packet */
184 for (i = 0; i < ARRAY_SIZE(pwd); i++)
185 pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 |
186 mac_addr[5 - i * 2];
187 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, pwd[0]);
188 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, pwd[1]);
189 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, pwd[2]);
190 } else {
191 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, 0);
192 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, 0);
193 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, 0);
194 }
195
196 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
197 for (i = 0; i < ARRAY_SIZE(pwd); i++)
198 pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 |
199 wol_conf->sopass[5 - i * 2];
200 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, pwd[0]);
201 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, pwd[1]);
202 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, pwd[2]);
203 } else {
204 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, 0);
205 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, 0);
206 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, 0);
207 }
208
209 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
210 if (wol_conf->wolopts & WAKE_MAGICSECURE)
211 reg_val |= SECURE_ON_ENABLE;
212 else
213 reg_val &= ~SECURE_ON_ENABLE;
214 phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
215
216 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
217 if (rc != 0)
218 goto out_unlock;
219
220 if (wol->wolopts & WAKE_MAGIC) {
221 /* Enable the WOL interrupt */
222 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
223 reg_val |= MII_VSC85XX_INT_MASK_WOL;
224 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
225 if (rc != 0)
226 goto out_unlock;
227 } else {
228 /* Disable the WOL interrupt */
229 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
230 reg_val &= (~MII_VSC85XX_INT_MASK_WOL);
231 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
232 if (rc != 0)
233 goto out_unlock;
234 }
235 /* Clear WOL iterrupt status */
236 reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS);
237
238out_unlock:
239 mutex_unlock(&phydev->lock);
240
241 return rc;
242}
243
244static void vsc85xx_wol_get(struct phy_device *phydev,
245 struct ethtool_wolinfo *wol)
246{
247 int rc;
248 u16 reg_val;
249 u8 i;
250 u16 pwd[3] = {0, 0, 0};
251 struct ethtool_wolinfo *wol_conf = wol;
252
253 mutex_lock(&phydev->lock);
254 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
255 if (rc != 0)
256 goto out_unlock;
257
258 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
259 if (reg_val & SECURE_ON_ENABLE)
260 wol_conf->wolopts |= WAKE_MAGICSECURE;
261 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
262 pwd[0] = phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD);
263 pwd[1] = phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD);
264 pwd[2] = phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD);
265 for (i = 0; i < ARRAY_SIZE(pwd); i++) {
266 wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff;
267 wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00)
268 >> 8;
269 }
270 }
271
272 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
273
274out_unlock:
275 mutex_unlock(&phydev->lock);
276}
277
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200278#ifdef CONFIG_OF_MDIO
279static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530280{
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530281 u8 sd;
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200282 u16 vdd;
283 int rc, i, j;
284 struct device *dev = &phydev->mdio.dev;
285 struct device_node *of_node = dev->of_node;
286 u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown);
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530287
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200288 if (!of_node)
289 return -ENODEV;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530290
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200291 rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd);
292 if (rc != 0)
293 vdd = MSCC_VDDMAC_3300;
294
295 rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd);
296 if (rc != 0)
297 sd = 0;
298
299 for (i = 0; i < ARRAY_SIZE(edge_table); i++)
300 if (edge_table[i].vddmac == vdd)
301 for (j = 0; j < sd_array_size; j++)
302 if (edge_table[i].slowdown[j] == sd)
303 return (sd_array_size - j - 1);
304
305 return -EINVAL;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530306}
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200307#else
308static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
309{
310 return 0;
311}
312#endif /* CONFIG_OF_MDIO */
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530313
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200314static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530315{
316 int rc;
317 u16 reg_val;
318
319 mutex_lock(&phydev->lock);
320 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
321 if (rc != 0)
322 goto out_unlock;
323 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
324 reg_val &= ~(EDGE_RATE_CNTL_MASK);
325 reg_val |= (edge_rate << EDGE_RATE_CNTL_POS);
326 rc = phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
327 if (rc != 0)
328 goto out_unlock;
329 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
330
331out_unlock:
332 mutex_unlock(&phydev->lock);
333
334 return rc;
335}
336
Raju Lakkaraju1a211012016-09-19 15:33:54 +0530337static int vsc85xx_mac_if_set(struct phy_device *phydev,
338 phy_interface_t interface)
339{
340 int rc;
341 u16 reg_val;
342
343 mutex_lock(&phydev->lock);
344 reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
345 reg_val &= ~(MAC_IF_SELECTION_MASK);
346 switch (interface) {
347 case PHY_INTERFACE_MODE_RGMII:
348 reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
349 break;
350 case PHY_INTERFACE_MODE_RMII:
351 reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
352 break;
353 case PHY_INTERFACE_MODE_MII:
354 case PHY_INTERFACE_MODE_GMII:
355 reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
356 break;
357 default:
358 rc = -EINVAL;
359 goto out_unlock;
360 }
361 rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
362 if (rc != 0)
363 goto out_unlock;
364
365 rc = genphy_soft_reset(phydev);
366
367out_unlock:
368 mutex_unlock(&phydev->lock);
369
370 return rc;
371}
372
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530373static int vsc85xx_default_config(struct phy_device *phydev)
374{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530375 int rc;
376 u16 reg_val;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530377
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530378 mutex_lock(&phydev->lock);
379 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
380 if (rc != 0)
381 goto out_unlock;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530382
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530383 reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
384 reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
385 reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
386 phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
387 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530388
389out_unlock:
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530390 mutex_unlock(&phydev->lock);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530391
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530392 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530393}
394
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100395static int vsc85xx_get_tunable(struct phy_device *phydev,
396 struct ethtool_tunable *tuna, void *data)
397{
398 switch (tuna->id) {
399 case ETHTOOL_PHY_DOWNSHIFT:
400 return vsc85xx_downshift_get(phydev, (u8 *)data);
401 default:
402 return -EINVAL;
403 }
404}
405
406static int vsc85xx_set_tunable(struct phy_device *phydev,
407 struct ethtool_tunable *tuna,
408 const void *data)
409{
410 switch (tuna->id) {
411 case ETHTOOL_PHY_DOWNSHIFT:
412 return vsc85xx_downshift_set(phydev, *(u8 *)data);
413 default:
414 return -EINVAL;
415 }
416}
417
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530418static int vsc85xx_config_init(struct phy_device *phydev)
419{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530420 int rc;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530421 struct vsc8531_private *vsc8531 = phydev->priv;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530422
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530423 rc = vsc85xx_default_config(phydev);
424 if (rc)
425 return rc;
Raju Lakkaraju1a211012016-09-19 15:33:54 +0530426
427 rc = vsc85xx_mac_if_set(phydev, phydev->interface);
428 if (rc)
429 return rc;
430
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200431 rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530432 if (rc)
433 return rc;
434
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530435 rc = genphy_config_init(phydev);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530436
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530437 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530438}
439
440static int vsc85xx_ack_interrupt(struct phy_device *phydev)
441{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530442 int rc = 0;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530443
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530444 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
445 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530446
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530447 return (rc < 0) ? rc : 0;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530448}
449
450static int vsc85xx_config_intr(struct phy_device *phydev)
451{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530452 int rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530453
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530454 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
455 rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
456 MII_VSC85XX_INT_MASK_MASK);
457 } else {
458 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
459 if (rc < 0)
460 return rc;
461 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
462 }
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530463
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530464 return rc;
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530465}
466
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530467static int vsc85xx_probe(struct phy_device *phydev)
468{
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200469 int rate_magic;
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530470 struct vsc8531_private *vsc8531;
471
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200472 rate_magic = vsc85xx_edge_rate_magic_get(phydev);
473 if (rate_magic < 0)
474 return rate_magic;
475
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530476 vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
477 if (!vsc8531)
478 return -ENOMEM;
479
480 phydev->priv = vsc8531;
481
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200482 vsc8531->rate_magic = rate_magic;
483
Raju Lakkarajua4cc96d2016-10-03 12:53:13 +0530484 return 0;
485}
486
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530487/* Microsemi VSC85xx PHYs */
488static struct phy_driver vsc85xx_driver[] = {
489{
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200490 .phy_id = PHY_ID_VSC8530,
491 .name = "Microsemi FE VSC8530",
492 .phy_id_mask = 0xfffffff0,
493 .features = PHY_BASIC_FEATURES,
494 .flags = PHY_HAS_INTERRUPT,
495 .soft_reset = &genphy_soft_reset,
496 .config_init = &vsc85xx_config_init,
497 .config_aneg = &genphy_config_aneg,
498 .aneg_done = &genphy_aneg_done,
499 .read_status = &genphy_read_status,
500 .ack_interrupt = &vsc85xx_ack_interrupt,
501 .config_intr = &vsc85xx_config_intr,
502 .suspend = &genphy_suspend,
503 .resume = &genphy_resume,
504 .probe = &vsc85xx_probe,
505 .set_wol = &vsc85xx_wol_set,
506 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100507 .get_tunable = &vsc85xx_get_tunable,
508 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200509},
510{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530511 .phy_id = PHY_ID_VSC8531,
512 .name = "Microsemi VSC8531",
513 .phy_id_mask = 0xfffffff0,
514 .features = PHY_GBIT_FEATURES,
515 .flags = PHY_HAS_INTERRUPT,
516 .soft_reset = &genphy_soft_reset,
517 .config_init = &vsc85xx_config_init,
518 .config_aneg = &genphy_config_aneg,
519 .aneg_done = &genphy_aneg_done,
520 .read_status = &genphy_read_status,
521 .ack_interrupt = &vsc85xx_ack_interrupt,
522 .config_intr = &vsc85xx_config_intr,
523 .suspend = &genphy_suspend,
524 .resume = &genphy_resume,
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200525 .probe = &vsc85xx_probe,
526 .set_wol = &vsc85xx_wol_set,
527 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100528 .get_tunable = &vsc85xx_get_tunable,
529 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530530},
531{
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200532 .phy_id = PHY_ID_VSC8540,
533 .name = "Microsemi FE VSC8540 SyncE",
534 .phy_id_mask = 0xfffffff0,
535 .features = PHY_BASIC_FEATURES,
536 .flags = PHY_HAS_INTERRUPT,
537 .soft_reset = &genphy_soft_reset,
538 .config_init = &vsc85xx_config_init,
539 .config_aneg = &genphy_config_aneg,
540 .aneg_done = &genphy_aneg_done,
541 .read_status = &genphy_read_status,
542 .ack_interrupt = &vsc85xx_ack_interrupt,
543 .config_intr = &vsc85xx_config_intr,
544 .suspend = &genphy_suspend,
545 .resume = &genphy_resume,
546 .probe = &vsc85xx_probe,
547 .set_wol = &vsc85xx_wol_set,
548 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100549 .get_tunable = &vsc85xx_get_tunable,
550 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200551},
552{
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530553 .phy_id = PHY_ID_VSC8541,
554 .name = "Microsemi VSC8541 SyncE",
555 .phy_id_mask = 0xfffffff0,
556 .features = PHY_GBIT_FEATURES,
557 .flags = PHY_HAS_INTERRUPT,
558 .soft_reset = &genphy_soft_reset,
559 .config_init = &vsc85xx_config_init,
560 .config_aneg = &genphy_config_aneg,
561 .aneg_done = &genphy_aneg_done,
562 .read_status = &genphy_read_status,
563 .ack_interrupt = &vsc85xx_ack_interrupt,
564 .config_intr = &vsc85xx_config_intr,
565 .suspend = &genphy_suspend,
566 .resume = &genphy_resume,
Allan W. Nielsen4f58e6d2016-10-12 15:47:51 +0200567 .probe = &vsc85xx_probe,
568 .set_wol = &vsc85xx_wol_set,
569 .get_wol = &vsc85xx_wol_get,
Raju Lakkaraju310d9ad2016-11-17 13:07:24 +0100570 .get_tunable = &vsc85xx_get_tunable,
571 .set_tunable = &vsc85xx_set_tunable,
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530572}
573
574};
575
576module_phy_driver(vsc85xx_driver);
577
578static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200579 { PHY_ID_VSC8530, 0xfffffff0, },
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530580 { PHY_ID_VSC8531, 0xfffffff0, },
Raju Lakkarajuaf1fee92016-10-28 12:10:11 +0200581 { PHY_ID_VSC8540, 0xfffffff0, },
Raju Lakkaraju4ffd03f2016-09-08 14:09:31 +0530582 { PHY_ID_VSC8541, 0xfffffff0, },
583 { }
Raju Lakkarajud50736a2016-08-05 17:54:21 +0530584};
585
586MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
587
588MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
589MODULE_AUTHOR("Nagaraju Lakkaraju");
590MODULE_LICENSE("Dual MIT/GPL");