Merge tag 'for-v4.17' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 3 Apr 2018 19:10:01 +0000 (12:10 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 3 Apr 2018 19:10:01 +0000 (12:10 -0700)
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
  ...

20 files changed:
Documentation/devicetree/bindings/power/reset/gpio-poweroff.txt
Documentation/devicetree/bindings/power/reset/ocelot-reset.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power/supply/axp20x_battery.txt
arch/arm/boot/dts/omap3-n900.dts
drivers/power/reset/Kconfig
drivers/power/reset/Makefile
drivers/power/reset/at91-poweroff.c
drivers/power/reset/at91-reset.c
drivers/power/reset/gemini-poweroff.c
drivers/power/reset/gpio-poweroff.c
drivers/power/reset/ocelot-reset.c [new file with mode: 0644]
drivers/power/reset/sc27xx-poweroff.c [new file with mode: 0644]
drivers/power/supply/axp20x_battery.c
drivers/power/supply/axp288_fuel_gauge.c
drivers/power/supply/bq2415x_charger.c
drivers/power/supply/bq27xxx_battery.c
drivers/power/supply/da9150-fg.c
drivers/power/supply/gpio-charger.c
drivers/power/supply/ltc2941-battery-gauge.c
drivers/power/supply/max17042_battery.c

index e62d53d844ccc65e157236fa1ffda92140e9e5d9..6d8980c18c3485ea55ee44cfbe33babc3a7f95b5 100644 (file)
@@ -27,10 +27,13 @@ Optional properties:
   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
   inactive state.
+- timeout-ms: Time to wait before asserting a WARN_ON(1). If nothing is
+              specified, 3000 ms is used.
 
 Examples:
 
 gpio-poweroff {
        compatible = "gpio-poweroff";
        gpios = <&gpio 4 0>;
+       timeout-ms = <3000>;
 };
diff --git a/Documentation/devicetree/bindings/power/reset/ocelot-reset.txt b/Documentation/devicetree/bindings/power/reset/ocelot-reset.txt
new file mode 100644 (file)
index 0000000..1b4213e
--- /dev/null
@@ -0,0 +1,14 @@
+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>;
+       };
+
index c24886676a6058522a04a2f0c73f4c7f0e77c712..41916f69902c585bd422bf898f2e59f3d5599ce7 100644 (file)
@@ -4,12 +4,12 @@ Required Properties:
  - compatible, one of:
                        "x-powers,axp209-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
-currents of the battery by reading ADC channels from the AXP20X/AXP22X
-ADC.
+The supported devices can read the battery voltage, charge and discharge
+currents of the battery by reading ADC channels from the ADC.
 
 Example:
 
index ab930581fc7a593d268f6e5a57970a317c7e9138..182a53991c901a387317db9710bceb9d0d1d5bb6 100644 (file)
        bq27200: bq27200@55 {
                compatible = "ti,bq27200";
                reg = <0x55>;
+               power-supplies = <&bq24150a>;
        };
 
        /* Stereo headphone amplifier */
index a102e74ab24e5e1d19d1f60c9977e06875b0c2a9..df58fc878b3e5278394a9b9e2fdc135f3353817e 100644 (file)
@@ -104,6 +104,13 @@ config POWER_RESET_MSM
        help
          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
        tristate "Intel PIIX4 power-off driver"
        depends on PCI
@@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE
          register, then the bootloader can read it to take different
          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
 
index dcc92f5f7a37d9b3934b2c7baa3020d69d36d4cd..7778c7485cf19b44b1ccd84fa5e6b738d5866faf 100644 (file)
@@ -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_HISI) += hisi-reboot.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_LTC2952) += ltc2952-poweroff.o
 obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
@@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
 obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
 obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
 obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
+obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
index c30c40193aaa71042cd253fce75f7fad0436ff42..fb2fc8f741a1e098d8fd6d1e88fe7608473a0adf 100644 (file)
@@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base;
 static struct clk *sclk;
 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);
-       char *reason = "unknown";
 
        /* Simple power-on, just bail out */
        if (!reg)
@@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void)
                reason = "RTT";
        else if (reg & AT91_SHDW_RTCWK)
                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)
@@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(at91_shdwc_base)) {
-               dev_err(&pdev->dev, "Could not map reset controller address\n");
+       if (IS_ERR(at91_shdwc_base))
                return PTR_ERR(at91_shdwc_base);
-       }
 
        sclk = devm_clk_get(&pdev->dev, NULL);
        if (IS_ERR(sclk))
@@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
                return ret;
        }
 
-       at91_wakeup_status();
+       at91_wakeup_status(pdev);
 
        if (pdev->dev.of_node)
                at91_poweroff_dt_set_wakeup_mode(pdev);
index b99769f8ab15678256a81eecb5eaad6f17ad2083..f44a9ffcc2ab32bb82344a5252e4738b36721e25 100644 (file)
@@ -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)
 {
+       const char *reason;
        u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
-       char *reason;
 
        switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
        case RESET_TYPE_GENERAL:
@@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev)
                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[] = {
index ff75af5abbc536c837aad62348fff3cd79e70719..2ac291af1265b3feb6770af7d8d337d044df48af 100644 (file)
@@ -47,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
        val &= 0x70U;
        switch (val) {
        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;
        case GEMINI_STAT_RTC:
                dev_info(gpw->dev, "RTC poweroff\n");
@@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
                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 |= GEMINI_CTRL_IRQ_CLR;
        writel(val, gpw->base + GEMINI_PWC_CTRLREG);
@@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
        pm_power_off = gemini_poweroff;
        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");
 
        return 0;
index be3d81ff51cc3f510d85e4eed7a52960e51e7bc1..6273ad3b411d8e32d33907225fb25455c19c6587 100644 (file)
 #include <linux/of_platform.h>
 #include <linux/module.h>
 
+#define DEFAULT_TIMEOUT_MS 3000
 /*
  * Hold configuration here, cannot be more than one instance of the driver
  * since pm_power_off itself is global.
  */
 static struct gpio_desc *reset_gpio;
+static u32 timeout = DEFAULT_TIMEOUT_MS;
 
 static void gpio_poweroff_do_poweroff(void)
 {
@@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void)
        gpiod_set_value(reset_gpio, 1);
 
        /* give it some time */
-       mdelay(3000);
+       mdelay(timeout);
 
        WARN_ON(1);
 }
@@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
                return -EBUSY;
        }
 
-       input = of_property_read_bool(pdev->dev.of_node, "input");
+       input = device_property_read_bool(&pdev->dev, "input");
        if (input)
                flags = GPIOD_IN;
        else
                flags = GPIOD_OUT_LOW;
 
+       device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
+
        reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
        if (IS_ERR(reset_gpio))
                return PTR_ERR(reset_gpio);
diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c
new file mode 100644 (file)
index 0000000..5a13a5c
--- /dev/null
@@ -0,0 +1,88 @@
+// 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);
diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c
new file mode 100644 (file)
index 0000000..29fb08b
--- /dev/null
@@ -0,0 +1,66 @@
+// 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);
index 7494f0f0eadb2703a91b2005e266af557f3a9bed..e84b6e4da14a8a9e522c18dffad341a403301c88 100644 (file)
 #define AXP22X_CHRG_CTRL1_TGT_4_22V    (1 << 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_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 regmap *regmap;
        struct power_supply *batt;
@@ -62,7 +74,7 @@ struct axp20x_batt_ps {
        struct iio_channel *batt_v;
        /* Maximum constant charge current */
        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,
@@ -123,20 +135,33 @@ static int axp22x_battery_get_max_voltage(struct axp20x_batt_ps *axp20x_batt,
        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)
-               *val = *val * 100000 + 300000;
-       else
-               *val = *val * 150000 + 300000;
-}
+       int ret, reg;
 
