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 <vbarshak@...>
*
* Based on "ehci-ppc-soc.c" by Stefan Roese <sr@...>
* and "ohci-ppc-of.c" by Sylvain Munaut <tnt@...>
*
* 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 <weissg@...>
* (C) Copyright 2000-2002 David Brownell <dbrownell@...>
* (C) Copyright 2002 Hewlett-Packard Company
* (C) Copyright 2006 Sylvain Munaut <tnt@...>
*
* 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,
},
};
|