misc.c 7.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/* $Id: misc.c,v 1.20 2001/09/21 03:17:07 kanoj Exp $
 * misc.c:  Miscellaneous prom functions that don't belong
 *          anywhere else.
 *
 * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
 * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
 */

#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <asm/openprom.h>
#include <asm/oplib.h>
#include <asm/system.h>

/* Reset and reboot the machine with the command 'bcommand'. */
20
void prom_reboot(const char *bcommand)
L
Linus Torvalds 已提交
21 22 23 24 25 26
{
	p1275_cmd("boot", P1275_ARG(0, P1275_ARG_IN_STRING) |
		  P1275_INOUT(1, 0), bcommand);
}

/* Forth evaluate the expression contained in 'fstring'. */
27
void prom_feval(const char *fstring)
L
Linus Torvalds 已提交
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
{
	if (!fstring || fstring[0] == 0)
		return;
	p1275_cmd("interpret", P1275_ARG(0, P1275_ARG_IN_STRING) |
		  P1275_INOUT(1, 1), fstring);
}

/* We want to do this more nicely some day. */
extern void (*prom_palette)(int);

#ifdef CONFIG_SMP
extern void smp_capture(void);
extern void smp_release(void);
#endif

/* Drop into the prom, with the chance to continue with the 'go'
 * prom command.
 */
void prom_cmdline(void)
{
	unsigned long flags;

	local_irq_save(flags);

	if (!serial_console && prom_palette)
		prom_palette(1);

#ifdef CONFIG_SMP
	smp_capture();
#endif

	p1275_cmd("enter", P1275_INOUT(0, 0));

#ifdef CONFIG_SMP
	smp_release();
#endif

	if (!serial_console && prom_palette)
		prom_palette(0);

	local_irq_restore(flags);
}

/* Drop into the prom, but completely terminate the program.
 * No chance of continuing.
 */
void prom_halt(void)
{
again:
	p1275_cmd("exit", P1275_INOUT(0, 0));
	goto again; /* PROM is out to get me -DaveM */
}

void prom_halt_power_off(void)
{
	p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0));

	/* if nothing else helps, we just halt */
	prom_halt();
}

/* Set prom sync handler to call function 'funcp'. */
void prom_setcallback(callback_func_t funcp)
{
	if (!funcp)
		return;
	p1275_cmd("set-callback", P1275_ARG(0, P1275_ARG_IN_FUNCTION) |
		  P1275_INOUT(1, 1), funcp);
}

/* Get the idprom and stuff it into buffer 'idbuf'.  Returns the
 * format type.  'num_bytes' is the number of bytes that your idbuf
 * has space for.  Returns 0xff on error.
 */
unsigned char prom_get_idprom(char *idbuf, int num_bytes)
{
	int len;

	len = prom_getproplen(prom_root_node, "idprom");
	if ((len >num_bytes) || (len == -1))
		return 0xff;
	if (!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
		return idbuf[0];

	return 0xff;
}

/* Get the major prom version number. */
int prom_version(void)
{
	return PROM_P1275;
}

/* Get the prom plugin-revision. */
int prom_getrev(void)
{
	return prom_rev;
}

/* Get the prom firmware print revision. */
int prom_getprev(void)
{
	return prom_prev;
}

/* Install Linux trap table so PROM uses that instead of its own. */
void prom_set_trap_table(unsigned long tba)
{
	p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba);
}

int prom_get_mmu_ihandle(void)
{
	int node, ret;

143 144
	if (prom_mmu_ihandle_cache != 0)
		return prom_mmu_ihandle_cache;
L
Linus Torvalds 已提交
145

146 147
	node = prom_finddevice(prom_chosen_path);
	ret = prom_getint(node, prom_mmu_name);
L
Linus Torvalds 已提交
148
	if (ret == -1 || ret == 0)
149
		prom_mmu_ihandle_cache = -1;
L
Linus Torvalds 已提交
150
	else
151
		prom_mmu_ihandle_cache = ret;
L
Linus Torvalds 已提交
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178

	return ret;
}

static int prom_get_memory_ihandle(void)
{
	static int memory_ihandle_cache;
	int node, ret;

	if (memory_ihandle_cache != 0)
		return memory_ihandle_cache;

	node = prom_finddevice("/chosen");
	ret = prom_getint(node, "memory");
	if (ret == -1 || ret == 0)
		memory_ihandle_cache = -1;
	else
		memory_ihandle_cache = ret;

	return ret;
}

