xref: /netbsd-src/sys/arch/evbarm/stand/bootimx23/power_prep.c (revision 4db03d42c9db855752ae131e52df3637db5afb32)
1 /* $Id: power_prep.c,v 1.5 2016/08/17 22:04:51 skrll Exp $ */
2 
3 /*
4  * Copyright (c) 2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Petri Laakso.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #include <sys/param.h>
34 #include <sys/types.h>
35 
36 #include <arm/imx/imx23_powerreg.h>
37 
38 #include <lib/libkern/libkern.h>
39 #include <lib/libsa/stand.h>
40 
41 #include "common.h"
42 
43 #define PWR_CTRL	(HW_POWER_BASE + HW_POWER_CTRL)
44 #define PWR_CTRL_S	(HW_POWER_BASE + HW_POWER_CTRL_SET)
45 #define PWR_CTRL_C	(HW_POWER_BASE + HW_POWER_CTRL_CLR)
46 #define PWR_5VCTRL	(HW_POWER_BASE + HW_POWER_5VCTRL)
47 #define PWR_5VCTRL_S	(HW_POWER_BASE + HW_POWER_5VCTRL_SET)
48 #define PWR_5VCTRL_C	(HW_POWER_BASE + HW_POWER_5VCTRL_CLR)
49 #define PWR_MINPWR	(HW_POWER_BASE + HW_POWER_MINPWR)
50 #define PWR_MINPWR_S	(HW_POWER_BASE + HW_POWER_MINPWR_SET)
51 #define PWR_MINPWR_C	(HW_POWER_BASE + HW_POWER_MINPWR_CLR)
52 #define PWR_CHARGE	(HW_POWER_BASE + HW_POWER_CHARGE)
53 #define PWR_CHARGE_S	(HW_POWER_BASE + HW_POWER_CHARGE_SET)
54 #define PWR_CHARGE_C	(HW_POWER_BASE + HW_POWER_CHARGE_CLR)
55 #define PWR_VDDDCTRL	(HW_POWER_BASE + HW_POWER_VDDDCTRL)
56 #define PWR_VDDACTRL	(HW_POWER_BASE + HW_POWER_VDDACTRL)
57 #define PWR_VDDIOCTRL	(HW_POWER_BASE + HW_POWER_VDDIOCTRL)
58 #define PWR_VDDMEMCTRL	(HW_POWER_BASE + HW_POWER_VDDMEMCTRL)
59 #define PWR_DCDC4P2	(HW_POWER_BASE + HW_POWER_DCDC4P2)
60 #define PWR_MISC	(HW_POWER_BASE + HW_POWER_MISC)
61 #define PWR_DCLIMITS	(HW_POWER_BASE + HW_POWER_DCLIMITS)
62 #define PWR_LOOPCTRL 	(HW_POWER_BASE + HW_POWER_LOOPCTRL)
63 #define PWR_LOOPCTRL_S	(HW_POWER_BASE + HW_POWER_LOOPCTRL_SET)
64 #define PWR_LOOPCTRL_C	(HW_POWER_BASE + HW_POWER_LOOPCTRL_CLR)
65 #define PWR_STATUS	(HW_POWER_BASE + HW_POWER_STS)
66 #define PWR_SPEED	(HW_POWER_BASE + HW_POWER_SPEED)
67 #define PWR_BATTMONITOR	(HW_POWER_BASE + HW_POWER_BATTMONITOR)
68 #define PWR_RESET	(HW_POWER_BASE + HW_POWER_RESET)
69 #define PWR_DEBUG	(HW_POWER_BASE + HW_POWER_DEBUG)
70 #define PWR_SPECIAL	(HW_POWER_BASE + HW_POWER_SPECIAL)
71 #define PWR_VERSION	(HW_POWER_BASE + HW_POWER_VERSION)
72 
73 #define VBUSVALID_TRSH 5	/* 4.4V */
74 #define CHARGE_4P2_ILIMIT_MAX 0x3f
75 #define CMPTRIP 0x1f	/* DCDC_4P2 pin >= 1.05 * BATTERY pin. */
76 #define DROPOUT_CTRL 0xa /* BO 100mV, DCDC selects higher. */
77 
78 void en_vbusvalid(void);
79 int vbusvalid(void);
80 void power_tune(void);
81 void en_4p2_reg(void);
82 void en_4p2_to_dcdc(void);
83 void power_vddd_from_dcdc(int, int);
84 void power_vdda_from_dcdc(int, int);
85 void power_vddio_from_dcdc(int, int);
86 void power_vddmem(int);
87 
88 /*
89  * Configure the DCDC control logic 5V detection to use VBUSVALID.
90  */
91 void
en_vbusvalid(void)92 en_vbusvalid(void)
93 {
94 	uint32_t tmp_r;
95 
96 	tmp_r = REG_RD(PWR_5VCTRL);
97 	tmp_r &= ~HW_POWER_5VCTRL_VBUSVALID_TRSH;
98 	tmp_r |= __SHIFTIN(VBUSVALID_TRSH, HW_POWER_5VCTRL_VBUSVALID_TRSH);
99 	REG_WR(PWR_5VCTRL, tmp_r);
100 
101 	REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_PWRUP_VBUS_CMPS);
102 	delay(1000);
103 
104 	REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_VBUSVALID_5VDETECT);
105 
106 	return;
107 }
108 /*
109  * Test VBUSVALID.
110  */
111 int
vbusvalid(void)112 vbusvalid(void)
113 {
114 	if (REG_RD(PWR_STATUS) & HW_POWER_STS_VBUSVALID)
115 		return 1;
116 	else
117 		return 0;
118 }
119 /*
120  * Set various registers.
121  */
122 void
power_tune(void)123 power_tune(void)
124 {
125 	uint32_t tmp_r;
126 
127 	REG_WR(PWR_LOOPCTRL_S, HW_POWER_LOOPCTRL_TOGGLE_DIF |
128 		HW_POWER_LOOPCTRL_EN_CM_HYST |
129 		HW_POWER_LOOPCTRL_EN_DF_HYST |
130 		HW_POWER_LOOPCTRL_RCSCALE_THRESH |
131 		__SHIFTIN(3, HW_POWER_LOOPCTRL_EN_RCSCALE));
132 
133 	REG_WR(PWR_MINPWR_S, HW_POWER_MINPWR_DOUBLE_FETS);
134 
135 	REG_WR(PWR_5VCTRL_S, __SHIFTIN(4, HW_POWER_5VCTRL_HEADROOM_ADJ));
136 
137 	tmp_r = REG_RD(PWR_DCLIMITS);
138 	tmp_r &= ~HW_POWER_DCLIMITS_POSLIMIT_BUCK;
139 	tmp_r |= __SHIFTIN(0x30, HW_POWER_DCLIMITS_POSLIMIT_BUCK);
140 	REG_WR(PWR_DCLIMITS, tmp_r);
141 
142 	return;
143 }
144 /*
145  * AN3883.pdf 2.1.3.1 Enabling the 4P2 LinReg
146  */
147 void
en_4p2_reg(void)148 en_4p2_reg(void)
149 {
150 	uint32_t tmp_r;
151 	int ilimit;
152 
153 	/* TRG is 4.2V by default. */
154 	tmp_r = REG_RD(PWR_DCDC4P2);
155 	tmp_r |= HW_POWER_DCDC4P2_ENABLE_4P2;
156 	REG_WR(PWR_DCDC4P2, tmp_r);
157 
158 	REG_WR(PWR_CHARGE_S, HW_POWER_CHARGE_ENABLE_LOAD);
159 
160 	/* Set CHARGE_4P2_ILIMIT to minimum. */
161 	REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
162 	REG_WR(PWR_5VCTRL_S, __SHIFTIN(1, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT));
163 
164 	/* Power up 4.2V regulation circuit. */
165 	REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_PWD_CHARGE_4P2);
166 
167 	/* Ungate path from 4P2 reg to DCDC. */
168 	tmp_r = REG_RD(PWR_DCDC4P2);
169 	tmp_r |= HW_POWER_DCDC4P2_ENABLE_DCDC;
170 	REG_WR(PWR_DCDC4P2, tmp_r);
171 
172 	delay(10000);
173 
174 	/* Charge 4P2 capacitance. */
175 	tmp_r = REG_RD(PWR_5VCTRL);
176 	for (ilimit = 2; ilimit <= CHARGE_4P2_ILIMIT_MAX; ilimit++) {
177 		tmp_r &= ~HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT;
178 		tmp_r |= __SHIFTIN(ilimit, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT);
179 		REG_WR(PWR_5VCTRL, tmp_r);
180 		delay(10000);
181 	}
182 
183 	return;
184 }
185 
186 /*
187  * AN3883.pdf 2.1.3.3 Enabling 4P2 Input to DC-DC
188  */
en_4p2_to_dcdc(void)189 void en_4p2_to_dcdc(void)
190 {
191 	uint32_t tmp_r;
192 
193 	tmp_r = REG_RD(PWR_DCDC4P2);
194 
195 	tmp_r &= ~HW_POWER_DCDC4P2_CMPTRIP;
196 	tmp_r |= __SHIFTIN(CMPTRIP, HW_POWER_DCDC4P2_CMPTRIP);
197 
198 	tmp_r &= ~HW_POWER_DCDC4P2_DROPOUT_CTRL;
199 	tmp_r |= __SHIFTIN(DROPOUT_CTRL, HW_POWER_DCDC4P2_DROPOUT_CTRL);
200 
201 	REG_WR(PWR_DCDC4P2, tmp_r);
202 
203 	REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_DCDC_XFER);
204 
205 	/* Enabling DCDC triggers 5V brownout. */
206 	REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_PWDN_5VBRNOUT);
207 	REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_ENABLE_DCDC);
208 	delay(10000);
209 	REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_PWDN_5VBRNOUT);
210 
211 	/* Now DCDC is using 4P2 so I can remove extra temporary load. */
212 	REG_WR(PWR_CHARGE_C, HW_POWER_CHARGE_ENABLE_LOAD);
213 
214 	return;
215 }
216 
217 /*
218  * Configure VDDD to source power from DCDC.
219  */
220 void
power_vddd_from_dcdc(int target,int brownout)221 power_vddd_from_dcdc(int target, int brownout)
222 {
223 	uint32_t tmp_r;
224 
225 	/* BO_OFFSET must be within 800mV - 1475mV */
226 	if (brownout > 1475)
227 		brownout = 1475;
228 	else if (brownout < 800)
229 		brownout = 800;
230 
231 
232 	/* Set LINREG_OFFSET one step below TRG. */
233 	tmp_r = REG_RD(PWR_VDDDCTRL);
234 	tmp_r &= ~HW_POWER_VDDDCTRL_LINREG_OFFSET;
235 	tmp_r |= __SHIFTIN(2, HW_POWER_VDDDCTRL_LINREG_OFFSET);
236 	REG_WR(PWR_VDDDCTRL, tmp_r);
237 	delay(10000);
238 
239 	/* Enable VDDD switching converter output. */
240 	tmp_r = REG_RD(PWR_VDDDCTRL);
241 	tmp_r &= ~HW_POWER_VDDDCTRL_DISABLE_FET;
242 	REG_WR(PWR_VDDDCTRL, tmp_r);
243 	delay(10000);
244 
245 	/* Disable linear regulator output. */
246 	tmp_r = REG_RD(PWR_VDDDCTRL);
247 	tmp_r &= ~HW_POWER_VDDDCTRL_ENABLE_LINREG;
248 	REG_WR(PWR_VDDDCTRL, tmp_r);
249 	delay(10000);
250 
251 	/* Set target voltage and brownout level. */
252 	tmp_r = REG_RD(PWR_VDDDCTRL);
253 	tmp_r &= ~(HW_POWER_VDDDCTRL_BO_OFFSET | HW_POWER_VDDDCTRL_TRG);
254 	tmp_r |= __SHIFTIN(((target - brownout) / 25),
255 		HW_POWER_VDDDCTRL_BO_OFFSET);
256 	tmp_r |= __SHIFTIN(((target - 800) / 25), HW_POWER_VDDDCTRL_TRG);
257 	REG_WR(PWR_VDDDCTRL, tmp_r);
258 	delay(10000);
259 
260 	/* Enable PWDN_BRNOUT. */
261 	REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDD_BO_IRQ);
262 
263 	tmp_r = REG_RD(PWR_VDDDCTRL);
264 	tmp_r |= HW_POWER_VDDDCTRL_PWDN_BRNOUT;
265 	REG_WR(PWR_VDDDCTRL, tmp_r);
266 
267 	return;
268 }
269 /*
270  * Configure VDDA to source power from DCDC.
271  */
272 void
power_vdda_from_dcdc(int target,int brownout)273 power_vdda_from_dcdc(int target, int brownout)
274 {
275 	uint32_t tmp_r;
276 
277 	/* BO_OFFSET must be within 1400mV - 2175mV */
278 	if (brownout > 2275)
279 		brownout = 2275;
280 	else if (brownout < 1400)
281 		brownout = 1400;
282 
283 
284 	/* Set LINREG_OFFSET one step below TRG. */
285 	tmp_r = REG_RD(PWR_VDDACTRL);
286 	tmp_r &= ~HW_POWER_VDDACTRL_LINREG_OFFSET;
287 	tmp_r |= __SHIFTIN(2, HW_POWER_VDDACTRL_LINREG_OFFSET);
288 	REG_WR(PWR_VDDACTRL, tmp_r);
289 	delay(10000);
290 
291 	/* Enable VDDA switching converter output. */
292 	tmp_r = REG_RD(PWR_VDDACTRL);
293 	tmp_r &= ~HW_POWER_VDDACTRL_DISABLE_FET;
294 	REG_WR(PWR_VDDACTRL, tmp_r);
295 	delay(10000);
296 
297 	/* Disable linear regulator output. */
298 	tmp_r = REG_RD(PWR_VDDACTRL);
299 	tmp_r &= ~HW_POWER_VDDACTRL_ENABLE_LINREG;
300 	REG_WR(PWR_VDDACTRL, tmp_r);
301 	delay(10000);
302 
303 	/* Set target voltage and brownout level. */
304 	tmp_r = REG_RD(PWR_VDDACTRL);
305 	tmp_r &= ~(HW_POWER_VDDACTRL_BO_OFFSET | HW_POWER_VDDACTRL_TRG);
306 	tmp_r |= __SHIFTIN(((target - brownout) / 25),
307 		HW_POWER_VDDACTRL_BO_OFFSET);
308 	tmp_r |= __SHIFTIN(((target - 1500) / 25), HW_POWER_VDDACTRL_TRG);
309 	REG_WR(PWR_VDDACTRL, tmp_r);
310 	delay(10000);
311 
312 	/* Enable PWDN_BRNOUT. */
313 	REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDA_BO_IRQ);
314 
315 	tmp_r = REG_RD(PWR_VDDACTRL);
316 	tmp_r |= HW_POWER_VDDACTRL_PWDN_BRNOUT;
317 	REG_WR(PWR_VDDACTRL, tmp_r);
318 
319 	return;
320 }
321 /*
322  * Configure VDDIO to source power from DCDC.
323  */
324 void
power_vddio_from_dcdc(int target,int brownout)325 power_vddio_from_dcdc(int target, int brownout)
326 {
327 	uint32_t tmp_r;
328 
329 	/* BO_OFFSET must be within 2700mV - 3475mV */
330 	if (brownout > 3475)
331 		brownout = 3475;
332 	else if (brownout < 2700)
333 		brownout = 2700;
334 
335 
336 	/* Set LINREG_OFFSET one step below TRG. */
337 	tmp_r = REG_RD(PWR_VDDIOCTRL);
338 	tmp_r &= ~HW_POWER_VDDIOCTRL_LINREG_OFFSET;
339 	tmp_r |= __SHIFTIN(2, HW_POWER_VDDIOCTRL_LINREG_OFFSET);
340 	REG_WR(PWR_VDDIOCTRL, tmp_r);
341 	delay(10000);
342 
343 	/* Enable VDDIO switching converter output. */
344 	tmp_r = REG_RD(PWR_VDDIOCTRL);
345 	tmp_r &= ~HW_POWER_VDDIOCTRL_DISABLE_FET;
346 	REG_WR(PWR_VDDIOCTRL, tmp_r);
347 	delay(10000);
348 
349 	/* Set target voltage and brownout level. */
350 	tmp_r = REG_RD(PWR_VDDIOCTRL);
351 	tmp_r &= ~(HW_POWER_VDDIOCTRL_BO_OFFSET | HW_POWER_VDDIOCTRL_TRG);
352 	tmp_r |= __SHIFTIN(((target - brownout) / 25),
353 		HW_POWER_VDDIOCTRL_BO_OFFSET);
354 	tmp_r |= __SHIFTIN(((target - 2800) / 25), HW_POWER_VDDIOCTRL_TRG);
355 	REG_WR(PWR_VDDIOCTRL, tmp_r);
356 	delay(10000);
357 
358 	/* Enable PWDN_BRNOUT. */
359 	REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDIO_BO_IRQ);
360 
361 	tmp_r = REG_RD(PWR_VDDIOCTRL);
362 	tmp_r |= HW_POWER_VDDIOCTRL_PWDN_BRNOUT;
363 	REG_WR(PWR_VDDIOCTRL, tmp_r);
364 
365 	return;
366 }
367 /*
368  * AN3883.pdf 2.3.1.2 Setting VDDMEM Target Voltage
369  */
370 void
power_vddmem(int target)371 power_vddmem(int target)
372 {
373 	uint32_t tmp_r;
374 
375 	/* Set target voltage. */
376 	tmp_r = REG_RD(PWR_VDDMEMCTRL);
377 	tmp_r &= ~(HW_POWER_VDDMEMCTRL_TRG);
378 	tmp_r |= __SHIFTIN(((target - 1700) / 50), HW_POWER_VDDMEMCTRL_TRG);
379 	REG_WR(PWR_VDDMEMCTRL, tmp_r);
380 	delay(10000);
381 
382 	tmp_r = REG_RD(PWR_VDDMEMCTRL);
383 	tmp_r |= (HW_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE |
384 		HW_POWER_VDDMEMCTRL_ENABLE_ILIMIT |
385 		HW_POWER_VDDMEMCTRL_ENABLE_LINREG);
386 	REG_WR(PWR_VDDMEMCTRL, tmp_r);
387 
388 	delay(1000);
389 
390 	tmp_r = REG_RD(PWR_VDDMEMCTRL);
391 	tmp_r &= ~(HW_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE |
392 		HW_POWER_VDDMEMCTRL_ENABLE_ILIMIT);
393 	REG_WR(PWR_VDDMEMCTRL, tmp_r);
394 
395 	return;
396 }
397