qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

mac_newworld: add PMU device

The PMU device supercedes the CUDA device found on older New World Macs and
is supported by a larger number of guest OSs from OS 9 to OS X 10.5.

Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>

authored by

Mark Cave-Ayland and committed by
David Gibson
d811d61f 84051eb4

+1193 -20
+1
default-configs/ppc-softmmu.mak
··· 38 38 CONFIG_ADB=y 39 39 CONFIG_MAC_NVRAM=y 40 40 CONFIG_MAC_DBDMA=y 41 + CONFIG_MAC_PMU=y 41 42 CONFIG_HEATHROW_PIC=y 42 43 CONFIG_GRACKLE_PCI=y 43 44 CONFIG_UNIN_PCI=y
+1
hw/misc/macio/Makefile.objs
··· 1 1 common-obj-y += macio.o 2 2 common-obj-$(CONFIG_CUDA) += cuda.o 3 + common-obj-$(CONFIG_MAC_PMU) += pmu.o 3 4 common-obj-$(CONFIG_MAC_DBDMA) += mac_dbdma.o 4 5 common-obj-$(CONFIG_MACIO_GPIO) += gpio.o
+50 -19
hw/misc/macio/macio.c
··· 105 105 memory_region_add_subregion(&s->bar, 0x08000, 106 106 sysbus_mmio_get_region(sysbus_dev, 0)); 107 107 108 - qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", 109 - s->frequency); 110 - object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); 111 - if (err) { 112 - error_propagate(errp, err); 113 - return; 114 - } 115 - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); 116 - memory_region_add_subregion(&s->bar, 0x16000, 117 - sysbus_mmio_get_region(sysbus_dev, 0)); 118 - 119 108 qdev_prop_set_uint32(DEVICE(&s->escc), "disabled", 0); 120 109 qdev_prop_set_uint32(DEVICE(&s->escc), "frequency", ESCC_CLOCK); 121 110 qdev_prop_set_uint32(DEVICE(&s->escc), "it_shift", 4); ··· 163 152 return; 164 153 } 165 154 155 + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", 156 + s->frequency); 157 + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); 158 + if (err) { 159 + error_propagate(errp, err); 160 + return; 161 + } 166 162 sysbus_dev = SYS_BUS_DEVICE(&s->cuda); 163 + memory_region_add_subregion(&s->bar, 0x16000, 164 + sysbus_mmio_get_region(sysbus_dev, 0)); 167 165 sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, 168 166 OLDWORLD_CUDA_IRQ)); 169 167 ··· 234 232 qdev_prop_allow_set_link_before_realize, 235 233 0, NULL); 236 234 235 + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); 236 + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); 237 + object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); 238 + 237 239 object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); 238 240 dev = DEVICE(&os->nvram); 239 241 qdev_prop_set_uint32(dev, "size", 0x2000); ··· 293 295 return; 294 296 } 295 297 296 - sysbus_dev = SYS_BUS_DEVICE(&s->cuda); 297 - sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, 298 - NEWWORLD_CUDA_IRQ)); 299 - 300 298 sysbus_dev = SYS_BUS_DEVICE(&s->escc); 301 299 sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, 302 300 NEWWORLD_ESCCB_IRQ)); ··· 341 339 memory_region_add_subregion(&s->bar, 0x50, 342 340 sysbus_mmio_get_region(sysbus_dev, 0)); 343 341 object_property_set_bool(OBJECT(&ns->gpio), true, "realized", &err); 342 + 343 + /* PMU */ 344 + object_initialize(&s->pmu, sizeof(s->pmu), TYPE_VIA_PMU); 345 + object_property_set_link(OBJECT(&s->pmu), OBJECT(sysbus_dev), "gpio", 346 + &error_abort); 347 + qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); 348 + qdev_set_parent_bus(DEVICE(&s->pmu), sysbus_get_default()); 349 + object_property_add_child(OBJECT(s), "pmu", OBJECT(&s->pmu), NULL); 350 + 351 + object_property_set_bool(OBJECT(&s->pmu), true, "realized", &err); 352 + if (err) { 353 + error_propagate(errp, err); 354 + return; 355 + } 356 + sysbus_dev = SYS_BUS_DEVICE(&s->pmu); 357 + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, 358 + NEWWORLD_PMU_IRQ)); 359 + memory_region_add_subregion(&s->bar, 0x16000, 360 + sysbus_mmio_get_region(sysbus_dev, 0)); 361 + } else { 362 + /* CUDA */ 363 + object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); 364 + qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); 365 + object_property_add_child(OBJECT(s), "cuda", OBJECT(&s->cuda), NULL); 366 + qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", 367 + s->frequency); 368 + 369 + object_property_set_bool(OBJECT(&s->cuda), true, "realized", &err); 370 + if (err) { 371 + error_propagate(errp, err); 372 + return; 373 + } 374 + sysbus_dev = SYS_BUS_DEVICE(&s->cuda); 375 + sysbus_connect_irq(sysbus_dev, 0, qdev_get_gpio_in(pic_dev, 376 + NEWWORLD_CUDA_IRQ)); 377 + memory_region_add_subregion(&s->bar, 0x16000, 378 + sysbus_mmio_get_region(sysbus_dev, 0)); 344 379 } 345 380 } 346 381 ··· 368 403 MacIOState *s = MACIO(obj); 369 404 370 405 memory_region_init(&s->bar, obj, "macio", 0x80000); 371 - 372 - object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); 373 - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); 374 - object_property_add_child(obj, "cuda", OBJECT(&s->cuda), NULL); 375 406 376 407 object_initialize(&s->dbdma, sizeof(s->dbdma), TYPE_MAC_DBDMA); 377 408 qdev_set_parent_bus(DEVICE(&s->dbdma), sysbus_get_default());
+871
hw/misc/macio/pmu.c
··· 1 + /* 2 + * QEMU PowerMac PMU device support 3 + * 4 + * Copyright (c) 2016 Benjamin Herrenschmidt, IBM Corp. 5 + * Copyright (c) 2018 Mark Cave-Ayland 6 + * 7 + * Based on the CUDA device by: 8 + * 9 + * Copyright (c) 2004-2007 Fabrice Bellard 10 + * Copyright (c) 2007 Jocelyn Mayer 11 + * 12 + * Permission is hereby granted, free of charge, to any person obtaining a copy 13 + * of this software and associated documentation files (the "Software"), to deal 14 + * in the Software without restriction, including without limitation the rights 15 + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 + * copies of the Software, and to permit persons to whom the Software is 17 + * furnished to do so, subject to the following conditions: 18 + * 19 + * The above copyright notice and this permission notice shall be included in 20 + * all copies or substantial portions of the Software. 21 + * 22 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 25 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 + * THE SOFTWARE. 29 + */ 30 + 31 + #include "qemu/osdep.h" 32 + #include "hw/hw.h" 33 + #include "hw/ppc/mac.h" 34 + #include "hw/input/adb.h" 35 + #include "hw/misc/mos6522.h" 36 + #include "hw/misc/macio/gpio.h" 37 + #include "hw/misc/macio/pmu.h" 38 + #include "qemu/timer.h" 39 + #include "sysemu/sysemu.h" 40 + #include "qemu/cutils.h" 41 + #include "qemu/log.h" 42 + #include "trace.h" 43 + 44 + 45 + /* Bits in B data register: all active low */ 46 + #define TACK 0x08 /* Transfer request (input) */ 47 + #define TREQ 0x10 /* Transfer acknowledge (output) */ 48 + 49 + /* PMU returns time_t's offset from Jan 1, 1904, not 1970 */ 50 + #define RTC_OFFSET 2082844800 51 + 52 + #define VIA_TIMER_FREQ (4700000 / 6) 53 + 54 + static void via_update_irq(PMUState *s) 55 + { 56 + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); 57 + MOS6522State *ms = MOS6522(mps); 58 + 59 + bool new_state = !!(ms->ifr & ms->ier & (SR_INT | T1_INT | T2_INT)); 60 + 61 + if (new_state != s->via_irq_state) { 62 + s->via_irq_state = new_state; 63 + qemu_set_irq(s->via_irq, new_state); 64 + } 65 + } 66 + 67 + static void via_set_sr_int(void *opaque) 68 + { 69 + PMUState *s = opaque; 70 + MOS6522PMUState *mps = MOS6522_PMU(&s->mos6522_pmu); 71 + MOS6522State *ms = MOS6522(mps); 72 + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); 73 + 74 + mdc->set_sr_int(ms); 75 + } 76 + 77 + static void pmu_update_extirq(PMUState *s) 78 + { 79 + if ((s->intbits & s->intmask) != 0) { 80 + macio_set_gpio(s->gpio, 1, false); 81 + } else { 82 + macio_set_gpio(s->gpio, 1, true); 83 + } 84 + } 85 + 86 + static void pmu_adb_poll(void *opaque) 87 + { 88 + PMUState *s = opaque; 89 + int olen; 90 + 91 + if (!(s->intbits & PMU_INT_ADB)) { 92 + olen = adb_poll(&s->adb_bus, s->adb_reply, s->adb_poll_mask); 93 + trace_pmu_adb_poll(olen); 94 + 95 + if (olen > 0) { 96 + s->adb_reply_size = olen; 97 + s->intbits |= PMU_INT_ADB | PMU_INT_ADB_AUTO; 98 + pmu_update_extirq(s); 99 + } 100 + } 101 + 102 + timer_mod(s->adb_poll_timer, 103 + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); 104 + } 105 + 106 + static void pmu_one_sec_timer(void *opaque) 107 + { 108 + PMUState *s = opaque; 109 + 110 + trace_pmu_one_sec_timer(); 111 + 112 + s->intbits |= PMU_INT_TICK; 113 + pmu_update_extirq(s); 114 + s->one_sec_target += 1000; 115 + 116 + timer_mod(s->one_sec_timer, s->one_sec_target); 117 + } 118 + 119 + static void pmu_cmd_int_ack(PMUState *s, 120 + const uint8_t *in_data, uint8_t in_len, 121 + uint8_t *out_data, uint8_t *out_len) 122 + { 123 + if (in_len != 0) { 124 + qemu_log_mask(LOG_GUEST_ERROR, 125 + "PMU: INT_ACK command, invalid len: %d want: 0\n", 126 + in_len); 127 + return; 128 + } 129 + 130 + /* Make appropriate reply packet */ 131 + if (s->intbits & PMU_INT_ADB) { 132 + if (!s->adb_reply_size) { 133 + qemu_log_mask(LOG_GUEST_ERROR, 134 + "Odd, PMU_INT_ADB set with no reply in buffer\n"); 135 + } 136 + 137 + memcpy(out_data + 1, s->adb_reply, s->adb_reply_size); 138 + out_data[0] = s->intbits & (PMU_INT_ADB | PMU_INT_ADB_AUTO); 139 + *out_len = s->adb_reply_size + 1; 140 + s->intbits &= ~(PMU_INT_ADB | PMU_INT_ADB_AUTO); 141 + s->adb_reply_size = 0; 142 + } else { 143 + out_data[0] = s->intbits; 144 + s->intbits = 0; 145 + *out_len = 1; 146 + } 147 + 148 + pmu_update_extirq(s); 149 + } 150 + 151 + static void pmu_cmd_set_int_mask(PMUState *s, 152 + const uint8_t *in_data, uint8_t in_len, 153 + uint8_t *out_data, uint8_t *out_len) 154 + { 155 + if (in_len != 1) { 156 + qemu_log_mask(LOG_GUEST_ERROR, 157 + "PMU: SET_INT_MASK command, invalid len: %d want: 1\n", 158 + in_len); 159 + return; 160 + } 161 + 162 + trace_pmu_cmd_set_int_mask(s->intmask); 163 + s->intmask = in_data[0]; 164 + 165 + pmu_update_extirq(s); 166 + } 167 + 168 + static void pmu_cmd_set_adb_autopoll(PMUState *s, uint16_t mask) 169 + { 170 + trace_pmu_cmd_set_adb_autopoll(mask); 171 + 172 + if (s->autopoll_mask == mask) { 173 + return; 174 + } 175 + 176 + s->autopoll_mask = mask; 177 + if (mask) { 178 + timer_mod(s->adb_poll_timer, 179 + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 30); 180 + } else { 181 + timer_del(s->adb_poll_timer); 182 + } 183 + } 184 + 185 + static void pmu_cmd_adb(PMUState *s, 186 + const uint8_t *in_data, uint8_t in_len, 187 + uint8_t *out_data, uint8_t *out_len) 188 + { 189 + int len, adblen; 190 + uint8_t adb_cmd[255]; 191 + 192 + if (in_len < 2) { 193 + qemu_log_mask(LOG_GUEST_ERROR, 194 + "PMU: ADB PACKET, invalid len: %d want at least 2\n", 195 + in_len); 196 + return; 197 + } 198 + 199 + *out_len = 0; 200 + 201 + if (!s->has_adb) { 202 + trace_pmu_cmd_adb_nobus(); 203 + return; 204 + } 205 + 206 + /* Set autopoll is a special form of the command */ 207 + if (in_data[0] == 0 && in_data[1] == 0x86) { 208 + uint16_t mask = in_data[2]; 209 + mask = (mask << 8) | in_data[3]; 210 + if (in_len != 4) { 211 + qemu_log_mask(LOG_GUEST_ERROR, 212 + "PMU: ADB Autopoll requires 4 bytes, got %d\n", 213 + in_len); 214 + return; 215 + } 216 + 217 + pmu_cmd_set_adb_autopoll(s, mask); 218 + return; 219 + } 220 + 221 + trace_pmu_cmd_adb_request(in_len, in_data[0], in_data[1], in_data[2], 222 + in_data[3], in_data[4]); 223 + 224 + *out_len = 0; 225 + 226 + /* Check ADB len */ 227 + adblen = in_data[2]; 228 + if (adblen > (in_len - 3)) { 229 + qemu_log_mask(LOG_GUEST_ERROR, 230 + "PMU: ADB len is %d > %d (in_len -3)...erroring\n", 231 + adblen, in_len - 3); 232 + len = -1; 233 + } else if (adblen > 252) { 234 + qemu_log_mask(LOG_GUEST_ERROR, "PMU: ADB command too big!\n"); 235 + len = -1; 236 + } else { 237 + /* Format command */ 238 + adb_cmd[0] = in_data[0]; 239 + memcpy(&adb_cmd[1], &in_data[3], in_len - 3); 240 + len = adb_request(&s->adb_bus, s->adb_reply + 2, adb_cmd, in_len - 2); 241 + 242 + trace_pmu_cmd_adb_reply(len); 243 + } 244 + 245 + if (len > 0) { 246 + /* XXX Check this */ 247 + s->adb_reply_size = len + 2; 248 + s->adb_reply[0] = 0x01; 249 + s->adb_reply[1] = len; 250 + } else { 251 + /* XXX Check this */ 252 + s->adb_reply_size = 1; 253 + s->adb_reply[0] = 0x00; 254 + } 255 + 256 + s->intbits |= PMU_INT_ADB; 257 + pmu_update_extirq(s); 258 + } 259 + 260 + static void pmu_cmd_adb_poll_off(PMUState *s, 261 + const uint8_t *in_data, uint8_t in_len, 262 + uint8_t *out_data, uint8_t *out_len) 263 + { 264 + if (in_len != 0) { 265 + qemu_log_mask(LOG_GUEST_ERROR, 266 + "PMU: ADB POLL OFF command, invalid len: %d want: 0\n", 267 + in_len); 268 + return; 269 + } 270 + 271 + if (s->has_adb && s->autopoll_mask) { 272 + timer_del(s->adb_poll_timer); 273 + s->autopoll_mask = false; 274 + } 275 + } 276 + 277 + static void pmu_cmd_shutdown(PMUState *s, 278 + const uint8_t *in_data, uint8_t in_len, 279 + uint8_t *out_data, uint8_t *out_len) 280 + { 281 + if (in_len != 4) { 282 + qemu_log_mask(LOG_GUEST_ERROR, 283 + "PMU: SHUTDOWN command, invalid len: %d want: 4\n", 284 + in_len); 285 + return; 286 + } 287 + 288 + *out_len = 1; 289 + out_data[0] = 0; 290 + 291 + if (in_data[0] != 'M' || in_data[1] != 'A' || in_data[2] != 'T' || 292 + in_data[3] != 'T') { 293 + 294 + qemu_log_mask(LOG_GUEST_ERROR, 295 + "PMU: SHUTDOWN command, Bad MATT signature\n"); 296 + return; 297 + } 298 + 299 + qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); 300 + } 301 + 302 + static void pmu_cmd_reset(PMUState *s, 303 + const uint8_t *in_data, uint8_t in_len, 304 + uint8_t *out_data, uint8_t *out_len) 305 + { 306 + if (in_len != 0) { 307 + qemu_log_mask(LOG_GUEST_ERROR, 308 + "PMU: RESET command, invalid len: %d want: 0\n", 309 + in_len); 310 + return; 311 + } 312 + 313 + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); 314 + } 315 + 316 + static void pmu_cmd_get_rtc(PMUState *s, 317 + const uint8_t *in_data, uint8_t in_len, 318 + uint8_t *out_data, uint8_t *out_len) 319 + { 320 + uint32_t ti; 321 + 322 + if (in_len != 0) { 323 + qemu_log_mask(LOG_GUEST_ERROR, 324 + "PMU: GET_RTC command, invalid len: %d want: 0\n", 325 + in_len); 326 + return; 327 + } 328 + 329 + ti = s->tick_offset + (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) 330 + / NANOSECONDS_PER_SECOND); 331 + out_data[0] = ti >> 24; 332 + out_data[1] = ti >> 16; 333 + out_data[2] = ti >> 8; 334 + out_data[3] = ti; 335 + *out_len = 4; 336 + } 337 + 338 + static void pmu_cmd_set_rtc(PMUState *s, 339 + const uint8_t *in_data, uint8_t in_len, 340 + uint8_t *out_data, uint8_t *out_len) 341 + { 342 + uint32_t ti; 343 + 344 + if (in_len != 4) { 345 + qemu_log_mask(LOG_GUEST_ERROR, 346 + "PMU: SET_RTC command, invalid len: %d want: 4\n", 347 + in_len); 348 + return; 349 + } 350 + 351 + ti = (((uint32_t)in_data[0]) << 24) + (((uint32_t)in_data[1]) << 16) 352 + + (((uint32_t)in_data[2]) << 8) + in_data[3]; 353 + 354 + s->tick_offset = ti - (qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) 355 + / NANOSECONDS_PER_SECOND); 356 + } 357 + 358 + static void pmu_cmd_system_ready(PMUState *s, 359 + const uint8_t *in_data, uint8_t in_len, 360 + uint8_t *out_data, uint8_t *out_len) 361 + { 362 + /* Do nothing */ 363 + } 364 + 365 + static void pmu_cmd_get_version(PMUState *s, 366 + const uint8_t *in_data, uint8_t in_len, 367 + uint8_t *out_data, uint8_t *out_len) 368 + { 369 + *out_len = 1; 370 + *out_data = 1; /* ??? Check what Apple does */ 371 + } 372 + 373 + static void pmu_cmd_power_events(PMUState *s, 374 + const uint8_t *in_data, uint8_t in_len, 375 + uint8_t *out_data, uint8_t *out_len) 376 + { 377 + if (in_len < 1) { 378 + qemu_log_mask(LOG_GUEST_ERROR, 379 + "PMU: POWER EVENTS command, invalid len %d, want at least 1\n", 380 + in_len); 381 + return; 382 + } 383 + 384 + switch (in_data[0]) { 385 + /* Dummies for now */ 386 + case PMU_PWR_GET_POWERUP_EVENTS: 387 + *out_len = 2; 388 + out_data[0] = 0; 389 + out_data[1] = 0; 390 + break; 391 + case PMU_PWR_SET_POWERUP_EVENTS: 392 + case PMU_PWR_CLR_POWERUP_EVENTS: 393 + break; 394 + case PMU_PWR_GET_WAKEUP_EVENTS: 395 + *out_len = 2; 396 + out_data[0] = 0; 397 + out_data[1] = 0; 398 + break; 399 + case PMU_PWR_SET_WAKEUP_EVENTS: 400 + case PMU_PWR_CLR_WAKEUP_EVENTS: 401 + break; 402 + default: 403 + qemu_log_mask(LOG_GUEST_ERROR, 404 + "PMU: POWER EVENTS unknown subcommand 0x%02x\n", 405 + in_data[0]); 406 + } 407 + } 408 + 409 + static void pmu_cmd_get_cover(PMUState *s, 410 + const uint8_t *in_data, uint8_t in_len, 411 + uint8_t *out_data, uint8_t *out_len) 412 + { 413 + /* Not 100% sure here, will have to check what a real Mac 414 + * returns other than byte 0 bit 0 is LID closed on laptops 415 + */ 416 + *out_len = 1; 417 + *out_data = 0x00; 418 + } 419 + 420 + static void pmu_cmd_download_status(PMUState *s, 421 + const uint8_t *in_data, uint8_t in_len, 422 + uint8_t *out_data, uint8_t *out_len) 423 + { 424 + /* This has to do with PMU firmware updates as far as I can tell. 425 + * 426 + * We return 0x62 which is what OpenPMU expects 427 + */ 428 + *out_len = 1; 429 + *out_data = 0x62; 430 + } 431 + 432 + static void pmu_cmd_read_pmu_ram(PMUState *s, 433 + const uint8_t *in_data, uint8_t in_len, 434 + uint8_t *out_data, uint8_t *out_len) 435 + { 436 + if (in_len < 3) { 437 + qemu_log_mask(LOG_GUEST_ERROR, 438 + "PMU: READ_PMU_RAM command, invalid len %d, expected 3\n", 439 + in_len); 440 + return; 441 + } 442 + 443 + qemu_log_mask(LOG_GUEST_ERROR, 444 + "PMU: Unsupported READ_PMU_RAM, args: %02x %02x %02x\n", 445 + in_data[0], in_data[1], in_data[2]); 446 + 447 + *out_len = 0; 448 + } 449 + 450 + /* description of commands */ 451 + typedef struct PMUCmdHandler { 452 + uint8_t command; 453 + const char *name; 454 + void (*handler)(PMUState *s, 455 + const uint8_t *in_args, uint8_t in_len, 456 + uint8_t *out_args, uint8_t *out_len); 457 + } PMUCmdHandler; 458 + 459 + static const PMUCmdHandler PMUCmdHandlers[] = { 460 + { PMU_INT_ACK, "INT ACK", pmu_cmd_int_ack }, 461 + { PMU_SET_INTR_MASK, "SET INT MASK", pmu_cmd_set_int_mask }, 462 + { PMU_ADB_CMD, "ADB COMMAND", pmu_cmd_adb }, 463 + { PMU_ADB_POLL_OFF, "ADB POLL OFF", pmu_cmd_adb_poll_off }, 464 + { PMU_RESET, "REBOOT", pmu_cmd_reset }, 465 + { PMU_SHUTDOWN, "SHUTDOWN", pmu_cmd_shutdown }, 466 + { PMU_READ_RTC, "GET RTC", pmu_cmd_get_rtc }, 467 + { PMU_SET_RTC, "SET RTC", pmu_cmd_set_rtc }, 468 + { PMU_SYSTEM_READY, "SYSTEM READY", pmu_cmd_system_ready }, 469 + { PMU_GET_VERSION, "GET VERSION", pmu_cmd_get_version }, 470 + { PMU_POWER_EVENTS, "POWER EVENTS", pmu_cmd_power_events }, 471 + { PMU_GET_COVER, "GET_COVER", pmu_cmd_get_cover }, 472 + { PMU_DOWNLOAD_STATUS, "DOWNLOAD STATUS", pmu_cmd_download_status }, 473 + { PMU_READ_PMU_RAM, "READ PMGR RAM", pmu_cmd_read_pmu_ram }, 474 + }; 475 + 476 + static void pmu_dispatch_cmd(PMUState *s) 477 + { 478 + unsigned int i; 479 + 480 + /* No response by default */ 481 + s->cmd_rsp_sz = 0; 482 + 483 + for (i = 0; i < ARRAY_SIZE(PMUCmdHandlers); i++) { 484 + const PMUCmdHandler *desc = &PMUCmdHandlers[i]; 485 + 486 + if (desc->command != s->cmd) { 487 + continue; 488 + } 489 + 490 + trace_pmu_dispatch_cmd(desc->name); 491 + desc->handler(s, s->cmd_buf, s->cmd_buf_pos, 492 + s->cmd_rsp, &s->cmd_rsp_sz); 493 + 494 + if (s->rsplen != -1 && s->rsplen != s->cmd_rsp_sz) { 495 + trace_pmu_debug_protocol_string("QEMU internal cmd resp mismatch!"); 496 + } else { 497 + trace_pmu_debug_protocol_resp_size(s->cmd_rsp_sz); 498 + } 499 + 500 + return; 501 + } 502 + 503 + trace_pmu_dispatch_unknown_cmd(s->cmd); 504 + 505 + /* Manufacture fake response with 0's */ 506 + if (s->rsplen == -1) { 507 + s->cmd_rsp_sz = 0; 508 + } else { 509 + s->cmd_rsp_sz = s->rsplen; 510 + memset(s->cmd_rsp, 0, s->rsplen); 511 + } 512 + } 513 + 514 + static void pmu_update(PMUState *s) 515 + { 516 + MOS6522PMUState *mps = &s->mos6522_pmu; 517 + MOS6522State *ms = MOS6522(mps); 518 + 519 + /* Only react to changes in reg B */ 520 + if (ms->b == s->last_b) { 521 + return; 522 + } 523 + s->last_b = ms->b; 524 + 525 + /* Check the TREQ / TACK state */ 526 + switch (ms->b & (TREQ | TACK)) { 527 + case TREQ: 528 + /* This is an ack release, handle it and bail out */ 529 + ms->b |= TACK; 530 + s->last_b = ms->b; 531 + 532 + trace_pmu_debug_protocol_string("handshake: TREQ high, setting TACK"); 533 + return; 534 + case TACK: 535 + /* This is a valid request, handle below */ 536 + break; 537 + case TREQ | TACK: 538 + /* This is an idle state */ 539 + return; 540 + default: 541 + /* Invalid state, log and ignore */ 542 + trace_pmu_debug_protocol_error(ms->b); 543 + return; 544 + } 545 + 546 + /* If we wanted to handle commands asynchronously, this is where 547 + * we would delay the clearing of TACK until we are ready to send 548 + * the response 549 + */ 550 + 551 + /* We have a request, handshake TACK so we don't stay in 552 + * an invalid state. If we were concurrent with the OS we 553 + * should only do this after we grabbed the SR but that isn't 554 + * a problem here. 555 + */ 556 + 557 + trace_pmu_debug_protocol_clear_treq(s->cmd_state); 558 + 559 + ms->b &= ~TACK; 560 + s->last_b = ms->b; 561 + 562 + /* Act according to state */ 563 + switch (s->cmd_state) { 564 + case pmu_state_idle: 565 + if (!(ms->acr & SR_OUT)) { 566 + trace_pmu_debug_protocol_string("protocol error! " 567 + "state idle, ACR reading"); 568 + break; 569 + } 570 + 571 + s->cmd = ms->sr; 572 + via_set_sr_int(s); 573 + s->cmdlen = pmu_data_len[s->cmd][0]; 574 + s->rsplen = pmu_data_len[s->cmd][1]; 575 + s->cmd_buf_pos = 0; 576 + s->cmd_rsp_pos = 0; 577 + s->cmd_state = pmu_state_cmd; 578 + 579 + trace_pmu_debug_protocol_cmd(s->cmd, s->cmdlen, s->rsplen); 580 + break; 581 + 582 + case pmu_state_cmd: 583 + if (!(ms->acr & SR_OUT)) { 584 + trace_pmu_debug_protocol_string("protocol error! " 585 + "state cmd, ACR reading"); 586 + break; 587 + } 588 + 589 + if (s->cmdlen == -1) { 590 + trace_pmu_debug_protocol_cmdlen(ms->sr); 591 + 592 + s->cmdlen = ms->sr; 593 + if (s->cmdlen > sizeof(s->cmd_buf)) { 594 + trace_pmu_debug_protocol_cmd_toobig(s->cmdlen); 595 + } 596 + } else if (s->cmd_buf_pos < sizeof(s->cmd_buf)) { 597 + s->cmd_buf[s->cmd_buf_pos++] = ms->sr; 598 + } 599 + 600 + via_set_sr_int(s); 601 + break; 602 + 603 + case pmu_state_rsp: 604 + if (ms->acr & SR_OUT) { 605 + trace_pmu_debug_protocol_string("protocol error! " 606 + "state resp, ACR writing"); 607 + break; 608 + } 609 + 610 + if (s->rsplen == -1) { 611 + trace_pmu_debug_protocol_cmd_send_resp_size(s->cmd_rsp_sz); 612 + 613 + ms->sr = s->cmd_rsp_sz; 614 + s->rsplen = s->cmd_rsp_sz; 615 + } else if (s->cmd_rsp_pos < s->cmd_rsp_sz) { 616 + trace_pmu_debug_protocol_cmd_send_resp(s->cmd_rsp_pos, s->rsplen); 617 + 618 + ms->sr = s->cmd_rsp[s->cmd_rsp_pos++]; 619 + } 620 + 621 + via_set_sr_int(s); 622 + break; 623 + } 624 + 625 + /* Check for state completion */ 626 + if (s->cmd_state == pmu_state_cmd && s->cmdlen == s->cmd_buf_pos) { 627 + trace_pmu_debug_protocol_string("Command reception complete, " 628 + "dispatching..."); 629 + 630 + pmu_dispatch_cmd(s); 631 + s->cmd_state = pmu_state_rsp; 632 + } 633 + 634 + if (s->cmd_state == pmu_state_rsp && s->rsplen == s->cmd_rsp_pos) { 635 + trace_pmu_debug_protocol_cmd_resp_complete(ms->ier); 636 + 637 + s->cmd_state = pmu_state_idle; 638 + } 639 + } 640 + 641 + static uint64_t mos6522_pmu_read(void *opaque, hwaddr addr, unsigned size) 642 + { 643 + PMUState *s = opaque; 644 + MOS6522PMUState *mps = &s->mos6522_pmu; 645 + MOS6522State *ms = MOS6522(mps); 646 + 647 + addr = (addr >> 9) & 0xf; 648 + return mos6522_read(ms, addr, size); 649 + } 650 + 651 + static void mos6522_pmu_write(void *opaque, hwaddr addr, uint64_t val, 652 + unsigned size) 653 + { 654 + PMUState *s = opaque; 655 + MOS6522PMUState *mps = &s->mos6522_pmu; 656 + MOS6522State *ms = MOS6522(mps); 657 + 658 + addr = (addr >> 9) & 0xf; 659 + mos6522_write(ms, addr, val, size); 660 + } 661 + 662 + static const MemoryRegionOps mos6522_pmu_ops = { 663 + .read = mos6522_pmu_read, 664 + .write = mos6522_pmu_write, 665 + .endianness = DEVICE_BIG_ENDIAN, 666 + .impl = { 667 + .min_access_size = 1, 668 + .max_access_size = 1, 669 + }, 670 + }; 671 + 672 + static bool pmu_adb_state_needed(void *opaque) 673 + { 674 + PMUState *s = opaque; 675 + 676 + return s->has_adb; 677 + } 678 + 679 + static const VMStateDescription vmstate_pmu_adb = { 680 + .name = "pmu/adb", 681 + .version_id = 0, 682 + .minimum_version_id = 0, 683 + .needed = pmu_adb_state_needed, 684 + .fields = (VMStateField[]) { 685 + VMSTATE_UINT16(adb_poll_mask, PMUState), 686 + VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), 687 + VMSTATE_UINT8(adb_reply_size, PMUState), 688 + VMSTATE_BUFFER(adb_reply, PMUState), 689 + } 690 + }; 691 + 692 + static const VMStateDescription vmstate_pmu = { 693 + .name = "pmu", 694 + .version_id = 0, 695 + .minimum_version_id = 0, 696 + .fields = (VMStateField[]) { 697 + VMSTATE_STRUCT(mos6522_pmu.parent_obj, PMUState, 0, vmstate_mos6522, 698 + MOS6522State), 699 + VMSTATE_UINT8(last_b, PMUState), 700 + VMSTATE_UINT8(cmd, PMUState), 701 + VMSTATE_UINT32(cmdlen, PMUState), 702 + VMSTATE_UINT32(rsplen, PMUState), 703 + VMSTATE_UINT8(cmd_buf_pos, PMUState), 704 + VMSTATE_BUFFER(cmd_buf, PMUState), 705 + VMSTATE_UINT8(cmd_rsp_pos, PMUState), 706 + VMSTATE_UINT8(cmd_rsp_sz, PMUState), 707 + VMSTATE_BUFFER(cmd_rsp, PMUState), 708 + VMSTATE_UINT8(intbits, PMUState), 709 + VMSTATE_UINT8(intmask, PMUState), 710 + VMSTATE_UINT8(autopoll_rate_ms, PMUState), 711 + VMSTATE_UINT8(autopoll_mask, PMUState), 712 + VMSTATE_UINT32(tick_offset, PMUState), 713 + VMSTATE_TIMER_PTR(one_sec_timer, PMUState), 714 + VMSTATE_INT64(one_sec_target, PMUState), 715 + VMSTATE_END_OF_LIST() 716 + }, 717 + .subsections = (const VMStateDescription * []) { 718 + &vmstate_pmu_adb, 719 + } 720 + }; 721 + 722 + static void pmu_reset(DeviceState *dev) 723 + { 724 + PMUState *s = VIA_PMU(dev); 725 + 726 + /* OpenBIOS needs to do this? MacOS 9 needs it */ 727 + s->intmask = PMU_INT_ADB | PMU_INT_TICK; 728 + s->intbits = 0; 729 + 730 + s->cmd_state = pmu_state_idle; 731 + s->autopoll_mask = 0; 732 + } 733 + 734 + static void pmu_realize(DeviceState *dev, Error **errp) 735 + { 736 + PMUState *s = VIA_PMU(dev); 737 + SysBusDevice *sbd; 738 + MOS6522State *ms; 739 + DeviceState *d; 740 + struct tm tm; 741 + 742 + /* Pass IRQ from 6522 */ 743 + d = DEVICE(&s->mos6522_pmu); 744 + ms = MOS6522(d); 745 + sbd = SYS_BUS_DEVICE(s); 746 + sysbus_pass_irq(sbd, SYS_BUS_DEVICE(ms)); 747 + 748 + qemu_get_timedate(&tm, 0); 749 + s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; 750 + s->one_sec_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_one_sec_timer, s); 751 + s->one_sec_target = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000; 752 + timer_mod(s->one_sec_timer, s->one_sec_target); 753 + 754 + if (s->has_adb) { 755 + qbus_create_inplace(&s->adb_bus, sizeof(s->adb_bus), TYPE_ADB_BUS, 756 + DEVICE(dev), "adb.0"); 757 + s->adb_poll_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, pmu_adb_poll, s); 758 + s->adb_poll_mask = 0xffff; 759 + s->autopoll_rate_ms = 20; 760 + } 761 + } 762 + 763 + static void pmu_init(Object *obj) 764 + { 765 + SysBusDevice *d = SYS_BUS_DEVICE(obj); 766 + PMUState *s = VIA_PMU(obj); 767 + 768 + object_property_add_link(obj, "gpio", TYPE_MACIO_GPIO, 769 + (Object **) &s->gpio, 770 + qdev_prop_allow_set_link_before_realize, 771 + 0, NULL); 772 + 773 + object_initialize(&s->mos6522_pmu, sizeof(s->mos6522_pmu), 774 + TYPE_MOS6522_PMU); 775 + qdev_set_parent_bus(DEVICE(&s->mos6522_pmu), sysbus_get_default()); 776 + 777 + memory_region_init_io(&s->mem, obj, &mos6522_pmu_ops, s, "via-pmu", 778 + 0x2000); 779 + sysbus_init_mmio(d, &s->mem); 780 + } 781 + 782 + static Property pmu_properties[] = { 783 + DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), 784 + DEFINE_PROP_END_OF_LIST() 785 + }; 786 + 787 + static void pmu_class_init(ObjectClass *oc, void *data) 788 + { 789 + DeviceClass *dc = DEVICE_CLASS(oc); 790 + 791 + dc->realize = pmu_realize; 792 + dc->reset = pmu_reset; 793 + dc->vmsd = &vmstate_pmu; 794 + dc->props = pmu_properties; 795 + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); 796 + } 797 + 798 + static const TypeInfo pmu_type_info = { 799 + .name = TYPE_VIA_PMU, 800 + .parent = TYPE_SYS_BUS_DEVICE, 801 + .instance_size = sizeof(PMUState), 802 + .instance_init = pmu_init, 803 + .class_init = pmu_class_init, 804 + }; 805 + 806 + static void mos6522_pmu_portB_write(MOS6522State *s) 807 + { 808 + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); 809 + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); 810 + 811 + if ((s->pcr & 0xe0) == 0x20 || (s->pcr & 0xe0) == 0x60) { 812 + s->ifr &= ~CB2_INT; 813 + } 814 + s->ifr &= ~CB1_INT; 815 + 816 + via_update_irq(ps); 817 + pmu_update(ps); 818 + } 819 + 820 + static void mos6522_pmu_portA_write(MOS6522State *s) 821 + { 822 + MOS6522PMUState *mps = container_of(s, MOS6522PMUState, parent_obj); 823 + PMUState *ps = container_of(mps, PMUState, mos6522_pmu); 824 + 825 + if ((s->pcr & 0x0e) == 0x02 || (s->pcr & 0x0e) == 0x06) { 826 + s->ifr &= ~CA2_INT; 827 + } 828 + s->ifr &= ~CA1_INT; 829 + 830 + via_update_irq(ps); 831 + } 832 + 833 + static void mos6522_pmu_reset(DeviceState *dev) 834 + { 835 + MOS6522State *ms = MOS6522(dev); 836 + MOS6522PMUState *mps = container_of(ms, MOS6522PMUState, parent_obj); 837 + PMUState *s = container_of(mps, PMUState, mos6522_pmu); 838 + MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); 839 + 840 + mdc->parent_reset(dev); 841 + 842 + ms->timers[0].frequency = VIA_TIMER_FREQ; 843 + ms->timers[1].frequency = (SCALE_US * 6000) / 4700; 844 + 845 + s->last_b = ms->b = TACK | TREQ; 846 + } 847 + 848 + static void mos6522_pmu_class_init(ObjectClass *oc, void *data) 849 + { 850 + DeviceClass *dc = DEVICE_CLASS(oc); 851 + MOS6522DeviceClass *mdc = MOS6522_DEVICE_CLASS(oc); 852 + 853 + dc->reset = mos6522_pmu_reset; 854 + mdc->portB_write = mos6522_pmu_portB_write; 855 + mdc->portA_write = mos6522_pmu_portA_write; 856 + } 857 + 858 + static const TypeInfo mos6522_pmu_type_info = { 859 + .name = TYPE_MOS6522_PMU, 860 + .parent = TYPE_MOS6522, 861 + .instance_size = sizeof(MOS6522PMUState), 862 + .class_init = mos6522_pmu_class_init, 863 + }; 864 + 865 + static void pmu_register_types(void) 866 + { 867 + type_register_static(&pmu_type_info); 868 + type_register_static(&mos6522_pmu_type_info); 869 + } 870 + 871 + type_init(pmu_register_types)
+21
hw/misc/macio/trace-events
··· 20 20 macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" 21 21 macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 22 22 macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 23 + 24 + # hw/misc/macio/pmu.c 25 + pmu_adb_poll(int olen) "ADB autopoll, olen=%d" 26 + pmu_one_sec_timer(void) "PMU one sec..." 27 + pmu_cmd_set_int_mask(int intmask) "Setting PMU int mask to 0x%02x" 28 + pmu_cmd_set_adb_autopoll(int mask) "ADB set autopoll, mask=0x%04x" 29 + pmu_cmd_adb_nobus(void) "ADB PACKET with no ADB bus!" 30 + pmu_cmd_adb_request(int inlen, int indata0, int indata1, int indata2, int indata3, int indata4) "ADB request: len=%d, cmd=0x%02x, pflags=0x%02x, adblen=%d: 0x%02x 0x%02x..." 31 + pmu_cmd_adb_reply(int len) "ADB reply is %d bytes" 32 + pmu_dispatch_cmd(const char *name) "handling command %s" 33 + pmu_dispatch_unknown_cmd(int cmd) "Unknown PMU command 0x%02x" 34 + pmu_debug_protocol_string(const char *str) "%s" 35 + pmu_debug_protocol_resp_size(int size) "sending %d resp bytes" 36 + pmu_debug_protocol_error(int portB) "protocol error! portB=0x%02x" 37 + pmu_debug_protocol_clear_treq(int state) "TREQ cleared, clearing TACK, state: %d" 38 + pmu_debug_protocol_cmd(int cmd, int cmdlen, int rsplen) "Got command byte 0x%02x, clen=%d, rlen=%d" 39 + pmu_debug_protocol_cmdlen(int len) "got cmd length byte: %d" 40 + pmu_debug_protocol_cmd_toobig(int len) "command too big (%d bytes)" 41 + pmu_debug_protocol_cmd_send_resp_size(int len) "sending length byte: %d" 42 + pmu_debug_protocol_cmd_send_resp(int pos, int len) "sending byte: %d/%d" 43 + pmu_debug_protocol_cmd_resp_complete(int ier) "Response send complete. IER=0x%02x"
+1
hw/ppc/mac.h
··· 59 59 60 60 /* New World IRQs */ 61 61 #define NEWWORLD_CUDA_IRQ 0x19 62 + #define NEWWORLD_PMU_IRQ 0x19 62 63 #define NEWWORLD_ESCCB_IRQ 0x24 63 64 #define NEWWORLD_ESCCA_IRQ 0x25 64 65 #define NEWWORLD_IDE0_IRQ 0xd
+9 -1
hw/ppc/mac_newworld.c
··· 399 399 macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]); 400 400 401 401 if (has_adb) { 402 - dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); 402 + if (has_pmu) { 403 + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "pmu")); 404 + } else { 405 + dev = DEVICE(object_resolve_path_component(OBJECT(macio), "cuda")); 406 + } 407 + 403 408 adb_bus = qdev_get_child_bus(dev, "adb.0"); 404 409 dev = qdev_create(adb_bus, TYPE_ADB_KEYBOARD); 410 + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); 405 411 qdev_init_nofail(dev); 412 + 406 413 dev = qdev_create(adb_bus, TYPE_ADB_MOUSE); 414 + qdev_prop_set_bit(dev, "disable-direct-reg3-writes", has_pmu); 407 415 qdev_init_nofail(dev); 408 416 } 409 417
+2
include/hw/misc/macio/macio.h
··· 30 30 #include "hw/intc/heathrow_pic.h" 31 31 #include "hw/misc/macio/cuda.h" 32 32 #include "hw/misc/macio/gpio.h" 33 + #include "hw/misc/macio/pmu.h" 33 34 #include "hw/ppc/mac_dbdma.h" 34 35 #include "hw/ppc/openpic.h" 35 36 ··· 43 44 44 45 MemoryRegion bar; 45 46 CUDAState cuda; 47 + PMUState pmu; 46 48 DBDMAState dbdma; 47 49 ESCCState escc; 48 50 uint64_t frequency;
+237
include/hw/misc/macio/pmu.h
··· 1 + /* 2 + * Definitions for talking to the PMU. The PMU is a microcontroller 3 + * which controls battery charging and system power on PowerBook 3400 4 + * and 2400 models as well as the RTC and various other things. 5 + * 6 + * Copyright (C) 1998 Paul Mackerras. 7 + * Copyright (C) 2016 Ben Herrenschmidt 8 + */ 9 + 10 + #ifndef PMU_H 11 + #define PMU_H 12 + 13 + /* 14 + * PMU commands 15 + */ 16 + 17 + #define PMU_POWER_CTRL0 0x10 /* control power of some devices */ 18 + #define PMU_POWER_CTRL 0x11 /* control power of some devices */ 19 + #define PMU_ADB_CMD 0x20 /* send ADB packet */ 20 + #define PMU_ADB_POLL_OFF 0x21 /* disable ADB auto-poll */ 21 + #define PMU_WRITE_NVRAM 0x33 /* write non-volatile RAM */ 22 + #define PMU_READ_NVRAM 0x3b /* read non-volatile RAM */ 23 + #define PMU_SET_RTC 0x30 /* set real-time clock */ 24 + #define PMU_READ_RTC 0x38 /* read real-time clock */ 25 + #define PMU_SET_VOLBUTTON 0x40 /* set volume up/down position */ 26 + #define PMU_BACKLIGHT_BRIGHT 0x41 /* set backlight brightness */ 27 + #define PMU_GET_VOLBUTTON 0x48 /* get volume up/down position */ 28 + #define PMU_PCEJECT 0x4c /* eject PC-card from slot */ 29 + #define PMU_BATTERY_STATE 0x6b /* report battery state etc. */ 30 + #define PMU_SMART_BATTERY_STATE 0x6f /* report battery state (new way) */ 31 + #define PMU_SET_INTR_MASK 0x70 /* set PMU interrupt mask */ 32 + #define PMU_INT_ACK 0x78 /* read interrupt bits */ 33 + #define PMU_SHUTDOWN 0x7e /* turn power off */ 34 + #define PMU_CPU_SPEED 0x7d /* control CPU speed on some models */ 35 + #define PMU_SLEEP 0x7f /* put CPU to sleep */ 36 + #define PMU_POWER_EVENTS 0x8f /* Send power-event commands to PMU */ 37 + #define PMU_I2C_CMD 0x9a /* I2C operations */ 38 + #define PMU_RESET 0xd0 /* reset CPU */ 39 + #define PMU_GET_BRIGHTBUTTON 0xd9 /* report brightness up/down pos */ 40 + #define PMU_GET_COVER 0xdc /* report cover open/closed */ 41 + #define PMU_SYSTEM_READY 0xdf /* tell PMU we are awake */ 42 + #define PMU_DOWNLOAD_STATUS 0xe2 /* Called by MacOS during boot... */ 43 + #define PMU_READ_PMU_RAM 0xe8 /* read the PMU RAM... ??? */ 44 + #define PMU_GET_VERSION 0xea /* read the PMU version */ 45 + 46 + /* Bits to use with the PMU_POWER_CTRL0 command */ 47 + #define PMU_POW0_ON 0x80 /* OR this to power ON the device */ 48 + #define PMU_POW0_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ 49 + #define PMU_POW0_HARD_DRIVE 0x04 /* Hard drive power 50 + * (on wallstreet/lombard ?) */ 51 + 52 + /* Bits to use with the PMU_POWER_CTRL command */ 53 + #define PMU_POW_ON 0x80 /* OR this to power ON the device */ 54 + #define PMU_POW_OFF 0x00 /* leave bit 7 to 0 to power it OFF */ 55 + #define PMU_POW_BACKLIGHT 0x01 /* backlight power */ 56 + #define PMU_POW_CHARGER 0x02 /* battery charger power */ 57 + #define PMU_POW_IRLED 0x04 /* IR led power (on wallstreet) */ 58 + #define PMU_POW_MEDIABAY 0x08 /* media bay power 59 + * (wallstreet/lombard ?) */ 60 + 61 + /* Bits in PMU interrupt and interrupt mask bytes */ 62 + #define PMU_INT_PCEJECT 0x04 /* PC-card eject buttons */ 63 + #define PMU_INT_SNDBRT 0x08 /* sound/brightness up/down buttons */ 64 + #define PMU_INT_ADB 0x10 /* ADB autopoll or reply data */ 65 + #define PMU_INT_BATTERY 0x20 /* Battery state change */ 66 + #define PMU_INT_ENVIRONMENT 0x40 /* Environment interrupts */ 67 + #define PMU_INT_TICK 0x80 /* 1-second tick interrupt */ 68 + 69 + /* Other bits in PMU interrupt valid when PMU_INT_ADB is set */ 70 + #define PMU_INT_ADB_AUTO 0x04 /* ADB autopoll, when PMU_INT_ADB */ 71 + #define PMU_INT_WAITING_CHARGER 0x01 /* ??? */ 72 + #define PMU_INT_AUTO_SRQ_POLL 0x02 /* ??? */ 73 + 74 + /* Bits in the environement message (either obtained via PMU_GET_COVER, 75 + * or via PMU_INT_ENVIRONMENT on core99 */ 76 + #define PMU_ENV_LID_CLOSED 0x01 /* The lid is closed */ 77 + 78 + /* I2C related definitions */ 79 + #define PMU_I2C_MODE_SIMPLE 0 80 + #define PMU_I2C_MODE_STDSUB 1 81 + #define PMU_I2C_MODE_COMBINED 2 82 + 83 + #define PMU_I2C_BUS_STATUS 0 84 + #define PMU_I2C_BUS_SYSCLK 1 85 + #define PMU_I2C_BUS_POWER 2 86 + 87 + #define PMU_I2C_STATUS_OK 0 88 + #define PMU_I2C_STATUS_DATAREAD 1 89 + #define PMU_I2C_STATUS_BUSY 0xfe 90 + 91 + /* Kind of PMU (model) */ 92 + enum { 93 + PMU_UNKNOWN, 94 + PMU_OHARE_BASED, /* 2400, 3400, 3500 (old G3 powerbook) */ 95 + PMU_HEATHROW_BASED, /* PowerBook G3 series */ 96 + PMU_PADDINGTON_BASED, /* 1999 PowerBook G3 */ 97 + PMU_KEYLARGO_BASED, /* Core99 motherboard (PMU99) */ 98 + PMU_68K_V1, /* 68K PMU, version 1 */ 99 + PMU_68K_V2, /* 68K PMU, version 2 */ 100 + }; 101 + 102 + /* PMU PMU_POWER_EVENTS commands */ 103 + enum { 104 + PMU_PWR_GET_POWERUP_EVENTS = 0x00, 105 + PMU_PWR_SET_POWERUP_EVENTS = 0x01, 106 + PMU_PWR_CLR_POWERUP_EVENTS = 0x02, 107 + PMU_PWR_GET_WAKEUP_EVENTS = 0x03, 108 + PMU_PWR_SET_WAKEUP_EVENTS = 0x04, 109 + PMU_PWR_CLR_WAKEUP_EVENTS = 0x05, 110 + }; 111 + 112 + /* Power events wakeup bits */ 113 + enum { 114 + PMU_PWR_WAKEUP_KEY = 0x01, /* Wake on key press */ 115 + PMU_PWR_WAKEUP_AC_INSERT = 0x02, /* Wake on AC adapter plug */ 116 + PMU_PWR_WAKEUP_AC_CHANGE = 0x04, 117 + PMU_PWR_WAKEUP_LID_OPEN = 0x08, 118 + PMU_PWR_WAKEUP_RING = 0x10, 119 + }; 120 + 121 + /* 122 + * This table indicates for each PMU opcode: 123 + * - the number of data bytes to be sent with the command, or -1 124 + * if a length byte should be sent, 125 + * - the number of response bytes which the PMU will return, or 126 + * -1 if it will send a length byte. 127 + */ 128 + 129 + static const int8_t pmu_data_len[256][2] = { 130 + /* 0 1 2 3 4 5 6 7 */ 131 + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 132 + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 133 + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 134 + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, 0}, 135 + {-1, 0},{ 0, 0},{ 2, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0}, 136 + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{ 0, -1}, 137 + { 4, 0},{20, 0},{-1, 0},{ 3, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 138 + { 0, 4},{ 0, 20},{ 2, -1},{ 2, 1},{ 3, -1},{-1, -1},{-1, -1},{ 4, 0}, 139 + { 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 140 + { 0, 1},{ 0, 1},{-1, -1},{ 1, 0},{ 1, 0},{-1, -1},{-1, -1},{-1, -1}, 141 + { 1, 0},{ 0, 0},{ 2, 0},{ 2, 0},{-1, 0},{ 1, 0},{ 3, 0},{ 1, 0}, 142 + { 0, 1},{ 1, 0},{ 0, 2},{ 0, 2},{ 0, -1},{-1, -1},{-1, -1},{-1, -1}, 143 + { 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 144 + { 0, 3},{ 0, 3},{ 0, 2},{ 0, 8},{ 0, -1},{ 0, -1},{-1, -1},{-1, -1}, 145 + { 1, 0},{ 1, 0},{ 1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 146 + { 0, -1},{ 0, -1},{-1, -1},{-1, -1},{-1, -1},{ 5, 1},{ 4, 1},{ 4, 1}, 147 + { 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 148 + { 0, 5},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 149 + { 1, 0},{ 2, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 150 + { 0, 1},{ 0, 1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 151 + { 2, 0},{ 2, 0},{ 2, 0},{ 4, 0},{-1, 0},{ 0, 0},{-1, 0},{-1, 0}, 152 + { 1, 1},{ 1, 0},{ 3, 0},{ 2, 0},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 153 + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 154 + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 155 + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 156 + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 157 + { 0, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 158 + { 1, 1},{ 1, 1},{-1, -1},{-1, -1},{ 0, 1},{ 0, -1},{-1, -1},{-1, -1}, 159 + {-1, 0},{ 4, 0},{ 0, 1},{-1, 0},{-1, 0},{ 4, 0},{-1, 0},{-1, 0}, 160 + { 3, -1},{-1, -1},{ 0, 1},{-1, -1},{ 0, -1},{-1, -1},{-1, -1},{ 0, 0}, 161 + {-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0},{-1, 0}, 162 + {-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1},{-1, -1}, 163 + }; 164 + 165 + /* Command protocol state machine */ 166 + typedef enum { 167 + pmu_state_idle, /* Waiting for command */ 168 + pmu_state_cmd, /* Receiving command */ 169 + pmu_state_rsp, /* Responding to command */ 170 + } PMUCmdState; 171 + 172 + /* MOS6522 PMU */ 173 + typedef struct MOS6522PMUState { 174 + /*< private >*/ 175 + MOS6522State parent_obj; 176 + } MOS6522PMUState; 177 + 178 + #define TYPE_MOS6522_PMU "mos6522-pmu" 179 + #define MOS6522_PMU(obj) OBJECT_CHECK(MOS6522PMUState, (obj), \ 180 + TYPE_MOS6522_PMU) 181 + /** 182 + * PMUState: 183 + * @last_b: last value of B register 184 + */ 185 + 186 + typedef struct PMUState { 187 + /*< private >*/ 188 + SysBusDevice parent_obj; 189 + /*< public >*/ 190 + 191 + MemoryRegion mem; 192 + uint64_t frequency; 193 + qemu_irq via_irq; 194 + bool via_irq_state; 195 + 196 + /* PMU state */ 197 + MOS6522PMUState mos6522_pmu; 198 + 199 + /* PMU low level protocol state */ 200 + PMUCmdState cmd_state; 201 + uint8_t last_b; 202 + uint8_t cmd; 203 + uint32_t cmdlen; 204 + uint32_t rsplen; 205 + uint8_t cmd_buf_pos; 206 + uint8_t cmd_buf[128]; 207 + uint8_t cmd_rsp_pos; 208 + uint8_t cmd_rsp_sz; 209 + uint8_t cmd_rsp[128]; 210 + 211 + /* PMU events/interrupts */ 212 + uint8_t intbits; 213 + uint8_t intmask; 214 + 215 + /* ADB */ 216 + bool has_adb; 217 + ADBBusState adb_bus; 218 + uint16_t adb_poll_mask; 219 + uint8_t autopoll_rate_ms; 220 + uint8_t autopoll_mask; 221 + QEMUTimer *adb_poll_timer; 222 + uint8_t adb_reply_size; 223 + uint8_t adb_reply[ADB_MAX_OUT_LEN]; 224 + 225 + /* RTC */ 226 + uint32_t tick_offset; 227 + QEMUTimer *one_sec_timer; 228 + int64_t one_sec_target; 229 + 230 + /* GPIO */ 231 + MacIOGPIOState *gpio; 232 + } PMUState; 233 + 234 + #define TYPE_VIA_PMU "via-pmu" 235 + #define VIA_PMU(obj) OBJECT_CHECK(PMUState, (obj), TYPE_VIA_PMU) 236 + 237 + #endif /* PMU_H */