brintos

brintos / linux-shallow public Read only

0
0
Text · 4.6 KiB · 41937ed Raw
162 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*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 12bool 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}23EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU);24 25void 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}53EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU);54 55void 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}74EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, SUNXI_CCU);75 76/*77 * Sigma delta modulation provides a way to do fractional-N frequency78 * synthesis, in essence allowing the PLL to output any frequency79 * 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 output84 * frequency. The only examples (and real world usage) are for the audio85 * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio86 * peripherals. The author lacks the underlying domain knowledge to87 * pursue this.88 *89 * The goal and function of the following code is to support the two90 * clock rates used by the audio subsystem, allowing for proper audio91 * playback and capture without any pitch or speed changes.92 */93bool 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}108EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU);109 110unsigned 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}139EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU);140 141int 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}161EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU);162