1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
6 #include <linux/clk-provider.h>
8 #include <linux/spinlock.h>
12 bool ccu_sdm_helper_is_enabled(struct ccu_common
*common
,
13 struct ccu_sdm_internal
*sdm
)
15 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
18 if (sdm
->enable
&& !(readl(common
->base
+ common
->reg
) & sdm
->enable
))
21 return !!(readl(common
->base
+ sdm
->tuning_reg
) & sdm
->tuning_enable
);
23 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled
, "SUNXI_CCU");
25 void ccu_sdm_helper_enable(struct ccu_common
*common
,
26 struct ccu_sdm_internal
*sdm
,
33 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
37 for (i
= 0; i
< sdm
->table_size
; i
++)
38 if (sdm
->table
[i
].rate
== rate
)
39 writel(sdm
->table
[i
].pattern
,
40 common
->base
+ sdm
->tuning_reg
);
42 /* Make sure SDM is enabled */
43 spin_lock_irqsave(common
->lock
, flags
);
44 reg
= readl(common
->base
+ sdm
->tuning_reg
);
45 writel(reg
| sdm
->tuning_enable
, common
->base
+ sdm
->tuning_reg
);
46 spin_unlock_irqrestore(common
->lock
, flags
);
48 spin_lock_irqsave(common
->lock
, flags
);
49 reg
= readl(common
->base
+ common
->reg
);
50 writel(reg
| sdm
->enable
, common
->base
+ common
->reg
);
51 spin_unlock_irqrestore(common
->lock
, flags
);
53 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable
, "SUNXI_CCU");
55 void ccu_sdm_helper_disable(struct ccu_common
*common
,
56 struct ccu_sdm_internal
*sdm
)
61 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
64 spin_lock_irqsave(common
->lock
, flags
);
65 reg
= readl(common
->base
+ common
->reg
);
66 writel(reg
& ~sdm
->enable
, common
->base
+ common
->reg
);
67 spin_unlock_irqrestore(common
->lock
, flags
);
69 spin_lock_irqsave(common
->lock
, flags
);
70 reg
= readl(common
->base
+ sdm
->tuning_reg
);
71 writel(reg
& ~sdm
->tuning_enable
, common
->base
+ sdm
->tuning_reg
);
72 spin_unlock_irqrestore(common
->lock
, flags
);
74 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable
, "SUNXI_CCU");
77 * Sigma delta modulation provides a way to do fractional-N frequency
78 * synthesis, in essence allowing the PLL to output any frequency
79 * within its operational range. On earlier SoCs such as the A10/A20,
80 * some PLLs support this. On later SoCs, all PLLs support this.
82 * The datasheets do not explain what the "wave top" and "wave bottom"
83 * parameters mean or do, nor how to calculate the effective output
84 * frequency. The only examples (and real world usage) are for the audio
85 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
86 * peripherals. The author lacks the underlying domain knowledge to
89 * The goal and function of the following code is to support the two
90 * clock rates used by the audio subsystem, allowing for proper audio
91 * playback and capture without any pitch or speed changes.
93 bool ccu_sdm_helper_has_rate(struct ccu_common
*common
,
94 struct ccu_sdm_internal
*sdm
,
99 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
102 for (i
= 0; i
< sdm
->table_size
; i
++)
103 if (sdm
->table
[i
].rate
== rate
)
108 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate
, "SUNXI_CCU");
110 unsigned long ccu_sdm_helper_read_rate(struct ccu_common
*common
,
111 struct ccu_sdm_internal
*sdm
,
117 pr_debug("%s: Read sigma-delta modulation setting\n",
118 clk_hw_get_name(&common
->hw
));
120 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
123 pr_debug("%s: clock is sigma-delta modulated\n",
124 clk_hw_get_name(&common
->hw
));
126 reg
= readl(common
->base
+ sdm
->tuning_reg
);
128 pr_debug("%s: pattern reg is 0x%x",
129 clk_hw_get_name(&common
->hw
), reg
);
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
;
136 /* We can't calculate the effective clock rate, so just fail. */
139 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate
, "SUNXI_CCU");
141 int ccu_sdm_helper_get_factors(struct ccu_common
*common
,
142 struct ccu_sdm_internal
*sdm
,
144 unsigned long *m
, unsigned long *n
)
148 if (!(common
->features
& CCU_FEATURE_SIGMA_DELTA_MOD
))
151 for (i
= 0; i
< sdm
->table_size
; i
++)
152 if (sdm
->table
[i
].rate
== rate
) {
153 *m
= sdm
->table
[i
].m
;
154 *n
= sdm
->table
[i
].n
;
161 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors
, "SUNXI_CCU");