From 4bad04eed1999b08090a395cc67da33b54bb38c6 Mon Sep 17 00:00:00 2001 From: Mike Westerhof Date: Thu, 13 Nov 2008 00:32:28 -0600 Subject: [PATCH] gta01-battery-driver.patch Adds a simple pass-through battery driver module for the GTA01. This will simplify user-space by providing the same sysfs API on both GTA01 and GTA02, and is a first step towards eliminating the need for APM emulation. Signed-off-by: Mike Westerhof --- defconfig-mw-gta01 | 1 + defconfig-mw-gta02 | 1 + drivers/i2c/chips/pcf50606.c | 96 ++++++++++++++++++++++++++++++++++++++++++ drivers/power/Kconfig | 6 +++ drivers/power/Makefile | 1 + drivers/power/gta01_battery.c | 97 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 202 insertions(+) create mode 100644 drivers/power/gta01_battery.c diff --git a/defconfig-mw-gta01 b/defconfig-mw-gta01 index 12d307301b9..bf8829b1e72 100644 --- a/defconfig-mw-gta01 +++ b/defconfig-mw-gta01 @@ -1021,6 +1021,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_PDA_POWER is not set # CONFIG_APM_POWER is not set # CONFIG_BATTERY_DS2760 is not set +CONFIG_BATTERY_GTA01=y CONFIG_BATTERY_BQ27000_HDQ=y CONFIG_GTA02_HDQ=y # CONFIG_HWMON is not set diff --git a/defconfig-mw-gta02 b/defconfig-mw-gta02 index ad841ed226a..9d10c117060 100644 --- a/defconfig-mw-gta02 +++ b/defconfig-mw-gta02 @@ -1021,6 +1021,7 @@ CONFIG_POWER_SUPPLY=y # CONFIG_PDA_POWER is not set CONFIG_APM_POWER=y # CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_GTA01 is not set CONFIG_BATTERY_BQ27000_HDQ=y CONFIG_GTA02_HDQ=y # CONFIG_HWMON is not set diff --git a/drivers/i2c/chips/pcf50606.c b/drivers/i2c/chips/pcf50606.c index 706ce6ddee4..f5850134814 100644 --- a/drivers/i2c/chips/pcf50606.c +++ b/drivers/i2c/chips/pcf50606.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include @@ -141,6 +142,12 @@ struct pcf50606_data { static struct i2c_driver pcf50606_driver; +/* This global is set by the pcf50606 driver to the correct callback + * for the gta01 battery driver. */ +int (*pmu_bat_get_property)(struct power_supply *, enum power_supply_property, + union power_supply_propval *); +EXPORT_SYMBOL(pmu_bat_get_property); + /* This is an ugly construct on how to access the (currently single/global) * pcf50606 handle from other code in the kernel. I didn't really come up with * a more decent method of dynamically resolving this */ @@ -1270,6 +1277,92 @@ static void pcf50606_get_power_status(struct apm_power_info *info) } /*********************************************************************** + * Battery driver interface + ***********************************************************************/ +static int pcf50606_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + u_int16_t adc, adc_adcin1; + u_int8_t mbcc1, chgmod; + struct pcf50606_data *pcf = pcf50606_global; + int ret = 0; + + switch (psp) { + + case POWER_SUPPLY_PROP_STATUS: + if (!(reg_read(pcf, PCF50606_REG_OOCS) & PCF50606_OOCS_EXTON)) { + /* No charger, clearly we're discharging then */ + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + } else { + + /* We have a charger present, get charge mode */ + mbcc1 = reg_read(pcf, PCF50606_REG_MBCC1); + chgmod = (mbcc1 & PCF50606_MBCC1_CHGMOD_MASK); + switch (chgmod) { + + /* TODO: How to determine POWER_SUPPLY_STATUS_FULL? */ + + case PCF50606_MBCC1_CHGMOD_QUAL: + case PCF50606_MBCC1_CHGMOD_PRE: + case PCF50606_MBCC1_CHGMOD_IDLE: + val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; + break; + + case PCF50606_MBCC1_CHGMOD_TRICKLE: + case PCF50606_MBCC1_CHGMOD_FAST_CCCV: + case PCF50606_MBCC1_CHGMOD_FAST_NOCC: + case PCF50606_MBCC1_CHGMOD_FAST_NOCV: + case PCF50606_MBCC1_CHGMOD_FAST_SW: + val->intval = POWER_SUPPLY_STATUS_CHARGING; + break; + + default: + val->intval = POWER_SUPPLY_STATUS_UNKNOWN; + break; + + } + } + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; /* Must be, or the magic smoke comes out */ + break; + + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !!(reg_read(pcf, PCF50606_REG_OOCS) & + PCF50606_OOCS_EXTON); + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + adc = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_RES, NULL); + /* (adc * 6000000) / 1024 == (adc * 46875) / 8 */ + val->intval = (adc * 46875) / 8; + break; + + case POWER_SUPPLY_PROP_CURRENT_NOW: + adc = adc_read(pcf, PCF50606_ADCMUX_BATVOLT_ADCIN1, + &adc_adcin1); + val->intval = adc_to_chg_milliamps(pcf, adc_adcin1, adc) * 1000; + break; + + case POWER_SUPPLY_PROP_TEMP: + adc = adc_read(pcf, PCF50606_ADCMUX_BATTEMP, NULL); + val->intval = rntc_to_temp(adc_to_rntc(pcf, adc)) * 10; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = battvolt_scale(pcf50606_battvolt(pcf)); + break; + + default: + ret = -EINVAL; + break; + } + + return ret; +} + +/*********************************************************************** * RTC ***********************************************************************/ @@ -1900,6 +1993,7 @@ static int pcf50606_detect(struct i2c_adapter *adapter, int address, int kind) } apm_get_power_status = pcf50606_get_power_status; + pmu_bat_get_property = pcf50606_bat_get_property; #ifdef CONFIG_MACH_NEO1973_GTA01 if (machine_is_neo1973_gta01()) { @@ -1962,6 +2056,8 @@ static int pcf50606_detach_client(struct i2c_client *client) struct pcf50606_data *pcf = i2c_get_clientdata(client); apm_get_power_status = NULL; + pmu_bat_get_property = NULL; + input_unregister_device(pcf->input_dev); if (pcf->pdata->used_features & PCF50606_FEAT_PWM_BL) diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 8c50ecb0cf8..470e08c0853 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -62,5 +62,11 @@ config GTA02_HDQ on the Neo Freerunner. You probably want to select at least BATTERY_BQ27000_HDQ as well +config BATTERY_GTA01 + tristate "Neo GTA01 battery" + depends on MACH_NEO1973_GTA01 + help + Say Y to enable support for the battery on the Neo GTA01 + endif # POWER_SUPPLY diff --git a/drivers/power/Makefile b/drivers/power/Makefile index d7e87ad0dcb..2013e89cc45 100644 --- a/drivers/power/Makefile +++ b/drivers/power/Makefile @@ -21,5 +21,6 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o obj-$(CONFIG_BATTERY_BQ27000_HDQ) += bq27000_battery.o +obj-$(CONFIG_BATTERY_GTA01) += gta01_battery.o obj-$(CONFIG_GTA02_HDQ) += gta02_hdq.o diff --git a/drivers/power/gta01_battery.c b/drivers/power/gta01_battery.c new file mode 100644 index 00000000000..873f8fd4520 --- /dev/null +++ b/drivers/power/gta01_battery.c @@ -0,0 +1,97 @@ +/* + * Battery driver for the Openmoko GTA01 device, using the pcf50606 chip. + * + * This is nothing more than a write-thru interface to the real logic, + * which is part of the pcf50606.c multifunction chip driver. + * Copyright © 2008 Mike Westerhof + * + * + * Portions liberally borrowed from olpc_battery.c, copyright below: + * Copyright © 2006 David Woodhouse + * + * 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 +#include +#include +#include +#include +#include + +/********************************************************************* + * This global is set by the pcf50606 driver to the correct callback + *********************************************************************/ + +extern int (*pmu_bat_get_property)(struct power_supply *, + enum power_supply_property, + union power_supply_propval *); + + +/********************************************************************* + * Battery properties + *********************************************************************/ +static int gta01_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + if (pmu_bat_get_property) + return (pmu_bat_get_property)(psy, psp, val); + else + return -ENODEV; +} + +static enum power_supply_property gta01_bat_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_TEMP, + POWER_SUPPLY_PROP_CAPACITY, +}; + +/********************************************************************* + * Initialisation + *********************************************************************/ + +static struct platform_device *bat_pdev; + +static struct power_supply gta01_bat = { + .properties = gta01_bat_props, + .num_properties = ARRAY_SIZE(gta01_bat_props), + .get_property = gta01_bat_get_property, + .use_for_apm = 0, /* pcf50606 driver has its own apm driver */ +}; + +static int __init gta01_bat_init(void) +{ + int ret; + + bat_pdev = platform_device_register_simple("gta01-battery", 0, NULL, 0); + if (IS_ERR(bat_pdev)) + return PTR_ERR(bat_pdev); + + gta01_bat.name = bat_pdev->name; + + ret = power_supply_register(&bat_pdev->dev, >a01_bat); + if (ret) + platform_device_unregister(bat_pdev); + + return ret; +} + +static void __exit gta01_bat_exit(void) +{ + power_supply_unregister(>a01_bat); + platform_device_unregister(bat_pdev); +} + +module_init(gta01_bat_init); +module_exit(gta01_bat_exit); + +MODULE_AUTHOR("Mike Westerhof "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Battery driver for GTA01"); -- 2.11.4.GIT