2010-12-07 17:53:56 +05:30
/* ehci-msm.c - HSUSB Host Controller Driver Implementation
*
2011-02-18 17:30:11 +05:30
* Copyright ( c ) 2008 - 2011 , Code Aurora Forum . All rights reserved .
2010-12-07 17:53:56 +05:30
*
* Partly derived from ehci - fsl . c and ehci - hcd . c
* Copyright ( c ) 2000 - 2004 by David Brownell
* Copyright ( c ) 2005 MontaVista Software
*
* All source code in this file is licensed under the following license except
* where indicated .
*
* This program 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 .
*
* 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 , you can find it at http : //www.fsf.org
*/
# include <linux/clk.h>
# include <linux/err.h>
2013-04-02 18:24:03 +02:00
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2010-12-07 17:53:57 +05:30
# include <linux/pm_runtime.h>
2010-12-07 17:53:56 +05:30
# include <linux/usb/otg.h>
# include <linux/usb/msm_hsusb_hw.h>
2013-04-02 18:24:03 +02:00
# include <linux/usb.h>
# include <linux/usb/hcd.h>
2015-12-10 18:28:55 -06:00
# include <linux/acpi.h>
2013-04-02 18:24:03 +02:00
# include "ehci.h"
2010-12-07 17:53:56 +05:30
# define MSM_USB_BASE (hcd->regs)
2013-04-02 18:24:03 +02:00
# define DRIVER_DESC "Qualcomm On-Chip EHCI Host Controller"
static const char hcd_name [ ] = " ehci-msm " ;
static struct hc_driver __read_mostly msm_hc_driver ;
2010-12-07 17:53:56 +05:30
static int ehci_msm_reset ( struct usb_hcd * hcd )
{
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
int retval ;
ehci - > caps = USB_CAPLENGTH ;
hcd - > has_tt = 1 ;
2011-07-02 20:59:46 +02:00
retval = ehci_setup ( hcd ) ;
2010-12-07 17:53:56 +05:30
if ( retval )
return retval ;
2015-12-10 18:28:56 -06:00
/* select ULPI phy and clear other status/control bits in PORTSC */
writel ( PORTSC_PTS_ULPI , USB_PORTSC ) ;
2010-12-07 17:53:56 +05:30
/* bursts of unspecified length. */
writel ( 0 , USB_AHBBURST ) ;
2015-11-06 00:04:07 -06:00
/* Use the AHB transactor, allow posted data writes */
writel ( 0x8 , USB_AHBMODE ) ;
2010-12-07 17:53:56 +05:30
/* Disable streaming mode and select host mode */
writel ( 0x13 , USB_USBMODE ) ;
2015-12-10 18:28:53 -06:00
/* Disable ULPI_TX_PKT_EN_CLR_FIX which is valid only for HSIC */
writel ( readl ( USB_GENCONFIG_2 ) & ~ ULPI_TX_PKT_EN_CLR_FIX , USB_GENCONFIG_2 ) ;
2010-12-07 17:53:56 +05:30
return 0 ;
}
static int ehci_msm_probe ( struct platform_device * pdev )
{
struct usb_hcd * hcd ;
struct resource * res ;
2013-10-11 14:46:09 +03:00
struct usb_phy * phy ;
2010-12-07 17:53:56 +05:30
int ret ;
dev_dbg ( & pdev - > dev , " ehci_msm proble \n " ) ;
hcd = usb_create_hcd ( & msm_hc_driver , & pdev - > dev , dev_name ( & pdev - > dev ) ) ;
if ( ! hcd ) {
dev_err ( & pdev - > dev , " Unable to create HCD \n " ) ;
return - ENOMEM ;
}
2015-09-21 15:33:39 +02:00
ret = platform_get_irq ( pdev , 0 ) ;
if ( ret < 0 ) {
2010-12-07 17:53:56 +05:30
dev_err ( & pdev - > dev , " Unable to get IRQ resource \n " ) ;
goto put_hcd ;
}
2015-09-21 15:33:39 +02:00
hcd - > irq = ret ;
2010-12-07 17:53:56 +05:30
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2015-04-27 15:27:36 +03:00
if ( ! res ) {
dev_err ( & pdev - > dev , " Unable to get memory resource \n " ) ;
ret = - ENODEV ;
2010-12-07 17:53:56 +05:30
goto put_hcd ;
}
2015-04-27 15:27:36 +03:00
2014-11-04 07:51:20 +05:30
hcd - > rsrc_start = res - > start ;
hcd - > rsrc_len = resource_size ( res ) ;
2015-04-27 15:27:36 +03:00
hcd - > regs = devm_ioremap ( & pdev - > dev , hcd - > rsrc_start , hcd - > rsrc_len ) ;
if ( ! hcd - > regs ) {
dev_err ( & pdev - > dev , " ioremap failed \n " ) ;
ret = - ENOMEM ;
goto put_hcd ;
}
2010-12-07 17:53:56 +05:30
/*
2015-12-10 18:28:54 -06:00
* If there is an OTG driver , let it take care of PHY initialization ,
* clock management , powering up VBUS , mapping of registers address
* space and power management .
2010-12-07 17:53:56 +05:30
*/
2013-10-11 14:46:10 +03:00
if ( pdev - > dev . of_node )
phy = devm_usb_get_phy_by_phandle ( & pdev - > dev , " usb-phy " , 0 ) ;
else
phy = devm_usb_get_phy ( & pdev - > dev , USB_PHY_TYPE_USB2 ) ;
2013-03-15 11:02:56 +02:00
if ( IS_ERR ( phy ) ) {
2015-12-10 18:28:54 -06:00
if ( PTR_ERR ( phy ) = = - EPROBE_DEFER ) {
dev_err ( & pdev - > dev , " unable to find transceiver \n " ) ;
ret = - EPROBE_DEFER ;
goto put_hcd ;
}
phy = NULL ;
2010-12-07 17:53:56 +05:30
}
2014-09-24 23:05:50 +04:00
hcd - > usb_phy = phy ;
2010-12-07 17:53:56 +05:30
device_init_wakeup ( & pdev - > dev , 1 ) ;
2010-12-07 17:53:57 +05:30
2015-12-10 18:28:54 -06:00
if ( phy & & phy - > otg ) {
/*
* MSM OTG driver takes care of adding the HCD and
* placing hardware into low power mode via runtime PM .
*/
ret = otg_set_host ( phy - > otg , & hcd - > self ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " unable to register with transceiver \n " ) ;
goto put_hcd ;
}
pm_runtime_no_callbacks ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
} else {
ret = usb_add_hcd ( hcd , hcd - > irq , IRQF_SHARED ) ;
if ( ret )
goto put_hcd ;
}
2013-04-02 18:24:03 +02:00
2010-12-07 17:53:56 +05:30
return 0 ;
put_hcd :
usb_put_hcd ( hcd ) ;
return ret ;
}
2012-11-19 13:26:20 -05:00
static int ehci_msm_remove ( struct platform_device * pdev )
2010-12-07 17:53:56 +05:30
{
struct usb_hcd * hcd = platform_get_drvdata ( pdev ) ;
device_init_wakeup ( & pdev - > dev , 0 ) ;
2010-12-07 17:53:57 +05:30
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
2010-12-07 17:53:56 +05:30
2015-12-10 18:28:54 -06:00
if ( hcd - > usb_phy & & hcd - > usb_phy - > otg )
otg_set_host ( hcd - > usb_phy - > otg , NULL ) ;
else
usb_remove_hcd ( hcd ) ;
2013-04-02 18:24:03 +02:00
2010-12-07 17:53:56 +05:30
usb_put_hcd ( hcd ) ;
return 0 ;
}
2010-12-07 17:53:57 +05:30
# ifdef CONFIG_PM
static int ehci_msm_pm_suspend ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2016-05-20 16:35:07 -05:00
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
2012-06-28 11:19:02 -04:00
bool do_wakeup = device_may_wakeup ( dev ) ;
2010-12-07 17:53:57 +05:30
dev_dbg ( dev , " ehci-msm PM suspend \n " ) ;
2016-05-20 16:35:07 -05:00
/* Only call ehci_suspend if ehci_setup has been done */
if ( ehci - > sbrn )
return ehci_suspend ( hcd , do_wakeup ) ;
return 0 ;
2010-12-07 17:53:57 +05:30
}
static int ehci_msm_pm_resume ( struct device * dev )
{
struct usb_hcd * hcd = dev_get_drvdata ( dev ) ;
2016-05-20 16:35:07 -05:00
struct ehci_hcd * ehci = hcd_to_ehci ( hcd ) ;
2010-12-07 17:53:57 +05:30
dev_dbg ( dev , " ehci-msm PM resume \n " ) ;
2016-05-20 16:35:07 -05:00
/* Only call ehci_resume if ehci_setup has been done */
if ( ehci - > sbrn )
ehci_resume ( hcd , false ) ;
2010-12-07 17:53:57 +05:30
return 0 ;
}
2016-05-20 16:35:07 -05:00
2010-12-07 17:53:57 +05:30
# else
# define ehci_msm_pm_suspend NULL
# define ehci_msm_pm_resume NULL
# endif
static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
. suspend = ehci_msm_pm_suspend ,
. resume = ehci_msm_pm_resume ,
} ;
2015-12-10 18:28:55 -06:00
static const struct acpi_device_id msm_ehci_acpi_ids [ ] = {
{ " QCOM8040 " , 0 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( acpi , msm_ehci_acpi_ids ) ;
2014-06-18 13:33:17 +09:00
static const struct of_device_id msm_ehci_dt_match [ ] = {
2013-10-11 14:46:10 +03:00
{ . compatible = " qcom,ehci-host " , } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , msm_ehci_dt_match ) ;
2010-12-07 17:53:56 +05:30
static struct platform_driver ehci_msm_driver = {
. probe = ehci_msm_probe ,
2012-11-19 13:21:08 -05:00
. remove = ehci_msm_remove ,
2015-12-10 18:28:57 -06:00
. shutdown = usb_hcd_platform_shutdown ,
2010-12-07 17:53:56 +05:30
. driver = {
. name = " msm_hsusb_host " ,
2010-12-07 17:53:57 +05:30
. pm = & ehci_msm_dev_pm_ops ,
2013-10-11 14:46:10 +03:00
. of_match_table = msm_ehci_dt_match ,
2015-12-10 18:28:55 -06:00
. acpi_match_table = ACPI_PTR ( msm_ehci_acpi_ids ) ,
2010-12-07 17:53:56 +05:30
} ,
} ;
2013-04-02 18:24:03 +02:00
2016-04-27 13:28:32 -04:00
static const struct ehci_driver_overrides msm_overrides __initconst = {
2013-04-02 18:24:03 +02:00
. reset = ehci_msm_reset ,
} ;
static int __init ehci_msm_init ( void )
{
if ( usb_disabled ( ) )
return - ENODEV ;
pr_info ( " %s: " DRIVER_DESC " \n " , hcd_name ) ;
ehci_init_driver ( & msm_hc_driver , & msm_overrides ) ;
return platform_driver_register ( & ehci_msm_driver ) ;
}
module_init ( ehci_msm_init ) ;
static void __exit ehci_msm_cleanup ( void )
{
platform_driver_unregister ( & ehci_msm_driver ) ;
}
module_exit ( ehci_msm_cleanup ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_ALIAS ( " platform:msm-ehci " ) ;
MODULE_LICENSE ( " GPL " ) ;