// ========== Copyright Header Begin ==========================================
// OpenSPARC T2 Processor File: pcie_bus.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 ============================================
//////////////////module functions////////////////
// print interesting info about this module
pcieDevMapIterator dmIter
;
printf(" bridge=<%s>\n", bridgeName
);
for(uint8_t i
= 0; i
< 3; i
++){
printf("%s",pcie_space_name((pcie_space
)i
));
for(dmIter
= pcieAddrSpace
[i
].begin(); dmIter
!= pcieAddrSpace
[i
].end(); dmIter
++)
pcieBus::parse_arg(const char *arg
){
if (argval("bridge", arg
, &bridgeName
))
debug_more("%s: bridge=<%s>\n", HERE
, bridgeName
);
pcieBus::module_added(mmi_instance_t target_mod
, const char *target_name
){
if (!strcmp(bridgeName
, target_name
)) {
bridgeMod
= target_mod
;//mmi_get_module(bridgeName);
// let the bridge set the interface it wants to export
// instead of the other way round
// bridgeIf = (genericPcieDevIf*) mmi_get_interface(bridgeMod, GENERIC_PCIE_DEV_INTERFACE);
// debug_info("%s: bridge <%s> connected\n", HERE, bridgeName);
// debug_err("%s: ERROR: bridge <%s> has no interface\n", HERE, bridgeName);
pcieBus::module_deleted(mmi_instance_t
, const char *target_name
){
if (!strcmp(bridgeName
, target_name
)) {
debug_info("%s: bridge <%s> disconnected\n", HERE
, bridgeName
);
busif_deleteDevice(target_name
);
pcieBus::get_interface(const char *name
){
if (!strcmp(name
, PCIE_BUS_INTERFACE
))
debug_err("%s: ERROR: must specify a host bridge\n", HERE
);
return Module::get_help_string();
Module::get_help_string(){
return "new pcie bus impl";
Module::create(const char *_modname
, const char *_instance_name
){
return new pcieBus(_modname
, _instance_name
);
pcieBus::pcieBus(const char *_modname
, const char *_instance_name
)
: Module(_modname
, _instance_name
),
bridgeName(0), bridgeMod(0), bridgeIf(0){}
debug_more("%s: destructor\n", HERE
);
/////////////////////exported interface functions///////////////
bool pcieBus::busif_addDevice(const char *devname
, int device
, int function
){
debug_info("%s: busif_addDevice: <%s> dev=%d fun=%d\n", HERE
, devname
, device
, function
);
mmi_instance_t dev_instance
= mmi_get_instance(devname
);
debug_err("%s: Device named <%s> is not a module\n", HERE
, devname
);
if(searchDevInfoListByName(devname
)){
debug_err("%s: Device named <%s> already exists\n", HERE
, devname
);
if(searchDevInfoListByDevFun(device
,function
)){
debug_err("%s: Can't install <%s>: device already exists at dev=%d fun=%d\n",\
HERE
, devname
, device
, function
);
genericPcieDevIf
* dI
= (genericPcieDevIf
*)mmi_get_interface(dev_instance
,GENERIC_PCIE_DEV_INTERFACE
);
debug_err("%s: Device named <%s> has no device interface\n", HERE
, devname
);
pcieDevInfo
* devInfo
= new pcieDevInfo(devname
, dev_instance
, dI
, device
, function
);
uint64_t base
= device
<< devShift
| function
<< funShift
; // 4 kb configuration space
// the mappings are local to this bus segment, hence no need for the bus number here
// which is needed for the global address resolution
//map the configuration space. this space is always accessible and cannot be unmapped etc.
if(!addSpace(new pcieRange(base
, base
+ 0xfff, devInfo
),PCIE_CFG
)){
debug_err("%s: cannot add configuration space for <%s>. Conflict in ranges!!\n",HERE
,devname
);
devInfoList
.push_back(devInfo
);
pcieBus::busif_deleteDevice(const char *devname
){
pcieDevInfo
* pdi
= searchDevInfoListByName(devname
);
debug_err("%s: cannot remove <%s>. non - existent device !!\n",HERE
,devname
);
bool pcieBus::busif_map(const char *devname
, pcie_space space
, uint64_t base
, uint64_t size
){
pcieDevInfo
* pdi
= searchDevInfoListByName(devname
);
if(!addSpace(new pcieRange(base
, base
+ size
- 1,pdi
),space
)){
debug_more("busif_map: can not map in space for device <%s>. Conflicting range!!\n", devname
);
debug_err("busif_map: device <%s> is not added to bus list\n",devname
);
bool pcieBus::busif_unmap(const char *devname
, pcie_space space
,uint64_t base
){
pcieDevInfo
* pdi
= searchDevInfoListByName(devname
);
if(!removeSpace(base
, space
)){
debug_err("busif_unmap: can not unmap space for device <%s>. map does not exist!!\n", devname
);
debug_err("busif_unmap: device <%s> is not added to bus list\n",devname
);
int pcieBus::busif_getBusno(){
int bus
= bridgeIf
->devif_getBusNo();
pcieCompleter
pcieBus::busif_msgAccess(uint8_t messageCode
, msgRouting route
, uint16_t reqId
, SAM_DeviceId
*id
,\
void * data
, uint16_t length
, \
uint16_t vendorId
, uint32_t vendor_data
, tlp_X args
){
// send it to the upstream bridge. If its not an RC it should forward the message upstream
return bridgeIf
->devif_msgAccess(messageCode
,route
,tarIdOrAddr
,reqId
,data
,length
,vendorId
, \
// XXX not clear if the address is in MEM space or IO space or some other space from specs.
// the specs however say no messages defined currently use this routing. hence print an error
debug_err("%s: address routing unsupported \n", getName());
int targetBus
= tarIdOrAddr
>> 8 & 0xff;
if(targetBus
!= busif_getBusno()){
// message not intended for this bus segment
// send the message to the bridge for further routing
return bridgeIf
->devif_msgAccess(messageCode
,route
,tarIdOrAddr
,reqId
,data
,length
,vendorId
, \
// target device on this bus segment
pcieDevInfo
* devInfo
= searchDevInfoListByDevFun(tarIdOrAddr
>> 3 & 0x1f, tarIdOrAddr
& 0x7);
return devInfo
->devIf
->devif_msgAccess(messageCode
, route
,tarIdOrAddr
,reqId
, data
, length
, \
vendorId
, vendor_data
, args
, id
);
debug_err("error %s:busif_msgAccess() - target %llx not present on this bus segment",getName(),tarIdOrAddr
);
return pcieCompleter(SIM_FAIL
);
// call the msgAccess function of all the downstream devices/functions
list
<pcieDevInfo
*>::iterator i
;
for(i
= devInfoList
.begin(); i
!= devInfoList
.end(); i
++){
if((*i
)->devIf
!= bridgeIf
)
(*i
)->devIf
->devif_msgAccess(messageCode
,route
,tarIdOrAddr
,reqId
,data
,length
,vendorId
,vendor_data
, args
, id
);
// should be terminated at the upstream endpoint. hence this message is just sent to the bridge.
// mesg could be intx, etc,
return bridgeIf
->devif_msgAccess(messageCode
,route
,tarIdOrAddr
,reqId
,data
,length
,vendorId
, \
case gathered_routed_to_RC
:
// send to the upstream switch. the switch gathers this message from all downstream devices
// and sends the mesg to RC ..
return bridgeIf
->devif_msgAccess(messageCode
,route
,tarIdOrAddr
,reqId
,data
,length
,vendorId
, \
debug_err("%s: unknown message routing type %x\n",getName(),route
);
return pcieCompleter(SIM_FAIL
);
return pcieCompleter(SIM_OK
);
pcieCompleter
pcieBus::busif_access(pcie_space space
, bool wr
, uint64_t addr
, void * data
, \
uint16_t length
, uint8_t be
, uint16_t reqId
, addrMd_xactnType am_xt
, SAM_DeviceId
*id
, tlp_X args
){
genericPcieDevIf
* targetDev
;
debug_info("%s: %s %s: addr %llx length %x be 0x%x \n", getName(), wr
?"WRITE":"READ ", \
pcie_space_name(space
), addr
, length
, be
);
int bus
= addr
>> busShift
& 0xff;
if(bus
!= busif_getBusno()){
// configuration access that does not belong to this bus segment.
// forward to bridge downstream
list
<pcieDevInfo
*>::iterator i
;
for(i
= devInfoList
.begin(); i
!= devInfoList
.end(); i
++){
if((*i
)->devIf
!= bridgeIf
){
if((*i
)->devIf
->devif_isBridge() == false)
int secbus
= (*i
)->devIf
->devif_getBusNo();
int subbus
= (*i
)->devIf
->devif_getSubBusNo();
if((bus
>= secbus
) && (bus
<= subbus
))
return (*i
)->devIf
->devif_confAccess(wr
,addr
,data
,be
,reqId
,conf_type1
,length
,args
,id
);
debug_err("%s: unable to route config space access at addr %llx", getName(), addr
);
return pcieCompleter(CA
);
// get the address relative to this bus hierarchy
addr
-= busif_getBusno() << busShift
;
pcieDevMapIterator dmIter
;
dmIter
= pcieAddrSpace
[space
].lower_bound(addr
);
if(dmIter
== pcieAddrSpace
[space
].end()){
// possible dma/dvma space
if(reqId
== getBridgeId())
return pcieCompleter(CA
);
debug_more("%s: unmapped access space = %s addr=%llx, forwarding to bridge\n",getName(),pcie_space_name(space
), addr
);
}else if(dmIter
->second
->end
< addr
){
// if the request is from the upstream bridge, don't send it back to it
if(reqId
== getBridgeId())
return pcieCompleter(CA
);
debug_more("%s: unmapped access space = %s addr=%llx, forwarding to bridge\n",getName(),pcie_space_name(space
), addr
);
targetDev
= dmIter
->second
->devInfo
->devIf
;
// get the conf space offset
addr
-= (dmIter
->second
->devInfo
->device
<< devShift
) + (dmIter
->second
->devInfo
->function
<< funShift
);
return targetDev
->devif_confAccess(wr
,addr
,data
,be
,reqId
,conf_type0
, length
,args
,id
);
return targetDev
->devif_memAccess(wr
,addr
,am_xt
,data
,length
,be
,reqId
,args
,id
);
return targetDev
->devif_ioAccess(wr
,addr
,data
,be
,reqId
,length
,args
,id
);
debug_err("%s: unknown pcie space %d accessed\n",space
);
return pcieCompleter(CA
);
mmi_instance_t
pcieBus::getDevInstance(int dev
,int fun
){
pcieDevInfo
* di
= searchDevInfoListByDevFun(dev
,fun
);
pcieCompleter pcieBus::busif_access(pcie_space space, bool wr, uint64_t addr, void * data, \
uint16_t length, uint8_t be, uint16_t reqId, addrMd_xactnType am_xt, tlp_X args){
genericPcieDevIf * targetDev;
debug_info("%s: %s %s: addr %llx length %x be 0x%x \n", getName(), wr?"WRITE":"READ ", \
pcie_space_name(space), addr, length, be);
int bus = addr >> busShift & 0xff;
if(bus != busif_getBusno()){
// configuration access that does not belong to this bus segment.
// forward to bridges downstream, or upstream.
if( bus == bridgeIf->devif_getPriBusNo() ){
// access the upstream bridge
return bridgeIf->devif_confAccess(wr,addr,data,be,reqId,conf_type1,length,args);
// check each downstream device for being a bridge.
// if yes, check if the target bus falls between its secondary
// and subordinate bus number. if yes, send the xactn to it.
list<pcieDevInfo *>::iterator i;
for(i = devInfoList.begin(); i != devInfoList.end(); i++){
if((*i)->devIf != bridgeIf){
if((*i)->devIf->devif_isBridge() == false)
int secbus = (*i)->devIf->devif_getBusNo();
int subbus = (*i)->devIf->devif_getSubBusNo();
if((bus >= secbus) && (bus <= subbus))
return (*i)->devIf->devif_confAccess(wr,addr,data,be,reqId,conf_type1,length,args);
debug_err("%s: unable to route config space access at addr %llx", getName(), addr);
return pcieCompleter(CA);
// get the address relative to this bus hierarchy
addr -= busif_getBusno() << busShift;
pcieDevMapIterator dmIter;
dmIter = pcieAddrSpace[space].lower_bound(addr);
if(dmIter == pcieAddrSpace[space].end()){
if(reqId == getBridgeId())
// access is from the upstream bridge, and
// no device exist at specified config address
return pcieCompleter(CA);
// device belongs to this bus.
addr -= (dmIter->second->devInfo->device << devShift) + (dmIter->second->devInfo->function << funShift);
return targetDev->devif_confAccess(wr,addr,data,be,reqId,conf_type0, length,args);
// space is either MEM, or IO.