diff options
Diffstat (limited to 'drivers/usb/host/ehci-npcm7xx.c')
-rw-r--r-- | drivers/usb/host/ehci-npcm7xx.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/drivers/usb/host/ehci-npcm7xx.c b/drivers/usb/host/ehci-npcm7xx.c new file mode 100644 index 000000000..6b5a7a873 --- /dev/null +++ b/drivers/usb/host/ehci-npcm7xx.c @@ -0,0 +1,210 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Nuvoton NPCM7xx driver for EHCI HCD + * + * Copyright (C) 2018 Nuvoton Technologies, + * Avi Fishman <avi.fishman@nuvoton.com> <avifishman70@gmail.com> + * Tomer Maimon <tomer.maimon@nuvoton.com> <tmaimon77@gmail.com> + * + * Based on various ehci-spear.c driver + */ + + +#include <linux/dma-mapping.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/usb.h> +#include <linux/usb/hcd.h> + +#include "ehci.h" + +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#define DRIVER_DESC "EHCI npcm7xx driver" + +static const char hcd_name[] = "npcm7xx-ehci"; + +#define USB2PHYCTL_OFFSET 0x144 + +#define IPSRST2_OFFSET 0x24 +#define IPSRST3_OFFSET 0x34 + + +static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver; + +static int __maybe_unused ehci_npcm7xx_drv_suspend(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + bool do_wakeup = device_may_wakeup(dev); + + return ehci_suspend(hcd, do_wakeup); +} + +static int __maybe_unused ehci_npcm7xx_drv_resume(struct device *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(dev); + + ehci_resume(hcd, false); + return 0; +} + +static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend, + ehci_npcm7xx_drv_resume); + +static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) +{ + struct usb_hcd *hcd; + struct resource *res; + struct regmap *gcr_regmap; + struct regmap *rst_regmap; + const struct hc_driver *driver = &ehci_npcm7xx_hc_driver; + int irq; + int retval; + + dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n"); + + gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr"); + if (IS_ERR(gcr_regmap)) { + dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-gcr\n", + __func__); + return PTR_ERR(gcr_regmap); + } + + rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst"); + if (IS_ERR(rst_regmap)) { + dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n", + __func__); + return PTR_ERR(rst_regmap); + } + + /********* phy init ******/ + // reset usb host + regmap_update_bits(rst_regmap, IPSRST2_OFFSET, + (0x1 << 26), (0x1 << 26)); + regmap_update_bits(rst_regmap, IPSRST3_OFFSET, + (0x1 << 25), (0x1 << 25)); + regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET, + (0x1 << 28), 0); + + udelay(1); + + // enable phy + regmap_update_bits(rst_regmap, IPSRST3_OFFSET, + (0x1 << 25), 0); + + udelay(50); // enable phy + + regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET, + (0x1 << 28), (0x1 << 28)); + + // enable host + regmap_update_bits(rst_regmap, IPSRST2_OFFSET, + (0x1 << 26), 0); + + if (usb_disabled()) + return -ENODEV; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = irq; + goto fail; + } + + /* + * 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. + */ + retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (retval) + goto fail; + + hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto fail; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hcd->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(hcd->regs)) { + retval = PTR_ERR(hcd->regs); + goto err_put_hcd; + } + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_size(res); + + /* registers start at offset 0x0 */ + hcd_to_ehci(hcd)->caps = hcd->regs; + + retval = usb_add_hcd(hcd, irq, IRQF_SHARED); + if (retval) + goto err_put_hcd; + + device_wakeup_enable(hcd->self.controller); + return retval; + +err_put_hcd: + usb_put_hcd(hcd); +fail: + dev_err(&pdev->dev, "init fail, %d\n", retval); + + return retval; +} + +static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev) +{ + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + usb_remove_hcd(hcd); + + usb_put_hcd(hcd); + + return 0; +} + +static const struct of_device_id npcm7xx_ehci_id_table[] = { + { .compatible = "nuvoton,npcm750-ehci" }, + { }, +}; +MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); + +static struct platform_driver npcm7xx_ehci_hcd_driver = { + .probe = npcm7xx_ehci_hcd_drv_probe, + .remove = npcm7xx_ehci_hcd_drv_remove, + .shutdown = usb_hcd_platform_shutdown, + .driver = { + .name = "npcm7xx-ehci", + .bus = &platform_bus_type, + .pm = pm_ptr(&ehci_npcm7xx_pm_ops), + .of_match_table = npcm7xx_ehci_id_table, + } +}; + +static int __init ehci_npcm7xx_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + pr_info("%s: " DRIVER_DESC "\n", hcd_name); + + ehci_init_driver(&ehci_npcm7xx_hc_driver, NULL); + return platform_driver_register(&npcm7xx_ehci_hcd_driver); +} +module_init(ehci_npcm7xx_init); + +static void __exit ehci_npcm7xx_cleanup(void) +{ + platform_driver_unregister(&npcm7xx_ehci_hcd_driver); +} +module_exit(ehci_npcm7xx_cleanup); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_ALIAS("platform:npcm7xx-ehci"); +MODULE_AUTHOR("Avi Fishman"); +MODULE_LICENSE("GPL v2"); |