diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/Kconfig | 16 | ||||
-rw-r--r-- | drivers/power/Makefile | 2 | ||||
-rw-r--r-- | drivers/power/bq27x00_battery.c | 11 | ||||
-rw-r--r-- | drivers/power/ds2760_battery.c | 6 | ||||
-rw-r--r-- | drivers/power/ds2780_battery.c | 853 | ||||
-rw-r--r-- | drivers/power/gpio-charger.c | 15 | ||||
-rw-r--r-- | drivers/power/intel_mid_battery.c | 2 | ||||
-rw-r--r-- | drivers/power/isp1704_charger.c | 22 | ||||
-rw-r--r-- | drivers/power/max8903_charger.c | 391 | ||||
-rw-r--r-- | drivers/power/max8925_power.c | 10 | ||||
-rw-r--r-- | drivers/power/test_power.c | 276 | ||||
-rw-r--r-- | drivers/power/z2_battery.c | 20 |
12 files changed, 1593 insertions, 31 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 52a462fc6b84..e57b50b38565 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -68,6 +68,13 @@ config BATTERY_DS2760 help Say Y here to enable support for batteries with ds2760 chip. +config BATTERY_DS2780 + tristate "DS2780 battery driver" + select W1 + select W1_SLAVE_DS2780 + help + Say Y here to enable support for batteries with ds2780 chip. + config BATTERY_DS2782 tristate "DS2782/DS2786 standalone gas-gauge" depends on I2C @@ -203,6 +210,15 @@ config CHARGER_ISP1704 Say Y to enable support for USB Charger Detection with ISP1707/ISP1704 USB transceivers. +config CHARGER_MAX8903 + tristate "MAX8903 Battery DC-DC Charger for USB and Adapter Power" + depends on GENERIC_HARDIRQS + help + Say Y to enable support for the MAX8903 DC-DC charger and sysfs. + The driver supports controlling charger-enable and current-limit + pins based on the status of charger connections with interrupt + handlers. + config CHARGER_TWL4030 tristate "OMAP TWL4030 BCI charger driver" depends on TWL4030_CORE diff --git a/drivers/power/Makefile b/drivers/power/Makefile index 8385bfae8728..009a90fa8ac9 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_WM8350_POWER) += wm8350_power.o obj-$(CONFIG_TEST_POWER) += test_power.o obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o +obj-$(CONFIG_BATTERY_DS2780) += ds2780_battery.o obj-$(CONFIG_BATTERY_DS2782) += ds2782_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o @@ -32,5 +33,6 @@ obj-$(CONFIG_CHARGER_PCF50633) += pcf50633-charger.o obj-$(CONFIG_BATTERY_JZ4740) += jz4740-battery.o 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_GPIO) += gpio-charger.o diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 59e68dbd028b..bb16f5b7e167 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -4,6 +4,7 @@ * Copyright (C) 2008 Rodolfo Giometti <giometti@linux.it> * Copyright (C) 2008 Eurotech S.p.A. <info@eurotech.it> * Copyright (C) 2010-2011 Lars-Peter Clausen <lars@metafoo.de> + * Copyright (C) 2011 Pali Rohár <pali.rohar@gmail.com> * * Based on a previous work by Copyright (C) 2008 Texas Instruments, Inc. * @@ -76,7 +77,7 @@ struct bq27x00_reg_cache { int time_to_empty_avg; int time_to_full; int charge_full; - int charge_counter; + int cycle_count; int capacity; int flags; @@ -115,7 +116,7 @@ static enum power_supply_property bq27x00_battery_props[] = { POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, - POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CYCLE_COUNT, POWER_SUPPLY_PROP_ENERGY_NOW, }; @@ -267,7 +268,7 @@ static void bq27x00_update(struct bq27x00_device_info *di) 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.charge_counter = bq27x00_battery_read_cyct(di); + cache.cycle_count = bq27x00_battery_read_cyct(di); if (!is_bq27500) cache.current_now = bq27x00_read(di, BQ27x00_REG_AI, false); @@ -496,8 +497,8 @@ static int bq27x00_battery_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ret = bq27x00_simple_value(di->charge_design_full, val); break; - case POWER_SUPPLY_PROP_CHARGE_COUNTER: - ret = bq27x00_simple_value(di->cache.charge_counter, val); + case POWER_SUPPLY_PROP_CYCLE_COUNT: + ret = bq27x00_simple_value(di->cache.cycle_count, val); break; case POWER_SUPPLY_PROP_ENERGY_NOW: ret = bq27x00_battery_energy(di, val); diff --git a/drivers/power/ds2760_battery.c b/drivers/power/ds2760_battery.c index e534290f3256..f2c9cc33c0f9 100644 --- a/drivers/power/ds2760_battery.c +++ b/drivers/power/ds2760_battery.c @@ -86,7 +86,11 @@ static int rated_capacities[] = { 920, /* NEC */ 1440, /* Samsung */ 1440, /* BYD */ +#ifdef CONFIG_MACH_H4700 + 1800, /* HP iPAQ hx4700 3.7V 1800mAh (359113-001) */ +#else 1440, /* Lishen */ +#endif 1440, /* NEC */ 2880, /* Samsung */ 2880, /* BYD */ @@ -186,7 +190,7 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di) scale[0] = di->full_active_uAh; for (i = 1; i < 5; i++) - scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i]; + scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 1 + i]; di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10); di->full_active_uAh *= 1000; /* convert to µAh */ diff --git a/drivers/power/ds2780_battery.c b/drivers/power/ds2780_battery.c new file mode 100644 index 000000000000..1fefe82e12e3 --- /dev/null +++ b/drivers/power/ds2780_battery.c @@ -0,0 +1,853 @@ +/* + * 1-wire client/driver for the Maxim/Dallas DS2780 Stand-Alone Fuel Gauge IC + * + * Copyright (C) 2010 Indesign, LLC + * + * Author: Clifton Barnes <cabarnes@indesign-llc.com> + * + * Based on ds2760_battery and ds2782_battery drivers + * + * 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/param.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/idr.h> + +#include "../w1/w1.h" +#include "../w1/slaves/w1_ds2780.h" + +/* Current unit measurement in uA for a 1 milli-ohm sense resistor */ +#define DS2780_CURRENT_UNITS 1563 +/* Charge unit measurement in uAh for a 1 milli-ohm sense resistor */ +#define DS2780_CHARGE_UNITS 6250 +/* Number of bytes in user EEPROM space */ +#define DS2780_USER_EEPROM_SIZE (DS2780_EEPROM_BLOCK0_END - \ + DS2780_EEPROM_BLOCK0_START + 1) +/* Number of bytes in parameter EEPROM space */ +#define DS2780_PARAM_EEPROM_SIZE (DS2780_EEPROM_BLOCK1_END - \ + DS2780_EEPROM_BLOCK1_START + 1) + +struct ds2780_device_info { + struct device *dev; + struct power_supply bat; + struct device *w1_dev; +}; + +enum current_types { + CURRENT_NOW, + CURRENT_AVG, +}; + +static const char model[] = "DS2780"; +static const char manufacturer[] = "Maxim/Dallas"; + +static inline struct ds2780_device_info *to_ds2780_device_info( + struct power_supply *psy) +{ + return container_of(psy, struct ds2780_device_info, bat); +} + +static inline struct power_supply *to_power_supply(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static inline int ds2780_read8(struct device *dev, u8 *val, int addr) +{ + return w1_ds2780_io(dev, val, addr, sizeof(u8), 0); +} + +static int ds2780_read16(struct device *dev, s16 *val, int addr) +{ + int ret; + u8 raw[2]; + + ret = w1_ds2780_io(dev, raw, addr, sizeof(u8) * 2, 0); + if (ret < 0) + return ret; + + *val = (raw[0] << 8) | raw[1]; + + return 0; +} + +static inline int ds2780_read_block(struct device *dev, u8 *val, int addr, + size_t count) +{ + return w1_ds2780_io(dev, val, addr, count, 0); +} + +static inline int ds2780_write(struct device *dev, u8 *val, int addr, + size_t count) +{ + return w1_ds2780_io(dev, val, addr, count, 1); +} + +static inline int ds2780_store_eeprom(struct device *dev, int addr) +{ + return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_COPY_DATA); +} + +static inline int ds2780_recall_eeprom(struct device *dev, int addr) +{ + return w1_ds2780_eeprom_cmd(dev, addr, W1_DS2780_RECALL_DATA); +} + +static int ds2780_save_eeprom(struct ds2780_device_info *dev_info, int reg) +{ + int ret; + + ret = ds2780_store_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + ret = ds2780_recall_eeprom(dev_info->w1_dev, reg); + if (ret < 0) + return ret; + + return 0; +} + +/* Set sense resistor value in mhos */ +static int ds2780_set_sense_register(struct ds2780_device_info *dev_info, + u8 conductance) +{ + int ret; + + ret = ds2780_write(dev_info->w1_dev, &conductance, + DS2780_RSNSP_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_RSNSP_REG); +} + +/* Get RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2780_get_rsgain_register(struct ds2780_device_info *dev_info, + u16 *rsgain) +{ + return ds2780_read16(dev_info->w1_dev, rsgain, DS2780_RSGAIN_MSB_REG); +} + +/* Set RSGAIN value from 0 to 1.999 in steps of 0.001 */ +static int ds2780_set_rsgain_register(struct ds2780_device_info *dev_info, + u16 rsgain) +{ + int ret; + u8 raw[] = {rsgain >> 8, rsgain & 0xFF}; + + ret = ds2780_write(dev_info->w1_dev, raw, + DS2780_RSGAIN_MSB_REG, sizeof(u8) * 2); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_RSGAIN_MSB_REG); +} + +static int ds2780_get_voltage(struct ds2780_device_info *dev_info, + int *voltage_uV) +{ + int ret; + s16 voltage_raw; + + /* + * The voltage value is located in 10 bits across the voltage MSB + * and LSB registers in two's compliment form + * Sign bit of the voltage value is in bit 7 of the voltage MSB register + * Bits 9 - 3 of the voltage value are in bits 6 - 0 of the + * voltage MSB register + * Bits 2 - 0 of the voltage value are in bits 7 - 5 of the + * voltage LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &voltage_raw, + DS2780_VOLT_MSB_REG); + if (ret < 0) + return ret; + + /* + * DS2780 reports voltage in units of 4.88mV, but the battery class + * reports in units of uV, so convert by multiplying by 4880. + */ + *voltage_uV = (voltage_raw / 32) * 4880; + return 0; +} + +static int ds2780_get_temperature(struct ds2780_device_info *dev_info, + int *temperature) +{ + int ret; + s16 temperature_raw; + + /* + * The temperature value is located in 10 bits across the temperature + * MSB and LSB registers in two's compliment form + * Sign bit of the temperature value is in bit 7 of the temperature + * MSB register + * Bits 9 - 3 of the temperature value are in bits 6 - 0 of the + * temperature MSB register + * Bits 2 - 0 of the temperature value are in bits 7 - 5 of the + * temperature LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &temperature_raw, + DS2780_TEMP_MSB_REG); + if (ret < 0) + return ret; + + /* + * Temperature is measured in units of 0.125 degrees celcius, the + * power_supply class measures temperature in tenths of degrees + * celsius. The temperature value is stored as a 10 bit number, plus + * sign in the upper bits of a 16 bit register. + */ + *temperature = ((temperature_raw / 32) * 125) / 100; + return 0; +} + +static int ds2780_get_current(struct ds2780_device_info *dev_info, + enum current_types type, int *current_uA) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw, reg_msb; + + /* + * The units of measurement for current are dependent on the value of + * the sense resistor. + */ + ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -ENXIO; + } + sense_res = 1000 / sense_res_raw; + + if (type == CURRENT_NOW) + reg_msb = DS2780_CURRENT_MSB_REG; + else if (type == CURRENT_AVG) + reg_msb = DS2780_IAVG_MSB_REG; + else + return -EINVAL; + + /* + * The current value is located in 16 bits across the current MSB + * and LSB registers in two's compliment form + * Sign bit of the current value is in bit 7 of the current MSB register + * Bits 14 - 8 of the current value are in bits 6 - 0 of the current + * MSB register + * Bits 7 - 0 of the current value are in bits 7 - 0 of the current + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, reg_msb); + if (ret < 0) + return ret; + + *current_uA = current_raw * (DS2780_CURRENT_UNITS / sense_res); + return 0; +} + +static int ds2780_get_accumulated_current(struct ds2780_device_info *dev_info, + int *accumulated_current) +{ + int ret, sense_res; + s16 current_raw; + u8 sense_res_raw; + + /* + * The units of measurement for accumulated current are dependent on + * the value of the sense resistor. + */ + ret = ds2780_read8(dev_info->w1_dev, &sense_res_raw, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + if (sense_res_raw == 0) { + dev_err(dev_info->dev, "sense resistor value is 0\n"); + return -ENXIO; + } + sense_res = 1000 / sense_res_raw; + + /* + * The ACR value is located in 16 bits across the ACR MSB and + * LSB registers + * Bits 15 - 8 of the ACR value are in bits 7 - 0 of the ACR + * MSB register + * Bits 7 - 0 of the ACR value are in bits 7 - 0 of the ACR + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, ¤t_raw, DS2780_ACR_MSB_REG); + if (ret < 0) + return ret; + + *accumulated_current = current_raw * (DS2780_CHARGE_UNITS / sense_res); + return 0; +} + +static int ds2780_get_capacity(struct ds2780_device_info *dev_info, + int *capacity) +{ + int ret; + u8 raw; + + ret = ds2780_read8(dev_info->w1_dev, &raw, DS2780_RARC_REG); + if (ret < 0) + return ret; + + *capacity = raw; + return raw; +} + +static int ds2780_get_status(struct ds2780_device_info *dev_info, int *status) +{ + int ret, current_uA, capacity; + + ret = ds2780_get_current(dev_info, CURRENT_NOW, ¤t_uA); + if (ret < 0) + return ret; + + ret = ds2780_get_capacity(dev_info, &capacity); + if (ret < 0) + return ret; + + if (capacity == 100) + *status = POWER_SUPPLY_STATUS_FULL; + else if (current_uA == 0) + *status = POWER_SUPPLY_STATUS_NOT_CHARGING; + else if (current_uA < 0) + *status = POWER_SUPPLY_STATUS_DISCHARGING; + else + *status = POWER_SUPPLY_STATUS_CHARGING; + + return 0; +} + +static int ds2780_get_charge_now(struct ds2780_device_info *dev_info, + int *charge_now) +{ + int ret; + u16 charge_raw; + + /* + * The RAAC value is located in 16 bits across the RAAC MSB and + * LSB registers + * Bits 15 - 8 of the RAAC value are in bits 7 - 0 of the RAAC + * MSB register + * Bits 7 - 0 of the RAAC value are in bits 7 - 0 of the RAAC + * LSB register + */ + ret = ds2780_read16(dev_info->w1_dev, &charge_raw, DS2780_RAAC_MSB_REG); + if (ret < 0) + return ret; + + *charge_now = charge_raw * 1600; + return 0; +} + +static int ds2780_get_control_register(struct ds2780_device_info *dev_info, + u8 *control_reg) +{ + return ds2780_read8(dev_info->w1_dev, control_reg, DS2780_CONTROL_REG); +} + +static int ds2780_set_control_register(struct ds2780_device_info *dev_info, + u8 control_reg) +{ + int ret; + + ret = ds2780_write(dev_info->w1_dev, &control_reg, + DS2780_CONTROL_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return ds2780_save_eeprom(dev_info, DS2780_CONTROL_REG); +} + +static int ds2780_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + switch (psp) { + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + ret = ds2780_get_voltage(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_TEMP: + ret = ds2780_get_temperature(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_MODEL_NAME: + val->strval = model; + break; + + case POWER_SUPPLY_PROP_MANUFACTURER: + val->strval = manufacturer; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + ret = ds2780_get_current(dev_info, CURRENT_NOW, &val->intval); + break; + + case POWER_SUPPLY_PROP_CURRENT_AVG: + ret = ds2780_get_current(dev_info, CURRENT_AVG, &val->intval); + break; + + case POWER_SUPPLY_PROP_STATUS: + ret = ds2780_get_status(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CAPACITY: + ret = ds2780_get_capacity(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_COUNTER: + ret = ds2780_get_accumulated_current(dev_info, &val->intval); + break; + + case POWER_SUPPLY_PROP_CHARGE_NOW: + ret = ds2780_get_charge_now(dev_info, &val->intval); + break; + + default: + ret = -EINVAL; + } + + return ret; +} + +static enum power_supply_property ds2780_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_MODEL_NAME, + POWER_SUPPLY_PROP_MANUFACTURER, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CURRENT_AVG, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_CHARGE_COUNTER, + POWER_SUPPLY_PROP_CHARGE_NOW, +}; + +static ssize_t ds2780_get_pmod_enabled(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 control_reg; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + /* Get power mode */ + ret = ds2780_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", + !!(control_reg & DS2780_CONTROL_REG_PMOD)); +} + +static ssize_t ds2780_set_pmod_enabled(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 control_reg, new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + /* Set power mode */ + ret = ds2780_get_control_register(dev_info, &control_reg); + if (ret < 0) + return ret; + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pmod setting (0 or 1)\n"); + return -EINVAL; + } + + if (new_setting) + control_reg |= DS2780_CONTROL_REG_PMOD; + else + control_reg &= ~DS2780_CONTROL_REG_PMOD; + + ret = ds2780_set_control_register(dev_info, control_reg); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sense_resistor; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_read8(dev_info->w1_dev, &sense_resistor, DS2780_RSNSP_REG); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sense_resistor); + return ret; +} + +static ssize_t ds2780_set_sense_resistor_value(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + ret = ds2780_set_sense_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_rsgain_setting(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u16 rsgain; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_get_rsgain_register(dev_info, &rsgain); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", rsgain); +} + +static ssize_t ds2780_set_rsgain_setting(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u16 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou16(buf, 0, &new_setting); + if (ret < 0) + return ret; + + /* Gain can only be from 0 to 1.999 in steps of .001 */ + if (new_setting > 1999) { + dev_err(dev_info->dev, "Invalid rsgain setting (0 - 1999)\n"); + return -EINVAL; + } + + ret = ds2780_set_rsgain_register(dev_info, new_setting); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_get_pio_pin(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int ret; + u8 sfr; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = ds2780_read8(dev_info->w1_dev, &sfr, DS2780_SFR_REG); + if (ret < 0) + return ret; + + ret = sprintf(buf, "%d\n", sfr & DS2780_SFR_REG_PIOSC); + return ret; +} + +static ssize_t ds2780_set_pio_pin(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + int ret; + u8 new_setting; + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + ret = kstrtou8(buf, 0, &new_setting); + if (ret < 0) + return ret; + + if ((new_setting != 0) && (new_setting != 1)) { + dev_err(dev_info->dev, "Invalid pio_pin setting (0 or 1)\n"); + return -EINVAL; + } + + ret = ds2780_write(dev_info->w1_dev, &new_setting, + DS2780_SFR_REG, sizeof(u8)); + if (ret < 0) + return ret; + + return count; +} + +static ssize_t ds2780_read_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK1_END - + DS2780_EEPROM_BLOCK1_START + 1 - off); + + return ds2780_read_block(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK1_START + off, count); +} + +static ssize_t ds2780_write_param_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK1_END - + DS2780_EEPROM_BLOCK1_START + 1 - off); + + ret = ds2780_write(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK1_START + off, count); + if (ret < 0) + return ret; + + ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK1_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2780_param_eeprom_bin_attr = { + .attr = { + .name = "param_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2780_EEPROM_BLOCK1_END - DS2780_EEPROM_BLOCK1_START + 1, + .read = ds2780_read_param_eeprom_bin, + .write = ds2780_write_param_eeprom_bin, +}; + +static ssize_t ds2780_read_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK0_END - + DS2780_EEPROM_BLOCK0_START + 1 - off); + + return ds2780_read_block(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK0_START + off, count); + +} + +static ssize_t ds2780_write_user_eeprom_bin(struct file *filp, + struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct power_supply *psy = to_power_supply(dev); + struct ds2780_device_info *dev_info = to_ds2780_device_info(psy); + int ret; + + count = min_t(loff_t, count, + DS2780_EEPROM_BLOCK0_END - + DS2780_EEPROM_BLOCK0_START + 1 - off); + + ret = ds2780_write(dev_info->w1_dev, buf, + DS2780_EEPROM_BLOCK0_START + off, count); + if (ret < 0) + return ret; + + ret = ds2780_save_eeprom(dev_info, DS2780_EEPROM_BLOCK0_START); + if (ret < 0) + return ret; + + return count; +} + +static struct bin_attribute ds2780_user_eeprom_bin_attr = { + .attr = { + .name = "user_eeprom", + .mode = S_IRUGO | S_IWUSR, + }, + .size = DS2780_EEPROM_BLOCK0_END - DS2780_EEPROM_BLOCK0_START + 1, + .read = ds2780_read_user_eeprom_bin, + .write = ds2780_write_user_eeprom_bin, +}; + +static DEVICE_ATTR(pmod_enabled, S_IRUGO | S_IWUSR, ds2780_get_pmod_enabled, + ds2780_set_pmod_enabled); +static DEVICE_ATTR(sense_resistor_value, S_IRUGO | S_IWUSR, + ds2780_get_sense_resistor_value, ds2780_set_sense_resistor_value); +static DEVICE_ATTR(rsgain_setting, S_IRUGO | S_IWUSR, ds2780_get_rsgain_setting, + ds2780_set_rsgain_setting); +static DEVICE_ATTR(pio_pin, S_IRUGO | S_IWUSR, ds2780_get_pio_pin, + ds2780_set_pio_pin); + + +static struct attribute *ds2780_attributes[] = { + &dev_attr_pmod_enabled.attr, + &dev_attr_sense_resistor_value.attr, + &dev_attr_rsgain_setting.attr, + &dev_attr_pio_pin.attr, + NULL +}; + +static const struct attribute_group ds2780_attr_group = { + .attrs = ds2780_attributes, +}; + +static int __devinit ds2780_battery_probe(struct platform_device *pdev) +{ + int ret = 0; + struct ds2780_device_info *dev_info; + + dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL); + if (!dev_info) { + ret = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, dev_info); + + dev_info->dev = &pdev->dev; + dev_info->w1_dev = pdev->dev.parent; + dev_info->bat.name = dev_name(&pdev->dev); + dev_info->bat.type = POWER_SUPPLY_TYPE_BATTERY; + dev_info->bat.properties = ds2780_battery_props; + dev_info->bat.num_properties = ARRAY_SIZE(ds2780_battery_props); + dev_info->bat.get_property = ds2780_battery_get_property; + + ret = power_supply_register(&pdev->dev, &dev_info->bat); + if (ret) { + dev_err(dev_info->dev, "failed to register battery\n"); + goto fail_free_info; + } + + ret = sysfs_create_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + if (ret) { + dev_err(dev_info->dev, "failed to create sysfs group\n"); + goto fail_unregister; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2780_param_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create param eeprom bin file"); + goto fail_remove_group; + } + + ret = sysfs_create_bin_file(&dev_info->bat.dev->kobj, + &ds2780_user_eeprom_bin_attr); + if (ret) { + dev_err(dev_info->dev, + "failed to create user eeprom bin file"); + goto fail_remove_bin_file; + } + + return 0; + +fail_remove_bin_file: + sysfs_remove_bin_file(&dev_info->bat.dev->kobj, + &ds2780_param_eeprom_bin_attr); +fail_remove_group: + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); +fail_unregister: + power_supply_unregister(&dev_info->bat); +fail_free_info: + kfree(dev_info); +fail: + return ret; +} + +static int __devexit ds2780_battery_remove(struct platform_device *pdev) +{ + struct ds2780_device_info *dev_info = platform_get_drvdata(pdev); + + /* remove attributes */ + sysfs_remove_group(&dev_info->bat.dev->kobj, &ds2780_attr_group); + + power_supply_unregister(&dev_info->bat); + + kfree(dev_info); + 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, +}; + +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_LICENSE("GPL"); +MODULE_AUTHOR("Clifton Barnes <cabarnes@indesign-llc.com>"); +MODULE_DESCRIPTION("Maxim/Dallas DS2780 Stand-Alone Fuel Gauage IC driver"); diff --git a/drivers/power/gpio-charger.c b/drivers/power/gpio-charger.c index 25b88ac1d44c..718f2c537827 100644 --- a/drivers/power/gpio-charger.c +++ b/drivers/power/gpio-charger.c @@ -161,12 +161,27 @@ static int __devexit gpio_charger_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int gpio_charger_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct gpio_charger *gpio_charger = platform_get_drvdata(pdev); + + power_supply_changed(&gpio_charger->charger); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(gpio_charger_pm_ops, NULL, gpio_charger_resume); + static struct platform_driver gpio_charger_driver = { .probe = gpio_charger_probe, .remove = __devexit_p(gpio_charger_remove), .driver = { .name = "gpio-charger", .owner = THIS_MODULE, + .pm = &gpio_charger_pm_ops, }, }; diff --git a/drivers/power/intel_mid_battery.c b/drivers/power/intel_mid_battery.c index bce3a01da2f0..cffcb7c00b00 100644 --- a/drivers/power/intel_mid_battery.c +++ b/drivers/power/intel_mid_battery.c @@ -522,7 +522,7 @@ static int pmic_battery_set_charger(struct pmic_power_module_info *pbi, if (retval) { dev_warn(pbi->dev, "%s(): ipc pmic read failed\n", __func__); - return retval;; + return retval; } return 0; diff --git a/drivers/power/isp1704_charger.c b/drivers/power/isp1704_charger.c index 2ad9b14a5ce3..f6d72b402a8e 100644 --- a/drivers/power/isp1704_charger.c +++ b/drivers/power/isp1704_charger.c @@ -33,6 +33,7 @@ #include <linux/usb/ulpi.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> +#include <linux/power/isp1704_charger.h> /* Vendor specific Power Control register */ #define ISP1704_PWR_CTRL 0x3d @@ -71,6 +72,18 @@ struct isp1704_charger { }; /* + * Disable/enable the power from the isp1704 if a function for it + * has been provided with platform data. + */ +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) + board->set_power(on); +} + +/* * Determine is the charging port DCP (dedicated charger) or CDP (Host/HUB * chargers). * @@ -222,6 +235,9 @@ static void isp1704_charger_work(struct work_struct *data) mutex_lock(&lock); + if (event != USB_EVENT_NONE) + isp1704_charger_set_power(isp, 1); + switch (event) { case USB_EVENT_VBUS: isp->online = true; @@ -269,6 +285,8 @@ static void isp1704_charger_work(struct work_struct *data) */ if (isp->otg->gadget) usb_gadget_disconnect(isp->otg->gadget); + + isp1704_charger_set_power(isp, 0); break; case USB_EVENT_ENUMERATED: if (isp->present) @@ -394,6 +412,8 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) isp->dev = &pdev->dev; platform_set_drvdata(pdev, isp); + isp1704_charger_set_power(isp, 1); + ret = isp1704_test_ulpi(isp); if (ret < 0) goto fail1; @@ -434,6 +454,7 @@ static int __devinit isp1704_charger_probe(struct platform_device *pdev) /* Detect charger if VBUS is valid (the cable was already plugged). */ ret = otg_io_read(isp->otg, ULPI_USB_INT_STS); + isp1704_charger_set_power(isp, 0); if ((ret & ULPI_INT_VBUS_VALID) && !isp->otg->default_a) { isp->event = USB_EVENT_VBUS; schedule_work(&isp->work); @@ -459,6 +480,7 @@ static int __devexit isp1704_charger_remove(struct platform_device *pdev) otg_unregister_notifier(isp->otg, &isp->nb); power_supply_unregister(&isp->psy); otg_put_transceiver(isp->otg); + isp1704_charger_set_power(isp, 0); kfree(isp); return 0; diff --git a/drivers/power/max8903_charger.c b/drivers/power/max8903_charger.c new file mode 100644 index 000000000000..33ff0e37809e --- /dev/null +++ b/drivers/power/max8903_charger.c @@ -0,0 +1,391 @@ +/* + * max8903_charger.c - Maxim 8903 USB/Adapter Charger Driver + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/power_supply.h> +#include <linux/platform_device.h> +#include <linux/power/max8903_charger.h> + +struct max8903_data { + struct max8903_pdata *pdata; + struct device *dev; + struct power_supply psy; + bool fault; + bool usb_in; + bool ta_in; +}; + +static enum power_supply_property max8903_charger_props[] = { + POWER_SUPPLY_PROP_STATUS, /* Charger status output */ + POWER_SUPPLY_PROP_ONLINE, /* External power source */ + POWER_SUPPLY_PROP_HEALTH, /* Fault or OK */ +}; + +static int max8903_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct max8903_data *data = container_of(psy, + struct max8903_data, psy); + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + if (data->pdata->chg) { + if (gpio_get_value(data->pdata->chg) == 0) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (data->usb_in || data->ta_in) + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = 0; + if (data->usb_in || data->ta_in) + val->intval = 1; + break; + case POWER_SUPPLY_PROP_HEALTH: + val->intval = POWER_SUPPLY_HEALTH_GOOD; + if (data->fault) + val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; + break; + default: + return -EINVAL; + } + return 0; +} + +static irqreturn_t max8903_dcin(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool ta_in; + enum power_supply_type old_type; + + ta_in = gpio_get_value(pdata->dok) ? false : true; + + if (ta_in == data->ta_in) + return IRQ_HANDLED; + + data->ta_in = ta_in; + + /* Set Current-Limit-Mode 1:DC 0:USB */ + if (pdata->dcm) + gpio_set_value(pdata->dcm, ta_in ? 1 : 0); + + /* Charger Enable / Disable (cen is negated) */ + if (pdata->cen) + gpio_set_value(pdata->cen, ta_in ? 0 : + (data->usb_in ? 0 : 1)); + + dev_dbg(data->dev, "TA(DC-IN) Charger %s.\n", ta_in ? + "Connected" : "Disconnected"); + + old_type = data->psy.type; + + if (data->ta_in) + data->psy.type = POWER_SUPPLY_TYPE_MAINS; + else if (data->usb_in) + data->psy.type = POWER_SUPPLY_TYPE_USB; + else + data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + + if (old_type != data->psy.type) + power_supply_changed(&data->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t max8903_usbin(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool usb_in; + enum power_supply_type old_type; + + usb_in = gpio_get_value(pdata->uok) ? false : true; + + if (usb_in == data->usb_in) + return IRQ_HANDLED; + + data->usb_in = usb_in; + + /* Do not touch Current-Limit-Mode */ + + /* Charger Enable / Disable (cen is negated) */ + if (pdata->cen) + gpio_set_value(pdata->cen, usb_in ? 0 : + (data->ta_in ? 0 : 1)); + + dev_dbg(data->dev, "USB Charger %s.\n", usb_in ? + "Connected" : "Disconnected"); + + old_type = data->psy.type; + + if (data->ta_in) + data->psy.type = POWER_SUPPLY_TYPE_MAINS; + else if (data->usb_in) + data->psy.type = POWER_SUPPLY_TYPE_USB; + else + data->psy.type = POWER_SUPPLY_TYPE_BATTERY; + + if (old_type != data->psy.type) + power_supply_changed(&data->psy); + + return IRQ_HANDLED; +} + +static irqreturn_t max8903_fault(int irq, void *_data) +{ + struct max8903_data *data = _data; + struct max8903_pdata *pdata = data->pdata; + bool fault; + + fault = gpio_get_value(pdata->flt) ? false : true; + + if (fault == data->fault) + return IRQ_HANDLED; + + data->fault = fault; + + if (fault) + dev_err(data->dev, "Charger suffers a fault and stops.\n"); + else + dev_err(data->dev, "Charger recovered from a fault.\n"); + + return IRQ_HANDLED; +} + +static __devinit int max8903_probe(struct platform_device *pdev) +{ + struct max8903_data *data; + struct device *dev = &pdev->dev; + struct max8903_pdata *pdata = pdev->dev.platform_data; + int ret = 0; + int gpio; + int ta_in = 0; + int usb_in = 0; + + data = kzalloc(sizeof(struct max8903_data), GFP_KERNEL); + if (data == NULL) { + dev_err(dev, "Cannot allocate memory.\n"); + return -ENOMEM; + } + data->pdata = pdata; + data->dev = dev; + platform_set_drvdata(pdev, data); + + if (pdata->dc_valid == false && pdata->usb_valid == false) { + dev_err(dev, "No valid power sources.\n"); + ret = -EINVAL; + goto err; + } + + if (pdata->dc_valid) { + if (pdata->dok && gpio_is_valid(pdata->dok) && + pdata->dcm && gpio_is_valid(pdata->dcm)) { + gpio = pdata->dok; /* PULL_UPed Interrupt */ + ta_in = gpio_get_value(gpio) ? 0 : 1; + + gpio = pdata->dcm; /* Output */ + gpio_set_value(gpio, ta_in); + } else { + dev_err(dev, "When DC is wired, DOK and DCM should" + " be wired as well.\n"); + ret = -EINVAL; + goto err; + } + } else { + if (pdata->dcm) { + if (gpio_is_valid(pdata->dcm)) + gpio_set_value(pdata->dcm, 0); + else { + dev_err(dev, "Invalid pin: dcm.\n"); + ret = -EINVAL; + goto err; + } + } + } + + if (pdata->usb_valid) { + if (pdata->uok && gpio_is_valid(pdata->uok)) { + gpio = pdata->uok; + usb_in = gpio_get_value(gpio) ? 0 : 1; + } else { + dev_err(dev, "When USB is wired, UOK should be wired." + "as well.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->cen) { + if (gpio_is_valid(pdata->cen)) { + gpio_set_value(pdata->cen, (ta_in || usb_in) ? 0 : 1); + } else { + dev_err(dev, "Invalid pin: cen.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->chg) { + if (!gpio_is_valid(pdata->chg)) { + dev_err(dev, "Invalid pin: chg.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->flt) { + if (!gpio_is_valid(pdata->flt)) { + dev_err(dev, "Invalid pin: flt.\n"); + ret = -EINVAL; + goto err; + } + } + + if (pdata->usus) { + if (!gpio_is_valid(pdata->usus)) { + dev_err(dev, "Invalid pin: usus.\n"); + ret = -EINVAL; + goto err; + } + } + + data->fault = false; + data->ta_in = ta_in; + data->usb_in = usb_in; + + data->psy.name = "max8903_charger"; + data->psy.type = (ta_in) ? POWER_SUPPLY_TYPE_MAINS : + ((usb_in) ? POWER_SUPPLY_TYPE_USB : + POWER_SUPPLY_TYPE_BATTERY); + data->psy.get_property = max8903_get_property; + data->psy.properties = max8903_charger_props; + data->psy.num_properties = ARRAY_SIZE(max8903_charger_props); + + ret = power_supply_register(dev, &data->psy); + if (ret) { + dev_err(dev, "failed: power supply register.\n"); + goto err; + } + + if (pdata->dc_valid) { + ret = request_threaded_irq(gpio_to_irq(pdata->dok), + NULL, max8903_dcin, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 DC IN", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for DC (%d)\n", + gpio_to_irq(pdata->dok), ret); + goto err_psy; + } + } + + if (pdata->usb_valid) { + ret = request_threaded_irq(gpio_to_irq(pdata->uok), + NULL, max8903_usbin, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 USB IN", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for USB (%d)\n", + gpio_to_irq(pdata->uok), ret); + goto err_dc_irq; + } + } + + if (pdata->flt) { + ret = request_threaded_irq(gpio_to_irq(pdata->flt), + NULL, max8903_fault, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + "MAX8903 Fault", data); + if (ret) { + dev_err(dev, "Cannot request irq %d for Fault (%d)\n", + gpio_to_irq(pdata->flt), ret); + goto err_usb_irq; + } + } + + return 0; + +err_usb_irq: + if (pdata->usb_valid) + free_irq(gpio_to_irq(pdata->uok), data); +err_dc_irq: + if (pdata->dc_valid) + free_irq(gpio_to_irq(pdata->dok), data); +err_psy: + power_supply_unregister(&data->psy); +err: + kfree(data); + return ret; +} + +static __devexit int max8903_remove(struct platform_device *pdev) +{ + struct max8903_data *data = platform_get_drvdata(pdev); + + if (data) { + struct max8903_pdata *pdata = data->pdata; + + if (pdata->flt) + free_irq(gpio_to_irq(pdata->flt), data); + if (pdata->usb_valid) + free_irq(gpio_to_irq(pdata->uok), data); + if (pdata->dc_valid) + free_irq(gpio_to_irq(pdata->dok), data); + power_supply_unregister(&data->psy); + kfree(data); + } + + return 0; +} + +static struct platform_driver max8903_driver = { + .probe = max8903_probe, + .remove = __devexit_p(max8903_remove), + .driver = { + .name = "max8903-charger", + .owner = THIS_MODULE, + }, +}; + +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_LICENSE("GPL"); +MODULE_DESCRIPTION("MAX8903 Charger Driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_ALIAS("max8903-charger"); diff --git a/drivers/power/max8925_power.c b/drivers/power/max8925_power.c index 8e5aec260866..a70e16d3a3dc 100644 --- a/drivers/power/max8925_power.c +++ b/drivers/power/max8925_power.c @@ -425,16 +425,11 @@ static __devexit int max8925_deinit_charger(struct max8925_power_info *info) static __devinit int max8925_power_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct max8925_platform_data *max8925_pdata; struct max8925_power_pdata *pdata = NULL; struct max8925_power_info *info; int ret; - if (pdev->dev.parent->platform_data) { - max8925_pdata = pdev->dev.parent->platform_data; - pdata = max8925_pdata->power; - } - + pdata = pdev->dev.platform_data; if (!pdata) { dev_err(&pdev->dev, "platform data isn't assigned to " "power supply\n"); @@ -447,6 +442,7 @@ static __devinit int max8925_power_probe(struct platform_device *pdev) info->chip = chip; info->gpm = chip->i2c; info->adc = chip->adc; + platform_set_drvdata(pdev, info); info->ac.name = "max8925-ac"; info->ac.type = POWER_SUPPLY_TYPE_MAINS; @@ -482,8 +478,6 @@ 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; - dev_set_drvdata(&pdev->dev, info); - platform_set_drvdata(pdev, info); max8925_init_charger(chip, info); return 0; diff --git a/drivers/power/test_power.c b/drivers/power/test_power.c index 0cd9f67d33e5..b527c93bf2f3 100644 --- a/drivers/power/test_power.c +++ b/drivers/power/test_power.c @@ -3,6 +3,12 @@ * * Copyright 2010 Anton Vorontsov <cbouatmailru@gmail.com> * + * Dynamic module parameter code from the Virtual Battery Driver + * Copyright (C) 2008 Pylone, Inc. + * By: Masashi YOKOTA <yokota@pylone.jp> + * Originally found here: + * http://downloads.pylone.jp/src/virtual_battery/virtual_battery-0.0.1.tar.bz2 + * * 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. @@ -15,8 +21,12 @@ #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 ac_online = 1; +static int battery_status = POWER_SUPPLY_STATUS_DISCHARGING; +static int battery_health = POWER_SUPPLY_HEALTH_GOOD; +static int battery_present = 1; /* true */ +static int battery_technology = POWER_SUPPLY_TECHNOLOGY_LION; +static int battery_capacity = 50; static int test_power_get_ac_property(struct power_supply *psy, enum power_supply_property psp, @@ -24,7 +34,7 @@ static int test_power_get_ac_property(struct power_supply *psy, { switch (psp) { case POWER_SUPPLY_PROP_ONLINE: - val->intval = test_power_ac_online; + val->intval = ac_online; break; default: return -EINVAL; @@ -47,22 +57,30 @@ static int test_power_get_battery_property(struct power_supply *psy, val->strval = UTS_RELEASE; break; case POWER_SUPPLY_PROP_STATUS: - val->intval = test_power_battery_status; + val->intval = 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; + val->intval = battery_health; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = battery_present; break; case POWER_SUPPLY_PROP_TECHNOLOGY: - val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + val->intval = battery_technology; break; case POWER_SUPPLY_PROP_CAPACITY_LEVEL: val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; break; case POWER_SUPPLY_PROP_CAPACITY: - val->intval = 50; + case POWER_SUPPLY_PROP_CHARGE_NOW: + val->intval = battery_capacity; + break; + case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: + case POWER_SUPPLY_PROP_CHARGE_FULL: + val->intval = 100; break; case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: @@ -84,9 +102,11 @@ 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_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, - POWER_SUPPLY_PROP_CHARGE_EMPTY, + POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_CAPACITY_LEVEL, POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG, @@ -118,6 +138,7 @@ static struct power_supply test_power_supplies[] = { }, }; + static int __init test_power_init(void) { int i; @@ -145,8 +166,8 @@ 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; + ac_online = 0; + 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", @@ -158,6 +179,241 @@ static void __exit test_power_exit(void) } module_exit(test_power_exit); + + +#define MAX_KEYLENGTH 256 +struct battery_property_map { + int value; + char const *key; +}; + +static struct battery_property_map map_ac_online[] = { + { 0, "on" }, + { 1, "off" }, + { -1, NULL }, +}; + +static struct battery_property_map map_status[] = { + { POWER_SUPPLY_STATUS_CHARGING, "charging" }, + { POWER_SUPPLY_STATUS_DISCHARGING, "discharging" }, + { POWER_SUPPLY_STATUS_NOT_CHARGING, "not-charging" }, + { POWER_SUPPLY_STATUS_FULL, "full" }, + { -1, NULL }, +}; + +static struct battery_property_map map_health[] = { + { POWER_SUPPLY_HEALTH_GOOD, "good" }, + { POWER_SUPPLY_HEALTH_OVERHEAT, "overheat" }, + { POWER_SUPPLY_HEALTH_DEAD, "dead" }, + { POWER_SUPPLY_HEALTH_OVERVOLTAGE, "overvoltage" }, + { POWER_SUPPLY_HEALTH_UNSPEC_FAILURE, "failure" }, + { -1, NULL }, +}; + +static struct battery_property_map map_present[] = { + { 0, "false" }, + { 1, "true" }, + { -1, NULL }, +}; + +static struct battery_property_map map_technology[] = { + { POWER_SUPPLY_TECHNOLOGY_NiMH, "NiMH" }, + { POWER_SUPPLY_TECHNOLOGY_LION, "LION" }, + { POWER_SUPPLY_TECHNOLOGY_LIPO, "LIPO" }, + { POWER_SUPPLY_TECHNOLOGY_LiFe, "LiFe" }, + { POWER_SUPPLY_TECHNOLOGY_NiCd, "NiCd" }, + { POWER_SUPPLY_TECHNOLOGY_LiMn, "LiMn" }, + { -1, NULL }, +}; + + +static int map_get_value(struct battery_property_map *map, const char *key, + int def_val) +{ + char buf[MAX_KEYLENGTH]; + int cr; + + strncpy(buf, key, MAX_KEYLENGTH); + buf[MAX_KEYLENGTH-1] = '\0'; + + cr = strnlen(buf, MAX_KEYLENGTH) - 1; + if (buf[cr] == '\n') + buf[cr] = '\0'; + + while (map->key) { + if (strncasecmp(map->key, buf, MAX_KEYLENGTH) == 0) + return map->value; + map++; + } + + return def_val; +} + + +static const char *map_get_key(struct battery_property_map *map, int value, + const char *def_key) +{ + while (map->key) { + if (map->value == value) + return map->key; + map++; + } + + return def_key; +} + +static int param_set_ac_online(const char *key, const struct kernel_param *kp) +{ + ac_online = map_get_value(map_ac_online, key, ac_online); + power_supply_changed(&test_power_supplies[0]); + return 0; +} + +static int param_get_ac_online(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_ac_online, ac_online, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_status(const char *key, + const struct kernel_param *kp) +{ + battery_status = map_get_value(map_status, key, battery_status); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_status(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_status, battery_status, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_health(const char *key, + const struct kernel_param *kp) +{ + battery_health = map_get_value(map_health, key, battery_health); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_health(char *buffer, const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_health, battery_health, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_present(const char *key, + const struct kernel_param *kp) +{ + battery_present = map_get_value(map_present, key, battery_present); + power_supply_changed(&test_power_supplies[0]); + return 0; +} + +static int param_get_battery_present(char *buffer, + const struct kernel_param *kp) +{ + strcpy(buffer, map_get_key(map_present, battery_present, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_technology(const char *key, + const struct kernel_param *kp) +{ + battery_technology = map_get_value(map_technology, key, + battery_technology); + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +static int param_get_battery_technology(char *buffer, + const struct kernel_param *kp) +{ + strcpy(buffer, + map_get_key(map_technology, battery_technology, "unknown")); + return strlen(buffer); +} + +static int param_set_battery_capacity(const char *key, + const struct kernel_param *kp) +{ + int tmp; + + if (1 != sscanf(key, "%d", &tmp)) + return -EINVAL; + + battery_capacity = tmp; + power_supply_changed(&test_power_supplies[1]); + return 0; +} + +#define param_get_battery_capacity param_get_int + + + +static struct kernel_param_ops param_ops_ac_online = { + .set = param_set_ac_online, + .get = param_get_ac_online, +}; + +static struct kernel_param_ops param_ops_battery_status = { + .set = param_set_battery_status, + .get = param_get_battery_status, +}; + +static struct kernel_param_ops param_ops_battery_present = { + .set = param_set_battery_present, + .get = param_get_battery_present, +}; + +static struct kernel_param_ops param_ops_battery_technology = { + .set = param_set_battery_technology, + .get = param_get_battery_technology, +}; + +static struct kernel_param_ops param_ops_battery_health = { + .set = param_set_battery_health, + .get = param_get_battery_health, +}; + +static struct kernel_param_ops param_ops_battery_capacity = { + .set = param_set_battery_capacity, + .get = param_get_battery_capacity, +}; + + +#define param_check_ac_online(name, p) __param_check(name, p, void); +#define param_check_battery_status(name, p) __param_check(name, p, void); +#define param_check_battery_present(name, p) __param_check(name, p, void); +#define param_check_battery_technology(name, p) __param_check(name, p, void); +#define param_check_battery_health(name, p) __param_check(name, p, void); +#define param_check_battery_capacity(name, p) __param_check(name, p, void); + + +module_param(ac_online, ac_online, 0644); +MODULE_PARM_DESC(ac_online, "AC charging state <on|off>"); + +module_param(battery_status, battery_status, 0644); +MODULE_PARM_DESC(battery_status, + "battery status <charging|discharging|not-charging|full>"); + +module_param(battery_present, battery_present, 0644); +MODULE_PARM_DESC(battery_present, + "battery presence state <good|overheat|dead|overvoltage|failure>"); + +module_param(battery_technology, battery_technology, 0644); +MODULE_PARM_DESC(battery_technology, + "battery technology <NiMH|LION|LIPO|LiFe|NiCd|LiMn>"); + +module_param(battery_health, battery_health, 0644); +MODULE_PARM_DESC(battery_health, + "battery health state <good|overheat|dead|overvoltage|failure>"); + +module_param(battery_capacity, battery_capacity, 0644); +MODULE_PARM_DESC(battery_capacity, "battery capacity (percentage)"); + + MODULE_DESCRIPTION("Power supply driver for testing"); MODULE_AUTHOR("Anton Vorontsov <cbouatmailru@gmail.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/z2_battery.c b/drivers/power/z2_battery.c index e5ced3a4c1ed..d119c38b3ff6 100644 --- a/drivers/power/z2_battery.c +++ b/drivers/power/z2_battery.c @@ -271,24 +271,33 @@ static int __devexit z2_batt_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int z2_batt_suspend(struct i2c_client *client, pm_message_t state) +static int z2_batt_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct z2_charger *charger = i2c_get_clientdata(client); flush_work_sync(&charger->bat_work); return 0; } -static int z2_batt_resume(struct i2c_client *client) +static int z2_batt_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct z2_charger *charger = i2c_get_clientdata(client); schedule_work(&charger->bat_work); return 0; } + +static const struct dev_pm_ops z2_battery_pm_ops = { + .suspend = z2_batt_suspend, + .resume = z2_batt_resume, +}; + +#define Z2_BATTERY_PM_OPS (&z2_battery_pm_ops) + #else -#define z2_batt_suspend NULL -#define z2_batt_resume NULL +#define Z2_BATTERY_PM_OPS (NULL) #endif static const struct i2c_device_id z2_batt_id[] = { @@ -301,11 +310,10 @@ static struct i2c_driver z2_batt_driver = { .driver = { .name = "z2-battery", .owner = THIS_MODULE, + .pm = Z2_BATTERY_PM_OPS }, .probe = z2_batt_probe, .remove = z2_batt_remove, - .suspend = z2_batt_suspend, - .resume = z2_batt_resume, .id_table = z2_batt_id, }; |