Hello USB maintainers, I'm porting kernel support for the Sony PlayStation 2 from v2.6.35 to v4.14, and a part of this effort is to port the PS2 OHCI driver. USB keyboards seem to work quite well with v4.14, but USB mass storage devices cause kernel crashes. Any help in finding the cause of this crash, as well as comments on how to improve this provisional driver are very much welcome. The crash trace is: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 34 at drivers/usb/core/hcd.c:1390 hcd_alloc_coherent+0x4c/0xc8 Modules linked in: CPU: 0 PID: 34 Comm: usb-storage Not tainted 4.14.0+ #718 Stack : 00000000 80402608 00000003 0000000b 805b3b98 805611c4 00000094 80062b8c 00000009 0000056e 81e9fc00 805b0000 00000094 10018c00 81f51b10 00000001 00000000 00000000 805b0000 00000001 0000000a 00000003 00000001 3a555043 80436790 0000000f 81f519c8 81f51990 00000000 00000000 802e544c 80517464 00000009 0000056e 81e9fc00 805b0000 00000003 ffffee7f 00000000 805b0000 ... Call Trace: [<80021104>] show_stack+0x74/0x104 [<80036310>] __warn+0x114/0x11c [<800363ac>] warn_slowpath_null+0x1c/0x30 [<802e544c>] hcd_alloc_coherent+0x4c/0xc8 [<802e6160>] usb_hcd_map_urb_for_dma+0x524/0x580 [<802e729c>] usb_hcd_submit_urb+0x7d8/0x7e0 [<802e95d8>] usb_sg_wait+0x14c/0x1a0 [<802fb0f0>] usb_stor_bulk_transfer_sglist.part.1+0xac/0x124 [<802fb4b4>] usb_stor_bulk_srb+0x40/0x60 [<802fb8a8>] usb_stor_Bulk_transport+0x160/0x37c [<802fbcec>] usb_stor_invoke_transport+0x3c/0x500 [<802fd220>] usb_stor_control_thread+0x260/0x2a0 [<8004ef64>] kthread+0x138/0x140 [<8001b578>] ret_from_kernel_thread+0x14/0x1c ---[ end trace 2be087478be6627e ]--- This particular WARN_ON_ONCE is explained here: commit 4307a28eb0128417d9a2b9d858d2bce70ee5b383 Author: Andrea Righi <arighi@xxxxxxxxxxx> Date: Mon Jun 28 16:56:45 2010 +0200 USB: EHCI: fix NULL pointer dererence in HCDs that use HCD_LOCAL_MEM If we use the HCD_LOCAL_MEM flag and dma_declare_coherent_memory() to enforce the host controller's local memory utilization we also need to disable native scatter-gather support, otherwise hcd_alloc_coherent() in map_urb_for_dma() is called with urb->transfer_buffer == NULL, that triggers a NULL pointer dereference. We can also consider to add a WARN_ON() and return an error code to better catch this problem in the future. At the moment no driver seems to hit this bug, so I should consider this a low-priority fix. Signed-off-by: Andrea Righi <arighi@xxxxxxxxxxx> Acked-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx> Signed-off-by: Greg Kroah-Hartman <gregkh@xxxxxxx> I've tried to disable scatter-gather, as suggested, by: diff --git a/drivers/usb/host/ohci-ps2.c b/drivers/usb/host/ohci-ps2.c index 066d6a9c7ccb..eaa099aa0740 100755 --- a/drivers/usb/host/ohci-ps2.c +++ b/drivers/usb/host/ohci-ps2.c @@ -25,6 +25,9 @@ static int ohci_ps2_start(struct usb_hcd *hcd) ohci_hcd_init(ohci); ohci_init(ohci); + + hcd->self.sg_tablesize = 0; + ohci_run(ohci); hcd->state = HC_STATE_RUNNING; return 0; 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 ]--- Please find the (provisional) patch for the PS2 OHCI driver below. Fredrik diff --git a/arch/mips/ps2/setup.c b/arch/mips/ps2/setup.c index 8f136f0495ab..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,14 +91,29 @@ 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(); ps2sif_init(); ps2rtc_init(); +#ifdef CONFIG_SYSFS_IOP_MODULES + ps2_loadfile_init(); +#endif ps2_powerbutton_init(); + if (load_module_firmware("ps2/ps2dev9.irx", 0) < 0) + pr_err("loading ps2/ps2dev9.irx failed\n"); + + 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/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 44924824fa41..f178b1a8bca8 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; @@ -1245,6 +1254,11 @@ MODULE_LICENSE ("GPL"); #define PLATFORM_DRIVER ohci_hcd_tilegx_driver #endif +#if defined(CONFIG_SONY_PS2) +#include "ohci-ps2.c" +#define PLATFORM_DRIVER ohci_hcd_ps2_driver +#endif + static int __init ohci_hcd_mod_init(void) { int retval = 0; diff --git a/drivers/usb/host/ohci-ps2.c b/drivers/usb/host/ohci-ps2.c new file mode 100755 index 000000000000..066d6a9c7ccb --- /dev/null +++ b/drivers/usb/host/ohci-ps2.c @@ -0,0 +1,178 @@ +/* + * USB OHCI HCD (Host Controller Driver) for PlayStation 2. + * + * Copyright (C) 2010 Jürgen Urban + * + * 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/platform_device.h> + +#include <asm/mach-ps2/sifdefs.h> + +/* Size of buffer allocated from IOP heap. */ +#define DMA_BUFFER_SIZE (256 * 1024) + +#define ERROR(args...) printk(KERN_ERR "ohci-ps: " args); + +static dma_addr_t iop_dma_addr = 0; + +static int ohci_ps2_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + ohci_hcd_init(ohci); + ohci_init(ohci); + ohci_run(ohci); + hcd->state = HC_STATE_RUNNING; + return 0; +} + +static const struct hc_driver ohci_ps2_hc_driver = { + .description = hcd_name, + .product_desc = "PS2 OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY | HCD_LOCAL_MEM, + + /* + * basic lifecycle operations + */ + .start = ohci_ps2_start, + .stop = ohci_stop, + .shutdown = ohci_shutdown, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +#ifdef CONFIG_PM + .bus_suspend = ohci_bus_suspend, + .bus_resume = ohci_bus_resume, +#endif + .start_port_reset = ohci_start_port_reset, +}; + +/*-------------------------------------------------------------------------*/ + +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) { + if (dma_declare_coherent_memory(dev, ps2sif_bustophys(iop_dma_addr), + iop_dma_addr, size, flags)) { + ERROR("cannot declare coherent memory\n"); + ps2sif_freeiopheap(iop_dma_addr); + return -ENXIO; + } + return 0; + } else { + ERROR("Out of IOP heap memory.\n"); + return -ENOMEM; + } +} + +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; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) +static int ohci_hcd_ps2_probe(struct platform_device *pdev) +{ + struct resource *res = NULL; + struct usb_hcd *hcd = NULL; + int irq = -1; + int ret; + + if (usb_disabled()) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ERROR("platform_get_resource error."); + return -ENODEV; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ERROR("platform_get_irq error."); + return -ENODEV; + } + ret = iopheap_alloc_coherent(&pdev->dev, DMA_BUFFER_SIZE, DMA_MEMORY_EXCLUSIVE); + if (ret) { + return ret; + } + + /* initialize hcd */ + hcd = usb_create_hcd(&ohci_ps2_hc_driver, &pdev->dev, (char *)hcd_name); + if (!hcd) { + ERROR("Failed to create hcd"); + return -ENOMEM; + } + + hcd->regs = (void __iomem *)res->start; + hcd->rsrc_start = res->start; + hcd->rsrc_len = resource_len(res); + ret = usb_add_hcd(hcd, irq, 0); + if (ret != 0) { + ERROR("Failed to add hcd"); + usb_put_hcd(hcd); + return ret; + } + + 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", + .owner = THIS_MODULE, + }, +}; + +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