This has been out there long enough, I need to get this in.

This was changed a little bit since my post on Feb 20 (to which
 there were no comments) due to changes I had to work around:
 
 Change b296b664ab "smbus: Add a helper to generate SPD EEPROM
 data" added a function to include/hw/i2c/smbus.h, which I had to move to
 include/hw/smbus_eeprom.h.
 
 There were some changes to hw/i2c/Makefile.objs that I had to fix up.
 
 Beyond that, no changes.
 
 Thanks,
 
 -corey
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEE/Q1c5nzg9ZpmiCaGYfOMkJGb/4EFAlx4Iv0ACgkQYfOMkJGb
 /4HoUw/+IcrfemAuaEt0f7hOENpeWD4HYFCk0wgzXraSLaurREQHNP4KmYxz2xOS
 ISLqgTty3dEjo95VXuSQUMm9ZaV1p8LquO+I1FnNGIt0otO3SMEh6/nOyrH1zY74
 Q+6IlUzTQlU8dQCsZOd5FqGxmH/nvIVufC1WCauwfHP0hEIx0F631i2l/DeZRhYj
 7SO+idIwHljKyiDgS+CtKygSXjEnwOqV9rVQiLWYrCu0+wXBv2WIDH66xPRnYA3F
 WM3MI3ViYekCw2jWLrkaM5sjgfQ/FhTpEFC8uCJXYBF6/FggCEfkd+Yp7G9RnXq+
 ZbezRw0HCNmm7inWWGW3hfaVUFS3QVapoppJTDAAsUCspj+TQ9NkbVWdqIqCqUtU
 GFgVzwMwSgoW8rekF4A4VxE9IAWPfh9KVKT6JVIYizx0Z/F7P+VmTAvbTlHZGHYX
 QtMzyDyIpj0FtZ7yL+6LIywGR4zOP37d97xlKiYQS2JAZMiLnDr0v+avY/Ps/rmV
 fFC0sNwctD22gXIW+OecEOEckv/dSIL2PlzZ2gSuJ5xGzyfw2OPa6C1CaoD7y3qn
 xbv0zY2jBvm5hLBG/GgorlSkQOyQwLupUYl8hf5EVNjjrOcWk0/Se7Pp2HMp+RrG
 krnc4CNhfmyiJxd7GvVA23GHUgC4jMOq6P0qlUu2XcDDQC/jnbs=
 =XTkI
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/cminyard/tags/i2c-for-release-20190228' into staging

This has been out there long enough, I need to get this in.

This was changed a little bit since my post on Feb 20 (to which
there were no comments) due to changes I had to work around:

Change b296b664ab "smbus: Add a helper to generate SPD EEPROM
data" added a function to include/hw/i2c/smbus.h, which I had to move to
include/hw/smbus_eeprom.h.

There were some changes to hw/i2c/Makefile.objs that I had to fix up.

Beyond that, no changes.

Thanks,

-corey

# gpg: Signature made Thu 28 Feb 2019 18:05:49 GMT
# gpg:                using RSA key FD0D5CE67CE0F59A6688268661F38C90919BFF81
# gpg: Good signature from "Corey Minyard <cminyard@mvista.com>" [unknown]
# gpg:                 aka "Corey Minyard <minyard@acm.org>" [unknown]
# gpg:                 aka "Corey Minyard <corey@minyard.net>" [unknown]
# gpg:                 aka "Corey Minyard <minyard@mvista.com>" [unknown]
# gpg: WARNING: This key is not certified with a trusted signature!
# gpg:          There is no indication that the signature belongs to the owner.
# Primary key fingerprint: FD0D 5CE6 7CE0 F59A 6688  2686 61F3 8C90 919B FF81

* remotes/cminyard/tags/i2c-for-release-20190228:
  i2c: Verify that the count passed in to smbus_eeprom_init() is valid
  i2c:smbus_eeprom: Add a reset function to smbus_eeprom
  i2c:smbus_eeprom: Add vmstate handling to the smbus eeprom
  i2c:smbus_eeprom: Add a size constant for the smbus_eeprom size
  i2c:smbus_eeprom: Add normal type name and cast to smbus_eeprom.c
  i2c:smbus_slave: Add an SMBus vmstate structure
  i2c:pm_smbus: Fix state transfer
  migration: Add a VMSTATE_BOOL_TEST() macro
  i2c:pm_smbus: Fix pm_smbus handling of I2C block read
  boards.h: Ignore migration for SMBus devices on older machines
  i2c:smbus: Make white space in switch statements consistent
  i2c:smbus_eeprom: Get rid of the quick command
  i2c:smbus: Simplify read handling
  i2c:smbus: Simplify write operation
  i2c:smbus: Correct the working of quick commands
  i2c: Don't check return value from i2c_recv()
  arm:i2c: Don't mask return from i2c_recv()
  i2c: have I2C receive operation return uint8_t
  i2c: Split smbus into parts

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
Peter Maydell 2019-03-01 11:20:49 +00:00
commit 20b084c4b1
44 changed files with 821 additions and 582 deletions

View File

@ -2192,6 +2192,18 @@ M: Viktor Prutyanov <viktor.prutyanov@phystech.edu>
S: Maintained
F: contrib/elf2dmp/
I2C and SMBus
M: Corey Minyard <cminyard@mvista.com>
S: Maintained
F: hw/i2c/core.c
F: hw/i2c/smbus_slave.c
F: hw/i2c/smbus_master.c
F: hw/i2c/smbus_eeprom.c
F: include/hw/i2c/i2c.h
F: include/hw/i2c/smbus_master.h
F: include/hw/i2c/smbus_slave.h
F: include/hw/i2c/smbus_eeprom.h
Usermode Emulation
------------------
Overall

View File

@ -302,6 +302,11 @@ static const VMStateDescription vmstate_cpuhp_state = {
}
};
static bool piix4_vmstate_need_smbus(void *opaque, int version_id)
{
return pm_smbus_vmstate_needed();
}
/* qemu-kvm 1.2 uses version 3 but advertised as 2
* To support incoming qemu-kvm 1.2 migration, change version_id
* and minimum_version_id to 2 below (which breaks migration from
@ -321,6 +326,8 @@ static const VMStateDescription vmstate_acpi = {
VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState),
VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState),
VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState),
VMSTATE_STRUCT_TEST(smb, PIIX4PMState, piix4_vmstate_need_smbus, 3,
pmsmb_vmstate, PMSMBus),
VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState),
VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState),
VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE),

View File

@ -18,7 +18,7 @@
#include "hw/arm/aspeed.h"
#include "hw/arm/aspeed_soc.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "qemu/log.h"
#include "sysemu/block-backend.h"
#include "hw/loader.h"

View File

@ -1286,7 +1286,7 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int pxa2xx_i2c_rx(I2CSlave *i2c)
static uint8_t pxa2xx_i2c_rx(I2CSlave *i2c)
{
PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c);
PXA2xxI2CState *s = slave->host;

View File

@ -811,7 +811,7 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset,
/* TODO: Handle errors. */
if (s->msa & 1) {
/* Recv */
s->mdr = i2c_recv(s->bus) & 0xff;
s->mdr = i2c_recv(s->bus);
} else {
/* Send */
i2c_send(s->bus, s->mdr);

View File

@ -197,10 +197,10 @@ static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int tosa_dac_recv(I2CSlave *s)
static uint8_t tosa_dac_recv(I2CSlave *s)
{
printf("%s: recv not supported!!!\n", __func__);
return -1;
return 0xff;
}
static void tosa_tg_init(PXA2xxState *cpu)

View File

@ -243,7 +243,7 @@ static int aer915_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int aer915_recv(I2CSlave *slave)
static uint8_t aer915_recv(I2CSlave *slave)
{
AER915State *s = AER915(slave);
int retval = 0x00;

View File

@ -561,7 +561,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data)
return 0;
}
static int wm8750_rx(I2CSlave *i2c)
static uint8_t wm8750_rx(I2CSlave *i2c)
{
return 0x00;
}

View File

@ -79,7 +79,7 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int sii9022_rx(I2CSlave *i2c)
static uint8_t sii9022_rx(I2CSlave *i2c)
{
sii9022_state *s = SII9022(i2c);
uint8_t res = 0x00;

View File

@ -62,10 +62,10 @@ typedef struct {
uint8_t framebuffer[132*8];
} ssd0303_state;
static int ssd0303_recv(I2CSlave *i2c)
static uint8_t ssd0303_recv(I2CSlave *i2c)
{
BADF("Reads not implemented\n");
return -1;
return 0xff;
}
static int ssd0303_send(I2CSlave *i2c, uint8_t data)

View File

@ -39,7 +39,7 @@ static void max7310_reset(DeviceState *dev)
s->command = 0x00;
}
static int max7310_rx(I2CSlave *i2c)
static uint8_t max7310_rx(I2CSlave *i2c)
{
MAX7310State *s = MAX7310(i2c);

View File

@ -1,4 +1,4 @@
common-obj-$(CONFIG_I2C) += core.o smbus.o
common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o
common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o
common-obj-$(CONFIG_DDC) += i2c-ddc.o
common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o

View File

@ -189,16 +189,11 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus)
static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus)
{
int ret;
uint8_t ret;
aspeed_i2c_set_state(bus, I2CD_MRXD);
ret = i2c_recv(bus->bus);
if (ret < 0) {
qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__);
ret = 0xff;
} else {
bus->intr_status |= I2CD_INTR_RX_DONE;
}
bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT;
if (bus->cmd & I2CD_M_S_RX_CMD_LAST) {
i2c_nack(bus->bus);

View File

@ -191,24 +191,18 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send)
}
return ret ? -1 : 0;
} else {
if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) {
return -1;
}
ret = 0xff;
if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) {
sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt);
if (sc->recv) {
s = QLIST_FIRST(&bus->current_devs)->elt;
ret = sc->recv(s);
trace_i2c_recv(s->address, ret);
if (ret < 0) {
return ret;
} else {
}
}
*data = ret;
return 0;
}
}
return -1;
}
}
int i2c_send(I2CBus *bus, uint8_t data)
@ -216,12 +210,12 @@ int i2c_send(I2CBus *bus, uint8_t data)
return i2c_send_recv(bus, &data, true);
}
int i2c_recv(I2CBus *bus)
uint8_t i2c_recv(I2CBus *bus)
{
uint8_t data;
int ret = i2c_send_recv(bus, &data, false);
uint8_t data = 0xff;
return ret < 0 ? ret : data;
i2c_send_recv(bus, &data, false);
return data;
}
void i2c_nack(I2CBus *bus)

View File

@ -106,16 +106,10 @@ static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s)
static void exynos4210_i2c_data_receive(void *opaque)
{
Exynos4210I2CState *s = (Exynos4210I2CState *)opaque;
int ret;
s->i2cstat &= ~I2CSTAT_LAST_BIT;
s->scl_free = false;
ret = i2c_recv(s->bus);
if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) {
s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */
} else {
s->i2cds = ret;
}
s->i2cds = i2c_recv(s->bus);
exynos4210_i2c_raise_interrupt(s);
}

View File

