Merge tag 'for-linus' of git://git.kernel.org/pub/scm/virt/kvm/kvm
[sfrench/cifs-2.6.git] / drivers / clk / meson / gxbb-aoclk-32k.c
1 /*
2  * Copyright (c) 2017 BayLibre, SAS.
3  * Author: Neil Armstrong <narmstrong@baylibre.com>
4  *
5  * SPDX-License-Identifier: GPL-2.0+
6  */
7
8 #include <linux/clk-provider.h>
9 #include <linux/bitfield.h>
10 #include <linux/regmap.h>
11 #include "gxbb-aoclk.h"
12
13 /*
14  * The AO Domain embeds a dual/divider to generate a more precise
15  * 32,768KHz clock for low-power suspend mode and CEC.
16  *                      ______   ______
17  *                     |      | |      |
18  *         ______      | Div1 |-| Cnt1 |       ______
19  *        |      |    /|______| |______|\     |      |
20  * Xtal-->| Gate |---|  ______   ______  X-X--| Gate |-->
21  *        |______| |  \|      | |      |/  |  |______|
22  *                 |   | Div2 |-| Cnt2 |   |
23  *                 |   |______| |______|   |
24  *                 |_______________________|
25  *
26  * The dividing can be switched to single or dual, with a counter
27  * for each divider to set when the switching is done.
28  * The entire dividing mechanism can be also bypassed.
29  */
30
31 #define CLK_CNTL0_N1_MASK       GENMASK(11, 0)
32 #define CLK_CNTL0_N2_MASK       GENMASK(23, 12)
33 #define CLK_CNTL0_DUALDIV_EN    BIT(28)
34 #define CLK_CNTL0_OUT_GATE_EN   BIT(30)
35 #define CLK_CNTL0_IN_GATE_EN    BIT(31)
36
37 #define CLK_CNTL1_M1_MASK       GENMASK(11, 0)
38 #define CLK_CNTL1_M2_MASK       GENMASK(23, 12)
39 #define CLK_CNTL1_BYPASS_EN     BIT(24)
40 #define CLK_CNTL1_SELECT_OSC    BIT(27)
41
42 #define PWR_CNTL_ALT_32K_SEL    GENMASK(13, 10)
43
44 struct cec_32k_freq_table {
45         unsigned long parent_rate;
46         unsigned long target_rate;
47         bool dualdiv;
48         unsigned int n1;
49         unsigned int n2;
50         unsigned int m1;
51         unsigned int m2;
52 };
53
54 static const struct cec_32k_freq_table aoclk_cec_32k_table[] = {
55         [0] = {
56                 .parent_rate = 24000000,
57                 .target_rate = 32768,
58                 .dualdiv = true,
59                 .n1 = 733,
60                 .n2 = 732,
61                 .m1 = 8,
62                 .m2 = 11,
63         },
64 };
65
66 /*
67  * If CLK_CNTL0_DUALDIV_EN == 0
68  *  - will use N1 divider only
69  * If CLK_CNTL0_DUALDIV_EN == 1
70  *  - hold M1 cycles of N1 divider then changes to N2
71  *  - hold M2 cycles of N2 divider then changes to N1
72  * Then we can get more accurate division.
73  */
74 static unsigned long aoclk_cec_32k_recalc_rate(struct clk_hw *hw,
75                                                unsigned long parent_rate)
76 {
77         struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
78         unsigned long n1;
79         u32 reg0, reg1;
80
81         regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, &reg0);
82         regmap_read(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, &reg1);
83
84         if (reg1 & CLK_CNTL1_BYPASS_EN)
85                 return parent_rate;
86
87         if (reg0 & CLK_CNTL0_DUALDIV_EN) {
88                 unsigned long n2, m1, m2, f1, f2, p1, p2;
89
90                 n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
91                 n2 = FIELD_GET(CLK_CNTL0_N2_MASK, reg0) + 1;
92
93                 m1 = FIELD_GET(CLK_CNTL1_M1_MASK, reg1) + 1;
94                 m2 = FIELD_GET(CLK_CNTL1_M2_MASK, reg1) + 1;
95
96                 f1 = DIV_ROUND_CLOSEST(parent_rate, n1);
97                 f2 = DIV_ROUND_CLOSEST(parent_rate, n2);
98
99                 p1 = DIV_ROUND_CLOSEST(100000000 * m1, f1 * (m1 + m2));
100                 p2 = DIV_ROUND_CLOSEST(100000000 * m2, f2 * (m1 + m2));
101
102                 return DIV_ROUND_UP(100000000, p1 + p2);
103         }
104
105         n1 = FIELD_GET(CLK_CNTL0_N1_MASK, reg0) + 1;
106
107         return DIV_ROUND_CLOSEST(parent_rate, n1);
108 }
109
110 static const struct cec_32k_freq_table *find_cec_32k_freq(unsigned long rate,
111                                                           unsigned long prate)
112 {
113         int i;
114
115         for (i = 0 ; i < ARRAY_SIZE(aoclk_cec_32k_table) ; ++i)
116                 if (aoclk_cec_32k_table[i].parent_rate == prate &&
117                     aoclk_cec_32k_table[i].target_rate == rate)
118                         return &aoclk_cec_32k_table[i];
119
120         return NULL;
121 }
122
123 static long aoclk_cec_32k_round_rate(struct clk_hw *hw, unsigned long rate,
124                                      unsigned long *prate)
125 {
126         const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
127                                                                   *prate);
128
129         /* If invalid return first one */
130         if (!freq)
131                 return aoclk_cec_32k_table[0].target_rate;
132
133         return freq->target_rate;
134 }
135
136 /*
137  * From the Amlogic init procedure, the IN and OUT gates needs to be handled
138  * in the init procedure to avoid any glitches.
139  */
140
141 static int aoclk_cec_32k_set_rate(struct clk_hw *hw, unsigned long rate,
142                                   unsigned long parent_rate)
143 {
144         const struct cec_32k_freq_table *freq = find_cec_32k_freq(rate,
145                                                                   parent_rate);
146         struct aoclk_cec_32k *cec_32k = to_aoclk_cec_32k(hw);
147         u32 reg = 0;
148
149         if (!freq)
150                 return -EINVAL;
151
152         /* Disable clock */
153         regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
154                            CLK_CNTL0_IN_GATE_EN | CLK_CNTL0_OUT_GATE_EN, 0);
155
156         reg = FIELD_PREP(CLK_CNTL0_N1_MASK, freq->n1 - 1);
157         if (freq->dualdiv)
158                 reg |= CLK_CNTL0_DUALDIV_EN |
159                        FIELD_PREP(CLK_CNTL0_N2_MASK, freq->n2 - 1);
160
161         regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0, reg);
162
163         reg = FIELD_PREP(CLK_CNTL1_M1_MASK, freq->m1 - 1);
164         if (freq->dualdiv)
165                 reg |= FIELD_PREP(CLK_CNTL1_M2_MASK, freq->m2 - 1);
166
167         regmap_write(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL1, reg);
168
169         /* Enable clock */
170         regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
171                            CLK_CNTL0_IN_GATE_EN, CLK_CNTL0_IN_GATE_EN);
172
173         udelay(200);
174
175         regmap_update_bits(cec_32k->regmap, AO_RTC_ALT_CLK_CNTL0,
176                            CLK_CNTL0_OUT_GATE_EN, CLK_CNTL0_OUT_GATE_EN);
177
178         regmap_update_bits(cec_32k->regmap, AO_CRT_CLK_CNTL1,
179                            CLK_CNTL1_SELECT_OSC, CLK_CNTL1_SELECT_OSC);
180
181         /* Select 32k from XTAL */
182         regmap_update_bits(cec_32k->regmap,
183                           AO_RTI_PWR_CNTL_REG0,
184                           PWR_CNTL_ALT_32K_SEL,
185                           FIELD_PREP(PWR_CNTL_ALT_32K_SEL, 4));
186
187         return 0;
188 }
189
190 const struct clk_ops meson_aoclk_cec_32k_ops = {
191         .recalc_rate = aoclk_cec_32k_recalc_rate,
192         .round_rate = aoclk_cec_32k_round_rate,
193         .set_rate = aoclk_cec_32k_set_rate,
194 };