2016-06-06 23:16:17 -07:00
/*
* This file is provided under a dual BSD / GPLv2 license . When using or
* redistributing this file , you may do so under either license .
*
* GPL LICENSE SUMMARY
*
* Copyright ( c ) 2016 AmLogic , Inc .
* Author : Michael Turquette < mturquette @ baylibre . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of version 2 of the GNU General Public License as
* published by the Free Software Foundation .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin St - Fifth Floor , Boston , MA 02110 - 1301 USA .
* The full GNU General Public License is included in this distribution
* in the file called COPYING
*
* BSD LICENSE
*
* Copyright ( c ) 2016 AmLogic , Inc .
* Author : Michael Turquette < mturquette @ baylibre . com >
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions
* are met :
*
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in
* the documentation and / or other materials provided with the
* distribution .
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* " AS IS " AND ANY EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT
* LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT
* LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
* DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
/*
* MultiPhase Locked Loops are outputs from a PLL with additional frequency
* scaling capabilities . MPLL rates are calculated as :
*
* f ( N2_integer , SDM_IN ) = 2.0 G / ( N2_integer + SDM_IN / 16384 )
*/
# include <linux/clk-provider.h>
# include "clkc.h"
2017-03-09 11:41:50 +01:00
# define SDM_DEN 16384
# define SDM_MIN 1
# define SDM_MAX 16383
# define N2_MIN 4
2017-03-09 11:41:53 +01:00
# define N2_MAX 511
2016-06-06 23:16:17 -07:00
# define to_meson_clk_mpll(_hw) container_of(_hw, struct meson_clk_mpll, hw)
2017-03-09 11:41:50 +01:00
static unsigned long rate_from_params ( unsigned long parent_rate ,
unsigned long sdm ,
unsigned long n2 )
{
return ( parent_rate * SDM_DEN ) / ( ( SDM_DEN * n2 ) + sdm ) ;
}
static void params_from_rate ( unsigned long requested_rate ,
unsigned long parent_rate ,
unsigned long * sdm ,
unsigned long * n2 )
{
uint64_t div = parent_rate ;
unsigned long rem = do_div ( div , requested_rate ) ;
if ( div < N2_MIN ) {
* n2 = N2_MIN ;
* sdm = SDM_MIN ;
} else if ( div > N2_MAX ) {
* n2 = N2_MAX ;
* sdm = SDM_MAX ;
} else {
* n2 = div ;
* sdm = DIV_ROUND_UP ( rem * SDM_DEN , requested_rate ) ;
if ( * sdm < SDM_MIN )
* sdm = SDM_MIN ;
else if ( * sdm > SDM_MAX )
* sdm = SDM_MAX ;
}
}
2016-06-06 23:16:17 -07:00
static unsigned long mpll_recalc_rate ( struct clk_hw * hw ,
unsigned long parent_rate )
{
struct meson_clk_mpll * mpll = to_meson_clk_mpll ( hw ) ;
struct parm * p ;
unsigned long reg , sdm , n2 ;
p = & mpll - > sdm ;
reg = readl ( mpll - > base + p - > reg_off ) ;
sdm = PARM_GET ( p - > width , p - > shift , reg ) ;
p = & mpll - > n2 ;
reg = readl ( mpll - > base + p - > reg_off ) ;
n2 = PARM_GET ( p - > width , p - > shift , reg ) ;
2017-03-09 11:41:50 +01:00
return rate_from_params ( parent_rate , sdm , n2 ) ;
}
static long mpll_round_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long * parent_rate )
{
unsigned long sdm , n2 ;
params_from_rate ( rate , * parent_rate , & sdm , & n2 ) ;
return rate_from_params ( * parent_rate , sdm , n2 ) ;
}
static int mpll_set_rate ( struct clk_hw * hw ,
unsigned long rate ,
unsigned long parent_rate )
{
struct meson_clk_mpll * mpll = to_meson_clk_mpll ( hw ) ;
struct parm * p ;
unsigned long reg , sdm , n2 ;
unsigned long flags = 0 ;
params_from_rate ( rate , parent_rate , & sdm , & n2 ) ;
if ( mpll - > lock )
spin_lock_irqsave ( mpll - > lock , flags ) ;
else
__acquire ( mpll - > lock ) ;
p = & mpll - > sdm ;
reg = readl ( mpll - > base + p - > reg_off ) ;
reg = PARM_SET ( p - > width , p - > shift , reg , sdm ) ;
writel ( reg , mpll - > base + p - > reg_off ) ;
p = & mpll - > sdm_en ;
reg = readl ( mpll - > base + p - > reg_off ) ;
reg = PARM_SET ( p - > width , p - > shift , reg , 1 ) ;
writel ( reg , mpll - > base + p - > reg_off ) ;
p = & mpll - > n2 ;
reg = readl ( mpll - > base + p - > reg_off ) ;
reg = PARM_SET ( p - > width , p - > shift , reg , n2 ) ;
writel ( reg , mpll - > base + p - > reg_off ) ;
if ( mpll - > lock )
spin_unlock_irqrestore ( mpll - > lock , flags ) ;
else
__release ( mpll - > lock ) ;
2016-06-06 23:16:17 -07:00
2017-03-09 11:41:50 +01:00
return 0 ;
}
static void mpll_enable_core ( struct clk_hw * hw , int enable )
{
struct meson_clk_mpll * mpll = to_meson_clk_mpll ( hw ) ;
struct parm * p ;
unsigned long reg ;
unsigned long flags = 0 ;
if ( mpll - > lock )
spin_lock_irqsave ( mpll - > lock , flags ) ;
else
__acquire ( mpll - > lock ) ;
p = & mpll - > en ;
reg = readl ( mpll - > base + p - > reg_off ) ;
reg = PARM_SET ( p - > width , p - > shift , reg , enable ? 1 : 0 ) ;
writel ( reg , mpll - > base + p - > reg_off ) ;
if ( mpll - > lock )
spin_unlock_irqrestore ( mpll - > lock , flags ) ;
else
__release ( mpll - > lock ) ;
}
static int mpll_enable ( struct clk_hw * hw )
{
mpll_enable_core ( hw , 1 ) ;
return 0 ;
}
static void mpll_disable ( struct clk_hw * hw )
{
mpll_enable_core ( hw , 0 ) ;
}
static int mpll_is_enabled ( struct clk_hw * hw )
{
struct meson_clk_mpll * mpll = to_meson_clk_mpll ( hw ) ;
struct parm * p ;
unsigned long reg ;
int en ;
p = & mpll - > en ;
reg = readl ( mpll - > base + p - > reg_off ) ;
en = PARM_GET ( p - > width , p - > shift , reg ) ;
return en ;
2016-06-06 23:16:17 -07:00
}
const struct clk_ops meson_clk_mpll_ro_ops = {
2017-03-09 11:41:50 +01:00
. recalc_rate = mpll_recalc_rate ,
. round_rate = mpll_round_rate ,
. is_enabled = mpll_is_enabled ,
} ;
const struct clk_ops meson_clk_mpll_ops = {
. recalc_rate = mpll_recalc_rate ,
. round_rate = mpll_round_rate ,
. set_rate = mpll_set_rate ,
. enable = mpll_enable ,
. disable = mpll_disable ,
. is_enabled = mpll_is_enabled ,
2016-06-06 23:16:17 -07:00
} ;