Commit 487875a832c5b16bd2b40e1ac65c201d4ee28da7
1 parent
47096e5310
Exists in
smarc_8mm-imx_v2018.03_4.14.98_2.0.0_ga
and in
4 other branches
MLK-21165 imx8: Workaround LPCG HW issue
There are two LPCG HW issues reported in TKT322331. Add workaround for them in u-boot. 1. Back to back LPCG write access must have 4x DSC cycle interval. 2. When DSC clock is gated, LPCG write access may be missed due to the edge detect is not see by DSC. Two writes shall be performed to re-enable the clock if DSC clock is gated Signed-off-by: Ye Li <ye.li@nxp.com> Reviewed-by: Peng Fan <peng.fan@nxp.com> (cherry picked from commit 96186ca0048e6ae261176e5f3ebf02be09bacb08)
Showing 1 changed file with 19 additions and 7 deletions Inline Diff
arch/arm/mach-imx/imx8/lpcg.c
1 | /* | 1 | /* |
2 | * Copyright 2017-2018 NXP | 2 | * Copyright 2017-2019 NXP |
3 | * | 3 | * |
4 | * SPDX-License-Identifier: GPL-2.0+ | 4 | * SPDX-License-Identifier: GPL-2.0+ |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #include <common.h> | 7 | #include <common.h> |
8 | #include <asm/io.h> | 8 | #include <asm/io.h> |
9 | #include <linux/errno.h> | 9 | #include <linux/errno.h> |
10 | #include <asm/arch/lpcg.h> | 10 | #include <asm/arch/lpcg.h> |
11 | 11 | ||
12 | #define LPCG_CLOCK_MASK 0x3U | 12 | #define LPCG_CLOCK_MASK 0x3U |
13 | #define LPCG_CLOCK_OFF 0x0U | 13 | #define LPCG_CLOCK_OFF 0x0U |
14 | #define LPCG_CLOCK_ON 0x2U | 14 | #define LPCG_CLOCK_ON 0x2U |
15 | #define LPCG_CLOCK_AUTO 0x3U | 15 | #define LPCG_CLOCK_AUTO 0x3U |
16 | #define LPCG_CLOCK_STOP 0x8U | 16 | #define LPCG_CLOCK_STOP 0x8U |
17 | 17 | ||
18 | #define LPCG_ALL_CLOCK_OFF 0x00000000U | 18 | #define LPCG_ALL_CLOCK_OFF 0x00000000U |
19 | #define LPCG_ALL_CLOCK_ON 0x22222222U | 19 | #define LPCG_ALL_CLOCK_ON 0x22222222U |
20 | #define LPCG_ALL_CLOCK_AUTO 0x33333333U | 20 | #define LPCG_ALL_CLOCK_AUTO 0x33333333U |
21 | #define LPCG_ALL_CLOCK_STOP 0x88888888U | 21 | #define LPCG_ALL_CLOCK_STOP 0x88888888U |
22 | 22 | ||
23 | static inline void lpcg_write(u32 lpcgVal, ulong lpcg_addr) | ||
24 | { | ||
25 | /* | ||
26 | * Write twice with 4x DSC clock cycles (40x IPS clock cycles) interval | ||
27 | * to work around LPCG issue | ||
28 | */ | ||
29 | writel(lpcgVal, lpcg_addr); | ||
30 | udelay(10); /* 10us is enough. Worst case is 40x IPS cycle (200Mhz) */ | ||
31 | writel(lpcgVal, lpcg_addr); | ||
32 | udelay(10); | ||
33 | } | ||
34 | |||
23 | void LPCG_ClockOff(u32 lpcg_addr, u8 clk) | 35 | void LPCG_ClockOff(u32 lpcg_addr, u8 clk) |
24 | { | 36 | { |
25 | u32 lpcgVal; | 37 | u32 lpcgVal; |
26 | 38 | ||
27 | /* Read from LPCG */ | 39 | /* Read from LPCG */ |
28 | lpcgVal = readl((ulong)lpcg_addr); | 40 | lpcgVal = readl((ulong)lpcg_addr); |
29 | 41 | ||
30 | /* Modify */ | 42 | /* Modify */ |
31 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); | 43 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); |
32 | lpcgVal |= ((u32)(LPCG_CLOCK_OFF) << (clk * 4U)); | 44 | lpcgVal |= ((u32)(LPCG_CLOCK_OFF) << (clk * 4U)); |
33 | 45 | ||
34 | /* Write to LPCG */ | 46 | /* Write to LPCG */ |
35 | writel(lpcgVal, (ulong)lpcg_addr); | 47 | lpcg_write(lpcgVal, (ulong)lpcg_addr); |
36 | } | 48 | } |
37 | 49 | ||
38 | void LPCG_ClockOn(u32 lpcg_addr, u8 clk) | 50 | void LPCG_ClockOn(u32 lpcg_addr, u8 clk) |
39 | { | 51 | { |
40 | u32 lpcgVal; | 52 | u32 lpcgVal; |
41 | 53 | ||
42 | /* Read from LPCG */ | 54 | /* Read from LPCG */ |
43 | lpcgVal = readl((ulong)lpcg_addr); | 55 | lpcgVal = readl((ulong)lpcg_addr); |
44 | 56 | ||
45 | /* Modify */ | 57 | /* Modify */ |
46 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); | 58 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); |
47 | lpcgVal |= ((u32)(LPCG_CLOCK_ON) << (clk * 4U)); | 59 | lpcgVal |= ((u32)(LPCG_CLOCK_ON) << (clk * 4U)); |
48 | 60 | ||
49 | /* Write to LPCG */ | 61 | /* Write to LPCG */ |
50 | writel(lpcgVal, (ulong)lpcg_addr); | 62 | lpcg_write(lpcgVal, (ulong)lpcg_addr); |
51 | } | 63 | } |
52 | 64 | ||
53 | void LPCG_ClockAutoGate(u32 lpcg_addr, u8 clk) | 65 | void LPCG_ClockAutoGate(u32 lpcg_addr, u8 clk) |
54 | { | 66 | { |
55 | u32 lpcgVal; | 67 | u32 lpcgVal; |
56 | 68 | ||
57 | /* Read from LPCG */ | 69 | /* Read from LPCG */ |
58 | lpcgVal = readl((ulong)lpcg_addr); | 70 | lpcgVal = readl((ulong)lpcg_addr); |
59 | 71 | ||
60 | /* Modify */ | 72 | /* Modify */ |
61 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); | 73 | lpcgVal &= ~((u32)(LPCG_CLOCK_MASK) << (clk * 4U)); |
62 | lpcgVal |= ((u32)(LPCG_CLOCK_AUTO) << (clk * 4U)); | 74 | lpcgVal |= ((u32)(LPCG_CLOCK_AUTO) << (clk * 4U)); |
63 | 75 | ||
64 | /* Write to LPCG */ | 76 | /* Write to LPCG */ |
65 | writel(lpcgVal, (ulong)lpcg_addr); | 77 | lpcg_write(lpcgVal, (ulong)lpcg_addr); |
66 | } | 78 | } |
67 | 79 | ||
68 | void LPCG_AllClockOff(u32 lpcg_addr) | 80 | void LPCG_AllClockOff(u32 lpcg_addr) |
69 | { | 81 | { |
70 | /* Write to LPCG */ | 82 | /* Write to LPCG */ |
71 | writel(LPCG_ALL_CLOCK_OFF, (ulong)lpcg_addr); | 83 | lpcg_write(LPCG_ALL_CLOCK_OFF, (ulong)lpcg_addr); |
72 | } | 84 | } |
73 | 85 | ||
74 | void LPCG_AllClockOn(u32 lpcg_addr) | 86 | void LPCG_AllClockOn(u32 lpcg_addr) |
75 | { | 87 | { |
76 | /* Write to LPCG */ | 88 | /* Write to LPCG */ |
77 | writel(LPCG_ALL_CLOCK_ON, (ulong)lpcg_addr); | 89 | lpcg_write(LPCG_ALL_CLOCK_ON, (ulong)lpcg_addr); |
78 | 90 | ||
79 | /* Wait for clocks to start */ | 91 | /* Wait for clocks to start */ |
80 | while ((readl((ulong)lpcg_addr) & LPCG_ALL_CLOCK_STOP) != 0U) | 92 | while ((readl((ulong)lpcg_addr) & LPCG_ALL_CLOCK_STOP) != 0U) |
81 | { | 93 | { |
82 | } | 94 | } |
83 | } | 95 | } |
84 | 96 | ||
85 | void LPCG_AllClockAutoGate(u32 lpcg_addr) | 97 | void LPCG_AllClockAutoGate(u32 lpcg_addr) |
86 | { | 98 | { |
87 | /* Write to LPCG */ | 99 | /* Write to LPCG */ |
88 | writel(LPCG_ALL_CLOCK_AUTO, (ulong)lpcg_addr); | 100 | lpcg_write(LPCG_ALL_CLOCK_AUTO, (ulong)lpcg_addr); |
89 | } | 101 | } |
90 | 102 |