source: trunk/target/linux/brcm-2.4/files/arch/mips/bcm947xx/nvram_linux.c @ 15242

Last change on this file since 15242 was 15242, checked in by nbd, 7 years ago

get rid of $Id$ - it has never helped us and it has broken too many patches ;)

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