2019-06-04 10:11:33 +02:00
// SPDX-License-Identifier: GPL-2.0-only
2005-04-16 15:20:36 -07:00
/*
* linux / arch / arm / common / icst307 . c
*
* Copyright ( C ) 2003 Deep Blue Solutions , Ltd , All Rights Reserved .
*
* Support functions for calculating clocks / divisors for the ICST307
2020-07-03 19:51:14 +02:00
* clock generators . See https : //www.idt.com/ for more information
2005-04-16 15:20:36 -07:00
* on these devices .
*
* This is an almost identical implementation to the ICST525 clock generator .
* The s2div and idx2s files are different
*/
# include <linux/module.h>
# include <linux/kernel.h>
2016-02-08 09:14:37 +01:00
# include <asm/div64.h>
2017-02-01 10:41:43 +01:00
# include "icst.h"
2005-04-16 15:20:36 -07:00
/*
* Divisors for each OD setting .
*/
2010-01-16 19:46:19 +00:00
const unsigned char icst307_s2div [ 8 ] = { 10 , 2 , 8 , 4 , 5 , 7 , 3 , 6 } ;
2010-01-16 20:16:10 +00:00
const unsigned char icst525_s2div [ 8 ] = { 10 , 2 , 8 , 4 , 5 , 7 , 9 , 6 } ;
2010-01-16 19:46:19 +00:00
EXPORT_SYMBOL ( icst307_s2div ) ;
2010-01-16 20:16:10 +00:00
EXPORT_SYMBOL ( icst525_s2div ) ;
2005-04-16 15:20:36 -07:00
2010-01-16 20:16:10 +00:00
unsigned long icst_hz ( const struct icst_params * p , struct icst_vco vco )
2005-04-16 15:20:36 -07:00
{
2016-02-08 09:14:37 +01:00
u64 dividend = p - > ref * 2 * ( u64 ) ( vco . v + 8 ) ;
u32 divisor = ( vco . r + 2 ) * p - > s2div [ vco . s ] ;
do_div ( dividend , divisor ) ;
return ( unsigned long ) dividend ;
2005-04-16 15:20:36 -07:00
}
2010-01-16 20:16:10 +00:00
EXPORT_SYMBOL ( icst_hz ) ;
2005-04-16 15:20:36 -07:00
/*
* Ascending divisor S values .
*/
2010-01-16 19:46:19 +00:00
const unsigned char icst307_idx2s [ 8 ] = { 1 , 6 , 3 , 4 , 7 , 5 , 2 , 0 } ;
2010-01-16 20:16:10 +00:00
const unsigned char icst525_idx2s [ 8 ] = { 1 , 3 , 4 , 7 , 5 , 2 , 6 , 0 } ;
2010-01-16 19:46:19 +00:00
EXPORT_SYMBOL ( icst307_idx2s ) ;
2010-01-16 20:16:10 +00:00
EXPORT_SYMBOL ( icst525_idx2s ) ;
2005-04-16 15:20:36 -07:00
2010-01-16 16:27:28 +00:00
struct icst_vco
2010-01-16 20:16:10 +00:00
icst_hz_to_vco ( const struct icst_params * p , unsigned long freq )
2005-04-16 15:20:36 -07:00
{
2010-01-16 16:27:28 +00:00
struct icst_vco vco = { . s = 1 , . v = p - > vd_max , . r = p - > rd_max } ;
2005-04-16 15:20:36 -07:00
unsigned long f ;
unsigned int i = 0 , rd , best = ( unsigned int ) - 1 ;
/*
* First , find the PLL output divisor such
* that the PLL output is within spec .
*/
do {
2010-01-16 19:46:19 +00:00
f = freq * p - > s2div [ p - > idx2s [ i ] ] ;
2005-04-16 15:20:36 -07:00
2010-01-16 19:49:39 +00:00
if ( f > p - > vco_min & & f < = p - > vco_max )
2005-04-16 15:20:36 -07:00
break ;
2016-02-10 09:25:17 +01:00
i + + ;
2010-01-16 19:46:19 +00:00
} while ( i < 8 ) ;
2005-04-16 15:20:36 -07:00
2010-01-16 19:46:19 +00:00
if ( i > = 8 )
2005-04-16 15:20:36 -07:00
return vco ;
2010-01-16 19:46:19 +00:00
vco . s = p - > idx2s [ i ] ;
2005-04-16 15:20:36 -07:00
/*
* Now find the closest divisor combination
* which gives a PLL output of ' f ' .
*/
for ( rd = p - > rd_min ; rd < = p - > rd_max ; rd + + ) {
unsigned long fref_div , f_pll ;
unsigned int vd ;
int f_diff ;
fref_div = ( 2 * p - > ref ) / rd ;
vd = ( f + fref_div / 2 ) / fref_div ;
if ( vd < p - > vd_min | | vd > p - > vd_max )
continue ;
f_pll = fref_div * vd ;
f_diff = f_pll - f ;
if ( f_diff < 0 )
f_diff = - f_diff ;
if ( ( unsigned ) f_diff < best ) {
vco . v = vd - 8 ;
vco . r = rd - 2 ;
if ( f_diff = = 0 )
break ;
best = f_diff ;
}
}
return vco ;
}
2010-01-16 20:16:10 +00:00
EXPORT_SYMBOL ( icst_hz_to_vco ) ;