1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/module.h>
3 #include <linux/platform_device.h>
7 #include <linux/delay.h>
8 #include <linux/usb/otg.h>
9 #include "phy-am335x-control.h"
11 struct am335x_control_usb
{
13 void __iomem
*phy_reg
;
16 struct phy_control phy_ctrl
;
19 #define AM335X_USB0_CTRL 0x0
20 #define AM335X_USB1_CTRL 0x8
21 #define AM335x_USB_WKUP 0x0
23 #define USBPHY_CM_PWRDN (1 << 0)
24 #define USBPHY_OTG_PWRDN (1 << 1)
25 #define USBPHY_OTGVDET_EN (1 << 19)
26 #define USBPHY_OTGSESSEND_EN (1 << 20)
28 #define AM335X_PHY0_WK_EN (1 << 0)
29 #define AM335X_PHY1_WK_EN (1 << 8)
31 static void am335x_phy_wkup(struct phy_control
*phy_ctrl
, u32 id
, bool on
)
33 struct am335x_control_usb
*usb_ctrl
;
37 usb_ctrl
= container_of(phy_ctrl
, struct am335x_control_usb
, phy_ctrl
);
41 reg
= AM335X_PHY0_WK_EN
;
44 reg
= AM335X_PHY1_WK_EN
;
51 spin_lock(&usb_ctrl
->lock
);
52 val
= readl(usb_ctrl
->wkup
);
59 writel(val
, usb_ctrl
->wkup
);
60 spin_unlock(&usb_ctrl
->lock
);
63 static void am335x_phy_power(struct phy_control
*phy_ctrl
, u32 id
,
64 enum usb_dr_mode dr_mode
, bool on
)
66 struct am335x_control_usb
*usb_ctrl
;
70 usb_ctrl
= container_of(phy_ctrl
, struct am335x_control_usb
, phy_ctrl
);
74 reg
= AM335X_USB0_CTRL
;
77 reg
= AM335X_USB1_CTRL
;
84 val
= readl(usb_ctrl
->phy_reg
+ reg
);
86 if (dr_mode
== USB_DR_MODE_HOST
) {
87 val
&= ~(USBPHY_CM_PWRDN
| USBPHY_OTG_PWRDN
|
89 val
|= USBPHY_OTGSESSEND_EN
;
91 val
&= ~(USBPHY_CM_PWRDN
| USBPHY_OTG_PWRDN
);
92 val
|= USBPHY_OTGVDET_EN
| USBPHY_OTGSESSEND_EN
;
95 val
|= USBPHY_CM_PWRDN
| USBPHY_OTG_PWRDN
;
98 writel(val
, usb_ctrl
->phy_reg
+ reg
);
101 * Give the PHY ~1ms to complete the power up operation.
102 * Tests have shown unstable behaviour if other USB PHY related
103 * registers are written too shortly after such a transition.
109 static const struct phy_control ctrl_am335x
= {
110 .phy_power
= am335x_phy_power
,
111 .phy_wkup
= am335x_phy_wkup
,
114 static const struct of_device_id omap_control_usb_id_table
[] = {
115 { .compatible
= "ti,am335x-usb-ctrl-module", .data
= &ctrl_am335x
},
118 MODULE_DEVICE_TABLE(of
, omap_control_usb_id_table
);
120 static struct platform_driver am335x_control_driver
;
121 static int match(struct device
*dev
, const void *data
)
123 const struct device_node
*node
= (const struct device_node
*)data
;
124 return dev
->of_node
== node
&&
125 dev
->driver
== &am335x_control_driver
.driver
;
128 struct phy_control
*am335x_get_phy_control(struct device
*dev
)
130 struct device_node
*node
;
131 struct am335x_control_usb
*ctrl_usb
;
133 node
= of_parse_phandle(dev
->of_node
, "ti,ctrl_mod", 0);
137 dev
= bus_find_device(&platform_bus_type
, NULL
, node
, match
);
142 ctrl_usb
= dev_get_drvdata(dev
);
146 return &ctrl_usb
->phy_ctrl
;
148 EXPORT_SYMBOL_GPL(am335x_get_phy_control
);
150 static int am335x_control_usb_probe(struct platform_device
*pdev
)
152 struct resource
*res
;
153 struct am335x_control_usb
*ctrl_usb
;
154 const struct of_device_id
*of_id
;
155 const struct phy_control
*phy_ctrl
;
157 of_id
= of_match_node(omap_control_usb_id_table
, pdev
->dev
.of_node
);
161 phy_ctrl
= of_id
->data
;
163 ctrl_usb
= devm_kzalloc(&pdev
->dev
, sizeof(*ctrl_usb
), GFP_KERNEL
);
167 ctrl_usb
->dev
= &pdev
->dev
;
169 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "phy_ctrl");
170 ctrl_usb
->phy_reg
= devm_ioremap_resource(&pdev
->dev
, res
);
171 if (IS_ERR(ctrl_usb
->phy_reg
))
172 return PTR_ERR(ctrl_usb
->phy_reg
);
174 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "wakeup");
175 ctrl_usb
->wkup
= devm_ioremap_resource(&pdev
->dev
, res
);
176 if (IS_ERR(ctrl_usb
->wkup
))
177 return PTR_ERR(ctrl_usb
->wkup
);
179 spin_lock_init(&ctrl_usb
->lock
);
180 ctrl_usb
->phy_ctrl
= *phy_ctrl
;
182 dev_set_drvdata(ctrl_usb
->dev
, ctrl_usb
);
186 static struct platform_driver am335x_control_driver
= {
187 .probe
= am335x_control_usb_probe
,
189 .name
= "am335x-control-usb",
190 .of_match_table
= omap_control_usb_id_table
,
194 module_platform_driver(am335x_control_driver
);
195 MODULE_LICENSE("GPL v2");