diff options
author | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
---|---|---|
committer | Srikant Patnaik | 2015-01-11 12:28:04 +0530 |
commit | 871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch) | |
tree | 8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/usb/host/ohci-omap.c | |
parent | 9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff) | |
download | FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2 FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip |
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized.
Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/usb/host/ohci-omap.c')
-rw-r--r-- | drivers/usb/host/ohci-omap.c | 557 |
1 files changed, 557 insertions, 0 deletions
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c new file mode 100644 index 00000000..96451e41 --- /dev/null +++ b/drivers/usb/host/ohci-omap.c @@ -0,0 +1,557 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> + * (C) Copyright 2000-2005 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * OMAP Bus Glue + * + * Modified for OMAP by Tony Lindgren <tony@atomide.com> + * Based on the 2.4 OMAP OHCI driver originally done by MontaVista Software Inc. + * and on ohci-sa1111.c by Christopher Hoover <ch@hpl.hp.com> + * + * This file is licenced under the GPL. + */ + +#include <linux/signal.h> +#include <linux/jiffies.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/gpio.h> + +#include <mach/hardware.h> +#include <asm/io.h> +#include <asm/mach-types.h> + +#include <plat/mux.h> +#include <mach/irqs.h> +#include <plat/fpga.h> +#include <plat/usb.h> + + +/* OMAP-1510 OHCI has its own MMU for DMA */ +#define OMAP1510_LB_MEMSIZE 32 /* Should be same as SDRAM size */ +#define OMAP1510_LB_CLOCK_DIV 0xfffec10c +#define OMAP1510_LB_MMU_CTL 0xfffec208 +#define OMAP1510_LB_MMU_LCK 0xfffec224 +#define OMAP1510_LB_MMU_LD_TLB 0xfffec228 +#define OMAP1510_LB_MMU_CAM_H 0xfffec22c +#define OMAP1510_LB_MMU_CAM_L 0xfffec230 +#define OMAP1510_LB_MMU_RAM_H 0xfffec234 +#define OMAP1510_LB_MMU_RAM_L 0xfffec238 + + +#ifndef CONFIG_ARCH_OMAP +#error "This file is OMAP bus glue. CONFIG_OMAP must be defined." +#endif + +#ifdef CONFIG_TPS65010 +#include <linux/i2c/tps65010.h> +#else + +#define LOW 0 +#define HIGH 1 + +#define GPIO1 1 + +static inline int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) +{ + return 0; +} + +#endif + +extern int usb_disabled(void); +extern int ocpi_enable(void); + +static struct clk *usb_host_ck; +static struct clk *usb_dc_ck; +static int host_enabled; +static int host_initialized; + +static void omap_ohci_clock_power(int on) +{ + if (on) { + clk_enable(usb_dc_ck); + clk_enable(usb_host_ck); + /* guesstimate for T5 == 1x 32K clock + APLL lock time */ + udelay(100); + } else { + clk_disable(usb_host_ck); + clk_disable(usb_dc_ck); + } +} + +/* + * Board specific gang-switched transceiver power on/off. + * NOTE: OSK supplies power from DC, not battery. + */ +static int omap_ohci_transceiver_power(int on) +{ + if (on) { + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) + | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, LOW); + } else { + if (machine_is_omap_innovator() && cpu_is_omap1510()) + fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL) + & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)), + INNOVATOR_FPGA_CAM_USB_CONTROL); + else if (machine_is_omap_osk()) + tps65010_set_gpio_out_value(GPIO1, HIGH); + } + + return 0; +} + +#ifdef CONFIG_ARCH_OMAP15XX +/* + * OMAP-1510 specific Local Bus clock on/off + */ +static int omap_1510_local_bus_power(int on) +{ + if (on) { + omap_writel((1 << 1) | (1 << 0), OMAP1510_LB_MMU_CTL); + udelay(200); + } else { + omap_writel(0, OMAP1510_LB_MMU_CTL); + } + + return 0; +} + +/* + * OMAP-1510 specific Local Bus initialization + * NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE. + * See also arch/mach-omap/memory.h for __virt_to_dma() and + * __dma_to_virt() which need to match with the physical + * Local Bus address below. + */ +static int omap_1510_local_bus_init(void) +{ + unsigned int tlb; + unsigned long lbaddr, physaddr; + + omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4, + OMAP1510_LB_CLOCK_DIV); + + /* Configure the Local Bus MMU table */ + for (tlb = 0; tlb < OMAP1510_LB_MEMSIZE; tlb++) { + lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET; + physaddr = tlb * 0x00100000 + PHYS_OFFSET; + omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H); + omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc, + OMAP1510_LB_MMU_CAM_L); + omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H); + omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L); + omap_writel(tlb << 4, OMAP1510_LB_MMU_LCK); + omap_writel(0x1, OMAP1510_LB_MMU_LD_TLB); + } + + /* Enable the walking table */ + omap_writel(omap_readl(OMAP1510_LB_MMU_CTL) | (1 << 3), OMAP1510_LB_MMU_CTL); + udelay(200); + + return 0; +} +#else +#define omap_1510_local_bus_power(x) {} +#define omap_1510_local_bus_init() {} +#endif + +#ifdef CONFIG_USB_OTG + +static void start_hnp(struct ohci_hcd *ohci) +{ + const unsigned port = ohci_to_hcd(ohci)->self.otg_port - 1; + unsigned long flags; + u32 l; + + otg_start_hnp(ohci->transceiver->otg); + + local_irq_save(flags); + ohci->transceiver->state = OTG_STATE_A_SUSPEND; + writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]); + l = omap_readl(OTG_CTRL); + l &= ~OTG_A_BUSREQ; + omap_writel(l, OTG_CTRL); + local_irq_restore(flags); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static int ohci_omap_init(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct omap_usb_config *config = hcd->self.controller->platform_data; + int need_transceiver = (config->otg != 0); + int ret; + + dev_dbg(hcd->self.controller, "starting USB Controller\n"); + + if (config->otg) { + ohci_to_hcd(ohci)->self.otg_port = config->otg; + /* default/minimum OTG power budget: 8 mA */ + ohci_to_hcd(ohci)->power_budget = 8; + } + + /* boards can use OTG transceivers in non-OTG modes */ + need_transceiver = need_transceiver + || machine_is_omap_h2() || machine_is_omap_h3(); + + if (cpu_is_omap16xx()) + ocpi_enable(); + +#ifdef CONFIG_USB_OTG + if (need_transceiver) { + ohci->transceiver = usb_get_transceiver(); + if (ohci->transceiver) { + int status = otg_set_host(ohci->transceiver->otg, + &ohci_to_hcd(ohci)->self); + dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n", + ohci->transceiver->label, status); + if (status) { + if (ohci->transceiver) + put_device(ohci->transceiver->dev); + return status; + } + } else { + dev_err(hcd->self.controller, "can't find transceiver\n"); + return -ENODEV; + } + ohci->start_hnp = start_hnp; + } +#endif + + omap_ohci_clock_power(1); + + if (cpu_is_omap15xx()) { + omap_1510_local_bus_power(1); + omap_1510_local_bus_init(); + } + + if ((ret = ohci_init(ohci)) < 0) + return ret; + + /* board-specific power switching and overcurrent support */ + if (machine_is_omap_osk() || machine_is_omap_innovator()) { + u32 rh = roothub_a (ohci); + + /* power switching (ganged by default) */ + rh &= ~RH_A_NPS; + + /* TPS2045 switch for internal transceiver (port 1) */ + if (machine_is_omap_osk()) { + ohci_to_hcd(ohci)->power_budget = 250; + + rh &= ~RH_A_NOCP; + + /* gpio9 for overcurrent detction */ + omap_cfg_reg(W8_1610_GPIO9); + gpio_request(9, "OHCI overcurrent"); + gpio_direction_input(9); + + /* for paranoia's sake: disable USB.PUEN */ + omap_cfg_reg(W4_USB_HIGHZ); + } + ohci_writel(ohci, rh, &ohci->regs->roothub.a); + ohci->flags &= ~OHCI_QUIRK_HUB_POWER; + } else if (machine_is_nokia770()) { + /* We require a self-powered hub, which should have + * plenty of power. */ + ohci_to_hcd(ohci)->power_budget = 0; + } + + /* FIXME khubd hub requests should manage power switching */ + omap_ohci_transceiver_power(1); + + /* board init will have already handled HMC and mux setup. + * any external transceiver should already be initialized + * too, so all configured ports use the right signaling now. + */ + + return 0; +} + +static void ohci_omap_stop(struct usb_hcd *hcd) +{ + dev_dbg(hcd->self.controller, "stopping USB Controller\n"); + ohci_stop(hcd); + omap_ohci_clock_power(0); +} + + +/*-------------------------------------------------------------------------*/ + +/** + * usb_hcd_omap_probe - initialize OMAP-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + */ +static int usb_hcd_omap_probe (const struct hc_driver *driver, + struct platform_device *pdev) +{ + int retval, irq; + struct usb_hcd *hcd = 0; + struct ohci_hcd *ohci; + + if (pdev->num_resources != 2) { + printk(KERN_ERR "hcd probe: invalid num_resources: %i\n", + pdev->num_resources); + return -ENODEV; + } + + if (pdev->resource[0].flags != IORESOURCE_MEM + || pdev->resource[1].flags != IORESOURCE_IRQ) { + printk(KERN_ERR "hcd probe: invalid resource type\n"); + return -ENODEV; + } + + usb_host_ck = clk_get(&pdev->dev, "usb_hhc_ck"); + if (IS_ERR(usb_host_ck)) + return PTR_ERR(usb_host_ck); + + if (!cpu_is_omap15xx()) + usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck"); + else + usb_dc_ck = clk_get(&pdev->dev, "lb_ck"); + + if (IS_ERR(usb_dc_ck)) { + clk_put(usb_host_ck); + return PTR_ERR(usb_dc_ck); + } + + + hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev)); + if (!hcd) { + retval = -ENOMEM; + goto err0; + } + hcd->rsrc_start = pdev->resource[0].start; + hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; + + if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + retval = -EBUSY; + goto err1; + } + + hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); + if (!hcd->regs) { + dev_err(&pdev->dev, "can't ioremap OHCI HCD\n"); + retval = -ENOMEM; + goto err2; + } + + ohci = hcd_to_ohci(hcd); + ohci_hcd_init(ohci); + + host_initialized = 0; + host_enabled = 1; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + retval = -ENXIO; + goto err3; + } + retval = usb_add_hcd(hcd, irq, 0); + if (retval) + goto err3; + + host_initialized = 1; + + if (!host_enabled) + omap_ohci_clock_power(0); + + return 0; +err3: + iounmap(hcd->regs); +err2: + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); +err1: + usb_put_hcd(hcd); +err0: + clk_put(usb_dc_ck); + clk_put(usb_host_ck); + return retval; +} + + +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_omap_remove - shutdown processing for OMAP-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_omap_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + */ +static inline void +usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) +{ + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + + usb_remove_hcd(hcd); + if (ohci->transceiver) { + (void) otg_set_host(ohci->transceiver->otg, 0); + put_device(ohci->transceiver->dev); + } + if (machine_is_omap_osk()) + gpio_free(9); + iounmap(hcd->regs); + release_mem_region(hcd->rsrc_start, hcd->rsrc_len); + usb_put_hcd(hcd); + clk_put(usb_dc_ck); + clk_put(usb_host_ck); +} + +/*-------------------------------------------------------------------------*/ + +static int +ohci_omap_start (struct usb_hcd *hcd) +{ + struct omap_usb_config *config; + struct ohci_hcd *ohci = hcd_to_ohci (hcd); + int ret; + + if (!host_enabled) + return 0; + config = hcd->self.controller->platform_data; + if (config->otg || config->rwc) { + ohci->hc_control = OHCI_CTRL_RWC; + writel(OHCI_CTRL_RWC, &ohci->regs->control); + } + + if ((ret = ohci_run (ohci)) < 0) { + dev_err(hcd->self.controller, "can't start\n"); + ohci_stop (hcd); + return ret; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static const struct hc_driver ohci_omap_hc_driver = { + .description = hcd_name, + .product_desc = "OMAP OHCI", + .hcd_priv_size = sizeof(struct ohci_hcd), + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_USB11 | HCD_MEMORY, + + /* + * basic lifecycle operations + */ + .reset = ohci_omap_init, + .start = ohci_omap_start, + .stop = ohci_omap_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 ohci_hcd_omap_drv_probe(struct platform_device *dev) +{ + return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev); +} + +static int ohci_hcd_omap_drv_remove(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + usb_hcd_omap_remove(hcd, dev); + platform_set_drvdata(dev, NULL); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PM + +static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) +{ + struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev)); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + omap_ohci_clock_power(0); + return 0; +} + +static int ohci_omap_resume(struct platform_device *dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + + omap_ohci_clock_power(1); + ohci_finish_controller_resume(hcd); + return 0; +} + +#endif + +/*-------------------------------------------------------------------------*/ + +/* + * Driver definition to register with the OMAP bus + */ +static struct platform_driver ohci_hcd_omap_driver = { + .probe = ohci_hcd_omap_drv_probe, + .remove = ohci_hcd_omap_drv_remove, + .shutdown = usb_hcd_platform_shutdown, +#ifdef CONFIG_PM + .suspend = ohci_omap_suspend, + .resume = ohci_omap_resume, +#endif + .driver = { + .owner = THIS_MODULE, + .name = "ohci", + }, +}; + +MODULE_ALIAS("platform:ohci"); |