1121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb/* 2121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * BCM947xx nvram variable access 3121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * 4121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Copyright (C) 2005 Broadcom Corporation 5121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> 6f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> 7121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * 87034228792cc561e79ff8600f02884bd4c80e287Ralf Baechle * This program is free software; you can redistribute it and/or modify it 97034228792cc561e79ff8600f02884bd4c80e287Ralf Baechle * under the terms of the GNU General Public License as published by the 10121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * Free Software Foundation; either version 2 of the License, or (at your 11121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb * option) any later version. 12121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb */ 13121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 14121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/types.h> 15121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/module.h> 16121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/ssb/ssb.h> 17121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/kernel.h> 18121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <linux/string.h> 19121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <asm/addrspace.h> 20111bd981e2216827aab95503596501ab71bc7a7dHauke Mehrtens#include <bcm47xx_nvram.h> 21121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb#include <asm/mach-bcm47xx/bcm47xx.h> 22121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 23121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorbstatic char nvram_buf[NVRAM_SPACE]; 24f4c4d589d5c1046bb5a9c17d98afcda43e04a315Hauke Mehrtensstatic const u32 nvram_sizes[] = {0x8000, 0xF000, 0x10000}; 25121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 26f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtensstatic u32 find_nvram_size(u32 end) 27f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens{ 28f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens struct nvram_header *header; 29f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens int i; 30f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens 31f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens for (i = 0; i < ARRAY_SIZE(nvram_sizes); i++) { 32f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens header = (struct nvram_header *)KSEG1ADDR(end - nvram_sizes[i]); 33f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (header->magic == NVRAM_HEADER) 34f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens return nvram_sizes[i]; 35f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens } 36f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens 37f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens return 0; 38f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens} 39f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens 40121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb/* Probe for NVRAM header */ 41cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_find_and_copy(u32 base, u32 lim) 42121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb{ 43121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb struct nvram_header *header; 44121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb int i; 4508ccf57283f89adbc2ff897ad82d6ad4560db7cdHauke Mehrtens u32 off; 46121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb u32 *src, *dst; 47f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens u32 size; 48121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 49c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens /* TODO: when nvram is on nand flash check for bad blocks first. */ 50121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb off = FLASH_MIN; 51121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb while (off <= lim) { 52121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb /* Windowed flash access */ 53f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens size = find_nvram_size(base + off); 54f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (size) { 55f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens header = (struct nvram_header *)KSEG1ADDR(base + off - 56f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens size); 57121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb goto found; 58f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens } 59121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb off <<= 1; 60121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb } 61121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 62121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb /* Try embedded NVRAM at 4 KB and 1 KB as last resorts */ 63121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb header = (struct nvram_header *) KSEG1ADDR(base + 4096); 64f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (header->magic == NVRAM_HEADER) { 65f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens size = NVRAM_SPACE; 66121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb goto found; 67f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens } 68121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 69121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb header = (struct nvram_header *) KSEG1ADDR(base + 1024); 70f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (header->magic == NVRAM_HEADER) { 71f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens size = NVRAM_SPACE; 72121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb goto found; 73f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens } 74121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 75f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens pr_err("no nvram found\n"); 76cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return -ENXIO; 77121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 78121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorbfound: 79f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens 80f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (header->len > size) 81f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens pr_err("The nvram size accoridng to the header seems to be bigger than the partition on flash\n"); 82f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens if (header->len > NVRAM_SPACE) 83f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens pr_err("nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes\n", 84f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens header->len, NVRAM_SPACE); 85f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens 86121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb src = (u32 *) header; 87121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb dst = (u32 *) nvram_buf; 88121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb for (i = 0; i < sizeof(struct nvram_header); i += 4) 89121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb *dst++ = *src++; 90f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens for (; i < header->len && i < NVRAM_SPACE && i < size; i += 4) 91121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb *dst++ = le32_to_cpu(*src++); 92f36738ddfeea02867b393e7f34da0cec48bafc54Hauke Mehrtens memset(dst, 0x0, NVRAM_SPACE - i); 93cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens 94cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return 0; 95121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb} 96121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 97bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_SSB 98cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_init_ssb(void) 99bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{ 100bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki struct ssb_mipscore *mcore = &bcm47xx_bus.ssb.mipscore; 101bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki u32 base; 102bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki u32 lim; 103bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 104bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki if (mcore->pflash.present) { 105bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki base = mcore->pflash.window; 106bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki lim = mcore->pflash.window_size; 107bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } else { 108bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki pr_err("Couldn't find supported flash memory\n"); 109cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return -ENXIO; 110bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } 111bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 112cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return nvram_find_and_copy(base, lim); 113bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki} 114bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif 115bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 116bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_BCMA 117cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtensstatic int nvram_init_bcma(void) 118bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{ 119bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki struct bcma_drv_cc *cc = &bcm47xx_bus.bcma.bus.drv_cc; 120bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki u32 base; 121bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki u32 lim; 122bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 123c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens#ifdef CONFIG_BCMA_NFLASH 124c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens if (cc->nflash.boot) { 125c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens base = BCMA_SOC_FLASH1; 126c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens lim = BCMA_SOC_FLASH1_SZ; 127c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens } else 128c4485671fbbb6fc453c2fb2dbb4bfc374770b0e7Hauke Mehrtens#endif 129bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki if (cc->pflash.present) { 130bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki base = cc->pflash.window; 131bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki lim = cc->pflash.window_size; 132bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCMA_SFLASH 133bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } else if (cc->sflash.present) { 134bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki base = cc->sflash.window; 135bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki lim = cc->sflash.size; 136bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif 137bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } else { 138bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki pr_err("Couldn't find supported flash memory\n"); 139cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return -ENXIO; 140bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } 141bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 142cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return nvram_find_and_copy(base, lim); 143bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki} 144bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif 145bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 146e58da16f716c0e7822e64a5d8a1f413041bc912eHauke Mehrtensstatic int nvram_init(void) 147bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki{ 148bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki switch (bcm47xx_bus_type) { 149bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_SSB 150bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki case BCM47XX_BUS_TYPE_SSB: 151cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return nvram_init_ssb(); 152bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif 153bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#ifdef CONFIG_BCM47XX_BCMA 154bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki case BCM47XX_BUS_TYPE_BCMA: 155cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return nvram_init_bcma(); 156bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki#endif 157bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki } 158cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return -ENXIO; 159bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki} 160bb76563214ec371a461ca5038904a5b93dbd6b46Rafał Miłecki 161111bd981e2216827aab95503596501ab71bc7a7dHauke Mehrtensint bcm47xx_nvram_getenv(char *name, char *val, size_t val_len) 162121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb{ 163121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb char *var, *value, *end, *eq; 164cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens int err; 165121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 166121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb if (!name) 167ee7e2f3c235f43d815c0be285101b5b91a3bcaa5Hauke Mehrtens return -EINVAL; 168121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 169cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens if (!nvram_buf[0]) { 170e58da16f716c0e7822e64a5d8a1f413041bc912eHauke Mehrtens err = nvram_init(); 171cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens if (err) 172cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens return err; 173cc4403e02541af226ae6b7da0917c8959dd73a75Hauke Mehrtens } 174121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb 175121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb /* Look for name=value and return value */ 176121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb var = &nvram_buf[sizeof(struct nvram_header)]; 177121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb end = nvram_buf + sizeof(nvram_buf) - 2; 178121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb end[0] = end[1] = '\0'; 179121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb for (; *var; var = value + strlen(value) + 1) { 180121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb eq = strchr(var, '='); 181121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb if (!eq) 182121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb break; 183121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb value = eq + 1; 184121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb if ((eq - var) == strlen(name) && 185121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb strncmp(var, name, (eq - var)) == 0) { 18644d4b2ae94b19080d6d033a1f5cf2cc47443be3bHauke Mehrtens return snprintf(val, val_len, "%s", value); 187121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb } 188121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb } 189ee7e2f3c235f43d815c0be285101b5b91a3bcaa5Hauke Mehrtens return -ENOENT; 190121915c4ee0812a14bc8d752bc210d0238d755c1Waldemar Brodkorb} 191111bd981e2216827aab95503596501ab71bc7a7dHauke MehrtensEXPORT_SYMBOL(bcm47xx_nvram_getenv); 19262cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens 19362cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtensint bcm47xx_nvram_gpio_pin(const char *name) 19462cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens{ 19562cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens int i, err; 19662cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens char nvram_var[10]; 19762cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens char buf[30]; 19862cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens 1994fe2169acecb6e62821dfe14bc5c5852870b516fRafał Miłecki for (i = 0; i < 32; i++) { 20062cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens err = snprintf(nvram_var, sizeof(nvram_var), "gpio%i", i); 20162cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens if (err <= 0) 20262cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens continue; 20362cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens err = bcm47xx_nvram_getenv(nvram_var, buf, sizeof(buf)); 20462cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens if (err <= 0) 20562cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens continue; 20662cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens if (!strcmp(name, buf)) 20762cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens return i; 20862cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens } 20962cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens return -ENOENT; 21062cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke Mehrtens} 21162cf3bc0b59cfb70a021784af914c6ea464d3af7Hauke MehrtensEXPORT_SYMBOL(bcm47xx_nvram_gpio_pin); 212