/* * notify.c * * Syslink driver support for OMAP Processors. * * Copyright (C) 2008-2009 Texas Instruments, Inc. * * This package 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 PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #include #include #include #include #include #include #include #include #include #include "../ipu_pm/ipu_pm.h" #include #include #include #include #include #include #include struct notify_event_listener { struct list_head element; struct notify_event_callback callback; }; /* Function registered with notify_exec when multiple registrations are present * for the events. */ static void _notify_exec_many(u16 proc_id, u16 line_id, u32 event_id, uint *arg, u32 payload); struct notify_module_object notify_state = { .def_cfg.num_events = 32u, .def_cfg.send_event_poll_count = -1u, .def_cfg.num_lines = 1u, .def_cfg.reserved_events = 5u, .gate_handle = NULL, .local_notify_handle = NULL }; /* Get the default configuration for the Notify module. */ void notify_get_config(struct notify_config *cfg) { s32 retval = 0; if (WARN_ON(unlikely(cfg == NULL))) { retval = -EINVAL; goto exit; } if (atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true) memcpy(cfg, ¬ify_state.def_cfg, sizeof(struct notify_config)); else memcpy(cfg, ¬ify_state.cfg, sizeof(struct notify_config)); exit: if (retval < 0) pr_err("notify_get_config failed! status = 0x%x", retval); return; } EXPORT_SYMBOL(notify_get_config); /* This function sets up the Notify module. This function must be called * before any other instance-level APIs can be invoked. */ int notify_setup(struct notify_config *cfg) { int status = NOTIFY_S_SUCCESS; struct notify_config tmp_cfg; atomic_cmpmask_and_set(¬ify_state.ref_count, NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(0)); if (atomic_inc_return(¬ify_state.ref_count) != NOTIFY_MAKE_MAGICSTAMP(1u)) { return NOTIFY_S_ALREADYSETUP; } if (cfg == NULL) { notify_get_config(&tmp_cfg); cfg = &tmp_cfg; } if (cfg->num_events > NOTIFY_MAXEVENTS) { status = NOTIFY_E_INVALIDARG; goto exit; } if (cfg->num_lines > NOTIFY_MAX_INTLINES) { status = NOTIFY_E_INVALIDARG; goto exit; } if (cfg->reserved_events > cfg->num_events) { status = NOTIFY_E_INVALIDARG; goto exit; } notify_state.gate_handle = kmalloc(sizeof(struct mutex), GFP_ATOMIC); if (notify_state.gate_handle == NULL) { status = NOTIFY_E_FAIL; goto exit; } /*User has not provided any gate handle, so create a default handle.*/ mutex_init(notify_state.gate_handle); memcpy(¬ify_state.cfg, cfg, sizeof(struct notify_config)); notify_state.local_enable_mask = -1u; notify_state.start_complete = false; memset(¬ify_state.drivers, 0, (sizeof(struct notify_driver_object) * NOTIFY_MAX_DRIVERS * NOTIFY_MAX_INTLINES)); /* tbd: Should return Notify_Handle */ notify_state.local_notify_handle = notify_create(NULL, multiproc_self(), 0, NULL); if (notify_state.local_notify_handle == NULL) { status = NOTIFY_E_FAIL; goto local_notify_fail; } return 0; local_notify_fail: kfree(notify_state.gate_handle); exit: atomic_set(¬ify_state.ref_count, NOTIFY_MAKE_MAGICSTAMP(0)); pr_err("notify_setup failed! status = 0x%x", status); return status; } EXPORT_SYMBOL(notify_setup); /* Once this function is called, other Notify module APIs, * except for the Notify_getConfig API cannot be called anymore. */ int notify_destroy(void) { int i; int j; int status = NOTIFY_S_SUCCESS; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (!(atomic_dec_return(¬ify_state.ref_count) == NOTIFY_MAKE_MAGICSTAMP(0))) return NOTIFY_S_ALREADYSETUP; /* Temporarily increment refCount here. */ atomic_set(¬ify_state.ref_count, NOTIFY_MAKE_MAGICSTAMP(1)); if (notify_state.local_notify_handle != NULL) status = notify_delete(¬ify_state.local_notify_handle); atomic_set(¬ify_state.ref_count, NOTIFY_MAKE_MAGICSTAMP(0)); /* Check if any Notify driver instances have * not been deleted so far. If not, assert. */ for (i = 0; i < NOTIFY_MAX_DRIVERS; i++) for (j = 0; j < NOTIFY_MAX_INTLINES; j++) WARN_ON(notify_state.drivers[i][j].is_init != false); kfree(notify_state.gate_handle); exit: if (status < 0) pr_err("notify_destroy failed! status = 0x%x", status); return status; } EXPORT_SYMBOL(notify_destroy); /* Function to create an instance of Notify driver */ struct notify_object *notify_create(void *driver_handle, u16 remote_proc_id, u16 line_id, const struct notify_params *params) { int status = NOTIFY_S_SUCCESS; struct notify_object *obj = NULL; uint i; /* driver_handle can be NULL for local create */ /* params can be NULL */ if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(remote_proc_id >= \ multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } /* Allocate memory for the Notify object. */ obj = kzalloc(sizeof(struct notify_object), GFP_KERNEL); if (obj == NULL) { status = NOTIFY_E_MEMORY; goto exit; } 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]); /* Used solely for remote driver * (NULL if remote_proc_id == self) */ obj->driver_handle = driver_handle; /* Send this handle to the NotifyDriver */ status = notify_set_driver_handle(remote_proc_id, line_id, obj); if (status < 0) goto notify_handle_fail; /* For local notify */ if (driver_handle == NULL) /* Set driver status to indicate that it is done. */ notify_state.drivers[multiproc_self()][line_id].is_init = NOTIFY_DRIVERINITSTATUS_DONE; return obj; notify_handle_fail: notify_set_driver_handle(remote_proc_id, line_id, NULL); kfree(obj); obj = NULL; exit: if (status < 0) pr_err("notify_create failed! status = 0x%x", status); return obj; } /* Function to delete an instance of Notify driver */ int notify_delete(struct notify_object **handle_ptr) { int status = NOTIFY_S_SUCCESS; struct notify_object *obj; u16 i; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely((handle_ptr == NULL) || (*handle_ptr == NULL)))) { status = NOTIFY_E_INVALIDARG; goto exit; } obj = (struct notify_object *)(*handle_ptr); if (obj->remote_proc_id == multiproc_self()) { if (WARN_ON(obj->line_id >= NOTIFY_MAX_INTLINES || multiproc_self() >= MULTIPROC_MAXPROCESSORS)) { status = NOTIFY_E_INVALIDARG; goto exit; } notify_state.drivers[multiproc_self()][obj->line_id].is_init = NOTIFY_DRIVERINITSTATUS_NOTDONE; } notify_set_driver_handle(obj->remote_proc_id, obj->line_id, NULL); for (i = 0; i < notify_state.cfg.num_events; i++) INIT_LIST_HEAD(&obj->event_list[i]); kfree(obj); obj = NULL; *handle_ptr = NULL; exit: if (status < 0) pr_err("notify_delete failed! status = 0x%x", status); return status; } /* This function registers a callback for a specific event with the * Notify module. */ int notify_register_event(u16 proc_id, u16 line_id, u32 event_id, notify_fn_notify_cbck notify_callback_fxn, void *cbck_arg) { int status = NOTIFY_S_SUCCESS; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct list_head *event_list; struct notify_event_listener *listener; bool list_was_empty; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(notify_callback_fxn == NULL))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } listener = kmalloc(sizeof(struct notify_event_listener), GFP_KERNEL); if (listener == NULL) { status = NOTIFY_E_MEMORY; 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(&obj->lock); if (list_was_empty) { /* Registering this event for the first time. Need to * register the callback function. */ status = notify_register_event_single(proc_id, line_id, event_id, _notify_exec_many, (uint *) obj); } exit: if (status < 0) pr_err("notify_register_event failed! status = 0x%x", status); return status; } EXPORT_SYMBOL(notify_register_event); /* This function registers a single callback for a specific event with the * Notify module. */ int notify_register_event_single(u16 proc_id, u16 line_id, u32 event_id, notify_fn_notify_cbck notify_callback_fxn, void *cbck_arg) { int status = NOTIFY_S_SUCCESS; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(notify_callback_fxn == NULL))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } if (obj->callbacks[stripped_event_id].fn_notify_cbck != NULL) { status = NOTIFY_E_ALREADYEXISTS; goto exit; } mutex_lock_killable(&obj->lock); obj->callbacks[stripped_event_id].fn_notify_cbck = notify_callback_fxn; obj->callbacks[stripped_event_id].cbck_arg = cbck_arg; if (proc_id != multiproc_self()) { status = driver_handle->fxn_table.register_event(driver_handle, stripped_event_id); } mutex_unlock(&obj->lock); exit: if (status < 0) { pr_err("notify_register_event_single failed! " "status = 0x%x", status); } return status; } EXPORT_SYMBOL(notify_register_event_single); /* This function un-registers the callback for the specific event with * the Notify module. */ int notify_unregister_event(u16 proc_id, u16 line_id, u32 event_id, notify_fn_notify_cbck notify_callback_fxn, void *cbck_arg) { int status = NOTIFY_S_SUCCESS; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_event_listener *listener; bool found = false; struct notify_driver_object *driver_handle; struct list_head *event_list; struct notify_object *obj; /*int *sys_key;*/ if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(notify_callback_fxn == NULL))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } event_list = &(obj->event_list[stripped_event_id]); if (list_empty(event_list)) { status = NOTIFY_E_NOTFOUND; 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; mutex_unlock(&obj->lock); goto exit; } mutex_unlock(&obj->lock); if (list_empty(event_list)) { status = notify_unregister_event_single(proc_id, line_id, event_id); } kfree(listener); exit: if (status < 0) { pr_err("notify_unregister_event failed! " "status = 0x%x", status); } return status; } EXPORT_SYMBOL(notify_unregister_event); /* This function un-registers a single callback for the specific event with * the Notify module. */ int notify_unregister_event_single(u16 proc_id, u16 line_id, u32 event_id) { int status = NOTIFY_S_SUCCESS; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } if (obj->callbacks[stripped_event_id].fn_notify_cbck == NULL) { status = NOTIFY_E_FAIL; goto exit; } mutex_lock_killable(&obj->lock); obj->callbacks[stripped_event_id].fn_notify_cbck = NULL; obj->callbacks[stripped_event_id].cbck_arg = NULL; if (proc_id != multiproc_self()) { status = driver_handle->fxn_table.unregister_event( driver_handle, stripped_event_id); } mutex_unlock(&obj->lock); exit: if (status < 0) { pr_err("notify_unregister_event_single failed! " "status = 0x%x", status); } return status; } EXPORT_SYMBOL(notify_unregister_event_single); /* This function sends a notification to the specified event. */ int notify_send_event(u16 proc_id, u16 line_id, u32 event_id, u32 payload, bool wait_clear) { int status = NOTIFY_S_SUCCESS; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } /* Maybe the proc is shutdown this functions will check and * restore if needed. * Currently only used to enable Idle and set HW_AUTO as * the ducati_clkstctrl mode*/ status = ipu_pm_restore_ctx(proc_id); if (status) goto exit; if (proc_id != multiproc_self()) { status = driver_handle->fxn_table.send_event(driver_handle, stripped_event_id, payload, wait_clear); } else { /* If nesting == 0 (the driver is enabled) and the event is * enabled, send the event */ if (obj->callbacks[stripped_event_id].fn_notify_cbck == NULL) { /* No callbacks are registered locally for the event. */ status = NOTIFY_E_EVTNOTREGISTERED; } else if (obj->nesting != 0) { /* Driver is disabled */ status = NOTIFY_E_FAIL; } else if (!test_bit(stripped_event_id, (unsigned long *) ¬ify_state.local_enable_mask)) { /* Event is disabled */ status = NOTIFY_E_EVTDISABLED; } else { /* Execute the callback function registered to the * event */ notify_exec(obj, event_id, payload); } } exit: if (status < 0) pr_err("notify_send_event failed! status = 0x%x", status); return status; } EXPORT_SYMBOL(notify_send_event); /* This function disables all events. This is equivalent to global * interrupt disable, however restricted within interrupts handled by * the Notify module. All callbacks registered for all events are * disabled with this API. It is not possible to disable a specific * callback. */ u32 notify_disable(u16 proc_id, u16 line_id) { uint key = 0; int status = NOTIFY_S_SUCCESS; struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } mutex_lock_killable(&obj->lock); obj->nesting++; if (obj->nesting == 1) { /* Disable receiving all events */ if (proc_id != multiproc_self()) driver_handle->fxn_table.disable(driver_handle); } key = obj->nesting; mutex_unlock(&obj->lock); exit: if (status < 0) pr_err("notify_disable failed! status = 0x%x", status); return key; } EXPORT_SYMBOL(notify_disable); /* This function restores the Notify module to the state before the * last notify_disable() was called. This is equivalent to global * interrupt restore, however restricted within interrupts handled by * the Notify module. All callbacks registered for all events as * specified in the flags are enabled with this API. It is not possible * to enable a specific callback. */ void notify_restore(u16 proc_id, u16 line_id, u32 key) { int status = NOTIFY_S_SUCCESS; struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } if (key != obj->nesting) { status = NOTIFY_E_INVALIDSTATE; 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); } mutex_unlock(&obj->lock); exit: if (status < 0) pr_err("notify_restore failed! status = 0x%x", status); return; } EXPORT_SYMBOL(notify_restore); /* This function disables a specific event. All callbacks registered * for the specific event are disabled with this API. It is not * possible to disable a specific callback. */ void notify_disable_event(u16 proc_id, u16 line_id, u32 event_id) { int status = 0; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } if (proc_id != multiproc_self()) { driver_handle->fxn_table.disable_event(driver_handle, stripped_event_id); } else { clear_bit(stripped_event_id, (unsigned long *) ¬ify_state.local_enable_mask); } exit: if (status < 0) pr_err("notify_disable_event failed! status = 0x%x", status); return; } EXPORT_SYMBOL(notify_disable_event); /* This function enables a specific event. All callbacks registered for * this specific event are enabled with this API. It is not possible to * enable a specific callback. */ void notify_enable_event(u16 proc_id, u16 line_id, u32 event_id) { int status = 0; u32 stripped_event_id = (event_id & NOTIFY_EVENT_MASK); struct notify_driver_object *driver_handle; struct notify_object *obj; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely((stripped_event_id >= \ notify_state.cfg.num_events)))) { status = NOTIFY_E_EVTNOTREGISTERED; goto exit; } if (WARN_ON(unlikely(!ISRESERVED(event_id, notify_state.cfg.reserved_events)))) { status = NOTIFY_E_EVTRESERVED; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if (WARN_ON(driver_handle == NULL)) { status = NOTIFY_E_DRIVERNOTREGISTERED; goto exit; } if (WARN_ON(driver_handle->is_init != NOTIFY_DRIVERINITSTATUS_DONE)) { status = NOTIFY_E_FAIL; goto exit; } obj = (struct notify_object *)driver_handle->notify_handle; if (WARN_ON(obj == NULL)) { status = NOTIFY_E_FAIL; goto exit; } if (proc_id != multiproc_self()) { driver_handle->fxn_table.enable_event(driver_handle, stripped_event_id); } else { set_bit(stripped_event_id, (unsigned long *)¬ify_state.local_enable_mask); } exit: if (status < 0) pr_err("notify_enable_event failed! status = 0x%x", status); return; } EXPORT_SYMBOL(notify_enable_event); /* Whether notification via interrupt line has been registered. */ bool notify_is_registered(u16 proc_id, u16 line_id) { int status = NOTIFY_S_SUCCESS; bool is_registered = false; struct notify_driver_object *driver_handle; if (WARN_ON(unlikely(atomic_cmpmask_and_lt(&(notify_state.ref_count), NOTIFY_MAKE_MAGICSTAMP(0), NOTIFY_MAKE_MAGICSTAMP(1)) == true))) { status = NOTIFY_E_INVALIDSTATE; goto exit; } if (WARN_ON(unlikely(proc_id >= multiproc_get_num_processors()))) { status = NOTIFY_E_INVALIDARG; goto exit; } if (WARN_ON(unlikely(line_id >= NOTIFY_MAX_INTLINES))) { status = NOTIFY_E_INVALIDARG; goto exit; } driver_handle = notify_get_driver_handle(proc_id, line_id); if ((driver_handle != NULL) && (driver_handle->notify_handle != NULL)) is_registered = true; exit: if (status < 0) pr_err("notify_is_registered failed! status = 0x%x", status); return is_registered; } EXPORT_SYMBOL(notify_is_registered); /* Creates notify drivers and registers them with Notify */ int notify_attach(u16 proc_id, void *shared_addr) { int status = NOTIFY_S_SUCCESS; /* Use the NotifySetup proxy to setup drivers */ status = notify_setup_proxy_attach(proc_id, shared_addr); notify_state.start_complete = true; return status; } EXPORT_SYMBOL(notify_attach); /* Creates notify drivers and registers them with Notify */ int notify_detach(u16 proc_id) { int status = 0; /* Use the NotifySetup proxy to destroy drivers */ status = notify_setup_proxy_detach(proc_id); notify_state.start_complete = false; return status; } EXPORT_SYMBOL(notify_detach); /* Returns the total amount of shared memory used by the Notify module * and all instances after notify_start has been called. */ uint notify_shared_mem_req(u16 proc_id, void *shared_addr) { uint mem_req = 0x0; if (multiproc_get_num_processors() > 1) /* Determine device-specific shared memory requirements */ mem_req = notify_setup_proxy_shared_mem_req(proc_id,\ shared_addr); else /* Only 1 processor: no shared memory needed */ mem_req = 0; return mem_req; } /* Indicates whether notify_start is completed. */ inline bool _notify_start_complete(void) { return notify_state.start_complete; } /* Function registered as callback with the Notify driver */ void notify_exec(struct notify_object *obj, u32 event_id, u32 payload) { struct notify_event_callback *callback; WARN_ON(obj == NULL); if (WARN_ON((event_id >= notify_state.cfg.num_events) || (event_id >= NOTIFY_MAXEVENTS))) { pr_err("Invalid event_id %d\n", event_id); } else { mutex_lock_killable(&obj->lock); callback = &(obj->callbacks[event_id]); WARN_ON(callback->fn_notify_cbck == NULL); /* Execute the callback function with its argument and the payload */ callback->fn_notify_cbck(obj->remote_proc_id, obj->line_id, event_id, callback->cbck_arg, payload); mutex_unlock(&obj->lock); } } /* Function registered with notify_exec when multiple registrations are present * for the events. */ static void _notify_exec_many(u16 proc_id, u16 line_id, u32 event_id, uint *arg, u32 payload) { struct notify_object *obj = (struct notify_object *)arg; struct list_head *event_list; struct notify_event_listener *listener; WARN_ON(proc_id >= multiproc_get_num_processors()); WARN_ON(obj == NULL); WARN_ON(line_id >= NOTIFY_MAX_INTLINES); WARN_ON(event_id >= notify_state.cfg.num_events); /* Both loopback and the the event itself are enabled */ event_list = &(obj->event_list[event_id]); /* Use "NULL" to get the first EventListener on the list */ list_for_each_entry(listener, event_list, element) { listener->callback.fn_notify_cbck(proc_id, line_id, event_id, listener->callback.cbck_arg, payload); } }