summaryrefslogtreecommitdiff
path: root/packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch
diff options
context:
space:
mode:
authorKoen Kooi <koen@openembedded.org>2007-10-31 13:46:47 +0000
committerKoen Kooi <koen@openembedded.org>2007-10-31 13:46:47 +0000
commit44cbb9985f111ce87e6990061c89e2e061b6c6ea (patch)
treef26c7f7df66d468547e5b5cc8ba8dc328c44f08d /packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch
parent43b6ecbcb39354a32e0b8dd7c02ec8789be0ab9a (diff)
parent821528ad116a72c2323d88d21e979614c78f1b41 (diff)
propagate from branch 'org.openembedded.dev' (head a6b798a43c05aef43ed650ab880f3edd386d0aa3)
to branch 'org.openembedded.dev.avr32' (head 77e1041de2eef682f183f3f5199826818cb9c5b1)
Diffstat (limited to 'packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch')
-rw-r--r--packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch3373
1 files changed, 3373 insertions, 0 deletions
diff --git a/packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch b/packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch
new file mode 100644
index 0000000000..face2f4ef2
--- /dev/null
+++ b/packages/linux/linux-rp-2.6.23+2.6.24-rc0+git/pda-power.patch
@@ -0,0 +1,3373 @@
+---
+ arch/arm/Kconfig | 2
+ drivers/Kconfig | 2
+ drivers/Makefile | 1
+ drivers/power/Kconfig | 70 +++++
+ drivers/power/Makefile | 28 ++
+ drivers/power/adc_battery.c | 278 +++++++++++++++++++++
+ drivers/power/apm_power.c | 247 +++++++++++++++++++
+ drivers/power/ds2760_battery.c | 475 +++++++++++++++++++++++++++++++++++++
+ drivers/power/micro_battery.c | 257 ++++++++++++++++++++
+ drivers/power/olpc_battery.c | 302 +++++++++++++++++++++++
+ drivers/power/pda_power.c | 263 ++++++++++++++++++++
+ drivers/power/pmu_battery.c | 215 ++++++++++++++++
+ drivers/power/power_supply.h | 42 +++
+ drivers/power/power_supply_core.c | 168 +++++++++++++
+ drivers/power/power_supply_leds.c | 188 ++++++++++++++
+ drivers/power/power_supply_sysfs.c | 289 ++++++++++++++++++++++
+ drivers/power/simpad-battery.c | 242 ++++++++++++++++++
+ include/linux/power_supply.h | 175 +++++++++++++
+ 18 files changed, 3244 insertions(+)
+
+Index: linux-2.6.22/drivers/power/adc_battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/adc_battery.c 2007-08-23 12:26:28.000000000 +0200
+@@ -0,0 +1,278 @@
++/*
++ * Copyright (c) 2007 Paul Sokolovsky
++ *
++ * 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.
++ *
++ */
++
++//#define DEBUG
++
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/pm.h>
++#include <linux/delay.h>
++#include <linux/workqueue.h>
++#include <linux/platform_device.h>
++#include <linux/power_supply.h>
++#include <linux/adc.h>
++#include <linux/adc_battery.h>
++
++#include <asm/irq.h>
++
++#define PIN_NO_VOLT 0
++#define PIN_NO_CURR 1
++#define PIN_NO_TEMP 2
++
++struct battery_adc_priv {
++ struct power_supply batt_cdev;
++
++ struct battery_adc_platform_data *pdata;
++
++ struct adc_request req;
++ struct adc_sense pins[3];
++ struct adc_sense last_good_pins[3];
++
++ struct workqueue_struct *wq;
++ struct delayed_work work;
++};
++
++/*
++ * Battery properties
++ */
++
++static int adc_battery_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct battery_adc_priv* drvdata = (struct battery_adc_priv*)psy;
++ int voltage;
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = drvdata->pdata->charge_status;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++ val->intval = drvdata->pdata->battery_info.voltage_max_design;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
++ val->intval = drvdata->pdata->battery_info.voltage_min_design;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
++ val->intval = drvdata->pdata->battery_info.charge_full_design;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
++ val->intval = drvdata->pdata->battery_info.charge_empty_design;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ val->intval = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult;
++ break;
++ case POWER_SUPPLY_PROP_CURRENT_NOW:
++ val->intval = drvdata->last_good_pins[PIN_NO_CURR].value * drvdata->pdata->current_mult;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_NOW:
++ /* We do calculations in mX, not uX, because todo it in uX we should use "long long"s,
++ * which is a mess (need to use do_div) when you need divide operation). */
++ voltage = drvdata->last_good_pins[PIN_NO_VOLT].value * drvdata->pdata->voltage_mult;
++ val->intval = ((voltage/1000 - drvdata->pdata->battery_info.voltage_min_design/1000) *
++ (drvdata->pdata->battery_info.charge_full_design/1000 -
++ drvdata->pdata->battery_info.charge_empty_design/1000)) /
++ (drvdata->pdata->battery_info.voltage_max_design/1000 -
++ drvdata->pdata->battery_info.voltage_min_design/1000);
++ val->intval *= 1000; /* convert final result to uX */
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = drvdata->last_good_pins[PIN_NO_TEMP].value * drvdata->pdata->temperature_mult / 1000;
++ break;
++ default:
++ return -EINVAL;
++ };
++ return 0;
++}
++
++/*
++ * Driver body
++ */
++
++static void adc_battery_query(struct battery_adc_priv *drvdata)
++{
++ struct battery_adc_platform_data *pdata = drvdata->pdata;
++ int powered, charging;
++
++ adc_request_sample(&drvdata->req);
++
++ powered = power_supply_am_i_supplied(&drvdata->batt_cdev);
++ charging = pdata->is_charging ? pdata->is_charging() : -1;
++
++ if (powered && charging)
++ pdata->charge_status = POWER_SUPPLY_STATUS_CHARGING;
++ else if (powered && !charging && charging != -1)
++ pdata->charge_status = POWER_SUPPLY_STATUS_FULL;
++ else
++ pdata->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
++
++ /* Throw away invalid samples, this may happen soon after resume for example. */
++ if (drvdata->pins[PIN_NO_VOLT].value > 0) {
++ memcpy(drvdata->last_good_pins, drvdata->pins, sizeof(drvdata->pins));
++#ifdef DEBUG
++ printk("%d %d %d\n", drvdata->pins[PIN_NO_VOLT].value,
++ drvdata->pins[PIN_NO_CURR].value,
++ drvdata->pins[PIN_NO_TEMP].value);
++#endif
++ }
++}
++
++static void adc_battery_charge_power_changed(struct power_supply *bat)
++{
++ struct battery_adc_priv *drvdata = (struct battery_adc_priv*)bat;
++ cancel_delayed_work(&drvdata->work);
++ queue_delayed_work(drvdata->wq, &drvdata->work, 0);
++}
++
++static void adc_battery_work_func(struct work_struct *work)
++{
++ struct delayed_work *delayed_work = container_of(work, struct delayed_work, work);
++ struct battery_adc_priv *drvdata = container_of(delayed_work, struct battery_adc_priv, work);
++
++ adc_battery_query(drvdata);
++ power_supply_changed(&drvdata->batt_cdev);
++
++ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000);
++}
++
++static int adc_battery_probe(struct platform_device *pdev)
++{
++ int retval;
++ struct battery_adc_platform_data *pdata = pdev->dev.platform_data;
++ struct battery_adc_priv *drvdata;
++ int i, j;
++ enum power_supply_property props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_CURRENT_NOW,
++ POWER_SUPPLY_PROP_CHARGE_NOW,
++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
++ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
++ POWER_SUPPLY_PROP_TEMP,
++ };
++
++ // Initialize ts data structure.
++ drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
++ if (!drvdata)
++ return -ENOMEM;
++
++ drvdata->batt_cdev.name = pdata->battery_info.name;
++ drvdata->batt_cdev.use_for_apm = pdata->battery_info.use_for_apm;
++ drvdata->batt_cdev.num_properties = ARRAY_SIZE(props);
++ drvdata->batt_cdev.get_property = adc_battery_get_property;
++ drvdata->batt_cdev.external_power_changed =
++ adc_battery_charge_power_changed;
++
++ if (!pdata->voltage_pin) {
++ drvdata->batt_cdev.num_properties--;
++ props[3] = -1;
++ }
++ if (!pdata->current_pin) {
++ drvdata->batt_cdev.num_properties--;
++ props[4] = -1;
++ }
++ if (!pdata->temperature_pin) {
++ drvdata->batt_cdev.num_properties--;
++ props[8] = -1;
++ }
++
++ drvdata->batt_cdev.properties = kmalloc(
++ sizeof(*drvdata->batt_cdev.properties) *
++ drvdata->batt_cdev.num_properties, GFP_KERNEL);
++ if (!drvdata->batt_cdev.properties)
++ return -ENOMEM;
++
++ j = 0;
++ for (i = 0; i < ARRAY_SIZE(props); i++) {
++ if (props[i] == -1)
++ continue;
++ drvdata->batt_cdev.properties[j++] = props[i];
++ }
++
++ retval = power_supply_register(&pdev->dev, &drvdata->batt_cdev);
++ if (retval) {
++ printk("adc-battery: Error registering battery classdev");
++ return retval;
++ }
++
++ drvdata->req.senses = drvdata->pins;
++ drvdata->req.num_senses = ARRAY_SIZE(drvdata->pins);
++ drvdata->pins[PIN_NO_VOLT].name = pdata->voltage_pin;
++ drvdata->pins[PIN_NO_CURR].name = pdata->current_pin;
++ drvdata->pins[PIN_NO_TEMP].name = pdata->temperature_pin;
++
++ adc_request_register(&drvdata->req);
++
++ /* Here we assume raw values in mV */
++ if (!pdata->voltage_mult)
++ pdata->voltage_mult = 1000;
++ /* Here we assume raw values in mA */
++ if (!pdata->current_mult)
++ pdata->current_mult = 1000;
++ /* Here we assume raw values in 1/10 C */
++ if (!pdata->temperature_mult)
++ pdata->temperature_mult = 1000;
++
++ drvdata->pdata = pdata;
++ pdata->drvdata = drvdata; /* Seems ugly, we need better solution */
++
++ platform_set_drvdata(pdev, drvdata);
++
++ // Load initial values ASAP
++ adc_battery_query(drvdata);
++
++ // Still schedule next sampling soon
++ INIT_DELAYED_WORK(&drvdata->work, adc_battery_work_func);
++ drvdata->wq = create_workqueue(pdev->dev.bus_id);
++ if (!drvdata->wq)
++ return -ESRCH;
++
++ queue_delayed_work(drvdata->wq, &drvdata->work, (5000 * HZ) / 1000);
++
++ return retval;
++}
++
++static int adc_battery_remove(struct platform_device *pdev)
++{
++ struct battery_adc_priv *drvdata = platform_get_drvdata(pdev);
++ cancel_delayed_work(&drvdata->work);
++ destroy_workqueue(drvdata->wq);
++ power_supply_unregister(&drvdata->batt_cdev);
++ adc_request_unregister(&drvdata->req);
++ kfree(drvdata->batt_cdev.properties);
++ return 0;
++}
++
++static struct platform_driver adc_battery_driver = {
++ .driver = {
++ .name = "adc-battery",
++ },
++ .probe = adc_battery_probe,
++ .remove = adc_battery_remove,
++};
++
++static int __init adc_battery_init(void)
++{
++ return platform_driver_register(&adc_battery_driver);
++}
++
++static void __exit adc_battery_exit(void)
++{
++ platform_driver_unregister(&adc_battery_driver);
++}
++
++module_init(adc_battery_init)
++module_exit(adc_battery_exit)
++
++MODULE_AUTHOR("Paul Sokolovsky");
++MODULE_DESCRIPTION("Battery driver for ADC device");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22/drivers/power/apm_power.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/apm_power.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,247 @@
++/*
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2007 Eugeny Boger <eugenyboger@dgap.mipt.ru>
++ *
++ * Author: Eugeny Boger <eugenyboger@dgap.mipt.ru>
++ *
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ */
++
++#include <linux/module.h>
++#include <linux/power_supply.h>
++#include <linux/apm-emulation.h>
++
++#define PSY_PROP(psy, prop, val) psy->get_property(psy, \
++ POWER_SUPPLY_PROP_##prop, val)
++
++#define _MPSY_PROP(prop, val) main_battery->get_property(main_battery, \
++ prop, val)
++
++#define MPSY_PROP(prop, val) _MPSY_PROP(POWER_SUPPLY_PROP_##prop, val)
++
++static struct power_supply *main_battery;
++
++static void find_main_battery(void)
++{
++ struct device *dev;
++ struct power_supply *bat, *batm;
++ union power_supply_propval full;
++ int max_charge = 0;
++
++ main_battery = NULL;
++ batm = NULL;
++ list_for_each_entry(dev, &power_supply_class->devices, node) {
++ bat = dev_get_drvdata(dev);
++ /* If none of battery devices cantains 'use_for_apm' flag,
++ choice one with maximum design charge */
++ if (!PSY_PROP(bat, CHARGE_FULL_DESIGN, &full)) {
++ if (full.intval > max_charge) {
++ batm = bat;
++ max_charge = full.intval;
++ }
++ }
++
++ if (bat->use_for_apm)
++ main_battery = bat;
++ }
++ if (!main_battery)
++ main_battery = batm;
++
++ return;
++}
++
++static int calculate_time(int status)
++{
++ union power_supply_propval charge_full, charge_empty;
++ union power_supply_propval charge, I;
++
++ if (MPSY_PROP(CHARGE_FULL, &charge_full)) {
++ /* if battery can't report this property, use design value */
++ if (MPSY_PROP(CHARGE_FULL_DESIGN, &charge_full))
++ return -1;
++ }
++
++ if (MPSY_PROP(CHARGE_EMPTY, &charge_empty)) {
++ /* if battery can't report this property, use design value */
++ if (MPSY_PROP(CHARGE_EMPTY_DESIGN, &charge_empty))
++ charge_empty.intval = 0;
++ }
++
++ if (MPSY_PROP(CHARGE_AVG, &charge)) {
++ /* if battery can't report average value, use momentary */
++ if (MPSY_PROP(CHARGE_NOW, &charge))
++ return -1;
++ }
++
++ if (MPSY_PROP(CURRENT_AVG, &I)) {
++ /* if battery can't report average value, use momentary */
++ if (MPSY_PROP(CURRENT_NOW, &I))
++ return -1;
++ }
++
++ if (I.intval == 0)
++ return 0;
++ else if (status == POWER_SUPPLY_STATUS_CHARGING)
++ return ((charge.intval - charge_full.intval) * 60L) /
++ I.intval;
++ else
++ return -((charge.intval - charge_empty.intval) * 60L) /
++ I.intval;
++}
++
++static int calculate_capacity(int using_charge)
++{
++ enum power_supply_property full_prop, empty_prop;
++ enum power_supply_property full_design_prop, empty_design_prop;
++ enum power_supply_property now_prop, avg_prop;
++ union power_supply_propval empty, full, cur;
++ int ret;
++
++ if (using_charge) {
++ full_prop = POWER_SUPPLY_PROP_CHARGE_FULL;
++ empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY;
++ full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN;
++ empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN;
++ now_prop = POWER_SUPPLY_PROP_CHARGE_NOW;
++ avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG;
++ }
++ else {
++ full_prop = POWER_SUPPLY_PROP_ENERGY_FULL;
++ empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY;
++ full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN;
++ empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN;
++ now_prop = POWER_SUPPLY_PROP_ENERGY_NOW;
++ avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG;
++ }
++
++ if (_MPSY_PROP(full_prop, &full)) {
++ /* if battery can't report this property, use design value */
++ if (_MPSY_PROP(full_design_prop, &full))
++ return -1;
++ }
++
++ if (_MPSY_PROP(avg_prop, &cur)) {
++ /* if battery can't report average value, use momentary */
++ if (_MPSY_PROP(now_prop, &cur))
++ return -1;
++ }
++
++ if (_MPSY_PROP(empty_prop, &empty)) {
++ /* if battery can't report this property, use design value */
++ if (_MPSY_PROP(empty_design_prop, &empty))
++ empty.intval = 0;
++ }
++
++ if (full.intval - empty.intval)
++ ret = ((cur.intval - empty.intval) * 100L) /
++ (full.intval - empty.intval);
++ else
++ return -1;
++
++ if (ret > 100)
++ return 100;
++ else if (ret < 0)
++ return 0;
++
++ return ret;
++}
++
++static void apm_battery_apm_get_power_status(struct apm_power_info *info)
++{
++ union power_supply_propval status;
++ union power_supply_propval capacity, time_to_full, time_to_empty;
++
++ down(&power_supply_class->sem);
++ find_main_battery();
++ if (!main_battery) {
++ up(&power_supply_class->sem);
++ return;
++ }
++
++ /* status */
++
++ if (MPSY_PROP(STATUS, &status))
++ status.intval = POWER_SUPPLY_STATUS_UNKNOWN;
++
++ /* ac line status */
++
++ if ((status.intval == POWER_SUPPLY_STATUS_CHARGING) ||
++ (status.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) ||
++ (status.intval == POWER_SUPPLY_STATUS_FULL))
++ info->ac_line_status = APM_AC_ONLINE;
++ else
++ info->ac_line_status = APM_AC_OFFLINE;
++
++ /* battery life (i.e. capacity, in percents) */
++
++ if (MPSY_PROP(CAPACITY, &capacity) == 0)
++ info->battery_life = capacity.intval;
++ else {
++ /* try calculate using energy */
++ info->battery_life = calculate_capacity(0);
++ /* if failed try calculate using charge instead */
++ if (info->battery_life == -1)
++ info->battery_life = calculate_capacity(1);
++ }
++
++ /* charging status */
++
++ if (status.intval == POWER_SUPPLY_STATUS_CHARGING)
++ info->battery_status = APM_BATTERY_STATUS_CHARGING;
++ else {
++ if (info->battery_life > 50)
++ info->battery_status = APM_BATTERY_STATUS_HIGH;
++ else if (info->battery_life > 5)
++ info->battery_status = APM_BATTERY_STATUS_LOW;
++ else
++ info->battery_status = APM_BATTERY_STATUS_CRITICAL;
++ }
++ info->battery_flag = info->battery_status;
++
++ /* time */
++
++ info->units = APM_UNITS_MINS;
++
++ if (status.intval == POWER_SUPPLY_STATUS_CHARGING) {
++ if (MPSY_PROP(TIME_TO_FULL_AVG, &time_to_full)) {
++ if (MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full))
++ info->time = calculate_time(status.intval);
++ else
++ info->time = time_to_full.intval / 60;
++ }
++ }
++ else {
++ if (MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty)) {
++ if (MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty))
++ info->time = calculate_time(status.intval);
++ else
++ info->time = time_to_empty.intval / 60;
++ }
++ }
++
++ up(&power_supply_class->sem);
++ return;
++}
++
++static int __init apm_battery_init(void)
++{
++ printk(KERN_INFO "APM Battery Driver\n");
++
++ apm_get_power_status = apm_battery_apm_get_power_status;
++ return 0;
++}
++
++static void __exit apm_battery_exit(void)
++{
++ apm_get_power_status = NULL;
++ return;
++}
++
++module_init(apm_battery_init);
++module_exit(apm_battery_exit);
++
++MODULE_AUTHOR("Eugeny Boger <eugenyboger@dgap.mipt.ru>");
++MODULE_DESCRIPTION("APM emulation driver for battery monitoring class");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22/drivers/power/ds2760_battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/ds2760_battery.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,475 @@
++/*
++ * Driver for batteries with DS2760 chips inside.
++ *
++ * Copyright (c) 2007 Anton Vorontsov
++ * 2004-2007 Matt Reimer
++ * 2004 Szabolcs Gyurko
++ *
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ *
++ * Author: Anton Vorontsov <cbou@mail.ru>
++ * February 2007
++ *
++ * Matt Reimer <mreimer@vpop.net>
++ * April 2004, 2005, 2007
++ *
++ * Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>
++ * September 2004
++ */
++
++#include <linux/module.h>
++#include <linux/param.h>
++#include <linux/jiffies.h>
++#include <linux/workqueue.h>
++#include <linux/pm.h>
++#include <linux/platform_device.h>
++#include <linux/power_supply.h>
++
++#include "../w1/w1.h"
++#include "../w1/slaves/w1_ds2760.h"
++
++struct ds2760_device_info {
++ struct device *dev;
++
++ /* DS2760 data, valid after calling ds2760_battery_read_status() */
++ unsigned long update_time; /* jiffies when data read */
++ char raw[DS2760_DATA_SIZE]; /* raw DS2760 data */
++ int voltage_raw; /* units of 4.88 mV */
++ int voltage_uV; /* units of uV */
++ int current_raw; /* units of 0.625 mA */
++ int current_uA; /* units of uA */
++ int accum_current_raw; /* units of 0.25 mAh */
++ int accum_current_uAh; /* units of uAh */
++ int temp_raw; /* units of 0.125 C */
++ int temp_C; /* units of 0.1 C */
++ int rated_capacity; /* units of uAh */
++ int rem_capacity; /* percentage */
++ int full_active_uAh; /* units of uAh */
++ int empty_uAh; /* units of uAh */
++ int life_sec; /* units of seconds */
++ int charge_status; /* POWER_SUPPLY_STATUS_* */
++
++ int full_counter;
++ struct power_supply bat;
++ struct device *w1_dev;
++ struct workqueue_struct *monitor_wqueue;
++ struct delayed_work monitor_work;
++};
++
++static unsigned int cache_time = 1000;
++module_param(cache_time, uint, 0644);
++MODULE_PARM_DESC(cache_time, "cache time in milliseconds");
++
++/* Some batteries have their rated capacity stored a N * 10 mAh, while
++ * others use an index into this table. */
++static int rated_capacities[] = {
++ 0,
++ 920, /* Samsung */
++ 920, /* BYD */
++ 920, /* Lishen */
++ 920, /* NEC */
++ 1440, /* Samsung */
++ 1440, /* BYD */
++ 1440, /* Lishen */
++ 1440, /* NEC */
++ 2880, /* Samsung */
++ 2880, /* BYD */
++ 2880, /* Lishen */
++ 2880 /* NEC */
++};
++
++/* array is level at temps 0C, 10C, 20C, 30C, 40C
++ * temp is in Celsius */
++static int battery_interpolate(int array[], int temp)
++{
++ int index, dt;
++
++ if (temp <= 0)
++ return array[0];
++ if (temp >= 40)
++ return array[4];
++
++ index = temp / 10;
++ dt = temp % 10;
++
++ return array[index] + (((array[index + 1] - array[index]) * dt) / 10);
++}
++
++static int ds2760_battery_read_status(struct ds2760_device_info *di)
++{
++ int ret, i, start, count, scale[5];
++
++ if (di->update_time && time_before(jiffies, di->update_time +
++ msecs_to_jiffies(cache_time)))
++ return 0;
++
++ /* The first time we read the entire contents of SRAM/EEPROM,
++ * but after that we just read the interesting bits that change. */
++ if (di->update_time == 0) {
++ start = 0;
++ count = DS2760_DATA_SIZE;
++ }
++ else {
++ start = DS2760_VOLTAGE_MSB;
++ count = DS2760_TEMP_LSB - start + 1;
++ }
++
++ ret = w1_ds2760_read(di->w1_dev, di->raw + start, start, count);
++ if (ret != count) {
++ dev_warn(di->dev, "call to w1_ds2760_read failed (0x%p)\n",
++ di->w1_dev);
++ return 1;
++ }
++
++ di->update_time = jiffies;
++
++ /* DS2760 reports voltage in units of 4.88mV, but the battery class
++ * reports in units of uV, so convert by multiplying by 4880. */
++ di->voltage_raw = (di->raw[DS2760_VOLTAGE_MSB] << 3) |
++ (di->raw[DS2760_VOLTAGE_LSB] >> 5);
++ di->voltage_uV = di->voltage_raw * 4880;
++
++ /* DS2760 reports current in signed units of 0.625mA, but the battery
++ * class reports in units of uA, so convert by multiplying by 625. */
++ di->current_raw =
++ (((signed char)di->raw[DS2760_CURRENT_MSB]) << 5) |
++ (di->raw[DS2760_CURRENT_LSB] >> 3);
++ di->current_uA = di->current_raw * 625;
++
++ /* DS2760 reports accumulated current in signed units of 0.25mAh. */
++ di->accum_current_raw =
++ (((signed char)di->raw[DS2760_CURRENT_ACCUM_MSB]) << 8) |
++ di->raw[DS2760_CURRENT_ACCUM_LSB];
++ di->accum_current_uAh = di->accum_current_raw * 250;
++
++ /* DS2760 reports temperature in signed units of 0.125C, but the
++ * battery class reports in units of 1/10 C, so we convert by
++ * multiplying by .125 * 10 = 1.25. */
++ di->temp_raw = (((signed char)di->raw[DS2760_TEMP_MSB]) << 3) |
++ (di->raw[DS2760_TEMP_LSB] >> 5);
++ di->temp_C = di->temp_raw + (di->temp_raw / 4);
++
++ /* At least some battery monitors (e.g. HP iPAQ) store the battery's
++ * maximum rated capacity. */
++ if (di->raw[DS2760_RATED_CAPACITY] < ARRAY_SIZE(rated_capacities))
++ di->rated_capacity = rated_capacities[
++ (unsigned int)di->raw[DS2760_RATED_CAPACITY]];
++ else
++ di->rated_capacity = di->raw[DS2760_RATED_CAPACITY] * 10;
++
++ di->rated_capacity *= 1000; /* convert to uAh */
++
++ /* Calculate the full level at the present temperature. */
++ 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];
++ for (i = 1; i < 5; i++)
++ scale[i] = scale[i - 1] + di->raw[DS2760_ACTIVE_FULL + 2 + i];
++
++ di->full_active_uAh = battery_interpolate(scale, di->temp_C / 10);
++ di->full_active_uAh *= 1000; /* convert to uAh */
++
++ /* Calculate the empty level at the present temperature. */
++ scale[4] = di->raw[DS2760_ACTIVE_EMPTY + 4];
++ for (i = 3; i >= 0; i--)
++ scale[i] = scale[i + 1] + di->raw[DS2760_ACTIVE_EMPTY + i];
++
++ di->empty_uAh = battery_interpolate(scale, di->temp_C / 10);
++ di->empty_uAh *= 1000; /* convert to uAh */
++
++ /* From Maxim Application Note 131: remaining capacity =
++ * ((ICA - Empty Value) / (Full Value - Empty Value)) x 100% */
++ di->rem_capacity = ((di->accum_current_uAh - di->empty_uAh) * 100L) /
++ (di->full_active_uAh - di->empty_uAh);
++
++ if (di->rem_capacity < 0)
++ di->rem_capacity = 0;
++ 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;
++ else
++ di->life_sec = 0;
++
++ return 0;
++}
++
++static void ds2760_battery_update_status(struct ds2760_device_info *di)
++{
++ int old_charge_status = di->charge_status;
++
++ ds2760_battery_read_status(di);
++
++ if (di->charge_status == POWER_SUPPLY_STATUS_UNKNOWN)
++ di->full_counter = 0;
++
++ if (power_supply_am_i_supplied(&di->bat)) {
++ if (di->current_uA > 10000) {
++ di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
++ di->full_counter = 0;
++ }
++ else if (di->current_uA < -5000) {
++ if (di->charge_status != POWER_SUPPLY_STATUS_NOT_CHARGING)
++ dev_notice(di->dev, "not enough power to "
++ "charge\n");
++ di->charge_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
++ di->full_counter = 0;
++ }
++ else if (di->current_uA < 10000 &&
++ di->charge_status != POWER_SUPPLY_STATUS_FULL) {
++
++ /* Don't consider the battery to be full unless
++ * we've seen the current < 10 mA at least two
++ * consecutive times. */
++
++ di->full_counter++;
++
++ 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;
++ }
++ }
++ }
++ else {
++ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
++ di->full_counter = 0;
++ }
++
++ if (di->charge_status != old_charge_status)
++ power_supply_changed(&di->bat);
++
++ return;
++}
++
++static void ds2760_battery_work(struct work_struct *work)
++{
++ struct ds2760_device_info *di = container_of(work,
++ struct ds2760_device_info, monitor_work.work);
++ const int interval = HZ * 60;
++
++ dev_dbg(di->dev, "%s\n", __FUNCTION__);
++
++ ds2760_battery_update_status(di);
++ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
++
++ return;
++}
++
++#define to_ds2760_device_info(x) container_of((x), struct ds2760_device_info, \
++ bat);
++
++static void ds2760_battery_external_power_changed(struct power_supply *psy)
++{
++ struct ds2760_device_info *di = to_ds2760_device_info(psy);
++
++ dev_dbg(di->dev, "%s\n", __FUNCTION__);
++
++ cancel_delayed_work(&di->monitor_work);
++ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ/10);
++
++ return;
++}
++
++static int ds2760_battery_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct ds2760_device_info *di = to_ds2760_device_info(psy);
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = di->charge_status;
++ return 0;
++ default:
++ break;
++ }
++
++ ds2760_battery_read_status(di);
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ val->intval = di->voltage_uV;
++ break;
++ case POWER_SUPPLY_PROP_CURRENT_NOW:
++ val->intval = di->current_uA;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
++ val->intval = di->rated_capacity;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_FULL:
++ val->intval = di->full_active_uAh;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_EMPTY:
++ val->intval = di->empty_uAh;
++ break;
++ case POWER_SUPPLY_PROP_CHARGE_NOW:
++ val->intval = di->accum_current_uAh;
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = di->temp_C;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static enum power_supply_property ds2760_battery_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_CURRENT_NOW,
++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
++ POWER_SUPPLY_PROP_CHARGE_FULL,
++ POWER_SUPPLY_PROP_CHARGE_EMPTY,
++ POWER_SUPPLY_PROP_CHARGE_NOW,
++ POWER_SUPPLY_PROP_TEMP,
++};
++
++static int ds2760_battery_probe(struct platform_device *pdev)
++{
++ int retval = 0;
++ struct ds2760_device_info *di;
++ struct ds2760_platform_data *pdata;
++
++ di = kzalloc(sizeof(*di), GFP_KERNEL);
++ if (!di) {
++ retval = -ENOMEM;
++ goto di_alloc_failed;
++ }
++
++ platform_set_drvdata(pdev, di);
++
++ pdata = pdev->dev.platform_data;
++ di->dev = &pdev->dev;
++ di->w1_dev = pdev->dev.parent;
++ di->bat.name = pdev->dev.bus_id;
++ di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
++ 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.external_power_changed =
++ ds2760_battery_external_power_changed;
++ di->bat.use_for_apm = 1;
++
++ 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");
++ goto batt_failed;
++ }
++
++ INIT_DELAYED_WORK(&di->monitor_work, ds2760_battery_work);
++ di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id);
++ if (!di->monitor_wqueue) {
++ retval = -ESRCH;
++ goto workqueue_failed;
++ }
++ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 1);
++
++ goto success;
++
++workqueue_failed:
++ power_supply_unregister(&di->bat);
++batt_failed:
++ kfree(di);
++di_alloc_failed:
++success:
++ return retval;
++}
++
++static int ds2760_battery_remove(struct platform_device *pdev)
++{
++ struct ds2760_device_info *di = platform_get_drvdata(pdev);
++
++ cancel_rearming_delayed_workqueue(di->monitor_wqueue,
++ &di->monitor_work);
++ destroy_workqueue(di->monitor_wqueue);
++ power_supply_unregister(&di->bat);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static int ds2760_battery_suspend(struct platform_device *pdev,
++ pm_message_t state)
++{
++ struct ds2760_device_info *di = platform_get_drvdata(pdev);
++
++ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
++
++ return 0;
++}
++
++static int ds2760_battery_resume(struct platform_device *pdev)
++{
++ struct ds2760_device_info *di = platform_get_drvdata(pdev);
++
++ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
++ power_supply_changed(&di->bat);
++
++ cancel_delayed_work(&di->monitor_work);
++ queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ);
++
++ return 0;
++}
++
++#else
++
++#define ds2760_battery_suspend NULL
++#define ds2760_battery_resume NULL
++
++#endif /* CONFIG_PM */
++
++static struct platform_driver ds2760_battery_driver = {
++ .driver = {
++ .name = "ds2760-battery",
++ },
++ .probe = ds2760_battery_probe,
++ .remove = ds2760_battery_remove,
++ .suspend = ds2760_battery_suspend,
++ .resume = ds2760_battery_resume,
++};
++
++static int __init ds2760_battery_init(void)
++{
++ return platform_driver_register(&ds2760_battery_driver);
++}
++
++static void __exit ds2760_battery_exit(void)
++{
++ platform_driver_unregister(&ds2760_battery_driver);
++ return;
++}
++
++module_init(ds2760_battery_init);
++module_exit(ds2760_battery_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Szabolcs Gyurko <szabolcs.gyurko@tlt.hu>, "
++ "Matt Reimer <mreimer@vpop.net>, "
++ "Anton Vorontsov <cbou@mail.ru>");
++MODULE_DESCRIPTION("ds2760 battery driver");
+Index: linux-2.6.22/drivers/power/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/Kconfig 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,70 @@
++menuconfig POWER_SUPPLY
++ tristate "Power supply class support"
++ help
++ Say Y here to enable power supply class support. This allows
++ power supply (batteries, AC, USB) monitoring by userspace
++ via sysfs and uevent (if available) and/or APM kernel interface
++ (if selected below).
++
++if POWER_SUPPLY
++
++config POWER_SUPPLY_DEBUG
++ bool "Power supply debug"
++ help
++ Say Y here to enable debugging messages for power supply class
++ and drivers.
++
++config PDA_POWER
++ tristate "Generic PDA/phone power driver"
++ help
++ Say Y here to enable generic power driver for PDAs and phones with
++ one or two external power supplies (AC/USB) connected to main and
++ backup batteries, and optional builtin charger.
++
++config APM_POWER
++ tristate "APM emulation for class batteries"
++ depends on APM_EMULATION
++ help
++ Say Y here to enable support APM status emulation using
++ battery class devices.
++
++config BATTERY_DS2760
++ tristate "DS2760 battery driver (HP iPAQ & others)"
++ select W1
++ select W1_SLAVE_DS2760
++ help
++ Say Y here to enable support for batteries with ds2760 chip.
++
++config BATTERY_PMU
++ tristate "Apple PMU battery"
++ depends on ADB_PMU
++ help
++ Say Y here to expose battery information on Apple machines
++ through the generic battery class.
++
++config BATTERY_OLPC
++ tristate "One Laptop Per Child battery"
++ depends on X86_32
++ help
++ Say Y to enable support for the battery on the OLPC laptop.
++
++# drivers below are not in battery2-2.6 tree
++
++config ADC_BATTERY
++ tristate "Generic ADC battery driver"
++ depends on ADC && POWER_SUPPLY
++ help
++ Say Y here to enable support for battery monitoring using generic ADC device.
++
++config IPAQ_MICRO_BATTERY
++ tristate "HP iPAQ Micro ASIC battery driver"
++ depends on IPAQ_MICRO && POWER_SUPPLY
++ help
++ Choose this option if you want to monitor battery status on
++ Compaq/HP iPAQ h3100 h3600
++
++config MCP_UCB1x00_SIMPAD_BATTERY
++ tristate "SIMpad Battery Reading Support"
++ depends on MCP_UCB1x00 && POWER_SUPPLY
++
++endif # POWER_SUPPLY
+Index: linux-2.6.22/drivers/power/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/Makefile 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,28 @@
++power_supply-objs := power_supply_core.o
++
++ifeq ($(CONFIG_SYSFS),y)
++power_supply-objs += power_supply_sysfs.o
++endif
++
++ifeq ($(CONFIG_LEDS_TRIGGERS),y)
++power_supply-objs += power_supply_leds.o
++endif
++
++ifeq ($(CONFIG_POWER_SUPPLY_DEBUG),y)
++EXTRA_CFLAGS += -DDEBUG
++endif
++
++obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
++
++obj-$(CONFIG_PDA_POWER) += pda_power.o
++obj-$(CONFIG_APM_POWER) += apm_power.o
++
++obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o
++obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o
++obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o
++
++# drivers below are not in battery2-2.6 tree
++
++obj-$(CONFIG_ADC_BATTERY) += adc_battery.o
++obj-$(CONFIG_IPAQ_MICRO_BATTERY) += micro_battery.o
++obj-$(CONFIG_MCP_UCB1x00_SIMPAD_BATTERY) += simpad-battery.o
+Index: linux-2.6.22/drivers/power/micro_battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/micro_battery.c 2007-08-23 12:25:20.000000000 +0200
+@@ -0,0 +1,257 @@
++/*
++ * 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.
++ *
++ * h3600 atmel micro companion support, battery subdevice
++ * based on previous kernel 2.4 version
++ * Author : Alessandro Gardich <gremlin@gremlin.it>
++ *
++ */
++
++
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/pm.h>
++#include <linux/sysctl.h>
++#include <linux/proc_fs.h>
++#include <linux/delay.h>
++#include <linux/device.h>
++#include <linux/power_supply.h>
++#include <linux/platform_device.h>
++#include <linux/timer.h>
++
++#include <asm/arch/hardware.h>
++
++#include <asm/arch/h3600.h>
++#include <asm/arch/SA-1100.h>
++
++#include <asm/hardware/micro.h>
++
++#define BATT_PERIOD 10*HZ
++
++#define H3600_BATT_STATUS_HIGH 0x01
++#define H3600_BATT_STATUS_LOW 0x02
++#define H3600_BATT_STATUS_CRITICAL 0x04
++#define H3600_BATT_STATUS_CHARGING 0x08
++#define H3600_BATT_STATUS_CHARGEMAIN 0x10
++#define H3600_BATT_STATUS_DEAD 0x20 /* Battery will not charge */
++#define H3600_BATT_STATUS_NOTINSTALLED 0x20 /* For expansion pack batteries */
++#define H3600_BATT_STATUS_FULL 0x40 /* Battery fully charged (and connected to AC) */
++#define H3600_BATT_STATUS_NOBATTERY 0x80
++#define H3600_BATT_STATUS_UNKNOWN 0xff
++
++
++//static struct power_supply_dev *micro_battery;
++
++static micro_private_t *p_micro;
++
++struct timer_list batt_timer;
++
++struct {
++ int ac;
++ int update_time;
++ int chemistry;
++ int voltage;
++ int temperature;
++ int flag;
++} micro_battery;
++
++static void micro_battery_receive (int len, unsigned char *data) {
++ if (0) {
++ printk(KERN_ERR "h3600_battery - AC = %02x\n", data[0]);
++ printk(KERN_ERR "h3600_battery - BAT1 chemistry = %02x\n", data[1]);
++ printk(KERN_ERR "h3600_battery - BAT1 voltage = %d %02x%02x\n", (data[3]<<8)+data[2], data[2], data[3]);
++ printk(KERN_ERR "h3600_battery - BAT1 status = %02x\n", data[4]);
++ }
++
++ micro_battery.ac = data[0];
++ micro_battery.chemistry = data[1];
++ micro_battery.voltage = ((((unsigned short)data[3]<<8)+data[2]) * 5000L ) * 1000 / 1024;
++ micro_battery.flag = data[4];
++
++ if (len == 9) {
++ if (0) {
++ printk(KERN_ERR "h3600_battery - BAT2 chemistry = %02x\n", data[5]);
++ printk(KERN_ERR "h3600_battery - BAT2 voltage = %d %02x%02x\n", (data[7]<<8)+data[6], data[6], data[7]);
++ printk(KERN_ERR "h3600_battery - BAT2 status = %02x\n", data[8]);
++ }
++ }
++}
++
++static void micro_temperature_receive (int len, unsigned char *data) {
++ micro_battery.temperature = ((unsigned short)data[1]<<8)+data[0];
++}
++
++void h3600_battery_read_status(unsigned long data) {
++
++ if (++data % 2)
++ h3600_micro_tx_msg(0x09,0,NULL);
++ else
++ h3600_micro_tx_msg(0x06,0,NULL);
++
++ batt_timer.expires += BATT_PERIOD;
++ batt_timer.data = data;
++
++ add_timer(&batt_timer);
++}
++
++int get_capacity(struct power_supply *b) {
++ switch (micro_battery.flag) {
++ case H3600_BATT_STATUS_HIGH : return 100; break;
++ case H3600_BATT_STATUS_LOW : return 50; break;
++ case H3600_BATT_STATUS_CRITICAL : return 5; break;
++ default: break;
++ }
++ return 0;
++}
++
++int get_status(struct power_supply *b) {
++
++ if (micro_battery.flag == H3600_BATT_STATUS_UNKNOWN)
++ return POWER_SUPPLY_STATUS_UNKNOWN;
++
++ if (micro_battery.flag & H3600_BATT_STATUS_FULL)
++ return POWER_SUPPLY_STATUS_FULL;
++
++ if ((micro_battery.flag & H3600_BATT_STATUS_CHARGING) ||
++ (micro_battery.flag & H3600_BATT_STATUS_CHARGEMAIN))
++ return POWER_SUPPLY_STATUS_CHARGING;
++
++ return POWER_SUPPLY_STATUS_DISCHARGING;
++}
++
++static int micro_batt_get_property(struct power_supply *b,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ val->intval = get_status(b);
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
++ val->intval = 4700000;
++ break;
++ case POWER_SUPPLY_PROP_CAPACITY:
++ val->intval = get_capacity(b);
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = micro_battery.temperature;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
++ val->intval = micro_battery.voltage;
++ break;
++ default:
++ return -EINVAL;
++ };
++
++ return 0;
++}
++
++static enum power_supply_property micro_batt_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++ POWER_SUPPLY_PROP_CAPACITY,
++ POWER_SUPPLY_PROP_TEMP,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++};
++
++static struct power_supply h3600_battery = {
++ .name = "main-battery",
++ .properties = micro_batt_props,
++ .num_properties = ARRAY_SIZE(micro_batt_props),
++ .get_property = micro_batt_get_property,
++ .use_for_apm = 1,
++};
++
++static int micro_batt_probe (struct platform_device *pdev)
++{
++ if (1) printk(KERN_ERR "micro battery probe : begin\n");
++
++ power_supply_register(&pdev->dev, &h3600_battery);
++
++ { /*--- callback ---*/
++ p_micro = platform_get_drvdata(pdev);
++ spin_lock(p_micro->lock);
++ p_micro->h_batt = micro_battery_receive;
++ p_micro->h_temp = micro_temperature_receive;
++ spin_unlock(p_micro->lock);
++ }
++
++ { /*--- timer ---*/
++ init_timer(&batt_timer);
++ batt_timer.expires = jiffies + BATT_PERIOD;
++ batt_timer.data = 0;
++ batt_timer.function = h3600_battery_read_status;
++
++ add_timer(&batt_timer);
++ }
++
++ if (1) printk(KERN_ERR "micro battery probe : end\n");
++ return 0;
++}
++
++static int micro_batt_remove (struct platform_device *pdev)
++{
++ power_supply_unregister(&h3600_battery);
++ { /*--- callback ---*/
++ init_timer(&batt_timer);
++ p_micro->h_batt = NULL;
++ p_micro->h_temp = NULL;
++ spin_unlock(p_micro->lock);
++ }
++ { /*--- timer ---*/
++ del_timer_sync(&batt_timer);
++ }
++ return 0;
++}
++
++static int micro_batt_suspend ( struct platform_device *pdev, pm_message_t state)
++{
++ { /*--- timer ---*/
++ del_timer(&batt_timer);
++ }
++ return 0;
++}
++
++static int micro_batt_resume ( struct platform_device *pdev)
++{
++ { /*--- timer ---*/
++ add_timer(&batt_timer);
++ }
++ return 0;
++}
++
++struct platform_driver micro_batt_device_driver = {
++ .driver = {
++ .name = "h3600-micro-battery",
++ },
++ .probe = micro_batt_probe,
++ .remove = micro_batt_remove,
++ .suspend = micro_batt_suspend,
++ .resume = micro_batt_resume,
++};
++
++static int micro_batt_init (void)
++{
++ return platform_driver_register(&micro_batt_device_driver);
++}
++
++static void micro_batt_cleanup (void)
++{
++ platform_driver_unregister (&micro_batt_device_driver);
++}
++
++module_init (micro_batt_init);
++module_exit (micro_batt_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("gremlin.it");
++MODULE_DESCRIPTION("driver for iPAQ Atmel micro battery");
++
++
+Index: linux-2.6.22/drivers/power/olpc_battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/olpc_battery.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,302 @@
++/*
++ * Battery driver for One Laptop Per Child board.
++ *
++ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
++ *
++ * 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/err.h>
++#include <linux/platform_device.h>
++#include <linux/power_supply.h>
++#include <linux/jiffies.h>
++#include <linux/sched.h>
++#include <asm/io.h>
++
++#define wBAT_VOLTAGE 0xf900 /* *9.76/32, mV */
++#define wBAT_CURRENT 0xf902 /* *15.625/120, mA */
++#define wBAT_TEMP 0xf906 /* *256/1000, °C */
++#define wAMB_TEMP 0xf908 /* *256/1000, °C */
++#define SOC 0xf910 /* percentage */
++#define sMBAT_STATUS 0xfaa4
++#define sBAT_PRESENT 1
++#define sBAT_FULL 2
++#define sBAT_DESTROY 4 /* what is this exactly? */
++#define sBAT_LOW 32
++#define sBAT_DISCHG 64
++#define sMCHARGE_STATUS 0xfaa5
++#define sBAT_CHARGE 1
++#define sBAT_OVERTEMP 4
++#define sBAT_NiMH 8
++#define sPOWER_FLAG 0xfa40
++#define ADAPTER_IN 1
++
++/*********************************************************************
++ * EC locking and access
++ *********************************************************************/
++
++static int lock_ec(void)
++{
++ unsigned long timeo = jiffies + HZ / 20;
++
++ while (1) {
++ unsigned char lock = inb(0x6c) & 0x80;
++ if (!lock)
++ return 0;
++ if (time_after(jiffies, timeo)) {
++ printk(KERN_ERR "olpc_battery: failed to lock EC for "
++ "battery access\n");
++ return 1;
++ }
++ yield();
++ }
++}
++
++static void unlock_ec(void)
++{
++ outb(0xff, 0x6c);
++ return;
++}
++
++static unsigned char read_ec_byte(unsigned short adr)
++{
++ outb(adr >> 8, 0x381);
++ outb(adr, 0x382);
++ return inb(0x383);
++}
++
++static unsigned short read_ec_word(unsigned short adr)
++{
++ return (read_ec_byte(adr) << 8) | read_ec_byte(adr + 1);
++}
++
++/*********************************************************************
++ * Power
++ *********************************************************************/
++
++static int olpc_ac_get_prop(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ int ret = 0;
++
++ if (lock_ec())
++ return -EIO;
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_ONLINE:
++ if (!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT)) {
++ ret = -ENODEV;
++ goto out;
++ }
++ val->intval = !!(read_ec_byte(sPOWER_FLAG) & ADAPTER_IN);
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++out:
++ unlock_ec();
++ return ret;
++}
++
++static enum power_supply_property olpc_ac_props[] = {
++ POWER_SUPPLY_PROP_ONLINE,
++};
++
++static struct power_supply olpc_ac = {
++ .name = "olpc-ac",
++ .type = POWER_SUPPLY_TYPE_MAINS,
++ .properties = olpc_ac_props,
++ .num_properties = ARRAY_SIZE(olpc_ac_props),
++ .get_property = olpc_ac_get_prop,
++};
++
++/*********************************************************************
++ * Battery properties
++ *********************************************************************/
++
++static int olpc_bat_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ int ret = 0;
++
++ if (lock_ec())
++ return -EIO;
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ {
++ int status = POWER_SUPPLY_STATUS_UNKNOWN;
++
++ val->intval = read_ec_byte(sMBAT_STATUS);
++
++ if (!(val->intval & sBAT_PRESENT)) {
++ ret = -ENODEV;
++ goto out;
++ }
++
++ if (val->intval & sBAT_DISCHG)
++ status = POWER_SUPPLY_STATUS_DISCHARGING;
++ else if (val->intval & sBAT_FULL)
++ status = POWER_SUPPLY_STATUS_FULL;
++
++ val->intval = read_ec_byte(sMCHARGE_STATUS);
++ if (val->intval & sBAT_CHARGE)
++ status = POWER_SUPPLY_STATUS_CHARGING;
++
++ val->intval = status;
++ break;
++ }
++ case POWER_SUPPLY_PROP_PRESENT:
++ val->intval = !!(read_ec_byte(sMBAT_STATUS) & sBAT_PRESENT);
++ break;
++ case POWER_SUPPLY_PROP_HEALTH:
++ val->intval = read_ec_byte(sMCHARGE_STATUS);
++ if (val->intval & sBAT_OVERTEMP)
++ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT;
++ else
++ val->intval = POWER_SUPPLY_HEALTH_GOOD;
++ break;
++ case POWER_SUPPLY_PROP_TECHNOLOGY:
++ val->intval = read_ec_byte(sMCHARGE_STATUS);
++ if (val->intval & sBAT_NiMH)
++ val->intval = POWER_SUPPLY_TECHNOLOGY_NIMH;
++ else
++ val->intval = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
++ val->intval = read_ec_byte(wBAT_VOLTAGE) * 9760L / 32;
++ break;
++ case POWER_SUPPLY_PROP_CURRENT_AVG:
++ val->intval = read_ec_byte(wBAT_CURRENT) * 15625L / 120;
++ break;
++ case POWER_SUPPLY_PROP_CAPACITY:
++ val->intval = read_ec_byte(SOC);
++ break;
++ case POWER_SUPPLY_PROP_CAPACITY_LEVEL:
++ val->intval = read_ec_byte(sMBAT_STATUS);
++ if (val->intval & sBAT_FULL)
++ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_FULL;
++ else if (val->intval & sBAT_LOW)
++ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_LOW;
++ else
++ val->intval = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL;
++ break;
++ case POWER_SUPPLY_PROP_TEMP:
++ val->intval = read_ec_byte(wBAT_TEMP) * 256 / 100;
++ break;
++ case POWER_SUPPLY_PROP_TEMP_AMBIENT:
++ val->intval = read_ec_byte(wAMB_TEMP) * 256 / 100;
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++
++out:
++ unlock_ec();
++ return ret;
++}
++
++static enum power_supply_property olpc_bat_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_PRESENT,
++ POWER_SUPPLY_PROP_HEALTH,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_AVG,
++ POWER_SUPPLY_PROP_CURRENT_AVG,
++ POWER_SUPPLY_PROP_CAPACITY,
++ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
++ POWER_SUPPLY_PROP_TEMP,
++ POWER_SUPPLY_PROP_TEMP_AMBIENT,
++};
++
++/*********************************************************************
++ * Initialisation
++ *********************************************************************/
++
++static struct platform_device *bat_pdev;
++
++static struct power_supply olpc_bat = {
++ .properties = olpc_bat_props,
++ .num_properties = ARRAY_SIZE(olpc_bat_props),
++ .get_property = olpc_bat_get_property,
++ .use_for_apm = 1,
++};
++
++static int __init olpc_bat_init(void)
++{
++ int ret = 0;
++ unsigned short tmp;
++
++ if (!request_region(0x380, 4, "olpc-battery")) {
++ ret = -EIO;
++ goto region_failed;
++ }
++
++ if (lock_ec()) {
++ ret = -EIO;
++ goto lock_failed;
++ }
++
++ tmp = read_ec_word(0xfe92);
++ unlock_ec();
++
++ if (tmp != 0x380) {
++ /* Doesn't look like OLPC EC */
++ ret = -ENODEV;
++ goto not_olpc_ec;
++ }
++
++ bat_pdev = platform_device_register_simple("olpc-battery", 0, NULL, 0);
++ if (IS_ERR(bat_pdev)) {
++ ret = PTR_ERR(bat_pdev);
++ goto pdev_failed;
++ }
++
++ ret = power_supply_register(&bat_pdev->dev, &olpc_ac);
++ if (ret)
++ goto ac_failed;
++
++ olpc_bat.name = bat_pdev->name;
++
++ ret = power_supply_register(&bat_pdev->dev, &olpc_bat);
++ if (ret)
++ goto battery_failed;
++
++ goto success;
++
++battery_failed:
++ power_supply_unregister(&olpc_ac);
++ac_failed:
++ platform_device_unregister(bat_pdev);
++pdev_failed:
++not_olpc_ec:
++lock_failed:
++ release_region(0x380, 4);
++region_failed:
++success:
++ return ret;
++}
++
++static void __exit olpc_bat_exit(void)
++{
++ power_supply_unregister(&olpc_bat);
++ power_supply_unregister(&olpc_ac);
++ platform_device_unregister(bat_pdev);
++ release_region(0x380, 4);
++ return;
++}
++
++module_init(olpc_bat_init);
++module_exit(olpc_bat_exit);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("Battery driver for One Laptop Per Child "
++ "($100 laptop) board.");
+Index: linux-2.6.22/drivers/power/pda_power.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/pda_power.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,263 @@
++/*
++ * Common power driver for PDAs and phones with one or two external
++ * power supplies (AC/USB) connected to main and backup batteries,
++ * and optional builtin charger.
++ *
++ * Copyright 2007 Anton Vorontsov <cbou@mail.ru>
++ *
++ * 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/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/power_supply.h>
++#include <linux/pda_power.h>
++#include <linux/timer.h>
++#include <linux/jiffies.h>
++
++static inline unsigned int get_irq_flags(struct resource *res)
++{
++ unsigned int flags = IRQF_DISABLED | IRQF_SHARED;
++
++ flags |= res->flags & IRQF_TRIGGER_MASK;
++
++ return flags;
++}
++
++static struct device *dev;
++static struct pda_power_pdata *pdata;
++static struct resource *ac_irq, *usb_irq;
++static struct timer_list charger_timer;
++static struct timer_list supply_timer;
++
++static int pda_power_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ switch (psp) {
++ case POWER_SUPPLY_PROP_ONLINE:
++ if (psy->type == POWER_SUPPLY_TYPE_MAINS)
++ val->intval = pdata->is_ac_online ?
++ pdata->is_ac_online() : 0;
++ else
++ val->intval = pdata->is_usb_online ?
++ pdata->is_usb_online() : 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static enum power_supply_property pda_power_props[] = {
++ POWER_SUPPLY_PROP_ONLINE,
++};
++
++static char *pda_power_supplied_to[] = {
++ "main-battery",
++ "backup-battery",
++};
++
++static struct power_supply pda_power_supplies[] = {
++ {
++ .name = "ac",
++ .type = POWER_SUPPLY_TYPE_MAINS,
++ .supplied_to = pda_power_supplied_to,
++ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
++ .properties = pda_power_props,
++ .num_properties = ARRAY_SIZE(pda_power_props),
++ .get_property = pda_power_get_property,
++ },
++ {
++ .name = "usb",
++ .type = POWER_SUPPLY_TYPE_USB,
++ .supplied_to = pda_power_supplied_to,
++ .num_supplicants = ARRAY_SIZE(pda_power_supplied_to),
++ .properties = pda_power_props,
++ .num_properties = ARRAY_SIZE(pda_power_props),
++ .get_property = pda_power_get_property,
++ },
++};
++
++static void update_charger(void)
++{
++ if (!pdata->set_charge)
++ return;
++
++ if (pdata->is_ac_online && pdata->is_ac_online()) {
++ dev_dbg(dev, "charger on (AC)\n");
++ pdata->set_charge(PDA_POWER_CHARGE_AC);
++ }
++ else if (pdata->is_usb_online && pdata->is_usb_online()) {
++ dev_dbg(dev, "charger on (USB)\n");
++ pdata->set_charge(PDA_POWER_CHARGE_USB);
++ }
++ else {
++ dev_dbg(dev, "charger off\n");
++ pdata->set_charge(0);
++ }
++
++ return;
++}
++
++static void supply_timer_func(unsigned long irq)
++{
++ if (ac_irq && irq == ac_irq->start)
++ power_supply_changed(&pda_power_supplies[0]);
++ else if (usb_irq && irq == usb_irq->start)
++ power_supply_changed(&pda_power_supplies[1]);
++ return;
++}
++
++static void charger_timer_func(unsigned long irq)
++{
++ update_charger();
++
++ /* Okay, charger set. Now wait a bit before notifying supplicants,
++ * charge power should stabilize. */
++ supply_timer.data = irq;
++ mod_timer(&supply_timer,
++ jiffies + msecs_to_jiffies(pdata->wait_for_charger));
++ return;
++}
++
++static irqreturn_t power_changed_isr(int irq, void *unused)
++{
++ /* Wait a bit before reading ac/usb line status and setting charger,
++ * because ac/usb status readings may lag from irq. */
++ charger_timer.data = irq;
++ mod_timer(&charger_timer,
++ jiffies + msecs_to_jiffies(pdata->wait_for_status));
++ return IRQ_HANDLED;
++}
++
++static int pda_power_probe(struct platform_device *pdev)
++{
++ int ret = 0;
++
++ dev = &pdev->dev;
++
++ if (pdev->id != -1) {
++ dev_err(dev, "it's meaningless to register several "
++ "pda_powers, use id = -1\n");
++ ret = -EINVAL;
++ goto wrongid;
++ }
++
++ pdata = pdev->dev.platform_data;
++
++ update_charger();
++
++ if (!pdata->wait_for_status)
++ pdata->wait_for_status = 500;
++
++ if (!pdata->wait_for_charger)
++ pdata->wait_for_charger = 500;
++
++ setup_timer(&charger_timer, charger_timer_func, 0);
++ setup_timer(&supply_timer, supply_timer_func, 0);
++
++ ac_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ac");
++ usb_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "usb");
++ if (!ac_irq && !usb_irq) {
++ dev_err(dev, "no ac/usb irq specified\n");
++ ret = -ENODEV;
++ goto noirqs;
++ }
++
++ if (pdata->supplied_to) {
++ pda_power_supplies[0].supplied_to = pdata->supplied_to;
++ pda_power_supplies[1].supplied_to = pdata->supplied_to;
++ pda_power_supplies[0].num_supplicants = pdata->num_supplicants;
++ pda_power_supplies[1].num_supplicants = pdata->num_supplicants;
++ }
++
++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]);
++ if (ret) {
++ dev_err(dev, "failed to register %s power supply\n",
++ pda_power_supplies[0].name);
++ goto supply0_failed;
++ }
++
++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]);
++ if (ret) {
++ dev_err(dev, "failed to register %s power supply\n",
++ pda_power_supplies[1].name);
++ goto supply1_failed;
++ }
++
++ if (ac_irq) {
++ ret = request_irq(ac_irq->start, power_changed_isr,
++ get_irq_flags(ac_irq), ac_irq->name,
++ &pda_power_supplies[0]);
++ if (ret) {
++ dev_err(dev, "request ac irq failed\n");
++ goto ac_irq_failed;
++ }
++ }
++
++ if (usb_irq) {
++ ret = request_irq(usb_irq->start, power_changed_isr,
++ get_irq_flags(usb_irq), usb_irq->name,
++ &pda_power_supplies[1]);
++ if (ret) {
++ dev_err(dev, "request usb irq failed\n");
++ goto usb_irq_failed;
++ }
++ }
++
++ goto success;
++
++usb_irq_failed:
++ if (ac_irq)
++ free_irq(ac_irq->start, &pda_power_supplies[0]);
++ac_irq_failed:
++ power_supply_unregister(&pda_power_supplies[1]);
++supply1_failed:
++ power_supply_unregister(&pda_power_supplies[0]);
++supply0_failed:
++noirqs:
++wrongid:
++success:
++ return ret;
++}
++
++static int pda_power_remove(struct platform_device *pdev)
++{
++ if (usb_irq)
++ free_irq(usb_irq->start, &pda_power_supplies[1]);
++ if (ac_irq)
++ free_irq(ac_irq->start, &pda_power_supplies[0]);
++ del_timer_sync(&charger_timer);
++ del_timer_sync(&supply_timer);
++ power_supply_unregister(&pda_power_supplies[1]);
++ power_supply_unregister(&pda_power_supplies[0]);
++ return 0;
++}
++
++static struct platform_driver pda_power_pdrv = {
++ .driver = {
++ .name = "pda-power",
++ },
++ .probe = pda_power_probe,
++ .remove = pda_power_remove,
++};
++
++static int __init pda_power_init(void)
++{
++ return platform_driver_register(&pda_power_pdrv);
++}
++
++static void __exit pda_power_exit(void)
++{
++ platform_driver_unregister(&pda_power_pdrv);
++ return;
++}
++
++module_init(pda_power_init);
++module_exit(pda_power_exit);
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
+Index: linux-2.6.22/drivers/power/pmu_battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/pmu_battery.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,215 @@
++/*
++ * Battery class driver for Apple PMU
++ *
++ * Copyright © 2006 David Woodhouse <dwmw2@infradead.org>
++ *
++ * 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/platform_device.h>
++#include <linux/err.h>
++#include <linux/power_supply.h>
++#include <linux/adb.h>
++#include <linux/pmu.h>
++
++static struct pmu_battery_dev {
++ struct power_supply bat;
++ struct pmu_battery_info *pbi;
++ char name[16];
++ int propval;
++} *pbats[PMU_MAX_BATTERIES];
++
++#define to_pmu_battery_dev(x) container_of(x, struct pmu_battery_dev, bat)
++
++/*********************************************************************
++ * Power
++ *********************************************************************/
++
++static int pmu_get_ac_prop(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ switch (psp) {
++ case POWER_SUPPLY_PROP_ONLINE:
++ val->intval = (!!(pmu_power_flags & PMU_PWR_AC_PRESENT)) ||
++ (pmu_battery_count == 0);
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static enum power_supply_property pmu_ac_props[] = {
++ POWER_SUPPLY_PROP_ONLINE,
++};
++
++static struct power_supply pmu_ac = {
++ .name = "pmu-ac",
++ .type = POWER_SUPPLY_TYPE_MAINS,
++ .properties = pmu_ac_props,
++ .num_properties = ARRAY_SIZE(pmu_ac_props),
++ .get_property = pmu_get_ac_prop,
++};
++
++/*********************************************************************
++ * Battery properties
++ *********************************************************************/
++
++static char *pmu_batt_types[] = {
++ "Smart", "Comet", "Hooper", "Unknown"
++};
++
++static char *pmu_bat_get_model_name(struct pmu_battery_info *pbi)
++{
++ switch (pbi->flags & PMU_BATT_TYPE_MASK) {
++ case PMU_BATT_TYPE_SMART:
++ return pmu_batt_types[0];
++ case PMU_BATT_TYPE_COMET:
++ return pmu_batt_types[1];
++ case PMU_BATT_TYPE_HOOPER:
++ return pmu_batt_types[2];
++ default: break;
++ }
++ return pmu_batt_types[3];
++}
++
++static int pmu_bat_get_property(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val)
++{
++ struct pmu_battery_dev *pbat = to_pmu_battery_dev(psy);
++ struct pmu_battery_info *pbi = pbat->pbi;
++
++ switch (psp) {
++ case POWER_SUPPLY_PROP_STATUS:
++ if (pbi->flags & PMU_BATT_CHARGING)
++ val->intval = POWER_SUPPLY_STATUS_CHARGING;
++ else
++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
++ break;
++ case POWER_SUPPLY_PROP_PRESENT:
++ val->intval = !!(pbi->flags & PMU_BATT_PRESENT);
++ break;
++ case POWER_SUPPLY_PROP_MODEL_NAME:
++ val->strval = pmu_bat_get_model_name(pbi);
++ break;
++ case POWER_SUPPLY_PROP_ENERGY_AVG:
++ val->intval = pbi->charge * 1000; /* mWh -> µWh */
++ break;
++ case POWER_SUPPLY_PROP_ENERGY_FULL:
++ val->intval = pbi->max_charge * 1000; /* mWh -> µWh */
++ break;
++ case POWER_SUPPLY_PROP_CURRENT_AVG:
++ val->intval = pbi->amperage * 1000; /* mA -> µA */
++ break;
++ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
++ val->intval = pbi->voltage * 1000; /* mV -> µV */
++ break;
++ case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
++ val->intval = pbi->time_remaining;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static enum power_supply_property pmu_bat_props[] = {
++ POWER_SUPPLY_PROP_STATUS,
++ POWER_SUPPLY_PROP_PRESENT,
++ POWER_SUPPLY_PROP_MODEL_NAME,
++ POWER_SUPPLY_PROP_ENERGY_AVG,
++ POWER_SUPPLY_PROP_ENERGY_FULL,
++ POWER_SUPPLY_PROP_CURRENT_AVG,
++ POWER_SUPPLY_PROP_VOLTAGE_AVG,
++ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
++};
++
++/*********************************************************************
++ * Initialisation
++ *********************************************************************/
++
++static struct platform_device *bat_pdev;
++
++static int __init pmu_bat_init(void)
++{
++ int ret;
++ int i;
++
++ bat_pdev = platform_device_register_simple("pmu-battery",
++ 0, NULL, 0);
++ if (IS_ERR(bat_pdev)) {
++ ret = PTR_ERR(bat_pdev);
++ goto pdev_register_failed;
++ }
++
++ ret = power_supply_register(&bat_pdev->dev, &pmu_ac);
++ if (ret)
++ goto ac_register_failed;
++
++ for (i = 0; i < pmu_battery_count; i++) {
++ struct pmu_battery_dev *pbat = kzalloc(sizeof(*pbat),
++ GFP_KERNEL);
++ if (!pbat)
++ break;
++
++ sprintf(pbat->name, "PMU battery %d", i);
++ pbat->bat.name = pbat->name;
++ pbat->bat.properties = pmu_bat_props;
++ pbat->bat.num_properties = ARRAY_SIZE(pmu_bat_props);
++ pbat->bat.get_property = pmu_bat_get_property;
++ pbat->pbi = &pmu_batteries[i];
++
++ ret = power_supply_register(&bat_pdev->dev, &pbat->bat);
++ if (ret) {
++ kfree(pbat);
++ goto battery_register_failed;
++ }
++ pbats[i] = pbat;
++ }
++
++ goto success;
++
++battery_register_failed:
++ while (i--) {
++ if (!pbats[i])
++ continue;
++ power_supply_unregister(&pbats[i]->bat);
++ kfree(pbats[i]);
++ }
++ power_supply_unregister(&pmu_ac);
++ac_register_failed:
++ platform_device_unregister(bat_pdev);
++pdev_register_failed:
++success:
++ return ret;
++}
++
++static void __exit pmu_bat_exit(void)
++{
++ int i;
++
++ for (i = 0; i < PMU_MAX_BATTERIES; i++) {
++ if (!pbats[i])
++ continue;
++ power_supply_unregister(&pbats[i]->bat);
++ kfree(pbats[i]);
++ }
++ power_supply_unregister(&pmu_ac);
++ platform_device_unregister(bat_pdev);
++
++ return;
++}
++
++module_init(pmu_bat_init);
++module_exit(pmu_bat_exit);
++
++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
++MODULE_LICENSE("GPL");
++MODULE_DESCRIPTION("PMU battery driver");
+Index: linux-2.6.22/drivers/power/power_supply_core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/power_supply_core.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,168 @@
++/*
++ * Universal power supply monitor class
++ *
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2004 Szabolcs Gyurko
++ * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
++ *
++ * Modified: 2004, Oct Szabolcs Gyurko
++ *
++ * You may use this code as per GPL version 2
++ */
++
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/err.h>
++#include <linux/power_supply.h>
++#include "power_supply.h"
++
++struct class *power_supply_class;
++
++static void power_supply_changed_work(struct work_struct *work)
++{
++ struct power_supply *psy = container_of(work, struct power_supply,
++ changed_work);
++ int i;
++
++ dev_dbg(psy->dev, "%s\n", __FUNCTION__);
++
++ for (i = 0; i < psy->num_supplicants; i++) {
++ struct device *dev;
++
++ down(&power_supply_class->sem);
++ list_for_each_entry(dev, &power_supply_class->devices, node) {
++ struct power_supply *pst = dev_get_drvdata(dev);
++
++ if (!strcmp(psy->supplied_to[i], pst->name)) {
++ if (pst->external_power_changed)
++ pst->external_power_changed(pst);
++ }
++ }
++ up(&power_supply_class->sem);
++ }
++
++ power_supply_update_leds(psy);
++
++ kobject_uevent(&psy->dev->kobj, KOBJ_CHANGE);
++
++ return;
++}
++
++void power_supply_changed(struct power_supply *psy)
++{
++ dev_dbg(psy->dev, "%s\n", __FUNCTION__);
++
++ schedule_work(&psy->changed_work);
++
++ return;
++}
++
++int power_supply_am_i_supplied(struct power_supply *psy)
++{
++ union power_supply_propval ret = {0,};
++ struct device *dev;
++
++ down(&power_supply_class->sem);
++ list_for_each_entry(dev, &power_supply_class->devices, node) {
++ struct power_supply *epsy = dev_get_drvdata(dev);
++ int i;
++
++ for (i = 0; i < epsy->num_supplicants; i++) {
++ if (!strcmp(epsy->supplied_to[i], psy->name)) {
++ if (epsy->get_property(epsy,
++ POWER_SUPPLY_PROP_ONLINE, &ret))
++ continue;
++ if (ret.intval)
++ goto out;
++ }
++ }
++ }
++out:
++ up(&power_supply_class->sem);
++
++ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, ret.intval);
++
++ return ret.intval;
++}
++
++int power_supply_register(struct device *parent, struct power_supply *psy)
++{
++ int rc = 0;
++
++ psy->dev = device_create(power_supply_class, parent, 0,
++ "%s", psy->name);
++ if (IS_ERR(psy->dev)) {
++ rc = PTR_ERR(psy->dev);
++ goto dev_create_failed;
++ }
++
++ dev_set_drvdata(psy->dev, psy);
++
++ 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);
++ if (rc)
++ goto create_triggers_failed;
++
++ power_supply_changed(psy);
++
++ goto success;
++
++create_triggers_failed:
++ power_supply_remove_attrs(psy);
++create_attrs_failed:
++ device_unregister(psy->dev);
++dev_create_failed:
++success:
++ return rc;
++}
++
++void power_supply_unregister(struct power_supply *psy)
++{
++ flush_scheduled_work();
++ power_supply_remove_triggers(psy);
++ power_supply_remove_attrs(psy);
++ device_unregister(psy->dev);
++ return;
++}
++
++static int __init power_supply_class_init(void)
++{
++ power_supply_class = class_create(THIS_MODULE, "power_supply");
++
++ if (IS_ERR(power_supply_class))
++ return PTR_ERR(power_supply_class);
++
++ power_supply_class->dev_uevent = power_supply_uevent;
++
++ return 0;
++}
++
++static void __exit power_supply_class_exit(void)
++{
++ class_destroy(power_supply_class);
++ return;
++}
++
++EXPORT_SYMBOL_GPL(power_supply_changed);
++EXPORT_SYMBOL_GPL(power_supply_am_i_supplied);
++EXPORT_SYMBOL_GPL(power_supply_register);
++EXPORT_SYMBOL_GPL(power_supply_unregister);
++
++/* exported for the APM Power driver, APM emulation */
++EXPORT_SYMBOL_GPL(power_supply_class);
++
++subsys_initcall(power_supply_class_init);
++module_exit(power_supply_class_exit);
++
++MODULE_DESCRIPTION("Universal power supply monitor class");
++MODULE_AUTHOR("Ian Molton <spyro@f2s.com>, "
++ "Szabolcs Gyurko, "
++ "Anton Vorontsov <cbou@mail.ru>");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22/drivers/power/power_supply.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/power_supply.h 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,42 @@
++/*
++ * Functions private to power supply class
++ *
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2004 Szabolcs Gyurko
++ * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
++ *
++ * Modified: 2004, Oct Szabolcs Gyurko
++ *
++ * You may use this code as per GPL version 2
++ */
++
++#ifdef CONFIG_SYSFS
++
++extern int power_supply_create_attrs(struct power_supply *psy);
++extern void power_supply_remove_attrs(struct power_supply *psy);
++extern int power_supply_uevent(struct device *dev, char **envp, int num_envp,
++ char *buffer, int buffer_size);
++
++#else
++
++static inline int power_supply_create_attrs(struct power_supply *psy)
++{ return 0; }
++static inline void power_supply_remove_attrs(struct power_supply *psy) {}
++#define power_supply_uevent NULL
++
++#endif /* CONFIG_SYSFS */
++
++#ifdef CONFIG_LEDS_TRIGGERS
++
++extern void power_supply_update_leds(struct power_supply *psy);
++extern int power_supply_create_triggers(struct power_supply *psy);
++extern void power_supply_remove_triggers(struct power_supply *psy);
++
++#else
++
++static inline void power_supply_update_leds(struct power_supply *psy) {}
++static inline int power_supply_create_triggers(struct power_supply *psy)
++{ return 0; }
++static inline void power_supply_remove_triggers(struct power_supply *psy) {}
++
++#endif /* CONFIG_LEDS_TRIGGERS */
+Index: linux-2.6.22/drivers/power/power_supply_leds.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/power_supply_leds.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,188 @@
++/*
++ * LEDs triggers for power supply class
++ *
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2004 Szabolcs Gyurko
++ * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
++ *
++ * Modified: 2004, Oct Szabolcs Gyurko
++ *
++ * You may use this code as per GPL version 2
++ */
++
++#include <linux/power_supply.h>
++
++/* If we have hwtimer trigger, then use it to blink charging LED */
++
++#if defined(CONFIG_LEDS_TRIGGER_HWTIMER) || \
++ (defined(CONFIG_BATTERY_MODULE) && \
++ defined(CONFIG_LEDS_TRIGGER_HWTIMER_MODULE))
++ #define led_trigger_register_charging led_trigger_register_hwtimer
++ #define led_trigger_unregister_charging led_trigger_unregister_hwtimer
++#else
++ #define led_trigger_register_charging led_trigger_register_simple
++ #define led_trigger_unregister_charging led_trigger_unregister_simple
++#endif
++
++/* Battery specific LEDs triggers. */
++
++static void power_supply_update_bat_leds(struct power_supply *psy)
++{
++ union power_supply_propval status;
++
++ if (psy->get_property(psy, POWER_SUPPLY_PROP_STATUS, &status))
++ return;
++
++ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, status.intval);
++
++ switch(status.intval) {
++ case POWER_SUPPLY_STATUS_FULL:
++ led_trigger_event(psy->charging_full_trig, LED_FULL);
++ led_trigger_event(psy->charging_trig, LED_OFF);
++ led_trigger_event(psy->full_trig, LED_FULL);
++ break;
++ case POWER_SUPPLY_STATUS_CHARGING:
++ led_trigger_event(psy->charging_full_trig, LED_FULL);
++ led_trigger_event(psy->charging_trig, LED_FULL);
++ led_trigger_event(psy->full_trig, LED_OFF);
++ break;
++ default:
++ led_trigger_event(psy->charging_full_trig, LED_OFF);
++ led_trigger_event(psy->charging_trig, LED_OFF);
++ led_trigger_event(psy->full_trig, LED_OFF);
++ break;
++ }
++
++ return;
++}
++
++static int power_supply_create_bat_triggers(struct power_supply *psy)
++{
++ int rc = 0;
++
++ psy->charging_full_trig_name = kmalloc(strlen(psy->name) +
++ sizeof("-charging-or-full"), GFP_KERNEL);
++ if (!psy->charging_full_trig_name)
++ goto charging_full_failed;
++
++ psy->charging_trig_name = kmalloc(strlen(psy->name) +
++ sizeof("-charging"), GFP_KERNEL);
++ if (!psy->charging_trig_name)
++ goto charging_failed;
++
++ psy->full_trig_name = kmalloc(strlen(psy->name) +
++ sizeof("-full"), GFP_KERNEL);
++ if (!psy->full_trig_name)
++ goto full_failed;
++
++ strcpy(psy->charging_full_trig_name, psy->name);
++ strcat(psy->charging_full_trig_name, "-charging-or-full");
++ strcpy(psy->charging_trig_name, psy->name);
++ strcat(psy->charging_trig_name, "-charging");
++ strcpy(psy->full_trig_name, psy->name);
++ strcat(psy->full_trig_name, "-full");
++
++ led_trigger_register_simple(psy->charging_full_trig_name,
++ &psy->charging_full_trig);
++ led_trigger_register_charging(psy->charging_trig_name,
++ &psy->charging_trig);
++ led_trigger_register_simple(psy->full_trig_name,
++ &psy->full_trig);
++
++ goto success;
++
++full_failed:
++ kfree(psy->charging_trig_name);
++charging_failed:
++ kfree(psy->charging_full_trig_name);
++charging_full_failed:
++ rc = -ENOMEM;
++success:
++ return rc;
++}
++
++static void power_supply_remove_bat_triggers(struct power_supply *psy)
++{
++ led_trigger_unregister_simple(psy->charging_full_trig);
++ led_trigger_unregister_charging(psy->charging_trig);
++ led_trigger_unregister_simple(psy->full_trig);
++ kfree(psy->full_trig_name);
++ kfree(psy->charging_trig_name);
++ kfree(psy->charging_full_trig_name);
++ return;
++}
++
++/* Generated power specific LEDs triggers. */
++
++static void power_supply_update_gen_leds(struct power_supply *psy)
++{
++ union power_supply_propval online;
++
++ if (psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &online))
++ return;
++
++ dev_dbg(psy->dev, "%s %d\n", __FUNCTION__, online.intval);
++
++ if (online.intval)
++ led_trigger_event(psy->online_trig, LED_FULL);
++ else
++ led_trigger_event(psy->online_trig, LED_OFF);
++
++ return;
++}
++
++static int power_supply_create_gen_triggers(struct power_supply *psy)
++{
++ int rc = 0;
++
++ psy->online_trig_name = kmalloc(strlen(psy->name) + sizeof("-online"),
++ GFP_KERNEL);
++ if (!psy->online_trig_name)
++ goto online_failed;
++
++ strcpy(psy->online_trig_name, psy->name);
++ strcat(psy->online_trig_name, "-online");
++
++ led_trigger_register_simple(psy->online_trig_name, &psy->online_trig);
++
++ goto success;
++
++online_failed:
++ rc = -ENOMEM;
++success:
++ return rc;
++}
++
++static void power_supply_remove_gen_triggers(struct power_supply *psy)
++{
++ led_trigger_unregister_simple(psy->online_trig);
++ kfree(psy->online_trig_name);
++ return;
++}
++
++/* Choice what triggers to create&update. */
++
++void power_supply_update_leds(struct power_supply *psy)
++{
++ if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
++ power_supply_update_bat_leds(psy);
++ else
++ power_supply_update_gen_leds(psy);
++ return;
++}
++
++int power_supply_create_triggers(struct power_supply *psy)
++{
++ if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
++ return power_supply_create_bat_triggers(psy);
++ return power_supply_create_gen_triggers(psy);
++}
++
++void power_supply_remove_triggers(struct power_supply *psy)
++{
++ if (psy->type == POWER_SUPPLY_TYPE_BATTERY)
++ power_supply_remove_bat_triggers(psy);
++ else
++ power_supply_remove_gen_triggers(psy);
++ return;
++}
+Index: linux-2.6.22/drivers/power/power_supply_sysfs.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/power_supply_sysfs.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,289 @@
++/*
++ * Sysfs interface for the universal power supply monitor class
++ *
++ * Copyright © 2007 David Woodhouse <dwmw2@infradead.org>
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2004 Szabolcs Gyurko
++ * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
++ *
++ * Modified: 2004, Oct Szabolcs Gyurko
++ *
++ * You may use this code as per GPL version 2
++ */
++
++#include <linux/ctype.h>
++#include <linux/power_supply.h>
++
++/*
++ * This is because the name "current" breaks the device attr macro.
++ * The "current" word resolvs to "(get_current())" so instead of
++ * "current" "(get_current())" appears in the sysfs.
++ *
++ * The source of this definition is the device.h which calls __ATTR
++ * macro in sysfs.h which calls the __stringify macro.
++ *
++ * Only modification that the name is not tried to be resolved
++ * (as a macro let's say).
++ */
++
++#define POWER_SUPPLY_ATTR(_name) \
++{ \
++ .attr = { .name = #_name, .mode = 0444, .owner = THIS_MODULE }, \
++ .show = power_supply_show_property, \
++ .store = NULL, \
++}
++
++static struct device_attribute power_supply_attrs[];
++
++static ssize_t power_supply_show_property(struct device *dev,
++ struct device_attribute *attr,
++ char *buf) {
++ static char *status_text[] = {
++ "Unknown", "Charging", "Discharging", "Not charging", "Full"
++ };
++ static char *health_text[] = {
++ "Unknown", "Good", "Overheat", "Dead"
++ };
++ static char *technology_text[] = {
++ "Unknown", "NiMH", "Li-ion", "Li-poly"
++ };
++ static char *capacity_level_text[] = {
++ "Unknown", "Critical", "Low", "Normal", "High", "Full"
++ };
++ ssize_t ret;
++ struct power_supply *psy = dev_get_drvdata(dev);
++ const ptrdiff_t off = attr - power_supply_attrs;
++ union power_supply_propval value;
++
++ ret = psy->get_property(psy, off, &value);
++
++ if (ret < 0) {
++ dev_err(dev, "driver failed to report `%s' property\n",
++ attr->attr.name);
++ return ret;
++ }
++
++ if (off == POWER_SUPPLY_PROP_STATUS)
++ return sprintf(buf, "%s\n", status_text[value.intval]);
++ else if (off == POWER_SUPPLY_PROP_HEALTH)
++ return sprintf(buf, "%s\n", health_text[value.intval]);
++ else if (off == POWER_SUPPLY_PROP_TECHNOLOGY)
++ return sprintf(buf, "%s\n", technology_text[value.intval]);
++ else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL)
++ return sprintf(buf, "%s\n",
++ capacity_level_text[value.intval]);
++ else if (off == POWER_SUPPLY_PROP_MODEL_NAME)
++ return sprintf(buf, "%s\n", value.strval);
++
++ return sprintf(buf, "%d\n", value.intval);
++}
++
++/* Must be in the same order as POWER_SUPPLY_PROP_* */
++static struct device_attribute power_supply_attrs[] = {
++ /* Properties of type `int' */
++ POWER_SUPPLY_ATTR(status),
++ POWER_SUPPLY_ATTR(health),
++ POWER_SUPPLY_ATTR(present),
++ POWER_SUPPLY_ATTR(online),
++ POWER_SUPPLY_ATTR(technology),
++ POWER_SUPPLY_ATTR(voltage_max_design),
++ POWER_SUPPLY_ATTR(voltage_min_design),
++ POWER_SUPPLY_ATTR(voltage_now),
++ POWER_SUPPLY_ATTR(voltage_avg),
++ POWER_SUPPLY_ATTR(current_now),
++ POWER_SUPPLY_ATTR(current_avg),
++ POWER_SUPPLY_ATTR(charge_full_design),
++ POWER_SUPPLY_ATTR(charge_empty_design),
++ POWER_SUPPLY_ATTR(charge_full),
++ POWER_SUPPLY_ATTR(charge_empty),
++ POWER_SUPPLY_ATTR(charge_now),
++ POWER_SUPPLY_ATTR(charge_avg),
++ POWER_SUPPLY_ATTR(energy_full_design),
++ POWER_SUPPLY_ATTR(energy_empty_design),
++ POWER_SUPPLY_ATTR(energy_full),
++ POWER_SUPPLY_ATTR(energy_empty),
++ POWER_SUPPLY_ATTR(energy_now),
++ POWER_SUPPLY_ATTR(energy_avg),
++ POWER_SUPPLY_ATTR(capacity),
++ POWER_SUPPLY_ATTR(capacity_level),
++ POWER_SUPPLY_ATTR(temp),
++ POWER_SUPPLY_ATTR(temp_ambient),
++ POWER_SUPPLY_ATTR(time_to_empty_now),
++ POWER_SUPPLY_ATTR(time_to_empty_avg),
++ POWER_SUPPLY_ATTR(time_to_full_now),
++ POWER_SUPPLY_ATTR(time_to_full_avg),
++ /* Properties of type `const char *' */
++ POWER_SUPPLY_ATTR(model_name),
++};
++
++static ssize_t power_supply_show_static_attrs(struct device *dev,
++ struct device_attribute *attr,
++ 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 struct device_attribute power_supply_static_attrs[] = {
++ __ATTR(type, 0444, power_supply_show_static_attrs, NULL),
++};
++
++int power_supply_create_attrs(struct power_supply *psy)
++{
++ int rc = 0;
++ int i, j;
++
++ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++) {
++ 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[psy->properties[i]]);
++succeed:
++ return rc;
++}
++
++void power_supply_remove_attrs(struct power_supply *psy)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(power_supply_static_attrs); i++)
++ device_remove_file(psy->dev,
++ &power_supply_static_attrs[i]);
++
++ for (i = 0; i < psy->num_properties; i++)
++ device_remove_file(psy->dev,
++ &power_supply_attrs[psy->properties[i]]);
++
++ return;
++}
++
++static char *kstruprdup(const char *str, gfp_t gfp)
++{
++ char *ret, *ustr;
++
++ ustr = ret = kmalloc(strlen(str) + 1, gfp);
++
++ if (!ret)
++ return NULL;
++
++ while (*str)
++ *ustr++ = toupper(*str++);
++
++ *ustr = 0;
++
++ return ret;
++}
++
++int power_supply_uevent(struct device *dev, char **envp, int num_envp,
++ char *buffer, int buffer_size)
++{
++ struct power_supply *psy = dev_get_drvdata(dev);
++ int i = 0, length = 0, ret = 0, j;
++ char *prop_buf;
++ char *attrname;
++
++ dev_dbg(dev, "uevent\n");
++
++ if (!psy) {
++ dev_dbg(dev, "No power supply yet\n");
++ return ret;
++ }
++
++ dev_dbg(dev, "POWER_SUPPLY_NAME=%s\n", psy->name);
++
++ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
++ &length, "POWER_SUPPLY_NAME=%s", psy->name);
++ if (ret)
++ return ret;
++
++ prop_buf = (char *)get_zeroed_page(GFP_KERNEL);
++ if (!prop_buf)
++ 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(envp, num_envp, &i, buffer, buffer_size,
++ &length, "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++) {
++ struct device_attribute *attr;
++ char *line;
++
++ attr = &power_supply_attrs[psy->properties[j]];
++
++ ret = power_supply_show_property(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, "prop %s=%s\n", attrname, prop_buf);
++
++ ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size,
++ &length, "POWER_SUPPLY_%s=%s",
++ attrname, prop_buf);
++ kfree(attrname);
++ if (ret)
++ goto out;
++ }
++
++out:
++ free_page((unsigned long)prop_buf);
++
++ return ret;
++}
+Index: linux-2.6.22/drivers/power/simpad-battery.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/drivers/power/simpad-battery.c 2007-08-23 12:13:52.000000000 +0200
+@@ -0,0 +1,242 @@
++/*
++ * linux/drivers/misc/simpad-battery.c
++ *
++ * Copyright (C) 2005 Holger Hans Peter Freyther
++ * Copyright (C) 2001 Juergen Messerer
++ *
++ * 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.
++ *
++ * Read the Battery Level through the UCB1x00 chip. T-Sinuspad is
++ * unsupported for now.
++ *
++ */
++
++#include <linux/battery.h>
++#include <asm/dma.h>
++#include "ucb1x00.h"
++
++
++/*
++ * Conversion from AD -> mV
++ * 7.5V = 1023 7.3313mV/Digit
++ *
++ * 400 Units == 9.7V
++ * a = ADC value
++ * 21 = ADC error
++ * 12600 = Divident to get 2*7.3242
++ * 860 = Divider to get 2*7.3242
++ * 170 = Voltagedrop over
++ */
++#define CALIBRATE_BATTERY(a) ((((a + 21)*12600)/860) + 170)
++
++/*
++ * We have two types of batteries a small and a large one
++ * To get the right value we to distinguish between those two
++ * 450 Units == 15 V
++ */
++#define CALIBRATE_SUPPLY(a) (((a) * 1500) / 45)
++#define MIN_SUPPLY 12000 /* Less then 12V means no powersupply */
++
++/*
++ * Charging Current
++ * if value is >= 50 then charging is on
++ */
++#define CALIBRATE_CHARGING(a) (((a)* 1000)/(152/4)))
++
++struct simpad_battery_t {
++ struct battery battery;
++ struct ucb1x00* ucb;
++
++ /*
++ * Variables for the values to one time support
++ * T-Sinuspad as well
++ */
++ int min_voltage;
++ int min_current;
++ int min_charge;
++
++ int max_voltage;
++ int max_current;
++ int max_charge;
++
++ int min_supply;
++ int charging_led_label;
++ int charging_max_label;
++ int batt_full;
++ int batt_low;
++ int batt_critical;
++ int batt_empty;
++};
++
++static int simpad_get_min_voltage(struct battery* _battery )
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->min_voltage;
++}
++
++static int simpad_get_min_current(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->min_current;
++}
++
++static int simpad_get_min_charge(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->min_charge;
++}
++
++static int simpad_get_max_voltage(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->max_voltage;
++}
++
++static int simpad_get_max_current(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->max_current;
++}
++
++static int simpad_get_max_charge(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++ return battery->max_charge;
++}
++
++static int simpad_get_temp(struct battery* _battery)
++{
++ return 0;
++}
++
++static int simpad_get_voltage(struct battery* _battery)
++{
++ int val;
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++
++
++ ucb1x00_adc_enable(battery->ucb);
++ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD1, UCB_NOSYNC);
++ ucb1x00_adc_disable(battery->ucb);
++
++ return CALIBRATE_BATTERY(val);
++}
++
++static int simpad_get_current(struct battery* _battery)
++{
++ int val;
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++
++ ucb1x00_adc_enable(battery->ucb);
++ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD3, UCB_NOSYNC);
++ ucb1x00_adc_disable(battery->ucb);
++
++ return val;
++}
++
++static int simpad_get_charge(struct battery* _battery)
++{
++ int val;
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)_battery;
++
++ ucb1x00_adc_enable(battery->ucb);
++ val = ucb1x00_adc_read(battery->ucb, UCB_ADC_INP_AD2, UCB_NOSYNC);
++ ucb1x00_adc_disable(battery->ucb);
++
++ return CALIBRATE_SUPPLY(val);
++
++}
++
++static int simpad_get_status(struct battery* _battery)
++{
++ struct simpad_battery_t* battery = (struct simpad_battery_t*)(_battery);
++ int vcharger = simpad_get_voltage(_battery);
++ int icharger = simpad_get_current(_battery);
++
++ int status = BATTERY_STATUS_UNKNOWN;
++ if(icharger > battery->charging_led_label)
++ status = BATTERY_STATUS_CHARGING;
++ else if(vcharger > battery->min_supply)
++ status = BATTERY_STATUS_NOT_CHARGING;
++ else
++ status = BATTERY_STATUS_DISCHARGING;
++
++ return status;
++}
++
++static struct simpad_battery_t simpad_battery = {
++ .battery = {
++ .get_min_voltage = simpad_get_min_voltage,
++ .get_min_current = simpad_get_min_current,
++ .get_min_charge = simpad_get_min_charge,
++ .get_max_voltage = simpad_get_max_voltage,
++ .get_max_current = simpad_get_max_current,
++ .get_max_charge = simpad_get_max_charge,
++ .get_temp = simpad_get_temp,
++ .get_voltage = simpad_get_voltage,
++ .get_current = simpad_get_current,
++ .get_charge = simpad_get_charge,
++ .get_status = simpad_get_status,
++ },
++ .min_voltage = 0,
++ .min_current = 0,
++ .min_charge = 0,
++ .max_voltage = 0,
++ .max_current = 0,
++ .max_charge = 0,
++
++ .min_supply = 1200,
++ .charging_led_label = 18,
++ .charging_max_label = 265,
++ .batt_full = 8300,
++ .batt_low = 7300,
++ .batt_critical = 6800,
++ .batt_empty = 6500,
++};
++
++
++
++/*
++ * UCB glue code
++ */
++static int ucb1x00_battery_add(struct class_device *dev)
++{
++ struct ucb1x00 *ucb = classdev_to_ucb1x00(dev);
++ simpad_battery.ucb = ucb;
++
++ battery_class_register(&simpad_battery.battery);
++
++ return 0;
++}
++
++static void ucb1x00_battery_remove(struct class_device *dev)
++{
++ return battery_class_unregister(&simpad_battery.battery);
++}
++
++
++static struct ucb1x00_class_interface ucb1x00_battery_interface = {
++ .interface = {
++ .add = ucb1x00_battery_add,
++ .remove = ucb1x00_battery_remove,
++ },
++};
++
++
++static int __init battery_register(void)
++{
++ return ucb1x00_register_interface(&ucb1x00_battery_interface);
++}
++
++static void __exit battery_unregister(void)
++{
++ ucb1x00_unregister_interface(&ucb1x00_battery_interface);
++}
++
++module_init(battery_register);
++module_exit(battery_unregister);
++
++MODULE_AUTHOR("Holger Hans Peter Freyther");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.22/arch/arm/Kconfig
+===================================================================
+--- linux-2.6.22.orig/arch/arm/Kconfig 2007-08-23 12:17:42.000000000 +0200
++++ linux-2.6.22/arch/arm/Kconfig 2007-08-23 12:22:28.000000000 +0200
+@@ -1016,6 +1016,8 @@
+
+ source "drivers/w1/Kconfig"
+
++source "drivers/power/Kconfig"
++
+ source "drivers/hwmon/Kconfig"
+
+ #source "drivers/l3/Kconfig"
+Index: linux-2.6.22/drivers/Kconfig
+===================================================================
+--- linux-2.6.22.orig/drivers/Kconfig 2007-08-23 12:21:27.000000000 +0200
++++ linux-2.6.22/drivers/Kconfig 2007-08-23 12:22:03.000000000 +0200
+@@ -54,6 +54,8 @@
+
+ source "drivers/w1/Kconfig"
+
++source "drivers/power/Kconfig"
++
+ source "drivers/hwmon/Kconfig"
+
+ source "drivers/mfd/Kconfig"
+Index: linux-2.6.22/drivers/Makefile
+===================================================================
+--- linux-2.6.22.orig/drivers/Makefile 2007-08-23 12:33:58.000000000 +0200
++++ linux-2.6.22/drivers/Makefile 2007-08-23 12:34:34.000000000 +0200
+@@ -61,6 +61,7 @@
+ obj-$(CONFIG_RTC_LIB) += rtc/
+ obj-y += i2c/
+ obj-$(CONFIG_W1) += w1/
++obj-$(CONFIG_POWER_SUPPLY) += power/
+ obj-$(CONFIG_HWMON) += hwmon/
+ obj-$(CONFIG_PHONE) += telephony/
+ obj-$(CONFIG_MD) += md/
+Index: linux-2.6.22/include/linux/power_supply.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.22/include/linux/power_supply.h 2007-08-23 12:37:10.000000000 +0200
+@@ -0,0 +1,175 @@
++/*
++ * Universal power supply monitor class
++ *
++ * Copyright (c) 2007 Anton Vorontsov <cbou@mail.ru>
++ * Copyright (c) 2004 Szabolcs Gyurko
++ * Copyright (c) 2003 Ian Molton <spyro@f2s.com>
++ *
++ * Modified: 2004, Oct Szabolcs Gyurko
++ *
++ * You may use this code as per GPL version 2
++ */
++
++#ifndef __LINUX_POWER_SUPPLY_H__
++#define __LINUX_POWER_SUPPLY_H__
++
++#include <linux/device.h>
++#include <linux/workqueue.h>
++#include <linux/leds.h>
++
++/*
++ * All voltages, currents, charges, energies, time and temperatures in uV,
++ * uA, uAh, uWh, seconds and tenths of degree Celsius unless otherwise
++ * stated. It's driver's job to convert its raw values to units in which
++ * this class operates.
++ */
++
++/*
++ * For systems where the charger determines the maximum battery capacity
++ * the min and max fields should be used to present these values to user
++ * space. Unused/unknown fields will not appear in sysfs.
++ */
++
++enum {
++ POWER_SUPPLY_STATUS_UNKNOWN = 0,
++ POWER_SUPPLY_STATUS_CHARGING,
++ POWER_SUPPLY_STATUS_DISCHARGING,
++ POWER_SUPPLY_STATUS_NOT_CHARGING,
++ POWER_SUPPLY_STATUS_FULL,
++};
++
++enum {
++ POWER_SUPPLY_HEALTH_UNKNOWN = 0,
++ POWER_SUPPLY_HEALTH_GOOD,
++ POWER_SUPPLY_HEALTH_OVERHEAT,
++ POWER_SUPPLY_HEALTH_DEAD,
++};
++
++enum {
++ POWER_SUPPLY_TECHNOLOGY_UNKNOWN = 0,
++ POWER_SUPPLY_TECHNOLOGY_NIMH,
++ POWER_SUPPLY_TECHNOLOGY_LION,
++ POWER_SUPPLY_TECHNOLOGY_LIPO,
++};
++
++enum {
++ POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN = 0,
++ POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL,
++ POWER_SUPPLY_CAPACITY_LEVEL_LOW,
++ POWER_SUPPLY_CAPACITY_LEVEL_NORMAL,
++ POWER_SUPPLY_CAPACITY_LEVEL_HIGH,
++ POWER_SUPPLY_CAPACITY_LEVEL_FULL,
++};
++
++enum power_supply_property {
++ /* Properties of type `int' */
++ POWER_SUPPLY_PROP_STATUS = 0,
++ POWER_SUPPLY_PROP_HEALTH,
++ POWER_SUPPLY_PROP_PRESENT,
++ POWER_SUPPLY_PROP_ONLINE,
++ POWER_SUPPLY_PROP_TECHNOLOGY,
++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
++ POWER_SUPPLY_PROP_VOLTAGE_NOW,
++ POWER_SUPPLY_PROP_VOLTAGE_AVG,
++ POWER_SUPPLY_PROP_CURRENT_NOW,
++ POWER_SUPPLY_PROP_CURRENT_AVG,
++ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
++ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
++ POWER_SUPPLY_PROP_CHARGE_FULL,
++ POWER_SUPPLY_PROP_CHARGE_EMPTY,
++ POWER_SUPPLY_PROP_CHARGE_NOW,
++ POWER_SUPPLY_PROP_CHARGE_AVG,
++ POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN,
++ POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN,
++ POWER_SUPPLY_PROP_ENERGY_FULL,
++ POWER_SUPPLY_PROP_ENERGY_EMPTY,
++ POWER_SUPPLY_PROP_ENERGY_NOW,
++ POWER_SUPPLY_PROP_ENERGY_AVG,
++ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */
++ POWER_SUPPLY_PROP_CAPACITY_LEVEL,
++ POWER_SUPPLY_PROP_TEMP,
++ POWER_SUPPLY_PROP_TEMP_AMBIENT,
++ POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW,
++ POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
++ POWER_SUPPLY_PROP_TIME_TO_FULL_NOW,
++ POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
++ /* Properties of type `const char *' */
++ POWER_SUPPLY_PROP_MODEL_NAME,
++};
++
++enum power_supply_type {
++ POWER_SUPPLY_TYPE_BATTERY = 0,
++ POWER_SUPPLY_TYPE_UPS,
++ POWER_SUPPLY_TYPE_MAINS,
++ POWER_SUPPLY_TYPE_USB,
++};
++
++union power_supply_propval {
++ int intval;
++ const char *strval;
++};
++
++struct power_supply {
++ const char *name;
++ enum power_supply_type type;
++ enum power_supply_property *properties;
++ size_t num_properties;
++
++ char **supplied_to;
++ size_t num_supplicants;
++
++ int (*get_property)(struct power_supply *psy,
++ enum power_supply_property psp,
++ union power_supply_propval *val);
++ void (*external_power_changed)(struct power_supply *psy);
++
++ /* For APM emulation, think legacy userspace. */
++ int use_for_apm;
++
++ /* private */
++ struct device *dev;
++ struct work_struct changed_work;
++
++#ifdef CONFIG_LEDS_TRIGGERS
++ struct led_trigger *charging_full_trig;
++ char *charging_full_trig_name;
++ struct led_trigger *charging_trig;
++ char *charging_trig_name;
++ struct led_trigger *full_trig;
++ char *full_trig_name;
++ struct led_trigger *online_trig;
++ char *online_trig_name;
++#endif
++};
++
++/*
++ * This is recommended structure to specify static power supply parameters.
++ * Generic one, parametrizable for different power supplies. Power supply
++ * class itself does not use it, but that's what implementing most platform
++ * drivers, should try reuse for consistency.
++ */
++
++struct power_supply_info {
++ const char *name;
++ int technology;
++ int voltage_max_design;
++ int voltage_min_design;
++ int charge_full_design;
++ int charge_empty_design;
++ int energy_full_design;
++ int energy_empty_design;
++ int use_for_apm;
++};
++
++extern void power_supply_changed(struct power_supply *psy);
++extern int power_supply_am_i_supplied(struct power_supply *psy);
++
++extern int power_supply_register(struct device *parent,
++ struct power_supply *psy);
++extern void power_supply_unregister(struct power_supply *psy);
++
++/* For APM emulation, think legacy userspace. */
++extern struct class *power_supply_class;
++
++#endif /* __LINUX_POWER_SUPPLY_H__ */