diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c
index a76d85f..9eac1c1 100644
--- a/drivers/i2c/busses/i2c-davinci.c
+++ b/drivers/i2c/busses/i2c-davinci.c
@@ -111,7 +111,7 @@ struct davinci_i2c_dev {
     u8            terminate;
     struct i2c_adapter    adapter;
 #ifdef CONFIG_CPU_FREQ
-    struct completion    xfr_complete;
+    struct mutex        xfr_lock;
     struct notifier_block    freq_transition;
 #endif
 };
@@ -438,10 +438,14 @@ i2c_davinci_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)

     dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num);

+#ifdef CONFIG_CPU_FREQ
+    mutex_lock(&dev->xfr_lock);
+#endif
+
     ret = i2c_davinci_wait_bus_not_busy(dev, 1);
     if (ret < 0) {
         dev_warn(dev->dev, "timeout waiting for bus ready\n");
-        return ret;
+        goto exit;
     }

     for (i = 0; i < num; i++) {
@@ -449,14 +453,17 @@ i2c_davinci_xfer(struct i2c_adapter *adap,
struct i2c_msg msgs[], int num)
         dev_dbg(dev->dev, "%s [%d/%d] ret: %d\n", __func__, i + 1, num,
             ret);
         if (ret < 0)
-            return ret;
+            goto exit;
     }
+    ret = num;
+
+exit:

 #ifdef CONFIG_CPU_FREQ
-    complete(&dev->xfr_complete);
+    mutex_unlock(&dev->xfr_lock);
 #endif

-    return num;
+    return ret;
 }

 static u32 i2c_davinci_func(struct i2c_adapter *adap)
