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