All pastes #297119 Raw Edit

Mine

public text v1 · immutable
#297119 ·published 2006-12-30 21:18 UTC
rendered paste body
Index: blueangel_battery.c
===================================================================
RCS file: /cvs/linux/kernel26/arch/arm/mach-pxa/htcblueangel/blueangel_battery.c,v
retrieving revision 1.5
diff -u -r1.5 blueangel_battery.c
--- blueangel_battery.c	22 Dec 2006 03:57:06 -0000	1.5
+++ blueangel_battery.c	30 Dec 2006 21:16:56 -0000
@@ -1,6 +1,6 @@
 /*
 * Driver interface to the battery on the HTC Blueangel
-*
+* from H2000
 * Use consistent with the GNU GPL is permitted,
 * provided that this copyright notice is
 * preserved in its entirety in all copies and derived works.
@@ -12,6 +12,8 @@
 #include <linux/version.h>
 #include <linux/battery.h>
 #include <linux/leds.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
 #include <linux/soc/asic3_base.h>
 #include <linux/platform_device.h>
 
@@ -23,15 +25,54 @@
 
 #include <linux/soc/tsc2200.h>
 
+#include "../../../../drivers/w1/w1.h"
+#include "../../../../drivers/w1/slaves/w1_ds2760.h"
+
 #ifdef DEBUG
 #define dprintk(x...) printk(x)
 #else
 #define dprintk(x...)
 #endif
 
+#define BATTERY_CHECK_INTERVAL  (HZ * 60)       /* every 60 seconds */
+
+#define BLUEANGEL_BATTERY_MAX_VOLTAGE 4400  /* My measurements */
+#define BLUEANGEL_BATTERY_MIN_VOLTAGE    0  /* Maybe incorrect */
+#define BLUEANGEL_BATTERY_MAX_CURRENT  950  /* MUST BE MEASURED */
+#define BLUEANGEL_BATTERY_MIN_CURRENT -950  /* Maybe incorrect */
+#define BLUEANGEL_BATTERY_MIN_CHARGE     0  /* Maybe incorrect */
+
+/* DS2760 device */
+struct device *ds2760_dev = NULL;
+
+/* Cache the battery status for this many seconds. */
+#define STATUS_CACHE_TIME (HZ * 1)
+static struct ds2760_status battery_status;
+
+
 extern struct platform_device blueangel_tsc2200;
+extern struct platform_device blueangel_asic3;
+
 DEFINE_LED_TRIGGER(ledtrig_cradle);
+DEFINE_LED_TRIGGER(ledtrig_phone);
 
+/* w1_samcop device */
+static struct workqueue_struct *probe_q;
+static struct work_struct       probe_work;
+
+/* battery monitor */
+static struct timer_list        monitor_timer;
+static struct workqueue_struct *monitor_q;
+static struct work_struct       monitor_work;
+
+static int battery_charge_state;
+#define BATTERY_STATE_UNKNOWN   0
+#define BATTERY_DISCHARGING     1
+#define BATTERY_CHARGING        2
+#define BATTERY_FULL            3
+
+/* AC present? */
+static int power_status;
 
 
 static void bat_tsc2200_start_conv_scan(void)
@@ -47,18 +88,6 @@
 		TSC2200_CTRLREG_ADC_PV  (TSC2200_CTRLREG_ADC_PV_500uS) );
 }
 
-static void bat_tsc2200_start_conv_temp(void)
-{
-	tsc2200_write(&blueangel_tsc2200.dev, TSC2200_CTRLREG_REF, 31);
-	tsc2200_write(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC,
-		TSC2200_CTRLREG_ADC_AD3 |
-		TSC2200_CTRLREG_ADC_AD1 |
-		TSC2200_CTRLREG_ADC_RES (TSC2200_CTRLREG_ADC_RES_12BITP) |
-		TSC2200_CTRLREG_ADC_AVG (TSC2200_CTRLREG_ADC_8AVG) |
-		TSC2200_CTRLREG_ADC_CL  (TSC2200_CTRLREG_ADC_CL_2MHZ_12BIT) |
-		TSC2200_CTRLREG_ADC_PV  (TSC2200_CTRLREG_ADC_PV_500uS) );
-}
-
 static void bat_tsc2200_stop_conv(void)
 {
 	tsc2200_write(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC,
@@ -70,6 +99,60 @@
 		TSC2200_CTRLREG_ADC_PV  (TSC2200_CTRLREG_ADC_PV_500uS) );
 }
 
