2015-08-27 12:34:33

by Gabriel Fernandez

[permalink] [raw]
Subject: [PATCH v4 0/4] PCI: st: provide support for dw pcie


This patchset is based on v4.2-rc1 and is based on
[PATCH v8 0/6] PCI: hisi: Add PCIe host support for HiSilicon SoC Hip05
patchset from Zhou Wang.

Changes in v4:
- Remove pci: designware: remove my pci_common_init_dev() patch and
use [PATCH v8 3/6] PCI: designware: Add ARM64 support instead.
This patch is a good solution for me to disable IO support.
- add __init to st_pcie_probe() and use module_init() instead
device_initcall() to prevent the probe function from being
deferred and to prevent module unloading.

Changes in v3:
- Remove power management functions (was not fully tested)
- Remove configuration space range from dt binding
- Remove pci_common_init_dev() call in pcie-designware.c to avoid
default IO space declaration.

Changes in v2:
- comestic corrections in device tree binding
- add pci-st.c into MAINTAINERS
- remove st_pcie_ops structure to avoid another level of indirection
- remove nasty busy-loop
- remove useless test using virt_to_phys()
- move disable io support into dw-pcie driver

I don't change the st_pcie_abort_handler() function because abort handling
is masked during boot.


This patch-set introduces a STMicroelectronics PCIe controller.
It's based on designware PCIe driver.

Gabriel Fernandez (4):
ARM: STi: Kconfig update for PCIe support
PCI: st: Add Device Tree bindings for sti pcie
PCI: st: Provide support for the sti PCIe controller
MAINTAINERS: Add pci-st.c to ARCH/STI architecture

Documentation/devicetree/bindings/pci/st-pcie.txt | 53 ++
MAINTAINERS | 1 +
arch/arm/mach-sti/Kconfig | 2 +
drivers/pci/host/Kconfig | 9 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-st.c | 583 ++++++++++++++++++++++
6 files changed, 649 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/st-pcie.txt
create mode 100644 drivers/pci/host/pci-st.c

--
1.9.1


2015-08-27 12:34:36

by Gabriel Fernandez

[permalink] [raw]
Subject: [PATCH v4 1/4] ARM: STi: Kconfig update for PCIe support

Update Kconfig:
- MIGHT_HAVE_PCI
- PCI_DOMAINS

Signed-off-by: Fabrice Gasnier <[email protected]>
---
arch/arm/mach-sti/Kconfig | 2 ++
1 file changed, 2 insertions(+)

diff --git a/arch/arm/mach-sti/Kconfig b/arch/arm/mach-sti/Kconfig
index 125865d..5f99e93 100644
--- a/arch/arm/mach-sti/Kconfig
+++ b/arch/arm/mach-sti/Kconfig
@@ -9,6 +9,8 @@ menuconfig ARCH_STI
select ARCH_HAS_RESET_CONTROLLER
select HAVE_ARM_SCU if SMP
select ARCH_REQUIRE_GPIOLIB
+ select PCI_DOMAINS if PCI
+ select MIGHT_HAVE_PCI
select ARM_ERRATA_754322
select ARM_ERRATA_764369 if SMP
select ARM_ERRATA_775420
--
1.9.1

2015-08-27 12:35:39

by Gabriel Fernandez

[permalink] [raw]
Subject: [PATCH v4 2/4] PCI: st: Add Device Tree bindings for sti pcie

sti pcie is built around a Synopsis Designware PCIe IP.

Signed-off-by: Fabrice Gasnier <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
Documentation/devicetree/bindings/pci/st-pcie.txt | 53 +++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/st-pcie.txt

