Linux Audio

Check our new training course

Loading...
v4.17
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 *   Copyright (C) 2000 Tilmann Bitterberg
  4 *   (tilmann@bitterberg.de)
  5 *
  6 *   RTAS (Runtime Abstraction Services) stuff
  7 *   Intention is to provide a clean user interface
  8 *   to use the RTAS.
  9 *
 10 *   TODO:
 11 *   Split off a header file and maybe move it to a different
 12 *   location. Write Documentation on what the /proc/rtas/ entries
 13 *   actually do.
 14 */
 15
 16#include <linux/errno.h>
 17#include <linux/sched.h>
 18#include <linux/proc_fs.h>
 19#include <linux/stat.h>
 20#include <linux/ctype.h>
 21#include <linux/time.h>
 22#include <linux/string.h>
 23#include <linux/init.h>
 24#include <linux/seq_file.h>
 25#include <linux/bitops.h>
 26#include <linux/rtc.h>
 27
 28#include <linux/uaccess.h>
 29#include <asm/processor.h>
 30#include <asm/io.h>
 31#include <asm/prom.h>
 32#include <asm/rtas.h>
 33#include <asm/machdep.h> /* for ppc_md */
 34#include <asm/time.h>
 35
 36/* Token for Sensors */
 37#define KEY_SWITCH		0x0001
 38#define ENCLOSURE_SWITCH	0x0002
 39#define THERMAL_SENSOR		0x0003
 40#define LID_STATUS		0x0004
 41#define POWER_SOURCE		0x0005
 42#define BATTERY_VOLTAGE		0x0006
 43#define BATTERY_REMAINING	0x0007
 44#define BATTERY_PERCENTAGE	0x0008
 45#define EPOW_SENSOR		0x0009
 46#define BATTERY_CYCLESTATE	0x000a
 47#define BATTERY_CHARGING	0x000b
 48
 49/* IBM specific sensors */
 50#define IBM_SURVEILLANCE	0x2328 /* 9000 */
 51#define IBM_FANRPM		0x2329 /* 9001 */
 52#define IBM_VOLTAGE		0x232a /* 9002 */
 53#define IBM_DRCONNECTOR		0x232b /* 9003 */
 54#define IBM_POWERSUPPLY		0x232c /* 9004 */
 55
 56/* Status return values */
 57#define SENSOR_CRITICAL_HIGH	13
 58#define SENSOR_WARNING_HIGH	12
 59#define SENSOR_NORMAL		11
 60#define SENSOR_WARNING_LOW	10
 61#define SENSOR_CRITICAL_LOW	 9
 62#define SENSOR_SUCCESS		 0
 63#define SENSOR_HW_ERROR		-1
 64#define SENSOR_BUSY		-2
 65#define SENSOR_NOT_EXIST	-3
 66#define SENSOR_DR_ENTITY	-9000
 67
 68/* Location Codes */
 69#define LOC_SCSI_DEV_ADDR	'A'
 70#define LOC_SCSI_DEV_LOC	'B'
 71#define LOC_CPU			'C'
 72#define LOC_DISKETTE		'D'
 73#define LOC_ETHERNET		'E'
 74#define LOC_FAN			'F'
 75#define LOC_GRAPHICS		'G'
 76/* reserved / not used		'H' */
 77#define LOC_IO_ADAPTER		'I'
 78/* reserved / not used		'J' */
 79#define LOC_KEYBOARD		'K'
 80#define LOC_LCD			'L'
 81#define LOC_MEMORY		'M'
 82#define LOC_NV_MEMORY		'N'
 83#define LOC_MOUSE		'O'
 84#define LOC_PLANAR		'P'
 85#define LOC_OTHER_IO		'Q'
 86#define LOC_PARALLEL		'R'
 87#define LOC_SERIAL		'S'
 88#define LOC_DEAD_RING		'T'
 89#define LOC_RACKMOUNTED		'U' /* for _u_nit is rack mounted */
 90#define LOC_VOLTAGE		'V'
 91#define LOC_SWITCH_ADAPTER	'W'
 92#define LOC_OTHER		'X'
 93#define LOC_FIRMWARE		'Y'
 94#define LOC_SCSI		'Z'
 95
 96/* Tokens for indicators */
 97#define TONE_FREQUENCY		0x0001 /* 0 - 1000 (HZ)*/
 98#define TONE_VOLUME		0x0002 /* 0 - 100 (%) */
 99#define SYSTEM_POWER_STATE	0x0003 
100#define WARNING_LIGHT		0x0004
101#define DISK_ACTIVITY_LIGHT	0x0005
102#define HEX_DISPLAY_UNIT	0x0006
103#define BATTERY_WARNING_TIME	0x0007
104#define CONDITION_CYCLE_REQUEST	0x0008
105#define SURVEILLANCE_INDICATOR	0x2328 /* 9000 */
106#define DR_ACTION		0x2329 /* 9001 */
107#define DR_INDICATOR		0x232a /* 9002 */
108/* 9003 - 9004: Vendor specific */
109/* 9006 - 9999: Vendor specific */
110
111/* other */
112#define MAX_SENSORS		 17  /* I only know of 17 sensors */    
113#define MAX_LINELENGTH          256
114#define SENSOR_PREFIX		"ibm,sensor-"
115#define cel_to_fahr(x)		((x*9/5)+32)
116
117struct individual_sensor {
118	unsigned int token;
119	unsigned int quant;
120};
121
122struct rtas_sensors {
123        struct individual_sensor sensor[MAX_SENSORS];
124	unsigned int quant;
125};
126
127/* Globals */
128static struct rtas_sensors sensors;
129static struct device_node *rtas_node = NULL;
130static unsigned long power_on_time = 0; /* Save the time the user set */
131static char progress_led[MAX_LINELENGTH];
132
133static unsigned long rtas_tone_frequency = 1000;
134static unsigned long rtas_tone_volume = 0;
135
 
 
 
 
 
 
 
 
 
 
 
