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
78void en_vbusvalid(void);
79int vbusvalid(void);
80void power_tune(void);
81void en_4p2_reg(void);
82void en_4p2_to_dcdc(void);
83void power_vddd_from_dcdc(int, int);
84void power_vdda_from_dcdc(int, int);
85void power_vddio_from_dcdc(int, int);
86void power_vddmem(int);
87
88/*
89 * Configure the DCDC control logic 5V detection to use VBUSVALID.
90 */
91void
92en_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 */
111int
112vbusvalid(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 */
122void
123power_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 */
147void
148en_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 */
189void 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 */
220void
221power_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 */
272void
273power_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 */
324void
325power_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 */
370void
371power_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