1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2018 Marvell 4 * 5 * Authors: 6 * Evan Wang <xswang@marvell.com> 7 * Miquèl Raynal <miquel.raynal@bootlin.com> 8 * 9 * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. 10 * SMC call initial support done by Grzegorz Jaszczyk. 11 */ 12 13 #include <linux/arm-smccc.h> 14 #include <linux/io.h> 15 #include <linux/iopoll.h> 16 #include <linux/mfd/syscon.h> 17 #include <linux/module.h> 18 #include <linux/phy.h> 19 #include <linux/phy/phy.h> 20 #include <linux/platform_device.h> 21 22 #define MVEBU_A3700_COMPHY_LANES 3 23 #define MVEBU_A3700_COMPHY_PORTS 2 24 25 /* COMPHY Fast SMC function identifiers */ 26 #define COMPHY_SIP_POWER_ON 0x82000001 27 #define COMPHY_SIP_POWER_OFF 0x82000002 28 #define COMPHY_SIP_PLL_LOCK 0x82000003 29 30 #define COMPHY_FW_MODE_SATA 0x1 31 #define COMPHY_FW_MODE_SGMII 0x2 32 #define COMPHY_FW_MODE_2500BASEX 0x3 33 #define COMPHY_FW_MODE_USB3H 0x4 34 #define COMPHY_FW_MODE_USB3D 0x5 35 #define COMPHY_FW_MODE_PCIE 0x6 36 #define COMPHY_FW_MODE_USB3 0xa 37 38 #define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ 39 #define COMPHY_FW_SPEED_2_5G 1 40 #define COMPHY_FW_SPEED_3_125G 2 /* 2500BASE-X */ 41 #define COMPHY_FW_SPEED_5G 3 42 #define COMPHY_FW_SPEED_MAX 0x3F 43 44 #define COMPHY_FW_MODE(mode) ((mode) << 12) 45 #define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ 46 ((idx) << 8) | \ 47 ((speed) << 2)) 48 #define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ 49 ((width) << 18)) 50 51 struct mvebu_a3700_comphy_conf { 52 unsigned int lane; 53 enum phy_mode mode; 54 int submode; 55 unsigned int port; 56 u32 fw_mode; 57 }; 58 59 #define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ 60 { \ 61 .lane = _lane, \ 62 .mode = _mode, \ 63 .submode = _smode, \ 64 .port = _port, \ 65 .fw_mode = _fw, \ 66 } 67 68 #define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ 69 MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) 70 71 #define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ 72 MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) 73 74 static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { 75 /* lane 0 */ 76 MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, 77 COMPHY_FW_MODE_USB3H), 78 MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, 79 COMPHY_FW_MODE_SGMII), 80 MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, 81 COMPHY_FW_MODE_2500BASEX), 82 /* lane 1 */ 83 MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, 84 COMPHY_FW_MODE_PCIE), 85 MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, 86 COMPHY_FW_MODE_SGMII), 87 MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, 88 COMPHY_FW_MODE_2500BASEX), 89 /* lane 2 */ 90 MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, 91 COMPHY_FW_MODE_SATA), 92 MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, 93 COMPHY_FW_MODE_USB3H), 94 }; 95 96 struct mvebu_a3700_comphy_lane { 97 struct device *dev; 98 unsigned int id; 99 enum phy_mode mode; 100 int submode; 101 int port; 102 }; 103 104 static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, 105 unsigned long mode) 106 { 107 struct arm_smccc_res res; 108 s32 ret; 109 110 arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); 111 ret = res.a0; 112 113 switch (ret) { 114 case SMCCC_RET_SUCCESS: 115 return 0; 116 case SMCCC_RET_NOT_SUPPORTED: 117 return -EOPNOTSUPP; 118 default: 119 return -EINVAL; 120 } 121 } 122 123 static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, 124 enum phy_mode mode, 125 int submode) 126 { 127 int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes); 128 129 /* Unused PHY mux value is 0x0 */ 130 if (mode == PHY_MODE_INVALID) 131 return -EINVAL; 132 133 for (i = 0; i < n; i++) { 134 if (mvebu_a3700_comphy_modes[i].lane == lane && 135 mvebu_a3700_comphy_modes[i].port == port && 136 mvebu_a3700_comphy_modes[i].mode == mode && 137 mvebu_a3700_comphy_modes[i].submode == submode) 138 break; 139 } 140 141 if (i == n) 142 return -EINVAL; 143 144 return mvebu_a3700_comphy_modes[i].fw_mode; 145 } 146 147 static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, 148 int submode) 149 { 150 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 151 int fw_mode; 152 153 if (submode == PHY_INTERFACE_MODE_1000BASEX) 154 submode = PHY_INTERFACE_MODE_SGMII; 155 156 fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, 157 submode); 158 if (fw_mode < 0) { 159 dev_err(lane->dev, "invalid COMPHY mode\n"); 160 return fw_mode; 161 } 162 163 /* Just remember the mode, ->power_on() will do the real setup */ 164 lane->mode = mode; 165 lane->submode = submode; 166 167 return 0; 168 } 169 170 static int mvebu_a3700_comphy_power_on(struct phy *phy) 171 { 172 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 173 u32 fw_param; 174 int fw_mode; 175 int ret; 176 177 fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, 178 lane->mode, lane->submode); 179 if (fw_mode < 0) { 180 dev_err(lane->dev, "invalid COMPHY mode\n"); 181 return fw_mode; 182 } 183 184 switch (lane->mode) { 185 case PHY_MODE_USB_HOST_SS: 186 dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); 187 fw_param = COMPHY_FW_MODE(fw_mode); 188 break; 189 case PHY_MODE_SATA: 190 dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); 191 fw_param = COMPHY_FW_MODE(fw_mode); 192 break; 193 case PHY_MODE_ETHERNET: 194 switch (lane->submode) { 195 case PHY_INTERFACE_MODE_SGMII: 196 dev_dbg(lane->dev, "set lane %d to SGMII mode\n", 197 lane->id); 198 fw_param = COMPHY_FW_NET(fw_mode, lane->port, 199 COMPHY_FW_SPEED_1_25G); 200 break; 201 case PHY_INTERFACE_MODE_2500BASEX: 202 dev_dbg(lane->dev, "set lane %d to 2500BASEX mode\n", 203 lane->id); 204 fw_param = COMPHY_FW_NET(fw_mode, lane->port, 205 COMPHY_FW_SPEED_3_125G); 206 break; 207 default: 208 dev_err(lane->dev, "unsupported PHY submode (%d)\n", 209 lane->submode); 210 return -ENOTSUPP; 211 } 212 break; 213 case PHY_MODE_PCIE: 214 dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); 215 fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, 216 COMPHY_FW_SPEED_5G, 217 phy->attrs.bus_width); 218 break; 219 default: 220 dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); 221 return -ENOTSUPP; 222 } 223 224 ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); 225 if (ret == -EOPNOTSUPP) 226 dev_err(lane->dev, 227 "unsupported SMC call, try updating your firmware\n"); 228 229 return ret; 230 } 231 232 static int mvebu_a3700_comphy_power_off(struct phy *phy) 233 { 234 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 235 236 return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); 237 } 238 239 static const struct phy_ops mvebu_a3700_comphy_ops = { 240 .power_on = mvebu_a3700_comphy_power_on, 241 .power_off = mvebu_a3700_comphy_power_off, 242 .set_mode = mvebu_a3700_comphy_set_mode, 243 .owner = THIS_MODULE, 244 }; 245 246 static struct phy *mvebu_a3700_comphy_xlate(struct device *dev, 247 struct of_phandle_args *args) 248 { 249 struct mvebu_a3700_comphy_lane *lane; 250 struct phy *phy; 251 252 if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) 253 return ERR_PTR(-EINVAL); 254 255 phy = of_phy_simple_xlate(dev, args); 256 if (IS_ERR(phy)) 257 return phy; 258 259 lane = phy_get_drvdata(phy); 260 lane->port = args->args[0]; 261 262 return phy; 263 } 264 265 static int mvebu_a3700_comphy_probe(struct platform_device *pdev) 266 { 267 struct phy_provider *provider; 268 struct device_node *child; 269 270 for_each_available_child_of_node(pdev->dev.of_node, child) { 271 struct mvebu_a3700_comphy_lane *lane; 272 struct phy *phy; 273 int ret; 274 u32 lane_id; 275 276 ret = of_property_read_u32(child, "reg", &lane_id); 277 if (ret < 0) { 278 dev_err(&pdev->dev, "missing 'reg' property (%d)\n", 279 ret); 280 continue; 281 } 282 283 if (lane_id >= MVEBU_A3700_COMPHY_LANES) { 284 dev_err(&pdev->dev, "invalid 'reg' property\n"); 285 continue; 286 } 287 288 lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL); 289 if (!lane) { 290 of_node_put(child); 291 return -ENOMEM; 292 } 293 294 phy = devm_phy_create(&pdev->dev, child, 295 &mvebu_a3700_comphy_ops); 296 if (IS_ERR(phy)) { 297 of_node_put(child); 298 return PTR_ERR(phy); 299 } 300 301 lane->dev = &pdev->dev; 302 lane->mode = PHY_MODE_INVALID; 303 lane->submode = PHY_INTERFACE_MODE_NA; 304 lane->id = lane_id; 305 lane->port = -1; 306 phy_set_drvdata(phy, lane); 307 } 308 309 provider = devm_of_phy_provider_register(&pdev->dev, 310 mvebu_a3700_comphy_xlate); 311 return PTR_ERR_OR_ZERO(provider); 312 } 313 314 static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = { 315 { .compatible = "marvell,comphy-a3700" }, 316 { }, 317 }; 318 MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table); 319 320 static struct platform_driver mvebu_a3700_comphy_driver = { 321 .probe = mvebu_a3700_comphy_probe, 322 .driver = { 323 .name = "mvebu-a3700-comphy", 324 .of_match_table = mvebu_a3700_comphy_of_match_table, 325 }, 326 }; 327 module_platform_driver(mvebu_a3700_comphy_driver); 328 329 MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); 330 MODULE_DESCRIPTION("Common PHY driver for A3700"); 331 MODULE_LICENSE("GPL v2"); 332