From ed24181c1b4eaa99bb607330ffec01aa70a6d7e5 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Thu, 14 Oct 2010 14:10:00 -0500 Subject: syslink:ipu_pm: send pid_death just to appm3 ipu_pm_notification is broadcasting all events to both sys/app processors whenever an event is sent using that api. For PID_DEATH event the requirement is only to send the message to APPM3 since is the one that will be executing the resource manager; also because of that sending the message to SYSM3 is unnecessary. Signed-off-by: Miguel Vadillo --- drivers/dsp/syslink/ipu_pm/ipu_pm.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/dsp/syslink/ipu_pm/ipu_pm.c b/drivers/dsp/syslink/ipu_pm/ipu_pm.c index a5a25b1783c9..9e4cb0f84af4 100644 --- a/drivers/dsp/syslink/ipu_pm/ipu_pm.c +++ b/drivers/dsp/syslink/ipu_pm/ipu_pm.c @@ -779,6 +779,11 @@ int ipu_pm_notifications(enum pm_event_type event_type, void *data) } break; case PM_PID_DEATH: + /* Just send the message to appm3 since is the one + * running the resource manager. + */ + if (proc_id == SYS_M3) + break; pm_msg.fields.msg_type = PM_NOTIFICATIONS; pm_msg.fields.msg_subtype = PM_PID_DEATH; pm_msg.fields.parm = PM_SUCCESS; -- cgit v1.2.3 From d292f78a06d437392147a696cb16d6c6f20f0da2 Mon Sep 17 00:00:00 2001 From: Fernando Guzman Lugo Date: Fri, 8 Oct 2010 01:48:08 -0500 Subject: syslink: notifier - add a mutext per notify_object Critital section are per notify_object we not need to block other notify_object of other processors. Instead create a new mutex per notify_object to protect critical sections which belong to a specific processor. This would increase the performance. Also fix unprotected part while calling the registered callback fucntions that was causing a kernel panic. Signed-off-by: Fernando Guzman Lugo --- arch/arm/plat-omap/include/syslink/notifydefs.h | 2 + drivers/dsp/syslink/omap_notify/notify.c | 147 ++++++++---------------- 2 files changed, 49 insertions(+), 100 deletions(-) diff --git a/arch/arm/plat-omap/include/syslink/notifydefs.h b/arch/arm/plat-omap/include/syslink/notifydefs.h index b0df5d536168..e04b76382ff8 100644 --- a/arch/arm/plat-omap/include/syslink/notifydefs.h +++ b/arch/arm/plat-omap/include/syslink/notifydefs.h @@ -86,6 +86,8 @@ struct notify_object { /* List of event callbacks registered */ struct list_head event_list[NOTIFY_MAXEVENTS]; /* List of event listeners registered */ + struct mutex lock; + /* Lock for event_list */ }; diff --git a/drivers/dsp/syslink/omap_notify/notify.c b/drivers/dsp/syslink/omap_notify/notify.c index 41793b7315fa..b7f2a267b1b8 100644 --- a/drivers/dsp/syslink/omap_notify/notify.c +++ b/drivers/dsp/syslink/omap_notify/notify.c @@ -220,6 +220,7 @@ struct notify_object *notify_create(void *driver_handle, u16 remote_proc_id, obj->remote_proc_id = remote_proc_id; obj->line_id = line_id; obj->nesting = 0; + mutex_init(&obj->lock); for (i = 0; i < notify_state.cfg.num_events; i++) INIT_LIST_HEAD(&obj->event_list[i]); @@ -335,36 +336,35 @@ int notify_register_event(u16 proc_id, u16 line_id, u32 event_id, goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } listener = kmalloc(sizeof(struct notify_event_listener), GFP_KERNEL); if (listener == NULL) { status = NOTIFY_E_MEMORY; - goto exit_unlock_mutex; + goto exit; } listener->callback.fn_notify_cbck = notify_callback_fxn; listener->callback.cbck_arg = cbck_arg; event_list = &(obj->event_list[stripped_event_id]); list_was_empty = list_empty(event_list); + mutex_lock_killable(&obj->lock); list_add_tail((struct list_head *) listener, event_list); - mutex_unlock(notify_state.gate_handle); + mutex_unlock(&obj->lock); if (list_was_empty) { /* Registering this event for the first time. Need to * register the callback function. @@ -373,10 +373,7 @@ int notify_register_event(u16 proc_id, u16 line_id, u32 event_id, event_id, _notify_exec_many, (uint *) obj); } - goto exit; -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_register_event failed! " @@ -425,27 +422,25 @@ int notify_register_event_single(u16 proc_id, u16 line_id, u32 event_id, goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } if (obj->callbacks[stripped_event_id].fn_notify_cbck != NULL) { status = NOTIFY_E_ALREADYEXISTS; - goto exit_unlock_mutex; + goto exit; } obj->callbacks[stripped_event_id].fn_notify_cbck = notify_callback_fxn; @@ -455,9 +450,6 @@ int notify_register_event_single(u16 proc_id, u16 line_id, u32 event_id, status = driver_handle->fxn_table.register_event(driver_handle, stripped_event_id); } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_register_event_single failed! " @@ -510,56 +502,50 @@ int notify_unregister_event(u16 proc_id, u16 line_id, u32 event_id, goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } event_list = &(obj->event_list[stripped_event_id]); if (list_empty(event_list)) { status = NOTIFY_E_NOTFOUND; - goto exit_unlock_mutex; + goto exit; } + mutex_lock_killable(&obj->lock); list_for_each_entry(listener, event_list, element) { /* Hash not matches, take next node */ if ((listener->callback.fn_notify_cbck == notify_callback_fxn) && (listener->callback.cbck_arg == cbck_arg)) { + list_del((struct list_head *)listener); found = true; break; } } if (found == false) { status = NOTIFY_E_NOTFOUND; - goto exit_unlock_mutex; + mutex_unlock(&obj->lock); + goto exit; } - /*sys_key = Gate_enterSystem();*/ - list_del((struct list_head *)listener); - /*Gate_leaveSystem(sys_key);*/ - mutex_unlock(notify_state.gate_handle); if (list_empty(event_list)) { status = notify_unregister_event_single(proc_id, line_id, event_id); } + mutex_unlock(&obj->lock); kfree(listener); - goto exit; - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_unregister_event failed! " @@ -603,28 +589,25 @@ int notify_unregister_event_single(u16 proc_id, u16 line_id, u32 event_id) goto exit; } - status = mutex_lock_interruptible(notify_state.gate_handle); - if (status) - goto exit; driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } if (obj->callbacks[stripped_event_id].fn_notify_cbck == NULL) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj->callbacks[stripped_event_id].fn_notify_cbck = NULL; @@ -633,9 +616,6 @@ int notify_unregister_event_single(u16 proc_id, u16 line_id, u32 event_id) status = driver_handle->fxn_table.unregister_event( driver_handle, stripped_event_id); } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_unregister_event_single failed! " @@ -679,22 +659,20 @@ int notify_send_event(u16 proc_id, u16 line_id, u32 event_id, u32 payload, goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } /* Maybe the proc is shutdown this functions will check and @@ -703,7 +681,7 @@ int notify_send_event(u16 proc_id, u16 line_id, u32 event_id, u32 payload, * the ducati_clkstctrl mode*/ status = ipu_pm_restore_ctx(proc_id); if (status) - goto exit_unlock_mutex; + goto exit; if (proc_id != multiproc_self()) { status = driver_handle->fxn_table.send_event(driver_handle, @@ -722,19 +700,11 @@ int notify_send_event(u16 proc_id, u16 line_id, u32 event_id, u32 payload, /* Event is disabled */ status = NOTIFY_E_EVTDISABLED; } else { - /* Leave critical section protection. */ - mutex_unlock(notify_state.gate_handle); /* Execute the callback function registered to the * event */ notify_exec(obj, event_id, payload); - /* Enter critical section protection. TBD: nesting */ - if (mutex_lock_interruptible(notify_state.gate_handle)) - WARN_ON(1); } } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_send_event failed! status = 0x%x", @@ -771,24 +741,23 @@ u32 notify_disable(u16 proc_id, u16 line_id) goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } + mutex_lock_killable(&obj->lock); obj->nesting++; if (obj->nesting == 1) { /* Disable receiving all events */ @@ -796,9 +765,7 @@ u32 notify_disable(u16 proc_id, u16 line_id) driver_handle->fxn_table.disable(driver_handle); } key = obj->nesting; - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); + mutex_unlock(&obj->lock); exit: if (status < 0) printk(KERN_ERR "notify_disable failed! status = 0x%x", status); @@ -833,38 +800,35 @@ void notify_restore(u16 proc_id, u16 line_id, u32 key) goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } if (key != obj->nesting) { status = NOTIFY_E_INVALIDSTATE; - goto exit_unlock_mutex; + goto exit; } + mutex_lock_killable(&obj->lock); obj->nesting--; if (obj->nesting == 0) { /* Enable receiving events */ if (proc_id != multiproc_self()) driver_handle->fxn_table.enable(driver_handle); } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); + mutex_unlock(&obj->lock); exit: if (status < 0) printk(KERN_ERR "notify_restore failed! status = 0x%x", status); @@ -907,22 +871,20 @@ void notify_disable_event(u16 proc_id, u16 line_id, u32 event_id) goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } if (proc_id != multiproc_self()) { @@ -932,9 +894,6 @@ void notify_disable_event(u16 proc_id, u16 line_id, u32 event_id) clear_bit(stripped_event_id, (unsigned long *) ¬ify_state.local_enable_mask); } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_disable_event failed! status = 0x%x", @@ -979,22 +938,20 @@ void notify_enable_event(u16 proc_id, u16 line_id, u32 event_id) goto exit; } - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; - goto exit_unlock_mutex; + goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; - goto exit_unlock_mutex; + goto exit; } if (proc_id != multiproc_self()) { @@ -1004,9 +961,6 @@ void notify_enable_event(u16 proc_id, u16 line_id, u32 event_id) set_bit(stripped_event_id, (unsigned long *)¬ify_state.local_enable_mask); } - -exit_unlock_mutex: - mutex_unlock(notify_state.gate_handle); exit: if (status < 0) { printk(KERN_ERR "notify_enable_event failed! status = 0x%x", @@ -1141,19 +1095,12 @@ static void _notify_exec_many(u16 proc_id, u16 line_id, u32 event_id, uint *arg, event_list = &(obj->event_list[event_id]); /* Enter critical section protection. */ - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); + mutex_lock_killable(&obj->lock); /* Use "NULL" to get the first EventListener on the list */ list_for_each_entry(listener, event_list, element) { - /* Leave critical section protection. */ - mutex_unlock(notify_state.gate_handle); listener->callback.fn_notify_cbck(proc_id, line_id, event_id, listener->callback.cbck_arg, payload); - /* Enter critical section protection. */ - if (mutex_lock_interruptible(notify_state.gate_handle) != 0) - WARN_ON(1); } - /* Leave critical section protection. */ - mutex_unlock(notify_state.gate_handle); + mutex_unlock(&obj->lock); } -- cgit v1.2.3 From a7a6b24a121178617dcf2bff9dcbddc74f300be8 Mon Sep 17 00:00:00 2001 From: Shivananda Hebbar Date: Wed, 13 Oct 2010 20:53:56 -0500 Subject: SYSLINK : Disable devh notification for tiler events. Currently tiler notification events is tied to the tiler release function. On each call to tiler release, notification is sent to Ducati for resource cleanup which shouldn't be the case always. Kconfig option is added to to disable tiler notification on PID_DEATH. TODO: Revisit later Change-Id: I4c42fd1886538483244f7bd1d16a0e91ce311ba9 Signed-off-by: Shivananda Hebbar --- drivers/dsp/syslink/Kconfig | 6 ++++++ drivers/dsp/syslink/devh/44xx/devh44xx.c | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/drivers/dsp/syslink/Kconfig b/drivers/dsp/syslink/Kconfig index 72ef7da40330..fdd80a92e977 100644 --- a/drivers/dsp/syslink/Kconfig +++ b/drivers/dsp/syslink/Kconfig @@ -88,4 +88,10 @@ config DMM_DMA_API cache operations on the userspace buffers. This option would be made default once the code is stabilized +config TILER_PID_KILL_NOTIFICATIONS + bool "TILER Event Notification" + default n + help + This is the temporary hack to disable tiler notification + on PID_DEATH. endif diff --git a/drivers/dsp/syslink/devh/44xx/devh44xx.c b/drivers/dsp/syslink/devh/44xx/devh44xx.c index 272ea565a993..6d589cf8e0b6 100644 --- a/drivers/dsp/syslink/devh/44xx/devh44xx.c +++ b/drivers/dsp/syslink/devh/44xx/devh44xx.c @@ -379,7 +379,11 @@ static int devh44xx_sysm3_tiler_notifier_call(struct notifier_block *nb, switch ((int)val) { case TILER_DEVICE_CLOSE: +#if defined(CONFIG_TILER_PID_KILL_NOTIFICATIONS) return devh44xx_notifier_call(nb, val, v, pdata); +#else + return 0; +#endif default: return 0; } @@ -393,7 +397,11 @@ static int devh44xx_appm3_tiler_notifier_call(struct notifier_block *nb, switch ((int)val) { case TILER_DEVICE_CLOSE: +#if defined(CONFIG_TILER_PID_KILL_NOTIFICATIONS) return devh44xx_notifier_call(nb, val, v, pdata); +#else + return 0; +#endif default: return 0; } @@ -407,7 +415,11 @@ static int devh44xx_tesla_tiler_notifier_call(struct notifier_block *nb, switch ((int)val) { case TILER_DEVICE_CLOSE: +#if defined(CONFIG_TILER_PID_KILL_NOTIFICATIONS) return devh44xx_notifier_call(nb, val, v, pdata); +#else + return 0; +#endif default: return 0; } -- cgit v1.2.3 From 60ee7272bc748cdf44d698e0a824b13fd3c835e6 Mon Sep 17 00:00:00 2001 From: Fernando Guzman Lugo Date: Tue, 19 Oct 2010 15:49:03 -0500 Subject: omap: remoteproc - fix sparse warnings in remote proc about static This patch fixes these warnings: arch/arm/plat-omap/remoteproc.c:45:6: warning: symbol 'rproc_eventfd_ntfy' was not declared. Should it be static? arch/arm/plat-omap/remoteproc.c:180:5: warning: symbol 'rproc_reg_user_event' was not declared. Should it be static? arch/arm/plat-omap/remoteproc.c:227:5: warning: symbol 'rproc_unreg_user_event' was not declared. Should it be static? Signed-off-by: Fernando Guzman Lugo --- arch/arm/plat-omap/remoteproc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/arch/arm/plat-omap/remoteproc.c b/arch/arm/plat-omap/remoteproc.c index 6b1e28de8493..34b32becf490 100644 --- a/arch/arm/plat-omap/remoteproc.c +++ b/arch/arm/plat-omap/remoteproc.c @@ -42,7 +42,7 @@ static dev_t omap_rproc_dev; static atomic_t num_of_rprocs; -void rproc_eventfd_ntfy(struct omap_rproc *obj, int event) +static void rproc_eventfd_ntfy(struct omap_rproc *obj, int event) { struct omap_rproc_ntfy *fd_reg; @@ -177,7 +177,8 @@ static inline int rproc_get_state(struct omap_rproc *rproc) return pdata->ops->get_state(rproc->dev); } -int rproc_reg_user_event(struct omap_rproc *rproc, const void __user *arg) +static int rproc_reg_user_event(struct omap_rproc *rproc, + const void __user *arg) { struct omap_rproc_ntfy *fd_reg; int state; @@ -224,7 +225,8 @@ int rproc_reg_user_event(struct omap_rproc *rproc, const void __user *arg) return 0; } -int rproc_unreg_user_event(struct omap_rproc *rproc, const void __user *arg) +static int rproc_unreg_user_event(struct omap_rproc *rproc, + const void __user *arg) { struct omap_rproc_ntfy *fd_reg, *temp_reg; struct omap_rproc_reg_event_args args; -- cgit v1.2.3 From bb2670770710f0240fb1053eaaea473330fad50e Mon Sep 17 00:00:00 2001 From: Hari Kanigeri Date: Tue, 19 Oct 2010 14:37:29 -0500 Subject: omap:iodmm-cleanup and fixes This patch handles the following: 1. cleaned the switch statement in dmm_user.c to move the copy_from_user to individual functions. 2. Seperated the building of Scatter gather list to Map function. The list is created when the Map function is created and deleted when the unmap function is called. Previously this list was created during flush function and deleted during the invalidate function. The previous implementation would fail if the Users call the invalidate function directly without calling flush function, as it would require calling flush function prior to calling invalidate function. The previous implementation also includes the overhead in creating and deleting the SG list for every flush and invalidate calls. 3. Changed the logic to track mapping object based on MPU VA instead of page aligned MPU VA. This is because if there are 2 MPU buffers with virtual addresses within the Page boundary, then there would be 2 Mapping objects with same virtual address. 4. Delete the DMM pools only when all the handles to IODMM are closed. The ioctl DMM_IOCDELETEPOOL can be deprecated with this change. 5. Added the SYS ADMIN check for create dmm pools, and set tlb entry ioctls. This code is currently kept commented out. 6. DMA Flush APIs are still optionally selected using CONFIG_DMM_DMA_API option. This is not selected by default. Signed-off-by: Hari Kanigeri --- arch/arm/plat-omap/dmm_user.c | 177 ++------ arch/arm/plat-omap/include/plat/dmm_user.h | 37 +- arch/arm/plat-omap/iodmm.c | 657 ++++++++++++++++++----------- 3 files changed, 455 insertions(+), 416 deletions(-) diff --git a/arch/arm/plat-omap/dmm_user.c b/arch/arm/plat-omap/dmm_user.c index 3afa28db3d2d..3cd9a025c868 100644 --- a/arch/arm/plat-omap/dmm_user.c +++ b/arch/arm/plat-omap/dmm_user.c @@ -30,12 +30,10 @@ #include #include #include -#include #include #include #include -#include "iopgtable.h" #define OMAP_DMM_NAME "iovmm-omap" @@ -59,161 +57,46 @@ static int omap_dmm_ioctl(struct inode *inode, struct file *filp, switch (cmd) { case DMM_IOCSETTLBENT: - { - struct iotlb_entry e; - int size; - size = copy_from_user(&e, (void __user *)args, - sizeof(struct iotlb_entry)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - load_iotlb_entry(obj->iovmm->iommu, &e); + /* FIXME: re-visit this check to perform + proper permission checks */ + /* if (!capable(CAP_SYS_ADMIN)) + return -EPERM; */ + ret = program_tlb_entry(obj, (const void __user *)args); break; - } case DMM_IOCCREATEPOOL: - { - struct iovmm_pool_info pool_info; - int size; - - size = copy_from_user(&pool_info, (void __user *)args, - sizeof(struct iovmm_pool_info)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - omap_create_dmm_pool(obj, pool_info.pool_id, pool_info.size, - pool_info.da_begin); - break; - } - case DMM_IOCDELETEPOOL: - { - int pool_id; - int size; - - size = copy_from_user(&pool_id, (void __user *)args, - sizeof(int)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - ret = omap_delete_dmm_pool(obj, pool_id); + /* FIXME: re-visit this check to perform + proper permission checks */ + /* if (!capable(CAP_SYS_ADMIN)) + return -EPERM; */ + ret = omap_create_dmm_pool(obj, (const void __user *)args); break; - } case DMM_IOCMEMMAP: - { - struct dmm_map_info map_info; - int size; - int status; - - size = copy_from_user(&map_info, (void __user *)args, - sizeof(struct dmm_map_info)); - - status = dmm_user(obj, map_info.mem_pool_id, - map_info.da, map_info.mpu_addr, - map_info.size, map_info.flags); - ret = copy_to_user((void __user *)args, &map_info, - sizeof(struct dmm_map_info)); + ret = dmm_user(obj, (void __user *)args); break; - } case DMM_IOCMEMUNMAP: - { - u32 da; - int size; - int status = 0; - - size = copy_from_user(&da, (void __user *)args, sizeof(u32)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - status = user_un_map(obj, da); - ret = status; + ret = user_un_map(obj, (const void __user *)args); break; - } case IOMMU_IOCEVENTREG: - { - int fd; - int size; - struct iommu_event_ntfy *fd_reg; - - size = copy_from_user(&fd, (void __user *)args, sizeof(int)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - - fd_reg = kzalloc(sizeof(struct iommu_event_ntfy), GFP_KERNEL); - fd_reg->fd = fd; - fd_reg->evt_ctx = eventfd_ctx_fdget(fd); - INIT_LIST_HEAD(&fd_reg->list); - spin_lock_irq(&obj->iovmm->iommu->event_lock); - list_add_tail(&fd_reg->list, &obj->iovmm->iommu->event_list); - spin_unlock_irq(&obj->iovmm->iommu->event_lock); + ret = register_mmufault(obj, (const void __user *)args); break; - } case IOMMU_IOCEVENTUNREG: - { - int fd; - int size; - struct iommu_event_ntfy *fd_reg, *temp_reg; - - size = copy_from_user(&fd, (void __user *)args, sizeof(int)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - /* Free DMM mapped memory resources */ - spin_lock_irq(&obj->iovmm->iommu->event_lock); - list_for_each_entry_safe(fd_reg, temp_reg, - &obj->iovmm->iommu->event_list, list) { - if (fd_reg->fd == fd) { - list_del(&fd_reg->list); - kfree(fd_reg); - } - } - spin_unlock_irq(&obj->iovmm->iommu->event_lock); + ret = register_mmufault(obj, (const void __user *)args); break; - } case DMM_IOCMEMFLUSH: - { - int size; - int status; - struct dmm_dma_info dma_info; - size = copy_from_user(&dma_info, (void __user *)args, - sizeof(struct dmm_dma_info)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - status = proc_begin_dma(obj, dma_info.pva, dma_info.ul_size, - dma_info.dir); - ret = status; + ret = proc_begin_dma(obj, (void __user *)args); break; - } case DMM_IOCMEMINV: - { - int size; - int status; - struct dmm_dma_info dma_info; - size = copy_from_user(&dma_info, (void __user *)args, - sizeof(struct dmm_dma_info)); - if (size) { - ret = -EINVAL; - goto err_user_buf; - } - status = proc_end_dma(obj, dma_info.pva, dma_info.ul_size, - dma_info.dir); - ret = status; + ret = proc_end_dma(obj, (void __user *)args); + break; + /* This ioctl can be deprecated */ + case DMM_IOCDELETEPOOL: break; - } case DMM_IOCDATOPA: default: return -ENOTTY; } -err_user_buf: - return ret; + return ret; } static int omap_dmm_open(struct inode *inode, struct file *filp) @@ -222,6 +105,7 @@ static int omap_dmm_open(struct inode *inode, struct file *filp) struct iovmm_device *obj; obj = container_of(inode->i_cdev, struct iovmm_device, cdev); + obj->refcount++; iodmm = kzalloc(sizeof(struct iodmm_struct), GFP_KERNEL); INIT_LIST_HEAD(&iodmm->map_list); @@ -240,9 +124,10 @@ static int omap_dmm_release(struct inode *inode, struct file *filp) if (!filp->private_data) { status = -EIO; - goto err; + goto err_out; } obj = filp->private_data; + flush_signals(current); status = mutex_lock_interruptible(&obj->iovmm->dmm_map_lock); @@ -253,12 +138,19 @@ static int omap_dmm_release(struct inode *inode, struct file *filp) pr_err("%s mutex_lock_interruptible returned 0x%x\n", __func__, status); } + user_remove_resources(obj); iommu_put(obj->iovmm->iommu); + + /* Delete all the DMM pools after the reference count goes to zero */ + if (--obj->iovmm->refcount == 0) + omap_delete_dmm_pools(obj); + kfree(obj); + filp->private_data = NULL; -err: +err_out: return status; } @@ -316,6 +208,7 @@ static int __devinit omap_dmm_probe(struct platform_device *pdev) mutex_init(&obj->dmm_map_lock); platform_set_drvdata(pdev, obj); return 0; + clean_cdev: cdev_del(&obj->cdev); err_cdev: @@ -326,14 +219,12 @@ static int __devexit omap_dmm_remove(struct platform_device *pdev) { struct iovmm_device *obj = platform_get_drvdata(pdev); int major = MAJOR(omap_dmm_dev); + device_destroy(omap_dmm_class, MKDEV(major, obj->minor)); cdev_del(&obj->cdev); platform_set_drvdata(pdev, NULL); - iopgtable_clear_entry_all(obj->iommu); - iommu_put(obj->iommu); - free_pages((unsigned long)obj->iommu->iopgd, - get_order(IOPGD_TABLE_SIZE)); kfree(obj); + return 0; } diff --git a/arch/arm/plat-omap/include/plat/dmm_user.h b/arch/arm/plat-omap/include/plat/dmm_user.h index b02d82d29e4a..c231314810ec 100644 --- a/arch/arm/plat-omap/include/plat/dmm_user.h +++ b/arch/arm/plat-omap/include/plat/dmm_user.h @@ -53,12 +53,8 @@ struct iovmm_pool_info { /* used to cache dma mapping information */ struct device_dma_map_info { - /* direction of DMA in action, or DMA_NONE */ - enum dma_data_direction dir; /* number of elements requested by us */ int num_pages; - /* number of elements returned from dma_map_sg */ - int sg_num; /* list of buffers used in this DMA action */ struct scatterlist *sg; }; @@ -68,7 +64,7 @@ struct dmm_map_info { u32 *da; u32 num_of_buf; u32 size; - u32 mem_pool_id; + u32 pool_id; u32 flags; }; @@ -100,22 +96,31 @@ struct iovmm_device { struct iommu *iommu; const char *name; /* List of memory pool it manages */ - struct list_head mmap_pool; + struct list_head mmap_pool; struct mutex dmm_map_lock; int minor; struct cdev cdev; + int refcount; }; /* user dmm functions */ -int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, - u32 va, size_t bytes, u32 flags); +int dmm_user(struct iodmm_struct *obj, void __user *args); + void user_remove_resources(struct iodmm_struct *obj); -int user_un_map(struct iodmm_struct *obj, u32 map_addr); -int proc_begin_dma(struct iodmm_struct *obj, void *pva, u32 ul_size, - enum dma_data_direction dir); -int proc_end_dma(struct iodmm_struct *obj, void *pva, u32 ul_size, - enum dma_data_direction dir); -int omap_create_dmm_pool(struct iodmm_struct *obj, int pool_id, int size, - int sa); -int omap_delete_dmm_pool(struct iodmm_struct *obj, int pool_id); + +int user_un_map(struct iodmm_struct *obj, const void __user *args); + +int proc_begin_dma(struct iodmm_struct *obj, const void __user *args); + +int proc_end_dma(struct iodmm_struct *obj, const void __user *args); + +int omap_create_dmm_pool(struct iodmm_struct *obj, const void __user *args); + +int omap_delete_dmm_pools(struct iodmm_struct *obj); + +int program_tlb_entry(struct iodmm_struct *obj, const void __user *args); + +int register_mmufault(struct iodmm_struct *obj, const void __user *args); + +int unregister_mmufault(struct iodmm_struct *obj, const void __user *args); #endif diff --git a/arch/arm/plat-omap/iodmm.c b/arch/arm/plat-omap/iodmm.c index 49eb0e40a6d1..a193c70c7c1d 100644 --- a/arch/arm/plat-omap/iodmm.c +++ b/arch/arm/plat-omap/iodmm.c @@ -6,6 +6,9 @@ * Authors: Ramesh Gupta * Hari Kanigeri * + * dma_map API usage in this code is inspired from Ohad Ben-Cohen's + * implementation in dspbridge code. + * * 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. @@ -28,6 +31,7 @@ #include #include #include +#include #include #include @@ -113,10 +117,48 @@ int temp_user_dma_op(unsigned long start, unsigned long end, int op) } while (start < end); up_read(&mm->mmap_sem); + return 0; } #endif +static inline struct gen_pool *get_pool_handle(struct iovmm_device *iovmm_obj, + int pool_id) +{ + struct iovmm_pool *pool; + + list_for_each_entry(pool, &iovmm_obj->mmap_pool, list) { + if (pool->pool_id == pool_id) + return pool->genpool; + } + return NULL; +} + +/* + * This function walks through the page tables to convert a userland + * virtual address to physical address + */ +static u32 __user_va2_pa(struct mm_struct *mm, u32 address) +{ + pgd_t *pgd; + pmd_t *pmd; + pte_t *ptep, pte; + + pgd = pgd_offset(mm, address); + if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { + pmd = pmd_offset(pgd, address); + if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { + ptep = pte_offset_map(pmd, address); + if (ptep) { + pte = *ptep; + if (pte_present(pte)) + return pte & PAGE_MASK; + } + } + } + return 0; +} + /* remember mapping information */ static struct dmm_map_object *add_mapping_info(struct iodmm_struct *obj, struct gen_pool *gen_pool, u32 va, u32 da, u32 size) @@ -222,6 +264,13 @@ static int match_containing_map_obj(struct dmm_map_object *map_obj, return res; } +/** + * Find the mapping object based on either MPU virtual address or + * Device virtual address. Which option to select to search for the mapping + * is specified with check_va flag. check_va is set to TRUE if search is + * based on MPU virtual address and FALSE if search is based on Device + * virtual address + */ static struct dmm_map_object *find_containing_mapping( struct iodmm_struct *obj, u32 va, u32 da, bool check_va, @@ -283,13 +332,36 @@ static int find_first_page_in_cache(struct dmm_map_object *map_obj, /* Cache operation against kernel address instead of users */ static int build_dma_sg(struct dmm_map_object *map_obj, unsigned long start, - ssize_t len, int pg_i) + size_t len) { struct page *page; unsigned long offset; ssize_t rest; int ret = 0, i = 0; - struct scatterlist *sg = map_obj->dma_info.sg; + unsigned long first_data_page = start >> PAGE_SHIFT; + unsigned long last_data_page = ((u32)(start + len - 1) >> PAGE_SHIFT); + /* calculating the number of pages this area spans */ + unsigned long num_pages = last_data_page - first_data_page + 1; + struct scatterlist *sg; + int pg_i; + + sg = kcalloc(num_pages, sizeof(*sg), GFP_KERNEL); + if (!sg) { + pr_err("%s: kcalloc failed\n", __func__); + ret = -ENOMEM; + goto out; + } + + sg_init_table(sg, num_pages); + + /* cleanup a previous sg allocation */ + /* this may happen if application doesn't signal for e/o DMA */ + kfree(map_obj->dma_info.sg); + + map_obj->dma_info.sg = sg; + map_obj->dma_info.num_pages = num_pages; + + pg_i = find_first_page_in_cache(map_obj, start); while (len) { page = get_mapping_page(map_obj, pg_i); @@ -335,16 +407,22 @@ static int memory_regain_ownership(struct device *dev, /* calculating the number of pages this area spans */ unsigned long num_pages = last_data_page - first_data_page + 1; struct device_dma_map_info *dma_info = &map_obj->dma_info; + long pg_i; if (!dma_info->sg) goto out; - if (dma_info->dir != dir || dma_info->num_pages != num_pages) { - pr_err("%s: dma info doesn't match given params\n", __func__); + if (num_pages > dma_info->num_pages) { + pr_err("%s: dma info params invalid\n", __func__); return -EINVAL; } - dma_unmap_sg(dev, dma_info->sg, num_pages, dma_info->dir); + pg_i = find_first_page_in_cache(map_obj, start); + if (pg_i == -1) { + ret = -EFAULT; + goto out; + } + dma_unmap_sg(dev, (dma_info->sg), num_pages, dir); pr_debug("%s: dma_map_sg unmapped\n", __func__); @@ -357,127 +435,111 @@ static int memory_give_ownership(struct device *dev, struct dmm_map_object *map_obj, unsigned long start, ssize_t len, enum dma_data_direction dir) { - int pg_i, ret, sg_num; - struct scatterlist *sg; + int ret, sg_num; + struct device_dma_map_info *dma_info = &map_obj->dma_info; unsigned long first_data_page = start >> PAGE_SHIFT; unsigned long last_data_page = ((u32)(start + len - 1) >> PAGE_SHIFT); /* calculating the number of pages this area spans */ unsigned long num_pages = last_data_page - first_data_page + 1; + long pg_i; pg_i = find_first_page_in_cache(map_obj, start); - if (pg_i < 0) { - pr_err("%s: failed to find first page in cache\n", __func__); - ret = -EINVAL; - goto out; - } - - sg = kcalloc(num_pages, sizeof(*sg), GFP_KERNEL); - if (!sg) { - pr_err("%s: kcalloc failed\n", __func__); - ret = -ENOMEM; + if (pg_i == -1) { + ret = -EFAULT; goto out; } - sg_init_table(sg, num_pages); - - /* cleanup a previous sg allocation */ - /* this may happen if application doesn't signal for e/o DMA */ - kfree(map_obj->dma_info.sg); - - map_obj->dma_info.sg = sg; - map_obj->dma_info.dir = dir; - map_obj->dma_info.num_pages = num_pages; - - ret = build_dma_sg(map_obj, start, len, pg_i); - if (ret) - goto kfree_sg; - - sg_num = dma_map_sg(dev, sg, num_pages, dir); + sg_num = dma_map_sg(dev, (dma_info->sg), num_pages, dir); if (sg_num < 1) { pr_err("%s: dma_map_sg failed: %d\n", __func__, sg_num); ret = -EFAULT; - goto kfree_sg; + goto out; } pr_debug("%s: dma_map_sg mapped %d elements\n", __func__, sg_num); - map_obj->dma_info.sg_num = sg_num; return 0; - -kfree_sg: - kfree(sg); - map_obj->dma_info.sg = NULL; out: return ret; } #endif -int proc_begin_dma(struct iodmm_struct *obj, void *pva, u32 ul_size, - enum dma_data_direction dir) +int proc_begin_dma(struct iodmm_struct *obj, const void __user *args) { int status = 0; + struct dmm_dma_info dma_info; #ifdef CONFIG_DMM_DMA_API - u32 va_align; struct dmm_map_object *map_obj; - struct device *dev = obj->iovmm->iommu->dev; - va_align = round_down((u32)pva, PAGE_SIZE); + struct device *dev; + + if (copy_from_user(&dma_info, (void __user *)args, + sizeof(struct dmm_dma_info))) + return -EFAULT; + dev = obj->iovmm->iommu->dev; mutex_lock(&obj->iovmm->dmm_map_lock); pr_debug("%s: addr 0x%x, size 0x%x, type %d\n", __func__, - (u32)va_align, - ul_size, dir); + (u32)dma_info.pva, + dma_info.ul_size, dma_info.dir); /* find requested memory are in cached mapping information */ - map_obj = find_containing_mapping(obj, (u32) va_align, 0, true, - ul_size); + map_obj = find_containing_mapping(obj, (u32)dma_info.pva, 0, true, + dma_info.ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; goto err_out; } - if (memory_give_ownership(dev, map_obj, (u32)pva, ul_size, dir)) { + if (memory_give_ownership(dev, map_obj, (u32)dma_info.pva, + dma_info.ul_size, dma_info.dir)) { pr_err("%s: InValid address parameters %x %x\n", - __func__, va_align, ul_size); + __func__, (u32)dma_info.pva, dma_info.ul_size); status = -EFAULT; } err_out: mutex_unlock(&obj->iovmm->dmm_map_lock); #else - - u32 end = (u32)pva + ul_size; - status = temp_user_dma_op((u32)pva, end, 3); + if (copy_from_user(&dma_info, (void __user *)args, + sizeof(struct dmm_dma_info))) + return -EFAULT; + status = temp_user_dma_op((u32)dma_info.pva, + (u32)dma_info.pva + dma_info.ul_size, 3); #endif return status; } -int proc_end_dma(struct iodmm_struct *obj, void *pva, u32 ul_size, - enum dma_data_direction dir) +int proc_end_dma(struct iodmm_struct *obj, const void __user *args) { int status = 0; + struct dmm_dma_info dma_info; #ifdef CONFIG_DMM_DMA_API - u32 va_align; + struct device *dev; struct dmm_map_object *map_obj; - struct device *dev = obj->iovmm->iommu->dev; - va_align = round_down((u32)pva, PAGE_SIZE); + + if (copy_from_user(&dma_info, (void __user *)args, + sizeof(struct dmm_dma_info))) + return -EFAULT; + dev = obj->iovmm->iommu->dev; pr_debug("%s: addr 0x%x, size 0x%x, type %d\n", __func__, - (u32)va_align, - ul_size, dir); + (u32)dma_info.pva, + dma_info.ul_size, dma_info.dir); mutex_lock(&obj->iovmm->dmm_map_lock); /* find requested memory are in cached mapping information */ - map_obj = find_containing_mapping(obj, (u32) va_align, 0, true, - ul_size); + map_obj = find_containing_mapping(obj, (u32)dma_info.pva, 0, true, + dma_info.ul_size); if (!map_obj) { pr_err("%s: find_containing_mapping failed\n", __func__); status = -EFAULT; goto err_out; } - if (memory_regain_ownership(dev, map_obj, (u32)pva, ul_size, dir)) { + if (memory_regain_ownership(dev, map_obj, (u32)dma_info.pva, + dma_info.ul_size, dma_info.dir)) { pr_err("%s: InValid address parameters %p %x\n", - __func__, pva, ul_size); + __func__, dma_info.pva, dma_info.ul_size); status = -EFAULT; goto err_out; } @@ -485,12 +547,116 @@ int proc_end_dma(struct iodmm_struct *obj, void *pva, u32 ul_size, err_out: mutex_unlock(&obj->iovmm->dmm_map_lock); #else - u32 end = (u32)pva + ul_size; - status = temp_user_dma_op((u32)pva, end, 1); + if (copy_from_user(&dma_info, (void __user *)args, + sizeof(struct dmm_dma_info))) + return -EFAULT; + status = temp_user_dma_op((u32)dma_info.pva, + (u32)dma_info.pva + dma_info.ul_size, 1); #endif return status; } +/** + * user_to_device_unmap() - unmaps Device virtual buffer. + * @mmu: Pointer to iommu handle. + * @da DSP address + * + * This function unmaps a user space buffer into DSP virtual address. + * + */ +static int user_to_device_unmap(struct iommu *mmu, u32 da, unsigned size) +{ + unsigned total = size; + unsigned start = da; + + while (total > 0) { + size_t bytes; + bytes = iopgtable_clear_entry(mmu, start); + if (bytes == 0) + bytes = PAGE_SIZE; + else + dev_dbg(mmu->dev, "%s: unmap 0x%x 0x%x\n", + __func__, start, bytes); + BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); + total -= bytes; + start += bytes; + } + return 0; +} + +static int __user_un_map(struct iodmm_struct *obj, u32 map_addr) +{ + int status = 0; + u32 va_align; + u32 size_align; + struct dmm_map_object *map_obj; + int i; + struct page *pg; + + va_align = round_down(map_addr, PAGE_SIZE); + + mutex_lock(&obj->iovmm->dmm_map_lock); + /* + * Update DMM structures. Get the size to unmap. + * This function returns error if the VA is not mapped + */ + /* find requested memory are in cached mapping information */ + map_obj = find_containing_mapping(obj, 0, map_addr, false, 0); + if (!map_obj) + goto err; + size_align = map_obj->size; + /* Remove mapping from the page tables. */ + status = user_to_device_unmap(obj->iovmm->iommu, va_align, + size_align); + if (status) + goto err; + + i = size_align/PAGE_SIZE; + while (i--) { + pg = map_obj->pages[i]; + if (pg && pfn_valid(page_to_pfn(pg))) { + if (page_count(pg) < 1) + pr_info("%s UNMAP FAILURE !!!\n", __func__); + else { + SetPageDirty(pg); + page_cache_release(pg); + } + } + } + /* + * A successful unmap should be followed by removal of map_obj + * from dmm_map_list, so that mapped memory resource tracking + * remains uptodate + */ + remove_mapping_information(obj, map_obj->da, map_obj->size); +err: + mutex_unlock(&obj->iovmm->dmm_map_lock); + return status; +} + + +/** + * user_un_map - Removes User's mapped address + * @obj: target dmm object + * @args Mapped address that needs to be unmapped + * + * removes user's dmm buffer mapping + **/ +int user_un_map(struct iodmm_struct *obj, const void __user *args) +{ + int status = 0; + u32 map_addr; + + if (copy_from_user(&map_addr, (void __user *)args, sizeof(u32))) + return -EFAULT; + + status = __user_un_map(obj, map_addr); + if (status) + pr_err("%s:Unmap of buffer 0x%x failedn", __func__, map_addr); + + return status; +} + /** * user_to_device_map() - maps user to dsp virtual address * @mmu: Pointer to iommu handle. @@ -562,19 +728,8 @@ static int user_to_device_map(struct iommu *mmu, u32 uva, u32 da, u32 size, break; } } - return res; -} - -static inline struct gen_pool *get_pool_handle(struct iovmm_device *iovmm_obj, - int pool_id) -{ - struct iovmm_pool *pool; - list_for_each_entry(pool, &iovmm_obj->mmap_pool, list) { - if (pool->pool_id == pool_id) - return pool->genpool; - } - return NULL; + return res; } /** @@ -597,7 +752,6 @@ static int phys_to_device_map(struct iodmm_struct *obj, struct dmm_map_object *dmm_obj; int da; u32 all_bits; - u32 num_bytes = bytes; int err = 0; u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K}; int size_flag[] = {MMU_CAM_PGSZ_16M, MMU_CAM_PGSZ_1M, @@ -605,7 +759,7 @@ static int phys_to_device_map(struct iodmm_struct *obj, int i; struct gen_pool *gen_pool; - if (!num_bytes) { + if (!bytes) { err = -EINVAL; goto exit; } @@ -620,34 +774,34 @@ static int phys_to_device_map(struct iodmm_struct *obj, gen_pool = get_pool_handle(obj->iovmm, pool_id); if (gen_pool) { da = gen_pool_alloc(gen_pool, bytes); - *mapped_addr = (da | ((u32)pa & (PAGE_SIZE - 1))); + *mapped_addr = (da | (pa & (PAGE_SIZE - 1))); } else { err = -EFAULT; goto exit; } } - dmm_obj = add_mapping_info(obj, gen_pool, pa, *mapped_addr, num_bytes); + dmm_obj = add_mapping_info(obj, gen_pool, pa, *mapped_addr, bytes); if (dmm_obj == NULL) { err = -ENODEV; goto err_add_map; } - while (num_bytes) { + while (bytes) { /* * To find the max. page size with which both PA & VA are * aligned */ all_bits = pa | da; for (i = 0; i < 4; i++) { - if ((num_bytes >= pg_size[i]) && ((all_bits & + if ((bytes >= pg_size[i]) && ((all_bits & (pg_size[i] - 1)) == 0)) { iotlb_init_entry(&e, da, pa, size_flag[i] | MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_32); iopgtable_store_entry(obj->iovmm->iommu, &e); - num_bytes -= pg_size[i]; + bytes -= pg_size[i]; da += pg_size[i]; pa += pg_size[i]; break; @@ -662,87 +816,33 @@ exit: return err; } -/** - * user_to_device_unmap() - unmaps Device virtual buffer. - * @mmu: Pointer to iommu handle. - * @da DSP address - * - * This function unmaps a user space buffer into DSP virtual address. - * - */ -static int user_to_device_unmap(struct iommu *mmu, u32 da, unsigned size) -{ - unsigned total = size; - unsigned start = da; - - while (total > 0) { - size_t bytes; - bytes = iopgtable_clear_entry(mmu, start); - if (bytes == 0) - bytes = PAGE_SIZE; - else - dev_dbg(mmu->dev, "%s: unmap 0x%x 0x%x\n", - __func__, start, bytes); - BUG_ON(!IS_ALIGNED(bytes, PAGE_SIZE)); - total -= bytes; - start += bytes; - } - return 0; -} - -/* - * ======== user_va2_pa ======== - * Purpose: - * This function walks through the page tables to convert a userland - * virtual address to physical address - */ -static u32 user_va2_pa(struct mm_struct *mm, u32 address) -{ - pgd_t *pgd; - pmd_t *pmd; - pte_t *ptep, pte; - - pgd = pgd_offset(mm, address); - if (!(pgd_none(*pgd) || pgd_bad(*pgd))) { - pmd = pmd_offset(pgd, address); - if (!(pmd_none(*pmd) || pmd_bad(*pmd))) { - ptep = pte_offset_map(pmd, address); - if (ptep) { - pte = *ptep; - if (pte_present(pte)) - return pte & PAGE_MASK; - } - } - } - return 0; -} - /** * dmm_user - Maps user buffer to Device address * @obj: target dmm object - * @pool_id: DMM pool id - * @da: Mapped Device Address - * @va: User virtual Address - * @bytes Size of the buffer to be mapped - * flags flags on how to interpret user buffer + * @args: DMM map information * * Maps given user buffer to Device address **/ -int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, - u32 va, size_t bytes, u32 flags) +int dmm_user(struct iodmm_struct *obj, void __user *args) { struct gen_pool *gen_pool; struct dmm_map_object *dmm_obj; struct iovmm_device *iovmm_obj = obj->iovmm; - u32 pa_align, da_align, size_align, tmp_addr; + u32 addr_align, da_align, size_align, tmp_addr; int err = 0; int i, num_of_pages; struct page *pg; struct vm_area_struct *vma; struct mm_struct *mm = current->mm; u32 io_addr; + struct dmm_map_info map_info; struct iotlb_entry e; + + if (copy_from_user(&map_info, (void __user *)args, + sizeof(struct dmm_map_info))) + return -EFAULT; + /* * Important Note: va is mapped from user application process * to current process - it must lie completely within the current @@ -751,8 +851,9 @@ int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, down_read(&mm->mmap_sem); /* Calculate the page-aligned PA, VA and size */ - pa_align = round_down((u32) va, PAGE_SIZE); - size_align = round_up(bytes + va - pa_align, PAGE_SIZE); + addr_align = round_down((u32) map_info.mpu_addr, PAGE_SIZE); + size_align = round_up(map_info.size + map_info.mpu_addr - addr_align, + PAGE_SIZE); mutex_lock(&iovmm_obj->dmm_map_lock); @@ -761,18 +862,19 @@ int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, * specified if pool_id as -1, so the da is interpreted * as the Device Address. */ - if (flags == DMM_DA_PHYS) { - err = phys_to_device_map(obj, pool_id, da, pa_align, - size_align, flags); - goto err; + if (map_info.flags == DMM_DA_PHYS) { + err = phys_to_device_map(obj, map_info.pool_id, map_info.da, + addr_align, size_align, map_info.flags); + goto exit; } - vma = find_vma(mm, va); + vma = find_vma(mm, map_info.mpu_addr); if (vma) { dev_dbg(iovmm_obj->iommu->dev, "VMAfor UserBuf: ul_mpu_addr=%x, ul_num_bytes=%x, " - "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", va, - bytes, vma->vm_start, vma->vm_end, + "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", + map_info.mpu_addr, + map_info.size, vma->vm_start, vma->vm_end, vma->vm_flags); } /* @@ -780,55 +882,57 @@ int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, * spread across several VMAs. So loop through and check if the entire * user buffer is covered */ - while ((vma) && (va + bytes > vma->vm_end)) { + while ((vma) && (map_info.mpu_addr + map_info.size > vma->vm_end)) { /* jump to the next VMA region */ vma = find_vma(mm, vma->vm_end + 1); dev_dbg(iovmm_obj->iommu->dev, "VMA for UserBuf ul_mpu_addr=%x ul_num_bytes=%x, " - "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", va, - bytes, vma->vm_start, vma->vm_end, + "vm_start=%lx, vm_end=%lx, vm_flags=%lx\n", + map_info.mpu_addr, + map_info.size, vma->vm_start, vma->vm_end, vma->vm_flags); } if (!vma) { pr_err("%s: Failed to get VMA region for 0x%x (%d)\n", - __func__, va, bytes); + __func__, map_info.mpu_addr, map_info.size); err = -EINVAL; - goto err; + goto exit; } /* * If user provided anonymous address, then don't allocate it from * from genpool */ - if (flags == DMM_DA_ANON) { + if (map_info.flags == DMM_DA_ANON) { gen_pool = NULL; - da_align = round_down(*da, PAGE_SIZE); + da_align = round_down((u32)map_info.da, PAGE_SIZE); } else { /* search through the list of available pools to * pool handle */ - gen_pool = get_pool_handle(iovmm_obj, pool_id); + gen_pool = get_pool_handle(iovmm_obj, map_info.pool_id); if (gen_pool) da_align = gen_pool_alloc(gen_pool, size_align); else { err = -EFAULT; - goto err; + goto exit; } } /* Mapped address = MSB of VA | LSB of PA */ - tmp_addr = (da_align | ((u32)va & (PAGE_SIZE - 1))); - dmm_obj = add_mapping_info(obj, gen_pool, pa_align, tmp_addr, + tmp_addr = (da_align | ((u32)map_info.mpu_addr & (PAGE_SIZE - 1))); + dmm_obj = add_mapping_info(obj, gen_pool, map_info.mpu_addr, tmp_addr, size_align); - *da = tmp_addr; if (!dmm_obj) - goto err; + goto exit; + + *map_info.da = tmp_addr; /* Mapping the IO buffers */ if (vma->vm_flags & VM_IO) { num_of_pages = size_align/PAGE_SIZE; for (i = 0; i < num_of_pages; i++) { - io_addr = user_va2_pa(current->mm, pa_align); + io_addr = __user_va2_pa(current->mm, addr_align); pg = phys_to_page(io_addr); iotlb_init_entry(&e, da_align, io_addr, @@ -837,81 +941,46 @@ int dmm_user(struct iodmm_struct *obj, u32 pool_id, u32 *da, MMU_RAM_ELSZ_32); iopgtable_store_entry(obj->iovmm->iommu, &e); da_align += PAGE_SIZE; - pa_align += PAGE_SIZE; + addr_align += PAGE_SIZE; dmm_obj->pages[i] = pg; } err = 0; - goto err; + goto exit; } /* Mapping the Userspace buffer */ - err = user_to_device_map(iovmm_obj->iommu, pa_align, + err = user_to_device_map(iovmm_obj->iommu, addr_align, da_align, size_align, dmm_obj->pages); - if (err) - remove_mapping_information(obj, tmp_addr, size_align); -err: + if (err) { + /* clean the entries that were mapped */ + __user_un_map(obj, tmp_addr); + goto exit; + } +#ifdef CONFIG_DMM_DMA_API + /* + * Build the SG list that would be required for dma map and + * unmap APIs + */ + err = build_dma_sg(dmm_obj, map_info.mpu_addr, map_info.size); + if (!err) { + /* + * calling dma_map_sg(cache flush) is essential for + * dma_unmap_sg to work since the sg->dma_address required + * for dma_unmap_sg is built during dma_map_sg call. + */ + err = memory_give_ownership(iovmm_obj->iommu->dev, dmm_obj, + map_info.mpu_addr, map_info.size, DMA_BIDIRECTIONAL); + } +#endif + +exit: + copy_to_user((void __user *)args, &map_info, + sizeof(struct dmm_map_info)); mutex_unlock(&iovmm_obj->dmm_map_lock); up_read(&mm->mmap_sem); return err; } -/** - * user_un_map - Removes User's mapped address - * @obj: target dmm object - * @map_addr Mapped address that needs to be unmapped - * - * removes user's dmm buffer mapping - **/ -int user_un_map(struct iodmm_struct *obj, u32 map_addr) -{ - int status = 0; - u32 va_align; - u32 size_align; - struct dmm_map_object *map_obj; - int i; - struct page *pg; - - va_align = round_down(map_addr, PAGE_SIZE); - - mutex_lock(&obj->iovmm->dmm_map_lock); - /* - * Update DMM structures. Get the size to unmap. - * This function returns error if the VA is not mapped - */ - /* find requested memory are in cached mapping information */ - map_obj = find_containing_mapping(obj, 0, map_addr, false, 0); - if (!map_obj) - goto err; - size_align = map_obj->size; - /* Remove mapping from the page tables. */ - status = user_to_device_unmap(obj->iovmm->iommu, va_align, - size_align); - if (status) - goto err; - - i = size_align/PAGE_SIZE; - while (i--) { - pg = map_obj->pages[i]; - if (pg && pfn_valid(page_to_pfn(pg))) { - if (page_count(pg) < 1) - pr_info("%s UNMAP FAILURE !!!\n", __func__); - else { - SetPageDirty(pg); - page_cache_release(pg); - } - } - } - /* - * A successful unmap should be followed by removal of map_obj - * from dmm_map_list, so that mapped memory resource tracking - * remains uptodate - */ - remove_mapping_information(obj, map_obj->da, map_obj->size); -err: - mutex_unlock(&obj->iovmm->dmm_map_lock); - return status; -} - /** * user_remove_resources - Removes User's dmm resources * @obj: target dmm object @@ -926,7 +995,7 @@ void user_remove_resources(struct iodmm_struct *obj) /* Free DMM mapped memory resources */ list_for_each_entry_safe(map_obj, temp_map, &obj->map_list, link) { - status = user_un_map(obj, map_obj->da); + status = __user_un_map(obj, map_obj->da); if (status) { pr_err("%s: proc_un_map failed!" " status = 0x%x\n", __func__, status); @@ -937,39 +1006,39 @@ void user_remove_resources(struct iodmm_struct *obj) /** * omap_create_dmm_pool - Create DMM pool * @obj: target dmm object - * @pool_id pool id to assign to the pool - * @size Size of the pool - * @sa Starting Address of the Virtual pool + * @args pool information **/ -int omap_create_dmm_pool(struct iodmm_struct *obj, int pool_id, int size, - int sa) +int omap_create_dmm_pool(struct iodmm_struct *obj, const void __user *args) { struct iovmm_pool *pool; struct iovmm_device *iovmm = obj->iovmm; + struct iovmm_pool_info pool_info; + + if (copy_from_user(&pool_info, args, sizeof(struct iovmm_pool_info))) + return -EFAULT; - pool = kzalloc(sizeof(struct iovmm_pool), GFP_ATOMIC); + pool = kzalloc(sizeof(struct iovmm_pool), GFP_KERNEL); if (!pool) - goto err_out; + return -EFAULT; + + pool->pool_id = pool_info.pool_id; + pool->da_begin = pool_info.da_begin; + pool->da_end = pool_info.da_begin + pool_info.size; - pool->pool_id = pool_id; - pool->da_begin = sa; - pool->da_end = sa + size; pool->genpool = gen_pool_create(12, -1); - gen_pool_add(pool->genpool, pool->da_begin, size, -1); + gen_pool_add(pool->genpool, pool->da_begin, pool_info.size, -1); + INIT_LIST_HEAD(&pool->list); list_add_tail(&pool->list, &iovmm->mmap_pool); - return 0; -err_out: - return -ENOMEM; + return 0; } /** - * omap_delete_dmm_pool - Delete DMM pool + * omap_delete_dmm_pool - Delete DMM pools * @obj: target dmm object - * @pool_id pool id to delete **/ -int omap_delete_dmm_pool(struct iodmm_struct *obj, int pool_id) +int omap_delete_dmm_pools(struct iodmm_struct *obj) { struct iovmm_pool *pool; struct iovmm_device *iovmm_obj = obj->iovmm; @@ -977,14 +1046,88 @@ int omap_delete_dmm_pool(struct iodmm_struct *obj, int pool_id) list_for_each_safe(_pool, _next_pool, &iovmm_obj->mmap_pool) { pool = list_entry(_pool, struct iovmm_pool, list); - if (pool->pool_id == pool_id) { - gen_pool_destroy(pool->genpool); - list_del(&pool->list); - kfree(pool); - return 0; + gen_pool_destroy(pool->genpool); + list_del(&pool->list); + kfree(pool); + } + + return 0; +} + +/** + * register_mmufault - Register for MMU fault notification + * @obj: target dmm object + * @args: Eventfd information + * + * Registering to MMU fault event notification + **/ +int register_mmufault(struct iodmm_struct *obj, const void __user *args) +{ + int fd; + struct iommu_event_ntfy *fd_reg; + + if (copy_from_user(&fd, args, sizeof(int))) + return -EFAULT; + + fd_reg = kzalloc(sizeof(struct iommu_event_ntfy), GFP_KERNEL); + fd_reg->fd = fd; + fd_reg->evt_ctx = eventfd_ctx_fdget(fd); + INIT_LIST_HEAD(&fd_reg->list); + spin_lock_irq(&obj->iovmm->iommu->event_lock); + list_add_tail(&fd_reg->list, &obj->iovmm->iommu->event_list); + spin_unlock_irq(&obj->iovmm->iommu->event_lock); + + return 0; +} + +/** + * unregister_mmufault - Unregister for MMU fault notification + * @obj: target dmm object + * @args: Eventfd information + * + * Unregister to MMU fault event notification + **/ +int unregister_mmufault(struct iodmm_struct *obj, const void __user *args) +{ + int fd; + struct iommu_event_ntfy *fd_reg, *temp_reg; + + if (copy_from_user(&fd, (void __user *)args, sizeof(int))) + return -EFAULT; + + /* Free DMM mapped memory resources */ + spin_lock_irq(&obj->iovmm->iommu->event_lock); + list_for_each_entry_safe(fd_reg, temp_reg, + &obj->iovmm->iommu->event_list, list) { + if (fd_reg->fd == fd) { + list_del(&fd_reg->list); + kfree(fd_reg); } } - return -ENODEV; + spin_unlock_irq(&obj->iovmm->iommu->event_lock); + + return 0; +} + +/** + * program_tlb_entry - Program the IOMMU TLB entry + * @obj: target dmm object + * @args: TLB entry information + * + * This function loads the TLB entry that the user specifies. + * This function should be used only during remote Processor + * boot time. + **/ +int program_tlb_entry(struct iodmm_struct *obj, const void __user *args) +{ + struct iotlb_entry e; + int ret; + + if (copy_from_user(&e, args, sizeof(struct iotlb_entry))) + return -EFAULT; + + ret = load_iotlb_entry(obj->iovmm->iommu, &e); + return ret; } MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 2851468c3c05813aa09de157c16a539542d5a64b Mon Sep 17 00:00:00 2001 From: Hari Kanigeri Date: Mon, 18 Oct 2010 21:59:38 -0500 Subject: omap:iodmm-send notification on close only when resources not free Instead of sending the notification on every IOMMU close, send it only when there are still mapped resources to avoid the remote processor accessing these resources. After compelting the notification, the iommu release function can safely remove the mapped entries. Signed-of-by: Hari Kanigeri --- arch/arm/plat-omap/dmm_user.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/arch/arm/plat-omap/dmm_user.c b/arch/arm/plat-omap/dmm_user.c index 3cd9a025c868..6137ebf22354 100644 --- a/arch/arm/plat-omap/dmm_user.c +++ b/arch/arm/plat-omap/dmm_user.c @@ -132,7 +132,15 @@ static int omap_dmm_release(struct inode *inode, struct file *filp) status = mutex_lock_interruptible(&obj->iovmm->dmm_map_lock); if (status == 0) { - iommu_notify_event(obj->iovmm->iommu, IOMMU_CLOSE, NULL); + /* + * Report to remote Processor of the cleanup of these + * resources before cleaning in order to avoid MMU fault + * type of behavior + */ + if (!list_empty(&obj->map_list)) { + iommu_notify_event(obj->iovmm->iommu, IOMMU_CLOSE, + NULL); + } mutex_unlock(&obj->iovmm->dmm_map_lock); } else { pr_err("%s mutex_lock_interruptible returned 0x%x\n", -- cgit v1.2.3 From 2cd42b8b4a083d515d4562cb663415f41227aa00 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Mon, 11 Oct 2010 14:32:25 -0500 Subject: omap:remoteproc: enable gpt3&4 for ipu hibernation gptimer 3/4 are set to be use at the hibernation timers for IPU, both clocks are enabled and configured in rproc_start and disable in rproc_stop. When mpu is hibernating IPU the iommu and mbox context will be saved and then rproc_sleep will be called to disable each core and also the associated gptimer needed to allowed retention. Whenever a message is sent to IPU the ipu_pm_restore will check and wake up ipu, if needed, restoring the iommu and mbox context and calling rproc_wake that will enable ipu and the associated gptimer again. When disable, the hibernating process will only happen if the system suspend/resume path is sent, IPU self hibernation wont be executed. Also gpt3/4 won't be requested at all. Signed-off-by: Miguel Vadillo Signed-off-by: Juan Gutierrez Signed-off-by: Paul Hunt --- arch/arm/mach-omap2/remoteproc44xx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arch/arm/mach-omap2/remoteproc44xx.c b/arch/arm/mach-omap2/remoteproc44xx.c index 4189456d018d..d25becba39f5 100644 --- a/arch/arm/mach-omap2/remoteproc44xx.c +++ b/arch/arm/mach-omap2/remoteproc44xx.c @@ -170,13 +170,22 @@ static struct omap_rproc_platform_data omap4_rproc_data[] = { .name = "ducati-proc0", .ops = &omap4_ducati0_ops, .oh_name = "ipu_c0", +#ifdef CONFIG_SYSLINK_DUCATI_PM + .timer_id = 3, +#else .timer_id = -1, +#endif }, { .name = "ducati-proc1", .ops = &omap4_ducati1_ops, .oh_name = "ipu_c1", +#ifdef CONFIG_SYSLINK_DUCATI_PM + .timer_id = 4, +#else .timer_id = -1, +#endif + }, }; -- cgit v1.2.3 From b70ec89757eebf4c6b2f146dd86c8a58df3d68a3 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Mon, 11 Oct 2010 14:50:08 -0500 Subject: syslink:ipu_pm: allow IPU hibernation -IPU self hibernation- After 5 seconds of inactivity ipu will send a notification to MPU indicating it can be turned off, mpu can proceed to turn off IPU just if there is no message (notify_send_event) going to IPU, in that case hibernation will be aborted. IPU is prepared to abort hibernation when a message arrives to its mbox. When mpu is hibernating IPU the iommu and mbox context will be saved and then rproc_sleep will be called to disable each core and also the associated gptimer, that is needed to allow retention. Whenever a message is sent to IPU the ipu_pm_restore will check and wake up IPU, if needed, restoring the iommu and mbox context and calling rproc_wake that will enable IPU and the associated gptimer again. -MPU hibernating IPU- Whenever the system suspend is sent to ipu_drv the suspend_drv will send a SUSPEND notification to IPU, it will send back an ack to MPU and then start the hibernation proccess by it self following the same scheme mentioned above but without waiting the 5 seconds, once ipu_drv receives the suspend ack it will call ipu_pm_save_ctx() in order to wait for IPU to be hibernated and in really idle then turn IPU off. This will work even if self hibernation in IPU is disable. Signed-off-by: Miguel Vadillo Signed-off-by: Juan Gutierrez Signed-off-by: Paul Hunt --- arch/arm/mach-omap2/ipu_dev.c | 9 +- arch/arm/mach-omap2/ipu_drv.c | 61 +++++++++++ arch/arm/plat-omap/include/plat/ipu_dev.h | 2 + drivers/dsp/syslink/ipu_pm/ipu_pm.c | 176 +++++++++++++++--------------- drivers/dsp/syslink/ipu_pm/ipu_pm.h | 18 ++- 5 files changed, 171 insertions(+), 95 deletions(-) diff --git a/arch/arm/mach-omap2/ipu_dev.c b/arch/arm/mach-omap2/ipu_dev.c index e10f25f36afb..8d3386f1f86c 100644 --- a/arch/arm/mach-omap2/ipu_dev.c +++ b/arch/arm/mach-omap2/ipu_dev.c @@ -201,8 +201,6 @@ inline int ipu_pm_module_set_bandwidth(unsigned rsrc, } EXPORT_SYMBOL(ipu_pm_module_set_bandwidth); -static struct omap_device *od_iva; - /* FIXME: not in use now * static struct omap_ipupm_mod_ops omap_ipu_ops = { * .start = NULL, @@ -330,6 +328,7 @@ static int __init omap_ipussdev_init(void) { int status = -ENODEV; int i; + int first = 1; struct omap_hwmod *oh; struct omap_device *od; char *oh_name; @@ -368,7 +367,11 @@ static int __init omap_ipussdev_init(void) WARN(status, "Could not build omap_device for %s %s\n", pdev_name, oh_name); if (!status) { - od_iva = od; + /* Save the id of the first registered dev */ + if (first) { + ipu_pm_first_dev = od->pdev.id; + first = 0; + } omap_ipupm_data[i].pdev = &od->pdev; omap_ipupm_data[i].dev = &od->pdev.dev; } diff --git a/arch/arm/mach-omap2/ipu_drv.c b/arch/arm/mach-omap2/ipu_drv.c index 85796c0a140c..88a78ba7be29 100644 --- a/arch/arm/mach-omap2/ipu_drv.c +++ b/arch/arm/mach-omap2/ipu_drv.c @@ -47,6 +47,7 @@ static struct class *omap_ipu_pm_class; static dev_t omap_ipu_pm_dev; +int ipu_pm_first_dev; static struct proc_dir_entry *ipu_pm_proc_entry; /* we could iterate over something much more @@ -189,12 +190,72 @@ static int __devinit ipu_pm_probe(struct platform_device *pdev) return 0; } +static int ipu_pm_drv_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int retval = 0; + + if (pdev->id == ipu_pm_first_dev) { + pr_debug("%s.%d ASKED TO SUSPEND", pdev->name, pdev->id); + /* save any local context, + * BIOS timers could be saved locally or on Ducati + */ + + /* call our notification function */ + retval = ipu_pm_notifications(PM_SUSPEND, NULL); + + /* FIXME: Currently sending SUSPEND is enough to send + * Ducati to hibernate, save ctx can be called at this + * point to save ctx and reset remote procs + * Currently the save ctx process can be called using + * which ever proc_id, maybe this will change when + * Tesla support is added. + */ + /* sysm3 is handling hibernation of ducati currently */ + ipu_pm_save_ctx(SYS_M3); + + /* return result, should be zero if all Ducati clients + * returned zero else fail code + */ + } + + return retval; +} + +static int ipu_pm_drv_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + int retval = 0; + + if (pdev->id == ipu_pm_first_dev) { + pr_debug("%s.%d ASKED TO RESUME", pdev->name, pdev->id); + /* restore any local context, + * BIOS timers could be restored locally or on Ducati + */ + + /* call our notification function */ + retval = ipu_pm_notifications(PM_RESUME, NULL); + + /* return result, should be zero if all Ducati clients + * returned zero else fail code + */ + } + + return retval; +} + +static const struct dev_pm_ops ipu_pm_ops = { + .suspend = ipu_pm_drv_suspend, + .resume = ipu_pm_drv_resume, +}; + static struct platform_driver ipu_pm_driver = { .probe = ipu_pm_probe, /*.remove = ipu_pm_remove, */ .driver = { .name = IPU_DRIVER_NAME, .owner = THIS_MODULE, + .pm = &ipu_pm_ops, }, }; diff --git a/arch/arm/plat-omap/include/plat/ipu_dev.h b/arch/arm/plat-omap/include/plat/ipu_dev.h index 4b24f3bc14f2..c7b7959c38b2 100644 --- a/arch/arm/plat-omap/include/plat/ipu_dev.h +++ b/arch/arm/plat-omap/include/plat/ipu_dev.h @@ -90,6 +90,8 @@ struct ipu_pm_dev { struct cdev cdev; }; +extern int ipu_pm_first_dev; + extern int ipu_pm_module_start(unsigned rsrc); extern int ipu_pm_module_stop(unsigned rsrc); extern int ipu_pm_module_set_rate(unsigned rsrc, diff --git a/drivers/dsp/syslink/ipu_pm/ipu_pm.c b/drivers/dsp/syslink/ipu_pm/ipu_pm.c index 9e4cb0f84af4..e9868c8016ea 100644 --- a/drivers/dsp/syslink/ipu_pm/ipu_pm.c +++ b/drivers/dsp/syslink/ipu_pm/ipu_pm.c @@ -65,10 +65,6 @@ * Macros and types * ============================================================================ */ -#define A9 3 -#define SYS_M3 2 -#define APP_M3 1 -#define TESLA 0 #define HW_AUTO 3 #define CM_DUCATI_M3_CLKSTCTRL 0x4A008900 #define SL2_RESOURCE 10 @@ -83,6 +79,8 @@ #define SYSM3_IDLE_FLAG_PHY_ADDR 0x9E0502D8 #define APPM3_IDLE_FLAG_PHY_ADDR 0x9E0502DC +#define _is_valid_event(e) ((PM_FIRST_EVENT <= e && e <= PM_LAST_EVENT) ? 1 : 0) + #define NUM_IDLE_CORES ((__raw_readl(appm3Idle) << 1) + \ (__raw_readl(sysm3Idle))) @@ -253,6 +251,7 @@ static struct omap_rproc *app_rproc; static struct omap_mbox *ducati_mbox; static struct iommu *ducati_iommu; static bool first_time = 1; +static bool mpu_hib_ipu; /* static struct omap_dm_timer *pm_gpt; */ /* Ducati Interrupt Capable Gptimers */ @@ -285,6 +284,7 @@ static struct ipu_pm_params pm_params = { .pm_iva_hd_counter = 0, .pm_ivaseq0_counter = 0, .pm_ivaseq1_counter = 0, + .pm_sl2if_counter = 0, .pm_l3_bus_counter = 0, .pm_mpu_counter = 0, .pm_sdmachan_counter = 0, @@ -608,22 +608,20 @@ void ipu_pm_callback(u16 proc_id, u16 line_id, u32 event_id, EXPORT_SYMBOL(ipu_pm_callback); -/* - Function for PM notifications Callback - * +/* Function for PM notifications Callback + * This functions receives an event coming from + * remote proc as an ack. + * Post semaphore based in eventType (payload) + * If PM_HIBERNATE is received the save_ctx is triggered + * in order to put remote proc in reset. */ void ipu_pm_notify_callback(u16 proc_id, u16 line_id, u32 event_id, uint *arg, u32 payload) { - /** - * Post semaphore based in eventType (payload); - * IPU has alreay finished the process for the - * notification - */ - /* Get the payload */ struct ipu_pm_object *handle; union message_slicer pm_msg; struct ipu_pm_params *params; + enum pm_event_type event; int retval; /* get the handle to proper ipu pm object */ @@ -635,32 +633,27 @@ void ipu_pm_notify_callback(u16 proc_id, u16 line_id, u32 event_id, return; pm_msg.whole = payload; - if (pm_msg.fields.msg_type == PM_NOTIFY_HIBERNATE) { - /* Remote proc requested hibernate */ - /* Remote Proc is ready to hibernate */ + /* get the event type sent by remote proc */ + event = pm_msg.fields.msg_subtype; + if (!_is_valid_event(event)) + goto error; + if (event == PM_HIBERNATE) { + /* Remote Proc is ready to hibernate + * PM_HIBERNATE is a one way notification + * Remote proc to Host proc + */ + pr_debug("Remote Proc is ready to hibernate\n"); retval = ipu_pm_save_ctx(proc_id); if (retval) - pr_info("Unable to stop proc %d\n", proc_id); + pr_err("Unable to stop proc %d\n", proc_id); } else { - switch (pm_msg.fields.msg_subtype) { - case PM_SUSPEND: - handle->pm_event[PM_SUSPEND].pm_msg = payload; - up(&handle->pm_event[PM_SUSPEND].sem_handle); - break; - case PM_RESUME: - handle->pm_event[PM_RESUME].pm_msg = payload; - up(&handle->pm_event[PM_RESUME].sem_handle); - break; - case PM_HIBERNATE: - handle->pm_event[PM_HIBERNATE].pm_msg = payload; - up(&handle->pm_event[PM_HIBERNATE].sem_handle); - break; - case PM_PID_DEATH: - handle->pm_event[PM_PID_DEATH].pm_msg = payload; - up(&handle->pm_event[PM_PID_DEATH].sem_handle); - break; - } + pr_debug("Remote Proc received %d event\n", event); + handle->pm_event[event].pm_msg = payload; + up(&handle->pm_event[event].sem_handle); } + return; +error: + pr_err("Unknow event received from remote proc: %d\n", event); } EXPORT_SYMBOL(ipu_pm_notify_callback); @@ -749,34 +742,7 @@ int ipu_pm_notifications(enum pm_event_type event_type, void *data) goto error; break; case PM_HIBERNATE: - pm_msg.fields.msg_type = PM_NOTIFICATIONS; - pm_msg.fields.msg_subtype = PM_HIBERNATE; - pm_msg.fields.parm = PM_SUCCESS; - /* put general purpose message in share memory */ - handle->rcb_table->gp_msg = (unsigned)data; - /* send the request to IPU*/ - retval = notify_send_event( - params->remote_proc_id, - params->line_id, - params->pm_notification_event | \ - (NOTIFY_SYSTEMKEY << 16), - (unsigned int)pm_msg.whole, - true); - if (retval < 0) - goto error_send; - /* wait until event from IPU (ipu_pm_notify_callback)*/ - retval = down_timeout - (&handle->pm_event[PM_HIBERNATE] - .sem_handle, - msecs_to_jiffies(params->timeout)); - pm_msg.whole = handle->pm_event[PM_HIBERNATE].pm_msg; - if (WARN_ON((retval < 0) || - (pm_msg.fields.parm != PM_SUCCESS))) - goto error; - else { - /*Remote Proc is ready to hibernate*/ - pm_ack = ipu_pm_save_ctx(proc_id); - } + pr_err("PM_HIBERNATE event currently not supported\n"); break; case PM_PID_DEATH: /* Just send the message to appm3 since is the one @@ -814,9 +780,9 @@ int ipu_pm_notifications(enum pm_event_type event_type, void *data) return pm_ack; error_send: - pr_err("Error notify_send event\n"); + pr_err("Error notify_send event %d to proc %d\n", event_type, proc_id); error: - pr_err("Error sending Notification events\n"); + pr_err("Error sending Notification event %d\n", event_type); return -EBUSY; } EXPORT_SYMBOL(ipu_pm_notifications); @@ -1466,13 +1432,14 @@ static inline int ipu_pm_get_ivaseq1(int proc_id, u32 rcb_num) retval = ipu_pm_module_start(rcb_p->sub_type); if (retval) return PM_UNSUPPORTED; + params->pm_ivaseq1_counter++; /*Requesting SL2*/ + /* FIXME: sl2if should be moved to a independent function */ retval = ipu_pm_module_start(SL2_RESOURCE); if (retval) return PM_UNSUPPORTED; - - params->pm_ivaseq1_counter++; + params->pm_sl2if_counter++; return PM_SUCCESS; } @@ -2038,9 +2005,13 @@ static inline int ipu_pm_rel_iva_hd(int proc_id, u32 rcb_num) goto error; /* Releasing SL2 */ - retval = ipu_pm_module_stop(SL2_RESOURCE); - if (retval) - return PM_UNSUPPORTED; + /* FIXME: sl2if should be moved to a independent function */ + if (params->pm_sl2if_counter) { + retval = ipu_pm_module_stop(SL2_RESOURCE); + if (retval) + return PM_UNSUPPORTED; + params->pm_sl2if_counter--; + } retval = ipu_pm_module_stop(rcb_p->sub_type); if (retval) @@ -3246,15 +3217,19 @@ int ipu_pm_save_ctx(int proc_id) /* get the handle to proper ipu pm object */ handle = ipu_pm_get_handle(proc_id); - if (WARN_ON(unlikely(handle == NULL))) - return -EINVAL; + if (unlikely(handle == NULL)) + return 0; - /* Check if the M3 was loaded */ + /* get M3's load flag */ sys_loaded = (ipu_pm_get_state(proc_id) & SYS_PROC_LOADED) >> PROC_LD_SHIFT; app_loaded = (ipu_pm_get_state(proc_id) & APP_PROC_LOADED) >> PROC_LD_SHIFT; + /* If already down don't kill it twice */ + if (ipu_pm_get_state(proc_id) & SYS_PROC_DOWN) + goto exit; + /* Because of the current scheme, we need to check * if APPM3 is enable and we need to shut it down too * Sysm3 is the only want sending the hibernate message @@ -3281,12 +3256,20 @@ int ipu_pm_save_ctx(int proc_id) /* Check for APPM3, if loaded reset first */ if (app_loaded) { + pr_info("Sleep APPM3\n"); retval = rproc_sleep(app_rproc); + cm_write_mod_reg(HW_AUTO, + OMAP4430_CM2_CORE_MOD, + OMAP4_CM_DUCATI_CLKSTCTRL_OFFSET); if (retval) goto error; handle->rcb_table->state_flag |= APP_PROC_DOWN; } + pr_info("Sleep SYSM3\n"); retval = rproc_sleep(sys_rproc); + cm_write_mod_reg(HW_AUTO, + OMAP4430_CM2_CORE_MOD, + OMAP4_CM_DUCATI_CLKSTCTRL_OFFSET); if (retval) goto error; handle->rcb_table->state_flag |= SYS_PROC_DOWN; @@ -3299,7 +3282,7 @@ exit: return 0; error: mutex_unlock(ipu_pm_state.gate_handle); - pr_info("Aborting hibernation process\n"); + pr_debug("Aborting hibernation process\n"); return -EINVAL; } EXPORT_SYMBOL(ipu_pm_save_ctx); @@ -3312,10 +3295,8 @@ EXPORT_SYMBOL(ipu_pm_save_ctx); int ipu_pm_restore_ctx(int proc_id) { int retval = 0; -#ifdef CONFIG_SYSLINK_DUCATI_PM int sys_loaded; int app_loaded; -#endif struct ipu_pm_object *handle; /*If feature not supported by proc, return*/ @@ -3328,23 +3309,26 @@ int ipu_pm_restore_ctx(int proc_id) if (WARN_ON(unlikely(handle == NULL))) return -EINVAL; - /* By default Ducati Hibernation is disable - * enabling just the first time and if - * CONFIG_SYSLINK_DUCATI_PM is defined + /* FIXME: This needs mor analysis. + * Since the sync of IPU and MPU is done this is a safe place + * to switch to HW_AUTO to allow transition of clocks to gated + * supervised by HW. */ if (first_time) { - handle->rcb_table->state_flag |= ENABLE_IPU_HIB; + /* Enable/disable ipu hibernation*/ +#ifdef CONFIG_SYSLINK_DUCATI_PM + handle->rcb_table->pm_flags.hibernateAllowed = 1; +#else handle->rcb_table->pm_flags.hibernateAllowed = 0; - handle->rcb_table->pm_flags.idleAllowed = 0; +#endif + pr_info("hibernateAllowed=%d\n", + handle->rcb_table->pm_flags.hibernateAllowed); first_time = 0; - __raw_writel(HW_AUTO, cm_ducati_clkstctrl); + cm_write_mod_reg(HW_AUTO, + OMAP4430_CM2_CORE_MOD, + OMAP4_CM_DUCATI_CLKSTCTRL_OFFSET); } - /* FIXME:This will be avoided with a change in Ducati. */ - handle->rcb_table->pm_flags.idleAllowed = 1; - -#ifdef CONFIG_SYSLINK_DUCATI_PM - /* Check if the M3 was loaded */ sys_loaded = (ipu_pm_get_state(proc_id) & SYS_PROC_LOADED) >> PROC_LD_SHIFT; @@ -3363,12 +3347,20 @@ int ipu_pm_restore_ctx(int proc_id) omap_mbox_restore_ctx(ducati_mbox); iommu_restore_ctx(ducati_iommu); + pr_info("Wakeup SYSM3\n"); retval = rproc_wakeup(sys_rproc); + cm_write_mod_reg(HW_AUTO, + OMAP4430_CM2_CORE_MOD, + OMAP4_CM_DUCATI_CLKSTCTRL_OFFSET); if (retval) goto error; handle->rcb_table->state_flag &= ~SYS_PROC_DOWN; if (ipu_pm_get_state(proc_id) & APP_PROC_LOADED) { + pr_info("Wakeup APPM3\n"); retval = rproc_wakeup(app_rproc); + cm_write_mod_reg(HW_AUTO, + OMAP4430_CM2_CORE_MOD, + OMAP4_CM_DUCATI_CLKSTCTRL_OFFSET); if (retval) goto error; handle->rcb_table->state_flag &= ~APP_PROC_DOWN; @@ -3377,14 +3369,11 @@ int ipu_pm_restore_ctx(int proc_id) goto error; exit: mutex_unlock(ipu_pm_state.gate_handle); -#endif return retval; -#ifdef CONFIG_SYSLINK_DUCATI_PM error: mutex_unlock(ipu_pm_state.gate_handle); - pr_info("Aborting restoring process\n"); + pr_debug("Aborting restoring process\n"); return -EINVAL; -#endif } EXPORT_SYMBOL(ipu_pm_restore_ctx); @@ -3495,6 +3484,8 @@ int ipu_pm_setup(struct ipu_pm_config *cfg) /*pm_gpt = omap_dm_timer_request_specific(GP_TIMER_3); if (pm_gpt == NULL) retval = -EINVAL;*/ + /* Reset hibernation from MPU flag */ + mpu_hib_ipu = 0; return retval; exit: @@ -3650,6 +3641,9 @@ int ipu_pm_detach(u16 remote_proc_id) goto exit; } + /* Reset the state_flag */ + handle->rcb_table->state_flag = 0; + /* Deleting the handle based on remote_proc_id */ ipu_pm_delete(handle); diff --git a/drivers/dsp/syslink/ipu_pm/ipu_pm.h b/drivers/dsp/syslink/ipu_pm/ipu_pm.h index b7e9ff46f900..c3c51d8a16a9 100644 --- a/drivers/dsp/syslink/ipu_pm/ipu_pm.h +++ b/drivers/dsp/syslink/ipu_pm/ipu_pm.h @@ -95,6 +95,12 @@ /* Suspend/resume/other... */ #define NUMBER_PM_EVENTS 4 +/* Processors id's */ +#define A9 3 +#define SYS_M3 2 +#define APP_M3 1 +#define TESLA 0 + #define PM_CSTR_PERF_MASK 0x00000001 #define PM_CSTR_LAT_MASK 0x00000002 #define PM_CSTR_BW_MASK 0x00000004 @@ -287,12 +293,21 @@ enum res_type{ AUX_CLK, }; -enum pm_event_type{PM_SUSPEND, +/* Events can start at any number but + * should be always consecutive + */ +#define PM_FIRST_EVENT 0 + +enum pm_event_type{PM_SUSPEND = PM_FIRST_EVENT, PM_RESUME, PM_PID_DEATH, PM_HIBERNATE }; +#define PM_LAST_EVENT ((sizeof(enum pm_event_type) / sizeof(void)) \ + + PM_FIRST_EVENT\ + - 1) + struct rcb_message { unsigned rcb_flag:1; unsigned rcb_num:6; @@ -366,6 +381,7 @@ struct ipu_pm_params { int pm_iva_hd_counter; int pm_ivaseq0_counter; int pm_ivaseq1_counter; + int pm_sl2if_counter; int pm_l3_bus_counter; int pm_mpu_counter; int pm_sdmachan_counter; -- cgit v1.2.3 From b4bba02195eb63861da3387d86c51a01e22f49a4 Mon Sep 17 00:00:00 2001 From: Miguel Vadillo Date: Fri, 22 Oct 2010 15:04:52 -0500 Subject: omap:iommu: restore just the prsvd tlb entries Previously, when restoring the iommu context, all the TLB entries marked valid were being reloaded. Reading dynamically loaded TLBs back from the MMU registers during a context save operation returned addresses that were not aligned to the boundaries of the page sizes indicated in the respective fields. Attempting to restore these unaligned entries in the MMU resulted in warnings from the restore routines. Since only the locked TLB entries need to be loaded before restarting the processor, we are now restoring only those. (All the locked entries were marked as protected/preserved when they were initially loaded.) We are still investigating the root cause of why the alignment appears wrong in the TLB entries loaded as a result of a TLB miss by the table walking logic. Signed-off-by: Miguel Vadillo --- arch/arm/plat-omap/iommu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/arch/arm/plat-omap/iommu.c b/arch/arm/plat-omap/iommu.c index d728af8aec6b..abc177d75072 100644 --- a/arch/arm/plat-omap/iommu.c +++ b/arch/arm/plat-omap/iommu.c @@ -400,10 +400,10 @@ u32 iommu_save_tlb_entries(struct iommu *obj) for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr_tmp) { iotlb_cr_to_e(&cr_tmp, e); + dev_dbg(obj->dev, "%s: %08x %08x %d %d %d", __func__, e->da, + e->pa, e->pgsz, e->prsvd, + e->valid); e++; - - dev_dbg(obj->dev, "%s: [%02x] %08x %08x\n", __func__, - i, cr_tmp.cam, cr_tmp.ram); } return 0; @@ -429,17 +429,17 @@ u32 iommu_restore_tlb_entries(struct iommu *obj) goto error; for (i = 0; i < obj->nr_tlb_entries; i++) { - if (!e->valid) { + if (!e->prsvd) { e++; continue; } + dev_dbg(obj->dev, "%s: %08x %08x %d %d %d", __func__, e->da, + e->pa, e->pgsz, e->prsvd, + e->valid); status = load_iotlb_entry(obj, e); if (status) goto error; e++; - - dev_dbg(obj->dev, "%s: [%02x] %08x\n", __func__, - i, e->pa); } return 0; -- cgit v1.2.3