source: src/linux/brcm/linux-2.6.23/arch/mips/brcm-boards/bcm947xx/nvram_linux.c @ 12699

Last change on this file since 12699 was 12699, checked in by BrainSlayer, 4 years ago

for netgear

File size: 16.6 KB
Line 
1/*
2 * NVRAM variable manipulation (Linux kernel half)
3 *
4 * Copyright (C) 2008, Broadcom Corporation
5 * All Rights Reserved.
6 *
7 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
8 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
9 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
10 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
11 *
12 * $Id: nvram_linux.c,v 1.8 2008/07/04 01:15:09 Exp $
13 */
14
15#include <linux/config.h>
16#include <linux/init.h>
17#include <linux/module.h>
18#include <linux/kernel.h>
19#include <linux/string.h>
20#include <linux/interrupt.h>
21#include <linux/spinlock.h>
22#include <linux/slab.h>
23#include <linux/bootmem.h>
24#include <linux/fs.h>
25#include <linux/miscdevice.h>
26#include <linux/mtd/mtd.h>
27#include <linux/devfs_fs_kernel.h>
28#include <asm/addrspace.h>
29#include <asm/io.h>
30#include <asm/uaccess.h>
31#include <asm/cacheflush.h>
32
33#include <typedefs.h>
34#include <bcmendian.h>
35#include <bcmnvram.h>
36#include <bcmutils.h>
37#include <bcmdefs.h>
38#include <hndsoc.h>
39#include <siutils.h>
40#include <hndmips.h>
41#include <sflash.h>
42
43/* In BSS to minimize text size and page aligned so it can be mmap()-ed */
44static char nvram_buf[NVRAM_SPACE] __attribute__((aligned(PAGE_SIZE)));
45
46#ifdef MODULE
47
48#define early_nvram_get(name) nvram_get(name)
49
50#else /* !MODULE */
51
52/* Global SB handle */
53extern void *bcm947xx_sih;
54extern spinlock_t bcm947xx_sih_lock;
55
56static int cfe_env;
57extern char *cfe_env_get(char *nv_buf, const char *name);
58
59/* Convenience */
60#define sih bcm947xx_sih
61#define sih_lock bcm947xx_sih_lock
62#define KB * 1024
63#define MB * 1024 * 1024
64
65/* Probe for NVRAM header */
66static int
67early_nvram_init(void)
68{
69        struct nvram_header *header;
70        chipcregs_t *cc;
71        struct sflash *info = NULL;
72        int i;
73        uint32 base, off, lim;
74        u32 *src, *dst;
75
76        if ((cc = si_setcore(sih, CC_CORE_ID, 0)) != NULL) {
77                base = KSEG1ADDR(SI_FLASH2);
78                switch (readl(&cc->capabilities) & CC_CAP_FLASH_MASK) {
79                case PFLASH:
80                        lim = SI_FLASH2_SZ;
81                        break;
82
83                case SFLASH_ST:
84                case SFLASH_AT:
85                        if ((info = sflash_init(sih, cc)) == NULL)
86                                return -1;
87                        lim = info->size;
88                        break;
89
90                case FLASH_NONE:
91                default:
92                        return -1;
93                }
94        } else {
95                /* extif assumed, Stop at 4 MB */
96                base = KSEG1ADDR(SI_FLASH1);
97                lim = SI_FLASH1_SZ;
98        }
99
100        src = (u32 *) KSEG1ADDR(base + 8 * 1024 * 1024 - 0x2000);
101        dst = (u32 *) nvram_buf;
102        if ((lim == 0x02000000) && ((*src & 0xff00ff) == 0x000001)) {
103                printk("early_nvram_init: WGT634U NVRAM found.\n");
104
105                for (i = 0; i < 0x1ff0; i++) {
106                        if (*src == 0xFFFFFFFF)
107                                break;
108                        *dst++ = *src++;
109                }
110                cfe_env = 1;
111                return;
112        }
113
114        off = FLASH_MIN;
115        while (off <= lim) {
116                /* Windowed flash access */
117                header = (struct nvram_header *) KSEG1ADDR(base + off - NVRAM_SPACE);
118                if (header->magic == NVRAM_MAGIC)
119                        if (nvram_calc_crc(header) == (uint8) header->crc_ver_init) {
120                                goto found;
121                        }
122                off <<= 1;
123        }
124
125        /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
126        header = (struct nvram_header *) KSEG1ADDR(base + 4 KB);
127        if (header->magic == NVRAM_MAGIC)
128                if (nvram_calc_crc(header) == (uint8) header->crc_ver_init) {
129                        goto found;
130                }
131
132        header = (struct nvram_header *) KSEG1ADDR(base + 1 KB);
133        if (header->magic == NVRAM_MAGIC)
134                if (nvram_calc_crc(header) == (uint8) header->crc_ver_init) {
135                        goto found;
136                }
137
138        return -1;
139
140found:
141        src = (u32 *) header;
142        dst = (u32 *) nvram_buf;
143        for (i = 0; i < sizeof(struct nvram_header); i += 4)
144                *dst++ = *src++;
145        for (; i < header->len && i < NVRAM_SPACE; i += 4)
146                *dst++ = ltoh32(*src++);
147
148        return 0;
149}
150
151/* Early (before mm or mtd) read-only access to NVRAM */
152static char *
153early_nvram_get(const char *name)
154{
155        char *var, *value, *end, *eq;
156
157        if (!name)
158                return NULL;
159
160        /* Too early? */
161        if (sih == NULL)
162                return NULL;
163
164        if (!nvram_buf[0])
165                if (early_nvram_init() != 0) {
166                        printk("early_nvram_get: Failed reading nvram var %s\n", name);
167                        return NULL;
168                }
169
170        if (cfe_env)
171                return cfe_env_get(nvram_buf, name);
172
173        /* Look for name=value and return value */
174        var = &nvram_buf[sizeof(struct nvram_header)];
175        end = nvram_buf + sizeof(nvram_buf) - 2;
176        end[0] = end[1] = '\0';
177        for (; *var; var = value + strlen(value) + 1) {
178                if (!(eq = strchr(var, '=')))
179                        break;
180                value = eq + 1;
181                if ((eq - var) == strlen(name) && strncmp(var, name, (eq - var)) == 0)
182                        return value;
183        }
184
185        return NULL;
186}
187
188static int
189early_nvram_getall(char *buf, int count)
190{
191        char *var, *end;
192        int len = 0;
193
194        /* Too early? */
195        if (sih == NULL)
196                return -1;
197
198        if (!nvram_buf[0])
199                if (early_nvram_init() != 0) {
200                        printk("early_nvram_getall: Failed reading nvram var\n");
201                        return -1;
202                }
203
204        bzero(buf, count);
205
206        /* Write name=value\0 ... \0\0 */
207        var = &nvram_buf[sizeof(struct nvram_header)];
208        end = nvram_buf + sizeof(nvram_buf) - 2;
209        end[0] = end[1] = '\0';
210        for (; *var; var += strlen(var) + 1) {
211                if ((count - len) <= (strlen(var) + 1))
212                        break;
213                len += sprintf(buf + len, "%s", var) + 1;
214        }
215
216        return 0;
217}
218#endif /* !MODULE */
219
220extern char * _nvram_get(const char *name);
221extern int _nvram_set(const char *name, const char *value);
222extern int _nvram_unset(const char *name);
223extern int _nvram_getall(char *buf, int count);
224extern int _nvram_commit(struct nvram_header *header);
225extern int _nvram_init(void *sih);
226extern void _nvram_exit(void);
227
228/* Globals */
229static spinlock_t nvram_lock = SPIN_LOCK_UNLOCKED;
230static struct semaphore nvram_sem;
231static unsigned long nvram_offset = 0;
232static int nvram_major = -1;
233static struct class *nvram_class = NULL;
234static struct mtd_info *nvram_mtd = NULL;
235
236int
237_nvram_read(char *buf)
238{
239        struct nvram_header *header = (struct nvram_header *) buf;
240        size_t len;
241
242        if (!nvram_mtd ||
243            nvram_mtd->read(nvram_mtd, nvram_mtd->size - NVRAM_SPACE, NVRAM_SPACE, &len, buf) ||
244            len != NVRAM_SPACE ||
245            header->magic != NVRAM_MAGIC) {
246                /* Maybe we can recover some data from early initialization */
247                memcpy(buf, nvram_buf, NVRAM_SPACE);
248        }
249
250        return 0;
251}
252
253struct nvram_tuple *
254_nvram_realloc(struct nvram_tuple *t, const char *name, const char *value)
255{
256        if ((nvram_offset + strlen(value) + 1) > NVRAM_SPACE)
257                return NULL;
258
259        if (!t) {
260                if (!(t = kmalloc(sizeof(struct nvram_tuple) + strlen(name) + 1, GFP_ATOMIC)))
261                        return NULL;
262
263                /* Copy name */
264                t->name = (char *) &t[1];
265                strcpy(t->name, name);
266
267                t->value = NULL;
268        }
269
270        /* Copy value */
271        if (!t->value || strcmp(t->value, value)) {
272                t->value = &nvram_buf[nvram_offset];
273                strcpy(t->value, value);
274                nvram_offset += strlen(value) + 1;
275        }
276
277        return t;
278}
279
280void
281_nvram_free(struct nvram_tuple *t)
282{
283        if (!t)
284                nvram_offset = 0;
285        else
286                kfree(t);
287}
288
289int
290nvram_init(void *sih)
291{
292        return 0;
293}
294
295int
296nvram_set(const char *name, const char *value)
297{
298        unsigned long flags;
299        int ret;
300        struct nvram_header *header;
301
302        spin_lock_irqsave(&nvram_lock, flags);
303        if ((ret = _nvram_set(name, value))) {
304                /* Consolidate space and try again */
305                if ((header = kmalloc(NVRAM_SPACE, GFP_ATOMIC))) {
306                        if (_nvram_commit(header) == 0)
307                                ret = _nvram_set(name, value);
308                        kfree(header);
309                }
310        }
311        spin_unlock_irqrestore(&nvram_lock, flags);
312
313        return ret;
314}
315
316char *
317real_nvram_get(const char *name)
318{
319        unsigned long flags;
320        char *value;
321
322        spin_lock_irqsave(&nvram_lock, flags);
323        value = _nvram_get(name);
324        spin_unlock_irqrestore(&nvram_lock, flags);
325
326        return value;
327}
328
329char *
330nvram_get(const char *name)
331{
332        if (nvram_major >= 0)
333                return real_nvram_get(name);
334        else
335                return early_nvram_get(name);
336}
337
338int
339nvram_unset(const char *name)
340{
341        unsigned long flags;
342        int ret;
343
344        spin_lock_irqsave(&nvram_lock, flags);
345        ret = _nvram_unset(name);
346        spin_unlock_irqrestore(&nvram_lock, flags);
347
348        return ret;
349}
350
351static void
352erase_callback(struct erase_info *done)
353{
354        wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
355        wake_up(wait_q);
356}
357
358int
359nvram_commit(void)
360{
361        char *buf;
362        size_t erasesize, len, magic_len;
363        unsigned int i;
364        int ret;
365        struct nvram_header *header;
366        unsigned long flags;
367        u_int32_t offset;
368        DECLARE_WAITQUEUE(wait, current);
369        wait_queue_head_t wait_q;
370        struct erase_info erase;
371        u_int32_t magic_offset = 0; /* Offset for writing MAGIC # */
372
373        if (!nvram_mtd) {
374                printk("nvram_commit: NVRAM not found\n");
375                return -ENODEV;
376        }
377
378        if (in_interrupt()) {
379                printk("nvram_commit: not committing in interrupt\n");
380                return -EINVAL;
381        }
382
383        /* Backup sector blocks to be erased */
384        erasesize = ROUNDUP(NVRAM_SPACE, nvram_mtd->erasesize);
385        if (!(buf = kmalloc(erasesize, GFP_KERNEL))) {
386                printk("nvram_commit: out of memory\n");
387                return -ENOMEM;
388        }
389
390        down(&nvram_sem);
391
392        if ((i = erasesize - NVRAM_SPACE) > 0) {
393                offset = nvram_mtd->size - erasesize;
394                len = 0;
395                ret = nvram_mtd->read(nvram_mtd, offset, i, &len, buf);
396                if (ret || len != i) {
397                        printk("nvram_commit: read error ret = %d, len = %d/%d\n", ret, len, i);
398                        ret = -EIO;
399                        goto done;
400                }
401                header = (struct nvram_header *)(buf + i);
402                magic_offset = i + ((void *)&header->magic - (void *)header);
403        } else {
404                offset = nvram_mtd->size - NVRAM_SPACE;
405                magic_offset = ((void *)&header->magic - (void *)header);
406                header = (struct nvram_header *)buf;
407        }
408
409        /* clear the existing magic # to mark the NVRAM as unusable
410         * we can pull MAGIC bits low without erase
411         */
412        header->magic = NVRAM_CLEAR_MAGIC; /* All zeros magic */
413        /* Unlock sector blocks */
414        if (nvram_mtd->unlock)
415                nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize);
416        ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic),
417                &magic_len, (char *)&header->magic);
418        if (ret || magic_len != sizeof(header->magic)) {
419                printk("nvram_commit: clear MAGIC error\n");
420                ret = -EIO;
421                goto done;
422        }
423
424        header->magic = NVRAM_MAGIC;
425        /* reset MAGIC before we regenerate the NVRAM,
426         * otherwise we'll have an incorrect CRC
427         */
428        /* Regenerate NVRAM */
429        spin_lock_irqsave(&nvram_lock, flags);
430        ret = _nvram_commit(header);
431        spin_unlock_irqrestore(&nvram_lock, flags);
432        if (ret)
433                goto done;
434
435        /* Erase sector blocks */
436        init_waitqueue_head(&wait_q);
437        for (; offset < nvram_mtd->size - NVRAM_SPACE + header->len;
438                offset += nvram_mtd->erasesize) {
439
440                erase.mtd = nvram_mtd;
441                erase.addr = offset;
442                erase.len = nvram_mtd->erasesize;
443                erase.callback = erase_callback;
444                erase.priv = (u_long) &wait_q;
445
446                set_current_state(TASK_INTERRUPTIBLE);
447                add_wait_queue(&wait_q, &wait);
448
449                /* Unlock sector blocks */
450                if (nvram_mtd->unlock)
451                        nvram_mtd->unlock(nvram_mtd, offset, nvram_mtd->erasesize);
452
453                if ((ret = nvram_mtd->erase(nvram_mtd, &erase))) {
454                        set_current_state(TASK_RUNNING);
455                        remove_wait_queue(&wait_q, &wait);
456                        printk("nvram_commit: erase error\n");
457                        goto done;
458                }
459
460                /* Wait for erase to finish */
461                schedule();
462                remove_wait_queue(&wait_q, &wait);
463        }
464
465        /* Write partition up to end of data area */
466        header->magic = NVRAM_INVALID_MAGIC; /* All ones magic */
467        offset = nvram_mtd->size - erasesize;
468        i = erasesize - NVRAM_SPACE + header->len;
469        ret = nvram_mtd->write(nvram_mtd, offset, i, &len, buf);
470        if (ret || len != i) {
471                printk("nvram_commit: write error\n");
472                ret = -EIO;
473                goto done;
474        }
475
476        /* Now mark the NVRAM in flash as "valid" by setting the correct
477         * MAGIC #
478         */
479        header->magic = NVRAM_MAGIC;
480        ret = nvram_mtd->write(nvram_mtd, offset + magic_offset, sizeof(header->magic),
481                &magic_len, (char *)&header->magic);
482        if (ret || magic_len != sizeof(header->magic)) {
483                printk("nvram_commit: write MAGIC error\n");
484                ret = -EIO;
485                goto done;
486        }
487
488        offset = nvram_mtd->size - erasesize;
489        ret = nvram_mtd->read(nvram_mtd, offset, 4, &len, buf);
490
491done:
492        up(&nvram_sem);
493        kfree(buf);
494        return ret;
495}
496
497int
498nvram_getall(char *buf, int count)
499{
500        unsigned long flags;
501        int ret;
502
503        spin_lock_irqsave(&nvram_lock, flags);
504        if (nvram_major >= 0)
505                ret = _nvram_getall(buf, count);
506        else
507                ret = early_nvram_getall(buf, count);
508        spin_unlock_irqrestore(&nvram_lock, flags);
509
510        return ret;
511}
512
513EXPORT_SYMBOL(nvram_init);
514EXPORT_SYMBOL(nvram_get);
515EXPORT_SYMBOL(nvram_getall);
516EXPORT_SYMBOL(nvram_set);
517EXPORT_SYMBOL(nvram_unset);
518EXPORT_SYMBOL(nvram_commit);
519
520/* User mode interface below */
521
522static ssize_t
523dev_nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos)
524{
525        char tmp[100], *name = tmp, *value;
526        ssize_t ret;
527        unsigned long off;
528
529        if (count > sizeof(tmp)) {
530                if (!(name = kmalloc(count, GFP_KERNEL)))
531                        return -ENOMEM;
532        }
533
534        if (copy_from_user(name, buf, count)) {
535                ret = -EFAULT;
536                goto done;
537        }
538
539        if (*name == '\0') {
540                /* Get all variables */
541                ret = nvram_getall(name, count);
542                if (ret == 0) {
543                        if (copy_to_user(buf, name, count)) {
544                                ret = -EFAULT;
545                                goto done;
546                        }
547                        ret = count;
548                }
549        } else {
550                if (!(value = nvram_get(name))) {
551                        ret = 0;
552                        goto done;
553                }
554
555                /* Provide the offset into mmap() space */
556                off = (unsigned long) value - (unsigned long) nvram_buf;
557
558                if (put_user(off, (unsigned long *) buf)) {
559                        ret = -EFAULT;
560                        goto done;
561                }
562
563                ret = sizeof(unsigned long);
564        }
565
566        flush_cache_all();
567
568done:
569        if (name != tmp)
570                kfree(name);
571
572        return ret;
573}
574
575static ssize_t
576dev_nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
577{
578        char tmp[100], *name = tmp, *value;
579        ssize_t ret;
580
581        if (count > sizeof(tmp)) {
582                if (!(name = kmalloc(count, GFP_KERNEL)))
583                        return -ENOMEM;
584        }
585
586        if (copy_from_user(name, buf, count)) {
587                ret = -EFAULT;
588                goto done;
589        }
590
591        value = name;
592        name = strsep(&value, "=");
593        if (value)
594                ret = nvram_set(name, value) ? : count;
595        else
596                ret = nvram_unset(name) ? : count;
597
598done:
599        if (name != tmp)
600                kfree(name);
601
602        return ret;
603}
604
605static int
606dev_nvram_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
607{
608        if (cmd != NVRAM_MAGIC)
609                return -EINVAL;
610
611        return nvram_commit();
612}
613
614static int
615dev_nvram_mmap(struct file *file, struct vm_area_struct *vma)
616{
617        unsigned long offset = __pa(nvram_buf) >> PAGE_SHIFT;
618
619        if (remap_pfn_range(vma, vma->vm_start, offset,
620                            vma->vm_end - vma->vm_start,
621                            vma->vm_page_prot))
622                return -EAGAIN;
623
624        return 0;
625}
626
627static int
628dev_nvram_open(struct inode *inode, struct file * file)
629{
630        return 0;
631}
632
633static int
634dev_nvram_release(struct inode *inode, struct file * file)
635{
636        return 0;
637}
638
639static struct file_operations dev_nvram_fops = {
640        owner:          THIS_MODULE,
641        open:           dev_nvram_open,
642        release:        dev_nvram_release,
643        read:           dev_nvram_read,
644        write:          dev_nvram_write,
645        ioctl:          dev_nvram_ioctl,
646        mmap:           dev_nvram_mmap
647};
648
649static void
650dev_nvram_exit(void)
651{
652        int order = 0;
653        struct page *page, *end;
654
655        if (nvram_class) {
656                class_device_destroy(nvram_class, MKDEV(nvram_major, 0));
657                class_destroy(nvram_class);
658        }
659
660        if (nvram_major >= 0)
661                unregister_chrdev(nvram_major, "nvram");
662
663        if (nvram_mtd)
664                put_mtd_device(nvram_mtd);
665
666        while ((PAGE_SIZE << order) < NVRAM_SPACE)
667                order++;
668        end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1);
669        for (page = virt_to_page(nvram_buf); page <= end; page++)
670                ClearPageReserved(page);
671
672        _nvram_exit();
673}
674
675static int
676dev_nvram_init(void)
677{
678        int order = 0, ret = 0;
679        struct page *page, *end;
680        unsigned int i;
681        osl_t *osh;
682
683        /* Allocate and reserve memory to mmap() */
684        while ((PAGE_SIZE << order) < NVRAM_SPACE)
685                order++;
686        end = virt_to_page(nvram_buf + (PAGE_SIZE << order) - 1);
687        for (page = virt_to_page(nvram_buf); page <= end; page++) {
688                SetPageReserved(page);
689        }
690
691#if defined(CONFIG_MTD) || defined(CONFIG_MTD_MODULE)
692        /* Find associated MTD device */
693        for (i = 0; i < MAX_MTD_DEVICES; i++) {
694                nvram_mtd = get_mtd_device(NULL, i);
695                if (!IS_ERR(nvram_mtd)) {
696                        if (!strcmp(nvram_mtd->name, "nvram") &&
697                            nvram_mtd->size >= NVRAM_SPACE) {
698                                break;
699                        }
700                        put_mtd_device(nvram_mtd);
701                }
702        }
703        if (i >= MAX_MTD_DEVICES)
704                nvram_mtd = NULL;
705        if (nvram_mtd==NULL)
706            printk("no nvram found\n");
707#endif
708
709        /* Initialize hash table lock */
710        spin_lock_init(&nvram_lock);
711
712        /* Initialize commit semaphore */
713        init_MUTEX(&nvram_sem);
714
715        /* Register char device */
716        if ((nvram_major = register_chrdev(229, "nvram", &dev_nvram_fops)) < 0) {
717                ret = nvram_major;
718                goto err;
719        }
720
721        if (si_osh(sih) == NULL) {
722                osh = osl_attach(NULL, SI_BUS, FALSE);
723                if (osh == NULL) {
724                        printk("Error allocating osh\n");
725                        unregister_chrdev(nvram_major, "nvram");
726                        goto err;
727                }
728                si_setosh(sih, osh);
729        }
730
731        /* Initialize hash table */
732        _nvram_init(sih);
733
734        /* Create /dev/nvram handle */
735        nvram_class = class_create(THIS_MODULE, "nvram");
736        if (IS_ERR(nvram_class)) {
737                printk("Error creating nvram class\n");
738                goto err;
739        }
740//      devfs_mk_cdev(MKDEV(nvram_major, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, "nvram");
741
742        /* Add the device nvram0 */
743        class_device_create(nvram_class, NULL, MKDEV(229, 0), NULL, "nvram");
744
745        /* Set the SDRAM NCDL value into NVRAM if not already done */
746        if (getintvar(NULL, "sdram_ncdl") == 0) {
747                unsigned int ncdl;
748                char buf[] = "0x00000000";
749
750                if ((ncdl = si_memc_get_ncdl(sih))) {
751                        sprintf(buf, "0x%08x", ncdl);
752                        nvram_set("sdram_ncdl", buf);
753                        nvram_commit();
754                }
755        }
756
757        return 0;
758
759err:
760        printk("Error initialising nvram %d\n",ret);
761        dev_nvram_exit();
762        return ret;
763}
764
765late_initcall(dev_nvram_init);
766module_exit(dev_nvram_exit);
Note: See TracBrowser for help on using the repository browser.