Example #1
0
/*
 * 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)
{
	struct i801_priv *priv = dev_id;
	u16 pcists;
	u8 status;

	/* Confirm this is our interrupt */
	pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
	if (!(pcists & SMBPCISTS_INTS))
		return IRQ_NONE;

	status = inb_p(SMBHSTSTS(priv));
	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.
	 */
	status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS;
	if (status) {
		outb_p(status, SMBHSTSTS(priv));
		priv->status |= status;
		wake_up(&priv->waitq);
	}

	return IRQ_HANDLED;
}
Example #2
0
/* Make sure the SMBus host is ready to start transmitting.
   Return 0 if it is, -EBUSY if it is not. */
static int i801_check_pre(struct i801_priv *priv)
{
	int status;

	status = inb_p(SMBHSTSTS(priv));
	if (status & SMBHSTSTS_HOST_BUSY) {
		dev_err(&priv->pci_dev->dev, "SMBus is busy, can't use it!\n");
		return -EBUSY;
	}

	status &= STATUS_FLAGS;
	if (status) {
		dev_dbg(&priv->pci_dev->dev, "Clearing status flags (%02x)\n",
			status);
		outb_p(status, SMBHSTSTS(priv));
		status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS;
		if (status) {
			dev_err(&priv->pci_dev->dev,
				"Failed clearing status flags (%02x)\n",
				status);
			return -EBUSY;
		}
	}

	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;
}
/* 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;
}
Example #5
0
/*
 * 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;
}
/* wait for INTR bit as advised by Intel */
static void i801_wait_hwpec(struct i801_priv *priv)
{
    int timeout = 0;
    int status;

    do {
        msleep(1);
        status = inb_p(SMBHSTSTS(priv));
    } while ((!(status & SMBHSTSTS_INTR))
             && (timeout++ < MAX_TIMEOUT));

    if (timeout > MAX_TIMEOUT)
        dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n");

    outb_p(status, SMBHSTSTS(priv));
}
Example #7
0
/* Wait for either BYTE_DONE or an error flag being set */
static int i801_wait_byte_done(struct i801_priv *priv)
{
	int timeout = 0;
	int status;

	/* We will always wait for a fraction of a second! */
	do {
		usleep_range(250, 500);
		status = inb_p(SMBHSTSTS(priv));
	} while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
		 (timeout++ < MAX_RETRIES));

	if (timeout > MAX_RETRIES) {
		dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
		return -ETIMEDOUT;
	}
	return status & STATUS_ERROR_FLAGS;
}
Example #8
0
/* Wait for BUSY being cleared and either INTR or an error flag being set */
static int i801_wait_intr(struct i801_priv *priv)
{
	int timeout = 0;
	int status;

	/* We will always wait for a fraction of a second! */
	do {
		usleep_range(250, 500);
		status = inb_p(SMBHSTSTS(priv));
	} while (((status & SMBHSTSTS_HOST_BUSY) ||
		  !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
		 (timeout++ < MAX_RETRIES));

	if (timeout > MAX_RETRIES) {
		dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
		return -ETIMEDOUT;
	}
	return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
}
Example #9
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));
}
Example #10
0
/*
 * 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;
}