]> Git Repo - linux.git/blob - drivers/net/phy/qcom/qca83xx.c
Linux 6.14-rc3
[linux.git] / drivers / net / phy / qcom / qca83xx.c
1 // SPDX-License-Identifier: GPL-2.0+
2
3 #include <linux/phy.h>
4 #include <linux/module.h>
5
6 #include "qcom.h"
7
8 #define AT803X_DEBUG_REG_3C                     0x3C
9
10 #define AT803X_DEBUG_REG_GREEN                  0x3D
11 #define   AT803X_DEBUG_GATE_CLK_IN1000          BIT(6)
12
13 #define MDIO_AZ_DEBUG                           0x800D
14
15 #define QCA8327_A_PHY_ID                        0x004dd033
16 #define QCA8327_B_PHY_ID                        0x004dd034
17 #define QCA8337_PHY_ID                          0x004dd036
18
19 #define QCA8K_DEVFLAGS_REVISION_MASK            GENMASK(2, 0)
20
21 static struct at803x_hw_stat qca83xx_hw_stats[] = {
22         { "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
23         { "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
24         { "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
25 };
26
27 struct qca83xx_priv {
28         u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
29 };
30
31 MODULE_DESCRIPTION("Qualcomm Atheros QCA83XX PHY driver");
32 MODULE_AUTHOR("Matus Ujhelyi");
33 MODULE_AUTHOR("Christian Marangi <[email protected]>");
34 MODULE_LICENSE("GPL");
35
36 static int qca83xx_get_sset_count(struct phy_device *phydev)
37 {
38         return ARRAY_SIZE(qca83xx_hw_stats);
39 }
40
41 static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
42 {
43         int i;
44
45         for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
46                 ethtool_puts(&data, qca83xx_hw_stats[i].string);
47 }
48
49 static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
50 {
51         struct at803x_hw_stat stat = qca83xx_hw_stats[i];
52         struct qca83xx_priv *priv = phydev->priv;
53         int val;
54         u64 ret;
55
56         if (stat.access_type == MMD)
57                 val = phy_read_mmd(phydev, MDIO_MMD_PCS, stat.reg);
58         else
59                 val = phy_read(phydev, stat.reg);
60
61         if (val < 0) {
62                 ret = U64_MAX;
63         } else {
64                 val = val & stat.mask;
65                 priv->stats[i] += val;
66                 ret = priv->stats[i];
67         }
68
69         return ret;
70 }
71
72 static void qca83xx_get_stats(struct phy_device *phydev,
73                               struct ethtool_stats *stats, u64 *data)
74 {
75         int i;
76
77         for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
78                 data[i] = qca83xx_get_stat(phydev, i);
79 }
80
81 static int qca83xx_probe(struct phy_device *phydev)
82 {
83         struct device *dev = &phydev->mdio.dev;
84         struct qca83xx_priv *priv;
85
86         priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
87         if (!priv)
88                 return -ENOMEM;
89
90         phydev->priv = priv;
91
92         return 0;
93 }
94
95 static int qca83xx_config_init(struct phy_device *phydev)
96 {
97         u8 switch_revision;
98
99         switch_revision = phydev->dev_flags & QCA8K_DEVFLAGS_REVISION_MASK;
100
101         switch (switch_revision) {
102         case 1:
103                 /* For 100M waveform */
104                 at803x_debug_reg_write(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL, 0x02ea);
105                 /* Turn on Gigabit clock */
106                 at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x68a0);
107                 break;
108
109         case 2:
110                 phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, 0x0);
111                 fallthrough;
112         case 4:
113                 phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_AZ_DEBUG, 0x803f);
114                 at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_GREEN, 0x6860);
115                 at803x_debug_reg_write(phydev, AT803X_DEBUG_SYSTEM_CTRL_MODE, 0x2c46);
116                 at803x_debug_reg_write(phydev, AT803X_DEBUG_REG_3C, 0x6000);
117                 break;
118         }
119
120         /* Following original QCA sourcecode set port to prefer master */
121         phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
122
123         return 0;
124 }
125
126 static int qca8327_config_init(struct phy_device *phydev)
127 {
128         /* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
129          * Disable on init and enable only with 100m speed following
130          * qca original source code.
131          */
132         at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
133                               QCA8327_DEBUG_MANU_CTRL_EN, 0);
134
135         return qca83xx_config_init(phydev);
136 }
137
138 static void qca83xx_link_change_notify(struct phy_device *phydev)
139 {
140         /* Set DAC Amplitude adjustment to +6% for 100m on link running */
141         if (phydev->state == PHY_RUNNING) {
142                 if (phydev->speed == SPEED_100)
143                         at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
144                                               QCA8327_DEBUG_MANU_CTRL_EN,
145                                               QCA8327_DEBUG_MANU_CTRL_EN);
146         } else {
147                 /* Reset DAC Amplitude adjustment */
148                 at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
149                                       QCA8327_DEBUG_MANU_CTRL_EN, 0);
150         }
151 }
152
153 static int qca83xx_resume(struct phy_device *phydev)
154 {
155         int ret, val;
156
157         /* Skip reset if not suspended */
158         if (!phydev->suspended)
159                 return 0;
160
161         /* Reinit the port, reset values set by suspend */
162         qca83xx_config_init(phydev);
163
164         /* Reset the port on port resume */
165         phy_set_bits(phydev, MII_BMCR, BMCR_RESET | BMCR_ANENABLE);
166
167         /* On resume from suspend the switch execute a reset and
168          * restart auto-negotiation. Wait for reset to complete.
169          */
170         ret = phy_read_poll_timeout(phydev, MII_BMCR, val, !(val & BMCR_RESET),
171                                     50000, 600000, true);
172         if (ret)
173                 return ret;
174
175         usleep_range(1000, 2000);
176
177         return 0;
178 }
179
180 static int qca83xx_suspend(struct phy_device *phydev)
181 {
182         at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
183                               AT803X_DEBUG_GATE_CLK_IN1000, 0);
184
185         at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_HIB_CTRL,
186                               AT803X_DEBUG_HIB_CTRL_EN_ANY_CHANGE |
187                               AT803X_DEBUG_HIB_CTRL_SEL_RST_80U, 0);
188
189         return 0;
190 }
191
192 static int qca8337_suspend(struct phy_device *phydev)
193 {
194         /* Only QCA8337 support actual suspend. */
195         genphy_suspend(phydev);
196
197         return qca83xx_suspend(phydev);
198 }
199
200 static int qca8327_suspend(struct phy_device *phydev)
201 {
202         u16 mask = 0;
203
204         /* QCA8327 cause port unreliability when phy suspend
205          * is set.
206          */
207         mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
208         phy_modify(phydev, MII_BMCR, mask, 0);
209
210         return qca83xx_suspend(phydev);
211 }
212
213 static struct phy_driver qca83xx_driver[] = {
214 {
215         /* QCA8337 */
216         PHY_ID_MATCH_EXACT(QCA8337_PHY_ID),
217         .name                   = "Qualcomm Atheros 8337 internal PHY",
218         /* PHY_GBIT_FEATURES */
219         .probe                  = qca83xx_probe,
220         .flags                  = PHY_IS_INTERNAL,
221         .config_init            = qca83xx_config_init,
222         .soft_reset             = genphy_soft_reset,
223         .get_sset_count         = qca83xx_get_sset_count,
224         .get_strings            = qca83xx_get_strings,
225         .get_stats              = qca83xx_get_stats,
226         .suspend                = qca8337_suspend,
227         .resume                 = qca83xx_resume,
228 }, {
229         /* QCA8327-A from switch QCA8327-AL1A */
230         PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID),
231         .name                   = "Qualcomm Atheros 8327-A internal PHY",
232         /* PHY_GBIT_FEATURES */
233         .link_change_notify     = qca83xx_link_change_notify,
234         .probe                  = qca83xx_probe,
235         .flags                  = PHY_IS_INTERNAL,
236         .config_init            = qca8327_config_init,
237         .soft_reset             = genphy_soft_reset,
238         .get_sset_count         = qca83xx_get_sset_count,
239         .get_strings            = qca83xx_get_strings,
240         .get_stats              = qca83xx_get_stats,
241         .suspend                = qca8327_suspend,
242         .resume                 = qca83xx_resume,
243 }, {
244         /* QCA8327-B from switch QCA8327-BL1A */
245         PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID),
246         .name                   = "Qualcomm Atheros 8327-B internal PHY",
247         /* PHY_GBIT_FEATURES */
248         .link_change_notify     = qca83xx_link_change_notify,
249         .probe                  = qca83xx_probe,
250         .flags                  = PHY_IS_INTERNAL,
251         .config_init            = qca8327_config_init,
252         .soft_reset             = genphy_soft_reset,
253         .get_sset_count         = qca83xx_get_sset_count,
254         .get_strings            = qca83xx_get_strings,
255         .get_stats              = qca83xx_get_stats,
256         .suspend                = qca8327_suspend,
257         .resume                 = qca83xx_resume,
258 }, };
259
260 module_phy_driver(qca83xx_driver);
261
262 static const struct mdio_device_id __maybe_unused qca83xx_tbl[] = {
263         { PHY_ID_MATCH_EXACT(QCA8337_PHY_ID) },
264         { PHY_ID_MATCH_EXACT(QCA8327_A_PHY_ID) },
265         { PHY_ID_MATCH_EXACT(QCA8327_B_PHY_ID) },
266         { }
267 };
268
269 MODULE_DEVICE_TABLE(mdio, qca83xx_tbl);
This page took 0.046704 seconds and 4 git commands to generate.