2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2015-04-23 11:35:39 +03:00
/*
* Copyright ( c ) 2014 MediaTek Inc .
* Author : James Liao < jamesjj . liao @ mediatek . com >
*/
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/clkdev.h>
# include <linux/delay.h>
# include "clk-mtk.h"
# define REG_CON0 0
# define REG_CON1 4
# define CON0_BASE_EN BIT(0)
# define CON0_PWR_ON BIT(0)
# define CON0_ISO_EN BIT(1)
2019-03-05 08:05:44 +03:00
# define PCW_CHG_MASK BIT(31)
2015-04-23 11:35:39 +03:00
# define AUDPLL_TUNER_EN BIT(31)
# define POSTDIV_MASK 0x7
2019-03-05 08:05:40 +03:00
/* default 7 bits integer, can be overridden with pcwibits. */
2015-04-23 11:35:39 +03:00
# define INTEGER_BITS 7
/*
* MediaTek PLLs are configured through their pcw value . The pcw value describes
* a divider in the PLL feedback loop which consists of 7 bits for the integer
* part and the remaining bits ( if present ) for the fractional part . Also they
* have a 3 bit power - of - two post divider .
*/
struct mtk_clk_pll {
struct clk_hw hw ;
void __iomem * base_addr ;
void __iomem * pd_addr ;
void __iomem * pwr_addr ;
void __iomem * tuner_addr ;
2017-10-23 07:10:34 +03:00
void __iomem * tuner_en_addr ;
2015-04-23 11:35:39 +03:00
void __iomem * pcw_addr ;
2019-03-05 08:05:44 +03:00
void __iomem * pcw_chg_addr ;
2021-07-26 13:57:05 +03:00
void __iomem * en_addr ;
2015-04-23 11:35:39 +03:00
const struct mtk_pll_data * data ;
} ;
static inline struct mtk_clk_pll * to_mtk_clk_pll ( struct clk_hw * hw )
{
return container_of ( hw , struct mtk_clk_pll , hw ) ;
}
static int mtk_pll_is_prepared ( struct clk_hw * hw )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
2021-07-26 13:57:05 +03:00
return ( readl ( pll - > en_addr ) & BIT ( pll - > data - > pll_en_bit ) ) ! = 0 ;
2015-04-23 11:35:39 +03:00
}
static unsigned long __mtk_pll_recalc_rate ( struct mtk_clk_pll * pll , u32 fin ,
u32 pcw , int postdiv )
{
int pcwbits = pll - > data - > pcwbits ;
2019-03-05 08:05:40 +03:00
int pcwfbits = 0 ;
int ibits ;
2015-04-23 11:35:39 +03:00
u64 vco ;
u8 c = 0 ;
/* The fractional part of the PLL divider. */
2019-03-05 08:05:40 +03:00
ibits = pll - > data - > pcwibits ? pll - > data - > pcwibits : INTEGER_BITS ;
if ( pcwbits > ibits )
pcwfbits = pcwbits - ibits ;
2015-04-23 11:35:39 +03:00
vco = ( u64 ) fin * pcw ;
if ( pcwfbits & & ( vco & GENMASK ( pcwfbits - 1 , 0 ) ) )
c = 1 ;
vco > > = pcwfbits ;
if ( c )
vco + + ;
return ( ( unsigned long ) vco + postdiv - 1 ) / postdiv ;
}
2019-03-05 08:05:38 +03:00
static void __mtk_pll_tuner_enable ( struct mtk_clk_pll * pll )
{
u32 r ;
if ( pll - > tuner_en_addr ) {
r = readl ( pll - > tuner_en_addr ) | BIT ( pll - > data - > tuner_en_bit ) ;
writel ( r , pll - > tuner_en_addr ) ;
} else if ( pll - > tuner_addr ) {
r = readl ( pll - > tuner_addr ) | AUDPLL_TUNER_EN ;
writel ( r , pll - > tuner_addr ) ;
}
}
static void __mtk_pll_tuner_disable ( struct mtk_clk_pll * pll )
{
u32 r ;
if ( pll - > tuner_en_addr ) {
r = readl ( pll - > tuner_en_addr ) & ~ BIT ( pll - > data - > tuner_en_bit ) ;
writel ( r , pll - > tuner_en_addr ) ;
} else if ( pll - > tuner_addr ) {
r = readl ( pll - > tuner_addr ) & ~ AUDPLL_TUNER_EN ;
writel ( r , pll - > tuner_addr ) ;
}
}
2015-04-23 11:35:39 +03:00
static void mtk_pll_set_rate_regs ( struct mtk_clk_pll * pll , u32 pcw ,
int postdiv )
{
2019-03-05 08:05:44 +03:00
u32 chg , val ;
2015-04-23 11:35:39 +03:00
2019-03-05 08:05:38 +03:00
/* disable tuner */
__mtk_pll_tuner_disable ( pll ) ;
2015-07-10 11:39:32 +03:00
/* set postdiv */
val = readl ( pll - > pd_addr ) ;
val & = ~ ( POSTDIV_MASK < < pll - > data - > pd_shift ) ;
val | = ( ffs ( postdiv ) - 1 ) < < pll - > data - > pd_shift ;
/* postdiv and pcw need to set at the same time if on same register */
if ( pll - > pd_addr ! = pll - > pcw_addr ) {
writel ( val , pll - > pd_addr ) ;
val = readl ( pll - > pcw_addr ) ;
}
2015-04-23 11:35:39 +03:00
2015-07-10 11:39:32 +03:00
/* set pcw */
2015-04-23 11:35:39 +03:00
val & = ~ GENMASK ( pll - > data - > pcw_shift + pll - > data - > pcwbits - 1 ,
pll - > data - > pcw_shift ) ;
val | = pcw < < pll - > data - > pcw_shift ;
writel ( val , pll - > pcw_addr ) ;
2019-03-05 08:05:46 +03:00
chg = readl ( pll - > pcw_chg_addr ) | PCW_CHG_MASK ;
2019-03-05 08:05:44 +03:00
writel ( chg , pll - > pcw_chg_addr ) ;
2015-04-23 11:35:39 +03:00
if ( pll - > tuner_addr )
2019-03-05 08:05:44 +03:00
writel ( val + 1 , pll - > tuner_addr ) ;
2015-04-23 11:35:39 +03:00
2019-03-05 08:05:38 +03:00
/* restore tuner_en */
__mtk_pll_tuner_enable ( pll ) ;
2019-03-05 08:05:46 +03:00
udelay ( 20 ) ;
2015-04-23 11:35:39 +03:00
}
/*
* mtk_pll_calc_values - calculate good values for a given input frequency .
* @ pll : The pll
* @ pcw : The pcw value ( output )
* @ postdiv : The post divider ( output )
* @ freq : The desired target frequency
* @ fin : The input frequency
*
*/
static void mtk_pll_calc_values ( struct mtk_clk_pll * pll , u32 * pcw , u32 * postdiv ,
u32 freq , u32 fin )
{
2019-03-05 08:05:40 +03:00
unsigned long fmin = pll - > data - > fmin ? pll - > data - > fmin : ( 1000 * MHZ ) ;
2015-07-10 11:39:34 +03:00
const struct mtk_pll_div_table * div_table = pll - > data - > div_table ;
2015-04-23 11:35:39 +03:00
u64 _pcw ;
2019-03-05 08:05:40 +03:00
int ibits ;
2015-04-23 11:35:39 +03:00
u32 val ;
if ( freq > pll - > data - > fmax )
freq = pll - > data - > fmax ;
2015-07-10 11:39:34 +03:00
if ( div_table ) {
if ( freq > div_table [ 0 ] . freq )
freq = div_table [ 0 ] . freq ;
for ( val = 0 ; div_table [ val + 1 ] . freq ! = 0 ; val + + ) {
if ( freq > div_table [ val + 1 ] . freq )
break ;
}
2015-04-23 11:35:39 +03:00
* postdiv = 1 < < val ;
2015-07-10 11:39:34 +03:00
} else {
for ( val = 0 ; val < 5 ; val + + ) {
* postdiv = 1 < < val ;
if ( ( u64 ) freq * * postdiv > = fmin )
break ;
}
2015-04-23 11:35:39 +03:00
}
/* _pcw = freq * postdiv / fin * 2^pcwfbits */
2019-03-05 08:05:40 +03:00
ibits = pll - > data - > pcwibits ? pll - > data - > pcwibits : INTEGER_BITS ;
_pcw = ( ( u64 ) freq < < val ) < < ( pll - > data - > pcwbits - ibits ) ;
2015-04-23 11:35:39 +03:00
do_div ( _pcw , fin ) ;
* pcw = ( u32 ) _pcw ;
}
static int mtk_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
u32 pcw = 0 ;
u32 postdiv ;
mtk_pll_calc_values ( pll , & pcw , & postdiv , rate , parent_rate ) ;
mtk_pll_set_rate_regs ( pll , pcw , postdiv ) ;
return 0 ;
}
static unsigned long mtk_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
u32 postdiv ;
u32 pcw ;
postdiv = ( readl ( pll - > pd_addr ) > > pll - > data - > pd_shift ) & POSTDIV_MASK ;
postdiv = 1 < < postdiv ;
pcw = readl ( pll - > pcw_addr ) > > pll - > data - > pcw_shift ;
pcw & = GENMASK ( pll - > data - > pcwbits - 1 , 0 ) ;
return __mtk_pll_recalc_rate ( pll , parent_rate , pcw , postdiv ) ;
}
static long mtk_pll_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
u32 pcw = 0 ;
int postdiv ;
mtk_pll_calc_values ( pll , & pcw , & postdiv , rate , * prate ) ;
return __mtk_pll_recalc_rate ( pll , * prate , pcw , postdiv ) ;
}
static int mtk_pll_prepare ( struct clk_hw * hw )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
u32 r ;
2021-07-26 13:57:04 +03:00
u32 div_en_mask ;
2015-04-23 11:35:39 +03:00
r = readl ( pll - > pwr_addr ) | CON0_PWR_ON ;
writel ( r , pll - > pwr_addr ) ;
udelay ( 1 ) ;
r = readl ( pll - > pwr_addr ) & ~ CON0_ISO_EN ;
writel ( r , pll - > pwr_addr ) ;
udelay ( 1 ) ;
2021-07-26 13:57:05 +03:00
r = readl ( pll - > en_addr ) | BIT ( pll - > data - > pll_en_bit ) ;
writel ( r , pll - > en_addr ) ;
2015-04-23 11:35:39 +03:00
2021-07-26 13:57:04 +03:00
div_en_mask = pll - > data - > en_mask & ~ CON0_BASE_EN ;
if ( div_en_mask ) {
r = readl ( pll - > base_addr + REG_CON0 ) | div_en_mask ;
writel ( r , pll - > base_addr + REG_CON0 ) ;
}
2019-03-05 08:05:38 +03:00
__mtk_pll_tuner_enable ( pll ) ;
2015-04-23 11:35:39 +03:00
udelay ( 20 ) ;
if ( pll - > data - > flags & HAVE_RST_BAR ) {
r = readl ( pll - > base_addr + REG_CON0 ) ;
r | = pll - > data - > rst_bar_mask ;
writel ( r , pll - > base_addr + REG_CON0 ) ;
}
return 0 ;
}
static void mtk_pll_unprepare ( struct clk_hw * hw )
{
struct mtk_clk_pll * pll = to_mtk_clk_pll ( hw ) ;
u32 r ;
2021-07-26 13:57:04 +03:00
u32 div_en_mask ;
2015-04-23 11:35:39 +03:00
if ( pll - > data - > flags & HAVE_RST_BAR ) {
r = readl ( pll - > base_addr + REG_CON0 ) ;
r & = ~ pll - > data - > rst_bar_mask ;
writel ( r , pll - > base_addr + REG_CON0 ) ;
}
2019-03-05 08:05:38 +03:00
__mtk_pll_tuner_disable ( pll ) ;
2015-04-23 11:35:39 +03:00
2021-07-26 13:57:04 +03:00
div_en_mask = pll - > data - > en_mask & ~ CON0_BASE_EN ;
if ( div_en_mask ) {
r = readl ( pll - > base_addr + REG_CON0 ) & ~ div_en_mask ;
writel ( r , pll - > base_addr + REG_CON0 ) ;
}
2021-07-26 13:57:05 +03:00
r = readl ( pll - > en_addr ) & ~ BIT ( pll - > data - > pll_en_bit ) ;
writel ( r , pll - > en_addr ) ;
2015-04-23 11:35:39 +03:00
r = readl ( pll - > pwr_addr ) | CON0_ISO_EN ;
writel ( r , pll - > pwr_addr ) ;
r = readl ( pll - > pwr_addr ) & ~ CON0_PWR_ON ;
writel ( r , pll - > pwr_addr ) ;
}
static const struct clk_ops mtk_pll_ops = {
. is_prepared = mtk_pll_is_prepared ,
. prepare = mtk_pll_prepare ,
. unprepare = mtk_pll_unprepare ,
. recalc_rate = mtk_pll_recalc_rate ,
. round_rate = mtk_pll_round_rate ,
. set_rate = mtk_pll_set_rate ,
} ;
static struct clk * mtk_clk_register_pll ( const struct mtk_pll_data * data ,
void __iomem * base )
{
struct mtk_clk_pll * pll ;
2015-05-18 17:00:26 +03:00
struct clk_init_data init = { } ;
2015-04-23 11:35:39 +03:00
struct clk * clk ;
const char * parent_name = " clk26m " ;
pll = kzalloc ( sizeof ( * pll ) , GFP_KERNEL ) ;
if ( ! pll )
return ERR_PTR ( - ENOMEM ) ;
pll - > base_addr = base + data - > reg ;
pll - > pwr_addr = base + data - > pwr_reg ;
pll - > pd_addr = base + data - > pd_reg ;
pll - > pcw_addr = base + data - > pcw_reg ;
2019-03-05 08:05:44 +03:00
if ( data - > pcw_chg_reg )
pll - > pcw_chg_addr = base + data - > pcw_chg_reg ;
else
pll - > pcw_chg_addr = pll - > base_addr + REG_CON1 ;
2015-04-23 11:35:39 +03:00
if ( data - > tuner_reg )
pll - > tuner_addr = base + data - > tuner_reg ;
2021-09-14 05:16:12 +03:00
if ( data - > tuner_en_reg | | data - > tuner_en_bit )
2017-10-23 07:10:34 +03:00
pll - > tuner_en_addr = base + data - > tuner_en_reg ;
2021-07-26 13:57:05 +03:00
if ( data - > en_reg )
pll - > en_addr = base + data - > en_reg ;
else
pll - > en_addr = pll - > base_addr + REG_CON0 ;
2015-04-23 11:35:39 +03:00
pll - > hw . init = & init ;
pll - > data = data ;
init . name = data - > name ;
2016-11-04 10:43:05 +03:00
init . flags = ( data - > flags & PLL_AO ) ? CLK_IS_CRITICAL : 0 ;
2015-04-23 11:35:39 +03:00
init . ops = & mtk_pll_ops ;
2017-10-05 06:50:23 +03:00
if ( data - > parent_name )
init . parent_names = & data - > parent_name ;
else
init . parent_names = & parent_name ;
2015-04-23 11:35:39 +03:00
init . num_parents = 1 ;
clk = clk_register ( NULL , & pll - > hw ) ;
if ( IS_ERR ( clk ) )
kfree ( pll ) ;
return clk ;
}
2016-08-16 10:30:21 +03:00
void mtk_clk_register_plls ( struct device_node * node ,
2015-04-23 11:35:39 +03:00
const struct mtk_pll_data * plls , int num_plls , struct clk_onecell_data * clk_data )
{
void __iomem * base ;
2015-05-20 10:59:21 +03:00
int i ;
2015-04-23 11:35:39 +03:00
struct clk * clk ;
base = of_iomap ( node , 0 ) ;
if ( ! base ) {
pr_err ( " %s(): ioremap failed \n " , __func__ ) ;
return ;
}
for ( i = 0 ; i < num_plls ; i + + ) {
const struct mtk_pll_data * pll = & plls [ i ] ;
clk = mtk_clk_register_pll ( pll , base ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " Failed to register clk %s: %ld \n " ,
pll - > name , PTR_ERR ( clk ) ) ;
continue ;
}
clk_data - > clks [ pll - > id ] = clk ;
}
}