diff --git a/Documentation/devicetree/bindings/pci/st-pcie.txt b/Documentation/devicetree/bindings/pci/st-pcie.txt
new file mode 100644
index 0000000..25fcab3
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/st-pcie.txt
@@ -0,0 +1,53 @@
+STMicroelectronics STi PCIe controller
+
+This PCIe host controller is based on the Synopsis Designware PCIe IP
+and thus inherits all the common properties defined in designware-pcie.txt.
+
+Required properties:
+ - compatible: "st,stih407-pcie"
+ - reg: base address and length of the pcie controller, mem-window address
+ and length available to the controller.
+ - interrupts: A list of interrupt outputs of the controller. Must contain an
+ entry for each entry in the interrupt-names property.
+ - interrupt-names: Should be "msi". STi interrupt that is asserted when an
+ MSI is received.
+ - st,syscfg : should be a phandle of the syscfg node. Also contains syscfg
+ offset for IP configuration.
+ - resets, reset-names: the power-down and soft-reset lines of PCIe IP.
+ Associated names must be "powerdown" and "softreset".
+ - phys, phy-names: the phandle for the PHY device.
+ Associated name must be "pcie"
+
+Optional properties:
+ - reset-gpio: a GPIO spec to define which pin is connected to the bus reset.
+
+Example:
+
+pcie0: pcie@9b00000 {
+ compatible = "st,pcie", "snps,dw-pcie";
+ device_type = "pci";
+ reg = <0x09b00000 0x4000>, /* dbi cntrl registers */
+ <0x2fff0000 0x00010000>, /* configuration space */
+ <0x40000000 0x80000000>; /* lmi mem window */
+ reg-names = "dbi", "config", "mem-window";
+ st,syscfg = <&syscfg_core 0xd8 0xe0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ ranges = <0x82000000 0 0x20000000 0x20000000 0 0x0FFF0000>; /* non-prefetchable memory */
+ num-lanes = <1>;
+ interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "msi";
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 7>;
+ interrupt-map = <0 0 0 1 &intc GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>, /* INT A */
+ <0 0 0 2 &intc GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>, /* INT B */
+ <0 0 0 3 &intc GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>, /* INT C */
+ <0 0 0 4 &intc GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; /* INT D */
+
+ resets = <&powerdown STIH407_PCIE0_POWERDOWN>,
+ <&softreset STIH407_PCIE0_SOFTRESET>;
+ reset-names = "powerdown",
+ "softreset";
+ phys = <&phy_port0 PHY_TYPE_PCIE>;
+ phy-names = "pcie";
+};
--
1.9.1

2015-08-27 12:34:43

by Gabriel Fernandez

[permalink] [raw]
Subject: [PATCH v4 3/4] PCI: st: Provide support for the sti PCIe controller

sti pcie is built around a Synopsis Designware PCIe IP.

Signed-off-by: Fabrice Gasnier <[email protected]>
Signed-off-by: Gabriel Fernandez <[email protected]>
---
drivers/pci/host/Kconfig | 9 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pci-st.c | 583 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 593 insertions(+)
create mode 100644 drivers/pci/host/pci-st.c

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index c132bdd..db56b8f 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..0e7aaa2
--- /dev/null
+++ b/drivers/pci/host/pci-st.c
@@ -0,0 +1,583 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * STMicroelectronics PCI express Driver for sti SoCs.
+ * ST PCIe IPs are built around a Synopsys IP Core.
+ *
+ * Author: Fabrice Gasnier <[email protected]>
+ *
+ * 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 <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_gpio.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#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 void st_pcie_board_reset(struct pcie_port *pp)
+{
+ struct st_pcie *pcie = to_st_pcie(pp);
+
+ if (!gpio_is_valid(pcie->reset_gpio))
+ return;
+
+ if (gpio_direction_output(pcie->reset_gpio, 0)) {
+ dev_err(pp->dev, "Cannot set PERST# (gpio %u) to output\n",
+ pcie->reset_gpio);
+ return;
+ }
+
+ /* From PCIe spec */
+ msleep(2);
+ gpio_direction_output(pcie->reset_gpio, 1);
+
+ /*
+ * PCIe specification states that you should not issue any config
+ * requests until 100ms after asserting reset, so we enforce that here
+ */
+ msleep(100);
+}
+
+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);
+
+ /* Now assert the board level reset to the other PCIe device */
+ st_pcie_board_reset(pp);
+}
+
+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;
+
+ /*
+ * 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);
+
+ /* Re-enable the link */
+ err = st_pcie_enable_ltssm(pp);
+ if (err)
+ dev_err(pp->dev, "enable ltssm failed, %d\n", err);
+
+ 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_byname(pdev, "msi");
+ 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 <[email protected]>");
+MODULE_DESCRIPTION("PCI express Driver for ST SoCs");
+MODULE_LICENSE("GPL v2");
--
1.9.1

2015-08-27 12:35:01

by Gabriel Fernandez

[permalink] [raw]
Subject: [PATCH v4 4/4] MAINTAINERS: Add pci-st.c to ARCH/STI architecture

This patch adds the pci-st.c pci driver found on STMicroelectronics
SoC's into the STI arch section of the maintainers file.

Signed-off-by: Gabriel Fernandez <[email protected]>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 8133cef..af5034f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1506,6 +1506,7 @@ F: drivers/clocksource/arm_global_timer.c
F: drivers/i2c/busses/i2c-st.c
F: drivers/media/rc/st_rc.c
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
--
1.9.1

2015-08-27 17:48:41

by Pratyush Anand

[permalink] [raw]
Subject: Re: [PATCH v4 3/4] PCI: st: Provide support for the sti PCIe controller

Hi Gabriel,

Looks good to me.

On Thu, Aug 27, 2015 at 6:04 PM, Gabriel Fernandez
<[email protected]> wrote:
> sti pcie is built around a Synopsis Designware PCIe IP.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> Signed-off-by: Gabriel Fernandez <[email protected]>


