0964da03a8d81e25490cdc02de37946a06d9cce4
[openwrt/staging/chunkeey.git] / target / linux / mvebu / patches-4.19 / 542-phy-add-A3700-COMPHY-support.patch
1 From 9695375a3f4a604406f2e61f2b735eca1de931ed Mon Sep 17 00:00:00 2001
2 From: Miquel Raynal <miquel.raynal@bootlin.com>
3 Date: Tue, 8 Jan 2019 17:31:20 +0100
4 Subject: [PATCH] phy: add A3700 COMPHY support
5
6 Add a driver to support COMPHY, a hardware block providing shared
7 serdes PHYs on Marvell Armada 3700. This driver uses SMC calls and
8 rely on having an up-to-date firmware.
9
10 SATA, PCie and USB3 host mode have been tested successfully with an
11 ESPRESSObin. (HS)SGMII mode cannot be tested with this platform.
12
13 Evan worked on the original driver structure and Grzegorz on the SMC
14 calls rework. The structure of this driver has been copied from
15 Antoine Tenart work on CP110 COMPHY driver.
16
17 Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
18 Co-developed-by: Evan Wang <xswang@marvell.com>
19 Signed-off-by: Evan Wang <xswang@marvell.com>
20 Co-developed-by: Grzegorz Jaszczyk <jaz@semihalf.com>
21 Signed-off-by: Grzegorz Jaszczyk <jaz@semihalf.com>
22 Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
23 ---
24 drivers/phy/marvell/Kconfig | 12 +
25 drivers/phy/marvell/Makefile | 1 +
26 drivers/phy/marvell/phy-mvebu-a3700-comphy.c | 318 +++++++++++++++++++++++++++
27 3 files changed, 331 insertions(+)
28 create mode 100644 drivers/phy/marvell/phy-mvebu-a3700-comphy.c
29
30 --- a/drivers/phy/marvell/Kconfig
31 +++ b/drivers/phy/marvell/Kconfig
32 @@ -21,6 +21,18 @@ config PHY_BERLIN_USB
33 help
34 Enable this to support the USB PHY on Marvell Berlin SoCs.
35
36 +config PHY_MVEBU_A3700_COMPHY
37 + tristate "Marvell A3700 comphy driver"
38 + depends on ARCH_MVEBU || COMPILE_TEST
39 + depends on OF
40 + depends on HAVE_ARM_SMCCC
41 + default y
42 + select GENERIC_PHY
43 + help
44 + This driver allows to control the comphy, a hardware block providing
45 + shared serdes PHYs on Marvell Armada 3700. Its serdes lanes can be
46 + used by various controllers: Ethernet, SATA, USB3, PCIe.
47 +
48 config PHY_MVEBU_CP110_COMPHY
49 tristate "Marvell CP110 comphy driver"
50 depends on ARCH_MVEBU || COMPILE_TEST
51 --- a/drivers/phy/marvell/Makefile
52 +++ b/drivers/phy/marvell/Makefile
53 @@ -2,6 +2,7 @@
54 obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
55 obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
56 obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
57 +obj-$(CONFIG_PHY_MVEBU_A3700_COMPHY) += phy-mvebu-a3700-comphy.o
58 obj-$(CONFIG_PHY_MVEBU_CP110_COMPHY) += phy-mvebu-cp110-comphy.o
59 obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
60 obj-$(CONFIG_PHY_PXA_28NM_HSIC) += phy-pxa-28nm-hsic.o
61 --- /dev/null
62 +++ b/drivers/phy/marvell/phy-mvebu-a3700-comphy.c
63 @@ -0,0 +1,318 @@
64 +// SPDX-License-Identifier: GPL-2.0
65 +/*
66 + * Copyright (C) 2018 Marvell
67 + *
68 + * Authors:
69 + * Evan Wang <xswang@marvell.com>
70 + * Miquèl Raynal <miquel.raynal@bootlin.com>
71 + *
72 + * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
73 + * SMC call initial support done by Grzegorz Jaszczyk.
74 + */
75 +
76 +#include <linux/arm-smccc.h>
77 +#include <linux/io.h>
78 +#include <linux/iopoll.h>
79 +#include <linux/mfd/syscon.h>
80 +#include <linux/module.h>
81 +#include <linux/phy.h>
82 +#include <linux/phy/phy.h>
83 +#include <linux/platform_device.h>
84 +
85 +#define MVEBU_A3700_COMPHY_LANES 3
86 +#define MVEBU_A3700_COMPHY_PORTS 2
87 +
88 +/* COMPHY Fast SMC function identifiers */
89 +#define COMPHY_SIP_POWER_ON 0x82000001
90 +#define COMPHY_SIP_POWER_OFF 0x82000002
91 +#define COMPHY_SIP_PLL_LOCK 0x82000003
92 +
93 +#define COMPHY_FW_MODE_SATA 0x1
94 +#define COMPHY_FW_MODE_SGMII 0x2
95 +#define COMPHY_FW_MODE_HS_SGMII 0x3
96 +#define COMPHY_FW_MODE_USB3H 0x4
97 +#define COMPHY_FW_MODE_USB3D 0x5
98 +#define COMPHY_FW_MODE_PCIE 0x6
99 +#define COMPHY_FW_MODE_RXAUI 0x7
100 +#define COMPHY_FW_MODE_XFI 0x8
101 +#define COMPHY_FW_MODE_SFI 0x9
102 +#define COMPHY_FW_MODE_USB3 0xa
103 +
104 +#define COMPHY_FW_SPEED_1_25G 0 /* SGMII 1G */
105 +#define COMPHY_FW_SPEED_2_5G 1
106 +#define COMPHY_FW_SPEED_3_125G 2 /* SGMII 2.5G */
107 +#define COMPHY_FW_SPEED_5G 3
108 +#define COMPHY_FW_SPEED_5_15625G 4 /* XFI 5G */
109 +#define COMPHY_FW_SPEED_6G 5
110 +#define COMPHY_FW_SPEED_10_3125G 6 /* XFI 10G */
111 +#define COMPHY_FW_SPEED_MAX 0x3F
112 +
113 +#define COMPHY_FW_MODE(mode) ((mode) << 12)
114 +#define COMPHY_FW_NET(mode, idx, speed) (COMPHY_FW_MODE(mode) | \
115 + ((idx) << 8) | \
116 + ((speed) << 2))
117 +#define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
118 + ((width) << 18))
119 +
120 +struct mvebu_a3700_comphy_conf {
121 + unsigned int lane;
122 + enum phy_mode mode;
123 + int submode;
124 + unsigned int port;
125 + u32 fw_mode;
126 +};
127 +
128 +#define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _fw) \
129 + { \
130 + .lane = _lane, \
131 + .mode = _mode, \
132 + .submode = _smode, \
133 + .port = _port, \
134 + .fw_mode = _fw, \
135 + }
136 +
137 +#define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
138 + MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
139 +
140 +#define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
141 + MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
142 +
143 +static const struct mvebu_a3700_comphy_conf mvebu_a3700_comphy_modes[] = {
144 + /* lane 0 */
145 + MVEBU_A3700_COMPHY_CONF_GEN(0, PHY_MODE_USB_HOST_SS, 0,
146 + COMPHY_FW_MODE_USB3H),
147 + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_SGMII, 1,
148 + COMPHY_FW_MODE_SGMII),
149 + MVEBU_A3700_COMPHY_CONF_ETH(0, PHY_INTERFACE_MODE_2500BASEX, 1,
150 + COMPHY_FW_MODE_HS_SGMII),
151 + /* lane 1 */
152 + MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE, 0,
153 + COMPHY_FW_MODE_PCIE),
154 + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_SGMII, 0,
155 + COMPHY_FW_MODE_SGMII),
156 + MVEBU_A3700_COMPHY_CONF_ETH(1, PHY_INTERFACE_MODE_2500BASEX, 0,
157 + COMPHY_FW_MODE_HS_SGMII),
158 + /* lane 2 */
159 + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA, 0,
160 + COMPHY_FW_MODE_SATA),
161 + MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS, 0,
162 + COMPHY_FW_MODE_USB3H),
163 +};
164 +
165 +struct mvebu_a3700_comphy_lane {
166 + struct device *dev;
167 + unsigned int id;
168 + enum phy_mode mode;
169 + int submode;
170 + int port;
171 +};
172 +
173 +static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
174 + unsigned long mode)
175 +{
176 + struct arm_smccc_res res;
177 +
178 + arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
179 +
180 + return res.a0;
181 +}
182 +
183 +static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
184 + enum phy_mode mode,
185 + int submode)
186 +{
187 + int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes);
188 +
189 + /* Unused PHY mux value is 0x0 */
190 + if (mode == PHY_MODE_INVALID)
191 + return -EINVAL;
192 +
193 + for (i = 0; i < n; i++) {
194 + if (mvebu_a3700_comphy_modes[i].lane == lane &&
195 + mvebu_a3700_comphy_modes[i].port == port &&
196 + mvebu_a3700_comphy_modes[i].mode == mode &&
197 + mvebu_a3700_comphy_modes[i].submode == submode)
198 + break;
199 + }
200 +
201 + if (i == n)
202 + return -EINVAL;
203 +
204 + return mvebu_a3700_comphy_modes[i].fw_mode;
205 +}
206 +
207 +static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
208 + int submode)
209 +{
210 + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
211 + int fw_mode;
212 +
213 + if (submode == PHY_INTERFACE_MODE_1000BASEX)
214 + submode = PHY_INTERFACE_MODE_SGMII;
215 +
216 + fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port, mode,
217 + submode);
218 + if (fw_mode < 0) {
219 + dev_err(lane->dev, "invalid COMPHY mode\n");
220 + return fw_mode;
221 + }
222 +
223 + /* Just remember the mode, ->power_on() will do the real setup */
224 + lane->mode = mode;
225 + lane->submode = submode;
226 +
227 + return 0;
228 +}
229 +
230 +static int mvebu_a3700_comphy_power_on(struct phy *phy)
231 +{
232 + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
233 + u32 fw_param;
234 + int fw_mode;
235 +
236 + fw_mode = mvebu_a3700_comphy_get_fw_mode(lane->id, lane->port,
237 + lane->mode, lane->submode);
238 + if (fw_mode < 0) {
239 + dev_err(lane->dev, "invalid COMPHY mode\n");
240 + return fw_mode;
241 + }
242 +
243 + switch (lane->mode) {
244 + case PHY_MODE_USB_HOST_SS:
245 + dev_dbg(lane->dev, "set lane %d to USB3 host mode\n", lane->id);
246 + fw_param = COMPHY_FW_MODE(fw_mode);
247 + break;
248 + case PHY_MODE_SATA:
249 + dev_dbg(lane->dev, "set lane %d to SATA mode\n", lane->id);
250 + fw_param = COMPHY_FW_MODE(fw_mode);
251 + break;
252 + case PHY_MODE_ETHERNET:
253 + switch (lane->submode) {
254 + case PHY_INTERFACE_MODE_SGMII:
255 + dev_dbg(lane->dev, "set lane %d to SGMII mode\n",
256 + lane->id);
257 + fw_param = COMPHY_FW_NET(fw_mode, lane->port,
258 + COMPHY_FW_SPEED_1_25G);
259 + break;
260 + case PHY_INTERFACE_MODE_2500BASEX:
261 + dev_dbg(lane->dev, "set lane %d to HS SGMII mode\n",
262 + lane->id);
263 + fw_param = COMPHY_FW_NET(fw_mode, lane->port,
264 + COMPHY_FW_SPEED_3_125G);
265 + break;
266 + default:
267 + dev_err(lane->dev, "unsupported PHY submode (%d)\n",
268 + lane->submode);
269 + return -ENOTSUPP;
270 + }
271 + break;
272 + case PHY_MODE_PCIE:
273 + dev_dbg(lane->dev, "set lane %d to PCIe mode\n", lane->id);
274 + fw_param = COMPHY_FW_PCIE(fw_mode, lane->port,
275 + COMPHY_FW_SPEED_5G,
276 + phy->attrs.bus_width);
277 + break;
278 + default:
279 + dev_err(lane->dev, "unsupported PHY mode (%d)\n", lane->mode);
280 + return -ENOTSUPP;
281 + }
282 +
283 + return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON, lane->id, fw_param);
284 +}
285 +
286 +static int mvebu_a3700_comphy_power_off(struct phy *phy)
287 +{
288 + struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
289 +
290 + return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
291 +}
292 +
293 +static const struct phy_ops mvebu_a3700_comphy_ops = {
294 + .power_on = mvebu_a3700_comphy_power_on,
295 + .power_off = mvebu_a3700_comphy_power_off,
296 + .set_mode = mvebu_a3700_comphy_set_mode,
297 + .owner = THIS_MODULE,
298 +};
299 +
300 +static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
301 + struct of_phandle_args *args)
302 +{
303 + struct mvebu_a3700_comphy_lane *lane;
304 + struct phy *phy;
305 +
306 + if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
307 + return ERR_PTR(-EINVAL);
308 +
309 + phy = of_phy_simple_xlate(dev, args);
310 + if (IS_ERR(phy))
311 + return phy;
312 +
313 + lane = phy_get_drvdata(phy);
314 + lane->port = args->args[0];
315 +
316 + return phy;
317 +}
318 +
319 +static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
320 +{
321 + struct phy_provider *provider;
322 + struct device_node *child;
323 +
324 + for_each_available_child_of_node(pdev->dev.of_node, child) {
325 + struct mvebu_a3700_comphy_lane *lane;
326 + struct phy *phy;
327 + int ret;
328 + u32 lane_id;
329 +
330 + ret = of_property_read_u32(child, "reg", &lane_id);
331 + if (ret < 0) {
332 + dev_err(&pdev->dev, "missing 'reg' property (%d)\n",
333 + ret);
334 + continue;
335 + }
336 +
337 + if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
338 + dev_err(&pdev->dev, "invalid 'reg' property\n");
339 + continue;
340 + }
341 +
342 + lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
343 + if (!lane)
344 + return -ENOMEM;
345 +
346 + phy = devm_phy_create(&pdev->dev, child,
347 + &mvebu_a3700_comphy_ops);
348 + if (IS_ERR(phy))
349 + return PTR_ERR(phy);
350 +
351 + lane->dev = &pdev->dev;
352 + lane->mode = PHY_MODE_INVALID;
353 + lane->submode = PHY_INTERFACE_MODE_NA;
354 + lane->id = lane_id;
355 + lane->port = -1;
356 + phy_set_drvdata(phy, lane);
357 + }
358 +
359 + provider = devm_of_phy_provider_register(&pdev->dev,
360 + mvebu_a3700_comphy_xlate);
361 + return PTR_ERR_OR_ZERO(provider);
362 +}
363 +
364 +static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
365 + { .compatible = "marvell,comphy-a3700" },
366 + { },
367 +};
368 +MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
369 +
370 +static struct platform_driver mvebu_a3700_comphy_driver = {
371 + .probe = mvebu_a3700_comphy_probe,
372 + .driver = {
373 + .name = "mvebu-a3700-comphy",
374 + .of_match_table = mvebu_a3700_comphy_of_match_table,
375 + },
376 +};
377 +module_platform_driver(mvebu_a3700_comphy_driver);
378 +
379 +MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
380 +MODULE_DESCRIPTION("Common PHY driver for A3700");
381 +MODULE_LICENSE("GPL v2");