// ========== 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 ============================================
/***************************************************************************************************/
//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
}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
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
){
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
);
//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
);
//hole size became 0. delete it
pciConfRegMap
.erase(iter_low
->first
);
iter_low
->second
->size
= size_hole1
;
pciConfReg
* hole
= new pciConfReg(size_hole2
);
pciConfRegMap
[offset
+ cr
->size
] = hole
;
pciConfRegMap
[offset
] = cr
;
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
);
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
);
pciConfReg
* hole
= new pciConfReg(iter
->second
->size
+ high_size
);
pciConfRegMap
[offset
] = hole
;
//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
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
iter_t iter
= pciConfRegMap
.lower_bound(offset
); //find the element with key less than or equal
assert(iter
!= pciConfRegMap
.end());
// iter->second->print();
uint8_t byte_offset
= offset
- iter
->first
;
uint8_t bytes_consumed
= 0;
if(iter
->second
->size
> byte_offset
){
bytes_consumed
= iter
->second
->write(*buf
,byte_offset
,size
);
*buf
>>= bytes_consumed
*8;
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;
byte_offset
+= bytes_consumed
;
// iter->second->print();
// 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
;
const int orig_offset
= offset
;
// 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;
ret
|= confAccessSize(offset
,wr
, &buf_1
,bytes_to_rd_wr
);
*in_buf
|= buf_1
<< (offset
- orig_offset
) * 8;
offset
+= bytes_to_rd_wr
;
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
);
pciConfIter
->second
->setMask(mask
);
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
);
pciConfIter
->second
->setVal(value
);