136/* ****************************************************************** */
137/* Declarations */
138static int ppc_rtas_sensors_show(struct seq_file *m, void *v);
139static int ppc_rtas_clock_show(struct seq_file *m, void *v);
140static ssize_t ppc_rtas_clock_write(struct file *file,
141		const char __user *buf, size_t count, loff_t *ppos);
142static int ppc_rtas_progress_show(struct seq_file *m, void *v);
143static ssize_t ppc_rtas_progress_write(struct file *file,
144		const char __user *buf, size_t count, loff_t *ppos);
145static int ppc_rtas_poweron_show(struct seq_file *m, void *v);
146static ssize_t ppc_rtas_poweron_write(struct file *file,
147		const char __user *buf, size_t count, loff_t *ppos);
148
149static ssize_t ppc_rtas_tone_freq_write(struct file *file,
150		const char __user *buf, size_t count, loff_t *ppos);
151static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v);
152static ssize_t ppc_rtas_tone_volume_write(struct file *file,
153		const char __user *buf, size_t count, loff_t *ppos);
154static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v);
155static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);
156
157static int sensors_open(struct inode *inode, struct file *file)
158{
159	return single_open(file, ppc_rtas_sensors_show, NULL);
160}
161
162static const struct file_operations ppc_rtas_sensors_operations = {
163	.open		= sensors_open,
164	.read		= seq_read,
165	.llseek		= seq_lseek,
166	.release	= single_release,
167};
168
169static int poweron_open(struct inode *inode, struct file *file)
170{
171	return single_open(file, ppc_rtas_poweron_show, NULL);
172}
173
174static const struct file_operations ppc_rtas_poweron_operations = {
175	.open		= poweron_open,
176	.read		= seq_read,
177	.llseek		= seq_lseek,
178	.write		= ppc_rtas_poweron_write,
179	.release	= single_release,
180};
181
182static int progress_open(struct inode *inode, struct file *file)
183{
184	return single_open(file, ppc_rtas_progress_show, NULL);
185}
186
187static const struct file_operations ppc_rtas_progress_operations = {
188	.open		= progress_open,
189	.read		= seq_read,
190	.llseek		= seq_lseek,
191	.write		= ppc_rtas_progress_write,
192	.release	= single_release,
193};
194
195static int clock_open(struct inode *inode, struct file *file)
196{
197	return single_open(file, ppc_rtas_clock_show, NULL);
198}
199
200static const struct file_operations ppc_rtas_clock_operations = {
201	.open		= clock_open,
202	.read		= seq_read,
203	.llseek		= seq_lseek,
204	.write		= ppc_rtas_clock_write,
205	.release	= single_release,
206};
207
208static int tone_freq_open(struct inode *inode, struct file *file)
209{
210	return single_open(file, ppc_rtas_tone_freq_show, NULL);
211}
212
213static const struct file_operations ppc_rtas_tone_freq_operations = {
214	.open		= tone_freq_open,
215	.read		= seq_read,
216	.llseek		= seq_lseek,
217	.write		= ppc_rtas_tone_freq_write,
218	.release	= single_release,
219};
220
221static int tone_volume_open(struct inode *inode, struct file *file)
222{
223	return single_open(file, ppc_rtas_tone_volume_show, NULL);
224}
225
226static const struct file_operations ppc_rtas_tone_volume_operations = {
227	.open		= tone_volume_open,
228	.read		= seq_read,
229	.llseek		= seq_lseek,
230	.write		= ppc_rtas_tone_volume_write,
231	.release	= single_release,
232};
233
234static int rmo_buf_open(struct inode *inode, struct file *file)
235{
236	return single_open(file, ppc_rtas_rmo_buf_show, NULL);
237}
238
239static const struct file_operations ppc_rtas_rmo_buf_ops = {
240	.open		= rmo_buf_open,
241	.read		= seq_read,
242	.llseek		= seq_lseek,
243	.release	= single_release,
244};
245
246static int ppc_rtas_find_all_sensors(void);
247static void ppc_rtas_process_sensor(struct seq_file *m,
248	struct individual_sensor *s, int state, int error, const char *loc);
249static char *ppc_rtas_process_error(int error);
250static void get_location_code(struct seq_file *m,
251	struct individual_sensor *s, const char *loc);
252static void check_location_string(struct seq_file *m, const char *c);
253static void check_location(struct seq_file *m, const char *c);
254
255static int __init proc_rtas_init(void)
256{
257	if (!machine_is(pseries))
258		return -ENODEV;
259
260	rtas_node = of_find_node_by_name(NULL, "rtas");
261	if (rtas_node == NULL)
262		return -ENODEV;
263
264	proc_create("powerpc/rtas/progress", 0644, NULL,
265		    &ppc_rtas_progress_operations);
266	proc_create("powerpc/rtas/clock", 0644, NULL,
267		    &ppc_rtas_clock_operations);
268	proc_create("powerpc/rtas/poweron", 0644, NULL,
269		    &ppc_rtas_poweron_operations);
270	proc_create("powerpc/rtas/sensors", 0444, NULL,
271		    &ppc_rtas_sensors_operations);
272	proc_create("powerpc/rtas/frequency", 0644, NULL,
273		    &ppc_rtas_tone_freq_operations);
274	proc_create("powerpc/rtas/volume", 0644, NULL,
275		    &ppc_rtas_tone_volume_operations);
276	proc_create("powerpc/rtas/rmo_buffer", 0400, NULL,
277		    &ppc_rtas_rmo_buf_ops);
278	return 0;
279}
280
281__initcall(proc_rtas_init);
282
283static int parse_number(const char __user *p, size_t count, unsigned long *val)
284{
285	char buf[40];
286	char *end;
287
288	if (count > 39)
289		return -EINVAL;
290
291	if (copy_from_user(buf, p, count))
292		return -EFAULT;
293
294	buf[count] = 0;
295
296	*val = simple_strtoul(buf, &end, 10);
297	if (*end && *end != '\n')
298		return -EINVAL;
299
300	return 0;
301}
302
303/* ****************************************************************** */
304/* POWER-ON-TIME                                                      */
305/* ****************************************************************** */
306static ssize_t ppc_rtas_poweron_write(struct file *file,
307		const char __user *buf, size_t count, loff_t *ppos)
308{
309	struct rtc_time tm;
310	unsigned long nowtime;
311	int error = parse_number(buf, count, &nowtime);
312	if (error)
313		return error;
314
315	power_on_time = nowtime; /* save the time */
316
317	to_tm(nowtime, &tm);
318
319	error = rtas_call(rtas_token("set-time-for-power-on"), 7, 1, NULL, 
320			tm.tm_year, tm.tm_mon, tm.tm_mday, 
321			tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */);
322	if (error)
323		printk(KERN_WARNING "error: setting poweron time returned: %s\n", 
324				ppc_rtas_process_error(error));
325	return count;
326}
327/* ****************************************************************** */
328static int ppc_rtas_poweron_show(struct seq_file *m, void *v)
329{
330	if (power_on_time == 0)
331		seq_printf(m, "Power on time not set\n");
332	else
333		seq_printf(m, "%lu\n",power_on_time);
334	return 0;
335}
336
337/* ****************************************************************** */
338/* PROGRESS                                                           */
339/* ****************************************************************** */
340static ssize_t ppc_rtas_progress_write(struct file *file,
341		const char __user *buf, size_t count, loff_t *ppos)
342{
343	unsigned long hex;
344
345	if (count >= MAX_LINELENGTH)
346		count = MAX_LINELENGTH -1;
347	if (copy_from_user(progress_led, buf, count)) { /* save the string */
348		return -EFAULT;
349	}
350	progress_led[count] = 0;
351
352	/* Lets see if the user passed hexdigits */
353	hex = simple_strtoul(progress_led, NULL, 10);
354
355	rtas_progress ((char *)progress_led, hex);
356	return count;
357
358	/* clear the line */
359	/* rtas_progress("                   ", 0xffff);*/
360}
361/* ****************************************************************** */
362static int ppc_rtas_progress_show(struct seq_file *m, void *v)
363{
364	if (progress_led[0])
365		seq_printf(m, "%s\n", progress_led);
366	return 0;
367}
368
369/* ****************************************************************** */
370/* CLOCK                                                              */
371/* ****************************************************************** */
372static ssize_t ppc_rtas_clock_write(struct file *file,
373		const char __user *buf, size_t count, loff_t *ppos)
374{
375	struct rtc_time tm;
376	unsigned long nowtime;
377	int error = parse_number(buf, count, &nowtime);
378	if (error)
379		return error;
380
381	to_tm(nowtime, &tm);
382	error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL, 
383			tm.tm_year, tm.tm_mon, tm.tm_mday, 
384			tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
385	if (error)
386		printk(KERN_WARNING "error: setting the clock returned: %s\n", 
387				ppc_rtas_process_error(error));
388	return count;
389}
390/* ****************************************************************** */
391static int ppc_rtas_clock_show(struct seq_file *m, void *v)
392{
393	int ret[8];
394	int error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret);
395
396	if (error) {
397		printk(KERN_WARNING "error: reading the clock returned: %s\n", 
398				ppc_rtas_process_error(error));
399		seq_printf(m, "0");
400	} else { 
401		unsigned int year, mon, day, hour, min, sec;
402		year = ret[0]; mon  = ret[1]; day  = ret[2];
403		hour = ret[3]; min  = ret[4]; sec  = ret[5];
404		seq_printf(m, "%lu\n",
405				mktime(year, mon, day, hour, min, sec));
406	}
407	return 0;
408}
409
410/* ****************************************************************** */
411/* SENSOR STUFF                                                       */
412/* ****************************************************************** */
413static int ppc_rtas_sensors_show(struct seq_file *m, void *v)
414{
415	int i,j;
416	int state, error;
417	int get_sensor_state = rtas_token("get-sensor-state");
418
419	seq_printf(m, "RTAS (RunTime Abstraction Services) Sensor Information\n");
420	seq_printf(m, "Sensor\t\tValue\t\tCondition\tLocation\n");
421	seq_printf(m, "********************************************************\n");
422
423	if (ppc_rtas_find_all_sensors() != 0) {
424		seq_printf(m, "\nNo sensors are available\n");
425		return 0;
426	}
427
428	for (i=0; i<sensors.quant; i++) {
429		struct individual_sensor *p = &sensors.sensor[i];
430		char rstr[64];
431		const char *loc;
432		int llen, offs;
433
434		sprintf (rstr, SENSOR_PREFIX"%04d", p->token);
435		loc = of_get_property(rtas_node, rstr, &llen);
436
437		/* A sensor may have multiple instances */
438		for (j = 0, offs = 0; j <= p->quant; j++) {
439			error =	rtas_call(get_sensor_state, 2, 2, &state, 
440				  	  p->token, j);
441
442			ppc_rtas_process_sensor(m, p, state, error, loc);
443			seq_putc(m, '\n');
444			if (loc) {
445				offs += strlen(loc) + 1;
446				loc += strlen(loc) + 1;
447				if (offs >= llen)
448					loc = NULL;
449			}
450		}
451	}
452	return 0;
453}
454
455/* ****************************************************************** */
456
457static int ppc_rtas_find_all_sensors(void)
458{
459	const unsigned int *utmp;
460	int len, i;
461
462	utmp = of_get_property(rtas_node, "rtas-sensors", &len);
463	if (utmp == NULL) {
464		printk (KERN_ERR "error: could not get rtas-sensors\n");
465		return 1;
466	}
467
468	sensors.quant = len / 8;      /* int + int */
469
470	for (i=0; i<sensors.quant; i++) {
471		sensors.sensor[i].token = *utmp++;
472		sensors.sensor[i].quant = *utmp++;
473	}
474	return 0;
475}
476
477/* ****************************************************************** */
478/*
479 * Builds a string of what rtas returned
480 */
481static char *ppc_rtas_process_error(int error)
482{
483	switch (error) {
484		case SENSOR_CRITICAL_HIGH:
485			return "(critical high)";
486		case SENSOR_WARNING_HIGH:
487			return "(warning high)";
488		case SENSOR_NORMAL:
489			return "(normal)";
490		case SENSOR_WARNING_LOW:
491			return "(warning low)";
492		case SENSOR_CRITICAL_LOW:
493			return "(critical low)";
494		case SENSOR_SUCCESS:
495			return "(read ok)";
496		case SENSOR_HW_ERROR:
497			return "(hardware error)";
498		case SENSOR_BUSY:
499			return "(busy)";
500		case SENSOR_NOT_EXIST:
501			return "(non existent)";
502		case SENSOR_DR_ENTITY:
503			return "(dr entity removed)";
504		default:
505			return "(UNKNOWN)";
506	}
507}
508
509/* ****************************************************************** */
510/*
511 * Builds a string out of what the sensor said
512 */
513
514static void ppc_rtas_process_sensor(struct seq_file *m,
515	struct individual_sensor *s, int state, int error, const char *loc)
516{
517	/* Defined return vales */
518	const char * key_switch[]        = { "Off\t", "Normal\t", "Secure\t", 
519						"Maintenance" };
520	const char * enclosure_switch[]  = { "Closed", "Open" };
521	const char * lid_status[]        = { " ", "Open", "Closed" };
522	const char * power_source[]      = { "AC\t", "Battery", 
523		  				"AC & Battery" };
524	const char * battery_remaining[] = { "Very Low", "Low", "Mid", "High" };
525	const char * epow_sensor[]       = { 
526		"EPOW Reset", "Cooling warning", "Power warning",
527		"System shutdown", "System halt", "EPOW main enclosure",
528		"EPOW power off" };
529	const char * battery_cyclestate[]  = { "None", "In progress", 
530						"Requested" };
531	const char * battery_charging[]    = { "Charging", "Discharching", 
532						"No current flow" };
533	const char * ibm_drconnector[]     = { "Empty", "Present", "Unusable", 
534						"Exchange" };
535
536	int have_strings = 0;
537	int num_states = 0;
538	int temperature = 0;
539	int unknown = 0;
540
541	/* What kind of sensor do we have here? */
542	
543	switch (s->token) {
544		case KEY_SWITCH:
545			seq_printf(m, "Key switch:\t");
546			num_states = sizeof(key_switch) / sizeof(char *);
547			if (state < num_states) {
548				seq_printf(m, "%s\t", key_switch[state]);
549				have_strings = 1;
550			}
551			break;
552		case ENCLOSURE_SWITCH:
553			seq_printf(m, "Enclosure switch:\t");
554			num_states = sizeof(enclosure_switch) / sizeof(char *);
555			if (state < num_states) {
556				seq_printf(m, "%s\t", 
557						enclosure_switch[state]);
558				have_strings = 1;
559			}
560			break;
561		case THERMAL_SENSOR:
562			seq_printf(m, "Temp. (C/F):\t");
563			temperature = 1;
564			break;
565		case LID_STATUS:
566			seq_printf(m, "Lid status:\t");
567			num_states = sizeof(lid_status) / sizeof(char *);
568			if (state < num_states) {
569				seq_printf(m, "%s\t", lid_status[state]);
570				have_strings = 1;
571			}
572			break;
573		case POWER_SOURCE:
574			seq_printf(m, "Power source:\t");
575			num_states = sizeof(power_source) / sizeof(char *);
576			if (state < num_states) {
577				seq_printf(m, "%s\t", 
578						power_source[state]);
579				have_strings = 1;
580			}
581			break;
582		case BATTERY_VOLTAGE:
583			seq_printf(m, "Battery voltage:\t");
584			break;
585		case BATTERY_REMAINING:
586			seq_printf(m, "Battery remaining:\t");
587			num_states = sizeof(battery_remaining) / sizeof(char *);
588			if (state < num_states)
589			{
590				seq_printf(m, "%s\t", 
591						battery_remaining[state]);
592				have_strings = 1;
593			}
594			break;
595		case BATTERY_PERCENTAGE:
596			seq_printf(m, "Battery percentage:\t");
597			break;
598		case EPOW_SENSOR:
599			seq_printf(m, "EPOW Sensor:\t");
600			num_states = sizeof(epow_sensor) / sizeof(char *);
601			if (state < num_states) {
602				seq_printf(m, "%s\t", epow_sensor[state]);
603				have_strings = 1;
604			}
605			break;
606		case BATTERY_CYCLESTATE:
607			seq_printf(m, "Battery cyclestate:\t");
608			num_states = sizeof(battery_cyclestate) / 
609				     	sizeof(char *);
610			if (state < num_states) {
611				seq_printf(m, "%s\t", 
612						battery_cyclestate[state]);
613				have_strings = 1;
614			}
615			break;
616		case BATTERY_CHARGING:
617			seq_printf(m, "Battery Charging:\t");
618			num_states = sizeof(battery_charging) / sizeof(char *);
619			if (state < num_states) {
620				seq_printf(m, "%s\t", 
621						battery_charging[state]);
622				have_strings = 1;
623			}
624			break;
625		case IBM_SURVEILLANCE:
626			seq_printf(m, "Surveillance:\t");
627			break;
628		case IBM_FANRPM:
629			seq_printf(m, "Fan (rpm):\t");
630			break;
631		case IBM_VOLTAGE:
632			seq_printf(m, "Voltage (mv):\t");
633			break;
634		case IBM_DRCONNECTOR:
635			seq_printf(m, "DR connector:\t");
636			num_states = sizeof(ibm_drconnector) / sizeof(char *);
637			if (state < num_states) {
638				seq_printf(m, "%s\t", 
639						ibm_drconnector[state]);
640				have_strings = 1;
641			}
642			break;
643		case IBM_POWERSUPPLY:
644			seq_printf(m, "Powersupply:\t");
645			break;
646		default:
647			seq_printf(m,  "Unknown sensor (type %d), ignoring it\n",
648					s->token);
649			unknown = 1;
650			have_strings = 1;
651			break;
652	}
653	if (have_strings == 0) {
654		if (temperature) {
655			seq_printf(m, "%4d /%4d\t", state, cel_to_fahr(state));
656		} else
657			seq_printf(m, "%10d\t", state);
658	}
659	if (unknown == 0) {
660		seq_printf(m, "%s\t", ppc_rtas_process_error(error));
661		get_location_code(m, s, loc);
662	}
663}
664
665/* ****************************************************************** */
666
667static void check_location(struct seq_file *m, const char *c)
668{
669	switch (c[0]) {
670		case LOC_PLANAR:
671			seq_printf(m, "Planar #%c", c[1]);
672			break;
673		case LOC_CPU:
674			seq_printf(m, "CPU #%c", c[1]);
675			break;
676		case LOC_FAN:
677			seq_printf(m, "Fan #%c", c[1]);
678			break;
679		case LOC_RACKMOUNTED:
680			seq_printf(m, "Rack #%c", c[1]);
681			break;
682		case LOC_VOLTAGE:
683			seq_printf(m, "Voltage #%c", c[1]);
684			break;
685		case LOC_LCD:
686			seq_printf(m, "LCD #%c", c[1]);
687			break;
688		case '.':
689			seq_printf(m, "- %c", c[1]);
690			break;
691		default:
692			seq_printf(m, "Unknown location");
693			break;
694	}
695}
696
697
698/* ****************************************************************** */
699/* 
700 * Format: 
701 * ${LETTER}${NUMBER}[[-/]${LETTER}${NUMBER} [ ... ] ]
702 * the '.' may be an abbreviation
703 */
704static void check_location_string(struct seq_file *m, const char *c)
705{
706	while (*c) {
707		if (isalpha(*c) || *c == '.')
708			check_location(m, c);
709		else if (*c == '/' || *c == '-')
710			seq_printf(m, " at ");
711		c++;
712	}
713}
714
715
716/* ****************************************************************** */
717
718static void get_location_code(struct seq_file *m, struct individual_sensor *s,
719		const char *loc)
720{
721	if (!loc || !*loc) {
722		seq_printf(m, "---");/* does not have a location */
723	} else {
724		check_location_string(m, loc);
725	}
726	seq_putc(m, ' ');
727}
728/* ****************************************************************** */
729/* INDICATORS - Tone Frequency                                        */
730/* ****************************************************************** */
731static ssize_t ppc_rtas_tone_freq_write(struct file *file,
732		const char __user *buf, size_t count, loff_t *ppos)
733{
734	unsigned long freq;
735	int error = parse_number(buf, count, &freq);
736	if (error)
737		return error;
738
739	rtas_tone_frequency = freq; /* save it for later */
740	error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL,
741			TONE_FREQUENCY, 0, freq);
742	if (error)
743		printk(KERN_WARNING "error: setting tone frequency returned: %s\n", 
744				ppc_rtas_process_error(error));
745	return count;
746}
747/* ****************************************************************** */
748static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
749{
750	seq_printf(m, "%lu\n", rtas_tone_frequency);
751	return 0;
752}
753/* ****************************************************************** */
754/* INDICATORS - Tone Volume                                           */
755/* ****************************************************************** */
756static ssize_t ppc_rtas_tone_volume_write(struct file *file,
757		const char __user *buf, size_t count, loff_t *ppos)
758{
759	unsigned long volume;
760	int error = parse_number(buf, count, &volume);
761	if (error)
762		return error;
763
764	if (volume > 100)
765		volume = 100;
766	
767        rtas_tone_volume = volume; /* save it for later */
768	error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL,
769			TONE_VOLUME, 0, volume);
770	if (error)
771		printk(KERN_WARNING "error: setting tone volume returned: %s\n", 
772				ppc_rtas_process_error(error));
773	return count;
774}
775/* ****************************************************************** */
776static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v)
777{
778	seq_printf(m, "%lu\n", rtas_tone_volume);
779	return 0;
780}
781
782#define RMO_READ_BUF_MAX 30
783
784/* RTAS Userspace access */
785static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v)
786{
787	seq_printf(m, "%016lx %x\n", rtas_rmo_buf, RTAS_RMOBUF_MAX);
788	return 0;
789}
v3.1
 
  1/*
  2 *   Copyright (C) 2000 Tilmann Bitterberg
  3 *   (tilmann@bitterberg.de)
  4 *
  5 *   RTAS (Runtime Abstraction Services) stuff
  6 *   Intention is to provide a clean user interface
  7 *   to use the RTAS.
  8 *
  9 *   TODO:
 10 *   Split off a header file and maybe move it to a different
 11 *   location. Write Documentation on what the /proc/rtas/ entries
 12 *   actually do.
 13 */
 14
 15#include <linux/errno.h>
 16#include <linux/sched.h>
 17#include <linux/proc_fs.h>
 18#include <linux/stat.h>
 19#include <linux/ctype.h>
 20#include <linux/time.h>
 21#include <linux/string.h>
 22#include <linux/init.h>
 23#include <linux/seq_file.h>
 24#include <linux/bitops.h>
 25#include <linux/rtc.h>
 26
 27#include <asm/uaccess.h>
 28#include <asm/processor.h>
 29#include <asm/io.h>
 30#include <asm/prom.h>
 31#include <asm/rtas.h>
 32#include <asm/machdep.h> /* for ppc_md */
 33#include <asm/time.h>
 34
 35/* Token for Sensors */
 36#define KEY_SWITCH		0x0001
 37#define ENCLOSURE_SWITCH	0x0002
 38#define THERMAL_SENSOR		0x0003
 39#define LID_STATUS		0x0004
 40#define POWER_SOURCE		0x0005
 41#define BATTERY_VOLTAGE		0x0006
 42#define BATTERY_REMAINING	0x0007
 43#define BATTERY_PERCENTAGE	0x0008
 44#define EPOW_SENSOR		0x0009
 45#define BATTERY_CYCLESTATE	0x000a
 46#define BATTERY_CHARGING	0x000b
 47
 48/* IBM specific sensors */
 49#define IBM_SURVEILLANCE	0x2328 /* 9000 */
 50#define IBM_FANRPM		0x2329 /* 9001 */
 51#define IBM_VOLTAGE		0x232a /* 9002 */
 52#define IBM_DRCONNECTOR		0x232b /* 9003 */
 53#define IBM_POWERSUPPLY		0x232c /* 9004 */
 54
 55/* Status return values */
 56#define SENSOR_CRITICAL_HIGH	13
 57#define SENSOR_WARNING_HIGH	12
 58#define SENSOR_NORMAL		11
 59#define SENSOR_WARNING_LOW	10
 60#define SENSOR_CRITICAL_LOW	 9
 61#define SENSOR_SUCCESS		 0
 62#define SENSOR_HW_ERROR		-1
 63#define SENSOR_BUSY		-2
 64#define SENSOR_NOT_EXIST	-3
 65#define SENSOR_DR_ENTITY	-9000
 66
 67/* Location Codes */
 68#define LOC_SCSI_DEV_ADDR	'A'
 69#define LOC_SCSI_DEV_LOC	'B'
 70#define LOC_CPU			'C'
 71#define LOC_DISKETTE		'D'
 72#define LOC_ETHERNET		'E'
 73#define LOC_FAN			'F'
 74#define LOC_GRAPHICS		'G'
 75/* reserved / not used		'H' */
 76#define LOC_IO_ADAPTER		'I'
 77/* reserved / not used		'J' */
 78#define LOC_KEYBOARD		'K'
 79#define LOC_LCD			'L'
 80#define LOC_MEMORY		'M'
 81#define LOC_NV_MEMORY		'N'
 82#define LOC_MOUSE		'O'
 83#define LOC_PLANAR		'P'
 84#define LOC_OTHER_IO		'Q'
 85#define LOC_PARALLEL		'R'
 86#define LOC_SERIAL		'S'
 87#define LOC_DEAD_RING		'T'
 88#define LOC_RACKMOUNTED		'U' /* for _u_nit is rack mounted */
 89#define LOC_VOLTAGE		'V'
 90#define LOC_SWITCH_ADAPTER	'W'
 91#define LOC_OTHER		'X'
 92#define LOC_FIRMWARE		'Y'
 93#define LOC_SCSI		'Z'
 94
 95/* Tokens for indicators */
 96#define TONE_FREQUENCY		0x0001 /* 0 - 1000 (HZ)*/
 97#define TONE_VOLUME		0x0002 /* 0 - 100 (%) */
 98#define SYSTEM_POWER_STATE	0x0003 
 99#define WARNING_LIGHT		0x0004
