2014-11-27 04:30:27 +03:00
/*
* Copyright 2014 Google , Inc
* Author : Alexandru M Stan < amstan @ chromium . org >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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/slab.h>
2015-06-20 01:00:46 +03:00
# include <linux/clk.h>
2014-11-27 04:30:27 +03:00
# include <linux/clk-provider.h>
2015-07-05 12:00:15 +03:00
# include <linux/io.h>
# include <linux/kernel.h>
2014-11-27 04:30:27 +03:00
# include "clk.h"
struct rockchip_mmc_clock {
struct clk_hw hw ;
void __iomem * reg ;
int id ;
int shift ;
} ;
# define to_mmc_clock(_hw) container_of(_hw, struct rockchip_mmc_clock, hw)
# define RK3288_MMC_CLKGEN_DIV 2
static unsigned long rockchip_mmc_recalc ( struct clk_hw * hw ,
unsigned long parent_rate )
{
return parent_rate / RK3288_MMC_CLKGEN_DIV ;
}
# define ROCKCHIP_MMC_DELAY_SEL BIT(10)
# define ROCKCHIP_MMC_DEGREE_MASK 0x3
# define ROCKCHIP_MMC_DELAYNUM_OFFSET 2
# define ROCKCHIP_MMC_DELAYNUM_MASK (0xff << ROCKCHIP_MMC_DELAYNUM_OFFSET)
2015-09-04 03:15:33 +03:00
# define ROCKCHIP_MMC_INIT_STATE_RESET 0x1
# define ROCKCHIP_MMC_INIT_STATE_SHIFT 1
2014-11-27 04:30:27 +03:00
# define PSECS_PER_SEC 1000000000000LL
/*
2015-09-30 17:07:37 +03:00
* Each fine delay is between 44 ps - 77 ps . Assume each fine delay is 60 ps to
* simplify calculations . So 45 degs could be anywhere between 33 deg and 57.8 deg .
2014-11-27 04:30:27 +03:00
*/
# define ROCKCHIP_MMC_DELAY_ELEMENT_PSEC 60
static int rockchip_mmc_get_phase ( struct clk_hw * hw )
{
struct rockchip_mmc_clock * mmc_clock = to_mmc_clock ( hw ) ;
unsigned long rate = clk_get_rate ( hw - > clk ) ;
u32 raw_value ;
u16 degrees ;
u32 delay_num = 0 ;
raw_value = readl ( mmc_clock - > reg ) > > ( mmc_clock - > shift ) ;
degrees = ( raw_value & ROCKCHIP_MMC_DEGREE_MASK ) * 90 ;
if ( raw_value & ROCKCHIP_MMC_DELAY_SEL ) {
/* degrees/delaynum * 10000 */
unsigned long factor = ( ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10 ) *
36 * ( rate / 1000000 ) ;
delay_num = ( raw_value & ROCKCHIP_MMC_DELAYNUM_MASK ) ;
delay_num > > = ROCKCHIP_MMC_DELAYNUM_OFFSET ;
2015-09-30 17:07:38 +03:00
degrees + = DIV_ROUND_CLOSEST ( delay_num * factor , 10000 ) ;
2014-11-27 04:30:27 +03:00
}
return degrees % 360 ;
}
static int rockchip_mmc_set_phase ( struct clk_hw * hw , int degrees )
{
struct rockchip_mmc_clock * mmc_clock = to_mmc_clock ( hw ) ;
unsigned long rate = clk_get_rate ( hw - > clk ) ;
u8 nineties , remainder ;
u8 delay_num ;
u32 raw_value ;
2015-09-30 17:07:38 +03:00
u32 delay ;
2014-11-27 04:30:27 +03:00
nineties = degrees / 90 ;
2015-09-30 17:07:37 +03:00
remainder = ( degrees % 90 ) ;
/*
* Due to the inexact nature of the " fine " delay , we might
* actually go non - monotonic . We don ' t go _too_ monotonic
* though , so we should be OK . Here are options of how we may
* work :
*
* Ideally we end up with :
* 1.0 , 2.0 , . . . , 69.0 , 70.0 , . . . , 89.0 , 90.0
*
* On one extreme ( if delay is actually 44 ps ) :
* .73 , 1.5 , . . . , 50.6 , 51.3 , . . . , 65.3 , 90.0
* The other ( if delay is actually 77 ps ) :
* 1.3 , 2.6 , . . . , 88.6 . 89.8 , . . . , 114.0 , 90
*
* It ' s possible we might make a delay that is up to 25
* degrees off from what we think we ' re making . That ' s OK
* though because we should be REALLY far from any bad range .
*/
/*
* Convert to delay ; do a little extra work to make sure we
* don ' t overflow 32 - bit / 64 - bit numbers .
*/
2015-09-30 17:07:38 +03:00
delay = 10000000 ; /* PSECS_PER_SEC / 10000 / 10 */
2014-11-27 04:30:27 +03:00
delay * = remainder ;
2015-09-30 17:07:38 +03:00
delay = DIV_ROUND_CLOSEST ( delay ,
( rate / 1000 ) * 36 *
( ROCKCHIP_MMC_DELAY_ELEMENT_PSEC / 10 ) ) ;
2015-09-30 17:07:37 +03:00
2015-09-30 17:07:38 +03:00
delay_num = ( u8 ) min_t ( u32 , delay , 255 ) ;
2014-11-27 04:30:27 +03:00
raw_value = delay_num ? ROCKCHIP_MMC_DELAY_SEL : 0 ;
raw_value | = delay_num < < ROCKCHIP_MMC_DELAYNUM_OFFSET ;
raw_value | = nineties ;
writel ( HIWORD_UPDATE ( raw_value , 0x07ff , mmc_clock - > shift ) , mmc_clock - > reg ) ;
pr_debug ( " %s->set_phase(%d) delay_nums=%u reg[0x%p]=0x%03x actual_degrees=%d \n " ,
2015-08-12 21:42:23 +03:00
clk_hw_get_name ( hw ) , degrees , delay_num ,
2014-11-27 04:30:27 +03:00
mmc_clock - > reg , raw_value > > ( mmc_clock - > shift ) ,
rockchip_mmc_get_phase ( hw )
) ;
return 0 ;
}
static const struct clk_ops rockchip_mmc_clk_ops = {
. recalc_rate = rockchip_mmc_recalc ,
. get_phase = rockchip_mmc_get_phase ,
. set_phase = rockchip_mmc_set_phase ,
} ;
struct clk * rockchip_clk_register_mmc ( const char * name ,
2015-05-28 11:45:51 +03:00
const char * const * parent_names , u8 num_parents ,
2014-11-27 04:30:27 +03:00
void __iomem * reg , int shift )
{
struct clk_init_data init ;
struct rockchip_mmc_clock * mmc_clock ;
struct clk * clk ;
mmc_clock = kmalloc ( sizeof ( * mmc_clock ) , GFP_KERNEL ) ;
if ( ! mmc_clock )
return NULL ;
2015-07-05 12:00:15 +03:00
init . name = name ;
2014-11-27 04:30:27 +03:00
init . num_parents = num_parents ;
init . parent_names = parent_names ;
init . ops = & rockchip_mmc_clk_ops ;
mmc_clock - > hw . init = & init ;
mmc_clock - > reg = reg ;
mmc_clock - > shift = shift ;
2015-09-04 03:15:33 +03:00
/*
* Assert init_state to soft reset the CLKGEN
* for mmc tuning phase and degree
*/
if ( mmc_clock - > shift = = ROCKCHIP_MMC_INIT_STATE_SHIFT )
writel ( HIWORD_UPDATE ( ROCKCHIP_MMC_INIT_STATE_RESET ,
ROCKCHIP_MMC_INIT_STATE_RESET ,
mmc_clock - > shift ) , mmc_clock - > reg ) ;
2014-11-27 04:30:27 +03:00
clk = clk_register ( NULL , & mmc_clock - > hw ) ;
if ( IS_ERR ( clk ) )
goto err_free ;
return clk ;
err_free :
kfree ( mmc_clock ) ;
return NULL ;
}