diff options
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mach-omap2/dvfs.c | 24 | ||||
-rw-r--r-- | arch/arm/mach-omap2/dvfs.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-omap2/iommu2.c | 14 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm44xx.c | 170 | ||||
-rw-r--r-- | arch/arm/mach-omap2/remoteproc.c | 2 | ||||
-rw-r--r-- | arch/arm/plat-omap/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/gpio.h | 2 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/plat/iommu.h | 4 | ||||
-rw-r--r-- | arch/arm/plat-omap/iommu.c | 49 | ||||
-rw-r--r-- | arch/arm/plat-omap/omap_rpmsg.c | 2 | ||||
-rw-r--r-- | arch/arm/plat-omap/rproc_user.c | 165 |
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"); |