11.1Sjmcneill/* $NetBSD: miniipc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2025 Jared McNeill <jmcneill@invisible.ca>
51.1Sjmcneill * All rights reserved.
61.1Sjmcneill *
71.1Sjmcneill * Redistribution and use in source and binary forms, with or without
81.1Sjmcneill * modification, are permitted provided that the following conditions
91.1Sjmcneill * are met:
101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright
111.1Sjmcneill *    notice, this list of conditions and the following disclaimer.
121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
131.1Sjmcneill *    notice, this list of conditions and the following disclaimer in the
141.1Sjmcneill *    documentation and/or other materials provided with the distribution.
151.1Sjmcneill *
161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
171.1Sjmcneill * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
181.1Sjmcneill * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
191.1Sjmcneill * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
201.1Sjmcneill * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
211.1Sjmcneill * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
221.1Sjmcneill * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
231.1Sjmcneill * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
241.1Sjmcneill * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
251.1Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
261.1Sjmcneill * SUCH DAMAGE.
271.1Sjmcneill */
281.1Sjmcneill
291.1Sjmcneill#include <lib/libsa/stand.h>
301.1Sjmcneill
311.1Sjmcneill#include <machine/pio.h>
321.1Sjmcneill
331.1Sjmcneill#include "miniipc.h"
341.1Sjmcneill#include "sdmmc.h"
351.1Sjmcneill#include "cache.h"
361.1Sjmcneill#include "timer.h"
371.1Sjmcneill
381.1Sjmcneill#define MINIIPC_INFO_PTR		0x13fffffc
391.1Sjmcneill
401.1Sjmcneill#define HW_BASE				0x0d800000
411.1Sjmcneill#define	HW_IPC_PPCMSG			(HW_BASE + 0x00)
421.1Sjmcneill#define  HW_IPC_PPCMSG_INDEX_OUT_MASK	__BITS(31, 16)
431.1Sjmcneill#define  HW_IPC_PPCMSG_INDEX_IN_MASK	__BITS(15, 0)
441.1Sjmcneill#define HW_IPC_PPCCTRL			(HW_BASE + 0x04)
451.1Sjmcneill#define  HW_IPC_PPCCTRL_IN		__BIT(2)
461.1Sjmcneill#define  HW_IPC_PPCCTRL_OUT		__BIT(0)
471.1Sjmcneill#define HW_IPC_ARMMSG			(HW_BASE + 0x08)
481.1Sjmcneill#define  HW_IPC_ARMMSG_INDEX_OUT_MASK	__BITS(15, 0)
491.1Sjmcneill
501.1Sjmcneill/* IPC request flags */
511.1Sjmcneill#define IPC_SLOW			0x00
521.1Sjmcneill#define IPC_FAST			0x01
531.1Sjmcneill
541.1Sjmcneill/* IPC devices and requests */
551.1Sjmcneill#define IPC_DEV_SYS			0x00
561.1Sjmcneill#define  IPC_SYS_PING			0x0000
571.1Sjmcneill#define IPC_DEV_SDMMC			0x07
581.1Sjmcneill#define  IPC_SDMMC_ACK			0x0000
591.1Sjmcneill#define  IPC_SDMMC_READ			0x0001
601.1Sjmcneill#define  IPC_SDMMC_WRITE		0x0002
611.1Sjmcneill#define  IPC_SDMMC_STATE		0x0003
621.1Sjmcneill#define  IPC_SDMMC_SIZE			0x0004
631.1Sjmcneill
641.1Sjmcneillstatic volatile ipc_info_header_t *infohdr;
651.1Sjmcneillstatic uint32_t index_in;
661.1Sjmcneillstatic uint32_t index_out;
671.1Sjmcneillstatic uint32_t request_tag;
681.1Sjmcneill
691.1Sjmcneillstatic uint32_t
701.1Sjmcneillminiipc_next_index_in(void)
711.1Sjmcneill{
721.1Sjmcneill	return (index_in + 1) % infohdr->ipc_in_size;
731.1Sjmcneill}
741.1Sjmcneill
751.1Sjmcneillstatic uint32_t
761.1Sjmcneillminiipc_next_index_out(void)
771.1Sjmcneill{
781.1Sjmcneill	return (index_out + 1) % infohdr->ipc_out_size;
791.1Sjmcneill}
801.1Sjmcneill
811.1Sjmcneillbool
821.1Sjmcneillminiipc_probe(void)
831.1Sjmcneill{
841.1Sjmcneill	paddr_t pa;
851.1Sjmcneill
861.1Sjmcneill	pa = in32(MINIIPC_INFO_PTR);
871.1Sjmcneill	if (pa == 0 || pa == 0xffffffff) {
881.1Sjmcneill		printf("No MINI IPC pointer at 0x%x\n", MINIIPC_INFO_PTR);
891.1Sjmcneill		return false;
901.1Sjmcneill	}
911.1Sjmcneill	infohdr = (ipc_info_header_t *)pa;
921.1Sjmcneill	cache_dcbi((void *)infohdr, sizeof(*infohdr));
931.1Sjmcneill
941.1Sjmcneill	if (memcmp((void *)infohdr->magic, "IPC", 3) != 0) {
951.1Sjmcneill		return false;
961.1Sjmcneill	}
971.1Sjmcneill
981.1Sjmcneill	index_in = __SHIFTOUT(in32(HW_IPC_PPCMSG), HW_IPC_PPCMSG_INDEX_IN_MASK);
991.1Sjmcneill	index_out = __SHIFTOUT(in32(HW_IPC_PPCMSG), HW_IPC_PPCMSG_INDEX_OUT_MASK);
1001.1Sjmcneill
1011.1Sjmcneill	miniipc_ping();
1021.1Sjmcneill
1031.1Sjmcneill	return true;
1041.1Sjmcneill}
1051.1Sjmcneill
1061.1Sjmcneillstatic int
1071.1Sjmcneillminiipc_sendrecv(const ipc_request_t *req_in, ipc_request_t *req_out)
1081.1Sjmcneill{
1091.1Sjmcneill	volatile ipc_request_t *req;
1101.1Sjmcneill	int retry, error = 0;
1111.1Sjmcneill
1121.1Sjmcneill	req = &infohdr->ipc_in[index_in];
1131.1Sjmcneill
1141.1Sjmcneill	*req = *req_in;
1151.1Sjmcneill	req->tag = ++request_tag;
1161.1Sjmcneill
1171.1Sjmcneill#ifdef MINIIPC_DEBUG
1181.1Sjmcneill	printf("IPC[%u] -> req code 0x%x tag %u args %u %u %u %u %u %u\n",
1191.1Sjmcneill	    index_in, req->code, req->tag,
1201.1Sjmcneill	    req->args[0], req->args[1], req->args[2], req->args[3],
1211.1Sjmcneill	    req->args[4], req->args[5]);
1221.1Sjmcneill#endif
1231.1Sjmcneill
1241.1Sjmcneill	cache_dcbf((void *)req, sizeof(*req));
1251.1Sjmcneill
1261.1Sjmcneill	req = &infohdr->ipc_out[index_out];
1271.1Sjmcneill	cache_dcbf((void *)req, sizeof(*req));
1281.1Sjmcneill
1291.1Sjmcneill	index_in = miniipc_next_index_in();
1301.1Sjmcneill
1311.1Sjmcneill	out32(HW_IPC_PPCMSG,
1321.1Sjmcneill	    (in32(HW_IPC_PPCMSG) & ~HW_IPC_PPCMSG_INDEX_IN_MASK) |
1331.1Sjmcneill	     __SHIFTIN(index_in, HW_IPC_PPCMSG_INDEX_IN_MASK));
1341.1Sjmcneill	out32(HW_IPC_PPCCTRL, in32(HW_IPC_PPCCTRL) | HW_IPC_PPCCTRL_OUT);
1351.1Sjmcneill
1361.1Sjmcneill	retry = 10000000;
1371.1Sjmcneill	while (__SHIFTOUT(in32(HW_IPC_ARMMSG), HW_IPC_ARMMSG_INDEX_OUT_MASK) ==
1381.1Sjmcneill	       index_out) {
1391.1Sjmcneill		timer_udelay(1);
1401.1Sjmcneill		if (--retry == 0) {
1411.1Sjmcneill#ifdef MINIIPC_DEBUG
1421.1Sjmcneill			printf("HW_IPC_PPCMSG: 0x%x\n", in32(HW_IPC_PPCMSG));
1431.1Sjmcneill			printf("HW_IPC_ARMMSG: 0x%x\n", in32(HW_IPC_ARMMSG));
1441.1Sjmcneill#endif
1451.1Sjmcneill			error = ETIMEDOUT;
1461.1Sjmcneill			break;
1471.1Sjmcneill		}
1481.1Sjmcneill	}
1491.1Sjmcneill
1501.1Sjmcneill	req = &infohdr->ipc_out[index_out];
1511.1Sjmcneill	cache_dcbi((void *)req, sizeof(*req));
1521.1Sjmcneill
1531.1Sjmcneill#ifdef MINIIPC_DEBUG
1541.1Sjmcneill	printf("IPC[%u] <- req code 0x%x tag %u args %u %u %u %u %u %u\n",
1551.1Sjmcneill	    index_out, req->code, req->tag,
1561.1Sjmcneill	    req->args[0], req->args[1], req->args[2], req->args[3],
1571.1Sjmcneill	    req->args[4], req->args[5]);
1581.1Sjmcneill#endif
1591.1Sjmcneill
1601.1Sjmcneill	*req_out = *req;
1611.1Sjmcneill
1621.1Sjmcneill#ifdef MINIIPC_DEBUG
1631.1Sjmcneill	printf(" DUMP OUTPUT RING\n");
1641.1Sjmcneill	for (int n = 0; n < infohdr->ipc_out_size; n++) {
1651.1Sjmcneill		req = &infohdr->ipc_out[n];
1661.1Sjmcneill		cache_dcbi((void *)req, sizeof(*req));
1671.1Sjmcneill		printf(" debug IPC[%u] <- req code 0x%x tag %u args %u %u %u %u %u %u\n",
1681.1Sjmcneill		    n, req->code, req->tag,
1691.1Sjmcneill		    req->args[0], req->args[1], req->args[2], req->args[3],
1701.1Sjmcneill		    req->args[4], req->args[5]);
1711.1Sjmcneill	}
1721.1Sjmcneill	printf(" END OUTPUT RING\n");
1731.1Sjmcneill#endif
1741.1Sjmcneill
1751.1Sjmcneill	index_out = miniipc_next_index_out();
1761.1Sjmcneill	out32(HW_IPC_PPCMSG,
1771.1Sjmcneill	    (in32(HW_IPC_PPCMSG) & ~HW_IPC_PPCMSG_INDEX_OUT_MASK) |
1781.1Sjmcneill	     __SHIFTIN(index_out, HW_IPC_PPCMSG_INDEX_OUT_MASK));
1791.1Sjmcneill
1801.1Sjmcneill	return error;
1811.1Sjmcneill}
1821.1Sjmcneill
1831.1Sjmcneillint
1841.1Sjmcneillminiipc_ping(void)
1851.1Sjmcneill{
1861.1Sjmcneill	ipc_request_t req = {
1871.1Sjmcneill		.flags = IPC_FAST,
1881.1Sjmcneill		.device = IPC_DEV_SYS,
1891.1Sjmcneill		.req = IPC_SYS_PING,
1901.1Sjmcneill	};
1911.1Sjmcneill
1921.1Sjmcneill	return miniipc_sendrecv(&req, &req);
1931.1Sjmcneill}
1941.1Sjmcneill
1951.1Sjmcneillint
1961.1Sjmcneillminiipc_sdmmc_ack(uint32_t *ack)
1971.1Sjmcneill{
1981.1Sjmcneill	ipc_request_t req = {
1991.1Sjmcneill		.flags = IPC_SLOW,
2001.1Sjmcneill		.device = IPC_DEV_SDMMC,
2011.1Sjmcneill		.req = IPC_SDMMC_ACK,
2021.1Sjmcneill	};
2031.1Sjmcneill	int error;
2041.1Sjmcneill
2051.1Sjmcneill	error = miniipc_sendrecv(&req, &req);
2061.1Sjmcneill	if (error != 0) {
2071.1Sjmcneill		*ack = 0;
2081.1Sjmcneill		return error;
2091.1Sjmcneill	}
2101.1Sjmcneill
2111.1Sjmcneill	*ack = req.args[0];
2121.1Sjmcneill	return 0;
2131.1Sjmcneill}
2141.1Sjmcneill
2151.1Sjmcneillint
2161.1Sjmcneillminiipc_sdmmc_state(uint32_t *state)
2171.1Sjmcneill{
2181.1Sjmcneill	ipc_request_t req = {
2191.1Sjmcneill		.flags = IPC_SLOW,
2201.1Sjmcneill		.device = IPC_DEV_SDMMC,
2211.1Sjmcneill		.req = IPC_SDMMC_STATE,
2221.1Sjmcneill	};
2231.1Sjmcneill	int error;
2241.1Sjmcneill
2251.1Sjmcneill	error = miniipc_sendrecv(&req, &req);
2261.1Sjmcneill	if (error != 0) {
2271.1Sjmcneill		*state = 0;
2281.1Sjmcneill		return error;
2291.1Sjmcneill	}
2301.1Sjmcneill
2311.1Sjmcneill	*state = req.args[0];
2321.1Sjmcneill	return 0;
2331.1Sjmcneill}
2341.1Sjmcneill
2351.1Sjmcneillint
2361.1Sjmcneillminiipc_sdmmc_size(uint32_t *size)
2371.1Sjmcneill{
2381.1Sjmcneill	ipc_request_t req = {
2391.1Sjmcneill		.flags = IPC_SLOW,
2401.1Sjmcneill		.device = IPC_DEV_SDMMC,
2411.1Sjmcneill		.req = IPC_SDMMC_SIZE,
2421.1Sjmcneill	};
2431.1Sjmcneill	int error;
2441.1Sjmcneill
2451.1Sjmcneill	error = miniipc_sendrecv(&req, &req);
2461.1Sjmcneill	if (error != 0) {
2471.1Sjmcneill		*size = 0;
2481.1Sjmcneill		return error;
2491.1Sjmcneill	}
2501.1Sjmcneill
2511.1Sjmcneill	*size = req.args[0];
2521.1Sjmcneill	return 0;
2531.1Sjmcneill}
2541.1Sjmcneill
2551.1Sjmcneillint
2561.1Sjmcneillminiipc_sdmmc_read(uint32_t start_blkno, uint32_t nblks, void *buf)
2571.1Sjmcneill{
2581.1Sjmcneill	ipc_request_t req = {
2591.1Sjmcneill		.flags = IPC_SLOW,
2601.1Sjmcneill		.device = IPC_DEV_SDMMC,
2611.1Sjmcneill		.req = IPC_SDMMC_READ,
2621.1Sjmcneill		.args = { start_blkno, nblks, (uint32_t)buf },
2631.1Sjmcneill	};
2641.1Sjmcneill	int error;
2651.1Sjmcneill
2661.1Sjmcneill	if (((uint32_t)buf & (CACHE_LINE_SIZE - 1)) != 0) {
2671.1Sjmcneill		cache_dcbf(buf, nblks * SDMMC_BLOCK_SIZE);
2681.1Sjmcneill	}
2691.1Sjmcneill	error = miniipc_sendrecv(&req, &req);
2701.1Sjmcneill	if (error != 0) {
2711.1Sjmcneill		return error;
2721.1Sjmcneill	} else if (req.args[0] != 0) {
2731.1Sjmcneill		printf("read block %u (nblks = %u) failed: %d\n",
2741.1Sjmcneill		    start_blkno, nblks, (int)req.args[0]);
2751.1Sjmcneill		error = EIO;
2761.1Sjmcneill		return error;
2771.1Sjmcneill	}
2781.1Sjmcneill	cache_dcbi(buf, nblks * SDMMC_BLOCK_SIZE);
2791.1Sjmcneill
2801.1Sjmcneill	return 0;
2811.1Sjmcneill}
282