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