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
10 if (sizeof(x) == sizeof(long)) { \
\r
12 ret = (__x < 0) ? -__x : __x; \
\r
15 ret = (__x < 0) ? -__x : __x; \
\r
22 static inline int fls(int x)
\r
26 asm("clz\t%0, %1": "=r"(ret):"r"(x));
\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
35 #define MEASURE_TIMES (15)
\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
41 /********************* Get vlaue from adc *****************************/
\r
42 #define MEASURE_TIMES (15)
\r
43 #define RATIO(_n_, _d_) (_n_ << 16 | _d_)
\r
45 static void bubble_sort(int a[], int N)
\r
48 for (i = 0; i < N - 1; i++) {
\r
49 for (j = i + 1; j < N; j++) {
\r
59 int sci_adc_request(int channel)
\r
62 static int results[MEASURE_TIMES];
\r
64 if (-1 == ADC_GetValues(channel, false, MEASURE_TIMES, results)) {
\r
68 bubble_sort(results, MEASURE_TIMES);
\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
77 return results[MEASURE_TIMES / 2];
\r
80 int sci_is_match_chip_id(u32 id)
\r
82 static u32 metalfix = 0;
\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
89 int sci_adc_ratio(int channel)
\r
92 case ADC_CHANNEL_VBAT:
\r
93 if (sci_is_match_chip_id(0x8820A001 /*2712AD */ ))
\r
94 return RATIO(7, 29);
\r
96 return RATIO(8, 30);
\r
97 case ADC_CHANNEL_DCDCCORE:
\r
98 case ADC_CHANNEL_DCDCARM:
\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
110 return RATIO(1, 1);
\r
112 return RATIO(1, 1);
\r
115 int sci_adc_vol_request(int channel)
\r
118 adc_res = sci_adc_request(channel);
\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
130 /********************* Ldo ***************************************/
\r
132 #define __section(S) __attribute__ ((__section__(#S)))
\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
139 const u32 __init0 __init_begin = 0xeeeebbbb;
\r
140 const u32 __init2 __init_end = 0xddddeeee;
\r
142 struct regulator_regs {
\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
152 struct regulator_desc {
\r
155 const struct regulator_regs *regs;
\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
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
175 struct regulator_desc __init1 DESC_##VDD = { \
\r
178 .regs = ®S_##VDD, \
\r
181 #include "__sc8825_regulator_map.h"
\r
183 int sci_ldo_turn_on(struct regulator_desc *desc)
\r
185 const struct regulator_regs *regs = desc->regs;
\r
186 if (!regs->pd_rst || !regs->pd_set)
\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
193 void *sci_ldo_request(const char *name)
\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
205 static int __match_dcdc_vol(const struct regulator_regs *regs, u32 vol)
\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
219 int dcdc_set_voltage(struct regulator_desc *desc, int ctl_vol, int to_vol)
\r
221 const struct regulator_regs *regs = desc->regs;
\r
223 int i, shft = __ffs(regs->vol_ctl_bits);
\r
224 int max = regs->vol_ctl_bits >> shft;
\r
227 if (!regs->vol_ctl || !regs->vol_trm)
\r
230 trim = (sci_adi_read(regs->vol_trm) & regs->vol_trm_bits) >> 0;
\r
232 printf("already set trimming\n");
\r
236 /* found the closely vol ctrl bits */
\r
237 i = __match_dcdc_vol(regs, mv);
\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
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
250 sci_adi_write(regs->vol_trm,
\r
251 (BITS_DCDC_CAL(j) | (BITS_DCDC_CAL_RST(BITS_DCDC_CAL(-1) - j))), -1);
\r
254 sci_adi_write(regs->vol_ctl, i | (max - i) << 4, -1);
\r
258 int sci_ldo_trimming(struct regulator_desc *desc, int ctl_vol, int to_vol)
\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
265 if (2 /*DCDC*/ == desc->regs->typ)
\r
266 return dcdc_set_voltage(desc, ctl_vol, to_vol);
\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
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
280 if (trim != 0x10 /* 100 % */ ) {
\r
281 sci_adi_write(regs->vol_trm, trim << __ffs(regs->vol_trm_bits), regs->vol_trm_bits);
\r
287 int sci_ldo_calibrate(struct regulator_desc *desc, int adc_chan, int def_vol, int to_vol, int is_cal)
\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
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
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
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
306 } else if (!is_cal)
\r
309 /* always set valid vol ctrl and trim bits */
\r
310 ctl_vol = DIV_ROUND(def_vol * to_vol, adc_vol);
\r
312 ret = sci_ldo_trimming(desc, ctl_vol, to_vol);
\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
322 printf("%s %s failure\n", __FUNCTION__, desc->name);
\r
325 /******************* Calibration ************************************/
\r
326 int dcdc_set_def_volt(struct regulator_desc *desc, int def_vol)
\r
328 const struct regulator_regs *regs = desc->regs;
\r
330 int i, shft = __ffs(regs->vol_ctl_bits);
\r
331 int max = regs->vol_ctl_bits >> shft;
\r
333 if (!regs->vol_ctl || !regs->vol_trm)
\r
336 mv = (def_vol / 100) * 100;
\r
337 /* found the closely vol ctrl bits */
\r
338 i = __match_dcdc_vol(regs, mv);
\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
346 /* set ctl value */
\r
347 sci_adi_write(regs->vol_ctl, i | (max - i) << 4, -1);
\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
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
368 int DCDC_Cal_ArmCore(void)
\r
374 ret = (u32) sci_adc_ratio(ADC_CHANNEL_VBAT);
\r
375 bat_numerators = ret >> 16;
\r
376 bat_denominators = ret & 0xffff;
\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
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
391 printf("regu (%s) default on\n", name);
\r
393 ret = sci_ldo_turn_on(desc);
\r
394 printf("regu (%s) turn on\n", name);
\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
403 /* calibrate ldo throught adc channel feedback */
\r
404 ret = sci_ldo_calibrate(desc, adc_chan, def_vol, to_vol, 1);
\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