]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
next-20150925/battery
authorStephen Rothwell <sfr@canb.auug.org.au>
Thu, 5 Nov 2015 01:46:42 +0000 (12:46 +1100)
committerStephen Rothwell <sfr@canb.auug.org.au>
Thu, 5 Nov 2015 01:46:42 +0000 (12:46 +1100)
32 files changed:
Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/tps65217_charger.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/configs/omap2plus_defconfig
arch/unicore32/Kconfig
drivers/power/88pm860x_battery.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/axp20x_usb_power.c [new file with mode: 0644]
drivers/power/bq2415x_charger.c
drivers/power/bq24190_charger.c
drivers/power/bq27x00_battery.c [deleted file]
drivers/power/bq27xxx_battery.c [new file with mode: 0644]
drivers/power/lp8727_charger.c
drivers/power/max17042_battery.c
drivers/power/max8903_charger.c
drivers/power/max8998_charger.c
drivers/power/pm2301_charger.c
drivers/power/qcom_smbb.c [new file with mode: 0644]
drivers/power/reset/Kconfig
drivers/power/reset/at91-poweroff.c
drivers/power/reset/at91-reset.c
drivers/power/rt9455_charger.c
drivers/power/smb347-charger.c
drivers/power/tps65090-charger.c
drivers/power/tps65217_charger.c [new file with mode: 0644]
drivers/power/wm831x_power.c
drivers/w1/slaves/w1_bq27000.c
include/linux/mfd/axp20x.h
include/linux/power/bq27x00_battery.h [deleted file]
include/linux/power/bq27xxx_battery.h [new file with mode: 0644]
include/linux/power/charger-manager.h

diff --git a/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt b/Documentation/devicetree/bindings/power_supply/axp20x_usb_power.txt
new file mode 100644 (file)
index 0000000..862f4a4
--- /dev/null
@@ -0,0 +1,34 @@
+AXP20x USB power supply
+
+Required Properties:
+-compatible: "x-powers,axp202-usb-power-supply"
+
+This node is a subnode of the axp20x PMIC.
+
+Example:
+
+axp209: pmic@34 {
+       compatible = "x-powers,axp209";
+       reg = <0x34>;
+       interrupt-parent = <&nmi_intc>;
+       interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
+       interrupt-controller;
+       #interrupt-cells = <1>;
+
+       regulators {
+               x-powers,dcdc-freq = <1500>;
+
+               vdd_cpu: dcdc2 {
+                       regulator-always-on;
+                       regulator-min-microvolt = <1000000>;
+                       regulator-max-microvolt = <1450000>;
+                       regulator-name = "vdd-cpu";
+               };
+
+               ...
+       };
+
+       usb-power-supply: usb-power-supply {
+               compatible = "x-powers,axp202-usb-power-supply";
+       };
+};
diff --git a/Documentation/devicetree/bindings/power_supply/tps65217_charger.txt b/Documentation/devicetree/bindings/power_supply/tps65217_charger.txt
new file mode 100644 (file)
index 0000000..98d131a
--- /dev/null
@@ -0,0 +1,12 @@
+TPS65217 Charger
+
+Required Properties:
+-compatible: "ti,tps65217-charger"
+
+This node is a subnode of the tps65217 PMIC.
+
+Example:
+
+       tps65217-charger {
+               compatible = "ti,tps65090-charger";
+       };
index 5d9e7fd176c5e0e9c4367b0dbcb41a92a2b5be33..9b6497c298bb896d60e0ba08bf8a82a186678974 100644 (file)
@@ -7485,10 +7485,10 @@ NOKIA N900 POWER SUPPLY DRIVERS
 M:     Pali Rohár <pali.rohar@gmail.com>
 S:     Maintained
 F:     include/linux/power/bq2415x_charger.h
-F:     include/linux/power/bq27x00_battery.h
+F:     include/linux/power/bq27xxx_battery.h
 F:     include/linux/power/isp1704_charger.h
 F:     drivers/power/bq2415x_charger.c
-F:     drivers/power/bq27x00_battery.c
+F:     drivers/power/bq27xxx_battery.c
 F:     drivers/power/isp1704_charger.c
 F:     drivers/power/rx51_battery.c
 
index 3f15a5cae16778137de0279a637306661d35099f..c5e1943e5427db037b98ce779566aeab1b1c9310 100644 (file)
@@ -246,7 +246,7 @@ CONFIG_GPIO_TWL4030=y
 CONFIG_GPIO_PALMAS=y
 CONFIG_W1=m
 CONFIG_HDQ_MASTER_OMAP=m
-CONFIG_BATTERY_BQ27x00=m
+CONFIG_BATTERY_BQ27XXX=m
 CONFIG_CHARGER_ISP1704=m
 CONFIG_CHARGER_TWL4030=m
 CONFIG_CHARGER_BQ2415X=m
index 928237a7b9cada873486bcb0e9d0ff909e19d153..c9faddc61100f1bc44dd68f1b42e6b596ecc8bc4 100644 (file)
@@ -222,7 +222,7 @@ config I2C_BATTERY_BQ27200
        tristate "I2C Battery BQ27200 Support"
        select I2C_PUV3
        select POWER_SUPPLY
-       select BATTERY_BQ27x00
+       select BATTERY_BQ27XXX
 
 config I2C_EEPROM_AT24
        tristate "I2C EEPROMs AT24 support"
index d49579b227ec13deb372226d45fee71eeee4ce18..63c57dc82ac1d5845c4f256376cac5fdee26ef04 100644 (file)
@@ -954,46 +954,32 @@ static int pm860x_battery_probe(struct platform_device *pdev)
        else
                info->resistor = 300;   /* set default internal resistor */
 
-       info->battery = power_supply_register(&pdev->dev, &pm860x_battery_desc,
-                                             NULL);
+       info->battery = devm_power_supply_register(&pdev->dev,
+                                                  &pm860x_battery_desc,
+                                                  NULL);
        if (IS_ERR(info->battery))
                return PTR_ERR(info->battery);
        info->battery->dev.parent = &pdev->dev;
 
-       ret = request_threaded_irq(info->irq_cc, NULL,
-                               pm860x_coulomb_handler, IRQF_ONESHOT,
-                               "coulomb", info);
+       ret = devm_request_threaded_irq(chip->dev, info->irq_cc, NULL,
+                                       pm860x_coulomb_handler, IRQF_ONESHOT,
+                                       "coulomb", info);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
                        info->irq_cc, ret);
-               goto out_reg;
+               return ret;
        }
 
-       ret = request_threaded_irq(info->irq_batt, NULL, pm860x_batt_handler,
-                               IRQF_ONESHOT, "battery", info);
+       ret = devm_request_threaded_irq(chip->dev, info->irq_batt, NULL,
+                                       pm860x_batt_handler,
+                                       IRQF_ONESHOT, "battery", info);
        if (ret < 0) {
                dev_err(chip->dev, "Failed to request IRQ: #%d: %d\n",
                        info->irq_batt, ret);
-               goto out_coulomb;
+               return ret;
        }
 
 
-       return 0;
-
-out_coulomb:
-       free_irq(info->irq_cc, info);
-out_reg:
-       power_supply_unregister(info->battery);
-       return ret;
-}
-
-static int pm860x_battery_remove(struct platform_device *pdev)
-{
-       struct pm860x_battery_info *info = platform_get_drvdata(pdev);
-
-       free_irq(info->irq_batt, info);
-       free_irq(info->irq_cc, info);
-       power_supply_unregister(info->battery);
        return 0;
 }
 
@@ -1028,7 +1014,6 @@ static struct platform_driver pm860x_battery_driver = {
                   .pm = &pm860x_battery_pm_ops,
        },
        .probe = pm860x_battery_probe,
-       .remove = pm860x_battery_remove,
 };
 module_platform_driver(pm860x_battery_driver);
 
index 8d53f58f20ddf1cc49882228c2a60d0cd6ab42ea..6de6ec26ad36e7174d6b3661311897817cf627a9 100644 (file)
@@ -157,26 +157,26 @@ config BATTERY_SBS
          Say Y to include support for SBS battery driver for SBS-compliant
          gas gauges.
 
-config BATTERY_BQ27x00
-       tristate "BQ27x00 battery driver"
+config BATTERY_BQ27XXX
+       tristate "BQ27xxx battery driver"
        depends on I2C || I2C=n
        help
-         Say Y here to enable support for batteries with BQ27x00 (I2C/HDQ) chips.
+         Say Y here to enable support for batteries with BQ27xxx (I2C/HDQ) chips.
 
-config BATTERY_BQ27X00_I2C
-       bool "BQ27200/BQ27500 support"
-       depends on BATTERY_BQ27x00
+config BATTERY_BQ27XXX_I2C
+       bool "BQ27xxx I2C support"
+       depends on BATTERY_BQ27XXX
        depends on I2C
        default y
        help
-         Say Y here to enable support for batteries with BQ27x00 (I2C) chips.
+         Say Y here to enable support for batteries with BQ27xxx (I2C) chips.
 
-config BATTERY_BQ27X00_PLATFORM
-       bool "BQ27000 support"
-       depends on BATTERY_BQ27x00
+config BATTERY_BQ27XXX_PLATFORM
+       bool "BQ27xxx HDQ support"
+       depends on BATTERY_BQ27XXX
        default y
        help
-         Say Y here to enable support for batteries with BQ27000 (HDQ) chips.
+         Say Y here to enable support for batteries with BQ27xxx (HDQ) chips.
 
 config BATTERY_DA9030
        tristate "DA9030 battery driver"
@@ -389,6 +389,18 @@ config CHARGER_MAX8998
          Say Y to enable support for the battery charger control sysfs and
          platform data of MAX8998/LP3974 PMICs.
 
+config CHARGER_QCOM_SMBB
+       tristate "Qualcomm Switch-Mode Battery Charger and Boost"
+       depends on MFD_SPMI_PMIC || COMPILE_TEST
+       depends on OF
+       help
+         Say Y to include support for the Switch-Mode Battery Charger and
+         Boost (SMBB) hardware found in Qualcomm PM8941 PMICs.  The charger
+         is an integrated, single-cell lithium-ion battery charger.  DT
+         configuration is required for loading, see the devicetree
+         documentation for more detail.  The base name for this driver is
+         'pm8941_charger'.
+
 config CHARGER_BQ2415X
        tristate "TI BQ2415x battery charger driver"
        depends on I2C
@@ -444,6 +456,13 @@ config CHARGER_TPS65090
         Say Y here to enable support for battery charging with TPS65090
         PMIC chips.
 
+config CHARGER_TPS65217
+       tristate "TPS65217 battery charger driver"
+       depends on MFD_TPS65217
+       help
+        Say Y here to enable support for battery charging with TPS65217
+        PMIC chips.
+
 config BATTERY_GAUGE_LTC2941
        tristate "LTC2941/LTC2943 Battery Gauge Driver"
        depends on I2C
@@ -482,6 +501,13 @@ config CHARGER_RT9455
        help
          Say Y to enable support for Richtek RT9455 battery charger.
 
+config AXP20X_POWER
+       tristate "AXP20x power supply driver"
+       depends on MFD_AXP20X
+       help
+         This driver provides support for the power supply features of
+         AXP20x PMIC.
+
 source "drivers/power/reset/Kconfig"
 
 endif # POWER_SUPPLY
index cbdf89d595a301133bc17bf46e32aed25e4f410b..b656638f8b3984ed41be4cb7bd26a57fb05d028c 100644 (file)
@@ -9,6 +9,7 @@ obj-$(CONFIG_GENERIC_ADC_BATTERY)       += generic-adc-battery.o
 
 obj-$(CONFIG_PDA_POWER)                += pda_power.o
 obj-$(CONFIG_APM_POWER)                += apm_power.o
+obj-$(CONFIG_AXP20X_POWER)     += axp20x_usb_power.o
 obj-$(CONFIG_MAX8925_POWER)    += max8925_power.o
 obj-$(CONFIG_WM831X_BACKUP)    += wm831x_backup.o
 obj-$(CONFIG_WM831X_POWER)     += wm831x_power.o
@@ -29,7 +30,7 @@ obj-$(CONFIG_BATTERY_COLLIE)  += collie_battery.o
 obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
 obj-$(CONFIG_BATTERY_SBS)      += sbs-battery.o
-obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
+obj-$(CONFIG_BATTERY_BQ27XXX)  += bq27xxx_battery.o
 obj-$(CONFIG_BATTERY_DA9030)   += da9030_battery.o
 obj-$(CONFIG_BATTERY_DA9052)   += da9052-battery.o
 obj-$(CONFIG_CHARGER_DA9150)   += da9150-charger.o
@@ -58,6 +59,7 @@ obj-$(CONFIG_CHARGER_MAX14577)        += max14577_charger.o
 obj-$(CONFIG_CHARGER_MAX77693) += max77693_charger.o
 obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
+obj-$(CONFIG_CHARGER_QCOM_SMBB)        += qcom_smbb.o
 obj-$(CONFIG_CHARGER_BQ2415X)  += bq2415x_charger.o
 obj-$(CONFIG_CHARGER_BQ24190)  += bq24190_charger.o
 obj-$(CONFIG_CHARGER_BQ24257)  += bq24257_charger.o
@@ -66,6 +68,7 @@ obj-$(CONFIG_CHARGER_BQ25890) += bq25890_charger.o
 obj-$(CONFIG_POWER_AVS)                += avs/
 obj-$(CONFIG_CHARGER_SMB347)   += smb347-charger.o
 obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
+obj-$(CONFIG_CHARGER_TPS65217) += tps65217_charger.o
 obj-$(CONFIG_POWER_RESET)      += reset/
 obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
 obj-$(CONFIG_AXP288_CHARGER)   += axp288_charger.o
