Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 433c24ed authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* git://git.infradead.org/battery-2.6:
  power_supply: Add driver for the PMU on WM831x PMICs
  ds2760_battery: Fix integer overflow for time_to_empty_now
  wm97xx_battery: Convert to dev_pm_ops
  wm97xx_battery: Use irq to detect charger state
  wm97xx_battery: Use platform_data
  wm97xx-core: Pass platform_data to battery
  ds2760_battery: implement set_charged() feature
  power_supply: get_by_name and set_charged functionality
  power_supply: EXPORT_SYMBOL cleanups
  ds2760_battery: add current_accum module parameter
  ds2760_battery: handle full_active_uAh == 0 case correctly
  ds2760_battery: add rated_capacity module parameter
  ds2760_battery: export more features
  ds2760_battery: delay power supply registration
  wm8350_power: Implement charge type property
  power_supply: Add a charge_type property, and use it for olpc driver
  olpc_battery: Add an 'error' sysfs device that displays raw errors
  Revert "power: remove POWER_SUPPLY_PROP_CAPACITY_LEVEL"
parents 85afd827 f0568783
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -76,6 +76,11 @@ STATUS - this attribute represents operating status (charging, full,
discharging (i.e. powering a load), etc.). This corresponds to
BATTERY_STATUS_* values, as defined in battery.h.

CHARGE_TYPE - batteries can typically charge at different rates.
This defines trickle and fast charges.  For batteries that
are already charged or discharging, 'n/a' can be displayed (or
'unknown', if the status is not known).

HEALTH - represents health of the battery, values corresponds to
POWER_SUPPLY_HEALTH_*, defined in battery.h.

@@ -108,6 +113,8 @@ relative, time-based measurements.
ENERGY_FULL, ENERGY_EMPTY - same as above but for energy.

CAPACITY - capacity in percents.
CAPACITY_LEVEL - capacity level. This corresponds to
POWER_SUPPLY_CAPACITY_LEVEL_*.

TEMP - temperature of the power supply.
TEMP_AMBIENT - ambient temperature.
+3 −0
Original line number Diff line number Diff line
@@ -561,6 +561,7 @@ static void wm97xx_ts_input_close(struct input_dev *idev)
static int wm97xx_probe(struct device *dev)
{
	struct wm97xx *wm;
	struct wm97xx_pdata *pdata = dev->platform_data;
	int ret = 0, id = 0;

	wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL);
@@ -658,6 +659,7 @@ static int wm97xx_probe(struct device *dev)
	}
	platform_set_drvdata(wm->battery_dev, wm);
	wm->battery_dev->dev.parent = dev;
	wm->battery_dev->dev.platform_data = pdata;
	ret = platform_device_add(wm->battery_dev);
	if (ret < 0)
		goto batt_reg_err;
@@ -671,6 +673,7 @@ static int wm97xx_probe(struct device *dev)
	}
	platform_set_drvdata(wm->touch_dev, wm);
	wm->touch_dev->dev.parent = dev;
	wm->touch_dev->dev.platform_data = pdata;
	ret = platform_device_add(wm->touch_dev);
	if (ret < 0)
		goto touch_reg_err;
+7 −0
Original line number Diff line number Diff line
@@ -29,6 +29,13 @@ config APM_POWER
	  Say Y here to enable support APM status emulation using
	  battery class devices.

config WM831X_POWER
	tristate "WM831X PMU support"
	depends on MFD_WM831X
	help
	  Say Y here to enable support for the power management unit
	  provided by Wolfson Microelectronics WM831x PMICs.

config WM8350_POWER
        tristate "WM8350 PMU support"
        depends on MFD_WM8350
+1 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@ obj-$(CONFIG_POWER_SUPPLY) += power_supply.o

obj-$(CONFIG_PDA_POWER)		+= pda_power.o
obj-$(CONFIG_APM_POWER)		+= apm_power.o
obj-$(CONFIG_WM831X_POWER)	+= wm831x_power.o
obj-$(CONFIG_WM8350_POWER)	+= wm8350_power.o

obj-$(CONFIG_BATTERY_DS2760)	+= ds2760_battery.o
+122 −25
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ struct ds2760_device_info {
	struct device *w1_dev;
	struct workqueue_struct *monitor_wqueue;
	struct delayed_work monitor_work;
	struct delayed_work set_charged_work;
};

