diff options
Diffstat (limited to 'drivers/usb/host/ehci-orion.c')
| -rw-r--r-- | drivers/usb/host/ehci-orion.c | 211 |
1 files changed, 109 insertions, 102 deletions
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index 914a3ecfb5d..22e15cab8ea 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -15,8 +15,15 @@ #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)) @@ -34,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 */ @@ -104,51 +124,6 @@ static void orion_usb_phy_v1_setup(struct usb_hcd *hcd) wrl(USB_MODE, 0x13); } -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_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 ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, const struct mbus_dram_target_info *dram) @@ -170,35 +145,34 @@ ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, } } -static u64 ehci_orion_dma_mask = DMA_BIT_MASK(32); +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; - struct clk *clk; void __iomem *regs; int irq, err; enum orion_ehci_phy_ver phy_version; + struct orion_ehci_hcd *priv; if (usb_disabled()) return -ENODEV; pr_debug("Initializing Orion-SoC USB Host Controller\n"); - if (pdev->dev.of_node) - irq = irq_of_parse_and_map(pdev->dev.of_node, 0); - else - irq = platform_get_irq(pdev, 0); + irq = platform_get_irq(pdev, 0); if (irq <= 0) { dev_err(&pdev->dev, "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); @@ -207,7 +181,7 @@ static int 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; } /* @@ -215,36 +189,21 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) * set. 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 = &ehci_orion_dma_mask; - - 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; - } - - regs = ioremap(res->start, resource_size(res)); - if (regs == NULL) { - dev_dbg(&pdev->dev, "error mapping memory\n"); - err = -EFAULT; - goto err2; - } + err = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + goto err; - /* Not all platforms can gate the clock, so it is not - an error if the clock does not exists. */ - clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(clk)) { - clk_prepare_enable(clk); - clk_put(clk); + 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; @@ -255,6 +214,29 @@ static int ehci_orion_drv_probe(struct platform_device *pdev) ehci->caps = hcd->regs + 0x100; hcd->has_tt = 1; + 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. */ @@ -279,52 +261,54 @@ static int 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); 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: - if (!IS_ERR(clk)) { - clk_disable_unprepare(clk); - clk_put(clk); - } - 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 clk *clk; + 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); - usb_put_hcd(hcd); - clk = clk_get(&pdev->dev, NULL); - if (!IS_ERR(clk)) { - clk_disable_unprepare(clk); - clk_put(clk); + 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", }, {}, @@ -333,11 +317,34 @@ 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", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(ehci_orion_dt_ids), + .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"); |
