| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * BCM947xx nvram variable access | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright (C) 2005 Broadcom Corporation | 
					
						
							|  |  |  |  * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  |  * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-01-22 12:59:30 +01:00
										 |  |  |  * 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 | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  |  * 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/ssb/ssb.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/string.h>
 | 
					
						
							|  |  |  | #include <asm/addrspace.h>
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:14 +00:00
										 |  |  | #include <bcm47xx_nvram.h>
 | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | #include <asm/mach-bcm47xx/bcm47xx.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static char nvram_buf[NVRAM_SPACE]; | 
					
						
							| 
									
										
										
										
											2013-10-13 22:56:50 +02:00
										 |  |  | static const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | static u32 find_nvram_size(u32 end) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct nvram_header *header; | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { | 
					
						
							|  |  |  | 		header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]); | 
					
						
							|  |  |  | 		if (header->magic == NVRAM_HEADER) | 
					
						
							|  |  |  | 			return nvram_sizes[i]; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | /* Probe for NVRAM header */ | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | static int nvram_find_and_copy(u32 base, u32 lim) | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct nvram_header *header; | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2011-07-23 01:20:12 +02:00
										 |  |  | 	u32 off; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 	u32 *src, *dst; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	u32 size; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:11 +00:00
										 |  |  | 	/* TODO: when nvram is on nand flash check for bad blocks first. */ | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 	off = FLASH_MIN; | 
					
						
							|  |  |  | 	while (off <= lim) { | 
					
						
							|  |  |  | 		/* Windowed flash access */ | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 		size = find_nvram_size(base + off); | 
					
						
							|  |  |  | 		if (size) { | 
					
						
							|  |  |  | 			header = (struct nvram_header *)KSEG1ADDR(base + off - | 
					
						
							|  |  |  | 								  size); | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 			goto found; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 		off <<= 1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ | 
					
						
							|  |  |  | 	header = (struct nvram_header *) KSEG1ADDR(base + 4096); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	if (header->magic == NVRAM_HEADER) { | 
					
						
							|  |  |  | 		size = NVRAM_SPACE; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 		goto found; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	header = (struct nvram_header *) KSEG1ADDR(base + 1024); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	if (header->magic == NVRAM_HEADER) { | 
					
						
							|  |  |  | 		size = NVRAM_SPACE; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 		goto found; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	pr_err("no nvram found\n"); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	return -ENXIO; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | found: | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 	src = (u32 *) header; | 
					
						
							|  |  |  | 	dst = (u32 *) nvram_buf; | 
					
						
							|  |  |  | 	for (i = 0; i < sizeof(struct nvram_header); i += 4) | 
					
						
							|  |  |  | 		*dst++ = *src++; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4) | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 		*dst++ = le32_to_cpu(*src++); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:13 +00:00
										 |  |  | 	memset(dst, 0x0, NVRAM_SPACE - i); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | #ifdef CONFIG_BCM47XX_SSB
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | static int nvram_init_ssb(void) | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; | 
					
						
							|  |  |  | 	u32 base; | 
					
						
							|  |  |  | 	u32 lim; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (mcore->pflash.present) { | 
					
						
							|  |  |  | 		base = mcore->pflash.window; | 
					
						
							|  |  |  | 		lim = mcore->pflash.window_size; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pr_err("Couldn't find supported flash memory\n"); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	return nvram_find_and_copy(base, lim); | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #ifdef CONFIG_BCM47XX_BCMA
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | static int nvram_init_bcma(void) | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc; | 
					
						
							|  |  |  | 	u32 base; | 
					
						
							|  |  |  | 	u32 lim; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:11 +00:00
										 |  |  | #ifdef CONFIG_BCMA_NFLASH
 | 
					
						
							|  |  |  | 	if (cc->nflash.boot) { | 
					
						
							|  |  |  | 		base = BCMA_SOC_FLASH1; | 
					
						
							|  |  |  | 		lim = BCMA_SOC_FLASH1_SZ; | 
					
						
							|  |  |  | 	} else | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | 	if (cc->pflash.present) { | 
					
						
							|  |  |  | 		base = cc->pflash.window; | 
					
						
							|  |  |  | 		lim = cc->pflash.window_size; | 
					
						
							|  |  |  | #ifdef CONFIG_BCMA_SFLASH
 | 
					
						
							|  |  |  | 	} else if (cc->sflash.present) { | 
					
						
							|  |  |  | 		base = cc->sflash.window; | 
					
						
							|  |  |  | 		lim = cc->sflash.size; | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		pr_err("Couldn't find supported flash memory\n"); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	return nvram_find_and_copy(base, lim); | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | } | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:12 +00:00
										 |  |  | static int nvram_init(void) | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | { | 
					
						
							|  |  |  | 	switch (bcm47xx_bus_type) { | 
					
						
							|  |  |  | #ifdef CONFIG_BCM47XX_SSB
 | 
					
						
							|  |  |  | 	case BCM47XX_BUS_TYPE_SSB: | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 		return nvram_init_ssb(); | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | #ifdef CONFIG_BCM47XX_BCMA
 | 
					
						
							|  |  |  | 	case BCM47XX_BUS_TYPE_BCMA: | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 		return nvram_init_bcma(); | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | #endif
 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	return -ENXIO; | 
					
						
							| 
									
										
										
										
											2012-12-26 08:29:17 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:14 +00:00
										 |  |  | int bcm47xx_nvram_getenv(char *name, char *val, size_t val_len) | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	char *var, *value, *end, *eq; | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (!name) | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:09 +00:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 	if (!nvram_buf[0]) { | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:12 +00:00
										 |  |  | 		err = nvram_init(); | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:10 +00:00
										 |  |  | 		if (err) | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Look for name=value and return value */ | 
					
						
							|  |  |  | 	var = &nvram_buf[sizeof(struct nvram_header)]; | 
					
						
							|  |  |  | 	end = nvram_buf + sizeof(nvram_buf) - 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) { | 
					
						
							| 
									
										
										
										
											2012-02-28 00:56:11 +01:00
										 |  |  | 			return snprintf(val, val_len, "%s", value); | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:09 +00:00
										 |  |  | 	return -ENOENT; | 
					
						
							| 
									
										
										
										
											2010-06-08 19:06:01 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2012-12-26 19:51:14 +00:00
										 |  |  | EXPORT_SYMBOL(bcm47xx_nvram_getenv); | 
					
						
							| 
									
										
										
										
											2013-09-18 13:31:15 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | int bcm47xx_nvram_gpio_pin(const char *name) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i, err; | 
					
						
							|  |  |  | 	char nvram_var[10]; | 
					
						
							|  |  |  | 	char buf[30]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-13 17:48:12 +01:00
										 |  |  | 	for (i = 0; i < 32; i++) { | 
					
						
							| 
									
										
										
										
											2013-09-18 13:31:15 +02:00
										 |  |  | 		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); |