File Index
Files and directories
Sign in
FILE

ccu_sdm.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/sunxi-ng/ccu_sdm.c C
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
  4 */
  5
  6#include <linux/clk-provider.h>
  7#include <linux/io.h>
  8#include <linux/spinlock.h>
  9
 10#include "ccu_sdm.h"
 11
 12bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
 13			       struct ccu_sdm_internal *sdm)
 14{
 15	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
 16		return false;
 17
 18	if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
 19		return false;
 20
 21	return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
 22}
 23EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, "SUNXI_CCU");
 24
 25void ccu_sdm_helper_enable(struct ccu_common *common,
 26			   struct ccu_sdm_internal *sdm,
 27			   unsigned long rate)
 28{
 29	unsigned long flags;
 30	unsigned int i;
 31	u32 reg;
 32
 33	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
 34		return;
 35
 36	/* Set the pattern */
 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);
 41
 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);
 47
 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);
 52}
 53EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, "SUNXI_CCU");
 54
 55void ccu_sdm_helper_disable(struct ccu_common *common,
 56			    struct ccu_sdm_internal *sdm)
 57{
 58	unsigned long flags;
 59	u32 reg;
 60
 61	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
 62		return;
 63
 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);
 68
 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);
 73}
 74EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, "SUNXI_CCU");
 75
 76/*
 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.
 81 *
 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
 87 * pursue this.
 88 *
 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.
 92 */
 93bool ccu_sdm_helper_has_rate(struct ccu_common *common,
 94			     struct ccu_sdm_internal *sdm,
 95			     unsigned long rate)
 96{
 97	unsigned int i;
 98
 99	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
100		return false;
101
102	for (i = 0; i < sdm->table_size; i++)
103		if (sdm->table[i].rate == rate)
104			return true;
105
106	return false;
107}
108EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, "SUNXI_CCU");
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}
139EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, "SUNXI_CCU");
140
141int ccu_sdm_helper_get_factors(struct ccu_common *common,
142			       struct ccu_sdm_internal *sdm,
143			       unsigned long rate,
144			       unsigned long *m, unsigned long *n)
145{
146	unsigned int i;
147
148	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
149		return -EINVAL;
150
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;
155			return 0;
156		}
157
158	/* nothing found */
159	return -EINVAL;
160}
161EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, "SUNXI_CCU");
~/ Read-only file preview.