2013-02-26 16:34:45 +04:00
/*
* Universal Flash Storage Host controller Platform bus based glue driver
*
* This code is based on drivers / scsi / ufs / ufshcd - pltfrm . c
* Copyright ( C ) 2011 - 2013 Samsung India Software Operations
*
* Authors :
* Santosh Yaraganavi < santosh . sy @ samsung . com >
* Vinayak Holikatti < h . vinayak @ samsung . com >
*
* 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 .
* See the COPYING file in the top - level directory or visit
* < http : //www.gnu.org/licenses/gpl-2.0.html>
*
* 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 .
*
* This program is provided " AS IS " and " WITH ALL FAULTS " and
* without warranty of any kind . You are solely responsible for
* determining the appropriateness of using and distributing
* the program and assume all risks associated with your exercise
* of rights with respect to the program , including but not limited
* to infringement of third party rights , the risks and costs of
* program errors , damage to or loss of data , programs or equipment ,
* and unavailability or interruption of operations . Under no
* circumstances will the contributor of this Program be liable for
* any damages of any kind arising from your use or distribution of
* this program .
*/
# include <linux/platform_device.h>
2013-07-29 23:06:00 +04:00
# include <linux/pm_runtime.h>
2014-09-25 16:32:21 +04:00
# include <linux/of.h>
2013-02-26 16:34:45 +04:00
2013-06-27 08:31:54 +04:00
# include "ufshcd.h"
2014-09-25 16:32:21 +04:00
static const struct of_device_id ufs_of_match [ ] ;
static struct ufs_hba_variant_ops * get_variant_ops ( struct device * dev )
{
if ( dev - > of_node ) {
const struct of_device_id * match ;
match = of_match_node ( ufs_of_match , dev - > of_node ) ;
if ( match )
return ( struct ufs_hba_variant_ops * ) match - > data ;
}
return NULL ;
}
2013-02-26 16:34:45 +04:00
# ifdef CONFIG_PM
/**
* ufshcd_pltfrm_suspend - suspend power management function
* @ dev : pointer to device handle
*
*
* Returns 0
*/
static int ufshcd_pltfrm_suspend ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct ufs_hba * hba = platform_get_drvdata ( pdev ) ;
/*
* TODO :
* 1. Call ufshcd_suspend
* 2. Do bus specific power management
*/
disable_irq ( hba - > irq ) ;
return 0 ;
}
/**
* ufshcd_pltfrm_resume - resume power management function
* @ dev : pointer to device handle
*
* Returns 0
*/
static int ufshcd_pltfrm_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct ufs_hba * hba = platform_get_drvdata ( pdev ) ;
/*
* TODO :
* 1. Call ufshcd_resume .
* 2. Do bus specific wake up
*/
enable_irq ( hba - > irq ) ;
return 0 ;
}
# else
# define ufshcd_pltfrm_suspend NULL
# define ufshcd_pltfrm_resume NULL
# endif
2013-07-29 23:06:00 +04:00
# ifdef CONFIG_PM_RUNTIME
static int ufshcd_pltfrm_runtime_suspend ( struct device * dev )
{
struct ufs_hba * hba = dev_get_drvdata ( dev ) ;
if ( ! hba )
return 0 ;
return ufshcd_runtime_suspend ( hba ) ;
}
static int ufshcd_pltfrm_runtime_resume ( struct device * dev )
{
struct ufs_hba * hba = dev_get_drvdata ( dev ) ;
if ( ! hba )
return 0 ;
return ufshcd_runtime_resume ( hba ) ;
}
static int ufshcd_pltfrm_runtime_idle ( struct device * dev )
{
struct ufs_hba * hba = dev_get_drvdata ( dev ) ;
if ( ! hba )
return 0 ;
return ufshcd_runtime_idle ( hba ) ;
}
# else /* !CONFIG_PM_RUNTIME */
# define ufshcd_pltfrm_runtime_suspend NULL
# define ufshcd_pltfrm_runtime_resume NULL
# define ufshcd_pltfrm_runtime_idle NULL
# endif /* CONFIG_PM_RUNTIME */
2013-02-26 16:34:45 +04:00
/**
* ufshcd_pltfrm_probe - probe routine of the driver
* @ pdev : pointer to Platform device handle
*
* Returns 0 on success , non - zero value on failure
*/
static int ufshcd_pltfrm_probe ( struct platform_device * pdev )
{
struct ufs_hba * hba ;
void __iomem * mmio_base ;
struct resource * mem_res ;
2013-06-27 08:31:54 +04:00
int irq , err ;
2013-02-26 16:34:45 +04:00
struct device * dev = & pdev - > dev ;
mem_res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
2013-06-27 08:31:54 +04:00
mmio_base = devm_ioremap_resource ( dev , mem_res ) ;
2014-09-25 16:32:21 +04:00
if ( IS_ERR ( * ( void * * ) & mmio_base ) ) {
err = PTR_ERR ( * ( void * * ) & mmio_base ) ;
2013-06-27 08:31:54 +04:00
goto out ;
2013-02-26 16:34:45 +04:00
}
2013-06-27 08:31:54 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
dev_err ( dev , " IRQ resource not available \n " ) ;
2013-02-26 16:34:45 +04:00
err = - ENODEV ;
2013-06-27 08:31:54 +04:00
goto out ;
2013-02-26 16:34:45 +04:00
}
2014-09-25 16:32:21 +04:00
err = ufshcd_alloc_host ( dev , & hba ) ;
if ( err ) {
dev_err ( & pdev - > dev , " Allocation failed \n " ) ;
goto out ;
}
hba - > vops = get_variant_ops ( & pdev - > dev ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2014-09-25 16:32:21 +04:00
err = ufshcd_init ( hba , mmio_base , irq ) ;
2013-02-26 16:34:45 +04:00
if ( err ) {
2013-06-27 08:31:54 +04:00
dev_err ( dev , " Intialization failed \n " ) ;
2013-07-29 23:06:00 +04:00
goto out_disable_rpm ;
2013-02-26 16:34:45 +04:00
}
platform_set_drvdata ( pdev , hba ) ;
2013-07-29 23:06:00 +04:00
return 0 ;
out_disable_rpm :
pm_runtime_disable ( & pdev - > dev ) ;
pm_runtime_set_suspended ( & pdev - > dev ) ;
2013-06-27 08:31:54 +04:00
out :
2013-02-26 16:34:45 +04:00
return err ;
}
/**
* ufshcd_pltfrm_remove - remove platform driver routine
* @ pdev : pointer to platform device handle
*
* Returns 0 on success , non - zero value on failure
*/
static int ufshcd_pltfrm_remove ( struct platform_device * pdev )
{
struct ufs_hba * hba = platform_get_drvdata ( pdev ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_get_sync ( & ( pdev ) - > dev ) ;
2013-02-26 16:34:45 +04:00
ufshcd_remove ( hba ) ;
return 0 ;
}
static const struct of_device_id ufs_of_match [ ] = {
{ . compatible = " jedec,ufs-1.1 " } ,
2013-06-26 21:09:32 +04:00
{ } ,
2013-02-26 16:34:45 +04:00
} ;
static const struct dev_pm_ops ufshcd_dev_pm_ops = {
. suspend = ufshcd_pltfrm_suspend ,
. resume = ufshcd_pltfrm_resume ,
2013-07-29 23:06:00 +04:00
. runtime_suspend = ufshcd_pltfrm_runtime_suspend ,
. runtime_resume = ufshcd_pltfrm_runtime_resume ,
. runtime_idle = ufshcd_pltfrm_runtime_idle ,
2013-02-26 16:34:45 +04:00
} ;
static struct platform_driver ufshcd_pltfrm_driver = {
. probe = ufshcd_pltfrm_probe ,
. remove = ufshcd_pltfrm_remove ,
. driver = {
. name = " ufshcd " ,
. owner = THIS_MODULE ,
. pm = & ufshcd_dev_pm_ops ,
. of_match_table = ufs_of_match ,
} ,
} ;
module_platform_driver ( ufshcd_pltfrm_driver ) ;
MODULE_AUTHOR ( " Santosh Yaragnavi <santosh.sy@samsung.com> " ) ;
MODULE_AUTHOR ( " Vinayak Holikatti <h.vinayak@samsung.com> " ) ;
MODULE_DESCRIPTION ( " UFS host controller Pltform bus based glue driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( UFSHCD_DRIVER_VERSION ) ;