From 999e07d63289f7a401972cf390f569ffcd3d3a7b Mon Sep 17 00:00:00 2001 From: Omar Ramirez Luna Date: Wed, 23 Jun 2010 16:01:56 +0300 Subject: staging: ti dspbridge: add core driver sources Add TI's DSP Bridge core driver sources Signed-off-by: Omar Ramirez Luna Signed-off-by: Kanigeri, Hari Signed-off-by: Ameya Palande Signed-off-by: Guzman Lugo, Fernando Signed-off-by: Hebbar, Shivananda Signed-off-by: Ramos Falcon, Ernesto Signed-off-by: Felipe Contreras Signed-off-by: Anna, Suman Signed-off-by: Gupta, Ramesh Signed-off-by: Gomez Castellanos, Ivan Signed-off-by: Andy Shevchenko Signed-off-by: Armando Uribe De Leon Signed-off-by: Deepak Chitriki Signed-off-by: Menon, Nishanth Signed-off-by: Phil Carmody Signed-off-by: Ohad Ben-Cohen Signed-off-by: Greg Kroah-Hartman --- drivers/staging/tidspbridge/core/msg_sm.c | 673 ++++++++++++++++++++++++++++++ 1 file changed, 673 insertions(+) create mode 100644 drivers/staging/tidspbridge/core/msg_sm.c (limited to 'drivers/staging/tidspbridge/core/msg_sm.c') diff --git a/drivers/staging/tidspbridge/core/msg_sm.c b/drivers/staging/tidspbridge/core/msg_sm.c new file mode 100644 index 000000000000..7c6d6cc83604 --- /dev/null +++ b/drivers/staging/tidspbridge/core/msg_sm.c @@ -0,0 +1,673 @@ +/* + * msg_sm.c + * + * DSP-BIOS Bridge driver support functions for TI OMAP processors. + * + * Implements upper edge functions for Bridge message module. + * + * Copyright (C) 2005-2006 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. + */ + +/* ----------------------------------- DSP/BIOS Bridge */ +#include +#include + +/* ----------------------------------- Trace & Debug */ +#include + +/* ----------------------------------- OS Adaptation Layer */ +#include +#include + +/* ----------------------------------- Platform Manager */ +#include + +/* ----------------------------------- Others */ +#include + +/* ----------------------------------- This */ +#include <_msg_sm.h> +#include + +/* ----------------------------------- Function Prototypes */ +static int add_new_msg(struct lst_list *msgList); +static void delete_msg_mgr(struct msg_mgr *hmsg_mgr); +static void delete_msg_queue(struct msg_queue *msg_queue_obj, u32 uNumToDSP); +static void free_msg_list(struct lst_list *msgList); + +/* + * ======== bridge_msg_create ======== + * Create an object to manage message queues. Only one of these objects + * can exist per device object. + */ +int bridge_msg_create(OUT struct msg_mgr **phMsgMgr, + struct dev_object *hdev_obj, + msg_onexit msgCallback) +{ + struct msg_mgr *msg_mgr_obj; + struct io_mgr *hio_mgr; + int status = 0; + + if (!phMsgMgr || !msgCallback || !hdev_obj) { + status = -EFAULT; + goto func_end; + } + dev_get_io_mgr(hdev_obj, &hio_mgr); + if (!hio_mgr) { + status = -EFAULT; + goto func_end; + } + *phMsgMgr = NULL; + /* Allocate msg_ctrl manager object */ + msg_mgr_obj = kzalloc(sizeof(struct msg_mgr), GFP_KERNEL); + + if (msg_mgr_obj) { + msg_mgr_obj->on_exit = msgCallback; + msg_mgr_obj->hio_mgr = hio_mgr; + /* List of MSG_QUEUEs */ + msg_mgr_obj->queue_list = kzalloc(sizeof(struct lst_list), + GFP_KERNEL); + /* Queues of message frames for messages to the DSP. Message + * frames will only be added to the free queue when a + * msg_queue object is created. */ + msg_mgr_obj->msg_free_list = kzalloc(sizeof(struct lst_list), + GFP_KERNEL); + msg_mgr_obj->msg_used_list = kzalloc(sizeof(struct lst_list), + GFP_KERNEL); + if (msg_mgr_obj->queue_list == NULL || + msg_mgr_obj->msg_free_list == NULL || + msg_mgr_obj->msg_used_list == NULL) { + status = -ENOMEM; + } else { + INIT_LIST_HEAD(&msg_mgr_obj->queue_list->head); + INIT_LIST_HEAD(&msg_mgr_obj->msg_free_list->head); + INIT_LIST_HEAD(&msg_mgr_obj->msg_used_list->head); + spin_lock_init(&msg_mgr_obj->msg_mgr_lock); + } + + /* Create an event to be used by bridge_msg_put() in waiting + * for an available free frame from the message manager. */ + msg_mgr_obj->sync_event = + kzalloc(sizeof(struct sync_object), GFP_KERNEL); + if (!msg_mgr_obj->sync_event) + status = -ENOMEM; + else + sync_init_event(msg_mgr_obj->sync_event); + + if (DSP_SUCCEEDED(status)) + *phMsgMgr = msg_mgr_obj; + else + delete_msg_mgr(msg_mgr_obj); + + } else { + status = -ENOMEM; + } +func_end: + return status; +} + +/* + * ======== bridge_msg_create_queue ======== + * Create a msg_queue for sending/receiving messages to/from a node + * on the DSP. + */ +int bridge_msg_create_queue(struct msg_mgr *hmsg_mgr, + OUT struct msg_queue **phMsgQueue, + u32 msgq_id, u32 max_msgs, void *arg) +{ + u32 i; + u32 num_allocated = 0; + struct msg_queue *msg_q; + int status = 0; + + if (!hmsg_mgr || phMsgQueue == NULL || !hmsg_mgr->msg_free_list) { + status = -EFAULT; + goto func_end; + } + + *phMsgQueue = NULL; + /* Allocate msg_queue object */ + msg_q = kzalloc(sizeof(struct msg_queue), GFP_KERNEL); + if (!msg_q) { + status = -ENOMEM; + goto func_end; + } + lst_init_elem((struct list_head *)msg_q); + msg_q->max_msgs = max_msgs; + msg_q->hmsg_mgr = hmsg_mgr; + msg_q->arg = arg; /* Node handle */ + msg_q->msgq_id = msgq_id; /* Node env (not valid yet) */ + /* Queues of Message frames for messages from the DSP */ + msg_q->msg_free_list = kzalloc(sizeof(struct lst_list), GFP_KERNEL); + msg_q->msg_used_list = kzalloc(sizeof(struct lst_list), GFP_KERNEL); + if (msg_q->msg_free_list == NULL || msg_q->msg_used_list == NULL) + status = -ENOMEM; + else { + INIT_LIST_HEAD(&msg_q->msg_free_list->head); + INIT_LIST_HEAD(&msg_q->msg_used_list->head); + } + + /* Create event that will be signalled when a message from + * the DSP is available. */ + if (DSP_SUCCEEDED(status)) { + msg_q->sync_event = kzalloc(sizeof(struct sync_object), + GFP_KERNEL); + if (msg_q->sync_event) + sync_init_event(msg_q->sync_event); + else + status = -ENOMEM; + } + + /* Create a notification list for message ready notification. */ + if (DSP_SUCCEEDED(status)) { + msg_q->ntfy_obj = kmalloc(sizeof(struct ntfy_object), + GFP_KERNEL); + if (msg_q->ntfy_obj) + ntfy_init(msg_q->ntfy_obj); + else + status = -ENOMEM; + } + + /* Create events that will be used to synchronize cleanup + * when the object is deleted. sync_done will be set to + * unblock threads in MSG_Put() or MSG_Get(). sync_done_ack + * will be set by the unblocked thread to signal that it + * is unblocked and will no longer reference the object. */ + if (DSP_SUCCEEDED(status)) { + msg_q->sync_done = kzalloc(sizeof(struct sync_object), + GFP_KERNEL); + if (msg_q->sync_done) + sync_init_event(msg_q->sync_done); + else + status = -ENOMEM; + } + + if (DSP_SUCCEEDED(status)) { + msg_q->sync_done_ack = kzalloc(sizeof(struct sync_object), + GFP_KERNEL); + if (msg_q->sync_done_ack) + sync_init_event(msg_q->sync_done_ack); + else + status = -ENOMEM; + } + + if (DSP_SUCCEEDED(status)) { + /* Enter critical section */ + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + /* Initialize message frames and put in appropriate queues */ + for (i = 0; i < max_msgs && DSP_SUCCEEDED(status); i++) { + status = add_new_msg(hmsg_mgr->msg_free_list); + if (DSP_SUCCEEDED(status)) { + num_allocated++; + status = add_new_msg(msg_q->msg_free_list); + } + } + if (DSP_FAILED(status)) { + /* Stay inside CS to prevent others from taking any + * of the newly allocated message frames. */ + delete_msg_queue(msg_q, num_allocated); + } else { + lst_put_tail(hmsg_mgr->queue_list, + (struct list_head *)msg_q); + *phMsgQueue = msg_q; + /* Signal that free frames are now available */ + if (!LST_IS_EMPTY(hmsg_mgr->msg_free_list)) + sync_set_event(hmsg_mgr->sync_event); + + } + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + } else { + delete_msg_queue(msg_q, 0); + } +func_end: + return status; +} + +/* + * ======== bridge_msg_delete ======== + * Delete a msg_ctrl manager allocated in bridge_msg_create(). + */ +void bridge_msg_delete(struct msg_mgr *hmsg_mgr) +{ + if (hmsg_mgr) + delete_msg_mgr(hmsg_mgr); +} + +/* + * ======== bridge_msg_delete_queue ======== + * Delete a msg_ctrl queue allocated in bridge_msg_create_queue. + */ +void bridge_msg_delete_queue(struct msg_queue *msg_queue_obj) +{ + struct msg_mgr *hmsg_mgr; + u32 io_msg_pend; + + if (!msg_queue_obj || !msg_queue_obj->hmsg_mgr) + goto func_end; + + hmsg_mgr = msg_queue_obj->hmsg_mgr; + msg_queue_obj->done = true; + /* Unblock all threads blocked in MSG_Get() or MSG_Put(). */ + io_msg_pend = msg_queue_obj->io_msg_pend; + while (io_msg_pend) { + /* Unblock thread */ + sync_set_event(msg_queue_obj->sync_done); + /* Wait for acknowledgement */ + sync_wait_on_event(msg_queue_obj->sync_done_ack, SYNC_INFINITE); + io_msg_pend = msg_queue_obj->io_msg_pend; + } + /* Remove message queue from hmsg_mgr->queue_list */ + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + lst_remove_elem(hmsg_mgr->queue_list, + (struct list_head *)msg_queue_obj); + /* Free the message queue object */ + delete_msg_queue(msg_queue_obj, msg_queue_obj->max_msgs); + if (!hmsg_mgr->msg_free_list) + goto func_cont; + if (LST_IS_EMPTY(hmsg_mgr->msg_free_list)) + sync_reset_event(hmsg_mgr->sync_event); +func_cont: + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); +func_end: + return; +} + +/* + * ======== bridge_msg_get ======== + * Get a message from a msg_ctrl queue. + */ +int bridge_msg_get(struct msg_queue *msg_queue_obj, + struct dsp_msg *pmsg, u32 utimeout) +{ + struct msg_frame *msg_frame_obj; + struct msg_mgr *hmsg_mgr; + bool got_msg = false; + struct sync_object *syncs[2]; + u32 index; + int status = 0; + + if (!msg_queue_obj || pmsg == NULL) { + status = -ENOMEM; + goto func_end; + } + + hmsg_mgr = msg_queue_obj->hmsg_mgr; + if (!msg_queue_obj->msg_used_list) { + status = -EFAULT; + goto func_end; + } + + /* Enter critical section */ + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + /* If a message is already there, get it */ + if (!LST_IS_EMPTY(msg_queue_obj->msg_used_list)) { + msg_frame_obj = (struct msg_frame *) + lst_get_head(msg_queue_obj->msg_used_list); + if (msg_frame_obj != NULL) { + *pmsg = msg_frame_obj->msg_data.msg; + lst_put_tail(msg_queue_obj->msg_free_list, + (struct list_head *)msg_frame_obj); + if (LST_IS_EMPTY(msg_queue_obj->msg_used_list)) + sync_reset_event(msg_queue_obj->sync_event); + + got_msg = true; + } + } else { + if (msg_queue_obj->done) + status = -EPERM; + else + msg_queue_obj->io_msg_pend++; + + } + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + if (DSP_SUCCEEDED(status) && !got_msg) { + /* Wait til message is available, timeout, or done. We don't + * have to schedule the DPC, since the DSP will send messages + * when they are available. */ + syncs[0] = msg_queue_obj->sync_event; + syncs[1] = msg_queue_obj->sync_done; + status = sync_wait_on_multiple_events(syncs, 2, utimeout, + &index); + /* Enter critical section */ + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + if (msg_queue_obj->done) { + msg_queue_obj->io_msg_pend--; + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + /* Signal that we're not going to access msg_queue_obj + * anymore, so it can be deleted. */ + (void)sync_set_event(msg_queue_obj->sync_done_ack); + status = -EPERM; + } else { + if (DSP_SUCCEEDED(status)) { + DBC_ASSERT(!LST_IS_EMPTY + (msg_queue_obj->msg_used_list)); + /* Get msg from used list */ + msg_frame_obj = (struct msg_frame *) + lst_get_head(msg_queue_obj->msg_used_list); + /* Copy message into pmsg and put frame on the + * free list */ + if (msg_frame_obj != NULL) { + *pmsg = msg_frame_obj->msg_data.msg; + lst_put_tail + (msg_queue_obj->msg_free_list, + (struct list_head *) + msg_frame_obj); + } + } + msg_queue_obj->io_msg_pend--; + /* Reset the event if there are still queued messages */ + if (!LST_IS_EMPTY(msg_queue_obj->msg_used_list)) + sync_set_event(msg_queue_obj->sync_event); + + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + } + } +func_end: + return status; +} + +/* + * ======== bridge_msg_put ======== + * Put a message onto a msg_ctrl queue. + */ +int bridge_msg_put(struct msg_queue *msg_queue_obj, + IN CONST struct dsp_msg *pmsg, u32 utimeout) +{ + struct msg_frame *msg_frame_obj; + struct msg_mgr *hmsg_mgr; + bool put_msg = false; + struct sync_object *syncs[2]; + u32 index; + int status = 0; + + if (!msg_queue_obj || !pmsg || !msg_queue_obj->hmsg_mgr) { + status = -ENOMEM; + goto func_end; + } + hmsg_mgr = msg_queue_obj->hmsg_mgr; + if (!hmsg_mgr->msg_free_list) { + status = -EFAULT; + goto func_end; + } + + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + + /* If a message frame is available, use it */ + if (!LST_IS_EMPTY(hmsg_mgr->msg_free_list)) { + msg_frame_obj = + (struct msg_frame *)lst_get_head(hmsg_mgr->msg_free_list); + if (msg_frame_obj != NULL) { + msg_frame_obj->msg_data.msg = *pmsg; + msg_frame_obj->msg_data.msgq_id = + msg_queue_obj->msgq_id; + lst_put_tail(hmsg_mgr->msg_used_list, + (struct list_head *)msg_frame_obj); + hmsg_mgr->msgs_pending++; + put_msg = true; + } + if (LST_IS_EMPTY(hmsg_mgr->msg_free_list)) + sync_reset_event(hmsg_mgr->sync_event); + + /* Release critical section before scheduling DPC */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + /* Schedule a DPC, to do the actual data transfer: */ + iosm_schedule(hmsg_mgr->hio_mgr); + } else { + if (msg_queue_obj->done) + status = -EPERM; + else + msg_queue_obj->io_msg_pend++; + + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + } + if (DSP_SUCCEEDED(status) && !put_msg) { + /* Wait til a free message frame is available, timeout, + * or done */ + syncs[0] = hmsg_mgr->sync_event; + syncs[1] = msg_queue_obj->sync_done; + status = sync_wait_on_multiple_events(syncs, 2, utimeout, + &index); + if (DSP_FAILED(status)) + goto func_end; + /* Enter critical section */ + spin_lock_bh(&hmsg_mgr->msg_mgr_lock); + if (msg_queue_obj->done) { + msg_queue_obj->io_msg_pend--; + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + /* Signal that we're not going to access msg_queue_obj + * anymore, so it can be deleted. */ + (void)sync_set_event(msg_queue_obj->sync_done_ack); + status = -EPERM; + } else { + if (LST_IS_EMPTY(hmsg_mgr->msg_free_list)) { + status = -EFAULT; + goto func_cont; + } + /* Get msg from free list */ + msg_frame_obj = (struct msg_frame *) + lst_get_head(hmsg_mgr->msg_free_list); + /* + * Copy message into pmsg and put frame on the + * used list. + */ + if (msg_frame_obj) { + msg_frame_obj->msg_data.msg = *pmsg; + msg_frame_obj->msg_data.msgq_id = + msg_queue_obj->msgq_id; + lst_put_tail(hmsg_mgr->msg_used_list, + (struct list_head *)msg_frame_obj); + hmsg_mgr->msgs_pending++; + /* + * Schedule a DPC, to do the actual + * data transfer. + */ + iosm_schedule(hmsg_mgr->hio_mgr); + } + + msg_queue_obj->io_msg_pend--; + /* Reset event if there are still frames available */ + if (!LST_IS_EMPTY(hmsg_mgr->msg_free_list)) + sync_set_event(hmsg_mgr->sync_event); +func_cont: + /* Exit critical section */ + spin_unlock_bh(&hmsg_mgr->msg_mgr_lock); + } + } +func_end: + return status; +} + +/* + * ======== bridge_msg_register_notify ======== + */ +int bridge_msg_register_notify(struct msg_queue *msg_queue_obj, + u32 event_mask, u32 notify_type, + struct dsp_notification *hnotification) +{ + int status = 0; + + if (!msg_queue_obj || !hnotification) { + status = -ENOMEM; + goto func_end; + } + + if (!(event_mask == DSP_NODEMESSAGEREADY || event_mask == 0)) { + status = -EPERM; + goto func_end; + } + + if (notify_type != DSP_SIGNALEVENT) { + status = -EBADR; + goto func_end; + } + + if (event_mask) + status = ntfy_register(msg_queue_obj->ntfy_obj, hnotification, + event_mask, notify_type); + else + status = ntfy_unregister(msg_queue_obj->ntfy_obj, + hnotification); + + if (status == -EINVAL) { + /* Not registered. Ok, since we couldn't have known. Node + * notifications are split between node state change handled + * by NODE, and message ready handled by msg_ctrl. */ + status = 0; + } +func_end: + return status; +} + +/* + * ======== bridge_msg_set_queue_id ======== + */ +void bridge_msg_set_queue_id(struct msg_queue *msg_queue_obj, u32 msgq_id) +{ + /* + * A message queue must be created when a node is allocated, + * so that node_register_notify() can be called before the node + * is created. Since we don't know the node environment until the + * node is created, we need this function to set msg_queue_obj->msgq_id + * to the node environment, after the node is created. + */ + if (msg_queue_obj) + msg_queue_obj->msgq_id = msgq_id; +} + +/* + * ======== add_new_msg ======== + * Must be called in message manager critical section. + */ +static int add_new_msg(struct lst_list *msgList) +{ + struct msg_frame *pmsg; + int status = 0; + + pmsg = kzalloc(sizeof(struct msg_frame), GFP_ATOMIC); + if (pmsg != NULL) { + lst_init_elem((struct list_head *)pmsg); + lst_put_tail(msgList, (struct list_head *)pmsg); + } else { + status = -ENOMEM; + } + + return status; +} + +/* + * ======== delete_msg_mgr ======== + */ +static void delete_msg_mgr(struct msg_mgr *hmsg_mgr) +{ + if (!hmsg_mgr) + goto func_end; + + if (hmsg_mgr->queue_list) { + if (LST_IS_EMPTY(hmsg_mgr->queue_list)) { + kfree(hmsg_mgr->queue_list); + hmsg_mgr->queue_list = NULL; + } + } + + if (hmsg_mgr->msg_free_list) { + free_msg_list(hmsg_mgr->msg_free_list); + hmsg_mgr->msg_free_list = NULL; + } + + if (hmsg_mgr->msg_used_list) { + free_msg_list(hmsg_mgr->msg_used_list); + hmsg_mgr->msg_used_list = NULL; + } + + kfree(hmsg_mgr->sync_event); + + kfree(hmsg_mgr); +func_end: + return; +} + +/* + * ======== delete_msg_queue ======== + */ +static void delete_msg_queue(struct msg_queue *msg_queue_obj, u32 uNumToDSP) +{ + struct msg_mgr *hmsg_mgr; + struct msg_frame *pmsg; + u32 i; + + if (!msg_queue_obj || + !msg_queue_obj->hmsg_mgr || !msg_queue_obj->hmsg_mgr->msg_free_list) + goto func_end; + + hmsg_mgr = msg_queue_obj->hmsg_mgr; + + /* Pull off uNumToDSP message frames from Msg manager and free */ + for (i = 0; i < uNumToDSP; i++) { + + if (!LST_IS_EMPTY(hmsg_mgr->msg_free_list)) { + pmsg = (struct msg_frame *) + lst_get_head(hmsg_mgr->msg_free_list); + kfree(pmsg); + } else { + /* Cannot free all of the message frames */ + break; + } + } + + if (msg_queue_obj->msg_free_list) { + free_msg_list(msg_queue_obj->msg_free_list); + msg_queue_obj->msg_free_list = NULL; + } + + if (msg_queue_obj->msg_used_list) { + free_msg_list(msg_queue_obj->msg_used_list); + msg_queue_obj->msg_used_list = NULL; + } + + if (msg_queue_obj->ntfy_obj) { + ntfy_delete(msg_queue_obj->ntfy_obj); + kfree(msg_queue_obj->ntfy_obj); + } + + kfree(msg_queue_obj->sync_event); + kfree(msg_queue_obj->sync_done); + kfree(msg_queue_obj->sync_done_ack); + + kfree(msg_queue_obj); +func_end: + return; + +} + +/* + * ======== free_msg_list ======== + */ +static void free_msg_list(struct lst_list *msgList) +{ + struct msg_frame *pmsg; + + if (!msgList) + goto func_end; + + while ((pmsg = (struct msg_frame *)lst_get_head(msgList)) != NULL) + kfree(pmsg); + + DBC_ASSERT(LST_IS_EMPTY(msgList)); + + kfree(msgList); +func_end: + return; +} -- cgit v1.2.3