r4921 - in developers/werner: . bqmeter bqmeter/patches

werner at docs.openmoko.org werner at docs.openmoko.org
Fri Feb 13 03:36:51 CET 2009


Author: werner
Date: 2009-02-13 03:36:49 +0100 (Fri, 13 Feb 2009)
New Revision: 4921

Added:
   developers/werner/bqmeter/
   developers/werner/bqmeter/Makefile
   developers/werner/bqmeter/bqmeter.c
   developers/werner/bqmeter/patches/
   developers/werner/bqmeter/patches/hdq-read-nosleep.patch
   developers/werner/bqmeter/patches/hdq-sysfs-read.patch
   developers/werner/bqmeter/patches/s3c-resume-early-action.patch
   developers/werner/bqmeter/patches/series
Log:
Coulomb-counter based power meter. Work in progress.



Added: developers/werner/bqmeter/Makefile
===================================================================
--- developers/werner/bqmeter/Makefile	                        (rev 0)
+++ developers/werner/bqmeter/Makefile	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,35 @@
+CC=arm-angstrom-linux-gnueabi-gcc
+
+CFLAGS=-Wall -Wshadow -g -O -I../pmu
+LDFLAGS=
+
+PREFIX=/usr
+
+NAME=bqmeter
+OBJS=bqmeter.o ../pmu/pmu.o
+
+.PHONY:		all install uninstall clean depend spotless
+
+all:		$(NAME)
+
+$(NAME):	$(OBJS)
+
+install:	$(NAME)
+		install -D $(NAME) $(PREFIX)/bin/$(NAME)
+
+uninstall:
+		rm -f $(PREFIX)/bin/$(NAME)
+
+depend:
+		$(CPP) $(CFLAGS) -MM -MG *.c >.depend || \
+		  { rm -f .depend; exit 1; }
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+clean:
+		rm -f $(OBJS) .depend
+
+spotless:	clean
+		rm -f $(NAME)

