2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
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
*/
2015-04-01 08:23:03 +02:00
# include <linux/io.h>
2010-06-08 19:06:01 +02:00
# include <linux/types.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/string.h>
2014-10-29 10:05:06 +01:00
# include <linux/mtd/mtd.h>
2014-12-01 07:58:18 +01:00
# include <linux/bcm47xx_nvram.h>
2010-06-08 19:06:01 +02:00
2014-12-10 17:38:26 +01:00
# define NVRAM_MAGIC 0x48534C46 /* 'FLSH' */
2015-04-01 08:23:04 +02:00
# define NVRAM_SPACE 0x10000
2014-12-10 17:38:26 +01:00
# define NVRAM_MAX_GPIO_ENTRIES 32
# define NVRAM_MAX_GPIO_VALUE_LEN 30
2014-10-30 12:50:03 +01:00
# 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 */
} ;
2010-06-08 19:06:01 +02:00
static char nvram_buf [ NVRAM_SPACE ] ;
2015-06-06 23:16:23 +02:00
static size_t nvram_len ;
2018-04-08 22:57:33 +02:00
static const u32 nvram_sizes [ ] = { 0x6000 , 0x8000 , 0xF000 , 0x10000 } ;
2010-06-08 19:06:01 +02:00
2014-09-03 22:51:06 +02:00
static u32 find_nvram_size ( void __iomem * end )
2012-12-26 19:51:13 +00:00
{
2014-09-03 22:51:06 +02:00
struct nvram_header __iomem * header ;
2012-12-26 19:51:13 +00:00
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( nvram_sizes ) ; i + + ) {
2014-09-03 22:51:06 +02:00
header = ( struct nvram_header * ) ( end - nvram_sizes [ i ] ) ;
2014-10-30 12:50:03 +01:00
if ( header - > magic = = NVRAM_MAGIC )
2012-12-26 19:51:13 +00:00
return nvram_sizes [ i ] ;
}
return 0 ;
}
2010-06-08 19:06:01 +02:00
/* Probe for NVRAM header */
2014-09-03 22:51:06 +02:00
static int nvram_find_and_copy ( void __iomem * iobase , u32 lim )
2010-06-08 19:06:01 +02:00
{
2014-09-03 22:51:06 +02:00
struct nvram_header __iomem * header ;
2011-07-23 01:20:12 +02:00
u32 off ;
2012-12-26 19:51:13 +00:00
u32 size ;
2010-06-08 19:06:01 +02:00
2015-06-06 23:16:23 +02:00
if ( nvram_len ) {
2014-09-03 22:51:06 +02:00
pr_warn ( " nvram already initialized \n " ) ;
return - EEXIST ;
}
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 */
2014-09-03 22:51:06 +02:00
size = find_nvram_size ( iobase + off ) ;
2012-12-26 19:51:13 +00:00
if ( size ) {
2014-09-03 22:51:06 +02:00
header = ( struct nvram_header * ) ( iobase + 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 */
2014-09-03 22:51:06 +02:00
header = ( struct nvram_header * ) ( iobase + 4096 ) ;
2014-10-30 12:50:03 +01:00
if ( header - > magic = = NVRAM_MAGIC ) {
2012-12-26 19:51:13 +00:00
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
2014-09-03 22:51:06 +02:00
header = ( struct nvram_header * ) ( iobase + 1024 ) ;
2014-10-30 12:50:03 +01:00
if ( header - > magic = = NVRAM_MAGIC ) {
2012-12-26 19:51:13 +00:00
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 :
2016-01-20 14:58:41 -08:00
__ioread32_copy ( nvram_buf , header , sizeof ( * header ) / 4 ) ;
2016-03-17 14:17:20 -07:00
nvram_len = ( ( struct nvram_header * ) ( nvram_buf ) ) - > len ;
2015-06-06 23:16:23 +02:00
if ( nvram_len > size ) {
2015-05-21 15:27:23 +02:00
pr_err ( " The nvram size according to the header seems to be bigger than the partition on flash \n " ) ;
2015-06-06 23:16:23 +02:00
nvram_len = size ;
2015-05-21 15:27:23 +02:00
}
2015-06-06 23:16:23 +02:00
if ( nvram_len > = NVRAM_SPACE ) {
2015-05-21 15:27:23 +02:00
pr_err ( " nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes \n " ,
2016-03-17 14:17:20 -07:00
nvram_len , NVRAM_SPACE - 1 ) ;
2015-06-06 23:16:23 +02:00
nvram_len = NVRAM_SPACE - 1 ;
2015-05-21 15:27:23 +02:00
}
/* proceed reading data after header */
2016-01-20 14:58:41 -08:00
__ioread32_copy ( nvram_buf + sizeof ( * header ) , header + 1 ,
DIV_ROUND_UP ( nvram_len , 4 ) ) ;
2015-05-12 18:46:11 +02:00
nvram_buf [ NVRAM_SPACE - 1 ] = ' \0 ' ;
2012-12-26 19:51:10 +00:00
return 0 ;
2010-06-08 19:06:01 +02:00
}
2014-09-03 22:59:45 +02:00
/*
* 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 )
2014-09-03 22:51:06 +02:00
{
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 ;
}
2012-12-26 19:51:12 +00:00
static int nvram_init ( void )
2012-12-26 08:29:17 +00:00
{
2014-10-29 10:05:06 +01:00
# ifdef CONFIG_MTD
struct mtd_info * mtd ;
struct nvram_header header ;
size_t bytes_read ;
2015-04-01 08:23:05 +02:00
int err ;
2014-10-29 10:05:06 +01:00
mtd = get_mtd_device_nm ( " nvram " ) ;
if ( IS_ERR ( mtd ) )
return - ENODEV ;
2015-04-01 08:23:05 +02:00
err = mtd_read ( mtd , 0 , sizeof ( header ) , & bytes_read , ( uint8_t * ) & header ) ;
2015-05-21 15:27:23 +02:00
if ( ! err & & header . magic = = NVRAM_MAGIC & &
header . len > sizeof ( header ) ) {
2015-06-06 23:16:23 +02:00
nvram_len = header . len ;
if ( nvram_len > = NVRAM_SPACE ) {
2015-04-01 08:23:05 +02:00
pr_err ( " nvram on flash (%i bytes) is bigger than the reserved space in memory, will just copy the first %i bytes \n " ,
2015-05-21 15:27:23 +02:00
header . len , NVRAM_SPACE ) ;
2015-06-06 23:16:23 +02:00
nvram_len = NVRAM_SPACE - 1 ;
2015-04-01 08:23:05 +02:00
}
2014-10-29 10:05:06 +01:00
2015-06-06 23:16:23 +02:00
err = mtd_read ( mtd , 0 , nvram_len , & nvram_len ,
2015-05-21 15:27:23 +02:00
( u8 * ) nvram_buf ) ;
2015-06-06 23:16:23 +02:00
return err ;
2014-10-29 10:05:06 +01:00
}
# endif
2014-10-28 13:30:23 +01:00
2012-12-26 19:51:10 +00:00
return - ENXIO ;
2012-12-26 08:29:17 +00:00
}
2014-10-30 12:50:03 +01:00
int bcm47xx_nvram_getenv ( const char * name , char * val , size_t val_len )
2010-06-08 19:06:01 +02:00
{
char * var , * value , * end , * eq ;
2015-05-12 18:46:12 +02: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
2015-06-06 23:16:23 +02:00
if ( ! nvram_len ) {
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 ) ] ;
2015-05-12 18:46:12 +02:00
end = nvram_buf + sizeof ( nvram_buf ) ;
while ( var < end & & * var ) {
eq = strchr ( var , ' = ' ) ;
2010-06-08 19:06:01 +02:00
if ( ! eq )
break ;
value = eq + 1 ;
2014-12-10 17:38:26 +01:00
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 ) ;
2015-05-12 18:46:12 +02:00
var = value + strlen ( value ) + 1 ;
2015-04-01 08:23:03 +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 ;
2014-12-10 17:38:26 +01:00
char nvram_var [ ] = " gpioXX " ;
char buf [ NVRAM_MAX_GPIO_VALUE_LEN ] ;
2013-09-18 13:31:15 +02:00
2014-12-10 17:38:26 +01:00
/* TODO: Optimize it to don't call getenv so many times */
for ( i = 0 ; i < NVRAM_MAX_GPIO_ENTRIES ; 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 ) ;
2015-05-21 15:27:23 +02:00
char * bcm47xx_nvram_get_contents ( size_t * nvram_size )
{
int err ;
char * nvram ;
2015-06-06 23:16:23 +02:00
if ( ! nvram_len ) {
2015-05-21 15:27:23 +02:00
err = nvram_init ( ) ;
if ( err )
return NULL ;
}
2015-06-06 23:16:23 +02:00
* nvram_size = nvram_len - sizeof ( struct nvram_header ) ;
2015-05-21 15:27:23 +02:00
nvram = vmalloc ( * nvram_size ) ;
if ( ! nvram )
return NULL ;
memcpy ( nvram , & nvram_buf [ sizeof ( struct nvram_header ) ] , * nvram_size ) ;
return nvram ;
}
EXPORT_SYMBOL ( bcm47xx_nvram_get_contents ) ;
2015-06-10 23:05:08 +02:00
2015-08-19 10:32:23 +08:00
MODULE_LICENSE ( " GPL v2 " ) ;