Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 316355
b: refs/heads/master
c: d3ff6ce
h: refs/heads/master
i:
  316353: 527e103
  316351: 3a48e5f
v: v3
  • Loading branch information
Daniel Kurtz authored and Jean Delvare committed Jul 24, 2012
1 parent 19f4bb4 commit 7be0e34
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 8 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 29b608540b030d38a978c972cbe99d40efdb7267
refs/heads/master: d3ff6ce40031e8401eab60c3de7db9b1f3f4c08b
85 changes: 78 additions & 7 deletions trunk/drivers/i2c/busses/i2c-i801.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ struct i801_priv {
/* isr processing */
wait_queue_head_t waitq;
u8 status;

/* Command state used by isr for byte-by-byte block transactions */
u8 cmd;
bool is_read;
int count;
int len;
u8 *data;
};

static struct pci_driver i801_driver;
Expand Down Expand Up @@ -373,14 +380,60 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
return 0;
}

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));
}

/*
* i801 signals transaction completion with one of these interrupts:
* INTR - Success
* DEV_ERR - Invalid command, NAK or communication timeout
* BUS_ERR - SMI# transaction collision
* FAILED - transaction was canceled due to a KILL request
* When any of these occur, update ->status and wake up the waitq.
* ->status must be cleared before kicking off the next transaction.
* There are two kinds of interrupts:
*
* 1) i801 signals transaction completion with one of these interrupts:
* INTR - Success
* DEV_ERR - Invalid command, NAK or communication timeout
* BUS_ERR - SMI# transaction collision
* FAILED - transaction was canceled due to a KILL request
* When any of these occur, update ->status and wake up the waitq.
* ->status must be cleared before kicking off the next transaction.
*
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
* occurs for each byte of a byte-by-byte to prepare the next byte.
*/
static irqreturn_t i801_isr(int irq, void *dev_id)
{
Expand All @@ -397,6 +450,9 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
if (status != 0x42)
dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status);

if (status & SMBHSTSTS_BYTE_DONE)
i801_isr_byte_done(priv);

/*
* Clear irq sources and report transaction result.
* ->status must be cleared before the next transaction is started.
Expand Down Expand Up @@ -443,6 +499,21 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
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));
wait_event(priv->waitq, (status = priv->status));
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;
Expand Down

0 comments on commit 7be0e34

Please sign in to comment.