Commit da0e8fb0 authored by Valentine Barshak's avatar Valentine Barshak Committed by Greg Kroah-Hartman

USB: add ehci-ppc-of bus glue (device-tree aware)

This adds device-tree-aware ehci-ppc-of driver.
The code is based on the ehci-ppc-soc driver by
Stefan Roese <sr@denx.de>.
Signed-off-by: default avatarValentine Barshak <vbarshak@ru.mvista.com>
Acked-by: default avatarArnd Bergmann <arnd@arndb.de>
Acked-by: default avatarStefan Roese <sr@denx.de>
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 040fa1b9
...@@ -85,6 +85,14 @@ config USB_EHCI_FSL ...@@ -85,6 +85,14 @@ config USB_EHCI_FSL
---help--- ---help---
Variation of ARC USB block used in some Freescale chips. Variation of ARC USB block used in some Freescale chips.
config USB_EHCI_HCD_PPC_OF
bool "EHCI support for PPC USB controller on OF platform bus"
depends on USB_EHCI_HCD && PPC_OF
default y
---help---
Enables support for the USB controller present on the PowerPC
OpenFirmware platform bus.
config USB_ISP116X_HCD config USB_ISP116X_HCD
tristate "ISP116X HCD support" tristate "ISP116X HCD support"
depends on USB depends on USB
......
...@@ -993,11 +993,16 @@ MODULE_LICENSE ("GPL"); ...@@ -993,11 +993,16 @@ MODULE_LICENSE ("GPL");
#define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver #define PS3_SYSTEM_BUS_DRIVER ps3_ehci_driver
#endif #endif
#ifdef CONFIG_440EPX #if defined(CONFIG_440EPX) && !defined(CONFIG_PPC_MERGE)
#include "ehci-ppc-soc.c" #include "ehci-ppc-soc.c"
#define PLATFORM_DRIVER ehci_ppc_soc_driver #define PLATFORM_DRIVER ehci_ppc_soc_driver
#endif #endif
#ifdef CONFIG_USB_EHCI_HCD_PPC_OF
#include "ehci-ppc-of.c"
#define OF_PLATFORM_DRIVER ehci_hcd_ppc_of_driver
#endif
#ifdef CONFIG_ARCH_ORION #ifdef CONFIG_ARCH_ORION
#include "ehci-orion.c" #include "ehci-orion.c"
#define PLATFORM_DRIVER ehci_orion_driver #define PLATFORM_DRIVER ehci_orion_driver
...@@ -1025,52 +1030,58 @@ static int __init ehci_hcd_init(void) ...@@ -1025,52 +1030,58 @@ static int __init ehci_hcd_init(void)
#ifdef PLATFORM_DRIVER #ifdef PLATFORM_DRIVER
retval = platform_driver_register(&PLATFORM_DRIVER); retval = platform_driver_register(&PLATFORM_DRIVER);
if (retval < 0) { if (retval < 0)
#ifdef DEBUG goto clean0;
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif
return retval;
}
#endif #endif
#ifdef PCI_DRIVER #ifdef PCI_DRIVER
retval = pci_register_driver(&PCI_DRIVER); retval = pci_register_driver(&PCI_DRIVER);
if (retval < 0) { if (retval < 0)
#ifdef DEBUG goto clean1;
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER);
#endif
return retval;
}
#endif #endif
#ifdef PS3_SYSTEM_BUS_DRIVER #ifdef PS3_SYSTEM_BUS_DRIVER
retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER); retval = ps3_ehci_driver_register(&PS3_SYSTEM_BUS_DRIVER);
if (retval < 0) { if (retval < 0)
#ifdef DEBUG goto clean2;
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif #endif
#ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER); #ifdef OF_PLATFORM_DRIVER
retval = of_register_platform_driver(&OF_PLATFORM_DRIVER);
if (retval < 0)
goto clean3;
#endif
return retval;
#ifdef OF_PLATFORM_DRIVER
/* of_unregister_platform_driver(&OF_PLATFORM_DRIVER); */
clean3:
#endif
#ifdef PS3_SYSTEM_BUS_DRIVER
ps3_ehci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER);
clean2:
#endif #endif
#ifdef PCI_DRIVER #ifdef PCI_DRIVER
pci_unregister_driver(&PCI_DRIVER); pci_unregister_driver(&PCI_DRIVER);
clean1:
#endif #endif
return retval; #ifdef PLATFORM_DRIVER
} platform_driver_unregister(&PLATFORM_DRIVER);
clean0:
#endif
#ifdef DEBUG
debugfs_remove(ehci_debug_root);
ehci_debug_root = NULL;
#endif #endif
return retval; return retval;
} }
module_init(ehci_hcd_init); module_init(ehci_hcd_init);
static void __exit ehci_hcd_cleanup(void) static void __exit ehci_hcd_cleanup(void)
{ {
#ifdef OF_PLATFORM_DRIVER
of_unregister_platform_driver(&OF_PLATFORM_DRIVER);
#endif
#ifdef PLATFORM_DRIVER #ifdef PLATFORM_DRIVER
platform_driver_unregister(&PLATFORM_DRIVER); platform_driver_unregister(&PLATFORM_DRIVER);
#endif #endif
......
/*
* 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@ru.mvista.com>
*
* Based on "ehci-ppc-soc.c" by Stefan Roese <sr@denx.de>
* and "ohci-ppc-of.c" by Sylvain Munaut <tnt@246tNt.com>
*
* This file is licenced under the GPL.
*/
#include <linux/signal.h>
#include <linux/of.h>
#include <linux/of_platform.h>
/* called during probe() after chip reset completes */
static int ehci_ppc_of_setup(struct usb_hcd *hcd)
{
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
int retval;
retval = ehci_halt(ehci);
if (retval)
return retval;
retval = ehci_init(hcd);
if (retval)
return retval;
ehci->sbrn = 0x20;
return ehci_reset(ehci);
}
static const struct hc_driver ehci_ppc_of_hc_driver = {
.description = hcd_name,
.product_desc = "OF EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
*/
.reset = ehci_ppc_of_setup,
.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,
/*
* 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
};
/*
* 440EPx Errata USBH_3
* Fix: Enable Break Memory Transfer (BMT) in INSNREG3
*/
#define PPC440EPX_EHCI0_INSREG_BMT (0x1 << 0)
static int __devinit
ppc44x_enable_bmt(struct device_node *dn)
{
__iomem u32 *insreg_virt;
insreg_virt = of_iomap(dn, 1);
if (!insreg_virt)
return -EINVAL;
out_be32(insreg_virt + 3, PPC440EPX_EHCI0_INSREG_BMT);
iounmap(insreg_virt);
return 0;
}
static int __devinit
ehci_hcd_ppc_of_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;
struct resource res;
int irq;
int rv;
if (usb_disabled())
return -ENODEV;
dev_dbg(&op->dev, "initializing PPC-OF USB Controller\n");
rv = of_address_to_resource(dn, 0, &res);
if (rv)
return rv;
hcd = usb_create_hcd(&ehci_ppc_of_hc_driver, &op->dev, "PPC-OF USB");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = res.start;
hcd->rsrc_len = res.end - res.start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
printk(KERN_ERR __FILE__ ": request_mem_region failed\n");
rv = -EBUSY;
goto err_rmr;
}
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR __FILE__ ": irq_of_parse_and_map failed\n");
rv = -EBUSY;
goto err_irq;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
printk(KERN_ERR __FILE__ ": ioremap failed\n");
rv = -ENOMEM;
goto err_ioremap;
}
ehci = hcd_to_ehci(hcd);
if (of_get_property(dn, "big-endian", NULL)) {
ehci->big_endian_mmio = 1;
ehci->big_endian_desc = 1;
}
if (of_get_property(dn, "big-endian-regs", NULL))
ehci->big_endian_mmio = 1;
if (of_get_property(dn, "big-endian-desc", NULL))
ehci->big_endian_desc = 1;
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);
if (of_device_is_compatible(dn, "ibm,usb-ehci-440epx")) {
rv = ppc44x_enable_bmt(dn);
ehci_dbg(ehci, "Break Memory Transfer (BMT) is %senabled!\n",
rv ? "NOT ": "");
}
rv = usb_add_hcd(hcd, irq, 0);
if (rv == 0)
return 0;
iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_rmr:
usb_put_hcd(hcd);
return rv;
}
static int ehci_hcd_ppc_of_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 PPC-OF USB Controller\n");
usb_remove_hcd(hcd);
iounmap(hcd->regs);
irq_dispose_mapping(hcd->irq);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
}
static int ehci_hcd_ppc_of_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_ppc_of_match[] = {
{
.compatible = "usb-ehci",
},
{},
};
MODULE_DEVICE_TABLE(of, ehci_hcd_ppc_of_match);
static struct of_platform_driver ehci_hcd_ppc_of_driver = {
.name = "ppc-of-ehci",
.match_table = ehci_hcd_ppc_of_match,
.probe = ehci_hcd_ppc_of_probe,
.remove = ehci_hcd_ppc_of_remove,
.shutdown = ehci_hcd_ppc_of_shutdown,
.driver = {
.name = "ppc-of-ehci",
.owner = THIS_MODULE,
},
};
...@@ -741,7 +741,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc) ...@@ -741,7 +741,7 @@ ehci_port_speed(struct ehci_hcd *ehci, unsigned int portsc)
* definition below can die once the 4xx support is * definition below can die once the 4xx support is
* finally ported over. * finally ported over.
*/ */
#if defined(CONFIG_PPC) #if defined(CONFIG_PPC) && !defined(CONFIG_PPC_MERGE)
#define readl_be(addr) in_be32((__force unsigned *)addr) #define readl_be(addr) in_be32((__force unsigned *)addr)
#define writel_be(val, addr) out_be32((__force unsigned *)addr, val) #define writel_be(val, addr) out_be32((__force unsigned *)addr, val)
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment