diff options
Diffstat (limited to 'arch/arm/mach-sa1100/jornada720_apm.c')
-rw-r--r-- | arch/arm/mach-sa1100/jornada720_apm.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/arch/arm/mach-sa1100/jornada720_apm.c b/arch/arm/mach-sa1100/jornada720_apm.c new file mode 100644 index 00000000000000..c6ed8d07a95ce5 --- /dev/null +++ b/arch/arm/mach-sa1100/jornada720_apm.c @@ -0,0 +1,166 @@ +/** + * + * Copryright (C) 2007,2008 Kristoffer Ericson <Kristoffer.Ericson@gmail.com> + * Copyright (C) 2006 Filip Zyzniewski <filip.zyzniewski@tefnet.pl> + * + * HP Jornada 710/720/728 Battery detection (APM) driver + * + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/apm-emulation.h> + +#include <asm/hardware.h> +#include <asm/arch/jornada720.h> + +MODULE_AUTHOR("Filip Zyzniewski <filip.zyzniewski@tefnet.pl>, Kristoffer Ericson <kristoffer.ericson@gmail.com>"); +MODULE_DESCRIPTION("HP Jornada 710/720/728 battery status reporting"); +MODULE_LICENSE("GPL"); + +/** + * voltage && detection + */ +#define max_voltage 670 /* max voltage without ac power */ +#define min_voltage 430 /* min voltage */ +#define ac_connected() ((GPLR & GPIO_GPIO4) ? 0 : 1) /* is ac connected? */ +#define ac_charging() (! (GPLR & GPIO_GPIO26) ) /* are we charging? */ + +/** + * battery calculations + */ +#define main_diff (max_voltage - min_voltage) /* just to keep defines small */ +#define main_ac_coeff 100 / 105 /* correcting battery values when with ac */ +#define main_divisor (main_diff * main_diff) / 100 /* battery power to percent */ +#define main_lin_corr (main_diff * main_diff) / 2 /* adjusting for non-linearity */ + +int jornada720_apm_get_battery_raw(int battnum) +{ + unsigned char low_byte, high_byte, msb; + + jornada_ssp_start(); + + /* if ssp connection fails we bail out */ + if(jornada_ssp_inout(GETBATTERYDATA) < 0) { + printk(KERN_WARNING "APM: Failed trying to aquire battery data \n"); + jornada_ssp_end(); + return -1; + } + + low_byte = jornada_ssp_inout(TXDUMMY); /* backup battery value */ + high_byte = jornada_ssp_inout(TXDUMMY); /* main battery value */ + msb = jornada_ssp_inout(TXDUMMY); /* status */ + + jornada_ssp_end(); + + /* main battery */ + if (battnum) { + if ((msb & 0x03) == 0x03) return -1; /* main battery absent */ + return ((msb & 0x03) << 8) + low_byte; /* wrapping values */ + } + /* backup battery */ + else { + if ((msb & 0x0c) == 0x00) return -1; /* backup battery abset */ + return ((msb & 0x0c) << 6) + high_byte; /* wrapping values */ + } +} + +int jornada720_apm_get_battery(int battnum) +{ + int ret = jornada720_apm_get_battery_raw(battnum); + + if (ret == -1) + return ret; + + /* main battery only, cannot calculate backup battery */ + if (battnum) { + ret -= min_voltage; /* we want 0 for fully drained battery */ + + /* trying to get values more linear */ + ret *= ret; + if(ret > 37000) ret = ret * 3/2 - main_lin_corr; + else ret = ret * 7/10; + + ret /= main_divisor; /* 0-100% range */ + + if (ac_connected()) /* adjusting for ac fluctuations */ + ret = ret * main_ac_coeff; + + if (ret > 100) ret=100; /* should never report above 100% */ + } + return ret; +} + +static void jornada720_apm_get_power_status(struct apm_power_info *info) +{ + + info->battery_life=jornada720_apm_get_battery(1); /* main battery */ + + if (info->battery_life==-1) { + info->battery_status = APM_BATTERY_STATUS_NOT_PRESENT; + info->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT; + + } else if (info->battery_life < 30) { + info->battery_status = APM_BATTERY_STATUS_LOW; + info->battery_flag = APM_BATTERY_FLAG_LOW; + + } else if (info->battery_life < 5) { + info->battery_status = APM_BATTERY_STATUS_CRITICAL; + info->battery_flag = APM_BATTERY_FLAG_CRITICAL; + + } else { + info->battery_status = APM_BATTERY_STATUS_HIGH; + info->battery_flag = APM_BATTERY_FLAG_HIGH; + } + + if (ac_charging()) + info->battery_status = APM_BATTERY_STATUS_CHARGING; + + info->ac_line_status = ac_connected(); +} + +static int jornada720_apm_probe(struct device *dev) { + + /* we provide a function to check battery levels etc */ + apm_get_power_status=jornada720_apm_get_power_status; + + return 0; +} + +static int jornada720_apm_remove(struct device *dev) { + if(apm_get_power_status==jornada720_apm_get_power_status) + apm_get_power_status=NULL; + return 0; +} + +static struct device_driver jornada720_apm_driver = { + .name = "jornada_apm_driver", + .probe = jornada720_apm_probe, + .remove = jornada720_apm_remove, + .owner = THIS_MODULE, +}; + +static int __init jornada720_apm_init(void) { + int ret, backup_level; + + ret = driver_register(&jornada720_apm_driver); + if(ret) return ret; + + backup_level = jornada720_apm_get_battery(0); /* backup battery */ + + if(backup_level != -1) + printk(KERN_INFO "jornada720_apm: backup battery level: %i\n", backup_level); + else + printk(KERN_INFO "jornada720_apm: backup battery not present\n"); + + return ret; +} + +static void __exit jornada720_apm_exit(void) { + driver_unregister(&jornada720_apm_driver); +} + +module_init(jornada720_apm_init); +module_exit(jornada720_apm_exit); |