Home | History | Annotate | Line # | Download | only in wii
      1 /* $NetBSD: miniipc.c,v 1.1 2025/11/16 20:11:47 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2025 Jared McNeill <jmcneill (at) 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 
     64 static volatile ipc_info_header_t *infohdr;
     65 static uint32_t index_in;
     66 static uint32_t index_out;
     67 static uint32_t request_tag;
     68 
     69 static uint32_t
     70 miniipc_next_index_in(void)
     71 {
     72 	return (index_in + 1) % infohdr->ipc_in_size;
     73 }
     74 
     75 static uint32_t
     76 miniipc_next_index_out(void)
     77 {
     78 	return (index_out + 1) % infohdr->ipc_out_size;
     79 }
     80 
     81 bool
     82 miniipc_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 
    106 static int
    107 miniipc_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 
    183 int
    184 miniipc_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 
    195 int
    196 miniipc_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 
    215 int
    216 miniipc_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 
    235 int
    236 miniipc_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 
    255 int
    256 miniipc_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