diff options
Diffstat (limited to 'drivers/usb/host/ohci-at91.c')
| -rw-r--r-- | drivers/usb/host/ohci-at91.c | 262 | 
1 files changed, 110 insertions, 152 deletions
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index caa3764a340..e49eb4f90f5 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -13,19 +13,24 @@   */  #include <linux/clk.h> -#include <linux/platform_device.h> +#include <linux/dma-mapping.h>  #include <linux/of_platform.h>  #include <linux/of_gpio.h> +#include <linux/platform_device.h>  #include <linux/platform_data/atmel.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h>  #include <mach/hardware.h>  #include <asm/gpio.h>  #include <mach/cpu.h> -#ifndef CONFIG_ARCH_AT91 -#error "CONFIG_ARCH_AT91 must be defined." -#endif + +#include "ohci.h"  #define valid_port(index)	((index) >= 0 && (index) < AT91_MAX_USBH_PORTS)  #define at91_for_each_port(index)	\ @@ -33,6 +38,13 @@  /* interface, function and usb clocks; sometimes also an AHB clock */  static struct clk *iclk, *fclk, *uclk, *hclk; +/* interface and function clocks; sometimes also an AHB clock */ + +#define DRIVER_DESC "OHCI Atmel driver" + +static const char hcd_name[] = "ohci-atmel"; + +static struct hc_driver __read_mostly ohci_at91_hc_driver;  static int clocked;  extern int usb_disabled(void); @@ -117,92 +129,80 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);  static int usb_hcd_at91_probe(const struct hc_driver *driver,  			struct platform_device *pdev)  { +	struct at91_usbh_data *board; +	struct ohci_hcd *ohci;  	int retval;  	struct usb_hcd *hcd = NULL; - -	if (pdev->num_resources != 2) { -		pr_debug("hcd probe: invalid num_resources"); -		return -ENODEV; +	struct device *dev = &pdev->dev; +	struct resource *res; +	int irq; + +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (!res) { +		dev_dbg(dev, "hcd probe: missing memory resource\n"); +		return -ENXIO;  	} -	if ((pdev->resource[0].flags != IORESOURCE_MEM) -			|| (pdev->resource[1].flags != IORESOURCE_IRQ)) { -		pr_debug("hcd probe: invalid resource type\n"); -		return -ENODEV; +	irq = platform_get_irq(pdev, 0); +	if (irq < 0) { +		dev_dbg(dev, "hcd probe: missing irq resource\n"); +		return irq;  	} -	hcd = usb_create_hcd(driver, &pdev->dev, "at91"); +	hcd = usb_create_hcd(driver, dev, "at91");  	if (!hcd)  		return -ENOMEM; -	hcd->rsrc_start = pdev->resource[0].start; -	hcd->rsrc_len = resource_size(&pdev->resource[0]); - -	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { -		pr_debug("request_mem_region failed\n"); -		retval = -EBUSY; -		goto err1; -	} +	hcd->rsrc_start = res->start; +	hcd->rsrc_len = resource_size(res); -	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); -	if (!hcd->regs) { -		pr_debug("ioremap failed\n"); -		retval = -EIO; -		goto err2; +	hcd->regs = devm_ioremap_resource(dev, res); +	if (IS_ERR(hcd->regs)) { +		retval = PTR_ERR(hcd->regs); +		goto err;  	} -	iclk = clk_get(&pdev->dev, "ohci_clk"); +	iclk = devm_clk_get(dev, "ohci_clk");  	if (IS_ERR(iclk)) { -		dev_err(&pdev->dev, "failed to get ohci_clk\n"); +		dev_err(dev, "failed to get ohci_clk\n");  		retval = PTR_ERR(iclk); -		goto err3; +		goto err;  	} -	fclk = clk_get(&pdev->dev, "uhpck"); +	fclk = devm_clk_get(dev, "uhpck");  	if (IS_ERR(fclk)) { -		dev_err(&pdev->dev, "failed to get uhpck\n"); +		dev_err(dev, "failed to get uhpck\n");  		retval = PTR_ERR(fclk); -		goto err4; +		goto err;  	} -	hclk = clk_get(&pdev->dev, "hclk"); +	hclk = devm_clk_get(dev, "hclk");  	if (IS_ERR(hclk)) { -		dev_err(&pdev->dev, "failed to get hclk\n"); +		dev_err(dev, "failed to get hclk\n");  		retval = PTR_ERR(hclk); -		goto err5; +		goto err;  	}  	if (IS_ENABLED(CONFIG_COMMON_CLK)) { -		uclk = clk_get(&pdev->dev, "usb_clk"); +		uclk = devm_clk_get(dev, "usb_clk");  		if (IS_ERR(uclk)) { -			dev_err(&pdev->dev, "failed to get uclk\n"); +			dev_err(dev, "failed to get uclk\n");  			retval = PTR_ERR(uclk); -			goto err6; +			goto err;  		}  	} +	board = hcd->self.controller->platform_data; +	ohci = hcd_to_ohci(hcd); +	ohci->num_ports = board->ports;  	at91_start_hc(pdev); -	ohci_hcd_init(hcd_to_ohci(hcd)); -	retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED); -	if (retval == 0) +	retval = usb_add_hcd(hcd, irq, IRQF_SHARED); +	if (retval == 0) { +		device_wakeup_enable(hcd->self.controller);  		return retval; +	}  	/* Error handling */  	at91_stop_hc(pdev); -	if (IS_ENABLED(CONFIG_COMMON_CLK)) -		clk_put(uclk); - err6: -	clk_put(hclk); - err5: -	clk_put(fclk); - err4: -	clk_put(iclk); - - err3: -	iounmap(hcd->regs); - - err2: -	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - - err1: + err:  	usb_put_hcd(hcd);  	return retval;  } @@ -225,49 +225,10 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,  {  	usb_remove_hcd(hcd);  	at91_stop_hc(pdev); -	iounmap(hcd->regs); -	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);  	usb_put_hcd(hcd); - -	if (IS_ENABLED(CONFIG_COMMON_CLK)) -		clk_put(uclk); -	clk_put(hclk); -	clk_put(fclk); -	clk_put(iclk); -	fclk = iclk = hclk = NULL;  }  /*-------------------------------------------------------------------------*/ - -static int -ohci_at91_reset (struct usb_hcd *hcd) -{ -	struct at91_usbh_data	*board = dev_get_platdata(hcd->self.controller); -	struct ohci_hcd		*ohci = hcd_to_ohci (hcd); -	int			ret; - -	if ((ret = ohci_init(ohci)) < 0) -		return ret; - -	ohci->num_ports = board->ports; -	return 0; -} - -static int -ohci_at91_start (struct usb_hcd *hcd) -{ -	struct ohci_hcd		*ohci = hcd_to_ohci (hcd); -	int			ret; - -	if ((ret = ohci_run(ohci)) < 0) { -		dev_err(hcd->self.controller, "can't start %s\n", -			hcd->self.bus_name); -		ohci_stop(hcd); -		return ret; -	} -	return 0; -} -  static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)  {  	if (!valid_port(port)) @@ -297,7 +258,7 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)   */  static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)  { -	struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller); +	struct at91_usbh_data *pdata = hcd->self.controller->platform_data;  	int length = ohci_hub_status_data(hcd, buf);  	int port; @@ -430,51 +391,6 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,  /*-------------------------------------------------------------------------*/ -static const struct hc_driver ohci_at91_hc_driver = { -	.description =		hcd_name, -	.product_desc =		"AT91 OHCI", -	.hcd_priv_size =	sizeof(struct ohci_hcd), - -	/* -	 * generic hardware linkage -	 */ -	.irq =			ohci_irq, -	.flags =		HCD_USB11 | HCD_MEMORY, - -	/* -	 * basic lifecycle operations -	 */ -	.reset =		ohci_at91_reset, -	.start =		ohci_at91_start, -	.stop =			ohci_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_at91_hub_status_data, -	.hub_control =		ohci_at91_hub_control, -#ifdef CONFIG_PM -	.bus_suspend =		ohci_bus_suspend, -	.bus_resume =		ohci_bus_resume, -#endif -	.start_port_reset =	ohci_start_port_reset, -}; - -/*-------------------------------------------------------------------------*/ -  static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)  {  	struct platform_device *pdev = data; @@ -524,7 +440,7 @@ MODULE_DEVICE_TABLE(of, at91_ohci_dt_ids);  static int ohci_at91_of_init(struct platform_device *pdev)  {  	struct device_node *np = pdev->dev.of_node; -	int i, gpio; +	int i, gpio, ret;  	enum of_gpio_flags flags;  	struct at91_usbh_data	*pdata;  	u32 ports; @@ -536,10 +452,9 @@ static int ohci_at91_of_init(struct platform_device *pdev)  	 * Since shared usb code relies on it, set it here for now.  	 * Once we have dma capability bindings this can go away.  	 */ -	if (!pdev->dev.dma_mask) -		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask; -	if (!pdev->dev.coherent_dma_mask) -		pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); +	ret = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); +	if (ret) +		return ret;  	pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);  	if (!pdata) @@ -691,10 +606,17 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)  {  	struct usb_hcd	*hcd = platform_get_drvdata(pdev);  	struct ohci_hcd	*ohci = hcd_to_ohci(hcd); +	bool		do_wakeup = device_may_wakeup(&pdev->dev); +	int		ret; -	if (device_may_wakeup(&pdev->dev)) +	if (do_wakeup)  		enable_irq_wake(hcd->irq); +	ret = ohci_suspend(hcd, do_wakeup); +	if (ret) { +		disable_irq_wake(hcd->irq); +		return ret; +	}  	/*  	 * The integrated transceivers seem unable to notice disconnect,  	 * reconnect, or wakeup without the 48 MHz clock active.  so for @@ -703,13 +625,17 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)  	 * REVISIT: some boards will be able to turn VBUS off...  	 */  	if (at91_suspend_entering_slow_clock()) { -		ohci_usb_reset (ohci); +		ohci->hc_control = ohci_readl(ohci, &ohci->regs->control); +		ohci->hc_control &= OHCI_CTRL_RWC; +		ohci_writel(ohci, ohci->hc_control, &ohci->regs->control); +		ohci->rh_state = OHCI_RH_HALTED; +  		/* flush the writes */  		(void) ohci_readl (ohci, &ohci->regs->control);  		at91_stop_clock();  	} -	return 0; +	return ret;  }  static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) @@ -730,8 +656,6 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)  #define ohci_hcd_at91_drv_resume  NULL  #endif -MODULE_ALIAS("platform:at91_ohci"); -  static struct platform_driver ohci_hcd_at91_driver = {  	.probe		= ohci_hcd_at91_drv_probe,  	.remove		= ohci_hcd_at91_drv_remove, @@ -744,3 +668,37 @@ static struct platform_driver ohci_hcd_at91_driver = {  		.of_match_table	= of_match_ptr(at91_ohci_dt_ids),  	},  }; + +static int __init ohci_at91_init(void) +{ +	if (usb_disabled()) +		return -ENODEV; + +	pr_info("%s: " DRIVER_DESC "\n", hcd_name); +	ohci_init_driver(&ohci_at91_hc_driver, NULL); + +	/* +	 * The Atmel HW has some unusual quirks, which require Atmel-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. +	 */ + +	ohci_at91_hc_driver.hub_status_data	= ohci_at91_hub_status_data; +	ohci_at91_hc_driver.hub_control		= ohci_at91_hub_control; + +	return platform_driver_register(&ohci_hcd_at91_driver); +} +module_init(ohci_at91_init); + +static void __exit ohci_at91_cleanup(void) +{ +	platform_driver_unregister(&ohci_hcd_at91_driver); +} +module_exit(ohci_at91_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:at91_ohci");  | 
