Renesas SH-4 patches queue

Patches from Zoltan:
 - Various clean up to align the code style with the rest of the code base
 - QOM'ify the SH_SERIAL device
 - Modify few memory region size to better match the hardware manual
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEE+qvnXhKRciHc/Wuy4+MsLN6twN4FAmF9ez8ACgkQ4+MsLN6t
 wN5jIBAAySm8Q7cDR/CVI+ubaAD1dW0sBAH6WVFd+P/pz6XotvLH9qvFjspTrKmw
 QujMLr/oDP3z/JvVZGpe3ArCkCoqoy9Q+hDFYfwgGmOun0p6bLLw7GC6qoWUwdfE
 BPVaZIbeW15ujn7Cawul+2rU2TIwcp+rtSg4KnKuLQp+1kKrkVlZDYp4YZeUjpIJ
 98KhnLRO38V/VUVvtuJjK/Vksc7mdP2es1kmStq5HKGYXKCTd68JAPi7OCxg4mKj
 IxGyamy8aK6khW1ccIeaCJv6NStHR1rVlFQDG10HildAcOvrJDUyQjBqYbDfh/Gj
 zsbpXw0fwz+GWmQlj2d7ndfYNNqr+G6IwBNrzVcnDoJYlVmLrJUOYnY509hcVhVY
 c33XgXYSurSskG5kSdhpZLIN4inrnRoX410gCi14mqxcjl88DTbeIjqUzizaHzRU
 xCgIAjNKnryEimGb8cEShftBT/U9t/VeXXxfe+MfoBCG0sKczUp6QVHhkq6dH5qV
 xFAZOQyxw6lLaTrcQQByCBh+NFyqtrYFsqS1b1Or3z0ZfKF65Y8M5sx1RufhybfV
 UutGY7JaZU3MJEeT6UY9XHSZXwGLajixjXuYR25Gj/LYgOSMIP0iqsci2V0AfBLQ
 u1Z54qIllDVAickaHLlBfI2jn6XXfzZ09+POHOsf//b39i1QjIk=
 =ktI+
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/philmd/tags/renesas-20211030' into staging

Renesas SH-4 patches queue

Patches from Zoltan:
- Various clean up to align the code style with the rest of the code base
- QOM'ify the SH_SERIAL device
- Modify few memory region size to better match the hardware manual

# gpg: Signature made Sat 30 Oct 2021 10:05:03 AM PDT
# gpg:                using RSA key FAABE75E12917221DCFD6BB2E3E32C2CDEADC0DE
# gpg: Good signature from "Philippe Mathieu-Daudé (F4BUG) <f4bug@amsat.org>" [full]

* remotes/philmd/tags/renesas-20211030: (30 commits)
  hw/timer/sh_timer: Remove use of hw_error
  hw/timer/sh_timer: Fix timer memory region size
  hw/timer/sh_timer: Do not wrap lines that are not too long
  hw/timer/sh_timer: Rename sh_timer_state to SHTimerState
  hw/intc/sh_intc: Remove unneeded local variable initialisers
  hw/intc/sh_intc: Simplify allocating sources array
  hw/intc/sh_intc: Avoid using continue in loops
  hw/intc/sh_intc: Replace abort() with g_assert_not_reached()
  hw/intc/sh_intc: Inline and drop sh_intc_source() function
  hw/intc/sh_intc: Use array index instead of pointer arithmetics
  hw/intc/sh_intc: Remove excessive parenthesis
  hw/intc/sh_intc: Move sh_intc_register() closer to its only user
  hw/intc/sh_intc: Drop another useless macro
  hw/intc/sh_intc: Rename iomem region
  hw/intc/sh_intc: Turn some defines into an enum
  hw/intc/sh_intc: Use existing macro instead of local one
  hw/char/sh_serial: Add device id to trace output
  hw/char/sh_serial: QOM-ify
  hw/char/sh_serial: Split off sh_serial_reset() from sh_serial_init()
  hw/char/sh_serial: Embed QEMUTimer in state struct
  ...

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-10-30 11:31:41 -07:00
commit af531756d2
17 changed files with 1713 additions and 1684 deletions

View File

