Commit 1e7fe1a9 authored by Keshava Munegowda's avatar Keshava Munegowda Committed by Paul Walmsley

MFD: OMAP: USB: Runtime PM support

The usbhs core driver does not enable/disable the interface and
functional clocks directly, These clocks are handled by runtime pm,
hence instead of the clock enable/disable, the runtime pm APIS are
used. however,the optional clocks and port clocks are handled by
the usbhs core.

Dependency:
This patch is dependent on this series:
[PATCH 0/5 v13 or latest version] omap: usb: host: Runtime PM preparation
for EHCI and OHCI drivers.

Validation performed:
The global suspend/resume of EHCI and OHCI is validated on
OMAP3430 sdp board with this patch combined with the series:
[PATCH 0/5 v13 or latest version] omap: usb: host: Runtime PM preparation
for EHCI and OHCI drivers.
Signed-off-by: default avatarKeshava Munegowda <keshava_mgowda@ti.com>
Reviewed-by: default avatarKevin Hilman <khilman@ti.com>
Reviewed-by: default avatarPartha Basak <parthab@india.ti.com>
Acked-by: default avatarFelipe Balbi <balbi@ti.com>
Acked-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
Signed-off-by: default avatarPaul Walmsley <paul@pwsan.com>
parent 6c984b06
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <plat/usb.h> #include <plat/usb.h>
#include <linux/pm_runtime.h>
#define USBHS_DRIVER_NAME "usbhs_omap" #define USBHS_DRIVER_NAME "usbhs_omap"
#define OMAP_EHCI_DEVICE "ehci-omap" #define OMAP_EHCI_DEVICE "ehci-omap"
...@@ -147,9 +148,6 @@ ...@@ -147,9 +148,6 @@
struct usbhs_hcd_omap { struct usbhs_hcd_omap {
struct clk *usbhost_ick;
struct clk *usbhost_hs_fck;
struct clk *usbhost_fs_fck;
struct clk *xclk60mhsp1_ck; struct clk *xclk60mhsp1_ck;
struct clk *xclk60mhsp2_ck; struct clk *xclk60mhsp2_ck;
struct clk *utmi_p1_fck; struct clk *utmi_p1_fck;
...@@ -159,8 +157,7 @@ struct usbhs_hcd_omap { ...@@ -159,8 +157,7 @@ struct usbhs_hcd_omap {
struct clk *usbhost_p2_fck; struct clk *usbhost_p2_fck;
struct clk *usbtll_p2_fck; struct clk *usbtll_p2_fck;
struct clk *init_60m_fclk; struct clk *init_60m_fclk;
struct clk *usbtll_fck; struct clk *ehci_logic_fck;
struct clk *usbtll_ick;
void __iomem *uhh_base; void __iomem *uhh_base;
void __iomem *tll_base; void __iomem *tll_base;
...@@ -169,7 +166,6 @@ struct usbhs_hcd_omap { ...@@ -169,7 +166,6 @@ struct usbhs_hcd_omap {
u32 usbhs_rev; u32 usbhs_rev;
spinlock_t lock; spinlock_t lock;
int count;
}; };
/*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/
...@@ -319,269 +315,6 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev) ...@@ -319,269 +315,6 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
return ret; return ret;
} }
/**
* usbhs_omap_probe - initialize TI-based HCDs
*
* Allocates basic resources for this USB host controller.
*/
static int __devinit usbhs_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct usbhs_omap_platform_data *pdata = dev->platform_data;
struct usbhs_hcd_omap *omap;
struct resource *res;
int ret = 0;
int i;
if (!pdata) {
dev_err(dev, "Missing platform data\n");
ret = -ENOMEM;
goto end_probe;
}
omap = kzalloc(sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto end_probe;
}
spin_lock_init(&omap->lock);
for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
omap->platdata.port_mode[i] = pdata->port_mode[i];
omap->platdata.ehci_data = pdata->ehci_data;
omap->platdata.ohci_data = pdata->ohci_data;
omap->usbhost_ick = clk_get(dev, "usbhost_ick");
if (IS_ERR(omap->usbhost_ick)) {
ret = PTR_ERR(omap->usbhost_ick);
dev_err(dev, "usbhost_ick failed error:%d\n", ret);
goto err_end;
}
omap->usbhost_hs_fck = clk_get(dev, "hs_fck");
if (IS_ERR(omap->usbhost_hs_fck)) {
ret = PTR_ERR(omap->usbhost_hs_fck);
dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret);
goto err_usbhost_ick;
}
omap->usbhost_fs_fck = clk_get(dev, "fs_fck");
if (IS_ERR(omap->usbhost_fs_fck)) {
ret = PTR_ERR(omap->usbhost_fs_fck);
dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret);
goto err_usbhost_hs_fck;
}
omap->usbtll_fck = clk_get(dev, "usbtll_fck");
if (IS_ERR(omap->usbtll_fck)) {
ret = PTR_ERR(omap->usbtll_fck);
dev_err(dev, "usbtll_fck failed error:%d\n", ret);
goto err_usbhost_fs_fck;
}
omap->usbtll_ick = clk_get(dev, "usbtll_ick");
if (IS_ERR(omap->usbtll_ick)) {
ret = PTR_ERR(omap->usbtll_ick);
dev_err(dev, "usbtll_ick failed error:%d\n", ret);
goto err_usbtll_fck;
}
omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
if (IS_ERR(omap->utmi_p1_fck)) {
ret = PTR_ERR(omap->utmi_p1_fck);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
goto err_usbtll_ick;
}
omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
if (IS_ERR(omap->xclk60mhsp1_ck)) {
ret = PTR_ERR(omap->xclk60mhsp1_ck);
dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
goto err_utmi_p1_fck;
}
omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
if (IS_ERR(omap->utmi_p2_fck)) {
ret = PTR_ERR(omap->utmi_p2_fck);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
goto err_xclk60mhsp1_ck;
}
omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
if (IS_ERR(omap->xclk60mhsp2_ck)) {
ret = PTR_ERR(omap->xclk60mhsp2_ck);
dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
goto err_utmi_p2_fck;
}
omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
if (IS_ERR(omap->usbhost_p1_fck)) {
ret = PTR_ERR(omap->usbhost_p1_fck);
dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
goto err_xclk60mhsp2_ck;
}
omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
if (IS_ERR(omap->usbtll_p1_fck)) {
ret = PTR_ERR(omap->usbtll_p1_fck);
dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
goto err_usbhost_p1_fck;
}
omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
if (IS_ERR(omap->usbhost_p2_fck)) {
ret = PTR_ERR(omap->usbhost_p2_fck);
dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
goto err_usbtll_p1_fck;
}
omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
if (IS_ERR(omap->usbtll_p2_fck)) {
ret = PTR_ERR(omap->usbtll_p2_fck);
dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
goto err_usbhost_p2_fck;
}
omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
if (IS_ERR(omap->init_60m_fclk)) {
ret = PTR_ERR(omap->init_60m_fclk);
dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
goto err_usbtll_p2_fck;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
if (!res) {
dev_err(dev, "UHH EHCI get resource failed\n");
ret = -ENODEV;
goto err_init_60m_fclk;
}
omap->uhh_base = ioremap(res->start, resource_size(res));
if (!omap->uhh_base) {
dev_err(dev, "UHH ioremap failed\n");
ret = -ENOMEM;
goto err_init_60m_fclk;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll");
if (!res) {
dev_err(dev, "UHH EHCI get resource failed\n");
ret = -ENODEV;
goto err_tll;
}
omap->tll_base = ioremap(res->start, resource_size(res));
if (!omap->tll_base) {
dev_err(dev, "TLL ioremap failed\n");
ret = -ENOMEM;
goto err_tll;
}
platform_set_drvdata(pdev, omap);
ret = omap_usbhs_alloc_children(pdev);
if (ret) {
dev_err(dev, "omap_usbhs_alloc_children failed\n");
goto err_alloc;
}
goto end_probe;
err_alloc:
iounmap(omap->tll_base);
err_tll:
iounmap(omap->uhh_base);
err_init_60m_fclk:
clk_put(omap->init_60m_fclk);
err_usbtll_p2_fck:
clk_put(omap->usbtll_p2_fck);
err_usbhost_p2_fck:
clk_put(omap->usbhost_p2_fck);
err_usbtll_p1_fck:
clk_put(omap->usbtll_p1_fck);
err_usbhost_p1_fck:
clk_put(omap->usbhost_p1_fck);
err_xclk60mhsp2_ck:
clk_put(omap->xclk60mhsp2_ck);
err_utmi_p2_fck:
clk_put(omap->utmi_p2_fck);
err_xclk60mhsp1_ck:
clk_put(omap->xclk60mhsp1_ck);
err_utmi_p1_fck:
clk_put(omap->utmi_p1_fck);
err_usbtll_ick:
clk_put(omap->usbtll_ick);
err_usbtll_fck:
clk_put(omap->usbtll_fck);
err_usbhost_fs_fck:
clk_put(omap->usbhost_fs_fck);
err_usbhost_hs_fck:
clk_put(omap->usbhost_hs_fck);
err_usbhost_ick:
clk_put(omap->usbhost_ick);
err_end:
kfree(omap);
end_probe:
return ret;
}
/**
* usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
* @pdev: USB Host Controller being removed
*
* Reverses the effect of usbhs_omap_probe().
*/
static int __devexit usbhs_omap_remove(struct platform_device *pdev)
{
struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
if (omap->count != 0) {
dev_err(&pdev->dev,
"Either EHCI or OHCI is still using usbhs core\n");
return -EBUSY;
}
iounmap(omap->tll_base);
iounmap(omap->uhh_base);
clk_put(omap->init_60m_fclk);
clk_put(omap->usbtll_p2_fck);
clk_put(omap->usbhost_p2_fck);
clk_put(omap->usbtll_p1_fck);
clk_put(omap->usbhost_p1_fck);
clk_put(omap->xclk60mhsp2_ck);
clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
clk_put(omap->utmi_p1_fck);
clk_put(omap->usbtll_ick);
clk_put(omap->usbtll_fck);
clk_put(omap->usbhost_fs_fck);
clk_put(omap->usbhost_hs_fck);
clk_put(omap->usbhost_ick);
kfree(omap);
return 0;
}
static bool is_ohci_port(enum usbhs_omap_port_mode pmode) static bool is_ohci_port(enum usbhs_omap_port_mode pmode)
{ {
switch (pmode) { switch (pmode) {
...@@ -689,30 +422,85 @@ static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count) ...@@ -689,30 +422,85 @@ static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count)
} }
} }
static int usbhs_enable(struct device *dev) static int usbhs_runtime_resume(struct device *dev)
{ {
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata; struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags = 0; unsigned long flags;
int ret = 0;
unsigned long timeout; dev_dbg(dev, "usbhs_runtime_resume\n");
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
if (!pdata) { if (!pdata) {
dev_dbg(dev, "missing platform_data\n"); dev_dbg(dev, "missing platform_data\n");
return -ENODEV; return -ENODEV;
} }
spin_lock_irqsave(&omap->lock, flags); spin_lock_irqsave(&omap->lock, flags);
if (omap->count > 0)
goto end_count;
clk_enable(omap->usbhost_ick); if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
clk_enable(omap->usbhost_hs_fck); clk_enable(omap->ehci_logic_fck);
clk_enable(omap->usbhost_fs_fck);
clk_enable(omap->usbtll_fck); if (is_ehci_tll_mode(pdata->port_mode[0])) {
clk_enable(omap->usbtll_ick); clk_enable(omap->usbhost_p1_fck);
clk_enable(omap->usbtll_p1_fck);
}
if (is_ehci_tll_mode(pdata->port_mode[1])) {
clk_enable(omap->usbhost_p2_fck);
clk_enable(omap->usbtll_p2_fck);
}
clk_enable(omap->utmi_p1_fck);
clk_enable(omap->utmi_p2_fck);
spin_unlock_irqrestore(&omap->lock, flags);
return 0;
}
static int usbhs_runtime_suspend(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags;
dev_dbg(dev, "usbhs_runtime_suspend\n");
if (!pdata) {
dev_dbg(dev, "missing platform_data\n");
return -ENODEV;
}
spin_lock_irqsave(&omap->lock, flags);
if (is_ehci_tll_mode(pdata->port_mode[0])) {
clk_disable(omap->usbhost_p1_fck);
clk_disable(omap->usbtll_p1_fck);
}
if (is_ehci_tll_mode(pdata->port_mode[1])) {
clk_disable(omap->usbhost_p2_fck);
clk_disable(omap->usbtll_p2_fck);
}
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
clk_disable(omap->ehci_logic_fck);
spin_unlock_irqrestore(&omap->lock, flags);
return 0;
}
static void omap_usbhs_init(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
unsigned long flags;
unsigned reg;
dev_dbg(dev, "starting TI HSUSB Controller\n");
pm_runtime_get_sync(dev);
spin_lock_irqsave(&omap->lock, flags);
if (pdata->ehci_data->phy_reset) { if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) { if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) {
...@@ -736,50 +524,6 @@ static int usbhs_enable(struct device *dev) ...@@ -736,50 +524,6 @@ static int usbhs_enable(struct device *dev)
omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev);
/* perform TLL soft reset, and wait until reset is complete */
usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
OMAP_USBTLL_SYSCONFIG_SOFTRESET);
/* Wait for TLL reset to complete */
timeout = jiffies + msecs_to_jiffies(1000);
while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
& OMAP_USBTLL_SYSSTATUS_RESETDONE)) {
cpu_relax();
if (time_after(jiffies, timeout)) {
dev_dbg(dev, "operation timed out\n");
ret = -EINVAL;
goto err_tll;
}
}
dev_dbg(dev, "TLL RESET DONE\n");
/* (1<<3) = no idle mode only for initial debugging */
usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG,
OMAP_USBTLL_SYSCONFIG_ENAWAKEUP |
OMAP_USBTLL_SYSCONFIG_SIDLEMODE |
OMAP_USBTLL_SYSCONFIG_AUTOIDLE);
/* Put UHH in NoIdle/NoStandby mode */
reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG);
if (is_omap_usbhs_rev1(omap)) {
reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP
| OMAP_UHH_SYSCONFIG_SIDLEMODE
| OMAP_UHH_SYSCONFIG_CACTIVITY
| OMAP_UHH_SYSCONFIG_MIDLEMODE);
reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE;
} else if (is_omap_usbhs_rev2(omap)) {
reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR;
reg |= OMAP4_UHH_SYSCONFIG_NOIDLE;
reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR;
reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY;
}
usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg);
reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG);
/* setup ULPI bypass and burst configurations */ /* setup ULPI bypass and burst configurations */
reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
...@@ -825,49 +569,6 @@ static int usbhs_enable(struct device *dev) ...@@ -825,49 +569,6 @@ static int usbhs_enable(struct device *dev)
reg &= ~OMAP4_P1_MODE_CLEAR; reg &= ~OMAP4_P1_MODE_CLEAR;
reg &= ~OMAP4_P2_MODE_CLEAR; reg &= ~OMAP4_P2_MODE_CLEAR;
if (is_ehci_phy_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_fck,
omap->xclk60mhsp1_ck);
if (ret != 0) {
dev_err(dev, "xclk60mhsp1_ck set parent"
"failed error:%d\n", ret);
goto err_tll;
}
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_fck,
omap->init_60m_fclk);
if (ret != 0) {
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
goto err_tll;
}
clk_enable(omap->usbhost_p1_fck);
clk_enable(omap->usbtll_p1_fck);
}
if (is_ehci_phy_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_fck,
omap->xclk60mhsp2_ck);
if (ret != 0) {
dev_err(dev, "xclk60mhsp1_ck set parent"
"failed error:%d\n", ret);
goto err_tll;
}
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_fck,
omap->init_60m_fclk);
if (ret != 0) {
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
goto err_tll;
}
clk_enable(omap->usbhost_p2_fck);
clk_enable(omap->usbtll_p2_fck);
}
clk_enable(omap->utmi_p1_fck);
clk_enable(omap->utmi_p2_fck);
if (is_ehci_tll_mode(pdata->port_mode[0]) || if (is_ehci_tll_mode(pdata->port_mode[0]) ||
(is_ohci_port(pdata->port_mode[0]))) (is_ohci_port(pdata->port_mode[0])))
reg |= OMAP4_P1_MODE_TLL; reg |= OMAP4_P1_MODE_TLL;
...@@ -913,12 +614,15 @@ static int usbhs_enable(struct device *dev) ...@@ -913,12 +614,15 @@ static int usbhs_enable(struct device *dev)
(pdata->ehci_data->reset_gpio_port[1], 1); (pdata->ehci_data->reset_gpio_port[1], 1);
} }
end_count:
omap->count++;
spin_unlock_irqrestore(&omap->lock, flags); spin_unlock_irqrestore(&omap->lock, flags);
return 0; pm_runtime_put_sync(dev);
}
static void omap_usbhs_deinit(struct device *dev)
{
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev);
struct usbhs_omap_platform_data *pdata = &omap->platdata;
err_tll:
if (pdata->ehci_data->phy_reset) { if (pdata->ehci_data->phy_reset) {
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
gpio_free(pdata->ehci_data->reset_gpio_port[0]); gpio_free(pdata->ehci_data->reset_gpio_port[0]);
...@@ -926,123 +630,272 @@ static int usbhs_enable(struct device *dev) ...@@ -926,123 +630,272 @@ static int usbhs_enable(struct device *dev)
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
gpio_free(pdata->ehci_data->reset_gpio_port[1]); gpio_free(pdata->ehci_data->reset_gpio_port[1]);
} }
clk_disable(omap->usbtll_ick);
clk_disable(omap->usbtll_fck);
clk_disable(omap->usbhost_fs_fck);
clk_disable(omap->usbhost_hs_fck);
clk_disable(omap->usbhost_ick);
spin_unlock_irqrestore(&omap->lock, flags);
return ret;
} }
static void usbhs_disable(struct device *dev)
/**
* usbhs_omap_probe - initialize TI-based HCDs
*
* Allocates basic resources for this USB host controller.
*/
static int __devinit usbhs_omap_probe(struct platform_device *pdev)
{ {
struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); struct device *dev = &pdev->dev;
struct usbhs_omap_platform_data *pdata = &omap->platdata; struct usbhs_omap_platform_data *pdata = dev->platform_data;
unsigned long flags = 0; struct usbhs_hcd_omap *omap;
unsigned long timeout; struct resource *res;
int ret = 0;
int i;
dev_dbg(dev, "stopping TI HSUSB Controller\n"); if (!pdata) {
dev_err(dev, "Missing platform data\n");
ret = -ENOMEM;
goto end_probe;
}
spin_lock_irqsave(&omap->lock, flags); omap = kzalloc(sizeof(*omap), GFP_KERNEL);
if (!omap) {
dev_err(dev, "Memory allocation failed\n");
ret = -ENOMEM;
goto end_probe;
}
if (omap->count == 0) spin_lock_init(&omap->lock);
goto end_disble;
omap->count--; for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
omap->platdata.port_mode[i] = pdata->port_mode[i];
omap->platdata.ehci_data = pdata->ehci_data;
omap->platdata.ohci_data = pdata->ohci_data;
if (omap->count != 0) pm_runtime_enable(dev);
goto end_disble;
/* Reset OMAP modules for insmod/rmmod to work */
usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG,
is_omap_usbhs_rev2(omap) ?
OMAP4_UHH_SYSCONFIG_SOFTRESET :
OMAP_UHH_SYSCONFIG_SOFTRESET);
timeout = jiffies + msecs_to_jiffies(100); for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
& (1 << 0))) { is_ehci_hsic_mode(i)) {
cpu_relax(); omap->ehci_logic_fck = clk_get(dev, "ehci_logic_fck");
if (IS_ERR(omap->ehci_logic_fck)) {
ret = PTR_ERR(omap->ehci_logic_fck);
dev_warn(dev, "ehci_logic_fck failed:%d\n",
ret);
}
break;
}
if (time_after(jiffies, timeout)) omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
dev_dbg(dev, "operation timed out\n"); if (IS_ERR(omap->utmi_p1_fck)) {
ret = PTR_ERR(omap->utmi_p1_fck);
dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
goto err_end;
} }
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
& (1 << 1))) { if (IS_ERR(omap->xclk60mhsp1_ck)) {
cpu_relax(); ret = PTR_ERR(omap->xclk60mhsp1_ck);
dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
goto err_utmi_p1_fck;
}
if (time_after(jiffies, timeout)) omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
dev_dbg(dev, "operation timed out\n"); if (IS_ERR(omap->utmi_p2_fck)) {
ret = PTR_ERR(omap->utmi_p2_fck);
dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
goto err_xclk60mhsp1_ck;
} }
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
& (1 << 2))) { if (IS_ERR(omap->xclk60mhsp2_ck)) {
cpu_relax(); ret = PTR_ERR(omap->xclk60mhsp2_ck);
dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
goto err_utmi_p2_fck;
}
if (time_after(jiffies, timeout)) omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
dev_dbg(dev, "operation timed out\n"); if (IS_ERR(omap->usbhost_p1_fck)) {
ret = PTR_ERR(omap->usbhost_p1_fck);
dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
goto err_xclk60mhsp2_ck;
} }
usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk");
if (IS_ERR(omap->usbtll_p1_fck)) {
ret = PTR_ERR(omap->usbtll_p1_fck);
dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret);
goto err_usbhost_p1_fck;
}
while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
& (1 << 0))) { if (IS_ERR(omap->usbhost_p2_fck)) {
cpu_relax(); ret = PTR_ERR(omap->usbhost_p2_fck);
dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
goto err_usbtll_p1_fck;
}
if (time_after(jiffies, timeout)) omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk");
dev_dbg(dev, "operation timed out\n"); if (IS_ERR(omap->usbtll_p2_fck)) {
ret = PTR_ERR(omap->usbtll_p2_fck);
dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret);
goto err_usbhost_p2_fck;
} }
if (is_omap_usbhs_rev2(omap)) { omap->init_60m_fclk = clk_get(dev, "init_60m_fclk");
if (is_ehci_tll_mode(pdata->port_mode[0])) if (IS_ERR(omap->init_60m_fclk)) {
clk_disable(omap->usbtll_p1_fck); ret = PTR_ERR(omap->init_60m_fclk);
if (is_ehci_tll_mode(pdata->port_mode[1])) dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
clk_disable(omap->usbtll_p2_fck); goto err_usbtll_p2_fck;
clk_disable(omap->utmi_p2_fck);
clk_disable(omap->utmi_p1_fck);
} }
clk_disable(omap->usbtll_ick); if (is_ehci_phy_mode(pdata->port_mode[0])) {
clk_disable(omap->usbtll_fck); /* for OMAP3 , the clk set paretn fails */
clk_disable(omap->usbhost_fs_fck); ret = clk_set_parent(omap->utmi_p1_fck,
clk_disable(omap->usbhost_hs_fck); omap->xclk60mhsp1_ck);
clk_disable(omap->usbhost_ick); if (ret != 0)
dev_err(dev, "xclk60mhsp1_ck set parent"
"failed error:%d\n", ret);
} else if (is_ehci_tll_mode(pdata->port_mode[0])) {
ret = clk_set_parent(omap->utmi_p1_fck,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
}
/* The gpio_free migh sleep; so unlock the spinlock */ if (is_ehci_phy_mode(pdata->port_mode[1])) {
spin_unlock_irqrestore(&omap->lock, flags); ret = clk_set_parent(omap->utmi_p2_fck,
omap->xclk60mhsp2_ck);
if (ret != 0)
dev_err(dev, "xclk60mhsp2_ck set parent"
"failed error:%d\n", ret);
} else if (is_ehci_tll_mode(pdata->port_mode[1])) {
ret = clk_set_parent(omap->utmi_p2_fck,
omap->init_60m_fclk);
if (ret != 0)
dev_err(dev, "init_60m_fclk set parent"
"failed error:%d\n", ret);
}
if (pdata->ehci_data->phy_reset) { res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh");
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) if (!res) {
gpio_free(pdata->ehci_data->reset_gpio_port[0]); dev_err(dev, "UHH EHCI get resource failed\n");
ret = -ENODEV;
goto err_init_60m_fclk;
}
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) omap->uhh_base = ioremap(res->start, resource_size(res));
gpio_free(pdata->ehci_data->reset_gpio_port[1]); if (!omap->uhh_base) {
dev_err(dev, "UHH ioremap failed\n");
ret = -ENOMEM;
goto err_init_60m_fclk;
} }
return;
end_disble: res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll");
spin_unlock_irqrestore(&omap->lock, flags); if (!res) {
} dev_err(dev, "UHH EHCI get resource failed\n");
ret = -ENODEV;
goto err_tll;
}
int omap_usbhs_enable(struct device *dev) omap->tll_base = ioremap(res->start, resource_size(res));
{ if (!omap->tll_base) {
return usbhs_enable(dev->parent); dev_err(dev, "TLL ioremap failed\n");
ret = -ENOMEM;
goto err_tll;
}
platform_set_drvdata(pdev, omap);
ret = omap_usbhs_alloc_children(pdev);
if (ret) {
dev_err(dev, "omap_usbhs_alloc_children failed\n");
goto err_alloc;
}
omap_usbhs_init(dev);
goto end_probe;
err_alloc:
iounmap(omap->tll_base);
err_tll:
iounmap(omap->uhh_base);
err_init_60m_fclk:
clk_put(omap->init_60m_fclk);
err_usbtll_p2_fck:
clk_put(omap->usbtll_p2_fck);
err_usbhost_p2_fck:
clk_put(omap->usbhost_p2_fck);
err_usbtll_p1_fck:
clk_put(omap->usbtll_p1_fck);
err_usbhost_p1_fck:
clk_put(omap->usbhost_p1_fck);
err_xclk60mhsp2_ck:
clk_put(omap->xclk60mhsp2_ck);
err_utmi_p2_fck:
clk_put(omap->utmi_p2_fck);
err_xclk60mhsp1_ck:
clk_put(omap->xclk60mhsp1_ck);
err_utmi_p1_fck:
clk_put(omap->utmi_p1_fck);
err_end:
clk_put(omap->ehci_logic_fck);
pm_runtime_disable(dev);
kfree(omap);
end_probe:
return ret;
} }
EXPORT_SYMBOL_GPL(omap_usbhs_enable);
void omap_usbhs_disable(struct device *dev) /**
* usbhs_omap_remove - shutdown processing for UHH & TLL HCDs
* @pdev: USB Host Controller being removed
*
* Reverses the effect of usbhs_omap_probe().
*/
static int __devexit usbhs_omap_remove(struct platform_device *pdev)
{ {
usbhs_disable(dev->parent); struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev);
omap_usbhs_deinit(&pdev->dev);
iounmap(omap->tll_base);
iounmap(omap->uhh_base);
clk_put(omap->init_60m_fclk);
clk_put(omap->usbtll_p2_fck);
clk_put(omap->usbhost_p2_fck);
clk_put(omap->usbtll_p1_fck);
clk_put(omap->usbhost_p1_fck);
clk_put(omap->xclk60mhsp2_ck);
clk_put(omap->utmi_p2_fck);
clk_put(omap->xclk60mhsp1_ck);
clk_put(omap->utmi_p1_fck);
clk_put(omap->ehci_logic_fck);
pm_runtime_disable(&pdev->dev);
kfree(omap);
return 0;
} }
EXPORT_SYMBOL_GPL(omap_usbhs_disable);
static const struct dev_pm_ops usbhsomap_dev_pm_ops = {
.runtime_suspend = usbhs_runtime_suspend,
.runtime_resume = usbhs_runtime_resume,
};
static struct platform_driver usbhs_omap_driver = { static struct platform_driver usbhs_omap_driver = {
.driver = { .driver = {
.name = (char *)usbhs_driver_name, .name = (char *)usbhs_driver_name,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &usbhsomap_dev_pm_ops,
}, },
.remove = __exit_p(usbhs_omap_remove), .remove = __exit_p(usbhs_omap_remove),
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment