2008-08-25 08:32:42 +04:00
/* chmc.c: Driver for UltraSPARC-III memory controller.
2005-04-17 02:20:36 +04:00
*
2008-08-25 08:32:42 +04:00
* Copyright ( C ) 2001 , 2007 , 2008 David S . Miller ( davem @ davemloft . net )
2005-04-17 02:20:36 +04:00
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/types.h>
# include <linux/slab.h>
# include <linux/list.h>
# include <linux/string.h>
# include <linux/sched.h>
# include <linux/smp.h>
# include <linux/errno.h>
# include <linux/init.h>
2008-08-25 08:32:42 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2005-04-17 02:20:36 +04:00
# include <asm/spitfire.h>
# include <asm/chmctrl.h>
2007-12-07 11:58:55 +03:00
# include <asm/cpudata.h>
2005-04-17 02:20:36 +04:00
# include <asm/oplib.h>
2006-06-23 07:04:30 +04:00
# include <asm/prom.h>
2008-08-25 08:32:42 +04:00
# include <asm/head.h>
2005-04-17 02:20:36 +04:00
# include <asm/io.h>
2008-08-25 09:08:34 +04:00
# include <asm/memctrl.h>
2005-04-17 02:20:36 +04:00
2008-08-25 08:32:42 +04:00
# define DRV_MODULE_NAME "chmc"
# define PFX DRV_MODULE_NAME ": "
# define DRV_MODULE_VERSION "0.2"
MODULE_AUTHOR ( " David S. Miller (davem@davemloft.net) " ) ;
MODULE_DESCRIPTION ( " UltraSPARC-III memory controller driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_MODULE_VERSION ) ;
2008-08-26 00:38:30 +04:00
static int mc_type ;
# define MC_TYPE_SAFARI 1
# define MC_TYPE_JBUS 2
static dimm_printer_t us3mc_dimm_printer ;
2005-04-17 02:20:36 +04:00
# define CHMCTRL_NDGRPS 2
# define CHMCTRL_NDIMMS 4
2008-08-25 08:45:44 +04:00
# define CHMC_DIMMS_PER_MC (CHMCTRL_NDGRPS * CHMCTRL_NDIMMS)
2005-04-17 02:20:36 +04:00
/* OBP memory-layout property format. */
2008-08-25 08:45:44 +04:00
struct chmc_obp_map {
2005-04-17 02:20:36 +04:00
unsigned char dimm_map [ 144 ] ;
unsigned char pin_map [ 576 ] ;
} ;
# define DIMM_LABEL_SZ 8
2008-08-25 08:45:44 +04:00
struct chmc_obp_mem_layout {
2005-04-17 02:20:36 +04:00
/* One max 8-byte string label per DIMM. Usually
* this matches the label on the motherboard where
* that DIMM resides .
*/
2008-08-25 08:45:44 +04:00
char dimm_labels [ CHMC_DIMMS_PER_MC ] [ DIMM_LABEL_SZ ] ;
2005-04-17 02:20:36 +04:00
/* If symmetric use map[0], else it is
* asymmetric and map [ 1 ] should be used .
*/
2008-08-25 08:45:44 +04:00
char symmetric ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
struct chmc_obp_map map [ 2 ] ;
2005-04-17 02:20:36 +04:00
} ;
# define CHMCTRL_NBANKS 4
2008-08-25 08:45:44 +04:00
struct chmc_bank_info {
struct chmc * p ;
2005-04-17 02:20:36 +04:00
int bank_id ;
u64 raw_reg ;
int valid ;
int uk ;
int um ;
int lk ;
int lm ;
int interleave ;
unsigned long base ;
unsigned long size ;
} ;
2008-08-25 08:45:44 +04:00
struct chmc {
struct list_head list ;
int portid ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
struct chmc_obp_mem_layout layout_prop ;
int layout_size ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
void __iomem * regs ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
u64 timing_control1 ;
u64 timing_control2 ;
u64 timing_control3 ;
u64 timing_control4 ;
u64 memaddr_control ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
struct chmc_bank_info logical_banks [ CHMCTRL_NBANKS ] ;
2005-04-17 02:20:36 +04:00
} ;
2008-08-26 00:38:30 +04:00
# define JBUSMC_REGS_SIZE 8
# define JB_MC_REG1_DIMM2_BANK3 0x8000000000000000
# define JB_MC_REG1_DIMM1_BANK1 0x4000000000000000
# define JB_MC_REG1_DIMM2_BANK2 0x2000000000000000
# define JB_MC_REG1_DIMM1_BANK0 0x1000000000000000
# define JB_MC_REG1_XOR 0x0000010000000000
# define JB_MC_REG1_ADDR_GEN_2 0x000000e000000000
# define JB_MC_REG1_ADDR_GEN_2_SHIFT 37
# define JB_MC_REG1_ADDR_GEN_1 0x0000001c00000000
# define JB_MC_REG1_ADDR_GEN_1_SHIFT 34
# define JB_MC_REG1_INTERLEAVE 0x0000000001800000
# define JB_MC_REG1_INTERLEAVE_SHIFT 23
# define JB_MC_REG1_DIMM2_PTYPE 0x0000000000200000
# define JB_MC_REG1_DIMM2_PTYPE_SHIFT 21
# define JB_MC_REG1_DIMM1_PTYPE 0x0000000000100000
# define JB_MC_REG1_DIMM1_PTYPE_SHIFT 20
# define PART_TYPE_X8 0
# define PART_TYPE_X4 1
# define INTERLEAVE_NONE 0
# define INTERLEAVE_SAME 1
# define INTERLEAVE_INTERNAL 2
# define INTERLEAVE_BOTH 3
# define ADDR_GEN_128MB 0
# define ADDR_GEN_256MB 1
# define ADDR_GEN_512MB 2
# define ADDR_GEN_1GB 3
# define JB_NUM_DIMM_GROUPS 2
# define JB_NUM_DIMMS_PER_GROUP 2
# define JB_NUM_DIMMS (JB_NUM_DIMM_GROUPS * JB_NUM_DIMMS_PER_GROUP)
struct jbusmc_obp_map {
unsigned char dimm_map [ 18 ] ;
unsigned char pin_map [ 144 ] ;
} ;
struct jbusmc_obp_mem_layout {
/* One max 8-byte string label per DIMM. Usually
* this matches the label on the motherboard where
* that DIMM resides .
*/
char dimm_labels [ JB_NUM_DIMMS ] [ DIMM_LABEL_SZ ] ;
/* If symmetric use map[0], else it is
* asymmetric and map [ 1 ] should be used .
*/
char symmetric ;
struct jbusmc_obp_map map ;
char _pad ;
} ;
struct jbusmc_dimm_group {
struct jbusmc * controller ;
int index ;
u64 base_addr ;
u64 size ;
} ;
struct jbusmc {
void __iomem * regs ;
u64 mc_reg_1 ;
u32 portid ;
struct jbusmc_obp_mem_layout layout ;
int layout_len ;
int num_dimm_groups ;
struct jbusmc_dimm_group dimm_groups [ JB_NUM_DIMM_GROUPS ] ;
struct list_head list ;
} ;
static DEFINE_SPINLOCK ( mctrl_list_lock ) ;
2005-04-17 02:20:36 +04:00
static LIST_HEAD ( mctrl_list ) ;
2008-08-26 00:38:30 +04:00
static void mc_list_add ( struct list_head * list )
{
spin_lock ( & mctrl_list_lock ) ;
list_add ( list , & mctrl_list ) ;
spin_unlock ( & mctrl_list_lock ) ;
}
static void mc_list_del ( struct list_head * list )
{
spin_lock ( & mctrl_list_lock ) ;
list_del_init ( list ) ;
spin_unlock ( & mctrl_list_lock ) ;
}
# define SYNDROME_MIN -1
# define SYNDROME_MAX 144
/* Covert syndrome code into the way the bits are positioned
* on the bus .
*/
static int syndrome_to_qword_code ( int syndrome_code )
{
if ( syndrome_code < 128 )
syndrome_code + = 16 ;
else if ( syndrome_code < 128 + 9 )
syndrome_code - = ( 128 - 7 ) ;
else if ( syndrome_code < ( 128 + 9 + 3 ) )
syndrome_code - = ( 128 + 9 - 4 ) ;
else
syndrome_code - = ( 128 + 9 + 3 ) ;
return syndrome_code ;
}
/* All this magic has to do with how a cache line comes over the wire
* on Safari and JBUS . A 64 - bit line comes over in 1 or more quadword
* cycles , each of which transmit ECC / MTAG info as well as the actual
* data .
*/
# define L2_LINE_SIZE 64
# define L2_LINE_ADDR_MSK (L2_LINE_SIZE - 1)
# define QW_PER_LINE 4
# define QW_BYTES (L2_LINE_SIZE / QW_PER_LINE)
# define QW_BITS 144
# define SAFARI_LAST_BIT (576 - 1)
# define JBUS_LAST_BIT (144 - 1)
static void get_pin_and_dimm_str ( int syndrome_code , unsigned long paddr ,
int * pin_p , char * * dimm_str_p , void * _prop ,
int base_dimm_offset )
{
int qword_code = syndrome_to_qword_code ( syndrome_code ) ;
int cache_line_offset ;
int offset_inverse ;
int dimm_map_index ;
int map_val ;
if ( mc_type = = MC_TYPE_JBUS ) {
struct jbusmc_obp_mem_layout * p = _prop ;
/* JBUS */
cache_line_offset = qword_code ;
offset_inverse = ( JBUS_LAST_BIT - cache_line_offset ) ;
dimm_map_index = offset_inverse / 8 ;
map_val = p - > map . dimm_map [ dimm_map_index ] ;
map_val = ( ( map_val > > ( ( 7 - ( offset_inverse & 7 ) ) ) ) & 1 ) ;
* dimm_str_p = p - > dimm_labels [ base_dimm_offset + map_val ] ;
* pin_p = p - > map . pin_map [ cache_line_offset ] ;
} else {
struct chmc_obp_mem_layout * p = _prop ;
struct chmc_obp_map * mp ;
int qword ;
/* Safari */
if ( p - > symmetric )
mp = & p - > map [ 0 ] ;
else
mp = & p - > map [ 1 ] ;
qword = ( paddr & L2_LINE_ADDR_MSK ) / QW_BYTES ;
cache_line_offset = ( ( 3 - qword ) * QW_BITS ) + qword_code ;
offset_inverse = ( SAFARI_LAST_BIT - cache_line_offset ) ;
dimm_map_index = offset_inverse > > 2 ;
map_val = mp - > dimm_map [ dimm_map_index ] ;
map_val = ( ( map_val > > ( ( 3 - ( offset_inverse & 3 ) ) < < 1 ) ) & 0x3 ) ;
* dimm_str_p = p - > dimm_labels [ base_dimm_offset + map_val ] ;
* pin_p = mp - > pin_map [ cache_line_offset ] ;
}
}
static struct jbusmc_dimm_group * jbusmc_find_dimm_group ( unsigned long phys_addr )
{
struct jbusmc * p ;
list_for_each_entry ( p , & mctrl_list , list ) {
int i ;
for ( i = 0 ; i < p - > num_dimm_groups ; i + + ) {
struct jbusmc_dimm_group * dp = & p - > dimm_groups [ i ] ;
if ( phys_addr < dp - > base_addr | |
( dp - > base_addr + dp - > size ) < = phys_addr )
continue ;
return dp ;
}
}
return NULL ;
}
static int jbusmc_print_dimm ( int syndrome_code ,
unsigned long phys_addr ,
char * buf , int buflen )
{
struct jbusmc_obp_mem_layout * prop ;
struct jbusmc_dimm_group * dp ;
struct jbusmc * p ;
int first_dimm ;
dp = jbusmc_find_dimm_group ( phys_addr ) ;
if ( dp = = NULL | |
syndrome_code < SYNDROME_MIN | |
syndrome_code > SYNDROME_MAX ) {
buf [ 0 ] = ' ? ' ;
buf [ 1 ] = ' ? ' ;
buf [ 2 ] = ' ? ' ;
buf [ 3 ] = ' \0 ' ;
}
p = dp - > controller ;
prop = & p - > layout ;
first_dimm = dp - > index * JB_NUM_DIMMS_PER_GROUP ;
if ( syndrome_code ! = SYNDROME_MIN ) {
char * dimm_str ;
int pin ;
get_pin_and_dimm_str ( syndrome_code , phys_addr , & pin ,
& dimm_str , prop , first_dimm ) ;
sprintf ( buf , " %s, pin %3d " , dimm_str , pin ) ;
} else {
int dimm ;
/* Multi-bit error, we just dump out all the
* dimm labels associated with this dimm group .
*/
for ( dimm = 0 ; dimm < JB_NUM_DIMMS_PER_GROUP ; dimm + + ) {
sprintf ( buf , " %s " ,
prop - > dimm_labels [ first_dimm + dimm ] ) ;
buf + = strlen ( buf ) ;
}
}
return 0 ;
}
static u64 __devinit jbusmc_dimm_group_size ( u64 base ,
const struct linux_prom64_registers * mem_regs ,
int num_mem_regs )
{
u64 max = base + ( 8UL * 1024 * 1024 * 1024 ) ;
u64 max_seen = base ;
int i ;
for ( i = 0 ; i < num_mem_regs ; i + + ) {
const struct linux_prom64_registers * ent ;
u64 this_base ;
u64 this_end ;
ent = & mem_regs [ i ] ;
this_base = ent - > phys_addr ;
this_end = this_base + ent - > reg_size ;
if ( base < this_base | | base > = this_end )
continue ;
if ( this_end > max )
this_end = max ;
if ( this_end > max_seen )
max_seen = this_end ;
}
return max_seen - base ;
}
static void __devinit jbusmc_construct_one_dimm_group ( struct jbusmc * p ,
unsigned long index ,
const struct linux_prom64_registers * mem_regs ,
int num_mem_regs )
{
struct jbusmc_dimm_group * dp = & p - > dimm_groups [ index ] ;
dp - > controller = p ;
dp - > index = index ;
dp - > base_addr = ( p - > portid * ( 64UL * 1024 * 1024 * 1024 ) ) ;
dp - > base_addr + = ( index * ( 8UL * 1024 * 1024 * 1024 ) ) ;
dp - > size = jbusmc_dimm_group_size ( dp - > base_addr , mem_regs , num_mem_regs ) ;
}
static void __devinit jbusmc_construct_dimm_groups ( struct jbusmc * p ,
const struct linux_prom64_registers * mem_regs ,
int num_mem_regs )
{
if ( p - > mc_reg_1 & JB_MC_REG1_DIMM1_BANK0 ) {
jbusmc_construct_one_dimm_group ( p , 0 , mem_regs , num_mem_regs ) ;
p - > num_dimm_groups + + ;
}
if ( p - > mc_reg_1 & JB_MC_REG1_DIMM2_BANK2 ) {
jbusmc_construct_one_dimm_group ( p , 1 , mem_regs , num_mem_regs ) ;
p - > num_dimm_groups + + ;
}
}
static int __devinit jbusmc_probe ( struct of_device * op ,
const struct of_device_id * match )
{
const struct linux_prom64_registers * mem_regs ;
struct device_node * mem_node ;
int err , len , num_mem_regs ;
struct jbusmc * p ;
const u32 * prop ;
const void * ml ;
err = - ENODEV ;
mem_node = of_find_node_by_path ( " /memory " ) ;
if ( ! mem_node ) {
printk ( KERN_ERR PFX " Cannot find /memory node. \n " ) ;
goto out ;
}
mem_regs = of_get_property ( mem_node , " reg " , & len ) ;
if ( ! mem_regs ) {
printk ( KERN_ERR PFX " Cannot get reg property of /memory node. \n " ) ;
goto out ;
}
num_mem_regs = len / sizeof ( * mem_regs ) ;
err = - ENOMEM ;
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p ) {
printk ( KERN_ERR PFX " Cannot allocate struct jbusmc. \n " ) ;
goto out ;
}
INIT_LIST_HEAD ( & p - > list ) ;
err = - ENODEV ;
prop = of_get_property ( op - > node , " portid " , & len ) ;
if ( ! prop | | len ! = 4 ) {
printk ( KERN_ERR PFX " Cannot find portid. \n " ) ;
goto out_free ;
}
p - > portid = * prop ;
prop = of_get_property ( op - > node , " memory-control-register-1 " , & len ) ;
if ( ! prop | | len ! = 8 ) {
printk ( KERN_ERR PFX " Cannot get memory control register 1. \n " ) ;
goto out_free ;
}
p - > mc_reg_1 = ( ( u64 ) prop [ 0 ] < < 32 ) | ( u64 ) prop [ 1 ] ;
err = - ENOMEM ;
p - > regs = of_ioremap ( & op - > resource [ 0 ] , 0 , JBUSMC_REGS_SIZE , " jbusmc " ) ;
if ( ! p - > regs ) {
printk ( KERN_ERR PFX " Cannot map jbusmc regs. \n " ) ;
goto out_free ;
}
err = - ENODEV ;
ml = of_get_property ( op - > node , " memory-layout " , & p - > layout_len ) ;
if ( ! ml ) {
printk ( KERN_ERR PFX " Cannot get memory layout property. \n " ) ;
goto out_iounmap ;
}
if ( p - > layout_len > sizeof ( p - > layout ) ) {
printk ( KERN_ERR PFX " Unexpected memory-layout size %d \n " ,
p - > layout_len ) ;
goto out_iounmap ;
}
memcpy ( & p - > layout , ml , p - > layout_len ) ;
jbusmc_construct_dimm_groups ( p , mem_regs , num_mem_regs ) ;
mc_list_add ( & p - > list ) ;
printk ( KERN_INFO PFX " UltraSPARC-IIIi memory controller at %s \n " ,
op - > node - > full_name ) ;
dev_set_drvdata ( & op - > dev , p ) ;
err = 0 ;
out :
return err ;
out_iounmap :
of_iounmap ( & op - > resource [ 0 ] , p - > regs , JBUSMC_REGS_SIZE ) ;
out_free :
kfree ( p ) ;
goto out ;
}
2005-04-17 02:20:36 +04:00
/* Does BANK decode PHYS_ADDR? */
2008-08-25 08:45:44 +04:00
static int chmc_bank_match ( struct chmc_bank_info * bp , unsigned long phys_addr )
2005-04-17 02:20:36 +04:00
{
unsigned long upper_bits = ( phys_addr & PA_UPPER_BITS ) > > PA_UPPER_BITS_SHIFT ;
unsigned long lower_bits = ( phys_addr & PA_LOWER_BITS ) > > PA_LOWER_BITS_SHIFT ;
/* Bank must be enabled to match. */
if ( bp - > valid = = 0 )
return 0 ;
/* Would BANK match upper bits? */
upper_bits ^ = bp - > um ; /* What bits are different? */
upper_bits = ~ upper_bits ; /* Invert. */
upper_bits | = bp - > uk ; /* What bits don't matter for matching? */
upper_bits = ~ upper_bits ; /* Invert. */
if ( upper_bits )
return 0 ;
/* Would BANK match lower bits? */
lower_bits ^ = bp - > lm ; /* What bits are different? */
lower_bits = ~ lower_bits ; /* Invert. */
lower_bits | = bp - > lk ; /* What bits don't matter for matching? */
lower_bits = ~ lower_bits ; /* Invert. */
if ( lower_bits )
return 0 ;
/* I always knew you'd be the one. */
return 1 ;
}
/* Given PHYS_ADDR, search memory controller banks for a match. */
2008-08-25 08:45:44 +04:00
static struct chmc_bank_info * chmc_find_bank ( unsigned long phys_addr )
2005-04-17 02:20:36 +04:00
{
2008-08-26 00:38:30 +04:00
struct chmc * p ;
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
list_for_each_entry ( p , & mctrl_list , list ) {
2005-04-17 02:20:36 +04:00
int bank_no ;
for ( bank_no = 0 ; bank_no < CHMCTRL_NBANKS ; bank_no + + ) {
2008-08-25 08:45:44 +04:00
struct chmc_bank_info * bp ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
bp = & p - > logical_banks [ bank_no ] ;
if ( chmc_bank_match ( bp , phys_addr ) )
2005-04-17 02:20:36 +04:00
return bp ;
}
}
return NULL ;
}
/* This is the main purpose of this driver. */
2008-08-25 09:08:34 +04:00
static int chmc_print_dimm ( int syndrome_code ,
unsigned long phys_addr ,
char * buf , int buflen )
2005-04-17 02:20:36 +04:00
{
2008-08-25 08:45:44 +04:00
struct chmc_bank_info * bp ;
struct chmc_obp_mem_layout * prop ;
2005-04-17 02:20:36 +04:00
int bank_in_controller , first_dimm ;
2008-08-25 08:45:44 +04:00
bp = chmc_find_bank ( phys_addr ) ;
2005-04-17 02:20:36 +04:00
if ( bp = = NULL | |
syndrome_code < SYNDROME_MIN | |
syndrome_code > SYNDROME_MAX ) {
buf [ 0 ] = ' ? ' ;
buf [ 1 ] = ' ? ' ;
buf [ 2 ] = ' ? ' ;
buf [ 3 ] = ' \0 ' ;
return 0 ;
}
2008-08-25 08:45:44 +04:00
prop = & bp - > p - > layout_prop ;
2005-04-17 02:20:36 +04:00
bank_in_controller = bp - > bank_id & ( CHMCTRL_NBANKS - 1 ) ;
first_dimm = ( bank_in_controller & ( CHMCTRL_NDGRPS - 1 ) ) ;
first_dimm * = CHMCTRL_NDIMMS ;
if ( syndrome_code ! = SYNDROME_MIN ) {
2008-08-26 00:38:30 +04:00
char * dimm_str ;
int pin ;
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
get_pin_and_dimm_str ( syndrome_code , phys_addr , & pin ,
& dimm_str , prop , first_dimm ) ;
sprintf ( buf , " %s, pin %3d " , dimm_str , pin ) ;
2005-04-17 02:20:36 +04:00
} else {
int dimm ;
/* Multi-bit error, we just dump out all the
* dimm labels associated with this bank .
*/
for ( dimm = 0 ; dimm < CHMCTRL_NDIMMS ; dimm + + ) {
sprintf ( buf , " %s " ,
prop - > dimm_labels [ first_dimm + dimm ] ) ;
buf + = strlen ( buf ) ;
}
}
return 0 ;
}
/* Accessing the registers is slightly complicated. If you want
* to get at the memory controller which is on the same processor
* the code is executing , you must use special ASI load / store else
* you go through the global mapping .
*/
2008-08-25 08:45:44 +04:00
static u64 chmc_read_mcreg ( struct chmc * p , unsigned long offset )
2005-04-17 02:20:36 +04:00
{
2007-12-07 11:58:55 +03:00
unsigned long ret , this_cpu ;
preempt_disable ( ) ;
this_cpu = real_hard_smp_processor_id ( ) ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
if ( p - > portid = = this_cpu ) {
2005-04-17 02:20:36 +04:00
__asm__ __volatile__ ( " ldxa [%1] %2, %0 "
: " =r " ( ret )
: " r " ( offset ) , " i " ( ASI_MCU_CTRL_REG ) ) ;
} else {
__asm__ __volatile__ ( " ldxa [%1] %2, %0 "
: " =r " ( ret )
2008-08-25 08:45:44 +04:00
: " r " ( p - > regs + offset ) ,
2005-04-17 02:20:36 +04:00
" i " ( ASI_PHYS_BYPASS_EC_E ) ) ;
}
2007-12-07 11:58:55 +03:00
preempt_enable ( ) ;
2005-04-17 02:20:36 +04:00
return ret ;
}
#if 0 /* currently unused */
2008-08-25 08:45:44 +04:00
static void chmc_write_mcreg ( struct chmc * p , unsigned long offset , u64 val )
2005-04-17 02:20:36 +04:00
{
2008-08-25 08:45:44 +04:00
if ( p - > portid = = smp_processor_id ( ) ) {
2005-04-17 02:20:36 +04:00
__asm__ __volatile__ ( " stxa %0, [%1] %2 "
: : " r " ( val ) ,
" r " ( offset ) , " i " ( ASI_MCU_CTRL_REG ) ) ;
} else {
__asm__ __volatile__ ( " ldxa %0, [%1] %2 "
: : " r " ( val ) ,
2008-08-25 08:45:44 +04:00
" r " ( p - > regs + offset ) ,
2005-04-17 02:20:36 +04:00
" i " ( ASI_PHYS_BYPASS_EC_E ) ) ;
}
}
# endif
2008-08-25 08:45:44 +04:00
static void chmc_interpret_one_decode_reg ( struct chmc * p , int which_bank , u64 val )
2005-04-17 02:20:36 +04:00
{
2008-08-25 08:45:44 +04:00
struct chmc_bank_info * bp = & p - > logical_banks [ which_bank ] ;
bp - > p = p ;
bp - > bank_id = ( CHMCTRL_NBANKS * p - > portid ) + which_bank ;
bp - > raw_reg = val ;
bp - > valid = ( val & MEM_DECODE_VALID ) > > MEM_DECODE_VALID_SHIFT ;
bp - > uk = ( val & MEM_DECODE_UK ) > > MEM_DECODE_UK_SHIFT ;
bp - > um = ( val & MEM_DECODE_UM ) > > MEM_DECODE_UM_SHIFT ;
bp - > lk = ( val & MEM_DECODE_LK ) > > MEM_DECODE_LK_SHIFT ;
bp - > lm = ( val & MEM_DECODE_LM ) > > MEM_DECODE_LM_SHIFT ;
bp - > base = ( bp - > um ) ;
bp - > base & = ~ ( bp - > uk ) ;
bp - > base < < = PA_UPPER_BITS_SHIFT ;
switch ( bp - > lk ) {
2005-04-17 02:20:36 +04:00
case 0xf :
default :
2008-08-25 08:45:44 +04:00
bp - > interleave = 1 ;
2005-04-17 02:20:36 +04:00
break ;
case 0xe :
2008-08-25 08:45:44 +04:00
bp - > interleave = 2 ;
2005-04-17 02:20:36 +04:00
break ;
case 0xc :
2008-08-25 08:45:44 +04:00
bp - > interleave = 4 ;
2005-04-17 02:20:36 +04:00
break ;
case 0x8 :
2008-08-25 08:45:44 +04:00
bp - > interleave = 8 ;
2005-04-17 02:20:36 +04:00
break ;
case 0x0 :
2008-08-25 08:45:44 +04:00
bp - > interleave = 16 ;
2005-04-17 02:20:36 +04:00
break ;
} ;
/* UK[10] is reserved, and UK[11] is not set for the SDRAM
* bank size definition .
*/
2008-08-25 08:45:44 +04:00
bp - > size = ( ( ( unsigned long ) bp - > uk &
( ( 1UL < < 10UL ) - 1UL ) ) + 1UL ) < < PA_UPPER_BITS_SHIFT ;
bp - > size / = bp - > interleave ;
2005-04-17 02:20:36 +04:00
}
2008-08-25 08:45:44 +04:00
static void chmc_fetch_decode_regs ( struct chmc * p )
2005-04-17 02:20:36 +04:00
{
2008-08-25 08:45:44 +04:00
if ( p - > layout_size = = 0 )
2005-04-17 02:20:36 +04:00
return ;
2008-08-25 08:45:44 +04:00
chmc_interpret_one_decode_reg ( p , 0 ,
chmc_read_mcreg ( p , CHMCTRL_DECODE1 ) ) ;
chmc_interpret_one_decode_reg ( p , 1 ,
chmc_read_mcreg ( p , CHMCTRL_DECODE2 ) ) ;
chmc_interpret_one_decode_reg ( p , 2 ,
chmc_read_mcreg ( p , CHMCTRL_DECODE3 ) ) ;
chmc_interpret_one_decode_reg ( p , 3 ,
chmc_read_mcreg ( p , CHMCTRL_DECODE4 ) ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-25 08:32:42 +04:00
static int __devinit chmc_probe ( struct of_device * op ,
const struct of_device_id * match )
2005-04-17 02:20:36 +04:00
{
2008-08-25 08:32:42 +04:00
struct device_node * dp = op - > node ;
unsigned long ver ;
2007-04-24 02:53:27 +04:00
const void * pval ;
2008-08-25 08:32:42 +04:00
int len , portid ;
2008-08-25 08:45:44 +04:00
struct chmc * p ;
int err ;
2008-08-25 08:32:42 +04:00
2008-08-25 08:45:44 +04:00
err = - ENODEV ;
2008-08-25 08:32:42 +04:00
__asm__ ( " rdpr %%ver, %0 " : " =r " ( ver ) ) ;
if ( ( ver > > 32UL ) = = __JALAPENO_ID | |
( ver > > 32UL ) = = __SERRANO_ID )
2008-08-25 08:45:44 +04:00
goto out ;
2008-08-25 08:32:42 +04:00
portid = of_getintprop_default ( dp , " portid " , - 1 ) ;
2005-04-17 02:20:36 +04:00
if ( portid = = - 1 )
2008-08-25 08:45:44 +04:00
goto out ;
2005-04-17 02:20:36 +04:00
2006-06-23 07:04:30 +04:00
pval = of_get_property ( dp , " memory-layout " , & len ) ;
2008-08-25 08:45:44 +04:00
if ( pval & & len > sizeof ( p - > layout_prop ) ) {
printk ( KERN_ERR PFX " Unexpected memory-layout property "
" size %d. \n " , len ) ;
goto out ;
}
err = - ENOMEM ;
p = kzalloc ( sizeof ( * p ) , GFP_KERNEL ) ;
if ( ! p ) {
printk ( KERN_ERR PFX " Could not allocate struct chmc. \n " ) ;
goto out ;
2006-06-23 07:04:30 +04:00
}
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
p - > portid = portid ;
p - > layout_size = len ;
if ( ! pval )
p - > layout_size = 0 ;
else
memcpy ( & p - > layout_prop , pval , len ) ;
p - > regs = of_ioremap ( & op - > resource [ 0 ] , 0 , 0x48 , " chmc " ) ;
if ( ! p - > regs ) {
2008-08-25 08:32:42 +04:00
printk ( KERN_ERR PFX " Could not map registers. \n " ) ;
2008-08-25 08:45:44 +04:00
goto out_free ;
2008-08-25 08:32:42 +04:00
}
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
if ( p - > layout_size ! = 0UL ) {
p - > timing_control1 = chmc_read_mcreg ( p , CHMCTRL_TCTRL1 ) ;
p - > timing_control2 = chmc_read_mcreg ( p , CHMCTRL_TCTRL2 ) ;
p - > timing_control3 = chmc_read_mcreg ( p , CHMCTRL_TCTRL3 ) ;
p - > timing_control4 = chmc_read_mcreg ( p , CHMCTRL_TCTRL4 ) ;
p - > memaddr_control = chmc_read_mcreg ( p , CHMCTRL_MACTRL ) ;
2005-04-17 02:20:36 +04:00
}
2008-08-25 08:45:44 +04:00
chmc_fetch_decode_regs ( p ) ;
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
mc_list_add ( & p - > list ) ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:32:42 +04:00
printk ( KERN_INFO PFX " UltraSPARC-III memory controller at %s [%s] \n " ,
2006-06-23 07:04:30 +04:00
dp - > full_name ,
2008-08-25 08:45:44 +04:00
( p - > layout_size ? " ACTIVE " : " INACTIVE " ) ) ;
2008-08-25 08:32:42 +04:00
2008-08-25 08:45:44 +04:00
dev_set_drvdata ( & op - > dev , p ) ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
err = 0 ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
out :
return err ;
out_free :
kfree ( p ) ;
goto out ;
2005-04-17 02:20:36 +04:00
}
2008-08-26 00:38:30 +04:00
static int __devinit us3mc_probe ( struct of_device * op ,
const struct of_device_id * match )
{
if ( mc_type = = MC_TYPE_SAFARI )
return chmc_probe ( op , match ) ;
else if ( mc_type = = MC_TYPE_JBUS )
return jbusmc_probe ( op , match ) ;
return - ENODEV ;
}
static void __devexit chmc_destroy ( struct of_device * op , struct chmc * p )
{
list_del ( & p - > list ) ;
of_iounmap ( & op - > resource [ 0 ] , p - > regs , 0x48 ) ;
kfree ( p ) ;
}
static void __devexit jbusmc_destroy ( struct of_device * op , struct jbusmc * p )
2005-04-17 02:20:36 +04:00
{
2008-08-26 00:38:30 +04:00
mc_list_del ( & p - > list ) ;
of_iounmap ( & op - > resource [ 0 ] , p - > regs , JBUSMC_REGS_SIZE ) ;
kfree ( p ) ;
}
static int __devexit us3mc_remove ( struct of_device * op )
{
void * p = dev_get_drvdata ( & op - > dev ) ;
2005-04-17 02:20:36 +04:00
2008-08-25 08:45:44 +04:00
if ( p ) {
2008-08-26 00:38:30 +04:00
if ( mc_type = = MC_TYPE_SAFARI )
chmc_destroy ( op , p ) ;
else if ( mc_type = = MC_TYPE_JBUS )
jbusmc_destroy ( op , p ) ;
2008-08-25 08:32:42 +04:00
}
return 0 ;
}
2005-04-17 02:20:36 +04:00
2008-08-31 12:23:17 +04:00
static const struct of_device_id us3mc_match [ ] = {
2008-08-25 08:32:42 +04:00
{
. name = " memory-controller " ,
} ,
{ } ,
} ;
2008-08-26 00:38:30 +04:00
MODULE_DEVICE_TABLE ( of , us3mc_match ) ;
2006-06-23 07:04:30 +04:00
2008-08-26 00:38:30 +04:00
static struct of_platform_driver us3mc_driver = {
. name = " us3mc " ,
. match_table = us3mc_match ,
. probe = us3mc_probe ,
. remove = __devexit_p ( us3mc_remove ) ,
2008-08-25 08:32:42 +04:00
} ;
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
static inline bool us3mc_platform ( void )
2008-08-25 08:32:42 +04:00
{
if ( tlb_type = = cheetah | | tlb_type = = cheetah_plus )
return true ;
return false ;
2005-04-17 02:20:36 +04:00
}
2008-08-26 00:38:30 +04:00
static int __init us3mc_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-08-26 00:38:30 +04:00
unsigned long ver ;
2008-08-25 09:08:34 +04:00
int ret ;
2008-08-26 00:38:30 +04:00
if ( ! us3mc_platform ( ) )
2008-08-25 08:32:42 +04:00
return - ENODEV ;
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
__asm__ ( " rdpr %%ver, %0 " : " =r " ( ver ) ) ;
if ( ( ver > > 32UL ) = = __JALAPENO_ID | |
( ver > > 32UL ) = = __SERRANO_ID ) {
mc_type = MC_TYPE_JBUS ;
us3mc_dimm_printer = jbusmc_print_dimm ;
} else {
mc_type = MC_TYPE_SAFARI ;
us3mc_dimm_printer = chmc_print_dimm ;
}
ret = register_dimm_printer ( us3mc_dimm_printer ) ;
2008-08-25 09:08:34 +04:00
if ( ! ret ) {
2008-08-26 00:38:30 +04:00
ret = of_register_driver ( & us3mc_driver , & of_bus_type ) ;
2008-08-25 09:08:34 +04:00
if ( ret )
2008-08-26 00:38:30 +04:00
unregister_dimm_printer ( us3mc_dimm_printer ) ;
2008-08-25 09:08:34 +04:00
}
return ret ;
2008-08-25 08:32:42 +04:00
}
2005-04-17 02:20:36 +04:00
2008-08-26 00:38:30 +04:00
static void __exit us3mc_cleanup ( void )
2008-08-25 08:32:42 +04:00
{
2008-08-26 00:38:30 +04:00
if ( us3mc_platform ( ) ) {
unregister_dimm_printer ( us3mc_dimm_printer ) ;
of_unregister_driver ( & us3mc_driver ) ;
2008-08-25 09:08:34 +04:00
}
2005-04-17 02:20:36 +04:00
}
2008-08-26 00:38:30 +04:00
module_init ( us3mc_init ) ;
module_exit ( us3mc_cleanup ) ;