xref: /openbmc/u-boot/arch/arm/mach-bcm283x/mbox.c (revision 00caae6d)
1 /*
2  * (C) Copyright 2012 Stephen Warren
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <asm/io.h>
9 #include <asm/arch/mbox.h>
10 #include <phys2bus.h>
11 
12 #define TIMEOUT 1000 /* ms */
13 
14 int 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
89 void 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 
102 int 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 
114 	flush_dcache_range((unsigned long)buffer,
115 			   (unsigned long)((void *)buffer +
116 			   roundup(buffer->buf_size, ARCH_DMA_MINALIGN)));
117 
118 	ret = bcm2835_mbox_call_raw(chan,
119 				    phys_to_bus((unsigned long)buffer),
120 				    &rbuffer);
121 	if (ret)
122 		return ret;
123 
124 	invalidate_dcache_range((unsigned long)buffer,
125 				(unsigned long)((void *)buffer +
126 				roundup(buffer->buf_size, ARCH_DMA_MINALIGN)));
127 
128 	if (rbuffer != phys_to_bus((unsigned long)buffer)) {
129 		printf("mbox: Response buffer mismatch\n");
130 		return -1;
131 	}
132 
133 #ifdef DEBUG
134 	printf("mbox: RX buffer\n");
135 	dump_buf(buffer);
136 #endif
137 
138 	/* Validate overall response status */
139 
140 	if (buffer->code != BCM2835_MBOX_RESP_CODE_SUCCESS) {
141 		printf("mbox: Header response code invalid\n");
142 		return -1;
143 	}
144 
145 	/* Validate each tag's response status */
146 
147 	tag = (void *)(buffer + 1);
148 	tag_index = 0;
149 	while (tag->tag) {
150 		if (!(tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE)) {
151 			printf("mbox: Tag %d missing val_len response bit\n",
152 				tag_index);
153 			return -1;
154 		}
155 		/*
156 		 * Clear the reponse bit so clients can just look right at the
157 		 * length field without extra processing
158 		 */
159 		tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
160 		tag = (void *)(((u8 *)tag) + sizeof(*tag) + tag->val_buf_size);
161 		tag_index++;
162 	}
163 
164 	return 0;
165 }
166