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