Commit a2efd681 authored by Bjorn Helgaas's avatar Bjorn Helgaas

Merge branch 'pci/host-mediatek' into next

* pci/host-mediatek:
  PCI: mediatek: Use PCI_NUM_INTX
  PCI: mediatek: Add MSI support for MT2712 and MT7622
  PCI: mediatek: Use bus->sysdata to get host private data
  dt-bindings: PCI: Add support for MT2712 and MT7622
  PCI: mediatek: Add controller support for MT2712 and MT7622
  dt-bindings: PCI: Cleanup MediaTek binding text
  dt-bindings: PCI: Rename MediaTek binding
  PCI: mediatek: Switch to use platform_get_resource_byname()
  PCI: mediatek: Add a structure to abstract the controller generations
  PCI: mediatek: Rename port->index and mtk_pcie_parse_ports()
  PCI: mediatek: Use readl_poll_timeout() to wait for Gen2 training
  PCI: mediatek: Explicitly request exclusive reset control
parents 9857f125 d84c246b
MediaTek Gen2 PCIe controller which is available on MT7623 series SoCs MediaTek Gen2 PCIe controller
PCIe subsys supports single root complex (RC) with 3 Root Ports. Each root
ports supports a Gen2 1-lane Link and has PIPE interface to PHY.
Required properties: Required properties:
- compatible: Should contain "mediatek,mt7623-pcie". - compatible: Should contain one of the following strings:
"mediatek,mt2701-pcie"
"mediatek,mt2712-pcie"
"mediatek,mt7622-pcie"
"mediatek,mt7623-pcie"
- device_type: Must be "pci" - device_type: Must be "pci"
- reg: Base addresses and lengths of the PCIe controller. - reg: Base addresses and lengths of the PCIe subsys and root ports.
- reg-names: Names of the above areas to use during resource lookup.
- #address-cells: Address representation for root ports (must be 3) - #address-cells: Address representation for root ports (must be 3)
- #size-cells: Size representation for root ports (must be 2) - #size-cells: Size representation for root ports (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.
- clocks: Must contain an entry for each entry in clock-names. - clocks: Must contain an entry for each entry in clock-names.
See ../clocks/clock-bindings.txt for details. See ../clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries: - clock-names:
- free_ck :for reference clock of PCIe subsys Mandatory entries:
- sys_ck0 :for clock of Port0 - sys_ckN :transaction layer and data link layer clock
- sys_ck1 :for clock of Port1 Required entries for MT2701/MT7623:
- sys_ck2 :for clock of Port2 - free_ck :for reference clock of PCIe subsys
- resets: Must contain an entry for each entry in reset-names. Required entries for MT2712/MT7622:
See ../reset/reset.txt for details. - ahb_ckN :AHB slave interface operating clock for CSR access and RC
- reset-names: Must include the following entries: initiated MMIO access
- pcie-rst0 :port0 reset Required entries for MT7622:
- pcie-rst1 :port1 reset - axi_ckN :application layer MMIO channel operating clock
- pcie-rst2 :port2 reset - aux_ckN :pe2_mac_bridge and pe2_mac_core operating clock when
pcie_mac_ck/pcie_pipe_ck is turned off
- obff_ckN :OBFF functional block operating clock
- pipe_ckN :LTSSM and PHY/MAC layer operating clock
where N starting from 0 to one less than the number of root ports.
- phys: List of PHY specifiers (used by generic PHY framework). - phys: List of PHY specifiers (used by generic PHY framework).
- phy-names : Must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the - phy-names : Must be "pcie-phy0", "pcie-phy1", "pcie-phyN".. based on the
number of PHYs as specified in *phys* property. number of PHYs as specified in *phys* property.
...@@ -34,6 +36,20 @@ Required properties: ...@@ -34,6 +36,20 @@ Required properties:
- bus-range: Range of bus numbers associated with this controller. - bus-range: Range of bus numbers associated with this controller.
- ranges: Ranges for the PCI memory and I/O regions. - ranges: Ranges for the PCI memory and I/O regions.
Required properties for MT7623/MT2701:
- #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.
- resets: Must contain an entry for each entry in reset-names.
See ../reset/reset.txt for details.
- reset-names: Must be "pcie-rst0", "pcie-rst1", "pcie-rstN".. based on the
number of root ports.
Required properties for MT2712/MT7622:
-interrupts: A list of interrupt outputs of the controller, must have one
entry for each PCIe port
In addition, the device tree node must have sub-nodes describing each In addition, the device tree node must have sub-nodes describing each
PCIe port interface, having the following mandatory properties: PCIe port interface, having the following mandatory properties:
...@@ -51,7 +67,7 @@ Required properties: ...@@ -51,7 +67,7 @@ Required properties:
property is sufficient. property is sufficient.
- num-lanes: Number of lanes to use for this port. - num-lanes: Number of lanes to use for this port.
Examples: Examples for MT7623:
hifsys: syscon@1a000000 { hifsys: syscon@1a000000 {
compatible = "mediatek,mt7623-hifsys", compatible = "mediatek,mt7623-hifsys",
...@@ -69,6 +85,7 @@ Examples: ...@@ -69,6 +85,7 @@ Examples:
<0 0x1a142000 0 0x1000>, /* Port0 registers */ <0 0x1a142000 0 0x1000>, /* Port0 registers */
<0 0x1a143000 0 0x1000>, /* Port1 registers */ <0 0x1a143000 0 0x1000>, /* Port1 registers */
<0 0x1a144000 0 0x1000>; /* Port2 registers */ <0 0x1a144000 0 0x1000>; /* Port2 registers */
reg-names = "subsys", "port0", "port1", "port2";
#address-cells = <3>; #address-cells = <3>;
#size-cells = <2>; #size-cells = <2>;
#interrupt-cells = <1>; #interrupt-cells = <1>;
...@@ -85,7 +102,8 @@ Examples: ...@@ -85,7 +102,8 @@ Examples:
<&hifsys MT2701_HIFSYS_PCIE1_RST>, <&hifsys MT2701_HIFSYS_PCIE1_RST>,
<&hifsys MT2701_HIFSYS_PCIE2_RST>; <&hifsys MT2701_HIFSYS_PCIE2_RST>;
reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2"; reset-names = "pcie-rst0", "pcie-rst1", "pcie-rst2";
phys = <&pcie0_phy>, <&pcie1_phy>, <&pcie2_phy>; phys = <&pcie0_phy PHY_TYPE_PCIE>, <&pcie1_phy PHY_TYPE_PCIE>,
<&pcie2_phy PHY_TYPE_PCIE>;
phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2"; phy-names = "pcie-phy0", "pcie-phy1", "pcie-phy2";
power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>; power-domains = <&scpsys MT2701_POWER_DOMAIN_HIF>;
bus-range = <0x00 0xff>; bus-range = <0x00 0xff>;
...@@ -128,3 +146,139 @@ Examples: ...@@ -128,3 +146,139 @@ Examples:
num-lanes = <1>; num-lanes = <1>;
}; };
}; };
Examples for MT2712:
pcie: pcie@11700000 {
compatible = "mediatek,mt2712-pcie";
device_type = "pci";
reg = <0 0x11700000 0 0x1000>,
<0 0x112ff000 0 0x1000>;
reg-names = "port0", "port1";
#address-cells = <3>;
#size-cells = <2>;
interrupts = <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&topckgen CLK_TOP_PE2_MAC_P0_SEL>,
<&topckgen CLK_TOP_PE2_MAC_P1_SEL>,
<&pericfg CLK_PERI_PCIE0>,
<&pericfg CLK_PERI_PCIE1>;
clock-names = "sys_ck0", "sys_ck1", "ahb_ck0", "ahb_ck1";
phys = <&pcie0_phy PHY_TYPE_PCIE>, <&pcie1_phy PHY_TYPE_PCIE>;
phy-names = "pcie-phy0", "pcie-phy1";
bus-range = <0x00 0xff>;
ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>;
pcie0: pcie@0,0 {
device_type = "pci";
reg = <0x0000 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
ranges;
num-lanes = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc0 0>,
<0 0 0 2 &pcie_intc0 1>,
<0 0 0 3 &pcie_intc0 2>,
<0 0 0 4 &pcie_intc0 3>;
pcie_intc0: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
pcie1: pcie@1,0 {
device_type = "pci";
reg = <0x0800 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
ranges;
num-lanes = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc1 0>,
<0 0 0 2 &pcie_intc1 1>,
<0 0 0 3 &pcie_intc1 2>,
<0 0 0 4 &pcie_intc1 3>;
pcie_intc1: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
};
Examples for MT7622:
pcie: pcie@1a140000 {
compatible = "mediatek,mt7622-pcie";
device_type = "pci";
reg = <0 0x1a140000 0 0x1000>,
<0 0x1a143000 0 0x1000>,
<0 0x1a145000 0 0x1000>;
reg-names = "subsys", "port0", "port1";
#address-cells = <3>;
#size-cells = <2>;
interrupts = <GIC_SPI 228 IRQ_TYPE_LEVEL_LOW>,
<GIC_SPI 229 IRQ_TYPE_LEVEL_LOW>;
clocks = <&pciesys CLK_PCIE_P0_MAC_EN>,
<&pciesys CLK_PCIE_P1_MAC_EN>,
<&pciesys CLK_PCIE_P0_AHB_EN>,
<&pciesys CLK_PCIE_P1_AHB_EN>,
<&pciesys CLK_PCIE_P0_AUX_EN>,
<&pciesys CLK_PCIE_P1_AUX_EN>,
<&pciesys CLK_PCIE_P0_AXI_EN>,
<&pciesys CLK_PCIE_P1_AXI_EN>,
<&pciesys CLK_PCIE_P0_OBFF_EN>,
<&pciesys CLK_PCIE_P1_OBFF_EN>,
<&pciesys CLK_PCIE_P0_PIPE_EN>,
<&pciesys CLK_PCIE_P1_PIPE_EN>;
clock-names = "sys_ck0", "sys_ck1", "ahb_ck0", "ahb_ck1",
"aux_ck0", "aux_ck1", "axi_ck0", "axi_ck1",
"obff_ck0", "obff_ck1", "pipe_ck0", "pipe_ck1";
phys = <&pcie0_phy PHY_TYPE_PCIE>, <&pcie1_phy PHY_TYPE_PCIE>;
phy-names = "pcie-phy0", "pcie-phy1";
power-domains = <&scpsys MT7622_POWER_DOMAIN_HIF0>;
bus-range = <0x00 0xff>;
ranges = <0x82000000 0 0x20000000 0x0 0x20000000 0 0x10000000>;
pcie0: pcie@0,0 {
device_type = "pci";
reg = <0x0000 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
ranges;
num-lanes = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc0 0>,
<0 0 0 2 &pcie_intc0 1>,
<0 0 0 3 &pcie_intc0 2>,
<0 0 0 4 &pcie_intc0 3>;
pcie_intc0: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
pcie1: pcie@1,0 {
device_type = "pci";
reg = <0x0800 0 0 0 0>;
#address-cells = <3>;
#size-cells = <2>;
#interrupt-cells = <1>;
ranges;
num-lanes = <1>;
interrupt-map-mask = <0 0 0 7>;
interrupt-map = <0 0 0 1 &pcie_intc1 0>,
<0 0 0 2 &pcie_intc1 1>,
<0 0 0 3 &pcie_intc1 2>,
<0 0 0 4 &pcie_intc1 3>;
pcie_intc1: interrupt-controller {
interrupt-controller;
#address-cells = <0>;
#interrupt-cells = <1>;
};
};
};
...@@ -182,14 +182,13 @@ config PCIE_ROCKCHIP ...@@ -182,14 +182,13 @@ config PCIE_ROCKCHIP
config PCIE_MEDIATEK config PCIE_MEDIATEK
bool "MediaTek PCIe controller" bool "MediaTek PCIe controller"
depends on ARM && (ARCH_MEDIATEK || COMPILE_TEST) depends on (ARM || ARM64) && (ARCH_MEDIATEK || COMPILE_TEST)
depends on OF depends on OF
depends on PCI depends on PCI
select PCIEPORTBUS select PCIEPORTBUS
help help
Say Y here if you want to enable PCIe controller support on Say Y here if you want to enable PCIe controller support on
MT7623 series SoCs. There is one single root complex with 3 root MediaTek SoCs.
ports available. Each port supports Gen2 lane x1.
config PCIE_TANGO_SMP8759 config PCIE_TANGO_SMP8759
bool "Tango SMP8759 PCIe controller (DANGEROUS)" bool "Tango SMP8759 PCIe controller (DANGEROUS)"
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
* *
* Copyright (c) 2017 MediaTek Inc. * Copyright (c) 2017 MediaTek Inc.
* Author: Ryder Lee <ryder.lee@mediatek.com> * Author: Ryder Lee <ryder.lee@mediatek.com>
* Honghui Zhang <honghui.zhang@mediatek.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License version 2 as
...@@ -16,6 +17,9 @@ ...@@ -16,6 +17,9 @@
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_pci.h> #include <linux/of_pci.h>
...@@ -63,16 +67,104 @@ ...@@ -63,16 +67,104 @@
#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16)) #define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
#define PCIE_FC_CREDIT_VAL(x) ((x) << 16) #define PCIE_FC_CREDIT_VAL(x) ((x) << 16)
/* PCIe V2 share registers */
#define PCIE_SYS_CFG_V2 0x0
#define PCIE_CSR_LTSSM_EN(x) BIT(0 + (x) * 8)
#define PCIE_CSR_ASPM_L1_EN(x) BIT(1 + (x) * 8)
/* PCIe V2 per-port registers */
#define PCIE_MSI_VECTOR 0x0c0
#define PCIE_INT_MASK 0x420
#define INTX_MASK GENMASK(19, 16)
#define INTX_SHIFT 16
#define PCIE_INT_STATUS 0x424
#define MSI_STATUS BIT(23)
#define PCIE_IMSI_STATUS 0x42c
#define PCIE_IMSI_ADDR 0x430
#define MSI_MASK BIT(23)
#define MTK_MSI_IRQS_NUM 32
#define PCIE_AHB_TRANS_BASE0_L 0x438
#define PCIE_AHB_TRANS_BASE0_H 0x43c
#define AHB2PCIE_SIZE(x) ((x) & GENMASK(4, 0))
#define PCIE_AXI_WINDOW0 0x448
#define WIN_ENABLE BIT(7)
/* PCIe V2 configuration transaction header */
#define PCIE_CFG_HEADER0 0x460
#define PCIE_CFG_HEADER1 0x464
#define PCIE_CFG_HEADER2 0x468
#define PCIE_CFG_WDATA 0x470
#define PCIE_APP_TLP_REQ 0x488
#define PCIE_CFG_RDATA 0x48c
#define APP_CFG_REQ BIT(0)
#define APP_CPL_STATUS GENMASK(7, 5)
#define CFG_WRRD_TYPE_0 4
#define CFG_WR_FMT 2
#define CFG_RD_FMT 0
#define CFG_DW0_LENGTH(length) ((length) & GENMASK(9, 0))
#define CFG_DW0_TYPE(type) (((type) << 24) & GENMASK(28, 24))
#define CFG_DW0_FMT(fmt) (((fmt) << 29) & GENMASK(31, 29))
#define CFG_DW2_REGN(regn) ((regn) & GENMASK(11, 2))
#define CFG_DW2_FUN(fun) (((fun) << 16) & GENMASK(18, 16))
#define CFG_DW2_DEV(dev) (((dev) << 19) & GENMASK(23, 19))
#define CFG_DW2_BUS(bus) (((bus) << 24) & GENMASK(31, 24))
#define CFG_HEADER_DW0(type, fmt) \
(CFG_DW0_LENGTH(1) | CFG_DW0_TYPE(type) | CFG_DW0_FMT(fmt))
#define CFG_HEADER_DW1(where, size) \
(GENMASK(((size) - 1), 0) << ((where) & 0x3))
#define CFG_HEADER_DW2(regn, fun, dev, bus) \
(CFG_DW2_REGN(regn) | CFG_DW2_FUN(fun) | \
CFG_DW2_DEV(dev) | CFG_DW2_BUS(bus))
#define PCIE_RST_CTRL 0x510
#define PCIE_PHY_RSTB BIT(0)
#define PCIE_PIPE_SRSTB BIT(1)
#define PCIE_MAC_SRSTB BIT(2)
#define PCIE_CRSTB BIT(3)
#define PCIE_PERSTB BIT(8)
#define PCIE_LINKDOWN_RST_EN GENMASK(15, 13)
#define PCIE_LINK_STATUS_V2 0x804
#define PCIE_PORT_LINKUP_V2 BIT(10)
struct mtk_pcie_port;
/**
* struct mtk_pcie_soc - differentiate between host generations
* @has_msi: whether this host supports MSI interrupts or not
* @ops: pointer to configuration access functions
* @startup: pointer to controller setting functions
* @setup_irq: pointer to initialize IRQ functions
*/
struct mtk_pcie_soc {
bool has_msi;
struct pci_ops *ops;
int (*startup)(struct mtk_pcie_port *port);
int (*setup_irq)(struct mtk_pcie_port *port, struct device_node *node);
};
/** /**
* struct mtk_pcie_port - PCIe port information * struct mtk_pcie_port - PCIe port information
* @base: IO mapped register base * @base: IO mapped register base
* @list: port list * @list: port list
* @pcie: pointer to PCIe host info * @pcie: pointer to PCIe host info
* @reset: pointer to port reset control * @reset: pointer to port reset control
* @sys_ck: pointer to bus clock * @sys_ck: pointer to transaction/data link layer clock
* @phy: pointer to phy control block * @ahb_ck: pointer to AHB slave interface operating clock for CSR access
* and RC initiated MMIO access
* @axi_ck: pointer to application layer MMIO channel operating clock
* @aux_ck: pointer to pe2_mac_bridge and pe2_mac_core operating clock
* when pcie_mac_ck/pcie_pipe_ck is turned off
* @obff_ck: pointer to OBFF functional block operating clock
* @pipe_ck: pointer to LTSSM and PHY/MAC layer operating clock
* @phy: pointer to PHY control block
* @lane: lane count * @lane: lane count
* @index: port index * @slot: port slot
* @irq_domain: legacy INTx IRQ domain
* @msi_domain: MSI IRQ domain
* @msi_irq_in_use: bit map for assigned MSI IRQ
*/ */
struct mtk_pcie_port { struct mtk_pcie_port {
void __iomem *base; void __iomem *base;
...@@ -80,9 +172,17 @@ struct mtk_pcie_port { ...@@ -80,9 +172,17 @@ struct mtk_pcie_port {
struct mtk_pcie *pcie; struct mtk_pcie *pcie;
struct reset_control *reset; struct reset_control *reset;
struct clk *sys_ck; struct clk *sys_ck;
struct clk *ahb_ck;
struct clk *axi_ck;
struct clk *aux_ck;
struct clk *obff_ck;
struct clk *pipe_ck;
struct phy *phy; struct phy *phy;
u32 lane; u32 lane;
u32 index; u32 slot;
struct irq_domain *irq_domain;
struct irq_domain *msi_domain;
DECLARE_BITMAP(msi_irq_in_use, MTK_MSI_IRQS_NUM);
}; };
/** /**
...@@ -96,6 +196,7 @@ struct mtk_pcie_port { ...@@ -96,6 +196,7 @@ struct mtk_pcie_port {
* @busn: bus range * @busn: bus range
* @offset: IO / Memory offset * @offset: IO / Memory offset
* @ports: pointer to PCIe port information * @ports: pointer to PCIe port information
* @soc: pointer to SoC-dependent operations
*/ */
struct mtk_pcie { struct mtk_pcie {
struct device *dev; struct device *dev;
...@@ -111,13 +212,9 @@ struct mtk_pcie { ...@@ -111,13 +212,9 @@ struct mtk_pcie {
resource_size_t io; resource_size_t io;
} offset; } offset;
struct list_head ports; struct list_head ports;
const struct mtk_pcie_soc *soc;
}; };
static inline bool mtk_pcie_link_up(struct mtk_pcie_port *port)
{
return !!(readl(port->base + PCIE_LINK_STATUS) & PCIE_PORT_LINKUP);
}
static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie) static void mtk_pcie_subsys_powerdown(struct mtk_pcie *pcie)
{ {
struct device *dev = pcie->dev; struct device *dev = pcie->dev;
...@@ -146,6 +243,12 @@ static void mtk_pcie_put_resources(struct mtk_pcie *pcie) ...@@ -146,6 +243,12 @@ static void mtk_pcie_put_resources(struct mtk_pcie *pcie)
list_for_each_entry_safe(port, tmp, &pcie->ports, list) { list_for_each_entry_safe(port, tmp, &pcie->ports, list) {
phy_power_off(port->phy); phy_power_off(port->phy);
phy_exit(port->phy);
clk_disable_unprepare(port->pipe_ck);
clk_disable_unprepare(port->obff_ck);
clk_disable_unprepare(port->axi_ck);
clk_disable_unprepare(port->aux_ck);
clk_disable_unprepare(port->ahb_ck);
clk_disable_unprepare(port->sys_ck); clk_disable_unprepare(port->sys_ck);
mtk_pcie_port_free(port); mtk_pcie_port_free(port);
} }
...@@ -153,11 +256,412 @@ static void mtk_pcie_put_resources(struct mtk_pcie *pcie) ...@@ -153,11 +256,412 @@ static void mtk_pcie_put_resources(struct mtk_pcie *pcie)
mtk_pcie_subsys_powerdown(pcie); mtk_pcie_subsys_powerdown(pcie);
} }
static int mtk_pcie_check_cfg_cpld(struct mtk_pcie_port *port)
{
u32 val;
int err;
err = readl_poll_timeout_atomic(port->base + PCIE_APP_TLP_REQ, val,
!(val & APP_CFG_REQ), 10,
100 * USEC_PER_MSEC);
if (err)
return PCIBIOS_SET_FAILED;
if (readl(port->base + PCIE_APP_TLP_REQ) & APP_CPL_STATUS)
return PCIBIOS_SET_FAILED;
return PCIBIOS_SUCCESSFUL;
}
static int mtk_pcie_hw_rd_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn,
int where, int size, u32 *val)
{
u32 tmp;
/* Write PCIe configuration transaction header for Cfgrd */
writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_RD_FMT),
port->base + PCIE_CFG_HEADER0);
writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1);
writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus),
port->base + PCIE_CFG_HEADER2);
/* Trigger h/w to transmit Cfgrd TLP */
tmp = readl(port->base + PCIE_APP_TLP_REQ);
tmp |= APP_CFG_REQ;
writel(tmp, port->base + PCIE_APP_TLP_REQ);
/* Check completion status */
if (mtk_pcie_check_cfg_cpld(port))
return PCIBIOS_SET_FAILED;
/* Read cpld payload of Cfgrd */
*val = readl(port->base + PCIE_CFG_RDATA);
if (size == 1)
*val = (*val >> (8 * (where & 3))) & 0xff;
else if (size == 2)
*val = (*val >> (8 * (where & 3))) & 0xffff;
return PCIBIOS_SUCCESSFUL;
}
static int mtk_pcie_hw_wr_cfg(struct mtk_pcie_port *port, u32 bus, u32 devfn,
int where, int size, u32 val)
{
/* Write PCIe configuration transaction header for Cfgwr */
writel(CFG_HEADER_DW0(CFG_WRRD_TYPE_0, CFG_WR_FMT),
port->base + PCIE_CFG_HEADER0);
writel(CFG_HEADER_DW1(where, size), port->base + PCIE_CFG_HEADER1);
writel(CFG_HEADER_DW2(where, PCI_FUNC(devfn), PCI_SLOT(devfn), bus),
port->base + PCIE_CFG_HEADER2);
/* Write Cfgwr data */
val = val << 8 * (where & 3);
writel(val, port->base + PCIE_CFG_WDATA);
/* Trigger h/w to transmit Cfgwr TLP */
val = readl(port->base + PCIE_APP_TLP_REQ);
val |= APP_CFG_REQ;
writel(val, port->base + PCIE_APP_TLP_REQ);
/* Check completion status */
return mtk_pcie_check_cfg_cpld(port);
}
static struct mtk_pcie_port *mtk_pcie_find_port(struct pci_bus *bus,
unsigned int devfn)
{
struct mtk_pcie *pcie = bus->sysdata;
struct mtk_pcie_port *port;
list_for_each_entry(port, &pcie->ports, list)
if (port->slot == PCI_SLOT(devfn))
return port;
return NULL;
}
static int mtk_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val)
{
struct mtk_pcie_port *port;
u32 bn = bus->number;
int ret;
port = mtk_pcie_find_port(bus, devfn);
if (!port) {
*val = ~0;
return PCIBIOS_DEVICE_NOT_FOUND;
}
ret = mtk_pcie_hw_rd_cfg(port, bn, devfn, where, size, val);
if (ret)
*val = ~0;
return ret;
}
static int mtk_pcie_config_write(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 val)
{
struct mtk_pcie_port *port;
u32 bn = bus->number;
port = mtk_pcie_find_port(bus, devfn);
if (!port)
return PCIBIOS_DEVICE_NOT_FOUND;
return mtk_pcie_hw_wr_cfg(port, bn, devfn, where, size, val);
}
static struct pci_ops mtk_pcie_ops_v2 = {
.read = mtk_pcie_config_read,
.write = mtk_pcie_config_write,
};
static int mtk_pcie_startup_port_v2(struct mtk_pcie_port *port)
{
struct mtk_pcie *pcie = port->pcie;
struct resource *mem = &pcie->mem;
u32 val;
size_t size;
int err;
/* MT7622 platforms need to enable LTSSM and ASPM from PCIe subsys */
if (pcie->base) {
val = readl(pcie->base + PCIE_SYS_CFG_V2);
val |= PCIE_CSR_LTSSM_EN(port->slot) |
PCIE_CSR_ASPM_L1_EN(port->slot);
writel(val, pcie->base + PCIE_SYS_CFG_V2);
}
/* Assert all reset signals */
writel(0, port->base + PCIE_RST_CTRL);
/*
* Enable PCIe link down reset, if link status changed from link up to
* link down, this will reset MAC control registers and configuration
* space.
*/
writel(PCIE_LINKDOWN_RST_EN, port->base + PCIE_RST_CTRL);
/* De-assert PHY, PE, PIPE, MAC and configuration reset */
val = readl(port->base + PCIE_RST_CTRL);
val |= PCIE_PHY_RSTB | PCIE_PERSTB | PCIE_PIPE_SRSTB |
PCIE_MAC_SRSTB | PCIE_CRSTB;
writel(val, port->base + PCIE_RST_CTRL);
/* 100ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout(port->base + PCIE_LINK_STATUS_V2, val,
!!(val & PCIE_PORT_LINKUP_V2), 20,
100 * USEC_PER_MSEC);
if (err)
return -ETIMEDOUT;
/* Set INTx mask */
val = readl(port->base + PCIE_INT_MASK);
val &= ~INTX_MASK;
writel(val, port->base + PCIE_INT_MASK);
/* Set AHB to PCIe translation windows */
size = mem->end - mem->start;
val = lower_32_bits(mem->start) | AHB2PCIE_SIZE(fls(size));
writel(val, port->base + PCIE_AHB_TRANS_BASE0_L);
val = upper_32_bits(mem->start);
writel(val, port->base + PCIE_AHB_TRANS_BASE0_H);
/* Set PCIe to AXI translation memory space.*/
val = fls(0xffffffff) | WIN_ENABLE;
writel(val, port->base + PCIE_AXI_WINDOW0);
return 0;
}
static int mtk_pcie_msi_alloc(struct mtk_pcie_port *port)
{
int msi;
msi = find_first_zero_bit(port->msi_irq_in_use, MTK_MSI_IRQS_NUM);
if (msi < MTK_MSI_IRQS_NUM)
set_bit(msi, port->msi_irq_in_use);
else
return -ENOSPC;
return msi;
}
static void mtk_pcie_msi_free(struct mtk_pcie_port *port, unsigned long hwirq)
{
clear_bit(hwirq, port->msi_irq_in_use);
}
static int mtk_pcie_msi_setup_irq(struct msi_controller *chip,
struct pci_dev *pdev, struct msi_desc *desc)
{
struct mtk_pcie_port *port;
struct msi_msg msg;
unsigned int irq;
int hwirq;
phys_addr_t msg_addr;
port = mtk_pcie_find_port(pdev->bus, pdev->devfn);
if (!port)
return -EINVAL;
hwirq = mtk_pcie_msi_alloc(port);
if (hwirq < 0)
return hwirq;
irq = irq_create_mapping(port->msi_domain, hwirq);
if (!irq) {
mtk_pcie_msi_free(port, hwirq);
return -EINVAL;
}
chip->dev = &pdev->dev;
irq_set_msi_desc(irq, desc);
/* MT2712/MT7622 only support 32-bit MSI addresses */
msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
msg.address_hi = 0;
msg.address_lo = lower_32_bits(msg_addr);
msg.data = hwirq;
pci_write_msi_msg(irq, &msg);
return 0;
}
static void mtk_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
{
struct pci_dev *pdev = to_pci_dev(chip->dev);
struct irq_data *d = irq_get_irq_data(irq);
irq_hw_number_t hwirq = irqd_to_hwirq(d);
struct mtk_pcie_port *port;
port = mtk_pcie_find_port(pdev->bus, pdev->devfn);
if (!port)
return;
irq_dispose_mapping(irq);
mtk_pcie_msi_free(port, hwirq);
}
static struct msi_controller mtk_pcie_msi_chip = {
.setup_irq = mtk_pcie_msi_setup_irq,
.teardown_irq = mtk_msi_teardown_irq,
};
static struct irq_chip mtk_msi_irq_chip = {
.name = "MTK PCIe MSI",
.irq_enable = pci_msi_unmask_irq,
.irq_disable = pci_msi_mask_irq,
.irq_mask = pci_msi_mask_irq,
.irq_unmask = pci_msi_unmask_irq,
};
static int mtk_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_and_handler(irq, &mtk_msi_irq_chip, handle_simple_irq);
irq_set_chip_data(irq, domain->host_data);
return 0;
}
static const struct irq_domain_ops msi_domain_ops = {
.map = mtk_pcie_msi_map,
};
static void mtk_pcie_enable_msi(struct mtk_pcie_port *port)
{
u32 val;
phys_addr_t msg_addr;
msg_addr = virt_to_phys(port->base + PCIE_MSI_VECTOR);
val = lower_32_bits(msg_addr);
writel(val, port->base + PCIE_IMSI_ADDR);
val = readl(port->base + PCIE_INT_MASK);
val &= ~MSI_MASK;
writel(val, port->base + PCIE_INT_MASK);
}
static int mtk_pcie_intx_map(struct irq_domain *domain, unsigned int irq,
irq_hw_number_t hwirq)
{
irq_set_chip_and_handler(irq, &dummy_irq_chip, handle_simple_irq);
irq_set_chip_data(irq, domain->host_data);
return 0;
}
static const struct irq_domain_ops intx_domain_ops = {
.map = mtk_pcie_intx_map,
};
static int mtk_pcie_init_irq_domain(struct mtk_pcie_port *port,
struct device_node *node)
{
struct device *dev = port->pcie->dev;
struct device_node *pcie_intc_node;
/* Setup INTx */
pcie_intc_node = of_get_next_child(node, NULL);
if (!pcie_intc_node) {
dev_err(dev, "no PCIe Intc node found\n");
return -ENODEV;
}
port->irq_domain = irq_domain_add_linear(pcie_intc_node, PCI_NUM_INTX,
&intx_domain_ops, port);
if (!port->irq_domain) {
dev_err(dev, "failed to get INTx IRQ domain\n");
return -ENODEV;
}
if (IS_ENABLED(CONFIG_PCI_MSI)) {
port->msi_domain = irq_domain_add_linear(node, MTK_MSI_IRQS_NUM,
&msi_domain_ops,
&mtk_pcie_msi_chip);
if (!port->msi_domain) {
dev_err(dev, "failed to create MSI IRQ domain\n");
return -ENODEV;
}
mtk_pcie_enable_msi(port);
}
return 0;
}
static irqreturn_t mtk_pcie_intr_handler(int irq, void *data)
{
struct mtk_pcie_port *port = (struct mtk_pcie_port *)data;
unsigned long status;
u32 virq;
u32 bit = INTX_SHIFT;
while ((status = readl(port->base + PCIE_INT_STATUS)) & INTX_MASK) {
for_each_set_bit_from(bit, &status, PCI_NUM_INTX + INTX_SHIFT) {
/* Clear the INTx */
writel(1 << bit, port->base + PCIE_INT_STATUS);
virq = irq_find_mapping(port->irq_domain,
bit - INTX_SHIFT);
generic_handle_irq(virq);
}
}
if (IS_ENABLED(CONFIG_PCI_MSI)) {
while ((status = readl(port->base + PCIE_INT_STATUS)) & MSI_STATUS) {
unsigned long imsi_status;
while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
/* Clear the MSI */
writel(1 << bit, port->base + PCIE_IMSI_STATUS);
virq = irq_find_mapping(port->msi_domain, bit);
generic_handle_irq(virq);
}
}
/* Clear MSI interrupt status */
writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
}
}
return IRQ_HANDLED;
}
static int mtk_pcie_setup_irq(struct mtk_pcie_port *port,
struct device_node *node)
{
struct mtk_pcie *pcie = port->pcie;
struct device *dev = pcie->dev;
struct platform_device *pdev = to_platform_device(dev);
int err, irq;
irq = platform_get_irq(pdev, port->slot);
err = devm_request_irq(dev, irq, mtk_pcie_intr_handler,
IRQF_SHARED, "mtk-pcie", port);
if (err) {
dev_err(dev, "unable to request IRQ %d\n", irq);
return err;
}
err = mtk_pcie_init_irq_domain(port, node);
if (err) {
dev_err(dev, "failed to init PCIe IRQ domain\n");
return err;
}
return 0;
}
static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus, static void __iomem *mtk_pcie_map_bus(struct pci_bus *bus,
unsigned int devfn, int where) unsigned int devfn, int where)
{ {
struct pci_host_bridge *host = pci_find_host_bridge(bus); struct mtk_pcie *pcie = bus->sysdata;
struct mtk_pcie *pcie = pci_host_bridge_priv(host);
writel(PCIE_CONF_ADDR(where, PCI_FUNC(devfn), PCI_SLOT(devfn), writel(PCIE_CONF_ADDR(where, PCI_FUNC(devfn), PCI_SLOT(devfn),
bus->number), pcie->base + PCIE_CFG_ADDR); bus->number), pcie->base + PCIE_CFG_ADDR);
...@@ -171,16 +675,34 @@ static struct pci_ops mtk_pcie_ops = { ...@@ -171,16 +675,34 @@ static struct pci_ops mtk_pcie_ops = {
.write = pci_generic_config_write, .write = pci_generic_config_write,
}; };
static void mtk_pcie_configure_rc(struct mtk_pcie_port *port) static int mtk_pcie_startup_port(struct mtk_pcie_port *port)
{ {
struct mtk_pcie *pcie = port->pcie; struct mtk_pcie *pcie = port->pcie;
u32 func = PCI_FUNC(port->index << 3); u32 func = PCI_FUNC(port->slot << 3);
u32 slot = PCI_SLOT(port->index << 3); u32 slot = PCI_SLOT(port->slot << 3);
u32 val; u32 val;
int err;
/* assert port PERST_N */
val = readl(pcie->base + PCIE_SYS_CFG);
val |= PCIE_PORT_PERST(port->slot);
writel(val, pcie->base + PCIE_SYS_CFG);
/* de-assert port PERST_N */
val = readl(pcie->base + PCIE_SYS_CFG);
val &= ~PCIE_PORT_PERST(port->slot);
writel(val, pcie->base + PCIE_SYS_CFG);
/* 100ms timeout value should be enough for Gen1/2 training */
err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val,
!!(val & PCIE_PORT_LINKUP), 20,
100 * USEC_PER_MSEC);
if (err)
return -ETIMEDOUT;
/* enable interrupt */ /* enable interrupt */
val = readl(pcie->base + PCIE_INT_ENABLE); val = readl(pcie->base + PCIE_INT_ENABLE);
val |= PCIE_PORT_INT_EN(port->index); val |= PCIE_PORT_INT_EN(port->slot);
writel(val, pcie->base + PCIE_INT_ENABLE); writel(val, pcie->base + PCIE_INT_ENABLE);
/* map to all DDR region. We need to set it before cfg operation. */ /* map to all DDR region. We need to set it before cfg operation. */
...@@ -209,67 +731,94 @@ static void mtk_pcie_configure_rc(struct mtk_pcie_port *port) ...@@ -209,67 +731,94 @@ static void mtk_pcie_configure_rc(struct mtk_pcie_port *port)
writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0), writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, func, slot, 0),
pcie->base + PCIE_CFG_ADDR); pcie->base + PCIE_CFG_ADDR);
writel(val, pcie->base + PCIE_CFG_DATA); writel(val, pcie->base + PCIE_CFG_DATA);
return 0;
} }
static void mtk_pcie_assert_ports(struct mtk_pcie_port *port) static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
{ {
struct mtk_pcie *pcie = port->pcie; struct mtk_pcie *pcie = port->pcie;
u32 val; struct device *dev = pcie->dev;
int err;
/* assert port PERST_N */ err = clk_prepare_enable(port->sys_ck);
val = readl(pcie->base + PCIE_SYS_CFG); if (err) {
val |= PCIE_PORT_PERST(port->index); dev_err(dev, "failed to enable sys_ck%d clock\n", port->slot);
writel(val, pcie->base + PCIE_SYS_CFG); goto err_sys_clk;
}
/* de-assert port PERST_N */ err = clk_prepare_enable(port->ahb_ck);
val = readl(pcie->base + PCIE_SYS_CFG); if (err) {
val &= ~PCIE_PORT_PERST(port->index); dev_err(dev, "failed to enable ahb_ck%d\n", port->slot);
writel(val, pcie->base + PCIE_SYS_CFG); goto err_ahb_clk;
}
/* PCIe v2.0 need at least 100ms delay to train from Gen1 to Gen2 */ err = clk_prepare_enable(port->aux_ck);
msleep(100); if (err) {
} dev_err(dev, "failed to enable aux_ck%d\n", port->slot);
goto err_aux_clk;
}
static void mtk_pcie_enable_ports(struct mtk_pcie_port *port) err = clk_prepare_enable(port->axi_ck);
{ if (err) {
struct device *dev = port->pcie->dev; dev_err(dev, "failed to enable axi_ck%d\n", port->slot);
int err; goto err_axi_clk;
}
err = clk_prepare_enable(port->sys_ck); err = clk_prepare_enable(port->obff_ck);
if (err) { if (err) {
dev_err(dev, "failed to enable port%d clock\n", port->index); dev_err(dev, "failed to enable obff_ck%d\n", port->slot);
goto err_sys_clk; goto err_obff_clk;
}
err = clk_prepare_enable(port->pipe_ck);
if (err) {
dev_err(dev, "failed to enable pipe_ck%d\n", port->slot);
goto err_pipe_clk;
} }
reset_control_assert(port->reset); reset_control_assert(port->reset);
reset_control_deassert(port->reset); reset_control_deassert(port->reset);
err = phy_init(port->phy);
if (err) {
dev_err(dev, "failed to initialize port%d phy\n", port->slot);
goto err_phy_init;
}
err = phy_power_on(port->phy); err = phy_power_on(port->phy);
if (err) { if (err) {
dev_err(dev, "failed to power on port%d phy\n", port->index); dev_err(dev, "failed to power on port%d phy\n", port->slot);
goto err_phy_on; goto err_phy_on;
} }
mtk_pcie_assert_ports(port); if (!pcie->soc->startup(port))
/* if link up, then setup root port configuration space */
if (mtk_pcie_link_up(port)) {
mtk_pcie_configure_rc(port);
return; return;
}
dev_info(dev, "Port%d link down\n", port->index); dev_info(dev, "Port%d link down\n", port->slot);
phy_power_off(port->phy); phy_power_off(port->phy);
err_phy_on: err_phy_on:
phy_exit(port->phy);
err_phy_init:
clk_disable_unprepare(port->pipe_ck);
err_pipe_clk:
clk_disable_unprepare(port->obff_ck);
err_obff_clk:
clk_disable_unprepare(port->axi_ck);
err_axi_clk:
clk_disable_unprepare(port->aux_ck);
err_aux_clk:
clk_disable_unprepare(port->ahb_ck);
err_ahb_clk:
clk_disable_unprepare(port->sys_ck); clk_disable_unprepare(port->sys_ck);
err_sys_clk: err_sys_clk:
mtk_pcie_port_free(port); mtk_pcie_port_free(port);
} }
static int mtk_pcie_parse_ports(struct mtk_pcie *pcie, static int mtk_pcie_parse_port(struct mtk_pcie *pcie,
struct device_node *node, struct device_node *node,
int index) int slot)
{ {
struct mtk_pcie_port *port; struct mtk_pcie_port *port;
struct resource *regs; struct resource *regs;
...@@ -288,34 +837,87 @@ static int mtk_pcie_parse_ports(struct mtk_pcie *pcie, ...@@ -288,34 +837,87 @@ static int mtk_pcie_parse_ports(struct mtk_pcie *pcie,
return err; return err;
} }
regs = platform_get_resource(pdev, IORESOURCE_MEM, index + 1); snprintf(name, sizeof(name), "port%d", slot);
regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
port->base = devm_ioremap_resource(dev, regs); port->base = devm_ioremap_resource(dev, regs);
if (IS_ERR(port->base)) { if (IS_ERR(port->base)) {
dev_err(dev, "failed to map port%d base\n", index); dev_err(dev, "failed to map port%d base\n", slot);
return PTR_ERR(port->base); return PTR_ERR(port->base);
} }
snprintf(name, sizeof(name), "sys_ck%d", index); snprintf(name, sizeof(name), "sys_ck%d", slot);
port->sys_ck = devm_clk_get(dev, name); port->sys_ck = devm_clk_get(dev, name);
if (IS_ERR(port->sys_ck)) { if (IS_ERR(port->sys_ck)) {
dev_err(dev, "failed to get port%d clock\n", index); dev_err(dev, "failed to get sys_ck%d clock\n", slot);
return PTR_ERR(port->sys_ck); return PTR_ERR(port->sys_ck);
} }
snprintf(name, sizeof(name), "pcie-rst%d", index); /* sys_ck might be divided into the following parts in some chips */
port->reset = devm_reset_control_get_optional(dev, name); snprintf(name, sizeof(name), "ahb_ck%d", slot);
port->ahb_ck = devm_clk_get(dev, name);
if (IS_ERR(port->ahb_ck)) {
if (PTR_ERR(port->ahb_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
port->ahb_ck = NULL;
}
snprintf(name, sizeof(name), "axi_ck%d", slot);
port->axi_ck = devm_clk_get(dev, name);
if (IS_ERR(port->axi_ck)) {
if (PTR_ERR(port->axi_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
port->axi_ck = NULL;
}
snprintf(name, sizeof(name), "aux_ck%d", slot);
port->aux_ck = devm_clk_get(dev, name);
if (IS_ERR(port->aux_ck)) {
if (PTR_ERR(port->aux_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
port->aux_ck = NULL;
}
snprintf(name, sizeof(name), "obff_ck%d", slot);
port->obff_ck = devm_clk_get(dev, name);
if (IS_ERR(port->obff_ck)) {
if (PTR_ERR(port->obff_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
port->obff_ck = NULL;
}
snprintf(name, sizeof(name), "pipe_ck%d", slot);
port->pipe_ck = devm_clk_get(dev, name);
if (IS_ERR(port->pipe_ck)) {
if (PTR_ERR(port->pipe_ck) == -EPROBE_DEFER)
return -EPROBE_DEFER;
port->pipe_ck = NULL;
}
snprintf(name, sizeof(name), "pcie-rst%d", slot);
port->reset = devm_reset_control_get_optional_exclusive(dev, name);
if (PTR_ERR(port->reset) == -EPROBE_DEFER) if (PTR_ERR(port->reset) == -EPROBE_DEFER)
return PTR_ERR(port->reset); return PTR_ERR(port->reset);
/* some platforms may use default PHY setting */ /* some platforms may use default PHY setting */
snprintf(name, sizeof(name), "pcie-phy%d", index); snprintf(name, sizeof(name), "pcie-phy%d", slot);
port->phy = devm_phy_optional_get(dev, name); port->phy = devm_phy_optional_get(dev, name);
if (IS_ERR(port->phy)) if (IS_ERR(port->phy))
return PTR_ERR(port->phy); return PTR_ERR(port->phy);
port->index = index; port->slot = slot;
port->pcie = pcie; port->pcie = pcie;
if (pcie->soc->setup_irq) {
err = pcie->soc->setup_irq(port, node);
if (err)
return err;
}
INIT_LIST_HEAD(&port->list); INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports); list_add_tail(&port->list, &pcie->ports);
...@@ -329,12 +931,14 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie) ...@@ -329,12 +931,14 @@ static int mtk_pcie_subsys_powerup(struct mtk_pcie *pcie)
struct resource *regs; struct resource *regs;
int err; int err;
/* get shared registers */ /* get shared registers, which are optional */
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); regs = platform_get_resource_byname(pdev, IORESOURCE_MEM, "subsys");
pcie->base = devm_ioremap_resource(dev, regs); if (regs) {
if (IS_ERR(pcie->base)) { pcie->base = devm_ioremap_resource(dev, regs);
dev_err(dev, "failed to map shared register\n"); if (IS_ERR(pcie->base)) {
return PTR_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"); pcie->free_ck = devm_clk_get(dev, "free_ck");
...@@ -422,7 +1026,7 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) ...@@ -422,7 +1026,7 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
} }
for_each_available_child_of_node(node, child) { for_each_available_child_of_node(node, child) {
int index; int slot;
err = of_pci_get_devfn(child); err = of_pci_get_devfn(child);
if (err < 0) { if (err < 0) {
...@@ -430,9 +1034,9 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) ...@@ -430,9 +1034,9 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
return err; return err;
} }
index = PCI_SLOT(err); slot = PCI_SLOT(err);
err = mtk_pcie_parse_ports(pcie, child, index); err = mtk_pcie_parse_port(pcie, child, slot);
if (err) if (err)
return err; return err;
} }
...@@ -443,7 +1047,7 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie) ...@@ -443,7 +1047,7 @@ static int mtk_pcie_setup(struct mtk_pcie *pcie)
/* enable each port, and then check link status */ /* enable each port, and then check link status */
list_for_each_entry_safe(port, tmp, &pcie->ports, list) list_for_each_entry_safe(port, tmp, &pcie->ports, list)
mtk_pcie_enable_ports(port); mtk_pcie_enable_port(port);
/* power down PCIe subsys if slots are all empty (link down) */ /* power down PCIe subsys if slots are all empty (link down) */
if (list_empty(&pcie->ports)) if (list_empty(&pcie->ports))
...@@ -480,9 +1084,12 @@ static int mtk_pcie_register_host(struct pci_host_bridge *host) ...@@ -480,9 +1084,12 @@ static int mtk_pcie_register_host(struct pci_host_bridge *host)
host->busnr = pcie->busn.start; host->busnr = pcie->busn.start;
host->dev.parent = pcie->dev; host->dev.parent = pcie->dev;
host->ops = &mtk_pcie_ops; host->ops = pcie->soc->ops;
host->map_irq = of_irq_parse_and_map_pci; host->map_irq = of_irq_parse_and_map_pci;
host->swizzle_irq = pci_common_swizzle; host->swizzle_irq = pci_common_swizzle;
host->sysdata = pcie;
if (IS_ENABLED(CONFIG_PCI_MSI) && pcie->soc->has_msi)
host->msi = &mtk_pcie_msi_chip;
err = pci_scan_root_bus_bridge(host); err = pci_scan_root_bus_bridge(host);
if (err < 0) if (err < 0)
...@@ -513,6 +1120,7 @@ static int mtk_pcie_probe(struct platform_device *pdev) ...@@ -513,6 +1120,7 @@ static int mtk_pcie_probe(struct platform_device *pdev)
pcie = pci_host_bridge_priv(host); pcie = pci_host_bridge_priv(host);
pcie->dev = dev; pcie->dev = dev;
pcie->soc = of_device_get_match_data(dev);
platform_set_drvdata(pdev, pcie); platform_set_drvdata(pdev, pcie);
INIT_LIST_HEAD(&pcie->ports); INIT_LIST_HEAD(&pcie->ports);
...@@ -537,9 +1145,23 @@ static int mtk_pcie_probe(struct platform_device *pdev) ...@@ -537,9 +1145,23 @@ static int mtk_pcie_probe(struct platform_device *pdev)
return err; return err;
} }
static const struct mtk_pcie_soc mtk_pcie_soc_v1 = {
.ops = &mtk_pcie_ops,
.startup = mtk_pcie_startup_port,
};
static const struct mtk_pcie_soc mtk_pcie_soc_v2 = {
.has_msi = true,
.ops = &mtk_pcie_ops_v2,
.startup = mtk_pcie_startup_port_v2,
.setup_irq = mtk_pcie_setup_irq,
};
static const struct of_device_id mtk_pcie_ids[] = { static const struct of_device_id mtk_pcie_ids[] = {
{ .compatible = "mediatek,mt7623-pcie"}, { .compatible = "mediatek,mt2701-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2701-pcie"}, { .compatible = "mediatek,mt7623-pcie", .data = &mtk_pcie_soc_v1 },
{ .compatible = "mediatek,mt2712-pcie", .data = &mtk_pcie_soc_v2 },
{ .compatible = "mediatek,mt7622-pcie", .data = &mtk_pcie_soc_v2 },
{}, {},
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment