123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- /*
- * QEMU model of the Milkymist System Controller.
- *
- * Copyright (c) 2010 Michael Walle <michael@walle.cc>
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, see <http://www.gnu.org/licenses/>.
- *
- *
- * Specification available at:
- * http://www.milkymist.org/socdoc/sysctl.pdf
- */
- #include "hw.h"
- #include "sysbus.h"
- #include "sysemu.h"
- #include "trace.h"
- #include "qemu-timer.h"
- #include "qemu-error.h"
- enum {
- CTRL_ENABLE = (1<<0),
- CTRL_AUTORESTART = (1<<1),
- };
- enum {
- ICAP_READY = (1<<0),
- };
- enum {
- R_GPIO_IN = 0,
- R_GPIO_OUT,
- R_GPIO_INTEN,
- R_RESERVED0,
- R_TIMER0_CONTROL,
- R_TIMER0_COMPARE,
- R_TIMER0_COUNTER,
- R_RESERVED1,
- R_TIMER1_CONTROL,
- R_TIMER1_COMPARE,
- R_TIMER1_COUNTER,
- R_RESERVED2,
- R_RESERVED3,
- R_ICAP,
- R_CAPABILITIES,
- R_SYSTEM_ID,
- R_MAX
- };
- struct MilkymistSysctlState {
- SysBusDevice busdev;
- MemoryRegion regs_region;
- QEMUBH *bh0;
- QEMUBH *bh1;
- ptimer_state *ptimer0;
- ptimer_state *ptimer1;
- uint32_t freq_hz;
- uint32_t capabilities;
- uint32_t systemid;
- uint32_t strappings;
- uint32_t regs[R_MAX];
- qemu_irq gpio_irq;
- qemu_irq timer0_irq;
- qemu_irq timer1_irq;
- };
- typedef struct MilkymistSysctlState MilkymistSysctlState;
- static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value)
- {
- trace_milkymist_sysctl_icap_write(value);
- switch (value & 0xffff) {
- case 0x000e:
- qemu_system_shutdown_request();
- break;
- }
- }
- static uint64_t sysctl_read(void *opaque, target_phys_addr_t addr,
- unsigned size)
- {
- MilkymistSysctlState *s = opaque;
- uint32_t r = 0;
- addr >>= 2;
- switch (addr) {
- case R_TIMER0_COUNTER:
- r = (uint32_t)ptimer_get_count(s->ptimer0);
- /* milkymist timer counts up */
- r = s->regs[R_TIMER0_COMPARE] - r;
- break;
- case R_TIMER1_COUNTER:
- r = (uint32_t)ptimer_get_count(s->ptimer1);
- /* milkymist timer counts up */
- r = s->regs[R_TIMER1_COMPARE] - r;
- break;
- case R_GPIO_IN:
- case R_GPIO_OUT:
- case R_GPIO_INTEN:
- case R_TIMER0_CONTROL:
- case R_TIMER0_COMPARE:
- case R_TIMER1_CONTROL:
- case R_TIMER1_COMPARE:
- case R_ICAP:
- case R_CAPABILITIES:
- case R_SYSTEM_ID:
- r = s->regs[addr];
- break;
- default:
- error_report("milkymist_sysctl: read access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
- trace_milkymist_sysctl_memory_read(addr << 2, r);
- return r;
- }
- static void sysctl_write(void *opaque, target_phys_addr_t addr, uint64_t value,
- unsigned size)
- {
- MilkymistSysctlState *s = opaque;
- trace_milkymist_sysctl_memory_write(addr, value);
- addr >>= 2;
- switch (addr) {
- case R_GPIO_OUT:
- case R_GPIO_INTEN:
- case R_TIMER0_COUNTER:
- case R_TIMER1_COUNTER:
- s->regs[addr] = value;
- break;
- case R_TIMER0_COMPARE:
- ptimer_set_limit(s->ptimer0, value, 0);
- s->regs[addr] = value;
- break;
- case R_TIMER1_COMPARE:
- ptimer_set_limit(s->ptimer1, value, 0);
- s->regs[addr] = value;
- break;
- case R_TIMER0_CONTROL:
- s->regs[addr] = value;
- if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) {
- trace_milkymist_sysctl_start_timer0();
- ptimer_set_count(s->ptimer0,
- s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]);
- ptimer_run(s->ptimer0, 0);
- } else {
- trace_milkymist_sysctl_stop_timer0();
- ptimer_stop(s->ptimer0);
- }
- break;
- case R_TIMER1_CONTROL:
- s->regs[addr] = value;
- if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) {
- trace_milkymist_sysctl_start_timer1();
- ptimer_set_count(s->ptimer1,
- s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]);
- ptimer_run(s->ptimer1, 0);
- } else {
- trace_milkymist_sysctl_stop_timer1();
- ptimer_stop(s->ptimer1);
- }
- break;
- case R_ICAP:
- sysctl_icap_write(s, value);
- break;
- case R_SYSTEM_ID:
- qemu_system_reset_request();
- break;
- case R_GPIO_IN:
- case R_CAPABILITIES:
- error_report("milkymist_sysctl: write to read-only register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- default:
- error_report("milkymist_sysctl: write access to unknown register 0x"
- TARGET_FMT_plx, addr << 2);
- break;
- }
- }
- static const MemoryRegionOps sysctl_mmio_ops = {
- .read = sysctl_read,
- .write = sysctl_write,
- .valid = {
- .min_access_size = 4,
- .max_access_size = 4,
- },
- .endianness = DEVICE_NATIVE_ENDIAN,
- };
- static void timer0_hit(void *opaque)
- {
- MilkymistSysctlState *s = opaque;
- if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) {
- s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE;
- trace_milkymist_sysctl_stop_timer0();
- ptimer_stop(s->ptimer0);
- }
- trace_milkymist_sysctl_pulse_irq_timer0();
- qemu_irq_pulse(s->timer0_irq);
- }
- static void timer1_hit(void *opaque)
- {
- MilkymistSysctlState *s = opaque;
- if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) {
- s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE;
- trace_milkymist_sysctl_stop_timer1();
- ptimer_stop(s->ptimer1);
- }
- trace_milkymist_sysctl_pulse_irq_timer1();
- qemu_irq_pulse(s->timer1_irq);
- }
- static void milkymist_sysctl_reset(DeviceState *d)
- {
- MilkymistSysctlState *s =
- container_of(d, MilkymistSysctlState, busdev.qdev);
- int i;
- for (i = 0; i < R_MAX; i++) {
- s->regs[i] = 0;
- }
- ptimer_stop(s->ptimer0);
- ptimer_stop(s->ptimer1);
- /* defaults */
- s->regs[R_ICAP] = ICAP_READY;
- s->regs[R_SYSTEM_ID] = s->systemid;
- s->regs[R_CAPABILITIES] = s->capabilities;
- s->regs[R_GPIO_IN] = s->strappings;
- }
- static int milkymist_sysctl_init(SysBusDevice *dev)
- {
- MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev);
- sysbus_init_irq(dev, &s->gpio_irq);
- sysbus_init_irq(dev, &s->timer0_irq);
- sysbus_init_irq(dev, &s->timer1_irq);
- s->bh0 = qemu_bh_new(timer0_hit, s);
- s->bh1 = qemu_bh_new(timer1_hit, s);
- s->ptimer0 = ptimer_init(s->bh0);
- s->ptimer1 = ptimer_init(s->bh1);
- ptimer_set_freq(s->ptimer0, s->freq_hz);
- ptimer_set_freq(s->ptimer1, s->freq_hz);
- memory_region_init_io(&s->regs_region, &sysctl_mmio_ops, s,
- "milkymist-sysctl", R_MAX * 4);
- sysbus_init_mmio_region(dev, &s->regs_region);
- return 0;
- }
- static const VMStateDescription vmstate_milkymist_sysctl = {
- .name = "milkymist-sysctl",
- .version_id = 1,
- .minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField[]) {
- VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX),
- VMSTATE_PTIMER(ptimer0, MilkymistSysctlState),
- VMSTATE_PTIMER(ptimer1, MilkymistSysctlState),
- VMSTATE_END_OF_LIST()
- }
- };
- static SysBusDeviceInfo milkymist_sysctl_info = {
- .init = milkymist_sysctl_init,
- .qdev.name = "milkymist-sysctl",
- .qdev.size = sizeof(MilkymistSysctlState),
- .qdev.vmsd = &vmstate_milkymist_sysctl,
- .qdev.reset = milkymist_sysctl_reset,
- .qdev.props = (Property[]) {
- DEFINE_PROP_UINT32("frequency", MilkymistSysctlState,
- freq_hz, 80000000),
- DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState,
- capabilities, 0x00000000),
- DEFINE_PROP_UINT32("systemid", MilkymistSysctlState,
- systemid, 0x10014d31),
- DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState,
- strappings, 0x00000001),
- DEFINE_PROP_END_OF_LIST(),
- }
- };
- static void milkymist_sysctl_register(void)
- {
- sysbus_register_withprop(&milkymist_sysctl_info);
- }
- device_init(milkymist_sysctl_register)
|