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