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