Merge git://git.infradead.org/users/cbou/battery-urgent
authorAnton Vorontsov <cbouatmailru@gmail.com>
Tue, 10 Jan 2012 16:55:11 +0000 (20:55 +0400)
committerAnton Vorontsov <cbouatmailru@gmail.com>
Tue, 10 Jan 2012 16:55:11 +0000 (20:55 +0400)
47 files changed:
Documentation/devicetree/bindings/power_supply/olpc_battery.txt [new file with mode: 0644]
Documentation/devicetree/bindings/power_supply/sbs_sbs-battery.txt [new file with mode: 0644]
Documentation/devicetree/bindings/vendor-prefixes.txt
Documentation/power/charger-manager.txt [new file with mode: 0644]
arch/arm/mach-omap2/board-rx51-peripherals.c
drivers/hid/hid-wacom.c
drivers/hid/hid-wiimote.c
drivers/mfd/max8925-core.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/bq27x00_battery.c
drivers/power/charger-manager.c [new file with mode: 0644]
drivers/power/collie_battery.c
drivers/power/da9030_battery.c
drivers/power/da9052-battery.c [new file with mode: 0644]
drivers/power/ds2760_battery.c
drivers/power/ds2780_battery.c
drivers/power/gpio-charger.c
drivers/power/intel_mid_battery.c
drivers/power/isp1704_charger.c
drivers/power/jz4740-battery.c
drivers/power/lp8727_charger.c [new file with mode: 0644]
drivers/power/max17042_battery.c
drivers/power/max8903_charger.c
drivers/power/max8925_power.c
drivers/power/max8997_charger.c
drivers/power/max8998_charger.c
drivers/power/olpc_battery.c
drivers/power/pcf50633-charger.c
drivers/power/pda_power.c
drivers/power/power_supply_core.c
drivers/power/power_supply_sysfs.c
drivers/power/s3c_adc_battery.c
drivers/power/sbs-battery.c [moved from drivers/power/bq20z75.c with 52% similarity]
drivers/power/tosa_battery.c
drivers/power/wm831x_backup.c
drivers/power/wm831x_power.c
drivers/power/wm8350_power.c
drivers/power/wm97xx_battery.c
drivers/power/z2_battery.c
include/linux/lp8727.h [new file with mode: 0755]
include/linux/mfd/max8925.h
include/linux/pda_power.h
include/linux/power/charger-manager.h [new file with mode: 0644]
include/linux/power/sbs-battery.h [moved from include/linux/power/bq20z75.h with 85% similarity]
include/linux/power_supply.h
include/linux/s3c_adc_battery.h

diff --git a/Documentation/devicetree/bindings/power_supply/olpc_battery.txt b/Documentation/devicetree/bindings/power_supply/olpc_battery.txt
new file mode 100644 (file)
index 0000000..c8901b3
--- /dev/null
@@ -0,0 +1,5 @@
+OLPC battery
+~~~~~~~~~~~~
+
+Required properties:
+  - compatible : "olpc,xo1-battery"
diff --git a/Documentation/devicetree/bindings/power_supply/sbs_sbs-battery.txt b/Documentation/devicetree/bindings/power_supply/sbs_sbs-battery.txt
new file mode 100644 (file)
index 0000000..c40e892
--- /dev/null
@@ -0,0 +1,23 @@
+SBS sbs-battery
+~~~~~~~~~~
+
+Required properties :
+ - compatible : "sbs,sbs-battery"
+
+Optional properties :
+ - sbs,i2c-retry-count : The number of times to retry i2c transactions on i2c
+   IO failure.
+ - sbs,poll-retry-count : The number of times to try looking for new status
+   after an external change notification.
+ - sbs,battery-detect-gpios : The gpio which signals battery detection and
+   a flag specifying its polarity.
+
+Example:
+
+       bq20z75@b {
+               compatible = "sbs,sbs-battery";
+               reg = < 0xb >;
+               sbs,i2c-retry-count = <2>;
+               sbs,poll-retry-count = <10>;
+               sbs,battery-detect-gpios = <&gpio-controller 122 1>;
+       }
index e855278..9f7bef6 100644 (file)
@@ -32,6 +32,7 @@ powervr       Imagination Technologies
 qcom   Qualcomm, Inc.
 ramtron        Ramtron International
 samsung        Samsung Semiconductor
+sbs    Smart Battery System
 schindler      Schindler
 simtek
 sirf   SiRF Technology, Inc.
diff --git a/Documentation/power/charger-manager.txt b/Documentation/power/charger-manager.txt
new file mode 100644 (file)
index 0000000..fdcca99
--- /dev/null
@@ -0,0 +1,163 @@
+Charger Manager
+       (C) 2011 MyungJoo Ham <myungjoo.ham@samsung.com>, GPL
+
+Charger Manager provides in-kernel battery charger management that
+requires temperature monitoring during suspend-to-RAM state
+and where each battery may have multiple chargers attached and the userland
+wants to look at the aggregated information of the multiple chargers.
+
+Charger Manager is a platform_driver with power-supply-class entries.
+An instance of Charger Manager (a platform-device created with Charger-Manager)
+represents an independent battery with chargers. If there are multiple
+batteries with their own chargers acting independently in a system,
+the system may need multiple instances of Charger Manager.
+
+1. Introduction
+===============
+
+Charger Manager supports the following:
+
+* Support for multiple chargers (e.g., a device with USB, AC, and solar panels)
+       A system may have multiple chargers (or power sources) and some of
+       they may be activated at the same time. Each charger may have its
+       own power-supply-class and each power-supply-class can provide
+       different information about the battery status. This framework
+       aggregates charger-related information from multiple sources and
+       shows combined information as a single power-supply-class.
+
+* Support for in suspend-to-RAM polling (with suspend_again callback)
+       While the battery is being charged and the system is in suspend-to-RAM,
+       we may need to monitor the battery health by looking at the ambient or
+       battery temperature. We can accomplish this by waking up the system
+       periodically. However, such a method wakes up devices unncessary for
+       monitoring the battery health and tasks, and user processes that are
+       supposed to be kept suspended. That, in turn, incurs unnecessary power
+       consumption and slow down charging process. Or even, such peak power
+       consumption can stop chargers in the middle of charging
+       (external power input < device power consumption), which not
+       only affects the charging time, but the lifespan of the battery.
+
+       Charger Manager provides a function "cm_suspend_again" that can be
+       used as suspend_again callback of platform_suspend_ops. If the platform
+       requires tasks other than cm_suspend_again, it may implement its own
+       suspend_again callback that calls cm_suspend_again in the middle.
+       Normally, the platform will need to resume and suspend some devices
+       that are used by Charger Manager.
+
+2. Global Charger-Manager Data related with suspend_again
+========================================================
+In order to setup Charger Manager with suspend-again feature
+(in-suspend monitoring), the user should provide charger_global_desc
+with setup_charger_manager(struct charger_global_desc *).
+This charger_global_desc data for in-suspend monitoring is global
+as the name suggests. Thus, the user needs to provide only once even
+if there are multiple batteries. If there are multiple batteries, the
+multiple instances of Charger Manager share the same charger_global_desc
+and it will manage in-suspend monitoring for all instances of Charger Manager.
+
+The user needs to provide all the two entries properly in order to activate
+in-suspend monitoring:
+
+struct charger_global_desc {
+
+char *rtc_name;
+       : The name of rtc (e.g., "rtc0") used to wakeup the system from
+       suspend for Charger Manager. The alarm interrupt (AIE) of the rtc
+       should be able to wake up the system from suspend. Charger Manager
+       saves and restores the alarm value and use the previously-defined
+       alarm if it is going to go off earlier than Charger Manager so that
+       Charger Manager does not interfere with previously-defined alarms.
+
+bool (*rtc_only_wakeup)(void);
+       : This callback should let CM know whether
+       the wakeup-from-suspend is caused only by the alarm of "rtc" in the
+       same struct. If there is any other wakeup source triggered the
+       wakeup, it should return false. If the "rtc" is the only wakeup
+       reason, it should return true.
+};
+
+3. How to setup suspend_again
+=============================
+Charger Manager provides a function "extern bool cm_suspend_again(void)".
+When cm_suspend_again is called, it monitors every battery. The suspend_ops
+callback of the system's platform_suspend_ops can call cm_suspend_again
+function to know whether Charger Manager wants to suspend again or not.
+If there are no other devices or tasks that want to use suspend_again
+feature, the platform_suspend_ops may directly refer to cm_suspend_again
+for its suspend_again callback.
+
+The cm_suspend_again() returns true (meaning "I want to suspend again")
+if the system was woken up by Charger Manager and the polling
+(in-suspend monitoring) results in "normal".
+
+4. Charger-Manager Data (struct charger_desc)
+=============================================
+For each battery charged independently from other batteries (if a series of
+batteries are charged by a single charger, they are counted as one independent
+battery), an instance of Charger Manager is attached to it.
+
+struct charger_desc {
+
+char *psy_name;
+       : The power-supply-class name of the battery. Default is
+       "battery" if psy_name is NULL. Users can access the psy entries
+       at "/sys/class/power_supply/[psy_name]/".
+
+enum polling_modes polling_mode;
+       : CM_POLL_DISABLE: do not poll this battery.
+         CM_POLL_ALWAYS: always poll this battery.
+         CM_POLL_EXTERNAL_POWER_ONLY: poll this battery if and only if
+                                      an external power source is attached.
+         CM_POLL_CHARGING_ONLY: poll this battery if and only if the
+                                battery is being charged.
+
+unsigned int fullbatt_uV;
+       : If specified with a non-zero value, Charger Manager assumes
+       that the battery is full (capacity = 100) if the battery is not being
+       charged and the battery voltage is equal to or greater than
+       fullbatt_uV.
+
+unsigned int polling_interval_ms;
+       : Required polling interval in ms. Charger Manager will poll
+       this battery every polling_interval_ms or more frequently.
+
+enum data_source battery_present;
+       CM_FUEL_GAUGE: get battery presence information from fuel gauge.
+       CM_CHARGER_STAT: get battery presence from chargers.
+
+char **psy_charger_stat;
+       : An array ending with NULL that has power-supply-class names of
+       chargers. Each power-supply-class should provide "PRESENT" (if
+       battery_present is "CM_CHARGER_STAT"), "ONLINE" (shows whether an
+       external power source is attached or not), and "STATUS" (shows whether
+       the battery is {"FULL" or not FULL} or {"FULL", "Charging",
+       "Discharging", "NotCharging"}).
+
+int num_charger_regulators;
+struct regulator_bulk_data *charger_regulators;
+       : Regulators representing the chargers in the form for
+       regulator framework's bulk functions.
+
+char *psy_fuel_gauge;
+       : Power-supply-class name of the fuel gauge.
+
+int (*temperature_out_of_range)(int *mC);
+bool measure_battery_temp;
+       : This callback returns 0 if the temperature is safe for charging,
+       a positive number if it is too hot to charge, and a negative number
+       if it is too cold to charge. With the variable mC, the callback returns
+       the temperature in 1/1000 of centigrade.
+       The source of temperature can be battery or ambient one according to
+       the value of measure_battery_temp.
+};
+
+5. Other Considerations
+=======================
+
+At the charger/battery-related events such as battery-pulled-out,
+charger-pulled-out, charger-inserted, DCIN-over/under-voltage, charger-stopped,
+and others critical to chargers, the system should be configured to wake up.
+At least the following should wake up the system from a suspend:
+a) charger-on/off b) external-power-in/out c) battery-in/out (while charging)
+
+It is usually accomplished by configuring the PMIC as a wakeup source.
index ba1aa07..e6c16f7 100644 (file)
@@ -940,6 +940,9 @@ static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_2[] = {
        },
 #endif
        {
+               I2C_BOARD_INFO("bq27200", 0x55),
+       },
+       {
                I2C_BOARD_INFO("tpa6130a2", 0x60),
                .platform_data = &rx51_tpa6130a2_data,
        }