-static void constant_charge_current_to_raw(struct axp20x_batt_ps *axp, int *val)
-{
-       if (axp->axp_id == AXP209_ID)
-               *val = (*val - 300000) / 100000;
-       else
-               *val = (*val - 300000) / 150000;
+       ret = regmap_read(axp20x_batt->regmap, AXP20X_CHRG_CTRL1, &reg);
+       if (ret)
+               return ret;
+
+       switch (reg & AXP20X_CHRG_CTRL1_TGT_VOLT) {
+       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,
@@ -150,7 +175,7 @@ static int axp20x_get_constant_charge_current(struct axp20x_batt_ps *axp,
 
        *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;
 }
@@ -269,8 +294,7 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
                if (ret)
                        return ret;
 
-               if (axp20x_batt->axp_id == AXP221_ID &&
-                   !(reg & AXP22X_FG_VALID))
+               if (axp20x_batt->data->has_fg_valid && !(reg & AXP22X_FG_VALID))
                        return -EINVAL;
 
                /*
@@ -281,11 +305,8 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
                break;
 
        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               if (axp20x_batt->axp_id == AXP209_ID)
-                       return axp20x_battery_get_max_voltage(axp20x_batt,
-                                                             &val->intval);
-               return axp22x_battery_get_max_voltage(axp20x_batt,
-                                                     &val->intval);
+               return axp20x_batt->data->get_max_voltage(axp20x_batt,
+                                                         &val->intval);
 
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
                ret = regmap_read(axp20x_batt->regmap, AXP20X_V_OFF, &reg);
@@ -312,6 +333,32 @@ static int axp20x_battery_get_prop(struct power_supply *psy,
        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,
                                          int val)
 {
@@ -321,9 +368,6 @@ static int axp20x_battery_set_max_voltage(struct axp20x_batt_ps *axp20x_batt,
                break;
 
        case 4150000:
-               if (axp20x_batt->axp_id == AXP221_ID)
-                       return -EINVAL;
-
                val = AXP20X_CHRG_CTRL1_TGT_4_15V;
                break;
 
@@ -351,7 +395,8 @@ static int axp20x_set_constant_charge_current(struct axp20x_batt_ps *axp_batt,
        if (charge_current > axp_batt->max_ccc)
                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)
                return -EINVAL;
@@ -365,12 +410,14 @@ static int axp20x_set_max_constant_charge_current(struct axp20x_batt_ps *axp,
 {
        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)
                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)
                dev_warn(axp->dev,
@@ -413,7 +460,7 @@ static int axp20x_battery_set_prop(struct power_supply *psy,
                return axp20x_set_voltage_min_design(axp20x_batt, val->intval);
 
        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:
                return axp20x_set_constant_charge_current(axp20x_batt,
@@ -460,13 +507,39 @@ static const struct power_supply_desc axp20x_batt_ps_desc = {
        .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[] = {
        {
                .compatible = "x-powers,axp209-battery-power-supply",
-               .data = (void *)AXP209_ID,
+               .data = (void *)&axp209_data,
        }, {
                .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 */ },
 };
 MODULE_DEVICE_TABLE(of, axp20x_battery_ps_id);
@@ -476,6 +549,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
        struct axp20x_batt_ps *axp20x_batt;
        struct power_supply_config psy_cfg = {};
        struct power_supply_battery_info info;
+       struct device *dev = &pdev->dev;
 
        if (!of_device_is_available(pdev->dev.of_node))
                return -ENODEV;
@@ -516,7 +590,7 @@ static int axp20x_power_probe(struct platform_device *pdev)
        psy_cfg.drv_data = axp20x_batt;
        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_ps_desc,
