diff options
author | Daniel Kurtz <djkurtz@chromium.org> | 2012-03-30 07:46:40 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-04-12 15:14:07 -0400 |
commit | 56f9eac05489912ac0165ffc0ebff0f8588f77d2 (patch) | |
tree | 5866d4fe25e1e618ec8680e9eb25c0bbf85ec8ae /drivers/gpu/drm/i915/intel_i2c.c | |
parent | 72d66afd1461effb143784a0f6cde2a9f9908b70 (diff) |
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 291e51ec309b..5e0912a4a737 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) | |||
204 | } | 204 | } |
205 | 205 | ||
206 | static int | 206 | static int |
207 | gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg) | 207 | gmbus_xfer_read(struct drm_i915_private *dev_priv, struct i2c_msg *msg, |
208 | u32 gmbus1_index) | ||
208 | { | 209 | { |
209 | int reg_offset = dev_priv->gpio_mmio_base; | 210 | int reg_offset = dev_priv->gpio_mmio_base; |
210 | u16 len = msg->len; | 211 | u16 len = msg->len; |
211 | u8 *buf = msg->buf; | 212 | u8 *buf = msg->buf; |
212 | 213 | ||
213 | I915_WRITE(GMBUS1 + reg_offset, | 214 | I915_WRITE(GMBUS1 + reg_offset, |
215 | gmbus1_index | | ||
214 | GMBUS_CYCLE_WAIT | | 216 | GMBUS_CYCLE_WAIT | |
215 | (len << GMBUS_BYTE_COUNT_SHIFT) | | 217 | (len << GMBUS_BYTE_COUNT_SHIFT) | |
216 | (msg->addr << GMBUS_SLAVE_ADDR_SHIFT) | | 218 | (msg->addr << GMBUS_SLAVE_ADDR_SHIFT) | |
@@ -276,6 +278,46 @@ gmbus_xfer_write(struct drm_i915_private *dev_priv, struct i2c_msg *msg) | |||
276 | return 0; | 278 | return 0; |
277 | } | 279 | } |
278 | 280 | ||
281 | /* | ||
282 | * The gmbus controller can combine a 1 or 2 byte write with a read that | ||
283 | * immediately follows it by using an "INDEX" cycle. | ||
284 | */ | ||
285 | static bool | ||
286 | gmbus_is_index_read(struct i2c_msg *msgs, int i, int num) | ||
287 | { | ||
288 | return (i + 1 < num && | ||
289 | !(msgs[i].flags & I2C_M_RD) && msgs[i].len <= 2 && | ||
290 | (msgs[i + 1].flags & I2C_M_RD)); | ||
291 | } | ||
292 | |||
293 | static int | ||
294 | gmbus_xfer_index_read(struct drm_i915_private *dev_priv, struct i2c_msg *msgs) | ||
295 | { | ||
296 | int reg_offset = dev_priv->gpio_mmio_base; | ||
297 | u32 gmbus1_index = 0; | ||
298 | u32 gmbus5 = 0; | ||
299 | int ret; | ||
300 | |||
301 | if (msgs[0].len == 2) | ||
302 | gmbus5 = GMBUS_2BYTE_INDEX_EN | | ||
303 | msgs[0].buf[1] | (msgs[0].buf[0] << 8); | ||
304 | if (msgs[0].len == 1) | ||
305 | gmbus1_index = GMBUS_CYCLE_INDEX | | ||
306 | (msgs[0].buf[0] << GMBUS_SLAVE_INDEX_SHIFT); | ||
307 | |||
308 | /* GMBUS5 holds 16-bit index */ | ||
309 | if (gmbus5) | ||
310 | I915_WRITE(GMBUS5 + reg_offset, gmbus5); | ||
311 | |||
312 | ret = gmbus_xfer_read(dev_priv, &msgs[1], gmbus1_index); | ||
313 | |||
314 | /* Clear GMBUS5 after each index transfer */ | ||
315 | if (gmbus5) | ||
316 | I915_WRITE(GMBUS5 + reg_offset, 0); | ||
317 | |||
318 | return ret; | ||
319 | } | ||
320 | |||
279 | static int | 321 | static int |
280 | gmbus_xfer(struct i2c_adapter *adapter, | 322 | gmbus_xfer(struct i2c_adapter *adapter, |
281 | struct i2c_msg *msgs, | 323 | struct i2c_msg *msgs, |
@@ -300,10 +342,14 @@ gmbus_xfer(struct i2c_adapter *adapter, | |||
300 | I915_WRITE(GMBUS0 + reg_offset, bus->reg0); | 342 | I915_WRITE(GMBUS0 + reg_offset, bus->reg0); |
301 | 343 | ||
302 | for (i = 0; i < num; i++) { | 344 | for (i = 0; i < num; i++) { |
303 | if (msgs[i].flags & I2C_M_RD) | 345 | if (gmbus_is_index_read(msgs, i, num)) { |
304 | ret = gmbus_xfer_read(dev_priv, &msgs[i]); | 346 | ret = gmbus_xfer_index_read(dev_priv, &msgs[i]); |
305 | else | 347 | i += 1; /* set i to the index of the read xfer */ |
348 | } else if (msgs[i].flags & I2C_M_RD) { | ||
349 | ret = gmbus_xfer_read(dev_priv, &msgs[i], 0); | ||
350 | } else { | ||
306 | ret = gmbus_xfer_write(dev_priv, &msgs[i]); | 351 | ret = gmbus_xfer_write(dev_priv, &msgs[i]); |
352 | } | ||
307 | 353 | ||
308 | if (ret == -ETIMEDOUT) | 354 | if (ret == -ETIMEDOUT) |
309 | goto timeout; | 355 | goto timeout; |