Skip to content

Commit

Permalink
drm/i915: Fix race condition in accessing GMBUS
Browse files Browse the repository at this point in the history
GMBUS has several ports and each has it's own corresponding
I2C adpater. When multiple I2C adapters call gmbus_xfer() at
the same time there is a race condition in using the underlying
GMBUS controller. Fixing this by adding a mutex lock when calling
gmbus_xfer().

v2: Moved gmbus_mutex below intel_gmbus and added comments.
Rebased to drm-intel-next-queued.

Signed-off-by: Yufeng Shen <miletus@chromium.org>
[danvet: Shortened the gmbus_mutex comment a bit and add the patch
revision comment to the commit message.]
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
  • Loading branch information
Yufeng Shen authored and Daniel Vetter committed Feb 14, 2012
1 parent b1d7e4b commit 8a8ed1f
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
4 changes: 4 additions & 0 deletions drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,10 @@ typedef struct drm_i915_private {
u32 reg0;
} *gmbus;

/** gmbus_mutex protects against concurrent usage of the single hw gmbus
* controller on different i2c buses. */
struct mutex gmbus_mutex;

struct pci_dev *bridge_dev;
struct intel_ring_buffer ring[I915_NUM_RINGS];
uint32_t next_seqno;
Expand Down
24 changes: 17 additions & 7 deletions drivers/gpu/drm/i915/intel_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,11 +233,15 @@ gmbus_xfer(struct i2c_adapter *adapter,
struct intel_gmbus,
adapter);
struct drm_i915_private *dev_priv = adapter->algo_data;
int i, reg_offset;
int i, reg_offset, ret;

if (bus->force_bit)
return intel_i2c_quirk_xfer(dev_priv,
mutex_lock(&dev_priv->gmbus_mutex);

if (bus->force_bit) {
ret = intel_i2c_quirk_xfer(dev_priv,
bus->force_bit, msgs, num);
goto out;
}

reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0;

Expand Down Expand Up @@ -321,7 +325,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
* start of the next xfer, till then let it sleep.
*/
I915_WRITE(GMBUS0 + reg_offset, 0);
return i;
ret = i;
goto out;

timeout:
DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
Expand All @@ -331,9 +336,12 @@ gmbus_xfer(struct i2c_adapter *adapter,
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff);
if (!bus->force_bit)
return -ENOMEM;

return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num);
ret = -ENOMEM;
else
ret = intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num);
out:
mutex_unlock(&dev_priv->gmbus_mutex);
return ret;
}

static u32 gmbus_func(struct i2c_adapter *adapter)
Expand Down Expand Up @@ -380,6 +388,8 @@ int intel_setup_gmbus(struct drm_device *dev)
if (dev_priv->gmbus == NULL)
return -ENOMEM;

mutex_init(&dev_priv->gmbus_mutex);

for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];

Expand Down

0 comments on commit 8a8ed1f

Please sign in to comment.