@ -51,7 +51,7 @@ static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int i2c_ddc_rx(I2CSlave *i2c)
static uint8_t i2c_ddc_rx(I2CSlave *i2c)
{
I2CDDCState *s = I2CDDC(i2c);

View File

@ -120,7 +120,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
value = s->i2dr_read;
if (imx_i2c_is_master(s)) {
int ret = 0xff;
uint8_t ret = 0xff;
if (s->address == ADDR_RESET) {
/* something is wrong as the address is not set */
@ -133,15 +133,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset,
} else {
/* get the next byte */
ret = i2c_recv(s->bus);
if (ret >= 0) {
imx_i2c_raise_interrupt(s);
} else {
qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: read failed "
"for device 0x%02x\n", TYPE_IMX_I2C,
__func__, s->address);
ret = 0xff;
}
}
s->i2dr_read = ret;

View File

@ -19,8 +19,9 @@
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/i2c/pm_smbus.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_master.h"
#define SMBHSTSTS 0x00
#define SMBHSTCNT 0x02
@ -118,19 +119,30 @@ static void smb_transaction(PMSMBus *s)
}
break;
case PROT_I2C_BLOCK_READ:
if (read) {
int xfersize = s->smb_data0;
if (xfersize > sizeof(s->smb_data)) {
xfersize = sizeof(s->smb_data);
}
ret = smbus_read_block(bus, addr, s->smb_data1, s->smb_data,
xfersize, false, true);
goto data8;
} else {
/* The manual says the behavior is undefined, just set DEV_ERR. */
/* According to the Linux i2c-i801 driver:
* NB: page 240 of ICH5 datasheet shows that the R/#W
* bit should be cleared here, even when reading.
* However if SPD Write Disable is set (Lynx Point and later),
* the read will fail if we don't set the R/#W bit.
* So at least Linux may or may not set the read bit here.
* So just ignore the read bit for this command.
*/
if (i2c_start_transfer(bus, addr, 0)) {
goto error;
}
break;
ret = i2c_send(bus, s->smb_data1);
if (ret) {
goto error;
}
if (i2c_start_transfer(bus, addr, 1)) {
goto error;
}
s->in_i2c_block_read = true;
s->smb_blkdata = i2c_recv(s->smbus);
s->op_done = false;
s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE;
goto out;
case PROT_BLOCK_DATA:
if (read) {
ret = smbus_read_block(bus, addr, cmd, s->smb_data,
@ -208,6 +220,7 @@ static void smb_transaction_start(PMSMBus *s)
{
if (s->smb_ctl & CTL_INTREN) {
smb_transaction(s);
s->start_transaction_on_status_read = false;
} else {
/* Do not execute immediately the command; it will be
* executed when guest will read SMB_STAT register. This
@ -217,6 +230,7 @@ static void smb_transaction_start(PMSMBus *s)
* checking for status. If STS_HOST_BUSY doesn't get
* set, it gets stuck. */
s->smb_stat |= STS_HOST_BUSY;
s->start_transaction_on_status_read = true;
}
}
@ -226,19 +240,38 @@ smb_irq_value(PMSMBus *s)
return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN);
}
static bool
smb_byte_by_byte(PMSMBus *s)
{
if (s->op_done) {
return false;
}
if (s->in_i2c_block_read) {
return true;
}
return !(s->smb_auxctl & AUX_BLK);
}
static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
unsigned width)
{
PMSMBus *s = opaque;
uint8_t clear_byte_done;
SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx
" val=0x%02" PRIx64 "\n", addr, val);
switch(addr) {
case SMBHSTSTS:
clear_byte_done = s->smb_stat & val & STS_BYTE_DONE;
s->smb_stat &= ~(val & ~STS_HOST_BUSY);
if (!s->op_done && !(s->smb_auxctl & AUX_BLK)) {
if (clear_byte_done && smb_byte_by_byte(s)) {
uint8_t read = s->smb_addr & 0x01;
if (s->in_i2c_block_read) {
/* See comment below PROT_I2C_BLOCK_READ above. */
read = 1;
}
s->smb_index++;
if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
s->smb_index = 0;
@ -268,12 +301,23 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
s->smb_stat |= STS_BYTE_DONE;
} else if (s->smb_ctl & CTL_LAST_BYTE) {
s->op_done = true;
if (s->in_i2c_block_read) {
s->in_i2c_block_read = false;
s->smb_blkdata = i2c_recv(s->smbus);
i2c_nack(s->smbus);
i2c_end_transfer(s->smbus);
} else {
s->smb_blkdata = s->smb_data[s->smb_index];
}
s->smb_index = 0;
s->smb_stat |= STS_INTR;
s->smb_stat &= ~STS_HOST_BUSY;
} else {
if (s->in_i2c_block_read) {
s->smb_blkdata = i2c_recv(s->smbus);
} else {
s->smb_blkdata = s->smb_data[s->smb_index];
}
s->smb_stat |= STS_BYTE_DONE;
}
}
@ -284,6 +328,10 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val,
if (!s->op_done) {
s->smb_index = 0;
s->op_done = true;
if (s->in_i2c_block_read) {
s->in_i2c_block_read = false;
i2c_end_transfer(s->smbus);
}
}
smb_transaction_start(s);
}
@ -337,8 +385,9 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
switch(addr) {
case SMBHSTSTS:
val = s->smb_stat;
if (s->smb_stat & STS_HOST_BUSY) {
if (s->start_transaction_on_status_read) {
/* execute command now */
s->start_transaction_on_status_read = false;
s->smb_stat &= ~STS_HOST_BUSY;
smb_transaction(s);
}
@ -359,10 +408,10 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width)
val = s->smb_data1;
break;
case SMBBLKDAT:
if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) {
if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
s->smb_index = 0;
}
if (s->smb_auxctl & AUX_BLK) {
val = s->smb_data[s->smb_index++];
if (!s->op_done && s->smb_index == s->smb_data0) {
s->op_done = true;
@ -405,6 +454,36 @@ static const MemoryRegionOps pm_smbus_ops = {
.endianness = DEVICE_LITTLE_ENDIAN,
};
bool pm_smbus_vmstate_needed(void)
{
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
return !mc->smbus_no_migration_support;
}
const VMStateDescription pmsmb_vmstate = {
.name = "pmsmb",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_UINT8(smb_stat, PMSMBus),
VMSTATE_UINT8(smb_ctl, PMSMBus),
VMSTATE_UINT8(smb_cmd, PMSMBus),
VMSTATE_UINT8(smb_addr, PMSMBus),
VMSTATE_UINT8(smb_data0, PMSMBus),
VMSTATE_UINT8(smb_data1, PMSMBus),
VMSTATE_UINT32(smb_index, PMSMBus),
VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE),
VMSTATE_UINT8(smb_auxctl, PMSMBus),
VMSTATE_UINT8(smb_blkdata, PMSMBus),
VMSTATE_BOOL(i2c_enable, PMSMBus),
VMSTATE_BOOL(op_done, PMSMBus),
VMSTATE_BOOL(in_i2c_block_read, PMSMBus),
VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus),
VMSTATE_END_OF_LIST()
}
};
void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk)
{
smb->op_done = true;

View File

@ -1,379 +0,0 @@
/*
* QEMU SMBus device emulation.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the LGPL.
*/
/* TODO: Implement PEC. */
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
//#define DEBUG_SMBUS 1
#ifdef DEBUG_SMBUS
#define DPRINTF(fmt, ...) \
do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
#endif
enum {
SMBUS_IDLE,
SMBUS_WRITE_DATA,
SMBUS_RECV_BYTE,
SMBUS_READ_DATA,
SMBUS_DONE,
SMBUS_CONFUSED = -1
};
static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
DPRINTF("Quick Command %d\n", recv);
if (sc->quick_cmd) {
sc->quick_cmd(dev, recv);
}
}
static void smbus_do_write(SMBusDevice *dev)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
if (dev->data_len == 0) {
smbus_do_quick_cmd(dev, 0);
} else if (dev->data_len == 1) {
DPRINTF("Send Byte\n");
if (sc->send_byte) {
sc->send_byte(dev, dev->data_buf[0]);
}
} else {
dev->command = dev->data_buf[0];
DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1);
if (sc->write_data) {
sc->write_data(dev, dev->command, dev->data_buf + 1,
dev->data_len - 1);
}
}
}
static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
switch (event) {
case I2C_START_SEND:
switch (dev->mode) {
case SMBUS_IDLE:
DPRINTF("Incoming data\n");
dev->mode = SMBUS_WRITE_DATA;
break;
default:
BADF("Unexpected send start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
break;
case I2C_START_RECV:
switch (dev->mode) {
case SMBUS_IDLE:
DPRINTF("Read mode\n");
dev->mode = SMBUS_RECV_BYTE;
break;
case SMBUS_WRITE_DATA:
if (dev->data_len == 0) {
BADF("Read after write with no data\n");
dev->mode = SMBUS_CONFUSED;
} else {
if (dev->data_len > 1) {
smbus_do_write(dev);
} else {
dev->command = dev->data_buf[0];
DPRINTF("%02x: Command %d\n", dev->i2c.address,
dev->command);
}
DPRINTF("Read mode\n");
dev->data_len = 0;
dev->mode = SMBUS_READ_DATA;
}
break;
default:
BADF("Unexpected recv start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
break;
case I2C_FINISH:
switch (dev->mode) {
case SMBUS_WRITE_DATA:
smbus_do_write(dev);
break;
case SMBUS_RECV_BYTE:
smbus_do_quick_cmd(dev, 1);
break;
case SMBUS_READ_DATA:
BADF("Unexpected stop during receive\n");
break;
default:
/* Nothing to do. */
break;
}
dev->mode = SMBUS_IDLE;
dev->data_len = 0;
break;
case I2C_NACK:
switch (dev->mode) {
case SMBUS_DONE:
/* Nothing to do. */
break;
case SMBUS_READ_DATA:
dev->mode = SMBUS_DONE;
break;
default:
BADF("Unexpected NACK in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
}
return 0;
}
static int smbus_i2c_recv(I2CSlave *s)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
int ret;
switch (dev->mode) {
case SMBUS_RECV_BYTE:
if (sc->receive_byte) {
ret = sc->receive_byte(dev);
} else {
ret = 0;
}
DPRINTF("Receive Byte %02x\n", ret);
dev->mode = SMBUS_DONE;
break;
case SMBUS_READ_DATA:
if (sc->read_data) {
ret = sc->read_data(dev, dev->command, dev->data_len);
dev->data_len++;
} else {
ret = 0;
}
DPRINTF("Read data %02x\n", ret);
break;
default:
BADF("Unexpected read in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
ret = 0;
break;
}
return ret;
}
static int smbus_i2c_send(I2CSlave *s, uint8_t data)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
switch (dev->mode) {
case SMBUS_WRITE_DATA:
DPRINTF("Write data %02x\n", data);
if (dev->data_len >= sizeof(dev->data_buf)) {
BADF("Too many bytes sent\n");
} else {
dev->data_buf[dev->data_len++] = data;
}
break;
default:
BADF("Unexpected write in state %d\n", dev->mode);
break;
}
return 0;
}
/* Master device commands. */
int smbus_quick_command(I2CBus *bus, uint8_t addr, int read)
{
if (i2c_start_transfer(bus, addr, read)) {
return -1;
}
i2c_end_transfer(bus);
return 0;
}
int smbus_receive_byte(I2CBus *bus, uint8_t addr)
{
uint8_t data;
if (i2c_start_transfer(bus, addr, 1)) {
return -1;
}
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, data);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
{
uint8_t data;
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (i2c_start_transfer(bus, addr, 1)) {
i2c_end_transfer(bus);
return -1;
}
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
i2c_send(bus, data);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
{
uint16_t data;
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (i2c_start_transfer(bus, addr, 1)) {
i2c_end_transfer(bus);
return -1;
}
data = i2c_recv(bus);
data |= i2c_recv(bus) << 8;
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
i2c_send(bus, data & 0xff);
i2c_send(bus, data >> 8);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int len, bool recv_len, bool send_cmd)
{
int rlen;
int i;
if (send_cmd) {
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
}
if (i2c_start_transfer(bus, addr, 1)) {
if (send_cmd) {
i2c_end_transfer(bus);
}
return -1;
}
if (recv_len) {
rlen = i2c_recv(bus);
} else {
rlen = len;
}
if (rlen > len) {
rlen = 0;
}
for (i = 0; i < rlen; i++) {
data[i] = i2c_recv(bus);
}
i2c_nack(bus);
i2c_end_transfer(bus);
return rlen;
}
int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int len, bool send_len)
{
int i;
if (len > 32)
len = 32;
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (send_len) {
i2c_send(bus, len);
}
for (i = 0; i < len; i++) {
i2c_send(bus, data[i]);
}
i2c_end_transfer(bus);
return 0;
}
static void smbus_device_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
sc->event = smbus_i2c_event;
sc->recv = smbus_i2c_recv;
sc->send = smbus_i2c_send;
}
static const TypeInfo smbus_device_type_info = {
.name = TYPE_SMBUS_DEVICE,
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(SMBusDevice),
.abstract = true,
.class_size = sizeof(SMBusDeviceClass),
.class_init = smbus_device_class_init,
};
static void smbus_device_register_types(void)
{
type_register_static(&smbus_device_type_info);
}
type_init(smbus_device_register_types)

View File

@ -26,39 +26,35 @@
#include "qemu/units.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/boards.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_slave.h"
#include "hw/i2c/smbus_eeprom.h"
//#define DEBUG
#define TYPE_SMBUS_EEPROM "smbus-eeprom"
#define SMBUS_EEPROM(obj) \
OBJECT_CHECK(SMBusEEPROMDevice, (obj), TYPE_SMBUS_EEPROM)
#define SMBUS_EEPROM_SIZE 256
typedef struct SMBusEEPROMDevice {
SMBusDevice smbusdev;
void *data;
uint8_t data[SMBUS_EEPROM_SIZE];
void *init_data;
uint8_t offset;
bool accessed;
} SMBusEEPROMDevice;
static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read)
{
#ifdef DEBUG
printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
#endif
}
static void eeprom_send_byte(SMBusDevice *dev, uint8_t val)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
#ifdef DEBUG
printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n",
dev->i2c.address, val);
#endif
eeprom->offset = val;
}
static uint8_t eeprom_receive_byte(SMBusDevice *dev)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
uint8_t *data = eeprom->data;
uint8_t val = data[eeprom->offset++];
eeprom->accessed = true;
#ifdef DEBUG
printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n",
dev->i2c.address, val);
@ -66,48 +62,77 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev)
return val;
}
static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
int n;
SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
uint8_t *data = eeprom->data;
eeprom->accessed = true;
#ifdef DEBUG
printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
dev->i2c.address, cmd, buf[0]);
dev->i2c.address, buf[0], buf[1]);
#endif
/* A page write operation is not a valid SMBus command.
It is a block write without a length byte. Fortunately we
get the full block anyway. */
/* TODO: Should this set the current location? */
if (cmd + len > 256)
n = 256 - cmd;
else
n = len;
memcpy(eeprom->data + cmd, buf, n);
len -= n;
if (len)
memcpy(eeprom->data, buf + n, len);
/* len is guaranteed to be > 0 */
eeprom->offset = buf[0];
buf++;
len--;
for (; len > 0; len--) {
data[eeprom->offset] = *buf++;
eeprom->offset = (eeprom->offset + 1) % SMBUS_EEPROM_SIZE;
}
return 0;
}
static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n)
static bool smbus_eeprom_vmstate_needed(void *opaque)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev;
/* If this is the first byte then set the current position. */
if (n == 0)
eeprom->offset = cmd;
/* As with writes, we implement block reads without the
SMBus length byte. */
return eeprom_receive_byte(dev);
MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine());
SMBusEEPROMDevice *eeprom = opaque;
return (eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev)) &&
!mc->smbus_no_migration_support;
}
static const VMStateDescription vmstate_smbus_eeprom = {
.name = "smbus-eeprom",
.version_id = 1,
.minimum_version_id = 1,
.needed = smbus_eeprom_vmstate_needed,
.fields = (VMStateField[]) {
VMSTATE_SMBUS_DEVICE(smbusdev, SMBusEEPROMDevice),
VMSTATE_UINT8_ARRAY(data, SMBusEEPROMDevice, SMBUS_EEPROM_SIZE),
VMSTATE_UINT8(offset, SMBusEEPROMDevice),
VMSTATE_BOOL(accessed, SMBusEEPROMDevice),
VMSTATE_END_OF_LIST()
}
};
/*
* Reset the EEPROM contents to the initial state on a reset. This
* isn't really how an EEPROM works, of course, but the general
* principle of QEMU is to restore function on reset to what it would
* be if QEMU was stopped and started.
*
* The proper thing to do would be to have a backing blockdev to hold
* the contents and restore that on startup, and not do this on reset.
* But until that time, act as if we had been stopped and restarted.
*/
static void smbus_eeprom_reset(DeviceState *dev)
{
SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev);
memcpy(eeprom->data, eeprom->init_data, SMBUS_EEPROM_SIZE);
eeprom->offset = 0;
}
static void smbus_eeprom_realize(DeviceState *dev, Error **errp)
{
SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev;
eeprom->offset = 0;
smbus_eeprom_reset(dev);
}
static Property smbus_eeprom_properties[] = {
DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data),
DEFINE_PROP_PTR("data", SMBusEEPROMDevice, init_data),
DEFINE_PROP_END_OF_LIST(),
};
@ -117,18 +142,17 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data)
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
dc->realize = smbus_eeprom_realize;
sc->quick_cmd = eeprom_quick_cmd;
sc->send_byte = eeprom_send_byte;
dc->reset = smbus_eeprom_reset;
sc->receive_byte = eeprom_receive_byte;
sc->write_data = eeprom_write_data;
sc->read_data = eeprom_read_data;
dc->props = smbus_eeprom_properties;
dc->vmsd = &vmstate_smbus_eeprom;
/* Reason: pointer property "data" */
dc->user_creatable = false;
}
static const TypeInfo smbus_eeprom_info = {
.name = "smbus-eeprom",
.name = TYPE_SMBUS_EEPROM,
.parent = TYPE_SMBUS_DEVICE,
.instance_size = sizeof(SMBusEEPROMDevice),
.class_init = smbus_eeprom_class_initfn,
@ -145,7 +169,7 @@ void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf)
{
DeviceState *dev;
dev = qdev_create((BusState *) smbus, "smbus-eeprom");
dev = qdev_create((BusState *) smbus, TYPE_SMBUS_EEPROM);
qdev_prop_set_uint8(dev, "address", address);
qdev_prop_set_ptr(dev, "data", eeprom_buf);
qdev_init_nofail(dev);
@ -155,13 +179,17 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom,
const uint8_t *eeprom_spd, int eeprom_spd_size)
{
int i;
uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */
/* XXX: make this persistent */
assert(nb_eeprom <= 8);
uint8_t *eeprom_buf = g_malloc0(8 * SMBUS_EEPROM_SIZE);
if (eeprom_spd_size > 0) {
memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size);
}
for (i = 0; i < nb_eeprom; i++) {
smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256));
smbus_eeprom_init_one(smbus, 0x50 + i,
eeprom_buf + (i * SMBUS_EEPROM_SIZE));
}
}

