^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * BCM947xx nvram variable access
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2005 Broadcom Corporation
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/string.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/mtd/mtd.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/bcm47xx_nvram.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #define NVRAM_SPACE 0x10000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) #define NVRAM_MAX_GPIO_ENTRIES 32
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) #define NVRAM_MAX_GPIO_VALUE_LEN 30
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) #define FLASH_MIN 0x00020000 /* Minimum flash size */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) struct nvram_header {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) u32 magic;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) u32 len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) u32 config_ncdl; /* ncdl values for memc */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) static char nvram_buf[NVRAM_SPACE];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) static size_t nvram_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) static const u32 nvram_sizes[] = {0x6000, 0x8000, 0xF000, 0x10000};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) static u32 find_nvram_size(void __iomem *end)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) struct nvram_header __iomem *header;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) int i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) header = (struct nvram_header *)(end - nvram_sizes[i]);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) if (header->magic == NVRAM_MAGIC)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) return nvram_sizes[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) /* Probe for NVRAM header */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) static int nvram_find_and_copy(void __iomem *iobase, u32 lim)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) struct nvram_header __iomem *header;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) u32 off;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) u32 size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) if (nvram_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) pr_warn("nvram already initialized\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) return -EEXIST;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) /* TODO: when nvram is on nand flash check for bad blocks first. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) off = FLASH_MIN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) while (off <= lim) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /* Windowed flash access */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) size = find_nvram_size(iobase + off);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) if (size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) header = (struct nvram_header *)(iobase + off - size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) goto found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) off <<= 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) header = (struct nvram_header *)(iobase + 4096);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) if (header->magic == NVRAM_MAGIC) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) size = NVRAM_SPACE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) goto found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) header = (struct nvram_header *)(iobase + 1024);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (header->magic == NVRAM_MAGIC) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) size = NVRAM_SPACE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) goto found;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) pr_err("no nvram found\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) found:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) __ioread32_copy(nvram_buf, header, sizeof(*header) / 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) nvram_len = ((struct nvram_header *)(nvram_buf))->len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) if (nvram_len > size) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) pr_err("The nvram size according to the header seems to be bigger than the partition on flash\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) nvram_len = size;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) if (nvram_len >= NVRAM_SPACE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) nvram_len, NVRAM_SPACE - 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) nvram_len = NVRAM_SPACE - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) /* proceed reading data after header */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) __ioread32_copy(nvram_buf + sizeof(*header), header + 1,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) DIV_ROUND_UP(nvram_len, 4));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) nvram_buf[NVRAM_SPACE - 1] = '\0';
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) * On bcm47xx we need access to the NVRAM very early, so we can't use mtd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) * subsystem to access flash. We can't even use platform device / driver to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) * store memory offset.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) * To handle this we provide following symbol. It's supposed to be called as
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) * soon as we get info about flash device, before any NVRAM entry is needed.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) int bcm47xx_nvram_init_from_mem(u32 base, u32 lim)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) void __iomem *iobase;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) iobase = ioremap(base, lim);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) if (!iobase)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) err = nvram_find_and_copy(iobase, lim);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) iounmap(iobase);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) static int nvram_init(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) #ifdef CONFIG_MTD
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) struct mtd_info *mtd;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) struct nvram_header header;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) size_t bytes_read;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) mtd = get_mtd_device_nm("nvram");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (IS_ERR(mtd))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) err = mtd_read(mtd, 0, sizeof(header), &bytes_read, (uint8_t *)&header);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) if (!err && header.magic == NVRAM_MAGIC &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) header.len > sizeof(header)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) nvram_len = header.len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) if (nvram_len >= NVRAM_SPACE) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) pr_err("nvram on flash (%zu bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) nvram_len, NVRAM_SPACE);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) nvram_len = NVRAM_SPACE - 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) err = mtd_read(mtd, 0, nvram_len, &nvram_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) (u8 *)nvram_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) return -ENXIO;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) char *var, *value, *end, *eq;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) if (!name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) if (!nvram_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) err = nvram_init();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) return err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) /* Look for name=value and return value */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180) var = &nvram_buf[sizeof(struct nvram_header)];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) end = nvram_buf + sizeof(nvram_buf);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) while (var < end && *var) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183) eq = strchr(var, '=');
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (!eq)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) value = eq + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) if (eq - var == strlen(name) &&
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) strncmp(var, name, eq - var) == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) return snprintf(val, val_len, "%s", value);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) var = value + strlen(value) + 1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) EXPORT_SYMBOL(bcm47xx_nvram_getenv);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) int bcm47xx_nvram_gpio_pin(const char *name)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) int i, err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) char nvram_var[] = "gpioXX";
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) char buf[NVRAM_MAX_GPIO_VALUE_LEN];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) /* TODO: Optimize it to don't call getenv so many times */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) for (i = 0; i < NVRAM_MAX_GPIO_ENTRIES; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205) if (err <= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) if (err <= 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) if (!strcmp(name, buf))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) return i;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) return -ENOENT;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) char *bcm47xx_nvram_get_contents(size_t *nvram_size)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 218) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 219) int err;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 220) char *nvram;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 221)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 222) if (!nvram_len) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 223) err = nvram_init();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 224) if (err)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 225) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 226) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 227)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 228) *nvram_size = nvram_len - sizeof(struct nvram_header);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 229) nvram = vmalloc(*nvram_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 230) if (!nvram)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 231) return NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 232) memcpy(nvram, &nvram_buf[sizeof(struct nvram_header)], *nvram_size);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 233)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 234) return nvram;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 235) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 236) EXPORT_SYMBOL(bcm47xx_nvram_get_contents);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 237)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 238) MODULE_LICENSE("GPL v2");