2010-10-21 19:55:01 +04:00
/*
* Copyright ( C ) 2010 , Lars - Peter Clausen < lars @ metafoo . de >
* Driver for chargers which report their online status through a GPIO pin
*
* 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 .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/device.h>
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <linux/power_supply.h>
# include <linux/slab.h>
# include <linux/power/gpio-charger.h>
struct gpio_charger {
const struct gpio_charger_platform_data * pdata ;
unsigned int irq ;
2013-12-11 02:47:16 +04:00
bool wakeup_enabled ;
2010-10-21 19:55:01 +04:00
struct power_supply charger ;
} ;
static irqreturn_t gpio_charger_irq ( int irq , void * devid )
{
struct power_supply * charger = devid ;
power_supply_changed ( charger ) ;
return IRQ_HANDLED ;
}
static inline struct gpio_charger * psy_to_gpio_charger ( struct power_supply * psy )
{
return container_of ( psy , struct gpio_charger , charger ) ;
}
static int gpio_charger_get_property ( struct power_supply * psy ,
enum power_supply_property psp , union power_supply_propval * val )
{
struct gpio_charger * gpio_charger = psy_to_gpio_charger ( psy ) ;
const struct gpio_charger_platform_data * pdata = gpio_charger - > pdata ;
switch ( psp ) {
case POWER_SUPPLY_PROP_ONLINE :
2012-05-22 12:50:04 +04:00
val - > intval = gpio_get_value_cansleep ( pdata - > gpio ) ;
2010-10-21 19:55:01 +04:00
val - > intval ^ = pdata - > gpio_active_low ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
static enum power_supply_property gpio_charger_properties [ ] = {
POWER_SUPPLY_PROP_ONLINE ,
} ;
2012-11-19 22:22:23 +04:00
static int gpio_charger_probe ( struct platform_device * pdev )
2010-10-21 19:55:01 +04:00
{
const struct gpio_charger_platform_data * pdata = pdev - > dev . platform_data ;
struct gpio_charger * gpio_charger ;
struct power_supply * charger ;
int ret ;
int irq ;
if ( ! pdata ) {
dev_err ( & pdev - > dev , " No platform data \n " ) ;
return - EINVAL ;
}
if ( ! gpio_is_valid ( pdata - > gpio ) ) {
dev_err ( & pdev - > dev , " Invalid gpio pin \n " ) ;
return - EINVAL ;
}
2013-03-11 10:33:27 +04:00
gpio_charger = devm_kzalloc ( & pdev - > dev , sizeof ( * gpio_charger ) ,
GFP_KERNEL ) ;
2010-11-19 01:08:37 +03:00
if ( ! gpio_charger ) {
dev_err ( & pdev - > dev , " Failed to alloc driver structure \n " ) ;
return - ENOMEM ;
}
2010-10-21 19:55:01 +04:00
charger = & gpio_charger - > charger ;
2010-11-19 01:08:39 +03:00
charger - > name = pdata - > name ? pdata - > name : " gpio-charger " ;
2010-10-21 19:55:01 +04:00
charger - > type = pdata - > type ;
charger - > properties = gpio_charger_properties ;
charger - > num_properties = ARRAY_SIZE ( gpio_charger_properties ) ;
2010-11-19 01:08:38 +03:00
charger - > get_property = gpio_charger_get_property ;
2010-10-21 19:55:01 +04:00
charger - > supplied_to = pdata - > supplied_to ;
charger - > num_supplicants = pdata - > num_supplicants ;
ret = gpio_request ( pdata - > gpio , dev_name ( & pdev - > dev ) ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to request gpio pin: %d \n " , ret ) ;
goto err_free ;
}
ret = gpio_direction_input ( pdata - > gpio ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Failed to set gpio to input: %d \n " , ret ) ;
goto err_gpio_free ;
}
2010-11-19 01:08:38 +03:00
gpio_charger - > pdata = pdata ;
ret = power_supply_register ( & pdev - > dev , charger ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Failed to register power supply: %d \n " ,
ret ) ;
goto err_gpio_free ;
}
2010-10-21 19:55:01 +04:00
irq = gpio_to_irq ( pdata - > gpio ) ;
if ( irq > 0 ) {
2010-11-19 01:08:38 +03:00
ret = request_any_context_irq ( irq , gpio_charger_irq ,
2010-10-21 19:55:01 +04:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
dev_name ( & pdev - > dev ) , charger ) ;
2011-07-04 14:04:15 +04:00
if ( ret < 0 )
2010-10-21 19:55:01 +04:00
dev_warn ( & pdev - > dev , " Failed to request irq: %d \n " , ret ) ;
else
gpio_charger - > irq = irq ;
}
platform_set_drvdata ( pdev , gpio_charger ) ;
2013-12-11 02:47:16 +04:00
device_init_wakeup ( & pdev - > dev , 1 ) ;
2010-10-21 19:55:01 +04:00
return 0 ;
err_gpio_free :
gpio_free ( pdata - > gpio ) ;
err_free :
return ret ;
}
2012-11-19 22:26:07 +04:00
static int gpio_charger_remove ( struct platform_device * pdev )
2010-10-21 19:55:01 +04:00
{
struct gpio_charger * gpio_charger = platform_get_drvdata ( pdev ) ;
if ( gpio_charger - > irq )
free_irq ( gpio_charger - > irq , & gpio_charger - > charger ) ;
2010-11-19 01:08:38 +03:00
power_supply_unregister ( & gpio_charger - > charger ) ;
2010-10-21 19:55:01 +04:00
gpio_free ( gpio_charger - > pdata - > gpio ) ;
return 0 ;
}
2011-04-07 03:55:20 +04:00
# ifdef CONFIG_PM_SLEEP
2013-12-11 02:47:16 +04:00
static int gpio_charger_suspend ( struct device * dev )
{
struct gpio_charger * gpio_charger = dev_get_drvdata ( dev ) ;
if ( device_may_wakeup ( dev ) )
gpio_charger - > wakeup_enabled =
enable_irq_wake ( gpio_charger - > irq ) ;
return 0 ;
}
2011-04-07 03:55:20 +04:00
static int gpio_charger_resume ( struct device * dev )
{
struct platform_device * pdev = to_platform_device ( dev ) ;
struct gpio_charger * gpio_charger = platform_get_drvdata ( pdev ) ;
2013-12-11 02:47:16 +04:00
if ( gpio_charger - > wakeup_enabled )
disable_irq_wake ( gpio_charger - > irq ) ;
2011-04-07 03:55:20 +04:00
power_supply_changed ( & gpio_charger - > charger ) ;
return 0 ;
}
# endif
2013-12-11 02:47:16 +04:00
static SIMPLE_DEV_PM_OPS ( gpio_charger_pm_ops ,
gpio_charger_suspend , gpio_charger_resume ) ;
2011-04-07 03:55:20 +04:00
2010-10-21 19:55:01 +04:00
static struct platform_driver gpio_charger_driver = {
. probe = gpio_charger_probe ,
2012-11-19 22:20:40 +04:00
. remove = gpio_charger_remove ,
2010-10-21 19:55:01 +04:00
. driver = {
. name = " gpio-charger " ,
. owner = THIS_MODULE ,
2011-04-07 03:55:20 +04:00
. pm = & gpio_charger_pm_ops ,
2010-10-21 19:55:01 +04:00
} ,
} ;
2011-11-26 08:01:10 +04:00
module_platform_driver ( gpio_charger_driver ) ;
2010-10-21 19:55:01 +04:00
MODULE_AUTHOR ( " Lars-Peter Clausen <lars@metafoo.de> " ) ;
MODULE_DESCRIPTION ( " Driver for chargers which report their online status through a GPIO " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform:gpio-charger " ) ;