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 mii_bus *bus = context; 22 u16 page, r, lo, hi; 23 int ret; 24 25 page = (reg >> 6) & 0x3ff; 26 r = (reg >> 2) & 0xf; 27 lo = val & 0xffff; 28 hi = val >> 16; 29 30 /* MT7530 uses 31 as the pseudo port */ 31 ret = bus->write(bus, 0x1f, 0x1f, page); 32 if (ret < 0) 33 return ret; 34 35 ret = bus->write(bus, 0x1f, r, lo); 36 if (ret < 0) 37 return ret; 38 39 ret = bus->write(bus, 0x1f, 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 mii_bus *bus = context; 47 u16 page, r, lo, hi; 48 int ret; 49 50 page = (reg >> 6) & 0x3ff; 51 r = (reg >> 2) & 0xf; 52 53 /* MT7530 uses 31 as the pseudo port */ 54 ret = bus->write(bus, 0x1f, 0x1f, page); 55 if (ret < 0) 56 return ret; 57 58 lo = bus->read(bus, 0x1f, r); 59 hi = bus->read(bus, 0x1f, 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, 111 &mt7530_regmap_bus, priv->bus, 112 mt7531_pcs_config[i]); 113 if (IS_ERR(regmap)) { 114 ret = PTR_ERR(regmap); 115 break; 116 } 117 pcs = mtk_pcs_lynxi_create(priv->dev, regmap, 118 MT7531_PHYA_CTRL_SIGNAL3, 0); 119 if (!pcs) { 120 ret = -ENXIO; 121 break; 122 } 123 priv->ports[5 + i].sgmii_pcs = pcs; 124 } 125 126 if (ret && i) 127 mtk_pcs_lynxi_destroy(priv->ports[5].sgmii_pcs); 128 129 return ret; 130 } 131 132 static const struct of_device_id mt7530_of_match[] = { 133 { .compatible = "mediatek,mt7621", .data = &mt753x_table[ID_MT7621], }, 134 { .compatible = "mediatek,mt7530", .data = &mt753x_table[ID_MT7530], }, 135 { .compatible = "mediatek,mt7531", .data = &mt753x_table[ID_MT7531], }, 136 { /* sentinel */ }, 137 }; 138 MODULE_DEVICE_TABLE(of, mt7530_of_match); 139 140 static int 141 mt7530_probe(struct mdio_device *mdiodev) 142 { 143 static struct regmap_config *regmap_config; 144 struct mt7530_priv *priv; 145 struct device_node *dn; 146 int ret; 147 148 dn = mdiodev->dev.of_node; 149 150 priv = devm_kzalloc(&mdiodev->dev, sizeof(*priv), GFP_KERNEL); 151 if (!priv) 152 return -ENOMEM; 153 154 priv->bus = mdiodev->bus; 155 priv->dev = &mdiodev->dev; 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, 207 priv->bus, 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 <sean.wang@mediatek.com>"); 267 MODULE_DESCRIPTION("Driver for Mediatek MT7530 Switch (MDIO)"); 268 MODULE_LICENSE("GPL"); 269