• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
mvebu_a3700_comphy_smc(unsigned long function,unsigned long lane,unsigned long mode)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 
mvebu_a3700_comphy_get_fw_mode(int lane,int port,enum phy_mode mode,int submode)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 
mvebu_a3700_comphy_set_mode(struct phy * phy,enum phy_mode mode,int submode)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 
mvebu_a3700_comphy_power_on(struct phy * phy)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 
mvebu_a3700_comphy_power_off(struct phy * phy)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 
mvebu_a3700_comphy_xlate(struct device * dev,struct of_phandle_args * args)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 
mvebu_a3700_comphy_probe(struct platform_device * pdev)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