Commit 8a8ed1f5 authored by Yufeng Shen's avatar Yufeng Shen Committed by Daniel Vetter

drm/i915: Fix race condition in accessing GMBUS

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: default avatarYufeng 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: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parent b1d7e4b4
...@@ -313,6 +313,10 @@ typedef struct drm_i915_private { ...@@ -313,6 +313,10 @@ typedef struct drm_i915_private {
u32 reg0; u32 reg0;
} *gmbus; } *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 pci_dev *bridge_dev;
struct intel_ring_buffer ring[I915_NUM_RINGS]; struct intel_ring_buffer ring[I915_NUM_RINGS];
uint32_t next_seqno; uint32_t next_seqno;
......
...@@ -233,11 +233,15 @@ gmbus_xfer(struct i2c_adapter *adapter, ...@@ -233,11 +233,15 @@ gmbus_xfer(struct i2c_adapter *adapter,
struct intel_gmbus, struct intel_gmbus,
adapter); adapter);
struct drm_i915_private *dev_priv = adapter->algo_data; struct drm_i915_private *dev_priv = adapter->algo_data;
int i, reg_offset; int i, reg_offset, ret;
if (bus->force_bit) mutex_lock(&dev_priv->gmbus_mutex);
return intel_i2c_quirk_xfer(dev_priv,
if (bus->force_bit) {
ret = intel_i2c_quirk_xfer(dev_priv,
bus->force_bit, msgs, num); bus->force_bit, msgs, num);
goto out;
}
reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0;
...@@ -321,7 +325,8 @@ gmbus_xfer(struct i2c_adapter *adapter, ...@@ -321,7 +325,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
* start of the next xfer, till then let it sleep. * start of the next xfer, till then let it sleep.
*/ */
I915_WRITE(GMBUS0 + reg_offset, 0); I915_WRITE(GMBUS0 + reg_offset, 0);
return i; ret = i;
goto out;
timeout: timeout:
DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
...@@ -331,9 +336,12 @@ gmbus_xfer(struct i2c_adapter *adapter, ...@@ -331,9 +336,12 @@ gmbus_xfer(struct i2c_adapter *adapter,
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff);
if (!bus->force_bit) if (!bus->force_bit)
return -ENOMEM; ret = -ENOMEM;
else
return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num); 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) static u32 gmbus_func(struct i2c_adapter *adapter)
...@@ -380,6 +388,8 @@ int intel_setup_gmbus(struct drm_device *dev) ...@@ -380,6 +388,8 @@ int intel_setup_gmbus(struct drm_device *dev)
if (dev_priv->gmbus == NULL) if (dev_priv->gmbus == NULL)
return -ENOMEM; return -ENOMEM;
mutex_init(&dev_priv->gmbus_mutex);
for (i = 0; i < GMBUS_NUM_PORTS; i++) { for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i]; struct intel_gmbus *bus = &dev_priv->gmbus[i];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment