From patchwork Fri Oct 2 22:37:53 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Daney X-Patchwork-Id: 525839 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 4586A140180 for ; Sat, 3 Oct 2015 08:39:56 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b=MATuS+kY; dkim-atps=neutral Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751977AbbJBWjZ (ORCPT ); Fri, 2 Oct 2015 18:39:25 -0400 Received: from mail-io0-f171.google.com ([209.85.223.171]:33157 "EHLO mail-io0-f171.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751569AbbJBWiD (ORCPT ); Fri, 2 Oct 2015 18:38:03 -0400 Received: by iofh134 with SMTP id h134so135650900iof.0; Fri, 02 Oct 2015 15:38:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=THjMcrrkUNrhqVWT5Wunqc6rhdrfrYotqUVefG4cPeU=; b=MATuS+kY7m0gHaGKe1FgR4gNWSxvR9NwWxwqTbCwZTEvGF1veVSUePAvzWWQw4SHXy AFYpHRkpa2yyGPXrxQ6lzNp7icBJq3mlFRfk7LxOpDEM2Wfky8X/nVR/f2lpjQYraPe6 PR27rlU8l0bfMJ/DZKnjkkp0Y/trZnepLENyz0Hbu2Oqrv7TpZKfBThG7ZyAjpIVeWBN hmISpZJPtK4zAJH93KbT9HY6M26o1cllWvE+biLHL3x3mS/VmSRQrgxrujaCM9D0S8hY kls6+LC8ACUItOx9tZL03C8gumumcrjgTYptMmPziveno9aku0+eaxqcbbwHU8wtw9MV VHCw== X-Received: by 10.107.128.145 with SMTP id k17mr21740288ioi.24.1443825481961; Fri, 02 Oct 2015 15:38:01 -0700 (PDT) Received: from dl.caveonetworks.com (64.2.3.194.ptr.us.xo.net. [64.2.3.194]) by smtp.gmail.com with ESMTPSA id o90sm5830492ioi.17.2015.10.02.15.37.59 (version=TLSv1 cipher=RC4-SHA bits=128/128); Fri, 02 Oct 2015 15:38:00 -0700 (PDT) Received: from dl.caveonetworks.com (localhost.localdomain [127.0.0.1]) by dl.caveonetworks.com (8.14.5/8.14.5) with ESMTP id t92MbwGE026927; Fri, 2 Oct 2015 15:37:58 -0700 Received: (from ddaney@localhost) by dl.caveonetworks.com (8.14.5/8.14.5/Submit) id t92Mbw8I026926; Fri, 2 Oct 2015 15:37:58 -0700 From: David Daney To: linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Bjorn Helgaas , "Michael S. Tsirkin" , =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= , linux-api@vger.kernel.org, "Sean O. Stalley" , yinghai@kernel.org, rajatxjain@gmail.com, gong.chen@linux.intel.com Cc: David Daney Subject: [PATCH v4 2/5] PCI: Add support for Enhanced Allocation devices Date: Fri, 2 Oct 2015 15:37:53 -0700 Message-Id: <1443825476-26880-3-git-send-email-ddaney.cavm@gmail.com> X-Mailer: git-send-email 1.7.11.7 In-Reply-To: <1443825476-26880-1-git-send-email-ddaney.cavm@gmail.com> References: <1443825476-26880-1-git-send-email-ddaney.cavm@gmail.com> Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org From: "Sean O. Stalley" Add support for devices using Enhanced Allocation entries instead of BARs. This patch allows the kernel to parse the EA Extended Capability structure in PCI configspace and claim the BAR-equivalent resources. Signed-off-by: Sean O. Stalley [david.daney@cavium.com: Add more support/checking for Entry Properties, allow EA behind bridges, rewrite some error messages.] Signed-off-by: David Daney --- drivers/pci/pci.c | 184 ++++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 1 + drivers/pci/probe.c | 3 + 3 files changed, 188 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6a9a111..54a0a98 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2148,6 +2148,190 @@ void pci_pm_init(struct pci_dev *dev) } } +static unsigned long pci_ea_set_flags(struct pci_dev *dev, u8 prop) +{ + unsigned long flags = IORESOURCE_PCI_FIXED; + + switch (prop) { + case PCI_EA_P_MEM: + case PCI_EA_P_VIRT_MEM: + case PCI_EA_P_BRIDGE_MEM: + flags |= IORESOURCE_MEM; + break; + case PCI_EA_P_MEM_PREFETCH: + case PCI_EA_P_VIRT_MEM_PREFETCH: + case PCI_EA_P_BRIDGE_MEM_PREFETCH: + flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + break; + case PCI_EA_P_IO: + case PCI_EA_P_BRIDGE_IO: + flags |= IORESOURCE_IO; + break; + default: + return 0; + } + + return flags; +} + +static struct resource *pci_ea_get_resource(struct pci_dev *dev, u8 bei, u8 prop) +{ + if (bei <= PCI_EA_BEI_BAR5 && prop <= PCI_EA_P_IO) + return &dev->resource[bei]; + else if (bei == PCI_EA_BEI_ROM) + return &dev->resource[PCI_ROM_RESOURCE]; + else + return NULL; +} + +/* Read an Enhanced Allocation (EA) entry */ +static int pci_ea_read(struct pci_dev *dev, int offset) +{ + struct resource *res; + int ent_offset = offset; + int ent_size; + resource_size_t start; + resource_size_t end; + unsigned long flags; + u32 dw0; + u32 base; + u32 max_offset; + u8 prop; + bool support_64 = (sizeof(resource_size_t) >= 8); + + pci_read_config_dword(dev, ent_offset, &dw0); + ent_offset += 4; + + /* Entry size field indicates DWORDs after 1st */ + ent_size = ((dw0 & PCI_EA_ES) + 1) << 2; + + if (!(dw0 & PCI_EA_ENABLE)) /* Entry not enabled */ + goto out; + + prop = PCI_EA_PP(dw0); + /* + * If the Property is in the reserved range, try the Secondary + * Property instead. + */ + if (prop > PCI_EA_P_BRIDGE_IO && prop < PCI_EA_P_MEM_RESERVED) + prop = PCI_EA_SP(dw0); + if (prop > PCI_EA_P_BRIDGE_IO) + goto out; + + res = pci_ea_get_resource(dev, PCI_EA_BEI(dw0), prop); + if (!res) { + dev_err(&dev->dev, "Unsupported EA entry BEI: %u\n", + PCI_EA_BEI(dw0)); + goto out; + } + + flags = pci_ea_set_flags(dev, prop); + if (!flags) { + dev_err(&dev->dev, "Unsupported EA properties: %u\n", prop); + goto out; + } + + /* Read Base */ + pci_read_config_dword(dev, ent_offset, &base); + start = (base & PCI_EA_FIELD_MASK); + ent_offset += 4; + + /* Read MaxOffset */ + pci_read_config_dword(dev, ent_offset, &max_offset); + ent_offset += 4; + + /* Read Base MSBs (if 64-bit entry) */ + if (base & PCI_EA_IS_64) { + u32 base_upper; + + pci_read_config_dword(dev, ent_offset, &base_upper); + ent_offset += 4; + + flags |= IORESOURCE_MEM_64; + + /* entry starts above 32-bit boundary, can't use */ + if (!support_64 && base_upper) + goto out; + + if (support_64) + start |= ((u64)base_upper << 32); + } + + dev_dbg(&dev->dev, + "EA (%u,%u) start = %pa\n", PCI_EA_BEI(dw0), prop, &start); + + end = start + (max_offset | 0x03); + + /* Read MaxOffset MSBs (if 64-bit entry) */ + if (max_offset & PCI_EA_IS_64) { + u32 max_offset_upper; + + pci_read_config_dword(dev, ent_offset, &max_offset_upper); + ent_offset += 4; + + flags |= IORESOURCE_MEM_64; + + /* entry too big, can't use */ + if (!support_64 && max_offset_upper) + goto out; + + if (support_64) + end += ((u64)max_offset_upper << 32); + } + + dev_dbg(&dev->dev, + "EA (%u,%u) end = %pa\n", PCI_EA_BEI(dw0), prop, &end); + + if (end < start) { + dev_err(&dev->dev, "EA Entry crosses address boundary\n"); + goto out; + } + + if (ent_size != ent_offset - offset) { + dev_err(&dev->dev, + "EA Entry Size (%d) does not match length read (%d)\n", + ent_size, ent_offset - offset); + goto out; + } + + res->name = pci_name(dev); + res->start = start; + res->end = end; + res->flags = flags; + +out: + return offset + ent_size; +} + +/* Enhanced Allocation Initalization */ +void pci_ea_init(struct pci_dev *dev) +{ + int ea; + u8 num_ent; + int offset; + int i; + + /* find PCI EA capability in list */ + ea = pci_find_capability(dev, PCI_CAP_ID_EA); + if (!ea) + return; + + /* determine the number of entries */ + pci_bus_read_config_byte(dev->bus, dev->devfn, ea + PCI_EA_NUM_ENT, + &num_ent); + num_ent &= PCI_EA_NUM_ENT_MASK; + + offset = ea + PCI_EA_FIRST_ENT; + + /* Skip DWORD 2 for type 1 functions */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) + offset += 4; + + /* parse each EA entry */ + for (i = 0; i < num_ent; ++i) + offset = pci_ea_read(dev, offset); +} + static void pci_add_saved_cap(struct pci_dev *pci_dev, struct pci_cap_saved_state *new_cap) { diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 24ba9dc..a160733 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -78,6 +78,7 @@ bool pci_dev_keep_suspended(struct pci_dev *dev); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_init(struct pci_dev *dev); +void pci_ea_init(struct pci_dev *dev); void pci_allocate_cap_save_buffers(struct pci_dev *dev); void pci_free_cap_save_buffers(struct pci_dev *dev); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0b2be17..4293eec 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1598,6 +1598,9 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) static void pci_init_capabilities(struct pci_dev *dev) { + /* Enhanced Allocation */ + pci_ea_init(dev); + /* MSI/MSI-X list */ pci_msi_init_pci_dev(dev);