Initial commit of OpenSPARC T2 architecture model.
[OpenSPARC-T2-SAM] / sam-t2 / sam / devices / pci_bus / pci_bus.cc
// ========== Copyright Header Begin ==========================================
//
// OpenSPARC T2 Processor File: pci_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 "pci_bus.h"
//////////////////module functions////////////////
// print interesting info about this module
void
pciBus::modinfo(){
pciDevMapIterator dmIter;
printf(" bridge=<%s>\n", bridgeName);
for(uint8_t i = 0; i < PCI_NSPACES; i++){
printf("%s",pci_space_name((pci_space_t)i));
printf("::devices:\n");
for(dmIter = pciAddrSpace[i].begin(); dmIter != pciAddrSpace[i].end(); dmIter++)
dmIter->second->print();
}
}
// parse an arg
bool
pciBus::parse_arg(const char *arg){
if (argval("bridge", arg, &bridgeName))
debug_more("%s: bridge=<%s>\n", HERE, bridgeName);
else
return false;
return true;
}
void
//pciBus::module_added(mmi_instance_t, const char *target_name){
pciBus::module_added(mmi_instance_t target_mod, const char *target_name){
if (!strcmp(bridgeName, target_name)) {
bridgeMod = target_mod;//mmi_get_module(bridgeName);
bridgeIf = (pciBridgeIf*) mmi_get_interface(bridgeMod, PCI_BRIDGE_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
//pciBus::module_deleted(mmi_instance_t, const char *target_name){
pciBus::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_delete_device(target_name);
}
}
void *
pciBus::get_interface(const char *name){
if (!strcmp(name, PCI_BUS_INTERFACE))
return (pciBusIf*)this;
return 0;
}
bool
pciBus::check_args(){
if (!bridgeName) {
debug_err("%s: ERROR: must specify a host bridge\n", HERE);
return false;
}
return true;
}
const char *
pciBus::get_help(){
return Module::get_help_string();
}
const char *
Module::get_help_string(){
return "new pci bus impl";
}
Module *
Module::create(const char *_modname, const char *_instance_name){
return new pciBus(_modname, _instance_name);
}
pciBus::pciBus(const char *_modname, const char *_instance_name)
: Module(_modname, _instance_name),
bridgeName(0), bridgeMod(0), bridgeIf(0){}
pciBus::~pciBus(){
debug_more("%s: destructor\n", HERE);
}
/////////////////////exported interface functions///////////////
bool pciBus::busif_add_device(const char *devname, uint8_t device, uint8_t function){
debug_info("%s: busif_add_device: <%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;
}
genericPciDevIf * dI = (genericPciDevIf*)mmi_get_interface(dev_instance,PCI_GENERIC_DEV_INTERFACE);
if(dI == 0){
debug_err("%s: Device named <%s> has no device interface\n", HERE, devname);
return false;
}
pciDevInfo* devInfo = new pciDevInfo(devname, dev_instance, dI, device, function);
uint64_t base = device << 11 | function << 8;
//map the configuration space. this space is always accessible and cannot be unmapped etc.
if(!addSpace(new pciRange(base, base + 0xff, devInfo),PCI_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
pciBus::busif_delete_device(const char *devname){
pciDevInfo * 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 pciBus::busif_map(const char *devname, pci_space_t space, uint64_t base, uint64_t size){
if(size == 0)
return true;
pciDevInfo * pdi = searchDevInfoListByName(devname);
if(pdi){
if(!addSpace(new pciRange(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 pciBus::busif_unmap(const char *devname, pci_space_t space,uint64_t base){
pciDevInfo * 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;
}
}
// interrupts
int
pciBus::busif_add_interrupt(int dev_num, int dev_type, int *slot_irl, bool isMultiFunction){
if (bridgeIf)
return bridgeIf->busif_add_interrupt(getInstance(), dev_num, dev_type,slot_irl,isMultiFunction);
return -1;
}
int
pciBus::busif_free_interrupt(int dev_number){
if (bridgeIf)
return bridgeIf->busif_free_interrupt(getInstance(),dev_number);
return -1;
}
bool
pciBus::busif_interrupt_in(bool set,int dev_num, int line, SAM_DeviceId *id)
{
if (bridgeIf){
bridgeIf->busif_interrupt_in(set,getInstance(),dev_num,line, id);
return true;
}
return false;
}
uint64_t
pciBus::busif_get_lowest_base(pci_space_t space, uint64_t sz){
pciDevMapIterator dmIter;
dmIter = pciAddrSpace[space].begin();
if(dmIter == pciAddrSpace[space].end()){
debug_info("%s:returning address 0x%llx, size %llx, space %s\n",getName(),0,sz,pci_space_name(space));
return 0; // there is no other mapped device.
}
uint64_t end_prev = dmIter->second->end;
uint64_t base = 0;
dmIter++;
for(; dmIter !=pciAddrSpace[space].end(); dmIter++){
uint64_t base_cur = dmIter->second->base;
base = end_prev + 1;
// align base to 'sz' bytes
if(base % sz != 0)
base = base + (sz - base%sz);
if( (base_cur - base ) >= sz)
debug_info("%s:returning address %llx, size%llx, space %s\n",getName(),base,sz,pci_space_name(space));
return base;
end_prev = dmIter->second->end;
}
// no fit found till the end of list.
base = end_prev + 1;
if(base % sz != 0)
base = base + (sz - base%sz);
debug_info("%s:returning address %llx, size%llx, space %s\n",getName(),base,sz,pci_space_name(space));
return base;
}
pciXactnStatus
pciBus::busif_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){
debug_info("%s: %s %s: paddr %llx offset %llx size %x\n", HERE, wr?"WRITE":"READ ", pci_space_name(space), paddr, offset, size);
if(space == PCI_CFG){
int target_bus = offset >> 16 & 0xff;
if(target_bus != busif_get_busno()){
printf("%s: type 1 transactions not supported on pci bus\n",getName());
return MASTER_ABORT;
}
offset -= busif_get_busno() << 16;
}
pciDevMapIterator dmIter;
dmIter = pciAddrSpace[space].lower_bound(offset);
//dmIter->second->print();
if(dmIter == pciAddrSpace[space].end()){
debug_more("unmapped access space = %s paddr=%llx, offset=%llx\n",pci_space_name(space), paddr,offset);
return MASTER_ABORT;
}else if(dmIter->second->end < offset){
debug_more("unmapped access space = %s paddr=%llx, offset=%llx\n",pci_space_name(space), paddr,offset);
return MASTER_ABORT;
}else{
if(debug_level >= 2)
dmIter->second->print();
//return (dmIter->second->devInfo->devIf->devif_access_w_size(space, paddr, (offset - dmIter->second->base), wr, buf, size));
return (dmIter->second->devInfo->devIf->devif_access_w_size(space, paddr, offset, wr, buf, size, id));
}
}
pciXactnStatus pciBus::busif_access_w_byte_enables(pci_space_t space, uint64_t paddr, uint64_t offset, bool_t wr, uint64_t* buf, uint8_t byte_enable, SAM_DeviceId * id){
debug_info("%s: %s %s: paddr %llx offset %llx byte_enable %x\n", HERE, wr?"WRITE":"READ ", pci_space_name(space), paddr, offset, byte_enable);
pciDevMapIterator dmIter;
dmIter = pciAddrSpace[space].upper_bound(offset);
if(dmIter == pciAddrSpace[space].end()){
debug_err("unmapped access space = %s paddr=%llx\n",pci_space_name(space), paddr);
return MASTER_ABORT;
}else if(dmIter->second->end < offset){
debug_err("unmapped access space = %s paddr=%llx\n",pci_space_name(space), paddr);
return MASTER_ABORT;
}else{
if(debug_level >= 2)
dmIter->second->print();
if(space == PCI_CFG)
offset -= busif_get_busno() << 16;
return (dmIter->second->devInfo->devIf->devif_access_w_size(space, paddr, (offset - dmIter->second->base), wr, buf, byte_enable, id));
}
}
pciXactnStatus
pciBus::busif_dma(bool wr, uint64_t vaddr, void *data, long count, SAM_DeviceId *id){
debug_info("%s: busif_dma_%s: vaddr 0x%llx count 0x%x\n", HERE, wr?"write":"read",vaddr, count);
assert(bridgeIf);
if(wr)
bridgeIf->busif_dma_out(vaddr, data, count, getInstance(), 0, id);
else
bridgeIf->busif_dma_in(vaddr, data, count, getInstance(), 0, id);
return SUCCESS;
}
pciXactnStatus pciBus::busif_special_cycle(uint16_t mssg, uint16_t data){
list<pciDevInfo *>::iterator i;
for(i = devInfoList.begin(); i != devInfoList.end(); i++)
(*i)->devIf->devif_special_cycle(mssg,data);
return MASTER_ABORT;
}
pciXactnStatus pciBus::busif_intr_ack(uint64_t * data){
list<pciDevInfo *>::iterator i;
for(i = devInfoList.begin(); i != devInfoList.end(); i++)
(*i)->devIf->devif_intr_ack(data);
return SUCCESS;
}