summaryrefslogtreecommitdiff
path: root/bsp/allwinner/libraries/sunxi-hal/hal/source/prcm/prcm-sun50iw11/ccu.c
blob: 094f0f21c7759744158b2e5638270085127d4244 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
*********************************************************************************************************
*                                                AR100 SYSTEM
*                                     AR100 Software System Develop Kits
*                                         clock control unit module
*
*                                    (c) Copyright 2012-2016, Sunny China
*                                             All Rights Reserved
*
* File    : ccu.c
* By      : Sunny
* Version : v1.0
* Date    : 2012-5-7
* Descript: clock control unit module.
* Update  : date                auther      ver     notes
*           2012-5-7 8:43:10    Sunny       1.0     Create this file.
*********************************************************************************************************
*/

#include "ccu_i.h"
#include "platform.h"
#include "cpucfg_regs.h"
#include "hal_prcm.h"
#include "compiler_attributes.h"
#include "aw_io.h"

#define DO_NOT_CALIBRATION
/* ccu module registers base address */
struct ccu_reg_list *ccu_reg_addr;
struct ccu_pll_c0_cpux_reg0000 *ccu_pll_c0_cpux_reg_addr;
struct ccu_pll_ddr0_reg0010 *ccu_pll_ddr0_reg_addr;
struct ccu_pll_periph_reg0010 *ccu_pll_periph0_reg_addr;
struct ccu_pll_audio0_reg0020 *ccu_pll_audio0_reg_addr;
/* struct ccu_pll_periph1_reg0028 *ccu_pll_periph1_reg_addr; */

/* apb clock change notifier list */
struct notifier *apbs2_notifier_head;
u32 iosc_freq = 16000000;
u32 losc_freq = 32768;

#ifndef DO_NOT_CALIBRATION
static u32 filter_channel[10] = {0};
static u32 filter_count;
#endif

void dcxo_cali_start(u32 __maybe_unused *bk)
{
#ifndef DO_NOT_CALIBRATION
    u32 calibration_status, xo_ctrl;

    calibration_status = readl(IOSC_CLK_AUTO_CALI);
    xo_ctrl = readl(XO_CTRL);
    writel(readl(XO_CTRL) | (0xa), XO_CTRL);
    writel(0x7, IOSC_CLK_AUTO_CALI);

    bk[0] = calibration_status;
    bk[1] = xo_ctrl;
#endif
}

void dcxo_cali_end(__maybe_unused u32 *bk)
{
#ifndef DO_NOT_CALIBRATION
    u32 calibration_status, xo_ctrl;

    calibration_status = bk[0];
    xo_ctrl = bk[1];

    writel(xo_ctrl, XO_CTRL);
    writel(calibration_status, IOSC_CLK_AUTO_CALI);
#endif
}

void osc_freq_init(void)
{
#ifndef DO_NOT_CALIBRATION
    u32 count = 0;
    u32 value, sum = 0;
    u32 integer, decimal;
    u32 dcxo_status_bk[2] = {0};

    filter_count = 0;
    dcxo_cali_start(dcxo_status_bk);
    time_mdelay(50);

    while (1) {
        count++;
        if (count > 20)
            break;
        value = readl(IOSC_CLK_AUTO_CALI);
        time_mdelay(16);
        integer = (value >> DCXO_CALI_INTEGER_OFFSET);
        decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
        value = integer * losc_freq + (losc_freq  * 65535 / decimal);
        if (value > 24000000 || value < 8000000)
            continue;
        sum = value + sum;
        filter_channel[filter_count] = value;
        filter_count++;
        if (filter_count == 5)
            break;

    }
    if (filter_count == 0)
        iosc_freq = 16000000;
    else
        iosc_freq = sum / filter_count;

    dcxo_cali_end(dcxo_status_bk);
#endif
}

