Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / common / pci_dev.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: pci_dev.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 "pci_dev.h"
#include "ui.h"
/*****************base address register*********/
baseAddrReg::baseAddrReg(const char * name, genericPciDev * d,uint64_t spaceSize,pci_space_t sp , bool isPrefetch):\
pciConfReg(name,4,0,0 ,(void*)d){
if(d == 0){
printf("baseAddrReg constructor error: must provide pointer to genericPciDev\n");
return;
}
if(spaceSize < 16){
printf("baseAddrReg constructor error: size must be greater than or equal 16\n");
return;
}
int first_set_bit = ffs((uint32_t)spaceSize);
if(first_set_bit == 0){
first_set_bit = ffs((uint32_t) (spaceSize >> 32));
mask = 0;
mask_32To63 = (uint32_t)-1 << (first_set_bit - 1);
}else{
mask_32To63 = 0xffffffff;
mask = (uint32_t)-1 << (first_set_bit - 1);
}
this->space = sp;
this->isPrefetchable = isPrefetch;
this->mapSize = spaceSize;
isMapped = false;
val_32To63 = 0;
mask_32To63 = 0xffffffff;
switch(space){
case PCI_MEM32:
switch(isPrefetchable){
case true:
val = MEM32 | PREFETCH;
break;
case false:
val = MEM32;
break;
}
break;
case PCI_MEM64:
switch(isPrefetchable){
case true:
val = MEM64 | PREFETCH;
size = 8;
break;
case false:
val = MEM64;
size = 8;
break;
}
break;
case PCI_IO:
val = IO;
break;
}
return;
}
uint8_t baseAddrReg::write(uint32_t buf, uint8_t byte_offset, uint8_t bytes_to_write){
// simpler assumption for now of whole reg reads.
// if assert ever fails then add support for (bytes_to_write <= 4 == true) etc.
assert(bytes_to_write == 4);
assert(byte_offset == 0 or byte_offset == 4);
oldVal = val | (uint64_t)val_32To63 << 32;
if(byte_offset > 3){
if(debug_level >= 2)
printf("Name<%s>,size<0x%x>offset<0x4>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name,size,mask,val_32To63,buf & mask_32To63);
val_32To63 = buf & mask_32To63;
}else
pciConfReg::write(buf, byte_offset, bytes_to_write);
reMap();
return 4;
}
void baseAddrReg::reMap(){
//read the command register
uint32_t commandRegVal;
pciConfReg * confReg;
confReg = ((genericPciDev*)data)->confSpace->getConfReg(PCI_CONF_COMM);
if(confReg){
confReg->read(&commandRegVal,confReg->size);
if(space == PCI_MEM32 && (commandRegVal & 1 << pciCommandReg::MEM_SPACE)){
if(isMapped)
((genericPciDev*)data)->unMapBar(PCI_MEM32,oldVal & ~0xf);
((genericPciDev*)data)->mapBar(PCI_MEM32,val & ~0xf , mapSize);
}else if(space == PCI_MEM64 && (commandRegVal & 1 << pciCommandReg::MEM_SPACE)){
if(isMapped)
((genericPciDev*)data)->unMapBar(PCI_MEM64,oldVal & ~0xf);
((genericPciDev*)data)->mapBar(PCI_MEM64,val & ~0xf , mapSize);
}else if(space == PCI_IO && (commandRegVal & 1 << pciCommandReg::IO_SPACE)){
if(isMapped)
((genericPciDev*)data)->unMapBar(PCI_IO,oldVal & ~0xf);
((genericPciDev*)data)->mapBar(PCI_IO,val & ~0xf , mapSize);
}//else
//printf("internal error base address reg remap()\n");
}
}
bool baseAddrReg::map(){
assert(!isMapped);
//isMapped = true;
isMapped = ((genericPciDev*)data)->mapBar(space,val & ~0xf , mapSize);
return isMapped;
}
bool baseAddrReg::unmap(){
assert(isMapped);
//isMapped = false;
isMapped = ((genericPciDev*)data)->unMapBar(space,val & ~0xf);
return isMapped;
}
/********************Command reg************************/
uint8_t pciCommandReg::write(uint32_t buf, uint8_t byte_offset, uint8_t bytes_to_write){
assert(byte_offset == 0);
assert(bytes_to_write == 2);
//uint32_t changed_bits = val ^ (mask & buf);
uint32_t new_val = val | (mask & buf);
uint32_t changed_bits = new_val ^ val;
if(debug_level >= 2)
printf("Name<%s>,size<0x%x>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name,size,mask,val,new_val);
val = new_val;
if(changed_bits & 1 << IO_SPACE){ //I/O space map bit has changed
if(val & 1 << IO_SPACE) //bit set to 1
mapSpace(PCI_IO);
else
unMapSpace(PCI_IO); //bit set to 0
}
if(changed_bits & 1 << MEM_SPACE){
if(val & 1 << MEM_SPACE){ //bit set to 1
mapSpace(PCI_MEM32);
mapSpace(PCI_MEM64);
}else{
unMapSpace(PCI_MEM32);
unMapSpace(PCI_MEM64); //bit set to 0
}
}
return 2;
}
void pciCommandReg::mapSpace(pci_space_t spc){
//base address regs range from 0x10 to 0x24
baseAddrReg * baseAddr;
for(int i = 0x10; i < 0x28; i+=4){
baseAddr = dynamic_cast<baseAddrReg*>(((genericPciDev*)data)->confSpace->getConfReg(i));
if(baseAddr && baseAddr->space == spc){
baseAddr->map();
if(spc == PCI_MEM64)
i+=4;
}
}
}
void pciCommandReg::unMapSpace(pci_space_t spc){
//base address regs range from 0x10 to 0x24
baseAddrReg * baseAddr;
for(int i = 0x10; i < 0x28; i+=4){
baseAddr = dynamic_cast<baseAddrReg*>(((genericPciDev*)data)->confSpace->getConfReg(i));
if(baseAddr && baseAddr->space == spc){
baseAddr->unmap();
if(spc == PCI_MEM64)
i+=4;
}
}
}
/***************************************************************************************************/
genericPciDev::genericPciDev(confHeaderType htype){
confSpace = new pciConfSpace(htype);
dev_type = 0;
for(int i = 0; i < 3; i++){
int_pin_status[i] = false;
slot_irl[i] = -1;
}
interrupt_added = false;
busName = 0;
busIf = 0;
intrptIf = 0;
device = -1;
function = 0;
isMultiFunction = false;
}
//////////////////////functions exported to pci bus
pciXactnStatus genericPciDev::devif_access_w_size(pci_space_t space, uint64_t paddr, uint64_t offset, bool_t wr, uint64_t* buf, uint8_t size, SAM_DeviceId * id){
bool ret;
if(id){
*id = (dynamic_cast<Module*>(this))->samId;
}
switch(space){
case PCI_CFG:
if(offset % size != 0)
return TARGET_ABORT;
offset -= device << 11 | function << 8;
ret = confSpace->confAccessSize(offset,wr,buf,size);
if(!ret)
return SIM_INTERNAL_ERROR;
pciDev_confAccessCb(wr,offset,size);
return SUCCESS;
case PCI_IO:
return pciTarget_ioaccess(offset,wr,buf,size);
case PCI_MEM32:
return pciTarget_mem32access(offset,wr,buf,size);
case PCI_MEM64:
return pciTarget_mem64access(offset,wr,buf,size);
default:
printf("Unknown PCI space %d\n",space);
return SIM_INTERNAL_ERROR;
}
}
pciXactnStatus genericPciDev::devif_access_w_byte_enable(pci_space_t, uint64_t paddr, uint64_t offset, bool_t wr, uint64_t* buf, uint8_t byte_enable, SAM_DeviceId * id){
if(id){
*id = (dynamic_cast<Module*>(this))->samId;
}
printf("INTERNAL ERROR : byte enable access not yet supported \n");
return SUCCESS;
}
/////////////////////////functions to implement device access as target
// generic device does not respond to any access over the bus. hence
// the access terminates in master abort.
// specific devices must override only those access functions, which correspond to
// implemented behaviour.
pciXactnStatus genericPciDev::pciTarget_ioaccess(uint64_t offset, bool wr,uint64_t * buf, uint8_t size){
Debug_err("%s: accessing unimplemented address space %s\n", pciDev_getName(), "PCI_IO");
return MASTER_ABORT;
}
pciXactnStatus genericPciDev::pciTarget_mem32access(uint64_t offset, bool wr,uint64_t * buf, uint8_t size){
Debug_err("%s: accessing unimplemented address space %s\n", pciDev_getName(), "PCI_MEM32");
return MASTER_ABORT;
}
pciXactnStatus genericPciDev::pciTarget_mem64access(uint64_t offset, bool wr,uint64_t * buf, uint8_t size){
Debug_err("%s: accessing unimplemented address space %s\n", pciDev_getName(), "PCI_MEM64");
return MASTER_ABORT;
}
pciXactnStatus genericPciDev::pciTarget_special_cycle(uint16_t mssg, uint16_t data){
return MASTER_ABORT;
}
pciXactnStatus genericPciDev::pciTarget_intr_ack(uint64_t *data){
return MASTER_ABORT;
}
////////////////////////////////initialization and module related fns
void genericPciDev::pciDevInfo(){
printf("bus=<%s> dev=%d fun=%d\n",busName,device,function);
printf("configuration space reg info:\n");
confSpace->print();
}
bool
genericPciDev::dev_module_added(const char *target_name)
{
if (busName && !strcmp(busName, target_name)) {
busName = target_name;
mmi_instance_t mod = mmi_get_instance(busName);
busIf = (pciBusIf*)mmi_get_interface(mod, PCI_BUS_INTERFACE);
if (!busIf) {
Debug_err("%s: ERROR: can't find interface for PCI bus <%s>\n", Here, busName);
return false;
}else if(!busIf->busif_add_device(pciDev_getName(), device, function)) {
Debug_err("%s: ERROR: could not add tp PCI bus <%s>\n", Here, busName);
return false;
}else{
ui->verbose("%s: bus <%s> connected: busif=%p\n", Here, busName, busIf);
}
}
if(!interrupt_added && busIf && (busIf->busif_add_interrupt(device,dev_type, slot_irl,isMultiFunction) != -1))
interrupt_added = true;
return true;
}
bool
genericPciDev::dev_module_deleted(const char *target_name){
if (busName && !strcmp(busName, target_name)) {
busName = 0;
busIf = 0;
Debug_info("%s: bus <%s> disconnected\n", Here, busName);
}
return true;
}
bool
genericPciDev::dev_parse_arg(const char *arg){
if (argval("bus", arg, &busName)) {
Debug_more("%s: bus=<%s>\n", Here, busName);
} else if (argval("dev", arg, &device)) {
Debug_more("%s: dev=%d\n", Here, device);
} else if (argval("fun", arg, &function)) {
Debug_more("%s: fun=%d\n", Here, function);
}else{
Debug_err("%s: WARNING: unrecognized arg '%s'\n", Here, arg);
return false;
}
return true;
}
bool
genericPciDev::dev_check_args(){
if (!busName) {
Debug_err("%s: ERROR: must specify a PCI bus\n", pciDev_getName());
return false;
}else if(device == -1){
Debug_err("%s: ERROR: must specify device number\n", pciDev_getName());
return false;
}else
return true;
}
void
genericPciDev::dev_init_done(int debug_level){
if(!interrupt_added){
int ret = busIf->busif_add_interrupt(device, 0, slot_irl);
if(ret == -1)
Debug_err("%s:ERROR: failed to be assingned interrupt\n", pciDev_getName());
else
interrupt_added = true;
}
confSpace->set_debug_level(debug_level);
pciDebug::set_debug_level(debug_level);
}
/////////////////////////////////functions to called by specific PCI device when acting as bus master
bool
genericPciDev::pciMaster_set_int(int line, bool raise){
Module * m = dynamic_cast<Module*>(this);
assert (m);
SAM_DeviceId id = m->samId;
if (line < 0 || 4 <= line) {
Debug_err("%s: ERROR: dev_set_int_pin: pin=%d: should be 0..3\n", pciDev_getName(), line);
return false;
}
if (int_pin_status[line] != raise) {
int_pin_status[line] = raise;
if (busIf && interrupt_added){
busIf->busif_interrupt_in(raise,device, line,&id);
return true;
}
}
return true;
}
pciXactnStatus genericPciDev::pciMaster_dma(bool wr, uint64_t vaddr, void *data, long count){
Module * m = dynamic_cast<Module*>(this);
assert (m);
SAM_DeviceId id = m->samId;
if(busIf)
return busIf->busif_dma(wr,vaddr, data, count,&id);
Debug_err("%s: ERROR: no bus interface <%s> \n",pciDev_getName());
return SIM_INTERNAL_ERROR;
}
pciXactnStatus genericPciDev::pciMaster_intr_ack_cycle(uint64_t * data){
if(busIf)
return busIf->busif_intr_ack(data);
Debug_err("%s: ERROR: no bus interface <%s>\n",pciDev_getName());
return SIM_INTERNAL_ERROR;
}
pciXactnStatus genericPciDev::pciMaster_special_cycle(uint16_t mssg, uint16_t data){
if(busIf)
return busIf->busif_special_cycle(mssg, data);
Debug_err("%s: ERROR: no bus interface <%s>\n",pciDev_getName());
return SIM_INTERNAL_ERROR;
}
pciXactnStatus genericPciDev::pciMaster_bus_access_w_size(pci_space_t space, uint64_t paddr, uint64_t offset, bool_t wr, uint64_t* buf, uint8_t size){
if(busIf)
return busIf->busif_access_w_size(space,paddr,offset,wr,buf,size);
Debug_err("%s: ERROR: no bus interface <%s>\n",pciDev_getName());
return SIM_INTERNAL_ERROR;
}
pciXactnStatus genericPciDev::pciMaster_bus_access_w_byte_enable(pci_space_t space, uint64_t paddr, uint64_t offset, bool_t wr, uint64_t* buf, uint8_t byte_enable){
if(busIf)
return busIf->busif_access_w_byte_enables(space,paddr,offset,wr,buf,byte_enable);
Debug_err("%s: ERROR: no bus interface <%s>\n",pciDev_getName());
return SIM_INTERNAL_ERROR;
}
/////////////////////////////////
bool genericPciDev::dump(const char * dirname,const char *file){
char *filename = new char[strlen(dirname) + strlen(file) + 6];
sprintf(filename,"%s/%s.pci",dirname,file);
FILE *fp = fopen(filename,"w");
if(!fp){
Debug_err("%s pci dump. fopen failed for %s\n",pciDev_getName(), filename);
return false;
}
pciConfReg * cr;
for(int offset = 0; offset < 0x100;){
if(offset == PCI_CONF_COMM){
offset += 2;
continue;
}
cr = confSpace->getConfReg(offset);
if(!cr) // hole
offset++;
else if(cr->isRO()) // Read only register. no need to save
offset += cr->size;
else{
fprintf(fp,"0x%x\t0x%x\n",offset,cr->getVal());
offset += cr->size;
}
}
cr = confSpace->getConfReg(PCI_CONF_COMM);
fprintf(fp,"0x%x\t0x%x\n",PCI_CONF_COMM,cr->getVal());
fclose(fp);
return true;
}
// to be called after initPci() has been called
bool genericPciDev::restore(const char * dirname,const char * file){
char *filename = new char[strlen(dirname) + strlen(file) + 6];
sprintf(filename,"%s/%s.pci",dirname,file);
int off;
uint32_t val;
FILE *fp = fopen(filename,"r");
if(!fp){
Debug_err("%s pci restore. fopen failed for %s\n",pciDev_getName(), filename);
return false;
}
pciConfReg * cr;
char str[100];
for(int offset = 0; offset < 0x100;){
if(offset == PCI_CONF_COMM){
offset += 2;
continue;
}
cr = confSpace->getConfReg(offset);
if(!cr) // hole
offset++;
else if(cr->isRO()) // Read only register. no need to restore
offset += cr->size;
else{
fscanf(fp,"0x%x\t0x%x\n",&off,&val);
assert(off == offset);
cr->write(val,0,cr->size);
pciDev_confAccessCb(true,offset,cr->size);
offset += cr->size;
}
}
// restore the command register
cr = confSpace->getConfReg(PCI_CONF_COMM);
fscanf(fp,"0x%x\t0x%x\n",&off,&val);
cr->write(val,0,cr->size);
pciDev_confAccessCb(true,PCI_CONF_COMM,cr->size);
fclose(fp);
return true;
}