1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (c) 2017 NXP. */
4 #include <linux/bitfield.h>
6 #include <linux/delay.h>
8 #include <linux/module.h>
9 #include <linux/of_platform.h>
10 #include <linux/phy/phy.h>
11 #include <linux/platform_device.h>
12 #include <linux/regulator/consumer.h>
15 #define PHY_CTRL0_REF_SSP_EN BIT(2)
16 #define PHY_CTRL0_FSEL_MASK GENMASK(10, 5)
17 #define PHY_CTRL0_FSEL_24M 0x2a
20 #define PHY_CTRL1_RESET BIT(0)
21 #define PHY_CTRL1_COMMONONN BIT(1)
22 #define PHY_CTRL1_ATERESET BIT(3)
23 #define PHY_CTRL1_VDATSRCENB0 BIT(19)
24 #define PHY_CTRL1_VDATDETENB0 BIT(20)
27 #define PHY_CTRL2_TXENABLEN0 BIT(8)
28 #define PHY_CTRL2_OTG_DISABLE BIT(9)
30 #define PHY_CTRL6 0x18
31 #define PHY_CTRL6_ALT_CLK_EN BIT(1)
32 #define PHY_CTRL6_ALT_CLK_SEL BIT(0)
34 struct imx8mq_usb_phy
{
38 struct regulator
*vbus
;
41 static int imx8mq_usb_phy_init(struct phy
*phy
)
43 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
46 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
47 value
&= ~(PHY_CTRL1_VDATSRCENB0
| PHY_CTRL1_VDATDETENB0
|
49 value
|= PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
;
50 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
52 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
53 value
|= PHY_CTRL0_REF_SSP_EN
;
54 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
56 value
= readl(imx_phy
->base
+ PHY_CTRL2
);
57 value
|= PHY_CTRL2_TXENABLEN0
;
58 writel(value
, imx_phy
->base
+ PHY_CTRL2
);
60 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
61 value
&= ~(PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
);
62 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
67 static int imx8mp_usb_phy_init(struct phy
*phy
)
69 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
72 /* USB3.0 PHY signal fsel for 24M ref */
73 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
74 value
&= ~PHY_CTRL0_FSEL_MASK
;
75 value
|= FIELD_PREP(PHY_CTRL0_FSEL_MASK
, PHY_CTRL0_FSEL_24M
);
76 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
78 /* Disable alt_clk_en and use internal MPLL clocks */
79 value
= readl(imx_phy
->base
+ PHY_CTRL6
);
80 value
&= ~(PHY_CTRL6_ALT_CLK_SEL
| PHY_CTRL6_ALT_CLK_EN
);
81 writel(value
, imx_phy
->base
+ PHY_CTRL6
);
83 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
84 value
&= ~(PHY_CTRL1_VDATSRCENB0
| PHY_CTRL1_VDATDETENB0
);
85 value
|= PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
;
86 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
88 value
= readl(imx_phy
->base
+ PHY_CTRL0
);
89 value
|= PHY_CTRL0_REF_SSP_EN
;
90 writel(value
, imx_phy
->base
+ PHY_CTRL0
);
92 value
= readl(imx_phy
->base
+ PHY_CTRL2
);
93 value
|= PHY_CTRL2_TXENABLEN0
| PHY_CTRL2_OTG_DISABLE
;
94 writel(value
, imx_phy
->base
+ PHY_CTRL2
);
98 value
= readl(imx_phy
->base
+ PHY_CTRL1
);
99 value
&= ~(PHY_CTRL1_RESET
| PHY_CTRL1_ATERESET
);
100 writel(value
, imx_phy
->base
+ PHY_CTRL1
);
105 static int imx8mq_phy_power_on(struct phy
*phy
)
107 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
110 ret
= regulator_enable(imx_phy
->vbus
);
114 return clk_prepare_enable(imx_phy
->clk
);
117 static int imx8mq_phy_power_off(struct phy
*phy
)
119 struct imx8mq_usb_phy
*imx_phy
= phy_get_drvdata(phy
);
121 clk_disable_unprepare(imx_phy
->clk
);
122 regulator_disable(imx_phy
->vbus
);
127 static const struct phy_ops imx8mq_usb_phy_ops
= {
128 .init
= imx8mq_usb_phy_init
,
129 .power_on
= imx8mq_phy_power_on
,
130 .power_off
= imx8mq_phy_power_off
,
131 .owner
= THIS_MODULE
,
134 static const struct phy_ops imx8mp_usb_phy_ops
= {
135 .init
= imx8mp_usb_phy_init
,
136 .power_on
= imx8mq_phy_power_on
,
137 .power_off
= imx8mq_phy_power_off
,
138 .owner
= THIS_MODULE
,
141 static const struct of_device_id imx8mq_usb_phy_of_match
[] = {
142 {.compatible
= "fsl,imx8mq-usb-phy",
143 .data
= &imx8mq_usb_phy_ops
,},
144 {.compatible
= "fsl,imx8mp-usb-phy",
145 .data
= &imx8mp_usb_phy_ops
,},
148 MODULE_DEVICE_TABLE(of
, imx8mq_usb_phy_of_match
);
150 static int imx8mq_usb_phy_probe(struct platform_device
*pdev
)
152 struct phy_provider
*phy_provider
;
153 struct device
*dev
= &pdev
->dev
;
154 struct imx8mq_usb_phy
*imx_phy
;
155 const struct phy_ops
*phy_ops
;
157 imx_phy
= devm_kzalloc(dev
, sizeof(*imx_phy
), GFP_KERNEL
);
161 imx_phy
->clk
= devm_clk_get(dev
, "phy");
162 if (IS_ERR(imx_phy
->clk
)) {
163 dev_err(dev
, "failed to get imx8mq usb phy clock\n");
164 return PTR_ERR(imx_phy
->clk
);
167 imx_phy
->base
= devm_platform_ioremap_resource(pdev
, 0);
168 if (IS_ERR(imx_phy
->base
))
169 return PTR_ERR(imx_phy
->base
);
171 phy_ops
= of_device_get_match_data(dev
);
175 imx_phy
->phy
= devm_phy_create(dev
, NULL
, phy_ops
);
176 if (IS_ERR(imx_phy
->phy
))
177 return PTR_ERR(imx_phy
->phy
);
179 imx_phy
->vbus
= devm_regulator_get(dev
, "vbus");
180 if (IS_ERR(imx_phy
->vbus
))
181 return PTR_ERR(imx_phy
->vbus
);
183 phy_set_drvdata(imx_phy
->phy
, imx_phy
);
185 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
187 return PTR_ERR_OR_ZERO(phy_provider
);
190 static struct platform_driver imx8mq_usb_phy_driver
= {
191 .probe
= imx8mq_usb_phy_probe
,
193 .name
= "imx8mq-usb-phy",
194 .of_match_table
= imx8mq_usb_phy_of_match
,
197 module_platform_driver(imx8mq_usb_phy_driver
);
199 MODULE_DESCRIPTION("FSL IMX8MQ USB PHY driver");
200 MODULE_LICENSE("GPL");