linux/drivers/usb/host/ehci-npcm7xx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Nuvoton NPCM7xx driver for EHCI HCD
   4 *
   5 * Copyright (C) 2018 Nuvoton Technologies,
   6 * Avi Fishman <avi.fishman@nuvoton.com> <avifishman70@gmail.com>
   7 * Tomer Maimon <tomer.maimon@nuvoton.com> <tmaimon77@gmail.com>
   8 *
   9 * Based on various ehci-spear.c driver
  10 */
  11
  12
  13#include <linux/dma-mapping.h>
  14
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/of.h>
  18#include <linux/platform_device.h>
  19#include <linux/pm.h>
  20#include <linux/usb.h>
  21#include <linux/usb/hcd.h>
  22
  23#include "ehci.h"
  24
  25#include <linux/regmap.h>
  26#include <linux/mfd/syscon.h>
  27
  28#define DRIVER_DESC "EHCI npcm7xx driver"
  29
  30static const char hcd_name[] = "npcm7xx-ehci";
  31
  32#define  USB2PHYCTL_OFFSET 0x144
  33
  34#define  IPSRST2_OFFSET 0x24
  35#define  IPSRST3_OFFSET 0x34
  36
  37
  38static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver;
  39
  40#ifdef CONFIG_PM_SLEEP
  41static int ehci_npcm7xx_drv_suspend(struct device *dev)
  42{
  43        struct usb_hcd *hcd = dev_get_drvdata(dev);
  44        bool do_wakeup = device_may_wakeup(dev);
  45
  46        return ehci_suspend(hcd, do_wakeup);
  47}
  48
  49static int ehci_npcm7xx_drv_resume(struct device *dev)
  50{
  51        struct usb_hcd *hcd = dev_get_drvdata(dev);
  52
  53        ehci_resume(hcd, false);
  54        return 0;
  55}
  56#endif /* CONFIG_PM_SLEEP */
  57
  58static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend,
  59                ehci_npcm7xx_drv_resume);
  60
  61static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
  62{
  63        struct usb_hcd *hcd;
  64        struct resource *res;
  65        struct regmap *gcr_regmap;
  66        struct regmap *rst_regmap;
  67        const struct hc_driver *driver = &ehci_npcm7xx_hc_driver;
  68        int irq;
  69        int retval;
  70
  71        dev_dbg(&pdev->dev,     "initializing npcm7xx ehci USB Controller\n");
  72
  73        gcr_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-gcr");
  74        if (IS_ERR(gcr_regmap)) {
  75                dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-gcr\n",
  76                        __func__);
  77                return PTR_ERR(gcr_regmap);
  78        }
  79
  80        rst_regmap = syscon_regmap_lookup_by_compatible("nuvoton,npcm750-rst");
  81        if (IS_ERR(rst_regmap)) {
  82                dev_err(&pdev->dev, "%s: failed to find nuvoton,npcm750-rst\n",
  83                        __func__);
  84                return PTR_ERR(rst_regmap);
  85        }
  86
  87        /********* phy init  ******/
  88        // reset usb host
  89        regmap_update_bits(rst_regmap, IPSRST2_OFFSET,
  90                        (0x1 << 26), (0x1 << 26));
  91        regmap_update_bits(rst_regmap, IPSRST3_OFFSET,
  92                        (0x1 << 25), (0x1 << 25));
  93        regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET,
  94                        (0x1 << 28), 0);
  95
  96        udelay(1);
  97
  98        // enable phy
  99        regmap_update_bits(rst_regmap, IPSRST3_OFFSET,
 100                        (0x1 << 25), 0);
 101
 102        udelay(50); // enable phy
 103
 104        regmap_update_bits(gcr_regmap, USB2PHYCTL_OFFSET,
 105                        (0x1 << 28), (0x1 << 28));
 106
 107        // enable host
 108        regmap_update_bits(rst_regmap, IPSRST2_OFFSET,
 109                        (0x1 << 26), 0);
 110
 111        if (usb_disabled())
 112                return -ENODEV;
 113
 114        irq = platform_get_irq(pdev, 0);
 115        if (irq < 0) {
 116                retval = irq;
 117                goto fail;
 118        }
 119
 120        /*
 121         * Right now device-tree probed devices don't get dma_mask set.
 122         * Since shared usb code relies on it, set it here for now.
 123         * Once we have dma capability bindings this can go away.
 124         */
 125        retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
 126        if (retval)
 127                goto fail;
 128
 129        hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
 130        if (!hcd) {
 131                retval = -ENOMEM;
 132                goto fail;
 133        }
 134
 135        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 136        hcd->regs = devm_ioremap_resource(&pdev->dev, res);
 137        if (IS_ERR(hcd->regs)) {
 138                retval = PTR_ERR(hcd->regs);
 139                goto err_put_hcd;
 140        }
 141        hcd->rsrc_start = res->start;
 142        hcd->rsrc_len = resource_size(res);
 143
 144        /* registers start at offset 0x0 */
 145        hcd_to_ehci(hcd)->caps = hcd->regs;
 146
 147        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
 148        if (retval)
 149                goto err_put_hcd;
 150
 151        device_wakeup_enable(hcd->self.controller);
 152        return retval;
 153
 154err_put_hcd:
 155        usb_put_hcd(hcd);
 156fail:
 157        dev_err(&pdev->dev, "init fail, %d\n", retval);
 158
 159        return retval;
 160}
 161
 162static int npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev)
 163{
 164        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 165
 166        usb_remove_hcd(hcd);
 167
 168        usb_put_hcd(hcd);
 169
 170        return 0;
 171}
 172
 173static const struct of_device_id npcm7xx_ehci_id_table[] = {
 174        { .compatible = "nuvoton,npcm750-ehci" },
 175        { },
 176};
 177MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table);
 178
 179static struct platform_driver npcm7xx_ehci_hcd_driver = {
 180        .probe          = npcm7xx_ehci_hcd_drv_probe,
 181        .remove         = npcm7xx_ehci_hcd_drv_remove,
 182        .shutdown       = usb_hcd_platform_shutdown,
 183        .driver         = {
 184                .name = "npcm7xx-ehci",
 185                .bus = &platform_bus_type,
 186                .pm = &ehci_npcm7xx_pm_ops,
 187                .of_match_table = npcm7xx_ehci_id_table,
 188        }
 189};
 190
 191static int __init ehci_npcm7xx_init(void)
 192{
 193        if (usb_disabled())
 194                return -ENODEV;
 195
 196        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 197
 198        ehci_init_driver(&ehci_npcm7xx_hc_driver, NULL);
 199        return platform_driver_register(&npcm7xx_ehci_hcd_driver);
 200}
 201module_init(ehci_npcm7xx_init);
 202
 203static void __exit ehci_npcm7xx_cleanup(void)
 204{
 205        platform_driver_unregister(&npcm7xx_ehci_hcd_driver);
 206}
 207module_exit(ehci_npcm7xx_cleanup);
 208
 209MODULE_DESCRIPTION(DRIVER_DESC);
 210MODULE_ALIAS("platform:npcm7xx-ehci");
 211MODULE_AUTHOR("Avi Fishman");
 212MODULE_LICENSE("GPL v2");
 213