2019-04-09 08:24:40 +02:00
// SPDX-License-Identifier: GPL-2.0+
2017-05-09 07:44:17 +02:00
/*
2019-04-09 08:24:40 +02:00
* Driver for Analog Devices ( Linear Technology ) LT3651 charger IC .
2017-05-09 07:44:17 +02:00
* Copyright ( C ) 2017 , Topic Embedded Products
*/
# include <linux/device.h>
# include <linux/gpio/consumer.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/of.h>
2019-04-09 08:24:40 +02:00
struct lt3651_charger {
2017-05-09 07:44:17 +02:00
struct power_supply * charger ;
struct power_supply_desc charger_desc ;
struct gpio_desc * acpr_gpio ;
struct gpio_desc * fault_gpio ;
struct gpio_desc * chrg_gpio ;
} ;
2019-04-09 08:24:40 +02:00
static irqreturn_t lt3651_charger_irq ( int irq , void * devid )
2017-05-09 07:44:17 +02:00
{
struct power_supply * charger = devid ;
power_supply_changed ( charger ) ;
return IRQ_HANDLED ;
}
2019-04-09 08:24:40 +02:00
static inline struct lt3651_charger * psy_to_lt3651_charger (
2017-05-09 07:44:17 +02:00
struct power_supply * psy )
{
return power_supply_get_drvdata ( psy ) ;
}
2019-04-09 08:24:40 +02:00
static int lt3651_charger_get_property ( struct power_supply * psy ,
2017-05-09 07:44:17 +02:00
enum power_supply_property psp , union power_supply_propval * val )
{
2019-04-09 08:24:40 +02:00
struct lt3651_charger * lt3651_charger = psy_to_lt3651_charger ( psy ) ;
2017-05-09 07:44:17 +02:00
switch ( psp ) {
case POWER_SUPPLY_PROP_STATUS :
2019-04-09 08:24:40 +02:00
if ( ! lt3651_charger - > chrg_gpio ) {
2017-05-09 07:44:17 +02:00
val - > intval = POWER_SUPPLY_STATUS_UNKNOWN ;
break ;
}
2019-04-09 08:24:40 +02:00
if ( gpiod_get_value ( lt3651_charger - > chrg_gpio ) )
2017-05-09 07:44:17 +02:00
val - > intval = POWER_SUPPLY_STATUS_CHARGING ;
else
val - > intval = POWER_SUPPLY_STATUS_NOT_CHARGING ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
2019-04-09 08:24:40 +02:00
val - > intval = gpiod_get_value ( lt3651_charger - > acpr_gpio ) ;
2017-05-09 07:44:17 +02:00
break ;
case POWER_SUPPLY_PROP_HEALTH :
2019-04-09 08:24:40 +02:00
if ( ! lt3651_charger - > fault_gpio ) {
2017-05-09 07:44:17 +02:00
val - > intval = POWER_SUPPLY_HEALTH_UNKNOWN ;
break ;
}
2019-04-09 08:24:40 +02:00
if ( ! gpiod_get_value ( lt3651_charger - > fault_gpio ) ) {
2017-05-09 07:44:17 +02:00
val - > intval = POWER_SUPPLY_HEALTH_GOOD ;
break ;
}
/*
* If the fault pin is active , the chrg pin explains the type
* of failure .
*/
2019-04-09 08:24:40 +02:00
if ( ! lt3651_charger - > chrg_gpio ) {
2017-05-09 07:44:17 +02:00
val - > intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE ;
break ;
}
2019-04-09 08:24:40 +02:00
val - > intval = gpiod_get_value ( lt3651_charger - > chrg_gpio ) ?
2017-05-09 07:44:17 +02:00
POWER_SUPPLY_HEALTH_OVERHEAT :
POWER_SUPPLY_HEALTH_DEAD ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2019-04-09 08:24:40 +02:00
static enum power_supply_property lt3651_charger_properties [ ] = {
2017-05-09 07:44:17 +02:00
POWER_SUPPLY_PROP_STATUS ,
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_HEALTH ,
} ;
2019-04-09 08:24:40 +02:00
static int lt3651_charger_probe ( struct platform_device * pdev )
2017-05-09 07:44:17 +02:00
{
struct power_supply_config psy_cfg = { } ;
2019-04-09 08:24:40 +02:00
struct lt3651_charger * lt3651_charger ;
2017-05-09 07:44:17 +02:00
struct power_supply_desc * charger_desc ;
int ret ;
2019-04-09 08:24:40 +02:00
lt3651_charger = devm_kzalloc ( & pdev - > dev , sizeof ( * lt3651_charger ) ,
2017-05-09 07:44:17 +02:00
GFP_KERNEL ) ;
2019-04-09 08:24:40 +02:00
if ( ! lt3651_charger )
2017-05-09 07:44:17 +02:00
return - ENOMEM ;
2019-04-09 08:24:40 +02:00
lt3651_charger - > acpr_gpio = devm_gpiod_get ( & pdev - > dev ,
2017-05-09 07:44:17 +02:00
" lltc,acpr " , GPIOD_IN ) ;
2019-04-09 08:24:40 +02:00
if ( IS_ERR ( lt3651_charger - > acpr_gpio ) ) {
ret = PTR_ERR ( lt3651_charger - > acpr_gpio ) ;
2017-05-09 07:44:17 +02:00
dev_err ( & pdev - > dev , " Failed to acquire acpr GPIO: %d \n " , ret ) ;
return ret ;
}
2019-04-09 08:24:40 +02:00
lt3651_charger - > fault_gpio = devm_gpiod_get_optional ( & pdev - > dev ,
2017-05-09 07:44:17 +02:00
" lltc,fault " , GPIOD_IN ) ;
2019-04-09 08:24:40 +02:00
if ( IS_ERR ( lt3651_charger - > fault_gpio ) ) {
ret = PTR_ERR ( lt3651_charger - > fault_gpio ) ;
2017-05-09 07:44:17 +02:00
dev_err ( & pdev - > dev , " Failed to acquire fault GPIO: %d \n " , ret ) ;
return ret ;
}
2019-04-09 08:24:40 +02:00
lt3651_charger - > chrg_gpio = devm_gpiod_get_optional ( & pdev - > dev ,
2017-05-09 07:44:17 +02:00
" lltc,chrg " , GPIOD_IN ) ;
2019-04-09 08:24:40 +02:00
if ( IS_ERR ( lt3651_charger - > chrg_gpio ) ) {
ret = PTR_ERR ( lt3651_charger - > chrg_gpio ) ;
2017-05-09 07:44:17 +02:00
dev_err ( & pdev - > dev , " Failed to acquire chrg GPIO: %d \n " , ret ) ;
return ret ;
}
2019-04-09 08:24:40 +02:00
charger_desc = & lt3651_charger - > charger_desc ;
2017-05-09 07:44:17 +02:00
charger_desc - > name = pdev - > dev . of_node - > name ;
charger_desc - > type = POWER_SUPPLY_TYPE_MAINS ;
2019-04-09 08:24:40 +02:00
charger_desc - > properties = lt3651_charger_properties ;
charger_desc - > num_properties = ARRAY_SIZE ( lt3651_charger_properties ) ;
charger_desc - > get_property = lt3651_charger_get_property ;
2017-05-09 07:44:17 +02:00
psy_cfg . of_node = pdev - > dev . of_node ;
2019-04-09 08:24:40 +02:00
psy_cfg . drv_data = lt3651_charger ;
2017-05-09 07:44:17 +02:00
2019-04-09 08:24:40 +02:00
lt3651_charger - > charger = devm_power_supply_register ( & pdev - > dev ,
2017-05-09 07:44:17 +02:00
charger_desc , & psy_cfg ) ;
2019-04-09 08:24:40 +02:00
if ( IS_ERR ( lt3651_charger - > charger ) ) {
ret = PTR_ERR ( lt3651_charger - > charger ) ;
2017-05-09 07:44:17 +02:00
dev_err ( & pdev - > dev , " Failed to register power supply: %d \n " ,
ret ) ;
return ret ;
}
/*
* Acquire IRQs for the GPIO pins if possible . If the system does not
* support IRQs on these pins , userspace will have to poll the sysfs
* files manually .
*/
2019-04-09 08:24:40 +02:00
if ( lt3651_charger - > acpr_gpio ) {
ret = gpiod_to_irq ( lt3651_charger - > acpr_gpio ) ;
2017-05-09 07:44:17 +02:00
if ( ret > = 0 )
ret = devm_request_any_context_irq ( & pdev - > dev , ret ,
2019-04-09 08:24:40 +02:00
lt3651_charger_irq ,
2017-05-09 07:44:17 +02:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
2019-04-09 08:24:40 +02:00
dev_name ( & pdev - > dev ) , lt3651_charger - > charger ) ;
2017-05-09 07:44:17 +02:00
if ( ret < 0 )
dev_warn ( & pdev - > dev , " Failed to request acpr irq \n " ) ;
}
2019-04-09 08:24:40 +02:00
if ( lt3651_charger - > fault_gpio ) {
ret = gpiod_to_irq ( lt3651_charger - > fault_gpio ) ;
2017-05-09 07:44:17 +02:00
if ( ret > = 0 )
ret = devm_request_any_context_irq ( & pdev - > dev , ret ,
2019-04-09 08:24:40 +02:00
lt3651_charger_irq ,
2017-05-09 07:44:17 +02:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
2019-04-09 08:24:40 +02:00
dev_name ( & pdev - > dev ) , lt3651_charger - > charger ) ;
2017-05-09 07:44:17 +02:00
if ( ret < 0 )
dev_warn ( & pdev - > dev , " Failed to request fault irq \n " ) ;
}
2019-04-09 08:24:40 +02:00
if ( lt3651_charger - > chrg_gpio ) {
ret = gpiod_to_irq ( lt3651_charger - > chrg_gpio ) ;
2017-05-09 07:44:17 +02:00
if ( ret > = 0 )
ret = devm_request_any_context_irq ( & pdev - > dev , ret ,
2019-04-09 08:24:40 +02:00
lt3651_charger_irq ,
2017-05-09 07:44:17 +02:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
2019-04-09 08:24:40 +02:00
dev_name ( & pdev - > dev ) , lt3651_charger - > charger ) ;
2017-05-09 07:44:17 +02:00
if ( ret < 0 )
dev_warn ( & pdev - > dev , " Failed to request chrg irq \n " ) ;
}
2019-04-09 08:24:40 +02:00
platform_set_drvdata ( pdev , lt3651_charger ) ;
2017-05-09 07:44:17 +02:00
return 0 ;
}
2019-04-09 08:24:40 +02:00
static const struct of_device_id lt3651_charger_match [ ] = {
{ . compatible = " lltc,ltc3651-charger " } , /* DEPRECATED */
{ . compatible = " lltc,lt3651-charger " } ,
2017-05-09 07:44:17 +02:00
{ }
} ;
2019-04-09 08:24:40 +02:00
MODULE_DEVICE_TABLE ( of , lt3651_charger_match ) ;
2017-05-09 07:44:17 +02:00
2019-04-09 08:24:40 +02:00
static struct platform_driver lt3651_charger_driver = {
. probe = lt3651_charger_probe ,
2017-05-09 07:44:17 +02:00
. driver = {
2019-04-09 08:24:40 +02:00
. name = " lt3651-charger " ,
. of_match_table = lt3651_charger_match ,
2017-05-09 07:44:17 +02:00
} ,
} ;
2019-04-09 08:24:40 +02:00
module_platform_driver ( lt3651_charger_driver ) ;
2017-05-09 07:44:17 +02:00
MODULE_AUTHOR ( " Mike Looijmans <mike.looijmans@topic.nl> " ) ;
2019-04-09 08:24:40 +02:00
MODULE_DESCRIPTION ( " Driver for LT3651 charger " ) ;
2017-05-09 07:44:17 +02:00
MODULE_LICENSE ( " GPL " ) ;
2019-04-09 08:24:40 +02:00
MODULE_ALIAS ( " platform:lt3651-charger " ) ;