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>
# 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 void mmc_pwrseq_emmc_free ( 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
unregister_restart_handler ( & pwrseq - > reset_nb ) ;
gpiod_put ( pwrseq - > reset_gpio ) ;
kfree ( pwrseq ) ;
}
2015-11-14 20:05:20 +03:00
static const struct mmc_pwrseq_ops mmc_pwrseq_emmc_ops = {
2015-02-03 16:07:53 +03:00
. post_power_on = mmc_pwrseq_emmc_reset ,
. free = mmc_pwrseq_emmc_free ,
} ;
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 ;
}
2015-02-12 07:36:11 +03:00
struct mmc_pwrseq * mmc_pwrseq_emmc_alloc ( struct mmc_host * host ,
struct device * dev )
2015-02-03 16:07:53 +03:00
{
struct mmc_pwrseq_emmc * pwrseq ;
int ret = 0 ;
pwrseq = kzalloc ( sizeof ( struct mmc_pwrseq_emmc ) , GFP_KERNEL ) ;
if ( ! pwrseq )
2015-02-12 07:36:11 +03:00
return ERR_PTR ( - ENOMEM ) ;
2015-02-03 16:07:53 +03:00
2015-09-14 15:00:35 +03:00
pwrseq - > reset_gpio = gpiod_get ( dev , " reset " , GPIOD_OUT_LOW ) ;
2015-02-03 16:07:53 +03:00
if ( IS_ERR ( pwrseq - > reset_gpio ) ) {
ret = PTR_ERR ( pwrseq - > reset_gpio ) ;
goto free ;
}
/*
* 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 ;
2015-02-12 07:36:11 +03:00
return & pwrseq - > pwrseq ;
2015-02-03 16:07:53 +03:00
free :
kfree ( pwrseq ) ;
2015-02-12 07:36:11 +03:00
return ERR_PTR ( ret ) ;
2015-02-03 16:07:53 +03:00
}