rk808: fix deadlock when shutown or reboot
author黄涛 <huangtao@rock-chips.com>
Fri, 6 Jun 2014 10:50:11 +0000 (18:50 +0800)
committer黄涛 <huangtao@rock-chips.com>
Fri, 6 Jun 2014 10:52:14 +0000 (18:52 +0800)
When shutdown, rk808 driver shutdown first, so io_lock mutex is locked.
At same time, gpu change freq by runtime pm, will locked on rk808_reg_read.
Finally, gpu device shutdown, call pm_runtime_barrier. Deadlock happens.

Fix this bug by make rk808 shutdown later.

  task                PC stack   pid father
init            D c06e9a14     0     1      0 0x00000000
[<c06e9a14>] (__schedule+0x48c/0x554) from [<c02e281c>] (__pm_runtime_barrier+0x114/0x138)
[<c02e281c>] (__pm_runtime_barrier+0x114/0x138) from [<c02e3cf4>] (pm_runtime_barrier+0x68/0xb8)
[<c02e3cf4>] (pm_runtime_barrier+0x68/0xb8) from [<c02d85ac>] (device_shutdown+0xa0/0x190)
[<c02d85ac>] (device_shutdown+0xa0/0x190) from [<c0042128>] (kernel_restart+0xc/0x50)
[<c0042128>] (kernel_restart+0xc/0x50) from [<c00422c4>] (SyS_reboot+0x150/0x1b0)
[<c00422c4>] (SyS_reboot+0x150/0x1b0) from [<c000da00>] (ret_fast_syscall+0x0/0x30)
kworker/2:1     D c06e9a14     0    32      2 0x00000000
Workqueue: pm pm_runtime_work
[<c06e9a14>] (__schedule+0x48c/0x554) from [<c06e9ec0>] (schedule_preempt_disabled+0x24/0x34)
[<c06e9ec0>] (schedule_preempt_disabled+0x24/0x34) from [<c06e8bf8>] (__mutex_lock_slowpath+0x1c0/0x22c)
[<c06e8bf8>] (__mutex_lock_slowpath+0x1c0/0x22c) from [<c06e8c94>] (mutex_lock+0x30/0x48)
[<c06e8c94>] (mutex_lock+0x30/0x48) from [<c02f0dbc>] (rk808_reg_read+0x24/0x5c)
[<c02f0dbc>] (rk808_reg_read+0x24/0x5c) from [<c02f0fa8>] (rk808_dcdc_is_enabled+0x28/0x38)
[<c02f0fa8>] (rk808_dcdc_is_enabled+0x28/0x38) from [<c02a6acc>] (_regulator_is_enabled+0x34/0x40)
[<c02a6acc>] (_regulator_is_enabled+0x34/0x40) from [<c02a6c50>] (_regulator_do_set_voltage+0xbc/0x3e4)
[<c02a6c50>] (_regulator_do_set_voltage+0xbc/0x3e4) from [<c02a7c1c>] (regulator_set_voltage+0xc0/0xe0)
[<c02a7c1c>] (regulator_set_voltage+0xc0/0xe0) from [<c0027b7c>] (dvfs_regulator_set_voltage_readback.isra.6+0x14/0x8c)
[<c0027b7c>] (dvfs_regulator_set_voltage_readback.isra.6+0x14/0x8c) from [<c0027d5c>] (dvfs_scale_volt_direct+0xa0/0xc4)
[<c0027d5c>] (dvfs_scale_volt_direct+0xa0/0xc4) from [<c0027f24>] (dvfs_target+0x1a4/0x1d4)
[<c0027f24>] (dvfs_target+0x1a4/0x1d4) from [<c00273e8>] (dvfs_clk_set_rate+0x44/0x80)
[<c00273e8>] (dvfs_clk_set_rate+0x44/0x80) from [<c02c1388>] (mali_dvfs_clk_set+0x28/0x48)
[<c02c1388>] (mali_dvfs_clk_set+0x28/0x48) from [<c02c01f4>] (kbase_platform_dvfs_set_level+0x9c/0xe0)
[<c02c01f4>] (kbase_platform_dvfs_set_level+0x9c/0xe0) from [<c02c032c>] (kbase_platform_dvfs_enable+0xf4/0x110)
[<c02c032c>] (kbase_platform_dvfs_enable+0xf4/0x110) from [<c02bf908>] (pm_callback_runtime_off+0x3c/0x58)
[<c02bf908>] (pm_callback_runtime_off+0x3c/0x58) from [<c02d2650>] (kbase_device_runtime_suspend+0x24/0x34)
[<c02d2650>] (kbase_device_runtime_suspend+0x24/0x34) from [<c02de738>] (pm_generic_runtime_suspend+0x2c/0x40)
[<c02de738>] (pm_generic_runtime_suspend+0x2c/0x40) from [<c02e25c4>] (__rpm_callback+0x38/0x68)
[<c02e25c4>] (__rpm_callback+0x38/0x68) from [<c02e2658>] (rpm_callback+0x64/0x7c)
[<c02e2658>] (rpm_callback+0x64/0x7c) from [<c02e3210>] (rpm_suspend+0x2d8/0x534)
[<c02e3210>] (rpm_suspend+0x2d8/0x534) from [<c02e3e60>] (pm_runtime_work+0x7c/0x94)
[<c02e3e60>] (pm_runtime_work+0x7c/0x94) from [<c004773c>] (process_one_work+0x29c/0x458)
[<c004773c>] (process_one_work+0x29c/0x458) from [<c0047a8c>] (worker_thread+0x194/0x2d4)
[<c0047a8c>] (worker_thread+0x194/0x2d4) from [<c004cf74>] (kthread+0xa0/0xac)
[<c004cf74>] (kthread+0xa0/0xac) from [<c000da98>] (ret_from_fork+0x14/0x3c)

drivers/mfd/rk808.c

index 9c964533a148e6e467162603fec73f75f1974480..b2852acb86c7fff530b209628909e846cc4c5d97 100755 (executable)
@@ -32,6 +32,7 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regmap.h>
+#include <linux/syscore_ops.h>
 
 #if 0
 #define DBG(x...)      printk(KERN_INFO x)
@@ -1138,7 +1139,7 @@ static struct rk808_board *rk808_parse_dt(struct i2c_client *i2c)
        return NULL;
 }
 #endif
-static int rk808_shutdown(struct i2c_client *i2c)
+static void rk808_shutdown(void)
 {
        int ret,i,val;
        u16 reg = 0;
@@ -1157,9 +1158,11 @@ static int rk808_shutdown(struct i2c_client *i2c)
        ret = rk808_clear_bits(rk808, RK808_RTC_INT_REG,(0x3<<2)); //close rtc int when power off
        mutex_lock(&rk808->io_lock);
        msleep(100);
-       return 0;       
 }
-EXPORT_SYMBOL_GPL(rk808_shutdown);
+
+static struct syscore_ops rk808_syscore_ops = {
+       .shutdown = rk808_shutdown,
+};
 
 static void rk808_device_shutdown(void)
 {
@@ -1503,6 +1506,8 @@ static int rk808_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *i
                }
        }
        #endif
+
+       register_syscore_ops(&rk808_syscore_ops);
        
        return 0;
 
@@ -1517,12 +1522,12 @@ static int rk808_i2c_remove(struct i2c_client *i2c)
        struct rk808 *rk808 = i2c_get_clientdata(i2c);
        int i;
 
+       unregister_syscore_ops(&rk808_syscore_ops);
        for (i = 0; i < rk808->num_regulators; i++)
                if (rk808->rdev[i])
                        regulator_unregister(rk808->rdev[i]);
        kfree(rk808->rdev);
        i2c_set_clientdata(i2c, NULL);
-       kfree(rk808);
 
        return 0;
 }
@@ -1551,7 +1556,6 @@ static struct i2c_driver rk808_i2c_driver = {
        .probe    = rk808_i2c_probe,
        .remove   = rk808_i2c_remove,
        .id_table = rk808_i2c_id,
-       .shutdown = rk808_shutdown,
 };
 
 static int __init rk808_module_init(void)