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/io.h> 12 #include <linux/spinlock.h> 13 14 #include "ccu_sdm.h" 15 16 bool ccu_sdm_helper_is_enabled(struct ccu_common *common, 17 struct ccu_sdm_internal *sdm) 18 { 19 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 20 return false; 21 22 if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable)) 23 return false; 24 25 return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable); 26 } 27 28 void ccu_sdm_helper_enable(struct ccu_common *common, 29 struct ccu_sdm_internal *sdm, 30 unsigned long rate) 31 { 32 unsigned long flags; 33 unsigned int i; 34 u32 reg; 35 36 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 37 return; 38 39 /* Set the pattern */ 40 for (i = 0; i < sdm->table_size; i++) 41 if (sdm->table[i].rate == rate) 42 writel(sdm->table[i].pattern, 43 common->base + sdm->tuning_reg); 44 45 /* Make sure SDM is enabled */ 46 spin_lock_irqsave(common->lock, flags); 47 reg = readl(common->base + sdm->tuning_reg); 48 writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg); 49 spin_unlock_irqrestore(common->lock, flags); 50 51 spin_lock_irqsave(common->lock, flags); 52 reg = readl(common->base + common->reg); 53 writel(reg | sdm->enable, common->base + common->reg); 54 spin_unlock_irqrestore(common->lock, flags); 55 } 56 57 void ccu_sdm_helper_disable(struct ccu_common *common, 58 struct ccu_sdm_internal *sdm) 59 { 60 unsigned long flags; 61 u32 reg; 62 63 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 64 return; 65 66 spin_lock_irqsave(common->lock, flags); 67 reg = readl(common->base + common->reg); 68 writel(reg & ~sdm->enable, common->base + common->reg); 69 spin_unlock_irqrestore(common->lock, flags); 70 71 spin_lock_irqsave(common->lock, flags); 72 reg = readl(common->base + sdm->tuning_reg); 73 writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg); 74 spin_unlock_irqrestore(common->lock, flags); 75 } 76 77 /* 78 * Sigma delta modulation provides a way to do fractional-N frequency 79 * synthesis, in essence allowing the PLL to output any frequency 80 * within its operational range. On earlier SoCs such as the A10/A20, 81 * some PLLs support this. On later SoCs, all PLLs support this. 82 * 83 * The datasheets do not explain what the "wave top" and "wave bottom" 84 * parameters mean or do, nor how to calculate the effective output 85 * frequency. The only examples (and real world usage) are for the audio 86 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio 87 * peripherals. The author lacks the underlying domain knowledge to 88 * pursue this. 89 * 90 * The goal and function of the following code is to support the two 91 * clock rates used by the audio subsystem, allowing for proper audio 92 * playback and capture without any pitch or speed changes. 93 */ 94 bool ccu_sdm_helper_has_rate(struct ccu_common *common, 95 struct ccu_sdm_internal *sdm, 96 unsigned long rate) 97 { 98 unsigned int i; 99 100 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 101 return false; 102 103 for (i = 0; i < sdm->table_size; i++) 104 if (sdm->table[i].rate == rate) 105 return true; 106 107 return false; 108 } 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 140 int ccu_sdm_helper_get_factors(struct ccu_common *common, 141 struct ccu_sdm_internal *sdm, 142 unsigned long rate, 143 unsigned long *m, unsigned long *n) 144 { 145 unsigned int i; 146 147 if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD)) 148 return -EINVAL; 149 150 for (i = 0; i < sdm->table_size; i++) 151 if (sdm->table[i].rate == rate) { 152 *m = sdm->table[i].m; 153 *n = sdm->table[i].n; 154 return 0; 155 } 156 157 /* nothing found */ 158 return -EINVAL; 159 } 160