diff options
Diffstat (limited to 'drivers/usb/host/ehci-orion.c')
| -rw-r--r-- | drivers/usb/host/ehci-orion.c | 255 | 
1 files changed, 144 insertions, 111 deletions
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 0f87dc72820..22e15cab8ea 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -12,7 +12,18 @@  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/mbus.h> -#include <plat/ehci-orion.h> +#include <linux/clk.h> +#include <linux/platform_data/usb-ehci-orion.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/of_device.h> +#include <linux/of_irq.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> +#include <linux/io.h> +#include <linux/dma-mapping.h> + +#include "ehci.h"  #define rdl(off)	__raw_readl(hcd->regs + (off))  #define wrl(off, val)	__raw_writel((val), hcd->regs + (off)) @@ -30,6 +41,19 @@  #define USB_PHY_IVREF_CTRL	0x440  #define USB_PHY_TST_GRP_CTRL	0x450 +#define DRIVER_DESC "EHCI orion driver" + +#define hcd_to_orion_priv(h) ((struct orion_ehci_hcd *)hcd_to_ehci(h)->priv) + +struct orion_ehci_hcd { +	struct clk *clk; +	struct phy *phy; +}; + +static const char hcd_name[] = "ehci-orion"; + +static struct hc_driver __read_mostly ehci_orion_hc_driver; +  /*   * Implement Orion USB controller specification guidelines   */ @@ -100,78 +124,9 @@ static void orion_usb_phy_v1_setup(struct usb_hcd *hcd)  	wrl(USB_MODE, 0x13);  } -static int ehci_orion_setup(struct usb_hcd *hcd) -{ -	struct ehci_hcd *ehci = hcd_to_ehci(hcd); -	int retval; - -	ehci_reset(ehci); -	retval = ehci_halt(ehci); -	if (retval) -		return retval; - -	/* -	 * data structure init -	 */ -	retval = ehci_init(hcd); -	if (retval) -		return retval; - -	hcd->has_tt = 1; - -	ehci_port_power(ehci, 0); - -	return retval; -} - -static const struct hc_driver ehci_orion_hc_driver = { -	.description = hcd_name, -	.product_desc = "Marvell Orion EHCI", -	.hcd_priv_size = sizeof(struct ehci_hcd), - -	/* -	 * generic hardware linkage -	 */ -	.irq = ehci_irq, -	.flags = HCD_MEMORY | HCD_USB2, - -	/* -	 * basic lifecycle operations -	 */ -	.reset = ehci_orion_setup, -	.start = ehci_run, -	.stop = ehci_stop, -	.shutdown = ehci_shutdown, - -	/* -	 * managing i/o requests and associated device resources -	 */ -	.urb_enqueue = ehci_urb_enqueue, -	.urb_dequeue = ehci_urb_dequeue, -	.endpoint_disable = ehci_endpoint_disable, -	.endpoint_reset = ehci_endpoint_reset, - -	/* -	 * scheduling support -	 */ -	.get_frame_number = ehci_get_frame, - -	/* -	 * root hub support -	 */ -	.hub_status_data = ehci_hub_status_data, -	.hub_control = ehci_hub_control, -	.bus_suspend = ehci_bus_suspend, -	.bus_resume = ehci_bus_resume, -	.relinquish_port = ehci_relinquish_port, -	.port_handed_over = ehci_port_handed_over, - -	.clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, -}; - -static void __init +static void  ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, -				struct mbus_dram_target_info *dram) +			     const struct mbus_dram_target_info *dram)  {  	int i; @@ -181,7 +136,7 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,  	}  	for (i = 0; i < dram->num_cs; i++) { -		struct mbus_dram_window *cs = dram->cs + i; +		const struct mbus_dram_window *cs = dram->cs + i;  		wrl(USB_WINDOW_CTRL(i), ((cs->size - 1) & 0xffff0000) |  					(cs->mbus_attr << 8) | @@ -190,14 +145,21 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd,  	}  } -static int __devinit ehci_orion_drv_probe(struct platform_device *pdev) +static const struct ehci_driver_overrides orion_overrides __initconst = { +	.extra_priv_size =	sizeof(struct orion_ehci_hcd), +}; + +static int ehci_orion_drv_probe(struct platform_device *pdev)  { -	struct orion_ehci_data *pd = pdev->dev.platform_data; +	struct orion_ehci_data *pd = dev_get_platdata(&pdev->dev); +	const struct mbus_dram_target_info *dram;  	struct resource *res;  	struct usb_hcd *hcd;  	struct ehci_hcd *ehci;  	void __iomem *regs;  	int irq, err; +	enum orion_ehci_phy_ver phy_version; +	struct orion_ehci_hcd *priv;  	if (usb_disabled())  		return -ENODEV; @@ -210,7 +172,7 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)  			"Found HC with no IRQ. Check %s setup!\n",  			dev_name(&pdev->dev));  		err = -ENODEV; -		goto err1; +		goto err;  	}  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -219,28 +181,29 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)  			"Found HC with no register addr. Check %s setup!\n",  			dev_name(&pdev->dev));  		err = -ENODEV; -		goto err1; +		goto err;  	} -	if (!request_mem_region(res->start, resource_size(res), -				ehci_orion_hc_driver.description)) { -		dev_dbg(&pdev->dev, "controller already in use\n"); -		err = -EBUSY; -		goto err1; -	} +	/* +	 * Right now device-tree probed devices don't get dma_mask +	 * set. Since shared usb code relies on it, set it here for +	 * now. Once we have dma capability bindings this can go away. +	 */ +	err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); +	if (err) +		goto err; -	regs = ioremap(res->start, resource_size(res)); -	if (regs == NULL) { -		dev_dbg(&pdev->dev, "error mapping memory\n"); -		err = -EFAULT; -		goto err2; +	regs = devm_ioremap_resource(&pdev->dev, res); +	if (IS_ERR(regs)) { +		err = PTR_ERR(regs); +		goto err;  	}  	hcd = usb_create_hcd(&ehci_orion_hc_driver,  			&pdev->dev, dev_name(&pdev->dev));  	if (!hcd) {  		err = -ENOMEM; -		goto err3; +		goto err;  	}  	hcd->rsrc_start = res->start; @@ -249,22 +212,47 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)  	ehci = hcd_to_ehci(hcd);  	ehci->caps = hcd->regs + 0x100; -	ehci->regs = hcd->regs + 0x100 + -		HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); -	ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);  	hcd->has_tt = 1; -	ehci->sbrn = 0x20; + +	priv = hcd_to_orion_priv(hcd); +	/* +	 * Not all platforms can gate the clock, so it is not an error if +	 * the clock does not exists. +	 */ +	priv->clk = devm_clk_get(&pdev->dev, NULL); +	if (!IS_ERR(priv->clk)) +		clk_prepare_enable(priv->clk); + +	priv->phy = devm_phy_optional_get(&pdev->dev, "usb"); +	if (IS_ERR(priv->phy)) { +		err = PTR_ERR(priv->phy); +		goto err_phy_get; +	} else { +		err = phy_init(priv->phy); +		if (err) +			goto err_phy_init; + +		err = phy_power_on(priv->phy); +		if (err) +			goto err_phy_power_on; +	}  	/*  	 * (Re-)program MBUS remapping windows if we are asked to.  	 */ -	if (pd != NULL && pd->dram != NULL) -		ehci_orion_conf_mbus_windows(hcd, pd->dram); +	dram = mv_mbus_dram_info(); +	if (dram) +		ehci_orion_conf_mbus_windows(hcd, dram);  	/*  	 * setup Orion USB controller.  	 */ -	switch (pd->phy_version) { +	if (pdev->dev.of_node) +		phy_version = EHCI_PHY_NA; +	else +		phy_version = pd->phy_version; + +	switch (phy_version) {  	case EHCI_PHY_NA:	/* dont change USB phy settings */  		break;  	case EHCI_PHY_ORION: @@ -273,45 +261,90 @@ static int __devinit ehci_orion_drv_probe(struct platform_device *pdev)  	case EHCI_PHY_DD:  	case EHCI_PHY_KW:  	default: -		printk(KERN_WARNING "Orion ehci -USB phy version isn't supported.\n"); +		dev_warn(&pdev->dev, "USB phy version isn't supported.\n");  	} -	err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); +	err = usb_add_hcd(hcd, irq, IRQF_SHARED);  	if (err) -		goto err4; +		goto err_add_hcd; +	device_wakeup_enable(hcd->self.controller);  	return 0; -err4: +err_add_hcd: +	if (!IS_ERR(priv->phy)) +		phy_power_off(priv->phy); +err_phy_power_on: +	if (!IS_ERR(priv->phy)) +		phy_exit(priv->phy); +err_phy_init: +err_phy_get: +	if (!IS_ERR(priv->clk)) +		clk_disable_unprepare(priv->clk);  	usb_put_hcd(hcd); -err3: -	iounmap(regs); -err2: -	release_mem_region(res->start, resource_size(res)); -err1: +err:  	dev_err(&pdev->dev, "init %s fail, %d\n",  		dev_name(&pdev->dev), err);  	return err;  } -static int __exit ehci_orion_drv_remove(struct platform_device *pdev) +static int ehci_orion_drv_remove(struct platform_device *pdev)  {  	struct usb_hcd *hcd = platform_get_drvdata(pdev); +	struct orion_ehci_hcd *priv = hcd_to_orion_priv(hcd);  	usb_remove_hcd(hcd); -	iounmap(hcd->regs); -	release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + +	if (!IS_ERR(priv->phy)) { +		phy_power_off(priv->phy); +		phy_exit(priv->phy); +	} + +	if (!IS_ERR(priv->clk)) +		clk_disable_unprepare(priv->clk); +  	usb_put_hcd(hcd);  	return 0;  } -MODULE_ALIAS("platform:orion-ehci"); +static const struct of_device_id ehci_orion_dt_ids[] = { +	{ .compatible = "marvell,orion-ehci", }, +	{}, +}; +MODULE_DEVICE_TABLE(of, ehci_orion_dt_ids);  static struct platform_driver ehci_orion_driver = {  	.probe		= ehci_orion_drv_probe, -	.remove		= __exit_p(ehci_orion_drv_remove), +	.remove		= ehci_orion_drv_remove,  	.shutdown	= usb_hcd_platform_shutdown, -	.driver.name	= "orion-ehci", +	.driver = { +		.name	= "orion-ehci", +		.owner  = THIS_MODULE, +		.of_match_table = ehci_orion_dt_ids, +	},  }; + +static int __init ehci_orion_init(void) +{ +	if (usb_disabled()) +		return -ENODEV; + +	pr_info("%s: " DRIVER_DESC "\n", hcd_name); + +	ehci_init_driver(&ehci_orion_hc_driver, &orion_overrides); +	return platform_driver_register(&ehci_orion_driver); +} +module_init(ehci_orion_init); + +static void __exit ehci_orion_cleanup(void) +{ +	platform_driver_unregister(&ehci_orion_driver); +} +module_exit(ehci_orion_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:orion-ehci"); +MODULE_AUTHOR("Tzachi Perelstein"); +MODULE_LICENSE("GPL v2");  | 
