From patchwork Tue Oct 6 07:56:08 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gabriel Fernandez X-Patchwork-Id: 54519 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-wi0-f198.google.com (mail-wi0-f198.google.com [209.85.212.198]) by patches.linaro.org (Postfix) with ESMTPS id 1DF9022F05 for ; Tue, 6 Oct 2015 07:57:51 +0000 (UTC) Received: by wicuu12 with SMTP id uu12sf38184588wic.2 for ; Tue, 06 Oct 2015 00:57:50 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:delivered-to:from:to:cc:subject :date:message-id:in-reply-to:references:sender:precedence:list-id :x-original-sender:x-original-authentication-results:mailing-list :list-post:list-help:list-archive:list-unsubscribe; bh=YDXQFXDAeCllF8gCKIeR/quL2/qGuZETV+xXhVKoZZY=; b=c+AQqWKBS3ppmUeYB01mO8FrqQa4XVOlCqHUBpgZji0pFBUl6OFLPWSNtXvedFRdZ+ pxyrFL5/Y3NvYJ0SBZRBI9xH58yExdCuXUvpBwWhfR3KC0u5L+tAQnu4vDuLQQLUSEHs mDPypp1sapVVF6rl//W/0JRdNav2oSfowRnJ3MObwHkT2/DnAL/qBcGIBvfnK7ghGnlY hXejrKJgwdWBYI4c3GCM2TsOegDsjFPCwisYGYpcM9wgqOg67oGMDKJw92xuCkCLnSwB eUsS3d/0/cOakZlIfQ+E1Zf5OkPVtngEFTu62qMuXd4OBx3E5GGG6cYYedcQJ9SZNotp AOwQ== X-Gm-Message-State: ALoCoQmrgwb2BLkWWj1KCg14t4niII6hRZu5Hc8HPiNadlQeVN1IKmrZBCT7FxV+TwSkTyt69p5X X-Received: by 10.194.240.230 with SMTP id wd6mr6112513wjc.0.1444118270433; Tue, 06 Oct 2015 00:57:50 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.25.19.221 with SMTP id 90ls502936lft.26.gmail; Tue, 06 Oct 2015 00:57:50 -0700 (PDT) X-Received: by 10.112.148.38 with SMTP id tp6mr13911807lbb.110.1444118270097; Tue, 06 Oct 2015 00:57:50 -0700 (PDT) Received: from mail-la0-f51.google.com (mail-la0-f51.google.com. [209.85.215.51]) by mx.google.com with ESMTPS id p4si20002891lbw.11.2015.10.06.00.57.50 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 06 Oct 2015 00:57:50 -0700 (PDT) Received-SPF: pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.51 as permitted sender) client-ip=209.85.215.51; Received: by lafb9 with SMTP id b9so50620603laf.0 for ; Tue, 06 Oct 2015 00:57:50 -0700 (PDT) X-Received: by 10.112.180.230 with SMTP id dr6mr13518120lbc.72.1444118269933; Tue, 06 Oct 2015 00:57:49 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patch@linaro.org Received: by 10.112.59.35 with SMTP id w3csp1692371lbq; Tue, 6 Oct 2015 00:57:48 -0700 (PDT) X-Received: by 10.68.243.37 with SMTP id wv5mr44607268pbc.68.1444118268400; Tue, 06 Oct 2015 00:57:48 -0700 (PDT) Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id rt6si46991806pbb.18.2015.10.06.00.57.48; Tue, 06 Oct 2015 00:57:48 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752732AbbJFH5q (ORCPT + 30 others); Tue, 6 Oct 2015 03:57:46 -0400 Received: from mail-wi0-f180.google.com ([209.85.212.180]:38869 "EHLO mail-wi0-f180.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752641AbbJFH5m (ORCPT ); Tue, 6 Oct 2015 03:57:42 -0400 Received: by wiclk2 with SMTP id lk2so146882681wic.1 for ; Tue, 06 Oct 2015 00:57:41 -0700 (PDT) X-Received: by 10.180.88.37 with SMTP id bd5mr17212946wib.82.1444118261073; Tue, 06 Oct 2015 00:57:41 -0700 (PDT) Received: from lmenx315.lme.st.com (LPuteaux-656-1-48-212.w82-127.abo.wanadoo.fr. [82.127.83.212]) by smtp.gmail.com with ESMTPSA id s1sm18717083wik.16.2015.10.06.00.57.29 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Tue, 06 Oct 2015 00:57:40 -0700 (PDT) From: Gabriel Fernandez To: Rob Herring , Pawel Moll , Mark Rutland , Ian Campbell , Kumar Gala , Srinivas Kandagatla , Maxime Coquelin , Patrice Chotard , Russell King , Bjorn Helgaas , Jingoo Han , Lucas Stach , Fabrice Gasnier , Kishon Vijay Abraham I , Andrew Morton , " David S. Miller" , Greg KH , Mauro Carvalho Chehab , Joe Perches , Tejun Heo , Arnd Bergmann , Viresh Kumar , Thierry Reding , Phil Edworthy , Minghuan Lian , Tanmay Inamdar , , Sachin Kamat , Andrew Lunn , Liviu Dudau , Zhou Wang Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, kernel@stlinux.com, linux-pci@vger.kernel.org, Lee Jones Subject: [PATCH v5 3/3] PCI: st: Provide support for the sti PCIe controller Date: Tue, 6 Oct 2015 09:56:08 +0200 Message-Id: <1444118168-13086-4-git-send-email-gabriel.fernandez@linaro.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1444118168-13086-1-git-send-email-gabriel.fernandez@linaro.org> References: <1444118168-13086-1-git-send-email-gabriel.fernandez@linaro.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: list List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Removed-Original-Auth: Dkim didn't pass. X-Original-Sender: gabriel.fernandez@linaro.org X-Original-Authentication-Results: mx.google.com; spf=pass (google.com: domain of patch+caf_=patchwork-forward=linaro.org@linaro.org designates 209.85.215.51 as permitted sender) smtp.mailfrom=patch+caf_=patchwork-forward=linaro.org@linaro.org Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , sti pcie is built around a Synopsis Designware PCIe IP. Signed-off-by: Fabrice Gasnier Signed-off-by: Gabriel Fernandez Reviewed-by: Pratyush Anand --- MAINTAINERS | 1 + drivers/pci/host/Kconfig | 9 + drivers/pci/host/Makefile | 1 + drivers/pci/host/pci-st.c | 587 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 598 insertions(+) create mode 100644 drivers/pci/host/pci-st.c diff --git a/MAINTAINERS b/MAINTAINERS index 9f6685f..f616ca6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1533,6 +1533,7 @@ F: drivers/i2c/busses/i2c-st.c F: drivers/media/rc/st_rc.c F: drivers/media/platform/sti/c8sectpfe/ F: drivers/mmc/host/sdhci-st.c +F: drivers/pci/host/pci-st.c F: drivers/phy/phy-miphy28lp.c F: drivers/phy/phy-miphy365x.c F: drivers/phy/phy-stih407-usb.c diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig index d5e58ba..23ab538 100644 --- a/drivers/pci/host/Kconfig +++ b/drivers/pci/host/Kconfig @@ -145,4 +145,13 @@ config PCIE_IPROC_BCMA Say Y here if you want to use the Broadcom iProc PCIe controller through the BCMA bus interface +config PCI_ST + bool "ST PCIe controller" + depends on ARCH_STI || (ARM && COMPILE_TEST) + select PCIE_DW + help + Enable PCIe controller support on ST Socs. This controller is based + on Designware hardware and therefore the driver re-uses the + Designware core functions to implement the driver. + endmenu diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile index 140d66f..c4024fa 100644 --- a/drivers/pci/host/Makefile +++ b/drivers/pci/host/Makefile @@ -17,3 +17,4 @@ obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o obj-$(CONFIG_PCIE_IPROC_PLATFORM) += pcie-iproc-platform.o obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o +obj-$(CONFIG_PCI_ST) += pci-st.o diff --git a/drivers/pci/host/pci-st.c b/drivers/pci/host/pci-st.c new file mode 100644 index 0000000..575a25b --- /dev/null +++ b/drivers/pci/host/pci-st.c @@ -0,0 +1,587 @@ +/* + * Copyright (C) 2014 STMicroelectronics + * + * STMicroelectronics PCI express Driver for sti SoCs. + * ST PCIe IPs are built around a Synopsys IP Core. + * + * Author: Fabrice Gasnier + * + * 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 +#include +#include +#include +#include + +#include "pcie-designware.h" + +#define TRANSLATION_CONTROL 0x900 +/* Controls if area is inclusive or exclusive */ +#define RC_PASS_ADDR_RANGE BIT(1) + +/* Base of area reserved for config accesses. Fixed size of 64K. */ +#define CFG_BASE_ADDRESS 0x92c +#define CFG_REGION_SIZE 65536 +#define CFG_SPACE1_OFFSET 0x1000 + +/* First 4K of config space has this BDF (bus,device,function) */ +#define FUNC0_BDF_NUM 0x930 + +/* Mem regions */ +#define IN0_MEM_ADDR_START 0x964 +#define IN0_MEM_ADDR_LIMIT 0x968 +#define IN1_MEM_ADDR_START 0x974 +#define IN1_MEM_ADDR_LIMIT 0x978 + +/* This actually contains the LTSSM state machine state */ +#define PORT_LOGIC_DEBUG_REG_0 0x728 + +/* LTSSM state machine values */ +#define DEBUG_REG_0_LTSSM_MASK 0x1f +#define S_DETECT_QUIET 0x00 +#define S_DETECT_ACT 0x01 +#define S_POLL_ACTIVE 0x02 +#define S_POLL_COMPLIANCE 0x03 +#define S_POLL_CONFIG 0x04 +#define S_PRE_DETECT_QUIET 0x05 +#define S_DETECT_WAIT 0x06 +#define S_CFG_LINKWD_START 0x07 +#define S_CFG_LINKWD_ACEPT 0x08 +#define S_CFG_LANENUM_WAIT 0x09 +#define S_CFG_LANENUM_ACEPT 0x0A +#define S_CFG_COMPLETE 0x0B +#define S_CFG_IDLE 0x0C +#define S_RCVRY_LOCK 0x0D +#define S_RCVRY_SPEED 0x0E +#define S_RCVRY_RCVRCFG 0x0F +#define S_RCVRY_IDLE 0x10 +#define S_L0 0x11 +#define S_L0S 0x12 +#define S_L123_SEND_EIDLE 0x13 +#define S_L1_IDLE 0x14 +#define S_L2_IDLE 0x15 +#define S_L2_WAKE 0x16 +#define S_DISABLED_ENTRY 0x17 +#define S_DISABLED_IDLE 0x18 +#define S_DISABLED 0x19 +#define S_LPBK_ENTRY 0x1A +#define S_LPBK_ACTIVE 0x1B +#define S_LPBK_EXIT 0x1C +#define S_LPBK_EXIT_TIMEOUT 0x1D +#define S_HOT_RESET_ENTRY 0x1E +#define S_HOT_RESET 0x1F + +/* syscfg bits */ +#define PCIE_SYS_INT BIT(5) +#define PCIE_APP_REQ_RETRY_EN BIT(3) +#define PCIE_APP_LTSSM_ENABLE BIT(2) +#define PCIE_APP_INIT_RST BIT(1) +#define PCIE_DEVICE_TYPE BIT(0) +#define PCIE_DEFAULT_VAL PCIE_DEVICE_TYPE + +/* Time to wait between testing the link in msecs (hardware poll interval) */ +#define LINK_LOOP_DELAY_MS 1 +/* Total amount of time to wait for the link to come up in msecs */ +#define LINK_WAIT_MS 120 +#define LINK_LOOP_COUNT (LINK_WAIT_MS / LINK_LOOP_DELAY_MS) + +/* st,syscfg offsets */ +#define SYSCFG0_REG 1 +#define SYSCFG1_REG 2 + +#define to_st_pcie(x) container_of(x, struct st_pcie, pp) + +/** + * struct st_pcie - private data of the controller + * @pp: designware pcie port + * @syscfg0: PCIe configuration 0 register, regmap offset + * @syscfg1: PCIe configuration 1 register, regmap offset + * @phy: associated pcie phy + * @lmi: memory made available to the controller + * @regmap: Syscfg registers bank in which PCIe port is configured + * @pwr: power control + * @rst: reset control + * @reset_gpio: optional reset gpio + * @config_window_start: start address of 64K config space area + */ +struct st_pcie { + struct pcie_port pp; + int syscfg0; + int syscfg1; + struct phy *phy; + struct resource *lmi; + struct regmap *regmap; + struct reset_control *pwr; + struct reset_control *rst; + int reset_gpio; + phys_addr_t config_window_start; +}; + +/* + * Function to test if the link is in an operational state or not. We must + * ensure the link is operational before we try to do a configuration access. + */ +static int st_pcie_link_up(struct pcie_port *pp) +{ + u32 status; + int link_up; + int count = 0; + + /* + * We have to be careful here. This is used in config read/write, + * The higher levels switch off interrupts, so you cannot use + * jiffies to do a timeout, or reschedule + */ + do { + /* + * What about L2? I think software intervention is + * required to get it out of L2, so in effect the link + * is down. Requires more work when/if we implement power + * management + */ + status = readl_relaxed(pp->dbi_base + PORT_LOGIC_DEBUG_REG_0); + status &= DEBUG_REG_0_LTSSM_MASK; + + link_up = (status == S_L0) || (status == S_L0S) || + (status == S_L1_IDLE); + + /* + * It can take some considerable time for the link to actually + * come up, caused by the PLLs. Experiments indicate it takes + * about 8ms to actually bring the link up, but this can vary + * considerably according to the specification. This code should + * allow sufficient time + */ + if (!link_up) + mdelay(LINK_LOOP_DELAY_MS); + + } while (!link_up && ++count < LINK_LOOP_COUNT); + + return link_up; +} + +/* + * On ARM platforms, we actually get a bus error returned when the PCIe IP + * returns a UR or CRS instead of an OK. + */ +static int st_pcie_abort_handler(unsigned long addr, unsigned int fsr, + struct pt_regs *regs) +{ + return 0; +} + +/* + * The PCI express core IP expects the following arrangement on it's address + * bus (slv_haddr) when driving config cycles. + * bus_number [31:24] + * dev_number [23:19] + * func_number [18:16] + * unused [15:12] + * ext_reg_number [11:8] + * reg_number [7:2] + * + * Bits [15:12] are unused. + * + * In the glue logic there is a 64K region of address space that can be + * written/read to generate config cycles. The base address of this is + * controlled by CFG_BASE_ADDRESS. There are 8 16 bit registers called + * FUNC0_BDF_NUM to FUNC8_BDF_NUM. These split the bottom half of the 64K + * window into 8 regions at 4K boundaries. These control the bus,device and + * function number you are trying to talk to. + * + * The decision on whether to generate a type 0 or type 1 access is controlled + * by bits 15:12 of the address you write to. If they are zero, then a type 0 + * is generated, if anything else it will be a type 1. Thus the bottom 4K + * region controlled by FUNC0_BDF_NUM can only generate type 0, all the others + * can only generate type 1. + * + * We only use FUNC0_BDF_NUM and FUNC1_BDF_NUM. Which one you use is selected + * by bit 12 of the address you write to. The selected register is then used + * for the top 16 bits of the slv_haddr to form the bus/dev/func, bit 15:12 are + * wired to zero, and bits 11:2 form the address of the register you want to + * read in config space. + * + * We always write FUNC0_BDF_NUM as a 32 bit write. So if we want type 1 + * accesses we have to shift by 16 so in effect we are writing to FUNC1_BDF_NUM + */ +static inline u32 bdf_num(int bus, int devfn, int is_root_bus) +{ + return ((bus << 8) | devfn) << (is_root_bus ? 0 : 16); +} + +static int st_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, + u32 *val) +{ + int ret; + u32 bdf, addr; + + addr = where & ~0x3; + bdf = bdf_num(bus->number, devfn, pci_is_root_bus(bus)); + + /* Set the config packet devfn */ + writel_relaxed(bdf, pp->dbi_base + FUNC0_BDF_NUM); + readl_relaxed(pp->dbi_base + FUNC0_BDF_NUM); + + if (bus->parent->number == pp->root_bus_nr) + ret = dw_pcie_cfg_read(pp->va_cfg0_base + addr, where, size, + val); + else + ret = dw_pcie_cfg_read(pp->va_cfg1_base + addr, where, size, + val); + return ret; +} + +static int st_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus, + unsigned int devfn, int where, int size, + u32 val) +{ + int ret; + u32 bdf, addr; + + addr = where & ~0x3; + bdf = bdf_num(bus->number, devfn, pci_is_root_bus(bus)); + + /* Set the config packet devfn */ + writel_relaxed(bdf, pp->dbi_base + FUNC0_BDF_NUM); + readl_relaxed(pp->dbi_base + FUNC0_BDF_NUM); + + if (bus->parent->number == pp->root_bus_nr) + ret = dw_pcie_cfg_write(pp->va_cfg0_base + addr, where, size, + val); + else + ret = dw_pcie_cfg_write(pp->va_cfg1_base + addr, where, size, + val); + return ret; +} + +static int st_pcie_board_reset(struct pcie_port *pp) +{ + int ret; + struct st_pcie *pcie = to_st_pcie(pp); + + if (!gpio_is_valid(pcie->reset_gpio)) + return -ENXIO; + + ret = gpio_direction_output(pcie->reset_gpio, 0); + if (ret) { + dev_err(pp->dev, "Cannot set PERST# (gpio %u) to output\n", + pcie->reset_gpio); + return ret; + } + + /* From PCIe spec */ + msleep(2); + + return gpio_direction_output(pcie->reset_gpio, 1); +} + +static void st_pcie_hw_setup(struct pcie_port *pp) +{ + struct st_pcie *pcie = to_st_pcie(pp); + + dw_pcie_setup_rc(pp); + + /* Set up the config window to the top of the PCI address space */ + writel_relaxed(pcie->config_window_start, + pp->dbi_base + CFG_BASE_ADDRESS); + + /* + * Open up memory to the PCI controller. We could do slightly + * better than this and exclude the kernel text segment and bss etc. + * They are base/limit registers so can be of arbitrary alignment + * presumably + */ + writel_relaxed(pcie->lmi->start, pp->dbi_base + IN0_MEM_ADDR_START); + writel_relaxed(pcie->lmi->end, pp->dbi_base + IN0_MEM_ADDR_LIMIT); + + /* Disable the 2nd region */ + writel_relaxed(~0, pp->dbi_base + IN1_MEM_ADDR_START); + writel_relaxed(0, pp->dbi_base + IN1_MEM_ADDR_LIMIT); + + writel_relaxed(RC_PASS_ADDR_RANGE, pp->dbi_base + TRANSLATION_CONTROL); +} + +static irqreturn_t st_pcie_msi_irq_handler(int irq, void *arg) +{ + struct pcie_port *pp = arg; + + return dw_handle_msi_irq(pp); +} + +static int st_pcie_init(struct pcie_port *pp) +{ + struct st_pcie *pcie = to_st_pcie(pp); + int ret; + + ret = reset_control_deassert(pcie->pwr); + if (ret) { + dev_err(pp->dev, "unable to bring out of powerdown\n"); + return ret; + } + + ret = reset_control_deassert(pcie->rst); + if (ret) { + dev_err(pp->dev, "unable to bring out of softreset\n"); + return ret; + } + + /* Set device type : Root Complex */ + ret = regmap_write(pcie->regmap, pcie->syscfg0, PCIE_DEVICE_TYPE); + if (ret < 0) { + dev_err(pp->dev, "unable to set device type\n"); + return ret; + } + + usleep_range(1000, 2000); + return ret; +} + +static int st_pcie_enable_ltssm(struct pcie_port *pp) +{ + struct st_pcie *pcie = to_st_pcie(pp); + + return regmap_update_bits(pcie->regmap, pcie->syscfg1, + PCIE_APP_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE); +} + +static int st_pcie_disable_ltssm(struct pcie_port *pp) +{ + struct st_pcie *pcie = to_st_pcie(pp); + + return regmap_update_bits(pcie->regmap, pcie->syscfg1, + PCIE_APP_LTSSM_ENABLE, 0); +} + +static void st_pcie_host_init(struct pcie_port *pp) +{ + struct st_pcie *pcie = to_st_pcie(pp); + int err, rst; + + /* + * We have to initialise the PCIe cell on some hardware before we can + * talk to the phy + */ + err = st_pcie_init(pp); + if (err) + return; + + err = st_pcie_disable_ltssm(pp); + if (err) { + dev_err(pp->dev, "disable ltssm failed, %d\n", err); + return; + } + + /* Now init the associated miphy */ + err = phy_init(pcie->phy); + if (err < 0) { + dev_err(pp->dev, "Cannot init PHY: %d\n", err); + return; + } + + /* Now do all the register poking */ + st_pcie_hw_setup(pp); + + /* Now assert the board level reset to the other PCIe device */ + rst = st_pcie_board_reset(pp); + + /* Re-enable the link, link training must begin shortly after reset */ + err = st_pcie_enable_ltssm(pp); + if (err) + dev_err(pp->dev, "enable ltssm failed, %d\n", err); + + /* + * PCIe specification states that you should not issue any config + * requests until 100ms after asserting reset, so we enforce that here + */ + if (!rst) + msleep(100); + + if (IS_ENABLED(CONFIG_PCI_MSI)) + dw_pcie_msi_init(pp); +} + +static struct pcie_host_ops st_pcie_host_ops = { + .rd_other_conf = st_pcie_rd_other_conf, + .wr_other_conf = st_pcie_wr_other_conf, + .link_up = st_pcie_link_up, + .host_init = st_pcie_host_init, +}; + +static const struct of_device_id st_pcie_of_match[] = { + { .compatible = "st,pcie", }, + { }, +}; + +static int __init st_pcie_probe(struct platform_device *pdev) +{ + struct resource *res; + struct st_pcie *pcie; + struct device_node *np = pdev->dev.of_node; + struct pcie_port *pp; + int ret; + + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + memset(pcie, 0, sizeof(*pcie)); + + pp = &pcie->pp; + pp->dev = &pdev->dev; + + /* mem regions */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dbi"); + pp->dbi_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pp->dbi_base)) + return PTR_ERR(pp->dbi_base); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (!res) + return -ENXIO; + + /* Check that this has sensible values */ + if ((resource_size(res) != CFG_REGION_SIZE) || + (res->start & (CFG_REGION_SIZE - 1))) { + dev_err(&pdev->dev, "Invalid config space properties\n"); + return -EINVAL; + } + pcie->config_window_start = res->start; + + pp->va_cfg0_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pp->va_cfg0_base)) + return PTR_ERR(pp->va_cfg0_base); + pp->va_cfg1_base = pp->va_cfg0_base + CFG_SPACE1_OFFSET; + + pcie->lmi = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "mem-window"); + if (!pcie->lmi) + return -ENXIO; + + /* regmap registers for PCIe IP configuration */ + pcie->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg"); + if (IS_ERR(pcie->regmap)) { + dev_err(&pdev->dev, "No syscfg phandle specified\n"); + return PTR_ERR(pcie->regmap); + } + + ret = of_property_read_u32_index(np, "st,syscfg", SYSCFG0_REG, + &pcie->syscfg0); + if (ret) { + dev_err(&pdev->dev, "can't get syscfg0 offset (%d)\n", ret); + return ret; + } + + ret = of_property_read_u32_index(np, "st,syscfg", SYSCFG1_REG, + &pcie->syscfg1); + if (ret) { + dev_err(&pdev->dev, "can't get syscfg1 offset (%d)\n", ret); + return ret; + } + + /* powerdown / resets */ + pcie->pwr = devm_reset_control_get(&pdev->dev, "powerdown"); + if (IS_ERR(pcie->pwr)) { + dev_err(&pdev->dev, "powerdown reset control not defined\n"); + return PTR_ERR(pcie->pwr); + } + + pcie->rst = devm_reset_control_get(&pdev->dev, "softreset"); + if (IS_ERR(pcie->rst)) { + dev_err(&pdev->dev, "Soft reset control not defined\n"); + return PTR_ERR(pcie->pwr); + } + + /* phy */ + pcie->phy = devm_phy_get(&pdev->dev, "pcie"); + if (IS_ERR(pcie->phy)) { + dev_err(&pdev->dev, "no PHY configured\n"); + return PTR_ERR(pcie->phy); + } + + /* Claim the GPIO for PRST# if available */ + pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0); + if (!gpio_is_valid(pcie->reset_gpio)) + dev_dbg(&pdev->dev, "No reset-gpio configured\n"); + else { + ret = devm_gpio_request(&pdev->dev, + pcie->reset_gpio, + "PCIe reset"); + if (ret) { + dev_err(&pdev->dev, "Cannot request reset-gpio %d\n", + pcie->reset_gpio); + return ret; + } + } + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + pp->msi_irq = platform_get_irq(pdev, 0); + if (pp->msi_irq <= 0) { + dev_err(&pdev->dev, "failed to get MSI irq\n"); + return -ENODEV; + } + + ret = devm_request_irq(&pdev->dev, pp->msi_irq, + st_pcie_msi_irq_handler, + IRQF_SHARED, "st-pcie-msi", pp); + if (ret) { + dev_err(&pdev->dev, "failed to request MSI irq\n"); + return -ENODEV; + } + } + + if (IS_ENABLED(CONFIG_ARM)) { + /* + * We have to hook the abort handler so that we can intercept + * bus errors when doing config read/write that return UR, + * which is flagged up as a bus error + */ + hook_fault_code(16+6, st_pcie_abort_handler, SIGBUS, 0, + "imprecise external abort"); + } + + pp->root_bus_nr = -1; + pp->ops = &st_pcie_host_ops; + + ret = dw_pcie_host_init(pp); + if (ret) { + dev_err(&pdev->dev, "failed to initialize host\n"); + return ret; + } + + platform_set_drvdata(pdev, pcie); + + dev_info(&pdev->dev, "Initialized\n"); + return 0; +} + +MODULE_DEVICE_TABLE(of, st_pcie_of_match); + +static struct platform_driver st_pcie_driver = { + .driver = { + .name = "st-pcie", + .of_match_table = st_pcie_of_match, + }, +}; + +/* ST PCIe driver does not allow module unload */ +static int __init pcie_init(void) +{ + return platform_driver_probe(&st_pcie_driver, st_pcie_probe); +} +module_init(pcie_init); + +MODULE_AUTHOR("Fabrice Gasnier "); +MODULE_DESCRIPTION("PCI express Driver for ST SoCs"); +MODULE_LICENSE("GPL v2");