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