// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: ncu.cc
// Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
// DO NOT ALTER OR REMOVE COPYRIGHT NOTICES.
// The above named program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License version 2 as published by the Free Software Foundation.
// The above named 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.
// You should have received a copy of the GNU General Public
// License along with this work; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
// ========== Copyright Header End ============================================
static const char *n2ncu_help
= "SAM N2 NCU module\n\
sysconf n2_ncu <instance_name> \n\
For UI help type <instance name> \n\
For module specific info type \"modinfo <instance name>\"";
static int access_ncu(uint32_t cpuid
, void* obj
, uint64_t paddr
, mmi_bool_t wr
, uint32_t size
, uint64_t* buf
, uint8_t bytemask
){
n2Ncu
* ncu
= (n2Ncu
*)obj
;
return ncu
->access_regs(cpuid
,paddr
,wr
,size
,buf
,bytemask
);
int n2ncu_ui_cmd(void * obj
, int argc
, char * argv
[]){
n2Ncu
* i
= (n2Ncu
*)obj
;
i
->handle_ui(argc
, argv
);
const uint64_t n2Ncu::dumpRegsV1
[25] = {
PCIE_A_MEM32_OFFSET_BASE
,
PCIE_A_MEM32_OFFSET_MASK
,
PCIE_A_MEM64_OFFSET_BASE
,
PCIE_A_MEM64_OFFSET_MASK
,
PCIE_A_IOCON_OFFSET_BASE
,
PCIE_A_IOCON_OFFSET_MASK
,
const char * Module::get_help_string(){
Module
* Module::create(const char *_modname
, const char *_instance_name
){
return new n2Ncu(_modname
, _instance_name
);
n2Ncu::n2Ncu(const char *_modname
, const char *_instance_name
)
: Module(_modname
, _instance_name
){
mmi_register_instance_cmd(getInstance(),n2ncu_help
,n2ncu_ui_cmd
);
current_dump_version
= strdup("v1.0");
//pthread_mutex_init(&ncu_lock, NULL);
* setup init value (NCU spec, v0.99)
for (device
=0; device
< NCU_DEV_MAX
; device
++) {
regs
.int_man
[device
] = 0x0;
regs
.mondo_int_vec
= 0x0;
regs
.ser_num
= 0xdeadbeef;
regs
.efu_stat
= MASK64(63,0);
regs
.bank_enb_stat
= 0x3cf;
regs
.l2_idx_hash_en_stat
= false;
regs
.pcie_a_mem32_offset_base
= 0x0;
regs
.pcie_a_mem32_offset_mask
= MASK64(39,36);
regs
.pcie_a_mem64_offset_base
= 0x0;
regs
.pcie_a_mem64_offset_mask
= MASK64(39,36);
regs
.pcie_a_iocon_offset_base
= 0x0;
regs
.pcie_a_iocon_offset_mask
= MASK64(39,36);
regs
.soc_log_enb
= 0x1fffffff;
regs
.soc_err_inject
= 0x0;
regs
.soc_fatal_enb
= 0x0;
regs
.soc_sii_err_syndrome
= 0x0;
regs
.soc_ncu_err_syndrome
= 0x0;
for (i
= 0; i
< NCU_TARGETS
; i
++) {
regs
.mondo_int_data0
[i
] = 0x0;
regs
.mondo_int_data1
[i
] = 0x0;
regs
.mondo_int_busy
[i
] = 0x0; //MASK64(6,6);
// support 8 byte reads/writes of registers.
int n2Ncu::access_regs(uint32_t cpuid
, uint64_t paddr
, mmi_bool_t wr
, uint32_t size
,\
uint64_t* buf
, uint8_t bytemask
){
debug_err("%s ERROR: %s access of size %d at addr %llx\n", \
getName(),wr
?"WITE":"READ",size
,paddr
);
debug_err("%s ERROR: unaligned %s access of size %d at addr %llx\n", \
getName(),wr
?"WITE":"READ",size
,paddr
);
uint64_t reg
= paddr
& NCU_REG_MASK
;
reg
= reg
& NCU_INT_MAN_MASK
;
if (reg
>= MONDO_INT_DATA0
) {
if (UINT64_RANGE_CHECK(MONDO_INT_DATA0
, reg
, MONDO_INT_DATA1
))
if (UINT64_RANGE_CHECK(MONDO_INT_DATA1
, reg
, MONDO_INT_ADATA0
))
if (UINT64_RANGE_CHECK(MONDO_INT_ADATA0
, reg
, MONDO_INT_ADATA1
))
if (UINT64_RANGE_CHECK(MONDO_INT_ADATA1
, reg
, MONDO_INT_BUSY
))
if (UINT64_RANGE_CHECK(MONDO_INT_BUSY
, reg
, MONDO_INT_ABUSY
))
//pthread_mutex_lock( &ncu_lock );
case true: //8 byte write
idx
= (paddr
>> 3) & (NCU_DEV_MAX
-1);
ASSIGN_NCU(regs
.int_man
[idx
],MASK64(13,8)|MASK64(5,0),val
,reg
);
ASSIGN_NCU(regs
.mondo_int_vec
, MASK64(5,0), val
, reg
);
case L2_IDX_HASH_EN_STATUS
:
debug_err("%s: attempted write to RO register\n \
Write 0x%llx to register %s (offset 0x%llx\n",\
getName(),val
, ncu_reg_name(reg
), reg
);
case PCIE_A_MEM32_OFFSET_BASE
:
ASSIGN_NCU( regs
.pcie_a_mem32_offset_base
, MASK64(63,63)|MASK64(35,24), val
, reg
);
niagara2_pcie_mapping(PIU_REGION_MEM32
);
case PCIE_A_MEM32_OFFSET_MASK
:
ASSIGN_NCU( regs
.pcie_a_mem32_offset_mask
, MASK64(39,24), val
, reg
);
niagara2_pcie_mapping(PIU_REGION_MEM32
);
case PCIE_A_MEM64_OFFSET_BASE
:
ASSIGN_NCU( regs
.pcie_a_mem64_offset_base
, MASK64(63,63)|MASK64(35,24), val
, reg
);
niagara2_pcie_mapping(PIU_REGION_MEM64
);
case PCIE_A_MEM64_OFFSET_MASK
:
ASSIGN_NCU( regs
.pcie_a_mem64_offset_mask
, MASK64(39,24), val
, reg
);
niagara2_pcie_mapping(PIU_REGION_MEM64
);
case PCIE_A_IOCON_OFFSET_BASE
:
ASSIGN_NCU( regs
.pcie_a_iocon_offset_base
, MASK64(63,63)|MASK64(35,24), val
, reg
);
niagara2_pcie_mapping(PIU_REGION_CFGIO
);
case PCIE_A_IOCON_OFFSET_MASK
:
ASSIGN_NCU( regs
.pcie_a_iocon_offset_mask
, MASK64(39,24) ,val
, reg
);
niagara2_pcie_mapping(PIU_REGION_CFGIO
);
debug_more("%s: register %s (offset 0x%llx) not implemented\n", \
getName(),ncu_reg_name(reg
), reg
);
ASSIGN_NCU( regs
.pcie_a_fsh
,~0ULL, val
, reg
);
ASSIGN_NCU( regs
.soc_esr
, MASK64(63,63)|NCU_SOC_MASK
, val
, reg
);
ASSIGN_NCU( regs
.soc_log_enb
, MASK64(42,0) , val
, reg
);
case SOC_INTERRUPT_ENABLE
:
ASSIGN_NCU( regs
.soc_intr_enb
, MASK64(42,0), val
, reg
);
case SOC_ERROR_INJECTION
:
ASSIGN_NCU( regs
.soc_err_inject
, MASK64(42,0), val
, reg
);
case SOC_FATAL_ERROR_ENABLE
:
ASSIGN_NCU( regs
.soc_fatal_enb
, MASK64(42,0), val
, reg
);
case SOC_PENDING_ERROR_STATUS
:
ASSIGN_NCU( regs
.soc_esr
, MASK64(63,63)|NCU_SOC_MASK
, val
, reg
);
case SOC_SII_ERROR_SYNDROME
:
ASSIGN_NCU( regs
.soc_sii_err_syndrome
, MASK64(63,63)|MASK64(58,0), val
, reg
);
case SOC_NCU_ERROR_SYNDROME
:
ASSIGN_NCU( regs
.soc_ncu_err_syndrome
, MASK64(63,58)|MASK64(55,0), val
, reg
);
target
= (paddr
>> 3) & (NCU_TARGETS
-1);
ASSIGN_NCU( regs
.mondo_int_busy
[target
], MASK64(6,6), val
, reg
);
ASSIGN_NCU( regs
.mondo_int_busy
[target
], MASK64(6,6), val
, reg
);
debug_err("%s: ERROR unknow register access at offset %llx\n",getName(),reg
);
//pthread_mutex_unlock( &ncu_lock );
//debug_more("%s: write at register %s (offset %llx)\n", getName(), ncu_reg_name(reg), val);
//pthread_mutex_unlock( &ncu_lock );
case false: // 8 byte read
idx
= (paddr
>> 3) & (NCU_DEV_MAX
-1);
val
= regs
.mondo_int_vec
;
// XXX this ro register becomes a big bottleneck as the cpu
// idleloop continuously reads this register. Remove the
// locks to reduce contention
// XXX need to access the ASI register to provide access.
debug_err("%s: ERROR:unimplemented reg read at %s",getName(),ncu_reg_name(reg
));
val
= regs
.bank_enb_stat
;
case PCIE_A_MEM32_OFFSET_BASE
:
val
= regs
.pcie_a_mem32_offset_base
;
case PCIE_A_MEM32_OFFSET_MASK
:
val
= regs
.pcie_a_mem32_offset_mask
;
case PCIE_A_MEM64_OFFSET_BASE
:
val
= regs
.pcie_a_mem64_offset_base
;
case PCIE_A_MEM64_OFFSET_MASK
:
val
= regs
.pcie_a_mem64_offset_mask
;
case PCIE_A_IOCON_OFFSET_BASE
:
val
= regs
.pcie_a_iocon_offset_base
;
case PCIE_A_IOCON_OFFSET_MASK
:
val
= regs
.pcie_a_iocon_offset_mask
;
case L2_IDX_HASH_EN_STATUS
:
debug_more("%s:register %s (offset 0x%llx) not implemented\n",
getName(), ncu_reg_name(reg
), reg
);
case SOC_INTERRUPT_ENABLE
:
case SOC_ERROR_INJECTION
:
val
= regs
.soc_err_inject
;
case SOC_FATAL_ERROR_ENABLE
:
val
= regs
.soc_fatal_enb
;
case SOC_PENDING_ERROR_STATUS
:
case SOC_SII_ERROR_SYNDROME
:
val
= regs
.soc_sii_err_syndrome
;
case SOC_NCU_ERROR_SYNDROME
:
val
= regs
.soc_ncu_err_syndrome
;
int target
= (paddr
>> 3) & (NCU_TARGETS
-1);
val
= regs
.mondo_int_data0
[target
];
int target
= (paddr
>> 3) & (NCU_TARGETS
-1);
val
= regs
.mondo_int_data1
[target
];
val
= regs
.mondo_int_data0
[target
];
val
= regs
.mondo_int_data1
[target
];
int target
= (paddr
>> 3) & (NCU_TARGETS
-1);
val
= regs
.mondo_int_busy
[target
];
val
= regs
.mondo_int_busy
[target
];
debug_err("%s: ERROR unknow read register access at offset %llx\n",getName(),reg
);
//pthread_mutex_unlock( &ncu_lock );
debug_more("%s: read register %s (value 0x%llx)\n", getName(), ncu_reg_name(reg
), val
);
//pthread_mutex_unlock( &ncu_lock );
//pthread_mutex_unlock( &ncu_lock );
* Create address mapping to access PCIE Cfg/IO, MEM32 and MEM64 space
void n2Ncu::niagara2_pcie_mapping(piu_region_t region
){
uint64_t base
, mask
, size
;
const char *name
[3] = {"Cfg/IO", "Mem32", "Mem64"};
base
= regs
.pcie_a_iocon_offset_base
;
mask
= regs
.pcie_a_iocon_offset_mask
;
base
= regs
.pcie_a_mem32_offset_base
;
mask
= regs
.pcie_a_mem32_offset_mask
;
base
= regs
.pcie_a_mem64_offset_base
;
mask
= regs
.pcie_a_mem64_offset_mask
;
enable
= GETMASK64(base
,63,63);
base
&= PIU_REGION_OFFSET_MASK
;
mask
&= PIU_REGION_OFFSET_MASK
;
size
= ~(MASK64(63,36)|mask
) + 1;
map
[region
].enable
= enable
;
debug_more("%s:PCIE %s is mapped at 0x%llx - 0x%llx\n", \
getName(),name
[region
],base
,base
+size
-1);
bool n2Ncu::dump_v1(FILE * fp
){
const char * dump_version
= "v1.0";
fwrite(dump_version
,strlen(dump_version
),1,fp
);
fwrite("\n",strlen("\n"),1,fp
);
for(int i
= 0; i
< (sizeof(dumpRegsV1
)/sizeof(uint64_t)); i
++ ){
char buf
[64 * 1024], tcptr
[64 * 1024];
sprintf(buf
,"%-50s0x%-8llx ",ncu_reg_name(dumpRegsV1
[i
]),dumpRegsV1
[i
]);
uint64_t base
= dumpRegsV1
[i
];
for(int j
= 0; j
< NCU_DEV_MAX
; j
++){
access_regs(0,base
+ j
*8, mmi_false
, 8, &val
,0xff);
sprintf(tcptr
, "0x%04x 0x%016llx,",j
,val
);
for(int j
= 0; j
< NCU_TARGETS
; j
++){
access_regs(0,base
+ j
*8, mmi_false
, 8, &val
,0xff);
sprintf(tcptr
, "0x%04x 0x%016llx,",j
,val
);
access_regs(0,base
,mmi_false
,8,&val
,0xff);
sprintf(tcptr
, "0x%016llx,",val
);
fwrite(buf
,strlen(buf
),1,fp
);
fwrite("\n",strlen("\n"),1,fp
);
bool n2Ncu::dump(FILE * fp
){
if(!strcmp(current_dump_version
,"v1.0"))
bool n2Ncu::restore(FILE * fp
){
const int bufsize
= 64 * 1024;
printf("%s restore: dump version mismatch, restore failed\n",getName());
// if the CSR offsets change, this function may need change as well
bool n2Ncu::restore_v1(FILE * fp
){
const int bufsize
= 64 * 1024;
while(fgets(buf
,bufsize
,fp
)){
strtok(buf
," "); // csr name, ignore
const char * csr_offset
= strtok(0," "); // csr offset
uint64_t offset
= strtoull(csr_offset
,0,0);
for(int j
= 0; j
< NCU_DEV_MAX
; j
++){
const char * csr_index
= strtok(0," ");
uint64_t index
= strtoull(csr_index
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.int_man
[index
] = val
;
for(int j
= 0; j
< NCU_TARGETS
; j
++){
const char * csr_index
= strtok(0," ");
uint64_t index
= strtoull(csr_index
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.mondo_int_data0
[index
] = val
;
for(int j
= 0; j
< NCU_TARGETS
; j
++){
const char * csr_index
= strtok(0," ");
uint64_t index
= strtoull(csr_index
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.mondo_int_data1
[index
] = val
;
for(int j
= 0; j
< NCU_TARGETS
; j
++){
const char * csr_index
= strtok(0," ");
uint64_t index
= strtoull(csr_index
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.mondo_int_busy
[index
] = val
;
case PCIE_A_MEM32_OFFSET_BASE
:
case PCIE_A_MEM32_OFFSET_MASK
:
case PCIE_A_MEM64_OFFSET_BASE
:
case PCIE_A_MEM64_OFFSET_MASK
:
case PCIE_A_IOCON_OFFSET_BASE
:
case PCIE_A_IOCON_OFFSET_MASK
:
case SOC_INTERRUPT_ENABLE
:
case SOC_ERROR_INJECTION
:
case SOC_FATAL_ERROR_ENABLE
:
case SOC_SII_ERROR_SYNDROME
:
case SOC_NCU_ERROR_SYNDROME
:
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
access_regs(0,offset
,mmi_true
,8,&val
,0xff);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.bank_enb_stat
= val
;
case L2_IDX_HASH_EN_STATUS
:
const char * csr_val
= strtok(0,",");
uint64_t val
= strtoull(csr_val
,0,0);
regs
.l2_idx_hash_en_stat
= val
;
void n2Ncu::handle_ui(int argc
, char * argv
[]){
}else if(!strcmp(argv
[1],"dump")){
printf("%s dump: error opening file <%s>\n",getName(),argv
[2]);
}else if(!strcmp(argv
[1],"restore")){
printf("%s restore: error opening file <%s>\n",getName(),argv
[2]);
printf("%s restore: no restore filename specified\n",getName());
}else if(!strcmp(argv
[1],"debug")){
debug_level
= atoi(argv
[2]);
printf("%s: set debug level to %d\n",getName(),debug_level
);
printf("%s: current debug level %d\n",getName(),debug_level
);
debug_err("%s: unsupported UI command <%s>\n",getName(),argv
[1]);
const char *n2Ncu::ncu_reg_name(uint64_t reg
){
case INT_MAN
: return "int_man";
case MONDO_INT_VEC
: return "mondo_int_vec";
case SER_NUM
: return "ser_num";
case EFU_STAT
: return "efu_stat";
case CORE_AVAIL
: return "core_avail";
case BANK_AVAIL
: return "bank_avail";
case BANK_ENABLE
: return "bank_enable";
case BANK_ENABLE_STATUS
: return "bank_enable_status";
case L2_IDX_HASH_EN
: return "l2_idx_hash_en";
case L2_IDX_HASH_EN_STATUS
: return "l2_idx_hash_en_status";
case PCIE_A_MEM32_OFFSET_BASE
: return "pcie_a_mem32_offset_base";
case PCIE_A_MEM32_OFFSET_MASK
: return "pcie_a_mem32_offset_mask";
case PCIE_A_MEM64_OFFSET_BASE
: return "pcie_a_mem64_offset_base";
case PCIE_A_MEM64_OFFSET_MASK
: return "pcie_a_mem64_offset_mask";
case PCIE_A_IOCON_OFFSET_BASE
: return "pcie_a_iocon_offset_base";
case PCIE_A_IOCON_OFFSET_MASK
: return "pcie_a_iocon_offset_mask";
case PCIE_A_FSH
: return "pcie_a_fsh";
case SOC_ESR
: return "soc_error_status";
case SOC_LOG_ENABLE
: return "soc_error_log_enable";
case SOC_INTERRUPT_ENABLE
: return "soc_error_interrupt_enable";
case SOC_FATAL_ERROR_ENABLE
: return "soc_fatal_error_enable";
case SOC_PENDING_ERROR_STATUS
: return "soc_pending_error_status";
case SOC_ERROR_INJECTION
: return "soc_error_injection";
case SOC_SII_ERROR_SYNDROME
: return "soc_sii_error_syndrome";
case SOC_NCU_ERROR_SYNDROME
: return "soc_sii_error_syndrome";
case MONDO_INT_DATA0
: return "mondo_int_data0";
case MONDO_INT_DATA1
: return "mondo_int_data1";
case MONDO_INT_ADATA0
: return "mondo_int_adata0";
case MONDO_INT_ADATA1
: return "mondo_int_adata1";
case MONDO_INT_BUSY
: return "mondo_int_busy";
case MONDO_INT_ABUSY
: return "mondo_int_abusy";
default: return "Illegal NCU register";