1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2010-2011 Freescale Semiconductor, Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun */
6*4882a593Smuzhiyun
7*4882a593Smuzhiyun #include <common.h>
8*4882a593Smuzhiyun #include <asm/io.h>
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include "ics307_clk.h"
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #if defined(CONFIG_FSL_NGPIXIS)
13*4882a593Smuzhiyun #include "ngpixis.h"
14*4882a593Smuzhiyun #define fpga_reg pixis
15*4882a593Smuzhiyun #elif defined(CONFIG_FSL_QIXIS)
16*4882a593Smuzhiyun #include "qixis.h"
17*4882a593Smuzhiyun #define fpga_reg ((struct qixis *)QIXIS_BASE)
18*4882a593Smuzhiyun #else
19*4882a593Smuzhiyun #include "pixis.h"
20*4882a593Smuzhiyun #define fpga_reg pixis
21*4882a593Smuzhiyun #endif
22*4882a593Smuzhiyun
23*4882a593Smuzhiyun /* define for SYS CLK or CLK1Frequency */
24*4882a593Smuzhiyun #define TTL 1
25*4882a593Smuzhiyun #define CLK2 0
26*4882a593Smuzhiyun #define CRYSTAL 0
27*4882a593Smuzhiyun #define MAX_VDW (511 + 8)
28*4882a593Smuzhiyun #define MAX_RDW (127 + 2)
29*4882a593Smuzhiyun #define MIN_VDW (4 + 8)
30*4882a593Smuzhiyun #define MIN_RDW (1 + 2)
31*4882a593Smuzhiyun #define NUM_OD_SETTING 8
32*4882a593Smuzhiyun /*
33*4882a593Smuzhiyun * These defines cover the industrial temperature range part,
34*4882a593Smuzhiyun * for commercial, change below to 400000 and 55000, respectively
35*4882a593Smuzhiyun */
36*4882a593Smuzhiyun #define MAX_VCO 360000
37*4882a593Smuzhiyun #define MIN_VCO 60000
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun /* decode S[0-2] to Output Divider (OD) */
40*4882a593Smuzhiyun static u8 ics307_s_to_od[] = {
41*4882a593Smuzhiyun 10, 2, 8, 4, 5, 7, 3, 6
42*4882a593Smuzhiyun };
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun /*
45*4882a593Smuzhiyun * Find one solution to generate required frequency for SYSCLK
46*4882a593Smuzhiyun * out_freq: KHz, required frequency to the SYSCLK
47*4882a593Smuzhiyun * the result will be retuned with component RDW, VDW, OD, TTL,
48*4882a593Smuzhiyun * CLK2 and crystal
49*4882a593Smuzhiyun */
ics307_sysclk_calculator(unsigned long out_freq)50*4882a593Smuzhiyun unsigned long ics307_sysclk_calculator(unsigned long out_freq)
51*4882a593Smuzhiyun {
52*4882a593Smuzhiyun const unsigned long input_freq = CONFIG_ICS307_REFCLK_HZ;
53*4882a593Smuzhiyun unsigned long vdw, rdw, odp, s_vdw = 0, s_rdw = 0, s_odp = 0, od;
54*4882a593Smuzhiyun unsigned long tmp_out, diff, result = 0;
55*4882a593Smuzhiyun int found = 0;
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun for (odp = 0; odp < NUM_OD_SETTING; odp++) {
58*4882a593Smuzhiyun od = ics307_s_to_od[odp];
59*4882a593Smuzhiyun if (od * out_freq < MIN_VCO || od * out_freq > MAX_VCO)
60*4882a593Smuzhiyun continue;
61*4882a593Smuzhiyun for (rdw = MIN_RDW; rdw <= MAX_RDW; rdw++) {
62*4882a593Smuzhiyun /* Calculate the VDW */
63*4882a593Smuzhiyun vdw = out_freq * 1000 * od * rdw / (input_freq * 2);
64*4882a593Smuzhiyun if (vdw > MAX_VDW)
65*4882a593Smuzhiyun vdw = MAX_VDW;
66*4882a593Smuzhiyun if (vdw < MIN_VDW)
67*4882a593Smuzhiyun continue;
68*4882a593Smuzhiyun /* Calculate the temp out frequency */
69*4882a593Smuzhiyun tmp_out = input_freq * 2 * vdw / (rdw * od * 1000);
70*4882a593Smuzhiyun diff = max(out_freq, tmp_out) - min(out_freq, tmp_out);
71*4882a593Smuzhiyun /*
72*4882a593Smuzhiyun * calculate the percent, the precision is 1/1000
73*4882a593Smuzhiyun * If greater than 1/1000, continue
74*4882a593Smuzhiyun * otherwise, we think the solution is we required
75*4882a593Smuzhiyun */
76*4882a593Smuzhiyun if (diff * 1000 / out_freq > 1)
77*4882a593Smuzhiyun continue;
78*4882a593Smuzhiyun else {
79*4882a593Smuzhiyun s_vdw = vdw;
80*4882a593Smuzhiyun s_rdw = rdw;
81*4882a593Smuzhiyun s_odp = odp;
82*4882a593Smuzhiyun found = 1;
83*4882a593Smuzhiyun break;
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun if (found)
89*4882a593Smuzhiyun result = (s_rdw - 2) | (s_vdw - 8) << 7 | s_odp << 16 |
90*4882a593Smuzhiyun CLK2 << 19 | TTL << 21 | CRYSTAL << 22;
91*4882a593Smuzhiyun
92*4882a593Smuzhiyun debug("ICS307-02: RDW: %ld, VDW: %ld, OD: %d\n", s_rdw - 2, s_vdw - 8,
93*4882a593Smuzhiyun ics307_s_to_od[s_odp]);
94*4882a593Smuzhiyun return result;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun /*
98*4882a593Smuzhiyun * Calculate frequency being generated by ICS307-02 clock chip based upon
99*4882a593Smuzhiyun * the control bytes being programmed into it.
100*4882a593Smuzhiyun */
ics307_clk_freq(u8 cw0,u8 cw1,u8 cw2)101*4882a593Smuzhiyun static unsigned long ics307_clk_freq(u8 cw0, u8 cw1, u8 cw2)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun const unsigned long input_freq = CONFIG_ICS307_REFCLK_HZ;
104*4882a593Smuzhiyun unsigned long vdw = ((cw1 << 1) & 0x1FE) + ((cw2 >> 7) & 1);
105*4882a593Smuzhiyun unsigned long rdw = cw2 & 0x7F;
106*4882a593Smuzhiyun unsigned long od = ics307_s_to_od[cw0 & 0x7];
107*4882a593Smuzhiyun unsigned long freq;
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun /*
110*4882a593Smuzhiyun * CLK1 Freq = Input Frequency * 2 * (VDW + 8) / ((RDW + 2) * OD)
111*4882a593Smuzhiyun *
112*4882a593Smuzhiyun * cw0: C1 C0 TTL F1 F0 S2 S1 S0
113*4882a593Smuzhiyun * cw1: V8 V7 V6 V5 V4 V3 V2 V1
114*4882a593Smuzhiyun * cw2: V0 R6 R5 R4 R3 R2 R1 R0
115*4882a593Smuzhiyun *
116*4882a593Smuzhiyun * R6:R0 = Reference Divider Word (RDW)
117*4882a593Smuzhiyun * V8:V0 = VCO Divider Word (VDW)
118*4882a593Smuzhiyun * S2:S0 = Output Divider Select (OD)
119*4882a593Smuzhiyun * F1:F0 = Function of CLK2 Output
120*4882a593Smuzhiyun * TTL = duty cycle
121*4882a593Smuzhiyun * C1:C0 = internal load capacitance for cyrstal
122*4882a593Smuzhiyun *
123*4882a593Smuzhiyun */
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun freq = input_freq * 2 * (vdw + 8) / ((rdw + 2) * od);
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun debug("ICS307: CW[0-2]: %02X %02X %02X => %lu Hz\n", cw0, cw1, cw2,
128*4882a593Smuzhiyun freq);
129*4882a593Smuzhiyun return freq;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun
get_board_sys_clk(void)132*4882a593Smuzhiyun unsigned long get_board_sys_clk(void)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun return ics307_clk_freq(
135*4882a593Smuzhiyun in_8(&fpga_reg->sclk[0]),
136*4882a593Smuzhiyun in_8(&fpga_reg->sclk[1]),
137*4882a593Smuzhiyun in_8(&fpga_reg->sclk[2]));
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
get_board_ddr_clk(void)140*4882a593Smuzhiyun unsigned long get_board_ddr_clk(void)
141*4882a593Smuzhiyun {
142*4882a593Smuzhiyun return ics307_clk_freq(
143*4882a593Smuzhiyun in_8(&fpga_reg->dclk[0]),
144*4882a593Smuzhiyun in_8(&fpga_reg->dclk[1]),
145*4882a593Smuzhiyun in_8(&fpga_reg->dclk[2]));
146*4882a593Smuzhiyun }
147