aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-sa1100/jornada720_apm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-sa1100/jornada720_apm.c')
-rw-r--r--arch/arm/mach-sa1100/jornada720_apm.c166
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);