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: 6088931 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.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id B4CAB9F731 for ; Wed, 25 Mar 2015 07:13:55 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 652182034E for ; Wed, 25 Mar 2015 07:13:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 02240202B4 for ; Wed, 25 Mar 2015 07:13:53 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751152AbbCYHNw (ORCPT ); Wed, 25 Mar 2015 03:13:52 -0400 Received: from mail-bl2on0117.outbound.protection.outlook.com ([65.55.169.117]:9088 "EHLO na01-bl2-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1751118AbbCYHNv (ORCPT ); Wed, 25 Mar 2015 03:13:51 -0400 X-Greylist: delayed 1968 seconds by postgrey-1.27 at vger.kernel.org; Wed, 25 Mar 2015 03:13:51 EDT 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: CC: , Zang Roy-R61911 , Hu Mingkai-B21284 , Scott Wood , Yoder Stuart-B08248 , Arnd Bergmann , Bjorn Helgaas , "Jingoo Han" , Minghuan Lian 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 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, 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 */