]> Pileus Git - ~andy/linux/blobdiff - drivers/mfd/db8500-prcmu.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[~andy/linux] / drivers / mfd / db8500-prcmu.c
index 28a60906f5f732ffd0574b418897a9af37887bb6..a25ab9c6b5afe81492c6a2d411e01acaf5aaa8d7 100644 (file)
@@ -1433,18 +1433,21 @@ static int request_sga_clock(u8 clock, bool enable)
  */
 int db8500_prcmu_request_clock(u8 clock, bool enable)
 {
-       if (clock == PRCMU_SGACLK)
+       switch(clock) {
+       case PRCMU_SGACLK:
                return request_sga_clock(clock, enable);
-       else if (clock < PRCMU_NUM_REG_CLOCKS)
-               return request_reg_clock(clock, enable);
-       else if (clock == PRCMU_TIMCLK)
+       case PRCMU_TIMCLK:
                return request_timclk(enable);
-       else if (clock == PRCMU_SYSCLK)
+       case PRCMU_SYSCLK:
                return request_sysclk(enable);
-       else if (clock == PRCMU_PLLSOC1)
+       case PRCMU_PLLSOC1:
                return request_pll(clock, enable);
-       else
-               return -EINVAL;
+       default:
+               break;
+       }
+       if (clock < PRCMU_NUM_REG_CLOCKS)
+               return request_reg_clock(clock, enable);
+       return -EINVAL;
 }
 
 int db8500_prcmu_config_esram0_deep_sleep(u8 state)
@@ -1543,6 +1546,78 @@ int prcmu_stop_temp_sense(void)
        return config_hot_period(0xFFFF);
 }
 
+static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)
+{
+
+       mutex_lock(&mb4_transfer.lock);
+
+       while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(4))
+               cpu_relax();
+
+       writeb(d0, (tcdm_base + PRCM_REQ_MB4_A9WDOG_0));
+       writeb(d1, (tcdm_base + PRCM_REQ_MB4_A9WDOG_1));
+       writeb(d2, (tcdm_base + PRCM_REQ_MB4_A9WDOG_2));
+       writeb(d3, (tcdm_base + PRCM_REQ_MB4_A9WDOG_3));
+
+       writeb(cmd, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB4));
+
+       writel(MBOX_BIT(4), PRCM_MBOX_CPU_SET);
+       wait_for_completion(&mb4_transfer.work);
+
+       mutex_unlock(&mb4_transfer.lock);
+
+       return 0;
+
+}
+
+int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)
+{
+       BUG_ON(num == 0 || num > 0xf);
+       return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0,
+                           sleep_auto_off ? A9WDOG_AUTO_OFF_EN :
+                           A9WDOG_AUTO_OFF_DIS);
+}
+
+int prcmu_enable_a9wdog(u8 id)
+{
+       return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);
+}
+
+int prcmu_disable_a9wdog(u8 id)
+{
+       return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);
+}
+
+int prcmu_kick_a9wdog(u8 id)
+{
+       return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);
+}
+
+/*
+ * timeout is 28 bit, in ms.
+ */
+#define MAX_WATCHDOG_TIMEOUT 131000
+int prcmu_load_a9wdog(u8 id, u32 timeout)
+{
+       if (timeout > MAX_WATCHDOG_TIMEOUT)
+               /*
+                * Due to calculation bug in prcmu fw, timeouts
+                * can't be bigger than 131 seconds.
+                */
+               return -EINVAL;
+
+       return prcmu_a9wdog(MB4H_A9WDOG_LOAD,
+                           (id & A9WDOG_ID_MASK) |
+                           /*
+                            * Put the lowest 28 bits of timeout at
+                            * offset 4. Four first bits are used for id.
+                            */
+                           (u8)((timeout << 4) & 0xf0),
+                           (u8)((timeout >> 4) & 0xff),
+                           (u8)((timeout >> 12) & 0xff),
+                           (u8)((timeout >> 20) & 0xff));
+}
+
 /**
  * prcmu_set_clock_divider() - Configure the clock divider.
  * @clock:     The clock for which the request is made.
@@ -1673,6 +1748,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)
 void prcmu_ac_wake_req(void)
 {
        u32 val;
+       u32 status;
 
        mutex_lock(&mb0_transfer.ac_wake_lock);
 
@@ -1682,11 +1758,34 @@ void prcmu_ac_wake_req(void)
 
        atomic_set(&ac_wake_req_state, 1);
 
+retry:
        writel((val | PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ), PRCM_HOSTACCESS_REQ);
 
        if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
-                       msecs_to_jiffies(20000))) {
-               pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+                       msecs_to_jiffies(5000))) {
+               pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
+                       __func__);
+               goto unlock_and_return;
+       }
+
+       /*
+        * The modem can generate an AC_WAKE_ACK, and then still go to sleep.
+        * As a workaround, we wait, and then check that the modem is indeed
+        * awake (in terms of the value of the PRCM_MOD_AWAKE_STATUS
+        * register, which may not be the whole truth).
+        */
+       udelay(400);
+       status = (readl(PRCM_MOD_AWAKE_STATUS) & BITS(0, 2));
+       if (status != (PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE |
+                       PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE)) {
+               pr_err("prcmu: %s received ack, but modem not awake (0x%X).\n",
+                       __func__, status);
+               udelay(1200);
+               writel(val, PRCM_HOSTACCESS_REQ);
+               if (wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
+                               msecs_to_jiffies(5000)))
+                       goto retry;
+               pr_crit("prcmu: %s timed out (5 s) waiting for AC_SLEEP_ACK.\n",
                        __func__);
        }
 