/* Load explicit I/D TLB entries. */
long prom_itlb_load(unsigned long index,
		    unsigned long tte_data,
		    unsigned long vaddr)
{
179
	return p1275_cmd(prom_callmethod_name,
L
Linus Torvalds 已提交
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
			  P1275_ARG(2, P1275_ARG_IN_64B) |
			  P1275_ARG(3, P1275_ARG_IN_64B) |
			  P1275_INOUT(5, 1)),
			 "SUNW,itlb-load",
			 prom_get_mmu_ihandle(),
			 /* And then our actual args are pushed backwards. */
			 vaddr,
			 tte_data,
			 index);
}

long prom_dtlb_load(unsigned long index,
		    unsigned long tte_data,
		    unsigned long vaddr)
{
196
	return p1275_cmd(prom_callmethod_name,
L
Linus Torvalds 已提交
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
			 (P1275_ARG(0, P1275_ARG_IN_STRING) |
			  P1275_ARG(2, P1275_ARG_IN_64B) |
			  P1275_ARG(3, P1275_ARG_IN_64B) |
			  P1275_INOUT(5, 1)),
			 "SUNW,dtlb-load",
			 prom_get_mmu_ihandle(),
			 /* And then our actual args are pushed backwards. */
			 vaddr,
			 tte_data,
			 index);
}

int prom_map(int mode, unsigned long size,
	     unsigned long vaddr, unsigned long paddr)
{
212
	int ret = p1275_cmd(prom_callmethod_name,
L
Linus Torvalds 已提交
213 214 215 216 217
			    (P1275_ARG(0, P1275_ARG_IN_STRING) |
			     P1275_ARG(3, P1275_ARG_IN_64B) |
			     P1275_ARG(4, P1275_ARG_IN_64B) |
			     P1275_ARG(6, P1275_ARG_IN_64B) |
			     P1275_INOUT(7, 1)),
218
			    prom_map_name,
L
Linus Torvalds 已提交
219 220 221 222 223 224 225 226 227 228 229 230 231 232
			    prom_get_mmu_ihandle(),
			    mode,
			    size,
			    vaddr,
			    0,
			    paddr);

	if (ret == 0)
		ret = -1;
	return ret;
}

void prom_unmap(unsigned long size, unsigned long vaddr)
{
233
	p1275_cmd(prom_callmethod_name,
L
Linus Torvalds 已提交
234 235 236 237
		  (P1275_ARG(0, P1275_ARG_IN_STRING) |
		   P1275_ARG(2, P1275_ARG_IN_64B) |
		   P1275_ARG(3, P1275_ARG_IN_64B) |
		   P1275_INOUT(4, 0)),
238
		  prom_unmap_name,
L
Linus Torvalds 已提交
239 240 241 242 243 244 245 246
		  prom_get_mmu_ihandle(),
		  size,
		  vaddr);
}

/* Set aside physical memory which is not touched or modified
 * across soft resets.
 */
247
unsigned long prom_retain(const char *name,
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
			  unsigned long pa_low, unsigned long pa_high,
			  long size, long align)
{
	/* XXX I don't think we return multiple values correctly.
	 * XXX OBP supposedly returns pa_low/pa_high here, how does
	 * XXX it work?
	 */

	/* If align is zero, the pa_low/pa_high args are passed,
	 * else they are not.
	 */
	if (align == 0)
		return p1275_cmd("SUNW,retain",
				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)),
				 name, pa_low, pa_high, size, align);
	else
		return p1275_cmd("SUNW,retain",
				 (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)),
				 name, size, align);
}

/* Get "Unumber" string for the SIMM at the given
 * memory address.  Usually this will be of the form
 * "Uxxxx" where xxxx is a decimal number which is
 * etched into the motherboard next to the SIMM slot
 * in question.
 */
int prom_getunumber(int syndrome_code,
		    unsigned long phys_addr,
		    char *buf, int buflen)
{
279
	return p1275_cmd(prom_callmethod_name,
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325
			 (P1275_ARG(0, P1275_ARG_IN_STRING)	|
			  P1275_ARG(3, P1275_ARG_OUT_BUF)	|
			  P1275_ARG(6, P1275_ARG_IN_64B)	|
			  P1275_INOUT(8, 2)),
			 "SUNW,get-unumber", prom_get_memory_ihandle(),
			 buflen, buf, P1275_SIZE(buflen),
			 0, phys_addr, syndrome_code);
}

/* Power management extensions. */
void prom_sleepself(void)
{
	p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0));
}

int prom_sleepsystem(void)
{
	return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1));
}

int prom_wakeupsystem(void)
{
	return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1));
}

#ifdef CONFIG_SMP
void prom_startcpu(int cpunode, unsigned long pc, unsigned long o0)
{
	p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0);
}

void prom_stopself(void)
{
	p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0));
}

void prom_idleself(void)
{
	p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0));
}

void prom_resumecpu(int cpunode)
{
	p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode);
}
#endif