From patchwork Wed Mar 25 06:42:40 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Minghuan Lian X-Patchwork-Id: 6088771 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 834B09F399 for ; Wed, 25 Mar 2015 06:44:30 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 32E76202F2 for ; Wed, 25 Mar 2015 06:44:29 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id C84CA20251 for ; Wed, 25 Mar 2015 06:44:27 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1Yaf1A-0003iO-B8; Wed, 25 Mar 2015 06:42:20 +0000 Received: from mail-by2on0133.outbound.protection.outlook.com ([207.46.100.133] helo=na01-by2-obe.outbound.protection.outlook.com) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Yaf0W-0003PB-SR for linux-arm-kernel@lists.infradead.org; Wed, 25 Mar 2015 06:41:43 +0000 Received: from CO2PR03CA0052.namprd03.prod.outlook.com (10.141.194.179) by DM2PR0301MB0637.namprd03.prod.outlook.com (25.160.96.11) with Microsoft SMTP Server (TLS) id 15.1.118.21; Wed, 25 Mar 2015 06:41:17 +0000 Received: from BN1BFFO11FD022.protection.gbl (2a01:111:f400:7c10::1:143) by CO2PR03CA0052.outlook.office365.com (2a01:111:e400:1414::51) with Microsoft SMTP Server (TLS) id 15.1.99.9 via Frontend Transport; Wed, 25 Mar 2015 06:41:16 +0000 Received: from tx30smr01.am.freescale.net ([192.88.168.50]) by BN1BFFO11FD022.mail.protection.outlook.com ([10.58.144.85]) with Microsoft SMTP Server (TLS) id 15.1.130.10 via Frontend Transport; Wed, 25 Mar 2015 06:41:15 +0000 Received: from lmh.ap.freescale.net (lmh.ap.freescale.net [10.193.20.53]) by tx30smr01.am.freescale.net (8.14.3/8.14.0) with ESMTP id t2P6esUx001401; Tue, 24 Mar 2015 23:41:11 -0700 From: Minghuan Lian To: Subject: [PATCH 2/3] pci/designware: Add base driver for Designware PCIe Date: Wed, 25 Mar 2015 14:42:40 +0800 Message-ID: <1427265761-31828-3-git-send-email-Minghuan.Lian@freescale.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1427265761-31828-1-git-send-email-Minghuan.Lian@freescale.com> References: <1427265761-31828-1-git-send-email-Minghuan.Lian@freescale.com> X-EOPAttributedMessage: 0 Received-SPF: Fail (protection.outlook.com: domain of freescale.com does not designate 192.88.168.50 as permitted sender) receiver=protection.outlook.com; client-ip=192.88.168.50; helo=tx30smr01.am.freescale.net; Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=Minghuan.Lian@freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; X-Forefront-Antispam-Report: CIP:192.88.168.50; CTRY:US; IPV:NLI; EFV:NLI; BMV:1; SFV:NSPM; SFS:(10019020)(6009001)(339900001)(199003)(189002)(229853001)(77096005)(19580395003)(110136001)(48376002)(2950100001)(50226001)(105606002)(2351001)(77156002)(19580405001)(62966003)(106466001)(36756003)(92566002)(46102003)(87936001)(86362001)(50466002)(50986999)(76176999)(85426001)(551934003)(47776003)(2004002); DIR:OUT; SFP:1102; SCL:1; SRVR:DM2PR0301MB0637; H:tx30smr01.am.freescale.net; FPR:; SPF:Fail; MLV:sfv; A:1; MX:1; LANG:en; MIME-Version: 1.0 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DM2PR0301MB0637; X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0; PCL:0; RULEID:(601004)(5002010)(5005006); SRVR:DM2PR0301MB0637; BCL:0; PCL:0; RULEID:; SRVR:DM2PR0301MB0637; X-Forefront-PRVS: 052670E5A4 X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 25 Mar 2015 06:41:15.9687 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d; Ip=[192.88.168.50]; Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM2PR0301MB0637 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20150324_234141_064597_210EA3C2 X-CRM114-Status: GOOD ( 18.91 ) X-Spam-Score: -0.0 (/) Cc: Arnd Bergmann , Minghuan Lian , Jingoo Han , Hu Mingkai-B21284 , Zang Roy-R61911 , Yoder Stuart-B08248 , Bjorn Helgaas , Scott Wood , linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD, 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 The Synopsys Designware IP is shared with couples of platforms under multiple architectures. The patch is to provide basic architecture-independent Designware PCIe host driver including ATU initialization and PCI OPS. Currently, which supports arm and arm64 simultaneously. Signed-off-by: Minghuan Lian --- drivers/pci/host/Kconfig | 3 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pcie-designware-base.c | 282 ++++++++++++++++++++++++++++++++ drivers/pci/host/pcie-designware-base.h | 62 +++++++ 4 files changed, 348 insertions(+) create mode 100644 drivers/pci/host/pcie-designware-base.c create mode 100644 drivers/pci/host/pcie-designware-base.h diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index 7b892a9..f883d47 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -18,6 +18,9 @@ config PCI_MVEBU config PCIE_DW bool +config PCIE_DW_BASE + bool + config PCI_EXYNOS bool "Samsung Exynos PCIe controller" depends on SOC_EXYNOS5440 diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index e61d91c..47e7fee 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -1,3 +1,4 @@ +obj-$(CONFIG_PCIE_DW_BASE) += pcie-designware-base.o obj-$(CONFIG_PCIE_DW) += pcie-designware.o obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o diff --git a/drivers/pci/host/pcie-designware-base.c b/drivers/pci/host/pcie-designware-base.c new file mode 100644 index 0000000..f661151 --- /dev/null +++ b/drivers/pci/host/pcie-designware-base.c @@ -0,0 +1,282 @@ +/* + * Synopsys Designware PCIe host controller base driver + * + * 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 +#include +#include +#include +#include +#include + +#include "pcie-designware-base.h" + +void dw_pcie_dbi_write(struct dw_pcie_port *pp, u32 value, u32 offset) +{ + iowrite32(value, pp->dbi + offset); +} + +u32 dw_pcie_dbi_read(struct dw_pcie_port *pp, u32 offset) +{ + return ioread32(pp->dbi + offset); +} + +int dw_pcie_host_link_up(struct dw_pcie_port *pp) +{ + if (pp->dw_ops->link_up) + return pp->dw_ops->link_up(pp); + else + return 0; +} + +void dw_pcie_atu_outbound_set(struct dw_pcie_port *pp, int idx, int type, + u64 cpu_addr, u64 pci_addr, u32 size) +{ + if (idx >= pp->atu_num) + return; + + dw_pcie_dbi_write(pp, PCIE_ATU_REGION_OUTBOUND | idx, + PCIE_ATU_VIEWPORT); + dw_pcie_dbi_write(pp, lower_32_bits(cpu_addr), + PCIE_ATU_LOWER_BASE); + dw_pcie_dbi_write(pp, upper_32_bits(cpu_addr), + PCIE_ATU_UPPER_BASE); + dw_pcie_dbi_write(pp, lower_32_bits(cpu_addr + size - 1), + PCIE_ATU_LIMIT); + dw_pcie_dbi_write(pp, lower_32_bits(pci_addr), + PCIE_ATU_LOWER_TARGET); + dw_pcie_dbi_write(pp, upper_32_bits(pci_addr), + PCIE_ATU_UPPER_TARGET); + dw_pcie_dbi_write(pp, type, PCIE_ATU_CR1); + dw_pcie_dbi_write(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2); +} + +static void __iomem * +dw_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, int offset) +{ + struct dw_pcie_port *pp = bus->sysdata; + u32 type, busdev; + + /* If there is no link, then there is no device */ + if (!pci_is_root_bus(bus) && !dw_pcie_host_link_up(pp)) + return NULL; + + /* access only one slot on each root port */ + if (pci_is_root_bus(bus) && devfn > 0) + return NULL; + + if (pci_is_root_bus(bus)) + return pp->dbi + offset; + + busdev = PCIE_ATU_BUS(bus->number) | + PCIE_ATU_DEV(PCI_SLOT(devfn)) | + PCIE_ATU_FUNC(PCI_FUNC(devfn)); + + if (pci_is_root_bus(bus->parent)) + type = PCIE_ATU_TYPE_CFG0; + else + type = PCIE_ATU_TYPE_CFG1; + + dw_pcie_atu_outbound_set(pp, + PCIE_ATU_REGION_INDEX0, + type, + pp->cfg_addr, + busdev, + pp->cfg_size); + + return pp->cfg + offset; +} + +static int dw_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *val) +{ + struct dw_pcie_port *pp = bus->sysdata; + int ret; + + ret = pci_generic_config_read32(bus, devfn, where, size, val); + + if (pp->atu_num == 2 && !pci_is_root_bus(bus)) + /* reassign ATU0 to map IO space */ + dw_pcie_atu_outbound_set(pp, + PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, + pp->io_cpu_addr, + pp->io_pci_addr, + pp->io_size); + + return ret; +} + +static int dw_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + struct dw_pcie_port *pp = bus->sysdata; + int ret; + + ret = pci_generic_config_write32(bus, devfn, where, size, val); + + if (pp->atu_num == 2 && !pci_is_root_bus(bus)) + /* reassign ATU0 to map IO space */ + dw_pcie_atu_outbound_set(pp, + PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_IO, + pp->io_cpu_addr, + pp->io_pci_addr, + pp->io_size); + + return ret; +} + +static struct pci_ops dw_pcie_ops = { + .map_bus = dw_pcie_map_bus, + .read = dw_pcie_config_read, + .write = dw_pcie_config_write, +}; + +static int dw_pcie_map_reg(struct dw_pcie_port *pp) +{ + struct platform_device *pdev = to_platform_device(pp->dev); + struct resource *res; + + if (!pp->dbi) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "dbi"); + if (!res) { + dev_err(pp->dev, "missing *dbi* reg space\n"); + return -ENODEV; + } + + pp->dbi = devm_ioremap_resource(pp->dev, res); + if (IS_ERR(pp->dbi)) + return PTR_ERR(pp->dbi); + } + + if (!pp->cfg) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "config"); + if (!res) { + dev_err(pp->dev, "missing *config* reg space\n"); + return -ENODEV; + } + + pp->cfg = devm_ioremap_resource(pp->dev, res); + if (IS_ERR(pp->cfg)) + return PTR_ERR(pp->cfg); + + pp->cfg_addr = res->start; + pp->cfg_size = resource_size(res); + } + + return 0; +} + +/* + * If ATU number = 2, ATU0 is shared by transaction CFG and IO, + * ATU1 is used for transaction MEM + * If ATU number > 2, ATU0 is used for transaction CFG + * the other ATUs are used for MEM and IO separately. + */ +static int dw_pcie_atu_init(struct dw_pcie_port *pp, + struct list_head *res, + resource_size_t io_base) +{ + struct resource_entry *window; + struct device *dev = pp->dev; + int idx = 1, ret; + + if (pp->atu_num < 2) + pp->atu_num = 2; + + resource_list_for_each_entry(window, res) { + struct resource *res = window->res; + unsigned long restype = resource_type(res); + + switch (restype) { + case IORESOURCE_IO: + if (pp->atu_num == 2) + idx = 0; + + pp->io_cpu_addr = io_base; + pp->io_pci_addr = res->start - window->offset; + pp->io_size = resource_size(res); + dw_pcie_atu_outbound_set(pp, + idx, + PCIE_ATU_TYPE_IO, + pp->io_cpu_addr, + pp->io_pci_addr, + pp->io_size); + ret = pci_remap_iospace(res, io_base); + if (ret < 0) + return ret; + idx++; + break; + case IORESOURCE_MEM: + if (pp->atu_num == 2) + idx = 1; + + dw_pcie_atu_outbound_set(pp, + idx, + PCIE_ATU_TYPE_MEM, + res->start, + res->start - window->offset, + resource_size(res)); + idx++; + break; + case IORESOURCE_BUS: + break; + default: + dev_err(dev, "invalid resource %pR\n", res); + return -EINVAL; + } + } + + return 0; +} + +int dw_pcie_port_init(struct dw_pcie_port *pp) +{ + struct device_node *dn = pp->dev->of_node; + resource_size_t iobase = 0; + struct pci_bus *bus; + int ret; + LIST_HEAD(res); + + ret = dw_pcie_map_reg(pp); + if (ret) + return ret; + + ret = of_pci_get_host_bridge_resources(dn, 0, 0xff, &res, &iobase); + if (ret) + return ret; + + ret = dw_pcie_atu_init(pp, &res, iobase); + if (ret) + return ret; + + if (!pp->pci_ops) + pp->pci_ops = &dw_pcie_ops; + + if (pp->dw_ops->host_init) { + if (pp->dw_ops->host_init(pp)) + return ret; + } + + bus = pci_create_root_bus(pp->dev, 0, pp->pci_ops, + pp, &res); + if (!bus) + return -ENOMEM; + + pci_scan_child_bus(bus); + pci_assign_unassigned_bus_resources(bus); + pci_bus_add_devices(bus); + + return 0; +} + +MODULE_AUTHOR("Minghuan Lian "); +MODULE_DESCRIPTION("Designware PCIe controller driver with Multiarch support"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pci/host/pcie-designware-base.h b/drivers/pci/host/pcie-designware-base.h new file mode 100644 index 0000000..dd7a3b3 --- /dev/null +++ b/drivers/pci/host/pcie-designware-base.h @@ -0,0 +1,62 @@ +/* + * Synopsys Designware PCIe host controller base driver + * + * 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. + */ + +#ifndef _PCIE_DESIGNWARE_BASE_H +#define _PCIE_DESIGNWARE_BASE_H + +/* Synopsis specific PCIE configuration registers */ +#define PCIE_ATU_VIEWPORT 0x900 +#define PCIE_ATU_REGION_INBOUND (0x1 << 31) +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#define PCIE_ATU_CR1 0x904 +#define PCIE_ATU_TYPE_MEM (0x0 << 0) +#define PCIE_ATU_TYPE_IO (0x2 << 0) +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0) +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0) +#define PCIE_ATU_CR2 0x908 +#define PCIE_ATU_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_LOWER_BASE 0x90C +#define PCIE_ATU_UPPER_BASE 0x910 +#define PCIE_ATU_LIMIT 0x914 +#define PCIE_ATU_LOWER_TARGET 0x918 +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) +#define PCIE_ATU_UPPER_TARGET 0x91C + +struct dw_pcie_port; + +struct dw_host_ops { + int (*link_up)(struct dw_pcie_port *pp); + int (*host_init)(struct dw_pcie_port *pp); +}; + +struct dw_pcie_port { + struct device *dev; + void __iomem *dbi; + void __iomem *cfg; + u64 cfg_addr; + u32 cfg_size; + u64 io_cpu_addr; + u64 io_pci_addr; + u32 io_size; + u32 atu_num; + struct dw_host_ops *dw_ops; + struct pci_ops *pci_ops; +}; + +void dw_pcie_dbi_write(struct dw_pcie_port *pp, u32 value, u32 offset); +u32 dw_pcie_dbi_read(struct dw_pcie_port *pp, u32 offset); +int dw_pcie_host_link_up(struct dw_pcie_port *pp); +void dw_pcie_atu_outbound_set(struct dw_pcie_port *pp, int idx, int type, + u64 cpu_addr, u64 pci_addr, u32 size); +int dw_pcie_port_init(struct dw_pcie_port *pp); + +#endif /* _PCIE_DESIGNWARE_BASE_H */