Commit 5439779e84e352f20ee2d3e26daec81292f1b59a
1 parent
2ea815ca
ETRAX: Allow boot from flash. Support the watchdog timer and resets through it.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@4592 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
163 additions
and
82 deletions
hw/etraxfs.c
... | ... | @@ -32,25 +32,29 @@ |
32 | 32 | |
33 | 33 | #include "etraxfs_dma.h" |
34 | 34 | |
35 | -static void main_cpu_reset(void *opaque) | |
36 | -{ | |
37 | - CPUState *env = opaque; | |
38 | - cpu_reset(env); | |
39 | -} | |
40 | - | |
41 | 35 | /* Init functions for different blocks. */ |
42 | 36 | extern qemu_irq *etraxfs_pic_init(CPUState *env, target_phys_addr_t base); |
43 | 37 | void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, |
44 | - target_phys_addr_t base); | |
45 | -void *etraxfs_eth_init(NICInfo *nd, CPUState *env, | |
46 | - qemu_irq *irq, target_phys_addr_t base); | |
38 | + target_phys_addr_t base); | |
39 | +void *etraxfs_eth_init(NICInfo *nd, CPUState *env, | |
40 | + qemu_irq *irq, target_phys_addr_t base); | |
47 | 41 | void etraxfs_ser_init(CPUState *env, qemu_irq *irq, CharDriverState *chr, |
48 | - target_phys_addr_t base); | |
42 | + target_phys_addr_t base); | |
49 | 43 | |
50 | 44 | #define FLASH_SIZE 0x2000000 |
51 | 45 | #define INTMEM_SIZE (128 * 1024) |
52 | 46 | |
53 | 47 | static void *etraxfs_dmac; |
48 | +static uint32_t bootstrap_pc; | |
49 | + | |
50 | +static void main_cpu_reset(void *opaque) | |
51 | +{ | |
52 | + CPUState *env = opaque; | |
53 | + cpu_reset(env); | |
54 | + | |
55 | + env->pregs[PR_CCS] &= ~I_FLAG; | |
56 | + env->pc = bootstrap_pc; | |
57 | +} | |
54 | 58 | |
55 | 59 | static |
56 | 60 | void bareetraxfs_init (ram_addr_t ram_size, int vga_ram_size, |
... | ... | @@ -64,6 +68,7 @@ void bareetraxfs_init (ram_addr_t ram_size, int vga_ram_size, |
64 | 68 | int kernel_size; |
65 | 69 | int i; |
66 | 70 | ram_addr_t phys_ram; |
71 | + ram_addr_t phys_flash; | |
67 | 72 | ram_addr_t phys_intmem; |
68 | 73 | |
69 | 74 | /* init CPUs */ |
... | ... | @@ -83,40 +88,42 @@ void bareetraxfs_init (ram_addr_t ram_size, int vga_ram_size, |
83 | 88 | /* The ETRAX-FS has 128Kb on chip ram, the docs refer to it as the |
84 | 89 | internal memory. Cached and uncached mappings. */ |
85 | 90 | phys_intmem = qemu_ram_alloc(INTMEM_SIZE); |
86 | - cpu_register_physical_memory(0xb8000000, INTMEM_SIZE, | |
87 | - phys_intmem | IO_MEM_RAM); | |
88 | - cpu_register_physical_memory(0x38000000, INTMEM_SIZE, | |
89 | - phys_intmem | IO_MEM_RAM); | |
90 | - | |
91 | - cpu_register_physical_memory(0, FLASH_SIZE, IO_MEM_ROM); | |
92 | - cpu_register_physical_memory(0x80000000, FLASH_SIZE, IO_MEM_ROM); | |
93 | - cpu_register_physical_memory(0x04000000, FLASH_SIZE, IO_MEM_ROM); | |
94 | - cpu_register_physical_memory(0x84000000, FLASH_SIZE, | |
95 | - 0x04000000 | IO_MEM_ROM); | |
96 | - i = drive_get_index(IF_PFLASH, 0, 0); | |
97 | - pflash_cfi02_register(0x80000000, qemu_ram_alloc(FLASH_SIZE), | |
98 | - drives_table[i].bdrv, (64 * 1024), | |
99 | - FLASH_SIZE >> 16, | |
100 | - 1, 2, 0x0000, 0x0000, 0x0000, 0x0000, 0x555, 0x2aa); | |
91 | + cpu_register_physical_memory(0xb8000000, INTMEM_SIZE, | |
92 | + phys_intmem | IO_MEM_RAM); | |
93 | + cpu_register_physical_memory(0x38000000, INTMEM_SIZE, | |
94 | + phys_intmem | IO_MEM_RAM); | |
95 | + | |
101 | 96 | |
97 | + phys_flash = qemu_ram_alloc(FLASH_SIZE); | |
98 | + i = drive_get_index(IF_PFLASH, 0, 0); | |
99 | + pflash_cfi02_register(0x80000000, phys_flash, | |
100 | + drives_table[i].bdrv, (64 * 1024), | |
101 | + FLASH_SIZE >> 16, | |
102 | + 1, 2, 0x0000, 0x0000, 0x0000, 0x0000, | |
103 | + 0x555, 0x2aa); | |
104 | + pflash_cfi02_register(0x0, phys_flash, | |
105 | + drives_table[i].bdrv, (64 * 1024), | |
106 | + FLASH_SIZE >> 16, | |
107 | + 1, 2, 0x0000, 0x0000, 0x0000, 0x0000, | |
108 | + 0x555, 0x2aa); | |
102 | 109 | pic = etraxfs_pic_init(env, 0xb001c000); |
103 | 110 | etraxfs_dmac = etraxfs_dmac_init(env, 0xb0000000, 10); |
104 | 111 | for (i = 0; i < 10; i++) { |
105 | - /* On ETRAX, odd numbered channels are inputs. */ | |
106 | - etraxfs_dmac_connect(etraxfs_dmac, i, pic + 7 + i, i & 1); | |
112 | + /* On ETRAX, odd numbered channels are inputs. */ | |
113 | + etraxfs_dmac_connect(etraxfs_dmac, i, pic + 7 + i, i & 1); | |
107 | 114 | } |
108 | 115 | |
109 | 116 | /* Add the two ethernet blocks. */ |
110 | 117 | eth[0] = etraxfs_eth_init(&nd_table[0], env, pic + 25, 0xb0034000); |
111 | 118 | if (nb_nics > 1) |
112 | - eth[1] = etraxfs_eth_init(&nd_table[1], env, pic + 26, 0xb0036000); | |
113 | - | |
119 | + eth[1] = etraxfs_eth_init(&nd_table[1], env, pic + 26, 0xb0036000); | |
120 | + | |
114 | 121 | /* The DMA Connector block is missing, hardwire things for now. */ |
115 | 122 | etraxfs_dmac_connect_client(etraxfs_dmac, 0, eth[0]); |
116 | 123 | etraxfs_dmac_connect_client(etraxfs_dmac, 1, eth[0] + 1); |
117 | 124 | if (eth[1]) { |
118 | - etraxfs_dmac_connect_client(etraxfs_dmac, 6, eth[1]); | |
119 | - etraxfs_dmac_connect_client(etraxfs_dmac, 7, eth[1] + 1); | |
125 | + etraxfs_dmac_connect_client(etraxfs_dmac, 6, eth[1]); | |
126 | + etraxfs_dmac_connect_client(etraxfs_dmac, 7, eth[1] + 1); | |
120 | 127 | } |
121 | 128 | |
122 | 129 | /* 2 timers. */ |
... | ... | @@ -124,40 +131,31 @@ void bareetraxfs_init (ram_addr_t ram_size, int vga_ram_size, |
124 | 131 | etraxfs_timer_init(env, pic + 0x1b, 0xb005e000); |
125 | 132 | |
126 | 133 | for (i = 0; i < 4; i++) { |
127 | - if (serial_hds[i]) { | |
128 | - etraxfs_ser_init(env, pic + 0x14 + i, | |
129 | - serial_hds[i], 0xb0026000 + i * 0x2000); | |
130 | - } | |
134 | + if (serial_hds[i]) { | |
135 | + etraxfs_ser_init(env, pic + 0x14 + i, | |
136 | + serial_hds[i], 0xb0026000 + i * 0x2000); | |
137 | + } | |
131 | 138 | } |
132 | 139 | |
140 | + if (kernel_filename) { | |
133 | 141 | #if 1 |
134 | - /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis devboard | |
135 | - SDK. */ | |
136 | - kernel_size = load_elf(kernel_filename, 0, &env->pc, NULL, NULL); | |
142 | + /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis | |
143 | + devboard SDK. */ | |
144 | + kernel_size = load_elf(kernel_filename, 0, | |
145 | + &bootstrap_pc, NULL, NULL); | |
137 | 146 | #else |
138 | - /* Takes a kimage from the axis devboard SDK. */ | |
139 | - kernel_size = load_image(kernel_filename, phys_ram_base + 0x4000); | |
140 | - env->pc = 0x40004000; | |
147 | + /* Takes a kimage from the axis devboard SDK. */ | |
148 | + kernel_size = load_image(kernel_filename, phys_ram_base + 0x4000); | |
149 | + bootstrap_pc = 0x40004000; | |
150 | + /* magic for boot. */ | |
151 | + env->regs[8] = 0x56902387; | |
152 | + env->regs[9] = 0x40004000 + kernel_size; | |
141 | 153 | #endif |
142 | - /* magic for boot. */ | |
143 | - env->regs[8] = 0x56902387; | |
144 | - env->regs[9] = 0x40004000 + kernel_size; | |
145 | - | |
146 | - { | |
147 | - unsigned char *ptr = phys_ram_base + 0x4000; | |
148 | - int i; | |
149 | - for (i = 0; i < 8; i++) | |
150 | - { | |
151 | - printf ("%2.2x ", ptr[i]); | |
152 | - } | |
153 | - printf("\n"); | |
154 | 154 | } |
155 | + env->pc = bootstrap_pc; | |
155 | 156 | |
156 | 157 | printf ("pc =%x\n", env->pc); |
157 | 158 | printf ("ram size =%ld\n", ram_size); |
158 | - printf ("kernel name =%s\n", kernel_filename); | |
159 | - printf ("kernel size =%d\n", kernel_size); | |
160 | - printf ("cpu haltd =%d\n", env->halted); | |
161 | 159 | } |
162 | 160 | |
163 | 161 | void DMA_run(void) |
... | ... | @@ -169,5 +167,5 @@ QEMUMachine bareetraxfs_machine = { |
169 | 167 | "bareetraxfs", |
170 | 168 | "Bare ETRAX FS board", |
171 | 169 | bareetraxfs_init, |
172 | - 0x4000000, | |
170 | + 0x8000000, | |
173 | 171 | }; | ... | ... |
hw/etraxfs_timer.c
... | ... | @@ -24,6 +24,7 @@ |
24 | 24 | #include <stdio.h> |
25 | 25 | #include <sys/time.h> |
26 | 26 | #include "hw.h" |
27 | +#include "sysemu.h" | |
27 | 28 | #include "qemu-timer.h" |
28 | 29 | |
29 | 30 | #define D(x) |
... | ... | @@ -36,6 +37,7 @@ |
36 | 37 | #define RW_TMR1_CTRL 0x18 |
37 | 38 | #define R_TIME 0x38 |
38 | 39 | #define RW_WD_CTRL 0x40 |
40 | +#define R_WD_STAT 0x44 | |
39 | 41 | #define RW_INTR_MASK 0x48 |
40 | 42 | #define RW_ACK_INTR 0x4c |
41 | 43 | #define R_INTR 0x50 |
... | ... | @@ -46,8 +48,12 @@ struct fs_timer_t { |
46 | 48 | qemu_irq *irq; |
47 | 49 | target_phys_addr_t base; |
48 | 50 | |
49 | - QEMUBH *bh; | |
50 | - ptimer_state *ptimer; | |
51 | + QEMUBH *bh_t0; | |
52 | + QEMUBH *bh_t1; | |
53 | + QEMUBH *bh_wd; | |
54 | + ptimer_state *ptimer_t0; | |
55 | + ptimer_state *ptimer_t1; | |
56 | + ptimer_state *ptimer_wd; | |
51 | 57 | struct timeval last; |
52 | 58 | |
53 | 59 | /* Control registers. */ |
... | ... | @@ -59,6 +65,8 @@ struct fs_timer_t { |
59 | 65 | uint32_t r_tmr1_data; |
60 | 66 | uint32_t rw_tmr1_ctrl; |
61 | 67 | |
68 | + uint32_t rw_wd_ctrl; | |
69 | + | |
62 | 70 | uint32_t rw_intr_mask; |
63 | 71 | uint32_t rw_ack_intr; |
64 | 72 | uint32_t r_intr; |
... | ... | @@ -114,15 +122,28 @@ timer_winvalid (void *opaque, target_phys_addr_t addr, uint32_t value) |
114 | 122 | } |
115 | 123 | |
116 | 124 | #define TIMER_SLOWDOWN 1 |
117 | -static void update_ctrl(struct fs_timer_t *t) | |
125 | +static void update_ctrl(struct fs_timer_t *t, int tnum) | |
118 | 126 | { |
119 | 127 | unsigned int op; |
120 | 128 | unsigned int freq; |
121 | 129 | unsigned int freq_hz; |
122 | 130 | unsigned int div; |
131 | + uint32_t ctrl; | |
132 | + ptimer_state *timer; | |
133 | + | |
134 | + if (tnum == 0) { | |
135 | + ctrl = t->rw_tmr0_ctrl; | |
136 | + div = t->rw_tmr0_div; | |
137 | + timer = t->ptimer_t0; | |
138 | + } else { | |
139 | + ctrl = t->rw_tmr1_ctrl; | |
140 | + div = t->rw_tmr1_div; | |
141 | + timer = t->ptimer_t1; | |
142 | + } | |
143 | + | |
123 | 144 | |
124 | - op = t->rw_tmr0_ctrl & 3; | |
125 | - freq = t->rw_tmr0_ctrl >> 2; | |
145 | + op = ctrl & 3; | |
146 | + freq = ctrl >> 2; | |
126 | 147 | freq_hz = 32000000; |
127 | 148 | |
128 | 149 | switch (freq) |
... | ... | @@ -134,33 +155,32 @@ static void update_ctrl(struct fs_timer_t *t) |
134 | 155 | case 4: freq_hz = 29493000; break; |
135 | 156 | case 5: freq_hz = 32000000; break; |
136 | 157 | case 6: freq_hz = 32768000; break; |
137 | - case 7: freq_hz = 100000000; break; | |
158 | + case 7: freq_hz = 100001000; break; | |
138 | 159 | default: |
139 | 160 | abort(); |
140 | 161 | break; |
141 | 162 | } |
142 | 163 | |
143 | - D(printf ("freq_hz=%d div=%d\n", freq_hz, t->rw_tmr0_div)); | |
144 | - div = t->rw_tmr0_div * TIMER_SLOWDOWN; | |
164 | + D(printf ("freq_hz=%d div=%d\n", freq_hz, div)); | |
165 | + div = div * TIMER_SLOWDOWN; | |
145 | 166 | div >>= 15; |
146 | 167 | freq_hz >>= 15; |
147 | - ptimer_set_freq(t->ptimer, freq_hz); | |
148 | - ptimer_set_limit(t->ptimer, div, 0); | |
168 | + ptimer_set_freq(timer, freq_hz); | |
169 | + ptimer_set_limit(timer, div, 0); | |
149 | 170 | |
150 | 171 | switch (op) |
151 | 172 | { |
152 | 173 | case 0: |
153 | 174 | /* Load. */ |
154 | - ptimer_set_limit(t->ptimer, div, 1); | |
155 | - ptimer_run(t->ptimer, 1); | |
175 | + ptimer_set_limit(timer, div, 1); | |
156 | 176 | break; |
157 | 177 | case 1: |
158 | 178 | /* Hold. */ |
159 | - ptimer_stop(t->ptimer); | |
179 | + ptimer_stop(timer); | |
160 | 180 | break; |
161 | 181 | case 2: |
162 | 182 | /* Run. */ |
163 | - ptimer_run(t->ptimer, 0); | |
183 | + ptimer_run(timer, 0); | |
164 | 184 | break; |
165 | 185 | default: |
166 | 186 | abort(); |
... | ... | @@ -180,13 +200,55 @@ static void timer_update_irq(struct fs_timer_t *t) |
180 | 200 | qemu_irq_lower(t->irq[0]); |
181 | 201 | } |
182 | 202 | |
183 | -static void timer_hit(void *opaque) | |
203 | +static void timer0_hit(void *opaque) | |
184 | 204 | { |
185 | 205 | struct fs_timer_t *t = opaque; |
186 | 206 | t->r_intr |= 1; |
187 | 207 | timer_update_irq(t); |
188 | 208 | } |
189 | 209 | |
210 | +static void timer1_hit(void *opaque) | |
211 | +{ | |
212 | + struct fs_timer_t *t = opaque; | |
213 | + t->r_intr |= 2; | |
214 | + timer_update_irq(t); | |
215 | +} | |
216 | + | |
217 | +static void watchdog_hit(void *opaque) | |
218 | +{ | |
219 | + qemu_system_reset_request(); | |
220 | +} | |
221 | + | |
222 | +static inline void timer_watchdog_update(struct fs_timer_t *t, uint32_t value) | |
223 | +{ | |
224 | + unsigned int wd_en = t->rw_wd_ctrl & (1 << 8); | |
225 | + unsigned int wd_key = t->rw_wd_ctrl >> 9; | |
226 | + unsigned int wd_cnt = t->rw_wd_ctrl & 511; | |
227 | + unsigned int new_key = value >> 9 & ((1 << 7) - 1); | |
228 | + unsigned int new_cmd = (value >> 8) & 1; | |
229 | + | |
230 | + /* If the watchdog is enabled, they written key must match the | |
231 | + complement of the previous. */ | |
232 | + wd_key = ~wd_key & ((1 << 7) - 1); | |
233 | + | |
234 | + if (wd_en && wd_key != new_key) | |
235 | + return; | |
236 | + | |
237 | + D(printf("en=%d new_key=%x oldkey=%x cmd=%d cnt=%d\n", | |
238 | + wd_en, new_key, wd_key, wd_cmd, wd_cnt)); | |
239 | + | |
240 | + ptimer_set_freq(t->ptimer_wd, 760); | |
241 | + if (wd_cnt == 0) | |
242 | + wd_cnt = 256; | |
243 | + ptimer_set_count(t->ptimer_wd, wd_cnt); | |
244 | + if (new_cmd) | |
245 | + ptimer_run(t->ptimer_wd, 1); | |
246 | + else | |
247 | + ptimer_stop(t->ptimer_wd); | |
248 | + | |
249 | + t->rw_wd_ctrl = value; | |
250 | +} | |
251 | + | |
190 | 252 | static void |
191 | 253 | timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
192 | 254 | { |
... | ... | @@ -203,13 +265,15 @@ timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
203 | 265 | case RW_TMR0_CTRL: |
204 | 266 | D(printf ("RW_TMR0_CTRL=%x\n", value)); |
205 | 267 | t->rw_tmr0_ctrl = value; |
206 | - update_ctrl(t); | |
268 | + update_ctrl(t, 0); | |
207 | 269 | break; |
208 | 270 | case RW_TMR1_DIV: |
209 | 271 | t->rw_tmr1_div = value; |
210 | 272 | break; |
211 | 273 | case RW_TMR1_CTRL: |
212 | 274 | D(printf ("RW_TMR1_CTRL=%x\n", value)); |
275 | + t->rw_tmr1_ctrl = value; | |
276 | + update_ctrl(t, 1); | |
213 | 277 | break; |
214 | 278 | case RW_INTR_MASK: |
215 | 279 | D(printf ("RW_INTR_MASK=%x\n", value)); |
... | ... | @@ -217,7 +281,7 @@ timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
217 | 281 | timer_update_irq(t); |
218 | 282 | break; |
219 | 283 | case RW_WD_CTRL: |
220 | - D(printf ("RW_WD_CTRL=%x\n", value)); | |
284 | + timer_watchdog_update(t, value); | |
221 | 285 | break; |
222 | 286 | case RW_ACK_INTR: |
223 | 287 | t->rw_ack_intr = value; |
... | ... | @@ -232,17 +296,30 @@ timer_writel (void *opaque, target_phys_addr_t addr, uint32_t value) |
232 | 296 | } |
233 | 297 | |
234 | 298 | static CPUReadMemoryFunc *timer_read[] = { |
235 | - &timer_rinvalid, | |
236 | - &timer_rinvalid, | |
237 | - &timer_readl, | |
299 | + &timer_rinvalid, | |
300 | + &timer_rinvalid, | |
301 | + &timer_readl, | |
238 | 302 | }; |
239 | 303 | |
240 | 304 | static CPUWriteMemoryFunc *timer_write[] = { |
241 | - &timer_winvalid, | |
242 | - &timer_winvalid, | |
243 | - &timer_writel, | |
305 | + &timer_winvalid, | |
306 | + &timer_winvalid, | |
307 | + &timer_writel, | |
244 | 308 | }; |
245 | 309 | |
310 | +static void etraxfs_timer_reset(void *opaque) | |
311 | +{ | |
312 | + struct fs_timer_t *t = opaque; | |
313 | + | |
314 | + ptimer_stop(t->ptimer_t0); | |
315 | + ptimer_stop(t->ptimer_t1); | |
316 | + ptimer_stop(t->ptimer_wd); | |
317 | + t->rw_wd_ctrl = 0; | |
318 | + t->r_intr = 0; | |
319 | + t->rw_intr_mask = 0; | |
320 | + qemu_irq_lower(t->irq[0]); | |
321 | +} | |
322 | + | |
246 | 323 | void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, |
247 | 324 | target_phys_addr_t base) |
248 | 325 | { |
... | ... | @@ -253,12 +330,18 @@ void etraxfs_timer_init(CPUState *env, qemu_irq *irqs, |
253 | 330 | if (!t) |
254 | 331 | return; |
255 | 332 | |
256 | - t->bh = qemu_bh_new(timer_hit, t); | |
257 | - t->ptimer = ptimer_init(t->bh); | |
333 | + t->bh_t0 = qemu_bh_new(timer0_hit, t); | |
334 | + t->bh_t1 = qemu_bh_new(timer1_hit, t); | |
335 | + t->bh_wd = qemu_bh_new(watchdog_hit, t); | |
336 | + t->ptimer_t0 = ptimer_init(t->bh_t0); | |
337 | + t->ptimer_t1 = ptimer_init(t->bh_t1); | |
338 | + t->ptimer_wd = ptimer_init(t->bh_wd); | |
258 | 339 | t->irq = irqs; |
259 | 340 | t->env = env; |
260 | 341 | t->base = base; |
261 | 342 | |
262 | 343 | timer_regs = cpu_register_io_memory(0, timer_read, timer_write, t); |
263 | 344 | cpu_register_physical_memory (base, 0x5c, timer_regs); |
345 | + | |
346 | + qemu_register_reset(etraxfs_timer_reset, t); | |
264 | 347 | } | ... | ... |