u-boot-brain/board/freescale/common/ics307_clk.c
Jerry Huang 71775d3b54 powerpc/mpc85xx: Set SYSCLK to the required frequency
For ICS307-02, there is one general expression to generate SYSCLK:
CLK1Frequency = InputFrequency * 2 * (VDW + 8) / ((RDW + 2) * OD)

If we want the required frequency for SYSCLK, we must find one solution
to generate this frequency, this solution includes VDW, RDW and OD.
For OD, there are only eight option value: 10, 2, 8, 4, 5, 7, 3, 6.
For RDW, the range is 1 to 127.
For VDW, the range is 4 to 511.

First, we use one OD, RDW and required SYSCLK to calculate the VDW,
if VDW is in it's range, we will calculate the CLK1Frequency with
the OD, RDW and VDW calculated, and we will check this percent
(CLK1Frequency / required SYSCLK), and the precision is 1/1000.
if the percent is less than 1/1000, we think the CLK1Frequency is we want.
Otherwise, We will continue to calculate it with the next OD and RDW.

Signed-off-by: Jerry Huang <Chang-Ming.Huang@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
2011-11-11 07:48:54 -06:00

158 lines
4.2 KiB
C

/*
* Copyright 2010 Freescale Semiconductor, Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <common.h>
#include <asm/io.h>
#include "ics307_clk.h"
#ifdef CONFIG_FSL_NGPIXIS
#include "ngpixis.h"
#else
#include "pixis.h"
#endif
/* define for SYS CLK or CLK1Frequency */
#define TTL 1
#define CLK2 0
#define CRYSTAL 0
#define MAX_VDW (511 + 8)
#define MAX_RDW (127 + 2)
#define MIN_VDW (4 + 8)
#define MIN_RDW (1 + 2)
#define NUM_OD_SETTING 8
/*
* These defines cover the industrial temperature range part,
* for commercial, change below to 400000 and 55000, respectively
*/
#define MAX_VCO 360000
#define MIN_VCO 60000
/* decode S[0-2] to Output Divider (OD) */
static u8 ics307_s_to_od[] = {
10, 2, 8, 4, 5, 7, 3, 6
};
/*
* Find one solution to generate required frequency for SYSCLK
* out_freq: KHz, required frequency to the SYSCLK
* the result will be retuned with component RDW, VDW, OD, TTL,
* CLK2 and crystal
*/
unsigned long ics307_sysclk_calculator(unsigned long out_freq)
{
const unsigned long input_freq = CONFIG_ICS307_REFCLK_HZ;
unsigned long vdw, rdw, odp, s_vdw = 0, s_rdw = 0, s_odp = 0, od;
unsigned long tmp_out, diff, result = 0;
int found = 0;
for (odp = 0; odp < NUM_OD_SETTING; odp++) {
od = ics307_s_to_od[odp];
if (od * out_freq < MIN_VCO || od * out_freq > MAX_VCO)
continue;
for (rdw = MIN_RDW; rdw <= MAX_RDW; rdw++) {
/* Calculate the VDW */
vdw = out_freq * 1000 * od * rdw / (input_freq * 2);
if (vdw > MAX_VDW)
vdw = MAX_VDW;
if (vdw < MIN_VDW)
continue;
/* Calculate the temp out frequency */
tmp_out = input_freq * 2 * vdw / (rdw * od * 1000);
diff = MAX(out_freq, tmp_out) - MIN(out_freq, tmp_out);
/*
* calculate the percent, the precision is 1/1000
* If greater than 1/1000, continue
* otherwise, we think the solution is we required
*/
if (diff * 1000 / out_freq > 1)
continue;
else {
s_vdw = vdw;
s_rdw = rdw;
s_odp = odp;
found = 1;
break;
}
}
}
if (found)
result = (s_rdw - 2) | (s_vdw - 8) << 7 | s_odp << 16 |
CLK2 << 19 | TTL << 21 | CRYSTAL << 22;
debug("ICS307-02: RDW: %ld, VDW: %ld, OD: %d\n", s_rdw - 2, s_vdw - 8,
ics307_s_to_od[s_odp]);
return result;
}
/*
* Calculate frequency being generated by ICS307-02 clock chip based upon
* the control bytes being programmed into it.
*/
static unsigned long ics307_clk_freq(u8 cw0, u8 cw1, u8 cw2)
{
const unsigned long input_freq = CONFIG_ICS307_REFCLK_HZ;
unsigned long vdw = ((cw1 << 1) & 0x1FE) + ((cw2 >> 7) & 1);
unsigned long rdw = cw2 & 0x7F;
unsigned long od = ics307_s_to_od[cw0 & 0x7];
unsigned long freq;
/*
* CLK1 Freq = Input Frequency * 2 * (VDW + 8) / ((RDW + 2) * OD)
*
* cw0: C1 C0 TTL F1 F0 S2 S1 S0
* cw1: V8 V7 V6 V5 V4 V3 V2 V1
* cw2: V0 R6 R5 R4 R3 R2 R1 R0
*
* R6:R0 = Reference Divider Word (RDW)
* V8:V0 = VCO Divider Word (VDW)
* S2:S0 = Output Divider Select (OD)
* F1:F0 = Function of CLK2 Output
* TTL = duty cycle
* C1:C0 = internal load capacitance for cyrstal
*
*/
freq = input_freq * 2 * (vdw + 8) / ((rdw + 2) * od);
debug("ICS307: CW[0-2]: %02X %02X %02X => %lu Hz\n", cw0, cw1, cw2,
freq);
return freq;
}
unsigned long get_board_sys_clk(void)
{
return ics307_clk_freq(
in_8(&pixis->sclk[0]),
in_8(&pixis->sclk[1]),
in_8(&pixis->sclk[2]));
}
unsigned long get_board_ddr_clk(void)
{
return ics307_clk_freq(
in_8(&pixis->dclk[0]),
in_8(&pixis->dclk[1]),
in_8(&pixis->dclk[2]));
}