View File

@ -29,8 +29,6 @@
#include "hw/i2c/pm_smbus.h"
#include "hw/pci/pci.h"
#include "sysemu/sysemu.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
#include "hw/i386/ich9.h"
@ -45,12 +43,20 @@ typedef struct ICH9SMBState {
PMSMBus smb;
} ICH9SMBState;
static bool ich9_vmstate_need_smbus(void *opaque, int version_id)
{
return pm_smbus_vmstate_needed();
}
static const VMStateDescription vmstate_ich9_smbus = {
.name = "ich9_smb",
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState),
VMSTATE_PCI_DEVICE(dev, ICH9SMBState),
VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus),
VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1,
pmsmb_vmstate, PMSMBus),
VMSTATE_END_OF_LIST()
}
};

165
hw/i2c/smbus_master.c Normal file
View File

@ -0,0 +1,165 @@
/*
* QEMU SMBus host (master) emulation.
*
* This code emulates SMBus transactions from the master point of view,
* it runs the individual I2C transaction to do the SMBus protocol
* over I2C.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the LGPL.
*/
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus_master.h"
/* Master device commands. */
int smbus_quick_command(I2CBus *bus, uint8_t addr, int read)
{
if (i2c_start_transfer(bus, addr, read)) {
return -1;
}
i2c_end_transfer(bus);
return 0;
}
int smbus_receive_byte(I2CBus *bus, uint8_t addr)
{
uint8_t data;
if (i2c_start_transfer(bus, addr, 1)) {
return -1;
}
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, data);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command)
{
uint8_t data;
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (i2c_start_transfer(bus, addr, 1)) {
i2c_end_transfer(bus);
return -1;
}
data = i2c_recv(bus);
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
i2c_send(bus, data);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command)
{
uint16_t data;
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (i2c_start_transfer(bus, addr, 1)) {
i2c_end_transfer(bus);
return -1;
}
data = i2c_recv(bus);
data |= i2c_recv(bus) << 8;
i2c_nack(bus);
i2c_end_transfer(bus);
return data;
}
int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
{
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
i2c_send(bus, data & 0xff);
i2c_send(bus, data >> 8);
i2c_end_transfer(bus);
return 0;
}
int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int len, bool recv_len, bool send_cmd)
{
int rlen;
int i;
if (send_cmd) {
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
}
if (i2c_start_transfer(bus, addr, 1)) {
if (send_cmd) {
i2c_end_transfer(bus);
}
return -1;
}
if (recv_len) {
rlen = i2c_recv(bus);
} else {
rlen = len;
}
if (rlen > len) {
rlen = 0;
}
for (i = 0; i < rlen; i++) {
data[i] = i2c_recv(bus);
}
i2c_nack(bus);
i2c_end_transfer(bus);
return rlen;
}
int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int len, bool send_len)
{
int i;
if (len > 32) {
len = 32;
}
if (i2c_start_transfer(bus, addr, 0)) {
return -1;
}
i2c_send(bus, command);
if (send_len) {
i2c_send(bus, len);
}
for (i = 0; i < len; i++) {
i2c_send(bus, data[i]);
}
i2c_end_transfer(bus);
return 0;
}

