]> Git Repo - J-linux.git/blob - drivers/net/phy/dp83td510.c
Merge tag 'amd-drm-next-6.5-2023-06-09' of https://gitlab.freedesktop.org/agd5f/linux...
[J-linux.git] / drivers / net / phy / dp83td510.c
1 // SPDX-License-Identifier: GPL-2.0
2 /* Driver for the Texas Instruments DP83TD510 PHY
3  * Copyright (c) 2022 Pengutronix, Oleksij Rempel <[email protected]>
4  */
5
6 #include <linux/bitfield.h>
7 #include <linux/kernel.h>
8 #include <linux/module.h>
9 #include <linux/phy.h>
10
11 #define DP83TD510E_PHY_ID                       0x20000181
12
13 /* MDIO_MMD_VEND2 registers */
14 #define DP83TD510E_PHY_STS                      0x10
15 #define DP83TD510E_STS_MII_INT                  BIT(7)
16 #define DP83TD510E_LINK_STATUS                  BIT(0)
17
18 #define DP83TD510E_GEN_CFG                      0x11
19 #define DP83TD510E_GENCFG_INT_POLARITY          BIT(3)
20 #define DP83TD510E_GENCFG_INT_EN                BIT(1)
21 #define DP83TD510E_GENCFG_INT_OE                BIT(0)
22
23 #define DP83TD510E_INTERRUPT_REG_1              0x12
24 #define DP83TD510E_INT1_LINK                    BIT(13)
25 #define DP83TD510E_INT1_LINK_EN                 BIT(5)
26
27 #define DP83TD510E_AN_STAT_1                    0x60c
28 #define DP83TD510E_MASTER_SLAVE_RESOL_FAIL      BIT(15)
29
30 #define DP83TD510E_MSE_DETECT                   0xa85
31
32 #define DP83TD510_SQI_MAX       7
33
34 /* Register values are converted to SNR(dB) as suggested by
35  * "Application Report - DP83TD510E Cable Diagnostics Toolkit":
36  * SNR(dB) = -10 * log10 (VAL/2^17) - 1.76 dB.
37  * SQI ranges are implemented according to "OPEN ALLIANCE - Advanced diagnostic
38  * features for 100BASE-T1 automotive Ethernet PHYs"
39  */
40 static const u16 dp83td510_mse_sqi_map[] = {
41         0x0569, /* < 18dB */
42         0x044c, /* 18dB =< SNR < 19dB */
43         0x0369, /* 19dB =< SNR < 20dB */
44         0x02b6, /* 20dB =< SNR < 21dB */
45         0x0227, /* 21dB =< SNR < 22dB */
46         0x01b6, /* 22dB =< SNR < 23dB */
47         0x015b, /* 23dB =< SNR < 24dB */
48         0x0000  /* 24dB =< SNR */
49 };
50
51 static int dp83td510_config_intr(struct phy_device *phydev)
52 {
53         int ret;
54
55         if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
56                 /* Clear any pending interrupts */
57                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
58                                     0x0);
59                 if (ret)
60                         return ret;
61
62                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
63                                     DP83TD510E_INTERRUPT_REG_1,
64                                     DP83TD510E_INT1_LINK_EN);
65                 if (ret)
66                         return ret;
67
68                 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
69                                        DP83TD510E_GEN_CFG,
70                                        DP83TD510E_GENCFG_INT_POLARITY |
71                                        DP83TD510E_GENCFG_INT_EN |
72                                        DP83TD510E_GENCFG_INT_OE);
73         } else {
74                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
75                                     DP83TD510E_INTERRUPT_REG_1, 0x0);
76                 if (ret)
77                         return ret;
78
79                 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
80                                          DP83TD510E_GEN_CFG,
81                                          DP83TD510E_GENCFG_INT_EN);
82                 if (ret)
83                         return ret;
84
85                 /* Clear any pending interrupts */
86                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
87                                     0x0);
88         }
89
90         return ret;
91 }
92
93 static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
94 {
95         int  ret;
96
97         ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
98         if (ret < 0) {
99                 phy_error(phydev);
100                 return IRQ_NONE;
101         } else if (!(ret & DP83TD510E_STS_MII_INT)) {
102                 return IRQ_NONE;
103         }
104
105         /* Read the current enabled interrupts */
106         ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
107         if (ret < 0) {
108                 phy_error(phydev);
109                 return IRQ_NONE;
110         } else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
111                    !(ret & DP83TD510E_INT1_LINK)) {
112                 return IRQ_NONE;
113         }
114
115         phy_trigger_machine(phydev);
116
117         return IRQ_HANDLED;
118 }
119
120 static int dp83td510_read_status(struct phy_device *phydev)
121 {
122         u16 phy_sts;
123         int ret;
124
125         phydev->speed = SPEED_UNKNOWN;
126         phydev->duplex = DUPLEX_UNKNOWN;
127         phydev->pause = 0;
128         phydev->asym_pause = 0;
129         linkmode_zero(phydev->lp_advertising);
130
131         phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
132
133         phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
134         if (phydev->link) {
135                 /* This PHY supports only one link mode: 10BaseT1L_Full */
136                 phydev->duplex = DUPLEX_FULL;
137                 phydev->speed = SPEED_10;
138
139                 if (phydev->autoneg == AUTONEG_ENABLE) {
140                         ret = genphy_c45_read_lpa(phydev);
141                         if (ret)
142                                 return ret;
143
144                         phy_resolve_aneg_linkmode(phydev);
145                 }
146         }
147
148         if (phydev->autoneg == AUTONEG_ENABLE) {
149                 ret = genphy_c45_baset1_read_status(phydev);
150                 if (ret < 0)
151                         return ret;
152
153                 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
154                                    DP83TD510E_AN_STAT_1);
155                 if (ret < 0)
156                         return ret;
157
158                 if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
159                         phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
160         } else {
161                 return genphy_c45_pma_baset1_read_master_slave(phydev);
162         }
163
164         return 0;
165 }
166
167 static int dp83td510_config_aneg(struct phy_device *phydev)
168 {
169         bool changed = false;
170         int ret;
171
172         ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
173         if (ret < 0)
174                 return ret;
175
176         if (phydev->autoneg == AUTONEG_DISABLE)
177                 return genphy_c45_an_disable_aneg(phydev);
178
179         ret = genphy_c45_an_config_aneg(phydev);
180         if (ret < 0)
181                 return ret;
182         if (ret > 0)
183                 changed = true;
184
185         return genphy_c45_check_and_restart_aneg(phydev, changed);
186 }
187
188 static int dp83td510_get_sqi(struct phy_device *phydev)
189 {
190         int sqi, ret;
191         u16 mse_val;
192
193         if (!phydev->link)
194                 return 0;
195
196         ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_MSE_DETECT);
197         if (ret < 0)
198                 return ret;
199
200         mse_val = 0xFFFF & ret;
201         for (sqi = 0; sqi < ARRAY_SIZE(dp83td510_mse_sqi_map); sqi++) {
202                 if (mse_val >= dp83td510_mse_sqi_map[sqi])
203                         return sqi;
204         }
205
206         return -EINVAL;
207 }
208
209 static int dp83td510_get_sqi_max(struct phy_device *phydev)
210 {
211         return DP83TD510_SQI_MAX;
212 }
213
214 static int dp83td510_get_features(struct phy_device *phydev)
215 {
216         /* This PHY can't respond on MDIO bus if no RMII clock is enabled.
217          * In case RMII mode is used (most meaningful mode for this PHY) and
218          * the PHY do not have own XTAL, and CLK providing MAC is not probed,
219          * we won't be able to read all needed ability registers.
220          * So provide it manually.
221          */
222
223         linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
224         linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
225         linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
226         linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
227                          phydev->supported);
228
229         return 0;
230 }
231
232 static struct phy_driver dp83td510_driver[] = {
233 {
234         PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
235         .name           = "TI DP83TD510E",
236
237         .config_aneg    = dp83td510_config_aneg,
238         .read_status    = dp83td510_read_status,
239         .get_features   = dp83td510_get_features,
240         .config_intr    = dp83td510_config_intr,
241         .handle_interrupt = dp83td510_handle_interrupt,
242         .get_sqi        = dp83td510_get_sqi,
243         .get_sqi_max    = dp83td510_get_sqi_max,
244
245         .suspend        = genphy_suspend,
246         .resume         = genphy_resume,
247 } };
248 module_phy_driver(dp83td510_driver);
249
250 static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
251         { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
252         { }
253 };
254 MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
255
256 MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
257 MODULE_AUTHOR("Oleksij Rempel <[email protected]>");
258 MODULE_LICENSE("GPL v2");
This page took 0.038029 seconds and 4 git commands to generate.