Re: [RFC v2] Add support for Sony PS2 OHCI

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thank you for your review, Alan Stern,

> > With the patch above, the kernel successfully detects USB mass storage
> > devices. However, it also causes another crash:
> > 
> > 	------------[ cut here ]------------
> > 	WARNING: CPU: 0 PID: 15 at ./include/linux/dma-mapping.h:530 hcd_buffer_free+0x130/0x200
> > 	Modules linked in:
> > 	CPU: 0 PID: 15 Comm: kworker/0:1 Not tainted 4.14.0+ #719
> > 	Workqueue: usb_hub_wq hub_event
> > 	Stack : 81f3b5b8 80402608 00000003 0000000b 000000f6 805611c4 81ccc280 804f398c
> > 	        802ee5e0 804fd520 00000009 00000212 00000001 10058400 81c09a38 00000001
> > 	        00000000 00000000 00000000 00000003 0000000a 00000002 00000001 00000000
> > 	        00000001 00003368 81c09878 81c09840 00000000 00000000 802ee5e0 804fd520
> > 	        00000009 00000212 00000001 fffffff3 00000002 00000000 00000000 805b0000
> > 	        ...
> > 	Call Trace:
> > 	[<80021104>] show_stack+0x74/0x104
> > 	[<80036310>] __warn+0x114/0x11c
> > 	[<800363ac>] warn_slowpath_null+0x1c/0x30
> > 	[<802ee5e0>] hcd_buffer_free+0x130/0x200
> > 	[<802e56ec>] usb_hcd_unmap_urb_for_dma+0x160/0x16c
> > 	[<802e576c>] __usb_hcd_giveback_urb+0x54/0xf0
> > 	[<802f5d64>] finish_urb+0x98/0x138
> > 	[<802f716c>] ohci_work+0x14c/0x494
> > 	[<802f9878>] ohci_irq+0x13c/0x2e4
> > 	[<802e4e0c>] usb_hcd_irq+0x2c/0x44
> > 	[<8006366c>] __handle_irq_event_percpu+0x98/0x158
> > 	[<8006374c>] handle_irq_event_percpu+0x20/0x64
> > 	[<800637cc>] handle_irq_event+0x3c/0x70
> > 	[<80067024>] handle_level_irq+0xe4/0x120
> > 	[<80062d70>] generic_handle_irq+0x28/0x38
> > 	[<80409b58>] do_IRQ+0x18/0x24
> > 	---[ end trace fa1f0201799de649 ]---
> 
> This is caused by a deficiency in the DMA core: dma_free_coherent()  
> wants interrupts to be enabled when it is called.  I'm not sure how the
> other host controller drivers cope with this.

The OHCI drivers

	drivers/usb/host/ohci-sm501.c and
	drivers/usb/host/ohci-tmio.c

appear to be very similar to my driver. They both use HCD_LOCAL_MEM and
dma_declare_coherent_memory(). Curiously, though, they don't seem to disable
scatter-gather, or handle DMA in any special way, which I think is a bit odd.

I wonder how they could possibly avoid these two DMA crashes? (Especially
given the explicit WARN_ON_ONCE note in commit 4307a28eb012 "USB: EHCI: fix
NULL pointer dererence in HCDs that use HCD_LOCAL_MEM".)

One notable difference is that my driver does

	hcd->regs = (void __iomem *)res->start;

where the other drivers use ioremap such that

	hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);

in the OHCI HCD probe function. (The hardware address logic is somewhat
involved and partially undocumented so there might be a good reason for this
difference in my driver.)

> Be aware that your driver should utilize ohci_init_driver(), like
> several of the other platform-specific OHCI drivers do.  Unless there's
> some very good reason, new drivers should never use the old interface.

Agreed, please find updated patch with the new interface. (I suppose the
changes to drivers/usb/host/ohci-hcd.c eventually will have to be clarified
and moved elsewhere too.)

Fredrik

diff --git a/arch/mips/ps2/setup.c b/arch/mips/ps2/setup.c
index d9957711283e..d9caceacfaac 100644
--- a/arch/mips/ps2/setup.c
+++ b/arch/mips/ps2/setup.c
@@ -45,6 +45,29 @@ const char *get_system_type(void)
 	return "Sony PlayStation 2";
 }
 
