1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2009-2018, Linux Foundation. All rights reserved.
4 * Copyright (c) 2018-2020, Linaro Limited
8 #include <linux/delay.h>
10 #include <linux/kernel.h>
11 #include <linux/module.h>
13 #include <linux/of_graph.h>
14 #include <linux/phy/phy.h>
15 #include <linux/platform_device.h>
16 #include <linux/regulator/consumer.h>
17 #include <linux/reset.h>
18 #include <linux/slab.h>
20 /* PHY register and bit definitions */
21 #define PHY_CTRL_COMMON0 0x078
23 #define PHY_IRQ_CMD 0x0d0
24 #define PHY_INTR_MASK0 0x0d4
25 #define PHY_INTR_CLEAR0 0x0dc
26 #define DPDM_MASK 0x1e
46 struct hsphy_init_seq
{
53 const struct hsphy_init_seq
*init_seq
;
54 unsigned int init_seq_num
;
59 struct clk_bulk_data
*clks
;
61 struct reset_control
*phy_reset
;
62 struct reset_control
*por_reset
;
63 struct regulator_bulk_data vregs
[VREG_NUM
];
64 const struct hsphy_data
*data
;
68 static int qcom_snps_hsphy_set_mode(struct phy
*phy
, enum phy_mode mode
,
71 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
73 priv
->mode
= PHY_MODE_INVALID
;
81 static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv
*priv
)
85 /* Clear any existing interrupts before enabling the interrupts */
86 val
= readb(priv
->base
+ PHY_INTR_CLEAR0
);
88 writeb(val
, priv
->base
+ PHY_INTR_CLEAR0
);
90 writeb(0x0, priv
->base
+ PHY_IRQ_CMD
);
91 usleep_range(200, 220);
92 writeb(0x1, priv
->base
+ PHY_IRQ_CMD
);
94 /* Make sure the interrupts are cleared */
95 usleep_range(200, 220);
97 val
= readb(priv
->base
+ PHY_INTR_MASK0
);
99 case PHY_MODE_USB_HOST_HS
:
100 case PHY_MODE_USB_HOST_FS
:
101 case PHY_MODE_USB_DEVICE_HS
:
102 case PHY_MODE_USB_DEVICE_FS
:
103 val
|= DP_1_0
| DM_0_1
;
105 case PHY_MODE_USB_HOST_LS
:
106 case PHY_MODE_USB_DEVICE_LS
:
107 val
|= DP_0_1
| DM_1_0
;
110 /* No device connected */
111 val
|= DP_0_1
| DM_0_1
;
114 writeb(val
, priv
->base
+ PHY_INTR_MASK0
);
117 static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv
*priv
)
121 val
= readb(priv
->base
+ PHY_INTR_MASK0
);
123 writeb(val
, priv
->base
+ PHY_INTR_MASK0
);
125 /* Clear any pending interrupts */
126 val
= readb(priv
->base
+ PHY_INTR_CLEAR0
);
128 writeb(val
, priv
->base
+ PHY_INTR_CLEAR0
);
130 writeb(0x0, priv
->base
+ PHY_IRQ_CMD
);
131 usleep_range(200, 220);
133 writeb(0x1, priv
->base
+ PHY_IRQ_CMD
);
134 usleep_range(200, 220);
137 static void qcom_snps_hsphy_enter_retention(struct hsphy_priv
*priv
)
141 val
= readb(priv
->base
+ PHY_CTRL_COMMON0
);
143 writeb(val
, priv
->base
+ PHY_CTRL_COMMON0
);
146 static void qcom_snps_hsphy_exit_retention(struct hsphy_priv
*priv
)
150 val
= readb(priv
->base
+ PHY_CTRL_COMMON0
);
152 writeb(val
, priv
->base
+ PHY_CTRL_COMMON0
);
155 static int qcom_snps_hsphy_power_on(struct phy
*phy
)
157 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
160 ret
= regulator_bulk_enable(VREG_NUM
, priv
->vregs
);
163 ret
= clk_bulk_prepare_enable(priv
->num_clks
, priv
->clks
);
165 goto err_disable_regulator
;
166 qcom_snps_hsphy_disable_hv_interrupts(priv
);
167 qcom_snps_hsphy_exit_retention(priv
);
171 err_disable_regulator
:
172 regulator_bulk_disable(VREG_NUM
, priv
->vregs
);
177 static int qcom_snps_hsphy_power_off(struct phy
*phy
)
179 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
181 qcom_snps_hsphy_enter_retention(priv
);
182 qcom_snps_hsphy_enable_hv_interrupts(priv
);
183 clk_bulk_disable_unprepare(priv
->num_clks
, priv
->clks
);
184 regulator_bulk_disable(VREG_NUM
, priv
->vregs
);
189 static int qcom_snps_hsphy_reset(struct hsphy_priv
*priv
)
193 ret
= reset_control_assert(priv
->phy_reset
);
197 usleep_range(10, 15);
199 ret
= reset_control_deassert(priv
->phy_reset
);
203 usleep_range(80, 100);
208 static void qcom_snps_hsphy_init_sequence(struct hsphy_priv
*priv
)
210 const struct hsphy_data
*data
= priv
->data
;
211 const struct hsphy_init_seq
*seq
;
214 /* Device match data is optional. */
218 seq
= data
->init_seq
;
220 for (i
= 0; i
< data
->init_seq_num
; i
++, seq
++) {
221 writeb(seq
->val
, priv
->base
+ seq
->offset
);
223 usleep_range(seq
->delay
, seq
->delay
+ 10);
227 static int qcom_snps_hsphy_por_reset(struct hsphy_priv
*priv
)
231 ret
= reset_control_assert(priv
->por_reset
);
236 * The Femto PHY is POR reset in the following scenarios.
238 * 1. After overriding the parameter registers.
239 * 2. Low power mode exit from PHY retention.
241 * Ensure that SIDDQ is cleared before bringing the PHY
244 qcom_snps_hsphy_exit_retention(priv
);
247 * As per databook, 10 usec delay is required between
248 * PHY POR assert and de-assert.
250 usleep_range(10, 20);
251 ret
= reset_control_deassert(priv
->por_reset
);
256 * As per databook, it takes 75 usec for PHY to stabilize
259 usleep_range(80, 100);
264 static int qcom_snps_hsphy_init(struct phy
*phy
)
266 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
269 ret
= qcom_snps_hsphy_reset(priv
);
273 qcom_snps_hsphy_init_sequence(priv
);
275 ret
= qcom_snps_hsphy_por_reset(priv
);
282 static const struct phy_ops qcom_snps_hsphy_ops
= {
283 .init
= qcom_snps_hsphy_init
,
284 .power_on
= qcom_snps_hsphy_power_on
,
285 .power_off
= qcom_snps_hsphy_power_off
,
286 .set_mode
= qcom_snps_hsphy_set_mode
,
287 .owner
= THIS_MODULE
,
290 static const char * const qcom_snps_hsphy_clks
[] = {
296 static int qcom_snps_hsphy_probe(struct platform_device
*pdev
)
298 struct device
*dev
= &pdev
->dev
;
299 struct phy_provider
*provider
;
300 struct hsphy_priv
*priv
;
305 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
309 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
310 if (IS_ERR(priv
->base
))
311 return PTR_ERR(priv
->base
);
313 priv
->num_clks
= ARRAY_SIZE(qcom_snps_hsphy_clks
);
314 priv
->clks
= devm_kcalloc(dev
, priv
->num_clks
, sizeof(*priv
->clks
),
319 for (i
= 0; i
< priv
->num_clks
; i
++)
320 priv
->clks
[i
].id
= qcom_snps_hsphy_clks
[i
];
322 ret
= devm_clk_bulk_get(dev
, priv
->num_clks
, priv
->clks
);
326 priv
->phy_reset
= devm_reset_control_get_exclusive(dev
, "phy");
327 if (IS_ERR(priv
->phy_reset
))
328 return PTR_ERR(priv
->phy_reset
);
330 priv
->por_reset
= devm_reset_control_get_exclusive(dev
, "por");
331 if (IS_ERR(priv
->por_reset
))
332 return PTR_ERR(priv
->por_reset
);
334 priv
->vregs
[VDD
].supply
= "vdd";
335 priv
->vregs
[VDDA_1P8
].supply
= "vdda1p8";
336 priv
->vregs
[VDDA_3P3
].supply
= "vdda3p3";
338 ret
= devm_regulator_bulk_get(dev
, VREG_NUM
, priv
->vregs
);
342 /* Get device match data */
343 priv
->data
= device_get_match_data(dev
);
345 phy
= devm_phy_create(dev
, dev
->of_node
, &qcom_snps_hsphy_ops
);
349 phy_set_drvdata(phy
, priv
);
351 provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
352 if (IS_ERR(provider
))
353 return PTR_ERR(provider
);
355 ret
= regulator_set_load(priv
->vregs
[VDDA_1P8
].consumer
, 19000);
359 ret
= regulator_set_load(priv
->vregs
[VDDA_3P3
].consumer
, 16000);
366 regulator_set_load(priv
->vregs
[VDDA_1P8
].consumer
, 0);
372 * The macro is used to define an initialization sequence. Each tuple
373 * is meant to program 'value' into phy register at 'offset' with 'delay'
376 #define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, }
378 static const struct hsphy_init_seq init_seq_femtophy
[] = {
379 HSPHY_INIT_CFG(0xc0, 0x01, 0),
380 HSPHY_INIT_CFG(0xe8, 0x0d, 0),
381 HSPHY_INIT_CFG(0x74, 0x12, 0),
382 HSPHY_INIT_CFG(0x98, 0x63, 0),
383 HSPHY_INIT_CFG(0x9c, 0x03, 0),
384 HSPHY_INIT_CFG(0xa0, 0x1d, 0),
385 HSPHY_INIT_CFG(0xa4, 0x03, 0),
386 HSPHY_INIT_CFG(0x8c, 0x23, 0),
387 HSPHY_INIT_CFG(0x78, 0x08, 0),
388 HSPHY_INIT_CFG(0x7c, 0xdc, 0),
389 HSPHY_INIT_CFG(0x90, 0xe0, 20),
390 HSPHY_INIT_CFG(0x74, 0x10, 0),
391 HSPHY_INIT_CFG(0x90, 0x60, 0),
394 static const struct hsphy_data hsphy_data_femtophy
= {
395 .init_seq
= init_seq_femtophy
,
396 .init_seq_num
= ARRAY_SIZE(init_seq_femtophy
),
399 static const struct of_device_id qcom_snps_hsphy_match
[] = {
400 { .compatible
= "qcom,usb-hs-28nm-femtophy", .data
= &hsphy_data_femtophy
, },
403 MODULE_DEVICE_TABLE(of
, qcom_snps_hsphy_match
);
405 static struct platform_driver qcom_snps_hsphy_driver
= {
406 .probe
= qcom_snps_hsphy_probe
,
408 .name
= "qcom,usb-hs-28nm-phy",
409 .of_match_table
= qcom_snps_hsphy_match
,
412 module_platform_driver(qcom_snps_hsphy_driver
);
414 MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver");
415 MODULE_LICENSE("GPL v2");