diff --git a/drivers/power/axp20x_usb_power.c b/drivers/power/axp20x_usb_power.c
new file mode 100644 (file)
index 0000000..421a90b
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * AXP20x PMIC USB power supply status driver
+ *
+ * Copyright (C) 2015 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (C) 2014 Bruno Prémont <bonbons@linux-vserver.org>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under  the terms of the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define DRVNAME "axp20x-usb-power-supply"
+
+#define AXP20X_PWR_STATUS_VBUS_PRESENT BIT(5)
+#define AXP20X_PWR_STATUS_VBUS_USED    BIT(4)
+
+#define AXP20X_USB_STATUS_VBUS_VALID   BIT(2)
+
+#define AXP20X_VBUS_VHOLD_uV(b)                (4000000 + (((b) >> 3) & 7) * 100000)
+#define AXP20X_VBUS_CLIMIT_MASK                3
+#define AXP20X_VBUC_CLIMIT_900mA       0
+#define AXP20X_VBUC_CLIMIT_500mA       1
+#define AXP20X_VBUC_CLIMIT_100mA       2
+#define AXP20X_VBUC_CLIMIT_NONE                3
+
+#define AXP20X_ADC_EN1_VBUS_CURR       BIT(2)
+#define AXP20X_ADC_EN1_VBUS_VOLT       BIT(3)
+
+#define AXP20X_VBUS_MON_VBUS_VALID     BIT(3)
+
+struct axp20x_usb_power {
+       struct regmap *regmap;
+       struct power_supply *supply;
+};
+
+static irqreturn_t axp20x_usb_power_irq(int irq, void *devid)
+{
+       struct axp20x_usb_power *power = devid;
+
+       power_supply_changed(power->supply);
+
+       return IRQ_HANDLED;
+}
+
+static int axp20x_usb_power_get_property(struct power_supply *psy,
+       enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct axp20x_usb_power *power = power_supply_get_drvdata(psy);
+       unsigned int input, v;
+       int ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
+               if (ret)
+                       return ret;
+
+               val->intval = AXP20X_VBUS_VHOLD_uV(v);
+               return 0;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = axp20x_read_variable_width(power->regmap,
+                                                AXP20X_VBUS_V_ADC_H, 12);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 1700; /* 1 step = 1.7 mV */
+               return 0;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               ret = regmap_read(power->regmap, AXP20X_VBUS_IPSOUT_MGMT, &v);
+               if (ret)
+                       return ret;
+
+               switch (v & AXP20X_VBUS_CLIMIT_MASK) {
+               case AXP20X_VBUC_CLIMIT_100mA:
+                       val->intval = 100000;
+                       break;
+               case AXP20X_VBUC_CLIMIT_500mA:
+                       val->intval = 500000;
+                       break;
+               case AXP20X_VBUC_CLIMIT_900mA:
+                       val->intval = 900000;
+                       break;
+               case AXP20X_VBUC_CLIMIT_NONE:
+                       val->intval = -1;
+                       break;
+               }
+               return 0;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = axp20x_read_variable_width(power->regmap,
+                                                AXP20X_VBUS_I_ADC_H, 12);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 375; /* 1 step = 0.375 mA */
+               return 0;
+       default:
+               break;
+       }
+
+       /* All the properties below need the input-status reg value */
+       ret = regmap_read(power->regmap, AXP20X_PWR_INPUT_STATUS, &input);
+       if (ret)
+               return ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (!(input & AXP20X_PWR_STATUS_VBUS_PRESENT)) {
+                       val->intval = POWER_SUPPLY_HEALTH_UNKNOWN;
+                       break;
+               }
+
+               ret = regmap_read(power->regmap, AXP20X_USB_OTG_STATUS, &v);
+               if (ret)
+                       return ret;
+
+               if (!(v & AXP20X_USB_STATUS_VBUS_VALID)) {
+                       val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+                       break;
+               }
+
+               val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = !!(input & AXP20X_PWR_STATUS_VBUS_USED);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static enum power_supply_property axp20x_usb_power_properties[] = {
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static const struct power_supply_desc axp20x_usb_power_desc = {
+       .name = "axp20x-usb",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = axp20x_usb_power_properties,
+       .num_properties = ARRAY_SIZE(axp20x_usb_power_properties),
+       .get_property = axp20x_usb_power_get_property,
+};
+
+static int axp20x_usb_power_probe(struct platform_device *pdev)
+{
+       struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+       struct power_supply_config psy_cfg = {};
+       struct axp20x_usb_power *power;
+       static const char * const irq_names[] = { "VBUS_PLUGIN",
+               "VBUS_REMOVAL", "VBUS_VALID", "VBUS_NOT_VALID" };
+       int i, irq, ret;
+
+       if (!of_device_is_available(pdev->dev.of_node))
+               return -ENODEV;
+
+       if (!axp20x) {
+               dev_err(&pdev->dev, "Parent drvdata not set\n");
+               return -EINVAL;
+       }
+
+       power = devm_kzalloc(&pdev->dev, sizeof(*power), GFP_KERNEL);
+       if (!power)
+               return -ENOMEM;
+
+       power->regmap = axp20x->regmap;
+
+       /* Enable vbus valid checking */
+       ret = regmap_update_bits(power->regmap, AXP20X_VBUS_MON,
+                   AXP20X_VBUS_MON_VBUS_VALID, AXP20X_VBUS_MON_VBUS_VALID);
+       if (ret)
+               return ret;
+
+       /* Enable vbus voltage and current measurement */
+       ret = regmap_update_bits(power->regmap, AXP20X_ADC_EN1,
+                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT,
+                       AXP20X_ADC_EN1_VBUS_CURR | AXP20X_ADC_EN1_VBUS_VOLT);
+       if (ret)
+               return ret;
+
+       psy_cfg.of_node = pdev->dev.of_node;
+       psy_cfg.drv_data = power;
+
+       power->supply = devm_power_supply_register(&pdev->dev,
+                                       &axp20x_usb_power_desc, &psy_cfg);
+       if (IS_ERR(power->supply))
+               return PTR_ERR(power->supply);
+
+       /* Request irqs after registering, as irqs may trigger immediately */
+       for (i = 0; i < ARRAY_SIZE(irq_names); i++) {
+               irq = platform_get_irq_byname(pdev, irq_names[i]);
+               if (irq < 0) {
+                       dev_warn(&pdev->dev, "No IRQ for %s: %d\n",
+                                irq_names[i], irq);
+                       continue;
+               }
+               irq = regmap_irq_get_virq(axp20x->regmap_irqc, irq);
+               ret = devm_request_any_context_irq(&pdev->dev, irq,
+                               axp20x_usb_power_irq, 0, DRVNAME, power);
+               if (ret < 0)
+                       dev_warn(&pdev->dev, "Error requesting %s IRQ: %d\n",
+                                irq_names[i], ret);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id axp20x_usb_power_match[] = {
+       { .compatible = "x-powers,axp202-usb-power-supply" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, axp20x_usb_power_match);
+
+static struct platform_driver axp20x_usb_power_driver = {
+       .probe = axp20x_usb_power_probe,
+       .driver = {
+               .name = DRVNAME,
+               .of_match_table = axp20x_usb_power_match,
+       },
+};
+
+module_platform_driver(axp20x_usb_power_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("AXP20x PMIC USB power supply status driver");
+MODULE_LICENSE("GPL");
index ec212b5be755c68cf28313fafe24910c2d0cfc03..4afd76848bce5bac52948e62c7c0bdf5aba79fa0 100644 (file)
@@ -1704,7 +1704,7 @@ error_4:
 error_3:
        bq2415x_power_supply_exit(bq);
 error_2:
-       if (bq->notify_node)
+       if (bq && bq->notify_node)
                of_node_put(bq->notify_node);
        kfree(name);
 error_1:
index 469a452cbe1004025d86326222671f11e0674b52..f5746b9f4e83900edd33fb049229b24aa48ba1b9 100644 (file)
@@ -1543,5 +1543,4 @@ module_i2c_driver(bq24190_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Mark A. Greer <mgreer@animalcreek.com>");
-MODULE_ALIAS("i2c:bq24190-charger");
 MODULE_DESCRIPTION("TI BQ24190 Charger Driver");
diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c
deleted file mode 100644 (file)
index 8287261..0000000
+++ /dev/null
@@ -1,1129 +0,0 @@
-/*
- * BQ27x00 battery driver
- *
- * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
- * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
- * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
- * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
- *
- * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
- *
- * This package is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Datasheets:
- * http://focus.ti.com/docs/prod/folders/print/bq27000.html
- * http://focus.ti.com/docs/prod/folders/print/bq27500.html
- * http://www.ti.com/product/bq27425-g1
- * http://www.ti.com/product/BQ27742-G1
- * http://www.ti.com/product/BQ27510-G3
- */
-
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/param.h>
-#include <linux/jiffies.h>
-#include <linux/workqueue.h>
-#include <linux/delay.h>
-#include <linux/platform_device.h>
-#include <linux/power_supply.h>
-#include <linux/idr.h>
-#include <linux/i2c.h>
-#include <linux/slab.h>
-#include <asm/unaligned.h>
-
-#include <linux/power/bq27x00_battery.h>
-
-#define DRIVER_VERSION         "1.2.0"
-
-#define BQ27XXX_MANUFACTURER   "Texas Instruments"
-
-#define BQ27x00_REG_TEMP       0x06
-#define BQ27x00_REG_VOLT       0x08
-#define BQ27x00_REG_AI         0x14
-#define BQ27x00_REG_FLAGS      0x0A
-#define BQ27x00_REG_TTE                0x16
-#define BQ27x00_REG_TTF                0x18
-#define BQ27x00_REG_TTECP      0x26
-#define BQ27x00_REG_NAC                0x0C /* Nominal available capacity */
-#define BQ27x00_REG_LMD                0x12 /* Last measured discharge */
-#define BQ27x00_REG_CYCT       0x2A /* Cycle count total */
-#define BQ27x00_REG_AE         0x22 /* Available energy */
-#define BQ27x00_POWER_AVG      0x24
-
-#define BQ27000_REG_RSOC       0x0B /* Relative State-of-Charge */
-#define BQ27000_REG_ILMD       0x76 /* Initial last measured discharge */
-#define BQ27000_FLAG_EDVF      BIT(0) /* Final End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_EDV1      BIT(1) /* First End-of-Discharge-Voltage flag */
-#define BQ27000_FLAG_CI                BIT(4) /* Capacity Inaccurate flag */
-#define BQ27000_FLAG_FC                BIT(5)
-#define BQ27000_FLAG_CHGS      BIT(7) /* Charge state flag */
-
-#define BQ27500_REG_SOC                0x2C
-#define BQ27500_REG_DCAP       0x3C /* Design capacity */
-#define BQ27500_FLAG_DSC       BIT(0)
-#define BQ27500_FLAG_SOCF      BIT(1) /* State-of-Charge threshold final */
-#define BQ27500_FLAG_SOC1      BIT(2) /* State-of-Charge threshold 1 */
-#define BQ27500_FLAG_FC                BIT(9)
-#define BQ27500_FLAG_OTC       BIT(15)
-
-#define BQ27742_POWER_AVG      0x76
-
-#define BQ27510_REG_SOC                0x20
-#define BQ27510_REG_DCAP       0x2E /* Design capacity */
-#define BQ27510_REG_CYCT       0x1E /* Cycle count total */
-
-/* bq27425 register addresses are same as bq27x00 addresses minus 4 */
-#define BQ27425_REG_OFFSET     0x04
-#define BQ27425_REG_SOC                (0x1C + BQ27425_REG_OFFSET)
-#define BQ27425_REG_DCAP       (0x3C + BQ27425_REG_OFFSET)
-
-#define BQ27000_RS                     20 /* Resistor sense */
-#define BQ27x00_POWER_CONSTANT         (256 * 29200 / 1000)
-
-struct bq27x00_device_info;
-struct bq27x00_access_methods {
-       int (*read)(struct bq27x00_device_info *di, u8 reg, bool single);
-};
-
-enum bq27x00_chip { BQ27000, BQ27500, BQ27425, BQ27742, BQ27510};
-
-struct bq27x00_reg_cache {
-       int temperature;
-       int time_to_empty;
-       int time_to_empty_avg;
-       int time_to_full;
-       int charge_full;
-       int cycle_count;
-       int capacity;
-       int energy;
-       int flags;
-       int power_avg;
-       int health;
-};
-
-struct bq27x00_device_info {
-       struct device           *dev;
-       int                     id;
-       enum bq27x00_chip       chip;
-
-       struct bq27x00_reg_cache cache;
-       int charge_design_full;
-
-       unsigned long last_update;
-       struct delayed_work work;
-
-       struct power_supply     *bat;
-
-       struct bq27x00_access_methods bus;
-
-       struct mutex lock;
-};
-
-static enum power_supply_property bq27x00_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
-       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_ENERGY_NOW,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27425_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27742_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static enum power_supply_property bq27510_battery_props[] = {
-       POWER_SUPPLY_PROP_STATUS,
-       POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_VOLTAGE_NOW,
-       POWER_SUPPLY_PROP_CURRENT_NOW,
-       POWER_SUPPLY_PROP_CAPACITY,
-       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
-       POWER_SUPPLY_PROP_TEMP,
-       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
-       POWER_SUPPLY_PROP_TECHNOLOGY,
-       POWER_SUPPLY_PROP_CHARGE_FULL,
-       POWER_SUPPLY_PROP_CHARGE_NOW,
-       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
-       POWER_SUPPLY_PROP_CYCLE_COUNT,
-       POWER_SUPPLY_PROP_POWER_AVG,
-       POWER_SUPPLY_PROP_HEALTH,
-       POWER_SUPPLY_PROP_MANUFACTURER,
-};
-
-static unsigned int poll_interval = 360;
-module_param(poll_interval, uint, 0644);
-MODULE_PARM_DESC(poll_interval,
-                "battery poll interval in seconds - 0 disables polling");
-
-/*
- * Common code for BQ27x00 devices
- */
-
-static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
-                              bool single)
-{
-       if (di->chip == BQ27425)
-               return di->bus.read(di, reg - BQ27425_REG_OFFSET, single);
-       return di->bus.read(di, reg, single);
-}
-
-/*
- * Higher versions of the chip like BQ27425 and BQ27500
- * differ from BQ27000 and BQ27200 in calculation of certain
- * parameters. Hence we need to check for the chip type.
- */
-static bool bq27xxx_is_chip_version_higher(struct bq27x00_device_info *di)
-{
-       if (di->chip == BQ27425 || di->chip == BQ27500 || di->chip == BQ27742
-           || di->chip == BQ27510)
-               return true;
-       return false;
-}
-
-/*
- * Return the battery Relative State-of-Charge
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
-{
-       int rsoc;
-
-       if (di->chip == BQ27500 || di->chip == BQ27742)
-               rsoc = bq27x00_read(di, BQ27500_REG_SOC, false);
-       else if (di->chip == BQ27510)
-               rsoc = bq27x00_read(di, BQ27510_REG_SOC, false);
-       else if (di->chip == BQ27425)
-               rsoc = bq27x00_read(di, BQ27425_REG_SOC, false);
-       else
-               rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
-
-       if (rsoc < 0)
-               dev_dbg(di->dev, "error reading relative State-of-Charge\n");
-
-       return rsoc;
-}
-
-/*
- * Return a battery charge value in µAh
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
-{
-       int charge;
-
-       charge = bq27x00_read(di, reg, false);
-       if (charge < 0) {
-               dev_dbg(di->dev, "error reading charge register %02x: %d\n",
-                       reg, charge);
-               return charge;
-       }
-
-       if (bq27xxx_is_chip_version_higher(di))
-               charge *= 1000;
-       else
-               charge = charge * 3570 / BQ27000_RS;
-
-       return charge;
-}
-
-/*
- * Return the battery Nominal available capaciy in µAh
- * Or < 0 if something fails.
- */
-static inline int bq27x00_battery_read_nac(struct bq27x00_device_info *di)
-{
-       int flags;
-       bool is_bq27500 = di->chip == BQ27500;
-       bool is_bq27742 = di->chip == BQ27742;
-       bool is_higher = bq27xxx_is_chip_version_higher(di);
-       bool flags_1b = !(is_bq27500 || is_bq27742);
-
-       flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b);
-       if (flags >= 0 && !is_higher && (flags & BQ27000_FLAG_CI))
-               return -ENODATA;
-
-       return bq27x00_battery_read_charge(di, BQ27x00_REG_NAC);
-}
-
-/*
- * Return the battery Last measured discharge in µAh
- * Or < 0 if something fails.
- */
-static inline int bq27x00_battery_read_lmd(struct bq27x00_device_info *di)
-{
-       return bq27x00_battery_read_charge(di, BQ27x00_REG_LMD);
-}
-
-/*
- * Return the battery Initial last measured discharge in µAh
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
-{
-       int ilmd;
-
-       if (bq27xxx_is_chip_version_higher(di)) {
-               if (di->chip == BQ27425)
-                       ilmd = bq27x00_read(di, BQ27425_REG_DCAP, false);
-               else if (di->chip == BQ27510)
-                       ilmd = bq27x00_read(di, BQ27510_REG_DCAP, false);
-               else
-                       ilmd = bq27x00_read(di, BQ27500_REG_DCAP, false);
-       } else {
-               ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
-       }
-
-       if (ilmd < 0) {
-               dev_dbg(di->dev, "error reading initial last measured discharge\n");
-               return ilmd;
-       }
-
-       if (bq27xxx_is_chip_version_higher(di))
-               ilmd *= 1000;
-       else
-               ilmd = ilmd * 256 * 3570 / BQ27000_RS;
-
-       return ilmd;
-}
-
-/*
- * Return the battery Available energy in µWh
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
-{
-       int ae;
-
-       ae = bq27x00_read(di, BQ27x00_REG_AE, false);
-       if (ae < 0) {
-               dev_dbg(di->dev, "error reading available energy\n");
-               return ae;
-       }
-
-       if (di->chip == BQ27500)
-               ae *= 1000;
-       else
-               ae = ae * 29200 / BQ27000_RS;
-
-       return ae;
-}
-
-/*
- * Return the battery temperature in tenths of degree Kelvin
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_temperature(struct bq27x00_device_info *di)
-{
-       int temp;
-
-       temp = bq27x00_read(di, BQ27x00_REG_TEMP, false);
-       if (temp < 0) {
-               dev_err(di->dev, "error reading temperature\n");
-               return temp;
-       }
-
-       if (!bq27xxx_is_chip_version_higher(di))
-               temp = 5 * temp / 2;
-
-       return temp;
-}
-
-/*
- * Return the battery Cycle count total
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_read_cyct(struct bq27x00_device_info *di)
-{
-       int cyct;
-
-       if (di->chip == BQ27510)
-               cyct = bq27x00_read(di, BQ27510_REG_CYCT, false);
-       else
-               cyct = bq27x00_read(di, BQ27x00_REG_CYCT, false);
-       if (cyct < 0)
-               dev_err(di->dev, "error reading cycle count total\n");
-
-       return cyct;
-}
-
-/*
- * Read a time register.
- * Return < 0 if something fails.
- */
-static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
-{
-       int tval;
-
-       tval = bq27x00_read(di, reg, false);
-       if (tval < 0) {
-               dev_dbg(di->dev, "error reading time register %02x: %d\n",
-                       reg, tval);
-               return tval;
-       }
-
-       if (tval == 65535)
-               return -ENODATA;
-
-       return tval * 60;
-}
-
-/*
- * Read a power avg register.
- * Return < 0 if something fails.
- */
-static int bq27x00_battery_read_pwr_avg(struct bq27x00_device_info *di, u8 reg)
-{
-       int tval;
-
-       tval = bq27x00_read(di, reg, false);
-       if (tval < 0) {
-               dev_err(di->dev, "error reading power avg rgister  %02x: %d\n",
-                       reg, tval);
-               return tval;
-       }
-
-       if (di->chip == BQ27500)
-               return tval;
-       else
-               return (tval * BQ27x00_POWER_CONSTANT) / BQ27000_RS;
-}
-
-/*
- * Read flag register.
- * Return < 0 if something fails.
- */
-static int bq27x00_battery_read_health(struct bq27x00_device_info *di)
-{
-       int tval;
-
-       tval = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
-       if (tval < 0) {
-               dev_err(di->dev, "error reading flag register:%d\n", tval);
-               return tval;
-       }
-
-       if (di->chip == BQ27500) {
-               if (tval & BQ27500_FLAG_SOCF)
-                       tval = POWER_SUPPLY_HEALTH_DEAD;
-               else if (tval & BQ27500_FLAG_OTC)
-                       tval = POWER_SUPPLY_HEALTH_OVERHEAT;
-               else
-                       tval = POWER_SUPPLY_HEALTH_GOOD;
-               return tval;
-       } else if (di->chip == BQ27510) {
-               if (tval & BQ27500_FLAG_OTC)
-                       return POWER_SUPPLY_HEALTH_OVERHEAT;
-               return POWER_SUPPLY_HEALTH_GOOD;
-       } else {
-               if (tval & BQ27000_FLAG_EDV1)
-                       tval = POWER_SUPPLY_HEALTH_DEAD;
-               else
-                       tval = POWER_SUPPLY_HEALTH_GOOD;
-               return tval;
-       }
-
-       return -1;
-}
-
-static void bq27x00_update(struct bq27x00_device_info *di)
-{
-       struct bq27x00_reg_cache cache = {0, };
-       bool is_bq27500 = di->chip == BQ27500;
-       bool is_bq27510 = di->chip == BQ27510;
-       bool is_bq27425 = di->chip == BQ27425;
-       bool is_bq27742 = di->chip == BQ27742;
-       bool flags_1b = !(is_bq27500 || is_bq27742);
-
-       cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, flags_1b);
-       if ((cache.flags & 0xff) == 0xff)
-               /* read error */
-               cache.flags = -1;
-       if (cache.flags >= 0) {
-               if (!is_bq27500 && !is_bq27425 && !is_bq27742 && !is_bq27510
-                               && (cache.flags & BQ27000_FLAG_CI)) {
-                       dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
-                       cache.capacity = -ENODATA;
-                       cache.energy = -ENODATA;
-                       cache.time_to_empty = -ENODATA;
-                       cache.time_to_empty_avg = -ENODATA;
-                       cache.time_to_full = -ENODATA;
-                       cache.charge_full = -ENODATA;
-                       cache.health = -ENODATA;
-               } else {
-                       cache.capacity = bq27x00_battery_read_rsoc(di);
-                       if (is_bq27742 || is_bq27510)
-                               cache.time_to_empty =
-                                       bq27x00_battery_read_time(di,
-                                                       BQ27x00_REG_TTE);
-                       else if (!is_bq27425) {
-                               cache.energy = bq27x00_battery_read_energy(di);
-                               cache.time_to_empty =
-                                       bq27x00_battery_read_time(di,
-                                                       BQ27x00_REG_TTE);
-                               cache.time_to_empty_avg =
-                                       bq27x00_battery_read_time(di,
-                                                       BQ27x00_REG_TTECP);
-                               cache.time_to_full =
-                                       bq27x00_battery_read_time(di,
-                                                       BQ27x00_REG_TTF);
-                       }
-                       cache.charge_full = bq27x00_battery_read_lmd(di);
-                       cache.health = bq27x00_battery_read_health(di);
-               }
-               cache.temperature = bq27x00_battery_read_temperature(di);
-               if (!is_bq27425)
-                       cache.cycle_count = bq27x00_battery_read_cyct(di);
-               if (is_bq27742)
-                       cache.power_avg =
-                               bq27x00_battery_read_pwr_avg(di,
-                                               BQ27742_POWER_AVG);
-               else
-                       cache.power_avg =
-                               bq27x00_battery_read_pwr_avg(di,
-                                               BQ27x00_POWER_AVG);
-
-               /* We only have to read charge design full once */
-               if (di->charge_design_full <= 0)
-                       di->charge_design_full = bq27x00_battery_read_ilmd(di);
-       }
-
-       if (di->cache.capacity != cache.capacity)
-               power_supply_changed(di->bat);
-
-       if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
-               di->cache = cache;
-
-       di->last_update = jiffies;
-}
-
-static void bq27x00_battery_poll(struct work_struct *work)
-{
-       struct bq27x00_device_info *di =
-               container_of(work, struct bq27x00_device_info, work.work);
-
-       bq27x00_update(di);
-
-       if (poll_interval > 0) {
-               /* The timer does not have to be accurate. */
-               set_timer_slack(&di->work.timer, poll_interval * HZ / 4);
-               schedule_delayed_work(&di->work, poll_interval * HZ);
-       }
-}
-
-/*
- * Return the battery average current in µA
- * Note that current can be negative signed as well
- * Or 0 if something fails.
- */
-static int bq27x00_battery_current(struct bq27x00_device_info *di,
-                                  union power_supply_propval *val)
-{
-       int curr;
-       int flags;
-
-       curr = bq27x00_read(di, BQ27x00_REG_AI, false);
-       if (curr < 0) {
-               dev_err(di->dev, "error reading current\n");
-               return curr;
-       }
-
-       if (bq27xxx_is_chip_version_higher(di)) {
-               /* bq27500 returns signed value */
-               val->intval = (int)((s16)curr) * 1000;
-       } else {
-               flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
-               if (flags & BQ27000_FLAG_CHGS) {
-                       dev_dbg(di->dev, "negative current!\n");
-                       curr = -curr;
-               }
-
-               val->intval = curr * 3570 / BQ27000_RS;
-       }
-
-       return 0;
-}
-
-static int bq27x00_battery_status(struct bq27x00_device_info *di,
-                                 union power_supply_propval *val)
-{
-       int status;
-
-       if (bq27xxx_is_chip_version_higher(di)) {
-               if (di->cache.flags & BQ27500_FLAG_FC)
-                       status = POWER_SUPPLY_STATUS_FULL;
-               else if (di->cache.flags & BQ27500_FLAG_DSC)
-                       status = POWER_SUPPLY_STATUS_DISCHARGING;
-               else
-                       status = POWER_SUPPLY_STATUS_CHARGING;
-       } else {
-               if (di->cache.flags & BQ27000_FLAG_FC)
-                       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))
-                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-               else
-                       status = POWER_SUPPLY_STATUS_DISCHARGING;
-       }
-
-       val->intval = status;
-
-       return 0;
-}
-
-static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
-                                         union power_supply_propval *val)
-{
-       int level;
-
-       if (bq27xxx_is_chip_version_higher(di)) {
-               if (di->cache.flags & BQ27500_FLAG_FC)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-               else if (di->cache.flags & BQ27500_FLAG_SOC1)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-               else if (di->cache.flags & BQ27500_FLAG_SOCF)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-               else
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-       } else {
-               if (di->cache.flags & BQ27000_FLAG_FC)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
-               else if (di->cache.flags & BQ27000_FLAG_EDV1)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
-               else if (di->cache.flags & BQ27000_FLAG_EDVF)
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
-               else
-                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
-       }
-
-       val->intval = level;
-
-       return 0;
-}
-
-/*
- * Return the battery Voltage in millivolts
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
-                                  union power_supply_propval *val)
-{
-       int volt;
-
-       volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
-       if (volt < 0) {
-               dev_err(di->dev, "error reading voltage\n");
-               return volt;
-       }
-
-       val->intval = volt * 1000;
-
-       return 0;
-}
-
-static int bq27x00_simple_value(int value,
-                               union power_supply_propval *val)
-{
-       if (value < 0)
-               return value;
-
-       val->intval = value;
-
-       return 0;
-}
-
-static int bq27x00_battery_get_property(struct power_supply *psy,
-                                       enum power_supply_property psp,
-                                       union power_supply_propval *val)
-{
-       int ret = 0;
-       struct bq27x00_device_info *di = power_supply_get_drvdata(psy);
-
-       mutex_lock(&di->lock);
-       if (time_is_before_jiffies(di->last_update + 5 * HZ)) {
-               cancel_delayed_work_sync(&di->work);
-               bq27x00_battery_poll(&di->work.work);
-       }
-       mutex_unlock(&di->lock);
-
-       if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
-               return -ENODEV;
-
-       switch (psp) {
-       case POWER_SUPPLY_PROP_STATUS:
-               ret = bq27x00_battery_status(di, val);
-               break;
-       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               ret = bq27x00_battery_voltage(di, val);
-               break;
-       case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = di->cache.flags < 0 ? 0 : 1;
-               break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               ret = bq27x00_battery_current(di, val);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY:
-               ret = bq27x00_simple_value(di->cache.capacity, val);
-               break;
-       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
-               ret = bq27x00_battery_capacity_level(di, val);
-               break;
-       case POWER_SUPPLY_PROP_TEMP:
-               ret = bq27x00_simple_value(di->cache.temperature, val);
-               if (ret == 0)
-                       val->intval -= 2731;
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
-               ret = bq27x00_simple_value(di->cache.time_to_empty, val);
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
-               ret = bq27x00_simple_value(di->cache.time_to_empty_avg, val);
-               break;
-       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
-               ret = bq27x00_simple_value(di->cache.time_to_full, val);
-               break;
-       case POWER_SUPPLY_PROP_TECHNOLOGY:
-               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_NOW:
-               ret = bq27x00_simple_value(bq27x00_battery_read_nac(di), val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL:
-               ret = bq27x00_simple_value(di->cache.charge_full, val);
-               break;
-       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
-               ret = bq27x00_simple_value(di->charge_design_full, val);
-               break;
-       case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               ret = bq27x00_simple_value(di->cache.cycle_count, val);
-               break;
-       case POWER_SUPPLY_PROP_ENERGY_NOW:
-               ret = bq27x00_simple_value(di->cache.energy, val);
-               break;
-       case POWER_SUPPLY_PROP_POWER_AVG:
-               ret = bq27x00_simple_value(di->cache.power_avg, val);
-               break;
-       case POWER_SUPPLY_PROP_HEALTH:
-               ret = bq27x00_simple_value(di->cache.health, val);
-               break;
-       case POWER_SUPPLY_PROP_MANUFACTURER:
-               val->strval = BQ27XXX_MANUFACTURER;
-               break;
-       default:
-               return -EINVAL;
-       }
-
-       return ret;
-}
-
-static void bq27x00_external_power_changed(struct power_supply *psy)
-{
-       struct bq27x00_device_info *di = power_supply_get_drvdata(psy);
-
-       cancel_delayed_work_sync(&di->work);
-       schedule_delayed_work(&di->work, 0);
-}
-
-static int bq27x00_powersupply_init(struct bq27x00_device_info *di,
-                                   const char *name)
-{
-       int ret;
-       struct power_supply_desc *psy_desc;
-       struct power_supply_config psy_cfg = { .drv_data = di, };
-
-       psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
-       if (!psy_desc)
-               return -ENOMEM;
-
-       psy_desc->name = name;
-       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
-       if (di->chip == BQ27425) {
-               psy_desc->properties = bq27425_battery_props;
-               psy_desc->num_properties = ARRAY_SIZE(bq27425_battery_props);
-       } else if (di->chip == BQ27742) {
-               psy_desc->properties = bq27742_battery_props;
-               psy_desc->num_properties = ARRAY_SIZE(bq27742_battery_props);
-       } else if (di->chip == BQ27510) {
-               psy_desc->properties = bq27510_battery_props;
-               psy_desc->num_properties = ARRAY_SIZE(bq27510_battery_props);
-       } else {
-               psy_desc->properties = bq27x00_battery_props;
-               psy_desc->num_properties = ARRAY_SIZE(bq27x00_battery_props);
-       }
-       psy_desc->get_property = bq27x00_battery_get_property;
-       psy_desc->external_power_changed = bq27x00_external_power_changed;
-
-       INIT_DELAYED_WORK(&di->work, bq27x00_battery_poll);
-       mutex_init(&di->lock);
-
-       di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
-       if (IS_ERR(di->bat)) {
-               ret = PTR_ERR(di->bat);
-               dev_err(di->dev, "failed to register battery: %d\n", ret);
-               return ret;
-       }
-
-       dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
-
-       bq27x00_update(di);
-
-       return 0;
-}
-
-static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
-{
-       /*
-        * power_supply_unregister call bq27x00_battery_get_property which
-        * call bq27x00_battery_poll.
-        * Make sure that bq27x00_battery_poll will not call
-        * schedule_delayed_work again after unregister (which cause OOPS).
-        */
-       poll_interval = 0;
-
-       cancel_delayed_work_sync(&di->work);
-
-       power_supply_unregister(di->bat);
-
-       mutex_destroy(&di->lock);
-}
-
-/* i2c specific code */
-#ifdef CONFIG_BATTERY_BQ27X00_I2C
-
-/* If the system has several batteries we need a different name for each
- * of them...
- */
-static DEFINE_IDR(battery_id);
-static DEFINE_MUTEX(battery_mutex);
-
-static int bq27x00_read_i2c(struct bq27x00_device_info *di, u8 reg, bool single)
-{
-       struct i2c_client *client = to_i2c_client(di->dev);
-       struct i2c_msg msg[2];
-       unsigned char data[2];
-       int ret;
-
-       if (!client->adapter)
-               return -ENODEV;
-
-       msg[0].addr = client->addr;
-       msg[0].flags = 0;
-       msg[0].buf = &reg;
-       msg[0].len = sizeof(reg);
-       msg[1].addr = client->addr;
-       msg[1].flags = I2C_M_RD;
-       msg[1].buf = data;
-       if (single)
-               msg[1].len = 1;
-       else
-               msg[1].len = 2;
-
-       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
-       if (ret < 0)
-               return ret;
-
-       if (!single)
-               ret = get_unaligned_le16(data);
-       else
-               ret = data[0];
-
-       return ret;
-}
-
-static int bq27x00_battery_probe(struct i2c_client *client,
-                                const struct i2c_device_id *id)
-{
-       char *name;
-       struct bq27x00_device_info *di;
-       int num;
-       int retval = 0;
-
-       /* Get new ID for the new battery device */
-       mutex_lock(&battery_mutex);
-       num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
-       mutex_unlock(&battery_mutex);
-       if (num < 0)
-               return num;
-
-       name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
-       if (!name) {
-               retval = -ENOMEM;
-               goto batt_failed;
-       }
-
-       di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
-       if (!di) {
-               retval = -ENOMEM;
-               goto batt_failed;
-       }
-
-       di->id = num;
-       di->dev = &client->dev;
-       di->chip = id->driver_data;
-       di->bus.read = &bq27x00_read_i2c;
-
-       retval = bq27x00_powersupply_init(di, name);
-       if (retval)
-               goto batt_failed;
-
-       i2c_set_clientdata(client, di);
-
-       return 0;
-
-batt_failed:
-       mutex_lock(&battery_mutex);
-       idr_remove(&battery_id, num);
-       mutex_unlock(&battery_mutex);
-
-       return retval;
-}
-
-static int bq27x00_battery_remove(struct i2c_client *client)
-{
-       struct bq27x00_device_info *di = i2c_get_clientdata(client);
-
-       bq27x00_powersupply_unregister(di);
-
-       mutex_lock(&battery_mutex);
-       idr_remove(&battery_id, di->id);
-       mutex_unlock(&battery_mutex);
-
-       return 0;
-}
-
-static const struct i2c_device_id bq27x00_id[] = {
-       { "bq27200", BQ27000 }, /* bq27200 is same as bq27000, but with i2c */
-       { "bq27500", BQ27500 },
-       { "bq27425", BQ27425 },
-       { "bq27742", BQ27742 },
-       { "bq27510", BQ27510 },
-       {},
-};
-MODULE_DEVICE_TABLE(i2c, bq27x00_id);
-
-static struct i2c_driver bq27x00_battery_driver = {
-       .driver = {
-               .name = "bq27x00-battery",
-       },
-       .probe = bq27x00_battery_probe,
-       .remove = bq27x00_battery_remove,
-       .id_table = bq27x00_id,
-};
-
-static inline int bq27x00_battery_i2c_init(void)
-{
-       int ret = i2c_add_driver(&bq27x00_battery_driver);
-
-       if (ret)
-               pr_err("Unable to register BQ27x00 i2c driver\n");
-
-       return ret;
-}
-
-static inline void bq27x00_battery_i2c_exit(void)
-{
-       i2c_del_driver(&bq27x00_battery_driver);
-}
-
-#else
-
-static inline int bq27x00_battery_i2c_init(void) { return 0; }
-static inline void bq27x00_battery_i2c_exit(void) {};
-
-#endif
-
-/* platform specific code */
-#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM
-
-static int bq27000_read_platform(struct bq27x00_device_info *di, u8 reg,
-                                bool single)
-{
-       struct device *dev = di->dev;
-       struct bq27000_platform_data *pdata = dev->platform_data;
-       unsigned int timeout = 3;
-       int upper, lower;
-       int temp;
-
-       if (!single) {
-               /* Make sure the value has not changed in between reading the
-                * lower and the upper part */
-               upper = pdata->read(dev, reg + 1);
-               do {
-                       temp = upper;
-                       if (upper < 0)
-                               return upper;
-
-                       lower = pdata->read(dev, reg);
-                       if (lower < 0)
-                               return lower;
-
-                       upper = pdata->read(dev, reg + 1);
-               } while (temp != upper && --timeout);
-
-               if (timeout == 0)
-                       return -EIO;
-
-               return (upper << 8) | lower;
-       }
-
-       return pdata->read(dev, reg);
-}
-
-static int bq27000_battery_probe(struct platform_device *pdev)
-{
-       struct bq27x00_device_info *di;
-       struct bq27000_platform_data *pdata = pdev->dev.platform_data;
-       const char *name;
-
-       if (!pdata) {
-               dev_err(&pdev->dev, "no platform_data supplied\n");
-               return -EINVAL;
-       }
-
-       if (!pdata->read) {
-               dev_err(&pdev->dev, "no hdq read callback supplied\n");
-               return -EINVAL;
-       }
-
-       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
-       if (!di)
-               return -ENOMEM;
-
-       platform_set_drvdata(pdev, di);
-
-       di->dev = &pdev->dev;
-       di->chip = BQ27000;
-
-       name = pdata->name ?: dev_name(&pdev->dev);
-       di->bus.read = &bq27000_read_platform;
-
-       return bq27x00_powersupply_init(di, name);
-}
-
-static int bq27000_battery_remove(struct platform_device *pdev)
-{
-       struct bq27x00_device_info *di = platform_get_drvdata(pdev);
-
-       bq27x00_powersupply_unregister(di);
-
-       return 0;
-}
-
-static struct platform_driver bq27000_battery_driver = {
-       .probe  = bq27000_battery_probe,
-       .remove = bq27000_battery_remove,
-       .driver = {
-               .name = "bq27000-battery",
-       },
-};
-
-static inline int bq27x00_battery_platform_init(void)
-{
-       int ret = platform_driver_register(&bq27000_battery_driver);
-
-       if (ret)
-               pr_err("Unable to register BQ27000 platform driver\n");
-
-       return ret;
-}
-
-static inline void bq27x00_battery_platform_exit(void)
-{
-       platform_driver_unregister(&bq27000_battery_driver);
-}
-
-#else
-
-static inline int bq27x00_battery_platform_init(void) { return 0; }
-static inline void bq27x00_battery_platform_exit(void) {};
-
-#endif
-
-/*
- * Module stuff
- */
-
-static int __init bq27x00_battery_init(void)
-{
-       int ret;
-
-       ret = bq27x00_battery_i2c_init();
-       if (ret)
-               return ret;
-
-       ret = bq27x00_battery_platform_init();
-       if (ret)
-               bq27x00_battery_i2c_exit();
-
-       return ret;
-}
-module_init(bq27x00_battery_init);
-
-static void __exit bq27x00_battery_exit(void)
-{
-       bq27x00_battery_platform_exit();
-       bq27x00_battery_i2c_exit();
-}
-module_exit(bq27x00_battery_exit);
-
-#ifdef CONFIG_BATTERY_BQ27X00_PLATFORM
-MODULE_ALIAS("platform:bq27000-battery");
-#endif
-
-#ifdef CONFIG_BATTERY_BQ27X00_I2C
-MODULE_ALIAS("i2c:bq27000-battery");
-#endif
-
-MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
-MODULE_DESCRIPTION("BQ27x00 battery monitor driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/power/bq27xxx_battery.c b/drivers/power/bq27xxx_battery.c
new file mode 100644 (file)
index 0000000..473aa2f
--- /dev/null
@@ -0,0 +1,1374 @@
+/*
+ * BQ27xxx battery driver
+ *
+ * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it>
+ * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it>
+ * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com>
+ *
+ * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc.
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Datasheets:
+ * http://www.ti.com/product/bq27000
+ * http://www.ti.com/product/bq27200
+ * http://www.ti.com/product/bq27010
+ * http://www.ti.com/product/bq27210
+ * http://www.ti.com/product/bq27500
+ * http://www.ti.com/product/bq27510-g3
+ * http://www.ti.com/product/bq27520-g4
+ * http://www.ti.com/product/bq27530-g1
+ * http://www.ti.com/product/bq27531-g1
+ * http://www.ti.com/product/bq27541-g1
+ * http://www.ti.com/product/bq27542-g1
+ * http://www.ti.com/product/bq27546-g1
+ * http://www.ti.com/product/bq27742-g1
+ * http://www.ti.com/product/bq27545-g1
+ * http://www.ti.com/product/bq27421-g1
+ * http://www.ti.com/product/bq27425-g1
+ * http://www.ti.com/product/bq27411-g1
+ * http://www.ti.com/product/bq27621-g1
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/jiffies.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/idr.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <asm/unaligned.h>
+
+#include <linux/power/bq27xxx_battery.h>
+
+#define DRIVER_VERSION         "1.2.0"
+
+#define BQ27XXX_MANUFACTURER   "Texas Instruments"
+
+/* BQ27XXX Flags */
+#define BQ27XXX_FLAG_DSC       BIT(0)
+#define BQ27XXX_FLAG_SOCF      BIT(1) /* State-of-Charge threshold final */
+#define BQ27XXX_FLAG_SOC1      BIT(2) /* State-of-Charge threshold 1 */
+#define BQ27XXX_FLAG_FC                BIT(9)
+#define BQ27XXX_FLAG_OTD       BIT(14)
+#define BQ27XXX_FLAG_OTC       BIT(15)
+#define BQ27XXX_FLAG_UT                BIT(14)
+#define BQ27XXX_FLAG_OT                BIT(15)
+
+/* BQ27000 has different layout for Flags register */
+#define BQ27000_FLAG_EDVF      BIT(0) /* Final End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_EDV1      BIT(1) /* First End-of-Discharge-Voltage flag */
+#define BQ27000_FLAG_CI                BIT(4) /* Capacity Inaccurate flag */
+#define BQ27000_FLAG_FC                BIT(5)
+#define BQ27000_FLAG_CHGS      BIT(7) /* Charge state flag */
+
+#define BQ27XXX_RS                     (20) /* Resistor sense mOhm */
+#define BQ27XXX_POWER_CONSTANT         (29200) /* 29.2 µV^2 * 1000 */
+#define BQ27XXX_CURRENT_CONSTANT       (3570) /* 3.57 µV * 1000 */
+
+struct bq27xxx_device_info;
+struct bq27xxx_access_methods {
+       int (*read)(struct bq27xxx_device_info *di, u8 reg, bool single);
+};
+
+#define INVALID_REG_ADDR       0xff
+
+/*
+ * bq27xxx_reg_index - Register names
+ *
+ * These are indexes into a device's register mapping array.
+ */
+enum bq27xxx_reg_index {
+       BQ27XXX_REG_CTRL = 0,   /* Control */
+       BQ27XXX_REG_TEMP,       /* Temperature */
+       BQ27XXX_REG_INT_TEMP,   /* Internal Temperature */
+       BQ27XXX_REG_VOLT,       /* Voltage */
+       BQ27XXX_REG_AI,         /* Average Current */
+       BQ27XXX_REG_FLAGS,      /* Flags */
+       BQ27XXX_REG_TTE,        /* Time-to-Empty */
+       BQ27XXX_REG_TTF,        /* Time-to-Full */
+       BQ27XXX_REG_TTES,       /* Time-to-Empty Standby */
+       BQ27XXX_REG_TTECP,      /* Time-to-Empty at Constant Power */
+       BQ27XXX_REG_NAC,        /* Nominal Available Capacity */
+       BQ27XXX_REG_FCC,        /* Full Charge Capacity */
+       BQ27XXX_REG_CYCT,       /* Cycle Count */
+       BQ27XXX_REG_AE,         /* Available Energy */
+       BQ27XXX_REG_SOC,        /* State-of-Charge */
+       BQ27XXX_REG_DCAP,       /* Design Capacity */
+       BQ27XXX_REG_AP,         /* Average Power */
+};
+
+struct bq27xxx_reg_cache {
+       int temperature;
+       int time_to_empty;
+       int time_to_empty_avg;
+       int time_to_full;
+       int charge_full;
+       int cycle_count;
+       int capacity;
+       int energy;
+       int flags;
+       int power_avg;
+       int health;
+};
+
+struct bq27xxx_device_info {
+       struct device           *dev;
+       int                     id;
+       enum bq27xxx_chip       chip;
+
+       struct bq27xxx_reg_cache cache;
+       int charge_design_full;
+
+       unsigned long last_update;
+       struct delayed_work work;
+
+       struct power_supply     *bat;
+
+       struct bq27xxx_access_methods bus;
+
+       struct mutex lock;
+
+       u8 *regs;
+};
+
+/* Register mappings */
+static u8 bq27000_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       INVALID_REG_ADDR,       /* INT TEMP - NA*/
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       0x18,   /* TTF          */
+       0x1c,   /* TTES         */
+       0x26,   /* TTECP        */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x2a,   /* CYCT         */
+       0x22,   /* AE           */
+       0x0b,   /* SOC(RSOC)    */
+       0x76,   /* DCAP(ILMD)   */
+       0x24,   /* AP           */
+};
+
+static u8 bq27010_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       INVALID_REG_ADDR,       /* INT TEMP - NA*/
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       0x18,   /* TTF          */
+       0x1c,   /* TTES         */
+       0x26,   /* TTECP        */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x2a,   /* CYCT         */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x0b,   /* SOC(RSOC)    */
+       0x76,   /* DCAP(ILMD)   */
+       INVALID_REG_ADDR,       /* AP - NA      */
+};
+
+static u8 bq27500_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       0x28,   /* INT TEMP     */
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       INVALID_REG_ADDR,       /* TTF - NA     */
+       0x1a,   /* TTES         */
+       INVALID_REG_ADDR,       /* TTECP - NA   */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x1e,   /* CYCT         */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x20,   /* SOC(RSOC)    */
+       0x2e,   /* DCAP(ILMD)   */
+       INVALID_REG_ADDR,       /* AP - NA      */
+};
+
+static u8 bq27530_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       0x32,   /* INT TEMP     */
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       INVALID_REG_ADDR,       /* TTF - NA     */
+       INVALID_REG_ADDR,       /* TTES - NA    */
+       INVALID_REG_ADDR,       /* TTECP - NA   */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x2a,   /* CYCT         */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x2c,   /* SOC(RSOC)    */
+       INVALID_REG_ADDR,       /* DCAP - NA    */
+       0x24,   /* AP           */
+};
+
+static u8 bq27541_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       0x28,   /* INT TEMP     */
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       INVALID_REG_ADDR,       /* TTF - NA     */
+       INVALID_REG_ADDR,       /* TTES - NA    */
+       INVALID_REG_ADDR,       /* TTECP - NA   */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x2a,   /* CYCT         */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x2c,   /* SOC(RSOC)    */
+       0x3c,   /* DCAP         */
+       0x76,   /* AP           */
+};
+
+static u8 bq27545_regs[] = {
+       0x00,   /* CONTROL      */
+       0x06,   /* TEMP         */
+       0x28,   /* INT TEMP     */
+       0x08,   /* VOLT         */
+       0x14,   /* AVG CURR     */
+       0x0a,   /* FLAGS        */
+       0x16,   /* TTE          */
+       INVALID_REG_ADDR,       /* TTF - NA     */
+       INVALID_REG_ADDR,       /* TTES - NA    */
+       INVALID_REG_ADDR,       /* TTECP - NA   */
+       0x0c,   /* NAC          */
+       0x12,   /* LMD(FCC)     */
+       0x2a,   /* CYCT         */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x2c,   /* SOC(RSOC)    */
+       INVALID_REG_ADDR,       /* DCAP - NA */
+       0x24,   /* AP           */
+};
+
+static u8 bq27421_regs[] = {
+       0x00,   /* CONTROL      */
+       0x02,   /* TEMP         */
+       0x1e,   /* INT TEMP     */
+       0x04,   /* VOLT         */
+       0x10,   /* AVG CURR     */
+       0x06,   /* FLAGS        */
+       INVALID_REG_ADDR,       /* TTE - NA     */
+       INVALID_REG_ADDR,       /* TTF - NA     */
+       INVALID_REG_ADDR,       /* TTES - NA    */
+       INVALID_REG_ADDR,       /* TTECP - NA   */
+       0x08,   /* NAC          */
+       0x0e,   /* FCC          */
+       INVALID_REG_ADDR,       /* CYCT - NA    */
+       INVALID_REG_ADDR,       /* AE - NA      */
+       0x1c,   /* SOC          */
+       0x3c,   /* DCAP         */
+       0x18,   /* AP           */
+};
+
+static u8 *bq27xxx_regs[] = {
+       [BQ27000] = bq27000_regs,
+       [BQ27010] = bq27010_regs,
+       [BQ27500] = bq27500_regs,
+       [BQ27530] = bq27530_regs,
+       [BQ27541] = bq27541_regs,
+       [BQ27545] = bq27545_regs,
+       [BQ27421] = bq27421_regs,
+};
+
+static enum power_supply_property bq27000_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_ENERGY_NOW,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27010_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
+       POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27500_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27530_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27541_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27545_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_CYCLE_COUNT,
+       POWER_SUPPLY_PROP_POWER_AVG,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+static enum power_supply_property bq27421_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_CAPACITY_LEVEL,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+       POWER_SUPPLY_PROP_MANUFACTURER,
+};
+
+#define BQ27XXX_PROP(_id, _prop)               \
+       [_id] = {                               \
+               .props = _prop,                 \
+               .size = ARRAY_SIZE(_prop),      \
+       }
+
+static struct {
+       enum power_supply_property *props;
+       size_t size;
+} bq27xxx_battery_props[] = {
+       BQ27XXX_PROP(BQ27000, bq27000_battery_props),
+       BQ27XXX_PROP(BQ27010, bq27010_battery_props),
+       BQ27XXX_PROP(BQ27500, bq27500_battery_props),
+       BQ27XXX_PROP(BQ27530, bq27530_battery_props),
+       BQ27XXX_PROP(BQ27541, bq27541_battery_props),
+       BQ27XXX_PROP(BQ27545, bq27545_battery_props),
+       BQ27XXX_PROP(BQ27421, bq27421_battery_props),
+};
+
+static unsigned int poll_interval = 360;
+module_param(poll_interval, uint, 0644);
+MODULE_PARM_DESC(poll_interval,
+                "battery poll interval in seconds - 0 disables polling");
+
+/*
+ * Common code for BQ27xxx devices
+ */
+
+static inline int bq27xxx_read(struct bq27xxx_device_info *di, int reg_index,
+                              bool single)
+{
+       /* Reports EINVAL for invalid/missing registers */
+       if (!di || di->regs[reg_index] == INVALID_REG_ADDR)
+               return -EINVAL;
+
+       return di->bus.read(di, di->regs[reg_index], single);
+}
+
+/*
+ * Return the battery State-of-Charge
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_soc(struct bq27xxx_device_info *di)
+{
+       int soc;
+
+       soc = bq27xxx_read(di, BQ27XXX_REG_SOC, false);
+
+       if (soc < 0)
+               dev_dbg(di->dev, "error reading State-of-Charge\n");
+
+       return soc;
+}
+
+/*
+ * Return a battery charge value in µAh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_charge(struct bq27xxx_device_info *di, u8 reg)
+{
+       int charge;
+
+       charge = bq27xxx_read(di, reg, false);
+       if (charge < 0) {
+               dev_dbg(di->dev, "error reading charge register %02x: %d\n",
+                       reg, charge);
+               return charge;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               charge *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       else
+               charge *= 1000;
+
+       return charge;
+}
+
+/*
+ * Return the battery Nominal available capacity in µAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27xxx_battery_read_nac(struct bq27xxx_device_info *di)
+{
+       int flags;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, true);
+               if (flags >= 0 && (flags & BQ27000_FLAG_CI))
+                       return -ENODATA;
+       }
+
+       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_NAC);
+}
+
+/*
+ * Return the battery Full Charge Capacity in µAh
+ * Or < 0 if something fails.
+ */
+static inline int bq27xxx_battery_read_fcc(struct bq27xxx_device_info *di)
+{
+       return bq27xxx_battery_read_charge(di, BQ27XXX_REG_FCC);
+}
+
+/*
+ * Return the Design Capacity in µAh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_dcap(struct bq27xxx_device_info *di)
+{
+       int dcap;
+
+       dcap = bq27xxx_read(di, BQ27XXX_REG_DCAP, false);
+
+       if (dcap < 0) {
+               dev_dbg(di->dev, "error reading initial last measured discharge\n");
+               return dcap;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               dcap *= BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       else
+               dcap *= 1000;
+
+       return dcap;
+}
+
+/*
+ * Return the battery Available energy in µWh
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_energy(struct bq27xxx_device_info *di)
+{
+       int ae;
+
+       ae = bq27xxx_read(di, BQ27XXX_REG_AE, false);
+       if (ae < 0) {
+               dev_dbg(di->dev, "error reading available energy\n");
+               return ae;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               ae *= BQ27XXX_POWER_CONSTANT / BQ27XXX_RS;
+       else
+               ae *= 1000;
+
+       return ae;
+}
+
+/*
+ * Return the battery temperature in tenths of degree Kelvin
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_temperature(struct bq27xxx_device_info *di)
+{
+       int temp;
+
+       temp = bq27xxx_read(di, BQ27XXX_REG_TEMP, false);
+       if (temp < 0) {
+               dev_err(di->dev, "error reading temperature\n");
+               return temp;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               temp = 5 * temp / 2;
+
+       return temp;
+}
+
+/*
+ * Return the battery Cycle count total
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_read_cyct(struct bq27xxx_device_info *di)
+{
+       int cyct;
+
+       cyct = bq27xxx_read(di, BQ27XXX_REG_CYCT, false);
+       if (cyct < 0)
+               dev_err(di->dev, "error reading cycle count total\n");
+
+       return cyct;
+}
+
+/*
+ * Read a time register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_time(struct bq27xxx_device_info *di, u8 reg)
+{
+       int tval;
+
+       tval = bq27xxx_read(di, reg, false);
+       if (tval < 0) {
+               dev_dbg(di->dev, "error reading time register %02x: %d\n",
+                       reg, tval);
+               return tval;
+       }
+
+       if (tval == 65535)
+               return -ENODATA;
+
+       return tval * 60;
+}
+
+/*
+ * Read an average power register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_pwr_avg(struct bq27xxx_device_info *di)
+{
+       int tval;
+
+       tval = bq27xxx_read(di, BQ27XXX_REG_AP, false);
+       if (tval < 0) {
+               dev_err(di->dev, "error reading average power register  %02x: %d\n",
+                       BQ27XXX_REG_AP, tval);
+               return tval;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               return (tval * BQ27XXX_POWER_CONSTANT) / BQ27XXX_RS;
+       else
+               return tval;
+}
+
+/*
+ * Returns true if a battery over temperature condition is detected
+ */
+static bool bq27xxx_battery_overtemp(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27500 || di->chip == BQ27541 || di->chip == BQ27545)
+               return flags & (BQ27XXX_FLAG_OTC | BQ27XXX_FLAG_OTD);
+       if (di->chip == BQ27530 || di->chip == BQ27421)
+               return flags & BQ27XXX_FLAG_OT;
+
+       return false;
+}
+
+/*
+ * Returns true if a battery under temperature condition is detected
+ */
+static bool bq27xxx_battery_undertemp(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27530 || di->chip == BQ27421)
+               return flags & BQ27XXX_FLAG_UT;
+
+       return false;
+}
+
+/*
+ * Returns true if a low state of charge condition is detected
+ */
+static bool bq27xxx_battery_dead(struct bq27xxx_device_info *di, u16 flags)
+{
+       if (di->chip == BQ27000 || di->chip == BQ27010)
+               return flags & (BQ27000_FLAG_EDV1 | BQ27000_FLAG_EDVF);
+       else
+               return flags & (BQ27XXX_FLAG_SOC1 | BQ27XXX_FLAG_SOCF);
+}
+
+/*
+ * Read flag register.
+ * Return < 0 if something fails.
+ */
+static int bq27xxx_battery_read_health(struct bq27xxx_device_info *di)
+{
+       u16 flags;
+
+       flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+       if (flags < 0) {
+               dev_err(di->dev, "error reading flag register:%d\n", flags);
+               return flags;
+       }
+
+       /* Unlikely but important to return first */
+       if (unlikely(bq27xxx_battery_overtemp(di, flags)))
+               return POWER_SUPPLY_HEALTH_OVERHEAT;
+       if (unlikely(bq27xxx_battery_undertemp(di, flags)))
+               return POWER_SUPPLY_HEALTH_COLD;
+       if (unlikely(bq27xxx_battery_dead(di, flags)))
+               return POWER_SUPPLY_HEALTH_DEAD;
+
+       return POWER_SUPPLY_HEALTH_GOOD;
+}
+
+static void bq27xxx_battery_update(struct bq27xxx_device_info *di)
+{
+       struct bq27xxx_reg_cache cache = {0, };
+       bool has_ci_flag = di->chip == BQ27000 || di->chip == BQ27010;
+       bool has_singe_flag = di->chip == BQ27000 || di->chip == BQ27010;
+
+       cache.flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, has_singe_flag);
+       if ((cache.flags & 0xff) == 0xff)
+               cache.flags = -1; /* read error */
+       if (cache.flags >= 0) {
+               cache.temperature = bq27xxx_battery_read_temperature(di);
+               if (has_ci_flag && (cache.flags & BQ27000_FLAG_CI)) {
+                       dev_info(di->dev, "battery is not calibrated! ignoring capacity values\n");
+                       cache.capacity = -ENODATA;
+                       cache.energy = -ENODATA;
+                       cache.time_to_empty = -ENODATA;
+                       cache.time_to_empty_avg = -ENODATA;
+                       cache.time_to_full = -ENODATA;
+                       cache.charge_full = -ENODATA;
+                       cache.health = -ENODATA;
+               } else {
+                       if (di->regs[BQ27XXX_REG_TTE] != INVALID_REG_ADDR)
+                               cache.time_to_empty = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTE);
+                       if (di->regs[BQ27XXX_REG_TTECP] != INVALID_REG_ADDR)
+                               cache.time_to_empty_avg = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTECP);
+                       if (di->regs[BQ27XXX_REG_TTF] != INVALID_REG_ADDR)
+                               cache.time_to_full = bq27xxx_battery_read_time(di, BQ27XXX_REG_TTF);
+                       cache.charge_full = bq27xxx_battery_read_fcc(di);
+                       cache.capacity = bq27xxx_battery_read_soc(di);
+                       if (di->regs[BQ27XXX_REG_AE] != INVALID_REG_ADDR)
+                               cache.energy = bq27xxx_battery_read_energy(di);
+                       cache.health = bq27xxx_battery_read_health(di);
+               }
+               if (di->regs[BQ27XXX_REG_CYCT] != INVALID_REG_ADDR)
+                       cache.cycle_count = bq27xxx_battery_read_cyct(di);
+               if (di->regs[BQ27XXX_REG_AP] != INVALID_REG_ADDR)
+                       cache.power_avg = bq27xxx_battery_read_pwr_avg(di);
+
+               /* We only have to read charge design full once */
+               if (di->charge_design_full <= 0)
+                       di->charge_design_full = bq27xxx_battery_read_dcap(di);
+       }
+
+       if (di->cache.capacity != cache.capacity)
+               power_supply_changed(di->bat);
+
+       if (memcmp(&di->cache, &cache, sizeof(cache)) != 0)
+               di->cache = cache;
+
+       di->last_update = jiffies;
+}
+
+static irqreturn_t bq27xxx_battery_irq_handler_thread(int irq, void *data)
+{
+       struct bq27xxx_device_info *di = data;
+
+       bq27xxx_battery_update(di);
+
+       return IRQ_HANDLED;
+}
+
+static void bq27xxx_battery_poll(struct work_struct *work)
+{
+       struct bq27xxx_device_info *di =
+                       container_of(work, struct bq27xxx_device_info,
+                                    work.work);
+
+       bq27xxx_battery_update(di);
+
+       if (poll_interval > 0) {
+               /* The timer does not have to be accurate. */
+               set_timer_slack(&di->work.timer, poll_interval * HZ / 4);
+               schedule_delayed_work(&di->work, poll_interval * HZ);
+       }
+}
+
+/*
+ * Return the battery average current in µA
+ * Note that current can be negative signed as well
+ * Or 0 if something fails.
+ */
+static int bq27xxx_battery_current(struct bq27xxx_device_info *di,
+                                  union power_supply_propval *val)
+{
+       int curr;
+       int flags;
+
+       curr = bq27xxx_read(di, BQ27XXX_REG_AI, false);
+       if (curr < 0) {
+               dev_err(di->dev, "error reading current\n");
+               return curr;
+       }
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               flags = bq27xxx_read(di, BQ27XXX_REG_FLAGS, false);
+               if (flags & BQ27000_FLAG_CHGS) {
+                       dev_dbg(di->dev, "negative current!\n");
+                       curr = -curr;
+               }
+
+               val->intval = curr * BQ27XXX_CURRENT_CONSTANT / BQ27XXX_RS;
+       } else {
+               /* Other gauges return signed value */
+               val->intval = (int)((s16)curr) * 1000;
+       }
+
+       return 0;
+}
+
+static int bq27xxx_battery_status(struct bq27xxx_device_info *di,
+                                 union power_supply_propval *val)
+{
+       int status;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               if (di->cache.flags & BQ27000_FLAG_FC)
+                       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))
+                       status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+       } else {
+               if (di->cache.flags & BQ27XXX_FLAG_FC)
+                       status = POWER_SUPPLY_STATUS_FULL;
+               else if (di->cache.flags & BQ27XXX_FLAG_DSC)
+                       status = POWER_SUPPLY_STATUS_DISCHARGING;
+               else
+                       status = POWER_SUPPLY_STATUS_CHARGING;
+       }
+
+       val->intval = status;
+
+       return 0;
+}
+
+static int bq27xxx_battery_capacity_level(struct bq27xxx_device_info *di,
+                                         union power_supply_propval *val)
+{
+       int level;
+
+       if (di->chip == BQ27000 || di->chip == BQ27010) {
+               if (di->cache.flags & BQ27000_FLAG_FC)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else if (di->cache.flags & BQ27000_FLAG_EDV1)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (di->cache.flags & BQ27000_FLAG_EDVF)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       } else {
+               if (di->cache.flags & BQ27XXX_FLAG_FC)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
+               else if (di->cache.flags & BQ27XXX_FLAG_SOC1)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
+               else if (di->cache.flags & BQ27XXX_FLAG_SOCF)
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
+               else
+                       level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
+       }
+
+       val->intval = level;
+
+       return 0;
+}
+
+/*
+ * Return the battery Voltage in millivolts
+ * Or < 0 if something fails.
+ */
+static int bq27xxx_battery_voltage(struct bq27xxx_device_info *di,
+                                  union power_supply_propval *val)
+{
+       int volt;
+
+       volt = bq27xxx_read(di, BQ27XXX_REG_VOLT, false);
+       if (volt < 0) {
+               dev_err(di->dev, "error reading voltage\n");
+               return volt;
+       }
+
+       val->intval = volt * 1000;
+
+       return 0;
+}
+
+static int bq27xxx_simple_value(int value,
+                               union power_supply_propval *val)
+{
+       if (value < 0)
+               return value;
+
+       val->intval = value;
+
+       return 0;
+}
+
+static int bq27xxx_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       int ret = 0;
+       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
+
+       mutex_lock(&di->lock);
+       if (time_is_before_jiffies(di->last_update + 5 * HZ)) {
+               cancel_delayed_work_sync(&di->work);
+               bq27xxx_battery_poll(&di->work.work);
+       }
+       mutex_unlock(&di->lock);
+
+       if (psp != POWER_SUPPLY_PROP_PRESENT && di->cache.flags < 0)
+               return -ENODEV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = bq27xxx_battery_status(di, val);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = bq27xxx_battery_voltage(di, val);
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = di->cache.flags < 0 ? 0 : 1;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = bq27xxx_battery_current(di, val);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = bq27xxx_simple_value(di->cache.capacity, val);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
+               ret = bq27xxx_battery_capacity_level(di, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               ret = bq27xxx_simple_value(di->cache.temperature, val);
+               if (ret == 0)
+                       val->intval -= 2731; /* convert decidegree k to c */
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
+               ret = bq27xxx_simple_value(di->cache.time_to_empty, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
+               ret = bq27xxx_simple_value(di->cache.time_to_empty_avg, val);
+               break;
+       case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
+               ret = bq27xxx_simple_value(di->cache.time_to_full, val);
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               ret = bq27xxx_simple_value(bq27xxx_battery_read_nac(di), val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               ret = bq27xxx_simple_value(di->cache.charge_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               ret = bq27xxx_simple_value(di->charge_design_full, val);
+               break;
+       case POWER_SUPPLY_PROP_CYCLE_COUNT:
+               ret = bq27xxx_simple_value(di->cache.cycle_count, val);
+               break;
+       case POWER_SUPPLY_PROP_ENERGY_NOW:
+               ret = bq27xxx_simple_value(di->cache.energy, val);
+               break;
+       case POWER_SUPPLY_PROP_POWER_AVG:
+               ret = bq27xxx_simple_value(di->cache.power_avg, val);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = bq27xxx_simple_value(di->cache.health, val);
+               break;
+       case POWER_SUPPLY_PROP_MANUFACTURER:
+               val->strval = BQ27XXX_MANUFACTURER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return ret;
+}
+
+static void bq27xxx_external_power_changed(struct power_supply *psy)
+{
+       struct bq27xxx_device_info *di = power_supply_get_drvdata(psy);
+
+       cancel_delayed_work_sync(&di->work);
+       schedule_delayed_work(&di->work, 0);
+}
+
+static int bq27xxx_powersupply_init(struct bq27xxx_device_info *di,
+                                   const char *name)
+{
+       int ret;
+       struct power_supply_desc *psy_desc;
+       struct power_supply_config psy_cfg = { .drv_data = di, };
+
+       psy_desc = devm_kzalloc(di->dev, sizeof(*psy_desc), GFP_KERNEL);
+       if (!psy_desc)
+               return -ENOMEM;
+
+       psy_desc->name = name;
+       psy_desc->type = POWER_SUPPLY_TYPE_BATTERY;
+       psy_desc->properties = bq27xxx_battery_props[di->chip].props;
+       psy_desc->num_properties = bq27xxx_battery_props[di->chip].size;
+       psy_desc->get_property = bq27xxx_battery_get_property;
+       psy_desc->external_power_changed = bq27xxx_external_power_changed;
+
+       INIT_DELAYED_WORK(&di->work, bq27xxx_battery_poll);
+       mutex_init(&di->lock);
+
+       di->bat = power_supply_register_no_ws(di->dev, psy_desc, &psy_cfg);
+       if (IS_ERR(di->bat)) {
+               ret = PTR_ERR(di->bat);
+               dev_err(di->dev, "failed to register battery: %d\n", ret);
+               return ret;
+       }
+
+       dev_info(di->dev, "support ver. %s enabled\n", DRIVER_VERSION);
+
+       bq27xxx_battery_update(di);
+
+       return 0;
+}
+
+static void bq27xxx_powersupply_unregister(struct bq27xxx_device_info *di)
+{
+       /*
+        * power_supply_unregister call bq27xxx_battery_get_property which
+        * call bq27xxx_battery_poll.
+        * Make sure that bq27xxx_battery_poll will not call
+        * schedule_delayed_work again after unregister (which cause OOPS).
+        */
+       poll_interval = 0;
+
+       cancel_delayed_work_sync(&di->work);
+
+       power_supply_unregister(di->bat);
+
+       mutex_destroy(&di->lock);
+}
+
+/* i2c specific code */
+#ifdef CONFIG_BATTERY_BQ27XXX_I2C
+
+/* If the system has several batteries we need a different name for each
+ * of them...
+ */
+static DEFINE_IDR(battery_id);
+static DEFINE_MUTEX(battery_mutex);
+
+static int bq27xxx_battery_i2c_read(struct bq27xxx_device_info *di, u8 reg,
+                                   bool single)
+{
+       struct i2c_client *client = to_i2c_client(di->dev);
+       struct i2c_msg msg[2];
+       unsigned char data[2];
+       int ret;
+
+       if (!client->adapter)
+               return -ENODEV;
+
+       msg[0].addr = client->addr;
+       msg[0].flags = 0;
+       msg[0].buf = &reg;
+       msg[0].len = sizeof(reg);
+       msg[1].addr = client->addr;
+       msg[1].flags = I2C_M_RD;
+       msg[1].buf = data;
+       if (single)
+               msg[1].len = 1;
+       else
+               msg[1].len = 2;
+
+       ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+       if (ret < 0)
+               return ret;
+
+       if (!single)
+               ret = get_unaligned_le16(data);
+       else
+               ret = data[0];
+
+       return ret;
+}
+
+static int bq27xxx_battery_i2c_probe(struct i2c_client *client,
+                                    const struct i2c_device_id *id)
+{
+       char *name;
+       struct bq27xxx_device_info *di;
+       int num;
+       int retval = 0;
+
+       /* Get new ID for the new battery device */
+       mutex_lock(&battery_mutex);
+       num = idr_alloc(&battery_id, client, 0, 0, GFP_KERNEL);
+       mutex_unlock(&battery_mutex);
+       if (num < 0)
+               return num;
+
+       name = devm_kasprintf(&client->dev, GFP_KERNEL, "%s-%d", id->name, num);
+       if (!name) {
+               retval = -ENOMEM;
+               goto batt_failed;
+       }
+
+       di = devm_kzalloc(&client->dev, sizeof(*di), GFP_KERNEL);
+       if (!di) {
+               retval = -ENOMEM;
+               goto batt_failed;
+       }
+
+       di->id = num;
+       di->dev = &client->dev;
+       di->chip = id->driver_data;
+       di->bus.read = &bq27xxx_battery_i2c_read;
+       di->regs = bq27xxx_regs[di->chip];
+
+       retval = bq27xxx_powersupply_init(di, name);
+       if (retval)
+               goto batt_failed;
+
+       /* Schedule a polling after about 1 min */
+       schedule_delayed_work(&di->work, 60 * HZ);
+
+       i2c_set_clientdata(client, di);
+
+       if (client->irq) {
+               retval = devm_request_threaded_irq(&client->dev, client->irq,
+                               NULL, bq27xxx_battery_irq_handler_thread,
+                               IRQF_ONESHOT,
+                               name, di);
+               if (retval) {
+                       dev_err(&client->dev,
+                               "Unable to register IRQ %d error %d\n",
+                               client->irq, retval);
+                       return retval;
+               }
+       }
+
+       return 0;
+
+batt_failed:
+       mutex_lock(&battery_mutex);
+       idr_remove(&battery_id, num);
+       mutex_unlock(&battery_mutex);
+
+       return retval;
+}
+
+static int bq27xxx_battery_i2c_remove(struct i2c_client *client)
+{
+       struct bq27xxx_device_info *di = i2c_get_clientdata(client);
+
+       bq27xxx_powersupply_unregister(di);
+
+       mutex_lock(&battery_mutex);
+       idr_remove(&battery_id, di->id);
+       mutex_unlock(&battery_mutex);
+
+       return 0;
+}
+
+static const struct i2c_device_id bq27xxx_id[] = {
+       { "bq27200", BQ27000 },
+       { "bq27210", BQ27010 },
+       { "bq27500", BQ27500 },
+       { "bq27510", BQ27500 },
+       { "bq27520", BQ27500 },
+       { "bq27530", BQ27530 },
+       { "bq27531", BQ27530 },
+       { "bq27541", BQ27541 },
+       { "bq27542", BQ27541 },
+       { "bq27546", BQ27541 },
+       { "bq27742", BQ27541 },
+       { "bq27545", BQ27545 },
+       { "bq27421", BQ27421 },
+       { "bq27425", BQ27421 },
+       { "bq27441", BQ27421 },
+       { "bq27621", BQ27421 },
+       {},
+};
+MODULE_DEVICE_TABLE(i2c, bq27xxx_id);
+
+static struct i2c_driver bq27xxx_battery_i2c_driver = {
+       .driver = {
+               .name = "bq27xxx-battery",
+       },
+       .probe = bq27xxx_battery_i2c_probe,
+       .remove = bq27xxx_battery_i2c_remove,
+       .id_table = bq27xxx_id,
+};
+
+static inline int bq27xxx_battery_i2c_init(void)
+{
+       int ret = i2c_add_driver(&bq27xxx_battery_i2c_driver);
+
+       if (ret)
+               pr_err("Unable to register BQ27xxx i2c driver\n");
+
+       return ret;
+}
+
+static inline void bq27xxx_battery_i2c_exit(void)
+{
+       i2c_del_driver(&bq27xxx_battery_i2c_driver);
+}
+
+#else
+
+static inline int bq27xxx_battery_i2c_init(void) { return 0; }
+static inline void bq27xxx_battery_i2c_exit(void) {};
+
+#endif
+
+/* platform specific code */
+#ifdef CONFIG_BATTERY_BQ27XXX_PLATFORM
+
+static int bq27xxx_battery_platform_read(struct bq27xxx_device_info *di, u8 reg,
+                                        bool single)
+{
+       struct device *dev = di->dev;
+       struct bq27xxx_platform_data *pdata = dev->platform_data;
+       unsigned int timeout = 3;
+       int upper, lower;
+       int temp;
+
+       if (!single) {
+               /* Make sure the value has not changed in between reading the
+                * lower and the upper part */
+               upper = pdata->read(dev, reg + 1);
+               do {
+                       temp = upper;
+                       if (upper < 0)
+                               return upper;
+
+                       lower = pdata->read(dev, reg);
+                       if (lower < 0)
+                               return lower;
+
+                       upper = pdata->read(dev, reg + 1);
+               } while (temp != upper && --timeout);
+
+               if (timeout == 0)
+                       return -EIO;
+
+               return (upper << 8) | lower;
+       }
+
+       return pdata->read(dev, reg);
+}
+
+static int bq27xxx_battery_platform_probe(struct platform_device *pdev)
+{
+       struct bq27xxx_device_info *di;
+       struct bq27xxx_platform_data *pdata = pdev->dev.platform_data;
+       const char *name;
+
+       if (!pdata) {
+               dev_err(&pdev->dev, "no platform_data supplied\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->read) {
+               dev_err(&pdev->dev, "no hdq read callback supplied\n");
+               return -EINVAL;
+       }
+
+       if (!pdata->chip) {
+               dev_err(&pdev->dev, "no device supplied\n");
+               return -EINVAL;
+       }
+
+       di = devm_kzalloc(&pdev->dev, sizeof(*di), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, di);
+
+       di->dev = &pdev->dev;
+       di->chip = pdata->chip;
+
+       name = pdata->name ?: dev_name(&pdev->dev);
+       di->bus.read = &bq27xxx_battery_platform_read;
+
+       return bq27xxx_powersupply_init(di, name);
+}
+
+static int bq27xxx_battery_platform_remove(struct platform_device *pdev)
+{
+       struct bq27xxx_device_info *di = platform_get_drvdata(pdev);
+
+       bq27xxx_powersupply_unregister(di);
+
+       return 0;
+}
+
+static struct platform_driver bq27xxx_battery_platform_driver = {
+       .probe  = bq27xxx_battery_platform_probe,
+       .remove = bq27xxx_battery_platform_remove,
+       .driver = {
+               .name = "bq27000-battery",
+       },
+};
+
+static inline int bq27xxx_battery_platform_init(void)
+{
+       int ret = platform_driver_register(&bq27xxx_battery_platform_driver);
+
+       if (ret)
+               pr_err("Unable to register BQ27xxx platform driver\n");
+
+       return ret;
+}
+
+static inline void bq27xxx_battery_platform_exit(void)
+{
+       platform_driver_unregister(&bq27xxx_battery_platform_driver);
+}
+
+#else
+
+static inline int bq27xxx_battery_platform_init(void) { return 0; }
+static inline void bq27xxx_battery_platform_exit(void) {};
+
+#endif
+
+/*
+ * Module stuff
+ */
+
+static int __init bq27xxx_battery_init(void)
+{
+       int ret;
+
+       ret = bq27xxx_battery_i2c_init();
+       if (ret)
+               return ret;
+
+       ret = bq27xxx_battery_platform_init();
+       if (ret)
+               bq27xxx_battery_i2c_exit();
+
+       return ret;
+}
+module_init(bq27xxx_battery_init);
+
+static void __exit bq27xxx_battery_exit(void)
+{
+       bq27xxx_battery_platform_exit();
+       bq27xxx_battery_i2c_exit();
+}
+module_exit(bq27xxx_battery_exit);
+
+#ifdef CONFIG_BATTERY_BQ27XXX_PLATFORM
+MODULE_ALIAS("platform:bq27000-battery");
+#endif
+
+MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
+MODULE_DESCRIPTION("BQ27xxx battery monitor driver");
+MODULE_LICENSE("GPL");
index 7e741f1d3cd5e392d8a13445daefc2688d861e7d..042fb3dacb461d460817093f61b6b69dc6b62a1d 100644 (file)
@@ -508,23 +508,23 @@ out:
        return param;
 }
 
-static int lp8727_parse_dt(struct device *dev)
+static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
 {
        struct device_node *np = dev->of_node;
        struct device_node *child;
        struct lp8727_platform_data *pdata;
        const char *type;
 
-       /* If charging parameter is not defined, just skip parsing the dt */
-       if (of_get_child_count(np) == 0)
-               goto out;
-
        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
        if (!pdata)
-               return -ENOMEM;
+               return ERR_PTR(-ENOMEM);
 
        of_property_read_u32(np, "debounce-ms", &pdata->debounce_msec);
 
+       /* If charging parameter is not defined, just skip parsing the dt */
+       if (of_get_child_count(np) == 0)
+               return pdata;
+
        for_each_child_of_node(np, child) {
                of_property_read_string(child, "charger-type", &type);
 
@@ -535,29 +535,30 @@ static int lp8727_parse_dt(struct device *dev)
                        pdata->usb = lp8727_parse_charge_pdata(dev, child);
        }
 
-       dev->platform_data = pdata;
-out:
-       return 0;
+       return pdata;
 }
 #else
-static int lp8727_parse_dt(struct device *dev)
+static struct lp8727_platform_data *lp8727_parse_dt(struct device *dev)
 {
-       return 0;
+       return NULL;
 }
 #endif
 
 static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 {
        struct lp8727_chg *pchg;
+       struct lp8727_platform_data *pdata;
        int ret;
 
        if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
                return -EIO;
 
        if (cl->dev.of_node) {
-               ret = lp8727_parse_dt(&cl->dev);
-               if (ret)
-                       return ret;
+               pdata = lp8727_parse_dt(&cl->dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       } else {
+               pdata = dev_get_platdata(&cl->dev);
        }
 
        pchg = devm_kzalloc(&cl->dev, sizeof(*pchg), GFP_KERNEL);
@@ -566,7 +567,7 @@ static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 
        pchg->client = cl;
        pchg->dev = &cl->dev;
-       pchg->pdata = cl->dev.platform_data;
+       pchg->pdata = pdata;
        i2c_set_clientdata(cl, pchg);
 
        mutex_init(&pchg->xfer_lock);
index e8925576474572c37da8b82d634d59b1dba85b15..9c65f134d4474d843ffa907051191e2d8c484a24 100644 (file)
@@ -909,18 +909,21 @@ static int max17042_probe(struct i2c_client *client,
                regmap_write(chip->regmap, MAX17042_LearnCFG, 0x0007);
        }
 
-       chip->battery = power_supply_register(&client->dev, max17042_desc,
-                                               &psy_cfg);
+       chip->battery = devm_power_supply_register(&client->dev, max17042_desc,
+                                                  &psy_cfg);
        if (IS_ERR(chip->battery)) {
                dev_err(&client->dev, "failed: power supply register\n");
                return PTR_ERR(chip->battery);
        }
 
        if (client->irq) {
-               ret = request_threaded_irq(client->irq, NULL,
-                                       max17042_thread_handler,
-                                       IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                       chip->battery->desc->name, chip);
+               ret = devm_request_threaded_irq(&client->dev, client->irq,
+                                               NULL,
+                                               max17042_thread_handler,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_ONESHOT,
+                                               chip->battery->desc->name,
+                                               chip);
                if (!ret) {
                        regmap_update_bits(chip->regmap, MAX17042_CONFIG,
                                        CONFIG_ALRT_BIT_ENBL,
@@ -944,16 +947,6 @@ static int max17042_probe(struct i2c_client *client,
        return 0;
 }
 
-static int max17042_remove(struct i2c_client *client)
-{
-       struct max17042_chip *chip = i2c_get_clientdata(client);
-
-       if (client->irq)
-               free_irq(client->irq, chip);
-       power_supply_unregister(chip->battery);
-       return 0;
-}
-
 #ifdef CONFIG_PM_SLEEP
 static int max17042_suspend(struct device *dev)
 {
@@ -1014,7 +1007,6 @@ static struct i2c_driver max17042_i2c_driver = {
                .pm     = &max17042_pm_ops,
        },
        .probe          = max17042_probe,
-       .remove         = max17042_remove,
        .id_table       = max17042_id,
 };
 module_i2c_driver(max17042_i2c_driver);
index bf2b4b3a7caef2db27317dc6e919f87ae3fdd72d..6d39d52040d4ab982f01066c47239ff295323427 100644 (file)
@@ -201,8 +201,7 @@ static int max8903_probe(struct platform_device *pdev)
 
        if (pdata->dc_valid == false && pdata->usb_valid == false) {
                dev_err(dev, "No valid power sources.\n");
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        if (pdata->dc_valid) {
@@ -216,8 +215,7 @@ static int max8903_probe(struct platform_device *pdev)
                } else {
                        dev_err(dev, "When DC is wired, DOK and DCM should"
                                        " be wired as well.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        } else {
                if (pdata->dcm) {
@@ -225,8 +223,7 @@ static int max8903_probe(struct platform_device *pdev)
                                gpio_set_value(pdata->dcm, 0);
                        else {
                                dev_err(dev, "Invalid pin: dcm.\n");
-                               ret = -EINVAL;
-                               goto err;
+                               return -EINVAL;
                        }
                }
        }
@@ -238,8 +235,7 @@ static int max8903_probe(struct platform_device *pdev)
                } else {
                        dev_err(dev, "When USB is wired, UOK should be wired."
                                        "as well.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        }
 
@@ -248,32 +244,28 @@ static int max8903_probe(struct platform_device *pdev)
                        gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1);
                } else {
                        dev_err(dev, "Invalid pin: cen.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        }
 
        if (pdata->chg) {
                if (!gpio_is_valid(pdata->chg)) {
                        dev_err(dev, "Invalid pin: chg.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        }
 
        if (pdata->flt) {
                if (!gpio_is_valid(pdata->flt)) {
                        dev_err(dev, "Invalid pin: flt.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        }
 
        if (pdata->usus) {
                if (!gpio_is_valid(pdata->usus)) {
                        dev_err(dev, "Invalid pin: usus.\n");
-                       ret = -EINVAL;
-                       goto err;
+                       return -EINVAL;
                }
        }
 
@@ -291,85 +283,56 @@ static int max8903_probe(struct platform_device *pdev)
 
        psy_cfg.drv_data = data;
 
-       data->psy = power_supply_register(dev, &data->psy_desc, &psy_cfg);
+       data->psy = devm_power_supply_register(dev, &data->psy_desc, &psy_cfg);
        if (IS_ERR(data->psy)) {
                dev_err(dev, "failed: power supply register.\n");
-               ret = PTR_ERR(data->psy);
-               goto err;
+               return PTR_ERR(data->psy);
        }
 
        if (pdata->dc_valid) {
-               ret = request_threaded_irq(gpio_to_irq(pdata->dok),
-                               NULL, max8903_dcin,
-                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                               "MAX8903 DC IN", data);
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->dok),
+                                               NULL, max8903_dcin,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_TRIGGER_RISING,
+                                               "MAX8903 DC IN", data);
                if (ret) {
                        dev_err(dev, "Cannot request irq %d for DC (%d)\n",
                                        gpio_to_irq(pdata->dok), ret);
-                       goto err_psy;
+                       return ret;
                }
        }
 
        if (pdata->usb_valid) {
-               ret = request_threaded_irq(gpio_to_irq(pdata->uok),
-                               NULL, max8903_usbin,
-                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                               "MAX8903 USB IN", data);
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->uok),
+                                               NULL, max8903_usbin,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_TRIGGER_RISING,
+                                               "MAX8903 USB IN", data);
                if (ret) {
                        dev_err(dev, "Cannot request irq %d for USB (%d)\n",
                                        gpio_to_irq(pdata->uok), ret);
-                       goto err_dc_irq;
+                       return ret;
                }
        }
 
        if (pdata->flt) {
-               ret = request_threaded_irq(gpio_to_irq(pdata->flt),
-                               NULL, max8903_fault,
-                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
-                               "MAX8903 Fault", data);
+               ret = devm_request_threaded_irq(dev, gpio_to_irq(pdata->flt),
+                                               NULL, max8903_fault,
+                                               IRQF_TRIGGER_FALLING |
+                                               IRQF_TRIGGER_RISING,
+                                               "MAX8903 Fault", data);
                if (ret) {
                        dev_err(dev, "Cannot request irq %d for Fault (%d)\n",
                                        gpio_to_irq(pdata->flt), ret);
-                       goto err_usb_irq;
+                       return ret;
                }
        }
 
-       return 0;
-
-err_usb_irq:
-       if (pdata->usb_valid)
-               free_irq(gpio_to_irq(pdata->uok), data);
-err_dc_irq:
-       if (pdata->dc_valid)
-               free_irq(gpio_to_irq(pdata->dok), data);
-err_psy:
-       power_supply_unregister(data->psy);
-err:
-       return ret;
-}
-
-static int max8903_remove(struct platform_device *pdev)
-{
-       struct max8903_data *data = platform_get_drvdata(pdev);
-
-       if (data) {
-               struct max8903_pdata *pdata = &data->pdata;
-
-               if (pdata->flt)
-                       free_irq(gpio_to_irq(pdata->flt), data);
-               if (pdata->usb_valid)
-                       free_irq(gpio_to_irq(pdata->uok), data);
-               if (pdata->dc_valid)
-                       free_irq(gpio_to_irq(pdata->dok), data);
-               power_supply_unregister(data->psy);
-       }
-
        return 0;
 }
 
 static struct platform_driver max8903_driver = {
        .probe  = max8903_probe,
-       .remove = max8903_remove,
        .driver = {
                .name   = "max8903-charger",
        },
index 47448d4bc6cd3a1fccba042ed1588564b2c1bcf1..b64cf0f1414254336e68c5e07906f420258ab6cd 100644 (file)
@@ -117,8 +117,7 @@ static int max8998_battery_probe(struct platform_device *pdev)
                        "EOC value not set: leave it unchanged.\n");
        } else {
                dev_err(max8998->dev, "Invalid EOC value\n");
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        /* Setup Charge Restart Level */
@@ -141,8 +140,7 @@ static int max8998_battery_probe(struct platform_device *pdev)
                break;
        default:
                dev_err(max8998->dev, "Invalid Restart Level\n");
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        /* Setup Charge Full Timeout */
@@ -165,33 +163,21 @@ static int max8998_battery_probe(struct platform_device *pdev)
                break;
        default:
                dev_err(max8998->dev, "Invalid Full Timeout value\n");
-               ret = -EINVAL;
-               goto err;
+               return -EINVAL;
        }
 
        psy_cfg.drv_data = max8998;
 
-       max8998->battery = power_supply_register(max8998->dev,
-                                                &max8998_battery_desc,
-                                                &psy_cfg);
+       max8998->battery = devm_power_supply_register(max8998->dev,
+                                                     &max8998_battery_desc,
+                                                     &psy_cfg);
        if (IS_ERR(max8998->battery)) {
                ret = PTR_ERR(max8998->battery);
                dev_err(max8998->dev, "failed: power supply register: %d\n",
                        ret);
-               goto err;
+               return ret;
        }
 
-       return 0;
-err:
-       return ret;
-}
-
-static int max8998_battery_remove(struct platform_device *pdev)
-{
-       struct max8998_battery_data *max8998 = platform_get_drvdata(pdev);
-
-       power_supply_unregister(max8998->battery);
-
        return 0;
 }
 
@@ -205,7 +191,6 @@ static struct platform_driver max8998_battery_driver = {
                .name = "max8998-battery",
        },
        .probe = max8998_battery_probe,
-       .remove = max8998_battery_remove,
        .id_table = max8998_battery_id,
 };
 
index 3a45cc0c4dceb13e46e7ea9a92d8c1a3f10ac2f9..8f9bd1d0eeb62ff18c69cda8878a671d6d0ab180 100644 (file)
@@ -1264,5 +1264,4 @@ module_exit(pm2xxx_charger_exit);
 
 MODULE_LICENSE("GPL v2");
 MODULE_AUTHOR("Rajkumar kasirajan, Olivier Launay");
-MODULE_ALIAS("i2c:pm2xxx-charger");
 MODULE_DESCRIPTION("PM2xxx charger management driver");
diff --git a/drivers/power/qcom_smbb.c b/drivers/power/qcom_smbb.c
new file mode 100644 (file)
index 0000000..5eb1e9e
--- /dev/null
@@ -0,0 +1,951 @@
+/* Copyright (c) 2014, Sony Mobile Communications Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * This driver is for the multi-block Switch-Mode Battery Charger and Boost
+ * (SMBB) hardware, found in Qualcomm PM8941 PMICs.  The charger is an
+ * integrated, single-cell lithium-ion battery charger.
+ *
+ * Sub-components:
+ *  - Charger core
+ *  - Buck
+ *  - DC charge-path
+ *  - USB charge-path
+ *  - Battery interface
+ *  - Boost (not implemented)
+ *  - Misc
+ *  - HF-Buck
+ */
+
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define SMBB_CHG_VMAX          0x040
+#define SMBB_CHG_VSAFE         0x041
+#define SMBB_CHG_CFG           0x043
+#define SMBB_CHG_IMAX          0x044
+#define SMBB_CHG_ISAFE         0x045
+#define SMBB_CHG_VIN_MIN       0x047
+#define SMBB_CHG_CTRL          0x049
+#define CTRL_EN                        BIT(7)
+#define SMBB_CHG_VBAT_WEAK     0x052
+#define SMBB_CHG_IBAT_TERM_CHG 0x05b
+#define IBAT_TERM_CHG_IEOC     BIT(7)
+#define IBAT_TERM_CHG_IEOC_BMS BIT(7)
+#define IBAT_TERM_CHG_IEOC_CHG 0
+#define SMBB_CHG_VBAT_DET      0x05d
+#define SMBB_CHG_TCHG_MAX_EN   0x060
+#define TCHG_MAX_EN            BIT(7)
+#define SMBB_CHG_WDOG_TIME     0x062
+#define SMBB_CHG_WDOG_EN       0x065
+#define WDOG_EN                        BIT(7)
+
+#define SMBB_BUCK_REG_MODE     0x174
+#define BUCK_REG_MODE          BIT(0)
+#define BUCK_REG_MODE_VBAT     BIT(0)
+#define BUCK_REG_MODE_VSYS     0
+
+#define SMBB_BAT_PRES_STATUS   0x208
+#define PRES_STATUS_BAT_PRES   BIT(7)
+#define SMBB_BAT_TEMP_STATUS   0x209
+#define TEMP_STATUS_OK         BIT(7)
+#define TEMP_STATUS_HOT                BIT(6)
+#define SMBB_BAT_BTC_CTRL      0x249
+#define BTC_CTRL_COMP_EN       BIT(7)
+#define BTC_CTRL_COLD_EXT      BIT(1)
+#define BTC_CTRL_HOT_EXT_N     BIT(0)
+
+#define SMBB_USB_IMAX          0x344
+#define SMBB_USB_ENUM_TIMER_STOP 0x34e
+#define ENUM_TIMER_STOP                BIT(0)
+#define SMBB_USB_SEC_ACCESS    0x3d0
+#define SEC_ACCESS_MAGIC       0xa5
+#define SMBB_USB_REV_BST       0x3ed
+#define REV_BST_CHG_GONE       BIT(7)
+
+#define SMBB_DC_IMAX           0x444
+
+#define SMBB_MISC_REV2         0x601
+#define SMBB_MISC_BOOT_DONE    0x642
+#define BOOT_DONE              BIT(7)
+
+#define STATUS_USBIN_VALID     BIT(0) /* USB connection is valid */
+#define STATUS_DCIN_VALID      BIT(1) /* DC connection is valid */
+#define STATUS_BAT_HOT         BIT(2) /* Battery temp 1=Hot, 0=Cold */
+#define STATUS_BAT_OK          BIT(3) /* Battery temp OK */
+#define STATUS_BAT_PRESENT     BIT(4) /* Battery is present */
+#define STATUS_CHG_DONE                BIT(5) /* Charge cycle is complete */
+#define STATUS_CHG_TRKL                BIT(6) /* Trickle charging */
+#define STATUS_CHG_FAST                BIT(7) /* Fast charging */
+#define STATUS_CHG_GONE                BIT(8) /* No charger is connected */
+
+enum smbb_attr {
+       ATTR_BAT_ISAFE,
+       ATTR_BAT_IMAX,
+       ATTR_USBIN_IMAX,
+       ATTR_DCIN_IMAX,
+       ATTR_BAT_VSAFE,
+       ATTR_BAT_VMAX,
+       ATTR_BAT_VMIN,
+       ATTR_CHG_VDET,
+       ATTR_VIN_MIN,
+       _ATTR_CNT,
+};
+
+struct smbb_charger {
+       unsigned int revision;
+       unsigned int addr;
+       struct device *dev;
+
+       bool dc_disabled;
+       bool jeita_ext_temp;
+       unsigned long status;
+       struct mutex statlock;
+
+       unsigned int attr[_ATTR_CNT];
+
+       struct power_supply *usb_psy;
+       struct power_supply *dc_psy;
+       struct power_supply *bat_psy;
+       struct regmap *regmap;
+};
+
+static int smbb_vbat_weak_fn(unsigned int index)
+{
+       return 2100000 + index * 100000;
+}
+
+static int smbb_vin_fn(unsigned int index)
+{
+       if (index > 42)
+               return 5600000 + (index - 43) * 200000;
+       return 3400000 + index * 50000;
+}
+
+static int smbb_vmax_fn(unsigned int index)
+{
+       return 3240000 + index * 10000;
+}
+
+static int smbb_vbat_det_fn(unsigned int index)
+{
+       return 3240000 + index * 20000;
+}
+
+static int smbb_imax_fn(unsigned int index)
+{
+       if (index < 2)
+               return 100000 + index * 50000;
+       return index * 100000;
+}
+
+static int smbb_bat_imax_fn(unsigned int index)
+{
+       return index * 50000;
+}
+
+static unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int))
+{
+       unsigned int widx;
+       unsigned int sel;
+
+       for (widx = sel = 0; (*fn)(widx) <= val; ++widx)
+               sel = widx;
+
+       return sel;
+}
+
+static const struct smbb_charger_attr {
+       const char *name;
+       unsigned int reg;
+       unsigned int safe_reg;
+       unsigned int max;
+       unsigned int min;
+       unsigned int fail_ok;
+       int (*hw_fn)(unsigned int);
+} smbb_charger_attrs[] = {
+       [ATTR_BAT_ISAFE] = {
+               .name = "qcom,fast-charge-safe-current",
+               .reg = SMBB_CHG_ISAFE,
+               .max = 3000000,
+               .min = 200000,
+               .hw_fn = smbb_bat_imax_fn,
+               .fail_ok = 1,
+       },
+       [ATTR_BAT_IMAX] = {
+               .name = "qcom,fast-charge-current-limit",
+               .reg = SMBB_CHG_IMAX,
+               .safe_reg = SMBB_CHG_ISAFE,
+               .max = 3000000,
+               .min = 200000,
+               .hw_fn = smbb_bat_imax_fn,
+       },
+       [ATTR_DCIN_IMAX] = {
+               .name = "qcom,dc-current-limit",
+               .reg = SMBB_DC_IMAX,
+               .max = 2500000,
+               .min = 100000,
+               .hw_fn = smbb_imax_fn,
+       },
+       [ATTR_BAT_VSAFE] = {
+               .name = "qcom,fast-charge-safe-voltage",
+               .reg = SMBB_CHG_VSAFE,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vmax_fn,
+               .fail_ok = 1,
+       },
+       [ATTR_BAT_VMAX] = {
+               .name = "qcom,fast-charge-high-threshold-voltage",
+               .reg = SMBB_CHG_VMAX,
+               .safe_reg = SMBB_CHG_VSAFE,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vmax_fn,
+       },
+       [ATTR_BAT_VMIN] = {
+               .name = "qcom,fast-charge-low-threshold-voltage",
+               .reg = SMBB_CHG_VBAT_WEAK,
+               .max = 3600000,
+               .min = 2100000,
+               .hw_fn = smbb_vbat_weak_fn,
+       },
+       [ATTR_CHG_VDET] = {
+               .name = "qcom,auto-recharge-threshold-voltage",
+               .reg = SMBB_CHG_VBAT_DET,
+               .max = 5000000,
+               .min = 3240000,
+               .hw_fn = smbb_vbat_det_fn,
+       },
+       [ATTR_VIN_MIN] = {
+               .name = "qcom,minimum-input-voltage",
+               .reg = SMBB_CHG_VIN_MIN,
+               .max = 9600000,
+               .min = 4200000,
+               .hw_fn = smbb_vin_fn,
+       },
+       [ATTR_USBIN_IMAX] = {
+               .name = "usb-charge-current-limit",
+               .reg = SMBB_USB_IMAX,
+               .max = 2500000,
+               .min = 100000,
+               .hw_fn = smbb_imax_fn,
+       },
+};
+
+static int smbb_charger_attr_write(struct smbb_charger *chg,
+               enum smbb_attr which, unsigned int val)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int wval;
+       unsigned int out;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       if (val > prop->max || val < prop->min) {
+               dev_err(chg->dev, "value out of range for %s [%u:%u]\n",
+                       prop->name, prop->min, prop->max);
+               return -EINVAL;
+       }
+
+       if (prop->safe_reg) {
+               rc = regmap_read(chg->regmap,
+                               chg->addr + prop->safe_reg, &wval);
+               if (rc) {
+                       dev_err(chg->dev,
+                               "unable to read safe value for '%s'\n",
+                               prop->name);
+                       return rc;
+               }
+
+               wval = prop->hw_fn(wval);
+
+               if (val > wval) {
+                       dev_warn(chg->dev,
+                               "%s above safe value, clamping at %u\n",
+                               prop->name, wval);
+                       val = wval;
+               }
+       }
+
+       wval = smbb_hw_lookup(val, prop->hw_fn);
+
+       rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval);
+       if (rc) {
+               dev_err(chg->dev, "unable to update %s", prop->name);
+               return rc;
+       }
+       out = prop->hw_fn(wval);
+       if (out != val) {
+               dev_warn(chg->dev,
+                       "%s inaccurate, rounded to %u\n",
+                       prop->name, out);
+       }
+
+       dev_dbg(chg->dev, "%s <= %d\n", prop->name, out);
+
+       chg->attr[which] = out;
+
+       return 0;
+}
+
+static int smbb_charger_attr_read(struct smbb_charger *chg,
+               enum smbb_attr which)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int val;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val);
+       if (rc) {
+               dev_err(chg->dev, "failed to read %s\n", prop->name);
+               return rc;
+       }
+       val = prop->hw_fn(val);
+       dev_dbg(chg->dev, "%s => %d\n", prop->name, val);
+
+       chg->attr[which] = val;
+
+       return 0;
+}
+
+static int smbb_charger_attr_parse(struct smbb_charger *chg,
+               enum smbb_attr which)
+{
+       const struct smbb_charger_attr *prop;
+       unsigned int val;
+       int rc;
+
+       prop = &smbb_charger_attrs[which];
+
+       rc = of_property_read_u32(chg->dev->of_node, prop->name, &val);
+       if (rc == 0) {
+               rc = smbb_charger_attr_write(chg, which, val);
+               if (!rc || !prop->fail_ok)
+                       return rc;
+       }
+       return smbb_charger_attr_read(chg, which);
+}
+
+static void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag)
+{
+       bool state;
+       int ret;
+
+       ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state);
+       if (ret < 0) {
+               dev_err(chg->dev, "failed to read irq line\n");
+               return;
+       }
+
+       mutex_lock(&chg->statlock);
+       if (state)
+               chg->status |= flag;
+       else
+               chg->status &= ~flag;
+       mutex_unlock(&chg->statlock);
+
+       dev_dbg(chg->dev, "status = %03lx\n", chg->status);
+}
+
+static irqreturn_t smbb_usb_valid_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID);
+       power_supply_changed(chg->usb_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_dc_valid_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID);
+       if (!chg->dc_disabled)
+               power_supply_changed(chg->dc_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_bat_temp_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+       unsigned int val;
+       int rc;
+
+       rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val);
+       if (rc)
+               return IRQ_HANDLED;
+
+       mutex_lock(&chg->statlock);
+       if (val & TEMP_STATUS_OK) {
+               chg->status |= STATUS_BAT_OK;
+       } else {
+               chg->status &= ~STATUS_BAT_OK;
+               if (val & TEMP_STATUS_HOT)
+                       chg->status |= STATUS_BAT_HOT;
+       }
+       mutex_unlock(&chg->statlock);
+
+       power_supply_changed(chg->bat_psy);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_bat_present_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_done_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_DONE);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_gone_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_GONE);
+       power_supply_changed(chg->bat_psy);
+       power_supply_changed(chg->usb_psy);
+       if (!chg->dc_disabled)
+               power_supply_changed(chg->dc_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_fast_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_FAST);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t smbb_chg_trkl_handler(int irq, void *_data)
+{
+       struct smbb_charger *chg = _data;
+
+       smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL);
+       power_supply_changed(chg->bat_psy);
+
+       return IRQ_HANDLED;
+}
+
+static const struct smbb_irq {
+       const char *name;
+       irqreturn_t (*handler)(int, void *);
+} smbb_charger_irqs[] = {
+       { "chg-done", smbb_chg_done_handler },
+       { "chg-fast", smbb_chg_fast_handler },
+       { "chg-trkl", smbb_chg_trkl_handler },
+       { "bat-temp-ok", smbb_bat_temp_handler },
+       { "bat-present", smbb_bat_present_handler },
+       { "chg-gone", smbb_chg_gone_handler },
+       { "usb-valid", smbb_usb_valid_handler },
+       { "dc-valid", smbb_dc_valid_handler },
+};
+
+static int smbb_usbin_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               mutex_lock(&chg->statlock);
+               val->intval = !(chg->status & STATUS_CHG_GONE) &&
+                               (chg->status & STATUS_USBIN_VALID);
+               mutex_unlock(&chg->statlock);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = chg->attr[ATTR_USBIN_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+               val->intval = 2500000;
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_usbin_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX,
+                               val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_dcin_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc = 0;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_ONLINE:
+               mutex_lock(&chg->statlock);
+               val->intval = !(chg->status & STATUS_CHG_GONE) &&
+                               (chg->status & STATUS_DCIN_VALID);
+               mutex_unlock(&chg->statlock);
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               val->intval = chg->attr[ATTR_DCIN_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+               val->intval = 2500000;
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_dcin_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX,
+                               val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_charger_writable_property(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT;
+}
+
+static int smbb_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       unsigned long status;
+       int rc = 0;
+
+       mutex_lock(&chg->statlock);
+       status = chg->status;
+       mutex_unlock(&chg->statlock);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (status & STATUS_CHG_GONE)
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID)))
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (status & STATUS_CHG_DONE)
+                       val->intval = POWER_SUPPLY_STATUS_FULL;
+               else if (!(status & STATUS_BAT_OK))
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else /* everything is ok for charging, but we are not... */
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (status & STATUS_BAT_OK)
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               else if (status & STATUS_BAT_HOT)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_COLD;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               if (status & STATUS_CHG_FAST)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
+               else if (status & STATUS_CHG_TRKL)
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+               else
+                       val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = !!(status & STATUS_BAT_PRESENT);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               val->intval = chg->attr[ATTR_BAT_IMAX];
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               val->intval = chg->attr[ATTR_BAT_VMAX];
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               /* this charger is a single-cell lithium-ion battery charger
+               * only.  If you hook up some other technology, there will be
+               * fireworks.
+               */
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = 3000000; /* single-cell li-ion low end */
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_battery_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct smbb_charger *chg = power_supply_get_drvdata(psy);
+       int rc;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval);
+               break;
+       default:
+               rc = -EINVAL;
+               break;
+       }
+
+       return rc;
+}
+
+static int smbb_battery_writable_property(struct power_supply *psy,
+               enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static enum power_supply_property smbb_charger_properties[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+};
+
+static enum power_supply_property smbb_battery_properties[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static const struct reg_off_mask_default {
+       unsigned int offset;
+       unsigned int mask;
+       unsigned int value;
+       unsigned int rev_mask;
+} smbb_charger_setup[] = {
+       /* The bootloader is supposed to set this... make sure anyway. */
+       { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE },
+
+       /* Disable software timer */
+       { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 },
+
+       /* Clear and disable watchdog */
+       { SMBB_CHG_WDOG_TIME, 0xff, 160 },
+       { SMBB_CHG_WDOG_EN, WDOG_EN, 0 },
+
+       /* Use charger based EoC detection */
+       { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG },
+
+       /* Disable GSM PA load adjustment.
+       * The PA signal is incorrectly connected on v2.
+       */
+       { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) },
+
+       /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */
+       { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT },
+
+       /* Enable battery temperature comparators */
+       { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN },
+
+       /* Stop USB enumeration timer */
+       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
+
+#if 0 /* FIXME supposedly only to disable hardware ARB termination */
+       { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC },
+       { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE },
+#endif
+
+       /* Stop USB enumeration timer, again */
+       { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP },
+
+       /* Enable charging */
+       { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN },
+};
+
+static char *smbb_bif[] = { "smbb-bif" };
+
+static const struct power_supply_desc bat_psy_desc = {
+       .name = "smbb-bif",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = smbb_battery_properties,
+       .num_properties = ARRAY_SIZE(smbb_battery_properties),
+       .get_property = smbb_battery_get_property,
+       .set_property = smbb_battery_set_property,
+       .property_is_writeable = smbb_battery_writable_property,
+};
+
+static const struct power_supply_desc usb_psy_desc = {
+       .name = "smbb-usbin",
+       .type = POWER_SUPPLY_TYPE_USB,
+       .properties = smbb_charger_properties,
+       .num_properties = ARRAY_SIZE(smbb_charger_properties),
+       .get_property = smbb_usbin_get_property,
+       .set_property = smbb_usbin_set_property,
+       .property_is_writeable = smbb_charger_writable_property,
+};
+
+static const struct power_supply_desc dc_psy_desc = {
+       .name = "smbb-dcin",
+       .type = POWER_SUPPLY_TYPE_MAINS,
+       .properties = smbb_charger_properties,
+       .num_properties = ARRAY_SIZE(smbb_charger_properties),
+       .get_property = smbb_dcin_get_property,
+       .set_property = smbb_dcin_set_property,
+       .property_is_writeable = smbb_charger_writable_property,
+};
+
+static int smbb_charger_probe(struct platform_device *pdev)
+{
+       struct power_supply_config bat_cfg = {};
+       struct power_supply_config usb_cfg = {};
+       struct power_supply_config dc_cfg = {};
+       struct smbb_charger *chg;
+       int rc, i;
+
+       chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
+       if (!chg)
+               return -ENOMEM;
+
+       chg->dev = &pdev->dev;
+       mutex_init(&chg->statlock);
+
+       chg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!chg->regmap) {
+               dev_err(&pdev->dev, "failed to locate regmap\n");
+               return -ENODEV;
+       }
+
+       rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr);
+       if (rc) {
+               dev_err(&pdev->dev, "missing or invalid 'reg' property\n");
+               return rc;
+       }
+
+       rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision);
+       if (rc) {
+               dev_err(&pdev->dev, "unable to read revision\n");
+               return rc;
+       }
+
+       chg->revision += 1;
+       if (chg->revision != 2 && chg->revision != 3) {
+               dev_err(&pdev->dev, "v1 hardware not supported\n");
+               return -ENODEV;
+       }
+       dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision);
+
+       chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc");
+
+       for (i = 0; i < _ATTR_CNT; ++i) {
+               rc = smbb_charger_attr_parse(chg, i);
+               if (rc) {
+                       dev_err(&pdev->dev, "failed to parse/apply settings\n");
+                       return rc;
+               }
+       }
+
+       bat_cfg.drv_data = chg;
+       bat_cfg.of_node = pdev->dev.of_node;
+       chg->bat_psy = devm_power_supply_register(&pdev->dev,
+                                                 &bat_psy_desc,
+                                                 &bat_cfg);
+       if (IS_ERR(chg->bat_psy)) {
+               dev_err(&pdev->dev, "failed to register battery\n");
+               return PTR_ERR(chg->bat_psy);
+       }
+
+       usb_cfg.drv_data = chg;
+       usb_cfg.supplied_to = smbb_bif;
+       usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
+       chg->usb_psy = devm_power_supply_register(&pdev->dev,
+                                                 &usb_psy_desc,
+                                                 &usb_cfg);
+       if (IS_ERR(chg->usb_psy)) {
+               dev_err(&pdev->dev, "failed to register USB power supply\n");
+               return PTR_ERR(chg->usb_psy);
+       }
+
+       if (!chg->dc_disabled) {
+               dc_cfg.drv_data = chg;
+               dc_cfg.supplied_to = smbb_bif;
+               dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif);
+               chg->dc_psy = devm_power_supply_register(&pdev->dev,
+                                                        &dc_psy_desc,
+                                                        &dc_cfg);
+               if (IS_ERR(chg->dc_psy)) {
+                       dev_err(&pdev->dev, "failed to register DC power supply\n");
+                       return PTR_ERR(chg->dc_psy);
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) {
+               int irq;
+
+               irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name);
+               if (irq < 0) {
+                       dev_err(&pdev->dev, "failed to get irq '%s'\n",
+                               smbb_charger_irqs[i].name);
+                       return irq;
+               }
+
+               smbb_charger_irqs[i].handler(irq, chg);
+
+               rc = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                               smbb_charger_irqs[i].handler, IRQF_ONESHOT,
+                               smbb_charger_irqs[i].name, chg);
+               if (rc) {
+                       dev_err(&pdev->dev, "failed to request irq '%s'\n",
+                               smbb_charger_irqs[i].name);
+                       return rc;
+               }
+       }
+
+       chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node,
+                       "qcom,jeita-extended-temp-range");
+
+       /* Set temperature range to [35%:70%] or [25%:80%] accordingly */
+       rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL,
+                       BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N,
+                       chg->jeita_ext_temp ?
+                               BTC_CTRL_COLD_EXT :
+                               BTC_CTRL_HOT_EXT_N);
+       if (rc) {
+               dev_err(&pdev->dev,
+                       "unable to set %s temperature range\n",
+                       chg->jeita_ext_temp ? "JEITA extended" : "normal");
+               return rc;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) {
+               const struct reg_off_mask_default *r = &smbb_charger_setup[i];
+
+               if (r->rev_mask & BIT(chg->revision))
+                       continue;
+
+               rc = regmap_update_bits(chg->regmap, chg->addr + r->offset,
+                               r->mask, r->value);
+               if (rc) {
+                       dev_err(&pdev->dev,
+                               "unable to initializing charging, bailing\n");
+                       return rc;
+               }
+       }
+
+       platform_set_drvdata(pdev, chg);
+
+       return 0;
+}
+
+static int smbb_charger_remove(struct platform_device *pdev)
+{
+       struct smbb_charger *chg;
+
+       chg = platform_get_drvdata(pdev);
+
+       regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0);
+
+       return 0;
+}
+
+static const struct of_device_id smbb_charger_id_table[] = {
+       { .compatible = "qcom,pm8941-charger" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, smbb_charger_id_table);
+
+static struct platform_driver smbb_charger_driver = {
+       .probe    = smbb_charger_probe,
+       .remove  = smbb_charger_remove,
+       .driver  = {
+               .name   = "qcom-smbb",
+               .of_match_table = smbb_charger_id_table,
+       },
+};
+module_platform_driver(smbb_charger_driver);
+
+MODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver");
+MODULE_LICENSE("GPL v2");
index 5a0189bf19bb145faccce601478910906b5ef839..1131cf75acc67ff024984037273a5f6db1c564f3 100644 (file)
@@ -15,7 +15,7 @@ config POWER_RESET_AS3722
          This driver supports turning off board via a ams AS3722 power-off.
 
 config POWER_RESET_AT91_POWEROFF
-       bool "Atmel AT91 poweroff driver"
+       tristate "Atmel AT91 poweroff driver"
        depends on ARCH_AT91
        default SOC_AT91SAM9 || SOC_SAMA5
        help
@@ -23,7 +23,7 @@ config POWER_RESET_AT91_POWEROFF
          SoCs
 
 config POWER_RESET_AT91_RESET
-       bool "Atmel AT91 reset driver"
+       tristate "Atmel AT91 reset driver"
        depends on ARCH_AT91
        default SOC_AT91SAM9 || SOC_SAMA5
        help
index 9847cfb7e23dd3a27f9ac94c3185667b970d5de9..e9e24df35f26bd8ecc68f92dac8cd2a5f26145f5 100644 (file)
@@ -10,6 +10,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -48,6 +49,7 @@ static const char *shdwc_wakeup_modes[] = {
 };
 
 static void __iomem *at91_shdwc_base;
+static struct clk *sclk;
 
 static void __init at91_wakeup_status(void)
 {
@@ -119,9 +121,10 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev)
        writel(wakeup_mode | mode, at91_shdwc_base + AT91_SHDW_MR);
 }
 