static unsigned int cache_time = 1000;
@@ -66,6 +67,14 @@ static unsigned int pmod_enabled;
module_param(pmod_enabled, bool, 0644);
MODULE_PARM_DESC(pmod_enabled, "PMOD enable bit");

static unsigned int rated_capacity;
module_param(rated_capacity, uint, 0644);
MODULE_PARM_DESC(rated_capacity, "rated battery capacity, 10*mAh or index");

static unsigned int current_accum;
module_param(current_accum, uint, 0644);
MODULE_PARM_DESC(current_accum, "current accumulator value");

/* Some batteries have their rated capacity stored a N * 10 mAh, while
 * others use an index into this table. */
static int rated_capacities[] = {
@@ -168,8 +177,13 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di)
	di->full_active_uAh = di->raw[DS2760_ACTIVE_FULL] << 8 |
			      di->raw[DS2760_ACTIVE_FULL + 1];

	scale[0] = di->raw[DS2760_ACTIVE_FULL] << 8 |
		   di->raw[DS2760_ACTIVE_FULL + 1];
	/* If the full_active_uAh value is not given, fall back to the rated
	 * capacity. This is likely to happen when chips are not part of the
	 * battery pack and is therefore not bootstrapped. */
	if (di->full_active_uAh == 0)
		di->full_active_uAh = di->rated_capacity / 1000L;

	scale[0] = di->full_active_uAh;
	for (i = 1; i < 5; i++)
		scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i];

@@ -197,15 +211,31 @@ static int ds2760_battery_read_status(struct ds2760_device_info *di)
	if (di->rem_capacity > 100)
		di->rem_capacity = 100;

	if (di->current_uA)
		di->life_sec = -((di->accum_current_uAh - di->empty_uAh) *
				 3600L) / di->current_uA;
	if (di->current_uA >= 100L)
		di->life_sec = -((di->accum_current_uAh - di->empty_uAh) * 36L)
					/ (di->current_uA / 100L);
	else
		di->life_sec = 0;

	return 0;
}

static void ds2760_battery_set_current_accum(struct ds2760_device_info *di,
					     unsigned int acr_val)
{
	unsigned char acr[2];

	/* acr is in units of 0.25 mAh */
	acr_val *= 4L;
	acr_val /= 1000;

	acr[0] = acr_val >> 8;
	acr[1] = acr_val & 0xff;

	if (w1_ds2760_write(di->w1_dev, acr, DS2760_CURRENT_ACCUM_MSB, 2) < 2)
		dev_warn(di->dev, "ACR write failed\n");
}

static void ds2760_battery_update_status(struct ds2760_device_info *di)
{
	int old_charge_status = di->charge_status;
@@ -237,21 +267,9 @@ static void ds2760_battery_update_status(struct ds2760_device_info *di)
			if (di->full_counter < 2) {
				di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
			} else {
				unsigned char acr[2];
				int acr_val;

				/* acr is in units of 0.25 mAh */
				acr_val = di->full_active_uAh * 4L / 1000;

				acr[0] = acr_val >> 8;
				acr[1] = acr_val & 0xff;

				if (w1_ds2760_write(di->w1_dev, acr,
				    DS2760_CURRENT_ACCUM_MSB, 2) < 2)
					dev_warn(di->dev,
						 "ACR reset failed\n");

				di->charge_status = POWER_SUPPLY_STATUS_FULL;
				ds2760_battery_set_current_accum(di,
						di->full_active_uAh);
			}
		}
	} else {
@@ -274,6 +292,17 @@ static void ds2760_battery_write_status(struct ds2760_device_info *di,
	w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
}

static void ds2760_battery_write_rated_capacity(struct ds2760_device_info *di,
						unsigned char rated_capacity)
{
	if (rated_capacity == di->raw[DS2760_RATED_CAPACITY])
		return;

	w1_ds2760_write(di->w1_dev, &rated_capacity, DS2760_RATED_CAPACITY, 1);
	w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
	w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
}

static void ds2760_battery_work(struct work_struct *work)
{
	struct ds2760_device_info *di = container_of(work,
@@ -299,6 +328,52 @@ static void ds2760_battery_external_power_changed(struct power_supply *psy)
	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
}


static void ds2760_battery_set_charged_work(struct work_struct *work)
{
	char bias;
	struct ds2760_device_info *di = container_of(work,
		struct ds2760_device_info, set_charged_work.work);

	dev_dbg(di->dev, "%s\n", __func__);

	ds2760_battery_read_status(di);

	/* When we get notified by external circuitry that the battery is
	 * considered fully charged now, we know that there is no current
	 * flow any more. However, the ds2760's internal current meter is
	 * too inaccurate to rely on - spec say something ~15% failure.
	 * Hence, we use the current offset bias register to compensate
	 * that error.
	 */

	if (!power_supply_am_i_supplied(&di->bat))
		return;

	bias = (signed char) di->current_raw +
		(signed char) di->raw[DS2760_CURRENT_OFFSET_BIAS];

	dev_dbg(di->dev, "%s: bias = %d\n", __func__, bias);

	w1_ds2760_write(di->w1_dev, &bias, DS2760_CURRENT_OFFSET_BIAS, 1);
	w1_ds2760_store_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);
	w1_ds2760_recall_eeprom(di->w1_dev, DS2760_EEPROM_BLOCK1);

	/* Write to the di->raw[] buffer directly - the CURRENT_OFFSET_BIAS
	 * value won't be read back by ds2760_battery_read_status() */
	di->raw[DS2760_CURRENT_OFFSET_BIAS] = bias;
}

static void ds2760_battery_set_charged(struct power_supply *psy)
{
	struct ds2760_device_info *di = to_ds2760_device_info(psy);

	/* postpone the actual work by 20 secs. This is for debouncing GPIO
	 * signals and to let the current value settle. See AN4188. */
	cancel_delayed_work(&di->set_charged_work);
	queue_delayed_work(di->monitor_wqueue, &di->set_charged_work, HZ * 20);
}

static int ds2760_battery_get_property(struct power_supply *psy,
				       enum power_supply_property psp,
				       union power_supply_propval *val)
@@ -337,6 +412,12 @@ static int ds2760_battery_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_TEMP:
		val->intval = di->temp_C;
		break;
	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW:
		val->intval = di->life_sec;
		break;
	case POWER_SUPPLY_PROP_CAPACITY:
		val->intval = di->rem_capacity;
		break;
	default:
		return -EINVAL;
	}
