summaryrefslogtreecommitdiff
path: root/firmware/src
diff options
context:
space:
mode:
authorlaforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-10-01 19:23:15 +0000
committerlaforge <laforge@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2006-10-01 19:23:15 +0000
commit6c0b462a2c43d8be50df627a2856d198ef76bf39 (patch)
tree48f33ad9f89443a121f23006bde53ddf62f1e110 /firmware/src
parent778802a3a429bcda51cf6d82a897f0e3de6fe644 (diff)
- finish implementation of timers based on PIT
- add [untested] code for timer-based LED blink codes git-svn-id: https://svn.openpcd.org:2342/trunk@242 6dc7ffe9-61d6-0310-9af1-9938baff3ed1
Diffstat (limited to 'firmware/src')
-rw-r--r--firmware/src/os/blinkcode.c101
-rw-r--r--firmware/src/os/blinkcode.h14
-rw-r--r--firmware/src/os/pit.c87
-rw-r--r--firmware/src/os/pit.h16
4 files changed, 217 insertions, 1 deletions
diff --git a/firmware/src/os/blinkcode.c b/firmware/src/os/blinkcode.c
new file mode 100644
index 0000000..8485392
--- /dev/null
+++ b/firmware/src/os/blinkcode.c
@@ -0,0 +1,101 @@
+/* SAM7DFU blink code support
+ * (C) 2006 by Harald Welte <laforge@gnumonks.org>
+ *
+ */
+
+#include <os/pit.h>
+#include <os/dbgu.h>
+#include <os/led.h>
+#include <os/blinkcode.h>
+
+#define NUM_LEDS 2
+
+enum blinkcode_state {
+ BLINKCODE_STATE_NONE,
+ BLINKCODE_STATE_SILENT, /* period of silence at start */
+ BLINKCODE_STATE_INIT, /* initial long light */
+ BLINKCODE_STATE_BLINK_OFF, /* blinking out, currently off */
+ BLINKCODE_STATE_BLINK_ON, /* blinking out, currently on */
+ BLINKCODE_STATE_DONE,
+};
+
+#define TIME_SILENT (1000)
+#define TIME_INIT (1000)
+#define TIME_BLINK (100)
+
+struct blinker {
+ struct timer_list timer;
+ enum blinkcode_state state;
+ int8_t num;
+ u_int8_t led;
+};
+
+static struct blinker blink_state[NUM_LEDS];
+
+static void blinkcode_cb(void *data)
+{
+ /* we got called back by the timer */
+ struct blinker *bl = data;
+
+ switch (bl->state) {
+ case BLINKCODE_STATE_NONE:
+ led_switch(bl->led, 0);
+ bl->state = BLINKCODE_STATE_SILENT;
+ bl->timer.expires = jiffies + TIME_SILENT;
+ break;
+ case BLINKCODE_STATE_SILENT:
+ /* we've finished the period of silence, turn led on */
+ led_switch(bl->led, 1);
+ bl->state = BLINKCODE_STATE_INIT;
+ bl->timer.expires = jiffies + TIME_INIT;
+ break;
+ case BLINKCODE_STATE_INIT:
+ /* we've finished the period of init */
+ led_switch(bl->led, 0);
+ bl->state = BLINKCODE_STATE_BLINK_OFF;
+ bl->timer.expires = jiffies + TIME_BLINK;
+ break;
+ case BLINKCODE_STATE_BLINK_OFF:
+ /* we've been off, turn on */
+ led_switch(bl->led, 1);
+ bl->state = BLINKCODE_STATE_BLINK_ON;
+ bl->num--;
+ if (bl->num <= 0) {
+ bl->state = BLINKCODE_STATE_NONE;
+ return;
+ }
+ break;
+ case BLINKCODE_STATE_BLINK_ON:
+ /* we've been on, turn off */
+ led_switch(bl->led, 0);
+ bl->state = BLINKCODE_STATE_BLINK_OFF;
+ break;
+ }
+ /* default case: re-add the timer */
+ timer_add(&bl->timer);
+}
+
+void blinkcode_set(int led, enum blinkcode_num num)
+{
+ if (led >= NUM_LEDS)
+ return;
+
+ timer_del(&blink_state[led].timer);
+
+ blink_state[led].num = num;
+ blink_state[led].state = BLINKCODE_STATE_NONE;
+
+ if (num != BLINKCODE_NONE)
+ timer_add(&blink_state[led].timer);
+}
+
+void blinkcode_init(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_LEDS; i++) {
+ blink_state[i].num = 0;
+ blink_state[i].state = BLINKCODE_STATE_NONE;
+ blink_state[i].led = i+1;
+ }
+}
diff --git a/firmware/src/os/blinkcode.h b/firmware/src/os/blinkcode.h
new file mode 100644
index 0000000..142c148
--- /dev/null
+++ b/firmware/src/os/blinkcode.h
@@ -0,0 +1,14 @@
+#ifndef _BLINKCODE_H
+#define _BLINKCODE_H
+
+enum blinkcode_num {
+ BLINKCODE_NONE,
+ BLINKCODE_IDLE,
+ BLINKCODE_DFU_MODE,
+ BLINKCODE_DFU_PROBLEM,
+ BLINKCODE_
+};
+
+extern void blinkcode_set(int led, enum blinkcode_num);
+extern void blinkcode_init(void);
+#endif
diff --git a/firmware/src/os/pit.c b/firmware/src/os/pit.c
index 5726f06..5c096cc 100644
--- a/firmware/src/os/pit.c
+++ b/firmware/src/os/pit.c
@@ -15,20 +15,105 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
+ *
+ * TODO: handle jiffies wrap correctly!
*/
#include <errno.h>
#include <sys/types.h>
+#include <asm/system.h>
+
#include <lib_AT91SAM7.h>
#include <AT91SAM7.h>
+#include <os/pit.h>
+
#include "../openpcd.h"
/* PIT runs at MCK/16 (= 3MHz) */
#define PIV_MS(x) (x * 3000)
+static struct timer_list *timers;
+
+unsigned long jiffies;
+
+static void __timer_insert(struct timer_list *new)
+{
+ struct timer_list *tl, *tl_prev = NULL;
+
+ if (!timers) {
+ /* optimize for the common, fast case */
+ new->next = NULL;
+ timers = new;
+ return;
+ }
+
+ for (tl = timers; tl != NULL; tl = tl->next) {
+ if (tl->expires < new->expires) {
+ /* we need ot add just before this one */
+ if (!tl_prev) {
+ new->next = timers;
+ timers = new;
+ } else {
+ new->next = tl;
+ tl_prev->next = new;
+ }
+ }
+ tl_prev = tl;
+ }
+}
+
+static void __timer_remove(struct timer_list *old)
+{
+ struct timer_list *tl, *tl_prev = NULL;
+
+ for (tl = timers; tl != NULL; tl = tl->next) {
+ if (tl == old) {
+ if (tl == timers)
+ timers = tl->next;
+ else
+ tl_prev->next = tl->next;
+ }
+ tl_prev = tl;
+ }
+}
+
+int timer_del(struct timer_list *tl)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ __timer_remove(tl);
+ local_irq_restore(flags);
+}
+
+void timer_add(struct timer_list *tl)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ __timer_insert(tl);
+ local_irq_restore(flags);
+}
+
static void pit_irq(void)
{
- /* FIXME: do something */
+ struct timer_list *tl;
+ unsigned long flags;
+
+ jiffies++;
+
+ /* this is the most simple/stupid algorithm one can come up with, but
+ * hey, we're on a small embedded platform with only a hand ful
+ * of timers, at all */
+ for (tl = timers; tl != 0; tl = tl->next) {
+ if (tl->expires >= jiffies) {
+ tl->function(tl->data);
+ local_irq_save(flags);
+ /* delete timer from list */
+ __timer_remove(tl);
+ local_irq_restore(flags);
+ }
+ }
}
void pit_mdelay(u_int32_t ms)
diff --git a/firmware/src/os/pit.h b/firmware/src/os/pit.h
index 92426e9..e941aeb 100644
--- a/firmware/src/os/pit.h
+++ b/firmware/src/os/pit.h
@@ -1,6 +1,22 @@
#ifndef _PIT_H
#define _PIT_H
+#include <sys/types.h>
+
+/* This API (but not the code) is modelled after the Linux API */
+
+struct timer_list {
+ struct timer_list *next;
+ unsigned long expires;
+ void (*function)(void *data);
+ void *data;
+};
+
+extern unsigned long jiffies;
+
+extern void timer_add(struct timer_list *timer);
+extern int timer_del(struct timer_list *timer);
+
extern void pit_init(void);
extern void pit_mdelay(u_int32_t ms);
personal git repositories of Harald Welte. Your mileage may vary