2009-08-18 13:23:37 +01:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2008 Maxime Bizon < mbizon @ freebox . fr >
* Copyright ( C ) 2009 Florian Fainelli < florian @ openwrt . org >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/cpu.h>
2010-10-16 14:22:30 -07:00
# include <asm/cpu.h>
2009-10-14 09:56:00 +02:00
# include <asm/cpu-info.h>
2010-10-16 14:22:30 -07:00
# include <asm/mipsregs.h>
2009-08-18 13:23:37 +01:00
# include <bcm63xx_cpu.h>
# include <bcm63xx_regs.h>
# include <bcm63xx_io.h>
# include <bcm63xx_irq.h>
const unsigned long * bcm63xx_regs_base ;
EXPORT_SYMBOL ( bcm63xx_regs_base ) ;
const int * bcm63xx_irqs ;
EXPORT_SYMBOL ( bcm63xx_irqs ) ;
static u16 bcm63xx_cpu_id ;
static u16 bcm63xx_cpu_rev ;
static unsigned int bcm63xx_cpu_freq ;
static unsigned int bcm63xx_memory_size ;
2012-07-24 16:33:12 +02:00
static const unsigned long bcm6328_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6328 )
} ;
static const int bcm6328_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6328 )
} ;
2011-11-04 19:09:29 +01:00
static const unsigned long bcm6338_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6338 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const int bcm6338_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6338 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const unsigned long bcm6345_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6345 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const int bcm6345_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6345 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const unsigned long bcm6348_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6348 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const int bcm6348_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6348 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const unsigned long bcm6358_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6358 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:29 +01:00
static const int bcm6358_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6358 )
2009-08-18 13:23:37 +01:00
} ;
2011-11-04 19:09:35 +01:00
static const unsigned long bcm6368_regs_base [ ] = {
__GEN_CPU_REGS_TABLE ( 6368 )
} ;
static const int bcm6368_irqs [ ] = {
__GEN_CPU_IRQ_TABLE ( 6368 )
} ;
2009-08-18 13:23:37 +01:00
u16 __bcm63xx_get_cpu_id ( void )
{
return bcm63xx_cpu_id ;
}
EXPORT_SYMBOL ( __bcm63xx_get_cpu_id ) ;
u16 bcm63xx_get_cpu_rev ( void )
{
return bcm63xx_cpu_rev ;
}
EXPORT_SYMBOL ( bcm63xx_get_cpu_rev ) ;
unsigned int bcm63xx_get_cpu_freq ( void )
{
return bcm63xx_cpu_freq ;
}
unsigned int bcm63xx_get_memory_size ( void )
{
return bcm63xx_memory_size ;
}
static unsigned int detect_cpu_clock ( void )
{
2011-11-04 19:09:35 +01:00
switch ( bcm63xx_get_cpu_id ( ) ) {
2012-07-24 16:33:12 +02:00
case BCM6328_CPU_ID :
{
unsigned int tmp , mips_pll_fcvo ;
tmp = bcm_misc_readl ( MISC_STRAPBUS_6328_REG ) ;
mips_pll_fcvo = ( tmp & STRAPBUS_6328_FCVO_MASK )
> > STRAPBUS_6328_FCVO_SHIFT ;
switch ( mips_pll_fcvo ) {
case 0x12 :
case 0x14 :
case 0x19 :
return 160000000 ;
case 0x1c :
return 192000000 ;
case 0x13 :
case 0x15 :
return 200000000 ;
case 0x1a :
return 384000000 ;
case 0x16 :
return 400000000 ;
default :
return 320000000 ;
}
}
2011-11-04 19:09:35 +01:00
case BCM6338_CPU_ID :
/* BCM6338 has a fixed 240 Mhz frequency */
2009-08-18 13:23:37 +01:00
return 240000000 ;
2011-11-04 19:09:35 +01:00
case BCM6345_CPU_ID :
/* BCM6345 has a fixed 140Mhz frequency */
2009-08-18 13:23:37 +01:00
return 140000000 ;
2011-11-04 19:09:35 +01:00
case BCM6348_CPU_ID :
{
unsigned int tmp , n1 , n2 , m1 ;
2009-08-18 13:23:37 +01:00
/* 16MHz * (N1 + 1) * (N2 + 2) / (M1_CPU + 1) */
tmp = bcm_perf_readl ( PERF_MIPSPLLCTL_REG ) ;
n1 = ( tmp & MIPSPLLCTL_N1_MASK ) > > MIPSPLLCTL_N1_SHIFT ;
n2 = ( tmp & MIPSPLLCTL_N2_MASK ) > > MIPSPLLCTL_N2_SHIFT ;
m1 = ( tmp & MIPSPLLCTL_M1CPU_MASK ) > > MIPSPLLCTL_M1CPU_SHIFT ;
n1 + = 1 ;
n2 + = 2 ;
m1 + = 1 ;
2011-11-04 19:09:35 +01:00
return ( 16 * 1000000 * n1 * n2 ) / m1 ;
2009-08-18 13:23:37 +01:00
}
2011-11-04 19:09:35 +01:00
case BCM6358_CPU_ID :
{
unsigned int tmp , n1 , n2 , m1 ;
2009-08-18 13:23:37 +01:00
/* 16MHz * N1 * N2 / M1_CPU */
tmp = bcm_ddr_readl ( DDR_DMIPSPLLCFG_REG ) ;
n1 = ( tmp & DMIPSPLLCFG_N1_MASK ) > > DMIPSPLLCFG_N1_SHIFT ;
n2 = ( tmp & DMIPSPLLCFG_N2_MASK ) > > DMIPSPLLCFG_N2_SHIFT ;
m1 = ( tmp & DMIPSPLLCFG_M1_MASK ) > > DMIPSPLLCFG_M1_SHIFT ;
2011-11-04 19:09:35 +01:00
return ( 16 * 1000000 * n1 * n2 ) / m1 ;
2009-08-18 13:23:37 +01:00
}
2011-11-04 19:09:35 +01:00
case BCM6368_CPU_ID :
{
unsigned int tmp , p1 , p2 , ndiv , m1 ;
/* (64MHz / P1) * P2 * NDIV / M1_CPU */
tmp = bcm_ddr_readl ( DDR_DMIPSPLLCFG_6368_REG ) ;
p1 = ( tmp & DMIPSPLLCFG_6368_P1_MASK ) > >
DMIPSPLLCFG_6368_P1_SHIFT ;
p2 = ( tmp & DMIPSPLLCFG_6368_P2_MASK ) > >
DMIPSPLLCFG_6368_P2_SHIFT ;
ndiv = ( tmp & DMIPSPLLCFG_6368_NDIV_MASK ) > >
DMIPSPLLCFG_6368_NDIV_SHIFT ;
tmp = bcm_ddr_readl ( DDR_DMIPSPLLDIV_6368_REG ) ;
m1 = ( tmp & DMIPSPLLDIV_6368_MDIV_MASK ) > >
DMIPSPLLDIV_6368_MDIV_SHIFT ;
return ( ( ( 64 * 1000000 ) / p1 ) * p2 * ndiv ) / m1 ;
}
default :
BUG ( ) ;
}
2009-08-18 13:23:37 +01:00
}
/*
* attempt to detect the amount of memory installed
*/
static unsigned int detect_memory_size ( void )
{
unsigned int cols = 0 , rows = 0 , is_32bits = 0 , banks = 0 ;
u32 val ;
2012-07-24 16:33:12 +02:00
if ( BCMCPU_IS_6328 ( ) )
return bcm_ddr_readl ( DDR_CSEND_REG ) < < 24 ;
2011-11-16 20:10:36 +01:00
if ( BCMCPU_IS_6345 ( ) ) {
val = bcm_sdram_readl ( SDRAM_MBASE_REG ) ;
return ( val * 8 * 1024 * 1024 ) ;
}
2009-08-18 13:23:37 +01:00
if ( BCMCPU_IS_6338 ( ) | | BCMCPU_IS_6348 ( ) ) {
val = bcm_sdram_readl ( SDRAM_CFG_REG ) ;
rows = ( val & SDRAM_CFG_ROW_MASK ) > > SDRAM_CFG_ROW_SHIFT ;
cols = ( val & SDRAM_CFG_COL_MASK ) > > SDRAM_CFG_COL_SHIFT ;
is_32bits = ( val & SDRAM_CFG_32B_MASK ) ? 1 : 0 ;
banks = ( val & SDRAM_CFG_BANK_MASK ) ? 2 : 1 ;
}
2011-11-04 19:09:35 +01:00
if ( BCMCPU_IS_6358 ( ) | | BCMCPU_IS_6368 ( ) ) {
2009-08-18 13:23:37 +01:00
val = bcm_memc_readl ( MEMC_CFG_REG ) ;
rows = ( val & MEMC_CFG_ROW_MASK ) > > MEMC_CFG_ROW_SHIFT ;
cols = ( val & MEMC_CFG_COL_MASK ) > > MEMC_CFG_COL_SHIFT ;
is_32bits = ( val & MEMC_CFG_32B_MASK ) ? 0 : 1 ;
banks = 2 ;
}
/* 0 => 11 address bits ... 2 => 13 address bits */
rows + = 11 ;
/* 0 => 8 address bits ... 2 => 10 address bits */
cols + = 8 ;
return 1 < < ( cols + rows + ( is_32bits + 1 ) + banks ) ;
}
void __init bcm63xx_cpu_init ( void )
{
unsigned int tmp , expected_cpu_id ;
struct cpuinfo_mips * c = & current_cpu_data ;
2009-10-14 09:56:00 +02:00
unsigned int cpu = smp_processor_id ( ) ;
2009-08-18 13:23:37 +01:00
/* soc registers location depends on cpu type */
expected_cpu_id = 0 ;
switch ( c - > cputype ) {
2010-10-16 14:22:30 -07:00
case CPU_BMIPS3300 :
if ( ( read_c0_prid ( ) & 0xff00 ) = = PRID_IMP_BMIPS3300_ALT ) {
expected_cpu_id = BCM6348_CPU_ID ;
2011-11-04 19:09:29 +01:00
bcm63xx_regs_base = bcm6348_regs_base ;
bcm63xx_irqs = bcm6348_irqs ;
2010-10-16 14:22:30 -07:00
} else {
__cpu_name [ cpu ] = " Broadcom BCM6338 " ;
expected_cpu_id = BCM6338_CPU_ID ;
2011-11-04 19:09:29 +01:00
bcm63xx_regs_base = bcm6338_regs_base ;
bcm63xx_irqs = bcm6338_irqs ;
2010-10-16 14:22:30 -07:00
}
2009-08-18 13:23:37 +01:00
break ;
2010-10-16 14:22:30 -07:00
case CPU_BMIPS32 :
2009-08-18 13:23:37 +01:00
expected_cpu_id = BCM6345_CPU_ID ;
2011-11-04 19:09:29 +01:00
bcm63xx_regs_base = bcm6345_regs_base ;
bcm63xx_irqs = bcm6345_irqs ;
2009-08-18 13:23:37 +01:00
break ;
2010-10-16 14:22:30 -07:00
case CPU_BMIPS4350 :
2012-07-24 16:33:12 +02:00
if ( ( read_c0_prid ( ) & 0xf0 ) = = 0x10 ) {
2011-11-04 19:09:35 +01:00
expected_cpu_id = BCM6358_CPU_ID ;
bcm63xx_regs_base = bcm6358_regs_base ;
bcm63xx_irqs = bcm6358_irqs ;
2012-07-24 16:33:12 +02:00
} else {
/* all newer chips have the same chip id location */
u16 chip_id = bcm_readw ( BCM_6368_PERF_BASE ) ;
switch ( chip_id ) {
2012-07-24 16:33:12 +02:00
case BCM6328_CPU_ID :
expected_cpu_id = BCM6328_CPU_ID ;
bcm63xx_regs_base = bcm6328_regs_base ;
bcm63xx_irqs = bcm6328_irqs ;
break ;
2012-07-24 16:33:12 +02:00
case BCM6368_CPU_ID :
expected_cpu_id = BCM6368_CPU_ID ;
bcm63xx_regs_base = bcm6368_regs_base ;
bcm63xx_irqs = bcm6368_irqs ;
break ;
}
2011-11-04 19:09:35 +01:00
}
2009-08-18 13:23:37 +01:00
break ;
}
/*
* really early to panic , but delaying panic would not help since we
* will never get any working console
*/
if ( ! expected_cpu_id )
panic ( " unsupported Broadcom CPU " ) ;
/*
* bcm63xx_regs_base is set , we can access soc registers
*/
/* double check CPU type */
tmp = bcm_perf_readl ( PERF_REV_REG ) ;
bcm63xx_cpu_id = ( tmp & REV_CHIPID_MASK ) > > REV_CHIPID_SHIFT ;
bcm63xx_cpu_rev = ( tmp & REV_REVID_MASK ) > > REV_REVID_SHIFT ;
if ( bcm63xx_cpu_id ! = expected_cpu_id )
panic ( " bcm63xx CPU id mismatch " ) ;
bcm63xx_cpu_freq = detect_cpu_clock ( ) ;
bcm63xx_memory_size = detect_memory_size ( ) ;
printk ( KERN_INFO " Detected Broadcom 0x%04x CPU revision %02x \n " ,
bcm63xx_cpu_id , bcm63xx_cpu_rev ) ;
printk ( KERN_INFO " CPU frequency is %u MHz \n " ,
bcm63xx_cpu_freq / 1000000 ) ;
printk ( KERN_INFO " %uMB of RAM installed \n " ,
bcm63xx_memory_size > > 20 ) ;
}