summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/ehci-omap.c
diff options
context:
space:
mode:
authorAnand Gadiyar <gadiyar@ti.com>2011-02-16 11:13:14 +0100
committerFelipe Balbi <balbi@ti.com>2011-03-01 16:00:56 +0100
commit87ecc73b3d74ca70100e7100313c005fa471f177 (patch)
tree18762ed9b092982b196aa0c5681d01cb80e2e1ab /drivers/usb/host/ehci-omap.c
parentusb: musb: tusb: Fix possible null pointer dereference in tusb6010_omap.c (diff)
downloadlinux-87ecc73b3d74ca70100e7100313c005fa471f177.tar.xz
linux-87ecc73b3d74ca70100e7100313c005fa471f177.zip
usb: ehci: omap: add support for TLL mode on OMAP4
The EHCI controller in OMAP4 supports a transceiver-less link mode (TLL mode), similar to the one in OMAP3. On the OMAP4 however, there are an additional set of clocks that need to be turned on to get this working. Request and configure these for each port if that port is connected in TLL mode. Signed-off-by: Anand Gadiyar <gadiyar@ti.com> Cc: Greg Kroah-Hartman <gregkh@suse.de> Signed-off-by: Felipe Balbi <balbi@ti.com>
Diffstat (limited to 'drivers/usb/host/ehci-omap.c')
-rw-r--r--drivers/usb/host/ehci-omap.c134
1 files changed, 134 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index f784ceb862a3..d7e223be1c9c 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -195,7 +195,11 @@ struct ehci_hcd_omap {
struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck;
struct clk *utmi_p1_fck;
+ struct clk *usbhost_p1_fck;
+ struct clk *usbtll_p1_fck;
struct clk *utmi_p2_fck;
+ struct clk *usbhost_p2_fck;
+ struct clk *usbtll_p2_fck;
/* FIXME the following two workarounds are
* board specific not silicon-specific so these
@@ -410,6 +414,50 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
}
break;
case EHCI_HCD_OMAP_MODE_TLL:
+ omap->xclk60mhsp1_ck = clk_get(omap->dev,
+ "init_60m_fclk");
+ if (IS_ERR(omap->xclk60mhsp1_ck)) {
+ ret = PTR_ERR(omap->xclk60mhsp1_ck);
+ dev_err(omap->dev,
+ "Unable to get Port1 ULPI clock\n");
+ }
+
+ omap->utmi_p1_fck = clk_get(omap->dev,
+ "utmi_p1_gfclk");
+ if (IS_ERR(omap->utmi_p1_fck)) {
+ ret = PTR_ERR(omap->utmi_p1_fck);
+ dev_err(omap->dev,
+ "Unable to get utmi_p1_fck\n");
+ }
+
+ ret = clk_set_parent(omap->utmi_p1_fck,
+ omap->xclk60mhsp1_ck);
+ if (ret != 0) {
+ dev_err(omap->dev,
+ "Unable to set P1 f-clock\n");
+ }
+
+ omap->usbhost_p1_fck = clk_get(omap->dev,
+ "usb_host_hs_utmi_p1_clk");
+ if (IS_ERR(omap->usbhost_p1_fck)) {
+ ret = PTR_ERR(omap->usbhost_p1_fck);
+ dev_err(omap->dev,
+ "Unable to get HOST PORT 1 clk\n");
+ } else {
+ clk_enable(omap->usbhost_p1_fck);
+ }
+
+ omap->usbtll_p1_fck = clk_get(omap->dev,
+ "usb_tll_hs_usb_ch0_clk");
+
+ if (IS_ERR(omap->usbtll_p1_fck)) {
+ ret = PTR_ERR(omap->usbtll_p1_fck);
+ dev_err(omap->dev,
+ "Unable to get TLL CH0 clk\n");
+ } else {
+ clk_enable(omap->usbtll_p1_fck);
+ }
+ break;
/* TODO */
default:
break;
@@ -440,6 +488,50 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
}
break;
case EHCI_HCD_OMAP_MODE_TLL:
+ omap->xclk60mhsp2_ck = clk_get(omap->dev,
+ "init_60m_fclk");
+ if (IS_ERR(omap->xclk60mhsp2_ck)) {
+ ret = PTR_ERR(omap->xclk60mhsp2_ck);
+ dev_err(omap->dev,
+ "Unable to get Port2 ULPI clock\n");
+ }
+
+ omap->utmi_p2_fck = clk_get(omap->dev,
+ "utmi_p2_gfclk");
+ if (IS_ERR(omap->utmi_p2_fck)) {
+ ret = PTR_ERR(omap->utmi_p2_fck);
+ dev_err(omap->dev,
+ "Unable to get utmi_p2_fck\n");
+ }
+
+ ret = clk_set_parent(omap->utmi_p2_fck,
+ omap->xclk60mhsp2_ck);
+ if (ret != 0) {
+ dev_err(omap->dev,
+ "Unable to set P2 f-clock\n");
+ }
+
+ omap->usbhost_p2_fck = clk_get(omap->dev,
+ "usb_host_hs_utmi_p2_clk");
+ if (IS_ERR(omap->usbhost_p2_fck)) {
+ ret = PTR_ERR(omap->usbhost_p2_fck);
+ dev_err(omap->dev,
+ "Unable to get HOST PORT 2 clk\n");
+ } else {
+ clk_enable(omap->usbhost_p2_fck);
+ }
+
+ omap->usbtll_p2_fck = clk_get(omap->dev,
+ "usb_tll_hs_usb_ch1_clk");
+
+ if (IS_ERR(omap->usbtll_p2_fck)) {
+ ret = PTR_ERR(omap->usbtll_p2_fck);
+ dev_err(omap->dev,
+ "Unable to get TLL CH1 clk\n");
+ } else {
+ clk_enable(omap->usbtll_p2_fck);
+ }
+ break;
/* TODO */
default:
break;
@@ -602,6 +694,24 @@ static int omap_start_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
return 0;
err_sys_status:
+
+ if (omap->usbtll_p2_fck != NULL) {
+ clk_disable(omap->usbtll_p2_fck);
+ clk_put(omap->usbtll_p2_fck);
+ }
+ if (omap->usbhost_p2_fck != NULL) {
+ clk_disable(omap->usbhost_p2_fck);
+ clk_put(omap->usbhost_p2_fck);
+ }
+ if (omap->usbtll_p1_fck != NULL) {
+ clk_disable(omap->usbtll_p1_fck);
+ clk_put(omap->usbtll_p1_fck);
+ }
+ if (omap->usbhost_p1_fck != NULL) {
+ clk_disable(omap->usbhost_p1_fck);
+ clk_put(omap->usbhost_p1_fck);
+ }
+
clk_disable(omap->utmi_p2_fck);
clk_put(omap->utmi_p2_fck);
clk_disable(omap->xclk60mhsp2_ck);
@@ -740,6 +850,30 @@ static void omap_stop_ehc(struct ehci_hcd_omap *omap, struct usb_hcd *hcd)
clk_put(omap->utmi_p2_fck);
omap->utmi_p2_fck = NULL;
}
+
+ if (omap->usbtll_p2_fck != NULL) {
+ clk_disable(omap->usbtll_p2_fck);
+ clk_put(omap->usbtll_p2_fck);
+ omap->usbtll_p2_fck = NULL;
+ }
+
+ if (omap->usbhost_p2_fck != NULL) {
+ clk_disable(omap->usbhost_p2_fck);
+ clk_put(omap->usbhost_p2_fck);
+ omap->usbhost_p2_fck = NULL;
+ }
+
+ if (omap->usbtll_p1_fck != NULL) {
+ clk_disable(omap->usbtll_p1_fck);
+ clk_put(omap->usbtll_p1_fck);
+ omap->usbtll_p1_fck = NULL;
+ }
+
+ if (omap->usbhost_p1_fck != NULL) {
+ clk_disable(omap->usbhost_p1_fck);
+ clk_put(omap->usbhost_p1_fck);
+ omap->usbhost_p1_fck = NULL;
+ }
}
if (omap->phy_reset) {