2014-01-15 22:47:25 +04:00
/*
* Copyright ( c ) 2013 , The Linux Foundation . All rights reserved .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/kernel.h>
# include <linux/bitops.h>
# include <linux/err.h>
# include <linux/bug.h>
# include <linux/export.h>
# include <linux/clk-provider.h>
# include <linux/delay.h>
# include <linux/regmap.h>
2014-05-17 03:07:11 +04:00
# include <linux/math64.h>
2014-01-15 22:47:25 +04:00
# include <asm/div64.h>
# include "clk-rcg.h"
2014-09-05 00:21:50 +04:00
# include "common.h"
2014-01-15 22:47:25 +04:00
# define CMD_REG 0x0
# define CMD_UPDATE BIT(0)
# define CMD_ROOT_EN BIT(1)
# define CMD_DIRTY_CFG BIT(4)
# define CMD_DIRTY_N BIT(5)
# define CMD_DIRTY_M BIT(6)
# define CMD_DIRTY_D BIT(7)
# define CMD_ROOT_OFF BIT(31)
# define CFG_REG 0x4
# define CFG_SRC_DIV_SHIFT 0
# define CFG_SRC_SEL_SHIFT 8
# define CFG_SRC_SEL_MASK (0x7 << CFG_SRC_SEL_SHIFT)
# define CFG_MODE_SHIFT 12
# define CFG_MODE_MASK (0x3 << CFG_MODE_SHIFT)
# define CFG_MODE_DUAL_EDGE (0x2 << CFG_MODE_SHIFT)
# define M_REG 0x8
# define N_REG 0xc
# define D_REG 0x10
static int clk_rcg2_is_enabled ( struct clk_hw * hw )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
u32 cmd ;
int ret ;
ret = regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CMD_REG , & cmd ) ;
if ( ret )
return ret ;
2014-05-17 03:07:08 +04:00
return ( cmd & CMD_ROOT_OFF ) = = 0 ;
2014-01-15 22:47:25 +04:00
}
static u8 clk_rcg2_get_parent ( struct clk_hw * hw )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
int num_parents = __clk_get_num_parents ( hw - > clk ) ;
u32 cfg ;
int i , ret ;
ret = regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG , & cfg ) ;
if ( ret )
2015-03-20 19:30:24 +03:00
goto err ;
2014-01-15 22:47:25 +04:00
cfg & = CFG_SRC_SEL_MASK ;
cfg > > = CFG_SRC_SEL_SHIFT ;
for ( i = 0 ; i < num_parents ; i + + )
2015-03-20 19:30:26 +03:00
if ( cfg = = rcg - > parent_map [ i ] . cfg )
2014-01-15 22:47:25 +04:00
return i ;
2015-03-20 19:30:24 +03:00
err :
pr_debug ( " %s: Clock %s has invalid parent, using default. \n " ,
__func__ , __clk_get_name ( hw - > clk ) ) ;
return 0 ;
2014-01-15 22:47:25 +04:00
}
static int update_config ( struct clk_rcg2 * rcg )
{
int count , ret ;
u32 cmd ;
struct clk_hw * hw = & rcg - > clkr . hw ;
const char * name = __clk_get_name ( hw - > clk ) ;
ret = regmap_update_bits ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CMD_REG ,
CMD_UPDATE , CMD_UPDATE ) ;
if ( ret )
return ret ;
/* Wait for update to take effect */
for ( count = 500 ; count > 0 ; count - - ) {
ret = regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CMD_REG , & cmd ) ;
if ( ret )
return ret ;
if ( ! ( cmd & CMD_UPDATE ) )
return 0 ;
udelay ( 1 ) ;
}
WARN ( 1 , " %s: rcg didn't update its configuration. " , name ) ;
return 0 ;
}
static int clk_rcg2_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
int ret ;
2015-03-20 19:30:26 +03:00
u32 cfg = rcg - > parent_map [ index ] . cfg < < CFG_SRC_SEL_SHIFT ;
2014-01-15 22:47:25 +04:00
ret = regmap_update_bits ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG ,
2015-03-20 19:30:26 +03:00
CFG_SRC_SEL_MASK , cfg ) ;
2014-01-15 22:47:25 +04:00
if ( ret )
return ret ;
return update_config ( rcg ) ;
}
/*
* Calculate m / n : d rate
*
* parent_rate m
* rate = - - - - - - - - - - - x - - -
* hid_div n
*/
static unsigned long
calc_rate ( unsigned long rate , u32 m , u32 n , u32 mode , u32 hid_div )
{
if ( hid_div ) {
rate * = 2 ;
rate / = hid_div + 1 ;
}
if ( mode ) {
u64 tmp = rate ;
tmp * = m ;
do_div ( tmp , n ) ;
rate = tmp ;
}
return rate ;
}
static unsigned long
clk_rcg2_recalc_rate ( struct clk_hw * hw , unsigned long parent_rate )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
u32 cfg , hid_div , m = 0 , n = 0 , mode = 0 , mask ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG , & cfg ) ;
if ( rcg - > mnd_width ) {
mask = BIT ( rcg - > mnd_width ) - 1 ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + M_REG , & m ) ;
m & = mask ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + N_REG , & n ) ;
n = ~ n ;
n & = mask ;
n + = m ;
mode = cfg & CFG_MODE_MASK ;
mode > > = CFG_MODE_SHIFT ;
}
mask = BIT ( rcg - > hid_width ) - 1 ;
hid_div = cfg > > CFG_SRC_DIV_SHIFT ;
hid_div & = mask ;
return calc_rate ( parent_rate , m , n , mode , hid_div ) ;
}
static long _freq_tbl_determine_rate ( struct clk_hw * hw ,
const struct freq_tbl * f , unsigned long rate ,
2014-12-02 10:54:22 +03:00
unsigned long * p_rate , struct clk_hw * * p_hw )
2014-01-15 22:47:25 +04:00
{
unsigned long clk_flags ;
2014-12-02 10:54:22 +03:00
struct clk * p ;
2015-04-07 17:14:51 +03:00
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
int index ;
2014-01-15 22:47:25 +04:00
2014-09-05 00:21:50 +04:00
f = qcom_find_freq ( f , rate ) ;
2014-01-15 22:47:25 +04:00
if ( ! f )
return - EINVAL ;
2015-04-07 17:14:51 +03:00
index = qcom_find_src_index ( hw , rcg - > parent_map , f - > src ) ;
if ( index < 0 )
return index ;
2014-01-15 22:47:25 +04:00
clk_flags = __clk_get_flags ( hw - > clk ) ;
2015-04-07 17:14:51 +03:00
p = clk_get_parent_by_index ( hw - > clk , index ) ;
2014-01-15 22:47:25 +04:00
if ( clk_flags & CLK_SET_RATE_PARENT ) {
if ( f - > pre_div ) {
rate / = 2 ;
rate * = f - > pre_div + 1 ;
}
if ( f - > n ) {
u64 tmp = rate ;
tmp = tmp * f - > n ;
do_div ( tmp , f - > m ) ;
rate = tmp ;
}
} else {
2014-12-02 10:54:22 +03:00
rate = __clk_get_rate ( p ) ;
2014-01-15 22:47:25 +04:00
}
2014-12-02 10:54:22 +03:00
* p_hw = __clk_get_hw ( p ) ;
2014-01-15 22:47:25 +04:00
* p_rate = rate ;
return f - > freq ;
}
static long clk_rcg2_determine_rate ( struct clk_hw * hw , unsigned long rate ,
2015-01-23 14:03:31 +03:00
unsigned long min_rate , unsigned long max_rate ,
2014-12-02 10:54:22 +03:00
unsigned long * p_rate , struct clk_hw * * p )
2014-01-15 22:47:25 +04:00
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
return _freq_tbl_determine_rate ( hw , rcg - > freq_tbl , rate , p_rate , p ) ;
}
2014-05-17 03:07:11 +04:00
static int clk_rcg2_configure ( struct clk_rcg2 * rcg , const struct freq_tbl * f )
2014-01-15 22:47:25 +04:00
{
u32 cfg , mask ;
2015-03-20 19:30:26 +03:00
struct clk_hw * hw = & rcg - > clkr . hw ;
int ret , index = qcom_find_src_index ( hw , rcg - > parent_map , f - > src ) ;
if ( index < 0 )
return index ;
2014-01-15 22:47:25 +04:00
if ( rcg - > mnd_width & & f - > n ) {
mask = BIT ( rcg - > mnd_width ) - 1 ;
2014-05-17 03:07:11 +04:00
ret = regmap_update_bits ( rcg - > clkr . regmap ,
rcg - > cmd_rcgr + M_REG , mask , f - > m ) ;
2014-01-15 22:47:25 +04:00
if ( ret )
return ret ;
2014-05-17 03:07:11 +04:00
ret = regmap_update_bits ( rcg - > clkr . regmap ,
rcg - > cmd_rcgr + N_REG , mask , ~ ( f - > n - f - > m ) ) ;
2014-01-15 22:47:25 +04:00
if ( ret )
return ret ;
2014-05-17 03:07:11 +04:00
ret = regmap_update_bits ( rcg - > clkr . regmap ,
rcg - > cmd_rcgr + D_REG , mask , ~ f - > n ) ;
2014-01-15 22:47:25 +04:00
if ( ret )
return ret ;
}
mask = BIT ( rcg - > hid_width ) - 1 ;
mask | = CFG_SRC_SEL_MASK | CFG_MODE_MASK ;
cfg = f - > pre_div < < CFG_SRC_DIV_SHIFT ;
2015-03-20 19:30:26 +03:00
cfg | = rcg - > parent_map [ index ] . cfg < < CFG_SRC_SEL_SHIFT ;
2015-03-04 12:49:35 +03:00
if ( rcg - > mnd_width & & f - > n & & ( f - > m ! = f - > n ) )
2014-01-15 22:47:25 +04:00
cfg | = CFG_MODE_DUAL_EDGE ;
2014-05-17 03:07:11 +04:00
ret = regmap_update_bits ( rcg - > clkr . regmap ,
rcg - > cmd_rcgr + CFG_REG , mask , cfg ) ;
2014-01-15 22:47:25 +04:00
if ( ret )
return ret ;
return update_config ( rcg ) ;
}
2014-05-17 03:07:11 +04:00
static int __clk_rcg2_set_rate ( struct clk_hw * hw , unsigned long rate )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
const struct freq_tbl * f ;
2014-09-05 00:21:50 +04:00
f = qcom_find_freq ( rcg - > freq_tbl , rate ) ;
2014-05-17 03:07:11 +04:00
if ( ! f )
return - EINVAL ;
return clk_rcg2_configure ( rcg , f ) ;
}
2014-01-15 22:47:25 +04:00
static int clk_rcg2_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
return __clk_rcg2_set_rate ( hw , rate ) ;
}
static int clk_rcg2_set_rate_and_parent ( struct clk_hw * hw ,
unsigned long rate , unsigned long parent_rate , u8 index )
{
return __clk_rcg2_set_rate ( hw , rate ) ;
}
const struct clk_ops clk_rcg2_ops = {
. is_enabled = clk_rcg2_is_enabled ,
. get_parent = clk_rcg2_get_parent ,
. set_parent = clk_rcg2_set_parent ,
. recalc_rate = clk_rcg2_recalc_rate ,
. determine_rate = clk_rcg2_determine_rate ,
. set_rate = clk_rcg2_set_rate ,
. set_rate_and_parent = clk_rcg2_set_rate_and_parent ,
} ;
EXPORT_SYMBOL_GPL ( clk_rcg2_ops ) ;
2014-05-17 03:07:11 +04:00
struct frac_entry {
int num ;
int den ;
} ;
static const struct frac_entry frac_table_675m [ ] = { /* link rate of 270M */
{ 52 , 295 } , /* 119 M */
{ 11 , 57 } , /* 130.25 M */
{ 63 , 307 } , /* 138.50 M */
{ 11 , 50 } , /* 148.50 M */
{ 47 , 206 } , /* 154 M */
{ 31 , 100 } , /* 205.25 M */
{ 107 , 269 } , /* 268.50 M */
{ } ,
} ;
static struct frac_entry frac_table_810m [ ] = { /* Link rate of 162M */
{ 31 , 211 } , /* 119 M */
{ 32 , 199 } , /* 130.25 M */
{ 63 , 307 } , /* 138.50 M */
{ 11 , 60 } , /* 148.50 M */
{ 50 , 263 } , /* 154 M */
{ 31 , 120 } , /* 205.25 M */
{ 119 , 359 } , /* 268.50 M */
{ } ,
} ;
static int clk_edp_pixel_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
struct freq_tbl f = * rcg - > freq_tbl ;
const struct frac_entry * frac ;
int delta = 100000 ;
s64 src_rate = parent_rate ;
s64 request ;
u32 mask = BIT ( rcg - > hid_width ) - 1 ;
u32 hid_div ;
if ( src_rate = = 810000000 )
frac = frac_table_810m ;
else
frac = frac_table_675m ;
for ( ; frac - > num ; frac + + ) {
request = rate ;
request * = frac - > den ;
request = div_s64 ( request , frac - > num ) ;
if ( ( src_rate < ( request - delta ) ) | |
( src_rate > ( request + delta ) ) )
continue ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG ,
& hid_div ) ;
f . pre_div = hid_div ;
f . pre_div > > = CFG_SRC_DIV_SHIFT ;
f . pre_div & = mask ;
f . m = frac - > num ;
f . n = frac - > den ;
return clk_rcg2_configure ( rcg , & f ) ;
}
return - EINVAL ;
}
static int clk_edp_pixel_set_rate_and_parent ( struct clk_hw * hw ,
unsigned long rate , unsigned long parent_rate , u8 index )
{
/* Parent index is set statically in frequency table */
return clk_edp_pixel_set_rate ( hw , rate , parent_rate ) ;
}
static long clk_edp_pixel_determine_rate ( struct clk_hw * hw , unsigned long rate ,
2015-01-23 14:03:31 +03:00
unsigned long min_rate ,
unsigned long max_rate ,
2014-12-02 10:54:22 +03:00
unsigned long * p_rate , struct clk_hw * * p )
2014-05-17 03:07:11 +04:00
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
const struct freq_tbl * f = rcg - > freq_tbl ;
const struct frac_entry * frac ;
int delta = 100000 ;
s64 src_rate = * p_rate ;
s64 request ;
u32 mask = BIT ( rcg - > hid_width ) - 1 ;
u32 hid_div ;
2015-04-07 17:14:51 +03:00
int index = qcom_find_src_index ( hw , rcg - > parent_map , f - > src ) ;
2014-05-17 03:07:11 +04:00
/* Force the correct parent */
2015-04-07 17:14:51 +03:00
* p = __clk_get_hw ( clk_get_parent_by_index ( hw - > clk , index ) ) ;
2014-05-17 03:07:11 +04:00
if ( src_rate = = 810000000 )
frac = frac_table_810m ;
else
frac = frac_table_675m ;
for ( ; frac - > num ; frac + + ) {
request = rate ;
request * = frac - > den ;
request = div_s64 ( request , frac - > num ) ;
if ( ( src_rate < ( request - delta ) ) | |
( src_rate > ( request + delta ) ) )
continue ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG ,
& hid_div ) ;
hid_div > > = CFG_SRC_DIV_SHIFT ;
hid_div & = mask ;
return calc_rate ( src_rate , frac - > num , frac - > den , ! ! frac - > den ,
hid_div ) ;
}
return - EINVAL ;
}
const struct clk_ops clk_edp_pixel_ops = {
. is_enabled = clk_rcg2_is_enabled ,
. get_parent = clk_rcg2_get_parent ,
. set_parent = clk_rcg2_set_parent ,
. recalc_rate = clk_rcg2_recalc_rate ,
. set_rate = clk_edp_pixel_set_rate ,
. set_rate_and_parent = clk_edp_pixel_set_rate_and_parent ,
. determine_rate = clk_edp_pixel_determine_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_edp_pixel_ops ) ;
static long clk_byte_determine_rate ( struct clk_hw * hw , unsigned long rate ,
2015-01-23 14:03:31 +03:00
unsigned long min_rate , unsigned long max_rate ,
2014-12-02 10:54:22 +03:00
unsigned long * p_rate , struct clk_hw * * p_hw )
2014-05-17 03:07:11 +04:00
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
const struct freq_tbl * f = rcg - > freq_tbl ;
2015-04-07 17:14:51 +03:00
int index = qcom_find_src_index ( hw , rcg - > parent_map , f - > src ) ;
2014-05-17 03:07:11 +04:00
unsigned long parent_rate , div ;
u32 mask = BIT ( rcg - > hid_width ) - 1 ;
2014-12-02 10:54:22 +03:00
struct clk * p ;
2014-05-17 03:07:11 +04:00
if ( rate = = 0 )
return - EINVAL ;
2015-04-07 17:14:51 +03:00
p = clk_get_parent_by_index ( hw - > clk , index ) ;
2014-12-02 10:54:22 +03:00
* p_hw = __clk_get_hw ( p ) ;
* p_rate = parent_rate = __clk_round_rate ( p , rate ) ;
2014-05-17 03:07:11 +04:00
div = DIV_ROUND_UP ( ( 2 * parent_rate ) , rate ) - 1 ;
div = min_t ( u32 , div , mask ) ;
return calc_rate ( parent_rate , 0 , 0 , 0 , div ) ;
}
static int clk_byte_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
struct freq_tbl f = * rcg - > freq_tbl ;
unsigned long div ;
u32 mask = BIT ( rcg - > hid_width ) - 1 ;
div = DIV_ROUND_UP ( ( 2 * parent_rate ) , rate ) - 1 ;
div = min_t ( u32 , div , mask ) ;
f . pre_div = div ;
return clk_rcg2_configure ( rcg , & f ) ;
}
static int clk_byte_set_rate_and_parent ( struct clk_hw * hw ,
unsigned long rate , unsigned long parent_rate , u8 index )
{
/* Parent index is set statically in frequency table */
return clk_byte_set_rate ( hw , rate , parent_rate ) ;
}
const struct clk_ops clk_byte_ops = {
. is_enabled = clk_rcg2_is_enabled ,
. get_parent = clk_rcg2_get_parent ,
. set_parent = clk_rcg2_set_parent ,
. recalc_rate = clk_rcg2_recalc_rate ,
. set_rate = clk_byte_set_rate ,
. set_rate_and_parent = clk_byte_set_rate_and_parent ,
. determine_rate = clk_byte_determine_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_byte_ops ) ;
static const struct frac_entry frac_table_pixel [ ] = {
{ 3 , 8 } ,
{ 2 , 9 } ,
{ 4 , 9 } ,
{ 1 , 1 } ,
{ }
} ;
static long clk_pixel_determine_rate ( struct clk_hw * hw , unsigned long rate ,
2015-01-23 14:03:31 +03:00
unsigned long min_rate ,
unsigned long max_rate ,
2014-12-02 10:54:22 +03:00
unsigned long * p_rate , struct clk_hw * * p )
2014-05-17 03:07:11 +04:00
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
unsigned long request , src_rate ;
int delta = 100000 ;
const struct freq_tbl * f = rcg - > freq_tbl ;
const struct frac_entry * frac = frac_table_pixel ;
2015-04-07 17:14:51 +03:00
int index = qcom_find_src_index ( hw , rcg - > parent_map , f - > src ) ;
struct clk * parent = clk_get_parent_by_index ( hw - > clk , index ) ;
2014-12-02 10:54:22 +03:00
* p = __clk_get_hw ( parent ) ;
2014-05-17 03:07:11 +04:00
for ( ; frac - > num ; frac + + ) {
request = ( rate * frac - > den ) / frac - > num ;
src_rate = __clk_round_rate ( parent , request ) ;
if ( ( src_rate < ( request - delta ) ) | |
( src_rate > ( request + delta ) ) )
continue ;
* p_rate = src_rate ;
return ( src_rate * frac - > num ) / frac - > den ;
}
return - EINVAL ;
}
static int clk_pixel_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
struct clk_rcg2 * rcg = to_clk_rcg2 ( hw ) ;
struct freq_tbl f = * rcg - > freq_tbl ;
const struct frac_entry * frac = frac_table_pixel ;
unsigned long request , src_rate ;
int delta = 100000 ;
u32 mask = BIT ( rcg - > hid_width ) - 1 ;
u32 hid_div ;
2015-04-07 17:14:51 +03:00
int index = qcom_find_src_index ( hw , rcg - > parent_map , f . src ) ;
struct clk * parent = clk_get_parent_by_index ( hw - > clk , index ) ;
2014-05-17 03:07:11 +04:00
for ( ; frac - > num ; frac + + ) {
request = ( rate * frac - > den ) / frac - > num ;
src_rate = __clk_round_rate ( parent , request ) ;
if ( ( src_rate < ( request - delta ) ) | |
( src_rate > ( request + delta ) ) )
continue ;
regmap_read ( rcg - > clkr . regmap , rcg - > cmd_rcgr + CFG_REG ,
& hid_div ) ;
f . pre_div = hid_div ;
f . pre_div > > = CFG_SRC_DIV_SHIFT ;
f . pre_div & = mask ;
f . m = frac - > num ;
f . n = frac - > den ;
return clk_rcg2_configure ( rcg , & f ) ;
}
return - EINVAL ;
}
static int clk_pixel_set_rate_and_parent ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate , u8 index )
{
/* Parent index is set statically in frequency table */
return clk_pixel_set_rate ( hw , rate , parent_rate ) ;
}
const struct clk_ops clk_pixel_ops = {
. is_enabled = clk_rcg2_is_enabled ,
. get_parent = clk_rcg2_get_parent ,
. set_parent = clk_rcg2_set_parent ,
. recalc_rate = clk_rcg2_recalc_rate ,
. set_rate = clk_pixel_set_rate ,
. set_rate_and_parent = clk_pixel_set_rate_and_parent ,
. determine_rate = clk_pixel_determine_rate ,
} ;
EXPORT_SYMBOL_GPL ( clk_pixel_ops ) ;