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: Changeb296b664ab
"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: Changeb296b664ab
"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:
commit
20b084c4b1
12
MAINTAINERS
12
MAINTAINERS
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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->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);
|
||||
|
@ -191,23 +191,17 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
*data = ret;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
imx_i2c_raise_interrupt(s);
|
||||
}
|
||||
|
||||
s->i2dr_read = ret;
|
||||
|
@ -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;
|
||||
s->smb_blkdata = s->smb_data[s->smb_index];
|
||||
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 {
|
||||
s->smb_blkdata = s->smb_data[s->smb_index];
|
||||
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_index >= PM_SMBUS_MAX_MSG_SIZE) {
|
||||
s->smb_index = 0;
|
||||
}
|
||||
if (s->smb_auxctl & AUX_BLK) {
|
||||
if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) {
|
||||
if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) {
|
||||
s->smb_index = 0;
|
||||
}
|
||||
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;
|
||||
|
379
hw/i2c/smbus.c
379
hw/i2c/smbus.c
@ -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)
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
165
hw/i2c/smbus_master.c
Normal 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
236
hw/i2c/smbus_slave.c
Normal 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)
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 */
|
||||
|
35
include/hw/i2c/smbus_eeprom.h
Normal file
35
include/hw/i2c/smbus_eeprom.h
Normal 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
|
@ -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
|
100
include/hw/i2c/smbus_slave.h
Normal file
100
include/hw/i2c/smbus_slave.h
Normal 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
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user