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 #define MII_VSC85XX_INT_MASK 25
27 #define MII_VSC85XX_INT_MASK_MASK 0xa000
28 #define MII_VSC85XX_INT_STATUS 26
30 #define MSCC_EXT_PAGE_ACCESS 31
31 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
32 #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
34 /* Extended Page 2 Registers */
35 #define MSCC_PHY_RGMII_CNTL 20
36 #define RGMII_RX_CLK_DELAY_MASK 0x0070
37 #define RGMII_RX_CLK_DELAY_POS 4
39 /* Microsemi PHY ID's */
40 #define PHY_ID_VSC8531 0x00070570
41 #define PHY_ID_VSC8541 0x00070770
43 static int vsc85xx_phy_page_set(struct phy_device
*phydev
, u8 page
)
47 rc
= phy_write(phydev
, MSCC_EXT_PAGE_ACCESS
, page
);
51 static int vsc85xx_default_config(struct phy_device
*phydev
)
56 mutex_lock(&phydev
->lock
);
57 rc
= vsc85xx_phy_page_set(phydev
, MSCC_PHY_PAGE_EXTENDED_2
);
61 reg_val
= phy_read(phydev
, MSCC_PHY_RGMII_CNTL
);
62 reg_val
&= ~(RGMII_RX_CLK_DELAY_MASK
);
63 reg_val
|= (RGMII_RX_CLK_DELAY_1_1_NS
<< RGMII_RX_CLK_DELAY_POS
);
64 phy_write(phydev
, MSCC_PHY_RGMII_CNTL
, reg_val
);
65 rc
= vsc85xx_phy_page_set(phydev
, MSCC_PHY_PAGE_STANDARD
);
68 mutex_unlock(&phydev
->lock
);
73 static int vsc85xx_config_init(struct phy_device
*phydev
)
77 rc
= vsc85xx_default_config(phydev
);
80 rc
= genphy_config_init(phydev
);
85 static int vsc85xx_ack_interrupt(struct phy_device
*phydev
)
89 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
)
90 rc
= phy_read(phydev
, MII_VSC85XX_INT_STATUS
);
92 return (rc
< 0) ? rc
: 0;
95 static int vsc85xx_config_intr(struct phy_device
*phydev
)
99 if (phydev
->interrupts
== PHY_INTERRUPT_ENABLED
) {
100 rc
= phy_write(phydev
, MII_VSC85XX_INT_MASK
,
101 MII_VSC85XX_INT_MASK_MASK
);
103 rc
= phy_write(phydev
, MII_VSC85XX_INT_MASK
, 0);
106 rc
= phy_read(phydev
, MII_VSC85XX_INT_STATUS
);
112 /* Microsemi VSC85xx PHYs */
113 static struct phy_driver vsc85xx_driver
[] = {
115 .phy_id
= PHY_ID_VSC8531
,
116 .name
= "Microsemi VSC8531",
117 .phy_id_mask
= 0xfffffff0,
118 .features
= PHY_GBIT_FEATURES
,
119 .flags
= PHY_HAS_INTERRUPT
,
120 .soft_reset
= &genphy_soft_reset
,
121 .config_init
= &vsc85xx_config_init
,
122 .config_aneg
= &genphy_config_aneg
,
123 .aneg_done
= &genphy_aneg_done
,
124 .read_status
= &genphy_read_status
,
125 .ack_interrupt
= &vsc85xx_ack_interrupt
,
126 .config_intr
= &vsc85xx_config_intr
,
127 .suspend
= &genphy_suspend
,
128 .resume
= &genphy_resume
,
131 .phy_id
= PHY_ID_VSC8541
,
132 .name
= "Microsemi VSC8541 SyncE",
133 .phy_id_mask
= 0xfffffff0,
134 .features
= PHY_GBIT_FEATURES
,
135 .flags
= PHY_HAS_INTERRUPT
,
136 .soft_reset
= &genphy_soft_reset
,
137 .config_init
= &vsc85xx_config_init
,
138 .config_aneg
= &genphy_config_aneg
,
139 .aneg_done
= &genphy_aneg_done
,
140 .read_status
= &genphy_read_status
,
141 .ack_interrupt
= &vsc85xx_ack_interrupt
,
142 .config_intr
= &vsc85xx_config_intr
,
143 .suspend
= &genphy_suspend
,
144 .resume
= &genphy_resume
,
149 module_phy_driver(vsc85xx_driver
);
151 static struct mdio_device_id __maybe_unused vsc85xx_tbl
[] = {
152 { PHY_ID_VSC8531
, 0xfffffff0, },
153 { PHY_ID_VSC8541
, 0xfffffff0, },
157 MODULE_DEVICE_TABLE(mdio
, vsc85xx_tbl
);
159 MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
160 MODULE_AUTHOR("Nagaraju Lakkaraju");
161 MODULE_LICENSE("Dual MIT/GPL");