/* $Id: power_prep.c,v 1.5 2016/08/17 22:04:51 skrll Exp $ */ /* * Copyright (c) 2012 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Petri Laakso. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include "common.h" #define PWR_CTRL (HW_POWER_BASE + HW_POWER_CTRL) #define PWR_CTRL_S (HW_POWER_BASE + HW_POWER_CTRL_SET) #define PWR_CTRL_C (HW_POWER_BASE + HW_POWER_CTRL_CLR) #define PWR_5VCTRL (HW_POWER_BASE + HW_POWER_5VCTRL) #define PWR_5VCTRL_S (HW_POWER_BASE + HW_POWER_5VCTRL_SET) #define PWR_5VCTRL_C (HW_POWER_BASE + HW_POWER_5VCTRL_CLR) #define PWR_MINPWR (HW_POWER_BASE + HW_POWER_MINPWR) #define PWR_MINPWR_S (HW_POWER_BASE + HW_POWER_MINPWR_SET) #define PWR_MINPWR_C (HW_POWER_BASE + HW_POWER_MINPWR_CLR) #define PWR_CHARGE (HW_POWER_BASE + HW_POWER_CHARGE) #define PWR_CHARGE_S (HW_POWER_BASE + HW_POWER_CHARGE_SET) #define PWR_CHARGE_C (HW_POWER_BASE + HW_POWER_CHARGE_CLR) #define PWR_VDDDCTRL (HW_POWER_BASE + HW_POWER_VDDDCTRL) #define PWR_VDDACTRL (HW_POWER_BASE + HW_POWER_VDDACTRL) #define PWR_VDDIOCTRL (HW_POWER_BASE + HW_POWER_VDDIOCTRL) #define PWR_VDDMEMCTRL (HW_POWER_BASE + HW_POWER_VDDMEMCTRL) #define PWR_DCDC4P2 (HW_POWER_BASE + HW_POWER_DCDC4P2) #define PWR_MISC (HW_POWER_BASE + HW_POWER_MISC) #define PWR_DCLIMITS (HW_POWER_BASE + HW_POWER_DCLIMITS) #define PWR_LOOPCTRL (HW_POWER_BASE + HW_POWER_LOOPCTRL) #define PWR_LOOPCTRL_S (HW_POWER_BASE + HW_POWER_LOOPCTRL_SET) #define PWR_LOOPCTRL_C (HW_POWER_BASE + HW_POWER_LOOPCTRL_CLR) #define PWR_STATUS (HW_POWER_BASE + HW_POWER_STS) #define PWR_SPEED (HW_POWER_BASE + HW_POWER_SPEED) #define PWR_BATTMONITOR (HW_POWER_BASE + HW_POWER_BATTMONITOR) #define PWR_RESET (HW_POWER_BASE + HW_POWER_RESET) #define PWR_DEBUG (HW_POWER_BASE + HW_POWER_DEBUG) #define PWR_SPECIAL (HW_POWER_BASE + HW_POWER_SPECIAL) #define PWR_VERSION (HW_POWER_BASE + HW_POWER_VERSION) #define VBUSVALID_TRSH 5 /* 4.4V */ #define CHARGE_4P2_ILIMIT_MAX 0x3f #define CMPTRIP 0x1f /* DCDC_4P2 pin >= 1.05 * BATTERY pin. */ #define DROPOUT_CTRL 0xa /* BO 100mV, DCDC selects higher. */ void en_vbusvalid(void); int vbusvalid(void); void power_tune(void); void en_4p2_reg(void); void en_4p2_to_dcdc(void); void power_vddd_from_dcdc(int, int); void power_vdda_from_dcdc(int, int); void power_vddio_from_dcdc(int, int); void power_vddmem(int); /* * Configure the DCDC control logic 5V detection to use VBUSVALID. */ void en_vbusvalid(void) { uint32_t tmp_r; tmp_r = REG_RD(PWR_5VCTRL); tmp_r &= ~HW_POWER_5VCTRL_VBUSVALID_TRSH; tmp_r |= __SHIFTIN(VBUSVALID_TRSH, HW_POWER_5VCTRL_VBUSVALID_TRSH); REG_WR(PWR_5VCTRL, tmp_r); REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_PWRUP_VBUS_CMPS); delay(1000); REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_VBUSVALID_5VDETECT); return; } /* * Test VBUSVALID. */ int vbusvalid(void) { if (REG_RD(PWR_STATUS) & HW_POWER_STS_VBUSVALID) return 1; else return 0; } /* * Set various registers. */ void power_tune(void) { uint32_t tmp_r; REG_WR(PWR_LOOPCTRL_S, HW_POWER_LOOPCTRL_TOGGLE_DIF | HW_POWER_LOOPCTRL_EN_CM_HYST | HW_POWER_LOOPCTRL_EN_DF_HYST | HW_POWER_LOOPCTRL_RCSCALE_THRESH | __SHIFTIN(3, HW_POWER_LOOPCTRL_EN_RCSCALE)); REG_WR(PWR_MINPWR_S, HW_POWER_MINPWR_DOUBLE_FETS); REG_WR(PWR_5VCTRL_S, __SHIFTIN(4, HW_POWER_5VCTRL_HEADROOM_ADJ)); tmp_r = REG_RD(PWR_DCLIMITS); tmp_r &= ~HW_POWER_DCLIMITS_POSLIMIT_BUCK; tmp_r |= __SHIFTIN(0x30, HW_POWER_DCLIMITS_POSLIMIT_BUCK); REG_WR(PWR_DCLIMITS, tmp_r); return; } /* * AN3883.pdf 2.1.3.1 Enabling the 4P2 LinReg */ void en_4p2_reg(void) { uint32_t tmp_r; int ilimit; /* TRG is 4.2V by default. */ tmp_r = REG_RD(PWR_DCDC4P2); tmp_r |= HW_POWER_DCDC4P2_ENABLE_4P2; REG_WR(PWR_DCDC4P2, tmp_r); REG_WR(PWR_CHARGE_S, HW_POWER_CHARGE_ENABLE_LOAD); /* Set CHARGE_4P2_ILIMIT to minimum. */ REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT); REG_WR(PWR_5VCTRL_S, __SHIFTIN(1, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT)); /* Power up 4.2V regulation circuit. */ REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_PWD_CHARGE_4P2); /* Ungate path from 4P2 reg to DCDC. */ tmp_r = REG_RD(PWR_DCDC4P2); tmp_r |= HW_POWER_DCDC4P2_ENABLE_DCDC; REG_WR(PWR_DCDC4P2, tmp_r); delay(10000); /* Charge 4P2 capacitance. */ tmp_r = REG_RD(PWR_5VCTRL); for (ilimit = 2; ilimit <= CHARGE_4P2_ILIMIT_MAX; ilimit++) { tmp_r &= ~HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT; tmp_r |= __SHIFTIN(ilimit, HW_POWER_5VCTRL_CHARGE_4P2_ILIMIT); REG_WR(PWR_5VCTRL, tmp_r); delay(10000); } return; } /* * AN3883.pdf 2.1.3.3 Enabling 4P2 Input to DC-DC */ void en_4p2_to_dcdc(void) { uint32_t tmp_r; tmp_r = REG_RD(PWR_DCDC4P2); tmp_r &= ~HW_POWER_DCDC4P2_CMPTRIP; tmp_r |= __SHIFTIN(CMPTRIP, HW_POWER_DCDC4P2_CMPTRIP); tmp_r &= ~HW_POWER_DCDC4P2_DROPOUT_CTRL; tmp_r |= __SHIFTIN(DROPOUT_CTRL, HW_POWER_DCDC4P2_DROPOUT_CTRL); REG_WR(PWR_DCDC4P2, tmp_r); REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_DCDC_XFER); /* Enabling DCDC triggers 5V brownout. */ REG_WR(PWR_5VCTRL_C, HW_POWER_5VCTRL_PWDN_5VBRNOUT); REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_ENABLE_DCDC); delay(10000); REG_WR(PWR_5VCTRL_S, HW_POWER_5VCTRL_PWDN_5VBRNOUT); /* Now DCDC is using 4P2 so I can remove extra temporary load. */ REG_WR(PWR_CHARGE_C, HW_POWER_CHARGE_ENABLE_LOAD); return; } /* * Configure VDDD to source power from DCDC. */ void power_vddd_from_dcdc(int target, int brownout) { uint32_t tmp_r; /* BO_OFFSET must be within 800mV - 1475mV */ if (brownout > 1475) brownout = 1475; else if (brownout < 800) brownout = 800; /* Set LINREG_OFFSET one step below TRG. */ tmp_r = REG_RD(PWR_VDDDCTRL); tmp_r &= ~HW_POWER_VDDDCTRL_LINREG_OFFSET; tmp_r |= __SHIFTIN(2, HW_POWER_VDDDCTRL_LINREG_OFFSET); REG_WR(PWR_VDDDCTRL, tmp_r); delay(10000); /* Enable VDDD switching converter output. */ tmp_r = REG_RD(PWR_VDDDCTRL); tmp_r &= ~HW_POWER_VDDDCTRL_DISABLE_FET; REG_WR(PWR_VDDDCTRL, tmp_r); delay(10000); /* Disable linear regulator output. */ tmp_r = REG_RD(PWR_VDDDCTRL); tmp_r &= ~HW_POWER_VDDDCTRL_ENABLE_LINREG; REG_WR(PWR_VDDDCTRL, tmp_r); delay(10000); /* Set target voltage and brownout level. */ tmp_r = REG_RD(PWR_VDDDCTRL); tmp_r &= ~(HW_POWER_VDDDCTRL_BO_OFFSET | HW_POWER_VDDDCTRL_TRG); tmp_r |= __SHIFTIN(((target - brownout) / 25), HW_POWER_VDDDCTRL_BO_OFFSET); tmp_r |= __SHIFTIN(((target - 800) / 25), HW_POWER_VDDDCTRL_TRG); REG_WR(PWR_VDDDCTRL, tmp_r); delay(10000); /* Enable PWDN_BRNOUT. */ REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDD_BO_IRQ); tmp_r = REG_RD(PWR_VDDDCTRL); tmp_r |= HW_POWER_VDDDCTRL_PWDN_BRNOUT; REG_WR(PWR_VDDDCTRL, tmp_r); return; } /* * Configure VDDA to source power from DCDC. */ void power_vdda_from_dcdc(int target, int brownout) { uint32_t tmp_r; /* BO_OFFSET must be within 1400mV - 2175mV */ if (brownout > 2275) brownout = 2275; else if (brownout < 1400) brownout = 1400; /* Set LINREG_OFFSET one step below TRG. */ tmp_r = REG_RD(PWR_VDDACTRL); tmp_r &= ~HW_POWER_VDDACTRL_LINREG_OFFSET; tmp_r |= __SHIFTIN(2, HW_POWER_VDDACTRL_LINREG_OFFSET); REG_WR(PWR_VDDACTRL, tmp_r); delay(10000); /* Enable VDDA switching converter output. */ tmp_r = REG_RD(PWR_VDDACTRL); tmp_r &= ~HW_POWER_VDDACTRL_DISABLE_FET; REG_WR(PWR_VDDACTRL, tmp_r); delay(10000); /* Disable linear regulator output. */ tmp_r = REG_RD(PWR_VDDACTRL); tmp_r &= ~HW_POWER_VDDACTRL_ENABLE_LINREG; REG_WR(PWR_VDDACTRL, tmp_r); delay(10000); /* Set target voltage and brownout level. */ tmp_r = REG_RD(PWR_VDDACTRL); tmp_r &= ~(HW_POWER_VDDACTRL_BO_OFFSET | HW_POWER_VDDACTRL_TRG); tmp_r |= __SHIFTIN(((target - brownout) / 25), HW_POWER_VDDACTRL_BO_OFFSET); tmp_r |= __SHIFTIN(((target - 1500) / 25), HW_POWER_VDDACTRL_TRG); REG_WR(PWR_VDDACTRL, tmp_r); delay(10000); /* Enable PWDN_BRNOUT. */ REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDA_BO_IRQ); tmp_r = REG_RD(PWR_VDDACTRL); tmp_r |= HW_POWER_VDDACTRL_PWDN_BRNOUT; REG_WR(PWR_VDDACTRL, tmp_r); return; } /* * Configure VDDIO to source power from DCDC. */ void power_vddio_from_dcdc(int target, int brownout) { uint32_t tmp_r; /* BO_OFFSET must be within 2700mV - 3475mV */ if (brownout > 3475) brownout = 3475; else if (brownout < 2700) brownout = 2700; /* Set LINREG_OFFSET one step below TRG. */ tmp_r = REG_RD(PWR_VDDIOCTRL); tmp_r &= ~HW_POWER_VDDIOCTRL_LINREG_OFFSET; tmp_r |= __SHIFTIN(2, HW_POWER_VDDIOCTRL_LINREG_OFFSET); REG_WR(PWR_VDDIOCTRL, tmp_r); delay(10000); /* Enable VDDIO switching converter output. */ tmp_r = REG_RD(PWR_VDDIOCTRL); tmp_r &= ~HW_POWER_VDDIOCTRL_DISABLE_FET; REG_WR(PWR_VDDIOCTRL, tmp_r); delay(10000); /* Set target voltage and brownout level. */ tmp_r = REG_RD(PWR_VDDIOCTRL); tmp_r &= ~(HW_POWER_VDDIOCTRL_BO_OFFSET | HW_POWER_VDDIOCTRL_TRG); tmp_r |= __SHIFTIN(((target - brownout) / 25), HW_POWER_VDDIOCTRL_BO_OFFSET); tmp_r |= __SHIFTIN(((target - 2800) / 25), HW_POWER_VDDIOCTRL_TRG); REG_WR(PWR_VDDIOCTRL, tmp_r); delay(10000); /* Enable PWDN_BRNOUT. */ REG_WR(PWR_CTRL_C, HW_POWER_CTRL_VDDIO_BO_IRQ); tmp_r = REG_RD(PWR_VDDIOCTRL); tmp_r |= HW_POWER_VDDIOCTRL_PWDN_BRNOUT; REG_WR(PWR_VDDIOCTRL, tmp_r); return; } /* * AN3883.pdf 2.3.1.2 Setting VDDMEM Target Voltage */ void power_vddmem(int target) { uint32_t tmp_r; /* Set target voltage. */ tmp_r = REG_RD(PWR_VDDMEMCTRL); tmp_r &= ~(HW_POWER_VDDMEMCTRL_TRG); tmp_r |= __SHIFTIN(((target - 1700) / 50), HW_POWER_VDDMEMCTRL_TRG); REG_WR(PWR_VDDMEMCTRL, tmp_r); delay(10000); tmp_r = REG_RD(PWR_VDDMEMCTRL); tmp_r |= (HW_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE | HW_POWER_VDDMEMCTRL_ENABLE_ILIMIT | HW_POWER_VDDMEMCTRL_ENABLE_LINREG); REG_WR(PWR_VDDMEMCTRL, tmp_r); delay(1000); tmp_r = REG_RD(PWR_VDDMEMCTRL); tmp_r &= ~(HW_POWER_VDDMEMCTRL_PULLDOWN_ACTIVE | HW_POWER_VDDMEMCTRL_ENABLE_ILIMIT); REG_WR(PWR_VDDMEMCTRL, tmp_r); return; }