static void mxs_mem_setup_cpu_and_hbus(void) { struct mxs_clkctrl_regs *clkctrl_regs = (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; /* Set fractional divider for ref_cpu to 480 * 18 / 19 = 454MHz * and ungate CPU clock */ writeb(19 & CLKCTRL_FRAC_FRAC_MASK, (uint8_t *)&clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_CPU]); /* Set CPU bypass */ writel(CLKCTRL_CLKSEQ_BYPASS_CPU, &clkctrl_regs->hw_clkctrl_clkseq_set); /* HBUS = 151MHz */ writel(CLKCTRL_HBUS_DIV_MASK, &clkctrl_regs->hw_clkctrl_hbus_set); writel(((~3) << CLKCTRL_HBUS_DIV_OFFSET) & CLKCTRL_HBUS_DIV_MASK, &clkctrl_regs->hw_clkctrl_hbus_clr); early_delay(10000); /* CPU clock divider = 1 */ clrsetbits_le32(&clkctrl_regs->hw_clkctrl_cpu, CLKCTRL_CPU_DIV_CPU_MASK, 1); /* Disable CPU bypass */ writel(CLKCTRL_CLKSEQ_BYPASS_CPU, &clkctrl_regs->hw_clkctrl_clkseq_clr); early_delay(15000); }
static void mx23_mem_init(void) { /* * Reset/ungate the EMI block. This is essential, otherwise the system * suffers from memory instability. This thing is mx23 specific and is * no longer present on mx28. */ mxs_reset_block((struct mxs_register_32 *)MXS_EMI_BASE); mx23_mem_setup_vddmem(); /* * Configure the DRAM registers */ /* Clear START and SREFRESH bit from DRAM_CTL8 */ clrbits_le32(MXS_DRAM_BASE + 0x20, (1 << 16) | (1 << 8)); initialize_dram_values(); /* Set START bit in DRAM_CTL8 */ setbits_le32(MXS_DRAM_BASE + 0x20, 1 << 16); clrbits_le32(MXS_DRAM_BASE + 0x40, 1 << 17); early_delay(20000); /* Adjust EMI port priority. */ clrsetbits_le32(0x80020000, 0x1f << 16, 0x2); early_delay(20000); setbits_le32(MXS_DRAM_BASE + 0x40, 1 << 19); setbits_le32(MXS_DRAM_BASE + 0x40, 1 << 11); }
void mx28_mem_init_clock(void) { struct mx28_clkctrl_regs *clkctrl_regs = (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; /* Gate EMI clock */ writeb(CLKCTRL_FRAC_CLKGATE, &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_EMI]); /* Set fractional divider for ref_emi to 480 * 18 / 21 = 411MHz */ writeb(CLKCTRL_FRAC_CLKGATE | (21 & CLKCTRL_FRAC_FRAC_MASK), &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]); /* Ungate EMI clock */ writeb(CLKCTRL_FRAC_CLKGATE, &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_EMI]); early_delay(11000); /* Set EMI clock divider for EMI clock to 411 / 2 = 205MHz */ writel((2 << CLKCTRL_EMI_DIV_EMI_OFFSET) | (1 << CLKCTRL_EMI_DIV_XTAL_OFFSET), &clkctrl_regs->hw_clkctrl_emi); /* Unbypass EMI */ writel(CLKCTRL_CLKSEQ_BYPASS_EMI, &clkctrl_regs->hw_clkctrl_clkseq_clr); early_delay(10000); }
static void mxs_power_enable_4p2(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; uint32_t vdddctrl, vddactrl, vddioctrl; uint32_t tmp; vdddctrl = readl(&power_regs->hw_power_vdddctrl); vddactrl = readl(&power_regs->hw_power_vddactrl); vddioctrl = readl(&power_regs->hw_power_vddioctrl); setbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_DISABLE_FET | POWER_VDDDCTRL_ENABLE_LINREG | POWER_VDDDCTRL_PWDN_BRNOUT); setbits_le32(&power_regs->hw_power_vddactrl, POWER_VDDACTRL_DISABLE_FET | POWER_VDDACTRL_ENABLE_LINREG | POWER_VDDACTRL_PWDN_BRNOUT); setbits_le32(&power_regs->hw_power_vddioctrl, POWER_VDDIOCTRL_DISABLE_FET | POWER_VDDIOCTRL_PWDN_BRNOUT); mxs_power_init_4p2_params(); mxs_power_init_4p2_regulator(); /* Shutdown battery (none present) */ if (!mxs_is_batt_ready()) { clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_BO_MASK); writel(POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); writel(POWER_CTRL_ENIRQ_DCDC4P2_BO, &power_regs->hw_power_ctrl_clr); } mxs_power_init_dcdc_4p2_source(); writel(vdddctrl, &power_regs->hw_power_vdddctrl); early_delay(20); writel(vddactrl, &power_regs->hw_power_vddactrl); early_delay(20); writel(vddioctrl, &power_regs->hw_power_vddioctrl); /* * Check if FET is enabled on either powerout and if so, * disable load. */ tmp = 0; tmp |= !(readl(&power_regs->hw_power_vdddctrl) & POWER_VDDDCTRL_DISABLE_FET); tmp |= !(readl(&power_regs->hw_power_vddactrl) & POWER_VDDACTRL_DISABLE_FET); tmp |= !(readl(&power_regs->hw_power_vddioctrl) & POWER_VDDIOCTRL_DISABLE_FET); if (tmp) writel(POWER_CHARGE_ENABLE_LOAD, &power_regs->hw_power_charge_clr); }
static void mx23_mem_setup_vddmem(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; /* We must wait before and after disabling the current limiter! */ early_delay(10000); clrbits_le32(&power_regs->hw_power_vddmemctrl, POWER_VDDMEMCTRL_ENABLE_ILIMIT); early_delay(10000); }
void mx28_mem_init(void) { struct mx28_clkctrl_regs *clkctrl_regs = (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; struct mx28_pinctrl_regs *pinctrl_regs = (struct mx28_pinctrl_regs *)MXS_PINCTRL_BASE; /* Set DDR2 mode */ writel(PINCTRL_EMI_DS_CTRL_DDR_MODE_DDR2, &pinctrl_regs->hw_pinctrl_emi_ds_ctrl_set); /* Power up PLL0 */ writel(CLKCTRL_PLL0CTRL0_POWER, &clkctrl_regs->hw_clkctrl_pll0ctrl0_set); early_delay(11000); mx28_mem_init_clock(); mx28_mem_setup_vdda(); /* * Configure the DRAM registers */ /* Clear START bit from DRAM_CTL16 */ clrbits_le32(MXS_DRAM_BASE + 0x40, 1); init_m28_200mhz_ddr2(); /* Clear SREFRESH bit from DRAM_CTL17 */ clrbits_le32(MXS_DRAM_BASE + 0x44, 1); /* Set START bit in DRAM_CTL16 */ setbits_le32(MXS_DRAM_BASE + 0x40, 1); /* Wait for bit 20 (DRAM init complete) in DRAM_CTL58 */ while (!(readl(MXS_DRAM_BASE + 0xe8) & (1 << 20))) ; mx28_mem_setup_vddd(); early_delay(10000); mx28_mem_setup_cpu_and_hbus(); mx28_mem_get_size(); }
/** * mxs_src_power_init() - Preconfigure the power block * * This function configures reasonable values for the DC-DC control loop * and battery monitor. */ static void mxs_src_power_init(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; debug("SPL: Pre-Configuring power block\n"); /* Improve efficieny and reduce transient ripple */ writel(POWER_LOOPCTRL_TOGGLE_DIF | POWER_LOOPCTRL_EN_CM_HYST | POWER_LOOPCTRL_EN_DF_HYST, &power_regs->hw_power_loopctrl_set); clrsetbits_le32(&power_regs->hw_power_dclimits, POWER_DCLIMITS_POSLIMIT_BUCK_MASK, 0x30 << POWER_DCLIMITS_POSLIMIT_BUCK_OFFSET); setbits_le32(&power_regs->hw_power_battmonitor, POWER_BATTMONITOR_EN_BATADJ); /* Increase the RCSCALE level for quick DCDC response to dynamic load */ clrsetbits_le32(&power_regs->hw_power_loopctrl, POWER_LOOPCTRL_EN_RCSCALE_MASK, POWER_LOOPCTRL_RCSCALE_THRESH | POWER_LOOPCTRL_EN_RCSCALE_8X); clrsetbits_le32(&power_regs->hw_power_minpwr, POWER_MINPWR_HALFFETS, POWER_MINPWR_DOUBLE_FETS); /* 5V to battery handoff ... FIXME */ setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); early_delay(30); clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); }
/** * mxs_power_clock2pll() - Switch CPU core clock source to PLL * * This function switches the CPU core clock from 24MHz XTAL oscilator * to PLL. This can only be called once the PLL has re-locked and once * the PLL is stable after reconfiguration. */ static void mxs_power_clock2pll(void) { struct mxs_clkctrl_regs *clkctrl_regs = (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; debug("SPL: Switching CPU core clock source to PLL\n"); /* * TODO: Are we really? It looks like we turn on PLL0, but we then * set the CLKCTRL_CLKSEQ_BYPASS_CPU bit of the (which was already * set by mxs_power_clock2xtal()). Clearing this bit here seems to * introduce some instability (causing the CPU core to hang). Maybe * we aren't giving PLL0 enough time to stabilise? */ setbits_le32(&clkctrl_regs->hw_clkctrl_pll0ctrl0, CLKCTRL_PLL0CTRL0_POWER); early_delay(100); /* * TODO: Should the PLL0 FORCE_LOCK bit be set here followed be a * wait on the PLL0 LOCK bit? */ setbits_le32(&clkctrl_regs->hw_clkctrl_clkseq, CLKCTRL_CLKSEQ_BYPASS_CPU); }
/** * mxs_5v_boot() - Configure the power block to boot from 5V input * * This function handles configuration of the power block when supplied by * a 5V input. */ static void mxs_5v_boot(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; debug("SPL: Configuring power block to boot from 5V input\n"); /* * NOTE: In original IMX-Bootlets, this also checks for VBUSVALID, * but their implementation always returns 1 so we omit it here. */ if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { debug("SPL: 5V VDD good\n"); mxs_boot_valid_5v(); return; } early_delay(1000); if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { debug("SPL: 5V VDD good (after delay)\n"); mxs_boot_valid_5v(); return; } debug("SPL: 5V VDD not good\n"); mxs_handle_5v_conflict(); }
void mx28_power_init(void) { struct mx28_power_regs *power_regs = (struct mx28_power_regs *)MXS_POWER_BASE; mx28_power_clock2xtal(); mx28_power_clear_auto_restart(); mx28_power_set_linreg(); mx28_power_setup_5v_detect(); mx28_power_configure_power_source(); mx28_enable_output_rail_protection(); mx28_power_set_vddio(3300, 3150); mx28_power_set_vddd(1350, 1200); writel(POWER_CTRL_VDDD_BO_IRQ | POWER_CTRL_VDDA_BO_IRQ | POWER_CTRL_VDDIO_BO_IRQ | POWER_CTRL_VDD5V_DROOP_IRQ | POWER_CTRL_VBUS_VALID_IRQ | POWER_CTRL_BATT_BO_IRQ | POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); writel(POWER_5VCTRL_PWDN_5VBRNOUT, &power_regs->hw_power_5vctrl_set); early_delay(1000); }
void mxs_mem_init(void) { early_delay(11000); mxs_mem_init_clock(); mxs_mem_setup_vdda(); #if defined(CONFIG_MX23) mx23_mem_init(); #elif defined(CONFIG_MX28) mx28_mem_init(); #endif early_delay(10000); mxs_mem_setup_cpu_and_hbus(); }
static void mxs_power_clock2pll(void) { struct mxs_clkctrl_regs *clkctrl_regs = (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; setbits_le32(&clkctrl_regs->hw_clkctrl_pll0ctrl0, CLKCTRL_PLL0CTRL0_POWER); early_delay(100); setbits_le32(&clkctrl_regs->hw_clkctrl_clkseq, CLKCTRL_CLKSEQ_BYPASS_CPU); }
void mx28_power_clock2pll(void) { struct mx28_clkctrl_regs *clkctrl_regs = (struct mx28_clkctrl_regs *)MXS_CLKCTRL_BASE; writel(CLKCTRL_PLL0CTRL0_POWER, &clkctrl_regs->hw_clkctrl_pll0ctrl0_set); early_delay(100); writel(CLKCTRL_CLKSEQ_BYPASS_CPU, &clkctrl_regs->hw_clkctrl_clkseq_clr); }
static void mxs_mem_init_clock(void) { struct mxs_clkctrl_regs *clkctrl_regs = (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE; #if defined(CONFIG_MX23) /* Fractional divider for ref_emi is 33 ; 480 * 18 / 33 = 266MHz */ const unsigned char divider = 33; #elif defined(CONFIG_MX28) /* Fractional divider for ref_emi is 21 ; 480 * 18 / 21 = 411MHz */ const unsigned char divider = 21; #endif debug("SPL: Initialising FRAC0\n"); /* Gate EMI clock */ writeb(CLKCTRL_FRAC_CLKGATE, &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_EMI]); /* Set fractional divider for ref_emi */ writeb(CLKCTRL_FRAC_CLKGATE | (divider & CLKCTRL_FRAC_FRAC_MASK), &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_EMI]); /* Ungate EMI clock */ writeb(CLKCTRL_FRAC_CLKGATE, &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_EMI]); early_delay(11000); /* Set EMI clock divider for EMI clock to 411 / 2 = 205MHz */ writel((2 << CLKCTRL_EMI_DIV_EMI_OFFSET) | (1 << CLKCTRL_EMI_DIV_XTAL_OFFSET), &clkctrl_regs->hw_clkctrl_emi); /* Unbypass EMI */ writel(CLKCTRL_CLKSEQ_BYPASS_EMI, &clkctrl_regs->hw_clkctrl_clkseq_clr); early_delay(10000); debug("SPL: FRAC0 Initialised\n"); }
/** * mxs_batt_boot() - Configure the power block to boot from battery input * * This function configures the power block to boot from the battery voltage * supply. */ static void mxs_batt_boot(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; debug("SPL: Configuring power block to boot from battery\n"); clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_PWDN_5VBRNOUT); clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_ENABLE_DCDC); clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_ENABLE_DCDC | POWER_DCDC4P2_ENABLE_4P2); writel(POWER_CHARGE_ENABLE_LOAD, &power_regs->hw_power_charge_clr); /* 5V to battery handoff. */ setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); early_delay(30); clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); writel(POWER_CTRL_ENIRQ_DCDC4P2_BO, &power_regs->hw_power_ctrl_clr); clrsetbits_le32(&power_regs->hw_power_minpwr, POWER_MINPWR_HALFFETS, POWER_MINPWR_DOUBLE_FETS); mxs_power_set_linreg(); clrbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_DISABLE_FET | POWER_VDDDCTRL_ENABLE_LINREG); clrbits_le32(&power_regs->hw_power_vddactrl, POWER_VDDACTRL_DISABLE_FET | POWER_VDDACTRL_ENABLE_LINREG); clrbits_le32(&power_regs->hw_power_vddioctrl, POWER_VDDIOCTRL_DISABLE_FET); setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_PWD_CHARGE_4P2_MASK); setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_ENABLE_DCDC); clrsetbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, 0x8 << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); mxs_power_enable_4p2(); }
int mx28_is_batt_good(void) { struct mx28_power_regs *power_regs = (struct mx28_power_regs *)MXS_POWER_BASE; uint32_t volt; volt = readl(&power_regs->hw_power_battmonitor); volt &= POWER_BATTMONITOR_BATT_VAL_MASK; volt >>= POWER_BATTMONITOR_BATT_VAL_OFFSET; volt *= 8; if ((volt >= 2400) && (volt <= 4300)) return 1; clrsetbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, 0x3 << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_clr); clrsetbits_le32(&power_regs->hw_power_charge, POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, POWER_CHARGE_STOP_ILIMIT_10MA | 0x3); writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_clr); writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_clr); early_delay(500000); volt = readl(&power_regs->hw_power_battmonitor); volt &= POWER_BATTMONITOR_BATT_VAL_MASK; volt >>= POWER_BATTMONITOR_BATT_VAL_OFFSET; volt *= 8; if (volt >= 3500) return 0; if (volt >= 2400) return 1; writel(POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, &power_regs->hw_power_charge_clr); writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_set); return 0; }
static void mx23_mem_setup_vddmem(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; writel((0x10 << POWER_VDDMEMCTRL_TRG_OFFSET) | POWER_VDDMEMCTRL_ENABLE_ILIMIT | POWER_VDDMEMCTRL_ENABLE_LINREG | POWER_VDDMEMCTRL_PULLDOWN_ACTIVE, &power_regs->hw_power_vddmemctrl); early_delay(10000); writel((0x10 << POWER_VDDMEMCTRL_TRG_OFFSET) | POWER_VDDMEMCTRL_ENABLE_LINREG, &power_regs->hw_power_vddmemctrl); }
/** * mxs_is_batt_good() - Test if battery is operational at all * * This function starts recharging the battery and tests if the input current * provided by the 5V input recharging the battery is also sufficient to power * the DC-DC converter. */ static int mxs_is_batt_good(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; uint32_t volt = mxs_get_batt_volt(); if ((volt >= 2400) && (volt <= 4300)) { debug("SPL: Battery is good\n"); return 1; } clrsetbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, 0x3 << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_clr); clrsetbits_le32(&power_regs->hw_power_charge, POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, POWER_CHARGE_STOP_ILIMIT_10MA | 0x3); writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_clr); writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_clr); early_delay(500000); volt = mxs_get_batt_volt(); if (volt >= 3500) { debug("SPL: Battery Voltage too high\n"); return 0; } if (volt >= 2400) { debug("SPL: Battery is good\n"); return 1; } writel(POWER_CHARGE_STOP_ILIMIT_MASK | POWER_CHARGE_BATTCHRG_I_MASK, &power_regs->hw_power_charge_clr); writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_set); debug("SPL: Battery Voltage too low\n"); return 0; }
static void mxs_5v_boot(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; /* * NOTE: In original IMX-Bootlets, this also checks for VBUSVALID, * but their implementation always returns 1 so we omit it here. */ if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { mxs_boot_valid_5v(); return; } early_delay(1000); if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) { mxs_boot_valid_5v(); return; } mxs_handle_5v_conflict(); }
void mx28_power_set_vddd(uint32_t new_target, uint32_t new_brownout) { struct mx28_power_regs *power_regs = (struct mx28_power_regs *)MXS_POWER_BASE; uint32_t cur_target, diff, bo_int = 0; uint32_t powered_by_linreg = 0; new_brownout = new_target - new_brownout; cur_target = readl(&power_regs->hw_power_vdddctrl); cur_target &= POWER_VDDDCTRL_TRG_MASK; cur_target *= 25; /* 25 mV step*/ cur_target += 800; /* 800 mV lowest */ powered_by_linreg = mx28_get_vddd_power_source_off(); if (new_target > cur_target) { if (powered_by_linreg) { bo_int = readl(&power_regs->hw_power_vdddctrl); clrbits_le32(&power_regs->hw_power_vdddctrl, POWER_CTRL_ENIRQ_VDDD_BO); } setbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_BO_OFFSET_MASK); do { if (new_target - cur_target > 100) diff = cur_target + 100; else diff = new_target; diff -= 800; diff /= 25; clrsetbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_TRG_MASK, diff); if (powered_by_linreg || (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO)) early_delay(1500); else { while (!(readl(&power_regs->hw_power_sts) & POWER_STS_DC_OK)) ; } cur_target = readl(&power_regs->hw_power_vdddctrl); cur_target &= POWER_VDDDCTRL_TRG_MASK; cur_target *= 25; /* 25 mV step*/ cur_target += 800; /* 800 mV lowest */ } while (new_target > cur_target); if (powered_by_linreg) { writel(POWER_CTRL_VDDD_BO_IRQ, &power_regs->hw_power_ctrl_clr); if (bo_int & POWER_CTRL_ENIRQ_VDDD_BO) setbits_le32(&power_regs->hw_power_vdddctrl, POWER_CTRL_ENIRQ_VDDD_BO); } } else { do { if (cur_target - new_target > 100) diff = cur_target - 100; else diff = new_target; diff -= 800; diff /= 25; clrsetbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_TRG_MASK, diff); if (powered_by_linreg || (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO)) early_delay(1500); else { while (!(readl(&power_regs->hw_power_sts) & POWER_STS_DC_OK)) ; } cur_target = readl(&power_regs->hw_power_vdddctrl); cur_target &= POWER_VDDDCTRL_TRG_MASK; cur_target *= 25; /* 25 mV step*/ cur_target += 800; /* 800 mV lowest */ } while (new_target < cur_target); } clrsetbits_le32(&power_regs->hw_power_vdddctrl, POWER_VDDDCTRL_BO_OFFSET_MASK, new_brownout << POWER_VDDDCTRL_BO_OFFSET_OFFSET); }
/** * mxs_power_init_4p2_regulator() - Start the 4P2 regulator * * This function enables the 4P2 regulator and switches the DC-DC converter * to use the 4P2 input. */ static void mxs_power_init_4p2_regulator(void) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; uint32_t tmp, tmp2; debug("SPL: Enabling 4P2 regulator\n"); setbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_ENABLE_4P2); writel(POWER_CHARGE_ENABLE_LOAD, &power_regs->hw_power_charge_set); writel(POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK, &power_regs->hw_power_5vctrl_clr); clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_TRG_MASK); /* Power up the 4p2 rail and logic/control */ writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_clr); /* * Start charging up the 4p2 capacitor. We ramp of this charge * gradually to avoid large inrush current from the 5V cable which can * cause transients/problems */ debug("SPL: Charging 4P2 capacitor\n"); mxs_enable_4p2_dcdc_input(0); if (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VBUS_VALID_IRQ) { /* * If we arrived here, we were unable to recover from mx23 chip * errata 5837. 4P2 is disabled and sufficient battery power is * not present. Exiting to not enable DCDC power during 5V * connected state. */ clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_ENABLE_DCDC); writel(POWER_5VCTRL_PWD_CHARGE_4P2_MASK, &power_regs->hw_power_5vctrl_set); debug("SPL: Unable to recover from mx23 errata 5837\n"); hang(); } /* * Here we set the 4p2 brownout level to something very close to 4.2V. * We then check the brownout status. If the brownout status is false, * the voltage is already close to the target voltage of 4.2V so we * can go ahead and set the 4P2 current limit to our max target limit. * If the brownout status is true, we need to ramp us the current limit * so that we don't cause large inrush current issues. We step up the * current limit until the brownout status is false or until we've * reached our maximum defined 4p2 current limit. */ debug("SPL: Setting 4P2 brownout level\n"); clrsetbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_BO_MASK, 22 << POWER_DCDC4P2_BO_OFFSET); /* 4.15V */ if (!(readl(&power_regs->hw_power_sts) & POWER_STS_DCDC_4P2_BO)) { setbits_le32(&power_regs->hw_power_5vctrl, 0x3f << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET); } else { tmp = (readl(&power_regs->hw_power_5vctrl) & POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK) >> POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET; while (tmp < 0x3f) { if (!(readl(&power_regs->hw_power_sts) & POWER_STS_DCDC_4P2_BO)) { tmp = readl(&power_regs->hw_power_5vctrl); tmp |= POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK; early_delay(100); writel(tmp, &power_regs->hw_power_5vctrl); break; } else { tmp++; tmp2 = readl(&power_regs->hw_power_5vctrl); tmp2 &= ~POWER_5VCTRL_CHARGE_4P2_ILIMIT_MASK; tmp2 |= tmp << POWER_5VCTRL_CHARGE_4P2_ILIMIT_OFFSET; writel(tmp2, &power_regs->hw_power_5vctrl); early_delay(100); } } } clrbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_BO_MASK); writel(POWER_CTRL_DCDC4P2_BO_IRQ, &power_regs->hw_power_ctrl_clr); }
/** * mxs_enable_4p2_dcdc_input() - Enable or disable the DCDC input from 4P2 * @xfer: Select if the input shall be enabled or disabled * * This function enables or disables the 4P2 input into the DC-DC converter. */ static void mxs_enable_4p2_dcdc_input(int xfer) { struct mxs_power_regs *power_regs = (struct mxs_power_regs *)MXS_POWER_BASE; uint32_t tmp, vbus_thresh, vbus_5vdetect, pwd_bo; uint32_t prev_5v_brnout, prev_5v_droop; debug("SPL: %s 4P2 DC-DC Input\n", xfer ? "Enabling" : "Disabling"); if (xfer && (readl(&power_regs->hw_power_5vctrl) & POWER_5VCTRL_ENABLE_DCDC)) { return; } prev_5v_brnout = readl(&power_regs->hw_power_5vctrl) & POWER_5VCTRL_PWDN_5VBRNOUT; prev_5v_droop = readl(&power_regs->hw_power_ctrl) & POWER_CTRL_ENIRQ_VDD5V_DROOP; clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_PWDN_5VBRNOUT); writel(POWER_RESET_UNLOCK_KEY | POWER_RESET_PWD_OFF, &power_regs->hw_power_reset); clrbits_le32(&power_regs->hw_power_ctrl, POWER_CTRL_ENIRQ_VDD5V_DROOP); /* * Recording orignal values that will be modified temporarlily * to handle a chip bug. See chip errata for CQ ENGR00115837 */ tmp = readl(&power_regs->hw_power_5vctrl); vbus_thresh = tmp & POWER_5VCTRL_VBUSVALID_TRSH_MASK; vbus_5vdetect = tmp & POWER_5VCTRL_VBUSVALID_5VDETECT; pwd_bo = readl(&power_regs->hw_power_minpwr) & POWER_MINPWR_PWD_BO; /* * Disable mechanisms that get erroneously tripped by when setting * the DCDC4P2 EN_DCDC */ clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_VBUSVALID_5VDETECT | POWER_5VCTRL_VBUSVALID_TRSH_MASK); writel(POWER_MINPWR_PWD_BO, &power_regs->hw_power_minpwr_set); if (xfer) { setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); early_delay(20); clrbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_DCDC_XFER); setbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_ENABLE_DCDC); } else { setbits_le32(&power_regs->hw_power_dcdc4p2, POWER_DCDC4P2_ENABLE_DCDC); } early_delay(25); clrsetbits_le32(&power_regs->hw_power_5vctrl, POWER_5VCTRL_VBUSVALID_TRSH_MASK, vbus_thresh); if (vbus_5vdetect) writel(vbus_5vdetect, &power_regs->hw_power_5vctrl_set); if (!pwd_bo) clrbits_le32(&power_regs->hw_power_minpwr, POWER_MINPWR_PWD_BO); while (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VBUS_VALID_IRQ) writel(POWER_CTRL_VBUS_VALID_IRQ, &power_regs->hw_power_ctrl_clr); if (prev_5v_brnout) { writel(POWER_5VCTRL_PWDN_5VBRNOUT, &power_regs->hw_power_5vctrl_set); writel(POWER_RESET_UNLOCK_KEY, &power_regs->hw_power_reset); } else { writel(POWER_5VCTRL_PWDN_5VBRNOUT, &power_regs->hw_power_5vctrl_clr); writel(POWER_RESET_UNLOCK_KEY | POWER_RESET_PWD_OFF, &power_regs->hw_power_reset); } while (readl(&power_regs->hw_power_ctrl) & POWER_CTRL_VDD5V_DROOP_IRQ) writel(POWER_CTRL_VDD5V_DROOP_IRQ, &power_regs->hw_power_ctrl_clr); if (prev_5v_droop) clrbits_le32(&power_regs->hw_power_ctrl, POWER_CTRL_ENIRQ_VDD5V_DROOP); else setbits_le32(&power_regs->hw_power_ctrl, POWER_CTRL_ENIRQ_VDD5V_DROOP); }