xref: /linux/drivers/usb/isp1760/isp1760-if.c (revision 8dd06ef34b6e2f41b29fbf5fc1663780f2524285)
1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2db11e47dSSebastian Siewior /*
3db11e47dSSebastian Siewior  * Glue code for the ISP1760 driver and bus
4db11e47dSSebastian Siewior  * Currently there is support for
5db11e47dSSebastian Siewior  * - OpenFirmware
6db11e47dSSebastian Siewior  * - PCI
79da69c60SMichael Hennerich  * - PDEV (generic platform device centralized driver model)
8db11e47dSSebastian Siewior  *
9db11e47dSSebastian Siewior  * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
10db11e47dSSebastian Siewior  *
11db11e47dSSebastian Siewior  */
12db11e47dSSebastian Siewior 
13db11e47dSSebastian Siewior #include <linux/usb.h>
14db11e47dSSebastian Siewior #include <linux/io.h>
156eb0de82SPaul Gortmaker #include <linux/module.h>
16c3cc40ccSLaurent Pinchart #include <linux/of.h>
17f7e7aa58SCatalin Marinas #include <linux/platform_device.h>
18c3cc40ccSLaurent Pinchart #include <linux/slab.h>
199da69c60SMichael Hennerich #include <linux/usb/isp1760.h>
2027729aadSEric Lescouet #include <linux/usb/hcd.h>
21db11e47dSSebastian Siewior 
224b1a577dSLaurent Pinchart #include "isp1760-core.h"
23e19c99e7SLaurent Pinchart #include "isp1760-regs.h"
24db11e47dSSebastian Siewior 
252c93e790Syuan linyu #ifdef CONFIG_USB_PCI
26db11e47dSSebastian Siewior #include <linux/pci.h>
27db11e47dSSebastian Siewior #endif
28db11e47dSSebastian Siewior 
292c93e790Syuan linyu #ifdef CONFIG_USB_PCI
30c770e001SLaurent Pinchart static int isp1761_pci_init(struct pci_dev *dev)
31db11e47dSSebastian Siewior {
32c770e001SLaurent Pinchart 	resource_size_t mem_start;
33c770e001SLaurent Pinchart 	resource_size_t mem_length;
346013bbbaSKarl Bongers 	u8 __iomem *iobase;
35c770e001SLaurent Pinchart 	u8 latency, limit;
36c770e001SLaurent Pinchart 	int retry_count;
37c770e001SLaurent Pinchart 	u32 reg_data;
38db11e47dSSebastian Siewior 
39c770e001SLaurent Pinchart 	/* Grab the PLX PCI shared memory of the ISP 1761 we need  */
40c770e001SLaurent Pinchart 	mem_start = pci_resource_start(dev, 3);
41c770e001SLaurent Pinchart 	mem_length = pci_resource_len(dev, 3);
42c770e001SLaurent Pinchart 	if (mem_length < 0xffff) {
43c770e001SLaurent Pinchart 		printk(KERN_ERR "memory length for this resource is wrong\n");
44c770e001SLaurent Pinchart 		return -ENOMEM;
45c770e001SLaurent Pinchart 	}
46db11e47dSSebastian Siewior 
47c770e001SLaurent Pinchart 	if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) {
48c770e001SLaurent Pinchart 		printk(KERN_ERR "host controller already in use\n");
49db11e47dSSebastian Siewior 		return -EBUSY;
50db11e47dSSebastian Siewior 	}
51db11e47dSSebastian Siewior 
526013bbbaSKarl Bongers 	/* map available memory */
53*4bdc0d67SChristoph Hellwig 	iobase = ioremap(mem_start, mem_length);
54c770e001SLaurent Pinchart 	if (!iobase) {
556013bbbaSKarl Bongers 		printk(KERN_ERR "Error ioremap failed\n");
56c770e001SLaurent Pinchart 		release_mem_region(mem_start, mem_length);
57c770e001SLaurent Pinchart 		return -ENOMEM;
58db11e47dSSebastian Siewior 	}
59db11e47dSSebastian Siewior 
60db11e47dSSebastian Siewior 	/* bad pci latencies can contribute to overruns */
61db11e47dSSebastian Siewior 	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
62db11e47dSSebastian Siewior 	if (latency) {
63db11e47dSSebastian Siewior 		pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
64db11e47dSSebastian Siewior 		if (limit && limit < latency)
65db11e47dSSebastian Siewior 			pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
66db11e47dSSebastian Siewior 	}
67db11e47dSSebastian Siewior 
68db11e47dSSebastian Siewior 	/* Try to check whether we can access Scratch Register of
69db11e47dSSebastian Siewior 	 * Host Controller or not. The initial PCI access is retried until
70db11e47dSSebastian Siewior 	 * local init for the PCI bridge is completed
71db11e47dSSebastian Siewior 	 */
72db11e47dSSebastian Siewior 	retry_count = 20;
73db11e47dSSebastian Siewior 	reg_data = 0;
74db11e47dSSebastian Siewior 	while ((reg_data != 0xFACE) && retry_count) {
75db11e47dSSebastian Siewior 		/*by default host is in 16bit mode, so
76db11e47dSSebastian Siewior 		 * io operations at this stage must be 16 bit
77db11e47dSSebastian Siewior 		 * */
78c770e001SLaurent Pinchart 		writel(0xface, iobase + HC_SCRATCH_REG);
79db11e47dSSebastian Siewior 		udelay(100);
80c770e001SLaurent Pinchart 		reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff;
81db11e47dSSebastian Siewior 		retry_count--;
82db11e47dSSebastian Siewior 	}
83db11e47dSSebastian Siewior 
84c770e001SLaurent Pinchart 	iounmap(iobase);
85c770e001SLaurent Pinchart 	release_mem_region(mem_start, mem_length);
866013bbbaSKarl Bongers 
87db11e47dSSebastian Siewior 	/* Host Controller presence is detected by writing to scratch register
88db11e47dSSebastian Siewior 	 * and reading back and checking the contents are same or not
89db11e47dSSebastian Siewior 	 */
90db11e47dSSebastian Siewior 	if (reg_data != 0xFACE) {
91802f389aSGreg Kroah-Hartman 		dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
92c770e001SLaurent Pinchart 		return -ENOMEM;
93db11e47dSSebastian Siewior 	}
94db11e47dSSebastian Siewior 
95c770e001SLaurent Pinchart 	/* Grab the PLX PCI mem maped port start address we need  */
96c770e001SLaurent Pinchart 	mem_start = pci_resource_start(dev, 0);
97c770e001SLaurent Pinchart 	mem_length = pci_resource_len(dev, 0);
98c770e001SLaurent Pinchart 
99c770e001SLaurent Pinchart 	if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) {
100c770e001SLaurent Pinchart 		printk(KERN_ERR "request region #1\n");
101c770e001SLaurent Pinchart 		return -EBUSY;
102c770e001SLaurent Pinchart 	}
103c770e001SLaurent Pinchart 
104*4bdc0d67SChristoph Hellwig 	iobase = ioremap(mem_start, mem_length);
105c770e001SLaurent Pinchart 	if (!iobase) {
106c770e001SLaurent Pinchart 		printk(KERN_ERR "ioremap #1\n");
107c770e001SLaurent Pinchart 		release_mem_region(mem_start, mem_length);
108c770e001SLaurent Pinchart 		return -ENOMEM;
109c770e001SLaurent Pinchart 	}
110db11e47dSSebastian Siewior 
1116013bbbaSKarl Bongers 	/* configure PLX PCI chip to pass interrupts */
1126013bbbaSKarl Bongers #define PLX_INT_CSR_REG 0x68
1136013bbbaSKarl Bongers 	reg_data = readl(iobase + PLX_INT_CSR_REG);
1146013bbbaSKarl Bongers 	reg_data |= 0x900;
1156013bbbaSKarl Bongers 	writel(reg_data, iobase + PLX_INT_CSR_REG);
116db11e47dSSebastian Siewior 
1176013bbbaSKarl Bongers 	/* done with PLX IO access */
1186013bbbaSKarl Bongers 	iounmap(iobase);
119c770e001SLaurent Pinchart 	release_mem_region(mem_start, mem_length);
120c770e001SLaurent Pinchart 
121c770e001SLaurent Pinchart 	return 0;
122c770e001SLaurent Pinchart }
123c770e001SLaurent Pinchart 
124c770e001SLaurent Pinchart static int isp1761_pci_probe(struct pci_dev *dev,
125c770e001SLaurent Pinchart 		const struct pci_device_id *id)
126c770e001SLaurent Pinchart {
127c770e001SLaurent Pinchart 	unsigned int devflags = 0;
128c770e001SLaurent Pinchart 	int ret;
129c770e001SLaurent Pinchart 
130c770e001SLaurent Pinchart 	if (!dev->irq)
131c770e001SLaurent Pinchart 		return -ENODEV;
132c770e001SLaurent Pinchart 
133c770e001SLaurent Pinchart 	if (pci_enable_device(dev) < 0)
134c770e001SLaurent Pinchart 		return -ENODEV;
135c770e001SLaurent Pinchart 
136c770e001SLaurent Pinchart 	ret = isp1761_pci_init(dev);
137c770e001SLaurent Pinchart 	if (ret < 0)
138c770e001SLaurent Pinchart 		goto error;
139c770e001SLaurent Pinchart 
140c770e001SLaurent Pinchart 	pci_set_master(dev);
1416013bbbaSKarl Bongers 
142667c45c2SLaurent Pinchart 	ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev,
143667c45c2SLaurent Pinchart 			       devflags);
144c770e001SLaurent Pinchart 	if (ret < 0)
145c770e001SLaurent Pinchart 		goto error;
1466013bbbaSKarl Bongers 
147c770e001SLaurent Pinchart 	return 0;
148c770e001SLaurent Pinchart 
149c770e001SLaurent Pinchart error:
150c770e001SLaurent Pinchart 	pci_disable_device(dev);
151c770e001SLaurent Pinchart 	return ret;
152db11e47dSSebastian Siewior }
1536013bbbaSKarl Bongers 
154db11e47dSSebastian Siewior static void isp1761_pci_remove(struct pci_dev *dev)
155db11e47dSSebastian Siewior {
15610c73f09SLaurent Pinchart 	isp1760_unregister(&dev->dev);
157db11e47dSSebastian Siewior 
158db11e47dSSebastian Siewior 	pci_disable_device(dev);
159db11e47dSSebastian Siewior }
160db11e47dSSebastian Siewior 
161db11e47dSSebastian Siewior static void isp1761_pci_shutdown(struct pci_dev *dev)
162db11e47dSSebastian Siewior {
163db11e47dSSebastian Siewior 	printk(KERN_ERR "ips1761_pci_shutdown\n");
164db11e47dSSebastian Siewior }
165db11e47dSSebastian Siewior 
1666c073568SSebastian Andrzej Siewior static const struct pci_device_id isp1760_plx[] = {
1676c073568SSebastian Andrzej Siewior 	{
1686c073568SSebastian Andrzej Siewior 		.class          = PCI_CLASS_BRIDGE_OTHER << 8,
1696c073568SSebastian Andrzej Siewior 		.class_mask     = ~0,
1706c073568SSebastian Andrzej Siewior 		.vendor		= PCI_VENDOR_ID_PLX,
1716c073568SSebastian Andrzej Siewior 		.device		= 0x5406,
1726c073568SSebastian Andrzej Siewior 		.subvendor	= PCI_VENDOR_ID_PLX,
1736c073568SSebastian Andrzej Siewior 		.subdevice	= 0x9054,
174db11e47dSSebastian Siewior 	},
1756c073568SSebastian Andrzej Siewior 	{ }
176db11e47dSSebastian Siewior };
177db11e47dSSebastian Siewior MODULE_DEVICE_TABLE(pci, isp1760_plx);
178db11e47dSSebastian Siewior 
179db11e47dSSebastian Siewior static struct pci_driver isp1761_pci_driver = {
180db11e47dSSebastian Siewior 	.name =         "isp1760",
181db11e47dSSebastian Siewior 	.id_table =     isp1760_plx,
182db11e47dSSebastian Siewior 	.probe =        isp1761_pci_probe,
183db11e47dSSebastian Siewior 	.remove =       isp1761_pci_remove,
184db11e47dSSebastian Siewior 	.shutdown =     isp1761_pci_shutdown,
185db11e47dSSebastian Siewior };
186db11e47dSSebastian Siewior #endif
187db11e47dSSebastian Siewior 
18841ac7b3aSBill Pemberton static int isp1760_plat_probe(struct platform_device *pdev)
189f7e7aa58SCatalin Marinas {
190667c45c2SLaurent Pinchart 	unsigned long irqflags;
191c3cc40ccSLaurent Pinchart 	unsigned int devflags = 0;
192f7e7aa58SCatalin Marinas 	struct resource *mem_res;
193f7e7aa58SCatalin Marinas 	struct resource *irq_res;
194c3cc40ccSLaurent Pinchart 	int ret;
195f7e7aa58SCatalin Marinas 
196f7e7aa58SCatalin Marinas 	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
197f7e7aa58SCatalin Marinas 
198f7e7aa58SCatalin Marinas 	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
199f7e7aa58SCatalin Marinas 	if (!irq_res) {
200a4e6a852SJoe Perches 		pr_warn("isp1760: IRQ resource not available\n");
20110feee14SLaurent Pinchart 		return -ENODEV;
202f7e7aa58SCatalin Marinas 	}
203667c45c2SLaurent Pinchart 	irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
204f7e7aa58SCatalin Marinas 
205c3cc40ccSLaurent Pinchart 	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
206c3cc40ccSLaurent Pinchart 		struct device_node *dp = pdev->dev.of_node;
207c3cc40ccSLaurent Pinchart 		u32 bus_width = 0;
208c3cc40ccSLaurent Pinchart 
209c3cc40ccSLaurent Pinchart 		if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
2109da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_ISP1761;
211c3cc40ccSLaurent Pinchart 
212c3cc40ccSLaurent Pinchart 		/* Some systems wire up only 16 of the 32 data lines */
213c3cc40ccSLaurent Pinchart 		of_property_read_u32(dp, "bus-width", &bus_width);
214c3cc40ccSLaurent Pinchart 		if (bus_width == 16)
2159da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
216c3cc40ccSLaurent Pinchart 
217c3cc40ccSLaurent Pinchart 		if (of_property_read_bool(dp, "port1-otg"))
2189da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_OTG_EN;
219c3cc40ccSLaurent Pinchart 
220c3cc40ccSLaurent Pinchart 		if (of_property_read_bool(dp, "analog-oc"))
2219da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_ANALOG_OC;
222c3cc40ccSLaurent Pinchart 
223c3cc40ccSLaurent Pinchart 		if (of_property_read_bool(dp, "dack-polarity"))
2249da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
225c3cc40ccSLaurent Pinchart 
226c3cc40ccSLaurent Pinchart 		if (of_property_read_bool(dp, "dreq-polarity"))
227c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
228c3cc40ccSLaurent Pinchart 	} else if (dev_get_platdata(&pdev->dev)) {
229c3cc40ccSLaurent Pinchart 		struct isp1760_platform_data *pdata =
230c3cc40ccSLaurent Pinchart 			dev_get_platdata(&pdev->dev);
231c3cc40ccSLaurent Pinchart 
232c3cc40ccSLaurent Pinchart 		if (pdata->is_isp1761)
233c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_ISP1761;
234c3cc40ccSLaurent Pinchart 		if (pdata->bus_width_16)
235c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
236c3cc40ccSLaurent Pinchart 		if (pdata->port1_otg)
237c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_OTG_EN;
238c3cc40ccSLaurent Pinchart 		if (pdata->analog_oc)
239c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_ANALOG_OC;
240c3cc40ccSLaurent Pinchart 		if (pdata->dack_polarity_high)
241c3cc40ccSLaurent Pinchart 			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
242c3cc40ccSLaurent Pinchart 		if (pdata->dreq_polarity_high)
2439da69c60SMichael Hennerich 			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
2449da69c60SMichael Hennerich 	}
2459da69c60SMichael Hennerich 
2464942e00eSLaurent Pinchart 	ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev,
2474942e00eSLaurent Pinchart 			       devflags);
248f0bdbb0eSLaurent Pinchart 	if (ret < 0)
24910feee14SLaurent Pinchart 		return ret;
250f7e7aa58SCatalin Marinas 
251f7e7aa58SCatalin Marinas 	pr_info("ISP1760 USB device initialised\n");
252c3cc40ccSLaurent Pinchart 	return 0;
253f7e7aa58SCatalin Marinas }
254f7e7aa58SCatalin Marinas 
255fb4e98abSBill Pemberton static int isp1760_plat_remove(struct platform_device *pdev)
256f7e7aa58SCatalin Marinas {
25710c73f09SLaurent Pinchart 	isp1760_unregister(&pdev->dev);
258310e9e33SMichael Grzeschik 
259f7e7aa58SCatalin Marinas 	return 0;
260f7e7aa58SCatalin Marinas }
261f7e7aa58SCatalin Marinas 
262c3cc40ccSLaurent Pinchart #ifdef CONFIG_OF
263c3cc40ccSLaurent Pinchart static const struct of_device_id isp1760_of_match[] = {
264c3cc40ccSLaurent Pinchart 	{ .compatible = "nxp,usb-isp1760", },
265c3cc40ccSLaurent Pinchart 	{ .compatible = "nxp,usb-isp1761", },
266c3cc40ccSLaurent Pinchart 	{ },
267c3cc40ccSLaurent Pinchart };
268c3cc40ccSLaurent Pinchart MODULE_DEVICE_TABLE(of, isp1760_of_match);
269c3cc40ccSLaurent Pinchart #endif
270c3cc40ccSLaurent Pinchart 
271f7e7aa58SCatalin Marinas static struct platform_driver isp1760_plat_driver = {
272f7e7aa58SCatalin Marinas 	.probe	= isp1760_plat_probe,
2737690417dSBill Pemberton 	.remove	= isp1760_plat_remove,
274f7e7aa58SCatalin Marinas 	.driver	= {
275f7e7aa58SCatalin Marinas 		.name	= "isp1760",
276c3cc40ccSLaurent Pinchart 		.of_match_table = of_match_ptr(isp1760_of_match),
277f7e7aa58SCatalin Marinas 	},
278f7e7aa58SCatalin Marinas };
279f7e7aa58SCatalin Marinas 
280db11e47dSSebastian Siewior static int __init isp1760_init(void)
281db11e47dSSebastian Siewior {
282f7e7aa58SCatalin Marinas 	int ret, any_ret = -ENODEV;
283db11e47dSSebastian Siewior 
2845a6356acSLaurent Pinchart 	isp1760_init_kmem_once();
285db11e47dSSebastian Siewior 
286f7e7aa58SCatalin Marinas 	ret = platform_driver_register(&isp1760_plat_driver);
287f7e7aa58SCatalin Marinas 	if (!ret)
288f7e7aa58SCatalin Marinas 		any_ret = 0;
2892c93e790Syuan linyu #ifdef CONFIG_USB_PCI
290db11e47dSSebastian Siewior 	ret = pci_register_driver(&isp1761_pci_driver);
291f7e7aa58SCatalin Marinas 	if (!ret)
292f7e7aa58SCatalin Marinas 		any_ret = 0;
293db11e47dSSebastian Siewior #endif
294db11e47dSSebastian Siewior 
295f7e7aa58SCatalin Marinas 	if (any_ret)
2965a6356acSLaurent Pinchart 		isp1760_deinit_kmem_cache();
297f7e7aa58SCatalin Marinas 	return any_ret;
298db11e47dSSebastian Siewior }
299db11e47dSSebastian Siewior module_init(isp1760_init);
300db11e47dSSebastian Siewior 
301db11e47dSSebastian Siewior static void __exit isp1760_exit(void)
302db11e47dSSebastian Siewior {
303f7e7aa58SCatalin Marinas 	platform_driver_unregister(&isp1760_plat_driver);
3042c93e790Syuan linyu #ifdef CONFIG_USB_PCI
305db11e47dSSebastian Siewior 	pci_unregister_driver(&isp1761_pci_driver);
306db11e47dSSebastian Siewior #endif
3075a6356acSLaurent Pinchart 	isp1760_deinit_kmem_cache();
308db11e47dSSebastian Siewior }
309db11e47dSSebastian Siewior module_exit(isp1760_exit);
310