--- drivers/parisc/Makefile.orig	2005-01-13 01:17:12.000000000 +0100
+++ drivers/parisc/Makefile	2006-05-03 19:44:05.000000000 +0200
@@ -23,5 +23,6 @@
 obj-$(CONFIG_SUPERIO)		+= superio.o
 obj-$(CONFIG_CHASSIS_LCD_LED)	+= led.o
 obj-$(CONFIG_PDC_STABLE)	+= pdc_stable.o
+obj-$(CONFIG_PDC_NVOLATILE)	+= pdc_nvolatile.o
 obj-y				+= power.o
 
--- drivers/parisc/Kconfig.orig	2005-01-13 16:35:18.000000000 +0100
+++ drivers/parisc/Kconfig	2006-05-03 19:46:01.000000000 +0200
@@ -166,4 +166,17 @@
 	  To compile this driver as a module, choose M here.
 	  The module will be called pdc_stable.
 
+config PDC_NVOLATILE
+	tristate "PDC Non-Volatile Memory support"
+	depends on SYSFS
+	default y
+	help
+	  Say Y here if you want to enable support for accessing Non-Volatile
+	  Memory through SysFS.
+	
+	  If unsure, say Y.
+	
+	  To compile this driver as a module, choose M here.
+	  The module will be called pdc_nvolatile.
+
 endmenu
--- include/asm-parisc/pdc.h.orig	2006-04-03 15:45:55.000000000 +0200
+++ include/asm-parisc/pdc.h	2006-05-03 21:02:28.000000000 +0200
@@ -102,6 +102,11 @@
 #define PDC_STABLE_INITIALIZE	4
 
 #define PDC_NVOLATILE	11		/* often not implemented	*/
+#define PDC_NVOLATILE_READ	0
+#define PDC_NVOLATILE_WRITE	1
+#define PDC_NVOLATILE_RETURN_SIZE	2
+#define PDC_NVOLATILE_VERIFY_CONTENTS	3
+#define PDC_NVOLATILE_INITIALIZE	4
 
 #define PDC_ADD_VALID	12		/* Memory validation PDC call	*/
 #define PDC_ADD_VALID_VERIFY	0	/* Make PDC_ADD_VALID verify region */
@@ -745,6 +750,12 @@
 int pdc_stable_verify_contents(void);
 int pdc_stable_initialize(void);
 
+int pdc_nvolatile_read(unsigned long staddr, void *memaddr, unsigned long count);
+int pdc_nvolatile_write(unsigned long staddr, void *memaddr, unsigned long count);
+int pdc_nvolatile_get_size(unsigned long *size);
+int pdc_nvolatile_verify_contents(void);
+int pdc_nvolatile_initialize(void);
+
 int pdc_pci_irt_size(unsigned long *num_entries, unsigned long hpa);
 int pdc_pci_irt(unsigned long num_entries, unsigned long hpa, void *tbl);
 
