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