|
|
1.1 ! root 1: /* ! 2: * QEMU model of the Milkymist System Controller. ! 3: * ! 4: * Copyright (c) 2010 Michael Walle <[email protected]> ! 5: * ! 6: * This library is free software; you can redistribute it and/or ! 7: * modify it under the terms of the GNU Lesser General Public ! 8: * License as published by the Free Software Foundation; either ! 9: * version 2 of the License, or (at your option) any later version. ! 10: * ! 11: * This library is distributed in the hope that it will be useful, ! 12: * but WITHOUT ANY WARRANTY; without even the implied warranty of ! 13: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ! 14: * Lesser General Public License for more details. ! 15: * ! 16: * You should have received a copy of the GNU Lesser General Public ! 17: * License along with this library; if not, see <http://www.gnu.org/licenses/>. ! 18: * ! 19: * ! 20: * Specification available at: ! 21: * http://www.milkymist.org/socdoc/sysctl.pdf ! 22: */ ! 23: ! 24: #include "hw.h" ! 25: #include "sysbus.h" ! 26: #include "sysemu.h" ! 27: #include "trace.h" ! 28: #include "qemu-timer.h" ! 29: #include "qemu-error.h" ! 30: ! 31: enum { ! 32: CTRL_ENABLE = (1<<0), ! 33: CTRL_AUTORESTART = (1<<1), ! 34: }; ! 35: ! 36: enum { ! 37: ICAP_READY = (1<<0), ! 38: }; ! 39: ! 40: enum { ! 41: R_GPIO_IN = 0, ! 42: R_GPIO_OUT, ! 43: R_GPIO_INTEN, ! 44: R_RESERVED0, ! 45: R_TIMER0_CONTROL, ! 46: R_TIMER0_COMPARE, ! 47: R_TIMER0_COUNTER, ! 48: R_RESERVED1, ! 49: R_TIMER1_CONTROL, ! 50: R_TIMER1_COMPARE, ! 51: R_TIMER1_COUNTER, ! 52: R_RESERVED2, ! 53: R_RESERVED3, ! 54: R_ICAP, ! 55: R_CAPABILITIES, ! 56: R_SYSTEM_ID, ! 57: R_MAX ! 58: }; ! 59: ! 60: struct MilkymistSysctlState { ! 61: SysBusDevice busdev; ! 62: ! 63: QEMUBH *bh0; ! 64: QEMUBH *bh1; ! 65: ptimer_state *ptimer0; ! 66: ptimer_state *ptimer1; ! 67: ! 68: uint32_t freq_hz; ! 69: uint32_t capabilities; ! 70: uint32_t systemid; ! 71: uint32_t strappings; ! 72: ! 73: uint32_t regs[R_MAX]; ! 74: ! 75: qemu_irq gpio_irq; ! 76: qemu_irq timer0_irq; ! 77: qemu_irq timer1_irq; ! 78: }; ! 79: typedef struct MilkymistSysctlState MilkymistSysctlState; ! 80: ! 81: static void sysctl_icap_write(MilkymistSysctlState *s, uint32_t value) ! 82: { ! 83: trace_milkymist_sysctl_icap_write(value); ! 84: switch (value & 0xffff) { ! 85: case 0x000e: ! 86: qemu_system_shutdown_request(); ! 87: break; ! 88: } ! 89: } ! 90: ! 91: static uint32_t sysctl_read(void *opaque, target_phys_addr_t addr) ! 92: { ! 93: MilkymistSysctlState *s = opaque; ! 94: uint32_t r = 0; ! 95: ! 96: addr >>= 2; ! 97: switch (addr) { ! 98: case R_TIMER0_COUNTER: ! 99: r = (uint32_t)ptimer_get_count(s->ptimer0); ! 100: /* milkymist timer counts up */ ! 101: r = s->regs[R_TIMER0_COMPARE] - r; ! 102: break; ! 103: case R_TIMER1_COUNTER: ! 104: r = (uint32_t)ptimer_get_count(s->ptimer1); ! 105: /* milkymist timer counts up */ ! 106: r = s->regs[R_TIMER1_COMPARE] - r; ! 107: break; ! 108: case R_GPIO_IN: ! 109: case R_GPIO_OUT: ! 110: case R_GPIO_INTEN: ! 111: case R_TIMER0_CONTROL: ! 112: case R_TIMER0_COMPARE: ! 113: case R_TIMER1_CONTROL: ! 114: case R_TIMER1_COMPARE: ! 115: case R_ICAP: ! 116: case R_CAPABILITIES: ! 117: case R_SYSTEM_ID: ! 118: r = s->regs[addr]; ! 119: break; ! 120: ! 121: default: ! 122: error_report("milkymist_sysctl: read access to unknown register 0x" ! 123: TARGET_FMT_plx, addr << 2); ! 124: break; ! 125: } ! 126: ! 127: trace_milkymist_sysctl_memory_read(addr << 2, r); ! 128: ! 129: return r; ! 130: } ! 131: ! 132: static void sysctl_write(void *opaque, target_phys_addr_t addr, uint32_t value) ! 133: { ! 134: MilkymistSysctlState *s = opaque; ! 135: ! 136: trace_milkymist_sysctl_memory_write(addr, value); ! 137: ! 138: addr >>= 2; ! 139: switch (addr) { ! 140: case R_GPIO_OUT: ! 141: case R_GPIO_INTEN: ! 142: case R_TIMER0_COUNTER: ! 143: case R_TIMER1_COUNTER: ! 144: s->regs[addr] = value; ! 145: break; ! 146: case R_TIMER0_COMPARE: ! 147: ptimer_set_limit(s->ptimer0, value, 0); ! 148: s->regs[addr] = value; ! 149: break; ! 150: case R_TIMER1_COMPARE: ! 151: ptimer_set_limit(s->ptimer1, value, 0); ! 152: s->regs[addr] = value; ! 153: break; ! 154: case R_TIMER0_CONTROL: ! 155: s->regs[addr] = value; ! 156: if (s->regs[R_TIMER0_CONTROL] & CTRL_ENABLE) { ! 157: trace_milkymist_sysctl_start_timer0(); ! 158: ptimer_set_count(s->ptimer0, ! 159: s->regs[R_TIMER0_COMPARE] - s->regs[R_TIMER0_COUNTER]); ! 160: ptimer_run(s->ptimer0, 0); ! 161: } else { ! 162: trace_milkymist_sysctl_stop_timer0(); ! 163: ptimer_stop(s->ptimer0); ! 164: } ! 165: break; ! 166: case R_TIMER1_CONTROL: ! 167: s->regs[addr] = value; ! 168: if (s->regs[R_TIMER1_CONTROL] & CTRL_ENABLE) { ! 169: trace_milkymist_sysctl_start_timer1(); ! 170: ptimer_set_count(s->ptimer1, ! 171: s->regs[R_TIMER1_COMPARE] - s->regs[R_TIMER1_COUNTER]); ! 172: ptimer_run(s->ptimer1, 0); ! 173: } else { ! 174: trace_milkymist_sysctl_stop_timer1(); ! 175: ptimer_stop(s->ptimer1); ! 176: } ! 177: break; ! 178: case R_ICAP: ! 179: sysctl_icap_write(s, value); ! 180: break; ! 181: case R_SYSTEM_ID: ! 182: qemu_system_reset_request(); ! 183: break; ! 184: ! 185: case R_GPIO_IN: ! 186: case R_CAPABILITIES: ! 187: error_report("milkymist_sysctl: write to read-only register 0x" ! 188: TARGET_FMT_plx, addr << 2); ! 189: break; ! 190: ! 191: default: ! 192: error_report("milkymist_sysctl: write access to unknown register 0x" ! 193: TARGET_FMT_plx, addr << 2); ! 194: break; ! 195: } ! 196: } ! 197: ! 198: static CPUReadMemoryFunc * const sysctl_read_fn[] = { ! 199: NULL, ! 200: NULL, ! 201: &sysctl_read, ! 202: }; ! 203: ! 204: static CPUWriteMemoryFunc * const sysctl_write_fn[] = { ! 205: NULL, ! 206: NULL, ! 207: &sysctl_write, ! 208: }; ! 209: ! 210: static void timer0_hit(void *opaque) ! 211: { ! 212: MilkymistSysctlState *s = opaque; ! 213: ! 214: if (!(s->regs[R_TIMER0_CONTROL] & CTRL_AUTORESTART)) { ! 215: s->regs[R_TIMER0_CONTROL] &= ~CTRL_ENABLE; ! 216: trace_milkymist_sysctl_stop_timer0(); ! 217: ptimer_stop(s->ptimer0); ! 218: } ! 219: ! 220: trace_milkymist_sysctl_pulse_irq_timer0(); ! 221: qemu_irq_pulse(s->timer0_irq); ! 222: } ! 223: ! 224: static void timer1_hit(void *opaque) ! 225: { ! 226: MilkymistSysctlState *s = opaque; ! 227: ! 228: if (!(s->regs[R_TIMER1_CONTROL] & CTRL_AUTORESTART)) { ! 229: s->regs[R_TIMER1_CONTROL] &= ~CTRL_ENABLE; ! 230: trace_milkymist_sysctl_stop_timer1(); ! 231: ptimer_stop(s->ptimer1); ! 232: } ! 233: ! 234: trace_milkymist_sysctl_pulse_irq_timer1(); ! 235: qemu_irq_pulse(s->timer1_irq); ! 236: } ! 237: ! 238: static void milkymist_sysctl_reset(DeviceState *d) ! 239: { ! 240: MilkymistSysctlState *s = ! 241: container_of(d, MilkymistSysctlState, busdev.qdev); ! 242: int i; ! 243: ! 244: for (i = 0; i < R_MAX; i++) { ! 245: s->regs[i] = 0; ! 246: } ! 247: ! 248: ptimer_stop(s->ptimer0); ! 249: ptimer_stop(s->ptimer1); ! 250: ! 251: /* defaults */ ! 252: s->regs[R_ICAP] = ICAP_READY; ! 253: s->regs[R_SYSTEM_ID] = s->systemid; ! 254: s->regs[R_CAPABILITIES] = s->capabilities; ! 255: s->regs[R_GPIO_IN] = s->strappings; ! 256: } ! 257: ! 258: static int milkymist_sysctl_init(SysBusDevice *dev) ! 259: { ! 260: MilkymistSysctlState *s = FROM_SYSBUS(typeof(*s), dev); ! 261: int sysctl_regs; ! 262: ! 263: sysbus_init_irq(dev, &s->gpio_irq); ! 264: sysbus_init_irq(dev, &s->timer0_irq); ! 265: sysbus_init_irq(dev, &s->timer1_irq); ! 266: ! 267: s->bh0 = qemu_bh_new(timer0_hit, s); ! 268: s->bh1 = qemu_bh_new(timer1_hit, s); ! 269: s->ptimer0 = ptimer_init(s->bh0); ! 270: s->ptimer1 = ptimer_init(s->bh1); ! 271: ptimer_set_freq(s->ptimer0, s->freq_hz); ! 272: ptimer_set_freq(s->ptimer1, s->freq_hz); ! 273: ! 274: sysctl_regs = cpu_register_io_memory(sysctl_read_fn, sysctl_write_fn, s, ! 275: DEVICE_NATIVE_ENDIAN); ! 276: sysbus_init_mmio(dev, R_MAX * 4, sysctl_regs); ! 277: ! 278: return 0; ! 279: } ! 280: ! 281: static const VMStateDescription vmstate_milkymist_sysctl = { ! 282: .name = "milkymist-sysctl", ! 283: .version_id = 1, ! 284: .minimum_version_id = 1, ! 285: .minimum_version_id_old = 1, ! 286: .fields = (VMStateField[]) { ! 287: VMSTATE_UINT32_ARRAY(regs, MilkymistSysctlState, R_MAX), ! 288: VMSTATE_PTIMER(ptimer0, MilkymistSysctlState), ! 289: VMSTATE_PTIMER(ptimer1, MilkymistSysctlState), ! 290: VMSTATE_END_OF_LIST() ! 291: } ! 292: }; ! 293: ! 294: static SysBusDeviceInfo milkymist_sysctl_info = { ! 295: .init = milkymist_sysctl_init, ! 296: .qdev.name = "milkymist-sysctl", ! 297: .qdev.size = sizeof(MilkymistSysctlState), ! 298: .qdev.vmsd = &vmstate_milkymist_sysctl, ! 299: .qdev.reset = milkymist_sysctl_reset, ! 300: .qdev.props = (Property[]) { ! 301: DEFINE_PROP_UINT32("frequency", MilkymistSysctlState, ! 302: freq_hz, 80000000), ! 303: DEFINE_PROP_UINT32("capabilities", MilkymistSysctlState, ! 304: capabilities, 0x00000000), ! 305: DEFINE_PROP_UINT32("systemid", MilkymistSysctlState, ! 306: systemid, 0x10014d31), ! 307: DEFINE_PROP_UINT32("gpio_strappings", MilkymistSysctlState, ! 308: strappings, 0x00000001), ! 309: DEFINE_PROP_END_OF_LIST(), ! 310: } ! 311: }; ! 312: ! 313: static void milkymist_sysctl_register(void) ! 314: { ! 315: sysbus_register_withprop(&milkymist_sysctl_info); ! 316: } ! 317: ! 318: device_init(milkymist_sysctl_register)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.