diff options
-rw-r--r-- | drivers/power/supply/axp288_charger.c | 150 |
1 files changed, 99 insertions, 51 deletions
diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index b9553be9bed5..fd4983c98fd9 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -22,6 +22,7 @@ #include <linux/mfd/axp20x.h> #include <linux/extcon.h> #include <linux/dmi.h> +#include <asm/iosf_mbi.h> #define PS_STAT_VBUS_TRIGGER BIT(0) #define PS_STAT_BAT_CHRG_DIR BIT(2) @@ -95,6 +96,8 @@ #define CV_4200MV 4200 /* 4200mV */ #define CV_4350MV 4350 /* 4350mV */ +#define AXP288_REG_UPDATE_INTERVAL (60 * HZ) + #define AXP288_EXTCON_DEV_NAME "axp288_extcon" #define USB_HOST_EXTCON_HID "INT3496" #define USB_HOST_EXTCON_NAME "INT3496:00" @@ -118,6 +121,7 @@ struct axp288_chrg_info { struct regmap_irq_chip_data *regmap_irqc; int irq[CHRG_INTR_END]; struct power_supply *psy_usb; + struct mutex lock; /* OTG/Host mode */ struct { @@ -138,6 +142,12 @@ struct axp288_chrg_info { int cv; int max_cc; int max_cv; + + unsigned long last_updated; + unsigned int input_status; + unsigned int op_mode; + unsigned int backend_control; + bool valid; }; static inline int axp288_charger_set_cc(struct axp288_chrg_info *info, int cc) @@ -197,11 +207,8 @@ static inline int axp288_charger_set_cv(struct axp288_chrg_info *info, int cv) static int axp288_charger_get_vbus_inlmt(struct axp288_chrg_info *info) { unsigned int val; - int ret; - ret = regmap_read(info->regmap, AXP20X_CHRG_BAK_CTRL, &val); - if (ret < 0) - return ret; + val = info->backend_control; val >>= CHRG_VBUS_ILIM_BIT_POS; switch (val) { @@ -297,55 +304,34 @@ static int axp288_charger_enable_charger(struct axp288_chrg_info *info, static int axp288_charger_is_present(struct axp288_chrg_info *info) { - int ret, present = 0; - unsigned int val; - - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if (ret < 0) - return ret; + int present = 0; - if (val & PS_STAT_VBUS_PRESENT) + if (info->input_status & PS_STAT_VBUS_PRESENT) present = 1; return present; } static int axp288_charger_is_online(struct axp288_chrg_info *info) { - int ret, online = 0; - unsigned int val; - - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if (ret < 0) - return ret; + int online = 0; - if (val & PS_STAT_VBUS_VALID) + if (info->input_status & PS_STAT_VBUS_VALID) online = 1; return online; } static int axp288_get_charger_health(struct axp288_chrg_info *info) { - int ret, pwr_stat, chrg_stat; int health = POWER_SUPPLY_HEALTH_UNKNOWN; - unsigned int val; - ret = regmap_read(info->regmap, AXP20X_PWR_INPUT_STATUS, &val); - if ((ret < 0) || !(val & PS_STAT_VBUS_PRESENT)) + if (!(info->input_status & PS_STAT_VBUS_PRESENT)) goto health_read_fail; - else - pwr_stat = val; - ret = regmap_read(info->regmap, AXP20X_PWR_OP_MODE, &val); - if (ret < 0) - goto health_read_fail; - else - chrg_stat = val; - - if (!(pwr_stat & PS_STAT_VBUS_VALID)) + if (!(info->input_status & PS_STAT_VBUS_VALID)) health = POWER_SUPPLY_HEALTH_DEAD; - else if (chrg_stat & CHRG_STAT_PMIC_OTP) + else if (info->op_mode & CHRG_STAT_PMIC_OTP) health = POWER_SUPPLY_HEALTH_OVERHEAT; - else if (chrg_stat & CHRG_STAT_BAT_SAFE_MODE) + else if (info->op_mode & CHRG_STAT_BAT_SAFE_MODE) health = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE; else health = POWER_SUPPLY_HEALTH_GOOD; @@ -362,30 +348,86 @@ static int axp288_charger_usb_set_property(struct power_supply *psy, int ret = 0; int scaled_val; + mutex_lock(&info->lock); switch (psp) { case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: scaled_val = min(val->intval, info->max_cc); scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); ret = axp288_charger_set_cc(info, scaled_val); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set charge current failed\n"); + goto out; + } break; case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: scaled_val = min(val->intval, info->max_cv); scaled_val = DIV_ROUND_CLOSEST(scaled_val, 1000); ret = axp288_charger_set_cv(info, scaled_val); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set charge voltage failed\n"); + goto out; + } break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ret = axp288_charger_set_vbus_inlmt(info, val->intval); - if (ret < 0) + if (ret < 0) { dev_warn(&info->pdev->dev, "set input current limit failed\n"); + goto out; + } + info->valid = false; break; default: ret = -EINVAL; } +out: + mutex_unlock(&info->lock); + return ret; +} + +static int axp288_charger_reg_readb(struct axp288_chrg_info *info, int reg, unsigned int *ret_val) +{ + int ret; + + ret = regmap_read(info->regmap, reg, ret_val); + if (ret < 0) { + dev_err(&info->pdev->dev, "Error %d on reading value from register 0x%04x\n", + ret, + reg); + return ret; + } + return 0; +} + +static int axp288_charger_usb_update_property(struct axp288_chrg_info *info) +{ + int ret = 0; + + if (info->valid && time_before(jiffies, info->last_updated + AXP288_REG_UPDATE_INTERVAL)) + return 0; + + dev_dbg(&info->pdev->dev, "Charger updating register values...\n"); + + ret = iosf_mbi_block_punit_i2c_access(); + if (ret < 0) + return ret; + + ret = axp288_charger_reg_readb(info, AXP20X_PWR_INPUT_STATUS, &info->input_status); + if (ret < 0) + goto out; + + ret = axp288_charger_reg_readb(info, AXP20X_PWR_OP_MODE, &info->op_mode); + if (ret < 0) + goto out; + + ret = axp288_charger_reg_readb(info, AXP20X_CHRG_BAK_CTRL, &info->backend_control); + if (ret < 0) + goto out; + + info->last_updated = jiffies; + info->valid = true; +out: + iosf_mbi_unblock_punit_i2c_access(); return ret; } @@ -396,6 +438,11 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, struct axp288_chrg_info *info = power_supply_get_drvdata(psy); int ret; + mutex_lock(&info->lock); + ret = axp288_charger_usb_update_property(info); + if (ret < 0) + goto out; + switch (psp) { case POWER_SUPPLY_PROP_PRESENT: /* Check for OTG case first */ @@ -403,10 +450,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = 0; break; } - ret = axp288_charger_is_present(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = axp288_charger_is_present(info); break; case POWER_SUPPLY_PROP_ONLINE: /* Check for OTG case first */ @@ -414,10 +458,7 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = 0; break; } - ret = axp288_charger_is_online(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = axp288_charger_is_online(info); break; case POWER_SUPPLY_PROP_HEALTH: val->intval = axp288_get_charger_health(info); @@ -435,16 +476,15 @@ static int axp288_charger_usb_get_property(struct power_supply *psy, val->intval = info->max_cv * 1000; break; case POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: - ret = axp288_charger_get_vbus_inlmt(info); - if (ret < 0) - return ret; - val->intval = ret; + val->intval = axp288_charger_get_vbus_inlmt(info); break; default: - return -EINVAL; + ret = -EINVAL; } - return 0; +out: + mutex_unlock(&info->lock); + return ret; } static int axp288_charger_property_is_writeable(struct power_supply *psy, @@ -540,7 +580,9 @@ static irqreturn_t axp288_charger_irq_thread_handler(int irq, void *dev) dev_warn(&info->pdev->dev, "Spurious Interrupt!!!\n"); goto out; } - + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); out: return IRQ_HANDLED; @@ -613,6 +655,9 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) if (!(val & PS_STAT_VBUS_VALID)) { dev_dbg(&info->pdev->dev, "USB charger disconnected\n"); axp288_charger_enable_charger(info, false); + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); return; } @@ -644,6 +689,9 @@ static void axp288_charger_extcon_evt_worker(struct work_struct *work) dev_err(&info->pdev->dev, "error setting current limit (%d)\n", ret); + mutex_lock(&info->lock); + info->valid = false; + mutex_unlock(&info->lock); power_supply_changed(info->psy_usb); } |