xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_sdm.c (revision 62e59c4e)
105d2eaacSChen-Yu Tsai /*
205d2eaacSChen-Yu Tsai  * Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
305d2eaacSChen-Yu Tsai  *
405d2eaacSChen-Yu Tsai  * This program is free software; you can redistribute it and/or
505d2eaacSChen-Yu Tsai  * modify it under the terms of the GNU General Public License as
605d2eaacSChen-Yu Tsai  * published by the Free Software Foundation; either version 2 of
705d2eaacSChen-Yu Tsai  * the License, or (at your option) any later version.
805d2eaacSChen-Yu Tsai  */
905d2eaacSChen-Yu Tsai 
1005d2eaacSChen-Yu Tsai #include <linux/clk-provider.h>
1162e59c4eSStephen Boyd #include <linux/io.h>
1205d2eaacSChen-Yu Tsai #include <linux/spinlock.h>
1305d2eaacSChen-Yu Tsai 
1405d2eaacSChen-Yu Tsai #include "ccu_sdm.h"
1505d2eaacSChen-Yu Tsai 
1605d2eaacSChen-Yu Tsai bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
1705d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm)
1805d2eaacSChen-Yu Tsai {
1905d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
2005d2eaacSChen-Yu Tsai 		return false;
2105d2eaacSChen-Yu Tsai 
2205d2eaacSChen-Yu Tsai 	if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
2305d2eaacSChen-Yu Tsai 		return false;
2405d2eaacSChen-Yu Tsai 
2505d2eaacSChen-Yu Tsai 	return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
2605d2eaacSChen-Yu Tsai }
2705d2eaacSChen-Yu Tsai 
2805d2eaacSChen-Yu Tsai void ccu_sdm_helper_enable(struct ccu_common *common,
2905d2eaacSChen-Yu Tsai 			   struct ccu_sdm_internal *sdm,
3005d2eaacSChen-Yu Tsai 			   unsigned long rate)
3105d2eaacSChen-Yu Tsai {
3205d2eaacSChen-Yu Tsai 	unsigned long flags;
3305d2eaacSChen-Yu Tsai 	unsigned int i;
3405d2eaacSChen-Yu Tsai 	u32 reg;
3505d2eaacSChen-Yu Tsai 
3605d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
3705d2eaacSChen-Yu Tsai 		return;
3805d2eaacSChen-Yu Tsai 
3905d2eaacSChen-Yu Tsai 	/* Set the pattern */
4005d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
4105d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate)
4205d2eaacSChen-Yu Tsai 			writel(sdm->table[i].pattern,
4305d2eaacSChen-Yu Tsai 			       common->base + sdm->tuning_reg);
4405d2eaacSChen-Yu Tsai 
4505d2eaacSChen-Yu Tsai 	/* Make sure SDM is enabled */
4605d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
4705d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
4805d2eaacSChen-Yu Tsai 	writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
4905d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
5005d2eaacSChen-Yu Tsai 
5105d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
5205d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
5305d2eaacSChen-Yu Tsai 	writel(reg | sdm->enable, common->base + common->reg);
5405d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
5505d2eaacSChen-Yu Tsai }
5605d2eaacSChen-Yu Tsai 
5705d2eaacSChen-Yu Tsai void ccu_sdm_helper_disable(struct ccu_common *common,
5805d2eaacSChen-Yu Tsai 			    struct ccu_sdm_internal *sdm)
5905d2eaacSChen-Yu Tsai {
6005d2eaacSChen-Yu Tsai 	unsigned long flags;
6105d2eaacSChen-Yu Tsai 	u32 reg;
6205d2eaacSChen-Yu Tsai 
6305d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
6405d2eaacSChen-Yu Tsai 		return;
6505d2eaacSChen-Yu Tsai 
6605d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
6705d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
6805d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->enable, common->base + common->reg);
6905d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
7005d2eaacSChen-Yu Tsai 
7105d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
7205d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
7305d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
7405d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
7505d2eaacSChen-Yu Tsai }
7605d2eaacSChen-Yu Tsai 
7705d2eaacSChen-Yu Tsai /*
7805d2eaacSChen-Yu Tsai  * Sigma delta modulation provides a way to do fractional-N frequency
7905d2eaacSChen-Yu Tsai  * synthesis, in essence allowing the PLL to output any frequency
8005d2eaacSChen-Yu Tsai  * within its operational range. On earlier SoCs such as the A10/A20,
8105d2eaacSChen-Yu Tsai  * some PLLs support this. On later SoCs, all PLLs support this.
8205d2eaacSChen-Yu Tsai  *
8305d2eaacSChen-Yu Tsai  * The datasheets do not explain what the "wave top" and "wave bottom"
8405d2eaacSChen-Yu Tsai  * parameters mean or do, nor how to calculate the effective output
8505d2eaacSChen-Yu Tsai  * frequency. The only examples (and real world usage) are for the audio
8605d2eaacSChen-Yu Tsai  * PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
8705d2eaacSChen-Yu Tsai  * peripherals. The author lacks the underlying domain knowledge to
8805d2eaacSChen-Yu Tsai  * pursue this.
8905d2eaacSChen-Yu Tsai  *
9005d2eaacSChen-Yu Tsai  * The goal and function of the following code is to support the two
9105d2eaacSChen-Yu Tsai  * clock rates used by the audio subsystem, allowing for proper audio
9205d2eaacSChen-Yu Tsai  * playback and capture without any pitch or speed changes.
9305d2eaacSChen-Yu Tsai  */
9405d2eaacSChen-Yu Tsai bool ccu_sdm_helper_has_rate(struct ccu_common *common,
9505d2eaacSChen-Yu Tsai 			     struct ccu_sdm_internal *sdm,
9605d2eaacSChen-Yu Tsai 			     unsigned long rate)
9705d2eaacSChen-Yu Tsai {
9805d2eaacSChen-Yu Tsai 	unsigned int i;
9905d2eaacSChen-Yu Tsai 
10005d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
10105d2eaacSChen-Yu Tsai 		return false;
10205d2eaacSChen-Yu Tsai 
10305d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
10405d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate)
10505d2eaacSChen-Yu Tsai 			return true;
10605d2eaacSChen-Yu Tsai 
10705d2eaacSChen-Yu Tsai 	return false;
10805d2eaacSChen-Yu Tsai }
10905d2eaacSChen-Yu Tsai 
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 }
13905d2eaacSChen-Yu Tsai 
14005d2eaacSChen-Yu Tsai int ccu_sdm_helper_get_factors(struct ccu_common *common,
14105d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm,
14205d2eaacSChen-Yu Tsai 			       unsigned long rate,
14305d2eaacSChen-Yu Tsai 			       unsigned long *m, unsigned long *n)
14405d2eaacSChen-Yu Tsai {
14505d2eaacSChen-Yu Tsai 	unsigned int i;
14605d2eaacSChen-Yu Tsai 
14705d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
14805d2eaacSChen-Yu Tsai 		return -EINVAL;
14905d2eaacSChen-Yu Tsai 
15005d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
15105d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate) {
15205d2eaacSChen-Yu Tsai 			*m = sdm->table[i].m;
15305d2eaacSChen-Yu Tsai 			*n = sdm->table[i].n;
15405d2eaacSChen-Yu Tsai 			return 0;
15505d2eaacSChen-Yu Tsai 		}
15605d2eaacSChen-Yu Tsai 
15705d2eaacSChen-Yu Tsai 	/* nothing found */
15805d2eaacSChen-Yu Tsai 	return -EINVAL;
15905d2eaacSChen-Yu Tsai }
160