]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.14.111/clk-fractional-divider-check-parent-rate-only-if-fla.patch
5.1-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.14.111 / clk-fractional-divider-check-parent-rate-only-if-fla.patch
1 From 987810fdd237206e07c8df6a183a0f874a0cec63 Mon Sep 17 00:00:00 2001
2 From: Katsuhiro Suzuki <katsuhiro@katsuster.net>
3 Date: Mon, 11 Feb 2019 00:38:06 +0900
4 Subject: clk: fractional-divider: check parent rate only if flag is set
5
6 [ Upstream commit d13501a2bedfbea0983cc868d3f1dc692627f60d ]
7
8 Custom approximation of fractional-divider may not need parent clock
9 rate checking. For example Rockchip SoCs work fine using grand parent
10 clock rate even if target rate is greater than parent.
11
12 This patch checks parent clock rate only if CLK_SET_RATE_PARENT flag
13 is set.
14
15 For detailed example, clock tree of Rockchip I2S audio hardware.
16 - Clock rate of CPLL is 1.2GHz, GPLL is 491.52MHz.
17 - i2s1_div is integer divider can divide N (N is 1~128).
18 Input clock is CPLL or GPLL. Initial divider value is N = 1.
19 Ex) PLL = CPLL, N = 10, i2s1_div output rate is
20 CPLL / 10 = 1.2GHz / 10 = 120MHz
21 - i2s1_frac is fractional divider can divide input to x/y, x and
22 y are 16bit integer.
23
24 CPLL --> | selector | ---> i2s1_div -+--> | selector | --> I2S1 MCLK
25 GPLL --> | | ,--------------' | |
26 `--> i2s1_frac ---> | |
27
28 Clock mux system try to choose suitable one from i2s1_div and
29 i2s1_frac for master clock (MCLK) of I2S1.
30
31 Bad scenario as follows:
32 - Try to set MCLK to 8.192MHz (32kHz audio replay)
33 Candidate setting is
34 - i2s1_div: GPLL / 60 = 8.192MHz
35 i2s1_div candidate is exactly same as target clock rate, so mux
36 choose this clock source. i2s1_div output rate is changed
37 491.52MHz -> 8.192MHz
38
39 - After that try to set to 11.2896MHz (44.1kHz audio replay)
40 Candidate settings are
41 - i2s1_div : CPLL / 107 = 11.214945MHz
42 - i2s1_frac: i2s1_div = 8.192MHz
43 This is because clk_fd_round_rate() thinks target rate
44 (11.2896MHz) is higher than parent rate (i2s1_div = 8.192MHz)
45 and returns parent clock rate.
46
47 Above is current upstreamed behavior. Clock mux system choose
48 i2s1_div, but this clock rate is not acceptable for I2S driver, so
49 users cannot replay audio.
50
51 Expected behavior is:
52 - Try to set master clock to 11.2896MHz (44.1kHz audio replay)
53 Candidate settings are
54 - i2s1_div : CPLL / 107 = 11.214945MHz
55 - i2s1_frac: i2s1_div * 147/6400 = 11.2896MHz
56 Change i2s1_div to GPLL / 1 = 491.52MHz at same
57 time.
58
59 If apply this commit, clk_fd_round_rate() calls custom approximate
60 function of Rockchip even if target rate is higher than parent.
61 Custom function changes both grand parent (i2s1_div) and parent
62 (i2s_frac) settings at same time. Clock mux system can choose
63 i2s1_frac and audio works fine.
64
65 Signed-off-by: Katsuhiro Suzuki <katsuhiro@katsuster.net>
66 Reviewed-by: Heiko Stuebner <heiko@sntech.de>
67 [sboyd@kernel.org: Make function into a macro instead]
68 Signed-off-by: Stephen Boyd <sboyd@kernel.org>
69 Signed-off-by: Sasha Levin <sashal@kernel.org>
70 ---
71 drivers/clk/clk-fractional-divider.c | 2 +-
72 include/linux/clk-provider.h | 3 +++
73 2 files changed, 4 insertions(+), 1 deletion(-)
74
75 diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
76 index fdf625fb10fa..083daa293280 100644
77 --- a/drivers/clk/clk-fractional-divider.c
78 +++ b/drivers/clk/clk-fractional-divider.c
79 @@ -77,7 +77,7 @@ static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
80 unsigned long m, n;
81 u64 ret;
82
83 - if (!rate || rate >= *parent_rate)
84 + if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate))
85 return *parent_rate;
86
87 if (fd->approximation)
88 diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
89 index 2f4e79fe7b86..3eb3376f1cc8 100644
90 --- a/include/linux/clk-provider.h
91 +++ b/include/linux/clk-provider.h
92 @@ -743,6 +743,9 @@ unsigned int __clk_get_enable_count(struct clk *clk);
93 unsigned long clk_hw_get_rate(const struct clk_hw *hw);
94 unsigned long __clk_get_flags(struct clk *clk);
95 unsigned long clk_hw_get_flags(const struct clk_hw *hw);
96 +#define clk_hw_can_set_rate_parent(hw) \
97 + (clk_hw_get_flags((hw)) & CLK_SET_RATE_PARENT)
98 +
99 bool clk_hw_is_prepared(const struct clk_hw *hw);
100 bool clk_hw_is_enabled(const struct clk_hw *hw);
101 bool __clk_is_enabled(struct clk *clk);
102 --
103 2.19.1
104