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 12 bool 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 } 23 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU); 24 25 void 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 } 53 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU); 54 55 void 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 } 74 EXPORT_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 */ 93 bool 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 } 108 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU); 109 110 unsigned 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 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU); 140 141 int 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 } 161 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU); 162