]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge branch 'master' of git://git.infradead.org/users/cbou/linux-cns3xxx into devel...
authorRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 3 Jan 2011 10:31:38 +0000 (10:31 +0000)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Mon, 3 Jan 2011 10:31:38 +0000 (10:31 +0000)
12 files changed:
arch/arm/mach-cns3xxx/cns3420vb.c
arch/arm/mach-cns3xxx/core.h
arch/arm/mach-cns3xxx/devices.c
arch/arm/mach-cns3xxx/include/mach/cns3xxx.h
arch/arm/mach-cns3xxx/include/mach/pm.h [new file with mode: 0644]
arch/arm/mach-cns3xxx/pm.c
drivers/usb/Kconfig
drivers/usb/host/Kconfig
drivers/usb/host/ehci-cns3xxx.c [new file with mode: 0644]
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ohci-cns3xxx.c [new file with mode: 0644]
drivers/usb/host/ohci-hcd.c

index 90fe9ab8591db4f7d96744328c7026ba47dd73df..08e5c8759502ba14cf3a83495ccd1df6d00dbf9b 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/compiler.h>
 #include <linux/io.h>
+#include <linux/dma-mapping.h>
 #include <linux/serial_core.h>
 #include <linux/serial_8250.h>
 #include <linux/platform_device.h>
@@ -107,11 +108,64 @@ static void __init cns3420_early_serial_setup(void)
 #endif
 }
 