--- /dev/null	2006-05-03 20:53:58.796818752 +0200
+++ drivers/parisc/pdc_nvolatile.c	2006-05-03 21:15:37.000000000 +0200
@@ -0,0 +1,251 @@
+/* 
+ *    Interfaces to retrieve and set PDC Non-Volatile Memory (firmware)
+ *
+ *    Copyright (C) 2006 Thibaut VARENE <varenet@parisc-linux.org>
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License, version 2, as
+ *    published by the Free Software Foundation.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License
+ *    along with this program; if not, write to the Free Software
+ *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ *
+ *
+ *    The current policy wrt file permissions is:
+ *	- write: root only
+ *	- read: (reading triggers PDC calls) ? root only : everyone
+ *    The rationale is that PDC calls could hog (DoS) the machine.
+ *
+ */
+
+#define PDCNV_DEBUG
+#ifdef PDCNV_DEBUG
+#define DPRINTK(fmt, args...)	printk(KERN_DEBUG fmt, ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/capability.h>
+#include <linux/ctype.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+
+#include <asm/pdc.h>
+#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+
+#define PDCNV_VERSION	"0.01"
+#define PDCNV_PREFIX	"PDC Non-Volatile"
+
+#define PDCNV_ADDR_HVDI	0x0000	/* HVERSION-Dependent Information */
+#define PDCNV_ADDR_IPL	0x0024	/* IPL Information */
+#define PDCNV_ADDR_IPL_BP	0x0024	/* Boot Path */
+#define PDCNV_ADDR_IPL_ISLR	0x0044	/* ISL Revision */
+#define PDCNV_ADDR_IPL_T	0x0048	/* Timestamp */
+#define PDCNV_ADDR_IPL_LUE	0x004C	/* LIF Utility Entries */
+#define PDCNV_ADDR_IPL_P	0x007C	/* Pointer */
+#define PDCNV_ADDR_OSPI	0x0080	/* OS Panic Information */
+
+MODULE_AUTHOR("Thibaut VARENE <varenet@parisc-linux.org>");
+MODULE_DESCRIPTION("sysfs interface to HP PDC Non-Volatile Memory");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(PDCNV_VERSION);
+
+/* holds NVRAM size. Initialized once and for all, no lock needed */
+static unsigned long pdcnv_size __read_mostly = 0;
+
+/* our big lock. No more than one PDC_NVOLATILE call may happen at a time */
+static spinlock_t nv_lock;
+
+/* This struct defines what we need to deal with a ipl info entry */
+struct pdcnvipl_entry {
+	rwlock_t rw_lock;		/* to protect path entry access */
+	short ready;			/* entry record is valid if != 0 */
+	unsigned long addr;		/* entry address in stable storage */
+	char *name;			/* entry name */
+//	struct kobject kobj;
+};
+
+struct pdcnvipl_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct pdcnvipl_entry *entry, char *buf);
+	ssize_t (*store)(struct pdcnvipl_entry *entry, const char *buf, size_t count);
+};
+
+#define PDCNVIPL_ENTRY(_addr, _name) \
+struct pdcnvipl_entry pdcnvipl_entry_##_name = { \
+	.ready = 0, \
+	.addr = _addr, \
+	.name = __stringify(_name), \
+};
+
+#define PDCNV_ATTR(_name, _mode, _show, _store) \
+struct subsys_attribute pdcnv_attr_##_name = { \
+	.attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+	.show = _show, \
+	.store = _store, \
+};
+
+#define IPL_ATTR(_name, _mode, _show, _store) \
+struct pdcnvipl_attribute ipl_attr_##_name = { \
+	.attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \
+	.show = _show, \
+	.store = _store, \
+};
+
+#define to_pdcnvipl_attribute(_attr) container_of(_attr, struct pdcnvipl_attribute, attr)
+#define to_pdcnvipl_entry(obj)  container_of(obj, struct pdcnvipl_entry, kobj)
+
+
+/* For more insight of what's going on here, refer to PDC Procedures doc,
+ * Section PDC_NVOLATILE */
+
+/**
+ * pdcnv_size_read - NVRAM size output.
+ * @entry: An allocated and populated subsytem struct. We don't use it tho.
+ * @buf: The output buffer to write to.
+ */
+static ssize_t
+pdcnv_size_read(struct subsystem *entry, char *buf)
+{
+	char *out = buf;
+	
+	if (!entry || !buf)
+		return -EINVAL;
+		
+	/* show the size of the NVRAM */
+	out += sprintf(out, "%ld\n", pdcnv_size);
+
+	return out - buf;
+}
+
+/**
+ */
+static ssize_t
+pdcnv_read(struct subsystem *entry, char *buf, unsigned long addr, unsigned long
+count)
+{
+	char *out = buf;
+	u32 result[count/4];
+	int ret, i;
+	
+	if (!entry || !buf)
+		return -EINVAL;
+
+	/* get fast-size */
+	spin_lock(&nv_lock);
+	ret = pdc_stable_read(addr, &result, sizeof(result));
+	spin_unlock(&nv_lock);
+	
+	if (ret != PDC_OK)
+		return -EIO;
+
+	for (i=0; i<=(count/4); i++)
+		out += sprintf(out, "%x\n", result[i]);
+	return out - buf;
+}
+
+/**
+ */
+static inline ssize_t
+pdcnv_hvdi_read(struct subsystem *entry, char *buf)
+{
+	return pdcnv_read(entry, buf, PDCNV_ADDR_HVDI, 36);
+}
+
+static inline ssize_t
+pdcnv_ipl_read(struct subsystem *entry, char *buf)
+{
+	return pdcnv_read(entry, buf, PDCNV_ADDR_IPL, 92);
+}
+
+static inline ssize_t
+pdcnv_ospi_read(struct subsystem *entry, char *buf)
+{
+	return pdcnv_read(entry, buf, PDCNV_ADDR_OSPI, 128);
+}
+
+
+
+/* The remaining attributes. */
+static PDCNV_ATTR(size, 0444, pdcnv_size_read, NULL);
+static PDCNV_ATTR(hvdi, 0400, pdcnv_hvdi_read, NULL);
+static PDCNV_ATTR(ipl, 0400, pdcnv_ipl_read, NULL);
+static PDCNV_ATTR(ospi, 0400, pdcnv_ospi_read, NULL);
+
+static struct subsys_attribute *pdcnv_subsys_attrs[] = {
+	&pdcnv_attr_size,
+	&pdcnv_attr_hvdi,
+	&pdcnv_attr_ipl,
+	&pdcnv_attr_ospi,
+	NULL,
+};
+
+static decl_subsys(nvolatile, NULL, NULL);
+
+/*
+ * For now we register the stable subsystem with the firmware subsystem
+ * and the paths subsystem with the stable subsystem
+ */
+static int __init
+pdc_nvolatile_init(void)
+{
+	struct subsys_attribute *attr;
+	int i, rc = 0, error = 0, ret;
+
+	spin_lock_init(&nv_lock);
+	
+	/* find the size of the stable storage */
+	spin_lock(&nv_lock);
+	ret = pdc_nvolatile_get_size(&pdcnv_size);
+	spin_unlock(&nv_lock);
+	
+	DPRINTK("fail: %d, %ld\n", ret, pdcnv_size);
+	if (ret != PDC_OK) 
+		return -ENODEV;
+
+	/* make sure we have enough data */
+	if (pdcnv_size < 256)
+		return -ENODATA;
+
+	printk(KERN_INFO PDCNV_PREFIX " facility v%s\n", PDCNV_VERSION);
+
+	/* For now we'll register the stable subsys within this driver */
+	if ((rc = firmware_register(&nvolatile_subsys)))
+		goto fail_firmreg;
+
+	/* Don't forget the root entries */
+	for (i = 0; (attr = pdcnv_subsys_attrs[i]) && !error; i++)
+		if (attr->show)
+			error = subsys_create_file(&nvolatile_subsys, attr);
+	return rc;
+		
+fail_firmreg:
+	printk(KERN_INFO PDCNV_PREFIX " bailing out\n");
+	return rc;
+}
+
+static void __exit
+pdc_nvolatile_exit(void)
+{
+	firmware_unregister(&nvolatile_subsys);
+}
+
+
+module_init(pdc_nvolatile_init);
+module_exit(pdc_nvolatile_exit);
--- arch/parisc/kernel/firmware.c.orig	2005-12-17 19:12:16.000000000 +0100
+++ arch/parisc/kernel/firmware.c	2006-05-03 18:56:01.000000000 +0200
@@ -574,6 +574,8 @@
 }
 EXPORT_SYMBOL(pdc_lan_station_id);
 
