]> Git Repo - J-linux.git/blob - drivers/net/dsa/mt7530-mdio.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / net / dsa / mt7530-mdio.c
1 // SPDX-License-Identifier: GPL-2.0-only
2
3 #include <linux/gpio/consumer.h>
4 #include <linux/mdio.h>
5 #include <linux/module.h>
6 #include <linux/pcs/pcs-mtk-lynxi.h>
7 #include <linux/of_irq.h>
8 #include <linux/of_mdio.h>
9 #include <linux/of_net.h>
10 #include <linux/of_platform.h>
11 #include <linux/regmap.h>
12 #include <linux/reset.h>
13 #include <linux/regulator/consumer.h>
14 #include <net/dsa.h>
15
16 #include "mt7530.h"
17
18 static int
19 mt7530_regmap_write(void *context, unsigned int reg, unsigned int val)
20 {
21         struct mt7530_priv *priv = context;
22         struct mii_bus *bus = priv->bus;
23         u16 page, r, lo, hi;
24         int ret;
25
26         page = (reg >> 6) & 0x3ff;
27         r  = (reg >> 2) & 0xf;
28         lo = val & 0xffff;
29         hi = val >> 16;
30
31         ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
32         if (ret < 0)
33                 return ret;
34
35         ret = bus->write(bus, priv->mdiodev->addr, r, lo);
36         if (ret < 0)
37                 return ret;
38
39         ret = bus->write(bus, priv->mdiodev->addr, 0x10, hi);
40         return ret;
41 }
42
43 static int
44 mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val)
45 {
46         struct mt7530_priv *priv = context;
47         struct mii_bus *bus = priv->bus;
48         u16 page, r, lo, hi;
49         int ret;
50
51         page = (reg >> 6) & 0x3ff;
52         r = (reg >> 2) & 0xf;
53
54         ret = bus->write(bus, priv->mdiodev->addr, 0x1f, page);
55         if (ret < 0)
56                 return ret;
57
58         lo = bus->read(bus, priv->mdiodev->addr, r);
59         hi = bus->read(bus, priv->mdiodev->addr, 0x10);
60
61         *val = (hi << 16) | (lo & 0xffff);
62
63         return 0;
64 }
65
66 static void
67 mt7530_mdio_regmap_lock(void *mdio_lock)
68 {
69         mutex_lock_nested(mdio_lock, MDIO_MUTEX_NESTED);
70 }
71
72 static void
73 mt7530_mdio_regmap_unlock(void *mdio_lock)
74 {
75         mutex_unlock(mdio_lock);
76 }
77
78 static const struct regmap_bus mt7530_regmap_bus = {
79         .reg_write = mt7530_regmap_write,
80         .reg_read = mt7530_regmap_read,
81 };
82
83 static int
84 mt7531_create_sgmii(struct mt7530_priv *priv)
85 {
86         struct regmap_config *mt7531_pcs_config[2] = {};
87         struct phylink_pcs *pcs;
88         struct regmap *regmap;
89         int i, ret = 0;
90
91         for (i = priv->p5_sgmii ? 0 : 1; i < 2; i++) {
92                 mt7531_pcs_config[i] = devm_kzalloc(priv->dev,
93                                                     sizeof(struct regmap_config),
94                                                     GFP_KERNEL);
95                 if (!mt7531_pcs_config[i]) {
96                         ret = -ENOMEM;
97                         break;
98                 }
99
100                 mt7531_pcs_config[i]->name = i ? "port6" : "port5";
101                 mt7531_pcs_config[i]->reg_bits = 16;
102                 mt7531_pcs_config[i]->val_bits = 32;
103                 mt7531_pcs_config[i]->reg_stride = 4;
104                 mt7531_pcs_config[i]->reg_base = MT7531_SGMII_REG_BASE(5 + i);
105                 mt7531_pcs_config[i]->max_register = 0x17c;
106                 mt7531_pcs_config[i]->lock = mt7530_mdio_regmap_lock;
107                 mt7531_pcs_config[i]->unlock = mt7530_mdio_regmap_unlock;
108                 mt7531_pcs_config[i]->lock_arg = &priv->bus->mdio_lock;
109
110                 regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
111                                           mt7531_pcs_config[i]);
112                 if (IS_ERR(regmap)) {
113                         ret = PTR_ERR(regmap);
114                         break;
115                 }
116                 pcs = mtk_pcs_lynxi_create(priv->dev, regmap,
117                                            MT7531_PHYA_CTRL_SIGNAL3, 0);
118                 if (!pcs) {
119                         ret = -ENXIO;
120                         break;
121                 }
122                 priv->ports[5 + i].sgmii_pcs = pcs;
123         }
124
125         if (ret && i)
126                 mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs);
127
128         return ret;
129 }
130
131 static const struct of_device_id mt7530_of_match[] = {
132         { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], },
133         { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], },
134         { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], },
135         { /* sentinel */ },
136 };
137 MODULE_DEVICE_TABLE(of, mt7530_of_match);
138
139 static int
140 mt7530_probe(struct mdio_device *mdiodev)
141 {
142         static struct regmap_config *regmap_config;
143         struct mt7530_priv *priv;
144         struct device_node *dn;
145         int ret;
146
147         dn = mdiodev->dev.of_node;
148
149         priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL);
150         if (!priv)
151                 return -ENOMEM;
152
153         priv->bus = mdiodev->bus;
154         priv->dev = &mdiodev->dev;
155         priv->mdiodev = mdiodev;
156
157         ret = mt7530_probe_common(priv);
158         if (ret)
159                 return ret;
160
161         /* Use medatek,mcm property to distinguish hardware type that would
162          * cause a little bit differences on power-on sequence.
163          * Not MCM that indicates switch works as the remote standalone
164          * integrated circuit so the GPIO pin would be used to complete
165          * the reset, otherwise memory-mapped register accessing used
166          * through syscon provides in the case of MCM.
167          */
168         priv->mcm = of_property_read_bool(dn, "mediatek,mcm");
169         if (priv->mcm) {
170                 dev_info(&mdiodev->dev, "MT7530 adapts as multi-chip module\n");
171
172                 priv->rstc = devm_reset_control_get(&mdiodev->dev, "mcm");
173                 if (IS_ERR(priv->rstc)) {
174                         dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
175                         return PTR_ERR(priv->rstc);
176                 }
177         } else {
178                 priv->reset = devm_gpiod_get_optional(&mdiodev->dev, "reset",
179                                                       GPIOD_OUT_LOW);
180                 if (IS_ERR(priv->reset)) {
181                         dev_err(&mdiodev->dev, "Couldn't get our reset line\n");
182                         return PTR_ERR(priv->reset);
183                 }
184         }
185
186         if (priv->id == ID_MT7530) {
187                 priv->core_pwr = devm_regulator_get(&mdiodev->dev, "core");
188                 if (IS_ERR(priv->core_pwr))
189                         return PTR_ERR(priv->core_pwr);
190
191                 priv->io_pwr = devm_regulator_get(&mdiodev->dev, "io");
192                 if (IS_ERR(priv->io_pwr))
193                         return PTR_ERR(priv->io_pwr);
194         }
195
196         regmap_config = devm_kzalloc(&mdiodev->dev, sizeof(*regmap_config),
197                                      GFP_KERNEL);
198         if (!regmap_config)
199                 return -ENOMEM;
200
201         regmap_config->reg_bits = 16;
202         regmap_config->val_bits = 32;
203         regmap_config->reg_stride = 4;
204         regmap_config->max_register = MT7530_CREV;
205         regmap_config->disable_locking = true;
206         priv->regmap = devm_regmap_init(priv->dev, &mt7530_regmap_bus, priv,
207                                         regmap_config);
208         if (IS_ERR(priv->regmap))
209                 return PTR_ERR(priv->regmap);
210
211         if (priv->id == ID_MT7531)
212                 priv->create_sgmii = mt7531_create_sgmii;
213
214         return dsa_register_switch(priv->ds);
215 }
216
217 static void
218 mt7530_remove(struct mdio_device *mdiodev)
219 {
220         struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
221         int ret = 0, i;
222
223         if (!priv)
224                 return;
225
226         ret = regulator_disable(priv->core_pwr);
227         if (ret < 0)
228                 dev_err(priv->dev,
229                         "Failed to disable core power: %d\n", ret);
230
231         ret = regulator_disable(priv->io_pwr);
232         if (ret < 0)
233                 dev_err(priv->dev, "Failed to disable io pwr: %d\n",
234                         ret);
235
236         mt7530_remove_common(priv);
237
238         for (i = 0; i < 2; ++i)
239                 mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs);
240 }
241
242 static void mt7530_shutdown(struct mdio_device *mdiodev)
243 {
244         struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev);
245
246         if (!priv)
247                 return;
248
249         dsa_switch_shutdown(priv->ds);
250
251         dev_set_drvdata(&mdiodev->dev, NULL);
252 }
253
254 static struct mdio_driver mt7530_mdio_driver = {
255         .probe  = mt7530_probe,
256         .remove = mt7530_remove,
257         .shutdown = mt7530_shutdown,
258         .mdiodrv.driver = {
259                 .name = "mt7530-mdio",
260                 .of_match_table = mt7530_of_match,
261         },
262 };
263
264 mdio_module_driver(mt7530_mdio_driver);
265
266 MODULE_AUTHOR("Sean Wang <[email protected]>");
267 MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");
268 MODULE_LICENSE("GPL");
This page took 0.04173 seconds and 4 git commands to generate.