index 17bb88f..3d28e49 100644 (file)
@@ -47,12 +47,14 @@ static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 };
 
 static enum power_supply_property wacom_battery_props[] = {
        POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_CAPACITY
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_SCOPE,
 };
 
 static enum power_supply_property wacom_ac_props[] = {
        POWER_SUPPLY_PROP_PRESENT,
-       POWER_SUPPLY_PROP_ONLINE
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_SCOPE,
 };
 
 static int wacom_battery_get_property(struct power_supply *psy,
@@ -68,6 +70,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = 1;
                break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
        case POWER_SUPPLY_PROP_CAPACITY:
                /* show 100% battery capacity when charging */
                if (power_state == 0)
@@ -99,6 +104,9 @@ static int wacom_ac_get_property(struct power_supply *psy,
                else
                        val->intval = 0;
                break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
        default:
                ret = -EINVAL;
                break;
@@ -389,6 +397,8 @@ static int wacom_probe(struct hid_device *hdev,
        wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
        wdata->battery.use_for_apm = 0;
 
+       power_supply_powers(&wdata->battery, &hdev->dev);
+
        ret = power_supply_register(&hdev->dev, &wdata->battery);
        if (ret) {
                hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
@@ -403,6 +413,8 @@ static int wacom_probe(struct hid_device *hdev,
        wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
        wdata->ac.use_for_apm = 0;
 
+       power_supply_powers(&wdata->battery, &hdev->dev);
+
        ret = power_supply_register(&hdev->dev, &wdata->ac);
        if (ret) {
                hid_warn(hdev,
index 76739c0..fc0b4db 100644 (file)
@@ -136,7 +136,8 @@ static __u16 wiiproto_keymap[] = {
 };
 
 static enum power_supply_property wiimote_battery_props[] = {
-       POWER_SUPPLY_PROP_CAPACITY
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_SCOPE,
 };
 
 /* requires the state.lock spinlock to be held */
@@ -468,6 +469,11 @@ static int wiimote_battery_get_property(struct power_supply *psy,
        int ret = 0, state;
        unsigned long flags;
 
+       if (psp == POWER_SUPPLY_PROP_SCOPE) {
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               return 0;
+       }
+
        ret = wiimote_cmd_acquire(wdata);
        if (ret)
                return ret;
@@ -1257,6 +1263,8 @@ static int wiimote_hid_probe(struct hid_device *hdev,
        wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
        wdata->battery.use_for_apm = 0;
 
+       power_supply_powers(&wdata->battery, &hdev->dev);
+
        ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
        if (ret) {
                hid_err(hdev, "Cannot register battery device\n");
index e1e59c9..ca881ef 100644 (file)
@@ -210,21 +210,6 @@ static struct max8925_irq_data max8925_irqs[] = {
                .mask_reg       = MAX8925_CHG_IRQ1_MASK,
                .offs           = 1 << 2,
        },
-       [MAX8925_IRQ_VCHG_USB_OVP] = {
-               .reg            = MAX8925_CHG_IRQ1,
-               .mask_reg       = MAX8925_CHG_IRQ1_MASK,
-               .offs           = 1 << 3,
-       },
-       [MAX8925_IRQ_VCHG_USB_F] =  {
-               .reg            = MAX8925_CHG_IRQ1,
-               .mask_reg       = MAX8925_CHG_IRQ1_MASK,
-               .offs           = 1 << 4,
-       },
-       [MAX8925_IRQ_VCHG_USB_R] = {
-               .reg            = MAX8925_CHG_IRQ1,
-               .mask_reg       = MAX8925_CHG_IRQ1_MASK,
-               .offs           = 1 << 5,
-       },
        [MAX8925_IRQ_VCHG_THM_OK_R] = {
                .reg            = MAX8925_CHG_IRQ2,
                .mask_reg       = MAX8925_CHG_IRQ2_MASK,
index 9f88641..3a8daf8 100644 (file)
@@ -116,12 +116,12 @@ config BATTERY_WM97XX
        help
          Say Y to enable support for battery measured by WM97xx aux port.
 
-config BATTERY_BQ20Z75
-        tristate "TI BQ20z75 gas gauge"
+config BATTERY_SBS
+        tristate "SBS Compliant gas gauge"
         depends on I2C
         help
-         Say Y to include support for TI BQ20z75 SBS-compliant
-         gas gauge and protection IC.
+         Say Y to include support for SBS battery driver for SBS-compliant
+         gas gauges.
 
 config BATTERY_BQ27x00
        tristate "BQ27x00 battery driver"
@@ -150,6 +150,14 @@ config BATTERY_DA9030
          Say Y here to enable support for batteries charger integrated into
          DA9030 PMIC.
 
+config BATTERY_DA9052
+       tristate "Dialog DA9052 Battery"
+       depends on PMIC_DA9052
+       depends on BROKEN
+       help
+         Say Y here to enable support for batteries charger integrated into
+         DA9052 PMIC.
+
 config BATTERY_MAX17040
        tristate "Maxim MAX17040 Fuel Gauge"
        depends on I2C
@@ -226,6 +234,12 @@ config CHARGER_TWL4030
        help
          Say Y here to enable support for TWL4030 Battery Charge Interface.
 
+config CHARGER_LP8727
+       tristate "National Semiconductor LP8727 charger driver"
+       depends on I2C
+       help
+         Say Y here to enable support for LP8727 Charger Driver.
+
 config CHARGER_GPIO
        tristate "GPIO charger"
        depends on GPIOLIB
@@ -236,6 +250,16 @@ config CHARGER_GPIO
          This driver can be build as a module. If so, the module will be
          called gpio-charger.
 
+config CHARGER_MANAGER
+       bool "Battery charger manager for multiple chargers"
+       depends on REGULATOR && RTC_CLASS
+       help
+          Say Y to enable charger-manager support, which allows multiple
+          chargers attached to a battery and multiple batteries attached to a
+          system. The charger-manager also can monitor charging status in
+          runtime and in suspend-to-RAM by waking up the system periodically
+          with help of suspend_again support.
+
 config CHARGER_MAX8997
        tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
        depends on MFD_MAX8997 && REGULATOR_MAX8997
index b4af13d..e429008 100644 (file)
@@ -22,9 +22,10 @@ obj-$(CONFIG_BATTERY_OLPC)   += olpc_battery.o
 obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
 obj-$(CONFIG_BATTERY_COLLIE)   += collie_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
-obj-$(CONFIG_BATTERY_BQ20Z75)  += bq20z75.o
+obj-$(CONFIG_BATTERY_SBS)      += sbs-battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
 obj-$(CONFIG_BATTERY_DA9030)   += da9030_battery.o
+obj-$(CONFIG_BATTERY_DA9052)   += da9052-battery.o
 obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
 obj-$(CONFIG_BATTERY_MAX17042) += max17042_battery.o
 obj-$(CONFIG_BATTERY_Z2)       += z2_battery.o
@@ -35,6 +36,8 @@ obj-$(CONFIG_BATTERY_INTEL_MID)       += intel_mid_battery.o
 obj-$(CONFIG_CHARGER_ISP1704)  += isp1704_charger.o
 obj-$(CONFIG_CHARGER_MAX8903)  += max8903_charger.o
 obj-$(CONFIG_CHARGER_TWL4030)  += twl4030_charger.o
+obj-$(CONFIG_CHARGER_LP8727)   += lp8727_charger.o
 obj-$(CONFIG_CHARGER_GPIO)     += gpio-charger.o
+obj-$(CONFIG_CHARGER_MANAGER)  += charger-manager.o
 obj-$(CONFIG_CHARGER_MAX8997)  += max8997_charger.o
 obj-$(CONFIG_CHARGER_MAX8998)  += max8998_charger.o
index bb16f5b..98bf567 100644 (file)
 
 #define BQ27000_REG_RSOC               0x0B /* Relative State-of-Charge */
 #define BQ27000_REG_ILMD               0x76 /* Initial last measured discharge */
-#define BQ27000_FLAG_CHGS              BIT(7)
+#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_FC                        BIT(9)
+#define BQ27500_FLAG_DSG               BIT(0) /* Discharging */
+#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_CHG               BIT(8) /* Charging */
+#define BQ27500_FLAG_FC                        BIT(9) /* Fully charged */
 
 #define BQ27000_RS                     20 /* Resistor sense */
 
@@ -79,9 +85,8 @@ struct bq27x00_reg_cache {
        int charge_full;
        int cycle_count;
        int capacity;
+       int energy;
        int flags;
-
-       int current_now;
 };
 
 struct bq27x00_device_info {
@@ -108,6 +113,7 @@ static enum power_supply_property bq27x00_battery_props[] = {
        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,
@@ -149,7 +155,7 @@ static int bq27x00_battery_read_rsoc(struct bq27x00_device_info *di)
                rsoc = bq27x00_read(di, BQ27000_REG_RSOC, true);
 
        if (rsoc < 0)
-               dev_err(di->dev, "error reading relative State-of-Charge\n");
+               dev_dbg(di->dev, "error reading relative State-of-Charge\n");
 
        return rsoc;
 }
@@ -164,7 +170,8 @@ static int bq27x00_battery_read_charge(struct bq27x00_device_info *di, u8 reg)
 
        charge = bq27x00_read(di, reg, false);
        if (charge < 0) {
-               dev_err(di->dev, "error reading nominal available capacity\n");
+               dev_dbg(di->dev, "error reading charge register %02x: %d\n",
+                       reg, charge);
                return charge;
        }
 
@@ -208,7 +215,7 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
                ilmd = bq27x00_read(di, BQ27000_REG_ILMD, true);
 
        if (ilmd < 0) {
-               dev_err(di->dev, "error reading initial last measured discharge\n");
+               dev_dbg(di->dev, "error reading initial last measured discharge\n");
                return ilmd;
        }
 
@@ -221,6 +228,50 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
 }
 
 /*
+ * 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 Celsius
+ * 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 (di->chip == BQ27500)
+               temp -= 2731;
+       else
+               temp = ((temp * 5) - 5463) / 2;
+
+       return temp;
+}
+
+/*
  * Return the battery Cycle count total
  * Or < 0 if something fails.
  */
@@ -245,7 +296,8 @@ static int bq27x00_battery_read_time(struct bq27x00_device_info *di, u8 reg)
 
        tval = bq27x00_read(di, reg, false);
        if (tval < 0) {
-               dev_err(di->dev, "error reading register %02x: %d\n", reg, tval);
+               dev_dbg(di->dev, "error reading time register %02x: %d\n",
+                       reg, tval);
                return tval;
        }
 
@@ -262,25 +314,31 @@ static void bq27x00_update(struct bq27x00_device_info *di)
 
        cache.flags = bq27x00_read(di, BQ27x00_REG_FLAGS, is_bq27500);
        if (cache.flags >= 0) {
-               cache.capacity = bq27x00_battery_read_rsoc(di);
-               cache.temperature = bq27x00_read(di, BQ27x00_REG_TEMP, false);
-               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);
+               if (!is_bq27500 && (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;
+               } else {
+                       cache.capacity = bq27x00_battery_read_rsoc(di);
+                       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.temperature = bq27x00_battery_read_temperature(di);
                cache.cycle_count = bq27x00_battery_read_cyct(di);
 
-               if (!is_bq27500)
-                       cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false);
-
                /* We only have to read charge design full once */
                if (di->charge_design_full <= 0)
                        di->charge_design_full = bq27x00_battery_read_ilmd(di);
        }
 
-       /* Ignore current_now which is a snapshot of the current battery state
-        * and is likely to be different even between two consecutive reads */
-       if (memcmp(&di->cache, &cache, sizeof(cache) - sizeof(int)) != 0) {
+       if (memcmp(&di->cache, &cache, sizeof(cache)) != 0) {
                di->cache = cache;
                power_supply_changed(&di->bat);
        }
@@ -302,25 +360,6 @@ static void bq27x00_battery_poll(struct work_struct *work)
        }
 }
 
-
-/*
- * Return the battery temperature in tenths of degree Celsius
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_temperature(struct bq27x00_device_info *di,
-       union power_supply_propval *val)
-{
-       if (di->cache.temperature < 0)
-               return di->cache.temperature;
-
-       if (di->chip == BQ27500)
-               val->intval = di->cache.temperature - 2731;
-       else
-               val->intval = ((di->cache.temperature * 5) - 5463) / 2;
-
-       return 0;
-}
-
 /*
  * Return the battery average current in ÂµA
  * Note that current can be negative signed as well
@@ -330,20 +369,20 @@ static int bq27x00_battery_current(struct bq27x00_device_info *di,
        union power_supply_propval *val)
 {
        int curr;
+       int flags;
 
-       if (di->chip == BQ27500)
-           curr = bq27x00_read(di, BQ27x00_REG_AI, false);
-       else
-           curr = di->cache.current_now;
-
-       if (curr < 0)
+       curr = bq27x00_read(di, BQ27x00_REG_AI, false);
+       if (curr < 0) {
+               dev_err(di->dev, "error reading current\n");
                return curr;
+       }
 
        if (di->chip == BQ27500) {
                /* bq27500 returns signed value */
                val->intval = (int)((s16)curr) * 1000;
        } else {
-               if (di->cache.flags & BQ27000_FLAG_CHGS) {
+               flags = bq27x00_read(di, BQ27x00_REG_FLAGS, false);
+               if (flags & BQ27000_FLAG_CHGS) {
                        dev_dbg(di->dev, "negative current!\n");
                        curr = -curr;
                }
@@ -362,10 +401,14 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
        if (di->chip == BQ27500) {
                if (di->cache.flags & BQ27500_FLAG_FC)
                        status = POWER_SUPPLY_STATUS_FULL;
-               else if (di->cache.flags & BQ27500_FLAG_DSC)
+               else if (di->cache.flags & BQ27500_FLAG_DSG)
                        status = POWER_SUPPLY_STATUS_DISCHARGING;
-               else
+               else if (di->cache.flags & BQ27500_FLAG_CHG)
                        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_UNKNOWN;
        } else {
                if (di->cache.flags & BQ27000_FLAG_FC)
                        status = POWER_SUPPLY_STATUS_FULL;
@@ -382,50 +425,56 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
        return 0;
 }
 
-/*
- * Return the battery Voltage in milivolts
- * Or < 0 if something fails.
- */
-static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
+static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
        union power_supply_propval *val)
 {
-       int volt;
+       int level;
 
-       volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
-       if (volt < 0)
-               return volt;
+       if (di->chip == BQ27500) {
+               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 = volt * 1000;
+       val->intval = level;
 
        return 0;
 }
 
 /*
- * Return the battery Available energy in ÂµWh
+ * Return the battery Voltage in milivolts
  * Or < 0 if something fails.
  */
-static int bq27x00_battery_energy(struct bq27x00_device_info *di,
+static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
        union power_supply_propval *val)
 {
-       int ae;
+       int volt;
 
-       ae = bq27x00_read(di, BQ27x00_REG_AE, false);
-       if (ae < 0) {
-               dev_err(di->dev, "error reading available energy\n");
-               return ae;
+       volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
+       if (volt < 0) {
+               dev_err(di->dev, "error reading voltage\n");
+               return volt;
        }
 
-       if (di->chip == BQ27500)
-               ae *= 1000;
-       else
-               ae = ae * 29200 / BQ27000_RS;
-
-       val->intval = ae;
+       val->intval = volt * 1000;
 
        return 0;
 }
 
-
 static int bq27x00_simple_value(int value,
        union power_supply_propval *val)
 {
@@ -473,8 +522,11 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
        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_battery_temperature(di, val);
+               ret = bq27x00_simple_value(di->cache.temperature, val);
                break;
        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
                ret = bq27x00_simple_value(di->cache.time_to_empty, val);
@@ -501,7 +553,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
                ret = bq27x00_simple_value(di->cache.cycle_count, val);
                break;
        case POWER_SUPPLY_PROP_ENERGY_NOW:
-               ret = bq27x00_battery_energy(di, val);
+               ret = bq27x00_simple_value(di->cache.energy, val);
                break;
        default:
                return -EINVAL;
@@ -546,6 +598,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
 
 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);
diff --git a/drivers/power/charger-manager.c b/drivers/power/charger-manager.c
new file mode 100644 (file)
index 0000000..0378d01
--- /dev/null
@@ -0,0 +1,1072 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This driver enables to monitor battery health and control charger
+ * during suspend-to-mem.
+ * Charger manager depends on other devices. register this later than
+ * the depending devices.
+ *
+ * This program 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.
+**/
+
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/rtc.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/power/charger-manager.h>
+#include <linux/regulator/consumer.h>
+
+/*
+ * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
+ * delayed works so that we can run delayed works with CM_JIFFIES_SMALL
+ * without any delays.
+ */
+#define        CM_JIFFIES_SMALL        (2)
+
+/* If y is valid (> 0) and smaller than x, do x = y */
+#define CM_MIN_VALID(x, y)     x = (((y > 0) && ((x) > (y))) ? (y) : (x))
+
+/*
+ * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
+ * rtc alarm. It should be 2 or larger
+ */
+#define CM_RTC_SMALL           (2)
+
+#define UEVENT_BUF_SIZE                32
+
+static LIST_HEAD(cm_list);
+static DEFINE_MUTEX(cm_list_mtx);
+
+/* About in-suspend (suspend-again) monitoring */
+static struct rtc_device *rtc_dev;
+/*
+ * Backup RTC alarm
+ * Save the wakeup alarm before entering suspend-to-RAM
+ */
+static struct rtc_wkalrm rtc_wkalarm_save;
+/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
+static unsigned long rtc_wkalarm_save_time;
+static bool cm_suspended;
+static bool cm_rtc_set;
+static unsigned long cm_suspend_duration_ms;
+
+/* Global charger-manager description */
+static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
+
+/**
+ * is_batt_present - See if the battery presents in place.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_batt_present(struct charger_manager *cm)
+{
+       union power_supply_propval val;
+       bool present = false;
+       int i, ret;
+
+       switch (cm->desc->battery_present) {
+       case CM_FUEL_GAUGE:
+               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                               POWER_SUPPLY_PROP_PRESENT, &val);
+               if (ret == 0 && val.intval)
+                       present = true;
+               break;
+       case CM_CHARGER_STAT:
+               for (i = 0; cm->charger_stat[i]; i++) {
+                       ret = cm->charger_stat[i]->get_property(
+                                       cm->charger_stat[i],
+                                       POWER_SUPPLY_PROP_PRESENT, &val);
+                       if (ret == 0 && val.intval) {
+                               present = true;
+                               break;
+                       }
+               }
+               break;
+       }
+
+       return present;
+}
+
+/**
+ * is_ext_pwr_online - See if an external power source is attached to charge
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Returns true if at least one of the chargers of the battery has an external
+ * power source attached to charge the battery regardless of whether it is
+ * actually charging or not.
+ */
+static bool is_ext_pwr_online(struct charger_manager *cm)
+{
+       union power_supply_propval val;
+       bool online = false;
+       int i, ret;
+
+       /* If at least one of them has one, it's yes. */
+       for (i = 0; cm->charger_stat[i]; i++) {
+               ret = cm->charger_stat[i]->get_property(
+                               cm->charger_stat[i],
+                               POWER_SUPPLY_PROP_ONLINE, &val);
+               if (ret == 0 && val.intval) {
+                       online = true;
+                       break;
+               }
+       }
+
+       return online;
+}
+
+/**
+ * get_batt_uV - Get the voltage level of the battery
+ * @cm: the Charger Manager representing the battery.
+ * @uV: the voltage level returned.
+ *
+ * Returns 0 if there is no error.
+ * Returns a negative value on error.
+ */
+static int get_batt_uV(struct charger_manager *cm, int *uV)
+{
+       union power_supply_propval val;
+       int ret;
+
+       if (cm->fuel_gauge)
+               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                               POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
+       else
+               return -ENODEV;
+
+       if (ret)
+               return ret;
+
+       *uV = val.intval;
+       return 0;
+}
+
+/**
+ * is_charging - Returns true if the battery is being charged.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_charging(struct charger_manager *cm)
+{
+       int i, ret;
+       bool charging = false;
+       union power_supply_propval val;
+
+       /* If there is no battery, it cannot be charged */
+       if (!is_batt_present(cm))
+               return false;
+
+       /* If at least one of the charger is charging, return yes */
+       for (i = 0; cm->charger_stat[i]; i++) {
+               /* 1. The charger sholuld not be DISABLED */
+               if (cm->emergency_stop)
+                       continue;
+               if (!cm->charger_enabled)
+                       continue;
+
+               /* 2. The charger should be online (ext-power) */
+               ret = cm->charger_stat[i]->get_property(
+                               cm->charger_stat[i],
+                               POWER_SUPPLY_PROP_ONLINE, &val);
+               if (ret) {
+                       dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+               if (val.intval == 0)
+                       continue;
+
+               /*
+                * 3. The charger should not be FULL, DISCHARGING,
+                * or NOT_CHARGING.
+                */
+               ret = cm->charger_stat[i]->get_property(
+                               cm->charger_stat[i],
+                               POWER_SUPPLY_PROP_STATUS, &val);
+               if (ret) {
+                       dev_warn(cm->dev, "Cannot read STATUS value from %s.\n",
+                                       cm->desc->psy_charger_stat[i]);
+                       continue;
+               }
+               if (val.intval == POWER_SUPPLY_STATUS_FULL ||
+                               val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
+                               val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
+                       continue;
+
+               /* Then, this is charging. */
+               charging = true;
+               break;
+       }
+
+       return charging;
+}
+
+/**
+ * is_polling_required - Return true if need to continue polling for this CM.
+ * @cm: the Charger Manager representing the battery.
+ */
+static bool is_polling_required(struct charger_manager *cm)
+{
+       switch (cm->desc->polling_mode) {
+       case CM_POLL_DISABLE:
+               return false;
+       case CM_POLL_ALWAYS:
+               return true;
+       case CM_POLL_EXTERNAL_POWER_ONLY:
+               return is_ext_pwr_online(cm);
+       case CM_POLL_CHARGING_ONLY:
+               return is_charging(cm);
+       default:
+               dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
+                       cm->desc->polling_mode);
+       }
+
+       return false;
+}
+
+/**
+ * try_charger_enable - Enable/Disable chargers altogether
+ * @cm: the Charger Manager representing the battery.
+ * @enable: true: enable / false: disable
+ *
+ * Note that Charger Manager keeps the charger enabled regardless whether
+ * the charger is charging or not (because battery is full or no external
+ * power source exists) except when CM needs to disable chargers forcibly
+ * bacause of emergency causes; when the battery is overheated or too cold.
+ */
+static int try_charger_enable(struct charger_manager *cm, bool enable)
+{
+       int err = 0, i;
+       struct charger_desc *desc = cm->desc;
+
+       /* Ignore if it's redundent command */
+       if (enable && cm->charger_enabled)
+               return 0;
+       if (!enable && !cm->charger_enabled)
+               return 0;
+
+       if (enable) {
+               if (cm->emergency_stop)
+                       return -EAGAIN;
+               err = regulator_bulk_enable(desc->num_charger_regulators,
+                                       desc->charger_regulators);
+       } else {
+               /*
+                * Abnormal battery state - Stop charging forcibly,
+                * even if charger was enabled at the other places
+                */
+               err = regulator_bulk_disable(desc->num_charger_regulators,
+                                       desc->charger_regulators);
+
+               for (i = 0; i < desc->num_charger_regulators; i++) {
+                       if (regulator_is_enabled(
+                                   desc->charger_regulators[i].consumer)) {
+                               regulator_force_disable(
+                                       desc->charger_regulators[i].consumer);
+                               dev_warn(cm->dev,
+                                       "Disable regulator(%s) forcibly.\n",
+                                       desc->charger_regulators[i].supply);
+                       }
+               }
+       }
+
+       if (!err)
+               cm->charger_enabled = enable;
+
+       return err;
+}
+
+/**
+ * uevent_notify - Let users know something has changed.
+ * @cm: the Charger Manager representing the battery.
+ * @event: the event string.
+ *
+ * If @event is null, it implies that uevent_notify is called
+ * by resume function. When called in the resume function, cm_suspended
+ * should be already reset to false in order to let uevent_notify
+ * notify the recent event during the suspend to users. While
+ * suspended, uevent_notify does not notify users, but tracks
+ * events so that uevent_notify can notify users later after resumed.
+ */
+static void uevent_notify(struct charger_manager *cm, const char *event)
+{
+       static char env_str[UEVENT_BUF_SIZE + 1] = "";
+       static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
+
+       if (cm_suspended) {
+               /* Nothing in suspended-event buffer */
+               if (env_str_save[0] == 0) {
+                       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
+                               return; /* status not changed */
+                       strncpy(env_str_save, event, UEVENT_BUF_SIZE);
+                       return;
+               }
+
+               if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
+                       return; /* Duplicated. */
+               else
+                       strncpy(env_str_save, event, UEVENT_BUF_SIZE);
+
+               return;
+       }
+
+       if (event == NULL) {
+               /* No messages pending */
+               if (!env_str_save[0])
+                       return;
+
+               strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
+               kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
+               env_str_save[0] = 0;
+
+               return;
+       }
+
+       /* status not changed */
+       if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
+               return;
+
+       /* save the status and notify the update */
+       strncpy(env_str, event, UEVENT_BUF_SIZE);
+       kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
+
+       dev_info(cm->dev, event);
+}
+
+/**
+ * _cm_monitor - Monitor the temperature and return true for exceptions.
+ * @cm: the Charger Manager representing the battery.
+ *
+ * Returns true if there is an event to notify for the battery.
+ * (True if the status of "emergency_stop" changes)
+ */
+static bool _cm_monitor(struct charger_manager *cm)
+{
+       struct charger_desc *desc = cm->desc;
+       int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
+
+       dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n",
+               cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
+
+       /* It has been stopped or charging already */
+       if (!!temp == !!cm->emergency_stop)
+               return false;
+
+       if (temp) {
+               cm->emergency_stop = temp;
+               if (!try_charger_enable(cm, false)) {
+                       if (temp > 0)
+                               uevent_notify(cm, "OVERHEAT");
+                       else
+                               uevent_notify(cm, "COLD");
+               }
+       } else {
+               cm->emergency_stop = 0;
+               if (!try_charger_enable(cm, true))
+                       uevent_notify(cm, "CHARGING");
+       }
+
+       return true;
+}
+
+/**
+ * cm_monitor - Monitor every battery.
+ *
+ * Returns true if there is an event to notify from any of the batteries.
+ * (True if the status of "emergency_stop" changes)
+ */
+static bool cm_monitor(void)
+{
+       bool stop = false;
+       struct charger_manager *cm;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry)
+               stop = stop || _cm_monitor(cm);
+
+       mutex_unlock(&cm_list_mtx);
+
+       return stop;
+}
+
+static int charger_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct charger_manager *cm = container_of(psy,
+                       struct charger_manager, charger_psy);
+       struct charger_desc *desc = cm->desc;
+       int i, ret = 0, uV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (is_charging(cm))
+                       val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               else if (is_ext_pwr_online(cm))
+                       val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               else
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               if (cm->emergency_stop > 0)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (cm->emergency_stop < 0)
+                       val->intval = POWER_SUPPLY_HEALTH_COLD;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               if (is_batt_present(cm))
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               ret = get_batt_uV(cm, &i);
+               val->intval = i;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                               POWER_SUPPLY_PROP_CURRENT_NOW, val);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               /* in thenth of centigrade */
+               if (cm->last_temp_mC == INT_MIN)
+                       desc->temperature_out_of_range(&cm->last_temp_mC);
+               val->intval = cm->last_temp_mC / 100;
+               if (!desc->measure_battery_temp)
+                       ret = -ENODEV;
+               break;
+       case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+               /* in thenth of centigrade */
+               if (cm->last_temp_mC == INT_MIN)
+                       desc->temperature_out_of_range(&cm->last_temp_mC);
+               val->intval = cm->last_temp_mC / 100;
+               if (desc->measure_battery_temp)
+                       ret = -ENODEV;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (!cm->fuel_gauge) {
+                       ret = -ENODEV;
+                       break;
+               }
+
+               if (!is_batt_present(cm)) {
+                       /* There is no battery. Assume 100% */
+                       val->intval = 100;
+                       break;
+               }
+
+               ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                                       POWER_SUPPLY_PROP_CAPACITY, val);
+               if (ret)
+                       break;
+
+               if (val->intval > 100) {
+                       val->intval = 100;
+                       break;
+               }
+               if (val->intval < 0)
+                       val->intval = 0;
+
+               /* Do not adjust SOC when charging: voltage is overrated */
+               if (is_charging(cm))
+                       break;
+
+               /*
+                * If the capacity value is inconsistent, calibrate it base on
+                * the battery voltage values and the thresholds given as desc
+                */
+               ret = get_batt_uV(cm, &uV);
+               if (ret) {
+                       /* Voltage information not available. No calibration */
+                       ret = 0;
+                       break;
+               }
+
+               if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
+                   !is_charging(cm)) {
+                       val->intval = 100;
+                       break;
+               }
+
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               if (is_ext_pwr_online(cm))
+                       val->intval = 1;
+               else
+                       val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_FULL:
+               if (cm->fuel_gauge) {
+                       if (cm->fuel_gauge->get_property(cm->fuel_gauge,
+                           POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0)
+                               break;
+               }
+
+               if (is_ext_pwr_online(cm)) {
+                       /* Not full if it's charging. */
+                       if (is_charging(cm)) {
+                               val->intval = 0;
+                               break;
+                       }
+                       /*
+                        * Full if it's powered but not charging andi
+                        * not forced stop by emergency
+                        */
+                       if (!cm->emergency_stop) {
+                               val->intval = 1;
+                               break;
+                       }
+               }
+
+               /* Full if it's over the fullbatt voltage */
+               ret = get_batt_uV(cm, &uV);
+               if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
+                   !is_charging(cm)) {
+                       val->intval = 1;
+                       break;
+               }
+
+               /* Full if the cap is 100 */
+               if (cm->fuel_gauge) {
+                       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                                       POWER_SUPPLY_PROP_CAPACITY, val);
+                       if (!ret && val->intval >= 100 && !is_charging(cm)) {
+                               val->intval = 1;
+                               break;
+                       }
+               }
+
+               val->intval = 0;
+               ret = 0;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               if (is_charging(cm)) {
+                       ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
+                                               POWER_SUPPLY_PROP_CHARGE_NOW,
+                                               val);
+                       if (ret) {
+                               val->intval = 1;
+                               ret = 0;
+                       } else {
+                               /* If CHARGE_NOW is supplied, use it */
+                               val->intval = (val->intval > 0) ?
+                                               val->intval : 1;
+                       }
+               } else {
+                       val->intval = 0;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+
+#define NUM_CHARGER_PSY_OPTIONAL       (4)
+static enum power_supply_property default_charger_props[] = {
+       /* Guaranteed to provide */
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_CHARGE_FULL,
+       /*
+        * Optional properties are:
+        * POWER_SUPPLY_PROP_CHARGE_NOW,
+        * POWER_SUPPLY_PROP_CURRENT_NOW,
+        * POWER_SUPPLY_PROP_TEMP, and
+        * POWER_SUPPLY_PROP_TEMP_AMBIENT,
+        */
+};
+
+static struct power_supply psy_default = {
+       .name = "battery",
+       .type = POWER_SUPPLY_TYPE_BATTERY,
+       .properties = default_charger_props,
+       .num_properties = ARRAY_SIZE(default_charger_props),
+       .get_property = charger_get_property,
+};
+
+/**
+ * cm_setup_timer - For in-suspend monitoring setup wakeup alarm
+ *                 for suspend_again.
+ *
+ * Returns true if the alarm is set for Charger Manager to use.
+ * Returns false if
+ *     cm_setup_timer fails to set an alarm,
+ *     cm_setup_timer does not need to set an alarm for Charger Manager,
+ *     or an alarm previously configured is to be used.
+ */
+static bool cm_setup_timer(void)
+{
+       struct charger_manager *cm;
+       unsigned int wakeup_ms = UINT_MAX;
+       bool ret = false;
+
+       mutex_lock(&cm_list_mtx);
+
+       list_for_each_entry(cm, &cm_list, entry) {
+               /* Skip if polling is not required for this CM */
+               if (!is_polling_required(cm) && !cm->emergency_stop)
+                       continue;
+               if (cm->desc->polling_interval_ms == 0)
+                       continue;
+               CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
+       }
+
+       mutex_unlock(&cm_list_mtx);
+
+       if (wakeup_ms < UINT_MAX && wakeup_ms > 0) {
+               pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms);
+               if (rtc_dev) {
+                       struct rtc_wkalrm tmp;
+                       unsigned long time, now;
+                       unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
+
+                       /*
+                        * Set alarm with the polling interval (wakeup_ms)
+                        * except when rtc_wkalarm_save comes first.
+                        * However, the alarm time should be NOW +
+                        * CM_RTC_SMALL or later.
+                        */
+                       tmp.enabled = 1;
+                       rtc_read_time(rtc_dev, &tmp.time);
+                       rtc_tm_to_time(&tmp.time, &now);
+                       if (add < CM_RTC_SMALL)
+                               add = CM_RTC_SMALL;
+                       time = now + add;
+
+                       ret = true;
+
+                       if (rtc_wkalarm_save.enabled &&
+                           rtc_wkalarm_save_time &&
+                           rtc_wkalarm_save_time < time) {
+                               if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
+                                       time = now + CM_RTC_SMALL;
+                               else
+                                       time = rtc_wkalarm_save_time;
+
+                               /* The timer is not appointed by CM */
+                               ret = false;
+                       }
+
+                       pr_info("Waking up after %lu secs.\n",
+                                       time - now);
+
+                       rtc_time_to_tm(time, &tmp.time);
+                       rtc_set_alarm(rtc_dev, &tmp);
+                       cm_suspend_duration_ms += wakeup_ms;
+                       return ret;
+               }
+       }
+
+       if (rtc_dev)
+               rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
+       return false;
+}
+
+/**
+ * cm_suspend_again - Determine whether suspend again or not
+ *
+ * Returns true if the system should be suspended again
+ * Returns false if the system should be woken up
+ */
+bool cm_suspend_again(void)
+{
+       struct charger_manager *cm;
+       bool ret = false;
+
+       if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
+           !cm_rtc_set)
+               return false;
+
+       if (cm_monitor())
+               goto out;
+
+       ret = true;
+       mutex_lock(&cm_list_mtx);
+       list_for_each_entry(cm, &cm_list, entry) {
+               if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
+                   cm->status_save_batt != is_batt_present(cm))
+                       ret = false;
+       }
+       mutex_unlock(&cm_list_mtx);
+
+       cm_rtc_set = cm_setup_timer();
+out:
+       /* It's about the time when the non-CM appointed timer goes off */
+       if (rtc_wkalarm_save.enabled) {
+               unsigned long now;
+               struct rtc_time tmp;
+
+               rtc_read_time(rtc_dev, &tmp);
+               rtc_tm_to_time(&tmp, &now);
+
+               if (rtc_wkalarm_save_time &&
+                   now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
+                       return false;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(cm_suspend_again);
+
+/**
+ * setup_charger_manager - initialize charger_global_desc data
+ * @gd: pointer to instance of charger_global_desc
+ */
+int setup_charger_manager(struct charger_global_desc *gd)
+{
+       if (!gd)
+               return -EINVAL;
+
+       if (rtc_dev)
+               rtc_class_close(rtc_dev);
+       rtc_dev = NULL;
+       g_desc = NULL;
+
+       if (!gd->rtc_only_wakeup) {
+               pr_err("The callback rtc_only_wakeup is not given.\n");
+               return -EINVAL;
+       }
+
+       if (gd->rtc_name) {
+               rtc_dev = rtc_class_open(gd->rtc_name);
+               if (IS_ERR_OR_NULL(rtc_dev)) {
+                       rtc_dev = NULL;
+                       /* Retry at probe. RTC may be not registered yet */
+               }
+       } else {
+               pr_warn("No wakeup timer is given for charger manager."
+                       "In-suspend monitoring won't work.\n");
+       }
+
+       g_desc = gd;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(setup_charger_manager);
+
+static int charger_manager_probe(struct platform_device *pdev)
+{
+       struct charger_desc *desc = dev_get_platdata(&pdev->dev);
+       struct charger_manager *cm;
+       int ret = 0, i = 0;
+       union power_supply_propval val;
+
+       if (g_desc && !rtc_dev && g_desc->rtc_name) {
+               rtc_dev = rtc_class_open(g_desc->rtc_name);
+               if (IS_ERR_OR_NULL(rtc_dev)) {
+                       rtc_dev = NULL;
+                       dev_err(&pdev->dev, "Cannot get RTC %s.\n",
+                               g_desc->rtc_name);
+                       ret = -ENODEV;
+                       goto err_alloc;
+               }
+       }
+
+       if (!desc) {
+               dev_err(&pdev->dev, "No platform data (desc) found.\n");
+               ret = -ENODEV;
+               goto err_alloc;
+       }
+
+       cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
+       if (!cm) {
+               dev_err(&pdev->dev, "Cannot allocate memory.\n");
+               ret = -ENOMEM;
+               goto err_alloc;
+       }
+
+       /* Basic Values. Unspecified are Null or 0 */
+       cm->dev = &pdev->dev;
+       cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
+       if (!cm->desc) {
+               dev_err(&pdev->dev, "Cannot allocate memory.\n");
+               ret = -ENOMEM;
+               goto err_alloc_desc;
+       }
+       memcpy(cm->desc, desc, sizeof(struct charger_desc));
+       cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
+
+       if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
+               ret = -EINVAL;
+               dev_err(&pdev->dev, "charger_regulators undefined.\n");
+               goto err_no_charger;
+       }
+
+       if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
+               dev_err(&pdev->dev, "No power supply defined.\n");
+               ret = -EINVAL;
+               goto err_no_charger_stat;
+       }
+
+       /* Counting index only */
+       while (desc->psy_charger_stat[i])
+               i++;
+
+       cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
+                                  GFP_KERNEL);
+       if (!cm->charger_stat) {
+               ret = -ENOMEM;
+               goto err_no_charger_stat;
+       }
+
+       for (i = 0; desc->psy_charger_stat[i]; i++) {
+               cm->charger_stat[i] = power_supply_get_by_name(
+                                       desc->psy_charger_stat[i]);
+               if (!cm->charger_stat[i]) {
+                       dev_err(&pdev->dev, "Cannot find power supply "
+                                       "\"%s\"\n",
+                                       desc->psy_charger_stat[i]);
+                       ret = -ENODEV;
+                       goto err_chg_stat;
+               }
+       }
+
+       cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
+       if (!cm->fuel_gauge) {
+               dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
+                               desc->psy_fuel_gauge);
+               ret = -ENODEV;
+               goto err_chg_stat;
+       }
+
+       if (desc->polling_interval_ms == 0 ||
+           msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
+               dev_err(&pdev->dev, "polling_interval_ms is too small\n");
+               ret = -EINVAL;
+               goto err_chg_stat;
+       }
+
+       if (!desc->temperature_out_of_range) {
+               dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
+               ret = -EINVAL;
+               goto err_chg_stat;
+       }
+
+       platform_set_drvdata(pdev, cm);
+
+       memcpy(&cm->charger_psy, &psy_default,
+                               sizeof(psy_default));
+       if (!desc->psy_name) {
+               strncpy(cm->psy_name_buf, psy_default.name,
+                               PSY_NAME_MAX);
+       } else {
+               strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
+       }
+       cm->charger_psy.name = cm->psy_name_buf;
+
+       /* Allocate for psy properties because they may vary */
+       cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property)
+                               * (ARRAY_SIZE(default_charger_props) +
+                               NUM_CHARGER_PSY_OPTIONAL),
+                               GFP_KERNEL);
+       if (!cm->charger_psy.properties) {
+               dev_err(&pdev->dev, "Cannot allocate for psy properties.\n");
+               ret = -ENOMEM;
+               goto err_chg_stat;
+       }
+       memcpy(cm->charger_psy.properties, default_charger_props,
+               sizeof(enum power_supply_property) *
+               ARRAY_SIZE(default_charger_props));
+       cm->charger_psy.num_properties = psy_default.num_properties;
+
+       /* Find which optional psy-properties are available */
+       if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+                                         POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
+               cm->charger_psy.properties[cm->charger_psy.num_properties] =
+                               POWER_SUPPLY_PROP_CHARGE_NOW;
+               cm->charger_psy.num_properties++;
+       }
+       if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
+                                         POWER_SUPPLY_PROP_CURRENT_NOW,
+                                         &val)) {
+               cm->charger_psy.properties[cm->charger_psy.num_properties] =
+                               POWER_SUPPLY_PROP_CURRENT_NOW;
+               cm->charger_psy.num_properties++;
+       }
+       if (!desc->measure_battery_temp) {
+               cm->charger_psy.properties[cm->charger_psy.num_properties] =
+                               POWER_SUPPLY_PROP_TEMP_AMBIENT;
+               cm->charger_psy.num_properties++;
+       }
+       if (desc->measure_battery_temp) {
+               cm->charger_psy.properties[cm->charger_psy.num_properties] =
+                               POWER_SUPPLY_PROP_TEMP;
+               cm->charger_psy.num_properties++;
+       }
+
+       ret = power_supply_register(NULL, &cm->charger_psy);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot register charger-manager with"
+                               " name \"%s\".\n", cm->charger_psy.name);
+               goto err_register;
+       }
+
+       ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
+                                desc->charger_regulators);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot get charger regulators.\n");
+               goto err_bulk_get;
+       }
+
+       ret = try_charger_enable(cm, true);
+       if (ret) {
+               dev_err(&pdev->dev, "Cannot enable charger regulators\n");
+               goto err_chg_enable;
+       }
+
+       /* Add to the list */
+       mutex_lock(&cm_list_mtx);
+       list_add(&cm->entry, &cm_list);
+       mutex_unlock(&cm_list_mtx);
+
+       return 0;
+
+err_chg_enable:
+       if (desc->charger_regulators)
+               regulator_bulk_free(desc->num_charger_regulators,
+                                       desc->charger_regulators);
+err_bulk_get:
+       power_supply_unregister(&cm->charger_psy);
+err_register:
+       kfree(cm->charger_psy.properties);
+err_chg_stat:
+       kfree(cm->charger_stat);
+err_no_charger_stat:
+err_no_charger:
+       kfree(cm->desc);
+err_alloc_desc:
+       kfree(cm);
+err_alloc:
+       return ret;
+}
+
+static int __devexit charger_manager_remove(struct platform_device *pdev)
+{
+       struct charger_manager *cm = platform_get_drvdata(pdev);
+       struct charger_desc *desc = cm->desc;
+
+       /* Remove from the list */
+       mutex_lock(&cm_list_mtx);
+       list_del(&cm->entry);
+       mutex_unlock(&cm_list_mtx);
+
+       if (desc->charger_regulators)
+               regulator_bulk_free(desc->num_charger_regulators,
+                                       desc->charger_regulators);
+
+       power_supply_unregister(&cm->charger_psy);
+       kfree(cm->charger_psy.properties);
+       kfree(cm->charger_stat);
+       kfree(cm->desc);
+       kfree(cm);
+
+       return 0;
+}
+
+const struct platform_device_id charger_manager_id[] = {
+       { "charger-manager", 0 },
+       { },
+};
+
+static int cm_suspend_prepare(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device,
+                                                   dev);
+       struct charger_manager *cm = platform_get_drvdata(pdev);
+
+       if (!cm_suspended) {
+               if (rtc_dev) {
+                       struct rtc_time tmp;
+                       unsigned long now;
+
+                       rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
+                       rtc_read_time(rtc_dev, &tmp);
+
+                       if (rtc_wkalarm_save.enabled) {
+                               rtc_tm_to_time(&rtc_wkalarm_save.time,
+                                              &rtc_wkalarm_save_time);
+                               rtc_tm_to_time(&tmp, &now);
+                               if (now > rtc_wkalarm_save_time)
+                                       rtc_wkalarm_save_time = 0;
+                       } else {
+                               rtc_wkalarm_save_time = 0;
+                       }
+               }
+               cm_suspended = true;
+       }
+
+       cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
+       cm->status_save_batt = is_batt_present(cm);
+
+       if (!cm_rtc_set) {
+               cm_suspend_duration_ms = 0;
+               cm_rtc_set = cm_setup_timer();
+       }
+
+       return 0;
+}
+
+static void cm_suspend_complete(struct device *dev)
+{
+       struct platform_device *pdev = container_of(dev, struct platform_device,
+                                                   dev);
+       struct charger_manager *cm = platform_get_drvdata(pdev);
+
+       if (cm_suspended) {
+               if (rtc_dev) {
+                       struct rtc_wkalrm tmp;
+
+                       rtc_read_alarm(rtc_dev, &tmp);
+                       rtc_wkalarm_save.pending = tmp.pending;
+                       rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
+               }
+               cm_suspended = false;
+               cm_rtc_set = false;
+       }
+
+       uevent_notify(cm, NULL);
+}
+
+static const struct dev_pm_ops charger_manager_pm = {
+       .prepare        = cm_suspend_prepare,
+       .complete       = cm_suspend_complete,
+};
+
+static struct platform_driver charger_manager_driver = {
+       .driver = {
+               .name = "charger-manager",
+               .owner = THIS_MODULE,
+               .pm = &charger_manager_pm,
+       },
+       .probe = charger_manager_probe,
+       .remove = __devexit_p(charger_manager_remove),
+       .id_table = charger_manager_id,
+};
+
+static int __init charger_manager_init(void)
+{
+       return platform_driver_register(&charger_manager_driver);
+}
+late_initcall(charger_manager_init);
+
+static void __exit charger_manager_cleanup(void)
+{
+       platform_driver_unregister(&charger_manager_driver);
+}
+module_exit(charger_manager_cleanup);
+
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_DESCRIPTION("Charger Manager");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("charger-manager");
index 7a40fce..74c6b23 100644 (file)
@@ -277,18 +277,13 @@ static struct collie_bat collie_bat_bu = {
        .adc_temp_divider = -1,
 };
 
