diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/reset/at91-sama5d2_shdwc.c | 25 | ||||
-rw-r--r-- | drivers/power/reset/syscon-reboot.c | 19 | ||||
-rw-r--r-- | drivers/power/supply/Kconfig | 19 | ||||
-rw-r--r-- | drivers/power/supply/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/supply/ab8500_bmdata.c | 1 | ||||
-rw-r--r-- | drivers/power/supply/axp20x_usb_power.c | 179 | ||||
-rw-r--r-- | drivers/power/supply/axp288_charger.c | 4 | ||||
-rw-r--r-- | drivers/power/supply/charger-manager.c | 3 | ||||
-rw-r--r-- | drivers/power/supply/cpcap-battery.c | 45 | ||||
-rw-r--r-- | drivers/power/supply/cpcap-charger.c | 5 | ||||
-rw-r--r-- | drivers/power/supply/gpio-charger.c | 57 | ||||
-rw-r--r-- | drivers/power/supply/ingenic-battery.c | 184 | ||||
-rw-r--r-- | drivers/power/supply/lt3651-charger.c (renamed from drivers/power/supply/ltc3651-charger.c) | 123 | ||||
-rw-r--r-- | drivers/power/supply/max14656_charger_detector.c | 27 | ||||
-rw-r--r-- | drivers/power/supply/power_supply_core.c | 32 |
15 files changed, 576 insertions, 150 deletions
diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index 2b686c55b717..e341cc5c0ea6 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -57,15 +57,21 @@ #define SHDW_WK_PIN(reg, cfg) ((reg) & AT91_SHDW_WKUPIS((cfg)->wkup_pin_input)) #define SHDW_RTCWK(reg, cfg) (((reg) >> ((cfg)->sr_rtcwk_shift)) & 0x1) +#define SHDW_RTTWK(reg, cfg) (((reg) >> ((cfg)->sr_rttwk_shift)) & 0x1) #define SHDW_RTCWKEN(cfg) (1 << ((cfg)->mr_rtcwk_shift)) +#define SHDW_RTTWKEN(cfg) (1 << ((cfg)->mr_rttwk_shift)) #define DBC_PERIOD_US(x) DIV_ROUND_UP_ULL((1000000 * (x)), \ SLOW_CLOCK_FREQ) +#define SHDW_CFG_NOT_USED (32) + struct shdwc_config { u8 wkup_pin_input; u8 mr_rtcwk_shift; + u8 mr_rttwk_shift; u8 sr_rtcwk_shift; + u8 sr_rttwk_shift; }; struct shdwc { @@ -104,6 +110,8 @@ static void __init at91_wakeup_status(struct platform_device *pdev) reason = "WKUP pin"; else if (SHDW_RTCWK(reg, shdw->cfg)) reason = "RTC"; + else if (SHDW_RTTWK(reg, shdw->cfg)) + reason = "RTT"; pr_info("AT91: Wake-Up source: %s\n", reason); } @@ -221,6 +229,9 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) if (of_property_read_bool(np, "atmel,wakeup-rtc-timer")) mode |= SHDW_RTCWKEN(shdw->cfg); + if (of_property_read_bool(np, "atmel,wakeup-rtt-timer")) + mode |= SHDW_RTTWKEN(shdw->cfg); + dev_dbg(&pdev->dev, "%s: mode = %#x\n", __func__, mode); writel(mode, shdw->shdwc_base + AT91_SHDW_MR); @@ -231,13 +242,27 @@ static void at91_shdwc_dt_configure(struct platform_device *pdev) static const struct shdwc_config sama5d2_shdwc_config = { .wkup_pin_input = 0, .mr_rtcwk_shift = 17, + .mr_rttwk_shift = SHDW_CFG_NOT_USED, .sr_rtcwk_shift = 5, + .sr_rttwk_shift = SHDW_CFG_NOT_USED, +}; + +static const struct shdwc_config sam9x60_shdwc_config = { + .wkup_pin_input = 0, + .mr_rtcwk_shift = 17, + .mr_rttwk_shift = 16, + .sr_rtcwk_shift = 5, + .sr_rttwk_shift = 4, }; static const struct of_device_id at91_shdwc_of_match[] = { { .compatible = "atmel,sama5d2-shdwc", .data = &sama5d2_shdwc_config, + }, + { + .compatible = "microchip,sam9x60-shdwc", + .data = &sam9x60_shdwc_config, }, { /*sentinel*/ } diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index 7d0d269a0837..5a6bb638c331 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -27,6 +27,7 @@ struct syscon_reboot_context { struct regmap *map; u32 offset; + u32 value; u32 mask; struct notifier_block restart_handler; }; @@ -39,7 +40,7 @@ static int syscon_restart_handle(struct notifier_block *this, restart_handler); /* Issue the reboot */ - regmap_write(ctx->map, ctx->offset, ctx->mask); + regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value); mdelay(1000); @@ -51,6 +52,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) { struct syscon_reboot_context *ctx; struct device *dev = &pdev->dev; + int mask_err, value_err; int err; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -64,8 +66,21 @@ static int syscon_reboot_probe(struct platform_device *pdev) if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) return -EINVAL; - if (of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask)) + value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value); + mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask); + if (value_err && mask_err) { + dev_err(dev, "unable to read 'value' and 'mask'"); return -EINVAL; + } + + if (value_err) { + /* support old binding */ + ctx->value = ctx->mask; + ctx->mask = 0xFFFFFFFF; + } else if (mask_err) { + /* support value without mask*/ + ctx->mask = 0xFFFFFFFF; + } ctx->restart_handler.notifier_call = syscon_restart_handle; ctx->restart_handler.priority = 192; diff --git a/drivers/power/supply/Kconfig b/drivers/power/supply/Kconfig index e901b9879e7e..4728a218656f 100644 --- a/drivers/power/supply/Kconfig +++ b/drivers/power/supply/Kconfig @@ -169,6 +169,17 @@ config BATTERY_COLLIE Say Y to enable support for the battery on the Sharp Zaurus SL-5500 (collie) models. +config BATTERY_INGENIC + tristate "Ingenic JZ47xx SoCs battery driver" + depends on MIPS || COMPILE_TEST + depends on INGENIC_ADC + help + Choose this option if you want to monitor battery status on + Ingenic JZ47xx SoC based devices. + + This driver can also be built as a module. If so, the module will be + called ingenic-battery. + config BATTERY_IPAQ_MICRO tristate "iPAQ Atmel Micro ASIC battery driver" depends on MFD_IPAQ_MICRO @@ -475,12 +486,12 @@ config CHARGER_MANAGER runtime and in suspend-to-RAM by waking up the system periodically with help of suspend_again support. -config CHARGER_LTC3651 - tristate "LTC3651 charger" +config CHARGER_LT3651 + tristate "Analog Devices LT3651 charger" depends on GPIOLIB help - Say Y to include support for the LTC3651 battery charger which reports - its status via GPIO lines. + Say Y to include support for the Analog Devices (Linear Technology) + LT3651 battery charger which reports its status via GPIO lines. config CHARGER_MAX14577 tristate "Maxim MAX14577/77836 battery charger driver" diff --git a/drivers/power/supply/Makefile b/drivers/power/supply/Makefile index b731c2a9b695..39fc3e91e619 100644 --- a/drivers/power/supply/Makefile +++ b/drivers/power/supply/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o obj-$(CONFIG_BATTERY_COLLIE) += collie_battery.o +obj-$(CONFIG_BATTERY_INGENIC) += ingenic-battery.o obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o obj-$(CONFIG_BATTERY_SBS) += sbs-battery.o @@ -67,7 +68,7 @@ obj-$(CONFIG_CHARGER_LP8727) += lp8727_charger.o obj-$(CONFIG_CHARGER_LP8788) += lp8788-charger.o obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o obj-$(CONFIG_CHARGER_MANAGER) += charger-manager.o -obj-$(CONFIG_CHARGER_LTC3651) += ltc3651-charger.o +obj-$(CONFIG_CHARGER_LT3651) += lt3651-charger.o obj-$(CONFIG_CHARGER_MAX14577) += max14577_charger.o obj-$(CONFIG_CHARGER_DETECTOR_MAX14656) += max14656_charger_detector.o obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index 7b2b69916f48..f6a66979cbb5 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -508,6 +508,7 @@ int ab8500_bm_of_probe(struct device *dev, btech = of_get_property(battery_node, "stericsson,battery-type", NULL); if (!btech) { dev_warn(dev, "missing property battery-name/type\n"); + of_node_put(battery_node); return -EINVAL; } diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index f52fe77edb6f..d2b1255ee1cc 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -24,6 +24,7 @@ #include <linux/regmap.h> #include <linux/slab.h> #include <linux/iio/consumer.h> +#include <linux/workqueue.h> #define DRVNAME "axp20x-usb-power-supply" @@ -36,16 +37,27 @@ #define AXP20X_VBUS_VHOLD_MASK GENMASK(5, 3) #define AXP20X_VBUS_VHOLD_OFFSET 3 #define AXP20X_VBUS_CLIMIT_MASK 3 -#define AXP20X_VBUC_CLIMIT_900mA 0 -#define AXP20X_VBUC_CLIMIT_500mA 1 -#define AXP20X_VBUC_CLIMIT_100mA 2 -#define AXP20X_VBUC_CLIMIT_NONE 3 +#define AXP20X_VBUS_CLIMIT_900mA 0 +#define AXP20X_VBUS_CLIMIT_500mA 1 +#define AXP20X_VBUS_CLIMIT_100mA 2 +#define AXP20X_VBUS_CLIMIT_NONE 3 + +#define AXP813_VBUS_CLIMIT_900mA 0 +#define AXP813_VBUS_CLIMIT_1500mA 1 +#define AXP813_VBUS_CLIMIT_2000mA 2 +#define AXP813_VBUS_CLIMIT_2500mA 3 #define AXP20X_ADC_EN1_VBUS_CURR BIT(2) #define AXP20X_ADC_EN1_VBUS_VOLT BIT(3) #define AXP20X_VBUS_MON_VBUS_VALID BIT(3) +/* + * Note do not raise the debounce time, we must report Vusb high within + * 100ms otherwise we get Vbus errors in musb. + */ +#define DEBOUNCE_TIME msecs_to_jiffies(50) + struct axp20x_usb_power { struct device_node *np; struct regmap *regmap; @@ -53,6 +65,8 @@ struct axp20x_usb_power { enum axp20x_variants axp20x_id; struct iio_channel *vbus_v; struct iio_channel *vbus_i; + struct delayed_work vbus_detect; + unsigned int old_status; }; static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) @@ -64,6 +78,89 @@ static irqreturn_t axp20x_usb_power_irq(int irq, void *devid) return IRQ_HANDLED; } +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; + +out: + mod_delayed_work(system_wq, &power->vbus_detect, DEBOUNCE_TIME); +} + +static bool axp20x_usb_vbus_needs_polling(struct axp20x_usb_power *power) +{ + if (power->axp20x_id >= AXP221_ID) + return true; + + return false; +} + +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; +} + +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; +} + static int axp20x_usb_power_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { @@ -102,28 +199,9 @@ static int axp20x_usb_power_get_property(struct power_supply *psy, val->intval = ret * 1700; /* 1 step = 1.7 mV */ return 0; case POWER_SUPPLY_PROP_CURRENT_MAX: - ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v); - if (ret) - return ret; - - switch (v & AXP20X_VBUS_CLIMIT_MASK) { - case AXP20X_VBUC_CLIMIT_100mA: - if (power->axp20x_id == AXP221_ID) - val->intval = -1; /* No 100mA limit */ - else - val->intval = 100000; - break; - case AXP20X_VBUC_CLIMIT_500mA: - val->intval = 500000; - break; - case AXP20X_VBUC_CLIMIT_900mA: - val->intval = 900000; - break; - case AXP20X_VBUC_CLIMIT_NONE: - val->intval = -1; - break; - } - return 0; + if (power->axp20x_id == AXP813_ID) + return axp813_get_current_max(power, &val->intval); + return axp20x_get_current_max(power, &val->intval); case POWER_SUPPLY_PROP_CURRENT_NOW: if (IS_ENABLED(CONFIG_AXP20X_ADC)) { ret = iio_read_channel_processed(power->vbus_i, @@ -214,6 +292,31 @@ static int axp20x_usb_power_set_voltage_min(struct axp20x_usb_power *power, return -EINVAL; } +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; +} + static int axp20x_usb_power_set_current_max(struct axp20x_usb_power *power, int intval) { @@ -248,6 +351,9 @@ static int axp20x_usb_power_set_property(struct power_supply *psy, return axp20x_usb_power_set_voltage_min(power, val->intval); case POWER_SUPPLY_PROP_CURRENT_MAX: + if (power->axp20x_id == AXP813_ID) + return axp813_usb_power_set_current_max(power, + val->intval); return axp20x_usb_power_set_current_max(power, val->intval); default: @@ -357,6 +463,7 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) if (!power) return -ENOMEM; + platform_set_drvdata(pdev, power); power->axp20x_id = (enum axp20x_variants)of_device_get_match_data( &pdev->dev); @@ -382,7 +489,8 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) usb_power_desc = &axp20x_usb_power_desc; irq_names = axp20x_irq_names; } else if (power->axp20x_id == AXP221_ID || - power->axp20x_id == AXP223_ID) { + power->axp20x_id == AXP223_ID || + power->axp20x_id == AXP813_ID) { usb_power_desc = &axp22x_usb_power_desc; irq_names = axp22x_irq_names; } else { @@ -415,6 +523,19 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) irq_names[i], ret); } + INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); + if (axp20x_usb_vbus_needs_polling(power)) + queue_delayed_work(system_wq, &power->vbus_detect, 0); + + return 0; +} + +static int axp20x_usb_power_remove(struct platform_device *pdev) +{ + struct axp20x_usb_power *power = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&power->vbus_detect); + return 0; } @@ -428,12 +549,16 @@ static const struct of_device_id axp20x_usb_power_match[] = { }, { .compatible = "x-powers,axp223-usb-power-supply", .data = (void *)AXP223_ID, + }, { + .compatible = "x-powers,axp813-usb-power-supply", + .data = (void *)AXP813_ID, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); static struct platform_driver axp20x_usb_power_driver = { .probe = axp20x_usb_power_probe, + .remove = axp20x_usb_power_remove, .driver = { .name = DRVNAME, .of_match_table = axp20x_usb_power_match, diff --git a/drivers/power/supply/axp288_charger.c b/drivers/power/supply/axp288_charger.c index f8c6da9277b3..00b961890a38 100644 --- a/drivers/power/supply/axp288_charger.c +++ b/drivers/power/supply/axp288_charger.c @@ -833,6 +833,10 @@ static int axp288_charger_probe(struct platform_device *pdev) /* Register charger interrupts */ for (i = 0; i < CHRG_INTR_END; i++) { pirq = platform_get_irq(info->pdev, i); + if (pirq < 0) { + dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pirq); + return pirq; + } info->irq[i] = regmap_irq_get_virq(info->regmap_irqc, pirq); if (info->irq[i] < 0) { dev_warn(&info->pdev->dev, diff --git a/drivers/power/supply/charger-manager.c b/drivers/power/supply/charger-manager.c index 2e8db5e6de0b..a6900aa0d2ed 100644 --- a/drivers/power/supply/charger-manager.c +++ b/drivers/power/supply/charger-manager.c @@ -1987,6 +1987,9 @@ static struct platform_driver charger_manager_driver = { static int __init charger_manager_init(void) { cm_wq = create_freezable_workqueue("charger_manager"); + if (unlikely(!cm_wq)) + return -ENOMEM; + INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); return platform_driver_register(&charger_manager_driver); diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index 08d5037fd052..61d6447d1966 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -82,9 +82,9 @@ struct cpcap_battery_config { }; struct cpcap_coulomb_counter_data { - s32 sample; /* 24-bits */ + s32 sample; /* 24 or 32 bits */ s32 accumulator; - s16 offset; /* 10-bits */ + s16 offset; /* 9 bits */ }; enum cpcap_battery_state { @@ -213,7 +213,7 @@ static int cpcap_battery_get_current(struct cpcap_battery_ddata *ddata) * TI or ST coulomb counter in the PMIC. */ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata, - u32 sample, s32 accumulator, + s32 sample, s32 accumulator, s16 offset, u32 divider) { s64 acc; @@ -221,8 +221,8 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata, int avg_current; u32 cc_lsb; - sample &= 0xffffff; /* 24-bits, unsigned */ - offset &= 0x7ff; /* 10-bits, signed */ + if (!divider) + return 0; switch (ddata->vendor) { case CPCAP_VENDOR_ST: @@ -256,7 +256,7 @@ static int cpcap_battery_cc_raw_div(struct cpcap_battery_ddata *ddata, /* 3600000μAms = 1μAh */ static int cpcap_battery_cc_to_uah(struct cpcap_battery_ddata *ddata, - u32 sample, s32 accumulator, + s32 sample, s32 accumulator, s16 offset) { return cpcap_battery_cc_raw_div(ddata, sample, @@ -265,7 +265,7 @@ static int cpcap_battery_cc_to_uah(struct cpcap_battery_ddata *ddata, } static int cpcap_battery_cc_to_ua(struct cpcap_battery_ddata *ddata, - u32 sample, s32 accumulator, + s32 sample, s32 accumulator, s16 offset) { return cpcap_battery_cc_raw_div(ddata, sample, @@ -309,17 +309,19 @@ cpcap_battery_read_accumulated(struct cpcap_battery_ddata *ddata, /* Sample value CPCAP_REG_CCS1 & 2 */ ccd->sample = (buf[1] & 0x0fff) << 16; ccd->sample |= buf[0]; + if (ddata->vendor == CPCAP_VENDOR_TI) + ccd->sample = sign_extend32(24, ccd->sample); /* Accumulator value CPCAP_REG_CCA1 & 2 */ ccd->accumulator = ((s16)buf[3]) << 16; ccd->accumulator |= buf[2]; - /* Offset value CPCAP_REG_CCO */ - ccd->offset = buf[5]; - - /* Adjust offset based on mode value CPCAP_REG_CCM? */ - if (buf[4] >= 0x200) - ccd->offset |= 0xfc00; + /* + * Coulomb counter calibration offset is CPCAP_REG_CCM, + * REG_CCO seems unused + */ + ccd->offset = buf[4]; + ccd->offset = sign_extend32(ccd->offset, 9); return cpcap_battery_cc_to_uah(ddata, ccd->sample, @@ -474,11 +476,11 @@ static int cpcap_battery_get_property(struct power_supply *psy, val->intval = ddata->config.info.voltage_min_design; break; case POWER_SUPPLY_PROP_CURRENT_AVG: - if (cached) { + sample = latest->cc.sample - previous->cc.sample; + if (!sample) { val->intval = cpcap_battery_cc_get_avg_current(ddata); break; } - sample = latest->cc.sample - previous->cc.sample; accumulator = latest->cc.accumulator - previous->cc.accumulator; val->intval = cpcap_battery_cc_to_ua(ddata, sample, accumulator, @@ -495,13 +497,13 @@ static int cpcap_battery_get_property(struct power_supply *psy, val->intval = div64_s64(tmp, 100); break; case POWER_SUPPLY_PROP_POWER_AVG: - if (cached) { + sample = latest->cc.sample - previous->cc.sample; + if (!sample) { tmp = cpcap_battery_cc_get_avg_current(ddata); tmp *= (latest->voltage / 10000); val->intval = div64_s64(tmp, 100); break; } - sample = latest->cc.sample - previous->cc.sample; accumulator = latest->cc.accumulator - previous->cc.accumulator; tmp = cpcap_battery_cc_to_ua(ddata, sample, accumulator, latest->cc.offset); @@ -559,11 +561,11 @@ static irqreturn_t cpcap_battery_irq_thread(int irq, void *data) switch (d->action) { case CPCAP_BATTERY_IRQ_ACTION_BATTERY_LOW: - if (latest->counter_uah >= 0) + if (latest->current_ua >= 0) dev_warn(ddata->dev, "Battery low at 3.3V!\n"); break; case CPCAP_BATTERY_IRQ_ACTION_POWEROFF: - if (latest->counter_uah >= 0) { + if (latest->current_ua >= 0) { dev_emerg(ddata->dev, "Battery empty at 3.1V, powering off\n"); orderly_poweroff(true); @@ -667,8 +669,9 @@ static int cpcap_battery_init_iio(struct cpcap_battery_ddata *ddata) return 0; out_err: - dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", - error); + if (error != -EPROBE_DEFER) + dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", + error); return error; } diff --git a/drivers/power/supply/cpcap-charger.c b/drivers/power/supply/cpcap-charger.c index c3ed7b476676..b4781b5d1e10 100644 --- a/drivers/power/supply/cpcap-charger.c +++ b/drivers/power/supply/cpcap-charger.c @@ -574,8 +574,9 @@ static int cpcap_charger_init_iio(struct cpcap_charger_ddata *ddata) return 0; out_err: - dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", - error); + if (error != -EPROBE_DEFER) + dev_err(ddata->dev, "could not initialize VBUS or ID IIO: %i\n", + error); return error; } diff --git a/drivers/power/supply/gpio-charger.c b/drivers/power/supply/gpio-charger.c index 7e4f11d5a230..f99e8f1eef23 100644 --- a/drivers/power/supply/gpio-charger.c +++ b/drivers/power/supply/gpio-charger.c @@ -29,11 +29,13 @@ struct gpio_charger { unsigned int irq; + unsigned int charge_status_irq; bool wakeup_enabled; struct power_supply *charger; struct power_supply_desc charger_desc; struct gpio_desc *gpiod; + struct gpio_desc *charge_status; }; static irqreturn_t gpio_charger_irq(int irq, void *devid) @@ -59,6 +61,12 @@ static int gpio_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_ONLINE: val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod); break; + case POWER_SUPPLY_PROP_STATUS: + if (gpiod_get_value_cansleep(gpio_charger->charge_status)) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; default: return -EINVAL; } @@ -93,8 +101,29 @@ static enum power_supply_type gpio_charger_get_type(struct device *dev) return POWER_SUPPLY_TYPE_UNKNOWN; } +static int gpio_charger_get_irq(struct device *dev, void *dev_id, + struct gpio_desc *gpio) +{ + int ret, irq = gpiod_to_irq(gpio); + + if (irq > 0) { + ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + dev_name(dev), + dev_id); + if (ret < 0) { + dev_warn(dev, "Failed to request irq: %d\n", ret); + irq = 0; + } + } + + return irq; +} + static enum power_supply_property gpio_charger_properties[] = { POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_STATUS /* Must always be last in the array. */ }; static int gpio_charger_probe(struct platform_device *pdev) @@ -104,8 +133,10 @@ static int gpio_charger_probe(struct platform_device *pdev) struct power_supply_config psy_cfg = {}; struct gpio_charger *gpio_charger; struct power_supply_desc *charger_desc; + struct gpio_desc *charge_status; + int charge_status_irq; unsigned long flags; - int irq, ret; + int ret; if (!pdata && !dev->of_node) { dev_err(dev, "No platform data\n"); @@ -151,9 +182,17 @@ static int gpio_charger_probe(struct platform_device *pdev) return PTR_ERR(gpio_charger->gpiod); } + charge_status = devm_gpiod_get_optional(dev, "charge-status", GPIOD_IN); + gpio_charger->charge_status = charge_status; + if (IS_ERR(gpio_charger->charge_status)) + return PTR_ERR(gpio_charger->charge_status); + charger_desc = &gpio_charger->charger_desc; charger_desc->properties = gpio_charger_properties; charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); + /* Remove POWER_SUPPLY_PROP_STATUS from the supported properties. */ + if (!gpio_charger->charge_status) + charger_desc->num_properties -= 1; charger_desc->get_property = gpio_charger_get_property; psy_cfg.of_node = dev->of_node; @@ -180,16 +219,12 @@ static int gpio_charger_probe(struct platform_device *pdev) return ret; } - irq = gpiod_to_irq(gpio_charger->gpiod); - if (irq > 0) { - ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(dev), gpio_charger->charger); - if (ret < 0) - dev_warn(dev, "Failed to request irq: %d\n", ret); - else - gpio_charger->irq = irq; - } + gpio_charger->irq = gpio_charger_get_irq(dev, gpio_charger->charger, + gpio_charger->gpiod); + + charge_status_irq = gpio_charger_get_irq(dev, gpio_charger->charger, + gpio_charger->charge_status); + gpio_charger->charge_status_irq = charge_status_irq; platform_set_drvdata(pdev, gpio_charger); diff --git a/drivers/power/supply/ingenic-battery.c b/drivers/power/supply/ingenic-battery.c new file mode 100644 index 000000000000..35816d4b3012 --- /dev/null +++ b/drivers/power/supply/ingenic-battery.c @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Battery driver for the Ingenic JZ47xx SoCs + * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu> + * + * based on drivers/power/supply/jz4740-battery.c + */ + +#include <linux/iio/consumer.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/property.h> + +struct ingenic_battery { + struct device *dev; + struct iio_channel *channel; + struct power_supply_desc desc; + struct power_supply *battery; + struct power_supply_battery_info info; +}; + +static int ingenic_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct ingenic_battery *bat = power_supply_get_drvdata(psy); + struct power_supply_battery_info *info = &bat->info; + int ret; + + switch (psp) { + case POWER_SUPPLY_PROP_HEALTH: + ret = iio_read_channel_processed(bat->channel, &val->intval); + val->intval *= 1000; + if (val->intval < info->voltage_min_design_uv) + val->intval = POWER_SUPPLY_HEALTH_DEAD; + else if (val->intval > info->voltage_max_design_uv) + val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; + else + val->intval = POWER_SUPPLY_HEALTH_GOOD; + return ret; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = iio_read_channel_processed(bat->channel, &val->intval); + val->intval *= 1000; + return ret; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = info->voltage_min_design_uv; + return 0; + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = info->voltage_max_design_uv; + return 0; + default: + return -EINVAL; + }; +} + +/* Set the most appropriate IIO channel voltage reference scale + * based on the battery's max voltage. + */ +static int ingenic_battery_set_scale(struct ingenic_battery *bat) +{ + const int *scale_raw; + int scale_len, scale_type, best_idx = -1, best_mV, max_raw, i, ret; + u64 max_mV; + + ret = iio_read_max_channel_raw(bat->channel, &max_raw); + if (ret) { + dev_err(bat->dev, "Unable to read max raw channel value\n"); + return ret; + } + + ret = iio_read_avail_channel_attribute(bat->channel, &scale_raw, + &scale_type, &scale_len, + IIO_CHAN_INFO_SCALE); + if (ret < 0) { + dev_err(bat->dev, "Unable to read channel avail scale\n"); + return ret; + } + if (ret != IIO_AVAIL_LIST || scale_type != IIO_VAL_FRACTIONAL_LOG2) + return -EINVAL; + + max_mV = bat->info.voltage_max_design_uv / 1000; + + for (i = 0; i < scale_len; i += 2) { + u64 scale_mV = (max_raw * scale_raw[i]) >> scale_raw[i + 1]; + + if (scale_mV < max_mV) + continue; + + if (best_idx >= 0 && scale_mV > best_mV) + continue; + + best_mV = scale_mV; + best_idx = i; + } + + if (best_idx < 0) { + dev_err(bat->dev, "Unable to find matching voltage scale\n"); + return -EINVAL; + } + + return iio_write_channel_attribute(bat->channel, + scale_raw[best_idx], + scale_raw[best_idx + 1], + IIO_CHAN_INFO_SCALE); +} + +static enum power_supply_property ingenic_battery_properties[] = { + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, +}; + +static int ingenic_battery_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ingenic_battery *bat; + struct power_supply_config psy_cfg = {}; + struct power_supply_desc *desc; + int ret; + + bat = devm_kzalloc(dev, sizeof(*bat), GFP_KERNEL); + if (!bat) + return -ENOMEM; + + bat->dev = dev; + bat->channel = devm_iio_channel_get(dev, "battery"); + if (IS_ERR(bat->channel)) + return PTR_ERR(bat->channel); + + desc = &bat->desc; + desc->name = "jz-battery"; + desc->type = POWER_SUPPLY_TYPE_BATTERY; + desc->properties = ingenic_battery_properties; + desc->num_properties = ARRAY_SIZE(ingenic_battery_properties); + desc->get_property = ingenic_battery_get_property; + psy_cfg.drv_data = bat; + psy_cfg.of_node = dev->of_node; + + bat->battery = devm_power_supply_register(dev, desc, &psy_cfg); + if (IS_ERR(bat->battery)) { + dev_err(dev, "Unable to register battery\n"); + return PTR_ERR(bat->battery); + } + + ret = power_supply_get_battery_info(bat->battery, &bat->info); + if (ret) { + dev_err(dev, "Unable to get battery info: %d\n", ret); + return ret; + } + if (bat->info.voltage_min_design_uv < 0) { + dev_err(dev, "Unable to get voltage min design\n"); + return bat->info.voltage_min_design_uv; + } + if (bat->info.voltage_max_design_uv < 0) { + dev_err(dev, "Unable to get voltage max design\n"); + return bat->info.voltage_max_design_uv; + } + + return ingenic_battery_set_scale(bat); +} + +#ifdef CONFIG_OF +static const struct of_device_id ingenic_battery_of_match[] = { + { .compatible = "ingenic,jz4740-battery", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ingenic_battery_of_match); +#endif + +static struct platform_driver ingenic_battery_driver = { + .driver = { + .name = "ingenic-battery", + .of_match_table = of_match_ptr(ingenic_battery_of_match), + }, + .probe = ingenic_battery_probe, +}; +module_platform_driver(ingenic_battery_driver); + +MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs"); +MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/supply/ltc3651-charger.c b/drivers/power/supply/lt3651-charger.c index eea63ff211c4..8de500ffad95 100644 --- a/drivers/power/supply/ltc3651-charger.c +++ b/drivers/power/supply/lt3651-charger.c @@ -1,11 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0+ /* + * Driver for Analog Devices (Linear Technology) LT3651 charger IC. * Copyright (C) 2017, Topic Embedded Products - * Driver for LTC3651 charger IC. - * - * 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. */ #include <linux/device.h> @@ -19,7 +15,7 @@ #include <linux/slab.h> #include <linux/of.h> -struct ltc3651_charger { +struct lt3651_charger { struct power_supply *charger; struct power_supply_desc charger_desc; struct gpio_desc *acpr_gpio; @@ -27,7 +23,7 @@ struct ltc3651_charger { struct gpio_desc *chrg_gpio; }; -static irqreturn_t ltc3651_charger_irq(int irq, void *devid) +static irqreturn_t lt3651_charger_irq(int irq, void *devid) { struct power_supply *charger = devid; @@ -36,37 +32,37 @@ static irqreturn_t ltc3651_charger_irq(int irq, void *devid) return IRQ_HANDLED; } -static inline struct ltc3651_charger *psy_to_ltc3651_charger( +static inline struct lt3651_charger *psy_to_lt3651_charger( struct power_supply *psy) { return power_supply_get_drvdata(psy); } -static int ltc3651_charger_get_property(struct power_supply *psy, +static int lt3651_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { - struct ltc3651_charger *ltc3651_charger = psy_to_ltc3651_charger(psy); + struct lt3651_charger *lt3651_charger = psy_to_lt3651_charger(psy); switch (psp) { case POWER_SUPPLY_PROP_STATUS: - if (!ltc3651_charger->chrg_gpio) { + if (!lt3651_charger->chrg_gpio) { val->intval = POWER_SUPPLY_STATUS_UNKNOWN; break; } - if (gpiod_get_value(ltc3651_charger->chrg_gpio)) + if (gpiod_get_value(lt3651_charger->chrg_gpio)) val->intval = POWER_SUPPLY_STATUS_CHARGING; else val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case POWER_SUPPLY_PROP_ONLINE: - val->intval = gpiod_get_value(ltc3651_charger->acpr_gpio); + val->intval = gpiod_get_value(lt3651_charger->acpr_gpio); break; case POWER_SUPPLY_PROP_HEALTH: - if (!ltc3651_charger->fault_gpio) { + if (!lt3651_charger->fault_gpio) { val->intval = POWER_SUPPLY_HEALTH_UNKNOWN; break; } - if (!gpiod_get_value(ltc3651_charger->fault_gpio)) { + if (!gpiod_get_value(lt3651_charger->fault_gpio)) { val->intval = POWER_SUPPLY_HEALTH_GOOD; break; } @@ -74,11 +70,11 @@ static int ltc3651_charger_get_property(struct power_supply *psy, * If the fault pin is active, the chrg pin explains the type * of failure. */ - if (!ltc3651_charger->chrg_gpio) { + if (!lt3651_charger->chrg_gpio) { val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; break; } - val->intval = gpiod_get_value(ltc3651_charger->chrg_gpio) ? + val->intval = gpiod_get_value(lt3651_charger->chrg_gpio) ? POWER_SUPPLY_HEALTH_OVERHEAT : POWER_SUPPLY_HEALTH_DEAD; break; @@ -89,59 +85,59 @@ static int ltc3651_charger_get_property(struct power_supply *psy, return 0; } -static enum power_supply_property ltc3651_charger_properties[] = { +static enum power_supply_property lt3651_charger_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, }; -static int ltc3651_charger_probe(struct platform_device *pdev) +static int lt3651_charger_probe(struct platform_device *pdev) { struct power_supply_config psy_cfg = {}; - struct ltc3651_charger *ltc3651_charger; + struct lt3651_charger *lt3651_charger; struct power_supply_desc *charger_desc; int ret; - ltc3651_charger = devm_kzalloc(&pdev->dev, sizeof(*ltc3651_charger), + lt3651_charger = devm_kzalloc(&pdev->dev, sizeof(*lt3651_charger), GFP_KERNEL); - if (!ltc3651_charger) + if (!lt3651_charger) return -ENOMEM; - ltc3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev, + lt3651_charger->acpr_gpio = devm_gpiod_get(&pdev->dev, "lltc,acpr", GPIOD_IN); - if (IS_ERR(ltc3651_charger->acpr_gpio)) { - ret = PTR_ERR(ltc3651_charger->acpr_gpio); + if (IS_ERR(lt3651_charger->acpr_gpio)) { + ret = PTR_ERR(lt3651_charger->acpr_gpio); dev_err(&pdev->dev, "Failed to acquire acpr GPIO: %d\n", ret); return ret; } - ltc3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev, + lt3651_charger->fault_gpio = devm_gpiod_get_optional(&pdev->dev, "lltc,fault", GPIOD_IN); - if (IS_ERR(ltc3651_charger->fault_gpio)) { - ret = PTR_ERR(ltc3651_charger->fault_gpio); + if (IS_ERR(lt3651_charger->fault_gpio)) { + ret = PTR_ERR(lt3651_charger->fault_gpio); dev_err(&pdev->dev, "Failed to acquire fault GPIO: %d\n", ret); return ret; } - ltc3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev, + lt3651_charger->chrg_gpio = devm_gpiod_get_optional(&pdev->dev, "lltc,chrg", GPIOD_IN); - if (IS_ERR(ltc3651_charger->chrg_gpio)) { - ret = PTR_ERR(ltc3651_charger->chrg_gpio); + if (IS_ERR(lt3651_charger->chrg_gpio)) { + ret = PTR_ERR(lt3651_charger->chrg_gpio); dev_err(&pdev->dev, "Failed to acquire chrg GPIO: %d\n", ret); return ret; } - charger_desc = <c3651_charger->charger_desc; + charger_desc = <3651_charger->charger_desc; charger_desc->name = pdev->dev.of_node->name; charger_desc->type = POWER_SUPPLY_TYPE_MAINS; - charger_desc->properties = ltc3651_charger_properties; - charger_desc->num_properties = ARRAY_SIZE(ltc3651_charger_properties); - charger_desc->get_property = ltc3651_charger_get_property; + charger_desc->properties = lt3651_charger_properties; + charger_desc->num_properties = ARRAY_SIZE(lt3651_charger_properties); + charger_desc->get_property = lt3651_charger_get_property; psy_cfg.of_node = pdev->dev.of_node; - psy_cfg.drv_data = ltc3651_charger; + psy_cfg.drv_data = lt3651_charger; - ltc3651_charger->charger = devm_power_supply_register(&pdev->dev, + lt3651_charger->charger = devm_power_supply_register(&pdev->dev, charger_desc, &psy_cfg); - if (IS_ERR(ltc3651_charger->charger)) { - ret = PTR_ERR(ltc3651_charger->charger); + if (IS_ERR(lt3651_charger->charger)) { + ret = PTR_ERR(lt3651_charger->charger); dev_err(&pdev->dev, "Failed to register power supply: %d\n", ret); return ret; @@ -152,59 +148,60 @@ static int ltc3651_charger_probe(struct platform_device *pdev) * support IRQs on these pins, userspace will have to poll the sysfs * files manually. */ - if (ltc3651_charger->acpr_gpio) { - ret = gpiod_to_irq(ltc3651_charger->acpr_gpio); + if (lt3651_charger->acpr_gpio) { + ret = gpiod_to_irq(lt3651_charger->acpr_gpio); if (ret >= 0) ret = devm_request_any_context_irq(&pdev->dev, ret, - ltc3651_charger_irq, + lt3651_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), ltc3651_charger->charger); + dev_name(&pdev->dev), lt3651_charger->charger); if (ret < 0) dev_warn(&pdev->dev, "Failed to request acpr irq\n"); } - if (ltc3651_charger->fault_gpio) { - ret = gpiod_to_irq(ltc3651_charger->fault_gpio); + if (lt3651_charger->fault_gpio) { + ret = gpiod_to_irq(lt3651_charger->fault_gpio); if (ret >= 0) ret = devm_request_any_context_irq(&pdev->dev, ret, - ltc3651_charger_irq, + lt3651_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), ltc3651_charger->charger); + dev_name(&pdev->dev), lt3651_charger->charger); if (ret < 0) dev_warn(&pdev->dev, "Failed to request fault irq\n"); } - if (ltc3651_charger->chrg_gpio) { - ret = gpiod_to_irq(ltc3651_charger->chrg_gpio); + if (lt3651_charger->chrg_gpio) { + ret = gpiod_to_irq(lt3651_charger->chrg_gpio); if (ret >= 0) ret = devm_request_any_context_irq(&pdev->dev, ret, - ltc3651_charger_irq, + lt3651_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - dev_name(&pdev->dev), ltc3651_charger->charger); + dev_name(&pdev->dev), lt3651_charger->charger); if (ret < 0) dev_warn(&pdev->dev, "Failed to request chrg irq\n"); } - platform_set_drvdata(pdev, ltc3651_charger); + platform_set_drvdata(pdev, lt3651_charger); return 0; } -static const struct of_device_id ltc3651_charger_match[] = { - { .compatible = "lltc,ltc3651-charger" }, +static const struct of_device_id lt3651_charger_match[] = { + { .compatible = "lltc,ltc3651-charger" }, /* DEPRECATED */ + { .compatible = "lltc,lt3651-charger" }, { } }; -MODULE_DEVICE_TABLE(of, ltc3651_charger_match); +MODULE_DEVICE_TABLE(of, lt3651_charger_match); -static struct platform_driver ltc3651_charger_driver = { - .probe = ltc3651_charger_probe, +static struct platform_driver lt3651_charger_driver = { + .probe = lt3651_charger_probe, .driver = { - .name = "ltc3651-charger", - .of_match_table = ltc3651_charger_match, + .name = "lt3651-charger", + .of_match_table = lt3651_charger_match, }, }; -module_platform_driver(ltc3651_charger_driver); +module_platform_driver(lt3651_charger_driver); MODULE_AUTHOR("Mike Looijmans <mike.looijmans@topic.nl>"); -MODULE_DESCRIPTION("Driver for LTC3651 charger"); +MODULE_DESCRIPTION("Driver for LT3651 charger"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:ltc3651-charger"); +MODULE_ALIAS("platform:lt3651-charger"); diff --git a/drivers/power/supply/max14656_charger_detector.c b/drivers/power/supply/max14656_charger_detector.c index b91b1d2999dc..9e6472834e37 100644 --- a/drivers/power/supply/max14656_charger_detector.c +++ b/drivers/power/supply/max14656_charger_detector.c @@ -240,6 +240,14 @@ static enum power_supply_property max14656_battery_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; +static void stop_irq_work(void *data) +{ + struct max14656_chip *chip = data; + + cancel_delayed_work_sync(&chip->irq_work); +} + + static int max14656_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -278,7 +286,19 @@ static int max14656_probe(struct i2c_client *client, if (ret) return -ENODEV; + chip->detect_psy = devm_power_supply_register(dev, + &chip->psy_desc, &psy_cfg); + if (IS_ERR(chip->detect_psy)) { + dev_err(dev, "power_supply_register failed\n"); + return -EINVAL; + } + INIT_DELAYED_WORK(&chip->irq_work, max14656_irq_worker); + ret = devm_add_action(dev, stop_irq_work, chip); + if (ret) { + dev_err(dev, "devm_add_action %d failed\n", ret); + return ret; + } ret = devm_request_irq(dev, chip->irq, max14656_irq, IRQF_TRIGGER_FALLING, @@ -289,13 +309,6 @@ static int max14656_probe(struct i2c_client *client, } enable_irq_wake(chip->irq); - chip->detect_psy = devm_power_supply_register(dev, - &chip->psy_desc, &psy_cfg); - if (IS_ERR(chip->detect_psy)) { - dev_err(dev, "power_supply_register failed\n"); - return -EINVAL; - } - schedule_delayed_work(&chip->irq_work, msecs_to_jiffies(2000)); return 0; diff --git a/drivers/power/supply/power_supply_core.c b/drivers/power/supply/power_supply_core.c index c917a8b43b2b..874495c6face 100644 --- a/drivers/power/supply/power_supply_core.c +++ b/drivers/power/supply/power_supply_core.c @@ -598,10 +598,12 @@ int power_supply_get_battery_info(struct power_supply *psy, err = of_property_read_string(battery_np, "compatible", &value); if (err) - return err; + goto out_put_node; - if (strcmp("simple-battery", value)) - return -ENODEV; + if (strcmp("simple-battery", value)) { + err = -ENODEV; + goto out_put_node; + } /* The property and field names below must correspond to elements * in enum power_supply_property. For reasoning, see @@ -620,19 +622,21 @@ int power_supply_get_battery_info(struct power_supply *psy, &info->precharge_current_ua); of_property_read_u32(battery_np, "charge-term-current-microamp", &info->charge_term_current_ua); - of_property_read_u32(battery_np, "constant_charge_current_max_microamp", + of_property_read_u32(battery_np, "constant-charge-current-max-microamp", &info->constant_charge_current_max_ua); - of_property_read_u32(battery_np, "constant_charge_voltage_max_microvolt", + of_property_read_u32(battery_np, "constant-charge-voltage-max-microvolt", &info->constant_charge_voltage_max_uv); of_property_read_u32(battery_np, "factory-internal-resistance-micro-ohms", &info->factory_internal_resistance_uohm); len = of_property_count_u32_elems(battery_np, "ocv-capacity-celsius"); if (len < 0 && len != -EINVAL) { - return len; + err = len; + goto out_put_node; } else if (len > POWER_SUPPLY_OCV_TEMP_MAX) { dev_err(&psy->dev, "Too many temperature values\n"); - return -EINVAL; + err = -EINVAL; + goto out_put_node; } else if (len > 0) { of_property_read_u32_array(battery_np, "ocv-capacity-celsius", info->ocv_temp, len); @@ -650,7 +654,8 @@ int power_supply_get_battery_info(struct power_supply *psy, dev_err(&psy->dev, "failed to get %s\n", propname); kfree(propname); power_supply_put_battery_info(psy, info); - return -EINVAL; + err = -EINVAL; + goto out_put_node; } kfree(propname); @@ -661,7 +666,8 @@ int power_supply_get_battery_info(struct power_supply *psy, devm_kcalloc(&psy->dev, tab_len, sizeof(*table), GFP_KERNEL); if (!info->ocv_table[index]) { power_supply_put_battery_info(psy, info); - return -ENOMEM; + err = -ENOMEM; + goto out_put_node; } for (i = 0; i < tab_len; i++) { @@ -670,7 +676,9 @@ int power_supply_get_battery_info(struct power_supply *psy, } } - return 0; +out_put_node: + of_node_put(battery_np); + return err; } EXPORT_SYMBOL_GPL(power_supply_get_battery_info); @@ -899,7 +907,7 @@ static int ps_get_max_charge_cntl_limit(struct thermal_cooling_device *tcd, return ret; } -static int ps_get_cur_chrage_cntl_limit(struct thermal_cooling_device *tcd, +static int ps_get_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, unsigned long *state) { struct power_supply *psy; @@ -934,7 +942,7 @@ static int ps_set_cur_charge_cntl_limit(struct thermal_cooling_device *tcd, static const struct thermal_cooling_device_ops psy_tcd_ops = { .get_max_state = ps_get_max_charge_cntl_limit, - .get_cur_state = ps_get_cur_chrage_cntl_limit, + .get_cur_state = ps_get_cur_charge_cntl_limit, .set_cur_state = ps_set_cur_charge_cntl_limit, }; |