summaryrefslogtreecommitdiff
path: root/drivers/staging/nmf-cm/cm_service.c
blob: 7335cccbd6abad290ba93b8dd091ae9f32a97f01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * Copyright (C) ST-Ericsson SA 2010
 * Author: Pierre Peiffer <pierre.peiffer@stericsson.com> for ST-Ericsson.
 * License terms: GNU General Public License (GPL), version 2.
 */

/** \file cm_service.c
 *
 * Nomadik Multiprocessing Framework Linux Driver
 *
 */

#include <linux/module.h>
#include <linux/plist.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spinlock_types.h>

#include <cm/engine/api/control/irq_engine.h>

#include "osal-kernel.h"
#include "cmld.h"
#include "cm_service.h"
#include "cm_dma.h"

/* Panic managment */
static void service_tasklet_func(unsigned long);
unsigned long service_tasklet_data = 0;
DECLARE_TASKLET(cmld_service_tasklet, service_tasklet_func, 0);

void dispatch_service_msg(struct osal_msg *msg)
{
	struct list_head *head, *next;
#ifdef CONFIG_DEBUG_FS
	bool dump_flag_to_set = true;
#endif
	/*
	 * Note: no lock needed to protect the channel_list against list
	 * changes, as the current tasklet is disabled each time we modify
	 * the list
	 */
	list_for_each_safe(head, next, &channel_list) {
		struct cm_channel_priv *channelPriv = list_entry(head, struct cm_channel_priv, entry);
		struct osal_msg *new_msg;
		size_t msg_size;

		if (channelPriv->state == CHANNEL_CLOSED)
			continue;
		msg_size = sizeof(new_msg->hdr) + sizeof(new_msg->d.srv);
		new_msg = kmalloc(msg_size, GFP_ATOMIC);
		if (new_msg == NULL) {
			pr_err("[CM] %s: can't allocate memory, service"
			       " message not dispatched !!\n", __func__);
			continue;
		}
		memcpy(new_msg, msg, msg_size);
		plist_node_init(&new_msg->msg_entry, 0);
#ifdef CONFIG_DEBUG_FS
		if (cmld_user_has_debugfs && dump_flag_to_set
		    && (new_msg->d.srv.srvType == NMF_SERVICE_PANIC)) {
			/*
			 * The reciever of this message will do the DSP
			 * memory dump
			 */
			new_msg->d.srv.srvData.panic.panicSource
				|= DEBUGFS_DUMP_FLAG;
			dump_flag_to_set = false;
			cmld_dump_ongoing = channelPriv->proc->pid;
		}
#endif
		spin_lock_bh(&channelPriv->bh_lock);
		plist_add(&new_msg->msg_entry, &channelPriv->messageQueue);
		spin_unlock_bh(&channelPriv->bh_lock);
		wake_up(&channelPriv->waitq);
	}
}

static void service_tasklet_func(unsigned long unused)
{
	t_cm_service_type type;
	t_cm_service_description desc;
	int i=0;

	do {
		if (test_and_clear_bit(i, &service_tasklet_data)) { 
			CM_getServiceDescription(osalEnv.mpc[i].coreId, &type, &desc);

			switch (type) {
			case CM_MPC_SERVICE_PANIC: {
				struct osal_msg msg;

				msg.msg_type = MSG_SERVICE;
				msg.d.srv.srvType = NMF_SERVICE_PANIC;
				msg.d.srv.srvData.panic = desc.u.panic;

				dispatch_service_msg(&msg);
				/*
				 * Stop DMA directly before shutdown, to avoid
				 * bad sound. Should be called after DSP has
				 * stopped executing, to avoid the DSP
				 * re-starting DMA
				 */
				if (osalEnv.mpc[i].coreId == SIA_CORE_ID)
					cmdma_stop_dma();
				break;
			}
			case CM_MPC_SERVICE_PRINT: {
				char msg[256];
				if (CM_ReadMPCString(osalEnv.mpc[i].coreId,
						     desc.u.print.dspAddress, msg,
						     sizeof(msg)) == CM_OK)
					printk(msg, desc.u.print.value1,
					       desc.u.print.value2);
				break;
			}
			case CM_MPC_SERVICE_TRACE:
				spin_lock_bh(&osalEnv.mpc[i].trace_reader_lock);
				if (osalEnv.mpc[i].trace_reader)
					wake_up_process(osalEnv.mpc[i].trace_reader);
				spin_unlock_bh(&osalEnv.mpc[i].trace_reader_lock);
				break;
			default:
				pr_err("[CM] %s: MPC Service Type %d not supported\n", __func__, type);
			}
			enable_irq(osalEnv.mpc[i].interrupt1);
		}
		i = (i+1) % NB_MPC;
	} while (service_tasklet_data != 0);
}