-static struct {
-       int gpio;
-       char *name;
-       bool output;
-       int value;
-} gpios[] = {
-       { COLLIE_GPIO_CO,               "main battery full",    0, 0 },
-       { COLLIE_GPIO_MAIN_BAT_LOW,     "main battery low",     0, 0 },
-       { COLLIE_GPIO_CHARGE_ON,        "main charge on",       1, 0 },
-       { COLLIE_GPIO_MBAT_ON,          "main battery",         1, 0 },
-       { COLLIE_GPIO_TMP_ON,           "main battery temp",    1, 0 },
-       { COLLIE_GPIO_BBAT_ON,          "backup battery",       1, 0 },
+static struct gpio collie_batt_gpios[] = {
+       { COLLIE_GPIO_CO,           GPIOF_IN,           "main battery full" },
+       { COLLIE_GPIO_MAIN_BAT_LOW, GPIOF_IN,           "main battery low" },
+       { COLLIE_GPIO_CHARGE_ON,    GPIOF_OUT_INIT_LOW, "main charge on" },
+       { COLLIE_GPIO_MBAT_ON,      GPIOF_OUT_INIT_LOW, "main battery" },
+       { COLLIE_GPIO_TMP_ON,       GPIOF_OUT_INIT_LOW, "main battery temp" },
+       { COLLIE_GPIO_BBAT_ON,      GPIOF_OUT_INIT_LOW, "backup battery" },
 };
 
 #ifdef CONFIG_PM
@@ -313,29 +308,16 @@ static int collie_bat_resume(struct ucb1x00_dev *dev)
 static int __devinit collie_bat_probe(struct ucb1x00_dev *dev)
 {
        int ret;
-       int i;
 
        if (!machine_is_collie())
                return -ENODEV;
 
        ucb = dev->ucb;
 
-       for (i = 0; i < ARRAY_SIZE(gpios); i++) {
-               ret = gpio_request(gpios[i].gpio, gpios[i].name);
-               if (ret) {
-                       i--;
-                       goto err_gpio;
-               }
-
-               if (gpios[i].output)
-                       ret = gpio_direction_output(gpios[i].gpio,
-                                       gpios[i].value);
-               else
-                       ret = gpio_direction_input(gpios[i].gpio);
-
-               if (ret)
-                       goto err_gpio;
-       }
+       ret = gpio_request_array(collie_batt_gpios,
+                                ARRAY_SIZE(collie_batt_gpios));
+       if (ret)
+               return ret;
 
        mutex_init(&collie_bat_main.work_lock);
 
@@ -363,19 +345,12 @@ err_psy_reg_main:
 
        /* see comment in collie_bat_remove */
        cancel_work_sync(&bat_work);
-
-       i--;
-err_gpio:
-       for (; i >= 0; i--)
-               gpio_free(gpios[i].gpio);
-
+       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
        return ret;
 }
 
 static void __devexit collie_bat_remove(struct ucb1x00_dev *dev)
 {
-       int i;
-
        free_irq(gpio_to_irq(COLLIE_GPIO_CO), &collie_bat_main);
 
        power_supply_unregister(&collie_bat_bu.psy);
@@ -387,9 +362,7 @@ static void __devexit collie_bat_remove(struct ucb1x00_dev *dev)
         * unregistered now.
         */
        cancel_work_sync(&bat_work);
-
-       for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--)
-               gpio_free(gpios[i].gpio);
+       gpio_free_array(collie_batt_gpios, ARRAY_SIZE(collie_batt_gpios));
 }
 
 static struct ucb1x00_driver collie_bat_driver = {
index d2c793c..3fd3e95 100644 (file)
@@ -588,18 +588,7 @@ static struct platform_driver da903x_battery_driver = {
        .remove = da9030_battery_remove,
 };
 
-static int da903x_battery_init(void)
-{
-       return platform_driver_register(&da903x_battery_driver);
-}
-
-static void da903x_battery_exit(void)
-{
-       platform_driver_unregister(&da903x_battery_driver);
-}
-
-module_init(da903x_battery_init);
-module_exit(da903x_battery_exit);
+module_platform_driver(da903x_battery_driver);
 
 MODULE_DESCRIPTION("DA9030 battery charger driver");
 MODULE_AUTHOR("Mike Rapoport, CompuLab");
diff --git a/drivers/power/da9052-battery.c b/drivers/power/da9052-battery.c
new file mode 100644 (file)
index 0000000..e8ea47a
--- /dev/null
@@ -0,0 +1,664 @@
+/*
+ * Batttery Driver for Dialog DA9052 PMICs
+ *
+ * Copyright(c) 2011 Dialog Semiconductor Ltd.
+ *
+ * Author: David Dajun Chen <dchen@diasemi.com>
+ *
+ * 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/delay.h>
+#include <linux/freezer.h>
+#include <linux/fs.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/mfd/da9052/da9052.h>
+#include <linux/mfd/da9052/pdata.h>
+#include <linux/mfd/da9052/reg.h>
+
+/* STATIC CONFIGURATION */
+#define DA9052_BAT_CUTOFF_VOLT         2800
+#define DA9052_BAT_TSH                 62000
+#define DA9052_BAT_LOW_CAP             4
+#define DA9052_AVG_SZ                  4
+#define DA9052_VC_TBL_SZ               68
+#define DA9052_VC_TBL_REF_SZ           3
+
+#define DA9052_ISET_USB_MASK           0x0F
+#define DA9052_CHG_USB_ILIM_MASK       0x40
+#define DA9052_CHG_LIM_COLS            16
+
+#define DA9052_MEAN(x, y)              ((x + y) / 2)
+
+enum charger_type_enum {
+       DA9052_NOCHARGER = 1,
+       DA9052_CHARGER,
+};
+
+static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = {
+       {70,  80,  90,  100, 110, 120, 400,  450,
+        500, 550, 600, 650, 700, 900, 1100, 1300},
+       {80,  90,  100, 110,  120,  400,  450,  500,
+        550, 600, 800, 1000, 1200, 1400, 1600, 1800},
+};
+
+static const u16 vc_tbl_ref[3] = {10, 25, 40};
+/* Lookup table for voltage vs capacity */
+static u32 const vc_tbl[3][68][2] = {
+       /* For temperature 10 degree Celsius */
+       {
+       {4082, 100}, {4036, 98},
+       {4020, 96}, {4008, 95},
+       {3997, 93}, {3983, 91},
+       {3964, 90}, {3943, 88},
+       {3926, 87}, {3912, 85},
+       {3900, 84}, {3890, 82},
+       {3881, 80}, {3873, 79},
+       {3865, 77}, {3857, 76},
+       {3848, 74}, {3839, 73},
+       {3829, 71}, {3820, 70},
+       {3811, 68}, {3802, 67},
+       {3794, 65}, {3785, 64},
+       {3778, 62}, {3770, 61},
+       {3763, 59}, {3756, 58},
+       {3750, 56}, {3744, 55},
+       {3738, 53}, {3732, 52},
+       {3727, 50}, {3722, 49},
+       {3717, 47}, {3712, 46},
+       {3708, 44}, {3703, 43},
+       {3700, 41}, {3696, 40},
+       {3693, 38}, {3691, 37},
+       {3688, 35}, {3686, 34},
+       {3683, 32}, {3681, 31},
+       {3678, 29}, {3675, 28},
+       {3672, 26}, {3669, 25},
+       {3665, 23}, {3661, 22},
+       {3656, 21}, {3651, 19},
+       {3645, 18}, {3639, 16},
+       {3631, 15}, {3622, 13},
+       {3611, 12}, {3600, 10},
+       {3587, 9}, {3572, 7},
+       {3548, 6}, {3503, 5},
+       {3420, 3}, {3268, 2},
+       {2992, 1}, {2746, 0}
+       },
+       /* For temperature 25 degree Celsius */
+       {
+       {4102, 100}, {4065, 98},
+       {4048, 96}, {4034, 95},
+       {4021, 93}, {4011, 92},
+       {4001, 90}, {3986, 88},
+       {3968, 87}, {3952, 85},
+       {3938, 84}, {3926, 82},
+       {3916, 81}, {3908, 79},
+       {3900, 77}, {3892, 76},
+       {3883, 74}, {3874, 73},
+       {3864, 71}, {3855, 70},
+       {3846, 68}, {3836, 67},
+       {3827, 65}, {3819, 64},
+       {3810, 62}, {3801, 61},
+       {3793, 59}, {3786, 58},
+       {3778, 56}, {3772, 55},
+       {3765, 53}, {3759, 52},
+       {3754, 50}, {3748, 49},
+       {3743, 47}, {3738, 46},
+       {3733, 44}, {3728, 43},
+       {3724, 41}, {3720, 40},
+       {3716, 38}, {3712, 37},
+       {3709, 35}, {3706, 34},
+       {3703, 33}, {3701, 31},
+       {3698, 30}, {3696, 28},
+       {3693, 27}, {3690, 25},
+       {3687, 24}, {3683, 22},
+       {3680, 21}, {3675, 19},
+       {3671, 18}, {3666, 17},
+       {3660, 15}, {3654, 14},
+       {3647, 12}, {3639, 11},
+       {3630, 9}, {3621, 8},
+       {3613, 6}, {3606, 5},
+       {3597, 4}, {3582, 2},
+       {3546, 1}, {2747, 0}
+       },
+       /* For temperature 40 degree Celsius */
+       {
+       {4114, 100}, {4081, 98},
+       {4065, 96}, {4050, 95},
+       {4036, 93}, {4024, 92},
+       {4013, 90}, {4002, 88},
+       {3990, 87}, {3976, 85},
+       {3962, 84}, {3950, 82},
+       {3939, 81}, {3930, 79},
+       {3921, 77}, {3912, 76},
+       {3902, 74}, {3893, 73},
+       {3883, 71}, {3874, 70},
+       {3865, 68}, {3856, 67},
+       {3847, 65}, {3838, 64},
+       {3829, 62}, {3820, 61},
+       {3812, 59}, {3803, 58},
+       {3795, 56}, {3787, 55},
+       {3780, 53}, {3773, 52},
+       {3767, 50}, {3761, 49},
+       {3756, 47}, {3751, 46},
+       {3746, 44}, {3741, 43},
+       {3736, 41}, {3732, 40},
+       {3728, 38}, {3724, 37},
+       {3720, 35}, {3716, 34},
+       {3713, 33}, {3710, 31},
+       {3707, 30}, {3704, 28},
+       {3701, 27}, {3698, 25},
+       {3695, 24}, {3691, 22},
+       {3686, 21}, {3681, 19},
+       {3676, 18}, {3671, 17},
+       {3666, 15}, {3661, 14},
+       {3655, 12}, {3648, 11},
+       {3640, 9}, {3632, 8},
+       {3622, 6}, {3616, 5},
+       {3611, 4}, {3604, 2},
+       {3594, 1}, {2747, 0}
+       }
+};
+
+struct da9052_battery {
+       struct da9052 *da9052;
+       struct power_supply psy;
+       struct notifier_block nb;
+       int charger_type;
+       int status;
+       int health;
+};
+
+static inline int volt_reg_to_mV(int value)
+{
+       return ((value * 1000) / 512) + 2500;
+}
+
+static inline int ichg_reg_to_mA(int value)
+{
+       return (value * 3900) / 1000;
+}
+
+static int da9052_read_chgend_current(struct da9052_battery *bat,
+                                      int *current_mA)
+{
+       int ret;
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EINVAL;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG);
+       if (ret < 0)
+               return ret;
+
+       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND);
+
+       return 0;
+}
+
+static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA)
+{
+       int ret;
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EINVAL;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG);
+       if (ret < 0)
+               return ret;
+
+       *current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV);
+
+       return 0;
+}
+
+static int da9052_bat_check_status(struct da9052_battery *bat, int *status)
+{
+       u8 v[2] = {0, 0};
+       u8 bat_status;
+       u8 chg_end;
+       int ret;
+       int chg_current;
+       int chg_end_current;
+       bool dcinsel;
+       bool dcindet;
+       bool vbussel;
+       bool vbusdet;
+       bool dc;
+       bool vbus;
+
+       ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v);
+       if (ret < 0)
+               return ret;
+
+       bat_status = v[0];
+       chg_end = v[1];
+
+       dcinsel = bat_status & DA9052_STATUSA_DCINSEL;
+       dcindet = bat_status & DA9052_STATUSA_DCINDET;
+       vbussel = bat_status & DA9052_STATUSA_VBUSSEL;
+       vbusdet = bat_status & DA9052_STATUSA_VBUSDET;
+       dc = dcinsel && dcindet;
+       vbus = vbussel && vbusdet;
+
+       /* Preference to WALL(DCIN) charger unit */
+       if (dc || vbus) {
+               bat->charger_type = DA9052_CHARGER;
+
+               /* If charging end flag is set and Charging current is greater
+                * than charging end limit then battery is charging
+               */
+               if ((chg_end & DA9052_STATUSB_CHGEND) != 0) {
+                       ret = da9052_read_chg_current(bat, &chg_current);
+                       if (ret < 0)
+                               return ret;
+                       ret = da9052_read_chgend_current(bat, &chg_end_current);
+                       if (ret < 0)
+                               return ret;
+
+                       if (chg_current >= chg_end_current)
+                               bat->status = POWER_SUPPLY_STATUS_CHARGING;
+                       else
+                               bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+               } else {
+                       /* If Charging end flag is cleared then battery is
+                        * charging
+                       */
+                       bat->status = POWER_SUPPLY_STATUS_CHARGING;
+               }
+       } else if (dcindet || vbusdet) {
+                       bat->charger_type = DA9052_CHARGER;
+                       bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+       } else {
+               bat->charger_type = DA9052_NOCHARGER;
+               bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       }
+
+       if (status != NULL)
+               *status = bat->status;
+       return 0;
+}
+
+static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV)
+{
+       int volt;
+
+       volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT);
+       if (volt < 0)
+               return volt;
+
+       *volt_mV = volt_reg_to_mV(volt);
+
+       return 0;
+}
+
+static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal)
+{
+       int bat_temp;
+
+       bat_temp = da9052_adc_read_temp(bat->da9052);
+       if (bat_temp < 0)
+               return bat_temp;
+
+       if (bat_temp > DA9052_BAT_TSH)
+               *illegal = 1;
+       else
+               *illegal = 0;
+
+       return 0;
+}
+
+static int da9052_bat_interpolate(int vbat_lower, int  vbat_upper,
+                                  int level_lower, int level_upper,
+                                  int bat_voltage)
+{
+       int tmp;
+
+       tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower);
+       tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000);
+
+       return tmp;
+}
+
+unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
+{
+       int i;
+
+       if (adc_temp <= vc_tbl_ref[0])
+               return 0;
+
+       if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
+               return DA9052_VC_TBL_REF_SZ - 1;
+
+       for (i = 0; i < DA9052_VC_TBL_REF_SZ; i++) {
+               if ((adc_temp > vc_tbl_ref[i]) &&
+                   (adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
+                               return i;
+               if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))
+                    && (adc_temp <= vc_tbl_ref[i]))
+                               return i + 1;
+       }
+}
+
+static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity)
+{
+       int adc_temp;
+       int bat_voltage;
+       int vbat_lower;
+       int vbat_upper;
+       int level_upper;
+       int level_lower;
+       int ret;
+       int flag;
+       int i = 0;
+       int j;
+
+       ret = da9052_bat_read_volt(bat, &bat_voltage);
+       if (ret < 0)
+               return ret;
+
+       adc_temp = da9052_adc_read_temp(bat->da9052);
+       if (adc_temp < 0)
+               return adc_temp;
+
+       i = da9052_determine_vc_tbl_index(adc_temp);
+
+       if (bat_voltage >= vc_tbl[i][0][0]) {
+               *capacity = 100;
+               return 0;
+       }
+       if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) {
+               *capacity = 0;
+               return 0;
+       }
+       flag = 0;
+
+       for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) {
+               if ((bat_voltage <= vc_tbl[i][j][0]) &&
+                   (bat_voltage >= vc_tbl[i][j + 1][0])) {
+                       vbat_upper = vc_tbl[i][j][0];
+                       vbat_lower = vc_tbl[i][j + 1][0];
+                       level_upper = vc_tbl[i][j][1];
+                       level_lower = vc_tbl[i][j + 1][1];
+                       flag = 1;
+                       break;
+               }
+       }
+       if (!flag)
+               return -EIO;
+
+       *capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower,
+                                          level_upper, bat_voltage);
+
+       return 0;
+}
+
+static int da9052_bat_check_health(struct da9052_battery *bat, int *health)
+{
+       int ret;
+       int bat_illegal;
+       int capacity;
+
+       ret = da9052_bat_check_presence(bat, &bat_illegal);
+       if (ret < 0)
+               return ret;
+
+       if (bat_illegal) {
+               bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+               return 0;
+       }
+
+       if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
+               ret = da9052_bat_read_capacity(bat, &capacity);
+               if (ret < 0)
+                       return ret;
+               if (capacity < DA9052_BAT_LOW_CAP)
+                       bat->health = POWER_SUPPLY_HEALTH_DEAD;
+               else
+                       bat->health = POWER_SUPPLY_HEALTH_GOOD;
+       }
+
+       *health = bat->health;
+
+       return 0;
+}
+
+static irqreturn_t da9052_bat_irq(int irq, void *data)
+{
+       struct da9052_battery *bat = data;
+
+       irq -= bat->da9052->irq_base;
+
+       if (irq == DA9052_IRQ_CHGEND)
+               bat->status = POWER_SUPPLY_STATUS_FULL;
+       else
+               da9052_bat_check_status(bat, NULL);
+
+       if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN ||
+           irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) {
+               power_supply_changed(&bat->psy);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int da9052_USB_current_notifier(struct notifier_block *nb,
+                                       unsigned long events, void *data)
+{
+       u8 row;
+       u8 col;
+       int *current_mA = data;
+       int ret;
+       struct da9052_battery *bat = container_of(nb, struct da9052_battery,
+                                                 nb);
+
+       if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
+               return -EPERM;
+
+       ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG);
+       if (ret & DA9052_CHG_USB_ILIM_MASK)
+               return -EPERM;
+
+       if (bat->da9052->chip_id == DA9052)
+               row = 0;
+       else
+               row = 1;
+
+       if (*current_mA < da9052_chg_current_lim[row][0] ||
+           *current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1])
+               return -EINVAL;
+
+       for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) {
+               if (*current_mA <= da9052_chg_current_lim[row][col])
+                       break;
+       }
+
+       return da9052_reg_update(bat->da9052, DA9052_ISET_REG,
+                                DA9052_ISET_USB_MASK, col);
+}
+
+static int da9052_bat_get_property(struct power_supply *psy,
+                                   enum power_supply_property psp,
+                                   union power_supply_propval *val)
+{
+       int ret;
+       int illegal;
+       struct da9052_battery *bat = container_of(psy, struct da9052_battery,
+                                                 psy);
+
+       ret = da9052_bat_check_presence(bat, &illegal);
+       if (ret < 0)
+               return ret;
+
+       if (illegal && psp != POWER_SUPPLY_PROP_PRESENT)
+               return -ENODEV;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               ret = da9052_bat_check_status(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval =
+                       (bat->charger_type == DA9052_NOCHARGER) ? 0 : 1;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               ret = da9052_bat_check_presence(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               ret = da9052_bat_check_health(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+               val->intval = DA9052_BAT_CUTOFF_VOLT * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               ret = da9052_bat_read_volt(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_AVG:
+               ret = da9052_read_chg_current(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               ret = da9052_bat_read_capacity(bat, &val->intval);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = da9052_adc_read_temp(bat->da9052);
+               ret = val->intval;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+
+static enum power_supply_property da9052_bat_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_CURRENT_AVG,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+};
+
+static struct power_supply template_battery = {
+       .name           = "da9052-bat",
+       .type           = POWER_SUPPLY_TYPE_BATTERY,
+       .properties     = da9052_bat_props,
+       .num_properties = ARRAY_SIZE(da9052_bat_props),
+       .get_property   = da9052_bat_get_property,
+};
+
+static const char *const da9052_bat_irqs[] = {
+       "BATT TEMP",
+       "DCIN DET",
+       "DCIN REM",
+       "VBUS DET",
+       "VBUS REM",
+       "CHG END",
+};
+
+static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
+{
+       struct da9052_pdata *pdata;
+       struct da9052_battery *bat;
+       int ret;
+       int irq;
+       int i;
+
+       bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL);
+       if (!bat)
+               return -ENOMEM;
+
+       bat->da9052 = dev_get_drvdata(pdev->dev.parent);
+       bat->psy = template_battery;
+       bat->charger_type = DA9052_NOCHARGER;
+       bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+       bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       bat->nb.notifier_call = da9052_USB_current_notifier;
+
+       pdata = bat->da9052->dev->platform_data;
+       if (pdata != NULL && pdata->use_for_apm)
+               bat->psy.use_for_apm = pdata->use_for_apm;
+       else
+               bat->psy.use_for_apm = 1;
+
+       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
+               irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
+               ret = request_threaded_irq(bat->da9052->irq_base + irq,
+                                          NULL, da9052_bat_irq,
+                                          IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                          da9052_bat_irqs[i], bat);
+               if (ret != 0) {
+                       dev_err(bat->da9052->dev,
+                               "DA9052 failed to request %s IRQ %d: %d\n",
+                               da9052_bat_irqs[i], irq, ret);
+                       goto err;
+               }
+       }
+
+       ret = power_supply_register(&pdev->dev, &bat->psy);
+        if (ret)
+               goto err;
+
+       return 0;
+
+err:
+       for (; i >= 0; i--) {
+               irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
+               free_irq(bat->da9052->irq_base + irq, bat);
+       }
+       kfree(bat);
+       return ret;
+}
+static int __devexit da9052_bat_remove(struct platform_device *pdev)
+{
+       int i;
+       int irq;
+       struct da9052_battery *bat = platform_get_drvdata(pdev);
+
+       for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
+               irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
+               free_irq(bat->da9052->irq_base + irq, bat);
+       }
+       power_supply_unregister(&bat->psy);
+
+       return 0;
+}
+
+static struct platform_driver da9052_bat_driver = {
+       .probe = da9052_bat_probe,
+       .remove = __devexit_p(da9052_bat_remove),
+       .driver = {
+               .name = "da9052-bat",
+               .owner = THIS_MODULE,
+       },
+};
+
+static int __init da9052_bat_init(void)
+{
+       return platform_driver_register(&da9052_bat_driver);
+}
+module_init(da9052_bat_init);
+
+static void __exit da9052_bat_exit(void)
+{
+       platform_driver_unregister(&da9052_bat_driver);
+}
+module_exit(da9052_bat_exit);
+
+MODULE_DESCRIPTION("DA9052 BAT Device Driver");
+MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9052-bat");
index f2c9cc3..545874b 100644 (file)
@@ -95,7 +95,11 @@ static int rated_capacities[] = {
        2880,   /* Samsung */
        2880,   /* BYD */
        2880,   /* Lishen */
-       2880    /* NEC */
+       2880,   /* NEC */
+#ifdef CONFIG_MACH_H4700
+       0,
+       3600,   /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */
+#endif
 };
 
 /* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C
@@ -637,18 +641,7 @@ static struct platform_driver ds2760_battery_driver = {
        .resume   = ds2760_battery_resume,
 };
 
-static int __init ds2760_battery_init(void)
-{
-       return platform_driver_register(&ds2760_battery_driver);
-}
-
-static void __exit ds2760_battery_exit(void)
-{
-       platform_driver_unregister(&ds2760_battery_driver);
-}
-
-module_init(ds2760_battery_init);
-module_exit(ds2760_battery_exit);
+module_platform_driver(ds2760_battery_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
index 91a783d..de31cae 100644 (file)
@@ -841,29 +841,17 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev)
        return 0;
 }
 
-MODULE_ALIAS("platform:ds2780-battery");
-
 static struct platform_driver ds2780_battery_driver = {
        .driver = {
                .name = "ds2780-battery",
        },
        .probe    = ds2780_battery_probe,
-       .remove   = ds2780_battery_remove,
+       .remove   = __devexit_p(ds2780_battery_remove),
 };
 
-static int __init ds2780_battery_init(void)
-{
-       return platform_driver_register(&ds2780_battery_driver);
-}
-
-static void __exit ds2780_battery_exit(void)
-{
-       platform_driver_unregister(&ds2780_battery_driver);
-}
-
-module_init(ds2780_battery_init);
-module_exit(ds2780_battery_exit);
+module_platform_driver(ds2780_battery_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
 MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
+MODULE_ALIAS("platform:ds2780-battery");
index a64b885..8672c91 100644 (file)
@@ -185,17 +185,7 @@ static struct platform_driver gpio_charger_driver = {
        },
 };
 
-static int __init gpio_charger_init(void)
-{
-       return platform_driver_register(&gpio_charger_driver);
-}
-module_init(gpio_charger_init);
-
-static void __exit gpio_charger_exit(void)
-{
-       platform_driver_unregister(&gpio_charger_driver);
-}
-module_exit(gpio_charger_exit);
+module_platform_driver(gpio_charger_driver);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
index cffcb7c..d096497 100644 (file)
@@ -61,7 +61,8 @@ MODULE_PARM_DESC(debug, "Flag to enable PMIC Battery debug messages.");
 #define PMIC_BATT_CHR_SBATDET_MASK     (1 << 5)
 #define PMIC_BATT_CHR_SDCLMT_MASK      (1 << 6)
 #define PMIC_BATT_CHR_SUSBOVP_MASK     (1 << 7)
-#define PMIC_BATT_CHR_EXCPT_MASK       0xC6
+#define PMIC_BATT_CHR_EXCPT_MASK       0x86
+
 #define PMIC_BATT_ADC_ACCCHRG_MASK     (1 << 31)
 #define PMIC_BATT_ADC_ACCCHRGVAL_MASK  0x7FFFFFFF
 
@@ -304,11 +305,6 @@ static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
                        pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
                        pmic_battery_log_event(BATT_EVENT_BATOVP_EXCPT);
                        batt_exception = 1;
-               } else if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
-                       pbi->batt_health = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
-                       pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
-                       pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
-                       batt_exception = 1;
                } else if (r8 & PMIC_BATT_CHR_STEMP_MASK) {
                        pbi->batt_health = POWER_SUPPLY_HEALTH_OVERHEAT;
                        pbi->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -316,6 +312,10 @@ static void pmic_battery_read_status(struct pmic_power_module_info *pbi)
                        batt_exception = 1;
                } else {
                        pbi->batt_health = POWER_SUPPLY_HEALTH_GOOD;
+                       if (r8 & PMIC_BATT_CHR_SDCLMT_MASK) {
+                               /* PMIC will change charging current automatically */
+                               pmic_battery_log_event(BATT_EVENT_DCLMT_EXCPT);
+                       }
                }
        }
 
@@ -779,18 +779,7 @@ static struct platform_driver platform_pmic_battery_driver = {
        .remove = __devexit_p(platform_pmic_battery_remove),
 };
 
-static int __init platform_pmic_battery_module_init(void)
-{
-       return platform_driver_register(&platform_pmic_battery_driver);
-}
-
-static void __exit platform_pmic_battery_module_exit(void)
-{
-       platform_driver_unregister(&platform_pmic_battery_driver);
-}
-
-module_init(platform_pmic_battery_module_init);
-module_exit(platform_pmic_battery_module_exit);
+module_platform_driver(platform_pmic_battery_driver);
 
 MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
 MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
index f6d72b4..b806667 100644 (file)
@@ -79,7 +79,7 @@ static void isp1704_charger_set_power(struct isp1704_charger *isp, bool on)
 {
        struct isp1704_charger_data     *board = isp->dev->platform_data;
 
-       if (board->set_power)
+       if (board && board->set_power)
                board->set_power(on);
 }
 
@@ -494,17 +494,7 @@ static struct platform_driver isp1704_charger_driver = {
        .remove = __devexit_p(isp1704_charger_remove),
 };
 
-static int __init isp1704_charger_init(void)
-{
-       return platform_driver_register(&isp1704_charger_driver);
-}
-module_init(isp1704_charger_init);
-
-static void __exit isp1704_charger_exit(void)
-{
-       platform_driver_unregister(&isp1704_charger_driver);
-}
-module_exit(isp1704_charger_exit);
+module_platform_driver(isp1704_charger_driver);
 
 MODULE_ALIAS("platform:isp1704_charger");
 MODULE_AUTHOR("Nokia Corporation");
index 763f894..8dbc7bf 100644 (file)
@@ -67,7 +67,7 @@ static irqreturn_t jz_battery_irq_handler(int irq, void *devid)
 
 static long jz_battery_read_voltage(struct jz_battery *battery)
 {
-       unsigned long t;
+       long t;
        unsigned long val;
        long voltage;
 
@@ -441,17 +441,7 @@ static struct platform_driver jz_battery_driver = {
        },
 };
 
-static int __init jz_battery_init(void)
-{
-       return platform_driver_register(&jz_battery_driver);
-}
-module_init(jz_battery_init);
-
-static void __exit jz_battery_exit(void)
-{
-       platform_driver_unregister(&jz_battery_driver);
-}
-module_exit(jz_battery_exit);
+module_platform_driver(jz_battery_driver);
 
 MODULE_ALIAS("platform:jz4740-battery");
 MODULE_LICENSE("GPL");
diff --git a/drivers/power/lp8727_charger.c b/drivers/power/lp8727_charger.c
new file mode 100644 (file)
index 0000000..b15b575
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * Driver for LP8727 Micro/Mini USB IC with intergrated charger
+ *
+ *                     Copyright (C) 2011 National Semiconductor
+ *
+ * This program 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/lp8727.h>
+
+#define DEBOUNCE_MSEC  270
+
+/* Registers */
+#define CTRL1          0x1
+#define CTRL2          0x2
+#define        SWCTRL          0x3
+#define INT1           0x4
+#define INT2           0x5
+#define STATUS1                0x6
+#define STATUS2        0x7
+#define CHGCTRL2       0x9
+
+/* CTRL1 register */
+#define CP_EN          (1 << 0)
+#define ADC_EN         (1 << 1)
+#define ID200_EN       (1 << 4)
+
+/* CTRL2 register */
+#define CHGDET_EN      (1 << 1)
+#define INT_EN         (1 << 6)
+
+/* SWCTRL register */
+#define SW_DM1_DM      (0x0 << 0)
+#define SW_DM1_U1      (0x1 << 0)
+#define SW_DM1_HiZ     (0x7 << 0)
+#define SW_DP2_DP      (0x0 << 3)
+#define SW_DP2_U2      (0x1 << 3)
+#define SW_DP2_HiZ     (0x7 << 3)
+
+/* INT1 register */
+#define IDNO           (0xF << 0)
+#define VBUS           (1 << 4)
+
+/* STATUS1 register */
+#define CHGSTAT                (3 << 4)
+#define CHPORT         (1 << 6)
+#define DCPORT         (1 << 7)
+
+/* STATUS2 register */
+#define TEMP_STAT      (3 << 5)
+
+enum lp8727_dev_id {
+       ID_NONE,
+       ID_TA,
+       ID_DEDICATED_CHG,
+       ID_USB_CHG,
+       ID_USB_DS,
+       ID_MAX,
+};
+
+enum lp8727_chg_stat {
+       PRECHG,
+       CC,
+       CV,
+       EOC,
+};
+
+struct lp8727_psy {
+       struct power_supply ac;
+       struct power_supply usb;
+       struct power_supply batt;
+};
+
+struct lp8727_chg {
+       struct device *dev;
+       struct i2c_client *client;
+       struct mutex xfer_lock;
+       struct delayed_work work;
+       struct workqueue_struct *irqthread;
+       struct lp8727_platform_data *pdata;
+       struct lp8727_psy *psy;
+       struct lp8727_chg_param *chg_parm;
+       enum lp8727_dev_id devid;
+};
+
+static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+{
+       s32 ret;
+
+       mutex_lock(&pchg->xfer_lock);
+       ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
+       mutex_unlock(&pchg->xfer_lock);
+
+       return (ret != len) ? -EIO : 0;
+}
+
+static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
+{
+       s32 ret;
+
+       mutex_lock(&pchg->xfer_lock);
+       ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
+       mutex_unlock(&pchg->xfer_lock);
+
+       return ret;
+}
+
+static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
+                                      u8 *data)
+{
+       return lp8727_i2c_read(pchg, reg, data, 1);
+}
+
+static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
+                                       u8 *data)
+{
+       return lp8727_i2c_write(pchg, reg, data, 1);
+}
+
+static int lp8727_is_charger_attached(const char *name, int id)
+{
+       if (name) {
+               if (!strcmp(name, "ac"))
+                       return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0;
+               else if (!strcmp(name, "usb"))
+                       return (id == ID_USB_CHG) ? 1 : 0;
+       }
+
+       return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
+}
+
+static void lp8727_init_device(struct lp8727_chg *pchg)
+{
+       u8 val;
+
+       val = ID200_EN | ADC_EN | CP_EN;
+       if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
+               dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1);
+
+       val = INT_EN | CHGDET_EN;
+       if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
+               dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2);
+}
+
+static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
+{
+       u8 val;
+       lp8727_i2c_read_byte(pchg, STATUS1, &val);
+       return (val & DCPORT);
+}
+
+static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
+{
+       u8 val;
+       lp8727_i2c_read_byte(pchg, STATUS1, &val);
+       return (val & CHPORT);
+}
+
+static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
+{
+       u8 val = sw;
+       lp8727_i2c_write_byte(pchg, SWCTRL, &val);
+}
+
+static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
+{
+       u8 devid = ID_NONE;
+       u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ;
+
+       switch (id) {
+       case 0x5:
+               devid = ID_TA;
+               pchg->chg_parm = &pchg->pdata->ac;
+               break;
+       case 0xB:
+               if (lp8727_is_dedicated_charger(pchg)) {
+                       pchg->chg_parm = &pchg->pdata->ac;
+                       devid = ID_DEDICATED_CHG;
+               } else if (lp8727_is_usb_charger(pchg)) {
+                       pchg->chg_parm = &pchg->pdata->usb;
+                       devid = ID_USB_CHG;
+                       swctrl = SW_DM1_DM | SW_DP2_DP;
+               } else if (vbusin) {
+                       devid = ID_USB_DS;
+                       swctrl = SW_DM1_DM | SW_DP2_DP;
+               }
+               break;
+       default:
+               devid = ID_NONE;
+               pchg->chg_parm = NULL;
+               break;
+       }
+
+       pchg->devid = devid;
+       lp8727_ctrl_switch(pchg, swctrl);
+}
+
+static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
+{
+       u8 val;
+
+       lp8727_i2c_read_byte(pchg, CTRL2, &val);
+       val |= CHGDET_EN;
+       lp8727_i2c_write_byte(pchg, CTRL2, &val);
+}
+
+static void lp8727_delayed_func(struct work_struct *_work)
+{
+       u8 intstat[2], idno, vbus;
+       struct lp8727_chg *pchg =
+           container_of(_work, struct lp8727_chg, work.work);
+
+       if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
+               dev_err(pchg->dev, "can not read INT registers\n");
+               return;
+       }
+
+       idno = intstat[0] & IDNO;
+       vbus = intstat[0] & VBUS;
+
+       lp8727_id_detection(pchg, idno, vbus);
+       lp8727_enable_chgdet(pchg);
+
+       power_supply_changed(&pchg->psy->ac);
+       power_supply_changed(&pchg->psy->usb);
+       power_supply_changed(&pchg->psy->batt);
+}
+
+static irqreturn_t lp8727_isr_func(int irq, void *ptr)
+{
+       struct lp8727_chg *pchg = ptr;
+       unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC);
+
+       queue_delayed_work(pchg->irqthread, &pchg->work, delay);
+
+       return IRQ_HANDLED;
+}
+
+static void lp8727_intr_config(struct lp8727_chg *pchg)
+{
+       INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
+
+       pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
+       if (!pchg->irqthread)
+               dev_err(pchg->dev, "can not create thread for lp8727\n");
+
+       if (request_threaded_irq(pchg->client->irq,
+                                NULL,
+                                lp8727_isr_func,
+                                IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
+               dev_err(pchg->dev, "lp8727 irq can not be registered\n");
+       }
+}
+
+static enum power_supply_property lp8727_charger_prop[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property lp8727_battery_prop[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static char *battery_supplied_to[] = {
+       "main_batt",
+};
+
+static int lp8727_charger_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
+
+       if (psp == POWER_SUPPLY_PROP_ONLINE)
+               val->intval = lp8727_is_charger_attached(psy->name,
+                                                        pchg->devid);
+
+       return 0;
+}
+
+static int lp8727_battery_get_property(struct power_supply *psy,
+                                      enum power_supply_property psp,
+                                      union power_supply_propval *val)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
+       u8 read;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
+                       lp8727_i2c_read_byte(pchg, STATUS1, &read);
+                       if (((read & CHGSTAT) >> 4) == EOC)
+                               val->intval = POWER_SUPPLY_STATUS_FULL;
+                       else
+                               val->intval = POWER_SUPPLY_STATUS_CHARGING;
+               } else {
+                       val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+               }
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               lp8727_i2c_read_byte(pchg, STATUS2, &read);
+               read = (read & TEMP_STAT) >> 5;
+               if (read >= 0x1 && read <= 0x3)
+                       val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else
+                       val->intval = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               if (pchg->pdata->get_batt_present)
+                       val->intval = pchg->pdata->get_batt_present();
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (pchg->pdata->get_batt_level)
+                       val->intval = pchg->pdata->get_batt_level();
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (pchg->pdata->get_batt_capacity)
+                       val->intval = pchg->pdata->get_batt_capacity();
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               if (pchg->pdata->get_batt_temp)
+                       val->intval = pchg->pdata->get_batt_temp();
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static void lp8727_charger_changed(struct power_supply *psy)
+{
+       struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
+       u8 val;
+       u8 eoc_level, ichg;
+
+       if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
+               if (pchg->chg_parm) {
+                       eoc_level = pchg->chg_parm->eoc_level;
+                       ichg = pchg->chg_parm->ichg;
+                       val = (ichg << 4) | eoc_level;
+                       lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
+               }
+       }
+}
+
+static int lp8727_register_psy(struct lp8727_chg *pchg)
+{
+       struct lp8727_psy *psy;
+
+       psy = kzalloc(sizeof(*psy), GFP_KERNEL);
+       if (!psy)
+               goto err_mem;
+
+       pchg->psy = psy;
+
+       psy->ac.name = "ac";
+       psy->ac.type = POWER_SUPPLY_TYPE_MAINS;
+       psy->ac.properties = lp8727_charger_prop;
+       psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop);
+       psy->ac.get_property = lp8727_charger_get_property;
+       psy->ac.supplied_to = battery_supplied_to;
+       psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to);
+
+       if (power_supply_register(pchg->dev, &psy->ac))
+               goto err_psy;
+
+       psy->usb.name = "usb";
+       psy->usb.type = POWER_SUPPLY_TYPE_USB;
+       psy->usb.properties = lp8727_charger_prop;
+       psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop);
+       psy->usb.get_property = lp8727_charger_get_property;
+       psy->usb.supplied_to = battery_supplied_to;
+       psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to);
+
+       if (power_supply_register(pchg->dev, &psy->usb))
+               goto err_psy;
+
+       psy->batt.name = "main_batt";
+       psy->batt.type = POWER_SUPPLY_TYPE_BATTERY;
+       psy->batt.properties = lp8727_battery_prop;
+       psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop);
+       psy->batt.get_property = lp8727_battery_get_property;
+       psy->batt.external_power_changed = lp8727_charger_changed;
+
+       if (power_supply_register(pchg->dev, &psy->batt))
+               goto err_psy;
+
+       return 0;
+
+err_mem:
+       return -ENOMEM;
+err_psy:
+       kfree(psy);
+       return -EPERM;
+}
+
+static void lp8727_unregister_psy(struct lp8727_chg *pchg)
+{
+       struct lp8727_psy *psy = pchg->psy;
+
+       if (!psy)
+               return;
+
+       power_supply_unregister(&psy->ac);
+       power_supply_unregister(&psy->usb);
+       power_supply_unregister(&psy->batt);
+       kfree(psy);
+}
+
+static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
+{
+       struct lp8727_chg *pchg;
+       int ret;
+
+       if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+               return -EIO;
+
+       pchg = kzalloc(sizeof(*pchg), GFP_KERNEL);
+       if (!pchg)
+               return -ENOMEM;
+
+       pchg->client = cl;
+       pchg->dev = &cl->dev;
+       pchg->pdata = cl->dev.platform_data;
+       i2c_set_clientdata(cl, pchg);
+
+       mutex_init(&pchg->xfer_lock);
+
+       lp8727_init_device(pchg);
+       lp8727_intr_config(pchg);
+
+       ret = lp8727_register_psy(pchg);
+       if (ret)
+               dev_err(pchg->dev,
+                       "can not register power supplies. err=%d", ret);
+
+       return 0;
+}
+
+static int __devexit lp8727_remove(struct i2c_client *cl)
+{
+       struct lp8727_chg *pchg = i2c_get_clientdata(cl);
+
+       lp8727_unregister_psy(pchg);
+       free_irq(pchg->client->irq, pchg);
+       flush_workqueue(pchg->irqthread);
+       destroy_workqueue(pchg->irqthread);
+       kfree(pchg);
+       return 0;
+}
+
+static const struct i2c_device_id lp8727_ids[] = {
+       {"lp8727", 0},
+};
+
+static struct i2c_driver lp8727_driver = {
+       .driver = {
+                  .name = "lp8727",
+                  },
+       .probe = lp8727_probe,
+       .remove = __devexit_p(lp8727_remove),
+       .id_table = lp8727_ids,
+};
+
+static int __init lp8727_init(void)
+{
+       return i2c_add_driver(&lp8727_driver);
+}
+
+static void __exit lp8727_exit(void)
+{
+       i2c_del_driver(&lp8727_driver);
+}
+
+module_init(lp8727_init);
+module_exit(lp8727_exit);
+
+MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
+MODULE_AUTHOR
+    ("Woogyom Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
+MODULE_LICENSE("GPL");
index 9f0183c..86acee2 100644 (file)
@@ -85,55 +85,79 @@ static int max17042_get_property(struct power_supply *psy,
 {
        struct max17042_chip *chip = container_of(psy,
                                struct max17042_chip, battery);
+       int ret;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_PRESENT:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_STATUS);
-               if (val->intval & MAX17042_STATUS_BattAbsent)
+               ret = max17042_read_reg(chip->client, MAX17042_STATUS);
+               if (ret < 0)
+                       return ret;
+
+               if (ret & MAX17042_STATUS_BattAbsent)
                        val->intval = 0;
                else
                        val->intval = 1;
                break;
        case POWER_SUPPLY_PROP_CYCLE_COUNT:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_Cycles);
+               ret = max17042_read_reg(chip->client, MAX17042_Cycles);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MAX:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_MinMaxVolt);
-               val->intval >>= 8;
+               ret = max17042_read_reg(chip->client, MAX17042_MinMaxVolt);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret >> 8;
                val->intval *= 20000; /* Units of LSB = 20mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_V_empty);
-               val->intval >>= 7;
+               ret = max17042_read_reg(chip->client, MAX17042_V_empty);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret >> 7;
                val->intval *= 10000; /* Units of LSB = 10mV */
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_NOW:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_VCELL) * 83; /* 1000 / 12 = 83 */
+               ret = max17042_read_reg(chip->client, MAX17042_VCELL);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_VOLTAGE_AVG:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_AvgVCELL) * 83;
+               ret = max17042_read_reg(chip->client, MAX17042_AvgVCELL);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret * 625 / 8;
                break;
        case POWER_SUPPLY_PROP_CAPACITY:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_SOC) / 256;
