1/* $NetBSD: miniipc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */
2
3/*-
4 * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29#include <lib/libsa/stand.h>
30
31#include <machine/pio.h>
32
33#include "miniipc.h"
34#include "sdmmc.h"
35#include "cache.h"
36#include "timer.h"
37
38#define MINIIPC_INFO_PTR		0x13fffffc
39
40#define HW_BASE				0x0d800000
41#define	HW_IPC_PPCMSG			(HW_BASE + 0x00)
42#define  HW_IPC_PPCMSG_INDEX_OUT_MASK	__BITS(31, 16)
43#define  HW_IPC_PPCMSG_INDEX_IN_MASK	__BITS(15, 0)
44#define HW_IPC_PPCCTRL			(HW_BASE + 0x04)
45#define  HW_IPC_PPCCTRL_IN		__BIT(2)
46#define  HW_IPC_PPCCTRL_OUT		__BIT(0)
47#define HW_IPC_ARMMSG			(HW_BASE + 0x08)
48#define  HW_IPC_ARMMSG_INDEX_OUT_MASK	__BITS(15, 0)
49
50/* IPC request flags */
51#define IPC_SLOW			0x00
52#define IPC_FAST			0x01
53
54/* IPC devices and requests */
55#define IPC_DEV_SYS			0x00
56#define  IPC_SYS_PING			0x0000
57#define IPC_DEV_SDMMC			0x07
58#define  IPC_SDMMC_ACK			0x0000
59#define  IPC_SDMMC_READ			0x0001
60#define  IPC_SDMMC_WRITE		0x0002
61#define  IPC_SDMMC_STATE		0x0003
62#define  IPC_SDMMC_SIZE			0x0004
63
64static volatile ipc_info_header_t *infohdr;
65static uint32_t index_in;
66static uint32_t index_out;
67static uint32_t request_tag;
68
69static uint32_t
70miniipc_next_index_in(void)
71{
72	return (index_in + 1) % infohdr->ipc_in_size;
73}
74
75static uint32_t
76miniipc_next_index_out(void)
77{
78	return (index_out + 1) % infohdr->ipc_out_size;
79}
80
81bool
82miniipc_probe(void)
83{
84	paddr_t pa;
85
86	pa = in32(MINIIPC_INFO_PTR);
87	if (pa == 0 || pa == 0xffffffff) {
88		printf("No MINI IPC pointer at 0x%x\n", MINIIPC_INFO_PTR);
89		return false;
90	}
91	infohdr = (ipc_info_header_t *)pa;
92	cache_dcbi((void *)infohdr, sizeof(*infohdr));
93
94	if (memcmp((void *)infohdr->magic, "IPC", 3) != 0) {
95		return false;
96	}
97
98	index_in = __SHIFTOUT(in32(HW_IPC_PPCMSG), HW_IPC_PPCMSG_INDEX_IN_MASK);
99	index_out = __SHIFTOUT(in32(HW_IPC_PPCMSG), HW_IPC_PPCMSG_INDEX_OUT_MASK);
100
101	miniipc_ping();
102
103	return true;
104}
105
106static int
107miniipc_sendrecv(const ipc_request_t *req_in, ipc_request_t *req_out)
108{
109	volatile ipc_request_t *req;
110	int retry, error = 0;
111
112	req = &infohdr->ipc_in[index_in];
113
114	*req = *req_in;
115	req->tag = ++request_tag;
116
117#ifdef MINIIPC_DEBUG
118	printf("IPC[%u] -> req code 0x%x tag %u args %u %u %u %u %u %u\n",
119	    index_in, req->code, req->tag,
120	    req->args[0], req->args[1], req->args[2], req->args[3],
121	    req->args[4], req->args[5]);
122#endif
123
124	cache_dcbf((void *)req, sizeof(*req));
125
126	req = &infohdr->ipc_out[index_out];
127	cache_dcbf((void *)req, sizeof(*req));
128
129	index_in = miniipc_next_index_in();
130
131	out32(HW_IPC_PPCMSG,
132	    (in32(HW_IPC_PPCMSG) & ~HW_IPC_PPCMSG_INDEX_IN_MASK) |
133	     __SHIFTIN(index_in, HW_IPC_PPCMSG_INDEX_IN_MASK));
134	out32(HW_IPC_PPCCTRL, in32(HW_IPC_PPCCTRL) | HW_IPC_PPCCTRL_OUT);
135
136	retry = 10000000;
137	while (__SHIFTOUT(in32(HW_IPC_ARMMSG), HW_IPC_ARMMSG_INDEX_OUT_MASK) ==
138	       index_out) {
139		timer_udelay(1);
140		if (--retry == 0) {
141#ifdef MINIIPC_DEBUG
142			printf("HW_IPC_PPCMSG: 0x%x\n", in32(HW_IPC_PPCMSG));
143			printf("HW_IPC_ARMMSG: 0x%x\n", in32(HW_IPC_ARMMSG));
144#endif
145			error = ETIMEDOUT;
146			break;
147		}
148	}
149
150	req = &infohdr->ipc_out[index_out];
151	cache_dcbi((void *)req, sizeof(*req));
152
153#ifdef MINIIPC_DEBUG
154	printf("IPC[%u] <- req code 0x%x tag %u args %u %u %u %u %u %u\n",
155	    index_out, req->code, req->tag,
156	    req->args[0], req->args[1], req->args[2], req->args[3],
157	    req->args[4], req->args[5]);
158#endif
159
160	*req_out = *req;
161
162#ifdef MINIIPC_DEBUG
163	printf(" DUMP OUTPUT RING\n");
164	for (int n = 0; n < infohdr->ipc_out_size; n++) {
165		req = &infohdr->ipc_out[n];
166		cache_dcbi((void *)req, sizeof(*req));
167		printf(" debug IPC[%u] <- req code 0x%x tag %u args %u %u %u %u %u %u\n",
168		    n, req->code, req->tag,
169		    req->args[0], req->args[1], req->args[2], req->args[3],
170		    req->args[4], req->args[5]);
171	}
172	printf(" END OUTPUT RING\n");
173#endif
174
175	index_out = miniipc_next_index_out();
176	out32(HW_IPC_PPCMSG,
177	    (in32(HW_IPC_PPCMSG) & ~HW_IPC_PPCMSG_INDEX_OUT_MASK) |
178	     __SHIFTIN(index_out, HW_IPC_PPCMSG_INDEX_OUT_MASK));
179
180	return error;
181}
182
183int
184miniipc_ping(void)
185{
186	ipc_request_t req = {
187		.flags = IPC_FAST,
188		.device = IPC_DEV_SYS,
189		.req = IPC_SYS_PING,
190	};
191
192	return miniipc_sendrecv(&req, &req);
193}
194
195int
196miniipc_sdmmc_ack(uint32_t *ack)
197{
198	ipc_request_t req = {
199		.flags = IPC_SLOW,
200		.device = IPC_DEV_SDMMC,
201		.req = IPC_SDMMC_ACK,
202	};
203	int error;
204
205	error = miniipc_sendrecv(&req, &req);
206	if (error != 0) {
207		*ack = 0;
208		return error;
209	}
210
211	*ack = req.args[0];
212	return 0;
213}
214
215int
216miniipc_sdmmc_state(uint32_t *state)
217{
218	ipc_request_t req = {
219		.flags = IPC_SLOW,
220		.device = IPC_DEV_SDMMC,
221		.req = IPC_SDMMC_STATE,
222	};
223	int error;
224
225	error = miniipc_sendrecv(&req, &req);
226	if (error != 0) {
227		*state = 0;
228		return error;
229	}
230
231	*state = req.args[0];
232	return 0;
233}
234
235int
236miniipc_sdmmc_size(uint32_t *size)
237{
238	ipc_request_t req = {
239		.flags = IPC_SLOW,
240		.device = IPC_DEV_SDMMC,
241		.req = IPC_SDMMC_SIZE,
242	};
243	int error;
244
245	error = miniipc_sendrecv(&req, &req);
246	if (error != 0) {
247		*size = 0;
248		return error;
249	}
250
251	*size = req.args[0];
252	return 0;
253}
254
255int
256miniipc_sdmmc_read(uint32_t start_blkno, uint32_t nblks, void *buf)
257{
258	ipc_request_t req = {
259		.flags = IPC_SLOW,
260		.device = IPC_DEV_SDMMC,
261		.req = IPC_SDMMC_READ,
262		.args = { start_blkno, nblks, (uint32_t)buf },
263	};
264	int error;
265
266	if (((uint32_t)buf & (CACHE_LINE_SIZE - 1)) != 0) {
267		cache_dcbf(buf, nblks * SDMMC_BLOCK_SIZE);
268	}
269	error = miniipc_sendrecv(&req, &req);
270	if (error != 0) {
271		return error;
272	} else if (req.args[0] != 0) {
273		printf("read block %u (nblks = %u) failed: %d\n",
274		    start_blkno, nblks, (int)req.args[0]);
275		error = EIO;
276		return error;
277	}
278	cache_dcbi(buf, nblks * SDMMC_BLOCK_SIZE);
279
280	return 0;
281}
282