diff options
author | Yufeng Shen <miletus@chromium.org> | 2012-02-13 17:36:54 -0500 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2012-02-14 04:39:53 -0500 |
commit | 8a8ed1f5143b3df312e436ab15290e4a7ca6a559 (patch) | |
tree | 90067c14702ee364b47c551d1467508329521f28 /drivers/gpu | |
parent | b1d7e4b41fd0f72ea8149056778db5d737739305 (diff) |
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: 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>
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_i2c.c | 24 |
2 files changed, 21 insertions, 7 deletions
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index caec1fdb7dda..8e3eb5e282a1 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -313,6 +313,10 @@ typedef struct drm_i915_private { | |||
313 | u32 reg0; | 313 | u32 reg0; |
314 | } *gmbus; | 314 | } *gmbus; |
315 | 315 | ||
316 | /** gmbus_mutex protects against concurrent usage of the single hw gmbus | ||
317 | * controller on different i2c buses. */ | ||
318 | struct mutex gmbus_mutex; | ||
319 | |||
316 | struct pci_dev *bridge_dev; | 320 | struct pci_dev *bridge_dev; |
317 | struct intel_ring_buffer ring[I915_NUM_RINGS]; | 321 | struct intel_ring_buffer ring[I915_NUM_RINGS]; |
318 | uint32_t next_seqno; | 322 | uint32_t next_seqno; |
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d30ccccb9d73..fc75d71de53b 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c | |||
@@ -233,11 +233,15 @@ gmbus_xfer(struct i2c_adapter *adapter, | |||
233 | struct intel_gmbus, | 233 | struct intel_gmbus, |
234 | adapter); | 234 | adapter); |
235 | struct drm_i915_private *dev_priv = adapter->algo_data; | 235 | struct drm_i915_private *dev_priv = adapter->algo_data; |
236 | int i, reg_offset; | 236 | int i, reg_offset, ret; |
237 | 237 | ||
238 | if (bus->force_bit) | 238 | mutex_lock(&dev_priv->gmbus_mutex); |
239 | return intel_i2c_quirk_xfer(dev_priv, | 239 | |
240 | if (bus->force_bit) { | ||
241 | ret = intel_i2c_quirk_xfer(dev_priv, | ||
240 | bus->force_bit, msgs, num); | 242 | bus->force_bit, msgs, num); |
243 | goto out; | ||
244 | } | ||
241 | 245 | ||
242 | reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; | 246 | reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; |
243 | 247 | ||
@@ -321,7 +325,8 @@ done: | |||
321 | * start of the next xfer, till then let it sleep. | 325 | * start of the next xfer, till then let it sleep. |
322 | */ | 326 | */ |
323 | I915_WRITE(GMBUS0 + reg_offset, 0); | 327 | I915_WRITE(GMBUS0 + reg_offset, 0); |
324 | return i; | 328 | ret = i; |
329 | goto out; | ||
325 | 330 | ||
326 | timeout: | 331 | timeout: |
327 | DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", | 332 | DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", |
@@ -331,9 +336,12 @@ timeout: | |||
331 | /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ | 336 | /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ |
332 | bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); | 337 | bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); |
333 | if (!bus->force_bit) | 338 | if (!bus->force_bit) |
334 | return -ENOMEM; | 339 | ret = -ENOMEM; |
335 | 340 | else | |
336 | return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num); | 341 | ret = intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num); |
342 | out: | ||
343 | mutex_unlock(&dev_priv->gmbus_mutex); | ||
344 | return ret; | ||
337 | } | 345 | } |
338 | 346 | ||
339 | static u32 gmbus_func(struct i2c_adapter *adapter) | 347 | static u32 gmbus_func(struct i2c_adapter *adapter) |
@@ -380,6 +388,8 @@ int intel_setup_gmbus(struct drm_device *dev) | |||
380 | if (dev_priv->gmbus == NULL) | 388 | if (dev_priv->gmbus == NULL) |
381 | return -ENOMEM; | 389 | return -ENOMEM; |
382 | 390 | ||
391 | mutex_init(&dev_priv->gmbus_mutex); | ||
392 | |||
383 | for (i = 0; i < GMBUS_NUM_PORTS; i++) { | 393 | for (i = 0; i < GMBUS_NUM_PORTS; i++) { |
384 | struct intel_gmbus *bus = &dev_priv->gmbus[i]; | 394 | struct intel_gmbus *bus = &dev_priv->gmbus[i]; |
385 | 395 | ||