blob: 806600582b9813fa442fd9f12cfe88ff70cb6076 [file] [log] [blame]
Eric Holmberg6275b602012-11-19 13:05:04 -07001/* arch/arm/mach-msm/smp2p.c
2 *
3 * Copyright (c) 2013, The Linux Foundation. All rights reserved.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 and
7 * only version 2 as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14#include <linux/list.h>
15#include <linux/ctype.h>
16#include <linux/slab.h>
17#include <linux/module.h>
18#include <linux/io.h>
19#include <linux/of.h>
20#include <linux/platform_device.h>
21#include <linux/interrupt.h>
22#include <mach/msm_smsm.h>
23#include <mach/msm_ipc_logging.h>
24#include "smp2p_private_api.h"
25#include "smp2p_private.h"
26
27#define NUM_LOG_PAGES 3
28
29/**
30 * struct msm_smp2p_out - This structure represents the outbound SMP2P entry.
31 *
32 * @remote_pid: Outbound processor ID.
33 * @name: Entry name.
34 * @out_edge_list: Adds this structure into smp2p_out_list_item::list.
35 * @msm_smp2p_notifier_list: Notifier block head used to notify for open event.
36 * @open_nb: Notifier block used to notify for open event.
37 * @l_smp2p_entry: Pointer to the actual entry in the SMEM item.
38 */
39struct msm_smp2p_out {
40 int remote_pid;
41 char name[SMP2P_MAX_ENTRY_NAME];
42 struct list_head out_edge_list;
43 struct raw_notifier_head msm_smp2p_notifier_list;
44 struct notifier_block *open_nb;
Eric Holmberg02c6f702013-01-24 18:32:49 -070045 uint32_t __iomem *l_smp2p_entry;
Eric Holmberg6275b602012-11-19 13:05:04 -070046};
47
48/**
49 * struct smp2p_out_list_item - Maintains the state of outbound edge.
50 *
51 * @out_item_lock_lha1: Lock protecting all elements of the structure.
52 * @list: list of outbound entries (struct msm_smp2p_out).
53 * @smem_edge_out: Pointer to outbound smem item.
54 * @smem_edge_state: State of the outbound edge.
55 * @ops_ptr: Pointer to internal version-specific SMEM item access functions.
56 */
57struct smp2p_out_list_item {
58 spinlock_t out_item_lock_lha1;
59
60 struct list_head list;
Eric Holmberg02c6f702013-01-24 18:32:49 -070061 struct smp2p_smem __iomem *smem_edge_out;
Eric Holmberg6275b602012-11-19 13:05:04 -070062 enum msm_smp2p_edge_state smem_edge_state;
63 struct smp2p_version_if *ops_ptr;
64};
65static struct smp2p_out_list_item out_list[SMP2P_NUM_PROCS];
66
67static void *log_ctx;
68static int smp2p_debug_mask = MSM_SMP2P_INFO;
69module_param_named(debug_mask, smp2p_debug_mask,
70 int, S_IRUGO | S_IWUSR | S_IWGRP);
71
72/**
73 * struct smp2p_in - Represents the entry on remote processor.
74 *
75 * @name: Name of the entry.
76 * @remote_pid: Outbound processor ID.
77 * @in_edge_list: Adds this structure into smp2p_in_list_item::list.
78 * @in_notifier_list: List for notifier block for entry opening/updates.
Eric Holmberg5b6219b2013-01-24 19:17:45 -070079 * @prev_entry_val: Previous value of the entry.
Eric Holmberg6275b602012-11-19 13:05:04 -070080 * @entry_ptr: Points to the current value in smem item.
81 * @notifier_count: Counts the number of notifier registered per pid,entry.
82 */
83struct smp2p_in {
84 int remote_pid;
85 char name[SMP2P_MAX_ENTRY_NAME];
86 struct list_head in_edge_list;
87 struct raw_notifier_head in_notifier_list;
Eric Holmberg5b6219b2013-01-24 19:17:45 -070088 uint32_t prev_entry_val;
Eric Holmberg02c6f702013-01-24 18:32:49 -070089 uint32_t __iomem *entry_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -070090 uint32_t notifier_count;
91};
92
93/**
94 * struct smp2p_in_list_item - Maintains the inbound edge state.
95 *
96 * @in_item_lock_lhb1: Lock protecting all elements of the structure.
97 * @list: List head for the entries on remote processor.
98 * @smem_edge_in: Pointer to the remote smem item.
99 */
100struct smp2p_in_list_item {
101 spinlock_t in_item_lock_lhb1;
102 struct list_head list;
Eric Holmberg02c6f702013-01-24 18:32:49 -0700103 struct smp2p_smem __iomem *smem_edge_in;
Eric Holmberg6275b602012-11-19 13:05:04 -0700104 uint32_t item_size;
105 uint32_t safe_total_entries;
106};
107static struct smp2p_in_list_item in_list[SMP2P_NUM_PROCS];
108
109/**
110 * SMEM Item access function interface.
111 *
112 * This interface is used to help isolate the implementation of
113 * the functionality from any changes in the shared data structures
114 * that may happen as versions are changed.
115 *
116 * @is_supported: True if this version is supported by SMP2P
117 * @negotiate_features: Returns (sub)set of supported features
118 * @find_entry: Finds existing / next empty entry
119 * @create_entry: Creates a new entry
120 * @read_entry: Reads the value of an entry
121 * @write_entry: Writes a new value to an entry
122 * @modify_entry: Does a read/modify/write of an entry
123 * validate_size: Verifies the size of the remote SMEM item to ensure that
124 * an invalid item size doesn't result in an out-of-bounds
125 * memory access.
126 */
127struct smp2p_version_if {
128 /* common functions */
129 bool is_supported;
130 uint32_t (*negotiate_features)(uint32_t features);
Eric Holmberg02c6f702013-01-24 18:32:49 -0700131 void (*find_entry)(struct smp2p_smem __iomem *item,
132 uint32_t entries_total, char *name,
133 uint32_t **entry_ptr, int *empty_spot);
Eric Holmberg6275b602012-11-19 13:05:04 -0700134
135 /* outbound entry functions */
136 int (*create_entry)(struct msm_smp2p_out *);
137 int (*read_entry)(struct msm_smp2p_out *, uint32_t *);
138 int (*write_entry)(struct msm_smp2p_out *, uint32_t);
139 int (*modify_entry)(struct msm_smp2p_out *, uint32_t, uint32_t);
140
141 /* inbound entry functions */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700142 struct smp2p_smem __iomem *(*validate_size)(int remote_pid,
143 struct smp2p_smem __iomem *, uint32_t);
Eric Holmberg6275b602012-11-19 13:05:04 -0700144};
145
146static int smp2p_do_negotiation(int remote_pid, struct smp2p_out_list_item *p);
147static void smp2p_send_interrupt(int remote_pid);
148
149/* v0 (uninitialized SMEM item) interface functions */
150static uint32_t smp2p_negotiate_features_v0(uint32_t features);
Eric Holmberg02c6f702013-01-24 18:32:49 -0700151static void smp2p_find_entry_v0(struct smp2p_smem __iomem *item,
Eric Holmberg6275b602012-11-19 13:05:04 -0700152 uint32_t entries_total, char *name, uint32_t **entry_ptr,
153 int *empty_spot);
154static int smp2p_out_create_v0(struct msm_smp2p_out *);
155static int smp2p_out_read_v0(struct msm_smp2p_out *, uint32_t *);
156static int smp2p_out_write_v0(struct msm_smp2p_out *, uint32_t);
157static int smp2p_out_modify_v0(struct msm_smp2p_out *, uint32_t, uint32_t);
Eric Holmberg02c6f702013-01-24 18:32:49 -0700158static struct smp2p_smem __iomem *smp2p_in_validate_size_v0(int remote_pid,
159 struct smp2p_smem __iomem *smem_item, uint32_t size);
Eric Holmberg6275b602012-11-19 13:05:04 -0700160
161/* v1 interface functions */
162static uint32_t smp2p_negotiate_features_v1(uint32_t features);
Eric Holmberg02c6f702013-01-24 18:32:49 -0700163static void smp2p_find_entry_v1(struct smp2p_smem __iomem *item,
Eric Holmberg6275b602012-11-19 13:05:04 -0700164 uint32_t entries_total, char *name, uint32_t **entry_ptr,
165 int *empty_spot);
166static int smp2p_out_create_v1(struct msm_smp2p_out *);
167static int smp2p_out_read_v1(struct msm_smp2p_out *, uint32_t *);
168static int smp2p_out_write_v1(struct msm_smp2p_out *, uint32_t);
169static int smp2p_out_modify_v1(struct msm_smp2p_out *, uint32_t, uint32_t);
Eric Holmberg02c6f702013-01-24 18:32:49 -0700170static struct smp2p_smem __iomem *smp2p_in_validate_size_v1(int remote_pid,
171 struct smp2p_smem __iomem *smem_item, uint32_t size);
Eric Holmberg6275b602012-11-19 13:05:04 -0700172
173/* Version interface functions */
174static struct smp2p_version_if version_if[] = {
175 [0] = {
176 .negotiate_features = smp2p_negotiate_features_v0,
177 .find_entry = smp2p_find_entry_v0,
178 .create_entry = smp2p_out_create_v0,
179 .read_entry = smp2p_out_read_v0,
180 .write_entry = smp2p_out_write_v0,
181 .modify_entry = smp2p_out_modify_v0,
182 .validate_size = smp2p_in_validate_size_v0,
183 },
184 [1] = {
185 .is_supported = true,
186 .negotiate_features = smp2p_negotiate_features_v1,
187 .find_entry = smp2p_find_entry_v1,
188 .create_entry = smp2p_out_create_v1,
189 .read_entry = smp2p_out_read_v1,
190 .write_entry = smp2p_out_write_v1,
191 .modify_entry = smp2p_out_modify_v1,
192 .validate_size = smp2p_in_validate_size_v1,
193 },
194};
195
196/* interrupt configuration (filled by device tree) */
197static struct smp2p_interrupt_config smp2p_int_cfgs[SMP2P_NUM_PROCS] = {
198 [SMP2P_MODEM_PROC].name = "modem",
199 [SMP2P_AUDIO_PROC].name = "lpass",
200 [SMP2P_WIRELESS_PROC].name = "wcnss",
201 [SMP2P_REMOTE_MOCK_PROC].name = "mock",
202};
203
204/**
205 * smp2p_get_log_ctx - Return log context for other SMP2P modules.
206 *
207 * @returns: Log context or NULL if none.
208 */
209void *smp2p_get_log_ctx(void)
210{
211 return log_ctx;
212}
213
214/**
215 * smp2p_get_debug_mask - Return debug mask.
216 *
217 * @returns: Current debug mask.
218 */
219int smp2p_get_debug_mask(void)
220{
221 return smp2p_debug_mask;
222}
223
224/**
225 * smp2p_interrupt_config - Return interrupt configuration.
226 *
227 * @returns interrupt configuration array for usage by debugfs.
228 */
229struct smp2p_interrupt_config *smp2p_get_interrupt_config(void)
230{
231 return smp2p_int_cfgs;
232}
233
234/**
235 * smp2p_pid_to_name - Lookup name for remote pid.
236 *
237 * @returns: name (may be NULL).
238 */
239const char *smp2p_pid_to_name(int remote_pid)
240{
241 if (remote_pid >= SMP2P_NUM_PROCS)
242 return NULL;
243
244 return smp2p_int_cfgs[remote_pid].name;
245}
246
247/**
248 * smp2p_get_in_item - Return pointer to remote smem item.
249 *
250 * @remote_pid: Processor ID of the remote system.
251 * @returns: Pointer to inbound SMEM item
252 *
253 * This is used by debugfs to print the smem items.
254 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700255struct smp2p_smem __iomem *smp2p_get_in_item(int remote_pid)
Eric Holmberg6275b602012-11-19 13:05:04 -0700256{
257 void *ret = NULL;
258 unsigned long flags;
259
260 spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
261 if (remote_pid < SMP2P_NUM_PROCS)
262 ret = in_list[remote_pid].smem_edge_in;
263 spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1,
264 flags);
265
266 return ret;
267}
268
269/**
270 * smp2p_get_out_item - Return pointer to outbound SMEM item.
271 *
272 * @remote_pid: Processor ID of remote system.
273 * @state: Edge state of the outbound SMEM item.
274 * @returns: Pointer to outbound (remote) SMEM item.
275 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700276struct smp2p_smem __iomem *smp2p_get_out_item(int remote_pid, int *state)
Eric Holmberg6275b602012-11-19 13:05:04 -0700277{
278 void *ret = NULL;
279 unsigned long flags;
280
281 spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
282 if (remote_pid < SMP2P_NUM_PROCS) {
283 ret = out_list[remote_pid].smem_edge_out;
284 if (state)
285 *state = out_list[remote_pid].smem_edge_state;
286 }
287 spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1, flags);
288
289 return ret;
290}
291
292/**
293 * smp2p_get_smem_item_id - Return the proper SMEM item ID.
294 *
295 * @write_id: Processor that will write to the item.
296 * @read_id: Processor that will read from the item.
297 * @returns: SMEM ID
298 */
299static int smp2p_get_smem_item_id(int write_pid, int read_pid)
300{
301 int ret = -EINVAL;
302
303 switch (write_pid) {
304 case SMP2P_APPS_PROC:
305 ret = SMEM_SMP2P_APPS_BASE + read_pid;
306 break;
307 case SMP2P_MODEM_PROC:
308 ret = SMEM_SMP2P_MODEM_BASE + read_pid;
309 break;
310 case SMP2P_AUDIO_PROC:
311 ret = SMEM_SMP2P_AUDIO_BASE + read_pid;
312 break;
313 case SMP2P_WIRELESS_PROC:
314 ret = SMEM_SMP2P_WIRLESS_BASE + read_pid;
315 break;
316 case SMP2P_POWER_PROC:
317 ret = SMEM_SMP2P_POWER_BASE + read_pid;
318 break;
319 }
320
321 return ret;
322}
323
324/**
325 * Return pointer to SMEM item owned by the local processor.
326 *
327 * @remote_pid: Remote processor ID
328 * @returns: NULL for failure; otherwise pointer to SMEM item
329 *
330 * Must be called with out_item_lock_lha1 locked for mock proc.
331 */
332static void *smp2p_get_local_smem_item(int remote_pid)
333{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700334 struct smp2p_smem __iomem *item_ptr = NULL;
Eric Holmberg6275b602012-11-19 13:05:04 -0700335
336 if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
337 unsigned size;
338 int smem_id;
339
340 /* lookup or allocate SMEM item */
341 smem_id = smp2p_get_smem_item_id(SMP2P_APPS_PROC, remote_pid);
342 if (smem_id >= 0) {
343 item_ptr = smem_get_entry(smem_id, &size);
344
345 if (!item_ptr) {
346 size = sizeof(struct smp2p_smem_item);
347 item_ptr = smem_alloc2(smem_id, size);
348 }
349 }
350 } else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
351 /*
352 * This path is only used during unit testing so
353 * the GFP_ATOMIC allocation should not be a
354 * concern.
355 */
356 if (!out_list[SMP2P_REMOTE_MOCK_PROC].smem_edge_out)
357 item_ptr = kzalloc(
358 sizeof(struct smp2p_smem_item),
359 GFP_ATOMIC);
360 }
361 return item_ptr;
362}
363
364/**
365 * smp2p_get_remote_smem_item - Return remote SMEM item.
366 *
367 * @remote_pid: Remote processor ID
368 * @out_item: Pointer to the output item structure
369 * @returns: NULL for failure; otherwise pointer to SMEM item
370 *
371 * Return pointer to SMEM item owned by the remote processor.
372 *
373 * Note that this function does an SMEM lookup which uses a remote spinlock,
374 * so this function should not be called more than necessary.
375 *
376 * Must be called with out_item_lock_lha1 and in_item_lock_lhb1 locked.
377 */
378static void *smp2p_get_remote_smem_item(int remote_pid,
379 struct smp2p_out_list_item *out_item)
380{
381 void *item_ptr = NULL;
382 unsigned size;
383
384 if (!out_item)
385 return item_ptr;
386
387 if (remote_pid < SMP2P_REMOTE_MOCK_PROC) {
388 int smem_id;
389
390 smem_id = smp2p_get_smem_item_id(remote_pid, SMP2P_APPS_PROC);
391 if (smem_id >= 0)
392 item_ptr = smem_get_entry(smem_id, &size);
393 } else if (remote_pid == SMP2P_REMOTE_MOCK_PROC) {
394 item_ptr = msm_smp2p_get_remote_mock_smem_item(&size);
395 }
396 item_ptr = out_item->ops_ptr->validate_size(remote_pid, item_ptr, size);
397
398 return item_ptr;
399}
400
401/**
402 * smp2p_negotiate_features_v0 - Initial feature negotiation.
403 *
404 * @features: Inbound feature set.
405 * @returns: Supported features (will be a same/subset of @features).
406 */
407static uint32_t smp2p_negotiate_features_v1(uint32_t features)
408{
409 /* no supported features */
410 return 0;
411}
412
413/**
414 * smp2p_find_entry_v1 - Search for an entry in SMEM item.
415 *
416 * @item: Pointer to the smem item.
417 * @entries_total: Total number of entries in @item.
418 * @name: Name of the entry.
419 * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
420 * @empty_spot: If non-null, set to the value of the next empty entry.
421 *
422 * Searches for entry @name in the SMEM item. If found, a pointer
423 * to the item is returned. If it isn't found, the first empty
424 * index is returned in @empty_spot.
425 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700426static void smp2p_find_entry_v1(struct smp2p_smem __iomem *item,
Eric Holmberg6275b602012-11-19 13:05:04 -0700427 uint32_t entries_total, char *name, uint32_t **entry_ptr,
428 int *empty_spot)
429{
430 int i;
431 struct smp2p_entry_v1 *pos;
432
433 if (!item || !name || !entry_ptr) {
434 SMP2P_ERR("%s: invalid arguments %p, %p, %p\n",
435 __func__, item, name, entry_ptr);
436 return;
437 }
438
439 *entry_ptr = NULL;
440 if (empty_spot)
441 *empty_spot = -1;
442
443 pos = (struct smp2p_entry_v1 *)(char *)(item + 1);
444 for (i = 0; i < entries_total; i++, ++pos) {
445 if (pos->name[0]) {
446 if (!strncmp(pos->name, name, SMP2P_MAX_ENTRY_NAME)) {
447 *entry_ptr = &pos->entry;
448 break;
449 }
450 } else if (empty_spot && *empty_spot < 0) {
451 *empty_spot = i;
452 }
453 }
454}
455
456/**
457 * smp2p_out_create_v1 - Creates a outbound SMP2P entry.
458 *
459 * @out_entry: Pointer to the SMP2P entry structure.
460 * @returns: 0 on success, standard Linux error code otherwise.
461 *
462 * Must be called with out_item_lock_lha1 locked.
463 */
464static int smp2p_out_create_v1(struct msm_smp2p_out *out_entry)
465{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700466 struct smp2p_smem __iomem *smp2p_h_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700467 struct smp2p_out_list_item *p_list;
468 uint32_t *state_entry_ptr;
469 uint32_t empty_spot;
470 uint32_t entries_total;
471 uint32_t entries_valid;
472
473 if (!out_entry)
474 return -EINVAL;
475
476 p_list = &out_list[out_entry->remote_pid];
477 if (p_list->smem_edge_state != SMP2P_EDGE_STATE_OPENED) {
478 SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
479 __func__, out_entry->name, out_entry->remote_pid);
480 return -ENODEV;
481 }
482
483 smp2p_h_ptr = p_list->smem_edge_out;
484 entries_total = SMP2P_GET_ENT_TOTAL(smp2p_h_ptr->valid_total_ent);
485 entries_valid = SMP2P_GET_ENT_VALID(smp2p_h_ptr->valid_total_ent);
486
487 p_list->ops_ptr->find_entry(smp2p_h_ptr, entries_total,
488 out_entry->name, &state_entry_ptr, &empty_spot);
489 if (state_entry_ptr) {
490 /* re-use existing entry */
491 out_entry->l_smp2p_entry = state_entry_ptr;
492
493 SMP2P_DBG("%s: item '%s':%d reused\n", __func__,
494 out_entry->name, out_entry->remote_pid);
495 } else if (entries_valid >= entries_total) {
496 /* need to allocate entry, but not more space */
497 SMP2P_ERR("%s: no space for item '%s':%d\n",
498 __func__, out_entry->name, out_entry->remote_pid);
499 return -ENOMEM;
500 } else {
501 /* allocate a new entry */
502 struct smp2p_entry_v1 *entry_ptr;
503
504 entry_ptr = (struct smp2p_entry_v1 *)((char *)(smp2p_h_ptr + 1)
505 + empty_spot * sizeof(struct smp2p_entry_v1));
506 strlcpy(entry_ptr->name, out_entry->name,
507 sizeof(entry_ptr->name));
508 out_entry->l_smp2p_entry = &entry_ptr->entry;
509 ++entries_valid;
510 SMP2P_DBG("%s: item '%s':%d fully created as entry %d of %d\n",
511 __func__, out_entry->name,
512 out_entry->remote_pid,
513 entries_valid, entries_total);
514 SMP2P_SET_ENT_VALID(smp2p_h_ptr->valid_total_ent,
515 entries_valid);
516 smp2p_send_interrupt(out_entry->remote_pid);
517 }
518 raw_notifier_call_chain(&out_entry->msm_smp2p_notifier_list,
519 SMP2P_OPEN, 0);
520
521 return 0;
522}
523
524/**
525 * smp2p_out_read_v1 - Read the data from an outbound entry.
526 *
527 * @out_entry: Pointer to the SMP2P entry structure.
528 * @data: Out pointer, the data is available in this argument on success.
529 * @returns: 0 on success, standard Linux error code otherwise.
530 *
531 * Must be called with out_item_lock_lha1 locked.
532 */
533static int smp2p_out_read_v1(struct msm_smp2p_out *out_entry, uint32_t *data)
534{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700535 struct smp2p_smem __iomem *smp2p_h_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700536 uint32_t remote_pid;
537
538 if (!out_entry)
539 return -EINVAL;
540
541 smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
542 remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
543
544 if (remote_pid != out_entry->remote_pid)
545 return -EINVAL;
546
547 if (out_entry->l_smp2p_entry) {
548 *data = readl_relaxed(out_entry->l_smp2p_entry);
549 } else {
550 SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
551 out_entry->name, remote_pid);
552 return -ENODEV;
553 }
554
555 return 0;
556}
557
558/**
559 * smp2p_out_write_v1 - Writes an outbound entry value.
560 *
561 * @out_entry: Pointer to the SMP2P entry structure.
562 * @data: The data to be written.
563 * @returns: 0 on success, standard Linux error code otherwise.
564 *
565 * Must be called with out_item_lock_lha1 locked.
566 */
567static int smp2p_out_write_v1(struct msm_smp2p_out *out_entry, uint32_t data)
568{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700569 struct smp2p_smem __iomem *smp2p_h_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700570 uint32_t remote_pid;
571
572 if (!out_entry)
573 return -EINVAL;
574
575 smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
576 remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
577
578 if (remote_pid != out_entry->remote_pid)
579 return -EINVAL;
580
581 if (out_entry->l_smp2p_entry) {
582 writel_relaxed(data, out_entry->l_smp2p_entry);
583 smp2p_send_interrupt(remote_pid);
584 } else {
585 SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
586 out_entry->name, remote_pid);
587 return -ENODEV;
588 }
589 return 0;
590}
591
592/**
593 * smp2p_out_modify_v1 - Modifies and outbound value.
594 *
595 * @set_mask: Mask containing the bits that needs to be set.
596 * @clear_mask: Mask containing the bits that needs to be cleared.
597 * @returns: 0 on success, standard Linux error code otherwise.
598 *
599 * The clear mask is applied first, so if a bit is set in both clear and
600 * set mask, the result will be that the bit is set.
601 *
602 * Must be called with out_item_lock_lha1 locked.
603 */
604static int smp2p_out_modify_v1(struct msm_smp2p_out *out_entry,
605 uint32_t set_mask, uint32_t clear_mask)
606{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700607 struct smp2p_smem __iomem *smp2p_h_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700608 uint32_t remote_pid;
609
610 if (!out_entry)
611 return -EINVAL;
612
613 smp2p_h_ptr = out_list[out_entry->remote_pid].smem_edge_out;
614 remote_pid = SMP2P_GET_REMOTE_PID(smp2p_h_ptr->rem_loc_proc_id);
615
616 if (remote_pid != out_entry->remote_pid)
617 return -EINVAL;
618
619 if (out_entry->l_smp2p_entry) {
620 uint32_t curr_value;
621
622 curr_value = readl_relaxed(out_entry->l_smp2p_entry);
623 writel_relaxed((curr_value & ~clear_mask) | set_mask,
624 out_entry->l_smp2p_entry);
625 } else {
626 SMP2P_ERR("%s: '%s':%d not yet OPEN\n", __func__,
627 out_entry->name, remote_pid);
628 return -ENODEV;
629 }
630
631 smp2p_send_interrupt(remote_pid);
632 return 0;
633}
634
635/**
636 * smp2p_in_validate_size_v1 - Size validation for version 1.
637 *
638 * @remote_pid: Remote processor ID.
639 * @smem_item: Pointer to the inbound SMEM item.
640 * @size: Size of the SMEM item.
641 * @returns: Validated smem_item pointer (or NULL if size is too small).
642 *
643 * Validates we don't end up with out-of-bounds array access due to invalid
644 * smem item size. If out-of-bound array access can't be avoided, then an
645 * error message is printed and NULL is returned to prevent usage of the
646 * item.
647 *
648 * Must be called with in_item_lock_lhb1 locked.
649 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700650static struct smp2p_smem __iomem *smp2p_in_validate_size_v1(int remote_pid,
651 struct smp2p_smem __iomem *smem_item, uint32_t size)
Eric Holmberg6275b602012-11-19 13:05:04 -0700652{
653 uint32_t total_entries;
654 unsigned expected_size;
Eric Holmberg02c6f702013-01-24 18:32:49 -0700655 struct smp2p_smem __iomem *item_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700656 struct smp2p_in_list_item *in_item;
657
658 if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
659 return NULL;
660
661 in_item = &in_list[remote_pid];
Eric Holmberg02c6f702013-01-24 18:32:49 -0700662 item_ptr = (struct smp2p_smem __iomem *)smem_item;
Eric Holmberg6275b602012-11-19 13:05:04 -0700663
664 total_entries = SMP2P_GET_ENT_TOTAL(item_ptr->valid_total_ent);
665 if (total_entries > 0) {
666 in_item->safe_total_entries = total_entries;
667 in_item->item_size = size;
668
669 expected_size = sizeof(struct smp2p_smem) +
670 (total_entries * sizeof(struct smp2p_entry_v1));
671
672 if (size < expected_size) {
673 unsigned new_size;
674
675 new_size = size;
676 new_size -= sizeof(struct smp2p_smem);
677 new_size /= sizeof(struct smp2p_entry_v1);
678 in_item->safe_total_entries = new_size;
679
680 SMP2P_ERR(
681 "%s pid %d item too small for %d entries; expected: %d actual: %d; reduced to %d entries\n",
682 __func__, remote_pid, total_entries,
683 expected_size, size, new_size);
684 }
685 } else {
686 /*
687 * Total entries is 0, so the entry is still being initialized
688 * or is invalid. Either way, treat it as if the item does
689 * not exist yet.
690 */
691 in_item->safe_total_entries = 0;
692 in_item->item_size = 0;
693 }
694 return item_ptr;
695}
696
697/**
698 * smp2p_negotiate_features_v0 - Initial feature negotiation.
699 *
700 * @features: Inbound feature set.
701 * @returns: 0 (no features supported for v0).
702 */
703static uint32_t smp2p_negotiate_features_v0(uint32_t features)
704{
705 /* no supported features */
706 return 0;
707}
708
709/**
710 * smp2p_find_entry_v0 - Stub function.
711 *
712 * @item: Pointer to the smem item.
713 * @entries_total: Total number of entries in @item.
714 * @name: Name of the entry.
715 * @entry_ptr: Set to pointer of entry if found, NULL otherwise.
716 * @empty_spot: If non-null, set to the value of the next empty entry.
717 *
718 * Entries cannot be searched for until item negotiation has been completed.
719 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700720static void smp2p_find_entry_v0(struct smp2p_smem __iomem *item,
Eric Holmberg6275b602012-11-19 13:05:04 -0700721 uint32_t entries_total, char *name, uint32_t **entry_ptr,
722 int *empty_spot)
723{
724 if (entry_ptr)
725 *entry_ptr = NULL;
726
727 if (empty_spot)
728 *empty_spot = -1;
729
730 SMP2P_ERR("%s: invalid - item negotiation incomplete\n", __func__);
731}
732
733/**
734 * smp2p_out_create_v0 - Initial creation function.
735 *
736 * @out_entry: Pointer to the SMP2P entry structure.
737 * @returns: 0 on success, standard Linux error code otherwise.
738 *
739 * If the outbound SMEM item negotiation is not complete, then
740 * this function is called to start the negotiation process.
741 * Eventually when the negotiation process is complete, this
742 * function pointer is switched with the appropriate function
743 * for the version of SMP2P being created.
744 *
745 * Must be called with out_item_lock_lha1 locked.
746 */
747static int smp2p_out_create_v0(struct msm_smp2p_out *out_entry)
748{
749 int edge_state;
750 struct smp2p_out_list_item *item_ptr;
751
752 if (!out_entry)
753 return -EINVAL;
754
755 edge_state = out_list[out_entry->remote_pid].smem_edge_state;
756
757 switch (edge_state) {
758 case SMP2P_EDGE_STATE_CLOSED:
759 /* start negotiation */
760 item_ptr = &out_list[out_entry->remote_pid];
761 edge_state = smp2p_do_negotiation(out_entry->remote_pid,
762 item_ptr);
763 break;
764
765 case SMP2P_EDGE_STATE_OPENING:
766 /* still negotiating */
767 break;
768
769 case SMP2P_EDGE_STATE_OPENED:
770 SMP2P_ERR("%s: item '%s':%d opened - wrong create called\n",
771 __func__, out_entry->name, out_entry->remote_pid);
772 break;
773
774 default:
775 SMP2P_ERR("%s: item '%s':%d invalid SMEM item state %d\n",
776 __func__, out_entry->name, out_entry->remote_pid,
777 edge_state);
778 break;
779 }
780 return 0;
781}
782
783/**
784 * smp2p_out_read_v0 - Stub function.
785 *
786 * @out_entry: Pointer to the SMP2P entry structure.
787 * @data: Out pointer, the data is available in this argument on success.
788 * @returns: -ENODEV
789 */
790static int smp2p_out_read_v0(struct msm_smp2p_out *out_entry, uint32_t *data)
791{
792 SMP2P_ERR("%s: item '%s':%d not OPEN\n",
793 __func__, out_entry->name, out_entry->remote_pid);
794
795 return -ENODEV;
796}
797
798/**
799 * smp2p_out_write_v0 - Stub function.
800 *
801 * @out_entry: Pointer to the SMP2P entry structure.
802 * @data: The data to be written.
803 * @returns: -ENODEV
804 */
805static int smp2p_out_write_v0(struct msm_smp2p_out *out_entry, uint32_t data)
806{
807 SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
808 __func__, out_entry->name, out_entry->remote_pid);
809
810 return -ENODEV;
811}
812
813/**
814 * smp2p_out_modify_v0 - Stub function.
815 *
816 * @set_mask: Mask containing the bits that needs to be set.
817 * @clear_mask: Mask containing the bits that needs to be cleared.
818 * @returns: -ENODEV
819 */
820static int smp2p_out_modify_v0(struct msm_smp2p_out *out_entry,
821 uint32_t set_mask, uint32_t clear_mask)
822{
823 SMP2P_ERR("%s: item '%s':%d not yet OPEN\n",
824 __func__, out_entry->name, out_entry->remote_pid);
825
826 return -ENODEV;
827}
828
829/**
830 * smp2p_in_validate_size_v0 - Stub function.
831 *
832 * @remote_pid: Remote processor ID.
833 * @smem_item: Pointer to the inbound SMEM item.
834 * @size: Size of the SMEM item.
835 * @returns: Validated smem_item pointer (or NULL if size is too small).
836 *
837 * Validates we don't end up with out-of-bounds array access due to invalid
838 * smem item size. If out-of-bound array access can't be avoided, then an
839 * error message is printed and NULL is returned to prevent usage of the
840 * item.
841 *
842 * Must be called with in_item_lock_lhb1 locked.
843 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700844static struct smp2p_smem __iomem *smp2p_in_validate_size_v0(int remote_pid,
845 struct smp2p_smem __iomem *smem_item, uint32_t size)
Eric Holmberg6275b602012-11-19 13:05:04 -0700846{
847 struct smp2p_in_list_item *in_item;
848
849 if (remote_pid >= SMP2P_NUM_PROCS || !smem_item)
850 return NULL;
851
852 in_item = &in_list[remote_pid];
853
854 if (size < sizeof(struct smp2p_smem)) {
855 SMP2P_ERR(
856 "%s pid %d item size too small; expected: %d actual: %d\n",
857 __func__, remote_pid,
858 sizeof(struct smp2p_smem), size);
859 smem_item = NULL;
860 in_item->item_size = 0;
861 } else {
862 in_item->item_size = size;
863 }
864 return smem_item;
865}
866
867/**
868 * smp2p_init_header - Initializes the header of the smem item.
869 *
870 * @header_ptr: Pointer to the smp2p header.
871 * @local_pid: Local processor ID.
872 * @remote_pid: Remote processor ID.
873 * @feature: Features of smp2p implementation.
874 * @version: Version of smp2p implementation.
875 *
876 * Initializes the header as defined in the protocol specification.
877 */
Eric Holmberg02c6f702013-01-24 18:32:49 -0700878void smp2p_init_header(struct smp2p_smem __iomem *header_ptr,
Eric Holmberg6275b602012-11-19 13:05:04 -0700879 int local_pid, int remote_pid,
880 uint32_t features, uint32_t version)
881{
882 header_ptr->magic = SMP2P_MAGIC;
883 SMP2P_SET_LOCAL_PID(header_ptr->rem_loc_proc_id, local_pid);
884 SMP2P_SET_REMOTE_PID(header_ptr->rem_loc_proc_id, remote_pid);
885 SMP2P_SET_FEATURES(header_ptr->feature_version, features);
886 SMP2P_SET_ENT_TOTAL(header_ptr->valid_total_ent, SMP2P_MAX_ENTRY);
887 SMP2P_SET_ENT_VALID(header_ptr->valid_total_ent, 0);
888 header_ptr->reserved = 0;
889
890 /* ensure that all fields are valid before version is written */
891 wmb();
892 SMP2P_SET_VERSION(header_ptr->feature_version, version);
893}
894
895/**
896 * smp2p_do_negotiation - Implements negotiation algorithm.
897 *
898 * @remote_pid: Remote processor ID.
899 * @out_item: Pointer to the outbound list item.
900 * @returns: 0 on success, standard Linux error code otherwise.
901 *
902 * Must be called with out_item_lock_lha1 locked. Will internally lock
903 * in_item_lock_lhb1.
904 */
905static int smp2p_do_negotiation(int remote_pid,
906 struct smp2p_out_list_item *out_item)
907{
Eric Holmberg02c6f702013-01-24 18:32:49 -0700908 struct smp2p_smem __iomem *r_smem_ptr;
909 struct smp2p_smem __iomem *l_smem_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -0700910 uint32_t r_version;
911 uint32_t r_feature;
912 uint32_t l_version, l_feature;
913 int prev_state;
914
915 if (remote_pid >= SMP2P_NUM_PROCS || !out_item)
916 return -EINVAL;
917 if (out_item->smem_edge_state == SMP2P_EDGE_STATE_FAILED)
918 return -EPERM;
919
920 prev_state = out_item->smem_edge_state;
921
922 /* create local item */
923 if (!out_item->smem_edge_out) {
924 out_item->smem_edge_out = smp2p_get_local_smem_item(remote_pid);
925 if (!out_item->smem_edge_out) {
926 SMP2P_ERR(
927 "%s unable to allocate SMEM item for pid %d\n",
928 __func__, remote_pid);
929 return -ENODEV;
930 }
931 out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENING;
932 }
933 l_smem_ptr = out_item->smem_edge_out;
934
935 /* retrieve remote side and version */
936 spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
937 r_smem_ptr = smp2p_get_remote_smem_item(remote_pid, out_item);
938 spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
939
940 r_version = 0;
941 if (r_smem_ptr) {
942 r_version = SMP2P_GET_VERSION(r_smem_ptr->feature_version);
943 r_feature = SMP2P_GET_FEATURES(r_smem_ptr->feature_version);
944 }
945
946 if (r_version == 0) {
947 /*
948 * Either remote side doesn't exist, or is in the
949 * process of being initialized (the version is set last).
950 *
951 * In either case, treat as if the other side doesn't exist
952 * and write out our maximum supported version.
953 */
954 r_smem_ptr = NULL;
955 r_version = ARRAY_SIZE(version_if) - 1;
956 r_feature = ~0U;
957 }
958
959 /* find maximum supported version and feature set */
960 l_version = min(r_version, ARRAY_SIZE(version_if) - 1);
961 for (; l_version > 0; --l_version) {
962 if (!version_if[l_version].is_supported)
963 continue;
964
965 /* found valid version */
966 l_feature = version_if[l_version].negotiate_features(~0U);
967 if (l_version == r_version)
968 l_feature &= r_feature;
969 break;
970 }
971
972 if (l_version == 0) {
973 SMP2P_ERR(
974 "%s: negotiation failure pid %d: RV %d RF %x\n",
975 __func__, remote_pid, r_version, r_feature
976 );
977 SMP2P_SET_VERSION(l_smem_ptr->feature_version,
978 SMP2P_EDGE_STATE_FAILED);
979 smp2p_send_interrupt(remote_pid);
980 out_item->smem_edge_state = SMP2P_EDGE_STATE_FAILED;
981 return -EPERM;
982 }
983
984 /* update header and notify remote side */
985 smp2p_init_header(l_smem_ptr, SMP2P_APPS_PROC, remote_pid,
986 l_feature, l_version);
987 smp2p_send_interrupt(remote_pid);
988
989 /* handle internal state changes */
990 if (r_smem_ptr && l_version == r_version &&
991 l_feature == r_feature) {
992 struct msm_smp2p_out *pos;
993
994 /* negotiation complete */
995 out_item->smem_edge_state = SMP2P_EDGE_STATE_OPENED;
996 out_item->ops_ptr = &version_if[l_version];
997 SMP2P_INFO(
998 "%s: negotiation complete pid %d: State %d->%d F0x%08x\n",
999 __func__, remote_pid, prev_state,
1000 out_item->smem_edge_state, l_feature);
1001
1002 /* create any pending outbound entries */
1003 list_for_each_entry(pos, &out_item->list, out_edge_list) {
1004 out_item->ops_ptr->create_entry(pos);
1005 }
1006
1007 /* update inbound edge */
1008 spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
1009 (void)out_item->ops_ptr->validate_size(remote_pid, r_smem_ptr,
1010 in_list[remote_pid].item_size);
1011 in_list[remote_pid].smem_edge_in = r_smem_ptr;
1012 spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
1013 } else {
1014 SMP2P_INFO("%s: negotiation pid %d: State %d->%d F0x%08x\n",
1015 __func__, remote_pid, prev_state,
1016 out_item->smem_edge_state, l_feature);
1017 }
1018 return 0;
1019}
1020
1021/**
1022 * msm_smp2p_out_open - Opens an outbound entry.
1023 *
1024 * @remote_pid: Outbound processor ID.
1025 * @name: Name of the entry.
1026 * @open_notifier: Notifier block for the open notification.
1027 * @handle: Handle to the smem entry structure.
1028 * @returns: 0 on success, standard Linux error code otherwise.
1029 *
1030 * Opens an outbound entry with the name specified by entry, from the
1031 * local processor to the remote processor(remote_pid). If the entry, remote_pid
1032 * and open_notifier are valid, then handle will be set and zero will be
1033 * returned. The smem item that holds this entry will be created if it has
1034 * not been created according to the version negotiation algorithm.
1035 * The open_notifier will be used to notify the clients about the
1036 * availability of the entry.
1037 */
1038int msm_smp2p_out_open(int remote_pid, const char *name,
1039 struct notifier_block *open_notifier,
1040 struct msm_smp2p_out **handle)
1041{
1042 struct msm_smp2p_out *out_entry;
1043 struct msm_smp2p_out *pos;
1044 int ret = 0;
1045 unsigned long flags;
1046
1047 if (handle)
1048 *handle = NULL;
1049
1050 if (remote_pid >= SMP2P_NUM_PROCS || !name || !open_notifier || !handle)
1051 return -EINVAL;
1052
1053 /* Allocate the smp2p object and node */
1054 out_entry = kzalloc(sizeof(*out_entry), GFP_KERNEL);
1055 if (!out_entry)
1056 return -ENOMEM;
1057
1058 /* Handle duplicate registration */
1059 spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
1060 list_for_each_entry(pos, &out_list[remote_pid].list,
1061 out_edge_list) {
1062 if (!strcmp(pos->name, name)) {
1063 spin_unlock_irqrestore(
1064 &out_list[remote_pid].out_item_lock_lha1,
1065 flags);
1066 kfree(out_entry);
1067 SMP2P_ERR("%s: duplicate registration '%s':%d\n",
1068 __func__, name, remote_pid);
1069 return -EBUSY;
1070 }
1071 }
1072
1073 out_entry->remote_pid = remote_pid;
1074 RAW_INIT_NOTIFIER_HEAD(&out_entry->msm_smp2p_notifier_list);
1075 strlcpy(out_entry->name, name, SMP2P_MAX_ENTRY_NAME);
1076 out_entry->open_nb = open_notifier;
1077 raw_notifier_chain_register(&out_entry->msm_smp2p_notifier_list,
1078 out_entry->open_nb);
1079 list_add(&out_entry->out_edge_list, &out_list[remote_pid].list);
1080
1081 ret = out_list[remote_pid].ops_ptr->create_entry(out_entry);
1082 if (ret) {
1083 list_del(&out_entry->out_edge_list);
1084 raw_notifier_chain_unregister(
1085 &out_entry->msm_smp2p_notifier_list,
1086 out_entry->open_nb);
1087 spin_unlock_irqrestore(
1088 &out_list[remote_pid].out_item_lock_lha1, flags);
1089 kfree(out_entry);
1090 SMP2P_ERR("%s: unable to open '%s':%d error %d\n",
1091 __func__, name, remote_pid, ret);
1092 return ret;
1093 }
1094 spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
1095 flags);
1096 *handle = out_entry;
1097
1098 return 0;
1099}
1100EXPORT_SYMBOL(msm_smp2p_out_open);
1101
1102/**
1103 * msm_smp2p_out_close - Closes the handle to an outbound entry.
1104 *
1105 * @handle: Pointer to smp2p out entry handle.
1106 * @returns: 0 on success, standard Linux error code otherwise.
1107 *
1108 * The actual entry will not be deleted and can be re-opened at a later
1109 * time. The handle will be set to NULL.
1110 */
1111int msm_smp2p_out_close(struct msm_smp2p_out **handle)
1112{
1113 unsigned long flags;
1114 struct msm_smp2p_out *out_entry;
1115 struct smp2p_out_list_item *out_item;
1116
1117 if (!handle || !*handle)
1118 return -EINVAL;
1119
1120 out_entry = *handle;
1121 *handle = NULL;
1122
1123 out_item = &out_list[out_entry->remote_pid];
1124 spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
1125 list_del(&out_entry->out_edge_list);
1126 raw_notifier_chain_unregister(&out_entry->msm_smp2p_notifier_list,
1127 out_entry->open_nb);
1128 spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
1129
1130 kfree(out_entry);
1131
1132 return 0;
1133}
1134EXPORT_SYMBOL(msm_smp2p_out_close);
1135
1136/**
1137 * msm_smp2p_out_read - Allows reading the entry.
1138 *
1139 * @handle: Handle to the smem entry structure.
1140 * @data: Out pointer that holds the read data.
1141 * @returns: 0 on success, standard Linux error code otherwise.
1142 *
1143 * Allows reading of the outbound entry for read-modify-write
1144 * operation.
1145 */
1146int msm_smp2p_out_read(struct msm_smp2p_out *handle, uint32_t *data)
1147{
1148 int ret = -EINVAL;
1149 unsigned long flags;
1150 struct smp2p_out_list_item *out_item;
1151
1152 if (!handle || !data)
1153 return ret;
1154
1155 out_item = &out_list[handle->remote_pid];
1156 spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
1157 ret = out_item->ops_ptr->read_entry(handle, data);
1158 spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
1159
1160 return ret;
1161}
1162EXPORT_SYMBOL(msm_smp2p_out_read);
1163
1164/**
1165 * msm_smp2p_out_write - Allows writing to the entry.
1166 *
1167 * @handle: Handle to smem entry structure.
1168 * @data: Data that has to be written.
1169 * @returns: 0 on success, standard Linux error code otherwise.
1170 *
1171 * Writes a new value to the output entry. Multiple back-to-back writes
1172 * may overwrite previous writes before the remote processor get a chance
1173 * to see them leading to ABA race condition. The client must implement
1174 * their own synchronization mechanism (such as echo mechanism) if this is
1175 * not acceptable.
1176 */
1177int msm_smp2p_out_write(struct msm_smp2p_out *handle, uint32_t data)
1178{
1179 int ret = -EINVAL;
1180 unsigned long flags;
1181 struct smp2p_out_list_item *out_item;
1182
1183 if (!handle)
1184 return ret;
1185
1186 out_item = &out_list[handle->remote_pid];
1187 spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
1188 ret = out_item->ops_ptr->write_entry(handle, data);
1189 spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
1190
1191 return ret;
1192
1193}
1194EXPORT_SYMBOL(msm_smp2p_out_write);
1195
1196/**
1197 * msm_smp2p_out_modify - Modifies the entry.
1198 *
1199 * @handle: Handle to the smem entry structure.
1200 * @set_mask: Specifies the bits that needs to be set.
1201 * @clear_mask: Specifies the bits that needs to be cleared.
1202 * @returns: 0 on success, standard Linux error code otherwise.
1203 *
1204 * The modification is done by doing a bitwise AND of clear mask followed by
1205 * the bit wise OR of set mask. The clear bit mask is applied first to the
1206 * data, so if a bit is set in both the clear mask and the set mask, then in
1207 * the result is a set bit. Multiple back-to-back modifications may overwrite
1208 * previous values before the remote processor gets a chance to see them
1209 * leading to ABA race condition. The client must implement their own
1210 * synchronization mechanism (such as echo mechanism) if this is not
1211 * acceptable.
1212 */
1213int msm_smp2p_out_modify(struct msm_smp2p_out *handle, uint32_t set_mask,
1214 uint32_t clear_mask)
1215{
1216 int ret = -EINVAL;
1217 unsigned long flags;
1218 struct smp2p_out_list_item *out_item;
1219
1220 if (!handle)
1221 return ret;
1222
1223 out_item = &out_list[handle->remote_pid];
1224 spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
1225 ret = out_item->ops_ptr->modify_entry(handle, set_mask, clear_mask);
1226 spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
1227
1228 return ret;
1229}
1230EXPORT_SYMBOL(msm_smp2p_out_modify);
1231
1232/**
1233 * msm_smp2p_in_read - Read an entry on a remote processor.
1234 *
1235 * @remote_pid: Processor ID of the remote processor.
1236 * @name: Name of the entry that is to be read.
1237 * @data: Output pointer, the value will be placed here if successful.
1238 * @returns: 0 on success, standard Linux error code otherwise.
1239 */
1240int msm_smp2p_in_read(int remote_pid, const char *name, uint32_t *data)
1241{
1242 unsigned long flags;
1243 struct smp2p_out_list_item *out_item;
1244 uint32_t *entry_ptr;
1245
1246 if (remote_pid >= SMP2P_NUM_PROCS)
1247 return -EINVAL;
1248
1249 out_item = &out_list[remote_pid];
1250 spin_lock_irqsave(&out_item->out_item_lock_lha1, flags);
1251 spin_lock(&in_list[remote_pid].in_item_lock_lhb1);
1252
1253 if (in_list[remote_pid].smem_edge_in)
1254 out_item->ops_ptr->find_entry(
1255 in_list[remote_pid].smem_edge_in,
1256 in_list[remote_pid].safe_total_entries,
1257 (char *)name, &entry_ptr, NULL);
1258
1259 spin_unlock(&in_list[remote_pid].in_item_lock_lhb1);
1260 spin_unlock_irqrestore(&out_item->out_item_lock_lha1, flags);
1261
1262 if (!entry_ptr)
1263 return -ENODEV;
1264
1265 *data = readl_relaxed(entry_ptr);
1266 return 0;
1267}
1268EXPORT_SYMBOL(msm_smp2p_in_read);
1269
1270/**
1271 * msm_smp2p_in_register - Notifies the change in value of the entry.
1272 *
1273 * @pid: Remote processor ID.
1274 * @name: Name of the entry.
1275 * @in_notifier: Notifier block used to notify about the event.
1276 * @returns: 0 on success, standard Linux error code otherwise.
1277 *
1278 * Register for change notifications for a remote entry. If the remote entry
1279 * does not exist yet, then the registration request will be held until the
1280 * remote side opens. Once the entry is open, then the SMP2P_OPEN notification
1281 * will be sent. Any changes to the entry will trigger a call to the notifier
1282 * block with an SMP2P_ENTRY_UPDATE event and the data field will point to an
1283 * msm_smp2p_update_notif structure containing the current and previous value.
1284 */
1285int msm_smp2p_in_register(int pid, const char *name,
1286 struct notifier_block *in_notifier)
1287{
1288 struct smp2p_in *pos;
1289 struct smp2p_in *in = NULL;
1290 int ret;
1291 unsigned long flags;
1292 struct msm_smp2p_update_notif data;
1293 uint32_t *entry_ptr;
1294
1295 if (pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
1296 return -EINVAL;
1297
1298 /* Pre-allocate before spinlock since we will likely needed it */
1299 in = kzalloc(sizeof(*in), GFP_KERNEL);
1300 if (!in)
1301 return -ENOMEM;
1302
1303 /* Search for existing entry */
1304 spin_lock_irqsave(&out_list[pid].out_item_lock_lha1, flags);
1305 spin_lock(&in_list[pid].in_item_lock_lhb1);
1306
1307 list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
1308 if (!strncmp(pos->name, name,
1309 SMP2P_MAX_ENTRY_NAME)) {
1310 kfree(in);
1311 in = pos;
1312 break;
1313 }
1314 }
1315
1316 /* Create and add it to the list */
Eric Holmberg4e9a83a2013-02-04 16:43:20 -07001317 if (!in->notifier_count) {
1318 in->remote_pid = pid;
1319 strlcpy(in->name, name, SMP2P_MAX_ENTRY_NAME);
1320 RAW_INIT_NOTIFIER_HEAD(&in->in_notifier_list);
1321 list_add(&in->in_edge_list, &in_list[pid].list);
1322 }
1323
Eric Holmberg6275b602012-11-19 13:05:04 -07001324 ret = raw_notifier_chain_register(&in->in_notifier_list,
1325 in_notifier);
1326 if (ret) {
Eric Holmberg4e9a83a2013-02-04 16:43:20 -07001327 if (!in->notifier_count) {
1328 list_del(&in->in_edge_list);
1329 kfree(in);
1330 }
Eric Holmberg6275b602012-11-19 13:05:04 -07001331 SMP2P_DBG("%s: '%s':%d failed %d\n", __func__, name, pid, ret);
1332 goto bail;
1333 }
1334 in->notifier_count++;
1335
1336 if (out_list[pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED) {
1337 out_list[pid].ops_ptr->find_entry(
1338 in_list[pid].smem_edge_in,
1339 in_list[pid].safe_total_entries, (char *)name,
1340 &entry_ptr, NULL);
1341 if (entry_ptr) {
1342 in->entry_ptr = entry_ptr;
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001343 in->prev_entry_val = readl_relaxed(entry_ptr);
Eric Holmberg6275b602012-11-19 13:05:04 -07001344
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001345 data.previous_value = in->prev_entry_val;
1346 data.current_value = in->prev_entry_val;
Eric Holmberg6275b602012-11-19 13:05:04 -07001347 in_notifier->notifier_call(in_notifier, SMP2P_OPEN,
1348 (void *)&data);
1349 }
1350 }
1351 SMP2P_DBG("%s: '%s':%d registered\n", __func__, name, pid);
1352
1353bail:
1354 spin_unlock(&in_list[pid].in_item_lock_lhb1);
1355 spin_unlock_irqrestore(&out_list[pid].out_item_lock_lha1, flags);
1356 return ret;
1357
1358}
1359EXPORT_SYMBOL(msm_smp2p_in_register);
1360
1361/**
1362 * msm_smp2p_in_unregister - Unregister the notifier for remote entry.
1363 *
1364 * @remote_pid: Processor Id of the remote processor.
1365 * @name: The name of the entry.
1366 * @in_notifier: Notifier block passed during registration.
1367 * @returns: 0 on success, standard Linux error code otherwise.
1368 */
1369int msm_smp2p_in_unregister(int remote_pid, const char *name,
1370 struct notifier_block *in_notifier)
1371{
1372 struct smp2p_in *pos;
1373 struct smp2p_in *in = NULL;
1374 int ret = -ENODEV;
1375 unsigned long flags;
1376
1377 if (remote_pid >= SMP2P_NUM_PROCS || !name || !in_notifier)
1378 return -EINVAL;
1379
1380 spin_lock_irqsave(&in_list[remote_pid].in_item_lock_lhb1, flags);
1381 list_for_each_entry(pos, &in_list[remote_pid].list,
1382 in_edge_list) {
1383 if (!strncmp(pos->name, name, SMP2P_MAX_ENTRY_NAME)) {
1384 in = pos;
1385 break;
1386 }
1387 }
1388 if (!in)
1389 goto fail;
1390
1391 ret = raw_notifier_chain_unregister(&pos->in_notifier_list,
1392 in_notifier);
1393 if (ret == 0) {
1394 pos->notifier_count--;
1395 if (!pos->notifier_count) {
1396 list_del(&pos->in_edge_list);
1397 kfree(pos);
1398 ret = 0;
1399 }
1400 } else {
1401 SMP2P_ERR("%s: unregister failure '%s':%d\n", __func__,
1402 name, remote_pid);
1403 ret = -ENODEV;
1404 }
1405
1406fail:
1407 spin_unlock_irqrestore(&in_list[remote_pid].in_item_lock_lhb1, flags);
1408
1409 return ret;
1410}
1411EXPORT_SYMBOL(msm_smp2p_in_unregister);
1412
1413/**
1414 * smp2p_send_interrupt - Send interrupt to remote system.
1415 *
1416 * @remote_pid: Processor ID of the remote system
1417 *
1418 * Must be called with out_item_lock_lha1 locked.
1419 */
1420static void smp2p_send_interrupt(int remote_pid)
1421{
1422 if (smp2p_int_cfgs[remote_pid].name)
1423 SMP2P_DBG("SMP2P Int Apps->%s(%d)\n",
1424 smp2p_int_cfgs[remote_pid].name, remote_pid);
1425
1426 ++smp2p_int_cfgs[remote_pid].out_interrupt_count;
1427 if (remote_pid != SMP2P_REMOTE_MOCK_PROC &&
1428 smp2p_int_cfgs[remote_pid].out_int_mask) {
1429 /* flush any pending writes before triggering interrupt */
1430 wmb();
1431 writel_relaxed(smp2p_int_cfgs[remote_pid].out_int_mask,
1432 smp2p_int_cfgs[remote_pid].out_int_ptr);
1433 } else {
1434 smp2p_remote_mock_rx_interrupt();
1435 }
1436}
1437
1438/**
1439 * smp2p_in_edge_notify - Notifies the entry changed on remote processor.
1440 *
1441 * @pid: Processor ID of the remote processor.
1442 *
1443 * This function is invoked on an incoming interrupt, it scans
1444 * the list of the clients registered for the entries on the remote
1445 * processor and notifies them if the data changes.
1446 *
Eric Holmberg6dd5bd22013-02-01 19:03:55 -07001447 * Note: Edge state must be OPENED to avoid a race condition with
1448 * out_list[pid].ops_ptr->find_entry.
Eric Holmberg6275b602012-11-19 13:05:04 -07001449 */
1450static void smp2p_in_edge_notify(int pid)
1451{
1452 struct smp2p_in *pos;
1453 uint32_t *entry_ptr;
1454 unsigned long flags;
Eric Holmberg02c6f702013-01-24 18:32:49 -07001455 struct smp2p_smem __iomem *smem_h_ptr;
Eric Holmberg6275b602012-11-19 13:05:04 -07001456 uint32_t curr_data;
1457 struct msm_smp2p_update_notif data;
1458
1459 spin_lock_irqsave(&in_list[pid].in_item_lock_lhb1, flags);
1460 smem_h_ptr = in_list[pid].smem_edge_in;
1461 if (!smem_h_ptr) {
1462 SMP2P_DBG("%s: No remote SMEM item for pid %d\n",
1463 __func__, pid);
1464 spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
1465 return;
1466 }
1467
1468 list_for_each_entry(pos, &in_list[pid].list, in_edge_list) {
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001469 if (pos->entry_ptr == NULL) {
Eric Holmberg6275b602012-11-19 13:05:04 -07001470 /* entry not open - try to open it */
1471 out_list[pid].ops_ptr->find_entry(smem_h_ptr,
1472 in_list[pid].safe_total_entries, pos->name,
1473 &entry_ptr, NULL);
1474
1475 if (entry_ptr) {
1476 pos->entry_ptr = entry_ptr;
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001477 pos->prev_entry_val = 0;
Eric Holmberg6275b602012-11-19 13:05:04 -07001478 data.previous_value = 0;
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001479 data.current_value = readl_relaxed(entry_ptr);
Eric Holmberg6275b602012-11-19 13:05:04 -07001480 raw_notifier_call_chain(
1481 &pos->in_notifier_list,
1482 SMP2P_OPEN, (void *)&data);
1483 }
1484 }
Eric Holmberg5b6219b2013-01-24 19:17:45 -07001485
1486 if (pos->entry_ptr != NULL) {
1487 /* send update notification */
1488 curr_data = readl_relaxed(pos->entry_ptr);
1489 if (curr_data != pos->prev_entry_val) {
1490 data.previous_value = pos->prev_entry_val;
1491 data.current_value = curr_data;
1492 pos->prev_entry_val = curr_data;
1493 raw_notifier_call_chain(
1494 &pos->in_notifier_list,
1495 SMP2P_ENTRY_UPDATE, (void *)&data);
1496 }
1497 }
Eric Holmberg6275b602012-11-19 13:05:04 -07001498 }
1499 spin_unlock_irqrestore(&in_list[pid].in_item_lock_lhb1, flags);
1500}
1501
1502/**
1503 * smp2p_interrupt_handler - Incoming interrupt handler.
1504 *
1505 * @irq: Interrupt ID
1506 * @data: Edge
1507 * @returns: IRQ_HANDLED or IRQ_NONE for invalid interrupt
1508 */
1509static irqreturn_t smp2p_interrupt_handler(int irq, void *data)
1510{
1511 unsigned long flags;
1512 uint32_t remote_pid = (uint32_t)data;
1513
1514 if (remote_pid >= SMP2P_NUM_PROCS) {
1515 SMP2P_ERR("%s: invalid interrupt pid %d\n",
1516 __func__, remote_pid);
1517 return IRQ_NONE;
1518 }
1519
1520 if (smp2p_int_cfgs[remote_pid].name)
1521 SMP2P_DBG("SMP2P Int %s(%d)->Apps\n",
1522 smp2p_int_cfgs[remote_pid].name, remote_pid);
1523
1524 spin_lock_irqsave(&out_list[remote_pid].out_item_lock_lha1, flags);
1525 ++smp2p_int_cfgs[remote_pid].in_interrupt_count;
1526
1527 if (out_list[remote_pid].smem_edge_state != SMP2P_EDGE_STATE_OPENED)
1528 smp2p_do_negotiation(remote_pid, &out_list[remote_pid]);
1529
Eric Holmberg6dd5bd22013-02-01 19:03:55 -07001530 if (out_list[remote_pid].smem_edge_state == SMP2P_EDGE_STATE_OPENED) {
1531 spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
1532 flags);
Eric Holmberg6275b602012-11-19 13:05:04 -07001533 smp2p_in_edge_notify(remote_pid);
Eric Holmberg6dd5bd22013-02-01 19:03:55 -07001534 } else {
1535 spin_unlock_irqrestore(&out_list[remote_pid].out_item_lock_lha1,
1536 flags);
1537 }
Eric Holmberg6275b602012-11-19 13:05:04 -07001538
1539 return IRQ_HANDLED;
1540}
1541
1542/**
1543 * smp2p_reset_mock_edge - Reinitializes the mock edge.
1544 *
1545 * @returns: 0 on success, -EAGAIN to retry later.
1546 *
1547 * Reinitializes the mock edge to initial power-up state values.
1548 */
1549int smp2p_reset_mock_edge(void)
1550{
1551 const int rpid = SMP2P_REMOTE_MOCK_PROC;
1552 unsigned long flags;
1553 int ret = 0;
1554
1555 spin_lock_irqsave(&out_list[rpid].out_item_lock_lha1, flags);
1556 spin_lock(&in_list[rpid].in_item_lock_lhb1);
1557
1558 if (!list_empty(&out_list[rpid].list) ||
1559 !list_empty(&in_list[rpid].list)) {
1560 ret = -EAGAIN;
1561 goto fail;
1562 }
1563
1564 kfree(out_list[rpid].smem_edge_out);
1565 out_list[rpid].smem_edge_out = NULL;
1566 out_list[rpid].ops_ptr = &version_if[0];
1567 out_list[rpid].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
1568
1569 in_list[rpid].smem_edge_in = NULL;
1570 in_list[rpid].item_size = 0;
1571 in_list[rpid].safe_total_entries = 0;
1572
1573fail:
1574 spin_unlock(&in_list[rpid].in_item_lock_lhb1);
1575 spin_unlock_irqrestore(&out_list[rpid].out_item_lock_lha1, flags);
1576
1577 return ret;
1578}
1579
1580/**
1581 * msm_smp2p_interrupt_handler - Triggers incoming interrupt.
1582 *
1583 * @remote_pid: Remote processor ID
1584 *
1585 * This function is used with the remote mock infrastructure
1586 * used for testing. It simulates triggering of interrupt in
1587 * a testing environment.
1588 */
1589void msm_smp2p_interrupt_handler(int remote_pid)
1590{
1591 smp2p_interrupt_handler(0, (void *)remote_pid);
1592}
1593
1594/**
1595 * msm_smp2p_probe - Device tree probe function.
1596 *
1597 * @pdev: Pointer to device tree data.
1598 * @returns: 0 on success; -ENODEV otherwise
1599 */
1600static int __devinit msm_smp2p_probe(struct platform_device *pdev)
1601{
1602 struct resource *irq_out_base;
1603 struct resource *irq_offset;
1604 char *key;
1605 uint32_t edge;
1606 int ret;
1607 struct device_node *node;
1608 uint32_t irq_bitmask;
1609 uint32_t irq_line;
1610
1611 node = pdev->dev.of_node;
1612
1613 key = "qcom,remote-pid";
1614 ret = of_property_read_u32(node, key, &edge);
1615 if (ret) {
1616 SMP2P_ERR("%s: missing edge '%s'\n", __func__, key);
1617 goto fail;
1618 }
1619
1620 key = "irq-reg-base";
1621 irq_out_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
1622 if (!irq_out_base)
1623 goto missing_key;
1624
1625 key = "irq-reg-offset";
1626 irq_offset = platform_get_resource_byname(pdev, IORESOURCE_MEM, key);
1627 if (!irq_offset)
1628 goto missing_key;
1629
1630 key = "qcom,irq-bitmask";
1631 ret = of_property_read_u32(node, key, &irq_bitmask);
1632 if (ret)
1633 goto missing_key;
1634
1635 key = "interrupts";
1636 irq_line = platform_get_irq(pdev, 0);
1637 if (irq_line == -ENXIO)
1638 goto missing_key;
1639
1640 ret = request_irq(irq_line, smp2p_interrupt_handler,
1641 IRQF_TRIGGER_RISING, "smp2p", (void *)edge);
1642 if (ret < 0) {
1643 SMP2P_ERR("%s: request_irq() failed on %d (edge %d)\n",
1644 __func__, irq_line, edge);
1645 goto fail;
1646 }
1647
1648 ret = enable_irq_wake(irq_line);
1649 if (ret < 0)
1650 SMP2P_ERR("%s: enable_irq_wake() failed on %d (edge %d)\n",
1651 __func__, irq_line, edge);
1652
1653 /*
1654 * Set entry (keep is_configured last to prevent usage before
1655 * initialization).
1656 */
1657 smp2p_int_cfgs[edge].in_int_id = irq_line;
1658 smp2p_int_cfgs[edge].out_int_mask = irq_bitmask;
1659 smp2p_int_cfgs[edge].out_int_ptr =
1660 (uint32_t *)((uint32_t)irq_out_base->start +
1661 (uint32_t)irq_offset->start);
1662 smp2p_int_cfgs[edge].is_configured = true;
1663 return 0;
1664
1665missing_key:
1666 SMP2P_ERR("%s: missing '%s' for edge %d\n", __func__, key, edge);
1667fail:
1668 return -ENODEV;
1669}
1670
1671static struct of_device_id msm_smp2p_match_table[] = {
1672 { .compatible = "qcom,smp2p" },
1673 {},
1674};
1675
1676static struct platform_driver msm_smp2p_driver = {
1677 .probe = msm_smp2p_probe,
1678 .driver = {
1679 .name = "msm_smp2p",
1680 .owner = THIS_MODULE,
1681 .of_match_table = msm_smp2p_match_table,
1682 },
1683};
1684
1685/**
1686 * msm_smp2p_init - Initialization function for the module.
1687 *
1688 * @returns: 0 on success, standard Linux error code otherwise.
1689 */
1690static int __init msm_smp2p_init(void)
1691{
1692 int i;
1693 int rc;
1694
1695 for (i = 0; i < SMP2P_NUM_PROCS; i++) {
1696 spin_lock_init(&out_list[i].out_item_lock_lha1);
1697 INIT_LIST_HEAD(&out_list[i].list);
1698 out_list[i].smem_edge_out = NULL;
1699 out_list[i].smem_edge_state = SMP2P_EDGE_STATE_CLOSED;
1700 out_list[i].ops_ptr = &version_if[0];
1701
1702 spin_lock_init(&in_list[i].in_item_lock_lhb1);
1703 INIT_LIST_HEAD(&in_list[i].list);
1704 in_list[i].smem_edge_in = NULL;
1705 }
1706
1707 log_ctx = ipc_log_context_create(NUM_LOG_PAGES, "smp2p");
1708 if (!log_ctx)
1709 SMP2P_ERR("%s: unable to create log context\n", __func__);
1710
1711 rc = platform_driver_register(&msm_smp2p_driver);
1712 if (rc) {
1713 SMP2P_ERR("%s: msm_smp2p_driver register failed %d\n",
1714 __func__, rc);
1715 return rc;
1716 }
1717
1718 return 0;
1719}
1720module_init(msm_smp2p_init);
1721
1722MODULE_DESCRIPTION("MSM Shared Memory Point to Point");
1723MODULE_LICENSE("GPL v2");