mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-18 07:47:50 +07:00
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
|
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;
|
int reg_offset = dev_priv->gpio_mmio_base;
|
||||||
u16 len = msg->len;
|
u16 len = msg->len;
|
||||||
u8 *buf = msg->buf;
|
u8 *buf = msg->buf;
|
||||||
|
|
||||||
I915_WRITE(GMBUS1 + reg_offset,
|
I915_WRITE(GMBUS1 + reg_offset,
|
||||||
|
gmbus1_index |
|
||||||
GMBUS_CYCLE_WAIT |
|
GMBUS_CYCLE_WAIT |
|
||||||
(len << GMBUS_BYTE_COUNT_SHIFT) |
|
(len << GMBUS_BYTE_COUNT_SHIFT) |
|
||||||
(msg->addr << GMBUS_SLAVE_ADDR_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;
|
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
|
static int
|
||||||
gmbus_xfer(struct i2c_adapter *adapter,
|
gmbus_xfer(struct i2c_adapter *adapter,
|
||||||
struct i2c_msg *msgs,
|
struct i2c_msg *msgs,
|
||||||
@ -300,10 +342,14 @@ gmbus_xfer(struct i2c_adapter *adapter,
|
|||||||
I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
|
I915_WRITE(GMBUS0 + reg_offset, bus->reg0);
|
||||||
|
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
if (msgs[i].flags & I2C_M_RD)
|
if (gmbus_is_index_read(msgs, i, num)) {
|
||||||
ret = gmbus_xfer_read(dev_priv, &msgs[i]);
|
ret = gmbus_xfer_index_read(dev_priv, &msgs[i]);
|
||||||
else
|
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]);
|
ret = gmbus_xfer_write(dev_priv, &msgs[i]);
|
||||||
|
}
|
||||||
|
|
||||||
if (ret == -ETIMEDOUT)
|
if (ret == -ETIMEDOUT)
|
||||||
goto timeout;
|
goto timeout;
|
||||||
|
Loading…
Reference in New Issue
Block a user