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