From patchwork Tue May 3 05:04:34 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gavin Shan X-Patchwork-Id: 617767 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.ozlabs.org (lists.ozlabs.org [103.22.144.68]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qzTvS1PFlz9t4k for ; Tue, 3 May 2016 15:17:40 +1000 (AEST) Received: from ozlabs.org (lists.ozlabs.org [IPv6:2401:3900:2:1::3]) by lists.ozlabs.org (Postfix) with ESMTP id 3qzTvS05JjzDqdv for ; Tue, 3 May 2016 15:17:40 +1000 (AEST) X-Original-To: skiboot@lists.ozlabs.org Delivered-To: skiboot@lists.ozlabs.org Received: from e23smtp03.au.ibm.com (e23smtp03.au.ibm.com [202.81.31.145]) (using TLSv1.2 with cipher CAMELLIA256-SHA (256/256 bits)) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 3qzTt05dfvzDqZw for ; Tue, 3 May 2016 15:16:24 +1000 (AEST) Received: from localhost by e23smtp03.au.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Tue, 3 May 2016 15:06:09 +1000 Received: from d23dlp02.au.ibm.com (202.81.31.213) by e23smtp03.au.ibm.com (202.81.31.209) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Tue, 3 May 2016 15:06:07 +1000 X-IBM-Helo: d23dlp02.au.ibm.com X-IBM-MailFrom: gwshan@linux.vnet.ibm.com X-IBM-RcptTo: skiboot@lists.ozlabs.org Received: from d23relay08.au.ibm.com (d23relay08.au.ibm.com [9.185.71.33]) by d23dlp02.au.ibm.com (Postfix) with ESMTP id C70C32BB0057 for ; Tue, 3 May 2016 15:06:06 +1000 (EST) Received: from d23av03.au.ibm.com (d23av03.au.ibm.com [9.190.234.97]) by d23relay08.au.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id u4355w2F66322624 for ; Tue, 3 May 2016 15:06:06 +1000 Received: from d23av03.au.ibm.com (localhost [127.0.0.1]) by d23av03.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVout) with ESMTP id u4355YqH024333 for ; Tue, 3 May 2016 15:05:34 +1000 Received: from ozlabs.au.ibm.com (ozlabs.au.ibm.com [9.192.253.14]) by d23av03.au.ibm.com (8.14.4/8.14.4/NCO v10.0 AVin) with ESMTP id u4355Xl3023538; Tue, 3 May 2016 15:05:33 +1000 Received: from bran.ozlabs.ibm.com (haven.au.ibm.com [9.192.254.114]) by ozlabs.au.ibm.com (Postfix) with ESMTP id 79E80A03B1; Tue, 3 May 2016 15:04:48 +1000 (AEST) Received: from gwshan (shangw.ozlabs.ibm.com [10.61.2.199]) by bran.ozlabs.ibm.com (Postfix) with ESMTP id 79A40E3A33; Tue, 3 May 2016 15:04:48 +1000 (AEST) Received: by gwshan (Postfix, from userid 1000) id 584EF94268E; Tue, 3 May 2016 15:04:48 +1000 (AEST) From: Gavin Shan To: skiboot@lists.ozlabs.org Date: Tue, 3 May 2016 15:04:34 +1000 Message-Id: <1462251882-12762-10-git-send-email-gwshan@linux.vnet.ibm.com> X-Mailer: git-send-email 2.1.0 In-Reply-To: <1462251882-12762-1-git-send-email-gwshan@linux.vnet.ibm.com> References: <1462251882-12762-1-git-send-email-gwshan@linux.vnet.ibm.com> X-TM-AS-MML: disable X-Content-Scanned: Fidelis XPS MAILER x-cbid: 16050305-0009-0000-0000-00000709CAA2 Subject: [Skiboot] [PATCH v10 09/17] core/pci: Support PCI slot X-BeenThere: skiboot@lists.ozlabs.org X-Mailman-Version: 2.1.22 Precedence: list List-Id: Mailing list for skiboot development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: skiboot-bounces+incoming=patchwork.ozlabs.org@lists.ozlabs.org Sender: "Skiboot" Every PCIE bridge port or PHB is expected to be bound with PCI slot , to which various PCI slot's functionalities are attached (e.g. power, link, reset). This supports PCI slot: * PCI slot is reprsented by "struct pci_slot". * "struct pci_slot_ops" represents the functions supported on the PCI slot. It's initialized by PCI slot core at the beginning and allowed to be overrided by platform partially or completely. * On PCI hot plugging event, the PCI devices behind the slot are enumarated. Device sub-tree is populated and sent to OS by OPAL message. * On PCI hot unplugging event, the PCI devices behind the slot are destroyed. Device sub-tree is removed and the slot is powered off. Signed-off-by: Gavin Shan --- core/Makefile.inc | 4 +- core/pci-slot.c | 255 ++++++++++++++++++++++++ core/pci.c | 128 +++++++++--- core/pcie-slot.c | 481 ++++++++++++++++++++++++++++++++++++++++++++++ include/opal-api.h | 11 ++ include/pci-slot.h | 251 ++++++++++++++++++++++++ include/pci.h | 66 ++----- include/platform.h | 3 +- platforms/ibm-fsp/lxvpd.c | 1 + 9 files changed, 1117 insertions(+), 83 deletions(-) create mode 100644 core/pci-slot.c create mode 100644 core/pcie-slot.c create mode 100644 include/pci-slot.h 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..32eae36 --- /dev/null +++ b/core/pci-slot.c @@ -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. + */ + +#include +#include +#include +#include +#include + +/* 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, uint8_t *val) +{ + uint64_t now = mftb(); + int64_t ret; + + /* Return timeout value if we have */ + 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; + case PCI_SLOT_STATE_GPOWER: + ret = slot->ops.get_power_status(slot, val); + break; + case PCI_SLOT_STATE_GPRESENCE: + ret = slot->ops.get_presence_status(slot, val); + break; + case PCI_SLOT_STATE_SPOWER: + ret = slot->ops.set_power_status(slot, *val); + 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_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 has + * been existing or not. + */ + slot = pd ? pd->slot : phb->slot; + if (slot) { + prlog(PR_ERR, PCI_SLOT_PREFIX + "Already existing\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 shouldn't be overrided by individual platform */ + slot->phb = phb; + slot->pd = pd; + pci_slot_set_state(slot, PCI_SLOT_STATE_NORMAL); + 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); + slot = phb ? phb->slot : NULL; + if (!(id & PCI_SLOT_ID_PREFIX)) + return slot; + + bdfn = PCI_SLOT_BDFN(id); + pd = phb ? pci_find_dev(phb, bdfn) : NULL; + slot = pd ? pd->slot : NULL; + return slot; +} + +int pci_slot_hotplug_event(struct pci_slot *slot, bool add, + void (*consumed)(void *data)) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + struct dt_node *dn = pd->dn; + uint8_t link = 0; + int64_t ret; + + /* + * On unplugging event, the subordinate devices attached + * to the PCI slot will be removed. In the mean time, a + * signal sent to kernel indicating the completion of the + * PCI unplugging. + */ + if (!add) { + pci_remove_bus(phb, &pd->children); + return opal_queue_msg(OPAL_MSG_PCI_HOTPLUG, + slot, consumed, + OPAL_PCI_SLOT_POWER_OFF, + (u64)(dn->phandle)); + } + + /* + * On PCI plugging event, we are not able to scan the subordinate + * devices attached to the specified PCI slot if the PCIe link is + * down. Otherwise, we will hit EEH error without exception. + */ + if (slot->ops.get_link_status && + (ret = slot->ops.get_link_status(slot, &link))) { + prlog(PR_ERR, PCI_SLOT_PREFIX + "Error %lld getting link status\n", slot->id, ret); + link = 0; + } + + if (link) { + pci_scan_bus(phb, pd->secondary_bus, pd->subordinate_bus, + &pd->children, pd, true); + pci_add_device_nodes(phb, &pd->children, dn, &phb->lstate, 0); + } + + return opal_queue_msg(OPAL_MSG_PCI_HOTPLUG, + slot, consumed, + OPAL_PCI_SLOT_POWER_ON, + (u64)(dn->phandle)); +} diff --git a/core/pci.c b/core/pci.c index e05ed6c..5bdf8da 100644 --- a/core/pci.c +++ b/core/pci.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -443,11 +444,72 @@ static void pci_cleanup_bridge(struct phb *phb, struct pci_device *pd) pci_cfg_write16(phb, pd->bdfn, PCI_CFG_CMD, cmd); } +/* + * Turn off the power suply to the slot if there're nothing connected + * to it for 2 purposes: saving power obviously, and initializing the + * slot to initial power-off state for hotplug + */ +static void pci_try_power_off_slot(struct phb *phb, struct pci_device *pd) +{ + uint32_t flags = (PCI_SLOT_FLAG_BOOTUP | + PCI_SLOT_FLAG_NO_HOTPLUG_MSG); + int64_t rc; + + /* Check if it's pluggable slot */ + if (!pd || + !pd->slot || + !pd->slot->pluggable || + !pd->slot->ops.set_power_status) + return; + + /* Bail if there're something connected */ + if (!list_empty(&pd->children)) + return; + + pci_slot_add_flags(pd->slot, flags); + rc = pd->slot->ops.set_power_status(pd->slot, 0); + while (rc > 0) { + time_wait(rc); + rc = pd->slot->ops.poll(pd->slot, NULL); + } + + pci_slot_remove_flags(pd->slot, flags); + if (rc != OPAL_SUCCESS) + PCINOTICE(phb, pd->bdfn, "Error %lld powering off slot\n", rc); + else + PCIDBG(phb, pd->bdfn, "Power off hotpluggable slot\n"); +} + +/* 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; + + if (list_empty(list)) + return; + + 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.. @@ -457,9 +519,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; @@ -505,10 +567,15 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus, * We only scan downstream if instructed to do so by the * caller. Typically we avoid the scan when we know the * link is down already, which happens for the top level - * root complex, and avoids a long secondary timeout + * root complex, and avoids a long secondary timeout. The + * power will be turned off if it's a empty hotpluggable + * slot. */ - if (!scan_downstream) + if (!scan_downstream) { + list_for_each(list, pd, link) + pci_try_power_off_slot(phb, pd); return bus; + } next_bus = bus + 1; max_sub = bus; @@ -576,8 +643,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 @@ -594,6 +661,9 @@ static uint8_t pci_scan(struct phb *phb, uint8_t bus, uint8_t max_bus, pd->subordinate_bus = max_sub; pci_cfg_write8(phb, pd->bdfn, PCI_CFG_SUBORDINATE_BUS, max_sub); next_bus = max_sub + 1; + + /* Turn off its power if it's empty hotpluggable slot */ + pci_try_power_off_slot(phb, pd); } return max_sub; @@ -785,7 +855,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); @@ -1311,12 +1381,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 @@ -1431,14 +1501,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 */ @@ -1446,8 +1516,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) @@ -1546,7 +1623,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..d3abca6 --- /dev/null +++ b/core/pcie-slot.c @@ -0,0 +1,481 @@ +/* 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 +#include +#include +#include +#include + +/* 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_status(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + int16_t slot_cap, slot_sts; + + /* + * We assume the downstream ports are always connected + * to the upstream port. Hard code the presence bit for + * the case. + */ + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { + *val = OPAL_PCI_SLOT_PRESENT; + return OPAL_SUCCESS; + } + + /* + * If downstream port doesn't support slot capability, + * we need hardcode it as "presence" according PCIE + * spec. + * + * Note that the power should be supplied to the slot + * before detecting the presence bit. + */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_CAPABILITY_REG, + &slot_cap); + if (pd->dev_type == PCIE_TYPE_SWITCH_DNPORT && + !(slot_cap & PCICAP_EXP_CAP_SLOT)) { + *val = OPAL_PCI_SLOT_PRESENT; + return OPAL_SUCCESS; + } + + /* Check presence bit */ + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, + &slot_sts); + if (slot_sts & 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_status(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + int16_t lstat; + + /* + * Upstream port doesn't have valid link indicator in + * data-link status. Lets hardcode it as link-up as + * the downtream ports are always connected to the + * upstream port. + */ + if (pd->dev_type == PCIE_TYPE_SWITCH_UPPORT) { + *val = 1; + return OPAL_SUCCESS; + } + + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_LSTAT, &lstat); + if (!(lstat & PCICAP_EXP_LSTAT_DLLL_ACT)) { + *val = 0; + return OPAL_SUCCESS; + } + + *val = GETFIELD(PCICAP_EXP_LSTAT_WIDTH, lstat); + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_power_status(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t slot_ctl; + + /* + * We assume the power is always on if we don't have + * power control functionality. + */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) { + *val = PCI_SLOT_POWER_ON; + return OPAL_SUCCESS; + } + + /* Check power supply bit */ + *val = PCI_SLOT_POWER_OFF; + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, + &slot_ctl); + if (!(slot_ctl & PCICAP_EXP_SLOTCTL_PWRCTLR)) + *val = PCI_SLOT_POWER_ON; + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_attention_status(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t slot_ctl; + + /* Attention is assumed as off if we don't have the capability */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) { + *val = 0; + return OPAL_SUCCESS; + } + + /* Check the attention bits */ + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &slot_ctl); + switch (GETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, slot_ctl)) { + case PCIE_INDIC_ON: + case PCIE_INDIC_BLINK: + *val = GETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, slot_ctl); + break; + case PCIE_INDIC_OFF: + default: + *val = 0; + } + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_get_latch_status(struct pci_slot *slot, + uint8_t *val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t slot_sts; + + /* We assume latch is always off if MRL sensor not existing */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_MRLSENS)) { + *val = 0; + return OPAL_SUCCESS; + } + + /* Check state of MRL sensor */ + *val = 0; + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTSTAT, &slot_sts); + if (slot_sts & PCICAP_EXP_SLOTSTAT_MRLSENSST) + *val = 1; + + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_set_attention_status(struct pci_slot *slot, + uint8_t val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t slot_ctl; + + /* + * Drop the request if the slot doesn't support + * attention capability + */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_ATTNI)) + return OPAL_SUCCESS; + + /* Write the attention bits */ + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &slot_ctl); + switch (val) { + case PCI_SLOT_ATTN_LED_OFF: + slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, + slot_ctl, PCIE_INDIC_OFF); + break; + case PCI_SLOT_ATTN_LED_ON: + slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, + slot_ctl, PCIE_INDIC_ON); + break; + case PCI_SLOT_ATTN_LED_BLINK: + slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_ATTNI, + slot_ctl, PCIE_INDIC_BLINK); + break; + default: + prlog(PR_ERR, PCIE_SLOT_PREFIX + "Invalid attention state (0x%x)\n", slot->id, val); + } + + pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, slot_ctl); + return OPAL_SUCCESS; +} + +static int64_t pcie_slot_set_power_status(struct pci_slot *slot, + uint8_t val) +{ + struct phb *phb = slot->phb; + struct pci_device *pd = slot->pd; + uint32_t ecap; + uint16_t slot_ctl; + + /* Drop the request in case power control isn't supported */ + if (!(slot->slot_cap & PCICAP_EXP_SLOTCAP_PWCTRL)) + return OPAL_SUCCESS; + + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + pci_cfg_read16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, &slot_ctl); + switch (val) { + case PCI_SLOT_POWER_OFF: + slot_ctl |= PCICAP_EXP_SLOTCTL_PWRCTLR; + slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, slot_ctl, + PCIE_INDIC_OFF); + break; + case PCI_SLOT_POWER_ON: + slot_ctl &= ~PCICAP_EXP_SLOTCTL_PWRCTLR; + slot_ctl = SETFIELD(PCICAP_EXP_SLOTCTL_PWRI, slot_ctl, + PCIE_INDIC_ON); + break; + } + + pci_cfg_write16(phb, pd->bdfn, ecap + PCICAP_EXP_SLOTCTL, + slot_ctl); + return OPAL_SUCCESS; +} + +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; + 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_status) + slot->ops.get_presence_status(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 */ + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + 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: + ecap = pci_cap(pd, PCI_CFG_CAP_ID_EXP, false); + 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 override the power + * management methods for fundamental reset because the + * hot reset 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_status) { + PCIE_SLOT_DBG(slot, "FRESET: Retrieve power state\n"); + slot->ops.get_power_status(slot, &power_state); + } + + /* In power on state, power it off */ + if (power_state == PCI_SLOT_POWER_ON && + slot->ops.set_power_status) { + PCIE_SLOT_DBG(slot, "FRESET: Power is on, turn off\n"); + slot->ops.set_power_status(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_status) + slot->ops.set_power_status(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); + } + + /* Slot info */ + 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 = GETFIELD(PCICAP_EXP_LCAP_MAXWDTH, slot->link_cap); + + /* Standard slot operations */ + slot->ops.get_presence_status = pcie_slot_get_presence_status; + slot->ops.get_link_status = pcie_slot_get_link_status; + slot->ops.get_power_status = pcie_slot_get_power_status; + slot->ops.get_attention_status = pcie_slot_get_attention_status; + slot->ops.get_latch_status = pcie_slot_get_latch_status; + slot->ops.set_power_status = pcie_slot_set_power_status; + slot->ops.set_attention_status = pcie_slot_set_attention_status; + + /* + * 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 41af0e5..50de6d1 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -376,6 +376,16 @@ enum OpalPciMaskAction { OPAL_MASK_ERROR_TYPE = 1 }; +enum OpalPciSlotPresentenceState { + OPAL_PCI_SLOT_EMPTY = 0, + OPAL_PCI_SLOT_PRESENT = 1 +}; + +enum OpalPciSlotPowerState { + 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 */ @@ -422,6 +432,7 @@ enum opal_msg_type { OPAL_MSG_DPO = 5, OPAL_MSG_PRD = 6, OPAL_MSG_OCC = 7, + OPAL_MSG_PCI_HOTPLUG = 8, OPAL_MSG_TYPE_MAX, }; diff --git a/include/pci-slot.h b/include/pci-slot.h new file mode 100644 index 0000000..93ab6cb --- /dev/null +++ b/include/pci-slot.h @@ -0,0 +1,251 @@ +/* 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 +#include +#include +#include + +/* + * 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_status)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_link_status)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_power_status)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_attention_status)(struct pci_slot *slot, uint8_t *val); + int64_t (*get_latch_status)(struct pci_slot *slot, uint8_t *val); + int64_t (*set_power_status)(struct pci_slot *slot, uint8_t val); + int64_t (*set_attention_status)(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, uint8_t *val); + + /* 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_GPRESENCE 0x00000800 +#define PCI_SLOT_STATE_GPRESENCE_START 0x00000801 + +struct pci_slot { + uint32_t flags; +#define PCI_SLOT_FLAG_BOOTUP 0x1 +#define PCI_SLOT_FLAG_NO_HOTPLUG_MSG 0x2 + + struct phb *phb; + struct pci_device *pd; + + /* Identifier */ + uint64_t id; + + /* 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; + + /* State machine */ + 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) +{ + uint64_t target, now = mftb(); + + target = now + dur; + if (target == 0) + target++; + slot->delay_tgt_tb = target; + + 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_properties(struct pci_slot *slot, + struct dt_node *np); +extern struct pci_slot *pci_slot_find(uint64_t id); +extern int pci_slot_hotplug_event(struct pci_slot *slot, bool add, + void (*consumed)(void *data)); +#endif /* __PCI_SLOT_H */ diff --git a/include/pci.h b/include/pci.h index 2b73598..632856b 100644 --- a/include/pci.h +++ b/include/pci.h @@ -22,60 +22,6 @@ #include #include -/* 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; @@ -461,6 +408,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 */ @@ -518,7 +466,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..e77bbe1 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 { @@ -72,7 +73,7 @@ struct platform { /* * This is called once per PHB before probing. It allows the - * platform to setup some PHB private data that can be used + * platform to setup some PHB private data that can be used * later on by calls such as pci_get_slot_info() below. The * "index" argument is the PHB index within the IO HUB (or * P8 chip). 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 #include #include +#include #include "lxvpd.h"