1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Copyright (c) 2017, The Linux Foundation. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/err.h> |
10 | #include <linux/io.h> |
11 | #include <linux/iopoll.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/of.h> |
15 | #include <linux/of_address.h> |
16 | #include <linux/phy/phy.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/regulator/consumer.h> |
19 | #include <linux/reset.h> |
20 | #include <linux/slab.h> |
21 | |
22 | #include "phy-qcom-qmp-common.h" |
23 | |
24 | #include "phy-qcom-qmp.h" |
25 | |
26 | /* QPHY_START_CONTROL bits */ |
27 | #define PLL_READY_GATE_EN BIT(3) |
28 | |
29 | /* QPHY_COM_PCS_READY_STATUS bit */ |
30 | #define PCS_READY BIT(0) |
31 | |
32 | #define PHY_INIT_COMPLETE_TIMEOUT 10000 |
33 | #define POWER_DOWN_DELAY_US_MIN 10 |
34 | #define POWER_DOWN_DELAY_US_MAX 20 |
35 | |
36 | /* set of registers with offsets different per-PHY */ |
37 | enum qphy_reg_layout { |
38 | /* Common block control registers */ |
39 | QPHY_COM_SW_RESET, |
40 | QPHY_COM_POWER_DOWN_CONTROL, |
41 | QPHY_COM_START_CONTROL, |
42 | QPHY_COM_PCS_READY_STATUS, |
43 | /* PCS registers */ |
44 | QPHY_SW_RESET, |
45 | QPHY_START_CTRL, |
46 | QPHY_PCS_STATUS, |
47 | /* Keep last to ensure regs_layout arrays are properly initialized */ |
48 | QPHY_LAYOUT_SIZE |
49 | }; |
50 | |
51 | static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = { |
52 | [QPHY_COM_SW_RESET] = 0x400, |
53 | [QPHY_COM_POWER_DOWN_CONTROL] = 0x404, |
54 | [QPHY_COM_START_CONTROL] = 0x408, |
55 | [QPHY_COM_PCS_READY_STATUS] = 0x448, |
56 | [QPHY_SW_RESET] = QPHY_V2_PCS_SW_RESET, |
57 | [QPHY_START_CTRL] = QPHY_V2_PCS_START_CONTROL, |
58 | [QPHY_PCS_STATUS] = QPHY_V2_PCS_PCI_PCS_STATUS, |
59 | }; |
60 | |
61 | static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { |
62 | QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1c), |
63 | QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), |
64 | QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), |
65 | QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), |
66 | QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x42), |
67 | QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), |
68 | QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff), |
69 | QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x1f), |
70 | QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x01), |
71 | QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), |
72 | QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), |
73 | QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a), |
74 | QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x09), |
75 | QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82), |
76 | QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03), |
77 | QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55), |
78 | QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55), |
79 | QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), |
80 | QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x1a), |
81 | QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0x0a), |
82 | QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x33), |
83 | QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x02), |
84 | QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1f), |
85 | QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x04), |
86 | QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b), |
87 | QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), |
88 | QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), |
89 | QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00), |
90 | QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80), |
91 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), |
92 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x31), |
93 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), |
94 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x02), |
95 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), |
96 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x2f), |
97 | QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x19), |
98 | QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x15), |
99 | QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), |
100 | QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), |
101 | QMP_PHY_INIT_CFG(QSERDES_COM_CLK_EP_DIV, 0x19), |
102 | QMP_PHY_INIT_CFG(QSERDES_COM_CLK_ENABLE1, 0x10), |
103 | QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), |
104 | QMP_PHY_INIT_CFG(QSERDES_COM_RESCODE_DIV_NUM, 0x40), |
105 | }; |
106 | |
107 | static const struct qmp_phy_init_tbl msm8996_pcie_tx_tbl[] = { |
108 | QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), |
109 | QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), |
110 | }; |
111 | |
112 | static const struct qmp_phy_init_tbl msm8996_pcie_rx_tbl[] = { |
113 | QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x1c), |
114 | QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x01), |
115 | QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x00), |
116 | QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xdb), |
117 | QMP_PHY_INIT_CFG(QSERDES_RX_RX_BAND, 0x18), |
118 | QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x04), |
119 | QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN_HALF, 0x04), |
120 | QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b), |
121 | QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x14), |
122 | QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x19), |
123 | }; |
124 | |
125 | static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = { |
126 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_RX_IDLE_DTCT_CNTRL, 0x4c), |
127 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x00), |
128 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01), |
129 | |
130 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_PLL_LOCK_CHK_DLY_TIME, 0x05), |
131 | |
132 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_ENDPOINT_REFCLK_DRIVE, 0x05), |
133 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_DOWN_CONTROL, 0x02), |
134 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG4, 0x00), |
135 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_POWER_STATE_CONFIG1, 0xa3), |
136 | QMP_PHY_INIT_CFG(QPHY_V2_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), |
137 | }; |
138 | |
139 | /* struct qmp_phy_cfg - per-PHY initialization config */ |
140 | struct qmp_phy_cfg { |
141 | /* number of PHYs provided by this block */ |
142 | int num_phys; |
143 | |
144 | /* Init sequence for PHY blocks - serdes, tx, rx, pcs */ |
145 | const struct qmp_phy_init_tbl *serdes_tbl; |
146 | int serdes_tbl_num; |
147 | const struct qmp_phy_init_tbl *tx_tbl; |
148 | int tx_tbl_num; |
149 | const struct qmp_phy_init_tbl *rx_tbl; |
150 | int rx_tbl_num; |
151 | const struct qmp_phy_init_tbl *pcs_tbl; |
152 | int pcs_tbl_num; |
153 | |
154 | /* clock ids to be requested */ |
155 | const char * const *clk_list; |
156 | int num_clks; |
157 | /* resets to be requested */ |
158 | const char * const *reset_list; |
159 | int num_resets; |
160 | /* regulators to be requested */ |
161 | const char * const *vreg_list; |
162 | int num_vregs; |
163 | |
164 | /* array of registers with different offsets */ |
165 | const unsigned int *regs; |
166 | }; |
167 | |
168 | /** |
169 | * struct qmp_phy - per-lane phy descriptor |
170 | * |
171 | * @phy: generic phy |
172 | * @cfg: phy specific configuration |
173 | * @serdes: iomapped memory space for phy's serdes (i.e. PLL) |
174 | * @tx: iomapped memory space for lane's tx |
175 | * @rx: iomapped memory space for lane's rx |
176 | * @pcs: iomapped memory space for lane's pcs |
177 | * @pipe_clk: pipe clock |
178 | * @index: lane index |
179 | * @qmp: QMP phy to which this lane belongs |
180 | * @lane_rst: lane's reset controller |
181 | */ |
182 | struct qmp_phy { |
183 | struct phy *phy; |
184 | const struct qmp_phy_cfg *cfg; |
185 | void __iomem *serdes; |
186 | void __iomem *tx; |
187 | void __iomem *rx; |
188 | void __iomem *pcs; |
189 | struct clk *pipe_clk; |
190 | unsigned int index; |
191 | struct qcom_qmp *qmp; |
192 | struct reset_control *lane_rst; |
193 | }; |
194 | |
195 | /** |
196 | * struct qcom_qmp - structure holding QMP phy block attributes |
197 | * |
198 | * @dev: device |
199 | * |
200 | * @clks: array of clocks required by phy |
201 | * @resets: array of resets required by phy |
202 | * @vregs: regulator supplies bulk data |
203 | * |
204 | * @phys: array of per-lane phy descriptors |
205 | * @phy_mutex: mutex lock for PHY common block initialization |
206 | * @init_count: phy common block initialization count |
207 | */ |
208 | struct qcom_qmp { |
209 | struct device *dev; |
210 | |
211 | struct clk_bulk_data *clks; |
212 | struct reset_control_bulk_data *resets; |
213 | struct regulator_bulk_data *vregs; |
214 | |
215 | struct qmp_phy **phys; |
216 | |
217 | struct mutex phy_mutex; |
218 | int init_count; |
219 | }; |
220 | |
221 | static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) |
222 | { |
223 | u32 reg; |
224 | |
225 | reg = readl(addr: base + offset); |
226 | reg |= val; |
227 | writel(val: reg, addr: base + offset); |
228 | |
229 | /* ensure that above write is through */ |
230 | readl(addr: base + offset); |
231 | } |
232 | |
233 | static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) |
234 | { |
235 | u32 reg; |
236 | |
237 | reg = readl(addr: base + offset); |
238 | reg &= ~val; |
239 | writel(val: reg, addr: base + offset); |
240 | |
241 | /* ensure that above write is through */ |
242 | readl(addr: base + offset); |
243 | } |
244 | |
245 | /* list of clocks required by phy */ |
246 | static const char * const msm8996_phy_clk_l[] = { |
247 | "aux" , "cfg_ahb" , "ref" , |
248 | }; |
249 | |
250 | /* list of resets */ |
251 | static const char * const msm8996_pciephy_reset_l[] = { |
252 | "phy" , "common" , "cfg" , |
253 | }; |
254 | |
255 | /* list of regulators */ |
256 | static const char * const qmp_phy_vreg_l[] = { |
257 | "vdda-phy" , "vdda-pll" , |
258 | }; |
259 | |
260 | static const struct qmp_phy_cfg msm8996_pciephy_cfg = { |
261 | .num_phys = 3, |
262 | |
263 | .serdes_tbl = msm8996_pcie_serdes_tbl, |
264 | .serdes_tbl_num = ARRAY_SIZE(msm8996_pcie_serdes_tbl), |
265 | .tx_tbl = msm8996_pcie_tx_tbl, |
266 | .tx_tbl_num = ARRAY_SIZE(msm8996_pcie_tx_tbl), |
267 | .rx_tbl = msm8996_pcie_rx_tbl, |
268 | .rx_tbl_num = ARRAY_SIZE(msm8996_pcie_rx_tbl), |
269 | .pcs_tbl = msm8996_pcie_pcs_tbl, |
270 | .pcs_tbl_num = ARRAY_SIZE(msm8996_pcie_pcs_tbl), |
271 | .clk_list = msm8996_phy_clk_l, |
272 | .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), |
273 | .reset_list = msm8996_pciephy_reset_l, |
274 | .num_resets = ARRAY_SIZE(msm8996_pciephy_reset_l), |
275 | .vreg_list = qmp_phy_vreg_l, |
276 | .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), |
277 | .regs = pciephy_regs_layout, |
278 | }; |
279 | |
280 | static int qmp_pcie_msm8996_serdes_init(struct qmp_phy *qphy) |
281 | { |
282 | struct qcom_qmp *qmp = qphy->qmp; |
283 | const struct qmp_phy_cfg *cfg = qphy->cfg; |
284 | void __iomem *serdes = qphy->serdes; |
285 | const struct qmp_phy_init_tbl *serdes_tbl = cfg->serdes_tbl; |
286 | int serdes_tbl_num = cfg->serdes_tbl_num; |
287 | void __iomem *status; |
288 | unsigned int val; |
289 | int ret; |
290 | |
291 | qmp_configure(base: serdes, tbl: serdes_tbl, num: serdes_tbl_num); |
292 | |
293 | qphy_clrbits(base: serdes, offset: cfg->regs[QPHY_COM_SW_RESET], SW_RESET); |
294 | qphy_setbits(base: serdes, offset: cfg->regs[QPHY_COM_START_CONTROL], |
295 | SERDES_START | PCS_START); |
296 | |
297 | status = serdes + cfg->regs[QPHY_COM_PCS_READY_STATUS]; |
298 | ret = readl_poll_timeout(status, val, (val & PCS_READY), 200, |
299 | PHY_INIT_COMPLETE_TIMEOUT); |
300 | if (ret) { |
301 | dev_err(qmp->dev, |
302 | "phy common block init timed-out\n" ); |
303 | return ret; |
304 | } |
305 | |
306 | return 0; |
307 | } |
308 | |
309 | static int qmp_pcie_msm8996_com_init(struct qmp_phy *qphy) |
310 | { |
311 | struct qcom_qmp *qmp = qphy->qmp; |
312 | const struct qmp_phy_cfg *cfg = qphy->cfg; |
313 | void __iomem *serdes = qphy->serdes; |
314 | int ret; |
315 | |
316 | mutex_lock(&qmp->phy_mutex); |
317 | if (qmp->init_count++) { |
318 | mutex_unlock(lock: &qmp->phy_mutex); |
319 | return 0; |
320 | } |
321 | |
322 | ret = regulator_bulk_enable(num_consumers: cfg->num_vregs, consumers: qmp->vregs); |
323 | if (ret) { |
324 | dev_err(qmp->dev, "failed to enable regulators, err=%d\n" , ret); |
325 | goto err_decrement_count; |
326 | } |
327 | |
328 | ret = reset_control_bulk_assert(num_rstcs: cfg->num_resets, rstcs: qmp->resets); |
329 | if (ret) { |
330 | dev_err(qmp->dev, "reset assert failed\n" ); |
331 | goto err_disable_regulators; |
332 | } |
333 | |
334 | ret = reset_control_bulk_deassert(num_rstcs: cfg->num_resets, rstcs: qmp->resets); |
335 | if (ret) { |
336 | dev_err(qmp->dev, "reset deassert failed\n" ); |
337 | goto err_disable_regulators; |
338 | } |
339 | |
340 | ret = clk_bulk_prepare_enable(num_clks: cfg->num_clks, clks: qmp->clks); |
341 | if (ret) |
342 | goto err_assert_reset; |
343 | |
344 | qphy_setbits(base: serdes, offset: cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], |
345 | SW_PWRDN); |
346 | |
347 | mutex_unlock(lock: &qmp->phy_mutex); |
348 | |
349 | return 0; |
350 | |
351 | err_assert_reset: |
352 | reset_control_bulk_assert(num_rstcs: cfg->num_resets, rstcs: qmp->resets); |
353 | err_disable_regulators: |
354 | regulator_bulk_disable(num_consumers: cfg->num_vregs, consumers: qmp->vregs); |
355 | err_decrement_count: |
356 | qmp->init_count--; |
357 | mutex_unlock(lock: &qmp->phy_mutex); |
358 | |
359 | return ret; |
360 | } |
361 | |
362 | static int qmp_pcie_msm8996_com_exit(struct qmp_phy *qphy) |
363 | { |
364 | struct qcom_qmp *qmp = qphy->qmp; |
365 | const struct qmp_phy_cfg *cfg = qphy->cfg; |
366 | void __iomem *serdes = qphy->serdes; |
367 | |
368 | mutex_lock(&qmp->phy_mutex); |
369 | if (--qmp->init_count) { |
370 | mutex_unlock(lock: &qmp->phy_mutex); |
371 | return 0; |
372 | } |
373 | |
374 | qphy_setbits(base: serdes, offset: cfg->regs[QPHY_COM_START_CONTROL], |
375 | SERDES_START | PCS_START); |
376 | qphy_clrbits(base: serdes, offset: cfg->regs[QPHY_COM_SW_RESET], |
377 | SW_RESET); |
378 | qphy_setbits(base: serdes, offset: cfg->regs[QPHY_COM_POWER_DOWN_CONTROL], |
379 | SW_PWRDN); |
380 | |
381 | reset_control_bulk_assert(num_rstcs: cfg->num_resets, rstcs: qmp->resets); |
382 | |
383 | clk_bulk_disable_unprepare(num_clks: cfg->num_clks, clks: qmp->clks); |
384 | |
385 | regulator_bulk_disable(num_consumers: cfg->num_vregs, consumers: qmp->vregs); |
386 | |
387 | mutex_unlock(lock: &qmp->phy_mutex); |
388 | |
389 | return 0; |
390 | } |
391 | |
392 | static int qmp_pcie_msm8996_init(struct phy *phy) |
393 | { |
394 | struct qmp_phy *qphy = phy_get_drvdata(phy); |
395 | struct qcom_qmp *qmp = qphy->qmp; |
396 | int ret; |
397 | dev_vdbg(qmp->dev, "Initializing QMP phy\n" ); |
398 | |
399 | ret = qmp_pcie_msm8996_com_init(qphy); |
400 | if (ret) |
401 | return ret; |
402 | |
403 | return 0; |
404 | } |
405 | |
406 | static int qmp_pcie_msm8996_power_on(struct phy *phy) |
407 | { |
408 | struct qmp_phy *qphy = phy_get_drvdata(phy); |
409 | struct qcom_qmp *qmp = qphy->qmp; |
410 | const struct qmp_phy_cfg *cfg = qphy->cfg; |
411 | void __iomem *tx = qphy->tx; |
412 | void __iomem *rx = qphy->rx; |
413 | void __iomem *pcs = qphy->pcs; |
414 | void __iomem *status; |
415 | unsigned int val; |
416 | int ret; |
417 | |
418 | qmp_pcie_msm8996_serdes_init(qphy); |
419 | |
420 | ret = reset_control_deassert(rstc: qphy->lane_rst); |
421 | if (ret) { |
422 | dev_err(qmp->dev, "lane%d reset deassert failed\n" , |
423 | qphy->index); |
424 | return ret; |
425 | } |
426 | |
427 | ret = clk_prepare_enable(clk: qphy->pipe_clk); |
428 | if (ret) { |
429 | dev_err(qmp->dev, "pipe_clk enable failed err=%d\n" , ret); |
430 | goto err_reset_lane; |
431 | } |
432 | |
433 | /* Tx, Rx, and PCS configurations */ |
434 | qmp_configure_lane(base: tx, tbl: cfg->tx_tbl, num: cfg->tx_tbl_num, lane_mask: 1); |
435 | qmp_configure_lane(base: rx, tbl: cfg->rx_tbl, num: cfg->rx_tbl_num, lane_mask: 1); |
436 | qmp_configure(base: pcs, tbl: cfg->pcs_tbl, num: cfg->pcs_tbl_num); |
437 | |
438 | /* |
439 | * Pull out PHY from POWER DOWN state. |
440 | * This is active low enable signal to power-down PHY. |
441 | */ |
442 | qphy_setbits(base: pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, |
443 | SW_PWRDN | REFCLK_DRV_DSBL); |
444 | |
445 | usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX); |
446 | |
447 | /* Pull PHY out of reset state */ |
448 | qphy_clrbits(base: pcs, offset: cfg->regs[QPHY_SW_RESET], SW_RESET); |
449 | |
450 | /* start SerDes and Phy-Coding-Sublayer */ |
451 | qphy_setbits(base: pcs, offset: cfg->regs[QPHY_START_CTRL], |
452 | PCS_START | PLL_READY_GATE_EN); |
453 | |
454 | status = pcs + cfg->regs[QPHY_PCS_STATUS]; |
455 | ret = readl_poll_timeout(status, val, !(val & PHYSTATUS), 200, |
456 | PHY_INIT_COMPLETE_TIMEOUT); |
457 | if (ret) { |
458 | dev_err(qmp->dev, "phy initialization timed-out\n" ); |
459 | goto err_disable_pipe_clk; |
460 | } |
461 | |
462 | return 0; |
463 | |
464 | err_disable_pipe_clk: |
465 | clk_disable_unprepare(clk: qphy->pipe_clk); |
466 | err_reset_lane: |
467 | reset_control_assert(rstc: qphy->lane_rst); |
468 | |
469 | return ret; |
470 | } |
471 | |
472 | static int qmp_pcie_msm8996_power_off(struct phy *phy) |
473 | { |
474 | struct qmp_phy *qphy = phy_get_drvdata(phy); |
475 | const struct qmp_phy_cfg *cfg = qphy->cfg; |
476 | |
477 | clk_disable_unprepare(clk: qphy->pipe_clk); |
478 | |
479 | /* PHY reset */ |
480 | qphy_setbits(base: qphy->pcs, offset: cfg->regs[QPHY_SW_RESET], SW_RESET); |
481 | |
482 | /* stop SerDes and Phy-Coding-Sublayer */ |
483 | qphy_clrbits(base: qphy->pcs, offset: cfg->regs[QPHY_START_CTRL], |
484 | SERDES_START | PCS_START); |
485 | |
486 | /* Put PHY into POWER DOWN state: active low */ |
487 | qphy_clrbits(base: qphy->pcs, QPHY_V2_PCS_POWER_DOWN_CONTROL, |
488 | SW_PWRDN | REFCLK_DRV_DSBL); |
489 | |
490 | return 0; |
491 | } |
492 | |
493 | static int qmp_pcie_msm8996_exit(struct phy *phy) |
494 | { |
495 | struct qmp_phy *qphy = phy_get_drvdata(phy); |
496 | |
497 | reset_control_assert(rstc: qphy->lane_rst); |
498 | |
499 | qmp_pcie_msm8996_com_exit(qphy); |
500 | |
501 | return 0; |
502 | } |
503 | |
504 | static int qmp_pcie_msm8996_enable(struct phy *phy) |
505 | { |
506 | int ret; |
507 | |
508 | ret = qmp_pcie_msm8996_init(phy); |
509 | if (ret) |
510 | return ret; |
511 | |
512 | ret = qmp_pcie_msm8996_power_on(phy); |
513 | if (ret) |
514 | qmp_pcie_msm8996_exit(phy); |
515 | |
516 | return ret; |
517 | } |
518 | |
519 | static int qmp_pcie_msm8996_disable(struct phy *phy) |
520 | { |
521 | int ret; |
522 | |
523 | ret = qmp_pcie_msm8996_power_off(phy); |
524 | if (ret) |
525 | return ret; |
526 | return qmp_pcie_msm8996_exit(phy); |
527 | } |
528 | |
529 | static int qmp_pcie_msm8996_vreg_init(struct device *dev, const struct qmp_phy_cfg *cfg) |
530 | { |
531 | struct qcom_qmp *qmp = dev_get_drvdata(dev); |
532 | int num = cfg->num_vregs; |
533 | int i; |
534 | |
535 | qmp->vregs = devm_kcalloc(dev, n: num, size: sizeof(*qmp->vregs), GFP_KERNEL); |
536 | if (!qmp->vregs) |
537 | return -ENOMEM; |
538 | |
539 | for (i = 0; i < num; i++) |
540 | qmp->vregs[i].supply = cfg->vreg_list[i]; |
541 | |
542 | return devm_regulator_bulk_get(dev, num_consumers: num, consumers: qmp->vregs); |
543 | } |
544 | |
545 | static int qmp_pcie_msm8996_reset_init(struct device *dev, const struct qmp_phy_cfg *cfg) |
546 | { |
547 | struct qcom_qmp *qmp = dev_get_drvdata(dev); |
548 | int i; |
549 | int ret; |
550 | |
551 | qmp->resets = devm_kcalloc(dev, n: cfg->num_resets, |
552 | size: sizeof(*qmp->resets), GFP_KERNEL); |
553 | if (!qmp->resets) |
554 | return -ENOMEM; |
555 | |
556 | for (i = 0; i < cfg->num_resets; i++) |
557 | qmp->resets[i].id = cfg->reset_list[i]; |
558 | |
559 | ret = devm_reset_control_bulk_get_exclusive(dev, num_rstcs: cfg->num_resets, rstcs: qmp->resets); |
560 | if (ret) |
561 | return dev_err_probe(dev, err: ret, fmt: "failed to get resets\n" ); |
562 | |
563 | return 0; |
564 | } |
565 | |
566 | static int qmp_pcie_msm8996_clk_init(struct device *dev, const struct qmp_phy_cfg *cfg) |
567 | { |
568 | struct qcom_qmp *qmp = dev_get_drvdata(dev); |
569 | int num = cfg->num_clks; |
570 | int i; |
571 | |
572 | qmp->clks = devm_kcalloc(dev, n: num, size: sizeof(*qmp->clks), GFP_KERNEL); |
573 | if (!qmp->clks) |
574 | return -ENOMEM; |
575 | |
576 | for (i = 0; i < num; i++) |
577 | qmp->clks[i].id = cfg->clk_list[i]; |
578 | |
579 | return devm_clk_bulk_get(dev, num_clks: num, clks: qmp->clks); |
580 | } |
581 | |
582 | static void phy_clk_release_provider(void *res) |
583 | { |
584 | of_clk_del_provider(np: res); |
585 | } |
586 | |
587 | /* |
588 | * Register a fixed rate pipe clock. |
589 | * |
590 | * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate |
591 | * controls it. The <s>_pipe_clk coming out of the GCC is requested |
592 | * by the PHY driver for its operations. |
593 | * We register the <s>_pipe_clksrc here. The gcc driver takes care |
594 | * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk. |
595 | * Below picture shows this relationship. |
596 | * |
597 | * +---------------+ |
598 | * | PHY block |<<---------------------------------------+ |
599 | * | | | |
600 | * | +-------+ | +-----+ | |
601 | * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+ |
602 | * clk | +-------+ | +-----+ |
603 | * +---------------+ |
604 | */ |
605 | static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) |
606 | { |
607 | struct clk_fixed_rate *fixed; |
608 | struct clk_init_data init = { }; |
609 | int ret; |
610 | |
611 | ret = of_property_read_string(np, propname: "clock-output-names" , out_string: &init.name); |
612 | if (ret) { |
613 | dev_err(qmp->dev, "%pOFn: No clock-output-names\n" , np); |
614 | return ret; |
615 | } |
616 | |
617 | fixed = devm_kzalloc(dev: qmp->dev, size: sizeof(*fixed), GFP_KERNEL); |
618 | if (!fixed) |
619 | return -ENOMEM; |
620 | |
621 | init.ops = &clk_fixed_rate_ops; |
622 | |
623 | /* controllers using QMP phys use 125MHz pipe clock interface */ |
624 | fixed->fixed_rate = 125000000; |
625 | fixed->hw.init = &init; |
626 | |
627 | ret = devm_clk_hw_register(dev: qmp->dev, hw: &fixed->hw); |
628 | if (ret) |
629 | return ret; |
630 | |
631 | ret = of_clk_add_hw_provider(np, get: of_clk_hw_simple_get, data: &fixed->hw); |
632 | if (ret) |
633 | return ret; |
634 | |
635 | /* |
636 | * Roll a devm action because the clock provider is the child node, but |
637 | * the child node is not actually a device. |
638 | */ |
639 | return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, np); |
640 | } |
641 | |
642 | static const struct phy_ops qmp_pcie_msm8996_ops = { |
643 | .power_on = qmp_pcie_msm8996_enable, |
644 | .power_off = qmp_pcie_msm8996_disable, |
645 | .owner = THIS_MODULE, |
646 | }; |
647 | |
648 | static void qcom_qmp_reset_control_put(void *data) |
649 | { |
650 | reset_control_put(rstc: data); |
651 | } |
652 | |
653 | static int qmp_pcie_msm8996_create(struct device *dev, struct device_node *np, int id, |
654 | void __iomem *serdes, const struct qmp_phy_cfg *cfg) |
655 | { |
656 | struct qcom_qmp *qmp = dev_get_drvdata(dev); |
657 | struct phy *generic_phy; |
658 | struct qmp_phy *qphy; |
659 | int ret; |
660 | |
661 | qphy = devm_kzalloc(dev, size: sizeof(*qphy), GFP_KERNEL); |
662 | if (!qphy) |
663 | return -ENOMEM; |
664 | |
665 | qphy->cfg = cfg; |
666 | qphy->serdes = serdes; |
667 | /* |
668 | * Get memory resources for each PHY: |
669 | * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. |
670 | */ |
671 | qphy->tx = devm_of_iomap(dev, node: np, index: 0, NULL); |
672 | if (IS_ERR(ptr: qphy->tx)) |
673 | return PTR_ERR(ptr: qphy->tx); |
674 | |
675 | qphy->rx = devm_of_iomap(dev, node: np, index: 1, NULL); |
676 | if (IS_ERR(ptr: qphy->rx)) |
677 | return PTR_ERR(ptr: qphy->rx); |
678 | |
679 | qphy->pcs = devm_of_iomap(dev, node: np, index: 2, NULL); |
680 | if (IS_ERR(ptr: qphy->pcs)) |
681 | return PTR_ERR(ptr: qphy->pcs); |
682 | |
683 | qphy->pipe_clk = devm_get_clk_from_child(dev, np, NULL); |
684 | if (IS_ERR(ptr: qphy->pipe_clk)) { |
685 | return dev_err_probe(dev, err: PTR_ERR(ptr: qphy->pipe_clk), |
686 | fmt: "failed to get lane%d pipe clock\n" , id); |
687 | } |
688 | |
689 | qphy->lane_rst = of_reset_control_get_exclusive_by_index(node: np, index: 0); |
690 | if (IS_ERR(ptr: qphy->lane_rst)) { |
691 | dev_err(dev, "failed to get lane%d reset\n" , id); |
692 | return PTR_ERR(ptr: qphy->lane_rst); |
693 | } |
694 | ret = devm_add_action_or_reset(dev, qcom_qmp_reset_control_put, |
695 | qphy->lane_rst); |
696 | if (ret) |
697 | return ret; |
698 | |
699 | generic_phy = devm_phy_create(dev, node: np, ops: &qmp_pcie_msm8996_ops); |
700 | if (IS_ERR(ptr: generic_phy)) { |
701 | ret = PTR_ERR(ptr: generic_phy); |
702 | dev_err(dev, "failed to create qphy %d\n" , ret); |
703 | return ret; |
704 | } |
705 | |
706 | qphy->phy = generic_phy; |
707 | qphy->index = id; |
708 | qphy->qmp = qmp; |
709 | qmp->phys[id] = qphy; |
710 | phy_set_drvdata(phy: generic_phy, data: qphy); |
711 | |
712 | return 0; |
713 | } |
714 | |
715 | static const struct of_device_id qmp_pcie_msm8996_of_match_table[] = { |
716 | { |
717 | .compatible = "qcom,msm8996-qmp-pcie-phy" , |
718 | .data = &msm8996_pciephy_cfg, |
719 | }, |
720 | { }, |
721 | }; |
722 | MODULE_DEVICE_TABLE(of, qmp_pcie_msm8996_of_match_table); |
723 | |
724 | static int qmp_pcie_msm8996_probe(struct platform_device *pdev) |
725 | { |
726 | struct qcom_qmp *qmp; |
727 | struct device *dev = &pdev->dev; |
728 | struct device_node *child; |
729 | struct phy_provider *phy_provider; |
730 | void __iomem *serdes; |
731 | const struct qmp_phy_cfg *cfg = NULL; |
732 | int num, id, expected_phys; |
733 | int ret; |
734 | |
735 | qmp = devm_kzalloc(dev, size: sizeof(*qmp), GFP_KERNEL); |
736 | if (!qmp) |
737 | return -ENOMEM; |
738 | |
739 | qmp->dev = dev; |
740 | dev_set_drvdata(dev, data: qmp); |
741 | |
742 | cfg = of_device_get_match_data(dev); |
743 | if (!cfg) |
744 | return -EINVAL; |
745 | |
746 | serdes = devm_platform_ioremap_resource(pdev, index: 0); |
747 | if (IS_ERR(ptr: serdes)) |
748 | return PTR_ERR(ptr: serdes); |
749 | |
750 | expected_phys = cfg->num_phys; |
751 | |
752 | mutex_init(&qmp->phy_mutex); |
753 | |
754 | ret = qmp_pcie_msm8996_clk_init(dev, cfg); |
755 | if (ret) |
756 | return ret; |
757 | |
758 | ret = qmp_pcie_msm8996_reset_init(dev, cfg); |
759 | if (ret) |
760 | return ret; |
761 | |
762 | ret = qmp_pcie_msm8996_vreg_init(dev, cfg); |
763 | if (ret) |
764 | return ret; |
765 | |
766 | num = of_get_available_child_count(np: dev->of_node); |
767 | /* do we have a rogue child node ? */ |
768 | if (num > expected_phys) |
769 | return -EINVAL; |
770 | |
771 | qmp->phys = devm_kcalloc(dev, n: num, size: sizeof(*qmp->phys), GFP_KERNEL); |
772 | if (!qmp->phys) |
773 | return -ENOMEM; |
774 | |
775 | id = 0; |
776 | for_each_available_child_of_node(dev->of_node, child) { |
777 | /* Create per-lane phy */ |
778 | ret = qmp_pcie_msm8996_create(dev, np: child, id, serdes, cfg); |
779 | if (ret) { |
780 | dev_err(dev, "failed to create lane%d phy, %d\n" , |
781 | id, ret); |
782 | goto err_node_put; |
783 | } |
784 | |
785 | /* |
786 | * Register the pipe clock provided by phy. |
787 | * See function description to see details of this pipe clock. |
788 | */ |
789 | ret = phy_pipe_clk_register(qmp, np: child); |
790 | if (ret) { |
791 | dev_err(qmp->dev, |
792 | "failed to register pipe clock source\n" ); |
793 | goto err_node_put; |
794 | } |
795 | |
796 | id++; |
797 | } |
798 | |
799 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); |
800 | |
801 | return PTR_ERR_OR_ZERO(ptr: phy_provider); |
802 | |
803 | err_node_put: |
804 | of_node_put(node: child); |
805 | return ret; |
806 | } |
807 | |
808 | static struct platform_driver qmp_pcie_msm8996_driver = { |
809 | .probe = qmp_pcie_msm8996_probe, |
810 | .driver = { |
811 | .name = "qcom-qmp-msm8996-pcie-phy" , |
812 | .of_match_table = qmp_pcie_msm8996_of_match_table, |
813 | }, |
814 | }; |
815 | |
816 | module_platform_driver(qmp_pcie_msm8996_driver); |
817 | |
818 | MODULE_AUTHOR("Vivek Gautam <vivek.gautam@codeaurora.org>" ); |
819 | MODULE_DESCRIPTION("Qualcomm QMP MSM8996 PCIe PHY driver" ); |
820 | MODULE_LICENSE("GPL v2" ); |
821 | |