2 * Driver for Microsemi VSC85xx PHYs
4 * Author: Nagaraju Lakkaraju
5 * License: Dual MIT/GPL
6 * Copyright (c) 2016 Microsemi Corporation
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>
15 enum rgmii_rx_clock_delay {
16 RGMII_RX_CLK_DELAY_0_2_NS = 0,
17 RGMII_RX_CLK_DELAY_0_8_NS = 1,
18 RGMII_RX_CLK_DELAY_1_1_NS = 2,
19 RGMII_RX_CLK_DELAY_1_7_NS = 3,
20 RGMII_RX_CLK_DELAY_2_0_NS = 4,
21 RGMII_RX_CLK_DELAY_2_3_NS = 5,
22 RGMII_RX_CLK_DELAY_2_6_NS = 6,
23 RGMII_RX_CLK_DELAY_3_4_NS = 7
26 /* Microsemi VSC85xx PHY registers */
27 /* IEEE 802. Std Registers */
28 #define MSCC_PHY_EXT_PHY_CNTL_1 23
29 #define MAC_IF_SELECTION_MASK 0x1800
30 #define MAC_IF_SELECTION_GMII 0
31 #define MAC_IF_SELECTION_RMII 1
32 #define MAC_IF_SELECTION_RGMII 2
33 #define MAC_IF_SELECTION_POS 11
34 #define FAR_END_LOOPBACK_MODE_MASK 0x0008
36 #define MII_VSC85XX_INT_MASK 25
37 #define MII_VSC85XX_INT_MASK_MASK 0xa000
38 #define MII_VSC85XX_INT_STATUS 26
40 #define MSCC_EXT_PAGE_ACCESS 31
41 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
42 #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
44 /* Extended Page 2 Registers */
45 #define MSCC_PHY_RGMII_CNTL 20
46 #define RGMII_RX_CLK_DELAY_MASK 0x0070
47 #define RGMII_RX_CLK_DELAY_POS 4
49 /* Microsemi PHY ID's */
50 #define PHY_ID_VSC8531 0x00070570
51 #define PHY_ID_VSC8541 0x00070770
53 static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
57 rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
61 static int vsc85xx_mac_if_set(struct phy_device *phydev,
62 phy_interface_t interface)
67 mutex_lock(&phydev->lock);
68 reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
69 reg_val &= ~(MAC_IF_SELECTION_MASK);
71 case PHY_INTERFACE_MODE_RGMII:
72 reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
74 case PHY_INTERFACE_MODE_RMII:
75 reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
77 case PHY_INTERFACE_MODE_MII:
78 case PHY_INTERFACE_MODE_GMII:
79 reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
85 rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
89 rc = genphy_soft_reset(phydev);
92 mutex_unlock(&phydev->lock);
97 static int vsc85xx_default_config(struct phy_device *phydev)
102 mutex_lock(&phydev->lock);
103 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
107 reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
108 reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
109 reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
110 phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
111 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
114 mutex_unlock(&phydev->lock);
119 static int vsc85xx_config_init(struct phy_device *phydev)
123 rc = vsc85xx_default_config(phydev);
127 rc = vsc85xx_mac_if_set(phydev, phydev->interface);
131 rc = genphy_config_init(phydev);
136 static int vsc85xx_ack_interrupt(struct phy_device *phydev)
140 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
141 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
143 return (rc < 0) ? rc : 0;
146 static int vsc85xx_config_intr(struct phy_device *phydev)
150 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
151 rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
152 MII_VSC85XX_INT_MASK_MASK);
154 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
157 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
163 /* Microsemi VSC85xx PHYs */
164 static struct phy_driver vsc85xx_driver[] = {
166 .phy_id = PHY_ID_VSC8531,
167 .name = "Microsemi VSC8531",
168 .phy_id_mask = 0xfffffff0,
169 .features = PHY_GBIT_FEATURES,
170 .flags = PHY_HAS_INTERRUPT,
171 .soft_reset = &genphy_soft_reset,
172 .config_init = &vsc85xx_config_init,
173 .config_aneg = &genphy_config_aneg,
174 .aneg_done = &genphy_aneg_done,
175 .read_status = &genphy_read_status,
176 .ack_interrupt = &vsc85xx_ack_interrupt,
177 .config_intr = &vsc85xx_config_intr,
178 .suspend = &genphy_suspend,
179 .resume = &genphy_resume,
182 .phy_id = PHY_ID_VSC8541,
183 .name = "Microsemi VSC8541 SyncE",
184 .phy_id_mask = 0xfffffff0,
185 .features = PHY_GBIT_FEATURES,
186 .flags = PHY_HAS_INTERRUPT,
187 .soft_reset = &genphy_soft_reset,
188 .config_init = &vsc85xx_config_init,
189 .config_aneg = &genphy_config_aneg,
190 .aneg_done = &genphy_aneg_done,
191 .read_status = &genphy_read_status,
192 .ack_interrupt = &vsc85xx_ack_interrupt,
193 .config_intr = &vsc85xx_config_intr,
194 .suspend = &genphy_suspend,
195 .resume = &genphy_resume,
200 module_phy_driver(vsc85xx_driver);
202 static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
203 { PHY_ID_VSC8531, 0xfffffff0, },
204 { PHY_ID_VSC8541, 0xfffffff0, },
208 MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
210 MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
211 MODULE_AUTHOR("Nagaraju Lakkaraju");
212 MODULE_LICENSE("Dual MIT/GPL");