+int blueangel_battery_read_status(void)
+{
+        if (jiffies < (battery_status.update_time + STATUS_CACHE_TIME))
+                return 0;
+
+        if (!ds2760_dev)
+                return 0;
+
+        if (!w1_ds2760_status(ds2760_dev, &battery_status)) {
+                printk("call to w1_ds2760_status failed (0x%08x)\n",
+                       (unsigned int)ds2760_dev);
+                return 1;
+        }
+
+        return 0;
+}
+
+static int
+blueangel_battery_match_callback(struct device *dev, void *data)
+{
+        struct w1_slave *sl;
+
+        if (!(dev->driver && dev->driver->name &&
+             (strcmp(dev->driver->name, "w1_slave_driver") == 0)))
+                return 0;
+
+        sl = container_of(dev, struct w1_slave, dev);
+
+        /* DS2760 w1 slave device names begin with the family number 0x30. */
+        if (strncmp(sl->name, "30-", 3) != 0)
+                return 0;
+
+        return 1;
+}
+
+void
+blueangel_battery_probe_work(void *data)
+{
+        struct bus_type *bus;
+
+        /* Get the battery w1 slave device. */
+        bus = find_bus("w1");
+        if (bus)
+                ds2760_dev = bus_find_device(bus, NULL, NULL,
+                                             blueangel_battery_match_callback);
+
+        if (!ds2760_dev) {
+                /* No DS2760 device found; try again later. */
+                //queue_delayed_work(probe_q, &probe_work, HZ * 5);
+                return;
+        }
+}
+
+
 static int blueangel_battery_get_status(struct battery *bat)
 {
 	dprintk("%s: in.\n", __FUNCTION__);
@@ -108,116 +191,176 @@
 static int blueangel_battery_get_voltage(struct battery *bat)
 {
 	int retval;
-	
-	dprintk("%s: in.\n", __FUNCTION__);
-
-	//down_interruptible(&tsc2200_sem);
-	//tsc2200_lock(&blueangel_tsc2200.dev);
-	bat_tsc2200_start_conv_scan();
-
-	while ( !(tsc2200_read(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC) & 0x4000) &&
-			 !tsc2200_dav(&blueangel_tsc2200.dev )) {
-		dprintk("B2: %X\n", tsc2200_read(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC));
-		mdelay(1);
-	}
-	
-	retval = tsc2200_read(&blueangel_tsc2200.dev, TSC2200_DATAREG_AUX2);
-	
-	bat_tsc2200_stop_conv();
-	//tsc2200_unlock(&blueangel_tsc2200.dev);
-	//up(&tsc2200_sem);
+	blueangel_battery_read_status();
+	retval = battery_status.voltage_mV;	
 	return retval;
 }
 
 static int blueangel_battery_get_temp(struct battery *bat)
 {
 	int retval;
+	blueangel_battery_read_status();
+	retval = battery_status.temp_C;	
 	
-	dprintk("%s: in.\n", __FUNCTION__);
+	return retval;
+}
+blueangel_battery_get_max_voltage(struct battery *bat)
+{
+        return BLUEANGEL_BATTERY_MAX_VOLTAGE;
+}
 
-	//down_interruptible(&tsc2200_sem);
-	//tsc2200_lock(&blueangel_tsc2200.dev);
-	bat_tsc2200_start_conv_temp();
+int
+blueangel_battery_get_min_voltage(struct battery *bat)
+{
+        return BLUEANGEL_BATTERY_MIN_VOLTAGE;
+}
 
-	while ( !(tsc2200_read(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC) & 0x4000) &&
-			 !tsc2200_dav(&blueangel_tsc2200.dev)) {
-		dprintk("1: %X\n", tsc2200_read(&blueangel_tsc2200.dev, TSC2200_CTRLREG_ADC)); 
-		mdelay(1);
-	}
-	
-	retval = tsc2200_read(&blueangel_tsc2200.dev, TSC2200_DATAREG_TEMP1);
-	dprintk("retval=0x%x\n", retval);
-	
-	bat_tsc2200_stop_conv();
-	//tsc2200_unlock(&blueangel_tsc2200.dev);
-	//up(&tsc2200_sem);
-	dprintk("%s: out\n", __FUNCTION__);
-	return retval;
+int
+blueangel_battery_get_max_current(struct battery *bat)
+{
+        return BLUEANGEL_BATTERY_MAX_CURRENT;
+}
+
+int
+blueangel_battery_get_min_current(struct battery *bat)
+{
+        return BLUEANGEL_BATTERY_MIN_CURRENT;
+}
+
+int
+blueangel_battery_get_current(struct battery *bat)
+{
+        blueangel_battery_read_status();
+        return battery_status.current_mA;
+}
+int
+blueangel_battery_get_max_charge(struct battery *bat)
+{
+        blueangel_battery_read_status();
+        return battery_status.rated_capacity;
 }
 
+int
+blueangel_battery_get_min_charge(struct battery *bat)
+{
+        return BLUEANGEL_BATTERY_MIN_CHARGE;
+}
+
+int
+blueangel_battery_get_charge(struct battery *bat)
+{
+        blueangel_battery_read_status();
+        return battery_status.accum_current_mAh;
+}
+
+
 struct battery battery_dev = {
-	.name           = "Main battery",
+	.name           = "main",
 	.id		= "battery0",
 	.get_voltage	= blueangel_battery_get_voltage,
-	.get_status	= blueangel_battery_get_status,
-	//.get_temp	= blueangel_battery_get_temp,
+        .get_min_voltage = blueangel_battery_get_min_voltage,
+        .get_max_voltage = blueangel_battery_get_max_voltage,
+        .get_current     = blueangel_battery_get_current,
+        .get_min_current = blueangel_battery_get_min_current,
+        .get_max_current = blueangel_battery_get_max_current,
+        .get_charge      = blueangel_battery_get_charge,
+        .get_min_charge  = blueangel_battery_get_min_charge,
+        .get_max_charge  = blueangel_battery_get_max_charge,
+        .get_temp        = blueangel_battery_get_temp,
+        .get_status      = blueangel_battery_get_status,
+
 };
 
 struct battery battery_dev1 = {
-	.name           = "Backup battery",
+	.name           = "backup",
 	.id		= "battery1",
 	.get_voltage	= blueangel_battery_get_voltage1,
 	.get_status	= blueangel_battery_get_status,
-	//.get_temp	= blueangel_battery_get_tempi1,
 };
 
+static irqreturn_t
+blueangel_ac_plug_isr (int isr, void *data)
+{
+
+        /* Update the LED status right away, for timely feedback. */
+        /* Set the proper charging rate. */
+	if (!(asic3_get_gpio_status_d(&blueangel_asic3.dev) & GPIOD_AC_CHARGER_N)) {
+        	power_status = APM_AC_ONLINE;
+	}
+	if (!(GPLR(GPIO_NR_BLUEANGEL_USB_DETECT_N) & GPIO_bit(GPIO_NR_BLUEANGEL_USB_DETECT_N))) {
+        	power_status = APM_AC_ONLINE;
+	}
+        if (power_status) {
+                /* Start off with a solid LED; if we're charging,
+                   the LED state will be updated when the queued work runs. */
+		#ifdef CONFIG_LEDS_TRIGGERS
+			led_trigger_event(ledtrig_cradle, LED_FULL);
+		#endif
+        } else {
+		#ifdef CONFIG_LEDS_TRIGGERS
+			led_trigger_event(ledtrig_cradle, LED_OFF);
+		#endif
+        }
+
+        /* Force the data to be re-read. */
+        battery_status.update_time = 0;
+
+        /* Wait a few seconds to let the DS2760 update its current reading. */
+        queue_delayed_work(monitor_q, &monitor_work, HZ * 7);
+
+        return IRQ_HANDLED;
+}
+
+
 static void
 blueangel_battery_get_power_status (struct apm_power_info *info)
 {
-	int val;
-	int val1;
-	int full=0x8ea;
-	int empty=0x8a3;
-
-	int full1=0xb0f;
-	int empty1=0x8a3;
-	val=blueangel_battery_get_voltage(NULL);
-	val1=blueangel_battery_get_voltage1(NULL);
-
-	info->battery_life = (val1-empty)*100/(full-empty);
-	if (info->battery_life < 0 || info->battery_life > 100) 
-		printk("battery voltage (0x%x) not within [0x%x,0x%x]. Please report.\n", val, full, empty);
-	if (info->battery_life < 0) 
-		info->battery_life = 0;
-	if (info->battery_life > 100)
-		info->battery_life = 100;
+        blueangel_battery_read_status();
+
         info->ac_line_status = APM_AC_OFFLINE;
-	if (!(asic3_get_gpio_status_d(&blueangel_tsc2200.dev) & GPIOD_AC_CHARGER_N)) {
+	if (!(asic3_get_gpio_status_d(&blueangel_asic3.dev) & GPIOD_AC_CHARGER_N)) {
         	info->ac_line_status = APM_AC_ONLINE;
 	}
 	if (!(GPLR(GPIO_NR_BLUEANGEL_USB_DETECT_N) & GPIO_bit(GPIO_NR_BLUEANGEL_USB_DETECT_N))) {
         	info->ac_line_status = APM_AC_ONLINE;
 	}
-	info->units=APM_UNITS_MINS;
-	info->time = 360 * info->battery_life/100;	/* time remaining */
-	info->battery_flag = 0;
-	if (info->ac_line_status == APM_AC_OFFLINE) {
+	power_status = info->ac_line_status;
+
+        if (info->ac_line_status == APM_AC_ONLINE && current > 0) {
 		#ifdef CONFIG_LEDS_TRIGGERS
-			led_trigger_event(ledtrig_cradle, LED_OFF);
+			led_trigger_event(ledtrig_cradle, LED_FULL);
 		#endif
-		info->battery_status = APM_BATTERY_STATUS_CRITICAL;
-		if(info->battery_life > 5) {
-			info->battery_status = APM_BATTERY_STATUS_LOW;
-		}
-		if(info->battery_life > 20) {
-			info->battery_status = APM_BATTERY_STATUS_HIGH;
-		}
-	} else {
-		info->battery_status = APM_BATTERY_STATUS_CHARGING;
+                info->battery_status = APM_BATTERY_STATUS_CHARGING;
+        } else {
 		#ifdef CONFIG_LEDS_TRIGGERS
-			led_trigger_event(ledtrig_cradle, LED_FULL);
+			led_trigger_event(ledtrig_cradle, LED_OFF);
 		#endif
-	}
+
+                info->battery_status = battery_status.accum_current_mAh >
+                    ((battery_status.rated_capacity * 75) / 100) ?
+                        APM_BATTERY_STATUS_HIGH :
+                    battery_status.accum_current_mAh >
+                        ((battery_status.rated_capacity* 25) / 100) ?
+                        APM_BATTERY_STATUS_LOW : APM_BATTERY_STATUS_CRITICAL;
+        }
+
+        info->battery_flag = info->battery_status;
+
+        if (battery_status.rated_capacity)
+                info->battery_life = (battery_status.accum_current_mAh * 100) /
+                                     battery_status.rated_capacity;
+
+        if (info->battery_life > 100)
+                info->battery_life = 100;
+        if (info->battery_life < 0)
+                info->battery_life = 0;
+
+        if (battery_status.current_mA) {
+                info->time = - ((battery_status.accum_current_mAh * 6000) /
+                                battery_status.current_mA) / 100;
+                info->units = APM_UNITS_MINS;
+        } else
+                info->units = APM_UNITS_UNKNOWN;
 }
 
 static int bat_setup(void)
@@ -227,17 +370,131 @@
 	return 0;
 }
 
-static int __init bat_init(void)
+static void
+blueangel_battery_timer(unsigned long data)
 {
-	bat_setup();
-	blueangel_battery_get_voltage(NULL);
-	blueangel_battery_get_voltage1(NULL);
-	blueangel_battery_get_temp(NULL);
-	apm_get_power_status = blueangel_battery_get_power_status;
+        queue_work(monitor_q, &monitor_work);
+        mod_timer(&monitor_timer, jiffies + BATTERY_CHECK_INTERVAL);
+}
+
+void
+blueangel_battery_update_status(void *data)
+{
+        static int full_counter;
+
+        blueangel_battery_read_status();
+
+        if (battery_charge_state == BATTERY_STATE_UNKNOWN)
+                full_counter = 0;
+
+        /* Set the proper charging rate. */
+	if (!(asic3_get_gpio_status_d(&blueangel_asic3.dev) & GPIOD_AC_CHARGER_N)) {
+        	power_status = APM_AC_ONLINE;
+	}
+	if (!(GPLR(GPIO_NR_BLUEANGEL_USB_DETECT_N) & GPIO_bit(GPIO_NR_BLUEANGEL_USB_DETECT_N))) {
+        	power_status = APM_AC_ONLINE;
+	}
+        if (power_status) {
+
+                asic3_set_gpio_out_c(&blueangel_asic3.dev,1 << GPIOC_CHARGER_MODE, 1 << GPIOC_CHARGER_MODE);
+
+                if (battery_status.current_mA > 10) {
+
+                        battery_charge_state = BATTERY_CHARGING;
+                        full_counter = 0;
+
+                } else if (battery_status.current_mA < 10 &&
+                           battery_charge_state != BATTERY_FULL) {
+
+                        /* Don't consider the battery to be full unless
+                         * we've seen the current < 10 mA at least two
+                         * consecutive times. */
+
+                        full_counter++;
+
+                        if (full_counter < 2)
+                                battery_charge_state = BATTERY_CHARGING;
+                        else  {
+
+                                unsigned char acr[2];
+
+                                acr[0] = (battery_status.rated_capacity * 4) >> 8;
+                                acr[1] = (battery_status.rated_capacity * 4) & 0xff;
+
+                                if (w1_ds2760_write(ds2760_dev, acr,
+                                    DS2760_CURRENT_ACCUM_MSB, 2) < 2)
+                                        printk(KERN_ERR "ACR reset failed\n");
+                                battery_charge_state = BATTERY_FULL;
+				#ifdef CONFIG_LEDS_TRIGGERS
+					led_trigger_event(ledtrig_phone, LED_FULL);
+				#endif
+                        }
+                }
+
+                if (battery_charge_state == BATTERY_CHARGING) {
+			#ifdef CONFIG_LEDS_TRIGGERS
+				led_trigger_event(ledtrig_cradle, LED_FULL);
+			#endif
+                } else {
+
+                        /* Set the LED solid while on AC and not charging. */
+			#ifdef CONFIG_LEDS_TRIGGERS
+				led_trigger_event(ledtrig_phone,  LED_FULL);
+				led_trigger_event(ledtrig_cradle, LED_OFF);
+			#endif
+                }
+
+        } else {
+
+                battery_charge_state = BATTERY_DISCHARGING;
+                full_counter = 0;
+                asic3_set_gpio_out_c(&blueangel_asic3.dev,1 << GPIOC_CHARGER_MODE, 0);
+
+                /* Turn off the LED while unplugged from AC. */
 		#ifdef CONFIG_LEDS_TRIGGERS
-	led_trigger_register_simple("charge", &ledtrig_cradle);
+			led_trigger_event(ledtrig_cradle, LED_OFF);
 		#endif
+        }
+
+}
+
+
+
+static int __init bat_init(void)
+{
+	bat_setup();
+
+        battery_charge_state = BATTERY_STATE_UNKNOWN;
+
+        /* Install an interrupt handler for AC plug/unplug. */
+        set_irq_type(IRQ_GPIO(GPIO_NR_BLUEANGEL_USB_DETECT_N), IRQT_BOTHEDGE);
+        request_irq(IRQ_GPIO(GPIO_NR_BLUEANGEL_USB_DETECT_N), blueangel_ac_plug_isr, SA_SAMPLE_RANDOM,
+                     "AC plug", NULL);
+
+        /* Create a workqueue in which the battery monitor will run. */
+        monitor_q = create_singlethread_workqueue("battmon");
+        INIT_WORK(&monitor_work, blueangel_battery_update_status, NULL);
+
+        /* Create a timer to run the battery monitor every
+           BATTERY_CHECK_INTERVAL seconds. */
+        init_timer(&monitor_timer);
+        monitor_timer.function = blueangel_battery_timer;
+        monitor_timer.data = 0;
 
+        /* Start the first monitor task a few seconds from now. */
+        mod_timer(&monitor_timer, jiffies + HZ * 10);
+
+	apm_get_power_status = blueangel_battery_get_power_status;
+	#ifdef CONFIG_LEDS_TRIGGERS
+		led_trigger_register_simple("charge", &ledtrig_cradle);
+		led_trigger_register_simple("phone", &ledtrig_phone);
+	#endif
+
+        /* Get the DS2760 W1 device. */
+        probe_q = create_singlethread_workqueue("battmonprb");
+        INIT_WORK(&probe_work, blueangel_battery_probe_work, NULL);
+        blueangel_battery_probe_work(NULL);
+	//enable_irq(IRQ_GPIO(GPIO_NR_BLUEANGEL_USB_DETECT_N));
 	return 0;
 }
 
@@ -246,14 +503,31 @@
 	apm_get_power_status = NULL;
 	battery_class_unregister(&battery_dev);
 	battery_class_unregister(&battery_dev1);
-		#ifdef CONFIG_LEDS_TRIGGERS
-        led_trigger_unregister_simple(ledtrig_cradle);
-		#endif
+	
+	disable_irq(IRQ_GPIO(GPIO_NR_BLUEANGEL_USB_DETECT_N));
+	free_irq(IRQ_GPIO(GPIO_NR_BLUEANGEL_USB_DETECT_N), NULL);
+
+
+        cancel_delayed_work(&monitor_work);
+        flush_workqueue(monitor_q);
+        destroy_workqueue(monitor_q);
+
+        cancel_delayed_work(&probe_work);
+        flush_workqueue(probe_q);
+        destroy_workqueue(probe_q);
+
+        if (ds2760_dev)
+                put_device(ds2760_dev);
+	#ifdef CONFIG_LEDS_TRIGGERS
+		led_trigger_event(ledtrig_phone, LED_OFF);
+       	        led_trigger_unregister_simple(ledtrig_phone);
+		led_trigger_event(ledtrig_cradle, LED_OFF);
+       	        led_trigger_unregister_simple(ledtrig_cradle);
+	#endif
 }
 
 module_init(bat_init)
 module_exit(bat_exit)
 
-MODULE_AUTHOR("Matthias Burghardt");
-MODULE_DESCRIPTION("Battery (TI TSC2200) support for the HTC Blueangel");
+MODULE_DESCRIPTION("Battery (TI TSC2200) and DS2760 Support for the HTC Blueangel");
 MODULE_LICENSE("GPL");