• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/arch/arm/plat-brcm/
1/*
2 * NVRAM variable manipulation (Linux kernel half)
3 *
4 * Copyright (C) 2012, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: nvram_linux.c,v 1.10 2010-09-17 04:51:19 $
19 */
20
21#include <linux/version.h>
22
23#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
24#include <linux/config.h>
25#endif
26
27#include <linux/init.h>
28#include <linux/module.h>
29#include <linux/kernel.h>
30#include <linux/string.h>
31#include <linux/interrupt.h>
32#include <linux/spinlock.h>
33#include <linux/slab.h>
34#include <linux/bootmem.h>
35#include <linux/fs.h>
36#include <linux/miscdevice.h>
37#include <linux/mtd/mtd.h>
38
39#include <typedefs.h>
40#include <bcmendian.h>
41#include <bcmnvram.h>
42#include <bcmutils.h>
43#include <bcmdefs.h>
44#include <hndsoc.h>
45#include <siutils.h>
46#include <hndmips.h>
47#include <hndsflash.h>
48#ifdef CONFIG_MTD_NFLASH
49#include <nflash.h>
50#endif
51
52int nvram_space = DEF_NVRAM_SPACE;
53
54/* Temp buffer to hold the nvram transfered romboot CFE */
55char __initdata ram_nvram_buf[MAX_NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE)));
56
57/* In BSS to minimize text size and page aligned so it can be mmap()-ed */
58static char nvram_buf[MAX_NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE)));
59static bool nvram_inram = FALSE;
60
61static char *early_nvram_get(const char *name);
62#ifdef MODULE
63
64#define early_nvram_get(name) nvram_get(name)
65
66#else /* !MODULE */
67
68/* Global SB handle */
69extern si_t *bcm947xx_sih;
70extern spinlock_t bcm947xx_sih_lock;
71
72/* Convenience */
73#define sih bcm947xx_sih
74#define sih_lock bcm947xx_sih_lock
75#define KB * 1024
76#define MB * 1024 * 1024
77
78#ifndef	MAX_MTD_DEVICES
79#define	MAX_MTD_DEVICES	32
80#endif
81
82static struct resource norflash_region = {
83	.name = "norflash",
84	.start = 0x1E000000,
85	.end =  0x1FFFFFFF,
86        .flags = IORESOURCE_MEM,
87};
88
89#ifdef CONFIG_MTD_NFLASH
90static unsigned char nflash_nvh[MAX_NVRAM_SPACE];
91
92static struct nvram_header *
93BCMINITFN(nand_find_nvram)(hndnand_t *nfl, uint32 off)
94{
95	int blocksize = nfl->blocksize;
96	unsigned char *buf = nflash_nvh;
97	int rlen = sizeof(nflash_nvh);
98	int len;
99
100	for (; off < nfl_boot_size(nfl); off += blocksize) {
101		if (hndnand_checkbadb(nfl, off) != 0)
102			continue;
103
104		len = blocksize;
105		if (len >= rlen)
106			len = rlen;
107
108		if (hndnand_read(nfl, off, len, buf) == 0)
109			break;
110
111		buf += len;
112		rlen -= len;
113		if (rlen == 0)
114			return (struct nvram_header *)nflash_nvh;
115	}
116
117	return NULL;
118}
119#endif /* CONFIG_MTD_NFLASH */
120
121/* Probe for NVRAM header */
122static int
123early_nvram_init(void)
124{
125	struct nvram_header *header;
126	int i;
127	u32 *src, *dst;
128#ifdef CONFIG_MTD_NFLASH
129	hndnand_t *nfl_info = NULL;
130	uint32 blocksize;
131#endif
132	char *nvram_space_str;
133	int bootdev;
134	uint32 flash_base;
135	uint32 lim = SI_FLASH_WINDOW;
136	uint32 off;
137	hndsflash_t *sfl_info;
138
139	header = (struct nvram_header *)ram_nvram_buf;
140	if (header->magic == NVRAM_MAGIC) {
141		if (nvram_calc_crc(header) == (uint8)header->crc_ver_init) {
142			nvram_inram = TRUE;
143			goto found;
144		}
145	}
146
147	bootdev = soc_boot_dev((void *)sih);
148#ifdef CONFIG_MTD_NFLASH
149	if (bootdev == SOC_BOOTDEV_NANDFLASH) {
150		if ((nfl_info = hndnand_init(sih)) == NULL)
151			return -1;
152		flash_base = nfl_info->base;
153		blocksize = nfl_info->blocksize;
154		off = blocksize;
155		for (; off < nfl_boot_size(nfl_info); off += blocksize) {
156			if (hndnand_checkbadb(nfl_info, off) != 0)
157				continue;
158			header = (struct nvram_header *)(flash_base + off);
159			if (header->magic != NVRAM_MAGIC)
160				continue;
161
162			/* Read into the nand_nvram */
163			if ((header = nand_find_nvram(nfl_info, off)) == NULL)
164				continue;
165			if (nvram_calc_crc(header) == (uint8)header->crc_ver_init)
166				goto found;
167		}
168	}
169	else
170#endif
171	if (bootdev == SOC_BOOTDEV_SFLASH ||
172	    bootdev == SOC_BOOTDEV_ROM) {
173		/* Boot from SFLASH or ROM */
174		if ((sfl_info = hndsflash_init(sih)) == NULL)
175			return -1;
176
177		lim = sfl_info->size;
178
179		BUG_ON(request_resource(&iomem_resource, &norflash_region));
180
181		flash_base = sfl_info->base;
182
183		BUG_ON(IS_ERR_OR_NULL((void *)flash_base));
184
185		off = FLASH_MIN;
186		while (off <= lim) {
187			/* Windowed flash access */
188			header = (struct nvram_header *)(flash_base + off - nvram_space);
189			if (header->magic == NVRAM_MAGIC)
190				if (nvram_calc_crc(header) == (uint8)header->crc_ver_init) {
191					goto found;
192				}
193			off += DEF_NVRAM_SPACE;
194		}
195	}
196	else {
197		/* This is the case bootdev == SOC_BOOTDEV_PFLASH, not applied on NorthStar */
198		ASSERT(0);
199	}
200
201	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
202	header = (struct nvram_header *)(flash_base + 4 KB);
203	if (header->magic == NVRAM_MAGIC)
204		if (nvram_calc_crc(header) == (uint8)header->crc_ver_init) {
205			goto found;
206		}
207
208	header = (struct nvram_header *)(flash_base + 1 KB);
209	if (header->magic == NVRAM_MAGIC)
210		if (nvram_calc_crc(header) == (uint8)header->crc_ver_init) {
211			goto found;
212		}
213
214	return -1;
215
216found:
217	src = (u32 *)header;
218	dst = (u32 *)nvram_buf;
219	for (i = 0; i < sizeof(struct nvram_header); i += 4)
220		*dst++ = *src++;
221	for (; i < header->len && i < MAX_NVRAM_SPACE; i += 4)
222		*dst++ = ltoh32(*src++);
223
224	nvram_space_str = early_nvram_get("nvram_space");
225	if (nvram_space_str)
226		nvram_space = bcm_strtoul(nvram_space_str, NULL, 0);
227
228	return 0;
229}
230
231/* Early (before mm or mtd) read-only access to NVRAM */
232static char *
233early_nvram_get(const char *name)
234{
235	char *var, *value, *end, *eq;
236
237	if (!name)
238		return NULL;
239
240	/* Too early? */
241	if (sih == NULL)
242		return NULL;
243
244	if (!nvram_buf[0])
245		if (early_nvram_init() != 0) {
246			printk("early_nvram_get: Failed reading nvram var %s\n", name);
247			return NULL;
248		}
249
250	/* Look for name=value and return value */
251	var = &nvram_buf[sizeof(struct nvram_header)];
252	end = nvram_buf + sizeof(nvram_buf) - 2;
253	end[0] = end[1] = '\0';
254	for (; *var; var = value + strlen(value) + 1) {
255		if (!(eq = strchr(var, '=')))
256			break;
257		value = eq + 1;
258		if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0)
259			return value;
260	}
261
262	return NULL;
263}
264
265static int
266early_nvram_getall(char *buf, int count)
267{
268	char *var, *end;
269	int len = 0;
270
271	/* Too early? */
272	if (sih == NULL)
273		return -1;
274
275	if (!nvram_buf[0])
276		if (early_nvram_init() != 0) {
277			printk("early_nvram_getall: Failed reading nvram var\n");
278			return -1;
279		}
280
281	bzero(buf, count);
282
283	/* Write name=value\0 ... \0\0 */
284	var = &nvram_buf[sizeof(struct nvram_header)];
285	end = nvram_buf + sizeof(nvram_buf) - 2;
286	end[0] = end[1] = '\0';
287	for (; *var; var += strlen(var) + 1) {
288		if ((count - len) <= (strlen(var) + 1))
289			break;
290		len += sprintf(buf + len, "%s", var) + 1;
291	}
292
293	return 0;
294}
295#endif /* !MODULE */
296
297extern char * _nvram_get(const char *name);
298extern int _nvram_set(const char *name, const char *value);
299extern int _nvram_unset(const char *name);
300extern int _nvram_getall(char *buf, int count);
301extern int _nvram_commit(struct nvram_header *header);
302extern int _nvram_init(si_t *sih);
303extern void _nvram_exit(void);
304
305/* Globals */
306static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED;
307static struct semaphore nvram_sem;
308static unsigned long nvram_offset = 0;
309static int nvram_major = -1;
310static struct class *nvram_class = NULL;
311static struct mtd_info *nvram_mtd = NULL;
312
313int
314_nvram_read(char *buf)
315{
316	struct nvram_header *header = (struct nvram_header *) buf;
317	size_t len;
318	int offset = 0;
319
320	if (nvram_mtd) {
321#ifdef CONFIG_MTD_NFLASH
322		if (nvram_mtd->type == MTD_NANDFLASH)
323			offset = 0;
324		else
325#endif
326			offset = nvram_mtd->size - nvram_space;
327	}
328	if (nvram_inram || !nvram_mtd ||
329	    nvram_mtd->read(nvram_mtd, offset, nvram_space, &len, buf) ||
330	    len != nvram_space ||
331	    header->magic != NVRAM_MAGIC) {
332		/* Maybe we can recover some data from early initialization */
333		if (nvram_inram)
334			printk("Sourcing NVRAM from ram\n");
335		memcpy(buf, nvram_buf, nvram_space);
336	}
337
338	return 0;
339}
340
341struct nvram_tuple *
342_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value)
343{
344	if ((nvram_offset + strlen(value) + 1) > nvram_space)
345		return NULL;
346
347	if (!t) {
348		if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC)))
349			return NULL;
350
351		/* Copy name */
352		t->name = (char *) &t[1];
353		strcpy(t->name, name);
354
355		t->value = NULL;
356	}
357
358	/* Copy value */
359	if (t->value == NULL || strlen(t->value) < strlen(value)) {
360		/* Alloc value space */
361		t->value = &nvram_buf[nvram_offset];
362		strcpy(t->value, value);
363		nvram_offset += strlen(value) + 1;
364	} else if( 0 != strcmp(t->value, value)) {
365		/* In place */
366		strcpy(t->value, value);
367	}
368
369	return t;
370}
371
372void
373_nvram_free(struct nvram_tuple *t)
374{
375	if (!t) {
376		nvram_offset = 0;
377		memset( nvram_buf, 0, sizeof(nvram_buf) );
378	} else {
379		kfree(t);
380	}
381}
382
383int
384nvram_init(void *sih)
385{
386	return 0;
387}
388
389int
390nvram_set(const char *name, const char *value)
391{
392	unsigned long flags;
393	int ret;
394	struct nvram_header *header;
395
396	spin_lock_irqsave(&nvram_lock, flags);
397	if ((ret = _nvram_set(name, value))) {
398		printk( KERN_INFO "nvram: consolidating space!\n");
399		/* Consolidate space and try again */
400		if ((header = kmalloc(nvram_space, GFP_ATOMIC))) {
401			if (_nvram_commit(header) == 0)
402				ret = _nvram_set(name, value);
403			kfree(header);
404		}
405	}
406	spin_unlock_irqrestore(&nvram_lock, flags);
407
408	return ret;
409}
410
411char *
412real_nvram_get(const char *name)
413{
414	unsigned long flags;
415	char *value;
416
417	spin_lock_irqsave(&nvram_lock, flags);
418	value = _nvram_get(name);
419	spin_unlock_irqrestore(&nvram_lock, flags);
420
421	return value;
422}
423
424char *
425nvram_get(const char *name)
426{
427	if (nvram_major >= 0)
428		return real_nvram_get(name);
429	else
430		return early_nvram_get(name);
431}
432
433int
434nvram_unset(const char *name)
435{
436	unsigned long flags;
437	int ret;
438
439	spin_lock_irqsave(&nvram_lock, flags);
440	ret = _nvram_unset(name);
441	spin_unlock_irqrestore(&nvram_lock, flags);
442
443	return ret;
444}
445
446static void
447erase_callback(struct erase_info *done)
448{
449	wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
450	wake_up(wait_q);
451}
452
453#ifdef CONFIG_MTD_NFLASH
454int
455nvram_nflash_commit(void)
456{
457	char *buf;
458	size_t len;
459	unsigned int i;
460	int ret;
461	struct nvram_header *header;
462	unsigned long flags;
463	u_int32_t offset;
464
465	if (!(buf = kmalloc(nvram_space, GFP_KERNEL))) {
466		printk(KERN_WARNING "nvram_commit: out of memory\n");
467		return -ENOMEM;
468	}
469
470	down(&nvram_sem);
471
472	offset = 0;
473	header = (struct nvram_header *)buf;
474	header->magic = NVRAM_MAGIC;
475	/* reset MAGIC before we regenerate the NVRAM,
476	 * otherwise we'll have an incorrect CRC
477	 */
478	/* Regenerate NVRAM */
479	spin_lock_irqsave(&nvram_lock, flags);
480	ret = _nvram_commit(header);
481	spin_unlock_irqrestore(&nvram_lock, flags);
482	if (ret)
483		goto done;
484
485	/* Write partition up to end of data area */
486	i = header->len;
487	ret = nvram_mtd->write(nvram_mtd, offset, i, &len, buf);
488	if (ret || len != i) {
489		printk(KERN_WARNING "nvram_commit: write error\n");
490		ret = -EIO;
491		goto done;
492	}
493
494done:
495	up(&nvram_sem);
496	kfree(buf);
497	return ret;
498}
499#endif
500
501int
502nvram_commit(void)
503{
504	char *buf;
505	size_t erasesize, len, magic_len;
506	unsigned int i;
507	int ret;
508	struct nvram_header *header;
509	unsigned long flags;
510	u_int32_t offset;
511	DECLARE_WAITQUEUE(wait, current);
512	wait_queue_head_t wait_q;
513	struct erase_info erase;
514	u_int32_t magic_offset = 0; /* Offset for writing MAGIC # */
515
516	if (!nvram_mtd) {
517		printk(KERN_ERR "nvram_commit: NVRAM not found\n");
518		return -ENODEV;
519	}
520
521	if (in_interrupt()) {
522		printk(KERN_WARNING "nvram_commit: not committing in interrupt\n");
523		return -EINVAL;
524	}
525
526#ifdef CONFIG_MTD_NFLASH
527	if (nvram_mtd->type == MTD_NANDFLASH)
528		return nvram_nflash_commit();
529#endif
530	/* Backup sector blocks to be erased */
531	erasesize = ROUNDUP(nvram_space, nvram_mtd->erasesize);
532	if (!(buf = kmalloc(erasesize, GFP_KERNEL))) {
533		printk(KERN_WARNING "nvram_commit: out of memory\n");
534		return -ENOMEM;
535	}
536
537	down(&nvram_sem);
538
539	if ((i = erasesize - nvram_space) > 0) {
540		offset = nvram_mtd->size - erasesize;
541		len = 0;
542		ret = nvram_mtd->read(nvram_mtd, offset, i, &len, buf);
543		if (ret || len != i) {
544			printk(KERN_ERR "nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i);
545			ret = -EIO;
546			goto done;
547		}
548		header = (struct nvram_header *)(buf + i);
549		magic_offset = i + ((void *)&header->magic - (void *)header);
550	} else {
551		offset = nvram_mtd->size - nvram_space;
552		magic_offset = ((void *)&header->magic - (void *)header);
553		header = (struct nvram_header *)buf;
554	}
555
556	/* clear the existing magic # to mark the NVRAM as unusable
557	 * we can pull MAGIC bits low without erase
558	 */
559	header->magic = NVRAM_CLEAR_MAGIC; /* All zeros magic */
560	/* Unlock sector blocks */
561	if (nvram_mtd->unlock)
562		nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize);
563	ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic),
564		&magic_len, (char *)&header->magic);
565	if (ret || magic_len != sizeof(header->magic)) {
566		printk(KERN_ERR "nvram_commit: clear MAGIC error\n");
567		ret = -EIO;
568		goto done;
569	}
570
571	header->magic = NVRAM_MAGIC;
572	/* reset MAGIC before we regenerate the NVRAM,
573	 * otherwise we'll have an incorrect CRC
574	 */
575	/* Regenerate NVRAM */
576	spin_lock_irqsave(&nvram_lock, flags);
577	ret = _nvram_commit(header);
578	spin_unlock_irqrestore(&nvram_lock, flags);
579	if (ret)
580		goto done;
581
582	/* Erase sector blocks */
583	init_waitqueue_head(&wait_q);
584	for (; offset < nvram_mtd->size - nvram_space + header->len;
585		offset += nvram_mtd->erasesize) {
586
587		erase.mtd = nvram_mtd;
588		erase.addr = offset;
589		erase.len = nvram_mtd->erasesize;
590		erase.callback = erase_callback;
591		erase.priv = (u_long) &wait_q;
592
593		set_current_state(TASK_INTERRUPTIBLE);
594		add_wait_queue(&wait_q, &wait);
595
596		/* Unlock sector blocks */
597		if (nvram_mtd->unlock)
598			nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize);
599
600		if ((ret = nvram_mtd->erase(nvram_mtd, &erase))) {
601			set_current_state(TASK_RUNNING);
602			remove_wait_queue(&wait_q, &wait);
603			printk(KERN_ERR "nvram_commit: erase error\n");
604			goto done;
605		}
606
607		/* Wait for erase to finish */
608		schedule();
609		remove_wait_queue(&wait_q, &wait);
610	}
611
612	/* Write partition up to end of data area */
613	header->magic = NVRAM_INVALID_MAGIC; /* All ones magic */
614	offset = nvram_mtd->size - erasesize;
615	i = erasesize - nvram_space + header->len;
616	ret = nvram_mtd->write(nvram_mtd, offset, i, &len, buf);
617	if (ret || len != i) {
618		printk(KERN_ERR "nvram_commit: write error\n");
619		ret = -EIO;
620		goto done;
621	}
622
623	/* Now mark the NVRAM in flash as "valid" by setting the correct
624	 * MAGIC #
625	 */
626	header->magic = NVRAM_MAGIC;
627	ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic),
628		&magic_len, (char *)&header->magic);
629	if (ret || magic_len != sizeof(header->magic)) {
630		printk(KERN_ERR "nvram_commit: write MAGIC error\n");
631		ret = -EIO;
632		goto done;
633	}
634
635	offset = nvram_mtd->size - erasesize;
636	ret = nvram_mtd->read(nvram_mtd, offset, 4, &len, buf);
637
638done:
639	up(&nvram_sem);
640	kfree(buf);
641	return ret;
642}
643
644int
645nvram_getall(char *buf, int count)
646{
647	unsigned long flags;
648	int ret;
649
650	spin_lock_irqsave(&nvram_lock, flags);
651	if (nvram_major >= 0)
652		ret = _nvram_getall(buf, count);
653	else
654		ret = early_nvram_getall(buf, count);
655	spin_unlock_irqrestore(&nvram_lock, flags);
656
657	return ret;
658}
659
660EXPORT_SYMBOL(nvram_init);
661EXPORT_SYMBOL(nvram_get);
662EXPORT_SYMBOL(nvram_getall);
663EXPORT_SYMBOL(nvram_set);
664EXPORT_SYMBOL(nvram_unset);
665EXPORT_SYMBOL(nvram_commit);
666
667/* User mode interface below */
668
669static ssize_t
670dev_nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos)
671{
672	char tmp[100], *name = tmp, *value;
673	ssize_t ret;
674	unsigned long off;
675
676	if ((count+1) > sizeof(tmp)) {
677		if (!(name = kmalloc(count+1, GFP_KERNEL)))
678			return -ENOMEM;
679	}
680
681	if (copy_from_user(name, buf, count)) {
682		ret = -EFAULT;
683		goto done;
684	}
685	name[count] = '\0';
686
687	if (*name == '\0') {
688		/* Get all variables */
689		ret = nvram_getall(name, count);
690		if (ret == 0) {
691			if (copy_to_user(buf, name, count)) {
692				ret = -EFAULT;
693				goto done;
694			}
695			ret = count;
696		}
697	} else {
698		if (!(value = nvram_get(name))) {
699			ret = 0;
700			goto done;
701		}
702
703		/* Provide the offset into mmap() space */
704		off = (unsigned long) value - (unsigned long) nvram_buf;
705
706		if (copy_to_user(buf, &off, ret = sizeof(off))) {
707			ret = -EFAULT;
708			goto done;
709		}
710	}
711#ifdef	_DEPRECATED
712	flush_cache_all();
713#endif
714done:
715	if (name != tmp)
716		kfree(name);
717
718	return ret;
719}
720
721static ssize_t
722dev_nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
723{
724	char tmp[512], *name = tmp, *value;
725	ssize_t ret;
726
727	if (count+1 > sizeof(tmp)) {
728		if (!(name = kmalloc(count+1, GFP_KERNEL)))
729			return -ENOMEM;
730	}
731
732	if (copy_from_user(name, buf, count)) {
733		ret = -EFAULT;
734		goto done;
735	}
736	name[ count ] = '\0';
737	value = name;
738	name = strsep(&value, "=");
739	if (value)
740		ret = nvram_set(name, value) ;
741	else
742		ret = nvram_unset(name) ;
743
744	if( 0 == ret )
745		ret = count;
746done:
747	if (name != tmp)
748		kfree(name);
749
750	return ret;
751}
752
753#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
754static int
755#else
756static long
757#endif
758dev_nvram_ioctl(
759#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
760	struct inode *inode,
761#endif
762	struct file *file,
763	unsigned int cmd,
764	unsigned long arg)
765{
766	if (cmd != NVRAM_MAGIC)
767		return -EINVAL;
768	return nvram_commit();
769}
770
771static int
772dev_nvram_mmap(struct file *file, struct vm_area_struct *vma)
773{
774	unsigned long offset = __pa(nvram_buf) >> PAGE_SHIFT;
775
776	if (remap_pfn_range(vma, vma->vm_start, offset,
777	                    vma->vm_end - vma->vm_start,
778	                    vma->vm_page_prot))
779		return -EAGAIN;
780
781	return 0;
782}
783
784static int
785dev_nvram_open(struct inode *inode, struct file * file)
786{
787	return 0;
788}
789
790static int
791dev_nvram_release(struct inode *inode, struct file * file)
792{
793	return 0;
794}
795
796static struct file_operations dev_nvram_fops = {
797	owner:		THIS_MODULE,
798	open:		dev_nvram_open,
799	release:	dev_nvram_release,
800	read:		dev_nvram_read,
801	write:		dev_nvram_write,
802#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
803	ioctl:		dev_nvram_ioctl,
804#else
805	unlocked_ioctl:	dev_nvram_ioctl,
806#endif
807	mmap:		dev_nvram_mmap
808};
809
810static void
811dev_nvram_exit(void)
812{
813	int order = 0;
814	struct page *page, *end;
815
816	if (nvram_class) {
817#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
818		class_device_destroy(nvram_class, MKDEV(nvram_major, 0));
819#else /* 2.6.36 and up */
820		device_destroy(nvram_class, MKDEV(nvram_major, 0));
821#endif
822		class_destroy(nvram_class);
823	}
824
825	if (nvram_major >= 0)
826		unregister_chrdev(nvram_major, "nvram");
827
828	if (nvram_mtd)
829		put_mtd_device(nvram_mtd);
830
831	while ((PAGE_SIZE << order) < MAX_NVRAM_SPACE)
832		order++;
833	end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1);
834	for (page = virt_to_page(nvram_buf); page <= end; page++)
835		ClearPageReserved(page);
836
837	_nvram_exit();
838}
839
840static int
841dev_nvram_init(void)
842{
843	int order = 0, ret = 0;
844	struct page *page, *end;
845	osl_t *osh;
846#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
847	unsigned int i;
848#endif
849
850	/* Allocate and reserve memory to mmap() */
851	while ((PAGE_SIZE << order) < nvram_space)
852		order++;
853	end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1);
854	for (page = virt_to_page(nvram_buf); page <= end; page++) {
855		SetPageReserved(page);
856	}
857
858#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
859	/* Find associated MTD device */
860	for (i = 0; i < MAX_MTD_DEVICES; i++) {
861		nvram_mtd = get_mtd_device(NULL, i);
862		if (!IS_ERR(nvram_mtd)) {
863			if (!strcmp(nvram_mtd->name, "nvram") &&
864			    nvram_mtd->size >= nvram_space) {
865				break;
866			}
867			put_mtd_device(nvram_mtd);
868		}
869	}
870	if (i >= MAX_MTD_DEVICES)
871		nvram_mtd = NULL;
872#endif
873
874	/* Initialize hash table lock */
875	spin_lock_init(&nvram_lock);
876
877	/* Initialize commit semaphore */
878	init_MUTEX(&nvram_sem);
879
880	/* Register char device */
881	if ((nvram_major = register_chrdev(0, "nvram", &dev_nvram_fops)) < 0) {
882		ret = nvram_major;
883		goto err;
884	}
885
886	if (si_osh(sih) == NULL) {
887		osh = osl_attach(NULL, SI_BUS, FALSE);
888		if (osh == NULL) {
889			printk("Error allocating osh\n");
890			unregister_chrdev(nvram_major, "nvram");
891			goto err;
892		}
893		si_setosh(sih, osh);
894	}
895
896	/* Initialize hash table */
897	_nvram_init(sih);
898
899	/* Create /dev/nvram handle */
900	nvram_class = class_create(THIS_MODULE, "nvram");
901	if (IS_ERR(nvram_class)) {
902		printk("Error creating nvram class\n");
903		goto err;
904	}
905
906	/* Add the device nvram0 */
907#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
908	class_device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram");
909#else /* Linux 2.6.36 and above */
910	device_create(nvram_class, NULL, MKDEV(nvram_major, 0), NULL, "nvram");
911#endif	/* Linux 2.6.36 */
912
913	return 0;
914
915err:
916	dev_nvram_exit();
917	return ret;
918}
919
920/*
921* This is not a module, and is not unloadable.
922* Also, this module must not be initialized before
923* the Flash MTD partitions have been set up, in case
924* the contents are stored in Flash.
925* Thus, late_initcall() macro is used to insert this
926* device driver initialization later.
927* An alternative solution would be to initialize
928* inside the xx_open() call.
929* -LR
930*/
931late_initcall(dev_nvram_init);
932