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