blob: 622483a15f8bac8e0bdab288c0abf5b7ff9f5aa4 [file] [log] [blame]
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
/* Copyright 2015-2018 IBM Corp. */
#include <skiboot.h>
#include <device.h>
#include <console.h>
#include <chip.h>
#include <pci-cfg.h>
#include <pci.h>
#include <pci-slot.h>
#include "astbmc.h"
static const struct slot_table_entry *slot_top_table;
void slot_table_init(const struct slot_table_entry *top_table)
{
slot_top_table = top_table;
}
static const struct slot_table_entry *match_slot_phb_entry(struct phb *phb)
{
uint32_t chip_id = dt_get_chip_id(phb->dt_node);
uint32_t phb_idx = dt_prop_get_u32_def(phb->dt_node,
"ibm,phb-index", 0);
const struct slot_table_entry *ent;
if (!slot_top_table)
return NULL;
for (ent = slot_top_table; ent->etype != st_end; ent++) {
if (ent->etype != st_phb) {
prerror("SLOT: Bad DEV entry type in table !\n");
continue;
}
if (ent->location == ST_LOC_PHB(chip_id, phb_idx))
return ent;
}
return NULL;
}
static const struct slot_table_entry *match_slot_dev_entry(struct phb *phb,
struct pci_device *pd)
{
const struct slot_table_entry *parent, *ent;
uint32_t bdfn;
/* Find a parent recursively */
if (pd->parent)
parent = match_slot_dev_entry(phb, pd->parent);
else {
/* No parent, this is a root complex, find the PHB */
parent = match_slot_phb_entry(phb);
}
/* No parent ? Oops ... */
if (!parent || !parent->children)
return NULL;
for (ent = parent->children; ent->etype != st_end; ent++) {
if (ent->etype == st_phb) {
prerror("SLOT: Bad PHB entry type in table !\n");
continue;
}
/* NPU slots match on device, not function */
if (ent->etype == st_npu_slot)
bdfn = pd->bdfn & 0xf8;
else
bdfn = pd->bdfn & 0xff;
if (ent->location == bdfn)
return ent;
}
return NULL;
}
static void slot_table_add_properties(struct pci_slot *slot,
struct dt_node *np)
{
struct slot_table_entry *ent = slot->data;
if (ent)
pci_slot_add_loc(slot, np, ent->name);
else
pci_slot_add_loc(slot, np, NULL);
}
void slot_table_add_slot_info(struct pci_device *pd,
const struct slot_table_entry *ent)
{
struct pci_slot *slot;
if (!ent || !ent->name) {
slot = pcie_slot_create_dynamic(pd->phb, pd);
if (slot) {
slot->ops.add_properties = slot_table_add_properties;
slot->pluggable = true;
}
return;
}
slot = pcie_slot_create(pd->phb, pd);
assert(slot);
slot->pluggable = !!(ent->etype == st_pluggable_slot);
slot->ops.add_properties = slot_table_add_properties;
slot->power_limit = ent->power_limit;
slot->data = (void *)ent;
}
void slot_table_get_slot_info(struct phb *phb, struct pci_device *pd)
{
const struct slot_table_entry *ent;
if (!pd || pd->slot)
return;
ent = match_slot_dev_entry(phb, pd);
slot_table_add_slot_info(pd, ent);
}
static void dt_slot_add_properties(struct pci_slot *slot,
struct dt_node *np)
{
struct dt_node *slot_np = slot->data;
const char *label = NULL;
if (slot_np)
label = dt_prop_get_def(slot_np, "ibm,slot-label", NULL);
pci_slot_add_loc(slot, np, label);
}
void dt_slot_get_slot_info(struct phb *phb, struct pci_device *pd)
{
struct dt_node *slot_np;
struct pci_slot *slot;
const char *name = NULL;
uint32_t power_limit = 0;
bool pluggable = false;
if (!pd || pd->slot)
return;
slot_np = map_pci_dev_to_slot(phb, pd);
if (slot_np) {
pluggable = dt_has_node_property(slot_np,
"ibm,pluggable", NULL);
power_limit = dt_prop_get_u32_def(slot_np,
"ibm,power-limit", 0);
name = dt_prop_get_def(slot_np, "ibm,slot-label", NULL);
}
if (!slot_np || !name) {
slot = pcie_slot_create_dynamic(phb, pd);
if (slot) {
slot->ops.add_properties = dt_slot_add_properties;
slot->pluggable = true;
slot->data = (void *)slot_np;
}
return;
}
slot = pcie_slot_create(phb, pd);
assert(slot);
slot->ops.add_properties = dt_slot_add_properties;
slot->pluggable = pluggable;
slot->power_limit = power_limit;
slot->data = (void *)slot_np;
}
static int __pci_find_dev_by_location(struct phb *phb,
struct pci_device *pd, void *userdata)
{
uint16_t location = *((uint16_t *)userdata);
if (!phb || !pd)
return 0;
if ((pd->bdfn & 0xff) == location)
return 1;
return 0;
}
static struct pci_device *pci_find_dev_by_location(struct phb *phb, uint16_t location)
{
return pci_walk_dev(phb, NULL, __pci_find_dev_by_location, &location);
}
static struct phb* get_phb_by_location(uint32_t location)
{
struct phb *phb = NULL;
uint32_t chip_id, phb_idx;
for_each_phb(phb) {
chip_id = dt_get_chip_id(phb->dt_node);
phb_idx = dt_prop_get_u32_def(phb->dt_node,
"ibm,phb-index", 0);
if (location == ST_LOC_PHB(chip_id, phb_idx))
break;
}
return phb;
}
static int check_slot_table(struct phb *phb,
const struct slot_table_entry *parent)
{
const struct slot_table_entry *ent;
struct pci_device *dev = NULL;
int r = 0;
if (parent == NULL)
return 0;
for (ent = parent; ent->etype != st_end; ent++) {
switch (ent->etype) {
case st_phb:
phb = get_phb_by_location(ent->location);
if (!phb) {
prlog(PR_ERR, "PCI: PHB %s (%x) not found\n",
ent->name, ent->location);
r++;
}
break;
case st_pluggable_slot:
case st_builtin_dev:
if (!phb)
break;
phb_lock(phb);
dev = pci_find_dev_by_location(phb, ent->location);
phb_unlock(phb);
if (!dev) {
prlog(PR_ERR, "PCI: built-in device not found: %s (loc: %x)\n",
ent->name, ent->location);
r++;
}
break;
case st_end:
case st_npu_slot:
break;
}
if (ent->children)
r+= check_slot_table(phb, ent->children);
}
return r;
}
void check_all_slot_table(void)
{
if (!slot_top_table)
return;
prlog(PR_DEBUG, "PCI: Checking slot table against detected devices\n");
check_slot_table(NULL, slot_top_table);
}