blob: 3ee83dd5eaa0212d4495e34b3d69124271002aba [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
//
#include "bus.h"
#include "common.h"
#include "config.h"
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/mmio-buffer.h>
#include <fbl/alloc_checker.h>
namespace pci {
// Creates the PCI bus driver instance and attempts initialization.
zx_status_t Bus::Create(zx_device_t* parent) {
zx_status_t status;
pciroot_protocol_t pciroot;
if ((status = device_get_protocol(parent, ZX_PROTOCOL_PCIROOT, &pciroot)) != ZX_OK) {
pci_errorf("failed to obtain pciroot protocol: %d!\n", status);
return status;
}
fbl::AllocChecker ac;
Bus* bus = new (&ac) Bus(parent, &pciroot);
if (!ac.check()) {
pci_errorf("failed to allocate bus object.\n");
return ZX_ERR_NO_MEMORY;
}
if ((status = bus->Initialize()) != ZX_OK) {
pci_errorf("failed to initialize bus driver: %d!\n", status);
return status;
}
// Grab the info beforehand so we can get segment/bus information from it
// and appropriately name our DDK device.
pci_platform_info_t info;
status = bus->pciroot().GetPciPlatformInfo(&info);
if (status != ZX_OK) {
pci_errorf("failed to obtain platform information: %d!\n", status);
return status;
}
// Name the bus instance with segment group and bus range, for example:
// pci[0][0:255] for a legacy pci bus in segment group 0.
char name[32];
snprintf(name, sizeof(name), "pci[%u][%u:%u]", info.segment_group, info.start_bus_num,
info.end_bus_num);
return bus->DdkAdd(name);
}
zx_status_t Bus::Initialize() {
zx_status_t status = pciroot_.GetPciPlatformInfo(&info_);
if (status != ZX_OK) {
pci_errorf("failed to obtain platform information: %d!\n", status);
return status;
}
if (info_.ecam_vmo != ZX_HANDLE_INVALID) {
if ((status = MapEcam()) != ZX_OK) {
pci_errorf("failed to map ecam: %d!\n", status);
return status;
}
}
ScanDownstream();
return ZX_OK;
}
// Maps a vmo as an mmio_buffer to be used as this Bus driver's ECAM region
// for config space access.
zx_status_t Bus::MapEcam(void) {
ZX_DEBUG_ASSERT(info_.ecam_vmo != ZX_HANDLE_INVALID);
size_t size;
zx_status_t status = zx_vmo_get_size(info_.ecam_vmo, &size);
if (status != ZX_OK) {
pci_errorf("couldn't get ecam vmo size: %d!\n", status);
return status;
}
status = mmio_buffer_init(&ecam_, 0, size, info_.ecam_vmo, ZX_CACHE_POLICY_UNCACHED);
if (status != ZX_OK) {
pci_errorf("couldn't map ecam vmo: %d!\n", status);
return status;
}
pci_infof("ecam for segment %u mapped at %p (size: %#zx)\n", info_.segment_group,
ecam_.vaddr, ecam_.size);
has_ecam_ = true;
return ZX_OK;
}
zx_status_t Bus::MakeConfig(pci_bdf_t bdf, fbl::RefPtr<Config>* config) {
if (has_ecam_) {
return MmioConfig::Create(bdf, &ecam_, info_.start_bus_num, info_.end_bus_num, config);
} else {
return ProxyConfig::Create(bdf, &pciroot_, config);
}
}
// Scan downstream starting at the start bus number provided to use by the platform.
// In the process of scanning, take note of bridges found and configure any that are
// unconfigured. In the end the Bus should have a list of all devides, and all bridges
// should have a list of references to their own downstream devices.
zx_status_t Bus::ScanDownstream(void) {
pci_infof("ScanDownstream %u:%u\n", info_.start_bus_num, info_.end_bus_num);
for (uint16_t bus_id = info_.start_bus_num; bus_id <= info_.end_bus_num; bus_id++) {
for (uint8_t dev_id = 0; dev_id < PCI_MAX_DEVICES_PER_BUS; dev_id++) {
for (uint8_t func_id = 0; func_id < PCI_MAX_FUNCTIONS_PER_DEVICE; func_id++) {
fbl::RefPtr<Config> config;
pci_bdf_t bdf = { static_cast<uint8_t>(bus_id), dev_id, func_id };
zx_status_t status = MakeConfig(bdf, &config);
if (status == ZX_OK) {
if (config->vendor_id() != 0xFFFF) {
pci_infof("found device at %02x:%02x.%1x\n", bus_id, dev_id, func_id);
}
}
}
}
}
return ZX_OK;
}
void Bus::DdkRelease() {
if (ecam_.vaddr) {
mmio_buffer_release(&ecam_);
}
delete this;
}
} // namespace pci
extern "C" zx_status_t pci_bus_bind(void* ctx, zx_device_t* parent) {
return pci::Bus::Create(parent);
}