162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  PS3 EHCI Host Controller driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *  Copyright (C) 2006 Sony Computer Entertainment Inc.
662306a36Sopenharmony_ci *  Copyright 2006 Sony Corp.
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <asm/firmware.h>
1062306a36Sopenharmony_ci#include <asm/ps3.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_cistatic void ps3_ehci_setup_insnreg(struct ehci_hcd *ehci)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	/* PS3 HC internal setup register offsets. */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci	enum ps3_ehci_hc_insnreg {
1762306a36Sopenharmony_ci		ps3_ehci_hc_insnreg01 = 0x084,
1862306a36Sopenharmony_ci		ps3_ehci_hc_insnreg02 = 0x088,
1962306a36Sopenharmony_ci		ps3_ehci_hc_insnreg03 = 0x08c,
2062306a36Sopenharmony_ci	};
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	/* PS3 EHCI HC errata fix 316 - The PS3 EHCI HC will reset its
2362306a36Sopenharmony_ci	 * internal INSNREGXX setup regs back to the chip default values
2462306a36Sopenharmony_ci	 * on Host Controller Reset (CMD_RESET) or Light Host Controller
2562306a36Sopenharmony_ci	 * Reset (CMD_LRESET).  The work-around for this is for the HC
2662306a36Sopenharmony_ci	 * driver to re-initialise these regs when ever the HC is reset.
2762306a36Sopenharmony_ci	 */
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci	/* Set burst transfer counts to 256 out, 32 in. */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	writel_be(0x01000020, (void __iomem *)ehci->regs +
3262306a36Sopenharmony_ci		ps3_ehci_hc_insnreg01);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* Enable burst transfer counts. */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	writel_be(0x00000001, (void __iomem *)ehci->regs +
3762306a36Sopenharmony_ci		ps3_ehci_hc_insnreg03);
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic int ps3_ehci_hc_reset(struct usb_hcd *hcd)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	int result;
4362306a36Sopenharmony_ci	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ehci->big_endian_mmio = 1;
4662306a36Sopenharmony_ci	ehci->caps = hcd->regs;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	result = ehci_setup(hcd);
4962306a36Sopenharmony_ci	if (result)
5062306a36Sopenharmony_ci		return result;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	ps3_ehci_setup_insnreg(ehci);
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	return result;
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cistatic const struct hc_driver ps3_ehci_hc_driver = {
5862306a36Sopenharmony_ci	.description		= hcd_name,
5962306a36Sopenharmony_ci	.product_desc		= "PS3 EHCI Host Controller",
6062306a36Sopenharmony_ci	.hcd_priv_size		= sizeof(struct ehci_hcd),
6162306a36Sopenharmony_ci	.irq			= ehci_irq,
6262306a36Sopenharmony_ci	.flags			= HCD_MEMORY | HCD_DMA | HCD_USB2 | HCD_BH,
6362306a36Sopenharmony_ci	.reset			= ps3_ehci_hc_reset,
6462306a36Sopenharmony_ci	.start			= ehci_run,
6562306a36Sopenharmony_ci	.stop			= ehci_stop,
6662306a36Sopenharmony_ci	.shutdown		= ehci_shutdown,
6762306a36Sopenharmony_ci	.urb_enqueue		= ehci_urb_enqueue,
6862306a36Sopenharmony_ci	.urb_dequeue		= ehci_urb_dequeue,
6962306a36Sopenharmony_ci	.endpoint_disable	= ehci_endpoint_disable,
7062306a36Sopenharmony_ci	.endpoint_reset		= ehci_endpoint_reset,
7162306a36Sopenharmony_ci	.get_frame_number	= ehci_get_frame,
7262306a36Sopenharmony_ci	.hub_status_data	= ehci_hub_status_data,
7362306a36Sopenharmony_ci	.hub_control		= ehci_hub_control,
7462306a36Sopenharmony_ci#if defined(CONFIG_PM)
7562306a36Sopenharmony_ci	.bus_suspend		= ehci_bus_suspend,
7662306a36Sopenharmony_ci	.bus_resume		= ehci_bus_resume,
7762306a36Sopenharmony_ci#endif
7862306a36Sopenharmony_ci	.relinquish_port	= ehci_relinquish_port,
7962306a36Sopenharmony_ci	.port_handed_over	= ehci_port_handed_over,
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	.clear_tt_buffer_complete	= ehci_clear_tt_buffer_complete,
8262306a36Sopenharmony_ci};
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic int ps3_ehci_probe(struct ps3_system_bus_device *dev)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	int result;
8762306a36Sopenharmony_ci	struct usb_hcd *hcd;
8862306a36Sopenharmony_ci	unsigned int virq;
8962306a36Sopenharmony_ci	static u64 dummy_mask;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	if (usb_disabled()) {
9262306a36Sopenharmony_ci		result = -ENODEV;
9362306a36Sopenharmony_ci		goto fail_start;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	result = ps3_open_hv_device(dev);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (result) {
9962306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: ps3_open_hv_device failed\n",
10062306a36Sopenharmony_ci			__func__, __LINE__);
10162306a36Sopenharmony_ci		goto fail_open;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	result = ps3_dma_region_create(dev->d_region);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (result) {
10762306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: ps3_dma_region_create failed: "
10862306a36Sopenharmony_ci			"(%d)\n", __func__, __LINE__, result);
10962306a36Sopenharmony_ci		BUG_ON("check region type");
11062306a36Sopenharmony_ci		goto fail_dma_region;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	result = ps3_mmio_region_create(dev->m_region);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	if (result) {
11662306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: ps3_map_mmio_region failed\n",
11762306a36Sopenharmony_ci			__func__, __LINE__);
11862306a36Sopenharmony_ci		result = -EPERM;
11962306a36Sopenharmony_ci		goto fail_mmio_region;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: mmio mapped_addr %lxh\n", __func__,
12362306a36Sopenharmony_ci		__LINE__, dev->m_region->lpar_addr);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	result = ps3_io_irq_setup(PS3_BINDING_CPU_ANY, dev->interrupt_id, &virq);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (result) {
12862306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: ps3_construct_io_irq(%d) failed.\n",
12962306a36Sopenharmony_ci			__func__, __LINE__, virq);
13062306a36Sopenharmony_ci		result = -EPERM;
13162306a36Sopenharmony_ci		goto fail_irq;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	dummy_mask = DMA_BIT_MASK(32);
13562306a36Sopenharmony_ci	dev->core.dma_mask = &dummy_mask;
13662306a36Sopenharmony_ci	dma_set_coherent_mask(&dev->core, dummy_mask);
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core));
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	if (!hcd) {
14162306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,
14262306a36Sopenharmony_ci			__LINE__);
14362306a36Sopenharmony_ci		result = -ENOMEM;
14462306a36Sopenharmony_ci		goto fail_create_hcd;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	hcd->rsrc_start = dev->m_region->lpar_addr;
14862306a36Sopenharmony_ci	hcd->rsrc_len = dev->m_region->len;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name))
15162306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: request_mem_region failed\n",
15262306a36Sopenharmony_ci			__func__, __LINE__);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	hcd->regs = ioremap(dev->m_region->lpar_addr, dev->m_region->len);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (!hcd->regs) {
15762306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: ioremap failed\n", __func__,
15862306a36Sopenharmony_ci			__LINE__);
15962306a36Sopenharmony_ci		result = -EPERM;
16062306a36Sopenharmony_ci		goto fail_ioremap;
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: hcd->rsrc_start %lxh\n", __func__, __LINE__,
16462306a36Sopenharmony_ci		(unsigned long)hcd->rsrc_start);
16562306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: hcd->rsrc_len   %lxh\n", __func__, __LINE__,
16662306a36Sopenharmony_ci		(unsigned long)hcd->rsrc_len);
16762306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: hcd->regs       %lxh\n", __func__, __LINE__,
16862306a36Sopenharmony_ci		(unsigned long)hcd->regs);
16962306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: virq            %lu\n", __func__, __LINE__,
17062306a36Sopenharmony_ci		(unsigned long)virq);
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	ps3_system_bus_set_drvdata(dev, hcd);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	result = usb_add_hcd(hcd, virq, 0);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (result) {
17762306a36Sopenharmony_ci		dev_dbg(&dev->core, "%s:%d: usb_add_hcd failed (%d)\n",
17862306a36Sopenharmony_ci			__func__, __LINE__, result);
17962306a36Sopenharmony_ci		goto fail_add_hcd;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	device_wakeup_enable(hcd->self.controller);
18362306a36Sopenharmony_ci	return result;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cifail_add_hcd:
18662306a36Sopenharmony_ci	iounmap(hcd->regs);
18762306a36Sopenharmony_cifail_ioremap:
18862306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
18962306a36Sopenharmony_ci	usb_put_hcd(hcd);
19062306a36Sopenharmony_cifail_create_hcd:
19162306a36Sopenharmony_ci	ps3_io_irq_destroy(virq);
19262306a36Sopenharmony_cifail_irq:
19362306a36Sopenharmony_ci	ps3_free_mmio_region(dev->m_region);
19462306a36Sopenharmony_cifail_mmio_region:
19562306a36Sopenharmony_ci	ps3_dma_region_free(dev->d_region);
19662306a36Sopenharmony_cifail_dma_region:
19762306a36Sopenharmony_ci	ps3_close_hv_device(dev);
19862306a36Sopenharmony_cifail_open:
19962306a36Sopenharmony_cifail_start:
20062306a36Sopenharmony_ci	return result;
20162306a36Sopenharmony_ci}
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_cistatic void ps3_ehci_remove(struct ps3_system_bus_device *dev)
20462306a36Sopenharmony_ci{
20562306a36Sopenharmony_ci	unsigned int tmp;
20662306a36Sopenharmony_ci	struct usb_hcd *hcd = ps3_system_bus_get_drvdata(dev);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	BUG_ON(!hcd);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: regs %p\n", __func__, __LINE__, hcd->regs);
21162306a36Sopenharmony_ci	dev_dbg(&dev->core, "%s:%d: irq %u\n", __func__, __LINE__, hcd->irq);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	tmp = hcd->irq;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	usb_remove_hcd(hcd);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	ps3_system_bus_set_drvdata(dev, NULL);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	BUG_ON(!hcd->regs);
22062306a36Sopenharmony_ci	iounmap(hcd->regs);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
22362306a36Sopenharmony_ci	usb_put_hcd(hcd);
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	ps3_io_irq_destroy(tmp);
22662306a36Sopenharmony_ci	ps3_free_mmio_region(dev->m_region);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	ps3_dma_region_free(dev->d_region);
22962306a36Sopenharmony_ci	ps3_close_hv_device(dev);
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_cistatic int __init ps3_ehci_driver_register(struct ps3_system_bus_driver *drv)
23362306a36Sopenharmony_ci{
23462306a36Sopenharmony_ci	return firmware_has_feature(FW_FEATURE_PS3_LV1)
23562306a36Sopenharmony_ci		? ps3_system_bus_driver_register(drv)
23662306a36Sopenharmony_ci		: 0;
23762306a36Sopenharmony_ci}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_cistatic void ps3_ehci_driver_unregister(struct ps3_system_bus_driver *drv)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	if (firmware_has_feature(FW_FEATURE_PS3_LV1))
24262306a36Sopenharmony_ci		ps3_system_bus_driver_unregister(drv);
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ciMODULE_ALIAS(PS3_MODULE_ALIAS_EHCI);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_cistatic struct ps3_system_bus_driver ps3_ehci_driver = {
24862306a36Sopenharmony_ci	.core.name = "ps3-ehci-driver",
24962306a36Sopenharmony_ci	.core.owner = THIS_MODULE,
25062306a36Sopenharmony_ci	.match_id = PS3_MATCH_ID_EHCI,
25162306a36Sopenharmony_ci	.probe = ps3_ehci_probe,
25262306a36Sopenharmony_ci	.remove = ps3_ehci_remove,
25362306a36Sopenharmony_ci	.shutdown = ps3_ehci_remove,
25462306a36Sopenharmony_ci};
255