-static int at91_poweroff_probe(struct platform_device *pdev)
+static int __init at91_poweroff_probe(struct platform_device *pdev)
 {
        struct resource *res;
+       int ret;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
@@ -130,6 +133,16 @@ static int at91_poweroff_probe(struct platform_device *pdev)
                return PTR_ERR(at91_shdwc_base);
        }
 
+       sclk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(sclk))
+               return PTR_ERR(sclk);
+
+       ret = clk_prepare_enable(sclk);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not enable slow clock\n");
+               return ret;
+       }
+
        at91_wakeup_status();
 
        if (pdev->dev.of_node)
@@ -140,6 +153,16 @@ static int at91_poweroff_probe(struct platform_device *pdev)
        return 0;
 }
 
+static int __exit at91_poweroff_remove(struct platform_device *pdev)
+{
+       if (pm_power_off == at91_poweroff)
+               pm_power_off = NULL;
+
+       clk_disable_unprepare(sclk);
+
+       return 0;
+}
+
 static const struct of_device_id at91_poweroff_of_match[] = {
        { .compatible = "atmel,at91sam9260-shdwc", },
        { .compatible = "atmel,at91sam9rl-shdwc", },
@@ -148,10 +171,14 @@ static const struct of_device_id at91_poweroff_of_match[] = {
 };
 
 static struct platform_driver at91_poweroff_driver = {
-       .probe = at91_poweroff_probe,
+       .remove = __exit_p(at91_poweroff_remove),
        .driver = {
                .name = "at91-poweroff",
                .of_match_table = at91_poweroff_of_match,
        },
 };
-module_platform_driver(at91_poweroff_driver);
+module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe);
+
+MODULE_AUTHOR("Atmel Corporation");
+MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs");
+MODULE_LICENSE("GPL v2");
index c378d4ec826f4248155cb9f786697caba7e307ad..3f6b5dd7c3d4e47041611b3e6baab483c2ae41db 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Atmel AT91 SAM9 SoCs reset code
+ * Atmel AT91 SAM9 & SAMA5 SoCs reset code
  *
  * Copyright (C) 2007 Atmel Corporation.
  * Copyright (C) BitBox Ltd 2010