+               ret = max17042_read_reg(chip->client, MAX17042_SOC);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret >> 8;
                break;
        case POWER_SUPPLY_PROP_CHARGE_FULL:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_RepSOC);
-               if ((val->intval / 256) >= MAX17042_BATTERY_FULL)
+               ret = max17042_read_reg(chip->client, MAX17042_RepSOC);
+               if (ret < 0)
+                       return ret;
+
+               if ((ret >> 8) >= MAX17042_BATTERY_FULL)
                        val->intval = 1;
-               else if (val->intval >= 0)
+               else if (ret >= 0)
                        val->intval = 0;
                break;
        case POWER_SUPPLY_PROP_TEMP:
-               val->intval = max17042_read_reg(chip->client,
-                               MAX17042_TEMP);
+               ret = max17042_read_reg(chip->client, MAX17042_TEMP);
+               if (ret < 0)
+                       return ret;
+
+               val->intval = ret;
                /* The value is signed. */
                if (val->intval & 0x8000) {
                        val->intval = (0x7fff & ~val->intval) + 1;
@@ -145,24 +169,30 @@ static int max17042_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_CURRENT_NOW:
                if (chip->pdata->enable_current_sense) {
-                       val->intval = max17042_read_reg(chip->client,
-                                       MAX17042_Current);
+                       ret = max17042_read_reg(chip->client, MAX17042_Current);
+                       if (ret < 0)
+                               return ret;
+
+                       val->intval = ret;
                        if (val->intval & 0x8000) {
                                /* Negative */
                                val->intval = ~val->intval & 0x7fff;
                                val->intval++;
                                val->intval *= -1;
                        }
-                       val->intval >>= 4;
-                       val->intval *= 1000000 * 25 / chip->pdata->r_sns;
+                       val->intval *= 1562500 / chip->pdata->r_sns;
                } else {
                        return -EINVAL;
                }
                break;
        case POWER_SUPPLY_PROP_CURRENT_AVG:
                if (chip->pdata->enable_current_sense) {
-                       val->intval = max17042_read_reg(chip->client,
-                                       MAX17042_AvgCurrent);
+                       ret = max17042_read_reg(chip->client,
+                                               MAX17042_AvgCurrent);
+                       if (ret < 0)
+                               return ret;
+
+                       val->intval = ret;
                        if (val->intval & 0x8000) {
                                /* Negative */
                                val->intval = ~val->intval & 0x7fff;
@@ -210,6 +240,9 @@ static int __devinit max17042_probe(struct i2c_client *client,
        if (!chip->pdata->enable_current_sense)
                chip->battery.num_properties -= 2;
 
+       if (chip->pdata->r_sns == 0)
+               chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
+
        ret = power_supply_register(&client->dev, &chip->battery);
        if (ret) {
                dev_err(&client->dev, "failed: power supply register\n");
@@ -226,9 +259,6 @@ static int __devinit max17042_probe(struct i2c_client *client,
                max17042_write_reg(client, MAX17042_CGAIN, 0x0000);
                max17042_write_reg(client, MAX17042_MiscCFG, 0x0003);
                max17042_write_reg(client, MAX17042_LearnCFG, 0x0007);
-       } else {
-               if (chip->pdata->r_sns == 0)
-                       chip->pdata->r_sns = MAX17042_DEFAULT_SNS_RESISTOR;
        }
 
        return 0;
index 2595145..3e23f43 100644 (file)
@@ -374,19 +374,9 @@ static struct platform_driver max8903_driver = {
        },
 };
 
-static int __init max8903_init(void)
-{
-       return platform_driver_register(&max8903_driver);
-}
-module_init(max8903_init);
-
-static void __exit max8903_exit(void)
-{
-       platform_driver_unregister(&max8903_driver);
-}
-module_exit(max8903_exit);
+module_platform_driver(max8903_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("MAX8903 Charger Driver");
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
-MODULE_ALIAS("max8903-charger");
+MODULE_ALIAS("platform:max8903-charger");
index a70e16d..daa333b 100644 (file)
@@ -78,6 +78,8 @@ struct max8925_power_info {
        unsigned                batt_detect:1;  /* detecing MB by ID pin */
        unsigned                topoff_threshold:2;
        unsigned                fast_charge:3;
+       unsigned                no_temp_support:1;
+       unsigned                no_insert_detect:1;
 
        int (*set_charger) (int);
 };
@@ -116,17 +118,7 @@ static irqreturn_t max8925_charger_handler(int irq, void *data)
        case MAX8925_IRQ_VCHG_DC_F:
                info->ac_online = 0;
                __set_charger(info, 0);
-               dev_dbg(chip->dev, "Adapter is removal\n");
-               break;
-       case MAX8925_IRQ_VCHG_USB_R:
-               info->usb_online = 1;
-               __set_charger(info, 1);
-               dev_dbg(chip->dev, "USB inserted\n");
-               break;
-       case MAX8925_IRQ_VCHG_USB_F:
-               info->usb_online = 0;
-               __set_charger(info, 0);
-               dev_dbg(chip->dev, "USB is removal\n");
+               dev_dbg(chip->dev, "Adapter removed\n");
                break;
        case MAX8925_IRQ_VCHG_THM_OK_F:
                /* Battery is not ready yet */
@@ -168,27 +160,33 @@ static irqreturn_t max8925_charger_handler(int irq, void *data)
 static int start_measure(struct max8925_power_info *info, int type)
 {
        unsigned char buf[2] = {0, 0};
+       int meas_cmd;
        int meas_reg = 0, ret;
 
        switch (type) {
        case MEASURE_VCHG:
+               meas_cmd = MAX8925_CMD_VCHG;
                meas_reg = MAX8925_ADC_VCHG;
                break;
        case MEASURE_VBBATT:
+               meas_cmd = MAX8925_CMD_VBBATT;
                meas_reg = MAX8925_ADC_VBBATT;
                break;
        case MEASURE_VMBATT:
+               meas_cmd = MAX8925_CMD_VMBATT;
                meas_reg = MAX8925_ADC_VMBATT;
                break;
        case MEASURE_ISNS:
+               meas_cmd = MAX8925_CMD_ISNS;
                meas_reg = MAX8925_ADC_ISNS;
                break;
        default:
                return -EINVAL;
        }
 
+       max8925_reg_write(info->adc, meas_cmd, 0);
        max8925_bulk_read(info->adc, meas_reg, 2, buf);
-       ret = (buf[0] << 4) | (buf[1] >> 4);
+       ret = ((buf[0]<<8) | buf[1]) >> 4;
 
        return ret;
 }
@@ -208,7 +206,7 @@ static int max8925_ac_get_prop(struct power_supply *psy,
                if (info->ac_online) {
                        ret = start_measure(info, MEASURE_VCHG);
                        if (ret >= 0) {
-                               val->intval = ret << 1; /* unit is mV */
+                               val->intval = ret * 2000;       /* unit is uV */
                                goto out;
                        }
                }
@@ -242,7 +240,7 @@ static int max8925_usb_get_prop(struct power_supply *psy,
                if (info->usb_online) {
                        ret = start_measure(info, MEASURE_VCHG);
                        if (ret >= 0) {
-                               val->intval = ret << 1; /* unit is mV */
+                               val->intval = ret * 2000;       /* unit is uV */
                                goto out;
                        }
                }
@@ -266,7 +264,6 @@ static int max8925_bat_get_prop(struct power_supply *psy,
                                union power_supply_propval *val)
 {
        struct max8925_power_info *info = dev_get_drvdata(psy->dev->parent);
-       long long int tmp = 0;
        int ret = 0;
 
        switch (psp) {
@@ -277,7 +274,7 @@ static int max8925_bat_get_prop(struct power_supply *psy,
                if (info->bat_online) {
                        ret = start_measure(info, MEASURE_VMBATT);
                        if (ret >= 0) {
-                               val->intval = ret << 1; /* unit is mV */
+                               val->intval = ret * 2000;       /* unit is uV */
                                ret = 0;
                                break;
                        }
@@ -288,8 +285,8 @@ static int max8925_bat_get_prop(struct power_supply *psy,
                if (info->bat_online) {
                        ret = start_measure(info, MEASURE_ISNS);
                        if (ret >= 0) {
-                               tmp = (long long int)ret * 6250 / 4096 - 3125;
-                               ret = (int)tmp;
+                               /* assume r_sns is 0.02 */
+                               ret = ((ret * 6250) - 3125) /* uA */;
                                val->intval = 0;
                                if (ret > 0)
                                        val->intval = ret; /* unit is mA */
@@ -365,13 +362,14 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip,
        int ret;
 
        REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_OVP, "ac-ovp");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_OVP, "usb-ovp");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_F, "usb-remove");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_USB_R, "usb-insert");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
-       REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
+       if (!info->no_insert_detect) {
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_F, "ac-remove");
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_DC_R, "ac-insert");
+       }
+       if (!info->no_temp_support) {
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_R, "batt-temp-in-range");
+               REQUEST_IRQ(MAX8925_IRQ_VCHG_THM_OK_F, "batt-temp-out-range");
+       }
        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_F, "vsys-high");
        REQUEST_IRQ(MAX8925_IRQ_VCHG_SYSLOW_R, "vsys-low");
        REQUEST_IRQ(MAX8925_IRQ_VCHG_RST, "charger-reset");
@@ -379,9 +377,15 @@ static __devinit int max8925_init_charger(struct max8925_chip *chip,
        REQUEST_IRQ(MAX8925_IRQ_VCHG_TOPOFF, "charger-topoff");
        REQUEST_IRQ(MAX8925_IRQ_VCHG_TMR_FAULT, "charger-timer-expire");
 
-       info->ac_online = 0;
        info->usb_online = 0;
        info->bat_online = 0;
+
+       /* check for power - can miss interrupt at boot time */
+       if (start_measure(info, MEASURE_VCHG) * 2000 > 500000)
+               info->ac_online = 1;
+       else
+               info->ac_online = 0;
+
        ret = max8925_reg_read(info->gpm, MAX8925_CHG_STATUS);
        if (ret >= 0) {
                /*
@@ -449,6 +453,8 @@ static __devinit int max8925_power_probe(struct platform_device *pdev)
        info->ac.properties = max8925_ac_props;
        info->ac.num_properties = ARRAY_SIZE(max8925_ac_props);
        info->ac.get_property = max8925_ac_get_prop;
+       info->ac.supplied_to = pdata->supplied_to;
+       info->ac.num_supplicants = pdata->num_supplicants;
        ret = power_supply_register(&pdev->dev, &info->ac);
        if (ret)
                goto out;
@@ -459,6 +465,9 @@ static __devinit int max8925_power_probe(struct platform_device *pdev)
        info->usb.properties = max8925_usb_props;
        info->usb.num_properties = ARRAY_SIZE(max8925_usb_props);
        info->usb.get_property = max8925_usb_get_prop;
+       info->usb.supplied_to = pdata->supplied_to;
+       info->usb.num_supplicants = pdata->num_supplicants;
+
        ret = power_supply_register(&pdev->dev, &info->usb);
        if (ret)
                goto out_usb;
@@ -478,6 +487,8 @@ static __devinit int max8925_power_probe(struct platform_device *pdev)
        info->topoff_threshold = pdata->topoff_threshold;
        info->fast_charge = pdata->fast_charge;
        info->set_charger = pdata->set_charger;
+       info->no_temp_support = pdata->no_temp_support;
+       info->no_insert_detect = pdata->no_insert_detect;
 
        max8925_init_charger(chip, info);
        return 0;
@@ -512,17 +523,7 @@ static struct platform_driver max8925_power_driver = {
        },
 };
 
-static int __init max8925_power_init(void)
-{
-       return platform_driver_register(&max8925_power_driver);
-}
-module_init(max8925_power_init);
-
-static void __exit max8925_power_exit(void)
-{
-       platform_driver_unregister(&max8925_power_driver);
-}
-module_exit(max8925_power_exit);
+module_platform_driver(max8925_power_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Power supply driver for MAX8925");
index a23317d..6e88c5d 100644 (file)
@@ -19,7 +19,6 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#include <linux/module.h>
 #include <linux/err.h>
 #include <linux/module.h>
 #include <linux/slab.h>
@@ -98,7 +97,7 @@ static __devinit int max8997_battery_probe(struct platform_device *pdev)
                return -EINVAL;
 
        if (pdata->eoc_mA) {
-               u8 val = (pdata->eoc_mA - 50) / 10;
+               int val = (pdata->eoc_mA - 50) / 10;
                if (val < 0)
                        val = 0;
                if (val > 0xf)
@@ -179,6 +178,7 @@ static int __devexit max8997_battery_remove(struct platform_device *pdev)
 
 static const struct platform_device_id max8997_battery_id[] = {
        { "max8997-battery", 0 },
+       { }
 };
 
 static struct platform_driver max8997_battery_driver = {
index 93e3bb4..9b3f2bf 100644 (file)
@@ -154,6 +154,7 @@ static __devinit int max8998_battery_probe(struct platform_device *pdev)
        case 0:
                dev_dbg(max8998->dev,
                        "Full Timeout not set: leave it unchanged.\n");
+               break;
        default:
                dev_err(max8998->dev, "Invalid Full Timeout value\n");
                ret = -EINVAL;
@@ -190,6 +191,7 @@ static int __devexit max8998_battery_remove(struct platform_device *pdev)
 
 static const struct platform_device_id max8998_battery_id[] = {
        { "max8998-battery", TYPE_MAX8998 },
+       { }
 };
 
 static struct platform_driver max8998_battery_driver = {
@@ -202,17 +204,7 @@ static struct platform_driver max8998_battery_driver = {
        .id_table = max8998_battery_id,
 };
 
-static int __init max8998_battery_init(void)
-{
-       return platform_driver_register(&max8998_battery_driver);
-}
-module_init(max8998_battery_init);
-
-static void __exit max8998_battery_cleanup(void)
-{
-       platform_driver_unregister(&max8998_battery_driver);
-}
-module_exit(max8998_battery_cleanup);
+module_platform_driver(max8998_battery_driver);
 
 MODULE_DESCRIPTION("MAXIM 8998 battery control driver");
 MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
index 0b0ff3a..7385092 100644 (file)
@@ -519,29 +519,35 @@ static struct device_attribute olpc_bat_error = {
  *             Initialisation
  *********************************************************************/
 
-static struct platform_device *bat_pdev;
-
 static struct power_supply olpc_bat = {
+       .name = "olpc-battery",
        .get_property = olpc_bat_get_property,
        .use_for_apm = 1,
 };
 
-void olpc_battery_trigger_uevent(unsigned long cause)
+static int olpc_battery_suspend(struct platform_device *pdev,
+                               pm_message_t state)
 {
-       if (cause & EC_SCI_SRC_ACPWR)
-               kobject_uevent(&olpc_ac.dev->kobj, KOBJ_CHANGE);
-       if (cause & (EC_SCI_SRC_BATERR|EC_SCI_SRC_BATSOC|EC_SCI_SRC_BATTERY))
-               kobject_uevent(&olpc_bat.dev->kobj, KOBJ_CHANGE);
+       if (device_may_wakeup(olpc_ac.dev))
+               olpc_ec_wakeup_set(EC_SCI_SRC_ACPWR);
+       else
+               olpc_ec_wakeup_clear(EC_SCI_SRC_ACPWR);
+
+       if (device_may_wakeup(olpc_bat.dev))
+               olpc_ec_wakeup_set(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
+                                  | EC_SCI_SRC_BATERR);
+       else
+               olpc_ec_wakeup_clear(EC_SCI_SRC_BATTERY | EC_SCI_SRC_BATSOC
+                                    | EC_SCI_SRC_BATERR);
+
+       return 0;
 }
 
-static int __init olpc_bat_init(void)
+static int __devinit olpc_battery_probe(struct platform_device *pdev)
 {
-       int ret = 0;
+       int ret;
        uint8_t status;
 
-       if (!olpc_platform_info.ecver)
-               return -ENXIO;
-
        /*
         * We've seen a number of EC protocol changes; this driver requires
         * the latest EC protocol, supported by 0x44 and above.
@@ -558,15 +564,10 @@ static int __init olpc_bat_init(void)
 
        /* Ignore the status. It doesn't actually matter */
 
-       bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0);
-       if (IS_ERR(bat_pdev))
-               return PTR_ERR(bat_pdev);
-
-       ret = power_supply_register(&bat_pdev->dev, &olpc_ac);
+       ret = power_supply_register(&pdev->dev, &olpc_ac);
        if (ret)
-               goto ac_failed;
+               return ret;
 
-       olpc_bat.name = bat_pdev->name;
        if (olpc_board_at_least(olpc_board_pre(0xd0))) { /* XO-1.5 */
                olpc_bat.properties = olpc_xo15_bat_props;
                olpc_bat.num_properties = ARRAY_SIZE(olpc_xo15_bat_props);
@@ -575,7 +576,7 @@ static int __init olpc_bat_init(void)
                olpc_bat.num_properties = ARRAY_SIZE(olpc_xo1_bat_props);
        }
 
-       ret = power_supply_register(&bat_pdev->dev, &olpc_bat);
+       ret = power_supply_register(&pdev->dev, &olpc_bat);
        if (ret)
                goto battery_failed;
 
@@ -587,7 +588,12 @@ static int __init olpc_bat_init(void)
        if (ret)
                goto error_failed;
 
-       goto success;
+       if (olpc_ec_wakeup_available()) {
+               device_set_wakeup_capable(olpc_ac.dev, true);
+               device_set_wakeup_capable(olpc_bat.dev, true);
+       }
+
+       return 0;
 
 error_failed:
        device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
@@ -595,23 +601,36 @@ eeprom_failed:
        power_supply_unregister(&olpc_bat);
 battery_failed:
        power_supply_unregister(&olpc_ac);
-ac_failed:
-       platform_device_unregister(bat_pdev);
-success:
        return ret;
 }
 
-static void __exit olpc_bat_exit(void)
+static int __devexit olpc_battery_remove(struct platform_device *pdev)
 {
        device_remove_file(olpc_bat.dev, &olpc_bat_error);
        device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
        power_supply_unregister(&olpc_bat);
        power_supply_unregister(&olpc_ac);
-       platform_device_unregister(bat_pdev);
+       return 0;
 }
 
-module_init(olpc_bat_init);
-module_exit(olpc_bat_exit);
+static const struct of_device_id olpc_battery_ids[] __devinitconst = {
+       { .compatible = "olpc,xo1-battery" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, olpc_battery_ids);
+
+static struct platform_driver olpc_battery_driver = {
+       .driver = {
+               .name = "olpc-battery",
+               .owner = THIS_MODULE,
+               .of_match_table = olpc_battery_ids,
+       },
+       .probe = olpc_battery_probe,
+       .remove = __devexit_p(olpc_battery_remove),
+       .suspend = olpc_battery_suspend,
+};
+
+module_platform_driver(olpc_battery_driver);
 
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_LICENSE("GPL");
index 4fa52e1..3d1e9ef 100644 (file)
@@ -474,17 +474,7 @@ static struct platform_driver pcf50633_mbc_driver = {
        .remove = __devexit_p(pcf50633_mbc_remove),
 };
 
-static int __init pcf50633_mbc_init(void)
-{
-       return platform_driver_register(&pcf50633_mbc_driver);
-}
-module_init(pcf50633_mbc_init);
-
-static void __exit pcf50633_mbc_exit(void)
-{
-       platform_driver_unregister(&pcf50633_mbc_driver);
-}
-module_exit(pcf50633_mbc_exit);
+module_platform_driver(pcf50633_mbc_driver);
 
 MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
 MODULE_DESCRIPTION("PCF50633 mbc driver");
index 69f8aa3..fd49689 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
+#include <linux/notifier.h>
 #include <linux/power_supply.h>
 #include <linux/pda_power.h>
 #include <linux/regulator/consumer.h>
@@ -40,7 +41,9 @@ static int polling;
 
 #ifdef CONFIG_USB_OTG_UTILS
 static struct otg_transceiver *transceiver;
+static struct notifier_block otg_nb;
 #endif
+
 static struct regulator *ac_draw;
 
 enum {
@@ -222,7 +225,42 @@ static void polling_timer_func(unsigned long unused)
 #ifdef CONFIG_USB_OTG_UTILS
 static int otg_is_usb_online(void)
 {
-       return (transceiver->state == OTG_STATE_B_PERIPHERAL);
+       return (transceiver->last_event == USB_EVENT_VBUS ||
+               transceiver->last_event == USB_EVENT_ENUMERATED);
+}
+
+static int otg_is_ac_online(void)
+{
+       return (transceiver->last_event == USB_EVENT_CHARGER);
+}
+
+static int otg_handle_notification(struct notifier_block *nb,
+               unsigned long event, void *unused)
+{
+       switch (event) {
+       case USB_EVENT_CHARGER:
+               ac_status = PDA_PSY_TO_CHANGE;
+               break;
+       case USB_EVENT_VBUS:
+       case USB_EVENT_ENUMERATED:
+               usb_status = PDA_PSY_TO_CHANGE;
+               break;
+       case USB_EVENT_NONE:
+               ac_status = PDA_PSY_TO_CHANGE;
+               usb_status = PDA_PSY_TO_CHANGE;
+               break;
+       default:
+               return NOTIFY_OK;
+       }
+
+       /*
+        * Wait a bit before reading ac/usb line status and setting charger,
+        * because ac/usb status readings may lag from irq.
+        */
+       mod_timer(&charger_timer,
+                 jiffies + msecs_to_jiffies(pdata->wait_for_status));
+
+       return NOTIFY_OK;
 }
 #endif
 
@@ -282,6 +320,16 @@ static int pda_power_probe(struct platform_device *pdev)
                ret = PTR_ERR(ac_draw);
        }
 
+#ifdef CONFIG_USB_OTG_UTILS
+       transceiver = otg_get_transceiver();
+       if (transceiver && !pdata->is_usb_online) {
+               pdata->is_usb_online = otg_is_usb_online;
+       }
+       if (transceiver && !pdata->is_ac_online) {
+               pdata->is_ac_online = otg_is_ac_online;
+       }
+#endif
+
        if (pdata->is_ac_online) {
                ret = power_supply_register(&pdev->dev, &pda_psy_ac);
                if (ret) {
@@ -303,13 +351,6 @@ static int pda_power_probe(struct platform_device *pdev)
                }
        }
 
-#ifdef CONFIG_USB_OTG_UTILS
-       transceiver = otg_get_transceiver();
-       if (transceiver && !pdata->is_usb_online) {
-               pdata->is_usb_online = otg_is_usb_online;
-       }
-#endif
-
        if (pdata->is_usb_online) {
                ret = power_supply_register(&pdev->dev, &pda_psy_usb);
                if (ret) {
@@ -331,6 +372,18 @@ static int pda_power_probe(struct platform_device *pdev)
                }
        }
 
+#ifdef CONFIG_USB_OTG_UTILS
+       if (transceiver && pdata->use_otg_notifier) {
+               otg_nb.notifier_call = otg_handle_notification;
+               ret = otg_register_notifier(transceiver, &otg_nb);
+               if (ret) {
+                       dev_err(dev, "failure to register otg notifier\n");
+                       goto otg_reg_notifier_failed;
+               }
+               polling = 0;
+       }
+#endif
+
        if (polling) {
                dev_dbg(dev, "will poll for status\n");
                setup_timer(&polling_timer, polling_timer_func, 0);
@@ -343,6 +396,11 @@ static int pda_power_probe(struct platform_device *pdev)
 
        return 0;
 
+#ifdef CONFIG_USB_OTG_UTILS
+otg_reg_notifier_failed:
+       if (pdata->is_usb_online && usb_irq)
+               free_irq(usb_irq->start, &pda_psy_usb);
+#endif
 usb_irq_failed:
        if (pdata->is_usb_online)
                power_supply_unregister(&pda_psy_usb);
@@ -440,8 +498,6 @@ static int pda_power_resume(struct platform_device *pdev)
 #define pda_power_resume NULL
 #endif /* CONFIG_PM */
 
-MODULE_ALIAS("platform:pda-power");
-
 static struct platform_driver pda_power_pdrv = {
        .driver = {
                .name = "pda-power",
@@ -452,17 +508,8 @@ static struct platform_driver pda_power_pdrv = {
        .resume = pda_power_resume,
 };
 
-static int __init pda_power_init(void)
-{
-       return platform_driver_register(&pda_power_pdrv);
-}
+module_platform_driver(pda_power_pdrv);
 
-static void __exit pda_power_exit(void)
-{
-       platform_driver_unregister(&pda_power_pdrv);
-}
-
-module_init(pda_power_init);
-module_exit(pda_power_exit);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
+MODULE_ALIAS("platform:pda-power");
index 329b46b..6ad6127 100644 (file)
@@ -98,7 +98,9 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data)
 {
        union power_supply_propval ret = {0,};
        struct power_supply *psy = dev_get_drvdata(dev);
+       unsigned int *count = data;
 
+       (*count)++;
        if (psy->type != POWER_SUPPLY_TYPE_BATTERY) {
                if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &ret))
                        return 0;
@@ -111,10 +113,18 @@ static int __power_supply_is_system_supplied(struct device *dev, void *data)
 int power_supply_is_system_supplied(void)
 {
        int error;
+       unsigned int count = 0;
 
-       error = class_for_each_device(power_supply_class, NULL, NULL,
+       error = class_for_each_device(power_supply_class, NULL, &count,
                                      __power_supply_is_system_supplied);
 
+       /*
+        * If no power class device was found at all, most probably we are
+        * running on a desktop system, so assume we are on mains power.
+        */
+       if (count == 0)
+               return 1;
+
        return error;
 }
 EXPORT_SYMBOL_GPL(power_supply_is_system_supplied);
@@ -147,6 +157,12 @@ struct power_supply *power_supply_get_by_name(char *name)
 }
 EXPORT_SYMBOL_GPL(power_supply_get_by_name);
 
+int power_supply_powers(struct power_supply *psy, struct device *dev)
+{
+       return sysfs_create_link(&psy->dev->kobj, &dev->kobj, "powers");
+}
+EXPORT_SYMBOL_GPL(power_supply_powers);
+
 static void power_supply_dev_release(struct device *dev)
 {
        pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
@@ -202,6 +218,7 @@ EXPORT_SYMBOL_GPL(power_supply_register);
 void power_supply_unregister(struct power_supply *psy)
 {
        cancel_work_sync(&psy->changed_work);
+       sysfs_remove_link(&psy->dev->kobj, "powers");
        power_supply_remove_triggers(psy);
        device_unregister(psy->dev);
 }
index e15d4c9..5faf7ae 100644 (file)
@@ -43,7 +43,7 @@ static ssize_t power_supply_show_property(struct device *dev,
                                          struct device_attribute *attr,
                                          char *buf) {
        static char *type_text[] = {
-               "Battery", "UPS", "Mains", "USB",
+               "Unknown", "Battery", "UPS", "Mains", "USB",
                "USB_DCP", "USB_CDP", "USB_ACA"
        };
        static char *status_text[] = {
@@ -63,6 +63,9 @@ static ssize_t power_supply_show_property(struct device *dev,
        static char *capacity_level_text[] = {
                "Unknown", "Critical", "Low", "Normal", "High", "Full"
        };
+       static char *scope_text[] = {
+               "Unknown", "System", "Device"
+       };
        ssize_t ret = 0;
        struct power_supply *psy = dev_get_drvdata(dev);
        const ptrdiff_t off = attr - power_supply_attrs;
@@ -78,8 +81,8 @@ static ssize_t power_supply_show_property(struct device *dev,
                        dev_dbg(dev, "driver has no data for `%s' property\n",
                                attr->attr.name);
                else if (ret != -ENODEV)
-                       dev_err(dev, "driver failed to report `%s' property\n",
-                               attr->attr.name);
+                       dev_err(dev, "driver failed to report `%s' property: %zd\n",
+                               attr->attr.name, ret);
                return ret;
        }
 
@@ -95,6 +98,8 @@ static ssize_t power_supply_show_property(struct device *dev,
                return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
        else if (off == POWER_SUPPLY_PROP_TYPE)
                return sprintf(buf, "%s\n", type_text[value.intval]);
+       else if (off == POWER_SUPPLY_PROP_SCOPE)
+               return sprintf(buf, "%s\n", scope_text[value.intval]);
        else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
                return sprintf(buf, "%s\n", value.strval);
 
@@ -167,6 +172,7 @@ static struct device_attribute power_supply_attrs[] = {
        POWER_SUPPLY_ATTR(time_to_full_now),
        POWER_SUPPLY_ATTR(time_to_full_avg),
        POWER_SUPPLY_ATTR(type),
+       POWER_SUPPLY_ATTR(scope),
        /* Properties of type `const char *' */
        POWER_SUPPLY_ATTR(model_name),
        POWER_SUPPLY_ATTR(manufacturer),
index d32d0d7..8b804a5 100644 (file)
@@ -47,6 +47,22 @@ static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
                msecs_to_jiffies(JITTER_DELAY));
 }
 
+static int gather_samples(struct s3c_adc_client *client, int num, int channel)
+{
+       int value, i;
+
+       /* default to 1 if nothing is set */
+       if (num < 1)
+               num = 1;
+
+       value = 0;
+       for (i = 0; i < num; i++)
+               value += s3c_adc_read(client, channel);
+       value /= num;
+
+       return value;
+}
+
 static enum power_supply_property s3c_adc_backup_bat_props[] = {
        POWER_SUPPLY_PROP_VOLTAGE_NOW,
        POWER_SUPPLY_PROP_VOLTAGE_MIN,
@@ -67,7 +83,8 @@ static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
        if (bat->volt_value < 0 ||
                jiffies_to_msecs(jiffies - bat->timestamp) >
                        BAT_POLL_INTERVAL) {
-               bat->volt_value = s3c_adc_read(bat->client,
+               bat->volt_value = gather_samples(bat->client,
+                       bat->pdata->backup_volt_samples,
                        bat->pdata->backup_volt_channel);
                bat->volt_value *= bat->pdata->backup_volt_mult;
                bat->timestamp = jiffies;
@@ -139,9 +156,11 @@ static int s3c_adc_bat_get_property(struct power_supply *psy,
        if (bat->volt_value < 0 || bat->cur_value < 0 ||
                jiffies_to_msecs(jiffies - bat->timestamp) >
                        BAT_POLL_INTERVAL) {
-               bat->volt_value = s3c_adc_read(bat->client,
+               bat->volt_value = gather_samples(bat->client,
+                       bat->pdata->volt_samples,
                        bat->pdata->volt_channel) * bat->pdata->volt_mult;
-               bat->cur_value = s3c_adc_read(bat->client,
+               bat->cur_value = gather_samples(bat->client,
+                       bat->pdata->current_samples,
                        bat->pdata->current_channel) * bat->pdata->current_mult;
                bat->timestamp = jiffies;
        }
@@ -421,17 +440,7 @@ static struct platform_driver s3c_adc_bat_driver = {
        .resume         = s3c_adc_bat_resume,
 };
 
-static int __init s3c_adc_bat_init(void)
-{
-       return platform_driver_register(&s3c_adc_bat_driver);
-}
-module_init(s3c_adc_bat_init);
-
-static void __exit s3c_adc_bat_exit(void)
-{
-       platform_driver_unregister(&s3c_adc_bat_driver);
-}
-module_exit(s3c_adc_bat_exit);
+module_platform_driver(s3c_adc_bat_driver);
 
 MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
 MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
similarity index 52%
rename from drivers/power/bq20z75.c
rename to drivers/power/sbs-battery.c
index 9c5e5be..9ff8af0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Gas Gauge driver for TI's BQ20Z75
+ * Gas Gauge driver for SBS Compliant Batteries
  *
  * Copyright (c) 2010, NVIDIA Corporation.
  *
@@ -28,7 +28,7 @@
 #include <linux/interrupt.h>
 #include <linux/gpio.h>
 
-#include <linux/power/bq20z75.h>
+#include <linux/power/sbs-battery.h>
 
 enum {
        REG_MANUFACTURER_DATA,
@@ -53,7 +53,7 @@ enum {
 /* Battery Mode defines */
 #define BATTERY_MODE_OFFSET            0x03
 #define BATTERY_MODE_MASK              0x8000
-enum bq20z75_battery_mode {
+enum sbs_battery_mode {
        BATTERY_MODE_AMPS,
        BATTERY_MODE_WATTS
 };
@@ -67,62 +67,56 @@ enum bq20z75_battery_mode {
 #define BATTERY_FULL_CHARGED           0x20
 #define BATTERY_FULL_DISCHARGED                0x10
 
-#define BQ20Z75_DATA(_psp, _addr, _min_value, _max_value) { \
+#define SBS_DATA(_psp, _addr, _min_value, _max_value) { \
        .psp = _psp, \
        .addr = _addr, \
        .min_value = _min_value, \
        .max_value = _max_value, \
 }
 
-static const struct bq20z75_device_data {
+static const struct chip_data {
        enum power_supply_property psp;
        u8 addr;
        int min_value;
        int max_value;
-} bq20z75_data[] = {
+} sbs_data[] = {
        [REG_MANUFACTURER_DATA] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_PRESENT, 0x00, 0, 65535),
        [REG_TEMPERATURE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_TEMP, 0x08, 0, 65535),
        [REG_VOLTAGE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
+               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_NOW, 0x09, 0, 20000),
        [REG_CURRENT] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768,
-                       32767),
+               SBS_DATA(POWER_SUPPLY_PROP_CURRENT_NOW, 0x0A, -32768, 32767),
        [REG_CAPACITY] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
+               SBS_DATA(POWER_SUPPLY_PROP_CAPACITY, 0x0E, 0, 100),
        [REG_REMAINING_CAPACITY] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_NOW, 0x0F, 0, 65535),
        [REG_REMAINING_CAPACITY_CHARGE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_NOW, 0x0F, 0, 65535),
        [REG_FULL_CHARGE_CAPACITY] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL, 0x10, 0, 65535),
        [REG_FULL_CHARGE_CAPACITY_CHARGE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL, 0x10, 0, 65535),
        [REG_TIME_TO_EMPTY] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0,
-                       65535),
+               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, 0x12, 0, 65535),
        [REG_TIME_TO_FULL] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0,
-                       65535),
+               SBS_DATA(POWER_SUPPLY_PROP_TIME_TO_FULL_AVG, 0x13, 0, 65535),
        [REG_STATUS] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_STATUS, 0x16, 0, 65535),
        [REG_CYCLE_COUNT] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_CYCLE_COUNT, 0x17, 0, 65535),
        [REG_DESIGN_CAPACITY] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0,
-                       65535),
+               SBS_DATA(POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, 0x18, 0, 65535),
        [REG_DESIGN_CAPACITY_CHARGE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0,
-                       65535),
+               SBS_DATA(POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, 0x18, 0, 65535),
        [REG_DESIGN_VOLTAGE] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0,
-                       65535),
+               SBS_DATA(POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, 0x19, 0, 65535),
        [REG_SERIAL_NUMBER] =
-               BQ20Z75_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
+               SBS_DATA(POWER_SUPPLY_PROP_SERIAL_NUMBER, 0x1C, 0, 65535),
 };
 
-static enum power_supply_property bq20z75_properties[] = {
+static enum power_supply_property sbs_properties[] = {
        POWER_SUPPLY_PROP_STATUS,
        POWER_SUPPLY_PROP_HEALTH,
        POWER_SUPPLY_PROP_PRESENT,
@@ -144,10 +138,10 @@ static enum power_supply_property bq20z75_properties[] = {
        POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 };
 
-struct bq20z75_info {
+struct sbs_info {
        struct i2c_client               *client;
        struct power_supply             power_supply;
-       struct bq20z75_platform_data    *pdata;
+       struct sbs_platform_data        *pdata;
        bool                            is_present;
        bool                            gpio_detect;
        bool                            enable_detection;
@@ -158,14 +152,14 @@ struct bq20z75_info {
        int                             ignore_changes;
 };
 
-static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
+static int sbs_read_word_data(struct i2c_client *client, u8 address)
 {
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
        s32 ret = 0;
        int retries = 1;
 
-       if (bq20z75_device->pdata)
-               retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1);
+       if (chip->pdata)
+               retries = max(chip->pdata->i2c_retry_count + 1, 1);
 
        while (retries > 0) {
                ret = i2c_smbus_read_word_data(client, address);
@@ -184,15 +178,15 @@ static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
        return le16_to_cpu(ret);
 }
 
-static int bq20z75_write_word_data(struct i2c_client *client, u8 address,
+static int sbs_write_word_data(struct i2c_client *client, u8 address,
        u16 value)
 {
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
        s32 ret = 0;
        int retries = 1;
 
-       if (bq20z75_device->pdata)
-               retries = max(bq20z75_device->pdata->i2c_retry_count + 1, 1);
+       if (chip->pdata)
+               retries = max(chip->pdata->i2c_retry_count + 1, 1);
 
        while (retries > 0) {
                ret = i2c_smbus_write_word_data(client, address,
@@ -212,44 +206,41 @@ static int bq20z75_write_word_data(struct i2c_client *client, u8 address,
        return 0;
 }
 
-static int bq20z75_get_battery_presence_and_health(
+static int sbs_get_battery_presence_and_health(
        struct i2c_client *client, enum power_supply_property psp,
        union power_supply_propval *val)
 {
        s32 ret;
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
 
        if (psp == POWER_SUPPLY_PROP_PRESENT &&
-               bq20z75_device->gpio_detect) {
-               ret = gpio_get_value(
-                       bq20z75_device->pdata->battery_detect);
-               if (ret == bq20z75_device->pdata->battery_detect_present)
+               chip->gpio_detect) {
+               ret = gpio_get_value(chip->pdata->battery_detect);
+               if (ret == chip->pdata->battery_detect_present)
                        val->intval = 1;
                else
                        val->intval = 0;
-               bq20z75_device->is_present = val->intval;
+               chip->is_present = val->intval;
                return ret;
        }
 
        /* Write to ManufacturerAccess with
         * ManufacturerAccess command and then
         * read the status */
-       ret = bq20z75_write_word_data(client,
-               bq20z75_data[REG_MANUFACTURER_DATA].addr,
-               MANUFACTURER_ACCESS_STATUS);
+       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
+                                       MANUFACTURER_ACCESS_STATUS);
        if (ret < 0) {
                if (psp == POWER_SUPPLY_PROP_PRESENT)
                        val->intval = 0; /* battery removed */
                return ret;
        }
 
-       ret = bq20z75_read_word_data(client,
-               bq20z75_data[REG_MANUFACTURER_DATA].addr);
+       ret = sbs_read_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr);
        if (ret < 0)
                return ret;
 
-       if (ret < bq20z75_data[REG_MANUFACTURER_DATA].min_value ||
-           ret > bq20z75_data[REG_MANUFACTURER_DATA].max_value) {
+       if (ret < sbs_data[REG_MANUFACTURER_DATA].min_value ||
+           ret > sbs_data[REG_MANUFACTURER_DATA].max_value) {
                val->intval = 0;
                return 0;
        }
@@ -279,24 +270,23 @@ static int bq20z75_get_battery_presence_and_health(
        return 0;
 }
 
-static int bq20z75_get_battery_property(struct i2c_client *client,
+static int sbs_get_battery_property(struct i2c_client *client,
        int reg_offset, enum power_supply_property psp,
        union power_supply_propval *val)
 {
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
        s32 ret;
 
-       ret = bq20z75_read_word_data(client,
-               bq20z75_data[reg_offset].addr);
+       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
        if (ret < 0)
                return ret;
 
        /* returned values are 16 bit */
-       if (bq20z75_data[reg_offset].min_value < 0)
+       if (sbs_data[reg_offset].min_value < 0)
                ret = (s16)ret;
 
-       if (ret >= bq20z75_data[reg_offset].min_value &&
-           ret <= bq20z75_data[reg_offset].max_value) {
+       if (ret >= sbs_data[reg_offset].min_value &&
+           ret <= sbs_data[reg_offset].max_value) {
                val->intval = ret;
                if (psp != POWER_SUPPLY_PROP_STATUS)
                        return 0;
@@ -310,12 +300,12 @@ static int bq20z75_get_battery_property(struct i2c_client *client,
                else
                        val->intval = POWER_SUPPLY_STATUS_CHARGING;
 
-               if (bq20z75_device->poll_time == 0)
-                       bq20z75_device->last_state = val->intval;
-               else if (bq20z75_device->last_state != val->intval) {
-                       cancel_delayed_work_sync(&bq20z75_device->work);
-                       power_supply_changed(&bq20z75_device->power_supply);
-                       bq20z75_device->poll_time = 0;
+               if (chip->poll_time == 0)
+                       chip->last_state = val->intval;
+               else if (chip->last_state != val->intval) {
+                       cancel_delayed_work_sync(&chip->work);
+                       power_supply_changed(&chip->power_supply);
+                       chip->poll_time = 0;
                }
        } else {
                if (psp == POWER_SUPPLY_PROP_STATUS)
@@ -327,7 +317,7 @@ static int bq20z75_get_battery_property(struct i2c_client *client,
        return 0;
 }
 
-static void  bq20z75_unit_adjustment(struct i2c_client *client,
+static void  sbs_unit_adjustment(struct i2c_client *client,
        enum power_supply_property psp, union power_supply_propval *val)
 {
 #define BASE_UNIT_CONVERSION           1000
@@ -338,7 +328,7 @@ static void  bq20z75_unit_adjustment(struct i2c_client *client,
        case POWER_SUPPLY_PROP_ENERGY_NOW:
        case POWER_SUPPLY_PROP_ENERGY_FULL:
        case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN:
-               /* bq20z75 provides energy in units of 10mWh.
+               /* sbs provides energy in units of 10mWh.
                 * Convert to ÂµWh
                 */
                val->intval *= BATTERY_MODE_CAP_MULT_WATT;
@@ -354,7 +344,7 @@ static void  bq20z75_unit_adjustment(struct i2c_client *client,
                break;
 
        case POWER_SUPPLY_PROP_TEMP:
-               /* bq20z75 provides battery temperature in 0.1K
+               /* sbs provides battery temperature in 0.1K
                 * so convert it to 0.1°C
                 */
                val->intval -= TEMP_KELVIN_TO_CELSIUS;
@@ -362,7 +352,7 @@ static void  bq20z75_unit_adjustment(struct i2c_client *client,
 
        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
        case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
-               /* bq20z75 provides time to empty and time to full in minutes.
+               /* sbs provides time to empty and time to full in minutes.
                 * Convert to seconds
                 */
                val->intval *= TIME_UNIT_CONVERSION;
@@ -374,13 +364,12 @@ static void  bq20z75_unit_adjustment(struct i2c_client *client,
        }
 }
 
-static enum bq20z75_battery_mode
-bq20z75_set_battery_mode(struct i2c_client *client,
-       enum bq20z75_battery_mode mode)
+static enum sbs_battery_mode sbs_set_battery_mode(struct i2c_client *client,
+       enum sbs_battery_mode mode)
 {
        int ret, original_val;
 
-       original_val = bq20z75_read_word_data(client, BATTERY_MODE_OFFSET);
+       original_val = sbs_read_word_data(client, BATTERY_MODE_OFFSET);
        if (original_val < 0)
                return original_val;
 
@@ -392,68 +381,67 @@ bq20z75_set_battery_mode(struct i2c_client *client,
        else
                ret = original_val | BATTERY_MODE_MASK;
 
-       ret = bq20z75_write_word_data(client, BATTERY_MODE_OFFSET, ret);
+       ret = sbs_write_word_data(client, BATTERY_MODE_OFFSET, ret);
        if (ret < 0)
                return ret;
 
        return original_val & BATTERY_MODE_MASK;
 }
 
-static int bq20z75_get_battery_capacity(struct i2c_client *client,
+static int sbs_get_battery_capacity(struct i2c_client *client,
        int reg_offset, enum power_supply_property psp,
        union power_supply_propval *val)
 {
        s32 ret;
-       enum bq20z75_battery_mode mode = BATTERY_MODE_WATTS;
+       enum sbs_battery_mode mode = BATTERY_MODE_WATTS;
 
        if (power_supply_is_amp_property(psp))
                mode = BATTERY_MODE_AMPS;
 
-       mode = bq20z75_set_battery_mode(client, mode);
+       mode = sbs_set_battery_mode(client, mode);
        if (mode < 0)
                return mode;
 
-       ret = bq20z75_read_word_data(client, bq20z75_data[reg_offset].addr);
+       ret = sbs_read_word_data(client, sbs_data[reg_offset].addr);
        if (ret < 0)
                return ret;
 
        if (psp == POWER_SUPPLY_PROP_CAPACITY) {
-               /* bq20z75 spec says that this can be >100 %
+               /* sbs spec says that this can be >100 %
                * even if max value is 100 % */
                val->intval = min(ret, 100);
        } else
                val->intval = ret;
 
-       ret = bq20z75_set_battery_mode(client, mode);
+       ret = sbs_set_battery_mode(client, mode);
        if (ret < 0)
                return ret;
 
        return 0;
 }
 
-static char bq20z75_serial[5];
-static int bq20z75_get_battery_serial_number(struct i2c_client *client,
+static char sbs_serial[5];
+static int sbs_get_battery_serial_number(struct i2c_client *client,
        union power_supply_propval *val)
 {
        int ret;
 
-       ret = bq20z75_read_word_data(client,
-               bq20z75_data[REG_SERIAL_NUMBER].addr);
+       ret = sbs_read_word_data(client, sbs_data[REG_SERIAL_NUMBER].addr);
        if (ret < 0)
                return ret;
 
-       ret = sprintf(bq20z75_serial, "%04x", ret);
-       val->strval = bq20z75_serial;
+       ret = sprintf(sbs_serial, "%04x", ret);
+       val->strval = sbs_serial;
 
        return 0;
 }
 
-static int bq20z75_get_property_index(struct i2c_client *client,
+static int sbs_get_property_index(struct i2c_client *client,
        enum power_supply_property psp)
 {
        int count;
-       for (count = 0; count < ARRAY_SIZE(bq20z75_data); count++)
-               if (psp == bq20z75_data[count].psp)
+       for (count = 0; count < ARRAY_SIZE(sbs_data); count++)
+               if (psp == sbs_data[count].psp)
                        return count;
 
        dev_warn(&client->dev,
@@ -462,19 +450,19 @@ static int bq20z75_get_property_index(struct i2c_client *client,
        return -EINVAL;
 }
 
-static int bq20z75_get_property(struct power_supply *psy,
+static int sbs_get_property(struct power_supply *psy,
        enum power_supply_property psp,
        union power_supply_propval *val)
 {
        int ret = 0;
-       struct bq20z75_info *bq20z75_device = container_of(psy,
-                               struct bq20z75_info, power_supply);
-       struct i2c_client *client = bq20z75_device->client;
+       struct sbs_info *chip = container_of(psy,
+                               struct sbs_info, power_supply);
+       struct i2c_client *client = chip->client;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_PRESENT:
        case POWER_SUPPLY_PROP_HEALTH:
-               ret = bq20z75_get_battery_presence_and_health(client, psp, val);
+               ret = sbs_get_battery_presence_and_health(client, psp, val);
                if (psp == POWER_SUPPLY_PROP_PRESENT)
                        return 0;
                break;
@@ -490,15 +478,15 @@ static int bq20z75_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_CHARGE_FULL:
        case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
        case POWER_SUPPLY_PROP_CAPACITY:
-               ret = bq20z75_get_property_index(client, psp);
+               ret = sbs_get_property_index(client, psp);
                if (ret < 0)
                        break;
 
-               ret = bq20z75_get_battery_capacity(client, ret, psp, val);
+               ret = sbs_get_battery_capacity(client, ret, psp, val);
                break;
 
        case POWER_SUPPLY_PROP_SERIAL_NUMBER:
-               ret = bq20z75_get_battery_serial_number(client, val);
+               ret = sbs_get_battery_serial_number(client, val);
                break;
 
        case POWER_SUPPLY_PROP_STATUS:
@@ -509,11 +497,11 @@ static int bq20z75_get_property(struct power_supply *psy,
        case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
        case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
        case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
-               ret = bq20z75_get_property_index(client, psp);
+               ret = sbs_get_property_index(client, psp);
                if (ret < 0)
                        break;
 
-               ret = bq20z75_get_battery_property(client, ret, psp, val);
+               ret = sbs_get_battery_property(client, ret, psp, val);
                break;
 
        default:
@@ -522,25 +510,25 @@ static int bq20z75_get_property(struct power_supply *psy,
                return -EINVAL;
        }
 
-       if (!bq20z75_device->enable_detection)
+       if (!chip->enable_detection)
                goto done;
 
-       if (!bq20z75_device->gpio_detect &&
-               bq20z75_device->is_present != (ret >= 0)) {
-               bq20z75_device->is_present = (ret >= 0);
-               power_supply_changed(&bq20z75_device->power_supply);
+       if (!chip->gpio_detect &&
+               chip->is_present != (ret >= 0)) {
+               chip->is_present = (ret >= 0);
+               power_supply_changed(&chip->power_supply);
        }
 
 done:
        if (!ret) {
                /* Convert units to match requirements for power supply class */
-               bq20z75_unit_adjustment(client, psp, val);
+               sbs_unit_adjustment(client, psp, val);
        }
 
        dev_dbg(&client->dev,
                "%s: property = %d, value = %x\n", __func__, psp, val->intval);
 
-       if (ret && bq20z75_device->is_present)
+       if (ret && chip->is_present)
                return ret;
 
        /* battery not present, so return NODATA for properties */
@@ -550,7 +538,7 @@ done:
        return 0;
 }
 
-static irqreturn_t bq20z75_irq(int irq, void *devid)
+static irqreturn_t sbs_irq(int irq, void *devid)
 {
        struct power_supply *battery = devid;
 
@@ -559,36 +547,35 @@ static irqreturn_t bq20z75_irq(int irq, void *devid)
        return IRQ_HANDLED;
 }
 
-static void bq20z75_external_power_changed(struct power_supply *psy)
+static void sbs_external_power_changed(struct power_supply *psy)
 {
-       struct bq20z75_info *bq20z75_device;
+       struct sbs_info *chip;
 
-       bq20z75_device = container_of(psy, struct bq20z75_info, power_supply);
+       chip = container_of(psy, struct sbs_info, power_supply);
 
-       if (bq20z75_device->ignore_changes > 0) {
-               bq20z75_device->ignore_changes--;
+       if (chip->ignore_changes > 0) {
+               chip->ignore_changes--;
                return;
        }
 
        /* cancel outstanding work */
-       cancel_delayed_work_sync(&bq20z75_device->work);
+       cancel_delayed_work_sync(&chip->work);
 
-       schedule_delayed_work(&bq20z75_device->work, HZ);
-       bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count;
+       schedule_delayed_work(&chip->work, HZ);
+       chip->poll_time = chip->pdata->poll_retry_count;
 }
 
-static void bq20z75_delayed_work(struct work_struct *work)
+static void sbs_delayed_work(struct work_struct *work)
 {
-       struct bq20z75_info *bq20z75_device;
+       struct sbs_info *chip;
        s32 ret;
 
-       bq20z75_device = container_of(work, struct bq20z75_info, work.work);
+       chip = container_of(work, struct sbs_info, work.work);
 
-       ret = bq20z75_read_word_data(bq20z75_device->client,
-                                    bq20z75_data[REG_STATUS].addr);
+       ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr);
        /* if the read failed, give up on this work */
        if (ret < 0) {
-               bq20z75_device->poll_time = 0;
+               chip->poll_time = 0;
                return;
        }
 
@@ -601,62 +588,145 @@ static void bq20z75_delayed_work(struct work_struct *work)
        else
                ret = POWER_SUPPLY_STATUS_CHARGING;
 
-       if (bq20z75_device->last_state != ret) {
-               bq20z75_device->poll_time = 0;
-               power_supply_changed(&bq20z75_device->power_supply);
+       if (chip->last_state != ret) {
+               chip->poll_time = 0;
+               power_supply_changed(&chip->power_supply);
                return;
        }
-       if (bq20z75_device->poll_time > 0) {
-               schedule_delayed_work(&bq20z75_device->work, HZ);
-               bq20z75_device->poll_time--;
+       if (chip->poll_time > 0) {
+               schedule_delayed_work(&chip->work, HZ);
+               chip->poll_time--;
                return;
        }
 }
 
-static int __devinit bq20z75_probe(struct i2c_client *client,
+#if defined(CONFIG_OF)
+
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+
+static const struct of_device_id sbs_dt_ids[] = {
+       { .compatible = "sbs,sbs-battery" },
+       { .compatible = "ti,bq20z75" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, sbs_dt_ids);
+
+static struct sbs_platform_data *sbs_of_populate_pdata(
+               struct i2c_client *client)
+{
+       struct device_node *of_node = client->dev.of_node;
+       struct sbs_platform_data *pdata = client->dev.platform_data;
+       enum of_gpio_flags gpio_flags;
+       int rc;
+       u32 prop;
+
+       /* verify this driver matches this device */
+       if (!of_node)
+               return NULL;
+
+       /* if platform data is set, honor it */
+       if (pdata)
+               return pdata;
+
+       /* first make sure at least one property is set, otherwise
+        * it won't change behavior from running without pdata.
+        */
+       if (!of_get_property(of_node, "sbs,i2c-retry-count", NULL) &&
+               !of_get_property(of_node, "sbs,poll-retry-count", NULL) &&
+               !of_get_property(of_node, "sbs,battery-detect-gpios", NULL))
+               goto of_out;
+
+       pdata = devm_kzalloc(&client->dev, sizeof(struct sbs_platform_data),
+                               GFP_KERNEL);
+       if (!pdata)
+               goto of_out;
+
+       rc = of_property_read_u32(of_node, "sbs,i2c-retry-count", &prop);
+       if (!rc)
+               pdata->i2c_retry_count = prop;
+
+       rc = of_property_read_u32(of_node, "sbs,poll-retry-count", &prop);
+       if (!rc)
+               pdata->poll_retry_count = prop;
+
+       if (!of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) {
+               pdata->battery_detect = -1;
+               goto of_out;
+       }
+
+       pdata->battery_detect = of_get_named_gpio_flags(of_node,
+                       "sbs,battery-detect-gpios", 0, &gpio_flags);
+
+       if (gpio_flags & OF_GPIO_ACTIVE_LOW)
+               pdata->battery_detect_present = 0;
+       else
+               pdata->battery_detect_present = 1;
+
+of_out:
+       return pdata;
+}
+#else
+#define sbs_dt_ids NULL
+static struct sbs_platform_data *sbs_of_populate_pdata(
+       struct i2c_client *client)
+{
+       return client->dev.platform_data;
+}
+#endif
+
+static int __devinit sbs_probe(struct i2c_client *client,
        const struct i2c_device_id *id)
 {
-       struct bq20z75_info *bq20z75_device;
-       struct bq20z75_platform_data *pdata = client->dev.platform_data;
+       struct sbs_info *chip;
+       struct sbs_platform_data *pdata = client->dev.platform_data;
        int rc;
        int irq;
+       char *name;
 
-       bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
-       if (!bq20z75_device)
+       name = kasprintf(GFP_KERNEL, "sbs-%s", dev_name(&client->dev));
+       if (!name) {
+               dev_err(&client->dev, "Failed to allocate device name\n");
                return -ENOMEM;
+       }
+
+       chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL);
+       if (!chip) {
+               rc = -ENOMEM;
+               goto exit_free_name;
+       }
 
-       bq20z75_device->client = client;
-       bq20z75_device->enable_detection = false;
-       bq20z75_device->gpio_detect = false;
-       bq20z75_device->power_supply.name = "battery";
-       bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
-       bq20z75_device->power_supply.properties = bq20z75_properties;
-       bq20z75_device->power_supply.num_properties =
-               ARRAY_SIZE(bq20z75_properties);
-       bq20z75_device->power_supply.get_property = bq20z75_get_property;
+       chip->client = client;
+       chip->enable_detection = false;
+       chip->gpio_detect = false;
+       chip->power_supply.name = name;
+       chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
+       chip->power_supply.properties = sbs_properties;
+       chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties);
+       chip->power_supply.get_property = sbs_get_property;
        /* ignore first notification of external change, it is generated
         * from the power_supply_register call back
         */
-       bq20z75_device->ignore_changes = 1;
-       bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
-       bq20z75_device->power_supply.external_power_changed =
-               bq20z75_external_power_changed;
+       chip->ignore_changes = 1;
+       chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
+       chip->power_supply.external_power_changed = sbs_external_power_changed;
+
+       pdata = sbs_of_populate_pdata(client);
 
        if (pdata) {
-               bq20z75_device->gpio_detect =
-                       gpio_is_valid(pdata->battery_detect);
-               bq20z75_device->pdata = pdata;
+               chip->gpio_detect = gpio_is_valid(pdata->battery_detect);
+               chip->pdata = pdata;
        }
 
-       i2c_set_clientdata(client, bq20z75_device);
+       i2c_set_clientdata(client, chip);
 
-       if (!bq20z75_device->gpio_detect)
+       if (!chip->gpio_detect)
                goto skip_gpio;
 
        rc = gpio_request(pdata->battery_detect, dev_name(&client->dev));
        if (rc) {
                dev_warn(&client->dev, "Failed to request gpio: %d\n", rc);
-               bq20z75_device->gpio_detect = false;
+               chip->gpio_detect = false;
                goto skip_gpio;
        }
 
@@ -664,7 +734,7 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
        if (rc) {
                dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc);
                gpio_free(pdata->battery_detect);
-               bq20z75_device->gpio_detect = false;
+               chip->gpio_detect = false;
                goto skip_gpio;
        }
 
@@ -672,25 +742,25 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
        if (irq <= 0) {
                dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq);
                gpio_free(pdata->battery_detect);
-               bq20z75_device->gpio_detect = false;
+               chip->gpio_detect = false;
                goto skip_gpio;
        }
 
-       rc = request_irq(irq, bq20z75_irq,
+       rc = request_irq(irq, sbs_irq,
                IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
-               dev_name(&client->dev), &bq20z75_device->power_supply);
+               dev_name(&client->dev), &chip->power_supply);
        if (rc) {
                dev_warn(&client->dev, "Failed to request irq: %d\n", rc);
                gpio_free(pdata->battery_detect);
-               bq20z75_device->gpio_detect = false;
+               chip->gpio_detect = false;
                goto skip_gpio;
        }
 
-       bq20z75_device->irq = irq;
+       chip->irq = irq;
 
 skip_gpio:
 
-       rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
+       rc = power_supply_register(&client->dev, &chip->power_supply);
        if (rc) {
                dev_err(&client->dev,
                        "%s: Failed to register power supply\n", __func__);
@@ -700,95 +770,100 @@ skip_gpio:
        dev_info(&client->dev,
                "%s: battery gas gauge device registered\n", client->name);
 
-       INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work);
+       INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
 
-       bq20z75_device->enable_detection = true;
+       chip->enable_detection = true;
 
        return 0;
 
 exit_psupply:
-       if (bq20z75_device->irq)
-               free_irq(bq20z75_device->irq, &bq20z75_device->power_supply);
-       if (bq20z75_device->gpio_detect)
+       if (chip->irq)
+               free_irq(chip->irq, &chip->power_supply);
+       if (chip->gpio_detect)
                gpio_free(pdata->battery_detect);
 
-       kfree(bq20z75_device);
+       kfree(chip);
+
+exit_free_name:
+       kfree(name);
 
        return rc;
 }
 
-static int __devexit bq20z75_remove(struct i2c_client *client)
+static int __devexit sbs_remove(struct i2c_client *client)
 {
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
 
-       if (bq20z75_device->irq)
-               free_irq(bq20z75_device->irq, &bq20z75_device->power_supply);
-       if (bq20z75_device->gpio_detect)
-               gpio_free(bq20z75_device->pdata->battery_detect);
+       if (chip->irq)
+               free_irq(chip->irq, &chip->power_supply);
+       if (chip->gpio_detect)
+               gpio_free(chip->pdata->battery_detect);
 
-       power_supply_unregister(&bq20z75_device->power_supply);
+       power_supply_unregister(&chip->power_supply);
 
-       cancel_delayed_work_sync(&bq20z75_device->work);
+       cancel_delayed_work_sync(&chip->work);
 
-       kfree(bq20z75_device);
-       bq20z75_device = NULL;
+       kfree(chip->power_supply.name);
+       kfree(chip);
+       chip = NULL;
 
        return 0;
 }
 
 #if defined CONFIG_PM
-static int bq20z75_suspend(struct i2c_client *client,
+static int sbs_suspend(struct i2c_client *client,
        pm_message_t state)
 {
-       struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
+       struct sbs_info *chip = i2c_get_clientdata(client);
        s32 ret;
 
-       if (bq20z75_device->poll_time > 0)
-               cancel_delayed_work_sync(&bq20z75_device->work);
+       if (chip->poll_time > 0)
+               cancel_delayed_work_sync(&chip->work);
 
        /* write to manufacturer access with sleep command */
-       ret = bq20z75_write_word_data(client,
-               bq20z75_data[REG_MANUFACTURER_DATA].addr,
+       ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
                MANUFACTURER_ACCESS_SLEEP);
-       if (bq20z75_device->is_present && ret < 0)
+       if (chip->is_present && ret < 0)
                return ret;
 
        return 0;
 }
 #else
-#define bq20z75_suspend                NULL
+#define sbs_suspend            NULL
 #endif
-/* any smbus transaction will wake up bq20z75 */
-#define bq20z75_resume         NULL
+/* any smbus transaction will wake up sbs */
+#define sbs_resume             NULL
 
-static const struct i2c_device_id bq20z75_id[] = {
+static const struct i2c_device_id sbs_id[] = {
        { "bq20z75", 0 },
+       { "sbs-battery", 1 },
        {}
 };
-MODULE_DEVICE_TABLE(i2c, bq20z75_id);
-
-static struct i2c_driver bq20z75_battery_driver = {
-       .probe          = bq20z75_probe,
-       .remove         = __devexit_p(bq20z75_remove),
-       .suspend        = bq20z75_suspend,
-       .resume         = bq20z75_resume,
-       .id_table       = bq20z75_id,
+MODULE_DEVICE_TABLE(i2c, sbs_id);
+
+static struct i2c_driver sbs_battery_driver = {
+       .probe          = sbs_probe,
+       .remove         = __devexit_p(sbs_remove),
+       .suspend        = sbs_suspend,
+       .resume         = sbs_resume,
+       .id_table       = sbs_id,
        .driver = {
-               .name   = "bq20z75-battery",
+               .name   = "sbs-battery",
+               .of_match_table = sbs_dt_ids,
        },
 };
 
-static int __init bq20z75_battery_init(void)
+static int __init sbs_battery_init(void)
 {
-       return i2c_add_driver(&bq20z75_battery_driver);
+       return i2c_add_driver(&sbs_battery_driver);
 }
-module_init(bq20z75_battery_init);
+module_init(sbs_battery_init);
 
-static void __exit bq20z75_battery_exit(void)
+static void __exit sbs_battery_exit(void)
 {
-       i2c_del_driver(&bq20z75_battery_driver);
+       i2c_del_driver(&sbs_battery_driver);
 }
-module_exit(bq20z75_battery_exit);
+module_exit(sbs_battery_exit);
 
-MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
+MODULE_DESCRIPTION("SBS battery monitor driver");
 MODULE_LICENSE("GPL");
index 53f0d35..28bbe7e 100644 (file)
@@ -307,25 +307,20 @@ static struct tosa_bat tosa_bat_bu = {
        .adc_temp_divider = -1,
 };
 
-static struct {
-       int gpio;
-       char *name;
-       bool output;
-       int value;
-} gpios[] = {
-       { TOSA_GPIO_CHARGE_OFF,         "main charge off",      1, 1 },
-       { TOSA_GPIO_CHARGE_OFF_JC,      "jacket charge off",    1, 1 },
-       { TOSA_GPIO_BAT_SW_ON,          "battery switch",       1, 0 },
-       { TOSA_GPIO_BAT0_V_ON,          "main battery",         1, 0 },
-       { TOSA_GPIO_BAT1_V_ON,          "jacket battery",       1, 0 },
-       { TOSA_GPIO_BAT1_TH_ON,         "main battery temp",    1, 0 },
-       { TOSA_GPIO_BAT0_TH_ON,         "jacket battery temp",  1, 0 },
-       { TOSA_GPIO_BU_CHRG_ON,         "backup battery",       1, 0 },
-       { TOSA_GPIO_BAT0_CRG,           "main battery full",    0, 0 },
-       { TOSA_GPIO_BAT1_CRG,           "jacket battery full",  0, 0 },
-       { TOSA_GPIO_BAT0_LOW,           "main battery low",     0, 0 },
-       { TOSA_GPIO_BAT1_LOW,           "jacket battery low",   0, 0 },
-       { TOSA_GPIO_JACKET_DETECT,      "jacket detect",        0, 0 },
+static struct gpio tosa_bat_gpios[] = {
+       { TOSA_GPIO_CHARGE_OFF,    GPIOF_OUT_INIT_HIGH, "main charge off" },
+       { TOSA_GPIO_CHARGE_OFF_JC, GPIOF_OUT_INIT_HIGH, "jacket charge off" },
+       { TOSA_GPIO_BAT_SW_ON,     GPIOF_OUT_INIT_LOW,  "battery switch" },
+       { TOSA_GPIO_BAT0_V_ON,     GPIOF_OUT_INIT_LOW,  "main battery" },
+       { TOSA_GPIO_BAT1_V_ON,     GPIOF_OUT_INIT_LOW,  "jacket battery" },
+       { TOSA_GPIO_BAT1_TH_ON,    GPIOF_OUT_INIT_LOW,  "main battery temp" },
+       { TOSA_GPIO_BAT0_TH_ON,    GPIOF_OUT_INIT_LOW,  "jacket battery temp" },
+       { TOSA_GPIO_BU_CHRG_ON,    GPIOF_OUT_INIT_LOW,  "backup battery" },
+       { TOSA_GPIO_BAT0_CRG,      GPIOF_IN,            "main battery full" },
+       { TOSA_GPIO_BAT1_CRG,      GPIOF_IN,            "jacket battery full" },
+       { TOSA_GPIO_BAT0_LOW,      GPIOF_IN,            "main battery low" },
+       { TOSA_GPIO_BAT1_LOW,      GPIOF_IN,            "jacket battery low" },
+       { TOSA_GPIO_JACKET_DETECT, GPIOF_IN,            "jacket detect" },
 };
 
 #ifdef CONFIG_PM
@@ -350,27 +345,13 @@ static int tosa_bat_resume(struct platform_device *dev)
 static int __devinit tosa_bat_probe(struct platform_device *dev)
 {
        int ret;
-       int i;
 
        if (!machine_is_tosa())
                return -ENODEV;
 
-       for (i = 0; i < ARRAY_SIZE(gpios); i++) {
-               ret = gpio_request(gpios[i].gpio, gpios[i].name);
-               if (ret) {
-                       i--;
-                       goto err_gpio;
-               }
-
-               if (gpios[i].output)
-                       ret = gpio_direction_output(gpios[i].gpio,
-                                       gpios[i].value);
-               else
-                       ret = gpio_direction_input(gpios[i].gpio);
-
-               if (ret)
-                       goto err_gpio;
-       }
+       ret = gpio_request_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
+       if (ret)
+               return ret;
 
        mutex_init(&tosa_bat_main.work_lock);
        mutex_init(&tosa_bat_jacket.work_lock);
@@ -424,18 +405,12 @@ err_psy_reg_main:
        /* see comment in tosa_bat_remove */
        cancel_work_sync(&bat_work);
 
-       i--;
-err_gpio:
-       for (; i >= 0; i--)
-               gpio_free(gpios[i].gpio);
-
+       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
        return ret;
 }
 
 static int __devexit tosa_bat_remove(struct platform_device *dev)
 {
-       int i;
-
        free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket);
        free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket);
        free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main);
@@ -450,10 +425,7 @@ static int __devexit tosa_bat_remove(struct platform_device *dev)
         * unregistered now.
         */
        cancel_work_sync(&bat_work);
-
-       for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i--)
-               gpio_free(gpios[i].gpio);
-
+       gpio_free_array(tosa_bat_gpios, ARRAY_SIZE(tosa_bat_gpios));
        return 0;
 }
 
@@ -466,18 +438,7 @@ static struct platform_driver tosa_bat_driver = {
        .resume         = tosa_bat_resume,
 };
 
-static int __init tosa_bat_init(void)
-{
-       return platform_driver_register(&tosa_bat_driver);
-}
-
-static void __exit tosa_bat_exit(void)
-{
-       platform_driver_unregister(&tosa_bat_driver);
-}
-
-module_init(tosa_bat_init);
-module_exit(tosa_bat_exit);
+module_platform_driver(tosa_bat_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Dmitry Baryshkov");
index e648cbe..6243e69 100644 (file)
@@ -226,17 +226,7 @@ static struct platform_driver wm831x_backup_driver = {
        },
 };
 
-static int __init wm831x_backup_init(void)
-{
-       return platform_driver_register(&wm831x_backup_driver);
-}
-module_init(wm831x_backup_init);
-
-static void __exit wm831x_backup_exit(void)
-{
-       platform_driver_unregister(&wm831x_backup_driver);
-}
-module_exit(wm831x_backup_exit);
+module_platform_driver(wm831x_backup_driver);
 
 MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
index 6cc2ca6..987332b 100644 (file)
@@ -27,6 +27,7 @@ struct wm831x_power {
        char wall_name[20];
        char usb_name[20];
        char battery_name[20];
+       bool have_battery;
 };
 
 static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
@@ -449,7 +450,8 @@ static irqreturn_t wm831x_bat_irq(int irq, void *data)
 
        /* The battery charger is autonomous so we don't need to do
         * anything except kick user space */
-       power_supply_changed(&wm831x_power->battery);
+       if (wm831x_power->have_battery)
+               power_supply_changed(&wm831x_power->battery);
 
        return IRQ_HANDLED;
 }
@@ -479,7 +481,8 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
        dev_dbg(wm831x->dev, "Power source changed\n");
 
        /* Just notify for everything - little harm in overnotifying. */
-       power_supply_changed(&wm831x_power->battery);
+       if (wm831x_power->have_battery)
+               power_supply_changed(&wm831x_power->battery);
        power_supply_changed(&wm831x_power->usb);
        power_supply_changed(&wm831x_power->wall);
 
@@ -537,15 +540,6 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        if (ret)
                goto err_kmalloc;
 
-       battery->name = power->battery_name;
-       battery->properties = wm831x_bat_props;
-       battery->num_properties = ARRAY_SIZE(wm831x_bat_props);
-       battery->get_property = wm831x_bat_get_prop;
-       battery->use_for_apm = 1;
-       ret = power_supply_register(&pdev->dev, battery);
-       if (ret)
-               goto err_wall;
-
        usb->name = power->usb_name,
        usb->type = POWER_SUPPLY_TYPE_USB;
        usb->properties = wm831x_usb_props;
@@ -553,7 +547,23 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        usb->get_property = wm831x_usb_get_prop;
        ret = power_supply_register(&pdev->dev, usb);
        if (ret)
-               goto err_battery;
+               goto err_wall;
+
+       ret = wm831x_reg_read(wm831x, WM831X_CHARGER_CONTROL_1);
+       if (ret < 0)
+               goto err_wall;
+       power->have_battery = ret & WM831X_CHG_ENA;
+
+       if (power->have_battery) {
+                   battery->name = power->battery_name;
+                   battery->properties = wm831x_bat_props;
+                   battery->num_properties = ARRAY_SIZE(wm831x_bat_props);
+                   battery->get_property = wm831x_bat_get_prop;
+                   battery->use_for_apm = 1;
+                   ret = power_supply_register(&pdev->dev, battery);
+                   if (ret)
+                           goto err_usb;
+       }
 
        irq = platform_get_irq_byname(pdev, "SYSLO");
        ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
@@ -562,7 +572,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
                        irq, ret);
-               goto err_usb;
+               goto err_battery;
        }
 
        irq = platform_get_irq_byname(pdev, "PWR SRC");
@@ -601,10 +611,11 @@ err_bat_irq:
 err_syslo:
        irq = platform_get_irq_byname(pdev, "SYSLO");
        free_irq(irq, power);
+err_battery:
+       if (power->have_battery)
+               power_supply_unregister(battery);
 err_usb:
        power_supply_unregister(usb);
-err_battery:
-       power_supply_unregister(battery);
 err_wall:
        power_supply_unregister(wall);
 err_kmalloc:
@@ -628,7 +639,8 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
        irq = platform_get_irq_byname(pdev, "SYSLO");
        free_irq(irq, wm831x_power);
 
-       power_supply_unregister(&wm831x_power->battery);
+       if (wm831x_power->have_battery)
+               power_supply_unregister(&wm831x_power->battery);
        power_supply_unregister(&wm831x_power->wall);
        power_supply_unregister(&wm831x_power->usb);
        kfree(wm831x_power);
@@ -643,17 +655,7 @@ static struct platform_driver wm831x_power_driver = {
        },
 };
 
-static int __init wm831x_power_init(void)
-{
-       return platform_driver_register(&wm831x_power_driver);
-}
-module_init(wm831x_power_init);
-
-static void __exit wm831x_power_exit(void)
-{
-       platform_driver_unregister(&wm831x_power_driver);
-}
-module_exit(wm831x_power_exit);
+module_platform_driver(wm831x_power_driver);
 
 MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
index 0693902..fae04d3 100644 (file)
@@ -522,17 +522,7 @@ static struct platform_driver wm8350_power_driver = {
        },
 };
 
-static int __init wm8350_power_init(void)
-{
-       return platform_driver_register(&wm8350_power_driver);
-}
-module_init(wm8350_power_init);
-
-static void __exit wm8350_power_exit(void)
-{
-       platform_driver_unregister(&wm8350_power_driver);
-}
-module_exit(wm8350_power_exit);
+module_platform_driver(wm8350_power_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Power supply driver for WM8350");
index 156559e..d2d4c08 100644 (file)
@@ -25,9 +25,8 @@
 #include <linux/irq.h>
 #include <linux/slab.h>
 
-static DEFINE_MUTEX(bat_lock);
 static struct work_struct bat_work;
-static struct mutex work_lock;
+static DEFINE_MUTEX(work_lock);
 static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
 static enum power_supply_property *prop;
 
@@ -181,8 +180,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
        if (dev->id != -1)
                return -EINVAL;
 
-       mutex_init(&work_lock);
-
        if (!pdata) {
                dev_err(&dev->dev, "No platform_data supplied\n");
                return -EINVAL;
@@ -196,7 +193,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
                if (ret)
                        goto err2;
                ret = request_irq(gpio_to_irq(pdata->charge_gpio),
-                               wm97xx_chrg_irq, IRQF_DISABLED,
+                               wm97xx_chrg_irq, 0,
                                "AC Detect", dev);
                if (ret)
                        goto err2;
@@ -291,18 +288,7 @@ static struct platform_driver wm97xx_bat_driver = {
        .remove         = __devexit_p(wm97xx_bat_remove),
 };
 
-static int __init wm97xx_bat_init(void)
-{
-       return platform_driver_register(&wm97xx_bat_driver);
-}
-
-static void __exit wm97xx_bat_exit(void)
-{
-       platform_driver_unregister(&wm97xx_bat_driver);
-}
-
-module_init(wm97xx_bat_init);
-module_exit(wm97xx_bat_exit);
+module_platform_driver(wm97xx_bat_driver);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
index d119c38..636ebb2 100644 (file)
@@ -218,7 +218,7 @@ static int __devinit z2_batt_probe(struct i2c_client *client,
                irq_set_irq_type(gpio_to_irq(info->charge_gpio),
                                 IRQ_TYPE_EDGE_BOTH);
                ret = request_irq(gpio_to_irq(info->charge_gpio),
-                               z2_charge_switch_irq, IRQF_DISABLED,
+                               z2_charge_switch_irq, 0,
                                "AC Detect", charger);
                if (ret)
                        goto err3;
@@ -313,7 +313,7 @@ static struct i2c_driver z2_batt_driver = {
                .pm     = Z2_BATTERY_PM_OPS
        },
        .probe          = z2_batt_probe,
-       .remove         = z2_batt_remove,
+       .remove         = __devexit_p(z2_batt_remove),
        .id_table       = z2_batt_id,
 };
 
diff --git a/include/linux/lp8727.h b/include/linux/lp8727.h
new file mode 100755 (executable)
index 0000000..d21fa28
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *                     Copyright (C) 2011 National Semiconductor
+ *
+ * This program 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.
+ */
+
+#ifndef _LP8727_H
+#define _LP8727_H
+
+enum lp8727_eoc_level {
+       EOC_5P,
+       EOC_10P,
+       EOC_16P,
+       EOC_20P,
+       EOC_25P,
+       EOC_33P,
+       EOC_50P,
+};
+
+enum lp8727_ichg {
+       ICHG_90mA,
+       ICHG_100mA,
+       ICHG_400mA,
+       ICHG_450mA,
+       ICHG_500mA,
+       ICHG_600mA,
+       ICHG_700mA,
+       ICHG_800mA,
+       ICHG_900mA,
+       ICHG_1000mA,
+};
+
+struct lp8727_chg_param {
+       /* end of charge level setting */
+       enum lp8727_eoc_level eoc_level;
+       /* charging current */
+       enum lp8727_ichg ichg;
+};
+
+struct lp8727_platform_data {
+       u8 (*get_batt_present)(void);
+       u16 (*get_batt_level)(void);
+       u8 (*get_batt_capacity)(void);
+       u8 (*get_batt_temp)(void);
+       struct lp8727_chg_param ac;
+       struct lp8727_chg_param usb;
+};
+
+#endif
index 5259dfe..b8e6d94 100644 (file)
@@ -167,9 +167,6 @@ enum {
        MAX8925_IRQ_VCHG_DC_OVP,
        MAX8925_IRQ_VCHG_DC_F,
        MAX8925_IRQ_VCHG_DC_R,
-       MAX8925_IRQ_VCHG_USB_OVP,
-       MAX8925_IRQ_VCHG_USB_F,
-       MAX8925_IRQ_VCHG_USB_R,
        MAX8925_IRQ_VCHG_THM_OK_R,
        MAX8925_IRQ_VCHG_THM_OK_F,
        MAX8925_IRQ_VCHG_SYSLOW_F,
@@ -223,6 +220,10 @@ struct max8925_power_pdata {
        unsigned        batt_detect:1;
        unsigned        topoff_threshold:2;
        unsigned        fast_charge:3;  /* charge current */
+       unsigned        no_temp_support:1; /* set if no temperature detect */
+       unsigned        no_insert_detect:1; /* set if no ac insert detect */
+       char            **supplied_to;
+       int             num_supplicants;
 };
 
 /*
index c9e4d81..2bb62bf 100644 (file)
@@ -35,6 +35,8 @@ struct pda_power_pdata {
        unsigned int polling_interval; /* msecs, default is 2000 */
 
        unsigned long ac_max_uA; /* current to draw when on AC */
+
+       bool use_otg_notifier;
 };
 
 #endif /* __PDA_POWER_H__ */
diff --git a/include/linux/power/charger-manager.h b/include/linux/power/charger-manager.h
new file mode 100644 (file)
index 0000000..4f75e53
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd.
+ * MyungJoo.Ham <myungjoo.ham@samsung.com>
+ *
+ * Charger Manager.
+ * This framework enables to control and multiple chargers and to
+ * monitor charging even in the context of suspend-to-RAM with
+ * an interface combining the chargers.
+ *
+ * This program 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.
+**/
+
+#ifndef _CHARGER_MANAGER_H
+#define _CHARGER_MANAGER_H
+
+#include <linux/power_supply.h>
+
+enum data_source {
+       CM_FUEL_GAUGE,
+       CM_CHARGER_STAT,
+};
+
+enum polling_modes {
+       CM_POLL_DISABLE = 0,
+       CM_POLL_ALWAYS,
+       CM_POLL_EXTERNAL_POWER_ONLY,
+       CM_POLL_CHARGING_ONLY,
+};
+
+/**
+ * struct charger_global_desc
+ * @rtc_name: the name of RTC used to wake up the system from suspend.
+ * @rtc_only_wakeup:
+ *     If the system is woken up by waekup-sources other than the RTC or
+ *     callbacks, Charger Manager should recognize with
+ *     rtc_only_wakeup() returning false.
+ *     If the RTC given to CM is the only wakeup reason,
+ *     rtc_only_wakeup should return true.
+ */
+struct charger_global_desc {
+       char *rtc_name;
+
+       bool (*rtc_only_wakeup)(void);
+};
+
+/**
+ * struct charger_desc
+ * @psy_name: the name of power-supply-class for charger manager
+ * @polling_mode:
+ *     Determine which polling mode will be used
+ * @fullbatt_uV: voltage in microvolt
+ *     If it is not being charged and VBATT >= fullbatt_uV,
+ *     it is assumed to be full.
+ * @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
+ * @psy_charger_stat: the names of power-supply for chargers
+ * @num_charger_regulator: the number of entries in charger_regulators
+ * @charger_regulators: array of regulator_bulk_data for chargers
+ * @psy_fuel_gauge: the name of power-supply for fuel gauge
+ * @temperature_out_of_range:
+ *     Determine whether the status is overheat or cold or normal.
+ *     return_value > 0: overheat
+ *     return_value == 0: normal
+ *     return_value < 0: cold
+ * @measure_battery_temp:
+ *     true: measure battery temperature
+ *     false: measure ambient temperature
+ */
+struct charger_desc {
+       char *psy_name;
+
+       enum polling_modes polling_mode;
+       unsigned int polling_interval_ms;
+
+       unsigned int fullbatt_uV;
+
+       enum data_source battery_present;
+
+       char **psy_charger_stat;
+
+       int num_charger_regulators;
+       struct regulator_bulk_data *charger_regulators;
+
+       char *psy_fuel_gauge;
+
+       int (*temperature_out_of_range)(int *mC);
+       bool measure_battery_temp;
+};
+
+#define PSY_NAME_MAX   30
+
+/**
+ * struct charger_manager
+ * @entry: entry for list
+ * @dev: device pointer
+ * @desc: instance of charger_desc
+ * @fuel_gauge: power_supply for fuel gauge
+ * @charger_stat: array of power_supply for chargers
+ * @charger_enabled: the state of charger
+ * @emergency_stop:
+ *     When setting true, stop charging
+ * @last_temp_mC: the measured temperature in milli-Celsius
+ * @psy_name_buf: the name of power-supply-class for charger manager
+ * @charger_psy: power_supply for charger manager
+ * @status_save_ext_pwr_inserted:
+ *     saved status of external power before entering suspend-to-RAM
+ * @status_save_batt:
+ *     saved status of battery before entering suspend-to-RAM
+ */
+struct charger_manager {
+       struct list_head entry;
+       struct device *dev;
+       struct charger_desc *desc;
+
+       struct power_supply *fuel_gauge;
+       struct power_supply **charger_stat;
+
+       bool charger_enabled;
+
+       int emergency_stop;
+       int last_temp_mC;
+
+       char psy_name_buf[PSY_NAME_MAX + 1];
+       struct power_supply charger_psy;
+
+       bool status_save_ext_pwr_inserted;
+       bool status_save_batt;
+};
+
+#ifdef CONFIG_CHARGER_MANAGER
+extern int setup_charger_manager(struct charger_global_desc *gd);
+extern bool cm_suspend_again(void);
+#else
+static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
+{ }
+
+static bool __maybe_unused cm_suspend_again(void)
+{
+       return false;
+}
+#endif
+
+#endif /* _CHARGER_MANAGER_H */
similarity index 85%
rename from include/linux/power/bq20z75.h
rename to include/linux/power/sbs-battery.h
index 1398eb0..2b0a9d9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Gas Gauge driver for TI's BQ20Z75
+ * Gas Gauge driver for SBS Compliant Gas Gauges
  *
  * Copyright (c) 2010, NVIDIA Corporation.
  *
  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 
-#ifndef __LINUX_POWER_BQ20Z75_H_
-#define __LINUX_POWER_BQ20Z75_H_
+#ifndef __LINUX_POWER_SBS_BATTERY_H_
+#define __LINUX_POWER_SBS_BATTERY_H_
 
 #include <linux/power_supply.h>
 #include <linux/types.h>
 
 /**
- * struct bq20z75_platform_data - platform data for bq20z75 devices
+ * struct sbs_platform_data - platform data for sbs devices
  * @battery_detect:            GPIO which is used to detect battery presence
  * @battery_detect_present:    gpio state when battery is present (0 / 1)
  * @i2c_retry_count:           # of times to retry on i2c IO failure
  * @poll_retry_count:          # of times to retry looking for new status after
  *                             external change notification
  */
-struct bq20z75_platform_data {
+struct sbs_platform_data {
        int battery_detect;
        int battery_detect_present;
        int i2c_retry_count;
index 204c18d..fa9b962 100644 (file)
@@ -74,6 +74,12 @@ enum {
        POWER_SUPPLY_CAPACITY_LEVEL_FULL,
 };
 
+enum {
+       POWER_SUPPLY_SCOPE_UNKNOWN = 0,
+       POWER_SUPPLY_SCOPE_SYSTEM,
+       POWER_SUPPLY_SCOPE_DEVICE,
+};
+
 enum power_supply_property {
        /* Properties of type `int' */
        POWER_SUPPLY_PROP_STATUS = 0,
@@ -116,6 +122,7 @@ enum power_supply_property {
        POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
        POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
        POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
+       POWER_SUPPLY_PROP_SCOPE,
        /* Properties of type `const char *' */
        POWER_SUPPLY_PROP_MODEL_NAME,
        POWER_SUPPLY_PROP_MANUFACTURER,
@@ -123,7 +130,8 @@ enum power_supply_property {
 };
 
 enum power_supply_type {
-       POWER_SUPPLY_TYPE_BATTERY = 0,
+       POWER_SUPPLY_TYPE_UNKNOWN = 0,
+       POWER_SUPPLY_TYPE_BATTERY,
        POWER_SUPPLY_TYPE_UPS,
        POWER_SUPPLY_TYPE_MAINS,
        POWER_SUPPLY_TYPE_USB,          /* Standard Downstream Port */
@@ -211,6 +219,7 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
 extern int power_supply_register(struct device *parent,
                                 struct power_supply *psy);
 extern void power_supply_unregister(struct power_supply *psy);
+extern int power_supply_powers(struct power_supply *psy, struct device *dev);
 
 /* For APM emulation, think legacy userspace. */
 extern struct class *power_supply_class;
index fbe58b7..99dadbf 100644 (file)
@@ -25,6 +25,10 @@ struct s3c_adc_bat_pdata {
        const unsigned int current_channel;
        const unsigned int backup_volt_channel;
 
+       const unsigned int volt_samples;
+       const unsigned int current_samples;
+       const unsigned int backup_volt_samples;
+
        const unsigned int volt_mult;
        const unsigned int current_mult;
        const unsigned int backup_volt_mult;