@@ -596,11 +603,16 @@ static int i2c_davinci_cpufreq_transition(struct
notifier_block *nb,

     dev = container_of(nb, struct davinci_i2c_dev, freq_transition);
     if (val == CPUFREQ_PRECHANGE) {
-        wait_for_completion(&dev->xfr_complete);
+#ifdef CONFIG_CPU_FREQ
+        mutex_lock(&dev->xfr_lock);
+#endif
         davinci_i2c_reset_ctrl(dev, 0);
     } else if (val == CPUFREQ_POSTCHANGE) {
         i2c_davinci_calc_clk_dividers(dev);
         davinci_i2c_reset_ctrl(dev, 1);
+#ifdef CONFIG_CPU_FREQ
+        mutex_unlock(&dev->xfr_lock);
+#endif
     }

     return 0;
@@ -670,7 +682,7 @@ static int davinci_i2c_probe(struct platform_device *pdev)

     init_completion(&dev->cmd_complete);
 #ifdef CONFIG_CPU_FREQ
-    init_completion(&dev->xfr_complete);
+    mutex_init(&dev->xfr_lock);
 #endif
     dev->dev = get_device(&pdev->dev);
     dev->irq = irq->start;



I'm using the 3.2 branch and the above patch revealed another bug.


INFO: task sh:2144 blocked for more than 120 seconds.
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
sh              D c03262f0     0  2144   1383 0x00000000
[<c03262f0>] (__schedule+0x350/0x3b0) from [<c03270f4>]
(__mutex_lock_slowpath+0
x90/0x100)
[<c03270f4>] (__mutex_lock_slowpath+0x90/0x100) from [<c021dde4>]
(i2c_davinci_x
fer+0x30/0x30c)
[<c021dde4>] (i2c_davinci_xfer+0x30/0x30c) from [<c021c310>]
(i2c_transfer+0xbc/                                             0x110)
[<c021c310>] (i2c_transfer+0xbc/0x110) from [<c021c3e8>]
(i2c_master_send+0x38/0
x48)
[<c021c3e8>] (i2c_master_send+0x38/0x48) from [<c01a06f0>]
(regmap_i2c_write+0x1
0/0x2c)
[<c01a06f0>] (regmap_i2c_write+0x10/0x2c) from [<c019e584>]
(_regmap_raw_write+0
xa4/0x144)
[<c019e584>] (_regmap_raw_write+0xa4/0x144) from [<c019ed44>]
(regmap_write+0x24                                             /0x38)
[<c019ed44>] (regmap_write+0x24/0x38) from [<c01728e0>]
(tps65023_dcdc_set_volta
ge+0xc0/0xe8)
[<c01728e0>] (tps65023_dcdc_set_voltage+0xc0/0xe8) from [<c0170efc>]
(_regulator
_do_set_voltage+0x3c/0x1d0)
[<c0170efc>] (_regulator_do_set_voltage+0x3c/0x1d0) from [<c0171f74>]
(regulator
_set_voltage+0xb8/0xcc)
[<c0171f74>] (regulator_set_voltage+0xb8/0xcc) from [<c0014360>]
(davinci_target
+0xcc/0x14c)
[<c0014360>] (davinci_target+0xcc/0x14c) from [<c021e5b8>]
(__cpufreq_driver_tar
get+0x2c/0x3c)
[<c021e5b8>] (__cpufreq_driver_target+0x2c/0x3c) from [<c0220328>]
(cpufreq_set+                                             0x54/0x70)
[<c0220328>] (cpufreq_set+0x54/0x70) from [<c021e9f8>]
(store_scaling_setspeed+0
x58/0x6c)
[<c021e9f8>] (store_scaling_setspeed+0x58/0x6c) from [<c021f954>]
(store+0x58/0x                                             70)
[<c021f954>] (store+0x58/0x70) from [<c00cea58>] (sysfs_write_file+0x108/0x140)
[<c00cea58>] (sysfs_write_file+0x108/0x140) from [<c0081eb0>]
(vfs_write+0xb0/0x                                             138)
[<c0081eb0>] (vfs_write+0xb0/0x138) from [<c0082110>] (sys_write+0x3c/0x68)
[<c0082110>] (sys_write+0x3c/0x68) from [<c00093a0>] (ret_fast_syscall+0x0/0x2c)

davinci_target was notifying CPUFREQ_PRECHANGE before calling
set_voltage which relied tried talking to the pmic over i2c causing a
hang.

@@ -101,17 +101,17 @@ static int davinci_target(struct cpufreq_policy *policy,

        cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);

        /* if moving to higher frequency, up the voltage beforehand */
        if (pdata->set_voltage && freqs.new > freqs.old) {
                ret = pdata->set_voltage(idx);
                if (ret)
                        goto out;
        }

I fixed this by moving the cpufreq_notifiy.  This doesn't apply to the
later kernels as the davinci cpufreq driver has changed quite a bit.
Unfortunately I don't have the setup to test a patch against a newer
kernel right now.

Author: Cormier, Jonathan <jcormier@criticallink.com>
Date:   Tue Jul 29 11:22:50 2014 -0400

    ARM: DAVINCI: Reorder cpufreq_nofity_transistion so that
set_voltage happens first.

    set_voltage may make use of the i2c bus to communicate with the
PMIC.  In this case we dont want the i2c to reset until after we set
the voltage.

    Signed-off-by: Cormier, Jonathan <jcormier@criticallink.com>

diff --git a/arch/arm/mach-davinci/cpufreq.c b/arch/arm/mach-davinci/cpufreq.c
index 5bba707..cbaee6c 100644
--- a/arch/arm/mach-davinci/cpufreq.c
+++ b/arch/arm/mach-davinci/cpufreq.c
@@ -102,8 +102,6 @@ static int davinci_target(struct cpufreq_policy *policy,
     if (ret)
         return -EINVAL;

-    cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
-
     /* if moving to higher frequency, up the voltage beforehand */
     if (pdata->set_voltage && freqs.new > freqs.old) {
         ret = pdata->set_voltage(idx);
@@ -111,6 +109,8 @@ static int davinci_target(struct cpufreq_policy *policy,
             goto out;
     }

+    cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
     ret = clk_set_rate(armclk, idx);
     if (ret)
         goto out;
