summaryrefslogtreecommitdiffstats
path: root/drivers/power
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-07-31 06:24:50 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2011-07-31 06:24:50 -1000
commitf0d15c96d4ddcf32b6f910c4c2487ee705e4d493 (patch)
tree07c715e05527545e40cbc57d4377f95247c20ebe /drivers/power
parent4d8a93c7e542c364b1c2ad1dd406ede85080eab9 (diff)
parentf8c63918c9a3004bd052fa4af08aa1cefbcd79ac (diff)
downloadlinux-f0d15c96d4ddcf32b6f910c4c2487ee705e4d493.tar.bz2
Merge git://git.infradead.org/battery-2.6
* git://git.infradead.org/battery-2.6: gpio-charger: Fix checking return value of request_any_context_irq power_supply: MAX17042: Support additional properties max8903_charger: Allow platform data to be __initdata power_supply: Add charger driver for MAX8998/LP3974 power_supply: Add charger driver for MAX8997/8966 max17042_battery: Remove obsolete cleanup for clientdata twl4030_charger: Fix warnings wm831x_power: Support multiple instances wm831x_backup: Support multiple instances apm_power: Fix style error in macros s3c_adc_battery: Fix annotation for s3c_adc_battery_probe() bq20z75: Enable detection after registering bq20z75: Add support for external notification
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/Kconfig14
-rw-r--r--drivers/power/Makefile2
-rw-r--r--drivers/power/apm_power.c8
-rw-r--r--drivers/power/bq20z75.c103
-rw-r--r--drivers/power/gpio-charger.c2
-rw-r--r--drivers/power/max17042_battery.c175
-rw-r--r--drivers/power/max8903_charger.c16
-rw-r--r--drivers/power/max8997_charger.c206
-rw-r--r--drivers/power/max8998_charger.c218
-rw-r--r--drivers/power/s3c_adc_battery.c2
-rw-r--r--drivers/power/twl4030_charger.c10
-rw-r--r--drivers/power/wm831x_backup.c12
-rw-r--r--drivers/power/wm831x_power.c26
13 files changed, 692 insertions, 102 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index e57b50b38565..57de051a74b3 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -235,4 +235,18 @@ config CHARGER_GPIO
This driver can be build as a module. If so, the module will be
called gpio-charger.
+config CHARGER_MAX8997
+ tristate "Maxim MAX8997/MAX8966 PMIC battery charger driver"
+ depends on MFD_MAX8997 && REGULATOR_MAX8997
+ help
+ Say Y to enable support for the battery charger control sysfs and
+ platform data of MAX8997/LP3974 PMICs.
+
+config CHARGER_MAX8998
+ tristate "Maxim MAX8998/LP3974 PMIC battery charger driver"
+ depends on MFD_MAX8998 && REGULATOR_MAX8998
+ help
+ Say Y to enable support for the battery charger control sysfs and
+ platform data of MAX8998/LP3974 PMICs.
+
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 009a90fa8ac9..b4af13dd8b66 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -36,3 +36,5 @@ obj-$(CONFIG_CHARGER_ISP1704) += isp1704_charger.o
obj-$(CONFIG_CHARGER_MAX8903) += max8903_charger.o
obj-$(CONFIG_CHARGER_TWL4030) += twl4030_charger.o
obj-$(CONFIG_CHARGER_GPIO) += gpio-charger.o
+obj-$(CONFIG_CHARGER_MAX8997) += max8997_charger.o
+obj-$(CONFIG_CHARGER_MAX8998) += max8998_charger.o
diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c
index dc628cb2e762..8a612dec9139 100644
--- a/drivers/power/apm_power.c
+++ b/drivers/power/apm_power.c
@@ -14,11 +14,11 @@
#include <linux/apm-emulation.h>
-#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
- POWER_SUPPLY_PROP_##prop, val)
+#define PSY_PROP(psy, prop, val) (psy->get_property(psy, \
+ POWER_SUPPLY_PROP_##prop, val))
-#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
- prop, val)
+#define _MPSY_PROP(prop, val) (main_battery->get_property(main_battery, \
+ prop, val))
#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
diff --git a/drivers/power/bq20z75.c b/drivers/power/bq20z75.c
index 506585e31a5b..9c5e5beda3a8 100644
--- a/drivers/power/bq20z75.c
+++ b/drivers/power/bq20z75.c
@@ -152,6 +152,10 @@ struct bq20z75_info {
bool gpio_detect;
bool enable_detection;
int irq;
+ int last_state;
+ int poll_time;
+ struct delayed_work work;
+ int ignore_changes;
};
static int bq20z75_read_word_data(struct i2c_client *client, u8 address)
@@ -279,6 +283,7 @@ static int bq20z75_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);
s32 ret;
ret = bq20z75_read_word_data(client,
@@ -293,15 +298,24 @@ static int bq20z75_get_battery_property(struct i2c_client *client,
if (ret >= bq20z75_data[reg_offset].min_value &&
ret <= bq20z75_data[reg_offset].max_value) {
val->intval = ret;
- if (psp == POWER_SUPPLY_PROP_STATUS) {
- if (ret & BATTERY_FULL_CHARGED)
- val->intval = POWER_SUPPLY_STATUS_FULL;
- else if (ret & BATTERY_FULL_DISCHARGED)
- val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
- else if (ret & BATTERY_DISCHARGING)
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
- else
- val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ if (psp != POWER_SUPPLY_PROP_STATUS)
+ return 0;
+
+ if (ret & BATTERY_FULL_CHARGED)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+ else if (ret & BATTERY_FULL_DISCHARGED)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (ret & BATTERY_DISCHARGING)
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ 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;
}
} else {
if (psp == POWER_SUPPLY_PROP_STATUS)
@@ -545,6 +559,60 @@ static irqreturn_t bq20z75_irq(int irq, void *devid)
return IRQ_HANDLED;
}
+static void bq20z75_external_power_changed(struct power_supply *psy)
+{
+ struct bq20z75_info *bq20z75_device;
+
+ bq20z75_device = container_of(psy, struct bq20z75_info, power_supply);
+
+ if (bq20z75_device->ignore_changes > 0) {
+ bq20z75_device->ignore_changes--;
+ return;
+ }
+
+ /* cancel outstanding work */
+ cancel_delayed_work_sync(&bq20z75_device->work);
+
+ schedule_delayed_work(&bq20z75_device->work, HZ);
+ bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count;
+}
+
+static void bq20z75_delayed_work(struct work_struct *work)
+{
+ struct bq20z75_info *bq20z75_device;
+ s32 ret;
+
+ bq20z75_device = container_of(work, struct bq20z75_info, work.work);
+
+ ret = bq20z75_read_word_data(bq20z75_device->client,
+ bq20z75_data[REG_STATUS].addr);
+ /* if the read failed, give up on this work */
+ if (ret < 0) {
+ bq20z75_device->poll_time = 0;
+ return;
+ }
+
+ if (ret & BATTERY_FULL_CHARGED)
+ ret = POWER_SUPPLY_STATUS_FULL;
+ else if (ret & BATTERY_FULL_DISCHARGED)
+ ret = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else if (ret & BATTERY_DISCHARGING)
+ ret = POWER_SUPPLY_STATUS_DISCHARGING;
+ else
+ ret = POWER_SUPPLY_STATUS_CHARGING;
+
+ if (bq20z75_device->last_state != ret) {
+ bq20z75_device->poll_time = 0;
+ power_supply_changed(&bq20z75_device->power_supply);
+ return;
+ }
+ if (bq20z75_device->poll_time > 0) {
+ schedule_delayed_work(&bq20z75_device->work, HZ);
+ bq20z75_device->poll_time--;
+ return;
+ }
+}
+
static int __devinit bq20z75_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -566,6 +634,13 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
bq20z75_device->power_supply.num_properties =
ARRAY_SIZE(bq20z75_properties);
bq20z75_device->power_supply.get_property = bq20z75_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;
if (pdata) {
bq20z75_device->gpio_detect =
@@ -625,6 +700,10 @@ skip_gpio:
dev_info(&client->dev,
"%s: battery gas gauge device registered\n", client->name);
+ INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work);
+
+ bq20z75_device->enable_detection = true;
+
return 0;
exit_psupply:
@@ -648,6 +727,9 @@ static int __devexit bq20z75_remove(struct i2c_client *client)
gpio_free(bq20z75_device->pdata->battery_detect);
power_supply_unregister(&bq20z75_device->power_supply);
+
+ cancel_delayed_work_sync(&bq20z75_device->work);
+
kfree(bq20z75_device);
bq20z75_device = NULL;
@@ -661,6 +743,9 @@ static int bq20z75_suspend(struct i2c_client *client,
struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
s32 ret;
+ if (bq20z75_device->poll_time > 0)
+ cancel_delayed_work_sync(&bq20z75_device->work);
+
/* write to manufacturer access with sleep command */
ret = bq20z75_write_word_data(client,
bq20z75_data[REG_MANUFACTURER_DATA].addr,
diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c
index 718f2c537827..a64b8854cfd5 100644
--- a/drivers/power/gpio-charger.c
+++ b/drivers/power/gpio-charger.c
@@ -127,7 +127,7 @@ static int __devinit gpio_charger_probe(struct platform_device *pdev)
ret = request_any_context_irq(irq, gpio_charger_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&pdev->dev), charger);
- if (ret)
+ if (ret < 0)
dev_warn(&pdev->dev, "Failed to request irq: %d\n", ret);
else
gpio_charger->irq = irq;
diff --git a/drivers/power/max17042_battery.c b/drivers/power/max17042_battery.c
index c5c8805156cb..98bfab35b8e9 100644
--- a/drivers/power/max17042_battery.c
+++ b/drivers/power/max17042_battery.c
@@ -29,74 +29,6 @@
#include <linux/power_supply.h>
#include <linux/power/max17042_battery.h>
-enum max17042_register {
- MAX17042_STATUS = 0x00,
- MAX17042_VALRT_Th = 0x01,
- MAX17042_TALRT_Th = 0x02,
- MAX17042_SALRT_Th = 0x03,
- MAX17042_AtRate = 0x04,
- MAX17042_RepCap = 0x05,
- MAX17042_RepSOC = 0x06,
- MAX17042_Age = 0x07,
- MAX17042_TEMP = 0x08,
- MAX17042_VCELL = 0x09,
- MAX17042_Current = 0x0A,
- MAX17042_AvgCurrent = 0x0B,
- MAX17042_Qresidual = 0x0C,
- MAX17042_SOC = 0x0D,
- MAX17042_AvSOC = 0x0E,
- MAX17042_RemCap = 0x0F,
- MAX17402_FullCAP = 0x10,
- MAX17042_TTE = 0x11,
- MAX17042_V_empty = 0x12,
-
- MAX17042_RSLOW = 0x14,
-
- MAX17042_AvgTA = 0x16,
- MAX17042_Cycles = 0x17,
- MAX17042_DesignCap = 0x18,
- MAX17042_AvgVCELL = 0x19,
- MAX17042_MinMaxTemp = 0x1A,
- MAX17042_MinMaxVolt = 0x1B,
- MAX17042_MinMaxCurr = 0x1C,
- MAX17042_CONFIG = 0x1D,
- MAX17042_ICHGTerm = 0x1E,
- MAX17042_AvCap = 0x1F,
- MAX17042_ManName = 0x20,
- MAX17042_DevName = 0x21,
- MAX17042_DevChem = 0x22,
-
- MAX17042_TempNom = 0x24,
- MAX17042_TempCold = 0x25,
- MAX17042_TempHot = 0x26,
- MAX17042_AIN = 0x27,
- MAX17042_LearnCFG = 0x28,
- MAX17042_SHFTCFG = 0x29,
- MAX17042_RelaxCFG = 0x2A,
- MAX17042_MiscCFG = 0x2B,
- MAX17042_TGAIN = 0x2C,
- MAx17042_TOFF = 0x2D,
- MAX17042_CGAIN = 0x2E,
- MAX17042_COFF = 0x2F,
-
- MAX17042_Q_empty = 0x33,
- MAX17042_T_empty = 0x34,
-
- MAX17042_RCOMP0 = 0x38,
- MAX17042_TempCo = 0x39,
- MAX17042_Rx = 0x3A,
- MAX17042_T_empty0 = 0x3B,
- MAX17042_TaskPeriod = 0x3C,
- MAX17042_FSTAT = 0x3D,
-
- MAX17042_SHDNTIMER = 0x3F,
-
- MAX17042_VFRemCap = 0x4A,
-
- MAX17042_QH = 0x4D,
- MAX17042_QL = 0x4E,
-};
-
struct max17042_chip {
struct i2c_client *client;
struct power_supply battery;
@@ -123,10 +55,27 @@ static int max17042_read_reg(struct i2c_client *client, u8 reg)
return ret;
}
+static void max17042_set_reg(struct i2c_client *client,
+ struct max17042_reg_data *data, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ max17042_write_reg(client, data[i].addr, data[i].data);
+}
+
static enum power_supply_property max17042_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_CYCLE_COUNT,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
};
static int max17042_get_property(struct power_supply *psy,
@@ -137,6 +86,30 @@ static int max17042_get_property(struct power_supply *psy,
struct max17042_chip, battery);
switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = max17042_read_reg(chip->client,
+ MAX17042_STATUS);
+ if (val->intval & 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);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+ val->intval = max17042_read_reg(chip->client,
+ MAX17042_MinMaxVolt);
+ val->intval >>= 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;
+ 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 */
@@ -149,6 +122,57 @@ static int max17042_get_property(struct power_supply *psy,
val->intval = max17042_read_reg(chip->client,
MAX17042_SOC) / 256;
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ val->intval = max17042_read_reg(chip->client,
+ MAX17042_RepSOC);
+ if ((val->intval / 256) >= MAX17042_BATTERY_FULL)
+ val->intval = 1;
+ else if (val->intval >= 0)
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = max17042_read_reg(chip->client,
+ MAX17042_TEMP);
+ /* The value is signed. */
+ if (val->intval & 0x8000) {
+ val->intval = (0x7fff & ~val->intval) + 1;
+ val->intval *= -1;
+ }
+ /* The value is converted into deci-centigrade scale */
+ /* Units of LSB = 1 / 256 degree Celsius */
+ val->intval = val->intval * 10 / 256;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ if (chip->pdata->enable_current_sense) {
+ val->intval = max17042_read_reg(chip->client,
+ MAX17042_Current);
+ 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;
+ } 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);
+ if (val->intval & 0x8000) {
+ /* Negative */
+ val->intval = ~val->intval & 0x7fff;
+ val->intval++;
+ val->intval *= -1;
+ }
+ val->intval *= 1562500 / chip->pdata->r_sns;
+ } else {
+ return -EINVAL;
+ }
+ break;
default:
return -EINVAL;
}
@@ -180,18 +204,30 @@ static int __devinit max17042_probe(struct i2c_client *client,
chip->battery.properties = max17042_battery_props;
chip->battery.num_properties = ARRAY_SIZE(max17042_battery_props);
+ /* When current is not measured,
+ * CURRENT_NOW and CURRENT_AVG properties should be invisible. */
+ if (!chip->pdata->enable_current_sense)
+ chip->battery.num_properties -= 2;
+
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
dev_err(&client->dev, "failed: power supply register\n");
- i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
+ /* Initialize registers according to values from the platform data */
+ if (chip->pdata->init_data)
+ max17042_set_reg(client, chip->pdata->init_data,
+ chip->pdata->num_init_data);
+
if (!chip->pdata->enable_current_sense) {
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;
@@ -202,7 +238,6 @@ static int __devexit max17042_remove(struct i2c_client *client)
struct max17042_chip *chip = i2c_get_clientdata(client);
power_supply_unregister(&chip->battery);
- i2c_set_clientdata(client, NULL);
kfree(chip);
return 0;
}
diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c
index 33ff0e37809e..a9b0209a2f55 100644
--- a/drivers/power/max8903_charger.c
+++ b/drivers/power/max8903_charger.c
@@ -28,7 +28,7 @@
#include <linux/power/max8903_charger.h>
struct max8903_data {
- struct max8903_pdata *pdata;
+ struct max8903_pdata pdata;
struct device *dev;
struct power_supply psy;
bool fault;
@@ -52,8 +52,8 @@ static int max8903_get_property(struct power_supply *psy,
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
val->intval = POWER_SUPPLY_STATUS_UNKNOWN;
- if (data->pdata->chg) {
- if (gpio_get_value(data->pdata->chg) == 0)
+ if (data->pdata.chg) {
+ if (gpio_get_value(data->pdata.chg) == 0)
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (data->usb_in || data->ta_in)
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
@@ -80,7 +80,7 @@ static int max8903_get_property(struct power_supply *psy,
static irqreturn_t max8903_dcin(int irq, void *_data)
{
struct max8903_data *data = _data;
- struct max8903_pdata *pdata = data->pdata;
+ struct max8903_pdata *pdata = &data->pdata;
bool ta_in;
enum power_supply_type old_type;
@@ -121,7 +121,7 @@ static irqreturn_t max8903_dcin(int irq, void *_data)
static irqreturn_t max8903_usbin(int irq, void *_data)
{
struct max8903_data *data = _data;
- struct max8903_pdata *pdata = data->pdata;
+ struct max8903_pdata *pdata = &data->pdata;
bool usb_in;
enum power_supply_type old_type;
@@ -160,7 +160,7 @@ static irqreturn_t max8903_usbin(int irq, void *_data)
static irqreturn_t max8903_fault(int irq, void *_data)
{
struct max8903_data *data = _data;
- struct max8903_pdata *pdata = data->pdata;
+ struct max8903_pdata *pdata = &data->pdata;
bool fault;
fault = gpio_get_value(pdata->flt) ? false : true;
@@ -193,7 +193,7 @@ static __devinit int max8903_probe(struct platform_device *pdev)
dev_err(dev, "Cannot allocate memory.\n");
return -ENOMEM;
}
- data->pdata = pdata;
+ memcpy(&data->pdata, pdata, sizeof(struct max8903_pdata));
data->dev = dev;
platform_set_drvdata(pdev, data);
@@ -349,7 +349,7 @@ static __devexit int max8903_remove(struct platform_device *pdev)
struct max8903_data *data = platform_get_drvdata(pdev);
if (data) {
- struct max8903_pdata *pdata = data->pdata;
+ struct max8903_pdata *pdata = &data->pdata;
if (pdata->flt)
free_irq(gpio_to_irq(pdata->flt), data);
diff --git a/drivers/power/max8997_charger.c b/drivers/power/max8997_charger.c
new file mode 100644
index 000000000000..7106b49b26e4
--- /dev/null
+++ b/drivers/power/max8997_charger.c
@@ -0,0 +1,206 @@
+/*
+ * max8997_charger.c - Power supply consumer driver for the Maxim 8997/8966
+ *
+ * Copyright (C) 2011 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max8997.h>
+#include <linux/mfd/max8997-private.h>
+
+struct charger_data {
+ struct device *dev;
+ struct max8997_dev *iodev;
+ struct power_supply battery;
+};
+
+static enum power_supply_property max8997_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS, /* "FULL" or "NOT FULL" only. */
+ POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
+ POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
+};
+
+/* Note that the charger control is done by a current regulator "CHARGER" */
+static int max8997_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct charger_data *charger = container_of(psy,
+ struct charger_data, battery);
+ struct i2c_client *i2c = charger->iodev->i2c;
+ int ret;
+ u8 reg;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = 0;
+ ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+ if (ret)
+ return ret;
+ if ((reg & (1 << 0)) == 0x1)
+ val->intval = POWER_SUPPLY_STATUS_FULL;
+
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = 0;
+ ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+ if (ret)
+ return ret;
+ if ((reg & (1 << 2)) == 0x0)
+ val->intval = 1;
+
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = 0;
+ ret = max8997_read_reg(i2c, MAX8997_REG_STATUS4, &reg);
+ if (ret)
+ return ret;
+ /* DCINOK */
+ if (reg & (1 << 1))
+ val->intval = 1;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static __devinit int max8997_battery_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct charger_data *charger;
+ struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev);
+
+ if (!pdata)
+ return -EINVAL;
+
+ if (pdata->eoc_mA) {
+ u8 val = (pdata->eoc_mA - 50) / 10;
+ if (val < 0)
+ val = 0;
+ if (val > 0xf)
+ val = 0xf;
+
+ ret = max8997_update_reg(iodev->i2c,
+ MAX8997_REG_MBCCTRL5, val, 0xf);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot use i2c bus.\n");
+ return ret;
+ }
+ }
+
+ switch (pdata->timeout) {
+ case 5:
+ ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+ 0x2 << 4, 0x7 << 4);
+ break;
+ case 6:
+ ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+ 0x3 << 4, 0x7 << 4);
+ break;
+ case 7:
+ ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+ 0x4 << 4, 0x7 << 4);
+ break;
+ case 0:
+ ret = max8997_update_reg(iodev->i2c, MAX8997_REG_MBCCTRL1,
+ 0x7 << 4, 0x7 << 4);
+ break;
+ default:
+ dev_err(&pdev->dev, "incorrect timeout value (%d)\n",
+ pdata->timeout);
+ return -EINVAL;
+ }
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot use i2c bus.\n");
+ return ret;
+ }
+
+ charger = kzalloc(sizeof(struct charger_data), GFP_KERNEL);
+ if (charger == NULL) {
+ dev_err(&pdev->dev, "Cannot allocate memory.\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, charger);
+
+ charger->battery.name = "max8997_pmic";
+ charger->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ charger->battery.get_property = max8997_battery_get_property;
+ charger->battery.properties = max8997_battery_props;
+ charger->battery.num_properties = ARRAY_SIZE(max8997_battery_props);
+
+ charger->dev = &pdev->dev;
+ charger->iodev = iodev;
+
+ ret = power_supply_register(&pdev->dev, &charger->battery);
+ if (ret) {
+ dev_err(&pdev->dev, "failed: power supply register\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ kfree(charger);
+ return ret;
+}
+
+static int __devexit max8997_battery_remove(struct platform_device *pdev)
+{
+ struct charger_data *charger = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&charger->battery);
+ kfree(charger);
+ return 0;
+}
+
+static const struct platform_device_id max8997_battery_id[] = {
+ { "max8997-battery", 0 },
+};
+
+static struct platform_driver max8997_battery_driver = {
+ .driver = {
+ .name = "max8997-battery",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8997_battery_probe,
+ .remove = __devexit_p(max8997_battery_remove),
+ .id_table = max8997_battery_id,
+};
+
+static int __init max8997_battery_init(void)
+{
+ return platform_driver_register(&max8997_battery_driver);
+}
+subsys_initcall(max8997_battery_init);
+
+static void __exit max8997_battery_cleanup(void)
+{
+ platform_driver_unregister(&max8997_battery_driver);
+}
+module_exit(max8997_battery_cleanup);
+
+MODULE_DESCRIPTION("MAXIM 8997/8966 battery control driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/max8998_charger.c b/drivers/power/max8998_charger.c
new file mode 100644
index 000000000000..cc21fa2120be
--- /dev/null
+++ b/drivers/power/max8998_charger.c
@@ -0,0 +1,218 @@
+/*
+ * max8998_charger.c - Power supply consumer driver for the Maxim 8998/LP3974
+ *
+ * Copyright (C) 2009-2010 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/mfd/max8998.h>
+#include <linux/mfd/max8998-private.h>
+
+struct max8998_battery_data {
+ struct device *dev;
+ struct max8998_dev *iodev;
+ struct power_supply battery;
+};
+
+static enum power_supply_property max8998_battery_props[] = {
+ POWER_SUPPLY_PROP_PRESENT, /* the presence of battery */
+ POWER_SUPPLY_PROP_ONLINE, /* charger is active or not */
+};
+
+/* Note that the charger control is done by a current regulator "CHARGER" */
+static int max8998_battery_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct max8998_battery_data *max8998 = container_of(psy,
+ struct max8998_battery_data, battery);
+ struct i2c_client *i2c = max8998->iodev->i2c;
+ int ret;
+ u8 reg;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_PRESENT:
+ ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
+ if (ret)
+ return ret;
+ if (reg & (1 << 4))
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ ret = max8998_read_reg(i2c, MAX8998_REG_STATUS2, &reg);
+ if (ret)
+ return ret;
+ if (reg & (1 << 3))
+ val->intval = 0;
+ else
+ val->intval = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static __devinit int max8998_battery_probe(struct platform_device *pdev)
+{
+ struct max8998_dev *iodev = dev_get_drvdata(pdev->dev.parent);
+ struct max8998_platform_data *pdata = dev_get_platdata(iodev->dev);
+ struct max8998_battery_data *max8998;
+ struct i2c_client *i2c;
+ int ret = 0;
+
+ if (!pdata) {
+ dev_err(pdev->dev.parent, "No platform init data supplied\n");
+ return -ENODEV;
+ }
+
+ max8998 = kzalloc(sizeof(struct max8998_battery_data), GFP_KERNEL);
+ if (!max8998)
+ return -ENOMEM;
+
+ max8998->dev = &pdev->dev;
+ max8998->iodev = iodev;
+ platform_set_drvdata(pdev, max8998);
+ i2c = max8998->iodev->i2c;
+
+ /* Setup "End of Charge" */
+ /* If EOC value equals 0,
+ * remain value set from bootloader or default value */
+ if (pdata->eoc >= 10 && pdata->eoc <= 45) {
+ max8998_update_reg(i2c, MAX8998_REG_CHGR1,
+ (pdata->eoc / 5 - 2) << 5, 0x7 << 5);
+ } else if (pdata->eoc == 0) {
+ dev_dbg(max8998->dev,
+ "EOC value not set: leave it unchanged.\n");
+ } else {
+ dev_err(max8998->dev, "Invalid EOC value\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Setup Charge Restart Level */
+ switch (pdata->restart) {
+ case 100:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x1 << 3, 0x3 << 3);
+ break;
+ case 150:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x0 << 3, 0x3 << 3);
+ break;
+ case 200:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x2 << 3, 0x3 << 3);
+ break;
+ case -1:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR1, 0x3 << 3, 0x3 << 3);
+ break;
+ case 0:
+ dev_dbg(max8998->dev,
+ "Restart Level not set: leave it unchanged.\n");
+ break;
+ default:
+ dev_err(max8998->dev, "Invalid Restart Level\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ /* Setup Charge Full Timeout */
+ switch (pdata->timeout) {
+ case 5:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x0 << 4, 0x3 << 4);
+ break;
+ case 6:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x1 << 4, 0x3 << 4);
+ break;
+ case 7:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x2 << 4, 0x3 << 4);
+ break;
+ case -1:
+ max8998_update_reg(i2c, MAX8998_REG_CHGR2, 0x3 << 4, 0x3 << 4);
+ break;
+ case 0:
+ dev_dbg(max8998->dev,
+ "Full Timeout not set: leave it unchanged.\n");
+ default:
+ dev_err(max8998->dev, "Invalid Full Timeout value\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ max8998->battery.name = "max8998_pmic";
+ max8998->battery.type = POWER_SUPPLY_TYPE_BATTERY;
+ max8998->battery.get_property = max8998_battery_get_property;
+ max8998->battery.properties = max8998_battery_props;
+ max8998->battery.num_properties = ARRAY_SIZE(max8998_battery_props);
+
+ ret = power_supply_register(max8998->dev, &max8998->battery);
+ if (ret) {
+ dev_err(max8998->dev, "failed: power supply register\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ kfree(max8998);
+ return ret;
+}
+
+static int __devexit max8998_battery_remove(struct platform_device *pdev)
+{
+ struct max8998_battery_data *max8998 = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&max8998->battery);
+ kfree(max8998);
+
+ return 0;
+}
+
+static const struct platform_device_id max8998_battery_id[] = {
+ { "max8998-battery", TYPE_MAX8998 },
+};
+
+static struct platform_driver max8998_battery_driver = {
+ .driver = {
+ .name = "max8998-battery",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8998_battery_probe,
+ .remove = __devexit_p(max8998_battery_remove),
+ .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_DESCRIPTION("MAXIM 8998 battery control driver");
+MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:max8998-battery");
diff --git a/drivers/power/s3c_adc_battery.c b/drivers/power/s3c_adc_battery.c
index d36c289aaef5..a675e31b4f13 100644
--- a/drivers/power/s3c_adc_battery.c
+++ b/drivers/power/s3c_adc_battery.c
@@ -266,7 +266,7 @@ static irqreturn_t s3c_adc_bat_charged(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int __init s3c_adc_bat_probe(struct platform_device *pdev)
+static int __devinit s3c_adc_bat_probe(struct platform_device *pdev)
{
struct s3c_adc_client *client;
struct s3c_adc_bat_pdata *pdata = pdev->dev.platform_data;
diff --git a/drivers/power/twl4030_charger.c b/drivers/power/twl4030_charger.c
index 92c16e1677bd..54b9198fa576 100644
--- a/drivers/power/twl4030_charger.c
+++ b/drivers/power/twl4030_charger.c
@@ -62,7 +62,7 @@
#define TWL4030_MSTATEC_COMPLETE4 0x0e
static bool allow_usb;
-module_param(allow_usb, bool, 1);
+module_param(allow_usb, bool, 0644);
MODULE_PARM_DESC(allow_usb, "Allow USB charge drawing default current");
struct twl4030_bci {
@@ -425,7 +425,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
{
struct twl4030_bci *bci;
int ret;
- int reg;
+ u32 reg;
bci = kzalloc(sizeof(*bci), GFP_KERNEL);
if (bci == NULL)
@@ -486,7 +486,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
}
/* Enable interrupts now. */
- reg = ~(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
+ reg = ~(u32)(TWL4030_ICHGLOW | TWL4030_ICHGEOC | TWL4030_TBATOR2 |
TWL4030_TBATOR1 | TWL4030_BATSTS);
ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
TWL4030_INTERRUPTS_BCIIMR1A);
@@ -495,7 +495,7 @@ static int __init twl4030_bci_probe(struct platform_device *pdev)
goto fail_unmask_interrupts;
}
- reg = ~(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
+ reg = ~(u32)(TWL4030_VBATOV | TWL4030_VBUSOV | TWL4030_ACCHGOV);
ret = twl_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, reg,
TWL4030_INTERRUPTS_BCIIMR2A);
if (ret < 0)
@@ -572,7 +572,7 @@ static void __exit twl4030_bci_exit(void)
}
module_exit(twl4030_bci_exit);
-MODULE_AUTHOR("Gražydas Ignotas");
+MODULE_AUTHOR("Gražvydas Ignotas");
MODULE_DESCRIPTION("TWL4030 Battery Charger Interface driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:twl4030_bci");
diff --git a/drivers/power/wm831x_backup.c b/drivers/power/wm831x_backup.c
index 0fd130d80f5d..e648cbea1e6a 100644
--- a/drivers/power/wm831x_backup.c
+++ b/drivers/power/wm831x_backup.c
@@ -22,6 +22,7 @@
struct wm831x_backup {
struct wm831x *wm831x;
struct power_supply backup;
+ char name[20];
};
static int wm831x_backup_read_voltage(struct wm831x *wm831x,
@@ -163,6 +164,7 @@ static enum power_supply_property wm831x_backup_props[] = {
static __devinit int wm831x_backup_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
struct wm831x_backup *devdata;
struct power_supply *backup;
int ret;
@@ -182,7 +184,14 @@ static __devinit int wm831x_backup_probe(struct platform_device *pdev)
*/
wm831x_config_backup(wm831x);
- backup->name = "wm831x-backup";
+ if (wm831x_pdata && wm831x_pdata->wm831x_num)
+ snprintf(devdata->name, sizeof(devdata->name),
+ "wm831x-backup.%d", wm831x_pdata->wm831x_num);
+ else
+ snprintf(devdata->name, sizeof(devdata->name),
+ "wm831x-backup");
+
+ backup->name = devdata->name;
backup->type = POWER_SUPPLY_TYPE_BATTERY;
backup->properties = wm831x_backup_props;
backup->num_properties = ARRAY_SIZE(wm831x_backup_props);
@@ -203,6 +212,7 @@ static __devexit int wm831x_backup_remove(struct platform_device *pdev)
struct wm831x_backup *devdata = platform_get_drvdata(pdev);
power_supply_unregister(&devdata->backup);
+ kfree(devdata->backup.name);
kfree(devdata);
return 0;
diff --git a/drivers/power/wm831x_power.c b/drivers/power/wm831x_power.c
index ddf8cf5f3204..6cc2ca6427f3 100644
--- a/drivers/power/wm831x_power.c
+++ b/drivers/power/wm831x_power.c
@@ -24,6 +24,9 @@ struct wm831x_power {
struct power_supply wall;
struct power_supply usb;
struct power_supply battery;
+ char wall_name[20];
+ char usb_name[20];
+ char battery_name[20];
};
static int wm831x_power_check_online(struct wm831x *wm831x, int supply,
@@ -486,6 +489,7 @@ static irqreturn_t wm831x_pwr_src_irq(int irq, void *data)
static __devinit int wm831x_power_probe(struct platform_device *pdev)
{
struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent);
+ struct wm831x_pdata *wm831x_pdata = wm831x->dev->platform_data;
struct wm831x_power *power;
struct power_supply *usb;
struct power_supply *battery;
@@ -503,12 +507,28 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
battery = &power->battery;
wall = &power->wall;
+ if (wm831x_pdata && wm831x_pdata->wm831x_num) {
+ snprintf(power->wall_name, sizeof(power->wall_name),
+ "wm831x-wall.%d", wm831x_pdata->wm831x_num);
+ snprintf(power->battery_name, sizeof(power->wall_name),
+ "wm831x-battery.%d", wm831x_pdata->wm831x_num);
+ snprintf(power->usb_name, sizeof(power->wall_name),
+ "wm831x-usb.%d", wm831x_pdata->wm831x_num);
+ } else {
+ snprintf(power->wall_name, sizeof(power->wall_name),
+ "wm831x-wall");
+ snprintf(power->battery_name, sizeof(power->wall_name),
+ "wm831x-battery");
+ snprintf(power->usb_name, sizeof(power->wall_name),
+ "wm831x-usb");
+ }
+
/* We ignore configuration failures since we can still read back
* the status without enabling the charger.
*/
wm831x_config_battery(wm831x);
- wall->name = "wm831x-wall";
+ wall->name = power->wall_name;
wall->type = POWER_SUPPLY_TYPE_MAINS;
wall->properties = wm831x_wall_props;
wall->num_properties = ARRAY_SIZE(wm831x_wall_props);
@@ -517,7 +537,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
if (ret)
goto err_kmalloc;
- battery->name = "wm831x-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;
@@ -526,7 +546,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
if (ret)
goto err_wall;
- usb->name = "wm831x-usb",
+ usb->name = power->usb_name,
usb->type = POWER_SUPPLY_TYPE_USB;
usb->properties = wm831x_usb_props;
usb->num_properties = ARRAY_SIZE(wm831x_usb_props);