index 4cc6e038dfdd50b46c3cf795027f9ba29ea8c00a..fd8f0b2210bc9c3e871aeab6b3daa93169c00eeb 100644 (file)
@@ -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)
 {
-       int pwr_stat, fg_res;
+       int pwr_stat, fg_res, curr, ret;
 
        pwr_stat = fuel_gauge_reg_readb(info, AXP20X_PWR_INPUT_STATUS);
        if (pwr_stat < 0) {
@@ -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% */
-       if (pwr_stat & PS_STAT_VBUS_VALID) {
-               fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
-               if (fg_res < 0) {
-                       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 (!(pwr_stat & PS_STAT_VBUS_VALID))
+               goto not_full;
+
+       fg_res = fuel_gauge_reg_readb(info, AXP20X_FG_RES);
+       if (fg_res < 0) {
+               dev_err(&info->pdev->dev, "FG RES read failed: %d\n", fg_res);
+               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)
                info->status = POWER_SUPPLY_STATUS_CHARGING;
        else
@@ -708,6 +731,12 @@ static const struct dmi_system_id axp288_fuel_gauge_blacklist[] = {
                        DMI_MATCH(DMI_BOARD_VERSION, "V1.1"),
                },
        },
+       {
+               /* ECS EF20EA */
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "EF20EA"),
+               },
+       },
        {}
 };
 
index c4770a94cc8e66a744e5ea4639873e2249e32d5e..cbec70f3e73efe48e397abf58b0556876c25b098 100644 (file)
@@ -1037,7 +1037,10 @@ static int bq2415x_power_supply_init(struct bq2415x_device *bq)
        int ret;
        int chip;
        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.type = POWER_SUPPLY_TYPE_USB;
index d99981542a46291891104741e2b88fdf54f764c7..7ce60519b1bc513d897cc58af2aea9461a84196d 100644 (file)
@@ -1670,7 +1670,7 @@ static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
                        status = POWER_SUPPLY_STATUS_FULL;
                else if (di->cache.flags & BQ27000_FLAG_CHGS)
                        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;
                else
                        status = POWER_SUPPLY_STATUS_DISCHARGING;
