aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware
diff options
context:
space:
mode:
authorThierry Reding <treding@nvidia.com>2016-08-19 13:05:04 -0400
committerThierry Reding <treding@nvidia.com>2016-11-18 08:33:42 -0500
commitca791d7f425635b63706e00896a141f85f7de463 (patch)
treeebb76fa51c8e19d74fdb0e480032687203921f72 /drivers/firmware
parentd55865608fe0191935665acc6bb6d36bc8bb4b2e (diff)
firmware: tegra: Add IVC library
The Inter-VM communication (IVC) is a communication protocol which is designed for interprocessor communication (IPC) or the communication between the hypervisor and the virtual machine with a guest OS. Message channels are used to communicate between processors. They are backed by DRAM or SRAM, so care must be taken to maintain coherence of data. The IVC library maintains memory-based descriptors for the transmission and reception channels as well as the data coherence of the counter and payload. Clients, such as the driver for the BPMP firmware, can use the library to exchange messages with remote processors. Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo <josephl@nvidia.com>. Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/firmware')
-rw-r--r--drivers/firmware/Kconfig1
-rw-r--r--drivers/firmware/Makefile1
-rw-r--r--drivers/firmware/tegra/Kconfig13
-rw-r--r--drivers/firmware/tegra/Makefile1
-rw-r--r--drivers/firmware/tegra/ivc.c695
5 files changed, 711 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bca172d42c74..9968de04d1d5 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -210,5 +210,6 @@ source "drivers/firmware/broadcom/Kconfig"
210source "drivers/firmware/google/Kconfig" 210source "drivers/firmware/google/Kconfig"
211source "drivers/firmware/efi/Kconfig" 211source "drivers/firmware/efi/Kconfig"
212source "drivers/firmware/meson/Kconfig" 212source "drivers/firmware/meson/Kconfig"
213source "drivers/firmware/tegra/Kconfig"
213 214
214endmenu 215endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 898ac41fa8b3..2afe75c52ac2 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -26,3 +26,4 @@ obj-y += meson/
26obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ 26obj-$(CONFIG_GOOGLE_FIRMWARE) += google/
27obj-$(CONFIG_EFI) += efi/ 27obj-$(CONFIG_EFI) += efi/
28obj-$(CONFIG_UEFI_CPER) += efi/ 28obj-$(CONFIG_UEFI_CPER) += efi/
29obj-y += tegra/
diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
new file mode 100644
index 000000000000..1fa3e4e136a5
--- /dev/null
+++ b/drivers/firmware/tegra/Kconfig
@@ -0,0 +1,13 @@
1menu "Tegra firmware driver"
2
3config TEGRA_IVC
4 bool "Tegra IVC protocol"
5 depends on ARCH_TEGRA
6 help
7 IVC (Inter-VM Communication) protocol is part of the IPC
8 (Inter Processor Communication) framework on Tegra. It maintains the
9 data and the different commuication channels in SysRAM or RAM and
10 keeps the content is synchronization between host CPU and remote
11 processors.
12
13endmenu
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
new file mode 100644
index 000000000000..92e2153e8173
--- /dev/null
+++ b/drivers/firmware/tegra/Makefile
@@ -0,0 +1 @@
obj-$(CONFIG_TEGRA_IVC) += ivc.o
diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c
new file mode 100644
index 000000000000..29ecfd815320
--- /dev/null
+++ b/drivers/firmware/tegra/ivc.c
@@ -0,0 +1,695 @@
1/*
2 * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms and conditions of the GNU General Public License,
6 * version 2, as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope it will be useful, but WITHOUT
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11 * more details.
12 */
13
14#include <soc/tegra/ivc.h>
15
16#define TEGRA_IVC_ALIGN 64
17
18/*
19 * IVC channel reset protocol.
20 *
21 * Each end uses its tx_channel.state to indicate its synchronization state.
22 */
23enum tegra_ivc_state {
24 /*
25 * This value is zero for backwards compatibility with services that
26 * assume channels to be initially zeroed. Such channels are in an
27 * initially valid state, but cannot be asynchronously reset, and must
28 * maintain a valid state at all times.
29 *
30 * The transmitting end can enter the established state from the sync or
31 * ack state when it observes the receiving endpoint in the ack or
32 * established state, indicating that has cleared the counters in our
33 * rx_channel.
34 */
35 TEGRA_IVC_STATE_ESTABLISHED = 0,
36
37 /*
38 * If an endpoint is observed in the sync state, the remote endpoint is
39 * allowed to clear the counters it owns asynchronously with respect to
40 * the current endpoint. Therefore, the current endpoint is no longer
41 * allowed to communicate.
42 */
43 TEGRA_IVC_STATE_SYNC,
44
45 /*
46 * When the transmitting end observes the receiving end in the sync
47 * state, it can clear the w_count and r_count and transition to the ack
48 * state. If the remote endpoint observes us in the ack state, it can
49 * return to the established state once it has cleared its counters.
50 */
51 TEGRA_IVC_STATE_ACK
52};
53
54/*
55 * This structure is divided into two-cache aligned parts, the first is only
56 * written through the tx.channel pointer, while the second is only written
57 * through the rx.channel pointer. This delineates ownership of the cache
58 * lines, which is critical to performance and necessary in non-cache coherent
59 * implementations.
60 */
61struct tegra_ivc_header {
62 union {
63 struct {
64 /* fields owned by the transmitting end */
65 u32 count;
66 u32 state;
67 };
68
69 u8 pad[TEGRA_IVC_ALIGN];
70 } tx;
71
72 union {
73 /* fields owned by the receiving end */
74 u32 count;
75 u8 pad[TEGRA_IVC_ALIGN];
76 } rx;
77};
78
79static inline void tegra_ivc_invalidate(struct tegra_ivc *ivc, dma_addr_t phys)
80{
81 if (!ivc->peer)
82 return;
83
84 dma_sync_single_for_cpu(ivc->peer, phys, TEGRA_IVC_ALIGN,
85 DMA_FROM_DEVICE);
86}
87
88static inline void tegra_ivc_flush(struct tegra_ivc *ivc, dma_addr_t phys)
89{
90 if (!ivc->peer)
91 return;
92
93 dma_sync_single_for_device(ivc->peer, phys, TEGRA_IVC_ALIGN,
94 DMA_TO_DEVICE);
95}
96
97static inline bool tegra_ivc_empty(struct tegra_ivc *ivc,
98 struct tegra_ivc_header *header)
99{
100 /*
101 * This function performs multiple checks on the same values with
102 * security implications, so create snapshots with ACCESS_ONCE() to
103 * ensure that these checks use the same values.
104 */
105 u32 tx = ACCESS_ONCE(header->tx.count);
106 u32 rx = ACCESS_ONCE(header->rx.count);
107
108 /*
109 * Perform an over-full check to prevent denial of service attacks
110 * where a server could be easily fooled into believing that there's
111 * an extremely large number of frames ready, since receivers are not
112 * expected to check for full or over-full conditions.
113 *
114 * Although the channel isn't empty, this is an invalid case caused by
115 * a potentially malicious peer, so returning empty is safer, because
116 * it gives the impression that the channel has gone silent.
117 */
118 if (tx - rx > ivc->num_frames)
119 return true;
120
121 return tx == rx;
122}
123
124static inline bool tegra_ivc_full(struct tegra_ivc *ivc,
125 struct tegra_ivc_header *header)
126{
127 u32 tx = ACCESS_ONCE(header->tx.count);
128 u32 rx = ACCESS_ONCE(header->rx.count);
129
130 /*
131 * Invalid cases where the counters indicate that the queue is over
132 * capacity also appear full.
133 */
134 return tx - rx >= ivc->num_frames;
135}
136
137static inline u32 tegra_ivc_available(struct tegra_ivc *ivc,
138 struct tegra_ivc_header *header)
139{
140 u32 tx = ACCESS_ONCE(header->tx.count);
141 u32 rx = ACCESS_ONCE(header->rx.count);
142
143 /*
144 * This function isn't expected to be used in scenarios where an
145 * over-full situation can lead to denial of service attacks. See the
146 * comment in tegra_ivc_empty() for an explanation about special
147 * over-full considerations.
148 */
149 return tx - rx;
150}
151
152static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
153{
154 ACCESS_ONCE(ivc->tx.channel->tx.count) =
155 ACCESS_ONCE(ivc->tx.channel->tx.count) + 1;
156
157 if (ivc->tx.position == ivc->num_frames - 1)
158 ivc->tx.position = 0;
159 else
160 ivc->tx.position++;
161}
162
163static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
164{
165 ACCESS_ONCE(ivc->rx.channel->rx.count) =
166 ACCESS_ONCE(ivc->rx.channel->rx.count) + 1;
167
168 if (ivc->rx.position == ivc->num_frames - 1)
169 ivc->rx.position = 0;
170 else
171 ivc->rx.position++;
172}
173
174static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
175{
176 unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
177
178 /*
179 * tx.channel->state is set locally, so it is not synchronized with
180 * state from the remote peer. The remote peer cannot reset its
181 * transmit counters until we've acknowledged its synchronization
182 * request, so no additional synchronization is required because an
183 * asynchronous transition of rx.channel->state to
184 * TEGRA_IVC_STATE_ACK is not allowed.
185 */
186 if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
187 return -ECONNRESET;
188
189 /*
190 * Avoid unnecessary invalidations when performing repeated accesses
191 * to an IVC channel by checking the old queue pointers first.
192 *
193 * Synchronization is only necessary when these pointers indicate
194 * empty or full.
195 */
196 if (!tegra_ivc_empty(ivc, ivc->rx.channel))
197 return 0;
198
199 tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
200
201 if (tegra_ivc_empty(ivc, ivc->rx.channel))
202 return -ENOSPC;
203
204 return 0;
205}
206
207static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
208{
209 unsigned int offset = offsetof(struct tegra_ivc_header, rx.count);
210
211 if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
212 return -ECONNRESET;
213
214 if (!tegra_ivc_full(ivc, ivc->tx.channel))
215 return 0;
216
217 tegra_ivc_invalidate(ivc, ivc->tx.phys + offset);
218
219 if (tegra_ivc_full(ivc, ivc->tx.channel))
220 return -ENOSPC;
221
222 return 0;
223}
224
225static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
226 struct tegra_ivc_header *header,
227 unsigned int frame)
228{
229 if (WARN_ON(frame >= ivc->num_frames))
230 return ERR_PTR(-EINVAL);
231
232 return (void *)(header + 1) + ivc->frame_size * frame;
233}
234
235static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
236 dma_addr_t phys,
237 unsigned int frame)
238{
239 unsigned long offset;
240
241 offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
242
243 return phys + offset;
244}
245
246static inline void tegra_ivc_invalidate_frame(struct tegra_ivc *ivc,
247 dma_addr_t phys,
248 unsigned int frame,
249 unsigned int offset,
250 size_t size)
251{
252 if (!ivc->peer || WARN_ON(frame >= ivc->num_frames))
253 return;
254
255 phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
256
257 dma_sync_single_for_cpu(ivc->peer, phys, size, DMA_FROM_DEVICE);
258}
259
260static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
261 dma_addr_t phys,
262 unsigned int frame,
263 unsigned int offset,
264 size_t size)
265{
266 if (!ivc->peer || WARN_ON(frame >= ivc->num_frames))
267 return;
268
269 phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
270
271 dma_sync_single_for_device(ivc->peer, phys, size, DMA_TO_DEVICE);
272}
273
274/* directly peek at the next frame rx'ed */
275void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc)
276{
277 int err;
278
279 if (WARN_ON(ivc == NULL))
280 return ERR_PTR(-EINVAL);
281
282 err = tegra_ivc_check_read(ivc);
283 if (err < 0)
284 return ERR_PTR(err);
285
286 /*
287 * Order observation of ivc->rx.position potentially indicating new
288 * data before data read.
289 */
290 smp_rmb();
291
292 tegra_ivc_invalidate_frame(ivc, ivc->rx.phys, ivc->rx.position, 0,
293 ivc->frame_size);
294
295 return tegra_ivc_frame_virt(ivc, ivc->rx.channel, ivc->rx.position);
296}
297EXPORT_SYMBOL(tegra_ivc_read_get_next_frame);
298
299int tegra_ivc_read_advance(struct tegra_ivc *ivc)
300{
301 unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
302 unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
303 int err;
304
305 /*
306 * No read barriers or synchronization here: the caller is expected to
307 * have already observed the channel non-empty. This check is just to
308 * catch programming errors.
309 */
310 err = tegra_ivc_check_read(ivc);
311 if (err < 0)
312 return err;
313
314 tegra_ivc_advance_rx(ivc);
315
316 tegra_ivc_flush(ivc, ivc->rx.phys + rx);
317
318 /*
319 * Ensure our write to ivc->rx.position occurs before our read from
320 * ivc->tx.position.
321 */
322 smp_mb();
323
324 /*
325 * Notify only upon transition from full to non-full. The available
326 * count can only asynchronously increase, so the worst possible
327 * side-effect will be a spurious notification.
328 */
329 tegra_ivc_invalidate(ivc, ivc->rx.phys + tx);
330
331 if (tegra_ivc_available(ivc, ivc->rx.channel) == ivc->num_frames - 1)
332 ivc->notify(ivc, ivc->notify_data);
333
334 return 0;
335}
336EXPORT_SYMBOL(tegra_ivc_read_advance);
337
338/* directly poke at the next frame to be tx'ed */
339void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc)
340{
341 int err;
342
343 err = tegra_ivc_check_write(ivc);
344 if (err < 0)
345 return ERR_PTR(err);
346
347 return tegra_ivc_frame_virt(ivc, ivc->tx.channel, ivc->tx.position);
348}
349EXPORT_SYMBOL(tegra_ivc_write_get_next_frame);
350
351/* advance the tx buffer */
352int tegra_ivc_write_advance(struct tegra_ivc *ivc)
353{
354 unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
355 unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
356 int err;
357
358 err = tegra_ivc_check_write(ivc);
359 if (err < 0)
360 return err;
361
362 tegra_ivc_flush_frame(ivc, ivc->tx.phys, ivc->tx.position, 0,
363 ivc->frame_size);
364
365 /*
366 * Order any possible stores to the frame before update of
367 * ivc->tx.position.
368 */
369 smp_wmb();
370
371 tegra_ivc_advance_tx(ivc);
372 tegra_ivc_flush(ivc, ivc->tx.phys + tx);
373
374 /*
375 * Ensure our write to ivc->tx.position occurs before our read from
376 * ivc->rx.position.
377 */
378 smp_mb();
379
380 /*
381 * Notify only upon transition from empty to non-empty. The available
382 * count can only asynchronously decrease, so the worst possible
383 * side-effect will be a spurious notification.
384 */
385 tegra_ivc_invalidate(ivc, ivc->tx.phys + rx);
386
387 if (tegra_ivc_available(ivc, ivc->tx.channel) == 1)
388 ivc->notify(ivc, ivc->notify_data);
389
390 return 0;
391}
392EXPORT_SYMBOL(tegra_ivc_write_advance);
393
394void tegra_ivc_reset(struct tegra_ivc *ivc)
395{
396 unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
397
398 ivc->tx.channel->tx.state = TEGRA_IVC_STATE_SYNC;
399 tegra_ivc_flush(ivc, ivc->tx.phys + offset);
400 ivc->notify(ivc, ivc->notify_data);
401}
402EXPORT_SYMBOL(tegra_ivc_reset);
403
404/*
405 * =======================================================
406 * IVC State Transition Table - see tegra_ivc_notified()
407 * =======================================================
408 *
409 * local remote action
410 * ----- ------ -----------------------------------
411 * SYNC EST <none>
412 * SYNC ACK reset counters; move to EST; notify
413 * SYNC SYNC reset counters; move to ACK; notify
414 * ACK EST move to EST; notify
415 * ACK ACK move to EST; notify
416 * ACK SYNC reset counters; move to ACK; notify
417 * EST EST <none>
418 * EST ACK <none>
419 * EST SYNC reset counters; move to ACK; notify
420 *
421 * ===============================================================
422 */
423
424int tegra_ivc_notified(struct tegra_ivc *ivc)
425{
426 unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
427 enum tegra_ivc_state state;
428
429 /* Copy the receiver's state out of shared memory. */
430 tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
431 state = ACCESS_ONCE(ivc->rx.channel->tx.state);
432
433 if (state == TEGRA_IVC_STATE_SYNC) {
434 offset = offsetof(struct tegra_ivc_header, tx.count);
435
436 /*
437 * Order observation of TEGRA_IVC_STATE_SYNC before stores
438 * clearing tx.channel.
439 */
440 smp_rmb();
441
442 /*
443 * Reset tx.channel counters. The remote end is in the SYNC
444 * state and won't make progress until we change our state,
445 * so the counters are not in use at this time.
446 */
447 ivc->tx.channel->tx.count = 0;
448 ivc->rx.channel->rx.count = 0;
449
450 ivc->tx.position = 0;
451 ivc->rx.position = 0;
452
453 /*
454 * Ensure that counters appear cleared before new state can be
455 * observed.
456 */
457 smp_wmb();
458
459 /*
460 * Move to ACK state. We have just cleared our counters, so it
461 * is now safe for the remote end to start using these values.
462 */
463 ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ACK;
464 tegra_ivc_flush(ivc, ivc->tx.phys + offset);
465
466 /*
467 * Notify remote end to observe state transition.
468 */
469 ivc->notify(ivc, ivc->notify_data);
470
471 } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_SYNC &&
472 state == TEGRA_IVC_STATE_ACK) {
473 offset = offsetof(struct tegra_ivc_header, tx.count);
474
475 /*
476 * Order observation of ivc_state_sync before stores clearing
477 * tx_channel.
478 */
479 smp_rmb();
480
481 /*
482 * Reset tx.channel counters. The remote end is in the ACK
483 * state and won't make progress until we change our state,
484 * so the counters are not in use at this time.
485 */
486 ivc->tx.channel->tx.count = 0;
487 ivc->rx.channel->rx.count = 0;
488
489 ivc->tx.position = 0;
490 ivc->rx.position = 0;
491
492 /*
493 * Ensure that counters appear cleared before new state can be
494 * observed.
495 */
496 smp_wmb();
497
498 /*
499 * Move to ESTABLISHED state. We know that the remote end has
500 * already cleared its counters, so it is safe to start
501 * writing/reading on this channel.
502 */
503 ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
504 tegra_ivc_flush(ivc, ivc->tx.phys + offset);
505
506 /*
507 * Notify remote end to observe state transition.
508 */
509 ivc->notify(ivc, ivc->notify_data);
510
511 } else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_ACK) {
512 offset = offsetof(struct tegra_ivc_header, tx.count);
513
514 /*
515 * At this point, we have observed the peer to be in either
516 * the ACK or ESTABLISHED state. Next, order observation of
517 * peer state before storing to tx.channel.
518 */
519 smp_rmb();
520
521 /*
522 * Move to ESTABLISHED state. We know that we have previously
523 * cleared our counters, and we know that the remote end has
524 * cleared its counters, so it is safe to start writing/reading
525 * on this channel.
526 */
527 ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
528 tegra_ivc_flush(ivc, ivc->tx.phys + offset);
529
530 /*
531 * Notify remote end to observe state transition.
532 */
533 ivc->notify(ivc, ivc->notify_data);
534
535 } else {
536 /*
537 * There is no need to handle any further action. Either the
538 * channel is already fully established, or we are waiting for
539 * the remote end to catch up with our current state. Refer
540 * to the diagram in "IVC State Transition Table" above.
541 */
542 }
543
544 if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
545 return -EAGAIN;
546
547 return 0;
548}
549EXPORT_SYMBOL(tegra_ivc_notified);
550
551size_t tegra_ivc_align(size_t size)
552{
553 return ALIGN(size, TEGRA_IVC_ALIGN);
554}
555EXPORT_SYMBOL(tegra_ivc_align);
556
557unsigned tegra_ivc_total_queue_size(unsigned queue_size)
558{
559 if (!IS_ALIGNED(queue_size, TEGRA_IVC_ALIGN)) {
560 pr_err("%s: queue_size (%u) must be %u-byte aligned\n",
561 __func__, queue_size, TEGRA_IVC_ALIGN);
562 return 0;
563 }
564
565 return queue_size + sizeof(struct tegra_ivc_header);
566}
567EXPORT_SYMBOL(tegra_ivc_total_queue_size);
568
569static int tegra_ivc_check_params(unsigned long rx, unsigned long tx,
570 unsigned int num_frames, size_t frame_size)
571{
572 BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct tegra_ivc_header, tx.count),
573 TEGRA_IVC_ALIGN));
574 BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct tegra_ivc_header, rx.count),
575 TEGRA_IVC_ALIGN));
576 BUILD_BUG_ON(!IS_ALIGNED(sizeof(struct tegra_ivc_header),
577 TEGRA_IVC_ALIGN));
578
579 if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000UL) {
580 pr_err("num_frames * frame_size overflows\n");
581 return -EINVAL;
582 }
583
584 if (!IS_ALIGNED(frame_size, TEGRA_IVC_ALIGN)) {
585 pr_err("frame size not adequately aligned: %zu\n", frame_size);
586 return -EINVAL;
587 }
588
589 /*
590 * The headers must at least be aligned enough for counters
591 * to be accessed atomically.
592 */
593 if (!IS_ALIGNED(rx, TEGRA_IVC_ALIGN)) {
594 pr_err("IVC channel start not aligned: %#lx\n", rx);
595 return -EINVAL;
596 }
597
598 if (!IS_ALIGNED(tx, TEGRA_IVC_ALIGN)) {
599 pr_err("IVC channel start not aligned: %#lx\n", tx);
600 return -EINVAL;
601 }
602
603 if (rx < tx) {
604 if (rx + frame_size * num_frames > tx) {
605 pr_err("queue regions overlap: %#lx + %zx > %#lx\n",
606 rx, frame_size * num_frames, tx);
607 return -EINVAL;
608 }
609 } else {
610 if (tx + frame_size * num_frames > rx) {
611 pr_err("queue regions overlap: %#lx + %zx > %#lx\n",
612 tx, frame_size * num_frames, rx);
613 return -EINVAL;
614 }
615 }
616
617 return 0;
618}
619
620int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer, void *rx,
621 dma_addr_t rx_phys, void *tx, dma_addr_t tx_phys,
622 unsigned int num_frames, size_t frame_size,
623 void (*notify)(struct tegra_ivc *ivc, void *data),
624 void *data)
625{
626 size_t queue_size;
627 int err;
628
629 if (WARN_ON(!ivc || !notify))
630 return -EINVAL;
631
632 /*
633 * All sizes that can be returned by communication functions should
634 * fit in an int.
635 */
636 if (frame_size > INT_MAX)
637 return -E2BIG;
638
639 err = tegra_ivc_check_params((unsigned long)rx, (unsigned long)tx,
640 num_frames, frame_size);
641 if (err < 0)
642 return err;
643
644 queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
645
646 if (peer) {
647 ivc->rx.phys = dma_map_single(peer, rx, queue_size,
648 DMA_BIDIRECTIONAL);
649 if (ivc->rx.phys == DMA_ERROR_CODE)
650 return -ENOMEM;
651
652 ivc->tx.phys = dma_map_single(peer, tx, queue_size,
653 DMA_BIDIRECTIONAL);
654 if (ivc->tx.phys == DMA_ERROR_CODE) {
655 dma_unmap_single(peer, ivc->rx.phys, queue_size,
656 DMA_BIDIRECTIONAL);
657 return -ENOMEM;
658 }
659 } else {
660 ivc->rx.phys = rx_phys;
661 ivc->tx.phys = tx_phys;
662 }
663
664 ivc->rx.channel = rx;
665 ivc->tx.channel = tx;
666 ivc->peer = peer;
667 ivc->notify = notify;
668 ivc->notify_data = data;
669 ivc->frame_size = frame_size;
670 ivc->num_frames = num_frames;
671
672 /*
673 * These values aren't necessarily correct until the channel has been
674 * reset.
675 */
676 ivc->tx.position = 0;
677 ivc->rx.position = 0;
678
679 return 0;
680}
681EXPORT_SYMBOL(tegra_ivc_init);
682
683void tegra_ivc_cleanup(struct tegra_ivc *ivc)
684{
685 if (ivc->peer) {
686 size_t size = tegra_ivc_total_queue_size(ivc->num_frames *
687 ivc->frame_size);
688
689 dma_unmap_single(ivc->peer, ivc->rx.phys, size,
690 DMA_BIDIRECTIONAL);
691 dma_unmap_single(ivc->peer, ivc->tx.phys, size,
692 DMA_BIDIRECTIONAL);
693 }
694}
695EXPORT_SYMBOL(tegra_ivc_cleanup);