xref: /linux/drivers/usb/host/ohci-ps3.c (revision 6d247e4d)
15fd54aceSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
26a6c957eSGeoff Levand /*
36a6c957eSGeoff Levand  *  PS3 OHCI Host Controller driver
46a6c957eSGeoff Levand  *
56a6c957eSGeoff Levand  *  Copyright (C) 2006 Sony Computer Entertainment Inc.
66a6c957eSGeoff Levand  *  Copyright 2006 Sony Corp.
76a6c957eSGeoff Levand  */
86a6c957eSGeoff Levand 
97a4eb7fdSGeoff Levand #include <asm/firmware.h>
106a6c957eSGeoff Levand #include <asm/ps3.h>
116a6c957eSGeoff Levand 
ps3_ohci_hc_reset(struct usb_hcd * hcd)126a6c957eSGeoff Levand static int ps3_ohci_hc_reset(struct usb_hcd *hcd)
136a6c957eSGeoff Levand {
146a6c957eSGeoff Levand 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
156a6c957eSGeoff Levand 
166a6c957eSGeoff Levand 	ohci->flags |= OHCI_QUIRK_BE_MMIO;
176a6c957eSGeoff Levand 	ohci_hcd_init(ohci);
186a6c957eSGeoff Levand 	return ohci_init(ohci);
196a6c957eSGeoff Levand }
206a6c957eSGeoff Levand 
ps3_ohci_hc_start(struct usb_hcd * hcd)2141ac7b3aSBill Pemberton static int ps3_ohci_hc_start(struct usb_hcd *hcd)
226a6c957eSGeoff Levand {
236a6c957eSGeoff Levand 	int result;
246a6c957eSGeoff Levand 	struct ohci_hcd *ohci = hcd_to_ohci(hcd);
256a6c957eSGeoff Levand 
266a6c957eSGeoff Levand 	/* Handle root hub init quirk in spider south bridge. */
276a6c957eSGeoff Levand 	/* Also set PwrOn2PwrGood to 0x7f (254ms). */
286a6c957eSGeoff Levand 
296a6c957eSGeoff Levand 	ohci_writel(ohci, 0x7f000000 | RH_A_PSM | RH_A_OCPM,
306a6c957eSGeoff Levand 		&ohci->regs->roothub.a);
316a6c957eSGeoff Levand 	ohci_writel(ohci, 0x00060000, &ohci->regs->roothub.b);
326a6c957eSGeoff Levand 
336a6c957eSGeoff Levand 	result = ohci_run(ohci);
346a6c957eSGeoff Levand 
356a6c957eSGeoff Levand 	if (result < 0) {
368ab60ea0SGreg Kroah-Hartman 		dev_err(hcd->self.controller, "can't start %s\n",
378ab60ea0SGreg Kroah-Hartman 			hcd->self.bus_name);
386a6c957eSGeoff Levand 		ohci_stop(hcd);
396a6c957eSGeoff Levand 	}
406a6c957eSGeoff Levand 
416a6c957eSGeoff Levand 	return result;
426a6c957eSGeoff Levand }
436a6c957eSGeoff Levand 
446a6c957eSGeoff Levand static const struct hc_driver ps3_ohci_hc_driver = {
456a6c957eSGeoff Levand 	.description		= hcd_name,
466a6c957eSGeoff Levand 	.product_desc		= "PS3 OHCI Host Controller",
476a6c957eSGeoff Levand 	.hcd_priv_size		= sizeof(struct ohci_hcd),
486a6c957eSGeoff Levand 	.irq			= ohci_irq,
497b81cb6bSChristoph Hellwig 	.flags			= HCD_MEMORY | HCD_DMA | HCD_USB11,
506a6c957eSGeoff Levand 	.reset			= ps3_ohci_hc_reset,
516a6c957eSGeoff Levand 	.start			= ps3_ohci_hc_start,
526a6c957eSGeoff Levand 	.stop			= ohci_stop,
536a6c957eSGeoff Levand 	.shutdown		= ohci_shutdown,
546a6c957eSGeoff Levand 	.urb_enqueue		= ohci_urb_enqueue,
556a6c957eSGeoff Levand 	.urb_dequeue		= ohci_urb_dequeue,
566a6c957eSGeoff Levand 	.endpoint_disable	= ohci_endpoint_disable,
576a6c957eSGeoff Levand 	.get_frame_number	= ohci_get_frame,
586a6c957eSGeoff Levand 	.hub_status_data	= ohci_hub_status_data,
596a6c957eSGeoff Levand 	.hub_control		= ohci_hub_control,
606a6c957eSGeoff Levand 	.start_port_reset	= ohci_start_port_reset,
616a6c957eSGeoff Levand #if defined(CONFIG_PM)
626a6c957eSGeoff Levand 	.bus_suspend 		= ohci_bus_suspend,
636a6c957eSGeoff Levand 	.bus_resume 		= ohci_bus_resume,
646a6c957eSGeoff Levand #endif
656a6c957eSGeoff Levand };
666a6c957eSGeoff Levand 
ps3_ohci_probe(struct ps3_system_bus_device * dev)6741ac7b3aSBill Pemberton static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
686a6c957eSGeoff Levand {
696a6c957eSGeoff Levand 	int result;
706a6c957eSGeoff Levand 	struct usb_hcd *hcd;
716a6c957eSGeoff Levand 	unsigned int virq;
7248e91846SGeoff Levand 	static u64 dummy_mask;
736a6c957eSGeoff Levand 
746a6c957eSGeoff Levand 	if (usb_disabled()) {
756a6c957eSGeoff Levand 		result = -ENODEV;
766a6c957eSGeoff Levand 		goto fail_start;
776a6c957eSGeoff Levand 	}
786a6c957eSGeoff Levand 
797a4eb7fdSGeoff Levand 	result = ps3_open_hv_device(dev);
807a4eb7fdSGeoff Levand 
817a4eb7fdSGeoff Levand 	if (result) {
827a4eb7fdSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed: %s\n",
837a4eb7fdSGeoff Levand 			__func__, __LINE__, ps3_result(result));
847a4eb7fdSGeoff Levand 		result = -EPERM;
857a4eb7fdSGeoff Levand 		goto fail_open;
867a4eb7fdSGeoff Levand 	}
877a4eb7fdSGeoff Levand 
887a4eb7fdSGeoff Levand 	result = ps3_dma_region_create(dev->d_region);
897a4eb7fdSGeoff Levand 
907a4eb7fdSGeoff Levand 	if (result) {
917a4eb7fdSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
927a4eb7fdSGeoff Levand 			"(%d)\n", __func__, __LINE__, result);
937a4eb7fdSGeoff Levand 		BUG_ON("check region type");
947a4eb7fdSGeoff Levand 		goto fail_dma_region;
957a4eb7fdSGeoff Levand 	}
967a4eb7fdSGeoff Levand 
976a6c957eSGeoff Levand 	result = ps3_mmio_region_create(dev->m_region);
986a6c957eSGeoff Levand 
996a6c957eSGeoff Levand 	if (result) {
1006a6c957eSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
1016a6c957eSGeoff Levand 			__func__, __LINE__);
1026a6c957eSGeoff Levand 		result = -EPERM;
1037a4eb7fdSGeoff Levand 		goto fail_mmio_region;
1046a6c957eSGeoff Levand 	}
1056a6c957eSGeoff Levand 
1066a6c957eSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
1076a6c957eSGeoff Levand 		__LINE__, dev->m_region->lpar_addr);
1086a6c957eSGeoff Levand 
109dc4f60c2SGeoff Levand 	result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);
1106a6c957eSGeoff Levand 
1116a6c957eSGeoff Levand 	if (result) {
1126a6c957eSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n",
1136a6c957eSGeoff Levand 			__func__, __LINE__, virq);
1146a6c957eSGeoff Levand 		result = -EPERM;
1156a6c957eSGeoff Levand 		goto fail_irq;
1166a6c957eSGeoff Levand 	}
1176a6c957eSGeoff Levand 
11848e91846SGeoff Levand 	dummy_mask = DMA_BIT_MASK(32);
11948e91846SGeoff Levand 	dev->core.dma_mask = &dummy_mask;
12048e91846SGeoff Levand 	dma_set_coherent_mask(&dev->core, dummy_mask);
1216a6c957eSGeoff Levand 
1227071a3ceSKay Sievers 	hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core));
1236a6c957eSGeoff Levand 
1246a6c957eSGeoff Levand 	if (!hcd) {
1256a6c957eSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
1266a6c957eSGeoff Levand 			__LINE__);
1276a6c957eSGeoff Levand 		result = -ENOMEM;
1286a6c957eSGeoff Levand 		goto fail_create_hcd;
1296a6c957eSGeoff Levand 	}
1306a6c957eSGeoff Levand 
1316a6c957eSGeoff Levand 	hcd->rsrc_start = dev->m_region->lpar_addr;
1326a6c957eSGeoff Levand 	hcd->rsrc_len = dev->m_region->len;
1337a4eb7fdSGeoff Levand 
1347a4eb7fdSGeoff Levand 	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
1357a4eb7fdSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
1367a4eb7fdSGeoff Levand 			__func__, __LINE__);
1377a4eb7fdSGeoff Levand 
1386a6c957eSGeoff Levand 	hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
1396a6c957eSGeoff Levand 
1406a6c957eSGeoff Levand 	if (!hcd->regs) {
1416a6c957eSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__,
1426a6c957eSGeoff Levand 			__LINE__);
1436a6c957eSGeoff Levand 		result = -EPERM;
1446a6c957eSGeoff Levand 		goto fail_ioremap;
1456a6c957eSGeoff Levand 	}
1466a6c957eSGeoff Levand 
1476a6c957eSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__,
1486a6c957eSGeoff Levand 		(unsigned long)hcd->rsrc_start);
1496a6c957eSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len   %lxh\n", __func__, __LINE__,
1506a6c957eSGeoff Levand 		(unsigned long)hcd->rsrc_len);
1516a6c957eSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: hcd->regs       %lxh\n", __func__, __LINE__,
1526a6c957eSGeoff Levand 		(unsigned long)hcd->regs);
1536a6c957eSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: virq            %lu\n", __func__, __LINE__,
1546a6c957eSGeoff Levand 		(unsigned long)virq);
1556a6c957eSGeoff Levand 
15603fa68c2SGeert Uytterhoeven 	ps3_system_bus_set_drvdata(dev, hcd);
1576a6c957eSGeoff Levand 
158b5dd18d8SYong Zhang 	result = usb_add_hcd(hcd, virq, 0);
1596a6c957eSGeoff Levand 
1606a6c957eSGeoff Levand 	if (result) {
1616a6c957eSGeoff Levand 		dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
1626a6c957eSGeoff Levand 			__func__, __LINE__, result);
1636a6c957eSGeoff Levand 		goto fail_add_hcd;
1646a6c957eSGeoff Levand 	}
1656a6c957eSGeoff Levand 
1663c9740a1SPeter Chen 	device_wakeup_enable(hcd->self.controller);
1676a6c957eSGeoff Levand 	return result;
1686a6c957eSGeoff Levand 
1696a6c957eSGeoff Levand fail_add_hcd:
1706a6c957eSGeoff Levand 	iounmap(hcd->regs);
1716a6c957eSGeoff Levand fail_ioremap:
1727a4eb7fdSGeoff Levand 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
1736a6c957eSGeoff Levand 	usb_put_hcd(hcd);
1746a6c957eSGeoff Levand fail_create_hcd:
175dc4f60c2SGeoff Levand 	ps3_io_irq_destroy(virq);
1766a6c957eSGeoff Levand fail_irq:
1776a6c957eSGeoff Levand 	ps3_free_mmio_region(dev->m_region);
1787a4eb7fdSGeoff Levand fail_mmio_region:
1797a4eb7fdSGeoff Levand 	ps3_dma_region_free(dev->d_region);
1807a4eb7fdSGeoff Levand fail_dma_region:
1817a4eb7fdSGeoff Levand 	ps3_close_hv_device(dev);
1827a4eb7fdSGeoff Levand fail_open:
1836a6c957eSGeoff Levand fail_start:
1846a6c957eSGeoff Levand 	return result;
1856a6c957eSGeoff Levand }
1866a6c957eSGeoff Levand 
ps3_ohci_remove(struct ps3_system_bus_device * dev)187*6d247e4dSUwe Kleine-König static void ps3_ohci_remove(struct ps3_system_bus_device *dev)
1886a6c957eSGeoff Levand {
1897a4eb7fdSGeoff Levand 	unsigned int tmp;
19003fa68c2SGeert Uytterhoeven 	struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
1916a6c957eSGeoff Levand 
1927a4eb7fdSGeoff Levand 	BUG_ON(!hcd);
1937a4eb7fdSGeoff Levand 
1947a4eb7fdSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
1957a4eb7fdSGeoff Levand 	dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
1967a4eb7fdSGeoff Levand 
1977a4eb7fdSGeoff Levand 	tmp = hcd->irq;
1987a4eb7fdSGeoff Levand 
199ddcb01ffSGeoff Levand 	ohci_shutdown(hcd);
2007a4eb7fdSGeoff Levand 	usb_remove_hcd(hcd);
2017a4eb7fdSGeoff Levand 
20203fa68c2SGeert Uytterhoeven 	ps3_system_bus_set_drvdata(dev, NULL);
2036a6c957eSGeoff Levand 
2047a4eb7fdSGeoff Levand 	BUG_ON(!hcd->regs);
2057a4eb7fdSGeoff Levand 	iounmap(hcd->regs);
2067a4eb7fdSGeoff Levand 
2077a4eb7fdSGeoff Levand 	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
2087a4eb7fdSGeoff Levand 	usb_put_hcd(hcd);
2097a4eb7fdSGeoff Levand 
2107a4eb7fdSGeoff Levand 	ps3_io_irq_destroy(tmp);
2117a4eb7fdSGeoff Levand 	ps3_free_mmio_region(dev->m_region);
2127a4eb7fdSGeoff Levand 
2137a4eb7fdSGeoff Levand 	ps3_dma_region_free(dev->d_region);
2147a4eb7fdSGeoff Levand 	ps3_close_hv_device(dev);
2156a6c957eSGeoff Levand }
2166a6c957eSGeoff Levand 
ps3_ohci_driver_register(struct ps3_system_bus_driver * drv)21731348517SGeert Uytterhoeven static int __init ps3_ohci_driver_register(struct ps3_system_bus_driver *drv)
2187a4eb7fdSGeoff Levand {
2197a4eb7fdSGeoff Levand 	return firmware_has_feature(FW_FEATURE_PS3_LV1)
2207a4eb7fdSGeoff Levand 		? ps3_system_bus_driver_register(drv)
2217a4eb7fdSGeoff Levand 		: 0;
2227a4eb7fdSGeoff Levand }
2236a6c957eSGeoff Levand 
ps3_ohci_driver_unregister(struct ps3_system_bus_driver * drv)2247a4eb7fdSGeoff Levand static void ps3_ohci_driver_unregister(struct ps3_system_bus_driver *drv)
2257a4eb7fdSGeoff Levand {
2267a4eb7fdSGeoff Levand 	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
2277a4eb7fdSGeoff Levand 		ps3_system_bus_driver_unregister(drv);
2287a4eb7fdSGeoff Levand }
2297a4eb7fdSGeoff Levand 
2307a4eb7fdSGeoff Levand MODULE_ALIAS(PS3_MODULE_ALIAS_OHCI);
2317a4eb7fdSGeoff Levand 
2327a4eb7fdSGeoff Levand static struct ps3_system_bus_driver ps3_ohci_driver = {
2337a4eb7fdSGeoff Levand 	.core.name = "ps3-ohci-driver",
2347a4eb7fdSGeoff Levand 	.core.owner = THIS_MODULE,
2356a6c957eSGeoff Levand 	.match_id = PS3_MATCH_ID_OHCI,
2367a4eb7fdSGeoff Levand 	.probe = ps3_ohci_probe,
2377a4eb7fdSGeoff Levand 	.remove = ps3_ohci_remove,
2387a4eb7fdSGeoff Levand 	.shutdown = ps3_ohci_remove,
2396a6c957eSGeoff Levand };
240