162 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 frequency78 * synthesis, in essence allowing the PLL to output any frequency79 * 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 output84 * frequency. The only examples (and real world usage) are for the audio85 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio86 * peripherals. The author lacks the underlying domain knowledge to87 * pursue this.88 *89 * The goal and function of the following code is to support the two90 * clock rates used by the audio subsystem, allowing for proper audio91 * 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);162