2019-01-22 09:31:41 +00:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2017 - 2018 NXP .
*/
2020-08-05 07:17:29 +08:00
# include <linux/bits.h>
2019-01-22 09:31:41 +00:00
# include <linux/clk-provider.h>
# include <linux/err.h>
2020-07-30 09:22:51 +08:00
# include <linux/export.h>
2019-01-22 09:31:41 +00:00
# include <linux/io.h>
# include <linux/iopoll.h>
# include <linux/slab.h>
# include <linux/jiffies.h>
# include "clk.h"
# define GNRL_CTL 0x0
# define DIV_CTL 0x4
# define LOCK_STATUS BIT(31)
# define LOCK_SEL_MASK BIT(29)
# define CLKE_MASK BIT(11)
# define RST_MASK BIT(9)
# define BYPASS_MASK BIT(4)
# define MDIV_SHIFT 12
# define MDIV_MASK GENMASK(21, 12)
# define PDIV_SHIFT 4
# define PDIV_MASK GENMASK(9, 4)
# define SDIV_SHIFT 0
# define SDIV_MASK GENMASK(2, 0)
# define KDIV_SHIFT 0
# define KDIV_MASK GENMASK(15, 0)
# define LOCK_TIMEOUT_US 10000
struct clk_pll14xx {
struct clk_hw hw ;
void __iomem * base ;
enum imx_pll14xx_type type ;
const struct imx_pll14xx_rate_table * rate_table ;
int rate_count ;
} ;
# define to_clk_pll14xx(_hw) container_of(_hw, struct clk_pll14xx, hw)
2019-10-08 15:19:08 +08:00
static const struct imx_pll14xx_rate_table imx_pll1416x_tbl [ ] = {
2019-09-06 09:34:05 -04:00
PLL_1416X_RATE ( 1800000000U , 225 , 3 , 0 ) ,
PLL_1416X_RATE ( 1600000000U , 200 , 3 , 0 ) ,
2019-09-06 09:34:06 -04:00
PLL_1416X_RATE ( 1500000000U , 375 , 3 , 1 ) ,
PLL_1416X_RATE ( 1400000000U , 350 , 3 , 1 ) ,
2019-09-06 09:34:05 -04:00
PLL_1416X_RATE ( 1200000000U , 300 , 3 , 1 ) ,
PLL_1416X_RATE ( 1000000000U , 250 , 3 , 1 ) ,
PLL_1416X_RATE ( 800000000U , 200 , 3 , 1 ) ,
PLL_1416X_RATE ( 750000000U , 250 , 2 , 2 ) ,
PLL_1416X_RATE ( 700000000U , 350 , 3 , 2 ) ,
PLL_1416X_RATE ( 600000000U , 300 , 3 , 2 ) ,
} ;
2019-10-08 15:19:08 +08:00
static const struct imx_pll14xx_rate_table imx_pll1443x_tbl [ ] = {
2020-01-16 14:50:49 +08:00
PLL_1443X_RATE ( 1039500000U , 173 , 2 , 1 , 16384 ) ,
2019-09-06 09:34:05 -04:00
PLL_1443X_RATE ( 650000000U , 325 , 3 , 2 , 0 ) ,
PLL_1443X_RATE ( 594000000U , 198 , 2 , 2 , 0 ) ,
2020-01-16 14:50:49 +08:00
PLL_1443X_RATE ( 519750000U , 173 , 2 , 2 , 16384 ) ,
2019-09-06 09:34:05 -04:00
PLL_1443X_RATE ( 393216000U , 262 , 2 , 3 , 9437 ) ,
PLL_1443X_RATE ( 361267200U , 361 , 3 , 3 , 17511 ) ,
} ;
struct imx_pll14xx_clk imx_1443x_pll = {
. type = PLL_1443X ,
. rate_table = imx_pll1443x_tbl ,
. rate_count = ARRAY_SIZE ( imx_pll1443x_tbl ) ,
} ;
2020-07-30 09:22:51 +08:00
EXPORT_SYMBOL_GPL ( imx_1443x_pll ) ;
2019-09-06 09:34:05 -04:00
2019-11-22 23:45:01 +02:00
struct imx_pll14xx_clk imx_1443x_dram_pll = {
. type = PLL_1443X ,
. rate_table = imx_pll1443x_tbl ,
. rate_count = ARRAY_SIZE ( imx_pll1443x_tbl ) ,
. flags = CLK_GET_RATE_NOCACHE ,
} ;
2020-07-30 09:22:51 +08:00
EXPORT_SYMBOL_GPL ( imx_1443x_dram_pll ) ;
2019-11-22 23:45:01 +02:00
2019-09-06 09:34:05 -04:00
struct imx_pll14xx_clk imx_1416x_pll = {
. type = PLL_1416X ,
. rate_table = imx_pll1416x_tbl ,
. rate_count = ARRAY_SIZE ( imx_pll1416x_tbl ) ,
} ;
2020-07-30 09:22:51 +08:00
EXPORT_SYMBOL_GPL ( imx_1416x_pll ) ;
2019-09-06 09:34:05 -04:00
2019-01-22 09:31:41 +00:00
static const struct imx_pll14xx_rate_table * imx_get_pll_settings (
struct clk_pll14xx * pll , unsigned long rate )
{
const struct imx_pll14xx_rate_table * rate_table = pll - > rate_table ;
int i ;
for ( i = 0 ; i < pll - > rate_count ; i + + )
if ( rate = = rate_table [ i ] . rate )
return & rate_table [ i ] ;
return NULL ;
}
static long clk_pll14xx_round_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long * prate )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
const struct imx_pll14xx_rate_table * rate_table = pll - > rate_table ;
int i ;
/* Assumming rate_table is in descending order */
for ( i = 0 ; i < pll - > rate_count ; i + + )
if ( rate > = rate_table [ i ] . rate )
return rate_table [ i ] . rate ;
/* return minimum supported value */
return rate_table [ i - 1 ] . rate ;
}
static unsigned long clk_pll1416x_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
2019-04-25 10:14:28 +00:00
u32 mdiv , pdiv , sdiv , pll_div ;
2019-01-22 09:31:41 +00:00
u64 fvco = parent_rate ;
pll_div = readl_relaxed ( pll - > base + 4 ) ;
mdiv = ( pll_div & MDIV_MASK ) > > MDIV_SHIFT ;
pdiv = ( pll_div & PDIV_MASK ) > > PDIV_SHIFT ;
sdiv = ( pll_div & SDIV_MASK ) > > SDIV_SHIFT ;
fvco * = mdiv ;
do_div ( fvco , pdiv < < sdiv ) ;
return fvco ;
}
static unsigned long clk_pll1443x_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
2019-04-25 10:14:28 +00:00
u32 mdiv , pdiv , sdiv , pll_div_ctl0 , pll_div_ctl1 ;
2019-01-22 09:31:41 +00:00
short int kdiv ;
u64 fvco = parent_rate ;
pll_div_ctl0 = readl_relaxed ( pll - > base + 4 ) ;
pll_div_ctl1 = readl_relaxed ( pll - > base + 8 ) ;
mdiv = ( pll_div_ctl0 & MDIV_MASK ) > > MDIV_SHIFT ;
pdiv = ( pll_div_ctl0 & PDIV_MASK ) > > PDIV_SHIFT ;
sdiv = ( pll_div_ctl0 & SDIV_MASK ) > > SDIV_SHIFT ;
kdiv = pll_div_ctl1 & KDIV_MASK ;
/* fvco = (m * 65536 + k) * Fin / (p * 65536) */
fvco * = ( mdiv * 65536 + kdiv ) ;
pdiv * = 65536 ;
do_div ( fvco , pdiv < < sdiv ) ;
return fvco ;
}
2019-09-04 12:49:18 +03:00
static inline bool clk_pll14xx_mp_change ( const struct imx_pll14xx_rate_table * rate ,
2019-01-22 09:31:41 +00:00
u32 pll_div )
{
u32 old_mdiv , old_pdiv ;
2019-09-04 12:49:18 +03:00
old_mdiv = ( pll_div & MDIV_MASK ) > > MDIV_SHIFT ;
old_pdiv = ( pll_div & PDIV_MASK ) > > PDIV_SHIFT ;
2019-01-22 09:31:41 +00:00
return rate - > mdiv ! = old_mdiv | | rate - > pdiv ! = old_pdiv ;
}
static int clk_pll14xx_wait_lock ( struct clk_pll14xx * pll )
{
u32 val ;
2019-12-09 08:19:55 +00:00
return readl_poll_timeout ( pll - > base , val , val & LOCK_STATUS , 0 ,
2019-01-22 09:31:41 +00:00
LOCK_TIMEOUT_US ) ;
}
static int clk_pll1416x_set_rate ( struct clk_hw * hw , unsigned long drate ,
unsigned long prate )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
const struct imx_pll14xx_rate_table * rate ;
u32 tmp , div_val ;
int ret ;
rate = imx_get_pll_settings ( pll , drate ) ;
if ( ! rate ) {
pr_err ( " %s: Invalid rate : %lu for pll clk %s \n " , __func__ ,
drate , clk_hw_get_name ( hw ) ) ;
return - EINVAL ;
}
tmp = readl_relaxed ( pll - > base + 4 ) ;
2019-09-04 12:49:18 +03:00
if ( ! clk_pll14xx_mp_change ( rate , tmp ) ) {
2019-01-22 09:31:41 +00:00
tmp & = ~ ( SDIV_MASK ) < < SDIV_SHIFT ;
tmp | = rate - > sdiv < < SDIV_SHIFT ;
writel_relaxed ( tmp , pll - > base + 4 ) ;
return 0 ;
}
/* Bypass clock and set lock to pll output lock */
tmp = readl_relaxed ( pll - > base ) ;
tmp | = LOCK_SEL_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
/* Enable RST */
tmp & = ~ RST_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
2019-09-09 03:39:34 +00:00
/* Enable BYPASS */
tmp | = BYPASS_MASK ;
writel ( tmp , pll - > base ) ;
2019-01-22 09:31:41 +00:00
div_val = ( rate - > mdiv < < MDIV_SHIFT ) | ( rate - > pdiv < < PDIV_SHIFT ) |
( rate - > sdiv < < SDIV_SHIFT ) ;
writel_relaxed ( div_val , pll - > base + 0x4 ) ;
/*
* According to SPEC , t3 - t2 need to be greater than
* 1u s and 1 / FREF , respectively .
* FREF is FIN / Prediv , the prediv is [ 1 , 63 ] , so choose
* 3u s .
*/
udelay ( 3 ) ;
/* Disable RST */
tmp | = RST_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
/* Wait Lock */
ret = clk_pll14xx_wait_lock ( pll ) ;
if ( ret )
return ret ;
/* Bypass */
tmp & = ~ BYPASS_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
return 0 ;
}
static int clk_pll1443x_set_rate ( struct clk_hw * hw , unsigned long drate ,
unsigned long prate )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
const struct imx_pll14xx_rate_table * rate ;
u32 tmp , div_val ;
int ret ;
rate = imx_get_pll_settings ( pll , drate ) ;
if ( ! rate ) {
pr_err ( " %s: Invalid rate : %lu for pll clk %s \n " , __func__ ,
drate , clk_hw_get_name ( hw ) ) ;
return - EINVAL ;
}
tmp = readl_relaxed ( pll - > base + 4 ) ;
2019-09-04 12:49:18 +03:00
if ( ! clk_pll14xx_mp_change ( rate , tmp ) ) {
2019-01-22 09:31:41 +00:00
tmp & = ~ ( SDIV_MASK ) < < SDIV_SHIFT ;
tmp | = rate - > sdiv < < SDIV_SHIFT ;
writel_relaxed ( tmp , pll - > base + 4 ) ;
2019-09-04 12:49:18 +03:00
tmp = rate - > kdiv < < KDIV_SHIFT ;
writel_relaxed ( tmp , pll - > base + 8 ) ;
2019-01-22 09:31:41 +00:00
return 0 ;
}
/* Enable RST */
tmp = readl_relaxed ( pll - > base ) ;
tmp & = ~ RST_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
2019-09-09 03:39:34 +00:00
/* Enable BYPASS */
tmp | = BYPASS_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
2019-01-22 09:31:41 +00:00
div_val = ( rate - > mdiv < < MDIV_SHIFT ) | ( rate - > pdiv < < PDIV_SHIFT ) |
( rate - > sdiv < < SDIV_SHIFT ) ;
writel_relaxed ( div_val , pll - > base + 0x4 ) ;
writel_relaxed ( rate - > kdiv < < KDIV_SHIFT , pll - > base + 0x8 ) ;
/*
* According to SPEC , t3 - t2 need to be greater than
* 1u s and 1 / FREF , respectively .
* FREF is FIN / Prediv , the prediv is [ 1 , 63 ] , so choose
* 3u s .
*/
udelay ( 3 ) ;
/* Disable RST */
tmp | = RST_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
/* Wait Lock*/
ret = clk_pll14xx_wait_lock ( pll ) ;
if ( ret )
return ret ;
/* Bypass */
tmp & = ~ BYPASS_MASK ;
writel_relaxed ( tmp , pll - > base ) ;
return 0 ;
}
static int clk_pll14xx_prepare ( struct clk_hw * hw )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
u32 val ;
2019-09-09 03:39:34 +00:00
int ret ;
2019-01-22 09:31:41 +00:00
/*
* RESETB = 1 from 0 , PLL starts its normal
* operation after lock time
*/
val = readl_relaxed ( pll - > base + GNRL_CTL ) ;
2019-09-09 03:39:34 +00:00
if ( val & RST_MASK )
return 0 ;
val | = BYPASS_MASK ;
writel_relaxed ( val , pll - > base + GNRL_CTL ) ;
2019-01-22 09:31:41 +00:00
val | = RST_MASK ;
writel_relaxed ( val , pll - > base + GNRL_CTL ) ;
2019-09-09 03:39:34 +00:00
ret = clk_pll14xx_wait_lock ( pll ) ;
if ( ret )
return ret ;
val & = ~ BYPASS_MASK ;
writel_relaxed ( val , pll - > base + GNRL_CTL ) ;
return 0 ;
2019-01-22 09:31:41 +00:00
}
static int clk_pll14xx_is_prepared ( struct clk_hw * hw )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
u32 val ;
val = readl_relaxed ( pll - > base + GNRL_CTL ) ;
return ( val & RST_MASK ) ? 1 : 0 ;
}
static void clk_pll14xx_unprepare ( struct clk_hw * hw )
{
struct clk_pll14xx * pll = to_clk_pll14xx ( hw ) ;
u32 val ;
/*
* Set RST to 0 , power down mode is enabled and
* every digital block is reset
*/
val = readl_relaxed ( pll - > base + GNRL_CTL ) ;
val & = ~ RST_MASK ;
writel_relaxed ( val , pll - > base + GNRL_CTL ) ;
}
static const struct clk_ops clk_pll1416x_ops = {
. prepare = clk_pll14xx_prepare ,
. unprepare = clk_pll14xx_unprepare ,
. is_prepared = clk_pll14xx_is_prepared ,
. recalc_rate = clk_pll1416x_recalc_rate ,
. round_rate = clk_pll14xx_round_rate ,
. set_rate = clk_pll1416x_set_rate ,
} ;
static const struct clk_ops clk_pll1416x_min_ops = {
. recalc_rate = clk_pll1416x_recalc_rate ,
} ;
static const struct clk_ops clk_pll1443x_ops = {
. prepare = clk_pll14xx_prepare ,
. unprepare = clk_pll14xx_unprepare ,
. is_prepared = clk_pll14xx_is_prepared ,
. recalc_rate = clk_pll1443x_recalc_rate ,
. round_rate = clk_pll14xx_round_rate ,
. set_rate = clk_pll1443x_set_rate ,
} ;
2020-04-15 11:02:46 +03:00
struct clk_hw * imx_dev_clk_hw_pll14xx ( struct device * dev , const char * name ,
const char * parent_name , void __iomem * base ,
const struct imx_pll14xx_clk * pll_clk )
2019-01-22 09:31:41 +00:00
{
struct clk_pll14xx * pll ;
2019-12-12 02:58:42 +00:00
struct clk_hw * hw ;
2019-01-22 09:31:41 +00:00
struct clk_init_data init ;
2019-12-12 02:58:42 +00:00
int ret ;
2019-09-09 03:39:39 +00:00
u32 val ;
2019-01-22 09:31:41 +00:00
pll = kzalloc ( sizeof ( * pll ) , GFP_KERNEL ) ;
if ( ! pll )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . flags = pll_clk - > flags ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
switch ( pll_clk - > type ) {
case PLL_1416X :
2019-04-12 14:10:03 +00:00
if ( ! pll_clk - > rate_table )
2019-01-22 09:31:41 +00:00
init . ops = & clk_pll1416x_min_ops ;
else
init . ops = & clk_pll1416x_ops ;
break ;
case PLL_1443X :
init . ops = & clk_pll1443x_ops ;
break ;
default :
pr_err ( " %s: Unknown pll type for pll clk %s \n " ,
__func__ , name ) ;
2020-02-21 14:31:56 +08:00
kfree ( pll ) ;
return ERR_PTR ( - EINVAL ) ;
2020-10-27 11:57:56 -07:00
}
2019-01-22 09:31:41 +00:00
pll - > base = base ;
pll - > hw . init = & init ;
pll - > type = pll_clk - > type ;
pll - > rate_table = pll_clk - > rate_table ;
pll - > rate_count = pll_clk - > rate_count ;
2019-09-09 03:39:39 +00:00
val = readl_relaxed ( pll - > base + GNRL_CTL ) ;
val & = ~ BYPASS_MASK ;
writel_relaxed ( val , pll - > base + GNRL_CTL ) ;
2019-12-12 02:58:42 +00:00
hw = & pll - > hw ;
2020-04-15 11:02:46 +03:00
ret = clk_hw_register ( dev , hw ) ;
2019-12-12 02:58:42 +00:00
if ( ret ) {
pr_err ( " %s: failed to register pll %s %d \n " ,
__func__ , name , ret ) ;
2019-01-22 09:31:41 +00:00
kfree ( pll ) ;
2019-12-12 02:58:42 +00:00
return ERR_PTR ( ret ) ;
2019-01-22 09:31:41 +00:00
}
2019-12-12 02:58:42 +00:00
return hw ;
2019-01-22 09:31:41 +00:00
}
2020-07-30 09:22:51 +08:00
EXPORT_SYMBOL_GPL ( imx_dev_clk_hw_pll14xx ) ;