1 /* 2 * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org> 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 */ 9 10 #include <linux/clk-provider.h> 11 #include <linux/spinlock.h> 12 13 #include "ccu_sdm.h" 14 15 bool ccu_sdm_helper_is_enabled(struct ccu_common *common, 16 struct ccu_sdm_internal *sdm) 17 { 18 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 19 return false; 20 21 if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable)) 22 return false; 23 24 return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable); 25 } 26 27 void ccu_sdm_helper_enable(struct ccu_common *common, 28 struct ccu_sdm_internal *sdm, 29 unsigned long rate) 30 { 31 unsigned long flags; 32 unsigned int i; 33 u32 reg; 34 35 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 36 return; 37 38 /* Set the pattern */ 39 for (i = 0; i < sdm->table_size; i++) 40 if (sdm->table[i].rate == rate) 41 writel(sdm->table[i].pattern, 42 common->base + sdm->tuning_reg); 43 44 /* Make sure SDM is enabled */ 45 spin_lock_irqsave(common->lock, flags); 46 reg = readl(common->base + sdm->tuning_reg); 47 writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg); 48 spin_unlock_irqrestore(common->lock, flags); 49 50 spin_lock_irqsave(common->lock, flags); 51 reg = readl(common->base + common->reg); 52 writel(reg | sdm->enable, common->base + common->reg); 53 spin_unlock_irqrestore(common->lock, flags); 54 } 55 56 void ccu_sdm_helper_disable(struct ccu_common *common, 57 struct ccu_sdm_internal *sdm) 58 { 59 unsigned long flags; 60 u32 reg; 61 62 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 63 return; 64 65 spin_lock_irqsave(common->lock, flags); 66 reg = readl(common->base + common->reg); 67 writel(reg & ~sdm->enable, common->base + common->reg); 68 spin_unlock_irqrestore(common->lock, flags); 69 70 spin_lock_irqsave(common->lock, flags); 71 reg = readl(common->base + sdm->tuning_reg); 72 writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg); 73 spin_unlock_irqrestore(common->lock, flags); 74 } 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 109 unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common, 110 struct ccu_sdm_internal *sdm, 111 u32 m, u32 n) 112 { 113 unsigned int i; 114 u32 reg; 115 116 pr_debug("%s: Read sigma-delta modulation setting\n", 117 clk_hw_get_name(&common->hw)); 118 119 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 120 return 0; 121 122 pr_debug("%s: clock is sigma-delta modulated\n", 123 clk_hw_get_name(&common->hw)); 124 125 reg = readl(common->base + sdm->tuning_reg); 126 127 pr_debug("%s: pattern reg is 0x%x", 128 clk_hw_get_name(&common->hw), reg); 129 130 for (i = 0; i < sdm->table_size; i++) 131 if (sdm->table[i].pattern == reg && 132 sdm->table[i].m == m && sdm->table[i].n == n) 133 return sdm->table[i].rate; 134 135 /* We can't calculate the effective clock rate, so just fail. */ 136 return 0; 137 } 138 139 int ccu_sdm_helper_get_factors(struct ccu_common *common, 140 struct ccu_sdm_internal *sdm, 141 unsigned long rate, 142 unsigned long *m, unsigned long *n) 143 { 144 unsigned int i; 145 146 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 147 return -EINVAL; 148 149 for (i = 0; i < sdm->table_size; i++) 150 if (sdm->table[i].rate == rate) { 151 *m = sdm->table[i].m; 152 *n = sdm->table[i].n; 153 return 0; 154 } 155 156 /* nothing found */ 157 return -EINVAL; 158 } 159