2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-08-01 11:39:38 +03:00
/*
* AXP20x PMIC USB power supply status driver
*
* Copyright ( C ) 2015 Hans de Goede < hdegoede @ redhat . com >
* Copyright ( C ) 2014 Bruno Prémont < bonbons @ linux - vserver . org >
*/
2018-11-26 18:27:54 +03:00
# include <linux/bitops.h>
2015-08-01 11:39:38 +03:00
# include <linux/device.h>
2021-03-23 16:57:56 +03:00
# include <linux/devm-helpers.h>
2015-08-01 11:39:38 +03:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/mfd/axp20x.h>
# include <linux/module.h>
# include <linux/of.h>
2016-12-09 14:04:09 +03:00
# include <linux/of_device.h>
2015-08-01 11:39:38 +03:00
# include <linux/platform_device.h>
2020-01-15 06:40:47 +03:00
# include <linux/pm.h>
2015-08-01 11:39:38 +03:00
# include <linux/power_supply.h>
# include <linux/regmap.h>
# include <linux/slab.h>
2017-01-27 11:54:36 +03:00
# include <linux/iio/consumer.h>
2019-04-16 09:40:20 +03:00
# include <linux/workqueue.h>
2015-08-01 11:39:38 +03:00
# define DRVNAME "axp20x-usb-power-supply"
# define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
# define AXP20X_PWR_STATUS_VBUS_USED BIT(4)
# define AXP20X_USB_STATUS_VBUS_VALID BIT(2)
2020-01-15 06:40:46 +03:00
# define AXP20X_VBUS_PATH_SEL BIT(7)
# define AXP20X_VBUS_PATH_SEL_OFFSET 7
2015-08-01 11:39:38 +03:00
# define AXP20X_VBUS_VHOLD_uV(b) (4000000 + (((b) >> 3) & 7) * 100000)
2016-12-09 14:04:11 +03:00
# define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3)
# define AXP20X_VBUS_VHOLD_OFFSET 3
2015-08-01 11:39:38 +03:00
# define AXP20X_VBUS_CLIMIT_MASK 3
2019-04-16 09:40:19 +03:00
# define AXP20X_VBUS_CLIMIT_900mA 0
# define AXP20X_VBUS_CLIMIT_500mA 1
# define AXP20X_VBUS_CLIMIT_100mA 2
# define AXP20X_VBUS_CLIMIT_NONE 3
2015-08-01 11:39:38 +03:00
2019-04-16 09:40:22 +03:00
# define AXP813_VBUS_CLIMIT_900mA 0
# define AXP813_VBUS_CLIMIT_1500mA 1
# define AXP813_VBUS_CLIMIT_2000mA 2
# define AXP813_VBUS_CLIMIT_2500mA 3
2015-08-01 11:39:38 +03:00
# define AXP20X_ADC_EN1_VBUS_CURR BIT(2)
# define AXP20X_ADC_EN1_VBUS_VOLT BIT(3)
# define AXP20X_VBUS_MON_VBUS_VALID BIT(3)
2019-10-02 14:25:44 +03:00
# define AXP813_BC_EN BIT(0)
2019-04-16 09:40:20 +03:00
/*
* Note do not raise the debounce time , we must report Vusb high within
* 100 ms otherwise we get Vbus errors in musb .
*/
# define DEBOUNCE_TIME msecs_to_jiffies(50)
2015-08-01 11:39:38 +03:00
struct axp20x_usb_power {
struct regmap * regmap ;
struct power_supply * supply ;
2017-01-10 20:48:12 +03:00
enum axp20x_variants axp20x_id ;
2017-01-27 11:54:36 +03:00
struct iio_channel * vbus_v ;
struct iio_channel * vbus_i ;
2019-04-16 09:40:20 +03:00
struct delayed_work vbus_detect ;
unsigned int old_status ;
2020-01-15 06:40:48 +03:00
unsigned int online ;
2020-01-15 06:40:47 +03:00
unsigned int num_irqs ;
unsigned int irqs [ ] ;
2015-08-01 11:39:38 +03:00
} ;
2020-01-15 06:40:48 +03:00
static bool axp20x_usb_vbus_needs_polling ( struct axp20x_usb_power * power )
{
/*
* Polling is only necessary while VBUS is offline . While online , a
* present - > absent transition implies an online - > offline transition
2020-06-10 01:50:35 +03:00
* and will trigger the VBUS_REMOVAL IRQ .
2020-01-15 06:40:48 +03:00
*/
if ( power - > axp20x_id > = AXP221_ID & & ! power - > online )
return true ;
return false ;
}
2015-08-01 11:39:38 +03:00
static irqreturn_t axp20x_usb_power_irq ( int irq , void * devid )
{
struct axp20x_usb_power * power = devid ;
power_supply_changed ( power - > supply ) ;
2020-11-09 15:11:04 +03:00
mod_delayed_work ( system_power_efficient_wq , & power - > vbus_detect , DEBOUNCE_TIME ) ;
2020-01-15 06:40:48 +03:00
2015-08-01 11:39:38 +03:00
return IRQ_HANDLED ;
}
2019-04-16 09:40:20 +03:00
static void axp20x_usb_power_poll_vbus ( struct work_struct * work )
{
struct axp20x_usb_power * power =
container_of ( work , struct axp20x_usb_power , vbus_detect . work ) ;
unsigned int val ;
int ret ;
ret = regmap_read ( power - > regmap , AXP20X_PWR_INPUT_STATUS , & val ) ;
if ( ret )
goto out ;
val & = ( AXP20X_PWR_STATUS_VBUS_PRESENT | AXP20X_PWR_STATUS_VBUS_USED ) ;
if ( val ! = power - > old_status )
power_supply_changed ( power - > supply ) ;
power - > old_status = val ;
2020-01-15 06:40:48 +03:00
power - > online = val & AXP20X_PWR_STATUS_VBUS_USED ;
2019-04-16 09:40:20 +03:00
out :
2020-01-15 06:40:48 +03:00
if ( axp20x_usb_vbus_needs_polling ( power ) )
2020-11-09 15:11:04 +03:00
mod_delayed_work ( system_power_efficient_wq , & power - > vbus_detect , DEBOUNCE_TIME ) ;
2019-04-16 09:40:20 +03:00
}
2019-04-16 09:40:21 +03:00
static int axp20x_get_current_max ( struct axp20x_usb_power * power , int * val )
{
unsigned int v ;
int ret = regmap_read ( power - > regmap , AXP20X_VBUS_IPSOUT_MGMT , & v ) ;
if ( ret )
return ret ;
switch ( v & AXP20X_VBUS_CLIMIT_MASK ) {
case AXP20X_VBUS_CLIMIT_100mA :
if ( power - > axp20x_id = = AXP221_ID )
* val = - 1 ; /* No 100mA limit */
else
* val = 100000 ;
break ;
case AXP20X_VBUS_CLIMIT_500mA :
* val = 500000 ;
break ;
case AXP20X_VBUS_CLIMIT_900mA :
* val = 900000 ;
break ;
case AXP20X_VBUS_CLIMIT_NONE :
* val = - 1 ;
break ;
}
return 0 ;
}
2019-04-16 09:40:22 +03:00
static int axp813_get_current_max ( struct axp20x_usb_power * power , int * val )
{
unsigned int v ;
int ret = regmap_read ( power - > regmap , AXP20X_VBUS_IPSOUT_MGMT , & v ) ;
if ( ret )
return ret ;
switch ( v & AXP20X_VBUS_CLIMIT_MASK ) {
case AXP813_VBUS_CLIMIT_900mA :
* val = 900000 ;
break ;
case AXP813_VBUS_CLIMIT_1500mA :
* val = 1500000 ;
break ;
case AXP813_VBUS_CLIMIT_2000mA :
* val = 2000000 ;
break ;
case AXP813_VBUS_CLIMIT_2500mA :
* val = 2500000 ;
break ;
}
return 0 ;
}
2015-08-01 11:39:38 +03:00
static int axp20x_usb_power_get_property ( struct power_supply * psy ,
enum power_supply_property psp , union power_supply_propval * val )
{
struct axp20x_usb_power * power = power_supply_get_drvdata ( psy ) ;
unsigned int input , v ;
int ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_VOLTAGE_MIN :
ret = regmap_read ( power - > regmap , AXP20X_VBUS_IPSOUT_MGMT , & v ) ;
if ( ret )
return ret ;
val - > intval = AXP20X_VBUS_VHOLD_uV ( v ) ;
return 0 ;
case POWER_SUPPLY_PROP_VOLTAGE_NOW :
2017-01-27 11:54:36 +03:00
if ( IS_ENABLED ( CONFIG_AXP20X_ADC ) ) {
ret = iio_read_channel_processed ( power - > vbus_v ,
& val - > intval ) ;
if ( ret )
return ret ;
/*
* IIO framework gives mV but Power Supply framework
* gives uV .
*/
val - > intval * = 1000 ;
return 0 ;
}
2015-08-01 11:39:38 +03:00
ret = axp20x_read_variable_width ( power - > regmap ,
AXP20X_VBUS_V_ADC_H , 12 ) ;
if ( ret < 0 )
return ret ;
val - > intval = ret * 1700 ; /* 1 step = 1.7 mV */
return 0 ;
case POWER_SUPPLY_PROP_CURRENT_MAX :
2019-04-16 09:40:22 +03:00
if ( power - > axp20x_id = = AXP813_ID )
return axp813_get_current_max ( power , & val - > intval ) ;
2019-04-16 09:40:21 +03:00
return axp20x_get_current_max ( power , & val - > intval ) ;
2015-08-01 11:39:38 +03:00
case POWER_SUPPLY_PROP_CURRENT_NOW :
2017-01-27 11:54:36 +03:00
if ( IS_ENABLED ( CONFIG_AXP20X_ADC ) ) {
ret = iio_read_channel_processed ( power - > vbus_i ,
& val - > intval ) ;
if ( ret )
return ret ;
/*
* IIO framework gives mA but Power Supply framework
* gives uA .
*/
val - > intval * = 1000 ;
return 0 ;
}
2015-08-01 11:39:38 +03:00
ret = axp20x_read_variable_width ( power - > regmap ,
AXP20X_VBUS_I_ADC_H , 12 ) ;
if ( ret < 0 )
return ret ;
val - > intval = ret * 375 ; /* 1 step = 0.375 mA */
return 0 ;
default :
break ;
}
/* All the properties below need the input-status reg value */
ret = regmap_read ( power - > regmap , AXP20X_PWR_INPUT_STATUS , & input ) ;
if ( ret )
return ret ;
switch ( psp ) {
case POWER_SUPPLY_PROP_HEALTH :
if ( ! ( input & AXP20X_PWR_STATUS_VBUS_PRESENT ) ) {
val - > intval = POWER_SUPPLY_HEALTH_UNKNOWN ;
break ;
}
2016-06-02 20:18:53 +03:00
val - > intval = POWER_SUPPLY_HEALTH_GOOD ;
2015-08-01 11:39:38 +03:00
2016-12-09 14:04:09 +03:00
if ( power - > axp20x_id = = AXP202_ID ) {
2016-06-02 20:18:53 +03:00
ret = regmap_read ( power - > regmap ,
AXP20X_USB_OTG_STATUS , & v ) ;
if ( ret )
return ret ;
2015-08-01 11:39:38 +03:00
2016-06-02 20:18:53 +03:00
if ( ! ( v & AXP20X_USB_STATUS_VBUS_VALID ) )
val - > intval =
POWER_SUPPLY_HEALTH_UNSPEC_FAILURE ;
}
2015-08-01 11:39:38 +03:00
break ;
case POWER_SUPPLY_PROP_PRESENT :
val - > intval = ! ! ( input & AXP20X_PWR_STATUS_VBUS_PRESENT ) ;
break ;
case POWER_SUPPLY_PROP_ONLINE :
val - > intval = ! ! ( input & AXP20X_PWR_STATUS_VBUS_USED ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
2020-01-15 06:40:46 +03:00
static int axp813_usb_power_set_online ( struct axp20x_usb_power * power ,
int intval )
{
int val = ! intval < < AXP20X_VBUS_PATH_SEL_OFFSET ;
return regmap_update_bits ( power - > regmap ,
AXP20X_VBUS_IPSOUT_MGMT ,
AXP20X_VBUS_PATH_SEL , val ) ;
}
2016-12-09 14:04:11 +03:00
static int axp20x_usb_power_set_voltage_min ( struct axp20x_usb_power * power ,
int intval )
{
int val ;
switch ( intval ) {
case 4000000 :
case 4100000 :
case 4200000 :
case 4300000 :
case 4400000 :
case 4500000 :
case 4600000 :
case 4700000 :
val = ( intval - 4000000 ) / 100000 ;
return regmap_update_bits ( power - > regmap ,
AXP20X_VBUS_IPSOUT_MGMT ,
AXP20X_VBUS_VHOLD_MASK ,
val < < AXP20X_VBUS_VHOLD_OFFSET ) ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
2019-04-16 09:40:22 +03:00
static int axp813_usb_power_set_current_max ( struct axp20x_usb_power * power ,
int intval )
{
int val ;
switch ( intval ) {
case 900000 :
return regmap_update_bits ( power - > regmap ,
AXP20X_VBUS_IPSOUT_MGMT ,
AXP20X_VBUS_CLIMIT_MASK ,
AXP813_VBUS_CLIMIT_900mA ) ;
case 1500000 :
case 2000000 :
case 2500000 :
val = ( intval - 1000000 ) / 500000 ;
return regmap_update_bits ( power - > regmap ,
AXP20X_VBUS_IPSOUT_MGMT ,
AXP20X_VBUS_CLIMIT_MASK , val ) ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
2016-12-09 14:04:11 +03:00
static int axp20x_usb_power_set_current_max ( struct axp20x_usb_power * power ,
int intval )
{
int val ;
switch ( intval ) {
case 100000 :
if ( power - > axp20x_id = = AXP221_ID )
return - EINVAL ;
2020-08-24 01:36:59 +03:00
fallthrough ;
2016-12-09 14:04:11 +03:00
case 500000 :
case 900000 :
val = ( 900000 - intval ) / 400000 ;
return regmap_update_bits ( power - > regmap ,
AXP20X_VBUS_IPSOUT_MGMT ,
AXP20X_VBUS_CLIMIT_MASK , val ) ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int axp20x_usb_power_set_property ( struct power_supply * psy ,
enum power_supply_property psp ,
const union power_supply_propval * val )
{
struct axp20x_usb_power * power = power_supply_get_drvdata ( psy ) ;
switch ( psp ) {
2020-01-15 06:40:46 +03:00
case POWER_SUPPLY_PROP_ONLINE :
if ( power - > axp20x_id ! = AXP813_ID )
return - EINVAL ;
return axp813_usb_power_set_online ( power , val - > intval ) ;
2016-12-09 14:04:11 +03:00
case POWER_SUPPLY_PROP_VOLTAGE_MIN :
return axp20x_usb_power_set_voltage_min ( power , val - > intval ) ;
case POWER_SUPPLY_PROP_CURRENT_MAX :
2019-04-16 09:40:22 +03:00
if ( power - > axp20x_id = = AXP813_ID )
return axp813_usb_power_set_current_max ( power ,
val - > intval ) ;
2016-12-09 14:04:11 +03:00
return axp20x_usb_power_set_current_max ( power , val - > intval ) ;
default :
return - EINVAL ;
}
return - EINVAL ;
}
static int axp20x_usb_power_prop_writeable ( struct power_supply * psy ,
enum power_supply_property psp )
{
2020-01-15 06:40:46 +03:00
struct axp20x_usb_power * power = power_supply_get_drvdata ( psy ) ;
/*
2020-11-09 15:10:38 +03:00
* The VBUS path select flag works differently on AXP288 and newer :
2020-01-15 06:40:46 +03:00
* - On AXP20x and AXP22x , the flag enables VBUS ( ignoring N_VBUSEN ) .
* - On AXP288 and AXP8xx , the flag disables VBUS ( ignoring N_VBUSEN ) .
* We only expose the control on variants where it can be used to force
* the VBUS input offline .
*/
if ( psp = = POWER_SUPPLY_PROP_ONLINE )
return power - > axp20x_id = = AXP813_ID ;
2016-12-09 14:04:11 +03:00
return psp = = POWER_SUPPLY_PROP_VOLTAGE_MIN | |
psp = = POWER_SUPPLY_PROP_CURRENT_MAX ;
}
2015-08-01 11:39:38 +03:00
static enum power_supply_property axp20x_usb_power_properties [ ] = {
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_VOLTAGE_MIN ,
POWER_SUPPLY_PROP_VOLTAGE_NOW ,
POWER_SUPPLY_PROP_CURRENT_MAX ,
POWER_SUPPLY_PROP_CURRENT_NOW ,
} ;
2016-06-02 20:18:53 +03:00
static enum power_supply_property axp22x_usb_power_properties [ ] = {
POWER_SUPPLY_PROP_HEALTH ,
POWER_SUPPLY_PROP_PRESENT ,
POWER_SUPPLY_PROP_ONLINE ,
POWER_SUPPLY_PROP_VOLTAGE_MIN ,
POWER_SUPPLY_PROP_CURRENT_MAX ,
} ;
2015-08-01 11:39:38 +03:00
static const struct power_supply_desc axp20x_usb_power_desc = {
. name = " axp20x-usb " ,
. type = POWER_SUPPLY_TYPE_USB ,
. properties = axp20x_usb_power_properties ,
. num_properties = ARRAY_SIZE ( axp20x_usb_power_properties ) ,
2016-12-09 14:04:11 +03:00
. property_is_writeable = axp20x_usb_power_prop_writeable ,
2015-08-01 11:39:38 +03:00
. get_property = axp20x_usb_power_get_property ,
2016-12-09 14:04:11 +03:00
. set_property = axp20x_usb_power_set_property ,
2015-08-01 11:39:38 +03:00
} ;
2016-06-02 20:18:53 +03:00
static const struct power_supply_desc axp22x_usb_power_desc = {
. name = " axp20x-usb " ,
. type = POWER_SUPPLY_TYPE_USB ,
. properties = axp22x_usb_power_properties ,
. num_properties = ARRAY_SIZE ( axp22x_usb_power_properties ) ,
2016-12-09 14:04:11 +03:00
. property_is_writeable = axp20x_usb_power_prop_writeable ,
2016-06-02 20:18:53 +03:00
. get_property = axp20x_usb_power_get_property ,
2016-12-09 14:04:11 +03:00
. set_property = axp20x_usb_power_set_property ,
2016-06-02 20:18:53 +03:00
} ;
2020-01-15 06:40:45 +03:00
static const char * const axp20x_irq_names [ ] = {
" VBUS_PLUGIN " ,
" VBUS_REMOVAL " ,
" VBUS_VALID " ,
" VBUS_NOT_VALID " ,
} ;
static const char * const axp22x_irq_names [ ] = {
" VBUS_PLUGIN " ,
" VBUS_REMOVAL " ,
} ;
struct axp_data {
const struct power_supply_desc * power_desc ;
const char * const * irq_names ;
2020-01-15 06:40:47 +03:00
unsigned int num_irq_names ;
2020-01-15 06:40:45 +03:00
enum axp20x_variants axp20x_id ;
} ;
static const struct axp_data axp202_data = {
. power_desc = & axp20x_usb_power_desc ,
. irq_names = axp20x_irq_names ,
2020-01-15 06:40:47 +03:00
. num_irq_names = ARRAY_SIZE ( axp20x_irq_names ) ,
2020-01-15 06:40:45 +03:00
. axp20x_id = AXP202_ID ,
} ;
static const struct axp_data axp221_data = {
. power_desc = & axp22x_usb_power_desc ,
. irq_names = axp22x_irq_names ,
2020-01-15 06:40:47 +03:00
. num_irq_names = ARRAY_SIZE ( axp22x_irq_names ) ,
2020-01-15 06:40:45 +03:00
. axp20x_id = AXP221_ID ,
} ;
static const struct axp_data axp223_data = {
. power_desc = & axp22x_usb_power_desc ,
. irq_names = axp22x_irq_names ,
2020-01-15 06:40:47 +03:00
. num_irq_names = ARRAY_SIZE ( axp22x_irq_names ) ,
2020-01-15 06:40:45 +03:00
. axp20x_id = AXP223_ID ,
} ;
static const struct axp_data axp813_data = {
. power_desc = & axp22x_usb_power_desc ,
. irq_names = axp22x_irq_names ,
2020-01-15 06:40:47 +03:00
. num_irq_names = ARRAY_SIZE ( axp22x_irq_names ) ,
2020-01-15 06:40:45 +03:00
. axp20x_id = AXP813_ID ,
} ;
2020-01-15 06:40:47 +03:00
# ifdef CONFIG_PM_SLEEP
static int axp20x_usb_power_suspend ( struct device * dev )
{
struct axp20x_usb_power * power = dev_get_drvdata ( dev ) ;
int i = 0 ;
/*
* Allow wake via VBUS_PLUGIN only .
*
* As nested threaded IRQs are not automatically disabled during
* suspend , we must explicitly disable the remainder of the IRQs .
*/
if ( device_may_wakeup ( & power - > supply - > dev ) )
enable_irq_wake ( power - > irqs [ i + + ] ) ;
while ( i < power - > num_irqs )
disable_irq ( power - > irqs [ i + + ] ) ;
return 0 ;
}
static int axp20x_usb_power_resume ( struct device * dev )
{
struct axp20x_usb_power * power = dev_get_drvdata ( dev ) ;
int i = 0 ;
if ( device_may_wakeup ( & power - > supply - > dev ) )
disable_irq_wake ( power - > irqs [ i + + ] ) ;
while ( i < power - > num_irqs )
enable_irq ( power - > irqs [ i + + ] ) ;
2020-11-09 15:11:04 +03:00
mod_delayed_work ( system_power_efficient_wq , & power - > vbus_detect , DEBOUNCE_TIME ) ;
2020-01-15 06:40:48 +03:00
2020-01-15 06:40:47 +03:00
return 0 ;
}
# endif
static SIMPLE_DEV_PM_OPS ( axp20x_usb_power_pm_ops , axp20x_usb_power_suspend ,
axp20x_usb_power_resume ) ;
2017-01-27 11:54:36 +03:00
static int configure_iio_channels ( struct platform_device * pdev ,
struct axp20x_usb_power * power )
{
power - > vbus_v = devm_iio_channel_get ( & pdev - > dev , " vbus_v " ) ;
if ( IS_ERR ( power - > vbus_v ) ) {
if ( PTR_ERR ( power - > vbus_v ) = = - ENODEV )
return - EPROBE_DEFER ;
return PTR_ERR ( power - > vbus_v ) ;
}
power - > vbus_i = devm_iio_channel_get ( & pdev - > dev , " vbus_i " ) ;
if ( IS_ERR ( power - > vbus_i ) ) {
if ( PTR_ERR ( power - > vbus_i ) = = - ENODEV )
return - EPROBE_DEFER ;
return PTR_ERR ( power - > vbus_i ) ;
}
return 0 ;
}
static int configure_adc_registers ( struct axp20x_usb_power * power )
{
/* Enable vbus voltage and current measurement */
return regmap_update_bits ( power - > regmap , AXP20X_ADC_EN1 ,
AXP20X_ADC_EN1_VBUS_CURR |
AXP20X_ADC_EN1_VBUS_VOLT ,
AXP20X_ADC_EN1_VBUS_CURR |
AXP20X_ADC_EN1_VBUS_VOLT ) ;
}
2015-08-01 11:39:38 +03:00
static int axp20x_usb_power_probe ( struct platform_device * pdev )
{
struct axp20x_dev * axp20x = dev_get_drvdata ( pdev - > dev . parent ) ;
struct power_supply_config psy_cfg = { } ;
struct axp20x_usb_power * power ;
2020-01-15 06:40:45 +03:00
const struct axp_data * axp_data ;
2015-08-01 11:39:38 +03:00
int i , irq , ret ;
if ( ! of_device_is_available ( pdev - > dev . of_node ) )
return - ENODEV ;
if ( ! axp20x ) {
dev_err ( & pdev - > dev , " Parent drvdata not set \n " ) ;
return - EINVAL ;
}
2020-01-15 06:40:47 +03:00
axp_data = of_device_get_match_data ( & pdev - > dev ) ;
power = devm_kzalloc ( & pdev - > dev ,
struct_size ( power , irqs , axp_data - > num_irq_names ) ,
GFP_KERNEL ) ;
2015-08-01 11:39:38 +03:00
if ( ! power )
return - ENOMEM ;
2019-04-16 09:40:20 +03:00
platform_set_drvdata ( pdev , power ) ;
2016-12-09 14:04:09 +03:00
2020-01-15 06:40:45 +03:00
power - > axp20x_id = axp_data - > axp20x_id ;
2015-08-01 11:39:38 +03:00
power - > regmap = axp20x - > regmap ;
2020-01-15 06:40:47 +03:00
power - > num_irqs = axp_data - > num_irq_names ;
2021-01-24 18:24:21 +03:00
INIT_DELAYED_WORK ( & power - > vbus_detect , axp20x_usb_power_poll_vbus ) ;
2015-08-01 11:39:38 +03:00
2016-12-09 14:04:09 +03:00
if ( power - > axp20x_id = = AXP202_ID ) {
2016-06-02 20:18:53 +03:00
/* Enable vbus valid checking */
ret = regmap_update_bits ( power - > regmap , AXP20X_VBUS_MON ,
AXP20X_VBUS_MON_VBUS_VALID ,
AXP20X_VBUS_MON_VBUS_VALID ) ;
if ( ret )
return ret ;
2015-08-01 11:39:38 +03:00
2017-01-27 11:54:36 +03:00
if ( IS_ENABLED ( CONFIG_AXP20X_ADC ) )
ret = configure_iio_channels ( pdev , power ) ;
else
ret = configure_adc_registers ( power ) ;
2016-06-02 20:18:53 +03:00
if ( ret )
return ret ;
}
2015-08-01 11:39:38 +03:00
2019-10-02 14:25:44 +03:00
if ( power - > axp20x_id = = AXP813_ID ) {
/* Enable USB Battery Charging specification detection */
regmap_update_bits ( axp20x - > regmap , AXP288_BC_GLOBAL ,
AXP813_BC_EN , AXP813_BC_EN ) ;
}
2015-08-01 11:39:38 +03:00
psy_cfg . of_node = pdev - > dev . of_node ;
psy_cfg . drv_data = power ;
2020-01-15 06:40:45 +03:00
power - > supply = devm_power_supply_register ( & pdev - > dev ,
axp_data - > power_desc ,
2016-06-02 20:18:53 +03:00
& psy_cfg ) ;
2015-08-01 11:39:38 +03:00
if ( IS_ERR ( power - > supply ) )
return PTR_ERR ( power - > supply ) ;
/* Request irqs after registering, as irqs may trigger immediately */
2020-01-15 06:40:47 +03:00
for ( i = 0 ; i < axp_data - > num_irq_names ; i + + ) {
2020-01-15 06:40:45 +03:00
irq = platform_get_irq_byname ( pdev , axp_data - > irq_names [ i ] ) ;
2015-08-01 11:39:38 +03:00
if ( irq < 0 ) {
2020-01-15 06:40:47 +03:00
dev_err ( & pdev - > dev , " No IRQ for %s: %d \n " ,
axp_data - > irq_names [ i ] , irq ) ;
return irq ;
}
power - > irqs [ i ] = regmap_irq_get_virq ( axp20x - > regmap_irqc , irq ) ;
ret = devm_request_any_context_irq ( & pdev - > dev , power - > irqs [ i ] ,
axp20x_usb_power_irq , 0 ,
DRVNAME , power ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " Error requesting %s IRQ: %d \n " ,
axp_data - > irq_names [ i ] , ret ) ;
return ret ;
2015-08-01 11:39:38 +03:00
}
}
2021-03-23 16:57:56 +03:00
ret = devm_delayed_work_autocancel ( & pdev - > dev , & power - > vbus_detect ,
axp20x_usb_power_poll_vbus ) ;
if ( ret )
return ret ;
2019-04-16 09:40:20 +03:00
if ( axp20x_usb_vbus_needs_polling ( power ) )
2020-11-09 15:11:04 +03:00
queue_delayed_work ( system_power_efficient_wq , & power - > vbus_detect , 0 ) ;
2019-04-16 09:40:20 +03:00
return 0 ;
}
2015-08-01 11:39:38 +03:00
static const struct of_device_id axp20x_usb_power_match [ ] = {
2016-12-09 14:04:09 +03:00
{
. compatible = " x-powers,axp202-usb-power-supply " ,
2020-01-15 06:40:45 +03:00
. data = & axp202_data ,
2016-12-09 14:04:09 +03:00
} , {
. compatible = " x-powers,axp221-usb-power-supply " ,
2020-01-15 06:40:45 +03:00
. data = & axp221_data ,
2016-12-09 14:04:13 +03:00
} , {
. compatible = " x-powers,axp223-usb-power-supply " ,
2020-01-15 06:40:45 +03:00
. data = & axp223_data ,
2019-04-16 09:40:22 +03:00
} , {
. compatible = " x-powers,axp813-usb-power-supply " ,
2020-01-15 06:40:45 +03:00
. data = & axp813_data ,
2016-12-09 14:04:09 +03:00
} , { /* sentinel */ }
2015-08-01 11:39:38 +03:00
} ;
MODULE_DEVICE_TABLE ( of , axp20x_usb_power_match ) ;
static struct platform_driver axp20x_usb_power_driver = {
. probe = axp20x_usb_power_probe ,
. driver = {
2020-01-15 06:40:47 +03:00
. name = DRVNAME ,
. of_match_table = axp20x_usb_power_match ,
. pm = & axp20x_usb_power_pm_ops ,
2015-08-01 11:39:38 +03:00
} ,
} ;
module_platform_driver ( axp20x_usb_power_driver ) ;
MODULE_AUTHOR ( " Hans de Goede <hdegoede@redhat.com> " ) ;
MODULE_DESCRIPTION ( " AXP20x PMIC USB power supply status driver " ) ;
MODULE_LICENSE ( " GPL " ) ;