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 [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to