xref: /openbmc/u-boot/arch/arm/mach-bcm283x/mbox.c (revision a65b25d1)
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 	ret = bcm2835_mbox_call_raw(chan, phys_to_bus((u32)buffer), &rbuffer);
115 	if (ret)
116 		return ret;
117 	if (rbuffer != phys_to_bus((u32)buffer)) {
118 		printf("mbox: Response buffer mismatch\n");
119 		return -1;
120 	}
121 
122 #ifdef DEBUG
123 	printf("mbox: RX buffer\n");
124 	dump_buf(buffer);
125 #endif
126 
127 	/* Validate overall response status */
128 
129 	if (buffer->code != BCM2835_MBOX_RESP_CODE_SUCCESS) {
130 		printf("mbox: Header response code invalid\n");
131 		return -1;
132 	}
133 
134 	/* Validate each tag's response status */
135 
136 	tag = (void *)(buffer + 1);
137 	tag_index = 0;
138 	while (tag->tag) {
139 		if (!(tag->val_len & BCM2835_MBOX_TAG_VAL_LEN_RESPONSE)) {
140 			printf("mbox: Tag %d missing val_len response bit\n",
141 				tag_index);
142 			return -1;
143 		}
144 		/*
145 		 * Clear the reponse bit so clients can just look right at the
146 		 * length field without extra processing
147 		 */
148 		tag->val_len &= ~BCM2835_MBOX_TAG_VAL_LEN_RESPONSE;
149 		tag = (void *)(((u8 *)tag) + sizeof(*tag) + tag->val_buf_size);
150 		tag_index++;
151 	}
152 
153 	return 0;
154 }
155