index 8b8ce978656a53fbde3800ab10b59cf3be3a4962..1e2e5b0520c913867bd852a8a57ae51108bc4870 100644 (file)
@@ -92,7 +92,7 @@ struct da9150_fg {
 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;
        u32 res = 0;
        int i;
@@ -111,7 +111,7 @@ static void da9150_fg_write_attr(struct da9150_fg *fg, u8 code, u8 size,
                                 u32 val)
 
 {
-       u8 buf[size];
+       u8 buf[DA9150_QIF_LONG_SIZE];
        u8 write_addr;
        int i;
 
index 001731e88718e38cd31a0314a6e3409dad722684..bd2468ca6b6333b099baa5b117d98bd50e670c35 100644 (file)
 #include <linux/power/gpio-charger.h>
 
 struct gpio_charger {
-       const struct gpio_charger_platform_data *pdata;
        unsigned int irq;
        bool wakeup_enabled;
 
        struct power_supply *charger;
        struct power_supply_desc charger_desc;
        struct gpio_desc *gpiod;
-       bool legacy_gpio_requested;
 };
 
 static irqreturn_t gpio_charger_irq(int irq, void *devid)
@@ -56,13 +54,10 @@ static int gpio_charger_get_property(struct power_supply *psy,
                enum power_supply_property psp, union power_supply_propval *val)
 {
        struct gpio_charger *gpio_charger = psy_to_gpio_charger(psy);
-       const struct gpio_charger_platform_data *pdata = gpio_charger->pdata;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_ONLINE:
                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;
        default:
                return -EINVAL;
@@ -71,175 +66,134 @@ static int gpio_charger_get_property(struct power_supply *psy,
        return 0;
 }
 
-static enum power_supply_property gpio_charger_properties[] = {
-       POWER_SUPPLY_PROP_ONLINE,
-};
-
-static
-struct gpio_charger_platform_data *gpio_charger_parse_dt(struct device *dev)
+static enum power_supply_type gpio_charger_get_type(struct device *dev)
 {
-       struct device_node *np = dev->of_node;
-       struct gpio_charger_platform_data *pdata;
        const char *chargetype;
-       int ret;
-
-       if (!np)
-               return ERR_PTR(-ENOENT);
-
-       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
-       if (!pdata)
-               return ERR_PTR(-ENOMEM);
-
-       pdata->name = np->name;
-       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
-       ret = of_property_read_string(np, "charger-type", &chargetype);
-       if (ret >= 0) {
-               if (!strncmp("unknown", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_UNKNOWN;
-               else if (!strncmp("battery", chargetype, 7))
-                       pdata->type = POWER_SUPPLY_TYPE_BATTERY;
-               else if (!strncmp("ups", chargetype, 3))
-                       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);
+
+       if (!device_property_read_string(dev, "charger-type", &chargetype)) {
+               if (!strcmp("unknown", chargetype))
+                       return POWER_SUPPLY_TYPE_UNKNOWN;
+               if (!strcmp("battery", chargetype))
+                       return POWER_SUPPLY_TYPE_BATTERY;
+               if (!strcmp("ups", chargetype))
+                       return POWER_SUPPLY_TYPE_UPS;
+               if (!strcmp("mains", chargetype))
+                       return POWER_SUPPLY_TYPE_MAINS;
+               if (!strcmp("usb-sdp", chargetype))
+                       return POWER_SUPPLY_TYPE_USB;
+               if (!strcmp("usb-dcp", chargetype))
+                       return POWER_SUPPLY_TYPE_USB_DCP;
+               if (!strcmp("usb-cdp", chargetype))
+                       return POWER_SUPPLY_TYPE_USB_CDP;
+               if (!strcmp("usb-aca", chargetype))
+                       return POWER_SUPPLY_TYPE_USB_ACA;
        }
+       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)
 {
-       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 gpio_charger *gpio_charger;
        struct power_supply_desc *charger_desc;
-       int ret;
-       int irq;
-
-       if (!pdata) {
-               pdata = gpio_charger_parse_dt(&pdev->dev);
-               if (IS_ERR(pdata)) {
-                       ret = PTR_ERR(pdata);
-                       if (ret != -EPROBE_DEFER)
-                               dev_err(&pdev->dev, "No platform data\n");
-                       return ret;
-               }
+       unsigned long flags;
+       int irq, ret;
+
+       if (!pdata && !dev->of_node) {
+               dev_err(dev, "No platform data\n");
+               return -ENOENT;
        }
 
-       gpio_charger = devm_kzalloc(&pdev->dev, sizeof(*gpio_charger),
-                                       GFP_KERNEL);
-       if (!gpio_charger) {
-               dev_err(&pdev->dev, "Failed to alloc driver structure\n");
+       gpio_charger = devm_kzalloc(dev, sizeof(*gpio_charger), GFP_KERNEL);
+       if (!gpio_charger)
                return -ENOMEM;
-       }
 
        /*
         * This will fetch a GPIO descriptor from device tree, ACPI or
         * 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
         * 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 */
                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;
                }
-               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) {
-                       dev_err(&pdev->dev, "Failed to request gpio pin: %d\n",
-                               ret);
+                       dev_err(dev, "Failed to request gpio pin: %d\n", 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 */
                gpio_charger->gpiod = gpio_to_desc(pdata->gpio);
        } else if (IS_ERR(gpio_charger->gpiod)) {
                /* Just try again if this happens */
                if (PTR_ERR(gpio_charger->gpiod) == -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);
        }
 
        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->num_properties = ARRAY_SIZE(gpio_charger_properties);
        charger_desc->get_property = gpio_charger_get_property;
 
-       psy_cfg.supplied_to = pdata->supplied_to;
-       psy_cfg.num_supplicants = pdata->num_supplicants;
-       psy_cfg.of_node = pdev->dev.of_node;
+       psy_cfg.of_node = dev->of_node;
        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,
-                                                     charger_desc, &psy_cfg);
+       gpio_charger->charger = devm_power_supply_register(dev, charger_desc,
+                                                          &psy_cfg);
        if (IS_ERR(gpio_charger->charger)) {
                ret = PTR_ERR(gpio_charger->charger);
-               dev_err(&pdev->dev, "Failed to register power supply: %d\n",
-                       ret);
-               goto err_gpio_free;
+               dev_err(dev, "Failed to register power supply: %d\n", ret);
+               return ret;
        }
 
        irq = gpiod_to_irq(gpio_charger->gpiod);
        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,
-                               dev_name(&pdev->dev), gpio_charger->charger);
+                               dev_name(dev), gpio_charger->charger);
                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
                        gpio_charger->irq = irq;
        }
 
        platform_set_drvdata(pdev, gpio_charger);
 
-       device_init_wakeup(&pdev->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);
+       device_init_wakeup(dev, 1);
 
        return 0;
 }
@@ -280,7 +234,6 @@ MODULE_DEVICE_TABLE(of, gpio_charger_match);
 
 static struct platform_driver gpio_charger_driver = {
        .probe = gpio_charger_probe,
-       .remove = gpio_charger_remove,
        .driver = {
                .name = "gpio-charger",
                .pm = &gpio_charger_pm_ops,
index 4cfa3f0cd689ef8fade9c1596303963e0785b6fa..4f129bb4c9729e9ab085cf465f636ab6395daf1f 100644 (file)
@@ -34,6 +34,10 @@ enum ltc294x_reg {
        LTC294X_REG_CONTROL             = 0x01,
        LTC294X_REG_ACC_CHARGE_MSB      = 0x02,
        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_LSB         = 0x09,
        LTC2942_REG_TEMPERATURE_MSB     = 0x0C,
@@ -179,21 +183,22 @@ error_exit:
        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;
        u8 datar[2];
 
-       ret = ltc294x_read_regs(info->client,
-               LTC294X_REG_ACC_CHARGE_MSB, &datar[0], 2);
+       ret = ltc294x_read_regs(info->client, reg, &datar[0], 2);
        if (ret < 0)
                return ret;
        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)
                return value;
@@ -245,10 +250,29 @@ error_exit:
        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(
        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)
                return value;
@@ -317,15 +341,15 @@ static int ltc294x_get_temperature(const struct ltc294x_info *info, int *val)
 
        if (info->id == LTC2942_ID) {
                reg = LTC2942_REG_TEMPERATURE_MSB;
-               value = 60000;  /* Full-scale is 600 Kelvin */
+               value = 6000  /* Full-scale is 600 Kelvin */
        } else {
                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);
        value *= (datar[0] << 8) | datar[1];
-       /* Convert to centidegrees  */
-       *val = value / 0xFFFF - 27215;
+       /* Convert to tenths of degree Celsius */
+       *val = value / 0xFFFF - 2722;
        return ret;
 }
 
@@ -336,8 +360,15 @@ static int ltc294x_get_property(struct power_supply *psy,
        struct ltc294x_info *info = power_supply_get_drvdata(psy);
 
        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:
-               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:
                return ltc294x_get_charge_counter(info, &val->intval);
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
@@ -358,6 +389,12 @@ static int ltc294x_set_property(struct power_supply *psy,
        struct ltc294x_info *info = power_supply_get_drvdata(psy);
 
        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:
                return ltc294x_set_charge_now(info, val->intval);
        default:
@@ -369,6 +406,8 @@ static int ltc294x_property_is_writeable(
        struct power_supply *psy, enum power_supply_property psp)
 {
        switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+       case POWER_SUPPLY_PROP_CHARGE_EMPTY:
        case POWER_SUPPLY_PROP_CHARGE_NOW:
                return 1;
        default:
@@ -378,7 +417,7 @@ static int ltc294x_property_is_writeable(
 
 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) {
                info->charge = charge;
@@ -397,6 +436,8 @@ static void ltc294x_work(struct work_struct *work)
 
 static enum power_supply_property ltc294x_properties[] = {
        POWER_SUPPLY_PROP_CHARGE_COUNTER,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_EMPTY,
        POWER_SUPPLY_PROP_CHARGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_TEMP,
index 35dde81b1c9b35a8959c50d63e69596491a094c2..1a568df383db8f1b2dec7525d96a5428683d4ec8 100644 (file)
@@ -1053,6 +1053,7 @@ static int max17042_probe(struct i2c_client *client,
 
        i2c_set_clientdata(client, chip);
        psy_cfg.drv_data = chip;
+       psy_cfg.of_node = dev->of_node;
 
        /* When current is not measured,
         * CURRENT_NOW and CURRENT_AVG properties should be invisible. */