diff options
Diffstat (limited to 'arch/arm/mach-msm/smd.c')
-rw-r--r-- | arch/arm/mach-msm/smd.c | 1046 |
1 files changed, 1046 insertions, 0 deletions
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c new file mode 100644 index 00000000000..cf11d414b42 --- /dev/null +++ b/arch/arm/mach-msm/smd.c | |||
@@ -0,0 +1,1046 @@ | |||
1 | /* arch/arm/mach-msm/smd.c | ||
2 | * | ||
3 | * Copyright (C) 2007 Google, Inc. | ||
4 | * Author: Brian Swetland <swetland@google.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/cdev.h> | ||
21 | #include <linux/device.h> | ||
22 | #include <linux/wait.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/irq.h> | ||
25 | #include <linux/list.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/debugfs.h> | ||
28 | #include <linux/delay.h> | ||
29 | |||
30 | #include <mach/msm_smd.h> | ||
31 | #include <mach/system.h> | ||
32 | |||
33 | #include "smd_private.h" | ||
34 | #include "proc_comm.h" | ||
35 | |||
36 | #if defined(CONFIG_ARCH_QSD8X50) | ||
37 | #define CONFIG_QDSP6 1 | ||
38 | #endif | ||
39 | |||
40 | void (*msm_hw_reset_hook)(void); | ||
41 | |||
42 | #define MODULE_NAME "msm_smd" | ||
43 | |||
44 | enum { | ||
45 | MSM_SMD_DEBUG = 1U << 0, | ||
46 | MSM_SMSM_DEBUG = 1U << 0, | ||
47 | }; | ||
48 | |||
49 | static int msm_smd_debug_mask; | ||
50 | |||
51 | struct shared_info { | ||
52 | int ready; | ||
53 | unsigned state; | ||
54 | }; | ||
55 | |||
56 | static unsigned dummy_state[SMSM_STATE_COUNT]; | ||
57 | |||
58 | static struct shared_info smd_info = { | ||
59 | .state = (unsigned) &dummy_state, | ||
60 | }; | ||
61 | |||
62 | module_param_named(debug_mask, msm_smd_debug_mask, | ||
63 | int, S_IRUGO | S_IWUSR | S_IWGRP); | ||
64 | |||
65 | static unsigned last_heap_free = 0xffffffff; | ||
66 | |||
67 | static inline void notify_other_smsm(void) | ||
68 | { | ||
69 | msm_a2m_int(5); | ||
70 | #ifdef CONFIG_QDSP6 | ||
71 | msm_a2m_int(8); | ||
72 | #endif | ||
73 | } | ||
74 | |||
75 | static inline void notify_modem_smd(void) | ||
76 | { | ||
77 | msm_a2m_int(0); | ||
78 | } | ||
79 | |||
80 | static inline void notify_dsp_smd(void) | ||
81 | { | ||
82 | msm_a2m_int(8); | ||
83 | } | ||
84 | |||
85 | static void smd_diag(void) | ||
86 | { | ||
87 | char *x; | ||
88 | |||
89 | x = smem_find(ID_DIAG_ERR_MSG, SZ_DIAG_ERR_MSG); | ||
90 | if (x != 0) { | ||
91 | x[SZ_DIAG_ERR_MSG - 1] = 0; | ||
92 | pr_info("smem: DIAG '%s'\n", x); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | /* call when SMSM_RESET flag is set in the A9's smsm_state */ | ||
97 | static void handle_modem_crash(void) | ||
98 | { | ||
99 | pr_err("ARM9 has CRASHED\n"); | ||
100 | smd_diag(); | ||
101 | |||
102 | /* hard reboot if possible */ | ||
103 | if (msm_hw_reset_hook) | ||
104 | msm_hw_reset_hook(); | ||
105 | |||
106 | /* in this case the modem or watchdog should reboot us */ | ||
107 | for (;;) | ||
108 | ; | ||
109 | } | ||
110 | |||
111 | uint32_t raw_smsm_get_state(enum smsm_state_item item) | ||
112 | { | ||
113 | return readl(smd_info.state + item * 4); | ||
114 | } | ||
115 | |||
116 | static int check_for_modem_crash(void) | ||
117 | { | ||
118 | if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) { | ||
119 | handle_modem_crash(); | ||
120 | return -1; | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | /* the spinlock is used to synchronize between the | ||
126 | * irq handler and code that mutates the channel | ||
127 | * list or fiddles with channel state | ||
128 | */ | ||
129 | DEFINE_SPINLOCK(smd_lock); | ||
130 | DEFINE_SPINLOCK(smem_lock); | ||
131 | |||
132 | /* the mutex is used during open() and close() | ||
133 | * operations to avoid races while creating or | ||
134 | * destroying smd_channel structures | ||
135 | */ | ||
136 | static DEFINE_MUTEX(smd_creation_mutex); | ||
137 | |||
138 | static int smd_initialized; | ||
139 | |||
140 | LIST_HEAD(smd_ch_closed_list); | ||
141 | LIST_HEAD(smd_ch_list_modem); | ||
142 | LIST_HEAD(smd_ch_list_dsp); | ||
143 | |||
144 | static unsigned char smd_ch_allocated[64]; | ||
145 | static struct work_struct probe_work; | ||
146 | |||
147 | /* how many bytes are available for reading */ | ||
148 | static int smd_stream_read_avail(struct smd_channel *ch) | ||
149 | { | ||
150 | return (ch->recv->head - ch->recv->tail) & ch->fifo_mask; | ||
151 | } | ||
152 | |||
153 | /* how many bytes we are free to write */ | ||
154 | static int smd_stream_write_avail(struct smd_channel *ch) | ||
155 | { | ||
156 | return ch->fifo_mask - | ||
157 | ((ch->send->head - ch->send->tail) & ch->fifo_mask); | ||
158 | } | ||
159 | |||
160 | static int smd_packet_read_avail(struct smd_channel *ch) | ||
161 | { | ||
162 | if (ch->current_packet) { | ||
163 | int n = smd_stream_read_avail(ch); | ||
164 | if (n > ch->current_packet) | ||
165 | n = ch->current_packet; | ||
166 | return n; | ||
167 | } else { | ||
168 | return 0; | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static int smd_packet_write_avail(struct smd_channel *ch) | ||
173 | { | ||
174 | int n = smd_stream_write_avail(ch); | ||
175 | return n > SMD_HEADER_SIZE ? n - SMD_HEADER_SIZE : 0; | ||
176 | } | ||
177 | |||
178 | static int ch_is_open(struct smd_channel *ch) | ||
179 | { | ||
180 | return (ch->recv->state == SMD_SS_OPENED) && | ||
181 | (ch->send->state == SMD_SS_OPENED); | ||
182 | } | ||
183 | |||
184 | /* provide a pointer and length to readable data in the fifo */ | ||
185 | static unsigned ch_read_buffer(struct smd_channel *ch, void **ptr) | ||
186 | { | ||
187 | unsigned head = ch->recv->head; | ||
188 | unsigned tail = ch->recv->tail; | ||
189 | *ptr = (void *) (ch->recv_data + tail); | ||
190 | |||
191 | if (tail <= head) | ||
192 | return head - tail; | ||
193 | else | ||
194 | return ch->fifo_size - tail; | ||
195 | } | ||
196 | |||
197 | /* advance the fifo read pointer after data from ch_read_buffer is consumed */ | ||
198 | static void ch_read_done(struct smd_channel *ch, unsigned count) | ||
199 | { | ||
200 | BUG_ON(count > smd_stream_read_avail(ch)); | ||
201 | ch->recv->tail = (ch->recv->tail + count) & ch->fifo_mask; | ||
202 | ch->send->fTAIL = 1; | ||
203 | } | ||
204 | |||
205 | /* basic read interface to ch_read_{buffer,done} used | ||
206 | * by smd_*_read() and update_packet_state() | ||
207 | * will read-and-discard if the _data pointer is null | ||
208 | */ | ||
209 | static int ch_read(struct smd_channel *ch, void *_data, int len) | ||
210 | { | ||
211 | void *ptr; | ||
212 | unsigned n; | ||
213 | unsigned char *data = _data; | ||
214 | int orig_len = len; | ||
215 | |||
216 | while (len > 0) { | ||
217 | n = ch_read_buffer(ch, &ptr); | ||
218 | if (n == 0) | ||
219 | break; | ||
220 | |||
221 | if (n > len) | ||
222 | n = len; | ||
223 | if (_data) | ||
224 | memcpy(data, ptr, n); | ||
225 | |||
226 | data += n; | ||
227 | len -= n; | ||
228 | ch_read_done(ch, n); | ||
229 | } | ||
230 | |||
231 | return orig_len - len; | ||
232 | } | ||
233 | |||
234 | static void update_stream_state(struct smd_channel *ch) | ||
235 | { | ||
236 | /* streams have no special state requiring updating */ | ||
237 | } | ||
238 | |||
239 | static void update_packet_state(struct smd_channel *ch) | ||
240 | { | ||
241 | unsigned hdr[5]; | ||
242 | int r; | ||
243 | |||
244 | /* can't do anything if we're in the middle of a packet */ | ||
245 | if (ch->current_packet != 0) | ||
246 | return; | ||
247 | |||
248 | /* don't bother unless we can get the full header */ | ||
249 | if (smd_stream_read_avail(ch) < SMD_HEADER_SIZE) | ||
250 | return; | ||
251 | |||
252 | r = ch_read(ch, hdr, SMD_HEADER_SIZE); | ||
253 | BUG_ON(r != SMD_HEADER_SIZE); | ||
254 | |||
255 | ch->current_packet = hdr[0]; | ||
256 | } | ||
257 | |||
258 | /* provide a pointer and length to next free space in the fifo */ | ||
259 | static unsigned ch_write_buffer(struct smd_channel *ch, void **ptr) | ||
260 | { | ||
261 | unsigned head = ch->send->head; | ||
262 | unsigned tail = ch->send->tail; | ||
263 | *ptr = (void *) (ch->send_data + head); | ||
264 | |||
265 | if (head < tail) { | ||
266 | return tail - head - 1; | ||
267 | } else { | ||
268 | if (tail == 0) | ||
269 | return ch->fifo_size - head - 1; | ||
270 | else | ||
271 | return ch->fifo_size - head; | ||
272 | } | ||
273 | } | ||
274 | |||
275 | /* advace the fifo write pointer after freespace | ||
276 | * from ch_write_buffer is filled | ||
277 | */ | ||
278 | static void ch_write_done(struct smd_channel *ch, unsigned count) | ||
279 | { | ||
280 | BUG_ON(count > smd_stream_write_avail(ch)); | ||
281 | ch->send->head = (ch->send->head + count) & ch->fifo_mask; | ||
282 | ch->send->fHEAD = 1; | ||
283 | } | ||
284 | |||
285 | static void ch_set_state(struct smd_channel *ch, unsigned n) | ||
286 | { | ||
287 | if (n == SMD_SS_OPENED) { | ||
288 | ch->send->fDSR = 1; | ||
289 | ch->send->fCTS = 1; | ||
290 | ch->send->fCD = 1; | ||
291 | } else { | ||
292 | ch->send->fDSR = 0; | ||
293 | ch->send->fCTS = 0; | ||
294 | ch->send->fCD = 0; | ||
295 | } | ||
296 | ch->send->state = n; | ||
297 | ch->send->fSTATE = 1; | ||
298 | ch->notify_other_cpu(); | ||
299 | } | ||
300 | |||
301 | static void do_smd_probe(void) | ||
302 | { | ||
303 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
304 | if (shared->heap_info.free_offset != last_heap_free) { | ||
305 | last_heap_free = shared->heap_info.free_offset; | ||
306 | schedule_work(&probe_work); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | static void smd_state_change(struct smd_channel *ch, | ||
311 | unsigned last, unsigned next) | ||
312 | { | ||
313 | ch->last_state = next; | ||
314 | |||
315 | pr_info("SMD: ch %d %d -> %d\n", ch->n, last, next); | ||
316 | |||
317 | switch (next) { | ||
318 | case SMD_SS_OPENING: | ||
319 | ch->recv->tail = 0; | ||
320 | case SMD_SS_OPENED: | ||
321 | if (ch->send->state != SMD_SS_OPENED) | ||
322 | ch_set_state(ch, SMD_SS_OPENED); | ||
323 | ch->notify(ch->priv, SMD_EVENT_OPEN); | ||
324 | break; | ||
325 | case SMD_SS_FLUSHING: | ||
326 | case SMD_SS_RESET: | ||
327 | /* we should force them to close? */ | ||
328 | default: | ||
329 | ch->notify(ch->priv, SMD_EVENT_CLOSE); | ||
330 | } | ||
331 | } | ||
332 | |||
333 | static void handle_smd_irq(struct list_head *list, void (*notify)(void)) | ||
334 | { | ||
335 | unsigned long flags; | ||
336 | struct smd_channel *ch; | ||
337 | int do_notify = 0; | ||
338 | unsigned ch_flags; | ||
339 | unsigned tmp; | ||
340 | |||
341 | spin_lock_irqsave(&smd_lock, flags); | ||
342 | list_for_each_entry(ch, list, ch_list) { | ||
343 | ch_flags = 0; | ||
344 | if (ch_is_open(ch)) { | ||
345 | if (ch->recv->fHEAD) { | ||
346 | ch->recv->fHEAD = 0; | ||
347 | ch_flags |= 1; | ||
348 | do_notify |= 1; | ||
349 | } | ||
350 | if (ch->recv->fTAIL) { | ||
351 | ch->recv->fTAIL = 0; | ||
352 | ch_flags |= 2; | ||
353 | do_notify |= 1; | ||
354 | } | ||
355 | if (ch->recv->fSTATE) { | ||
356 | ch->recv->fSTATE = 0; | ||
357 | ch_flags |= 4; | ||
358 | do_notify |= 1; | ||
359 | } | ||
360 | } | ||
361 | tmp = ch->recv->state; | ||
362 | if (tmp != ch->last_state) | ||
363 | smd_state_change(ch, ch->last_state, tmp); | ||
364 | if (ch_flags) { | ||
365 | ch->update_state(ch); | ||
366 | ch->notify(ch->priv, SMD_EVENT_DATA); | ||
367 | } | ||
368 | } | ||
369 | if (do_notify) | ||
370 | notify(); | ||
371 | spin_unlock_irqrestore(&smd_lock, flags); | ||
372 | do_smd_probe(); | ||
373 | } | ||
374 | |||
375 | static irqreturn_t smd_modem_irq_handler(int irq, void *data) | ||
376 | { | ||
377 | handle_smd_irq(&smd_ch_list_modem, notify_modem_smd); | ||
378 | return IRQ_HANDLED; | ||
379 | } | ||
380 | |||
381 | #if defined(CONFIG_QDSP6) | ||
382 | static irqreturn_t smd_dsp_irq_handler(int irq, void *data) | ||
383 | { | ||
384 | handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd); | ||
385 | return IRQ_HANDLED; | ||
386 | } | ||
387 | #endif | ||
388 | |||
389 | static void smd_fake_irq_handler(unsigned long arg) | ||
390 | { | ||
391 | handle_smd_irq(&smd_ch_list_modem, notify_modem_smd); | ||
392 | handle_smd_irq(&smd_ch_list_dsp, notify_dsp_smd); | ||
393 | } | ||
394 | |||
395 | static DECLARE_TASKLET(smd_fake_irq_tasklet, smd_fake_irq_handler, 0); | ||
396 | |||
397 | static inline int smd_need_int(struct smd_channel *ch) | ||
398 | { | ||
399 | if (ch_is_open(ch)) { | ||
400 | if (ch->recv->fHEAD || ch->recv->fTAIL || ch->recv->fSTATE) | ||
401 | return 1; | ||
402 | if (ch->recv->state != ch->last_state) | ||
403 | return 1; | ||
404 | } | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | void smd_sleep_exit(void) | ||
409 | { | ||
410 | unsigned long flags; | ||
411 | struct smd_channel *ch; | ||
412 | int need_int = 0; | ||
413 | |||
414 | spin_lock_irqsave(&smd_lock, flags); | ||
415 | list_for_each_entry(ch, &smd_ch_list_modem, ch_list) { | ||
416 | if (smd_need_int(ch)) { | ||
417 | need_int = 1; | ||
418 | break; | ||
419 | } | ||
420 | } | ||
421 | list_for_each_entry(ch, &smd_ch_list_dsp, ch_list) { | ||
422 | if (smd_need_int(ch)) { | ||
423 | need_int = 1; | ||
424 | break; | ||
425 | } | ||
426 | } | ||
427 | spin_unlock_irqrestore(&smd_lock, flags); | ||
428 | do_smd_probe(); | ||
429 | |||
430 | if (need_int) { | ||
431 | if (msm_smd_debug_mask & MSM_SMD_DEBUG) | ||
432 | pr_info("smd_sleep_exit need interrupt\n"); | ||
433 | tasklet_schedule(&smd_fake_irq_tasklet); | ||
434 | } | ||
435 | } | ||
436 | |||
437 | |||
438 | void smd_kick(smd_channel_t *ch) | ||
439 | { | ||
440 | unsigned long flags; | ||
441 | unsigned tmp; | ||
442 | |||
443 | spin_lock_irqsave(&smd_lock, flags); | ||
444 | ch->update_state(ch); | ||
445 | tmp = ch->recv->state; | ||
446 | if (tmp != ch->last_state) { | ||
447 | ch->last_state = tmp; | ||
448 | if (tmp == SMD_SS_OPENED) | ||
449 | ch->notify(ch->priv, SMD_EVENT_OPEN); | ||
450 | else | ||
451 | ch->notify(ch->priv, SMD_EVENT_CLOSE); | ||
452 | } | ||
453 | ch->notify(ch->priv, SMD_EVENT_DATA); | ||
454 | ch->notify_other_cpu(); | ||
455 | spin_unlock_irqrestore(&smd_lock, flags); | ||
456 | } | ||
457 | |||
458 | static int smd_is_packet(int chn, unsigned type) | ||
459 | { | ||
460 | type &= SMD_KIND_MASK; | ||
461 | if (type == SMD_KIND_PACKET) | ||
462 | return 1; | ||
463 | if (type == SMD_KIND_STREAM) | ||
464 | return 0; | ||
465 | |||
466 | /* older AMSS reports SMD_KIND_UNKNOWN always */ | ||
467 | if ((chn > 4) || (chn == 1)) | ||
468 | return 1; | ||
469 | else | ||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | static int smd_stream_write(smd_channel_t *ch, const void *_data, int len) | ||
474 | { | ||
475 | void *ptr; | ||
476 | const unsigned char *buf = _data; | ||
477 | unsigned xfer; | ||
478 | int orig_len = len; | ||
479 | |||
480 | if (len < 0) | ||
481 | return -EINVAL; | ||
482 | |||
483 | while ((xfer = ch_write_buffer(ch, &ptr)) != 0) { | ||
484 | if (!ch_is_open(ch)) | ||
485 | break; | ||
486 | if (xfer > len) | ||
487 | xfer = len; | ||
488 | memcpy(ptr, buf, xfer); | ||
489 | ch_write_done(ch, xfer); | ||
490 | len -= xfer; | ||
491 | buf += xfer; | ||
492 | if (len == 0) | ||
493 | break; | ||
494 | } | ||
495 | |||
496 | ch->notify_other_cpu(); | ||
497 | |||
498 | return orig_len - len; | ||
499 | } | ||
500 | |||
501 | static int smd_packet_write(smd_channel_t *ch, const void *_data, int len) | ||
502 | { | ||
503 | unsigned hdr[5]; | ||
504 | |||
505 | if (len < 0) | ||
506 | return -EINVAL; | ||
507 | |||
508 | if (smd_stream_write_avail(ch) < (len + SMD_HEADER_SIZE)) | ||
509 | return -ENOMEM; | ||
510 | |||
511 | hdr[0] = len; | ||
512 | hdr[1] = hdr[2] = hdr[3] = hdr[4] = 0; | ||
513 | |||
514 | smd_stream_write(ch, hdr, sizeof(hdr)); | ||
515 | smd_stream_write(ch, _data, len); | ||
516 | |||
517 | return len; | ||
518 | } | ||
519 | |||
520 | static int smd_stream_read(smd_channel_t *ch, void *data, int len) | ||
521 | { | ||
522 | int r; | ||
523 | |||
524 | if (len < 0) | ||
525 | return -EINVAL; | ||
526 | |||
527 | r = ch_read(ch, data, len); | ||
528 | if (r > 0) | ||
529 | ch->notify_other_cpu(); | ||
530 | |||
531 | return r; | ||
532 | } | ||
533 | |||
534 | static int smd_packet_read(smd_channel_t *ch, void *data, int len) | ||
535 | { | ||
536 | unsigned long flags; | ||
537 | int r; | ||
538 | |||
539 | if (len < 0) | ||
540 | return -EINVAL; | ||
541 | |||
542 | if (len > ch->current_packet) | ||
543 | len = ch->current_packet; | ||
544 | |||
545 | r = ch_read(ch, data, len); | ||
546 | if (r > 0) | ||
547 | ch->notify_other_cpu(); | ||
548 | |||
549 | spin_lock_irqsave(&smd_lock, flags); | ||
550 | ch->current_packet -= r; | ||
551 | update_packet_state(ch); | ||
552 | spin_unlock_irqrestore(&smd_lock, flags); | ||
553 | |||
554 | return r; | ||
555 | } | ||
556 | |||
557 | static int smd_alloc_channel(const char *name, uint32_t cid, uint32_t type) | ||
558 | { | ||
559 | struct smd_channel *ch; | ||
560 | |||
561 | ch = kzalloc(sizeof(struct smd_channel), GFP_KERNEL); | ||
562 | if (ch == 0) { | ||
563 | pr_err("smd_alloc_channel() out of memory\n"); | ||
564 | return -1; | ||
565 | } | ||
566 | ch->n = cid; | ||
567 | |||
568 | if (_smd_alloc_channel(ch)) { | ||
569 | kfree(ch); | ||
570 | return -1; | ||
571 | } | ||
572 | |||
573 | ch->fifo_mask = ch->fifo_size - 1; | ||
574 | ch->type = type; | ||
575 | |||
576 | if ((type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM) | ||
577 | ch->notify_other_cpu = notify_modem_smd; | ||
578 | else | ||
579 | ch->notify_other_cpu = notify_dsp_smd; | ||
580 | |||
581 | if (smd_is_packet(cid, type)) { | ||
582 | ch->read = smd_packet_read; | ||
583 | ch->write = smd_packet_write; | ||
584 | ch->read_avail = smd_packet_read_avail; | ||
585 | ch->write_avail = smd_packet_write_avail; | ||
586 | ch->update_state = update_packet_state; | ||
587 | } else { | ||
588 | ch->read = smd_stream_read; | ||
589 | ch->write = smd_stream_write; | ||
590 | ch->read_avail = smd_stream_read_avail; | ||
591 | ch->write_avail = smd_stream_write_avail; | ||
592 | ch->update_state = update_stream_state; | ||
593 | } | ||
594 | |||
595 | if ((type & 0xff) == 0) | ||
596 | memcpy(ch->name, "SMD_", 4); | ||
597 | else | ||
598 | memcpy(ch->name, "DSP_", 4); | ||
599 | memcpy(ch->name + 4, name, 20); | ||
600 | ch->name[23] = 0; | ||
601 | ch->pdev.name = ch->name; | ||
602 | ch->pdev.id = -1; | ||
603 | |||
604 | pr_info("smd_alloc_channel() cid=%02d size=%05d '%s'\n", | ||
605 | ch->n, ch->fifo_size, ch->name); | ||
606 | |||
607 | mutex_lock(&smd_creation_mutex); | ||
608 | list_add(&ch->ch_list, &smd_ch_closed_list); | ||
609 | mutex_unlock(&smd_creation_mutex); | ||
610 | |||
611 | platform_device_register(&ch->pdev); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static void smd_channel_probe_worker(struct work_struct *work) | ||
616 | { | ||
617 | struct smd_alloc_elm *shared; | ||
618 | unsigned ctype; | ||
619 | unsigned type; | ||
620 | unsigned n; | ||
621 | |||
622 | shared = smem_find(ID_CH_ALLOC_TBL, sizeof(*shared) * 64); | ||
623 | if (!shared) { | ||
624 | pr_err("smd: cannot find allocation table\n"); | ||
625 | return; | ||
626 | } | ||
627 | for (n = 0; n < 64; n++) { | ||
628 | if (smd_ch_allocated[n]) | ||
629 | continue; | ||
630 | if (!shared[n].ref_count) | ||
631 | continue; | ||
632 | if (!shared[n].name[0]) | ||
633 | continue; | ||
634 | ctype = shared[n].ctype; | ||
635 | type = ctype & SMD_TYPE_MASK; | ||
636 | |||
637 | /* DAL channels are stream but neither the modem, | ||
638 | * nor the DSP correctly indicate this. Fixup manually. | ||
639 | */ | ||
640 | if (!memcmp(shared[n].name, "DAL", 3)) | ||
641 | ctype = (ctype & (~SMD_KIND_MASK)) | SMD_KIND_STREAM; | ||
642 | |||
643 | type = shared[n].ctype & SMD_TYPE_MASK; | ||
644 | if ((type == SMD_TYPE_APPS_MODEM) || | ||
645 | (type == SMD_TYPE_APPS_DSP)) | ||
646 | if (!smd_alloc_channel(shared[n].name, shared[n].cid, ctype)) | ||
647 | smd_ch_allocated[n] = 1; | ||
648 | } | ||
649 | } | ||
650 | |||
651 | static void do_nothing_notify(void *priv, unsigned flags) | ||
652 | { | ||
653 | } | ||
654 | |||
655 | struct smd_channel *smd_get_channel(const char *name) | ||
656 | { | ||
657 | struct smd_channel *ch; | ||
658 | |||
659 | mutex_lock(&smd_creation_mutex); | ||
660 | list_for_each_entry(ch, &smd_ch_closed_list, ch_list) { | ||
661 | if (!strcmp(name, ch->name)) { | ||
662 | list_del(&ch->ch_list); | ||
663 | mutex_unlock(&smd_creation_mutex); | ||
664 | return ch; | ||
665 | } | ||
666 | } | ||
667 | mutex_unlock(&smd_creation_mutex); | ||
668 | |||
669 | return NULL; | ||
670 | } | ||
671 | |||
672 | int smd_open(const char *name, smd_channel_t **_ch, | ||
673 | void *priv, void (*notify)(void *, unsigned)) | ||
674 | { | ||
675 | struct smd_channel *ch; | ||
676 | unsigned long flags; | ||
677 | |||
678 | if (smd_initialized == 0) { | ||
679 | pr_info("smd_open() before smd_init()\n"); | ||
680 | return -ENODEV; | ||
681 | } | ||
682 | |||
683 | ch = smd_get_channel(name); | ||
684 | if (!ch) | ||
685 | return -ENODEV; | ||
686 | |||
687 | if (notify == 0) | ||
688 | notify = do_nothing_notify; | ||
689 | |||
690 | ch->notify = notify; | ||
691 | ch->current_packet = 0; | ||
692 | ch->last_state = SMD_SS_CLOSED; | ||
693 | ch->priv = priv; | ||
694 | |||
695 | *_ch = ch; | ||
696 | |||
697 | spin_lock_irqsave(&smd_lock, flags); | ||
698 | |||
699 | if ((ch->type & SMD_TYPE_MASK) == SMD_TYPE_APPS_MODEM) | ||
700 | list_add(&ch->ch_list, &smd_ch_list_modem); | ||
701 | else | ||
702 | list_add(&ch->ch_list, &smd_ch_list_dsp); | ||
703 | |||
704 | /* If the remote side is CLOSING, we need to get it to | ||
705 | * move to OPENING (which we'll do by moving from CLOSED to | ||
706 | * OPENING) and then get it to move from OPENING to | ||
707 | * OPENED (by doing the same state change ourselves). | ||
708 | * | ||
709 | * Otherwise, it should be OPENING and we can move directly | ||
710 | * to OPENED so that it will follow. | ||
711 | */ | ||
712 | if (ch->recv->state == SMD_SS_CLOSING) { | ||
713 | ch->send->head = 0; | ||
714 | ch_set_state(ch, SMD_SS_OPENING); | ||
715 | } else { | ||
716 | ch_set_state(ch, SMD_SS_OPENED); | ||
717 | } | ||
718 | spin_unlock_irqrestore(&smd_lock, flags); | ||
719 | smd_kick(ch); | ||
720 | |||
721 | return 0; | ||
722 | } | ||
723 | |||
724 | int smd_close(smd_channel_t *ch) | ||
725 | { | ||
726 | unsigned long flags; | ||
727 | |||
728 | pr_info("smd_close(%p)\n", ch); | ||
729 | |||
730 | if (ch == 0) | ||
731 | return -1; | ||
732 | |||
733 | spin_lock_irqsave(&smd_lock, flags); | ||
734 | ch->notify = do_nothing_notify; | ||
735 | list_del(&ch->ch_list); | ||
736 | ch_set_state(ch, SMD_SS_CLOSED); | ||
737 | spin_unlock_irqrestore(&smd_lock, flags); | ||
738 | |||
739 | mutex_lock(&smd_creation_mutex); | ||
740 | list_add(&ch->ch_list, &smd_ch_closed_list); | ||
741 | mutex_unlock(&smd_creation_mutex); | ||
742 | |||
743 | return 0; | ||
744 | } | ||
745 | |||
746 | int smd_read(smd_channel_t *ch, void *data, int len) | ||
747 | { | ||
748 | return ch->read(ch, data, len); | ||
749 | } | ||
750 | |||
751 | int smd_write(smd_channel_t *ch, const void *data, int len) | ||
752 | { | ||
753 | return ch->write(ch, data, len); | ||
754 | } | ||
755 | |||
756 | int smd_write_atomic(smd_channel_t *ch, const void *data, int len) | ||
757 | { | ||
758 | unsigned long flags; | ||
759 | int res; | ||
760 | spin_lock_irqsave(&smd_lock, flags); | ||
761 | res = ch->write(ch, data, len); | ||
762 | spin_unlock_irqrestore(&smd_lock, flags); | ||
763 | return res; | ||
764 | } | ||
765 | |||
766 | int smd_read_avail(smd_channel_t *ch) | ||
767 | { | ||
768 | return ch->read_avail(ch); | ||
769 | } | ||
770 | |||
771 | int smd_write_avail(smd_channel_t *ch) | ||
772 | { | ||
773 | return ch->write_avail(ch); | ||
774 | } | ||
775 | |||
776 | int smd_wait_until_readable(smd_channel_t *ch, int bytes) | ||
777 | { | ||
778 | return -1; | ||
779 | } | ||
780 | |||
781 | int smd_wait_until_writable(smd_channel_t *ch, int bytes) | ||
782 | { | ||
783 | return -1; | ||
784 | } | ||
785 | |||
786 | int smd_cur_packet_size(smd_channel_t *ch) | ||
787 | { | ||
788 | return ch->current_packet; | ||
789 | } | ||
790 | |||
791 | |||
792 | /* ------------------------------------------------------------------------- */ | ||
793 | |||
794 | void *smem_alloc(unsigned id, unsigned size) | ||
795 | { | ||
796 | return smem_find(id, size); | ||
797 | } | ||
798 | |||
799 | void *smem_item(unsigned id, unsigned *size) | ||
800 | { | ||
801 | struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE; | ||
802 | struct smem_heap_entry *toc = shared->heap_toc; | ||
803 | |||
804 | if (id >= SMEM_NUM_ITEMS) | ||
805 | return 0; | ||
806 | |||
807 | if (toc[id].allocated) { | ||
808 | *size = toc[id].size; | ||
809 | return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset); | ||
810 | } else { | ||
811 | *size = 0; | ||
812 | } | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | void *smem_find(unsigned id, unsigned size_in) | ||
818 | { | ||
819 | unsigned size; | ||
820 | void *ptr; | ||
821 | |||
822 | ptr = smem_item(id, &size); | ||
823 | if (!ptr) | ||
824 | return 0; | ||
825 | |||
826 | size_in = ALIGN(size_in, 8); | ||
827 | if (size_in != size) { | ||
828 | pr_err("smem_find(%d, %d): wrong size %d\n", | ||
829 | id, size_in, size); | ||
830 | return 0; | ||
831 | } | ||
832 | |||
833 | return ptr; | ||
834 | } | ||
835 | |||
836 | static irqreturn_t smsm_irq_handler(int irq, void *data) | ||
837 | { | ||
838 | unsigned long flags; | ||
839 | unsigned apps, modm; | ||
840 | |||
841 | spin_lock_irqsave(&smem_lock, flags); | ||
842 | |||
843 | apps = raw_smsm_get_state(SMSM_STATE_APPS); | ||
844 | modm = raw_smsm_get_state(SMSM_STATE_MODEM); | ||
845 | |||
846 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
847 | pr_info("<SM %08x %08x>\n", apps, modm); | ||
848 | if (modm & SMSM_RESET) | ||
849 | handle_modem_crash(); | ||
850 | |||
851 | do_smd_probe(); | ||
852 | |||
853 | spin_unlock_irqrestore(&smem_lock, flags); | ||
854 | return IRQ_HANDLED; | ||
855 | } | ||
856 | |||
857 | int smsm_change_state(enum smsm_state_item item, | ||
858 | uint32_t clear_mask, uint32_t set_mask) | ||
859 | { | ||
860 | unsigned long addr = smd_info.state + item * 4; | ||
861 | unsigned long flags; | ||
862 | unsigned state; | ||
863 | |||
864 | if (!smd_info.ready) | ||
865 | return -EIO; | ||
866 | |||
867 | spin_lock_irqsave(&smem_lock, flags); | ||
868 | |||
869 | if (raw_smsm_get_state(SMSM_STATE_MODEM) & SMSM_RESET) | ||
870 | handle_modem_crash(); | ||
871 | |||
872 | state = (readl(addr) & ~clear_mask) | set_mask; | ||
873 | writel(state, addr); | ||
874 | |||
875 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
876 | pr_info("smsm_change_state %d %x\n", item, state); | ||
877 | notify_other_smsm(); | ||
878 | |||
879 | spin_unlock_irqrestore(&smem_lock, flags); | ||
880 | |||
881 | return 0; | ||
882 | } | ||
883 | |||
884 | uint32_t smsm_get_state(enum smsm_state_item item) | ||
885 | { | ||
886 | unsigned long flags; | ||
887 | uint32_t rv; | ||
888 | |||
889 | spin_lock_irqsave(&smem_lock, flags); | ||
890 | |||
891 | rv = readl(smd_info.state + item * 4); | ||
892 | |||
893 | if (item == SMSM_STATE_MODEM && (rv & SMSM_RESET)) | ||
894 | handle_modem_crash(); | ||
895 | |||
896 | spin_unlock_irqrestore(&smem_lock, flags); | ||
897 | |||
898 | return rv; | ||
899 | } | ||
900 | |||
901 | #ifdef CONFIG_ARCH_MSM_SCORPION | ||
902 | |||
903 | int smsm_set_sleep_duration(uint32_t delay) | ||
904 | { | ||
905 | struct msm_dem_slave_data *ptr; | ||
906 | |||
907 | ptr = smem_find(SMEM_APPS_DEM_SLAVE_DATA, sizeof(*ptr)); | ||
908 | if (ptr == NULL) { | ||
909 | pr_err("smsm_set_sleep_duration <SM NO APPS_DEM_SLAVE_DATA>\n"); | ||
910 | return -EIO; | ||
911 | } | ||
912 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
913 | pr_info("smsm_set_sleep_duration %d -> %d\n", | ||
914 | ptr->sleep_time, delay); | ||
915 | ptr->sleep_time = delay; | ||
916 | return 0; | ||
917 | } | ||
918 | |||
919 | #else | ||
920 | |||
921 | int smsm_set_sleep_duration(uint32_t delay) | ||
922 | { | ||
923 | uint32_t *ptr; | ||
924 | |||
925 | ptr = smem_find(SMEM_SMSM_SLEEP_DELAY, sizeof(*ptr)); | ||
926 | if (ptr == NULL) { | ||
927 | pr_err("smsm_set_sleep_duration <SM NO SLEEP_DELAY>\n"); | ||
928 | return -EIO; | ||
929 | } | ||
930 | if (msm_smd_debug_mask & MSM_SMSM_DEBUG) | ||
931 | pr_info("smsm_set_sleep_duration %d -> %d\n", | ||
932 | *ptr, delay); | ||
933 | *ptr = delay; | ||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | #endif | ||
938 | |||
939 | int smd_core_init(void) | ||
940 | { | ||
941 | int r; | ||
942 | pr_info("smd_core_init()\n"); | ||
943 | |||
944 | /* wait for essential items to be initialized */ | ||
945 | for (;;) { | ||
946 | unsigned size; | ||
947 | void *state; | ||
948 | state = smem_item(SMEM_SMSM_SHARED_STATE, &size); | ||
949 | if (size == SMSM_V1_SIZE || size == SMSM_V2_SIZE) { | ||
950 | smd_info.state = (unsigned)state; | ||
951 | break; | ||
952 | } | ||
953 | } | ||
954 | |||
955 | smd_info.ready = 1; | ||
956 | |||
957 | r = request_irq(INT_A9_M2A_0, smd_modem_irq_handler, | ||
958 | IRQF_TRIGGER_RISING, "smd_dev", 0); | ||
959 | if (r < 0) | ||
960 | return r; | ||
961 | r = enable_irq_wake(INT_A9_M2A_0); | ||
962 | if (r < 0) | ||
963 | pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_0\n"); | ||
964 | |||
965 | r = request_irq(INT_A9_M2A_5, smsm_irq_handler, | ||
966 | IRQF_TRIGGER_RISING, "smsm_dev", 0); | ||
967 | if (r < 0) { | ||
968 | free_irq(INT_A9_M2A_0, 0); | ||
969 | return r; | ||
970 | } | ||
971 | r = enable_irq_wake(INT_A9_M2A_5); | ||
972 | if (r < 0) | ||
973 | pr_err("smd_core_init: enable_irq_wake failed for A9_M2A_5\n"); | ||
974 | |||
975 | #if defined(CONFIG_QDSP6) | ||
976 | r = request_irq(INT_ADSP_A11, smd_dsp_irq_handler, | ||
977 | IRQF_TRIGGER_RISING, "smd_dsp", 0); | ||
978 | if (r < 0) { | ||
979 | free_irq(INT_A9_M2A_0, 0); | ||
980 | free_irq(INT_A9_M2A_5, 0); | ||
981 | return r; | ||
982 | } | ||
983 | #endif | ||
984 | |||
985 | /* check for any SMD channels that may already exist */ | ||
986 | do_smd_probe(); | ||
987 | |||
988 | /* indicate that we're up and running */ | ||
989 | smsm_change_state(SMSM_STATE_APPS, | ||
990 | ~0, SMSM_INIT | SMSM_SMDINIT | SMSM_RPCINIT | SMSM_RUN); | ||
991 | #ifdef CONFIG_ARCH_MSM_SCORPION | ||
992 | smsm_change_state(SMSM_STATE_APPS_DEM, ~0, 0); | ||
993 | #endif | ||
994 | |||
995 | pr_info("smd_core_init() done\n"); | ||
996 | |||
997 | return 0; | ||
998 | } | ||
999 | |||
1000 | static int __init msm_smd_probe(struct platform_device *pdev) | ||
1001 | { | ||
1002 | pr_info("smd_init()\n"); | ||
1003 | |||
1004 | /* | ||
1005 | * If we haven't waited for the ARM9 to boot up till now, | ||
1006 | * then we need to wait here. Otherwise this should just | ||
1007 | * return immediately. | ||
1008 | */ | ||
1009 | proc_comm_boot_wait(); | ||
1010 | |||
1011 | INIT_WORK(&probe_work, smd_channel_probe_worker); | ||
1012 | |||
1013 | if (smd_core_init()) { | ||
1014 | pr_err("smd_core_init() failed\n"); | ||
1015 | return -1; | ||
1016 | } | ||
1017 | |||
1018 | do_smd_probe(); | ||
1019 | |||
1020 | msm_check_for_modem_crash = check_for_modem_crash; | ||
1021 | |||
1022 | msm_init_last_radio_log(THIS_MODULE); | ||
1023 | |||
1024 | smd_initialized = 1; | ||
1025 | |||
1026 | return 0; | ||
1027 | } | ||
1028 | |||
1029 | static struct platform_driver msm_smd_driver = { | ||
1030 | .probe = msm_smd_probe, | ||
1031 | .driver = { | ||
1032 | .name = MODULE_NAME, | ||
1033 | .owner = THIS_MODULE, | ||
1034 | }, | ||
1035 | }; | ||
1036 | |||
1037 | static int __init msm_smd_init(void) | ||
1038 | { | ||
1039 | return platform_driver_register(&msm_smd_driver); | ||
1040 | } | ||
1041 | |||
1042 | module_init(msm_smd_init); | ||
1043 | |||
1044 | MODULE_DESCRIPTION("MSM Shared Memory Core"); | ||
1045 | MODULE_AUTHOR("Brian Swetland <swetland@google.com>"); | ||
1046 | MODULE_LICENSE("GPL"); | ||