Hi,
This patch series add Mediatek Gen2 PCIe host controller driver and
dt-binding document. It can be found on MT7623 series SoCs.
This driver was validated using Broadcom Tigon3 and Intel(R) 82575/82576
gigabit ethernet card.
Changes since v1:
- add .suppress_bind_attrs.
- remove unnecessary *_valid_device() pattern.
- remove PCI_PROBE_ONLY.
- use the regular readl() instead of readl_relaxed().
- add .map_bus() and change to use pci_generic_config_read/pci_generic_config_write.
- revise dt-binding document and move nonstandard properties to root node.
- change compatible string.
- use interrupt-map property and replace mtk_pcie_map_irq() with of_irq_parse_and_map_pci().
- use the new pci_register_host_bridge() method instead of pci_scan_root_bus().
Ryder Lee (2):
PCI: mediatek: Add Mediatek PCIe host controller support
dt-bindings: pcie: Add documentation for Mediatek PCIe
.../bindings/pci/mediatek,gen2v1-pcie.txt | 174 +++++++
drivers/pci/host/Kconfig | 11 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie-mediatek.c | 563 +++++++++++++++++++++
4 files changed, 749 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
create mode 100644 drivers/pci/host/pcie-mediatek.c
--
1.9.1
Add binding document for Mediatek PCIe Gen2 v1 host controller driver.
Signed-off-by: Ryder Lee <[email protected]>
---
.../bindings/pci/mediatek,gen2v1-pcie.txt | 174 +++++++++++++++++++++
1 file changed, 174 insertions(+)
create mode 100644 Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
diff --git a/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
new file mode 100644
index 0000000..545d8cf
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
@@ -0,0 +1,174 @@
+Mediatek Gen2 V1 PCIe controller
+
+PCIe subsys supports single root complex (RC) with 3 Root Ports. Each root
+ports supports a Gen2 1-lane Link. It includes one Host/PCI bridge and 3
+PCIe MAC. Each port has PIPE interface to PHY. There are 3 bus master for
+data access and 1 bus slave for Configuration and Status Register access.
+
+This controller is available on MT7623 series SoCs.
+
+Required properties:
+- compatible: Should contain "mediatek,gen2v1-pcie".
+- device_type: Must be "pci"
+- reg: Base addresses and lengths of the PCIe controller.
+- #address-cells: Address representation for root ports (must be 3)
+ - cell 0 specifies the bus and device numbers of the root port:
+ [23:16]: bus number
+ [15:11]: device number
+ - cell 1 denotes the upper 32 address bits and should be 0
+ - cell 2 contains the lower 32 address bits and is used to translate to the
+ CPU address space
+- #size-cells: Size representation for root ports (must be 2)
+- #interrupt-cells: Size representation for interrupts (must be 1)
+- interrupts: Three interrupt outputs of the controller. Must contain an
+ entry for each entry in the interrupt-names property.
+- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
+ Please refer to the standard PCI bus binding document for a more detailed
+ explanation.
+- clocks: Must contain an entry for each entry in clock-names.
+ See ../clocks/clock-bindings.txt for details.
+- clock-names: Must include the following entries:
+ - free_ck :for reference clock of PCIe subsys
+ - sys_ck0 :for clock of Port0 MAC
+ - sys_ck1 :for clock of Port1 MAC
+ - sys_ck2 :for clock of Port2 MAC
+- resets: Must contain an entry for each entry in reset-names.
+ See ../reset/reset.txt for details.
+- reset-names: Must include the following entries:
+ - pcie-rst0 :port0 reset
+ - pcie-rst1 :port1 reset
+ - pcie-rst2 :port2 reset
+- phys: list of PHY specifiers (used by generic PHY framework)
+- phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
+ number of PHYs as specified in *phys* property.
+- power-domains: A phandle and power domain specifier pair to the power domain
+ which is responsible for collapsing and restoring power to the peripheral
+- bus-range: Range of bus numbers associated with this controller
+- ranges: Describes the translation of addresses for root ports and standard
+ PCI regions. The entries must be 6 cells each, where the first three cells
+ correspond to the address as described for the #address-cells property
+ above, the fourth cell is the physical CPU address to translate to and the
+ fifth and six cells are as described for the #size-cells property above.
+ - The first three entries are expected to translate the addresses for the root
+ port registers, which are referenced by the assigned-addresses property of
+ the root port nodes (see below).
+ - The remaining entries setup the mapping for the standard I/O and memory
+ regions.
+ Please refer to the standard PCI bus binding document for a more detailed
+ explanation.
+
+In addition, the device tree node must have sub-nodes describing each
+PCIe port interface, having the following mandatory properties:
+
+Required properties:
+- device_type: Must be "pci"
+- assigned-addresses: Address and size of the port configuration registers
+- reg: Only the first four bytes are used to refer to the correct bus number
+ and device number.
+- #address-cells: Must be 3
+- #size-cells: Must be 2
+- #interrupt-cells: Size representation for interrupts (must be 1)
+- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
+ Please refer to the standard PCI bus binding document for a more detailed
+ explanation.
+- ranges: Sub-ranges distributed from the PCIe controller node. An empty
+ property is sufficient.
+- num-lanes: Number of lanes to use for this port.
+
+Examples:
+
+SoC dtsi:
+
+ hifsys: syscon@1a000000 {
+ compatible = "mediatek,mt7623-hifsys", "syscon";
+ reg = <0 0x1a000000 0 0x1000>;
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+ pcie: pcie-controller@1a140000 {
+ compatible = "mediatek,gen2v1-pcie";
+ device_type = "pci";
+ reg = <0 0x1a140000 0 0x1000>; /* PCIe shared registers */
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>,
+ <GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
+ interrupt-map-mask = <0xf800 0 0 0>;
+ interrupt-map = <0x0000 0 0 0 &gic GIC_SPI 193 IRQ_TYPE_NONE>,
+ <0x0800 0 0 0 &gic GIC_SPI 194 IRQ_TYPE_NONE>,
+ <0x1000 0 0 0 &gic GIC_SPI 195 IRQ_TYPE_NONE>;
+ clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
+ <&hifsys CLK_HIFSYS_PCIE0>,
+ <&hifsys CLK_HIFSYS_PCIE1>,
+ <&hifsys CLK_HIFSYS_PCIE2>;
+ clock-names = "free_ck", "sys_ck0", "sys_ck1", "sys_ck2";
+ resets = <&hifsys MT2701_HIFSYS_PCIE0_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE1_RST>,
+ <&hifsys MT2701_HIFSYS_PCIE2_RST>;
+ reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2";
+ phys = <&pcie0_phy>, <&pcie1_phy>, <&pcie2_phy>;
+ phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2";
+ power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
+ bus-range = <0x00 0xff>;
+ ranges = <0x82000000 0 0x1a142000 0 0x1a142000 0 0x1000 /* Port0 registers */
+ 0x82000000 0 0x1a143000 0 0x1a143000 0 0x1000 /* Port1 registers */
+ 0x82000000 0 0x1a144000 0 0x1a144000 0 0x1000 /* Port2 registers */
+ 0x81000000 0 0x1a160000 0 0x1a160000 0 0x00010000 /* I/O space */
+ 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */
+ status = "disabled";
+
+ pcie@1,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000000 0 0x1a142000 0 0x1000>;
+ reg = <0x0000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 193 IRQ_TYPE_NONE>;
+ ranges;
+ num-lanes = <1>;
+ status = "disabled";
+ };
+
+ pcie@2,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82000800 0 0x1a143000 0 0x1000>;
+ reg = <0x0800 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 194 IRQ_TYPE_NONE>;
+ ranges;
+ num-lanes = <1>;
+ status = "disabled";
+ };
+
+ pcie@3,0 {
+ device_type = "pci";
+ assigned-addresses = <0x82001000 0 0x1a144000 0 0x1000>;
+ reg = <0x1000 0 0 0 0>;
+ #address-cells = <3>;
+ #size-cells = <2>;
+ #interrupt-cells = <1>;
+ interrupt-map-mask = <0 0 0 0>;
+ interrupt-map = <0 0 0 0 &gic GIC_SPI 195 IRQ_TYPE_NONE>;
+ ranges;
+ num-lanes = <1>;
+ status = "disabled";
+ };
+ };
+
+Board dts:
+
+ &pcie {
+ status = "okay";
+
+ pcie@1,0 {
+ status = "okay";
+ };
+ };
--
1.9.1
Add support for the Mediatek PCIe Gen2 V1 controller which can
be found on MT7623 series SoCs.
Signed-off-by: Ryder Lee <[email protected]>
---
drivers/pci/host/Kconfig | 11 +
drivers/pci/host/Makefile | 1 +
drivers/pci/host/pcie-mediatek.c | 563 +++++++++++++++++++++++++++++++++++++++
3 files changed, 575 insertions(+)
create mode 100644 drivers/pci/host/pcie-mediatek.c
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index f7c1d4d..e8f793f 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -174,6 +174,17 @@ config PCIE_ROCKCHIP
There is 1 internal PCIe port available to support GEN2 with
4 slots.
+config PCIE_MEDIATEK
+ bool "Mediatek Gen2 v1 PCIe host controller"
+ depends on (ARM && ARCH_MEDIATEK) || COMPILE_TEST
+ depends on OF
+ depends on PCI
+ select PCIEPORTBUS
+ help
+ Say Y here if you want to enable PCIe controller support on MT7623 series
+ SoCs. There is one single root complex with 3 root ports available.
+ Each port supports Gen2 lane x1.
+
config VMD
depends on PCI_MSI && X86_64 && SRCU
tristate "Intel Volume Management Device Driver"
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 4d36866..265adff 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_PCIE_IPROC_BCMA) += pcie-iproc-bcma.o
obj-$(CONFIG_PCIE_ALTERA) += pcie-altera.o
obj-$(CONFIG_PCIE_ALTERA_MSI) += pcie-altera-msi.o
obj-$(CONFIG_PCIE_ROCKCHIP) += pcie-rockchip.o
+obj-$(CONFIG_PCIE_MEDIATEK) += pcie-mediatek.o
obj-$(CONFIG_VMD) += vmd.o
# The following drivers are for devices that use the generic ACPI
diff --git a/drivers/pci/host/pcie-mediatek.c b/drivers/pci/host/pcie-mediatek.c
new file mode 100644
index 0000000..bc77179
--- /dev/null
+++ b/drivers/pci/host/pcie-mediatek.c
@@ -0,0 +1,563 @@
+/*
+ * Mediatek Gen2 v1 PCIe host controller driver which can be found
+ * on MT7623 SoCs families.
+ *
+ * Copyright (c) 2017 MediaTek Inc.
+ * Author: Ryder Lee <[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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_pci.h>
+#include <linux/of_platform.h>
+#include <linux/pci.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+/* PCIe shared registers */
+#define PCIE_SYS_CFG 0x00
+#define PCIE_INT_ENABLE 0x0c
+#define PCIE_CFG_ADDR 0x20
+#define PCIE_CFG_DATA 0x24
+
+/* PCIe per port registers */
+#define PCIE_BAR0_SETUP 0x10
+#define PCIE_BAR1_SETUP 0x14
+#define PCIE_BAR0_MEM_BASE 0x18
+#define PCIE_CLASS 0x34
+#define PCIE_LINK_STATUS 0x50
+
+#define PCIE_PORT_INT_EN(x) BIT(20 + (x))
+#define PCIE_PORT_PERST(x) BIT(1 + (x))
+#define PCIE_PORT_LINKUP BIT(0)
+#define PCIE_BAR_MAP_MAX GENMASK(31, 16)
+
+#define PCIE_BAR_ENABLE BIT(0)
+#define PCIE_REVISION_ID BIT(0)
+#define PCIE_CLASS_CODE (0x60400 << 8)
+#define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \
+ ((((regn) >> 8) & GENMASK(3, 0)) << 24))
+#define PCIE_CONF_FUN(fun) (((fun) << 8) & GENMASK(10, 8))
+#define PCIE_CONF_DEV(dev) (((dev) << 11) & GENMASK(15, 11))
+#define PCIE_CONF_BUS(bus) (((bus) << 16) & GENMASK(23, 16))
+#define PCIE_CONF_ADDR(regn, fun, dev, bus) \
+ (PCIE_CONF_REG(regn) | PCIE_CONF_FUN(fun) | \
+ PCIE_CONF_DEV(dev) | PCIE_CONF_BUS(bus))
+
+/* Mediatek specific configuration registers */
+#define PCIE_FTS_NUM 0x70c
+#define PCIE_FTS_NUM_MASK GENMASK(15, 8)
+#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
+
+#define PCIE_FC_CREDIT 0x73c
+#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
+#define PCIE_FC_CREDIT_VAL(x) ((x) << 16)
+
+/**
+ * struct mtk_pcie_port - PCIe port information
+ * @base: IO mapped register base
+ * @list: port list
+ * @pcie: pointer to PCIe host info
+ * @reset: pointer to port reset control
+ * @regs: port memory region
+ * @sys_ck: pointer to bus clock
+ * @phy: pointer to phy control block
+ * @lane: lane count
+ * @index: port index
+ */
+struct mtk_pcie_port {
+ void __iomem *base;
+ struct list_head list;
+ struct mtk_pcie *pcie;
+ struct reset_control *reset;
+ struct resource regs;
+ struct clk *sys_ck;
+ struct phy *phy;
+ u32 lane;
+ u32 index;
+};
+
+/**
+ * struct mtk_pcie - PCIe host information
+ * @dev: pointer to PCIe device
+ * @base: IO mapped register Base
+ * @free_ck: free-run reference clock
+ * @io: IO resource
+ * @pio: PIO resource
+ * @mem: non-prefetchable memory resource
+ * @busn: bus range
+ * @offset: IO / Memory offset
+ * @ports: pointer to PCIe port information
+ */
+struct mtk_pcie {
+ struct device *dev;
+ void __iomem *base;
+ struct clk *free_ck;
+
+ struct resource io;
+ struct resource pio;
+ struct resource mem;
+ struct resource busn;
+ struct {
+ resource_size_t mem;
+ resource_size_t io;
+ } offset;
+ struct list_head ports;
+};
+
+static inline bool mtk_pcie_link_is_up(struct mtk_pcie_port *port)
+{
+ return !!(readl(port->base + PCIE_LINK_STATUS) &
+ PCIE_PORT_LINKUP);
+}
+
+static void mtk_pcie_port_free(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ struct device *dev = pcie->dev;
+
+ devm_iounmap(dev, port->base);
+ devm_release_mem_region(dev, port->regs.start,
+ resource_size(&port->regs));
+ list_del(&port->list);
+ devm_kfree(dev, port);
+}
+
+static void mtk_pcie_put_resources(struct mtk_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ struct mtk_pcie_port *port, *tmp;
+
+ list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
+ phy_power_off(port->phy);
+ clk_disable_unprepare(port->sys_ck);
+ mtk_pcie_port_free(port);
+ }
+
+ clk_disable_unprepare(pcie->free_ck);
+ pm_runtime_put_sync(dev);
+ pm_runtime_disable(dev);
+}
+
+static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
+ unsigned int devfn, int where)
+{
+ struct pci_host_bridge *host = pci_find_host_bridge(bus);
+ struct mtk_pcie *pcie = pci_host_bridge_priv(host);
+
+ writel(PCIE_CONF_ADDR(where, PCI_FUNC(devfn), PCI_SLOT(devfn),
+ bus->number), pcie->base + PCIE_CFG_ADDR);
+
+ return pcie->base + PCIE_CFG_DATA + (where & 3);
+}
+
+static struct pci_ops mtk_pcie_ops = {
+ .map_bus = mtk_pcie_map_bus,
+ .read = pci_generic_config_read,
+ .write = pci_generic_config_write,
+};
+
+static void mtk_pcie_configure_rc(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ u32 func = PCI_FUNC(port->index << 3);
+ u32 slot = PCI_SLOT(port->index << 3);
+ u32 val;
+
+ /* enable interrupt */
+ val = readl(pcie->base + PCIE_INT_ENABLE);
+ val |= PCIE_PORT_INT_EN(port->index);
+ writel(val, pcie->base + PCIE_INT_ENABLE);
+
+ /* map to all DDR region. We need to set it before cfg operation. */
+ writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE,
+ port->base + PCIE_BAR0_SETUP);
+
+ /* configure class Code and revision ID */
+ writel(PCIE_CLASS_CODE | PCIE_REVISION_ID,
+ port->base + PCIE_CLASS);
+
+ /* configure FC credit */
+ writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0),
+ pcie->base + PCIE_CFG_ADDR);
+ val = readl(pcie->base + PCIE_CFG_DATA);
+ val &= ~PCIE_FC_CREDIT_MASK;
+ val |= PCIE_FC_CREDIT_VAL(0x806c);
+ writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, func, slot, 0),
+ pcie->base + PCIE_CFG_ADDR);
+ writel(val, pcie->base + PCIE_CFG_DATA);
+
+ /* configure RC FTS number to 250 when it leaves L0s */
+ writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0),
+ pcie->base + PCIE_CFG_ADDR);
+ val = readl(pcie->base + PCIE_CFG_DATA);
+ val &= ~PCIE_FTS_NUM_MASK;
+ val |= PCIE_FTS_NUM_L0(0x50);
+ writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0),
+ pcie->base + PCIE_CFG_ADDR);
+ writel(val, pcie->base + PCIE_CFG_DATA);
+}
+
+static void mtk_pcie_assert_ports(struct mtk_pcie_port *port)
+{
+ struct mtk_pcie *pcie = port->pcie;
+ u32 val;
+
+ /* assert port PERST_N */
+ val = readl(pcie->base + PCIE_SYS_CFG);
+ val |= PCIE_PORT_PERST(port->index);
+ writel(val, pcie->base + PCIE_SYS_CFG);
+
+ /* de-assert port PERST_N */
+ val = readl(pcie->base + PCIE_SYS_CFG);
+ val &= ~PCIE_PORT_PERST(port->index);
+ writel(val, pcie->base + PCIE_SYS_CFG);
+
+ /* PCIe v2.0 need at least 100ms delay to train from Gen1 to Gen2 */
+ msleep(100);
+}
+
+static int mtk_pcie_enable_ports(struct mtk_pcie_port *port)
+{
+ struct device *dev = port->pcie->dev;
+ int err;
+
+ err = clk_prepare_enable(port->sys_ck);
+ if (err) {
+ dev_err(dev, "failed to enable port%d clock\n", port->index);
+ goto err_sys_clk;
+ }
+
+ reset_control_assert(port->reset);
+ reset_control_deassert(port->reset);
+
+ err = phy_power_on(port->phy);
+ if (err) {
+ dev_err(dev, "failed to power on port%d phy\n", port->index);
+ goto err_phy_on;
+ }
+
+ mtk_pcie_assert_ports(port);
+
+ /* if link up, then setup root port configuration space */
+ if (mtk_pcie_link_is_up(port)) {
+ mtk_pcie_configure_rc(port);
+ return 0;
+ }
+
+ dev_info(dev, "Port%d link down\n", port->index);
+
+ phy_power_off(port->phy);
+err_phy_on:
+ clk_disable_unprepare(port->sys_ck);
+ mtk_pcie_port_free(port);
+err_sys_clk:
+ return err;
+}
+
+static int mtk_pcie_parse_ports(struct mtk_pcie *pcie,
+ struct mtk_pcie_port **p,
+ struct device_node *node,
+ int index)
+{
+ struct mtk_pcie_port *port;
+ struct device *dev = pcie->dev;
+ char name[10];
+ int err;
+
+ *p = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
+ if (!*p)
+ return -ENOMEM;
+
+ port = *p;
+
+ err = of_property_read_u32(node, "num-lanes", &port->lane);
+ if (err) {
+ dev_err(dev, "missing num-lanes property\n");
+ return err;
+ }
+
+ err = of_address_to_resource(node, 0, &port->regs);
+ if (err) {
+ dev_err(dev, "failed to parse address: %d\n", err);
+ return err;
+ }
+
+ port->base = devm_ioremap_resource(dev, &port->regs);
+ if (IS_ERR(port->base)) {
+ dev_err(dev, "failed to map port%d base\n", index);
+ return PTR_ERR(port->base);
+ }
+
+ snprintf(name, sizeof(name), "sys_ck%d", index);
+ port->sys_ck = devm_clk_get(dev, name);
+ if (IS_ERR(port->sys_ck)) {
+ dev_err(dev, "failed to get port%d clock\n", index);
+ return PTR_ERR(port->sys_ck);
+ }
+
+ snprintf(name, sizeof(name), "pcie-rst%d", index);
+ port->reset = devm_reset_control_get(dev, name);
+ if (IS_ERR(port->reset)) {
+ dev_err(dev, "failed to get port%d reset\n", index);
+ return PTR_ERR(port->reset);
+ }
+
+ snprintf(name, sizeof(name), "pcie-phy%d", index);
+ port->phy = devm_phy_get(dev, name);
+ if (IS_ERR(port->phy)) {
+ dev_err(dev, "failed to get port%d phy\n", index);
+ return PTR_ERR(port->phy);
+ }
+
+ port->index = index;
+ port->pcie = pcie;
+
+ INIT_LIST_HEAD(&port->list);
+ list_add_tail(&port->list, &pcie->ports);
+ return 0;
+}
+
+static int mtk_pcie_handle_shared_resource(struct mtk_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *regs;
+ int err;
+
+ /* get shared registers */
+ regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ pcie->base = devm_ioremap_resource(dev, regs);
+ if (IS_ERR(pcie->base)) {
+ dev_err(dev, "failed to map shared register\n");
+ return PTR_ERR(pcie->base);
+ }
+
+ pcie->free_ck = devm_clk_get(dev, "free_ck");
+ if (IS_ERR(pcie->free_ck))
+ return PTR_ERR(pcie->free_ck);
+
+ pm_runtime_enable(dev);
+ err = pm_runtime_get_sync(dev);
+ if (err)
+ goto err_pm;
+
+ /* enable top level clock */
+ err = clk_prepare_enable(pcie->free_ck);
+ if (err) {
+ dev_err(dev, "failed to enable free_ck\n");
+ goto err_free_ck;
+ }
+
+ return 0;
+
+err_free_ck:
+ pm_runtime_put_sync(dev);
+err_pm:
+ pm_runtime_disable(dev);
+
+ return err;
+}
+
+static int mtk_pcie_parse_and_add_res(struct mtk_pcie *pcie)
+{
+ struct device *dev = pcie->dev;
+ struct device_node *node = dev->of_node, *child;
+ struct of_pci_range_parser parser;
+ struct of_pci_range range;
+ struct resource res;
+ int err, linkup = 0;
+
+ /* parse shared resources */
+ err = mtk_pcie_handle_shared_resource(pcie);
+ if (err)
+ return err;
+
+ if (of_pci_range_parser_init(&parser, node)) {
+ dev_err(dev, "missing \"ranges\" property\n");
+ return -EINVAL;
+ }
+
+ for_each_of_pci_range(&parser, &range) {
+ err = of_pci_range_to_resource(&range, node, &res);
+ if (err < 0)
+ return err;
+
+ switch (res.flags & IORESOURCE_TYPE_BITS) {
+ case IORESOURCE_IO:
+ pcie->offset.io = res.start - range.pci_addr;
+
+ memcpy(&pcie->pio, &res, sizeof(res));
+ pcie->pio.name = node->full_name;
+
+ pcie->io.start = range.cpu_addr;
+ pcie->io.end = range.cpu_addr + range.size - 1;
+ pcie->io.flags = IORESOURCE_MEM;
+ pcie->io.name = "I/O";
+
+ memcpy(&res, &pcie->io, sizeof(res));
+ break;
+
+ case IORESOURCE_MEM:
+ pcie->offset.mem = res.start - range.pci_addr;
+
+ memcpy(&pcie->mem, &res, sizeof(res));
+ pcie->mem.name = "non-prefetchable";
+ break;
+ }
+ }
+
+ err = of_pci_parse_bus_range(node, &pcie->busn);
+ if (err < 0) {
+ dev_err(dev, "failed to parse ranges property: %d\n", err);
+ pcie->busn.name = node->name;
+ pcie->busn.start = 0;
+ pcie->busn.end = 0xff;
+ pcie->busn.flags = IORESOURCE_BUS;
+ }
+
+ for_each_child_of_node(node, child) {
+ struct mtk_pcie_port *port;
+ int index;
+
+ err = of_pci_get_devfn(child);
+ if (err < 0) {
+ dev_err(dev, "failed to parse devfn: %d\n", err);
+ return err;
+ }
+
+ index = PCI_SLOT(err);
+
+ if (!of_device_is_available(child))
+ continue;
+
+ err = mtk_pcie_parse_ports(pcie, &port, child, index);
+ if (err)
+ return err;
+
+ /* enable each port, and then check link status */
+ err = mtk_pcie_enable_ports(port);
+ if (!err)
+ linkup++;
+ }
+
+ return !linkup;
+}
+
+static int mtk_pcie_request_resources(struct mtk_pcie *pcie)
+{
+ struct pci_host_bridge *host = pci_host_bridge_from_priv(pcie);
+ struct list_head *windows = &host->windows;
+ struct device *dev = pcie->dev;
+ int err;
+
+ pci_add_resource_offset(windows, &pcie->pio, pcie->offset.io);
+ pci_add_resource_offset(windows, &pcie->mem, pcie->offset.mem);
+ pci_add_resource(windows, &pcie->busn);
+
+ err = devm_request_pci_bus_resources(dev, windows);
+ if (err < 0)
+ return err;
+
+ pci_remap_iospace(&pcie->pio, pcie->io.start);
+
+ return 0;
+}
+
+static int mtk_pcie_register_host(struct pci_host_bridge *host)
+{
+ struct mtk_pcie *pcie = pci_host_bridge_priv(host);
+ struct pci_bus *child;
+ int err;
+
+ pci_add_flags(PCI_REASSIGN_ALL_RSRC | PCI_REASSIGN_ALL_BUS);
+ host->busnr = pcie->busn.start;
+ host->dev.parent = pcie->dev;
+ host->ops = &mtk_pcie_ops;
+
+ err = pci_register_host_bridge(host);
+ if (err < 0)
+ return err;
+
+ pci_scan_child_bus(host->bus);
+
+ pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+ pci_bus_size_bridges(host->bus);
+ pci_bus_assign_resources(host->bus);
+
+ list_for_each_entry(child, &host->bus->children, node)
+ pcie_bus_configure_settings(child);
+
+ pci_bus_add_devices(host->bus);
+
+ return 0;
+}
+
+static int mtk_pcie_probe(struct platform_device *pdev)
+{
+ struct mtk_pcie *pcie;
+ struct pci_host_bridge *host;
+ int err;
+
+ host = pci_alloc_host_bridge(sizeof(*pcie));
+ if (!host)
+ return -ENOMEM;
+
+ pcie = pci_host_bridge_priv(host);
+
+ pcie->dev = &pdev->dev;
+ platform_set_drvdata(pdev, pcie);
+ INIT_LIST_HEAD(&pcie->ports);
+
+ err = mtk_pcie_parse_and_add_res(pcie);
+ if (err)
+ return err;
+
+ err = mtk_pcie_request_resources(pcie);
+ if (err)
+ goto put_resources;
+
+ err = mtk_pcie_register_host(host);
+ if (err)
+ goto put_resources;
+
+ return 0;
+
+put_resources:
+ mtk_pcie_put_resources(pcie);
+ return err;
+}
+
+static const struct of_device_id mtk_pcie_ids[] = {
+ { .compatible = "mediatek,gen2v1-pcie"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, mtk_pcie_ids);
+
+static struct platform_driver mtk_pcie_driver = {
+ .probe = mtk_pcie_probe,
+ .driver = {
+ .name = "mtk-pcie",
+ .of_match_table = mtk_pcie_ids,
+ .suppress_bind_attrs = true,
+ },
+};
+
+builtin_platform_driver(mtk_pcie_driver);
+
+MODULE_DESCRIPTION("Mediatek PCIe Gen2 v1 host controller driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
On Fri, Apr 28, 2017 at 05:10:34PM +0800, Ryder Lee wrote:
> Add binding document for Mediatek PCIe Gen2 v1 host controller driver.
>
> Signed-off-by: Ryder Lee <[email protected]>
> ---
> .../bindings/pci/mediatek,gen2v1-pcie.txt | 174 +++++++++++++++++++++
> 1 file changed, 174 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
>
> diff --git a/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt b/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
> new file mode 100644
> index 0000000..545d8cf
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pci/mediatek,gen2v1-pcie.txt
> @@ -0,0 +1,174 @@
> +Mediatek Gen2 V1 PCIe controller
> +
> +PCIe subsys supports single root complex (RC) with 3 Root Ports. Each root
> +ports supports a Gen2 1-lane Link. It includes one Host/PCI bridge and 3
> +PCIe MAC. Each port has PIPE interface to PHY. There are 3 bus master for
> +data access and 1 bus slave for Configuration and Status Register access.
> +
> +This controller is available on MT7623 series SoCs.
> +
> +Required properties:
> +- compatible: Should contain "mediatek,gen2v1-pcie".
> +- device_type: Must be "pci"
> +- reg: Base addresses and lengths of the PCIe controller.
> +- #address-cells: Address representation for root ports (must be 3)
> + - cell 0 specifies the bus and device numbers of the root port:
> + [23:16]: bus number
> + [15:11]: device number
> + - cell 1 denotes the upper 32 address bits and should be 0
> + - cell 2 contains the lower 32 address bits and is used to translate to the
> + CPU address space
This is all standard PCI bus binding. You don't need to define it here.
"must be 3" is sufficient.
> +- #size-cells: Size representation for root ports (must be 2)
> +- #interrupt-cells: Size representation for interrupts (must be 1)
> +- interrupts: Three interrupt outputs of the controller. Must contain an
> + entry for each entry in the interrupt-names property.
Where's interrupt-names?
> +- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
> + Please refer to the standard PCI bus binding document for a more detailed
> + explanation.
> +- clocks: Must contain an entry for each entry in clock-names.
> + See ../clocks/clock-bindings.txt for details.
> +- clock-names: Must include the following entries:
> + - free_ck :for reference clock of PCIe subsys
> + - sys_ck0 :for clock of Port0 MAC
> + - sys_ck1 :for clock of Port1 MAC
> + - sys_ck2 :for clock of Port2 MAC
> +- resets: Must contain an entry for each entry in reset-names.
> + See ../reset/reset.txt for details.
> +- reset-names: Must include the following entries:
> + - pcie-rst0 :port0 reset
> + - pcie-rst1 :port1 reset
> + - pcie-rst2 :port2 reset
> +- phys: list of PHY specifiers (used by generic PHY framework)
> +- phy-names : must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
> + number of PHYs as specified in *phys* property.
> +- power-domains: A phandle and power domain specifier pair to the power domain
> + which is responsible for collapsing and restoring power to the peripheral
> +- bus-range: Range of bus numbers associated with this controller
> +- ranges: Describes the translation of addresses for root ports and standard
> + PCI regions. The entries must be 6 cells each, where the first three cells
> + correspond to the address as described for the #address-cells property
> + above, the fourth cell is the physical CPU address to translate to and the
> + fifth and six cells are as described for the #size-cells property above.
Don't need to define what ranges is here, just what the entries should
be:
> + - The first three entries are expected to translate the addresses for the root
> + port registers, which are referenced by the assigned-addresses property of
> + the root port nodes (see below).
> + - The remaining entries setup the mapping for the standard I/O and memory
> + regions.
> + Please refer to the standard PCI bus binding document for a more detailed
> + explanation.
> +
> +In addition, the device tree node must have sub-nodes describing each
> +PCIe port interface, having the following mandatory properties:
> +
> +Required properties:
> +- device_type: Must be "pci"
> +- assigned-addresses: Address and size of the port configuration registers
> +- reg: Only the first four bytes are used to refer to the correct bus number
> + and device number.
> +- #address-cells: Must be 3
> +- #size-cells: Must be 2
> +- #interrupt-cells: Size representation for interrupts (must be 1)
> +- interrupt-map-mask and interrupt-map: Standard PCI IRQ mapping properties
> + Please refer to the standard PCI bus binding document for a more detailed
> + explanation.
> +- ranges: Sub-ranges distributed from the PCIe controller node. An empty
> + property is sufficient.
> +- num-lanes: Number of lanes to use for this port.
> +
> +Examples:
> +
> +SoC dtsi:
Don't show the board vs. SoC split in examples. And drop all the status
properties.
> +
> + hifsys: syscon@1a000000 {
> + compatible = "mediatek,mt7623-hifsys", "syscon";
> + reg = <0 0x1a000000 0 0x1000>;
> + #clock-cells = <1>;
> + #reset-cells = <1>;
> + };
> +
> + pcie: pcie-controller@1a140000 {
> + compatible = "mediatek,gen2v1-pcie";
> + device_type = "pci";
> + reg = <0 0x1a140000 0 0x1000>; /* PCIe shared registers */
> + #address-cells = <3>;
> + #size-cells = <2>;
> + #interrupt-cells = <1>;
> + interrupts = <GIC_SPI 193 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 194 IRQ_TYPE_LEVEL_LOW>,
> + <GIC_SPI 195 IRQ_TYPE_LEVEL_LOW>;
> + interrupt-map-mask = <0xf800 0 0 0>;
> + interrupt-map = <0x0000 0 0 0 &gic GIC_SPI 193 IRQ_TYPE_NONE>,
> + <0x0800 0 0 0 &gic GIC_SPI 194 IRQ_TYPE_NONE>,
> + <0x1000 0 0 0 &gic GIC_SPI 195 IRQ_TYPE_NONE>;
> + clocks = <&topckgen CLK_TOP_ETHIF_SEL>,
> + <&hifsys CLK_HIFSYS_PCIE0>,
> + <&hifsys CLK_HIFSYS_PCIE1>,
> + <&hifsys CLK_HIFSYS_PCIE2>;
> + clock-names = "free_ck", "sys_ck0", "sys_ck1", "sys_ck2";
> + resets = <&hifsys MT2701_HIFSYS_PCIE0_RST>,
> + <&hifsys MT2701_HIFSYS_PCIE1_RST>,
> + <&hifsys MT2701_HIFSYS_PCIE2_RST>;
> + reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2";
> + phys = <&pcie0_phy>, <&pcie1_phy>, <&pcie2_phy>;
> + phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2";
> + power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
> + bus-range = <0x00 0xff>;
> + ranges = <0x82000000 0 0x1a142000 0 0x1a142000 0 0x1000 /* Port0 registers */
> + 0x82000000 0 0x1a143000 0 0x1a143000 0 0x1000 /* Port1 registers */
> + 0x82000000 0 0x1a144000 0 0x1a144000 0 0x1000 /* Port2 registers */
> + 0x81000000 0 0x1a160000 0 0x1a160000 0 0x00010000 /* I/O space */
> + 0x83000000 0 0x60000000 0 0x60000000 0 0x10000000>; /* memory space */
> + status = "disabled";
> +
> + pcie@1,0 {
> + device_type = "pci";
> + assigned-addresses = <0x82000000 0 0x1a142000 0 0x1000>;
> + reg = <0x0000 0 0 0 0>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 0>;
> + interrupt-map = <0 0 0 0 &gic GIC_SPI 193 IRQ_TYPE_NONE>;
> + ranges;
> + num-lanes = <1>;
> + status = "disabled";
> + };
> +
> + pcie@2,0 {
> + device_type = "pci";
> + assigned-addresses = <0x82000800 0 0x1a143000 0 0x1000>;
> + reg = <0x0800 0 0 0 0>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 0>;
> + interrupt-map = <0 0 0 0 &gic GIC_SPI 194 IRQ_TYPE_NONE>;
> + ranges;
> + num-lanes = <1>;
> + status = "disabled";
> + };
> +
> + pcie@3,0 {
> + device_type = "pci";
> + assigned-addresses = <0x82001000 0 0x1a144000 0 0x1000>;
> + reg = <0x1000 0 0 0 0>;
> + #address-cells = <3>;
> + #size-cells = <2>;
> + #interrupt-cells = <1>;
> + interrupt-map-mask = <0 0 0 0>;
> + interrupt-map = <0 0 0 0 &gic GIC_SPI 195 IRQ_TYPE_NONE>;
> + ranges;
> + num-lanes = <1>;
> + status = "disabled";
> + };
> + };
> +
> +Board dts:
> +
> + &pcie {
> + status = "okay";
> +
> + pcie@1,0 {
> + status = "okay";
> + };
> + };
> --
> 1.9.1
>
Hi Ryder,
[auto build test ERROR on pci/next]
[also build test ERROR on v4.11-rc8 next-20170428]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Ryder-Lee/Add-PCIe-host-driver-support-for-Mediatek-SoCs/20170429-181028
base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next
config: s390-allmodconfig (attached as .config)
compiler: s390x-linux-gnu-gcc (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=s390
All errors (new ones prefixed by >>):
drivers/built-in.o: In function `mtk_pcie_probe':
>> drivers/pci/host/.tmp_gl_pcie-mediatek.o:(.text+0x5b68c): undefined reference to `pci_fixup_irqs'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Ryder,
[auto build test ERROR on pci/next]
[also build test ERROR on v4.11-rc8 next-20170428]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Ryder-Lee/Add-PCIe-host-driver-support-for-Mediatek-SoCs/20170429-181028
base: https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next
config: xtensa-allmodconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 4.9.0
reproduce:
wget https://raw.githubusercontent.com/01org/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=xtensa
All errors (new ones prefixed by >>):
drivers/built-in.o: In function `pci_host_bridge_of_msi_domain':
>> (.text+0x2f154): undefined reference to `pci_fixup_irqs'
drivers/built-in.o: In function `mtk_pcie_probe':
pcie-mediatek.c:(.text+0x2f956): undefined reference to `pci_fixup_irqs'
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation