19695375aSMiquel Raynal // SPDX-License-Identifier: GPL-2.0 29695375aSMiquel Raynal /* 39695375aSMiquel Raynal * Copyright (C) 2018 Marvell 49695375aSMiquel Raynal * 59695375aSMiquel Raynal * Authors: 69695375aSMiquel Raynal * Evan Wang <xswang@marvell.com> 79695375aSMiquel Raynal * Miquèl Raynal <miquel.raynal@bootlin.com> 89695375aSMiquel Raynal * 99695375aSMiquel Raynal * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart. 109695375aSMiquel Raynal * SMC call initial support done by Grzegorz Jaszczyk. 119695375aSMiquel Raynal */ 129695375aSMiquel Raynal 139695375aSMiquel Raynal #include <linux/arm-smccc.h> 149695375aSMiquel Raynal #include <linux/io.h> 159695375aSMiquel Raynal #include <linux/iopoll.h> 169695375aSMiquel Raynal #include <linux/mfd/syscon.h> 179695375aSMiquel Raynal #include <linux/module.h> 189695375aSMiquel Raynal #include <linux/phy.h> 199695375aSMiquel Raynal #include <linux/phy/phy.h> 209695375aSMiquel Raynal #include <linux/platform_device.h> 219695375aSMiquel Raynal 229695375aSMiquel Raynal #define MVEBU_A3700_COMPHY_LANES 3 239695375aSMiquel Raynal #define MVEBU_A3700_COMPHY_PORTS 2 249695375aSMiquel Raynal 259695375aSMiquel Raynal /* COMPHY Fast SMC function identifiers */ 269695375aSMiquel Raynal #define COMPHY_SIP_POWER_ON 0x82000001 279695375aSMiquel Raynal #define COMPHY_SIP_POWER_OFF 0x82000002 289695375aSMiquel Raynal #define COMPHY_SIP_PLL_LOCK 0x82000003 29cacc9539SMiquel Raynal #define COMPHY_FW_NOT_SUPPORTED (-1) 309695375aSMiquel Raynal 319695375aSMiquel Raynal #define COMPHY_FW_MODE_SATA 0x1 329695375aSMiquel Raynal #define COMPHY_FW_MODE_SGMII 0x2 339695375aSMiquel Raynal #define COMPHY_FW_MODE_HS_SGMII 0x3 349695375aSMiquel Raynal #define COMPHY_FW_MODE_USB3H 0x4 359695375aSMiquel Raynal #define COMPHY_FW_MODE_USB3D 0x5 369695375aSMiquel Raynal #define COMPHY_FW_MODE_PCIE 0x6 379695375aSMiquel Raynal #define COMPHY_FW_MODE_RXAUI 0x7 389695375aSMiquel Raynal #define COMPHY_FW_MODE_XFI 0x8 399695375aSMiquel Raynal #define COMPHY_FW_MODE_SFI 0x9 409695375aSMiquel Raynal #define COMPHY_FW_MODE_USB3 0xa 419695375aSMiquel Raynal 429695375aSMiquel Raynal #define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */ 439695375aSMiquel Raynal #define COMPHY_FW_SPEED_2_5G 1 449695375aSMiquel Raynal #define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */ 459695375aSMiquel Raynal #define COMPHY_FW_SPEED_5G 3 469695375aSMiquel Raynal #define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */ 479695375aSMiquel Raynal #define COMPHY_FW_SPEED_6G 5 489695375aSMiquel Raynal #define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */ 499695375aSMiquel Raynal #define COMPHY_FW_SPEED_MAX 0x3F 509695375aSMiquel Raynal 519695375aSMiquel Raynal #define COMPHY_FW_MODE(mode) ((mode) << 12) 529695375aSMiquel Raynal #define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \ 539695375aSMiquel Raynal ((idx) << 8) | \ 549695375aSMiquel Raynal ((speed) << 2)) 559695375aSMiquel Raynal #define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \ 569695375aSMiquel Raynal ((width) << 18)) 579695375aSMiquel Raynal 589695375aSMiquel Raynal struct mvebu_a3700_comphy_conf { 599695375aSMiquel Raynal unsigned int lane; 609695375aSMiquel Raynal enum phy_mode mode; 619695375aSMiquel Raynal int submode; 629695375aSMiquel Raynal unsigned int port; 639695375aSMiquel Raynal u32 fw_mode; 649695375aSMiquel Raynal }; 659695375aSMiquel Raynal 669695375aSMiquel Raynal #define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \ 679695375aSMiquel Raynal { \ 689695375aSMiquel Raynal .lane = _lane, \ 699695375aSMiquel Raynal .mode = _mode, \ 709695375aSMiquel Raynal .submode = _smode, \ 719695375aSMiquel Raynal .port = _port, \ 729695375aSMiquel Raynal .fw_mode = _fw, \ 739695375aSMiquel Raynal } 749695375aSMiquel Raynal 759695375aSMiquel Raynal #define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \ 769695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw) 779695375aSMiquel Raynal 789695375aSMiquel Raynal #define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \ 799695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw) 809695375aSMiquel Raynal 819695375aSMiquel Raynal static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = { 829695375aSMiquel Raynal /* lane 0 */ 839695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0, 849695375aSMiquel Raynal COMPHY_FW_MODE_USB3H), 859695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1, 869695375aSMiquel Raynal COMPHY_FW_MODE_SGMII), 879695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1, 889695375aSMiquel Raynal COMPHY_FW_MODE_HS_SGMII), 899695375aSMiquel Raynal /* lane 1 */ 909695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0, 919695375aSMiquel Raynal COMPHY_FW_MODE_PCIE), 929695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0, 939695375aSMiquel Raynal COMPHY_FW_MODE_SGMII), 949695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0, 959695375aSMiquel Raynal COMPHY_FW_MODE_HS_SGMII), 969695375aSMiquel Raynal /* lane 2 */ 979695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0, 989695375aSMiquel Raynal COMPHY_FW_MODE_SATA), 999695375aSMiquel Raynal MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0, 1009695375aSMiquel Raynal COMPHY_FW_MODE_USB3H), 1019695375aSMiquel Raynal }; 1029695375aSMiquel Raynal 1039695375aSMiquel Raynal struct mvebu_a3700_comphy_lane { 1049695375aSMiquel Raynal struct device *dev; 1059695375aSMiquel Raynal unsigned int id; 1069695375aSMiquel Raynal enum phy_mode mode; 1079695375aSMiquel Raynal int submode; 1089695375aSMiquel Raynal int port; 1099695375aSMiquel Raynal }; 1109695375aSMiquel Raynal 1119695375aSMiquel Raynal static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane, 1129695375aSMiquel Raynal unsigned long mode) 1139695375aSMiquel Raynal { 1149695375aSMiquel Raynal struct arm_smccc_res res; 1159695375aSMiquel Raynal 1169695375aSMiquel Raynal arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res); 1179695375aSMiquel Raynal 1189695375aSMiquel Raynal return res.a0; 1199695375aSMiquel Raynal } 1209695375aSMiquel Raynal 1219695375aSMiquel Raynal static int mvebu_a3700_comphy_get_fw_mode(int lane, int port, 1229695375aSMiquel Raynal enum phy_mode mode, 1239695375aSMiquel Raynal int submode) 1249695375aSMiquel Raynal { 1259695375aSMiquel Raynal int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes); 1269695375aSMiquel Raynal 1279695375aSMiquel Raynal /* Unused PHY mux value is 0x0 */ 1289695375aSMiquel Raynal if (mode == PHY_MODE_INVALID) 1299695375aSMiquel Raynal return -EINVAL; 1309695375aSMiquel Raynal 1319695375aSMiquel Raynal for (i = 0; i < n; i++) { 1329695375aSMiquel Raynal if (mvebu_a3700_comphy_modes[i].lane == lane && 1339695375aSMiquel Raynal mvebu_a3700_comphy_modes[i].port == port && 1349695375aSMiquel Raynal mvebu_a3700_comphy_modes[i].mode == mode && 1359695375aSMiquel Raynal mvebu_a3700_comphy_modes[i].submode == submode) 1369695375aSMiquel Raynal break; 1379695375aSMiquel Raynal } 1389695375aSMiquel Raynal 1399695375aSMiquel Raynal if (i == n) 1409695375aSMiquel Raynal return -EINVAL; 1419695375aSMiquel Raynal 1429695375aSMiquel Raynal return mvebu_a3700_comphy_modes[i].fw_mode; 1439695375aSMiquel Raynal } 1449695375aSMiquel Raynal 1459695375aSMiquel Raynal static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode, 1469695375aSMiquel Raynal int submode) 1479695375aSMiquel Raynal { 1489695375aSMiquel Raynal struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 1499695375aSMiquel Raynal int fw_mode; 1509695375aSMiquel Raynal 1519695375aSMiquel Raynal if (submode == PHY_INTERFACE_MODE_1000BASEX) 1529695375aSMiquel Raynal submode = PHY_INTERFACE_MODE_SGMII; 1539695375aSMiquel Raynal 1549695375aSMiquel Raynal fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode, 1559695375aSMiquel Raynal submode); 1569695375aSMiquel Raynal if (fw_mode < 0) { 1579695375aSMiquel Raynal dev_err(lane->dev, "invalid COMPHY mode\n"); 1589695375aSMiquel Raynal return fw_mode; 1599695375aSMiquel Raynal } 1609695375aSMiquel Raynal 1619695375aSMiquel Raynal /* Just remember the mode, ->power_on() will do the real setup */ 1629695375aSMiquel Raynal lane->mode = mode; 1639695375aSMiquel Raynal lane->submode = submode; 1649695375aSMiquel Raynal 1659695375aSMiquel Raynal return 0; 1669695375aSMiquel Raynal } 1679695375aSMiquel Raynal 1689695375aSMiquel Raynal static int mvebu_a3700_comphy_power_on(struct phy *phy) 1699695375aSMiquel Raynal { 1709695375aSMiquel Raynal struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 1719695375aSMiquel Raynal u32 fw_param; 1729695375aSMiquel Raynal int fw_mode; 173cacc9539SMiquel Raynal int ret; 1749695375aSMiquel Raynal 1759695375aSMiquel Raynal fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, 1769695375aSMiquel Raynal lane->mode, lane->submode); 1779695375aSMiquel Raynal if (fw_mode < 0) { 1789695375aSMiquel Raynal dev_err(lane->dev, "invalid COMPHY mode\n"); 1799695375aSMiquel Raynal return fw_mode; 1809695375aSMiquel Raynal } 1819695375aSMiquel Raynal 1829695375aSMiquel Raynal switch (lane->mode) { 1839695375aSMiquel Raynal case PHY_MODE_USB_HOST_SS: 1849695375aSMiquel Raynal dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id); 1859695375aSMiquel Raynal fw_param = COMPHY_FW_MODE(fw_mode); 1869695375aSMiquel Raynal break; 1879695375aSMiquel Raynal case PHY_MODE_SATA: 1889695375aSMiquel Raynal dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id); 1899695375aSMiquel Raynal fw_param = COMPHY_FW_MODE(fw_mode); 1909695375aSMiquel Raynal break; 1919695375aSMiquel Raynal case PHY_MODE_ETHERNET: 1929695375aSMiquel Raynal switch (lane->submode) { 1939695375aSMiquel Raynal case PHY_INTERFACE_MODE_SGMII: 1949695375aSMiquel Raynal dev_dbg(lane->dev, "set lane %d to SGMII mode\n", 1959695375aSMiquel Raynal lane->id); 1969695375aSMiquel Raynal fw_param = COMPHY_FW_NET(fw_mode, lane->port, 1979695375aSMiquel Raynal COMPHY_FW_SPEED_1_25G); 1989695375aSMiquel Raynal break; 1999695375aSMiquel Raynal case PHY_INTERFACE_MODE_2500BASEX: 2009695375aSMiquel Raynal dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n", 2019695375aSMiquel Raynal lane->id); 2029695375aSMiquel Raynal fw_param = COMPHY_FW_NET(fw_mode, lane->port, 2039695375aSMiquel Raynal COMPHY_FW_SPEED_3_125G); 2049695375aSMiquel Raynal break; 2059695375aSMiquel Raynal default: 2069695375aSMiquel Raynal dev_err(lane->dev, "unsupported PHY submode (%d)\n", 2079695375aSMiquel Raynal lane->submode); 2089695375aSMiquel Raynal return -ENOTSUPP; 2099695375aSMiquel Raynal } 2109695375aSMiquel Raynal break; 2119695375aSMiquel Raynal case PHY_MODE_PCIE: 2129695375aSMiquel Raynal dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id); 2139695375aSMiquel Raynal fw_param = COMPHY_FW_PCIE(fw_mode, lane->port, 2149695375aSMiquel Raynal COMPHY_FW_SPEED_5G, 2159695375aSMiquel Raynal phy->attrs.bus_width); 2169695375aSMiquel Raynal break; 2179695375aSMiquel Raynal default: 2189695375aSMiquel Raynal dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode); 2199695375aSMiquel Raynal return -ENOTSUPP; 2209695375aSMiquel Raynal } 2219695375aSMiquel Raynal 222cacc9539SMiquel Raynal ret = mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param); 223cacc9539SMiquel Raynal if (ret == COMPHY_FW_NOT_SUPPORTED) 224cacc9539SMiquel Raynal dev_err(lane->dev, 225cacc9539SMiquel Raynal "unsupported SMC call, try updating your firmware\n"); 226cacc9539SMiquel Raynal 227cacc9539SMiquel Raynal return ret; 2289695375aSMiquel Raynal } 2299695375aSMiquel Raynal 2309695375aSMiquel Raynal static int mvebu_a3700_comphy_power_off(struct phy *phy) 2319695375aSMiquel Raynal { 2329695375aSMiquel Raynal struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy); 2339695375aSMiquel Raynal 2349695375aSMiquel Raynal return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0); 2359695375aSMiquel Raynal } 2369695375aSMiquel Raynal 2379695375aSMiquel Raynal static const struct phy_ops mvebu_a3700_comphy_ops = { 2389695375aSMiquel Raynal .power_on = mvebu_a3700_comphy_power_on, 2399695375aSMiquel Raynal .power_off = mvebu_a3700_comphy_power_off, 2409695375aSMiquel Raynal .set_mode = mvebu_a3700_comphy_set_mode, 2419695375aSMiquel Raynal .owner = THIS_MODULE, 2429695375aSMiquel Raynal }; 2439695375aSMiquel Raynal 2449695375aSMiquel Raynal static struct phy *mvebu_a3700_comphy_xlate(struct device *dev, 2459695375aSMiquel Raynal struct of_phandle_args *args) 2469695375aSMiquel Raynal { 2479695375aSMiquel Raynal struct mvebu_a3700_comphy_lane *lane; 2489695375aSMiquel Raynal struct phy *phy; 2499695375aSMiquel Raynal 2509695375aSMiquel Raynal if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS)) 2519695375aSMiquel Raynal return ERR_PTR(-EINVAL); 2529695375aSMiquel Raynal 2539695375aSMiquel Raynal phy = of_phy_simple_xlate(dev, args); 2549695375aSMiquel Raynal if (IS_ERR(phy)) 2559695375aSMiquel Raynal return phy; 2569695375aSMiquel Raynal 2579695375aSMiquel Raynal lane = phy_get_drvdata(phy); 2589695375aSMiquel Raynal lane->port = args->args[0]; 2599695375aSMiquel Raynal 2609695375aSMiquel Raynal return phy; 2619695375aSMiquel Raynal } 2629695375aSMiquel Raynal 2639695375aSMiquel Raynal static int mvebu_a3700_comphy_probe(struct platform_device *pdev) 2649695375aSMiquel Raynal { 2659695375aSMiquel Raynal struct phy_provider *provider; 2669695375aSMiquel Raynal struct device_node *child; 2679695375aSMiquel Raynal 2689695375aSMiquel Raynal for_each_available_child_of_node(pdev->dev.of_node, child) { 2699695375aSMiquel Raynal struct mvebu_a3700_comphy_lane *lane; 2709695375aSMiquel Raynal struct phy *phy; 2719695375aSMiquel Raynal int ret; 2729695375aSMiquel Raynal u32 lane_id; 2739695375aSMiquel Raynal 2749695375aSMiquel Raynal ret = of_property_read_u32(child, "reg", &lane_id); 2759695375aSMiquel Raynal if (ret < 0) { 2769695375aSMiquel Raynal dev_err(&pdev->dev, "missing 'reg' property (%d)\n", 2779695375aSMiquel Raynal ret); 2789695375aSMiquel Raynal continue; 2799695375aSMiquel Raynal } 2809695375aSMiquel Raynal 2819695375aSMiquel Raynal if (lane_id >= MVEBU_A3700_COMPHY_LANES) { 2829695375aSMiquel Raynal dev_err(&pdev->dev, "invalid 'reg' property\n"); 2839695375aSMiquel Raynal continue; 2849695375aSMiquel Raynal } 2859695375aSMiquel Raynal 2869695375aSMiquel Raynal lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL); 287beae796dSNishka Dasgupta if (!lane) { 288beae796dSNishka Dasgupta of_node_put(child); 2899695375aSMiquel Raynal return -ENOMEM; 290beae796dSNishka Dasgupta } 2919695375aSMiquel Raynal 2929695375aSMiquel Raynal phy = devm_phy_create(&pdev->dev, child, 2939695375aSMiquel Raynal &mvebu_a3700_comphy_ops); 294beae796dSNishka Dasgupta if (IS_ERR(phy)) { 295beae796dSNishka Dasgupta of_node_put(child); 2969695375aSMiquel Raynal return PTR_ERR(phy); 297beae796dSNishka Dasgupta } 2989695375aSMiquel Raynal 2999695375aSMiquel Raynal lane->dev = &pdev->dev; 3009695375aSMiquel Raynal lane->mode = PHY_MODE_INVALID; 3019695375aSMiquel Raynal lane->submode = PHY_INTERFACE_MODE_NA; 3029695375aSMiquel Raynal lane->id = lane_id; 3039695375aSMiquel Raynal lane->port = -1; 3049695375aSMiquel Raynal phy_set_drvdata(phy, lane); 3059695375aSMiquel Raynal } 3069695375aSMiquel Raynal 3079695375aSMiquel Raynal provider = devm_of_phy_provider_register(&pdev->dev, 3089695375aSMiquel Raynal mvebu_a3700_comphy_xlate); 3099695375aSMiquel Raynal return PTR_ERR_OR_ZERO(provider); 3109695375aSMiquel Raynal } 3119695375aSMiquel Raynal 3129695375aSMiquel Raynal static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = { 3139695375aSMiquel Raynal { .compatible = "marvell,comphy-a3700" }, 3149695375aSMiquel Raynal { }, 3159695375aSMiquel Raynal }; 3169695375aSMiquel Raynal MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table); 3179695375aSMiquel Raynal 3189695375aSMiquel Raynal static struct platform_driver mvebu_a3700_comphy_driver = { 3199695375aSMiquel Raynal .probe = mvebu_a3700_comphy_probe, 3209695375aSMiquel Raynal .driver = { 3219695375aSMiquel Raynal .name = "mvebu-a3700-comphy", 3229695375aSMiquel Raynal .of_match_table = mvebu_a3700_comphy_of_match_table, 3239695375aSMiquel Raynal }, 3249695375aSMiquel Raynal }; 3259695375aSMiquel Raynal module_platform_driver(mvebu_a3700_comphy_driver); 3269695375aSMiquel Raynal 3279695375aSMiquel Raynal MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>"); 3289695375aSMiquel Raynal MODULE_DESCRIPTION("Common PHY driver for A3700"); 3299695375aSMiquel Raynal MODULE_LICENSE("GPL v2"); 330