1 /* $NetBSD: rtas.c,v 1.16 2025/09/07 21:45:14 thorpej Exp $ */ 2 3 /* 4 * CHRP RTAS support routines 5 * Common Hardware Reference Platform / Run-Time Abstraction Services 6 * 7 * Started by Aymeric Vincent in 2007, public domain. 8 * Modifications by Tim Rightnour 2007. 9 */ 10 11 #include <sys/cdefs.h> 12 __KERNEL_RCSID(0, "$NetBSD: rtas.c,v 1.16 2025/09/07 21:45:14 thorpej Exp $"); 13 14 #include <sys/param.h> 15 #include <sys/systm.h> 16 #include <sys/device.h> 17 #include <sys/errno.h> 18 #include <uvm/uvm.h> 19 20 #include <dev/clock_subr.h> 21 #include <dev/ofw/openfirm.h> 22 23 #include <powerpc/rtas.h> 24 #include <powerpc/psl.h> 25 26 #include <machine/autoconf.h> 27 28 bool machine_has_rtas; 29 30 struct rtas_softc *rtas0_softc; 31 32 struct rtas_softc { 33 device_t ra_dev; 34 int ra_phandle; 35 int ra_version; 36 37 void (*ra_entry_pa)(paddr_t, paddr_t); 38 paddr_t ra_base_pa; 39 40 struct todr_chip_handle ra_todr_handle; 41 }; 42 43 static struct { 44 int token; 45 int exists; 46 } rtas_function_token[RTAS_FUNC_number]; 47 48 static struct { 49 const char *name; 50 int index; 51 } rtas_function_lookup[] = { 52 { "restart-rtas", RTAS_FUNC_RESTART_RTAS }, 53 { "nvram-fetch", RTAS_FUNC_NVRAM_FETCH }, 54 { "nvram-store", RTAS_FUNC_NVRAM_STORE }, 55 { "get-time-of-day", RTAS_FUNC_GET_TIME_OF_DAY }, 56 { "set-time-of-day", RTAS_FUNC_SET_TIME_OF_DAY }, 57 { "set-time-for-power-on", RTAS_FUNC_SET_TIME_FOR_POWER_ON }, 58 { "event-scan", RTAS_FUNC_EVENT_SCAN }, 59 { "check-exception", RTAS_FUNC_CHECK_EXCEPTION }, 60 /* Typo in my Efika's firmware */ 61 { "check-execption", RTAS_FUNC_CHECK_EXCEPTION }, 62 { "read-pci-config", RTAS_FUNC_READ_PCI_CONFIG }, 63 { "write-pci-config", RTAS_FUNC_WRITE_PCI_CONFIG }, 64 { "display-character", RTAS_FUNC_DISPLAY_CHARACTER }, 65 { "set-indicator", RTAS_FUNC_SET_INDICATOR }, 66 { "power-off", RTAS_FUNC_POWER_OFF }, 67 { "suspend", RTAS_FUNC_SUSPEND }, 68 { "hibernate", RTAS_FUNC_HIBERNATE }, 69 { "system-reboot", RTAS_FUNC_SYSTEM_REBOOT }, 70 { "freeze-time-base", RTAS_FUNC_FREEZE_TIME_BASE }, 71 { "thaw-time-base", RTAS_FUNC_THAW_TIME_BASE }, 72 }; 73 74 static int rtas_match(device_t, cfdata_t, void *); 75 static void rtas_attach(device_t, device_t, void *); 76 static int rtas_detach(device_t, int); 77 static int rtas_activate(device_t, enum devact); 78 static int rtas_todr_gettime_ymdhms(struct todr_chip_handle *, 79 struct clock_ymdhms *); 80 static int rtas_todr_settime_ymdhms(struct todr_chip_handle *, 81 struct clock_ymdhms *); 82 83 CFATTACH_DECL_NEW(rtas, sizeof (struct rtas_softc), 84 rtas_match, rtas_attach, rtas_detach, rtas_activate); 85 86 static int 87 rtas_match(device_t parent, cfdata_t match, void *aux) 88 { 89 struct confargs *ca = aux; 90 91 if (strcmp(ca->ca_name, "rtas")) 92 return 0; 93 94 return 1; 95 } 96 97 static void 98 rtas_attach(device_t parent, device_t self, void *aux) 99 { 100 struct confargs *ca = aux; 101 struct rtas_softc *sc = device_private(self); 102 int ph = ca->ca_node; 103 int ih; 104 int rtas_size; 105 uintptr_t rtas_entry; 106 struct pglist pglist; 107 char buf[4]; 108 int i; 109 110 machine_has_rtas = true; 111 112 sc->ra_dev = self; 113 sc->ra_phandle = ph; 114 if (OF_getprop(ph, "rtas-version", buf, sizeof buf) != sizeof buf) 115 goto fail; 116 sc->ra_version = of_decode_int(buf); 117 if (OF_getprop(ph, "rtas-size", buf, sizeof buf) != sizeof buf) 118 goto fail; 119 rtas_size = of_decode_int(buf); 120 121 /* 122 * Instantiate the RTAS. 123 * The physical base address should be in the first 256 MB segment. 124 */ 125 if (uvm_pglistalloc(rtas_size, 0x100000, 0x0fffffff, 4096, 256 << 20, 126 &pglist, 1, 0)) 127 goto fail; 128 129 sc->ra_base_pa = VM_PAGE_TO_PHYS(TAILQ_FIRST(&pglist)); 130 131 ih = OF_open("/rtas"); 132 if (ih == -1) 133 goto fail_and_free; 134 135 rtas_entry = 136 OF_call_method_1("instantiate-rtas", ih, 1, sc->ra_base_pa); 137 138 if (rtas_entry == -1) 139 goto fail_and_free; 140 141 sc->ra_entry_pa = (void *) rtas_entry; 142 143 /* 144 * Get the tokens of the methods the RTAS provides 145 */ 146 147 for (i = 0; 148 i < sizeof rtas_function_lookup / sizeof rtas_function_lookup[0]; 149 i++) { 150 int index = rtas_function_lookup[i].index; 151 152 if (OF_getprop(ph, rtas_function_lookup[i].name, buf, 153 sizeof buf) != sizeof buf) 154 continue; 155 156 rtas_function_token[index].token = of_decode_int(buf); 157 rtas_function_token[index].exists = 1; 158 } 159 160 rtas0_softc = sc; 161 162 printf(": version %d, entry @pa 0x%"PRIxPTR"\n", sc->ra_version, 163 rtas_entry); 164 165 /* 166 * Initialise TODR support 167 */ 168 sc->ra_todr_handle.todr_dev = self; 169 sc->ra_todr_handle.todr_gettime_ymdhms = rtas_todr_gettime_ymdhms; 170 sc->ra_todr_handle.todr_settime_ymdhms = rtas_todr_settime_ymdhms; 171 todr_attach(&sc->ra_todr_handle); 172 173 return; 174 175 fail_and_free: 176 uvm_pglistfree(&pglist); 177 fail: 178 aprint_error(": attach failed!\n"); 179 } 180 181 static int 182 rtas_detach(device_t self, int flags) 183 { 184 return EOPNOTSUPP; 185 } 186 187 static int 188 rtas_activate(device_t self, enum devact act) 189 { 190 return EOPNOTSUPP; 191 } 192 193 /* 194 * Support for calling to the RTAS 195 */ 196 197 int 198 rtas_call(int token, int nargs, int nreturns, ...) 199 { 200 va_list ap; 201 static struct { 202 int token; 203 int nargs; 204 int nreturns; 205 int args_n_results[RTAS_MAXARGS]; 206 } args; 207 paddr_t pargs = (paddr_t)&args; 208 paddr_t base; 209 register_t msr; 210 void (*entry)(paddr_t, paddr_t); 211 int n; 212 213 if (rtas0_softc == NULL) 214 return -1; 215 216 if (nargs + nreturns > RTAS_MAXARGS) 217 return -1; 218 219 if (!rtas_function_token[token].exists) 220 return -1; 221 222 base = rtas0_softc->ra_base_pa; 223 entry = rtas0_softc->ra_entry_pa; 224 225 memset(args.args_n_results, 0, RTAS_MAXARGS * sizeof(int)); 226 args.nargs = nargs; 227 args.nreturns = nreturns; 228 args.token = rtas_function_token[token].token; 229 230 va_start(ap, nreturns); 231 for (n=0; n < nargs && n < RTAS_MAXARGS; n++) 232 args.args_n_results[n] = va_arg(ap, int); 233 234 __insn_barrier(); 235 msr = mfmsr(); 236 mtmsr(msr & ~(PSL_EE | PSL_FP | PSL_ME | PSL_FE0 | PSL_SE | PSL_BE | 237 PSL_FE1 | PSL_IR | PSL_DR | PSL_RI)); 238 __asm("isync;\n"); 239 240 entry(pargs, base); 241 242 mtmsr(msr); 243 __asm("isync;\n"); 244 245 for (n = nargs; n < nargs + nreturns && n < RTAS_MAXARGS; n++) 246 *va_arg(ap, int *) = args.args_n_results[n]; 247 248 va_end(ap); 249 250 return args.args_n_results[nargs]; 251 } 252 253 int 254 rtas_has_func(int token) 255 { 256 return rtas_function_token[token].exists; 257 } 258 259 /* 260 * Real-Time Clock support 261 */ 262 263 static int 264 rtas_todr_gettime_ymdhms(struct todr_chip_handle *h, struct clock_ymdhms *t) 265 { 266 int status, year, month, day, hour, minute, second, nanosecond; 267 268 if (!rtas_function_token[RTAS_FUNC_GET_TIME_OF_DAY].exists) 269 return ENXIO; 270 271 if (rtas_call(RTAS_FUNC_GET_TIME_OF_DAY, 0, 8, &status, &year, 272 &month, &day, &hour, &minute, &second, &nanosecond) < 0) 273 return ENXIO; 274 275 t->dt_year = year; 276 t->dt_mon = month; 277 t->dt_day = day; 278 t->dt_hour = hour; 279 t->dt_min = minute; 280 t->dt_sec = second; 281 282 return 0; 283 } 284 285 static int 286 rtas_todr_settime_ymdhms(struct todr_chip_handle *h, struct clock_ymdhms *t) 287 { 288 int status, year, month, day, hour, minute, second, nanosecond; 289 290 if (!rtas_function_token[RTAS_FUNC_SET_TIME_OF_DAY].exists) 291 return ENXIO; 292 293 year = t->dt_year; 294 month = t->dt_mon; 295 day = t->dt_day; 296 hour = t->dt_hour; 297 minute = t->dt_min; 298 second = t->dt_sec; 299 nanosecond = 0; 300 301 if (rtas_call(RTAS_FUNC_SET_TIME_OF_DAY, 7, 1, year, month, 302 day, hour, minute, second, nanosecond, &status) < 0) 303 return ENXIO; 304 305 return 0; 306 } 307