@ -26,13 +26,17 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "hw/sysbus.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "hw/qdev-core.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "hw/sh4/sh.h" #include "hw/sh4/sh.h"
#include "chardev/char-fe.h" #include "chardev/char-fe.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/timer.h" #include "qemu/timer.h"
#include "qemu/log.h"
//#define DEBUG_SERIAL #include "trace.h"
#define SH_SERIAL_FLAG_TEND (1 << 0) #define SH_SERIAL_FLAG_TEND (1 << 0)
#define SH_SERIAL_FLAG_TDE (1 << 1) #define SH_SERIAL_FLAG_TDE (1 << 1)
@ -42,10 +46,10 @@
#define SH_RX_FIFO_LENGTH (16) #define SH_RX_FIFO_LENGTH (16)
typedef struct { OBJECT_DECLARE_SIMPLE_TYPE(SHSerialState, SH_SERIAL)
MemoryRegion iomem;
MemoryRegion iomem_p4; struct SHSerialState {
MemoryRegion iomem_a7; SysBusDevice parent;
uint8_t smr; uint8_t smr;
uint8_t brr; uint8_t brr;
uint8_t scr; uint8_t scr;
@ -59,13 +63,12 @@ typedef struct {
uint8_t rx_tail; uint8_t rx_tail;
uint8_t rx_head; uint8_t rx_head;
int freq; uint8_t feat;
int feat;
int flags; int flags;
int rtrg; int rtrg;
CharBackend chr; CharBackend chr;
QEMUTimer *fifo_timeout_timer; QEMUTimer fifo_timeout_timer;
uint64_t etu; /* Elementary Time Unit (ns) */ uint64_t etu; /* Elementary Time Unit (ns) */
qemu_irq eri; qemu_irq eri;
@ -73,9 +76,13 @@ typedef struct {
qemu_irq txi; qemu_irq txi;
qemu_irq tei; qemu_irq tei;
qemu_irq bri; qemu_irq bri;
} sh_serial_state; };
static void sh_serial_clear_fifo(sh_serial_state * s) typedef struct {} SHSerialStateClass;
OBJECT_DEFINE_TYPE(SHSerialState, sh_serial, SH_SERIAL, SYS_BUS_DEVICE)
static void sh_serial_clear_fifo(SHSerialState *s)
{ {
memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH);
s->rx_cnt = 0; s->rx_cnt = 0;
@ -86,14 +93,12 @@ static void sh_serial_clear_fifo(sh_serial_state * s)
static void sh_serial_write(void *opaque, hwaddr offs, static void sh_serial_write(void *opaque, hwaddr offs,
uint64_t val, unsigned size) uint64_t val, unsigned size)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
DeviceState *d = DEVICE(s);
unsigned char ch; unsigned char ch;
#ifdef DEBUG_SERIAL trace_sh_serial_write(d->id, size, offs, val);
printf("sh_serial: write offs=0x%02x val=0x%02x\n", switch (offs) {
offs, val);
#endif
switch(offs) {
case 0x00: /* SMR */ case 0x00: /* SMR */
s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff); s->smr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0x7b : 0xff);
return; return;
@ -103,8 +108,9 @@ static void sh_serial_write(void *opaque, hwaddr offs,
case 0x08: /* SCR */ case 0x08: /* SCR */
/* TODO : For SH7751, SCIF mask should be 0xfb. */ /* TODO : For SH7751, SCIF mask should be 0xfb. */
s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff);
if (!(val & (1 << 5))) if (!(val & (1 << 5))) {
s->flags |= SH_SERIAL_FLAG_TEND; s->flags |= SH_SERIAL_FLAG_TEND;
}
if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) {
qemu_set_irq(s->txi, val & (1 << 7)); qemu_set_irq(s->txi, val & (1 << 7));
} }
@ -115,8 +121,10 @@ static void sh_serial_write(void *opaque, hwaddr offs,
case 0x0c: /* FTDR / TDR */ case 0x0c: /* FTDR / TDR */
if (qemu_chr_fe_backend_connected(&s->chr)) { if (qemu_chr_fe_backend_connected(&s->chr)) {
ch = val; ch = val;
/* XXX this blocks entire thread. Rewrite to use /*
* qemu_chr_fe_write and background I/O callbacks */ * XXX this blocks entire thread. Rewrite to use
* qemu_chr_fe_write and background I/O callbacks
*/
qemu_chr_fe_write_all(&s->chr, &ch, 1); qemu_chr_fe_write_all(&s->chr, &ch, 1);
} }
s->dr = val; s->dr = val;
@ -129,18 +137,23 @@ static void sh_serial_write(void *opaque, hwaddr offs,
#endif #endif
} }
if (s->feat & SH_SERIAL_FEAT_SCIF) { if (s->feat & SH_SERIAL_FEAT_SCIF) {
switch(offs) { switch (offs) {
case 0x10: /* FSR */ case 0x10: /* FSR */
if (!(val & (1 << 6))) if (!(val & (1 << 6))) {
s->flags &= ~SH_SERIAL_FLAG_TEND; s->flags &= ~SH_SERIAL_FLAG_TEND;
if (!(val & (1 << 5))) }
if (!(val & (1 << 5))) {
s->flags &= ~SH_SERIAL_FLAG_TDE; s->flags &= ~SH_SERIAL_FLAG_TDE;
if (!(val & (1 << 4))) }
if (!(val & (1 << 4))) {
s->flags &= ~SH_SERIAL_FLAG_BRK; s->flags &= ~SH_SERIAL_FLAG_BRK;
if (!(val & (1 << 1))) }
if (!(val & (1 << 1))) {
s->flags &= ~SH_SERIAL_FLAG_RDF; s->flags &= ~SH_SERIAL_FLAG_RDF;
if (!(val & (1 << 0))) }
if (!(val & (1 << 0))) {
s->flags &= ~SH_SERIAL_FLAG_DR; s->flags &= ~SH_SERIAL_FLAG_DR;
}
if (!(val & (1 << 1)) || !(val & (1 << 0))) { if (!(val & (1 << 1)) || !(val & (1 << 0))) {
if (s->rxi) { if (s->rxi) {
@ -176,9 +189,8 @@ static void sh_serial_write(void *opaque, hwaddr offs,
case 0x24: /* LSR */ case 0x24: /* LSR */
return; return;
} }
} } else {
else { switch (offs) {
switch(offs) {
#if 0 #if 0
case 0x0c: case 0x0c:
ret = s->dr; ret = s->dr;
@ -192,20 +204,20 @@ static void sh_serial_write(void *opaque, hwaddr offs,
return; return;
} }
} }
qemu_log_mask(LOG_GUEST_ERROR,
fprintf(stderr, "sh_serial: unsupported write to 0x%02" "%s: unsupported write to 0x%02" HWADDR_PRIx "\n",
HWADDR_PRIx "\n", offs); __func__, offs);
abort();
} }
static uint64_t sh_serial_read(void *opaque, hwaddr offs, static uint64_t sh_serial_read(void *opaque, hwaddr offs,
unsigned size) unsigned size)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
uint32_t ret = ~0; DeviceState *d = DEVICE(s);
uint32_t ret = UINT32_MAX;
#if 0 #if 0
switch(offs) { switch (offs) {
case 0x00: case 0x00:
ret = s->smr; ret = s->smr;
break; break;
@ -221,7 +233,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
} }
#endif #endif
if (s->feat & SH_SERIAL_FEAT_SCIF) { if (s->feat & SH_SERIAL_FEAT_SCIF) {
switch(offs) { switch (offs) {
case 0x00: /* SMR */ case 0x00: /* SMR */
ret = s->smr; ret = s->smr;
break; break;
@ -230,29 +242,37 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
break; break;
case 0x10: /* FSR */ case 0x10: /* FSR */
ret = 0; ret = 0;
if (s->flags & SH_SERIAL_FLAG_TEND) if (s->flags & SH_SERIAL_FLAG_TEND) {
ret |= (1 << 6); ret |= (1 << 6);
if (s->flags & SH_SERIAL_FLAG_TDE) }
if (s->flags & SH_SERIAL_FLAG_TDE) {
ret |= (1 << 5); ret |= (1 << 5);
if (s->flags & SH_SERIAL_FLAG_BRK) }
if (s->flags & SH_SERIAL_FLAG_BRK) {
ret |= (1 << 4); ret |= (1 << 4);
if (s->flags & SH_SERIAL_FLAG_RDF) }
if (s->flags & SH_SERIAL_FLAG_RDF) {
ret |= (1 << 1); ret |= (1 << 1);
if (s->flags & SH_SERIAL_FLAG_DR) }
if (s->flags & SH_SERIAL_FLAG_DR) {
ret |= (1 << 0); ret |= (1 << 0);
}
if (s->scr & (1 << 5)) if (s->scr & (1 << 5)) {
s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND; s->flags |= SH_SERIAL_FLAG_TDE | SH_SERIAL_FLAG_TEND;
}
break; break;
case 0x14: case 0x14:
if (s->rx_cnt > 0) { if (s->rx_cnt > 0) {
ret = s->rx_fifo[s->rx_tail++]; ret = s->rx_fifo[s->rx_tail++];
s->rx_cnt--; s->rx_cnt--;
if (s->rx_tail == SH_RX_FIFO_LENGTH) if (s->rx_tail == SH_RX_FIFO_LENGTH) {
s->rx_tail = 0; s->rx_tail = 0;
if (s->rx_cnt < s->rtrg) }
if (s->rx_cnt < s->rtrg) {
s->flags &= ~SH_SERIAL_FLAG_RDF; s->flags &= ~SH_SERIAL_FLAG_RDF;
}
} }
break; break;
case 0x18: case 0x18:
@ -268,9 +288,8 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
ret = 0; ret = 0;
break; break;
} }
} } else {
else { switch (offs) {
switch(offs) {
#if 0 #if 0
case 0x0c: case 0x0c:
ret = s->dr; ret = s->dr;
@ -287,40 +306,39 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs,
break; break;
} }
} }
#ifdef DEBUG_SERIAL trace_sh_serial_read(d->id, size, offs, ret);
printf("sh_serial: read offs=0x%02x val=0x%x\n",
offs, ret);
#endif
if (ret & ~((1 << 16) - 1)) { if (ret > UINT16_MAX) {
fprintf(stderr, "sh_serial: unsupported read from 0x%02" qemu_log_mask(LOG_GUEST_ERROR,
HWADDR_PRIx "\n", offs); "%s: unsupported read from 0x%02" HWADDR_PRIx "\n",
abort(); __func__, offs);
ret = 0;
} }
return ret; return ret;
} }
static int sh_serial_can_receive(sh_serial_state *s) static int sh_serial_can_receive(SHSerialState *s)
{ {
return s->scr & (1 << 4); return s->scr & (1 << 4);
} }
static void sh_serial_receive_break(sh_serial_state *s) static void sh_serial_receive_break(SHSerialState *s)
{ {
if (s->feat & SH_SERIAL_FEAT_SCIF) if (s->feat & SH_SERIAL_FEAT_SCIF) {
s->sr |= (1 << 4); s->sr |= (1 << 4);
}
} }
static int sh_serial_can_receive1(void *opaque) static int sh_serial_can_receive1(void *opaque)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
return sh_serial_can_receive(s); return sh_serial_can_receive(s);
} }
static void sh_serial_timeout_int(void *opaque) static void sh_serial_timeout_int(void *opaque)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
s->flags |= SH_SERIAL_FLAG_RDF; s->flags |= SH_SERIAL_FLAG_RDF;
if (s->scr & (1 << 6) && s->rxi) { if (s->scr & (1 << 6) && s->rxi) {
@ -330,7 +348,7 @@ static void sh_serial_timeout_int(void *opaque)
static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
if (s->feat & SH_SERIAL_FEAT_SCIF) { if (s->feat & SH_SERIAL_FEAT_SCIF) {
int i; int i;
@ -344,11 +362,11 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
if (s->rx_cnt >= s->rtrg) { if (s->rx_cnt >= s->rtrg) {
s->flags |= SH_SERIAL_FLAG_RDF; s->flags |= SH_SERIAL_FLAG_RDF;
if (s->scr & (1 << 6) && s->rxi) { if (s->scr & (1 << 6) && s->rxi) {
timer_del(s->fifo_timeout_timer); timer_del(&s->fifo_timeout_timer);
qemu_set_irq(s->rxi, 1); qemu_set_irq(s->rxi, 1);
} }
} else { } else {
timer_mod(s->fifo_timeout_timer, timer_mod(&s->fifo_timeout_timer,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu);
} }
} }
@ -360,9 +378,10 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size)
static void sh_serial_event(void *opaque, QEMUChrEvent event) static void sh_serial_event(void *opaque, QEMUChrEvent event)
{ {
sh_serial_state *s = opaque; SHSerialState *s = opaque;
if (event == CHR_EVENT_BREAK) if (event == CHR_EVENT_BREAK) {
sh_serial_receive_break(s); sh_serial_receive_break(s);
}
} }
static const MemoryRegionOps sh_serial_ops = { static const MemoryRegionOps sh_serial_ops = {
@ -371,20 +390,10 @@ static const MemoryRegionOps sh_serial_ops = {
.endianness = DEVICE_NATIVE_ENDIAN, .endianness = DEVICE_NATIVE_ENDIAN,
}; };
void sh_serial_init(MemoryRegion *sysmem, static void sh_serial_reset(DeviceState *dev)
hwaddr base, int feat,
uint32_t freq, Chardev *chr,
qemu_irq eri_source,
qemu_irq rxi_source,
qemu_irq txi_source,
qemu_irq tei_source,
qemu_irq bri_source)
{ {
sh_serial_state *s; SHSerialState *s = SH_SERIAL(dev);
s = g_malloc0(sizeof(sh_serial_state));
s->feat = feat;
s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE; s->flags = SH_SERIAL_FLAG_TEND | SH_SERIAL_FLAG_TDE;
s->rtrg = 1; s->rtrg = 1;
@ -393,39 +402,64 @@ void sh_serial_init(MemoryRegion *sysmem,
s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */ s->scr = 1 << 5; /* pretend that TX is enabled so early printk works */
s->sptr = 0; s->sptr = 0;
if (feat & SH_SERIAL_FEAT_SCIF) { if (s->feat & SH_SERIAL_FEAT_SCIF) {
s->fcr = 0; s->fcr = 0;
} } else {
else {
s->dr = 0xff; s->dr = 0xff;
} }
sh_serial_clear_fifo(s); sh_serial_clear_fifo(s);
}
memory_region_init_io(&s->iomem, NULL, &sh_serial_ops, s, static void sh_serial_realize(DeviceState *d, Error **errp)
"serial", 0x100000000ULL); {
SHSerialState *s = SH_SERIAL(d);
MemoryRegion *iomem = g_malloc(sizeof(*iomem));
memory_region_init_alias(&s->iomem_p4, NULL, "serial-p4", &s->iomem, assert(d->id);
0, 0x28); memory_region_init_io(iomem, OBJECT(d), &sh_serial_ops, s, d->id, 0x28);
memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); sysbus_init_mmio(SYS_BUS_DEVICE(d), iomem);
qdev_init_gpio_out_named(d, &s->eri, "eri", 1);
qdev_init_gpio_out_named(d, &s->rxi, "rxi", 1);
qdev_init_gpio_out_named(d, &s->txi, "txi", 1);
qdev_init_gpio_out_named(d, &s->tei, "tei", 1);
qdev_init_gpio_out_named(d, &s->bri, "bri", 1);
memory_region_init_alias(&s->iomem_a7, NULL, "serial-a7", &s->iomem, if (qemu_chr_fe_backend_connected(&s->chr)) {
0, 0x28);
memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
if (chr) {
qemu_chr_fe_init(&s->chr, chr, &error_abort);
qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1, qemu_chr_fe_set_handlers(&s->chr, sh_serial_can_receive1,
sh_serial_receive1, sh_serial_receive1,
sh_serial_event, NULL, s, NULL, true); sh_serial_event, NULL, s, NULL, true);
} }
s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, timer_init_ns(&s->fifo_timeout_timer, QEMU_CLOCK_VIRTUAL,
sh_serial_timeout_int, s); sh_serial_timeout_int, s);
s->etu = NANOSECONDS_PER_SECOND / 9600; s->etu = NANOSECONDS_PER_SECOND / 9600;
s->eri = eri_source; }
s->rxi = rxi_source;
s->txi = txi_source; static void sh_serial_finalize(Object *obj)
s->tei = tei_source; {
s->bri = bri_source; SHSerialState *s = SH_SERIAL(obj);
timer_del(&s->fifo_timeout_timer);
}
static void sh_serial_init(Object *obj)
{
}
static Property sh_serial_properties[] = {
DEFINE_PROP_CHR("chardev", SHSerialState, chr),
DEFINE_PROP_UINT8("features", SHSerialState, feat, 0),
DEFINE_PROP_END_OF_LIST()
};
static void sh_serial_class_init(ObjectClass *oc, void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
device_class_set_props(dc, sh_serial_properties);
dc->realize = sh_serial_realize;
dc->reset = sh_serial_reset;
/* Reason: part of SuperH CPU/SoC, needs to be wired up */
dc->user_creatable = false;
} }

View File

@ -101,3 +101,7 @@ exynos_uart_rx_timeout(uint32_t channel, uint32_t stat, uint32_t intsp) "UART%d:
# cadence_uart.c # cadence_uart.c
cadence_uart_baudrate(unsigned baudrate) "baudrate %u" cadence_uart_baudrate(unsigned baudrate) "baudrate %u"
# sh_serial.c
sh_serial_read(char *id, unsigned size, uint64_t offs, uint64_t val) " %s size %d offs 0x%02" PRIx64 " -> 0x%02" PRIx64
sh_serial_write(char *id, unsigned size, uint64_t offs, uint64_t val) "%s size %d offs 0x%02" PRIx64 " <- 0x%02" PRIx64

View File

@ -9,40 +9,37 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/log.h"
#include "cpu.h" #include "cpu.h"
#include "hw/sh4/sh_intc.h" #include "hw/sh4/sh_intc.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "hw/sh4/sh.h" #include "hw/sh4/sh.h"
#include "trace.h"
//#define DEBUG_INTC
//#define DEBUG_INTC_SOURCES
#define INTC_A7(x) ((x) & 0x1fffffff)
void sh_intc_toggle_source(struct intc_source *source, void sh_intc_toggle_source(struct intc_source *source,
int enable_adj, int assert_adj) int enable_adj, int assert_adj)
{ {
int enable_changed = 0; int enable_changed = 0;
int pending_changed = 0; int pending_changed = 0;
int old_pending; int old_pending;
if ((source->enable_count == source->enable_max) && (enable_adj == -1)) if (source->enable_count == source->enable_max && enable_adj == -1) {
enable_changed = -1; enable_changed = -1;
}
source->enable_count += enable_adj; source->enable_count += enable_adj;
if (source->enable_count == source->enable_max) if (source->enable_count == source->enable_max) {
enable_changed = 1; enable_changed = 1;
}
source->asserted += assert_adj; source->asserted += assert_adj;
old_pending = source->pending; old_pending = source->pending;
source->pending = source->asserted && source->pending = source->asserted &&
(source->enable_count == source->enable_max); (source->enable_count == source->enable_max);
if (old_pending != source->pending) if (old_pending != source->pending) {
pending_changed = 1; pending_changed = 1;
}
if (pending_changed) { if (pending_changed) {
if (source->pending) { if (source->pending) {
source->parent->pending++; source->parent->pending++;
@ -54,35 +51,30 @@ void sh_intc_toggle_source(struct intc_source *source,
if (source->parent->pending == 0) { if (source->parent->pending == 0) {
cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD); cpu_reset_interrupt(first_cpu, CPU_INTERRUPT_HARD);
} }
} }
} }
if (enable_changed || assert_adj || pending_changed) { if (enable_changed || assert_adj || pending_changed) {
#ifdef DEBUG_INTC_SOURCES trace_sh_intc_sources(source->parent->pending, source->asserted,
printf("sh_intc: (%d/%d/%d/%d) interrupt source 0x%x %s%s%s\n", source->enable_count, source->enable_max,
source->parent->pending, source->vect, source->asserted ? "asserted " :
source->asserted, assert_adj ? "deasserted" : "",
source->enable_count, enable_changed == 1 ? "enabled " :
source->enable_max, enable_changed == -1 ? "disabled " : "",
source->vect, source->pending ? "pending" : "");
source->asserted ? "asserted " : }
assert_adj ? "deasserted" : "",
enable_changed == 1 ? "enabled " :
enable_changed == -1 ? "disabled " : "",
source->pending ? "pending" : "");
#endif
}
} }
static void sh_intc_set_irq (void *opaque, int n, int level) static void sh_intc_set_irq(void *opaque, int n, int level)
{ {
struct intc_desc *desc = opaque; struct intc_desc *desc = opaque;
struct intc_source *source = &(desc->sources[n]); struct intc_source *source = &desc->sources[n];
if (level && !source->asserted) if (level && !source->asserted) {
sh_intc_toggle_source(source, 0, 1); sh_intc_toggle_source(source, 0, 1);
else if (!level && source->asserted) } else if (!level && source->asserted) {
sh_intc_toggle_source(source, 0, -1); sh_intc_toggle_source(source, 0, -1);
}
} }
int sh_intc_get_pending_vector(struct intc_desc *desc, int imask) int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
@ -97,147 +89,124 @@ int sh_intc_get_pending_vector(struct intc_desc *desc, int imask)
} }
for (i = 0; i < desc->nr_sources; i++) { for (i = 0; i < desc->nr_sources; i++) {
struct intc_source *source = desc->sources + i; struct intc_source *source = &desc->sources[i];
if (source->pending) { if (source->pending) {
#ifdef DEBUG_INTC_SOURCES trace_sh_intc_pending(desc->pending, source->vect);
printf("sh_intc: (%d) returning interrupt source 0x%x\n",
desc->pending, source->vect);
#endif
return source->vect; return source->vect;
} }
} }
g_assert_not_reached();
abort();
} }
#define INTC_MODE_NONE 0 typedef enum {
#define INTC_MODE_DUAL_SET 1 INTC_MODE_NONE,
#define INTC_MODE_DUAL_CLR 2 INTC_MODE_DUAL_SET,
#define INTC_MODE_ENABLE_REG 3 INTC_MODE_DUAL_CLR,
#define INTC_MODE_MASK_REG 4 INTC_MODE_ENABLE_REG,
#define INTC_MODE_IS_PRIO 8 INTC_MODE_MASK_REG,
} SHIntCMode;
#define INTC_MODE_IS_PRIO 0x80
static unsigned int sh_intc_mode(unsigned long address, static SHIntCMode sh_intc_mode(unsigned long address, unsigned long set_reg,
unsigned long set_reg, unsigned long clr_reg) unsigned long clr_reg)
{ {
if ((address != INTC_A7(set_reg)) && if (address != A7ADDR(set_reg) && address != A7ADDR(clr_reg)) {
(address != INTC_A7(clr_reg)))
return INTC_MODE_NONE; return INTC_MODE_NONE;
if (set_reg && clr_reg) {
if (address == INTC_A7(set_reg))
return INTC_MODE_DUAL_SET;
else
return INTC_MODE_DUAL_CLR;
} }
if (set_reg && clr_reg) {
if (set_reg) return address == A7ADDR(set_reg) ?
return INTC_MODE_ENABLE_REG; INTC_MODE_DUAL_SET : INTC_MODE_DUAL_CLR;
else }
return INTC_MODE_MASK_REG; return set_reg ? INTC_MODE_ENABLE_REG : INTC_MODE_MASK_REG;
} }
static void sh_intc_locate(struct intc_desc *desc, static void sh_intc_locate(struct intc_desc *desc,
unsigned long address, unsigned long address,
unsigned long **datap, unsigned long **datap,
intc_enum **enums, intc_enum **enums,
unsigned int *first, unsigned int *first,
unsigned int *width, unsigned int *width,
unsigned int *modep) unsigned int *modep)
{ {
unsigned int i, mode; SHIntCMode mode;
unsigned int i;
/* this is slow but works for now */ /* this is slow but works for now */
if (desc->mask_regs) { if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) { for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i; struct intc_mask_reg *mr = &desc->mask_regs[i];
mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg); mode = sh_intc_mode(address, mr->set_reg, mr->clr_reg);
if (mode == INTC_MODE_NONE) if (mode != INTC_MODE_NONE) {
continue; *modep = mode;
*datap = &mr->value;
*modep = mode; *enums = mr->enum_ids;
*datap = &mr->value; *first = mr->reg_width - 1;
*enums = mr->enum_ids; *width = 1;
*first = mr->reg_width - 1; return;
*width = 1; }
return; }
}
} }
if (desc->prio_regs) { if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) { for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i; struct intc_prio_reg *pr = &desc->prio_regs[i];
mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg); mode = sh_intc_mode(address, pr->set_reg, pr->clr_reg);
if (mode == INTC_MODE_NONE) if (mode != INTC_MODE_NONE) {
continue; *modep = mode | INTC_MODE_IS_PRIO;
*datap = &pr->value;
*modep = mode | INTC_MODE_IS_PRIO; *enums = pr->enum_ids;
*datap = &pr->value; *first = pr->reg_width / pr->field_width - 1;
*enums = pr->enum_ids; *width = pr->field_width;
*first = (pr->reg_width / pr->field_width) - 1; return;
*width = pr->field_width; }
return; }
}
} }
g_assert_not_reached();
abort();
} }
static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id, static void sh_intc_toggle_mask(struct intc_desc *desc, intc_enum id,
int enable, int is_group) int enable, int is_group)
{ {
struct intc_source *source = desc->sources + id; struct intc_source *source = &desc->sources[id];
if (!id)
return;
if (!id) {
return;
}
if (!source->next_enum_id && (!source->enable_max || !source->vect)) { if (!source->next_enum_id && (!source->enable_max || !source->vect)) {
#ifdef DEBUG_INTC_SOURCES qemu_log_mask(LOG_UNIMP,
printf("sh_intc: reserved interrupt source %d modified\n", id); "sh_intc: reserved interrupt source %d modified\n", id);
#endif return;
return;
} }
if (source->vect) if (source->vect) {
sh_intc_toggle_source(source, enable ? 1 : -1, 0); sh_intc_toggle_source(source, enable ? 1 : -1, 0);
#ifdef DEBUG_INTC
else {
printf("setting interrupt group %d to %d\n", id, !!enable);
} }
#endif
if ((is_group || !source->vect) && source->next_enum_id) { if ((is_group || !source->vect) && source->next_enum_id) {
sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1); sh_intc_toggle_mask(desc, source->next_enum_id, enable, 1);
} }
#ifdef DEBUG_INTC
if (!source->vect) { if (!source->vect) {
printf("setting interrupt group %d to %d - done\n", id, !!enable); trace_sh_intc_set(id, !!enable);
} }
#endif
} }
static uint64_t sh_intc_read(void *opaque, hwaddr offset, static uint64_t sh_intc_read(void *opaque, hwaddr offset, unsigned size)
unsigned size)
{ {
struct intc_desc *desc = opaque; struct intc_desc *desc = opaque;
intc_enum *enum_ids = NULL; intc_enum *enum_ids;
unsigned int first = 0; unsigned int first;
unsigned int width = 0; unsigned int width;
unsigned int mode = 0; unsigned int mode;
unsigned long *valuep; unsigned long *valuep;
#ifdef DEBUG_INTC sh_intc_locate(desc, (unsigned long)offset, &valuep,
printf("sh_intc_read 0x%lx\n", (unsigned long) offset); &enum_ids, &first, &width, &mode);
#endif trace_sh_intc_read(size, (uint64_t)offset, *valuep);
sh_intc_locate(desc, (unsigned long)offset, &valuep,
&enum_ids, &first, &width, &mode);
return *valuep; return *valuep;
} }
@ -245,45 +214,40 @@ static void sh_intc_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size) uint64_t value, unsigned size)
{ {
struct intc_desc *desc = opaque; struct intc_desc *desc = opaque;
intc_enum *enum_ids = NULL; intc_enum *enum_ids;
unsigned int first = 0; unsigned int first;
unsigned int width = 0; unsigned int width;
unsigned int mode = 0; unsigned int mode;
unsigned int k;
unsigned long *valuep; unsigned long *valuep;
unsigned int k;
unsigned long mask; unsigned long mask;
#ifdef DEBUG_INTC trace_sh_intc_write(size, (uint64_t)offset, value);
printf("sh_intc_write 0x%lx 0x%08x\n", (unsigned long) offset, value); sh_intc_locate(desc, (unsigned long)offset, &valuep,
#endif &enum_ids, &first, &width, &mode);
sh_intc_locate(desc, (unsigned long)offset, &valuep,
&enum_ids, &first, &width, &mode);
switch (mode) { switch (mode) {
case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO: break; case INTC_MODE_ENABLE_REG | INTC_MODE_IS_PRIO:
case INTC_MODE_DUAL_SET: value |= *valuep; break; break;
case INTC_MODE_DUAL_CLR: value = *valuep & ~value; break; case INTC_MODE_DUAL_SET:
default: abort(); value |= *valuep;
break;
case INTC_MODE_DUAL_CLR:
value = *valuep & ~value;
break;
default:
g_assert_not_reached();
} }
for (k = 0; k <= first; k++) { for (k = 0; k <= first; k++) {
mask = ((1 << width) - 1) << ((first - k) * width); mask = (1 << width) - 1;
mask <<= (first - k) * width;
if ((*valuep & mask) == (value & mask)) if ((*valuep & mask) != (value & mask)) {
continue; sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
#if 0 }
printf("k = %d, first = %d, enum = %d, mask = 0x%08x\n",
k, first, enum_ids[k], (unsigned int)mask);
#endif
sh_intc_toggle_mask(desc, enum_ids[k], value & mask, 0);
} }
*valuep = value; *valuep = value;
#ifdef DEBUG_INTC
printf("sh_intc_write 0x%lx -> 0x%08x\n", (unsigned long) offset, value);
#endif
} }
static const MemoryRegionOps sh_intc_ops = { static const MemoryRegionOps sh_intc_ops = {
@ -292,20 +256,105 @@ static const MemoryRegionOps sh_intc_ops = {
.endianness = DEVICE_NATIVE_ENDIAN, .endianness = DEVICE_NATIVE_ENDIAN,
}; };
struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id) static void sh_intc_register_source(struct intc_desc *desc,
intc_enum source,
struct intc_group *groups,
int nr_groups)
{ {
if (id) unsigned int i, k;
return desc->sources + id; intc_enum id;
return NULL; if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = &desc->mask_regs[i];
for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
id = mr->enum_ids[k];
if (id && id == source) {
desc->sources[id].enable_max++;
}
}
}
}
if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = &desc->prio_regs[i];
for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
id = pr->enum_ids[k];
if (id && id == source) {
desc->sources[id].enable_max++;
}
}
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = &groups[i];
for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
id = gr->enum_ids[k];
if (id && id == source) {
desc->sources[id].enable_max++;
}
}
}
}
}
void sh_intc_register_sources(struct intc_desc *desc,
struct intc_vect *vectors,
int nr_vectors,
struct intc_group *groups,
int nr_groups)
{
unsigned int i, k;
intc_enum id;
struct intc_source *s;
for (i = 0; i < nr_vectors; i++) {
struct intc_vect *vect = &vectors[i];
sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
id = vect->enum_id;
if (id) {
s = &desc->sources[id];
s->vect = vect->vect;
trace_sh_intc_register("source", vect->enum_id, s->vect,
s->enable_count, s->enable_max);
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = &groups[i];
id = gr->enum_id;
s = &desc->sources[id];
s->next_enum_id = gr->enum_ids[0];
for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
if (gr->enum_ids[k]) {
id = gr->enum_ids[k - 1];
s = &desc->sources[id];
s->next_enum_id = gr->enum_ids[k];
}
}
trace_sh_intc_register("group", gr->enum_id, 0xffff,
s->enable_count, s->enable_max);
}
}
} }
static unsigned int sh_intc_register(MemoryRegion *sysmem, static unsigned int sh_intc_register(MemoryRegion *sysmem,
struct intc_desc *desc, struct intc_desc *desc,
const unsigned long address, const unsigned long address,
const char *type, const char *type,
const char *action, const char *action,
const unsigned int index) const unsigned int index)
{ {
char name[60]; char name[60];
MemoryRegion *iomem, *iomem_p4, *iomem_a7; MemoryRegion *iomem, *iomem_p4, *iomem_a7;
@ -315,132 +364,28 @@ static unsigned int sh_intc_register(MemoryRegion *sysmem,
} }
iomem = &desc->iomem; iomem = &desc->iomem;
iomem_p4 = desc->iomem_aliases + index; iomem_p4 = &desc->iomem_aliases[index];
iomem_a7 = iomem_p4 + 1; iomem_a7 = iomem_p4 + 1;
#define SH_INTC_IOMEM_FORMAT "interrupt-controller-%s-%s-%s" snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "p4");
snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "p4"); memory_region_init_alias(iomem_p4, NULL, name, iomem, A7ADDR(address), 4);
memory_region_init_alias(iomem_p4, NULL, name, iomem, INTC_A7(address), 4);
memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4); memory_region_add_subregion(sysmem, P4ADDR(address), iomem_p4);
snprintf(name, sizeof(name), SH_INTC_IOMEM_FORMAT, type, action, "a7"); snprintf(name, sizeof(name), "intc-%s-%s-%s", type, action, "a7");
memory_region_init_alias(iomem_a7, NULL, name, iomem, INTC_A7(address), 4); memory_region_init_alias(iomem_a7, NULL, name, iomem, A7ADDR(address), 4);
memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7); memory_region_add_subregion(sysmem, A7ADDR(address), iomem_a7);
#undef SH_INTC_IOMEM_FORMAT
/* used to increment aliases index */ /* used to increment aliases index */
return 2; return 2;
} }
static void sh_intc_register_source(struct intc_desc *desc,
intc_enum source,
struct intc_group *groups,
int nr_groups)
{
unsigned int i, k;
struct intc_source *s;
if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i;
for (k = 0; k < ARRAY_SIZE(mr->enum_ids); k++) {
if (mr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, mr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i;
for (k = 0; k < ARRAY_SIZE(pr->enum_ids); k++) {
if (pr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, pr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = groups + i;
for (k = 0; k < ARRAY_SIZE(gr->enum_ids); k++) {
if (gr->enum_ids[k] != source)
continue;
s = sh_intc_source(desc, gr->enum_ids[k]);
if (s)
s->enable_max++;
}
}
}
}
void sh_intc_register_sources(struct intc_desc *desc,
struct intc_vect *vectors,
int nr_vectors,
struct intc_group *groups,
int nr_groups)
{
unsigned int i, k;
struct intc_source *s;
for (i = 0; i < nr_vectors; i++) {
struct intc_vect *vect = vectors + i;
sh_intc_register_source(desc, vect->enum_id, groups, nr_groups);
s = sh_intc_source(desc, vect->enum_id);
if (s) {
s->vect = vect->vect;
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered source %d -> 0x%04x (%d/%d)\n",
vect->enum_id, s->vect, s->enable_count, s->enable_max);
#endif
}
}
if (groups) {
for (i = 0; i < nr_groups; i++) {
struct intc_group *gr = groups + i;
s = sh_intc_source(desc, gr->enum_id);
s->next_enum_id = gr->enum_ids[0];
for (k = 1; k < ARRAY_SIZE(gr->enum_ids); k++) {
if (!gr->enum_ids[k])
continue;
s = sh_intc_source(desc, gr->enum_ids[k - 1]);
s->next_enum_id = gr->enum_ids[k];
}
#ifdef DEBUG_INTC_SOURCES
printf("sh_intc: registered group %d (%d/%d)\n",
gr->enum_id, s->enable_count, s->enable_max);
#endif
}
}
}
int sh_intc_init(MemoryRegion *sysmem, int sh_intc_init(MemoryRegion *sysmem,
struct intc_desc *desc, struct intc_desc *desc,
int nr_sources, int nr_sources,
struct intc_mask_reg *mask_regs, struct intc_mask_reg *mask_regs,
int nr_mask_regs, int nr_mask_regs,
struct intc_prio_reg *prio_regs, struct intc_prio_reg *prio_regs,
int nr_prio_regs) int nr_prio_regs)
{ {
unsigned int i, j; unsigned int i, j;
@ -450,65 +395,55 @@ int sh_intc_init(MemoryRegion *sysmem,
desc->nr_mask_regs = nr_mask_regs; desc->nr_mask_regs = nr_mask_regs;
desc->prio_regs = prio_regs; desc->prio_regs = prio_regs;
desc->nr_prio_regs = nr_prio_regs; desc->nr_prio_regs = nr_prio_regs;
/* Allocate 4 MemoryRegions per register (2 actions * 2 aliases). /* Allocate 4 MemoryRegions per register (2 actions * 2 aliases) */
**/
desc->iomem_aliases = g_new0(MemoryRegion, desc->iomem_aliases = g_new0(MemoryRegion,
(nr_mask_regs + nr_prio_regs) * 4); (nr_mask_regs + nr_prio_regs) * 4);
desc->sources = g_new0(struct intc_source, nr_sources);
j = 0; for (i = 0; i < nr_sources; i++) {
i = sizeof(struct intc_source) * nr_sources; desc->sources[i].parent = desc;
desc->sources = g_malloc0(i);
for (i = 0; i < desc->nr_sources; i++) {
struct intc_source *source = desc->sources + i;
source->parent = desc;
} }
desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources); desc->irqs = qemu_allocate_irqs(sh_intc_set_irq, desc, nr_sources);
memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, "intc",
memory_region_init_io(&desc->iomem, NULL, &sh_intc_ops, desc, 0x100000000ULL);
"interrupt-controller", 0x100000000ULL); j = 0;
#define INT_REG_PARAMS(reg_struct, type, action, j) \
reg_struct->action##_reg, #type, #action, j
if (desc->mask_regs) { if (desc->mask_regs) {
for (i = 0; i < desc->nr_mask_regs; i++) { for (i = 0; i < desc->nr_mask_regs; i++) {
struct intc_mask_reg *mr = desc->mask_regs + i; struct intc_mask_reg *mr = &desc->mask_regs[i];
j += sh_intc_register(sysmem, desc, j += sh_intc_register(sysmem, desc, mr->set_reg, "mask", "set", j);
INT_REG_PARAMS(mr, mask, set, j)); j += sh_intc_register(sysmem, desc, mr->clr_reg, "mask", "clr", j);
j += sh_intc_register(sysmem, desc, }
INT_REG_PARAMS(mr, mask, clr, j));
}
} }
if (desc->prio_regs) { if (desc->prio_regs) {
for (i = 0; i < desc->nr_prio_regs; i++) { for (i = 0; i < desc->nr_prio_regs; i++) {
struct intc_prio_reg *pr = desc->prio_regs + i; struct intc_prio_reg *pr = &desc->prio_regs[i];
j += sh_intc_register(sysmem, desc, j += sh_intc_register(sysmem, desc, pr->set_reg, "prio", "set", j);
INT_REG_PARAMS(pr, prio, set, j)); j += sh_intc_register(sysmem, desc, pr->clr_reg, "prio", "clr", j);
j += sh_intc_register(sysmem, desc, }
INT_REG_PARAMS(pr, prio, clr, j));
}
} }
#undef INT_REG_PARAMS
return 0; return 0;
} }
/* Assert level <n> IRL interrupt. /*
0:deassert. 1:lowest priority,... 15:highest priority. */ * Assert level <n> IRL interrupt.
* 0:deassert. 1:lowest priority,... 15:highest priority
*/
void sh_intc_set_irl(void *opaque, int n, int level) void sh_intc_set_irl(void *opaque, int n, int level)
{ {
struct intc_source *s = opaque; struct intc_source *s = opaque;
int i, irl = level ^ 15; int i, irl = level ^ 15;
for (i = 0; (s = sh_intc_source(s->parent, s->next_enum_id)); i++) { intc_enum id = s->next_enum_id;
if (i == irl)
sh_intc_toggle_source(s, s->enable_count?0:1, s->asserted?0:1); for (i = 0; id; id = s->next_enum_id, i++) {
else s = &s->parent->sources[id];
if (s->asserted) if (i == irl) {
sh_intc_toggle_source(s, 0, -1); sh_intc_toggle_source(s, s->enable_count ? 0 : 1,
s->asserted ? 0 : 1);
} else if (s->asserted) {
sh_intc_toggle_source(s, 0, -1);
}
} }
} }

View File

@ -238,3 +238,11 @@ goldfish_pic_write(void *dev, int idx, unsigned int addr, unsigned int size, uin
goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d" goldfish_pic_reset(void *dev, int idx) "pic: %p goldfish-irq.%d"
goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d" goldfish_pic_realize(void *dev, int idx) "pic: %p goldfish-irq.%d"
goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq" goldfish_pic_instance_init(void *dev) "pic: %p goldfish-irq"
# sh_intc.c
sh_intc_sources(int p, int a, int c, int m, unsigned short v, const char *s1, const char *s2, const char *s3) "(%d/%d/%d/%d) interrupt source 0x%x %s%s%s"
sh_intc_pending(int p, unsigned short v) "(%d) returning interrupt source 0x%x"
sh_intc_register(const char *s, int id, unsigned short v, int c, int m) "%s %u -> 0x%04x (%d/%d)"
sh_intc_read(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " -> 0x%lx"
sh_intc_write(unsigned size, uint64_t offset, unsigned long val) "size %u 0x%" PRIx64 " <- 0x%lx"
sh_intc_set(int id, int enable) "setting interrupt group %d to %d"

View File

@ -49,13 +49,12 @@ struct SHPCIState {
uint32_t iobr; uint32_t iobr;
}; };
static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val, static void sh_pci_reg_write(void *p, hwaddr addr, uint64_t val, unsigned size)
unsigned size)
{ {
SHPCIState *pcic = p; SHPCIState *pcic = p;
PCIHostState *phb = PCI_HOST_BRIDGE(pcic); PCIHostState *phb = PCI_HOST_BRIDGE(pcic);
switch(addr) { switch (addr) {
case 0 ... 0xfc: case 0 ... 0xfc:
stl_le_p(pcic->dev->config + addr, val); stl_le_p(pcic->dev->config + addr, val);
break; break;
@ -75,13 +74,12 @@ static void sh_pci_reg_write (void *p, hwaddr addr, uint64_t val,
} }
} }
static uint64_t sh_pci_reg_read (void *p, hwaddr addr, static uint64_t sh_pci_reg_read(void *p, hwaddr addr, unsigned size)
unsigned size)
{ {
SHPCIState *pcic = p; SHPCIState *pcic = p;
PCIHostState *phb = PCI_HOST_BRIDGE(pcic); PCIHostState *phb = PCI_HOST_BRIDGE(pcic);
switch(addr) { switch (addr) {
case 0 ... 0xfc: case 0 ... 0xfc:
return ldl_le_p(pcic->dev->config + addr); return ldl_le_p(pcic->dev->config + addr);
case 0x1c0: case 0x1c0:

View File

@ -26,6 +26,7 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qemu/units.h" #include "qemu/units.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "qemu/error-report.h"
#include "cpu.h" #include "cpu.h"
#include "hw/sysbus.h" #include "hw/sysbus.h"
#include "hw/sh4/sh.h" #include "hw/sh4/sh.h"
@ -56,10 +57,10 @@
#define LINUX_LOAD_OFFSET 0x0800000 #define LINUX_LOAD_OFFSET 0x0800000
#define INITRD_LOAD_OFFSET 0x1800000 #define INITRD_LOAD_OFFSET 0x1800000
#define PA_IRLMSK 0x00 #define PA_IRLMSK 0x00
#define PA_POWOFF 0x30 #define PA_POWOFF 0x30
#define PA_VERREG 0x32 #define PA_VERREG 0x32
#define PA_OUTPORT 0x36 #define PA_OUTPORT 0x36
typedef struct { typedef struct {
uint16_t bcr; uint16_t bcr;
@ -96,38 +97,41 @@ enum r2d_fpga_irq {
}; };
static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = { static const struct { short irl; uint16_t msk; } irqtab[NR_IRQS] = {
[CF_IDE] = { 1, 1<<9 }, [CF_IDE] = { 1, 1 << 9 },
[CF_CD] = { 2, 1<<8 }, [CF_CD] = { 2, 1 << 8 },
[PCI_INTA] = { 9, 1<<14 }, [PCI_INTA] = { 9, 1 << 14 },
[PCI_INTB] = { 10, 1<<13 }, [PCI_INTB] = { 10, 1 << 13 },
[PCI_INTC] = { 3, 1<<12 }, [PCI_INTC] = { 3, 1 << 12 },
[PCI_INTD] = { 0, 1<<11 }, [PCI_INTD] = { 0, 1 << 11 },
[SM501] = { 4, 1<<10 }, [SM501] = { 4, 1 << 10 },
[KEY] = { 5, 1<<6 }, [KEY] = { 5, 1 << 6 },
[RTC_A] = { 6, 1<<5 }, [RTC_A] = { 6, 1 << 5 },
[RTC_T] = { 7, 1<<4 }, [RTC_T] = { 7, 1 << 4 },
[SDCARD] = { 8, 1<<7 }, [SDCARD] = { 8, 1 << 7 },
[EXT] = { 11, 1<<0 }, [EXT] = { 11, 1 << 0 },
[TP] = { 12, 1<<15 }, [TP] = { 12, 1 << 15 },
}; };
static void update_irl(r2d_fpga_t *fpga) static void update_irl(r2d_fpga_t *fpga)
{ {
int i, irl = 15; int i, irl = 15;
for (i = 0; i < NR_IRQS; i++) for (i = 0; i < NR_IRQS; i++) {
if (fpga->irlmon & fpga->irlmsk & irqtab[i].msk) if ((fpga->irlmon & fpga->irlmsk & irqtab[i].msk) &&
if (irqtab[i].irl < irl) irqtab[i].irl < irl) {
irl = irqtab[i].irl; irl = irqtab[i].irl;
}
}
qemu_set_irq(fpga->irl, irl ^ 15); qemu_set_irq(fpga->irl, irl ^ 15);
} }
static void r2d_fpga_irq_set(void *opaque, int n, int level) static void r2d_fpga_irq_set(void *opaque, int n, int level)
{ {
r2d_fpga_t *fpga = opaque; r2d_fpga_t *fpga = opaque;
if (level) if (level) {
fpga->irlmon |= irqtab[n].msk; fpga->irlmon |= irqtab[n].msk;
else } else {
fpga->irlmon &= ~irqtab[n].msk; fpga->irlmon &= ~irqtab[n].msk;
}
update_irl(fpga); update_irl(fpga);
} }
@ -306,7 +310,7 @@ static void r2d_init(MachineState *machine)
/* NIC: rtl8139 on-board, and 2 slots. */ /* NIC: rtl8139 on-board, and 2 slots. */
for (i = 0; i < nb_nics; i++) for (i = 0; i < nb_nics; i++)
pci_nic_init_nofail(&nd_table[i], pci_bus, pci_nic_init_nofail(&nd_table[i], pci_bus,
"rtl8139", i==0 ? "2" : NULL); "rtl8139", i == 0 ? "2" : NULL);
/* USB keyboard */ /* USB keyboard */
usb_create_simple(usb_bus_find(-1), "usb-kbd"); usb_create_simple(usb_bus_find(-1), "usb-kbd");
@ -321,8 +325,8 @@ static void r2d_init(MachineState *machine)
SDRAM_BASE + LINUX_LOAD_OFFSET, SDRAM_BASE + LINUX_LOAD_OFFSET,
INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET); INITRD_LOAD_OFFSET - LINUX_LOAD_OFFSET);
if (kernel_size < 0) { if (kernel_size < 0) {
fprintf(stderr, "qemu: could not load kernel '%s'\n", kernel_filename); error_report("qemu: could not load kernel '%s'", kernel_filename);
exit(1); exit(1);
} }
/* initialization which should be done by firmware */ /* initialization which should be done by firmware */
@ -330,7 +334,8 @@ static void r2d_init(MachineState *machine)
MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 SDRAM */ MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 SDRAM */
address_space_stw(&address_space_memory, SH7750_BCR2, 3 << (3 * 2), address_space_stw(&address_space_memory, SH7750_BCR2, 3 << (3 * 2),
MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 32bit */ MEMTXATTRS_UNSPECIFIED, NULL); /* cs3 32bit */
reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000; /* Start from P2 area */ /* Start from P2 area */
reset_info->vector = (SDRAM_BASE + LINUX_LOAD_OFFSET) | 0xa0000000;
} }
if (initrd_filename) { if (initrd_filename) {
@ -341,8 +346,8 @@ static void r2d_init(MachineState *machine)
SDRAM_SIZE - INITRD_LOAD_OFFSET); SDRAM_SIZE - INITRD_LOAD_OFFSET);
if (initrd_size < 0) { if (initrd_size < 0) {
fprintf(stderr, "qemu: could not load initrd '%s'\n", initrd_filename); error_report("qemu: could not load initrd '%s'", initrd_filename);
exit(1); exit(1);
} }
/* initialization which should be done by firmware */ /* initialization which should be done by firmware */
@ -352,8 +357,10 @@ static void r2d_init(MachineState *machine)
} }
if (kernel_cmdline) { if (kernel_cmdline) {
/* I see no evidence that this .kernel_cmdline buffer requires /*
NUL-termination, so using strncpy should be ok. */ * I see no evidence that this .kernel_cmdline buffer requires
* NUL-termination, so using strncpy should be ok.
*/
strncpy(boot_params.kernel_cmdline, kernel_cmdline, strncpy(boot_params.kernel_cmdline, kernel_cmdline,
sizeof(boot_params.kernel_cmdline)); sizeof(boot_params.kernel_cmdline));
} }

View File

@ -24,14 +24,19 @@
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h"
#include "hw/sysbus.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "hw/sh4/sh.h" #include "hw/sh4/sh.h"
#include "sysemu/sysemu.h" #include "sysemu/sysemu.h"
#include "hw/qdev-properties.h"
#include "hw/qdev-properties-system.h"
#include "sh7750_regs.h" #include "sh7750_regs.h"
#include "sh7750_regnames.h" #include "sh7750_regnames.h"
#include "hw/sh4/sh_intc.h" #include "hw/sh4/sh_intc.h"
#include "hw/timer/tmu012.h" #include "hw/timer/tmu012.h"
#include "exec/exec-all.h" #include "exec/exec-all.h"
#include "trace.h"
#define NB_DEVICES 4 #define NB_DEVICES 4
@ -60,17 +65,17 @@ typedef struct SH7750State {
uint16_t gpioic; uint16_t gpioic;
uint32_t pctra; uint32_t pctra;
uint32_t pctrb; uint32_t pctrb;
uint16_t portdira; /* Cached */ uint16_t portdira; /* Cached */
uint16_t portpullupa; /* Cached */ uint16_t portpullupa; /* Cached */
uint16_t portdirb; /* Cached */ uint16_t portdirb; /* Cached */
uint16_t portpullupb; /* Cached */ uint16_t portpullupb; /* Cached */
uint16_t pdtra; uint16_t pdtra;
uint16_t pdtrb; uint16_t pdtrb;
uint16_t periph_pdtra; /* Imposed by the peripherals */ uint16_t periph_pdtra; /* Imposed by the peripherals */
uint16_t periph_portdira; /* Direction seen from the peripherals */ uint16_t periph_portdira; /* Direction seen from the peripherals */
uint16_t periph_pdtrb; /* Imposed by the peripherals */ uint16_t periph_pdtrb; /* Imposed by the peripherals */
uint16_t periph_portdirb; /* Direction seen from the peripherals */ uint16_t periph_portdirb; /* Direction seen from the peripherals */
sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */ sh7750_io_device *devices[NB_DEVICES]; /* External peripherals */
/* Cache */ /* Cache */
uint32_t ccr; uint32_t ccr;
@ -78,143 +83,145 @@ typedef struct SH7750State {
struct intc_desc intc; struct intc_desc intc;
} SH7750State; } SH7750State;
static inline int has_bcr3_and_bcr4(SH7750State * s) static inline int has_bcr3_and_bcr4(SH7750State *s)
{ {
return s->cpu->env.features & SH_FEATURE_BCR3_AND_BCR4; return s->cpu->env.features & SH_FEATURE_BCR3_AND_BCR4;
} }
/**********************************************************************
I/O ports
**********************************************************************/
int sh7750_register_io_device(SH7750State * s, sh7750_io_device * device) /*
* I/O ports
*/
int sh7750_register_io_device(SH7750State *s, sh7750_io_device *device)
{ {
int i; int i;
for (i = 0; i < NB_DEVICES; i++) { for (i = 0; i < NB_DEVICES; i++) {
if (s->devices[i] == NULL) { if (s->devices[i] == NULL) {
s->devices[i] = device; s->devices[i] = device;
return 0; return 0;
} }
} }
return -1; return -1;
} }
static uint16_t portdir(uint32_t v) static uint16_t portdir(uint32_t v)
{ {
#define EVENPORTMASK(n) ((v & (1<<((n)<<1))) >> (n)) #define EVENPORTMASK(n) ((v & (1 << ((n) << 1))) >> (n))
return return
EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) | EVENPORTMASK(15) | EVENPORTMASK(14) | EVENPORTMASK(13) |
EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) | EVENPORTMASK(12) | EVENPORTMASK(11) | EVENPORTMASK(10) |
EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) | EVENPORTMASK(9) | EVENPORTMASK(8) | EVENPORTMASK(7) |
EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) | EVENPORTMASK(6) | EVENPORTMASK(5) | EVENPORTMASK(4) |
EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) | EVENPORTMASK(3) | EVENPORTMASK(2) | EVENPORTMASK(1) |
EVENPORTMASK(0); EVENPORTMASK(0);
} }
static uint16_t portpullup(uint32_t v) static uint16_t portpullup(uint32_t v)
{ {
#define ODDPORTMASK(n) ((v & (1<<(((n)<<1)+1))) >> (n)) #define ODDPORTMASK(n) ((v & (1 << (((n) << 1) + 1))) >> (n))
return return
ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) | ODDPORTMASK(15) | ODDPORTMASK(14) | ODDPORTMASK(13) |
ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) | ODDPORTMASK(12) | ODDPORTMASK(11) | ODDPORTMASK(10) |
ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) | ODDPORTMASK(9) | ODDPORTMASK(8) | ODDPORTMASK(7) | ODDPORTMASK(6) |
ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) | ODDPORTMASK(5) | ODDPORTMASK(4) | ODDPORTMASK(3) | ODDPORTMASK(2) |
ODDPORTMASK(1) | ODDPORTMASK(0); ODDPORTMASK(1) | ODDPORTMASK(0);
} }
static uint16_t porta_lines(SH7750State * s) static uint16_t porta_lines(SH7750State *s)
{ {
return (s->portdira & s->pdtra) | /* CPU */ return (s->portdira & s->pdtra) | /* CPU */
(s->periph_portdira & s->periph_pdtra) | /* Peripherals */ (s->periph_portdira & s->periph_pdtra) | /* Peripherals */
(~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */ (~(s->portdira | s->periph_portdira) & s->portpullupa); /* Pullups */
} }
static uint16_t portb_lines(SH7750State * s) static uint16_t portb_lines(SH7750State *s)
{ {
return (s->portdirb & s->pdtrb) | /* CPU */ return (s->portdirb & s->pdtrb) | /* CPU */
(s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */ (s->periph_portdirb & s->periph_pdtrb) | /* Peripherals */
(~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */ (~(s->portdirb | s->periph_portdirb) & s->portpullupb); /* Pullups */
} }
static void gen_port_interrupts(SH7750State * s) static void gen_port_interrupts(SH7750State *s)
{ {
/* XXXXX interrupts not generated */ /* XXXXX interrupts not generated */
} }
static void porta_changed(SH7750State * s, uint16_t prev) static void porta_changed(SH7750State *s, uint16_t prev)
{ {
uint16_t currenta, changes; uint16_t currenta, changes;
int i, r = 0; int i, r = 0;
#if 0
fprintf(stderr, "porta changed from 0x%04x to 0x%04x\n",
prev, porta_lines(s));
fprintf(stderr, "pdtra=0x%04x, pctra=0x%08x\n", s->pdtra, s->pctra);
#endif
currenta = porta_lines(s); currenta = porta_lines(s);
if (currenta == prev) if (currenta == prev) {
return; return;
}
trace_sh7750_porta(prev, currenta, s->pdtra, s->pctra);
changes = currenta ^ prev; changes = currenta ^ prev;
for (i = 0; i < NB_DEVICES; i++) { for (i = 0; i < NB_DEVICES; i++) {
if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) { if (s->devices[i] && (s->devices[i]->portamask_trigger & changes)) {
r |= s->devices[i]->port_change_cb(currenta, portb_lines(s), r |= s->devices[i]->port_change_cb(currenta, portb_lines(s),
&s->periph_pdtra, &s->periph_pdtra,
&s->periph_portdira, &s->periph_portdira,
&s->periph_pdtrb, &s->periph_pdtrb,
&s->periph_portdirb); &s->periph_portdirb);
} }
} }
if (r) if (r) {
gen_port_interrupts(s); gen_port_interrupts(s);
}
} }
static void portb_changed(SH7750State * s, uint16_t prev) static void portb_changed(SH7750State *s, uint16_t prev)
{ {
uint16_t currentb, changes; uint16_t currentb, changes;
int i, r = 0; int i, r = 0;
currentb = portb_lines(s); currentb = portb_lines(s);
if (currentb == prev) if (currentb == prev) {
return; return;
}
trace_sh7750_portb(prev, currentb, s->pdtrb, s->pctrb);
changes = currentb ^ prev; changes = currentb ^ prev;
for (i = 0; i < NB_DEVICES; i++) { for (i = 0; i < NB_DEVICES; i++) {
if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) { if (s->devices[i] && (s->devices[i]->portbmask_trigger & changes)) {
r |= s->devices[i]->port_change_cb(portb_lines(s), currentb, r |= s->devices[i]->port_change_cb(portb_lines(s), currentb,
&s->periph_pdtra, &s->periph_pdtra,
&s->periph_portdira, &s->periph_portdira,
&s->periph_pdtrb, &s->periph_pdtrb,
&s->periph_portdirb); &s->periph_portdirb);
} }
} }
if (r) if (r) {
gen_port_interrupts(s); gen_port_interrupts(s);
}
} }
/********************************************************************** /*
Memory * Memory
**********************************************************************/ */
static void error_access(const char *kind, hwaddr addr) static void error_access(const char *kind, hwaddr addr)
{ {
fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n", fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") not supported\n",
kind, regname(addr), addr); kind, regname(addr), addr);
} }
static void ignore_access(const char *kind, hwaddr addr) static void ignore_access(const char *kind, hwaddr addr)
{ {
fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n", fprintf(stderr, "%s to %s (0x" TARGET_FMT_plx ") ignored\n",
kind, regname(addr), addr); kind, regname(addr), addr);
} }
static uint32_t sh7750_mem_readb(void *opaque, hwaddr addr) static uint32_t sh7750_mem_readb(void *opaque, hwaddr addr)
{ {
switch (addr) { switch (addr) {
default: default:
error_access("byte read", addr); error_access("byte read", addr);
abort(); abort();
} }
} }
@ -225,30 +232,31 @@ static uint32_t sh7750_mem_readw(void *opaque, hwaddr addr)
switch (addr) { switch (addr) {
case SH7750_BCR2_A7: case SH7750_BCR2_A7:
return s->bcr2; return s->bcr2;
case SH7750_BCR3_A7: case SH7750_BCR3_A7:
if(!has_bcr3_and_bcr4(s)) if (!has_bcr3_and_bcr4(s)) {
error_access("word read", addr); error_access("word read", addr);
return s->bcr3; }
return s->bcr3;
case SH7750_FRQCR_A7: case SH7750_FRQCR_A7:
return 0; return 0;
case SH7750_PCR_A7: case SH7750_PCR_A7:
return s->pcr; return s->pcr;
case SH7750_RFCR_A7: case SH7750_RFCR_A7:
fprintf(stderr, fprintf(stderr,
"Read access to refresh count register, incrementing\n"); "Read access to refresh count register, incrementing\n");
return s->rfcr++; return s->rfcr++;
case SH7750_PDTRA_A7: case SH7750_PDTRA_A7:
return porta_lines(s); return porta_lines(s);
case SH7750_PDTRB_A7: case SH7750_PDTRB_A7:
return portb_lines(s); return portb_lines(s);
case SH7750_RTCOR_A7: case SH7750_RTCOR_A7:
case SH7750_RTCNT_A7: case SH7750_RTCNT_A7:
case SH7750_RTCSR_A7: case SH7750_RTCSR_A7:
ignore_access("word read", addr); ignore_access("word read", addr);
return 0; return 0;
default: default:
error_access("word read", addr); error_access("word read", addr);
abort(); abort();
} }
} }
@ -260,11 +268,12 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr)
switch (addr) { switch (addr) {
case SH7750_BCR1_A7: case SH7750_BCR1_A7:
return s->bcr1; return s->bcr1;
case SH7750_BCR4_A7: case SH7750_BCR4_A7:
if(!has_bcr3_and_bcr4(s)) if (!has_bcr3_and_bcr4(s)) {
error_access("long read", addr); error_access("long read", addr);
return s->bcr4; }
return s->bcr4;
case SH7750_WCR1_A7: case SH7750_WCR1_A7:
case SH7750_WCR2_A7: case SH7750_WCR2_A7:
case SH7750_WCR3_A7: case SH7750_WCR3_A7:
@ -288,31 +297,31 @@ static uint32_t sh7750_mem_readl(void *opaque, hwaddr addr)
case SH7750_INTEVT_A7: case SH7750_INTEVT_A7:
return s->cpu->env.intevt; return s->cpu->env.intevt;
case SH7750_CCR_A7: case SH7750_CCR_A7:
return s->ccr; return s->ccr;
case 0x1f000030: /* Processor version */ case 0x1f000030: /* Processor version */
scc = SUPERH_CPU_GET_CLASS(s->cpu); scc = SUPERH_CPU_GET_CLASS(s->cpu);
return scc->pvr; return scc->pvr;
case 0x1f000040: /* Cache version */ case 0x1f000040: /* Cache version */
scc = SUPERH_CPU_GET_CLASS(s->cpu); scc = SUPERH_CPU_GET_CLASS(s->cpu);
return scc->cvr; return scc->cvr;
case 0x1f000044: /* Processor revision */ case 0x1f000044: /* Processor revision */
scc = SUPERH_CPU_GET_CLASS(s->cpu); scc = SUPERH_CPU_GET_CLASS(s->cpu);
return scc->prr; return scc->prr;
default: default:
error_access("long read", addr); error_access("long read", addr);
abort(); abort();
} }
} }
#define is_in_sdrmx(a, x) (a >= SH7750_SDMR ## x ## _A7 \ #define is_in_sdrmx(a, x) (a >= SH7750_SDMR ## x ## _A7 \
&& a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB)) && a <= (SH7750_SDMR ## x ## _A7 + SH7750_SDMR ## x ## _REGNB))
static void sh7750_mem_writeb(void *opaque, hwaddr addr, static void sh7750_mem_writeb(void *opaque, hwaddr addr,
uint32_t mem_value) uint32_t mem_value)
{ {
if (is_in_sdrmx(addr, 2) || is_in_sdrmx(addr, 3)) { if (is_in_sdrmx(addr, 2) || is_in_sdrmx(addr, 3)) {
ignore_access("byte write", addr); ignore_access("byte write", addr);
return; return;
} }
error_access("byte write", addr); error_access("byte write", addr);
@ -320,94 +329,96 @@ static void sh7750_mem_writeb(void *opaque, hwaddr addr,
} }
static void sh7750_mem_writew(void *opaque, hwaddr addr, static void sh7750_mem_writew(void *opaque, hwaddr addr,
uint32_t mem_value) uint32_t mem_value)
{ {
SH7750State *s = opaque; SH7750State *s = opaque;
uint16_t temp; uint16_t temp;
switch (addr) { switch (addr) {
/* SDRAM controller */ /* SDRAM controller */
case SH7750_BCR2_A7: case SH7750_BCR2_A7:
s->bcr2 = mem_value; s->bcr2 = mem_value;
return; return;
case SH7750_BCR3_A7: case SH7750_BCR3_A7:
if(!has_bcr3_and_bcr4(s)) if (!has_bcr3_and_bcr4(s)) {
error_access("word write", addr); error_access("word write", addr);
s->bcr3 = mem_value; }
return; s->bcr3 = mem_value;
return;
case SH7750_PCR_A7: case SH7750_PCR_A7:
s->pcr = mem_value; s->pcr = mem_value;
return; return;
case SH7750_RTCNT_A7: case SH7750_RTCNT_A7:
case SH7750_RTCOR_A7: case SH7750_RTCOR_A7:
case SH7750_RTCSR_A7: case SH7750_RTCSR_A7:
ignore_access("word write", addr); ignore_access("word write", addr);
return; return;
/* IO ports */ /* IO ports */
case SH7750_PDTRA_A7: case SH7750_PDTRA_A7:
temp = porta_lines(s); temp = porta_lines(s);
s->pdtra = mem_value; s->pdtra = mem_value;
porta_changed(s, temp); porta_changed(s, temp);
return; return;
case SH7750_PDTRB_A7: case SH7750_PDTRB_A7:
temp = portb_lines(s); temp = portb_lines(s);
s->pdtrb = mem_value; s->pdtrb = mem_value;
portb_changed(s, temp); portb_changed(s, temp);
return; return;
case SH7750_RFCR_A7: case SH7750_RFCR_A7:
fprintf(stderr, "Write access to refresh count register\n"); fprintf(stderr, "Write access to refresh count register\n");
s->rfcr = mem_value; s->rfcr = mem_value;
return; return;
case SH7750_GPIOIC_A7: case SH7750_GPIOIC_A7:
s->gpioic = mem_value; s->gpioic = mem_value;
if (mem_value != 0) { if (mem_value != 0) {
fprintf(stderr, "I/O interrupts not implemented\n"); fprintf(stderr, "I/O interrupts not implemented\n");
abort(); abort();
} }
return; return;
default: default:
error_access("word write", addr); error_access("word write", addr);
abort(); abort();
} }
} }
static void sh7750_mem_writel(void *opaque, hwaddr addr, static void sh7750_mem_writel(void *opaque, hwaddr addr,
uint32_t mem_value) uint32_t mem_value)
{ {
SH7750State *s = opaque; SH7750State *s = opaque;
uint16_t temp; uint16_t temp;
switch (addr) { switch (addr) {
/* SDRAM controller */ /* SDRAM controller */
case SH7750_BCR1_A7: case SH7750_BCR1_A7:
s->bcr1 = mem_value; s->bcr1 = mem_value;
return; return;
case SH7750_BCR4_A7: case SH7750_BCR4_A7:
if(!has_bcr3_and_bcr4(s)) if (!has_bcr3_and_bcr4(s)) {
error_access("long write", addr); error_access("long write", addr);
s->bcr4 = mem_value; }
return; s->bcr4 = mem_value;
return;
case SH7750_WCR1_A7: case SH7750_WCR1_A7:
case SH7750_WCR2_A7: case SH7750_WCR2_A7:
case SH7750_WCR3_A7: case SH7750_WCR3_A7:
case SH7750_MCR_A7: case SH7750_MCR_A7:
ignore_access("long write", addr); ignore_access("long write", addr);
return; return;
/* IO ports */ /* IO ports */
case SH7750_PCTRA_A7: case SH7750_PCTRA_A7:
temp = porta_lines(s); temp = porta_lines(s);
s->pctra = mem_value; s->pctra = mem_value;
s->portdira = portdir(mem_value); s->portdira = portdir(mem_value);
s->portpullupa = portpullup(mem_value); s->portpullupa = portpullup(mem_value);
porta_changed(s, temp); porta_changed(s, temp);
return; return;
case SH7750_PCTRB_A7: case SH7750_PCTRB_A7:
temp = portb_lines(s); temp = portb_lines(s);
s->pctrb = mem_value; s->pctrb = mem_value;
s->portdirb = portdir(mem_value); s->portdirb = portdir(mem_value);
s->portpullupb = portpullup(mem_value); s->portpullupb = portpullup(mem_value);
portb_changed(s, temp); portb_changed(s, temp);
return; return;
case SH7750_MMUCR_A7: case SH7750_MMUCR_A7:
if (mem_value & MMUCR_TI) { if (mem_value & MMUCR_TI) {
cpu_sh4_invalidate_tlb(&s->cpu->env); cpu_sh4_invalidate_tlb(&s->cpu->env);
@ -443,10 +454,10 @@ static void sh7750_mem_writel(void *opaque, hwaddr addr,
s->cpu->env.intevt = mem_value & 0x000007ff; s->cpu->env.intevt = mem_value & 0x000007ff;
return; return;
case SH7750_CCR_A7: case SH7750_CCR_A7:
s->ccr = mem_value; s->ccr = mem_value;
return; return;
default: default:
error_access("long write", addr); error_access("long write", addr);
abort(); abort();
} }
} }
@ -491,161 +502,161 @@ static const MemoryRegionOps sh7750_mem_ops = {
.endianness = DEVICE_NATIVE_ENDIAN, .endianness = DEVICE_NATIVE_ENDIAN,
}; };
/* sh775x interrupt controller tables for sh_intc.c /*
* sh775x interrupt controller tables for sh_intc.c
* stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c * stolen from linux/arch/sh/kernel/cpu/sh4/setup-sh7750.c
*/ */
enum { enum {
UNUSED = 0, UNUSED = 0,
/* interrupt sources */ /* interrupt sources */
IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, IRL_7,
IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E,
IRL0, IRL1, IRL2, IRL3, IRL0, IRL1, IRL2, IRL3,
HUDI, GPIOI, HUDI, GPIOI,
DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, DMAC_DMTE3,
DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7, DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE6, DMAC_DMTE7,
DMAC_DMAE, DMAC_DMAE,
PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, PCIC0_PCISERR, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3, PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3,
TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI, TMU3, TMU4, TMU0, TMU1, TMU2_TUNI, TMU2_TICPI,
RTC_ATI, RTC_PRI, RTC_CUI, RTC_ATI, RTC_PRI, RTC_CUI,
SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI,
SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI,
WDT, WDT,
REF_RCMI, REF_ROVI, REF_RCMI, REF_ROVI,
/* interrupt groups */ /* interrupt groups */
DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF, DMAC, PCIC1, TMU2, RTC, SCI1, SCIF, REF,
/* irl bundle */ /* irl bundle */
IRL, IRL,
NR_SOURCES, NR_SOURCES,
}; };
static struct intc_vect vectors[] = { static struct intc_vect vectors[] = {
INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620), INTC_VECT(HUDI, 0x600), INTC_VECT(GPIOI, 0x620),
INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420), INTC_VECT(TMU0, 0x400), INTC_VECT(TMU1, 0x420),
INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460), INTC_VECT(TMU2_TUNI, 0x440), INTC_VECT(TMU2_TICPI, 0x460),
INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0), INTC_VECT(RTC_ATI, 0x480), INTC_VECT(RTC_PRI, 0x4a0),
INTC_VECT(RTC_CUI, 0x4c0), INTC_VECT(RTC_CUI, 0x4c0),
INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500), INTC_VECT(SCI1_ERI, 0x4e0), INTC_VECT(SCI1_RXI, 0x500),
INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540), INTC_VECT(SCI1_TXI, 0x520), INTC_VECT(SCI1_TEI, 0x540),
INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720), INTC_VECT(SCIF_ERI, 0x700), INTC_VECT(SCIF_RXI, 0x720),
INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760), INTC_VECT(SCIF_BRI, 0x740), INTC_VECT(SCIF_TXI, 0x760),
INTC_VECT(WDT, 0x560), INTC_VECT(WDT, 0x560),
INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0), INTC_VECT(REF_RCMI, 0x580), INTC_VECT(REF_ROVI, 0x5a0),
}; };
static struct intc_group groups[] = { static struct intc_group groups[] = {
INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI), INTC_GROUP(TMU2, TMU2_TUNI, TMU2_TICPI),
INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI), INTC_GROUP(RTC, RTC_ATI, RTC_PRI, RTC_CUI),
INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI), INTC_GROUP(SCI1, SCI1_ERI, SCI1_RXI, SCI1_TXI, SCI1_TEI),
INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI), INTC_GROUP(SCIF, SCIF_ERI, SCIF_RXI, SCIF_BRI, SCIF_TXI),
INTC_GROUP(REF, REF_RCMI, REF_ROVI), INTC_GROUP(REF, REF_RCMI, REF_ROVI),
}; };
static struct intc_prio_reg prio_registers[] = { static struct intc_prio_reg prio_registers[] = {
{ 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } }, { 0xffd00004, 0, 16, 4, /* IPRA */ { TMU0, TMU1, TMU2, RTC } },
{ 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } }, { 0xffd00008, 0, 16, 4, /* IPRB */ { WDT, REF, SCI1, 0 } },
{ 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } }, { 0xffd0000c, 0, 16, 4, /* IPRC */ { GPIOI, DMAC, SCIF, HUDI } },
{ 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } }, { 0xffd00010, 0, 16, 4, /* IPRD */ { IRL0, IRL1, IRL2, IRL3 } },
{ 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, { 0xfe080000, 0, 32, 4, /* INTPRI00 */ { 0, 0, 0, 0, TMU4, TMU3,
TMU4, TMU3, PCIC1, PCIC0_PCISERR } },
PCIC1, PCIC0_PCISERR } },
}; };
/* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */ /* SH7750, SH7750S, SH7751 and SH7091 all have 4-channel DMA controllers */
static struct intc_vect vectors_dma4[] = { static struct intc_vect vectors_dma4[] = {
INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
INTC_VECT(DMAC_DMAE, 0x6c0), INTC_VECT(DMAC_DMAE, 0x6c0),
}; };
static struct intc_group groups_dma4[] = { static struct intc_group groups_dma4[] = {
INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
DMAC_DMTE3, DMAC_DMAE), DMAC_DMTE3, DMAC_DMAE),
}; };
/* SH7750R and SH7751R both have 8-channel DMA controllers */ /* SH7750R and SH7751R both have 8-channel DMA controllers */
static struct intc_vect vectors_dma8[] = { static struct intc_vect vectors_dma8[] = {
INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660), INTC_VECT(DMAC_DMTE0, 0x640), INTC_VECT(DMAC_DMTE1, 0x660),
INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0), INTC_VECT(DMAC_DMTE2, 0x680), INTC_VECT(DMAC_DMTE3, 0x6a0),
INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0), INTC_VECT(DMAC_DMTE4, 0x780), INTC_VECT(DMAC_DMTE5, 0x7a0),
INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0), INTC_VECT(DMAC_DMTE6, 0x7c0), INTC_VECT(DMAC_DMTE7, 0x7e0),
INTC_VECT(DMAC_DMAE, 0x6c0), INTC_VECT(DMAC_DMAE, 0x6c0),
}; };
static struct intc_group groups_dma8[] = { static struct intc_group groups_dma8[] = {
INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2, INTC_GROUP(DMAC, DMAC_DMTE0, DMAC_DMTE1, DMAC_DMTE2,
DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5, DMAC_DMTE3, DMAC_DMTE4, DMAC_DMTE5,
DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE), DMAC_DMTE6, DMAC_DMTE7, DMAC_DMAE),
}; };
/* SH7750R, SH7751 and SH7751R all have two extra timer channels */ /* SH7750R, SH7751 and SH7751R all have two extra timer channels */
static struct intc_vect vectors_tmu34[] = { static struct intc_vect vectors_tmu34[] = {
INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80), INTC_VECT(TMU3, 0xb00), INTC_VECT(TMU4, 0xb80),
}; };
static struct intc_mask_reg mask_registers[] = { static struct intc_mask_reg mask_registers[] = {
{ 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */ { 0xfe080040, 0xfe080060, 32, /* INTMSK00 / INTMSKCLR00 */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, TMU4, TMU3, 0, 0, 0, 0, 0, 0, TMU4, TMU3,
PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2,
PCIC1_PCIDMA3, PCIC0_PCISERR } }, PCIC1_PCIDMA3, PCIC0_PCISERR } },
}; };
/* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */ /* SH7750S, SH7750R, SH7751 and SH7751R all have IRLM priority registers */
static struct intc_vect vectors_irlm[] = { static struct intc_vect vectors_irlm[] = {
INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0), INTC_VECT(IRL0, 0x240), INTC_VECT(IRL1, 0x2a0),
INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360), INTC_VECT(IRL2, 0x300), INTC_VECT(IRL3, 0x360),
}; };
/* SH7751 and SH7751R both have PCI */ /* SH7751 and SH7751R both have PCI */
static struct intc_vect vectors_pci[] = { static struct intc_vect vectors_pci[] = {
INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0), INTC_VECT(PCIC0_PCISERR, 0xa00), INTC_VECT(PCIC1_PCIERR, 0xae0),
INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0), INTC_VECT(PCIC1_PCIPWDWN, 0xac0), INTC_VECT(PCIC1_PCIPWON, 0xaa0),
INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60), INTC_VECT(PCIC1_PCIDMA0, 0xa80), INTC_VECT(PCIC1_PCIDMA1, 0xa60),
INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20), INTC_VECT(PCIC1_PCIDMA2, 0xa40), INTC_VECT(PCIC1_PCIDMA3, 0xa20),
}; };
static struct intc_group groups_pci[] = { static struct intc_group groups_pci[] = {
INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON, INTC_GROUP(PCIC1, PCIC1_PCIERR, PCIC1_PCIPWDWN, PCIC1_PCIPWON,
PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3), PCIC1_PCIDMA0, PCIC1_PCIDMA1, PCIC1_PCIDMA2, PCIC1_PCIDMA3),
}; };
static struct intc_vect vectors_irl[] = { static struct intc_vect vectors_irl[] = {
INTC_VECT(IRL_0, 0x200), INTC_VECT(IRL_0, 0x200),
INTC_VECT(IRL_1, 0x220), INTC_VECT(IRL_1, 0x220),
INTC_VECT(IRL_2, 0x240), INTC_VECT(IRL_2, 0x240),
INTC_VECT(IRL_3, 0x260), INTC_VECT(IRL_3, 0x260),
INTC_VECT(IRL_4, 0x280), INTC_VECT(IRL_4, 0x280),
INTC_VECT(IRL_5, 0x2a0), INTC_VECT(IRL_5, 0x2a0),
INTC_VECT(IRL_6, 0x2c0), INTC_VECT(IRL_6, 0x2c0),
INTC_VECT(IRL_7, 0x2e0), INTC_VECT(IRL_7, 0x2e0),
INTC_VECT(IRL_8, 0x300), INTC_VECT(IRL_8, 0x300),
INTC_VECT(IRL_9, 0x320), INTC_VECT(IRL_9, 0x320),
INTC_VECT(IRL_A, 0x340), INTC_VECT(IRL_A, 0x340),
INTC_VECT(IRL_B, 0x360), INTC_VECT(IRL_B, 0x360),
INTC_VECT(IRL_C, 0x380), INTC_VECT(IRL_C, 0x380),
INTC_VECT(IRL_D, 0x3a0), INTC_VECT(IRL_D, 0x3a0),
INTC_VECT(IRL_E, 0x3c0), INTC_VECT(IRL_E, 0x3c0),
}; };
static struct intc_group groups_irl[] = { static struct intc_group groups_irl[] = {
INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6, INTC_GROUP(IRL, IRL_0, IRL_1, IRL_2, IRL_3, IRL_4, IRL_5, IRL_6,
IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E), IRL_7, IRL_8, IRL_9, IRL_A, IRL_B, IRL_C, IRL_D, IRL_E),
}; };
/********************************************************************** /*
Memory mapped cache and TLB * Memory mapped cache and TLB
**********************************************************************/ */
#define MM_REGION_MASK 0x07000000 #define MM_REGION_MASK 0x07000000
#define MM_ICACHE_ADDR (0) #define MM_ICACHE_ADDR (0)
@ -679,7 +690,7 @@ static uint64_t sh7750_mmct_read(void *opaque, hwaddr addr,
case MM_ICACHE_ADDR: case MM_ICACHE_ADDR:
case MM_ICACHE_DATA: case MM_ICACHE_DATA:
/* do nothing */ /* do nothing */
break; break;
case MM_ITLB_ADDR: case MM_ITLB_ADDR:
ret = cpu_sh4_read_mmaped_itlb_addr(&s->cpu->env, addr); ret = cpu_sh4_read_mmaped_itlb_addr(&s->cpu->env, addr);
break; break;
@ -689,7 +700,7 @@ static uint64_t sh7750_mmct_read(void *opaque, hwaddr addr,
case MM_OCACHE_ADDR: case MM_OCACHE_ADDR:
case MM_OCACHE_DATA: case MM_OCACHE_DATA:
/* do nothing */ /* do nothing */
break; break;
case MM_UTLB_ADDR: case MM_UTLB_ADDR:
ret = cpu_sh4_read_mmaped_utlb_addr(&s->cpu->env, addr); ret = cpu_sh4_read_mmaped_utlb_addr(&s->cpu->env, addr);
break; break;
@ -722,27 +733,27 @@ static void sh7750_mmct_write(void *opaque, hwaddr addr,
case MM_ICACHE_ADDR: case MM_ICACHE_ADDR:
case MM_ICACHE_DATA: case MM_ICACHE_DATA:
/* do nothing */ /* do nothing */
break; break;
case MM_ITLB_ADDR: case MM_ITLB_ADDR:
cpu_sh4_write_mmaped_itlb_addr(&s->cpu->env, addr, mem_value); cpu_sh4_write_mmaped_itlb_addr(&s->cpu->env, addr, mem_value);
break; break;
case MM_ITLB_DATA: case MM_ITLB_DATA:
cpu_sh4_write_mmaped_itlb_data(&s->cpu->env, addr, mem_value); cpu_sh4_write_mmaped_itlb_data(&s->cpu->env, addr, mem_value);
abort(); abort();
break; break;
case MM_OCACHE_ADDR: case MM_OCACHE_ADDR:
case MM_OCACHE_DATA: case MM_OCACHE_DATA:
/* do nothing */ /* do nothing */
break; break;
case MM_UTLB_ADDR: case MM_UTLB_ADDR:
cpu_sh4_write_mmaped_utlb_addr(&s->cpu->env, addr, mem_value); cpu_sh4_write_mmaped_utlb_addr(&s->cpu->env, addr, mem_value);
break; break;
case MM_UTLB_DATA: case MM_UTLB_DATA:
cpu_sh4_write_mmaped_utlb_data(&s->cpu->env, addr, mem_value); cpu_sh4_write_mmaped_utlb_data(&s->cpu->env, addr, mem_value);
break; break;
default: default:
abort(); abort();
break; break;
} }
} }
@ -755,10 +766,13 @@ static const MemoryRegionOps sh7750_mmct_ops = {
SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem) SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
{ {
SH7750State *s; SH7750State *s;
DeviceState *dev;
SysBusDevice *sb;
MemoryRegion *mr, *alias;
s = g_malloc0(sizeof(SH7750State)); s = g_malloc0(sizeof(SH7750State));
s->cpu = cpu; s->cpu = cpu;
s->periph_freq = 60000000; /* 60MHz */ s->periph_freq = 60000000; /* 60MHz */
memory_region_init_io(&s->iomem, NULL, &sh7750_mem_ops, s, memory_region_init_io(&s->iomem, NULL, &sh7750_mem_ops, s,
"memory", 0x1fc01000); "memory", 0x1fc01000);
@ -791,81 +805,100 @@ SH7750State *sh7750_init(SuperHCPU *cpu, MemoryRegion *sysmem)
memory_region_add_subregion(sysmem, 0xf0000000, &s->mmct_iomem); memory_region_add_subregion(sysmem, 0xf0000000, &s->mmct_iomem);
sh_intc_init(sysmem, &s->intc, NR_SOURCES, sh_intc_init(sysmem, &s->intc, NR_SOURCES,
_INTC_ARRAY(mask_registers), _INTC_ARRAY(mask_registers),
_INTC_ARRAY(prio_registers)); _INTC_ARRAY(prio_registers));
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors), _INTC_ARRAY(vectors),
_INTC_ARRAY(groups)); _INTC_ARRAY(groups));
cpu->env.intc_handle = &s->intc; cpu->env.intc_handle = &s->intc;
sh_serial_init(sysmem, 0x1fe00000, /* SCI */
0, s->periph_freq, serial_hd(0), dev = qdev_new(TYPE_SH_SERIAL);
s->intc.irqs[SCI1_ERI], dev->id = g_strdup("sci");
s->intc.irqs[SCI1_RXI], qdev_prop_set_chr(dev, "chardev", serial_hd(0));
s->intc.irqs[SCI1_TXI], sb = SYS_BUS_DEVICE(dev);
s->intc.irqs[SCI1_TEI], sysbus_realize_and_unref(sb, &error_fatal);
NULL); sysbus_mmio_map(sb, 0, 0xffe00000);
sh_serial_init(sysmem, 0x1fe80000, alias = g_malloc(sizeof(*alias));
SH_SERIAL_FEAT_SCIF, mr = sysbus_mmio_get_region(sb, 0);
s->periph_freq, serial_hd(1), memory_region_init_alias(alias, OBJECT(dev), "sci-a7", mr,
s->intc.irqs[SCIF_ERI], 0, memory_region_size(mr));
s->intc.irqs[SCIF_RXI], memory_region_add_subregion(sysmem, A7ADDR(0xffe00000), alias);
s->intc.irqs[SCIF_TXI], qdev_connect_gpio_out_named(dev, "eri", 0, s->intc.irqs[SCI1_ERI]);
NULL, qdev_connect_gpio_out_named(dev, "rxi", 0, s->intc.irqs[SCI1_RXI]);
s->intc.irqs[SCIF_BRI]); qdev_connect_gpio_out_named(dev, "txi", 0, s->intc.irqs[SCI1_TXI]);
qdev_connect_gpio_out_named(dev, "tei", 0, s->intc.irqs[SCI1_TEI]);
/* SCIF */
dev = qdev_new(TYPE_SH_SERIAL);
dev->id = g_strdup("scif");
qdev_prop_set_chr(dev, "chardev", serial_hd(1));
qdev_prop_set_uint8(dev, "features", SH_SERIAL_FEAT_SCIF);
sb = SYS_BUS_DEVICE(dev);
sysbus_realize_and_unref(sb, &error_fatal);
sysbus_mmio_map(sb, 0, 0xffe80000);
alias = g_malloc(sizeof(*alias));
mr = sysbus_mmio_get_region(sb, 0);
memory_region_init_alias(alias, OBJECT(dev), "scif-a7", mr,
0, memory_region_size(mr));
memory_region_add_subregion(sysmem, A7ADDR(0xffe80000), alias);
qdev_connect_gpio_out_named(dev, "eri", 0, s->intc.irqs[SCIF_ERI]);
qdev_connect_gpio_out_named(dev, "rxi", 0, s->intc.irqs[SCIF_RXI]);
qdev_connect_gpio_out_named(dev, "txi", 0, s->intc.irqs[SCIF_TXI]);
qdev_connect_gpio_out_named(dev, "bri", 0, s->intc.irqs[SCIF_BRI]);
tmu012_init(sysmem, 0x1fd80000, tmu012_init(sysmem, 0x1fd80000,
TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK, TMU012_FEAT_TOCR | TMU012_FEAT_3CHAN | TMU012_FEAT_EXTCLK,
s->periph_freq, s->periph_freq,
s->intc.irqs[TMU0], s->intc.irqs[TMU0],
s->intc.irqs[TMU1], s->intc.irqs[TMU1],
s->intc.irqs[TMU2_TUNI], s->intc.irqs[TMU2_TUNI],
s->intc.irqs[TMU2_TICPI]); s->intc.irqs[TMU2_TICPI]);
if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) { if (cpu->env.id & (SH_CPU_SH7750 | SH_CPU_SH7750S | SH_CPU_SH7751)) {
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_dma4), _INTC_ARRAY(vectors_dma4),
_INTC_ARRAY(groups_dma4)); _INTC_ARRAY(groups_dma4));
} }
if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751R)) { if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751R)) {
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_dma8), _INTC_ARRAY(vectors_dma8),
_INTC_ARRAY(groups_dma8)); _INTC_ARRAY(groups_dma8));
} }
if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) { if (cpu->env.id & (SH_CPU_SH7750R | SH_CPU_SH7751 | SH_CPU_SH7751R)) {
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_tmu34), _INTC_ARRAY(vectors_tmu34),
NULL, 0); NULL, 0);
tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq, tmu012_init(sysmem, 0x1e100000, 0, s->periph_freq,
s->intc.irqs[TMU3], s->intc.irqs[TMU3],
s->intc.irqs[TMU4], s->intc.irqs[TMU4],
NULL, NULL); NULL, NULL);
} }
if (cpu->env.id & (SH_CPU_SH7751_ALL)) { if (cpu->env.id & (SH_CPU_SH7751_ALL)) {
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_pci), _INTC_ARRAY(vectors_pci),
_INTC_ARRAY(groups_pci)); _INTC_ARRAY(groups_pci));
} }
if (cpu->env.id & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) { if (cpu->env.id & (SH_CPU_SH7750S | SH_CPU_SH7750R | SH_CPU_SH7751_ALL)) {
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_irlm), _INTC_ARRAY(vectors_irlm),
NULL, 0); NULL, 0);
} }
sh_intc_register_sources(&s->intc, sh_intc_register_sources(&s->intc,
_INTC_ARRAY(vectors_irl), _INTC_ARRAY(vectors_irl),
_INTC_ARRAY(groups_irl)); _INTC_ARRAY(groups_irl));
return s; return s;
} }
qemu_irq sh7750_irl(SH7750State *s) qemu_irq sh7750_irl(SH7750State *s)
{ {
sh_intc_toggle_source(sh_intc_source(&s->intc, IRL), 1, 0); /* enable */ sh_intc_toggle_source(&s->intc.sources[IRL], 1, 0); /* enable */
return qemu_allocate_irq(sh_intc_set_irl, sh_intc_source(&s->intc, IRL), 0); return qemu_allocate_irq(sh_intc_set_irl, &s->intc.sources[IRL], 0);
} }

View File

@ -12,85 +12,87 @@ typedef struct {
static regname_t regnames[] = { static regname_t regnames[] = {
REGNAME(SH7750_PTEH_A7) REGNAME(SH7750_PTEH_A7)
REGNAME(SH7750_PTEL_A7) REGNAME(SH7750_PTEL_A7)
REGNAME(SH7750_PTEA_A7) REGNAME(SH7750_PTEA_A7)
REGNAME(SH7750_TTB_A7) REGNAME(SH7750_TTB_A7)
REGNAME(SH7750_TEA_A7) REGNAME(SH7750_TEA_A7)
REGNAME(SH7750_MMUCR_A7) REGNAME(SH7750_MMUCR_A7)
REGNAME(SH7750_CCR_A7) REGNAME(SH7750_CCR_A7)
REGNAME(SH7750_QACR0_A7) REGNAME(SH7750_QACR0_A7)
REGNAME(SH7750_QACR1_A7) REGNAME(SH7750_QACR1_A7)
REGNAME(SH7750_TRA_A7) REGNAME(SH7750_TRA_A7)
REGNAME(SH7750_EXPEVT_A7) REGNAME(SH7750_EXPEVT_A7)
REGNAME(SH7750_INTEVT_A7) REGNAME(SH7750_INTEVT_A7)
REGNAME(SH7750_STBCR_A7) REGNAME(SH7750_STBCR_A7)
REGNAME(SH7750_STBCR2_A7) REGNAME(SH7750_STBCR2_A7)
REGNAME(SH7750_FRQCR_A7) REGNAME(SH7750_FRQCR_A7)
REGNAME(SH7750_WTCNT_A7) REGNAME(SH7750_WTCNT_A7)
REGNAME(SH7750_WTCSR_A7) REGNAME(SH7750_WTCSR_A7)
REGNAME(SH7750_R64CNT_A7) REGNAME(SH7750_R64CNT_A7)
REGNAME(SH7750_RSECCNT_A7) REGNAME(SH7750_RSECCNT_A7)
REGNAME(SH7750_RMINCNT_A7) REGNAME(SH7750_RMINCNT_A7)
REGNAME(SH7750_RHRCNT_A7) REGNAME(SH7750_RHRCNT_A7)
REGNAME(SH7750_RWKCNT_A7) REGNAME(SH7750_RWKCNT_A7)
REGNAME(SH7750_RDAYCNT_A7) REGNAME(SH7750_RDAYCNT_A7)
REGNAME(SH7750_RMONCNT_A7) REGNAME(SH7750_RMONCNT_A7)
REGNAME(SH7750_RYRCNT_A7) REGNAME(SH7750_RYRCNT_A7)
REGNAME(SH7750_RSECAR_A7) REGNAME(SH7750_RSECAR_A7)
REGNAME(SH7750_RMINAR_A7) REGNAME(SH7750_RMINAR_A7)
REGNAME(SH7750_RHRAR_A7) REGNAME(SH7750_RHRAR_A7)
REGNAME(SH7750_RWKAR_A7) REGNAME(SH7750_RWKAR_A7)
REGNAME(SH7750_RDAYAR_A7) REGNAME(SH7750_RDAYAR_A7)
REGNAME(SH7750_RMONAR_A7) REGNAME(SH7750_RMONAR_A7)
REGNAME(SH7750_RCR1_A7) REGNAME(SH7750_RCR1_A7)
REGNAME(SH7750_RCR2_A7) REGNAME(SH7750_RCR2_A7)
REGNAME(SH7750_BCR1_A7) REGNAME(SH7750_BCR1_A7)
REGNAME(SH7750_BCR2_A7) REGNAME(SH7750_BCR2_A7)
REGNAME(SH7750_WCR1_A7) REGNAME(SH7750_WCR1_A7)
REGNAME(SH7750_WCR2_A7) REGNAME(SH7750_WCR2_A7)
REGNAME(SH7750_WCR3_A7) REGNAME(SH7750_WCR3_A7)
REGNAME(SH7750_MCR_A7) REGNAME(SH7750_MCR_A7)
REGNAME(SH7750_PCR_A7) REGNAME(SH7750_PCR_A7)
REGNAME(SH7750_RTCSR_A7) REGNAME(SH7750_RTCSR_A7)
REGNAME(SH7750_RTCNT_A7) REGNAME(SH7750_RTCNT_A7)
REGNAME(SH7750_RTCOR_A7) REGNAME(SH7750_RTCOR_A7)
REGNAME(SH7750_RFCR_A7) REGNAME(SH7750_RFCR_A7)
REGNAME(SH7750_SAR0_A7) REGNAME(SH7750_SAR0_A7)
REGNAME(SH7750_SAR1_A7) REGNAME(SH7750_SAR1_A7)
REGNAME(SH7750_SAR2_A7) REGNAME(SH7750_SAR2_A7)
REGNAME(SH7750_SAR3_A7) REGNAME(SH7750_SAR3_A7)
REGNAME(SH7750_DAR0_A7) REGNAME(SH7750_DAR0_A7)
REGNAME(SH7750_DAR1_A7) REGNAME(SH7750_DAR1_A7)
REGNAME(SH7750_DAR2_A7) REGNAME(SH7750_DAR2_A7)
REGNAME(SH7750_DAR3_A7) REGNAME(SH7750_DAR3_A7)
REGNAME(SH7750_DMATCR0_A7) REGNAME(SH7750_DMATCR0_A7)
REGNAME(SH7750_DMATCR1_A7) REGNAME(SH7750_DMATCR1_A7)
REGNAME(SH7750_DMATCR2_A7) REGNAME(SH7750_DMATCR2_A7)
REGNAME(SH7750_DMATCR3_A7) REGNAME(SH7750_DMATCR3_A7)
REGNAME(SH7750_CHCR0_A7) REGNAME(SH7750_CHCR0_A7)
REGNAME(SH7750_CHCR1_A7) REGNAME(SH7750_CHCR1_A7)
REGNAME(SH7750_CHCR2_A7) REGNAME(SH7750_CHCR2_A7)
REGNAME(SH7750_CHCR3_A7) REGNAME(SH7750_CHCR3_A7)
REGNAME(SH7750_DMAOR_A7) REGNAME(SH7750_DMAOR_A7)
REGNAME(SH7750_PCTRA_A7) REGNAME(SH7750_PCTRA_A7)
REGNAME(SH7750_PDTRA_A7) REGNAME(SH7750_PDTRA_A7)
REGNAME(SH7750_PCTRB_A7) REGNAME(SH7750_PCTRB_A7)
REGNAME(SH7750_PDTRB_A7) REGNAME(SH7750_PDTRB_A7)
REGNAME(SH7750_GPIOIC_A7) REGNAME(SH7750_GPIOIC_A7)
REGNAME(SH7750_ICR_A7) REGNAME(SH7750_ICR_A7)
REGNAME(SH7750_BCR3_A7) REGNAME(SH7750_BCR3_A7)
REGNAME(SH7750_BCR4_A7) REGNAME(SH7750_BCR4_A7)
REGNAME(SH7750_SDMR2_A7) REGNAME(SH7750_SDMR2_A7)
REGNAME(SH7750_SDMR3_A7) {(uint32_t) - 1, NULL} REGNAME(SH7750_SDMR3_A7)
{ (uint32_t)-1, NULL }
}; };
const char *regname(uint32_t addr) const char *regname(uint32_t addr)
{ {
unsigned int i; unsigned int i;
for (i = 0; regnames[i].regaddr != (uint32_t) - 1; i++) { for (i = 0; regnames[i].regaddr != (uint32_t)-1; i++) {
if (regnames[i].regaddr == addr) if (regnames[i].regaddr == addr) {
return regnames[i].regname; return regnames[i].regname;
}
} }
return "<unknown reg>"; return "<unknown reg>";

File diff suppressed because it is too large Load Diff

View File

@ -22,11 +22,11 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
/* /*
Shix 2.0 board by Alexis Polti, described at * Shix 2.0 board by Alexis Polti, described at
https://web.archive.org/web/20070917001736/perso.enst.fr/~polti/realisations/shix20 * https://web.archive.org/web/20070917001736/perso.enst.fr/~polti/realisations/shix20
*
More information in target/sh4/README.sh4 * More information in target/sh4/README.sh4
*/ */
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "qapi/error.h" #include "qapi/error.h"
#include "cpu.h" #include "cpu.h"
@ -48,7 +48,7 @@ static void shix_init(MachineState *machine)
MemoryRegion *rom = g_new(MemoryRegion, 1); MemoryRegion *rom = g_new(MemoryRegion, 1);
MemoryRegion *sdram = g_new(MemoryRegion, 2); MemoryRegion *sdram = g_new(MemoryRegion, 2);
const char *bios_name = machine->firmware ?: BIOS_FILENAME; const char *bios_name = machine->firmware ?: BIOS_FILENAME;
cpu = SUPERH_CPU(cpu_create(machine->cpu_type)); cpu = SUPERH_CPU(cpu_create(machine->cpu_type));
/* Allocate memory space */ /* Allocate memory space */

3
hw/sh4/trace-events Normal file
View File

@ -0,0 +1,3 @@
# sh7750.c
sh7750_porta(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "porta changed from 0x%04x to 0x%04x\npdtra=0x%04x, pctra=0x%08x"
sh7750_portb(uint16_t prev, uint16_t cur, uint16_t pdtr, uint16_t pctr) "portb changed from 0x%04x to 0x%04x\npdtrb=0x%04x, pctrb=0x%08x"

1
hw/sh4/trace.h Normal file
View File

@ -0,0 +1 @@
#include "trace/trace-hw_sh4.h"

View File

@ -10,13 +10,12 @@
#include "qemu/osdep.h" #include "qemu/osdep.h"
#include "exec/memory.h" #include "exec/memory.h"
#include "hw/hw.h" #include "qemu/log.h"
#include "hw/irq.h" #include "hw/irq.h"
#include "hw/sh4/sh.h" #include "hw/sh4/sh.h"
#include "hw/timer/tmu012.h" #include "hw/timer/tmu012.h"
#include "hw/ptimer.h" #include "hw/ptimer.h"
#include "trace.h"
//#define DEBUG_TIMER
#define TIMER_TCR_TPSC (7 << 0) #define TIMER_TCR_TPSC (7 << 0)
#define TIMER_TCR_CKEG (3 << 3) #define TIMER_TCR_CKEG (3 << 3)
@ -46,24 +45,24 @@ typedef struct {
int feat; int feat;
int enabled; int enabled;
qemu_irq irq; qemu_irq irq;
} sh_timer_state; } SHTimerState;
/* Check all active timers, and schedule the next timer interrupt. */ /* Check all active timers, and schedule the next timer interrupt. */
static void sh_timer_update(sh_timer_state *s) static void sh_timer_update(SHTimerState *s)
{ {
int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE); int new_level = s->int_level && (s->tcr & TIMER_TCR_UNIE);
if (new_level != s->old_level) if (new_level != s->old_level) {
qemu_set_irq (s->irq, new_level); qemu_set_irq(s->irq, new_level);
}
s->old_level = s->int_level; s->old_level = s->int_level;
s->int_level = new_level; s->int_level = new_level;
} }
static uint32_t sh_timer_read(void *opaque, hwaddr offset) static uint32_t sh_timer_read(void *opaque, hwaddr offset)
{ {
sh_timer_state *s = (sh_timer_state *)opaque; SHTimerState *s = opaque;
switch (offset >> 2) { switch (offset >> 2) {
case OFFSET_TCOR: case OFFSET_TCOR:
@ -73,19 +72,18 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset)
case OFFSET_TCR: case OFFSET_TCR:
return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0); return s->tcr | (s->int_level ? TIMER_TCR_UNF : 0);
case OFFSET_TCPR: case OFFSET_TCPR:
if (s->feat & TIMER_FEAT_CAPT) if (s->feat & TIMER_FEAT_CAPT) {
return s->tcpr; return s->tcpr;
/* fall through */ }
default:
hw_error("sh_timer_read: Bad offset %x\n", (int)offset);
return 0;
} }
qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
return 0;
} }
static void sh_timer_write(void *opaque, hwaddr offset, static void sh_timer_write(void *opaque, hwaddr offset, uint32_t value)
uint32_t value)
{ {
sh_timer_state *s = (sh_timer_state *)opaque; SHTimerState *s = opaque;
int freq; int freq;
switch (offset >> 2) { switch (offset >> 2) {
@ -104,19 +102,30 @@ static void sh_timer_write(void *opaque, hwaddr offset,
case OFFSET_TCR: case OFFSET_TCR:
ptimer_transaction_begin(s->timer); ptimer_transaction_begin(s->timer);
if (s->enabled) { if (s->enabled) {
/* Pause the timer if it is running. This may cause some /*
inaccuracy dure to rounding, but avoids a whole lot of other * Pause the timer if it is running. This may cause some inaccuracy
messyness. */ * due to rounding, but avoids a whole lot of other messiness
*/
ptimer_stop(s->timer); ptimer_stop(s->timer);
} }
freq = s->freq; freq = s->freq;
/* ??? Need to recalculate expiry time after changing divisor. */ /* ??? Need to recalculate expiry time after changing divisor. */
switch (value & TIMER_TCR_TPSC) { switch (value & TIMER_TCR_TPSC) {
case 0: freq >>= 2; break; case 0:
case 1: freq >>= 4; break; freq >>= 2;
case 2: freq >>= 6; break; break;
case 3: freq >>= 8; break; case 1:
case 4: freq >>= 10; break; freq >>= 4;
break;
case 2:
freq >>= 6;
break;
case 3:
freq >>= 8;
break;
case 4:
freq >>= 10;
break;
case 6: case 6:
case 7: case 7:
if (s->feat & TIMER_FEAT_EXTCLK) { if (s->feat & TIMER_FEAT_EXTCLK) {
@ -124,7 +133,8 @@ static void sh_timer_write(void *opaque, hwaddr offset,
} }
/* fallthrough */ /* fallthrough */
default: default:
hw_error("sh_timer_write: Reserved TPSC value\n"); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Reserved TPSC value\n", __func__);
} }
switch ((value & TIMER_TCR_CKEG) >> 3) { switch ((value & TIMER_TCR_CKEG) >> 3) {
case 0: case 0:
@ -137,7 +147,8 @@ static void sh_timer_write(void *opaque, hwaddr offset,
} }
/* fallthrough */ /* fallthrough */
default: default:
hw_error("sh_timer_write: Reserved CKEG value\n"); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Reserved CKEG value\n", __func__);
} }
switch ((value & TIMER_TCR_ICPE) >> 6) { switch ((value & TIMER_TCR_ICPE) >> 6) {
case 0: case 0:
@ -149,7 +160,8 @@ static void sh_timer_write(void *opaque, hwaddr offset,
} }
/* fallthrough */ /* fallthrough */
default: default:
hw_error("sh_timer_write: Reserved ICPE value\n"); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Reserved ICPE value\n", __func__);
} }
if ((value & TIMER_TCR_UNF) == 0) { if ((value & TIMER_TCR_UNF) == 0) {
s->int_level = 0; s->int_level = 0;
@ -158,13 +170,15 @@ static void sh_timer_write(void *opaque, hwaddr offset,
value &= ~TIMER_TCR_UNF; value &= ~TIMER_TCR_UNF;
if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) { if ((value & TIMER_TCR_ICPF) && (!(s->feat & TIMER_FEAT_CAPT))) {
hw_error("sh_timer_write: Reserved ICPF value\n"); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Reserved ICPF value\n", __func__);
} }
value &= ~TIMER_TCR_ICPF; /* capture not supported */ value &= ~TIMER_TCR_ICPF; /* capture not supported */
if (value & TIMER_TCR_RESERVED) { if (value & TIMER_TCR_RESERVED) {
hw_error("sh_timer_write: Reserved TCR bits set\n"); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Reserved TCR bits set\n", __func__);
} }
s->tcr = value; s->tcr = value;
ptimer_set_limit(s->timer, s->tcor, 0); ptimer_set_limit(s->timer, s->tcor, 0);
@ -182,19 +196,17 @@ static void sh_timer_write(void *opaque, hwaddr offset,
} }
/* fallthrough */ /* fallthrough */
default: default:
hw_error("sh_timer_write: Bad offset %x\n", (int)offset); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
} }
sh_timer_update(s); sh_timer_update(s);
} }
static void sh_timer_start_stop(void *opaque, int enable) static void sh_timer_start_stop(void *opaque, int enable)
{ {
sh_timer_state *s = (sh_timer_state *)opaque; SHTimerState *s = opaque;
#ifdef DEBUG_TIMER
printf("sh_timer_start_stop %d (%d)\n", enable, s->enabled);
#endif
trace_sh_timer_start_stop(enable, s->enabled);
ptimer_transaction_begin(s->timer); ptimer_transaction_begin(s->timer);
if (s->enabled && !enable) { if (s->enabled && !enable) {
ptimer_stop(s->timer); ptimer_stop(s->timer);
@ -204,24 +216,20 @@ static void sh_timer_start_stop(void *opaque, int enable)
} }
ptimer_transaction_commit(s->timer); ptimer_transaction_commit(s->timer);
s->enabled = !!enable; s->enabled = !!enable;
#ifdef DEBUG_TIMER
printf("sh_timer_start_stop done %d\n", s->enabled);
#endif
} }
static void sh_timer_tick(void *opaque) static void sh_timer_tick(void *opaque)
{ {
sh_timer_state *s = (sh_timer_state *)opaque; SHTimerState *s = opaque;
s->int_level = s->enabled; s->int_level = s->enabled;
sh_timer_update(s); sh_timer_update(s);
} }
static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq) static void *sh_timer_init(uint32_t freq, int feat, qemu_irq irq)
{ {
sh_timer_state *s; SHTimerState *s;
s = (sh_timer_state *)g_malloc0(sizeof(sh_timer_state)); s = g_malloc0(sizeof(*s));
s->freq = freq; s->freq = freq;
s->feat = feat; s->feat = feat;
s->tcor = 0xffffffff; s->tcor = 0xffffffff;
@ -252,50 +260,49 @@ typedef struct {
int feat; int feat;
} tmu012_state; } tmu012_state;
static uint64_t tmu012_read(void *opaque, hwaddr offset, static uint64_t tmu012_read(void *opaque, hwaddr offset, unsigned size)
unsigned size)
{ {
tmu012_state *s = (tmu012_state *)opaque; tmu012_state *s = opaque;
#ifdef DEBUG_TIMER
printf("tmu012_read 0x%lx\n", (unsigned long) offset);
#endif
trace_sh_timer_read(offset);
if (offset >= 0x20) { if (offset >= 0x20) {
if (!(s->feat & TMU012_FEAT_3CHAN)) { if (!(s->feat & TMU012_FEAT_3CHAN)) {
hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
} }
return sh_timer_read(s->timer[2], offset - 0x20); return sh_timer_read(s->timer[2], offset - 0x20);
} }
if (offset >= 0x14) if (offset >= 0x14) {
return sh_timer_read(s->timer[1], offset - 0x14); return sh_timer_read(s->timer[1], offset - 0x14);
}
if (offset >= 0x08) if (offset >= 0x08) {
return sh_timer_read(s->timer[0], offset - 0x08); return sh_timer_read(s->timer[0], offset - 0x08);
}
if (offset == 4) if (offset == 4) {
return s->tstr; return s->tstr;
}
if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) if ((s->feat & TMU012_FEAT_TOCR) && offset == 0) {
return s->tocr; return s->tocr;
}
hw_error("tmu012_write: Bad offset %x\n", (int)offset); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad offset 0x%" HWADDR_PRIx "\n", __func__, offset);
return 0; return 0;
} }
static void tmu012_write(void *opaque, hwaddr offset, static void tmu012_write(void *opaque, hwaddr offset,
uint64_t value, unsigned size) uint64_t value, unsigned size)
{ {
tmu012_state *s = (tmu012_state *)opaque; tmu012_state *s = opaque;
#ifdef DEBUG_TIMER
printf("tmu012_write 0x%lx 0x%08x\n", (unsigned long) offset, value);
#endif
trace_sh_timer_write(offset, value);
if (offset >= 0x20) { if (offset >= 0x20) {
if (!(s->feat & TMU012_FEAT_3CHAN)) { if (!(s->feat & TMU012_FEAT_3CHAN)) {
hw_error("tmu012_write: Bad channel offset %x\n", (int)offset); qemu_log_mask(LOG_GUEST_ERROR,
"%s: Bad channel offset 0x%" HWADDR_PRIx "\n",
__func__, offset);
} }
sh_timer_write(s->timer[2], offset - 0x20, value); sh_timer_write(s->timer[2], offset - 0x20, value);
return; return;
@ -318,7 +325,7 @@ static void tmu012_write(void *opaque, hwaddr offset,
sh_timer_start_stop(s->timer[2], value & (1 << 2)); sh_timer_start_stop(s->timer[2], value & (1 << 2));
} else { } else {
if (value & (1 << 2)) { if (value & (1 << 2)) {
hw_error("tmu012_write: Bad channel\n"); qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad channel\n", __func__);
} }
} }
@ -337,15 +344,14 @@ static const MemoryRegionOps tmu012_ops = {
.endianness = DEVICE_NATIVE_ENDIAN, .endianness = DEVICE_NATIVE_ENDIAN,
}; };
void tmu012_init(MemoryRegion *sysmem, hwaddr base, void tmu012_init(MemoryRegion *sysmem, hwaddr base, int feat, uint32_t freq,
int feat, uint32_t freq,
qemu_irq ch0_irq, qemu_irq ch1_irq, qemu_irq ch0_irq, qemu_irq ch1_irq,
qemu_irq ch2_irq0, qemu_irq ch2_irq1) qemu_irq ch2_irq0, qemu_irq ch2_irq1)
{ {
tmu012_state *s; tmu012_state *s;
int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0; int timer_feat = (feat & TMU012_FEAT_EXTCLK) ? TIMER_FEAT_EXTCLK : 0;
s = (tmu012_state *)g_malloc0(sizeof(tmu012_state)); s = g_malloc0(sizeof(*s));
s->feat = feat; s->feat = feat;
s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq); s->timer[0] = sh_timer_init(freq, timer_feat, ch0_irq);
s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq); s->timer[1] = sh_timer_init(freq, timer_feat, ch1_irq);
@ -354,15 +360,14 @@ void tmu012_init(MemoryRegion *sysmem, hwaddr base,
ch2_irq0); /* ch2_irq1 not supported */ ch2_irq0); /* ch2_irq1 not supported */
} }
memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, memory_region_init_io(&s->iomem, NULL, &tmu012_ops, s, "timer", 0x30);
"timer", 0x100000000ULL);
memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4", memory_region_init_alias(&s->iomem_p4, NULL, "timer-p4",
&s->iomem, 0, 0x1000); &s->iomem, 0, memory_region_size(&s->iomem));
memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4); memory_region_add_subregion(sysmem, P4ADDR(base), &s->iomem_p4);
memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7", memory_region_init_alias(&s->iomem_a7, NULL, "timer-a7",
&s->iomem, 0, 0x1000); &s->iomem, 0, memory_region_size(&s->iomem));
memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7); memory_region_add_subregion(sysmem, A7ADDR(base), &s->iomem_a7);
/* ??? Save/restore. */ /* ??? Save/restore. */
} }

