2010-05-10 21:56:11 +05:30
/*
* ohci - omap3 . c - driver for OHCI on OMAP3 and later processors
*
* Bus Glue for OMAP3 USBHOST 3 port OHCI controller
* This controller is also used in later OMAPs and AM35x chips
*
* Copyright ( C ) 2007 - 2010 Texas Instruments , Inc .
* Author : Vikram Pandita < vikram . pandita @ ti . com >
* Author : Anand Gadiyar < gadiyar @ ti . com >
2011-03-01 20:08:21 +05:30
* Author : Keshava Munegowda < keshava_mgowda @ ti . com >
2010-05-10 21:56:11 +05:30
*
* Based on ehci - omap . c and some other ohci glue layers
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
2011-03-01 20:08:21 +05:30
* TODO ( last updated Feb 27 , 2011 ) :
2010-05-10 21:56:11 +05:30
* - add kernel - doc
*/
# include <linux/platform_device.h>
2011-10-11 13:22:11 +05:30
# include <linux/pm_runtime.h>
2010-05-10 21:56:11 +05:30
/*-------------------------------------------------------------------------*/
static int ohci_omap3_init ( struct usb_hcd * hcd )
{
dev_dbg ( hcd - > self . controller , " starting OHCI controller \n " ) ;
return ohci_init ( hcd_to_ohci ( hcd ) ) ;
}
/*-------------------------------------------------------------------------*/
static int ohci_omap3_start ( struct usb_hcd * hcd )
{
struct ohci_hcd * ohci = hcd_to_ohci ( hcd ) ;
int ret ;
/*
* RemoteWakeupConnected has to be set explicitly before
* calling ohci_run . The reset value of RWC is 0.
*/
ohci - > hc_control = OHCI_CTRL_RWC ;
writel ( OHCI_CTRL_RWC , & ohci - > regs - > control ) ;
ret = ohci_run ( ohci ) ;
if ( ret < 0 ) {
dev_err ( hcd - > self . controller , " can't start \n " ) ;
ohci_stop ( hcd ) ;
}
return ret ;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_omap3_hc_driver = {
. description = hcd_name ,
. product_desc = " OMAP3 OHCI Host Controller " ,
. hcd_priv_size = sizeof ( struct ohci_hcd ) ,
/*
* generic hardware linkage
*/
. irq = ohci_irq ,
. flags = HCD_USB11 | HCD_MEMORY ,
/*
* basic lifecycle operations
*/
. reset = ohci_omap3_init ,
. start = ohci_omap3_start ,
. stop = ohci_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 ,
} ;
/*-------------------------------------------------------------------------*/
/*
* configure so an HC device and id are always provided
* always called with process context ; sleeping is OK
*/
/**
* ohci_hcd_omap3_probe - initialize OMAP - based HCDs
*
* 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 .
*/
2012-11-19 13:21:48 -05:00
static int ohci_hcd_omap3_probe ( struct platform_device * pdev )
2010-05-10 21:56:11 +05:30
{
2011-03-01 20:08:21 +05:30
struct device * dev = & pdev - > dev ;
struct usb_hcd * hcd = NULL ;
void __iomem * regs = NULL ;
struct resource * res ;
int ret = - ENODEV ;
int irq ;
2010-05-10 21:56:11 +05:30
if ( usb_disabled ( ) )
2011-10-11 13:22:11 +05:30
return - ENODEV ;
2010-05-10 21:56:11 +05:30
2011-03-01 20:08:21 +05:30
if ( ! dev - > parent ) {
dev_err ( dev , " Missing parent device \n " ) ;
return - ENODEV ;
2010-05-10 21:56:11 +05:30
}
2011-03-01 20:08:21 +05:30
irq = platform_get_irq_byname ( pdev , " ohci-irq " ) ;
if ( irq < 0 ) {
dev_err ( dev , " OHCI irq failed \n " ) ;
return - ENODEV ;
}
2010-05-10 21:56:11 +05:30
2011-03-01 20:08:21 +05:30
res = platform_get_resource_byname ( pdev ,
IORESOURCE_MEM , " ohci " ) ;
2011-08-22 16:00:35 +02:00
if ( ! res ) {
2011-03-01 20:08:21 +05:30
dev_err ( dev , " UHH OHCI get resource failed \n " ) ;
return - ENOMEM ;
2010-05-10 21:56:11 +05:30
}
2011-03-01 20:08:21 +05:30
regs = ioremap ( res - > start , resource_size ( res ) ) ;
if ( ! regs ) {
dev_err ( dev , " UHH OHCI ioremap failed \n " ) ;
return - ENOMEM ;
2010-05-10 21:56:11 +05:30
}
2011-03-01 20:08:21 +05:30
hcd = usb_create_hcd ( & ohci_omap3_hc_driver , dev ,
dev_name ( dev ) ) ;
if ( ! hcd ) {
dev_err ( dev , " usb_create_hcd failed \n " ) ;
goto err_io ;
}
2010-05-10 21:56:11 +05:30
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2011-03-01 20:08:21 +05:30
hcd - > regs = regs ;
2010-05-10 21:56:11 +05:30
2011-10-11 13:22:11 +05:30
pm_runtime_enable ( dev ) ;
pm_runtime_get_sync ( dev ) ;
2010-05-10 21:56:11 +05:30
2011-03-01 20:08:21 +05:30
ohci_hcd_init ( hcd_to_ohci ( hcd ) ) ;
2010-05-10 21:56:11 +05:30
2011-09-07 16:10:52 +08:00
ret = usb_add_hcd ( hcd , irq , 0 ) ;
2010-05-10 21:56:11 +05:30
if ( ret ) {
2011-03-01 20:08:21 +05:30
dev_dbg ( dev , " failed to add hcd with err %d \n " , ret ) ;
2010-05-10 21:56:11 +05:30
goto err_add_hcd ;
}
return 0 ;
err_add_hcd :
2011-10-11 13:22:11 +05:30
pm_runtime_put_sync ( dev ) ;
2010-05-10 21:56:11 +05:30
usb_put_hcd ( hcd ) ;
2011-03-01 20:08:21 +05:30
err_io :
iounmap ( regs ) ;
2010-05-10 21:56:11 +05:30
return ret ;
}
/*
* may be called without controller electrically present
* may be called with controller , bus , and devices active
*/
/**
* ohci_hcd_omap3_remove - shutdown processing for OHCI HCDs
* @ pdev : USB Host Controller being removed
*
* Reverses the effect of ohci_hcd_omap3_probe ( ) , first invoking
* the HCD ' s stop ( ) method . It is always called from a thread
* context , normally " rmmod " , " apmd " , or something similar .
*/
2012-11-19 13:26:20 -05:00
static int ohci_hcd_omap3_remove ( struct platform_device * pdev )
2010-05-10 21:56:11 +05:30
{
2011-03-01 20:08:21 +05:30
struct device * dev = & pdev - > dev ;
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2010-05-10 21:56:11 +05:30
iounmap ( hcd - > regs ) ;
2011-03-01 20:08:21 +05:30
usb_remove_hcd ( hcd ) ;
2011-10-11 13:22:11 +05:30
pm_runtime_put_sync ( dev ) ;
pm_runtime_disable ( dev ) ;
2010-05-10 21:56:11 +05:30
usb_put_hcd ( hcd ) ;
return 0 ;
}
static void ohci_hcd_omap3_shutdown ( struct platform_device * pdev )
{
2011-03-01 20:08:21 +05:30
struct usb_hcd * hcd = dev_get_drvdata ( & pdev - > dev ) ;
2010-05-10 21:56:11 +05:30
if ( hcd - > driver - > shutdown )
hcd - > driver - > shutdown ( hcd ) ;
}
static struct platform_driver ohci_hcd_omap3_driver = {
. probe = ohci_hcd_omap3_probe ,
2012-11-19 13:21:08 -05:00
. remove = ohci_hcd_omap3_remove ,
2010-05-10 21:56:11 +05:30
. shutdown = ohci_hcd_omap3_shutdown ,
. driver = {
. name = " ohci-omap3 " ,
} ,
} ;
MODULE_ALIAS ( " platform:ohci-omap3 " ) ;
MODULE_AUTHOR ( " Anand Gadiyar <gadiyar@ti.com> " ) ;