1 // SPDX-License-Identifier: GPL-2.0-only
3 * IMG Pistachio USB PHY driver
5 * Copyright (C) 2015 Google, Inc.
9 #include <linux/delay.h>
11 #include <linux/kernel.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
19 #include <dt-bindings/phy/phy-pistachio-usb.h>
21 #define USB_PHY_CONTROL1 0x04
22 #define USB_PHY_CONTROL1_FSEL_SHIFT 2
23 #define USB_PHY_CONTROL1_FSEL_MASK 0x7
25 #define USB_PHY_STRAP_CONTROL 0x10
26 #define USB_PHY_STRAP_CONTROL_REFCLK_SHIFT 4
27 #define USB_PHY_STRAP_CONTROL_REFCLK_MASK 0x3
29 #define USB_PHY_STATUS 0x14
30 #define USB_PHY_STATUS_RX_PHY_CLK BIT(9)
31 #define USB_PHY_STATUS_RX_UTMI_CLK BIT(8)
32 #define USB_PHY_STATUS_VBUS_FAULT BIT(7)
34 struct pistachio_usb_phy
{
36 struct regmap
*cr_top
;
41 static const unsigned long fsel_rate_map
[] = {
52 static int pistachio_usb_phy_power_on(struct phy
*phy
)
54 struct pistachio_usb_phy
*p_phy
= phy_get_drvdata(phy
);
55 unsigned long timeout
, rate
;
59 ret
= clk_prepare_enable(p_phy
->phy_clk
);
61 dev_err(p_phy
->dev
, "Failed to enable PHY clock: %d\n", ret
);
65 regmap_update_bits(p_phy
->cr_top
, USB_PHY_STRAP_CONTROL
,
66 USB_PHY_STRAP_CONTROL_REFCLK_MASK
<<
67 USB_PHY_STRAP_CONTROL_REFCLK_SHIFT
,
68 p_phy
->refclk
<< USB_PHY_STRAP_CONTROL_REFCLK_SHIFT
);
70 rate
= clk_get_rate(p_phy
->phy_clk
);
71 if (p_phy
->refclk
== REFCLK_XO_CRYSTAL
&& rate
!= 12000000) {
72 dev_err(p_phy
->dev
, "Unsupported rate for XO crystal: %ld\n",
78 for (i
= 0; i
< ARRAY_SIZE(fsel_rate_map
); i
++) {
79 if (rate
== fsel_rate_map
[i
])
82 if (i
== ARRAY_SIZE(fsel_rate_map
)) {
83 dev_err(p_phy
->dev
, "Unsupported clock rate: %lu\n", rate
);
88 regmap_update_bits(p_phy
->cr_top
, USB_PHY_CONTROL1
,
89 USB_PHY_CONTROL1_FSEL_MASK
<<
90 USB_PHY_CONTROL1_FSEL_SHIFT
,
91 i
<< USB_PHY_CONTROL1_FSEL_SHIFT
);
93 timeout
= jiffies
+ msecs_to_jiffies(200);
94 while (time_before(jiffies
, timeout
)) {
97 regmap_read(p_phy
->cr_top
, USB_PHY_STATUS
, &val
);
98 if (val
& USB_PHY_STATUS_VBUS_FAULT
) {
99 dev_err(p_phy
->dev
, "VBUS fault detected\n");
103 if ((val
& USB_PHY_STATUS_RX_PHY_CLK
) &&
104 (val
& USB_PHY_STATUS_RX_UTMI_CLK
))
106 usleep_range(1000, 1500);
109 dev_err(p_phy
->dev
, "Timed out waiting for PHY to power on\n");
113 clk_disable_unprepare(p_phy
->phy_clk
);
117 static int pistachio_usb_phy_power_off(struct phy
*phy
)
119 struct pistachio_usb_phy
*p_phy
= phy_get_drvdata(phy
);
121 clk_disable_unprepare(p_phy
->phy_clk
);
126 static const struct phy_ops pistachio_usb_phy_ops
= {
127 .power_on
= pistachio_usb_phy_power_on
,
128 .power_off
= pistachio_usb_phy_power_off
,
129 .owner
= THIS_MODULE
,
132 static int pistachio_usb_phy_probe(struct platform_device
*pdev
)
134 struct pistachio_usb_phy
*p_phy
;
135 struct phy_provider
*provider
;
139 p_phy
= devm_kzalloc(&pdev
->dev
, sizeof(*p_phy
), GFP_KERNEL
);
142 p_phy
->dev
= &pdev
->dev
;
143 platform_set_drvdata(pdev
, p_phy
);
145 p_phy
->cr_top
= syscon_regmap_lookup_by_phandle(p_phy
->dev
->of_node
,
147 if (IS_ERR(p_phy
->cr_top
)) {
148 dev_err(p_phy
->dev
, "Failed to get CR_TOP registers: %ld\n",
149 PTR_ERR(p_phy
->cr_top
));
150 return PTR_ERR(p_phy
->cr_top
);
153 p_phy
->phy_clk
= devm_clk_get(p_phy
->dev
, "usb_phy");
154 if (IS_ERR(p_phy
->phy_clk
)) {
155 dev_err(p_phy
->dev
, "Failed to get usb_phy clock: %ld\n",
156 PTR_ERR(p_phy
->phy_clk
));
157 return PTR_ERR(p_phy
->phy_clk
);
160 ret
= of_property_read_u32(p_phy
->dev
->of_node
, "img,refclk",
163 dev_err(p_phy
->dev
, "No reference clock selector specified\n");
167 phy
= devm_phy_create(p_phy
->dev
, NULL
, &pistachio_usb_phy_ops
);
169 dev_err(p_phy
->dev
, "Failed to create PHY: %ld\n",
173 phy_set_drvdata(phy
, p_phy
);
175 provider
= devm_of_phy_provider_register(p_phy
->dev
,
176 of_phy_simple_xlate
);
177 if (IS_ERR(provider
)) {
178 dev_err(p_phy
->dev
, "Failed to register PHY provider: %ld\n",
180 return PTR_ERR(provider
);
186 static const struct of_device_id pistachio_usb_phy_of_match
[] = {
187 { .compatible
= "img,pistachio-usb-phy", },
190 MODULE_DEVICE_TABLE(of
, pistachio_usb_phy_of_match
);
192 static struct platform_driver pistachio_usb_phy_driver
= {
193 .probe
= pistachio_usb_phy_probe
,
195 .name
= "pistachio-usb-phy",
196 .of_match_table
= pistachio_usb_phy_of_match
,
199 module_platform_driver(pistachio_usb_phy_driver
);
201 MODULE_AUTHOR("Andrew Bresticker <abrestic@chromium.org>");
202 MODULE_DESCRIPTION("IMG Pistachio USB2.0 PHY driver");
203 MODULE_LICENSE("GPL v2");