236
hw/i2c/smbus_slave.c Normal file
View File

@ -0,0 +1,236 @@
/*
* QEMU SMBus device emulation.
*
* This code is a helper for SMBus device emulation. It implements an
* I2C device inteface and runs the SMBus protocol from the device
* point of view and maps those to simple calls to emulate.
*
* Copyright (c) 2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the LGPL.
*/
/* TODO: Implement PEC. */
#include "qemu/osdep.h"
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus_slave.h"
//#define DEBUG_SMBUS 1
#ifdef DEBUG_SMBUS
#define DPRINTF(fmt, ...) \
do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
#else
#define DPRINTF(fmt, ...) do {} while(0)
#define BADF(fmt, ...) \
do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
#endif
enum {
SMBUS_IDLE,
SMBUS_WRITE_DATA,
SMBUS_READ_DATA,
SMBUS_DONE,
SMBUS_CONFUSED = -1
};
static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
DPRINTF("Quick Command %d\n", recv);
if (sc->quick_cmd) {
sc->quick_cmd(dev, recv);
}
}
static void smbus_do_write(SMBusDevice *dev)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len);
if (sc->write_data) {
sc->write_data(dev, dev->data_buf, dev->data_len);
}
}
static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
switch (event) {
case I2C_START_SEND:
switch (dev->mode) {
case SMBUS_IDLE:
DPRINTF("Incoming data\n");
dev->mode = SMBUS_WRITE_DATA;
break;
default:
BADF("Unexpected send start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
break;
case I2C_START_RECV:
switch (dev->mode) {
case SMBUS_IDLE:
DPRINTF("Read mode\n");
dev->mode = SMBUS_READ_DATA;
break;
case SMBUS_WRITE_DATA:
if (dev->data_len == 0) {
BADF("Read after write with no data\n");
dev->mode = SMBUS_CONFUSED;
} else {
smbus_do_write(dev);
DPRINTF("Read mode\n");
dev->mode = SMBUS_READ_DATA;
}
break;
default:
BADF("Unexpected recv start condition in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
break;
case I2C_FINISH:
if (dev->data_len == 0) {
if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) {
smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA);
}
} else {
switch (dev->mode) {
case SMBUS_WRITE_DATA:
smbus_do_write(dev);
break;
case SMBUS_READ_DATA:
BADF("Unexpected stop during receive\n");
break;
default:
/* Nothing to do. */
break;
}
}
dev->mode = SMBUS_IDLE;
dev->data_len = 0;
break;
case I2C_NACK:
switch (dev->mode) {
case SMBUS_DONE:
/* Nothing to do. */
break;
case SMBUS_READ_DATA:
dev->mode = SMBUS_DONE;
break;
default:
BADF("Unexpected NACK in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
}
return 0;
}
static uint8_t smbus_i2c_recv(I2CSlave *s)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
uint8_t ret = 0xff;
switch (dev->mode) {
case SMBUS_READ_DATA:
if (sc->receive_byte) {
ret = sc->receive_byte(dev);
}
DPRINTF("Read data %02x\n", ret);
break;
default:
BADF("Unexpected read in state %d\n", dev->mode);
dev->mode = SMBUS_CONFUSED;
break;
}
return ret;
}
static int smbus_i2c_send(I2CSlave *s, uint8_t data)
{
SMBusDevice *dev = SMBUS_DEVICE(s);
switch (dev->mode) {
case SMBUS_WRITE_DATA:
DPRINTF("Write data %02x\n", data);
if (dev->data_len >= sizeof(dev->data_buf)) {
BADF("Too many bytes sent\n");
} else {
dev->data_buf[dev->data_len++] = data;
}
break;
default:
BADF("Unexpected write in state %d\n", dev->mode);
break;
}
return 0;
}
static void smbus_device_class_init(ObjectClass *klass, void *data)
{
I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
sc->event = smbus_i2c_event;
sc->recv = smbus_i2c_recv;
sc->send = smbus_i2c_send;
}
bool smbus_vmstate_needed(SMBusDevice *dev)
{
return dev->mode != SMBUS_IDLE;
}
const VMStateDescription vmstate_smbus_device = {
.name = TYPE_SMBUS_DEVICE,
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_I2C_SLAVE(i2c, SMBusDevice),
VMSTATE_INT32(mode, SMBusDevice),
VMSTATE_INT32(data_len, SMBusDevice),
VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN),
VMSTATE_END_OF_LIST()
}
};
static const TypeInfo smbus_device_type_info = {
.name = TYPE_SMBUS_DEVICE,
.parent = TYPE_I2C_SLAVE,
.instance_size = sizeof(SMBusDevice),
.abstract = true,
.class_size = sizeof(SMBusDeviceClass),
.class_init = smbus_device_class_init,
};
static void smbus_device_register_types(void)
{
type_register_static(&smbus_device_type_info);
}
type_init(smbus_device_register_types)

