xref: /linux/drivers/usb/dwc3/dwc3-keystone.c (revision 5b76f6a0b0f868d8f4fae4e79a92e433a250a2eb)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2943befc3SWingMan Kwok /**
3943befc3SWingMan Kwok  * dwc3-keystone.c - Keystone Specific Glue layer
4943befc3SWingMan Kwok  *
5943befc3SWingMan Kwok  * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com
6943befc3SWingMan Kwok  *
7943befc3SWingMan Kwok  * Author: WingMan Kwok <w-kwok2@ti.com>
8943befc3SWingMan Kwok  */
9943befc3SWingMan Kwok 
10943befc3SWingMan Kwok #include <linux/module.h>
11943befc3SWingMan Kwok #include <linux/kernel.h>
12943befc3SWingMan Kwok #include <linux/interrupt.h>
13943befc3SWingMan Kwok #include <linux/platform_device.h>
14943befc3SWingMan Kwok #include <linux/dma-mapping.h>
15943befc3SWingMan Kwok #include <linux/io.h>
16943befc3SWingMan Kwok #include <linux/of_platform.h>
17c14af973SFranklin S Cooper Jr #include <linux/pm_runtime.h>
18943befc3SWingMan Kwok 
19943befc3SWingMan Kwok /* USBSS register offsets */
20943befc3SWingMan Kwok #define USBSS_REVISION		0x0000
21943befc3SWingMan Kwok #define USBSS_SYSCONFIG		0x0010
22943befc3SWingMan Kwok #define USBSS_IRQ_EOI		0x0018
23943befc3SWingMan Kwok #define USBSS_IRQSTATUS_RAW_0	0x0020
24943befc3SWingMan Kwok #define USBSS_IRQSTATUS_0	0x0024
25943befc3SWingMan Kwok #define USBSS_IRQENABLE_SET_0	0x0028
26943befc3SWingMan Kwok #define USBSS_IRQENABLE_CLR_0	0x002c
27943befc3SWingMan Kwok 
28943befc3SWingMan Kwok /* IRQ register bits */
29943befc3SWingMan Kwok #define USBSS_IRQ_EOI_LINE(n)	BIT(n)
30943befc3SWingMan Kwok #define USBSS_IRQ_EVENT_ST	BIT(0)
31943befc3SWingMan Kwok #define USBSS_IRQ_COREIRQ_EN	BIT(0)
32943befc3SWingMan Kwok #define USBSS_IRQ_COREIRQ_CLR	BIT(0)
33943befc3SWingMan Kwok 
34943befc3SWingMan Kwok struct dwc3_keystone {
35943befc3SWingMan Kwok 	struct device			*dev;
36943befc3SWingMan Kwok 	void __iomem			*usbss;
37943befc3SWingMan Kwok };
38943befc3SWingMan Kwok 
39943befc3SWingMan Kwok static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
40943befc3SWingMan Kwok {
41943befc3SWingMan Kwok 	return readl(base + offset);
42943befc3SWingMan Kwok }
43943befc3SWingMan Kwok 
44943befc3SWingMan Kwok static inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value)
45943befc3SWingMan Kwok {
46943befc3SWingMan Kwok 	writel(value, base + offset);
47943befc3SWingMan Kwok }
48943befc3SWingMan Kwok 
49943befc3SWingMan Kwok static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
50943befc3SWingMan Kwok {
51943befc3SWingMan Kwok 	u32 val;
52943befc3SWingMan Kwok 
53943befc3SWingMan Kwok 	val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
54943befc3SWingMan Kwok 	val |= USBSS_IRQ_COREIRQ_EN;
55943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
56943befc3SWingMan Kwok }
57943befc3SWingMan Kwok 
58943befc3SWingMan Kwok static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
59943befc3SWingMan Kwok {
60943befc3SWingMan Kwok 	u32 val;
61943befc3SWingMan Kwok 
62943befc3SWingMan Kwok 	val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
63943befc3SWingMan Kwok 	val &= ~USBSS_IRQ_COREIRQ_EN;
64943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
65943befc3SWingMan Kwok }
66943befc3SWingMan Kwok 
67943befc3SWingMan Kwok static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc)
68943befc3SWingMan Kwok {
69943befc3SWingMan Kwok 	struct dwc3_keystone	*kdwc = _kdwc;
70943befc3SWingMan Kwok 
71943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR);
72943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST);
73943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN);
74943befc3SWingMan Kwok 	kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0));
75943befc3SWingMan Kwok 
76943befc3SWingMan Kwok 	return IRQ_HANDLED;
77943befc3SWingMan Kwok }
78943befc3SWingMan Kwok 
79943befc3SWingMan Kwok static int kdwc3_probe(struct platform_device *pdev)
80943befc3SWingMan Kwok {
81943befc3SWingMan Kwok 	struct device		*dev = &pdev->dev;
82943befc3SWingMan Kwok 	struct device_node	*node = pdev->dev.of_node;
83943befc3SWingMan Kwok 	struct dwc3_keystone	*kdwc;
84943befc3SWingMan Kwok 	int			error, irq;
85943befc3SWingMan Kwok 
86943befc3SWingMan Kwok 	kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
87943befc3SWingMan Kwok 	if (!kdwc)
88943befc3SWingMan Kwok 		return -ENOMEM;
89943befc3SWingMan Kwok 
90943befc3SWingMan Kwok 	platform_set_drvdata(pdev, kdwc);
91943befc3SWingMan Kwok 
92943befc3SWingMan Kwok 	kdwc->dev = dev;
93943befc3SWingMan Kwok 
94*5b76f6a0SYueHaibing 	kdwc->usbss = devm_platform_ioremap_resource(pdev, 0);
95943befc3SWingMan Kwok 	if (IS_ERR(kdwc->usbss))
96943befc3SWingMan Kwok 		return PTR_ERR(kdwc->usbss);
97943befc3SWingMan Kwok 
98c14af973SFranklin S Cooper Jr 	pm_runtime_enable(kdwc->dev);
99943befc3SWingMan Kwok 
100c14af973SFranklin S Cooper Jr 	error = pm_runtime_get_sync(kdwc->dev);
101943befc3SWingMan Kwok 	if (error < 0) {
102c14af973SFranklin S Cooper Jr 		dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
103943befc3SWingMan Kwok 			error);
104c14af973SFranklin S Cooper Jr 		goto err_irq;
105943befc3SWingMan Kwok 	}
106943befc3SWingMan Kwok 
107eca6b494SRoger Quadros 	/* IRQ processing not required currently for AM65 */
108eca6b494SRoger Quadros 	if (of_device_is_compatible(node, "ti,am654-dwc3"))
109eca6b494SRoger Quadros 		goto skip_irq;
110eca6b494SRoger Quadros 
111943befc3SWingMan Kwok 	irq = platform_get_irq(pdev, 0);
112943befc3SWingMan Kwok 	if (irq < 0) {
113943befc3SWingMan Kwok 		dev_err(&pdev->dev, "missing irq\n");
11462c60697SJulia Lawall 		error = irq;
115943befc3SWingMan Kwok 		goto err_irq;
116943befc3SWingMan Kwok 	}
117943befc3SWingMan Kwok 
118943befc3SWingMan Kwok 	error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
119943befc3SWingMan Kwok 			dev_name(dev), kdwc);
120943befc3SWingMan Kwok 	if (error) {
121943befc3SWingMan Kwok 		dev_err(dev, "failed to request IRQ #%d --> %d\n",
122943befc3SWingMan Kwok 				irq, error);
123943befc3SWingMan Kwok 		goto err_irq;
124943befc3SWingMan Kwok 	}
125943befc3SWingMan Kwok 
126943befc3SWingMan Kwok 	kdwc3_enable_irqs(kdwc);
127943befc3SWingMan Kwok 
128eca6b494SRoger Quadros skip_irq:
129943befc3SWingMan Kwok 	error = of_platform_populate(node, NULL, NULL, dev);
130943befc3SWingMan Kwok 	if (error) {
131943befc3SWingMan Kwok 		dev_err(&pdev->dev, "failed to create dwc3 core\n");
132943befc3SWingMan Kwok 		goto err_core;
133943befc3SWingMan Kwok 	}
134943befc3SWingMan Kwok 
135943befc3SWingMan Kwok 	return 0;
136943befc3SWingMan Kwok 
137943befc3SWingMan Kwok err_core:
138943befc3SWingMan Kwok 	kdwc3_disable_irqs(kdwc);
139943befc3SWingMan Kwok err_irq:
140c14af973SFranklin S Cooper Jr 	pm_runtime_put_sync(kdwc->dev);
141c14af973SFranklin S Cooper Jr 	pm_runtime_disable(kdwc->dev);
142943befc3SWingMan Kwok 
143943befc3SWingMan Kwok 	return error;
144943befc3SWingMan Kwok }
145943befc3SWingMan Kwok 
146943befc3SWingMan Kwok static int kdwc3_remove_core(struct device *dev, void *c)
147943befc3SWingMan Kwok {
148943befc3SWingMan Kwok 	struct platform_device *pdev = to_platform_device(dev);
149943befc3SWingMan Kwok 
150943befc3SWingMan Kwok 	platform_device_unregister(pdev);
151943befc3SWingMan Kwok 
152943befc3SWingMan Kwok 	return 0;
153943befc3SWingMan Kwok }
154943befc3SWingMan Kwok 
155943befc3SWingMan Kwok static int kdwc3_remove(struct platform_device *pdev)
156943befc3SWingMan Kwok {
157943befc3SWingMan Kwok 	struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
158eca6b494SRoger Quadros 	struct device_node *node = pdev->dev.of_node;
159943befc3SWingMan Kwok 
160eca6b494SRoger Quadros 	if (!of_device_is_compatible(node, "ti,am654-dwc3"))
161943befc3SWingMan Kwok 		kdwc3_disable_irqs(kdwc);
162eca6b494SRoger Quadros 
163943befc3SWingMan Kwok 	device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core);
164c14af973SFranklin S Cooper Jr 	pm_runtime_put_sync(kdwc->dev);
165c14af973SFranklin S Cooper Jr 	pm_runtime_disable(kdwc->dev);
166c14af973SFranklin S Cooper Jr 
167943befc3SWingMan Kwok 	platform_set_drvdata(pdev, NULL);
168943befc3SWingMan Kwok 
169943befc3SWingMan Kwok 	return 0;
170943befc3SWingMan Kwok }
171943befc3SWingMan Kwok 
172943befc3SWingMan Kwok static const struct of_device_id kdwc3_of_match[] = {
173943befc3SWingMan Kwok 	{ .compatible = "ti,keystone-dwc3", },
174eca6b494SRoger Quadros 	{ .compatible = "ti,am654-dwc3" },
175943befc3SWingMan Kwok 	{},
176943befc3SWingMan Kwok };
177943befc3SWingMan Kwok MODULE_DEVICE_TABLE(of, kdwc3_of_match);
178943befc3SWingMan Kwok 
179943befc3SWingMan Kwok static struct platform_driver kdwc3_driver = {
180943befc3SWingMan Kwok 	.probe		= kdwc3_probe,
181943befc3SWingMan Kwok 	.remove		= kdwc3_remove,
182943befc3SWingMan Kwok 	.driver		= {
183943befc3SWingMan Kwok 		.name	= "keystone-dwc3",
184943befc3SWingMan Kwok 		.of_match_table	= kdwc3_of_match,
185943befc3SWingMan Kwok 	},
186943befc3SWingMan Kwok };
187943befc3SWingMan Kwok 
188943befc3SWingMan Kwok module_platform_driver(kdwc3_driver);
189943befc3SWingMan Kwok 
190943befc3SWingMan Kwok MODULE_ALIAS("platform:keystone-dwc3");
191943befc3SWingMan Kwok MODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>");
192943befc3SWingMan Kwok MODULE_LICENSE("GPL v2");
193943befc3SWingMan Kwok MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer");
194