static int i801_transaction(struct i801_priv *priv, int xact) { int status; int result; const struct i2c_adapter *adap = &priv->adapter; result = i801_check_pre(priv); if (result < 0) return result; if (priv->features & FEATURE_IRQ) { outb_p(xact | SMBHSTCNT_INTREN | 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); } /* the current contents of SMBHSTCNT can be overwritten, since PEC, * SMBSCMD are passed in xact */ outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); status = i801_wait_intr(priv); return i801_check_post(priv, status); }
/* Convert the status register to an error code, and clear it. */ static int i801_check_post(struct i801_priv *priv, int status, int timeout) { int result = 0; /* If the SMBus is still busy, we give up */ if (timeout) { dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); /* try to stop the current command */ dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, SMBHSTCNT(priv)); msleep(1); outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), SMBHSTCNT(priv)); /* Check if it worked */ status = inb_p(SMBHSTSTS(priv)); if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) dev_err(&priv->pci_dev->dev, "Failed terminating the transaction\n"); outb_p(STATUS_FLAGS, SMBHSTSTS(priv)); return -ETIMEDOUT; } if (status & SMBHSTSTS_FAILED) { result = -EIO; dev_err(&priv->pci_dev->dev, "Transaction failed\n"); } if (status & SMBHSTSTS_DEV_ERR) { result = -ENXIO; dev_dbg(&priv->pci_dev->dev, "No response\n"); } if (status & SMBHSTSTS_BUS_ERR) { result = -EAGAIN; dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); } if (result) { /* Clear error flags */ outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; if (status) { dev_warn(&priv->pci_dev->dev, "Failed clearing status " "flags at end of transaction (%02x)\n", status); } } return result; }
/* * Convert the status register to an error code, and clear it. * Note that status only contains the bits we want to clear, not the * actual register value. */ static int i801_check_post(struct i801_priv *priv, int status) { int result = 0; /* * If the SMBus is still busy, we give up * Note: This timeout condition only happens when using polling * transactions. For interrupt operation, NAK/timeout is indicated by * DEV_ERR. */ if (unlikely(status < 0)) { dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); /* try to stop the current command */ dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL, SMBHSTCNT(priv)); usleep_range(1000, 2000); outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL), SMBHSTCNT(priv)); /* Check if it worked */ status = inb_p(SMBHSTSTS(priv)); if ((status & SMBHSTSTS_HOST_BUSY) || !(status & SMBHSTSTS_FAILED)) dev_err(&priv->pci_dev->dev, "Failed terminating the transaction\n"); outb_p(STATUS_FLAGS, SMBHSTSTS(priv)); return -ETIMEDOUT; } if (status & SMBHSTSTS_FAILED) { result = -EIO; dev_err(&priv->pci_dev->dev, "Transaction failed\n"); } if (status & SMBHSTSTS_DEV_ERR) { result = -ENXIO; dev_dbg(&priv->pci_dev->dev, "No response\n"); } if (status & SMBHSTSTS_BUS_ERR) { result = -EAGAIN; dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); } /* Clear status flags except BYTE_DONE, to be cleared by caller */ outb_p(status, SMBHSTSTS(priv)); return result; }
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; }
static int i801_transaction(struct i801_priv *priv, int xact) { int status; int result; int timeout = 0; result = i801_check_pre(priv); if (result < 0) return result; /* the current contents of SMBHSTCNT can be overwritten, since PEC, * INTREN, SMBSCMD are passed in xact */ outb_p(xact | I801_START, SMBHSTCNT(priv)); /* We will always wait for a fraction of a second! */ do { msleep(1); status = inb_p(SMBHSTSTS(priv)); } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_TIMEOUT)); result = i801_check_post(priv, status, timeout > MAX_TIMEOUT); if (result < 0) return result; outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); return 0; }
static int i801_transaction(struct i801_priv *priv, int xact) { int status; int result; result = i801_check_pre(priv); if (result < 0) return result; if (priv->features & FEATURE_IRQ) { outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, SMBHSTCNT(priv)); wait_event(priv->waitq, (status = priv->status)); priv->status = 0; return i801_check_post(priv, status); } /* the current contents of SMBHSTCNT can be overwritten, since PEC, * SMBSCMD are passed in xact */ outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); status = i801_wait_intr(priv); return i801_check_post(priv, status); }
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)); }
/* * 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; }