2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-10-12 16:36:59 +08:00
/*
* Copyright ( C ) 2017 Chen - Yu Tsai < wens @ csie . org >
*/
# include <linux/clk-provider.h>
2019-04-18 15:20:22 -07:00
# include <linux/io.h>
2017-10-12 16:36:59 +08:00
# 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 ;
}