Added: developers/werner/bqmeter/bqmeter.c
===================================================================
--- developers/werner/bqmeter/bqmeter.c	                        (rev 0)
+++ developers/werner/bqmeter/bqmeter.c	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,269 @@
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include "pmu.h"
+
+
+/*
+ * PMU 1s	|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
+ * bq update  |              |              |               |              |
+ *                                                                 ^
+ *               ^                          ^               ^      |       ^
+ *               |------------->----------->|-------------->|--->->|------>|
+ * Duration      | t_setup       t_wait_bq  | Tbq           |  (*) | t_valid
+ * Time          t_init                     t_start         t_stop t_wake  t_dl
+ *
+ * (*) = t_fuzz, t_wait_rtc
+ *
+ * t_init	Time when we initiate the measurement in suspend process
+ * t_setup	Time until the system has entered suspend (must include t_fuzz)
+ * t_wait_bq	Time to wait until the next bq update interval
+ * t_start	Time at which the measurement begins
+ * Tbq		bq measurement interval
+ * t_stop	Time at which the measurement ends
+ * t_fuzz	Maximum timing error we tolerate
+ * t_wait_rtc	Time to wait for the next second interrupt of the RTC
+ * t_wake	Time at which we schdule a wakeup interrupt
+ * t_valid	Time we have to retrieve the measurement result
+ * t_dl		Time after which the measurement is invalid, t_dl = t_stop+Tbq
+ */
+
+
+#define	PMU_RTCSC	0x59		/* RTC second value register */
+
+#define T_BQ_US		5120000L	/* bq updates every 5.12 s */
+#define	T_PMU_US	1000000L	/* PMU updates every second */
+
+#define	T_FUZZ_US	100000L		/* 100 ms */
+
+
+static int pmu_fd;
+
+
+static int sysfs_read(const char *path)
+{
+	FILE *f;
+	int value;
+
+	f = fopen(path, "r");
+	if (!f)
+		goto fail;
+	if (fscanf(f, "%d\n", &value) < 0)
+		goto fail;
+	if (fclose(f) != EOF)
+		return;
+
+fail:
+	perror(path);
+	exit(1);
+}
+
+
+static void sysfs_write(const char *path, int value)
+{
+	FILE *f;
+
+	f = fopen(path, "w");
+	if (!f)
+		goto fail;
+	if (fprintf(f, "%d\n", value) < 0)
+		goto fail;
+	if (fclose(f) != EOF)
+		return;
+
+fail:
+	perror(path);
+	exit(1);
+}
+
+
+static int get_current(void)
+{
+}
+
+
+static void set_current(int high)
+{
+}
+
+
+static uint8_t read_bq(void)
+{
+}
+
+
+static uint8_t read_rtc(void)
+{
+	return pmu_read(pmu_fd, PMU_RTCSC);
+}
+
+
+static void now(struct timeval *t)
+{
+	if (gettimeofday(t, NULL) < 0) {
+		perror("gettimeofday");
+		exit(1);
+	}
+}
+
+
+/*
+ * returns b-a in microseconds. Caps the difference at ~1000 seconds.
+ */
+
+static long delta_us(const struct timeval *a, const struct timeval *b)
+{
+	long s, us;
+	
+	s = b->tv_sec-a->tv_sec;
+	if (s < -1000)
+		return -1000*1000*1000;
+	if (s > 1000)
+		return 1000*1000*1000;
+	us = b->tv_usec-a->tv_usec;
+	return us+1000*1000*s;
+}
+
+
+static void plus_us(struct timeval *t, long add_us)
+{
+	t->tv_usec += add_us;
+	while (t->tv_usec > 999999) {
+		t->tv_usec -= 1000000;
+		t->tv_sec++;
+	}
+}
+
+
+static void next_interval(struct timeval *t, const struct timeval *earliest,
+    long increment)
+{
+	while (1) {
+		if (t->tv_sec > earliest->tv_sec)
+			return;
+		if (t->tv_sec == earliest->tv_sec &&
+		    t->tv_usec >= earliest->tv_usec)
+			return;
+		plus_us(t, increment);
+	}
+}
+
+
+static int get_time_changes(struct timeval *bq, struct timeval *rtc)
+{
+	int old_current;
+	uint8_t bq0, rtc0;
+	int need_bq = 1, need_rtc = 1;
+	struct timeval t, next;
+	int ok = 0;
+	long diff;
+
+	old_current = get_current();
+	set_current(0);
+	sleep(6); /* > 5.12s, to make sure meter has taken the low value */
+	now(&t);
+	set_current(1);
+	bq0 = read_bq();
+	rtc0 = read_rtc();
+	while (need_bq || need_rtc) {
+		if (need_bq && bq0 != read_bq()) {
+			need_bq = 0;
+			*bq = t;
+		}
+		if (need_rtc && rtc0 != read_rtc()) {
+			need_rtc = 0;
+			*rtc = t;
+		}
+		now(&next);
+		diff = delta_us(&t, &next);
+		if (diff > T_FUZZ_US) {
+			fprintf(stderr, "rejecting result (%ld us > %ld us)\n",
+			    diff, T_FUZZ_US);
+			goto out;
+		}
+		t = next;
+	}
+	ok = 1;
+out:
+	if (!old_current)
+		set_current(0);
+	return ok;
+}
+
+
+static void foo(long t_setup_us, long t_fuzz_us)
+{
+	struct timeval t_ref, t_init;
+	struct timeval t_start_min, t_start, t_stop;
+	struct timeval t_wake_min, t_wake;
+	struct timeval t_dl, t_end;
+	struct timeval bq, rtc;
+
+	/* synchronize the clocks */
+
+	while (1) {
+		now(&t_ref);
+		if (get_time_changes(&bq, &rtc))
+			break;
+	}
+	now(&t_init);
+
+	/* find t_start */
+
+	t_start_min = t_init;
+	plus_us(&t_start_min, t_setup_us);
+	t_start = bq;
+	next_interval(&t_start, &t_start_min, T_BQ_US);
+
+	/* find t_stop */
+
+	t_stop = t_start;
+	plus_us(&t_stop, T_BQ_US);
+
+	/* find t_wake */
+
+	t_wake_min = t_stop;
+	plus_us(&t_wake_min, t_fuzz_us);
+	t_wake = rtc;
+	next_interval(&t_wake, &t_wake_min, T_PMU_US);
+
+	/* find t_dl (deadline) */
+
+	t_dl = t_stop;
+	plus_us(&t_dl, T_BQ_US);
+
+	/* @@@ setup */
+	/* @@@ suspend */
+	/* @@@ retrieve result */
+	/* @@@ check deadline */
+
+	now(&t_end);
+
+	printf("t_init   %6.3f\n", delta_us(&t_ref, &t_init)/1000.0);
+	printf("t_start  %6.3f\n", delta_us(&t_ref, &t_start)/1000.0);
+	printf("t_stop   %6.3f\n", delta_us(&t_ref, &t_stop)/1000.0);
+	printf("t_wake   %6.3f\n", delta_us(&t_ref, &t_wake)/1000.0);
+	printf("t_dl     %6.3f\n", delta_us(&t_ref, &t_dl)/1000.0);
+	printf("end      %6.3f\n", delta_us(&t_ref, &t_end)/1000.0);
+
+	/*
+	 * @@@ if we're serious about this check, then we should do it in the
+	 * kernel, right after retrieving the data.
+	 */
+	if (delta_us(&t_dl, &t_end) < 0) {
+		fprintf(stderr, "missed the deadline :-(\n");
+		exit(1);
+	}
+}
+
+
+int main(void)
+{
+	pmu_fd = pmu_open();
+	foo(10, 10);
+	return 0;
+}
+

Added: developers/werner/bqmeter/patches/hdq-read-nosleep.patch
===================================================================
--- developers/werner/bqmeter/patches/hdq-read-nosleep.patch	                        (rev 0)
+++ developers/werner/bqmeter/patches/hdq-read-nosleep.patch	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,88 @@
+Index: ktrack/drivers/power/gta02_hdq.c
+===================================================================
+--- ktrack.orig/drivers/power/gta02_hdq.c	2009-02-04 06:38:20.000000000 -0200
++++ ktrack/drivers/power/gta02_hdq.c	2009-02-04 06:38:51.000000000 -0200
+@@ -121,6 +121,71 @@
+ }
+ EXPORT_SYMBOL_GPL(gta02hdq_write);
+ 
++static inline int get_hdq(void)
++{
++	return s3c2410_gpio_getpin(fiq_ipc.hdq_gpio_pin);
++}
++
++static inline void set_hdq(int on)
++{
++	s3c2410_gpio_setpin(fiq_ipc.hdq_gpio_pin, on);
++}
++
++int gta02hdq_read_nosleep(int address)
++{
++	int i, j;
++	uint8_t res = 0;
++
++	if (!fiq_ipc.hdq_probed)
++		return -EINVAL;
++	/*
++	 * It would be nice if we could mutex_trylock here, but that's
++	 * forbidden. So we just make sure nothing is horribly wrong.
++	 */
++	BUG_ON(mutex_is_locked(&fiq_ipc.hdq_lock));
++
++	s3c2410_gpio_cfgpin(fiq_ipc.hdq_gpio_pin, S3C2410_GPIO_OUTPUT);
++
++	/* send break */
++	set_hdq(0);
++	udelay(200);	/* min 190 us */
++	set_hdq(1);
++	udelay(50);	/* min 40 us */
++
++	/* send address and W/nR */
++	for (i = 0; i != 8; i++) {
++		set_hdq(0);
++		if (address & (1 << i)) {
++			udelay(20);	/* min 0.5 us, max 50 us */
++		} else {
++			udelay(100);	/* min 86 us, max 145 us */
++		}
++		set_hdq(1);
++		udelay(200);	/* min 190-tHW us */
++	}
++
++	/* get data byte */
++	s3c2410_gpio_cfgpin(fiq_ipc.hdq_gpio_pin, S3C2410_GPIO_INPUT);
++	for (i = 0; i != 8; i++) {
++		/* tCYCD = max 250 us and we've already have waited 100 us */
++		for (j = 200; !get_hdq() && j; j--)
++			udelay(1);
++		if (!j)
++			return -EIO;
++		/* tRSPS = max 320 us from start of W/nR */
++		for (j = 300; get_hdq() && j; j--)
++			udelay(1);
++		if (!j)
++			return -EIO;
++		udelay(100);	/* 1: 32-50 us; 0: 80-145 us */
++		if (get_hdq())
++			res |= 1 << i;
++	}
++
++	return res;
++}
++EXPORT_SYMBOL_GPL(gta02hdq_read_nosleep);
++
+ /* sysfs */
+ 
+ static ssize_t hdq_sysfs_dump(struct device *dev, struct device_attribute *attr,
+Index: ktrack/include/linux/gta02_hdq.h
+===================================================================
+--- ktrack.orig/include/linux/gta02_hdq.h	2009-02-04 06:38:20.000000000 -0200
++++ ktrack/include/linux/gta02_hdq.h	2009-02-04 06:39:20.000000000 -0200
+@@ -12,6 +12,7 @@
+ };
+ 
+ int gta02hdq_read(int address);
++int gta02hdq_read_nosleep(int address);
+ int gta02hdq_write(int address, u8 data);
+ int gta02hdq_initialized(void);
+ 

