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