diff options
-rw-r--r-- | core/Makefile.inc | 4 | ||||
-rw-r--r-- | core/pci-slot.c | 206 | ||||
-rw-r--r-- | core/pci.c | 77 | ||||
-rw-r--r-- | core/pcie-slot.c | 452 | ||||
-rw-r--r-- | include/opal-api.h | 10 | ||||
-rw-r--r-- | include/pci-slot.h | 255 | ||||
-rw-r--r-- | include/pci.h | 66 | ||||
-rw-r--r-- | include/platform.h | 1 | ||||
-rw-r--r-- | platforms/ibm-fsp/lxvpd.c | 1 |
9 files changed, 992 insertions, 80 deletions
diff --git a/core/Makefile.inc b/core/Makefile.inc index 5af0d7c..13b287c 100644 --- a/core/Makefile.inc +++ b/core/Makefile.inc @@ -3,8 +3,8 @@ SUBDIRS += core CORE_OBJS = relocate.o console.o stack.o init.o chip.o mem_region.o CORE_OBJS += malloc.o lock.o cpu.o utils.o fdt.o opal.o interrupts.o -CORE_OBJS += timebase.o opal-msg.o pci.o pci-opal.o fast-reboot.o -CORE_OBJS += device.o exceptions.o trace.o affinity.o vpd.o +CORE_OBJS += timebase.o opal-msg.o pci.o pci-slot.o pcie-slot.o pci-opal.o +CORE_OBJS += fast-reboot.o device.o exceptions.o trace.o affinity.o vpd.o CORE_OBJS += hostservices.o platform.o nvram.o nvram-format.o hmi.o CORE_OBJS += console-log.o ipmi.o time-utils.o pel.o pool.o errorlog.o CORE_OBJS += timer.o i2c.o rtc.o flash.o sensor.o ipmi-opal.o diff --git a/core/pci-slot.c b/core/pci-slot.c new file mode 100644 index 0000000..8735598 --- /dev/null +++ b/core/pci-slot.c @@ -0,0 +1,206 @@ +/* Copyright 2013-2016 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 <opal-msg.h> +#include <pci-cfg.h> +#include <pci.h> +#include <pci-slot.h> + +/* Debugging options */ +#define PCI_SLOT_PREFIX "PCI-SLOT-%016llx " +#define PCI_SLOT_DBG(s, fmt, a...) \ + prlog(PR_DEBUG, PCI_SLOT_PREFIX fmt, (s)->id, ##a) + +static void pci_slot_prepare_link_change(struct pci_slot *slot, bool up) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t aercap, mask; + + /* + * Mask the link down and receiver error before the link becomes + * down. Otherwise, unmask the errors when the link is up. + */ + if (pci_has_cap(pd, PCIECAP_ID_AER, true)) { + aercap = pci_cap(pd, PCIECAP_ID_AER, true); + + /* Link down error */ + pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK, + &mask); + if (up) + mask &= ~PCIECAP_AER_UE_MASK_SURPRISE_DOWN; + else + mask |= PCIECAP_AER_UE_MASK_SURPRISE_DOWN; + pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_UE_MASK, + mask); + + /* Receiver error */ + pci_cfg_read32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_MASK, + &mask); + if (up) + mask &= ~PCIECAP_AER_CE_RECVR_ERR; + else + mask |= PCIECAP_AER_CE_RECVR_ERR; + pci_cfg_write32(phb, pd->bdfn, aercap + PCIECAP_AER_CE_MASK, + mask); + } + + /* + * We're coming back from reset. We need restore bus ranges + * and reinitialize the affected bridges and devices. + */ + if (up) { + pci_restore_bridge_buses(phb, pd); + if (phb->ops->device_init) + pci_walk_dev(phb, pd, phb->ops->device_init, NULL); + } +} + +static int64_t pci_slot_sm_poll(struct pci_slot *slot) +{ + uint64_t now = mftb(); + int64_t ret; + + /* Return remaining timeout if we're still waiting */ + if (slot->delay_tgt_tb && + tb_compare(now, slot->delay_tgt_tb) == TB_ABEFOREB) + return slot->delay_tgt_tb - now; + + slot->delay_tgt_tb = 0; + switch (slot->state & PCI_SLOT_STATE_MASK) { + case PCI_SLOT_STATE_LINK: + ret = slot->ops.poll_link(slot); + break; + case PCI_SLOT_STATE_HRESET: + ret = slot->ops.hreset(slot); + break; + case PCI_SLOT_STATE_FRESET: + ret = slot->ops.freset(slot); + break; + case PCI_SLOT_STATE_PFRESET: + ret = slot->ops.pfreset(slot); + break; + case PCI_SLOT_STATE_CRESET: + ret = slot->ops.creset(slot); + break; + default: + prlog(PR_ERR, PCI_SLOT_PREFIX + "Invalid state %08x\n", slot->id, slot->state); + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_HARDWARE; + } + + return ret; +} + +void pci_slot_add_dt_properties(struct pci_slot *slot, + struct dt_node *np) +{ + /* Bail without device node */ + if (!np) + return; + + dt_add_property_cells(np, "ibm,reset-by-firmware", 1); + dt_add_property_cells(np, "ibm,slot-pluggable", slot->pluggable); + dt_add_property_cells(np, "ibm,slot-power-ctl", slot->power_ctl); + dt_add_property_cells(np, "ibm,slot-power-led-ctlled", + slot->power_led_ctl); + dt_add_property_cells(np, "ibm,slot-attn-led", slot->attn_led_ctl); + dt_add_property_cells(np, "ibm,slot-connector-type", + slot->connector_type); + dt_add_property_cells(np, "ibm,slot-card-desc", slot->card_desc); + dt_add_property_cells(np, "ibm,slot-card-mech", slot->card_mech); + dt_add_property_cells(np, "ibm,slot-wired-lanes", slot->wired_lanes); + + if (slot->ops.add_properties) + slot->ops.add_properties(slot, np); +} + +struct pci_slot *pci_slot_alloc(struct phb *phb, + struct pci_device *pd) +{ + struct pci_slot *slot = NULL; + + /* + * The function can be used to allocate either PHB slot or normal + * one. For both cases, the @phb should be always valid. + */ + if (!phb) + return NULL; + + /* + * When @pd is NULL, we're going to create a PHB slot. Otherwise, + * a normal slot will be created. Check if the specified slot + * already exists or not. + */ + slot = pd ? pd->slot : phb->slot; + if (slot) { + prlog(PR_ERR, PCI_SLOT_PREFIX "Already exists\n", slot->id); + return slot; + } + + /* Allocate memory chunk */ + slot = zalloc(sizeof(struct pci_slot)); + if (!slot) { + prlog(PR_ERR, "%s: Out of memory\n", __func__); + return NULL; + } + + /* + * The polling function sholdn't be overridden by individual + * platforms + */ + slot->phb = phb; + slot->pd = pd; + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + slot->power_state = PCI_SLOT_POWER_ON; + slot->ops.poll = pci_slot_sm_poll; + slot->ops.prepare_link_change = pci_slot_prepare_link_change; + if (!pd) { + slot->id = PCI_PHB_SLOT_ID(phb); + phb->slot = slot; + } else { + slot->id = PCI_SLOT_ID(phb, pd->bdfn); + pd->slot = slot; + } + + return slot; +} + +struct pci_slot *pci_slot_find(uint64_t id) +{ + struct phb *phb; + struct pci_device *pd; + struct pci_slot *slot; + uint64_t index; + uint16_t bdfn; + + index = PCI_SLOT_PHB_INDEX(id); + phb = pci_get_phb(index); + + /* PHB slot */ + if (!(id & PCI_SLOT_ID_PREFIX)) { + slot = phb ? phb->slot : NULL; + return slot; + } + + /* Normal PCI slot */ + bdfn = PCI_SLOT_BDFN(id); + pd = phb ? pci_find_dev(phb, bdfn) : NULL; + slot = pd ? pd->slot : NULL; + return slot; +} @@ -18,6 +18,7 @@ #include <cpu.h> #include <pci.h> #include <pci-cfg.h> +#include <pci-slot.h> #include <timebase.h> #include <device.h> #include <fsp.h> @@ -459,11 +460,33 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd) pci_cfg_write16(phb, pd->bdfn, PCI_CFG_CMD, cmd); } +/* Remove all subordinate PCI devices leading from the indicated + * PCI bus. It's used to remove all PCI devices behind one PCI + * slot at unplugging time + */ +void pci_remove_bus(struct phb *phb, struct list_head *list) +{ + struct pci_device *pd, *tmp; + + list_for_each_safe(list, pd, tmp, link) { + pci_remove_bus(phb, &pd->children); + + /* Release device node and PCI slot */ + if (pd->dn) + dt_free(pd->dn); + if (pd->slot) + free(pd->slot); + + /* Remove from parent list and release itself */ + list_del(&pd->link); + free(pd); + } +} -/* pci_scan - Perform a recursive scan of the bus at bus_number - * populating the list passed as an argument. This also - * performs the bus numbering, so it returns the largest - * bus number that was assigned. +/* Perform a recursive scan of the bus at bus_number populating + * the list passed as an argument. This also performs the bus + * numbering, so it returns the largest bus number that was + * assigned. * * Note: Eventually this might want to access some VPD information * in order to know what slots to scan and what not etc.. @@ -473,9 +496,9 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd) * XXX NOTE: We might also want to setup the PCIe MPS/MRSS properly * here as Linux may or may not do it */ -static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus, - struct list_head *list, struct pci_device *parent, - bool scan_downstream) +uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus, + struct list_head *list, struct pci_device *parent, + bool scan_downstream) { struct pci_device *pd = NULL; uint8_t dev, fn, next_bus, max_sub, save_max; @@ -592,8 +615,8 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus, /* Perform recursive scan */ if (do_scan) { - max_sub = pci_scan(phb, next_bus, max_bus, - &pd->children, pd, true); + max_sub = pci_scan_bus(phb, next_bus, max_bus, + &pd->children, pd, true); } else if (!use_max) { /* XXX Empty bridge... we leave room for hotplug * slots etc.. but we should be smarter at figuring @@ -801,7 +824,7 @@ static void pci_scan_phb(void *data) /* Scan root port and downstream ports if applicable */ PCIDBG(phb, 0, "Scanning (upstream%s)...\n", has_link ? "+downsteam" : " only"); - pci_scan(phb, 0, 0xff, &phb->devices, NULL, has_link); + pci_scan_bus(phb, 0, 0xff, &phb->devices, NULL, has_link); /* Configure MPS (Max Payload Size) for PCIe domain */ pci_walk_dev(phb, NULL, pci_get_mps, &mps); @@ -1327,12 +1350,12 @@ static void pci_print_summary_line(struct phb *phb, struct pci_device *pd, rev_class & 0xff, rev_class >> 8, cname, slotstr); } - -static void pci_add_one_node(struct phb *phb, struct pci_device *pd, - struct dt_node *parent_node, - struct pci_lsi_state *lstate, uint8_t swizzle) +static void pci_add_one_device_node(struct phb *phb, + struct pci_device *pd, + struct dt_node *parent_node, + struct pci_lsi_state *lstate, + uint8_t swizzle) { - struct pci_device *child; struct dt_node *np; const char *cname; #define MAX_NAME 256 @@ -1447,14 +1470,14 @@ static void pci_add_one_node(struct phb *phb, struct pci_device *pd, * Instead add a ranges property that explicitly translates 1:1. */ dt_add_property(np, "ranges", ranges_direct, sizeof(ranges_direct)); - - list_for_each(&pd->children, child, link) - pci_add_one_node(phb, child, np, lstate, swizzle); } -static void pci_add_nodes(struct phb *phb) +void pci_add_device_nodes(struct phb *phb, + struct list_head *list, + struct dt_node *parent_node, + struct pci_lsi_state *lstate, + uint8_t swizzle) { - struct pci_lsi_state *lstate = &phb->lstate; struct pci_device *pd; /* If the PHB has its own slot info, add them */ @@ -1462,8 +1485,15 @@ static void pci_add_nodes(struct phb *phb) pci_add_slot_properties(phb, phb->slot_info, NULL); /* Add all child devices */ - list_for_each(&phb->devices, pd, link) - pci_add_one_node(phb, pd, phb->dt_node, lstate, 0); + list_for_each(list, pd, link) { + pci_add_one_device_node(phb, pd, parent_node, + lstate, swizzle); + if (list_empty(&pd->children)) + continue; + + pci_add_device_nodes(phb, &pd->children, + pd->dn, lstate, swizzle); + } } static void __pci_reset(struct list_head *list) @@ -1544,7 +1574,8 @@ void pci_init_slots(void) for (i = 0; i < ARRAY_SIZE(phbs); i++) { if (!phbs[i]) continue; - pci_add_nodes(phbs[i]); + pci_add_device_nodes(phbs[i], &phbs[i]->devices, + phbs[i]->dt_node, &phbs[i]->lstate, 0); } /* PHB final fixup */ diff --git a/core/pcie-slot.c b/core/pcie-slot.c new file mode 100644 index 0000000..62933a4 --- /dev/null +++ b/core/pcie-slot.c @@ -0,0 +1,452 @@ +/* Copyright 2013-2016 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 <opal-msg.h> +#include <pci-cfg.h> +#include <pci.h> +#include <pci-slot.h> + +/* Debugging options */ +#define PCIE_SLOT_PREFIX "PCIE-SLOT-%016llx " +#define PCIE_SLOT_DBG(s, fmt, a...) \ + prlog(PR_DEBUG, PCIE_SLOT_PREFIX fmt, (s)->id, ##a) + +static int64_t pcie_slot_get_presence_state(struct pci_slot *slot, uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t state; + + /* The presence is always on if it's a switch upstream port */ + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { + *val = OPAL_PCI_SLOT_PRESENT; + return OPAL_SUCCESS; + } + + /* + * The presence is always on if a switch downstream port + * doesn't support slot capability according to PCIE spec. + */ + if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && + !(slot->slot_cap & PCICAP_EXP_CAP_SLOT)) { + *val = OPAL_PCI_SLOT_PRESENT; + return OPAL_SUCCESS; + } + + /* Retrieve presence status */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state); + if (state & PCICAP_EXP_SLOTSTAT_PDETECTST) + *val = OPAL_PCI_SLOT_PRESENT; + else + *val = OPAL_PCI_SLOT_EMPTY; + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_link_state(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + int16_t state; + + /* + * The link behind switch upstream port is always on + * since it doesn't have a valid link indicator. + */ + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { + *val = 1; + return OPAL_SUCCESS; + } + + /* Retrieve link width */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &state); + if (state & PCICAP_EXP_LSTAT_DLLL_ACT) + *val = ((state & PCICAP_EXP_LSTAT_WIDTH) >> 4); + else + *val = 0; + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_power_state(struct pci_slot *slot __unused, + uint8_t *val) +{ + /* The power is always on if no functionality is supported */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) + *val = PCI_SLOT_POWER_ON; + else + *val = slot->power_state; + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_attention_state(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t state; + + /* Attention is off if the capability is missing */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) { + *val = 0; + return OPAL_SUCCESS; + } + + /* Retrieve attention state */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); + state = (state & PCICAP_EXP_SLOTCTL_ATTNI) >> 6; + switch (state) { + case PCIE_INDIC_ON: + *val = PCI_SLOT_ATTN_LED_ON; + break; + case PCIE_INDIC_BLINK: + *val = PCI_SLOT_ATTN_LED_BLINK; + break; + case PCIE_INDIC_OFF: + default: + *val = PCI_SLOT_ATTN_LED_OFF; + } + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_latch_state(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t state; + + /* Latch is off if MRL sensor doesn't exist */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_MRLSENS)) { + *val = 0; + return OPAL_SUCCESS; + } + + /* Retrieve MRL sensor state */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &state); + if (state & PCICAP_EXP_SLOTSTAT_MRLSENSST) + *val = 1; + else + *val = 0; + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_set_attention_state(struct pci_slot *slot, + uint8_t val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t state; + + /* Drop the request if functionality doesn't exist */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) + return OPAL_SUCCESS; + + /* Update with the requested state */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); + state &= ~PCICAP_EXP_SLOTCTL_ATTNI; + switch (val) { + case PCI_SLOT_ATTN_LED_ON: + state |= (PCIE_INDIC_ON << 6); + break; + case PCI_SLOT_ATTN_LED_BLINK: + state |= (PCIE_INDIC_BLINK << 6); + break; + case PCI_SLOT_ATTN_LED_OFF: + state |= (PCIE_INDIC_OFF << 6); + break; + default: + prlog(PR_ERR, PCIE_SLOT_PREFIX + "Invalid attention state (0x%x)\n", slot->id, val); + return OPAL_PARAMETER; + } + + pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state); + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_set_power_state(struct pci_slot *slot, uint8_t val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t state; + + /* Drop the request if functionality doesn't exist */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) + return OPAL_SUCCESS; + + if (slot->power_state == val) + return OPAL_SUCCESS; + + pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_START); + slot->power_state = val; + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &state); + state &= ~(PCICAP_EXP_SLOTCTL_PWRCTLR | PCICAP_EXP_SLOTCTL_PWRI); + switch (val) { + case PCI_SLOT_POWER_OFF: + state |= (PCICAP_EXP_SLOTCTL_PWRCTLR | (PCIE_INDIC_OFF << 8)); + break; + case PCI_SLOT_POWER_ON: + state |= (PCIE_INDIC_ON << 8); + break; + default: + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + prlog(PR_ERR, PCIE_SLOT_PREFIX + "Invalid power state (0x%x)\n", slot->id, val); + return OPAL_PARAMETER; + } + + pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, state); + pci_slot_set_state(slot, PCI_SLOT_STATE_SPOWER_DONE); + + return OPAL_ASYNC_COMPLETION; +} + +static int64_t pcie_slot_sm_poll_link(struct pci_slot *slot) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + uint16_t val; + uint8_t presence = 0; + + switch (slot->state) { + case PCI_SLOT_STATE_LINK_START_POLL: + PCIE_SLOT_DBG(slot, "LINK: Start polling\n"); + + /* Link is down for ever without devices attached */ + if (slot->ops.get_presence_state) + slot->ops.get_presence_state(slot, &presence); + if (!presence) { + PCIE_SLOT_DBG(slot, "LINK: No adapter, end polling\n"); + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_SUCCESS; + } + + /* Enable the link without check */ + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, &val); + val &= ~PCICAP_EXP_LCTL_LINK_DIS; + pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_LCTL, val); + + /* + * If the link change report isn't supported, we expect + * the link is up and stabilized after one second. + */ + if (!(slot->link_cap & PCICAP_EXP_LCAP_DL_ACT_REP)) { + pci_slot_set_state(slot, + PCI_SLOT_STATE_LINK_DELAY_FINALIZED); + return pci_slot_set_sm_timeout(slot, secs_to_tb(1)); + } + + /* + * Poll the link state if link state change report is + * supported on the link. + */ + pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_POLLING); + slot->retries = 250; + return pci_slot_set_sm_timeout(slot, msecs_to_tb(20)); + case PCI_SLOT_STATE_LINK_DELAY_FINALIZED: + PCIE_SLOT_DBG(slot, "LINK: No link report, end polling\n"); + if (slot->ops.prepare_link_change) + slot->ops.prepare_link_change(slot, true); + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_SUCCESS; + case PCI_SLOT_STATE_LINK_POLLING: + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &val); + if (val & PCICAP_EXP_LSTAT_DLLL_ACT) { + PCIE_SLOT_DBG(slot, "LINK: Link is up, end polling\n"); + if (slot->ops.prepare_link_change) + slot->ops.prepare_link_change(slot, true); + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_SUCCESS; + } + + /* Check link state again until timeout */ + if (slot->retries-- == 0) { + prlog(PR_ERR, PCIE_SLOT_PREFIX + "LINK: Timeout waiting for up (%04x)\n", + slot->id, val); + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_SUCCESS; + } + + return pci_slot_set_sm_timeout(slot, msecs_to_tb(20)); + default: + prlog(PR_ERR, PCIE_SLOT_PREFIX + "Link: Unexpected slot state %08x\n", + slot->id, slot->state); + } + + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_HARDWARE; +} + +static void pcie_slot_reset(struct pci_slot *slot, bool assert) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint16_t ctl; + + pci_cfg_read16(phb, pd->bdfn, PCI_CFG_BRCTL, &ctl); + if (assert) + ctl |= PCI_CFG_BRCTL_SECONDARY_RESET; + else + ctl &= ~PCI_CFG_BRCTL_SECONDARY_RESET; + pci_cfg_write16(phb, pd->bdfn, PCI_CFG_BRCTL, ctl); +} + +static int64_t pcie_slot_sm_hreset(struct pci_slot *slot) +{ + switch (slot->state) { + case PCI_SLOT_STATE_NORMAL: + PCIE_SLOT_DBG(slot, "HRESET: Starts\n"); + if (slot->ops.prepare_link_change) { + PCIE_SLOT_DBG(slot, "HRESET: Prepare for link down\n"); + slot->ops.prepare_link_change(slot, false); + } + /* fall through */ + case PCI_SLOT_STATE_HRESET_START: + PCIE_SLOT_DBG(slot, "HRESET: Assert\n"); + pcie_slot_reset(slot, true); + pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_HOLD); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(250)); + case PCI_SLOT_STATE_HRESET_HOLD: + PCIE_SLOT_DBG(slot, "HRESET: Deassert\n"); + pcie_slot_reset(slot, false); + pci_slot_set_state(slot, PCI_SLOT_STATE_LINK_START_POLL); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(1800)); + default: + PCIE_SLOT_DBG(slot, "HRESET: Unexpected slot state %08x\n", + slot->state); + } + + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_HARDWARE; +} + +/* + * Usually, individual platforms need to override the power + * management methods for fundamental reset, but the hot + * reset method is commonly shared. + */ +static int64_t pcie_slot_sm_freset(struct pci_slot *slot) +{ + uint8_t power_state = PCI_SLOT_POWER_ON; + + switch (slot->state) { + case PCI_SLOT_STATE_NORMAL: + PCIE_SLOT_DBG(slot, "FRESET: Starts\n"); + if (slot->ops.prepare_link_change) + slot->ops.prepare_link_change(slot, false); + + /* Retrieve power state */ + if (slot->ops.get_power_state) { + PCIE_SLOT_DBG(slot, "FRESET: Retrieve power state\n"); + slot->ops.get_power_state(slot, &power_state); + } + + /* In power on state, power it off */ + if (power_state == PCI_SLOT_POWER_ON && + slot->ops.set_power_state) { + PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n"); + slot->ops.set_power_state(slot, PCI_SLOT_POWER_OFF); + pci_slot_set_state(slot, + PCI_SLOT_STATE_FRESET_POWER_OFF); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); + } + /* No power state change, fall through */ + case PCI_SLOT_STATE_FRESET_POWER_OFF: + PCIE_SLOT_DBG(slot, "FRESET: Power is off, turn on\n"); + if (slot->ops.set_power_state) + slot->ops.set_power_state(slot, PCI_SLOT_POWER_ON); + pci_slot_set_state(slot, PCI_SLOT_STATE_HRESET_START); + return pci_slot_set_sm_timeout(slot, msecs_to_tb(50)); + default: + prlog(PR_ERR, PCIE_SLOT_PREFIX + "FRESET: Unexpected slot state %08x\n", + slot->id, slot->state); + } + + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + return OPAL_HARDWARE; +} + +struct pci_slot *pcie_slot_create(struct phb *phb, struct pci_device *pd) +{ + struct pci_slot *slot; + uint32_t ecap; + + /* Allocate PCI slot */ + slot = pci_slot_alloc(phb, pd); + if (!slot) + return NULL; + + /* Cache the link and slot capabilities */ + if (pd) { + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_LCAP, + &slot->link_cap); + pci_cfg_read32(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCAP, + &slot->slot_cap); + } + + if ((slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_SURP) && + (slot->slot_cap & PCICAP_EXP_SLOTCAP_HPLUG_CAP)) + slot->pluggable = 1; + if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL) + slot->power_ctl = 1; + if (slot->slot_cap & PCICAP_EXP_SLOTCAP_PWRI) + slot->power_led_ctl = PCI_SLOT_PWR_LED_CTL_KERNEL; + if (slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI) + slot->attn_led_ctl = PCI_SLOT_ATTN_LED_CTL_KERNEL; + slot->wired_lanes = ((slot->link_cap & PCICAP_EXP_LCAP_MAXWDTH) >> 4); + + /* Standard slot operations */ + slot->ops.get_presence_state = pcie_slot_get_presence_state; + slot->ops.get_link_state = pcie_slot_get_link_state; + slot->ops.get_power_state = pcie_slot_get_power_state; + slot->ops.get_attention_state = pcie_slot_get_attention_state; + slot->ops.get_latch_state = pcie_slot_get_latch_state; + slot->ops.set_power_state = pcie_slot_set_power_state; + slot->ops.set_attention_state = pcie_slot_set_attention_state; + + /* + * State machine (SM) based reset stuff. The poll function is always + * unified for all cases. + */ + slot->ops.poll_link = pcie_slot_sm_poll_link; + slot->ops.hreset = pcie_slot_sm_hreset; + slot->ops.freset = pcie_slot_sm_freset; + slot->ops.pfreset = NULL; + + return slot; +} diff --git a/include/opal-api.h b/include/opal-api.h index 2f91715..b4d96b8 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -376,6 +376,16 @@ enum OpalPciMaskAction { OPAL_MASK_ERROR_TYPE = 1 }; +enum OpalPciSlotPresence { + OPAL_PCI_SLOT_EMPTY = 0, + OPAL_PCI_SLOT_PRESENT = 1 +}; + +enum OpalPciSlotPower { + OPAL_PCI_SLOT_POWER_OFF = 0, + OPAL_PCI_SLOT_POWER_ON = 1 +}; + enum OpalSlotLedType { OPAL_SLOT_LED_TYPE_ID = 0, /* IDENTIFY LED */ OPAL_SLOT_LED_TYPE_FAULT = 1, /* FAULT LED */ diff --git a/include/pci-slot.h b/include/pci-slot.h new file mode 100644 index 0000000..cf22432 --- /dev/null +++ b/include/pci-slot.h @@ -0,0 +1,255 @@ +/* Copyright 2013-2016 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. + */ + +#ifndef __PCI_SLOT_H +#define __PCI_SLOT_H + +#include <opal.h> +#include <device.h> +#include <timebase.h> +#include <timer.h> +#include <ccan/list/list.h> + +/* + * PCI Slot Info: Wired Lane Values + * + * Values 0 to 6 match slot map 1005. In case of *any* change here + * make sure to keep the lxvpd.c parsing code in sync *and* the + * corresponding label strings in pci.c + */ +#define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00 +#define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01 +#define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02 +#define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03 +#define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04 +#define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05 +#define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06 +#define PCI_SLOT_WIRED_LANES_PCIX_32 0x07 +#define PCI_SLOT_WIRED_LANES_PCIX_64 0x08 + +/* PCI Slot Info: Bus Clock Values */ +#define PCI_SLOT_BUS_CLK_RESERVED 0x00 +#define PCI_SLOT_BUS_CLK_GEN_1 0x01 +#define PCI_SLOT_BUS_CLK_GEN_2 0x02 +#define PCI_SLOT_BUS_CLK_GEN_3 0x03 + +/* PCI Slot Info: Connector Type Values */ +#define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00 +#define PCI_SLOT_CONNECTOR_PCIE_X1 0x01 +#define PCI_SLOT_CONNECTOR_PCIE_X2 0x02 +#define PCI_SLOT_CONNECTOR_PCIE_X4 0x03 +#define PCI_SLOT_CONNECTOR_PCIE_X8 0x04 +#define PCI_SLOT_CONNECTOR_PCIE_X16 0x05 +#define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */ + +/* PCI Slot Info: Card Description Values */ +#define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard */ +#define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */ +#define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */ +#define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */ +#define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */ + +/* PCI Slot Info: Mechanicals Values */ +#define PCI_SLOT_MECH_NONE 0x00 +#define PCI_SLOT_MECH_RIGHT 0x01 +#define PCI_SLOT_MECH_LEFT 0x02 +#define PCI_SLOT_MECH_RIGHT_LEFT 0x03 + +/* PCI Slot Info: Power LED Control Values */ +#define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */ +#define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */ +#define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ + +/* PCI Slot Info: ATTN LED Control Values */ +#define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */ +#define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */ +#define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ + +/* Attention LED */ +#define PCI_SLOT_ATTN_LED_OFF 0 +#define PCI_SLOT_ATTN_LED_ON 1 +#define PCI_SLOT_ATTN_LED_BLINK 2 + +/* Power state */ +#define PCI_SLOT_POWER_OFF 0 +#define PCI_SLOT_POWER_ON 1 + +/* + * We have hard and soft reset for slot. Hard reset requires + * power-off and then power-on, but soft reset only resets + * secondary bus. + */ +struct pci_slot; +struct pci_slot_ops { + /* For slot management */ + int64_t (*get_presence_state)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_link_state)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_power_state)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_attention_state)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_latch_state)(struct pci_slot *slot, uint8_t *val); + int64_t (*set_power_state)(struct pci_slot *slot, uint8_t val); + int64_t (*set_attention_state)(struct pci_slot *slot, uint8_t val); + + /* SM based functions for reset */ + void (*prepare_link_change)(struct pci_slot *slot, bool is_up); + int64_t (*poll_link)(struct pci_slot *slot); + int64_t (*creset)(struct pci_slot *slot); + int64_t (*freset)(struct pci_slot *slot); + int64_t (*pfreset)(struct pci_slot *slot); + int64_t (*hreset)(struct pci_slot *slot); + int64_t (*poll)(struct pci_slot *slot); + + /* Auxillary functions */ + void (*add_properties)(struct pci_slot *slot, struct dt_node *np); +}; + +/* + * The PCI slot state is split up into base and number. With this + * design, the individual platforms can introduce their own PCI + * slot states with addition to the base. Eventually, the base + * state can be recognized by PCI slot core. + */ +#define PCI_SLOT_STATE_MASK 0xFFFFFF00 +#define PCI_SLOT_STATE_NORMAL 0x00000000 +#define PCI_SLOT_STATE_LINK 0x00000100 +#define PCI_SLOT_STATE_LINK_START_POLL 0x00000101 +#define PCI_SLOT_STATE_LINK_DELAY_FINALIZED 0x00000102 +#define PCI_SLOT_STATE_LINK_POLLING 0x00000103 +#define PCI_SLOT_STATE_HRESET 0x00000200 +#define PCI_SLOT_STATE_HRESET_START 0x00000201 +#define PCI_SLOT_STATE_HRESET_HOLD 0x00000202 +#define PCI_SLOT_STATE_FRESET 0x00000300 +#define PCI_SLOT_STATE_FRESET_POWER_OFF 0x00000301 +#define PCI_SLOT_STATE_PFRESET 0x00000400 +#define PCI_SLOT_STATE_PFRESET_START 0x00000401 +#define PCI_SLOT_STATE_CRESET 0x00000500 +#define PCI_SLOT_STATE_CRESET_START 0x00000501 +#define PCI_SLOT_STATE_GPOWER 0x00000600 +#define PCI_SLOT_STATE_GPOWER_START 0x00000601 +#define PCI_SLOT_STATE_SPOWER 0x00000700 +#define PCI_SLOT_STATE_SPOWER_START 0x00000701 +#define PCI_SLOT_STATE_SPOWER_DONE 0x00000702 +#define PCI_SLOT_STATE_GPRESENCE 0x00000800 +#define PCI_SLOT_STATE_GPRESENCE_START 0x00000801 + + +struct pci_slot { + uint32_t flags; +#define PCI_SLOT_FLAG_BOOTUP 0x1 + + struct phb *phb; + struct pci_device *pd; + + /* Identifier */ + uint64_t id; + struct timer timer; + uint64_t async_token; + uint8_t power_state; + + /* Slot information */ + uint8_t pluggable; + uint8_t power_ctl; + uint8_t power_led_ctl; + uint8_t attn_led_ctl; + uint8_t connector_type; + uint8_t card_desc; + uint8_t card_mech; + uint8_t wired_lanes; + + /* + * PCI slot is driven by state machine with polling function. + * @delay_tgt_tb tracks the current delay while @retries has + * the left rounds of delays. They should be set prior to + * switching next PCI slot state and changed (decreased) + * accordingly in the polling function. + */ + uint32_t state; + uint32_t retry_state; + uint32_t link_cap; + uint32_t slot_cap; + uint64_t delay_tgt_tb; + uint64_t retries; + struct pci_slot_ops ops; + void *data; +}; + +#define PCI_SLOT_ID_PREFIX 0x8000000000000000 +#define PCI_SLOT_ID(phb, bdfn) \ + (PCI_SLOT_ID_PREFIX | ((uint64_t)(bdfn) << 16) | (phb)->opal_id) +#define PCI_PHB_SLOT_ID(phb) ((phb)->opal_id) +#define PCI_SLOT_PHB_INDEX(id) ((id) & 0xfffful) +#define PCI_SLOT_BDFN(id) (((id) >> 16) & 0xfffful) + +static inline uint32_t pci_slot_add_flags(struct pci_slot *slot, + uint32_t flags) +{ + uint32_t old = 0; + + if (slot) { + old = slot->flags; + slot->flags |= flags; + } + + return old; +} + +static inline bool pci_slot_has_flags(struct pci_slot *slot, + uint32_t flags) +{ + if (!slot) + return false; + + if ((slot->flags & flags) == flags) + return true; + + return false; +} + +static inline uint32_t pci_slot_remove_flags(struct pci_slot *slot, + uint32_t flags) +{ + uint32_t old = 0; + + if (slot) { + old = slot->flags; + slot->flags &= ~flags; + } + + return old; +} + +static inline void pci_slot_set_state(struct pci_slot *slot, uint32_t state) +{ + if (slot) + slot->state = state; +} + +static inline uint64_t pci_slot_set_sm_timeout(struct pci_slot *slot, + uint64_t dur) +{ + if (slot) + slot->delay_tgt_tb = mftb() + dur; + return dur; +} + +extern struct pci_slot *pci_slot_alloc(struct phb *phb, + struct pci_device *pd); +extern struct pci_slot *pcie_slot_create(struct phb *phb, + struct pci_device *pd); +extern void pci_slot_add_dt_properties(struct pci_slot *slot, + struct dt_node *np); +extern struct pci_slot *pci_slot_find(uint64_t id); +#endif /* __PCI_SLOT_H */ diff --git a/include/pci.h b/include/pci.h index 69be49e..486b712 100644 --- a/include/pci.h +++ b/include/pci.h @@ -22,60 +22,6 @@ #include <lock.h> #include <ccan/list/list.h> -/* PCI Slot Info: Wired Lane Values - * - * Values 0 to 6 match slot map 1005. In case of *any* change here - * make sure to keep the lxvpd.c parsing code in sync *and* the - * corresponding label strings in pci.c - */ -#define PCI_SLOT_WIRED_LANES_UNKNOWN 0x00 -#define PCI_SLOT_WIRED_LANES_PCIE_X1 0x01 -#define PCI_SLOT_WIRED_LANES_PCIE_X2 0x02 -#define PCI_SLOT_WIRED_LANES_PCIE_X4 0x03 -#define PCI_SLOT_WIRED_LANES_PCIE_X8 0x04 -#define PCI_SLOT_WIRED_LANES_PCIE_X16 0x05 -#define PCI_SLOT_WIRED_LANES_PCIE_X32 0x06 -#define PCI_SLOT_WIRED_LANES_PCIX_32 0x07 -#define PCI_SLOT_WIRED_LANES_PCIX_64 0x08 - -/* PCI Slot Info: Bus Clock Values */ -#define PCI_SLOT_BUS_CLK_RESERVED 0x00 -#define PCI_SLOT_BUS_CLK_GEN_1 0x01 -#define PCI_SLOT_BUS_CLK_GEN_2 0x02 -#define PCI_SLOT_BUS_CLK_GEN_3 0x03 - -/* PCI Slot Info: Connector Type Values */ -#define PCI_SLOT_CONNECTOR_PCIE_EMBED 0x00 -#define PCI_SLOT_CONNECTOR_PCIE_X1 0x01 -#define PCI_SLOT_CONNECTOR_PCIE_X2 0x02 -#define PCI_SLOT_CONNECTOR_PCIE_X4 0x03 -#define PCI_SLOT_CONNECTOR_PCIE_X8 0x04 -#define PCI_SLOT_CONNECTOR_PCIE_X16 0x05 -#define PCI_SLOT_CONNECTOR_PCIE_NS 0x0E /* Non-Standard */ - -/* PCI Slot Info: Card Description Values */ -#define PCI_SLOT_DESC_NON_STANDARD 0x00 /* Embed/Non-Standard Connector */ -#define PCI_SLOT_DESC_PCIE_FH_FL 0x00 /* Full Height, Full Length */ -#define PCI_SLOT_DESC_PCIE_FH_HL 0x01 /* Full Height, Half Length */ -#define PCI_SLOT_DESC_PCIE_HH_FL 0x02 /* Half Height, Full Length */ -#define PCI_SLOT_DESC_PCIE_HH_HL 0x03 /* Half Height, Half Length */ - -/* PCI Slot Info: Mechanicals Values */ -#define PCI_SLOT_MECH_NONE 0x00 -#define PCI_SLOT_MECH_RIGHT 0x01 -#define PCI_SLOT_MECH_LEFT 0x02 -#define PCI_SLOT_MECH_RIGHT_LEFT 0x03 - -/* PCI Slot Info: Power LED Control Values */ -#define PCI_SLOT_PWR_LED_CTL_NONE 0x00 /* No Control */ -#define PCI_SLOT_PWR_LED_CTL_FSP 0x01 /* FSP Controlled */ -#define PCI_SLOT_PWR_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ - -/* PCI Slot Info: ATTN LED Control Values */ -#define PCI_SLOT_ATTN_LED_CTL_NONE 0x00 /* No Control */ -#define PCI_SLOT_ATTN_LED_CTL_FSP 0x01 /* FSP Controlled */ -#define PCI_SLOT_ATTN_LED_CTL_KERNEL 0x02 /* Kernel Controlled */ - /* PCI Slot Entry Information */ struct pci_slot_info { char label[16]; @@ -151,6 +97,7 @@ struct pci_device { struct list_head pcrf; struct dt_node *dn; + struct pci_slot *slot; struct pci_slot_info *slot_info; struct pci_device *parent; struct list_head children; @@ -462,6 +409,7 @@ struct phb { uint32_t mps; /* PCI-X only slot info, for PCI-E this is in the RC bridge */ + struct pci_slot *slot; struct pci_slot_info *slot_info; /* Base location code used to generate the children one */ @@ -519,7 +467,15 @@ static inline int64_t pci_cfg_write32(struct phb *phb, uint32_t bdfn, } /* Utilities */ - +extern void pci_remove_bus(struct phb *phb, struct list_head *list); +extern uint8_t pci_scan_bus(struct phb *phb, uint8_t bus, uint8_t max_bus, + struct list_head *list, struct pci_device *parent, + bool scan_downstream); +extern void pci_add_device_nodes(struct phb *phb, + struct list_head *list, + struct dt_node *parent_node, + struct pci_lsi_state *lstate, + uint8_t swizzle); extern int64_t pci_find_cap(struct phb *phb, uint16_t bdfn, uint8_t cap); extern int64_t pci_find_ecap(struct phb *phb, uint16_t bdfn, uint16_t cap, uint8_t *version); diff --git a/include/platform.h b/include/platform.h index f1bdc30..d07994f 100644 --- a/include/platform.h +++ b/include/platform.h @@ -20,6 +20,7 @@ /* Some fwd declarations for types used further down */ struct phb; struct pci_device; +struct pci_slot; struct errorlog; enum resource_id { diff --git a/platforms/ibm-fsp/lxvpd.c b/platforms/ibm-fsp/lxvpd.c index 90c9e09..542c49a 100644 --- a/platforms/ibm-fsp/lxvpd.c +++ b/platforms/ibm-fsp/lxvpd.c @@ -24,6 +24,7 @@ #include <vpd.h> #include <pci.h> #include <pci-cfg.h> +#include <pci-slot.h> #include "lxvpd.h" |