2015-02-03 16:07:53 +03:00
/*
* Copyright ( C ) 2015 , Samsung Electronics Co . , Ltd .
*
* Author : Marek Szyprowski < m . szyprowski @ samsung . com >
*
* License terms : GNU General Public License ( GPL ) version 2
*
* Simple eMMC hardware reset provider
*/
# include <linux/delay.h>
# include <linux/kernel.h>
2016-04-14 16:02:16 +03:00
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/module.h>
2015-02-03 16:07:53 +03:00
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/err.h>
# include <linux/gpio/consumer.h>
# include <linux/reboot.h>
# include <linux/mmc/host.h>
# include "pwrseq.h"
struct mmc_pwrseq_emmc {
struct mmc_pwrseq pwrseq ;
struct notifier_block reset_nb ;
struct gpio_desc * reset_gpio ;
} ;
2016-04-14 16:02:15 +03:00
# define to_pwrseq_emmc(p) container_of(p, struct mmc_pwrseq_emmc, pwrseq)
2015-02-03 16:07:53 +03:00
static void __mmc_pwrseq_emmc_reset ( struct mmc_pwrseq_emmc * pwrseq )
{
gpiod_set_value ( pwrseq - > reset_gpio , 1 ) ;
udelay ( 1 ) ;
gpiod_set_value ( pwrseq - > reset_gpio , 0 ) ;
udelay ( 200 ) ;
}
static void mmc_pwrseq_emmc_reset ( struct mmc_host * host )
{
2016-04-14 16:02:15 +03:00
struct mmc_pwrseq_emmc * pwrseq = to_pwrseq_emmc ( host - > pwrseq ) ;
2015-02-03 16:07:53 +03:00
__mmc_pwrseq_emmc_reset ( pwrseq ) ;
}
static int mmc_pwrseq_emmc_reset_nb ( struct notifier_block * this ,
unsigned long mode , void * cmd )
{
struct mmc_pwrseq_emmc * pwrseq = container_of ( this ,
struct mmc_pwrseq_emmc , reset_nb ) ;
__mmc_pwrseq_emmc_reset ( pwrseq ) ;
return NOTIFY_DONE ;
}
2016-04-14 16:02:16 +03:00
static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
. post_power_on = mmc_pwrseq_emmc_reset ,
} ;
static int mmc_pwrseq_emmc_probe ( struct platform_device * pdev )
2015-02-03 16:07:53 +03:00
{
struct mmc_pwrseq_emmc * pwrseq ;
2016-04-14 16:02:16 +03:00
struct device * dev = & pdev - > dev ;
2015-02-03 16:07:53 +03:00
2016-04-14 16:02:16 +03:00
pwrseq = devm_kzalloc ( dev , sizeof ( * pwrseq ) , GFP_KERNEL ) ;
2015-02-03 16:07:53 +03:00
if ( ! pwrseq )
2016-04-14 16:02:16 +03:00
return - ENOMEM ;
2015-02-03 16:07:53 +03:00
2016-04-14 16:02:16 +03:00
pwrseq - > reset_gpio = devm_gpiod_get ( dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( pwrseq - > reset_gpio ) )
return PTR_ERR ( pwrseq - > reset_gpio ) ;
2015-02-03 16:07:53 +03:00
/*
* register reset handler to ensure emmc reset also from
2015-10-21 18:15:40 +03:00
* emergency_reboot ( ) , priority 255 is the highest priority
* so it will be executed before any system reboot handler .
2015-02-03 16:07:53 +03:00
*/
pwrseq - > reset_nb . notifier_call = mmc_pwrseq_emmc_reset_nb ;
2015-10-21 18:15:40 +03:00
pwrseq - > reset_nb . priority = 255 ;
2015-02-03 16:07:53 +03:00
register_restart_handler ( & pwrseq - > reset_nb ) ;
pwrseq - > pwrseq . ops = & mmc_pwrseq_emmc_ops ;
2016-04-14 16:02:16 +03:00
pwrseq - > pwrseq . dev = dev ;
pwrseq - > pwrseq . owner = THIS_MODULE ;
platform_set_drvdata ( pdev , pwrseq ) ;
return mmc_pwrseq_register ( & pwrseq - > pwrseq ) ;
}
static int mmc_pwrseq_emmc_remove ( struct platform_device * pdev )
{
struct mmc_pwrseq_emmc * pwrseq = platform_get_drvdata ( pdev ) ;
unregister_restart_handler ( & pwrseq - > reset_nb ) ;
mmc_pwrseq_unregister ( & pwrseq - > pwrseq ) ;
2015-02-03 16:07:53 +03:00
2016-04-14 16:02:16 +03:00
return 0 ;
2015-02-03 16:07:53 +03:00
}
2016-04-14 16:02:16 +03:00
static const struct of_device_id mmc_pwrseq_emmc_of_match [ ] = {
{ . compatible = " mmc-pwrseq-emmc " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , mmc_pwrseq_emmc_of_match ) ;
static struct platform_driver mmc_pwrseq_emmc_driver = {
. probe = mmc_pwrseq_emmc_probe ,
. remove = mmc_pwrseq_emmc_remove ,
. driver = {
. name = " pwrseq_emmc " ,
. of_match_table = mmc_pwrseq_emmc_of_match ,
} ,
} ;
module_platform_driver ( mmc_pwrseq_emmc_driver ) ;
MODULE_LICENSE ( " GPL v2 " ) ;