From: Albert H. <he...@us...> - 2009-12-09 18:47:55
|
Update of /cvsroot/gc-linux/linux/drivers/usb/host In directory sfp-cvsdas-2.v30.ch3.sourceforge.com:/tmp/cvs-serv23815/drivers/usb/host Added Files: ehci-hlwd.c ohci-hlwd.c Log Message: Add new files for gc-linux-2.6.32. --- NEW FILE: ehci-hlwd.c --- /* * drivers/usb/host/ehci-hlwd.c * * Nintendo Wii (Hollywood) USB Enhanced Host Controller Interface. * Copyright (C) 2009 The GameCube Linux Team * Copyright (C) 2009 Albert Herranz * * 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; either version 2 of the License, or (at * your option) any later version. * * Based on ehci-ppc-of.c * * EHCI HCD (Host Controller Driver) for USB. * * Bus Glue for PPC On-Chip EHCI driver on the of_platform bus * Tested on AMCC PPC 440EPx * * Valentine Barshak <vba...@ru...> * * Based on "ehci-ppc-soc.c" by Stefan Roese <sr...@de...> * and "ohci-ppc-of.c" by Sylvain Munaut <tn...@24...> * * This file is licenced under the GPL. */ #include <linux/signal.h> #include <linux/of.h> #include <linux/of_platform.h> #include <asm/starlet.h> #define DRV_MODULE_NAME "ehci-hlwd" #define DRV_DESCRIPTION "Nintendo Wii EHCI Host Controller" #define DRV_AUTHOR "Albert Herranz" #define HLWD_EHCI_CTL 0x0d0400cc #define HLWD_EHCI_CTL_INTE (1<<15) /* called during probe() after chip reset completes */ static int ehci_hlwd_reset(struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci(hcd); void __iomem *ehci_ctl; int error; dbg_hcs_params(ehci, "reset"); dbg_hcc_params(ehci, "reset"); error = ehci_halt(ehci); if (error) goto out; error = ehci_init(hcd); if (error) goto out; ehci_ctl = ioremap(HLWD_EHCI_CTL, 4); if (!ehci_ctl) { printk(KERN_ERR __FILE__ ": ioremap failed\n"); error = -EBUSY; goto out; } /* enable notification of EHCI interrupts */ out_be32(ehci_ctl, in_be32(ehci_ctl) | HLWD_EHCI_CTL_INTE); iounmap(ehci_ctl); ehci->sbrn = 0x20; error = ehci_reset(ehci); ehci_port_power(ehci, 0); out: return error; } static const struct hc_driver ehci_hlwd_hc_driver = { .description = hcd_name, .product_desc = "Nintendo Wii EHCI Host Controller", .hcd_priv_size = sizeof(struct ehci_hcd), /* * generic hardware linkage */ .irq = ehci_irq, .flags = HCD_USB2 | HCD_BOUNCE_DMA_MEM, /* * basic lifecycle operations */ .reset = ehci_hlwd_reset, .start = ehci_run, .stop = ehci_stop, .shutdown = ehci_shutdown, /* * managing i/o requests and associated device resources */ .urb_enqueue = ehci_urb_enqueue, .urb_dequeue = ehci_urb_dequeue, .endpoint_disable = ehci_endpoint_disable, .endpoint_reset = ehci_endpoint_reset, /* * scheduling support */ .get_frame_number = ehci_get_frame, /* * root hub support */ .hub_status_data = ehci_hub_status_data, .hub_control = ehci_hub_control, #ifdef CONFIG_PM .bus_suspend = ehci_bus_suspend, .bus_resume = ehci_bus_resume, #endif .relinquish_port = ehci_relinquish_port, .port_handed_over = ehci_port_handed_over, .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete, }; static int __devinit ehci_hcd_hlwd_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dn = op->node; struct usb_hcd *hcd; struct ehci_hcd *ehci = NULL; struct resource res; dma_addr_t coherent_mem_addr; size_t coherent_mem_size; int irq; int error = -ENODEV; if (usb_disabled()) goto out; if (starlet_get_ipc_flavour() != STARLET_IPC_MINI) goto out; dev_dbg(&op->dev, "initializing " DRV_MODULE_NAME " USB Controller\n"); error = of_address_to_resource(dn, 0, &res); if (error) goto out; hcd = usb_create_hcd(&ehci_hlwd_hc_driver, &op->dev, DRV_MODULE_NAME); if (!hcd) { error = -ENOMEM; goto out; } hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); error = of_address_to_resource(dn, 1, &res); if (error) { /* satisfy coherent memory allocations from mem1 or mem2 */ dev_warn(&op->dev, "using normal memory\n"); } else { coherent_mem_addr = res.start; coherent_mem_size = res.end - res.start + 1; if (!dma_declare_coherent_memory(&op->dev, coherent_mem_addr, coherent_mem_addr, coherent_mem_size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { dev_err(&op->dev, "error declaring %u bytes of" " coherent memory at 0x%p\n", coherent_mem_size, (void *)coherent_mem_addr); error = -EBUSY; goto err_decl_coherent; } } irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); error = -EBUSY; goto err_irq; } hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { printk(KERN_ERR __FILE__ ": ioremap failed\n"); error = -EBUSY; goto err_ioremap; } ehci = hcd_to_ehci(hcd); ehci->caps = hcd->regs; ehci->regs = hcd->regs + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase)); /* cache this readonly data; minimize chip reads */ ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params); error = usb_add_hcd(hcd, irq, 0); if (error == 0) return 0; iounmap(hcd->regs); err_ioremap: irq_dispose_mapping(irq); err_irq: dma_release_declared_memory(&op->dev); err_decl_coherent: usb_put_hcd(hcd); out: return error; } static int ehci_hcd_hlwd_remove(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); dev_dbg(&op->dev, "stopping " DRV_MODULE_NAME " USB Controller\n"); usb_remove_hcd(hcd); iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); dma_release_declared_memory(&op->dev); usb_put_hcd(hcd); return 0; } static int ehci_hcd_hlwd_shutdown(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); return 0; } static struct of_device_id ehci_hcd_hlwd_match[] = { { .compatible = "nintendo,hollywood-ehci", }, {}, }; MODULE_DEVICE_TABLE(of, ehci_hcd_hlwd_match); static struct of_platform_driver ehci_hcd_hlwd_driver = { .name = DRV_MODULE_NAME, .match_table = ehci_hcd_hlwd_match, .probe = ehci_hcd_hlwd_probe, .remove = ehci_hcd_hlwd_remove, .shutdown = ehci_hcd_hlwd_shutdown, .driver = { .name = DRV_MODULE_NAME, .owner = THIS_MODULE, }, }; --- NEW FILE: ohci-hlwd.c --- /* * drivers/usb/host/ohci-hlwd.c * * Nintendo Wii (Hollywood) USB Open Host Controller Interface. * Copyright (C) 2009 The GameCube Linux Team * Copyright (C) 2009 Albert Herranz * * 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; either version 2 of the License, or (at * your option) any later version. * * Based on ohci-ppc-of.c * * OHCI HCD (Host Controller Driver) for USB. * * (C) Copyright 1999 Roman Weissgaerber <we...@vi...> * (C) Copyright 2000-2002 David Brownell <dbr...@us...> * (C) Copyright 2002 Hewlett-Packard Company * (C) Copyright 2006 Sylvain Munaut <tn...@24...> * * Bus glue for OHCI HC on the of_platform bus * * Modified for of_platform bus from ohci-sa1111.c * * This file is licenced under the GPL. */ #include <linux/signal.h> #include <linux/of_platform.h> #include <asm/prom.h> #include <asm/starlet.h> #include <asm/time.h> /* for get_tbl() */ #define DRV_MODULE_NAME "ohci-hlwd" #define DRV_DESCRIPTION "Nintendo Wii OHCI Host Controller" #define DRV_AUTHOR "Albert Herranz" #define HLWD_EHCI_CTL 0x0d0400cc /* vendor control register */ #define HLWD_EHCI_CTL_OH0INTE (1<<11) /* oh0 interrupt enable */ #define HLWD_EHCI_CTL_OH1INTE (1<<12) /* oh1 interrupt enable */ #define __spin_event_timeout(condition, timeout_usecs, result, __end_tbl) \ for (__end_tbl = get_tbl() + tb_ticks_per_usec * timeout_usecs; \ !(result = (condition)) && (int)(__end_tbl - get_tbl()) > 0;) static DEFINE_SPINLOCK(control_quirk_lock); void ohci_hlwd_control_quirk(struct ohci_hcd *ohci) { static struct ed *ed; /* empty ED */ struct td *td; /* dummy TD */ __hc32 head; __hc32 current; unsigned long ctx; int result; unsigned long flags; /* * One time only. * Allocate and keep a special empty ED with just a dummy TD. */ if (!ed) { ed = ed_alloc(ohci, GFP_ATOMIC); if (!ed) return; td = td_alloc(ohci, GFP_ATOMIC); if (!td) { ed_free(ohci, ed); ed = NULL; return; } ed->hwNextED = 0; ed->hwTailP = ed->hwHeadP = cpu_to_hc32(ohci, td->td_dma & ED_MASK); ed->hwINFO |= cpu_to_hc32(ohci, ED_OUT); wmb(); } spin_lock_irqsave(&control_quirk_lock, flags); /* * The OHCI USB host controllers on the Nintendo Wii * video game console stop working when new TDs are * added to a scheduled control ED after a transfer has * has taken place on it. * * Before scheduling any new control TD, we make the * controller happy by always loading a special control ED * with a single dummy TD and letting the controller attempt * the transfer. * The controller won't do anything with it, as the special * ED has no TDs, but it will keep the controller from failing * on the next transfer. */ head = ohci_readl(ohci, &ohci->regs->ed_controlhead); if (head) { /* * Load the special empty ED and tell the controller to * process the control list. */ ohci_writel(ohci, ed->dma, &ohci->regs->ed_controlhead); ohci_writel (ohci, ohci->hc_control | OHCI_CTRL_CLE, &ohci->regs->control); ohci_writel (ohci, OHCI_CLF, &ohci->regs->cmdstatus); /* spin until the controller is done with the control list */ current = ohci_readl(ohci, &ohci->regs->ed_controlcurrent); __spin_event_timeout(!current, 10 /* usecs */, result, ctx) { cpu_relax(); current = ohci_readl(ohci, &ohci->regs->ed_controlcurrent); } /* restore the old control head and control settings */ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control); ohci_writel(ohci, head, &ohci->regs->ed_controlhead); } spin_unlock_irqrestore(&control_quirk_lock, flags); } void ohci_hlwd_bulk_quirk(struct ohci_hcd *ohci) { /* * There seem to be issues too with the bulk list processing on the * OHCI controller found in the Nintendo Wii video game console. * The exact problem remains still unidentified, but adding a small * delay seems to workaround it. * * As an example, without this quirk the wiimote controller stops * responding after a few seconds because one of its bulk endpoint * descriptors gets stuck. */ udelay(250); } static int __devinit ohci_hlwd_start(struct usb_hcd *hcd) { struct ohci_hcd *ohci = hcd_to_ohci(hcd); void __iomem *ehci_ctl; int error; error = ohci_init(ohci); if (error) goto out; ehci_ctl = ioremap(HLWD_EHCI_CTL, 4); if (!ehci_ctl) { printk(KERN_ERR __FILE__ ": ioremap failed\n"); error = -EBUSY; ohci_stop(hcd); goto out; } /* enable notification of OHCI interrupts */ out_be32(ehci_ctl, in_be32(ehci_ctl) | 0xe0000 | HLWD_EHCI_CTL_OH0INTE | HLWD_EHCI_CTL_OH1INTE); iounmap(ehci_ctl); error = ohci_run(ohci); if (error) { err("can't start %s", ohci_to_hcd(ohci)->self.bus_name); ohci_stop(hcd); goto out; } out: return error; } static const struct hc_driver ohci_hlwd_hc_driver = { .description = hcd_name, .product_desc = "Nintendo Wii OHCI Host Controller", .hcd_priv_size = sizeof(struct ohci_hcd), /* * generic hardware linkage */ .irq = ohci_irq, .flags = HCD_USB11 | HCD_BOUNCE_DMA_MEM, /* * basic lifecycle operations */ .start = ohci_hlwd_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 __devinit ohci_hcd_hlwd_probe(struct of_device *op, const struct of_device_id *match) { struct device_node *dn = op->node; struct usb_hcd *hcd; struct ohci_hcd *ohci = NULL; struct resource res; dma_addr_t coherent_mem_addr; size_t coherent_mem_size; int irq; int error = -ENODEV; if (usb_disabled()) goto out; if (starlet_get_ipc_flavour() != STARLET_IPC_MINI) goto out; dev_dbg(&op->dev, "initializing " DRV_MODULE_NAME " USB Controller\n"); error = of_address_to_resource(dn, 0, &res); if (error) goto out; hcd = usb_create_hcd(&ohci_hlwd_hc_driver, &op->dev, DRV_MODULE_NAME); if (!hcd) { error = -ENOMEM; goto out; } hcd->rsrc_start = res.start; hcd->rsrc_len = resource_size(&res); error = of_address_to_resource(dn, 1, &res); if (error) { /* satisfy coherent memory allocations from mem1 or mem2 */ dev_warn(&op->dev, "using normal memory\n"); } else { coherent_mem_addr = res.start; coherent_mem_size = res.end - res.start + 1; if (!dma_declare_coherent_memory(&op->dev, coherent_mem_addr, coherent_mem_addr, coherent_mem_size, DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE)) { dev_err(&op->dev, "error declaring %u bytes of" " coherent memory at 0x%p\n", coherent_mem_size, (void *)coherent_mem_addr); error = -EBUSY; goto err_decl_coherent; } } irq = irq_of_parse_and_map(dn, 0); if (irq == NO_IRQ) { printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n"); error = -EBUSY; goto err_irq; } hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); if (!hcd->regs) { printk(KERN_ERR __FILE__ ": ioremap failed\n"); error = -EBUSY; goto err_ioremap; } ohci = hcd_to_ohci(hcd); ohci->flags |= OHCI_QUIRK_WII; ohci_hcd_init(ohci); error = usb_add_hcd(hcd, irq, IRQF_DISABLED); if (error) goto err_add_hcd; return 0; err_add_hcd: iounmap(hcd->regs); err_ioremap: irq_dispose_mapping(irq); err_irq: dma_release_declared_memory(&op->dev); err_decl_coherent: usb_put_hcd(hcd); out: return error; } static int ohci_hcd_hlwd_remove(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); dev_set_drvdata(&op->dev, NULL); dev_dbg(&op->dev, "stopping " DRV_MODULE_NAME " USB Controller\n"); usb_remove_hcd(hcd); iounmap(hcd->regs); irq_dispose_mapping(hcd->irq); dma_release_declared_memory(&op->dev); usb_put_hcd(hcd); return 0; } static int ohci_hcd_hlwd_shutdown(struct of_device *op) { struct usb_hcd *hcd = dev_get_drvdata(&op->dev); if (hcd->driver->shutdown) hcd->driver->shutdown(hcd); return 0; } static struct of_device_id ohci_hcd_hlwd_match[] = { { .compatible = "nintendo,hollywood-ohci", }, {}, }; MODULE_DEVICE_TABLE(of, ohci_hcd_hlwd_match); static struct of_platform_driver ohci_hcd_hlwd_driver = { .name = DRV_MODULE_NAME, .match_table = ohci_hcd_hlwd_match, .probe = ohci_hcd_hlwd_probe, .remove = ohci_hcd_hlwd_remove, .shutdown = ohci_hcd_hlwd_shutdown, .driver = { .name = DRV_MODULE_NAME, .owner = THIS_MODULE, }, }; |