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 | #define DRIVER_DESC "EHCI npcm7xx driver" |
26 | |
27 | static struct hc_driver __read_mostly ehci_npcm7xx_hc_driver; |
28 | |
29 | static int __maybe_unused ehci_npcm7xx_drv_suspend(struct device *dev) |
30 | { |
31 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
32 | bool do_wakeup = device_may_wakeup(dev); |
33 | |
34 | return ehci_suspend(hcd, do_wakeup); |
35 | } |
36 | |
37 | static int __maybe_unused ehci_npcm7xx_drv_resume(struct device *dev) |
38 | { |
39 | struct usb_hcd *hcd = dev_get_drvdata(dev); |
40 | |
41 | ehci_resume(hcd, force_reset: false); |
42 | return 0; |
43 | } |
44 | |
45 | static SIMPLE_DEV_PM_OPS(ehci_npcm7xx_pm_ops, ehci_npcm7xx_drv_suspend, |
46 | ehci_npcm7xx_drv_resume); |
47 | |
48 | static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev) |
49 | { |
50 | struct usb_hcd *hcd; |
51 | struct resource *res; |
52 | const struct hc_driver *driver = &ehci_npcm7xx_hc_driver; |
53 | int irq; |
54 | int retval; |
55 | |
56 | dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n" ); |
57 | |
58 | if (usb_disabled()) |
59 | return -ENODEV; |
60 | |
61 | irq = platform_get_irq(pdev, 0); |
62 | if (irq < 0) { |
63 | retval = irq; |
64 | goto fail; |
65 | } |
66 | |
67 | /* |
68 | * Right now device-tree probed devices don't get dma_mask set. |
69 | * Since shared usb code relies on it, set it here for now. |
70 | * Once we have dma capability bindings this can go away. |
71 | */ |
72 | retval = dma_coerce_mask_and_coherent(dev: &pdev->dev, DMA_BIT_MASK(32)); |
73 | if (retval) |
74 | goto fail; |
75 | |
76 | hcd = usb_create_hcd(driver, dev: &pdev->dev, bus_name: dev_name(dev: &pdev->dev)); |
77 | if (!hcd) { |
78 | retval = -ENOMEM; |
79 | goto fail; |
80 | } |
81 | |
82 | hcd->regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, res: &res); |
83 | if (IS_ERR(ptr: hcd->regs)) { |
84 | retval = PTR_ERR(ptr: hcd->regs); |
85 | goto err_put_hcd; |
86 | } |
87 | hcd->rsrc_start = res->start; |
88 | hcd->rsrc_len = resource_size(res); |
89 | |
90 | /* registers start at offset 0x0 */ |
91 | hcd_to_ehci(hcd)->caps = hcd->regs; |
92 | |
93 | retval = usb_add_hcd(hcd, irqnum: irq, IRQF_SHARED); |
94 | if (retval) |
95 | goto err_put_hcd; |
96 | |
97 | device_wakeup_enable(dev: hcd->self.controller); |
98 | return retval; |
99 | |
100 | err_put_hcd: |
101 | usb_put_hcd(hcd); |
102 | fail: |
103 | dev_err(&pdev->dev, "init fail, %d\n" , retval); |
104 | |
105 | return retval; |
106 | } |
107 | |
108 | static void npcm7xx_ehci_hcd_drv_remove(struct platform_device *pdev) |
109 | { |
110 | struct usb_hcd *hcd = platform_get_drvdata(pdev); |
111 | |
112 | usb_remove_hcd(hcd); |
113 | |
114 | usb_put_hcd(hcd); |
115 | } |
116 | |
117 | static const struct of_device_id npcm7xx_ehci_id_table[] = { |
118 | { .compatible = "nuvoton,npcm750-ehci" }, |
119 | { }, |
120 | }; |
121 | MODULE_DEVICE_TABLE(of, npcm7xx_ehci_id_table); |
122 | |
123 | static struct platform_driver npcm7xx_ehci_hcd_driver = { |
124 | .probe = npcm7xx_ehci_hcd_drv_probe, |
125 | .remove_new = npcm7xx_ehci_hcd_drv_remove, |
126 | .shutdown = usb_hcd_platform_shutdown, |
127 | .driver = { |
128 | .name = "npcm7xx-ehci" , |
129 | .bus = &platform_bus_type, |
130 | .pm = pm_ptr(&ehci_npcm7xx_pm_ops), |
131 | .of_match_table = npcm7xx_ehci_id_table, |
132 | } |
133 | }; |
134 | |
135 | static int __init ehci_npcm7xx_init(void) |
136 | { |
137 | if (usb_disabled()) |
138 | return -ENODEV; |
139 | |
140 | ehci_init_driver(drv: &ehci_npcm7xx_hc_driver, NULL); |
141 | return platform_driver_register(&npcm7xx_ehci_hcd_driver); |
142 | } |
143 | module_init(ehci_npcm7xx_init); |
144 | |
145 | static void __exit ehci_npcm7xx_cleanup(void) |
146 | { |
147 | platform_driver_unregister(&npcm7xx_ehci_hcd_driver); |
148 | } |
149 | module_exit(ehci_npcm7xx_cleanup); |
150 | |
151 | MODULE_DESCRIPTION(DRIVER_DESC); |
152 | MODULE_ALIAS("platform:npcm7xx-ehci" ); |
153 | MODULE_AUTHOR("Avi Fishman" ); |
154 | MODULE_LICENSE("GPL v2" ); |
155 | |