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