WIP FPC-III support
[linux/fpc-iii.git] / drivers / phy / marvell / phy-mvebu-a3700-comphy.c
blob810f25a47632108ebeaf21cd45a5f47c5d4190d7
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2018 Marvell
5 * Authors:
6 * Evan Wang <xswang@marvell.com>
7 * Miquèl Raynal <miquel.raynal@bootlin.com>
9 * Structure inspired from phy-mvebu-cp110-comphy.c written by Antoine Tenart.
10 * SMC call initial support done by Grzegorz Jaszczyk.
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>
22 #define MVEBU_A3700_COMPHY_LANES 3
23 #define MVEBU_A3700_COMPHY_PORTS 2
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
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
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
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))
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;
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, \
74 #define MVEBU_A3700_COMPHY_CONF_GEN(_lane, _mode, _port, _fw) \
75 MVEBU_A3700_COMPHY_CONF(_lane, _mode, PHY_INTERFACE_MODE_NA, _port, _fw)
77 #define MVEBU_A3700_COMPHY_CONF_ETH(_lane, _smode, _port, _fw) \
78 MVEBU_A3700_COMPHY_CONF(_lane, PHY_MODE_ETHERNET, _smode, _port, _fw)
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),
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;
110 static int mvebu_a3700_comphy_smc(unsigned long function, unsigned long lane,
111 unsigned long mode)
113 struct arm_smccc_res res;
114 s32 ret;
116 arm_smccc_smc(function, lane, mode, 0, 0, 0, 0, 0, &res);
117 ret = res.a0;
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;
129 static int mvebu_a3700_comphy_get_fw_mode(int lane, int port,
130 enum phy_mode mode,
131 int submode)
133 int i, n = ARRAY_SIZE(mvebu_a3700_comphy_modes);
135 /* Unused PHY mux value is 0x0 */
136 if (mode == PHY_MODE_INVALID)
137 return -EINVAL;
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;
147 if (i == n)
148 return -EINVAL;
150 return mvebu_a3700_comphy_modes[i].fw_mode;
153 static int mvebu_a3700_comphy_set_mode(struct phy *phy, enum phy_mode mode,
154 int submode)
156 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
157 int fw_mode;
159 if (submode == PHY_INTERFACE_MODE_1000BASEX)
160 submode = PHY_INTERFACE_MODE_SGMII;
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;
169 /* Just remember the mode, ->power_on() will do the real setup */
170 lane->mode = mode;
171 lane->submode = submode;
173 return 0;
176 static int mvebu_a3700_comphy_power_on(struct phy *phy)
178 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
179 u32 fw_param;
180 int fw_mode;
181 int ret;
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;
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;
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;
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");
235 return ret;
238 static int mvebu_a3700_comphy_power_off(struct phy *phy)
240 struct mvebu_a3700_comphy_lane *lane = phy_get_drvdata(phy);
242 return mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_OFF, lane->id, 0);
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,
252 static struct phy *mvebu_a3700_comphy_xlate(struct device *dev,
253 struct of_phandle_args *args)
255 struct mvebu_a3700_comphy_lane *lane;
256 struct phy *phy;
258 if (WARN_ON(args->args[0] >= MVEBU_A3700_COMPHY_PORTS))
259 return ERR_PTR(-EINVAL);
261 phy = of_phy_simple_xlate(dev, args);
262 if (IS_ERR(phy))
263 return phy;
265 lane = phy_get_drvdata(phy);
266 lane->port = args->args[0];
268 return phy;
271 static int mvebu_a3700_comphy_probe(struct platform_device *pdev)
273 struct phy_provider *provider;
274 struct device_node *child;
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;
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;
289 if (lane_id >= MVEBU_A3700_COMPHY_LANES) {
290 dev_err(&pdev->dev, "invalid 'reg' property\n");
291 continue;
294 lane = devm_kzalloc(&pdev->dev, sizeof(*lane), GFP_KERNEL);
295 if (!lane) {
296 of_node_put(child);
297 return -ENOMEM;
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);
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);
315 provider = devm_of_phy_provider_register(&pdev->dev,
316 mvebu_a3700_comphy_xlate);
317 return PTR_ERR_OR_ZERO(provider);
320 static const struct of_device_id mvebu_a3700_comphy_of_match_table[] = {
321 { .compatible = "marvell,comphy-a3700" },
322 { },
324 MODULE_DEVICE_TABLE(of, mvebu_a3700_comphy_of_match_table);
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,
333 module_platform_driver(mvebu_a3700_comphy_driver);
335 MODULE_AUTHOR("Miquèl Raynal <miquel.raynal@bootlin.com>");
336 MODULE_DESCRIPTION("Common PHY driver for A3700");
337 MODULE_LICENSE("GPL v2");