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
);
164 qcom_snps_hsphy_disable_hv_interrupts(priv
);
165 qcom_snps_hsphy_exit_retention(priv
);
170 static int qcom_snps_hsphy_power_off(struct phy
*phy
)
172 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
174 qcom_snps_hsphy_enter_retention(priv
);
175 qcom_snps_hsphy_enable_hv_interrupts(priv
);
176 regulator_bulk_disable(VREG_NUM
, priv
->vregs
);
181 static int qcom_snps_hsphy_reset(struct hsphy_priv
*priv
)
185 ret
= reset_control_assert(priv
->phy_reset
);
189 usleep_range(10, 15);
191 ret
= reset_control_deassert(priv
->phy_reset
);
195 usleep_range(80, 100);
200 static void qcom_snps_hsphy_init_sequence(struct hsphy_priv
*priv
)
202 const struct hsphy_data
*data
= priv
->data
;
203 const struct hsphy_init_seq
*seq
;
206 /* Device match data is optional. */
210 seq
= data
->init_seq
;
212 for (i
= 0; i
< data
->init_seq_num
; i
++, seq
++) {
213 writeb(seq
->val
, priv
->base
+ seq
->offset
);
215 usleep_range(seq
->delay
, seq
->delay
+ 10);
219 static int qcom_snps_hsphy_por_reset(struct hsphy_priv
*priv
)
223 ret
= reset_control_assert(priv
->por_reset
);
228 * The Femto PHY is POR reset in the following scenarios.
230 * 1. After overriding the parameter registers.
231 * 2. Low power mode exit from PHY retention.
233 * Ensure that SIDDQ is cleared before bringing the PHY
236 qcom_snps_hsphy_exit_retention(priv
);
239 * As per databook, 10 usec delay is required between
240 * PHY POR assert and de-assert.
242 usleep_range(10, 20);
243 ret
= reset_control_deassert(priv
->por_reset
);
248 * As per databook, it takes 75 usec for PHY to stabilize
251 usleep_range(80, 100);
256 static int qcom_snps_hsphy_init(struct phy
*phy
)
258 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
261 ret
= clk_bulk_prepare_enable(priv
->num_clks
, priv
->clks
);
265 ret
= qcom_snps_hsphy_reset(priv
);
269 qcom_snps_hsphy_init_sequence(priv
);
271 ret
= qcom_snps_hsphy_por_reset(priv
);
278 clk_bulk_disable_unprepare(priv
->num_clks
, priv
->clks
);
282 static int qcom_snps_hsphy_exit(struct phy
*phy
)
284 struct hsphy_priv
*priv
= phy_get_drvdata(phy
);
286 clk_bulk_disable_unprepare(priv
->num_clks
, priv
->clks
);
291 static const struct phy_ops qcom_snps_hsphy_ops
= {
292 .init
= qcom_snps_hsphy_init
,
293 .exit
= qcom_snps_hsphy_exit
,
294 .power_on
= qcom_snps_hsphy_power_on
,
295 .power_off
= qcom_snps_hsphy_power_off
,
296 .set_mode
= qcom_snps_hsphy_set_mode
,
297 .owner
= THIS_MODULE
,
300 static const char * const qcom_snps_hsphy_clks
[] = {
306 static int qcom_snps_hsphy_probe(struct platform_device
*pdev
)
308 struct device
*dev
= &pdev
->dev
;
309 struct phy_provider
*provider
;
310 struct hsphy_priv
*priv
;
315 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
319 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
320 if (IS_ERR(priv
->base
))
321 return PTR_ERR(priv
->base
);
323 priv
->num_clks
= ARRAY_SIZE(qcom_snps_hsphy_clks
);
324 priv
->clks
= devm_kcalloc(dev
, priv
->num_clks
, sizeof(*priv
->clks
),
329 for (i
= 0; i
< priv
->num_clks
; i
++)
330 priv
->clks
[i
].id
= qcom_snps_hsphy_clks
[i
];
332 ret
= devm_clk_bulk_get(dev
, priv
->num_clks
, priv
->clks
);
336 priv
->phy_reset
= devm_reset_control_get_exclusive(dev
, "phy");
337 if (IS_ERR(priv
->phy_reset
))
338 return PTR_ERR(priv
->phy_reset
);
340 priv
->por_reset
= devm_reset_control_get_exclusive(dev
, "por");
341 if (IS_ERR(priv
->por_reset
))
342 return PTR_ERR(priv
->por_reset
);
344 priv
->vregs
[VDD
].supply
= "vdd";
345 priv
->vregs
[VDDA_1P8
].supply
= "vdda1p8";
346 priv
->vregs
[VDDA_3P3
].supply
= "vdda3p3";
348 ret
= devm_regulator_bulk_get(dev
, VREG_NUM
, priv
->vregs
);
352 /* Get device match data */
353 priv
->data
= device_get_match_data(dev
);
355 phy
= devm_phy_create(dev
, dev
->of_node
, &qcom_snps_hsphy_ops
);
359 phy_set_drvdata(phy
, priv
);
361 provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
362 if (IS_ERR(provider
))
363 return PTR_ERR(provider
);
365 ret
= regulator_set_load(priv
->vregs
[VDDA_1P8
].consumer
, 19000);
369 ret
= regulator_set_load(priv
->vregs
[VDDA_3P3
].consumer
, 16000);
376 regulator_set_load(priv
->vregs
[VDDA_1P8
].consumer
, 0);
382 * The macro is used to define an initialization sequence. Each tuple
383 * is meant to program 'value' into phy register at 'offset' with 'delay'
386 #define HSPHY_INIT_CFG(o, v, d) { .offset = o, .val = v, .delay = d, }
388 static const struct hsphy_init_seq init_seq_femtophy
[] = {
389 HSPHY_INIT_CFG(0xc0, 0x01, 0),
390 HSPHY_INIT_CFG(0xe8, 0x0d, 0),
391 HSPHY_INIT_CFG(0x74, 0x12, 0),
392 HSPHY_INIT_CFG(0x98, 0x63, 0),
393 HSPHY_INIT_CFG(0x9c, 0x03, 0),
394 HSPHY_INIT_CFG(0xa0, 0x1d, 0),
395 HSPHY_INIT_CFG(0xa4, 0x03, 0),
396 HSPHY_INIT_CFG(0x8c, 0x23, 0),
397 HSPHY_INIT_CFG(0x78, 0x08, 0),
398 HSPHY_INIT_CFG(0x7c, 0xdc, 0),
399 HSPHY_INIT_CFG(0x90, 0xe0, 20),
400 HSPHY_INIT_CFG(0x74, 0x10, 0),
401 HSPHY_INIT_CFG(0x90, 0x60, 0),
404 static const struct hsphy_data hsphy_data_femtophy
= {
405 .init_seq
= init_seq_femtophy
,
406 .init_seq_num
= ARRAY_SIZE(init_seq_femtophy
),
409 static const struct of_device_id qcom_snps_hsphy_match
[] = {
410 { .compatible
= "qcom,usb-hs-28nm-femtophy", .data
= &hsphy_data_femtophy
, },
413 MODULE_DEVICE_TABLE(of
, qcom_snps_hsphy_match
);
415 static struct platform_driver qcom_snps_hsphy_driver
= {
416 .probe
= qcom_snps_hsphy_probe
,
418 .name
= "qcom,usb-hs-28nm-phy",
419 .of_match_table
= qcom_snps_hsphy_match
,
422 module_platform_driver(qcom_snps_hsphy_driver
);
424 MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver");
425 MODULE_LICENSE("GPL v2");