162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *    Interfaces to retrieve and set PDC Stable options (firmware)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci *    DEV NOTE: the PDC Procedures reference states that:
862306a36Sopenharmony_ci *    "A minimum of 96 bytes of Stable Storage is required. Providing more than
962306a36Sopenharmony_ci *    96 bytes of Stable Storage is optional [...]. Failure to provide the
1062306a36Sopenharmony_ci *    optional locations from 96 to 192 results in the loss of certain
1162306a36Sopenharmony_ci *    functionality during boot."
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci *    Since locations between 96 and 192 are the various paths, most (if not
1462306a36Sopenharmony_ci *    all) PA-RISC machines should have them. Anyway, for safety reasons, the
1562306a36Sopenharmony_ci *    following code can deal with just 96 bytes of Stable Storage, and all
1662306a36Sopenharmony_ci *    sizes between 96 and 192 bytes (provided they are multiple of struct
1762306a36Sopenharmony_ci *    pdc_module_path size, eg: 128, 160 and 192) to provide full information.
1862306a36Sopenharmony_ci *    One last word: there's one path we can always count on: the primary path.
1962306a36Sopenharmony_ci *    Anything above 224 bytes is used for 'osdep2' OS-dependent storage area.
2062306a36Sopenharmony_ci *
2162306a36Sopenharmony_ci *    The first OS-dependent area should always be available. Obviously, this is
2262306a36Sopenharmony_ci *    not true for the other one. Also bear in mind that reading/writing from/to
2362306a36Sopenharmony_ci *    osdep2 is much more expensive than from/to osdep1.
2462306a36Sopenharmony_ci *    NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first
2562306a36Sopenharmony_ci *    2 bytes of storage available right after OSID. That's a total of 4 bytes
2662306a36Sopenharmony_ci *    sacrificed: -ETOOLAZY :P
2762306a36Sopenharmony_ci *
2862306a36Sopenharmony_ci *    The current policy wrt file permissions is:
2962306a36Sopenharmony_ci *	- write: root only
3062306a36Sopenharmony_ci *	- read: (reading triggers PDC calls) ? root only : everyone
3162306a36Sopenharmony_ci *    The rationale is that PDC calls could hog (DoS) the machine.
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci *	TODO:
3462306a36Sopenharmony_ci *	- timer/fastsize write calls
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#undef PDCS_DEBUG
3862306a36Sopenharmony_ci#ifdef PDCS_DEBUG
3962306a36Sopenharmony_ci#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
4062306a36Sopenharmony_ci#else
4162306a36Sopenharmony_ci#define DPRINTK(fmt, args...)
4262306a36Sopenharmony_ci#endif
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci#include <linux/module.h>
4562306a36Sopenharmony_ci#include <linux/init.h>
4662306a36Sopenharmony_ci#include <linux/kernel.h>
4762306a36Sopenharmony_ci#include <linux/string.h>
4862306a36Sopenharmony_ci#include <linux/capability.h>
4962306a36Sopenharmony_ci#include <linux/ctype.h>
5062306a36Sopenharmony_ci#include <linux/sysfs.h>
5162306a36Sopenharmony_ci#include <linux/kobject.h>
5262306a36Sopenharmony_ci#include <linux/device.h>
5362306a36Sopenharmony_ci#include <linux/errno.h>
5462306a36Sopenharmony_ci#include <linux/spinlock.h>
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci#include <asm/pdc.h>
5762306a36Sopenharmony_ci#include <asm/page.h>
5862306a36Sopenharmony_ci#include <linux/uaccess.h>
5962306a36Sopenharmony_ci#include <asm/hardware.h>
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci#define PDCS_VERSION	"0.30"
6262306a36Sopenharmony_ci#define PDCS_PREFIX	"PDC Stable Storage"
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci#define PDCS_ADDR_PPRI	0x00
6562306a36Sopenharmony_ci#define PDCS_ADDR_OSID	0x40
6662306a36Sopenharmony_ci#define PDCS_ADDR_OSD1	0x48
6762306a36Sopenharmony_ci#define PDCS_ADDR_DIAG	0x58
6862306a36Sopenharmony_ci#define PDCS_ADDR_FSIZ	0x5C
6962306a36Sopenharmony_ci#define PDCS_ADDR_PCON	0x60
7062306a36Sopenharmony_ci#define PDCS_ADDR_PALT	0x80
7162306a36Sopenharmony_ci#define PDCS_ADDR_PKBD	0xA0
7262306a36Sopenharmony_ci#define PDCS_ADDR_OSD2	0xE0
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciMODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
7562306a36Sopenharmony_ciMODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");
7662306a36Sopenharmony_ciMODULE_LICENSE("GPL");
7762306a36Sopenharmony_ciMODULE_VERSION(PDCS_VERSION);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/* holds Stable Storage size. Initialized once and for all, no lock needed */
8062306a36Sopenharmony_cistatic unsigned long pdcs_size __read_mostly;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */
8362306a36Sopenharmony_cistatic u16 pdcs_osid __read_mostly;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/* This struct defines what we need to deal with a parisc pdc path entry */
8662306a36Sopenharmony_cistruct pdcspath_entry {
8762306a36Sopenharmony_ci	rwlock_t rw_lock;		/* to protect path entry access */
8862306a36Sopenharmony_ci	short ready;			/* entry record is valid if != 0 */
8962306a36Sopenharmony_ci	unsigned long addr;		/* entry address in stable storage */
9062306a36Sopenharmony_ci	char *name;			/* entry name */
9162306a36Sopenharmony_ci	struct pdc_module_path devpath;	/* device path in parisc representation */
9262306a36Sopenharmony_ci	struct device *dev;		/* corresponding device */
9362306a36Sopenharmony_ci	struct kobject kobj;
9462306a36Sopenharmony_ci};
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistruct pdcspath_attribute {
9762306a36Sopenharmony_ci	struct attribute attr;
9862306a36Sopenharmony_ci	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);
9962306a36Sopenharmony_ci	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);
10062306a36Sopenharmony_ci};
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci#define PDCSPATH_ENTRY(_addr, _name) \
10362306a36Sopenharmony_cistruct pdcspath_entry pdcspath_entry_##_name = { \
10462306a36Sopenharmony_ci	.ready = 0, \
10562306a36Sopenharmony_ci	.addr = _addr, \
10662306a36Sopenharmony_ci	.name = __stringify(_name), \
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#define PDCS_ATTR(_name, _mode, _show, _store) \
11062306a36Sopenharmony_cistruct kobj_attribute pdcs_attr_##_name = { \
11162306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode}, \
11262306a36Sopenharmony_ci	.show = _show, \
11362306a36Sopenharmony_ci	.store = _store, \
11462306a36Sopenharmony_ci};
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci#define PATHS_ATTR(_name, _mode, _show, _store) \
11762306a36Sopenharmony_cistruct pdcspath_attribute paths_attr_##_name = { \
11862306a36Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode}, \
11962306a36Sopenharmony_ci	.show = _show, \
12062306a36Sopenharmony_ci	.store = _store, \
12162306a36Sopenharmony_ci};
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)
12462306a36Sopenharmony_ci#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci/**
12762306a36Sopenharmony_ci * pdcspath_fetch - This function populates the path entry structs.
12862306a36Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry.
12962306a36Sopenharmony_ci *
13062306a36Sopenharmony_ci * The general idea is that you don't read from the Stable Storage every time
13162306a36Sopenharmony_ci * you access the files provided by the facilities. We store a copy of the
13262306a36Sopenharmony_ci * content of the stable storage WRT various paths in these structs. We read
13362306a36Sopenharmony_ci * these structs when reading the files, and we will write to these structs when
13462306a36Sopenharmony_ci * writing to the files, and only then write them back to the Stable Storage.
13562306a36Sopenharmony_ci *
13662306a36Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold.
13762306a36Sopenharmony_ci */
13862306a36Sopenharmony_cistatic int
13962306a36Sopenharmony_cipdcspath_fetch(struct pdcspath_entry *entry)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	struct pdc_module_path *devpath;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	if (!entry)
14462306a36Sopenharmony_ci		return -EINVAL;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	devpath = &entry->devpath;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
14962306a36Sopenharmony_ci			entry, devpath, entry->addr);
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* addr, devpath and count must be word aligned */
15262306a36Sopenharmony_ci	if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
15362306a36Sopenharmony_ci		return -EIO;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	/* Find the matching device.
15662306a36Sopenharmony_ci	   NOTE: hardware_path overlays with pdc_module_path, so the nice cast can
15762306a36Sopenharmony_ci	   be used */
15862306a36Sopenharmony_ci	entry->dev = hwpath_to_device((struct hardware_path *)devpath);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	entry->ready = 1;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	return 0;
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci/**
16862306a36Sopenharmony_ci * pdcspath_store - This function writes a path to stable storage.
16962306a36Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry.
17062306a36Sopenharmony_ci *
17162306a36Sopenharmony_ci * It can be used in two ways: either by passing it a preset devpath struct
17262306a36Sopenharmony_ci * containing an already computed hardware path, or by passing it a device
17362306a36Sopenharmony_ci * pointer, from which it'll find out the corresponding hardware path.
17462306a36Sopenharmony_ci * For now we do not handle the case where there's an error in writing to the
17562306a36Sopenharmony_ci * Stable Storage area, so you'd better not mess up the data :P
17662306a36Sopenharmony_ci *
17762306a36Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold.
17862306a36Sopenharmony_ci */
17962306a36Sopenharmony_cistatic void
18062306a36Sopenharmony_cipdcspath_store(struct pdcspath_entry *entry)
18162306a36Sopenharmony_ci{
18262306a36Sopenharmony_ci	struct pdc_module_path *devpath;
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	BUG_ON(!entry);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	devpath = &entry->devpath;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* We expect the caller to set the ready flag to 0 if the hardware
18962306a36Sopenharmony_ci	   path struct provided is invalid, so that we know we have to fill it.
19062306a36Sopenharmony_ci	   First case, we don't have a preset hwpath... */
19162306a36Sopenharmony_ci	if (!entry->ready) {
19262306a36Sopenharmony_ci		/* ...but we have a device, map it */
19362306a36Sopenharmony_ci		BUG_ON(!entry->dev);
19462306a36Sopenharmony_ci		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
19562306a36Sopenharmony_ci	}
19662306a36Sopenharmony_ci	/* else, we expect the provided hwpath to be valid. */
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
19962306a36Sopenharmony_ci			entry, devpath, entry->addr);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	/* addr, devpath and count must be word aligned */
20262306a36Sopenharmony_ci	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
20362306a36Sopenharmony_ci		WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.\n"
20462306a36Sopenharmony_ci				"It is likely that the Stable Storage data has been corrupted.\n"
20562306a36Sopenharmony_ci				"Please check it carefully upon next reboot.\n", __func__);
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	/* kobject is already registered */
20862306a36Sopenharmony_ci	entry->ready = 2;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci/**
21462306a36Sopenharmony_ci * pdcspath_hwpath_read - This function handles hardware path pretty printing.
21562306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
21662306a36Sopenharmony_ci * @buf: The output buffer to write to.
21762306a36Sopenharmony_ci *
21862306a36Sopenharmony_ci * We will call this function to format the output of the hwpath attribute file.
21962306a36Sopenharmony_ci */
22062306a36Sopenharmony_cistatic ssize_t
22162306a36Sopenharmony_cipdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	char *out = buf;
22462306a36Sopenharmony_ci	struct pdc_module_path *devpath;
22562306a36Sopenharmony_ci	short i;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	if (!entry || !buf)
22862306a36Sopenharmony_ci		return -EINVAL;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	read_lock(&entry->rw_lock);
23162306a36Sopenharmony_ci	devpath = &entry->devpath;
23262306a36Sopenharmony_ci	i = entry->ready;
23362306a36Sopenharmony_ci	read_unlock(&entry->rw_lock);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	if (!i)	/* entry is not ready */
23662306a36Sopenharmony_ci		return -ENODATA;
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
23962306a36Sopenharmony_ci		if (devpath->path.bc[i] < 0)
24062306a36Sopenharmony_ci			continue;
24162306a36Sopenharmony_ci		out += sprintf(out, "%d/", devpath->path.bc[i]);
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci	out += sprintf(out, "%u\n", (unsigned char)devpath->path.mod);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	return out - buf;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci/**
24962306a36Sopenharmony_ci * pdcspath_hwpath_write - This function handles hardware path modifying.
25062306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
25162306a36Sopenharmony_ci * @buf: The input buffer to read from.
25262306a36Sopenharmony_ci * @count: The number of bytes to be read.
25362306a36Sopenharmony_ci *
25462306a36Sopenharmony_ci * We will call this function to change the current hardware path.
25562306a36Sopenharmony_ci * Hardware paths are to be given '/'-delimited, without brackets.
25662306a36Sopenharmony_ci * We make sure that the provided path actually maps to an existing
25762306a36Sopenharmony_ci * device, BUT nothing would prevent some foolish user to set the path to some
25862306a36Sopenharmony_ci * PCI bridge or even a CPU...
25962306a36Sopenharmony_ci * A better work around would be to make sure we are at the end of a device tree
26062306a36Sopenharmony_ci * for instance, but it would be IMHO beyond the simple scope of that driver.
26162306a36Sopenharmony_ci * The aim is to provide a facility. Data correctness is left to userland.
26262306a36Sopenharmony_ci */
26362306a36Sopenharmony_cistatic ssize_t
26462306a36Sopenharmony_cipdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	struct hardware_path hwpath;
26762306a36Sopenharmony_ci	unsigned short i;
26862306a36Sopenharmony_ci	char in[64], *temp;
26962306a36Sopenharmony_ci	struct device *dev;
27062306a36Sopenharmony_ci	int ret;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	if (!entry || !buf || !count)
27362306a36Sopenharmony_ci		return -EINVAL;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	/* We'll use a local copy of buf */
27662306a36Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
27762306a36Sopenharmony_ci	strscpy(in, buf, count + 1);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	/* Let's clean up the target. 0xff is a blank pattern */
28062306a36Sopenharmony_ci	memset(&hwpath, 0xff, sizeof(hwpath));
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	/* First, pick the mod field (the last one of the input string) */
28362306a36Sopenharmony_ci	if (!(temp = strrchr(in, '/')))
28462306a36Sopenharmony_ci		return -EINVAL;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	hwpath.mod = simple_strtoul(temp+1, NULL, 10);
28762306a36Sopenharmony_ci	in[temp-in] = '\0';	/* truncate the remaining string. just precaution */
28862306a36Sopenharmony_ci	DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	/* Then, loop for each delimiter, making sure we don't have too many.
29162306a36Sopenharmony_ci	   we write the bc fields in a down-top way. No matter what, we stop
29262306a36Sopenharmony_ci	   before writing the last field. If there are too many fields anyway,
29362306a36Sopenharmony_ci	   then the user is a moron and it'll be caught up later when we'll
29462306a36Sopenharmony_ci	   check the consistency of the given hwpath. */
29562306a36Sopenharmony_ci	for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {
29662306a36Sopenharmony_ci		hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);
29762306a36Sopenharmony_ci		in[temp-in] = '\0';
29862306a36Sopenharmony_ci		DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]);
29962306a36Sopenharmony_ci	}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	/* Store the final field */
30262306a36Sopenharmony_ci	hwpath.bc[i] = simple_strtoul(in, NULL, 10);
30362306a36Sopenharmony_ci	DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.path.bc[i]);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	/* Now we check that the user isn't trying to lure us */
30662306a36Sopenharmony_ci	if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {
30762306a36Sopenharmony_ci		printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "
30862306a36Sopenharmony_ci			"hardware path: %s\n", __func__, entry->name, buf);
30962306a36Sopenharmony_ci		return -EINVAL;
31062306a36Sopenharmony_ci	}
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	/* So far so good, let's get in deep */
31362306a36Sopenharmony_ci	write_lock(&entry->rw_lock);
31462306a36Sopenharmony_ci	entry->ready = 0;
31562306a36Sopenharmony_ci	entry->dev = dev;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
31862306a36Sopenharmony_ci	pdcspath_store(entry);
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	/* Update the symlink to the real device */
32162306a36Sopenharmony_ci	sysfs_remove_link(&entry->kobj, "device");
32262306a36Sopenharmony_ci	write_unlock(&entry->rw_lock);
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
32562306a36Sopenharmony_ci	WARN_ON(ret);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n",
32862306a36Sopenharmony_ci		entry->name, buf);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	return count;
33162306a36Sopenharmony_ci}
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/**
33462306a36Sopenharmony_ci * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.
33562306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
33662306a36Sopenharmony_ci * @buf: The output buffer to write to.
33762306a36Sopenharmony_ci *
33862306a36Sopenharmony_ci * We will call this function to format the output of the layer attribute file.
33962306a36Sopenharmony_ci */
34062306a36Sopenharmony_cistatic ssize_t
34162306a36Sopenharmony_cipdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	char *out = buf;
34462306a36Sopenharmony_ci	struct pdc_module_path *devpath;
34562306a36Sopenharmony_ci	short i;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	if (!entry || !buf)
34862306a36Sopenharmony_ci		return -EINVAL;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	read_lock(&entry->rw_lock);
35162306a36Sopenharmony_ci	devpath = &entry->devpath;
35262306a36Sopenharmony_ci	i = entry->ready;
35362306a36Sopenharmony_ci	read_unlock(&entry->rw_lock);
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (!i)	/* entry is not ready */
35662306a36Sopenharmony_ci		return -ENODATA;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	for (i = 0; i < 6 && devpath->layers[i]; i++)
35962306a36Sopenharmony_ci		out += sprintf(out, "%u ", devpath->layers[i]);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	out += sprintf(out, "\n");
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	return out - buf;
36462306a36Sopenharmony_ci}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci/**
36762306a36Sopenharmony_ci * pdcspath_layer_write - This function handles extended layer modifying.
36862306a36Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
36962306a36Sopenharmony_ci * @buf: The input buffer to read from.
37062306a36Sopenharmony_ci * @count: The number of bytes to be read.
37162306a36Sopenharmony_ci *
37262306a36Sopenharmony_ci * We will call this function to change the current layer value.
37362306a36Sopenharmony_ci * Layers are to be given '.'-delimited, without brackets.
37462306a36Sopenharmony_ci * XXX beware we are far less checky WRT input data provided than for hwpath.
37562306a36Sopenharmony_ci * Potential harm can be done, since there's no way to check the validity of
37662306a36Sopenharmony_ci * the layer fields.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic ssize_t
37962306a36Sopenharmony_cipdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */
38262306a36Sopenharmony_ci	unsigned short i;
38362306a36Sopenharmony_ci	char in[64], *temp;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	if (!entry || !buf || !count)
38662306a36Sopenharmony_ci		return -EINVAL;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/* We'll use a local copy of buf */
38962306a36Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
39062306a36Sopenharmony_ci	strscpy(in, buf, count + 1);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* Let's clean up the target. 0 is a blank pattern */
39362306a36Sopenharmony_ci	memset(&layers, 0, sizeof(layers));
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/* First, pick the first layer */
39662306a36Sopenharmony_ci	if (unlikely(!isdigit(*in)))
39762306a36Sopenharmony_ci		return -EINVAL;
39862306a36Sopenharmony_ci	layers[0] = simple_strtoul(in, NULL, 10);
39962306a36Sopenharmony_ci	DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	temp = in;
40262306a36Sopenharmony_ci	for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) {
40362306a36Sopenharmony_ci		if (unlikely(!isdigit(*(++temp))))
40462306a36Sopenharmony_ci			return -EINVAL;
40562306a36Sopenharmony_ci		layers[i] = simple_strtoul(temp, NULL, 10);
40662306a36Sopenharmony_ci		DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]);
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	/* So far so good, let's get in deep */
41062306a36Sopenharmony_ci	write_lock(&entry->rw_lock);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	/* First, overwrite the current layers with the new ones, not touching
41362306a36Sopenharmony_ci	   the hardware path. */
41462306a36Sopenharmony_ci	memcpy(&entry->devpath.layers, &layers, sizeof(layers));
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
41762306a36Sopenharmony_ci	pdcspath_store(entry);
41862306a36Sopenharmony_ci	write_unlock(&entry->rw_lock);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n",
42162306a36Sopenharmony_ci		entry->name, buf);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	return count;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci/**
42762306a36Sopenharmony_ci * pdcspath_attr_show - Generic read function call wrapper.
42862306a36Sopenharmony_ci * @kobj: The kobject to get info from.
42962306a36Sopenharmony_ci * @attr: The attribute looked upon.
43062306a36Sopenharmony_ci * @buf: The output buffer.
43162306a36Sopenharmony_ci */
43262306a36Sopenharmony_cistatic ssize_t
43362306a36Sopenharmony_cipdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
43462306a36Sopenharmony_ci{
43562306a36Sopenharmony_ci	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
43662306a36Sopenharmony_ci	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
43762306a36Sopenharmony_ci	ssize_t ret = 0;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (pdcs_attr->show)
44062306a36Sopenharmony_ci		ret = pdcs_attr->show(entry, buf);
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	return ret;
44362306a36Sopenharmony_ci}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci/**
44662306a36Sopenharmony_ci * pdcspath_attr_store - Generic write function call wrapper.
44762306a36Sopenharmony_ci * @kobj: The kobject to write info to.
44862306a36Sopenharmony_ci * @attr: The attribute to be modified.
44962306a36Sopenharmony_ci * @buf: The input buffer.
45062306a36Sopenharmony_ci * @count: The size of the buffer.
45162306a36Sopenharmony_ci */
45262306a36Sopenharmony_cistatic ssize_t
45362306a36Sopenharmony_cipdcspath_attr_store(struct kobject *kobj, struct attribute *attr,
45462306a36Sopenharmony_ci			const char *buf, size_t count)
45562306a36Sopenharmony_ci{
45662306a36Sopenharmony_ci	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
45762306a36Sopenharmony_ci	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
45862306a36Sopenharmony_ci	ssize_t ret = 0;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
46162306a36Sopenharmony_ci		return -EACCES;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	if (pdcs_attr->store)
46462306a36Sopenharmony_ci		ret = pdcs_attr->store(entry, buf, count);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	return ret;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic const struct sysfs_ops pdcspath_attr_ops = {
47062306a36Sopenharmony_ci	.show = pdcspath_attr_show,
47162306a36Sopenharmony_ci	.store = pdcspath_attr_store,
47262306a36Sopenharmony_ci};
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci/* These are the two attributes of any PDC path. */
47562306a36Sopenharmony_cistatic PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write);
47662306a36Sopenharmony_cistatic PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write);
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_cistatic struct attribute *paths_subsys_attrs[] = {
47962306a36Sopenharmony_ci	&paths_attr_hwpath.attr,
48062306a36Sopenharmony_ci	&paths_attr_layer.attr,
48162306a36Sopenharmony_ci	NULL,
48262306a36Sopenharmony_ci};
48362306a36Sopenharmony_ciATTRIBUTE_GROUPS(paths_subsys);
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci/* Specific kobject type for our PDC paths */
48662306a36Sopenharmony_cistatic struct kobj_type ktype_pdcspath = {
48762306a36Sopenharmony_ci	.sysfs_ops = &pdcspath_attr_ops,
48862306a36Sopenharmony_ci	.default_groups = paths_subsys_groups,
48962306a36Sopenharmony_ci};
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci/* We hard define the 4 types of path we expect to find */
49262306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);
49362306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);
49462306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);
49562306a36Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/* An array containing all PDC paths we will deal with */
49862306a36Sopenharmony_cistatic struct pdcspath_entry *pdcspath_entries[] = {
49962306a36Sopenharmony_ci	&pdcspath_entry_primary,
50062306a36Sopenharmony_ci	&pdcspath_entry_alternative,
50162306a36Sopenharmony_ci	&pdcspath_entry_console,
50262306a36Sopenharmony_ci	&pdcspath_entry_keyboard,
50362306a36Sopenharmony_ci	NULL,
50462306a36Sopenharmony_ci};
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/* For more insight of what's going on here, refer to PDC Procedures doc,
50862306a36Sopenharmony_ci * Section PDC_STABLE */
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci/**
51162306a36Sopenharmony_ci * pdcs_size_read - Stable Storage size output.
51262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
51362306a36Sopenharmony_ci * @attr: The kobject attributes.
51462306a36Sopenharmony_ci * @buf: The output buffer to write to.
51562306a36Sopenharmony_ci */
51662306a36Sopenharmony_cistatic ssize_t pdcs_size_read(struct kobject *kobj,
51762306a36Sopenharmony_ci			      struct kobj_attribute *attr,
51862306a36Sopenharmony_ci			      char *buf)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	char *out = buf;
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_ci	if (!buf)
52362306a36Sopenharmony_ci		return -EINVAL;
52462306a36Sopenharmony_ci
52562306a36Sopenharmony_ci	/* show the size of the stable storage */
52662306a36Sopenharmony_ci	out += sprintf(out, "%ld\n", pdcs_size);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	return out - buf;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/**
53262306a36Sopenharmony_ci * pdcs_auto_read - Stable Storage autoboot/search flag output.
53362306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
53462306a36Sopenharmony_ci * @attr: The kobject attributes.
53562306a36Sopenharmony_ci * @buf: The output buffer to write to.
53662306a36Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
53762306a36Sopenharmony_ci */
53862306a36Sopenharmony_cistatic ssize_t pdcs_auto_read(struct kobject *kobj,
53962306a36Sopenharmony_ci			      struct kobj_attribute *attr,
54062306a36Sopenharmony_ci			      char *buf, int knob)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	char *out = buf;
54362306a36Sopenharmony_ci	struct pdcspath_entry *pathentry;
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (!buf)
54662306a36Sopenharmony_ci		return -EINVAL;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
54962306a36Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	read_lock(&pathentry->rw_lock);
55262306a36Sopenharmony_ci	out += sprintf(out, "%s\n", (pathentry->devpath.path.flags & knob) ?
55362306a36Sopenharmony_ci					"On" : "Off");
55462306a36Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	return out - buf;
55762306a36Sopenharmony_ci}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/**
56062306a36Sopenharmony_ci * pdcs_autoboot_read - Stable Storage autoboot flag output.
56162306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
56262306a36Sopenharmony_ci * @attr: The kobject attributes.
56362306a36Sopenharmony_ci * @buf: The output buffer to write to.
56462306a36Sopenharmony_ci */
56562306a36Sopenharmony_cistatic ssize_t pdcs_autoboot_read(struct kobject *kobj,
56662306a36Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/**
57262306a36Sopenharmony_ci * pdcs_autosearch_read - Stable Storage autoboot flag output.
57362306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
57462306a36Sopenharmony_ci * @attr: The kobject attributes.
57562306a36Sopenharmony_ci * @buf: The output buffer to write to.
57662306a36Sopenharmony_ci */
57762306a36Sopenharmony_cistatic ssize_t pdcs_autosearch_read(struct kobject *kobj,
57862306a36Sopenharmony_ci				    struct kobj_attribute *attr, char *buf)
57962306a36Sopenharmony_ci{
58062306a36Sopenharmony_ci	return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);
58162306a36Sopenharmony_ci}
58262306a36Sopenharmony_ci
58362306a36Sopenharmony_ci/**
58462306a36Sopenharmony_ci * pdcs_timer_read - Stable Storage timer count output (in seconds).
58562306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
58662306a36Sopenharmony_ci * @attr: The kobject attributes.
58762306a36Sopenharmony_ci * @buf: The output buffer to write to.
58862306a36Sopenharmony_ci *
58962306a36Sopenharmony_ci * The value of the timer field correponds to a number of seconds in powers of 2.
59062306a36Sopenharmony_ci */
59162306a36Sopenharmony_cistatic ssize_t pdcs_timer_read(struct kobject *kobj,
59262306a36Sopenharmony_ci			       struct kobj_attribute *attr, char *buf)
59362306a36Sopenharmony_ci{
59462306a36Sopenharmony_ci	char *out = buf;
59562306a36Sopenharmony_ci	struct pdcspath_entry *pathentry;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci	if (!buf)
59862306a36Sopenharmony_ci		return -EINVAL;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
60162306a36Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* print the timer value in seconds */
60462306a36Sopenharmony_ci	read_lock(&pathentry->rw_lock);
60562306a36Sopenharmony_ci	out += sprintf(out, "%u\n", (pathentry->devpath.path.flags & PF_TIMER) ?
60662306a36Sopenharmony_ci				(1 << (pathentry->devpath.path.flags & PF_TIMER)) : 0);
60762306a36Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return out - buf;
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci/**
61362306a36Sopenharmony_ci * pdcs_osid_read - Stable Storage OS ID register output.
61462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
61562306a36Sopenharmony_ci * @attr: The kobject attributes.
61662306a36Sopenharmony_ci * @buf: The output buffer to write to.
61762306a36Sopenharmony_ci */
61862306a36Sopenharmony_cistatic ssize_t pdcs_osid_read(struct kobject *kobj,
61962306a36Sopenharmony_ci			      struct kobj_attribute *attr, char *buf)
62062306a36Sopenharmony_ci{
62162306a36Sopenharmony_ci	char *out = buf;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (!buf)
62462306a36Sopenharmony_ci		return -EINVAL;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	out += sprintf(out, "%s dependent data (0x%.4x)\n",
62762306a36Sopenharmony_ci		os_id_to_string(pdcs_osid), pdcs_osid);
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ci	return out - buf;
63062306a36Sopenharmony_ci}
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci/**
63362306a36Sopenharmony_ci * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.
63462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
63562306a36Sopenharmony_ci * @attr: The kobject attributes.
63662306a36Sopenharmony_ci * @buf: The output buffer to write to.
63762306a36Sopenharmony_ci *
63862306a36Sopenharmony_ci * This can hold 16 bytes of OS-Dependent data.
63962306a36Sopenharmony_ci */
64062306a36Sopenharmony_cistatic ssize_t pdcs_osdep1_read(struct kobject *kobj,
64162306a36Sopenharmony_ci				struct kobj_attribute *attr, char *buf)
64262306a36Sopenharmony_ci{
64362306a36Sopenharmony_ci	char *out = buf;
64462306a36Sopenharmony_ci	u32 result[4];
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (!buf)
64762306a36Sopenharmony_ci		return -EINVAL;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK)
65062306a36Sopenharmony_ci		return -EIO;
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[0]);
65362306a36Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[1]);
65462306a36Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[2]);
65562306a36Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[3]);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return out - buf;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/**
66162306a36Sopenharmony_ci * pdcs_diagnostic_read - Stable Storage Diagnostic register output.
66262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
66362306a36Sopenharmony_ci * @attr: The kobject attributes.
66462306a36Sopenharmony_ci * @buf: The output buffer to write to.
66562306a36Sopenharmony_ci *
66662306a36Sopenharmony_ci * I have NFC how to interpret the content of that register ;-).
66762306a36Sopenharmony_ci */
66862306a36Sopenharmony_cistatic ssize_t pdcs_diagnostic_read(struct kobject *kobj,
66962306a36Sopenharmony_ci				    struct kobj_attribute *attr, char *buf)
67062306a36Sopenharmony_ci{
67162306a36Sopenharmony_ci	char *out = buf;
67262306a36Sopenharmony_ci	u32 result;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	if (!buf)
67562306a36Sopenharmony_ci		return -EINVAL;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	/* get diagnostic */
67862306a36Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK)
67962306a36Sopenharmony_ci		return -EIO;
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	out += sprintf(out, "0x%.4x\n", (result >> 16));
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	return out - buf;
68462306a36Sopenharmony_ci}
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci/**
68762306a36Sopenharmony_ci * pdcs_fastsize_read - Stable Storage FastSize register output.
68862306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
68962306a36Sopenharmony_ci * @attr: The kobject attributes.
69062306a36Sopenharmony_ci * @buf: The output buffer to write to.
69162306a36Sopenharmony_ci *
69262306a36Sopenharmony_ci * This register holds the amount of system RAM to be tested during boot sequence.
69362306a36Sopenharmony_ci */
69462306a36Sopenharmony_cistatic ssize_t pdcs_fastsize_read(struct kobject *kobj,
69562306a36Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)
69662306a36Sopenharmony_ci{
69762306a36Sopenharmony_ci	char *out = buf;
69862306a36Sopenharmony_ci	u32 result;
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	if (!buf)
70162306a36Sopenharmony_ci		return -EINVAL;
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_ci	/* get fast-size */
70462306a36Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)
70562306a36Sopenharmony_ci		return -EIO;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	if ((result & 0x0F) < 0x0E)
70862306a36Sopenharmony_ci		out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256);
70962306a36Sopenharmony_ci	else
71062306a36Sopenharmony_ci		out += sprintf(out, "All");
71162306a36Sopenharmony_ci	out += sprintf(out, "\n");
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci	return out - buf;
71462306a36Sopenharmony_ci}
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci/**
71762306a36Sopenharmony_ci * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output.
71862306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
71962306a36Sopenharmony_ci * @attr: The kobject attributes.
72062306a36Sopenharmony_ci * @buf: The output buffer to write to.
72162306a36Sopenharmony_ci *
72262306a36Sopenharmony_ci * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available.
72362306a36Sopenharmony_ci */
72462306a36Sopenharmony_cistatic ssize_t pdcs_osdep2_read(struct kobject *kobj,
72562306a36Sopenharmony_ci				struct kobj_attribute *attr, char *buf)
72662306a36Sopenharmony_ci{
72762306a36Sopenharmony_ci	char *out = buf;
72862306a36Sopenharmony_ci	unsigned long size;
72962306a36Sopenharmony_ci	unsigned short i;
73062306a36Sopenharmony_ci	u32 result;
73162306a36Sopenharmony_ci
73262306a36Sopenharmony_ci	if (unlikely(pdcs_size <= 224))
73362306a36Sopenharmony_ci		return -ENODATA;
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci	size = pdcs_size - 224;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	if (!buf)
73862306a36Sopenharmony_ci		return -EINVAL;
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	for (i=0; i<size; i+=4) {
74162306a36Sopenharmony_ci		if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result,
74262306a36Sopenharmony_ci					sizeof(result)) != PDC_OK))
74362306a36Sopenharmony_ci			return -EIO;
74462306a36Sopenharmony_ci		out += sprintf(out, "0x%.8x\n", result);
74562306a36Sopenharmony_ci	}
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	return out - buf;
74862306a36Sopenharmony_ci}
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci/**
75162306a36Sopenharmony_ci * pdcs_auto_write - This function handles autoboot/search flag modifying.
75262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
75362306a36Sopenharmony_ci * @attr: The kobject attributes.
75462306a36Sopenharmony_ci * @buf: The input buffer to read from.
75562306a36Sopenharmony_ci * @count: The number of bytes to be read.
75662306a36Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
75762306a36Sopenharmony_ci *
75862306a36Sopenharmony_ci * We will call this function to change the current autoboot flag.
75962306a36Sopenharmony_ci * We expect a precise syntax:
76062306a36Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoBoot Off or On
76162306a36Sopenharmony_ci */
76262306a36Sopenharmony_cistatic ssize_t pdcs_auto_write(struct kobject *kobj,
76362306a36Sopenharmony_ci			       struct kobj_attribute *attr, const char *buf,
76462306a36Sopenharmony_ci			       size_t count, int knob)
76562306a36Sopenharmony_ci{
76662306a36Sopenharmony_ci	struct pdcspath_entry *pathentry;
76762306a36Sopenharmony_ci	unsigned char flags;
76862306a36Sopenharmony_ci	char in[8], *temp;
76962306a36Sopenharmony_ci	char c;
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
77262306a36Sopenharmony_ci		return -EACCES;
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	if (!buf || !count)
77562306a36Sopenharmony_ci		return -EINVAL;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	/* We'll use a local copy of buf */
77862306a36Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
77962306a36Sopenharmony_ci	strscpy(in, buf, count + 1);
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
78262306a36Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/* Be nice to the existing flag record */
78562306a36Sopenharmony_ci	read_lock(&pathentry->rw_lock);
78662306a36Sopenharmony_ci	flags = pathentry->devpath.path.flags;
78762306a36Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	temp = skip_spaces(in);
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	c = *temp++ - '0';
79462306a36Sopenharmony_ci	if ((c != 0) && (c != 1))
79562306a36Sopenharmony_ci		goto parse_error;
79662306a36Sopenharmony_ci	if (c == 0)
79762306a36Sopenharmony_ci		flags &= ~knob;
79862306a36Sopenharmony_ci	else
79962306a36Sopenharmony_ci		flags |= knob;
80062306a36Sopenharmony_ci
80162306a36Sopenharmony_ci	DPRINTK("%s: flags after: 0x%X\n", __func__, flags);
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_ci	/* So far so good, let's get in deep */
80462306a36Sopenharmony_ci	write_lock(&pathentry->rw_lock);
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	/* Change the path entry flags first */
80762306a36Sopenharmony_ci	pathentry->devpath.path.flags = flags;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
81062306a36Sopenharmony_ci	pdcspath_store(pathentry);
81162306a36Sopenharmony_ci	write_unlock(&pathentry->rw_lock);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n",
81462306a36Sopenharmony_ci		(knob & PF_AUTOBOOT) ? "autoboot" : "autosearch",
81562306a36Sopenharmony_ci		(flags & knob) ? "On" : "Off");
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci	return count;
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ciparse_error:
82062306a36Sopenharmony_ci	printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__);
82162306a36Sopenharmony_ci	return -EINVAL;
82262306a36Sopenharmony_ci}
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci/**
82562306a36Sopenharmony_ci * pdcs_autoboot_write - This function handles autoboot flag modifying.
82662306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
82762306a36Sopenharmony_ci * @attr: The kobject attributes.
82862306a36Sopenharmony_ci * @buf: The input buffer to read from.
82962306a36Sopenharmony_ci * @count: The number of bytes to be read.
83062306a36Sopenharmony_ci *
83162306a36Sopenharmony_ci * We will call this function to change the current boot flags.
83262306a36Sopenharmony_ci * We expect a precise syntax:
83362306a36Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
83462306a36Sopenharmony_ci */
83562306a36Sopenharmony_cistatic ssize_t pdcs_autoboot_write(struct kobject *kobj,
83662306a36Sopenharmony_ci				   struct kobj_attribute *attr,
83762306a36Sopenharmony_ci				   const char *buf, size_t count)
83862306a36Sopenharmony_ci{
83962306a36Sopenharmony_ci	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT);
84062306a36Sopenharmony_ci}
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci/**
84362306a36Sopenharmony_ci * pdcs_autosearch_write - This function handles autosearch flag modifying.
84462306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
84562306a36Sopenharmony_ci * @attr: The kobject attributes.
84662306a36Sopenharmony_ci * @buf: The input buffer to read from.
84762306a36Sopenharmony_ci * @count: The number of bytes to be read.
84862306a36Sopenharmony_ci *
84962306a36Sopenharmony_ci * We will call this function to change the current boot flags.
85062306a36Sopenharmony_ci * We expect a precise syntax:
85162306a36Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
85262306a36Sopenharmony_ci */
85362306a36Sopenharmony_cistatic ssize_t pdcs_autosearch_write(struct kobject *kobj,
85462306a36Sopenharmony_ci				     struct kobj_attribute *attr,
85562306a36Sopenharmony_ci				     const char *buf, size_t count)
85662306a36Sopenharmony_ci{
85762306a36Sopenharmony_ci	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH);
85862306a36Sopenharmony_ci}
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci/**
86162306a36Sopenharmony_ci * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input.
86262306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
86362306a36Sopenharmony_ci * @attr: The kobject attributes.
86462306a36Sopenharmony_ci * @buf: The input buffer to read from.
86562306a36Sopenharmony_ci * @count: The number of bytes to be read.
86662306a36Sopenharmony_ci *
86762306a36Sopenharmony_ci * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte
86862306a36Sopenharmony_ci * write approach. It's up to userspace to deal with it when constructing
86962306a36Sopenharmony_ci * its input buffer.
87062306a36Sopenharmony_ci */
87162306a36Sopenharmony_cistatic ssize_t pdcs_osdep1_write(struct kobject *kobj,
87262306a36Sopenharmony_ci				 struct kobj_attribute *attr,
87362306a36Sopenharmony_ci				 const char *buf, size_t count)
87462306a36Sopenharmony_ci{
87562306a36Sopenharmony_ci	u8 in[16];
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
87862306a36Sopenharmony_ci		return -EACCES;
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ci	if (!buf || !count)
88162306a36Sopenharmony_ci		return -EINVAL;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	if (unlikely(pdcs_osid != OS_ID_LINUX))
88462306a36Sopenharmony_ci		return -EPERM;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci	if (count > 16)
88762306a36Sopenharmony_ci		return -EMSGSIZE;
88862306a36Sopenharmony_ci
88962306a36Sopenharmony_ci	/* We'll use a local copy of buf */
89062306a36Sopenharmony_ci	memset(in, 0, 16);
89162306a36Sopenharmony_ci	memcpy(in, buf, count);
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_ci	if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK)
89462306a36Sopenharmony_ci		return -EIO;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	return count;
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/**
90062306a36Sopenharmony_ci * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input.
90162306a36Sopenharmony_ci * @kobj: The kobject used to share data with userspace.
90262306a36Sopenharmony_ci * @attr: The kobject attributes.
90362306a36Sopenharmony_ci * @buf: The input buffer to read from.
90462306a36Sopenharmony_ci * @count: The number of bytes to be read.
90562306a36Sopenharmony_ci *
90662306a36Sopenharmony_ci * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a
90762306a36Sopenharmony_ci * byte-by-byte write approach. It's up to userspace to deal with it when
90862306a36Sopenharmony_ci * constructing its input buffer.
90962306a36Sopenharmony_ci */
91062306a36Sopenharmony_cistatic ssize_t pdcs_osdep2_write(struct kobject *kobj,
91162306a36Sopenharmony_ci				 struct kobj_attribute *attr,
91262306a36Sopenharmony_ci				 const char *buf, size_t count)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	unsigned long size;
91562306a36Sopenharmony_ci	unsigned short i;
91662306a36Sopenharmony_ci	u8 in[4];
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
91962306a36Sopenharmony_ci		return -EACCES;
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	if (!buf || !count)
92262306a36Sopenharmony_ci		return -EINVAL;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	if (unlikely(pdcs_size <= 224))
92562306a36Sopenharmony_ci		return -ENOSYS;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	if (unlikely(pdcs_osid != OS_ID_LINUX))
92862306a36Sopenharmony_ci		return -EPERM;
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	size = pdcs_size - 224;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (count > size)
93362306a36Sopenharmony_ci		return -EMSGSIZE;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* We'll use a local copy of buf */
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	for (i=0; i<count; i+=4) {
93862306a36Sopenharmony_ci		memset(in, 0, 4);
93962306a36Sopenharmony_ci		memcpy(in, buf+i, (count-i < 4) ? count-i : 4);
94062306a36Sopenharmony_ci		if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in,
94162306a36Sopenharmony_ci					sizeof(in)) != PDC_OK))
94262306a36Sopenharmony_ci			return -EIO;
94362306a36Sopenharmony_ci	}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_ci	return count;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci/* The remaining attributes. */
94962306a36Sopenharmony_cistatic PDCS_ATTR(size, 0444, pdcs_size_read, NULL);
95062306a36Sopenharmony_cistatic PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write);
95162306a36Sopenharmony_cistatic PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write);
95262306a36Sopenharmony_cistatic PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL);
95362306a36Sopenharmony_cistatic PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL);
95462306a36Sopenharmony_cistatic PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write);
95562306a36Sopenharmony_cistatic PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL);
95662306a36Sopenharmony_cistatic PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL);
95762306a36Sopenharmony_cistatic PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write);
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic struct attribute *pdcs_subsys_attrs[] = {
96062306a36Sopenharmony_ci	&pdcs_attr_size.attr,
96162306a36Sopenharmony_ci	&pdcs_attr_autoboot.attr,
96262306a36Sopenharmony_ci	&pdcs_attr_autosearch.attr,
96362306a36Sopenharmony_ci	&pdcs_attr_timer.attr,
96462306a36Sopenharmony_ci	&pdcs_attr_osid.attr,
96562306a36Sopenharmony_ci	&pdcs_attr_osdep1.attr,
96662306a36Sopenharmony_ci	&pdcs_attr_diagnostic.attr,
96762306a36Sopenharmony_ci	&pdcs_attr_fastsize.attr,
96862306a36Sopenharmony_ci	&pdcs_attr_osdep2.attr,
96962306a36Sopenharmony_ci	NULL,
97062306a36Sopenharmony_ci};
97162306a36Sopenharmony_ci
97262306a36Sopenharmony_cistatic const struct attribute_group pdcs_attr_group = {
97362306a36Sopenharmony_ci	.attrs = pdcs_subsys_attrs,
97462306a36Sopenharmony_ci};
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_cistatic struct kobject *stable_kobj;
97762306a36Sopenharmony_cistatic struct kset *paths_kset;
97862306a36Sopenharmony_ci
97962306a36Sopenharmony_ci/**
98062306a36Sopenharmony_ci * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
98162306a36Sopenharmony_ci *
98262306a36Sopenharmony_ci * It creates kobjects corresponding to each path entry with nice sysfs
98362306a36Sopenharmony_ci * links to the real device. This is where the magic takes place: when
98462306a36Sopenharmony_ci * registering the subsystem attributes during module init, each kobject hereby
98562306a36Sopenharmony_ci * created will show in the sysfs tree as a folder containing files as defined
98662306a36Sopenharmony_ci * by path_subsys_attr[].
98762306a36Sopenharmony_ci */
98862306a36Sopenharmony_cistatic inline int __init
98962306a36Sopenharmony_cipdcs_register_pathentries(void)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	unsigned short i;
99262306a36Sopenharmony_ci	struct pdcspath_entry *entry;
99362306a36Sopenharmony_ci	int err;
99462306a36Sopenharmony_ci
99562306a36Sopenharmony_ci	/* Initialize the entries rw_lock before anything else */
99662306a36Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++)
99762306a36Sopenharmony_ci		rwlock_init(&entry->rw_lock);
99862306a36Sopenharmony_ci
99962306a36Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++) {
100062306a36Sopenharmony_ci		write_lock(&entry->rw_lock);
100162306a36Sopenharmony_ci		err = pdcspath_fetch(entry);
100262306a36Sopenharmony_ci		write_unlock(&entry->rw_lock);
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci		if (err < 0)
100562306a36Sopenharmony_ci			continue;
100662306a36Sopenharmony_ci
100762306a36Sopenharmony_ci		entry->kobj.kset = paths_kset;
100862306a36Sopenharmony_ci		err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,
100962306a36Sopenharmony_ci					   "%s", entry->name);
101062306a36Sopenharmony_ci		if (err) {
101162306a36Sopenharmony_ci			kobject_put(&entry->kobj);
101262306a36Sopenharmony_ci			return err;
101362306a36Sopenharmony_ci		}
101462306a36Sopenharmony_ci
101562306a36Sopenharmony_ci		/* kobject is now registered */
101662306a36Sopenharmony_ci		write_lock(&entry->rw_lock);
101762306a36Sopenharmony_ci		entry->ready = 2;
101862306a36Sopenharmony_ci		write_unlock(&entry->rw_lock);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci		/* Add a nice symlink to the real device */
102162306a36Sopenharmony_ci		if (entry->dev) {
102262306a36Sopenharmony_ci			err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
102362306a36Sopenharmony_ci			WARN_ON(err);
102462306a36Sopenharmony_ci		}
102562306a36Sopenharmony_ci
102662306a36Sopenharmony_ci		kobject_uevent(&entry->kobj, KOBJ_ADD);
102762306a36Sopenharmony_ci	}
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	return 0;
103062306a36Sopenharmony_ci}
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci/**
103362306a36Sopenharmony_ci * pdcs_unregister_pathentries - Routine called when unregistering the module.
103462306a36Sopenharmony_ci */
103562306a36Sopenharmony_cistatic inline void
103662306a36Sopenharmony_cipdcs_unregister_pathentries(void)
103762306a36Sopenharmony_ci{
103862306a36Sopenharmony_ci	unsigned short i;
103962306a36Sopenharmony_ci	struct pdcspath_entry *entry;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++) {
104262306a36Sopenharmony_ci		read_lock(&entry->rw_lock);
104362306a36Sopenharmony_ci		if (entry->ready >= 2)
104462306a36Sopenharmony_ci			kobject_put(&entry->kobj);
104562306a36Sopenharmony_ci		read_unlock(&entry->rw_lock);
104662306a36Sopenharmony_ci	}
104762306a36Sopenharmony_ci}
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci/*
105062306a36Sopenharmony_ci * For now we register the stable subsystem with the firmware subsystem
105162306a36Sopenharmony_ci * and the paths subsystem with the stable subsystem
105262306a36Sopenharmony_ci */
105362306a36Sopenharmony_cistatic int __init
105462306a36Sopenharmony_cipdc_stable_init(void)
105562306a36Sopenharmony_ci{
105662306a36Sopenharmony_ci	int rc = 0, error;
105762306a36Sopenharmony_ci	u32 result;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	/* find the size of the stable storage */
106062306a36Sopenharmony_ci	if (pdc_stable_get_size(&pdcs_size) != PDC_OK)
106162306a36Sopenharmony_ci		return -ENODEV;
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	/* make sure we have enough data */
106462306a36Sopenharmony_ci	if (pdcs_size < 96)
106562306a36Sopenharmony_ci		return -ENODATA;
106662306a36Sopenharmony_ci
106762306a36Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION);
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* get OSID */
107062306a36Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)
107162306a36Sopenharmony_ci		return -EIO;
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	/* the actual result is 16 bits away */
107462306a36Sopenharmony_ci	pdcs_osid = (u16)(result >> 16);
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	/* For now we'll register the directory at /sys/firmware/stable */
107762306a36Sopenharmony_ci	stable_kobj = kobject_create_and_add("stable", firmware_kobj);
107862306a36Sopenharmony_ci	if (!stable_kobj) {
107962306a36Sopenharmony_ci		rc = -ENOMEM;
108062306a36Sopenharmony_ci		goto fail_firmreg;
108162306a36Sopenharmony_ci	}
108262306a36Sopenharmony_ci
108362306a36Sopenharmony_ci	/* Don't forget the root entries */
108462306a36Sopenharmony_ci	error = sysfs_create_group(stable_kobj, &pdcs_attr_group);
108562306a36Sopenharmony_ci	if (error) {
108662306a36Sopenharmony_ci		rc = -ENOMEM;
108762306a36Sopenharmony_ci		goto fail_ksetreg;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* register the paths kset as a child of the stable kset */
109162306a36Sopenharmony_ci	paths_kset = kset_create_and_add("paths", NULL, stable_kobj);
109262306a36Sopenharmony_ci	if (!paths_kset) {
109362306a36Sopenharmony_ci		rc = -ENOMEM;
109462306a36Sopenharmony_ci		goto fail_ksetreg;
109562306a36Sopenharmony_ci	}
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	/* now we create all "files" for the paths kset */
109862306a36Sopenharmony_ci	if ((rc = pdcs_register_pathentries()))
109962306a36Sopenharmony_ci		goto fail_pdcsreg;
110062306a36Sopenharmony_ci
110162306a36Sopenharmony_ci	return rc;
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_cifail_pdcsreg:
110462306a36Sopenharmony_ci	pdcs_unregister_pathentries();
110562306a36Sopenharmony_ci	kset_unregister(paths_kset);
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cifail_ksetreg:
110862306a36Sopenharmony_ci	kobject_put(stable_kobj);
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cifail_firmreg:
111162306a36Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX " bailing out\n");
111262306a36Sopenharmony_ci	return rc;
111362306a36Sopenharmony_ci}
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_cistatic void __exit
111662306a36Sopenharmony_cipdc_stable_exit(void)
111762306a36Sopenharmony_ci{
111862306a36Sopenharmony_ci	pdcs_unregister_pathentries();
111962306a36Sopenharmony_ci	kset_unregister(paths_kset);
112062306a36Sopenharmony_ci	kobject_put(stable_kobj);
112162306a36Sopenharmony_ci}
112262306a36Sopenharmony_ci
112362306a36Sopenharmony_ci
112462306a36Sopenharmony_cimodule_init(pdc_stable_init);
112562306a36Sopenharmony_cimodule_exit(pdc_stable_exit);
1126