+#define IOP_REG_BASE	0xbf801460
+#define IOP_USB_BASE	(IOP_REG_BASE + 0x1a0)
+
+static struct resource ps2_usb_ohci_resources[] = {
+	[0] = {
+		.start	= IOP_USB_BASE,
+		.end	= IOP_USB_BASE + 0xff,
+		.flags	= IORESOURCE_MEM, /* 256 byte HCCA */
+	},
+	[1] = {
+		.start	= IRQ_SBUS_USB,
+		.end	= IRQ_SBUS_USB,
+		.flags	= IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device ps2_usb_ohci_device = {
+	.name		= "ps2_ohci",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(ps2_usb_ohci_resources),
+	.resource	= ps2_usb_ohci_resources,
+};
+
 void __init plat_mem_setup(void)
 {
 	ps2_reset_init();
@@ -68,6 +91,10 @@ void __init plat_mem_setup(void)
 	set_io_port_base(CKSEG1);
 }
 
+static struct platform_device *ps2_platform_devices[] __initdata = {
+	&ps2_usb_ohci_device,
+};
+
 static int __init ps2_board_setup(void)
 {
 	ps2dma_init();
@@ -85,6 +112,8 @@ static int __init ps2_board_setup(void)
 	if (load_module_firmware("ps2/dev9_dma.irx", 0) < 0)
 		pr_err("loading ps2/dev9_dma.irx failed\n");
 
+	platform_add_devices(ps2_platform_devices, ARRAY_SIZE(ps2_platform_devices));
+
 	return 0;
 }
 arch_initcall(ps2_board_setup);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index fa5692dec832..0f74d3420066 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -571,6 +571,13 @@ config USB_OHCI_EXYNOS
 	help
 	 Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
 
+config USB_OHCI_HCD_PS2
+	tristate "OHCI support for the Sony PlayStation 2"
+	depends on SONY_PS2
+	default y
+	help
+	 Enable support for the Sony PlayStation 2 OHCI controller.
+
 config USB_CNS3XXX_OHCI
 	bool "Cavium CNS3XXX OHCI Module (DEPRECATED)"
 	depends on ARCH_CNS3XXX
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index 4ab2689c8952..2c6546fb2de6 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_USB_OHCI_HCD_S3C2410)	+= ohci-s3c2410.o
 obj-$(CONFIG_USB_OHCI_HCD_LPC32XX)	+= ohci-nxp.o
 obj-$(CONFIG_USB_OHCI_HCD_PXA27X)	+= ohci-pxa27x.o
 obj-$(CONFIG_USB_OHCI_HCD_DAVINCI)	+= ohci-da8xx.o
+obj-$(CONFIG_USB_OHCI_HCD_PS2)	+= ohci-ps2.o
 
 obj-$(CONFIG_USB_UHCI_HCD)	+= uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)	+= fhci.o
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 44924824fa41..485ded7469ac 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -590,6 +590,11 @@ static int ohci_run (struct ohci_hcd *ohci)
 		}
 		udelay (1);
 	}
+#ifdef CONFIG_SONY_PS2
+	/* Enable USB. Leave PS2DEV enabled. */
+	outl(inl(0x1F801570) | 0x08000080, 0x1F801570);
+	outl(1, 0x1F801680);
+#endif
 
 	/* now we're in the SUSPEND state ... must go OPERATIONAL
 	 * within 2msec else HC enters RESUME
@@ -875,6 +880,10 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd)
 	/* We only care about interrupts that are enabled */
 	ints &= ohci_readl(ohci, &regs->intrenable);
 
+#ifdef CONFIG_SONY_PS2
+	ohci_writel(ohci, OHCI_INTR_MIE, &regs->intrdisable);
+#endif
+
 	/* interrupt for some other device? */
 	if (ints == 0 || unlikely(ohci->rh_state == OHCI_RH_HALTED))
 		return IRQ_NOTMINE;