> +static int st_pcie_link_up(struct pcie_port *pp)
> +{
> + u32 status;
> + int link_up;

nit: why not bool

> + int count = 0;

[...]

> +static void st_pcie_board_reset(struct pcie_port *pp)
> +{
> + struct st_pcie *pcie = to_st_pcie(pp);
> +
> + if (!gpio_is_valid(pcie->reset_gpio))
> + return;
> +
> + if (gpio_direction_output(pcie->reset_gpio, 0)) {
> + dev_err(pp->dev, "Cannot set PERST# (gpio %u) to output\n",
> + pcie->reset_gpio);
> + return;
> + }
> +
> + /* From PCIe spec */
> + msleep(2);
> + gpio_direction_output(pcie->reset_gpio, 1);
> +
> + /*
> + * PCIe specification states that you should not issue any config
> + * requests until 100ms after asserting reset, so we enforce that here
> + */
> + msleep(100);

IIRC, specification says to wait after link training completes. So
shouldn't it be after st_pcie_enable_ltssm. Moreover, I wonder why
others do not need it.

Reviewed-by: Pratyush Anand <[email protected]>

2015-08-28 00:06:51

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v4 2/4] PCI: st: Add Device Tree bindings for sti pcie

On Thu, Aug 27, 2015 at 7:34 AM, Gabriel Fernandez
<[email protected]> wrote:
> sti pcie is built around a Synopsis Designware PCIe IP.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
> Signed-off-by: Gabriel Fernandez <[email protected]>
> ---
> Documentation/devicetree/bindings/pci/st-pcie.txt | 53 +++++++++++++++++++++++
> 1 file changed, 53 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pci/st-pcie.txt
>
> diff --git a/Documentation/devicetree/bindings/pci/st-pcie.txt b/Documentation/devicetree/bindings/pci/st-pcie.txt
> new file mode 100644
> index 0000000..25fcab3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/st-pcie.txt
> @@ -0,0 +1,53 @@
> +STMicroelectronics STi PCIe controller
> +
> +This PCIe host controller is based on the Synopsis Designware PCIe IP
> +and thus inherits all the common properties defined in designware-pcie.txt.
> +
> +Required properties:
> + - compatible: "st,stih407-pcie"

What about "snps,dw-pcie" as well?

> + - reg: base address and length of the pcie controller, mem-window address
> + and length available to the controller.

What is mem-window? Seems rather large and perhaps should be under ranges.

> + - interrupts: A list of interrupt outputs of the controller. Must contain an
> + entry for each entry in the interrupt-names property.

Define how many interrupts.

> + - interrupt-names: Should be "msi". STi interrupt that is asserted when an
> + MSI is received.

Kind of pointless with a single interrupt.

> + - st,syscfg : should be a phandle of the syscfg node. Also contains syscfg
> + offset for IP configuration.
> + - resets, reset-names: the power-down and soft-reset lines of PCIe IP.
> + Associated names must be "powerdown" and "softreset".
> + - phys, phy-names: the phandle for the PHY device.
> + Associated name must be "pcie"

What does this mean?

> +
> +Optional properties:
> + - reset-gpio: a GPIO spec to define which pin is connected to the bus reset.
> +
> +Example:
> +
> +pcie0: pcie@9b00000 {
> + compatible = "st,pcie", "snps,dw-pcie";
> + device_type = "pci";
> + reg = <0x09b00000 0x4000>, /* dbi cntrl registers */
> + <0x2fff0000 0x00010000>, /* configuration space */
> + <0x40000000 0x80000000>; /* lmi mem window */
> + reg-names = "dbi", "config", "mem-window";
> + st,syscfg = <&syscfg_core 0xd8 0xe0>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + ranges = <0x82000000 0 0x20000000 0x20000000 0 0x0FFF0000>; /* non-prefetchable memory */

No i/o support?

> + num-lanes = <1>;
> + interrupts = <GIC_SPI 164 IRQ_TYPE_LEVEL_HIGH>;
> + interrupt-names = "msi";
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 7>;
> + interrupt-map = <0 0 0 1 &intc GIC_SPI 160 IRQ_TYPE_LEVEL_HIGH>, /* INT A */
> + <0 0 0 2 &intc GIC_SPI 161 IRQ_TYPE_LEVEL_HIGH>, /* INT B */
> + <0 0 0 3 &intc GIC_SPI 162 IRQ_TYPE_LEVEL_HIGH>, /* INT C */
> + <0 0 0 4 &intc GIC_SPI 163 IRQ_TYPE_LEVEL_HIGH>; /* INT D */
> +
> + resets = <&powerdown STIH407_PCIE0_POWERDOWN>,
> + <&softreset STIH407_PCIE0_SOFTRESET>;
> + reset-names = "powerdown",
> + "softreset";
> + phys = <&phy_port0 PHY_TYPE_PCIE>;
> + phy-names = "pcie";
> +};
> --
> 1.9.1
>