Home | History | Annotate | Line # | Download | only in powerpc
      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