mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-06 19:07:52 +00:00
Merge git://git.infradead.org/battery-2.6
* git://git.infradead.org/battery-2.6: ds2760_battery: Document ABI change ds2760_battery: Make charge_now and charge_full writeable power_supply: Add support for writeable properties power_supply: Use attribute groups power_supply: Add test_power driver tosa_battery: Fix build error due to direct driver_data usage wm97xx_battery: Quieten sparse warning (bat_set_pdata not declared) ds2782_battery: Get rid of magic numbers in driver_data ds2782_battery: Add support for ds2786 battery gas gauge pda_power: Add function callbacks for suspend and resume wm831x_power: Use genirq Driver for Zipit Z2 battery chip ds2782_battery: Fix clientdata on removal
This commit is contained in:
commit
15953654cc
18 changed files with 905 additions and 172 deletions
20
Documentation/ABI/testing/sysfs-class-power
Normal file
20
Documentation/ABI/testing/sysfs-class-power
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
What: /sys/class/power/ds2760-battery.*/charge_now
|
||||||
|
Date: May 2010
|
||||||
|
KernelVersion: 2.6.35
|
||||||
|
Contact: Daniel Mack <daniel@caiaq.de>
|
||||||
|
Description:
|
||||||
|
This file is writeable and can be used to set the current
|
||||||
|
coloumb counter value inside the battery monitor chip. This
|
||||||
|
is needed for unavoidable corrections of aging batteries.
|
||||||
|
A userspace daemon can monitor the battery charging logic
|
||||||
|
and once the counter drops out of considerable bounds, take
|
||||||
|
appropriate action.
|
||||||
|
|
||||||
|
What: /sys/class/power/ds2760-battery.*/charge_full
|
||||||
|
Date: May 2010
|
||||||
|
KernelVersion: 2.6.35
|
||||||
|
Contact: Daniel Mack <daniel@caiaq.de>
|
||||||
|
Description:
|
||||||
|
This file is writeable and can be used to set the assumed
|
||||||
|
battery 'full level'. As batteries age, this value has to be
|
||||||
|
amended over time.
|
|
@ -57,6 +57,11 @@ config WM8350_POWER
|
||||||
Say Y here to enable support for the power management unit
|
Say Y here to enable support for the power management unit
|
||||||
provided by the Wolfson Microelectronics WM8350 PMIC.
|
provided by the Wolfson Microelectronics WM8350 PMIC.
|
||||||
|
|
||||||
|
config TEST_POWER
|
||||||
|
tristate "Test power driver"
|
||||||
|
help
|
||||||
|
This driver is used for testing. It's safe to say M here.
|
||||||
|
|
||||||
config BATTERY_DS2760
|
config BATTERY_DS2760
|
||||||
tristate "DS2760 battery driver (HP iPAQ & others)"
|
tristate "DS2760 battery driver (HP iPAQ & others)"
|
||||||
select W1
|
select W1
|
||||||
|
@ -65,10 +70,10 @@ config BATTERY_DS2760
|
||||||
Say Y here to enable support for batteries with ds2760 chip.
|
Say Y here to enable support for batteries with ds2760 chip.
|
||||||
|
|
||||||
config BATTERY_DS2782
|
config BATTERY_DS2782
|
||||||
tristate "DS2782 standalone gas-gauge"
|
tristate "DS2782/DS2786 standalone gas-gauge"
|
||||||
depends on I2C
|
depends on I2C
|
||||||
help
|
help
|
||||||
Say Y here to enable support for the DS2782 standalone battery
|
Say Y here to enable support for the DS2782/DS2786 standalone battery
|
||||||
gas-gauge.
|
gas-gauge.
|
||||||
|
|
||||||
config BATTERY_PMU
|
config BATTERY_PMU
|
||||||
|
@ -125,6 +130,12 @@ config BATTERY_MAX17040
|
||||||
in handheld and portable equipment. The MAX17040 is configured
|
in handheld and portable equipment. The MAX17040 is configured
|
||||||
to operate with a single lithium cell
|
to operate with a single lithium cell
|
||||||
|
|
||||||
|
config BATTERY_Z2
|
||||||
|
tristate "Z2 battery driver"
|
||||||
|
depends on I2C && MACH_ZIPIT2
|
||||||
|
help
|
||||||
|
Say Y to include support for the battery on the Zipit Z2.
|
||||||
|
|
||||||
config CHARGER_PCF50633
|
config CHARGER_PCF50633
|
||||||
tristate "NXP PCF50633 MBC"
|
tristate "NXP PCF50633 MBC"
|
||||||
depends on MFD_PCF50633
|
depends on MFD_PCF50633
|
||||||
|
|
|
@ -20,6 +20,7 @@ obj-$(CONFIG_MAX8925_POWER) += max8925_power.o
|
||||||
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
|
obj-$(CONFIG_WM831X_BACKUP) += wm831x_backup.o
|
||||||
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
|
obj-$(CONFIG_WM831X_POWER) += wm831x_power.o
|
||||||
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
|
obj-$(CONFIG_WM8350_POWER) += wm8350_power.o
|
||||||
|
obj-$(CONFIG_TEST_POWER) += test_power.o
|
||||||
|
|
||||||
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
|
||||||
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o
|
||||||
|
@ -31,4 +32,5 @@ obj-$(CONFIG_BATTERY_WM97XX) += wm97xx_battery.o
|
||||||
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
|
obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o
|
||||||
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
obj-$(CONFIG_BATTERY_DA9030) += da9030_battery.o
|
||||||
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
|
obj-$(CONFIG_BATTERY_MAX17040) += max17040_battery.o
|
||||||
|
obj-$(CONFIG_BATTERY_Z2) += z2_battery.o
|
||||||
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o
|
||||||
|
|
|
@ -304,6 +304,28 @@ static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
|
||||||
w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
|
w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ds2760_battery_write_active_full(struct ds2760_device_info *di,
|
||||||
|
int active_full)
|
||||||
|
{
|
||||||
|
unsigned char tmp[2] = {
|
||||||
|
active_full >> 8,
|
||||||
|
active_full & 0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tmp[0] == di->raw[DS2760_ACTIVE_FULL] &&
|
||||||
|
tmp[1] == di->raw[DS2760_ACTIVE_FULL + 1])
|
||||||
|
return;
|
||||||
|
|
||||||
|
w1_ds2760_write(di->w1_dev, tmp, DS2760_ACTIVE_FULL, sizeof(tmp));
|
||||||
|
w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
|
||||||
|
w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK0);
|
||||||
|
|
||||||
|
/* Write to the di->raw[] buffer directly - the DS2760_ACTIVE_FULL
|
||||||
|
* values won't be read back by ds2760_battery_read_status() */
|
||||||
|
di->raw[DS2760_ACTIVE_FULL] = tmp[0];
|
||||||
|
di->raw[DS2760_ACTIVE_FULL + 1] = tmp[1];
|
||||||
|
}
|
||||||
|
|
||||||
static void ds2760_battery_work(struct work_struct *work)
|
static void ds2760_battery_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct ds2760_device_info *di = container_of(work,
|
struct ds2760_device_info *di = container_of(work,
|
||||||
|
@ -426,6 +448,45 @@ static int ds2760_battery_get_property(struct power_supply *psy,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ds2760_battery_set_property(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
const union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct ds2760_device_info *di = to_ds2760_device_info(psy);
|
||||||
|
|
||||||
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||||
|
/* the interface counts in uAh, convert the value */
|
||||||
|
ds2760_battery_write_active_full(di, val->intval / 1000L);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||||
|
/* ds2760_battery_set_current_accum() does the conversion */
|
||||||
|
ds2760_battery_set_current_accum(di, val->intval);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds2760_battery_property_is_writeable(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp)
|
||||||
|
{
|
||||||
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_NOW:
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static enum power_supply_property ds2760_battery_props[] = {
|
static enum power_supply_property ds2760_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_STATUS,
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
|
@ -460,6 +521,9 @@ static int ds2760_battery_probe(struct platform_device *pdev)
|
||||||
di->bat.properties = ds2760_battery_props;
|
di->bat.properties = ds2760_battery_props;
|
||||||
di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props);
|
di->bat.num_properties = ARRAY_SIZE(ds2760_battery_props);
|
||||||
di->bat.get_property = ds2760_battery_get_property;
|
di->bat.get_property = ds2760_battery_get_property;
|
||||||
|
di->bat.set_property = ds2760_battery_set_property;
|
||||||
|
di->bat.property_is_writeable =
|
||||||
|
ds2760_battery_property_is_writeable;
|
||||||
di->bat.set_charged = ds2760_battery_set_charged;
|
di->bat.set_charged = ds2760_battery_set_charged;
|
||||||
di->bat.external_power_changed =
|
di->bat.external_power_changed =
|
||||||
ds2760_battery_external_power_changed;
|
ds2760_battery_external_power_changed;
|
||||||
|
|
|
@ -5,6 +5,8 @@
|
||||||
*
|
*
|
||||||
* Author: Ryan Mallon <ryan@bluewatersys.com>
|
* Author: Ryan Mallon <ryan@bluewatersys.com>
|
||||||
*
|
*
|
||||||
|
* DS2786 added by Yulia Vilensky <vilensky@compulab.co.il>
|
||||||
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
|
@ -20,12 +22,13 @@
|
||||||
#include <linux/idr.h>
|
#include <linux/idr.h>
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/ds2782_battery.h>
|
||||||
|
|
||||||
#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */
|
#define DS2782_REG_RARC 0x06 /* Remaining active relative capacity */
|
||||||
|
|
||||||
#define DS2782_REG_VOLT_MSB 0x0c
|
#define DS278x_REG_VOLT_MSB 0x0c
|
||||||
#define DS2782_REG_TEMP_MSB 0x0a
|
#define DS278x_REG_TEMP_MSB 0x0a
|
||||||
#define DS2782_REG_CURRENT_MSB 0x0e
|
#define DS278x_REG_CURRENT_MSB 0x0e
|
||||||
|
|
||||||
/* EEPROM Block */
|
/* EEPROM Block */
|
||||||
#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */
|
#define DS2782_REG_RSNSP 0x69 /* Sense resistor value */
|
||||||
|
@ -33,18 +36,33 @@
|
||||||
/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
|
/* Current unit measurement in uA for a 1 milli-ohm sense resistor */
|
||||||
#define DS2782_CURRENT_UNITS 1563
|
#define DS2782_CURRENT_UNITS 1563
|
||||||
|
|
||||||
#define to_ds2782_info(x) container_of(x, struct ds2782_info, battery)
|
#define DS2786_REG_RARC 0x02 /* Remaining active relative capacity */
|
||||||
|
|
||||||
struct ds2782_info {
|
#define DS2786_CURRENT_UNITS 25
|
||||||
|
|
||||||
|
struct ds278x_info;
|
||||||
|
|
||||||
|
struct ds278x_battery_ops {
|
||||||
|
int (*get_current)(struct ds278x_info *info, int *current_uA);
|
||||||
|
int (*get_voltage)(struct ds278x_info *info, int *voltage_uA);
|
||||||
|
int (*get_capacity)(struct ds278x_info *info, int *capacity_uA);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#define to_ds278x_info(x) container_of(x, struct ds278x_info, battery)
|
||||||
|
|
||||||
|
struct ds278x_info {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct power_supply battery;
|
struct power_supply battery;
|
||||||
|
struct ds278x_battery_ops *ops;
|
||||||
int id;
|
int id;
|
||||||
|
int rsns;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DEFINE_IDR(battery_id);
|
static DEFINE_IDR(battery_id);
|
||||||
static DEFINE_MUTEX(battery_lock);
|
static DEFINE_MUTEX(battery_lock);
|
||||||
|
|
||||||
static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val)
|
static inline int ds278x_read_reg(struct ds278x_info *info, int reg, u8 *val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -58,7 +76,7 @@ static inline int ds2782_read_reg(struct ds2782_info *info, int reg, u8 *val)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb,
|
static inline int ds278x_read_reg16(struct ds278x_info *info, int reg_msb,
|
||||||
s16 *val)
|
s16 *val)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -73,7 +91,7 @@ static inline int ds2782_read_reg16(struct ds2782_info *info, int reg_msb,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_get_temp(struct ds2782_info *info, int *temp)
|
static int ds278x_get_temp(struct ds278x_info *info, int *temp)
|
||||||
{
|
{
|
||||||
s16 raw;
|
s16 raw;
|
||||||
int err;
|
int err;
|
||||||
|
@ -84,14 +102,14 @@ static int ds2782_get_temp(struct ds2782_info *info, int *temp)
|
||||||
* celsius. The temperature value is stored as a 10 bit number, plus
|
* celsius. The temperature value is stored as a 10 bit number, plus
|
||||||
* sign in the upper bits of a 16 bit register.
|
* sign in the upper bits of a 16 bit register.
|
||||||
*/
|
*/
|
||||||
err = ds2782_read_reg16(info, DS2782_REG_TEMP_MSB, &raw);
|
err = ds278x_read_reg16(info, DS278x_REG_TEMP_MSB, &raw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
*temp = ((raw / 32) * 125) / 100;
|
*temp = ((raw / 32) * 125) / 100;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
|
static int ds2782_get_current(struct ds278x_info *info, int *current_uA)
|
||||||
{
|
{
|
||||||
int sense_res;
|
int sense_res;
|
||||||
int err;
|
int err;
|
||||||
|
@ -102,7 +120,7 @@ static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
|
||||||
* The units of measurement for current are dependent on the value of
|
* The units of measurement for current are dependent on the value of
|
||||||
* the sense resistor.
|
* the sense resistor.
|
||||||
*/
|
*/
|
||||||
err = ds2782_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
|
err = ds278x_read_reg(info, DS2782_REG_RSNSP, &sense_res_raw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (sense_res_raw == 0) {
|
if (sense_res_raw == 0) {
|
||||||
|
@ -113,14 +131,14 @@ static int ds2782_get_current(struct ds2782_info *info, int *current_uA)
|
||||||
|
|
||||||
dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
|
dev_dbg(&info->client->dev, "sense resistor = %d milli-ohms\n",
|
||||||
sense_res);
|
sense_res);
|
||||||
err = ds2782_read_reg16(info, DS2782_REG_CURRENT_MSB, &raw);
|
err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
|
*current_uA = raw * (DS2782_CURRENT_UNITS / sense_res);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA)
|
static int ds2782_get_voltage(struct ds278x_info *info, int *voltage_uA)
|
||||||
{
|
{
|
||||||
s16 raw;
|
s16 raw;
|
||||||
int err;
|
int err;
|
||||||
|
@ -129,36 +147,77 @@ static int ds2782_get_voltage(struct ds2782_info *info, int *voltage_uA)
|
||||||
* Voltage is measured in units of 4.88mV. The voltage is stored as
|
* Voltage is measured in units of 4.88mV. The voltage is stored as
|
||||||
* a 10-bit number plus sign, in the upper bits of a 16-bit register
|
* a 10-bit number plus sign, in the upper bits of a 16-bit register
|
||||||
*/
|
*/
|
||||||
err = ds2782_read_reg16(info, DS2782_REG_VOLT_MSB, &raw);
|
err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
*voltage_uA = (raw / 32) * 4800;
|
*voltage_uA = (raw / 32) * 4800;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_get_capacity(struct ds2782_info *info, int *capacity)
|
static int ds2782_get_capacity(struct ds278x_info *info, int *capacity)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
u8 raw;
|
u8 raw;
|
||||||
|
|
||||||
err = ds2782_read_reg(info, DS2782_REG_RARC, &raw);
|
err = ds278x_read_reg(info, DS2782_REG_RARC, &raw);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
*capacity = raw;
|
*capacity = raw;
|
||||||
return raw;
|
return raw;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_get_status(struct ds2782_info *info, int *status)
|
static int ds2786_get_current(struct ds278x_info *info, int *current_uA)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
s16 raw;
|
||||||
|
|
||||||
|
err = ds278x_read_reg16(info, DS278x_REG_CURRENT_MSB, &raw);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
*current_uA = (raw / 16) * (DS2786_CURRENT_UNITS / info->rsns);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds2786_get_voltage(struct ds278x_info *info, int *voltage_uA)
|
||||||
|
{
|
||||||
|
s16 raw;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Voltage is measured in units of 1.22mV. The voltage is stored as
|
||||||
|
* a 10-bit number plus sign, in the upper bits of a 16-bit register
|
||||||
|
*/
|
||||||
|
err = ds278x_read_reg16(info, DS278x_REG_VOLT_MSB, &raw);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
*voltage_uA = (raw / 8) * 1220;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds2786_get_capacity(struct ds278x_info *info, int *capacity)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
u8 raw;
|
||||||
|
|
||||||
|
err = ds278x_read_reg(info, DS2786_REG_RARC, &raw);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
/* Relative capacity is displayed with resolution 0.5 % */
|
||||||
|
*capacity = raw/2 ;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ds278x_get_status(struct ds278x_info *info, int *status)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
int current_uA;
|
int current_uA;
|
||||||
int capacity;
|
int capacity;
|
||||||
|
|
||||||
err = ds2782_get_current(info, ¤t_uA);
|
err = info->ops->get_current(info, ¤t_uA);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
err = ds2782_get_capacity(info, &capacity);
|
err = info->ops->get_capacity(info, &capacity);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
@ -174,32 +233,32 @@ static int ds2782_get_status(struct ds2782_info *info, int *status)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_battery_get_property(struct power_supply *psy,
|
static int ds278x_battery_get_property(struct power_supply *psy,
|
||||||
enum power_supply_property prop,
|
enum power_supply_property prop,
|
||||||
union power_supply_propval *val)
|
union power_supply_propval *val)
|
||||||
{
|
{
|
||||||
struct ds2782_info *info = to_ds2782_info(psy);
|
struct ds278x_info *info = to_ds278x_info(psy);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (prop) {
|
switch (prop) {
|
||||||
case POWER_SUPPLY_PROP_STATUS:
|
case POWER_SUPPLY_PROP_STATUS:
|
||||||
ret = ds2782_get_status(info, &val->intval);
|
ret = ds278x_get_status(info, &val->intval);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_CAPACITY:
|
case POWER_SUPPLY_PROP_CAPACITY:
|
||||||
ret = ds2782_get_capacity(info, &val->intval);
|
ret = info->ops->get_capacity(info, &val->intval);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||||
ret = ds2782_get_voltage(info, &val->intval);
|
ret = info->ops->get_voltage(info, &val->intval);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
case POWER_SUPPLY_PROP_CURRENT_NOW:
|
||||||
ret = ds2782_get_current(info, &val->intval);
|
ret = info->ops->get_current(info, &val->intval);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case POWER_SUPPLY_PROP_TEMP:
|
case POWER_SUPPLY_PROP_TEMP:
|
||||||
ret = ds2782_get_temp(info, &val->intval);
|
ret = ds278x_get_temp(info, &val->intval);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -209,7 +268,7 @@ static int ds2782_battery_get_property(struct power_supply *psy,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum power_supply_property ds2782_battery_props[] = {
|
static enum power_supply_property ds278x_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_STATUS,
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
POWER_SUPPLY_PROP_CAPACITY,
|
POWER_SUPPLY_PROP_CAPACITY,
|
||||||
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
POWER_SUPPLY_PROP_VOLTAGE_NOW,
|
||||||
|
@ -217,18 +276,18 @@ static enum power_supply_property ds2782_battery_props[] = {
|
||||||
POWER_SUPPLY_PROP_TEMP,
|
POWER_SUPPLY_PROP_TEMP,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ds2782_power_supply_init(struct power_supply *battery)
|
static void ds278x_power_supply_init(struct power_supply *battery)
|
||||||
{
|
{
|
||||||
battery->type = POWER_SUPPLY_TYPE_BATTERY;
|
battery->type = POWER_SUPPLY_TYPE_BATTERY;
|
||||||
battery->properties = ds2782_battery_props;
|
battery->properties = ds278x_battery_props;
|
||||||
battery->num_properties = ARRAY_SIZE(ds2782_battery_props);
|
battery->num_properties = ARRAY_SIZE(ds278x_battery_props);
|
||||||
battery->get_property = ds2782_battery_get_property;
|
battery->get_property = ds278x_battery_get_property;
|
||||||
battery->external_power_changed = NULL;
|
battery->external_power_changed = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_battery_remove(struct i2c_client *client)
|
static int ds278x_battery_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct ds2782_info *info = i2c_get_clientdata(client);
|
struct ds278x_info *info = i2c_get_clientdata(client);
|
||||||
|
|
||||||
power_supply_unregister(&info->battery);
|
power_supply_unregister(&info->battery);
|
||||||
kfree(info->battery.name);
|
kfree(info->battery.name);
|
||||||
|
@ -237,19 +296,45 @@ static int ds2782_battery_remove(struct i2c_client *client)
|
||||||
idr_remove(&battery_id, info->id);
|
idr_remove(&battery_id, info->id);
|
||||||
mutex_unlock(&battery_lock);
|
mutex_unlock(&battery_lock);
|
||||||
|
|
||||||
i2c_set_clientdata(client, info);
|
|
||||||
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ds2782_battery_probe(struct i2c_client *client,
|
enum ds278x_num_id {
|
||||||
|
DS2782 = 0,
|
||||||
|
DS2786,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct ds278x_battery_ops ds278x_ops[] = {
|
||||||
|
[DS2782] = {
|
||||||
|
.get_current = ds2782_get_current,
|
||||||
|
.get_voltage = ds2782_get_voltage,
|
||||||
|
.get_capacity = ds2782_get_capacity,
|
||||||
|
},
|
||||||
|
[DS2786] = {
|
||||||
|
.get_current = ds2786_get_current,
|
||||||
|
.get_voltage = ds2786_get_voltage,
|
||||||
|
.get_capacity = ds2786_get_capacity,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int ds278x_battery_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
struct ds2782_info *info;
|
struct ds278x_platform_data *pdata = client->dev.platform_data;
|
||||||
|
struct ds278x_info *info;
|
||||||
int ret;
|
int ret;
|
||||||
int num;
|
int num;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ds2786 should have the sense resistor value set
|
||||||
|
* in the platform data
|
||||||
|
*/
|
||||||
|
if (id->driver_data == DS2786 && !pdata) {
|
||||||
|
dev_err(&client->dev, "missing platform data for ds2786\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Get an ID for this battery */
|
/* Get an ID for this battery */
|
||||||
ret = idr_pre_get(&battery_id, GFP_KERNEL);
|
ret = idr_pre_get(&battery_id, GFP_KERNEL);
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
@ -269,15 +354,20 @@ static int ds2782_battery_probe(struct i2c_client *client,
|
||||||
goto fail_info;
|
goto fail_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->battery.name = kasprintf(GFP_KERNEL, "ds2782-%d", num);
|
info->battery.name = kasprintf(GFP_KERNEL, "%s-%d", client->name, num);
|
||||||
if (!info->battery.name) {
|
if (!info->battery.name) {
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto fail_name;
|
goto fail_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (id->driver_data == DS2786)
|
||||||
|
info->rsns = pdata->rsns;
|
||||||
|
|
||||||
i2c_set_clientdata(client, info);
|
i2c_set_clientdata(client, info);
|
||||||
info->client = client;
|
info->client = client;
|
||||||
ds2782_power_supply_init(&info->battery);
|
info->id = num;
|
||||||
|
info->ops = &ds278x_ops[id->driver_data];
|
||||||
|
ds278x_power_supply_init(&info->battery);
|
||||||
|
|
||||||
ret = power_supply_register(&client->dev, &info->battery);
|
ret = power_supply_register(&client->dev, &info->battery);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
@ -290,7 +380,6 @@ static int ds2782_battery_probe(struct i2c_client *client,
|
||||||
fail_register:
|
fail_register:
|
||||||
kfree(info->battery.name);
|
kfree(info->battery.name);
|
||||||
fail_name:
|
fail_name:
|
||||||
i2c_set_clientdata(client, info);
|
|
||||||
kfree(info);
|
kfree(info);
|
||||||
fail_info:
|
fail_info:
|
||||||
mutex_lock(&battery_lock);
|
mutex_lock(&battery_lock);
|
||||||
|
@ -300,31 +389,32 @@ static int ds2782_battery_probe(struct i2c_client *client,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct i2c_device_id ds2782_id[] = {
|
static const struct i2c_device_id ds278x_id[] = {
|
||||||
{"ds2782", 0},
|
{"ds2782", DS2782},
|
||||||
|
{"ds2786", DS2786},
|
||||||
{},
|
{},
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct i2c_driver ds2782_battery_driver = {
|
static struct i2c_driver ds278x_battery_driver = {
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "ds2782-battery",
|
.name = "ds2782-battery",
|
||||||
},
|
},
|
||||||
.probe = ds2782_battery_probe,
|
.probe = ds278x_battery_probe,
|
||||||
.remove = ds2782_battery_remove,
|
.remove = ds278x_battery_remove,
|
||||||
.id_table = ds2782_id,
|
.id_table = ds278x_id,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init ds2782_init(void)
|
static int __init ds278x_init(void)
|
||||||
{
|
{
|
||||||
return i2c_add_driver(&ds2782_battery_driver);
|
return i2c_add_driver(&ds278x_battery_driver);
|
||||||
}
|
}
|
||||||
module_init(ds2782_init);
|
module_init(ds278x_init);
|
||||||
|
|
||||||
static void __exit ds2782_exit(void)
|
static void __exit ds278x_exit(void)
|
||||||
{
|
{
|
||||||
i2c_del_driver(&ds2782_battery_driver);
|
i2c_del_driver(&ds278x_battery_driver);
|
||||||
}
|
}
|
||||||
module_exit(ds2782_exit);
|
module_exit(ds278x_exit);
|
||||||
|
|
||||||
MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
|
MODULE_AUTHOR("Ryan Mallon <ryan@bluewatersys.com>");
|
||||||
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
|
MODULE_DESCRIPTION("Maxim/Dallas DS2782 Stand-Alone Fuel Gauage IC driver");
|
||||||
|
|
|
@ -404,6 +404,13 @@ static int usb_wakeup_enabled;
|
||||||
|
|
||||||
static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
|
static int pda_power_suspend(struct platform_device *pdev, pm_message_t state)
|
||||||
{
|
{
|
||||||
|
if (pdata->suspend) {
|
||||||
|
int ret = pdata->suspend(state);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
if (device_may_wakeup(&pdev->dev)) {
|
if (device_may_wakeup(&pdev->dev)) {
|
||||||
if (ac_irq)
|
if (ac_irq)
|
||||||
ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
|
ac_wakeup_enabled = !enable_irq_wake(ac_irq->start);
|
||||||
|
@ -423,6 +430,9 @@ static int pda_power_resume(struct platform_device *pdev)
|
||||||
disable_irq_wake(ac_irq->start);
|
disable_irq_wake(ac_irq->start);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pdata->resume)
|
||||||
|
return pdata->resume();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -12,15 +12,12 @@
|
||||||
|
|
||||||
#ifdef CONFIG_SYSFS
|
#ifdef CONFIG_SYSFS
|
||||||
|
|
||||||
extern int power_supply_create_attrs(struct power_supply *psy);
|
extern void power_supply_init_attrs(struct device_type *dev_type);
|
||||||
extern void power_supply_remove_attrs(struct power_supply *psy);
|
|
||||||
extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
|
extern int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env);
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
static inline int power_supply_create_attrs(struct power_supply *psy)
|
static inline void power_supply_init_attrs(struct device_type *dev_type) {}
|
||||||
{ return 0; }
|
|
||||||
static inline void power_supply_remove_attrs(struct power_supply *psy) {}
|
|
||||||
#define power_supply_uevent NULL
|
#define power_supply_uevent NULL
|
||||||
|
|
||||||
#endif /* CONFIG_SYSFS */
|
#endif /* CONFIG_SYSFS */
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/power_supply.h>
|
#include <linux/power_supply.h>
|
||||||
|
@ -22,6 +23,8 @@
|
||||||
struct class *power_supply_class;
|
struct class *power_supply_class;
|
||||||
EXPORT_SYMBOL_GPL(power_supply_class);
|
EXPORT_SYMBOL_GPL(power_supply_class);
|
||||||
|
|
||||||
|
static struct device_type power_supply_dev_type;
|
||||||
|
|
||||||
static int __power_supply_changed_work(struct device *dev, void *data)
|
static int __power_supply_changed_work(struct device *dev, void *data)
|
||||||
{
|
{
|
||||||
struct power_supply *psy = (struct power_supply *)data;
|
struct power_supply *psy = (struct power_supply *)data;
|
||||||
|
@ -144,23 +147,40 @@ struct power_supply *power_supply_get_by_name(char *name)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
|
EXPORT_SYMBOL_GPL(power_supply_get_by_name);
|
||||||
|
|
||||||
|
static void power_supply_dev_release(struct device *dev)
|
||||||
|
{
|
||||||
|
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
|
||||||
|
kfree(dev);
|
||||||
|
}
|
||||||
|
|
||||||
int power_supply_register(struct device *parent, struct power_supply *psy)
|
int power_supply_register(struct device *parent, struct power_supply *psy)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
struct device *dev;
|
||||||
|
int rc;
|
||||||
|
|
||||||
psy->dev = device_create(power_supply_class, parent, 0, psy,
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
||||||
"%s", psy->name);
|
if (!dev)
|
||||||
if (IS_ERR(psy->dev)) {
|
return -ENOMEM;
|
||||||
rc = PTR_ERR(psy->dev);
|
|
||||||
goto dev_create_failed;
|
device_initialize(dev);
|
||||||
}
|
|
||||||
|
dev->class = power_supply_class;
|
||||||
|
dev->type = &power_supply_dev_type;
|
||||||
|
dev->parent = parent;
|
||||||
|
dev->release = power_supply_dev_release;
|
||||||
|
dev_set_drvdata(dev, psy);
|
||||||
|
psy->dev = dev;
|
||||||
|
|
||||||
|
rc = kobject_set_name(&dev->kobj, "%s", psy->name);
|
||||||
|
if (rc)
|
||||||
|
goto kobject_set_name_failed;
|
||||||
|
|
||||||
|
rc = device_add(dev);
|
||||||
|
if (rc)
|
||||||
|
goto device_add_failed;
|
||||||
|
|
||||||
INIT_WORK(&psy->changed_work, power_supply_changed_work);
|
INIT_WORK(&psy->changed_work, power_supply_changed_work);
|
||||||
|
|
||||||
rc = power_supply_create_attrs(psy);
|
|
||||||
if (rc)
|
|
||||||
goto create_attrs_failed;
|
|
||||||
|
|
||||||
rc = power_supply_create_triggers(psy);
|
rc = power_supply_create_triggers(psy);
|
||||||
if (rc)
|
if (rc)
|
||||||
goto create_triggers_failed;
|
goto create_triggers_failed;
|
||||||
|
@ -170,10 +190,10 @@ int power_supply_register(struct device *parent, struct power_supply *psy)
|
||||||
goto success;
|
goto success;
|
||||||
|
|
||||||
create_triggers_failed:
|
create_triggers_failed:
|
||||||
power_supply_remove_attrs(psy);
|
|
||||||
create_attrs_failed:
|
|
||||||
device_unregister(psy->dev);
|
device_unregister(psy->dev);
|
||||||
dev_create_failed:
|
kobject_set_name_failed:
|
||||||
|
device_add_failed:
|
||||||
|
kfree(dev);
|
||||||
success:
|
success:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +203,6 @@ void power_supply_unregister(struct power_supply *psy)
|
||||||
{
|
{
|
||||||
flush_scheduled_work();
|
flush_scheduled_work();
|
||||||
power_supply_remove_triggers(psy);
|
power_supply_remove_triggers(psy);
|
||||||
power_supply_remove_attrs(psy);
|
|
||||||
device_unregister(psy->dev);
|
device_unregister(psy->dev);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(power_supply_unregister);
|
EXPORT_SYMBOL_GPL(power_supply_unregister);
|
||||||
|
@ -196,6 +215,7 @@ static int __init power_supply_class_init(void)
|
||||||
return PTR_ERR(power_supply_class);
|
return PTR_ERR(power_supply_class);
|
||||||
|
|
||||||
power_supply_class->dev_uevent = power_supply_uevent;
|
power_supply_class->dev_uevent = power_supply_uevent;
|
||||||
|
power_supply_init_attrs(&power_supply_dev_type);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,9 +31,9 @@
|
||||||
|
|
||||||
#define POWER_SUPPLY_ATTR(_name) \
|
#define POWER_SUPPLY_ATTR(_name) \
|
||||||
{ \
|
{ \
|
||||||
.attr = { .name = #_name, .mode = 0444 }, \
|
.attr = { .name = #_name }, \
|
||||||
.show = power_supply_show_property, \
|
.show = power_supply_show_property, \
|
||||||
.store = NULL, \
|
.store = power_supply_store_property, \
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device_attribute power_supply_attrs[];
|
static struct device_attribute power_supply_attrs[];
|
||||||
|
@ -41,6 +41,9 @@ static struct device_attribute power_supply_attrs[];
|
||||||
static ssize_t power_supply_show_property(struct device *dev,
|
static ssize_t power_supply_show_property(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
char *buf) {
|
char *buf) {
|
||||||
|
static char *type_text[] = {
|
||||||
|
"Battery", "UPS", "Mains", "USB"
|
||||||
|
};
|
||||||
static char *status_text[] = {
|
static char *status_text[] = {
|
||||||
"Unknown", "Charging", "Discharging", "Not charging", "Full"
|
"Unknown", "Charging", "Discharging", "Not charging", "Full"
|
||||||
};
|
};
|
||||||
|
@ -58,11 +61,14 @@ static ssize_t power_supply_show_property(struct device *dev,
|
||||||
static char *capacity_level_text[] = {
|
static char *capacity_level_text[] = {
|
||||||
"Unknown", "Critical", "Low", "Normal", "High", "Full"
|
"Unknown", "Critical", "Low", "Normal", "High", "Full"
|
||||||
};
|
};
|
||||||
ssize_t ret;
|
ssize_t ret = 0;
|
||||||
struct power_supply *psy = dev_get_drvdata(dev);
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
const ptrdiff_t off = attr - power_supply_attrs;
|
const ptrdiff_t off = attr - power_supply_attrs;
|
||||||
union power_supply_propval value;
|
union power_supply_propval value;
|
||||||
|
|
||||||
|
if (off == POWER_SUPPLY_PROP_TYPE)
|
||||||
|
value.intval = psy->type;
|
||||||
|
else
|
||||||
ret = psy->get_property(psy, off, &value);
|
ret = psy->get_property(psy, off, &value);
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
|
@ -85,12 +91,37 @@ static ssize_t power_supply_show_property(struct device *dev,
|
||||||
return sprintf(buf, "%s\n", technology_text[value.intval]);
|
return sprintf(buf, "%s\n", technology_text[value.intval]);
|
||||||
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
|
else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
|
||||||
return sprintf(buf, "%s\n", capacity_level_text[value.intval]);
|
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_MODEL_NAME)
|
else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
|
||||||
return sprintf(buf, "%s\n", value.strval);
|
return sprintf(buf, "%s\n", value.strval);
|
||||||
|
|
||||||
return sprintf(buf, "%d\n", value.intval);
|
return sprintf(buf, "%d\n", value.intval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static ssize_t power_supply_store_property(struct device *dev,
|
||||||
|
struct device_attribute *attr,
|
||||||
|
const char *buf, size_t count) {
|
||||||
|
ssize_t ret;
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
const ptrdiff_t off = attr - power_supply_attrs;
|
||||||
|
union power_supply_propval value;
|
||||||
|
long long_val;
|
||||||
|
|
||||||
|
/* TODO: support other types than int */
|
||||||
|
ret = strict_strtol(buf, 10, &long_val);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
value.intval = long_val;
|
||||||
|
|
||||||
|
ret = psy->set_property(psy, off, &value);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
/* Must be in the same order as POWER_SUPPLY_PROP_* */
|
/* Must be in the same order as POWER_SUPPLY_PROP_* */
|
||||||
static struct device_attribute power_supply_attrs[] = {
|
static struct device_attribute power_supply_attrs[] = {
|
||||||
/* Properties of type `int' */
|
/* Properties of type `int' */
|
||||||
|
@ -132,67 +163,59 @@ static struct device_attribute power_supply_attrs[] = {
|
||||||
POWER_SUPPLY_ATTR(time_to_empty_avg),
|
POWER_SUPPLY_ATTR(time_to_empty_avg),
|
||||||
POWER_SUPPLY_ATTR(time_to_full_now),
|
POWER_SUPPLY_ATTR(time_to_full_now),
|
||||||
POWER_SUPPLY_ATTR(time_to_full_avg),
|
POWER_SUPPLY_ATTR(time_to_full_avg),
|
||||||
|
POWER_SUPPLY_ATTR(type),
|
||||||
/* Properties of type `const char *' */
|
/* Properties of type `const char *' */
|
||||||
POWER_SUPPLY_ATTR(model_name),
|
POWER_SUPPLY_ATTR(model_name),
|
||||||
POWER_SUPPLY_ATTR(manufacturer),
|
POWER_SUPPLY_ATTR(manufacturer),
|
||||||
POWER_SUPPLY_ATTR(serial_number),
|
POWER_SUPPLY_ATTR(serial_number),
|
||||||
};
|
};
|
||||||
|
|
||||||
static ssize_t power_supply_show_static_attrs(struct device *dev,
|
static struct attribute *
|
||||||
struct device_attribute *attr,
|
__power_supply_attrs[ARRAY_SIZE(power_supply_attrs) + 1];
|
||||||
char *buf) {
|
|
||||||
static char *type_text[] = { "Battery", "UPS", "Mains", "USB" };
|
|
||||||
struct power_supply *psy = dev_get_drvdata(dev);
|
|
||||||
|
|
||||||
return sprintf(buf, "%s\n", type_text[psy->type]);
|
static mode_t power_supply_attr_is_visible(struct kobject *kobj,
|
||||||
|
struct attribute *attr,
|
||||||
|
int attrno)
|
||||||
|
{
|
||||||
|
struct device *dev = container_of(kobj, struct device, kobj);
|
||||||
|
struct power_supply *psy = dev_get_drvdata(dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < psy->num_properties; i++) {
|
||||||
|
int property = psy->properties[i];
|
||||||
|
|
||||||
|
if (property == attrno) {
|
||||||
|
mode_t mode = S_IRUSR | S_IRGRP | S_IROTH;
|
||||||
|
|
||||||
|
if (psy->property_is_writeable &&
|
||||||
|
psy->property_is_writeable(psy, property) > 0)
|
||||||
|
mode |= S_IWUSR;
|
||||||
|
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct device_attribute power_supply_static_attrs[] = {
|
static struct attribute_group power_supply_attr_group = {
|
||||||
__ATTR(type, 0444, power_supply_show_static_attrs, NULL),
|
.attrs = __power_supply_attrs,
|
||||||
|
.is_visible = power_supply_attr_is_visible,
|
||||||
};
|
};
|
||||||
|
|
||||||
int power_supply_create_attrs(struct power_supply *psy)
|
static const struct attribute_group *power_supply_attr_groups[] = {
|
||||||
{
|
&power_supply_attr_group,
|
||||||
int rc = 0;
|
NULL,
|
||||||
int i, j;
|
};
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
|
void power_supply_init_attrs(struct device_type *dev_type)
|
||||||
rc = device_create_file(psy->dev,
|
|
||||||
&power_supply_static_attrs[i]);
|
|
||||||
if (rc)
|
|
||||||
goto statics_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (j = 0; j < psy->num_properties; j++) {
|
|
||||||
rc = device_create_file(psy->dev,
|
|
||||||
&power_supply_attrs[psy->properties[j]]);
|
|
||||||
if (rc)
|
|
||||||
goto dynamics_failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto succeed;
|
|
||||||
|
|
||||||
dynamics_failed:
|
|
||||||
while (j--)
|
|
||||||
device_remove_file(psy->dev,
|
|
||||||
&power_supply_attrs[psy->properties[j]]);
|
|
||||||
statics_failed:
|
|
||||||
while (i--)
|
|
||||||
device_remove_file(psy->dev, &power_supply_static_attrs[i]);
|
|
||||||
succeed:
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void power_supply_remove_attrs(struct power_supply *psy)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
|
dev_type->groups = power_supply_attr_groups;
|
||||||
device_remove_file(psy->dev, &power_supply_static_attrs[i]);
|
|
||||||
|
|
||||||
for (i = 0; i < psy->num_properties; i++)
|
for (i = 0; i < ARRAY_SIZE(power_supply_attrs); i++)
|
||||||
device_remove_file(psy->dev,
|
__power_supply_attrs[i] = &power_supply_attrs[i].attr;
|
||||||
&power_supply_attrs[psy->properties[i]]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *kstruprdup(const char *str, gfp_t gfp)
|
static char *kstruprdup(const char *str, gfp_t gfp)
|
||||||
|
@ -236,36 +259,6 @@ int power_supply_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||||
if (!prop_buf)
|
if (!prop_buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
for (j = 0; j < ARRAY_SIZE(power_supply_static_attrs); j++) {
|
|
||||||
struct device_attribute *attr;
|
|
||||||
char *line;
|
|
||||||
|
|
||||||
attr = &power_supply_static_attrs[j];
|
|
||||||
|
|
||||||
ret = power_supply_show_static_attrs(dev, attr, prop_buf);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
line = strchr(prop_buf, '\n');
|
|
||||||
if (line)
|
|
||||||
*line = 0;
|
|
||||||
|
|
||||||
attrname = kstruprdup(attr->attr.name, GFP_KERNEL);
|
|
||||||
if (!attrname) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(dev, "Static prop %s=%s\n", attrname, prop_buf);
|
|
||||||
|
|
||||||
ret = add_uevent_var(env, "POWER_SUPPLY_%s=%s", attrname, prop_buf);
|
|
||||||
kfree(attrname);
|
|
||||||
if (ret)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
dev_dbg(dev, "%zd dynamic props\n", psy->num_properties);
|
|
||||||
|
|
||||||
for (j = 0; j < psy->num_properties; j++) {
|
for (j = 0; j < psy->num_properties; j++) {
|
||||||
struct device_attribute *attr;
|
struct device_attribute *attr;
|
||||||
char *line;
|
char *line;
|
||||||
|
|
163
drivers/power/test_power.c
Normal file
163
drivers/power/test_power.c
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
/*
|
||||||
|
* Power supply driver for testing.
|
||||||
|
*
|
||||||
|
* Copyright 2010 Anton Vorontsov <cbouatmailru@gmail.com>
|
||||||
|
*
|
||||||
|
* 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/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/power_supply.h>
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/delay.h>
|
||||||
|
#include <linux/vermagic.h>
|
||||||
|
|
||||||
|
static int test_power_ac_online = 1;
|
||||||
|
static int test_power_battery_status = POWER_SUPPLY_STATUS_CHARGING;
|
||||||
|
|
||||||
|
static int test_power_get_ac_property(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_ONLINE:
|
||||||
|
val->intval = test_power_ac_online;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int test_power_get_battery_property(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_MODEL_NAME:
|
||||||
|
val->strval = "Test battery";
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_MANUFACTURER:
|
||||||
|
val->strval = "Linux";
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_SERIAL_NUMBER:
|
||||||
|
val->strval = UTS_RELEASE;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_STATUS:
|
||||||
|
val->intval = test_power_battery_status;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_CHARGE_TYPE:
|
||||||
|
val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_HEALTH:
|
||||||
|
val->intval = POWER_SUPPLY_HEALTH_GOOD;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||||
|
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
|
||||||
|
val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_CAPACITY:
|
||||||
|
val->intval = 50;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
|
||||||
|
case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW:
|
||||||
|
val->intval = 3600;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pr_info("%s: some properties deliberately report errors.\n",
|
||||||
|
__func__);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum power_supply_property test_power_ac_props[] = {
|
||||||
|
POWER_SUPPLY_PROP_ONLINE,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum power_supply_property test_power_battery_props[] = {
|
||||||
|
POWER_SUPPLY_PROP_STATUS,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_TYPE,
|
||||||
|
POWER_SUPPLY_PROP_HEALTH,
|
||||||
|
POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_FULL,
|
||||||
|
POWER_SUPPLY_PROP_CHARGE_EMPTY,
|
||||||
|
POWER_SUPPLY_PROP_CAPACITY,
|
||||||
|
POWER_SUPPLY_PROP_CAPACITY_LEVEL,
|
||||||
|
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||||
|
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||||
|
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||||
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
|
POWER_SUPPLY_PROP_SERIAL_NUMBER,
|
||||||
|
};
|
||||||
|
|
||||||
|
static char *test_power_ac_supplied_to[] = {
|
||||||
|
"test_battery",
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct power_supply test_power_supplies[] = {
|
||||||
|
{
|
||||||
|
.name = "test_ac",
|
||||||
|
.type = POWER_SUPPLY_TYPE_MAINS,
|
||||||
|
.supplied_to = test_power_ac_supplied_to,
|
||||||
|
.num_supplicants = ARRAY_SIZE(test_power_ac_supplied_to),
|
||||||
|
.properties = test_power_ac_props,
|
||||||
|
.num_properties = ARRAY_SIZE(test_power_ac_props),
|
||||||
|
.get_property = test_power_get_ac_property,
|
||||||
|
}, {
|
||||||
|
.name = "test_battery",
|
||||||
|
.type = POWER_SUPPLY_TYPE_BATTERY,
|
||||||
|
.properties = test_power_battery_props,
|
||||||
|
.num_properties = ARRAY_SIZE(test_power_battery_props),
|
||||||
|
.get_property = test_power_get_battery_property,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init test_power_init(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++) {
|
||||||
|
ret = power_supply_register(NULL, &test_power_supplies[i]);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("%s: failed to register %s\n", __func__,
|
||||||
|
test_power_supplies[i].name);
|
||||||
|
goto failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
failed:
|
||||||
|
while (--i >= 0)
|
||||||
|
power_supply_unregister(&test_power_supplies[i]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
module_init(test_power_init);
|
||||||
|
|
||||||
|
static void __exit test_power_exit(void)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Let's see how we handle changes... */
|
||||||
|
test_power_ac_online = 0;
|
||||||
|
test_power_battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
|
||||||
|
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
|
||||||
|
power_supply_changed(&test_power_supplies[i]);
|
||||||
|
pr_info("%s: 'changed' event sent, sleeping for 10 seconds...\n",
|
||||||
|
__func__);
|
||||||
|
ssleep(10);
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(test_power_supplies); i++)
|
||||||
|
power_supply_unregister(&test_power_supplies[i]);
|
||||||
|
}
|
||||||
|
module_exit(test_power_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION("Power supply driver for testing");
|
||||||
|
MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -61,7 +61,7 @@ static unsigned long tosa_read_bat(struct tosa_bat *bat)
|
||||||
mutex_lock(&bat_lock);
|
mutex_lock(&bat_lock);
|
||||||
gpio_set_value(bat->gpio_bat, 1);
|
gpio_set_value(bat->gpio_bat, 1);
|
||||||
msleep(5);
|
msleep(5);
|
||||||
value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data,
|
value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent),
|
||||||
bat->adc_bat);
|
bat->adc_bat);
|
||||||
gpio_set_value(bat->gpio_bat, 0);
|
gpio_set_value(bat->gpio_bat, 0);
|
||||||
mutex_unlock(&bat_lock);
|
mutex_unlock(&bat_lock);
|
||||||
|
@ -81,7 +81,7 @@ static unsigned long tosa_read_temp(struct tosa_bat *bat)
|
||||||
mutex_lock(&bat_lock);
|
mutex_lock(&bat_lock);
|
||||||
gpio_set_value(bat->gpio_temp, 1);
|
gpio_set_value(bat->gpio_temp, 1);
|
||||||
msleep(5);
|
msleep(5);
|
||||||
value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data,
|
value = wm97xx_read_aux_adc(dev_get_drvdata(bat->psy.dev->parent),
|
||||||
bat->adc_temp);
|
bat->adc_temp);
|
||||||
gpio_set_value(bat->gpio_temp, 0);
|
gpio_set_value(bat->gpio_temp, 0);
|
||||||
mutex_unlock(&bat_lock);
|
mutex_unlock(&bat_lock);
|
||||||
|
|
|
@ -537,8 +537,8 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||||
goto err_battery;
|
goto err_battery;
|
||||||
|
|
||||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||||
ret = wm831x_request_irq(wm831x, irq, wm831x_syslo_irq,
|
ret = request_threaded_irq(irq, NULL, wm831x_syslo_irq,
|
||||||
IRQF_TRIGGER_RISING, "SYSLO",
|
IRQF_TRIGGER_RISING, "System power low",
|
||||||
power);
|
power);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
|
dev_err(&pdev->dev, "Failed to request SYSLO IRQ %d: %d\n",
|
||||||
|
@ -547,7 +547,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||||
ret = wm831x_request_irq(wm831x, irq, wm831x_pwr_src_irq,
|
ret = request_threaded_irq(irq, NULL, wm831x_pwr_src_irq,
|
||||||
IRQF_TRIGGER_RISING, "Power source",
|
IRQF_TRIGGER_RISING, "Power source",
|
||||||
power);
|
power);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
|
@ -558,7 +558,7 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
||||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||||
ret = wm831x_request_irq(wm831x, irq, wm831x_bat_irq,
|
ret = request_threaded_irq(irq, NULL, wm831x_bat_irq,
|
||||||
IRQF_TRIGGER_RISING,
|
IRQF_TRIGGER_RISING,
|
||||||
wm831x_bat_irqs[i],
|
wm831x_bat_irqs[i],
|
||||||
power);
|
power);
|
||||||
|
@ -575,13 +575,13 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||||
err_bat_irq:
|
err_bat_irq:
|
||||||
for (; i >= 0; i--) {
|
for (; i >= 0; i--) {
|
||||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||||
wm831x_free_irq(wm831x, irq, power);
|
free_irq(irq, power);
|
||||||
}
|
}
|
||||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||||
wm831x_free_irq(wm831x, irq, power);
|
free_irq(irq, power);
|
||||||
err_syslo:
|
err_syslo:
|
||||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||||
wm831x_free_irq(wm831x, irq, power);
|
free_irq(irq, power);
|
||||||
err_usb:
|
err_usb:
|
||||||
power_supply_unregister(usb);
|
power_supply_unregister(usb);
|
||||||
err_battery:
|
err_battery:
|
||||||
|
@ -596,19 +596,18 @@ static __devinit int wm831x_power_probe(struct platform_device *pdev)
|
||||||
static __devexit int wm831x_power_remove(struct platform_device *pdev)
|
static __devexit int wm831x_power_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
|
struct wm831x_power *wm831x_power = platform_get_drvdata(pdev);
|
||||||
struct wm831x *wm831x = wm831x_power->wm831x;
|
|
||||||
int irq, i;
|
int irq, i;
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
for (i = 0; i < ARRAY_SIZE(wm831x_bat_irqs); i++) {
|
||||||
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
irq = platform_get_irq_byname(pdev, wm831x_bat_irqs[i]);
|
||||||
wm831x_free_irq(wm831x, irq, wm831x_power);
|
free_irq(irq, wm831x_power);
|
||||||
}
|
}
|
||||||
|
|
||||||
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
irq = platform_get_irq_byname(pdev, "PWR SRC");
|
||||||
wm831x_free_irq(wm831x, irq, wm831x_power);
|
free_irq(irq, wm831x_power);
|
||||||
|
|
||||||
irq = platform_get_irq_byname(pdev, "SYSLO");
|
irq = platform_get_irq_byname(pdev, "SYSLO");
|
||||||
wm831x_free_irq(wm831x, irq, wm831x_power);
|
free_irq(irq, wm831x_power);
|
||||||
|
|
||||||
power_supply_unregister(&wm831x_power->battery);
|
power_supply_unregister(&wm831x_power->battery);
|
||||||
power_supply_unregister(&wm831x_power->wall);
|
power_supply_unregister(&wm831x_power->wall);
|
||||||
|
|
|
@ -308,6 +308,9 @@ static void __exit wm97xx_bat_exit(void)
|
||||||
platform_driver_unregister(&wm97xx_bat_driver);
|
platform_driver_unregister(&wm97xx_bat_driver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The interface is deprecated, as well as linux/wm97xx_batt.h */
|
||||||
|
void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data);
|
||||||
|
|
||||||
void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
|
void wm97xx_bat_set_pdata(struct wm97xx_batt_info *data)
|
||||||
{
|
{
|
||||||
gpdata = data;
|
gpdata = data;
|
||||||
|
|
328
drivers/power/z2_battery.c
Normal file
328
drivers/power/z2_battery.c
Normal file
|
@ -0,0 +1,328 @@
|
||||||
|
/*
|
||||||
|
* Battery measurement code for Zipit Z2
|
||||||
|
*
|
||||||
|
* Copyright (C) 2009 Peter Edwards <sweetlilmre@gmail.com>
|
||||||
|
*
|
||||||
|
* 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/init.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/power_supply.h>
|
||||||
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/gpio.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
#include <asm/irq.h>
|
||||||
|
#include <asm/mach/irq.h>
|
||||||
|
#include <linux/z2_battery.h>
|
||||||
|
|
||||||
|
#define Z2_DEFAULT_NAME "Z2"
|
||||||
|
|
||||||
|
struct z2_charger {
|
||||||
|
struct z2_battery_info *info;
|
||||||
|
int bat_status;
|
||||||
|
struct i2c_client *client;
|
||||||
|
struct power_supply batt_ps;
|
||||||
|
struct mutex work_lock;
|
||||||
|
struct work_struct bat_work;
|
||||||
|
};
|
||||||
|
|
||||||
|
static unsigned long z2_read_bat(struct z2_charger *charger)
|
||||||
|
{
|
||||||
|
int data;
|
||||||
|
data = i2c_smbus_read_byte_data(charger->client,
|
||||||
|
charger->info->batt_I2C_reg);
|
||||||
|
if (data < 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return data * charger->info->batt_mult / charger->info->batt_div;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int z2_batt_get_property(struct power_supply *batt_ps,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
union power_supply_propval *val)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
|
||||||
|
batt_ps);
|
||||||
|
struct z2_battery_info *info = charger->info;
|
||||||
|
|
||||||
|
switch (psp) {
|
||||||
|
case POWER_SUPPLY_PROP_STATUS:
|
||||||
|
val->intval = charger->bat_status;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_TECHNOLOGY:
|
||||||
|
val->intval = info->batt_tech;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
|
||||||
|
if (info->batt_I2C_reg >= 0)
|
||||||
|
val->intval = z2_read_bat(charger);
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
|
||||||
|
if (info->max_voltage >= 0)
|
||||||
|
val->intval = info->max_voltage;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
|
||||||
|
if (info->min_voltage >= 0)
|
||||||
|
val->intval = info->min_voltage;
|
||||||
|
else
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
case POWER_SUPPLY_PROP_PRESENT:
|
||||||
|
val->intval = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void z2_batt_ext_power_changed(struct power_supply *batt_ps)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger = container_of(batt_ps, struct z2_charger,
|
||||||
|
batt_ps);
|
||||||
|
schedule_work(&charger->bat_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void z2_batt_update(struct z2_charger *charger)
|
||||||
|
{
|
||||||
|
int old_status = charger->bat_status;
|
||||||
|
struct z2_battery_info *info;
|
||||||
|
|
||||||
|
info = charger->info;
|
||||||
|
|
||||||
|
mutex_lock(&charger->work_lock);
|
||||||
|
|
||||||
|
charger->bat_status = (info->charge_gpio >= 0) ?
|
||||||
|
(gpio_get_value(info->charge_gpio) ?
|
||||||
|
POWER_SUPPLY_STATUS_CHARGING :
|
||||||
|
POWER_SUPPLY_STATUS_DISCHARGING) :
|
||||||
|
POWER_SUPPLY_STATUS_UNKNOWN;
|
||||||
|
|
||||||
|
if (old_status != charger->bat_status) {
|
||||||
|
pr_debug("%s: %i -> %i\n", charger->batt_ps.name, old_status,
|
||||||
|
charger->bat_status);
|
||||||
|
power_supply_changed(&charger->batt_ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&charger->work_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void z2_batt_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger;
|
||||||
|
charger = container_of(work, struct z2_charger, bat_work);
|
||||||
|
z2_batt_update(charger);
|
||||||
|
}
|
||||||
|
|
||||||
|
static irqreturn_t z2_charge_switch_irq(int irq, void *devid)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger = devid;
|
||||||
|
schedule_work(&charger->bat_work);
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int z2_batt_ps_init(struct z2_charger *charger, int props)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
enum power_supply_property *prop;
|
||||||
|
struct z2_battery_info *info = charger->info;
|
||||||
|
|
||||||
|
if (info->batt_tech >= 0)
|
||||||
|
props++; /* POWER_SUPPLY_PROP_TECHNOLOGY */
|
||||||
|
if (info->batt_I2C_reg >= 0)
|
||||||
|
props++; /* POWER_SUPPLY_PROP_VOLTAGE_NOW */
|
||||||
|
if (info->max_voltage >= 0)
|
||||||
|
props++; /* POWER_SUPPLY_PROP_VOLTAGE_MAX */
|
||||||
|
if (info->min_voltage >= 0)
|
||||||
|
props++; /* POWER_SUPPLY_PROP_VOLTAGE_MIN */
|
||||||
|
|
||||||
|
prop = kzalloc(props * sizeof(*prop), GFP_KERNEL);
|
||||||
|
if (!prop)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_PRESENT;
|
||||||
|
if (info->charge_gpio >= 0)
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_STATUS;
|
||||||
|
if (info->batt_tech >= 0)
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_TECHNOLOGY;
|
||||||
|
if (info->batt_I2C_reg >= 0)
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_NOW;
|
||||||
|
if (info->max_voltage >= 0)
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MAX;
|
||||||
|
if (info->min_voltage >= 0)
|
||||||
|
prop[i++] = POWER_SUPPLY_PROP_VOLTAGE_MIN;
|
||||||
|
|
||||||
|
if (!info->batt_name) {
|
||||||
|
dev_info(&charger->client->dev,
|
||||||
|
"Please consider setting proper battery "
|
||||||
|
"name in platform definition file, falling "
|
||||||
|
"back to name \" Z2_DEFAULT_NAME \"\n");
|
||||||
|
charger->batt_ps.name = Z2_DEFAULT_NAME;
|
||||||
|
} else
|
||||||
|
charger->batt_ps.name = info->batt_name;
|
||||||
|
|
||||||
|
charger->batt_ps.properties = prop;
|
||||||
|
charger->batt_ps.num_properties = props;
|
||||||
|
charger->batt_ps.type = POWER_SUPPLY_TYPE_BATTERY;
|
||||||
|
charger->batt_ps.get_property = z2_batt_get_property;
|
||||||
|
charger->batt_ps.external_power_changed = z2_batt_ext_power_changed;
|
||||||
|
charger->batt_ps.use_for_apm = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devinit z2_batt_probe(struct i2c_client *client,
|
||||||
|
const struct i2c_device_id *id)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int props = 1; /* POWER_SUPPLY_PROP_PRESENT */
|
||||||
|
struct z2_charger *charger;
|
||||||
|
struct z2_battery_info *info = client->dev.platform_data;
|
||||||
|
|
||||||
|
if (info == NULL) {
|
||||||
|
dev_err(&client->dev,
|
||||||
|
"Please set platform device platform_data"
|
||||||
|
" to a valid z2_battery_info pointer!\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
charger = kzalloc(sizeof(*charger), GFP_KERNEL);
|
||||||
|
if (charger == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
charger->bat_status = POWER_SUPPLY_STATUS_UNKNOWN;
|
||||||
|
charger->info = info;
|
||||||
|
charger->client = client;
|
||||||
|
i2c_set_clientdata(client, charger);
|
||||||
|
|
||||||
|
mutex_init(&charger->work_lock);
|
||||||
|
|
||||||
|
if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
|
||||||
|
ret = gpio_request(info->charge_gpio, "BATT CHRG");
|
||||||
|
if (ret)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
ret = gpio_direction_input(info->charge_gpio);
|
||||||
|
if (ret)
|
||||||
|
goto err2;
|
||||||
|
|
||||||
|
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,
|
||||||
|
"AC Detect", charger);
|
||||||
|
if (ret)
|
||||||
|
goto err3;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = z2_batt_ps_init(charger, props);
|
||||||
|
if (ret)
|
||||||
|
goto err3;
|
||||||
|
|
||||||
|
INIT_WORK(&charger->bat_work, z2_batt_work);
|
||||||
|
|
||||||
|
ret = power_supply_register(&client->dev, &charger->batt_ps);
|
||||||
|
if (ret)
|
||||||
|
goto err4;
|
||||||
|
|
||||||
|
schedule_work(&charger->bat_work);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
err4:
|
||||||
|
kfree(charger->batt_ps.properties);
|
||||||
|
err3:
|
||||||
|
if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
|
||||||
|
free_irq(gpio_to_irq(info->charge_gpio), charger);
|
||||||
|
err2:
|
||||||
|
if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio))
|
||||||
|
gpio_free(info->charge_gpio);
|
||||||
|
err:
|
||||||
|
kfree(charger);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __devexit z2_batt_remove(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger = i2c_get_clientdata(client);
|
||||||
|
struct z2_battery_info *info = charger->info;
|
||||||
|
|
||||||
|
flush_scheduled_work();
|
||||||
|
power_supply_unregister(&charger->batt_ps);
|
||||||
|
|
||||||
|
kfree(charger->batt_ps.properties);
|
||||||
|
if (info->charge_gpio >= 0 && gpio_is_valid(info->charge_gpio)) {
|
||||||
|
free_irq(gpio_to_irq(info->charge_gpio), charger);
|
||||||
|
gpio_free(info->charge_gpio);
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(charger);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM
|
||||||
|
static int z2_batt_suspend(struct i2c_client *client, pm_message_t state)
|
||||||
|
{
|
||||||
|
flush_scheduled_work();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int z2_batt_resume(struct i2c_client *client)
|
||||||
|
{
|
||||||
|
struct z2_charger *charger = i2c_get_clientdata(client);
|
||||||
|
|
||||||
|
schedule_work(&charger->bat_work);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define z2_batt_suspend NULL
|
||||||
|
#define z2_batt_resume NULL
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const struct i2c_device_id z2_batt_id[] = {
|
||||||
|
{ "aer915", 0 },
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct i2c_driver z2_batt_driver = {
|
||||||
|
.driver = {
|
||||||
|
.name = "z2-battery",
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
},
|
||||||
|
.probe = z2_batt_probe,
|
||||||
|
.remove = z2_batt_remove,
|
||||||
|
.suspend = z2_batt_suspend,
|
||||||
|
.resume = z2_batt_resume,
|
||||||
|
.id_table = z2_batt_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init z2_batt_init(void)
|
||||||
|
{
|
||||||
|
return i2c_add_driver(&z2_batt_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit z2_batt_exit(void)
|
||||||
|
{
|
||||||
|
i2c_del_driver(&z2_batt_driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(z2_batt_init);
|
||||||
|
module_exit(z2_batt_exit);
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Peter Edwards <sweetlilmre@gmail.com>");
|
||||||
|
MODULE_DESCRIPTION("Zipit Z2 battery driver");
|
8
include/linux/ds2782_battery.h
Normal file
8
include/linux/ds2782_battery.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef __LINUX_DS2782_BATTERY_H
|
||||||
|
#define __LINUX_DS2782_BATTERY_H
|
||||||
|
|
||||||
|
struct ds278x_platform_data {
|
||||||
|
int rsns;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -24,6 +24,8 @@ struct pda_power_pdata {
|
||||||
int (*is_usb_online)(void);
|
int (*is_usb_online)(void);
|
||||||
void (*set_charge)(int flags);
|
void (*set_charge)(int flags);
|
||||||
void (*exit)(struct device *dev);
|
void (*exit)(struct device *dev);
|
||||||
|
int (*suspend)(pm_message_t state);
|
||||||
|
int (*resume)(void);
|
||||||
|
|
||||||
char **supplied_to;
|
char **supplied_to;
|
||||||
size_t num_supplicants;
|
size_t num_supplicants;
|
||||||
|
|
|
@ -114,6 +114,7 @@ enum power_supply_property {
|
||||||
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
|
||||||
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
|
||||||
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
|
||||||
|
POWER_SUPPLY_PROP_TYPE, /* use power_supply.type instead */
|
||||||
/* Properties of type `const char *' */
|
/* Properties of type `const char *' */
|
||||||
POWER_SUPPLY_PROP_MODEL_NAME,
|
POWER_SUPPLY_PROP_MODEL_NAME,
|
||||||
POWER_SUPPLY_PROP_MANUFACTURER,
|
POWER_SUPPLY_PROP_MANUFACTURER,
|
||||||
|
@ -144,6 +145,11 @@ struct power_supply {
|
||||||
int (*get_property)(struct power_supply *psy,
|
int (*get_property)(struct power_supply *psy,
|
||||||
enum power_supply_property psp,
|
enum power_supply_property psp,
|
||||||
union power_supply_propval *val);
|
union power_supply_propval *val);
|
||||||
|
int (*set_property)(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp,
|
||||||
|
const union power_supply_propval *val);
|
||||||
|
int (*property_is_writeable)(struct power_supply *psy,
|
||||||
|
enum power_supply_property psp);
|
||||||
void (*external_power_changed)(struct power_supply *psy);
|
void (*external_power_changed)(struct power_supply *psy);
|
||||||
void (*set_charged)(struct power_supply *psy);
|
void (*set_charged)(struct power_supply *psy);
|
||||||
|
|
||||||
|
|
17
include/linux/z2_battery.h
Normal file
17
include/linux/z2_battery.h
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#ifndef _LINUX_Z2_BATTERY_H
|
||||||
|
#define _LINUX_Z2_BATTERY_H
|
||||||
|
|
||||||
|
struct z2_battery_info {
|
||||||
|
int batt_I2C_bus;
|
||||||
|
int batt_I2C_addr;
|
||||||
|
int batt_I2C_reg;
|
||||||
|
int charge_gpio;
|
||||||
|
int min_voltage;
|
||||||
|
int max_voltage;
|
||||||
|
int batt_div;
|
||||||
|
int batt_mult;
|
||||||
|
int batt_tech;
|
||||||
|
char *batt_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
Loading…
Reference in a new issue