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