@@ -11,6 +11,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_address.h>
@@ -46,6 +47,7 @@ enum reset_type {
 };
 
 static void __iomem *at91_ramc_base[2], *at91_rstc_base;
+static struct clk *sclk;
 
 /*
 * unless the SDRAM is cleanly shutdown before we hit the
@@ -178,11 +180,11 @@ static struct notifier_block at91_restart_nb = {
        .priority = 192,
 };
 
-static int at91_reset_of_probe(struct platform_device *pdev)
+static int __init at91_reset_probe(struct platform_device *pdev)
 {
        const struct of_device_id *match;
        struct device_node *np;
-       int idx = 0;
+       int ret, idx = 0;
 
        at91_rstc_base = of_iomap(pdev->dev.of_node, 0);
        if (!at91_rstc_base) {
@@ -204,53 +206,32 @@ static int at91_reset_of_probe(struct platform_device *pdev)
 
        match = of_match_node(at91_reset_of_match, pdev->dev.of_node);
        at91_restart_nb.notifier_call = match->data;
-       return register_restart_handler(&at91_restart_nb);
-}
 
-static int at91_reset_platform_probe(struct platform_device *pdev)
-{
-       const struct platform_device_id *match;
-       struct resource *res;
-       int idx = 0;
+       sclk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(sclk))
+               return PTR_ERR(sclk);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       at91_rstc_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(at91_rstc_base)) {
-               dev_err(&pdev->dev, "Could not map reset controller address\n");
-               return PTR_ERR(at91_rstc_base);
+       ret = clk_prepare_enable(sclk);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not enable slow clock\n");
+               return ret;
        }
 
-       for (idx = 0; idx < 2; idx++) {
-               res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1 );
-               at91_ramc_base[idx] = devm_ioremap(&pdev->dev, res->start,
-                                                  resource_size(res));
-               if (!at91_ramc_base[idx]) {
-                       dev_err(&pdev->dev, "Could not map ram controller address\n");
-                       return -ENOMEM;
-               }
+       ret = register_restart_handler(&at91_restart_nb);
+       if (ret) {
+               clk_disable_unprepare(sclk);
+               return ret;
        }
 
-       match = platform_get_device_id(pdev);
-       at91_restart_nb.notifier_call =
-               (int (*)(struct notifier_block *,
-                        unsigned long, void *)) match->driver_data;
+       at91_reset_status(pdev);
 
-       return register_restart_handler(&at91_restart_nb);
+       return 0;
 }
 
-static int at91_reset_probe(struct platform_device *pdev)
+static int __exit at91_reset_remove(struct platform_device *pdev)
 {
-       int ret;
-
-       if (pdev->dev.of_node)
-               ret = at91_reset_of_probe(pdev);
-       else
-               ret = at91_reset_platform_probe(pdev);
-
-       if (ret)
-               return ret;
-
-       at91_reset_status(pdev);
+       unregister_restart_handler(&at91_restart_nb);
+       clk_disable_unprepare(sclk);
 
        return 0;
 }
@@ -262,11 +243,15 @@ static const struct platform_device_id at91_reset_plat_match[] = {
 };
 
 static struct platform_driver at91_reset_driver = {
-       .probe = at91_reset_probe,
+       .remove = __exit_p(at91_reset_remove),
        .driver = {
                .name = "at91-reset",
                .of_match_table = at91_reset_of_match,
        },
        .id_table = at91_reset_plat_match,
 };
-module_platform_driver(at91_reset_driver);
+module_platform_driver_probe(at91_reset_driver, at91_reset_probe);
+
+MODULE_AUTHOR("Atmel Corporation");
+MODULE_DESCRIPTION("Reset driver for Atmel SoCs");
+MODULE_LICENSE("GPL v2");
index a49a9d44bdda84dce49166280c12bbd7fb125bf6..cfdbde9daf94bd3c88a6e0ffd36a3658c40a2cf1 100644 (file)
@@ -1760,5 +1760,4 @@ module_i2c_driver(rt9455_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Anda-Maria Nicolae <anda-maria.nicolae@intel.com>");
-MODULE_ALIAS("i2c:rt9455-charger");
 MODULE_DESCRIPTION("Richtek RT9455 Charger Driver");
index 0b60a0b5878bd4ccc99972644b3552ae15bff4a1..072c5189bd6d10acc40e2fbf22f7cba55b62eb73 100644 (file)
@@ -1332,4 +1332,3 @@ MODULE_AUTHOR("Bruce E. Robertson <bruce.e.robertson@intel.com>");
 MODULE_AUTHOR("Mika Westerberg <mika.westerberg@linux.intel.com>");
 MODULE_DESCRIPTION("SMB347 battery charger driver");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("i2c:smb347");
index 7e8fbd29c30eee6874bda989e4552972040e8b52..1b4b5e09538e15cad0b5921b234929457e1f664a 100644 (file)
@@ -353,6 +353,7 @@ static const struct of_device_id of_tps65090_charger_match[] = {
        { .compatible = "ti,tps65090-charger", },
        { /* end */ }
 };
