Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / common / pci_common.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: pci_common.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_common.h"
/***************************************************************************************************/
//pci configuration space class functions//
//fill in the default values for register values, masks, names etc.
void pciConfSpace::initConf(confHeaderType htype){
if(htype == pciHeaderNull){
// for devices that know what they want
return;
}else if(htype == pciHeader0){
//for now only type 0 header. type 1 support to be done
//common registers for all pci devices
//specific implementations must provide specific
//values for mask and init value if different from default
//NOTE: command and status registers are not added here since they are specialized
//versions of pciConfReg, requiring callback data pointer. they must be initialized
//after device instantiation and added to pci configuration space.
addConfReg(new pciConfReg("Vendor Id", 2, 0x0, 0x0), PCI_CONF_VENID);
addConfReg(new pciConfReg("Device id", 2, 0x0, 0x0), PCI_CONF_DEVID);
//addConfReg(new pciCommandReg("Command", 0x3ff, this), PCI_CONF_COMM);
//addConfReg(new pciStatusReg("Status", 0x0, 0xff90, this), PCI_CONF_STAT);
addConfReg(new pciConfReg("Revision Id", 1, 0x0, 0x0), PCI_CONF_REVID);
addConfReg(new pciConfReg("Programming class", 1, 0x0, 0x0), PCI_CONF_PROGCLASS);
addConfReg(new pciConfReg("Sub class", 1, 0x0, 0x0), PCI_CONF_SUBCLASS);
addConfReg(new pciConfReg("Base class", 1, 0x0, 0x0), PCI_CONF_BASCLASS);
addConfReg(new pciConfReg("Cache line size", 1, 0x0, 0xff), PCI_CONF_CACHE_LINESZ);
addConfReg(new pciConfReg("Latency time", 1, 0, 0xff), PCI_CONF_LATENCY_TIMER);
addConfReg(new pciConfReg("Header type", 1, 0x0, 0x00), PCI_CONF_HEADER); //default type 0
addConfReg(new pciConfReg("BIST", 4, 0x0, 0x40), PCI_CONF_BIST);
//these registers may or may not be implemented. The write mask is
//0x0. The specific device implementation must change the mask to correct
//value if the register is implemented and provide the correct init value.
//in case the register is read only, provide the init value
//NOTE: the base address registers(BAR) are not added here since they need the size
//information, known only to specific device implementation. should be added during initialization.
addConfReg(new pciConfReg("CardBus CIS pointer", 4,0x0,0x0), PCI_CONF_CIS);
addConfReg(new pciConfReg("Subsystem vendor id", 2,0x0,0x0), PCI_CONF_SUBVENID);
addConfReg(new pciConfReg("Subsystem Id", 2,0x0,0x0), PCI_CONF_SUBSYSID);
addConfReg(new pciConfReg("Expansion ROM base address", 4,0x0,0x0), PCI_CONF_ROM);
addConfReg(new pciConfReg("Capabilities pointer", 1,0x0,0x0), PCI_CONF_CAP_PTR);
addConfReg(new pciConfReg("Interrupt line", 1,0x0,0x0), PCI_CONF_ILINE);
addConfReg(new pciConfReg("Interrupt pin", 1,0x0,0x0), PCI_CONF_IPIN); //rdonly register
addConfReg(new pciConfReg("Min grant", 1,0x0,0x0), PCI_CONF_MIN_G); //rdonly
addConfReg(new pciConfReg("Min grant", 1,0x0,0x0), PCI_CONF_MAX_L); //rdonly
}else{
printf("confspace init: header type 1 unsupported yet.\n");
}
}
//add a configuration register to the map. If there is a conflict
//in address range , then the old reg(s) are removed and new is installed.
//this allows the device models to have device specific configuration
//registers not part of the generic PCI model.
bool pciConfSpace::addConfReg(pciConfReg * cr, int offset){
iter_t iter_low, iter;
if( ((iter = pciConfRegMap.lower_bound(offset)) != pciConfRegMap.end() ) && (iter->second->isHole == false) ){
printf("current Conf space registers-->\n");print();
printf("addConfReg error: conf reg at offset 0x%x already exist, name<%s>\nDeleted..\n",offset,iter->second->name);
deleteConfReg(offset);
//XXX corner case. inserting a larger reg than the 1 being deleted
}
cr->setDebug(debug_level);
iter_low = pciConfRegMap.lower_bound(offset);
assert(iter_low->second->isHole);
int size_hole1 = offset - iter_low->first;
int size_hole2 = iter_low->second->size - (size_hole1 + cr->size);
if(size_hole1 <= 0)
//hole size became 0. delete it
pciConfRegMap.erase(iter_low->first);
else
iter_low->second->size = size_hole1;
if(size_hole2 > 0){
pciConfReg * hole = new pciConfReg(size_hole2);
pciConfRegMap[offset + cr->size] = hole;
}
pciConfRegMap[offset] = cr;
return true;
}
bool pciConfSpace::deleteConfReg(int offset){
if( (pciConfRegMap.find(offset) == pciConfRegMap.end()) || pciConfRegMap.find(offset)->second->isHole == true ){
printf("deleteConfReg error: conf reg at offset 0x%x does not exist\n",offset);
return false;
}
iter_t iter_low, iter_high, iter;
iter = pciConfRegMap.find(offset);
iter_low = pciConfRegMap.upper_bound(offset);
iter_high = pciConfRegMap.lower_bound(offset);
int high_size = 0, high_offset = 0;
if(iter_high->second->isHole){
high_offset = iter_high->first;
high_size = iter_high->second->size;
pciConfRegMap.erase(iter_high->first);
}
if(iter_low->second->isHole){
iter_low->second->size += iter->second->size + high_size;
pciConfRegMap.erase(offset);
}else{
pciConfReg * hole = new pciConfReg(iter->second->size + high_size);
pciConfRegMap[offset] = hole;
}
return true;
}
//read/write contiguous 'size' bytes from 'offset' and return the value in buf in case of read.
//it is assumed that alignment restrictions are maintained, with this kind of access.
bool pciConfSpace::confAccessSize(int offset,bool wr, uint64_t * in_buf,uint8_t size){
// assert(offset <= 0xff); move to caller XXX
assert(size <= 4);
uint64_t local_buf = *in_buf;
uint64_t *buf = &local_buf;
uint32_t buf_lo = local_buf;
//if(offset % size != 0)//XXX move checking to caller
// return TARGET_ABORT;
iter_t iter = pciConfRegMap.lower_bound(offset); //find the element with key less than or equal
//to offset
assert(iter != pciConfRegMap.end());
//if(debug_level >= 2)
// iter->second->print();
uint8_t byte_offset = offset - iter->first;
uint8_t bytes_consumed = 0;
uint8_t buf_offset = 0;
while(size!=0){
if(iter->second->size > byte_offset){
if(wr){
bytes_consumed = iter->second->write(*buf,byte_offset,size);
*buf >>= bytes_consumed*8;
}else{
bytes_consumed = iter->second->read(&buf_lo,size,byte_offset,buf_offset);
local_buf = ((local_buf >> 32) << 32) | buf_lo;
buf_offset += bytes_consumed;
//*buf <<= (size - bytes_consumed) * 8;
}
size -= bytes_consumed;
byte_offset += bytes_consumed;
}else{
//iter++;
iter--;
//if(debug_level >= 2)
// iter->second->print();
byte_offset = 0;
}
}
if(!wr)
*in_buf = *buf;
return true;
}
// access the conf space with 'be' supplying the byte enables.
// for a read, bytes with corresponding byte enable as 1 will be meaning ful
// for a write, only those bytes with corresponding byte as 1 will be written to.
// the function would try to read/write contiguous bytes using a single confAccessSize() call.
bool pciConfSpace::confAccessByteE(int offset,bool wr, uint64_t * in_buf,uint8_t be){
assert ((be & 0xf0) == 0); // upper 4 bits have to be 0.
uint64_t local_buf = *in_buf;
bool ret = false;
bool to_rd_wr = false;
int bytes_to_rd_wr = 0;
uint64_t buf_1 = 0;
const int orig_offset = offset;
if(!wr){
// if a read, 0 out the bytes that have to be read
for(int i = 0; i < 4; i++)
if( (be >> i & 0x1) == 1)
*in_buf &= ~((uint64_t)0xff << i * 8);
}
for(int r_offset = 0; r_offset <= 4; r_offset++){// an extra iteration is needed to r/w the last byte
if( (be >> r_offset & 0x1) == 1){
buf_1 |= (local_buf & 0xff) << r_offset * 8;
to_rd_wr = true;
bytes_to_rd_wr++;
}else if(to_rd_wr){
ret |= confAccessSize(offset,wr, &buf_1,bytes_to_rd_wr);
to_rd_wr = false;
if(!wr)
*in_buf |= buf_1 << (offset - orig_offset) * 8;
offset += bytes_to_rd_wr;
buf_1 = 0;
bytes_to_rd_wr = 0;
}else
offset++;
local_buf >>= 8;
}
return ret;
}
inline bool pciConfSpace::setMask(int offset, uint32_t mask){
pciConfIter = pciConfRegMap.lower_bound(offset);
if(pciConfIter->first != offset || pciConfIter->second->isHole){
printf("conf space setMask: no register at offset 0x%x\n",offset);
return false;
}
pciConfIter->second->setMask(mask);
return true;
}
inline bool pciConfSpace::setVal(int offset, uint32_t value){
pciConfIter = pciConfRegMap.lower_bound(offset);
if(pciConfIter->first != offset || pciConfIter->second->isHole){
printf("conf space setVal : no register at offset 0x%x\n",offset);
return false;
}
pciConfIter->second->setVal(value);
return true;
}