diff options
Diffstat (limited to 'drivers/usb/host/ohci-da8xx.c')
-rw-r--r-- | drivers/usb/host/ohci-da8xx.c | 279 |
1 files changed, 123 insertions, 156 deletions
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c index e5c33bc98ea4..bd6cf3c9ec60 100644 --- a/drivers/usb/host/ohci-da8xx.c +++ b/drivers/usb/host/ohci-da8xx.c @@ -11,62 +11,67 @@ * kind, whether express or implied. */ +#include <linux/clk.h> +#include <linux/io.h> #include <linux/interrupt.h> #include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> #include <linux/platform_device.h> -#include <linux/clk.h> - -#include <mach/da8xx.h> +#include <linux/phy/phy.h> #include <linux/platform_data/usb-davinci.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <asm/unaligned.h> -#ifndef CONFIG_ARCH_DAVINCI_DA8XX -#error "This file is DA8xx bus glue. Define CONFIG_ARCH_DAVINCI_DA8XX." -#endif +#include "ohci.h" + +#define DRIVER_DESC "DA8XX" +#define DRV_NAME "ohci" + +static struct hc_driver __read_mostly ohci_da8xx_hc_driver; -#define CFGCHIP2 DA8XX_SYSCFG0_VIRT(DA8XX_CFGCHIP2_REG) +static int (*orig_ohci_hub_control)(struct usb_hcd *hcd, u16 typeReq, + u16 wValue, u16 wIndex, char *buf, u16 wLength); +static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf); static struct clk *usb11_clk; -static struct clk *usb20_clk; +static struct phy *usb11_phy; /* Over-current indicator change bitmask */ static volatile u16 ocic_mask; -static void ohci_da8xx_clock(int on) +static int ohci_da8xx_enable(void) { - u32 cfgchip2; - - cfgchip2 = __raw_readl(CFGCHIP2); - if (on) { - clk_enable(usb11_clk); - - /* - * If USB 1.1 reference clock is sourced from USB 2.0 PHY, we - * need to enable the USB 2.0 module clocking, start its PHY, - * and not allow it to stop the clock during USB 2.0 suspend. - */ - if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) { - clk_enable(usb20_clk); - - cfgchip2 &= ~(CFGCHIP2_RESET | CFGCHIP2_PHYPWRDN); - cfgchip2 |= CFGCHIP2_PHY_PLLON; - __raw_writel(cfgchip2, CFGCHIP2); - - pr_info("Waiting for USB PHY clock good...\n"); - while (!(__raw_readl(CFGCHIP2) & CFGCHIP2_PHYCLKGD)) - cpu_relax(); - } + int ret; + + ret = clk_prepare_enable(usb11_clk); + if (ret) + return ret; - /* Enable USB 1.1 PHY */ - cfgchip2 |= CFGCHIP2_USB1SUSPENDM; - } else { - clk_disable(usb11_clk); - if (!(cfgchip2 & CFGCHIP2_USB1PHYCLKMUX)) - clk_disable(usb20_clk); + ret = phy_init(usb11_phy); + if (ret) + goto err_phy_init; - /* Disable USB 1.1 PHY */ - cfgchip2 &= ~CFGCHIP2_USB1SUSPENDM; - } - __raw_writel(cfgchip2, CFGCHIP2); + ret = phy_power_on(usb11_phy); + if (ret) + goto err_phy_power_on; + + return 0; + +err_phy_power_on: + phy_exit(usb11_phy); +err_phy_init: + clk_disable_unprepare(usb11_clk); + + return ret; +} + +static void ohci_da8xx_disable(void) +{ + phy_power_off(usb11_phy); + phy_exit(usb11_phy); + clk_disable_unprepare(usb11_clk); } /* @@ -82,7 +87,7 @@ static void ohci_da8xx_ocic_handler(struct da8xx_ohci_root_hub *hub, hub->set_power(port, 0); } -static int ohci_da8xx_init(struct usb_hcd *hcd) +static int ohci_da8xx_reset(struct usb_hcd *hcd) { struct device *dev = hcd->self.controller; struct da8xx_ohci_root_hub *hub = dev_get_platdata(dev); @@ -92,7 +97,9 @@ static int ohci_da8xx_init(struct usb_hcd *hcd) dev_dbg(dev, "starting USB controller\n"); - ohci_da8xx_clock(1); + result = ohci_da8xx_enable(); + if (result < 0) + return result; /* * DA8xx only have 1 port connected to the pins but the HC root hub @@ -100,9 +107,11 @@ static int ohci_da8xx_init(struct usb_hcd *hcd) */ ohci->num_ports = 1; - result = ohci_init(ohci); - if (result < 0) + result = ohci_setup(hcd); + if (result < 0) { + ohci_da8xx_disable(); return result; + } /* * Since we're providing a board-specific root hub port power control @@ -126,30 +135,12 @@ static int ohci_da8xx_init(struct usb_hcd *hcd) return result; } -static void ohci_da8xx_stop(struct usb_hcd *hcd) -{ - ohci_stop(hcd); - ohci_da8xx_clock(0); -} - -static int ohci_da8xx_start(struct usb_hcd *hcd) -{ - struct ohci_hcd *ohci = hcd_to_ohci(hcd); - int result; - - result = ohci_run(ohci); - if (result < 0) - ohci_da8xx_stop(hcd); - - return result; -} - /* * Update the status data from the hub with the over-current indicator change. */ static int ohci_da8xx_hub_status_data(struct usb_hcd *hcd, char *buf) { - int length = ohci_hub_status_data(hcd, buf); + int length = orig_ohci_hub_status_data(hcd, buf); /* See if we have OCIC bit set on port 1 */ if (ocic_mask & (1 << 1)) { @@ -231,66 +222,13 @@ check_port: } } - return ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength); + return orig_ohci_hub_control(hcd, typeReq, wValue, + wIndex, buf, wLength); } -static const struct hc_driver ohci_da8xx_hc_driver = { - .description = hcd_name, - .product_desc = "DA8xx OHCI", - .hcd_priv_size = sizeof(struct ohci_hcd), - - /* - * generic hardware linkage - */ - .irq = ohci_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - /* - * basic lifecycle operations - */ - .reset = ohci_da8xx_init, - .start = ohci_da8xx_start, - .stop = ohci_da8xx_stop, - .shutdown = ohci_shutdown, - - /* - * managing i/o requests and associated device resources - */ - .urb_enqueue = ohci_urb_enqueue, - .urb_dequeue = ohci_urb_dequeue, - .endpoint_disable = ohci_endpoint_disable, - - /* - * scheduling support - */ - .get_frame_number = ohci_get_frame, - - /* - * root hub support - */ - .hub_status_data = ohci_da8xx_hub_status_data, - .hub_control = ohci_da8xx_hub_control, - -#ifdef CONFIG_PM - .bus_suspend = ohci_bus_suspend, - .bus_resume = ohci_bus_resume, -#endif - .start_port_reset = ohci_start_port_reset, -}; - /*-------------------------------------------------------------------------*/ - -/** - * usb_hcd_da8xx_probe - initialize DA8xx-based HCDs - * Context: !in_interrupt() - * - * Allocates basic resources for this USB host controller, and - * then invokes the start() method for the HCD associated with it - * through the hotplug entry's driver_data. - */ -static int usb_hcd_da8xx_probe(const struct hc_driver *driver, - struct platform_device *pdev) +static int ohci_da8xx_probe(struct platform_device *pdev) { struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); struct usb_hcd *hcd; @@ -301,14 +239,21 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, return -ENODEV; usb11_clk = devm_clk_get(&pdev->dev, "usb11"); - if (IS_ERR(usb11_clk)) + if (IS_ERR(usb11_clk)) { + if (PTR_ERR(usb11_clk) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get clock.\n"); return PTR_ERR(usb11_clk); + } - usb20_clk = devm_clk_get(&pdev->dev, "usb20"); - if (IS_ERR(usb20_clk)) - return PTR_ERR(usb20_clk); + usb11_phy = devm_phy_get(&pdev->dev, "usb-phy"); + if (IS_ERR(usb11_phy)) { + if (PTR_ERR(usb11_phy) != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get phy.\n"); + return PTR_ERR(usb11_phy); + } - hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + hcd = usb_create_hcd(&ohci_da8xx_hc_driver, &pdev->dev, + dev_name(&pdev->dev)); if (!hcd) return -ENOMEM; @@ -321,13 +266,12 @@ static int usb_hcd_da8xx_probe(const struct hc_driver *driver, hcd->rsrc_start = mem->start; hcd->rsrc_len = resource_size(mem); - ohci_hcd_init(hcd_to_ohci(hcd)); - irq = platform_get_irq(pdev, 0); if (irq < 0) { error = -ENODEV; goto err; } + error = usb_add_hcd(hcd, irq, 0); if (error) goto err; @@ -346,35 +290,14 @@ err: return error; } -/** - * usb_hcd_da8xx_remove - shutdown processing for DA8xx-based HCDs - * @dev: USB Host Controller being removed - * Context: !in_interrupt() - * - * Reverses the effect of usb_hcd_da8xx_probe(), first invoking - * the HCD's stop() method. It is always called from a thread - * context, normally "rmmod", "apmd", or something similar. - */ -static inline void -usb_hcd_da8xx_remove(struct usb_hcd *hcd, struct platform_device *pdev) +static int ohci_da8xx_remove(struct platform_device *pdev) { + struct usb_hcd *hcd = platform_get_drvdata(pdev); struct da8xx_ohci_root_hub *hub = dev_get_platdata(&pdev->dev); hub->ocic_notify(NULL); usb_remove_hcd(hcd); usb_put_hcd(hcd); -} - -static int ohci_hcd_da8xx_drv_probe(struct platform_device *dev) -{ - return usb_hcd_da8xx_probe(&ohci_da8xx_hc_driver, dev); -} - -static int ohci_hcd_da8xx_drv_remove(struct platform_device *dev) -{ - struct usb_hcd *hcd = platform_get_drvdata(dev); - - usb_hcd_da8xx_remove(hcd, dev); return 0; } @@ -397,7 +320,7 @@ static int ohci_da8xx_suspend(struct platform_device *pdev, if (ret) return ret; - ohci_da8xx_clock(0); + ohci_da8xx_disable(); hcd->state = HC_STATE_SUSPENDED; return ret; @@ -407,32 +330,76 @@ static int ohci_da8xx_resume(struct platform_device *dev) { struct usb_hcd *hcd = platform_get_drvdata(dev); struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; if (time_before(jiffies, ohci->next_statechange)) msleep(5); ohci->next_statechange = jiffies; - ohci_da8xx_clock(1); + ret = ohci_da8xx_enable(); + if (ret) + return ret; + dev->dev.power.power_state = PMSG_ON; usb_hcd_resume_root_hub(hcd); + return 0; } #endif +static const struct ohci_driver_overrides da8xx_overrides __initconst = { + .reset = ohci_da8xx_reset, +}; + /* * Driver definition to register with platform structure. */ static struct platform_driver ohci_hcd_da8xx_driver = { - .probe = ohci_hcd_da8xx_drv_probe, - .remove = ohci_hcd_da8xx_drv_remove, + .probe = ohci_da8xx_probe, + .remove = ohci_da8xx_remove, .shutdown = usb_hcd_platform_shutdown, #ifdef CONFIG_PM .suspend = ohci_da8xx_suspend, .resume = ohci_da8xx_resume, #endif .driver = { - .name = "ohci", + .name = DRV_NAME, }, }; -MODULE_ALIAS("platform:ohci"); +static int __init ohci_da8xx_init(void) +{ + + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", DRV_NAME); + ohci_init_driver(&ohci_da8xx_hc_driver, &da8xx_overrides); + + /* + * The Davinci da8xx HW has some unusual quirks, which require + * da8xx-specific workarounds. We override certain hc_driver + * functions here to achieve that. We explicitly do not enhance + * ohci_driver_overrides to allow this more easily, since this + * is an unusual case, and we don't want to encourage others to + * override these functions by making it too easy. + */ + + orig_ohci_hub_control = ohci_da8xx_hc_driver.hub_control; + orig_ohci_hub_status_data = ohci_da8xx_hc_driver.hub_status_data; + + ohci_da8xx_hc_driver.hub_status_data = ohci_da8xx_hub_status_data; + ohci_da8xx_hc_driver.hub_control = ohci_da8xx_hub_control; + + return platform_driver_register(&ohci_hcd_da8xx_driver); +} +module_init(ohci_da8xx_init); + +static void __exit ohci_da8xx_exit(void) +{ + platform_driver_unregister(&ohci_hcd_da8xx_driver); +} +module_exit(ohci_da8xx_exit); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRV_NAME); |