+MODULE_DEVICE_TABLE(of, of_tps65090_charger_match);
 
 static struct platform_driver tps65090_charger_driver = {
        .driver = {
diff --git a/drivers/power/tps65217_charger.c b/drivers/power/tps65217_charger.c
new file mode 100644 (file)
index 0000000..d9f5673
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Battery charger driver for TI's tps65217
+ *
+ * Copyright (c) 2015, Collabora Ltd.
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Battery charger driver for TI's tps65217
+ */
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/core.h>
+#include <linux/mfd/tps65217.h>
+
+#define POLL_INTERVAL          (HZ * 2)
+
+struct tps65217_charger {
+       struct tps65217 *tps;
+       struct device *dev;
+       struct power_supply *ac;
+
+       int     ac_online;
+       int     prev_ac_online;
+
+       struct task_struct      *poll_task;
+};
+
+static enum power_supply_property tps65217_ac_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int tps65217_config_charger(struct tps65217_charger *charger)
+{
+       int ret;
+
+       dev_dbg(charger->dev, "%s\n", __func__);
+
+       /*
+        * tps65217 rev. G, p. 31 (see p. 32 for NTC schematic)
+        *
+        * The device can be configured to support a 100k NTC (B = 3960) by
+        * setting the the NTC_TYPE bit in register CHGCONFIG1 to 1. However it
+        * is not recommended to do so. In sleep mode, the charger continues
+        * charging the battery, but all register values are reset to default
+        * values. Therefore, the charger would get the wrong temperature
+        * information. If 100k NTC setting is required, please contact the
+        * factory.
+        *
+        * ATTENTION, conflicting information, from p. 46
+        *
+        * NTC TYPE (for battery temperature measurement)
+        *   0 – 100k (curve 1, B = 3960)
+        *   1 – 10k  (curve 2, B = 3480) (default on reset)
+        *
+        */
+       ret = tps65217_clear_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
+                                 TPS65217_CHGCONFIG1_NTC_TYPE,
+                                 TPS65217_PROTECT_NONE);
+       if (ret) {
+               dev_err(charger->dev,
+                       "failed to set 100k NTC setting: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tps65217_enable_charging(struct tps65217_charger *charger)
+{
+       int ret;
+
+       /* charger already enabled */
+       if (charger->ac_online)
+               return 0;
+
+       dev_dbg(charger->dev, "%s: enable charging\n", __func__);
+       ret = tps65217_set_bits(charger->tps, TPS65217_REG_CHGCONFIG1,
+                               TPS65217_CHGCONFIG1_CHG_EN,
+                               TPS65217_CHGCONFIG1_CHG_EN,
+                               TPS65217_PROTECT_NONE);
+       if (ret) {
+               dev_err(charger->dev,
+                       "%s: Error in writing CHG_EN in reg 0x%x: %d\n",
+                       __func__, TPS65217_REG_CHGCONFIG1, ret);
+               return ret;
+       }
+
+       charger->ac_online = 1;
+
+       return 0;
+}
+
+static int tps65217_ac_get_property(struct power_supply *psy,
+                       enum power_supply_property psp,
+                       union power_supply_propval *val)
+{
+       struct tps65217_charger *charger = power_supply_get_drvdata(psy);
+
+       if (psp == POWER_SUPPLY_PROP_ONLINE) {
+               val->intval = charger->ac_online;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static irqreturn_t tps65217_charger_irq(int irq, void *dev)
+{
+       int ret, val;
+       struct tps65217_charger *charger = dev;
+
+       charger->prev_ac_online = charger->ac_online;
+
+       ret = tps65217_reg_read(charger->tps, TPS65217_REG_STATUS, &val);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
+                       __func__, TPS65217_REG_STATUS);
+               return IRQ_HANDLED;
+       }
+
+       dev_dbg(charger->dev, "%s: 0x%x\n", __func__, val);
+
+       /* check for AC status bit */
+       if (val & TPS65217_STATUS_ACPWR) {
+               ret = tps65217_enable_charging(charger);
+               if (ret) {
+                       dev_err(charger->dev,
+                               "failed to enable charger: %d\n", ret);
+                       return IRQ_HANDLED;
+               }
+       } else {
+               charger->ac_online = 0;
+       }
+
+       if (charger->prev_ac_online != charger->ac_online)
+               power_supply_changed(charger->ac);
+
+       ret = tps65217_reg_read(charger->tps, TPS65217_REG_CHGCONFIG0, &val);
+       if (ret < 0) {
+               dev_err(charger->dev, "%s: Error in reading reg 0x%x\n",
+                       __func__, TPS65217_REG_CHGCONFIG0);
+               return IRQ_HANDLED;
+       }
+
+       if (val & TPS65217_CHGCONFIG0_ACTIVE)
+               dev_dbg(charger->dev, "%s: charger is charging\n", __func__);
+       else
+               dev_dbg(charger->dev,
+                       "%s: charger is NOT charging\n", __func__);
+
+       return IRQ_HANDLED;
+}
+
+static int tps65217_charger_poll_task(void *data)
+{
+       set_freezable();
+
+       while (!kthread_should_stop()) {
+               schedule_timeout_interruptible(POLL_INTERVAL);
+               try_to_freeze();
+               tps65217_charger_irq(-1, data);
+       }
+       return 0;
+}
+
+static const struct power_supply_desc tps65217_charger_desc = {
+       .name                   = "tps65217-ac",
+       .type                   = POWER_SUPPLY_TYPE_MAINS,
+       .get_property           = tps65217_ac_get_property,
+       .properties             = tps65217_ac_props,
+       .num_properties         = ARRAY_SIZE(tps65217_ac_props),
+};
+
+static int tps65217_charger_probe(struct platform_device *pdev)
+{
+       struct tps65217 *tps = dev_get_drvdata(pdev->dev.parent);
+       struct tps65217_charger *charger;
+       int ret;
+
+       dev_dbg(&pdev->dev, "%s\n", __func__);
+
+       charger = devm_kzalloc(&pdev->dev, sizeof(*charger), GFP_KERNEL);
+       if (!charger)
+               return -ENOMEM;
+
+       charger->tps = tps;
+       charger->dev = &pdev->dev;
+
+       charger->ac = devm_power_supply_register(&pdev->dev,
+                                                &tps65217_charger_desc,
+                                                NULL);
+       if (IS_ERR(charger->ac)) {
+               dev_err(&pdev->dev, "failed: power supply register\n");
+               return PTR_ERR(charger->ac);
+       }
+
+       ret = tps65217_config_charger(charger);
+       if (ret < 0) {
+               dev_err(charger->dev, "charger config failed, err %d\n", ret);
+               return ret;
+       }
+
+       charger->poll_task = kthread_run(tps65217_charger_poll_task,
+                                     charger, "ktps65217charger");
+       if (IS_ERR(charger->poll_task)) {
+               ret = PTR_ERR(charger->poll_task);
+               dev_err(charger->dev, "Unable to run kthread err %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int tps65217_charger_remove(struct platform_device *pdev)
+{
+       struct tps65217_charger *charger = platform_get_drvdata(pdev);
+
+       kthread_stop(charger->poll_task);
+
+       return 0;
+}
+
+static const struct of_device_id tps65217_charger_match_table[] = {
+       { .compatible = "ti,tps65217-charger", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, tps65217_charger_match_table);
+
+static struct platform_driver tps65217_charger_driver = {
+       .probe  = tps65217_charger_probe,
+       .remove = tps65217_charger_remove,
+       .driver = {
+               .name   = "tps65217-charger",
+               .of_match_table = of_match_ptr(tps65217_charger_match_table),
+       },
+
+};
+module_platform_driver(tps65217_charger_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Enric Balletbo Serra <enric.balletbo@collabora.com>");
+MODULE_DESCRIPTION("TPS65217 battery charger driver");
index db11ae6599f341546ebfd2d3995f06ecdbec5097..c826c83fdf9850fa510d0583550b1f8e3cdfc7e6 100644 (file)
@@ -499,7 +499,8 @@ static int wm831x_power_probe(struct platform_device *pdev)
        struct wm831x_power *power;
        int ret, irq, i;
 
-       power = kzalloc(sizeof(struct wm831x_power), GFP_KERNEL);
+       power = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_power),
+                            GFP_KERNEL);
        if (power == NULL)
                return -ENOMEM;
 
@@ -536,7 +537,7 @@ static int wm831x_power_probe(struct platform_device *pdev)
                                            NULL);
        if (IS_ERR(power->wall)) {
                ret = PTR_ERR(power->wall);
-               goto err_kmalloc;
+               goto err;
        }
 
        power->usb_desc.name = power->usb_name,
@@ -626,8 +627,7 @@ err_usb:
        power_supply_unregister(power->usb);
 err_wall:
        power_supply_unregister(power->wall);
-err_kmalloc:
-       kfree(power);
+err:
        return ret;
 }
 
@@ -654,7 +654,6 @@ static int wm831x_power_remove(struct platform_device *pdev)
                power_supply_unregister(wm831x_power->battery);
        power_supply_unregister(wm831x_power->wall);
        power_supply_unregister(wm831x_power->usb);
-       kfree(wm831x_power);
        return 0;
 }
 
index caafb1722783ce66c615deb32d0a744a8f07f503..9f4a86b754baff4660c4d3deb8b79857f4c5310d 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/types.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
-#include <linux/power/bq27x00_battery.h>
+#include <linux/power/bq27xxx_battery.h>
 
 #include "../w1.h"
 #include "../w1_int.h"
@@ -39,9 +39,10 @@ static int w1_bq27000_read(struct device *dev, unsigned int reg)
        return val;
 }
 
-static struct bq27000_platform_data bq27000_battery_info = {
+static struct bq27xxx_platform_data bq27000_battery_info = {
        .read   = w1_bq27000_read,
        .name   = "bq27000-battery",
+       .chip   = BQ27000,
 };
 
 static int w1_bq27000_add_slave(struct w1_slave *sl)
index cc8ad1e1a307bd1cf4eda99ed109dd9e53f178bf..b24c771cebd5c417dc2206553d4b2ecaa47773b5 100644 (file)
@@ -11,6 +11,8 @@
 #ifndef __LINUX_MFD_AXP20X_H
 #define __LINUX_MFD_AXP20X_H
 
+#include <linux/regmap.h>
+
 enum {
        AXP152_ID = 0,
        AXP202_ID,
@@ -438,4 +440,26 @@ struct axp288_extcon_pdata {
        struct gpio_desc *gpio_mux_cntl;
 };
 
+/* generic helper function for reading 9-16 bit wide regs */
+static inline int axp20x_read_variable_width(struct regmap *regmap,
+       unsigned int reg, unsigned int width)
+{
+       unsigned int reg_val, result;
+       int err;
+
+       err = regmap_read(regmap, reg, &reg_val);
+       if (err)
+               return err;
+
+       result = reg_val << (width - 8);
+
+       err = regmap_read(regmap, reg + 1, &reg_val);
+       if (err)
+               return err;
+
+       result |= reg_val;
+
+       return result;
+}
+
 #endif /* __LINUX_MFD_AXP20X_H */
diff --git a/include/linux/power/bq27x00_battery.h b/include/linux/power/bq27x00_battery.h
deleted file mode 100644 (file)
index a857f71..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#ifndef __LINUX_BQ27X00_BATTERY_H__
-#define __LINUX_BQ27X00_BATTERY_H__
-
-/**
- * struct bq27000_plaform_data - Platform data for bq27000 devices
- * @name: Name of the battery. If NULL the driver will fallback to "bq27000".
- * @read: HDQ read callback.
- *     This function should provide access to the HDQ bus the battery is
- *     connected to.
- *     The first parameter is a pointer to the battery device, the second the
- *     register to be read. The return value should either be the content of
- *     the passed register or an error value.
- */
-struct bq27000_platform_data {
-       const char *name;
-       int (*read)(struct device *dev, unsigned int);
-};
-
-#endif
diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h
new file mode 100644 (file)
index 0000000..45f6a7b
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __LINUX_BQ27X00_BATTERY_H__
+#define __LINUX_BQ27X00_BATTERY_H__
+
+/**
+ * struct bq27xxx_plaform_data - Platform data for bq27xxx devices
+ * @name: Name of the battery.
+ * @chip: Chip class number of this device.
+ * @read: HDQ read callback.
+ *     This function should provide access to the HDQ bus the battery is
+ *     connected to.
+ *     The first parameter is a pointer to the battery device, the second the
+ *     register to be read. The return value should either be the content of
+ *     the passed register or an error value.
+ */
+enum bq27xxx_chip {
+       BQ27000 = 1, /* bq27000, bq27200 */
+       BQ27010, /* bq27010, bq27210 */
+       BQ27500, /* bq27500, bq27510, bq27520 */
+       BQ27530, /* bq27530, bq27531 */
+       BQ27541, /* bq27541, bq27542, bq27546, bq27742 */
+       BQ27545, /* bq27545 */
+       BQ27421, /* bq27421, bq27425, bq27441, bq27621 */
+};
+
+struct bq27xxx_platform_data {
+       const char *name;
+       enum bq27xxx_chip chip;
+       int (*read)(struct device *dev, unsigned int);
+};
+
+#endif
index eadf28cb2fc91b7cbb732cb93f75979b5d256865..c4fa907c8f148abb08657827850fdc093110dd43 100644 (file)
@@ -65,7 +65,7 @@ struct charger_cable {
        const char *extcon_name;
        const char *name;
 
-       /* The charger-manager use Exton framework*/
+       /* The charger-manager use Extcon framework */
        struct extcon_specific_cable_nb extcon_dev;
        struct work_struct wq;
        struct notifier_block nb;
@@ -94,7 +94,7 @@ struct charger_cable {
  *     the charger will be maintained with disabled state.
  * @cables:
  *     the array of charger cables to enable/disable charger
- *     and set current limit according to constratint data of
+ *     and set current limit according to constraint data of
  *     struct charger_cable if only charger cable included
  *     in the array of charger cables is attached/detached.
  * @num_cables: the number of charger cables.
@@ -148,7 +148,7 @@ struct charger_regulator {
  * @polling_interval_ms: interval in millisecond at which
  *     charger manager will monitor battery health
  * @battery_present:
- *     Specify where information for existance of battery can be obtained
+ *     Specify where information for existence of battery can be obtained
  * @psy_charger_stat: the names of power-supply for chargers
  * @num_charger_regulator: the number of entries in charger_regulators
  * @charger_regulators: array of charger regulators
@@ -156,7 +156,7 @@ struct charger_regulator {
  * @thermal_zone : the name of thermal zone for battery
  * @temp_min : Minimum battery temperature for charging.
  * @temp_max : Maximum battery temperature for charging.
- * @temp_diff : Temperature diffential to restart charging.
+ * @temp_diff : Temperature difference to restart charging.
  * @measure_battery_temp:
  *     true: measure battery temperature
  *     false: measure ambient temperature