Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / pcie_bus / pcie_bus.cc
// ========== 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 ============================================
#include "pcie_bus.h"
//////////////////module functions////////////////
// print interesting info about this module
void
pcieBus::modinfo(){
pcieDevMapIterator dmIter;
printf(" bridge=<%s>\n", bridgeName);
for(uint8_t i = 0; i < 3; i++){
printf("%s",pcie_space_name((pcie_space)i));
printf("::devices:\n");
for(dmIter = pcieAddrSpace[i].begin(); dmIter != pcieAddrSpace[i].end(); dmIter++)
dmIter->second->print();
}
}
// parse an arg
bool
pcieBus::parse_arg(const char *arg){
if (argval("bridge", arg, &bridgeName))
debug_more("%s: bridge=<%s>\n", HERE, bridgeName);
else
return false;
return true;
}
void
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);
//if (bridgeIf)
// debug_info("%s: bridge <%s> connected\n", HERE, bridgeName);
//else
// debug_err("%s: ERROR: bridge <%s> has no interface\n", HERE, bridgeName);
}
}
void
pcieBus::module_deleted(mmi_instance_t, const char *target_name){
if (!strcmp(bridgeName, target_name)) {
debug_info("%s: bridge <%s> disconnected\n", HERE, bridgeName);
bridgeMod = 0;
bridgeIf = 0;
}else{
busif_deleteDevice(target_name);
}
}
void *
pcieBus::get_interface(const char *name){
if (!strcmp(name, PCIE_BUS_INTERFACE))
return (pcieBusIf*)this;
return 0;
}
bool
pcieBus::check_args(){
if (!bridgeName) {
debug_err("%s: ERROR: must specify a host bridge\n", HERE);
return false;
}
return true;
}
const char *
pcieBus::get_help(){
return Module::get_help_string();
}
const char *
Module::get_help_string(){
return "new pcie bus impl";
}
Module *
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){}
pcieBus::~pcieBus(){
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);
if(dev_instance == 0){
debug_err("%s: Device named <%s> is not a module\n", HERE, devname);
return false;
}
if(searchDevInfoListByName(devname)){
debug_err("%s: Device named <%s> already exists\n", HERE, devname);
return false;
}
if(searchDevInfoListByDevFun(device,function)){
debug_err("%s: Can't install <%s>: device already exists at dev=%d fun=%d\n",\
HERE, devname, device, function);
return false;
}
genericPcieDevIf * dI = (genericPcieDevIf*)mmi_get_interface(dev_instance,GENERIC_PCIE_DEV_INTERFACE);
if(dI == 0){
debug_err("%s: Device named <%s> has no device interface\n", HERE, devname);
return false;
}
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);
return false;
}
devInfoList.push_back(devInfo);
return true;
}
bool
pcieBus::busif_deleteDevice(const char *devname){
pcieDevInfo * pdi = searchDevInfoListByName(devname);
if(pdi){
removeMap(pdi);
devInfoList.remove(pdi);
delete(pdi);
return true;
}else{
debug_err("%s: cannot remove <%s>. non - existent device !!\n",HERE,devname);
return false;
}
}
bool pcieBus::busif_map(const char *devname, pcie_space space, uint64_t base, uint64_t size){
if(size == 0)
return true;
pcieDevInfo * pdi = searchDevInfoListByName(devname);
if(pdi){
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);
return false;
}
return true;
}else{
debug_err("busif_map: device <%s> is not added to bus list\n",devname);
return false;
}
}
bool pcieBus::busif_unmap(const char *devname, pcie_space space,uint64_t base){
pcieDevInfo * pdi = searchDevInfoListByName(devname);
if(pdi){
if(!removeSpace(base, space)){
debug_err("busif_unmap: can not unmap space for device <%s>. map does not exist!!\n", devname);
return false;
}
return true;
}else{
debug_err("busif_unmap: device <%s> is not added to bus list\n",devname);
return false;
}
}
int pcieBus::busif_getBusno(){
int bus = bridgeIf->devif_getBusNo();
assert(bus != -1);
return bus;
}
pcieCompleter pcieBus::busif_msgAccess(uint8_t messageCode, msgRouting route, uint16_t reqId, SAM_DeviceId *id,\
uint64_t tarIdOrAddr, \
void * data, uint16_t length, \
uint16_t vendorId, uint32_t vendor_data, tlp_X args){
switch(route){
case routed_to_RC:
// 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, \
vendor_data, args, id);
case routed_by_addr:
// 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
// message for now.
debug_err("%s: address routing unsupported \n", getName());
break;
case routed_by_id:
{
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, \
vendor_data, args, id);
}
// target device on this bus segment
pcieDevInfo* devInfo = searchDevInfoListByDevFun(tarIdOrAddr >> 3 & 0x1f, tarIdOrAddr & 0x7);
if(devInfo){
return devInfo->devIf->devif_msgAccess(messageCode, route,tarIdOrAddr,reqId, data, length, \
vendorId, vendor_data, args, id);
}else{
debug_err("error %s:busif_msgAccess() - target %llx not present on this bus segment",getName(),tarIdOrAddr);
return pcieCompleter(SIM_FAIL);
}
}
case broadcast_from_RC:
{
// 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);
}
break;
}
case local:
// 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, \
vendor_data, args, id);
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, \
vendor_data, args, id);
default:
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);
if(space == PCIE_CFG){
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)
continue;
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);
}
}
if(space == PCIE_CFG)
// 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);
targetDev = bridgeIf;
}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 = bridgeIf;
}else{
targetDev = dmIter->second->devInfo->devIf;
if(debug_level >= 2)
dmIter->second->print();
}
switch(space){
case PCIE_CFG:
// 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);
case PCIE_MEM:
return targetDev->devif_memAccess(wr,addr,am_xt,data,length,be,reqId,args,id);
case PCIE_IO:
return targetDev->devif_ioAccess(wr,addr,data,be,reqId,length,args,id);
default:
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);
if(di)
return di->instance;
else
return 0;
}
/*
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);
if(space == PCIE_CFG){
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);
}else{
// 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)
continue;
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);
}
}else{
// 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);
}else{
// device belongs to this bus.
if(debug_level >= 2)
dmIter->second->print();
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.
}
*/