From patchwork Sun May 18 14:08:45 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Srikanth Thokala X-Patchwork-Id: 4198111 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 6608D9F32A for ; Sun, 18 May 2014 14:09:25 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 25FD82022D for ; Sun, 18 May 2014 14:09:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 04F852024D for ; Sun, 18 May 2014 14:09:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751426AbaEROJE (ORCPT ); Sun, 18 May 2014 10:09:04 -0400 Received: from mail-qg0-f44.google.com ([209.85.192.44]:63563 "EHLO mail-qg0-f44.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751211AbaEROJC (ORCPT ); Sun, 18 May 2014 10:09:02 -0400 Received: by mail-qg0-f44.google.com with SMTP id i50so7089806qgf.17 for ; Sun, 18 May 2014 07:09:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=sender:from:to:cc:subject:date:message-id; bh=KskCsuhKth+8kIPT7t12xKAdk7mBgaZSy2ODNeey8uI=; b=WbG+SC50HlK6AaM/vE51vp8c9HPAggPSzI/US8uSmmOMJrhdhUWyDdP9ZUOfcGqxUI E1k+Qbm+btw9j1OT/IvB/2+xJGymEdjitR4ea5t9+rVyefDZcGUQQpoy396+6jSjNMIm u3gpV1cfhWu18SFDR1c9MhWGbB8Khm7ElM60gYsKdKXYgv1XpLU1whnZGujdNnWhuyvb U9theVzBExaX306TRvhYq/MlvYTaK5e2HJamY2aRw7wL18FDXnHMfR3qCz0+6qu8L4jm p8irD+pdQktaQGekT38GPUO2MA8RMRv+jF6VI3/g1w1jppf+3wsLU9762mCW8XTbMpm0 iOBw== X-Received: by 10.140.49.171 with SMTP id q40mr39697120qga.7.1400422141063; Sun, 18 May 2014 07:09:01 -0700 (PDT) Received: from localhost ([149.199.62.254]) by mx.google.com with ESMTPSA id q16sm8967538qga.42.2014.05.18.07.08.58 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Sun, 18 May 2014 07:08:59 -0700 (PDT) From: Srikanth Thokala To: bhelgaas@google.com Cc: will.deacon@arm.com, arnd@arndb.de, michals@xilinx.com, linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, Srikanth Thokala Subject: [PATCH] PCI: Generic Configuration Access Mechanism support Date: Sun, 18 May 2014 19:38:45 +0530 Message-Id: <1400422125-2209-1-git-send-email-sthokal@xilinx.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI,RP_MATCHES_RCVD,T_DKIM_INVALID,UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds support for a generic CAM and ECAM configuration space accesses. Signed-off-by: Srikanth Thokala --- This patch is created with reference from Will's patch series: 1/3 - "ARM: kconfig: allow PCI support to be selected with ARCH_MULTIPLATFORM" 2/3 - "PCI: ARM: add support for generic PCI host controller" 3/3 - "MAINTAINERS: add entry for generic PCI host controller driver" --- drivers/pci/Makefile | 2 +- drivers/pci/pci-cfg.c | 162 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 34 +++++++++++ 3 files changed, 197 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/pci-cfg.c diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e04fe2d..37cfc33 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -4,7 +4,7 @@ obj-y += access.o bus.o probe.o host-bridge.o remove.o pci.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ - irq.o vpd.o setup-bus.o vc.o + irq.o vpd.o setup-bus.o vc.o pci-cfg.o obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o diff --git a/drivers/pci/pci-cfg.c b/drivers/pci/pci-cfg.c new file mode 100644 index 0000000..2b15fe4 --- /dev/null +++ b/drivers/pci/pci-cfg.c @@ -0,0 +1,162 @@ +/* + * PCI generic configuration access mechanism + * + * Copyright (C) 2014 ARM Limited + * Copyright (c) 2014 Xilinx, Inc. + * + * Author: Will Deacon + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +/* CAM definitions */ +#define PCI_CFG_CAM_BUS_NUM 16 +#define PCI_CFG_CAM_DEV_NUM 8 + +/* ECAM definitions */ +#define PCI_CFG_ECAM_BUS_NUM 20 +#define PCI_CFG_ECAM_DEV_NUM 12 + +/* Invalid device/function value */ +#define PCI_CFG_INVALID_DEVFN 0xFFFFFFFF + +/** + * pci_cfg_map_bus_cam - Get the CAM based configuration space address + * @bus: PCI Bus pointer + * @devfn: Device/Function + * @where: Offset from base + * + * Return: Configuration Space address + */ +static void __iomem *pci_cfg_map_bus_cam(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct pci_cfg_windows *cfg = sys->private_data; + resource_size_t idx = bus->number - cfg->bus_range.start; + + return cfg->win[idx] + ((devfn << PCI_CFG_CAM_DEV_NUM) | where); +} + +/** + * pci_cfg_map_bus_ecam - Get the ECAM based configuration space address + * @bus: PCI bus pointer + * @devfn: Device/Function + * @where: Offset from base + * + * Return: Configuration space address + */ +static void __iomem *pci_cfg_map_bus_ecam(struct pci_bus *bus, + unsigned int devfn, + int where) +{ + struct pci_sys_data *sys = bus->sysdata; + struct pci_cfg_windows *cfg = sys->private_data; + resource_size_t idx = bus->number - cfg->bus_range.start; + + return cfg->win[idx] + ((devfn << PCI_CFG_ECAM_DEV_NUM) | where); +} + +/** + * pci_cfg_read - Read configuration space + * @bus: PCI bus pointer + * @devfn: Device/function + * @where: Offset from base + * @size: Byte/word/dword + * @val: Value to be read + * + * Return: PCIBIOS_SUCCESSFUL on success + * PCIBIOS_DEVICE_NOT_FOUND on failure + */ +static int pci_cfg_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, unsigned int *val) +{ + void __iomem *addr; + struct pci_sys_data *sys = bus->sysdata; + struct pci_cfg_windows *cfg = sys->private_data; + + if (cfg->ops->is_valid_cfg_access) { + if (!cfg->ops->is_valid_cfg_access(bus, devfn)) { + *val = PCI_CFG_INVALID_DEVFN; + return PCIBIOS_DEVICE_NOT_FOUND; + } + } + + addr = cfg->ops->map_bus(bus, devfn, where); + + switch (size) { + case 1: + *val = readb(addr); + break; + case 2: + *val = readw(addr); + break; + default: + *val = readl(addr); + } + + return PCIBIOS_SUCCESSFUL; +} + +/** + * pci_cfg_write - Write configuration space + * @bus: PCI bus pointer + * @devfn: Device/function + * @where: Offset from base + * @size: Byte/word/dword + * @val: Value to write + * + * Return: PCIBIOS_SUCCESSFUL on success + * PCIBIOS_DEVICE_NOT_FOUND on failure + */ +static int pci_cfg_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, unsigned int val) +{ + void __iomem *addr; + struct pci_sys_data *sys = bus->sysdata; + struct pci_cfg_windows *cfg = sys->private_data; + + if (cfg->ops->is_valid_cfg_access) + if (!cfg->ops->is_valid_cfg_access(bus, devfn)) + return PCIBIOS_DEVICE_NOT_FOUND; + + addr = cfg->ops->map_bus(bus, devfn, where); + + switch (size) { + case 1: + writeb(val, addr); + break; + case 2: + writew(val, addr); + break; + default: + writel(val, addr); + } + + return PCIBIOS_SUCCESSFUL; +} + +/* Generic PCI CAM/ECAM Configuration Bus Operations */ + +struct pci_cfg_bus_ops pci_cfg_cam_bus_ops = { + .bus_shift = PCI_CFG_CAM_BUS_NUM, + .map_bus = pci_cfg_map_bus_cam, +}; +EXPORT_SYMBOL_GPL(pci_cfg_cam_bus_ops); + +struct pci_cfg_bus_ops pci_cfg_ecam_bus_ops = { + .bus_shift = PCI_CFG_ECAM_BUS_NUM, + .map_bus = pci_cfg_map_bus_ecam, +}; +EXPORT_SYMBOL_GPL(pci_cfg_ecam_bus_ops); + +struct pci_ops pci_cfg_ops = { + .read = pci_cfg_read, + .write = pci_cfg_write, +}; +EXPORT_SYMBOL_GPL(pci_cfg_ops); diff --git a/include/linux/pci.h b/include/linux/pci.h index aab57b4..6ebe21e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1806,4 +1806,38 @@ static inline struct eeh_dev *pci_dev_to_eeh_dev(struct pci_dev *pdev) */ struct pci_dev *pci_find_upstream_pcie_bridge(struct pci_dev *pdev); +/** + * struct pci_cfg_bus_ops - PCI bus configuration operations + * @bus_shift: Bus number + * @map_bus: Function pointer to get the configuration space address + * @is_valid_cfg_access: Function pointer to check for a valid device/function + */ +struct pci_cfg_bus_ops { + u32 bus_shift; + void __iomem *(*map_bus)(struct pci_bus *, unsigned int, int); + /* + * This function pointer is to check if we are addressing a valid + * device's function under a particular bus. + */ + int (*is_valid_cfg_access)(struct pci_bus *, unsigned int); +}; + +/** + * struct pci_cfg_windows - PCI bus configuration memory windows + * @res: Configuration space resource + * @bus_range: Bus range + * @win: Configuration space memory windows + * @ops: PCI bus configuration operations + */ +struct pci_cfg_windows { + struct resource res; + struct resource bus_range; + void __iomem **win; + struct pci_cfg_bus_ops *ops; +}; + +extern struct pci_ops pci_cfg_ops; +extern struct pci_cfg_bus_ops pci_cfg_ecam_bus_ops; +extern struct pci_cfg_bus_ops pci_cfg_cam_bus_ops; + #endif /* LINUX_PCI_H */