xref: /linux/drivers/phy/qualcomm/phy-qcom-qmp-pcie-multiphy.c (revision 16e6a1a3cb3fa5fa11cd76a9d71235d927923329)
1*1781be3cSQiang Yu // SPDX-License-Identifier: GPL-2.0-only
2*1781be3cSQiang Yu /*
3*1781be3cSQiang Yu  * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
4*1781be3cSQiang Yu  */
5*1781be3cSQiang Yu 
6*1781be3cSQiang Yu #include <linux/clk.h>
7*1781be3cSQiang Yu #include <linux/clk-provider.h>
8*1781be3cSQiang Yu #include <linux/delay.h>
9*1781be3cSQiang Yu #include <linux/err.h>
10*1781be3cSQiang Yu #include <linux/io.h>
11*1781be3cSQiang Yu #include <linux/iopoll.h>
12*1781be3cSQiang Yu #include <linux/mfd/syscon.h>
13*1781be3cSQiang Yu #include <linux/module.h>
14*1781be3cSQiang Yu #include <linux/of.h>
15*1781be3cSQiang Yu #include <linux/phy/phy.h>
16*1781be3cSQiang Yu #include <linux/platform_device.h>
17*1781be3cSQiang Yu #include <linux/pm_domain.h>
18*1781be3cSQiang Yu #include <linux/pm_runtime.h>
19*1781be3cSQiang Yu #include <linux/regmap.h>
20*1781be3cSQiang Yu #include <linux/regulator/consumer.h>
21*1781be3cSQiang Yu #include <linux/reset.h>
22*1781be3cSQiang Yu #include <linux/slab.h>
23*1781be3cSQiang Yu 
24*1781be3cSQiang Yu #include "phy-qcom-qmp.h"
25*1781be3cSQiang Yu 
26*1781be3cSQiang Yu #define PHY_INIT_COMPLETE_TIMEOUT_US		10000
27*1781be3cSQiang Yu 
28*1781be3cSQiang Yu enum qmp_pcie_glymur_link_mode {
29*1781be3cSQiang Yu 	QMP_PCIE_GLYMUR_MODE_X8,
30*1781be3cSQiang Yu 	QMP_PCIE_GLYMUR_MODE_X4X4,
31*1781be3cSQiang Yu };
32*1781be3cSQiang Yu 
33*1781be3cSQiang Yu enum qphy_reg_layout {
34*1781be3cSQiang Yu 	QPHY_PCS_STATUS,
35*1781be3cSQiang Yu 	QPHY_LAYOUT_SIZE
36*1781be3cSQiang Yu };
37*1781be3cSQiang Yu 
38*1781be3cSQiang Yu static const unsigned int pciephy_v8_50_regs_layout[QPHY_LAYOUT_SIZE] = {
39*1781be3cSQiang Yu 	[QPHY_PCS_STATUS]		= QPHY_V8_50_PCS_STATUS1,
40*1781be3cSQiang Yu };
41*1781be3cSQiang Yu 
42*1781be3cSQiang Yu struct qmp_pcie_offsets {
43*1781be3cSQiang Yu 	u16 pcs;
44*1781be3cSQiang Yu };
45*1781be3cSQiang Yu 
46*1781be3cSQiang Yu struct qmp_phy_cfg {
47*1781be3cSQiang Yu 	const struct qmp_pcie_offsets *offsets;
48*1781be3cSQiang Yu 	const char * const *reg_names;
49*1781be3cSQiang Yu 	int num_regs;
50*1781be3cSQiang Yu 	const char * const *pd_names;
51*1781be3cSQiang Yu 	int num_pds;
52*1781be3cSQiang Yu 	const char * const *nocsr_reset_list;
53*1781be3cSQiang Yu 	int num_nocsr_resets;
54*1781be3cSQiang Yu 	const char * const *vreg_list;
55*1781be3cSQiang Yu 	int num_vregs;
56*1781be3cSQiang Yu 	const unsigned int *regs;
57*1781be3cSQiang Yu 	unsigned int phy_status;
58*1781be3cSQiang Yu 	const char * const *clk_list;
59*1781be3cSQiang Yu 	int num_clks;
60*1781be3cSQiang Yu 	const char * const *pipe_clk_list;
61*1781be3cSQiang Yu 	int num_pipe_clks;
62*1781be3cSQiang Yu };
63*1781be3cSQiang Yu 
64*1781be3cSQiang Yu struct qmp_pcie {
65*1781be3cSQiang Yu 	struct device *dev;
66*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg;
67*1781be3cSQiang Yu 	void __iomem **base;
68*1781be3cSQiang Yu 	struct clk_bulk_data *clks;
69*1781be3cSQiang Yu 	struct clk_bulk_data *pipe_clks;
70*1781be3cSQiang Yu 	struct reset_control_bulk_data *nocsr_resets;
71*1781be3cSQiang Yu 	struct regulator_bulk_data *vregs;
72*1781be3cSQiang Yu 	struct device **pd_devs;
73*1781be3cSQiang Yu };
74*1781be3cSQiang Yu 
75*1781be3cSQiang Yu struct qmp_pcie_multiphy {
76*1781be3cSQiang Yu 	struct phy **phys;
77*1781be3cSQiang Yu 	const struct qmp_pcie_link_mode_cfg *mode_cfg;
78*1781be3cSQiang Yu 	int num_pipe_outputs;
79*1781be3cSQiang Yu 	struct clk_fixed_rate *pipe_out_clks;
80*1781be3cSQiang Yu };
81*1781be3cSQiang Yu 
82*1781be3cSQiang Yu struct qmp_pcie_link_mode_cfg {
83*1781be3cSQiang Yu 	const struct qmp_phy_cfg * const *cfgs;
84*1781be3cSQiang Yu 	u32 num_phys;
85*1781be3cSQiang Yu };
86*1781be3cSQiang Yu 
87*1781be3cSQiang Yu struct qmp_pcie_match_data {
88*1781be3cSQiang Yu 	const struct qmp_pcie_link_mode_cfg *mode_cfgs;
89*1781be3cSQiang Yu 	u32 num_modes;
90*1781be3cSQiang Yu };
91*1781be3cSQiang Yu 
92*1781be3cSQiang Yu static const char * const glymur_pciephy_clk_l[] = {
93*1781be3cSQiang Yu 	"aux", "cfg_ahb", "ref", "rchng", "phy_b_aux",
94*1781be3cSQiang Yu };
95*1781be3cSQiang Yu 
96*1781be3cSQiang Yu static const char * const glymur_pciephy_a_clk_l[] = {
97*1781be3cSQiang Yu 	"aux", "cfg_ahb", "ref", "rchng",
98*1781be3cSQiang Yu };
99*1781be3cSQiang Yu 
100*1781be3cSQiang Yu static const char * const glymur_pciephy_b_clk_l[] = {
101*1781be3cSQiang Yu 	"phy_b_aux", "cfg_ahb_b", "ref", "rchng_b",
102*1781be3cSQiang Yu };
103*1781be3cSQiang Yu 
104*1781be3cSQiang Yu static const char * const glymur_pciephy_pipeclk_l[] = {
105*1781be3cSQiang Yu 	"pipe",
106*1781be3cSQiang Yu };
107*1781be3cSQiang Yu 
108*1781be3cSQiang Yu static const char * const glymur_pipephy_b_pipeclk_l[] = {
109*1781be3cSQiang Yu 	"pipe_b", "pipediv2_b",
110*1781be3cSQiang Yu };
111*1781be3cSQiang Yu 
112*1781be3cSQiang Yu static const char * const glymur_vreg_l[] = {
113*1781be3cSQiang Yu 	"vdda-phy", "vdda-pll", "vdda-refgen0p9", "vdda-refgen1p2",
114*1781be3cSQiang Yu };
115*1781be3cSQiang Yu 
116*1781be3cSQiang Yu static const char * const glymur_pciephy_a_reg_l[] = {
117*1781be3cSQiang Yu 	"port_a",
118*1781be3cSQiang Yu };
119*1781be3cSQiang Yu 
120*1781be3cSQiang Yu static const char * const glymur_pciephy_b_reg_l[] = {
121*1781be3cSQiang Yu 	"port_b",
122*1781be3cSQiang Yu };
123*1781be3cSQiang Yu 
124*1781be3cSQiang Yu static const char * const glymur_pciephy_reg_l[] = {
125*1781be3cSQiang Yu 	"port_a", "port_b",
126*1781be3cSQiang Yu };
127*1781be3cSQiang Yu 
128*1781be3cSQiang Yu static const char * const glymur_pciephy_a_pd_l[] = {
129*1781be3cSQiang Yu 	"port_a",
130*1781be3cSQiang Yu };
131*1781be3cSQiang Yu 
132*1781be3cSQiang Yu static const char * const glymur_pciephy_b_pd_l[] = {
133*1781be3cSQiang Yu 	"port_b",
134*1781be3cSQiang Yu };
135*1781be3cSQiang Yu 
136*1781be3cSQiang Yu static const char * const glymur_pciephy_pd_l[] = {
137*1781be3cSQiang Yu 	"port_a", "port_b",
138*1781be3cSQiang Yu };
139*1781be3cSQiang Yu 
140*1781be3cSQiang Yu static const char * const glymur_pciephy_a_nocsr_reset_l[] = {
141*1781be3cSQiang Yu 	"port_a_nocsr",
142*1781be3cSQiang Yu };
143*1781be3cSQiang Yu 
144*1781be3cSQiang Yu static const char * const glymur_pciephy_nocsr_reset_l[] = {
145*1781be3cSQiang Yu 	"port_a_nocsr", "port_b_nocsr",
146*1781be3cSQiang Yu };
147*1781be3cSQiang Yu 
148*1781be3cSQiang Yu static const char * const glymur_pciephy_b_nocsr_reset_l[] = {
149*1781be3cSQiang Yu 	"port_b_nocsr",
150*1781be3cSQiang Yu };
151*1781be3cSQiang Yu 
152*1781be3cSQiang Yu static const struct qmp_pcie_offsets glymur_pcie_offsets_v8_50 = {
153*1781be3cSQiang Yu 	.pcs		= 0x9000,
154*1781be3cSQiang Yu };
155*1781be3cSQiang Yu 
156*1781be3cSQiang Yu static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_a_cfg = {
157*1781be3cSQiang Yu 	.offsets		= &glymur_pcie_offsets_v8_50,
158*1781be3cSQiang Yu 	.reg_names		= glymur_pciephy_a_reg_l,
159*1781be3cSQiang Yu 	.num_regs		= ARRAY_SIZE(glymur_pciephy_a_reg_l),
160*1781be3cSQiang Yu 	.pd_names		= glymur_pciephy_a_pd_l,
161*1781be3cSQiang Yu 	.num_pds		= ARRAY_SIZE(glymur_pciephy_a_pd_l),
162*1781be3cSQiang Yu 	.nocsr_reset_list	= glymur_pciephy_a_nocsr_reset_l,
163*1781be3cSQiang Yu 	.num_nocsr_resets	= ARRAY_SIZE(glymur_pciephy_a_nocsr_reset_l),
164*1781be3cSQiang Yu 	.vreg_list		= glymur_vreg_l,
165*1781be3cSQiang Yu 	.num_vregs		= ARRAY_SIZE(glymur_vreg_l),
166*1781be3cSQiang Yu 	.regs			= pciephy_v8_50_regs_layout,
167*1781be3cSQiang Yu 	.phy_status		= PHYSTATUS_4_20,
168*1781be3cSQiang Yu 	.pipe_clk_list		= glymur_pciephy_pipeclk_l,
169*1781be3cSQiang Yu 	.num_pipe_clks		= ARRAY_SIZE(glymur_pciephy_pipeclk_l),
170*1781be3cSQiang Yu 	.clk_list		= glymur_pciephy_a_clk_l,
171*1781be3cSQiang Yu 	.num_clks		= ARRAY_SIZE(glymur_pciephy_a_clk_l),
172*1781be3cSQiang Yu };
173*1781be3cSQiang Yu 
174*1781be3cSQiang Yu static const struct qmp_phy_cfg glymur_qmp_gen5x4_pciephy_b_cfg = {
175*1781be3cSQiang Yu 	.offsets		= &glymur_pcie_offsets_v8_50,
176*1781be3cSQiang Yu 	.reg_names		= glymur_pciephy_b_reg_l,
177*1781be3cSQiang Yu 	.num_regs		= ARRAY_SIZE(glymur_pciephy_b_reg_l),
178*1781be3cSQiang Yu 	.pd_names		= glymur_pciephy_b_pd_l,
179*1781be3cSQiang Yu 	.num_pds		= ARRAY_SIZE(glymur_pciephy_b_pd_l),
180*1781be3cSQiang Yu 	.nocsr_reset_list	= glymur_pciephy_b_nocsr_reset_l,
181*1781be3cSQiang Yu 	.num_nocsr_resets	= ARRAY_SIZE(glymur_pciephy_b_nocsr_reset_l),
182*1781be3cSQiang Yu 	.vreg_list		= glymur_vreg_l,
183*1781be3cSQiang Yu 	.num_vregs		= ARRAY_SIZE(glymur_vreg_l),
184*1781be3cSQiang Yu 	.regs			= pciephy_v8_50_regs_layout,
185*1781be3cSQiang Yu 	.phy_status		= PHYSTATUS_4_20,
186*1781be3cSQiang Yu 	.pipe_clk_list		= glymur_pipephy_b_pipeclk_l,
187*1781be3cSQiang Yu 	.num_pipe_clks		= ARRAY_SIZE(glymur_pipephy_b_pipeclk_l),
188*1781be3cSQiang Yu 	.clk_list		= glymur_pciephy_b_clk_l,
189*1781be3cSQiang Yu 	.num_clks		= ARRAY_SIZE(glymur_pciephy_b_clk_l),
190*1781be3cSQiang Yu };
191*1781be3cSQiang Yu 
192*1781be3cSQiang Yu static const struct qmp_phy_cfg glymur_qmp_gen5x8_pciephy_cfg = {
193*1781be3cSQiang Yu 	.offsets		= &glymur_pcie_offsets_v8_50,
194*1781be3cSQiang Yu 	.reg_names		= glymur_pciephy_reg_l,
195*1781be3cSQiang Yu 	.num_regs		= ARRAY_SIZE(glymur_pciephy_reg_l),
196*1781be3cSQiang Yu 	.pd_names		= glymur_pciephy_pd_l,
197*1781be3cSQiang Yu 	.num_pds		= ARRAY_SIZE(glymur_pciephy_pd_l),
198*1781be3cSQiang Yu 	.nocsr_reset_list	= glymur_pciephy_nocsr_reset_l,
199*1781be3cSQiang Yu 	.num_nocsr_resets	= ARRAY_SIZE(glymur_pciephy_nocsr_reset_l),
200*1781be3cSQiang Yu 	.vreg_list		= glymur_vreg_l,
201*1781be3cSQiang Yu 	.num_vregs		= ARRAY_SIZE(glymur_vreg_l),
202*1781be3cSQiang Yu 	.regs			= pciephy_v8_50_regs_layout,
203*1781be3cSQiang Yu 	.phy_status		= PHYSTATUS_4_20,
204*1781be3cSQiang Yu 	.pipe_clk_list		= glymur_pciephy_pipeclk_l,
205*1781be3cSQiang Yu 	.num_pipe_clks		= ARRAY_SIZE(glymur_pciephy_pipeclk_l),
206*1781be3cSQiang Yu 	.clk_list		= glymur_pciephy_clk_l,
207*1781be3cSQiang Yu 	.num_clks		= ARRAY_SIZE(glymur_pciephy_clk_l),
208*1781be3cSQiang Yu };
209*1781be3cSQiang Yu 
210*1781be3cSQiang Yu static const struct qmp_phy_cfg * const glymur_qmp_gen5x8_mode_x8_cfgs[] = {
211*1781be3cSQiang Yu 	&glymur_qmp_gen5x8_pciephy_cfg,
212*1781be3cSQiang Yu };
213*1781be3cSQiang Yu 
214*1781be3cSQiang Yu static const struct qmp_phy_cfg * const glymur_qmp_gen5x8_mode_x4x4_cfgs[] = {
215*1781be3cSQiang Yu 	&glymur_qmp_gen5x4_pciephy_a_cfg,
216*1781be3cSQiang Yu 	&glymur_qmp_gen5x4_pciephy_b_cfg,
217*1781be3cSQiang Yu };
218*1781be3cSQiang Yu 
219*1781be3cSQiang Yu static const struct qmp_pcie_link_mode_cfg glymur_qmp_gen5x8_mode_cfgs[] = {
220*1781be3cSQiang Yu 	[QMP_PCIE_GLYMUR_MODE_X8] = {
221*1781be3cSQiang Yu 		.cfgs		= glymur_qmp_gen5x8_mode_x8_cfgs,
222*1781be3cSQiang Yu 		.num_phys	= ARRAY_SIZE(glymur_qmp_gen5x8_mode_x8_cfgs),
223*1781be3cSQiang Yu 	},
224*1781be3cSQiang Yu 	[QMP_PCIE_GLYMUR_MODE_X4X4] = {
225*1781be3cSQiang Yu 		.cfgs		= glymur_qmp_gen5x8_mode_x4x4_cfgs,
226*1781be3cSQiang Yu 		.num_phys	= ARRAY_SIZE(glymur_qmp_gen5x8_mode_x4x4_cfgs),
227*1781be3cSQiang Yu 	},
228*1781be3cSQiang Yu };
229*1781be3cSQiang Yu 
230*1781be3cSQiang Yu static const struct qmp_pcie_match_data glymur_qmp_gen5x8_match_data = {
231*1781be3cSQiang Yu 	.mode_cfgs	= glymur_qmp_gen5x8_mode_cfgs,
232*1781be3cSQiang Yu 	.num_modes	= ARRAY_SIZE(glymur_qmp_gen5x8_mode_cfgs),
233*1781be3cSQiang Yu };
234*1781be3cSQiang Yu 
qmp_pcie_pd_power_on(struct qmp_pcie * qmp)235*1781be3cSQiang Yu static int qmp_pcie_pd_power_on(struct qmp_pcie *qmp)
236*1781be3cSQiang Yu {
237*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
238*1781be3cSQiang Yu 	int i, ret;
239*1781be3cSQiang Yu 
240*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_pds; i++) {
241*1781be3cSQiang Yu 		ret = pm_runtime_resume_and_get(qmp->pd_devs[i]);
242*1781be3cSQiang Yu 		if (ret < 0) {
243*1781be3cSQiang Yu 			dev_err(qmp->dev, "failed to power on %s domain: %d\n",
244*1781be3cSQiang Yu 				cfg->pd_names[i], ret);
245*1781be3cSQiang Yu 			goto err_power_off;
246*1781be3cSQiang Yu 		}
247*1781be3cSQiang Yu 	}
248*1781be3cSQiang Yu 
249*1781be3cSQiang Yu 	return 0;
250*1781be3cSQiang Yu 
251*1781be3cSQiang Yu err_power_off:
252*1781be3cSQiang Yu 	while (--i >= 0)
253*1781be3cSQiang Yu 		pm_runtime_put(qmp->pd_devs[i]);
254*1781be3cSQiang Yu 
255*1781be3cSQiang Yu 	return ret;
256*1781be3cSQiang Yu }
257*1781be3cSQiang Yu 
qmp_pcie_pd_power_off(struct qmp_pcie * qmp)258*1781be3cSQiang Yu static void qmp_pcie_pd_power_off(struct qmp_pcie *qmp)
259*1781be3cSQiang Yu {
260*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
261*1781be3cSQiang Yu 	int i;
262*1781be3cSQiang Yu 
263*1781be3cSQiang Yu 	for (i = cfg->num_pds - 1; i >= 0; i--)
264*1781be3cSQiang Yu 		pm_runtime_put(qmp->pd_devs[i]);
265*1781be3cSQiang Yu }
266*1781be3cSQiang Yu 
qmp_pcie_init(struct phy * phy)267*1781be3cSQiang Yu static int qmp_pcie_init(struct phy *phy)
268*1781be3cSQiang Yu {
269*1781be3cSQiang Yu 	struct qmp_pcie *qmp = phy_get_drvdata(phy);
270*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
271*1781be3cSQiang Yu 	int ret;
272*1781be3cSQiang Yu 
273*1781be3cSQiang Yu 	ret = qmp_pcie_pd_power_on(qmp);
274*1781be3cSQiang Yu 	if (ret)
275*1781be3cSQiang Yu 		return ret;
276*1781be3cSQiang Yu 
277*1781be3cSQiang Yu 	ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs);
278*1781be3cSQiang Yu 	if (ret) {
279*1781be3cSQiang Yu 		dev_err(qmp->dev, "failed to enable regulators: %d\n", ret);
280*1781be3cSQiang Yu 		goto err_pd_power_off;
281*1781be3cSQiang Yu 	}
282*1781be3cSQiang Yu 
283*1781be3cSQiang Yu 	ret = reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
284*1781be3cSQiang Yu 	if (ret) {
285*1781be3cSQiang Yu 		dev_err(qmp->dev, "no-csr reset assert failed: %d\n", ret);
286*1781be3cSQiang Yu 		goto err_disable_regulators;
287*1781be3cSQiang Yu 	}
288*1781be3cSQiang Yu 
289*1781be3cSQiang Yu 	usleep_range(200, 300);
290*1781be3cSQiang Yu 
291*1781be3cSQiang Yu 	ret = clk_bulk_prepare_enable(qmp->cfg->num_clks, qmp->clks);
292*1781be3cSQiang Yu 	if (ret)
293*1781be3cSQiang Yu 		goto err_disable_regulators;
294*1781be3cSQiang Yu 
295*1781be3cSQiang Yu 	return 0;
296*1781be3cSQiang Yu 
297*1781be3cSQiang Yu err_disable_regulators:
298*1781be3cSQiang Yu 	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
299*1781be3cSQiang Yu err_pd_power_off:
300*1781be3cSQiang Yu 	qmp_pcie_pd_power_off(qmp);
301*1781be3cSQiang Yu 
302*1781be3cSQiang Yu 	return ret;
303*1781be3cSQiang Yu }
304*1781be3cSQiang Yu 
qmp_pcie_exit(struct phy * phy)305*1781be3cSQiang Yu static int qmp_pcie_exit(struct phy *phy)
306*1781be3cSQiang Yu {
307*1781be3cSQiang Yu 	struct qmp_pcie *qmp = phy_get_drvdata(phy);
308*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
309*1781be3cSQiang Yu 
310*1781be3cSQiang Yu 	reset_control_bulk_assert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
311*1781be3cSQiang Yu 
312*1781be3cSQiang Yu 	clk_bulk_disable_unprepare(qmp->cfg->num_clks, qmp->clks);
313*1781be3cSQiang Yu 	regulator_bulk_disable(cfg->num_vregs, qmp->vregs);
314*1781be3cSQiang Yu 	qmp_pcie_pd_power_off(qmp);
315*1781be3cSQiang Yu 
316*1781be3cSQiang Yu 	return 0;
317*1781be3cSQiang Yu }
318*1781be3cSQiang Yu 
qmp_pcie_power_on(struct phy * phy)319*1781be3cSQiang Yu static int qmp_pcie_power_on(struct phy *phy)
320*1781be3cSQiang Yu {
321*1781be3cSQiang Yu 	struct qmp_pcie *qmp = phy_get_drvdata(phy);
322*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
323*1781be3cSQiang Yu 	const struct qmp_pcie_offsets *offs = cfg->offsets;
324*1781be3cSQiang Yu 	void __iomem *status;
325*1781be3cSQiang Yu 	unsigned int val;
326*1781be3cSQiang Yu 	int i, ret;
327*1781be3cSQiang Yu 
328*1781be3cSQiang Yu 	ret = clk_bulk_prepare_enable(qmp->cfg->num_pipe_clks, qmp->pipe_clks);
329*1781be3cSQiang Yu 	if (ret)
330*1781be3cSQiang Yu 		return ret;
331*1781be3cSQiang Yu 
332*1781be3cSQiang Yu 	ret = reset_control_bulk_deassert(qmp->cfg->num_nocsr_resets, qmp->nocsr_resets);
333*1781be3cSQiang Yu 	if (ret) {
334*1781be3cSQiang Yu 		dev_err(qmp->dev, "no-csr reset deassert failed: %d\n", ret);
335*1781be3cSQiang Yu 		goto err_disable_pipe_clk;
336*1781be3cSQiang Yu 	}
337*1781be3cSQiang Yu 
338*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_regs; i++) {
339*1781be3cSQiang Yu 		status = qmp->base[i] + offs->pcs + cfg->regs[QPHY_PCS_STATUS];
340*1781be3cSQiang Yu 		ret = readl_poll_timeout(status, val, !(val & cfg->phy_status), 200,
341*1781be3cSQiang Yu 					 PHY_INIT_COMPLETE_TIMEOUT_US);
342*1781be3cSQiang Yu 		if (ret) {
343*1781be3cSQiang Yu 			dev_err(qmp->dev, "PHY power on timed-out (%s): %d\n",
344*1781be3cSQiang Yu 				cfg->reg_names[i], ret);
345*1781be3cSQiang Yu 			goto err_disable_pipe_clk;
346*1781be3cSQiang Yu 		}
347*1781be3cSQiang Yu 	}
348*1781be3cSQiang Yu 
349*1781be3cSQiang Yu 	return 0;
350*1781be3cSQiang Yu 
351*1781be3cSQiang Yu err_disable_pipe_clk:
352*1781be3cSQiang Yu 	clk_bulk_disable_unprepare(qmp->cfg->num_pipe_clks, qmp->pipe_clks);
353*1781be3cSQiang Yu 
354*1781be3cSQiang Yu 	return ret;
355*1781be3cSQiang Yu }
356*1781be3cSQiang Yu 
qmp_pcie_power_off(struct phy * phy)357*1781be3cSQiang Yu static int qmp_pcie_power_off(struct phy *phy)
358*1781be3cSQiang Yu {
359*1781be3cSQiang Yu 	struct qmp_pcie *qmp = phy_get_drvdata(phy);
360*1781be3cSQiang Yu 
361*1781be3cSQiang Yu 	clk_bulk_disable_unprepare(qmp->cfg->num_pipe_clks, qmp->pipe_clks);
362*1781be3cSQiang Yu 
363*1781be3cSQiang Yu 	return 0;
364*1781be3cSQiang Yu }
365*1781be3cSQiang Yu 
qmp_pcie_enable(struct phy * phy)366*1781be3cSQiang Yu static int qmp_pcie_enable(struct phy *phy)
367*1781be3cSQiang Yu {
368*1781be3cSQiang Yu 	int ret;
369*1781be3cSQiang Yu 
370*1781be3cSQiang Yu 	ret = qmp_pcie_init(phy);
371*1781be3cSQiang Yu 	if (ret)
372*1781be3cSQiang Yu 		return ret;
373*1781be3cSQiang Yu 
374*1781be3cSQiang Yu 	ret = qmp_pcie_power_on(phy);
375*1781be3cSQiang Yu 	if (ret)
376*1781be3cSQiang Yu 		qmp_pcie_exit(phy);
377*1781be3cSQiang Yu 
378*1781be3cSQiang Yu 	return ret;
379*1781be3cSQiang Yu }
380*1781be3cSQiang Yu 
qmp_pcie_disable(struct phy * phy)381*1781be3cSQiang Yu static int qmp_pcie_disable(struct phy *phy)
382*1781be3cSQiang Yu {
383*1781be3cSQiang Yu 	int ret;
384*1781be3cSQiang Yu 
385*1781be3cSQiang Yu 	ret = qmp_pcie_power_off(phy);
386*1781be3cSQiang Yu 	if (ret)
387*1781be3cSQiang Yu 		return ret;
388*1781be3cSQiang Yu 
389*1781be3cSQiang Yu 	return qmp_pcie_exit(phy);
390*1781be3cSQiang Yu }
391*1781be3cSQiang Yu 
392*1781be3cSQiang Yu static const struct phy_ops qmp_pcie_phy_ops = {
393*1781be3cSQiang Yu 	.power_on	= qmp_pcie_enable,
394*1781be3cSQiang Yu 	.power_off	= qmp_pcie_disable,
395*1781be3cSQiang Yu 	.owner		= THIS_MODULE,
396*1781be3cSQiang Yu };
397*1781be3cSQiang Yu 
qmp_pcie_pd_detach(void * data)398*1781be3cSQiang Yu static void qmp_pcie_pd_detach(void *data)
399*1781be3cSQiang Yu {
400*1781be3cSQiang Yu 	struct qmp_pcie *qmp = data;
401*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
402*1781be3cSQiang Yu 	int i;
403*1781be3cSQiang Yu 
404*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_pds; i++) {
405*1781be3cSQiang Yu 		if (!IS_ERR_OR_NULL(qmp->pd_devs[i]))
406*1781be3cSQiang Yu 			dev_pm_domain_detach(qmp->pd_devs[i], true);
407*1781be3cSQiang Yu 	}
408*1781be3cSQiang Yu }
409*1781be3cSQiang Yu 
qmp_pcie_pd_init(struct qmp_pcie * qmp)410*1781be3cSQiang Yu static int qmp_pcie_pd_init(struct qmp_pcie *qmp)
411*1781be3cSQiang Yu {
412*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
413*1781be3cSQiang Yu 	struct device *dev = qmp->dev;
414*1781be3cSQiang Yu 	int i, ret;
415*1781be3cSQiang Yu 
416*1781be3cSQiang Yu 	if (!cfg->num_pds)
417*1781be3cSQiang Yu 		return 0;
418*1781be3cSQiang Yu 
419*1781be3cSQiang Yu 	qmp->pd_devs = devm_kcalloc(dev, cfg->num_pds, sizeof(*qmp->pd_devs),
420*1781be3cSQiang Yu 				    GFP_KERNEL);
421*1781be3cSQiang Yu 	if (!qmp->pd_devs)
422*1781be3cSQiang Yu 		return -ENOMEM;
423*1781be3cSQiang Yu 
424*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_pds; i++) {
425*1781be3cSQiang Yu 		qmp->pd_devs[i] = dev_pm_domain_attach_by_name(dev,
426*1781be3cSQiang Yu 							       cfg->pd_names[i]);
427*1781be3cSQiang Yu 		if (IS_ERR_OR_NULL(qmp->pd_devs[i])) {
428*1781be3cSQiang Yu 			ret = PTR_ERR(qmp->pd_devs[i]) ? : -ENODATA;
429*1781be3cSQiang Yu 			goto err_detach;
430*1781be3cSQiang Yu 		}
431*1781be3cSQiang Yu 	}
432*1781be3cSQiang Yu 
433*1781be3cSQiang Yu 	return devm_add_action_or_reset(dev, qmp_pcie_pd_detach, qmp);
434*1781be3cSQiang Yu 
435*1781be3cSQiang Yu err_detach:
436*1781be3cSQiang Yu 	while (--i >= 0)
437*1781be3cSQiang Yu 		dev_pm_domain_detach(qmp->pd_devs[i], false);
438*1781be3cSQiang Yu 
439*1781be3cSQiang Yu 	return ret;
440*1781be3cSQiang Yu }
441*1781be3cSQiang Yu 
qmp_pcie_vreg_init(struct qmp_pcie * qmp)442*1781be3cSQiang Yu static int qmp_pcie_vreg_init(struct qmp_pcie *qmp)
443*1781be3cSQiang Yu {
444*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
445*1781be3cSQiang Yu 	struct device *dev = qmp->dev;
446*1781be3cSQiang Yu 	int i;
447*1781be3cSQiang Yu 
448*1781be3cSQiang Yu 	qmp->vregs = devm_kcalloc(dev, cfg->num_vregs, sizeof(*qmp->vregs),
449*1781be3cSQiang Yu 				  GFP_KERNEL);
450*1781be3cSQiang Yu 	if (!qmp->vregs)
451*1781be3cSQiang Yu 		return -ENOMEM;
452*1781be3cSQiang Yu 
453*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_vregs; i++)
454*1781be3cSQiang Yu 		qmp->vregs[i].supply = cfg->vreg_list[i];
455*1781be3cSQiang Yu 
456*1781be3cSQiang Yu 	return devm_regulator_bulk_get(dev, cfg->num_vregs, qmp->vregs);
457*1781be3cSQiang Yu }
458*1781be3cSQiang Yu 
qmp_pcie_reset_init(struct qmp_pcie * qmp)459*1781be3cSQiang Yu static int qmp_pcie_reset_init(struct qmp_pcie *qmp)
460*1781be3cSQiang Yu {
461*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
462*1781be3cSQiang Yu 	struct device *dev = qmp->dev;
463*1781be3cSQiang Yu 	int i, ret;
464*1781be3cSQiang Yu 
465*1781be3cSQiang Yu 	qmp->nocsr_resets = devm_kcalloc(dev, cfg->num_nocsr_resets,
466*1781be3cSQiang Yu 					 sizeof(*qmp->nocsr_resets),
467*1781be3cSQiang Yu 					 GFP_KERNEL);
468*1781be3cSQiang Yu 	if (!qmp->nocsr_resets)
469*1781be3cSQiang Yu 		return -ENOMEM;
470*1781be3cSQiang Yu 
471*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_nocsr_resets; i++)
472*1781be3cSQiang Yu 		qmp->nocsr_resets[i].id = cfg->nocsr_reset_list[i];
473*1781be3cSQiang Yu 
474*1781be3cSQiang Yu 	ret = devm_reset_control_bulk_get_exclusive(dev,
475*1781be3cSQiang Yu 						    cfg->num_nocsr_resets,
476*1781be3cSQiang Yu 						    qmp->nocsr_resets);
477*1781be3cSQiang Yu 	if (ret)
478*1781be3cSQiang Yu 		return dev_err_probe(dev, ret, "failed to get no-csr resets\n");
479*1781be3cSQiang Yu 
480*1781be3cSQiang Yu 	return 0;
481*1781be3cSQiang Yu }
482*1781be3cSQiang Yu 
qmp_pcie_clk_init(struct qmp_pcie * qmp)483*1781be3cSQiang Yu static int qmp_pcie_clk_init(struct qmp_pcie *qmp)
484*1781be3cSQiang Yu {
485*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
486*1781be3cSQiang Yu 	struct device *dev = qmp->dev;
487*1781be3cSQiang Yu 	int i, ret;
488*1781be3cSQiang Yu 
489*1781be3cSQiang Yu 	qmp->clks = devm_kcalloc(dev, cfg->num_clks, sizeof(*qmp->clks),
490*1781be3cSQiang Yu 				 GFP_KERNEL);
491*1781be3cSQiang Yu 	if (!qmp->clks)
492*1781be3cSQiang Yu 		return -ENOMEM;
493*1781be3cSQiang Yu 
494*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_clks; i++)
495*1781be3cSQiang Yu 		qmp->clks[i].id = cfg->clk_list[i];
496*1781be3cSQiang Yu 
497*1781be3cSQiang Yu 	ret = devm_clk_bulk_get_optional(dev, cfg->num_clks, qmp->clks);
498*1781be3cSQiang Yu 	if (ret)
499*1781be3cSQiang Yu 		return ret;
500*1781be3cSQiang Yu 
501*1781be3cSQiang Yu 	qmp->pipe_clks = devm_kcalloc(dev, cfg->num_pipe_clks,
502*1781be3cSQiang Yu 				      sizeof(*qmp->pipe_clks), GFP_KERNEL);
503*1781be3cSQiang Yu 	if (!qmp->pipe_clks)
504*1781be3cSQiang Yu 		return -ENOMEM;
505*1781be3cSQiang Yu 
506*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_pipe_clks; i++)
507*1781be3cSQiang Yu 		qmp->pipe_clks[i].id = cfg->pipe_clk_list[i];
508*1781be3cSQiang Yu 
509*1781be3cSQiang Yu 	return devm_clk_bulk_get_optional(dev, cfg->num_pipe_clks,
510*1781be3cSQiang Yu 					  qmp->pipe_clks);
511*1781be3cSQiang Yu }
512*1781be3cSQiang Yu 
__phy_pipe_clk_register(struct device * dev,struct device_node * np,int idx,struct clk_fixed_rate * fixed)513*1781be3cSQiang Yu static int __phy_pipe_clk_register(struct device *dev, struct device_node *np,
514*1781be3cSQiang Yu 				   int idx, struct clk_fixed_rate *fixed)
515*1781be3cSQiang Yu {
516*1781be3cSQiang Yu 	struct clk_init_data init = { };
517*1781be3cSQiang Yu 	int ret;
518*1781be3cSQiang Yu 
519*1781be3cSQiang Yu 	ret = of_property_read_string_index(np, "clock-output-names", idx,
520*1781be3cSQiang Yu 					    &init.name);
521*1781be3cSQiang Yu 	if (ret) {
522*1781be3cSQiang Yu 		dev_err(dev, "%pOFn: No clock-output-names\n", np);
523*1781be3cSQiang Yu 		return ret;
524*1781be3cSQiang Yu 	}
525*1781be3cSQiang Yu 
526*1781be3cSQiang Yu 	init.ops = &clk_fixed_rate_ops;
527*1781be3cSQiang Yu 
528*1781be3cSQiang Yu 	fixed->fixed_rate = 125000000;
529*1781be3cSQiang Yu 
530*1781be3cSQiang Yu 	fixed->hw.init = &init;
531*1781be3cSQiang Yu 
532*1781be3cSQiang Yu 	return devm_clk_hw_register(dev, &fixed->hw);
533*1781be3cSQiang Yu }
534*1781be3cSQiang Yu 
535*1781be3cSQiang Yu static struct clk_hw *
qmp_pcie_multiphy_clk_hw_get(struct of_phandle_args * clkspec,void * data)536*1781be3cSQiang Yu qmp_pcie_multiphy_clk_hw_get(struct of_phandle_args *clkspec, void *data)
537*1781be3cSQiang Yu {
538*1781be3cSQiang Yu 	struct qmp_pcie_multiphy *qmp_data = data;
539*1781be3cSQiang Yu 	unsigned int idx = 0;
540*1781be3cSQiang Yu 
541*1781be3cSQiang Yu 	if (clkspec->args_count)
542*1781be3cSQiang Yu 		idx = clkspec->args[0];
543*1781be3cSQiang Yu 
544*1781be3cSQiang Yu 	if (idx < (unsigned int)qmp_data->num_pipe_outputs)
545*1781be3cSQiang Yu 		return &qmp_data->pipe_out_clks[idx].hw;
546*1781be3cSQiang Yu 
547*1781be3cSQiang Yu 	return ERR_PTR(-EINVAL);
548*1781be3cSQiang Yu }
549*1781be3cSQiang Yu 
qmp_pcie_multiphy_register_clocks(struct device * dev,struct device_node * np,struct qmp_pcie_multiphy * qmp_data)550*1781be3cSQiang Yu static int qmp_pcie_multiphy_register_clocks(struct device *dev,
551*1781be3cSQiang Yu 					     struct device_node *np,
552*1781be3cSQiang Yu 					     struct qmp_pcie_multiphy *qmp_data)
553*1781be3cSQiang Yu {
554*1781be3cSQiang Yu 	int num_pipe_outputs;
555*1781be3cSQiang Yu 	int i, ret;
556*1781be3cSQiang Yu 
557*1781be3cSQiang Yu 	num_pipe_outputs = of_property_count_strings(np, "clock-output-names");
558*1781be3cSQiang Yu 
559*1781be3cSQiang Yu 	qmp_data->num_pipe_outputs = num_pipe_outputs;
560*1781be3cSQiang Yu 	qmp_data->pipe_out_clks = devm_kcalloc(dev, num_pipe_outputs,
561*1781be3cSQiang Yu 					       sizeof(*qmp_data->pipe_out_clks),
562*1781be3cSQiang Yu 					       GFP_KERNEL);
563*1781be3cSQiang Yu 	if (!qmp_data->pipe_out_clks)
564*1781be3cSQiang Yu 		return -ENOMEM;
565*1781be3cSQiang Yu 
566*1781be3cSQiang Yu 	for (i = 0; i < num_pipe_outputs; i++) {
567*1781be3cSQiang Yu 		ret = __phy_pipe_clk_register(dev, np, i,
568*1781be3cSQiang Yu 					      &qmp_data->pipe_out_clks[i]);
569*1781be3cSQiang Yu 		if (ret)
570*1781be3cSQiang Yu 			return ret;
571*1781be3cSQiang Yu 	}
572*1781be3cSQiang Yu 
573*1781be3cSQiang Yu 	return devm_of_clk_add_hw_provider(dev, qmp_pcie_multiphy_clk_hw_get, qmp_data);
574*1781be3cSQiang Yu }
575*1781be3cSQiang Yu 
qmp_pcie_get_mmio(struct qmp_pcie * qmp)576*1781be3cSQiang Yu static int qmp_pcie_get_mmio(struct qmp_pcie *qmp)
577*1781be3cSQiang Yu {
578*1781be3cSQiang Yu 	struct platform_device *pdev = to_platform_device(qmp->dev);
579*1781be3cSQiang Yu 	const struct qmp_phy_cfg *cfg = qmp->cfg;
580*1781be3cSQiang Yu 	struct device *dev = qmp->dev;
581*1781be3cSQiang Yu 	void __iomem *base;
582*1781be3cSQiang Yu 	int i;
583*1781be3cSQiang Yu 
584*1781be3cSQiang Yu 	qmp->base = devm_kcalloc(dev, cfg->num_regs, sizeof(*qmp->base),
585*1781be3cSQiang Yu 				 GFP_KERNEL);
586*1781be3cSQiang Yu 	if (!qmp->base)
587*1781be3cSQiang Yu 		return -ENOMEM;
588*1781be3cSQiang Yu 
589*1781be3cSQiang Yu 	for (i = 0; i < cfg->num_regs; i++) {
590*1781be3cSQiang Yu 		base = devm_platform_ioremap_resource_byname(pdev, cfg->reg_names[i]);
591*1781be3cSQiang Yu 		if (IS_ERR(base))
592*1781be3cSQiang Yu 			return PTR_ERR(base);
593*1781be3cSQiang Yu 
594*1781be3cSQiang Yu 		qmp->base[i] = base;
595*1781be3cSQiang Yu 	}
596*1781be3cSQiang Yu 
597*1781be3cSQiang Yu 	return 0;
598*1781be3cSQiang Yu }
599*1781be3cSQiang Yu 
qmp_pcie_read_link_mode(struct device * dev,unsigned int * link_mode)600*1781be3cSQiang Yu static int qmp_pcie_read_link_mode(struct device *dev, unsigned int *link_mode)
601*1781be3cSQiang Yu {
602*1781be3cSQiang Yu 	struct regmap *map;
603*1781be3cSQiang Yu 	unsigned int args[1];
604*1781be3cSQiang Yu 
605*1781be3cSQiang Yu 	map = syscon_regmap_lookup_by_phandle_args(dev->of_node, "qcom,link-mode",
606*1781be3cSQiang Yu 						   ARRAY_SIZE(args), args);
607*1781be3cSQiang Yu 	if (IS_ERR(map))
608*1781be3cSQiang Yu 		return PTR_ERR(map);
609*1781be3cSQiang Yu 
610*1781be3cSQiang Yu 	return regmap_read(map, args[0], link_mode);
611*1781be3cSQiang Yu }
612*1781be3cSQiang Yu 
qmp_pcie_multiphy_xlate(struct device * dev,const struct of_phandle_args * args)613*1781be3cSQiang Yu static struct phy *qmp_pcie_multiphy_xlate(struct device *dev,
614*1781be3cSQiang Yu 					   const struct of_phandle_args *args)
615*1781be3cSQiang Yu {
616*1781be3cSQiang Yu 	struct qmp_pcie_multiphy *qmp_data = dev_get_drvdata(dev);
617*1781be3cSQiang Yu 	unsigned int idx;
618*1781be3cSQiang Yu 
619*1781be3cSQiang Yu 	if (!qmp_data || args->args_count < 1)
620*1781be3cSQiang Yu 		return ERR_PTR(-EINVAL);
621*1781be3cSQiang Yu 
622*1781be3cSQiang Yu 	idx = args->args[0];
623*1781be3cSQiang Yu 
624*1781be3cSQiang Yu 	if (idx < (unsigned int)qmp_data->mode_cfg->num_phys)
625*1781be3cSQiang Yu 		return qmp_data->phys[idx] ?: ERR_PTR(-EINVAL);
626*1781be3cSQiang Yu 
627*1781be3cSQiang Yu 	return ERR_PTR(-EINVAL);
628*1781be3cSQiang Yu }
629*1781be3cSQiang Yu 
qmp_pcie_probe_phy(struct qmp_pcie * qmp,struct device_node * np,struct phy ** out_phy)630*1781be3cSQiang Yu static int qmp_pcie_probe_phy(struct qmp_pcie *qmp, struct device_node *np,
631*1781be3cSQiang Yu 			      struct phy **out_phy)
632*1781be3cSQiang Yu {
633*1781be3cSQiang Yu 	int ret;
634*1781be3cSQiang Yu 
635*1781be3cSQiang Yu 	ret = qmp_pcie_get_mmio(qmp);
636*1781be3cSQiang Yu 	if (ret)
637*1781be3cSQiang Yu 		return ret;
638*1781be3cSQiang Yu 
639*1781be3cSQiang Yu 	ret = qmp_pcie_clk_init(qmp);
640*1781be3cSQiang Yu 	if (ret)
641*1781be3cSQiang Yu 		return ret;
642*1781be3cSQiang Yu 
643*1781be3cSQiang Yu 	ret = qmp_pcie_reset_init(qmp);
644*1781be3cSQiang Yu 	if (ret)
645*1781be3cSQiang Yu 		return ret;
646*1781be3cSQiang Yu 
647*1781be3cSQiang Yu 	ret = qmp_pcie_vreg_init(qmp);
648*1781be3cSQiang Yu 	if (ret)
649*1781be3cSQiang Yu 		return ret;
650*1781be3cSQiang Yu 
651*1781be3cSQiang Yu 	ret = qmp_pcie_pd_init(qmp);
652*1781be3cSQiang Yu 	if (ret)
653*1781be3cSQiang Yu 		return ret;
654*1781be3cSQiang Yu 
655*1781be3cSQiang Yu 	*out_phy = devm_phy_create(qmp->dev, np, &qmp_pcie_phy_ops);
656*1781be3cSQiang Yu 	if (IS_ERR(*out_phy))
657*1781be3cSQiang Yu 		return PTR_ERR(*out_phy);
658*1781be3cSQiang Yu 
659*1781be3cSQiang Yu 	phy_set_drvdata(*out_phy, qmp);
660*1781be3cSQiang Yu 
661*1781be3cSQiang Yu 	return 0;
662*1781be3cSQiang Yu }
663*1781be3cSQiang Yu 
qmp_pcie_multiphy_probe(struct platform_device * pdev)664*1781be3cSQiang Yu static int qmp_pcie_multiphy_probe(struct platform_device *pdev)
665*1781be3cSQiang Yu {
666*1781be3cSQiang Yu 	const struct qmp_pcie_match_data *match_data;
667*1781be3cSQiang Yu 	struct qmp_pcie_multiphy *qmp_data;
668*1781be3cSQiang Yu 	struct phy_provider *phy_provider;
669*1781be3cSQiang Yu 	struct device *dev = &pdev->dev;
670*1781be3cSQiang Yu 	unsigned int link_mode;
671*1781be3cSQiang Yu 	struct qmp_pcie *qmp;
672*1781be3cSQiang Yu 	struct phy **phys;
673*1781be3cSQiang Yu 	int phy_index;
674*1781be3cSQiang Yu 	int ret;
675*1781be3cSQiang Yu 
676*1781be3cSQiang Yu 	qmp_data = devm_kzalloc(dev, sizeof(*qmp_data), GFP_KERNEL);
677*1781be3cSQiang Yu 
678*1781be3cSQiang Yu 	match_data = of_device_get_match_data(dev);
679*1781be3cSQiang Yu 	if (!match_data)
680*1781be3cSQiang Yu 		return -EINVAL;
681*1781be3cSQiang Yu 
682*1781be3cSQiang Yu 	if (!qmp_data)
683*1781be3cSQiang Yu 		return -ENOMEM;
684*1781be3cSQiang Yu 
685*1781be3cSQiang Yu 	ret = qmp_pcie_read_link_mode(dev, &link_mode);
686*1781be3cSQiang Yu 	if (ret)
687*1781be3cSQiang Yu 		return dev_err_probe(dev, ret, "failed to read qcom,link-mode\n");
688*1781be3cSQiang Yu 
689*1781be3cSQiang Yu 	if (link_mode >= match_data->num_modes)
690*1781be3cSQiang Yu 		return dev_err_probe(dev, -EINVAL, "invalid qcom,link-mode: %u\n",
691*1781be3cSQiang Yu 				     link_mode);
692*1781be3cSQiang Yu 
693*1781be3cSQiang Yu 	qmp_data->mode_cfg = &match_data->mode_cfgs[link_mode];
694*1781be3cSQiang Yu 
695*1781be3cSQiang Yu 	qmp = devm_kcalloc(dev, qmp_data->mode_cfg->num_phys, sizeof(*qmp), GFP_KERNEL);
696*1781be3cSQiang Yu 	if (!qmp)
697*1781be3cSQiang Yu 		return -ENOMEM;
698*1781be3cSQiang Yu 
699*1781be3cSQiang Yu 	phys = devm_kcalloc(dev, qmp_data->mode_cfg->num_phys, sizeof(*phys), GFP_KERNEL);
700*1781be3cSQiang Yu 	if (!phys)
701*1781be3cSQiang Yu 		return -ENOMEM;
702*1781be3cSQiang Yu 
703*1781be3cSQiang Yu 	qmp_data->phys = phys;
704*1781be3cSQiang Yu 	dev_set_drvdata(dev, qmp_data);
705*1781be3cSQiang Yu 
706*1781be3cSQiang Yu 	for (phy_index = 0; phy_index < qmp_data->mode_cfg->num_phys; phy_index++) {
707*1781be3cSQiang Yu 		qmp[phy_index].dev = dev;
708*1781be3cSQiang Yu 		qmp[phy_index].cfg = qmp_data->mode_cfg->cfgs[phy_index];
709*1781be3cSQiang Yu 		ret = qmp_pcie_probe_phy(&qmp[phy_index], dev->of_node, &phys[phy_index]);
710*1781be3cSQiang Yu 		if (ret)
711*1781be3cSQiang Yu 			return ret;
712*1781be3cSQiang Yu 	}
713*1781be3cSQiang Yu 
714*1781be3cSQiang Yu 	ret = qmp_pcie_multiphy_register_clocks(dev, dev->of_node, qmp_data);
715*1781be3cSQiang Yu 	if (ret)
716*1781be3cSQiang Yu 		return ret;
717*1781be3cSQiang Yu 
718*1781be3cSQiang Yu 	phy_provider = devm_of_phy_provider_register(dev, qmp_pcie_multiphy_xlate);
719*1781be3cSQiang Yu 
720*1781be3cSQiang Yu 	return PTR_ERR_OR_ZERO(phy_provider);
721*1781be3cSQiang Yu }
722*1781be3cSQiang Yu 
723*1781be3cSQiang Yu static const struct of_device_id qmp_pcie_multiphy_of_match_table[] = {
724*1781be3cSQiang Yu 	{
725*1781be3cSQiang Yu 		.compatible = "qcom,glymur-qmp-gen5x8-pcie-phy",
726*1781be3cSQiang Yu 		.data = &glymur_qmp_gen5x8_match_data,
727*1781be3cSQiang Yu 	},
728*1781be3cSQiang Yu 	{ }
729*1781be3cSQiang Yu };
730*1781be3cSQiang Yu MODULE_DEVICE_TABLE(of, qmp_pcie_multiphy_of_match_table);
731*1781be3cSQiang Yu 
732*1781be3cSQiang Yu static struct platform_driver qmp_pcie_multiphy_driver = {
733*1781be3cSQiang Yu 	.probe		= qmp_pcie_multiphy_probe,
734*1781be3cSQiang Yu 	.driver = {
735*1781be3cSQiang Yu 		.name	= "qcom-qmp-pcie-multiphy",
736*1781be3cSQiang Yu 		.of_match_table = qmp_pcie_multiphy_of_match_table,
737*1781be3cSQiang Yu 	},
738*1781be3cSQiang Yu };
739*1781be3cSQiang Yu module_platform_driver(qmp_pcie_multiphy_driver);
740*1781be3cSQiang Yu 
741*1781be3cSQiang Yu MODULE_AUTHOR("Qiang Yu <qiang.yu@oss.qualcomm.com>");
742*1781be3cSQiang Yu MODULE_DESCRIPTION("Qualcomm QMP PCIe Multi-PHY driver for Glymur");
743*1781be3cSQiang Yu MODULE_LICENSE("GPL");
744