@@ -353,6 +434,8 @@ static enum power_supply_property ds2760_battery_props[] = {
	POWER_SUPPLY_PROP_CHARGE_EMPTY,
	POWER_SUPPLY_PROP_CHARGE_NOW,
	POWER_SUPPLY_PROP_TEMP,
	POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
	POWER_SUPPLY_PROP_CAPACITY,
};

static int ds2760_battery_probe(struct platform_device *pdev)
@@ -376,17 +459,12 @@ static int ds2760_battery_probe(struct platform_device *pdev)
	di->bat.properties	= ds2760_battery_props;
	di->bat.num_properties	= ARRAY_SIZE(ds2760_battery_props);
	di->bat.get_property	= ds2760_battery_get_property;
	di->bat.set_charged	= ds2760_battery_set_charged;
	di->bat.external_power_changed =
				  ds2760_battery_external_power_changed;

	di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;

	retval = power_supply_register(&pdev->dev, &di->bat);
	if (retval) {
		dev_err(di->dev, "failed to register battery\n");
		goto batt_failed;
	}

	/* enable sleep mode feature */
	ds2760_battery_read_status(di);
	status = di->raw[DS2760_STATUS_REG];
@@ -397,7 +475,24 @@ static int ds2760_battery_probe(struct platform_device *pdev)

	ds2760_battery_write_status(di, status);

	/* set rated capacity from module param */
	if (rated_capacity)
		ds2760_battery_write_rated_capacity(di, rated_capacity);

	/* set current accumulator if given as parameter.
	 * this should only be done for bootstrapping the value */
	if (current_accum)
		ds2760_battery_set_current_accum(di, current_accum);

	retval = power_supply_register(&pdev->dev, &di->bat);
	if (retval) {
		dev_err(di->dev, "failed to register battery\n");
		goto batt_failed;
	}

	INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
	INIT_DELAYED_WORK(&di->set_charged_work,
			  ds2760_battery_set_charged_work);
	di->monitor_wqueue = create_singlethread_workqueue(dev_name(&pdev->dev));
	if (!di->monitor_wqueue) {
		retval = -ESRCH;
@@ -422,6 +517,8 @@ static int ds2760_battery_remove(struct platform_device *pdev)

	cancel_rearming_delayed_workqueue(di->monitor_wqueue,
					  &di->monitor_work);
	cancel_rearming_delayed_workqueue(di->monitor_wqueue,
					  &di->set_charged_work);
	destroy_workqueue(di->monitor_wqueue);
	power_supply_unregister(&di->bat);

Loading