1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2018 Marvell
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>
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) | \
54 #define COMPHY_FW_PCIE(mode, idx, speed, width) (COMPHY_FW_NET(mode, idx, speed) | \
57 struct mvebu_a3700_comphy_conf
{
65 #define MVEBU_A3700_COMPHY_CONF(_lane, _mode, _smode, _port, _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
[] = {
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
),
89 MVEBU_A3700_COMPHY_CONF_GEN(1, PHY_MODE_PCIE
, 0,
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
),
96 MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_SATA
, 0,
98 MVEBU_A3700_COMPHY_CONF_GEN(2, PHY_MODE_USB_HOST_SS
, 0,
99 COMPHY_FW_MODE_USB3H
),
102 struct mvebu_a3700_comphy_lane
{
110 static int mvebu_a3700_comphy_smc(unsigned long function
, unsigned long lane
,
113 struct arm_smccc_res res
;
116 arm_smccc_smc(function
, lane
, mode
, 0, 0, 0, 0, 0, &res
);
120 case SMCCC_RET_SUCCESS
:
122 case SMCCC_RET_NOT_SUPPORTED
:
129 static int mvebu_a3700_comphy_get_fw_mode(int lane
, int port
,
133 int i
, n
= ARRAY_SIZE(mvebu_a3700_comphy_modes
);
135 /* Unused PHY mux value is 0x0 */
136 if (mode
== PHY_MODE_INVALID
)
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
)
150 return mvebu_a3700_comphy_modes
[i
].fw_mode
;
153 static int mvebu_a3700_comphy_set_mode(struct phy
*phy
, enum phy_mode mode
,
156 struct mvebu_a3700_comphy_lane
*lane
= phy_get_drvdata(phy
);
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
,
165 dev_err(lane
->dev
, "invalid COMPHY mode\n");
169 /* Just remember the mode, ->power_on() will do the real setup */
171 lane
->submode
= submode
;
176 static int mvebu_a3700_comphy_power_on(struct phy
*phy
)
178 struct mvebu_a3700_comphy_lane
*lane
= phy_get_drvdata(phy
);
183 fw_mode
= mvebu_a3700_comphy_get_fw_mode(lane
->id
, lane
->port
,
184 lane
->mode
, lane
->submode
);
186 dev_err(lane
->dev
, "invalid COMPHY mode\n");
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
);
196 dev_dbg(lane
->dev
, "set lane %d to SATA mode\n", lane
->id
);
197 fw_param
= COMPHY_FW_MODE(fw_mode
);
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",
204 fw_param
= COMPHY_FW_NET(fw_mode
, lane
->port
,
205 COMPHY_FW_SPEED_1_25G
);
207 case PHY_INTERFACE_MODE_2500BASEX
:
208 dev_dbg(lane
->dev
, "set lane %d to HS SGMII mode\n",
210 fw_param
= COMPHY_FW_NET(fw_mode
, lane
->port
,
211 COMPHY_FW_SPEED_3_125G
);
214 dev_err(lane
->dev
, "unsupported PHY submode (%d)\n",
220 dev_dbg(lane
->dev
, "set lane %d to PCIe mode\n", lane
->id
);
221 fw_param
= COMPHY_FW_PCIE(fw_mode
, lane
->port
,
223 phy
->attrs
.bus_width
);
226 dev_err(lane
->dev
, "unsupported PHY mode (%d)\n", lane
->mode
);
230 ret
= mvebu_a3700_comphy_smc(COMPHY_SIP_POWER_ON
, lane
->id
, fw_param
);
231 if (ret
== -EOPNOTSUPP
)
233 "unsupported SMC call, try updating your firmware\n");
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
;
258 if (WARN_ON(args
->args
[0] >= MVEBU_A3700_COMPHY_PORTS
))
259 return ERR_PTR(-EINVAL
);
261 phy
= of_phy_simple_xlate(dev
, args
);
265 lane
= phy_get_drvdata(phy
);
266 lane
->port
= args
->args
[0];
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
;
282 ret
= of_property_read_u32(child
, "reg", &lane_id
);
284 dev_err(&pdev
->dev
, "missing 'reg' property (%d)\n",
289 if (lane_id
>= MVEBU_A3700_COMPHY_LANES
) {
290 dev_err(&pdev
->dev
, "invalid 'reg' property\n");
294 lane
= devm_kzalloc(&pdev
->dev
, sizeof(*lane
), GFP_KERNEL
);
300 phy
= devm_phy_create(&pdev
->dev
, child
,
301 &mvebu_a3700_comphy_ops
);
307 lane
->dev
= &pdev
->dev
;
308 lane
->mode
= PHY_MODE_INVALID
;
309 lane
->submode
= PHY_INTERFACE_MODE_NA
;
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" },
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
,
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");