diff options
author | Rafał Miłecki <zajec5@gmail.com> | 2014-12-10 21:50:57 +0000 |
---|---|---|
committer | Rafał Miłecki <zajec5@gmail.com> | 2014-12-10 21:50:57 +0000 |
commit | 40d94a3ef026e9e9f27203f744af405fb2e5c82b (patch) | |
tree | 7d15a6ed54dbeb4f254454cbe4eafaf9a761fc2b /target/linux/bcm53xx/files | |
parent | ba81966001af4ff028795b6edbf25ce49596d654 (diff) | |
download | upstream-40d94a3ef026e9e9f27203f744af405fb2e5c82b.tar.gz upstream-40d94a3ef026e9e9f27203f744af405fb2e5c82b.tar.bz2 upstream-40d94a3ef026e9e9f27203f744af405fb2e5c82b.zip |
bcm53xx: backport NVRAM driver sent upstream
Signed-off-by: Rafał Miłecki <zajec5@gmail.com>
SVN-Revision: 43612
Diffstat (limited to 'target/linux/bcm53xx/files')
5 files changed, 255 insertions, 228 deletions
diff --git a/target/linux/bcm53xx/files/drivers/firmware/broadcom/Kconfig b/target/linux/bcm53xx/files/drivers/firmware/broadcom/Kconfig new file mode 100644 index 0000000000..6bed119930 --- /dev/null +++ b/target/linux/bcm53xx/files/drivers/firmware/broadcom/Kconfig @@ -0,0 +1,11 @@ +config BCM47XX_NVRAM + bool "Broadcom NVRAM driver" + depends on BCM47XX || ARCH_BCM_5301X + help + Broadcom home routers contain flash partition called "nvram" with all + important hardware configuration as well as some minor user setup. + NVRAM partition contains a text-like data representing name=value + pairs. + This driver provides an easy way to get value of requested parameter. + It simply reads content of NVRAM and parses it. It doesn't control any + hardware part itself. diff --git a/target/linux/bcm53xx/files/drivers/firmware/broadcom/Makefile b/target/linux/bcm53xx/files/drivers/firmware/broadcom/Makefile new file mode 100644 index 0000000000..d0e683583c --- /dev/null +++ b/target/linux/bcm53xx/files/drivers/firmware/broadcom/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o diff --git a/target/linux/bcm53xx/files/drivers/firmware/broadcom/bcm47xx_nvram.c b/target/linux/bcm53xx/files/drivers/firmware/broadcom/bcm47xx_nvram.c new file mode 100644 index 0000000000..b6e1cc733d --- /dev/null +++ b/target/linux/bcm53xx/files/drivers/firmware/broadcom/bcm47xx_nvram.c @@ -0,0 +1,232 @@ +/* + * BCM947xx nvram variable access + * + * Copyright (C) 2005 Broadcom Corporation + * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> + * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/types.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/mtd/mtd.h> +#include <linux/bcm47xx_nvram.h> + +#define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */ +#define NVRAM_SPACE 0x8000 +#define NVRAM_MAX_GPIO_ENTRIES 32 +#define NVRAM_MAX_GPIO_VALUE_LEN 30 + +#define FLASH_MIN 0x00020000 /* Minimum flash size */ + +struct nvram_header { + u32 magic; + u32 len; + u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ + u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ + u32 config_ncdl; /* ncdl values for memc */ +}; + +static char nvram_buf[NVRAM_SPACE]; +static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; + +static u32 find_nvram_size(void __iomem *end) +{ + struct nvram_header __iomem *header; + int i; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { + header = (struct nvram_header *)(end - nvram_sizes[i]); + if (header->magic == NVRAM_MAGIC) + return nvram_sizes[i]; + } + + return 0; +} + +/* Probe for NVRAM header */ +static int nvram_find_and_copy(void __iomem *iobase, u32 lim) +{ + struct nvram_header __iomem *header; + int i; + u32 off; + u32 *src, *dst; + u32 size; + + if (nvram_buf[0]) { + pr_warn("nvram already initialized\n"); + return -EEXIST; + } + + /* TODO: when nvram is on nand flash check for bad blocks first. */ + off = FLASH_MIN; + while (off <= lim) { + /* Windowed flash access */ + size = find_nvram_size(iobase + off); + if (size) { + header = (struct nvram_header *)(iobase + off - size); + goto found; + } + off <<= 1; + } + + /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ + header = (struct nvram_header *)(iobase + 4096); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + + header = (struct nvram_header *)(iobase + 1024); + if (header->magic == NVRAM_MAGIC) { + size = NVRAM_SPACE; + goto found; + } + + pr_err("no nvram found\n"); + return -ENXIO; + +found: + if (header->len > size) + pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n"); + if (header->len > NVRAM_SPACE) + pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", + header->len, NVRAM_SPACE); + + src = (u32 *)header; + dst = (u32 *)nvram_buf; + for (i = 0; i < sizeof(struct nvram_header); i += 4) + *dst++ = __raw_readl(src++); + for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4) + *dst++ = readl(src++); + + return 0; +} + +/* + * On bcm47xx we need access to the NVRAM very early, so we can't use mtd + * subsystem to access flash. We can't even use platform device / driver to + * store memory offset. + * To handle this we provide following symbol. It's supposed to be called as + * soon as we get info about flash device, before any NVRAM entry is needed. + */ +int bcm47xx_nvram_init_from_mem(u32 base, u32 lim) +{ + void __iomem *iobase; + int err; + + iobase = ioremap_nocache(base, lim); + if (!iobase) + return -ENOMEM; + + err = nvram_find_and_copy(iobase, lim); + + iounmap(iobase); + + return err; +} + +static int nvram_init(void) +{ +#ifdef CONFIG_MTD + struct mtd_info *mtd; + struct nvram_header header; + size_t bytes_read; + int err, i; + + mtd = get_mtd_device_nm("nvram"); + if (IS_ERR(mtd)) + return -ENODEV; + + for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { + loff_t from = mtd->size - nvram_sizes[i]; + + if (from < 0) + continue; + + err = mtd_read(mtd, from, sizeof(header), &bytes_read, + (uint8_t *)&header); + if (!err && header.magic == NVRAM_MAGIC) { + u8 *dst = (uint8_t *)nvram_buf; + size_t len = header.len; + + if (header.len > NVRAM_SPACE) { + pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", + header.len, NVRAM_SPACE); + len = NVRAM_SPACE; + } + + err = mtd_read(mtd, from, len, &bytes_read, dst); + if (err) + return err; + + return 0; + } + } +#endif + + return -ENXIO; +} + +int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len) +{ + char *var, *value, *end, *eq; + int data_left, err; + + if (!name) + return -EINVAL; + + if (!nvram_buf[0]) { + err = nvram_init(); + if (err) + return err; + } + + /* Look for name=value and return value */ + var = &nvram_buf[sizeof(struct nvram_header)]; + end = nvram_buf + sizeof(nvram_buf) - 2; + end[0] = '\0'; + end[1] = '\0'; + for (; *var; var = value + strlen(value) + 1) { + data_left = end - var; + + eq = strnchr(var, data_left, '='); + if (!eq) + break; + value = eq + 1; + if (eq - var == strlen(name) && + strncmp(var, name, eq - var) == 0) + return snprintf(val, val_len, "%s", value); + } + return -ENOENT; +} +EXPORT_SYMBOL(bcm47xx_nvram_getenv); + +int bcm47xx_nvram_gpio_pin(const char *name) +{ + int i, err; + char nvram_var[] = "gpioXX"; + char buf[NVRAM_MAX_GPIO_VALUE_LEN]; + + /* TODO: Optimize it to don't call getenv so many times */ + for (i = 0; i < NVRAM_MAX_GPIO_ENTRIES; i++) { + err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i); + if (err <= 0) + continue; + err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)); + if (err <= 0) + continue; + if (!strcmp(name, buf)) + return i; + } + return -ENOENT; +} +EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin); + +MODULE_LICENSE("GPLv2"); diff --git a/target/linux/bcm53xx/files/drivers/misc/bcm47xx-nvram.c b/target/linux/bcm53xx/files/drivers/misc/bcm47xx-nvram.c deleted file mode 100644 index d1f9d7c6ae..0000000000 --- a/target/linux/bcm53xx/files/drivers/misc/bcm47xx-nvram.c +++ /dev/null @@ -1,217 +0,0 @@ -/* - * BCM947xx nvram variable access - * - * Copyright (C) 2005 Broadcom Corporation - * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2010-2014 Hauke Mehrtens <hauke@hauke-m.de> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - */ - -#include <linux/types.h> -#include <linux/module.h> -#include <linux/kernel.h> -#include <linux/string.h> -#include <linux/of_address.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/io.h> -#include <linux/bcm47xx_nvram.h> - -#define NVRAM_HEADER 0x48534C46 /* 'FLSH' */ -#define NVRAM_SPACE 0x8000 - -#define FLASH_MIN 0x00020000 /* Minimum flash size */ - -struct nvram_header { - u32 magic; - u32 len; - u32 crc_ver_init; /* 0:7 crc, 8:15 ver, 16:31 sdram_init */ - u32 config_refresh; /* 0:15 sdram_config, 16:31 sdram_refresh */ - u32 config_ncdl; /* ncdl values for memc */ -}; - -struct bcm47xx_nvram { - size_t nvram_len; - char *nvram_buf; -}; - -static struct bcm47xx_nvram *nvram = NULL; - -static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; - -static u32 find_nvram_size(void __iomem *end) -{ - struct nvram_header __iomem *header; - int i; - - for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { - header = (struct nvram_header __iomem *)(end - nvram_sizes[i]); - if (__raw_readl(&header->magic) == NVRAM_HEADER) - return nvram_sizes[i]; - } - - return 0; -} - -/* Probe for NVRAM header */ -static int nvram_find_and_copy(struct device *dev, void __iomem *base, - size_t len, char **nvram_buf, - size_t *nvram_len) -{ - struct nvram_header __iomem *header; - int i; - u32 off; - u32 *dst; - __le32 __iomem *src; - u32 size; - - /* TODO: when nvram is on nand flash check for bad blocks first. */ - off = FLASH_MIN; - while (off <= len) { - /* Windowed flash access */ - size = find_nvram_size(base + off); - if (size) { - header = (struct nvram_header __iomem *) - (base + off - size); - goto found; - } - off += 0x10000; - } - - /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ - header = (struct nvram_header __iomem *)(base + 4096); - if (__raw_readl(&header->magic) == NVRAM_HEADER) { - size = NVRAM_SPACE; - goto found; - } - - header = (struct nvram_header __iomem *)(base + 1024); - if (__raw_readl(&header->magic) == NVRAM_HEADER) { - size = NVRAM_SPACE; - goto found; - } - - *nvram_buf = NULL; - *nvram_len = 0; - pr_err("no nvram found\n"); - return -ENXIO; - -found: - if (readl(&header->len) > size) - pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n"); - *nvram_len = min_t(u32, readl(&header->len), size); - - *nvram_buf = devm_kzalloc(dev, *nvram_len, GFP_KERNEL); - if (!*nvram_buf) - return -ENOMEM; - - src = (__le32 __iomem *) header; - dst = (u32 *) *nvram_buf; - for (i = 0; i < sizeof(struct nvram_header); i += 4) - *dst++ = __raw_readl(src++); - for (; i < *nvram_len; i += 4) - *dst++ = readl(src++); - - return 0; -} - -int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len) -{ - char *var, *value, *end, *eq; - - if (!name || !nvram || !nvram->nvram_len) - return -EINVAL; - - /* Look for name=value and return value */ - var = nvram->nvram_buf + sizeof(struct nvram_header); - end = nvram->nvram_buf + nvram->nvram_len - 2; - end[0] = end[1] = '\0'; - for (; *var; var = value + strlen(value) + 1) { - eq = strchr(var, '='); - if (!eq) - break; - value = eq + 1; - if ((eq - var) == strlen(name) && - strncmp(var, name, (eq - var)) == 0) { - return snprintf(val, val_len, "%s", value); - } - } - return -ENOENT; -} -EXPORT_SYMBOL(bcm47xx_nvram_getenv); - -int bcm47xx_nvram_gpio_pin(const char *name) -{ - int i, err; - char nvram_var[10]; - char buf[30]; - - for (i = 0; i < 32; i++) { - err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i); - if (err <= 0) - continue; - err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)); - if (err <= 0) - continue; - if (!strcmp(name, buf)) - return i; - } - return -ENOENT; -} -EXPORT_SYMBOL(bcm47xx_nvram_gpio_pin); - -static int bcm47xx_nvram_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - int err; - struct resource flash_mem; - void __iomem *mmio; - - /* Alloc */ - nvram = devm_kzalloc(dev, sizeof(*nvram), GFP_KERNEL); - if (!nvram) - return -ENOMEM; - - err = of_address_to_resource(np, 0, &flash_mem); - if (err) - return err; - - mmio = ioremap_nocache(flash_mem.start, resource_size(&flash_mem)); - if (!mmio) - return -ENOMEM; - - err = nvram_find_and_copy(dev, mmio, resource_size(&flash_mem), - &nvram->nvram_buf, &nvram->nvram_len); - if (err) - goto err_unmap_mmio; - -err_unmap_mmio: - iounmap(mmio); - return err; -} - -static const struct of_device_id bcm47xx_nvram_of_match_table[] = { - { .compatible = "brcm,bcm47xx-nvram", }, - {}, -}; -MODULE_DEVICE_TABLE(of, mvebu_pcie_of_match_table); - -static struct platform_driver bcm47xx_nvram_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "bcm47xx-nvram", - .of_match_table = bcm47xx_nvram_of_match_table, - /* driver unloading/unbinding currently not supported */ - .suppress_bind_attrs = true, - }, - .probe = bcm47xx_nvram_probe, -}; -module_platform_driver(bcm47xx_nvram_driver); - -MODULE_AUTHOR("Hauke Mehrtens <hauke@hauke-m.de>"); -MODULE_LICENSE("GPLv2"); diff --git a/target/linux/bcm53xx/files/include/linux/bcm47xx_nvram.h b/target/linux/bcm53xx/files/include/linux/bcm47xx_nvram.h index 82890be7c4..0e52a92e84 100644 --- a/target/linux/bcm53xx/files/include/linux/bcm47xx_nvram.h +++ b/target/linux/bcm53xx/files/include/linux/bcm47xx_nvram.h @@ -1,8 +1,4 @@ /* - * Copyright (C) 2005, Broadcom Corporation - * Copyright (C) 2006, Felix Fietkau <nbd@openwrt.org> - * Copyright (C) 2014 Hauke Mehrtens <hauke@hauke-m.de> - * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -15,20 +11,24 @@ #include <linux/types.h> #include <linux/kernel.h> -#ifdef CONFIG_BCM47XX +#ifdef CONFIG_BCM47XX_NVRAM +int bcm47xx_nvram_init_from_mem(u32 base, u32 lim); int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len); - int bcm47xx_nvram_gpio_pin(const char *name); #else +static inline int bcm47xx_nvram_init_from_mem(u32 base, u32 lim) +{ + return -ENOTSUPP; +}; static inline int bcm47xx_nvram_getenv(const char *name, char *val, size_t val_len) { - return -ENXIO; -} - + return -ENOTSUPP; +}; static inline int bcm47xx_nvram_gpio_pin(const char *name) { - return -ENXIO; -} + return -ENOTSUPP; +}; #endif + #endif /* __BCM47XX_NVRAM_H */ |