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