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