@@ -1711,8 +1810,8 @@ void prcmu_ac_sleep_req()
                PRCM_HOSTACCESS_REQ);
 
        if (!wait_for_completion_timeout(&mb0_transfer.ac_wake_work,
-                       msecs_to_jiffies(20000))) {
-               pr_err("prcmu: %s timed out (20 s) waiting for a reply.\n",
+                       msecs_to_jiffies(5000))) {
+               pr_crit("prcmu: %s timed out (5 s) waiting for a reply.\n",
                        __func__);
        }
 
@@ -1739,6 +1838,17 @@ void db8500_prcmu_system_reset(u16 reset_code)
        writel(1, PRCM_APE_SOFTRST);
 }
 
+/**
+ * db8500_prcmu_get_reset_code - Retrieve SW reset reason code
+ *
+ * Retrieves the reset reason code stored by prcmu_system_reset() before
+ * last restart.
+ */
+u16 db8500_prcmu_get_reset_code(void)
+{
+       return readw(tcdm_base + PRCM_SW_RST_REASON);
+}
+
 /**
  * prcmu_reset_modem - ask the PRCMU to reset modem
  */
@@ -2047,7 +2157,7 @@ void __init db8500_prcmu_early_init(void)
        }
 }
 
-static void __init init_prcm_registers(void)
+static void __init db8500_prcmu_init_clkforce(void)
 {
        u32 val;
 
@@ -2092,7 +2202,42 @@ static struct regulator_consumer_supply db8500_vsmps2_consumers[] = {
 
 static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = {
        REGULATOR_SUPPLY("vsupply", "b2r2.0"),
-       REGULATOR_SUPPLY("vsupply", "mcde.0"),
+       REGULATOR_SUPPLY("vsupply", "mcde"),
+};
+
+/* SVA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_svammdsp_consumers[] = {
+       REGULATOR_SUPPLY("sva-mmdsp", "cm_control"),
+};
+
+/* SVA pipe regulator switch */
+static struct regulator_consumer_supply db8500_svapipe_consumers[] = {
+       REGULATOR_SUPPLY("sva-pipe", "cm_control"),
+};
+
+/* SIA MMDSP regulator switch */
+static struct regulator_consumer_supply db8500_siammdsp_consumers[] = {
+       REGULATOR_SUPPLY("sia-mmdsp", "cm_control"),
+};
+
+/* SIA pipe regulator switch */
+static struct regulator_consumer_supply db8500_siapipe_consumers[] = {
+       REGULATOR_SUPPLY("sia-pipe", "cm_control"),
+};
+
+static struct regulator_consumer_supply db8500_sga_consumers[] = {
+       REGULATOR_SUPPLY("v-mali", NULL),
+};
+
+/* ESRAM1 and 2 regulator switch */
+static struct regulator_consumer_supply db8500_esram12_consumers[] = {
+       REGULATOR_SUPPLY("esram12", "cm_control"),
+};
+
+/* ESRAM3 and 4 regulator switch */
+static struct regulator_consumer_supply db8500_esram34_consumers[] = {
+       REGULATOR_SUPPLY("v-esram34", "mcde"),
+       REGULATOR_SUPPLY("esram34", "cm_control"),
 };
 
 static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
@@ -2154,6 +2299,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-sva-mmdsp",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_svammdsp_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_svammdsp_consumers),
        },
        [DB8500_REGULATOR_SWITCH_SVAMMDSPRET] = {
                .constraints = {
@@ -2168,6 +2315,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-sva-pipe",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_svapipe_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),
        },
        [DB8500_REGULATOR_SWITCH_SIAMMDSP] = {
                .supply_regulator = "db8500-vape",
@@ -2175,6 +2324,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-sia-mmdsp",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_siammdsp_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_siammdsp_consumers),
        },
        [DB8500_REGULATOR_SWITCH_SIAMMDSPRET] = {
                .constraints = {
@@ -2188,6 +2339,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-sia-pipe",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_siapipe_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_siapipe_consumers),
        },
        [DB8500_REGULATOR_SWITCH_SGA] = {
                .supply_regulator = "db8500-vape",
@@ -2195,6 +2348,9 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-sga",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_sga_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_sga_consumers),
+
        },
        [DB8500_REGULATOR_SWITCH_B2R2_MCDE] = {
                .supply_regulator = "db8500-vape",
@@ -2211,6 +2367,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-esram12",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_esram12_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_esram12_consumers),
        },
        [DB8500_REGULATOR_SWITCH_ESRAM12RET] = {
                .constraints = {
@@ -2224,6 +2382,8 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {
                        .name = "db8500-esram34",
                        .valid_ops_mask = REGULATOR_CHANGE_STATUS,
                },
+               .consumer_supplies = db8500_esram34_consumers,
+               .num_consumer_supplies = ARRAY_SIZE(db8500_esram34_consumers),
        },
        [DB8500_REGULATOR_SWITCH_ESRAM34RET] = {
                .constraints = {
@@ -2255,7 +2415,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)
        if (ux500_is_svp())
                return -ENODEV;
 
-       init_prcm_registers();
+       db8500_prcmu_init_clkforce();
 
        /* Clean up the mailbox interrupts after pre-kernel code. */
        writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR);