2021-10-25 06:10:37 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* Toshiba Visconti PLL driver
*
* Copyright ( c ) 2021 TOSHIBA CORPORATION
* Copyright ( c ) 2021 Toshiba Electronic Devices & Storage Corporation
*
* Nobuhiro Iwamatsu < nobuhiro1 . iwamatsu @ toshiba . co . jp >
*/
# include <linux/bitfield.h>
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/slab.h>
# include <linux/io.h>
# include "pll.h"
struct visconti_pll {
struct clk_hw hw ;
void __iomem * pll_base ;
spinlock_t * lock ;
unsigned long flags ;
const struct visconti_pll_rate_table * rate_table ;
size_t rate_count ;
struct visconti_pll_provider * ctx ;
} ;
# define PLL_CONF_REG 0x0000
# define PLL_CTRL_REG 0x0004
# define PLL_FRACMODE_REG 0x0010
# define PLL_INTIN_REG 0x0014
# define PLL_FRACIN_REG 0x0018
# define PLL_REFDIV_REG 0x001c
# define PLL_POSTDIV_REG 0x0020
# define PLL_CONFIG_SEL BIT(0)
# define PLL_PLLEN BIT(4)
# define PLL_BYPASS BIT(16)
# define PLL_INTIN_MASK GENMASK(11, 0)
# define PLL_FRACIN_MASK GENMASK(23, 0)
# define PLL_REFDIV_MASK GENMASK(5, 0)
# define PLL_POSTDIV_MASK GENMASK(2, 0)
# define PLL0_FRACMODE_DACEN BIT(4)
# define PLL0_FRACMODE_DSMEN BIT(0)
# define PLL_CREATE_FRACMODE(table) (table->dacen << 4 | table->dsmen)
# define PLL_CREATE_OSTDIV(table) (table->postdiv2 << 4 | table->postdiv1)
static inline struct visconti_pll * to_visconti_pll ( struct clk_hw * hw )
{
return container_of ( hw , struct visconti_pll , hw ) ;
}
static void visconti_pll_get_params ( struct visconti_pll * pll ,
struct visconti_pll_rate_table * rate_table )
{
u32 postdiv , val ;
val = readl ( pll - > pll_base + PLL_FRACMODE_REG ) ;
rate_table - > dacen = FIELD_GET ( PLL0_FRACMODE_DACEN , val ) ;
rate_table - > dsmen = FIELD_GET ( PLL0_FRACMODE_DSMEN , val ) ;
rate_table - > fracin = readl ( pll - > pll_base + PLL_FRACIN_REG ) & PLL_FRACIN_MASK ;
rate_table - > intin = readl ( pll - > pll_base + PLL_INTIN_REG ) & PLL_INTIN_MASK ;
rate_table - > refdiv = readl ( pll - > pll_base + PLL_REFDIV_REG ) & PLL_REFDIV_MASK ;
postdiv = readl ( pll - > pll_base + PLL_POSTDIV_REG ) ;
rate_table - > postdiv1 = postdiv & PLL_POSTDIV_MASK ;
rate_table - > postdiv2 = ( postdiv > > 4 ) & PLL_POSTDIV_MASK ;
}
static const struct visconti_pll_rate_table * visconti_get_pll_settings ( struct visconti_pll * pll ,
unsigned long rate )
{
const struct visconti_pll_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 unsigned long visconti_get_pll_rate_from_data ( struct visconti_pll * pll ,
const struct visconti_pll_rate_table * rate )
{
const struct visconti_pll_rate_table * rate_table = pll - > rate_table ;
int i ;
for ( i = 0 ; i < pll - > rate_count ; i + + )
if ( memcmp ( & rate_table [ i ] . dacen , & rate - > dacen ,
sizeof ( * rate ) - sizeof ( unsigned long ) ) = = 0 )
return rate_table [ i ] . rate ;
/* set default */
return rate_table [ 0 ] . rate ;
}
static long visconti_pll_round_rate ( struct clk_hw * hw ,
unsigned long rate , unsigned long * prate )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
const struct visconti_pll_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 visconti_pll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
struct visconti_pll_rate_table rate_table ;
memset ( & rate_table , 0 , sizeof ( rate_table ) ) ;
visconti_pll_get_params ( pll , & rate_table ) ;
return visconti_get_pll_rate_from_data ( pll , & rate_table ) ;
}
static int visconti_pll_set_params ( struct visconti_pll * pll ,
const struct visconti_pll_rate_table * rate_table )
{
writel ( PLL_CREATE_FRACMODE ( rate_table ) , pll - > pll_base + PLL_FRACMODE_REG ) ;
writel ( PLL_CREATE_OSTDIV ( rate_table ) , pll - > pll_base + PLL_POSTDIV_REG ) ;
writel ( rate_table - > intin , pll - > pll_base + PLL_INTIN_REG ) ;
writel ( rate_table - > fracin , pll - > pll_base + PLL_FRACIN_REG ) ;
writel ( rate_table - > refdiv , pll - > pll_base + PLL_REFDIV_REG ) ;
return 0 ;
}
static int visconti_pll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
const struct visconti_pll_rate_table * rate_table ;
rate_table = visconti_get_pll_settings ( pll , rate ) ;
if ( ! rate_table )
return - EINVAL ;
return visconti_pll_set_params ( pll , rate_table ) ;
}
static int visconti_pll_is_enabled ( struct clk_hw * hw )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
u32 reg ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
return ( reg & PLL_PLLEN ) ;
}
static int visconti_pll_enable ( struct clk_hw * hw )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
const struct visconti_pll_rate_table * rate_table = pll - > rate_table ;
unsigned long flags ;
u32 reg ;
if ( visconti_pll_is_enabled ( hw ) )
return 0 ;
spin_lock_irqsave ( pll - > lock , flags ) ;
writel ( PLL_CONFIG_SEL , pll - > pll_base + PLL_CONF_REG ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg | = PLL_BYPASS ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
visconti_pll_set_params ( pll , & rate_table [ 0 ] ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg & = ~ PLL_PLLEN ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
udelay ( 1 ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg | = PLL_PLLEN ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
udelay ( 40 ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg & = ~ PLL_BYPASS ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
spin_unlock_irqrestore ( pll - > lock , flags ) ;
return 0 ;
}
static void visconti_pll_disable ( struct clk_hw * hw )
{
struct visconti_pll * pll = to_visconti_pll ( hw ) ;
unsigned long flags ;
u32 reg ;
if ( ! visconti_pll_is_enabled ( hw ) )
return ;
spin_lock_irqsave ( pll - > lock , flags ) ;
writel ( PLL_CONFIG_SEL , pll - > pll_base + PLL_CONF_REG ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg | = PLL_BYPASS ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
reg = readl ( pll - > pll_base + PLL_CTRL_REG ) ;
reg & = ~ PLL_PLLEN ;
writel ( reg , pll - > pll_base + PLL_CTRL_REG ) ;
spin_unlock_irqrestore ( pll - > lock , flags ) ;
}
static const struct clk_ops visconti_pll_ops = {
. enable = visconti_pll_enable ,
. disable = visconti_pll_disable ,
. is_enabled = visconti_pll_is_enabled ,
. round_rate = visconti_pll_round_rate ,
. recalc_rate = visconti_pll_recalc_rate ,
. set_rate = visconti_pll_set_rate ,
} ;
static struct clk_hw * visconti_register_pll ( struct visconti_pll_provider * ctx ,
const char * name ,
const char * parent_name ,
int offset ,
const struct visconti_pll_rate_table * rate_table ,
spinlock_t * lock )
{
struct clk_init_data init ;
struct visconti_pll * pll ;
struct clk_hw * pll_hw_clk ;
size_t len ;
int ret ;
pll = kzalloc ( sizeof ( * pll ) , GFP_KERNEL ) ;
if ( ! pll )
return ERR_PTR ( - ENOMEM ) ;
init . name = name ;
init . flags = CLK_IGNORE_UNUSED ;
init . parent_names = & parent_name ;
init . num_parents = 1 ;
for ( len = 0 ; rate_table [ len ] . rate ! = 0 ; )
len + + ;
pll - > rate_count = len ;
pll - > rate_table = kmemdup ( rate_table ,
pll - > rate_count * sizeof ( struct visconti_pll_rate_table ) ,
GFP_KERNEL ) ;
WARN ( ! pll - > rate_table , " %s: could not allocate rate table for %s \n " , __func__ , name ) ;
init . ops = & visconti_pll_ops ;
pll - > hw . init = & init ;
pll - > pll_base = ctx - > reg_base + offset ;
pll - > lock = lock ;
pll - > ctx = ctx ;
pll_hw_clk = & pll - > hw ;
ret = clk_hw_register ( NULL , & pll - > hw ) ;
if ( ret ) {
2022-01-11 10:25:29 +03:00
pr_err ( " failed to register pll clock %s : %d \n " , name , ret ) ;
2022-11-22 18:23:53 +03:00
kfree ( pll - > rate_table ) ;
2021-10-25 06:10:37 +03:00
kfree ( pll ) ;
pll_hw_clk = ERR_PTR ( ret ) ;
}
return pll_hw_clk ;
}
static void visconti_pll_add_lookup ( struct visconti_pll_provider * ctx ,
struct clk_hw * hw_clk ,
unsigned int id )
{
2022-01-07 21:33:03 +03:00
if ( id )
2021-10-25 06:10:37 +03:00
ctx - > clk_data . hws [ id ] = hw_clk ;
}
void __init visconti_register_plls ( struct visconti_pll_provider * ctx ,
const struct visconti_pll_info * list ,
unsigned int nr_plls ,
spinlock_t * lock )
{
int idx ;
for ( idx = 0 ; idx < nr_plls ; idx + + , list + + ) {
struct clk_hw * clk ;
clk = visconti_register_pll ( ctx ,
list - > name ,
list - > parent ,
list - > base_reg ,
list - > rate_table ,
lock ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " failed to register clock %s \n " , list - > name ) ;
continue ;
}
visconti_pll_add_lookup ( ctx , clk , list - > id ) ;
}
}
struct visconti_pll_provider * __init visconti_init_pll ( struct device_node * np ,
void __iomem * base ,
unsigned long nr_plls )
{
struct visconti_pll_provider * ctx ;
int i ;
ctx = kzalloc ( struct_size ( ctx , clk_data . hws , nr_plls ) , GFP_KERNEL ) ;
if ( ! ctx )
return ERR_PTR ( - ENOMEM ) ;
for ( i = 0 ; i < nr_plls ; + + i )
ctx - > clk_data . hws [ i ] = ERR_PTR ( - ENOENT ) ;
ctx - > node = np ;
ctx - > reg_base = base ;
ctx - > clk_data . num = nr_plls ;
return ctx ;
}