[RAMEN9610-20413][9610] wlbt: SCSC Driver version 10.6.1.0
[GitHub/MotorolaMobilityLLC/kernel-slsi.git] / drivers / misc / samsung / scsc / mxmgmt_transport.c
1 /****************************************************************************
2 *
3 * Copyright (c) 2014 - 2017 Samsung Electronics Co., Ltd. All rights reserved
4 *
5 ****************************************************************************/
6
7 /**
8 * Maxwell management transport (implementation)
9 */
10
11 /** Implements */
12 #include "mxmgmt_transport.h"
13
14 /** Uses */
15 #include <scsc/scsc_logring.h>
16 #include <linux/module.h>
17 #include "mxmgmt_transport_format.h"
18 #include "mifintrbit.h"
19
20 /* Flag that an error has occurred so the I/O thread processing should stop */
21 void mxmgmt_transport_set_error(struct mxmgmt_transport *mxmgmt_transport)
22 {
23 SCSC_TAG_WARNING(MXMGT_TRANS, "I/O thread processing is suspended\n");
24
25 mxmgmt_transport->mxmgmt_thread.block_thread = 1;
26 }
27
28 /** MIF Interrupt handler for writes made to the AP */
29 static void input_irq_handler(int irq, void *data)
30 {
31 struct mxmgmt_transport *mxmgmt_transport = (struct mxmgmt_transport *)data;
32 struct mxmgmt_thread *th = &mxmgmt_transport->mxmgmt_thread;
33 struct scsc_mif_abs *mif_abs;
34
35 SCSC_TAG_DEBUG(MXMGT_TRANS, "IN\n");
36 /* Clear the interrupt first to ensure we can't possibly miss one */
37 mif_abs = scsc_mx_get_mif_abs(mxmgmt_transport->mx);
38 mif_abs->irq_bit_clear(mif_abs, irq);
39
40 /* The the other side wrote some data to the input stream, wake up the thread
41 * that deals with this. */
42 if (th->task == NULL) {
43 SCSC_TAG_ERR(MXMGT_TRANS, "th is NOT running\n");
44 return;
45 }
46 /*
47 * If an error has occured, we discard silently all messages from the stream
48 * until the error has been processed and the system has been reinitialised.
49 */
50 if (th->block_thread == 1) {
51 SCSC_TAG_DEBUG(MXMGT_TRANS, "discard message.\n");
52 /*
53 * Do not try to acknowledge a pending interrupt here.
54 * This function is called by a function which in turn can be
55 * running in an atomic or 'disabled irq' level.
56 */
57 return;
58 }
59 th->wakeup_flag = 1;
60
61 /* wake up I/O thread */
62 wake_up_interruptible(&th->wakeup_q);
63 }
64
65 /** MIF Interrupt handler for acknowledging writes made by the AP */
66 static void output_irq_handler(int irq, void *data)
67 {
68 struct scsc_mif_abs *mif_abs;
69 struct mxmgmt_transport *mxmgmt_transport = (struct mxmgmt_transport *)data;
70
71 SCSC_TAG_DEBUG(MXMGT_TRANS, "OUT\n");
72
73 /* Clear the interrupt first to ensure we can't possibly miss one */
74 /* The FW read some data from the output stream.
75 * Currently we do not care, so just clear the interrupt. */
76 mif_abs = scsc_mx_get_mif_abs(mxmgmt_transport->mx);
77 mif_abs->irq_bit_clear(mif_abs, irq);
78
79 /* The driver doesn't use the ack IRQ, so mask it from now on,
80 * otherwise we may get spurious host-wakes.
81 */
82 mif_abs->irq_bit_mask(mif_abs, irq);
83 }
84
85
86 static void thread_wait_until_stopped(struct mxmgmt_transport *mxmgmt_transport)
87 {
88 struct mxmgmt_thread *th = &mxmgmt_transport->mxmgmt_thread;
89
90 /*
91 * kthread_stop() cannot handle the th exiting while
92 * kthread_should_stop() is false, so sleep until kthread_stop()
93 * wakes us up.
94 */
95 SCSC_TAG_DEBUG(MXMGT_TRANS, "%s waiting for the stop signal.\n", th->name);
96 set_current_state(TASK_INTERRUPTIBLE);
97 if (!kthread_should_stop()) {
98 SCSC_TAG_DEBUG(MXMGT_TRANS, "%s schedule....\n", th->name);
99 schedule();
100 }
101
102 th->task = NULL;
103 SCSC_TAG_DEBUG(MXMGT_TRANS, "%s exiting....\n", th->name);
104 }
105
106 /**
107 * A thread that forwards messages sent across the transport to
108 * the registered handlers for each channel.
109 */
110 static int mxmgmt_thread_function(void *arg)
111 {
112 struct mxmgmt_transport *mxmgmt_transport = (struct mxmgmt_transport *)arg;
113 struct mxmgmt_thread *th = &mxmgmt_transport->mxmgmt_thread;
114 const struct mxmgr_message *current_message;
115 int ret;
116
117 complete(&th->completion);
118
119 th->block_thread = 0;
120 while (!kthread_should_stop()) {
121 /* wait until an error occurs, or we need to process something. */
122
123 ret = wait_event_interruptible(th->wakeup_q,
124 (th->wakeup_flag && !th->block_thread) ||
125 kthread_should_stop());
126
127 if (kthread_should_stop()) {
128 SCSC_TAG_DEBUG(MXMGT_TRANS, "signalled to exit\n");
129 break;
130 }
131 if (ret < 0) {
132 SCSC_TAG_DEBUG(MXMGT_TRANS, "wait_event returned %d, thread will exit\n", ret);
133 thread_wait_until_stopped(mxmgmt_transport);
134 break;
135 }
136 th->wakeup_flag = 0;
137 SCSC_TAG_DEBUG(MXMGT_TRANS, "wokeup: r=%d\n", ret);
138 /* Forward each pending message to the applicable channel handler */
139 current_message = mif_stream_peek(&mxmgmt_transport->mif_istream, NULL);
140 while (current_message != NULL) {
141 mutex_lock(&mxmgmt_transport->channel_handler_mutex);
142 if (current_message->channel_id < MMTRANS_NUM_CHANNELS &&
143 mxmgmt_transport->channel_handler_fns[current_message->channel_id]) {
144 SCSC_TAG_DEBUG(MXMGT_TRANS, "Calling handler for channel_id: %d\n", current_message->channel_id);
145 (*mxmgmt_transport->channel_handler_fns[current_message->channel_id])(current_message->payload,
146 mxmgmt_transport->channel_handler_data[current_message->channel_id]);
147 } else
148 /* HERE: Invalid channel or no handler, raise fault or log message */
149 SCSC_TAG_WARNING(MXMGT_TRANS, "Invalid channel or no handler channel_id: %d\n", current_message->channel_id);
150 mutex_unlock(&mxmgmt_transport->channel_handler_mutex);
151 /* Remove the current message from the buffer before processing the next
152 * one in case it generated another message, otherwise it's possible we
153 * could run out of space in the stream before we get through all the messages. */
154 mif_stream_peek_complete(&mxmgmt_transport->mif_istream, current_message);
155 current_message = mif_stream_peek(&mxmgmt_transport->mif_istream, NULL);
156 }
157 }
158
159 SCSC_TAG_DEBUG(MXMGT_TRANS, "exiting....\n");
160 complete(&th->completion);
161 return 0;
162 }
163
164
165 static int mxmgmt_thread_start(struct mxmgmt_transport *mxmgmt_transport)
166 {
167 int err;
168 struct mxmgmt_thread *th = &mxmgmt_transport->mxmgmt_thread;
169
170 if (th->task != NULL) {
171 SCSC_TAG_WARNING(MXMGT_TRANS, "%s thread already started\n", th->name);
172 return 0;
173 }
174
175 /* Initialise thread structure */
176 th->block_thread = 1;
177 init_waitqueue_head(&th->wakeup_q);
178 init_completion(&th->completion);
179 th->wakeup_flag = 0;
180 snprintf(th->name, MXMGMT_THREAD_NAME_MAX_LENGTH, "mxmgmt_thread");
181
182 /* Start the kernel thread */
183 th->task = kthread_run(mxmgmt_thread_function, mxmgmt_transport, "%s", th->name);
184 if (IS_ERR(th->task)) {
185 SCSC_TAG_ERR(MXMGT_TRANS, "error creating kthread\n");
186 return (int)PTR_ERR(th->task);
187 }
188
189 SCSC_TAG_DEBUG(MXMGT_TRANS, "Started thread %s\n", th->name);
190
191 /* wait until thread is started */
192 #define MGMT_THREAD_START_TMO_SEC (3)
193 err = wait_for_completion_timeout(&th->completion, msecs_to_jiffies(MGMT_THREAD_START_TMO_SEC*1000));
194 if (err == 0) {
195 SCSC_TAG_ERR(MXMGT_TRANS, "timeout in starting thread\n");
196 return -ETIMEDOUT;
197 }
198 return 0;
199 }
200
201 static void mgmt_thread_stop(struct mxmgmt_transport *mxmgmt_transport)
202 {
203 unsigned long left_jiffies;
204 struct mxmgmt_thread *th = &mxmgmt_transport->mxmgmt_thread;
205
206 if (!th->task) {
207 SCSC_TAG_WARNING(MXMGT_TRANS, "%s mgmt_thread is already stopped\n", th->name);
208 return;
209 }
210 SCSC_TAG_DEBUG(MXMGT_TRANS, "Stopping %s mgmt_thread\n", th->name);
211 kthread_stop(th->task);
212 /* wait until th stopped */
213 #define MGMT_THREAD_STOP_TMO_SEC (3)
214 left_jiffies =
215 wait_for_completion_timeout(&th->completion, msecs_to_jiffies(MGMT_THREAD_STOP_TMO_SEC*1000));
216 if (!left_jiffies)
217 SCSC_TAG_ERR(MXMGT_TRANS, "Failed to stop mgmt_thread %s\n",
218 th->name);
219 else
220 th->task = NULL;
221 }
222
223 void mxmgmt_transport_release(struct mxmgmt_transport *mxmgmt_transport)
224 {
225 mgmt_thread_stop(mxmgmt_transport);
226 mif_stream_release(&mxmgmt_transport->mif_istream);
227 mif_stream_release(&mxmgmt_transport->mif_ostream);
228 }
229
230 void mxmgmt_transport_config_serialise(struct mxmgmt_transport *mxmgmt_transport,
231 struct mxtransconf *trans_conf)
232 {
233 mif_stream_config_serialise(&mxmgmt_transport->mif_istream, &trans_conf->to_ap_stream_conf);
234 mif_stream_config_serialise(&mxmgmt_transport->mif_ostream, &trans_conf->from_ap_stream_conf);
235 }
236
237
238 /** Public functions */
239 int mxmgmt_transport_init(struct mxmgmt_transport *mxmgmt_transport, struct scsc_mx *mx)
240 {
241 #define MEM_LENGTH 512
242 int r;
243 uint32_t mem_length = MEM_LENGTH;
244 uint32_t packet_size = sizeof(struct mxmgr_message);
245 uint32_t num_packets;
246
247
248 /*
249 * Initialising a buffer of 1 byte is never legitimate, do not allow it.
250 * The memory buffer length must be a multiple of the packet size.
251 */
252 if (mem_length <= 1 || mem_length % packet_size != 0)
253 return -EIO;
254 memset(mxmgmt_transport, 0, sizeof(struct mxmgmt_transport));
255 num_packets = mem_length / packet_size;
256 mutex_init(&mxmgmt_transport->channel_handler_mutex);
257 mxmgmt_transport->mx = mx;
258 r = mif_stream_init(&mxmgmt_transport->mif_istream, SCSC_MIF_ABS_TARGET_R4, MIF_STREAM_DIRECTION_IN, num_packets, packet_size, mx, MIF_STREAM_INTRBIT_TYPE_ALLOC, input_irq_handler, mxmgmt_transport);
259 if (r) {
260 SCSC_TAG_ERR(MXMGT_TRANS, "mif_stream_init IN failed %d\n", r);
261 return r;
262 }
263 r = mif_stream_init(&mxmgmt_transport->mif_ostream, SCSC_MIF_ABS_TARGET_R4, MIF_STREAM_DIRECTION_OUT, num_packets, packet_size, mx, MIF_STREAM_INTRBIT_TYPE_ALLOC, output_irq_handler, mxmgmt_transport);
264 if (r) {
265 SCSC_TAG_ERR(MXMGT_TRANS, "mif_stream_init OUT failed %d\n", r);
266 mif_stream_release(&mxmgmt_transport->mif_istream);
267 return r;
268 }
269
270 r = mxmgmt_thread_start(mxmgmt_transport);
271 if (r) {
272 SCSC_TAG_ERR(MXMGT_TRANS, "mxmgmt_thread_start failed %d\n", r);
273 mif_stream_release(&mxmgmt_transport->mif_istream);
274 mif_stream_release(&mxmgmt_transport->mif_ostream);
275 return r;
276 }
277 return 0;
278 }
279
280 void mxmgmt_transport_register_channel_handler(struct mxmgmt_transport *mxmgmt_transport, enum mxmgr_channels channel_id,
281 mxmgmt_channel_handler handler, void *data)
282 {
283 if (channel_id >= MMTRANS_NUM_CHANNELS) {
284 SCSC_TAG_ERR(MXMGT_TRANS, "Invalid channel id: %d\n", channel_id);
285 return;
286 }
287 mutex_lock(&mxmgmt_transport->channel_handler_mutex);
288 mxmgmt_transport->channel_handler_fns[channel_id] = handler;
289 mxmgmt_transport->channel_handler_data[channel_id] = data;
290 mutex_unlock(&mxmgmt_transport->channel_handler_mutex);
291 }
292
293 void mxmgmt_transport_send(struct mxmgmt_transport *mxmgmt_transport, enum mxmgr_channels channel_id,
294 void *message, uint32_t message_length)
295 {
296 struct mxmgr_message transport_msg = { .channel_id = channel_id };
297
298 const void *bufs[2] = { &transport_msg.channel_id, message };
299 uint32_t buf_lengths[2] = { sizeof(transport_msg.channel_id), message_length };
300
301 mif_stream_write_gather(&mxmgmt_transport->mif_ostream, bufs, buf_lengths, 2);
302 }