blob: 311bd8feaab071fdb7ffdb23477ef8c68cdbcd23 [file] [log] [blame]
Stephen Warren91ea2882013-01-29 16:37:36 +00001/*
2 * (C) Copyright 2012 Stephen Warren
3 *
Wolfgang Denkd79de1d2013-07-08 09:37:19 +02004 * SPDX-License-Identifier: GPL-2.0+
Stephen Warren91ea2882013-01-29 16:37:36 +00005 */
6
7#include <common.h>
8#include <asm/io.h>
9#include <asm/arch/mbox.h>
Stephen Warrenb44229c2015-04-06 20:28:39 -060010#include <phys2bus.h>
Stephen Warren91ea2882013-01-29 16:37:36 +000011
Stephen Warrenc2b50ff2014-01-13 19:50:12 -070012#define TIMEOUT 1000 /* ms */
Stephen Warren91ea2882013-01-29 16:37:36 +000013
14int bcm2835_mbox_call_raw(u32 chan, u32 send, u32 *recv)
15{
16 struct bcm2835_mbox_regs *regs =
17 (struct bcm2835_mbox_regs *)BCM2835_MBOX_PHYSADDR;
18 ulong endtime = get_timer(0) + TIMEOUT;
19 u32 val;
20
21 debug("time: %lu timeout: %lu\n", get_timer(0), endtime);
22
23 if (send & BCM2835_CHAN_MASK) {
24 printf("mbox: Illegal mbox data 0x%08x\n", send);
25 return -1;
26 }
27
28 /* Drain any stale responses */
29
30 for (;;) {
31 val = readl(&regs->status);
32 if (val & BCM2835_MBOX_STATUS_RD_EMPTY)
33 break;
34 if (get_timer(0) >= endtime) {
35 printf("mbox: Timeout draining stale responses\n");
36 return -1;
37 }
38 val = readl(&regs->read);
39 }
40
41 /* Wait for space to send */
42
43 for (;;) {
44 val = readl(&regs->status);
45 if (!(val & BCM2835_MBOX_STATUS_WR_FULL))
46 break;
47 if (get_timer(0) >= endtime) {
48 printf("mbox: Timeout waiting for send space\n");
49 return -1;
50 }
51 }
52
53 /* Send the request */
54
55 val = BCM2835_MBOX_PACK(chan, send);
56 debug("mbox: TX raw: 0x%08x\n", val);
57 writel(val, &regs->write);
58
59 /* Wait for the response */
60
61 for (;;) {
62 val = readl(&regs->status);
63 if (!(val & BCM2835_MBOX_STATUS_RD_EMPTY))
64 break;
65 if (get_timer(0) >= endtime) {
66 printf("mbox: Timeout waiting for response\n");
67 return -1;
68 }
69 }
70
71 /* Read the response */
72
73 val = readl(&regs->read);
74 debug("mbox: RX raw: 0x%08x\n", val);
75
76 /* Validate the response */
77
78 if (BCM2835_MBOX_UNPACK_CHAN(val) != chan) {
79 printf("mbox: Response channel mismatch\n");
80 return -1;
81 }
82
83 *recv = BCM2835_MBOX_UNPACK_DATA(val);
84
85 return 0;
86}
87
88#ifdef DEBUG
89void dump_buf(struct bcm2835_mbox_hdr *buffer)
90{
91 u32 *p;
92 u32 words;
93 int i;
94
95 p = (u32 *)buffer;
96 words = buffer->buf_size / 4;
97 for (i = 0; i < words; i++)
98 printf(" 0x%04x: 0x%08x\n", i * 4, p[i]);
99}
100#endif
101
102int bcm2835_mbox_call_prop(u32 chan, struct bcm2835_mbox_hdr *buffer)
103{
104 int ret;
105 u32 rbuffer;
106 struct bcm2835_mbox_tag_hdr *tag;
107 int tag_index;
108
109#ifdef DEBUG
110 printf("mbox: TX buffer\n");
111 dump_buf(buffer);
112#endif
113
Alexander Steinfbeb89d2015-07-24 09:22:13 +0200114 flush_dcache_range((unsigned long)buffer,
115 (unsigned long)((void *)buffer +
116 roundup(buffer->buf_size, ARCH_DMA_MINALIGN)));
117
Stephen Warrenb44229c2015-04-06 20:28:39 -0600118 ret = bcm2835_mbox_call_raw(chan, phys_to_bus((u32)buffer), &rbuffer);
Stephen Warren91ea2882013-01-29 16:37:36 +0000119 if (ret)
120 return ret;
Alexander Steinfbeb89d2015-07-24 09:22:13 +0200121
122 invalidate_dcache_range((unsigned long)buffer,
123 (unsigned long)((void *)buffer +
124 roundup(buffer->buf_size, ARCH_DMA_MINALIGN)));
125
Stephen Warrenb44229c2015-04-06 20:28:39 -0600126 if (rbuffer != phys_to_bus((u32)buffer)) {
Stephen Warren91ea2882013-01-29 16:37:36 +0000127 printf("mbox: Response buffer mismatch\n");
128 return -1;
129 }
130
131#ifdef DEBUG
132 printf("mbox: RX buffer\n");
133 dump_buf(buffer);
134#endif
135
136 /* Validate overall response status */
137
138 if (buffer->code != BCM2835_MBOX_RESP_CODE_SUCCESS) {
139 printf("mbox: Header response code invalid\n");
140 return -1;
141 }
142
143 /* Validate each tag's response status */
144
145 tag = (void *)(buffer + 1);
146 tag_index = 0;
147 while (tag->tag) {
148 if (!(tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE)) {
149 printf("mbox: Tag %d missing val_len response bit\n",
150 tag_index);
151 return -1;
152 }
153 /*
154 * Clear the reponse bit so clients can just look right at the
155 * length field without extra processing
156 */
157 tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
158 tag = (void *)(((u8 *)tag) + sizeof(*tag) + tag->val_buf_size);
159 tag_index++;
160 }
161
162 return 0;
163}