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
17int 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
92void 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
105int 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