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