diff --git a/hw/misc/pic-slave.c b/hw/misc/pic-slave.c new file mode 100644 index 0000000..59912ab --- /dev/null +++ b/hw/misc/pic-slave.c @@ -0,0 +1,244 @@ +/* + * AXIOM Beta PIC16F1718 Slave + * + * Copyright (C) 2017 Herbert Poetzl + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "pic-slave.h" +#include "qapi/error.h" +#include "qapi/visitor.h" + + +static const char *state_desc[] = { + [STOPPED] = "STOPPED", + [ADDRESS] = "ADDRESS", + [READING] = "READING", + [WRITING] = "WRITING", +}; + +static const char *event_desc[] = { + [I2C_START_RECV] = "RECV", + [I2C_START_SEND] = "SEND", + [I2C_FINISH] = "FINISH", + [I2C_NACK] = "NACK", +}; + + +static uint8_t pic_addr_to_idx(PICState *s, int addr) +{ + I2CSlave *i2c = &s->i2c; + + if ((addr >= i2c->address) && + (addr < i2c->address + 0x10)) { + return addr - i2c->address; + } + if ((addr >= i2c->address + 0x20) && + (addr < i2c->address + 0x30)) { + return addr - i2c->address + 0x10;; + } + return 0xFF; +} + +static uint8_t pic_read(PICState *s) +{ + if (s->idx < sizeof(s->reg)) { + rprintf(s, "[0x%02X] -> 0x%02X", + s->idx, s->reg[s->idx]); + return s->reg[s->idx]; + } + return 0xFF; +} + +static void pic_write(PICState *s, uint8_t data) +{ + if (s->idx < sizeof(s->reg)) { + rprintf(s, "[0x%02X] <- 0x%02X", + s->idx, data); + s->reg[s->idx] = data; + } +} + + +static int pic_recv(I2CSlave *i2c) +{ + PICState *s = PIC(i2c); + int ret = 0; + dprintf(s, "state=%s[%d]", + state_desc[s->bus_state], s->bus_state); + + switch (s->bus_state) { + case (READING): + ret = pic_read(s); + dprintf(s, "ret=0x%02X", ret); + return ret; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "read from PIC not in read state"); + return 0xFF; + } + return 0; +} + +static int pic_send(I2CSlave *i2c, uint8_t data) +{ + PICState *s = PIC(i2c); + dprintf(s, "data=0x%02X state=%s[%d]", + data, state_desc[s->bus_state], s->bus_state); + + switch (s->bus_state) { + case (ADDRESS): + // s->addr = data; + pic_write(s, data); + s->bus_state = WRITING; + return 0; + + case (WRITING): + pic_write(s, data); + return 0; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "write to PIC not in writable state"); + return 1; + } + return 0; +} + +static void pic_event(I2CSlave *i2c, enum i2c_event event) +{ + PICState *s = PIC(i2c); + dprintf(s, "event=%s[%d]", event_desc[event], event); + + switch (event) { + case I2C_START_SEND: + s->bus_state = ADDRESS; + break; + + case I2C_START_RECV: + s->bus_state = READING; + break; + + case I2C_FINISH: + s->bus_state = STOPPED; + break; + + case I2C_NACK: + break; + } +} + +static int pic_decode_address(I2CSlave *i2c, uint8_t address) +{ + PICState *s = PIC(i2c); + + dprintf(s, "address=0x%02X", address); + + if ((s->idx = pic_addr_to_idx(s, address)) == 0xFF) + return 1; + + return 0; +} + + + +static void pic_pre_save(void *opaque) +{ + PICState *s = opaque; + dprintf(s, ""); +} + +static int pic_post_load(void *opaque, int version_id) +{ + PICState *s = opaque; + dprintf(s, "vers=%d", version_id); + return 0; +} + +static const VMStateDescription vmstate_pic = { + .name = "PIC", + .version_id = 0, + .minimum_version_id = 0, + .pre_save = pic_pre_save, + .post_load = pic_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT8(idx, PICState), + VMSTATE_UINT8(bus_state, PICState), + VMSTATE_UINT8_ARRAY(reg, PICState, 32), + VMSTATE_I2C_SLAVE(i2c, PICState), + VMSTATE_END_OF_LIST() + } +}; + +static void pic_realize(DeviceState *dev, Error **errp) +{ + I2CSlave *i2c = I2C_SLAVE(dev); + PICState *s = PIC(dev); + + i2c->address_range = 0x30; + dprintf(s, "range=0x%02X", i2c->address_range); +} + +static void pic_reset(DeviceState *dev) +{ + // I2CSlave *i2c = I2C_SLAVE(dev); + PICState *s = PIC(dev); + int i; + + for (i=0; i<32; i++) + s->reg[i] = 255-i; + + s->idx = -1; + s->bus_state = STOPPED; + dprintf(s, "state=%s[%d]", + state_desc[s->bus_state], s->bus_state); +} + + +static void pic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); + + // k->init = pic_init; + k->event = pic_event; + k->recv = pic_recv; + k->send = pic_send; + k->decode_address = pic_decode_address; + + dc->realize = pic_realize; + dc->reset = pic_reset; + dc->vmsd = &vmstate_pic; +} + +static const TypeInfo pic_info = { + .name = TYPE_PIC, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(PICState), + // .instance_init = pic_initfn, + .class_init = pic_class_init, +}; + +static void pic_register_types(void) +{ + type_register_static(&pic_info); +} + +type_init(pic_register_types) diff --git a/hw/misc/pic-slave.h b/hw/misc/pic-slave.h new file mode 100644 index 0000000..1360b97ad --- /dev/null +++ b/hw/misc/pic-slave.h @@ -0,0 +1,46 @@ +/* + * AXIOM Beta PIC16F1718 Slave + * + * Copyright (C) 2017 Herbert Poetzl + * + * This work is licensed under the terms of the GNU GPL, version 2 or + * later. See the COPYING file in the top-level directory. + */ +#ifndef QEMU_PIC_H +#define QEMU_PIC_H + +#include "hw/i2c/i2c.h" + +#define TYPE_PIC "pic" +#define PIC(obj) OBJECT_CHECK(PICState, (obj), TYPE_PIC) + +// #define DEBUG +#ifdef DEBUG +#define dprintf(s,f, ...) printf("[pic@%02X] %s:" f "\n", s->i2c.address, __func__, ##__VA_ARGS__) +#else +#define dprintf(s,f, ...) do { snprintf(NULL, 0, "%p" f, s, ##__VA_ARGS__); } while(0) +#endif + +#define REGLOG +#ifdef REGLOG +#define rprintf(s,f, ...) printf("[pic@%02X] " f "\n", s->i2c.address, ##__VA_ARGS__) +#else +#define rprintf(s,f, ...) do { snprintf(NULL, 0, "%p" f, s, ##__VA_ARGS__); } while(0) +#endif + +typedef enum { + STOPPED, + ADDRESS, + READING, + WRITING, +} PICBusState; + +typedef struct PICState { + I2CSlave i2c; + + uint8_t idx; + uint8_t bus_state; + uint8_t reg[32]; +} PICState; + +#endif diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index ed5f5ea..356f0f0 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -88,3 +88,4 @@ obj-$(CONFIG_EDU) += edu.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o +obj-$(CONFIG_ZYNQ) += pic-slave.o