+/* Stable Storage routines - always implemented */
+
 /**
  * pdc_stable_read - Read data from Stable Storage.
  * @staddr: Stable Storage address to access.
@@ -682,6 +684,120 @@
 }
 EXPORT_SYMBOL(pdc_stable_initialize);
 
+/* NVRAM routines - not always implemented
+ * stdaddr is supposed to be 32bit on pa1.1, 64bit on pa2.0.
+ * count and size are u32 multiple of 4 on pa1.1,
+ *                    u64 multiple of 8 on pa2.0.
+ * NVRAM will blow if there's more than one PDC_NVOLATILE call at a time
+ */
+
+/**
+ * pdc_nvolatile_read - Read data from NVRAM.
+ * @staddr: NVRAM address to access.
+ * @memaddr: The memory address where NVRAM data shall be copied.
+ * @count: number of bytes to transfert.
+ *
+ * This PDC call reads from the NVRAM address supplied in staddr
+ * and copies count bytes to the memory address memaddr.
+ * The call will fail if staddr+count > PDC_NVOLATILE size.
+ */
+int pdc_nvolatile_read(unsigned long staddr, void *memaddr, unsigned long count)
+{
+       int retval;
+
+       spin_lock_irq(&pdc_lock);
+       retval = mem_pdc_call(PDC_NVOLATILE, PDC_NVOLATILE_READ, staddr,
+               __pa(pdc_result), count);
+       convert_to_wide(pdc_result);
+       memcpy(memaddr, pdc_result, count);
+       spin_unlock_irq(&pdc_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(pdc_nvolatile_read);
+
+/**
+ * pdc_nvolatile_write - Write data to NVRAM.
+ * @staddr: NVRAM address to access.
+ * @memaddr: The memory address where NVRAM data shall be read from.
+ * @count: number of bytes to transfert.
+ *
+ * This PDC call reads count bytes from the supplied memaddr address,
+ * and copies count bytes to the Stable Storage address staddr.
+ * The call will fail if staddr+count > PDC_NVOLATILE size.
+ */
+int pdc_nvolatile_write(unsigned long staddr, void *memaddr, unsigned long count)
+{
+       int retval;
+
+       spin_lock_irq(&pdc_lock);
+       memcpy(pdc_result, memaddr, count);
+       convert_to_wide(pdc_result);
+       retval = mem_pdc_call(PDC_NVOLATILE, PDC_NVOLATILE_WRITE, staddr,
+               __pa(pdc_result), count);
+       spin_unlock_irq(&pdc_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(pdc_nvolatile_write);
+
+/**
+ * pdc_nvolatile_get_size - Get NVRAM size in bytes.
+ * @size: pointer where the size will be stored.
+ *
+ * This PDC call returns the number of bytes in the processor's NVRAM,
+ * which is the number of contiguous bytes implemented in NVRAM
+ * starting from staddr=0.
+ */
+int pdc_nvolatile_get_size(unsigned long *size)
+{
+       int retval;
+
+       spin_lock_irq(&pdc_lock);
+       retval = mem_pdc_call(PDC_NVOLATILE, PDC_NVOLATILE_RETURN_SIZE, __pa(pdc_result));
+       *size = pdc_result[0];
+       spin_unlock_irq(&pdc_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(pdc_nvolatile_get_size);
+
+/**
+ * pdc_nvolatile_verify_contents - Checks that NVRAM contents are valid.
+ *
+ * This PDC call is meant to be used to check the integrity of the current
+ * contents of NVRAM.
+ */
+int pdc_nvolatile_verify_contents(void)
+{
+       int retval;
+
+       spin_lock_irq(&pdc_lock);
+       retval = mem_pdc_call(PDC_NVOLATILE, PDC_NVOLATILE_VERIFY_CONTENTS);
+       spin_unlock_irq(&pdc_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(pdc_nvolatile_verify_contents);
+
+/**
+ * pdc_nvolatile_initialize - Sets NVRAM contents to zero and initialize
+ * the validity indicator.
+ *
+ * This PDC call will erase all contents of NVRAM. Use with care!
+ */
+int pdc_nvolatile_initialize(void)
+{
+       int retval;
+
+       spin_lock_irq(&pdc_lock);
+       retval = mem_pdc_call(PDC_NVOLATILE, PDC_NVOLATILE_INITIALIZE);
+       spin_unlock_irq(&pdc_lock);
+
+       return retval;
+}
+EXPORT_SYMBOL(pdc_nvolatile_initialize);
+
 /**
  * pdc_get_initiator - Get the SCSI Interface Card params (SCSI ID, SDTR, SE or LVD)
  * @hwpath: fully bc.mod style path to the device.

