Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / n2_piu / sam_piu.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: sam_piu.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 ============================================
#include "sam_piu.h"
#include "dev_registry.h"
extern devRegistry * samDevs;
static const char *n2piu_help = "SAM N2 NCU module\n\
Sysconf format is :\n\
sysconf n2_piu <instance_name> ncu=<ncu name>\n\
For UI help type <instance name>\n\
For module specific info type \"modinfo <instance name>\"";
static int access_piu(uint32_t cpuid, void* obj, uint64_t paddr, mmi_bool_t wr, \
uint32_t size, uint64_t* buf, uint8_t bytemask){
samPiu * sp = (samPiu *)obj;
return sp->handlePio(cpuid,paddr,wr,size,buf,bytemask);
}
int n2piu_ui_cmd(void * obj, int argc, char * argv[]){
samPiu * i = (samPiu *)obj;
i->handle_ui(argc, argv);
return 0;
}
Module * Module::create(const char *_modname, const char *_instance_name){
return new samPiu(_modname, _instance_name);
}
samPiu::samPiu(const char *_modname, const char *_instance_name)
: Module(_modname, _instance_name){
piuModel.proc_type_namep = strdup("n2");
piuModel.config_devp = 0;
piuModel.config_procp = 0;
piuModel.sam_piu = (void*)this;
/*
* init PIU CSR with power on reset
*/
piu_init_csr(&piuModel);
/*
* init error lookup table
*/
piu_init_error_list();
primary_bus_no = 0;
secondary_bus_no = 0;
subordinate_bus_no = 0xff;
device = 0;
function = 0;
bus = ncu = 0;
busMod = ncuMod = 0;
busIf = 0;
ncuIf = 0;
N2_PIU_VERSION = strdup("1.0");
for(int i = 0; i < 64; i++)
pendingIntr[i] = false;
assert(pthread_mutex_init(&intxMutex,NULL)==0);
assert(pthread_mutex_init(&pendIntMutex,NULL)==0);
assert(pthread_mutex_init(&piuMutex,NULL)==0);
for (int i = 0; i< 32; i++)
assert(pthread_mutex_init(&msiMutex[i],NULL)==0);
mmi_register_instance_cmd(getInstance(),n2piu_help,n2piu_ui_cmd);
assert(pthread_mutex_init(&ncuMutex,NULL)==0);
event_fire_time = 0;
}
const char * Module::get_help_string(){
return n2piu_help;
}
const char * samPiu::get_help(){
return Module::get_help_string();
}
const char * samPiu::get_version(){
return N2_PIU_VERSION;
}
void samPiu::modinfo(){
printf("SAM-Legion PIU module\n");
printf("IOMAP a) base<%llx> size<%llx>\n",PIU_CSR_BASE,PIU_CSR_SIZE);
printf(" b) base<%llx> size<%llx>\n",PIU_PCIE_BASE,PIU_PCIE_SIZE);
printf("pri_bus <%d>, sec_bus<%d>, device<%d>, function<%d>\n",\
primary_bus_no, secondary_bus_no, device, function );
return;
}
bool samPiu::parse_arg(const char *arg){
if (argval("bus", arg, &bus)){
debug_more("%s: bus = %s\n", instance_name, bus);
return true;
}else if (argval("ncu", arg, &ncu)){
debug_more("%s: ncu = %s\n", instance_name, ncu);
return true;
}
return false;
}
bool samPiu::check_args(){
if(bus == 0){
debug_err("%s: ERROR: must specify downstream pcie bus name\n",getName());
return false;
}
return true;
}
void samPiu::module_added(mmi_instance_t i, const char *target_name){
if(bus && !strcmp(target_name,bus)){
busMod = i;
busIf = (pcieBusIf*)mmi_get_interface(i,PCIE_BUS_INTERFACE);
if(busIf == 0){
debug_err("%s: FATAL ERROR, could not get bus interface for %s\n",getName,bus);
exit(0);
}
busIf->setBridgeIf((genericPcieDevIf*)this);
}else if(ncu && !strcmp(ncu,target_name)){
ncuMod = i;
ncuIf = (n2Ncu*)mmi_get_interface(i,"ncu interface");
if(!ncuIf){
debug_err("%s: FATAL ERROR, could not get ncu interface for %s\n",getName,ncu);
exit(0);
}
}
return;
}
void samPiu::module_deleted(mmi_instance_t i, const char *target_name){
if(bus && !strcmp(bus,target_name)){
busMod = 0;
busIf = 0;
bus = 0;
}
return;
}
void *samPiu::get_interface(const char *name){
// no one should be calling this interface, since the bus's bridge interface
// is set by the bridge itself (for PIU in module_added)
if( !strcmp(name,GENERIC_PCIE_DEV_INTERFACE) )
return (genericPcieDevIf*)this;
return 0;
}
void samPiu::init_done(){
mmi_map_physio(PIU_CSR_BASE,PIU_CSR_SIZE,(void*)this,access_piu);
mmi_map_physio(PIU_PCIE_BASE,PIU_PCIE_SIZE,(void*)this,access_piu);
if(!restore_v5_dump()){
mmi_register_event(1000,samPiu_1ms_callback,(void*)this,0);
event_fire_time = 1000;
}
return;
}
void samPiu::timer_callback(){
// register a callback that fires every 10ms. Looks for any nacked
// interrupt by NCU and sends it if one exists. If the interrupt is
// nacked again, try it again later.
pthread_mutex_lock(&ncuMutex);
if(!irql.empty()){
VCPU_InterruptRequest * ir = irql.front();
int stat = ncuIf->sendPiuMondo(ir);
if(stat == -1){
// don't really expect this to happen, but if it does, just try again
debug_more("%s:interrupt nacked by NCU after 1ms delay !!\n",getName());
goto out;
}
debug_more("%s 1ms cb: sent interrupt to itid %d\n",getName(),irql.front()->itid);
// remove the irq and delete it since the interrupt has been sent
irql.pop_front();
delete ir;
}
out:
event_fire_time = mmi_get_time()+1000;
mmi_register_event(event_fire_time,samPiu_1ms_callback,(void*)this,0);
pthread_mutex_unlock(&ncuMutex);
}
void samPiu_1ms_callback(void * obj, void * ){
samPiu * t = (samPiu*) obj;
t->timer_callback();
}
pcieCompleter samPiu::devif_memAccess(bool wr, uint64_t addr, addrMd_xactnType mode,\
void * data, uint16_t length, uint8_t be, uint16_t reqId, tlp_X args, SAM_DeviceId *id){
// XXX place the dma record identifier 'id' here XXX //
int count = 0;
dev_access_t type = wr?DA_Store:DA_Load;
dev_mode_t md = mode == mem_addr32?PCIE_IS32:PCIE_IS64;
for(int i = 0; i < 8; i++)
if(be >> i & 0x1)
count++;
if(length > 2)
count += (length - 2) * 4;
uint8_t * data_ptr = (uint8_t*)data;
//debug_more("%s:devif_memAccess %s,length 0x%x, be 0x%x, count 0x%x\n", \
getName(),wr?"write":"read",length,be,count);
debug_more("%s: device %s DMA %s 0x%x bytes\n",getName(),samDevs->getName(*id),wr?"out":"in",count);
bool ret = true;
while(count){
// break down the requests on 8K boundary (smallest page size)
int bytes_in_page = (8*1024) - addr % (8*1024);
int bytes_to_transfer = count > bytes_in_page ? bytes_in_page:count;
ret &= piu_dma_access(&piuModel,addr,data_ptr,bytes_to_transfer,reqId,type,md,*id);
addr += bytes_to_transfer;
data_ptr += bytes_to_transfer;
count -= bytes_to_transfer;
}
if(ret)
return pcieCompleter(SC,getReqId());
else
return pcieCompleter(CA,getReqId()); // XXX ???
}
pcieCompleter samPiu::devif_msgAccess(uint8_t messageCode, msgRouting route, \
uint64_t tarIdOrAddr, uint16_t reqId, \
void * data, uint16_t length, \
uint16_t vendorId, uint32_t vendor_data, tlp_X args, SAM_DeviceId *id){
// handle intx messages
// XXX create trace records for device 'id' INTX interrupts here XXX //
debug_more("%s: device %s interrupt %s\n",getName(),samDevs->getName(*id),messageCode<=MSG_Assert_INTD ? "assert":"deassert");
switch(messageCode){
case MSG_Assert_INTA:
setIntx(reqId & 0xff, 0, true);
piu_assert_intx(&piuModel,0,reqId >> 3 & 0x1f);
break;
case MSG_Assert_INTB:
setIntx(reqId & 0xff, 1, true);
piu_assert_intx(&piuModel,1,reqId >> 3 & 0x1f);
break;
case MSG_Assert_INTC:
setIntx(reqId & 0xff, 2, true);
piu_assert_intx(&piuModel,2,reqId >> 3 & 0x1f);
break;
case MSG_Assert_INTD:
setIntx(reqId & 0xff, 3, true);
piu_assert_intx(&piuModel,3,reqId >> 3 & 0x1f);
break;
case MSG_Deassert_INTA:
setIntx(reqId & 0xff, 0, false);
piu_deassert_intx(&piuModel,0,reqId >> 3 & 0x1f);
break;
case MSG_Deassert_INTB:
setIntx(reqId & 0xff, 1, false);
piu_deassert_intx(&piuModel,1,reqId >> 3 & 0x1f);
break;
case MSG_Deassert_INTC:
setIntx(reqId & 0xff, 2, false);
piu_deassert_intx(&piuModel,2,reqId >> 3 & 0x1f);
break;
case MSG_Deassert_INTD:
setIntx(reqId & 0xff, 3, false);
piu_deassert_intx(&piuModel,3,reqId >> 3 & 0x1f);
break;
default:
debug_err("%s:devif_msgAccess: message code %x not supported yet\n", \
getName(),messageCode);
}
return SIM_OK;
}
int samPiu::handlePio(uint32_t cpuid,uint64_t paddr, mmi_bool_t wr, \
uint32_t size, uint64_t* buf, uint8_t bytemask){
pcieCompleter s;
maccess_t memop;
switch(size){
case 1:
memop = MA_Size8;
break;
case 2:
memop = MA_Size16;
break;
case 4:
memop = MA_Size32;
break;
case 8:
memop = MA_Size64;
break;
default:
assert(0);
}
switch(wr){
case false:
memop = (MACCESS)(memop | MA_Ld);
break;
case true:
memop = (MACCESS)(memop | MA_St);
break;
default:
assert(0);
}
if(wr && (paddr == PIU_CSR_BASE + PIU_DMU_PCIE_CONFREG_OFFSET)){
secondary_bus_no = (*buf) >> 24 & 0xff;
primary_bus_no = (*buf) >> 8 & 0xff;
device = (*buf) >> 3 & 0x1f;
function = (*buf) & 0x7;
}
if(!wr)
*buf = 0;
// hook for target of the PIO
SAM_DeviceId target = 0;
s = piu_cpu_access(&piuModel,paddr,memop,buf,&target);
debug_more("%s: accessed %s at 0x%llx\n",getName(),samDevs->getName(target),paddr);
if(s.status == SC)
return 0;
return -1;
}
#include "vcpu.h"
void samPiu::sendMondo(pcie_mondo_t *m){
VCPU_InterruptRequest r;
r.itid = m->thread_id;
r.isid = getReqId();
r.data[0] = m->data[0];
r.data[1] = m->data[1];
// assume only a single cpu (64 strand) configuration.
int stat = ncuIf->sendPiuMondo(&r);
if(stat == -1){
debug_more("%s:interrupt nacked by NCU\n",getName());
pthread_mutex_lock(&ncuMutex);
VCPU_InterruptRequest *ir = new VCPU_InterruptRequest;
*ir = r;
irql.push_back(ir);
pthread_mutex_unlock(&ncuMutex);
return;
}
debug_more("%s: sent interrupt to strand %d, return stat %d\n",getName(),m->thread_id,stat);
//printf("%s: sent interrupt to strand %d, return stat %d\n",getName(),m->thread_id,stat);
}
void samPiu::handle_ui(int argc, char * argv[]){
if(argc == 1){
ui_cmd_usage();
return;
}else if(!strcmp(argv[1],"dump")){
FILE * fp = stderr;
if(argv[2]){
fp = fopen(argv[2],"w");
if(!fp){
printf("%s dump: error opening file <%s>\n",getName(),argv[2]);
fp = stderr;
}
}
dump(fp);
if( fp != stderr )
fclose(fp);
}else if(!strcmp(argv[1],"restore")){
FILE * fp;
if(argv[2]){
fp = fopen(argv[2],"r");
if(fp){
restore(fp);
fclose(fp);
}else
printf("%s restore: error opening file <%s>\n",getName(),argv[2]);
}else
printf("%s restore: no restore filename specified\n",getName());
}else if(!strcmp(argv[1],"debug")){
if(argv[2]){
debug_level = atoi(argv[2]);
printf("%s: set debug level to %d\n",getName(),debug_level);
}else
printf("%s: current debug level %d\n",getName(),debug_level);
}else
debug_err("%s: unsupported UI command <%s>\n",getName(),argv[1]);
return;
}