tizen 2.4 release
[kernel/u-boot-tm1.git] / arch / arm / cpu / armv7 / sc8825 / dcdc_cal.c
1 #include <common.h>\r
2 #include <asm/arch/hardware.h>\r
3 #include <asm/arch/adc_drvapi.h>\r
4 #include <asm/arch/regs_glb.h>\r
5 #include <asm/arch/regs_ana_glb.h>\r
6 #include <asm/arch/adi.h>\r
7 \r
8 #define abs(x) ({                                                                       \\r
9                                 long ret;                                                       \\r
10                                 if (sizeof(x) == sizeof(long)) {                \\r
11                                         long __x = (x);                         \\r
12                                         ret = (__x < 0) ? -__x : __x;           \\r
13                                 } else {                                                        \\r
14                                         int __x = (x);                                  \\r
15                                         ret = (__x < 0) ? -__x : __x;           \\r
16                                 }                                                               \\r
17                                 ret;                                                            \\r
18                         })\r
19 #undef ffs\r
20 #undef fls\r
21 \r
22 static inline int fls(int x)\r
23 {\r
24         int ret;\r
25 \r
26         asm("clz\t%0, %1": "=r"(ret):"r"(x));\r
27         ret = 32 - ret;\r
28         return ret;\r
29 }\r
30 #define __fls(x)                                                        (fls(x) - 1)\r
31 #define ffs(x)                                                          ({ unsigned long __t = (x); fls(__t & -__t); })\r
32 #define __ffs(x)                                                        (ffs(x) - 1)\r
33 #define ffz(x)                                                          __ffs( ~(x) )\r
34 \r
35 #define MEASURE_TIMES                                   (15)\r
36 \r
37 static unsigned int  bat_numerators, bat_denominators = 0;\r
38 extern uint16_t sprdbat_auxadc2vbatvol (uint16_t adcvalue);\r
39 extern void udelay(unsigned long usec);\r
40 \r
41 /*********************     Get vlaue from adc     *****************************/\r
42 #define MEASURE_TIMES                                   (15)\r
43 #define RATIO(_n_, _d_)                                 (_n_ << 16 | _d_)\r
44 \r
45 static void bubble_sort(int a[], int N)\r
46 {\r
47         int i, j, t;\r
48         for (i = 0; i < N - 1; i++) {\r
49                 for (j = i + 1; j < N; j++) {\r
50                         if (a[i] > a[j]) {\r
51                                 t = a[i];\r
52                                 a[i] = a[j];\r
53                                 a[j] = t;\r
54                         }\r
55                 }\r
56         }\r
57 }\r
58 \r
59 int sci_adc_request(int channel)\r
60 {\r
61         int i;\r
62         static int results[MEASURE_TIMES];\r
63 \r
64         if (-1 == ADC_GetValues(channel, false, MEASURE_TIMES, results)) {\r
65                 return 0;\r
66         }\r
67 \r
68         bubble_sort(results, MEASURE_TIMES);\r
69 \r
70         /* dump results */\r
71         //printf("channel is %d:\r\n",channel);\r
72         for (i = 0; i < MEASURE_TIMES; i++) {\r
73                 printf("%d ", results[i]);\r
74         }\r
75         printf("\r\n");\r
76 \r
77         return results[MEASURE_TIMES / 2];\r
78 }\r
79 \r
80 int sci_is_match_chip_id(u32 id)\r
81 {\r
82         static u32 metalfix = 0;\r
83         if (!metalfix)\r
84                 metalfix = sci_adi_read(ANA_REG_GLB_CHIP_ID_LOW) |\r
85                     (sci_adi_read(ANA_REG_GLB_CHIP_ID_HIGH) << 16);\r
86         return id == metalfix;\r
87 }\r
88 \r
89 int sci_adc_ratio(int channel)\r
90 {\r
91         switch (channel) {\r
92         case ADC_CHANNEL_VBAT:\r
93                 if (sci_is_match_chip_id(0x8820A001 /*2712AD */ ))\r
94                         return RATIO(7, 29);\r
95                 else\r
96                         return RATIO(8, 30);\r
97         case ADC_CHANNEL_DCDCCORE:\r
98         case ADC_CHANNEL_DCDCARM:\r
99                 return RATIO(4, 5);\r
100         case ADC_CHANNEL_DCDCMEM:\r
101                 return RATIO(3, 5);\r
102         case ADC_CHANNEL_DCDCLDO:\r
103                 return RATIO(4, 9);\r
104         case ADC_CHANNEL_LDO0:\r
105         case ADC_CHANNEL_LDO1:\r
106                 return RATIO(1, 3);\r
107         case ADC_CHANNEL_LDO2:\r
108                 return RATIO(1, 2);\r
109         default:\r
110                 return RATIO(1, 1);\r
111         }\r
112         return RATIO(1, 1);\r
113 }\r
114 \r
115 int sci_adc_vol_request(int channel)\r
116 {\r
117         int adc_res;\r
118         adc_res = sci_adc_request(channel);\r
119         if (adc_res) {\r
120                 u32 res, chan_numerators, chan_denominators;\r
121                 res = (u32) sci_adc_ratio(channel);\r
122                 chan_numerators = res >> 16;\r
123                 chan_denominators = res & 0xffff;\r
124                 return sprdbat_auxadc2vbatvol(adc_res)\r
125                     * (bat_numerators * chan_denominators)\r
126                     / (bat_denominators * chan_numerators);\r
127         }\r
128         return 0;\r
129 }\r
130 /*********************      Ldo      ***************************************/\r
131 #ifndef __section\r
132 #define __section(S) __attribute__ ((__section__(#S)))\r
133 #endif\r
134 \r
135 #define __init0 __section(.data.regu.init0)\r
136 #define __init1 __section(.data.regu.init1)\r
137 #define __init2 __section(.data.regu.init2)\r
138 \r
139 const u32 __init0 __init_begin = 0xeeeebbbb;\r
140 const u32 __init2 __init_end = 0xddddeeee;\r
141 \r
142 struct regulator_regs {\r
143         int typ;\r
144         u32 pd_set, pd_set_bit;\r
145         u32 pd_rst, pd_rst_bit;\r
146         u32 slp_ctl, slp_ctl_bit;\r
147         u32 vol_trm, vol_trm_bits;\r
148         u32 vol_ctl, vol_ctl_bits;\r
149         u32 vol_sel_cnt, vol_sel[];\r
150 };\r
151 \r
152 struct regulator_desc {\r
153         int id;\r
154         const char *name;\r
155         const struct regulator_regs *regs;\r
156 };\r
157 \r
158 #define SCI_REGU_REG(VDD, TYP, PD_SET, SET_BIT, PD_RST, RST_BIT, SLP_CTL, SLP_CTL_BIT, \\r
159                      VOL_TRM, VOL_TRM_BITS, VOL_CTL, VOL_CTL_BITS, VOL_SEL_CNT, ...)    \\r
160         static const struct regulator_regs REGS_##VDD = {                                                               \\r
161                 .typ = TYP,                                                     \\r
162                 .pd_set = PD_SET,                                               \\r
163                 .pd_set_bit = SET_BIT,                                  \\r
164                 .pd_rst = PD_RST,                                               \\r
165                 .pd_rst_bit = RST_BIT,                                  \\r
166                 .slp_ctl = SLP_CTL,                                             \\r
167                 .slp_ctl_bit = SLP_CTL_BIT,                             \\r
168                 .vol_trm = VOL_TRM,                                     \\r
169                 .vol_trm_bits = VOL_TRM_BITS,                   \\r
170                 .vol_ctl = VOL_CTL,                                     \\r
171                 .vol_ctl_bits = VOL_CTL_BITS,                   \\r
172                 .vol_sel_cnt = VOL_SEL_CNT,                     \\r
173                 .vol_sel = {__VA_ARGS__},                               \\r
174         };                                                                              \\r
175         struct regulator_desc __init1 DESC_##VDD = {    \\r
176                 .id = -1,                                                               \\r
177                 .name = #VDD,                                           \\r
178                 .regs = &REGS_##VDD,                                    \\r
179         };                                                                              \\r
180 \r
181 #include "__sc8825_regulator_map.h"\r
182 \r
183 int sci_ldo_turn_on(struct regulator_desc *desc)\r
184 {\r
185         const struct regulator_regs *regs = desc->regs;\r
186         if (!regs->pd_rst || !regs->pd_set)\r
187                 return -1;\r
188         sci_adi_set(regs->pd_rst, regs->pd_rst_bit);\r
189         sci_adi_clr(regs->pd_set, regs->pd_set_bit);\r
190         return 0;\r
191 }\r
192 \r
193 void *sci_ldo_request(const char *name)\r
194 {\r
195         struct regulator_desc *desc = (struct regulator_desc *)(&__init_begin + 1);\r
196         while (desc < (struct regulator_desc *)&__init_end) {\r
197                 if (0 == strcmp(name, desc->name)) {\r
198                         return desc;\r
199                 }\r
200                 desc++;\r
201         }\r
202         return (void *)-1;\r
203 }\r
204 \r
205 static int __match_dcdc_vol(const struct regulator_regs *regs, u32 vol)\r
206 {\r
207         int i, j = -1;\r
208         int ds, min_ds = 100;   /* mV, the max range of small voltage */\r
209         for (i = 0; i < regs->vol_sel_cnt; i++) {\r
210                 ds = vol - regs->vol_sel[i];\r
211                 if (ds >= 0 && ds < min_ds) {\r
212                         min_ds = ds;\r
213                         j = i;\r
214                 }\r
215         }\r
216         return j;\r
217 }\r
218 \r
219 int dcdc_set_voltage(struct regulator_desc *desc, int ctl_vol, int to_vol)\r
220 {\r
221         const struct regulator_regs *regs = desc->regs;\r
222         int mv = ctl_vol;\r
223         int i, shft = __ffs(regs->vol_ctl_bits);\r
224         int max = regs->vol_ctl_bits >> shft;\r
225         u32 trim;\r
226 \r
227         if (!regs->vol_ctl || !regs->vol_trm)\r
228                 return -1;\r
229 \r
230         trim = (sci_adi_read(regs->vol_trm) & regs->vol_trm_bits) >> 0;\r
231         if (trim != 0) {\r
232                 printf("already set trimming\n");\r
233                 return 0;\r
234         }\r
235 \r
236         /* found the closely vol ctrl bits */\r
237         i = __match_dcdc_vol(regs, mv);\r
238         if (i < 0)\r
239                 return -1;\r
240 \r
241         printf("dcdc (%s) %d = %d +%d mv\n", desc->name, \r
242                 mv, regs->vol_sel[i], mv - regs->vol_sel[i]);\r
243         {\r
244                 /* dcdc calibration control bits (default 00000),\r
245                 * small adjust voltage: 100/32mv ~= 3.125mv */\r
246                 int j = DIV_ROUND((mv - regs->vol_sel[i]) * 32, 100) ;\r
247                 if (j >= 32) {\r
248                         j = 31;\r
249                 }\r
250                 sci_adi_write(regs->vol_trm,\r
251                         (BITS_DCDC_CAL(j) | (BITS_DCDC_CAL_RST(BITS_DCDC_CAL(-1) - j))), -1);\r
252         }\r
253 \r
254         sci_adi_write(regs->vol_ctl, i | (max - i) << 4, -1);\r
255         return 0;\r
256 }\r
257 \r
258 int sci_ldo_trimming(struct regulator_desc *desc, int ctl_vol, int to_vol)\r
259 {\r
260         int cal_vol = ctl_vol - to_vol * 90 / 100;      /* cal range 90% ~ 110% */\r
261         const struct regulator_regs *regs = desc->regs;\r
262         int shft = __ffs(regs->vol_trm_bits);\r
263         u32 trim;\r
264 \r
265         if (2 /*DCDC*/ == desc->regs->typ)\r
266                 return dcdc_set_voltage(desc, ctl_vol, to_vol);\r
267 \r
268         trim = (sci_adi_read(regs->vol_trm) &  regs->vol_trm_bits) >> shft;\r
269         if (trim != 0x10 /* 100 % */ ) {\r
270                 printf("already set trimming\n");\r
271                 goto exit;\r
272         }\r
273 \r
274         /* assert 5 valid trim bits */\r
275         trim = DIV_ROUND(cal_vol * 100 * 32, to_vol * 20) & 0x1f;\r
276         printf("ldo (%s) trimming %u = %u %+dmv, got [%02X] %u.%03u%%\n",\r
277                 desc->name, ctl_vol, to_vol, (cal_vol - to_vol / 10), trim, ctl_vol * 100 / to_vol,\r
278                 (ctl_vol * 100 * 1000 / to_vol) % 1000);\r
279 \r
280         if (trim != 0x10 /* 100 % */ ) {\r
281                 sci_adi_write(regs->vol_trm, trim << __ffs(regs->vol_trm_bits), regs->vol_trm_bits);\r
282         }\r
283 exit:\r
284         return 0;\r
285 }\r
286 \r
287 int sci_ldo_calibrate(struct regulator_desc *desc, int adc_chan, int def_vol, int to_vol, int is_cal)\r
288 {\r
289         int ret, cal_vol, ctl_vol, adc_vol;\r
290         adc_vol = sci_adc_vol_request(adc_chan);\r
291         cal_vol = abs(adc_vol - to_vol);\r
292 \r
293         printf("%s default %dmv, from %dmv to %dmv, %c%d.%02d%%, vbat %d\n",\r
294                __FUNCTION__, def_vol, adc_vol, to_vol,\r
295                (adc_vol > to_vol) ? '+' : '-', cal_vol * 100 / to_vol,\r
296                cal_vol * 100 * 100 / to_vol % 100, sci_adc_vol_request(ADC_CHANNEL_VBAT));\r
297 \r
298         if (adc_vol && def_vol && to_vol) {\r
299                 cal_vol = abs(adc_vol - to_vol);\r
300                 if (cal_vol > to_vol / 10)      /* adjust limit 10% */\r
301                         goto exit;\r
302                 else if (cal_vol < to_vol / 100 || (adc_vol > to_vol && cal_vol < to_vol * 15 / 1000)) {\r
303                         /* margin 1% ~ 1.5% */\r
304                         printf("%s %s is ok\n\n", __FUNCTION__, desc->name);\r
305                         return 0;\r
306                 } else if (!is_cal)\r
307                         goto exit;\r
308 \r
309                 /* always set valid vol ctrl and trim bits */\r
310                 ctl_vol = DIV_ROUND(def_vol * to_vol, adc_vol);\r
311 \r
312                 ret = sci_ldo_trimming(desc, ctl_vol, to_vol);\r
313                 if (ret == 0) {\r
314                         udelay(10 * 1000);      /* wait a moment before cal verify */\r
315                         sci_ldo_calibrate(desc, adc_chan, ctl_vol, to_vol, 0);\r
316                 }\r
317 \r
318                 return ctl_vol;\r
319         }\r
320 \r
321 exit:\r
322         printf("%s %s failure\n", __FUNCTION__, desc->name);\r
323         return -1;\r
324 }\r
325 /*******************      Calibration      ************************************/\r
326 int dcdc_set_def_volt(struct regulator_desc *desc, int def_vol)\r
327 {\r
328         const struct regulator_regs *regs = desc->regs;\r
329         u32 mv;\r
330         int i, shft = __ffs(regs->vol_ctl_bits);\r
331         int max = regs->vol_ctl_bits >> shft;\r
332 \r
333         if (!regs->vol_ctl || !regs->vol_trm)\r
334                 return -1;\r
335 \r
336         mv = (def_vol / 100) * 100;\r
337         /* found the closely vol ctrl bits */\r
338         i = __match_dcdc_vol(regs, mv);\r
339         if (i < 0)\r
340                 return -1;\r
341 \r
342         /* clear trm register value */\r
343         sci_adi_write(regs->vol_trm,\r
344                         (BITS_DCDC_CAL(0) | (BITS_DCDC_CAL_RST(BITS_DCDC_CAL(-1)))), -1);\r
345 \r
346         /* set ctl value */\r
347         sci_adi_write(regs->vol_ctl, i | (max - i) << 4, -1);\r
348         return 0;\r
349 }\r
350 \r
351 struct ldo_map {\r
352         const char *name;       /* a-die DCDC or LDO name */\r
353         int def_on;             /* 1: default ON, 0: default OFF */\r
354         u32 def_vol;            /* default voltage (mV), could not read from a-die */\r
355         u32 to_vol;             /* want to set voltage write in this */\r
356         u32 cal_sel;            /* only one ldo cal can be enable at the same time */\r
357         int adc_chan;           /* multiplexed adc-channel id for LDOs */\r
358 };\r
359 #define BITS_LDO_VSIM_CAL_EN(_x_)                               ( (_x_) & (BIT(3)|BIT(5)) )\r
360 struct ldo_map ldo_map[] = {\r
361         {"vddcore", 1, 1100, 1100, 0, ADC_CHANNEL_DCDCCORE},\r
362         {"vddarm", 1, 1200, 1200, 0, ADC_CHANNEL_DCDCARM},\r
363         {"vddmem", 1, 1200, 1200, 0, ADC_CHANNEL_DCDCMEM},      /*DDR2 */\r
364 //      {"vddmem1", 0, 1800, 0, ADC_CHANNEL_DCDCMEM},       /*DDR1 */\r
365         {"vddsim0", 0, 1800, 1800, BITS_LDO_VSIM_CAL_EN(-1), ADC_CHANNEL_LDO1},\r
366 };\r
367 \r
368 int DCDC_Cal_ArmCore(void)\r
369 {\r
370         int i, ret;\r
371 \r
372         ADC_Init();\r
373 \r
374         ret = (u32) sci_adc_ratio(ADC_CHANNEL_VBAT);\r
375         bat_numerators = ret >> 16;\r
376         bat_denominators = ret & 0xffff;\r
377         \r
378         /* four DCDCs and all LDOs Trimming if default ON or not */\r
379         for (i = 0; i < (sizeof(ldo_map) / sizeof(ldo_map[0])); i++) {\r
380                 int adc_chan = ldo_map[i].adc_chan;\r
381                 int def_vol = ldo_map[i].def_vol;\r
382                 int to_vol = ldo_map[i].to_vol;\r
383                 const char *name = ldo_map[i].name;\r
384                 struct regulator_desc *desc = sci_ldo_request(name);\r
385 \r
386                 /* turn on ldo at first */\r
387                 if (ldo_map[i].def_on) {\r
388                         if (2 /*DCDC*/ == desc->regs->typ) {\r
389                                 dcdc_set_def_volt(desc, ldo_map[i].def_vol);\r
390                         }\r
391                         printf("regu (%s) default on\n", name);\r
392                 } else {\r
393                         ret = sci_ldo_turn_on(desc);\r
394                         printf("regu (%s) turn on\n", name);\r
395                 }\r
396 \r
397                 /* enable ldo cal before adc sampling and ldo calibration */\r
398                 if (0 != ldo_map[i].cal_sel) {\r
399                         sci_adi_write((ANA_REGS_GLB2_BASE + 0x24),\r
400                                       ldo_map[i].cal_sel, -1);\r
401                 }\r
402 \r
403                 /* calibrate ldo throught adc channel feedback */\r
404                 ret = sci_ldo_calibrate(desc, adc_chan, def_vol, to_vol, 1);\r
405 \r
406                 /* close ldo cal */\r
407                 if (0 != ldo_map[i].cal_sel) {\r
408                         sci_adi_write((ANA_REGS_GLB2_BASE + 0x24), 0, -1);\r
409                 }\r
410         }\r
411         return 0;\r
412 }\r
413 \r