diff --git a/drivers/usb/host/ohci-ps2.c b/drivers/usb/host/ohci-ps2.c
new file mode 100755
index 000000000000..c160a1649500
--- /dev/null
+++ b/drivers/usb/host/ohci-ps2.c
@@ -0,0 +1,183 @@
+/*
+ * USB OHCI HCD (Host Controller Driver) for the PlayStation 2.
+ *
+ * Copyright (C) 2010 Jürgen Urban
+ * Copyright (C) 2017 Fredrik Noring
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include <asm/mach-ps2/sifdefs.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI PS2 driver"
+
+#define DMA_BUFFER_SIZE (256 * 1024) /* Size allocated from IOP heap. */
+
+static dma_addr_t iop_dma_addr = 0;
+
+static const char hcd_name[] = "ohci-ps2";
+static struct hc_driver __read_mostly ohci_ps2_hc_driver;
+
+static int ohci_ps2_reset(struct usb_hcd *hcd)
+{
+	const int ret = ohci_setup(hcd);
+
+	/*
+	 * Native scatter-gather support needs to be disabled since
+	 * HCD_LOCAL_MEM and dma_declare_coherent_memory() are used
+	 * to enforce the host controller's local memory utilization,
+	 * otherwise hcd_alloc_coherent() in map_urb_for_dma() is
+	 * called with urb->transfer_buffer == NULL, that triggers a
+	 * NULL pointer dereference.
+	 */
+	hcd->self.sg_tablesize = 0;
+
+	return ret;
+}
+
+static int iopheap_alloc_coherent(struct device *dev, size_t size, int flags)
+{
+	if (iop_dma_addr != 0)
+		return 0;
+
+	iop_dma_addr = ps2sif_allociopheap(size);
+	if (iop_dma_addr == 0) {
+		dev_err(dev, "ps2sif_allociopheap failed\n");
+		return -ENOMEM;
+	}
+
+	if (dma_declare_coherent_memory(dev, ps2sif_bustophys(iop_dma_addr),
+					iop_dma_addr, size, flags)) {
+		dev_err(dev, "dma_declare_coherent_memory failed\n");
+		ps2sif_freeiopheap(iop_dma_addr);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void iopheap_free_coherent(struct device *dev)
+{
+	if (iop_dma_addr == 0)
+		return;
+
+	dma_release_declared_memory(dev);
+	ps2sif_freeiopheap(iop_dma_addr);
+	iop_dma_addr = 0;
+}
+
+static int ohci_hcd_ps2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct usb_hcd *hcd;
+	int irq;
+	int ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "platform_get_irq failed\n");
+		return irq;
+	}
+
+	hcd = usb_create_hcd(&ohci_ps2_hc_driver, dev, hcd_name);
+	if (hcd == NULL)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (res == NULL) {
+		dev_err(dev, "platform_get_resource failed\n");
+		ret = -ENOENT;
+		goto err;
+	}
+	hcd->rsrc_start = res->start;
+	hcd->rsrc_len = resource_size(res);
+	hcd->regs = (void __iomem *)res->start;
+	if (IS_ERR(hcd->regs)) {
+		ret = PTR_ERR(hcd->regs);
+		goto err;
+	}
+
+	ret = iopheap_alloc_coherent(dev, DMA_BUFFER_SIZE, DMA_MEMORY_EXCLUSIVE);
+	if (ret != 0)
+		goto err;
+
+	ret = usb_add_hcd(hcd, irq, 0);
+	if (ret != 0)
+		goto err_free;
+
+	ret = device_wakeup_enable(hcd->self.controller);
+	if (ret != 0)
+		goto err_remove_hcd;
+
+	return ret;
+
+err_remove_hcd:
+	usb_remove_hcd(hcd);
+err_free:
+	iopheap_free_coherent(dev);
+err:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static int ohci_hcd_ps2_remove(struct platform_device *pdev)
+{
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+	usb_remove_hcd(hcd);
+	usb_put_hcd(hcd);
+
+	iopheap_free_coherent(&pdev->dev);
+
+	return 0;
+}
+
+static struct platform_driver ohci_hcd_ps2_driver = {
+	.probe		= ohci_hcd_ps2_probe,
+	.remove		= ohci_hcd_ps2_remove,
+	.shutdown	= usb_hcd_platform_shutdown,
+	.driver		= {
+		.name	= "ps2_ohci",
+	},
+};
+
+static const struct ohci_driver_overrides ps2_overrides __initconst = {
+	.reset		= ohci_ps2_reset,
+	.product_desc	= "PS2 OHCI",
+};
+
+static int __init ohci_ps2_init(void)
+{
+	if (usb_disabled())
+		return -ENODEV;
+
+	pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+	ohci_init_driver(&ohci_ps2_hc_driver, &ps2_overrides);
+	ohci_ps2_hc_driver.flags |= HCD_LOCAL_MEM;
+
+	return platform_driver_register(&ohci_hcd_ps2_driver);
+}
+module_init(ohci_ps2_init);
+
+static void __exit ohci_ps2_cleanup(void)
+{
+	platform_driver_unregister(&ohci_hcd_ps2_driver);
+}
+module_exit(ohci_ps2_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ps2_ohci");
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux