]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - arch/arm/cpu/arm926ejs/mx28/spl_power_init.c
clear POWER_VDDMEMCTRL_ENABLE_ILIMIT which was accidentally left enabled after debugging
[karo-tx-uboot.git] / arch / arm / cpu / arm926ejs / mx28 / spl_power_init.c
index 9d98811fd81c89dd319d722d53141e90a756515e..dc15c030c46bf3546b8abf499f55cac9a5599614 100644 (file)
 
 #include "mx28_init.h"
 
+#ifdef CONFIG_SYS_SPL_VDDD_VAL
+#define VDDD_VAL       CONFIG_SYS_SPL_VDDD_VAL
+#else
+#define VDDD_VAL       1350
+#endif
+#ifdef CONFIG_SYS_SPL_VDDIO_VAL
+#define VDDIO_VAL      CONFIG_SYS_SPL_VDDIO_VAL
+#else
+#define VDDIO_VAL      3300
+#endif
+#ifdef CONFIG_SYS_SPL_VDDA_VAL
+#define VDDA_VAL       CONFIG_SYS_SPL_VDDA_VAL
+#else
+#define VDDA_VAL       1800
+#endif
+#ifdef CONFIG_SYS_SPL_VDDMEM_VAL
+#define VDDMEM_VAL     CONFIG_SYS_SPL_VDDMEM_VAL
+#else
+#define VDDMEM_VAL     1500
+#endif
+
+#ifdef CONFIG_SYS_SPL_VDDD_BO_VAL
+#define VDDD_BO_VAL    CONFIG_SYS_SPL_VDDD_BO_VAL
+#else
+#define VDDD_BO_VAL    150
+#endif
+#ifdef CONFIG_SYS_SPL_VDDIO_BO_VAL
+#define VDDIO_BO_VAL   CONFIG_SYS_SPL_VDDIO_BO_VAL
+#else
+#define VDDIO_BO_VAL   150
+#endif
+#ifdef CONFIG_SYS_SPL_VDDA_BO_VAL
+#define VDDA_BO_VAL    CONFIG_SYS_SPL_VDDA_BO_VAL
+#else
+#define VDDA_BO_VAL    175
+#endif
+#ifdef CONFIG_SYS_SPL_VDDMEM_BO_VAL
+#define VDDMEM_BO_VAL  CONFIG_SYS_SPL_VDDMEM_BO_VAL
+#else
+#define VDDMEM_BO_VAL  25
+#endif
+
+#ifdef CONFIG_SYS_SPL_BATT_BO_LEVEL
+#if CONFIG_SYS_SPL_BATT_BO_LEVEL < 2400 || CONFIG_SYS_SPL_BATT_BO_LEVEL > 3640
+#error CONFIG_SYS_SPL_BATT_BO_LEVEL out of range
+#endif
+#define BATT_BO_VAL    (((CONFIG_SYS_SPL_BATT_BO_LEVEL) - 2400) / 40)
+#else
+/* Brownout default at 3V */
+#define BATT_BO_VAL    ((3000 - 2400) / 40)
+#endif
+
+#ifdef CONFIG_SYS_SPL_FIXED_BATT_SUPPLY
+static const int fixed_batt_supply = 1;
+#else
+static const int fixed_batt_supply;
+#endif
+
 static struct mx28_power_regs *power_regs = (void *)MXS_POWER_BASE;
 
 static void mx28_power_clock2xtal(void)
@@ -122,8 +180,14 @@ static void mx28_src_power_init(void)
                        POWER_DCLIMITS_POSLIMIT_BUCK_MASK,
                        0x30 << POWER_DCLIMITS_POSLIMIT_BUCK_OFFSET);
 
-       setbits_le32(&power_regs->hw_power_battmonitor,
+       if (!fixed_batt_supply) {
+               /* FIXME: This requires the LRADC to be set up! */
+               setbits_le32(&power_regs->hw_power_battmonitor,
                        POWER_BATTMONITOR_EN_BATADJ);
+       } else {
+               clrbits_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,
@@ -134,10 +198,12 @@ static void mx28_src_power_init(void)
        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);
+       if (!fixed_batt_supply) {
+               /* 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);
+       }
 }
 
 static void mx28_power_init_4p2_params(void)
@@ -495,13 +561,42 @@ static void mx28_5v_boot(void)
        mx28_handle_5v_conflict();
 }
 
-static void mx28_init_batt_bo(void)
+static void mx28_fixed_batt_boot(void)
 {
+       writel(POWER_CTRL_ENIRQ_BATT_BO, &power_regs->hw_power_ctrl_clr);
+
+       setbits_le32(&power_regs->hw_power_5vctrl,
+               POWER_5VCTRL_PWDN_5VBRNOUT |
+               POWER_5VCTRL_ENABLE_DCDC |
+               POWER_5VCTRL_ILIMIT_EQ_ZERO |
+               POWER_5VCTRL_PWDN_5VBRNOUT |
+               POWER_5VCTRL_PWD_CHARGE_4P2_MASK);
+
+       writel(POWER_CHARGE_PWD_BATTCHRG, &power_regs->hw_power_charge_set);
 
-       /* Brownout at 3V */
+       clrbits_le32(&power_regs->hw_power_vdddctrl,
+               POWER_VDDDCTRL_DISABLE_FET |
+               POWER_VDDDCTRL_ENABLE_LINREG |
+               POWER_VDDDCTRL_DISABLE_STEPPING);
+
+       clrbits_le32(&power_regs->hw_power_vddactrl,
+               POWER_VDDACTRL_DISABLE_FET | POWER_VDDACTRL_ENABLE_LINREG |
+               POWER_VDDACTRL_DISABLE_STEPPING);
+
+       clrbits_le32(&power_regs->hw_power_vddioctrl,
+               POWER_VDDIOCTRL_DISABLE_FET |
+               POWER_VDDIOCTRL_DISABLE_STEPPING);
+
+       /* Stop 5V detection */
+       writel(POWER_5VCTRL_PWRUP_VBUS_CMPS,
+               &power_regs->hw_power_5vctrl_clr);
+}
+
+static void mx28_init_batt_bo(void)
+{
        clrsetbits_le32(&power_regs->hw_power_battmonitor,
                POWER_BATTMONITOR_BRWNOUT_LVL_MASK,
-               15 << POWER_BATTMONITOR_BRWNOUT_LVL_OFFSET);
+               BATT_BO_VAL << POWER_BATTMONITOR_BRWNOUT_LVL_OFFSET);
 
        writel(POWER_CTRL_BATT_BO_IRQ, &power_regs->hw_power_ctrl_clr);
        writel(POWER_CTRL_ENIRQ_BATT_BO, &power_regs->hw_power_ctrl_clr);
@@ -562,7 +657,11 @@ static void mx28_power_configure_power_source(void)
 {
        mx28_src_power_init();
 
-       mx28_5v_boot();
+       if (!fixed_batt_supply)
+               mx28_5v_boot();
+       else
+               mx28_fixed_batt_boot();
+
        mx28_power_clock2pll();
 
        mx28_init_batt_bo();
@@ -641,63 +740,89 @@ static inline int mx28_get_vddd_power_source_off(void)
        return 0;
 }
 
-static void mx28_power_set_vddio(uint32_t new_target, uint32_t new_brownout)
+static inline int mx28_get_vdda_power_source_off(void)
 {
-       uint32_t cur_target, diff, bo_int = 0;
-       uint32_t powered_by_linreg = 0;
+       uint32_t tmp;
 
-       new_brownout = new_target - new_brownout;
+       tmp = readl(&power_regs->hw_power_vddactrl);
+       if (tmp & POWER_VDDACTRL_DISABLE_FET) {
+               if ((tmp & POWER_VDDACTRL_LINREG_OFFSET_MASK) ==
+                       POWER_VDDACTRL_LINREG_OFFSET_0STEPS)
+                       return 1;
+       }
 
-       cur_target = readl(&power_regs->hw_power_vddioctrl);
-       cur_target &= POWER_VDDIOCTRL_TRG_MASK;
-       cur_target *= 50;       /* 50 mV step*/
-       cur_target += 2800;     /* 2800 mV lowest */
+       if (readl(&power_regs->hw_power_sts) & POWER_STS_VDD5V_GT_VDDIO) {
+               if (!(readl(&power_regs->hw_power_5vctrl) &
+                       POWER_5VCTRL_ENABLE_DCDC))
+                       return 1;
+       }
 
-       powered_by_linreg = mx28_get_vddio_power_source_off();
-       if (new_target > cur_target) {
+       if (!(tmp & POWER_VDDACTRL_ENABLE_LINREG)) {
+               if ((tmp & POWER_VDDACTRL_LINREG_OFFSET_MASK) ==
+                       POWER_VDDACTRL_LINREG_OFFSET_1STEPS_BELOW)
+                       return 1;
+       }
+       return 0;
+}
 
-               if (powered_by_linreg) {
-                       bo_int = readl(&power_regs->hw_power_vddioctrl);
-                       clrbits_le32(&power_regs->hw_power_vddioctrl,
-                                       POWER_CTRL_ENIRQ_VDDIO_BO);
-               }
+static inline void mx28_power_set_vddx(
+       uint32_t new_target, uint32_t new_brownout,
+       uint32_t *reg, const char *name,
+       uint32_t min_trg, uint32_t max_trg,
+       uint8_t step_size,
+       uint32_t trg_mask, uint32_t trg_shift,
+       uint32_t bo_mask, uint32_t bo_shift,
+       int powered_by_linreg)
+{
+       uint32_t cur_target, cur_brownout;
+       uint32_t diff;
+
+       if (new_target < min_trg || new_target > max_trg)
+               new_target = (new_target > max_trg) ? max_trg : min_trg;
+
+       if (new_brownout / step_size > 7)
+               new_brownout = 7 * step_size;
+
+       cur_target = readl(reg);
 
-               setbits_le32(&power_regs->hw_power_vddioctrl,
-                               POWER_VDDIOCTRL_BO_OFFSET_MASK);
+       cur_brownout = (cur_target & bo_mask) >> bo_shift;
+       cur_brownout *= step_size;
+
+       cur_target = (cur_target & trg_mask) >> trg_shift;
+       cur_target *= step_size;
+       cur_target += min_trg;
+       if (cur_target > max_trg)
+               cur_target = max_trg;
+
+       if (new_target == cur_target && new_brownout == cur_brownout)
+               return;
+
+       if (new_target > cur_target) {
+               setbits_le32(reg, bo_mask);
                do {
                        if (new_target - cur_target > 100)
                                diff = cur_target + 100;
                        else
                                diff = new_target;
 
-                       diff -= 2800;
-                       diff /= 50;
+                       diff -= min_trg;
+                       diff /= step_size;
 
-                       clrsetbits_le32(&power_regs->hw_power_vddioctrl,
-                               POWER_VDDIOCTRL_TRG_MASK, diff);
+                       clrsetbits_le32(reg, trg_mask, diff);
 
-                       if (powered_by_linreg)
+                       if (powered_by_linreg) {
                                early_delay(1500);
-                       else {
+                       else {
                                while (!(readl(&power_regs->hw_power_sts) &
-                                       POWER_STS_DC_OK))
-                                       ;
-
+                                       POWER_STS_DC_OK)) {
+                               }
                        }
 
-                       cur_target = readl(&power_regs->hw_power_vddioctrl);
-                       cur_target &= POWER_VDDIOCTRL_TRG_MASK;
-                       cur_target *= 50;       /* 50 mV step*/
-                       cur_target += 2800;     /* 2800 mV lowest */
+                       cur_target = readl(reg);
+                       cur_target &= trg_mask;
+                       cur_target *= step_size;
+                       cur_target += min_trg;
                } while (new_target > cur_target);
-
-               if (powered_by_linreg) {
-                       writel(POWER_CTRL_VDDIO_BO_IRQ,
-                               &power_regs->hw_power_ctrl_clr);
-                       if (bo_int & POWER_CTRL_ENIRQ_VDDIO_BO)
-                               setbits_le32(&power_regs->hw_power_vddioctrl,
-                                               POWER_CTRL_ENIRQ_VDDIO_BO);
-               }
        } else {
                do {
                        if (cur_target - new_target > 100)
@@ -705,122 +830,111 @@ static void mx28_power_set_vddio(uint32_t new_target, uint32_t new_brownout)
                        else
                                diff = new_target;
 
-                       diff -= 2800;
-                       diff /= 50;
+                       diff -= min_trg;
+                       diff /= step_size;
 
-                       clrsetbits_le32(&power_regs->hw_power_vddioctrl,
-                               POWER_VDDIOCTRL_TRG_MASK, diff);
+                       clrsetbits_le32(reg, trg_mask, diff);
 
-                       if (powered_by_linreg)
+                       if (powered_by_linreg) {
                                early_delay(1500);
-                       else {
+                       else {
                                while (!(readl(&power_regs->hw_power_sts) &
-                                       POWER_STS_DC_OK))
-                                       ;
-
+                                       POWER_STS_DC_OK)) {
+                               }
                        }
 
-                       cur_target = readl(&power_regs->hw_power_vddioctrl);
-                       cur_target &= POWER_VDDIOCTRL_TRG_MASK;
-                       cur_target *= 50;       /* 50 mV step*/
-                       cur_target += 2800;     /* 2800 mV lowest */
+                       cur_target = readl(reg);
+                       cur_target &= trg_mask;
+                       cur_target *= step_size;
+                       cur_target += min_trg;
                } while (new_target < cur_target);
        }
 
-       clrsetbits_le32(&power_regs->hw_power_vddioctrl,
-                       POWER_VDDDCTRL_BO_OFFSET_MASK,
-                       new_brownout << POWER_VDDDCTRL_BO_OFFSET_OFFSET);
+       clrsetbits_le32(reg, bo_mask, (new_brownout / step_size) << bo_shift);
 }
 
-static void mx28_power_set_vddd(uint32_t new_target, uint32_t new_brownout)
-{
-       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)
-                               early_delay(1500);
-                       else {
-                               while (!(readl(&power_regs->hw_power_sts) &
-                                       POWER_STS_DC_OK))
-                                       ;
+#define __mx28_power_set_vddx(trg, bo, min, max, step, reg, name, lr)  \
+       mx28_power_set_vddx(trg, bo,                                    \
+                       &power_regs->hw_power_##reg##ctrl, #name,       \
+                       min, max, step,                                 \
+                       POWER_##name##CTRL_TRG_MASK,                    \
+                       POWER_##name##CTRL_TRG_OFFSET,                  \
+                       POWER_##name##CTRL_BO_OFFSET_MASK,              \
+                       POWER_##name##CTRL_BO_OFFSET_OFFSET, lr)
 
-                       }
+static inline void mx28_power_set_vddd(uint32_t target, uint32_t brownout)
+{
+       int powered_by_linreg = mx28_get_vddd_power_source_off();
+       uint32_t bo_int = 0;
 
-                       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) {
+               bo_int = readl(&power_regs->hw_power_vdddctrl);
+               clrbits_le32(&power_regs->hw_power_vdddctrl,
+                       POWER_CTRL_ENIRQ_VDDD_BO);
+       }
 
-               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;
+       __mx28_power_set_vddx(target, brownout, 800, 1575, 25, vddd, VDDD,
+                       powered_by_linreg);
 
-                       diff -= 800;
-                       diff /= 25;
+       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);
+       }
+}
 
-                       clrsetbits_le32(&power_regs->hw_power_vdddctrl,
-                                       POWER_VDDDCTRL_TRG_MASK, diff);
+static inline void mx28_power_set_vddio(uint32_t target, uint32_t brownout)
+{
+       int powered_by_linreg = mx28_get_vddio_power_source_off();
+       uint32_t bo_int = 0;
 
-                       if (powered_by_linreg)
-                               early_delay(1500);
-                       else {
-                               while (!(readl(&power_regs->hw_power_sts) &
-                                       POWER_STS_DC_OK))
-                                       ;
+       if (powered_by_linreg) {
+               bo_int = readl(&power_regs->hw_power_vddioctrl);
+               clrbits_le32(&power_regs->hw_power_vddioctrl,
+                       POWER_CTRL_ENIRQ_VDDIO_BO);
+       }
+       __mx28_power_set_vddx(target, brownout, 2800, 3600, 50, vddio, VDDIO,
+                       powered_by_linreg);
+       if (powered_by_linreg) {
+               writel(POWER_CTRL_VDDIO_BO_IRQ,
+                       &power_regs->hw_power_ctrl_clr);
+               if (bo_int & POWER_CTRL_ENIRQ_VDDIO_BO)
+                       setbits_le32(&power_regs->hw_power_vddioctrl,
+                               POWER_CTRL_ENIRQ_VDDIO_BO);
+       }
+}
 
-                       }
+static inline void mx28_power_set_vdda(uint32_t target, uint32_t brownout)
+{
+       int powered_by_linreg = mx28_get_vdda_power_source_off();
+       uint32_t bo_int = 0;
 
-                       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) {
+               bo_int = readl(&power_regs->hw_power_vddioctrl);
+               clrbits_le32(&power_regs->hw_power_vddioctrl,
+                       POWER_CTRL_ENIRQ_VDDIO_BO);
+       }
+       __mx28_power_set_vddx(target, brownout, 1500, 2275, 25, vdda, VDDA,
+               powered_by_linreg);
+       if (powered_by_linreg) {
+               writel(POWER_CTRL_VDDIO_BO_IRQ,
+                       &power_regs->hw_power_ctrl_clr);
+               if (bo_int & POWER_CTRL_ENIRQ_VDDIO_BO)
+                       setbits_le32(&power_regs->hw_power_vddioctrl,
+                               POWER_CTRL_ENIRQ_VDDIO_BO);
        }
