Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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
 58struct 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
 81static 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
103struct 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
111static 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
121static 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
145static 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
168static 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
230static 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
237static 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
244static 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
263static 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
312static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
313	{ .compatible = "marvell,comphy-a3700" },
314	{ },
315};
316MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
317
318static 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};
325module_platform_driver(mvebu_a3700_comphy_driver);
326
327MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
328MODULE_DESCRIPTION("Common PHY driver for A3700");
329MODULE_LICENSE("GPL v2");