aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-ux500/prcmu-fw.c
diff options
context:
space:
mode:
authorMian Yousaf Kaukab <mian.yousaf.kaukab@stericsson.com>2010-05-10 18:08:53 +0200
committerJohn Rigby <john.rigby@linaro.org>2010-09-02 22:44:36 -0600
commit3ea2f3a7cd8e2d470bdaba599efb57968d7114a6 (patch)
treec84adc3431887fb0b515ec5265e86905c3cef0f5 /arch/arm/mach-ux500/prcmu-fw.c
parent747e3fcf01bb45034fe21512fe244d175559b493 (diff)
ux500: move mach-u8500 to mach-ux500
Diffstat (limited to 'arch/arm/mach-ux500/prcmu-fw.c')
-rwxr-xr-xarch/arm/mach-ux500/prcmu-fw.c1992
1 files changed, 1992 insertions, 0 deletions
diff --git a/arch/arm/mach-ux500/prcmu-fw.c b/arch/arm/mach-ux500/prcmu-fw.c
new file mode 100755
index 00000000000..44208066790
--- /dev/null
+++ b/arch/arm/mach-ux500/prcmu-fw.c
@@ -0,0 +1,1992 @@
+/*
+ * Copyright 2009 ST-Ericsson.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ */
+
+/* Routines to talk to PRCMU firmware services */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/ktime.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+
+#include <mach/hardware.h>
+#include <mach/prcmu-regs.h>
+#include "prcmu-fw_ed.h"
+#include "prcmu-fw_v1.h"
+
+#define NAME "PRCMU"
+#define PM_DEBUG 0
+#define dbg_printk(format, arg...) (PM_DEBUG & 1) ? \
+ (printk(KERN_ALERT NAME ": " format , ## arg)) : \
+ ({do {} while (0); })
+
+void __iomem *rtc_register_base ;
+
+struct hw_usage{
+ char b2r2:1;
+ char mcde:1;
+ char esram1:1;
+ char esram2:1;
+ char esram3:1;
+ char esram4:1;
+};
+struct hw_usage hw_usg_state = {0};
+
+/* TASKLET declarations */
+static void prcmu_ack_mb7_status_tasklet(unsigned long);
+DECLARE_TASKLET(prcmu_ack_mb7_tasklet, prcmu_ack_mb7_status_tasklet, 0);
+
+static void prcmu_ack_mb0_wkuph_status_tasklet(unsigned long);
+DECLARE_TASKLET(prcmu_ack_mb0_wkuph_tasklet, \
+ prcmu_ack_mb0_wkuph_status_tasklet, 0);
+
+
+/* WAITQUEUES */
+DECLARE_WAIT_QUEUE_HEAD(ack_mb0_queue);
+DECLARE_WAIT_QUEUE_HEAD(ack_mb5_queue);
+DECLARE_WAIT_QUEUE_HEAD(ack_mb7_queue);
+
+/* Global var to determine if ca_wake_req is pending or not */
+static int ca_wake_req_pending;
+
+/* function pointer for shrm callback sequence for modem waking up arm */
+static void (*prcmu_ca_wake_req_shrm_callback)(u8);
+
+/* function pointer for shrm callback sequence for modem requesting reset */
+static void (*prcmu_modem_reset_shrm)(void);
+
+static int prcmu_set_hwacc_st(enum hw_acc_t hw_acc, enum hw_accst_t hw_accst);
+
+/* Internal functions */
+
+/**
+ * _wait_for_req_complete () - used for PWRSTTRH for AckMb0 and AckMb1 on V1
+ * @num: Mailbox number to operate on
+ *
+ * Polling loop to ensure that PRCMU FW has completed the requested op
+ */
+int _wait_for_req_complete(enum mailbox_t num)
+{
+ int timeout = 1000;
+
+ if (u8500_is_earlydrop()) {
+
+ /* Clear any error/status */
+ writel(0, PRCM_ACK_MB0_ED);
+ /* Set an interrupt to XP70 */
+ writel(1 << num, PRCM_MBOX_CPU_SET);
+ /* As of now only polling method, need to check if interrupt
+ * possible ?? TODO */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << num)) && timeout--)
+ cpu_relax();
+
+ if (!timeout)
+ return -EBUSY;
+ else
+ return readl(PRCM_ACK_MB0_ED);
+
+
+ } else {
+
+ /* checking any already on-going transaction */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << num)) && timeout--)
+ cpu_relax();
+
+ timeout = 1000;
+
+
+ /* Clear any error/status */
+ if (num == REQ_MB0)
+ writeb(0, PRCM_ACK_MB0_AP_PWRST_STATUS);
+ else if (num == REQ_MB1)
+ writel(0, PRCM_ACK_MB1);
+ else if (num == REQ_MB2)
+ writel(0, PRCM_ACK_MB2);
+
+
+
+ /* Set an interrupt to XP70 */
+ writel(1 << num, PRCM_MBOX_CPU_SET);
+ /* As of now only polling method, need to check if interrupt
+ * possible ?? TODO */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << num)) && timeout--)
+ cpu_relax();
+
+
+ if (!timeout)
+ return -EBUSY;
+
+ return 0;
+
+ }
+
+}
+
+/**
+ * prcmu_request_mailbox0 - request op on mailbox0
+ * @req: mailbox0 type param
+ *
+ * Fill up the mailbox 0 required fields and call polling loop
+ */
+int prcmu_request_mailbox0(union req_mb0_t *req)
+{
+ if (u8500_is_earlydrop()) {
+ writel(req->complete_field, PRCM_REQ_MB0_ED);
+ return _wait_for_req_complete(REQ_MB0_ED);
+
+ } else {
+ writel(req->complete_field, PRCM_REQ_MB0);
+ return _wait_for_req_complete(REQ_MB0);
+ }
+}
+
+/**
+ * prcmu_request_mailbox1 - request op on mailbox1
+ * @req: mailbox1 type param
+ *
+ * Fill up the mailbox 1 required fields and call polling loop
+ */
+int prcmu_request_mailbox1(union req_mb1_t *req)
+{
+ if (u8500_is_earlydrop()) {
+ writew(req->complete_field, PRCM_REQ_MB1_ED);
+ return _wait_for_req_complete(REQ_MB1_ED);
+ } else {
+ writew(req->complete_field, PRCM_REQ_MB1);
+ return _wait_for_req_complete(REQ_MB1);
+ }
+}
+
+/**
+ * prcmu_request_mailbox2 - request op on mailbox2
+ * @req: mailbox2 type param
+ *
+ * Fill up the mailbox 2 required fields and call polling loop
+ */
+int prcmu_request_mailbox2(union req_mb2_t *req)
+{
+ if (u8500_is_earlydrop()) {
+ writel(req->complete_field[0], PRCM_REQ_MB2_ED);
+ writel(req->complete_field[1], PRCM_REQ_MB2_ED + 4);
+ writel(req->complete_field[2], PRCM_REQ_MB2_ED + 8);
+ return _wait_for_req_complete(REQ_MB2_ED);
+
+ } else {
+ writel(req->complete_field[0], PRCM_REQ_MB2);
+ writel(req->complete_field[1], PRCM_REQ_MB2 + 4);
+ writel(req->complete_field[2], PRCM_REQ_MB2 + 8);
+ return _wait_for_req_complete(REQ_MB2);
+ }
+
+}
+
+/* Exported APIs */
+
+/**
+ * prcmu_get_boot_status - PRCMU boot status checking
+ * Returns: the current PRCMU boot status
+ */
+int prcmu_get_boot_status(void)
+{
+ if (u8500_is_earlydrop())
+ return readb(PRCM_BOOT_STATUS_ED);
+ else
+ return readb(PRCM_BOOT_STATUS);
+}
+EXPORT_SYMBOL(prcmu_get_boot_status);
+
+/**
+ * prcmu_set_rc_a2p - This function is used to run few power state sequences
+ * @val: Value to be set, i.e. transition requested
+ * Returns: 0 on success, -EINVAL on invalid argument
+ *
+ * This function is used to run the following power state sequences -
+ * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+int prcmu_set_rc_a2p(enum romcode_write_t val)
+{
+ if (u8500_is_earlydrop()) {
+ if (val < RDY_2_DS_ED || val > RDY_2_XP70_RST_ED)
+ return -EINVAL;
+ writeb(val, PRCM_ROMCODE_A2P_ED);
+ return 0;
+ }
+
+
+ if (val < RDY_2_DS || val > RDY_2_XP70_RST)
+ return -EINVAL;
+ writeb(val, PRCM_ROMCODE_A2P);
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_set_rc_a2p);
+
+/**
+ * prcmu_get_rc_p2a - This function is used to get power state sequences
+ * Returns: the power transition that has last happened
+ *
+ * This function can return the following transitions-
+ * any state to ApReset, ApDeepSleep to ApExecute, ApExecute to ApDeepSleep
+ */
+enum romcode_read_t prcmu_get_rc_p2a(void)
+{
+ if (u8500_is_earlydrop())
+ return readb(PRCM_ROMCODE_P2A_ED);
+
+
+ return readb(PRCM_ROMCODE_P2A);
+}
+EXPORT_SYMBOL(prcmu_get_rc_p2a);
+
+/**
+ * prcmu_get_current_mode - Return the current XP70 power mode
+ * Returns: Returns the current AP(ARM) power mode: init,
+ * apBoot, apExecute, apDeepSleep, apSleep, apIdle, apReset
+ */
+enum ap_pwrst_t prcmu_get_xp70_current_state(void)
+{
+ if (u8500_is_earlydrop())
+ return readb(PRCM_AP_PWR_STATE_ED);
+
+
+ return readb(PRCM_XP70_CUR_PWR_STATE);
+}
+EXPORT_SYMBOL(prcmu_get_xp70_current_state);
+
+/**
+ * prcmu_set_ap_mode - set the appropriate AP power mode
+ * @ap_pwrst_trans: Transition to be requested to move to new AP power mode
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function set the appropriate AP power mode.
+ * The caller can check the status following this call.
+ */
+int prcmu_set_ap_mode(enum ap_pwrst_trans_t ap_pwrst_trans)
+{
+ union req_mb0_t request = { {0} };
+
+ if (u8500_is_earlydrop()) {
+ if (ap_pwrst_trans
+ && (ap_pwrst_trans < APEXECUTE_TO_APSLEEP_ED
+ || ap_pwrst_trans > APEXECUTE_TO_APIDLE_ED))
+ return -EINVAL;
+ request.req_field.ap_pwrst_trans = ap_pwrst_trans;
+ return prcmu_request_mailbox0(&request);
+ }
+
+ if (ap_pwrst_trans
+ && (ap_pwrst_trans < APEXECUTE_TO_APSLEEP
+ || ap_pwrst_trans > APEXECUTE_TO_APIDLE))
+ return -EINVAL;
+ request.req_field.ap_pwrst_trans = ap_pwrst_trans;
+ return prcmu_request_mailbox0(&request);
+}
+EXPORT_SYMBOL(prcmu_set_ap_mode);
+
+/**
+ * prcmu_set_fifo_4500wu - Configures 4500 fifo interrupt as wake-up events
+ * @fifo_4500wu: The 4500 fifo interrupt to be configured as wakeup or not
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function de/configures 4500 fifo interrupt as wake-up events
+ * The caller can check the status following this call.
+ */
+int prcmu_set_fifo_4500wu(enum intr_wakeup_t fifo_4500wu)
+{
+ union req_mb0_t request = { {0} };
+
+ if (u8500_is_earlydrop()) {
+ if (fifo_4500wu < INTR_NOT_AS_WAKEUP_ED \
+ || fifo_4500wu > INTR_AS_WAKEUP_ED)
+ return -EINVAL;
+ request.req_field.fifo_4500wu = fifo_4500wu;
+ return prcmu_request_mailbox0(&request);
+ }
+
+ if (fifo_4500wu < INTR_NOT_AS_WAKEUP || fifo_4500wu > INTR_AS_WAKEUP)
+ return -EINVAL;
+ request.req_field.fifo_4500wu = fifo_4500wu;
+ return prcmu_request_mailbox0(&request);
+}
+EXPORT_SYMBOL(prcmu_set_fifo_4500wu);
+
+/**
+ * prcmu_set_ddr_pwrst - set the appropriate DDR power mode
+ * @ddr_pwrst: Power mode of DDR to which DDR needs to be switched
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function is not supported on ED by the PRCMU firmware
+ */
+int prcmu_set_ddr_pwrst(enum ddr_pwrst_t ddr_pwrst)
+{
+ union req_mb0_t request = { {0} };
+
+ if (u8500_is_earlydrop()) {
+ if (ddr_pwrst < DDR_PWR_STATE_UNCHANGED_ED || \
+ ddr_pwrst > TOBEDEFINED_ED)
+ return -EINVAL;
+ request.req_field.ddr_pwrst = ddr_pwrst;
+ return prcmu_request_mailbox0(&request);
+ }
+
+ if (ddr_pwrst < DDR_PWR_STATE_UNCHANGED || \
+ ddr_pwrst > DDR_PWR_STATE_OFFHIGHLAT)
+ return -EINVAL;
+ request.req_field.ddr_pwrst = ddr_pwrst;
+ return prcmu_request_mailbox0(&request);
+}
+EXPORT_SYMBOL(prcmu_set_ddr_pwrst);
+
+/**
+ * prcmu_set_arm_opp - set the appropriate ARM OPP
+ * @arm_opp: The new ARM operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function set the appropriate ARM operation mode
+ * The caller can check the status following this call.
+ * NOTE : THE PRCMU DOES NOT ISSUE AN IT TO ARM FOR A DVFS
+ * XISITION REQUEST. HENCE WE DO NOT NEED A MAILBOX
+ * TIMEOUT WAIT HERE FOR OPP DVFS
+ */
+int prcmu_set_arm_opp(enum arm_opp_t arm_opp)
+{
+ int timeout = 200;
+ enum arm_opp_t current_arm_opp;
+
+ if (u8500_is_earlydrop()) {
+ if (arm_opp < ARM_NO_CHANGE_ED || arm_opp > ARM_EXTCLK_ED)
+ return -EINVAL;
+ /* check for any ongoing AP state transitions */
+ while ((readl(PRCM_MBOX_CPU_VAL) & 1) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+ /* check for any ongoing ARM DVFS */
+ timeout = 200;
+ while ((readb(PRCM_M2A_DVFS_STAT_ED) == 0xff) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ /* Clear any error/status */
+ writel(0, PRCM_ACK_MB0_ED);
+
+ writeb(arm_opp, PRCM_ARM_OPP_ED);
+ writeb(APE_NO_CHANGE, PRCM_APE_OPP_ED);
+ writel(2, PRCM_MBOX_CPU_SET);
+
+ timeout = 1000;
+ while ((readl(PRCM_MBOX_CPU_VAL) & 2) && timeout--)
+ cpu_relax();
+
+ /* TODO: read mbox to ARM error here.. */
+ return 0;
+ }
+
+
+ if (arm_opp < ARM_NO_CHANGE || arm_opp > ARM_EXTCLK)
+ return -EINVAL;
+
+ /* check for any ongoing AP state transitions */
+ while ((readl(PRCM_MBOX_CPU_VAL) & 1) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ /* check for any ongoing ARM DVFS */
+ timeout = 200;
+ while ((readb(PRCM_ACK_MB1_CURR_DVFS_STATUS) == 0xff) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ /* clear the mailbox */
+ writel(0, PRCM_ACK_MB1);
+
+ /* write 0x0 into the header for ARM/APE operating point mgmt */
+ writel(0x0, _PRCM_MBOX_HEADER);
+
+ /* write ARMOPP request into the mailbox */
+ writel(arm_opp, PRCM_REQ_MB1_ARMOPP);
+ writel(APE_NO_CHANGE, PRCM_REQ_MB1_APEOPP);
+
+ _wait_for_req_complete(REQ_MB1);
+
+ current_arm_opp = readb(PRCM_ACK_MB1_CURR_ARMOPP);
+ if (arm_opp != current_arm_opp) {
+ printk(KERN_WARNING
+ "u8500-prcm : requested ARM DVFS Not Complete\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_set_arm_opp);
+
+/**
+ * prcmu_get_arm_opp - get the appropriate ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int prcmu_get_arm_opp(void)
+{
+ return readb(PRCM_ACK_MB1_CURR_ARMOPP);
+}
+EXPORT_SYMBOL(prcmu_get_arm_opp);
+
+/**
+ * prcmu_set_ape_opp - set the appropriate APE OPP
+ * @ape_opp: The new APE operating point to which transition is to be made
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function set the appropriate APE operation mode
+ * The caller can check the status following this call.
+ */
+int prcmu_set_ape_opp(enum ape_opp_t ape_opp)
+{
+ int timeout = 200;
+ enum ape_opp_t current_ape_opp;
+
+ if (ape_opp < APE_NO_CHANGE || ape_opp > APE_50_OPP)
+ return -EINVAL;
+
+ /* check for any ongoing AP state transitions */
+ while ((readl(PRCM_MBOX_CPU_VAL) & 1) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ /* check for any ongoing ARM DVFS */
+ timeout = 200;
+ while ((readb(PRCM_ACK_MB1_CURR_DVFS_STATUS) == 0xff) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ /* clear the mailbox */
+ writel(0, PRCM_ACK_MB1);
+
+ /* write 0x0 into the header for ARM/APE operating point mgmt */
+ writel(0x0, _PRCM_MBOX_HEADER);
+
+ /* write ARMOPP request into the mailbox */
+ writel(ARM_NO_CHANGE, PRCM_REQ_MB1_ARMOPP);
+ writel(ape_opp, PRCM_REQ_MB1_APEOPP);
+
+ /* trigger the XP70 IT11 to the XP70 */
+ writel(PRCM_XP70_TRIG_IT11, PRCM_MBOX_CPU_SET);
+
+ timeout = 1000;
+ while ((readl(PRCM_MBOX_CPU_VAL) & PRCM_XP70_TRIG_IT11)
+ && timeout--)
+ cpu_relax();
+
+ current_ape_opp = readb(PRCM_ACK_MB1_CURR_APEOPP);
+ if (ape_opp != current_ape_opp) {
+ printk(KERN_WARNING
+ "u8500-prcm : requested APE DVFS Not Complete\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_set_ape_opp);
+
+/**
+ * prcmu_get_arm_opp - get the appropriate ARM OPP
+ *
+ * Returns: the current ARM OPP
+ */
+int prcmu_get_ape_opp(void)
+{
+ return readb(PRCM_ACK_MB1_CURR_APEOPP);
+}
+EXPORT_SYMBOL(prcmu_get_ape_opp);
+
+
+int prcmu_set_hwacc(enum hw_acc_dev hw_acc, enum hw_accst_t hw_accst)
+{
+ enum hw_acc_t hw_acc_device;
+
+ /* check on which device is being used */
+ switch (hw_acc) {
+ case HW_ACC_SVAMMDSP:
+ hw_acc_device = SVAMMDSP;
+ break;
+ case HW_ACC_SVAPIPE:
+ hw_acc_device = SVAPIPE;
+ break;
+ case HW_ACC_SIAMMDSP:
+ hw_acc_device = SIAMMDSP;
+ break;
+ case HW_ACC_SIAPIPE:
+ hw_acc_device = SIAPIPE;
+ break;
+ case HW_ACC_SGA:
+ hw_acc_device = SGA;
+ break;
+ case HW_ACC_B2R2:
+ if (hw_accst == HW_ON)
+ hw_usg_state.b2r2 = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.b2r2 = 0;
+
+ if (hw_usg_state.mcde == 1)
+ return -EINVAL;
+ hw_acc_device = B2R2MCDE;
+ break;
+ case HW_ACC_MCDE:
+ if (hw_accst == HW_ON)
+ hw_usg_state.mcde = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.mcde = 0;
+
+ if (hw_usg_state.b2r2 == 1)
+ return -EINVAL;
+ hw_acc_device = B2R2MCDE;
+ break;
+ case HW_ACC_ESRAM1:
+ if (hw_accst == HW_ON)
+ hw_usg_state.esram1 = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.esram1 = 0;
+
+ if (hw_usg_state.esram2 == 1)
+ return -EINVAL;
+ hw_acc_device = ESRAM1;
+ break;
+ case HW_ACC_ESRAM2:
+ if (hw_accst == HW_ON)
+ hw_usg_state.esram2 = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.esram2 = 0;
+
+ if (hw_usg_state.esram1 == 1)
+ return -EINVAL;
+ hw_acc_device = ESRAM2;
+ break;
+ case HW_ACC_ESRAM3:
+ if (hw_accst == HW_ON)
+ hw_usg_state.esram3 = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.esram3 = 0;
+
+ if (hw_usg_state.esram4 == 1)
+ return -EINVAL;
+ hw_acc_device = ESRAM3;
+ break;
+ case HW_ACC_ESRAM4:
+ if (hw_accst == HW_ON)
+ hw_usg_state.esram4 = 1;
+ else if (hw_accst == HW_OFF)
+ hw_usg_state.esram4 = 0;
+
+ if (hw_usg_state.esram3 == 1)
+ return -EINVAL;
+ hw_acc_device = ESRAM4;
+ break;
+ default:
+ return -EINVAL;
+ };
+ return prcmu_set_hwacc_st(hw_acc_device, hw_accst);
+}
+EXPORT_SYMBOL(prcmu_set_hwacc);
+
+
+/**
+ * prcmu_set_ape_opp - set the appropriate h/w accelerator to power mode
+ * @hw_acc: the hardware accelerator being considered
+ * @hw_accst: The new h/w accelerator state(on/off/retention)
+ * Returns: 0 on success, non-zero on failure
+ *
+ * This function set the appropriate hardware accelerator to the requested
+ * power mode.
+ * The caller can check the status following this call.
+ */
+static int prcmu_set_hwacc_st(enum hw_acc_t hw_acc, enum hw_accst_t hw_accst)
+{
+ union req_mb2_t request;
+ int err = 0;
+ if (u8500_is_earlydrop()) {
+ if (hw_acc < SVAMMDSP_ED || hw_acc > ESRAM4_ED ||
+ hw_accst < HW_NO_CHANGE_ED || hw_accst \
+ > HW_ON_ED)
+ return -EINVAL;
+ memset(&request, 0x0, sizeof(request));
+ request.hw_accst_list[hw_acc].hw_accst = hw_accst;
+ return prcmu_request_mailbox2(&request);
+ }
+
+ if (hw_acc < SVAMMDSP || hw_acc > ESRAM4 ||
+ hw_accst < HW_NO_CHANGE || hw_accst > HW_ON)
+ return -EINVAL;
+
+ /* write the header into mailbox 2 */
+ writeb(DPS_H, PRCM_MBOX_HEADER_REQ_MB2);
+ /* fill out the request */
+ writel(0x0, PRCM_REQ_MB2_DPS_SVAMMDSP);
+ writel(0x0, PRCM_REQ_MB2_DPS_SGA);
+ writeb(hw_accst, PRCM_REQ_MB2 + hw_acc);
+ /* request for completion */
+ err = _wait_for_req_complete(REQ_MB2);
+ printk("\n readb(PRCM_ACK_MB2) = %x\n", readb(PRCM_ACK_MB2));\
+ if (readb(PRCM_ACK_MB2) == HWACC_PWRST_OK)
+ err = 0;
+ else
+ err = -EINVAL;
+ return err;
+}
+
+/**
+ * prcmu_get_m2a_status - Get the status or transition of the last request
+ * Returns: The status from MBOX to ARM during the last request.
+ * See the list of status/transitions for details.
+ */
+enum mbox_2_arm_stat_ed_t prcmu_get_m2a_status(void)
+{
+ return readb(PRCM_M2A_STATUS_ED);
+}
+EXPORT_SYMBOL(prcmu_get_m2a_status);
+
+/**
+ * prcmu_get_m2a_error - Get the error that occured in last request
+ * Returns: The error from MBOX to ARM during the last request.
+ * See the list of errors for details.
+ */
+enum mbox_to_arm_err_ed_t prcmu_get_m2a_error(void)
+{
+ return readb(PRCM_M2A_ERROR_ED);
+}
+EXPORT_SYMBOL(prcmu_get_m2a_error);
+
+/**
+ * prcmu_get_m2a_dvfs_status - Get the state of DVFS transition
+ * Returns: Either that state transition on DVFS is on going
+ * or completed, 0 if not used.
+ */
+enum dvfs_stat_t prcmu_get_m2a_dvfs_status(void)
+{
+ if (u8500_is_earlydrop())
+ return readb(PRCM_M2A_DVFS_STAT_ED);
+
+
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_get_m2a_dvfs_status);
+
+/**
+ * prcmu_get_m2a_hwacc_status - Get the state of HW Accelerator
+ * Returns: Either that state transition on hardware accelerator
+ * is on going or completed, 0 if not used.
+ */
+enum mbox_2_arm_hwacc_pwr_stat_t prcmu_get_m2a_hwacc_status(void)
+{
+ if (u8500_is_earlydrop())
+ return readb(PRCM_M2A_HWACC_STAT_ED);
+
+
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_get_m2a_hwacc_status);
+
+/**
+ * prcmu_set_irq_wakeup - set the ARM IRQ wake-up events
+ * @irq: ARM IRQ that needs to be set as wake up source
+ * Returns: 0 on success, -EINVAL on invalid argument
+ */
+int prcmu_set_irq_wakeup(uint32_t irq)
+{
+ unsigned long mask_reg;
+
+
+ if (irq < 32) {
+ mask_reg = PRCM_ARMITMSK31TO0;
+ } else if (irq < 64) {
+ mask_reg = PRCM_ARMITMSK63TO32;
+ irq -= 32;
+ } else if (irq < 96) {
+ mask_reg = PRCM_ARMITMSK95TO64;
+ irq -= 64;
+ } else if (irq < 128) {
+ mask_reg = PRCM_ARMITMSK127TO96;
+ irq -= 96;
+ } else
+ return -EINVAL;
+
+ writel(1 << irq | readl((volatile unsigned long *)(mask_reg)),
+ (volatile unsigned long *)(mask_reg));
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_set_irq_wakeup);
+
+/* FIXME : Make these generic enough to pass backup
+ * addresses for re-using the same functions
+ * for sleep/deep sleep modes
+ */
+/* Context Backups for ApSleep */
+unsigned int PRCC_backup[15];
+
+#define _PRCC_CLK_RST1_BASE IO_ADDRESS(U8500_PER1_BASE + 0xF000)
+#define _PRCC_CLK_RST2_BASE IO_ADDRESS(U8500_PER2_BASE + 0xF000)
+#define _PRCC_CLK_RST3_BASE IO_ADDRESS(U8500_PER3_BASE + 0xF000)
+#define _PRCC_CLK_RST5_BASE IO_ADDRESS(U8500_PER5_BASE + 0x1F000)
+#define _PRCC_CLK_RST6_BASE IO_ADDRESS(U8500_PER6_BASE + 0xF000)
+#define _PRCC_CLK_RST7_BASE IO_ADDRESS(U8500_PER7_BASE_ED + 0xF000)
+
+void pm_save_config_PRCC(void)
+{
+ uint8_t i = 0;
+
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST1_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST1_BASE + 0x14);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST2_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST2_BASE + 0x14);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST3_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST3_BASE + 0x14);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST5_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST5_BASE + 0x14);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST6_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST6_BASE + 0x14);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST7_BASE + 0x10);
+ PRCC_backup[i++] = readl(_PRCC_CLK_RST7_BASE + 0x14);
+}
+
+void pm_restore_config_PRCC(void)
+{
+ uint8_t i = 0;
+
+ writel(PRCC_backup[i++], _PRCC_CLK_RST1_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST1_BASE + 0x8);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST2_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST2_BASE + 0x8);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST3_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST3_BASE + 0x8);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST5_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST5_BASE + 0x8);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST6_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST6_BASE + 0x8);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST7_BASE + 0x0);
+ writel(PRCC_backup[i++], _PRCC_CLK_RST7_BASE + 0x8);
+}
+
+void __iomem *uart_backup_base, *uart_register_base;
+
+/* TODO : return success value to let decide to proceed
+ * for low power request or not
+ */
+void pm_save_config_UART(void)
+{
+ uart_register_base = ioremap(U8500_UART2_BASE, SZ_4K);
+ if (!uart_register_base) {
+ printk(KERN_WARNING
+ "u8500-prcm : cannot allocate uart register base\n");
+ }
+
+ uart_backup_base = kzalloc(SZ_4K, GFP_KERNEL);
+ if (!uart_backup_base) {
+ printk(KERN_WARNING
+ "u8500-prcm : cannot allocate uart register backup base\n");
+ }
+
+ /* Copy UART config */
+ memcpy(uart_backup_base, uart_register_base, SZ_4K);
+}
+
+void __iomem *ab8500_backup_base, *ab8500_register_base;
+
+void pm_restore_config_UART(void)
+{
+ memcpy(uart_register_base, uart_backup_base, SZ_4K);
+}
+
+/* Deep Sleep context save */
+
+/* public context backup */
+/* TODO : remove hard coded backup RAM addresses */
+void a9ss_save_public_config(void)
+{
+ int val;
+
+ /* this happens for CPU0 only as of now */
+ /* CP15 SCTLR */
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c1, c1, 2"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F80));
+
+ /* CP15 TTBR0 */
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c2, c0, 0"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F84));
+
+ /* CP15 TTBR1 */
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c2, c0, 1"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F88));
+
+ /* CP15 TTBCR */
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c2, c0, 2"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F8C));
+
+ /* CP15 DACR */
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c3, c0, 0"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F90));
+
+ /* CPSR */
+ __asm__ __volatile__(
+ "mrs %0, cpsr"
+ : "=r" (val)
+ :
+ : "cc");
+ writel(val, IO_ADDRESS(0x80151F98));
+
+ return;
+}
+
+void a9ss_restore_public_config(void)
+{
+ int val;
+
+ /* CPSR */
+ val = readl(IO_ADDRESS(0x80151F98));
+ __asm__ __volatile__(
+ "msr cpsr, %0"
+ :
+ : "r" (val));
+
+ /* CP15 DACR */
+ val = readl(IO_ADDRESS(0x80151F90));
+ __asm__ __volatile__(
+ "mcr p15, 0, %0, c3, c0, 0"
+ :
+ : "r" (val));
+
+ /* CP15 TTBCR */
+ val = readl(IO_ADDRESS(0x80151F8C));
+ __asm__ __volatile__(
+ "mcr p15, 0, %0, c2, c0, 2"
+ :
+ : "r" (val));
+
+ /* CP15 TTBR1 */
+ val = readl(IO_ADDRESS(0x80151F88));
+ __asm__ __volatile__(
+ "mcr p15, 0, %0, c2, c0, 1"
+ :
+ : "r" (val));
+
+ /* CP15 TTBR0 */
+ val = readl(IO_ADDRESS(0x80151F84));
+ __asm__ __volatile__(
+ "mcr p15, 0, %0, c2, c0, 0"
+ :
+ : "r" (val));
+
+ /* CP15 SCTLR */
+ val = readl(IO_ADDRESS(0x80151F80));
+ __asm__ __volatile__(
+ "mrc p15, 0, %0, c1, c1, 2"
+ :
+ : "r" (val));
+
+ return;
+}
+
+/* ARM SCU backup */
+#include <mach/scu.h>
+unsigned int scu_backup_config[5];
+
+void a9ss_save_scu_config(void)
+{
+ scu_backup_config[0] = __raw_readl(IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CTRL);
+ scu_backup_config[1] = __raw_readl(IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CONFIG);
+ scu_backup_config[2] = __raw_readl(IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CPU_STATUS);
+ scu_backup_config[3] = __raw_readl(IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_INVALIDATE);
+
+ return;
+}
+
+void a9ss_restore_scu_config(void)
+{
+ __raw_writel(scu_backup_config[0], IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CTRL);
+ __raw_writel(scu_backup_config[1], IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CONFIG);
+ __raw_writel(scu_backup_config[2], IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_CPU_STATUS);
+ __raw_writel(scu_backup_config[3], IO_ADDRESS(U8500_SCU_BASE)
+ + SCU_INVALIDATE);
+
+ return;
+}
+
+/**
+ * prcmu_apply_ap_state_transition - PRCMU State Transition function
+ * @transition: Transition to be requested to move to new AP power mode
+ * @ddr_state_req: Power mode of DDR to which DDR needs to be switched
+ * @_4500_fifo_wakeup: 4500 fifo interrupt to be configured as wakeup or not
+ * Returns: 0 on success, -EINVAL on failure
+ *
+ * NOTES: This routine assumes that ARM side housekeeping that
+ * needs to be done before entering sleep/deep-sleep, eg. the routines
+ * begin(), prepare() in platform_suspend_ops have done their job
+ * this routine only makes the required state transition and post-transition
+ * ARM side handling e.g. calling WFI for the non-boot cpus for idle/sleep
+ * or either waiting for the go-ahead from the deep-sleep secure interrupt
+ * handler and then setting the romcode before entering wfi. The typical
+ * users of this routine are the CPUIdle and LDM suspend ie. pm_suspend().
+ * The accelerators also need this service but currently this routine does
+ * not support it. This also assumes that the non-boot cpu's are in wfi
+ * and not wfe.
+ */
+/* FIXME : get these from platform/header files instead */
+/* GIC BAse Address */
+#define GIC_BASE_ADDR IO_ADDRESS(0xA0411000)
+
+/* ITs enabled for GIC. 104 is due to skipping of the STI and PPI sets.
+ * Rfer page 648 of the DB8500V1 spec v2.5
+ */
+#define DIST_ENABLE_SET (GIC_BASE_ADDR + 0x104)
+#define DIST_PENDING_SET (GIC_BASE_ADDR + 0x200)
+#define DIST_ENABLE_CLEAR (GIC_BASE_ADDR + 0x180)
+#define DIST_ACTIVE_BIT (GIC_BASE_ADDR + 0x304)
+
+#define PRCM_DEBUG_NOPWRDOWN_VAL IO_ADDRESS(0x80157194)
+#define PRCM_POWER_STATE_VAL IO_ADDRESS(0x8015725C)
+
+int prcmu_apply_ap_state_transition(enum ap_pwrst_trans_t transition,
+ enum ddr_pwrst_t ddr_state_req,
+ int _4500_fifo_wakeup)
+{
+ int ret = 0;
+ int sync = 0;
+ unsigned int val = 0, timeout = 0, tmp ;
+
+ if (u8500_is_earlydrop()) {
+ /* The PRCMU does do state transition validation, so we wl not
+ repeat it, just go ahead and call it */
+ if (transition == APBOOT_TO_APEXECUTE_ED)
+ sync = 1;
+
+ val = (ddr_state_req << 16) | (_4500_fifo_wakeup << 8) | \
+ transition;
+ writel(val, PRCM_REQ_MB0_ED);
+ writel(1, PRCM_MBOX_CPU_SET);
+
+ while ((readl(PRCM_MBOX_CPU_VAL) & 1) && sync)
+ cpu_relax();
+
+ switch (transition) {
+
+ case APEXECUTE_TO_APSLEEP_ED:
+ case APEXECUTE_TO_APIDLE_ED:
+ __asm__ __volatile__("dsb\n\t" "wfi\n\t" \
+ : : : "memory");
+ break;
+ case APEXECUTE_TO_APDEEPSLEEP_ED:
+ printk(KERN_INFO "TODO: deep sleep \n");
+ break;
+ case APBOOT_TO_APEXECUTE_ED:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+ }
+
+ switch (transition) {
+ case APEXECUTE_TO_APSLEEP:
+
+ /* PROGRAM THE ITMASK* registers for a Sleep */
+ writel(0x00000000, PRCM_ARMITMSK31TO0);
+ writel(0x00000100, PRCM_ARMITMSK63TO32);
+ writel(0x00000000, PRCM_ARMITMSK95TO64);
+ writel(0x00000000, PRCM_ARMITMSK127TO96);
+
+ /* probe the GIC if its already frozen */
+ tmp = readl(PRCM_A9_MASK_ACK);
+ if (tmp & 0x00000001)
+ printk(KERN_WARNING "GIC is already frozen\n");
+
+ /* write to GIC freeze */
+ tmp = readl(PRCM_A9_MASK_REQ);
+ tmp |= 0x01;
+ writel(tmp, PRCM_A9_MASK_REQ);
+
+ /* we wait for a random timeout. as of now the
+ * the GIC freeze isnt ACKed; so wait
+ */
+ timeout = 100;
+ while (timeout--)
+ cpu_relax();
+
+ /* PROGRAM WAKEUP EVENTS */
+ /* write CfgWkUpsH in the Header */
+ writeb(WKUPCFGH, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write to the mailbox */
+ /* E8500Wakeup_ARMITMGMT Bit (1<<17). it is interpreted by
+ * the firmware to set the register prcm_armit_maskxp70_it
+ * (which is btw secure and thus only accessed by the xp70)
+ */
+ writel((1<<17), PRCM_REQ_MB0_WKUP_8500);
+ writel(0x0, PRCM_REQ_MB0_WKUP_4500);
+
+ /* SIGNAL MAILBOX */
+ /* set the MBOX_CPU_SET bit to set an IT to xP70 */
+ writel(1 << 0, PRCM_MBOX_CPU_SET);
+
+ /* wait for corresponding MB0X_CPU_VAL bit to be cleared */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << 0)) && timeout--)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_INFO
+ "Timeout in prcmu_configure_wakeup_events!!\n");
+ return -EBUSY;
+ }
+
+ /* CREATE MAILBOX FOR EXECUTE TO IDLE POWER TRANSITION */
+ /* Write PwrStTrH=0 header to request a Power state xsition */
+ writeb(0x0, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write request to the MBOX0 */
+ writeb(APEXECUTE_TO_APSLEEP, PRCM_REQ_MB0_PWRSTTRH_APPWRST);
+ writeb(OFF, PRCM_REQ_MB0_PWRSTTRH_APPLLST);
+ writeb(ON, PRCM_REQ_MB0_PWRSTTRH_ULPCLKST);
+ writeb(0x56, PRCM_REQ_MB0_PWRSTTRH_BYTEFILL);
+
+ /* As per the sync logic, we are supposed to be the final CPU.
+ * If the other CPU isnt in wfi, better exit by putting
+ * ourselves in wfi
+ */
+ if (smp_processor_id()) {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x8)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ } else /* running on CPU0, check for CPU1 WFI standby */ {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x10)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ }
+
+ /* Enable RTC-INT in the GIC */
+ if (!(readl(PRCM_ARMITMSK31TO0) & 0x40000))
+ writel((readl(PRCM_ARMITMSK31TO0) | 0x40000),
+ PRCM_ARMITMSK31TO0);
+
+
+ /* FIXME : later on, the ARM should not request a Idle if one
+ * of the ITSTATUS0/5 are still alive!!!
+ */
+ if (readl(PRCM_ITSTATUS0) == 0x80) {
+ printk(KERN_WARNING "PRCM_ITSTATUS0 Not cleared\n");
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return -EBUSY;
+ }
+
+ /*
+ *
+ * REQUEST POWER XSITION
+ *
+ */
+
+ /* clear ACK MB0 */
+ writeb(0x0, PRCM_ACK_MB0);
+
+ /* trigger the XP70 IT10 to the XP70 */
+ writel(1, PRCM_MBOX_CPU_SET);
+
+ timeout = 200;
+ while (timeout--)
+ cpu_relax();
+
+ printk(KERN_INFO "u8500-prcm : To Deep Sleep\n");
+
+ /* Context Saving beings */
+
+ pm_save_config_PRCC();
+
+ pm_save_config_UART();
+
+ a9ss_save_public_config();
+
+ a9ss_save_scu_config();
+
+ /* TODO : take a backup of the SP and the public rom code
+ * entry point
+ */
+
+ /* here comes the wfi */
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+
+ a9ss_restore_scu_config();
+
+ a9ss_restore_public_config();
+
+ /* Restore PRCC Configs */
+ pm_restore_config_PRCC();
+
+ /* Restore UART Configs */
+ pm_restore_config_UART();
+
+ if (readb(PRCM_ACK_MB0_AP_PWRST_STATUS) == 0xF3)
+ printk(KERN_INFO "u8500-prcm : Woken from Sleep OK\n");
+
+ break;
+ case APEXECUTE_TO_APIDLE:
+ /* Copy the current GIC set enable config as wakeup */
+ for (val = 0; val < 4; val++) {
+ tmp = readl(DIST_ENABLE_SET + (val * 4));
+ writel(tmp, PRCM_ARMITMSK31TO0 + (val * 4));
+ }
+
+ /* PROGRAM WAKEUP EVENTS */
+ /* write CfgWkUpsH in the Header */
+ writeb(WKUPCFGH, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write to the mailbox */
+ /* E8500Wakeup_ARMITMGMT Bit (1<<17). it is interpreted by
+ * the firmware to set the register prcm_armit_maskxp70_it
+ * (which is btw secure and thus only accessed by the xp70)
+ */
+ writel((1<<17), PRCM_REQ_MB0_WKUP_8500);
+ writel(0x0, PRCM_REQ_MB0_WKUP_4500);
+ _wait_for_req_complete(REQ_MB0);
+
+#if 0
+ /* SIGNAL MAILBOX */
+ /* set the MBOX_CPU_SET bit to set an IT to xP70 */
+ writel(1 << 0, PRCM_MBOX_CPU_SET);
+
+ /* wait for corresponding MB0X_CPU_VAL bit to be cleared */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << 0)) && timeout--)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_INFO
+ "Timeout in prcmu_configure_wakeup_events!!\n");
+ return -EBUSY;
+ }
+#endif
+ /* CREATE MAILBOX FOR EXECUTE TO IDLE POWER TRANSITION */
+ /* Write PwrStTrH=0 header to request a Power state xsition */
+ writeb(0x0, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write request to the MBOX0 */
+ writeb(APEXECUTE_TO_APIDLE, PRCM_REQ_MB0_PWRSTTRH_APPWRST);
+ writeb(ON, PRCM_REQ_MB0_PWRSTTRH_APPLLST);
+ writeb(ON, PRCM_REQ_MB0_PWRSTTRH_ULPCLKST);
+ writeb(0x55, PRCM_REQ_MB0_PWRSTTRH_BYTEFILL);
+
+ /* As per the sync logic, we are supposed to be the final CPU.
+ * If the other CPU isnt in wfi, better exit by putting
+ * ourselves in wfi
+ */
+ if (smp_processor_id()) {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x8)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ } else /* running on CPU0, check for CPU1 WFI standby */ {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x10)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ }
+
+ /* FIXME : temporary hack to let UART2 interrupts enable the
+ * wake-up from ARM
+ */
+ if (!(readl(PRCM_ARMITMSK31TO0) & 0x04000000))
+ writel((readl(PRCM_ARMITMSK31TO0) | 0x04000000),
+ PRCM_ARMITMSK31TO0);
+
+ /* FIXME : tempoeary hack to wake up from RTC */
+ if (!(readl(PRCM_ARMITMSK31TO0) & 0x40000))
+ writel((readl(PRCM_ARMITMSK31TO0) | 0x40000),
+ PRCM_ARMITMSK31TO0);
+
+ /* FIXME : later on, the ARM should not request a Idle if one
+ * of the ITSTATUS0/5 are still alive!!!
+ */
+ if (readl(PRCM_ITSTATUS0) == 0x80) {
+ printk(KERN_WARNING "PRCM_ITSTATUS0 Not cleared\n");
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return -EBUSY;
+ }
+
+ /*
+ *
+ * REQUEST POWER XSITION
+ *
+ */
+
+ /* clear ACK MB0 */
+ writeb(0x0, PRCM_ACK_MB0);
+
+ /* trigger the XP70 IT10 to the XP70 */
+ writel(1, PRCM_MBOX_CPU_SET);
+
+ /* here comes the wfi */
+ __asm__ __volatile__("dsb\n\t" "wfi\n\t" : : : "memory");
+
+ writeb(RDWKUPACKH, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* Set an interrupt to XP70 */
+ writel(1 , PRCM_MBOX_CPU_SET);
+ while ((readl(PRCM_MBOX_CPU_VAL) & 1) && timeout--)
+ cpu_relax();
+ if (!timeout)
+ return -EBUSY;
+
+ break;
+ case APEXECUTE_TO_APDEEPSLEEP:
+ /* PROGRAM THE ITMASK* registers for deep sleep */
+ writel(0x00000000, PRCM_ARMITMSK31TO0);
+ writel(0x00000100, PRCM_ARMITMSK63TO32);
+ writel(0x00000000, PRCM_ARMITMSK95TO64);
+ writel(0x00000000, PRCM_ARMITMSK127TO96);
+
+ /* we skip the GIC freeze due to the FIQ being
+ * not handled by the ARM later on
+ */
+
+ /* PROGRAM WAKEUP EVENTS */
+ /* write CfgWkUpsH in the Header */
+ writeb(WKUPCFGH, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write to the mailbox */
+ /* E8500Wakeup_ARMITMGMT Bit (1<<17). it is interpreted by
+ * the firmware to set the register prcm_armit_maskxp70_it
+ * (which is btw secure and thus only accessed by the xp70)
+ */
+ writel((1<<17), PRCM_REQ_MB0_WKUP_8500);
+ writel(0x0, PRCM_REQ_MB0_WKUP_4500);
+
+ /* SIGNAL MAILBOX */
+ /* set the MBOX_CPU_SET bit to set an IT to xP70 */
+ writel(1 << 0, PRCM_MBOX_CPU_SET);
+
+ /* wait for corresponding MB0X_CPU_VAL bit to be cleared */
+ while ((readl(PRCM_MBOX_CPU_VAL) & (1 << 0)) && timeout--)
+ cpu_relax();
+ if (!timeout) {
+ printk(KERN_INFO
+ "Timeout in prcmu_configure_wakeup_events!!\n");
+ return -EBUSY;
+ }
+
+ /* CREATE MAILBOX FOR EXECUTE TO IDLE POWER TRANSITION */
+ /* Write PwrStTrH=0 header to request a Power state xsition */
+ writeb(0x0, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write request to the MBOX0 */
+ writeb(APEXECUTE_TO_APDEEPSLEEP, PRCM_REQ_MB0_PWRSTTRH_APPWRST);
+ writeb(OFF, PRCM_REQ_MB0_PWRSTTRH_APPLLST);
+ writeb(ON, PRCM_REQ_MB0_PWRSTTRH_ULPCLKST);
+ writeb(0x56, PRCM_REQ_MB0_PWRSTTRH_BYTEFILL);
+
+ /* As per the sync logic, we are supposed to be the final CPU.
+ * If the other CPU isnt in wfi, better exit by putting
+ * ourselves in wfi
+ */
+ if (smp_processor_id()) {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x8)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ } else /* running on CPU0, check for CPU1 WFI standby */ {
+ if (!(readl(PRCM_ARM_WFI_STANDBY) & 0x10)) {
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return 0;
+ }
+ }
+
+ /* Enable RTC-INT in the GIC */
+ if (!(readl(PRCM_ARMITMSK31TO0) & 0x40000))
+ writel((readl(PRCM_ARMITMSK31TO0) | 0x40000),
+ PRCM_ARMITMSK31TO0);
+
+
+ /* FIXME : later on, the ARM should not request a Idle if one
+ * of the ITSTATUS0/5 are still alive!!!
+ */
+ if (readl(PRCM_ITSTATUS0) == 0x80) {
+ printk(KERN_WARNING "PRCM_ITSTATUS0 Not cleared\n");
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+ return -EBUSY;
+ }
+
+ /*
+ *
+ * REQUEST POWER XSITION
+ *
+ */
+
+ /* clear ACK MB0 */
+ writeb(0x0, PRCM_ACK_MB0);
+
+ /* trigger the XP70 IT10 to the XP70 */
+ writel(1, PRCM_MBOX_CPU_SET);
+
+ timeout = 200;
+ while (timeout--)
+ cpu_relax();
+
+ printk(KERN_INFO "u8500-prcm : To Sleep\n");
+
+ /* Restore PRCC configs */
+ pm_save_config_PRCC();
+
+ pm_save_config_UART();
+
+ a9ss_save_public_config();
+
+ /* here comes the wfi */
+ __asm__ __volatile__(
+ "dsb\n\t" "wfi\n\t" : : : "memory");
+
+ /* Restore PRCC Configs */
+ pm_restore_config_PRCC();
+
+ /* Restore UART Configs */
+ pm_restore_config_UART();
+
+ if (readb(PRCM_ACK_MB0_AP_PWRST_STATUS) == 0xF3)
+ printk(KERN_INFO "u8500-prcm : Woken from Sleep OK\n");
+
+ break;
+
+ printk(KERN_INFO "TODO: deep sleep \n");
+ break;
+ case APBOOT_TO_APEXECUTE:
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ return ret;
+
+
+}
+EXPORT_SYMBOL(prcmu_apply_ap_state_transition);
+
+
+
+/**
+ * prcmu_i2c_read - PRCMU - 4500 communication using PRCMU I2C
+ * @reg: - db8500 register bank to be accessed
+ * @slave: - db8500 register to be accessed
+ * Returns: ACK_MB5 value containing the status
+ */
+int prcmu_i2c_read(u8 reg, u8 slave)
+{
+ uint8_t i2c_status;
+ uint8_t i2c_val;
+
+ dbg_printk("\nprcmu_4500_i2c_read: \
+ bank=%x;reg=%x;\n", reg, slave);
+
+ /* code related to FW 1_0_0_16 */
+ writeb(I2CREAD, PRCM_REQ_MB5_I2COPTYPE);
+ writeb(reg, PRCM_REQ_MB5_I2CREG);
+ writeb(slave, PRCM_REQ_MB5_I2CSLAVE);
+ writeb(0, PRCM_REQ_MB5_I2CVAL);
+
+ /* clear any previous ack */
+ writel(0, PRCM_ACK_MB5);
+
+ /* Set an interrupt to XP70 */
+ writel(PRCM_XP70_TRIG_IT17, PRCM_MBOX_CPU_SET);
+
+ dbg_printk("\n readl PRCM_ARM_IT1_VAL = %d \
+ ", readb(PRCM_ARM_IT1_VAL));
+ dbg_printk("\nwaiting at wait queue");
+
+
+ /* wait for interrupt */
+ wait_event_interruptible(ack_mb5_queue, \
+ !(readl(PRCM_MBOX_CPU_VAL) & (PRCM_XP70_TRIG_IT17)));
+
+ dbg_printk("\nAfter wait queue");
+
+ /* retrieve values */
+ dbg_printk("ack-mb5:transfer status = %x\n", \
+ readb(PRCM_ACK_MB5 + 0x1));
+ dbg_printk("ack-mb5:reg_add = %x\n", readb(PRCM_ACK_MB5 + 0x2));
+ dbg_printk("ack-mb5:slave_add = %x\n", \
+ readb(PRCM_ACK_MB5 + 0x2));
+ dbg_printk("ack-mb5:reg_val = %d\n", readb(PRCM_ACK_MB5 + 0x3));
+
+
+ /* return ack_mb5.req_field.reg_val for a
+ req->req_field.I2C_op == I2C_READ */
+
+ i2c_status = readb(PRCM_ACK_MB5 + 0x1);
+ i2c_val = readb(PRCM_ACK_MB5 + 0x3);
+
+
+ if (i2c_status == I2C_RD_OK)
+ return i2c_val;
+ else {
+
+ printk(KERN_INFO "prcmu_request_mailbox5:read return status = \
+ %d\n", i2c_status);
+ return -EINVAL;
+ }
+
+}
+EXPORT_SYMBOL(prcmu_i2c_read);
+
+
+/**
+ * prcmu_i2c_write - PRCMU-db8500 communication using PRCMU I2C
+ * @reg: - db8500 register bank to be accessed
+ * @slave: - db800 register to be written to
+ * @reg_data: - the data to write
+ * Returns: ACK_MB5 value containing the status
+ */
+int prcmu_i2c_write(u8 reg, u8 slave, u8 reg_data)
+{
+ uint8_t i2c_status;
+
+ /* NOTE : due to the I2C workaround, use
+ * MB5 for data, MB4 for the header
+ */
+
+ /* request I2C workaround header 0x0F */
+ writeb(0x0F, PRCM_MBOX_HEADER_REQ_MB4);
+
+ /* prepare the data for mailbox 5 */
+
+ /* register bank and I2C command */
+ writeb((reg << 1) | I2CWRITE, PRCM_REQ_MB5 + 0x0);
+ /* APE_I2C comm. specifics */
+ writeb((1 << 3) | 0x0, PRCM_REQ_MB5 + 0x1);
+ writeb(slave, PRCM_REQ_MB5_I2CSLAVE);
+ writeb(reg_data, PRCM_REQ_MB5_I2CVAL);
+
+ /* we request the mailbox 4 */
+ writel(PRCM_XP70_TRIG_IT14, PRCM_MBOX_CPU_SET);
+
+ /* we wait for ACK mailbox 5 */
+ /* FIXME : regularise this code with mailbox constants */
+ while ((readl(PRCM_ARM_IT1_VAL) & (0x1 << 5)) != (0x1 << 5))
+ cpu_relax();
+
+ /* we clear the ACK mailbox 5 interrupt */
+ writel((0x1 << 5), PRCM_ARM_IT1_CLEAR);
+
+ i2c_status = readb(PRCM_ACK_MB5 + 0x1);
+ if (i2c_status == I2C_WR_OK)
+ return I2C_WR_OK;
+ else {
+ printk(KERN_INFO "ape-i2c: i2c_status : 0x%x\n", i2c_status);
+ return -EINVAL;
+ }
+}
+EXPORT_SYMBOL(prcmu_i2c_write);
+
+int prcmu_i2c_get_status()
+{
+ return readb(PRCM_ACK_MB5_STATUS);
+}
+EXPORT_SYMBOL(prcmu_i2c_get_status);
+
+int prcmu_i2c_get_bank()
+{
+ return readb(PRCM_ACK_MB5_BANK);
+}
+EXPORT_SYMBOL(prcmu_i2c_get_bank);
+
+int prcmu_i2c_get_reg()
+{
+ return readb(PRCM_ACK_MB5_REG);
+}
+EXPORT_SYMBOL(prcmu_i2c_get_reg);
+
+int prcmu_i2c_get_val()
+{
+ return readb(PRCM_ACK_MB5_VAL);
+}
+EXPORT_SYMBOL(prcmu_i2c_get_val);
+
+
+
+/**
+ * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem
+ *
+ * Mailbox used : AckMb7
+ * ACK : HostPortAck
+ */
+int prcmu_ac_wake_req()
+{
+ u32 prcm_hostaccess = readl(PRCM_HOSTACCESS_REQ);
+ prcm_hostaccess = prcm_hostaccess | ARM_WAKEUP_MODEM;
+
+ /* 1. write to the PRCMU host_port_req register */
+ writel(prcm_hostaccess, PRCM_HOSTACCESS_REQ);
+
+ /* 2. wait for HostPortAck message in AckMb7
+ wait_event_interruptible(ack_mb7_queue, (readl(PRCM_ARM_IT1_VAL)
+ & (1<<7)));
+ */
+
+ if (prcmu_read_ack_mb7() == HOST_PORT_ACK)
+ return 0;
+ else {
+ dbg_printk(KERN_INFO "\nprcmu_ac_wake_req: ack_mb7 \
+ status = %x", prcmu_read_ack_mb7());
+ return -EINVAL;
+ }
+
+
+
+}
+EXPORT_SYMBOL(prcmu_ac_wake_req);
+
+/**
+ * prcmu_ac_sleep_req - called when ARM no longer needs to talk to modem
+ *
+ * Mailbox used - None
+ * ACK - None
+ */
+int prcmu_ac_sleep_req()
+{
+ /* clear the PRCMU host_port_req register */
+ u32 orig_val = readl(PRCM_HOSTACCESS_REQ);
+ orig_val = orig_val & 0xFFFFFFFE;
+
+ writel(orig_val, PRCM_HOSTACCESS_REQ);
+
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_ac_sleep_req);
+
+
+
+/**
+ * prcmu_configure_wakeup_events - configure 8500 and 4500 hw events on which
+ * PRCMU should wakeup AP
+ *
+ * Mailbox : PRCM_REQ_MB0
+ * Header : WKUPCFGH
+ * ACK : None
+ */
+int prcmu_configure_wakeup_events(u32 event_8500_mask, \
+ u32 event_4500_mask)
+{
+ int err = 0;
+
+ /* write CfgWkUpsH in the Header */
+ writeb(WKUPCFGH, PRCM_MBOX_HEADER_REQ_MB0);
+
+ /* write to the mailbox */
+ writel(event_8500_mask, PRCM_REQ_MB0_WKUP_8500);
+ writel(event_4500_mask, PRCM_REQ_MB0_WKUP_4500);
+
+ err = _wait_for_req_complete(REQ_MB0);
+
+ if (err) {
+ dbg_printk(KERN_INFO "\nTimeout configure_wakeup_events\n");
+ } else {
+ dbg_printk(KERN_INFO "\nprcmu_configure_wakeup_events \
+ done successfully!!\n");
+ }
+
+ /* No ack for this service. Directly return */
+ return err;
+
+
+
+}
+EXPORT_SYMBOL(prcmu_configure_wakeup_events);
+
+
+/**
+ * prcmu_get_wakeup_reason - Retrieve the event which caused AP wakeup
+ * @event_8500: - corresponds to 8500 events
+ * @event_4500: - corresponds to 4500 events
+ *
+ *
+ * Mailbox : PRCM_ACK_MB0
+ * Header : WKUPH
+ * ACK : None
+ */
+int prcmu_get_wakeup_reason(u32 *event_8500, u8 *event_4500 /* 20 bytes */)
+{
+ int i = 0;
+
+ /* read the Rdp field */
+ u8 rdp = readb(PRCM_ACK_MB0_PINGPONG_RDP);
+
+ /* right now, some issues present in ping pong from fw side :) */
+ /* read the event fields */
+
+ if (rdp) {
+ *event_8500 = readl(PRCM_ACK_MB0_WK1_EVENT_8500);
+ for (i = 0; i < PRCM_ACK_MB0_EVENT_4500_NUMBERS; i++)
+ event_4500[i] = readb(PRCM_ACK_MB0_WK1_EVENT_4500 + i);
+ } else {
+ *event_8500 = readl(PRCM_ACK_MB0_WK0_EVENT_8500);
+ for (i = 0; i < PRCM_ACK_MB0_EVENT_4500_NUMBERS; i++)
+ event_4500[i] = readb(PRCM_ACK_MB0_WK0_EVENT_4500 + i);
+ }
+
+ /* No ack. Directly return */
+ return 0;
+}
+EXPORT_SYMBOL(prcmu_get_wakeup_reason);
+
+
+/**
+ * prcmu_ack_wakeup_reason - Arm acks to PRCMU that it read the wakeup reason
+ *
+ *
+ * Mailbox used : PRCM_ACK_MB0
+ * Header used : RDWKUPACKH
+ * ACK : None
+ */
+int prcmu_ack_wakeup_reason()
+{
+ int err = 0;
+
+ /* Acknowledge the wakeup events */
+ writeb(RDWKUPACKH, PRCM_MBOX_HEADER_REQ_MB0);
+ err = _wait_for_req_complete(REQ_MB0);
+
+ return err;
+}
+EXPORT_SYMBOL(prcmu_ack_wakeup_reason);
+
+
+/**
+ * prcmu_is_ca_wake_req_pending - determine if ca_wake_req is pending
+ *
+ * Mailbox used - None
+ * Ack - None
+ *
+ * To be used by shrm driver to check if any ca_wake_req is pending
+ * initially and service the request accordingly.
+ */
+int prcmu_is_ca_wake_req_pending(void)
+{
+ int retval = ca_wake_req_pending;
+ if (ca_wake_req_pending)
+ ca_wake_req_pending--;
+
+ return retval;
+}
+EXPORT_SYMBOL(prcmu_is_ca_wake_req_pending);
+
+/**
+ * prcmu_set_callback_cawakereq - callback of shrm for ca_wake_req
+ * @*func: Function pointer of shrm
+ *
+ * To call the registered shrm callback whenever ca_wake_req is got
+ */
+void prcmu_set_callback_cawakereq(void (*func)(u8))
+{
+ prcmu_ca_wake_req_shrm_callback = func;
+}
+EXPORT_SYMBOL(prcmu_set_callback_cawakereq);
+
+
+/**
+ * prcmu_set_callback_modem_reset_request - Set the callback function to call
+ * when modem requests for Reset
+ *
+ * Mailbox used - None
+ * Ack - None
+ */
+void prcmu_set_callback_modem_reset_request(void (*func)(void))
+{
+ prcmu_modem_reset_shrm = func;
+}
+EXPORT_SYMBOL(prcmu_set_callback_modem_reset_request);
+
+/**
+ * prcmu_system_reset - System reset
+ *
+ * Sets the APE_SOFRST register which fires interrupt to fw
+ */
+void prcmu_system_reset(void)
+{
+ writel(1, PRCM_APE_SOFTRST);
+}
+EXPORT_SYMBOL(prcmu_system_reset);
+
+/**
+ * prcmu_read_ack_mb7 - Read the AckMb7 Status message
+ *
+ *
+ * Mailbox : AckMb7
+ * Header : None
+ * ACK : None
+ * associated IT : prcm_arm_it1_val[7]
+ */
+int prcmu_read_ack_mb7()
+{
+ return readb(PRCM_ACK_MB7);
+}
+EXPORT_SYMBOL(prcmu_read_ack_mb7);
+
+
+/**
+ * prcmu_ack_mb0_wkuph_status_tasklet - tasklet for ack mb0 IRQ
+ * @tasklet_data: tasklet data
+ *
+ * Tasklet for handling wakeup events given by IRQ corresponding to
+ * AckMb0
+ */
+void prcmu_ack_mb0_wkuph_status_tasklet(unsigned long tasklet_data)
+{
+ uint32_t event_8500 = 0;
+ unsigned char event_4500[20];
+
+ prcmu_get_wakeup_reason(&event_8500, event_4500);
+
+#if 0
+ dbg_printk("\n Inside prcmu_ack_mb0_wkuph_status_tasklet \n");
+ dbg_printk("\n\nAcknowledging by RDWKUPACKH\n\n");
+ prcmu_ack_wakeup_reason();
+#endif
+
+ /* ca_wake_req signal - modem wakes up ARM */
+ if (event_8500 & (1 << 5)) {
+ /* call shrm driver callback */
+ if (prcmu_ca_wake_req_shrm_callback != NULL)
+ (*prcmu_ca_wake_req_shrm_callback)(1);
+ else {
+ printk(KERN_INFO "\n prcmu: SHRM callback for \
+ ca_wake_req not registered!!\n");
+ }
+ }
+}
+
+/**
+ * prcmu_ack_mb7_status_tasklet - tasklet for mb7 IRQ
+ * @tasklet_data: tasklet data
+ *
+ * Tasklet for handling IPC communication related to AckMb7
+ */
+void prcmu_ack_mb7_status_tasklet(unsigned long tasklet_data)
+{
+ /* read the ack mb7 value and carry out actions accordingly */
+ u8 ack_mb7 = readb(PRCM_ACK_MB7);
+
+ switch (ack_mb7) {
+ case MOD_SW_RESET_REQ:
+ /*forward the reset request to ARM */
+ break;
+ case CA_SLEEP_REQ:
+ /* modem no longer requires to communicate
+ * with ARM so ARM can go to sleep */
+ if (prcmu_ca_wake_req_shrm_callback != NULL)
+ (*prcmu_ca_wake_req_shrm_callback)(0);
+ else {
+ printk(KERN_INFO "\n prcmu: SHRM callback for \
+ ca_wake_req not registered!!\n");
+ }
+ break;
+ case HOST_PORT_ACK:
+ /* modem up.ARM can communicate to modem */
+ /* wake_up_interruptible- prcmu_arm_wakeup_modem api*/
+ wake_up_interruptible(&ack_mb7_queue);
+ break;
+ };
+
+}
+
+/**
+ * prcmu_ack_mbox_irq_handler - IRQ handler for Ack mailboxes
+ * @irq: the irq number
+ *
+ * IRQ Handler for the Ack Mailboxes
+ * Find out the arm_it1_val bit and carry out the tasks
+ * accordingly
+ */
+irqreturn_t prcmu_ack_mbox_irq_handler(int irq, void *ctrlr)
+{
+ uint8_t prcm_arm_it1_val = 0;
+
+ dbg_printk("\nInside prcmu_ack_mbox_irq_handler !!\n");
+
+ /* check the prcm_arm_it1_val register to find which ACK mailbox is
+ * filled by PRCMU fw */
+ prcm_arm_it1_val = readb(PRCM_ARM_IT1_VAL);
+
+ dbg_printk(" prcm_arm_it1_val = %d ", prcm_arm_it1_val);
+
+ if (prcm_arm_it1_val & (1 << 0)) {
+ dbg_printk("\n Inside IRQ handler for Ack mb0 ");
+ wake_up_interruptible(&ack_mb0_queue);
+
+ dbg_printk("\n readb(PRCM_MBOX_HEADER_ACK_MB0) = %x", \
+ readb(PRCM_MBOX_HEADER_ACK_MB0));
+
+ /*read the header */
+ if ((readb(PRCM_MBOX_HEADER_ACK_MB0)) == PWRSTTRH) {
+ /* call the callback for AckMb0_PWRSTTRH */
+ /*tasklet_schedule(&prcmu_ack_mb0_pwrst_tasklet);*/
+ /* call wake_up_event_interruptible for mb0 */
+ dbg_printk("\n Inside IRQ handler for Ack mb0 \
+ PWRSTTRH and waking up ");
+
+ } else if ((readb(PRCM_MBOX_HEADER_ACK_MB0)) == WKUPH) {
+ dbg_printk("\n IRQ handler for Ack mb0 WKUPH ");
+ tasklet_schedule(&prcmu_ack_mb0_wkuph_tasklet);
+
+ }
+ } else if (prcm_arm_it1_val & (1<<1))
+ dbg_printk("\n IRQ handler for Ack mb1\n");
+ else if (prcm_arm_it1_val & (1<<2))
+ dbg_printk("\n IRQ handler for Ack mb2\n");
+ else if (prcm_arm_it1_val & (1<<3))
+ dbg_printk("\n IRQ handler for Ack mb3\n");
+ else if (prcm_arm_it1_val & (1<<4))
+ dbg_printk("\n IRQ handler for Ack mb4\n");
+ else if (prcm_arm_it1_val & (1<<5)) {
+ /* No header reading required */
+ /* call wake_up_event_interruptible for mb5 transaction */
+ dbg_printk("\nInside prcmu IRQ handler for mb5 ");
+ wake_up_interruptible(&ack_mb5_queue);
+ } else if (prcm_arm_it1_val & (1<<6))
+ dbg_printk("\n IRQ handler for Ack mb6\n");
+ else if (prcm_arm_it1_val & (1<<7)) {
+ /* No header reading required */
+ dbg_printk("\n IRQ handler for Ack mb7\n");
+ tasklet_schedule(&prcmu_ack_mb7_tasklet);
+ }
+
+ /* clear arm_it1_val bits */
+ writeb(255, PRCM_ARM_IT1_CLEAR);
+ return IRQ_HANDLED;
+}
+
+
+/**
+ * prcmu_fw_init - arch init call for the Linux PRCMU fw init logic
+ *
+ */
+static int prcmu_fw_init(void)
+{
+ int err = 0;
+ /* configure the wake-up events */
+ u32 event_8500 = 0x0;
+ u32 event_4500 = 0x0;
+ int prcm_arm_it1_val = 0;
+
+ if (u8500_is_earlydrop()) {
+ int i;
+ int status = prcmu_get_boot_status();
+ if (status != 0xFF || status != 0x2F) {
+ printk("PRCMU Firmware not ready\n");
+ return -EIO;
+ }
+ /* This can be enabled once PRCMU fw flashing is default */
+ prcmu_apply_ap_state_transition(APBOOT_TO_APEXECUTE_ED,
+ DDR_PWR_STATE_UNCHANGED_ED, 0);
+
+ /* for the time being assign wake-up duty to all interrupts */
+ /* Enable all interrupts as wake-up source */
+ for (i = 0; i < 128; i++)
+ prcmu_set_irq_wakeup(i);
+
+ return 0;
+ }
+
+ /* configure the wakeup events */
+ event_8500 = (1 << 5);
+ event_4500 = 0x0;
+ prcmu_configure_wakeup_events(event_8500, event_4500);
+ dbg_printk("(WkUpCfgOk=0xEA)PRCM_ACK_MB0_AP_PWRST_STATUS = %x\n",
+ readb(PRCM_ACK_MB0_AP_PWRST_STATUS));
+
+ /* retrieve the current interrupt status from PRCMU FW */
+ prcm_arm_it1_val = readb(PRCM_ARM_IT1_VAL);
+ if (prcm_arm_it1_val && (1 << 0)) {
+ /* clear the arm_it1_val to low the IT#47 */
+ writeb(0xFF, PRCM_ARM_IT1_CLEAR);
+
+ /* init irqs */
+ err = request_irq(IRQ_PRCM_ACK_MBOX,
+ prcmu_ack_mbox_irq_handler, IRQF_TRIGGER_RISING,
+ "prcmu_ack_mbox", NULL);
+ if (err < 0) {
+ printk(KERN_ERR "\nFailed to allocate \
+ IRQ_PRCM_ACK_MBOX!!\n");
+ err = -EBUSY;
+ free_irq(IRQ_PRCM_ACK_MBOX, NULL);
+ goto err_return;
+ }
+
+ /* check for any existing wakeup events */
+ if (readb(PRCM_MBOX_HEADER_ACK_MB0) == WKUPH) {
+ /* check here for the wakeup source for cawakereq
+ debugging on-going with fw for ping-pong.
+ Currently, its not possible to read source of
+ wakeup event correctly from fw mailbox */
+
+ /* increment pending flag for the shrm driver
+ to check */
+ ca_wake_req_pending++;
+
+ /* acknowledge reading the wakeup reason to fw */
+ prcmu_ack_wakeup_reason();
+ }
+ }
+
+
+ if (prcmu_get_xp70_current_state() == AP_BOOT)
+ prcmu_apply_ap_state_transition(APBOOT_TO_APEXECUTE, \
+ DDR_PWR_STATE_UNCHANGED, 0);
+ else if (prcmu_get_xp70_current_state() == AP_EXECUTE) {
+ printk(KERN_INFO "PRCMU firmware booted.\n");
+ } else {
+ printk(KERN_WARNING "WARNING - PRCMU firmware not yet booted!!!\n");
+ return -ENODEV;
+ }
+
+err_return:
+ return err;
+}
+
+arch_initcall(prcmu_fw_init);