1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2011 by Amaury Pouly
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
27 #include "system-target.h"
28 #include "power-imx233.h"
29 #include "pinctrl-imx233.h"
30 #include "fmradio_i2c.h"
32 struct current_step_bit_t
38 /* in decreasing order */
39 static struct current_step_bit_t g_charger_current_bits
[] =
41 { 400, HW_POWER_CHARGE__BATTCHRG_I__400mA
},
42 { 200, HW_POWER_CHARGE__BATTCHRG_I__200mA
},
43 { 100, HW_POWER_CHARGE__BATTCHRG_I__100mA
},
44 { 50, HW_POWER_CHARGE__BATTCHRG_I__50mA
},
45 { 20, HW_POWER_CHARGE__BATTCHRG_I__20mA
},
46 { 10, HW_POWER_CHARGE__BATTCHRG_I__10mA
}
49 /* in decreasing order */
50 static struct current_step_bit_t g_charger_stop_current_bits
[] =
52 { 100, HW_POWER_CHARGE__STOP_ILIMIT__100mA
},
53 { 50, HW_POWER_CHARGE__STOP_ILIMIT__50mA
},
54 { 20, HW_POWER_CHARGE__STOP_ILIMIT__20mA
},
55 { 10, HW_POWER_CHARGE__STOP_ILIMIT__10mA
}
58 /* in decreasing order */
59 static struct current_step_bit_t g_4p2_charge_limit_bits
[] =
61 { 400, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__400mA
},
62 { 200, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__200mA
},
63 { 100, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__100mA
},
64 { 50, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__50mA
},
65 { 20, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__20mA
},
66 { 10, HW_POWER_5VCTRL__CHARGE_4P2_ILIMIT__10mA
}
71 if(HW_POWER_CTRL
& HW_POWER_CTRL__VBUSVALID_IRQ
)
73 if(HW_POWER_STS
& HW_POWER_STS__VBUSVALID
)
77 /* reverse polarity */
78 __REG_TOG(HW_POWER_CTRL
) = HW_POWER_CTRL__POLARITY_VBUSVALID
;
80 __REG_CLR(HW_POWER_CTRL
) = HW_POWER_CTRL__VBUSVALID_IRQ
;
86 /* setup vbusvalid parameters: set threshold to 4v and power up comparators */
87 __REG_CLR(HW_POWER_5VCTRL
) = HW_POWER_5VCTRL__VBUSVALID_TRSH_BM
;
88 __REG_SET(HW_POWER_5VCTRL
) = HW_POWER_5VCTRL__VBUSVALID_TRSH_4V
|
89 HW_POWER_5VCTRL__PWRUP_VBUS_CMPS
;
90 /* enable vbusvalid detection method for the dcdc (improves efficiency) */
91 __REG_SET(HW_POWER_5VCTRL
) = HW_POWER_5VCTRL__VBUSVALID_5VDETECT
;
92 /* clear vbusvalid irq and set correct polarity */
93 __REG_CLR(HW_POWER_CTRL
) = HW_POWER_CTRL__VBUSVALID_IRQ
;
94 if(HW_POWER_STS
& HW_POWER_STS__VBUSVALID
)
95 __REG_CLR(HW_POWER_CTRL
) = HW_POWER_CTRL__POLARITY_VBUSVALID
;
97 __REG_SET(HW_POWER_CTRL
) = HW_POWER_CTRL__POLARITY_VBUSVALID
;
98 __REG_SET(HW_POWER_CTRL
) = HW_POWER_CTRL__ENIRQ_VBUS_VALID
;
99 imx233_icoll_enable_interrupt(INT_SRC_VDD5V
, true);
100 /* setup linear regulator offsets to 25 mV below to prevent contention between
101 * linear regulators and DCDC */
102 __FIELD_SET(HW_POWER_VDDDCTRL
, LINREG_OFFSET
, 2);
103 __FIELD_SET(HW_POWER_VDDACTRL
, LINREG_OFFSET
, 2);
104 __FIELD_SET(HW_POWER_VDDIOCTRL
, LINREG_OFFSET
, 2);
105 /* enable DCDC (more efficient) */
106 __REG_SET(HW_POWER_5VCTRL
) = HW_POWER_5VCTRL__ENABLE_DCDC
;
107 /* enable a few bits controlling the DC-DC as recommended by Freescale */
108 __REG_SET(HW_POWER_LOOPCTRL
) = HW_POWER_LOOPCTRL__TOGGLE_DIF
|
109 HW_POWER_LOOPCTRL__EN_CM_HYST
;
110 __FIELD_SET(HW_POWER_LOOPCTRL
, EN_RCSCALE
, HW_POWER_LOOPCTRL__EN_RCSCALE__2X
);
115 /* wait a bit, useful for the user to stop touching anything */
117 #ifdef SANSA_FUZEPLUS
118 /* This pin seems to be important to shutdown the hardware properly */
119 imx233_pinctrl_acquire_pin(0, 9, "power off");
120 imx233_set_pin_function(0, 9, PINCTRL_FUNCTION_GPIO
);
121 imx233_enable_gpio_output(0, 9, true);
122 imx233_set_gpio_output(0, 9, true);
125 HW_POWER_RESET
= HW_POWER_RESET__UNLOCK
| HW_POWER_RESET__PWD
;
129 unsigned int power_input_status(void)
131 return (usb_detect() == USB_INSERTED
)
132 ? POWER_INPUT_MAIN_CHARGER
: POWER_INPUT_NONE
;
135 bool charging_state(void)
137 return HW_POWER_STS
& HW_POWER_STS__CHRGSTS
;
140 void imx233_power_set_charge_current(unsigned current
)
142 __REG_CLR(HW_POWER_CHARGE
) = HW_POWER_CHARGE__BATTCHRG_I_BM
;
143 /* find closest current LOWER THAN OR EQUAL TO the expected current */
144 for(unsigned i
= 0; i
< ARRAYLEN(g_charger_current_bits
); i
++)
145 if(current
>= g_charger_current_bits
[i
].current
)
147 current
-= g_charger_current_bits
[i
].current
;
148 __REG_SET(HW_POWER_CHARGE
) = g_charger_current_bits
[i
].bit
;
152 void imx233_power_set_stop_current(unsigned current
)
154 __REG_CLR(HW_POWER_CHARGE
) = HW_POWER_CHARGE__STOP_ILIMIT_BM
;
155 /* find closest current GREATHER THAN OR EQUAL TO the expected current */
157 for(unsigned i
= 0; i
< ARRAYLEN(g_charger_stop_current_bits
); i
++)
158 sum
+= g_charger_stop_current_bits
[i
].current
;
159 for(unsigned i
= 0; i
< ARRAYLEN(g_charger_stop_current_bits
); i
++)
161 sum
-= g_charger_stop_current_bits
[i
].current
;
164 current
-= g_charger_stop_current_bits
[i
].current
;
165 __REG_SET(HW_POWER_CHARGE
) = g_charger_stop_current_bits
[i
].bit
;
171 #define HAS_BO (1 << 0)
172 #define HAS_LINREG (1 << 1)
173 #define HAS_LINREG_OFFSET (1 << 2)
178 volatile uint32_t *reg
;
179 uint32_t trg_bm
, trg_bp
; // bitmask and bitpos
181 uint32_t bo_bm
, bo_bp
; // bitmask and bitpos
183 uint32_t linreg_offset_bm
, linreg_offset_bp
; // bitmask and bitpos
186 #define ADD_REGULATOR(name, mask) \
187 .min = HW_POWER_##name##CTRL__TRG_MIN, \
188 .step = HW_POWER_##name##CTRL__TRG_STEP, \
189 .reg = &HW_POWER_##name##CTRL, \
190 .trg_bm = HW_POWER_##name##CTRL__TRG_BM, \
191 .trg_bp = HW_POWER_##name##CTRL__TRG_BP, \
193 #define ADD_REGULATOR_BO(name) \
194 .bo_bm = HW_POWER_##name##CTRL__BO_OFFSET_BM, \
195 .bo_bp = HW_POWER_##name##CTRL__BO_OFFSET_BP
196 #define ADD_REGULATOR_LINREG(name) \
197 .linreg_bm = HW_POWER_##name##CTRL__ENABLE_LINREG
198 #define ADD_REGULATOR_LINREG_OFFSET(name) \
199 .linreg_offset_bm = HW_POWER_##name##CTRL__LINREG_OFFSET_BM, \
200 .linreg_offset_bp = HW_POWER_##name##CTRL__LINREG_OFFSET_BP
203 ADD_REGULATOR(VDDD
, HAS_BO
|HAS_LINREG
|HAS_LINREG_OFFSET
),
204 ADD_REGULATOR_BO(VDDD
),
205 ADD_REGULATOR_LINREG(VDDD
),
206 ADD_REGULATOR_LINREG_OFFSET(VDDD
)
210 ADD_REGULATOR(VDDA
, HAS_BO
|HAS_LINREG
|HAS_LINREG_OFFSET
),
211 ADD_REGULATOR_BO(VDDA
),
212 ADD_REGULATOR_LINREG(VDDA
),
213 ADD_REGULATOR_LINREG_OFFSET(VDDA
)
217 ADD_REGULATOR(VDDIO
, HAS_BO
|HAS_LINREG_OFFSET
),
218 ADD_REGULATOR_BO(VDDIO
),
219 ADD_REGULATOR_LINREG_OFFSET(VDDIO
)
223 ADD_REGULATOR(VDDMEM
, HAS_LINREG
),
224 ADD_REGULATOR_LINREG(VDDMEM
),
228 void imx233_power_get_regulator(enum imx233_regulator_t reg
, unsigned *value_mv
,
229 unsigned *brownout_mv
)
231 uint32_t reg_val
= *regulator_info
[reg
].reg
;
232 /* read target value */
233 unsigned raw_val
= (reg_val
& regulator_info
[reg
].trg_bm
) >> regulator_info
[reg
].trg_bp
;
234 /* convert it to mv */
236 *value_mv
= regulator_info
[reg
].min
+ regulator_info
[reg
].step
* raw_val
;
237 if(regulator_info
[reg
].flags
& HAS_BO
)
239 /* read brownout offset */
240 unsigned raw_bo
= (reg_val
& regulator_info
[reg
].bo_bm
) >> regulator_info
[reg
].bo_bp
;
241 /* convert it to mv */
243 *brownout_mv
= regulator_info
[reg
].min
+ regulator_info
[reg
].step
* (raw_val
- raw_bo
);
249 void imx233_power_set_regulator(enum imx233_regulator_t reg
, unsigned value_mv
,
250 unsigned brownout_mv
)
252 // compute raw values
253 unsigned raw_val
= (value_mv
- regulator_info
[reg
].min
) / regulator_info
[reg
].step
;
254 unsigned raw_bo_offset
= (value_mv
- brownout_mv
) / regulator_info
[reg
].step
;
255 // clear dc-dc ok flag
256 __REG_SET(HW_POWER_CTRL
) = HW_POWER_CTRL__DC_OK_IRQ
;
258 uint32_t reg_val
= (*regulator_info
[reg
].reg
) & ~regulator_info
[reg
].trg_bm
;
259 reg_val
|= raw_val
<< regulator_info
[reg
].trg_bp
;
260 if(regulator_info
[reg
].flags
& HAS_BO
)
262 reg_val
&= ~regulator_info
[reg
].bo_bm
;
263 reg_val
|= raw_bo_offset
<< regulator_info
[reg
].bo_bp
;
265 *regulator_info
[reg
].reg
= reg_val
;
266 /* Wait until regulator is stable (ie brownout condition is gone)
267 * If DC-DC is used, we can use the DCDC_OK irq
268 * Otherwise it is unreliable (doesn't work when lowering voltage on linregs)
269 * It usually takes between 0.5ms and 2.5ms */
270 if(!(HW_POWER_5VCTRL
& HW_POWER_5VCTRL__ENABLE_DCDC
))
271 panicf("regulator %d: wait for voltage stabilize in linreg mode !", reg
);
272 unsigned timeout
= current_tick
+ (HZ
* 20) / 1000;
273 while(!(HW_POWER_CTRL
& HW_POWER_CTRL__DC_OK_IRQ
) || !TIME_AFTER(current_tick
, timeout
))
275 if(!(HW_POWER_CTRL
& HW_POWER_CTRL__DC_OK_IRQ
))
276 panicf("regulator %d: failed to stabilize", reg
);
279 // offset is -1,0 or 1
280 void imx233_power_get_regulator_linreg(enum imx233_regulator_t reg
,
281 bool *enabled
, int *linreg_offset
)
283 if(enabled
&& regulator_info
[reg
].flags
& HAS_LINREG
)
284 *enabled
= !!(*regulator_info
[reg
].reg
& regulator_info
[reg
].linreg_bm
);
287 if(regulator_info
[reg
].flags
& HAS_LINREG_OFFSET
)
289 unsigned v
= (*regulator_info
[reg
].reg
& regulator_info
[reg
].linreg_offset_bm
);
290 v
>>= regulator_info
[reg
].linreg_offset_bp
;
292 *linreg_offset
= (v
== 0) ? 0 : (v
== 1) ? 1 : -1;
294 else if(linreg_offset
)
298 // offset is -1,0 or 1
300 void imx233_power_set_regulator_linreg(enum imx233_regulator_t reg,
301 bool enabled, int linreg_offset)
307 struct imx233_power_info_t
imx233_power_get_info(unsigned flags
)
309 static int dcdc_freqsel
[8] = {
310 [HW_POWER_MISC__FREQSEL__RES
] = 0,
311 [HW_POWER_MISC__FREQSEL__20MHz
] = 20000,
312 [HW_POWER_MISC__FREQSEL__24MHz
] = 24000,
313 [HW_POWER_MISC__FREQSEL__19p2MHz
] = 19200,
314 [HW_POWER_MISC__FREQSEL__14p4MHz
] = 14200,
315 [HW_POWER_MISC__FREQSEL__18MHz
] = 18000,
316 [HW_POWER_MISC__FREQSEL__21p6MHz
] = 21600,
317 [HW_POWER_MISC__FREQSEL__17p28MHz
] = 17280,
320 struct imx233_power_info_t s
;
321 memset(&s
, 0, sizeof(s
));
322 if(flags
& POWER_INFO_DCDC
)
324 s
.dcdc_sel_pllclk
= HW_POWER_MISC
& HW_POWER_MISC__SEL_PLLCLK
;
325 s
.dcdc_freqsel
= dcdc_freqsel
[__XTRACT(HW_POWER_MISC
, FREQSEL
)];
327 if(flags
& POWER_INFO_CHARGE
)
329 for(unsigned i
= 0; i
< ARRAYLEN(g_charger_current_bits
); i
++)
330 if(HW_POWER_CHARGE
& g_charger_current_bits
[i
].bit
)
331 s
.charge_current
+= g_charger_current_bits
[i
].current
;
332 for(unsigned i
= 0; i
< ARRAYLEN(g_charger_stop_current_bits
); i
++)
333 if(HW_POWER_CHARGE
& g_charger_stop_current_bits
[i
].bit
)
334 s
.stop_current
+= g_charger_stop_current_bits
[i
].current
;
335 s
.charging
= HW_POWER_STS
& HW_POWER_STS__CHRGSTS
;
336 s
.batt_adj
= HW_POWER_BATTMONITOR
& HW_POWER_BATTMONITOR__ENBATADJ
;
338 if(flags
& POWER_INFO_4P2
)
340 s
._4p2_enable
= HW_POWER_DCDC4P2
& HW_POWER_DCDC4P2__ENABLE_4P2
;
341 s
._4p2_dcdc
= HW_POWER_DCDC4P2
& HW_POWER_DCDC4P2__ENABLE_DCDC
;
342 s
._4p2_cmptrip
= __XTRACT(HW_POWER_DCDC4P2
, CMPTRIP
);
343 s
._4p2_dropout
= __XTRACT(HW_POWER_DCDC4P2
, DROPOUT_CTRL
);
345 if(flags
& POWER_INFO_5V
)
347 s
._5v_pwd_charge_4p2
= HW_POWER_5VCTRL
& HW_POWER_5VCTRL__PWD_CHARGE_4P2
;
348 s
._5v_dcdc_xfer
= HW_POWER_5VCTRL
& HW_POWER_5VCTRL__DCDC_XFER
;
349 s
._5v_enable_dcdc
= HW_POWER_5VCTRL
& HW_POWER_5VCTRL__ENABLE_DCDC
;
350 for(unsigned i
= 0; i
< ARRAYLEN(g_4p2_charge_limit_bits
); i
++)
351 if(HW_POWER_5VCTRL
& g_4p2_charge_limit_bits
[i
].bit
)
352 s
._5v_charge_4p2_limit
+= g_4p2_charge_limit_bits
[i
].current
;
353 s
._5v_vbusvalid_detect
= HW_POWER_5VCTRL
& HW_POWER_5VCTRL__VBUSVALID_5VDETECT
;
354 s
._5v_vbus_cmps
= HW_POWER_5VCTRL
& HW_POWER_5VCTRL__PWRUP_VBUS_CMPS
;
355 s
._5v_vbusvalid_thr
=
356 __XTRACT(HW_POWER_5VCTRL
, VBUSVALID_TRSH
) == 0 ?
358 : 3900 + __XTRACT(HW_POWER_5VCTRL
, VBUSVALID_TRSH
) * 100;