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, ®s->intrenable); +#ifdef CONFIG_SONY_PS2 + ohci_writel(ohci, OHCI_INTR_MIE, ®s->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