++uint
++osl_getcycles(void)
++{
++ uint cycles;
++
++#if defined(mips)
++ cycles = read_c0_count() * 2;
++#elif defined(__i386__)
++ rdtscl(cycles);
++#else
++ cycles = 0;
++#endif
++ return cycles;
++}
++
++void *
++osl_reg_map(uint32 pa, uint size)
++{
++ return (ioremap_nocache((unsigned long)pa, (unsigned long)size));
++}
++
++void
++osl_reg_unmap(void *va)
++{
++ iounmap(va);
++}
++
++int
++osl_busprobe(uint32 *val, uint32 addr)
++{
++#ifdef mips
++ return get_dbe(*val, (uint32*)addr);
++#else
++ *val = readl(addr);
++ return 0;
++#endif
++}
++
++void*
++osl_dma_alloc_consistent(void *dev, uint size, ulong *pap)
++{
++ return (pci_alloc_consistent((struct pci_dev*)dev, size, (dma_addr_t*)pap));
++}
++
++void
++osl_dma_free_consistent(void *dev, void *va, uint size, ulong pa)
++{
++ pci_free_consistent((struct pci_dev*)dev, size, va, (dma_addr_t)pa);
++}
++
++uint
++osl_dma_map(void *dev, void *va, uint size, int direction)
++{
++ int dir;
++
++ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
++ return (pci_map_single(dev, va, size, dir));
++}
++
++void
++osl_dma_unmap(void *dev, uint pa, uint size, int direction)
++{
++ int dir;
++
++ dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
++ pci_unmap_single(dev, (uint32)pa, size, dir);
++}
++
++void
++osl_delay(uint usec)
++{
++ udelay(usec);
++}
++
++uchar*
++osl_pktdata(void *drv, void *skb)
++{
++ return (((struct sk_buff*)skb)->data);
++}
++
++uint
++osl_pktlen(void *drv, void *skb)
++{
++ return (((struct sk_buff*)skb)->len);
++}
++
++void*
++osl_pktnext(void *drv, void *skb)
++{
++ return (((struct sk_buff*)skb)->next);
++}
++
++void
++osl_pktsetnext(void *skb, void *x)
++{
++ ((struct sk_buff*)skb)->next = (struct sk_buff*)x;
++}
++
++void
++osl_pktsetlen(void *drv, void *skb, uint len)
++{
++ __skb_trim((struct sk_buff*)skb, len);
++}
++
++uchar*
++osl_pktpush(void *drv, void *skb, int bytes)
++{
++ return (skb_push((struct sk_buff*)skb, bytes));
++}
++
++uchar*
++osl_pktpull(void *drv, void *skb, int bytes)
++{
++ return (skb_pull((struct sk_buff*)skb, bytes));
++}
++
++void*
++osl_pktdup(void *drv, void *skb)
++{
++ return (skb_clone((struct sk_buff*)skb, GFP_ATOMIC));
++}
++
++void*
++osl_pktcookie(void *skb)
++{
++ return ((void*)((struct sk_buff*)skb)->csum);
++}
++
++void
++osl_pktsetcookie(void *skb, void *x)
++{
++ ((struct sk_buff*)skb)->csum = (uint)x;
++}
++
++void*
++osl_pktlink(void *skb)
++{
++ return (((struct sk_buff*)skb)->prev);
++}
++
++void
++osl_pktsetlink(void *skb, void *x)
++{
++ ((struct sk_buff*)skb)->prev = (struct sk_buff*)x;
++}
++
++#endif
+diff -Nur linux-2.6.12.5/arch/mips/bcm947xx/broadcom/Makefile linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/Makefile
+--- linux-2.6.12.5/arch/mips/bcm947xx/broadcom/Makefile 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/Makefile 2005-11-19 14:16:38.941631500 +0100
+@@ -0,0 +1,7 @@
++#
++# Makefile for the BCM47xx specific kernel interface routines
++# under Linux.
++#
++
++obj-y := sbutils.o linux_osl.o bcmsrom.o bcmutils.o sbmips.o sbpci.o hnddma.o
++#obj-y := nvram.o nvram_linux.o
+diff -Nur linux-2.6.12.5/arch/mips/bcm947xx/broadcom/nvram.c linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/nvram.c
+--- linux-2.6.12.5/arch/mips/bcm947xx/broadcom/nvram.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/nvram.c 2005-11-19 02:28:26.438059500 +0100
+@@ -0,0 +1,321 @@
++/*
++ * NVRAM variable manipulation (common)
++ *
++ * Copyright 2004, Broadcom Corporation
++ * All Rights Reserved.
++ *
++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
++ *
++ * $Id$
++ */
++
++#include <typedefs.h>
++#include <osl.h>
++#include <bcmendian.h>
++#include <bcmnvram.h>
++#include <bcmutils.h>
++#include <sbsdram.h>
++
++extern struct nvram_tuple * BCMINIT(_nvram_realloc)(struct nvram_tuple *t, const char *name, const char *value);
++extern void BCMINIT(_nvram_free)(struct nvram_tuple *t);
++extern int BCMINIT(_nvram_read)(void *buf);
++
++char * BCMINIT(_nvram_get)(const char *name);
++int BCMINIT(_nvram_set)(const char *name, const char *value);
++int BCMINIT(_nvram_unset)(const char *name);
++int BCMINIT(_nvram_getall)(char *buf, int count);
++int BCMINIT(_nvram_commit)(struct nvram_header *header);
++int BCMINIT(_nvram_init)(void);
++void BCMINIT(_nvram_exit)(void);
++
++static struct nvram_tuple * BCMINITDATA(nvram_hash)[257];
++static struct nvram_tuple * nvram_dead;
++
++/* Free all tuples. Should be locked. */
++static void
++BCMINITFN(nvram_free)(void)
++{
++ uint i;
++ struct nvram_tuple *t, *next;
++
++ /* Free hash table */
++ for (i = 0; i < ARRAYSIZE(BCMINIT(nvram_hash)); i++) {
++ for (t = BCMINIT(nvram_hash)[i]; t; t = next) {
++ next = t->next;
++ BCMINIT(_nvram_free)(t);
++ }
++ BCMINIT(nvram_hash)[i] = NULL;
++ }
++
++ /* Free dead table */
++ for (t = nvram_dead; t; t = next) {
++ next = t->next;
++ BCMINIT(_nvram_free)(t);
++ }
++ nvram_dead = NULL;
++
++ /* Indicate to per-port code that all tuples have been freed */
++ BCMINIT(_nvram_free)(NULL);
++}
++
++/* String hash */
++static INLINE uint
++hash(const char *s)
++{
++ uint hash = 0;
++
++ while (*s)
++ hash = 31 * hash + *s++;
++
++ return hash;
++}
++
++/* (Re)initialize the hash table. Should be locked. */
++static int
++BCMINITFN(nvram_rehash)(struct nvram_header *header)
++{
++ char buf[] = "0xXXXXXXXX", *name, *value, *end, *eq;
++
++ /* (Re)initialize hash table */
++ BCMINIT(nvram_free)();
++
++ /* Parse and set "name=value\0 ... \0\0" */
++ name = (char *) &header[1];
++ end = (char *) header + NVRAM_SPACE - 2;
++ end[0] = end[1] = '\0';
++ for (; *name; name = value + strlen(value) + 1) {
++ if (!(eq = strchr(name, '=')))
++ break;
++ *eq = '\0';
++ value = eq + 1;
++ BCMINIT(_nvram_set)(name, value);
++ *eq = '=';
++ }
++
++ /* Set special SDRAM parameters */
++ if (!BCMINIT(_nvram_get)("sdram_init")) {
++ sprintf(buf, "0x%04X", (uint16)(header->crc_ver_init >> 16));
++ BCMINIT(_nvram_set)("sdram_init", buf);
++ }
++ if (!BCMINIT(_nvram_get)("sdram_config")) {
++ sprintf(buf, "0x%04X", (uint16)(header->config_refresh & 0xffff));
++ BCMINIT(_nvram_set)("sdram_config", buf);
++ }
++ if (!BCMINIT(_nvram_get)("sdram_refresh")) {
++ sprintf(buf, "0x%04X", (uint16)((header->config_refresh >> 16) & 0xffff));
++ BCMINIT(_nvram_set)("sdram_refresh", buf);
++ }
++ if (!BCMINIT(_nvram_get)("sdram_ncdl")) {
++ sprintf(buf, "0x%08X", header->config_ncdl);
++ BCMINIT(_nvram_set)("sdram_ncdl", buf);
++ }
++
++ return 0;
++}
++
++/* Get the value of an NVRAM variable. Should be locked. */
++char *
++BCMINITFN(_nvram_get)(const char *name)
++{
++ uint i;
++ struct nvram_tuple *t;
++ char *value;
++
++ if (!name)
++ return NULL;
++
++ /* Hash the name */
++ i = hash(name) % ARRAYSIZE(BCMINIT(nvram_hash));
++
++ /* Find the associated tuple in the hash table */
++ for (t = BCMINIT(nvram_hash)[i]; t && strcmp(t->name, name); t = t->next);
++
++ value = t ? t->value : NULL;
++
++ return value;
++}
++
++/* Get the value of an NVRAM variable. Should be locked. */
++int
++BCMINITFN(_nvram_set)(const char *name, const char *value)
++{
++ uint i;
++ struct nvram_tuple *t, *u, **prev;
++
++ /* Hash the name */
++ i = hash(name) % ARRAYSIZE(BCMINIT(nvram_hash));
++
++ /* Find the associated tuple in the hash table */
++ for (prev = &BCMINIT(nvram_hash)[i], t = *prev; t && strcmp(t->name, name); prev = &t->next, t = *prev);
++
++ /* (Re)allocate tuple */
++ if (!(u = BCMINIT(_nvram_realloc)(t, name, value)))
++ return -12; /* -ENOMEM */
++
++ /* Value reallocated */
++ if (t && t == u)
++ return 0;
++
++ /* Move old tuple to the dead table */
++ if (t) {
++ *prev = t->next;
++ t->next = nvram_dead;
++ nvram_dead = t;
++ }
++
++ /* Add new tuple to the hash table */
++ u->next = BCMINIT(nvram_hash)[i];
++ BCMINIT(nvram_hash)[i] = u;
++
++ return 0;
++}
++
++/* Unset the value of an NVRAM variable. Should be locked. */
++int
++BCMINITFN(_nvram_unset)(const char *name)
++{
++ uint i;
++ struct nvram_tuple *t, **prev;
++
++ if (!name)
++ return 0;
++
++ /* Hash the name */
++ i = hash(name) % ARRAYSIZE(BCMINIT(nvram_hash));
++
++ /* Find the associated tuple in the hash table */
++ for (prev = &BCMINIT(nvram_hash)[i], t = *prev; t && strcmp(t->name, name); prev = &t->next, t = *prev);
++
++ /* Move it to the dead table */
++ if (t) {
++ *prev = t->next;
++ t->next = nvram_dead;
++ nvram_dead = t;
++ }
++
++ return 0;
++}
++
++/* Get all NVRAM variables. Should be locked. */
++int
++BCMINITFN(_nvram_getall)(char *buf, int count)
++{
++ uint i;
++ struct nvram_tuple *t;
++ int len = 0;
++
++ bzero(buf, count);
++
++ /* Write name=value\0 ... \0\0 */
++ for (i = 0; i < ARRAYSIZE(BCMINIT(nvram_hash)); i++) {
++ for (t = BCMINIT(nvram_hash)[i]; t; t = t->next) {
++ if ((count - len) > (strlen(t->name) + 1 + strlen(t->value) + 1))
++ len += sprintf(buf + len, "%s=%s", t->name, t->value) + 1;
++ else
++ break;
++ }
++ }
++
++ return 0;
++}
++
++/* Regenerate NVRAM. Should be locked. */
++int
++BCMINITFN(_nvram_commit)(struct nvram_header *header)
++{
++ char *init, *config, *refresh, *ncdl;
++ char *ptr, *end;
++ int i;
++ struct nvram_tuple *t;
++ struct nvram_header tmp;
++ uint8 crc;
++
++ /* Regenerate header */
++ header->magic = NVRAM_MAGIC;
++ header->crc_ver_init = (NVRAM_VERSION << 8);
++ if (!(init = BCMINIT(_nvram_get)("sdram_init")) ||
++ !(config = BCMINIT(_nvram_get)("sdram_config")) ||
++ !(refresh = BCMINIT(_nvram_get)("sdram_refresh")) ||
++ !(ncdl = BCMINIT(_nvram_get)("sdram_ncdl"))) {
++ header->crc_ver_init |= SDRAM_INIT << 16;
++ header->config_refresh = SDRAM_CONFIG;
++ header->config_refresh |= SDRAM_REFRESH << 16;
++ header->config_ncdl = 0;
++ } else {
++ header->crc_ver_init |= (bcm_strtoul(init, NULL, 0) & 0xffff) << 16;
++ header->config_refresh = bcm_strtoul(config, NULL, 0) & 0xffff;
++ header->config_refresh |= (bcm_strtoul(refresh, NULL, 0) & 0xffff) << 16;
++ header->config_ncdl = bcm_strtoul(ncdl, NULL, 0);
++ }
++
++ /* Clear data area */
++ ptr = (char *) header + sizeof(struct nvram_header);
++ bzero(ptr, NVRAM_SPACE - sizeof(struct nvram_header));
++
++ /* Leave space for a double NUL at the end */
++ end = (char *) header + NVRAM_SPACE - 2;
++
++ /* Write out all tuples */
++ for (i = 0; i < ARRAYSIZE(BCMINIT(nvram_hash)); i++) {
++ for (t = BCMINIT(nvram_hash)[i]; t; t = t->next) {
++ if ((ptr + strlen(t->name) + 1 + strlen(t->value) + 1) > end)
++ break;
++ ptr += sprintf(ptr, "%s=%s", t->name, t->value) + 1;
++ }
++ }
++
++ /* End with a double NUL */
++ ptr += 2;
++
++ /* Set new length */
++ header->len = ROUNDUP(ptr - (char *) header, 4);
++
++ /* Little-endian CRC8 over the last 11 bytes of the header */
++ tmp.crc_ver_init = htol32(header->crc_ver_init);
++ tmp.config_refresh = htol32(header->config_refresh);
++ tmp.config_ncdl = htol32(header->config_ncdl);
++ crc = hndcrc8((char *) &tmp + 9, sizeof(struct nvram_header) - 9, CRC8_INIT_VALUE);
++
++ /* Continue CRC8 over data bytes */
++ crc = hndcrc8((char *) &header[1], header->len - sizeof(struct nvram_header), crc);
++
++ /* Set new CRC8 */
++ header->crc_ver_init |= crc;
++
++ /* Reinitialize hash table */
++ return BCMINIT(nvram_rehash)(header);
++}
++
++/* Initialize hash table. Should be locked. */
++int
++BCMINITFN(_nvram_init)(void)
++{
++ struct nvram_header *header;
++ int ret;
++ void *osh;
++
++ /* get kernel osl handler */
++ osh = osl_attach(NULL);
++
++ if (!(header = (struct nvram_header *) MALLOC(osh, NVRAM_SPACE))) {
++ printf("nvram_init: out of memory, malloced %d bytes\n", MALLOCED(osh));
++ return -12; /* -ENOMEM */
++ }
++
++ if ((ret = BCMINIT(_nvram_read)(header)) == 0 &&
++ header->magic == NVRAM_MAGIC)
++ BCMINIT(nvram_rehash)(header);
++
++ MFREE(osh, header, NVRAM_SPACE);
++ return ret;
++}
++
++/* Free hash table. Should be locked. */
++void
++BCMINITFN(_nvram_exit)(void)
++{
++ BCMINIT(nvram_free)();
++}
+diff -Nur linux-2.6.12.5/arch/mips/bcm947xx/broadcom/nvram_linux.c linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/nvram_linux.c
+--- linux-2.6.12.5/arch/mips/bcm947xx/broadcom/nvram_linux.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.12.5-brcm/arch/mips/bcm947xx/broadcom/nvram_linux.c 2005-11-19 02:28:26.438059500 +0100
+@@ -0,0 +1,633 @@
++/*
++ * NVRAM variable manipulation (Linux kernel half)
++ *
++ * Copyright 2005, Broadcom Corporation
++ * All Rights Reserved.
++ *
++ * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
++ * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
++ * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
++ * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
++ *
++ * $Id$
++ */
++
++#include <linux/config.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/string.h>
++#include <linux/interrupt.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/bootmem.h>
++#include <linux/wrapper.h>
++#include <linux/fs.h>
++#include <linux/miscdevice.h>
++#include <linux/mtd/mtd.h>
++#include <asm/addrspace.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++
++#include <typedefs.h>
++#include <bcmendian.h>
++#include <bcmnvram.h>
++#include <bcmutils.h>
++#include <sbconfig.h>
++#include <sbchipc.h>
++#include <sbutils.h>
++#include <sbmips.h>
++#include <sflash.h>
++
++/* In BSS to minimize text size and page aligned so it can be mmap()-ed */
++static char nvram_buf[NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE)));
++
++#ifdef MODULE
++
++#define early_nvram_get(name) nvram_get(name)
++
++#else /* !MODULE */
++
++/* Global SB handle */
++extern void *bcm947xx_sbh;
++extern spinlock_t bcm947xx_sbh_lock;
++
++/* Convenience */
++#define sbh bcm947xx_sbh
++#define sbh_lock bcm947xx_sbh_lock
++#define KB * 1024
++#define MB * 1024 * 1024
++
++/* Probe for NVRAM header */
++static void __init
++early_nvram_init(void)
++{
++ struct nvram_header *header;
++ chipcregs_t *cc;
++ struct sflash *info = NULL;
++ int i;
++ uint32 base, off, lim;
++ u32 *src, *dst;
++
++ if ((cc = sb_setcore(sbh, SB_CC, 0)) != NULL) {
++ base = KSEG1ADDR(SB_FLASH2);
++ switch (readl(&cc->capabilities) & CAP_FLASH_MASK) {
++ case PFLASH:
++ lim = SB_FLASH2_SZ;
++ break;
++
++ case SFLASH_ST:
++ case SFLASH_AT:
++ if ((info = sflash_init(cc)) == NULL)
++ return;
++ lim = info->size;
++ break;
++
++ case FLASH_NONE:
++ default:
++ return;
++ }
++ } else {
++ /* extif assumed, Stop at 4 MB */
++ base = KSEG1ADDR(SB_FLASH1);
++ lim = SB_FLASH1_SZ;
++ }
++
++ off = FLASH_MIN;
++ while (off <= lim) {
++ /* Windowed flash access */
++ header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE);
++ if (header->magic == NVRAM_MAGIC)
++ goto found;
++ off <<= 1;
++ }
++
++ /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
++ header = (struct nvram_header *) KSEG1ADDR(base + 4 KB);
++ if (header->magic == NVRAM_MAGIC)
++ goto found;
++
++ header = (struct nvram_header *) KSEG1ADDR(base + 1 KB);
++ if (header->magic == NVRAM_MAGIC)
++ goto found;
++
++ printk("early_nvram_init: NVRAM not found\n");
++ return;
++
++found:
++ src = (u32 *) header;
++ dst = (u32 *) nvram_buf;
++ for (i = 0; i < sizeof(struct nvram_header); i += 4)
++ *dst++ = *src++;
++ for (; i < header->len && i < NVRAM_SPACE; i += 4)
++ *dst++ = ltoh32(*src++);
++}
++
++/* Early (before mm or mtd) read-only access to NVRAM */
++static char * __init
++early_nvram_get(const char *name)
++{
++ char *var, *value, *end, *eq;
++
++ if (!name)
++ return NULL;
++
++ /* Too early? */
++ if (sbh == NULL)
++ return NULL;
++
++ if (!nvram_buf[0])
++ early_nvram_init();
++
++ /* Look for name=value and return value */
++ var = &nvram_buf[sizeof(struct nvram_header)];
++ end = nvram_buf + sizeof(nvram_buf) - 2;
++ end[0] = end[1] = '\0';
++ for (; *var; var = value + strlen(value) + 1) {
++ if (!(eq = strchr(var, '=')))
++ break;
++ value = eq + 1;
++ if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0)
++ return value;
++ }
++
++ return NULL;
++}
++
++#endif /* !MODULE */
++
++extern char * _nvram_get(const char *name);
++extern int _nvram_set(const char *name, const char *value);
++extern int _nvram_unset(const char *name);
++extern int _nvram_getall(char *buf, int count);
++extern int _nvram_commit(struct nvram_header *header);
++extern int _nvram_init(void);
++extern void _nvram_exit(void);
++
++/* Globals */
++static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED;
++static struct semaphore nvram_sem;
++static unsigned long nvram_offset = 0;
++static int nvram_major = -1;
++static devfs_handle_t nvram_handle = NULL;
++static struct mtd_info *nvram_mtd = NULL;
++
++int
++_nvram_read(char *buf)
++{
++ struct nvram_header *header = (struct nvram_header *) buf;
++ size_t len;
++
++ if (!nvram_mtd ||
++ MTD_READ(nvram_mtd, nvram_mtd->size - NVRAM_SPACE, NVRAM_SPACE, &len, buf) ||
++ len != NVRAM_SPACE ||
++ header->magic != NVRAM_MAGIC) {
++ /* Maybe we can recover some data from early initialization */
++ memcpy(buf, nvram_buf, NVRAM_SPACE);
++ }
++
++ return 0;
++}
++
++struct nvram_tuple *
++_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value)
++{
++ if ((nvram_offset + strlen(value) + 1) > NVRAM_SPACE)
++ return NULL;
++
++ if (!t) {
++ if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC)))
++ return NULL;
++
++ /* Copy name */
++ t->name = (char *) &t[1];
++ strcpy(t->name, name);
++
++ t->value = NULL;
++ }
++
++ /* Copy value */
++ if (!t->value || strcmp(t->value, value)) {
++ t->value = &nvram_buf[nvram_offset];
++ strcpy(t->value, value);
++ nvram_offset += strlen(value) + 1;
++ }
++
++ return t;
++}
++
++void
++_nvram_free(struct nvram_tuple *t)
++{
++ if (!t)
++ nvram_offset = 0;
++ else
++ kfree(t);
++}
++
++int
++nvram_set(const char *name, const char *value)
++{
++ unsigned long flags;
++ int ret;
++ struct nvram_header *header;
++
++ spin_lock_irqsave(&nvram_lock, flags);
++ if ((ret = _nvram_set(name, value))) {
++ /* Consolidate space and try again */
++ if ((header = kmalloc(NVRAM_SPACE, GFP_ATOMIC))) {
++ if (_nvram_commit(header) == 0)
++ ret = _nvram_set(name, value);
++ kfree(header);
++ }
++ }
++ spin_unlock_irqrestore(&nvram_lock, flags);
++
++ return ret;
++}
++
++char *
++real_nvram_get(const char *name)
++{
++ unsigned long flags;
++ char *value;
++
++ spin_lock_irqsave(&nvram_lock, flags);
++ value = _nvram_get(name);
++ spin_unlock_irqrestore(&nvram_lock, flags);
++
++ return value;
++}
++
++char *
++nvram_get(const char *name)
++{
++ if (nvram_major >= 0)
++ return real_nvram_get(name);
++ else
++ return early_nvram_get(name);
++}
++
++int
++nvram_unset(const char *name)
++{
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&nvram_lock, flags);
++ ret = _nvram_unset(name);
++ spin_unlock_irqrestore(&nvram_lock, flags);
++
++ return ret;
++}
++
++static void
++erase_callback(struct erase_info *done)
++{
++ wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
++ wake_up(wait_q);
++}
++
++int
++nvram_commit(void)
++{
++ char *buf;
++ size_t erasesize, len;
++ unsigned int i;
++ int ret;
++ struct nvram_header *header;
++ unsigned long flags;
++ u_int32_t offset;
++ DECLARE_WAITQUEUE(wait, current);
++ wait_queue_head_t wait_q;
++ struct erase_info erase;
++
++ if (!nvram_mtd) {
++ printk("nvram_commit: NVRAM not found\n");
++ return -ENODEV;
++ }
++
++ if (in_interrupt()) {
++ printk("nvram_commit: not committing in interrupt\n");
++ return -EINVAL;
++ }
++
++ /* Backup sector blocks to be erased */
++ erasesize = ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize);
++ if (!(buf = kmalloc(erasesize, GFP_KERNEL))) {
++ printk("nvram_commit: out of memory\n");
++ return -ENOMEM;
++ }
++
++ down(&nvram_sem);
++
++ if ((i = erasesize - NVRAM_SPACE) > 0) {
++ offset = nvram_mtd->size - erasesize;
++ len = 0;
++ ret = MTD_READ(nvram_mtd, offset, i, &len, buf);
++ if (ret || len != i) {
++ printk("nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i);
++ ret = -EIO;
++ goto done;
++ }
++ header = (struct nvram_header *)(buf + i);
++ } else {
++ offset = nvram_mtd->size - NVRAM_SPACE;
++ header = (struct nvram_header *)buf;
++ }
++
++ /* Regenerate NVRAM */
++ spin_lock_irqsave(&nvram_lock, flags);
++ ret = _nvram_commit(header);
++ spin_unlock_irqrestore(&nvram_lock, flags);
++ if (ret)
++ goto done;
++
++ /* Erase sector blocks */
++ init_waitqueue_head(&wait_q);
++ for (; offset < nvram_mtd->size - NVRAM_SPACE + header->len; offset += nvram_mtd->erasesize) {
++ erase.mtd = nvram_mtd;
++ erase.addr = offset;
++ erase.len = nvram_mtd->erasesize;
++ erase.callback = erase_callback;
++ erase.priv = (u_long) &wait_q;
++
++ set_current_state(TASK_INTERRUPTIBLE);
++ add_wait_queue(&wait_q, &wait);
++
++ /* Unlock sector blocks */
++ if (nvram_mtd->unlock)
++ nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize);
++
++ if ((ret = MTD_ERASE(nvram_mtd, &erase))) {
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&wait_q, &wait);
++ printk("nvram_commit: erase error\n");
++ goto done;
++ }
++
++ /* Wait for erase to finish */
++ schedule();
++ remove_wait_queue(&wait_q, &wait);
++ }
++
++ /* Write partition up to end of data area */
++ offset = nvram_mtd->size - erasesize;
++ i = erasesize - NVRAM_SPACE + header->len;
++ ret = MTD_WRITE(nvram_mtd, offset, i, &len, buf);
++ if (ret || len != i) {
++ printk("nvram_commit: write error\n");
++ ret = -EIO;
++ goto done;
++ }
++
++ offset = nvram_mtd->size - erasesize;
++ ret = MTD_READ(nvram_mtd, offset, 4, &len, buf);
++
++ done:
++ up(&nvram_sem);
++ kfree(buf);
++ return ret;
++}
++
++int
++nvram_getall(char *buf, int count)
++{
++ unsigned long flags;
++ int ret;
++
++ spin_lock_irqsave(&nvram_lock, flags);
++ ret = _nvram_getall(buf, count);
++ spin_unlock_irqrestore(&nvram_lock, flags);
++
++ return ret;
++}
++
++EXPORT_SYMBOL(nvram_get);
++EXPORT_SYMBOL(nvram_getall);
++EXPORT_SYMBOL(nvram_set);
++EXPORT_SYMBOL(nvram_unset);
++EXPORT_SYMBOL(nvram_commit);
++
++/* User mode interface below */
++
++static ssize_t
++dev_nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos)
++{
++ char tmp[100], *name = tmp, *value;
++ ssize_t ret;
++ unsigned long off;
++
++ if (count > sizeof(tmp)) {
++ if (!(name = kmalloc(count, GFP_KERNEL)))
++ return -ENOMEM;
++ }
++
++ if (copy_from_user(name, buf, count)) {
++ ret = -EFAULT;
++ goto done;
++ }
++
++ if (*name == '\0') {
++ /* Get all variables */
++ ret = nvram_getall(name, count);
++ if (ret == 0) {
++ if (copy_to_user(buf, name, count)) {
++ ret = -EFAULT;
++ goto done;
++ }
++ ret = count;
++ }
++ } else {
++ if (!(value = nvram_get(name))) {
++ ret = 0;
++ goto done;
++ }
++
++ /* Provide the offset into mmap() space */
++ off = (unsigned long) value - (unsigned long) nvram_buf;
++
++ if (put_user(off, (unsigned long *) buf)) {
++ ret = -EFAULT;
++ goto done;
++ }
++
++ ret = sizeof(unsigned long);
++ }
++
++ flush_cache_all();
++
++done:
++ if (name != tmp)
++ kfree(name);