View File

@ -42,7 +42,7 @@
#include "sysemu/sysemu.h"
#include "hw/sysbus.h"
#include "sysemu/arch_init.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/xen/xen.h"
#include "exec/memory.h"
#include "exec/address-spaces.h"
@ -444,6 +444,7 @@ static void pc_i440fx_3_1_machine_options(MachineClass *m)
pc_i440fx_4_0_machine_options(m);
m->is_default = 0;
m->smbus_no_migration_support = true;
m->alias = NULL;
pcmc->pvh_enabled = false;
compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len);

View File

@ -33,7 +33,7 @@
#include "hw/hw.h"
#include "hw/loader.h"
#include "sysemu/arch_init.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/boards.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/xen/xen.h"
@ -380,6 +380,7 @@ static void pc_q35_3_1_machine_options(MachineClass *m)
pc_q35_4_0_machine_options(m);
m->default_kernel_irqchip_split = false;
m->smbus_no_migration_support = true;
m->alias = NULL;
pcmc->pvh_enabled = false;
compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len);

View File

@ -401,7 +401,7 @@ static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int lm_i2c_rx(I2CSlave *i2c)
static uint8_t lm_i2c_rx(I2CSlave *i2c)
{
LM823KbdState *s = LM8323(i2c);

View File

@ -14,7 +14,6 @@
#include "hw/hw.h"
#include "hw/isa/vt82c686.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
#include "hw/pci/pci.h"
#include "hw/isa/isa.h"
#include "hw/isa/superio.h"

View File

@ -28,7 +28,7 @@
#include "hw/isa/superio.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"

View File

@ -33,7 +33,7 @@
#include "hw/char/serial.h"
#include "net/net.h"
#include "hw/boards.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/block/flash.h"
#include "hw/mips/mips.h"
#include "hw/mips/cpudevs.h"

View File

@ -115,7 +115,7 @@ static void pca9552_autoinc(PCA9552State *s)
}
}
static int pca9552_recv(I2CSlave *i2c)
static uint8_t pca9552_recv(I2CSlave *i2c)
{
PCA9552State *s = PCA9552(i2c);
uint8_t ret;

View File

@ -147,7 +147,7 @@ static void tmp105_write(TMP105State *s)
}
}
static int tmp105_rx(I2CSlave *i2c)
static uint8_t tmp105_rx(I2CSlave *i2c)
{
TMP105State *s = TMP105(i2c);

View File

@ -249,7 +249,7 @@ static void tmp421_write(TMP421State *s)
}
}
static int tmp421_rx(I2CSlave *i2c)
static uint8_t tmp421_rx(I2CSlave *i2c)
{
TMP421State *s = TMP421(i2c);

View File

@ -74,10 +74,10 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event)
}
static
int at24c_eeprom_recv(I2CSlave *s)
uint8_t at24c_eeprom_recv(I2CSlave *s)
{
EEPROMState *ee = AT24C_EE(s);
int ret;
uint8_t ret;
ret = ee->mem[ee->cur];

View File

@ -34,7 +34,7 @@
#include "hw/sysbus.h"
#include "hw/char/serial.h"
#include "hw/i2c/ppc4xx_i2c.h"
#include "hw/i2c/smbus.h"
#include "hw/i2c/smbus_eeprom.h"
#include "hw/usb/hcd-ehci.h"
#include "hw/ppc/fdt.h"

View File

@ -117,7 +117,7 @@ static int ds1338_event(I2CSlave *i2c, enum i2c_event event)
return 0;
}
static int ds1338_recv(I2CSlave *i2c)
static uint8_t ds1338_recv(I2CSlave *i2c)
{
DS1338State *s = DS1338(i2c);
uint8_t res;

View File

@ -40,7 +40,7 @@ static int m41t80_send(I2CSlave *i2c, uint8_t data)
return 0;
}
static int m41t80_recv(I2CSlave *i2c)
static uint8_t m41t80_recv(I2CSlave *i2c)
{
M41t80State *s = M41T80(i2c);
struct tm now;

View File

@ -737,7 +737,7 @@ static int menelaus_tx(I2CSlave *i2c, uint8_t data)
return 0;
}
static int menelaus_rx(I2CSlave *i2c)
static uint8_t menelaus_rx(I2CSlave *i2c)
{
MenelausState *s = TWL92230(i2c);

View File

@ -206,6 +206,7 @@ struct MachineClass {
void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes,
int nb_nodes, ram_addr_t size);
bool ignore_boot_device_suffixes;
bool smbus_no_migration_support;
HotplugHandler *(*get_hotplug_handler)(MachineState *machine,
DeviceState *dev);

View File

@ -33,10 +33,9 @@ typedef struct I2CSlaveClass {
/*
* Slave to master. This cannot fail, the device should always
* return something here. Negative values probably result in 0xff
* and a possible log from the driver, and shouldn't be used.
* return something here.
*/
int (*recv)(I2CSlave *s);
uint8_t (*recv)(I2CSlave *s);
/*
* Notify the slave of a bus state change. For start event,
@ -78,7 +77,7 @@ void i2c_end_transfer(I2CBus *bus);
void i2c_nack(I2CBus *bus);
int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send);
int i2c_send(I2CBus *bus, uint8_t data);
int i2c_recv(I2CBus *bus);
uint8_t i2c_recv(I2CBus *bus);
DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr);

View File

@ -1,6 +1,8 @@
#ifndef PM_SMBUS_H
#define PM_SMBUS_H
#include "hw/i2c/smbus_master.h"
#define PM_SMBUS_MAX_MSG_SIZE 32
typedef struct PMSMBus {
@ -31,8 +33,23 @@ typedef struct PMSMBus {
/* Set on block transfers after the last byte has been read, so the
INTR bit can be set at the right time. */
bool op_done;
/* Set during an I2C block read, so we know how to handle data. */
bool in_i2c_block_read;
/* Used to work around a bug in AMIBIOS, see smb_transaction_start() */
bool start_transaction_on_status_read;
} PMSMBus;
void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk);
/*
* For backwards compatibility on migration, older versions don't have
* working migration for pm_smbus, this lets us ignore the migrations
* for older machine versions.
*/
bool pm_smbus_vmstate_needed(void);
extern const VMStateDescription pmsmb_vmstate;
#endif /* PM_SMBUS_H */

