From df8f4d2f3ef9ca2154f7bd531492a83c4ab4558a Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Fri, 26 Nov 2010 20:48:35 +0300 Subject: ARM: cns3xxx: Make cns3xxx_pwr_soft_rst_force() to actually reset blocks commit 6eb5d146d4535 ("ARM: cns3xxx: Use IO memory accessors everywhere") breaks cns3xxx_pwr_soft_rst_force() function, so that it doesn't write cleared bit into the register. This patch fixes the issue by adding the necessary __raw_writel(). Signed-off-by: Anton Vorontsov diff --git a/arch/arm/mach-cns3xxx/pm.c b/arch/arm/mach-cns3xxx/pm.c index 38e4470..dfccabb 100644 --- a/arch/arm/mach-cns3xxx/pm.c +++ b/arch/arm/mach-cns3xxx/pm.c @@ -51,6 +51,7 @@ 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); } -- cgit v0.10.2 From 38e64ba0f13918d11e50bbd5bb775781ccd6439d Mon Sep 17 00:00:00 2001 From: Mac Lin Date: Tue, 23 Nov 2010 00:32:43 +0800 Subject: ARM: cns3xxx: Add new and export the old power management functions This patch adds cns3xxx_pwr_clk_dis, and exports these power management functions that may be used by many other device drivers on CNS3XXX. Signed-off-by: Mac Lin Signed-off-by: Anton Vorontsov diff --git a/arch/arm/mach-cns3xxx/core.h b/arch/arm/mach-cns3xxx/core.h index 6b33ec1..73898a7 100644 --- a/arch/arm/mach-cns3xxx/core.h +++ b/arch/arm/mach-cns3xxx/core.h @@ -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 */ diff --git a/arch/arm/mach-cns3xxx/devices.c b/arch/arm/mach-cns3xxx/devices.c index 50b4d31..79d1fb0 100644 --- a/arch/arm/mach-cns3xxx/devices.c +++ b/arch/arm/mach-cns3xxx/devices.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "core.h" #include "devices.h" diff --git a/arch/arm/mach-cns3xxx/include/mach/pm.h b/arch/arm/mach-cns3xxx/include/mach/pm.h new file mode 100644 index 0000000..102617b --- /dev/null +++ b/arch/arm/mach-cns3xxx/include/mach/pm.h @@ -0,0 +1,19 @@ +/* + * 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 + +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); + +#endif /* __CNS3XXX_PM_H */ diff --git a/arch/arm/mach-cns3xxx/pm.c b/arch/arm/mach-cns3xxx/pm.c index dfccabb..c047c1a 100644 --- a/arch/arm/mach-cns3xxx/pm.c +++ b/arch/arm/mach-cns3xxx/pm.c @@ -6,10 +6,13 @@ * published by the Free Software Foundation. */ +#include +#include #include #include #include #include +#include void cns3xxx_pwr_clk_en(unsigned int block) { @@ -18,6 +21,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 +42,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 +52,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) { @@ -57,6 +72,7 @@ static void cns3xxx_pwr_soft_rst_force(unsigned int block) __raw_writel(reg, PM_SOFT_RST_REG); } +EXPORT_SYMBOL(cns3xxx_pwr_soft_rst_force); void cns3xxx_pwr_soft_rst(unsigned int block) { @@ -70,6 +86,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) { @@ -100,3 +117,4 @@ int cns3xxx_cpu_clock(void) return cpu; } +EXPORT_SYMBOL(cns3xxx_cpu_clock); -- cgit v0.10.2 From cf36797f35676dafae9d44484391ac7f56b2485a Mon Sep 17 00:00:00 2001 From: Mac Lin Date: Tue, 23 Nov 2010 00:32:44 +0800 Subject: ARM: cns3xxx: Add architecture definition for EHCI/OHCI controller This patch add plateform_device for EHCI and OHCI controller on CNS3XXX. Power reference count (usb_pwr_ref) is used to control enabling and disabling the single clock control for both EHCI and OHCI controller. It also removes EHCI/OHCI unused virtual address definitions. Signed-off-by: Mac Lin Signed-off-by: Anton Vorontsov diff --git a/arch/arm/mach-cns3xxx/cns3420vb.c b/arch/arm/mach-cns3xxx/cns3420vb.c index 90fe9ab..08e5c87 100644 --- a/arch/arm/mach-cns3xxx/cns3420vb.c +++ b/arch/arm/mach-cns3xxx/cns3420vb.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -108,10 +109,63 @@ static void __init cns3420_early_serial_setup(void) } /* + * 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) diff --git a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h index 6dbce13..191c8e5 100644 --- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h +++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h @@ -165,7 +165,6 @@ #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 @@ -184,7 +183,6 @@ #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 index 102617b..6eae7f7 100644 --- a/arch/arm/mach-cns3xxx/include/mach/pm.h +++ b/arch/arm/mach-cns3xxx/include/mach/pm.h @@ -11,9 +11,13 @@ #ifndef __CNS3XXX_PM_H #define __CNS3XXX_PM_H +#include + 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 */ diff --git a/arch/arm/mach-cns3xxx/pm.c b/arch/arm/mach-cns3xxx/pm.c index c047c1a..5e57955 100644 --- a/arch/arm/mach-cns3xxx/pm.c +++ b/arch/arm/mach-cns3xxx/pm.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -118,3 +119,6 @@ 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); -- cgit v0.10.2 From 760efe6910d5743084b586d3d0a3b65aea96fb2f Mon Sep 17 00:00:00 2001 From: Mac Lin Date: Thu, 25 Nov 2010 23:58:00 +0800 Subject: USB: cns3xxx: Add EHCI and OHCI bus glue for cns3xxx SOCs The CNS3XXX SOC has include USB EHCI and OHCI compatible controllers. This patch adds the necessary glue logic to allow ehci-hcd and ohci-hcd drivers to work on CNS3XXX The EHCI and OHCI controllers share a common clock control and reset bit, therefore additional check for the timming of enabling and disabling is required. The USB bit of PLL Power Down Control is also shared by OTG, 24MHzUART clock, Crypto clock, PCIe reference clock, and Clock Scale Generator. Therefore we only ensure it is enabled, while not disabling it. Signed-off-by: Mac Lin Acked-by: Alan Stern Acked-by: Greg Kroah-Hartman Signed-off-by: Anton Vorontsov diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 67eb377..5a7c8f1 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -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. diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 6f4f8e6..f8970d1 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -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 index 0000000..708a05b --- /dev/null +++ b/drivers/usb/host/ehci-cns3xxx.c @@ -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 +#include +#include +#include + +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", + }, +}; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 502a7e6..0653540 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -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 index 0000000..f05ef87 --- /dev/null +++ b/drivers/usb/host/ohci-cns3xxx.c @@ -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 +#include +#include +#include + +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", + }, +}; diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 5179acb..5cb6731 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -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) && \ -- cgit v0.10.2