1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
4 */
5 #include "ccu.h"
6 #include "ccu_sdm.h"
7 #include <hal_log.h>
8 #include <hal_atomic.h>
9
ccu_sdm_helper_is_enabled(struct ccu_common * common,struct ccu_sdm_internal * sdm)10 bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
11 struct ccu_sdm_internal *sdm)
12 {
13 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
14 {
15 return false;
16 }
17
18 if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
19 {
20 return false;
21 }
22
23 return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
24 }
25
ccu_sdm_helper_enable(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate)26 void ccu_sdm_helper_enable(struct ccu_common *common,
27 struct ccu_sdm_internal *sdm,
28 unsigned long rate)
29 {
30 unsigned int i;
31 u32 reg;
32 u32 __cspr;
33
34 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
35 {
36 return;
37 }
38
39 /* Set the pattern */
40 for (i = 0; i < sdm->table_size; i++)
41 if (sdm->table[i].rate == rate)
42 writel(sdm->table[i].pattern,
43 common->base + sdm->tuning_reg);
44
45 /* Make sure SDM is enabled */
46 __cspr = hal_spin_lock_irqsave(&common->lock);
47 reg = readl(common->base + sdm->tuning_reg);
48 writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
49 hal_spin_unlock_irqrestore(&common->lock, __cspr);
50
51 __cspr = hal_spin_lock_irqsave(&common->lock);
52 reg = readl(common->base + common->reg);
53 writel(reg | sdm->enable, common->base + common->reg);
54 hal_spin_unlock_irqrestore(&common->lock, __cspr);
55 }
56
ccu_sdm_helper_disable(struct ccu_common * common,struct ccu_sdm_internal * sdm)57 void ccu_sdm_helper_disable(struct ccu_common *common,
58 struct ccu_sdm_internal *sdm)
59 {
60 u32 reg;
61 u32 __cspr;
62
63 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
64 {
65 return;
66 }
67
68 __cspr = hal_spin_lock_irqsave(&common->lock);
69 reg = readl(common->base + common->reg);
70 writel(reg & ~sdm->enable, common->base + common->reg);
71 hal_spin_unlock_irqrestore(&common->lock, __cspr);
72
73 __cspr = hal_spin_lock_irqsave(&common->lock);
74 reg = readl(common->base + sdm->tuning_reg);
75 writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
76 hal_spin_unlock_irqrestore(&common->lock, __cspr);
77 }
78
79 /*
80 * Sigma delta modulation provides a way to do fractional-N frequency
81 * synthesis, in essence allowing the PLL to output any frequency
82 * within its operational range. On earlier SoCs such as the A10/A20,
83 * some PLLs support this. On later SoCs, all PLLs support this.
84 *
85 * The datasheets do not explain what the "wave top" and "wave bottom"
86 * parameters mean or do, nor how to calculate the effective output
87 * frequency. The only examples (and real world usage) are for the audio
88 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
89 * peripherals. The author lacks the underlying domain knowledge to
90 * pursue this.
91 *
92 * The goal and function of the following code is to support the two
93 * clock rates used by the audio subsystem, allowing for proper audio
94 * playback and capture without any pitch or speed changes.
95 */
ccu_sdm_helper_has_rate(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate)96 bool ccu_sdm_helper_has_rate(struct ccu_common *common,
97 struct ccu_sdm_internal *sdm,
98 unsigned long rate)
99 {
100 unsigned int i;
101
102 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
103 {
104 return false;
105 }
106
107 for (i = 0; i < sdm->table_size; i++)
108 if (sdm->table[i].rate == rate)
109 {
110 return true;
111 }
112
113 return false;
114 }
115
ccu_sdm_helper_read_rate(struct ccu_common * common,struct ccu_sdm_internal * sdm,u32 m,u32 n)116 unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
117 struct ccu_sdm_internal *sdm,
118 u32 m, u32 n)
119 {
120 unsigned int i;
121 u32 reg;
122
123 hal_log_debug("%s: Read sigma-delta modulation setting\n",
124 clk_hw_get_name(&common->hw));
125
126 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
127 {
128 return 0;
129 }
130
131 hal_log_debug("%s: clock is sigma-delta modulated\n",
132 clk_hw_get_name(&common->hw));
133
134 reg = readl(common->base + sdm->tuning_reg);
135
136 hal_log_debug("%s: pattern reg is 0x%x",
137 clk_hw_get_name(&common->hw), reg);
138
139 for (i = 0; i < sdm->table_size; i++)
140 if (sdm->table[i].pattern == reg &&
141 sdm->table[i].m == m && sdm->table[i].n == n)
142 {
143 return sdm->table[i].rate;
144 }
145
146 /* We can't calculate the effective clock rate, so just fail. */
147 return 0;
148 }
149
ccu_sdm_helper_get_factors(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate,unsigned long * m,unsigned long * n)150 int ccu_sdm_helper_get_factors(struct ccu_common *common,
151 struct ccu_sdm_internal *sdm,
152 unsigned long rate,
153 unsigned long *m, unsigned long *n)
154 {
155 unsigned int i;
156
157 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
158 {
159 return -1;
160 }
161
162 for (i = 0; i < sdm->table_size; i++)
163 if (sdm->table[i].rate == rate)
164 {
165 *m = sdm->table[i].m;
166 *n = sdm->table[i].n;
167 return 0;
168 }
169
170 /* nothing found */
171 return -1;
172 }
173