From 96d239c460ed2b7f7c359b30449b5d9547a4fd03 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 15 Feb 2023 07:21:47 +0300 Subject: [PATCH 01/16] net: phy: motorcomm: uninitialized variables in yt8531_link_change_notify() These booleans are never set to false, but are just used without being initialized. Fixes: 4ac94f728a58 ("net: phy: Add driver for Motorcomm yt8531 gigabit ethernet phy") Signed-off-by: Dan Carpenter Reviewed-by: Frank Sae Link: https://lore.kernel.org/r/Y+xd2yJet2ImHLoQ@kili Signed-off-by: Jakub Kicinski Signed-off-by: Jaehoon Chung Change-Id: I1b34e53290b0e99aa7d2e5c7269625cdc6a779cb --- drivers/net/phy/motorcomm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c index ee7c37d..2fa5a90 100644 --- a/drivers/net/phy/motorcomm.c +++ b/drivers/net/phy/motorcomm.c @@ -1533,10 +1533,10 @@ static int yt8531_config_init(struct phy_device *phydev) static void yt8531_link_change_notify(struct phy_device *phydev) { struct device_node *node = phydev->mdio.dev.of_node; + bool tx_clk_1000_inverted = false; + bool tx_clk_100_inverted = false; + bool tx_clk_10_inverted = false; bool tx_clk_adj_enabled = false; - bool tx_clk_1000_inverted; - bool tx_clk_100_inverted; - bool tx_clk_10_inverted; u16 val = 0; int ret; -- 2.7.4 From 375c3483898ea1f2cfbec0eb07c8bab4f17a9778 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Mon, 10 Apr 2023 11:14:16 +0900 Subject: [PATCH 02/16] RISCV: configs: enable motorcomm phy driver Enable motrocomm phy driver to use ethernet. Change-Id: I71e988989fc9332b2d38339858fa49109daf6163 Signed-off-by: Jaehoon Chung --- arch/riscv/configs/tizen_vf2_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index 51e6e94..c0dc85b 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -134,6 +134,7 @@ CONFIG_R8169=y CONFIG_STMMAC_ETH=y CONFIG_DWMAC_STARFIVE=y CONFIG_MICROSEMI_PHY=y +CONFIG_MOTORCOMM_PHY=y CONFIG_INPUT_MOUSEDEV=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y -- 2.7.4 From 5c11e5a3bf84e547b55ced394519528da0cfe1d4 Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Fri, 7 Apr 2023 17:46:01 +0900 Subject: [PATCH 03/16] RISCV: configs: Fix to build-in clk_starfive When the clk operates as a module, defered occurs in ethernet, causing a problem in which eth0 and eth1 are switched. Modify the corresponding clk to built-in. Change-Id: I734b470294538a080aa02a01f0585c52a3d29acf Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index c0dc85b..02e7378 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -187,6 +187,10 @@ CONFIG_VIRTIO_PCI=y CONFIG_VIRTIO_BALLOON=y CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_MMIO=y +CONFIG_CLK_STARFIVE_JH7110_AON=y +CONFIG_CLK_STARFIVE_JH7110_STG=y +CONFIG_CLK_STARFIVE_JH7110_ISP=y +CONFIG_CLK_STARFIVE_JH7110_VOUT=y CONFIG_RPMSG_CHAR=y CONFIG_RPMSG_CTRL=y CONFIG_RPMSG_VIRTIO=y -- 2.7.4 From b31cbbfa7c21b27610b850734b419469494948b7 Mon Sep 17 00:00:00 2001 From: "ke.zhu" Date: Fri, 14 Apr 2023 13:22:11 +0900 Subject: [PATCH 04/16] PCI: Add support plda pcie host controller driver Add support plda pcie host controller driver and device tree of starfive JH7110. This driver is from [1], and the imported patch list [2]. We modified pinctrl and clock in device-tree to fit kernel v6.1. [1] https://github.com/starfive-tech/linux [2] patch list from v5.15.y 9edf6749fde8 drivers: pci: Fix crash in rt-linux because of an uninitialized lock. 2757ebeaf399 drivers: pci: Add PHY settings in pcie host driver. d3b5483a84d4 drivers: pci: Set PCIe PCI standard configuration ID. 758a22e27bc6 dts: starfive: Modified PCIe pin setting for bring up PCIe USB hub. 1c1671ff173d drivers: pci: Support system pm no irq ops. d38610f5403c drivers: pci: Support runtime pm & release when found empty slot in probe. fecebab6581f drivers: pci: Support 64bit prefetchable MMIO range. 39cbd7746b45 driver: pci: Add extended reset time for better compatibility b1163a7a7de2 driver: pci: Fix kernel stuck caused by ASPM LTR 036944785f52 PCIe: plda: Add support for evb 38e23080fe16 PCI: plda: Add port1 support 157cf2806f0a PCI: plda: Fix kernel compile warnings 64e6d9f6f659 PCI: plda: Add syscon register config 48ed9fb901ff PCI: plda: Add pcie clk & rst 08c3249e8021 PCI: plda: Optimize plda pcie host driver 4ac91988fa82 1.Add plda pcie host controller driver. 2.Add PCIe host controller DT bingdings of starfive JH7110. Signed-off-by: ke.zhu Signed-off-by: mason.huo Signed-off-by: Kevin.xie Change-Id: I7e58f62026e69318c9305e5fea4d0a3d9e40e7e3 Signed-off-by: Hoegeun Kwon --- .../dts/starfive/jh7110-starfive-visionfive-2.dtsi | 91 ++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 90 ++ drivers/pci/controller/Kconfig | 8 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pcie-plda.c | 1061 ++++++++++++++++++++ 5 files changed, 1251 insertions(+) mode change 100644 => 100755 drivers/pci/controller/Kconfig mode change 100644 => 100755 drivers/pci/controller/Makefile create mode 100644 drivers/pci/controller/pcie-plda.c diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 752bb0b..3fbcdaa 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -319,6 +319,78 @@ slew-rate = <0>; }; }; + + pcie0_perst_default: pcie0-perst-default { + perst-pins { + pinmux = ; + }; + }; + + pcie0_perst_active: pcie0-perst-active { + perst-pins { + pinmux = ; + }; + }; + + pcie0_wake_default: pcie0-wake-default { + wake-pins { + pinmux = ; + }; + }; + + pcie0_clkreq_default: pcie0-clkreq-default { + clkreq-pins { + pinmux = ; + }; + }; + + pcie0_vbus_default: pcie0-vbus-default { + drive-vbus-pin { + pinmux = ; + }; + }; + + pcie1_perst_default: pcie1-perst-default { + perst-pins { + pinmux = ; + }; + }; + + pcie1_perst_active: pcie1-perst-active { + perst-pins { + pinmux = ; + }; + }; + + pcie1_wake_default: pcie1-wake-default { + wake-pins { + pinmux = ; + }; + }; + + pcie1_clkreq_default: pcie1-clkreq-default { + clkreq-pins { + pinmux = ; + }; + }; }; &uart0 { @@ -333,3 +405,22 @@ dr_mode = "peripheral"; }; }; + +&pcie0 { + pinctrl-names = "default", "perst-default", "perst-active"; + pinctrl-0 = <&pcie0_wake_default>, + <&pcie0_clkreq_default>, + <&pcie0_vbus_default>; + pinctrl-1 = <&pcie0_perst_default>; + pinctrl-2 = <&pcie0_perst_active>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default", "perst-default", "perst-active"; + pinctrl-0 = <&pcie1_wake_default>, + <&pcie1_clkreq_default>; + pinctrl-1 = <&pcie1_perst_default>; + pinctrl-2 = <&pcie1_perst_active>; + status = "okay"; +}; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index 95abd5f..da70de9 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -293,6 +293,16 @@ cache-unified; }; + phyctrl0: multi-phyctrl@10210000 { + compatible = "starfive,phyctrl"; + reg = <0x0 0x10210000 0x0 0x10000>; + }; + + phyctrl1: pcie1-phyctrl@10220000 { + compatible = "starfive,phyctrl"; + reg = <0x0 0x10220000 0x0 0x10000>; + }; + plic: interrupt-controller@c000000 { compatible = "starfive,jh7110-plic", "sifive,plic-1.0.0"; reg = <0x0 0xc000000 0x0 0x4000000>; @@ -922,5 +932,85 @@ #reset-cells = <1>; power-domains = <&pwrc JH7110_PD_VOUT>; }; + + pcie0: pcie@2B000000 { + compatible = "plda,pci-xpressrich3-axi"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x2B000000 0x0 0x1000000 + 0x9 0x40000000 0x0 0x10000000>; + reg-names = "reg", "config"; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130 0x1b8>; + starfive,phyctrl = <&phyctrl0 0x28 0x80>; + bus-range = <0x0 0xff>; + ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, + <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; + msi-parent = <&plic>; + interrupts = <56>; + interrupt-controller; + interrupt-names = "msi"; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &plic 0x1>, + <0x0 0x0 0x0 0x2 &plic 0x2>, + <0x0 0x0 0x0 0x3 &plic 0x3>, + <0x0 0x0 0x0 0x4 &plic 0x4>; + resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE0_BRG>, + <&stgcrg JH7110_STGRST_PCIE0_CORE>, + <&stgcrg JH7110_STGRST_PCIE0_APB>; + reset-names = "rst_mst0", "rst_slv0", "rst_slv", + "rst_brg", "rst_core", "rst_apb"; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE0_TL>, + <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE0_APB>; + clock-names = "noc_bus_stg_axi", "tl", "axi_mst0", "apb"; + status = "disabled"; + }; + + pcie1:pcie@2C000000 { + compatible = "plda,pci-xpressrich3-axi"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x2C000000 0x0 0x1000000 + 0x9 0xc0000000 0x0 0x10000000>; + reg-names = "reg", "config"; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon 0x270 0x274 0x2e0 0x368>; + starfive,phyctrl = <&phyctrl1 0x28 0x80>; + bus-range = <0x0 0xff>; + ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, + <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; + msi-parent = <&plic>; + interrupts = <57>; + interrupt-controller; + interrupt-names = "msi"; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &plic 0x1>, + <0x0 0x0 0x0 0x2 &plic 0x2>, + <0x0 0x0 0x0 0x3 &plic 0x3>, + <0x0 0x0 0x0 0x4 &plic 0x4>; + resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE1_BRG>, + <&stgcrg JH7110_STGRST_PCIE1_CORE>, + <&stgcrg JH7110_STGRST_PCIE1_APB>; + reset-names = "rst_mst0", "rst_slv0", "rst_slv", + "rst_brg", "rst_core", "rst_apb"; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE1_TL>, + <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE1_APB>; + clock-names = "noc_bus_stg_axi", "tl", "axi_mst0", "apb"; + status = "disabled"; + }; }; }; diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig old mode 100644 new mode 100755 index bfd9bac..be1fcb8 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -343,6 +343,14 @@ config PCIE_MT7621 help This selects a driver for the MediaTek MT7621 PCIe Controller. +config PCIE_PLDA + tristate "PLDA XpressRICH3-AXI PCIe controller" + depends on OF + select PCI_MSI_IRQ_DOMAIN + help + Say 'Y' here if you want kernel to support the PLDA XpressRICH3-AXI + (with ECAM disabled) PCIe Host driver. + source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" source "drivers/pci/controller/cadence/Kconfig" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile old mode 100644 new mode 100755 index 37c8663..edcc6d4 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCIE_PLDA) += pcie-plda.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-plda.c b/drivers/pci/controller/pcie-plda.c new file mode 100644 index 0000000..3eb0487 --- /dev/null +++ b/drivers/pci/controller/pcie-plda.c @@ -0,0 +1,1061 @@ +/* + * PCIe host controller driver for Starfive JH7110 Soc. + * + * Based on pcie-altera.c, pcie-altera-msi.c. + * + * Copyright (C) Shanghai StarFive Technology Co., Ltd. + * + * Author: ke.zhu@starfivetech.com + * + * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS + * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE + * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY + * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING + * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE + * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +#define PCIE_BASIC_STATUS 0x018 +#define PCIE_CFGNUM 0x140 +#define IMASK_LOCAL 0x180 +#define ISTATUS_LOCAL 0x184 +#define IMSI_ADDR 0x190 +#define ISTATUS_MSI 0x194 +#define CFG_SPACE 0x1000 +#define GEN_SETTINGS 0x80 +#define PCIE_PCI_IDS 0x9C +#define PCIE_WINROM 0xFC +#define PMSG_SUPPORT_RX 0x3F0 + +#define PCI_MISC 0xB4 + +#define PLDA_EP_ENABLE 0 +#define PLDA_RP_ENABLE 1 + +#define PLDA_LINK_UP 1 +#define PLDA_LINK_DOWN 0 + +#define IDS_REVISION_ID 0x02 +#define IDS_PCI_TO_PCI_BRIDGE 0x060400 +#define IDS_CLASS_CODE_SHIFT 8 + +#define PLDA_DATA_LINK_ACTIVE BIT(5) +#define PREF_MEM_WIN_64_SUPPORT BIT(3) +#define PMSG_LTR_SUPPORT BIT(2) +#define PDLA_LINK_SPEED_GEN2 BIT(12) +#define PLDA_FUNCTION_DIS BIT(15) +#define PLDA_FUNC_NUM 4 +#define PLDA_PHY_FUNC_SHIFT 9 +#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 +#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc + +#define XR3PCI_ATR_AXI4_SLV0 0x800 +#define XR3PCI_ATR_SRC_ADDR_LOW 0x0 +#define XR3PCI_ATR_SRC_ADDR_HIGH 0x4 +#define XR3PCI_ATR_TRSL_ADDR_LOW 0x8 +#define XR3PCI_ATR_TRSL_ADDR_HIGH 0xc +#define XR3PCI_ATR_TRSL_PARAM 0x10 +#define XR3PCI_ATR_TABLE_OFFSET 0x20 +#define XR3PCI_ATR_MAX_TABLE_NUM 8 + +#define XR3PCI_ATR_SRC_WIN_SIZE_SHIFT 1 +#define XR3PCI_ATR_SRC_ADDR_MASK 0xfffff000 +#define XR3PCI_ATR_TRSL_ADDR_MASK 0xfffff000 +#define XR3_PCI_ECAM_SIZE 28 +#define XR3PCI_ATR_TRSL_DIR BIT(22) +/* IDs used in the XR3PCI_ATR_TRSL_PARAM */ +#define XR3PCI_ATR_TRSLID_PCIE_MEMORY 0x0 +#define XR3PCI_ATR_TRSLID_PCIE_CONFIG 0x1 + +#define CFGNUM_DEVFN_SHIFT 0 +#define CFGNUM_BUS_SHIFT 8 +#define CFGNUM_BE_SHIFT 16 +#define CFGNUM_FBE_SHIFT 20 + +#define ECAM_BUS_SHIFT 20 +#define ECAM_DEV_SHIFT 15 +#define ECAM_FUNC_SHIFT 12 + +#define INT_AXI_POST_ERROR BIT(16) +#define INT_AXI_FETCH_ERROR BIT(17) +#define INT_AXI_DISCARD_ERROR BIT(18) +#define INT_PCIE_POST_ERROR BIT(20) +#define INT_PCIE_FETCH_ERROR BIT(21) +#define INT_PCIE_DISCARD_ERROR BIT(22) +#define INT_ERRORS (INT_AXI_POST_ERROR | INT_AXI_FETCH_ERROR | \ + INT_AXI_DISCARD_ERROR | INT_PCIE_POST_ERROR | \ + INT_PCIE_FETCH_ERROR | INT_PCIE_DISCARD_ERROR) + +#define INTA_OFFSET 24 +#define INTA BIT(24) +#define INTB BIT(25) +#define INTC BIT(26) +#define INTD BIT(27) +#define INT_MSI BIT(28) +#define INT_INTX_MASK (INTA | INTB | INTC | INTD) +#define INT_MASK (INT_INTX_MASK | INT_MSI | INT_ERRORS) + +#define INT_PCI_MSI_NR 32 +#define LINK_UP_MASK 0xff + +#define PERST_DELAY_US 1000 + +/* system control */ +#define STG_SYSCON_K_RP_NEP_SHIFT 0x8 +#define STG_SYSCON_K_RP_NEP_MASK 0x100 +#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK 0x7FFF00 +#define STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT 0x8 +#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK 0x7FFF +#define STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT 0x0 +#define STG_SYSCON_CLKREQ_SHIFT 0x16 +#define STG_SYSCON_CLKREQ_MASK 0x400000 +#define STG_SYSCON_CKREF_SRC_SHIFT 0x12 +#define STG_SYSCON_CKREF_SRC_MASK 0xC0000 + +#define PCI_DEV(d) (((d) >> 3) & 0x1f) + +/* MSI information */ +struct plda_msi { + DECLARE_BITMAP(used, INT_PCI_MSI_NR); + struct irq_domain *msi_domain; + struct irq_domain *inner_domain; + /* Protect bitmap variable */ + struct mutex lock; +}; + +struct plda_pcie { + struct platform_device *pdev; + void __iomem *reg_base; + void __iomem *config_base; + struct resource *cfg_res; + struct regmap *reg_syscon; + struct regmap *reg_phyctrl; + u32 stg_arfun; + u32 stg_awfun; + u32 stg_rp_nep; + u32 stg_lnksta; + u32 phy_kvco_level; + u32 phy_kvco_tune_signals; + int irq; + struct irq_domain *legacy_irq_domain; + struct pci_host_bridge *bridge; + struct plda_msi msi; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + int atr_table_num; + struct pinctrl *pinctrl; + struct pinctrl_state *perst_state_def; + struct pinctrl_state *perst_state_active; +}; + +static inline void plda_writel(struct plda_pcie *pcie, const u32 value, + const u32 reg) +{ + writel_relaxed(value, pcie->reg_base + reg); +} + +static inline u32 plda_readl(struct plda_pcie *pcie, const u32 reg) +{ + return readl_relaxed(pcie->reg_base + reg); +} + +static bool plda_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, + int offset) +{ + if (pci_is_root_bus(bus) && (devfn == 0) && + (offset == PCI_BASE_ADDRESS_0)) + return true; + + return false; +} + +static int _plda_pcie_config_read(struct plda_pcie *pcie, unsigned char busno, + unsigned int devfn, int where, int size, + u32 *value) +{ + void __iomem *addr; + + addr = pcie->config_base; + addr += (busno << ECAM_BUS_SHIFT); + addr += (PCI_DEV(devfn) << ECAM_DEV_SHIFT); + addr += (PCI_FUNC(devfn) << ECAM_FUNC_SHIFT); + addr += where; + + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + *(unsigned char *)value = readb(addr); + break; + case 2: + *(unsigned short *)value = readw(addr); + break; + case 4: + *(unsigned int *)value = readl(addr); + break; + default: + return PCIBIOS_SET_FAILED; + } + + return PCIBIOS_SUCCESSFUL; +} + +int _plda_pcie_config_write(struct plda_pcie *pcie, unsigned char busno, + unsigned int devfn, int where, int size, u32 value) +{ + void __iomem *addr; + + addr = pcie->config_base; + addr += (busno << ECAM_BUS_SHIFT); + addr += (PCI_DEV(devfn) << ECAM_DEV_SHIFT); + addr += (PCI_FUNC(devfn) << ECAM_FUNC_SHIFT); + addr += where; + + if (!addr) + return PCIBIOS_DEVICE_NOT_FOUND; + + switch (size) { + case 1: + writeb(value, addr); + break; + case 2: + writew(value, addr); + break; + case 4: + writel(value, addr); + break; + default: + return PCIBIOS_SET_FAILED; + } + + return PCIBIOS_SUCCESSFUL; +} + +static int plda_pcie_config_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 *value) +{ + struct plda_pcie *pcie = bus->sysdata; + + return _plda_pcie_config_read(pcie, bus->number, devfn, where, size, + value); +} + +int plda_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + struct plda_pcie *pcie = bus->sysdata; + + if (plda_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return _plda_pcie_config_write(pcie, bus->number, devfn, where, size, + value); +} + +static void plda_pcie_handle_msi_irq(struct plda_pcie *pcie) +{ + struct plda_msi *msi = &pcie->msi; + u32 bit; + u32 virq; + unsigned long status = plda_readl(pcie, ISTATUS_MSI); + + for_each_set_bit(bit, &status, INT_PCI_MSI_NR) { + /* Clear interrupts */ + plda_writel(pcie, 1 << bit, ISTATUS_MSI); + + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) { + if (test_bit(bit, msi->used)) + generic_handle_irq(virq); + else + dev_err(&pcie->pdev->dev, + "Unhandled MSI, MSI%d virq %d\n", bit, + virq); + } else + dev_err(&pcie->pdev->dev, "Unexpected MSI, MSI%d\n", + bit); + + } + plda_writel(pcie, INT_MSI, ISTATUS_LOCAL); +} + +static void plda_pcie_handle_intx_irq(struct plda_pcie *pcie, + unsigned long status) +{ + u32 bit; + u32 virq; + + status >>= INTA_OFFSET; + + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + /* Clear interrupts */ + plda_writel(pcie, 1 << (bit + INTA_OFFSET), ISTATUS_LOCAL); + + virq = irq_find_mapping(pcie->legacy_irq_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_err(&pcie->pdev->dev, + "plda_pcie_handle_intx_irq unexpected IRQ, INT%d\n", bit); + } +} + +static void plda_pcie_handle_errors_irq(struct plda_pcie *pcie, u32 status) +{ + if (status & INT_AXI_POST_ERROR) + dev_err(&pcie->pdev->dev, "AXI post error\n"); + if (status & INT_AXI_FETCH_ERROR) + dev_err(&pcie->pdev->dev, "AXI fetch error\n"); + if (status & INT_AXI_DISCARD_ERROR) + dev_err(&pcie->pdev->dev, "AXI discard error\n"); + if (status & INT_PCIE_POST_ERROR) + dev_err(&pcie->pdev->dev, "PCIe post error\n"); + if (status & INT_PCIE_FETCH_ERROR) + dev_err(&pcie->pdev->dev, "PCIe fetch error\n"); + if (status & INT_PCIE_DISCARD_ERROR) + dev_err(&pcie->pdev->dev, "PCIe discard error\n"); + + plda_writel(pcie, INT_ERRORS, ISTATUS_LOCAL); +} + +static void plda_pcie_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct plda_pcie *pcie; + u32 status; + + chained_irq_enter(chip, desc); + pcie = irq_desc_get_handler_data(desc); + + status = plda_readl(pcie, ISTATUS_LOCAL); + while ((status = (plda_readl(pcie, ISTATUS_LOCAL) & INT_MASK))) { + if (status & INT_INTX_MASK) + plda_pcie_handle_intx_irq(pcie, status); + + if (status & INT_MSI) + plda_pcie_handle_msi_irq(pcie); + + if (status & INT_ERRORS) + plda_pcie_handle_errors_irq(pcie, status); + } + + chained_irq_exit(chip, desc); +} + +#ifdef CONFIG_PCI_MSI +static struct irq_chip plda_msi_irq_chip = { + .name = "PLDA PCIe MSI", + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info plda_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &plda_msi_irq_chip, +}; +#endif + +static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct plda_pcie *pcie = irq_data_get_irq_chip_data(data); + phys_addr_t msi_addr = plda_readl(pcie, IMSI_ADDR); + + msg->address_lo = lower_32_bits(msi_addr); + msg->address_hi = upper_32_bits(msi_addr); + msg->data = data->hwirq; + + dev_info(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int plda_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip plda_irq_chip = { + .name = "PLDA MSI", + .irq_compose_msi_msg = plda_compose_msi_msg, + .irq_set_affinity = plda_msi_set_affinity, +}; + +static int plda_msi_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct plda_pcie *pcie = domain->host_data; + struct plda_msi *msi = &pcie->msi; + int bit; + + WARN_ON(nr_irqs != 1); + mutex_lock(&msi->lock); + + bit = find_first_zero_bit(msi->used, INT_PCI_MSI_NR); + if (bit >= INT_PCI_MSI_NR) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + set_bit(bit, msi->used); + + irq_domain_set_info(domain, virq, bit, &plda_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + mutex_unlock(&msi->lock); + + return 0; +} + +static void plda_msi_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct plda_pcie *pcie = irq_data_get_irq_chip_data(data); + struct plda_msi *msi = &pcie->msi; + + mutex_lock(&msi->lock); + + if (!test_bit(data->hwirq, msi->used)) + dev_err(&pcie->pdev->dev, "Trying to free unused MSI#%lu\n", + data->hwirq); + else + __clear_bit(data->hwirq, msi->used); + + mutex_unlock(&msi->lock); +} + +static const struct irq_domain_ops dev_msi_domain_ops = { + .alloc = plda_msi_alloc, + .free = plda_msi_free, +}; + +static void plda_msi_free_irq_domain(struct plda_pcie *pcie) +{ +#ifdef CONFIG_PCI_MSI + struct plda_msi *msi = &pcie->msi; + u32 irq; + int i; + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->inner_domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + if (msi->msi_domain) + irq_domain_remove(msi->msi_domain); + + if (msi->inner_domain) + irq_domain_remove(msi->inner_domain); +#endif +} + +static void plda_pcie_free_irq_domain(struct plda_pcie *pcie) +{ + int i; + u32 irq; + + /* Disable all interrupts */ + plda_writel(pcie, 0, IMASK_LOCAL); + + if (pcie->legacy_irq_domain) { + for (i = 0; i < PCI_NUM_INTX; i++) { + irq = irq_find_mapping(pcie->legacy_irq_domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + irq_domain_remove(pcie->legacy_irq_domain); + } + + if (pci_msi_enabled()) + plda_msi_free_irq_domain(pcie); + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); +} + +static int plda_pcie_init_msi_irq_domain(struct plda_pcie *pcie) +{ +#ifdef CONFIG_PCI_MSI + struct fwnode_handle *fwn = of_node_to_fwnode(pcie->pdev->dev.of_node); + struct plda_msi *msi = &pcie->msi; + + msi->inner_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, + &dev_msi_domain_ops, pcie); + if (!msi->inner_domain) { + dev_err(&pcie->pdev->dev, "Failed to create dev IRQ domain\n"); + return -ENOMEM; + } + msi->msi_domain = pci_msi_create_irq_domain(fwn, &plda_msi_domain_info, + msi->inner_domain); + if (!msi->msi_domain) { + dev_err(&pcie->pdev->dev, "Failed to create msi IRQ domain\n"); + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } +#endif + return 0; +} + +static int plda_pcie_enable_msi(struct plda_pcie *pcie, struct pci_bus *bus) +{ + struct plda_msi *msi = &pcie->msi; + u32 reg; + + mutex_init(&msi->lock); + + /* Enable MSI */ + reg = plda_readl(pcie, IMASK_LOCAL); + reg |= INT_MSI; + plda_writel(pcie, reg, IMASK_LOCAL); + return 0; +} + +static int plda_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 = plda_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, +}; + +static int plda_pcie_init_irq_domain(struct plda_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + int ret; + + if (pci_msi_enabled()) { + ret = plda_pcie_init_msi_irq_domain(pcie); + if (ret != 0) + return -ENOMEM; + } + + /* Setup INTx */ + pcie->legacy_irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, + &intx_domain_ops, pcie); + + if (!pcie->legacy_irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENOMEM; + } + + irq_set_chained_handler_and_data(pcie->irq, plda_pcie_isr, pcie); + return 0; +} + +static int plda_pcie_parse_dt(struct plda_pcie *pcie) +{ + struct resource *reg_res; + struct platform_device *pdev = pcie->pdev; + struct of_phandle_args syscon_args, phyctrl_args; + int ret; + + reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); + if (!reg_res) { + dev_err(&pdev->dev, "Missing required reg address range\n"); + return -ENODEV; + } + + pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg_res); + if (IS_ERR(pcie->reg_base)) { + dev_err(&pdev->dev, "Failed to map reg memory\n"); + return PTR_ERR(pcie->reg_base); + } + + pcie->cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (!pcie->cfg_res) { + dev_err(&pdev->dev, "Missing required config address range"); + return -ENODEV; + } + + pcie->config_base = devm_ioremap_resource(&pdev->dev, pcie->cfg_res); + if (IS_ERR(pcie->config_base)){ + dev_err(&pdev->dev, "Failed to map config memory\n"); + return PTR_ERR(pcie->config_base); + } + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "starfive,phyctrl", 2, 0, &phyctrl_args); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to parse starfive,phyctrl\n"); + return -EINVAL; + } + + if (!of_device_is_compatible(phyctrl_args.np, "starfive,phyctrl")) + return -EINVAL; + pcie->reg_phyctrl = device_node_to_regmap(phyctrl_args.np); + of_node_put(phyctrl_args.np); + if (IS_ERR(pcie->reg_phyctrl)) + return PTR_ERR(pcie->reg_phyctrl); + + pcie->phy_kvco_level = phyctrl_args.args[0]; + pcie->phy_kvco_tune_signals = phyctrl_args.args[1]; + + pcie->irq = platform_get_irq(pdev, 0); + if (pcie->irq <= 0) { + dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pcie->irq); + return -EINVAL; + } + + ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, + "starfive,stg-syscon", 4, 0, &syscon_args); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to parse starfive,stg-syscon\n"); + return -EINVAL; + } + + pcie->reg_syscon = syscon_node_to_regmap(syscon_args.np); + of_node_put(syscon_args.np); + if (IS_ERR(pcie->reg_syscon)) + return PTR_ERR(pcie->reg_syscon); + + pcie->stg_arfun = syscon_args.args[0]; + pcie->stg_awfun = syscon_args.args[1]; + pcie->stg_rp_nep = syscon_args.args[2]; + pcie->stg_lnksta = syscon_args.args[3]; + + /* Clear all interrupts */ + plda_writel(pcie, 0xffffffff, ISTATUS_LOCAL); + plda_writel(pcie, INT_INTX_MASK | INT_ERRORS, IMASK_LOCAL); + + return 0; +} + +static struct pci_ops plda_pcie_ops = { + .read = plda_pcie_config_read, + .write = plda_pcie_config_write, +}; + +void plda_set_atr_entry(struct plda_pcie *pcie, phys_addr_t src_addr, + phys_addr_t trsl_addr, size_t window_size, + int trsl_param) +{ + void __iomem *base = + pcie->reg_base + XR3PCI_ATR_AXI4_SLV0; + + /* Support AXI4 Slave 0 Address Translation Tables 0-7. */ + if (pcie->atr_table_num >= XR3PCI_ATR_MAX_TABLE_NUM) + pcie->atr_table_num = XR3PCI_ATR_MAX_TABLE_NUM - 1; + base += XR3PCI_ATR_TABLE_OFFSET * pcie->atr_table_num; + pcie->atr_table_num++; + + /* X3PCI_ATR_SRC_ADDR_LOW: + * - bit 0: enable entry, + * - bits 1-6: ATR window size: total size in bytes: 2^(ATR_WSIZE + 1) + * - bits 7-11: reserved + * - bits 12-31: start of source address + */ + writel((lower_32_bits(src_addr) & XR3PCI_ATR_SRC_ADDR_MASK) | + (fls(window_size) - 1) << XR3PCI_ATR_SRC_WIN_SIZE_SHIFT | 1, + base + XR3PCI_ATR_SRC_ADDR_LOW); + writel(upper_32_bits(src_addr), base + XR3PCI_ATR_SRC_ADDR_HIGH); + writel((lower_32_bits(trsl_addr) & XR3PCI_ATR_TRSL_ADDR_MASK), + base + XR3PCI_ATR_TRSL_ADDR_LOW); + writel(upper_32_bits(trsl_addr), base + XR3PCI_ATR_TRSL_ADDR_HIGH); + writel(trsl_param, base + XR3PCI_ATR_TRSL_PARAM); + + pr_info("ATR entry: 0x%010llx %s 0x%010llx [0x%010llx] (param: 0x%06x)\n", + src_addr, (trsl_param & XR3PCI_ATR_TRSL_DIR) ? "<-" : "->", + trsl_addr, (u64)window_size, trsl_param); +} + +static int plda_pcie_setup_windows(struct plda_pcie *pcie) +{ + struct pci_host_bridge *bridge = pcie->bridge; + struct resource_entry *entry; + u64 pci_addr; + + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + pci_addr = entry->res->start - entry->offset; + plda_set_atr_entry(pcie, + entry->res->start, pci_addr, + resource_size(entry->res), + XR3PCI_ATR_TRSLID_PCIE_MEMORY); + } + } + + return 0; +} + +static int plda_clk_rst_init(struct plda_pcie *pcie) +{ + int ret; + struct device *dev = &pcie->pdev->dev; + + pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); + if (pcie->num_clks < 0) { + dev_err(dev, "Failed to get pcie clocks\n"); + ret = -ENODEV; + goto exit; + } + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) { + dev_err(&pcie->pdev->dev, "Failed to enable clocks\n"); + goto exit; + } + + pcie->resets = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(pcie->resets)) { + ret = PTR_ERR(pcie->resets); + dev_err(dev, "Failed to get pcie resets"); + goto err_clk_init; + } + ret = reset_control_deassert(pcie->resets); + goto exit; + +err_clk_init: + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); +exit: + return ret; +} + +static void plda_clk_rst_deinit(struct plda_pcie *pcie) +{ + reset_control_assert(pcie->resets); + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); +} + +int plda_pinctrl_init(struct plda_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + + pcie->pinctrl = devm_pinctrl_get(dev); + if (IS_ERR_OR_NULL(pcie->pinctrl)) { + dev_err(dev, "Getting pinctrl handle failed\n"); + return -EINVAL; + } + + pcie->perst_state_def + = pinctrl_lookup_state(pcie->pinctrl, "perst-default"); + if (IS_ERR_OR_NULL(pcie->perst_state_def)) { + dev_err(dev, "Failed to get the perst-default pinctrl handle\n"); + return -EINVAL; + } + + pcie->perst_state_active + = pinctrl_lookup_state(pcie->pinctrl, "perst-active"); + if (IS_ERR_OR_NULL(pcie->perst_state_active)) { + dev_err(dev, "Failed to get the perst-active pinctrl handle\n"); + return -EINVAL; + } + + return 0; +} + +static void plda_pcie_hw_init(struct plda_pcie *pcie) +{ + unsigned int value; + int i, ret; + struct device *dev = &pcie->pdev->dev; + + if (pcie->perst_state_active) { + ret = pinctrl_select_state(pcie->pinctrl, pcie->perst_state_active); + if (ret) + dev_err(dev, "Cannot set reset pin to low\n"); + } + + /* Disable physical functions except #0 */ + for (i = 1; i < PLDA_FUNC_NUM; i++) { + regmap_update_bits(pcie->reg_syscon, + pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, + (i << PLDA_PHY_FUNC_SHIFT) << + STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT); + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, + (i << PLDA_PHY_FUNC_SHIFT) << + STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT); + + value = readl(pcie->reg_base + PCI_MISC); + value |= PLDA_FUNCTION_DIS; + writel(value, pcie->reg_base + PCI_MISC); + } + regmap_update_bits(pcie->reg_syscon, + pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, + 0 << STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT); + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, + 0 << STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT); + + /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ + regmap_write(pcie->reg_phyctrl, pcie->phy_kvco_level, + PHY_KVCO_FINE_TUNE_LEVEL); + regmap_write(pcie->reg_phyctrl, pcie->phy_kvco_tune_signals, + PHY_KVCO_FINE_TUNE_SIGNALS); + + /* Enable root port*/ + value = readl(pcie->reg_base + GEN_SETTINGS); + value |= PLDA_RP_ENABLE; + writel(value, pcie->reg_base + GEN_SETTINGS); + + /* PCIe PCI Standard Configuration Identification Settings. */ + value = (IDS_PCI_TO_PCI_BRIDGE << IDS_CLASS_CODE_SHIFT) | IDS_REVISION_ID; + writel(value, pcie->reg_base + PCIE_PCI_IDS); + + /* The LTR message forwarding of PCIe Message Reception was set by core + * as default, but the forward id & addr are also need to be reset. + * If we do not disable LTR message forwarding here, or set a legal + * forwarding address, the kernel will get stuck after this driver probe. + * To workaround, disable the LTR message forwarding support on + * PCIe Message Reception. + */ + value = readl(pcie->reg_base + PMSG_SUPPORT_RX); + value &= ~PMSG_LTR_SUPPORT; + writel(value, pcie->reg_base + PMSG_SUPPORT_RX); + + /* Prefetchable memory window 64-bit addressing support */ + value = readl(pcie->reg_base + PCIE_WINROM); + value |= PREF_MEM_WIN_64_SUPPORT; + writel(value, pcie->reg_base + PCIE_WINROM); + + /* As the two host bridges in JH7110 soc have the same default + * address translation table, this cause the second root port can't + * access it's host bridge config space correctly. + * To workaround, config the ATR of host bridge config space by SW. + */ + plda_set_atr_entry(pcie, + pcie->cfg_res->start, 0, + 1 << XR3_PCI_ECAM_SIZE, + XR3PCI_ATR_TRSLID_PCIE_CONFIG); + + plda_pcie_setup_windows(pcie); + + /* Ensure that PERST has been asserted for at least 100 ms */ + msleep(300); + if (pcie->perst_state_def) { + ret = pinctrl_select_state(pcie->pinctrl, pcie->perst_state_def); + if (ret) + dev_err(dev, "Cannot set reset pin to high\n"); + } +} + +static int plda_pcie_is_link_up(struct plda_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + int ret; + u32 stg_reg_val; + + /* 100ms timeout value should be enough for Gen1/2 training */ + ret = regmap_read_poll_timeout(pcie->reg_syscon, + pcie->stg_lnksta, + stg_reg_val, + stg_reg_val & PLDA_DATA_LINK_ACTIVE, + 10 * 1000, 100 * 1000); + + /* If the link is down (no device in slot), then exit. */ + if (ret == -ETIMEDOUT) { + dev_info(dev, "Port link down, exit.\n"); + return PLDA_LINK_DOWN; + } else if (ret == 0) { + dev_info(dev, "Port link up.\n"); + return PLDA_LINK_UP; + } + + dev_warn(dev, "Read stg_linksta failed.\n"); + return ret; +} + +static int plda_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct plda_pcie *pcie; + struct pci_bus *bus; + struct pci_host_bridge *bridge; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + pcie->atr_table_num = 0; + + ret = plda_pcie_parse_dt(pcie); + if (ret) { + dev_err(&pdev->dev, "Parsing DT failed\n"); + return ret; + } + + platform_set_drvdata(pdev, pcie); + + plda_pinctrl_init(pcie); + if (ret) { + dev_err(&pdev->dev, "Init pinctrl failed\n"); + return ret; + } + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_rp_nep, + STG_SYSCON_K_RP_NEP_MASK, + 1 << STG_SYSCON_K_RP_NEP_SHIFT); + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_CKREF_SRC_MASK, + 2 << STG_SYSCON_CKREF_SRC_SHIFT); + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_CLKREQ_MASK, + 1 << STG_SYSCON_CLKREQ_SHIFT); + + ret = plda_clk_rst_init(pcie); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to init pcie clk reset: %d\n", ret); + goto exit; + } + + ret = plda_pcie_init_irq_domain(pcie); + if (ret) { + dev_err(&pdev->dev, "Failed creating IRQ Domain\n"); + goto exit; + } + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) { + ret = -ENOMEM; + goto exit; + } + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + /* Set default bus ops */ + bridge->ops = &plda_pcie_ops; + bridge->sysdata = pcie; + pcie->bridge = bridge; + + plda_pcie_hw_init(pcie); + + if (plda_pcie_is_link_up(pcie) == PLDA_LINK_DOWN) + goto release; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = plda_pcie_enable_msi(pcie, bus); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to enable MSI support: %d\n", ret); + goto release; + } + } + + ret = pci_host_probe(bridge); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to pci host probe: %d\n", ret); + goto release; + } + +exit: + return ret; + +release: + plda_clk_rst_deinit(pcie); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + pci_free_host_bridge(pcie->bridge); + devm_kfree(&pdev->dev, pcie); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int plda_pcie_remove(struct platform_device *pdev) +{ + struct plda_pcie *pcie = platform_get_drvdata(pdev); + + plda_pcie_free_irq_domain(pcie); + plda_clk_rst_deinit(pcie); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int __maybe_unused plda_pcie_suspend_noirq(struct device *dev) +{ + struct plda_pcie *pcie = dev_get_drvdata(dev); + + if (!pcie) + return 0; + + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + + return 0; +} + +static int __maybe_unused plda_pcie_resume_noirq(struct device *dev) +{ + struct plda_pcie *pcie = dev_get_drvdata(dev); + int ret; + + if (!pcie) + return 0; + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) + dev_err(dev, "Failed to enable clocks\n"); + + return ret; +} + +static const struct dev_pm_ops plda_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(plda_pcie_suspend_noirq, + plda_pcie_resume_noirq) +}; +#endif + +static const struct of_device_id plda_pcie_of_match[] = { + { .compatible = "plda,pci-xpressrich3-axi"}, + { }, +}; +MODULE_DEVICE_TABLE(of, plda_pcie_of_match); + +static struct platform_driver plda_pcie_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = of_match_ptr(plda_pcie_of_match), +#ifdef CONFIG_PM_SLEEP + .pm = &plda_pcie_pm_ops, +#endif + }, + .probe = plda_pcie_probe, + .remove = plda_pcie_remove, +}; +module_platform_driver(plda_pcie_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); +MODULE_AUTHOR("ke.zhu "); +MODULE_AUTHOR("Mason Huo "); +MODULE_LICENSE("GPL v2"); -- 2.7.4 From c8cfb82195d6e397017aa28e3e347766402e43ac Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Fri, 14 Apr 2023 13:27:13 +0900 Subject: [PATCH 05/16] RISCV: configs: Enable pcie plda defconfig Enable CONFIG_PCIE_PLDA defconfig for plda pcie host controller driver. Change-Id: Ie09fdf5761d4b5a4a758b69c91f7f7100e820f0f Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index 02e7378..6a90238 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -100,6 +100,7 @@ CONFIG_PCI=y CONFIG_PCIEPORTBUS=y CONFIG_PCI_HOST_GENERIC=y CONFIG_PCIE_XILINX=y +CONFIG_PCIE_PLDA=y CONFIG_PCIE_FU740=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y -- 2.7.4 From 93d6e1e743901c1abde97f8de0aa60b4655bd0e1 Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Thu, 13 Apr 2023 13:33:52 +0900 Subject: [PATCH 06/16] RISCV: configs: Enable usb wireless defconfig Enable usb wireless defconfig and realtek module. Change-Id: Id186de5d13078a11b7142afceb881ade5da2683d Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index 6a90238..a59b4e9 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -94,6 +94,7 @@ CONFIG_CGROUP_NET_PRIO=y CONFIG_CFG80211=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_CFG80211_WEXT=y +CONFIG_MAC80211=y CONFIG_NET_9P=y CONFIG_NET_9P_VIRTIO=y CONFIG_PCI=y @@ -136,6 +137,10 @@ CONFIG_STMMAC_ETH=y CONFIG_DWMAC_STARFIVE=y CONFIG_MICROSEMI_PHY=y CONFIG_MOTORCOMM_PHY=y +CONFIG_USB_RTL8152=y +CONFIG_USB_LAN78XX=y +CONFIG_USB_USBNET=y +CONFIG_RTL8192CU=m CONFIG_INPUT_MOUSEDEV=y CONFIG_SERIAL_8250=y CONFIG_SERIAL_8250_CONSOLE=y -- 2.7.4 From 86a52f26d44ec8c20ec20621ed0b573f462a563a Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Thu, 13 Apr 2023 20:43:26 +0900 Subject: [PATCH 07/16] RISCV: configs: Enable usb BT defconfig Enable usb BT defconfig. Change-Id: Ia751e369e4a6aad13c730c7e1f5129f347877b6b Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index a59b4e9..a9694d2 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -87,14 +87,27 @@ CONFIG_IP6_NF_MANGLE=m CONFIG_BRIDGE=y CONFIG_BRIDGE_VLAN_FILTERING=y CONFIG_VLAN_8021Q=y +CONFIG_6LOWPAN=y CONFIG_NET_SCHED=y CONFIG_NET_CLS_CGROUP=m CONFIG_NETLINK_DIAG=y CONFIG_CGROUP_NET_PRIO=y +CONFIG_BT=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_BNEP_MC_FILTER=y +CONFIG_BT_BNEP_PROTO_FILTER=y +CONFIG_BT_HIDP=y +CONFIG_BT_6LOWPAN=y +CONFIG_BT_HCIBTUSB=m +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIBCM203X=m CONFIG_CFG80211=y # CONFIG_CFG80211_CRDA_SUPPORT is not set CONFIG_CFG80211_WEXT=y CONFIG_MAC80211=y +CONFIG_RFKILL=y CONFIG_NET_9P=y CONFIG_NET_9P_VIRTIO=y CONFIG_PCI=y -- 2.7.4 From 2ca6b2ad5466c8e656004f23e62ba86eeea45f9e Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Mon, 17 Apr 2023 15:18:41 +0900 Subject: [PATCH 08/16] Revert "RISCV: configs: Enable pcie plda defconfig" This reverts commit c8cfb82195d6e397017aa28e3e347766402e43ac. Revert this patch for use the pcie driver from upstream branch of starfive-tech/linux. Change-Id: I2769cb8e90bd8430508bda95762f39ac21fe0580 Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 1 - 1 file changed, 1 deletion(-) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index a9694d2..87f1f64 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -114,7 +114,6 @@ CONFIG_PCI=y CONFIG_PCIEPORTBUS=y CONFIG_PCI_HOST_GENERIC=y CONFIG_PCIE_XILINX=y -CONFIG_PCIE_PLDA=y CONFIG_PCIE_FU740=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y -- 2.7.4 From 14d237f47641896175a1f5c5d87a83f4e8d72981 Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Mon, 17 Apr 2023 15:18:48 +0900 Subject: [PATCH 09/16] Revert "PCI: Add support plda pcie host controller driver" This reverts commit b31cbbfa7c21b27610b850734b419469494948b7. Revert this patch for use the pcie driver from upstream branch of starfive-tech/linux. Change-Id: I7617faba6679d0988140b22bda05c2cfd2a8d4cf Signed-off-by: Hoegeun Kwon --- .../dts/starfive/jh7110-starfive-visionfive-2.dtsi | 91 -- arch/riscv/boot/dts/starfive/jh7110.dtsi | 90 -- drivers/pci/controller/Kconfig | 8 - drivers/pci/controller/Makefile | 1 - drivers/pci/controller/pcie-plda.c | 1061 -------------------- 5 files changed, 1251 deletions(-) mode change 100755 => 100644 drivers/pci/controller/Kconfig mode change 100755 => 100644 drivers/pci/controller/Makefile delete mode 100644 drivers/pci/controller/pcie-plda.c diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 3fbcdaa..752bb0b 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -319,78 +319,6 @@ slew-rate = <0>; }; }; - - pcie0_perst_default: pcie0-perst-default { - perst-pins { - pinmux = ; - }; - }; - - pcie0_perst_active: pcie0-perst-active { - perst-pins { - pinmux = ; - }; - }; - - pcie0_wake_default: pcie0-wake-default { - wake-pins { - pinmux = ; - }; - }; - - pcie0_clkreq_default: pcie0-clkreq-default { - clkreq-pins { - pinmux = ; - }; - }; - - pcie0_vbus_default: pcie0-vbus-default { - drive-vbus-pin { - pinmux = ; - }; - }; - - pcie1_perst_default: pcie1-perst-default { - perst-pins { - pinmux = ; - }; - }; - - pcie1_perst_active: pcie1-perst-active { - perst-pins { - pinmux = ; - }; - }; - - pcie1_wake_default: pcie1-wake-default { - wake-pins { - pinmux = ; - }; - }; - - pcie1_clkreq_default: pcie1-clkreq-default { - clkreq-pins { - pinmux = ; - }; - }; }; &uart0 { @@ -405,22 +333,3 @@ dr_mode = "peripheral"; }; }; - -&pcie0 { - pinctrl-names = "default", "perst-default", "perst-active"; - pinctrl-0 = <&pcie0_wake_default>, - <&pcie0_clkreq_default>, - <&pcie0_vbus_default>; - pinctrl-1 = <&pcie0_perst_default>; - pinctrl-2 = <&pcie0_perst_active>; - status = "okay"; -}; - -&pcie1 { - pinctrl-names = "default", "perst-default", "perst-active"; - pinctrl-0 = <&pcie1_wake_default>, - <&pcie1_clkreq_default>; - pinctrl-1 = <&pcie1_perst_default>; - pinctrl-2 = <&pcie1_perst_active>; - status = "okay"; -}; diff --git a/arch/riscv/boot/dts/starfive/jh7110.dtsi b/arch/riscv/boot/dts/starfive/jh7110.dtsi index da70de9..95abd5f 100644 --- a/arch/riscv/boot/dts/starfive/jh7110.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110.dtsi @@ -293,16 +293,6 @@ cache-unified; }; - phyctrl0: multi-phyctrl@10210000 { - compatible = "starfive,phyctrl"; - reg = <0x0 0x10210000 0x0 0x10000>; - }; - - phyctrl1: pcie1-phyctrl@10220000 { - compatible = "starfive,phyctrl"; - reg = <0x0 0x10220000 0x0 0x10000>; - }; - plic: interrupt-controller@c000000 { compatible = "starfive,jh7110-plic", "sifive,plic-1.0.0"; reg = <0x0 0xc000000 0x0 0x4000000>; @@ -932,85 +922,5 @@ #reset-cells = <1>; power-domains = <&pwrc JH7110_PD_VOUT>; }; - - pcie0: pcie@2B000000 { - compatible = "plda,pci-xpressrich3-axi"; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - reg = <0x0 0x2B000000 0x0 0x1000000 - 0x9 0x40000000 0x0 0x10000000>; - reg-names = "reg", "config"; - device_type = "pci"; - starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130 0x1b8>; - starfive,phyctrl = <&phyctrl0 0x28 0x80>; - bus-range = <0x0 0xff>; - ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, - <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; - msi-parent = <&plic>; - interrupts = <56>; - interrupt-controller; - interrupt-names = "msi"; - interrupt-parent = <&plic>; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0x0 0x0 0x0 0x1 &plic 0x1>, - <0x0 0x0 0x0 0x2 &plic 0x2>, - <0x0 0x0 0x0 0x3 &plic 0x3>, - <0x0 0x0 0x0 0x4 &plic 0x4>; - resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, - <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, - <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, - <&stgcrg JH7110_STGRST_PCIE0_BRG>, - <&stgcrg JH7110_STGRST_PCIE0_CORE>, - <&stgcrg JH7110_STGRST_PCIE0_APB>; - reset-names = "rst_mst0", "rst_slv0", "rst_slv", - "rst_brg", "rst_core", "rst_apb"; - clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, - <&stgcrg JH7110_STGCLK_PCIE0_TL>, - <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, - <&stgcrg JH7110_STGCLK_PCIE0_APB>; - clock-names = "noc_bus_stg_axi", "tl", "axi_mst0", "apb"; - status = "disabled"; - }; - - pcie1:pcie@2C000000 { - compatible = "plda,pci-xpressrich3-axi"; - #address-cells = <3>; - #size-cells = <2>; - #interrupt-cells = <1>; - reg = <0x0 0x2C000000 0x0 0x1000000 - 0x9 0xc0000000 0x0 0x10000000>; - reg-names = "reg", "config"; - device_type = "pci"; - starfive,stg-syscon = <&stg_syscon 0x270 0x274 0x2e0 0x368>; - starfive,phyctrl = <&phyctrl1 0x28 0x80>; - bus-range = <0x0 0xff>; - ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, - <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; - msi-parent = <&plic>; - interrupts = <57>; - interrupt-controller; - interrupt-names = "msi"; - interrupt-parent = <&plic>; - interrupt-map-mask = <0x0 0x0 0x0 0x7>; - interrupt-map = <0x0 0x0 0x0 0x1 &plic 0x1>, - <0x0 0x0 0x0 0x2 &plic 0x2>, - <0x0 0x0 0x0 0x3 &plic 0x3>, - <0x0 0x0 0x0 0x4 &plic 0x4>; - resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, - <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, - <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, - <&stgcrg JH7110_STGRST_PCIE1_BRG>, - <&stgcrg JH7110_STGRST_PCIE1_CORE>, - <&stgcrg JH7110_STGRST_PCIE1_APB>; - reset-names = "rst_mst0", "rst_slv0", "rst_slv", - "rst_brg", "rst_core", "rst_apb"; - clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, - <&stgcrg JH7110_STGCLK_PCIE1_TL>, - <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, - <&stgcrg JH7110_STGCLK_PCIE1_APB>; - clock-names = "noc_bus_stg_axi", "tl", "axi_mst0", "apb"; - status = "disabled"; - }; }; }; diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig old mode 100755 new mode 100644 index be1fcb8..bfd9bac --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -343,14 +343,6 @@ config PCIE_MT7621 help This selects a driver for the MediaTek MT7621 PCIe Controller. -config PCIE_PLDA - tristate "PLDA XpressRICH3-AXI PCIe controller" - depends on OF - select PCI_MSI_IRQ_DOMAIN - help - Say 'Y' here if you want kernel to support the PLDA XpressRICH3-AXI - (with ECAM disabled) PCIe Host driver. - source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" source "drivers/pci/controller/cadence/Kconfig" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile old mode 100755 new mode 100644 index edcc6d4..37c8663 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -39,7 +39,6 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o -obj-$(CONFIG_PCIE_PLDA) += pcie-plda.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-plda.c b/drivers/pci/controller/pcie-plda.c deleted file mode 100644 index 3eb0487..0000000 --- a/drivers/pci/controller/pcie-plda.c +++ /dev/null @@ -1,1061 +0,0 @@ -/* - * PCIe host controller driver for Starfive JH7110 Soc. - * - * Based on pcie-altera.c, pcie-altera-msi.c. - * - * Copyright (C) Shanghai StarFive Technology Co., Ltd. - * - * Author: ke.zhu@starfivetech.com - * - * THE PRESENT SOFTWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS - * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE - * TIME. AS A RESULT, STARFIVE SHALL NOT BE HELD LIABLE FOR ANY - * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING - * FROM THE CONTENT OF SUCH SOFTWARE AND/OR THE USE MADE BY CUSTOMERS OF THE - * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "../pci.h" - -#define PCIE_BASIC_STATUS 0x018 -#define PCIE_CFGNUM 0x140 -#define IMASK_LOCAL 0x180 -#define ISTATUS_LOCAL 0x184 -#define IMSI_ADDR 0x190 -#define ISTATUS_MSI 0x194 -#define CFG_SPACE 0x1000 -#define GEN_SETTINGS 0x80 -#define PCIE_PCI_IDS 0x9C -#define PCIE_WINROM 0xFC -#define PMSG_SUPPORT_RX 0x3F0 - -#define PCI_MISC 0xB4 - -#define PLDA_EP_ENABLE 0 -#define PLDA_RP_ENABLE 1 - -#define PLDA_LINK_UP 1 -#define PLDA_LINK_DOWN 0 - -#define IDS_REVISION_ID 0x02 -#define IDS_PCI_TO_PCI_BRIDGE 0x060400 -#define IDS_CLASS_CODE_SHIFT 8 - -#define PLDA_DATA_LINK_ACTIVE BIT(5) -#define PREF_MEM_WIN_64_SUPPORT BIT(3) -#define PMSG_LTR_SUPPORT BIT(2) -#define PDLA_LINK_SPEED_GEN2 BIT(12) -#define PLDA_FUNCTION_DIS BIT(15) -#define PLDA_FUNC_NUM 4 -#define PLDA_PHY_FUNC_SHIFT 9 -#define PHY_KVCO_FINE_TUNE_LEVEL 0x91 -#define PHY_KVCO_FINE_TUNE_SIGNALS 0xc - -#define XR3PCI_ATR_AXI4_SLV0 0x800 -#define XR3PCI_ATR_SRC_ADDR_LOW 0x0 -#define XR3PCI_ATR_SRC_ADDR_HIGH 0x4 -#define XR3PCI_ATR_TRSL_ADDR_LOW 0x8 -#define XR3PCI_ATR_TRSL_ADDR_HIGH 0xc -#define XR3PCI_ATR_TRSL_PARAM 0x10 -#define XR3PCI_ATR_TABLE_OFFSET 0x20 -#define XR3PCI_ATR_MAX_TABLE_NUM 8 - -#define XR3PCI_ATR_SRC_WIN_SIZE_SHIFT 1 -#define XR3PCI_ATR_SRC_ADDR_MASK 0xfffff000 -#define XR3PCI_ATR_TRSL_ADDR_MASK 0xfffff000 -#define XR3_PCI_ECAM_SIZE 28 -#define XR3PCI_ATR_TRSL_DIR BIT(22) -/* IDs used in the XR3PCI_ATR_TRSL_PARAM */ -#define XR3PCI_ATR_TRSLID_PCIE_MEMORY 0x0 -#define XR3PCI_ATR_TRSLID_PCIE_CONFIG 0x1 - -#define CFGNUM_DEVFN_SHIFT 0 -#define CFGNUM_BUS_SHIFT 8 -#define CFGNUM_BE_SHIFT 16 -#define CFGNUM_FBE_SHIFT 20 - -#define ECAM_BUS_SHIFT 20 -#define ECAM_DEV_SHIFT 15 -#define ECAM_FUNC_SHIFT 12 - -#define INT_AXI_POST_ERROR BIT(16) -#define INT_AXI_FETCH_ERROR BIT(17) -#define INT_AXI_DISCARD_ERROR BIT(18) -#define INT_PCIE_POST_ERROR BIT(20) -#define INT_PCIE_FETCH_ERROR BIT(21) -#define INT_PCIE_DISCARD_ERROR BIT(22) -#define INT_ERRORS (INT_AXI_POST_ERROR | INT_AXI_FETCH_ERROR | \ - INT_AXI_DISCARD_ERROR | INT_PCIE_POST_ERROR | \ - INT_PCIE_FETCH_ERROR | INT_PCIE_DISCARD_ERROR) - -#define INTA_OFFSET 24 -#define INTA BIT(24) -#define INTB BIT(25) -#define INTC BIT(26) -#define INTD BIT(27) -#define INT_MSI BIT(28) -#define INT_INTX_MASK (INTA | INTB | INTC | INTD) -#define INT_MASK (INT_INTX_MASK | INT_MSI | INT_ERRORS) - -#define INT_PCI_MSI_NR 32 -#define LINK_UP_MASK 0xff - -#define PERST_DELAY_US 1000 - -/* system control */ -#define STG_SYSCON_K_RP_NEP_SHIFT 0x8 -#define STG_SYSCON_K_RP_NEP_MASK 0x100 -#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK 0x7FFF00 -#define STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT 0x8 -#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK 0x7FFF -#define STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT 0x0 -#define STG_SYSCON_CLKREQ_SHIFT 0x16 -#define STG_SYSCON_CLKREQ_MASK 0x400000 -#define STG_SYSCON_CKREF_SRC_SHIFT 0x12 -#define STG_SYSCON_CKREF_SRC_MASK 0xC0000 - -#define PCI_DEV(d) (((d) >> 3) & 0x1f) - -/* MSI information */ -struct plda_msi { - DECLARE_BITMAP(used, INT_PCI_MSI_NR); - struct irq_domain *msi_domain; - struct irq_domain *inner_domain; - /* Protect bitmap variable */ - struct mutex lock; -}; - -struct plda_pcie { - struct platform_device *pdev; - void __iomem *reg_base; - void __iomem *config_base; - struct resource *cfg_res; - struct regmap *reg_syscon; - struct regmap *reg_phyctrl; - u32 stg_arfun; - u32 stg_awfun; - u32 stg_rp_nep; - u32 stg_lnksta; - u32 phy_kvco_level; - u32 phy_kvco_tune_signals; - int irq; - struct irq_domain *legacy_irq_domain; - struct pci_host_bridge *bridge; - struct plda_msi msi; - struct reset_control *resets; - struct clk_bulk_data *clks; - int num_clks; - int atr_table_num; - struct pinctrl *pinctrl; - struct pinctrl_state *perst_state_def; - struct pinctrl_state *perst_state_active; -}; - -static inline void plda_writel(struct plda_pcie *pcie, const u32 value, - const u32 reg) -{ - writel_relaxed(value, pcie->reg_base + reg); -} - -static inline u32 plda_readl(struct plda_pcie *pcie, const u32 reg) -{ - return readl_relaxed(pcie->reg_base + reg); -} - -static bool plda_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, - int offset) -{ - if (pci_is_root_bus(bus) && (devfn == 0) && - (offset == PCI_BASE_ADDRESS_0)) - return true; - - return false; -} - -static int _plda_pcie_config_read(struct plda_pcie *pcie, unsigned char busno, - unsigned int devfn, int where, int size, - u32 *value) -{ - void __iomem *addr; - - addr = pcie->config_base; - addr += (busno << ECAM_BUS_SHIFT); - addr += (PCI_DEV(devfn) << ECAM_DEV_SHIFT); - addr += (PCI_FUNC(devfn) << ECAM_FUNC_SHIFT); - addr += where; - - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 1: - *(unsigned char *)value = readb(addr); - break; - case 2: - *(unsigned short *)value = readw(addr); - break; - case 4: - *(unsigned int *)value = readl(addr); - break; - default: - return PCIBIOS_SET_FAILED; - } - - return PCIBIOS_SUCCESSFUL; -} - -int _plda_pcie_config_write(struct plda_pcie *pcie, unsigned char busno, - unsigned int devfn, int where, int size, u32 value) -{ - void __iomem *addr; - - addr = pcie->config_base; - addr += (busno << ECAM_BUS_SHIFT); - addr += (PCI_DEV(devfn) << ECAM_DEV_SHIFT); - addr += (PCI_FUNC(devfn) << ECAM_FUNC_SHIFT); - addr += where; - - if (!addr) - return PCIBIOS_DEVICE_NOT_FOUND; - - switch (size) { - case 1: - writeb(value, addr); - break; - case 2: - writew(value, addr); - break; - case 4: - writel(value, addr); - break; - default: - return PCIBIOS_SET_FAILED; - } - - return PCIBIOS_SUCCESSFUL; -} - -static int plda_pcie_config_read(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 *value) -{ - struct plda_pcie *pcie = bus->sysdata; - - return _plda_pcie_config_read(pcie, bus->number, devfn, where, size, - value); -} - -int plda_pcie_config_write(struct pci_bus *bus, unsigned int devfn, - int where, int size, u32 value) -{ - struct plda_pcie *pcie = bus->sysdata; - - if (plda_pcie_hide_rc_bar(bus, devfn, where)) - return PCIBIOS_BAD_REGISTER_NUMBER; - - return _plda_pcie_config_write(pcie, bus->number, devfn, where, size, - value); -} - -static void plda_pcie_handle_msi_irq(struct plda_pcie *pcie) -{ - struct plda_msi *msi = &pcie->msi; - u32 bit; - u32 virq; - unsigned long status = plda_readl(pcie, ISTATUS_MSI); - - for_each_set_bit(bit, &status, INT_PCI_MSI_NR) { - /* Clear interrupts */ - plda_writel(pcie, 1 << bit, ISTATUS_MSI); - - virq = irq_find_mapping(msi->inner_domain, bit); - if (virq) { - if (test_bit(bit, msi->used)) - generic_handle_irq(virq); - else - dev_err(&pcie->pdev->dev, - "Unhandled MSI, MSI%d virq %d\n", bit, - virq); - } else - dev_err(&pcie->pdev->dev, "Unexpected MSI, MSI%d\n", - bit); - - } - plda_writel(pcie, INT_MSI, ISTATUS_LOCAL); -} - -static void plda_pcie_handle_intx_irq(struct plda_pcie *pcie, - unsigned long status) -{ - u32 bit; - u32 virq; - - status >>= INTA_OFFSET; - - for_each_set_bit(bit, &status, PCI_NUM_INTX) { - /* Clear interrupts */ - plda_writel(pcie, 1 << (bit + INTA_OFFSET), ISTATUS_LOCAL); - - virq = irq_find_mapping(pcie->legacy_irq_domain, bit); - if (virq) - generic_handle_irq(virq); - else - dev_err(&pcie->pdev->dev, - "plda_pcie_handle_intx_irq unexpected IRQ, INT%d\n", bit); - } -} - -static void plda_pcie_handle_errors_irq(struct plda_pcie *pcie, u32 status) -{ - if (status & INT_AXI_POST_ERROR) - dev_err(&pcie->pdev->dev, "AXI post error\n"); - if (status & INT_AXI_FETCH_ERROR) - dev_err(&pcie->pdev->dev, "AXI fetch error\n"); - if (status & INT_AXI_DISCARD_ERROR) - dev_err(&pcie->pdev->dev, "AXI discard error\n"); - if (status & INT_PCIE_POST_ERROR) - dev_err(&pcie->pdev->dev, "PCIe post error\n"); - if (status & INT_PCIE_FETCH_ERROR) - dev_err(&pcie->pdev->dev, "PCIe fetch error\n"); - if (status & INT_PCIE_DISCARD_ERROR) - dev_err(&pcie->pdev->dev, "PCIe discard error\n"); - - plda_writel(pcie, INT_ERRORS, ISTATUS_LOCAL); -} - -static void plda_pcie_isr(struct irq_desc *desc) -{ - struct irq_chip *chip = irq_desc_get_chip(desc); - struct plda_pcie *pcie; - u32 status; - - chained_irq_enter(chip, desc); - pcie = irq_desc_get_handler_data(desc); - - status = plda_readl(pcie, ISTATUS_LOCAL); - while ((status = (plda_readl(pcie, ISTATUS_LOCAL) & INT_MASK))) { - if (status & INT_INTX_MASK) - plda_pcie_handle_intx_irq(pcie, status); - - if (status & INT_MSI) - plda_pcie_handle_msi_irq(pcie); - - if (status & INT_ERRORS) - plda_pcie_handle_errors_irq(pcie, status); - } - - chained_irq_exit(chip, desc); -} - -#ifdef CONFIG_PCI_MSI -static struct irq_chip plda_msi_irq_chip = { - .name = "PLDA PCIe MSI", - .irq_mask = pci_msi_mask_irq, - .irq_unmask = pci_msi_unmask_irq, -}; - -static struct msi_domain_info plda_msi_domain_info = { - .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | - MSI_FLAG_PCI_MSIX), - .chip = &plda_msi_irq_chip, -}; -#endif - -static void plda_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) -{ - struct plda_pcie *pcie = irq_data_get_irq_chip_data(data); - phys_addr_t msi_addr = plda_readl(pcie, IMSI_ADDR); - - msg->address_lo = lower_32_bits(msi_addr); - msg->address_hi = upper_32_bits(msi_addr); - msg->data = data->hwirq; - - dev_info(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", - (int)data->hwirq, msg->address_hi, msg->address_lo); -} - -static int plda_msi_set_affinity(struct irq_data *irq_data, - const struct cpumask *mask, bool force) -{ - return -EINVAL; -} - -static struct irq_chip plda_irq_chip = { - .name = "PLDA MSI", - .irq_compose_msi_msg = plda_compose_msi_msg, - .irq_set_affinity = plda_msi_set_affinity, -}; - -static int plda_msi_alloc(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs, void *args) -{ - struct plda_pcie *pcie = domain->host_data; - struct plda_msi *msi = &pcie->msi; - int bit; - - WARN_ON(nr_irqs != 1); - mutex_lock(&msi->lock); - - bit = find_first_zero_bit(msi->used, INT_PCI_MSI_NR); - if (bit >= INT_PCI_MSI_NR) { - mutex_unlock(&msi->lock); - return -ENOSPC; - } - - set_bit(bit, msi->used); - - irq_domain_set_info(domain, virq, bit, &plda_irq_chip, - domain->host_data, handle_simple_irq, - NULL, NULL); - mutex_unlock(&msi->lock); - - return 0; -} - -static void plda_msi_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) -{ - struct irq_data *data = irq_domain_get_irq_data(domain, virq); - struct plda_pcie *pcie = irq_data_get_irq_chip_data(data); - struct plda_msi *msi = &pcie->msi; - - mutex_lock(&msi->lock); - - if (!test_bit(data->hwirq, msi->used)) - dev_err(&pcie->pdev->dev, "Trying to free unused MSI#%lu\n", - data->hwirq); - else - __clear_bit(data->hwirq, msi->used); - - mutex_unlock(&msi->lock); -} - -static const struct irq_domain_ops dev_msi_domain_ops = { - .alloc = plda_msi_alloc, - .free = plda_msi_free, -}; - -static void plda_msi_free_irq_domain(struct plda_pcie *pcie) -{ -#ifdef CONFIG_PCI_MSI - struct plda_msi *msi = &pcie->msi; - u32 irq; - int i; - - for (i = 0; i < INT_PCI_MSI_NR; i++) { - irq = irq_find_mapping(msi->inner_domain, i); - if (irq > 0) - irq_dispose_mapping(irq); - } - - if (msi->msi_domain) - irq_domain_remove(msi->msi_domain); - - if (msi->inner_domain) - irq_domain_remove(msi->inner_domain); -#endif -} - -static void plda_pcie_free_irq_domain(struct plda_pcie *pcie) -{ - int i; - u32 irq; - - /* Disable all interrupts */ - plda_writel(pcie, 0, IMASK_LOCAL); - - if (pcie->legacy_irq_domain) { - for (i = 0; i < PCI_NUM_INTX; i++) { - irq = irq_find_mapping(pcie->legacy_irq_domain, i); - if (irq > 0) - irq_dispose_mapping(irq); - } - irq_domain_remove(pcie->legacy_irq_domain); - } - - if (pci_msi_enabled()) - plda_msi_free_irq_domain(pcie); - irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); -} - -static int plda_pcie_init_msi_irq_domain(struct plda_pcie *pcie) -{ -#ifdef CONFIG_PCI_MSI - struct fwnode_handle *fwn = of_node_to_fwnode(pcie->pdev->dev.of_node); - struct plda_msi *msi = &pcie->msi; - - msi->inner_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, - &dev_msi_domain_ops, pcie); - if (!msi->inner_domain) { - dev_err(&pcie->pdev->dev, "Failed to create dev IRQ domain\n"); - return -ENOMEM; - } - msi->msi_domain = pci_msi_create_irq_domain(fwn, &plda_msi_domain_info, - msi->inner_domain); - if (!msi->msi_domain) { - dev_err(&pcie->pdev->dev, "Failed to create msi IRQ domain\n"); - irq_domain_remove(msi->inner_domain); - return -ENOMEM; - } -#endif - return 0; -} - -static int plda_pcie_enable_msi(struct plda_pcie *pcie, struct pci_bus *bus) -{ - struct plda_msi *msi = &pcie->msi; - u32 reg; - - mutex_init(&msi->lock); - - /* Enable MSI */ - reg = plda_readl(pcie, IMASK_LOCAL); - reg |= INT_MSI; - plda_writel(pcie, reg, IMASK_LOCAL); - return 0; -} - -static int plda_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 = plda_pcie_intx_map, - .xlate = pci_irqd_intx_xlate, -}; - -static int plda_pcie_init_irq_domain(struct plda_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - struct device_node *node = dev->of_node; - int ret; - - if (pci_msi_enabled()) { - ret = plda_pcie_init_msi_irq_domain(pcie); - if (ret != 0) - return -ENOMEM; - } - - /* Setup INTx */ - pcie->legacy_irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, - &intx_domain_ops, pcie); - - if (!pcie->legacy_irq_domain) { - dev_err(dev, "Failed to get a INTx IRQ domain\n"); - return -ENOMEM; - } - - irq_set_chained_handler_and_data(pcie->irq, plda_pcie_isr, pcie); - return 0; -} - -static int plda_pcie_parse_dt(struct plda_pcie *pcie) -{ - struct resource *reg_res; - struct platform_device *pdev = pcie->pdev; - struct of_phandle_args syscon_args, phyctrl_args; - int ret; - - reg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "reg"); - if (!reg_res) { - dev_err(&pdev->dev, "Missing required reg address range\n"); - return -ENODEV; - } - - pcie->reg_base = devm_ioremap_resource(&pdev->dev, reg_res); - if (IS_ERR(pcie->reg_base)) { - dev_err(&pdev->dev, "Failed to map reg memory\n"); - return PTR_ERR(pcie->reg_base); - } - - pcie->cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); - if (!pcie->cfg_res) { - dev_err(&pdev->dev, "Missing required config address range"); - return -ENODEV; - } - - pcie->config_base = devm_ioremap_resource(&pdev->dev, pcie->cfg_res); - if (IS_ERR(pcie->config_base)){ - dev_err(&pdev->dev, "Failed to map config memory\n"); - return PTR_ERR(pcie->config_base); - } - - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "starfive,phyctrl", 2, 0, &phyctrl_args); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to parse starfive,phyctrl\n"); - return -EINVAL; - } - - if (!of_device_is_compatible(phyctrl_args.np, "starfive,phyctrl")) - return -EINVAL; - pcie->reg_phyctrl = device_node_to_regmap(phyctrl_args.np); - of_node_put(phyctrl_args.np); - if (IS_ERR(pcie->reg_phyctrl)) - return PTR_ERR(pcie->reg_phyctrl); - - pcie->phy_kvco_level = phyctrl_args.args[0]; - pcie->phy_kvco_tune_signals = phyctrl_args.args[1]; - - pcie->irq = platform_get_irq(pdev, 0); - if (pcie->irq <= 0) { - dev_err(&pdev->dev, "Failed to get IRQ: %d\n", pcie->irq); - return -EINVAL; - } - - ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node, - "starfive,stg-syscon", 4, 0, &syscon_args); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to parse starfive,stg-syscon\n"); - return -EINVAL; - } - - pcie->reg_syscon = syscon_node_to_regmap(syscon_args.np); - of_node_put(syscon_args.np); - if (IS_ERR(pcie->reg_syscon)) - return PTR_ERR(pcie->reg_syscon); - - pcie->stg_arfun = syscon_args.args[0]; - pcie->stg_awfun = syscon_args.args[1]; - pcie->stg_rp_nep = syscon_args.args[2]; - pcie->stg_lnksta = syscon_args.args[3]; - - /* Clear all interrupts */ - plda_writel(pcie, 0xffffffff, ISTATUS_LOCAL); - plda_writel(pcie, INT_INTX_MASK | INT_ERRORS, IMASK_LOCAL); - - return 0; -} - -static struct pci_ops plda_pcie_ops = { - .read = plda_pcie_config_read, - .write = plda_pcie_config_write, -}; - -void plda_set_atr_entry(struct plda_pcie *pcie, phys_addr_t src_addr, - phys_addr_t trsl_addr, size_t window_size, - int trsl_param) -{ - void __iomem *base = - pcie->reg_base + XR3PCI_ATR_AXI4_SLV0; - - /* Support AXI4 Slave 0 Address Translation Tables 0-7. */ - if (pcie->atr_table_num >= XR3PCI_ATR_MAX_TABLE_NUM) - pcie->atr_table_num = XR3PCI_ATR_MAX_TABLE_NUM - 1; - base += XR3PCI_ATR_TABLE_OFFSET * pcie->atr_table_num; - pcie->atr_table_num++; - - /* X3PCI_ATR_SRC_ADDR_LOW: - * - bit 0: enable entry, - * - bits 1-6: ATR window size: total size in bytes: 2^(ATR_WSIZE + 1) - * - bits 7-11: reserved - * - bits 12-31: start of source address - */ - writel((lower_32_bits(src_addr) & XR3PCI_ATR_SRC_ADDR_MASK) | - (fls(window_size) - 1) << XR3PCI_ATR_SRC_WIN_SIZE_SHIFT | 1, - base + XR3PCI_ATR_SRC_ADDR_LOW); - writel(upper_32_bits(src_addr), base + XR3PCI_ATR_SRC_ADDR_HIGH); - writel((lower_32_bits(trsl_addr) & XR3PCI_ATR_TRSL_ADDR_MASK), - base + XR3PCI_ATR_TRSL_ADDR_LOW); - writel(upper_32_bits(trsl_addr), base + XR3PCI_ATR_TRSL_ADDR_HIGH); - writel(trsl_param, base + XR3PCI_ATR_TRSL_PARAM); - - pr_info("ATR entry: 0x%010llx %s 0x%010llx [0x%010llx] (param: 0x%06x)\n", - src_addr, (trsl_param & XR3PCI_ATR_TRSL_DIR) ? "<-" : "->", - trsl_addr, (u64)window_size, trsl_param); -} - -static int plda_pcie_setup_windows(struct plda_pcie *pcie) -{ - struct pci_host_bridge *bridge = pcie->bridge; - struct resource_entry *entry; - u64 pci_addr; - - resource_list_for_each_entry(entry, &bridge->windows) { - if (resource_type(entry->res) == IORESOURCE_MEM) { - pci_addr = entry->res->start - entry->offset; - plda_set_atr_entry(pcie, - entry->res->start, pci_addr, - resource_size(entry->res), - XR3PCI_ATR_TRSLID_PCIE_MEMORY); - } - } - - return 0; -} - -static int plda_clk_rst_init(struct plda_pcie *pcie) -{ - int ret; - struct device *dev = &pcie->pdev->dev; - - pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); - if (pcie->num_clks < 0) { - dev_err(dev, "Failed to get pcie clocks\n"); - ret = -ENODEV; - goto exit; - } - ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); - if (ret) { - dev_err(&pcie->pdev->dev, "Failed to enable clocks\n"); - goto exit; - } - - pcie->resets = devm_reset_control_array_get_exclusive(dev); - if (IS_ERR(pcie->resets)) { - ret = PTR_ERR(pcie->resets); - dev_err(dev, "Failed to get pcie resets"); - goto err_clk_init; - } - ret = reset_control_deassert(pcie->resets); - goto exit; - -err_clk_init: - clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); -exit: - return ret; -} - -static void plda_clk_rst_deinit(struct plda_pcie *pcie) -{ - reset_control_assert(pcie->resets); - clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); -} - -int plda_pinctrl_init(struct plda_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - - pcie->pinctrl = devm_pinctrl_get(dev); - if (IS_ERR_OR_NULL(pcie->pinctrl)) { - dev_err(dev, "Getting pinctrl handle failed\n"); - return -EINVAL; - } - - pcie->perst_state_def - = pinctrl_lookup_state(pcie->pinctrl, "perst-default"); - if (IS_ERR_OR_NULL(pcie->perst_state_def)) { - dev_err(dev, "Failed to get the perst-default pinctrl handle\n"); - return -EINVAL; - } - - pcie->perst_state_active - = pinctrl_lookup_state(pcie->pinctrl, "perst-active"); - if (IS_ERR_OR_NULL(pcie->perst_state_active)) { - dev_err(dev, "Failed to get the perst-active pinctrl handle\n"); - return -EINVAL; - } - - return 0; -} - -static void plda_pcie_hw_init(struct plda_pcie *pcie) -{ - unsigned int value; - int i, ret; - struct device *dev = &pcie->pdev->dev; - - if (pcie->perst_state_active) { - ret = pinctrl_select_state(pcie->pinctrl, pcie->perst_state_active); - if (ret) - dev_err(dev, "Cannot set reset pin to low\n"); - } - - /* Disable physical functions except #0 */ - for (i = 1; i < PLDA_FUNC_NUM; i++) { - regmap_update_bits(pcie->reg_syscon, - pcie->stg_arfun, - STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, - (i << PLDA_PHY_FUNC_SHIFT) << - STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT); - regmap_update_bits(pcie->reg_syscon, - pcie->stg_awfun, - STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, - (i << PLDA_PHY_FUNC_SHIFT) << - STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT); - - value = readl(pcie->reg_base + PCI_MISC); - value |= PLDA_FUNCTION_DIS; - writel(value, pcie->reg_base + PCI_MISC); - } - regmap_update_bits(pcie->reg_syscon, - pcie->stg_arfun, - STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, - 0 << STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT); - regmap_update_bits(pcie->reg_syscon, - pcie->stg_awfun, - STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, - 0 << STG_SYSCON_AXI4_SLVL_AWFUNC_SHIFT); - - /* PCIe Multi-PHY PLL KVCO Gain fine tune settings: */ - regmap_write(pcie->reg_phyctrl, pcie->phy_kvco_level, - PHY_KVCO_FINE_TUNE_LEVEL); - regmap_write(pcie->reg_phyctrl, pcie->phy_kvco_tune_signals, - PHY_KVCO_FINE_TUNE_SIGNALS); - - /* Enable root port*/ - value = readl(pcie->reg_base + GEN_SETTINGS); - value |= PLDA_RP_ENABLE; - writel(value, pcie->reg_base + GEN_SETTINGS); - - /* PCIe PCI Standard Configuration Identification Settings. */ - value = (IDS_PCI_TO_PCI_BRIDGE << IDS_CLASS_CODE_SHIFT) | IDS_REVISION_ID; - writel(value, pcie->reg_base + PCIE_PCI_IDS); - - /* The LTR message forwarding of PCIe Message Reception was set by core - * as default, but the forward id & addr are also need to be reset. - * If we do not disable LTR message forwarding here, or set a legal - * forwarding address, the kernel will get stuck after this driver probe. - * To workaround, disable the LTR message forwarding support on - * PCIe Message Reception. - */ - value = readl(pcie->reg_base + PMSG_SUPPORT_RX); - value &= ~PMSG_LTR_SUPPORT; - writel(value, pcie->reg_base + PMSG_SUPPORT_RX); - - /* Prefetchable memory window 64-bit addressing support */ - value = readl(pcie->reg_base + PCIE_WINROM); - value |= PREF_MEM_WIN_64_SUPPORT; - writel(value, pcie->reg_base + PCIE_WINROM); - - /* As the two host bridges in JH7110 soc have the same default - * address translation table, this cause the second root port can't - * access it's host bridge config space correctly. - * To workaround, config the ATR of host bridge config space by SW. - */ - plda_set_atr_entry(pcie, - pcie->cfg_res->start, 0, - 1 << XR3_PCI_ECAM_SIZE, - XR3PCI_ATR_TRSLID_PCIE_CONFIG); - - plda_pcie_setup_windows(pcie); - - /* Ensure that PERST has been asserted for at least 100 ms */ - msleep(300); - if (pcie->perst_state_def) { - ret = pinctrl_select_state(pcie->pinctrl, pcie->perst_state_def); - if (ret) - dev_err(dev, "Cannot set reset pin to high\n"); - } -} - -static int plda_pcie_is_link_up(struct plda_pcie *pcie) -{ - struct device *dev = &pcie->pdev->dev; - int ret; - u32 stg_reg_val; - - /* 100ms timeout value should be enough for Gen1/2 training */ - ret = regmap_read_poll_timeout(pcie->reg_syscon, - pcie->stg_lnksta, - stg_reg_val, - stg_reg_val & PLDA_DATA_LINK_ACTIVE, - 10 * 1000, 100 * 1000); - - /* If the link is down (no device in slot), then exit. */ - if (ret == -ETIMEDOUT) { - dev_info(dev, "Port link down, exit.\n"); - return PLDA_LINK_DOWN; - } else if (ret == 0) { - dev_info(dev, "Port link up.\n"); - return PLDA_LINK_UP; - } - - dev_warn(dev, "Read stg_linksta failed.\n"); - return ret; -} - -static int plda_pcie_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct plda_pcie *pcie; - struct pci_bus *bus; - struct pci_host_bridge *bridge; - int ret; - - pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); - if (!pcie) - return -ENOMEM; - - pcie->pdev = pdev; - pcie->atr_table_num = 0; - - ret = plda_pcie_parse_dt(pcie); - if (ret) { - dev_err(&pdev->dev, "Parsing DT failed\n"); - return ret; - } - - platform_set_drvdata(pdev, pcie); - - plda_pinctrl_init(pcie); - if (ret) { - dev_err(&pdev->dev, "Init pinctrl failed\n"); - return ret; - } - - regmap_update_bits(pcie->reg_syscon, - pcie->stg_rp_nep, - STG_SYSCON_K_RP_NEP_MASK, - 1 << STG_SYSCON_K_RP_NEP_SHIFT); - - regmap_update_bits(pcie->reg_syscon, - pcie->stg_awfun, - STG_SYSCON_CKREF_SRC_MASK, - 2 << STG_SYSCON_CKREF_SRC_SHIFT); - - regmap_update_bits(pcie->reg_syscon, - pcie->stg_awfun, - STG_SYSCON_CLKREQ_MASK, - 1 << STG_SYSCON_CLKREQ_SHIFT); - - ret = plda_clk_rst_init(pcie); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to init pcie clk reset: %d\n", ret); - goto exit; - } - - ret = plda_pcie_init_irq_domain(pcie); - if (ret) { - dev_err(&pdev->dev, "Failed creating IRQ Domain\n"); - goto exit; - } - - bridge = devm_pci_alloc_host_bridge(dev, 0); - if (!bridge) { - ret = -ENOMEM; - goto exit; - } - - pm_runtime_enable(&pdev->dev); - pm_runtime_get_sync(&pdev->dev); - - /* Set default bus ops */ - bridge->ops = &plda_pcie_ops; - bridge->sysdata = pcie; - pcie->bridge = bridge; - - plda_pcie_hw_init(pcie); - - if (plda_pcie_is_link_up(pcie) == PLDA_LINK_DOWN) - goto release; - - if (IS_ENABLED(CONFIG_PCI_MSI)) { - ret = plda_pcie_enable_msi(pcie, bus); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to enable MSI support: %d\n", ret); - goto release; - } - } - - ret = pci_host_probe(bridge); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to pci host probe: %d\n", ret); - goto release; - } - -exit: - return ret; - -release: - plda_clk_rst_deinit(pcie); - - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); - - pci_free_host_bridge(pcie->bridge); - devm_kfree(&pdev->dev, pcie); - platform_set_drvdata(pdev, NULL); - - return ret; -} - -static int plda_pcie_remove(struct platform_device *pdev) -{ - struct plda_pcie *pcie = platform_get_drvdata(pdev); - - plda_pcie_free_irq_domain(pcie); - plda_clk_rst_deinit(pcie); - platform_set_drvdata(pdev, NULL); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int __maybe_unused plda_pcie_suspend_noirq(struct device *dev) -{ - struct plda_pcie *pcie = dev_get_drvdata(dev); - - if (!pcie) - return 0; - - clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); - - return 0; -} - -static int __maybe_unused plda_pcie_resume_noirq(struct device *dev) -{ - struct plda_pcie *pcie = dev_get_drvdata(dev); - int ret; - - if (!pcie) - return 0; - - ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); - if (ret) - dev_err(dev, "Failed to enable clocks\n"); - - return ret; -} - -static const struct dev_pm_ops plda_pcie_pm_ops = { - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(plda_pcie_suspend_noirq, - plda_pcie_resume_noirq) -}; -#endif - -static const struct of_device_id plda_pcie_of_match[] = { - { .compatible = "plda,pci-xpressrich3-axi"}, - { }, -}; -MODULE_DEVICE_TABLE(of, plda_pcie_of_match); - -static struct platform_driver plda_pcie_driver = { - .driver = { - .name = KBUILD_MODNAME, - .of_match_table = of_match_ptr(plda_pcie_of_match), -#ifdef CONFIG_PM_SLEEP - .pm = &plda_pcie_pm_ops, -#endif - }, - .probe = plda_pcie_probe, - .remove = plda_pcie_remove, -}; -module_platform_driver(plda_pcie_driver); - -MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); -MODULE_AUTHOR("ke.zhu "); -MODULE_AUTHOR("Mason Huo "); -MODULE_LICENSE("GPL v2"); -- 2.7.4 From 871007e46f0f61928bc742304da43f977408d2cd Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 6 Apr 2023 09:52:10 +0800 Subject: [PATCH 10/16] dt-bindings: phy: Add StarFive JH7110 USB document Add StarFive JH7110 SoC USB 2.0 PHY dt-binding. Signed-off-by: Minda Chen [Backported from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_upstream] Change-Id: I402d255181b5ff3a14e2faea7f54753f83757cd2 Signed-off-by: Hoegeun Kwon --- .../bindings/phy/starfive,jh7110-usb-phy.yaml | 50 ++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml new file mode 100644 index 0000000..80604df --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-usb-phy.yaml @@ -0,0 +1,50 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-usb-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive USB 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-usb-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + clocks: + items: + - description: usb 125m clock + - description: app 125m clock + + clock-names: + items: + - const: 125m + - const: app_125 + +required: + - compatible + - reg + - clocks + - clock-names + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10200000 { + compatible = "starfive,jh7110-usb-phy"; + reg = <0x10200000 0x10000>; + clocks = <&syscrg 95>, + <&stgcrg 6>; + clock-names = "125m", "app_125"; + #phy-cells = <0>; + }; -- 2.7.4 From 2b436977d0141ecbdd3264bc68ae104a60b6eaf3 Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 6 Apr 2023 09:52:11 +0800 Subject: [PATCH 11/16] dt-bindings: phy: Add StarFive JH7110 PCIe document Add StarFive JH7110 SoC PCIe 2.0 PHY dt-binding. PCIe PHY0 (phy@10210000) can be used as USB 3.0 PHY. Signed-off-by: Minda Chen [Backported from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_upstream] Change-Id: I67a7522fe31e9adb476e895f2107f4432d2ad19a Signed-off-by: Hoegeun Kwon --- .../bindings/phy/starfive,jh7110-pcie-phy.yaml | 58 ++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml new file mode 100644 index 0000000..1b868f7 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/starfive,jh7110-pcie-phy.yaml @@ -0,0 +1,58 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/starfive,jh7110-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive PCIe 2.0 PHY + +maintainers: + - Minda Chen + +properties: + compatible: + const: starfive,jh7110-pcie-phy + + reg: + maxItems: 1 + + "#phy-cells": + const: 0 + + starfive,sys-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller sys_syscon node. + - description: PHY connect offset of SYS_SYSCONSAIF__SYSCFG register for USB PHY. + description: + The phandle to System Register Controller syscon node and the PHY connect offset + of SYS_SYSCONSAIF__SYSCFG register. Connect PHY to USB3 controller. + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller stg_syscon node. + - description: PHY mode offset of STG_SYSCONSAIF__SYSCFG register. + - description: PHY enable for USB offset of STG_SYSCONSAIF__SYSCFG register. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for PCIe PHY. Total 2 regsisters offset. + +required: + - compatible + - reg + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@10210000 { + compatible = "starfive,jh7110-pcie-phy"; + reg = <0x10210000 0x10000>; + #phy-cells = <0>; + starfive,sys-syscon = <&sys_syscon 0x18>; + starfive,stg-syscon = <&stg_syscon 0x148 0x1f4>; + }; -- 2.7.4 From 3e1012247d6ba1df41167136f941b7d4f36ad43a Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 6 Apr 2023 19:11:40 +0800 Subject: [PATCH 12/16] dt-binding: pci: add JH7110 PCIe dt-binding documents. Add PCIe controller driver dt-binding documents for StarFive JH7110 SoC platform. Signed-off-by: Minda Chen [Backported from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_upstream] Change-Id: Ibd3ed0ecf830d56cedaf496510fecc610b4700cf Signed-off-by: Hoegeun Kwon --- .../bindings/pci/starfive,jh7110-pcie.yaml | 163 +++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml diff --git a/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml new file mode 100644 index 0000000..fa48297 --- /dev/null +++ b/Documentation/devicetree/bindings/pci/starfive,jh7110-pcie.yaml @@ -0,0 +1,163 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pci/starfive,jh7110-pcie.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: StarFive JH7110 PCIe 2.0 host controller + +maintainers: + - Minda Chen + +allOf: + - $ref: /schemas/pci/pci-bus.yaml# + - $ref: /schemas/interrupt-controller/msi-controller.yaml# + +properties: + compatible: + const: starfive,jh7110-pcie + + reg: + maxItems: 2 + + reg-names: + items: + - const: reg + - const: config + + msi-parent: true + + interrupts: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: noc + - const: tl + - const: axi_mst0 + - const: apb + + resets: + items: + - description: AXI MST0 reset + - description: AXI SLAVE reset + - description: AXI SLAVE0 reset + - description: PCIE BRIDGE reset + - description: PCIE CORE reset + - description: PCIE APB reset + + reset-names: + items: + - const: mst0 + - const: slv0 + - const: slv + - const: brg + - const: core + - const: apb + + starfive,stg-syscon: + $ref: /schemas/types.yaml#/definitions/phandle-array + items: + items: + - description: phandle to System Register Controller stg_syscon node. + - description: register0 offset of STG_SYSCONSAIF__SYSCFG register for PCIe. + - description: register1 offset of STG_SYSCONSAIF__SYSCFG register for PCIe. + - description: register2 offset of STG_SYSCONSAIF__SYSCFG register for PCIe. + - description: register3 offset of STG_SYSCONSAIF__SYSCFG register for PCIe. + description: + The phandle to System Register Controller syscon node and the offset + of STG_SYSCONSAIF__SYSCFG register for PCIe. Total 4 regsisters offset + for PCIe. + + pwren-gpios: + description: Should specify the GPIO for controlling the PCI bus device power on. + maxItems: 1 + + reset-gpios: + maxItems: 1 + + phys: + maxItems: 1 + + interrupt-controller: + type: object + properties: + '#address-cells': + const: 0 + + '#interrupt-cells': + const: 1 + + interrupt-controller: true + + required: + - '#address-cells' + - '#interrupt-cells' + - interrupt-controller + + additionalProperties: false + +required: + - reg + - reg-names + - "#interrupt-cells" + - interrupts + - interrupt-map-mask + - interrupt-map + - clocks + - clock-names + - resets + - msi-controller + +unevaluatedProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + pcie0: pcie@2B000000 { + compatible = "starfive,jh7110-pcie"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x2B000000 0x0 0x1000000>, + <0x9 0x40000000 0x0 0x10000000>; + reg-names = "reg", "config"; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130 0x1b8>; + bus-range = <0x0 0xff>; + ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, + <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; + interrupt-parent = <&plic>; + interrupts = <56>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; + msi-parent = <&pcie0>; + msi-controller; + clocks = <&syscrg 86>, + <&stgcrg 10>, + <&stgcrg 8>, + <&stgcrg 9>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg 11>, + <&stgcrg 12>, + <&stgcrg 13>, + <&stgcrg 14>, + <&stgcrg 15>, + <&stgcrg 16>; + + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + }; -- 2.7.4 From 15056c53b119b91d03669df76feb86a79da8e8aa Mon Sep 17 00:00:00 2001 From: Minda Chen Date: Thu, 6 Apr 2023 19:11:41 +0800 Subject: [PATCH 13/16] pcie: starfive: add StarFive JH7110 PCIe driver. Add PCIe controller driver for StarFive JH7110 SoC platform. The PCIe controller is PCIe 2.0, single lane. Signed-off-by: Minda Chen [Backported from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_upstream] Change-Id: I04b0fd6ef7085030ecc682de384b009e2e1d66fe Signed-off-by: Hoegeun Kwon --- drivers/pci/controller/Kconfig | 8 + drivers/pci/controller/Makefile | 1 + drivers/pci/controller/pcie-starfive.c | 958 +++++++++++++++++++++++++++++++++ 3 files changed, 967 insertions(+) create mode 100644 drivers/pci/controller/pcie-starfive.c diff --git a/drivers/pci/controller/Kconfig b/drivers/pci/controller/Kconfig index bfd9bac..7f171e1 100644 --- a/drivers/pci/controller/Kconfig +++ b/drivers/pci/controller/Kconfig @@ -343,6 +343,14 @@ config PCIE_MT7621 help This selects a driver for the MediaTek MT7621 PCIe Controller. +config PCIE_STARFIVE + tristate "StarFive JH7110 PCIe controller" + depends on PCI_MSI && OF + select PCI_MSI_IRQ_DOMAIN + help + Say 'Y' here if you want kernel to support the StarFive JH7110 + PCIe Host driver. + source "drivers/pci/controller/dwc/Kconfig" source "drivers/pci/controller/mobiveil/Kconfig" source "drivers/pci/controller/cadence/Kconfig" diff --git a/drivers/pci/controller/Makefile b/drivers/pci/controller/Makefile index 37c8663..2370822 100644 --- a/drivers/pci/controller/Makefile +++ b/drivers/pci/controller/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PCI_LOONGSON) += pci-loongson.o obj-$(CONFIG_PCIE_HISI_ERR) += pcie-hisi-error.o obj-$(CONFIG_PCIE_APPLE) += pcie-apple.o obj-$(CONFIG_PCIE_MT7621) += pcie-mt7621.o +obj-$(CONFIG_PCIE_STARFIVE) += pcie-starfive.o # pcie-hisi.o quirks are needed even without CONFIG_PCIE_DW obj-y += dwc/ diff --git a/drivers/pci/controller/pcie-starfive.c b/drivers/pci/controller/pcie-starfive.c new file mode 100644 index 0000000..e1dc8ec --- /dev/null +++ b/drivers/pci/controller/pcie-starfive.c @@ -0,0 +1,958 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * PCIe host controller driver for Starfive JH7110 Soc. + * + * Based on pcie-altera.c, pcie-altera-msi.c. + * + * Copyright (C) StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../pci.h" + +#define IMASK_LOCAL 0x180 +#define ISTATUS_LOCAL 0x184 +#define IMSI_ADDR 0x190 +#define ISTATUS_MSI 0x194 +#define GEN_SETTINGS 0x80 +#define PCIE_PCI_IDS 0x9C +#define PCIE_WINROM 0xFC +#define PMSG_SUPPORT_RX 0x3F0 + +#define PCI_MISC 0xB4 + +#define RP_ENABLE 1 + +#define IDS_CLASS_CODE_SHIFT 16 + +#define DATA_LINK_ACTIVE BIT(5) +#define PREF_MEM_WIN_64_SUPPORT BIT(3) +#define PMSG_LTR_SUPPORT BIT(2) +#define LINK_SPEED_GEN2 BIT(12) +#define PHY_FUNCTION_DIS BIT(15) +#define PCIE_FUNC_NUM 4 +#define PHY_FUNC_SHIFT 9 + +#define XR3PCI_ATR_AXI4_SLV0 0x800 +#define XR3PCI_ATR_SRC_ADDR_LOW 0x0 +#define XR3PCI_ATR_SRC_ADDR_HIGH 0x4 +#define XR3PCI_ATR_TRSL_ADDR_LOW 0x8 +#define XR3PCI_ATR_TRSL_ADDR_HIGH 0xc +#define XR3PCI_ATR_TRSL_PARAM 0x10 +#define XR3PCI_ATR_TABLE_OFFSET 0x20 +#define XR3PCI_ATR_MAX_TABLE_NUM 8 + +#define XR3PCI_ATR_SRC_WIN_SIZE_SHIFT 1 +#define XR3PCI_ATR_SRC_ADDR_MASK GENMASK(31, 12) +#define XR3PCI_ATR_TRSL_ADDR_MASK GENMASK(31, 12) +#define XR3PCI_ECAM_SIZE BIT(28) +#define XR3PCI_ATR_TRSL_DIR BIT(22) +/* IDs used in the XR3PCI_ATR_TRSL_PARAM */ +#define XR3PCI_ATR_TRSLID_PCIE_MEMORY 0x0 +#define XR3PCI_ATR_TRSLID_PCIE_CONFIG 0x1 + +#define INT_AXI_POST_ERROR BIT(16) +#define INT_AXI_FETCH_ERROR BIT(17) +#define INT_AXI_DISCARD_ERROR BIT(18) +#define INT_PCIE_POST_ERROR BIT(20) +#define INT_PCIE_FETCH_ERROR BIT(21) +#define INT_PCIE_DISCARD_ERROR BIT(22) +#define INT_ERRORS (INT_AXI_POST_ERROR | INT_AXI_FETCH_ERROR | \ + INT_AXI_DISCARD_ERROR | INT_PCIE_POST_ERROR | \ + INT_PCIE_FETCH_ERROR | INT_PCIE_DISCARD_ERROR) + +#define INTA_OFFSET 24 +#define INTA BIT(24) +#define INTB BIT(25) +#define INTC BIT(26) +#define INTD BIT(27) +#define INT_MSI BIT(28) +#define INT_INTX_MASK (INTA | INTB | INTC | INTD) +#define INT_MASK (INT_INTX_MASK | INT_MSI | INT_ERRORS) + +#define INT_PCI_MSI_NR 32 + +/* system control */ +#define STG_SYSCON_K_RP_NEP BIT(8) +#define STG_SYSCON_AXI4_SLVL_ARFUNC_MASK GENMASK(22, 8) +#define STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT 8 +#define STG_SYSCON_AXI4_SLVL_AWFUNC_MASK GENMASK(14, 0) +#define STG_SYSCON_CLKREQ BIT(22) +#define STG_SYSCON_CKREF_SRC_SHIFT 18 +#define STG_SYSCON_CKREF_SRC_MASK GENMASK(19, 18) + +/* MSI information */ +struct jh7110_pcie_msi { + DECLARE_BITMAP(used, INT_PCI_MSI_NR); + struct irq_domain *msi_domain; + struct irq_domain *inner_domain; + /* Protect bitmap variable */ + struct mutex lock; +}; + +struct starfive_jh7110_pcie { + struct platform_device *pdev; + void __iomem *reg_base; + void __iomem *config_base; + phys_addr_t config_phyaddr; + struct regmap *reg_syscon; + struct phy *phy; + u32 stg_arfun; + u32 stg_awfun; + u32 stg_rp_nep; + u32 stg_lnksta; + int irq; + struct irq_domain *legacy_irq_domain; + struct pci_host_bridge *bridge; + struct jh7110_pcie_msi msi; + struct reset_control *resets; + struct clk_bulk_data *clks; + int num_clks; + int atr_table_num; + struct gpio_desc *power_gpio; + struct gpio_desc *reset_gpio; +}; + +/* + * StarFive PCIe port uses BAR0-BAR1 of RC's configuration space as + * the translation from PCI bus to native BUS. Entire DDR region + * is mapped into PCIe space using these registers, so it can be + * reached by DMA from EP devices. The BAR0/1 of bridge should be + * hidden during enumeration to avoid the sizing and resource allocation + * by PCIe core. + */ +static bool starfive_pcie_hide_rc_bar(struct pci_bus *bus, unsigned int devfn, + int offset) +{ + if (pci_is_root_bus(bus) && (devfn == 0) + && ((offset == PCI_BASE_ADDRESS_0) + || (offset == PCI_BASE_ADDRESS_1))) + return true; + + return false; +} + +void __iomem *starfive_pcie_map_bus(struct pci_bus *bus, unsigned int devfn, + int where) +{ + struct starfive_jh7110_pcie *pcie = bus->sysdata; + + return pcie->config_base + PCIE_ECAM_OFFSET(bus->number, devfn, where); +} + +int starfive_pcie_config_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 value) +{ + if (starfive_pcie_hide_rc_bar(bus, devfn, where)) + return PCIBIOS_BAD_REGISTER_NUMBER; + + return pci_generic_config_write(bus, devfn, where, size, value); +} + +static void starfive_pcie_handle_msi_irq(struct starfive_jh7110_pcie *pcie) +{ + struct jh7110_pcie_msi *msi = &pcie->msi; + u32 bit; + u32 virq; + unsigned long status = readl(pcie->reg_base + ISTATUS_MSI); + + for_each_set_bit(bit, &status, INT_PCI_MSI_NR) { + /* Clear interrupts */ + writel(1 << bit, pcie->reg_base + ISTATUS_MSI); + virq = irq_find_mapping(msi->inner_domain, bit); + if (virq) { + if (test_bit(bit, msi->used)) + generic_handle_irq(virq); + else + dev_err(&pcie->pdev->dev, + "Unhandled MSI, MSI%d virq %d\n", bit, + virq); + } else + dev_err(&pcie->pdev->dev, "Unexpected MSI, MSI%d\n", + bit); + } + writel(INT_MSI, pcie->reg_base + ISTATUS_LOCAL); +} + +static void starfive_pcie_handle_intx_irq(struct starfive_jh7110_pcie *pcie, + unsigned long status) +{ + u32 bit; + u32 virq; + + status >>= INTA_OFFSET; + + for_each_set_bit(bit, &status, PCI_NUM_INTX) { + /* Clear interrupts */ + writel(1 << (bit + INTA_OFFSET), pcie->reg_base + ISTATUS_LOCAL); + + virq = irq_find_mapping(pcie->legacy_irq_domain, bit); + if (virq) + generic_handle_irq(virq); + else + dev_err(&pcie->pdev->dev, + "unexpected IRQ, INT%d\n", bit); + } +} + +static void starfive_pcie_handle_errors_irq(struct starfive_jh7110_pcie *pcie, u32 status) +{ + if (status & INT_AXI_POST_ERROR) + dev_err(&pcie->pdev->dev, "AXI post error\n"); + if (status & INT_AXI_FETCH_ERROR) + dev_err(&pcie->pdev->dev, "AXI fetch error\n"); + if (status & INT_AXI_DISCARD_ERROR) + dev_err(&pcie->pdev->dev, "AXI discard error\n"); + if (status & INT_PCIE_POST_ERROR) + dev_err(&pcie->pdev->dev, "PCIe post error\n"); + if (status & INT_PCIE_FETCH_ERROR) + dev_err(&pcie->pdev->dev, "PCIe fetch error\n"); + if (status & INT_PCIE_DISCARD_ERROR) + dev_err(&pcie->pdev->dev, "PCIe discard error\n"); + + writel(INT_ERRORS, pcie->reg_base + ISTATUS_LOCAL); +} + +static void starfive_pcie_isr(struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct starfive_jh7110_pcie *pcie; + u32 status; + + chained_irq_enter(chip, desc); + pcie = irq_desc_get_handler_data(desc); + + status = readl(pcie->reg_base + ISTATUS_LOCAL); + while ((status = (readl(pcie->reg_base + ISTATUS_LOCAL) & INT_MASK))) { + if (status & INT_INTX_MASK) + starfive_pcie_handle_intx_irq(pcie, status); + + if (status & INT_MSI) + starfive_pcie_handle_msi_irq(pcie); + + if (status & INT_ERRORS) + starfive_pcie_handle_errors_irq(pcie, status); + } + + chained_irq_exit(chip, desc); +} + +#ifdef CONFIG_PCI_MSI +static struct irq_chip starfive_pcie_msi_irq_chip = { + .name = "StarFive PCIe MSI", + .irq_mask = pci_msi_mask_irq, + .irq_unmask = pci_msi_unmask_irq, +}; + +static struct msi_domain_info starfive_pcie_msi_domain_info = { + .flags = (MSI_FLAG_USE_DEF_DOM_OPS | MSI_FLAG_USE_DEF_CHIP_OPS | + MSI_FLAG_PCI_MSIX), + .chip = &starfive_pcie_msi_irq_chip, +}; +#endif + +static void starfive_pcie_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) +{ + struct starfive_jh7110_pcie *pcie = irq_data_get_irq_chip_data(data); + phys_addr_t msi_addr = readl(pcie->reg_base + IMSI_ADDR); + + msg->address_lo = lower_32_bits(msi_addr); + msg->address_hi = upper_32_bits(msi_addr); + msg->data = data->hwirq; + + dev_info(&pcie->pdev->dev, "msi#%d address_hi %#x address_lo %#x\n", + (int)data->hwirq, msg->address_hi, msg->address_lo); +} + +static int starfive_pcie_msi_set_affinity(struct irq_data *irq_data, + const struct cpumask *mask, bool force) +{ + return -EINVAL; +} + +static struct irq_chip starfive_irq_chip = { + .name = "StarFive MSI", + .irq_compose_msi_msg = starfive_pcie_compose_msi_msg, + .irq_set_affinity = starfive_pcie_msi_set_affinity, +}; + +static int starfive_pcie_msi_alloc(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs, void *args) +{ + struct starfive_jh7110_pcie *pcie = domain->host_data; + struct jh7110_pcie_msi *msi = &pcie->msi; + int bit; + + WARN_ON(nr_irqs != 1); + mutex_lock(&msi->lock); + + bit = find_first_zero_bit(msi->used, INT_PCI_MSI_NR); + if (bit >= INT_PCI_MSI_NR) { + mutex_unlock(&msi->lock); + return -ENOSPC; + } + + set_bit(bit, msi->used); + + irq_domain_set_info(domain, virq, bit, &starfive_irq_chip, + domain->host_data, handle_simple_irq, + NULL, NULL); + mutex_unlock(&msi->lock); + + return 0; +} + +static void starfive_pcie_msi_free(struct irq_domain *domain, unsigned int virq, + unsigned int nr_irqs) +{ + struct irq_data *data = irq_domain_get_irq_data(domain, virq); + struct starfive_jh7110_pcie *pcie = irq_data_get_irq_chip_data(data); + struct jh7110_pcie_msi *msi = &pcie->msi; + + mutex_lock(&msi->lock); + + if (!test_bit(data->hwirq, msi->used)) + dev_err(&pcie->pdev->dev, "Trying to free unused MSI#%lu\n", + data->hwirq); + else + __clear_bit(data->hwirq, msi->used); + + writel(0xffffffff, pcie->reg_base + ISTATUS_MSI); + mutex_unlock(&msi->lock); +} + +static const struct irq_domain_ops dev_msi_domain_ops = { + .alloc = starfive_pcie_msi_alloc, + .free = starfive_pcie_msi_free, +}; + +static void starfive_pcie_msi_free_irq_domain(struct starfive_jh7110_pcie *pcie) +{ +#ifdef CONFIG_PCI_MSI + struct jh7110_pcie_msi *msi = &pcie->msi; + u32 irq; + int i; + + for (i = 0; i < INT_PCI_MSI_NR; i++) { + irq = irq_find_mapping(msi->inner_domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + + if (msi->msi_domain) + irq_domain_remove(msi->msi_domain); + + if (msi->inner_domain) + irq_domain_remove(msi->inner_domain); +#endif +} + +static void starfive_pcie_free_irq_domain(struct starfive_jh7110_pcie *pcie) +{ + int i; + u32 irq; + + /* Disable all interrupts */ + writel(0, pcie->reg_base + IMASK_LOCAL); + + if (pcie->legacy_irq_domain) { + for (i = 0; i < PCI_NUM_INTX; i++) { + irq = irq_find_mapping(pcie->legacy_irq_domain, i); + if (irq > 0) + irq_dispose_mapping(irq); + } + irq_domain_remove(pcie->legacy_irq_domain); + } + + if (pci_msi_enabled()) + starfive_pcie_msi_free_irq_domain(pcie); + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); +} + +static int starfive_pcie_init_msi_irq_domain(struct starfive_jh7110_pcie *pcie) +{ +#ifdef CONFIG_PCI_MSI + struct fwnode_handle *fwn = of_node_to_fwnode(pcie->pdev->dev.of_node); + struct jh7110_pcie_msi *msi = &pcie->msi; + + msi->inner_domain = irq_domain_add_linear(NULL, INT_PCI_MSI_NR, + &dev_msi_domain_ops, pcie); + if (!msi->inner_domain) { + dev_err(&pcie->pdev->dev, "Failed to create dev IRQ domain\n"); + return -ENOMEM; + } + msi->msi_domain = pci_msi_create_irq_domain(fwn, &starfive_pcie_msi_domain_info, + msi->inner_domain); + if (!msi->msi_domain) { + dev_err(&pcie->pdev->dev, "Failed to create msi IRQ domain\n"); + irq_domain_remove(msi->inner_domain); + return -ENOMEM; + } +#endif + return 0; +} + +static int starfive_pcie_enable_msi(struct starfive_jh7110_pcie *pcie, struct pci_bus *bus) +{ + struct jh7110_pcie_msi *msi = &pcie->msi; + u32 reg; + + mutex_init(&msi->lock); + + /* Enable MSI */ + reg = readl(pcie->reg_base + IMASK_LOCAL); + reg |= INT_MSI; + writel(reg, pcie->reg_base + IMASK_LOCAL); + return 0; +} + +static int starfive_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 = starfive_pcie_intx_map, + .xlate = pci_irqd_intx_xlate, +}; + +static int starfive_pcie_init_irq_domain(struct starfive_jh7110_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + struct device_node *node = dev->of_node; + int ret; + + if (pci_msi_enabled()) { + ret = starfive_pcie_init_msi_irq_domain(pcie); + if (ret != 0) + return -ENOMEM; + } + + /* Setup INTx */ + pcie->legacy_irq_domain = irq_domain_add_linear(node, PCI_NUM_INTX, + &intx_domain_ops, pcie); + + if (!pcie->legacy_irq_domain) { + dev_err(dev, "Failed to get a INTx IRQ domain\n"); + return -ENOMEM; + } + + irq_set_chained_handler_and_data(pcie->irq, starfive_pcie_isr, pcie); + + return 0; +} + +static int starfive_pcie_parse_dt(struct starfive_jh7110_pcie *pcie) +{ + struct resource *cfg_res; + struct platform_device *pdev = pcie->pdev; + unsigned int args[4]; + + pcie->reg_base = + devm_platform_ioremap_resource_byname(pdev, "reg"); + + if (IS_ERR(pcie->reg_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(pcie->reg_base), + "Failed to map reg memory\n"); + + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); + if (!cfg_res) + return dev_err_probe(&pdev->dev, -ENODEV, + "Failed to get config memory\n"); + + pcie->config_base = devm_ioremap_resource(&pdev->dev, cfg_res); + if (IS_ERR(pcie->config_base)) + return dev_err_probe(&pdev->dev, PTR_ERR(pcie->config_base), + "Failed to map config memory\n"); + + pcie->config_phyaddr = cfg_res->start; + + pcie->phy = devm_phy_optional_get(&pdev->dev, NULL); + if (IS_ERR(pcie->phy)) + return dev_err_probe(&pdev->dev, PTR_ERR(pcie->phy), + "Failed to get pcie phy\n"); + + pcie->irq = platform_get_irq(pdev, 0); + if (pcie->irq < 0) + return dev_err_probe(&pdev->dev, -EINVAL, + "Failed to get IRQ: %d\n", pcie->irq); + + pcie->reg_syscon = syscon_regmap_lookup_by_phandle_args(pdev->dev.of_node, + "starfive,stg-syscon", 4, args); + + if (IS_ERR(pcie->reg_syscon)) + return dev_err_probe(&pdev->dev, PTR_ERR(pcie->reg_syscon), + "Failed to parse starfive,stg-syscon\n"); + + pcie->stg_arfun = args[0]; + pcie->stg_awfun = args[1]; + pcie->stg_rp_nep = args[2]; + pcie->stg_lnksta = args[3]; + + /* Clear all interrupts */ + writel(0xffffffff, pcie->reg_base + ISTATUS_LOCAL); + writel(INT_INTX_MASK | INT_ERRORS, pcie->reg_base + IMASK_LOCAL); + + return 0; +} + +static struct pci_ops starfive_pcie_ops = { + .map_bus = starfive_pcie_map_bus, + .read = pci_generic_config_read, + .write = starfive_pcie_config_write, +}; + +static void starfive_pcie_set_atr_entry(struct starfive_jh7110_pcie *pcie, + phys_addr_t src_addr, phys_addr_t trsl_addr, + size_t window_size, int trsl_param) +{ + void __iomem *base = + pcie->reg_base + XR3PCI_ATR_AXI4_SLV0; + + /* Support AXI4 Slave 0 Address Translation Tables 0-7. */ + if (pcie->atr_table_num >= XR3PCI_ATR_MAX_TABLE_NUM) + pcie->atr_table_num = XR3PCI_ATR_MAX_TABLE_NUM - 1; + base += XR3PCI_ATR_TABLE_OFFSET * pcie->atr_table_num; + pcie->atr_table_num++; + + /* + * X3PCI_ATR_SRC_ADDR_LOW: + * - bit 0: enable entry, + * - bits 1-6: ATR window size: total size in bytes: 2^(ATR_WSIZE + 1) + * - bits 7-11: reserved + * - bits 12-31: start of source address + */ + writel((lower_32_bits(src_addr) & XR3PCI_ATR_SRC_ADDR_MASK) | + (fls(window_size) - 1) << XR3PCI_ATR_SRC_WIN_SIZE_SHIFT | 1, + base + XR3PCI_ATR_SRC_ADDR_LOW); + writel(upper_32_bits(src_addr), base + XR3PCI_ATR_SRC_ADDR_HIGH); + writel((lower_32_bits(trsl_addr) & XR3PCI_ATR_TRSL_ADDR_MASK), + base + XR3PCI_ATR_TRSL_ADDR_LOW); + writel(upper_32_bits(trsl_addr), base + XR3PCI_ATR_TRSL_ADDR_HIGH); + writel(trsl_param, base + XR3PCI_ATR_TRSL_PARAM); + + dev_info(&pcie->pdev->dev, "ATR entry: 0x%010llx %s 0x%010llx [0x%010llx] (param: 0x%06x)\n", + src_addr, (trsl_param & XR3PCI_ATR_TRSL_DIR) ? "<-" : "->", + trsl_addr, (u64)window_size, trsl_param); +} + +static int starfive_pcie_setup_windows(struct starfive_jh7110_pcie *pcie) +{ + struct pci_host_bridge *bridge = pcie->bridge; + struct resource_entry *entry; + u64 pci_addr; + + resource_list_for_each_entry(entry, &bridge->windows) { + if (resource_type(entry->res) == IORESOURCE_MEM) { + pci_addr = entry->res->start - entry->offset; + starfive_pcie_set_atr_entry(pcie, + entry->res->start, pci_addr, + resource_size(entry->res), + XR3PCI_ATR_TRSLID_PCIE_MEMORY); + } + } + + return 0; +} + +static int starfive_pcie_clk_rst_init(struct starfive_jh7110_pcie *pcie) +{ + int ret; + struct device *dev = &pcie->pdev->dev; + + pcie->num_clks = devm_clk_bulk_get_all(dev, &pcie->clks); + if (pcie->num_clks < 0) + return dev_err_probe(dev, -ENODEV, + "Failed to get pcie clocks\n"); + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) + return dev_err_probe(&pcie->pdev->dev, ret, + "Failed to enable clocks\n"); + + pcie->resets = devm_reset_control_array_get_exclusive(dev); + if (IS_ERR(pcie->resets)) { + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + return dev_err_probe(dev, PTR_ERR(pcie->resets), + "Failed to get pcie resets"); + } + + return reset_control_deassert(pcie->resets); +} + +static void starfive_pcie_clk_rst_deinit(struct starfive_jh7110_pcie *pcie) +{ + reset_control_assert(pcie->resets); + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); +} + +int starfive_pcie_gpio_init(struct starfive_jh7110_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + + pcie->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR_OR_NULL(pcie->reset_gpio)) { + dev_warn(dev, "Failed to get reset-gpio.\n"); + return -EINVAL; + } + + pcie->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW); + if (IS_ERR_OR_NULL(pcie->power_gpio)) + pcie->power_gpio = NULL; + + return 0; +} + +static void starfive_pcie_hw_init(struct starfive_jh7110_pcie *pcie) +{ + unsigned int value; + int i; + + if (pcie->power_gpio) + gpiod_set_value_cansleep(pcie->power_gpio, 1); + + if (pcie->reset_gpio) + gpiod_set_value_cansleep(pcie->reset_gpio, 1); + + /* Disable physical functions except #0 */ + for (i = 1; i < PCIE_FUNC_NUM; i++) { + regmap_update_bits(pcie->reg_syscon, + pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, + (i << PHY_FUNC_SHIFT) << + STG_SYSCON_AXI4_SLVL_ARFUNC_SHIFT); + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, + i << PHY_FUNC_SHIFT); + + value = readl(pcie->reg_base + PCI_MISC); + value |= PHY_FUNCTION_DIS; + writel(value, pcie->reg_base + PCI_MISC); + } + + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_arfun, + STG_SYSCON_AXI4_SLVL_ARFUNC_MASK, + 0); + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_AXI4_SLVL_AWFUNC_MASK, + 0); + + /* Enable root port */ + value = readl(pcie->reg_base + GEN_SETTINGS); + value |= RP_ENABLE; + writel(value, pcie->reg_base + GEN_SETTINGS); + + /* PCIe PCI Standard Configuration Identification Settings. */ + value = (PCI_CLASS_BRIDGE_PCI << IDS_CLASS_CODE_SHIFT); + writel(value, pcie->reg_base + PCIE_PCI_IDS); + + /* + * The LTR message forwarding of PCIe Message Reception was set by core + * as default, but the forward id & addr are also need to be reset. + * If we do not disable LTR message forwarding here, or set a legal + * forwarding address, the kernel will get stuck after this driver probe. + * To workaround, disable the LTR message forwarding support on + * PCIe Message Reception. + */ + value = readl(pcie->reg_base + PMSG_SUPPORT_RX); + value &= ~PMSG_LTR_SUPPORT; + writel(value, pcie->reg_base + PMSG_SUPPORT_RX); + + /* Prefetchable memory window 64-bit addressing support */ + value = readl(pcie->reg_base + PCIE_WINROM); + value |= PREF_MEM_WIN_64_SUPPORT; + writel(value, pcie->reg_base + PCIE_WINROM); + + /* + * As the two host bridges in JH7110 soc have the same default + * address translation table, this cause the second root port can't + * access it's host bridge config space correctly. + * To workaround, config the ATR of host bridge config space by SW. + */ + starfive_pcie_set_atr_entry(pcie, + pcie->config_phyaddr, 0, + XR3PCI_ECAM_SIZE, + XR3PCI_ATR_TRSLID_PCIE_CONFIG); + + starfive_pcie_setup_windows(pcie); + + /* Ensure that PERST has been asserted for at least 100 ms */ + msleep(300); + if (pcie->reset_gpio) + gpiod_set_value_cansleep(pcie->reset_gpio, 0); +} + +static bool starfive_pcie_is_link_up(struct starfive_jh7110_pcie *pcie) +{ + struct device *dev = &pcie->pdev->dev; + int ret; + u32 stg_reg_val; + + /* 100ms timeout value should be enough for Gen1/2 training */ + ret = regmap_read_poll_timeout(pcie->reg_syscon, + pcie->stg_lnksta, + stg_reg_val, + stg_reg_val & DATA_LINK_ACTIVE, + 10 * 1000, 100 * 1000); + + /* If the link is down (no device in slot), then exit. */ + if (ret == -ETIMEDOUT) { + dev_info(dev, "Port link down, exit.\n"); + return false; + } else if (ret == 0) { + dev_info(dev, "Port link up.\n"); + return true; + } + + dev_warn(dev, "Read stg_linksta failed.\n"); + + return false; +} + +static int starfive_pcie_enable_phy(struct device *dev, + struct starfive_jh7110_pcie *pcie) +{ + int ret; + + if (!pcie->phy) + return 0; + + ret = phy_init(pcie->phy); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize pcie phy\n"); + + ret = phy_set_mode(pcie->phy, PHY_MODE_PCIE); + if (ret) { + ret = dev_err_probe(dev, ret, + "failed to set pcie mode\n"); + goto err_phy_on; + } + + ret = phy_power_on(pcie->phy); + if (ret) { + ret = dev_err_probe(dev, ret, "failed to power on pcie phy\n"); + goto err_phy_on; + } + + return 0; + +err_phy_on: + phy_exit(pcie->phy); + return ret; +} + +static void starfive_pcie_disable_phy(struct starfive_jh7110_pcie *pcie) +{ + phy_power_off(pcie->phy); + phy_exit(pcie->phy); +} + +static int starfive_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct starfive_jh7110_pcie *pcie; + struct pci_bus *bus; + struct pci_host_bridge *bridge; + int ret; + + pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); + if (!pcie) + return -ENOMEM; + + pcie->pdev = pdev; + pcie->atr_table_num = 0; + + ret = starfive_pcie_parse_dt(pcie); + if (ret) + return ret; + + platform_set_drvdata(pdev, pcie); + + ret = starfive_pcie_gpio_init(pcie); + if (ret) + return ret; + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_rp_nep, + STG_SYSCON_K_RP_NEP, + STG_SYSCON_K_RP_NEP); + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_CKREF_SRC_MASK, + 2 << STG_SYSCON_CKREF_SRC_SHIFT); + + regmap_update_bits(pcie->reg_syscon, + pcie->stg_awfun, + STG_SYSCON_CLKREQ, + STG_SYSCON_CLKREQ); + + ret = starfive_pcie_clk_rst_init(pcie); + if (ret) + return ret; + + ret = starfive_pcie_init_irq_domain(pcie); + if (ret) + return ret; + + bridge = devm_pci_alloc_host_bridge(dev, 0); + if (!bridge) + return -ENOMEM; + + pm_runtime_enable(&pdev->dev); + pm_runtime_get_sync(&pdev->dev); + + /* Set default bus ops */ + bridge->ops = &starfive_pcie_ops; + bridge->sysdata = pcie; + pcie->bridge = bridge; + + starfive_pcie_hw_init(pcie); + + if (starfive_pcie_is_link_up(pcie) == false) + goto release; + + if (IS_ENABLED(CONFIG_PCI_MSI)) { + ret = starfive_pcie_enable_msi(pcie, bus); + if (ret < 0) { + dev_err(dev, "Failed to enable MSI support: %d\n", ret); + goto release; + } + } + + ret = starfive_pcie_enable_phy(dev, pcie); + if (ret) + goto release; + + ret = pci_host_probe(bridge); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to pci host probe: %d\n", ret); + goto err_phy_on; + } + + return ret; + +err_phy_on: + starfive_pcie_disable_phy(pcie); +release: + if (pcie->power_gpio) + gpiod_set_value_cansleep(pcie->power_gpio, 0); + + starfive_pcie_clk_rst_deinit(pcie); + + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + + pci_free_host_bridge(pcie->bridge); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int starfive_pcie_remove(struct platform_device *pdev) +{ + struct starfive_jh7110_pcie *pcie = platform_get_drvdata(pdev); + + starfive_pcie_disable_phy(pcie); + if (pcie->power_gpio) + gpiod_set_value_cansleep(pcie->power_gpio, 0); + starfive_pcie_free_irq_domain(pcie); + starfive_pcie_clk_rst_deinit(pcie); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int __maybe_unused starfive_pcie_suspend_noirq(struct device *dev) +{ + struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); + + if (!pcie) + return 0; + + starfive_pcie_disable_phy(pcie); + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + + return 0; +} + +static int __maybe_unused starfive_pcie_resume_noirq(struct device *dev) +{ + struct starfive_jh7110_pcie *pcie = dev_get_drvdata(dev); + int ret; + + if (!pcie) + return 0; + + ret = clk_bulk_prepare_enable(pcie->num_clks, pcie->clks); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable clocks\n"); + + ret = starfive_pcie_enable_phy(dev, pcie); + if (ret) + clk_bulk_disable_unprepare(pcie->num_clks, pcie->clks); + + return ret; +} + +static const struct dev_pm_ops starfive_pcie_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(starfive_pcie_suspend_noirq, + starfive_pcie_resume_noirq) +}; +#endif + +static const struct of_device_id starfive_pcie_of_match[] = { + { .compatible = "starfive,jh7110-pcie"}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, starfive_pcie_of_match); + +static struct platform_driver starfive_pcie_driver = { + .driver = { + .name = "pcie-starfive", + .of_match_table = of_match_ptr(starfive_pcie_of_match), +#ifdef CONFIG_PM_SLEEP + .pm = &starfive_pcie_pm_ops, +#endif + }, + .probe = starfive_pcie_probe, + .remove = starfive_pcie_remove, +}; +module_platform_driver(starfive_pcie_driver); + +MODULE_DESCRIPTION("StarFive JH7110 PCIe host driver"); +MODULE_AUTHOR("Mason Huo "); +MODULE_AUTHOR("Kevin Xie "); +MODULE_AUTHOR("Minda Chen "); +MODULE_LICENSE("GPL v2"); -- 2.7.4 From ed348a0be1d707f83bfa31455be0543c947da1a8 Mon Sep 17 00:00:00 2001 From: Hal Feng Date: Tue, 11 Apr 2023 16:31:15 +0800 Subject: [PATCH 14/16] riscv: dts: starfive: Add full support for JH7110 and VisionFive 2 board Merge all StarFive dts patches together. Signed-off-by: Hal Feng Conflicts: arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi arch/riscv/boot/dts/starfive/jh7110.dtsi [Backported from https://github.com/starfive-tech/linux/tree/JH7110_VisionFive2_upstream] Change-Id: I0b498d1c7694877a210026717bd1c4e8fffac22a Signed-off-by: Hoegeun Kwon --- .../dts/starfive/jh7110-starfive-visionfive-2.dtsi | 58 ++++++++++++++ arch/riscv/boot/dts/starfive/jh7110.dtsi | 88 ++++++++++++++++++++++ 2 files changed, 146 insertions(+) diff --git a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi index 752bb0b..63eecf3 100644 --- a/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi +++ b/arch/riscv/boot/dts/starfive/jh7110-starfive-visionfive-2.dtsi @@ -219,6 +219,20 @@ status = "okay"; }; +&pcie0 { + pinctrl-names = "default"; + reset-gpios = <&sysgpio 26 GPIO_ACTIVE_LOW>; + phys = <&pciephy0>; + status = "okay"; +}; + +&pcie1 { + pinctrl-names = "default"; + reset-gpios = <&sysgpio 28 GPIO_ACTIVE_LOW>; + phys = <&pciephy1>; + status = "okay"; +}; + &ptc { pinctrl-names = "default"; pinctrl-0 = <&pwm_pins>; @@ -282,6 +296,50 @@ }; }; + pcie0_wake_default: pcie0_wake_default { + wake-pins { + pinmux = ; + bias-disable; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pcie0_clkreq_default: pcie0_clkreq_default { + clkreq-pins { + bias-disable; + pinmux = ; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pcie1_wake_default: pcie1_wake_default { + wake-pins { + bias-disable; + pinmux = ; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + + pcie1_clkreq_default: pcie1_clkreq_default { + clkreq-pins { + bias-disable; + pinmux = ; + drive-strength = <2>; + input-enable; + input-schmitt-disable; + slew-rate = <0>; + }; + }; + pwm_pins: pwm-0 { pwm-pins { pinmux = ; power-domains = <&pwrc JH7110_PD_VOUT>; }; + + pcie0: pcie@2B000000 { + compatible = "starfive,jh7110-pcie"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x2B000000 0x0 0x1000000 + 0x9 0x40000000 0x0 0x10000000>; + reg-names = "reg", "config"; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon 0xc0 0xc4 0x130 0x1b8>; + bus-range = <0x0 0xff>; + ranges = <0x82000000 0x0 0x30000000 0x0 0x30000000 0x0 0x08000000>, + <0xc3000000 0x9 0x00000000 0x9 0x00000000 0x0 0x40000000>; + interrupts = <56>; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc0 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc0 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc0 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc0 0x4>; + msi-parent = <&pcie0>; + msi-controller; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE0_TL>, + <&stgcrg JH7110_STGCLK_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE0_APB>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg JH7110_STGRST_PCIE0_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE0_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE0_BRG>, + <&stgcrg JH7110_STGRST_PCIE0_CORE>, + <&stgcrg JH7110_STGRST_PCIE0_APB>; + reset-names = "mst0", "slv0", "slv", "brg", + "core", "apb"; + status = "disabled"; + + pcie_intc0: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; + + pcie1: pcie@2C000000 { + compatible = "starfive,jh7110-pcie"; + #address-cells = <3>; + #size-cells = <2>; + #interrupt-cells = <1>; + reg = <0x0 0x2C000000 0x0 0x1000000 + 0x9 0xc0000000 0x0 0x10000000>; + reg-names = "reg", "config"; + device_type = "pci"; + starfive,stg-syscon = <&stg_syscon 0x270 0x274 0x2e0 0x368>; + bus-range = <0x0 0xff>; + ranges = <0x82000000 0x0 0x38000000 0x0 0x38000000 0x0 0x08000000>, + <0xc3000000 0x9 0x80000000 0x9 0x80000000 0x0 0x40000000>; + interrupts = <57>; + interrupt-parent = <&plic>; + interrupt-map-mask = <0x0 0x0 0x0 0x7>; + interrupt-map = <0x0 0x0 0x0 0x1 &pcie_intc1 0x1>, + <0x0 0x0 0x0 0x2 &pcie_intc1 0x2>, + <0x0 0x0 0x0 0x3 &pcie_intc1 0x3>, + <0x0 0x0 0x0 0x4 &pcie_intc1 0x4>; + msi-parent = <&pcie1>; + msi-controller; + clocks = <&syscrg JH7110_SYSCLK_NOC_BUS_STG_AXI>, + <&stgcrg JH7110_STGCLK_PCIE1_TL>, + <&stgcrg JH7110_STGCLK_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGCLK_PCIE1_APB>; + clock-names = "noc", "tl", "axi_mst0", "apb"; + resets = <&stgcrg JH7110_STGRST_PCIE1_AXI_MST0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV0>, + <&stgcrg JH7110_STGRST_PCIE1_AXI_SLV>, + <&stgcrg JH7110_STGRST_PCIE1_BRG>, + <&stgcrg JH7110_STGRST_PCIE1_CORE>, + <&stgcrg JH7110_STGRST_PCIE1_APB>; + reset-names = "mst0", "slv0", "slv", "brg", + "core", "apb"; + status = "disabled"; + + pcie_intc1: interrupt-controller { + #address-cells = <0>; + #interrupt-cells = <1>; + interrupt-controller; + }; + }; }; }; -- 2.7.4 From 3a0de1d9b0e72d4cbcbae1f4bf93e1c78df32d3b Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Mon, 17 Apr 2023 15:46:56 +0900 Subject: [PATCH 15/16] RISCV: configs: Enable pcie defconfig Enable CONFIG_PCIE_STARFIVE defconfig for pcie host controller driver. Change-Id: Ie55481519cd03f9a7bf2679c0ca6a0f5703ceb21 Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index 87f1f64..8c3fb28 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -114,6 +114,7 @@ CONFIG_PCI=y CONFIG_PCIEPORTBUS=y CONFIG_PCI_HOST_GENERIC=y CONFIG_PCIE_XILINX=y +CONFIG_PCIE_STARFIVE=y CONFIG_PCIE_FU740=y CONFIG_DEVTMPFS=y CONFIG_DEVTMPFS_MOUNT=y -- 2.7.4 From 5444611a92ee40befa2ff00262db11212d0323af Mon Sep 17 00:00:00 2001 From: Hoegeun Kwon Date: Tue, 25 Apr 2023 15:50:03 +0900 Subject: [PATCH 16/16] RISCV: configs: Enable power reset gpio When rebooting, there is a problem that the pmic gpio is not set, so rebooting is not possible. Enable power reset gpio. Change-Id: I59c35303fe9416b4839d244e161776a034259a4b Signed-off-by: Hoegeun Kwon --- arch/riscv/configs/tizen_vf2_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/riscv/configs/tizen_vf2_defconfig b/arch/riscv/configs/tizen_vf2_defconfig index 8c3fb28..fb94d93 100644 --- a/arch/riscv/configs/tizen_vf2_defconfig +++ b/arch/riscv/configs/tizen_vf2_defconfig @@ -168,6 +168,7 @@ CONFIG_SPI=y CONFIG_SPI_SIFIVE=y # CONFIG_PTP_1588_CLOCK is not set CONFIG_GPIO_SIFIVE=y +CONFIG_POWER_RESET_GPIO_RESTART=y CONFIG_WATCHDOG=y # CONFIG_MEDIA_CEC_SUPPORT is not set CONFIG_MEDIA_SUPPORT=y -- 2.7.4