25 |
* SUCH DAMAGE. |
* SUCH DAMAGE. |
26 |
* |
* |
27 |
* |
* |
28 |
* $Id: dev_footbridge.c,v 1.44 2006/08/11 17:43:30 debug Exp $ |
* $Id: dev_footbridge.c,v 1.48 2006/09/30 10:09:19 debug Exp $ |
29 |
* |
* |
30 |
* Footbridge. Used in Netwinder and Cats. |
* Footbridge. Used in Netwinder and Cats. |
31 |
* |
* |
49 |
#include "machine.h" |
#include "machine.h" |
50 |
#include "memory.h" |
#include "memory.h" |
51 |
#include "misc.h" |
#include "misc.h" |
52 |
|
#include "timer.h" |
53 |
|
|
54 |
|
|
55 |
#include "dc21285reg.h" |
#include "dc21285reg.h" |
56 |
|
|
57 |
#define DEV_FOOTBRIDGE_TICK_SHIFT 14 |
#define DEV_FOOTBRIDGE_TICK_SHIFT 14 |
58 |
#define DEV_FOOTBRIDGE_LENGTH 0x400 |
#define DEV_FOOTBRIDGE_LENGTH 0x400 |
59 |
#define TIMER_POLL_THRESHOLD 15 |
|
60 |
|
|
61 |
|
static void timer_tick0(struct timer *t, void *extra) |
62 |
|
{ ((struct footbridge_data *)extra)->pending_timer_interrupts[0] ++; } |
63 |
|
static void timer_tick1(struct timer *t, void *extra) |
64 |
|
{ ((struct footbridge_data *)extra)->pending_timer_interrupts[1] ++; } |
65 |
|
static void timer_tick2(struct timer *t, void *extra) |
66 |
|
{ ((struct footbridge_data *)extra)->pending_timer_interrupts[2] ++; } |
67 |
|
static void timer_tick3(struct timer *t, void *extra) |
68 |
|
{ ((struct footbridge_data *)extra)->pending_timer_interrupts[3] ++; } |
69 |
|
|
70 |
|
|
71 |
|
static void reload_timer_value(struct cpu *cpu, struct footbridge_data *d, |
72 |
|
int timer_nr) |
73 |
|
{ |
74 |
|
double freq = (double)cpu->machine->emulated_hz; |
75 |
|
int cycles = d->timer_load[timer_nr]; |
76 |
|
|
77 |
|
if (d->timer_control[timer_nr] & TIMER_FCLK_16) |
78 |
|
cycles <<= 4; |
79 |
|
else if (d->timer_control[timer_nr] & TIMER_FCLK_256) |
80 |
|
cycles <<= 8; |
81 |
|
freq /= (double)cycles; |
82 |
|
|
83 |
|
d->timer_value[timer_nr] = d->timer_load[timer_nr]; |
84 |
|
d->timer_tick_countdown[timer_nr] = 1; |
85 |
|
|
86 |
|
/* printf("%i: %i -> %f Hz\n", timer_nr, |
87 |
|
d->timer_load[timer_nr], freq); */ |
88 |
|
|
89 |
|
if (d->timer[timer_nr] == NULL) { |
90 |
|
switch (timer_nr) { |
91 |
|
case 0: d->timer[0] = timer_add(freq, timer_tick0, d); break; |
92 |
|
case 1: d->timer[1] = timer_add(freq, timer_tick1, d); break; |
93 |
|
case 2: d->timer[2] = timer_add(freq, timer_tick2, d); break; |
94 |
|
case 3: d->timer[3] = timer_add(freq, timer_tick3, d); break; |
95 |
|
} |
96 |
|
} else { |
97 |
|
timer_update_frequency(d->timer[timer_nr], freq); |
98 |
|
} |
99 |
|
} |
100 |
|
|
101 |
|
|
102 |
/* |
/* |
103 |
* dev_footbridge_tick(): |
* dev_footbridge_tick(): |
104 |
* |
* |
105 |
* The 4 footbridge timers should decrease every now and then, and cause |
* The 4 footbridge timers should decrease and cause interrupts. Periodic |
106 |
* interrupts. Periodic interrupts restart as soon as they are acknowledged, |
* interrupts restart as soon as they are acknowledged, non-periodic |
107 |
* non-periodic interrupts need to be "reloaded" to restart. |
* interrupts need to be "reloaded" to restart. |
108 |
|
* |
109 |
|
* TODO: Hm. I thought I had solved this, but it didn't quite work. |
110 |
|
* This needs to be re-checked against documentation, sometime. |
111 |
*/ |
*/ |
112 |
void dev_footbridge_tick(struct cpu *cpu, void *extra) |
void dev_footbridge_tick(struct cpu *cpu, void *extra) |
113 |
{ |
{ |
114 |
int i; |
int i; |
115 |
struct footbridge_data *d = (struct footbridge_data *) extra; |
struct footbridge_data *d = (struct footbridge_data *) extra; |
116 |
|
|
|
if (!d->timer_being_read) |
|
|
d->timer_poll_mode = 0; |
|
|
|
|
117 |
for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) { |
for (i=0; i<N_FOOTBRIDGE_TIMERS; i++) { |
118 |
unsigned int amount = 1 << DEV_FOOTBRIDGE_TICK_SHIFT; |
if (d->timer_control[i] & TIMER_ENABLE) { |
119 |
if (d->timer_control[i] & TIMER_FCLK_16) |
if (d->pending_timer_interrupts[i] > 0) { |
120 |
amount >>= 4; |
d->timer_value[i] = random() % d->timer_load[i]; |
|
else if (d->timer_control[i] & TIMER_FCLK_256) |
|
|
amount >>= 8; |
|
|
|
|
|
if (d->timer_tick_countdown[i] == 0) |
|
|
continue; |
|
|
|
|
|
if (d->timer_value[i] > amount) |
|
|
d->timer_value[i] -= amount; |
|
|
else |
|
|
d->timer_value[i] = 0; |
|
|
|
|
|
if (d->timer_value[i] == 0) { |
|
|
d->timer_tick_countdown[i] --; |
|
|
if (d->timer_tick_countdown[i] > 0) |
|
|
continue; |
|
|
|
|
|
if (d->timer_control[i] & TIMER_ENABLE) |
|
121 |
cpu_interrupt(cpu, IRQ_TIMER_1 + i); |
cpu_interrupt(cpu, IRQ_TIMER_1 + i); |
122 |
d->timer_tick_countdown[i] = 0; |
} |
123 |
} |
} |
124 |
} |
} |
125 |
} |
} |
362 |
if (writeflag == MEM_READ) |
if (writeflag == MEM_READ) |
363 |
odata = d->timer_load[timer_nr]; |
odata = d->timer_load[timer_nr]; |
364 |
else { |
else { |
365 |
d->timer_value[timer_nr] = |
d->timer_load[timer_nr] = idata & TIMER_MAX_VAL; |
366 |
d->timer_load[timer_nr] = idata & TIMER_MAX_VAL; |
reload_timer_value(cpu, d, timer_nr); |
367 |
debug("[ footbridge: timer %i (1-based), value %i ]\n", |
/* debug("[ footbridge: timer %i (1-based), " |
368 |
timer_nr + 1, (int)d->timer_value[timer_nr]); |
"value %i ]\n", timer_nr + 1, |
369 |
d->timer_tick_countdown[timer_nr] = 1; |
(int)d->timer_value[timer_nr]); */ |
370 |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
371 |
} |
} |
372 |
break; |
break; |
373 |
|
|
374 |
case TIMER_1_VALUE: |
case TIMER_1_VALUE: |
375 |
if (writeflag == MEM_READ) { |
if (writeflag == MEM_READ) |
|
/* |
|
|
* NOTE/TODO: This is INCORRECT but speeds up NetBSD |
|
|
* and OpenBSD boot sequences: if the timer is polled |
|
|
* "very often" (such as during bootup), then this |
|
|
* causes the timers to expire quickly. |
|
|
*/ |
|
|
d->timer_being_read = 1; |
|
|
d->timer_poll_mode ++; |
|
|
if (d->timer_poll_mode >= TIMER_POLL_THRESHOLD) { |
|
|
d->timer_poll_mode = TIMER_POLL_THRESHOLD; |
|
|
dev_footbridge_tick(cpu, d); |
|
|
dev_footbridge_tick(cpu, d); |
|
|
dev_footbridge_tick(cpu, d); |
|
|
} |
|
376 |
odata = d->timer_value[timer_nr]; |
odata = d->timer_value[timer_nr]; |
377 |
d->timer_being_read = 0; |
else |
|
} else |
|
378 |
d->timer_value[timer_nr] = idata & TIMER_MAX_VAL; |
d->timer_value[timer_nr] = idata & TIMER_MAX_VAL; |
379 |
break; |
break; |
380 |
|
|
390 |
exit(1); |
exit(1); |
391 |
} |
} |
392 |
if (idata & TIMER_ENABLE) { |
if (idata & TIMER_ENABLE) { |
393 |
d->timer_value[timer_nr] = |
reload_timer_value(cpu, d, timer_nr); |
394 |
d->timer_load[timer_nr]; |
} else { |
395 |
d->timer_tick_countdown[timer_nr] = 1; |
d->pending_timer_interrupts[timer_nr] = 0; |
396 |
} |
} |
397 |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
398 |
} |
} |
400 |
|
|
401 |
case TIMER_1_CLEAR: |
case TIMER_1_CLEAR: |
402 |
if (d->timer_control[timer_nr] & TIMER_MODE_PERIODIC) { |
if (d->timer_control[timer_nr] & TIMER_MODE_PERIODIC) { |
403 |
d->timer_value[timer_nr] = d->timer_load[timer_nr]; |
reload_timer_value(cpu, d, timer_nr); |
|
d->timer_tick_countdown[timer_nr] = 1; |
|
404 |
} |
} |
405 |
|
|
406 |
|
if (d->pending_timer_interrupts[timer_nr] > 0) { |
407 |
|
d->pending_timer_interrupts[timer_nr] --; |
408 |
|
} |
409 |
|
|
410 |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
cpu_interrupt_ack(cpu, IRQ_TIMER_1 + timer_nr); |
411 |
break; |
break; |
412 |
|
|