11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * OHCI HCD (Host Controller Driver) for USB. 31da177e4SLinus Torvalds * 41da177e4SLinus Torvalds * (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> 51da177e4SLinus Torvalds * (C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> 61da177e4SLinus Torvalds * (C) Copyright 2002 Hewlett-Packard Company 71da177e4SLinus Torvalds * 81da177e4SLinus Torvalds * Bus Glue for pxa27x 91da177e4SLinus Torvalds * 101da177e4SLinus Torvalds * Written by Christopher Hoover <ch@hpl.hp.com> 111da177e4SLinus Torvalds * Based on fragments of previous driver by Russell King et al. 121da177e4SLinus Torvalds * 131da177e4SLinus Torvalds * Modified for LH7A404 from ohci-sa1111.c 141da177e4SLinus Torvalds * by Durgesh Pattamatta <pattamattad@sharpsec.com> 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * Modified for pxa27x from ohci-lh7a404.c 171da177e4SLinus Torvalds * by Nick Bane <nick@cecomputing.co.uk> 26-8-2004 181da177e4SLinus Torvalds * 191da177e4SLinus Torvalds * This file is licenced under the GPL. 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <linux/device.h> 234e57b681STim Schmielau #include <linux/signal.h> 24d052d1beSRussell King #include <linux/platform_device.h> 254fd5f826SLinus Torvalds 261da177e4SLinus Torvalds #include <asm/mach-types.h> 271da177e4SLinus Torvalds #include <asm/hardware.h> 281da177e4SLinus Torvalds #include <asm/arch/pxa-regs.h> 2981f280e2SRichard Purdie #include <asm/arch/ohci.h> 301da177e4SLinus Torvalds 311da177e4SLinus Torvalds #define PXA_UHC_MAX_PORTNUM 3 321da177e4SLinus Torvalds 331da177e4SLinus Torvalds #define UHCRHPS(x) __REG2( 0x4C000050, (x)<<2 ) 341da177e4SLinus Torvalds 351da177e4SLinus Torvalds /* 361da177e4SLinus Torvalds PMM_NPS_MODE -- PMM Non-power switching mode 371da177e4SLinus Torvalds Ports are powered continuously. 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds PMM_GLOBAL_MODE -- PMM global switching mode 401da177e4SLinus Torvalds All ports are powered at the same time. 411da177e4SLinus Torvalds 421da177e4SLinus Torvalds PMM_PERPORT_MODE -- PMM per port switching mode 431da177e4SLinus Torvalds Ports are powered individually. 441da177e4SLinus Torvalds */ 451da177e4SLinus Torvalds static int pxa27x_ohci_select_pmm( int mode ) 461da177e4SLinus Torvalds { 471da177e4SLinus Torvalds switch ( mode ) { 481da177e4SLinus Torvalds case PMM_NPS_MODE: 491da177e4SLinus Torvalds UHCRHDA |= RH_A_NPS; 501da177e4SLinus Torvalds break; 511da177e4SLinus Torvalds case PMM_GLOBAL_MODE: 521da177e4SLinus Torvalds UHCRHDA &= ~(RH_A_NPS & RH_A_PSM); 531da177e4SLinus Torvalds break; 541da177e4SLinus Torvalds case PMM_PERPORT_MODE: 551da177e4SLinus Torvalds UHCRHDA &= ~(RH_A_NPS); 561da177e4SLinus Torvalds UHCRHDA |= RH_A_PSM; 571da177e4SLinus Torvalds 581da177e4SLinus Torvalds /* Set port power control mask bits, only 3 ports. */ 591da177e4SLinus Torvalds UHCRHDB |= (0x7<<17); 601da177e4SLinus Torvalds break; 611da177e4SLinus Torvalds default: 621da177e4SLinus Torvalds printk( KERN_ERR 631da177e4SLinus Torvalds "Invalid mode %d, set to non-power switch mode.\n", 641da177e4SLinus Torvalds mode ); 651da177e4SLinus Torvalds 661da177e4SLinus Torvalds UHCRHDA |= RH_A_NPS; 671da177e4SLinus Torvalds } 681da177e4SLinus Torvalds 691da177e4SLinus Torvalds return 0; 701da177e4SLinus Torvalds } 711da177e4SLinus Torvalds 721da177e4SLinus Torvalds extern int usb_disabled(void); 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /*-------------------------------------------------------------------------*/ 751da177e4SLinus Torvalds 7681f280e2SRichard Purdie static int pxa27x_start_hc(struct device *dev) 771da177e4SLinus Torvalds { 7881f280e2SRichard Purdie int retval = 0; 7981f280e2SRichard Purdie struct pxaohci_platform_data *inf; 8081f280e2SRichard Purdie 8181f280e2SRichard Purdie inf = dev->platform_data; 8281f280e2SRichard Purdie 831da177e4SLinus Torvalds pxa_set_cken(CKEN10_USBHOST, 1); 841da177e4SLinus Torvalds 851da177e4SLinus Torvalds UHCHR |= UHCHR_FHR; 861da177e4SLinus Torvalds udelay(11); 871da177e4SLinus Torvalds UHCHR &= ~UHCHR_FHR; 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds UHCHR |= UHCHR_FSBIR; 901da177e4SLinus Torvalds while (UHCHR & UHCHR_FSBIR) 911da177e4SLinus Torvalds cpu_relax(); 921da177e4SLinus Torvalds 9381f280e2SRichard Purdie if (inf->init) 9481f280e2SRichard Purdie retval = inf->init(dev); 951da177e4SLinus Torvalds 9681f280e2SRichard Purdie if (retval < 0) 9781f280e2SRichard Purdie return retval; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds UHCHR &= ~UHCHR_SSE; 1001da177e4SLinus Torvalds 1011da177e4SLinus Torvalds UHCHIE = (UHCHIE_UPRIE | UHCHIE_RWIE); 102155faf5eSDavid Brownell 103155faf5eSDavid Brownell /* Clear any OTG Pin Hold */ 104155faf5eSDavid Brownell if (PSSR & PSSR_OTGPH) 105155faf5eSDavid Brownell PSSR |= PSSR_OTGPH; 10681f280e2SRichard Purdie 10781f280e2SRichard Purdie return 0; 1081da177e4SLinus Torvalds } 1091da177e4SLinus Torvalds 11081f280e2SRichard Purdie static void pxa27x_stop_hc(struct device *dev) 1111da177e4SLinus Torvalds { 11281f280e2SRichard Purdie struct pxaohci_platform_data *inf; 11381f280e2SRichard Purdie 11481f280e2SRichard Purdie inf = dev->platform_data; 11581f280e2SRichard Purdie 11681f280e2SRichard Purdie if (inf->exit) 11781f280e2SRichard Purdie inf->exit(dev); 11881f280e2SRichard Purdie 1191da177e4SLinus Torvalds UHCHR |= UHCHR_FHR; 1201da177e4SLinus Torvalds udelay(11); 1211da177e4SLinus Torvalds UHCHR &= ~UHCHR_FHR; 1221da177e4SLinus Torvalds 1231da177e4SLinus Torvalds UHCCOMS |= 1; 1241da177e4SLinus Torvalds udelay(10); 1251da177e4SLinus Torvalds 1261da177e4SLinus Torvalds pxa_set_cken(CKEN10_USBHOST, 0); 1271da177e4SLinus Torvalds } 1281da177e4SLinus Torvalds 1291da177e4SLinus Torvalds 1301da177e4SLinus Torvalds /*-------------------------------------------------------------------------*/ 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds /* configure so an HC device and id are always provided */ 1331da177e4SLinus Torvalds /* always called with process context; sleeping is OK */ 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds 1361da177e4SLinus Torvalds /** 1371da177e4SLinus Torvalds * usb_hcd_pxa27x_probe - initialize pxa27x-based HCDs 1381da177e4SLinus Torvalds * Context: !in_interrupt() 1391da177e4SLinus Torvalds * 1401da177e4SLinus Torvalds * Allocates basic resources for this USB host controller, and 1411da177e4SLinus Torvalds * then invokes the start() method for the HCD associated with it 1421da177e4SLinus Torvalds * through the hotplug entry's driver_data. 1431da177e4SLinus Torvalds * 1441da177e4SLinus Torvalds */ 14581f280e2SRichard Purdie int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device *pdev) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds int retval; 1481da177e4SLinus Torvalds struct usb_hcd *hcd; 14981f280e2SRichard Purdie struct pxaohci_platform_data *inf; 1501da177e4SLinus Torvalds 15181f280e2SRichard Purdie inf = pdev->dev.platform_data; 15281f280e2SRichard Purdie 15381f280e2SRichard Purdie if (!inf) 15481f280e2SRichard Purdie return -ENODEV; 15581f280e2SRichard Purdie 15681f280e2SRichard Purdie if (pdev->resource[1].flags != IORESOURCE_IRQ) { 1571da177e4SLinus Torvalds pr_debug ("resource[1] is not IORESOURCE_IRQ"); 1581da177e4SLinus Torvalds return -ENOMEM; 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 16181f280e2SRichard Purdie hcd = usb_create_hcd (driver, &pdev->dev, "pxa27x"); 1621da177e4SLinus Torvalds if (!hcd) 1631da177e4SLinus Torvalds return -ENOMEM; 16481f280e2SRichard Purdie hcd->rsrc_start = pdev->resource[0].start; 16581f280e2SRichard Purdie hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1; 1661da177e4SLinus Torvalds 1671da177e4SLinus Torvalds if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) { 1681da177e4SLinus Torvalds pr_debug("request_mem_region failed"); 1691da177e4SLinus Torvalds retval = -EBUSY; 1701da177e4SLinus Torvalds goto err1; 1711da177e4SLinus Torvalds } 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); 1741da177e4SLinus Torvalds if (!hcd->regs) { 1751da177e4SLinus Torvalds pr_debug("ioremap failed"); 1761da177e4SLinus Torvalds retval = -ENOMEM; 1771da177e4SLinus Torvalds goto err2; 1781da177e4SLinus Torvalds } 1791da177e4SLinus Torvalds 18081f280e2SRichard Purdie if ((retval = pxa27x_start_hc(&pdev->dev)) < 0) { 18181f280e2SRichard Purdie pr_debug("pxa27x_start_hc failed"); 18281f280e2SRichard Purdie goto err3; 18381f280e2SRichard Purdie } 1841da177e4SLinus Torvalds 1851da177e4SLinus Torvalds /* Select Power Management Mode */ 18681f280e2SRichard Purdie pxa27x_ohci_select_pmm(inf->port_mode); 1871da177e4SLinus Torvalds 188*0c27c5d5SRichard Purdie if (inf->power_budget) 189*0c27c5d5SRichard Purdie hcd->power_budget = inf->power_budget; 190*0c27c5d5SRichard Purdie 1911da177e4SLinus Torvalds ohci_hcd_init(hcd_to_ohci(hcd)); 1921da177e4SLinus Torvalds 19381f280e2SRichard Purdie retval = usb_add_hcd(hcd, pdev->resource[1].start, SA_INTERRUPT); 1941da177e4SLinus Torvalds if (retval == 0) 1951da177e4SLinus Torvalds return retval; 1961da177e4SLinus Torvalds 19781f280e2SRichard Purdie pxa27x_stop_hc(&pdev->dev); 19881f280e2SRichard Purdie err3: 1991da177e4SLinus Torvalds iounmap(hcd->regs); 2001da177e4SLinus Torvalds err2: 2011da177e4SLinus Torvalds release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 2021da177e4SLinus Torvalds err1: 2031da177e4SLinus Torvalds usb_put_hcd(hcd); 2041da177e4SLinus Torvalds return retval; 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds /* may be called without controller electrically present */ 2091da177e4SLinus Torvalds /* may be called with controller, bus, and devices active */ 2101da177e4SLinus Torvalds 2111da177e4SLinus Torvalds /** 2121da177e4SLinus Torvalds * usb_hcd_pxa27x_remove - shutdown processing for pxa27x-based HCDs 2131da177e4SLinus Torvalds * @dev: USB Host Controller being removed 2141da177e4SLinus Torvalds * Context: !in_interrupt() 2151da177e4SLinus Torvalds * 2161da177e4SLinus Torvalds * Reverses the effect of usb_hcd_pxa27x_probe(), first invoking 2171da177e4SLinus Torvalds * the HCD's stop() method. It is always called from a thread 2181da177e4SLinus Torvalds * context, normally "rmmod", "apmd", or something similar. 2191da177e4SLinus Torvalds * 2201da177e4SLinus Torvalds */ 22181f280e2SRichard Purdie void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev) 2221da177e4SLinus Torvalds { 2231da177e4SLinus Torvalds usb_remove_hcd(hcd); 22481f280e2SRichard Purdie pxa27x_stop_hc(&pdev->dev); 2251da177e4SLinus Torvalds iounmap(hcd->regs); 2261da177e4SLinus Torvalds release_mem_region(hcd->rsrc_start, hcd->rsrc_len); 2271da177e4SLinus Torvalds usb_put_hcd(hcd); 2281da177e4SLinus Torvalds } 2291da177e4SLinus Torvalds 2301da177e4SLinus Torvalds /*-------------------------------------------------------------------------*/ 2311da177e4SLinus Torvalds 2321da177e4SLinus Torvalds static int __devinit 2331da177e4SLinus Torvalds ohci_pxa27x_start (struct usb_hcd *hcd) 2341da177e4SLinus Torvalds { 2351da177e4SLinus Torvalds struct ohci_hcd *ohci = hcd_to_ohci (hcd); 2361da177e4SLinus Torvalds int ret; 2371da177e4SLinus Torvalds 2381da177e4SLinus Torvalds ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci); 2391da177e4SLinus Torvalds 240fdd13b36SDavid Brownell /* The value of NDP in roothub_a is incorrect on this hardware */ 241fdd13b36SDavid Brownell ohci->num_ports = 3; 242fdd13b36SDavid Brownell 2431da177e4SLinus Torvalds if ((ret = ohci_init(ohci)) < 0) 2441da177e4SLinus Torvalds return ret; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds if ((ret = ohci_run (ohci)) < 0) { 2471da177e4SLinus Torvalds err ("can't start %s", hcd->self.bus_name); 2481da177e4SLinus Torvalds ohci_stop (hcd); 2491da177e4SLinus Torvalds return ret; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds 2521da177e4SLinus Torvalds return 0; 2531da177e4SLinus Torvalds } 2541da177e4SLinus Torvalds 2551da177e4SLinus Torvalds /*-------------------------------------------------------------------------*/ 2561da177e4SLinus Torvalds 2571da177e4SLinus Torvalds static const struct hc_driver ohci_pxa27x_hc_driver = { 2581da177e4SLinus Torvalds .description = hcd_name, 2591da177e4SLinus Torvalds .product_desc = "PXA27x OHCI", 2601da177e4SLinus Torvalds .hcd_priv_size = sizeof(struct ohci_hcd), 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds /* 2631da177e4SLinus Torvalds * generic hardware linkage 2641da177e4SLinus Torvalds */ 2651da177e4SLinus Torvalds .irq = ohci_irq, 2661da177e4SLinus Torvalds .flags = HCD_USB11 | HCD_MEMORY, 2671da177e4SLinus Torvalds 2681da177e4SLinus Torvalds /* 2691da177e4SLinus Torvalds * basic lifecycle operations 2701da177e4SLinus Torvalds */ 2711da177e4SLinus Torvalds .start = ohci_pxa27x_start, 2721da177e4SLinus Torvalds .stop = ohci_stop, 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds /* 2751da177e4SLinus Torvalds * managing i/o requests and associated device resources 2761da177e4SLinus Torvalds */ 2771da177e4SLinus Torvalds .urb_enqueue = ohci_urb_enqueue, 2781da177e4SLinus Torvalds .urb_dequeue = ohci_urb_dequeue, 2791da177e4SLinus Torvalds .endpoint_disable = ohci_endpoint_disable, 2801da177e4SLinus Torvalds 2811da177e4SLinus Torvalds /* 2821da177e4SLinus Torvalds * scheduling support 2831da177e4SLinus Torvalds */ 2841da177e4SLinus Torvalds .get_frame_number = ohci_get_frame, 2851da177e4SLinus Torvalds 2861da177e4SLinus Torvalds /* 2871da177e4SLinus Torvalds * root hub support 2881da177e4SLinus Torvalds */ 2891da177e4SLinus Torvalds .hub_status_data = ohci_hub_status_data, 2901da177e4SLinus Torvalds .hub_control = ohci_hub_control, 2918ad7fe16SDavid Brownell #ifdef CONFIG_PM 2920c0382e3SAlan Stern .bus_suspend = ohci_bus_suspend, 2930c0382e3SAlan Stern .bus_resume = ohci_bus_resume, 2941da177e4SLinus Torvalds #endif 2959293677aSDavid Brownell .start_port_reset = ohci_start_port_reset, 2961da177e4SLinus Torvalds }; 2971da177e4SLinus Torvalds 2981da177e4SLinus Torvalds /*-------------------------------------------------------------------------*/ 2991da177e4SLinus Torvalds 3003ae5eaecSRussell King static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev) 3011da177e4SLinus Torvalds { 3021da177e4SLinus Torvalds pr_debug ("In ohci_hcd_pxa27x_drv_probe"); 3031da177e4SLinus Torvalds 3041da177e4SLinus Torvalds if (usb_disabled()) 3051da177e4SLinus Torvalds return -ENODEV; 3061da177e4SLinus Torvalds 30781f280e2SRichard Purdie return usb_hcd_pxa27x_probe(&ohci_pxa27x_hc_driver, pdev); 3081da177e4SLinus Torvalds } 3091da177e4SLinus Torvalds 3103ae5eaecSRussell King static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev) 3111da177e4SLinus Torvalds { 3123ae5eaecSRussell King struct usb_hcd *hcd = platform_get_drvdata(pdev); 3131da177e4SLinus Torvalds 3141da177e4SLinus Torvalds usb_hcd_pxa27x_remove(hcd, pdev); 315a5e36d20SRichard Purdie platform_set_drvdata(pdev, NULL); 3161da177e4SLinus Torvalds return 0; 3171da177e4SLinus Torvalds } 3181da177e4SLinus Torvalds 3192e1dcc16SRichard Purdie #ifdef CONFIG_PM 3202e1dcc16SRichard Purdie static int ohci_hcd_pxa27x_drv_suspend(struct platform_device *pdev, pm_message_t state) 3211da177e4SLinus Torvalds { 322a5e36d20SRichard Purdie struct usb_hcd *hcd = platform_get_drvdata(pdev); 323a5e36d20SRichard Purdie struct ohci_hcd *ohci = hcd_to_ohci(hcd); 3242e1dcc16SRichard Purdie 3252e1dcc16SRichard Purdie if (time_before(jiffies, ohci->next_statechange)) 3262e1dcc16SRichard Purdie msleep(5); 3272e1dcc16SRichard Purdie ohci->next_statechange = jiffies; 3282e1dcc16SRichard Purdie 3292e1dcc16SRichard Purdie pxa27x_stop_hc(&pdev->dev); 330a5e36d20SRichard Purdie hcd->state = HC_STATE_SUSPENDED; 3312e1dcc16SRichard Purdie pdev->dev.power.power_state = PMSG_SUSPEND; 3321da177e4SLinus Torvalds 3331da177e4SLinus Torvalds return 0; 3341da177e4SLinus Torvalds } 3351da177e4SLinus Torvalds 3362e1dcc16SRichard Purdie static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev) 3371da177e4SLinus Torvalds { 338a5e36d20SRichard Purdie struct usb_hcd *hcd = platform_get_drvdata(pdev); 339a5e36d20SRichard Purdie struct ohci_hcd *ohci = hcd_to_ohci(hcd); 3402e1dcc16SRichard Purdie int status; 3412e1dcc16SRichard Purdie 3422e1dcc16SRichard Purdie if (time_before(jiffies, ohci->next_statechange)) 3432e1dcc16SRichard Purdie msleep(5); 3442e1dcc16SRichard Purdie ohci->next_statechange = jiffies; 3452e1dcc16SRichard Purdie 3462e1dcc16SRichard Purdie if ((status = pxa27x_start_hc(&pdev->dev)) < 0) 3472e1dcc16SRichard Purdie return status; 3482e1dcc16SRichard Purdie 3492e1dcc16SRichard Purdie pdev->dev.power.power_state = PMSG_ON; 350a5e36d20SRichard Purdie usb_hcd_resume_root_hub(hcd); 3511da177e4SLinus Torvalds 3521da177e4SLinus Torvalds return 0; 3531da177e4SLinus Torvalds } 3542e1dcc16SRichard Purdie #endif 3551da177e4SLinus Torvalds 3561da177e4SLinus Torvalds 3573ae5eaecSRussell King static struct platform_driver ohci_hcd_pxa27x_driver = { 3581da177e4SLinus Torvalds .probe = ohci_hcd_pxa27x_drv_probe, 3591da177e4SLinus Torvalds .remove = ohci_hcd_pxa27x_drv_remove, 3602e1dcc16SRichard Purdie #ifdef CONFIG_PM 3611da177e4SLinus Torvalds .suspend = ohci_hcd_pxa27x_drv_suspend, 3621da177e4SLinus Torvalds .resume = ohci_hcd_pxa27x_drv_resume, 3632e1dcc16SRichard Purdie #endif 3643ae5eaecSRussell King .driver = { 3653ae5eaecSRussell King .name = "pxa27x-ohci", 3663ae5eaecSRussell King }, 3671da177e4SLinus Torvalds }; 3681da177e4SLinus Torvalds 3691da177e4SLinus Torvalds static int __init ohci_hcd_pxa27x_init (void) 3701da177e4SLinus Torvalds { 3711da177e4SLinus Torvalds pr_debug (DRIVER_INFO " (pxa27x)"); 3721da177e4SLinus Torvalds pr_debug ("block sizes: ed %d td %d\n", 3731da177e4SLinus Torvalds sizeof (struct ed), sizeof (struct td)); 3741da177e4SLinus Torvalds 3753ae5eaecSRussell King return platform_driver_register(&ohci_hcd_pxa27x_driver); 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds static void __exit ohci_hcd_pxa27x_cleanup (void) 3791da177e4SLinus Torvalds { 3803ae5eaecSRussell King platform_driver_unregister(&ohci_hcd_pxa27x_driver); 3811da177e4SLinus Torvalds } 3821da177e4SLinus Torvalds 3831da177e4SLinus Torvalds module_init (ohci_hcd_pxa27x_init); 3841da177e4SLinus Torvalds module_exit (ohci_hcd_pxa27x_cleanup); 385