diff options
Diffstat (limited to 'drivers/usb/host/ehci-orion.c')
| -rw-r--r-- | drivers/usb/host/ehci-orion.c | 284 |
1 files changed, 181 insertions, 103 deletions
diff --git a/drivers/usb/host/ehci-orion.c b/drivers/usb/host/ehci-orion.c index e129981f139..22e15cab8ea 100644 --- a/drivers/usb/host/ehci-orion.c +++ b/drivers/usb/host/ehci-orion.c @@ -11,15 +11,29 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/platform_device.h> -#include <asm/arch/orion.h> +#include <linux/mbus.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)) -#define USB_CAUSE 0x310 -#define USB_MASK 0x314 #define USB_CMD 0x140 #define USB_MODE 0x1a8 +#define USB_CAUSE 0x310 +#define USB_MASK 0x314 +#define USB_WINDOW_CTRL(i) (0x320 + ((i) << 4)) +#define USB_WINDOW_BASE(i) (0x324 + ((i) << 4)) #define USB_IPG 0x360 #define USB_PHY_PWR_CTRL 0x400 #define USB_PHY_TX_CTRL 0x420 @@ -27,11 +41,25 @@ #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 */ -static void orion_usb_setup(struct usb_hcd *hcd) +static void orion_usb_phy_v1_setup(struct usb_hcd *hcd) { + /* The below GLs are according to the Orion Errata document */ /* * Clear interrupt cause and mask */ @@ -96,79 +124,42 @@ static void orion_usb_setup(struct usb_hcd *hcd) wrl(USB_MODE, 0x13); } -static int ehci_orion_setup(struct usb_hcd *hcd) +static void +ehci_orion_conf_mbus_windows(struct usb_hcd *hcd, + const struct mbus_dram_target_info *dram) { - struct ehci_hcd *ehci = hcd_to_ehci(hcd); - int retval; + int i; - retval = ehci_halt(ehci); - if (retval) - return retval; - - /* - * data structure init - */ - retval = ehci_init(hcd); - if (retval) - return retval; + for (i = 0; i < 4; i++) { + wrl(USB_WINDOW_CTRL(i), 0); + wrl(USB_WINDOW_BASE(i), 0); + } - ehci_reset(ehci); - ehci_port_power(ehci, 0); + for (i = 0; i < dram->num_cs; i++) { + const struct mbus_dram_window *cs = dram->cs + i; - return retval; + wrl(USB_WINDOW_CTRL(i), ((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1); + wrl(USB_WINDOW_BASE(i), cs->base); + } } -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, -#ifdef CONFIG_PM - .suspend = ehci_bus_suspend, - .resume = ehci_bus_resume, -#endif - .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, - - /* - * 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, +static const struct ehci_driver_overrides orion_overrides __initconst = { + .extra_priv_size = sizeof(struct orion_ehci_hcd), }; -static int __init ehci_orion_drv_probe(struct platform_device *pdev) +static int ehci_orion_drv_probe(struct platform_device *pdev) { + 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; @@ -179,94 +170,181 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev) if (irq <= 0) { dev_err(&pdev->dev, "Found HC with no IRQ. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); err = -ENODEV; - goto err1; + goto err; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "Found HC with no register addr. Check %s setup!\n", - pdev->dev.bus_id); + dev_name(&pdev->dev)); err = -ENODEV; - goto err1; + goto err; } - if (!request_mem_region(res->start, res->end - res->start + 1, - 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, res->end - res->start + 1); - 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, pdev->dev.bus_id); + &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { err = -ENOMEM; - goto err3; + goto err; } hcd->rsrc_start = res->start; - hcd->rsrc_len = res->end - res->start + 1; + hcd->rsrc_len = resource_size(res); hcd->regs = regs; 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); - ehci->is_tdi_rh_tt = 1; - ehci->sbrn = 0x20; + 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; + } /* - * setup Orion USB controller + * (Re-)program MBUS remapping windows if we are asked to. */ - orion_usb_setup(hcd); + dram = mv_mbus_dram_info(); + if (dram) + ehci_orion_conf_mbus_windows(hcd, dram); - err = usb_add_hcd(hcd, irq, IRQF_SHARED | IRQF_DISABLED); + /* + * setup Orion USB controller. + */ + 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: + orion_usb_phy_v1_setup(hcd); + break; + case EHCI_PHY_DD: + case EHCI_PHY_KW: + default: + 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: - iounmap(regs); -err2: - release_mem_region(res->start, res->end - res->start + 1); -err1: +err: dev_err(&pdev->dev, "init %s fail, %d\n", - pdev->dev.bus_id, err); + 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"); |
