2010-06-23 15:49:17 -07:00
/*
* arch / arm / mach - tegra / fuse . c
*
* Copyright ( C ) 2010 Google , Inc .
*
* Author :
* Colin Cross < ccross @ android . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/kernel.h>
# include <linux/io.h>
2012-03-27 16:41:24 -07:00
# include <linux/export.h>
2013-01-11 13:16:19 +05:30
# include <linux/tegra-soc.h>
2010-06-23 15:49:17 -07:00
# include "fuse.h"
2012-10-04 14:24:09 -06:00
# include "iomap.h"
2011-10-13 00:14:08 -07:00
# include "apbio.h"
2010-06-23 15:49:17 -07:00
# define FUSE_UID_LOW 0x108
# define FUSE_UID_HIGH 0x10c
# define FUSE_SKU_INFO 0x110
2012-11-15 15:42:32 +08:00
# define TEGRA20_FUSE_SPARE_BIT 0x200
2012-11-15 15:42:34 +08:00
# define TEGRA30_FUSE_SPARE_BIT 0x244
2010-06-23 15:49:17 -07:00
2011-10-13 00:31:20 -07:00
int tegra_sku_id ;
int tegra_cpu_process_id ;
int tegra_core_process_id ;
2012-02-10 01:47:42 +02:00
int tegra_chip_id ;
2012-11-15 15:42:34 +08:00
int tegra_cpu_speedo_id ; /* only exist in Tegra30 and later */
2012-11-15 15:42:33 +08:00
int tegra_soc_speedo_id ;
2011-10-13 00:31:20 -07:00
enum tegra_revision tegra_revision ;
2012-11-15 15:42:32 +08:00
static int tegra_fuse_spare_bit ;
2012-11-15 15:42:33 +08:00
static void ( * tegra_init_speedo_data ) ( void ) ;
2012-11-15 15:42:32 +08:00
2011-10-17 16:39:24 -07:00
/* The BCT to use at boot is specified by board straps that can be read
* through a APB misc register and decoded . 2 bits , i . e . 4 possible BCTs .
*/
int tegra_bct_strapping ;
# define STRAP_OPT 0x008
# define GMI_AD0 (1 << 4)
# define GMI_AD1 (1 << 5)
# define RAM_ID_MASK (GMI_AD0 | GMI_AD1)
# define RAM_CODE_SHIFT 4
2011-10-13 00:31:20 -07:00
static const char * tegra_revision_name [ TEGRA_REVISION_MAX ] = {
[ TEGRA_REVISION_UNKNOWN ] = " unknown " ,
[ TEGRA_REVISION_A01 ] = " A01 " ,
[ TEGRA_REVISION_A02 ] = " A02 " ,
[ TEGRA_REVISION_A03 ] = " A03 " ,
[ TEGRA_REVISION_A03p ] = " A03 prime " ,
[ TEGRA_REVISION_A04 ] = " A04 " ,
} ;
2012-11-15 15:42:32 +08:00
u32 tegra_fuse_readl ( unsigned long offset )
2010-06-23 15:49:17 -07:00
{
2011-10-13 00:14:08 -07:00
return tegra_apb_readl ( TEGRA_FUSE_BASE + offset ) ;
2010-06-23 15:49:17 -07:00
}
2012-11-15 15:42:32 +08:00
bool tegra_spare_fuse ( int bit )
2010-06-23 15:49:17 -07:00
{
2012-11-15 15:42:32 +08:00
return tegra_fuse_readl ( tegra_fuse_spare_bit + bit * 4 ) ;
2010-06-23 15:49:17 -07:00
}
2012-02-10 01:47:41 +02:00
static enum tegra_revision tegra_get_revision ( u32 id )
2010-06-23 15:49:17 -07:00
{
2011-10-13 00:31:20 -07:00
u32 minor_rev = ( id > > 16 ) & 0xf ;
switch ( minor_rev ) {
case 1 :
return TEGRA_REVISION_A01 ;
case 2 :
return TEGRA_REVISION_A02 ;
case 3 :
2012-02-10 01:47:41 +02:00
if ( tegra_chip_id = = TEGRA20 & &
2012-11-15 15:42:32 +08:00
( tegra_spare_fuse ( 18 ) | | tegra_spare_fuse ( 19 ) ) )
2011-10-13 00:31:20 -07:00
return TEGRA_REVISION_A03p ;
else
return TEGRA_REVISION_A03 ;
case 4 :
return TEGRA_REVISION_A04 ;
default :
return TEGRA_REVISION_UNKNOWN ;
}
2010-06-23 15:49:17 -07:00
}
2012-11-15 15:42:33 +08:00
static void tegra_get_process_id ( void )
{
u32 reg ;
reg = tegra_fuse_readl ( tegra_fuse_spare_bit ) ;
tegra_cpu_process_id = ( reg > > 6 ) & 3 ;
reg = tegra_fuse_readl ( tegra_fuse_spare_bit ) ;
tegra_core_process_id = ( reg > > 12 ) & 3 ;
}
2013-01-11 13:16:19 +05:30
u32 tegra_read_chipid ( void )
{
return readl_relaxed ( IO_ADDRESS ( TEGRA_APB_MISC_BASE ) + 0x804 ) ;
}
2010-06-23 15:49:17 -07:00
void tegra_init_fuse ( void )
{
2012-02-10 01:47:41 +02:00
u32 id ;
2012-08-10 18:33:02 +05:30
u32 reg = readl ( IO_ADDRESS ( TEGRA_CLK_RESET_BASE + 0x48 ) ) ;
2010-06-23 15:49:17 -07:00
reg | = 1 < < 28 ;
2012-08-10 18:33:02 +05:30
writel ( reg , IO_ADDRESS ( TEGRA_CLK_RESET_BASE + 0x48 ) ) ;
2010-06-23 15:49:17 -07:00
2011-10-13 00:31:20 -07:00
reg = tegra_fuse_readl ( FUSE_SKU_INFO ) ;
tegra_sku_id = reg & 0xFF ;
2011-10-17 16:39:24 -07:00
reg = tegra_apb_readl ( TEGRA_APB_MISC_BASE + STRAP_OPT ) ;
tegra_bct_strapping = ( reg & RAM_ID_MASK ) > > RAM_CODE_SHIFT ;
2013-01-11 13:16:19 +05:30
id = tegra_read_chipid ( ) ;
2012-02-10 01:47:41 +02:00
tegra_chip_id = ( id > > 8 ) & 0xff ;
2012-11-15 15:42:33 +08:00
switch ( tegra_chip_id ) {
case TEGRA20 :
2012-11-15 15:42:34 +08:00
tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT ;
2012-11-15 15:42:33 +08:00
tegra_init_speedo_data = & tegra20_init_speedo_data ;
break ;
2012-11-15 15:42:34 +08:00
case TEGRA30 :
tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT ;
tegra_init_speedo_data = & tegra30_init_speedo_data ;
break ;
2012-11-15 15:42:33 +08:00
default :
2012-11-15 15:42:34 +08:00
pr_warn ( " Tegra: unknown chip id %d \n " , tegra_chip_id ) ;
tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT ;
2012-11-15 15:42:33 +08:00
tegra_init_speedo_data = & tegra_get_process_id ;
}
2012-02-10 01:47:41 +02:00
tegra_revision = tegra_get_revision ( id ) ;
2012-11-15 15:42:33 +08:00
tegra_init_speedo_data ( ) ;
2011-10-13 00:31:20 -07:00
pr_info ( " Tegra Revision: %s SKU: %d CPU Process: %d Core Process: %d \n " ,
2012-02-10 01:47:41 +02:00
tegra_revision_name [ tegra_revision ] ,
2011-10-13 00:31:20 -07:00
tegra_sku_id , tegra_cpu_process_id ,
tegra_core_process_id ) ;
2010-06-23 15:49:17 -07:00
}
unsigned long long tegra_chip_uid ( void )
{
unsigned long long lo , hi ;
2011-10-13 00:14:08 -07:00
lo = tegra_fuse_readl ( FUSE_UID_LOW ) ;
hi = tegra_fuse_readl ( FUSE_UID_HIGH ) ;
2010-06-23 15:49:17 -07:00
return ( hi < < 32ull ) | lo ;
}
2012-01-13 16:38:37 +11:00
EXPORT_SYMBOL ( tegra_chip_uid ) ;