From a028dd423ee6dfd091a8c63028240832bf10f671 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Wed, 20 Jun 2018 12:24:13 +0200 Subject: ppc/xics: introduce ICP DeviceRealize and DeviceReset handlers This changes the ICP realize and reset handlers in DeviceRealize and DeviceReset handlers. parent handlers are now called from the inheriting classes which is a cleaner object pattern. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- include/hw/ppc/xics.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6cebff47a7..4b04b295a7 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -65,10 +65,11 @@ typedef struct XICSFabric XICSFabric; struct ICPStateClass { DeviceClass parent_class; - void (*realize)(ICPState *icp, Error **errp); + DeviceRealize parent_realize; + DeviceReset parent_reset; + void (*pre_save)(ICPState *icp); int (*post_load)(ICPState *icp, int version_id); - void (*reset)(ICPState *icp); void (*synchronize_state)(ICPState *icp); }; -- cgit v1.2.3-55-g7522 From 0a647b76dbf29e6a20c328cd8676a1ca49526f09 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 25 Jun 2018 11:17:14 +0200 Subject: ppc/xics: introduce a parent_realize in ICSStateClass This makes possible to move the common ICSState code of the realize handlers in the ics-base class. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 37 ++++++++++++++++++++++--------------- hw/intc/xics_kvm.c | 20 +++++++++++++++----- include/hw/ppc/xics.h | 3 ++- 3 files changed, 39 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 063491f387..d6066d561f 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -618,30 +618,31 @@ static void ics_simple_initfn(Object *obj) ics->offset = XICS_IRQ_BASE; } -static void ics_simple_realize(ICSState *ics, Error **errp) +static void ics_simple_realize(DeviceState *dev, Error **errp) { - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); + ICSState *ics = ICS_SIMPLE(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); + ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); qemu_register_reset(ics_simple_reset, ics); } -static Property ics_simple_properties[] = { - DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), - DEFINE_PROP_END_OF_LIST(), -}; - static void ics_simple_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_BASE_CLASS(klass); - isc->realize = ics_simple_realize; - dc->props = ics_simple_properties; + device_class_set_parent_realize(dc, ics_simple_realize, + &isc->parent_realize); + dc->vmsd = &vmstate_ics_simple; isc->reject = ics_simple_reject; isc->resend = ics_simple_resend; @@ -659,7 +660,6 @@ static const TypeInfo ics_simple_info = { static void ics_base_realize(DeviceState *dev, Error **errp) { - ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); ICSState *ics = ICS_BASE(dev); Object *obj; Error *err = NULL; @@ -672,17 +672,24 @@ static void ics_base_realize(DeviceState *dev, Error **errp) } ics->xics = XICS_FABRIC(obj); - - if (icsc->realize) { - icsc->realize(ics, errp); + if (!ics->nr_irqs) { + error_setg(errp, "Number of interrupts needs to be greater 0"); + return; } + ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); } +static Property ics_base_properties[] = { + DEFINE_PROP_UINT32("nr-irqs", ICSState, nr_irqs, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void ics_base_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ics_base_realize; + dc->props = ics_base_properties; } static const TypeInfo ics_base_info = { diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index f511e50a80..1f27eb4979 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -345,13 +345,17 @@ static void ics_kvm_reset(void *dev) ics_set_kvm_state(ics, 1); } -static void ics_kvm_realize(ICSState *ics, Error **errp) +static void ics_kvm_realize(DeviceState *dev, Error **errp) { - if (!ics->nr_irqs) { - error_setg(errp, "Number of interrupts needs to be greater 0"); + ICSState *ics = ICS_KVM(dev); + ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); + Error *local_err = NULL; + + icsc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - ics->irqs = g_malloc0(ics->nr_irqs * sizeof(ICSIRQState)); ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); qemu_register_reset(ics_kvm_reset, ics); @@ -360,8 +364,14 @@ static void ics_kvm_realize(ICSState *ics, Error **errp) static void ics_kvm_class_init(ObjectClass *klass, void *data) { ICSStateClass *icsc = ICS_BASE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * Use device_class_set_parent_realize() when ics-kvm inherits + * directly from ics-base and not from ics-simple anymore. + */ + dc->realize = ics_kvm_realize; - icsc->realize = ics_kvm_realize; icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; icsc->synchronize_state = ics_synchronize_state; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 4b04b295a7..44e96e6400 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -115,7 +115,8 @@ struct PnvICPState { struct ICSStateClass { DeviceClass parent_class; - void (*realize)(ICSState *s, Error **errp); + DeviceRealize parent_realize; + void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); void (*reject)(ICSState *s, uint32_t irq); -- cgit v1.2.3-55-g7522 From eeefd43b3cf342d1696128462a16e092995ff1b5 Mon Sep 17 00:00:00 2001 From: Cédric Le Goater Date: Mon, 25 Jun 2018 11:17:16 +0200 Subject: ppx/xics: introduce a parent_reset in ICSStateClass Just like for the realize handlers, this makes possible to move the common ICSState code of the reset handlers in the ics-base class. Signed-off-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/intc/xics.c | 45 ++++++++++++++++++++++++++++++--------------- hw/intc/xics_kvm.c | 26 ++++++++++---------------- include/hw/ppc/xics.h | 1 + 3 files changed, 41 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 83340770f7..8cfe223153 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -537,23 +537,16 @@ static void ics_simple_eoi(ICSState *ics, uint32_t nr) } } -static void ics_simple_reset(void *dev) +static void ics_simple_reset(DeviceState *dev) { - ICSState *ics = ICS_SIMPLE(dev); - int i; - uint8_t flags[ics->nr_irqs]; + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } - - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); + icsc->parent_reset(dev); +} - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } +static void ics_simple_reset_handler(void *dev) +{ + ics_simple_reset(dev); } static int ics_simple_dispatch_pre_save(void *opaque) @@ -625,7 +618,7 @@ static void ics_simple_realize(DeviceState *dev, Error **errp) ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_simple_reset, ics); + qemu_register_reset(ics_simple_reset_handler, ics); } static void ics_simple_class_init(ObjectClass *klass, void *data) @@ -635,6 +628,8 @@ static void ics_simple_class_init(ObjectClass *klass, void *data) device_class_set_parent_realize(dc, ics_simple_realize, &isc->parent_realize); + device_class_set_parent_reset(dc, ics_simple_reset, + &isc->parent_reset); dc->vmsd = &vmstate_ics_simple; isc->reject = ics_simple_reject; @@ -650,6 +645,25 @@ static const TypeInfo ics_simple_info = { .class_size = sizeof(ICSStateClass), }; +static void ics_base_reset(DeviceState *dev) +{ + ICSState *ics = ICS_BASE(dev); + int i; + uint8_t flags[ics->nr_irqs]; + + for (i = 0; i < ics->nr_irqs; i++) { + flags[i] = ics->irqs[i].flags; + } + + memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); + + for (i = 0; i < ics->nr_irqs; i++) { + ics->irqs[i].priority = 0xff; + ics->irqs[i].saved_priority = 0xff; + ics->irqs[i].flags = flags[i]; + } +} + static void ics_base_realize(DeviceState *dev, Error **errp) { ICSState *ics = ICS_BASE(dev); @@ -689,6 +703,7 @@ static void ics_base_class_init(ObjectClass *klass, void *data) dc->realize = ics_base_realize; dc->props = ics_base_properties; + dc->reset = ics_base_reset; } static const TypeInfo ics_base_info = { diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 1f27eb4979..b314eb7d16 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -324,25 +324,18 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val) } } -static void ics_kvm_reset(void *dev) +static void ics_kvm_reset(DeviceState *dev) { - ICSState *ics = ICS_SIMPLE(dev); - int i; - uint8_t flags[ics->nr_irqs]; - - for (i = 0; i < ics->nr_irqs; i++) { - flags[i] = ics->irqs[i].flags; - } + ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - memset(ics->irqs, 0, sizeof(ICSIRQState) * ics->nr_irqs); + icsc->parent_reset(dev); - for (i = 0; i < ics->nr_irqs; i++) { - ics->irqs[i].priority = 0xff; - ics->irqs[i].saved_priority = 0xff; - ics->irqs[i].flags = flags[i]; - } + ics_set_kvm_state(ICS_KVM(dev), 1); +} - ics_set_kvm_state(ics, 1); +static void ics_kvm_reset_handler(void *dev) +{ + ics_kvm_reset(dev); } static void ics_kvm_realize(DeviceState *dev, Error **errp) @@ -358,7 +351,7 @@ static void ics_kvm_realize(DeviceState *dev, Error **errp) } ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_kvm_reset, ics); + qemu_register_reset(ics_kvm_reset_handler, ics); } static void ics_kvm_class_init(ObjectClass *klass, void *data) @@ -371,6 +364,7 @@ static void ics_kvm_class_init(ObjectClass *klass, void *data) * directly from ics-base and not from ics-simple anymore. */ dc->realize = ics_kvm_realize; + dc->reset = ics_kvm_reset; icsc->pre_save = ics_get_kvm_state; icsc->post_load = ics_set_kvm_state; diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 44e96e6400..6ac8a9392d 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -116,6 +116,7 @@ struct ICSStateClass { DeviceClass parent_class; DeviceRealize parent_realize; + DeviceReset parent_reset; void (*pre_save)(ICSState *s); int (*post_load)(ICSState *s, int version_id); -- cgit v1.2.3-55-g7522 From afb6e20429d5853e79e9a8af4a68b51d14b0c0c1 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 29 Jun 2018 00:38:33 +0200 Subject: ppc4xx_i2c: Rewrite to model hardware more closely Rewrite to make it closer to how real device works so that guest OS drivers can access I2C devices. Previously this was only a hack to allow U-Boot to get past accessing SPD EEPROMs but to support other I2C devices and allow guests to access them we need to model real device more properly. Signed-off-by: BALATON Zoltan Reviewed-by: Cédric Le Goater Signed-off-by: David Gibson --- hw/i2c/ppc4xx_i2c.c | 299 +++++++++++++++++++++++--------------------- include/hw/i2c/ppc4xx_i2c.h | 3 +- 2 files changed, 159 insertions(+), 143 deletions(-) (limited to 'include') diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index fca80d695a..d6dfafab31 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -34,16 +34,50 @@ #define PPC4xx_I2C_MEM_SIZE 18 +enum { + IIC_MDBUF = 0, + /* IIC_SDBUF = 2, */ + IIC_LMADR = 4, + IIC_HMADR, + IIC_CNTL, + IIC_MDCNTL, + IIC_STS, + IIC_EXTSTS, + IIC_LSADR, + IIC_HSADR, + IIC_CLKDIV, + IIC_INTRMSK, + IIC_XFRCNT, + IIC_XTCNTLSS, + IIC_DIRECTCNTL + /* IIC_INTR */ +}; + #define IIC_CNTL_PT (1 << 0) #define IIC_CNTL_READ (1 << 1) #define IIC_CNTL_CHT (1 << 2) #define IIC_CNTL_RPST (1 << 3) +#define IIC_CNTL_AMD (1 << 6) +#define IIC_CNTL_HMT (1 << 7) + +#define IIC_MDCNTL_EINT (1 << 2) +#define IIC_MDCNTL_ESM (1 << 3) +#define IIC_MDCNTL_FMDB (1 << 6) #define IIC_STS_PT (1 << 0) +#define IIC_STS_IRQA (1 << 1) #define IIC_STS_ERR (1 << 2) +#define IIC_STS_MDBF (1 << 4) #define IIC_STS_MDBS (1 << 5) #define IIC_EXTSTS_XFRA (1 << 0) +#define IIC_EXTSTS_BCS_FREE (4 << 4) +#define IIC_EXTSTS_BCS_BUSY (5 << 4) + +#define IIC_INTRMSK_EIMTC (1 << 0) +#define IIC_INTRMSK_EITA (1 << 1) +#define IIC_INTRMSK_EIIC (1 << 2) +#define IIC_INTRMSK_EIHE (1 << 3) #define IIC_XTCNTLSS_SRST (1 << 0) @@ -56,130 +90,83 @@ static void ppc4xx_i2c_reset(DeviceState *s) { PPC4xxI2CState *i2c = PPC4xx_I2C(s); - /* FIXME: Should also reset bus? - *if (s->address != ADDR_RESET) { - * i2c_end_transfer(s->bus); - *} - */ - - i2c->mdata = 0; - i2c->lmadr = 0; - i2c->hmadr = 0; + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + /* [hl][ms]addr are not affected by reset */ i2c->cntl = 0; i2c->mdcntl = 0; i2c->sts = 0; - i2c->extsts = 0x8f; - i2c->lsadr = 0; - i2c->hsadr = 0; + i2c->extsts = IIC_EXTSTS_BCS_FREE; i2c->clkdiv = 0; i2c->intrmsk = 0; i2c->xfrcnt = 0; i2c->xtcntlss = 0; - i2c->directcntl = 0xf; -} - -static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c) -{ - return true; + i2c->directcntl = 0xf; /* all non-reserved bits set */ } static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) { PPC4xxI2CState *i2c = PPC4xx_I2C(opaque); uint64_t ret; + int i; switch (addr) { - case 0: - ret = i2c->mdata; - if (ppc4xx_i2c_is_master(i2c)) { + case IIC_MDBUF: + if (i2c->mdidx < 0) { ret = 0xff; - - if (!(i2c->sts & IIC_STS_MDBS)) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Trying to read " - "without starting transfer\n", - TYPE_PPC4xx_I2C, __func__); - } else { - int pending = (i2c->cntl >> 4) & 3; - - /* get the next byte */ - int byte = i2c_recv(i2c->bus); - - if (byte < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed " - "for device 0x%02x\n", TYPE_PPC4xx_I2C, - __func__, i2c->lmadr); - ret = 0xff; - } else { - ret = byte; - /* Raise interrupt if enabled */ - /*ppc4xx_i2c_raise_interrupt(i2c)*/; - } - - if (!pending) { - i2c->sts &= ~IIC_STS_MDBS; - /*i2c_end_transfer(i2c->bus);*/ - /*} else if (i2c->cntl & (IIC_CNTL_RPST | IIC_CNTL_CHT)) {*/ - } else if (pending) { - /* current smbus implementation doesn't like - multibyte xfer repeated start */ - i2c_end_transfer(i2c->bus); - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } - } - pending--; - i2c->cntl = (i2c->cntl & 0xcf) | (pending << 4); - } - } else { - qemu_log_mask(LOG_UNIMP, "[%s]%s: slave mode not implemented\n", - TYPE_PPC4xx_I2C, __func__); + break; + } + ret = i2c->mdata[0]; + if (i2c->mdidx == 3) { + i2c->sts &= ~IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts &= ~IIC_STS_MDBS; + } + for (i = 0; i < i2c->mdidx; i++) { + i2c->mdata[i] = i2c->mdata[i + 1]; + } + if (i2c->mdidx >= 0) { + i2c->mdidx--; } break; - case 4: + case IIC_LMADR: ret = i2c->lmadr; break; - case 5: + case IIC_HMADR: ret = i2c->hmadr; break; - case 6: + case IIC_CNTL: ret = i2c->cntl; break; - case 7: + case IIC_MDCNTL: ret = i2c->mdcntl; break; - case 8: + case IIC_STS: ret = i2c->sts; break; - case 9: - ret = i2c->extsts; + case IIC_EXTSTS: + ret = i2c_bus_busy(i2c->bus) ? + IIC_EXTSTS_BCS_BUSY : IIC_EXTSTS_BCS_FREE; break; - case 10: + case IIC_LSADR: ret = i2c->lsadr; break; - case 11: + case IIC_HSADR: ret = i2c->hsadr; break; - case 12: + case IIC_CLKDIV: ret = i2c->clkdiv; break; - case 13: + case IIC_INTRMSK: ret = i2c->intrmsk; break; - case 14: + case IIC_XFRCNT: ret = i2c->xfrcnt; break; - case 15: + case IIC_XTCNTLSS: ret = i2c->xtcntlss; break; - case 16: + case IIC_DIRECTCNTL: ret = i2c->directcntl; break; default: @@ -202,99 +189,127 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, PPC4xxI2CState *i2c = opaque; switch (addr) { - case 0: - i2c->mdata = value; - if (!i2c_bus_busy(i2c->bus)) { - /* assume we start a write transfer */ - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 0)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - i2c->sts |= IIC_STS_PT; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; - } + case IIC_MDBUF: + if (i2c->mdidx >= 3) { + break; } - if (i2c_bus_busy(i2c->bus)) { - if (i2c_send(i2c->bus, i2c->mdata)) { - /* if the target return non zero then end the transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c->sts |= IIC_STS_ERR; - i2c->extsts |= IIC_EXTSTS_XFRA; - i2c_end_transfer(i2c->bus); - } + i2c->mdata[++i2c->mdidx] = value; + if (i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } else if (i2c->mdidx == 0) { + i2c->sts |= IIC_STS_MDBS; } break; - case 4: + case IIC_LMADR: i2c->lmadr = value; - if (i2c_bus_busy(i2c->bus)) { - i2c_end_transfer(i2c->bus); - } break; - case 5: + case IIC_HMADR: i2c->hmadr = value; break; - case 6: - i2c->cntl = value; - if (i2c->cntl & IIC_CNTL_PT) { - if (i2c->cntl & IIC_CNTL_READ) { - if (i2c_bus_busy(i2c->bus)) { - /* end previous transfer */ - i2c->sts &= ~IIC_STS_PT; - i2c_end_transfer(i2c->bus); + case IIC_CNTL: + i2c->cntl = value & ~IIC_CNTL_PT; + if (value & IIC_CNTL_AMD) { + qemu_log_mask(LOG_UNIMP, "%s: only 7 bit addresses supported\n", + __func__); + } + if (value & IIC_CNTL_HMT && i2c_bus_busy(i2c->bus)) { + i2c_end_transfer(i2c->bus); + if (i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIHE) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); + } + } else if (value & IIC_CNTL_PT) { + int recv = (value & IIC_CNTL_READ) >> 1; + int tct = value >> 4 & 3; + int i; + + if (recv && (i2c->lmadr >> 1) >= 0x50 && (i2c->lmadr >> 1) < 0x58) { + /* smbus emulation does not like multi byte reads w/o restart */ + value |= IIC_CNTL_RPST; + } + + for (i = 0; i <= tct; i++) { + if (!i2c_bus_busy(i2c->bus)) { + i2c->extsts = IIC_EXTSTS_BCS_FREE; + if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, recv)) { + i2c->sts |= IIC_STS_ERR; + i2c->extsts |= IIC_EXTSTS_XFRA; + break; + } else { + i2c->sts &= ~IIC_STS_ERR; + } } - if (i2c_start_transfer(i2c->bus, i2c->lmadr >> 1, 1)) { - /* if non zero is returned, the adress is not valid */ - i2c->sts &= ~IIC_STS_PT; + if (!(i2c->sts & IIC_STS_ERR) && + i2c_send_recv(i2c->bus, &i2c->mdata[i], !recv)) { i2c->sts |= IIC_STS_ERR; i2c->extsts |= IIC_EXTSTS_XFRA; - } else { - /*i2c->sts |= IIC_STS_PT;*/ - i2c->sts |= IIC_STS_MDBS; - i2c->sts &= ~IIC_STS_ERR; - i2c->extsts = 0; + break; + } + if (value & IIC_CNTL_RPST || !(value & IIC_CNTL_CHT)) { + i2c_end_transfer(i2c->bus); } - } else { - /* we actually already did the write transfer... */ - i2c->sts &= ~IIC_STS_PT; + } + i2c->xfrcnt = i; + i2c->mdidx = i - 1; + if (recv && i2c->mdidx >= 0) { + i2c->sts |= IIC_STS_MDBS; + } + if (recv && i2c->mdidx == 3) { + i2c->sts |= IIC_STS_MDBF; + } + if (i && i2c->mdcntl & IIC_MDCNTL_EINT && + i2c->intrmsk & IIC_INTRMSK_EIMTC) { + i2c->sts |= IIC_STS_IRQA; + qemu_irq_raise(i2c->irq); } } break; - case 7: - i2c->mdcntl = value & 0xdf; + case IIC_MDCNTL: + i2c->mdcntl = value & 0x3d; + if (value & IIC_MDCNTL_ESM) { + qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", + __func__); + } + if (value & IIC_MDCNTL_FMDB) { + i2c->mdidx = -1; + memset(i2c->mdata, 0, ARRAY_SIZE(i2c->mdata)); + i2c->sts &= ~(IIC_STS_MDBF | IIC_STS_MDBS); + } break; - case 8: - i2c->sts &= ~(value & 0xa); + case IIC_STS: + i2c->sts &= ~(value & 0x0a); + if (value & IIC_STS_IRQA && i2c->mdcntl & IIC_MDCNTL_EINT) { + qemu_irq_lower(i2c->irq); + } break; - case 9: + case IIC_EXTSTS: i2c->extsts &= ~(value & 0x8f); break; - case 10: + case IIC_LSADR: i2c->lsadr = value; break; - case 11: + case IIC_HSADR: i2c->hsadr = value; break; - case 12: + case IIC_CLKDIV: i2c->clkdiv = value; break; - case 13: + case IIC_INTRMSK: i2c->intrmsk = value; break; - case 14: + case IIC_XFRCNT: i2c->xfrcnt = value & 0x77; break; - case 15: + case IIC_XTCNTLSS: + i2c->xtcntlss &= ~(value & 0xf0); if (value & IIC_XTCNTLSS_SRST) { /* Is it actually a full reset? U-Boot sets some regs before */ ppc4xx_i2c_reset(DEVICE(i2c)); break; } - i2c->xtcntlss = value; break; - case 16: + case IIC_DIRECTCNTL: i2c->directcntl = value & (IIC_DIRECTCNTL_SDAC & IIC_DIRECTCNTL_SCLC); i2c->directcntl |= (value & IIC_DIRECTCNTL_SCLC ? 1 : 0); bitbang_i2c_set(i2c->bitbang, BITBANG_I2C_SCL, diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index ea6c8e1a58..0891a9c948 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -46,7 +46,8 @@ typedef struct PPC4xxI2CState { qemu_irq irq; MemoryRegion iomem; bitbang_i2c_interface *bitbang; - uint8_t mdata; + int mdidx; + uint8_t mdata[4]; uint8_t lmadr; uint8_t hmadr; uint8_t cntl; -- cgit v1.2.3-55-g7522