Merge git://git.infradead.org/battery-2.6

* git://git.infradead.org/battery-2.6: (68 commits)
  power_supply: Mark da9052 driver as broken
  power_supply: Drop usage of nowarn variant of sysfs_create_link()
  s3c_adc_battery: Average over more than one adc sample
  power_supply: Add DA9052 battery driver
  isp1704_charger: Fix missing check
  jz4740-battery: Fix signedness bug
  power_supply: Assume mains power by default
  sbs-battery: Fix devicetree match table
  ARM: rx51: Add bq27200 i2c board info
  sbs-battery: Change power supply name
  devicetree-bindings: Propagate bq20z75->sbs rename to dt bindings
  devicetree-bindings: Add vendor entry for Smart Battery Systems
  sbs-battery: Rename internals to new name
  bq20z75: Rename to sbs-battery
  wm97xx_battery: Use DEFINE_MUTEX() for work_lock
  max8997_charger: Remove duplicate module.h
  lp8727_charger: Some minor fixes for the header
  lp8727_charger: Add header file
  power_supply: Convert drivers/power/* to use module_platform_driver()
  power_supply: Add "unknown" in power supply type
  ...
This commit is contained in:
Linus Torvalds 2012-01-11 18:53:05 -08:00
commit b24ca57e76
47 changed files with 3475 additions and 737 deletions

View 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

View 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

View file

@ -54,13 +54,19 @@
#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;
}
@ -220,6 +227,50 @@ static int bq27x00_battery_read_ilmd(struct bq27x00_device_info *di)
return ilmd;
}
/*
* Return the battery Available energy in µWh
* Or < 0 if something fails.
*/
static int bq27x00_battery_read_energy(struct bq27x00_device_info *di)
{
int ae;
ae = bq27x00_read(di, BQ27x00_REG_AE, false);
if (ae < 0) {
dev_dbg(di->dev, "error reading available energy\n");
return ae;
}
if (di->chip == BQ27500)
ae *= 1000;
else
ae = ae * 29200 / BQ27000_RS;
return ae;
}
/*
* Return the battery temperature in tenths of degree 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,6 +425,36 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
return 0;
}
static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
int level;
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 = level;
return 0;
}
/*
* Return the battery Voltage in milivolts
* Or < 0 if something fails.
@ -392,40 +465,16 @@ static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
int volt;
volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
if (volt < 0)
if (volt < 0) {
dev_err(di->dev, "error reading voltage\n");
return volt;
}
val->intval = volt * 1000;
return 0;
}
/*
* Return the battery Available energy in µWh
* Or < 0 if something fails.
*/
static int bq27x00_battery_energy(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
int ae;
ae = bq27x00_read(di, BQ27x00_REG_AE, false);
if (ae < 0) {
dev_err(di->dev, "error reading available energy\n");
return ae;
}
if (di->chip == BQ27500)
ae *= 1000;
else
ae = ae * 29200 / BQ27000_RS;
val->intval = ae;
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);

File diff suppressed because it is too large Load diff

View file

@ -146,7 +146,7 @@ static void collie_bat_external_power_changed(struct power_supply *psy)
static irqreturn_t collie_bat_gpio_isr(int irq, void *data)
{
pr_info("collie_bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq)));
pr_info("collie_bat_gpio irq\n");
schedule_work(&bat_work);
return IRQ_HANDLED;
}
@ -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 = {

View 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");

View file

@ -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 <[email protected]>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:da9052-bat");

View 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 <[email protected]>, "

View 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 <[email protected]>");
MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
MODULE_ALIAS("platform:ds2780-battery");

View 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 <[email protected]>");
MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");

View file

@ -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 <[email protected]>");
MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");

View 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");

View 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");

View file

@ -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 <[email protected]>, Daniel Jeong <[email protected]>");
MODULE_LICENSE("GPL");

View 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;

View 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 <[email protected]>");
MODULE_ALIAS("max8903-charger");
MODULE_ALIAS("platform:max8903-charger");

View 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");

View 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 = {

View 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 <[email protected]>");

View 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 <[email protected]>");
MODULE_LICENSE("GPL");

View 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 <[email protected]>");
MODULE_DESCRIPTION("PCF50633 mbc driver");

View 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 <[email protected]>");
MODULE_ALIAS("platform:pda-power");

View 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);
}

View 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),

View 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 <[email protected]>");
MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");

View 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;
}
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 = kzalloc(sizeof(struct sbs_info), GFP_KERNEL);
if (!chip) {
rc = -ENOMEM;
goto exit_free_name;
}
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);
MODULE_DEVICE_TABLE(i2c, sbs_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,
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");

View 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");

View 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 <[email protected]>");

View 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 <[email protected]>");

View 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");

View 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 <[email protected]>");

View 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,
};