From 3bace2de5b4d7ffe9041538c805f291071706d27 Mon Sep 17 00:00:00 2001 From: Eugeni Dodonov Date: Thu, 22 Sep 2011 21:50:33 -0300 Subject: [PATCH] Mark non-existent i2c buses as disabled and skip them in the future. This adds a check for the stuck bits prior to adding a i2c device. When it happens, we mark the i2c device as disabled, and skip it in the future. This should avoid excessively long delays when communicating with the non-phisically present hardware, such as in https://bugs.freedesktop.org/show_bug.cgi?id=41059 for example. In case the communication with i2c fails by a different reason, we also log the failure error code for further investigation. Signed-off-by: Eugeni Dodonov --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_i2c.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 7916bd9..760ceff 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -284,6 +284,7 @@ typedef struct drm_i915_private { struct intel_gmbus { struct i2c_adapter adapter; struct i2c_adapter *force_bit; + int disabled; u32 reg0; } *gmbus; diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d98cee6..8741192 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -160,10 +160,15 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) GPIOF, }; struct intel_gpio *gpio; + int err; if (pin >= ARRAY_SIZE(map_pin_to_reg) || !map_pin_to_reg[pin]) return NULL; + /* Was pin already disabled? */ + if (dev_priv->gmbus[pin].disabled == 1) + return NULL; + gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL); if (gpio == NULL) return NULL; @@ -186,8 +191,23 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) gpio->algo.timeout = usecs_to_jiffies(2200); gpio->algo.data = gpio; - if (i2c_bit_add_bus(&gpio->adapter)) + /* Let's verify if we are talking to a real device. This can be indicated + * by stuck bits. A similar testing is performed in i2c_algo_bit module + * when using bit_test=1, but we do it locally to avoid extra overhead. + */ + set_data(gpio, 0); + udelay((gpio->algo.udelay + 1) / 2); + + if (get_data(gpio)) { + printk(KERN_WARNING "i915: i2c SDA stuck high for %s, disabling pin\n", + gpio->adapter.name); + dev_priv->gmbus[pin].disabled = 1; goto out_free; + } + if ((err = i2c_bit_add_bus(&gpio->adapter)) < 0) { + printk(KERN_WARNING "i915: i2c_bit_add_bus failed with %d\n", err); + goto out_free; + } return &gpio->adapter; @@ -233,6 +253,13 @@ gmbus_xfer(struct i2c_adapter *adapter, adapter); struct drm_i915_private *dev_priv = adapter->algo_data; int i, reg_offset; + int pin = bus->reg0 & 0xff; + + /* If pin is marked as disabled, we have already found out that a device is + * not there. So let's return this status to the caller. + */ + if (pin < GMBUS_NUM_PORTS && dev_priv->gmbus[pin].disabled == 1) + return -ENODEV; if (bus->force_bit) return intel_i2c_quirk_xfer(dev_priv, -- 1.7.6.4