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