static u32 __maybe_unused filter_sliding(u32 *channel, u32 value, u32 max_count)
{
    u32 *bk, *ch = channel + max_count - 1;
    u32 sum = 0;

    do {
        bk = ch;
        ch--;
        *bk = *ch;
        sum += *bk;
    } while (ch >= (channel + 1));

    *ch = value;
    sum = sum + *ch;

    return sum / max_count;
}

void osc_freq_filter(void)
{
#ifndef DO_NOT_CALIBRATION
    u32 integer, decimal;
    u32 value, sum;

    time_mdelay(16);
    value = readl(IOSC_CLK_AUTO_CALI);
    integer = (value >> DCXO_CALI_INTEGER_OFFSET);
    decimal = (value >> DCXO_CALI_DECIMAL_OFFSET) & 0xffff;
    value = integer * losc_freq + (losc_freq  * 65535 / decimal);

    if (value > 24000000 || value < 8000000)
        return ;

    if (filter_count < 10) {
        sum = iosc_freq * filter_count;
        filter_channel[filter_count] = value;
        sum = sum + value;
        filter_count++;
        iosc_freq = sum / filter_count;
    } else {
        iosc_freq = filter_sliding(&filter_channel[0], value, filter_count);
    }
#endif
}


/*
*********************************************************************************************************
*                                       INITIALIZE CCU
*
* Description:  initialize clock control unit.
*
* Arguments  :  none.
*
* Returns    :  OK if initialize ccu succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_init(void)
{

    /* initialize ccu register address */
    ccu_reg_addr = (struct ccu_reg_list *)R_PRCM_REG_BASE;
    ccu_pll_c0_cpux_reg_addr = (struct ccu_pll_c0_cpux_reg0000 *)CCU_PLL_C0_REG;
    ccu_pll_ddr0_reg_addr = (struct ccu_pll_ddr0_reg0010 *)CCU_PLL_DDR0_REG;
    ccu_pll_periph0_reg_addr = (struct ccu_pll_periph_reg0010 *)CCU_PLL_PERIPH0_REG;
    ccu_pll_audio0_reg_addr = (struct ccu_pll_audio0_reg0020 *)CCU_PLL_AUDIO0_REG;
    /* ccu_pll_periph1_reg_addr = (struct ccu_pll_periph1_reg0028 *)CCU_PLL_PERIPH1_REG; */
#ifndef CFG_FPGA_PLATFORM
    /* setup cpus post div source to 200M(CCU_CPUS_POST_DIV) */
    /* FIXME: board in fix */
    /* u32 value;
    value = (ccu_get_sclk_freq(CCU_SYS_CLK_PLL3)) / CCU_CPUS_POST_DIV;
    if (value < 1) {
        [>to avoid PLL5 freq less than CCU_CPUS_POST_DIV<]
        value = 1;
    }
    ccu_reg_addr->cpus_clk_cfg.factor_m = value - 1;
    [>set ar100 clock source to PLL5<]
    ccu_set_mclk_src(CCU_MOD_CLK_CPUS, CCU_SYS_CLK_PLL3); */
#endif

    /* initialize apb notifier list */
    apbs2_notifier_head = NULL;

    /* ccu initialize succeeded */
    return OK;
}

/*
*********************************************************************************************************
*                                       EXIT CCU
*
* Description:  exit clock control unit.
*
* Arguments  :  none.
*
* Returns    :  OK if exit ccu succeeded, others if failed.
*********************************************************************************************************
*/
s32 ccu_exit(void)
{
    ccu_pll_c0_cpux_reg_addr = NULL;
    ccu_reg_addr = NULL;
    ccu_pll_periph0_reg_addr = NULL;
    ccu_pll_audio0_reg_addr = NULL;

    return OK;
}

void write_rtc_domain_reg(u32 reg, u32 value)
{
    writel((unsigned long)value, (unsigned long)reg);
}

u32 read_rtc_domain_reg(u32 reg)
{
    return readl((unsigned long)reg);
}