View File

@ -0,0 +1,35 @@
/*
* QEMU SMBus EEPROM API
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_SMBUS_EEPROM_H
#define HW_SMBUS_EEPROM_H
#include "hw/i2c/i2c.h"
void smbus_eeprom_init_one(I2CBus *bus, uint8_t address, uint8_t *eeprom_buf);
void smbus_eeprom_init(I2CBus *bus, int nb_eeprom,
const uint8_t *eeprom_spd, int size);
enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 };
uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size, Error **errp);
#endif

View File

@ -1,8 +1,5 @@
#ifndef QEMU_SMBUS_H
#define QEMU_SMBUS_H
/*
* QEMU SMBus API
* QEMU SMBus host (master) API
*
* Copyright (c) 2007 Arastra, Inc.
*
@ -25,47 +22,11 @@
* THE SOFTWARE.
*/
#ifndef HW_SMBUS_MASTER_H
#define HW_SMBUS_MASTER_H
#include "hw/i2c/i2c.h"
#define TYPE_SMBUS_DEVICE "smbus-device"
#define SMBUS_DEVICE(obj) \
OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE)
#define SMBUS_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE)
#define SMBUS_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE)
typedef struct SMBusDevice SMBusDevice;
typedef struct SMBusDeviceClass
{
I2CSlaveClass parent_class;
void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
void (*send_byte)(SMBusDevice *dev, uint8_t val);
uint8_t (*receive_byte)(SMBusDevice *dev);
/* We can't distinguish between a word write and a block write with
length 1, so pass the whole data block including the length byte
(if present). The device is responsible figuring out what type of
command this is. */
void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len);
/* Likewise we can't distinguish between different reads, or even know
the length of the read until the read is complete, so read data a
byte at a time. The device is responsible for adding the length
byte on block reads. */
uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n);
} SMBusDeviceClass;
struct SMBusDevice {
/* The SMBus protocol is implemented on top of I2C. */
I2CSlave i2c;
/* Remaining fields for internal use only. */
int mode;
int data_len;
uint8_t data_buf[34]; /* command + len + 32 bytes of data. */
uint8_t command;
};
/* Master device commands. */
int smbus_quick_command(I2CBus *bus, uint8_t addr, int read);
int smbus_receive_byte(I2CBus *bus, uint8_t addr);
@ -91,11 +52,4 @@ int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
int len, bool send_len);
void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf);
void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom,
const uint8_t *eeprom_spd, int size);
enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 };
uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size, Error **errp);
#endif

