blob: e510467ea24c2e7813dde03dfa2aacaa3d5b1096 [file] [log] [blame]
Chen-Yu Tsai05d2eaa2017-10-12 16:36:59 +08001/*
2 * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of
7 * the License, or (at your option) any later version.
8 */
9
10#include <linux/clk-provider.h>
Stephen Boyd62e59c42019-04-18 15:20:22 -070011#include <linux/io.h>
Chen-Yu Tsai05d2eaa2017-10-12 16:36:59 +080012#include <linux/spinlock.h>
13
14#include "ccu_sdm.h"
15
16bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
17 struct ccu_sdm_internal *sdm)
18{
19 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
20 return false;
21
22 if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
23 return false;
24
25 return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
26}
27
28void ccu_sdm_helper_enable(struct ccu_common *common,
29 struct ccu_sdm_internal *sdm,
30 unsigned long rate)
31{
32 unsigned long flags;
33 unsigned int i;
34 u32 reg;
35
36 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
37 return;
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 spin_lock_irqsave(common->lock, flags);
47 reg = readl(common->base + sdm->tuning_reg);
48 writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
49 spin_unlock_irqrestore(common->lock, flags);
50
51 spin_lock_irqsave(common->lock, flags);
52 reg = readl(common->base + common->reg);
53 writel(reg | sdm->enable, common->base + common->reg);
54 spin_unlock_irqrestore(common->lock, flags);
55}
56
57void ccu_sdm_helper_disable(struct ccu_common *common,
58 struct ccu_sdm_internal *sdm)
59{
60 unsigned long flags;
61 u32 reg;
62
63 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
64 return;
65
66 spin_lock_irqsave(common->lock, flags);
67 reg = readl(common->base + common->reg);
68 writel(reg & ~sdm->enable, common->base + common->reg);
69 spin_unlock_irqrestore(common->lock, flags);
70
71 spin_lock_irqsave(common->lock, flags);
72 reg = readl(common->base + sdm->tuning_reg);
73 writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
74 spin_unlock_irqrestore(common->lock, flags);
75}
76
77/*
78 * Sigma delta modulation provides a way to do fractional-N frequency
79 * synthesis, in essence allowing the PLL to output any frequency
80 * within its operational range. On earlier SoCs such as the A10/A20,
81 * some PLLs support this. On later SoCs, all PLLs support this.
82 *
83 * The datasheets do not explain what the "wave top" and "wave bottom"
84 * parameters mean or do, nor how to calculate the effective output
85 * frequency. The only examples (and real world usage) are for the audio
86 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
87 * peripherals. The author lacks the underlying domain knowledge to
88 * pursue this.
89 *
90 * The goal and function of the following code is to support the two
91 * clock rates used by the audio subsystem, allowing for proper audio
92 * playback and capture without any pitch or speed changes.
93 */
94bool ccu_sdm_helper_has_rate(struct ccu_common *common,
95 struct ccu_sdm_internal *sdm,
96 unsigned long rate)
97{
98 unsigned int i;
99
100 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
101 return false;
102
103 for (i = 0; i < sdm->table_size; i++)
104 if (sdm->table[i].rate == rate)
105 return true;
106
107 return false;
108}
109
110unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
111 struct ccu_sdm_internal *sdm,
112 u32 m, u32 n)
113{
114 unsigned int i;
115 u32 reg;
116
117 pr_debug("%s: Read sigma-delta modulation setting\n",
118 clk_hw_get_name(&common->hw));
119
120 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
121 return 0;
122
123 pr_debug("%s: clock is sigma-delta modulated\n",
124 clk_hw_get_name(&common->hw));
125
126 reg = readl(common->base + sdm->tuning_reg);
127
128 pr_debug("%s: pattern reg is 0x%x",
129 clk_hw_get_name(&common->hw), reg);
130
131 for (i = 0; i < sdm->table_size; i++)
132 if (sdm->table[i].pattern == reg &&
133 sdm->table[i].m == m && sdm->table[i].n == n)
134 return sdm->table[i].rate;
135
136 /* We can't calculate the effective clock rate, so just fail. */
137 return 0;
138}
139
140int ccu_sdm_helper_get_factors(struct ccu_common *common,
141 struct ccu_sdm_internal *sdm,
142 unsigned long rate,
143 unsigned long *m, unsigned long *n)
144{
145 unsigned int i;
146
147 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
148 return -EINVAL;
149
150 for (i = 0; i < sdm->table_size; i++)
151 if (sdm->table[i].rate == rate) {
152 *m = sdm->table[i].m;
153 *n = sdm->table[i].n;
154 return 0;
155 }
156
157 /* nothing found */
158 return -EINVAL;
159}