1 // SPDX-License-Identifier: GPL-2.0-only
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>
19 mt7530_regmap_write(void *context
, unsigned int reg
, unsigned int val
)
21 struct mt7530_priv
*priv
= context
;
22 struct mii_bus
*bus
= priv
->bus
;
26 page
= (reg
>> 6) & 0x3ff;
31 ret
= bus
->write(bus
, priv
->mdiodev
->addr
, 0x1f, page
);
35 ret
= bus
->write(bus
, priv
->mdiodev
->addr
, r
, lo
);
39 ret
= bus
->write(bus
, priv
->mdiodev
->addr
, 0x10, hi
);
44 mt7530_regmap_read(void *context
, unsigned int reg
, unsigned int *val
)
46 struct mt7530_priv
*priv
= context
;
47 struct mii_bus
*bus
= priv
->bus
;
51 page
= (reg
>> 6) & 0x3ff;
54 ret
= bus
->write(bus
, priv
->mdiodev
->addr
, 0x1f, page
);
58 lo
= bus
->read(bus
, priv
->mdiodev
->addr
, r
);
59 hi
= bus
->read(bus
, priv
->mdiodev
->addr
, 0x10);
61 *val
= (hi
<< 16) | (lo
& 0xffff);
67 mt7530_mdio_regmap_lock(void *mdio_lock
)
69 mutex_lock_nested(mdio_lock
, MDIO_MUTEX_NESTED
);
73 mt7530_mdio_regmap_unlock(void *mdio_lock
)
75 mutex_unlock(mdio_lock
);
78 static const struct regmap_bus mt7530_regmap_bus
= {
79 .reg_write
= mt7530_regmap_write
,
80 .reg_read
= mt7530_regmap_read
,
84 mt7531_create_sgmii(struct mt7530_priv
*priv
)
86 struct regmap_config
*mt7531_pcs_config
[2] = {};
87 struct phylink_pcs
*pcs
;
88 struct regmap
*regmap
;
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
),
95 if (!mt7531_pcs_config
[i
]) {
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
;
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
);
116 pcs
= mtk_pcs_lynxi_create(priv
->dev
, regmap
,
117 MT7531_PHYA_CTRL_SIGNAL3
, 0);
122 priv
->ports
[5 + i
].sgmii_pcs
= pcs
;
126 mtk_pcs_lynxi_destroy(priv
->ports
[5].sgmii_pcs
);
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
], },
137 MODULE_DEVICE_TABLE(of
, mt7530_of_match
);
140 mt7530_probe(struct mdio_device
*mdiodev
)
142 static struct regmap_config
*regmap_config
;
143 struct mt7530_priv
*priv
;
144 struct device_node
*dn
;
147 dn
= mdiodev
->dev
.of_node
;
149 priv
= devm_kzalloc(&mdiodev
->dev
, sizeof(*priv
), GFP_KERNEL
);
153 priv
->bus
= mdiodev
->bus
;
154 priv
->dev
= &mdiodev
->dev
;
155 priv
->mdiodev
= mdiodev
;
157 ret
= mt7530_probe_common(priv
);
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.
168 priv
->mcm
= of_property_read_bool(dn
, "mediatek,mcm");
170 dev_info(&mdiodev
->dev
, "MT7530 adapts as multi-chip module\n");
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
);
178 priv
->reset
= devm_gpiod_get_optional(&mdiodev
->dev
, "reset",
180 if (IS_ERR(priv
->reset
)) {
181 dev_err(&mdiodev
->dev
, "Couldn't get our reset line\n");
182 return PTR_ERR(priv
->reset
);
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
);
191 priv
->io_pwr
= devm_regulator_get(&mdiodev
->dev
, "io");
192 if (IS_ERR(priv
->io_pwr
))
193 return PTR_ERR(priv
->io_pwr
);
196 regmap_config
= devm_kzalloc(&mdiodev
->dev
, sizeof(*regmap_config
),
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
,
208 if (IS_ERR(priv
->regmap
))
209 return PTR_ERR(priv
->regmap
);
211 if (priv
->id
== ID_MT7531
)
212 priv
->create_sgmii
= mt7531_create_sgmii
;
214 return dsa_register_switch(priv
->ds
);
218 mt7530_remove(struct mdio_device
*mdiodev
)
220 struct mt7530_priv
*priv
= dev_get_drvdata(&mdiodev
->dev
);
226 ret
= regulator_disable(priv
->core_pwr
);
229 "Failed to disable core power: %d\n", ret
);
231 ret
= regulator_disable(priv
->io_pwr
);
233 dev_err(priv
->dev
, "Failed to disable io pwr: %d\n",
236 mt7530_remove_common(priv
);
238 for (i
= 0; i
< 2; ++i
)
239 mtk_pcs_lynxi_destroy(priv
->ports
[5 + i
].sgmii_pcs
);
242 static void mt7530_shutdown(struct mdio_device
*mdiodev
)
244 struct mt7530_priv
*priv
= dev_get_drvdata(&mdiodev
->dev
);
249 dsa_switch_shutdown(priv
->ds
);
251 dev_set_drvdata(&mdiodev
->dev
, NULL
);
254 static struct mdio_driver mt7530_mdio_driver
= {
255 .probe
= mt7530_probe
,
256 .remove
= mt7530_remove
,
257 .shutdown
= mt7530_shutdown
,
259 .name
= "mt7530-mdio",
260 .of_match_table
= mt7530_of_match
,
264 mdio_module_driver(mt7530_mdio_driver
);
266 MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
267 MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)");
268 MODULE_LICENSE("GPL");