18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *    Interfaces to retrieve and set PDC Stable options (firmware)
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.org>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *    DEV NOTE: the PDC Procedures reference states that:
88c2ecf20Sopenharmony_ci *    "A minimum of 96 bytes of Stable Storage is required. Providing more than
98c2ecf20Sopenharmony_ci *    96 bytes of Stable Storage is optional [...]. Failure to provide the
108c2ecf20Sopenharmony_ci *    optional locations from 96 to 192 results in the loss of certain
118c2ecf20Sopenharmony_ci *    functionality during boot."
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci *    Since locations between 96 and 192 are the various paths, most (if not
148c2ecf20Sopenharmony_ci *    all) PA-RISC machines should have them. Anyway, for safety reasons, the
158c2ecf20Sopenharmony_ci *    following code can deal with just 96 bytes of Stable Storage, and all
168c2ecf20Sopenharmony_ci *    sizes between 96 and 192 bytes (provided they are multiple of struct
178c2ecf20Sopenharmony_ci *    device_path size, eg: 128, 160 and 192) to provide full information.
188c2ecf20Sopenharmony_ci *    One last word: there's one path we can always count on: the primary path.
198c2ecf20Sopenharmony_ci *    Anything above 224 bytes is used for 'osdep2' OS-dependent storage area.
208c2ecf20Sopenharmony_ci *
218c2ecf20Sopenharmony_ci *    The first OS-dependent area should always be available. Obviously, this is
228c2ecf20Sopenharmony_ci *    not true for the other one. Also bear in mind that reading/writing from/to
238c2ecf20Sopenharmony_ci *    osdep2 is much more expensive than from/to osdep1.
248c2ecf20Sopenharmony_ci *    NOTE: We do not handle the 2 bytes OS-dep area at 0x5D, nor the first
258c2ecf20Sopenharmony_ci *    2 bytes of storage available right after OSID. That's a total of 4 bytes
268c2ecf20Sopenharmony_ci *    sacrificed: -ETOOLAZY :P
278c2ecf20Sopenharmony_ci *
288c2ecf20Sopenharmony_ci *    The current policy wrt file permissions is:
298c2ecf20Sopenharmony_ci *	- write: root only
308c2ecf20Sopenharmony_ci *	- read: (reading triggers PDC calls) ? root only : everyone
318c2ecf20Sopenharmony_ci *    The rationale is that PDC calls could hog (DoS) the machine.
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci *	TODO:
348c2ecf20Sopenharmony_ci *	- timer/fastsize write calls
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#undef PDCS_DEBUG
388c2ecf20Sopenharmony_ci#ifdef PDCS_DEBUG
398c2ecf20Sopenharmony_ci#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
408c2ecf20Sopenharmony_ci#else
418c2ecf20Sopenharmony_ci#define DPRINTK(fmt, args...)
428c2ecf20Sopenharmony_ci#endif
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <linux/module.h>
458c2ecf20Sopenharmony_ci#include <linux/init.h>
468c2ecf20Sopenharmony_ci#include <linux/kernel.h>
478c2ecf20Sopenharmony_ci#include <linux/string.h>
488c2ecf20Sopenharmony_ci#include <linux/capability.h>
498c2ecf20Sopenharmony_ci#include <linux/ctype.h>
508c2ecf20Sopenharmony_ci#include <linux/sysfs.h>
518c2ecf20Sopenharmony_ci#include <linux/kobject.h>
528c2ecf20Sopenharmony_ci#include <linux/device.h>
538c2ecf20Sopenharmony_ci#include <linux/errno.h>
548c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci#include <asm/pdc.h>
578c2ecf20Sopenharmony_ci#include <asm/page.h>
588c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
598c2ecf20Sopenharmony_ci#include <asm/hardware.h>
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci#define PDCS_VERSION	"0.30"
628c2ecf20Sopenharmony_ci#define PDCS_PREFIX	"PDC Stable Storage"
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define PDCS_ADDR_PPRI	0x00
658c2ecf20Sopenharmony_ci#define PDCS_ADDR_OSID	0x40
668c2ecf20Sopenharmony_ci#define PDCS_ADDR_OSD1	0x48
678c2ecf20Sopenharmony_ci#define PDCS_ADDR_DIAG	0x58
688c2ecf20Sopenharmony_ci#define PDCS_ADDR_FSIZ	0x5C
698c2ecf20Sopenharmony_ci#define PDCS_ADDR_PCON	0x60
708c2ecf20Sopenharmony_ci#define PDCS_ADDR_PALT	0x80
718c2ecf20Sopenharmony_ci#define PDCS_ADDR_PKBD	0xA0
728c2ecf20Sopenharmony_ci#define PDCS_ADDR_OSD2	0xE0
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("sysfs interface to HP PDC Stable Storage data");
768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
778c2ecf20Sopenharmony_ciMODULE_VERSION(PDCS_VERSION);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci/* holds Stable Storage size. Initialized once and for all, no lock needed */
808c2ecf20Sopenharmony_cistatic unsigned long pdcs_size __read_mostly;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* holds OS ID. Initialized once and for all, hopefully to 0x0006 */
838c2ecf20Sopenharmony_cistatic u16 pdcs_osid __read_mostly;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci/* This struct defines what we need to deal with a parisc pdc path entry */
868c2ecf20Sopenharmony_cistruct pdcspath_entry {
878c2ecf20Sopenharmony_ci	rwlock_t rw_lock;		/* to protect path entry access */
888c2ecf20Sopenharmony_ci	short ready;			/* entry record is valid if != 0 */
898c2ecf20Sopenharmony_ci	unsigned long addr;		/* entry address in stable storage */
908c2ecf20Sopenharmony_ci	char *name;			/* entry name */
918c2ecf20Sopenharmony_ci	struct device_path devpath;	/* device path in parisc representation */
928c2ecf20Sopenharmony_ci	struct device *dev;		/* corresponding device */
938c2ecf20Sopenharmony_ci	struct kobject kobj;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct pdcspath_attribute {
978c2ecf20Sopenharmony_ci	struct attribute attr;
988c2ecf20Sopenharmony_ci	ssize_t (*show)(struct pdcspath_entry *entry, char *buf);
998c2ecf20Sopenharmony_ci	ssize_t (*store)(struct pdcspath_entry *entry, const char *buf, size_t count);
1008c2ecf20Sopenharmony_ci};
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#define PDCSPATH_ENTRY(_addr, _name) \
1038c2ecf20Sopenharmony_cistruct pdcspath_entry pdcspath_entry_##_name = { \
1048c2ecf20Sopenharmony_ci	.ready = 0, \
1058c2ecf20Sopenharmony_ci	.addr = _addr, \
1068c2ecf20Sopenharmony_ci	.name = __stringify(_name), \
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci#define PDCS_ATTR(_name, _mode, _show, _store) \
1108c2ecf20Sopenharmony_cistruct kobj_attribute pdcs_attr_##_name = { \
1118c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode}, \
1128c2ecf20Sopenharmony_ci	.show = _show, \
1138c2ecf20Sopenharmony_ci	.store = _store, \
1148c2ecf20Sopenharmony_ci};
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci#define PATHS_ATTR(_name, _mode, _show, _store) \
1178c2ecf20Sopenharmony_cistruct pdcspath_attribute paths_attr_##_name = { \
1188c2ecf20Sopenharmony_ci	.attr = {.name = __stringify(_name), .mode = _mode}, \
1198c2ecf20Sopenharmony_ci	.show = _show, \
1208c2ecf20Sopenharmony_ci	.store = _store, \
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define to_pdcspath_attribute(_attr) container_of(_attr, struct pdcspath_attribute, attr)
1248c2ecf20Sopenharmony_ci#define to_pdcspath_entry(obj)  container_of(obj, struct pdcspath_entry, kobj)
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/**
1278c2ecf20Sopenharmony_ci * pdcspath_fetch - This function populates the path entry structs.
1288c2ecf20Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry.
1298c2ecf20Sopenharmony_ci *
1308c2ecf20Sopenharmony_ci * The general idea is that you don't read from the Stable Storage every time
1318c2ecf20Sopenharmony_ci * you access the files provided by the facilities. We store a copy of the
1328c2ecf20Sopenharmony_ci * content of the stable storage WRT various paths in these structs. We read
1338c2ecf20Sopenharmony_ci * these structs when reading the files, and we will write to these structs when
1348c2ecf20Sopenharmony_ci * writing to the files, and only then write them back to the Stable Storage.
1358c2ecf20Sopenharmony_ci *
1368c2ecf20Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold.
1378c2ecf20Sopenharmony_ci */
1388c2ecf20Sopenharmony_cistatic int
1398c2ecf20Sopenharmony_cipdcspath_fetch(struct pdcspath_entry *entry)
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci	struct device_path *devpath;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	if (!entry)
1448c2ecf20Sopenharmony_ci		return -EINVAL;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	devpath = &entry->devpath;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	DPRINTK("%s: fetch: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
1498c2ecf20Sopenharmony_ci			entry, devpath, entry->addr);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	/* addr, devpath and count must be word aligned */
1528c2ecf20Sopenharmony_ci	if (pdc_stable_read(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
1538c2ecf20Sopenharmony_ci		return -EIO;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* Find the matching device.
1568c2ecf20Sopenharmony_ci	   NOTE: hardware_path overlays with device_path, so the nice cast can
1578c2ecf20Sopenharmony_ci	   be used */
1588c2ecf20Sopenharmony_ci	entry->dev = hwpath_to_device((struct hardware_path *)devpath);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	entry->ready = 1;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return 0;
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/**
1688c2ecf20Sopenharmony_ci * pdcspath_store - This function writes a path to stable storage.
1698c2ecf20Sopenharmony_ci * @entry: A pointer to an allocated pdcspath_entry.
1708c2ecf20Sopenharmony_ci *
1718c2ecf20Sopenharmony_ci * It can be used in two ways: either by passing it a preset devpath struct
1728c2ecf20Sopenharmony_ci * containing an already computed hardware path, or by passing it a device
1738c2ecf20Sopenharmony_ci * pointer, from which it'll find out the corresponding hardware path.
1748c2ecf20Sopenharmony_ci * For now we do not handle the case where there's an error in writing to the
1758c2ecf20Sopenharmony_ci * Stable Storage area, so you'd better not mess up the data :P
1768c2ecf20Sopenharmony_ci *
1778c2ecf20Sopenharmony_ci * This function expects to be called with @entry->rw_lock write-hold.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cistatic void
1808c2ecf20Sopenharmony_cipdcspath_store(struct pdcspath_entry *entry)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct device_path *devpath;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	BUG_ON(!entry);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	devpath = &entry->devpath;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	/* We expect the caller to set the ready flag to 0 if the hardware
1898c2ecf20Sopenharmony_ci	   path struct provided is invalid, so that we know we have to fill it.
1908c2ecf20Sopenharmony_ci	   First case, we don't have a preset hwpath... */
1918c2ecf20Sopenharmony_ci	if (!entry->ready) {
1928c2ecf20Sopenharmony_ci		/* ...but we have a device, map it */
1938c2ecf20Sopenharmony_ci		BUG_ON(!entry->dev);
1948c2ecf20Sopenharmony_ci		device_to_hwpath(entry->dev, (struct hardware_path *)devpath);
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	/* else, we expect the provided hwpath to be valid. */
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	DPRINTK("%s: store: 0x%p, 0x%p, addr: 0x%lx\n", __func__,
1998c2ecf20Sopenharmony_ci			entry, devpath, entry->addr);
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	/* addr, devpath and count must be word aligned */
2028c2ecf20Sopenharmony_ci	if (pdc_stable_write(entry->addr, devpath, sizeof(*devpath)) != PDC_OK)
2038c2ecf20Sopenharmony_ci		WARN(1, KERN_ERR "%s: an error occurred when writing to PDC.\n"
2048c2ecf20Sopenharmony_ci				"It is likely that the Stable Storage data has been corrupted.\n"
2058c2ecf20Sopenharmony_ci				"Please check it carefully upon next reboot.\n", __func__);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* kobject is already registered */
2088c2ecf20Sopenharmony_ci	entry->ready = 2;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	DPRINTK("%s: device: 0x%p\n", __func__, entry->dev);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci/**
2148c2ecf20Sopenharmony_ci * pdcspath_hwpath_read - This function handles hardware path pretty printing.
2158c2ecf20Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
2168c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
2178c2ecf20Sopenharmony_ci *
2188c2ecf20Sopenharmony_ci * We will call this function to format the output of the hwpath attribute file.
2198c2ecf20Sopenharmony_ci */
2208c2ecf20Sopenharmony_cistatic ssize_t
2218c2ecf20Sopenharmony_cipdcspath_hwpath_read(struct pdcspath_entry *entry, char *buf)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	char *out = buf;
2248c2ecf20Sopenharmony_ci	struct device_path *devpath;
2258c2ecf20Sopenharmony_ci	short i;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (!entry || !buf)
2288c2ecf20Sopenharmony_ci		return -EINVAL;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	read_lock(&entry->rw_lock);
2318c2ecf20Sopenharmony_ci	devpath = &entry->devpath;
2328c2ecf20Sopenharmony_ci	i = entry->ready;
2338c2ecf20Sopenharmony_ci	read_unlock(&entry->rw_lock);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	if (!i)	/* entry is not ready */
2368c2ecf20Sopenharmony_ci		return -ENODATA;
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (i = 0; i < 6; i++) {
2398c2ecf20Sopenharmony_ci		if (devpath->bc[i] >= 128)
2408c2ecf20Sopenharmony_ci			continue;
2418c2ecf20Sopenharmony_ci		out += sprintf(out, "%u/", (unsigned char)devpath->bc[i]);
2428c2ecf20Sopenharmony_ci	}
2438c2ecf20Sopenharmony_ci	out += sprintf(out, "%u\n", (unsigned char)devpath->mod);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	return out - buf;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci/**
2498c2ecf20Sopenharmony_ci * pdcspath_hwpath_write - This function handles hardware path modifying.
2508c2ecf20Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
2518c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
2528c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
2538c2ecf20Sopenharmony_ci *
2548c2ecf20Sopenharmony_ci * We will call this function to change the current hardware path.
2558c2ecf20Sopenharmony_ci * Hardware paths are to be given '/'-delimited, without brackets.
2568c2ecf20Sopenharmony_ci * We make sure that the provided path actually maps to an existing
2578c2ecf20Sopenharmony_ci * device, BUT nothing would prevent some foolish user to set the path to some
2588c2ecf20Sopenharmony_ci * PCI bridge or even a CPU...
2598c2ecf20Sopenharmony_ci * A better work around would be to make sure we are at the end of a device tree
2608c2ecf20Sopenharmony_ci * for instance, but it would be IMHO beyond the simple scope of that driver.
2618c2ecf20Sopenharmony_ci * The aim is to provide a facility. Data correctness is left to userland.
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic ssize_t
2648c2ecf20Sopenharmony_cipdcspath_hwpath_write(struct pdcspath_entry *entry, const char *buf, size_t count)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct hardware_path hwpath;
2678c2ecf20Sopenharmony_ci	unsigned short i;
2688c2ecf20Sopenharmony_ci	char in[64], *temp;
2698c2ecf20Sopenharmony_ci	struct device *dev;
2708c2ecf20Sopenharmony_ci	int ret;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (!entry || !buf || !count)
2738c2ecf20Sopenharmony_ci		return -EINVAL;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	/* We'll use a local copy of buf */
2768c2ecf20Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
2778c2ecf20Sopenharmony_ci	strncpy(in, buf, count);
2788c2ecf20Sopenharmony_ci	in[count] = '\0';
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	/* Let's clean up the target. 0xff is a blank pattern */
2818c2ecf20Sopenharmony_ci	memset(&hwpath, 0xff, sizeof(hwpath));
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	/* First, pick the mod field (the last one of the input string) */
2848c2ecf20Sopenharmony_ci	if (!(temp = strrchr(in, '/')))
2858c2ecf20Sopenharmony_ci		return -EINVAL;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	hwpath.mod = simple_strtoul(temp+1, NULL, 10);
2888c2ecf20Sopenharmony_ci	in[temp-in] = '\0';	/* truncate the remaining string. just precaution */
2898c2ecf20Sopenharmony_ci	DPRINTK("%s: mod: %d\n", __func__, hwpath.mod);
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	/* Then, loop for each delimiter, making sure we don't have too many.
2928c2ecf20Sopenharmony_ci	   we write the bc fields in a down-top way. No matter what, we stop
2938c2ecf20Sopenharmony_ci	   before writing the last field. If there are too many fields anyway,
2948c2ecf20Sopenharmony_ci	   then the user is a moron and it'll be caught up later when we'll
2958c2ecf20Sopenharmony_ci	   check the consistency of the given hwpath. */
2968c2ecf20Sopenharmony_ci	for (i=5; ((temp = strrchr(in, '/'))) && (temp-in > 0) && (likely(i)); i--) {
2978c2ecf20Sopenharmony_ci		hwpath.bc[i] = simple_strtoul(temp+1, NULL, 10);
2988c2ecf20Sopenharmony_ci		in[temp-in] = '\0';
2998c2ecf20Sopenharmony_ci		DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
3008c2ecf20Sopenharmony_ci	}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/* Store the final field */
3038c2ecf20Sopenharmony_ci	hwpath.bc[i] = simple_strtoul(in, NULL, 10);
3048c2ecf20Sopenharmony_ci	DPRINTK("%s: bc[%d]: %d\n", __func__, i, hwpath.bc[i]);
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	/* Now we check that the user isn't trying to lure us */
3078c2ecf20Sopenharmony_ci	if (!(dev = hwpath_to_device((struct hardware_path *)&hwpath))) {
3088c2ecf20Sopenharmony_ci		printk(KERN_WARNING "%s: attempt to set invalid \"%s\" "
3098c2ecf20Sopenharmony_ci			"hardware path: %s\n", __func__, entry->name, buf);
3108c2ecf20Sopenharmony_ci		return -EINVAL;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/* So far so good, let's get in deep */
3148c2ecf20Sopenharmony_ci	write_lock(&entry->rw_lock);
3158c2ecf20Sopenharmony_ci	entry->ready = 0;
3168c2ecf20Sopenharmony_ci	entry->dev = dev;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
3198c2ecf20Sopenharmony_ci	pdcspath_store(entry);
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	/* Update the symlink to the real device */
3228c2ecf20Sopenharmony_ci	sysfs_remove_link(&entry->kobj, "device");
3238c2ecf20Sopenharmony_ci	write_unlock(&entry->rw_lock);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	ret = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
3268c2ecf20Sopenharmony_ci	WARN_ON(ret);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" path to \"%s\"\n",
3298c2ecf20Sopenharmony_ci		entry->name, buf);
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	return count;
3328c2ecf20Sopenharmony_ci}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci/**
3358c2ecf20Sopenharmony_ci * pdcspath_layer_read - Extended layer (eg. SCSI ids) pretty printing.
3368c2ecf20Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
3378c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
3388c2ecf20Sopenharmony_ci *
3398c2ecf20Sopenharmony_ci * We will call this function to format the output of the layer attribute file.
3408c2ecf20Sopenharmony_ci */
3418c2ecf20Sopenharmony_cistatic ssize_t
3428c2ecf20Sopenharmony_cipdcspath_layer_read(struct pdcspath_entry *entry, char *buf)
3438c2ecf20Sopenharmony_ci{
3448c2ecf20Sopenharmony_ci	char *out = buf;
3458c2ecf20Sopenharmony_ci	struct device_path *devpath;
3468c2ecf20Sopenharmony_ci	short i;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	if (!entry || !buf)
3498c2ecf20Sopenharmony_ci		return -EINVAL;
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	read_lock(&entry->rw_lock);
3528c2ecf20Sopenharmony_ci	devpath = &entry->devpath;
3538c2ecf20Sopenharmony_ci	i = entry->ready;
3548c2ecf20Sopenharmony_ci	read_unlock(&entry->rw_lock);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	if (!i)	/* entry is not ready */
3578c2ecf20Sopenharmony_ci		return -ENODATA;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	for (i = 0; i < 6 && devpath->layers[i]; i++)
3608c2ecf20Sopenharmony_ci		out += sprintf(out, "%u ", devpath->layers[i]);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	out += sprintf(out, "\n");
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return out - buf;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci/**
3688c2ecf20Sopenharmony_ci * pdcspath_layer_write - This function handles extended layer modifying.
3698c2ecf20Sopenharmony_ci * @entry: An allocated and populated pdscpath_entry struct.
3708c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
3718c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
3728c2ecf20Sopenharmony_ci *
3738c2ecf20Sopenharmony_ci * We will call this function to change the current layer value.
3748c2ecf20Sopenharmony_ci * Layers are to be given '.'-delimited, without brackets.
3758c2ecf20Sopenharmony_ci * XXX beware we are far less checky WRT input data provided than for hwpath.
3768c2ecf20Sopenharmony_ci * Potential harm can be done, since there's no way to check the validity of
3778c2ecf20Sopenharmony_ci * the layer fields.
3788c2ecf20Sopenharmony_ci */
3798c2ecf20Sopenharmony_cistatic ssize_t
3808c2ecf20Sopenharmony_cipdcspath_layer_write(struct pdcspath_entry *entry, const char *buf, size_t count)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	unsigned int layers[6]; /* device-specific info (ctlr#, unit#, ...) */
3838c2ecf20Sopenharmony_ci	unsigned short i;
3848c2ecf20Sopenharmony_ci	char in[64], *temp;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (!entry || !buf || !count)
3878c2ecf20Sopenharmony_ci		return -EINVAL;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/* We'll use a local copy of buf */
3908c2ecf20Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
3918c2ecf20Sopenharmony_ci	strncpy(in, buf, count);
3928c2ecf20Sopenharmony_ci	in[count] = '\0';
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* Let's clean up the target. 0 is a blank pattern */
3958c2ecf20Sopenharmony_ci	memset(&layers, 0, sizeof(layers));
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	/* First, pick the first layer */
3988c2ecf20Sopenharmony_ci	if (unlikely(!isdigit(*in)))
3998c2ecf20Sopenharmony_ci		return -EINVAL;
4008c2ecf20Sopenharmony_ci	layers[0] = simple_strtoul(in, NULL, 10);
4018c2ecf20Sopenharmony_ci	DPRINTK("%s: layer[0]: %d\n", __func__, layers[0]);
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	temp = in;
4048c2ecf20Sopenharmony_ci	for (i=1; ((temp = strchr(temp, '.'))) && (likely(i<6)); i++) {
4058c2ecf20Sopenharmony_ci		if (unlikely(!isdigit(*(++temp))))
4068c2ecf20Sopenharmony_ci			return -EINVAL;
4078c2ecf20Sopenharmony_ci		layers[i] = simple_strtoul(temp, NULL, 10);
4088c2ecf20Sopenharmony_ci		DPRINTK("%s: layer[%d]: %d\n", __func__, i, layers[i]);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	/* So far so good, let's get in deep */
4128c2ecf20Sopenharmony_ci	write_lock(&entry->rw_lock);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	/* First, overwrite the current layers with the new ones, not touching
4158c2ecf20Sopenharmony_ci	   the hardware path. */
4168c2ecf20Sopenharmony_ci	memcpy(&entry->devpath.layers, &layers, sizeof(layers));
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
4198c2ecf20Sopenharmony_ci	pdcspath_store(entry);
4208c2ecf20Sopenharmony_ci	write_unlock(&entry->rw_lock);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" layers to \"%s\"\n",
4238c2ecf20Sopenharmony_ci		entry->name, buf);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return count;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci/**
4298c2ecf20Sopenharmony_ci * pdcspath_attr_show - Generic read function call wrapper.
4308c2ecf20Sopenharmony_ci * @kobj: The kobject to get info from.
4318c2ecf20Sopenharmony_ci * @attr: The attribute looked upon.
4328c2ecf20Sopenharmony_ci * @buf: The output buffer.
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistatic ssize_t
4358c2ecf20Sopenharmony_cipdcspath_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
4368c2ecf20Sopenharmony_ci{
4378c2ecf20Sopenharmony_ci	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
4388c2ecf20Sopenharmony_ci	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
4398c2ecf20Sopenharmony_ci	ssize_t ret = 0;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (pdcs_attr->show)
4428c2ecf20Sopenharmony_ci		ret = pdcs_attr->show(entry, buf);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	return ret;
4458c2ecf20Sopenharmony_ci}
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci/**
4488c2ecf20Sopenharmony_ci * pdcspath_attr_store - Generic write function call wrapper.
4498c2ecf20Sopenharmony_ci * @kobj: The kobject to write info to.
4508c2ecf20Sopenharmony_ci * @attr: The attribute to be modified.
4518c2ecf20Sopenharmony_ci * @buf: The input buffer.
4528c2ecf20Sopenharmony_ci * @count: The size of the buffer.
4538c2ecf20Sopenharmony_ci */
4548c2ecf20Sopenharmony_cistatic ssize_t
4558c2ecf20Sopenharmony_cipdcspath_attr_store(struct kobject *kobj, struct attribute *attr,
4568c2ecf20Sopenharmony_ci			const char *buf, size_t count)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct pdcspath_entry *entry = to_pdcspath_entry(kobj);
4598c2ecf20Sopenharmony_ci	struct pdcspath_attribute *pdcs_attr = to_pdcspath_attribute(attr);
4608c2ecf20Sopenharmony_ci	ssize_t ret = 0;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
4638c2ecf20Sopenharmony_ci		return -EACCES;
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	if (pdcs_attr->store)
4668c2ecf20Sopenharmony_ci		ret = pdcs_attr->store(entry, buf, count);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	return ret;
4698c2ecf20Sopenharmony_ci}
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_cistatic const struct sysfs_ops pdcspath_attr_ops = {
4728c2ecf20Sopenharmony_ci	.show = pdcspath_attr_show,
4738c2ecf20Sopenharmony_ci	.store = pdcspath_attr_store,
4748c2ecf20Sopenharmony_ci};
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci/* These are the two attributes of any PDC path. */
4778c2ecf20Sopenharmony_cistatic PATHS_ATTR(hwpath, 0644, pdcspath_hwpath_read, pdcspath_hwpath_write);
4788c2ecf20Sopenharmony_cistatic PATHS_ATTR(layer, 0644, pdcspath_layer_read, pdcspath_layer_write);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic struct attribute *paths_subsys_attrs[] = {
4818c2ecf20Sopenharmony_ci	&paths_attr_hwpath.attr,
4828c2ecf20Sopenharmony_ci	&paths_attr_layer.attr,
4838c2ecf20Sopenharmony_ci	NULL,
4848c2ecf20Sopenharmony_ci};
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci/* Specific kobject type for our PDC paths */
4878c2ecf20Sopenharmony_cistatic struct kobj_type ktype_pdcspath = {
4888c2ecf20Sopenharmony_ci	.sysfs_ops = &pdcspath_attr_ops,
4898c2ecf20Sopenharmony_ci	.default_attrs = paths_subsys_attrs,
4908c2ecf20Sopenharmony_ci};
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/* We hard define the 4 types of path we expect to find */
4938c2ecf20Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PPRI, primary);
4948c2ecf20Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PCON, console);
4958c2ecf20Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PALT, alternative);
4968c2ecf20Sopenharmony_cistatic PDCSPATH_ENTRY(PDCS_ADDR_PKBD, keyboard);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci/* An array containing all PDC paths we will deal with */
4998c2ecf20Sopenharmony_cistatic struct pdcspath_entry *pdcspath_entries[] = {
5008c2ecf20Sopenharmony_ci	&pdcspath_entry_primary,
5018c2ecf20Sopenharmony_ci	&pdcspath_entry_alternative,
5028c2ecf20Sopenharmony_ci	&pdcspath_entry_console,
5038c2ecf20Sopenharmony_ci	&pdcspath_entry_keyboard,
5048c2ecf20Sopenharmony_ci	NULL,
5058c2ecf20Sopenharmony_ci};
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/* For more insight of what's going on here, refer to PDC Procedures doc,
5098c2ecf20Sopenharmony_ci * Section PDC_STABLE */
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci/**
5128c2ecf20Sopenharmony_ci * pdcs_size_read - Stable Storage size output.
5138c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
5148c2ecf20Sopenharmony_ci */
5158c2ecf20Sopenharmony_cistatic ssize_t pdcs_size_read(struct kobject *kobj,
5168c2ecf20Sopenharmony_ci			      struct kobj_attribute *attr,
5178c2ecf20Sopenharmony_ci			      char *buf)
5188c2ecf20Sopenharmony_ci{
5198c2ecf20Sopenharmony_ci	char *out = buf;
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (!buf)
5228c2ecf20Sopenharmony_ci		return -EINVAL;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	/* show the size of the stable storage */
5258c2ecf20Sopenharmony_ci	out += sprintf(out, "%ld\n", pdcs_size);
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	return out - buf;
5288c2ecf20Sopenharmony_ci}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci/**
5318c2ecf20Sopenharmony_ci * pdcs_auto_read - Stable Storage autoboot/search flag output.
5328c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
5338c2ecf20Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
5348c2ecf20Sopenharmony_ci */
5358c2ecf20Sopenharmony_cistatic ssize_t pdcs_auto_read(struct kobject *kobj,
5368c2ecf20Sopenharmony_ci			      struct kobj_attribute *attr,
5378c2ecf20Sopenharmony_ci			      char *buf, int knob)
5388c2ecf20Sopenharmony_ci{
5398c2ecf20Sopenharmony_ci	char *out = buf;
5408c2ecf20Sopenharmony_ci	struct pdcspath_entry *pathentry;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (!buf)
5438c2ecf20Sopenharmony_ci		return -EINVAL;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
5468c2ecf20Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci	read_lock(&pathentry->rw_lock);
5498c2ecf20Sopenharmony_ci	out += sprintf(out, "%s\n", (pathentry->devpath.flags & knob) ?
5508c2ecf20Sopenharmony_ci					"On" : "Off");
5518c2ecf20Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	return out - buf;
5548c2ecf20Sopenharmony_ci}
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci/**
5578c2ecf20Sopenharmony_ci * pdcs_autoboot_read - Stable Storage autoboot flag output.
5588c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
5598c2ecf20Sopenharmony_ci */
5608c2ecf20Sopenharmony_cistatic ssize_t pdcs_autoboot_read(struct kobject *kobj,
5618c2ecf20Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)
5628c2ecf20Sopenharmony_ci{
5638c2ecf20Sopenharmony_ci	return pdcs_auto_read(kobj, attr, buf, PF_AUTOBOOT);
5648c2ecf20Sopenharmony_ci}
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci/**
5678c2ecf20Sopenharmony_ci * pdcs_autosearch_read - Stable Storage autoboot flag output.
5688c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
5698c2ecf20Sopenharmony_ci */
5708c2ecf20Sopenharmony_cistatic ssize_t pdcs_autosearch_read(struct kobject *kobj,
5718c2ecf20Sopenharmony_ci				    struct kobj_attribute *attr, char *buf)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	return pdcs_auto_read(kobj, attr, buf, PF_AUTOSEARCH);
5748c2ecf20Sopenharmony_ci}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci/**
5778c2ecf20Sopenharmony_ci * pdcs_timer_read - Stable Storage timer count output (in seconds).
5788c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
5798c2ecf20Sopenharmony_ci *
5808c2ecf20Sopenharmony_ci * The value of the timer field correponds to a number of seconds in powers of 2.
5818c2ecf20Sopenharmony_ci */
5828c2ecf20Sopenharmony_cistatic ssize_t pdcs_timer_read(struct kobject *kobj,
5838c2ecf20Sopenharmony_ci			       struct kobj_attribute *attr, char *buf)
5848c2ecf20Sopenharmony_ci{
5858c2ecf20Sopenharmony_ci	char *out = buf;
5868c2ecf20Sopenharmony_ci	struct pdcspath_entry *pathentry;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (!buf)
5898c2ecf20Sopenharmony_ci		return -EINVAL;
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
5928c2ecf20Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	/* print the timer value in seconds */
5958c2ecf20Sopenharmony_ci	read_lock(&pathentry->rw_lock);
5968c2ecf20Sopenharmony_ci	out += sprintf(out, "%u\n", (pathentry->devpath.flags & PF_TIMER) ?
5978c2ecf20Sopenharmony_ci				(1 << (pathentry->devpath.flags & PF_TIMER)) : 0);
5988c2ecf20Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci	return out - buf;
6018c2ecf20Sopenharmony_ci}
6028c2ecf20Sopenharmony_ci
6038c2ecf20Sopenharmony_ci/**
6048c2ecf20Sopenharmony_ci * pdcs_osid_read - Stable Storage OS ID register output.
6058c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
6068c2ecf20Sopenharmony_ci */
6078c2ecf20Sopenharmony_cistatic ssize_t pdcs_osid_read(struct kobject *kobj,
6088c2ecf20Sopenharmony_ci			      struct kobj_attribute *attr, char *buf)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	char *out = buf;
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (!buf)
6138c2ecf20Sopenharmony_ci		return -EINVAL;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	out += sprintf(out, "%s dependent data (0x%.4x)\n",
6168c2ecf20Sopenharmony_ci		os_id_to_string(pdcs_osid), pdcs_osid);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	return out - buf;
6198c2ecf20Sopenharmony_ci}
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci/**
6228c2ecf20Sopenharmony_ci * pdcs_osdep1_read - Stable Storage OS-Dependent data area 1 output.
6238c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
6248c2ecf20Sopenharmony_ci *
6258c2ecf20Sopenharmony_ci * This can hold 16 bytes of OS-Dependent data.
6268c2ecf20Sopenharmony_ci */
6278c2ecf20Sopenharmony_cistatic ssize_t pdcs_osdep1_read(struct kobject *kobj,
6288c2ecf20Sopenharmony_ci				struct kobj_attribute *attr, char *buf)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	char *out = buf;
6318c2ecf20Sopenharmony_ci	u32 result[4];
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (!buf)
6348c2ecf20Sopenharmony_ci		return -EINVAL;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_OSD1, &result, sizeof(result)) != PDC_OK)
6378c2ecf20Sopenharmony_ci		return -EIO;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[0]);
6408c2ecf20Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[1]);
6418c2ecf20Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[2]);
6428c2ecf20Sopenharmony_ci	out += sprintf(out, "0x%.8x\n", result[3]);
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	return out - buf;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci/**
6488c2ecf20Sopenharmony_ci * pdcs_diagnostic_read - Stable Storage Diagnostic register output.
6498c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
6508c2ecf20Sopenharmony_ci *
6518c2ecf20Sopenharmony_ci * I have NFC how to interpret the content of that register ;-).
6528c2ecf20Sopenharmony_ci */
6538c2ecf20Sopenharmony_cistatic ssize_t pdcs_diagnostic_read(struct kobject *kobj,
6548c2ecf20Sopenharmony_ci				    struct kobj_attribute *attr, char *buf)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	char *out = buf;
6578c2ecf20Sopenharmony_ci	u32 result;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	if (!buf)
6608c2ecf20Sopenharmony_ci		return -EINVAL;
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	/* get diagnostic */
6638c2ecf20Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_DIAG, &result, sizeof(result)) != PDC_OK)
6648c2ecf20Sopenharmony_ci		return -EIO;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	out += sprintf(out, "0x%.4x\n", (result >> 16));
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	return out - buf;
6698c2ecf20Sopenharmony_ci}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci/**
6728c2ecf20Sopenharmony_ci * pdcs_fastsize_read - Stable Storage FastSize register output.
6738c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
6748c2ecf20Sopenharmony_ci *
6758c2ecf20Sopenharmony_ci * This register holds the amount of system RAM to be tested during boot sequence.
6768c2ecf20Sopenharmony_ci */
6778c2ecf20Sopenharmony_cistatic ssize_t pdcs_fastsize_read(struct kobject *kobj,
6788c2ecf20Sopenharmony_ci				  struct kobj_attribute *attr, char *buf)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	char *out = buf;
6818c2ecf20Sopenharmony_ci	u32 result;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	if (!buf)
6848c2ecf20Sopenharmony_ci		return -EINVAL;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	/* get fast-size */
6878c2ecf20Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_FSIZ, &result, sizeof(result)) != PDC_OK)
6888c2ecf20Sopenharmony_ci		return -EIO;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	if ((result & 0x0F) < 0x0E)
6918c2ecf20Sopenharmony_ci		out += sprintf(out, "%d kB", (1<<(result & 0x0F))*256);
6928c2ecf20Sopenharmony_ci	else
6938c2ecf20Sopenharmony_ci		out += sprintf(out, "All");
6948c2ecf20Sopenharmony_ci	out += sprintf(out, "\n");
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return out - buf;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci/**
7008c2ecf20Sopenharmony_ci * pdcs_osdep2_read - Stable Storage OS-Dependent data area 2 output.
7018c2ecf20Sopenharmony_ci * @buf: The output buffer to write to.
7028c2ecf20Sopenharmony_ci *
7038c2ecf20Sopenharmony_ci * This can hold pdcs_size - 224 bytes of OS-Dependent data, when available.
7048c2ecf20Sopenharmony_ci */
7058c2ecf20Sopenharmony_cistatic ssize_t pdcs_osdep2_read(struct kobject *kobj,
7068c2ecf20Sopenharmony_ci				struct kobj_attribute *attr, char *buf)
7078c2ecf20Sopenharmony_ci{
7088c2ecf20Sopenharmony_ci	char *out = buf;
7098c2ecf20Sopenharmony_ci	unsigned long size;
7108c2ecf20Sopenharmony_ci	unsigned short i;
7118c2ecf20Sopenharmony_ci	u32 result;
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	if (unlikely(pdcs_size <= 224))
7148c2ecf20Sopenharmony_ci		return -ENODATA;
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	size = pdcs_size - 224;
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci	if (!buf)
7198c2ecf20Sopenharmony_ci		return -EINVAL;
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	for (i=0; i<size; i+=4) {
7228c2ecf20Sopenharmony_ci		if (unlikely(pdc_stable_read(PDCS_ADDR_OSD2 + i, &result,
7238c2ecf20Sopenharmony_ci					sizeof(result)) != PDC_OK))
7248c2ecf20Sopenharmony_ci			return -EIO;
7258c2ecf20Sopenharmony_ci		out += sprintf(out, "0x%.8x\n", result);
7268c2ecf20Sopenharmony_ci	}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	return out - buf;
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci/**
7328c2ecf20Sopenharmony_ci * pdcs_auto_write - This function handles autoboot/search flag modifying.
7338c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
7348c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
7358c2ecf20Sopenharmony_ci * @knob: The PF_AUTOBOOT or PF_AUTOSEARCH flag
7368c2ecf20Sopenharmony_ci *
7378c2ecf20Sopenharmony_ci * We will call this function to change the current autoboot flag.
7388c2ecf20Sopenharmony_ci * We expect a precise syntax:
7398c2ecf20Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoBoot Off or On
7408c2ecf20Sopenharmony_ci */
7418c2ecf20Sopenharmony_cistatic ssize_t pdcs_auto_write(struct kobject *kobj,
7428c2ecf20Sopenharmony_ci			       struct kobj_attribute *attr, const char *buf,
7438c2ecf20Sopenharmony_ci			       size_t count, int knob)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	struct pdcspath_entry *pathentry;
7468c2ecf20Sopenharmony_ci	unsigned char flags;
7478c2ecf20Sopenharmony_ci	char in[8], *temp;
7488c2ecf20Sopenharmony_ci	char c;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
7518c2ecf20Sopenharmony_ci		return -EACCES;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	if (!buf || !count)
7548c2ecf20Sopenharmony_ci		return -EINVAL;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	/* We'll use a local copy of buf */
7578c2ecf20Sopenharmony_ci	count = min_t(size_t, count, sizeof(in)-1);
7588c2ecf20Sopenharmony_ci	strncpy(in, buf, count);
7598c2ecf20Sopenharmony_ci	in[count] = '\0';
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	/* Current flags are stored in primary boot path entry */
7628c2ecf20Sopenharmony_ci	pathentry = &pdcspath_entry_primary;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	/* Be nice to the existing flag record */
7658c2ecf20Sopenharmony_ci	read_lock(&pathentry->rw_lock);
7668c2ecf20Sopenharmony_ci	flags = pathentry->devpath.flags;
7678c2ecf20Sopenharmony_ci	read_unlock(&pathentry->rw_lock);
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	DPRINTK("%s: flags before: 0x%X\n", __func__, flags);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	temp = skip_spaces(in);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	c = *temp++ - '0';
7748c2ecf20Sopenharmony_ci	if ((c != 0) && (c != 1))
7758c2ecf20Sopenharmony_ci		goto parse_error;
7768c2ecf20Sopenharmony_ci	if (c == 0)
7778c2ecf20Sopenharmony_ci		flags &= ~knob;
7788c2ecf20Sopenharmony_ci	else
7798c2ecf20Sopenharmony_ci		flags |= knob;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	DPRINTK("%s: flags after: 0x%X\n", __func__, flags);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* So far so good, let's get in deep */
7848c2ecf20Sopenharmony_ci	write_lock(&pathentry->rw_lock);
7858c2ecf20Sopenharmony_ci
7868c2ecf20Sopenharmony_ci	/* Change the path entry flags first */
7878c2ecf20Sopenharmony_ci	pathentry->devpath.flags = flags;
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	/* Now, dive in. Write back to the hardware */
7908c2ecf20Sopenharmony_ci	pdcspath_store(pathentry);
7918c2ecf20Sopenharmony_ci	write_unlock(&pathentry->rw_lock);
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX ": changed \"%s\" to \"%s\"\n",
7948c2ecf20Sopenharmony_ci		(knob & PF_AUTOBOOT) ? "autoboot" : "autosearch",
7958c2ecf20Sopenharmony_ci		(flags & knob) ? "On" : "Off");
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	return count;
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ciparse_error:
8008c2ecf20Sopenharmony_ci	printk(KERN_WARNING "%s: Parse error: expect \"n\" (n == 0 or 1)\n", __func__);
8018c2ecf20Sopenharmony_ci	return -EINVAL;
8028c2ecf20Sopenharmony_ci}
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/**
8058c2ecf20Sopenharmony_ci * pdcs_autoboot_write - This function handles autoboot flag modifying.
8068c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
8078c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
8088c2ecf20Sopenharmony_ci *
8098c2ecf20Sopenharmony_ci * We will call this function to change the current boot flags.
8108c2ecf20Sopenharmony_ci * We expect a precise syntax:
8118c2ecf20Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
8128c2ecf20Sopenharmony_ci */
8138c2ecf20Sopenharmony_cistatic ssize_t pdcs_autoboot_write(struct kobject *kobj,
8148c2ecf20Sopenharmony_ci				   struct kobj_attribute *attr,
8158c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
8168c2ecf20Sopenharmony_ci{
8178c2ecf20Sopenharmony_ci	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOBOOT);
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci/**
8218c2ecf20Sopenharmony_ci * pdcs_autosearch_write - This function handles autosearch flag modifying.
8228c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
8238c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
8248c2ecf20Sopenharmony_ci *
8258c2ecf20Sopenharmony_ci * We will call this function to change the current boot flags.
8268c2ecf20Sopenharmony_ci * We expect a precise syntax:
8278c2ecf20Sopenharmony_ci *	\"n\" (n == 0 or 1) to toggle AutoSearch Off or On
8288c2ecf20Sopenharmony_ci */
8298c2ecf20Sopenharmony_cistatic ssize_t pdcs_autosearch_write(struct kobject *kobj,
8308c2ecf20Sopenharmony_ci				     struct kobj_attribute *attr,
8318c2ecf20Sopenharmony_ci				     const char *buf, size_t count)
8328c2ecf20Sopenharmony_ci{
8338c2ecf20Sopenharmony_ci	return pdcs_auto_write(kobj, attr, buf, count, PF_AUTOSEARCH);
8348c2ecf20Sopenharmony_ci}
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci/**
8378c2ecf20Sopenharmony_ci * pdcs_osdep1_write - Stable Storage OS-Dependent data area 1 input.
8388c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
8398c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
8408c2ecf20Sopenharmony_ci *
8418c2ecf20Sopenharmony_ci * This can store 16 bytes of OS-Dependent data. We use a byte-by-byte
8428c2ecf20Sopenharmony_ci * write approach. It's up to userspace to deal with it when constructing
8438c2ecf20Sopenharmony_ci * its input buffer.
8448c2ecf20Sopenharmony_ci */
8458c2ecf20Sopenharmony_cistatic ssize_t pdcs_osdep1_write(struct kobject *kobj,
8468c2ecf20Sopenharmony_ci				 struct kobj_attribute *attr,
8478c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
8488c2ecf20Sopenharmony_ci{
8498c2ecf20Sopenharmony_ci	u8 in[16];
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
8528c2ecf20Sopenharmony_ci		return -EACCES;
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (!buf || !count)
8558c2ecf20Sopenharmony_ci		return -EINVAL;
8568c2ecf20Sopenharmony_ci
8578c2ecf20Sopenharmony_ci	if (unlikely(pdcs_osid != OS_ID_LINUX))
8588c2ecf20Sopenharmony_ci		return -EPERM;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if (count > 16)
8618c2ecf20Sopenharmony_ci		return -EMSGSIZE;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	/* We'll use a local copy of buf */
8648c2ecf20Sopenharmony_ci	memset(in, 0, 16);
8658c2ecf20Sopenharmony_ci	memcpy(in, buf, count);
8668c2ecf20Sopenharmony_ci
8678c2ecf20Sopenharmony_ci	if (pdc_stable_write(PDCS_ADDR_OSD1, &in, sizeof(in)) != PDC_OK)
8688c2ecf20Sopenharmony_ci		return -EIO;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	return count;
8718c2ecf20Sopenharmony_ci}
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci/**
8748c2ecf20Sopenharmony_ci * pdcs_osdep2_write - Stable Storage OS-Dependent data area 2 input.
8758c2ecf20Sopenharmony_ci * @buf: The input buffer to read from.
8768c2ecf20Sopenharmony_ci * @count: The number of bytes to be read.
8778c2ecf20Sopenharmony_ci *
8788c2ecf20Sopenharmony_ci * This can store pdcs_size - 224 bytes of OS-Dependent data. We use a
8798c2ecf20Sopenharmony_ci * byte-by-byte write approach. It's up to userspace to deal with it when
8808c2ecf20Sopenharmony_ci * constructing its input buffer.
8818c2ecf20Sopenharmony_ci */
8828c2ecf20Sopenharmony_cistatic ssize_t pdcs_osdep2_write(struct kobject *kobj,
8838c2ecf20Sopenharmony_ci				 struct kobj_attribute *attr,
8848c2ecf20Sopenharmony_ci				 const char *buf, size_t count)
8858c2ecf20Sopenharmony_ci{
8868c2ecf20Sopenharmony_ci	unsigned long size;
8878c2ecf20Sopenharmony_ci	unsigned short i;
8888c2ecf20Sopenharmony_ci	u8 in[4];
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
8918c2ecf20Sopenharmony_ci		return -EACCES;
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	if (!buf || !count)
8948c2ecf20Sopenharmony_ci		return -EINVAL;
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	if (unlikely(pdcs_size <= 224))
8978c2ecf20Sopenharmony_ci		return -ENOSYS;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (unlikely(pdcs_osid != OS_ID_LINUX))
9008c2ecf20Sopenharmony_ci		return -EPERM;
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	size = pdcs_size - 224;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci	if (count > size)
9058c2ecf20Sopenharmony_ci		return -EMSGSIZE;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci	/* We'll use a local copy of buf */
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	for (i=0; i<count; i+=4) {
9108c2ecf20Sopenharmony_ci		memset(in, 0, 4);
9118c2ecf20Sopenharmony_ci		memcpy(in, buf+i, (count-i < 4) ? count-i : 4);
9128c2ecf20Sopenharmony_ci		if (unlikely(pdc_stable_write(PDCS_ADDR_OSD2 + i, &in,
9138c2ecf20Sopenharmony_ci					sizeof(in)) != PDC_OK))
9148c2ecf20Sopenharmony_ci			return -EIO;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci	return count;
9188c2ecf20Sopenharmony_ci}
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci/* The remaining attributes. */
9218c2ecf20Sopenharmony_cistatic PDCS_ATTR(size, 0444, pdcs_size_read, NULL);
9228c2ecf20Sopenharmony_cistatic PDCS_ATTR(autoboot, 0644, pdcs_autoboot_read, pdcs_autoboot_write);
9238c2ecf20Sopenharmony_cistatic PDCS_ATTR(autosearch, 0644, pdcs_autosearch_read, pdcs_autosearch_write);
9248c2ecf20Sopenharmony_cistatic PDCS_ATTR(timer, 0444, pdcs_timer_read, NULL);
9258c2ecf20Sopenharmony_cistatic PDCS_ATTR(osid, 0444, pdcs_osid_read, NULL);
9268c2ecf20Sopenharmony_cistatic PDCS_ATTR(osdep1, 0600, pdcs_osdep1_read, pdcs_osdep1_write);
9278c2ecf20Sopenharmony_cistatic PDCS_ATTR(diagnostic, 0400, pdcs_diagnostic_read, NULL);
9288c2ecf20Sopenharmony_cistatic PDCS_ATTR(fastsize, 0400, pdcs_fastsize_read, NULL);
9298c2ecf20Sopenharmony_cistatic PDCS_ATTR(osdep2, 0600, pdcs_osdep2_read, pdcs_osdep2_write);
9308c2ecf20Sopenharmony_ci
9318c2ecf20Sopenharmony_cistatic struct attribute *pdcs_subsys_attrs[] = {
9328c2ecf20Sopenharmony_ci	&pdcs_attr_size.attr,
9338c2ecf20Sopenharmony_ci	&pdcs_attr_autoboot.attr,
9348c2ecf20Sopenharmony_ci	&pdcs_attr_autosearch.attr,
9358c2ecf20Sopenharmony_ci	&pdcs_attr_timer.attr,
9368c2ecf20Sopenharmony_ci	&pdcs_attr_osid.attr,
9378c2ecf20Sopenharmony_ci	&pdcs_attr_osdep1.attr,
9388c2ecf20Sopenharmony_ci	&pdcs_attr_diagnostic.attr,
9398c2ecf20Sopenharmony_ci	&pdcs_attr_fastsize.attr,
9408c2ecf20Sopenharmony_ci	&pdcs_attr_osdep2.attr,
9418c2ecf20Sopenharmony_ci	NULL,
9428c2ecf20Sopenharmony_ci};
9438c2ecf20Sopenharmony_ci
9448c2ecf20Sopenharmony_cistatic const struct attribute_group pdcs_attr_group = {
9458c2ecf20Sopenharmony_ci	.attrs = pdcs_subsys_attrs,
9468c2ecf20Sopenharmony_ci};
9478c2ecf20Sopenharmony_ci
9488c2ecf20Sopenharmony_cistatic struct kobject *stable_kobj;
9498c2ecf20Sopenharmony_cistatic struct kset *paths_kset;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci/**
9528c2ecf20Sopenharmony_ci * pdcs_register_pathentries - Prepares path entries kobjects for sysfs usage.
9538c2ecf20Sopenharmony_ci *
9548c2ecf20Sopenharmony_ci * It creates kobjects corresponding to each path entry with nice sysfs
9558c2ecf20Sopenharmony_ci * links to the real device. This is where the magic takes place: when
9568c2ecf20Sopenharmony_ci * registering the subsystem attributes during module init, each kobject hereby
9578c2ecf20Sopenharmony_ci * created will show in the sysfs tree as a folder containing files as defined
9588c2ecf20Sopenharmony_ci * by path_subsys_attr[].
9598c2ecf20Sopenharmony_ci */
9608c2ecf20Sopenharmony_cistatic inline int __init
9618c2ecf20Sopenharmony_cipdcs_register_pathentries(void)
9628c2ecf20Sopenharmony_ci{
9638c2ecf20Sopenharmony_ci	unsigned short i;
9648c2ecf20Sopenharmony_ci	struct pdcspath_entry *entry;
9658c2ecf20Sopenharmony_ci	int err;
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	/* Initialize the entries rw_lock before anything else */
9688c2ecf20Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++)
9698c2ecf20Sopenharmony_ci		rwlock_init(&entry->rw_lock);
9708c2ecf20Sopenharmony_ci
9718c2ecf20Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++) {
9728c2ecf20Sopenharmony_ci		write_lock(&entry->rw_lock);
9738c2ecf20Sopenharmony_ci		err = pdcspath_fetch(entry);
9748c2ecf20Sopenharmony_ci		write_unlock(&entry->rw_lock);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		if (err < 0)
9778c2ecf20Sopenharmony_ci			continue;
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci		entry->kobj.kset = paths_kset;
9808c2ecf20Sopenharmony_ci		err = kobject_init_and_add(&entry->kobj, &ktype_pdcspath, NULL,
9818c2ecf20Sopenharmony_ci					   "%s", entry->name);
9828c2ecf20Sopenharmony_ci		if (err) {
9838c2ecf20Sopenharmony_ci			kobject_put(&entry->kobj);
9848c2ecf20Sopenharmony_ci			return err;
9858c2ecf20Sopenharmony_ci		}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci		/* kobject is now registered */
9888c2ecf20Sopenharmony_ci		write_lock(&entry->rw_lock);
9898c2ecf20Sopenharmony_ci		entry->ready = 2;
9908c2ecf20Sopenharmony_ci		write_unlock(&entry->rw_lock);
9918c2ecf20Sopenharmony_ci
9928c2ecf20Sopenharmony_ci		/* Add a nice symlink to the real device */
9938c2ecf20Sopenharmony_ci		if (entry->dev) {
9948c2ecf20Sopenharmony_ci			err = sysfs_create_link(&entry->kobj, &entry->dev->kobj, "device");
9958c2ecf20Sopenharmony_ci			WARN_ON(err);
9968c2ecf20Sopenharmony_ci		}
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_ci		kobject_uevent(&entry->kobj, KOBJ_ADD);
9998c2ecf20Sopenharmony_ci	}
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return 0;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci/**
10058c2ecf20Sopenharmony_ci * pdcs_unregister_pathentries - Routine called when unregistering the module.
10068c2ecf20Sopenharmony_ci */
10078c2ecf20Sopenharmony_cistatic inline void
10088c2ecf20Sopenharmony_cipdcs_unregister_pathentries(void)
10098c2ecf20Sopenharmony_ci{
10108c2ecf20Sopenharmony_ci	unsigned short i;
10118c2ecf20Sopenharmony_ci	struct pdcspath_entry *entry;
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	for (i = 0; (entry = pdcspath_entries[i]); i++) {
10148c2ecf20Sopenharmony_ci		read_lock(&entry->rw_lock);
10158c2ecf20Sopenharmony_ci		if (entry->ready >= 2)
10168c2ecf20Sopenharmony_ci			kobject_put(&entry->kobj);
10178c2ecf20Sopenharmony_ci		read_unlock(&entry->rw_lock);
10188c2ecf20Sopenharmony_ci	}
10198c2ecf20Sopenharmony_ci}
10208c2ecf20Sopenharmony_ci
10218c2ecf20Sopenharmony_ci/*
10228c2ecf20Sopenharmony_ci * For now we register the stable subsystem with the firmware subsystem
10238c2ecf20Sopenharmony_ci * and the paths subsystem with the stable subsystem
10248c2ecf20Sopenharmony_ci */
10258c2ecf20Sopenharmony_cistatic int __init
10268c2ecf20Sopenharmony_cipdc_stable_init(void)
10278c2ecf20Sopenharmony_ci{
10288c2ecf20Sopenharmony_ci	int rc = 0, error = 0;
10298c2ecf20Sopenharmony_ci	u32 result;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	/* find the size of the stable storage */
10328c2ecf20Sopenharmony_ci	if (pdc_stable_get_size(&pdcs_size) != PDC_OK)
10338c2ecf20Sopenharmony_ci		return -ENODEV;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	/* make sure we have enough data */
10368c2ecf20Sopenharmony_ci	if (pdcs_size < 96)
10378c2ecf20Sopenharmony_ci		return -ENODATA;
10388c2ecf20Sopenharmony_ci
10398c2ecf20Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX " facility v%s\n", PDCS_VERSION);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	/* get OSID */
10428c2ecf20Sopenharmony_ci	if (pdc_stable_read(PDCS_ADDR_OSID, &result, sizeof(result)) != PDC_OK)
10438c2ecf20Sopenharmony_ci		return -EIO;
10448c2ecf20Sopenharmony_ci
10458c2ecf20Sopenharmony_ci	/* the actual result is 16 bits away */
10468c2ecf20Sopenharmony_ci	pdcs_osid = (u16)(result >> 16);
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_ci	/* For now we'll register the directory at /sys/firmware/stable */
10498c2ecf20Sopenharmony_ci	stable_kobj = kobject_create_and_add("stable", firmware_kobj);
10508c2ecf20Sopenharmony_ci	if (!stable_kobj) {
10518c2ecf20Sopenharmony_ci		rc = -ENOMEM;
10528c2ecf20Sopenharmony_ci		goto fail_firmreg;
10538c2ecf20Sopenharmony_ci	}
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	/* Don't forget the root entries */
10568c2ecf20Sopenharmony_ci	error = sysfs_create_group(stable_kobj, &pdcs_attr_group);
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	/* register the paths kset as a child of the stable kset */
10598c2ecf20Sopenharmony_ci	paths_kset = kset_create_and_add("paths", NULL, stable_kobj);
10608c2ecf20Sopenharmony_ci	if (!paths_kset) {
10618c2ecf20Sopenharmony_ci		rc = -ENOMEM;
10628c2ecf20Sopenharmony_ci		goto fail_ksetreg;
10638c2ecf20Sopenharmony_ci	}
10648c2ecf20Sopenharmony_ci
10658c2ecf20Sopenharmony_ci	/* now we create all "files" for the paths kset */
10668c2ecf20Sopenharmony_ci	if ((rc = pdcs_register_pathentries()))
10678c2ecf20Sopenharmony_ci		goto fail_pdcsreg;
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	return rc;
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cifail_pdcsreg:
10728c2ecf20Sopenharmony_ci	pdcs_unregister_pathentries();
10738c2ecf20Sopenharmony_ci	kset_unregister(paths_kset);
10748c2ecf20Sopenharmony_ci
10758c2ecf20Sopenharmony_cifail_ksetreg:
10768c2ecf20Sopenharmony_ci	kobject_put(stable_kobj);
10778c2ecf20Sopenharmony_ci
10788c2ecf20Sopenharmony_cifail_firmreg:
10798c2ecf20Sopenharmony_ci	printk(KERN_INFO PDCS_PREFIX " bailing out\n");
10808c2ecf20Sopenharmony_ci	return rc;
10818c2ecf20Sopenharmony_ci}
10828c2ecf20Sopenharmony_ci
10838c2ecf20Sopenharmony_cistatic void __exit
10848c2ecf20Sopenharmony_cipdc_stable_exit(void)
10858c2ecf20Sopenharmony_ci{
10868c2ecf20Sopenharmony_ci	pdcs_unregister_pathentries();
10878c2ecf20Sopenharmony_ci	kset_unregister(paths_kset);
10888c2ecf20Sopenharmony_ci	kobject_put(stable_kobj);
10898c2ecf20Sopenharmony_ci}
10908c2ecf20Sopenharmony_ci
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_cimodule_init(pdc_stable_init);
10938c2ecf20Sopenharmony_cimodule_exit(pdc_stable_exit);
1094