Commit b24ca57e authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 6296e5d3 913272b3
OLPC battery
~~~~~~~~~~~~
Required properties:
- compatible : "olpc,xo1-battery"
SBS sbs-battery
~~~~~~~~~~
Required properties :
- compatible : "sbs,sbs-battery"
Optional properties :
- sbs,i2c-retry-count : The number of times to retry i2c transactions on i2c
IO failure.
- sbs,poll-retry-count : The number of times to try looking for new status
after an external change notification.
- sbs,battery-detect-gpios : The gpio which signals battery detection and
a flag specifying its polarity.
Example:
bq20z75@b {
compatible = "sbs,sbs-battery";
reg = < 0xb >;
sbs,i2c-retry-count = <2>;
sbs,poll-retry-count = <10>;
sbs,battery-detect-gpios = <&gpio-controller 122 1>;
}
......@@ -34,6 +34,7 @@ powervr Imagination Technologies
qcom Qualcomm, Inc.
ramtron Ramtron International
samsung Samsung Semiconductor
sbs Smart Battery System
schindler Schindler
sil Silicon Image
simtek
......
Charger Manager
(C) 2011 MyungJoo Ham <myungjoo.ham@samsung.com>, GPL
Charger Manager provides in-kernel battery charger management that
requires temperature monitoring during suspend-to-RAM state
and where each battery may have multiple chargers attached and the userland
wants to look at the aggregated information of the multiple chargers.
Charger Manager is a platform_driver with power-supply-class entries.
An instance of Charger Manager (a platform-device created with Charger-Manager)
represents an independent battery with chargers. If there are multiple
batteries with their own chargers acting independently in a system,
the system may need multiple instances of Charger Manager.
1. Introduction
===============
Charger Manager supports the following:
* Support for multiple chargers (e.g., a device with USB, AC, and solar panels)
A system may have multiple chargers (or power sources) and some of
they may be activated at the same time. Each charger may have its
own power-supply-class and each power-supply-class can provide
different information about the battery status. This framework
aggregates charger-related information from multiple sources and
shows combined information as a single power-supply-class.
* Support for in suspend-to-RAM polling (with suspend_again callback)
While the battery is being charged and the system is in suspend-to-RAM,
we may need to monitor the battery health by looking at the ambient or
battery temperature. We can accomplish this by waking up the system
periodically. However, such a method wakes up devices unncessary for
monitoring the battery health and tasks, and user processes that are
supposed to be kept suspended. That, in turn, incurs unnecessary power
consumption and slow down charging process. Or even, such peak power
consumption can stop chargers in the middle of charging
(external power input < device power consumption), which not
only affects the charging time, but the lifespan of the battery.
Charger Manager provides a function "cm_suspend_again" that can be
used as suspend_again callback of platform_suspend_ops. If the platform
requires tasks other than cm_suspend_again, it may implement its own
suspend_again callback that calls cm_suspend_again in the middle.
Normally, the platform will need to resume and suspend some devices
that are used by Charger Manager.
2. Global Charger-Manager Data related with suspend_again
========================================================
In order to setup Charger Manager with suspend-again feature
(in-suspend monitoring), the user should provide charger_global_desc
with setup_charger_manager(struct charger_global_desc *).
This charger_global_desc data for in-suspend monitoring is global
as the name suggests. Thus, the user needs to provide only once even
if there are multiple batteries. If there are multiple batteries, the
multiple instances of Charger Manager share the same charger_global_desc
and it will manage in-suspend monitoring for all instances of Charger Manager.
The user needs to provide all the two entries properly in order to activate
in-suspend monitoring:
struct charger_global_desc {
char *rtc_name;
: The name of rtc (e.g., "rtc0") used to wakeup the system from
suspend for Charger Manager. The alarm interrupt (AIE) of the rtc
should be able to wake up the system from suspend. Charger Manager
saves and restores the alarm value and use the previously-defined
alarm if it is going to go off earlier than Charger Manager so that
Charger Manager does not interfere with previously-defined alarms.
bool (*rtc_only_wakeup)(void);
: This callback should let CM know whether
the wakeup-from-suspend is caused only by the alarm of "rtc" in the
same struct. If there is any other wakeup source triggered the
wakeup, it should return false. If the "rtc" is the only wakeup
reason, it should return true.
};
3. How to setup suspend_again
=============================
Charger Manager provides a function "extern bool cm_suspend_again(void)".
When cm_suspend_again is called, it monitors every battery. The suspend_ops
callback of the system's platform_suspend_ops can call cm_suspend_again
function to know whether Charger Manager wants to suspend again or not.
If there are no other devices or tasks that want to use suspend_again
feature, the platform_suspend_ops may directly refer to cm_suspend_again
for its suspend_again callback.
The cm_suspend_again() returns true (meaning "I want to suspend again")
if the system was woken up by Charger Manager and the polling
(in-suspend monitoring) results in "normal".
4. Charger-Manager Data (struct charger_desc)
=============================================
For each battery charged independently from other batteries (if a series of
batteries are charged by a single charger, they are counted as one independent
battery), an instance of Charger Manager is attached to it.
struct charger_desc {
char *psy_name;
: The power-supply-class name of the battery. Default is
"battery" if psy_name is NULL. Users can access the psy entries
at "/sys/class/power_supply/[psy_name]/".
enum polling_modes polling_mode;
: CM_POLL_DISABLE: do not poll this battery.
CM_POLL_ALWAYS: always poll this battery.
CM_POLL_EXTERNAL_POWER_ONLY: poll this battery if and only if
an external power source is attached.
CM_POLL_CHARGING_ONLY: poll this battery if and only if the
battery is being charged.
unsigned int fullbatt_uV;
: If specified with a non-zero value, Charger Manager assumes
that the battery is full (capacity = 100) if the battery is not being
charged and the battery voltage is equal to or greater than
fullbatt_uV.
unsigned int polling_interval_ms;
: Required polling interval in ms. Charger Manager will poll
this battery every polling_interval_ms or more frequently.
enum data_source battery_present;
CM_FUEL_GAUGE: get battery presence information from fuel gauge.
CM_CHARGER_STAT: get battery presence from chargers.
char **psy_charger_stat;
: An array ending with NULL that has power-supply-class names of
chargers. Each power-supply-class should provide "PRESENT" (if
battery_present is "CM_CHARGER_STAT"), "ONLINE" (shows whether an
external power source is attached or not), and "STATUS" (shows whether
the battery is {"FULL" or not FULL} or {"FULL", "Charging",
"Discharging", "NotCharging"}).
int num_charger_regulators;
struct regulator_bulk_data *charger_regulators;
: Regulators representing the chargers in the form for
regulator framework's bulk functions.
char *psy_fuel_gauge;
: Power-supply-class name of the fuel gauge.
int (*temperature_out_of_range)(int *mC);
bool measure_battery_temp;
: This callback returns 0 if the temperature is safe for charging,
a positive number if it is too hot to charge, and a negative number
if it is too cold to charge. With the variable mC, the callback returns
the temperature in 1/1000 of centigrade.
The source of temperature can be battery or ambient one according to
the value of measure_battery_temp.
};
5. Other Considerations
=======================
At the charger/battery-related events such as battery-pulled-out,
charger-pulled-out, charger-inserted, DCIN-over/under-voltage, charger-stopped,
and others critical to chargers, the system should be configured to wake up.
At least the following should wake up the system from a suspend:
a) charger-on/off b) external-power-in/out c) battery-in/out (while charging)
It is usually accomplished by configuring the PMIC as a wakeup source.
......@@ -944,6 +944,9 @@ static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_2[] = {
.platform_data = &rx51_lp5523_platform_data,
},
#endif
{
I2C_BOARD_INFO("bq27200", 0x55),
},
{
I2C_BOARD_INFO("tpa6130a2", 0x60),
.platform_data = &rx51_tpa6130a2_data,
......
......@@ -49,12 +49,14 @@ static unsigned short batcap[8] = { 1, 15, 25, 35, 50, 70, 100, 0 };
static enum power_supply_property wacom_battery_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CAPACITY
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SCOPE,
};
static enum power_supply_property wacom_ac_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_SCOPE,
};
static int wacom_battery_get_property(struct power_supply *psy,
......@@ -70,6 +72,9 @@ static int wacom_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_PRESENT:
val->intval = 1;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
case POWER_SUPPLY_PROP_CAPACITY:
/* show 100% battery capacity when charging */
if (power_state == 0)
......@@ -101,6 +106,9 @@ static int wacom_ac_get_property(struct power_supply *psy,
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_SCOPE:
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
break;
default:
ret = -EINVAL;
break;
......@@ -523,6 +531,8 @@ static int wacom_probe(struct hid_device *hdev,
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
wdata->battery.use_for_apm = 0;
power_supply_powers(&wdata->battery, &hdev->dev);
ret = power_supply_register(&hdev->dev, &wdata->battery);
if (ret) {
hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
......@@ -537,6 +547,8 @@ static int wacom_probe(struct hid_device *hdev,
wdata->ac.type = POWER_SUPPLY_TYPE_MAINS;
wdata->ac.use_for_apm = 0;
power_supply_powers(&wdata->battery, &hdev->dev);
ret = power_supply_register(&hdev->dev, &wdata->ac);
if (ret) {
hid_warn(hdev,
......
......@@ -52,7 +52,8 @@ static __u16 wiiproto_keymap[] = {
};
static enum power_supply_property wiimote_battery_props[] = {
POWER_SUPPLY_PROP_CAPACITY
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_SCOPE,
};
static ssize_t wiimote_hid_send(struct hid_device *hdev, __u8 *buffer,
......@@ -402,6 +403,11 @@ static int wiimote_battery_get_property(struct power_supply *psy,
int ret = 0, state;
unsigned long flags;
if (psp == POWER_SUPPLY_PROP_SCOPE) {
val->intval = POWER_SUPPLY_SCOPE_DEVICE;
return 0;
}
ret = wiimote_cmd_acquire(wdata);
if (ret)
return ret;
......@@ -1220,6 +1226,8 @@ static int wiimote_hid_probe(struct hid_device *hdev,
wdata->battery.type = POWER_SUPPLY_TYPE_BATTERY;
wdata->battery.use_for_apm = 0;
power_supply_powers(&wdata->battery, &hdev->dev);
ret = power_supply_register(&wdata->hdev->dev, &wdata->battery);
if (ret) {
hid_err(hdev, "Cannot register battery device\n");
......
......@@ -210,21 +210,6 @@ static struct max8925_irq_data max8925_irqs[] = {
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 2,
},
[MAX8925_IRQ_VCHG_USB_OVP] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 3,
},
[MAX8925_IRQ_VCHG_USB_F] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 4,
},
[MAX8925_IRQ_VCHG_USB_R] = {
.reg = MAX8925_CHG_IRQ1,
.mask_reg = MAX8925_CHG_IRQ1_MASK,
.offs = 1 << 5,
},
[MAX8925_IRQ_VCHG_THM_OK_R] = {
.reg = MAX8925_CHG_IRQ2,
.mask_reg = MAX8925_CHG_IRQ2_MASK,
......
......@@ -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
......
......@@ -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
......@@ -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,50 +425,56 @@ static int bq27x00_battery_status(struct bq27x00_device_info *di,
return 0;
}
/*
* Return the battery Voltage in milivolts
* Or < 0 if something fails.
*/
static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
static int bq27x00_battery_capacity_level(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
int volt;
int level;
volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
if (volt < 0)
return volt;
if (di->chip == BQ27500) {
if (di->cache.flags & BQ27500_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27500_FLAG_SOC1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (di->cache.flags & BQ27500_FLAG_SOCF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
} else {
if (di->cache.flags & BQ27000_FLAG_FC)
level = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
else if (di->cache.flags & BQ27000_FLAG_EDV1)
level = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
else if (di->cache.flags & BQ27000_FLAG_EDVF)
level = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL;
else
level = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
}
val->intval = volt * 1000;
val->intval = level;
return 0;
}
/*
* Return the battery Available energy in µWh
* Return the battery Voltage in milivolts
* Or < 0 if something fails.
*/
static int bq27x00_battery_energy(struct bq27x00_device_info *di,
static int bq27x00_battery_voltage(struct bq27x00_device_info *di,
union power_supply_propval *val)
{
int ae;
int volt;
ae = bq27x00_read(di, BQ27x00_REG_AE, false);
if (ae < 0) {
dev_err(di->dev, "error reading available energy\n");
return ae;
volt = bq27x00_read(di, BQ27x00_REG_VOLT, false);
if (volt < 0) {
dev_err(di->dev, "error reading voltage\n");
return volt;
}
if (di->chip == BQ27500)
ae *= 1000;
else
ae = ae * 29200 / BQ27000_RS;
val->intval = ae;
val->intval = volt * 1000;
return 0;
}
static int bq27x00_simple_value(int value,
union power_supply_propval *val)
{
......@@ -473,8 +522,11 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
ret = bq27x00_simple_value(di->cache.capacity, val);
break;
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
ret = bq27x00_battery_capacity_level(di, val);
break;
case POWER_SUPPLY_PROP_TEMP:
ret = bq27x00_battery_temperature(di, val);
ret = bq27x00_simple_value(di->cache.temperature, val);
break;
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
ret = bq27x00_simple_value(di->cache.time_to_empty, val);
......@@ -501,7 +553,7 @@ static int bq27x00_battery_get_property(struct power_supply *psy,
ret = bq27x00_simple_value(di->cache.cycle_count, val);
break;
case POWER_SUPPLY_PROP_ENERGY_NOW:
ret = bq27x00_battery_energy(di, val);
ret = bq27x00_simple_value(di->cache.energy, val);
break;
default:
return -EINVAL;
......@@ -546,6 +598,14 @@ static int bq27x00_powersupply_init(struct bq27x00_device_info *di)
static void bq27x00_powersupply_unregister(struct bq27x00_device_info *di)
{
/*
* power_supply_unregister call bq27x00_battery_get_property which
* call bq27x00_battery_poll.
* Make sure that bq27x00_battery_poll will not call
* schedule_delayed_work again after unregister (which cause OOPS).
*/
poll_interval = 0;
cancel_delayed_work_sync(&di->work);
power_supply_unregister(&di->bat);
......
/*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* This driver enables to monitor battery health and control charger
* during suspend-to-mem.
* Charger manager depends on other devices. register this later than
* the depending devices.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
**/
#include <linux/io.h>
#include <linux/module.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/platform_device.h>
#include <linux/power/charger-manager.h>
#include <linux/regulator/consumer.h>
/*
* Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for
* delayed works so that we can run delayed works with CM_JIFFIES_SMALL
* without any delays.
*/
#define CM_JIFFIES_SMALL (2)
/* If y is valid (> 0) and smaller than x, do x = y */
#define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x))
/*
* Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking
* rtc alarm. It should be 2 or larger
*/
#define CM_RTC_SMALL (2)
#define UEVENT_BUF_SIZE 32
static LIST_HEAD(cm_list);
static DEFINE_MUTEX(cm_list_mtx);
/* About in-suspend (suspend-again) monitoring */
static struct rtc_device *rtc_dev;
/*
* Backup RTC alarm
* Save the wakeup alarm before entering suspend-to-RAM
*/
static struct rtc_wkalrm rtc_wkalarm_save;
/* Backup RTC alarm time in terms of seconds since 01-01-1970 00:00:00 */
static unsigned long rtc_wkalarm_save_time;
static bool cm_suspended;
static bool cm_rtc_set;
static unsigned long cm_suspend_duration_ms;
/* Global charger-manager description */
static struct charger_global_desc *g_desc; /* init with setup_charger_manager */
/**
* is_batt_present - See if the battery presents in place.
* @cm: the Charger Manager representing the battery.
*/
static bool is_batt_present(struct charger_manager *cm)
{
union power_supply_propval val;
bool present = false;
int i, ret;
switch (cm->desc->battery_present) {
case CM_FUEL_GAUGE:
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_PRESENT, &val);
if (ret == 0 && val.intval)
present = true;
break;
case CM_CHARGER_STAT:
for (i = 0; cm->charger_stat[i]; i++) {
ret = cm->charger_stat[i]->get_property(
cm->charger_stat[i],
POWER_SUPPLY_PROP_PRESENT, &val);
if (ret == 0 && val.intval) {
present = true;
break;
}
}
break;
}
return present;
}
/**
* is_ext_pwr_online - See if an external power source is attached to charge
* @cm: the Charger Manager representing the battery.
*
* Returns true if at least one of the chargers of the battery has an external
* power source attached to charge the battery regardless of whether it is
* actually charging or not.
*/
static bool is_ext_pwr_online(struct charger_manager *cm)
{
union power_supply_propval val;
bool online = false;
int i, ret;
/* If at least one of them has one, it's yes. */
for (i = 0; cm->charger_stat[i]; i++) {
ret = cm->charger_stat[i]->get_property(
cm->charger_stat[i],
POWER_SUPPLY_PROP_ONLINE, &val);
if (ret == 0 && val.intval) {
online = true;
break;
}
}
return online;
}
/**
* get_batt_uV - Get the voltage level of the battery
* @cm: the Charger Manager representing the battery.
* @uV: the voltage level returned.
*
* Returns 0 if there is no error.
* Returns a negative value on error.
*/
static int get_batt_uV(struct charger_manager *cm, int *uV)
{
union power_supply_propval val;
int ret;
if (cm->fuel_gauge)
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
else
return -ENODEV;
if (ret)
return ret;
*uV = val.intval;
return 0;
}
/**
* is_charging - Returns true if the battery is being charged.
* @cm: the Charger Manager representing the battery.
*/
static bool is_charging(struct charger_manager *cm)
{
int i, ret;
bool charging = false;
union power_supply_propval val;
/* If there is no battery, it cannot be charged */
if (!is_batt_present(cm))
return false;
/* If at least one of the charger is charging, return yes */
for (i = 0; cm->charger_stat[i]; i++) {
/* 1. The charger sholuld not be DISABLED */
if (cm->emergency_stop)
continue;
if (!cm->charger_enabled)
continue;
/* 2. The charger should be online (ext-power) */
ret = cm->charger_stat[i]->get_property(
cm->charger_stat[i],
POWER_SUPPLY_PROP_ONLINE, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read ONLINE value from %s.\n",
cm->desc->psy_charger_stat[i]);
continue;
}
if (val.intval == 0)
continue;
/*
* 3. The charger should not be FULL, DISCHARGING,
* or NOT_CHARGING.
*/
ret = cm->charger_stat[i]->get_property(
cm->charger_stat[i],
POWER_SUPPLY_PROP_STATUS, &val);
if (ret) {
dev_warn(cm->dev, "Cannot read STATUS value from %s.\n",
cm->desc->psy_charger_stat[i]);
continue;
}
if (val.intval == POWER_SUPPLY_STATUS_FULL ||
val.intval == POWER_SUPPLY_STATUS_DISCHARGING ||
val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING)
continue;
/* Then, this is charging. */
charging = true;
break;
}
return charging;
}
/**
* is_polling_required - Return true if need to continue polling for this CM.
* @cm: the Charger Manager representing the battery.
*/
static bool is_polling_required(struct charger_manager *cm)
{
switch (cm->desc->polling_mode) {
case CM_POLL_DISABLE:
return false;
case CM_POLL_ALWAYS:
return true;
case CM_POLL_EXTERNAL_POWER_ONLY:
return is_ext_pwr_online(cm);
case CM_POLL_CHARGING_ONLY:
return is_charging(cm);
default:
dev_warn(cm->dev, "Incorrect polling_mode (%d)\n",
cm->desc->polling_mode);
}
return false;
}
/**
* try_charger_enable - Enable/Disable chargers altogether
* @cm: the Charger Manager representing the battery.
* @enable: true: enable / false: disable
*
* Note that Charger Manager keeps the charger enabled regardless whether
* the charger is charging or not (because battery is full or no external
* power source exists) except when CM needs to disable chargers forcibly
* bacause of emergency causes; when the battery is overheated or too cold.
*/
static int try_charger_enable(struct charger_manager *cm, bool enable)
{
int err = 0, i;
struct charger_desc *desc = cm->desc;
/* Ignore if it's redundent command */
if (enable && cm->charger_enabled)
return 0;
if (!enable && !cm->charger_enabled)
return 0;
if (enable) {
if (cm->emergency_stop)
return -EAGAIN;
err = regulator_bulk_enable(desc->num_charger_regulators,
desc->charger_regulators);
} else {
/*
* Abnormal battery state - Stop charging forcibly,
* even if charger was enabled at the other places
*/
err = regulator_bulk_disable(desc->num_charger_regulators,
desc->charger_regulators);
for (i = 0; i < desc->num_charger_regulators; i++) {
if (regulator_is_enabled(
desc->charger_regulators[i].consumer)) {
regulator_force_disable(
desc->charger_regulators[i].consumer);
dev_warn(cm->dev,
"Disable regulator(%s) forcibly.\n",
desc->charger_regulators[i].supply);
}
}
}
if (!err)
cm->charger_enabled = enable;
return err;
}
/**
* uevent_notify - Let users know something has changed.
* @cm: the Charger Manager representing the battery.
* @event: the event string.
*
* If @event is null, it implies that uevent_notify is called
* by resume function. When called in the resume function, cm_suspended
* should be already reset to false in order to let uevent_notify
* notify the recent event during the suspend to users. While
* suspended, uevent_notify does not notify users, but tracks
* events so that uevent_notify can notify users later after resumed.
*/
static void uevent_notify(struct charger_manager *cm, const char *event)
{
static char env_str[UEVENT_BUF_SIZE + 1] = "";
static char env_str_save[UEVENT_BUF_SIZE + 1] = "";
if (cm_suspended) {
/* Nothing in suspended-event buffer */
if (env_str_save[0] == 0) {
if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
return; /* status not changed */
strncpy(env_str_save, event, UEVENT_BUF_SIZE);
return;
}
if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE))
return; /* Duplicated. */
else
strncpy(env_str_save, event, UEVENT_BUF_SIZE);
return;
}
if (event == NULL) {
/* No messages pending */
if (!env_str_save[0])
return;
strncpy(env_str, env_str_save, UEVENT_BUF_SIZE);
kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
env_str_save[0] = 0;
return;
}
/* status not changed */
if (!strncmp(env_str, event, UEVENT_BUF_SIZE))
return;
/* save the status and notify the update */
strncpy(env_str, event, UEVENT_BUF_SIZE);
kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE);
dev_info(cm->dev, event);
}
/**
* _cm_monitor - Monitor the temperature and return true for exceptions.
* @cm: the Charger Manager representing the battery.
*
* Returns true if there is an event to notify for the battery.
* (True if the status of "emergency_stop" changes)
*/
static bool _cm_monitor(struct charger_manager *cm)
{
struct charger_desc *desc = cm->desc;
int temp = desc->temperature_out_of_range(&cm->last_temp_mC);
dev_dbg(cm->dev, "monitoring (%2.2d.%3.3dC)\n",
cm->last_temp_mC / 1000, cm->last_temp_mC % 1000);
/* It has been stopped or charging already */
if (!!temp == !!cm->emergency_stop)
return false;
if (temp) {
cm->emergency_stop = temp;
if (!try_charger_enable(cm, false)) {
if (temp > 0)
uevent_notify(cm, "OVERHEAT");
else
uevent_notify(cm, "COLD");
}
} else {
cm->emergency_stop = 0;
if (!try_charger_enable(cm, true))
uevent_notify(cm, "CHARGING");
}
return true;
}
/**
* cm_monitor - Monitor every battery.
*
* Returns true if there is an event to notify from any of the batteries.
* (True if the status of "emergency_stop" changes)
*/
static bool cm_monitor(void)
{
bool stop = false;
struct charger_manager *cm;
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry)
stop = stop || _cm_monitor(cm);
mutex_unlock(&cm_list_mtx);
return stop;
}
static int charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct charger_manager *cm = container_of(psy,
struct charger_manager, charger_psy);
struct charger_desc *desc = cm->desc;
int i, ret = 0, uV;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (is_charging(cm))
val->intval = POWER_SUPPLY_STATUS_CHARGING;
else if (is_ext_pwr_online(cm))
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
else
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
break;
case POWER_SUPPLY_PROP_HEALTH:
if (cm->emergency_stop > 0)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
else if (cm->emergency_stop < 0)
val->intval = POWER_SUPPLY_HEALTH_COLD;
else
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_PRESENT:
if (is_batt_present(cm))
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = get_batt_uV(cm, &i);
val->intval = i;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW, val);
break;
case POWER_SUPPLY_PROP_TEMP:
/* in thenth of centigrade */
if (cm->last_temp_mC == INT_MIN)
desc->temperature_out_of_range(&cm->last_temp_mC);
val->intval = cm->last_temp_mC / 100;
if (!desc->measure_battery_temp)
ret = -ENODEV;
break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
/* in thenth of centigrade */
if (cm->last_temp_mC == INT_MIN)
desc->temperature_out_of_range(&cm->last_temp_mC);
val->intval = cm->last_temp_mC / 100;
if (desc->measure_battery_temp)
ret = -ENODEV;
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (!cm->fuel_gauge) {
ret = -ENODEV;
break;
}
if (!is_batt_present(cm)) {
/* There is no battery. Assume 100% */
val->intval = 100;
break;
}
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, val);
if (ret)
break;
if (val->intval > 100) {
val->intval = 100;
break;
}
if (val->intval < 0)
val->intval = 0;
/* Do not adjust SOC when charging: voltage is overrated */
if (is_charging(cm))
break;
/*
* If the capacity value is inconsistent, calibrate it base on
* the battery voltage values and the thresholds given as desc
*/
ret = get_batt_uV(cm, &uV);
if (ret) {
/* Voltage information not available. No calibration */
ret = 0;
break;
}
if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
!is_charging(cm)) {
val->intval = 100;
break;
}
break;
case POWER_SUPPLY_PROP_ONLINE:
if (is_ext_pwr_online(cm))
val->intval = 1;
else
val->intval = 0;
break;
case POWER_SUPPLY_PROP_CHARGE_FULL:
if (cm->fuel_gauge) {
if (cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_FULL, val) == 0)
break;
}
if (is_ext_pwr_online(cm)) {
/* Not full if it's charging. */
if (is_charging(cm)) {
val->intval = 0;
break;
}
/*
* Full if it's powered but not charging andi
* not forced stop by emergency
*/
if (!cm->emergency_stop) {
val->intval = 1;
break;
}
}
/* Full if it's over the fullbatt voltage */
ret = get_batt_uV(cm, &uV);
if (!ret && desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV &&
!is_charging(cm)) {
val->intval = 1;
break;
}
/* Full if the cap is 100 */
if (cm->fuel_gauge) {
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CAPACITY, val);
if (!ret && val->intval >= 100 && !is_charging(cm)) {
val->intval = 1;
break;
}
}
val->intval = 0;
ret = 0;
break;
case POWER_SUPPLY_PROP_CHARGE_NOW:
if (is_charging(cm)) {
ret = cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW,
val);
if (ret) {
val->intval = 1;
ret = 0;
} else {
/* If CHARGE_NOW is supplied, use it */
val->intval = (val->intval > 0) ?
val->intval : 1;
}
} else {
val->intval = 0;
}
break;
default:
return -EINVAL;
}
return ret;
}
#define NUM_CHARGER_PSY_OPTIONAL (4)
static enum power_supply_property default_charger_props[] = {
/* Guaranteed to provide */
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CHARGE_FULL,
/*
* Optional properties are:
* POWER_SUPPLY_PROP_CHARGE_NOW,
* POWER_SUPPLY_PROP_CURRENT_NOW,
* POWER_SUPPLY_PROP_TEMP, and
* POWER_SUPPLY_PROP_TEMP_AMBIENT,
*/
};
static struct power_supply psy_default = {
.name = "battery",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = default_charger_props,
.num_properties = ARRAY_SIZE(default_charger_props),
.get_property = charger_get_property,
};
/**
* cm_setup_timer - For in-suspend monitoring setup wakeup alarm
* for suspend_again.
*
* Returns true if the alarm is set for Charger Manager to use.
* Returns false if
* cm_setup_timer fails to set an alarm,
* cm_setup_timer does not need to set an alarm for Charger Manager,
* or an alarm previously configured is to be used.
*/
static bool cm_setup_timer(void)
{
struct charger_manager *cm;
unsigned int wakeup_ms = UINT_MAX;
bool ret = false;
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
/* Skip if polling is not required for this CM */
if (!is_polling_required(cm) && !cm->emergency_stop)
continue;
if (cm->desc->polling_interval_ms == 0)
continue;
CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms);
}
mutex_unlock(&cm_list_mtx);
if (wakeup_ms < UINT_MAX && wakeup_ms > 0) {
pr_info("Charger Manager wakeup timer: %u ms.\n", wakeup_ms);
if (rtc_dev) {
struct rtc_wkalrm tmp;
unsigned long time, now;
unsigned long add = DIV_ROUND_UP(wakeup_ms, 1000);
/*
* Set alarm with the polling interval (wakeup_ms)
* except when rtc_wkalarm_save comes first.
* However, the alarm time should be NOW +
* CM_RTC_SMALL or later.
*/
tmp.enabled = 1;
rtc_read_time(rtc_dev, &tmp.time);
rtc_tm_to_time(&tmp.time, &now);
if (add < CM_RTC_SMALL)
add = CM_RTC_SMALL;
time = now + add;
ret = true;
if (rtc_wkalarm_save.enabled &&
rtc_wkalarm_save_time &&
rtc_wkalarm_save_time < time) {
if (rtc_wkalarm_save_time < now + CM_RTC_SMALL)
time = now + CM_RTC_SMALL;
else
time = rtc_wkalarm_save_time;
/* The timer is not appointed by CM */
ret = false;
}
pr_info("Waking up after %lu secs.\n",
time - now);
rtc_time_to_tm(time, &tmp.time);
rtc_set_alarm(rtc_dev, &tmp);
cm_suspend_duration_ms += wakeup_ms;
return ret;
}
}
if (rtc_dev)
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
return false;
}
/**
* cm_suspend_again - Determine whether suspend again or not
*
* Returns true if the system should be suspended again
* Returns false if the system should be woken up
*/
bool cm_suspend_again(void)
{
struct charger_manager *cm;
bool ret = false;
if (!g_desc || !g_desc->rtc_only_wakeup || !g_desc->rtc_only_wakeup() ||
!cm_rtc_set)
return false;
if (cm_monitor())
goto out;
ret = true;
mutex_lock(&cm_list_mtx);
list_for_each_entry(cm, &cm_list, entry) {
if (cm->status_save_ext_pwr_inserted != is_ext_pwr_online(cm) ||
cm->status_save_batt != is_batt_present(cm))
ret = false;
}
mutex_unlock(&cm_list_mtx);
cm_rtc_set = cm_setup_timer();
out:
/* It's about the time when the non-CM appointed timer goes off */
if (rtc_wkalarm_save.enabled) {
unsigned long now;
struct rtc_time tmp;
rtc_read_time(rtc_dev, &tmp);
rtc_tm_to_time(&tmp, &now);
if (rtc_wkalarm_save_time &&
now + CM_RTC_SMALL >= rtc_wkalarm_save_time)
return false;
}
return ret;
}
EXPORT_SYMBOL_GPL(cm_suspend_again);
/**
* setup_charger_manager - initialize charger_global_desc data
* @gd: pointer to instance of charger_global_desc
*/
int setup_charger_manager(struct charger_global_desc *gd)
{
if (!gd)
return -EINVAL;
if (rtc_dev)
rtc_class_close(rtc_dev);
rtc_dev = NULL;
g_desc = NULL;
if (!gd->rtc_only_wakeup) {
pr_err("The callback rtc_only_wakeup is not given.\n");
return -EINVAL;
}
if (gd->rtc_name) {
rtc_dev = rtc_class_open(gd->rtc_name);
if (IS_ERR_OR_NULL(rtc_dev)) {
rtc_dev = NULL;
/* Retry at probe. RTC may be not registered yet */
}
} else {
pr_warn("No wakeup timer is given for charger manager."
"In-suspend monitoring won't work.\n");
}
g_desc = gd;
return 0;
}
EXPORT_SYMBOL_GPL(setup_charger_manager);
static int charger_manager_probe(struct platform_device *pdev)
{
struct charger_desc *desc = dev_get_platdata(&pdev->dev);
struct charger_manager *cm;
int ret = 0, i = 0;
union power_supply_propval val;
if (g_desc && !rtc_dev && g_desc->rtc_name) {
rtc_dev = rtc_class_open(g_desc->rtc_name);
if (IS_ERR_OR_NULL(rtc_dev)) {
rtc_dev = NULL;
dev_err(&pdev->dev, "Cannot get RTC %s.\n",
g_desc->rtc_name);
ret = -ENODEV;
goto err_alloc;
}
}
if (!desc) {
dev_err(&pdev->dev, "No platform data (desc) found.\n");
ret = -ENODEV;
goto err_alloc;
}
cm = kzalloc(sizeof(struct charger_manager), GFP_KERNEL);
if (!cm) {
dev_err(&pdev->dev, "Cannot allocate memory.\n");
ret = -ENOMEM;
goto err_alloc;
}
/* Basic Values. Unspecified are Null or 0 */
cm->dev = &pdev->dev;
cm->desc = kzalloc(sizeof(struct charger_desc), GFP_KERNEL);
if (!cm->desc) {
dev_err(&pdev->dev, "Cannot allocate memory.\n");
ret = -ENOMEM;
goto err_alloc_desc;
}
memcpy(cm->desc, desc, sizeof(struct charger_desc));
cm->last_temp_mC = INT_MIN; /* denotes "unmeasured, yet" */
if (!desc->charger_regulators || desc->num_charger_regulators < 1) {
ret = -EINVAL;
dev_err(&pdev->dev, "charger_regulators undefined.\n");
goto err_no_charger;
}
if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) {
dev_err(&pdev->dev, "No power supply defined.\n");
ret = -EINVAL;
goto err_no_charger_stat;
}
/* Counting index only */
while (desc->psy_charger_stat[i])
i++;
cm->charger_stat = kzalloc(sizeof(struct power_supply *) * (i + 1),
GFP_KERNEL);
if (!cm->charger_stat) {
ret = -ENOMEM;
goto err_no_charger_stat;
}
for (i = 0; desc->psy_charger_stat[i]; i++) {
cm->charger_stat[i] = power_supply_get_by_name(
desc->psy_charger_stat[i]);
if (!cm->charger_stat[i]) {
dev_err(&pdev->dev, "Cannot find power supply "
"\"%s\"\n",
desc->psy_charger_stat[i]);
ret = -ENODEV;
goto err_chg_stat;
}
}
cm->fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge);
if (!cm->fuel_gauge) {
dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n",
desc->psy_fuel_gauge);
ret = -ENODEV;
goto err_chg_stat;
}
if (desc->polling_interval_ms == 0 ||
msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) {
dev_err(&pdev->dev, "polling_interval_ms is too small\n");
ret = -EINVAL;
goto err_chg_stat;
}
if (!desc->temperature_out_of_range) {
dev_err(&pdev->dev, "there is no temperature_out_of_range\n");
ret = -EINVAL;
goto err_chg_stat;
}
platform_set_drvdata(pdev, cm);
memcpy(&cm->charger_psy, &psy_default,
sizeof(psy_default));
if (!desc->psy_name) {
strncpy(cm->psy_name_buf, psy_default.name,
PSY_NAME_MAX);
} else {
strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX);
}
cm->charger_psy.name = cm->psy_name_buf;
/* Allocate for psy properties because they may vary */
cm->charger_psy.properties = kzalloc(sizeof(enum power_supply_property)
* (ARRAY_SIZE(default_charger_props) +
NUM_CHARGER_PSY_OPTIONAL),
GFP_KERNEL);
if (!cm->charger_psy.properties) {
dev_err(&pdev->dev, "Cannot allocate for psy properties.\n");
ret = -ENOMEM;
goto err_chg_stat;
}
memcpy(cm->charger_psy.properties, default_charger_props,
sizeof(enum power_supply_property) *
ARRAY_SIZE(default_charger_props));
cm->charger_psy.num_properties = psy_default.num_properties;
/* Find which optional psy-properties are available */
if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CHARGE_NOW, &val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_CHARGE_NOW;
cm->charger_psy.num_properties++;
}
if (!cm->fuel_gauge->get_property(cm->fuel_gauge,
POWER_SUPPLY_PROP_CURRENT_NOW,
&val)) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_CURRENT_NOW;
cm->charger_psy.num_properties++;
}
if (!desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP_AMBIENT;
cm->charger_psy.num_properties++;
}
if (desc->measure_battery_temp) {
cm->charger_psy.properties[cm->charger_psy.num_properties] =
POWER_SUPPLY_PROP_TEMP;
cm->charger_psy.num_properties++;
}
ret = power_supply_register(NULL, &cm->charger_psy);
if (ret) {
dev_err(&pdev->dev, "Cannot register charger-manager with"
" name \"%s\".\n", cm->charger_psy.name);
goto err_register;
}
ret = regulator_bulk_get(&pdev->dev, desc->num_charger_regulators,
desc->charger_regulators);
if (ret) {
dev_err(&pdev->dev, "Cannot get charger regulators.\n");
goto err_bulk_get;
}
ret = try_charger_enable(cm, true);
if (ret) {
dev_err(&pdev->dev, "Cannot enable charger regulators\n");
goto err_chg_enable;
}
/* Add to the list */
mutex_lock(&cm_list_mtx);
list_add(&cm->entry, &cm_list);
mutex_unlock(&cm_list_mtx);
return 0;
err_chg_enable:
if (desc->charger_regulators)
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
err_bulk_get:
power_supply_unregister(&cm->charger_psy);
err_register:
kfree(cm->charger_psy.properties);
err_chg_stat:
kfree(cm->charger_stat);
err_no_charger_stat:
err_no_charger:
kfree(cm->desc);
err_alloc_desc:
kfree(cm);
err_alloc:
return ret;
}
static int __devexit charger_manager_remove(struct platform_device *pdev)
{
struct charger_manager *cm = platform_get_drvdata(pdev);
struct charger_desc *desc = cm->desc;
/* Remove from the list */
mutex_lock(&cm_list_mtx);
list_del(&cm->entry);
mutex_unlock(&cm_list_mtx);
if (desc->charger_regulators)
regulator_bulk_free(desc->num_charger_regulators,
desc->charger_regulators);
power_supply_unregister(&cm->charger_psy);
kfree(cm->charger_psy.properties);
kfree(cm->charger_stat);
kfree(cm->desc);
kfree(cm);
return 0;
}
const struct platform_device_id charger_manager_id[] = {
{ "charger-manager", 0 },
{ },
};
static int cm_suspend_prepare(struct device *dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct charger_manager *cm = platform_get_drvdata(pdev);
if (!cm_suspended) {
if (rtc_dev) {
struct rtc_time tmp;
unsigned long now;
rtc_read_alarm(rtc_dev, &rtc_wkalarm_save);
rtc_read_time(rtc_dev, &tmp);
if (rtc_wkalarm_save.enabled) {
rtc_tm_to_time(&rtc_wkalarm_save.time,
&rtc_wkalarm_save_time);
rtc_tm_to_time(&tmp, &now);
if (now > rtc_wkalarm_save_time)
rtc_wkalarm_save_time = 0;
} else {
rtc_wkalarm_save_time = 0;
}
}
cm_suspended = true;
}
cm->status_save_ext_pwr_inserted = is_ext_pwr_online(cm);
cm->status_save_batt = is_batt_present(cm);
if (!cm_rtc_set) {
cm_suspend_duration_ms = 0;
cm_rtc_set = cm_setup_timer();
}
return 0;
}
static void cm_suspend_complete(struct device *dev)
{
struct platform_device *pdev = container_of(dev, struct platform_device,
dev);
struct charger_manager *cm = platform_get_drvdata(pdev);
if (cm_suspended) {
if (rtc_dev) {
struct rtc_wkalrm tmp;
rtc_read_alarm(rtc_dev, &tmp);
rtc_wkalarm_save.pending = tmp.pending;
rtc_set_alarm(rtc_dev, &rtc_wkalarm_save);
}
cm_suspended = false;
cm_rtc_set = false;
}
uevent_notify(cm, NULL);
}
static const struct dev_pm_ops charger_manager_pm = {
.prepare = cm_suspend_prepare,
.complete = cm_suspend_complete,
};
static struct platform_driver charger_manager_driver = {
.driver = {
.name = "charger-manager",
.owner = THIS_MODULE,
.pm = &charger_manager_pm,
},
.probe = charger_manager_probe,
.remove = __devexit_p(charger_manager_remove),
.id_table = charger_manager_id,
};
static int __init charger_manager_init(void)
{
return platform_driver_register(&charger_manager_driver);
}
late_initcall(charger_manager_init);
static void __exit charger_manager_cleanup(void)
{
platform_driver_unregister(&charger_manager_driver);
}
module_exit(charger_manager_cleanup);
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_DESCRIPTION("Charger Manager");
MODULE_LICENSE("GPL");
MODULE_ALIAS("charger-manager");
......@@ -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 @@ static int __devinit collie_bat_probe(struct ucb1x00_dev *dev)
/* 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 = {
......
......@@ -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");
......
/*
* Batttery Driver for Dialog DA9052 PMICs
*
* Copyright(c) 2011 Dialog Semiconductor Ltd.
*
* Author: David Dajun Chen <dchen@diasemi.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/delay.h>
#include <linux/freezer.h>
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/mfd/da9052/da9052.h>
#include <linux/mfd/da9052/pdata.h>
#include <linux/mfd/da9052/reg.h>
/* STATIC CONFIGURATION */
#define DA9052_BAT_CUTOFF_VOLT 2800
#define DA9052_BAT_TSH 62000
#define DA9052_BAT_LOW_CAP 4
#define DA9052_AVG_SZ 4
#define DA9052_VC_TBL_SZ 68
#define DA9052_VC_TBL_REF_SZ 3
#define DA9052_ISET_USB_MASK 0x0F
#define DA9052_CHG_USB_ILIM_MASK 0x40
#define DA9052_CHG_LIM_COLS 16
#define DA9052_MEAN(x, y) ((x + y) / 2)
enum charger_type_enum {
DA9052_NOCHARGER = 1,
DA9052_CHARGER,
};
static const u16 da9052_chg_current_lim[2][DA9052_CHG_LIM_COLS] = {
{70, 80, 90, 100, 110, 120, 400, 450,
500, 550, 600, 650, 700, 900, 1100, 1300},
{80, 90, 100, 110, 120, 400, 450, 500,
550, 600, 800, 1000, 1200, 1400, 1600, 1800},
};
static const u16 vc_tbl_ref[3] = {10, 25, 40};
/* Lookup table for voltage vs capacity */
static u32 const vc_tbl[3][68][2] = {
/* For temperature 10 degree Celsius */
{
{4082, 100}, {4036, 98},
{4020, 96}, {4008, 95},
{3997, 93}, {3983, 91},
{3964, 90}, {3943, 88},
{3926, 87}, {3912, 85},
{3900, 84}, {3890, 82},
{3881, 80}, {3873, 79},
{3865, 77}, {3857, 76},
{3848, 74}, {3839, 73},
{3829, 71}, {3820, 70},
{3811, 68}, {3802, 67},
{3794, 65}, {3785, 64},
{3778, 62}, {3770, 61},
{3763, 59}, {3756, 58},
{3750, 56}, {3744, 55},
{3738, 53}, {3732, 52},
{3727, 50}, {3722, 49},
{3717, 47}, {3712, 46},
{3708, 44}, {3703, 43},
{3700, 41}, {3696, 40},
{3693, 38}, {3691, 37},
{3688, 35}, {3686, 34},
{3683, 32}, {3681, 31},
{3678, 29}, {3675, 28},
{3672, 26}, {3669, 25},
{3665, 23}, {3661, 22},
{3656, 21}, {3651, 19},
{3645, 18}, {3639, 16},
{3631, 15}, {3622, 13},
{3611, 12}, {3600, 10},
{3587, 9}, {3572, 7},
{3548, 6}, {3503, 5},
{3420, 3}, {3268, 2},
{2992, 1}, {2746, 0}
},
/* For temperature 25 degree Celsius */
{
{4102, 100}, {4065, 98},
{4048, 96}, {4034, 95},
{4021, 93}, {4011, 92},
{4001, 90}, {3986, 88},
{3968, 87}, {3952, 85},
{3938, 84}, {3926, 82},
{3916, 81}, {3908, 79},
{3900, 77}, {3892, 76},
{3883, 74}, {3874, 73},
{3864, 71}, {3855, 70},
{3846, 68}, {3836, 67},
{3827, 65}, {3819, 64},
{3810, 62}, {3801, 61},
{3793, 59}, {3786, 58},
{3778, 56}, {3772, 55},
{3765, 53}, {3759, 52},
{3754, 50}, {3748, 49},
{3743, 47}, {3738, 46},
{3733, 44}, {3728, 43},
{3724, 41}, {3720, 40},
{3716, 38}, {3712, 37},
{3709, 35}, {3706, 34},
{3703, 33}, {3701, 31},
{3698, 30}, {3696, 28},
{3693, 27}, {3690, 25},
{3687, 24}, {3683, 22},
{3680, 21}, {3675, 19},
{3671, 18}, {3666, 17},
{3660, 15}, {3654, 14},
{3647, 12}, {3639, 11},
{3630, 9}, {3621, 8},
{3613, 6}, {3606, 5},
{3597, 4}, {3582, 2},
{3546, 1}, {2747, 0}
},
/* For temperature 40 degree Celsius */
{
{4114, 100}, {4081, 98},
{4065, 96}, {4050, 95},
{4036, 93}, {4024, 92},
{4013, 90}, {4002, 88},
{3990, 87}, {3976, 85},
{3962, 84}, {3950, 82},
{3939, 81}, {3930, 79},
{3921, 77}, {3912, 76},
{3902, 74}, {3893, 73},
{3883, 71}, {3874, 70},
{3865, 68}, {3856, 67},
{3847, 65}, {3838, 64},
{3829, 62}, {3820, 61},
{3812, 59}, {3803, 58},
{3795, 56}, {3787, 55},
{3780, 53}, {3773, 52},
{3767, 50}, {3761, 49},
{3756, 47}, {3751, 46},
{3746, 44}, {3741, 43},
{3736, 41}, {3732, 40},
{3728, 38}, {3724, 37},
{3720, 35}, {3716, 34},
{3713, 33}, {3710, 31},
{3707, 30}, {3704, 28},
{3701, 27}, {3698, 25},
{3695, 24}, {3691, 22},
{3686, 21}, {3681, 19},
{3676, 18}, {3671, 17},
{3666, 15}, {3661, 14},
{3655, 12}, {3648, 11},
{3640, 9}, {3632, 8},
{3622, 6}, {3616, 5},
{3611, 4}, {3604, 2},
{3594, 1}, {2747, 0}
}
};
struct da9052_battery {
struct da9052 *da9052;
struct power_supply psy;
struct notifier_block nb;
int charger_type;
int status;
int health;
};
static inline int volt_reg_to_mV(int value)
{
return ((value * 1000) / 512) + 2500;
}
static inline int ichg_reg_to_mA(int value)
{
return (value * 3900) / 1000;
}
static int da9052_read_chgend_current(struct da9052_battery *bat,
int *current_mA)
{
int ret;
if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
return -EINVAL;
ret = da9052_reg_read(bat->da9052, DA9052_ICHG_END_REG);
if (ret < 0)
return ret;
*current_mA = ichg_reg_to_mA(ret & DA9052_ICHGEND_ICHGEND);
return 0;
}
static int da9052_read_chg_current(struct da9052_battery *bat, int *current_mA)
{
int ret;
if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
return -EINVAL;
ret = da9052_reg_read(bat->da9052, DA9052_ICHG_AV_REG);
if (ret < 0)
return ret;
*current_mA = ichg_reg_to_mA(ret & DA9052_ICHGAV_ICHGAV);
return 0;
}
static int da9052_bat_check_status(struct da9052_battery *bat, int *status)
{
u8 v[2] = {0, 0};
u8 bat_status;
u8 chg_end;
int ret;
int chg_current;
int chg_end_current;
bool dcinsel;
bool dcindet;
bool vbussel;
bool vbusdet;
bool dc;
bool vbus;
ret = da9052_group_read(bat->da9052, DA9052_STATUS_A_REG, 2, v);
if (ret < 0)
return ret;
bat_status = v[0];
chg_end = v[1];
dcinsel = bat_status & DA9052_STATUSA_DCINSEL;
dcindet = bat_status & DA9052_STATUSA_DCINDET;
vbussel = bat_status & DA9052_STATUSA_VBUSSEL;
vbusdet = bat_status & DA9052_STATUSA_VBUSDET;
dc = dcinsel && dcindet;
vbus = vbussel && vbusdet;
/* Preference to WALL(DCIN) charger unit */
if (dc || vbus) {
bat->charger_type = DA9052_CHARGER;
/* If charging end flag is set and Charging current is greater
* than charging end limit then battery is charging
*/
if ((chg_end & DA9052_STATUSB_CHGEND) != 0) {
ret = da9052_read_chg_current(bat, &chg_current);
if (ret < 0)
return ret;
ret = da9052_read_chgend_current(bat, &chg_end_current);
if (ret < 0)
return ret;
if (chg_current >= chg_end_current)
bat->status = POWER_SUPPLY_STATUS_CHARGING;
else
bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
/* If Charging end flag is cleared then battery is
* charging
*/
bat->status = POWER_SUPPLY_STATUS_CHARGING;
}
} else if (dcindet || vbusdet) {
bat->charger_type = DA9052_CHARGER;
bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
} else {
bat->charger_type = DA9052_NOCHARGER;
bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
}
if (status != NULL)
*status = bat->status;
return 0;
}
static int da9052_bat_read_volt(struct da9052_battery *bat, int *volt_mV)
{
int volt;
volt = da9052_adc_manual_read(bat->da9052, DA9052_ADC_MAN_MUXSEL_VBAT);
if (volt < 0)
return volt;
*volt_mV = volt_reg_to_mV(volt);
return 0;
}
static int da9052_bat_check_presence(struct da9052_battery *bat, int *illegal)
{
int bat_temp;
bat_temp = da9052_adc_read_temp(bat->da9052);
if (bat_temp < 0)
return bat_temp;
if (bat_temp > DA9052_BAT_TSH)
*illegal = 1;
else
*illegal = 0;
return 0;
}
static int da9052_bat_interpolate(int vbat_lower, int vbat_upper,
int level_lower, int level_upper,
int bat_voltage)
{
int tmp;
tmp = ((level_upper - level_lower) * 1000) / (vbat_upper - vbat_lower);
tmp = level_lower + (((bat_voltage - vbat_lower) * tmp) / 1000);
return tmp;
}
unsigned char da9052_determine_vc_tbl_index(unsigned char adc_temp)
{
int i;
if (adc_temp <= vc_tbl_ref[0])
return 0;
if (adc_temp > vc_tbl_ref[DA9052_VC_TBL_REF_SZ - 1])
return DA9052_VC_TBL_REF_SZ - 1;
for (i = 0; i < DA9052_VC_TBL_REF_SZ; i++) {
if ((adc_temp > vc_tbl_ref[i]) &&
(adc_temp <= DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1])))
return i;
if ((adc_temp > DA9052_MEAN(vc_tbl_ref[i], vc_tbl_ref[i + 1]))
&& (adc_temp <= vc_tbl_ref[i]))
return i + 1;
}
}
static int da9052_bat_read_capacity(struct da9052_battery *bat, int *capacity)
{
int adc_temp;
int bat_voltage;
int vbat_lower;
int vbat_upper;
int level_upper;
int level_lower;
int ret;
int flag;
int i = 0;
int j;
ret = da9052_bat_read_volt(bat, &bat_voltage);
if (ret < 0)
return ret;
adc_temp = da9052_adc_read_temp(bat->da9052);
if (adc_temp < 0)
return adc_temp;
i = da9052_determine_vc_tbl_index(adc_temp);
if (bat_voltage >= vc_tbl[i][0][0]) {
*capacity = 100;
return 0;
}
if (bat_voltage <= vc_tbl[i][DA9052_VC_TBL_SZ - 1][0]) {
*capacity = 0;
return 0;
}
flag = 0;
for (j = 0; j < (DA9052_VC_TBL_SZ-1); j++) {
if ((bat_voltage <= vc_tbl[i][j][0]) &&
(bat_voltage >= vc_tbl[i][j + 1][0])) {
vbat_upper = vc_tbl[i][j][0];
vbat_lower = vc_tbl[i][j + 1][0];
level_upper = vc_tbl[i][j][1];
level_lower = vc_tbl[i][j + 1][1];
flag = 1;
break;
}
}
if (!flag)
return -EIO;
*capacity = da9052_bat_interpolate(vbat_lower, vbat_upper, level_lower,
level_upper, bat_voltage);
return 0;
}
static int da9052_bat_check_health(struct da9052_battery *bat, int *health)
{
int ret;
int bat_illegal;
int capacity;
ret = da9052_bat_check_presence(bat, &bat_illegal);
if (ret < 0)
return ret;
if (bat_illegal) {
bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
return 0;
}
if (bat->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
ret = da9052_bat_read_capacity(bat, &capacity);
if (ret < 0)
return ret;
if (capacity < DA9052_BAT_LOW_CAP)
bat->health = POWER_SUPPLY_HEALTH_DEAD;
else
bat->health = POWER_SUPPLY_HEALTH_GOOD;
}
*health = bat->health;
return 0;
}
static irqreturn_t da9052_bat_irq(int irq, void *data)
{
struct da9052_battery *bat = data;
irq -= bat->da9052->irq_base;
if (irq == DA9052_IRQ_CHGEND)
bat->status = POWER_SUPPLY_STATUS_FULL;
else
da9052_bat_check_status(bat, NULL);
if (irq == DA9052_IRQ_CHGEND || irq == DA9052_IRQ_DCIN ||
irq == DA9052_IRQ_VBUS || irq == DA9052_IRQ_TBAT) {
power_supply_changed(&bat->psy);
}
return IRQ_HANDLED;
}
static int da9052_USB_current_notifier(struct notifier_block *nb,
unsigned long events, void *data)
{
u8 row;
u8 col;
int *current_mA = data;
int ret;
struct da9052_battery *bat = container_of(nb, struct da9052_battery,
nb);
if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING)
return -EPERM;
ret = da9052_reg_read(bat->da9052, DA9052_CHGBUCK_REG);
if (ret & DA9052_CHG_USB_ILIM_MASK)
return -EPERM;
if (bat->da9052->chip_id == DA9052)
row = 0;
else
row = 1;
if (*current_mA < da9052_chg_current_lim[row][0] ||
*current_mA > da9052_chg_current_lim[row][DA9052_CHG_LIM_COLS - 1])
return -EINVAL;
for (col = 0; col <= DA9052_CHG_LIM_COLS - 1 ; col++) {
if (*current_mA <= da9052_chg_current_lim[row][col])
break;
}
return da9052_reg_update(bat->da9052, DA9052_ISET_REG,
DA9052_ISET_USB_MASK, col);
}
static int da9052_bat_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret;
int illegal;
struct da9052_battery *bat = container_of(psy, struct da9052_battery,
psy);
ret = da9052_bat_check_presence(bat, &illegal);
if (ret < 0)
return ret;
if (illegal && psp != POWER_SUPPLY_PROP_PRESENT)
return -ENODEV;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
ret = da9052_bat_check_status(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval =
(bat->charger_type == DA9052_NOCHARGER) ? 0 : 1;
break;
case POWER_SUPPLY_PROP_PRESENT:
ret = da9052_bat_check_presence(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_HEALTH:
ret = da9052_bat_check_health(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
val->intval = DA9052_BAT_CUTOFF_VOLT * 1000;
break;
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
ret = da9052_bat_read_volt(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = da9052_read_chg_current(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = da9052_bat_read_capacity(bat, &val->intval);
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = da9052_adc_read_temp(bat->da9052);
ret = val->intval;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
default:
return -EINVAL;
}
return ret;
}
static enum power_supply_property da9052_bat_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
POWER_SUPPLY_PROP_VOLTAGE_AVG,
POWER_SUPPLY_PROP_CURRENT_AVG,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TECHNOLOGY,
};
static struct power_supply template_battery = {
.name = "da9052-bat",
.type = POWER_SUPPLY_TYPE_BATTERY,
.properties = da9052_bat_props,
.num_properties = ARRAY_SIZE(da9052_bat_props),
.get_property = da9052_bat_get_property,
};
static const char *const da9052_bat_irqs[] = {
"BATT TEMP",
"DCIN DET",
"DCIN REM",
"VBUS DET",
"VBUS REM",
"CHG END",
};
static s32 __devinit da9052_bat_probe(struct platform_device *pdev)
{
struct da9052_pdata *pdata;
struct da9052_battery *bat;
int ret;
int irq;
int i;
bat = kzalloc(sizeof(struct da9052_battery), GFP_KERNEL);
if (!bat)
return -ENOMEM;
bat->da9052 = dev_get_drvdata(pdev->dev.parent);
bat->psy = template_battery;
bat->charger_type = DA9052_NOCHARGER;
bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
bat->health = POWER_SUPPLY_HEALTH_UNKNOWN;
bat->nb.notifier_call = da9052_USB_current_notifier;
pdata = bat->da9052->dev->platform_data;
if (pdata != NULL && pdata->use_for_apm)
bat->psy.use_for_apm = pdata->use_for_apm;
else
bat->psy.use_for_apm = 1;
for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
ret = request_threaded_irq(bat->da9052->irq_base + irq,
NULL, da9052_bat_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
da9052_bat_irqs[i], bat);
if (ret != 0) {
dev_err(bat->da9052->dev,
"DA9052 failed to request %s IRQ %d: %d\n",
da9052_bat_irqs[i], irq, ret);
goto err;
}
}
ret = power_supply_register(&pdev->dev, &bat->psy);
if (ret)
goto err;
return 0;
err:
for (; i >= 0; i--) {
irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
free_irq(bat->da9052->irq_base + irq, bat);
}
kfree(bat);
return ret;
}
static int __devexit da9052_bat_remove(struct platform_device *pdev)
{
int i;
int irq;
struct da9052_battery *bat = platform_get_drvdata(pdev);
for (i = 0; i < ARRAY_SIZE(da9052_bat_irqs); i++) {
irq = platform_get_irq_byname(pdev, da9052_bat_irqs[i]);
free_irq(bat->da9052->irq_base + irq, bat);
}
power_supply_unregister(&bat->psy);
return 0;
}
static struct platform_driver da9052_bat_driver = {
.probe = da9052_bat_probe,
.remove = __devexit_p(da9052_bat_remove),
.driver = {
.name = "da9052-bat",
.owner = THIS_MODULE,
},
};
static int __init da9052_bat_init(void)
{
return platform_driver_register(&da9052_bat_driver);
}
module_init(da9052_bat_init);
static void __exit da9052_bat_exit(void)
{
platform_driver_unregister(&da9052_bat_driver);
}
module_exit(da9052_bat_exit);
MODULE_DESCRIPTION("DA9052 BAT Device Driver");
MODULE_AUTHOR("David Dajun Chen <dchen@diasemi.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:da9052-bat");
......@@ -95,7 +95,11 @@ static int rated_capacities[] = {
2880, /* Samsung */
2880, /* BYD */
2880, /* Lishen */
2880 /* NEC */
2880, /* NEC */
#ifdef CONFIG_MACH_H4700
0,
3600, /* HP iPAQ hx4700 3.7V 3600mAh (359114-001) */
#endif
};
/* array is level at temps 0°C, 10°C, 20°C, 30°C, 40°C
......@@ -637,18 +641,7 @@ static struct platform_driver ds2760_battery_driver = {
.resume = ds2760_battery_resume,
};
static int __init ds2760_battery_init(void)
{
return platform_driver_register(&ds2760_battery_driver);
}
static void __exit ds2760_battery_exit(void)
{
platform_driver_unregister(&ds2760_battery_driver);
}
module_init(ds2760_battery_init);
module_exit(ds2760_battery_exit);
module_platform_driver(ds2760_battery_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
......
......@@ -841,29 +841,17 @@ static int __devexit ds2780_battery_remove(struct platform_device *pdev)
return 0;
}
MODULE_ALIAS("platform:ds2780-battery");
static struct platform_driver ds2780_battery_driver = {
.driver = {
.name = "ds2780-battery",
},
.probe = ds2780_battery_probe,
.remove = ds2780_battery_remove,
.remove = __devexit_p(ds2780_battery_remove),
};
static int __init ds2780_battery_init(void)
{
return platform_driver_register(&ds2780_battery_driver);
}
static void __exit ds2780_battery_exit(void)
{
platform_driver_unregister(&ds2780_battery_driver);
}
module_init(ds2780_battery_init);
module_exit(ds2780_battery_exit);
module_platform_driver(ds2780_battery_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>");
MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver");
MODULE_ALIAS("platform:ds2780-battery");
......@@ -185,17 +185,7 @@ static struct platform_driver gpio_charger_driver = {
},
};
static int __init gpio_charger_init(void)
{
return platform_driver_register(&gpio_charger_driver);
}
module_init(gpio_charger_init);
static void __exit gpio_charger_exit(void)
{
platform_driver_unregister(&gpio_charger_driver);
}
module_exit(gpio_charger_exit);
module_platform_driver(gpio_charger_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("Driver for chargers which report their online status through a GPIO");
......
......@@ -779,18 +779,7 @@ static struct platform_driver platform_pmic_battery_driver = {
.remove = __devexit_p(platform_pmic_battery_remove),
};
static int __init platform_pmic_battery_module_init(void)
{
return platform_driver_register(&platform_pmic_battery_driver);
}
static void __exit platform_pmic_battery_module_exit(void)
{
platform_driver_unregister(&platform_pmic_battery_driver);
}
module_init(platform_pmic_battery_module_init);
module_exit(platform_pmic_battery_module_exit);
module_platform_driver(platform_pmic_battery_driver);
MODULE_AUTHOR("Nithish Mahalingam <nithish.mahalingam@intel.com>");
MODULE_DESCRIPTION("Intel Moorestown PMIC Battery Driver");
......
......@@ -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");
......
......@@ -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");
......
/*
* Driver for LP8727 Micro/Mini USB IC with intergrated charger
*
* Copyright (C) 2011 National Semiconductor
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/power_supply.h>
#include <linux/lp8727.h>
#define DEBOUNCE_MSEC 270
/* Registers */
#define CTRL1 0x1
#define CTRL2 0x2
#define SWCTRL 0x3
#define INT1 0x4
#define INT2 0x5
#define STATUS1 0x6
#define STATUS2 0x7
#define CHGCTRL2 0x9
/* CTRL1 register */
#define CP_EN (1 << 0)
#define ADC_EN (1 << 1)
#define ID200_EN (1 << 4)
/* CTRL2 register */
#define CHGDET_EN (1 << 1)
#define INT_EN (1 << 6)
/* SWCTRL register */
#define SW_DM1_DM (0x0 << 0)
#define SW_DM1_U1 (0x1 << 0)
#define SW_DM1_HiZ (0x7 << 0)
#define SW_DP2_DP (0x0 << 3)
#define SW_DP2_U2 (0x1 << 3)
#define SW_DP2_HiZ (0x7 << 3)
/* INT1 register */
#define IDNO (0xF << 0)
#define VBUS (1 << 4)
/* STATUS1 register */
#define CHGSTAT (3 << 4)
#define CHPORT (1 << 6)
#define DCPORT (1 << 7)
/* STATUS2 register */
#define TEMP_STAT (3 << 5)
enum lp8727_dev_id {
ID_NONE,
ID_TA,
ID_DEDICATED_CHG,
ID_USB_CHG,
ID_USB_DS,
ID_MAX,
};
enum lp8727_chg_stat {
PRECHG,
CC,
CV,
EOC,
};
struct lp8727_psy {
struct power_supply ac;
struct power_supply usb;
struct power_supply batt;
};
struct lp8727_chg {
struct device *dev;
struct i2c_client *client;
struct mutex xfer_lock;
struct delayed_work work;
struct workqueue_struct *irqthread;
struct lp8727_platform_data *pdata;
struct lp8727_psy *psy;
struct lp8727_chg_param *chg_parm;
enum lp8727_dev_id devid;
};
static int lp8727_i2c_read(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
{
s32 ret;
mutex_lock(&pchg->xfer_lock);
ret = i2c_smbus_read_i2c_block_data(pchg->client, reg, len, data);
mutex_unlock(&pchg->xfer_lock);
return (ret != len) ? -EIO : 0;
}
static int lp8727_i2c_write(struct lp8727_chg *pchg, u8 reg, u8 *data, u8 len)
{
s32 ret;
mutex_lock(&pchg->xfer_lock);
ret = i2c_smbus_write_i2c_block_data(pchg->client, reg, len, data);
mutex_unlock(&pchg->xfer_lock);
return ret;
}
static inline int lp8727_i2c_read_byte(struct lp8727_chg *pchg, u8 reg,
u8 *data)
{
return lp8727_i2c_read(pchg, reg, data, 1);
}
static inline int lp8727_i2c_write_byte(struct lp8727_chg *pchg, u8 reg,
u8 *data)
{
return lp8727_i2c_write(pchg, reg, data, 1);
}
static int lp8727_is_charger_attached(const char *name, int id)
{
if (name) {
if (!strcmp(name, "ac"))
return (id == ID_TA || id == ID_DEDICATED_CHG) ? 1 : 0;
else if (!strcmp(name, "usb"))
return (id == ID_USB_CHG) ? 1 : 0;
}
return (id >= ID_TA && id <= ID_USB_CHG) ? 1 : 0;
}
static void lp8727_init_device(struct lp8727_chg *pchg)
{
u8 val;
val = ID200_EN | ADC_EN | CP_EN;
if (lp8727_i2c_write_byte(pchg, CTRL1, &val))
dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL1);
val = INT_EN | CHGDET_EN;
if (lp8727_i2c_write_byte(pchg, CTRL2, &val))
dev_err(pchg->dev, "i2c write err : addr=0x%.2x\n", CTRL2);
}
static int lp8727_is_dedicated_charger(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, STATUS1, &val);
return (val & DCPORT);
}
static int lp8727_is_usb_charger(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, STATUS1, &val);
return (val & CHPORT);
}
static void lp8727_ctrl_switch(struct lp8727_chg *pchg, u8 sw)
{
u8 val = sw;
lp8727_i2c_write_byte(pchg, SWCTRL, &val);
}
static void lp8727_id_detection(struct lp8727_chg *pchg, u8 id, int vbusin)
{
u8 devid = ID_NONE;
u8 swctrl = SW_DM1_HiZ | SW_DP2_HiZ;
switch (id) {
case 0x5:
devid = ID_TA;
pchg->chg_parm = &pchg->pdata->ac;
break;
case 0xB:
if (lp8727_is_dedicated_charger(pchg)) {
pchg->chg_parm = &pchg->pdata->ac;
devid = ID_DEDICATED_CHG;
} else if (lp8727_is_usb_charger(pchg)) {
pchg->chg_parm = &pchg->pdata->usb;
devid = ID_USB_CHG;
swctrl = SW_DM1_DM | SW_DP2_DP;
} else if (vbusin) {
devid = ID_USB_DS;
swctrl = SW_DM1_DM | SW_DP2_DP;
}
break;
default:
devid = ID_NONE;
pchg->chg_parm = NULL;
break;
}
pchg->devid = devid;
lp8727_ctrl_switch(pchg, swctrl);
}
static void lp8727_enable_chgdet(struct lp8727_chg *pchg)
{
u8 val;
lp8727_i2c_read_byte(pchg, CTRL2, &val);
val |= CHGDET_EN;
lp8727_i2c_write_byte(pchg, CTRL2, &val);
}
static void lp8727_delayed_func(struct work_struct *_work)
{
u8 intstat[2], idno, vbus;
struct lp8727_chg *pchg =
container_of(_work, struct lp8727_chg, work.work);
if (lp8727_i2c_read(pchg, INT1, intstat, 2)) {
dev_err(pchg->dev, "can not read INT registers\n");
return;
}
idno = intstat[0] & IDNO;
vbus = intstat[0] & VBUS;
lp8727_id_detection(pchg, idno, vbus);
lp8727_enable_chgdet(pchg);
power_supply_changed(&pchg->psy->ac);
power_supply_changed(&pchg->psy->usb);
power_supply_changed(&pchg->psy->batt);
}
static irqreturn_t lp8727_isr_func(int irq, void *ptr)
{
struct lp8727_chg *pchg = ptr;
unsigned long delay = msecs_to_jiffies(DEBOUNCE_MSEC);
queue_delayed_work(pchg->irqthread, &pchg->work, delay);
return IRQ_HANDLED;
}
static void lp8727_intr_config(struct lp8727_chg *pchg)
{
INIT_DELAYED_WORK(&pchg->work, lp8727_delayed_func);
pchg->irqthread = create_singlethread_workqueue("lp8727-irqthd");
if (!pchg->irqthread)
dev_err(pchg->dev, "can not create thread for lp8727\n");
if (request_threaded_irq(pchg->client->irq,
NULL,
lp8727_isr_func,
IRQF_TRIGGER_FALLING, "lp8727_irq", pchg)) {
dev_err(pchg->dev, "lp8727 irq can not be registered\n");
}
}
static enum power_supply_property lp8727_charger_prop[] = {
POWER_SUPPLY_PROP_ONLINE,
};
static enum power_supply_property lp8727_battery_prop[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_TEMP,
};
static char *battery_supplied_to[] = {
"main_batt",
};
static int lp8727_charger_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
if (psp == POWER_SUPPLY_PROP_ONLINE)
val->intval = lp8727_is_charger_attached(psy->name,
pchg->devid);
return 0;
}
static int lp8727_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
u8 read;
switch (psp) {
case POWER_SUPPLY_PROP_STATUS:
if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
lp8727_i2c_read_byte(pchg, STATUS1, &read);
if (((read & CHGSTAT) >> 4) == EOC)
val->intval = POWER_SUPPLY_STATUS_FULL;
else
val->intval = POWER_SUPPLY_STATUS_CHARGING;
} else {
val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
}
break;
case POWER_SUPPLY_PROP_HEALTH:
lp8727_i2c_read_byte(pchg, STATUS2, &read);
read = (read & TEMP_STAT) >> 5;
if (read >= 0x1 && read <= 0x3)
val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
else
val->intval = POWER_SUPPLY_HEALTH_GOOD;
break;
case POWER_SUPPLY_PROP_PRESENT:
if (pchg->pdata->get_batt_present)
val->intval = pchg->pdata->get_batt_present();
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
if (pchg->pdata->get_batt_level)
val->intval = pchg->pdata->get_batt_level();
break;
case POWER_SUPPLY_PROP_CAPACITY:
if (pchg->pdata->get_batt_capacity)
val->intval = pchg->pdata->get_batt_capacity();
break;
case POWER_SUPPLY_PROP_TEMP:
if (pchg->pdata->get_batt_temp)
val->intval = pchg->pdata->get_batt_temp();
break;
default:
break;
}
return 0;
}
static void lp8727_charger_changed(struct power_supply *psy)
{
struct lp8727_chg *pchg = dev_get_drvdata(psy->dev->parent);
u8 val;
u8 eoc_level, ichg;
if (lp8727_is_charger_attached(psy->name, pchg->devid)) {
if (pchg->chg_parm) {
eoc_level = pchg->chg_parm->eoc_level;
ichg = pchg->chg_parm->ichg;
val = (ichg << 4) | eoc_level;
lp8727_i2c_write_byte(pchg, CHGCTRL2, &val);
}
}
}
static int lp8727_register_psy(struct lp8727_chg *pchg)
{
struct lp8727_psy *psy;
psy = kzalloc(sizeof(*psy), GFP_KERNEL);
if (!psy)
goto err_mem;
pchg->psy = psy;
psy->ac.name = "ac";
psy->ac.type = POWER_SUPPLY_TYPE_MAINS;
psy->ac.properties = lp8727_charger_prop;
psy->ac.num_properties = ARRAY_SIZE(lp8727_charger_prop);
psy->ac.get_property = lp8727_charger_get_property;
psy->ac.supplied_to = battery_supplied_to;
psy->ac.num_supplicants = ARRAY_SIZE(battery_supplied_to);
if (power_supply_register(pchg->dev, &psy->ac))
goto err_psy;
psy->usb.name = "usb";
psy->usb.type = POWER_SUPPLY_TYPE_USB;
psy->usb.properties = lp8727_charger_prop;
psy->usb.num_properties = ARRAY_SIZE(lp8727_charger_prop);
psy->usb.get_property = lp8727_charger_get_property;
psy->usb.supplied_to = battery_supplied_to;
psy->usb.num_supplicants = ARRAY_SIZE(battery_supplied_to);
if (power_supply_register(pchg->dev, &psy->usb))
goto err_psy;
psy->batt.name = "main_batt";
psy->batt.type = POWER_SUPPLY_TYPE_BATTERY;
psy->batt.properties = lp8727_battery_prop;
psy->batt.num_properties = ARRAY_SIZE(lp8727_battery_prop);
psy->batt.get_property = lp8727_battery_get_property;
psy->batt.external_power_changed = lp8727_charger_changed;
if (power_supply_register(pchg->dev, &psy->batt))
goto err_psy;
return 0;
err_mem:
return -ENOMEM;
err_psy:
kfree(psy);
return -EPERM;
}
static void lp8727_unregister_psy(struct lp8727_chg *pchg)
{
struct lp8727_psy *psy = pchg->psy;
if (!psy)
return;
power_supply_unregister(&psy->ac);
power_supply_unregister(&psy->usb);
power_supply_unregister(&psy->batt);
kfree(psy);
}
static int lp8727_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp8727_chg *pchg;
int ret;
if (!i2c_check_functionality(cl->adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
return -EIO;
pchg = kzalloc(sizeof(*pchg), GFP_KERNEL);
if (!pchg)
return -ENOMEM;
pchg->client = cl;
pchg->dev = &cl->dev;
pchg->pdata = cl->dev.platform_data;
i2c_set_clientdata(cl, pchg);
mutex_init(&pchg->xfer_lock);
lp8727_init_device(pchg);
lp8727_intr_config(pchg);
ret = lp8727_register_psy(pchg);
if (ret)
dev_err(pchg->dev,
"can not register power supplies. err=%d", ret);
return 0;
}
static int __devexit lp8727_remove(struct i2c_client *cl)
{
struct lp8727_chg *pchg = i2c_get_clientdata(cl);
lp8727_unregister_psy(pchg);
free_irq(pchg->client->irq, pchg);
flush_workqueue(pchg->irqthread);
destroy_workqueue(pchg->irqthread);
kfree(pchg);
return 0;
}
static const struct i2c_device_id lp8727_ids[] = {
{"lp8727", 0},
};
static struct i2c_driver lp8727_driver = {
.driver = {
.name = "lp8727",
},
.probe = lp8727_probe,
.remove = __devexit_p(lp8727_remove),
.id_table = lp8727_ids,
};
static int __init lp8727_init(void)
{
return i2c_add_driver(&lp8727_driver);
}
static void __exit lp8727_exit(void)
{
i2c_del_driver(&lp8727_driver);
}
module_init(lp8727_init);
module_exit(lp8727_exit);
MODULE_DESCRIPTION("National Semiconductor LP8727 charger driver");
MODULE_AUTHOR
("Woogyom Kim <milo.kim@ti.com>, Daniel Jeong <daniel.jeong@ti.com>");
MODULE_LICENSE("GPL");
......@@ -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;
......
......@@ -374,19 +374,9 @@ static struct platform_driver max8903_driver = {
},
};
static int __init max8903_init(void)
{
return platform_driver_register(&max8903_driver);
}
module_init(max8903_init);
static void __exit max8903_exit(void)
{
platform_driver_unregister(&max8903_driver);
}
module_exit(max8903_exit);
module_platform_driver(max8903_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MAX8903 Charger Driver");
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
MODULE_ALIAS("max8903-charger");
MODULE_ALIAS("platform:max8903-charger");
......@@ -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");
......
......@@ -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 = {
......
......@@ -154,6 +154,7 @@ static __devinit int max8998_battery_probe(struct platform_device *pdev)
case 0:
dev_dbg(max8998->dev,
"Full Timeout not set: leave it unchanged.\n");
break;
default:
dev_err(max8998->dev, "Invalid Full Timeout value\n");
ret = -EINVAL;
......@@ -190,6 +191,7 @@ static int __devexit max8998_battery_remove(struct platform_device *pdev)
static const struct platform_device_id max8998_battery_id[] = {
{ "max8998-battery", TYPE_MAX8998 },
{ }
};
static struct platform_driver max8998_battery_driver = {
......@@ -202,17 +204,7 @@ static struct platform_driver max8998_battery_driver = {
.id_table = max8998_battery_id,
};
static int __init max8998_battery_init(void)
{
return platform_driver_register(&max8998_battery_driver);
}
module_init(max8998_battery_init);
static void __exit max8998_battery_cleanup(void)
{
platform_driver_unregister(&max8998_battery_driver);
}
module_exit(max8998_battery_cleanup);
module_platform_driver(max8998_battery_driver);
MODULE_DESCRIPTION("MAXIM 8998 battery control driver");
MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>");
......
......@@ -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 @@ static int __init olpc_bat_init(void)
power_supply_unregister(&olpc_bat);
battery_failed:
power_supply_unregister(&olpc_ac);
ac_failed:
platform_device_unregister(bat_pdev);
success:
return ret;
}
static void __exit olpc_bat_exit(void)
static int __devexit olpc_battery_remove(struct platform_device *pdev)
{
device_remove_file(olpc_bat.dev, &olpc_bat_error);
device_remove_bin_file(olpc_bat.dev, &olpc_bat_eeprom);
power_supply_unregister(&olpc_bat);
power_supply_unregister(&olpc_ac);
platform_device_unregister(bat_pdev);
return 0;
}
module_init(olpc_bat_init);
module_exit(olpc_bat_exit);
static const struct of_device_id olpc_battery_ids[] __devinitconst = {
{ .compatible = "olpc,xo1-battery" },
{}
};
MODULE_DEVICE_TABLE(of, olpc_battery_ids);
static struct platform_driver olpc_battery_driver = {
.driver = {
.name = "olpc-battery",
.owner = THIS_MODULE,
.of_match_table = olpc_battery_ids,
},
.probe = olpc_battery_probe,
.remove = __devexit_p(olpc_battery_remove),
.suspend = olpc_battery_suspend,
};
module_platform_driver(olpc_battery_driver);
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_LICENSE("GPL");
......
......@@ -474,17 +474,7 @@ static struct platform_driver pcf50633_mbc_driver = {
.remove = __devexit_p(pcf50633_mbc_remove),
};
static int __init pcf50633_mbc_init(void)
{
return platform_driver_register(&pcf50633_mbc_driver);
}
module_init(pcf50633_mbc_init);
static void __exit pcf50633_mbc_exit(void)
{
platform_driver_unregister(&pcf50633_mbc_driver);
}
module_exit(pcf50633_mbc_exit);
module_platform_driver(pcf50633_mbc_driver);
MODULE_AUTHOR("Balaji Rao <balajirrao@openmoko.org>");
MODULE_DESCRIPTION("PCF50633 mbc driver");
......
......@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/notifier.h>
#include <linux/power_supply.h>
#include <linux/pda_power.h>
#include <linux/regulator/consumer.h>
......@@ -40,7 +41,9 @@ static int polling;
#ifdef CONFIG_USB_OTG_UTILS
static struct otg_transceiver *transceiver;
static struct notifier_block otg_nb;
#endif
static struct regulator *ac_draw;
enum {
......@@ -222,7 +225,42 @@ static void polling_timer_func(unsigned long unused)
#ifdef CONFIG_USB_OTG_UTILS
static int otg_is_usb_online(void)
{
return (transceiver->state == OTG_STATE_B_PERIPHERAL);
return (transceiver->last_event == USB_EVENT_VBUS ||
transceiver->last_event == USB_EVENT_ENUMERATED);
}
static int otg_is_ac_online(void)
{
return (transceiver->last_event == USB_EVENT_CHARGER);
}
static int otg_handle_notification(struct notifier_block *nb,
unsigned long event, void *unused)
{
switch (event) {
case USB_EVENT_CHARGER:
ac_status = PDA_PSY_TO_CHANGE;
break;
case USB_EVENT_VBUS:
case USB_EVENT_ENUMERATED:
usb_status = PDA_PSY_TO_CHANGE;
break;
case USB_EVENT_NONE:
ac_status = PDA_PSY_TO_CHANGE;
usb_status = PDA_PSY_TO_CHANGE;
break;
default:
return NOTIFY_OK;
}
/*
* Wait a bit before reading ac/usb line status and setting charger,
* because ac/usb status readings may lag from irq.
*/
mod_timer(&charger_timer,
jiffies + msecs_to_jiffies(pdata->wait_for_status));
return NOTIFY_OK;
}
#endif
......@@ -282,6 +320,16 @@ static int pda_power_probe(struct platform_device *pdev)
ret = PTR_ERR(ac_draw);
}
#ifdef CONFIG_USB_OTG_UTILS
transceiver = otg_get_transceiver();
if (transceiver && !pdata->is_usb_online) {
pdata->is_usb_online = otg_is_usb_online;
}
if (transceiver && !pdata->is_ac_online) {
pdata->is_ac_online = otg_is_ac_online;
}
#endif
if (pdata->is_ac_online) {
ret = power_supply_register(&pdev->dev, &pda_psy_ac);
if (ret) {
......@@ -303,13 +351,6 @@ static int pda_power_probe(struct platform_device *pdev)
}
}
#ifdef CONFIG_USB_OTG_UTILS
transceiver = otg_get_transceiver();
if (transceiver && !pdata->is_usb_online) {
pdata->is_usb_online = otg_is_usb_online;
}
#endif
if (pdata->is_usb_online) {
ret = power_supply_register(&pdev->dev, &pda_psy_usb);
if (ret) {
......@@ -331,6 +372,18 @@ static int pda_power_probe(struct platform_device *pdev)
}
}
#ifdef CONFIG_USB_OTG_UTILS
if (transceiver && pdata->use_otg_notifier) {
otg_nb.notifier_call = otg_handle_notification;
ret = otg_register_notifier(transceiver, &otg_nb);
if (ret) {
dev_err(dev, "failure to register otg notifier\n");
goto otg_reg_notifier_failed;
}
polling = 0;
}
#endif
if (polling) {
dev_dbg(dev, "will poll for status\n");
setup_timer(&polling_timer, polling_timer_func, 0);
......@@ -343,6 +396,11 @@ static int pda_power_probe(struct platform_device *pdev)
return 0;
#ifdef CONFIG_USB_OTG_UTILS
otg_reg_notifier_failed:
if (pdata->is_usb_online && usb_irq)
free_irq(usb_irq->start, &pda_psy_usb);
#endif
usb_irq_failed:
if (pdata->is_usb_online)
power_supply_unregister(&pda_psy_usb);
......@@ -440,8 +498,6 @@ static int pda_power_resume(struct platform_device *pdev)
#define pda_power_resume NULL
#endif /* CONFIG_PM */
MODULE_ALIAS("platform:pda-power");
static struct platform_driver pda_power_pdrv = {
.driver = {
.name = "pda-power",
......@@ -452,17 +508,8 @@ static struct platform_driver pda_power_pdrv = {
.resume = pda_power_resume,
};
static int __init pda_power_init(void)
{
return platform_driver_register(&pda_power_pdrv);
}
module_platform_driver(pda_power_pdrv);
static void __exit pda_power_exit(void)
{
platform_driver_unregister(&pda_power_pdrv);
}
module_init(pda_power_init);
module_exit(pda_power_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
MODULE_ALIAS("platform:pda-power");
......@@ -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);
}
......
......@@ -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),
......
......@@ -47,6 +47,22 @@ static void s3c_adc_bat_ext_power_changed(struct power_supply *psy)
msecs_to_jiffies(JITTER_DELAY));
}
static int gather_samples(struct s3c_adc_client *client, int num, int channel)
{
int value, i;
/* default to 1 if nothing is set */
if (num < 1)
num = 1;
value = 0;
for (i = 0; i < num; i++)
value += s3c_adc_read(client, channel);
value /= num;
return value;
}
static enum power_supply_property s3c_adc_backup_bat_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
......@@ -67,7 +83,8 @@ static int s3c_adc_backup_bat_get_property(struct power_supply *psy,
if (bat->volt_value < 0 ||
jiffies_to_msecs(jiffies - bat->timestamp) >
BAT_POLL_INTERVAL) {
bat->volt_value = s3c_adc_read(bat->client,
bat->volt_value = gather_samples(bat->client,
bat->pdata->backup_volt_samples,
bat->pdata->backup_volt_channel);
bat->volt_value *= bat->pdata->backup_volt_mult;
bat->timestamp = jiffies;
......@@ -139,9 +156,11 @@ static int s3c_adc_bat_get_property(struct power_supply *psy,
if (bat->volt_value < 0 || bat->cur_value < 0 ||
jiffies_to_msecs(jiffies - bat->timestamp) >
BAT_POLL_INTERVAL) {
bat->volt_value = s3c_adc_read(bat->client,
bat->volt_value = gather_samples(bat->client,
bat->pdata->volt_samples,
bat->pdata->volt_channel) * bat->pdata->volt_mult;
bat->cur_value = s3c_adc_read(bat->client,
bat->cur_value = gather_samples(bat->client,
bat->pdata->current_samples,
bat->pdata->current_channel) * bat->pdata->current_mult;
bat->timestamp = jiffies;
}
......@@ -421,17 +440,7 @@ static struct platform_driver s3c_adc_bat_driver = {
.resume = s3c_adc_bat_resume,
};
static int __init s3c_adc_bat_init(void)
{
return platform_driver_register(&s3c_adc_bat_driver);
}
module_init(s3c_adc_bat_init);
static void __exit s3c_adc_bat_exit(void)
{
platform_driver_unregister(&s3c_adc_bat_driver);
}
module_exit(s3c_adc_bat_exit);
module_platform_driver(s3c_adc_bat_driver);
MODULE_AUTHOR("Vasily Khoruzhick <anarsoul@gmail.com>");
MODULE_DESCRIPTION("iPAQ H1930/H1940/RX1950 battery controller driver");
......
/*
* 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 @@ static int bq20z75_get_property(struct power_supply *psy,
return 0;
}
static irqreturn_t bq20z75_irq(int irq, void *devid)
static irqreturn_t sbs_irq(int irq, void *devid)
{
struct power_supply *battery = devid;
......@@ -559,36 +547,35 @@ static irqreturn_t bq20z75_irq(int irq, void *devid)
return IRQ_HANDLED;
}
static void bq20z75_external_power_changed(struct power_supply *psy)
static void sbs_external_power_changed(struct power_supply *psy)
{
struct bq20z75_info *bq20z75_device;
struct sbs_info *chip;
bq20z75_device = container_of(psy, struct bq20z75_info, power_supply);
chip = container_of(psy, struct sbs_info, power_supply);
if (bq20z75_device->ignore_changes > 0) {
bq20z75_device->ignore_changes--;
if (chip->ignore_changes > 0) {
chip->ignore_changes--;
return;
}
/* cancel outstanding work */
cancel_delayed_work_sync(&bq20z75_device->work);
cancel_delayed_work_sync(&chip->work);
schedule_delayed_work(&bq20z75_device->work, HZ);
bq20z75_device->poll_time = bq20z75_device->pdata->poll_retry_count;
schedule_delayed_work(&chip->work, HZ);
chip->poll_time = chip->pdata->poll_retry_count;
}
static void bq20z75_delayed_work(struct work_struct *work)
static void sbs_delayed_work(struct work_struct *work)
{
struct bq20z75_info *bq20z75_device;
struct sbs_info *chip;
s32 ret;
bq20z75_device = container_of(work, struct bq20z75_info, work.work);
chip = container_of(work, struct sbs_info, work.work);
ret = bq20z75_read_word_data(bq20z75_device->client,
bq20z75_data[REG_STATUS].addr);
ret = sbs_read_word_data(chip->client, sbs_data[REG_STATUS].addr);
/* if the read failed, give up on this work */
if (ret < 0) {
bq20z75_device->poll_time = 0;
chip->poll_time = 0;
return;
}
......@@ -601,62 +588,145 @@ static void bq20z75_delayed_work(struct work_struct *work)
else
ret = POWER_SUPPLY_STATUS_CHARGING;
if (bq20z75_device->last_state != ret) {
bq20z75_device->poll_time = 0;
power_supply_changed(&bq20z75_device->power_supply);
if (chip->last_state != ret) {
chip->poll_time = 0;
power_supply_changed(&chip->power_supply);
return;
}
if (bq20z75_device->poll_time > 0) {
schedule_delayed_work(&bq20z75_device->work, HZ);
bq20z75_device->poll_time--;
if (chip->poll_time > 0) {
schedule_delayed_work(&chip->work, HZ);
chip->poll_time--;
return;
}
}
static int __devinit bq20z75_probe(struct i2c_client *client,
#if defined(CONFIG_OF)
#include <linux/of_device.h>
#include <linux/of_gpio.h>
static const struct of_device_id sbs_dt_ids[] = {
{ .compatible = "sbs,sbs-battery" },
{ .compatible = "ti,bq20z75" },
{ }
};
MODULE_DEVICE_TABLE(of, sbs_dt_ids);
static struct sbs_platform_data *sbs_of_populate_pdata(
struct i2c_client *client)
{
struct device_node *of_node = client->dev.of_node;
struct sbs_platform_data *pdata = client->dev.platform_data;
enum of_gpio_flags gpio_flags;
int rc;
u32 prop;
/* verify this driver matches this device */
if (!of_node)
return NULL;
/* if platform data is set, honor it */
if (pdata)
return pdata;
/* first make sure at least one property is set, otherwise
* it won't change behavior from running without pdata.
*/
if (!of_get_property(of_node, "sbs,i2c-retry-count", NULL) &&
!of_get_property(of_node, "sbs,poll-retry-count", NULL) &&
!of_get_property(of_node, "sbs,battery-detect-gpios", NULL))
goto of_out;
pdata = devm_kzalloc(&client->dev, sizeof(struct sbs_platform_data),
GFP_KERNEL);
if (!pdata)
goto of_out;
rc = of_property_read_u32(of_node, "sbs,i2c-retry-count", &prop);
if (!rc)
pdata->i2c_retry_count = prop;
rc = of_property_read_u32(of_node, "sbs,poll-retry-count", &prop);
if (!rc)
pdata->poll_retry_count = prop;
if (!of_get_property(of_node, "sbs,battery-detect-gpios", NULL)) {
pdata->battery_detect = -1;
goto of_out;
}
pdata->battery_detect = of_get_named_gpio_flags(of_node,
"sbs,battery-detect-gpios", 0, &gpio_flags);
if (gpio_flags & OF_GPIO_ACTIVE_LOW)
pdata->battery_detect_present = 0;
else
pdata->battery_detect_present = 1;
of_out:
return pdata;
}
#else
#define sbs_dt_ids NULL
static struct sbs_platform_data *sbs_of_populate_pdata(
struct i2c_client *client)
{
return client->dev.platform_data;
}
#endif
static int __devinit sbs_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct bq20z75_info *bq20z75_device;
struct bq20z75_platform_data *pdata = client->dev.platform_data;
struct sbs_info *chip;
struct sbs_platform_data *pdata = client->dev.platform_data;
int rc;
int irq;
char *name;
bq20z75_device = kzalloc(sizeof(struct bq20z75_info), GFP_KERNEL);
if (!bq20z75_device)
name = kasprintf(GFP_KERNEL, "sbs-%s", dev_name(&client->dev));
if (!name) {
dev_err(&client->dev, "Failed to allocate device name\n");
return -ENOMEM;
}
chip = kzalloc(sizeof(struct sbs_info), GFP_KERNEL);
if (!chip) {
rc = -ENOMEM;
goto exit_free_name;
}
bq20z75_device->client = client;
bq20z75_device->enable_detection = false;
bq20z75_device->gpio_detect = false;
bq20z75_device->power_supply.name = "battery";
bq20z75_device->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
bq20z75_device->power_supply.properties = bq20z75_properties;
bq20z75_device->power_supply.num_properties =
ARRAY_SIZE(bq20z75_properties);
bq20z75_device->power_supply.get_property = bq20z75_get_property;
chip->client = client;
chip->enable_detection = false;
chip->gpio_detect = false;
chip->power_supply.name = name;
chip->power_supply.type = POWER_SUPPLY_TYPE_BATTERY;
chip->power_supply.properties = sbs_properties;
chip->power_supply.num_properties = ARRAY_SIZE(sbs_properties);
chip->power_supply.get_property = sbs_get_property;
/* ignore first notification of external change, it is generated
* from the power_supply_register call back
*/
bq20z75_device->ignore_changes = 1;
bq20z75_device->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
bq20z75_device->power_supply.external_power_changed =
bq20z75_external_power_changed;
chip->ignore_changes = 1;
chip->last_state = POWER_SUPPLY_STATUS_UNKNOWN;
chip->power_supply.external_power_changed = sbs_external_power_changed;
pdata = sbs_of_populate_pdata(client);
if (pdata) {
bq20z75_device->gpio_detect =
gpio_is_valid(pdata->battery_detect);
bq20z75_device->pdata = pdata;
chip->gpio_detect = gpio_is_valid(pdata->battery_detect);
chip->pdata = pdata;
}
i2c_set_clientdata(client, bq20z75_device);
i2c_set_clientdata(client, chip);
if (!bq20z75_device->gpio_detect)
if (!chip->gpio_detect)
goto skip_gpio;
rc = gpio_request(pdata->battery_detect, dev_name(&client->dev));
if (rc) {
dev_warn(&client->dev, "Failed to request gpio: %d\n", rc);
bq20z75_device->gpio_detect = false;
chip->gpio_detect = false;
goto skip_gpio;
}
......@@ -664,7 +734,7 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
if (rc) {
dev_warn(&client->dev, "Failed to get gpio as input: %d\n", rc);
gpio_free(pdata->battery_detect);
bq20z75_device->gpio_detect = false;
chip->gpio_detect = false;
goto skip_gpio;
}
......@@ -672,25 +742,25 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
if (irq <= 0) {
dev_warn(&client->dev, "Failed to get gpio as irq: %d\n", irq);
gpio_free(pdata->battery_detect);
bq20z75_device->gpio_detect = false;
chip->gpio_detect = false;
goto skip_gpio;
}
rc = request_irq(irq, bq20z75_irq,
rc = request_irq(irq, sbs_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
dev_name(&client->dev), &bq20z75_device->power_supply);
dev_name(&client->dev), &chip->power_supply);
if (rc) {
dev_warn(&client->dev, "Failed to request irq: %d\n", rc);
gpio_free(pdata->battery_detect);
bq20z75_device->gpio_detect = false;
chip->gpio_detect = false;
goto skip_gpio;
}
bq20z75_device->irq = irq;
chip->irq = irq;
skip_gpio:
rc = power_supply_register(&client->dev, &bq20z75_device->power_supply);
rc = power_supply_register(&client->dev, &chip->power_supply);
if (rc) {
dev_err(&client->dev,
"%s: Failed to register power supply\n", __func__);
......@@ -700,95 +770,100 @@ static int __devinit bq20z75_probe(struct i2c_client *client,
dev_info(&client->dev,
"%s: battery gas gauge device registered\n", client->name);
INIT_DELAYED_WORK(&bq20z75_device->work, bq20z75_delayed_work);
INIT_DELAYED_WORK(&chip->work, sbs_delayed_work);
bq20z75_device->enable_detection = true;
chip->enable_detection = true;
return 0;
exit_psupply:
if (bq20z75_device->irq)
free_irq(bq20z75_device->irq, &bq20z75_device->power_supply);
if (bq20z75_device->gpio_detect)
if (chip->irq)
free_irq(chip->irq, &chip->power_supply);
if (chip->gpio_detect)
gpio_free(pdata->battery_detect);
kfree(bq20z75_device);
kfree(chip);
exit_free_name:
kfree(name);
return rc;
}
static int __devexit bq20z75_remove(struct i2c_client *client)
static int __devexit sbs_remove(struct i2c_client *client)
{
struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
struct sbs_info *chip = i2c_get_clientdata(client);
if (bq20z75_device->irq)
free_irq(bq20z75_device->irq, &bq20z75_device->power_supply);
if (bq20z75_device->gpio_detect)
gpio_free(bq20z75_device->pdata->battery_detect);
if (chip->irq)
free_irq(chip->irq, &chip->power_supply);
if (chip->gpio_detect)
gpio_free(chip->pdata->battery_detect);
power_supply_unregister(&bq20z75_device->power_supply);
power_supply_unregister(&chip->power_supply);
cancel_delayed_work_sync(&bq20z75_device->work);
cancel_delayed_work_sync(&chip->work);
kfree(bq20z75_device);
bq20z75_device = NULL;
kfree(chip->power_supply.name);
kfree(chip);
chip = NULL;
return 0;
}
#if defined CONFIG_PM
static int bq20z75_suspend(struct i2c_client *client,
static int sbs_suspend(struct i2c_client *client,
pm_message_t state)
{
struct bq20z75_info *bq20z75_device = i2c_get_clientdata(client);
struct sbs_info *chip = i2c_get_clientdata(client);
s32 ret;
if (bq20z75_device->poll_time > 0)
cancel_delayed_work_sync(&bq20z75_device->work);
if (chip->poll_time > 0)
cancel_delayed_work_sync(&chip->work);
/* write to manufacturer access with sleep command */
ret = bq20z75_write_word_data(client,
bq20z75_data[REG_MANUFACTURER_DATA].addr,
ret = sbs_write_word_data(client, sbs_data[REG_MANUFACTURER_DATA].addr,
MANUFACTURER_ACCESS_SLEEP);
if (bq20z75_device->is_present && ret < 0)
if (chip->is_present && ret < 0)
return ret;
return 0;
}
#else
#define bq20z75_suspend NULL
#define sbs_suspend NULL
#endif
/* any smbus transaction will wake up bq20z75 */
#define bq20z75_resume NULL
/* any smbus transaction will wake up sbs */
#define sbs_resume NULL
static const struct i2c_device_id bq20z75_id[] = {
static const struct i2c_device_id sbs_id[] = {
{ "bq20z75", 0 },
{ "sbs-battery", 1 },
{}
};
MODULE_DEVICE_TABLE(i2c, bq20z75_id);
static struct i2c_driver bq20z75_battery_driver = {
.probe = bq20z75_probe,
.remove = __devexit_p(bq20z75_remove),
.suspend = bq20z75_suspend,
.resume = bq20z75_resume,
.id_table = bq20z75_id,
MODULE_DEVICE_TABLE(i2c, sbs_id);
static struct i2c_driver sbs_battery_driver = {
.probe = sbs_probe,
.remove = __devexit_p(sbs_remove),
.suspend = sbs_suspend,
.resume = sbs_resume,
.id_table = sbs_id,
.driver = {
.name = "bq20z75-battery",
.name = "sbs-battery",
.of_match_table = sbs_dt_ids,
},
};
static int __init bq20z75_battery_init(void)
static int __init sbs_battery_init(void)
{
return i2c_add_driver(&bq20z75_battery_driver);
return i2c_add_driver(&sbs_battery_driver);
}
module_init(bq20z75_battery_init);
module_init(sbs_battery_init);
static void __exit bq20z75_battery_exit(void)
static void __exit sbs_battery_exit(void)
{
i2c_del_driver(&bq20z75_battery_driver);
i2c_del_driver(&sbs_battery_driver);
}
module_exit(bq20z75_battery_exit);
module_exit(sbs_battery_exit);
MODULE_DESCRIPTION("BQ20z75 battery monitor driver");
MODULE_DESCRIPTION("SBS battery monitor driver");
MODULE_LICENSE("GPL");
......@@ -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 @@ static int __devinit tosa_bat_probe(struct platform_device *dev)
/* 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");
......
......@@ -226,17 +226,7 @@ static struct platform_driver wm831x_backup_driver = {
},
};
static int __init wm831x_backup_init(void)
{
return platform_driver_register(&wm831x_backup_driver);
}
module_init(wm831x_backup_init);
static void __exit wm831x_backup_exit(void)
{
platform_driver_unregister(&wm831x_backup_driver);
}
module_exit(wm831x_backup_exit);
module_platform_driver(wm831x_backup_driver);
MODULE_DESCRIPTION("Backup battery charger driver for WM831x PMICs");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
......
......@@ -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 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
err_syslo:
irq = platform_get_irq_byname(pdev, "SYSLO");
free_irq(irq, power);
err_battery:
if (power->have_battery)
power_supply_unregister(battery);
err_usb:
power_supply_unregister(usb);
err_battery:
power_supply_unregister(battery);
err_wall:
power_supply_unregister(wall);
err_kmalloc:
......@@ -628,7 +639,8 @@ static __devexit int wm831x_power_remove(struct platform_device *pdev)
irq = platform_get_irq_byname(pdev, "SYSLO");
free_irq(irq, wm831x_power);
power_supply_unregister(&wm831x_power->battery);
if (wm831x_power->have_battery)
power_supply_unregister(&wm831x_power->battery);
power_supply_unregister(&wm831x_power->wall);
power_supply_unregister(&wm831x_power->usb);
kfree(wm831x_power);
......@@ -643,17 +655,7 @@ static struct platform_driver wm831x_power_driver = {
},
};
static int __init wm831x_power_init(void)
{
return platform_driver_register(&wm831x_power_driver);
}
module_init(wm831x_power_init);
static void __exit wm831x_power_exit(void)
{
platform_driver_unregister(&wm831x_power_driver);
}
module_exit(wm831x_power_exit);
module_platform_driver(wm831x_power_driver);
MODULE_DESCRIPTION("Power supply driver for WM831x PMICs");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
......
......@@ -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");
......
......@@ -25,9 +25,8 @@
#include <linux/irq.h>
#include <linux/slab.h>
static DEFINE_MUTEX(bat_lock);
static struct work_struct bat_work;
static struct mutex work_lock;
static DEFINE_MUTEX(work_lock);
static int bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
static enum power_supply_property *prop;
......@@ -181,8 +180,6 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
if (dev->id != -1)
return -EINVAL;
mutex_init(&work_lock);
if (!pdata) {
dev_err(&dev->dev, "No platform_data supplied\n");
return -EINVAL;
......@@ -196,7 +193,7 @@ static int __devinit wm97xx_bat_probe(struct platform_device *dev)
if (ret)
goto err2;
ret = request_irq(gpio_to_irq(pdata->charge_gpio),
wm97xx_chrg_irq, IRQF_DISABLED,
wm97xx_chrg_irq, 0,
"AC Detect", dev);
if (ret)
goto err2;
......@@ -291,18 +288,7 @@ static struct platform_driver wm97xx_bat_driver = {
.remove = __devexit_p(wm97xx_bat_remove),
};
static int __init wm97xx_bat_init(void)
{
return platform_driver_register(&wm97xx_bat_driver);
}
static void __exit wm97xx_bat_exit(void)
{
platform_driver_unregister(&wm97xx_bat_driver);
}
module_init(wm97xx_bat_init);
module_exit(wm97xx_bat_exit);
module_platform_driver(wm97xx_bat_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
......
......@@ -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,
};
......
/*
* Copyright (C) 2011 National Semiconductor
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef _LP8727_H
#define _LP8727_H
enum lp8727_eoc_level {
EOC_5P,
EOC_10P,
EOC_16P,
EOC_20P,
EOC_25P,
EOC_33P,
EOC_50P,
};
enum lp8727_ichg {
ICHG_90mA,
ICHG_100mA,
ICHG_400mA,
ICHG_450mA,
ICHG_500mA,
ICHG_600mA,
ICHG_700mA,
ICHG_800mA,
ICHG_900mA,
ICHG_1000mA,
};
struct lp8727_chg_param {
/* end of charge level setting */
enum lp8727_eoc_level eoc_level;
/* charging current */
enum lp8727_ichg ichg;
};
struct lp8727_platform_data {
u8 (*get_batt_present)(void);
u16 (*get_batt_level)(void);
u8 (*get_batt_capacity)(void);
u8 (*get_batt_temp)(void);
struct lp8727_chg_param ac;
struct lp8727_chg_param usb;
};
#endif
......@@ -167,9 +167,6 @@ enum {
MAX8925_IRQ_VCHG_DC_OVP,
MAX8925_IRQ_VCHG_DC_F,
MAX8925_IRQ_VCHG_DC_R,
MAX8925_IRQ_VCHG_USB_OVP,
MAX8925_IRQ_VCHG_USB_F,
MAX8925_IRQ_VCHG_USB_R,
MAX8925_IRQ_VCHG_THM_OK_R,
MAX8925_IRQ_VCHG_THM_OK_F,
MAX8925_IRQ_VCHG_SYSLOW_F,
......@@ -223,6 +220,10 @@ struct max8925_power_pdata {
unsigned batt_detect:1;
unsigned topoff_threshold:2;
unsigned fast_charge:3; /* charge current */
unsigned no_temp_support:1; /* set if no temperature detect */
unsigned no_insert_detect:1; /* set if no ac insert detect */
char **supplied_to;
int num_supplicants;
};
/*
......
......@@ -35,6 +35,8 @@ struct pda_power_pdata {
unsigned int polling_interval; /* msecs, default is 2000 */
unsigned long ac_max_uA; /* current to draw when on AC */
bool use_otg_notifier;
};
#endif /* __PDA_POWER_H__ */
/*
* Copyright (C) 2011 Samsung Electronics Co., Ltd.
* MyungJoo.Ham <myungjoo.ham@samsung.com>
*
* Charger Manager.
* This framework enables to control and multiple chargers and to
* monitor charging even in the context of suspend-to-RAM with
* an interface combining the chargers.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
**/
#ifndef _CHARGER_MANAGER_H
#define _CHARGER_MANAGER_H
#include <linux/power_supply.h>
enum data_source {
CM_FUEL_GAUGE,
CM_CHARGER_STAT,
};
enum polling_modes {
CM_POLL_DISABLE = 0,
CM_POLL_ALWAYS,
CM_POLL_EXTERNAL_POWER_ONLY,
CM_POLL_CHARGING_ONLY,
};
/**
* struct charger_global_desc
* @rtc_name: the name of RTC used to wake up the system from suspend.
* @rtc_only_wakeup:
* If the system is woken up by waekup-sources other than the RTC or
* callbacks, Charger Manager should recognize with
* rtc_only_wakeup() returning false.
* If the RTC given to CM is the only wakeup reason,
* rtc_only_wakeup should return true.
*/
struct charger_global_desc {
char *rtc_name;
bool (*rtc_only_wakeup)(void);
};
/**
* struct charger_desc
* @psy_name: the name of power-supply-class for charger manager
* @polling_mode:
* Determine which polling mode will be used
* @fullbatt_uV: voltage in microvolt
* If it is not being charged and VBATT >= fullbatt_uV,
* it is assumed to be full.
* @polling_interval_ms: interval in millisecond at which
* charger manager will monitor battery health
* @battery_present:
* Specify where information for existance of battery can be obtained
* @psy_charger_stat: the names of power-supply for chargers
* @num_charger_regulator: the number of entries in charger_regulators
* @charger_regulators: array of regulator_bulk_data for chargers
* @psy_fuel_gauge: the name of power-supply for fuel gauge
* @temperature_out_of_range:
* Determine whether the status is overheat or cold or normal.
* return_value > 0: overheat
* return_value == 0: normal
* return_value < 0: cold
* @measure_battery_temp:
* true: measure battery temperature
* false: measure ambient temperature
*/
struct charger_desc {
char *psy_name;
enum polling_modes polling_mode;
unsigned int polling_interval_ms;
unsigned int fullbatt_uV;
enum data_source battery_present;
char **psy_charger_stat;
int num_charger_regulators;
struct regulator_bulk_data *charger_regulators;
char *psy_fuel_gauge;
int (*temperature_out_of_range)(int *mC);
bool measure_battery_temp;
};
#define PSY_NAME_MAX 30
/**
* struct charger_manager
* @entry: entry for list
* @dev: device pointer
* @desc: instance of charger_desc
* @fuel_gauge: power_supply for fuel gauge
* @charger_stat: array of power_supply for chargers
* @charger_enabled: the state of charger
* @emergency_stop:
* When setting true, stop charging
* @last_temp_mC: the measured temperature in milli-Celsius
* @psy_name_buf: the name of power-supply-class for charger manager
* @charger_psy: power_supply for charger manager
* @status_save_ext_pwr_inserted:
* saved status of external power before entering suspend-to-RAM
* @status_save_batt:
* saved status of battery before entering suspend-to-RAM
*/
struct charger_manager {
struct list_head entry;
struct device *dev;
struct charger_desc *desc;
struct power_supply *fuel_gauge;
struct power_supply **charger_stat;
bool charger_enabled;
int emergency_stop;
int last_temp_mC;
char psy_name_buf[PSY_NAME_MAX + 1];
struct power_supply charger_psy;
bool status_save_ext_pwr_inserted;
bool status_save_batt;
};
#ifdef CONFIG_CHARGER_MANAGER
extern int setup_charger_manager(struct charger_global_desc *gd);
extern bool cm_suspend_again(void);
#else
static void __maybe_unused setup_charger_manager(struct charger_global_desc *gd)
{ }
static bool __maybe_unused cm_suspend_again(void)
{
return false;
}
#endif
#endif /* _CHARGER_MANAGER_H */
/*
* Gas Gauge driver for TI's BQ20Z75
* Gas Gauge driver for SBS Compliant Gas Gauges
*
* Copyright (c) 2010, NVIDIA Corporation.
*
......@@ -18,21 +18,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __LINUX_POWER_BQ20Z75_H_
#define __LINUX_POWER_BQ20Z75_H_
#ifndef __LINUX_POWER_SBS_BATTERY_H_
#define __LINUX_POWER_SBS_BATTERY_H_
#include <linux/power_supply.h>
#include <linux/types.h>
/**
* struct bq20z75_platform_data - platform data for bq20z75 devices
* struct sbs_platform_data - platform data for sbs devices
* @battery_detect: GPIO which is used to detect battery presence
* @battery_detect_present: gpio state when battery is present (0 / 1)
* @i2c_retry_count: # of times to retry on i2c IO failure
* @poll_retry_count: # of times to retry looking for new status after
* external change notification
*/
struct bq20z75_platform_data {
struct sbs_platform_data {
int battery_detect;
int battery_detect_present;
int i2c_retry_count;
......
......@@ -74,6 +74,12 @@ enum {
POWER_SUPPLY_CAPACITY_LEVEL_FULL,
};
enum {
POWER_SUPPLY_SCOPE_UNKNOWN = 0,
POWER_SUPPLY_SCOPE_SYSTEM,
POWER_SUPPLY_SCOPE_DEVICE,
};
enum power_supply_property {
/* Properties of type `int' */
POWER_SUPPLY_PROP_STATUS = 0,
......@@ -116,6 +122,7 @@ enum power_supply_property {
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
POWER_SUPPLY_PROP_SCOPE,
/* Properties of type `const char *' */
POWER_SUPPLY_PROP_MODEL_NAME,
POWER_SUPPLY_PROP_MANUFACTURER,
......@@ -123,7 +130,8 @@ enum power_supply_property {
};
enum power_supply_type {
POWER_SUPPLY_TYPE_BATTERY = 0,
POWER_SUPPLY_TYPE_UNKNOWN = 0,
POWER_SUPPLY_TYPE_BATTERY,
POWER_SUPPLY_TYPE_UPS,
POWER_SUPPLY_TYPE_MAINS,
POWER_SUPPLY_TYPE_USB, /* Standard Downstream Port */
......@@ -211,6 +219,7 @@ static inline int power_supply_is_system_supplied(void) { return -ENOSYS; }
extern int power_supply_register(struct device *parent,
struct power_supply *psy);
extern void power_supply_unregister(struct power_supply *psy);
extern int power_supply_powers(struct power_supply *psy, struct device *dev);
/* For APM emulation, think legacy userspace. */
extern struct class *power_supply_class;
......
......@@ -25,6 +25,10 @@ struct s3c_adc_bat_pdata {
const unsigned int current_channel;
const unsigned int backup_volt_channel;
const unsigned int volt_samples;
const unsigned int current_samples;
const unsigned int backup_volt_samples;
const unsigned int volt_mult;
const unsigned int current_mult;
const unsigned int backup_volt_mult;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment