GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / net / phy / nxp-cbtx.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Driver for 100BASE-TX PHY embedded into NXP SJA1110 switch
3  *
4  * Copyright 2022-2023 NXP
5  */
6
7 #include <linux/kernel.h>
8 #include <linux/mii.h>
9 #include <linux/module.h>
10 #include <linux/phy.h>
11
12 #define PHY_ID_CBTX_SJA1110                     0x001bb020
13
14 /* Registers */
15 #define  CBTX_MODE_CTRL_STAT                    0x11
16 #define  CBTX_PDOWN_CTRL                        0x18
17 #define  CBTX_RX_ERR_COUNTER                    0x1a
18 #define  CBTX_IRQ_STAT                          0x1d
19 #define  CBTX_IRQ_ENABLE                        0x1e
20
21 /* Fields */
22 #define CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN        BIT(7)
23 #define CBTX_MODE_CTRL_STAT_MDIX_MODE           BIT(6)
24
25 #define CBTX_PDOWN_CTL_TRUE_PDOWN               BIT(0)
26
27 #define CBTX_IRQ_ENERGYON                       BIT(7)
28 #define CBTX_IRQ_AN_COMPLETE                    BIT(6)
29 #define CBTX_IRQ_REM_FAULT                      BIT(5)
30 #define CBTX_IRQ_LINK_DOWN                      BIT(4)
31 #define CBTX_IRQ_AN_LP_ACK                      BIT(3)
32 #define CBTX_IRQ_PARALLEL_DETECT_FAULT          BIT(2)
33 #define CBTX_IRQ_AN_PAGE_RECV                   BIT(1)
34
35 static int cbtx_soft_reset(struct phy_device *phydev)
36 {
37         int ret;
38
39         /* Can't soft reset unless we remove PHY from true power down mode */
40         ret = phy_clear_bits(phydev, CBTX_PDOWN_CTRL,
41                              CBTX_PDOWN_CTL_TRUE_PDOWN);
42         if (ret)
43                 return ret;
44
45         return genphy_soft_reset(phydev);
46 }
47
48 static int cbtx_config_init(struct phy_device *phydev)
49 {
50         /* Wait for cbtx_config_aneg() to kick in and apply this */
51         phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
52
53         return 0;
54 }
55
56 static int cbtx_mdix_status(struct phy_device *phydev)
57 {
58         int ret;
59
60         ret = phy_read(phydev, CBTX_MODE_CTRL_STAT);
61         if (ret < 0)
62                 return ret;
63
64         if (ret & CBTX_MODE_CTRL_STAT_MDIX_MODE)
65                 phydev->mdix = ETH_TP_MDI_X;
66         else
67                 phydev->mdix = ETH_TP_MDI;
68
69         return 0;
70 }
71
72 static int cbtx_read_status(struct phy_device *phydev)
73 {
74         int ret;
75
76         ret = cbtx_mdix_status(phydev);
77         if (ret)
78                 return ret;
79
80         return genphy_read_status(phydev);
81 }
82
83 static int cbtx_mdix_config(struct phy_device *phydev)
84 {
85         int ret;
86
87         switch (phydev->mdix_ctrl) {
88         case ETH_TP_MDI_AUTO:
89                 return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,
90                                     CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
91         case ETH_TP_MDI:
92                 ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
93                                      CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
94                 if (ret)
95                         return ret;
96
97                 return phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
98                                       CBTX_MODE_CTRL_STAT_MDIX_MODE);
99         case ETH_TP_MDI_X:
100                 ret = phy_clear_bits(phydev, CBTX_MODE_CTRL_STAT,
101                                      CBTX_MODE_CTRL_STAT_AUTO_MDIX_EN);
102                 if (ret)
103                         return ret;
104
105                 return phy_set_bits(phydev, CBTX_MODE_CTRL_STAT,
106                                     CBTX_MODE_CTRL_STAT_MDIX_MODE);
107         }
108
109         return 0;
110 }
111
112 static int cbtx_config_aneg(struct phy_device *phydev)
113 {
114         int ret;
115
116         ret = cbtx_mdix_config(phydev);
117         if (ret)
118                 return ret;
119
120         return genphy_config_aneg(phydev);
121 }
122
123 static int cbtx_ack_interrupts(struct phy_device *phydev)
124 {
125         return phy_read(phydev, CBTX_IRQ_STAT);
126 }
127
128 static int cbtx_config_intr(struct phy_device *phydev)
129 {
130         int ret;
131
132         if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
133                 ret = cbtx_ack_interrupts(phydev);
134                 if (ret < 0)
135                         return ret;
136
137                 ret = phy_write(phydev, CBTX_IRQ_ENABLE, CBTX_IRQ_LINK_DOWN |
138                                 CBTX_IRQ_AN_COMPLETE | CBTX_IRQ_ENERGYON);
139                 if (ret)
140                         return ret;
141         } else {
142                 ret = phy_write(phydev, CBTX_IRQ_ENABLE, 0);
143                 if (ret)
144                         return ret;
145
146                 ret = cbtx_ack_interrupts(phydev);
147                 if (ret < 0)
148                         return ret;
149         }
150
151         return 0;
152 }
153
154 static irqreturn_t cbtx_handle_interrupt(struct phy_device *phydev)
155 {
156         int irq_stat, irq_enabled;
157
158         irq_stat = cbtx_ack_interrupts(phydev);
159         if (irq_stat < 0) {
160                 phy_error(phydev);
161                 return IRQ_NONE;
162         }
163
164         irq_enabled = phy_read(phydev, CBTX_IRQ_ENABLE);
165         if (irq_enabled < 0) {
166                 phy_error(phydev);
167                 return IRQ_NONE;
168         }
169
170         if (!(irq_enabled & irq_stat))
171                 return IRQ_NONE;
172
173         phy_trigger_machine(phydev);
174
175         return IRQ_HANDLED;
176 }
177
178 static int cbtx_get_sset_count(struct phy_device *phydev)
179 {
180         return 1;
181 }
182
183 static void cbtx_get_strings(struct phy_device *phydev, u8 *data)
184 {
185         strncpy(data, "100btx_rx_err", ETH_GSTRING_LEN);
186 }
187
188 static void cbtx_get_stats(struct phy_device *phydev,
189                            struct ethtool_stats *stats, u64 *data)
190 {
191         int ret;
192
193         ret = phy_read(phydev, CBTX_RX_ERR_COUNTER);
194         data[0] = (ret < 0) ? U64_MAX : ret;
195 }
196
197 static struct phy_driver cbtx_driver[] = {
198         {
199                 PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110),
200                 .name                   = "NXP CBTX (SJA1110)",
201                 /* PHY_BASIC_FEATURES */
202                 .soft_reset             = cbtx_soft_reset,
203                 .config_init            = cbtx_config_init,
204                 .suspend                = genphy_suspend,
205                 .resume                 = genphy_resume,
206                 .config_intr            = cbtx_config_intr,
207                 .handle_interrupt       = cbtx_handle_interrupt,
208                 .read_status            = cbtx_read_status,
209                 .config_aneg            = cbtx_config_aneg,
210                 .get_sset_count         = cbtx_get_sset_count,
211                 .get_strings            = cbtx_get_strings,
212                 .get_stats              = cbtx_get_stats,
213         },
214 };
215
216 module_phy_driver(cbtx_driver);
217
218 static struct mdio_device_id __maybe_unused cbtx_tbl[] = {
219         { PHY_ID_MATCH_MODEL(PHY_ID_CBTX_SJA1110) },
220         { },
221 };
222
223 MODULE_DEVICE_TABLE(mdio, cbtx_tbl);
224
225 MODULE_AUTHOR("Vladimir Oltean <vladimir.oltean@nxp.com>");
226 MODULE_DESCRIPTION("NXP CBTX PHY driver");
227 MODULE_LICENSE("GPL");