Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6342064c authored by Jean Delvare's avatar Jean Delvare Committed by Jean Delvare
Browse files

i2c-i801: Implement I2C block read support



I2C block read is supported since the ICH5. I couldn't get it to work
using the block buffer, so it's using the old-style byte-by-byte mode
for now.

Note: I'm also updating the driver author... The i2c-i801 driver was
really written by Mark Studebaker, even though he based his work on
the i2c-piix4 driver which was written by Philip Edelbrock.

Signed-off-by: default avatarJean Delvare <khali@linux-fr.org>
parent a0921b6c
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -17,9 +17,8 @@ Supported adapters:
   Datasheets: Publicly available at the Intel website

Authors: 
	Frodo Looijaard <frodol@dds.nl>, 
	Philip Edelbrock <phil@netroedge.com>, 
	Mark Studebaker <mdsxyz123@yahoo.com>
	Jean Delvare <khali@linux-fr.org>


Module Parameters
@@ -62,7 +61,7 @@ Not supported.
I2C Block Read Support
----------------------

Not supported at the moment.
I2C block read is supported on the 82801EB (ICH5) and later chips.


SMBus 2.0 Support
+54 −25
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@
    Copyright (c) 1998 - 2002  Frodo Looijaard <frodol@dds.nl>,
    Philip Edelbrock <phil@netroedge.com>, and Mark D. Studebaker
    <mdsxyz123@yahoo.com>
    Copyright (C) 2007         Jean Delvare <khali@linux-fr.org>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -46,7 +47,7 @@
  Hardware PEC                     yes
  Block buffer                     yes
  Block process call transaction   no
  I2C block read transaction       no
  I2C block read transaction       yes  (doesn't use the block buffer)

  See the file Documentation/i2c/busses/i2c-i801 for details.
*/
@@ -102,9 +103,9 @@
#define I801_WORD_DATA		0x0C
#define I801_PROC_CALL		0x10	/* unimplemented */
#define I801_BLOCK_DATA		0x14
#define I801_I2C_BLOCK_DATA	0x18	/* unimplemented */
#define I801_I2C_BLOCK_DATA	0x18	/* ICH5 and later */
#define I801_BLOCK_LAST		0x34
#define I801_I2C_BLOCK_LAST	0x38	/* unimplemented */
#define I801_I2C_BLOCK_LAST	0x38	/* ICH5 and later */
#define I801_START		0x40
#define I801_PEC_EN		0x80	/* ICH3 and later */

@@ -256,7 +257,8 @@ static int i801_block_transaction_by_block(union i2c_smbus_data *data,
}

static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
					       char read_write, int hwpec)
					       char read_write, int command,
					       int hwpec)
{
	int i, len;
	int smbcmd;
@@ -273,16 +275,24 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
	}

	for (i = 1; i <= len; i++) {
		if (i == len && read_write == I2C_SMBUS_READ)
		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);

		dev_dbg(&I801_dev->dev, "Block (pre %d): CNT=%02x, CMD=%02x, "
			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
			"ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i,
			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
			inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT));

		/* Make sure the SMBus host is ready to start transmitting */
		temp = inb_p(SMBHSTSTS);
@@ -346,7 +356,8 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
			dev_dbg(&I801_dev->dev, "Error: no response!\n");
		}

		if (i == 1 && read_write == I2C_SMBUS_READ) {
		if (i == 1 && read_write == I2C_SMBUS_READ
		 && command != I2C_SMBUS_I2C_BLOCK_DATA) {
			len = inb_p(SMBHSTDAT0);
			if (len < 1 || len > I2C_SMBUS_BLOCK_MAX)
				return -1;
@@ -367,9 +378,9 @@ static int i801_block_transaction_byte_by_byte(union i2c_smbus_data *data,
				temp);
		}
		dev_dbg(&I801_dev->dev, "Block (post %d): CNT=%02x, CMD=%02x, "
			"ADD=%02x, DAT0=%02x, BLKDAT=%02x\n", i,
			"ADD=%02x, DAT0=%02x, DAT1=%02x, BLKDAT=%02x\n", i,
			inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), inb_p(SMBHSTADD),
			inb_p(SMBHSTDAT0), inb_p(SMBBLKDAT));
			inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1), inb_p(SMBBLKDAT));

		if (result < 0)
			return result;
