diff options
| author | Daniel Kurtz <djkurtz@chromium.org> | 2012-03-30 19:46:40 +0800 | 
|---|---|---|
| committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-04-12 21:14:07 +0200 | 
| commit | 56f9eac05489912ac0165ffc0ebff0f8588f77d2 (patch) | |
| tree | 5866d4fe25e1e618ec8680e9eb25c0bbf85ec8ae /drivers/gpu/drm/i915/intel_i2c.c | |
| parent | 72d66afd1461effb143784a0f6cde2a9f9908b70 (diff) | |
| download | olio-linux-3.10-56f9eac05489912ac0165ffc0ebff0f8588f77d2.tar.xz olio-linux-3.10-56f9eac05489912ac0165ffc0ebff0f8588f77d2.zip  | |
drm/i915/intel_i2c: use INDEX cycles for i2c read transactions
It is very common for an i2c device to require a small 1 or 2 byte write
followed by a read.  For example, when reading from an i2c EEPROM it is
common to write and address, offset or index followed by a reading some
values.
The i915 gmbus controller provides a special "INDEX" cycle for performing
such a small write followed by a read.  The INDEX can be either one or two
bytes long.  The advantage of using such a cycle is that the CPU has
slightly less work to do once the read with INDEX cycle is started.
Signed-off-by: Daniel Kurtz <djkurtz@chromium.org>
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/i915/intel_i2c.c')
| -rw-r--r-- | drivers/gpu/drm/i915/intel_i2c.c | 54 | 
1 files changed, 50 insertions, 4 deletions
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 291e51ec309..5e0912a4a73 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -204,13 +204,15 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)  }  static int -gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg) +gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, +		u32 gmbus1_index)  {  	int reg_offset = dev_priv->gpio_mmio_base;  	u16 len = msg->len;  	u8 *buf = msg->buf;  	I915_WRITE(GMBUS1 + reg_offset, +		   gmbus1_index |  		   GMBUS_CYCLE_WAIT |  		   (len << GMBUS_BYTE_COUNT_SHIFT) |  		   (msg->addr << GMBUS_SLAVE_ADDR_SHIFT) | @@ -276,6 +278,46 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg)  	return 0;  } +/* + * The gmbus controller can combine a 1 or 2 byte write with a read that + * immediately follows it by using an "INDEX" cycle. + */ +static bool +gmbus_is_index_read(struct i2c_msg *msgs, int i, int num) +{ +	return (i + 1 < num && +		!(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 && +		(msgs[i + 1].flags & I2C_M_RD)); +} + +static int +gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs) +{ +	int reg_offset = dev_priv->gpio_mmio_base; +	u32 gmbus1_index = 0; +	u32 gmbus5 = 0; +	int ret; + +	if (msgs[0].len == 2) +		gmbus5 = GMBUS_2BYTE_INDEX_EN | +			 msgs[0].buf[1] | (msgs[0].buf[0] << 8); +	if (msgs[0].len == 1) +		gmbus1_index = GMBUS_CYCLE_INDEX | +			       (msgs[0].buf[0] << GMBUS_SLAVE_INDEX_SHIFT); + +	/* GMBUS5 holds 16-bit index */ +	if (gmbus5) +		I915_WRITE(GMBUS5 + reg_offset, gmbus5); + +	ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index); + +	/* Clear GMBUS5 after each index transfer */ +	if (gmbus5) +		I915_WRITE(GMBUS5 + reg_offset, 0); + +	return ret; +} +  static int  gmbus_xfer(struct i2c_adapter *adapter,  	   struct i2c_msg *msgs, @@ -300,10 +342,14 @@ gmbus_xfer(struct i2c_adapter *adapter,  	I915_WRITE(GMBUS0 + reg_offset, bus->reg0);  	for (i = 0; i < num; i++) { -		if (msgs[i].flags & I2C_M_RD) -			ret = gmbus_xfer_read(dev_priv, &msgs[i]); -		else +		if (gmbus_is_index_read(msgs, i, num)) { +			ret = gmbus_xfer_index_read(dev_priv, &msgs[i]); +			i += 1;  /* set i to the index of the read xfer */ +		} else if (msgs[i].flags & I2C_M_RD) { +			ret = gmbus_xfer_read(dev_priv, &msgs[i], 0); +		} else {  			ret = gmbus_xfer_write(dev_priv, &msgs[i]); +		}  		if (ret == -ETIMEDOUT)  			goto timeout;  |