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