diff options
| author | Peter Maydell <peter.maydell@linaro.org> | 2018-01-11 13:24:17 +0000 |
|---|---|---|
| committer | Peter Maydell <peter.maydell@linaro.org> | 2018-01-11 13:24:17 +0000 |
| commit | e890966d60867810358449ec5384a109d5a48f46 (patch) | |
| tree | 67c154ec1fa298436a33fd89bf10bbdc309589dd /hw/i2c | |
| parent | 612061b277915fadd80631eb7a6926f48a110c44 (diff) | |
| parent | 51f84465dd985fc21589b2eac1f18658fc9783e9 (diff) | |
| download | focaccia-qemu-e890966d60867810358449ec5384a109d5a48f46.tar.gz focaccia-qemu-e890966d60867810358449ec5384a109d5a48f46.zip | |
Merge remote-tracking branch 'remotes/dgibson/tags/ppc-for-2.12-20180111' into staging
ppc patch queue 2018-01-11 This pull request supersedes ppc-for-2.12-20180108 and several before it. The earlier pull request included a patch which exposed a bug in the ARM TCG backend. I've pulled that out and will repost once the ARM bug is fixed (a patch has been posted by Richard Henderson). Higlights from this series: * SLOF update * Several new devices for embedded platforms * Fix to correctly set compatiblity mode for hotplugged CPUs * dtc compile fix for older MacOS versions # gpg: Signature made Thu 11 Jan 2018 04:58:11 GMT # gpg: using RSA key 0x6C38CACA20D9B392 # gpg: Good signature from "David Gibson <david@gibson.dropbear.id.au>" # gpg: aka "David Gibson (Red Hat) <dgibson@redhat.com>" # gpg: aka "David Gibson (ozlabs.org) <dgibson@ozlabs.org>" # gpg: aka "David Gibson (kernel.org) <dwg@kernel.org>" # Primary key fingerprint: 75F4 6586 AE61 A66C C44E 87DC 6C38 CACA 20D9 B392 * remotes/dgibson/tags/ppc-for-2.12-20180111: spapr: Correct compatibility mode setting for hotplugged CPUs hw/ppc: Remove the deprecated spapr-pci-vfio-host-bridge device Update dtc to fix compilation problem on Mac OS 10.6 target/ppc: more use of the PPC_*() macros ppc/pnv: change powernv_ prefix to pnv_ for overall naming consistency hw/ide: Emulate SiI3112 SATA controller spapr_pci: use warn_report() ppc4xx_i2c: Implement basic I2C functions sm501: Add some more unimplemented registers sm501: Add panel hardware cursor registers also to read function pseries: Update SLOF firmware image to qemu-slof-20171214 Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/i2c')
| -rw-r--r-- | hw/i2c/ppc4xx_i2c.c | 198 |
1 files changed, 168 insertions, 30 deletions
diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 5a6bde951e..e873a445da 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -2,6 +2,8 @@ * PPC4xx I2C controller emulation * * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2012 François Revol + * Copyright (c) 2016 BALATON Zoltan * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,26 +27,118 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "qemu/log.h" #include "cpu.h" #include "hw/hw.h" #include "hw/i2c/ppc4xx_i2c.h" -/*#define DEBUG_I2C*/ +#define PPC4xx_I2C_MEM_SIZE 0x12 -#define PPC4xx_I2C_MEM_SIZE 0x11 +#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_STS_PT (1 << 0) +#define IIC_STS_ERR (1 << 2) +#define IIC_STS_MDBS (1 << 5) + +#define IIC_EXTSTS_XFRA (1 << 0) + +#define IIC_XTCNTLSS_SRST (1 << 0) + +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->cntl = 0; + i2c->mdcntl = 0; + i2c->sts = 0; + i2c->extsts = 0x8f; + i2c->sdata = 0; + i2c->lsadr = 0; + i2c->hsadr = 0; + i2c->clkdiv = 0; + i2c->intrmsk = 0; + i2c->xfrcnt = 0; + i2c->xtcntlss = 0; + i2c->directcntl = 0x0f; + i2c->intr = 0; +} + +static inline bool ppc4xx_i2c_is_master(PPC4xxI2CState *i2c) +{ + return true; +} static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) { PPC4xxI2CState *i2c = PPC4xx_I2C(opaque); uint64_t ret; -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif switch (addr) { case 0x00: - /*i2c_readbyte(&i2c->mdata);*/ ret = i2c->mdata; + if (ppc4xx_i2c_is_master(i2c)) { + 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; case 0x02: ret = i2c->sdata; @@ -88,13 +182,15 @@ static uint64_t ppc4xx_i2c_readb(void *opaque, hwaddr addr, unsigned int size) case 0x10: ret = i2c->directcntl; break; + case 0x11: + ret = i2c->intr; + break; default: - ret = 0x00; + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + ret = 0; break; } -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " %02" PRIx64 "\n", __func__, addr, ret); -#endif return ret; } @@ -103,26 +199,70 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { PPC4xxI2CState *i2c = opaque; -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx64 "\n", - __func__, addr, value); -#endif + switch (addr) { case 0x00: i2c->mdata = value; - /*i2c_sendbyte(&i2c->mdata);*/ + 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; + } + } + 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); + } + } break; case 0x02: i2c->sdata = value; break; case 0x04: i2c->lmadr = value; + if (i2c_bus_busy(i2c->bus)) { + i2c_end_transfer(i2c->bus); + } break; case 0x05: i2c->hmadr = value; break; case 0x06: 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); + } + 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; + } + } else { + /* we actually already did the write transfer... */ + i2c->sts &= ~IIC_STS_PT; + } + } break; case 0x07: i2c->mdcntl = value & 0xDF; @@ -135,6 +275,7 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, break; case 0x0A: i2c->lsadr = value; + /*i2c_set_slave_address(i2c->bus, i2c->lsadr);*/ break; case 0x0B: i2c->hsadr = value; @@ -149,11 +290,23 @@ static void ppc4xx_i2c_writeb(void *opaque, hwaddr addr, uint64_t value, i2c->xfrcnt = value & 0x77; break; case 0x0F: + 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 0x10: i2c->directcntl = value & 0x7; break; + case 0x11: + i2c->intr = value; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" + HWADDR_PRIx "\n", TYPE_PPC4xx_I2C, __func__, addr); + break; } } @@ -167,21 +320,6 @@ static const MemoryRegionOps ppc4xx_i2c_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void ppc4xx_i2c_reset(DeviceState *s) -{ - PPC4xxI2CState *i2c = PPC4xx_I2C(s); - - i2c->mdata = 0x00; - i2c->sdata = 0x00; - i2c->cntl = 0x00; - i2c->mdcntl = 0x00; - i2c->sts = 0x00; - i2c->extsts = 0x00; - i2c->clkdiv = 0x00; - i2c->xfrcnt = 0x00; - i2c->directcntl = 0x0F; -} - static void ppc4xx_i2c_init(Object *o) { PPC4xxI2CState *s = PPC4xx_I2C(o); |