@@ -398,34 +409,38 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write,
			pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc);
			pci_write_config_byte(I801_dev, SMBHSTCFG,
					      hostc | SMBHSTCFG_I2C_EN);
		} else {
		} else if (!(i801_features & FEATURE_I2C_BLOCK_READ)) {
			dev_err(&I801_dev->dev,
				"I2C_SMBUS_I2C_BLOCK_READ not DB!\n");
				"I2C block read is unsupported!\n");
			return -1;
		}
	}

	if (read_write == I2C_SMBUS_WRITE) {
	if (read_write == I2C_SMBUS_WRITE
	 || command == I2C_SMBUS_I2C_BLOCK_DATA) {
		if (data->block[0] < 1)
			data->block[0] = 1;
		if (data->block[0] > I2C_SMBUS_BLOCK_MAX)
			data->block[0] = I2C_SMBUS_BLOCK_MAX;
	} else {
		data->block[0] = 32;	/* max for reads */
		data->block[0] = 32;	/* max for SMBus block reads */
	}

	if ((i801_features & FEATURE_BLOCK_BUFFER)
	 && !(command == I2C_SMBUS_I2C_BLOCK_DATA
	      && read_write == I2C_SMBUS_READ)
	 && i801_set_block_buffer_mode() == 0)
		result = i801_block_transaction_by_block(data, read_write,
							 hwpec);
	else
		result = i801_block_transaction_byte_by_byte(data, read_write,
							     hwpec);
							     command, hwpec);

	if (result == 0 && hwpec)
		i801_wait_hwpec();

	if (command == I2C_SMBUS_I2C_BLOCK_DATA) {
	if (command == I2C_SMBUS_I2C_BLOCK_DATA
	 && read_write == I2C_SMBUS_WRITE) {
		/* restore saved configuration register value */
		pci_write_config_byte(I801_dev, SMBHSTCFG, hostc);
	}
@@ -477,12 +492,23 @@ static s32 i801_access(struct i2c_adapter * adap, u16 addr,
		xact = I801_WORD_DATA;
		break;
	case I2C_SMBUS_BLOCK_DATA:
	case I2C_SMBUS_I2C_BLOCK_DATA:
		outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
		       SMBHSTADD);
		outb_p(command, SMBHSTCMD);
		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);
		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);
		} else
			outb_p(command, SMBHSTCMD);
		block = 1;
		break;
	case I2C_SMBUS_PROC_CALL:
	default:
		dev_err(&I801_dev->dev, "Unsupported transaction %d\n", size);
@@ -531,7 +557,9 @@ static u32 i801_func(struct i2c_adapter *adapter)
	return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
	       I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
	       I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
	       ((i801_features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0);
	       ((i801_features & FEATURE_SMBUS_PEC) ? I2C_FUNC_SMBUS_PEC : 0) |
	       ((i801_features & FEATURE_I2C_BLOCK_READ) ?
		I2C_FUNC_SMBUS_READ_I2C_BLOCK : 0);
}

static const struct i2c_algorithm smbus_algorithm = {
@@ -573,7 +601,6 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
	I801_dev = dev;
	i801_features = 0;
	switch (dev->device) {
	case PCI_DEVICE_ID_INTEL_82801DB_3:
	case PCI_DEVICE_ID_INTEL_82801EB_3:
	case PCI_DEVICE_ID_INTEL_ESB_4:
	case PCI_DEVICE_ID_INTEL_ICH6_16:
@@ -581,6 +608,9 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
	case PCI_DEVICE_ID_INTEL_ESB2_17:
	case PCI_DEVICE_ID_INTEL_ICH8_5:
	case PCI_DEVICE_ID_INTEL_ICH9_6:
		i801_features |= FEATURE_I2C_BLOCK_READ;
		/* fall through */
	case PCI_DEVICE_ID_INTEL_82801DB_3:
	case PCI_DEVICE_ID_INTEL_TOLAPAI_1:
		i801_features |= FEATURE_SMBUS_PEC;
		i801_features |= FEATURE_BLOCK_BUFFER;
@@ -698,9 +728,8 @@ static void __exit i2c_i801_exit(void)
	pci_unregister_driver(&i801_driver);
}

MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, "
		"Philip Edelbrock <phil@netroedge.com>, "
		"and Mark D. Studebaker <mdsxyz123@yahoo.com>");
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>, "
	      "Jean Delvare <khali@linux-fr.org>");
MODULE_DESCRIPTION("I801 SMBus driver");
MODULE_LICENSE("GPL");