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: Keshava Munegowda <keshava_mgowda@ti.com> Reviewed-by: Kevin Hilman <khilman@ti.com> Reviewed-by: Partha Basak <parthab@india.ti.com> Acked-by: Felipe Balbi <balbi@ti.com> Acked-by: Samuel Ortiz <sameo@linux.intel.com> Signed-off-by: Paul Walmsley <paul@pwsan.com>
This commit is contained in:
parent
6c984b066d
commit
1e7fe1a925
1 changed files with 328 additions and 475 deletions
|
@ -27,6 +27,7 @@
|
|||
#include <linux/spinlock.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <plat/usb.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#define USBHS_DRIVER_NAME "usbhs_omap"
|
||||
#define OMAP_EHCI_DEVICE "ehci-omap"
|
||||
|
@ -147,9 +148,6 @@
|
|||
|
||||
|
||||
struct usbhs_hcd_omap {
|
||||
struct clk *usbhost_ick;
|
||||
struct clk *usbhost_hs_fck;
|
||||
struct clk *usbhost_fs_fck;
|
||||
struct clk *xclk60mhsp1_ck;
|
||||
struct clk *xclk60mhsp2_ck;
|
||||
struct clk *utmi_p1_fck;
|
||||
|
@ -159,8 +157,7 @@ struct usbhs_hcd_omap {
|
|||
struct clk *usbhost_p2_fck;
|
||||
struct clk *usbtll_p2_fck;
|
||||
struct clk *init_60m_fclk;
|
||||
struct clk *usbtll_fck;
|
||||
struct clk *usbtll_ick;
|
||||
struct clk *ehci_logic_fck;
|
||||
|
||||
void __iomem *uhh_base;
|
||||
void __iomem *tll_base;
|
||||
|
@ -169,7 +166,6 @@ struct usbhs_hcd_omap {
|
|||
|
||||
u32 usbhs_rev;
|
||||
spinlock_t lock;
|
||||
int count;
|
||||
};
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
|
@ -319,269 +315,6 @@ static int omap_usbhs_alloc_children(struct platform_device *pdev)
|
|||
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)
|
||||
{
|
||||
switch (pmode) {
|
||||
|
@ -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_omap_platform_data *pdata = &omap->platdata;
|
||||
unsigned long flags = 0;
|
||||
int ret = 0;
|
||||
unsigned long timeout;
|
||||
unsigned reg;
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(dev, "usbhs_runtime_resume\n");
|
||||
|
||||
dev_dbg(dev, "starting TI HSUSB Controller\n");
|
||||
if (!pdata) {
|
||||
dev_dbg(dev, "missing platform_data\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&omap->lock, flags);
|
||||
if (omap->count > 0)
|
||||
goto end_count;
|
||||
|
||||
clk_enable(omap->usbhost_ick);
|
||||
clk_enable(omap->usbhost_hs_fck);
|
||||
clk_enable(omap->usbhost_fs_fck);
|
||||
clk_enable(omap->usbtll_fck);
|
||||
clk_enable(omap->usbtll_ick);
|
||||
if (omap->ehci_logic_fck && !IS_ERR(omap->ehci_logic_fck))
|
||||
clk_enable(omap->ehci_logic_fck);
|
||||
|
||||
if (is_ehci_tll_mode(pdata->port_mode[0])) {
|
||||
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 (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) {
|
||||
|
@ -736,50 +524,6 @@ static int usbhs_enable(struct device *dev)
|
|||
omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION);
|
||||
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);
|
||||
/* setup ULPI bypass and burst configurations */
|
||||
reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
|
||||
|
@ -825,49 +569,6 @@ static int usbhs_enable(struct device *dev)
|
|||
reg &= ~OMAP4_P1_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]) ||
|
||||
(is_ohci_port(pdata->port_mode[0])))
|
||||
reg |= OMAP4_P1_MODE_TLL;
|
||||
|
@ -913,106 +614,14 @@ static int usbhs_enable(struct device *dev)
|
|||
(pdata->ehci_data->reset_gpio_port[1], 1);
|
||||
}
|
||||
|
||||
end_count:
|
||||
omap->count++;
|
||||
spin_unlock_irqrestore(&omap->lock, flags);
|
||||
return 0;
|
||||
|
||||
err_tll:
|
||||
if (pdata->ehci_data->phy_reset) {
|
||||
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
|
||||
gpio_free(pdata->ehci_data->reset_gpio_port[0]);
|
||||
|
||||
if (gpio_is_valid(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;
|
||||
pm_runtime_put_sync(dev);
|
||||
}
|
||||
|
||||
static void usbhs_disable(struct device *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;
|
||||
unsigned long flags = 0;
|
||||
unsigned long timeout;
|
||||
|
||||
dev_dbg(dev, "stopping TI HSUSB Controller\n");
|
||||
|
||||
spin_lock_irqsave(&omap->lock, flags);
|
||||
|
||||
if (omap->count == 0)
|
||||
goto end_disble;
|
||||
|
||||
omap->count--;
|
||||
|
||||
if (omap->count != 0)
|
||||
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);
|
||||
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& (1 << 0))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& (1 << 1))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS)
|
||||
& (1 << 2))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1));
|
||||
|
||||
while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS)
|
||||
& (1 << 0))) {
|
||||
cpu_relax();
|
||||
|
||||
if (time_after(jiffies, timeout))
|
||||
dev_dbg(dev, "operation timed out\n");
|
||||
}
|
||||
|
||||
if (is_omap_usbhs_rev2(omap)) {
|
||||
if (is_ehci_tll_mode(pdata->port_mode[0]))
|
||||
clk_disable(omap->usbtll_p1_fck);
|
||||
if (is_ehci_tll_mode(pdata->port_mode[1]))
|
||||
clk_disable(omap->usbtll_p2_fck);
|
||||
clk_disable(omap->utmi_p2_fck);
|
||||
clk_disable(omap->utmi_p1_fck);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* The gpio_free migh sleep; so unlock the spinlock */
|
||||
spin_unlock_irqrestore(&omap->lock, flags);
|
||||
|
||||
if (pdata->ehci_data->phy_reset) {
|
||||
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0]))
|
||||
|
@ -1021,28 +630,272 @@ static void usbhs_disable(struct device *dev)
|
|||
if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1]))
|
||||
gpio_free(pdata->ehci_data->reset_gpio_port[1]);
|
||||
}
|
||||
return;
|
||||
|
||||
end_disble:
|
||||
spin_unlock_irqrestore(&omap->lock, flags);
|
||||
}
|
||||
|
||||
int omap_usbhs_enable(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)
|
||||
{
|
||||
return usbhs_enable(dev->parent);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(omap_usbhs_enable);
|
||||
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;
|
||||
|
||||
void omap_usbhs_disable(struct device *dev)
|
||||
{
|
||||
usbhs_disable(dev->parent);
|
||||
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;
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
|
||||
for (i = 0; i < OMAP3_HS_USB_PORTS; i++)
|
||||
if (is_ehci_phy_mode(i) || is_ehci_tll_mode(i) ||
|
||||
is_ehci_hsic_mode(i)) {
|
||||
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;
|
||||
}
|
||||
|
||||
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_end;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (is_ehci_phy_mode(pdata->port_mode[0])) {
|
||||
/* for OMAP3 , the clk set paretn fails */
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
|
||||
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, "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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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_disable);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 = {
|
||||
.driver = {
|
||||
.name = (char *)usbhs_driver_name,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &usbhsomap_dev_pm_ops,
|
||||
},
|
||||
.remove = __exit_p(usbhs_omap_remove),
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue