Revision 2a29ddee hw/armv7m_nvic.c
b/hw/armv7m_nvic.c | ||
---|---|---|
30 | 30 |
int64_t tick; |
31 | 31 |
QEMUTimer *timer; |
32 | 32 |
} systick; |
33 |
MemoryRegion sysregmem; |
|
34 |
MemoryRegion gic_iomem_alias; |
|
35 |
MemoryRegion container; |
|
33 | 36 |
uint32_t num_irq; |
34 | 37 |
} nvic_state; |
35 | 38 |
|
39 |
static const uint8_t nvic_id[] = { |
|
40 |
0x00, 0xb0, 0x1b, 0x00, 0x0d, 0xe0, 0x05, 0xb1 |
|
41 |
}; |
|
42 |
|
|
36 | 43 |
/* qemu timers run at 1GHz. We want something closer to 1MHz. */ |
37 | 44 |
#define SYSTICK_SCALE 1000ULL |
38 | 45 |
|
... | ... | |
358 | 365 |
case 0xd38: /* Bus Fault Address. */ |
359 | 366 |
case 0xd3c: /* Aux Fault Status. */ |
360 | 367 |
goto bad_reg; |
368 |
case 0xf00: /* Software Triggered Interrupt Register */ |
|
369 |
if ((value & 0x1ff) < s->num_irq) { |
|
370 |
gic_set_pending_private(&s->gic, 0, value & 0x1ff); |
|
371 |
} |
|
372 |
break; |
|
361 | 373 |
default: |
362 | 374 |
bad_reg: |
363 | 375 |
hw_error("NVIC: Bad write offset 0x%x\n", offset); |
364 | 376 |
} |
365 | 377 |
} |
366 | 378 |
|
379 |
static uint64_t nvic_sysreg_read(void *opaque, target_phys_addr_t addr, |
|
380 |
unsigned size) |
|
381 |
{ |
|
382 |
/* At the moment we only support the ID registers for byte/word access. |
|
383 |
* This is not strictly correct as a few of the other registers also |
|
384 |
* allow byte access. |
|
385 |
*/ |
|
386 |
uint32_t offset = addr; |
|
387 |
if (offset >= 0xfe0) { |
|
388 |
if (offset & 3) { |
|
389 |
return 0; |
|
390 |
} |
|
391 |
return nvic_id[(offset - 0xfe0) >> 2]; |
|
392 |
} |
|
393 |
if (size == 4) { |
|
394 |
return nvic_readl(opaque, offset); |
|
395 |
} |
|
396 |
hw_error("NVIC: Bad read of size %d at offset 0x%x\n", size, offset); |
|
397 |
} |
|
398 |
|
|
399 |
static void nvic_sysreg_write(void *opaque, target_phys_addr_t addr, |
|
400 |
uint64_t value, unsigned size) |
|
401 |
{ |
|
402 |
uint32_t offset = addr; |
|
403 |
if (size == 4) { |
|
404 |
nvic_writel(opaque, offset, value); |
|
405 |
return; |
|
406 |
} |
|
407 |
hw_error("NVIC: Bad write of size %d at offset 0x%x\n", size, offset); |
|
408 |
} |
|
409 |
|
|
410 |
static const MemoryRegionOps nvic_sysreg_ops = { |
|
411 |
.read = nvic_sysreg_read, |
|
412 |
.write = nvic_sysreg_write, |
|
413 |
.endianness = DEVICE_NATIVE_ENDIAN, |
|
414 |
}; |
|
415 |
|
|
367 | 416 |
static const VMStateDescription vmstate_nvic = { |
368 | 417 |
.name = "armv7m_nvic", |
369 | 418 |
.version_id = 1, |
... | ... | |
399 | 448 |
/* The NVIC always has only one CPU */ |
400 | 449 |
s->gic.num_cpu = 1; |
401 | 450 |
gic_init(&s->gic, s->num_irq); |
402 |
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->gic.iomem); |
|
451 |
/* The NVIC and system controller register area looks like this: |
|
452 |
* 0..0xff : system control registers, including systick |
|
453 |
* 0x100..0xcff : GIC-like registers |
|
454 |
* 0xd00..0xfff : system control registers |
|
455 |
* We use overlaying to put the GIC like registers |
|
456 |
* over the top of the system control register region. |
|
457 |
*/ |
|
458 |
memory_region_init(&s->container, "nvic", 0x1000); |
|
459 |
/* The system register region goes at the bottom of the priority |
|
460 |
* stack as it covers the whole page. |
|
461 |
*/ |
|
462 |
memory_region_init_io(&s->sysregmem, &nvic_sysreg_ops, s, |
|
463 |
"nvic_sysregs", 0x1000); |
|
464 |
memory_region_add_subregion(&s->container, 0, &s->sysregmem); |
|
465 |
/* Alias the GIC region so we can get only the section of it |
|
466 |
* we need, and layer it on top of the system register region. |
|
467 |
*/ |
|
468 |
memory_region_init_alias(&s->gic_iomem_alias, "nvic-gic", &s->gic.iomem, |
|
469 |
0x100, 0xc00); |
|
470 |
memory_region_add_subregion_overlap(&s->container, 0x100, &s->gic.iomem, 1); |
|
471 |
/* Map the whole thing into system memory at the location required |
|
472 |
* by the v7M architecture. |
|
473 |
*/ |
|
474 |
memory_region_add_subregion(get_system_memory(), 0xe000e000, &s->container); |
|
403 | 475 |
s->systick.timer = qemu_new_timer_ns(vm_clock, systick_timer_tick, s); |
404 | 476 |
return 0; |
405 | 477 |
} |
Also available in: Unified diff