xref: /openbmc/linux/drivers/clk/sunxi-ng/ccu_sdm.c (revision 05d2eaac)
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>
1105d2eaacSChen-Yu Tsai #include <linux/spinlock.h>
1205d2eaacSChen-Yu Tsai 
1305d2eaacSChen-Yu Tsai #include "ccu_sdm.h"
1405d2eaacSChen-Yu Tsai 
1505d2eaacSChen-Yu Tsai bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
1605d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm)
1705d2eaacSChen-Yu Tsai {
1805d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
1905d2eaacSChen-Yu Tsai 		return false;
2005d2eaacSChen-Yu Tsai 
2105d2eaacSChen-Yu Tsai 	if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
2205d2eaacSChen-Yu Tsai 		return false;
2305d2eaacSChen-Yu Tsai 
2405d2eaacSChen-Yu Tsai 	return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
2505d2eaacSChen-Yu Tsai }
2605d2eaacSChen-Yu Tsai 
2705d2eaacSChen-Yu Tsai void ccu_sdm_helper_enable(struct ccu_common *common,
2805d2eaacSChen-Yu Tsai 			   struct ccu_sdm_internal *sdm,
2905d2eaacSChen-Yu Tsai 			   unsigned long rate)
3005d2eaacSChen-Yu Tsai {
3105d2eaacSChen-Yu Tsai 	unsigned long flags;
3205d2eaacSChen-Yu Tsai 	unsigned int i;
3305d2eaacSChen-Yu Tsai 	u32 reg;
3405d2eaacSChen-Yu Tsai 
3505d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
3605d2eaacSChen-Yu Tsai 		return;
3705d2eaacSChen-Yu Tsai 
3805d2eaacSChen-Yu Tsai 	/* Set the pattern */
3905d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
4005d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate)
4105d2eaacSChen-Yu Tsai 			writel(sdm->table[i].pattern,
4205d2eaacSChen-Yu Tsai 			       common->base + sdm->tuning_reg);
4305d2eaacSChen-Yu Tsai 
4405d2eaacSChen-Yu Tsai 	/* Make sure SDM is enabled */
4505d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
4605d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
4705d2eaacSChen-Yu Tsai 	writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
4805d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
4905d2eaacSChen-Yu Tsai 
5005d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
5105d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
5205d2eaacSChen-Yu Tsai 	writel(reg | sdm->enable, common->base + common->reg);
5305d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
5405d2eaacSChen-Yu Tsai }
5505d2eaacSChen-Yu Tsai 
5605d2eaacSChen-Yu Tsai void ccu_sdm_helper_disable(struct ccu_common *common,
5705d2eaacSChen-Yu Tsai 			    struct ccu_sdm_internal *sdm)
5805d2eaacSChen-Yu Tsai {
5905d2eaacSChen-Yu Tsai 	unsigned long flags;
6005d2eaacSChen-Yu Tsai 	u32 reg;
6105d2eaacSChen-Yu Tsai 
6205d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
6305d2eaacSChen-Yu Tsai 		return;
6405d2eaacSChen-Yu Tsai 
6505d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
6605d2eaacSChen-Yu Tsai 	reg = readl(common->base + common->reg);
6705d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->enable, common->base + common->reg);
6805d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
6905d2eaacSChen-Yu Tsai 
7005d2eaacSChen-Yu Tsai 	spin_lock_irqsave(common->lock, flags);
7105d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
7205d2eaacSChen-Yu Tsai 	writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
7305d2eaacSChen-Yu Tsai 	spin_unlock_irqrestore(common->lock, flags);
7405d2eaacSChen-Yu Tsai }
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  */
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 }
10805d2eaacSChen-Yu Tsai 
10905d2eaacSChen-Yu Tsai unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
11005d2eaacSChen-Yu Tsai 				       struct ccu_sdm_internal *sdm,
11105d2eaacSChen-Yu Tsai 				       u32 m, u32 n)
11205d2eaacSChen-Yu Tsai {
11305d2eaacSChen-Yu Tsai 	unsigned int i;
11405d2eaacSChen-Yu Tsai 	u32 reg;
11505d2eaacSChen-Yu Tsai 
11605d2eaacSChen-Yu Tsai 	pr_debug("%s: Read sigma-delta modulation setting\n",
11705d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw));
11805d2eaacSChen-Yu Tsai 
11905d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
12005d2eaacSChen-Yu Tsai 		return 0;
12105d2eaacSChen-Yu Tsai 
12205d2eaacSChen-Yu Tsai 	pr_debug("%s: clock is sigma-delta modulated\n",
12305d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw));
12405d2eaacSChen-Yu Tsai 
12505d2eaacSChen-Yu Tsai 	reg = readl(common->base + sdm->tuning_reg);
12605d2eaacSChen-Yu Tsai 
12705d2eaacSChen-Yu Tsai 	pr_debug("%s: pattern reg is 0x%x",
12805d2eaacSChen-Yu Tsai 		 clk_hw_get_name(&common->hw), reg);
12905d2eaacSChen-Yu Tsai 
13005d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
13105d2eaacSChen-Yu Tsai 		if (sdm->table[i].pattern == reg &&
13205d2eaacSChen-Yu Tsai 		    sdm->table[i].m == m && sdm->table[i].n == n)
13305d2eaacSChen-Yu Tsai 			return sdm->table[i].rate;
13405d2eaacSChen-Yu Tsai 
13505d2eaacSChen-Yu Tsai 	/* We can't calculate the effective clock rate, so just fail. */
13605d2eaacSChen-Yu Tsai 	return 0;
13705d2eaacSChen-Yu Tsai }
13805d2eaacSChen-Yu Tsai 
13905d2eaacSChen-Yu Tsai int ccu_sdm_helper_get_factors(struct ccu_common *common,
14005d2eaacSChen-Yu Tsai 			       struct ccu_sdm_internal *sdm,
14105d2eaacSChen-Yu Tsai 			       unsigned long rate,
14205d2eaacSChen-Yu Tsai 			       unsigned long *m, unsigned long *n)
14305d2eaacSChen-Yu Tsai {
14405d2eaacSChen-Yu Tsai 	unsigned int i;
14505d2eaacSChen-Yu Tsai 
14605d2eaacSChen-Yu Tsai 	if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
14705d2eaacSChen-Yu Tsai 		return -EINVAL;
14805d2eaacSChen-Yu Tsai 
14905d2eaacSChen-Yu Tsai 	for (i = 0; i < sdm->table_size; i++)
15005d2eaacSChen-Yu Tsai 		if (sdm->table[i].rate == rate) {
15105d2eaacSChen-Yu Tsai 			*m = sdm->table[i].m;
15205d2eaacSChen-Yu Tsai 			*n = sdm->table[i].n;
15305d2eaacSChen-Yu Tsai 			return 0;
15405d2eaacSChen-Yu Tsai 		}
15505d2eaacSChen-Yu Tsai 
15605d2eaacSChen-Yu Tsai 	/* nothing found */
15705d2eaacSChen-Yu Tsai 	return -EINVAL;
15805d2eaacSChen-Yu Tsai }
159