100#define DISK_ACTIVITY_LIGHT	0x0005
101#define HEX_DISPLAY_UNIT	0x0006
102#define BATTERY_WARNING_TIME	0x0007
103#define CONDITION_CYCLE_REQUEST	0x0008
104#define SURVEILLANCE_INDICATOR	0x2328 /* 9000 */
105#define DR_ACTION		0x2329 /* 9001 */
106#define DR_INDICATOR		0x232a /* 9002 */
107/* 9003 - 9004: Vendor specific */
108/* 9006 - 9999: Vendor specific */
109
110/* other */
111#define MAX_SENSORS		 17  /* I only know of 17 sensors */    
112#define MAX_LINELENGTH          256
113#define SENSOR_PREFIX		"ibm,sensor-"
114#define cel_to_fahr(x)		((x*9/5)+32)
115
 
 
 
 
 
 
 
 
 
116
117/* Globals */
118static struct rtas_sensors sensors;
119static struct device_node *rtas_node = NULL;
120static unsigned long power_on_time = 0; /* Save the time the user set */
121static char progress_led[MAX_LINELENGTH];
122
123static unsigned long rtas_tone_frequency = 1000;
124static unsigned long rtas_tone_volume = 0;
125
126/* ****************STRUCTS******************************************* */
127struct individual_sensor {
128	unsigned int token;
129	unsigned int quant;
130};
131
132struct rtas_sensors {
133        struct individual_sensor sensor[MAX_SENSORS];
134	unsigned int quant;
135};
136
137/* ****************************************************************** */
138/* Declarations */
139static int ppc_rtas_sensors_show(struct seq_file *m, void *v);
140static int ppc_rtas_clock_show(struct seq_file *m, void *v);
141static ssize_t ppc_rtas_clock_write(struct file *file,
142		const char __user *buf, size_t count, loff_t *ppos);
143static int ppc_rtas_progress_show(struct seq_file *m, void *v);
144static ssize_t ppc_rtas_progress_write(struct file *file,
145		const char __user *buf, size_t count, loff_t *ppos);
146static int ppc_rtas_poweron_show(struct seq_file *m, void *v);
147static ssize_t ppc_rtas_poweron_write(struct file *file,
148		const char __user *buf, size_t count, loff_t *ppos);
149
150static ssize_t ppc_rtas_tone_freq_write(struct file *file,
151		const char __user *buf, size_t count, loff_t *ppos);
152static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v);
153static ssize_t ppc_rtas_tone_volume_write(struct file *file,
154		const char __user *buf, size_t count, loff_t *ppos);
155static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v);
156static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v);
157
158static int sensors_open(struct inode *inode, struct file *file)
159{
160	return single_open(file, ppc_rtas_sensors_show, NULL);
161}
162
163static const struct file_operations ppc_rtas_sensors_operations = {
164	.open		= sensors_open,
165	.read		= seq_read,
166	.llseek		= seq_lseek,
167	.release	= single_release,
168};
169
170static int poweron_open(struct inode *inode, struct file *file)
171{
172	return single_open(file, ppc_rtas_poweron_show, NULL);
173}
174
175static const struct file_operations ppc_rtas_poweron_operations = {
176	.open		= poweron_open,
177	.read		= seq_read,
178	.llseek		= seq_lseek,
179	.write		= ppc_rtas_poweron_write,
180	.release	= single_release,
181};
182
183static int progress_open(struct inode *inode, struct file *file)
184{
185	return single_open(file, ppc_rtas_progress_show, NULL);
186}
187
188static const struct file_operations ppc_rtas_progress_operations = {
189	.open		= progress_open,
190	.read		= seq_read,
191	.llseek		= seq_lseek,
192	.write		= ppc_rtas_progress_write,
193	.release	= single_release,
194};
195
196static int clock_open(struct inode *inode, struct file *file)
197{
198	return single_open(file, ppc_rtas_clock_show, NULL);
199}
200
201static const struct file_operations ppc_rtas_clock_operations = {
202	.open		= clock_open,
203	.read		= seq_read,
204	.llseek		= seq_lseek,
205	.write		= ppc_rtas_clock_write,
206	.release	= single_release,
207};
208
209static int tone_freq_open(struct inode *inode, struct file *file)
210{
211	return single_open(file, ppc_rtas_tone_freq_show, NULL);
212}
213
214static const struct file_operations ppc_rtas_tone_freq_operations = {
215	.open		= tone_freq_open,
216	.read		= seq_read,
217	.llseek		= seq_lseek,
218	.write		= ppc_rtas_tone_freq_write,
219	.release	= single_release,
220};
221
222static int tone_volume_open(struct inode *inode, struct file *file)
223{
224	return single_open(file, ppc_rtas_tone_volume_show, NULL);
225}
226
227static const struct file_operations ppc_rtas_tone_volume_operations = {
228	.open		= tone_volume_open,
229	.read		= seq_read,
230	.llseek		= seq_lseek,
231	.write		= ppc_rtas_tone_volume_write,
232	.release	= single_release,
233};
234
235static int rmo_buf_open(struct inode *inode, struct file *file)
236{
237	return single_open(file, ppc_rtas_rmo_buf_show, NULL);
238}
239
240static const struct file_operations ppc_rtas_rmo_buf_ops = {
241	.open		= rmo_buf_open,
242	.read		= seq_read,
243	.llseek		= seq_lseek,
244	.release	= single_release,
245};
246
247static int ppc_rtas_find_all_sensors(void);
248static void ppc_rtas_process_sensor(struct seq_file *m,
249	struct individual_sensor *s, int state, int error, const char *loc);
250static char *ppc_rtas_process_error(int error);
251static void get_location_code(struct seq_file *m,
252	struct individual_sensor *s, const char *loc);
253static void check_location_string(struct seq_file *m, const char *c);
254static void check_location(struct seq_file *m, const char *c);
255
256static int __init proc_rtas_init(void)
257{
258	if (!machine_is(pseries))
259		return -ENODEV;
260
261	rtas_node = of_find_node_by_name(NULL, "rtas");
262	if (rtas_node == NULL)
263		return -ENODEV;
264
265	proc_create("powerpc/rtas/progress", S_IRUGO|S_IWUSR, NULL,
266		    &ppc_rtas_progress_operations);
267	proc_create("powerpc/rtas/clock", S_IRUGO|S_IWUSR, NULL,
268		    &ppc_rtas_clock_operations);
269	proc_create("powerpc/rtas/poweron", S_IWUSR|S_IRUGO, NULL,
270		    &ppc_rtas_poweron_operations);
271	proc_create("powerpc/rtas/sensors", S_IRUGO, NULL,
272		    &ppc_rtas_sensors_operations);
273	proc_create("powerpc/rtas/frequency", S_IWUSR|S_IRUGO, NULL,
274		    &ppc_rtas_tone_freq_operations);
275	proc_create("powerpc/rtas/volume", S_IWUSR|S_IRUGO, NULL,
276		    &ppc_rtas_tone_volume_operations);
277	proc_create("powerpc/rtas/rmo_buffer", S_IRUSR, NULL,
278		    &ppc_rtas_rmo_buf_ops);
279	return 0;
280}
281
282__initcall(proc_rtas_init);
283
284static int parse_number(const char __user *p, size_t count, unsigned long *val)
285{
286	char buf[40];
287	char *end;
288
289	if (count > 39)
290		return -EINVAL;
291
292	if (copy_from_user(buf, p, count))
293		return -EFAULT;
294
295	buf[count] = 0;
296
297	*val = simple_strtoul(buf, &end, 10);
298	if (*end && *end != '\n')
299		return -EINVAL;
300
301	return 0;
302}
303
304/* ****************************************************************** */
305/* POWER-ON-TIME                                                      */
306/* ****************************************************************** */
307static ssize_t ppc_rtas_poweron_write(struct file *file,
308		const char __user *buf, size_t count, loff_t *ppos)
309{
310	struct rtc_time tm;
311	unsigned long nowtime;
312	int error = parse_number(buf, count, &nowtime);
313	if (error)
314		return error;
315
316	power_on_time = nowtime; /* save the time */
317
318	to_tm(nowtime, &tm);
319
320	error = rtas_call(rtas_token("set-time-for-power-on"), 7, 1, NULL, 
321			tm.tm_year, tm.tm_mon, tm.tm_mday, 
322			tm.tm_hour, tm.tm_min, tm.tm_sec, 0 /* nano */);
323	if (error)
324		printk(KERN_WARNING "error: setting poweron time returned: %s\n", 
325				ppc_rtas_process_error(error));
326	return count;
327}
328/* ****************************************************************** */
329static int ppc_rtas_poweron_show(struct seq_file *m, void *v)
330{
331	if (power_on_time == 0)
332		seq_printf(m, "Power on time not set\n");
333	else
334		seq_printf(m, "%lu\n",power_on_time);
335	return 0;
336}
337
338/* ****************************************************************** */
339/* PROGRESS                                                           */
340/* ****************************************************************** */
341static ssize_t ppc_rtas_progress_write(struct file *file,
342		const char __user *buf, size_t count, loff_t *ppos)
343{
344	unsigned long hex;
345
346	if (count >= MAX_LINELENGTH)
347		count = MAX_LINELENGTH -1;
348	if (copy_from_user(progress_led, buf, count)) { /* save the string */
349		return -EFAULT;
350	}
351	progress_led[count] = 0;
352
353	/* Lets see if the user passed hexdigits */
354	hex = simple_strtoul(progress_led, NULL, 10);
355
356	rtas_progress ((char *)progress_led, hex);
357	return count;
358
359	/* clear the line */
360	/* rtas_progress("                   ", 0xffff);*/
361}
362/* ****************************************************************** */
363static int ppc_rtas_progress_show(struct seq_file *m, void *v)
364{
365	if (progress_led[0])
366		seq_printf(m, "%s\n", progress_led);
367	return 0;
368}
369
370/* ****************************************************************** */
371/* CLOCK                                                              */
372/* ****************************************************************** */
373static ssize_t ppc_rtas_clock_write(struct file *file,
374		const char __user *buf, size_t count, loff_t *ppos)
375{
376	struct rtc_time tm;
377	unsigned long nowtime;
378	int error = parse_number(buf, count, &nowtime);
379	if (error)
380		return error;
381
382	to_tm(nowtime, &tm);
383	error = rtas_call(rtas_token("set-time-of-day"), 7, 1, NULL, 
384			tm.tm_year, tm.tm_mon, tm.tm_mday, 
385			tm.tm_hour, tm.tm_min, tm.tm_sec, 0);
386	if (error)
387		printk(KERN_WARNING "error: setting the clock returned: %s\n", 
388				ppc_rtas_process_error(error));
389	return count;
390}
391/* ****************************************************************** */
392static int ppc_rtas_clock_show(struct seq_file *m, void *v)
393{
394	int ret[8];
395	int error = rtas_call(rtas_token("get-time-of-day"), 0, 8, ret);
396
397	if (error) {
398		printk(KERN_WARNING "error: reading the clock returned: %s\n", 
399				ppc_rtas_process_error(error));
400		seq_printf(m, "0");
401	} else { 
402		unsigned int year, mon, day, hour, min, sec;
403		year = ret[0]; mon  = ret[1]; day  = ret[2];
404		hour = ret[3]; min  = ret[4]; sec  = ret[5];
405		seq_printf(m, "%lu\n",
406				mktime(year, mon, day, hour, min, sec));
407	}
408	return 0;
409}
410
411/* ****************************************************************** */
412/* SENSOR STUFF                                                       */
413/* ****************************************************************** */
414static int ppc_rtas_sensors_show(struct seq_file *m, void *v)
415{
416	int i,j;
417	int state, error;
418	int get_sensor_state = rtas_token("get-sensor-state");
419
420	seq_printf(m, "RTAS (RunTime Abstraction Services) Sensor Information\n");
421	seq_printf(m, "Sensor\t\tValue\t\tCondition\tLocation\n");
422	seq_printf(m, "********************************************************\n");
423
424	if (ppc_rtas_find_all_sensors() != 0) {
425		seq_printf(m, "\nNo sensors are available\n");
426		return 0;
427	}
428
429	for (i=0; i<sensors.quant; i++) {
430		struct individual_sensor *p = &sensors.sensor[i];
431		char rstr[64];
432		const char *loc;
433		int llen, offs;
434
435		sprintf (rstr, SENSOR_PREFIX"%04d", p->token);
436		loc = of_get_property(rtas_node, rstr, &llen);
437
438		/* A sensor may have multiple instances */
439		for (j = 0, offs = 0; j <= p->quant; j++) {
440			error =	rtas_call(get_sensor_state, 2, 2, &state, 
441				  	  p->token, j);
442
443			ppc_rtas_process_sensor(m, p, state, error, loc);
444			seq_putc(m, '\n');
445			if (loc) {
446				offs += strlen(loc) + 1;
447				loc += strlen(loc) + 1;
448				if (offs >= llen)
449					loc = NULL;
450			}
451		}
452	}
453	return 0;
454}
455
456/* ****************************************************************** */
457
458static int ppc_rtas_find_all_sensors(void)
459{
460	const unsigned int *utmp;
461	int len, i;
462
463	utmp = of_get_property(rtas_node, "rtas-sensors", &len);
464	if (utmp == NULL) {
465		printk (KERN_ERR "error: could not get rtas-sensors\n");
466		return 1;
467	}
468
469	sensors.quant = len / 8;      /* int + int */
470
471	for (i=0; i<sensors.quant; i++) {
472		sensors.sensor[i].token = *utmp++;
473		sensors.sensor[i].quant = *utmp++;
474	}
475	return 0;
476}
477
478/* ****************************************************************** */
479/*
480 * Builds a string of what rtas returned
481 */
482static char *ppc_rtas_process_error(int error)
483{
484	switch (error) {
485		case SENSOR_CRITICAL_HIGH:
486			return "(critical high)";
487		case SENSOR_WARNING_HIGH:
488			return "(warning high)";
489		case SENSOR_NORMAL:
490			return "(normal)";
491		case SENSOR_WARNING_LOW:
492			return "(warning low)";
493		case SENSOR_CRITICAL_LOW:
494			return "(critical low)";
495		case SENSOR_SUCCESS:
496			return "(read ok)";
497		case SENSOR_HW_ERROR:
498			return "(hardware error)";
499		case SENSOR_BUSY:
500			return "(busy)";
501		case SENSOR_NOT_EXIST:
502			return "(non existent)";
503		case SENSOR_DR_ENTITY:
504			return "(dr entity removed)";
505		default:
506			return "(UNKNOWN)";
507	}
508}
509
510/* ****************************************************************** */
511/*
512 * Builds a string out of what the sensor said
513 */
514
515static void ppc_rtas_process_sensor(struct seq_file *m,
516	struct individual_sensor *s, int state, int error, const char *loc)
517{
518	/* Defined return vales */
519	const char * key_switch[]        = { "Off\t", "Normal\t", "Secure\t", 
520						"Maintenance" };
521	const char * enclosure_switch[]  = { "Closed", "Open" };
522	const char * lid_status[]        = { " ", "Open", "Closed" };
523	const char * power_source[]      = { "AC\t", "Battery", 
524		  				"AC & Battery" };
525	const char * battery_remaining[] = { "Very Low", "Low", "Mid", "High" };
526	const char * epow_sensor[]       = { 
527		"EPOW Reset", "Cooling warning", "Power warning",
528		"System shutdown", "System halt", "EPOW main enclosure",
529		"EPOW power off" };
530	const char * battery_cyclestate[]  = { "None", "In progress", 
531						"Requested" };
532	const char * battery_charging[]    = { "Charging", "Discharching", 
533						"No current flow" };
534	const char * ibm_drconnector[]     = { "Empty", "Present", "Unusable", 
535						"Exchange" };
536
537	int have_strings = 0;
538	int num_states = 0;
539	int temperature = 0;
540	int unknown = 0;
541
542	/* What kind of sensor do we have here? */
543	
544	switch (s->token) {
545		case KEY_SWITCH:
546			seq_printf(m, "Key switch:\t");
547			num_states = sizeof(key_switch) / sizeof(char *);
548			if (state < num_states) {
549				seq_printf(m, "%s\t", key_switch[state]);
550				have_strings = 1;
551			}
552			break;
553		case ENCLOSURE_SWITCH:
554			seq_printf(m, "Enclosure switch:\t");
555			num_states = sizeof(enclosure_switch) / sizeof(char *);
556			if (state < num_states) {
557				seq_printf(m, "%s\t", 
558						enclosure_switch[state]);
559				have_strings = 1;
560			}
561			break;
562		case THERMAL_SENSOR:
563			seq_printf(m, "Temp. (C/F):\t");
564			temperature = 1;
565			break;
566		case LID_STATUS:
567			seq_printf(m, "Lid status:\t");
568			num_states = sizeof(lid_status) / sizeof(char *);
569			if (state < num_states) {
570				seq_printf(m, "%s\t", lid_status[state]);
571				have_strings = 1;
572			}
573			break;
574		case POWER_SOURCE:
575			seq_printf(m, "Power source:\t");
576			num_states = sizeof(power_source) / sizeof(char *);
577			if (state < num_states) {
578				seq_printf(m, "%s\t", 
579						power_source[state]);
580				have_strings = 1;
581			}
582			break;
583		case BATTERY_VOLTAGE:
584			seq_printf(m, "Battery voltage:\t");
585			break;
586		case BATTERY_REMAINING:
587			seq_printf(m, "Battery remaining:\t");
588			num_states = sizeof(battery_remaining) / sizeof(char *);
589			if (state < num_states)
590			{
591				seq_printf(m, "%s\t", 
592						battery_remaining[state]);
593				have_strings = 1;
594			}
595			break;
596		case BATTERY_PERCENTAGE:
597			seq_printf(m, "Battery percentage:\t");
598			break;
599		case EPOW_SENSOR:
600			seq_printf(m, "EPOW Sensor:\t");
601			num_states = sizeof(epow_sensor) / sizeof(char *);
602			if (state < num_states) {
603				seq_printf(m, "%s\t", epow_sensor[state]);
604				have_strings = 1;
605			}
606			break;
607		case BATTERY_CYCLESTATE:
608			seq_printf(m, "Battery cyclestate:\t");
609			num_states = sizeof(battery_cyclestate) / 
610				     	sizeof(char *);
611			if (state < num_states) {
612				seq_printf(m, "%s\t", 
613						battery_cyclestate[state]);
614				have_strings = 1;
615			}
616			break;
617		case BATTERY_CHARGING:
618			seq_printf(m, "Battery Charging:\t");
619			num_states = sizeof(battery_charging) / sizeof(char *);
620			if (state < num_states) {
621				seq_printf(m, "%s\t", 
622						battery_charging[state]);
623				have_strings = 1;
624			}
625			break;
626		case IBM_SURVEILLANCE:
627			seq_printf(m, "Surveillance:\t");
628			break;
629		case IBM_FANRPM:
630			seq_printf(m, "Fan (rpm):\t");
631			break;
632		case IBM_VOLTAGE:
633			seq_printf(m, "Voltage (mv):\t");
634			break;
635		case IBM_DRCONNECTOR:
636			seq_printf(m, "DR connector:\t");
637			num_states = sizeof(ibm_drconnector) / sizeof(char *);
638			if (state < num_states) {
639				seq_printf(m, "%s\t", 
640						ibm_drconnector[state]);
641				have_strings = 1;
642			}
643			break;
644		case IBM_POWERSUPPLY:
645			seq_printf(m, "Powersupply:\t");
646			break;
647		default:
648			seq_printf(m,  "Unknown sensor (type %d), ignoring it\n",
649					s->token);
650			unknown = 1;
651			have_strings = 1;
652			break;
653	}
654	if (have_strings == 0) {
655		if (temperature) {
656			seq_printf(m, "%4d /%4d\t", state, cel_to_fahr(state));
657		} else
658			seq_printf(m, "%10d\t", state);
659	}
660	if (unknown == 0) {
661		seq_printf(m, "%s\t", ppc_rtas_process_error(error));
662		get_location_code(m, s, loc);
663	}
664}
665
666/* ****************************************************************** */
667
668static void check_location(struct seq_file *m, const char *c)
669{
670	switch (c[0]) {
671		case LOC_PLANAR:
672			seq_printf(m, "Planar #%c", c[1]);
673			break;
674		case LOC_CPU:
675			seq_printf(m, "CPU #%c", c[1]);
676			break;
677		case LOC_FAN:
678			seq_printf(m, "Fan #%c", c[1]);
679			break;
680		case LOC_RACKMOUNTED:
681			seq_printf(m, "Rack #%c", c[1]);
682			break;
683		case LOC_VOLTAGE:
684			seq_printf(m, "Voltage #%c", c[1]);
685			break;
686		case LOC_LCD:
687			seq_printf(m, "LCD #%c", c[1]);
688			break;
689		case '.':
690			seq_printf(m, "- %c", c[1]);
691			break;
692		default:
693			seq_printf(m, "Unknown location");
694			break;
695	}
696}
697
698
699/* ****************************************************************** */
700/* 
701 * Format: 
702 * ${LETTER}${NUMBER}[[-/]${LETTER}${NUMBER} [ ... ] ]
703 * the '.' may be an abbrevation
704 */
705static void check_location_string(struct seq_file *m, const char *c)
706{
707	while (*c) {
708		if (isalpha(*c) || *c == '.')
709			check_location(m, c);
710		else if (*c == '/' || *c == '-')
711			seq_printf(m, " at ");
712		c++;
713	}
714}
715
716
717/* ****************************************************************** */
718
719static void get_location_code(struct seq_file *m, struct individual_sensor *s,
720		const char *loc)
721{
722	if (!loc || !*loc) {
723		seq_printf(m, "---");/* does not have a location */
724	} else {
725		check_location_string(m, loc);
726	}
727	seq_putc(m, ' ');
728}
729/* ****************************************************************** */
730/* INDICATORS - Tone Frequency                                        */
731/* ****************************************************************** */
732static ssize_t ppc_rtas_tone_freq_write(struct file *file,
733		const char __user *buf, size_t count, loff_t *ppos)
734{
735	unsigned long freq;
736	int error = parse_number(buf, count, &freq);
737	if (error)
738		return error;
739
740	rtas_tone_frequency = freq; /* save it for later */
741	error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL,
742			TONE_FREQUENCY, 0, freq);
743	if (error)
744		printk(KERN_WARNING "error: setting tone frequency returned: %s\n", 
745				ppc_rtas_process_error(error));
746	return count;
747}
748/* ****************************************************************** */
749static int ppc_rtas_tone_freq_show(struct seq_file *m, void *v)
750{
751	seq_printf(m, "%lu\n", rtas_tone_frequency);
752	return 0;
753}
754/* ****************************************************************** */
755/* INDICATORS - Tone Volume                                           */
756/* ****************************************************************** */
757static ssize_t ppc_rtas_tone_volume_write(struct file *file,
758		const char __user *buf, size_t count, loff_t *ppos)
759{
760	unsigned long volume;
761	int error = parse_number(buf, count, &volume);
762	if (error)
763		return error;
764
765	if (volume > 100)
766		volume = 100;
767	
768        rtas_tone_volume = volume; /* save it for later */
769	error = rtas_call(rtas_token("set-indicator"), 3, 1, NULL,
770			TONE_VOLUME, 0, volume);
771	if (error)
772		printk(KERN_WARNING "error: setting tone volume returned: %s\n", 
773				ppc_rtas_process_error(error));
774	return count;
775}
776/* ****************************************************************** */
777static int ppc_rtas_tone_volume_show(struct seq_file *m, void *v)
778{
779	seq_printf(m, "%lu\n", rtas_tone_volume);
780	return 0;
781}
782
783#define RMO_READ_BUF_MAX 30
784
785/* RTAS Userspace access */
786static int ppc_rtas_rmo_buf_show(struct seq_file *m, void *v)
787{
788	seq_printf(m, "%016lx %x\n", rtas_rmo_buf, RTAS_RMOBUF_MAX);
789	return 0;
790}