Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 57struct 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
 80static 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
102struct 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
110static 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
129static 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
153static 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
176static 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
238static 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
245static 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
252static 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
271static 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
320static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
321	{ .compatible = "marvell,comphy-a3700" },
322	{ },
323};
324MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
325
326static 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};
333module_platform_driver(mvebu_a3700_comphy_driver);
334
335MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
336MODULE_DESCRIPTION("Common PHY driver for A3700");
337MODULE_LICENSE("GPL v2");