View File

@ -0,0 +1,100 @@
/*
* QEMU SMBus device (slave) API
*
* Copyright (c) 2007 Arastra, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef HW_SMBUS_SLAVE_H
#define HW_SMBUS_SLAVE_H
#include "hw/i2c/i2c.h"
#define TYPE_SMBUS_DEVICE "smbus-device"
#define SMBUS_DEVICE(obj) \
OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE)
#define SMBUS_DEVICE_CLASS(klass) \
OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE)
#define SMBUS_DEVICE_GET_CLASS(obj) \
OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE)
typedef struct SMBusDevice SMBusDevice;
typedef struct SMBusDeviceClass
{
I2CSlaveClass parent_class;
/*
* An operation with no data, special in SMBus.
* This may be NULL, quick commands are ignore in that case.
*/
void (*quick_cmd)(SMBusDevice *dev, uint8_t read);
/*
* We can't distinguish between a word write and a block write with
* length 1, so pass the whole data block including the length byte
* (if present). The device is responsible figuring out what type of
* command this is.
* This may be NULL if no data is written to the device. Writes
* will be ignore in that case.
*/
int (*write_data)(SMBusDevice *dev, uint8_t *buf, uint8_t len);
/*
* Likewise we can't distinguish between different reads, or even know
* the length of the read until the read is complete, so read data a
* byte at a time. The device is responsible for adding the length
* byte on block reads. This call cannot fail, it should return
* something, preferably 0xff if nothing is available.
* This may be NULL if no data is read from the device. Reads will
* return 0xff in that case.
*/
uint8_t (*receive_byte)(SMBusDevice *dev);
} SMBusDeviceClass;
#define SMBUS_DATA_MAX_LEN 34 /* command + len + 32 bytes of data. */
struct SMBusDevice {
/* The SMBus protocol is implemented on top of I2C. */
I2CSlave i2c;
/* Remaining fields for internal use only. */
int32_t mode;
int32_t data_len;
uint8_t data_buf[SMBUS_DATA_MAX_LEN];
};
extern const VMStateDescription vmstate_smbus_device;
#define VMSTATE_SMBUS_DEVICE(_field, _state) { \
.name = (stringify(_field)), \
.size = sizeof(SMBusDevice), \
.vmsd = &vmstate_smbus_device, \
.flags = VMS_STRUCT, \
.offset = vmstate_offset_value(_state, _field, SMBusDevice), \
}
/*
* Users should call this in their .needed functions to know if the
* SMBus slave data needs to be transferred.
*/
bool smbus_vmstate_needed(SMBusDevice *dev);
#endif

View File

@ -851,6 +851,9 @@ extern const VMStateInfo vmstate_info_qtailq;
#define VMSTATE_INT32_POSITIVE_LE(_f, _s) \
VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t)
#define VMSTATE_BOOL_TEST(_f, _s, _t) \
VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_bool, bool)
#define VMSTATE_INT8_TEST(_f, _s, _t) \
VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int8, int8_t)