Added: developers/werner/bqmeter/patches/hdq-sysfs-read.patch
===================================================================
--- developers/werner/bqmeter/patches/hdq-sysfs-read.patch	                        (rev 0)
+++ developers/werner/bqmeter/patches/hdq-sysfs-read.patch	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,53 @@
+Index: ktrack/drivers/power/gta02_hdq.c
+===================================================================
+--- ktrack.orig/drivers/power/gta02_hdq.c	2009-02-04 05:41:48.000000000 -0200
++++ ktrack/drivers/power/gta02_hdq.c	2009-02-04 05:56:41.000000000 -0200
+@@ -26,6 +26,10 @@
+ #define HDQ_READ 0
+ #define HDQ_WRITE 0x80
+ 
++
++static atomic_t hdq_read_addr; /* address for sysfs read */
++
++
+ static int fiq_busy(void)
+ {
+ 	int request = (volatile u8)fiq_ipc.hdq_request_ctr;
+@@ -180,12 +184,37 @@
+ 	return count;
+ }
+ 
++static ssize_t hdq_sysfs_read_get(struct device *dev,
++    struct device_attribute *attr, char *buf)
++{
++	/* cache address so that we don't race with hdq_sysfs_read_set */
++	int addr = atomic_read(&hdq_read_addr);
++	uint8_t data;
++
++	data = gta02hdq_read(addr);
++
++	/*
++	 * We also return the address so that the reader can retry or complain
++	 * if there was a race with another user of the single-register read.
++	 */
++	return sprintf(buf, "%d %u\n", addr, data);
++}
++
++static ssize_t hdq_sysfs_read_set(struct device *dev,
++    struct device_attribute *attr, const char *buf, size_t count)
++{
++	atomic_set(&hdq_read_addr, atoi(buf));
++	return 0;
++}
++
+ static DEVICE_ATTR(dump, 0400, hdq_sysfs_dump, NULL);
+ static DEVICE_ATTR(write, 0600, NULL, hdq_sysfs_write);
++static DEVICE_ATTR(read, 0600, hdq_sysfs_read_get, hdq_sysfs_read_set);
+ 
+ static struct attribute *gta02hdq_sysfs_entries[] = {
+ 	&dev_attr_dump.attr,
+ 	&dev_attr_write.attr,
++	&dev_attr_read.attr,
+ 	NULL
+ };
+ 

Added: developers/werner/bqmeter/patches/s3c-resume-early-action.patch
===================================================================
--- developers/werner/bqmeter/patches/s3c-resume-early-action.patch	                        (rev 0)
+++ developers/werner/bqmeter/patches/s3c-resume-early-action.patch	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,32 @@
+Index: ktrack/arch/arm/plat-s3c/include/plat/pm.h
+===================================================================
+--- ktrack.orig/arch/arm/plat-s3c/include/plat/pm.h	2009-02-04 13:21:00.000000000 -0200
++++ ktrack/arch/arm/plat-s3c/include/plat/pm.h	2009-02-04 13:22:43.000000000 -0200
+@@ -182,3 +182,5 @@
+ extern void s3c_pm_save_core(void);
+ extern void s3c_pm_restore_core(void);
+ 
++/* Early callback for instrumentation */
++extern void (*s3c_pm_resume_early_action)(void);
+Index: ktrack/arch/arm/plat-s3c/pm.c
+===================================================================
+--- ktrack.orig/arch/arm/plat-s3c/pm.c	2009-02-04 13:22:56.000000000 -0200
++++ ktrack/arch/arm/plat-s3c/pm.c	2009-02-04 13:23:16.000000000 -0200
+@@ -34,6 +34,7 @@
+ /* for external use */
+ 
+ unsigned long s3c_pm_flags;
++void (*s3c_pm_resume_early_action)(void);
+ 
+ /* Debug code:
+  *
+@@ -320,6 +321,9 @@
+ 	s3c_pm_restore_uarts();
+ 	s3c_pm_restore_gpios();
+ 
++	if (s3c_pm_resume_early_action)
++		s3c_pm_resume_early_action();
++
+ 	s3c_pm_debug_init();
+ 
+ 	/* check what irq (if any) restored the system */

Added: developers/werner/bqmeter/patches/series
===================================================================
--- developers/werner/bqmeter/patches/series	                        (rev 0)
+++ developers/werner/bqmeter/patches/series	2009-02-13 02:36:49 UTC (rev 4921)
@@ -0,0 +1,3 @@
+hdq-sysfs-read.patch
+hdq-read-nosleep.patch
+s3c-resume-early-action.patch




More information about the commitlog mailing list