forked from Minki/linux
2874c5fd28
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license as published by the free software foundation either version 2 of the license or at your option any later version extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 3029 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070032.746973796@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
156 lines
4.3 KiB
C
156 lines
4.3 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright (C) 2017 Chen-Yu Tsai <wens@csie.org>
|
|
*/
|
|
|
|
#include <linux/clk-provider.h>
|
|
#include <linux/io.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
#include "ccu_sdm.h"
|
|
|
|
bool ccu_sdm_helper_is_enabled(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm)
|
|
{
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return false;
|
|
|
|
if (sdm->enable && !(readl(common->base + common->reg) & sdm->enable))
|
|
return false;
|
|
|
|
return !!(readl(common->base + sdm->tuning_reg) & sdm->tuning_enable);
|
|
}
|
|
|
|
void ccu_sdm_helper_enable(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm,
|
|
unsigned long rate)
|
|
{
|
|
unsigned long flags;
|
|
unsigned int i;
|
|
u32 reg;
|
|
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return;
|
|
|
|
/* Set the pattern */
|
|
for (i = 0; i < sdm->table_size; i++)
|
|
if (sdm->table[i].rate == rate)
|
|
writel(sdm->table[i].pattern,
|
|
common->base + sdm->tuning_reg);
|
|
|
|
/* Make sure SDM is enabled */
|
|
spin_lock_irqsave(common->lock, flags);
|
|
reg = readl(common->base + sdm->tuning_reg);
|
|
writel(reg | sdm->tuning_enable, common->base + sdm->tuning_reg);
|
|
spin_unlock_irqrestore(common->lock, flags);
|
|
|
|
spin_lock_irqsave(common->lock, flags);
|
|
reg = readl(common->base + common->reg);
|
|
writel(reg | sdm->enable, common->base + common->reg);
|
|
spin_unlock_irqrestore(common->lock, flags);
|
|
}
|
|
|
|
void ccu_sdm_helper_disable(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm)
|
|
{
|
|
unsigned long flags;
|
|
u32 reg;
|
|
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return;
|
|
|
|
spin_lock_irqsave(common->lock, flags);
|
|
reg = readl(common->base + common->reg);
|
|
writel(reg & ~sdm->enable, common->base + common->reg);
|
|
spin_unlock_irqrestore(common->lock, flags);
|
|
|
|
spin_lock_irqsave(common->lock, flags);
|
|
reg = readl(common->base + sdm->tuning_reg);
|
|
writel(reg & ~sdm->tuning_enable, common->base + sdm->tuning_reg);
|
|
spin_unlock_irqrestore(common->lock, flags);
|
|
}
|
|
|
|
/*
|
|
* Sigma delta modulation provides a way to do fractional-N frequency
|
|
* synthesis, in essence allowing the PLL to output any frequency
|
|
* within its operational range. On earlier SoCs such as the A10/A20,
|
|
* some PLLs support this. On later SoCs, all PLLs support this.
|
|
*
|
|
* The datasheets do not explain what the "wave top" and "wave bottom"
|
|
* parameters mean or do, nor how to calculate the effective output
|
|
* frequency. The only examples (and real world usage) are for the audio
|
|
* PLL to generate 24.576 and 22.5792 MHz clock rates used by the audio
|
|
* peripherals. The author lacks the underlying domain knowledge to
|
|
* pursue this.
|
|
*
|
|
* The goal and function of the following code is to support the two
|
|
* clock rates used by the audio subsystem, allowing for proper audio
|
|
* playback and capture without any pitch or speed changes.
|
|
*/
|
|
bool ccu_sdm_helper_has_rate(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm,
|
|
unsigned long rate)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return false;
|
|
|
|
for (i = 0; i < sdm->table_size; i++)
|
|
if (sdm->table[i].rate == rate)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned long ccu_sdm_helper_read_rate(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm,
|
|
u32 m, u32 n)
|
|
{
|
|
unsigned int i;
|
|
u32 reg;
|
|
|
|
pr_debug("%s: Read sigma-delta modulation setting\n",
|
|
clk_hw_get_name(&common->hw));
|
|
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return 0;
|
|
|
|
pr_debug("%s: clock is sigma-delta modulated\n",
|
|
clk_hw_get_name(&common->hw));
|
|
|
|
reg = readl(common->base + sdm->tuning_reg);
|
|
|
|
pr_debug("%s: pattern reg is 0x%x",
|
|
clk_hw_get_name(&common->hw), reg);
|
|
|
|
for (i = 0; i < sdm->table_size; i++)
|
|
if (sdm->table[i].pattern == reg &&
|
|
sdm->table[i].m == m && sdm->table[i].n == n)
|
|
return sdm->table[i].rate;
|
|
|
|
/* We can't calculate the effective clock rate, so just fail. */
|
|
return 0;
|
|
}
|
|
|
|
int ccu_sdm_helper_get_factors(struct ccu_common *common,
|
|
struct ccu_sdm_internal *sdm,
|
|
unsigned long rate,
|
|
unsigned long *m, unsigned long *n)
|
|
{
|
|
unsigned int i;
|
|
|
|
if (!(common->features & CCU_FEATURE_SIGMA_DELTA_MOD))
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < sdm->table_size; i++)
|
|
if (sdm->table[i].rate == rate) {
|
|
*m = sdm->table[i].m;
|
|
*n = sdm->table[i].n;
|
|
return 0;
|
|
}
|
|
|
|
/* nothing found */
|
|
return -EINVAL;
|
|
}
|