+}
 
-       clrsetbits_le32(&power_regs->hw_power_vdddctrl,
-                       POWER_VDDDCTRL_BO_OFFSET_MASK,
-                       new_brownout << POWER_VDDDCTRL_BO_OFFSET_OFFSET);
+static inline void mx28_power_set_vddmem(uint32_t target, uint32_t brownout)
+{
+       __mx28_power_set_vddx(target, brownout, 1100, 1750, 25, vddmem, VDDMEM,
+                       0);
+
+       clrbits_le32(&power_regs->hw_power_vddmemctrl,
+               POWER_VDDMEMCTRL_ENABLE_LINREG |
+               POWER_VDDMEMCTRL_ENABLE_ILIMIT);
 }
 
 void mx28_power_init(void)
@@ -828,21 +942,27 @@ void mx28_power_init(void)
        mx28_power_clock2xtal();
        mx28_power_clear_auto_restart();
        mx28_power_set_linreg();
-       mx28_power_setup_5v_detect();
+       if (!fixed_batt_supply)
+               mx28_power_setup_5v_detect();
+
        mx28_power_configure_power_source();
        mx28_enable_output_rail_protection();
 
-       mx28_power_set_vddio(3300, 3150);
+       mx28_power_set_vddio(VDDIO_VAL, VDDIO_BO_VAL);
+
+       mx28_power_set_vddd(VDDD_VAL, VDDD_BO_VAL);
 
-       mx28_power_set_vddd(1350, 1200);
+       mx28_power_set_vdda(VDDA_VAL, VDDA_BO_VAL);
+
+       mx28_power_set_vddmem(VDDMEM_VAL, VDDMEM_BO_VAL);
 
        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);
-
+       if (!fixed_batt_supply)
+               writel(POWER_5VCTRL_PWDN_5VBRNOUT,
+                       &power_regs->hw_power_5vctrl_set);
 }
 
 #ifdef CONFIG_SPL_MX28_PSWITCH_WAIT