2009-12-08 18:47:16 -07:00
/*
* OMAP3 / 4 - specific DPLL control functions
*
2010-02-22 22:09:08 -07:00
* Copyright ( C ) 2009 - 2010 Texas Instruments , Inc .
* Copyright ( C ) 2009 - 2010 Nokia Corporation
2009-12-08 18:47:16 -07:00
*
* Written by Paul Walmsley
2010-02-22 22:09:08 -07:00
* Testing and integration fixes by Jouni Högander
*
* 36 xx support added by Vishwanath BS , Richard Woodruff , and Nishanth
* Menon
2009-12-08 18:47:16 -07:00
*
* Parts of this code are based on code written by
* Richard Woodruff , Tony Lindgren , Tuukka Tikkanen , Karthik Dasu
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/bitops.h>
2010-11-17 10:04:33 +01:00
# include <linux/clkdev.h>
2015-03-03 13:27:48 +02:00
# include <linux/clk/ti.h>
2009-12-08 18:47:16 -07:00
# include "clock.h"
/* CM_AUTOIDLE_PLL*.AUTO_* bit values */
# define DPLL_AUTOIDLE_DISABLE 0x0
# define DPLL_AUTOIDLE_LOW_POWER_STOP 0x1
# define MAX_DPLL_WAIT_TRIES 1000000
2015-03-03 13:27:48 +02:00
# define OMAP3XXX_EN_DPLL_LOCKED 0x7
/* Forward declarations */
static u32 omap3_dpll_autoidle_read ( struct clk_hw_omap * clk ) ;
static void omap3_dpll_deny_idle ( struct clk_hw_omap * clk ) ;
static void omap3_dpll_allow_idle ( struct clk_hw_omap * clk ) ;
2010-01-26 20:13:11 -07:00
/* Private functions */
2009-12-08 18:47:16 -07:00
/* _omap3_dpll_write_clken - write clken_bits arg to a DPLL's enable bits */
2012-11-10 16:58:41 -07:00
static void _omap3_dpll_write_clken ( struct clk_hw_omap * clk , u8 clken_bits )
2009-12-08 18:47:16 -07:00
{
const struct dpll_data * dd ;
u32 v ;
dd = clk - > dpll_data ;
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > control_reg ) ;
2009-12-08 18:47:16 -07:00
v & = ~ dd - > enable_mask ;
v | = clken_bits < < __ffs ( dd - > enable_mask ) ;
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > control_reg ) ;
2009-12-08 18:47:16 -07:00
}
/* _omap3_wait_dpll_status: wait for a DPLL to enter a specific state */
2012-11-10 16:58:41 -07:00
static int _omap3_wait_dpll_status ( struct clk_hw_omap * clk , u8 state )
2009-12-08 18:47:16 -07:00
{
const struct dpll_data * dd ;
int i = 0 ;
int ret = - EINVAL ;
2012-09-22 02:24:17 -06:00
const char * clk_name ;
2009-12-08 18:47:16 -07:00
dd = clk - > dpll_data ;
2015-07-30 17:20:57 -07:00
clk_name = clk_hw_get_name ( & clk - > hw ) ;
2009-12-08 18:47:16 -07:00
state < < = __ffs ( dd - > idlest_mask ) ;
2015-03-03 13:27:48 +02:00
while ( ( ( ti_clk_ll_ops - > clk_readl ( dd - > idlest_reg ) & dd - > idlest_mask )
2013-10-22 11:49:58 +03:00
! = state ) & & i < MAX_DPLL_WAIT_TRIES ) {
2009-12-08 18:47:16 -07:00
i + + ;
udelay ( 1 ) ;
}
if ( i = = MAX_DPLL_WAIT_TRIES ) {
2015-03-03 13:27:48 +02:00
pr_err ( " clock: %s failed transition to '%s' \n " ,
2012-09-22 02:24:17 -06:00
clk_name , ( state ) ? " locked " : " bypassed " ) ;
2009-12-08 18:47:16 -07:00
} else {
pr_debug ( " clock: %s transition to '%s' in %d loops \n " ,
2012-09-22 02:24:17 -06:00
clk_name , ( state ) ? " locked " : " bypassed " , i ) ;
2009-12-08 18:47:16 -07:00
ret = 0 ;
}
return ret ;
}
/* From 3430 TRM ES2 4.7.6.2 */
2012-11-10 16:58:41 -07:00
static u16 _omap3_dpll_compute_freqsel ( struct clk_hw_omap * clk , u8 n )
2009-12-08 18:47:16 -07:00
{
unsigned long fint ;
u16 f = 0 ;
2016-02-20 13:24:26 +02:00
fint = clk_hw_get_rate ( clk - > dpll_data - > clk_ref ) / n ;
2009-12-08 18:47:16 -07:00
pr_debug ( " clock: fint is %lu \n " , fint ) ;
if ( fint > = 750000 & & fint < = 1000000 )
f = 0x3 ;
else if ( fint > 1000000 & & fint < = 1250000 )
f = 0x4 ;
else if ( fint > 1250000 & & fint < = 1500000 )
f = 0x5 ;
else if ( fint > 1500000 & & fint < = 1750000 )
f = 0x6 ;
else if ( fint > 1750000 & & fint < = 2100000 )
f = 0x7 ;
else if ( fint > 7500000 & & fint < = 10000000 )
f = 0xB ;
else if ( fint > 10000000 & & fint < = 12500000 )
f = 0xC ;
else if ( fint > 12500000 & & fint < = 15000000 )
f = 0xD ;
else if ( fint > 15000000 & & fint < = 17500000 )
f = 0xE ;
else if ( fint > 17500000 & & fint < = 21000000 )
f = 0xF ;
else
pr_debug ( " clock: unknown freqsel setting for %d \n " , n ) ;
return f ;
}
/*
* _omap3_noncore_dpll_lock - instruct a DPLL to lock and wait for readiness
* @ clk : pointer to a DPLL struct clk
*
* Instructs a non - CORE DPLL to lock . Waits for the DPLL to report
* readiness before returning . Will save and restore the DPLL ' s
* autoidle state across the enable , per the CDP code . If the DPLL
* locked successfully , return 0 ; if the DPLL did not lock in the time
* allotted , or DPLL3 was passed in , return - EINVAL .
*/
2012-11-10 16:58:41 -07:00
static int _omap3_noncore_dpll_lock ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
2012-07-04 05:00:44 -06:00
const struct dpll_data * dd ;
2009-12-08 18:47:16 -07:00
u8 ai ;
2012-07-04 05:00:44 -06:00
u8 state = 1 ;
int r = 0 ;
2009-12-08 18:47:16 -07:00
2015-07-30 17:20:57 -07:00
pr_debug ( " clock: locking DPLL %s \n " , clk_hw_get_name ( & clk - > hw ) ) ;
2009-12-08 18:47:16 -07:00
2012-07-04 05:00:44 -06:00
dd = clk - > dpll_data ;
state < < = __ffs ( dd - > idlest_mask ) ;
/* Check if already locked */
2015-03-03 13:27:48 +02:00
if ( ( ti_clk_ll_ops - > clk_readl ( dd - > idlest_reg ) & dd - > idlest_mask ) = =
state )
2012-07-04 05:00:44 -06:00
goto done ;
2009-12-08 18:47:16 -07:00
ai = omap3_dpll_autoidle_read ( clk ) ;
2012-05-07 23:55:30 -06:00
if ( ai )
omap3_dpll_deny_idle ( clk ) ;
2009-12-08 18:47:16 -07:00
_omap3_dpll_write_clken ( clk , DPLL_LOCKED ) ;
r = _omap3_wait_dpll_status ( clk , 1 ) ;
if ( ai )
omap3_dpll_allow_idle ( clk ) ;
2012-07-04 05:00:44 -06:00
done :
2009-12-08 18:47:16 -07:00
return r ;
}
/*
* _omap3_noncore_dpll_bypass - instruct a DPLL to bypass and wait for readiness
* @ clk : pointer to a DPLL struct clk
*
* Instructs a non - CORE DPLL to enter low - power bypass mode . In
* bypass mode , the DPLL ' s rate is set equal to its parent clock ' s
* rate . Waits for the DPLL to report readiness before returning .
* Will save and restore the DPLL ' s autoidle state across the enable ,
* per the CDP code . If the DPLL entered bypass mode successfully ,
* return 0 ; if the DPLL did not enter bypass in the time allotted , or
* DPLL3 was passed in , or the DPLL does not support low - power bypass ,
* return - EINVAL .
*/
2012-11-10 16:58:41 -07:00
static int _omap3_noncore_dpll_bypass ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
int r ;
u8 ai ;
if ( ! ( clk - > dpll_data - > modes & ( 1 < < DPLL_LOW_POWER_BYPASS ) ) )
return - EINVAL ;
pr_debug ( " clock: configuring DPLL %s for low-power bypass \n " ,
2015-07-30 17:20:57 -07:00
clk_hw_get_name ( & clk - > hw ) ) ;
2009-12-08 18:47:16 -07:00
ai = omap3_dpll_autoidle_read ( clk ) ;
_omap3_dpll_write_clken ( clk , DPLL_LOW_POWER_BYPASS ) ;
r = _omap3_wait_dpll_status ( clk , 0 ) ;
if ( ai )
omap3_dpll_allow_idle ( clk ) ;
return r ;
}
/*
* _omap3_noncore_dpll_stop - instruct a DPLL to stop
* @ clk : pointer to a DPLL struct clk
*
* Instructs a non - CORE DPLL to enter low - power stop . Will save and
* restore the DPLL ' s autoidle state across the stop , per the CDP
* code . If DPLL3 was passed in , or the DPLL does not support
* low - power stop , return - EINVAL ; otherwise , return 0.
*/
2012-11-10 16:58:41 -07:00
static int _omap3_noncore_dpll_stop ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
u8 ai ;
if ( ! ( clk - > dpll_data - > modes & ( 1 < < DPLL_LOW_POWER_STOP ) ) )
return - EINVAL ;
2015-07-30 17:20:57 -07:00
pr_debug ( " clock: stopping DPLL %s \n " , clk_hw_get_name ( & clk - > hw ) ) ;
2009-12-08 18:47:16 -07:00
ai = omap3_dpll_autoidle_read ( clk ) ;
_omap3_dpll_write_clken ( clk , DPLL_LOW_POWER_STOP ) ;
if ( ai )
omap3_dpll_allow_idle ( clk ) ;
return 0 ;
}
2010-02-22 22:09:08 -07:00
/**
2010-12-21 21:31:43 -07:00
* _lookup_dco - Lookup DCO used by j - type DPLL
2010-02-22 22:09:08 -07:00
* @ clk : pointer to a DPLL struct clk
* @ dco : digital control oscillator selector
* @ m : DPLL multiplier to set
* @ n : DPLL divider to set
*
* See 36 xx TRM section 3.5 .3 .3 .3 .2 " Type B DPLL (Low-Jitter) "
*
* XXX This code is not needed for 3430 / AM35xx ; can it be optimized
* out in non - multi - OMAP builds for those chips ?
*/
2012-11-10 16:58:41 -07:00
static void _lookup_dco ( struct clk_hw_omap * clk , u8 * dco , u16 m , u8 n )
2010-02-22 22:09:08 -07:00
{
2010-12-21 21:31:43 -07:00
unsigned long fint , clkinp ; /* watch out for overflow */
2010-02-22 22:09:08 -07:00
2015-07-30 17:20:57 -07:00
clkinp = clk_hw_get_rate ( clk_hw_get_parent ( & clk - > hw ) ) ;
2010-02-22 22:09:08 -07:00
fint = ( clkinp / n ) * m ;
if ( fint < 1000000000 )
* dco = 2 ;
else
* dco = 4 ;
2010-12-21 21:31:43 -07:00
}
/**
* _lookup_sddiv - Calculate sigma delta divider for j - type DPLL
* @ clk : pointer to a DPLL struct clk
* @ sd_div : target sigma - delta divider
* @ m : DPLL multiplier to set
* @ n : DPLL divider to set
*
* See 36 xx TRM section 3.5 .3 .3 .3 .2 " Type B DPLL (Low-Jitter) "
*
* XXX This code is not needed for 3430 / AM35xx ; can it be optimized
* out in non - multi - OMAP builds for those chips ?
*/
2012-11-10 16:58:41 -07:00
static void _lookup_sddiv ( struct clk_hw_omap * clk , u8 * sd_div , u16 m , u8 n )
2010-12-21 21:31:43 -07:00
{
unsigned long clkinp , sd ; /* watch out for overflow */
int mod1 , mod2 ;
2015-07-30 17:20:57 -07:00
clkinp = clk_hw_get_rate ( clk_hw_get_parent ( & clk - > hw ) ) ;
2010-12-21 21:31:43 -07:00
2010-02-22 22:09:08 -07:00
/*
* target sigma - delta to near 250 MHz
* sd = ceil [ ( m / ( n + 1 ) ) * ( clkinp_MHz / 250 ) ]
*/
clkinp / = 100000 ; /* shift from MHz to 10*Hz for 38.4 and 19.2 */
mod1 = ( clkinp * m ) % ( 250 * n ) ;
sd = ( clkinp * m ) / ( 250 * n ) ;
mod2 = sd % 10 ;
sd / = 10 ;
if ( mod1 | | mod2 )
sd + + ;
* sd_div = sd ;
}
2010-01-26 20:13:11 -07:00
/*
* _omap3_noncore_dpll_program - set non - core DPLL M , N values directly
2012-12-15 01:35:46 -07:00
* @ clk : struct clk * of DPLL to set
* @ freqsel : FREQSEL value to set
2010-01-26 20:13:11 -07:00
*
2012-12-15 01:35:46 -07:00
* Program the DPLL with the last M , N values calculated , and wait for
* the DPLL to lock . Returns - EINVAL upon error , or 0 upon success .
2010-01-26 20:13:11 -07:00
*/
2012-12-15 01:35:46 -07:00
static int omap3_noncore_dpll_program ( struct clk_hw_omap * clk , u16 freqsel )
2010-01-26 20:13:11 -07:00
{
struct dpll_data * dd = clk - > dpll_data ;
2015-11-30 16:43:25 +02:00
u8 dco , sd_div , ai = 0 ;
2010-01-26 20:13:11 -07:00
u32 v ;
2015-11-30 16:43:25 +02:00
bool errata_i810 ;
2010-01-26 20:13:11 -07:00
/* 3430 ES2 TRM: 4.7.6.9 DPLL Programming Sequence */
_omap3_noncore_dpll_bypass ( clk ) ;
2010-02-24 12:05:57 -07:00
/*
2013-01-29 18:33:49 +05:30
* Set jitter correction . Jitter correction applicable for OMAP343X
* only since freqsel field is no longer present on other devices .
2010-02-24 12:05:57 -07:00
*/
2015-02-27 17:54:14 +02:00
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DPLL_HAS_FREQSEL ) {
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > control_reg ) ;
2010-01-26 20:13:11 -07:00
v & = ~ dd - > freqsel_mask ;
v | = freqsel < < __ffs ( dd - > freqsel_mask ) ;
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > control_reg ) ;
2010-01-26 20:13:11 -07:00
}
/* Set DPLL multiplier, divider */
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > mult_div1_reg ) ;
2014-05-16 05:45:58 -05:00
/* Handle Duty Cycle Correction */
if ( dd - > dcc_mask ) {
if ( dd - > last_rounded_rate > = dd - > dcc_rate )
v | = dd - > dcc_mask ; /* Enable DCC */
else
v & = ~ dd - > dcc_mask ; /* Disable DCC */
}
2010-01-26 20:13:11 -07:00
v & = ~ ( dd - > mult_mask | dd - > div1_mask ) ;
2012-12-15 01:35:46 -07:00
v | = dd - > last_rounded_m < < __ffs ( dd - > mult_mask ) ;
v | = ( dd - > last_rounded_n - 1 ) < < __ffs ( dd - > div1_mask ) ;
2010-02-22 22:09:08 -07:00
2010-12-21 21:31:43 -07:00
/* Configure dco and sd_div for dplls that have these fields */
if ( dd - > dco_mask ) {
2012-12-15 01:35:46 -07:00
_lookup_dco ( clk , & dco , dd - > last_rounded_m , dd - > last_rounded_n ) ;
2010-12-21 21:31:43 -07:00
v & = ~ ( dd - > dco_mask ) ;
v | = dco < < __ffs ( dd - > dco_mask ) ;
}
if ( dd - > sddiv_mask ) {
2012-12-15 01:35:46 -07:00
_lookup_sddiv ( clk , & sd_div , dd - > last_rounded_m ,
dd - > last_rounded_n ) ;
2010-12-21 21:31:43 -07:00
v & = ~ ( dd - > sddiv_mask ) ;
v | = sd_div < < __ffs ( dd - > sddiv_mask ) ;
2010-02-22 22:09:08 -07:00
}
2015-11-30 16:43:25 +02:00
/*
* Errata i810 - DPLL controller can get stuck while transitioning
* to a power saving state . Software must ensure the DPLL can not
* transition to a low power state while changing M / N values .
* Easiest way to accomplish this is to prevent DPLL autoidle
* before doing the M / N re - program .
*/
errata_i810 = ti_clk_get_features ( ) - > flags & TI_CLK_ERRATA_I810 ;
if ( errata_i810 ) {
ai = omap3_dpll_autoidle_read ( clk ) ;
if ( ai ) {
omap3_dpll_deny_idle ( clk ) ;
/* OCP barrier */
omap3_dpll_autoidle_read ( clk ) ;
}
}
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > mult_div1_reg ) ;
2010-01-26 20:13:11 -07:00
2012-12-15 01:35:46 -07:00
/* Set 4X multiplier and low-power mode */
if ( dd - > m4xen_mask | | dd - > lpmode_mask ) {
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > control_reg ) ;
2012-12-15 01:35:46 -07:00
if ( dd - > m4xen_mask ) {
if ( dd - > last_rounded_m4xen )
v | = dd - > m4xen_mask ;
else
v & = ~ dd - > m4xen_mask ;
}
if ( dd - > lpmode_mask ) {
if ( dd - > last_rounded_lpmode )
v | = dd - > lpmode_mask ;
else
v & = ~ dd - > lpmode_mask ;
}
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > control_reg ) ;
2012-12-15 01:35:46 -07:00
}
2010-01-26 20:13:11 -07:00
/* We let the clock framework set the other output dividers later */
/* REVISIT: Set ramp-up delay? */
_omap3_noncore_dpll_lock ( clk ) ;
2015-11-30 16:43:25 +02:00
if ( errata_i810 & & ai )
omap3_dpll_allow_idle ( clk ) ;
2010-01-26 20:13:11 -07:00
return 0 ;
}
/* Public functions */
/**
* omap3_dpll_recalc - recalculate DPLL rate
* @ clk : DPLL struct clk
*
* Recalculate and propagate the DPLL rate .
*/
2012-11-10 16:58:41 -07:00
unsigned long omap3_dpll_recalc ( struct clk_hw * hw , unsigned long parent_rate )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
2012-11-10 19:32:46 -07:00
2010-01-26 20:13:11 -07:00
return omap2_get_dpll_rate ( clk ) ;
}
/* Non-CORE DPLL (e.g., DPLLs that do not control SDRC) clock functions */
2009-12-08 18:47:16 -07:00
/**
* omap3_noncore_dpll_enable - instruct a DPLL to enter bypass or lock mode
* @ clk : pointer to a DPLL struct clk
*
* Instructs a non - CORE DPLL to enable , e . g . , to enter bypass or lock .
* The choice of modes depends on the DPLL ' s programmed rate : if it is
* the same as the DPLL ' s parent clock , it will enter bypass ;
* otherwise , it will enter lock . This code will wait for the DPLL to
* indicate readiness before returning , unless the DPLL takes too long
* to enter the target state . Intended to be used as the struct clk ' s
* enable function . If DPLL3 was passed in , or the DPLL does not
* support low - power stop , or if the DPLL took too long to enter
* bypass or lock , return - EINVAL ; otherwise , return 0.
*/
2012-11-10 16:58:41 -07:00
int omap3_noncore_dpll_enable ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
2009-12-08 18:47:16 -07:00
int r ;
struct dpll_data * dd ;
2015-01-23 12:03:30 +01:00
struct clk_hw * parent ;
2009-12-08 18:47:16 -07:00
dd = clk - > dpll_data ;
if ( ! dd )
return - EINVAL ;
2012-11-10 16:58:41 -07:00
if ( clk - > clkdm ) {
2015-03-03 13:27:48 +02:00
r = ti_clk_ll_ops - > clkdm_clk_enable ( clk - > clkdm , hw - > clk ) ;
2012-11-10 16:58:41 -07:00
if ( r ) {
WARN ( 1 ,
" %s: could not enable %s's clockdomain %s: %d \n " ,
2015-07-30 17:20:57 -07:00
__func__ , clk_hw_get_name ( hw ) ,
2015-03-03 13:27:48 +02:00
clk - > clkdm_name , r ) ;
2012-11-10 16:58:41 -07:00
return r ;
}
}
2015-07-30 17:20:57 -07:00
parent = clk_hw_get_parent ( hw ) ;
2012-11-10 16:58:41 -07:00
2016-02-20 13:24:26 +02:00
if ( clk_hw_get_rate ( hw ) = = clk_hw_get_rate ( dd - > clk_bypass ) ) {
WARN_ON ( parent ! = dd - > clk_bypass ) ;
2009-12-08 18:47:16 -07:00
r = _omap3_noncore_dpll_bypass ( clk ) ;
} else {
2016-02-20 13:24:26 +02:00
WARN_ON ( parent ! = dd - > clk_ref ) ;
2009-12-08 18:47:16 -07:00
r = _omap3_noncore_dpll_lock ( clk ) ;
}
2012-11-10 16:58:41 -07:00
2009-12-08 18:47:16 -07:00
return r ;
}
/**
* omap3_noncore_dpll_disable - instruct a DPLL to enter low - power stop
* @ clk : pointer to a DPLL struct clk
*
* Instructs a non - CORE DPLL to enter low - power stop . This function is
* intended for use in struct clkops . No return value .
*/
2012-11-10 16:58:41 -07:00
void omap3_noncore_dpll_disable ( struct clk_hw * hw )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
_omap3_noncore_dpll_stop ( clk ) ;
if ( clk - > clkdm )
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clkdm_clk_disable ( clk - > clkdm , hw - > clk ) ;
2009-12-08 18:47:16 -07:00
}
/* Non-CORE DPLL rate set code */
2014-10-03 16:57:11 +03:00
/**
* omap3_noncore_dpll_determine_rate - determine rate for a DPLL
* @ hw : pointer to the clock to determine rate for
2015-07-07 20:48:08 +02:00
* @ req : target rate request
2014-10-03 16:57:11 +03:00
*
* Determines which DPLL mode to use for reaching a desired target rate .
* Checks whether the DPLL shall be in bypass or locked mode , and if
* locked , calculates the M , N values for the DPLL via round - rate .
2015-07-07 20:48:08 +02:00
* Returns a 0 on success , negative error value in failure .
2014-10-03 16:57:11 +03:00
*/
2015-07-07 20:48:08 +02:00
int omap3_noncore_dpll_determine_rate ( struct clk_hw * hw ,
struct clk_rate_request * req )
2014-10-03 16:57:11 +03:00
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
struct dpll_data * dd ;
2015-07-07 20:48:08 +02:00
if ( ! req - > rate )
2014-10-03 16:57:11 +03:00
return - EINVAL ;
dd = clk - > dpll_data ;
if ( ! dd )
return - EINVAL ;
2016-02-20 13:24:26 +02:00
if ( clk_hw_get_rate ( dd - > clk_bypass ) = = req - > rate & &
2014-10-03 16:57:11 +03:00
( dd - > modes & ( 1 < < DPLL_LOW_POWER_BYPASS ) ) ) {
2016-02-20 13:24:26 +02:00
req - > best_parent_hw = dd - > clk_bypass ;
2014-10-03 16:57:11 +03:00
} else {
2015-07-07 20:48:08 +02:00
req - > rate = omap2_dpll_round_rate ( hw , req - > rate ,
& req - > best_parent_rate ) ;
2016-02-20 13:24:26 +02:00
req - > best_parent_hw = dd - > clk_ref ;
2014-10-03 16:57:11 +03:00
}
2015-07-07 20:48:08 +02:00
req - > best_parent_rate = req - > rate ;
2014-10-03 16:57:11 +03:00
2015-07-07 20:48:08 +02:00
return 0 ;
2014-10-03 16:57:11 +03:00
}
/**
* omap3_noncore_dpll_set_parent - set parent for a DPLL clock
* @ hw : pointer to the clock to set parent for
* @ index : parent index to select
*
* Sets parent for a DPLL clock . This sets the DPLL into bypass or
* locked mode . Returns 0 with success , negative error value otherwise .
*/
int omap3_noncore_dpll_set_parent ( struct clk_hw * hw , u8 index )
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
int ret ;
if ( ! hw )
return - EINVAL ;
if ( index )
ret = _omap3_noncore_dpll_bypass ( clk ) ;
else
ret = _omap3_noncore_dpll_lock ( clk ) ;
return ret ;
}
/**
2014-10-03 16:57:14 +03:00
* omap3_noncore_dpll_set_rate - set rate for a DPLL clock
2014-10-03 16:57:11 +03:00
* @ hw : pointer to the clock to set parent for
* @ rate : target rate for the clock
* @ parent_rate : rate of the parent clock
*
* Sets rate for a DPLL clock . First checks if the clock parent is
* reference clock ( in bypass mode , the rate of the clock can ' t be
* changed ) and proceeds with the rate change operation . Returns 0
* with success , negative error value otherwise .
*/
2014-10-03 16:57:14 +03:00
int omap3_noncore_dpll_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
2014-10-03 16:57:11 +03:00
{
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
struct dpll_data * dd ;
u16 freqsel = 0 ;
int ret ;
if ( ! hw | | ! rate )
return - EINVAL ;
dd = clk - > dpll_data ;
if ( ! dd )
return - EINVAL ;
2016-02-20 13:24:26 +02:00
if ( clk_hw_get_parent ( hw ) ! = dd - > clk_ref )
2014-10-03 16:57:11 +03:00
return - EINVAL ;
if ( dd - > last_rounded_rate = = 0 )
return - EINVAL ;
/* Freqsel is available only on OMAP343X devices */
2015-02-27 17:54:14 +02:00
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DPLL_HAS_FREQSEL ) {
2014-10-03 16:57:11 +03:00
freqsel = _omap3_dpll_compute_freqsel ( clk , dd - > last_rounded_n ) ;
WARN_ON ( ! freqsel ) ;
}
pr_debug ( " %s: %s: set rate: locking rate to %lu. \n " , __func__ ,
2015-07-30 17:20:57 -07:00
clk_hw_get_name ( hw ) , rate ) ;
2014-10-03 16:57:11 +03:00
ret = omap3_noncore_dpll_program ( clk , freqsel ) ;
return ret ;
}
/**
* omap3_noncore_dpll_set_rate_and_parent - set rate and parent for a DPLL clock
* @ hw : pointer to the clock to set rate and parent for
* @ rate : target rate for the DPLL
* @ parent_rate : clock rate of the DPLL parent
* @ index : new parent index for the DPLL , 0 - reference , 1 - bypass
*
* Sets rate and parent for a DPLL clock . If new parent is the bypass
* clock , only selects the parent . Otherwise proceeds with a rate
* change , as this will effectively also change the parent as the
* DPLL is put into locked mode . Returns 0 with success , negative error
* value otherwise .
*/
int omap3_noncore_dpll_set_rate_and_parent ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long parent_rate ,
u8 index )
{
int ret ;
if ( ! hw | | ! rate )
return - EINVAL ;
/*
* clk - ref at index [ 0 ] , in which case we only need to set rate ,
* the parent will be changed automatically with the lock sequence .
* With clk - bypass case we only need to change parent .
*/
if ( index )
ret = omap3_noncore_dpll_set_parent ( hw , index ) ;
else
2014-10-03 16:57:14 +03:00
ret = omap3_noncore_dpll_set_rate ( hw , rate , parent_rate ) ;
2014-10-03 16:57:11 +03:00
return ret ;
}
2009-12-08 18:47:16 -07:00
/* DPLL autoidle read/set code */
/**
* omap3_dpll_autoidle_read - read a DPLL ' s autoidle bits
* @ clk : struct clk * of the DPLL to read
*
* Return the DPLL ' s autoidle bits , shifted down to bit 0. Returns
* - EINVAL if passed a null pointer or if the struct clk does not
* appear to refer to a DPLL .
*/
2015-03-03 13:27:48 +02:00
static u32 omap3_dpll_autoidle_read ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
const struct dpll_data * dd ;
u32 v ;
if ( ! clk | | ! clk - > dpll_data )
return - EINVAL ;
dd = clk - > dpll_data ;
2012-05-07 23:55:30 -06:00
if ( ! dd - > autoidle_reg )
return - EINVAL ;
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > autoidle_reg ) ;
2009-12-08 18:47:16 -07:00
v & = dd - > autoidle_mask ;
v > > = __ffs ( dd - > autoidle_mask ) ;
return v ;
}
/**
* omap3_dpll_allow_idle - enable DPLL autoidle bits
* @ clk : struct clk * of the DPLL to operate on
*
* Enable DPLL automatic idle control . This automatic idle mode
* switching takes effect only when the DPLL is locked , at least on
* OMAP3430 . The DPLL will enter low - power stop when its downstream
* clocks are gated . No return value .
*/
2015-03-03 13:27:48 +02:00
static void omap3_dpll_allow_idle ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
const struct dpll_data * dd ;
u32 v ;
if ( ! clk | | ! clk - > dpll_data )
return ;
dd = clk - > dpll_data ;
2012-11-10 19:32:46 -07:00
if ( ! dd - > autoidle_reg )
2012-05-07 23:55:30 -06:00
return ;
2009-12-08 18:47:16 -07:00
/*
* REVISIT : CORE DPLL can optionally enter low - power bypass
* by writing 0x5 instead of 0x1 . Add some mechanism to
* optionally enter this mode .
*/
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > autoidle_reg ) ;
2009-12-08 18:47:16 -07:00
v & = ~ dd - > autoidle_mask ;
v | = DPLL_AUTOIDLE_LOW_POWER_STOP < < __ffs ( dd - > autoidle_mask ) ;
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > autoidle_reg ) ;
2009-12-08 18:47:16 -07:00
}
/**
* omap3_dpll_deny_idle - prevent DPLL from automatically idling
* @ clk : struct clk * of the DPLL to operate on
*
* Disable DPLL automatic idle control . No return value .
*/
2015-03-03 13:27:48 +02:00
static void omap3_dpll_deny_idle ( struct clk_hw_omap * clk )
2009-12-08 18:47:16 -07:00
{
const struct dpll_data * dd ;
u32 v ;
if ( ! clk | | ! clk - > dpll_data )
return ;
dd = clk - > dpll_data ;
2012-11-10 19:32:46 -07:00
if ( ! dd - > autoidle_reg )
2012-05-07 23:55:30 -06:00
return ;
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > autoidle_reg ) ;
2009-12-08 18:47:16 -07:00
v & = ~ dd - > autoidle_mask ;
v | = DPLL_AUTOIDLE_DISABLE < < __ffs ( dd - > autoidle_mask ) ;
2015-03-03 13:27:48 +02:00
ti_clk_ll_ops - > clk_writel ( v , dd - > autoidle_reg ) ;
2009-12-08 18:47:16 -07:00
}
/* Clock control for DPLL outputs */
2014-01-30 13:17:20 +02:00
/* Find the parent DPLL for the given clkoutx2 clock */
static struct clk_hw_omap * omap3_find_clkoutx2_dpll ( struct clk_hw * hw )
{
struct clk_hw_omap * pclk = NULL ;
/* Walk up the parents of clk, looking for a DPLL */
do {
do {
2015-07-30 17:20:57 -07:00
hw = clk_hw_get_parent ( hw ) ;
2015-06-29 16:56:30 -07:00
} while ( hw & & ( clk_hw_get_flags ( hw ) & CLK_IS_BASIC ) ) ;
2014-01-30 13:17:20 +02:00
if ( ! hw )
break ;
pclk = to_clk_hw_omap ( hw ) ;
} while ( pclk & & ! pclk - > dpll_data ) ;
/* clk does not have a DPLL as a parent? error in the clock data */
if ( ! pclk ) {
WARN_ON ( 1 ) ;
return NULL ;
}
return pclk ;
}
2009-12-08 18:47:16 -07:00
/**
* omap3_clkoutx2_recalc - recalculate DPLL X2 output virtual clock rate
* @ clk : DPLL output struct clk
*
* Using parent clock DPLL data , look up DPLL state . If locked , set our
* rate to the dpll_clk * 2 ; otherwise , just use dpll_clk .
*/
2012-11-10 16:58:41 -07:00
unsigned long omap3_clkoutx2_recalc ( struct clk_hw * hw ,
unsigned long parent_rate )
{
const struct dpll_data * dd ;
unsigned long rate ;
u32 v ;
struct clk_hw_omap * pclk = NULL ;
2013-10-22 11:49:58 +03:00
if ( ! parent_rate )
return 0 ;
2014-01-30 13:17:20 +02:00
pclk = omap3_find_clkoutx2_dpll ( hw ) ;
2009-12-08 18:47:16 -07:00
2014-01-30 13:17:20 +02:00
if ( ! pclk )
2012-08-03 09:21:10 -06:00
return 0 ;
2009-12-08 18:47:16 -07:00
dd = pclk - > dpll_data ;
WARN_ON ( ! dd - > enable_mask ) ;
2015-03-03 13:27:48 +02:00
v = ti_clk_ll_ops - > clk_readl ( dd - > control_reg ) & dd - > enable_mask ;
2009-12-08 18:47:16 -07:00
v > > = __ffs ( dd - > enable_mask ) ;
2010-02-22 22:09:08 -07:00
if ( ( v ! = OMAP3XXX_EN_DPLL_LOCKED ) | | ( dd - > flags & DPLL_J_TYPE ) )
2012-09-22 02:24:17 -06:00
rate = parent_rate ;
2009-12-08 18:47:16 -07:00
else
2012-09-22 02:24:17 -06:00
rate = parent_rate * 2 ;
2009-12-08 18:47:16 -07:00
return rate ;
}
2012-07-05 08:05:15 -07:00
2015-03-03 13:27:48 +02:00
/* OMAP3/4 non-CORE DPLL clkops */
const struct clk_hw_omap_ops clkhwops_omap3_dpll = {
. allow_idle = omap3_dpll_allow_idle ,
. deny_idle = omap3_dpll_deny_idle ,
} ;
2014-01-30 13:17:20 +02:00
2015-03-03 13:27:48 +02:00
/**
* omap3_dpll4_set_rate - set rate for omap3 per - dpll
* @ hw : clock to change
* @ rate : target rate for clock
* @ parent_rate : rate of the parent clock
*
* Check if the current SoC supports the per - dpll reprogram operation
* or not , and then do the rate change if supported . Returns - EINVAL
* if not supported , 0 for success , and potential error codes from the
* clock rate change .
*/
int omap3_dpll4_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
2014-01-30 13:17:20 +02:00
{
2015-03-03 13:27:48 +02:00
/*
* According to the 12 - 5 CDP code from TI , " Limitation 2.5 "
* on 3430 ES1 prevents us from changing DPLL multipliers or dividers
* on DPLL4 .
*/
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DPLL4_DENY_REPROGRAM ) {
pr_err ( " clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1. \n " ) ;
return - EINVAL ;
2014-01-30 13:17:20 +02:00
}
2015-03-03 13:27:48 +02:00
return omap3_noncore_dpll_set_rate ( hw , rate , parent_rate ) ;
}
2014-01-30 13:17:20 +02:00
2015-03-03 13:27:48 +02:00
/**
* omap3_dpll4_set_rate_and_parent - set rate and parent for omap3 per - dpll
* @ hw : clock to change
* @ rate : target rate for clock
* @ parent_rate : rate of the parent clock
* @ index : parent index , 0 - reference clock , 1 - bypass clock
*
* Check if the current SoC support the per - dpll reprogram operation
* or not , and then do the rate + parent change if supported . Returns
* - EINVAL if not supported , 0 for success , and potential error codes
* from the clock rate change .
*/
int omap3_dpll4_set_rate_and_parent ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate , u8 index )
{
if ( ti_clk_get_features ( ) - > flags & TI_CLK_DPLL4_DENY_REPROGRAM ) {
pr_err ( " clock: DPLL4 cannot change rate due to silicon 'Limitation 2.5' on 3430ES1. \n " ) ;
return - EINVAL ;
2014-01-30 13:17:20 +02:00
}
2015-03-03 13:27:48 +02:00
return omap3_noncore_dpll_set_rate_and_parent ( hw , rate , parent_rate ,
index ) ;
2014-01-30 13:17:20 +02:00
}
2016-12-02 23:14:38 +02:00
/* Apply DM3730 errata sprz319 advisory 2.1. */
static bool omap3_dpll5_apply_errata ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct omap3_dpll5_settings {
unsigned int rate , m , n ;
} ;
static const struct omap3_dpll5_settings precomputed [ ] = {
/*
* From DM3730 errata advisory 2.1 , table 35 and 36.
* The N value is increased by 1 compared to the tables as the
* errata lists register values while last_rounded_field is the
* real divider value .
*/
{ 12000000 , 80 , 0 + 1 } ,
{ 13000000 , 443 , 5 + 1 } ,
{ 19200000 , 50 , 0 + 1 } ,
{ 26000000 , 443 , 11 + 1 } ,
{ 38400000 , 25 , 0 + 1 }
} ;
const struct omap3_dpll5_settings * d ;
struct clk_hw_omap * clk = to_clk_hw_omap ( hw ) ;
struct dpll_data * dd ;
unsigned int i ;
for ( i = 0 ; i < ARRAY_SIZE ( precomputed ) ; + + i ) {
if ( parent_rate = = precomputed [ i ] . rate )
break ;
}
if ( i = = ARRAY_SIZE ( precomputed ) )
return false ;
d = & precomputed [ i ] ;
/* Update the M, N and rounded rate values and program the DPLL. */
dd = clk - > dpll_data ;
dd - > last_rounded_m = d - > m ;
dd - > last_rounded_n = d - > n ;
dd - > last_rounded_rate = div_u64 ( ( u64 ) parent_rate * d - > m , d - > n ) ;
omap3_noncore_dpll_program ( clk , 0 ) ;
return true ;
}
/**
* omap3_dpll5_set_rate - set rate for omap3 dpll5
* @ hw : clock to change
* @ rate : target rate for clock
* @ parent_rate : rate of the parent clock
*
* Set rate for the DPLL5 clock . Apply the sprz319 advisory 2.1 on OMAP36xx if
* the DPLL is used for USB host ( detected through the requested rate ) .
*/
int omap3_dpll5_set_rate ( struct clk_hw * hw , unsigned long rate ,
unsigned long parent_rate )
{
if ( rate = = OMAP3_DPLL5_FREQ_FOR_USBHOST * 8 ) {
if ( omap3_dpll5_apply_errata ( hw , parent_rate ) )
return 0 ;
}
return omap3_noncore_dpll_set_rate ( hw , rate , parent_rate ) ;
}