Commit 4b615be5 authored by Corey Minyard's avatar Corey Minyard Committed by Paolo Bonzini
Browse files

i2c: pm_smbus: Fix the semantics of block I2C transfers



The I2C block transfer commands was not implemented correctly, it
read a length byte and such like it was an smbus transfer.

So fix the smbus_read_block() and smbus_write_block() functions
so they can properly handle I2C transfers, and normal SMBus
transfers (for upcoming changes).  Pass in a transfer size and
a bool to know whether to use the size byte (like SMBus) or use
the length given (like I2C).

Signed-off-by: default avatarCorey Minyard <cminyard@mvista.com>
Cc: Michael S. Tsirkin <mst@redhat.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Message-Id: <1534796770-10295-3-git-send-email-minyard@acm.org>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent b8fb9043
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -117,10 +117,16 @@ static void smb_transaction(PMSMBus *s)
        break;
    case PROT_I2C_BLOCK_DATA:
        if (read) {
            ret = smbus_read_block(bus, addr, cmd, s->smb_data);
            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 {
            ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
            ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0,
                                    false);
            goto done;
        }
        break;
+24 −13
Original line number Diff line number Diff line
@@ -293,33 +293,42 @@ int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data)
    return 0;
}

int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data)
int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
                     int len, bool recv_len, bool send_cmd)
{
    int len;
    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;
    }
    len = i2c_recv(bus);
    if (len > 32) {
        len = 0;
    if (recv_len) {
        rlen = i2c_recv(bus);
    } else {
        rlen = len;
    }
    if (rlen > len) {
        rlen = 0;
    }
    for (i = 0; i < len; i++) {
    for (i = 0; i < rlen; i++) {
        data[i] = i2c_recv(bus);
    }
    i2c_nack(bus);
    i2c_end_transfer(bus);
    return len;
    return rlen;
}

int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
                      int len)
                      int len, bool send_len)
{
    int i;

@@ -330,7 +339,9 @@ int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
        return -1;
    }
    i2c_send(bus, command);
    if (send_len) {
        i2c_send(bus, len);
    }
    for (i = 0; i < len; i++) {
        i2c_send(bus, data[i]);
    }
+15 −2
Original line number Diff line number Diff line
@@ -72,9 +72,22 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command);
int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data);
int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command);
int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data);
int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data);

/*
 * Do a block transfer from an I2C device.  If recv_len is set, then the
 * first received byte is a length field and is used to know how much data
 * to receive.  Otherwise receive "len" bytes.  If send_cmd is set, send
 * the command byte first before receiving the data.
 */
int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
                     int len, bool recv_len, bool send_cmd);

/*
 * Do a block transfer to an I2C device.  If send_len is set, send the
 * "len" value before the data.
 */
int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data,
                      int len);
                      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,