View File

@ -94,3 +94,8 @@ sifive_pwm_set_alarm(uint64_t alarm, uint64_t now) "Setting alarm to: 0x%" PRIx6
sifive_pwm_interrupt(int num) "Interrupt %d" sifive_pwm_interrupt(int num) "Interrupt %d"
sifive_pwm_read(uint64_t offset) "Read at address: 0x%" PRIx64 sifive_pwm_read(uint64_t offset) "Read at address: 0x%" PRIx64
sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address: 0x%" PRIx64 sifive_pwm_write(uint64_t data, uint64_t offset) "Write 0x%" PRIx64 " at address: 0x%" PRIx64
# sh_timer.c
sh_timer_start_stop(int enable, int current) "%d (%d)"
sh_timer_read(uint64_t offset) "tmu012_read 0x%" PRIx64
sh_timer_write(uint64_t offset, uint64_t value) "tmu012_write 0x%" PRIx64 " 0x%08" PRIx64

View File

@ -44,25 +44,18 @@ typedef struct {
uint16_t portbmask_trigger; uint16_t portbmask_trigger;
/* Return 0 if no action was taken */ /* Return 0 if no action was taken */
int (*port_change_cb) (uint16_t porta, uint16_t portb, int (*port_change_cb) (uint16_t porta, uint16_t portb,
uint16_t * periph_pdtra, uint16_t *periph_pdtra,
uint16_t * periph_portdira, uint16_t *periph_portdira,
uint16_t * periph_pdtrb, uint16_t *periph_pdtrb,
uint16_t * periph_portdirb); uint16_t *periph_portdirb);
} sh7750_io_device; } sh7750_io_device;
int sh7750_register_io_device(struct SH7750State *s, int sh7750_register_io_device(struct SH7750State *s,
sh7750_io_device * device); sh7750_io_device *device);
/* sh_serial.c */ /* sh_serial.c */
#define TYPE_SH_SERIAL "sh-serial"
#define SH_SERIAL_FEAT_SCIF (1 << 0) #define SH_SERIAL_FEAT_SCIF (1 << 0)
void sh_serial_init(MemoryRegion *sysmem,
hwaddr base, int feat,
uint32_t freq, Chardev *chr,
qemu_irq eri_source,
qemu_irq rxi_source,
qemu_irq txi_source,
qemu_irq tei_source,
qemu_irq bri_source);
/* sh7750.c */ /* sh7750.c */
qemu_irq sh7750_irl(struct SH7750State *s); qemu_irq sh7750_irl(struct SH7750State *s);

View File

@ -58,7 +58,7 @@ struct intc_desc {
}; };
int sh_intc_get_pending_vector(struct intc_desc *desc, int imask); int sh_intc_get_pending_vector(struct intc_desc *desc, int imask);
struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id);
void sh_intc_toggle_source(struct intc_source *source, void sh_intc_toggle_source(struct intc_source *source,
int enable_adj, int assert_adj); int enable_adj, int assert_adj);

View File

@ -2459,6 +2459,7 @@ if have_system
'hw/s390x', 'hw/s390x',
'hw/scsi', 'hw/scsi',
'hw/sd', 'hw/sd',
'hw/sh4',
'hw/sparc', 'hw/sparc',
'hw/sparc64', 'hw/sparc64',
'hw/ssi', 'hw/ssi',