1df44831eSAvi Fishman // SPDX-License-Identifier: GPL-2.0
2df44831eSAvi Fishman /*
3df44831eSAvi Fishman * Nuvoton NPCM7xx driver for EHCI HCD
4df44831eSAvi Fishman *
5df44831eSAvi Fishman * Copyright (C) 2018 Nuvoton Technologies,
6df44831eSAvi Fishman * Avi Fishman <avi.fishman@nuvoton.com> <avifishman70@gmail.com>
7df44831eSAvi Fishman * Tomer Maimon <tomer.maimon@nuvoton.com> <tmaimon77@gmail.com>
8df44831eSAvi Fishman *
9df44831eSAvi Fishman * Based on various ehci-spear.c driver
10df44831eSAvi Fishman */
11df44831eSAvi Fishman
12df44831eSAvi Fishman
13df44831eSAvi Fishman #include <linux/dma-mapping.h>
14df44831eSAvi Fishman
15df44831eSAvi Fishman #include <linux/kernel.h>
16df44831eSAvi Fishman #include <linux/module.h>
17df44831eSAvi Fishman #include <linux/of.h>
18df44831eSAvi Fishman #include <linux/platform_device.h>
19df44831eSAvi Fishman #include <linux/pm.h>
20df44831eSAvi Fishman #include <linux/usb.h>
21df44831eSAvi Fishman #include <linux/usb/hcd.h>
22df44831eSAvi Fishman
23df44831eSAvi Fishman #include "ehci.h"
24df44831eSAvi Fishman
25df44831eSAvi Fishman #define DRIVER_DESC "EHCI npcm7xx driver"
26df44831eSAvi Fishman
27df44831eSAvi Fishman static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver;
28df44831eSAvi Fishman
ehci_npcm7xx_drv_suspend(struct device * dev)29f0dbd25fSPaul Cercueil static int __maybe_unused ehci_npcm7xx_drv_suspend(struct device *dev)
30df44831eSAvi Fishman {
31df44831eSAvi Fishman struct usb_hcd *hcd = dev_get_drvdata(dev);
32df44831eSAvi Fishman bool do_wakeup = device_may_wakeup(dev);
33df44831eSAvi Fishman
34df44831eSAvi Fishman return ehci_suspend(hcd, do_wakeup);
35df44831eSAvi Fishman }
36df44831eSAvi Fishman
ehci_npcm7xx_drv_resume(struct device * dev)37f0dbd25fSPaul Cercueil static int __maybe_unused ehci_npcm7xx_drv_resume(struct device *dev)
38df44831eSAvi Fishman {
39df44831eSAvi Fishman struct usb_hcd *hcd = dev_get_drvdata(dev);
40df44831eSAvi Fishman
41df44831eSAvi Fishman ehci_resume(hcd, false);
42df44831eSAvi Fishman return 0;
43df44831eSAvi Fishman }
44df44831eSAvi Fishman
45df44831eSAvi Fishman static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend,
46df44831eSAvi Fishman ehci_npcm7xx_drv_resume);
47df44831eSAvi Fishman
npcm7xx_ehci_hcd_drv_probe(struct platform_device * pdev)48df44831eSAvi Fishman static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
49df44831eSAvi Fishman {
50df44831eSAvi Fishman struct usb_hcd *hcd;
51df44831eSAvi Fishman struct resource *res;
52df44831eSAvi Fishman const struct hc_driver *driver = &ehci_npcm7xx_hc_driver;
53df44831eSAvi Fishman int irq;
54df44831eSAvi Fishman int retval;
55df44831eSAvi Fishman
56df44831eSAvi Fishman dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
57df44831eSAvi Fishman
58df44831eSAvi Fishman if (usb_disabled())
59df44831eSAvi Fishman return -ENODEV;
60df44831eSAvi Fishman
61df44831eSAvi Fishman irq = platform_get_irq(pdev, 0);
62df44831eSAvi Fishman if (irq < 0) {
63df44831eSAvi Fishman retval = irq;
64df44831eSAvi Fishman goto fail;
65df44831eSAvi Fishman }
66df44831eSAvi Fishman
67df44831eSAvi Fishman /*
68df44831eSAvi Fishman * Right now device-tree probed devices don't get dma_mask set.
69df44831eSAvi Fishman * Since shared usb code relies on it, set it here for now.
70df44831eSAvi Fishman * Once we have dma capability bindings this can go away.
71df44831eSAvi Fishman */
72df44831eSAvi Fishman retval = dma_coerce_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
73df44831eSAvi Fishman if (retval)
74df44831eSAvi Fishman goto fail;
75df44831eSAvi Fishman
76df44831eSAvi Fishman hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
77df44831eSAvi Fishman if (!hcd) {
78df44831eSAvi Fishman retval = -ENOMEM;
79df44831eSAvi Fishman goto fail;
80df44831eSAvi Fishman }
81df44831eSAvi Fishman
82*438ca588SYangtao Li hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
83df44831eSAvi Fishman if (IS_ERR(hcd->regs)) {
84df44831eSAvi Fishman retval = PTR_ERR(hcd->regs);
85df44831eSAvi Fishman goto err_put_hcd;
86df44831eSAvi Fishman }
87df44831eSAvi Fishman hcd->rsrc_start = res->start;
88df44831eSAvi Fishman hcd->rsrc_len = resource_size(res);
89df44831eSAvi Fishman
90df44831eSAvi Fishman /* registers start at offset 0x0 */
91df44831eSAvi Fishman hcd_to_ehci(hcd)->caps = hcd->regs;
92df44831eSAvi Fishman
93df44831eSAvi Fishman retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
94df44831eSAvi Fishman if (retval)
95df44831eSAvi Fishman goto err_put_hcd;
96df44831eSAvi Fishman
97df44831eSAvi Fishman device_wakeup_enable(hcd->self.controller);
98df44831eSAvi Fishman return retval;
99df44831eSAvi Fishman
100df44831eSAvi Fishman err_put_hcd:
101df44831eSAvi Fishman usb_put_hcd(hcd);
102df44831eSAvi Fishman fail:
103df44831eSAvi Fishman dev_err(&pdev->dev, "init fail, %d\n", retval);
104df44831eSAvi Fishman
105df44831eSAvi Fishman return retval;
106df44831eSAvi Fishman }
107df44831eSAvi Fishman
npcm7xx_ehci_hcd_drv_remove(struct platform_device * pdev)1087d6d8199SUwe Kleine-König static void npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev)
109df44831eSAvi Fishman {
110df44831eSAvi Fishman struct usb_hcd *hcd = platform_get_drvdata(pdev);
111df44831eSAvi Fishman
112df44831eSAvi Fishman usb_remove_hcd(hcd);
113df44831eSAvi Fishman
114df44831eSAvi Fishman usb_put_hcd(hcd);
115df44831eSAvi Fishman }
116df44831eSAvi Fishman
117df44831eSAvi Fishman static const struct of_device_id npcm7xx_ehci_id_table[] = {
118df44831eSAvi Fishman { .compatible = "nuvoton,npcm750-ehci" },
119df44831eSAvi Fishman { },
120df44831eSAvi Fishman };
121df44831eSAvi Fishman MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table);
122df44831eSAvi Fishman
123df44831eSAvi Fishman static struct platform_driver npcm7xx_ehci_hcd_driver = {
124df44831eSAvi Fishman .probe = npcm7xx_ehci_hcd_drv_probe,
1257d6d8199SUwe Kleine-König .remove_new = npcm7xx_ehci_hcd_drv_remove,
126df44831eSAvi Fishman .shutdown = usb_hcd_platform_shutdown,
127df44831eSAvi Fishman .driver = {
128df44831eSAvi Fishman .name = "npcm7xx-ehci",
129df44831eSAvi Fishman .bus = &platform_bus_type,
130f0dbd25fSPaul Cercueil .pm = pm_ptr(&ehci_npcm7xx_pm_ops),
131df44831eSAvi Fishman .of_match_table = npcm7xx_ehci_id_table,
132df44831eSAvi Fishman }
133df44831eSAvi Fishman };
134df44831eSAvi Fishman
ehci_npcm7xx_init(void)135df44831eSAvi Fishman static int __init ehci_npcm7xx_init(void)
136df44831eSAvi Fishman {
137df44831eSAvi Fishman if (usb_disabled())
138df44831eSAvi Fishman return -ENODEV;
139df44831eSAvi Fishman
140df44831eSAvi Fishman ehci_init_driver(&ehci_npcm7xx_hc_driver, NULL);
141df44831eSAvi Fishman return platform_driver_register(&npcm7xx_ehci_hcd_driver);
142df44831eSAvi Fishman }
143df44831eSAvi Fishman module_init(ehci_npcm7xx_init);
144df44831eSAvi Fishman
ehci_npcm7xx_cleanup(void)145df44831eSAvi Fishman static void __exit ehci_npcm7xx_cleanup(void)
146df44831eSAvi Fishman {
147df44831eSAvi Fishman platform_driver_unregister(&npcm7xx_ehci_hcd_driver);
148df44831eSAvi Fishman }
149df44831eSAvi Fishman module_exit(ehci_npcm7xx_cleanup);
150df44831eSAvi Fishman
151df44831eSAvi Fishman MODULE_DESCRIPTION(DRIVER_DESC);
152df44831eSAvi Fishman MODULE_ALIAS("platform:npcm7xx-ehci");
153df44831eSAvi Fishman MODULE_AUTHOR("Avi Fishman");
154df44831eSAvi Fishman MODULE_LICENSE("GPL v2");
155