Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[cascardo/linux.git] / drivers / net / phy / mscc.c
1 /*
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>
14
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
24 };
25
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
35
36 #define MII_VSC85XX_INT_MASK              25
37 #define MII_VSC85XX_INT_MASK_MASK         0xa000
38 #define MII_VSC85XX_INT_STATUS            26
39
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 */
43
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
48
49 /* Microsemi PHY ID's */
50 #define PHY_ID_VSC8531                    0x00070570
51 #define PHY_ID_VSC8541                    0x00070770
52
53 static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
54 {
55         int rc;
56
57         rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
58         return rc;
59 }
60
61 static int vsc85xx_mac_if_set(struct phy_device *phydev,
62                               phy_interface_t interface)
63 {
64         int rc;
65         u16 reg_val;
66
67         mutex_lock(&phydev->lock);
68         reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
69         reg_val &= ~(MAC_IF_SELECTION_MASK);
70         switch (interface) {
71         case PHY_INTERFACE_MODE_RGMII:
72                 reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
73                 break;
74         case PHY_INTERFACE_MODE_RMII:
75                 reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
76                 break;
77         case PHY_INTERFACE_MODE_MII:
78         case PHY_INTERFACE_MODE_GMII:
79                 reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
80                 break;
81         default:
82                 rc = -EINVAL;
83                 goto out_unlock;
84         }
85         rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
86         if (rc != 0)
87                 goto out_unlock;
88
89         rc = genphy_soft_reset(phydev);
90
91 out_unlock:
92         mutex_unlock(&phydev->lock);
93
94         return rc;
95 }
96
97 static int vsc85xx_default_config(struct phy_device *phydev)
98 {
99         int rc;
100         u16 reg_val;
101
102         mutex_lock(&phydev->lock);
103         rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
104         if (rc != 0)
105                 goto out_unlock;
106
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);
112
113 out_unlock:
114         mutex_unlock(&phydev->lock);
115
116         return rc;
117 }
118
119 static int vsc85xx_config_init(struct phy_device *phydev)
120 {
121         int rc;
122
123         rc = vsc85xx_default_config(phydev);
124         if (rc)
125                 return rc;
126
127         rc = vsc85xx_mac_if_set(phydev, phydev->interface);
128         if (rc)
129                 return rc;
130
131         rc = genphy_config_init(phydev);
132
133         return rc;
134 }
135
136 static int vsc85xx_ack_interrupt(struct phy_device *phydev)
137 {
138         int rc = 0;
139
140         if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
141                 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
142
143         return (rc < 0) ? rc : 0;
144 }
145
146 static int vsc85xx_config_intr(struct phy_device *phydev)
147 {
148         int rc;
149
150         if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
151                 rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
152                                MII_VSC85XX_INT_MASK_MASK);
153         } else {
154                 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
155                 if (rc < 0)
156                         return rc;
157                 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
158         }
159
160         return rc;
161 }
162
163 /* Microsemi VSC85xx PHYs */
164 static struct phy_driver vsc85xx_driver[] = {
165 {
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,
180 },
181 {
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,
196 }
197
198 };
199
200 module_phy_driver(vsc85xx_driver);
201
202 static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
203         { PHY_ID_VSC8531, 0xfffffff0, },
204         { PHY_ID_VSC8541, 0xfffffff0, },
205         { }
206 };
207
208 MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
209
210 MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
211 MODULE_AUTHOR("Nagaraju Lakkaraju");
212 MODULE_LICENSE("Dual MIT/GPL");