+/*
+ * USB
+ */
+static struct resource cns3xxx_usb_ehci_resources[] = {
+       [0] = {
+               .start = CNS3XXX_USB_BASE,
+               .end   = CNS3XXX_USB_BASE + SZ_16M - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = IRQ_CNS3XXX_USB_EHCI,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static u64 cns3xxx_usb_ehci_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device cns3xxx_usb_ehci_device = {
+       .name          = "cns3xxx-ehci",
+       .num_resources = ARRAY_SIZE(cns3xxx_usb_ehci_resources),
+       .resource      = cns3xxx_usb_ehci_resources,
+       .dev           = {
+               .dma_mask          = &cns3xxx_usb_ehci_dma_mask,
+               .coherent_dma_mask = DMA_BIT_MASK(32),
+       },
+};
+
+static struct resource cns3xxx_usb_ohci_resources[] = {
+       [0] = {
+               .start = CNS3XXX_USB_OHCI_BASE,
+               .end   = CNS3XXX_USB_OHCI_BASE + SZ_16M - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = IRQ_CNS3XXX_USB_OHCI,
+               .flags = IORESOURCE_IRQ,
+       },
+};
+
+static u64 cns3xxx_usb_ohci_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device cns3xxx_usb_ohci_device = {
+       .name          = "cns3xxx-ohci",
+       .num_resources = ARRAY_SIZE(cns3xxx_usb_ohci_resources),
+       .resource      = cns3xxx_usb_ohci_resources,
+       .dev           = {
+               .dma_mask          = &cns3xxx_usb_ohci_dma_mask,
+               .coherent_dma_mask = DMA_BIT_MASK(32),
+       },
+};
+
 /*
  * Initialization
  */
 static struct platform_device *cns3420_pdevs[] __initdata = {
        &cns3420_nor_pdev,
+       &cns3xxx_usb_ehci_device,
+       &cns3xxx_usb_ohci_device,
 };
 
 static void __init cns3420_init(void)
index 6b33ec11346e54b701fa61867804ecd1120d76e2..73898a7835d399abddfe4dad39d4f13eeb9e05d0 100644 (file)
@@ -17,7 +17,5 @@ extern struct sys_timer cns3xxx_timer;
 void __init cns3xxx_map_io(void);
 void __init cns3xxx_init_irq(void);
 void cns3xxx_power_off(void);
-void cns3xxx_pwr_power_up(unsigned int block);
-void cns3xxx_pwr_power_down(unsigned int block);
 
 #endif /* __CNS3XXX_CORE_H */
index 50b4d31c27c0f5d6ba08e4fcec406ad31fa06737..79d1fb02c23fdba10279d1e2e4af1d22220e5494 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/platform_device.h>
 #include <mach/cns3xxx.h>
 #include <mach/irqs.h>
+#include <mach/pm.h>
 #include "core.h"
 #include "devices.h"
 
index 6dbce13771ca49c83c89dde5b4f0b6f734e20c1f..191c8e57f2890f09c30610bce584194bc1c3d7f1 100644 (file)
 #define CNS3XXX_USBOTG_BASE_VIRT               0xFFF15000
 
 #define CNS3XXX_USB_BASE                       0x82000000      /* USB Host Control */
-#define CNS3XXX_USB_BASE_VIRT                  0xFFF16000
 
 #define CNS3XXX_SATA2_BASE                     0x83000000      /* SATA */
 #define CNS3XXX_SATA2_SIZE                     SZ_16M
 #define CNS3XXX_2DG_BASE_VIRT                  0xFFF1B000
 
 #define CNS3XXX_USB_OHCI_BASE                  0x88000000      /* USB OHCI */
-#define CNS3XXX_USB_OHCI_BASE_VIRT             0xFFF1C000
 
 #define CNS3XXX_L2C_BASE                       0x92000000      /* L2 Cache Control */
 #define CNS3XXX_L2C_BASE_VIRT                  0xFFF27000
diff --git a/arch/arm/mach-cns3xxx/include/mach/pm.h b/arch/arm/mach-cns3xxx/include/mach/pm.h
new file mode 100644 (file)
index 0000000..6eae7f7
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2000 Deep Blue Solutions Ltd
+ * Copyright 2004 ARM Limited
+ * Copyright 2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __CNS3XXX_PM_H
+#define __CNS3XXX_PM_H
+
+#include <asm/atomic.h>
+
+void cns3xxx_pwr_clk_en(unsigned int block);
+void cns3xxx_pwr_clk_dis(unsigned int block);
+void cns3xxx_pwr_power_up(unsigned int block);
+void cns3xxx_pwr_power_down(unsigned int block);
+
+extern atomic_t usb_pwr_ref;
+
+#endif /* __CNS3XXX_PM_H */
index 38e44706feabf43a69a9a3429f3a6753b51d34a0..5e579552aa5444fb7189b8df54e09758fd912b56 100644 (file)
@@ -6,10 +6,14 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/init.h>
+#include <linux/module.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <asm/atomic.h>
 #include <mach/system.h>
 #include <mach/cns3xxx.h>
+#include <mach/pm.h>
 
 void cns3xxx_pwr_clk_en(unsigned int block)
 {
@@ -18,6 +22,16 @@ void cns3xxx_pwr_clk_en(unsigned int block)
        reg |= (block & PM_CLK_GATE_REG_MASK);
        __raw_writel(reg, PM_CLK_GATE_REG);
 }
+EXPORT_SYMBOL(cns3xxx_pwr_clk_en);
+
+void cns3xxx_pwr_clk_dis(unsigned int block)
+{
+       u32 reg = __raw_readl(PM_CLK_GATE_REG);
+
+       reg &= ~(block & PM_CLK_GATE_REG_MASK);
+       __raw_writel(reg, PM_CLK_GATE_REG);
+}
+EXPORT_SYMBOL(cns3xxx_pwr_clk_dis);
 
 void cns3xxx_pwr_power_up(unsigned int block)
 {
@@ -29,6 +43,7 @@ void cns3xxx_pwr_power_up(unsigned int block)
        /* Wait for 300us for the PLL output clock locked. */
        udelay(300);
 };
+EXPORT_SYMBOL(cns3xxx_pwr_power_up);
 
 void cns3xxx_pwr_power_down(unsigned int block)
 {
@@ -38,6 +53,7 @@ void cns3xxx_pwr_power_down(unsigned int block)
        reg |= (block & CNS3XXX_PWR_PLL_ALL);
        __raw_writel(reg, PM_PLL_HM_PD_CTRL_REG);
 };
+EXPORT_SYMBOL(cns3xxx_pwr_power_down);
 
 static void cns3xxx_pwr_soft_rst_force(unsigned int block)
 {
@@ -51,11 +67,13 @@ static void cns3xxx_pwr_soft_rst_force(unsigned int block)
                reg &= ~(block & PM_SOFT_RST_REG_MASK);
        } else {
                reg &= ~(block & PM_SOFT_RST_REG_MASK);
+               __raw_writel(reg, PM_SOFT_RST_REG);
                reg |= (block & PM_SOFT_RST_REG_MASK);
        }
 
        __raw_writel(reg, PM_SOFT_RST_REG);
 }
+EXPORT_SYMBOL(cns3xxx_pwr_soft_rst_force);
 
 void cns3xxx_pwr_soft_rst(unsigned int block)
 {
@@ -69,6 +87,7 @@ void cns3xxx_pwr_soft_rst(unsigned int block)
        }
        cns3xxx_pwr_soft_rst_force(block);
 }
