static int i801_block_transaction_by_block(struct i801_priv *priv, union i2c_smbus_data *data, char read_write, int hwpec) { int i, len; int status; inb_p(SMBHSTCNT(priv)); /* reset the data buffer index */ /* Use 32-byte buffer to process this transaction */ if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; outb_p(len, SMBHSTDAT0(priv)); for (i = 0; i < len; i++) outb_p(data->block[i+1], SMBBLKDAT(priv)); } status = i801_transaction(priv, I801_BLOCK_DATA | (hwpec ? SMBHSTCNT_PEC_EN : 0)); if (status) return status; if (read_write == I2C_SMBUS_READ) { len = inb_p(SMBHSTDAT0(priv)); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) return -EPROTO; data->block[0] = len; for (i = 0; i < len; i++) data->block[i + 1] = inb_p(SMBBLKDAT(priv)); } return 0; }
/** * \brief Read a byte from the SMBus. * * @param smbus_dev The base SMBus IO port * @param addr The address location of the DIMM on the SMBus. * @param offset The offset the data is located at. */ u8 smbus_read_byte(u32 smbus_dev, u8 addr, u8 offset) { u8 val; /* Initialize SMBus sequence */ smbus_reset(smbus_dev); /* Clear host data port. */ outb(0x00, SMBHSTDAT0(smbus_dev)); smbus_wait_until_ready(smbus_dev); /* Actual addr to reg format. */ addr = (addr << 1); addr |= 1; /* read command */ outb(addr, SMBXMITADD(smbus_dev)); outb(offset, SMBHSTCMD(smbus_dev)); /* Start transaction, byte data read. */ outb(0x48, SMBHSTCTL(smbus_dev)); smbus_wait_until_ready(smbus_dev); val = inb(SMBHSTDAT0(smbus_dev)); return val; }
static void i801_isr_byte_done(struct i801_priv *priv) { if (priv->is_read) { /* For SMBus block reads, length is received with first byte */ if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) && (priv->count == 0)) { priv->len = inb_p(SMBHSTDAT0(priv)); if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { dev_err(&priv->pci_dev->dev, "Illegal SMBus block read size %d\n", priv->len); /* FIXME: Recover */ priv->len = I2C_SMBUS_BLOCK_MAX; } else { dev_dbg(&priv->pci_dev->dev, "SMBus block read size is %d\n", priv->len); } priv->data[-1] = priv->len; } /* Read next byte */ if (priv->count < priv->len) priv->data[priv->count++] = inb(SMBBLKDAT(priv)); else dev_dbg(&priv->pci_dev->dev, "Discarding extra byte on block read\n"); /* Set LAST_BYTE for last byte of read transaction */ if (priv->count == priv->len - 1) outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE, SMBHSTCNT(priv)); } else if (priv->count < priv->len - 1) { /* Write next byte, except for IRQ after last byte */ outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); } /* Clear BYTE_DONE to continue with next byte */ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); }
/* Return negative errno on error. */ static s32 i801_access(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data) { int hwpec; int block = 0; int ret = 0, xact = 0; struct i801_priv *priv = i2c_get_adapdata(adap); mutex_lock(&priv->acpi_lock); if (priv->acpi_reserved) { mutex_unlock(&priv->acpi_lock); return -EBUSY; } hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC) && size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA; switch (size) { case I2C_SMBUS_QUICK: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); xact = I801_QUICK; break; case I2C_SMBUS_BYTE: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); if (read_write == I2C_SMBUS_WRITE) outb_p(command, SMBHSTCMD(priv)); xact = I801_BYTE; break; case I2C_SMBUS_BYTE_DATA: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); outb_p(command, SMBHSTCMD(priv)); if (read_write == I2C_SMBUS_WRITE) outb_p(data->byte, SMBHSTDAT0(priv)); xact = I801_BYTE_DATA; break; case I2C_SMBUS_WORD_DATA: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); outb_p(command, SMBHSTCMD(priv)); if (read_write == I2C_SMBUS_WRITE) { outb_p(data->word & 0xff, SMBHSTDAT0(priv)); outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv)); } xact = I801_WORD_DATA; break; case I2C_SMBUS_BLOCK_DATA: outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), SMBHSTADD(priv)); outb_p(command, SMBHSTCMD(priv)); block = 1; break; case I2C_SMBUS_I2C_BLOCK_DATA: /* NB: page 240 of ICH5 datasheet shows that the R/#W * bit should be cleared here, even when reading */ outb_p((addr & 0x7f) << 1, SMBHSTADD(priv)); if (read_write == I2C_SMBUS_READ) { /* NB: page 240 of ICH5 datasheet also shows * that DATA1 is the cmd field when reading */ outb_p(command, SMBHSTDAT1(priv)); } else outb_p(command, SMBHSTCMD(priv)); block = 1; break; default: dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n", size); ret = -EOPNOTSUPP; goto out; }
/* * For "byte-by-byte" block transactions: * I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1 * I2C read uses cmd=I801_I2C_BLOCK_DATA */ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) { int i, len; int smbcmd; int status; int result; const struct i2c_adapter *adap = &priv->adapter; result = i801_check_pre(priv); if (result < 0) return result; len = data->block[0]; if (read_write == I2C_SMBUS_WRITE) { outb_p(len, SMBHSTDAT0(priv)); outb_p(data->block[1], SMBBLKDAT(priv)); } if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) smbcmd = I801_I2C_BLOCK_DATA; else smbcmd = I801_BLOCK_DATA; if (priv->features & FEATURE_IRQ) { priv->is_read = (read_write == I2C_SMBUS_READ); if (len == 1 && priv->is_read) smbcmd |= SMBHSTCNT_LAST_BYTE; priv->cmd = smbcmd | SMBHSTCNT_INTREN; priv->len = len; priv->count = 0; priv->data = &data->block[1]; outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); result = wait_event_timeout(priv->waitq, (status = priv->status), adap->timeout); if (!result) { status = -ETIMEDOUT; dev_warn(&priv->pci_dev->dev, "Timeout waiting for interrupt!\n"); } priv->status = 0; return i801_check_post(priv, status); } for (i = 1; i <= len; i++) { if (i == len && read_write == I2C_SMBUS_READ) smbcmd |= SMBHSTCNT_LAST_BYTE; outb_p(smbcmd, SMBHSTCNT(priv)); if (i == 1) outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START, SMBHSTCNT(priv)); status = i801_wait_byte_done(priv); if (status) goto exit; if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) { len = inb_p(SMBHSTDAT0(priv)); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { dev_err(&priv->pci_dev->dev, "Illegal SMBus block read size %d\n", len); /* Recover */ while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); return -EPROTO; } data->block[0] = len; } /* Retrieve/store value in SMBBLKDAT */ if (read_write == I2C_SMBUS_READ) data->block[i] = inb_p(SMBBLKDAT(priv)); if (read_write == I2C_SMBUS_WRITE && i+1 <= len) outb_p(data->block[i+1], SMBBLKDAT(priv)); /* signals SMBBLKDAT ready */ outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); } status = i801_wait_intr(priv); exit: return i801_check_post(priv, status); }
static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, union i2c_smbus_data *data, char read_write, int command, int hwpec) { int i, len; int smbcmd; int status; int result; int timeout; result = i801_check_pre(priv); if (result < 0) return result; len = data->block[0]; if (read_write == I2C_SMBUS_WRITE) { outb_p(len, SMBHSTDAT0(priv)); outb_p(data->block[1], SMBBLKDAT(priv)); } for (i = 1; i <= len; i++) { if (i == len && read_write == I2C_SMBUS_READ) { if (command == I2C_SMBUS_I2C_BLOCK_DATA) smbcmd = I801_I2C_BLOCK_LAST; else smbcmd = I801_BLOCK_LAST; } else { if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_READ) smbcmd = I801_I2C_BLOCK_DATA; else smbcmd = I801_BLOCK_DATA; } outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv)); if (i == 1) outb_p(inb(SMBHSTCNT(priv)) | I801_START, SMBHSTCNT(priv)); /* We will always wait for a fraction of a second! */ timeout = 0; do { msleep(1); status = inb_p(SMBHSTSTS(priv)); } while ((!(status & SMBHSTSTS_BYTE_DONE)) && (timeout++ < MAX_TIMEOUT)); result = i801_check_post(priv, status, timeout > MAX_TIMEOUT); if (result < 0) return result; if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) { len = inb_p(SMBHSTDAT0(priv)); if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) { dev_err(&priv->pci_dev->dev, "Illegal SMBus block read size %d\n", len); /* Recover */ while (inb_p(SMBHSTSTS(priv)) & SMBHSTSTS_HOST_BUSY) outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); return -EPROTO; } data->block[0] = len; } /* Retrieve/store value in SMBBLKDAT */ if (read_write == I2C_SMBUS_READ) data->block[i] = inb_p(SMBBLKDAT(priv)); if (read_write == I2C_SMBUS_WRITE && i+1 <= len) outb_p(data->block[i+1], SMBBLKDAT(priv)); /* signals SMBBLKDAT ready */ outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); } return 0; }