Commit 3ac684b8 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply

Pull power supply and reset updates from Sebastian Reichel:

 - Microsemi Ocelot reset support

 - Spreadtrum SC27xx reset support

 - generic gpio charger: lot's of cleanups

 - axp20x fuel gauge: add AXP813 support

 - misc fixes, including one devicetree change for the Nokia N900, that
   has been Acked-by Tony Lindgren

* tag 'for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply: (27 commits)
  power: reset: at91-reset: Switch from the pr_*() to the dev_*() logging functions
  power: reset: at91-poweroff: Remove redundant dev_err call in at91_poweroff_probe()
  power: reset: at91-poweroff: Switch from the pr_*() to the dev_*() logging functions
  power: reset: make function sc27xx_poweroff_shutdown static
  power: supply: da9150-fg: remove VLA usage
  ARM: dts: omap3-n900: Add link between battery and charger
  power: supply: bq2415x: add DT referencing support
  power: supply: bq27xxx: support missing supplier device
  max17042: propagate of_node to power supply device
  power: supply: axp288_fuel_gauge: Fix full status reporting
  power: supply: axp288_fuel_gauge: Do not register FG on ECS EF20EA
  power: reset: gpio-poweroff: Support for timeout from device property
  dt-bindings: power: reset: gpio-poweroff: Add 'timeout-ms' property
  power: reset: Add Spreadtrum SC27xx PMIC power off support
  power: supply: axp20x_battery: add support for AXP813
  dt-bindings: power: supply: axp20x: add AXP813 battery DT binding
  power: supply: axp20x_battery: use data struct for variant specific code
  power: supply: gpio-charger: Remove pdata from gpio_charger
  power: supply: gpio-charger: Use GPIOF_ACTIVE_LOW for legacy setup
  power: supply: gpio-charger: Remove redundant dev_err call in probe function
  ...
