@@ -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>
@@ -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;