271 lines
6.1 KiB
C
271 lines
6.1 KiB
C
/* Copyright 2015 IBM Corp.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
* implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#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);
|
|
}
|