parents 75dcc7ef fd73a3e6
...@@ -27,10 +27,13 @@ Optional properties: ...@@ -27,10 +27,13 @@ Optional properties:
it to an output when the power-off handler is called. If this optional it to an output when the power-off handler is called. If this optional
property is not specified, the GPIO is initialized as an output in its property is not specified, the GPIO is initialized as an output in its
inactive state. inactive state.
- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
specified, 3000 ms is used.
Examples: Examples:
gpio-poweroff { gpio-poweroff {
compatible = "gpio-poweroff"; compatible = "gpio-poweroff";
gpios = <&gpio 4 0>; gpios = <&gpio 4 0>;
timeout-ms = <3000>;
}; };
Microsemi Ocelot reset controller
The DEVCPU_GCB:CHIP_REGS have a SOFT_RST register that can be used to reset the
SoC MIPS core.
Required Properties:
- compatible: "mscc,ocelot-chip-reset"
Example:
reset@1070008 {
compatible = "mscc,ocelot-chip-reset";
reg = <0x1070008 0x4>;
};
...@@ -4,12 +4,12 @@ Required Properties: ...@@ -4,12 +4,12 @@ Required Properties:
- compatible, one of: - compatible, one of:
"x-powers,axp209-battery-power-supply" "x-powers,axp209-battery-power-supply"
"x-powers,axp221-battery-power-supply" "x-powers,axp221-battery-power-supply"
"x-powers,axp813-battery-power-supply"
This node is a subnode of the axp20x/axp22x PMIC. This node is a subnode of its respective PMIC DT node.
The AXP20X and AXP22X can read the battery voltage, charge and discharge The supported devices can read the battery voltage, charge and discharge
currents of the battery by reading ADC channels from the AXP20X/AXP22X currents of the battery by reading ADC channels from the ADC.
ADC.
Example: Example:
......
...@@ -673,6 +673,7 @@ chan8 { ...@@ -673,6 +673,7 @@ chan8 {
bq27200: bq27200@55 { bq27200: bq27200@55 {
compatible = "ti,bq27200"; compatible = "ti,bq27200";
reg = <0x55>; reg = <0x55>;
power-supplies = <&bq24150a>;
}; };
/* Stereo headphone amplifier */ /* Stereo headphone amplifier */
......
...@@ -104,6 +104,13 @@ config POWER_RESET_MSM ...@@ -104,6 +104,13 @@ config POWER_RESET_MSM
help help
Power off and restart support for Qualcomm boards. Power off and restart support for Qualcomm boards.
config POWER_RESET_OCELOT_RESET
bool "Microsemi Ocelot reset driver"
depends on MSCC_OCELOT || COMPILE_TEST
select MFD_SYSCON
help
This driver supports restart for Microsemi Ocelot SoC.
config POWER_RESET_PIIX4_POWEROFF config POWER_RESET_PIIX4_POWEROFF
tristate "Intel PIIX4 power-off driver" tristate "Intel PIIX4 power-off driver"
depends on PCI depends on PCI
...@@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE ...@@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE
register, then the bootloader can read it to take different register, then the bootloader can read it to take different
action according to the mode. action according to the mode.
config POWER_RESET_SC27XX
bool "Spreadtrum SC27xx PMIC power-off driver"
depends on MFD_SC27XX_PMIC || COMPILE_TEST
help
This driver supports powering off a system through
Spreadtrum SC27xx series PMICs. The SC27xx series
PMICs includes the SC2720, SC2721, SC2723, SC2730
and SC2731 chips.
endif endif
...@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o ...@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
...@@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o ...@@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
...@@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base; ...@@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base;
static struct clk *sclk; static struct clk *sclk;
static void __iomem *mpddrc_base; static void __iomem *mpddrc_base;
static void __init at91_wakeup_status(void) static void __init at91_wakeup_status(struct platform_device *pdev)
{ {
const char *reason;
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR); u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
char *reason = "unknown";
/* Simple power-on, just bail out */ /* Simple power-on, just bail out */
if (!reg) if (!reg)
...@@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void) ...@@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void)
reason = "RTT"; reason = "RTT";
else if (reg & AT91_SHDW_RTCWK) else if (reg & AT91_SHDW_RTCWK)
reason = "RTC"; reason = "RTC";
else
reason = "unknown";
pr_info("AT91: Wake-Up source: %s\n", reason); dev_info(&pdev->dev, "Wake-Up source: %s\n", reason);
} }
static void at91_poweroff(void) static void at91_poweroff(void)
...@@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) ...@@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res); at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(at91_shdwc_base)) { if (IS_ERR(at91_shdwc_base))
dev_err(&pdev->dev, "Could not map reset controller address\n");
return PTR_ERR(at91_shdwc_base); return PTR_ERR(at91_shdwc_base);
}
sclk = devm_clk_get(&pdev->dev, NULL); sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk)) if (IS_ERR(sclk))
...@@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev) ...@@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
return ret; return ret;
} }
at91_wakeup_status(); at91_wakeup_status(pdev);
if (pdev->dev.of_node) if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev); at91_poweroff_dt_set_wakeup_mode(pdev);
......
...@@ -145,8 +145,8 @@ static int samx7_restart(struct notifier_block *this, unsigned long mode, ...@@ -145,8 +145,8 @@ static int samx7_restart(struct notifier_block *this, unsigned long mode,
static void __init at91_reset_status(struct platform_device *pdev) static void __init at91_reset_status(struct platform_device *pdev)
{ {
const char *reason;
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR); u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
char *reason;
switch ((reg & AT91_RSTC_RSTTYP) >> 8) { switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL: case RESET_TYPE_GENERAL:
...@@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev) ...@@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev)
break; break;
} }
pr_info("AT91: Starting after %s\n", reason); dev_info(&pdev->dev, "Starting after %s\n", reason);
} }
static const struct of_device_id at91_ramc_of_match[] = { static const struct of_device_id at91_ramc_of_match[] = {
......
...@@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data) ...@@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
val &= 0x70U; val &= 0x70U;
switch (val) { switch (val) {
case GEMINI_STAT_CIR: case GEMINI_STAT_CIR:
dev_info(gpw->dev, "infrared poweroff\n"); /*
orderly_poweroff(true); * We do not yet have a driver for the infrared
* controller so it can cause spurious poweroff
* events. Ignore those for now.
*/
dev_info(gpw->dev, "infrared poweroff - ignored\n");
break; break;
case GEMINI_STAT_RTC: case GEMINI_STAT_RTC:
dev_info(gpw->dev, "RTC poweroff\n"); dev_info(gpw->dev, "RTC poweroff\n");
...@@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev) ...@@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
/* Clear the power management IRQ */ /*
* Enable the power controller. This is crucial on Gemini
* systems: if this is not done, pressing the power button
* will result in unconditional poweroff without any warning.
* This makes the kernel handle the poweroff.
*/
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_ENABLE;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
/* Now that the state machine is active, clear the IRQ */
val = readl(gpw->base + GEMINI_PWC_CTRLREG); val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR; val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG); writel(val, gpw->base + GEMINI_PWC_CTRLREG);
...@@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev) ...@@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
pm_power_off = gemini_poweroff; pm_power_off = gemini_poweroff;
gpw_poweroff = gpw; gpw_poweroff = gpw;
/*
* Enable the power controller. This is crucial on Gemini
* systems: if this is not done, pressing the power button
* will result in unconditional poweroff without any warning.
* This makes the kernel handle the poweroff.
*/
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_ENABLE;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
dev_info(dev, "Gemini poweroff driver registered\n"); dev_info(dev, "Gemini poweroff driver registered\n");
return 0; return 0;
......
...@@ -19,11 +19,13 @@ ...@@ -19,11 +19,13 @@
#include <linux/of_platform.h> #include <linux/of_platform.h>
#include <linux/module.h> #include <linux/module.h>
#define DEFAULT_TIMEOUT_MS 3000
/* /*
* Hold configuration here, cannot be more than one instance of the driver * Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global. * since pm_power_off itself is global.
*/ */
static struct gpio_desc *reset_gpio; static struct gpio_desc *reset_gpio;
static u32 timeout = DEFAULT_TIMEOUT_MS;
static void gpio_poweroff_do_poweroff(void) static void gpio_poweroff_do_poweroff(void)
{ {
...@@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void) ...@@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void)
gpiod_set_value(reset_gpio, 1); gpiod_set_value(reset_gpio, 1);
/* give it some time */ /* give it some time */
mdelay(3000); mdelay(timeout);
WARN_ON(1); WARN_ON(1);
} }
...@@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev) ...@@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
return -EBUSY; return -EBUSY;
} }
input = of_property_read_bool(pdev->dev.of_node, "input"); input = device_property_read_bool(&pdev->dev, "input");
if (input) if (input)
flags = GPIOD_IN; flags = GPIOD_IN;
else else
flags = GPIOD_OUT_LOW; flags = GPIOD_OUT_LOW;
device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags); reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
if (IS_ERR(reset_gpio)) if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio); return PTR_ERR(reset_gpio);
......
// SPDX-License-Identifier: (GPL-2.0 OR MIT)
/*
* Microsemi MIPS SoC reset driver
*
* License: Dual MIT/GPL
* Copyright (c) 2017 Microsemi Corporation
*/
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/notifier.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
struct ocelot_reset_context {
void __iomem *base;
struct regmap *cpu_ctrl;
struct notifier_block restart_handler;
};
#define ICPU_CFG_CPU_SYSTEM_CTRL_RESET 0x20
#define CORE_RST_PROTECT BIT(2)
#define SOFT_CHIP_RST BIT(0)
static int ocelot_restart_handle(struct notifier_block *this,
unsigned long mode, void *cmd)
{
struct ocelot_reset_context *ctx = container_of(this, struct
ocelot_reset_context,
restart_handler);
/* Make sure the core is not protected from reset */
regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET,
CORE_RST_PROTECT, 0);
writel(SOFT_CHIP_RST, ctx->base);
pr_emerg("Unable to restart system\n");
return NOTIFY_DONE;
}
static int ocelot_reset_probe(struct platform_device *pdev)
{
struct ocelot_reset_context *ctx;
struct resource *res;
struct device *dev = &pdev->dev;
int err;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
ctx->base = devm_ioremap_resource(dev, res);
if (IS_ERR(ctx->base))
return PTR_ERR(ctx->base);
ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon");
if (IS_ERR(ctx->cpu_ctrl))
return PTR_ERR(ctx->cpu_ctrl);
ctx->restart_handler.notifier_call = ocelot_restart_handle;
ctx->restart_handler.priority = 192;
err = register_restart_handler(&ctx->restart_handler);
if (err)
dev_err(dev, "can't register restart notifier (err=%d)\n", err);
return err;
}
static const struct of_device_id ocelot_reset_of_match[] = {
{ .compatible = "mscc,ocelot-chip-reset" },
{}
};
static struct platform_driver ocelot_reset_driver = {
.probe = ocelot_reset_probe,
.driver = {
.name = "ocelot-chip-reset",
.of_match_table = ocelot_reset_of_match,
},
};
builtin_platform_driver(ocelot_reset_driver);
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2018 Spreadtrum Communications Inc.
* Copyright (C) 2018 Linaro Ltd.
*/
#include <linux/cpu.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#include <linux/syscore_ops.h>
#define SC27XX_PWR_PD_HW 0xc2c
#define SC27XX_PWR_OFF_EN BIT(0)
static struct regmap *regmap;
/*
* On Spreadtrum platform, we need power off system through external SC27xx
* series PMICs, and it is one similar SPI bus mapped by regmap to access PMIC,
* which is not fast io access.
*
* So before stopping other cores, we need release other cores' resource by
* taking cpus down to avoid racing regmap or spi mutex lock when poweroff
* system through PMIC.
*/
static void sc27xx_poweroff_shutdown(void)
{
#ifdef CONFIG_PM_SLEEP_SMP
int cpu = smp_processor_id();
freeze_secondary_cpus(cpu);
#endif
}
static struct syscore_ops poweroff_syscore_ops = {
.shutdown = sc27xx_poweroff_shutdown,
};
static void sc27xx_poweroff_do_poweroff(void)
{
regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN);
}
static int sc27xx_poweroff_probe(struct platform_device *pdev)
{
if (regmap)
return -EINVAL;
regmap = dev_get_regmap(pdev->dev.parent, NULL);
if (!regmap)
return -ENODEV;
pm_power_off = sc27xx_poweroff_do_poweroff;
register_syscore_ops(&poweroff_syscore_ops);
return 0;
}
static struct platform_driver sc27xx_poweroff_driver = {
.probe = sc27xx_poweroff_probe,
.driver = {
.name = "sc27xx-poweroff",
},
};
builtin_platform_driver(sc27xx_poweroff_driver);
...@@ -49,10 +49,22 @@ ...@@ -49,10 +49,22 @@
#define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5) #define AXP22X_CHRG_CTRL1_TGT_4_22V (1 << 5)
#define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5) #define AXP22X_CHRG_CTRL1_TGT_4_24V (3 << 5)
#define AXP813_CHRG_CTRL1_TGT_4_35V (3 << 5)
#define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0) #define AXP20X_CHRG_CTRL1_TGT_CURR GENMASK(3, 0)
#define AXP20X_V_OFF_MASK GENMASK(2, 0) #define AXP20X_V_OFF_MASK GENMASK(2, 0)
struct axp20x_batt_ps;
struct axp_data {
int ccc_scale;
int ccc_offset;
bool has_fg_valid;
int (*get_max_voltage)(struct axp20x_batt_ps *batt, int *val);
int (*set_max_voltage)(struct axp20x_batt_ps *batt, int val);
};
struct axp20x_batt_ps { struct axp20x_batt_ps {
struct regmap *regmap; struct regmap *regmap;
struct power_supply *batt; struct power_supply *batt;
...@@ -62,7 +74,7 @@ struct axp20x_batt_ps { ...@@ -62,7 +74,7 @@ struct axp20x_batt_ps {
struct iio_channel *batt_v; struct iio_channel *batt_v;
/* Maximum constant charge current */ /* Maximum constant charge current */
unsigned int max_ccc; unsigned int max_ccc;
u8 axp_id; const struct axp_data *data;
}; };
static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, static int axp20x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
...@@ -123,20 +135,33 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt, ...@@ -123,20 +135,33 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
return 0; return 0;
} }
static void raw_to_constant_charge_current(struct axp20x_batt_ps *axp, int *val) static int axp813_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int *val)
{ {
if (axp->axp_id == AXP209_ID) int ret, reg;
*val = *val * 100000 + 300000;
else
*val = *val * 150000 + 300000;
}
static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val) ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, &reg);
{ if (ret)
if (axp->axp_id == AXP209_ID) return ret;
*val = (*val - 300000) / 100000;
else switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
*val = (*val - 300000) / 150000; case AXP20X_CHRG_CTRL1_TGT_4_1V:
*val = 4100000;
break;
case AXP20X_CHRG_CTRL1_TGT_4_15V:
*val = 4150000;
break;
case AXP20X_CHRG_CTRL1_TGT_4_2V:
*val = 4200000;
break;
case AXP813_CHRG_CTRL1_TGT_4_35V:
*val = 4350000;
break;
default:
return -EINVAL;
}
return 0;
} }
static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
...@@ -150,7 +175,7 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp, ...@@ -150,7 +175,7 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
*val &= AXP20X_CHRG_CTRL1_TGT_CURR; *val &= AXP20X_CHRG_CTRL1_TGT_CURR;
raw_to_constant_charge_current(axp, val); *val = *val * axp->data->ccc_scale + axp->data->ccc_offset;
return 0; return 0;
} }
...@@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy, ...@@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
if (ret) if (ret)
return ret; return ret;
if (axp20x_batt->axp_id == AXP221_ID && if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID))
!(reg & AXP22X_FG_VALID))
return -EINVAL; return -EINVAL;
/* /*
...@@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy, ...@@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
break; break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
if (axp20x_batt->axp_id == AXP209_ID) return axp20x_batt->data->get_max_voltage(axp20x_batt,
return axp20x_battery_get_max_voltage(axp20x_batt, &val->intval);
&val->intval);
return axp22x_battery_get_max_voltage(axp20x_batt,
&val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, &reg); ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, &reg);
...@@ -312,6 +333,32 @@ static int axp20x_battery_get_prop(struct power_supply *psy, ...@@ -312,6 +333,32 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
return 0; return 0;
} }
static int axp22x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int val)
{
switch (val) {
case 4100000:
val = AXP20X_CHRG_CTRL1_TGT_4_1V;
break;
case 4200000:
val = AXP20X_CHRG_CTRL1_TGT_4_2V;
break;
default:
/*
* AXP20x max voltage can be set to 4.36V and AXP22X max voltage
* can be set to 4.22V and 4.24V, but these voltages are too
* high for Lithium based batteries (AXP PMICs are supposed to
* be used with these kinds of battery).
*/
return -EINVAL;
}
return regmap_update_bits(axp20x_batt->regmap, AXP20X_CHRG_CTRL1,
AXP20X_CHRG_CTRL1_TGT_VOLT, val);
}
static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
int val) int val)
{ {
...@@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt, ...@@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
break; break;
case 4150000: case 4150000:
if (axp20x_batt->axp_id == AXP221_ID)
return -EINVAL;
val = AXP20X_CHRG_CTRL1_TGT_4_15V; val = AXP20X_CHRG_CTRL1_TGT_4_15V;
break; break;
...@@ -351,7 +395,8 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt, ...@@ -351,7 +395,8 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
if (charge_current > axp_batt->max_ccc) if (charge_current > axp_batt->max_ccc)
return -EINVAL; return -EINVAL;
constant_charge_current_to_raw(axp_batt, &charge_current); charge_current = (charge_current - axp_batt->data->ccc_offset) /
axp_batt->data->ccc_scale;
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL; return -EINVAL;
...@@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp, ...@@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
{ {
bool lower_max = false; bool lower_max = false;
constant_charge_current_to_raw(axp, &charge_current); charge_current = (charge_current - axp->data->ccc_offset) /
axp->data->ccc_scale;
if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0) if (charge_current > AXP20X_CHRG_CTRL1_TGT_CURR || charge_current < 0)
return -EINVAL; return -EINVAL;
raw_to_constant_charge_current(axp, &charge_current); charge_current = charge_current * axp->data->ccc_scale +
axp->data->ccc_offset;
if (charge_current > axp->max_ccc) if (charge_current > axp->max_ccc)
dev_warn(axp->dev, dev_warn(axp->dev,
...@@ -413,7 +460,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy, ...@@ -413,7 +460,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
return axp20x_set_voltage_min_design(axp20x_batt, val->intval); return axp20x_set_voltage_min_design(axp20x_batt, val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
return axp20x_battery_set_max_voltage(axp20x_batt, val->intval); return axp20x_batt->data->set_max_voltage(axp20x_batt, val->intval);
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
return axp20x_set_constant_charge_current(axp20x_batt, return axp20x_set_constant_charge_current(axp20x_batt,
...@@ -460,13 +507,39 @@ static const struct power_supply_desc axp20x_batt_ps_desc = { ...@@ -460,13 +507,39 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
.set_property = axp20x_battery_set_prop, .set_property = axp20x_battery_set_prop,
}; };
static const struct axp_data axp209_data = {
.ccc_scale = 100000,
.ccc_offset = 300000,
.get_max_voltage = axp20x_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
};
static const struct axp_data axp221_data = {
.ccc_scale = 150000,
.ccc_offset = 300000,
.has_fg_valid = true,
.get_max_voltage = axp22x_battery_get_max_voltage,
.set_max_voltage = axp22x_battery_set_max_voltage,
};
static const struct axp_data axp813_data = {
.ccc_scale = 200000,
.ccc_offset = 200000,
.has_fg_valid = true,
.get_max_voltage = axp813_battery_get_max_voltage,
.set_max_voltage = axp20x_battery_set_max_voltage,
};
static const struct of_device_id axp20x_battery_ps_id[] = { static const struct of_device_id axp20x_battery_ps_id[] = {
{ {
.compatible = "x-powers,axp209-battery-power-supply", .compatible = "x-powers,axp209-battery-power-supply",
.data = (void *)AXP209_ID, .data = (void *)&axp209_data,
}, { }, {
.compatible = "x-powers,axp221-battery-power-supply", .compatible = "x-powers,axp221-battery-power-supply",
.data = (void *)AXP221_ID, .data = (void *)&axp221_data,
}, {
.compatible = "x-powers,axp813-battery-power-supply",
.data = (void *)&axp813_data,
}, { /* sentinel */ }, }, { /* sentinel */ },
}; };
MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id); MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
...@@ -476,6 +549,7 @@ static int axp20x_power_probe(struct platform_device *pdev) ...@@ -476,6 +549,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
struct axp20x_batt_ps *axp20x_batt; struct axp20x_batt_ps *axp20x_batt;
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct power_supply_battery_info info; struct power_supply_battery_info info;
struct device *dev = &pdev->dev;
if (!of_device_is_available(pdev->dev.of_node)) if (!of_device_is_available(pdev->dev.of_node))
return -ENODEV; return -ENODEV;
...@@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev) ...@@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
psy_cfg.drv_data = axp20x_batt; psy_cfg.drv_data = axp20x_batt;
psy_cfg.of_node = pdev->dev.of_node; psy_cfg.of_node = pdev->dev.of_node;
axp20x_batt->axp_id = (uintptr_t)of_device_get_match_data(&pdev->dev); axp20x_batt->data = (struct axp_data *)of_device_get_match_data(dev);
axp20x_batt->batt = devm_power_supply_register(&pdev->dev, axp20x_batt->batt = devm_power_supply_register(&pdev->dev,
&axp20x_batt_ps_desc, &axp20x_batt_ps_desc,
......
...@@ -343,7 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info) ...@@ -343,7 +343,7 @@ static inline void fuel_gauge_remove_debugfs(struct axp288_fg_info *info)
static void fuel_gauge_get_status(struct axp288_fg_info *info) static void fuel_gauge_get_status(struct axp288_fg_info *info)
{ {
int pwr_stat, fg_res; int pwr_stat, fg_res, curr, ret;
pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS); pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
if (pwr_stat < 0) { if (pwr_stat < 0) {
...@@ -353,19 +353,42 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info) ...@@ -353,19 +353,42 @@ static void fuel_gauge_get_status(struct axp288_fg_info *info)
} }
/* Report full if Vbus is valid and the reported capacity is 100% */ /* Report full if Vbus is valid and the reported capacity is 100% */
if (pwr_stat & PS_STAT_VBUS_VALID) { if (!(pwr_stat & PS_STAT_VBUS_VALID))
fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES); goto not_full;
if (fg_res < 0) {
dev_err(&info->pdev->dev, fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
"FG RES read failed: %d\n", fg_res); if (fg_res < 0) {
return; dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
} return;
if (fg_res == (FG_REP_CAP_VALID | 100)) {
info->status = POWER_SUPPLY_STATUS_FULL;
return;
}
} }
if (!(fg_res & FG_REP_CAP_VALID))
goto not_full;
fg_res &= ~FG_REP_CAP_VALID;
if (fg_res == 100) {
info->status = POWER_SUPPLY_STATUS_FULL;
return;
}
/*
* Sometimes the charger turns itself off before fg-res reaches 100%.
* When this happens the AXP288 reports a not-charging status and
* 0 mA discharge current.
*/
if (fg_res < 90 || (pwr_stat & PS_STAT_BAT_CHRG_DIR))
goto not_full;
ret = iio_read_channel_raw(info->iio_channel[BAT_D_CURR], &curr);
if (ret < 0) {
dev_err(&info->pdev->dev, "FG get current failed: %d\n", ret);
return;
}
if (curr == 0) {
info->status = POWER_SUPPLY_STATUS_FULL;
return;
}
not_full:
if (pwr_stat & PS_STAT_BAT_CHRG_DIR) if (pwr_stat & PS_STAT_BAT_CHRG_DIR)
info->status = POWER_SUPPLY_STATUS_CHARGING; info->status = POWER_SUPPLY_STATUS_CHARGING;
else else
...@@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = { ...@@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
DMI_MATCH(DMI_BOARD_VERSION, "V1.1"), DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
}, },
}, },
{
/* ECS EF20EA */
.matches = {
DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
},
},
{} {}
}; };
......
...@@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq) ...@@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
int ret; int ret;
int chip; int chip;
char revstr[8]; char revstr[8];
struct power_supply_config psy_cfg = { .drv_data = bq, }; struct power_supply_config psy_cfg = {
.drv_data = bq,
.of_node = bq->dev->of_node,
};
bq->charger_desc.name = bq->name; bq->charger_desc.name = bq->name;
bq->charger_desc.type = POWER_SUPPLY_TYPE_USB; bq->charger_desc.type = POWER_SUPPLY_TYPE_USB;
......
...@@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di, ...@@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
status = POWER_SUPPLY_STATUS_FULL; status = POWER_SUPPLY_STATUS_FULL;
else if (di->cache.flags & BQ27000_FLAG_CHGS) else if (di->cache.flags & BQ27000_FLAG_CHGS)
status = POWER_SUPPLY_STATUS_CHARGING; status = POWER_SUPPLY_STATUS_CHARGING;
else if (power_supply_am_i_supplied(di->bat)) else if (power_supply_am_i_supplied(di->bat) > 0)
status = POWER_SUPPLY_STATUS_NOT_CHARGING; status = POWER_SUPPLY_STATUS_NOT_CHARGING;
else else
status = POWER_SUPPLY_STATUS_DISCHARGING; status = POWER_SUPPLY_STATUS_DISCHARGING;
......
...@@ -92,7 +92,7 @@ struct da9150_fg { ...@@ -92,7 +92,7 @@ struct da9150_fg {
static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size) static u32 da9150_fg_read_attr(struct da9150_fg *fg, u8 code, u8 size)
{ {
u8 buf[size]; u8 buf[DA9150_QIF_LONG_SIZE];
u8 read_addr; u8 read_addr;
u32 res = 0; u32 res = 0;
int i; int i;
...@@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size, ...@@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
u32 val) u32 val)
{ {
u8 buf[size]; u8 buf[DA9150_QIF_LONG_SIZE];
u8 write_addr; u8 write_addr;
int i; int i;
......
...@@ -28,14 +28,12 @@ ...@@ -28,14 +28,12 @@
#include <linux/power/gpio-charger.h> #include <linux/power/gpio-charger.h>
struct gpio_charger { struct gpio_charger {
const struct gpio_charger_platform_data *pdata;
unsigned int irq; unsigned int irq;
bool wakeup_enabled; bool wakeup_enabled;
struct power_supply *charger; struct power_supply *charger;
struct power_supply_desc charger_desc; struct power_supply_desc charger_desc;
struct gpio_desc *gpiod; struct gpio_desc *gpiod;
bool legacy_gpio_requested;
}; };
static irqreturn_t gpio_charger_irq(int irq, void *devid) static irqreturn_t gpio_charger_irq(int irq, void *devid)
...@@ -56,13 +54,10 @@ static int gpio_charger_get_property(struct power_supply *psy, ...@@ -56,13 +54,10 @@ static int gpio_charger_get_property(struct power_supply *psy,
enum power_supply_property psp, union power_supply_propval *val) enum power_supply_property psp, union power_supply_propval *val)
{ {
struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy); struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_ONLINE:
val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod); val->intval = gpiod_get_value_cansleep(gpio_charger->gpiod);
/* This xor is only ever used with legacy pdata GPIO */
val->intval ^= pdata->gpio_active_low;
break; break;
default: default:
return -EINVAL; return -EINVAL;
...@@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy, ...@@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy,
return 0; return 0;
} }
static enum power_supply_property gpio_charger_properties[] = { static enum power_supply_type gpio_charger_get_type(struct device *dev)
POWER_SUPPLY_PROP_ONLINE,
};
static
struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
{ {
struct device_node *np = dev->of_node;
struct gpio_charger_platform_data *pdata;
const char *chargetype; const char *chargetype;
int ret;
if (!device_property_read_string(dev, "charger-type", &chargetype)) {
if (!np) if (!strcmp("unknown", chargetype))
return ERR_PTR(-ENOENT); return POWER_SUPPLY_TYPE_UNKNOWN;
if (!strcmp("battery", chargetype))
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); return POWER_SUPPLY_TYPE_BATTERY;
if (!pdata) if (!strcmp("ups", chargetype))
return ERR_PTR(-ENOMEM); return POWER_SUPPLY_TYPE_UPS;
if (!strcmp("mains", chargetype))
pdata->name = np->name; return POWER_SUPPLY_TYPE_MAINS;
pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; if (!strcmp("usb-sdp", chargetype))
ret = of_property_read_string(np, "charger-type", &chargetype); return POWER_SUPPLY_TYPE_USB;
if (ret >= 0) { if (!strcmp("usb-dcp", chargetype))
if (!strncmp("unknown", chargetype, 7)) return POWER_SUPPLY_TYPE_USB_DCP;
pdata->type = POWER_SUPPLY_TYPE_UNKNOWN; if (!strcmp("usb-cdp", chargetype))
else if (!strncmp("battery", chargetype, 7)) return POWER_SUPPLY_TYPE_USB_CDP;
pdata->type = POWER_SUPPLY_TYPE_BATTERY; if (!strcmp("usb-aca", chargetype))
else if (!strncmp("ups", chargetype, 3)) return POWER_SUPPLY_TYPE_USB_ACA;
pdata->type = POWER_SUPPLY_TYPE_UPS;
else if (!strncmp("mains", chargetype, 5))
pdata->type = POWER_SUPPLY_TYPE_MAINS;
else if (!strncmp("usb-sdp", chargetype, 7))
pdata->type = POWER_SUPPLY_TYPE_USB;
else if (!strncmp("usb-dcp", chargetype, 7))
pdata->type = POWER_SUPPLY_TYPE_USB_DCP;
else if (!strncmp("usb-cdp", chargetype, 7))
pdata->type = POWER_SUPPLY_TYPE_USB_CDP;
else if (!strncmp("usb-aca", chargetype, 7))
pdata->type = POWER_SUPPLY_TYPE_USB_ACA;
else
dev_warn(dev, "unknown charger type %s\n", chargetype);
} }
dev_warn(dev, "unknown charger type %s\n", chargetype);
return pdata; return POWER_SUPPLY_TYPE_UNKNOWN;
} }
static enum power_supply_property gpio_charger_properties[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static int gpio_charger_probe(struct platform_device *pdev) static int gpio_charger_probe(struct platform_device *pdev)
{ {
const struct gpio_charger_platform_data *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev;
const struct gpio_charger_platform_data *pdata = dev->platform_data;
struct power_supply_config psy_cfg = {}; struct power_supply_config psy_cfg = {};
struct gpio_charger *gpio_charger; struct gpio_charger *gpio_charger;
struct power_supply_desc *charger_desc; struct power_supply_desc *charger_desc;
int ret; unsigned long flags;
int irq; int irq, ret;
if (!pdata) { if (!pdata && !dev->of_node) {
pdata = gpio_charger_parse_dt(&pdev->dev); dev_err(dev, "No platform data\n");
if (IS_ERR(pdata)) { return -ENOENT;
ret = PTR_ERR(pdata);
if (ret != -EPROBE_DEFER)
dev_err(&pdev->dev, "No platform data\n");
return ret;
}
} }
gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger), gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
GFP_KERNEL); if (!gpio_charger)
if (!gpio_charger) {
dev_err(&pdev->dev, "Failed to alloc driver structure\n");
return -ENOMEM; return -ENOMEM;
}
/* /*
* This will fetch a GPIO descriptor from device tree, ACPI or * This will fetch a GPIO descriptor from device tree, ACPI or
* boardfile descriptor tables. It's good to try this first. * boardfile descriptor tables. It's good to try this first.
*/ */
gpio_charger->gpiod = devm_gpiod_get(&pdev->dev, NULL, GPIOD_IN); gpio_charger->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
/* /*
* If this fails and we're not using device tree, try the * If this fails and we're not using device tree, try the
* legacy platform data method. * legacy platform data method.
*/ */
if (IS_ERR(gpio_charger->gpiod) && !pdev->dev.of_node) { if (IS_ERR(gpio_charger->gpiod) && !dev->of_node) {
/* Non-DT: use legacy GPIO numbers */ /* Non-DT: use legacy GPIO numbers */
if (!gpio_is_valid(pdata->gpio)) { if (!gpio_is_valid(pdata->gpio)) {
dev_err(&pdev->dev, "Invalid gpio pin in pdata\n"); dev_err(dev, "Invalid gpio pin in pdata\n");
return -EINVAL; return -EINVAL;
} }
ret = gpio_request(pdata->gpio, dev_name(&pdev->dev)); flags = GPIOF_IN;
if (pdata->gpio_active_low)
flags |= GPIOF_ACTIVE_LOW;
ret = devm_gpio_request_one(dev, pdata->gpio, flags,
dev_name(dev));
if (ret) { if (ret) {
dev_err(&pdev->dev, "Failed to request gpio pin: %d\n", dev_err(dev, "Failed to request gpio pin: %d\n", ret);
ret);
return ret; return ret;
} }
gpio_charger->legacy_gpio_requested = true;
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;
}
/* Then convert this to gpiod for now */ /* Then convert this to gpiod for now */
gpio_charger->gpiod = gpio_to_desc(pdata->gpio); gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
} else if (IS_ERR(gpio_charger->gpiod)) { } else if (IS_ERR(gpio_charger->gpiod)) {
/* Just try again if this happens */ /* Just try again if this happens */
if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER) if (PTR_ERR(gpio_charger->gpiod) == -EPROBE_DEFER)
return -EPROBE_DEFER; return -EPROBE_DEFER;
dev_err(&pdev->dev, "error getting GPIO descriptor\n"); dev_err(dev, "error getting GPIO descriptor\n");
return PTR_ERR(gpio_charger->gpiod); return PTR_ERR(gpio_charger->gpiod);
} }
charger_desc = &gpio_charger->charger_desc; charger_desc = &gpio_charger->charger_desc;
charger_desc->name = pdata->name ? pdata->name : "gpio-charger";
charger_desc->type = pdata->type;
charger_desc->properties = gpio_charger_properties; charger_desc->properties = gpio_charger_properties;
charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties); charger_desc->num_properties = ARRAY_SIZE(gpio_charger_properties);
charger_desc->get_property = gpio_charger_get_property; charger_desc->get_property = gpio_charger_get_property;
psy_cfg.supplied_to = pdata->supplied_to; psy_cfg.of_node = dev->of_node;
psy_cfg.num_supplicants = pdata->num_supplicants;
psy_cfg.of_node = pdev->dev.of_node;
psy_cfg.drv_data = gpio_charger; psy_cfg.drv_data = gpio_charger;
gpio_charger->pdata = pdata; if (pdata) {
charger_desc->name = pdata->name;
charger_desc->type = pdata->type;
psy_cfg.supplied_to = pdata->supplied_to;
psy_cfg.num_supplicants = pdata->num_supplicants;
} else {
charger_desc->name = dev->of_node->name;
charger_desc->type = gpio_charger_get_type(dev);
}
if (!charger_desc->name)
charger_desc->name = pdev->name;
gpio_charger->charger = power_supply_register(&pdev->dev, gpio_charger->charger = devm_power_supply_register(dev, charger_desc,
charger_desc, &psy_cfg); &psy_cfg);
if (IS_ERR(gpio_charger->charger)) { if (IS_ERR(gpio_charger->charger)) {
ret = PTR_ERR(gpio_charger->charger); ret = PTR_ERR(gpio_charger->charger);
dev_err(&pdev->dev, "Failed to register power supply: %d\n", dev_err(dev, "Failed to register power supply: %d\n", ret);
ret); return ret;
goto err_gpio_free;
} }
irq = gpiod_to_irq(gpio_charger->gpiod); irq = gpiod_to_irq(gpio_charger->gpiod);
if (irq > 0) { if (irq > 0) {
ret = request_any_context_irq(irq, gpio_charger_irq, ret = devm_request_any_context_irq(dev, irq, gpio_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), gpio_charger->charger); dev_name(dev), gpio_charger->charger);
if (ret < 0) if (ret < 0)
dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret); dev_warn(dev, "Failed to request irq: %d\n", ret);
else else
gpio_charger->irq = irq; gpio_charger->irq = irq;
} }
platform_set_drvdata(pdev, gpio_charger); platform_set_drvdata(pdev, gpio_charger);
device_init_wakeup(&pdev->dev, 1); device_init_wakeup(dev, 1);
return 0;
err_gpio_free:
if (gpio_charger->legacy_gpio_requested)
gpio_free(pdata->gpio);
return ret;
}
static int gpio_charger_remove(struct platform_device *pdev)
{
struct gpio_charger *gpio_charger = platform_get_drvdata(pdev);
if (gpio_charger->irq)
free_irq(gpio_charger->irq, gpio_charger->charger);
power_supply_unregister(gpio_charger->charger);
if (gpio_charger->legacy_gpio_requested)
gpio_free(gpio_charger->pdata->gpio);
return 0; return 0;
} }
...@@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match); ...@@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match);
static struct platform_driver gpio_charger_driver = { static struct platform_driver gpio_charger_driver = {
.probe = gpio_charger_probe, .probe = gpio_charger_probe,
.remove = gpio_charger_remove,
.driver = { .driver = {
.name = "gpio-charger", .name = "gpio-charger",
.pm = &gpio_charger_pm_ops, .pm = &gpio_charger_pm_ops,
......
...@@ -34,6 +34,10 @@ enum ltc294x_reg { ...@@ -34,6 +34,10 @@ enum ltc294x_reg {
LTC294X_REG_CONTROL = 0x01, LTC294X_REG_CONTROL = 0x01,
LTC294X_REG_ACC_CHARGE_MSB = 0x02, LTC294X_REG_ACC_CHARGE_MSB = 0x02,
LTC294X_REG_ACC_CHARGE_LSB = 0x03, LTC294X_REG_ACC_CHARGE_LSB = 0x03,
LTC294X_REG_CHARGE_THR_HIGH_MSB = 0x04,
LTC294X_REG_CHARGE_THR_HIGH_LSB = 0x05,
LTC294X_REG_CHARGE_THR_LOW_MSB = 0x06,
LTC294X_REG_CHARGE_THR_LOW_LSB = 0x07,
LTC294X_REG_VOLTAGE_MSB = 0x08, LTC294X_REG_VOLTAGE_MSB = 0x08,
LTC294X_REG_VOLTAGE_LSB = 0x09, LTC294X_REG_VOLTAGE_LSB = 0x09,
LTC2942_REG_TEMPERATURE_MSB = 0x0C, LTC2942_REG_TEMPERATURE_MSB = 0x0C,
...@@ -179,21 +183,22 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp) ...@@ -179,21 +183,22 @@ static int ltc294x_reset(const struct ltc294x_info *info, int prescaler_exp)
return ret; return ret;
} }
static int ltc294x_read_charge_register(const struct ltc294x_info *info) static int ltc294x_read_charge_register(const struct ltc294x_info *info,
{ enum ltc294x_reg reg)
{
int ret; int ret;
u8 datar[2]; u8 datar[2];
ret = ltc294x_read_regs(info->client, ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
return (datar[0] << 8) + datar[1]; return (datar[0] << 8) + datar[1];
} }
static int ltc294x_get_charge_now(const struct ltc294x_info *info, int *val) static int ltc294x_get_charge(const struct ltc294x_info *info,
enum ltc294x_reg reg, int *val)
{ {
int value = ltc294x_read_charge_register(info); int value = ltc294x_read_charge_register(info, reg);
if (value < 0) if (value < 0)
return value; return value;
...@@ -245,10 +250,29 @@ static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val) ...@@ -245,10 +250,29 @@ static int ltc294x_set_charge_now(const struct ltc294x_info *info, int val)
return ret < 0 ? ret : 0; return ret < 0 ? ret : 0;
} }
static int ltc294x_set_charge_thr(const struct ltc294x_info *info,
enum ltc294x_reg reg, int val)
{
u8 dataw[2];
s32 value;
value = convert_uAh_to_bin(info, val);
/* Direction depends on how sense+/- were connected */
if (info->Qlsb < 0)
value += 0xFFFF;
if ((value < 0) || (value > 0xFFFF)) /* input validation */
return -EINVAL;
/* Set new charge value */
dataw[0] = I16_MSB(value);
dataw[1] = I16_LSB(value);
return ltc294x_write_regs(info->client, reg, &dataw[0], 2);
}
static int ltc294x_get_charge_counter( static int ltc294x_get_charge_counter(
const struct ltc294x_info *info, int *val) const struct ltc294x_info *info, int *val)
{ {
int value = ltc294x_read_charge_register(info); int value = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);
if (value < 0) if (value < 0)
return value; return value;
...@@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val) ...@@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
if (info->id == LTC2942_ID) { if (info->id == LTC2942_ID) {
reg = LTC2942_REG_TEMPERATURE_MSB; reg = LTC2942_REG_TEMPERATURE_MSB;
value = 60000; /* Full-scale is 600 Kelvin */ value = 6000; /* Full-scale is 600 Kelvin */
} else { } else {
reg = LTC2943_REG_TEMPERATURE_MSB; reg = LTC2943_REG_TEMPERATURE_MSB;
value = 51000; /* Full-scale is 510 Kelvin */ value = 5100; /* Full-scale is 510 Kelvin */
} }
ret = ltc294x_read_regs(info->client, reg, &datar[0], 2); ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
value *= (datar[0] << 8) | datar[1]; value *= (datar[0] << 8) | datar[1];
/* Convert to centidegrees */ /* Convert to tenths of degree Celsius */
*val = value / 0xFFFF - 27215; *val = value / 0xFFFF - 2722;
return ret; return ret;
} }
...@@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy, ...@@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy,
struct ltc294x_info *info = power_supply_get_drvdata(psy); struct ltc294x_info *info = power_supply_get_drvdata(psy);
switch (prop) { switch (prop) {
case POWER_SUPPLY_PROP_CHARGE_FULL:
return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_HIGH_MSB,
&val->intval);
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
return ltc294x_get_charge(info, LTC294X_REG_CHARGE_THR_LOW_MSB,
&val->intval);
case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW:
return ltc294x_get_charge_now(info, &val->intval); return ltc294x_get_charge(info, LTC294X_REG_ACC_CHARGE_MSB,
&val->intval);
case POWER_SUPPLY_PROP_CHARGE_COUNTER: case POWER_SUPPLY_PROP_CHARGE_COUNTER:
return ltc294x_get_charge_counter(info, &val->intval); return ltc294x_get_charge_counter(info, &val->intval);
case POWER_SUPPLY_PROP_VOLTAGE_NOW: case POWER_SUPPLY_PROP_VOLTAGE_NOW:
...@@ -358,6 +389,12 @@ static int ltc294x_set_property(struct power_supply *psy, ...@@ -358,6 +389,12 @@ static int ltc294x_set_property(struct power_supply *psy,
struct ltc294x_info *info = power_supply_get_drvdata(psy); struct ltc294x_info *info = power_supply_get_drvdata(psy);
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_FULL:
return ltc294x_set_charge_thr(info,
LTC294X_REG_CHARGE_THR_HIGH_MSB, val->intval);
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
return ltc294x_set_charge_thr(info,
LTC294X_REG_CHARGE_THR_LOW_MSB, val->intval);
case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW:
return ltc294x_set_charge_now(info, val->intval); return ltc294x_set_charge_now(info, val->intval);
default: default:
...@@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable( ...@@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable(
struct power_supply *psy, enum power_supply_property psp) struct power_supply *psy, enum power_supply_property psp)
{ {
switch (psp) { switch (psp) {
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CHARGE_EMPTY:
case POWER_SUPPLY_PROP_CHARGE_NOW: case POWER_SUPPLY_PROP_CHARGE_NOW:
return 1; return 1;
default: default:
...@@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable( ...@@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable(
static void ltc294x_update(struct ltc294x_info *info) static void ltc294x_update(struct ltc294x_info *info)
{ {
int charge = ltc294x_read_charge_register(info); int charge = ltc294x_read_charge_register(info, LTC294X_REG_ACC_CHARGE_MSB);
if (charge != info->charge) { if (charge != info->charge) {
info->charge = charge; info->charge = charge;
...@@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work) ...@@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work)
static enum power_supply_property ltc294x_properties[] = { static enum power_supply_property ltc294x_properties[] = {
POWER_SUPPLY_PROP_CHARGE_COUNTER, POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_CHARGE_FULL,
POWER_SUPPLY_PROP_CHARGE_EMPTY,
POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_TEMP,
......
...@@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client, ...@@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client,
i2c_set_clientdata(client, chip); i2c_set_clientdata(client, chip);
psy_cfg.drv_data = chip; psy_cfg.drv_data = chip;
psy_cfg.of_node = dev->of_node;
/* When current is not measured, /* When current is not measured,
* CURRENT_NOW and CURRENT_AVG properties should be invisible. */ * CURRENT_NOW and CURRENT_AVG properties should be invisible. */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment