Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
0004  */
0005 
0006 #include <linux/clk-provider.h>
0007 #include <linux/io.h>
0008 #include <linux/spinlock.h>
0009 
0010 #include "ccu_sdm.h"
0011 
0012 bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
0013                    struct ccu_sdm_internal *sdm)
0014 {
0015     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0016         return false;
0017 
0018     if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
0019         return false;
0020 
0021     return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
0022 }
0023 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU);
0024 
0025 void ccu_sdm_helper_enable(struct ccu_common *common,
0026                struct ccu_sdm_internal *sdm,
0027                unsigned long rate)
0028 {
0029     unsigned long flags;
0030     unsigned int i;
0031     u32 reg;
0032 
0033     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0034         return;
0035 
0036     /* Set the pattern */
0037     for (i = 0; i < sdm->table_size; i++)
0038         if (sdm->table[i].rate == rate)
0039             writel(sdm->table[i].pattern,
0040                    common->base + sdm->tuning_reg);
0041 
0042     /* Make sure SDM is enabled */
0043     spin_lock_irqsave(common->lock, flags);
0044     reg = readl(common->base + sdm->tuning_reg);
0045     writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
0046     spin_unlock_irqrestore(common->lock, flags);
0047 
0048     spin_lock_irqsave(common->lock, flags);
0049     reg = readl(common->base + common->reg);
0050     writel(reg | sdm->enable, common->base + common->reg);
0051     spin_unlock_irqrestore(common->lock, flags);
0052 }
0053 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU);
0054 
0055 void ccu_sdm_helper_disable(struct ccu_common *common,
0056                 struct ccu_sdm_internal *sdm)
0057 {
0058     unsigned long flags;
0059     u32 reg;
0060 
0061     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0062         return;
0063 
0064     spin_lock_irqsave(common->lock, flags);
0065     reg = readl(common->base + common->reg);
0066     writel(reg & ~sdm->enable, common->base + common->reg);
0067     spin_unlock_irqrestore(common->lock, flags);
0068 
0069     spin_lock_irqsave(common->lock, flags);
0070     reg = readl(common->base + sdm->tuning_reg);
0071     writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
0072     spin_unlock_irqrestore(common->lock, flags);
0073 }
0074 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, SUNXI_CCU);
0075 
0076 /*
0077  * Sigma delta modulation provides a way to do fractional-N frequency
0078  * synthesis, in essence allowing the PLL to output any frequency
0079  * within its operational range. On earlier SoCs such as the A10/A20,
0080  * some PLLs support this. On later SoCs, all PLLs support this.
0081  *
0082  * The datasheets do not explain what the "wave top" and "wave bottom"
0083  * parameters mean or do, nor how to calculate the effective output
0084  * frequency. The only examples (and real world usage) are for the audio
0085  * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
0086  * peripherals. The author lacks the underlying domain knowledge to
0087  * pursue this.
0088  *
0089  * The goal and function of the following code is to support the two
0090  * clock rates used by the audio subsystem, allowing for proper audio
0091  * playback and capture without any pitch or speed changes.
0092  */
0093 bool ccu_sdm_helper_has_rate(struct ccu_common *common,
0094                  struct ccu_sdm_internal *sdm,
0095                  unsigned long rate)
0096 {
0097     unsigned int i;
0098 
0099     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0100         return false;
0101 
0102     for (i = 0; i < sdm->table_size; i++)
0103         if (sdm->table[i].rate == rate)
0104             return true;
0105 
0106     return false;
0107 }
0108 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU);
0109 
0110 unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
0111                        struct ccu_sdm_internal *sdm,
0112                        u32 m, u32 n)
0113 {
0114     unsigned int i;
0115     u32 reg;
0116 
0117     pr_debug("%s: Read sigma-delta modulation setting\n",
0118          clk_hw_get_name(&common->hw));
0119 
0120     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0121         return 0;
0122 
0123     pr_debug("%s: clock is sigma-delta modulated\n",
0124          clk_hw_get_name(&common->hw));
0125 
0126     reg = readl(common->base + sdm->tuning_reg);
0127 
0128     pr_debug("%s: pattern reg is 0x%x",
0129          clk_hw_get_name(&common->hw), reg);
0130 
0131     for (i = 0; i < sdm->table_size; i++)
0132         if (sdm->table[i].pattern == reg &&
0133             sdm->table[i].m == m && sdm->table[i].n == n)
0134             return sdm->table[i].rate;
0135 
0136     /* We can't calculate the effective clock rate, so just fail. */
0137     return 0;
0138 }
0139 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU);
0140 
0141 int ccu_sdm_helper_get_factors(struct ccu_common *common,
0142                    struct ccu_sdm_internal *sdm,
0143                    unsigned long rate,
0144                    unsigned long *m, unsigned long *n)
0145 {
0146     unsigned int i;
0147 
0148     if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
0149         return -EINVAL;
0150 
0151     for (i = 0; i < sdm->table_size; i++)
0152         if (sdm->table[i].rate == rate) {
0153             *m = sdm->table[i].m;
0154             *n = sdm->table[i].n;
0155             return 0;
0156         }
0157 
0158     /* nothing found */
0159     return -EINVAL;
0160 }
0161 EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU);