mirror of https://gitee.com/openkylin/linux.git
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>
This commit is contained in:
parent
72d66afd14
commit
56f9eac054
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue