diff options
author | Fabio Estevam <fabio.estevam@nxp.com> | 2016-10-22 08:20:56 -0400 |
---|---|---|
committer | Shawn Guo <shawnguo@kernel.org> | 2016-10-24 03:13:02 -0400 |
commit | f9d1f7a7ad919c93dfb708aae6e19d33c5437443 (patch) | |
tree | 75c9960e6763dd6173c20cf15198e5ee20b9dec4 /arch/arm/mach-imx | |
parent | eef0b282bb586259d35548851cf6a4ce847bb804 (diff) |
ARM: imx: gpc: Fix the imx_gpc_genpd_init() error path
If of_genpd_add_provider_onecell() fails the following kernel crash is
observed on a kernel built with multi_v7_defconfig:
[ 1.739301] [00000040] *pgd=00000000
[ 1.739310] Internal error: Oops: 5 [#1] SMP ARM
[ 1.739319] Modules linked in:
[ 1.739328] CPU: 1 PID: 95 Comm: kworker/1:4 Not tainted 4.8.0-11897-g6b5e09a #1
[ 1.739331] Hardware name: Freescale i.MX6 Quad/DualLite (Device Tree)
[ 1.739352] Workqueue: pm genpd_power_off_work_fn
[ 1.739356] task: ee63d400 task.stack: ee70a000
[ 1.739365] PC is at mutex_lock+0xc/0x4c
[ 1.739374] LR is at regulator_disable+0x2c/0x60
[ 1.739379] pc : [<c0bc0da0>] lr : [<c06e4b10>] psr: 60000013
[ 1.739379] sp : ee70beb0 ip : 10624dd3 fp : ee6e6280
[ 1.739382] r10: eefb0900 r9 : 00000000 r8 : c1309918
[ 1.739385] r7 : 00000000 r6 : 00000040 r5 : 00000000 r4 : 00000040
[ 1.739390] r3 : 0000004c r2 : 7fffd540 r1 : 000001e4 r0 : 00000040
Instead of returning of_genpd_add_provider_onecell() directly,
we should check its return value and in the case of error we
should unwind the previously taken actions, which in these case are:
- Call imx6q_pm_pu_power_off()
- Set imx6q_pu_domain.reg back to NULL
Setting imx6q_pu_domain.reg to NULL in the error case is important
as it will prevent further operations in the pu_reg regulator.
This kernel crash is not observed with imx_v6_v7_defconfig because
it selects GPU and VPU drivers, which are consumers of the GPC block
and thus change the refcount of the pu_reg regulator.
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Signed-off-by: Shawn Guo <shawnguo@kernel.org>
Diffstat (limited to 'arch/arm/mach-imx')
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index d0463e9d8a08..b54db47f6f32 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c | |||
@@ -408,7 +408,7 @@ static struct genpd_onecell_data imx_gpc_onecell_data = { | |||
408 | static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg) | 408 | static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg) |
409 | { | 409 | { |
410 | struct clk *clk; | 410 | struct clk *clk; |
411 | int i; | 411 | int i, ret; |
412 | 412 | ||
413 | imx6q_pu_domain.reg = pu_reg; | 413 | imx6q_pu_domain.reg = pu_reg; |
414 | 414 | ||
@@ -432,12 +432,20 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg) | |||
432 | 432 | ||
433 | for (i = 0; i < ARRAY_SIZE(imx_gpc_domains); i++) | 433 | for (i = 0; i < ARRAY_SIZE(imx_gpc_domains); i++) |
434 | pm_genpd_init(imx_gpc_domains[i], NULL, false); | 434 | pm_genpd_init(imx_gpc_domains[i], NULL, false); |
435 | return of_genpd_add_provider_onecell(dev->of_node, | 435 | |
436 | ret = of_genpd_add_provider_onecell(dev->of_node, | ||
436 | &imx_gpc_onecell_data); | 437 | &imx_gpc_onecell_data); |
438 | if (ret) | ||
439 | goto power_off; | ||
440 | |||
441 | return 0; | ||
437 | 442 | ||
443 | power_off: | ||
444 | imx6q_pm_pu_power_off(&imx6q_pu_domain.base); | ||
438 | clk_err: | 445 | clk_err: |
439 | while (i--) | 446 | while (i--) |
440 | clk_put(imx6q_pu_domain.clk[i]); | 447 | clk_put(imx6q_pu_domain.clk[i]); |
448 | imx6q_pu_domain.reg = NULL; | ||
441 | return -EINVAL; | 449 | return -EINVAL; |
442 | } | 450 | } |
443 | 451 | ||