Merge tag 'ata-5.19-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/dlemoal...
[linux-2.6-microblaze.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 <kernel@pengutronix.de>
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 static int dp83td510_config_intr(struct phy_device *phydev)
31 {
32         int ret;
33
34         if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
35                 /* Clear any pending interrupts */
36                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
37                                     0x0);
38                 if (ret)
39                         return ret;
40
41                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
42                                     DP83TD510E_INTERRUPT_REG_1,
43                                     DP83TD510E_INT1_LINK_EN);
44                 if (ret)
45                         return ret;
46
47                 ret = phy_set_bits_mmd(phydev, MDIO_MMD_VEND2,
48                                        DP83TD510E_GEN_CFG,
49                                        DP83TD510E_GENCFG_INT_POLARITY |
50                                        DP83TD510E_GENCFG_INT_EN |
51                                        DP83TD510E_GENCFG_INT_OE);
52         } else {
53                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2,
54                                     DP83TD510E_INTERRUPT_REG_1, 0x0);
55                 if (ret)
56                         return ret;
57
58                 ret = phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2,
59                                          DP83TD510E_GEN_CFG,
60                                          DP83TD510E_GENCFG_INT_EN);
61                 if (ret)
62                         return ret;
63
64                 /* Clear any pending interrupts */
65                 ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS,
66                                     0x0);
67         }
68
69         return ret;
70 }
71
72 static irqreturn_t dp83td510_handle_interrupt(struct phy_device *phydev)
73 {
74         int  ret;
75
76         ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_PHY_STS);
77         if (ret < 0) {
78                 phy_error(phydev);
79                 return IRQ_NONE;
80         } else if (!(ret & DP83TD510E_STS_MII_INT)) {
81                 return IRQ_NONE;
82         }
83
84         /* Read the current enabled interrupts */
85         ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, DP83TD510E_INTERRUPT_REG_1);
86         if (ret < 0) {
87                 phy_error(phydev);
88                 return IRQ_NONE;
89         } else if (!(ret & DP83TD510E_INT1_LINK_EN) ||
90                    !(ret & DP83TD510E_INT1_LINK)) {
91                 return IRQ_NONE;
92         }
93
94         phy_trigger_machine(phydev);
95
96         return IRQ_HANDLED;
97 }
98
99 static int dp83td510_read_status(struct phy_device *phydev)
100 {
101         u16 phy_sts;
102         int ret;
103
104         phydev->speed = SPEED_UNKNOWN;
105         phydev->duplex = DUPLEX_UNKNOWN;
106         phydev->pause = 0;
107         phydev->asym_pause = 0;
108         linkmode_zero(phydev->lp_advertising);
109
110         phy_sts = phy_read(phydev, DP83TD510E_PHY_STS);
111
112         phydev->link = !!(phy_sts & DP83TD510E_LINK_STATUS);
113         if (phydev->link) {
114                 /* This PHY supports only one link mode: 10BaseT1L_Full */
115                 phydev->duplex = DUPLEX_FULL;
116                 phydev->speed = SPEED_10;
117
118                 if (phydev->autoneg == AUTONEG_ENABLE) {
119                         ret = genphy_c45_read_lpa(phydev);
120                         if (ret)
121                                 return ret;
122
123                         phy_resolve_aneg_linkmode(phydev);
124                 }
125         }
126
127         if (phydev->autoneg == AUTONEG_ENABLE) {
128                 ret = genphy_c45_baset1_read_status(phydev);
129                 if (ret < 0)
130                         return ret;
131
132                 ret = phy_read_mmd(phydev, MDIO_MMD_VEND2,
133                                    DP83TD510E_AN_STAT_1);
134                 if (ret < 0)
135                         return ret;
136
137                 if (ret & DP83TD510E_MASTER_SLAVE_RESOL_FAIL)
138                         phydev->master_slave_state = MASTER_SLAVE_STATE_ERR;
139         } else {
140                 return genphy_c45_pma_baset1_read_master_slave(phydev);
141         }
142
143         return 0;
144 }
145
146 static int dp83td510_config_aneg(struct phy_device *phydev)
147 {
148         bool changed = false;
149         int ret;
150
151         ret = genphy_c45_pma_baset1_setup_master_slave(phydev);
152         if (ret < 0)
153                 return ret;
154
155         if (phydev->autoneg == AUTONEG_DISABLE)
156                 return genphy_c45_an_disable_aneg(phydev);
157
158         ret = genphy_c45_an_config_aneg(phydev);
159         if (ret < 0)
160                 return ret;
161         if (ret > 0)
162                 changed = true;
163
164         return genphy_c45_check_and_restart_aneg(phydev, changed);
165 }
166
167 static int dp83td510_get_features(struct phy_device *phydev)
168 {
169         /* This PHY can't respond on MDIO bus if no RMII clock is enabled.
170          * In case RMII mode is used (most meaningful mode for this PHY) and
171          * the PHY do not have own XTAL, and CLK providing MAC is not probed,
172          * we won't be able to read all needed ability registers.
173          * So provide it manually.
174          */
175
176         linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
177         linkmode_set_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, phydev->supported);
178         linkmode_set_bit(ETHTOOL_LINK_MODE_Pause_BIT, phydev->supported);
179         linkmode_set_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
180                          phydev->supported);
181
182         return 0;
183 }
184
185 static struct phy_driver dp83td510_driver[] = {
186 {
187         PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID),
188         .name           = "TI DP83TD510E",
189
190         .config_aneg    = dp83td510_config_aneg,
191         .read_status    = dp83td510_read_status,
192         .get_features   = dp83td510_get_features,
193         .config_intr    = dp83td510_config_intr,
194         .handle_interrupt = dp83td510_handle_interrupt,
195
196         .suspend        = genphy_suspend,
197         .resume         = genphy_resume,
198 } };
199 module_phy_driver(dp83td510_driver);
200
201 static struct mdio_device_id __maybe_unused dp83td510_tbl[] = {
202         { PHY_ID_MATCH_MODEL(DP83TD510E_PHY_ID) },
203         { }
204 };
205 MODULE_DEVICE_TABLE(mdio, dp83td510_tbl);
206
207 MODULE_DESCRIPTION("Texas Instruments DP83TD510E PHY driver");
208 MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>");
209 MODULE_LICENSE("GPL v2");