aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
Diffstat (limited to 'arch')
-rw-r--r--arch/arm/mach-omap2/dvfs.c24
-rw-r--r--arch/arm/mach-omap2/dvfs.h5
-rw-r--r--arch/arm/mach-omap2/iommu2.c14
-rw-r--r--arch/arm/mach-omap2/pm44xx.c170
-rw-r--r--arch/arm/mach-omap2/remoteproc.c2
-rw-r--r--arch/arm/plat-omap/Makefile2
-rw-r--r--arch/arm/plat-omap/include/plat/gpio.h2
-rw-r--r--arch/arm/plat-omap/include/plat/iommu.h4
-rw-r--r--arch/arm/plat-omap/iommu.c49
-rw-r--r--arch/arm/plat-omap/omap_rpmsg.c2
-rw-r--r--arch/arm/plat-omap/rproc_user.c165
11 files changed, 393 insertions, 46 deletions
diff --git a/arch/arm/mach-omap2/dvfs.c b/arch/arm/mach-omap2/dvfs.c
index c1efdbf..6af0e67 100644
--- a/arch/arm/mach-omap2/dvfs.c
+++ b/arch/arm/mach-omap2/dvfs.c
@@ -1017,6 +1017,30 @@ bool omap_dvfs_is_scaling(struct voltagedomain *voltdm)
}
EXPORT_SYMBOL(omap_dvfs_is_scaling);
+/**
+ * omap_dvfs_is_any_dev_scaling() - returns true if any devices are scaling
+ *
+ * Should be called in non_preemptible context and is meant for
+ * Arch code to check and take appropriate measures if it is not-advisable
+ * for any activity which might conflict with scaling operation.
+ *
+ * IMPORTANT: This function is expected to be called with interrupt disabled,
+ * non-preemptible context.
+ */
+bool omap_dvfs_is_any_dev_scaling(void)
+{
+ struct omap_vdd_dvfs_info *dvfs_info;
+
+ WARN(!irqs_disabled(), "Called with IRQs enabled, inaccurate!\n");
+
+ list_for_each_entry(dvfs_info, &omap_dvfs_info_list, node) {
+ if (dvfs_info->is_scaling)
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL(omap_dvfs_is_any_dev_scaling);
+
#ifdef CONFIG_PM_DEBUG
static int dvfs_dump_vdd(struct seq_file *sf, void *unused)
{
diff --git a/arch/arm/mach-omap2/dvfs.h b/arch/arm/mach-omap2/dvfs.h
index 64f0afa..284e60f 100644
--- a/arch/arm/mach-omap2/dvfs.h
+++ b/arch/arm/mach-omap2/dvfs.h
@@ -23,6 +23,7 @@ int omap_device_scale(struct device *req_dev, struct device *target_dev,
unsigned long rate);
bool omap_dvfs_is_scaling(struct voltagedomain *voltdm);
+bool omap_dvfs_is_any_dev_scaling(void);
#else
static inline int omap_dvfs_register_device(struct device *dev,
char *voltdm_name, char *clk_name)
@@ -38,5 +39,9 @@ static inline bool omap_dvfs_is_scaling(struct voltagedomain *voltdm)
{
return false;
}
+static inline bool omap_dvfs_is_any_dev_scaling(void)
+{
+ return false;
+}
#endif
#endif
diff --git a/arch/arm/mach-omap2/iommu2.c b/arch/arm/mach-omap2/iommu2.c
index faa7646..f42a4a3 100644
--- a/arch/arm/mach-omap2/iommu2.c
+++ b/arch/arm/mach-omap2/iommu2.c
@@ -85,19 +85,21 @@ static void __iommu_set_twl(struct iommu *obj, bool on)
iommu_write_reg(obj, l, MMU_CNTL);
}
-
static int omap2_iommu_enable(struct iommu *obj)
{
u32 l, pa;
unsigned long timeout;
int ret = 0;
- if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K))
- return -EINVAL;
+ if (!obj->secure_mode) {
+ if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K))
+ return -EINVAL;
- pa = virt_to_phys(obj->iopgd);
- if (!IS_ALIGNED(pa, SZ_16K))
- return -EINVAL;
+ pa = virt_to_phys(obj->iopgd);
+ if (!IS_ALIGNED(pa, SZ_16K))
+ return -EINVAL;
+ } else
+ pa = (u32)obj->secure_ttb;
ret = omap_device_enable(obj->pdev);
if (ret)
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index 01d022d..6cfd501 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -165,6 +165,8 @@ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state, bool suspend)
}
}
+ omap2_gpio_set_edge_wakeup();
+
if (omap4_device_next_state_off()) {
omap2_gpio_prepare_for_idle(true);
omap_gpmc_save_context();
@@ -191,6 +193,8 @@ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state, bool suspend)
omap4_enter_lowpower(cpu, power_state);
if (omap4_device_prev_state_off()) {
+ /* Reconfigure the trim settings as well */
+ omap4_ldo_trim_configure();
omap4_dpll_resume_off();
omap4_cm_resume_off();
#ifdef CONFIG_PM_DEBUG
@@ -216,8 +220,6 @@ abort_device_off:
if (omap4_device_prev_state_off()) {
omap_dma_global_context_restore();
omap_gpmc_restore_context();
- /* Reconfigure the trim settings as well */
- omap4_ldo_trim_configure();
}
if (omap4_device_next_state_off()) {
@@ -233,6 +235,8 @@ abort_device_off:
OMAP4430_PRM_DEVICE_INST, OMAP4_PRM_IO_PMCTRL_OFFSET);
}
+ omap2_gpio_restore_edge_wakeup();
+
if (mpu_next_state < PWRDM_POWER_INACTIVE) {
omap_vc_set_auto_trans(mpu_voltdm,
OMAP_VC_CHANNEL_AUTO_TRANSITION_DISABLE);
@@ -487,6 +491,10 @@ static void omap4_configure_pwdm_suspend(bool is_off_mode)
list_for_each_entry(pwrst, &pwrst_list, node) {
bool parent_power_domain = false;
+
+ pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
+ pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm);
+
if ((!strcmp(pwrst->pwrdm->name, "cpu0_pwrdm")) ||
(!strcmp(pwrst->pwrdm->name, "cpu1_pwrdm")))
continue;
@@ -505,33 +513,104 @@ static void omap4_configure_pwdm_suspend(bool is_off_mode)
als =
get_achievable_state(pwrst->pwrdm->pwrsts_logic_ret,
logic_state, parent_power_domain);
- pwrdm_set_logic_retst(pwrst->pwrdm, als);
+ if (als < pwrst->saved_logic_state)
+ pwrdm_set_logic_retst(pwrst->pwrdm, als);
}
if (pwrst->pwrdm->pwrsts) {
pwrst->next_state =
get_achievable_state(pwrst->pwrdm->pwrsts, state,
parent_power_domain);
- omap_set_pwrdm_state(pwrst->pwrdm, pwrst->next_state);
+ if (pwrst->next_state < pwrst->saved_state)
+ omap_set_pwrdm_state(pwrst->pwrdm,
+ pwrst->next_state);
+ else
+ pwrst->next_state = pwrst->saved_state;
}
}
}
-static int omap4_pm_suspend(void)
+/**
+ * omap4_restore_pwdms_after_suspend() - Restore powerdomains after suspend
+ *
+ * Re-program all powerdomains to saved power domain states.
+ *
+ * returns 0 if all power domains hit targeted power state, -1 if any domain
+ * failed to hit targeted power state (status related to the actual restore
+ * is not returned).
+ */
+static int omap4_restore_pwdms_after_suspend(void)
{
struct power_state *pwrst;
- int state, ret = 0;
+ int cstate, pstate, ret = 0;
+
+ /* Restore next powerdomain state */
+ list_for_each_entry(pwrst, &pwrst_list, node) {
+ cstate = pwrdm_read_pwrst(pwrst->pwrdm);
+ pstate = pwrdm_read_prev_pwrst(pwrst->pwrdm);
+ if (pstate > pwrst->next_state) {
+ pr_info("Powerdomain (%s) didn't enter "
+ "target state %d Vs achieved state %d. "
+ "current state %d\n",
+ pwrst->pwrdm->name, pwrst->next_state,
+ pstate, cstate);
+ ret = -1;
+ }
+
+ /* If state already ON due to h/w dep, don't do anything */
+ if (cstate == PWRDM_POWER_ON)
+ continue;
+
+ /* If we have already achieved saved state, nothing to do */
+ if (cstate == pwrst->saved_state)
+ continue;
+
+ /* mpuss code takes care of this */
+ if ((!strcmp(pwrst->pwrdm->name, "cpu0_pwrdm")) ||
+ (!strcmp(pwrst->pwrdm->name, "cpu1_pwrdm")))
+ continue;
+
+ /*
+ * Skip pd program if saved state higher than current state
+ * Since we would have already returned if the state
+ * was ON, if the current state is yet another low power
+ * state, the PRCM specification clearly states that
+ * transition from a lower LP state to a higher LP state
+ * is forbidden.
+ */
+ if (pwrst->saved_state > cstate)
+ continue;
+
+ if (pwrst->pwrdm->pwrsts)
+ omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
+
+ if (pwrst->pwrdm->pwrsts_logic_ret)
+ pwrdm_set_logic_retst(pwrst->pwrdm,
+ pwrst->saved_logic_state);
+ }
+
+ return ret;
+}
+
+static int omap4_pm_suspend(void)
+{
+ int ret = 0;
+
+ /*
+ * If any device was in the middle of a scale operation
+ * then abort, as we cannot predict which part of the scale
+ * operation we interrupted.
+ */
+ if (omap_dvfs_is_any_dev_scaling()) {
+ pr_err("%s: oops.. middle of scale op.. aborting suspend\n",
+ __func__);
+ return -EBUSY;
+ }
/* Wakeup timer from suspend */
if (wakeup_timer_seconds || wakeup_timer_milliseconds)
omap2_pm_wakeup_on_timer(wakeup_timer_seconds,
wakeup_timer_milliseconds);
- /* Save current powerdomain state */
- list_for_each_entry(pwrst, &pwrst_list, node) {
- pwrst->saved_state = pwrdm_read_next_pwrst(pwrst->pwrdm);
- pwrst->saved_logic_state = pwrdm_read_logic_retst(pwrst->pwrdm);
- }
-
omap4_configure_pwdm_suspend(off_mode_enabled);
/* Enable Device OFF */
@@ -556,18 +635,8 @@ static int omap4_pm_suspend(void)
if (off_mode_enabled)
omap4_device_set_state_off(0);
- /* Restore next powerdomain state */
- list_for_each_entry(pwrst, &pwrst_list, node) {
- state = pwrdm_read_prev_pwrst(pwrst->pwrdm);
- if (state > pwrst->next_state) {
- pr_info("Powerdomain (%s) didn't enter "
- "target state %d\n",
- pwrst->pwrdm->name, pwrst->next_state);
- ret = -1;
- }
- omap_set_pwrdm_state(pwrst->pwrdm, pwrst->saved_state);
- pwrdm_set_logic_retst(pwrst->pwrdm, pwrst->saved_logic_state);
- }
+ ret = omap4_restore_pwdms_after_suspend();
+
if (ret)
pr_err("Could not enter target state in pm_suspend\n");
else
@@ -795,7 +864,8 @@ static void __init prcm_setup_regs(void)
* when a SWakeup is asserted from HSI to MPU (and DSP) :
* - force a DSP SW wakeup
* - wait DSP module to be fully ON
-* - force a DSP SW sleep
+* - Configure a DSP CLK CTRL to HW_AUTO
+* - Wait on DSP module to be OFF
*
* Note : we detect a Swakeup is asserted to MPU by checking when an interrupt
* is received while HSI module is ON.
@@ -804,26 +874,54 @@ static void __init prcm_setup_regs(void)
*/
static void omap_pm_clear_dsp_wake_up(void)
{
+ int ret;
+ int timeout = 10;
+
if (!tesla_pwrdm || !tesla_clkdm) {
WARN_ONCE(1, "%s: unable to use tesla workaround\n", __func__);
return;
}
- if (omap4_prminst_read_inst_reg(tesla_pwrdm->prcm_partition,
- tesla_pwrdm->prcm_offs,
- OMAP4_PM_PWSTST) & OMAP_INTRANSITION_MASK) {
+ ret = pwrdm_read_pwrst(tesla_pwrdm);
+ /* If Tesla power state in RET or OFF, then not hit by errata */
+ if (ret <= PWRDM_POWER_RET)
+ return;
+
+ if (clkdm_wakeup(tesla_clkdm))
+ pr_err("%s: Failed to force wakeup of %s\n", __func__,
+ tesla_clkdm->name);
- if (clkdm_wakeup(tesla_clkdm))
- pr_err("%s: Failed to force wakeup of %s\n", __func__,
- tesla_clkdm->name);
+ /* This takes less than a few microseconds, hence in context */
+ pwrdm_wait_transition(tesla_pwrdm);
- /* This takes less than a few microseconds, hence in context */
+ /*
+ * Check current power state of Tesla after transition, to make sure
+ * that Tesla is indeed turned ON.
+ */
+ ret = pwrdm_read_pwrst(tesla_pwrdm);
+ do {
pwrdm_wait_transition(tesla_pwrdm);
+ ret = pwrdm_read_pwrst(tesla_pwrdm);
+ } while ((ret < PWRDM_POWER_INACTIVE) && --timeout);
+
+ if (!timeout)
+ pr_err("%s: Tesla failed to transition to ON state!\n",
+ __func__);
+
+ timeout = 10;
+ clkdm_allow_idle(tesla_clkdm);
+
+ /* Ensure Tesla power state in OFF state */
+ ret = pwrdm_read_pwrst(tesla_pwrdm);
+ do {
+ pwrdm_wait_transition(tesla_pwrdm);
+ ret = pwrdm_read_pwrst(tesla_pwrdm);
+ } while ((ret >= PWRDM_POWER_INACTIVE) && --timeout);
+
+ if (!timeout)
+ pr_err("%s: Tesla failed to transition to OFF state\n",
+ __func__);
- if (clkdm_sleep(tesla_clkdm))
- pr_err("%s: Failed to force sleep of %s\n", __func__,
- tesla_clkdm->name);
- }
}
static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
diff --git a/arch/arm/mach-omap2/remoteproc.c b/arch/arm/mach-omap2/remoteproc.c
index 43d49eb..b8ae36f 100644
--- a/arch/arm/mach-omap2/remoteproc.c
+++ b/arch/arm/mach-omap2/remoteproc.c
@@ -59,7 +59,7 @@ static struct omap_rproc_pdata omap4_rproc_data[] = {
.timers_cnt = ARRAY_SIZE(ipu_timers),
.idle_addr = OMAP4430_CM_M3_M3_CLKCTRL,
.idle_mask = OMAP4430_STBYST_MASK,
- .suspend_addr = 0xb43f02d8,
+ .suspend_addr = 0xb3bf02d8,
.suspend_mask = ~0,
.sus_timeout = 5000,
.sus_mbox_name = "mailbox-1",
diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile
index f3ab483..c3fa015 100644
--- a/arch/arm/plat-omap/Makefile
+++ b/arch/arm/plat-omap/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o
# omap_device support (OMAP2+ only at the moment)
obj-$(CONFIG_ARCH_OMAP2) += omap_device.o
obj-$(CONFIG_ARCH_OMAP3) += omap_device.o
-obj-$(CONFIG_ARCH_OMAP4) += omap_device.o
+obj-$(CONFIG_ARCH_OMAP4) += omap_device.o rproc_user.o
obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
obj-$(CONFIG_OMAP_RPMSG) += omap_rpmsg.o
diff --git a/arch/arm/plat-omap/include/plat/gpio.h b/arch/arm/plat-omap/include/plat/gpio.h
index 24b61af..89af4ad 100644
--- a/arch/arm/plat-omap/include/plat/gpio.h
+++ b/arch/arm/plat-omap/include/plat/gpio.h
@@ -217,6 +217,8 @@ extern void omap2_gpio_prepare_for_idle(int off_mode);
extern void omap2_gpio_resume_after_idle(void);
extern void omap_set_gpio_debounce(int gpio, int enable);
extern void omap_set_gpio_debounce_time(int gpio, int enable);
+extern void omap2_gpio_set_edge_wakeup(void);
+extern void omap2_gpio_restore_edge_wakeup(void);
/*-------------------------------------------------------------------------*/
/* Wrappers for "new style" GPIO calls, using the new infrastructure
diff --git a/arch/arm/plat-omap/include/plat/iommu.h b/arch/arm/plat-omap/include/plat/iommu.h
index 38d92b1..ed33ddf 100644
--- a/arch/arm/plat-omap/include/plat/iommu.h
+++ b/arch/arm/plat-omap/include/plat/iommu.h
@@ -56,6 +56,8 @@ struct iommu {
u32 da_end;
struct platform_device *pdev;
struct pm_qos_request_list *qos_request;
+ void *secure_ttb;
+ bool secure_mode;
};
struct cr_regs {
@@ -179,6 +181,8 @@ extern int iommu_set_isr(const char *name,
void *priv),
void *isr_priv);
+extern int iommu_set_secure(const char *name, bool enable, void *data);
+
extern void iommu_save_ctx(struct iommu *obj);
extern void iommu_restore_ctx(struct iommu *obj);
diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c
index 0a19b41..7f52a30 100644
--- a/arch/arm/plat-omap/iommu.c
+++ b/arch/arm/plat-omap/iommu.c
@@ -237,6 +237,11 @@ int load_iotlb_entry(struct iommu *obj, struct iotlb_entry *e)
struct iotlb_lock l;
struct cr_regs *cr;
+ if (obj && obj->secure_mode) {
+ WARN_ON(1);
+ return -EBUSY;
+ }
+
if (!obj || !obj->nr_tlb_entries || !e)
return -EINVAL;
@@ -297,6 +302,11 @@ void flush_iotlb_page(struct iommu *obj, u32 da)
int i;
struct cr_regs cr;
+ if (obj && obj->secure_mode) {
+ WARN_ON(1);
+ return;
+ }
+
for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) {
u32 start;
size_t bytes;
@@ -616,6 +626,11 @@ int iopgtable_store_entry(struct iommu *obj, struct iotlb_entry *e)
{
int err;
+ if (obj && obj->secure_mode) {
+ WARN_ON(1);
+ return -EBUSY;
+ }
+
flush_iotlb_page(obj, e->da);
err = iopgtable_store_entry_core(obj, e);
#ifdef PREFETCH_IOTLB
@@ -637,6 +652,11 @@ void iopgtable_lookup_entry(struct iommu *obj, u32 da, u32 **ppgd, u32 **ppte)
{
u32 *iopgd, *iopte = NULL;
+ if (obj && obj->secure_mode) {
+ WARN_ON(1);
+ return;
+ }
+
iopgd = iopgd_offset(obj, da);
if (!*iopgd)
goto out;
@@ -706,6 +726,11 @@ size_t iopgtable_clear_entry(struct iommu *obj, u32 da)
{
size_t bytes;
+ if (obj && obj->secure_mode) {
+ WARN_ON(1);
+ return 0;
+ }
+
spin_lock(&obj->page_table_lock);
bytes = iopgtable_clear_entry_core(obj, da);
@@ -921,6 +946,30 @@ int iommu_set_isr(const char *name,
}
EXPORT_SYMBOL_GPL(iommu_set_isr);
+int iommu_set_secure(const char *name, bool enable, void *data)
+{
+ struct device *dev;
+ struct iommu *obj;
+
+ dev = driver_find_device(&omap_iommu_driver.driver, NULL, (void *)name,
+ device_match_by_alias);
+ if (!dev)
+ return -ENODEV;
+
+ obj = to_iommu(dev);
+ mutex_lock(&obj->iommu_lock);
+ if (obj->refcount) {
+ mutex_unlock(&obj->iommu_lock);
+ return -EBUSY;
+ }
+ obj->secure_mode = enable;
+ obj->secure_ttb = data;
+ mutex_unlock(&obj->iommu_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(iommu_set_secure);
+
/*
* OMAP Device MMU(IOMMU) detection
*/
diff --git a/arch/arm/plat-omap/omap_rpmsg.c b/arch/arm/plat-omap/omap_rpmsg.c
index 44efd71..63d7eb7 100644
--- a/arch/arm/plat-omap/omap_rpmsg.c
+++ b/arch/arm/plat-omap/omap_rpmsg.c
@@ -165,8 +165,6 @@ static int omap_rpmsg_mbox_callback(struct notifier_block *this,
pr_debug("mbox msg: 0x%x\n", msg);
- rproc_last_busy(rpdev->rproc);
-
switch (msg) {
case RP_MBOX_CRASH:
pr_err("%s has just crashed !\n", rpdev->rproc_name);
diff --git a/arch/arm/plat-omap/rproc_user.c b/arch/arm/plat-omap/rproc_user.c
new file mode 100644
index 0000000..0764cfe
--- /dev/null
+++ b/arch/arm/plat-omap/rproc_user.c
@@ -0,0 +1,165 @@
+/*
+ * Secure Mode Input interface to remoteproc driver
+ *
+ * Copyright (C) 2011 Texas Instruments. All rights reserved.
+ *
+ * Authors: Suman Anna <s-anna@ti.com>
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/miscdevice.h>
+#include <linux/cdev.h>
+#include <linux/uaccess.h>
+#include <linux/file.h>
+#include <linux/poll.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+#include <linux/remoteproc.h>
+
+
+#define RPROC_USER_NAME "rproc_user"
+#define RPROC_USER_DEVICES 1
+
+static DEFINE_MUTEX(rproc_user_mutex);
+
+struct rproc_user_device {
+ struct miscdevice mdev;
+};
+
+static struct rproc_user_device *ipu_device;
+static char *rproc_user_name = RPROC_USER_NAME;
+static bool secure_mode;
+static bool secure_attempt;
+
+
+static int rproc_user_open(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int rproc_user_release(struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static ssize_t rproc_user_read(struct file *filp, char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ u8 enable;
+ int ret = 1;
+
+ if (len != 1)
+ return -EINVAL;
+
+ if (mutex_lock_interruptible(&rproc_user_mutex))
+ return -EINTR;
+ enable = secure_mode ? 1 : 0;
+ if (copy_to_user((void *)ubuf, &enable, sizeof(enable)))
+ ret = -EFAULT;
+ mutex_unlock(&rproc_user_mutex);
+
+ return ret;
+}
+
+static ssize_t rproc_user_write(struct file *filp, const char __user *ubuf,
+ size_t len, loff_t *offp)
+{
+ int ret;
+ u8 enable;
+
+ if (len != 1)
+ return -EINVAL;
+
+ //enable = !(*(u8 *)ubuf == 0);
+ if (copy_from_user(&enable, (char __user *) ubuf, sizeof(enable)))
+ return -EFAULT;
+ enable = !(enable == 0);
+
+ if (mutex_lock_interruptible(&rproc_user_mutex))
+ return -EINTR;
+ if (enable && !secure_mode) {
+ ret = rproc_set_secure("ipu", enable);
+ if (!ret)
+ secure_mode = enable;
+ else
+ pr_err("rproc secure start failed, 0x%x\n", ret);
+ secure_attempt = enable;
+ } else if (!enable && secure_attempt) {
+ ret = rproc_set_secure("ipu", enable);
+ if (ret)
+ pr_err("rproc normal start failed 0x%x, urghh!!", ret);
+ secure_mode = enable;
+ secure_attempt = enable;
+ } else
+ ret = -EINVAL;
+ mutex_unlock(&rproc_user_mutex);
+
+ return ret ? ret : 1;
+}
+
+static const struct file_operations rproc_user_fops = {
+ .owner = THIS_MODULE,
+ .open = rproc_user_open,
+ .release = rproc_user_release,
+ .read = rproc_user_read,
+ .write = rproc_user_write,
+};
+
+static int __init rproc_user_init(void)
+{
+ int ret;
+
+ ipu_device = kzalloc(sizeof(struct rproc_user_device), GFP_KERNEL);
+ if (!ipu_device) {
+ pr_err("%s: memory allocation failed for ipu_device\n",
+ __func__);
+ ret = -ENOMEM;
+ goto exit;
+ }
+
+ ipu_device->mdev.minor = MISC_DYNAMIC_MINOR;
+ ipu_device->mdev.name = rproc_user_name;
+ ipu_device->mdev.fops = &rproc_user_fops;
+ ipu_device->mdev.parent = NULL;
+ ret = misc_register(&ipu_device->mdev);
+ if (ret) {
+ pr_err("rproc_user_init: failed to register rproc_user misc "
+ "device\n");
+ goto misc_fail;
+ }
+ return ret;
+
+misc_fail:
+ kfree(ipu_device);
+exit:
+ return ret;
+}
+module_init(rproc_user_init);
+
+static void __exit rproc_user_exit(void)
+{
+ misc_deregister(&ipu_device->mdev);
+ kfree(ipu_device);
+}
+module_exit(rproc_user_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("RemoteProc Secure Mode Interface Driver");
+MODULE_AUTHOR("Suman Anna");