xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_sdm.c (revision 551b62b1)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
205d2eaacSChen-Yu Tsai /*
305d2eaacSChen-Yu Tsai  * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
405d2eaacSChen-Yu Tsai  */
505d2eaacSChen-Yu Tsai 
605d2eaacSChen-Yu Tsai #include <linux/clk-provider.h>
762e59c4eSStephen Boyd #include <linux/io.h>
805d2eaacSChen-Yu Tsai #include <linux/spinlock.h>
905d2eaacSChen-Yu Tsai 
1005d2eaacSChen-Yu Tsai #include "ccu_sdm.h"
1105d2eaacSChen-Yu Tsai 
ccu_sdm_helper_is_enabled(struct ccu_common * common,struct ccu_sdm_internal * sdm)1205d2eaacSChen-Yu Tsai bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
1305d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm)
1405d2eaacSChen-Yu Tsai {
1505d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
1605d2eaacSChen-Yu Tsai 		return false;
1705d2eaacSChen-Yu Tsai 
1805d2eaacSChen-Yu Tsai 	if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
1905d2eaacSChen-Yu Tsai 		return false;
2005d2eaacSChen-Yu Tsai 
2105d2eaacSChen-Yu Tsai 	return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
2205d2eaacSChen-Yu Tsai }
23*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_is_enabled, SUNXI_CCU);
2405d2eaacSChen-Yu Tsai 
ccu_sdm_helper_enable(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate)2505d2eaacSChen-Yu Tsai void ccu_sdm_helper_enable(struct ccu_common *common,
2605d2eaacSChen-Yu Tsai 			   struct ccu_sdm_internal *sdm,
2705d2eaacSChen-Yu Tsai 			   unsigned long rate)
2805d2eaacSChen-Yu Tsai {
2905d2eaacSChen-Yu Tsai 	unsigned long flags;
3005d2eaacSChen-Yu Tsai 	unsigned int i;
3105d2eaacSChen-Yu Tsai 	u32 reg;
3205d2eaacSChen-Yu Tsai 
3305d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
3405d2eaacSChen-Yu Tsai 		return;
3505d2eaacSChen-Yu Tsai 
3605d2eaacSChen-Yu Tsai 	/* Set the pattern */
3705d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
3805d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate)
3905d2eaacSChen-Yu Tsai 			writel(sdm->table[i].pattern,
4005d2eaacSChen-Yu Tsai 			       common->base + sdm->tuning_reg);
4105d2eaacSChen-Yu Tsai 
4205d2eaacSChen-Yu Tsai 	/* Make sure SDM is enabled */
4305d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
4405d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
4505d2eaacSChen-Yu Tsai 	writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
4605d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
4705d2eaacSChen-Yu Tsai 
4805d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
4905d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
5005d2eaacSChen-Yu Tsai 	writel(reg | sdm->enable, common->base + common->reg);
5105d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
5205d2eaacSChen-Yu Tsai }
53*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_enable, SUNXI_CCU);
5405d2eaacSChen-Yu Tsai 
ccu_sdm_helper_disable(struct ccu_common * common,struct ccu_sdm_internal * sdm)5505d2eaacSChen-Yu Tsai void ccu_sdm_helper_disable(struct ccu_common *common,
5605d2eaacSChen-Yu Tsai 			    struct ccu_sdm_internal *sdm)
5705d2eaacSChen-Yu Tsai {
5805d2eaacSChen-Yu Tsai 	unsigned long flags;
5905d2eaacSChen-Yu Tsai 	u32 reg;
6005d2eaacSChen-Yu Tsai 
6105d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
6205d2eaacSChen-Yu Tsai 		return;
6305d2eaacSChen-Yu Tsai 
6405d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
6505d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
6605d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->enable, common->base + common->reg);
6705d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
6805d2eaacSChen-Yu Tsai 
6905d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
7005d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
7105d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
7205d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
7305d2eaacSChen-Yu Tsai }
74*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_disable, SUNXI_CCU);
7505d2eaacSChen-Yu Tsai 
7605d2eaacSChen-Yu Tsai /*
7705d2eaacSChen-Yu Tsai  * Sigma delta modulation provides a way to do fractional-N frequency
7805d2eaacSChen-Yu Tsai  * synthesis, in essence allowing the PLL to output any frequency
7905d2eaacSChen-Yu Tsai  * within its operational range. On earlier SoCs such as the A10/A20,
8005d2eaacSChen-Yu Tsai  * some PLLs support this. On later SoCs, all PLLs support this.
8105d2eaacSChen-Yu Tsai  *
8205d2eaacSChen-Yu Tsai  * The datasheets do not explain what the "wave top" and "wave bottom"
8305d2eaacSChen-Yu Tsai  * parameters mean or do, nor how to calculate the effective output
8405d2eaacSChen-Yu Tsai  * frequency. The only examples (and real world usage) are for the audio
8505d2eaacSChen-Yu Tsai  * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
8605d2eaacSChen-Yu Tsai  * peripherals. The author lacks the underlying domain knowledge to
8705d2eaacSChen-Yu Tsai  * pursue this.
8805d2eaacSChen-Yu Tsai  *
8905d2eaacSChen-Yu Tsai  * The goal and function of the following code is to support the two
9005d2eaacSChen-Yu Tsai  * clock rates used by the audio subsystem, allowing for proper audio
9105d2eaacSChen-Yu Tsai  * playback and capture without any pitch or speed changes.
9205d2eaacSChen-Yu Tsai  */
ccu_sdm_helper_has_rate(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate)9305d2eaacSChen-Yu Tsai bool ccu_sdm_helper_has_rate(struct ccu_common *common,
9405d2eaacSChen-Yu Tsai 			     struct ccu_sdm_internal *sdm,
9505d2eaacSChen-Yu Tsai 			     unsigned long rate)
9605d2eaacSChen-Yu Tsai {
9705d2eaacSChen-Yu Tsai 	unsigned int i;
9805d2eaacSChen-Yu Tsai 
9905d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
10005d2eaacSChen-Yu Tsai 		return false;
10105d2eaacSChen-Yu Tsai 
10205d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
10305d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate)
10405d2eaacSChen-Yu Tsai 			return true;
10505d2eaacSChen-Yu Tsai 
10605d2eaacSChen-Yu Tsai 	return false;
10705d2eaacSChen-Yu Tsai }
108*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_has_rate, SUNXI_CCU);
10905d2eaacSChen-Yu Tsai 
ccu_sdm_helper_read_rate(struct ccu_common * common,struct ccu_sdm_internal * sdm,u32 m,u32 n)11005d2eaacSChen-Yu Tsai unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
11105d2eaacSChen-Yu Tsai 				       struct ccu_sdm_internal *sdm,
11205d2eaacSChen-Yu Tsai 				       u32 m, u32 n)
11305d2eaacSChen-Yu Tsai {
11405d2eaacSChen-Yu Tsai 	unsigned int i;
11505d2eaacSChen-Yu Tsai 	u32 reg;
11605d2eaacSChen-Yu Tsai 
11705d2eaacSChen-Yu Tsai 	pr_debug("%s: Read sigma-delta modulation setting\n",
11805d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw));
11905d2eaacSChen-Yu Tsai 
12005d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
12105d2eaacSChen-Yu Tsai 		return 0;
12205d2eaacSChen-Yu Tsai 
12305d2eaacSChen-Yu Tsai 	pr_debug("%s: clock is sigma-delta modulated\n",
12405d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw));
12505d2eaacSChen-Yu Tsai 
12605d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
12705d2eaacSChen-Yu Tsai 
12805d2eaacSChen-Yu Tsai 	pr_debug("%s: pattern reg is 0x%x",
12905d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw), reg);
13005d2eaacSChen-Yu Tsai 
13105d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
13205d2eaacSChen-Yu Tsai 		if (sdm->table[i].pattern == reg &&
13305d2eaacSChen-Yu Tsai 		    sdm->table[i].m == m && sdm->table[i].n == n)
13405d2eaacSChen-Yu Tsai 			return sdm->table[i].rate;
13505d2eaacSChen-Yu Tsai 
13605d2eaacSChen-Yu Tsai 	/* We can't calculate the effective clock rate, so just fail. */
13705d2eaacSChen-Yu Tsai 	return 0;
13805d2eaacSChen-Yu Tsai }
139*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_read_rate, SUNXI_CCU);
14005d2eaacSChen-Yu Tsai 
ccu_sdm_helper_get_factors(struct ccu_common * common,struct ccu_sdm_internal * sdm,unsigned long rate,unsigned long * m,unsigned long * n)14105d2eaacSChen-Yu Tsai int ccu_sdm_helper_get_factors(struct ccu_common *common,
14205d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm,
14305d2eaacSChen-Yu Tsai 			       unsigned long rate,
14405d2eaacSChen-Yu Tsai 			       unsigned long *m, unsigned long *n)
14505d2eaacSChen-Yu Tsai {
14605d2eaacSChen-Yu Tsai 	unsigned int i;
14705d2eaacSChen-Yu Tsai 
14805d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
14905d2eaacSChen-Yu Tsai 		return -EINVAL;
15005d2eaacSChen-Yu Tsai 
15105d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
15205d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate) {
15305d2eaacSChen-Yu Tsai 			*m = sdm->table[i].m;
15405d2eaacSChen-Yu Tsai 			*n = sdm->table[i].n;
15505d2eaacSChen-Yu Tsai 			return 0;
15605d2eaacSChen-Yu Tsai 		}
15705d2eaacSChen-Yu Tsai 
15805d2eaacSChen-Yu Tsai 	/* nothing found */
15905d2eaacSChen-Yu Tsai 	return -EINVAL;
16005d2eaacSChen-Yu Tsai }
161*551b62b1SSamuel Holland EXPORT_SYMBOL_NS_GPL(ccu_sdm_helper_get_factors, SUNXI_CCU);
162