From fca0ec4edc16095e7f4cce240b563d9b9be9cd04 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 -ENODEV error when adding a i2c device, provided by the test_bus() routine. 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 hardware, such as in https://bugs.freedesktop.org/show_bug.cgi?id=41059 for example. This version of the patch relies on the testing facility of the i2c_algo_bit module. In order to use it, you need to add the following line into /etc/modprobe.conf or into /etc/modprobe.d/i2c_algo_bit.conf file (creating it when necessary): options i2c_algo_bit bit_test=1 Signed-off-by: Eugeni Dodonov --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/intel_i2c.c | 28 +++++++++++++++++++++++++++- 2 files changed, 28 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..6bcb48e 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -160,10 +160,17 @@ 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) { + printk(KERN_INFO "i915: pin %d is disabled, skipping\n", pin); + return NULL; + } + gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL); if (gpio == NULL) return NULL; @@ -186,8 +193,18 @@ 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)) + if ((err = i2c_bit_add_bus(&gpio->adapter)) < 0) { + /* i2c communication failed, if it failed with -ENODEV, we have + * failed the bit testing so let's mark bus as invalid and skip + * it in the future */ + if (err == -ENODEV) { + printk(KERN_WARNING "i915: i2c check for %s failed, disabling pin\n", + gpio->adapter.name); + dev_priv->gmbus[pin].disabled = 1; + } goto out_free; + } + gpio->dev_priv->gmbus[pin].disabled = 0; return &gpio->adapter; @@ -233,6 +250,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; + + /* Was pin already disabled? */ + if (pin < GMBUS_NUM_PORTS && dev_priv->gmbus[pin].disabled == 1) { + printk(KERN_INFO "i915: gmbus_xfer: pin %d is disabled, skipping\n", pin); + return -ENODEV; + } if (bus->force_bit) return intel_i2c_quirk_xfer(dev_priv, @@ -328,6 +352,7 @@ timeout: I915_WRITE(GMBUS0 + reg_offset, 0); /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ + printk(KERN_INFO "gmbus_xfer: timeout: fallback to gpio_create\n"); bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); if (!bus->force_bit) return -ENOMEM; @@ -401,6 +426,7 @@ int intel_setup_gmbus(struct drm_device *dev) bus->reg0 = i | GMBUS_RATE_100KHZ; /* XXX force bit banging until GMBUS is fully debugged */ + printk(KERN_INFO "setup_gmbus: trying intel_gpio_create\n"); bus->force_bit = intel_gpio_create(dev_priv, i); } -- 1.7.6.4