+EXPORT_SYMBOL(cns3xxx_pwr_soft_rst);
 
 void arch_reset(char mode, const char *cmd)
 {
@@ -99,3 +118,7 @@ int cns3xxx_cpu_clock(void)
 
        return cpu;
 }
+EXPORT_SYMBOL(cns3xxx_cpu_clock);
+
+atomic_t usb_pwr_ref = ATOMIC_INIT(0);
+EXPORT_SYMBOL(usb_pwr_ref);
index 67eb3770868fbbb09b94ab26c6f0fc1a96ed13c0..5a7c8f1d76c6e4994b629826c6395176e9a7eb75 100644 (file)
@@ -41,6 +41,7 @@ config USB_ARCH_HAS_OHCI
        default y if MFD_TC6393XB
        default y if ARCH_W90X900
        default y if ARCH_DAVINCI_DA8XX
+       default y if ARCH_CNS3XXX
        # PPC:
        default y if STB03xxx
        default y if PPC_MPC52xx
@@ -66,6 +67,7 @@ config USB_ARCH_HAS_EHCI
        default y if ARCH_AT91SAM9G45
        default y if ARCH_MXC
        default y if ARCH_OMAP3
+       default y if ARCH_CNS3XXX
        default PCI
 
 # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface.
index 6f4f8e6a40c7e2d1fe034ce3ddb056696fc0be00..f8970d151d2a61806a8f10f87b20b6446fad794d 100644 (file)
@@ -147,6 +147,14 @@ config USB_W90X900_EHCI
        ---help---
                Enables support for the W90X900 USB controller
 
+config USB_CNS3XXX_EHCI
+       bool "Cavium CNS3XXX EHCI Module"
+       depends on USB_EHCI_HCD && ARCH_CNS3XXX
+       ---help---
+         Enable support for the CNS3XXX SOC's on-chip EHCI controller.
+         It is needed for high-speed (480Mbit/sec) USB 2.0 device
+         support.
+
 config USB_OXU210HP_HCD
        tristate "OXU210HP HCD support"
        depends on USB
@@ -286,6 +294,13 @@ config USB_OHCI_HCD_SSB
 
          If unsure, say N.
 
+config USB_CNS3XXX_OHCI
+       bool "Cavium CNS3XXX OHCI Module"
+       depends on USB_OHCI_HCD && ARCH_CNS3XXX
+       ---help---
+         Enable support for the CNS3XXX SOC's on-chip OHCI controller.
+         It is needed for low-speed USB 1.0 device support.
+
 config USB_OHCI_BIG_ENDIAN_DESC
        bool
        depends on USB_OHCI_HCD
diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c
new file mode 100644 (file)
index 0000000..708a05b
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/atomic.h>
+#include <mach/cns3xxx.h>
+#include <mach/pm.h>
+
+static int cns3xxx_ehci_init(struct usb_hcd *hcd)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       int retval;
+
+       /*
+        * EHCI and OHCI share the same clock and power,
+        * resetting twice would cause the 1st controller been reset.
+        * Therefore only do power up  at the first up device, and
+        * power down at the last down device.
+        *
+        * Set USB AHB INCR length to 16
+        */
+       if (atomic_inc_return(&usb_pwr_ref) == 1) {
+               cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
+               cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
+               cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
+               __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
+                       MISC_CHIP_CONFIG_REG);
+       }
+
+       ehci->caps = hcd->regs;
+       ehci->regs = hcd->regs
+               + HC_LENGTH(ehci_readl(ehci, &ehci->caps->hc_capbase));
+       ehci->hcs_params = ehci_readl(ehci, &ehci->caps->hcs_params);
+
+       hcd->has_tt = 0;
+       ehci_reset(ehci);
+
+       retval = ehci_init(hcd);
+       if (retval)
+               return retval;
+
+       ehci_port_power(ehci, 0);
+
+       return retval;
+}
+
+static const struct hc_driver cns3xxx_ehci_hc_driver = {
+       .description            = hcd_name,
+       .product_desc           = "CNS3XXX EHCI Host Controller",
+       .hcd_priv_size          = sizeof(struct ehci_hcd),
+       .irq                    = ehci_irq,
+       .flags                  = HCD_MEMORY | HCD_USB2,
+       .reset                  = cns3xxx_ehci_init,
+       .start                  = ehci_run,
+       .stop                   = ehci_stop,
+       .shutdown               = ehci_shutdown,
+       .urb_enqueue            = ehci_urb_enqueue,
+       .urb_dequeue            = ehci_urb_dequeue,
+       .endpoint_disable       = ehci_endpoint_disable,
+       .endpoint_reset         = ehci_endpoint_reset,
+       .get_frame_number       = ehci_get_frame,
+       .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 cns3xxx_ehci_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct usb_hcd *hcd;
+       const struct hc_driver *driver = &cns3xxx_ehci_hc_driver;
+       struct resource *res;
+       int irq;
+       int retval;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "Found HC with no IRQ.\n");
+               return -ENODEV;
+       }
+       irq = res->start;
+
+       hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Found HC with no register addr.\n");
+               retval = -ENODEV;
+               goto err1;
+       }
+
+       hcd->rsrc_start = res->start;
+       hcd->rsrc_len = res->end - res->start + 1;
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+                               driver->description)) {
+               dev_dbg(dev, "controller already in use\n");
+               retval = -EBUSY;
+               goto err1;
+       }
+
+       hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+       if (hcd->regs == NULL) {
+               dev_dbg(dev, "error mapping memory\n");
+               retval = -EFAULT;
+               goto err2;
+       }
+
+       retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+       if (retval == 0)
+               return retval;
+
+       iounmap(hcd->regs);
+err2:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+       usb_put_hcd(hcd);
+
+       return retval;
+}
+
+static int cns3xxx_ehci_remove(struct platform_device *pdev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+       usb_remove_hcd(hcd);
+       iounmap(hcd->regs);
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+       /*
+        * EHCI and OHCI share the same clock and power,
+        * resetting twice would cause the 1st controller been reset.
+        * Therefore only do power up  at the first up device, and
+        * power down at the last down device.
+        */
+       if (atomic_dec_return(&usb_pwr_ref) == 0)
+               cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
+
+       usb_put_hcd(hcd);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+MODULE_ALIAS("platform:cns3xxx-ehci");
+
+static struct platform_driver cns3xxx_ehci_driver = {
+       .probe = cns3xxx_ehci_probe,
+       .remove = cns3xxx_ehci_remove,
+       .driver = {
+               .name = "cns3xxx-ehci",
+       },
+};
index e9062806d4a24898286240f139a2f28dadd8522c..d0c8f7c03e05d599dc126e65eb1a8b64df927813 100644 (file)
@@ -1216,6 +1216,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER                ehci_octeon_driver
 #endif
 
+#ifdef CONFIG_USB_CNS3XXX_EHCI
+#include "ehci-cns3xxx.c"
+#define PLATFORM_DRIVER                cns3xxx_ehci_driver
+#endif
+
 #if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER) && \
     !defined(PS3_SYSTEM_BUS_DRIVER) && !defined(OF_PLATFORM_DRIVER) && \
     !defined(XILINX_OF_PLATFORM_DRIVER)
diff --git a/drivers/usb/host/ohci-cns3xxx.c b/drivers/usb/host/ohci-cns3xxx.c
new file mode 100644 (file)
index 0000000..f05ef87
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2008 Cavium Networks
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, Version 2, as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/atomic.h>
+#include <mach/cns3xxx.h>
+#include <mach/pm.h>
+
+static int __devinit
+cns3xxx_ohci_start(struct usb_hcd *hcd)
+{
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       int ret;
+
+       /*
+        * EHCI and OHCI share the same clock and power,
+        * resetting twice would cause the 1st controller been reset.
+        * Therefore only do power up  at the first up device, and
+        * power down at the last down device.
+        *
+        * Set USB AHB INCR length to 16
+        */
+       if (atomic_inc_return(&usb_pwr_ref) == 1) {
+               cns3xxx_pwr_power_up(1 << PM_PLL_HM_PD_CTRL_REG_OFFSET_PLL_USB);
+               cns3xxx_pwr_clk_en(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
+               cns3xxx_pwr_soft_rst(1 << PM_SOFT_RST_REG_OFFST_USB_HOST);
+               __raw_writel((__raw_readl(MISC_CHIP_CONFIG_REG) | (0X2 << 24)),
+                       MISC_CHIP_CONFIG_REG);
+       }
+
+       ret = ohci_init(ohci);
+       if (ret < 0)
+               return ret;
+
+       ohci->num_ports = 1;
+
+       ret = ohci_run(ohci);
+       if (ret < 0) {
+               err("can't start %s", hcd->self.bus_name);
+               ohci_stop(hcd);
+               return ret;
+       }
+       return 0;
+}
+
+static const struct hc_driver cns3xxx_ohci_hc_driver = {
+       .description            = hcd_name,
+       .product_desc           = "CNS3XXX OHCI Host controller",
+       .hcd_priv_size          = sizeof(struct ohci_hcd),
+       .irq                    = ohci_irq,
+       .flags                  = HCD_USB11 | HCD_MEMORY,
+       .start                  = cns3xxx_ohci_start,
+       .stop                   = ohci_stop,
+       .shutdown               = ohci_shutdown,
+       .urb_enqueue            = ohci_urb_enqueue,
+       .urb_dequeue            = ohci_urb_dequeue,
+       .endpoint_disable       = ohci_endpoint_disable,
+       .get_frame_number       = ohci_get_frame,
+       .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 cns3xxx_ohci_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct usb_hcd *hcd;
+       const struct hc_driver *driver = &cns3xxx_ohci_hc_driver;
+       struct resource *res;
+       int irq;
+       int retval;
+
+       if (usb_disabled())
+               return -ENODEV;
+
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "Found HC with no IRQ.\n");
+               return -ENODEV;
+       }
+       irq = res->start;
+
+       hcd = usb_create_hcd(driver, dev, dev_name(dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "Found HC with no register addr.\n");
+               retval = -ENODEV;
+               goto err1;
+       }
+       hcd->rsrc_start = res->start;
+       hcd->rsrc_len = res->end - res->start + 1;
+
+       if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+                       driver->description)) {
+               dev_dbg(dev, "controller already in use\n");
+               retval = -EBUSY;
+               goto err1;
+       }
+
+       hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+       if (!hcd->regs) {
+               dev_dbg(dev, "error mapping memory\n");
+               retval = -EFAULT;
+               goto err2;
+       }
+
+       ohci_hcd_init(hcd_to_ohci(hcd));
+
+       retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
+       if (retval == 0)
+               return retval;
+
+       iounmap(hcd->regs);
+err2:
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err1:
+       usb_put_hcd(hcd);
+       return retval;
+}
+
+static int cns3xxx_ohci_remove(struct platform_device *pdev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+       usb_remove_hcd(hcd);
+       iounmap(hcd->regs);
+       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+
+       /*
+        * EHCI and OHCI share the same clock and power,
+        * resetting twice would cause the 1st controller been reset.
+        * Therefore only do power up  at the first up device, and
+        * power down at the last down device.
+        */
+       if (atomic_dec_return(&usb_pwr_ref) == 0)
+               cns3xxx_pwr_clk_dis(1 << PM_CLK_GATE_REG_OFFSET_USB_HOST);
+
+       usb_put_hcd(hcd);
+
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+MODULE_ALIAS("platform:cns3xxx-ohci");
+
+static struct platform_driver ohci_hcd_cns3xxx_driver = {
+       .probe = cns3xxx_ohci_probe,
+       .remove = cns3xxx_ohci_remove,
+       .driver = {
+               .name = "cns3xxx-ohci",
+       },
+};
index 5179acb7aa2fb08be16b4b5becbabeb449cd1911..5cb6731ba44381288b8e36b78bca5919b20667a5 100644 (file)
@@ -1111,6 +1111,11 @@ MODULE_LICENSE ("GPL");
 #define PLATFORM_DRIVER                ohci_octeon_driver
 #endif
 
+#ifdef CONFIG_USB_CNS3XXX_OHCI
+#include "ohci-cns3xxx.c"
+#define PLATFORM_DRIVER                ohci_hcd_cns3xxx_driver
+#endif
+
 #if    !defined(PCI_DRIVER) &&         \
        !defined(PLATFORM_DRIVER) &&    \
        !defined(OMAP1_PLATFORM_DRIVER) &&      \