1499 lines
43 KiB
Diff
1499 lines
43 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Marvin Wewer <mwewer37@proton.me>
|
|
Date: Thu, 30 Oct 2025 22:33:03 +0000
|
|
Subject: PCIe: Add support for Allwinner SUN55I DesignWare PCIe controller
|
|
|
|
Signed-off-by: Marvin Wewer <mwewer37@proton.me>
|
|
---
|
|
drivers/pci/Kconfig | 8 +
|
|
drivers/pci/Makefile | 1 +
|
|
drivers/pci/pcie-sun55i-plat.c | 444 +++++++++
|
|
drivers/pci/pcie-sun55i.c | 498 ++++++++++
|
|
drivers/pci/pcie-sun55i.h | 490 +++++++++
|
|
5 files changed, 1441 insertions(+)
|
|
|
|
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/pci/Kconfig
|
|
+++ b/drivers/pci/Kconfig
|
|
@@ -462,6 +462,14 @@ config PCIE_DW_IMX
|
|
select SYSCON
|
|
help
|
|
Say Y here if you want to enable DW PCIe controller support on
|
|
iMX SoCs.
|
|
|
|
+
|
|
+config PCIE_SUN55I_RC
|
|
+ bool "Allwinner SUN55I DesignWare PCIe controller"
|
|
+ default n
|
|
+ depends on ARCH_SUNXI
|
|
+ help
|
|
+ Enables support for the DW PCIe controller in the Allwinner Sun55i SoC.
|
|
+
|
|
endif
|
|
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/pci/Makefile
|
|
+++ b/drivers/pci/Makefile
|
|
@@ -55,5 +55,6 @@ obj-$(CONFIG_PCIE_DW_SIFIVE) += pcie_dw_sifive.o
|
|
obj-$(CONFIG_PCIE_UNIPHIER) += pcie_uniphier.o
|
|
obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
|
|
obj-$(CONFIG_PCIE_PLDA_COMMON) += pcie_plda_common.o
|
|
obj-$(CONFIG_PCIE_STARFIVE_JH7110) += pcie_starfive_jh7110.o
|
|
obj-$(CONFIG_PCIE_DW_IMX) += pcie_dw_imx.o
|
|
+obj-$(CONFIG_PCIE_SUN55I_RC) += pcie-sun55i-plat.o pcie-sun55i.o
|
|
\ No newline at end of file
|
|
diff --git a/drivers/pci/pcie-sun55i-plat.c b/drivers/pci/pcie-sun55i-plat.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/pci/pcie-sun55i-plat.c
|
|
@@ -0,0 +1,444 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Allwinner PCI Express plat driver
|
|
+ *
|
|
+ * Copyright(c) 2020 - 2024 Allwinner Technology Co.,Ltd. All rights reserved.
|
|
+ *
|
|
+ * pcie-sun55i-plat.c: chenhuaqiang <chenhuaqiang@allwinnertech.com>
|
|
+ */
|
|
+
|
|
+
|
|
+#include "pci.h"
|
|
+#include "pcie-sun55i.h"
|
|
+#include <linux/types.h>
|
|
+#include <linux/delay.h>
|
|
+#include <asm/arch/clock.h>
|
|
+#include <power/regulator.h>
|
|
+#include <dm.h>
|
|
+
|
|
+/* Indexed by PCI_EXP_LNKCAP_SLS, PCI_EXP_LNKSTA_CLS */
|
|
+const unsigned char pcie_link_speed[] = {
|
|
+ PCI_SPEED_UNKNOWN, /* 0 */
|
|
+ PCIE_SPEED_2_5GT, /* 1 */
|
|
+ PCIE_SPEED_5_0GT, /* 2 */
|
|
+ PCIE_SPEED_8_0GT, /* 3 */
|
|
+ PCIE_SPEED_16_0GT, /* 4 */
|
|
+ PCIE_SPEED_32_0GT, /* 5 */
|
|
+};
|
|
+
|
|
+int sun55i_pcie_cfg_write(void __iomem *addr, int size, ulong val)
|
|
+{
|
|
+ if ((uintptr_t)addr & (size - 1))
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+
|
|
+ if (size == 4)
|
|
+ writel(val, addr);
|
|
+ else if (size == 2)
|
|
+ writew(val, addr);
|
|
+ else if (size == 1)
|
|
+ writeb(val, addr);
|
|
+ else
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+}
|
|
+
|
|
+int sun55i_pcie_cfg_read(void __iomem *addr, int size, ulong *val)
|
|
+{
|
|
+
|
|
+ if ((uintptr_t)addr & (size - 1)) {
|
|
+ *val = 0;
|
|
+ printf("CFG_READ: Bad alignment\n");
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+ }
|
|
+
|
|
+ if (size == 4) {
|
|
+ *val = readl(addr);
|
|
+ } else if (size == 2) {
|
|
+ *val = readw(addr);
|
|
+ } else if (size == 1) {
|
|
+ *val = readb(addr);
|
|
+ } else {
|
|
+ *val = 0;
|
|
+ printf("CFG_READ: Bad size\n");
|
|
+ return PCIBIOS_BAD_REGISTER_NUMBER;
|
|
+ }
|
|
+
|
|
+ return PCIBIOS_SUCCESSFUL;
|
|
+}
|
|
+
|
|
+void sun55i_pcie_writel(u32 val, struct sun55i_pcie *pcie, u32 offset)
|
|
+{
|
|
+ writel(val, pcie->app_base + offset);
|
|
+}
|
|
+
|
|
+u32 sun55i_pcie_readl(struct sun55i_pcie *pcie, u32 offset)
|
|
+{
|
|
+ return readl(pcie->app_base + offset);
|
|
+}
|
|
+
|
|
+static void sun55i_pcie_write_dbi(struct sun55i_pcie *pci, u32 reg, size_t size, u32 val)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = sun55i_pcie_cfg_write(pci->dbi_base + reg, size, val);
|
|
+ if (ret)
|
|
+ printf("Write DBI address failed\n");
|
|
+}
|
|
+
|
|
+static ulong sun55i_pcie_read_dbi(struct sun55i_pcie *pci, u32 reg, size_t size)
|
|
+{
|
|
+ int ret;
|
|
+ ulong val;
|
|
+
|
|
+ ret = sun55i_pcie_cfg_read(pci->dbi_base + reg, size, &val);
|
|
+ if (ret)
|
|
+ printf("Read DBI address failed\n");
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+void sun55i_pcie_writel_dbi(struct sun55i_pcie *pci, u32 reg, u32 val)
|
|
+{
|
|
+ sun55i_pcie_write_dbi(pci, reg, 0x4, val);
|
|
+}
|
|
+
|
|
+u32 sun55i_pcie_readl_dbi(struct sun55i_pcie *pci, u32 reg)
|
|
+{
|
|
+ return sun55i_pcie_read_dbi(pci, reg, 0x4);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_writew_dbi(struct sun55i_pcie *pci, u32 reg, u16 val)
|
|
+{
|
|
+ sun55i_pcie_write_dbi(pci, reg, 0x2, val);
|
|
+}
|
|
+
|
|
+u16 sun55i_pcie_readw_dbi(struct sun55i_pcie *pci, u32 reg)
|
|
+{
|
|
+ return sun55i_pcie_read_dbi(pci, reg, 0x2);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_writeb_dbi(struct sun55i_pcie *pci, u32 reg, u8 val)
|
|
+{
|
|
+ sun55i_pcie_write_dbi(pci, reg, 0x1, val);
|
|
+}
|
|
+
|
|
+u8 sun55i_pcie_readb_dbi(struct sun55i_pcie *pci, u32 reg)
|
|
+{
|
|
+ return sun55i_pcie_read_dbi(pci, reg, 0x1);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_dbi_ro_wr_en(struct sun55i_pcie *pci)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_MISC_CONTROL_1_CFG);
|
|
+ val |= (0x1 << 0);
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_MISC_CONTROL_1_CFG, val);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_dbi_ro_wr_dis(struct sun55i_pcie *pci)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_MISC_CONTROL_1_CFG);
|
|
+ val &= ~(0x1 << 0);
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_MISC_CONTROL_1_CFG, val);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_plat_ltssm_enable(struct sun55i_pcie *pcie)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = sun55i_pcie_readl(pcie, PCIE_LTSSM_CTRL);
|
|
+ val |= PCIE_LINK_TRAINING;
|
|
+ sun55i_pcie_writel(val, pcie, PCIE_LTSSM_CTRL);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_plat_ltssm_disable(struct sun55i_pcie *pcie)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ val = sun55i_pcie_readl(pcie, PCIE_LTSSM_CTRL);
|
|
+ val &= ~PCIE_LINK_TRAINING;
|
|
+ sun55i_pcie_writel(val, pcie, PCIE_LTSSM_CTRL);
|
|
+}
|
|
+
|
|
+static u8 __sun55i_pcie_find_next_cap(struct sun55i_pcie *pci, u8 cap_ptr,
|
|
+ u8 cap)
|
|
+{
|
|
+ u8 cap_id, next_cap_ptr;
|
|
+ u16 reg;
|
|
+
|
|
+ if (!cap_ptr)
|
|
+ return 0;
|
|
+
|
|
+ reg = sun55i_pcie_readw_dbi(pci, cap_ptr);
|
|
+ cap_id = (reg & CAP_ID_MASK);
|
|
+
|
|
+ if (cap_id > PCI_CAP_ID_MAX)
|
|
+ return 0;
|
|
+
|
|
+ if (cap_id == cap)
|
|
+ return cap_ptr;
|
|
+
|
|
+ next_cap_ptr = (reg & NEXT_CAP_PTR_MASK) >> 8;
|
|
+ return __sun55i_pcie_find_next_cap(pci, next_cap_ptr, cap);
|
|
+}
|
|
+
|
|
+u8 sun55i_pcie_plat_find_capability(struct sun55i_pcie *pci, u8 cap)
|
|
+{
|
|
+ u8 next_cap_ptr;
|
|
+ u16 reg;
|
|
+
|
|
+ reg = sun55i_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST);
|
|
+ next_cap_ptr = (reg & CAP_ID_MASK);
|
|
+
|
|
+ return __sun55i_pcie_find_next_cap(pci, next_cap_ptr, cap);
|
|
+}
|
|
+
|
|
+static void sun55i_pcie_plat_set_link_cap(struct sun55i_pcie *pci, u32 link_gen)
|
|
+{
|
|
+ u32 cap, ctrl2, link_speed = 0;
|
|
+
|
|
+ u8 offset = sun55i_pcie_plat_find_capability(pci, PCI_CAP_ID_EXP);
|
|
+
|
|
+ cap = sun55i_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCAP);
|
|
+ ctrl2 = sun55i_pcie_readl_dbi(pci, offset + PCI_EXP_LNKCTL2);
|
|
+ ctrl2 &= ~PCI_EXP_LNKCTL2_TLS;
|
|
+
|
|
+ switch (pcie_link_speed[link_gen]) {
|
|
+ case PCIE_SPEED_2_5GT:
|
|
+ link_speed = PCI_EXP_LNKCTL2_TLS_2_5GT;
|
|
+ break;
|
|
+ case PCIE_SPEED_5_0GT:
|
|
+ link_speed = PCI_EXP_LNKCTL2_TLS_5_0GT;
|
|
+ break;
|
|
+ case PCIE_SPEED_8_0GT:
|
|
+ link_speed = PCI_EXP_LNKCTL2_TLS_8_0GT;
|
|
+ break;
|
|
+ case PCIE_SPEED_16_0GT:
|
|
+ link_speed = PCI_EXP_LNKCTL2_TLS_16_0GT;
|
|
+ break;
|
|
+ default:
|
|
+ /* Use hardware capability */
|
|
+ // link_speed = FIELD_GET(PCI_EXP_LNKCAP_SLS, cap);
|
|
+ // ctrl2 &= ~PCI_EXP_LNKCTL2_HASD;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ sun55i_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCTL2, ctrl2 | link_speed);
|
|
+
|
|
+ cap &= ~((u32)PCI_EXP_LNKCAP_SLS);
|
|
+ sun55i_pcie_writel_dbi(pci, offset + PCI_EXP_LNKCAP, cap | link_speed);
|
|
+}
|
|
+
|
|
+void sun55i_pcie_plat_set_rate(struct sun55i_pcie *pci)
|
|
+{
|
|
+ u32 val;
|
|
+
|
|
+ sun55i_pcie_plat_set_link_cap(pci, pci->link_gen);
|
|
+ /* set the number of lanes */
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_PORT_LINK_CONTROL);
|
|
+ val &= ~PORT_LINK_MODE_MASK;
|
|
+ switch (pci->lanes) {
|
|
+ case 1:
|
|
+ val |= PORT_LINK_MODE_1_LANES;
|
|
+ break;
|
|
+ case 2:
|
|
+ val |= PORT_LINK_MODE_2_LANES;
|
|
+ break;
|
|
+ case 4:
|
|
+ val |= PORT_LINK_MODE_4_LANES;
|
|
+ break;
|
|
+ default:
|
|
+ printf("num-lanes %u: invalid value\n", pci->lanes);
|
|
+ return;
|
|
+ }
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_PORT_LINK_CONTROL, val);
|
|
+
|
|
+ /* set link width speed control register */
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
|
+ val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
|
|
+ switch (pci->lanes) {
|
|
+ case 1:
|
|
+ val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
|
|
+ break;
|
|
+ case 2:
|
|
+ val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
|
|
+ break;
|
|
+ case 4:
|
|
+ val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
|
|
+ break;
|
|
+ }
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_plat_init_port(struct udevice *dev)
|
|
+{
|
|
+ struct sun55i_pcie *pci = dev_get_priv(dev);
|
|
+ int ret;
|
|
+
|
|
+ if (dm_gpio_is_valid(&pci->wake_gpio)) {
|
|
+ ret = dm_gpio_set_value(&pci->wake_gpio, 1);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to set wake GPIO: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = dm_gpio_set_value(&pci->switch_gpio, 1);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to set switch GPIO: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = regulator_set_enable(pci->slot_3v3, true);
|
|
+ if (ret && ret != -EALREADY) {
|
|
+ printf("PCIe: Failed to enable 3.3V slot supply: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ mdelay(50);
|
|
+
|
|
+ ret = clk_enable(&pci->pcie_aux);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to enable bus clock: %d\n", ret);
|
|
+ goto err_disable_slot_supply;
|
|
+ }
|
|
+
|
|
+ if (pci->drvdata && pci->drvdata->need_pcie_rst) {
|
|
+ ret = reset_deassert(&pci->pcie_rst);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to deassert internal reset: %d\n", ret);
|
|
+ goto err_disable_clk;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = generic_phy_init(&pci->phy);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to init phy: %d\n", ret);
|
|
+ goto err_assert_reset;
|
|
+ }
|
|
+ ret = generic_phy_power_on(&pci->phy);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to power on phy: %d\n", ret);
|
|
+ goto err_assert_reset;
|
|
+ }
|
|
+
|
|
+ printf("PCIe: Toggling external device reset (PERST#)...\n");
|
|
+ ret = dm_gpio_set_value(&pci->rst_gpio, 0);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to assert external reset: %d\n", ret);
|
|
+ goto err_power_off_phy;
|
|
+ }
|
|
+
|
|
+ mdelay(100);
|
|
+
|
|
+ ret = dm_gpio_set_value(&pci->rst_gpio, 1);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to deassert external reset: %d\n", ret);
|
|
+ goto err_power_off_phy;
|
|
+ }
|
|
+
|
|
+ mdelay(40);
|
|
+
|
|
+ printf("PCIe: Hardware power-on sequence successful.\n");
|
|
+ return 0;
|
|
+
|
|
+err_power_off_phy:
|
|
+ generic_phy_power_off(&pci->phy);
|
|
+err_assert_reset:
|
|
+ if (pci->drvdata && pci->drvdata->need_pcie_rst)
|
|
+ reset_assert(&pci->pcie_rst);
|
|
+err_disable_clk:
|
|
+ clk_disable(&pci->pcie_aux);
|
|
+err_disable_slot_supply:
|
|
+ regulator_set_enable(pci->slot_3v3, false);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+int sun55i_pcie_plat_hw_init(struct udevice *dev)
|
|
+{
|
|
+ struct sun55i_pcie *pci = dev_get_priv(dev);
|
|
+ int ret;
|
|
+
|
|
+ printf("PCIe: Acquiring resources...\n");
|
|
+
|
|
+ ret = dev_read_u32(dev, "num-lanes", &pci->lanes);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to parse num-lanes, using default: 1\n");
|
|
+ pci->lanes = 1;
|
|
+ }
|
|
+
|
|
+ ret = dev_read_u32(dev, "max-link-speed", &pci->link_gen);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Couldn't parse max-link-speed, using default link speed: Gen2\n");
|
|
+ pci->link_gen = 2;
|
|
+ }
|
|
+
|
|
+ if (pci->lanes != 1 && pci->lanes != 2 && pci->lanes != 4) {
|
|
+ printf("PCIe: Invalid num-lanes %d, using 1\n", pci->lanes);
|
|
+ pci->lanes = 1;
|
|
+ }
|
|
+
|
|
+ if (pci->link_gen < 1 || pci->link_gen > 3) {
|
|
+ printf("PCIe: Invalid max-link-speed %d, using 2\n", pci->link_gen);
|
|
+ pci->link_gen = 2;
|
|
+ }
|
|
+
|
|
+ ret = gpio_request_by_name(dev, "switch-sel-gpios", 0, &pci->switch_gpio, GPIOD_IS_OUT);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get switch-sel GPIO: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = gpio_request_by_name(dev, "reset-gpios", 0, &pci->rst_gpio,
|
|
+ GPIOD_IS_OUT);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get reset-gpios: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = gpio_request_by_name(dev, "wake-gpios", 0, &pci->wake_gpio,
|
|
+ GPIOD_IS_OUT);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Warning: Failed to get wake-gpios: %d\n", ret);
|
|
+ }
|
|
+
|
|
+ ret = device_get_supply_regulator(dev, "slot-3v3-supply", &pci->slot_3v3);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get 3.3V slot supply: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = clk_get_by_name(dev, "pclk_aux", &pci->pcie_aux);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get bus clock: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pci->drvdata = (const struct sun55i_pcie_of_data *)dev_get_driver_data(dev);
|
|
+ if (pci->drvdata && pci->drvdata->need_pcie_rst) {
|
|
+ ret = reset_get_by_index(dev, 0, &pci->pcie_rst);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get reset controller: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = generic_phy_get_by_index(dev, 0, &pci->phy);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Failed to get phy: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ printf("PCIe: All resources acquired. Starting power-on sequence...\n");
|
|
+
|
|
+ return sun55i_pcie_plat_init_port(dev);
|
|
+
|
|
+}
|
|
+
|
|
+
|
|
+
|
|
diff --git a/drivers/pci/pcie-sun55i.c b/drivers/pci/pcie-sun55i.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/pci/pcie-sun55i.c
|
|
@@ -0,0 +1,498 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+/*
|
|
+ * sun55i DesignWare based PCIe host controller driver
|
|
+ *
|
|
+ * Copyright (c) 2021 sun55i, Inc.
|
|
+ */
|
|
+
|
|
+
|
|
+#include <dm.h>
|
|
+#include <syscon.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/arch-sunxi/clock.h>
|
|
+#include <linux/iopoll.h>
|
|
+#include <linux/ioport.h>
|
|
+#include "pcie-sun55i.h"
|
|
+
|
|
+DECLARE_GLOBAL_DATA_PTR;
|
|
+
|
|
+#define sun55i_pcie_DBG 0
|
|
+
|
|
+#define __pcie_dev_print_emit(fmt, ...) \
|
|
+({ \
|
|
+ printf(fmt, ##__VA_ARGS__); \
|
|
+})
|
|
+
|
|
+#ifdef dev_err
|
|
+#undef dev_err
|
|
+#define dev_err(dev, fmt, ...) \
|
|
+({ \
|
|
+ if (dev) \
|
|
+ __pcie_dev_print_emit("%s: " fmt, dev->name, \
|
|
+ ##__VA_ARGS__); \
|
|
+})
|
|
+#endif
|
|
+
|
|
+#ifdef dev_info
|
|
+#undef dev_info
|
|
+#define dev_info dev_err
|
|
+#endif
|
|
+
|
|
+#ifdef DEBUG
|
|
+#define dev_dbg dev_err
|
|
+#else
|
|
+#define dev_dbg(dev, fmt, ...) \
|
|
+({ \
|
|
+ if (0) \
|
|
+ __dev_printk(7, dev, fmt, ##__VA_ARGS__); \
|
|
+})
|
|
+#endif
|
|
+
|
|
+
|
|
+
|
|
+static int sun55i_pcie_addr_valid(pci_dev_t d, int first_busno)
|
|
+{
|
|
+ if ((PCI_BUS(d) == first_busno) && (PCI_DEV(d) > 0))
|
|
+ return 0;
|
|
+ if ((PCI_BUS(d) == first_busno + 1) && (PCI_DEV(d) > 0))
|
|
+ return 0;
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static void sun55i_pcie_prog_outbound_atu(struct sun55i_pcie_port *pp, int index, int type,
|
|
+ u64 cpu_addr, u64 pci_addr, u32 size)
|
|
+{
|
|
+ struct sun55i_pcie *pci = to_sun55i_pcie_from_pp(pp);
|
|
+
|
|
+
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_LOWER_BASE_OUTBOUND(index), lower_32_bits(cpu_addr));
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_UPPER_BASE_OUTBOUND(index), upper_32_bits(cpu_addr));
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_LIMIT_OUTBOUND(index), lower_32_bits(cpu_addr + size - 1));
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_LOWER_TARGET_OUTBOUND(index), lower_32_bits(pci_addr));
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_UPPER_TARGET_OUTBOUND(index), upper_32_bits(pci_addr));
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_CR1_OUTBOUND(index), type);
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_ATU_CR2_OUTBOUND(index), PCIE_ATU_ENABLE);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_rd_other_conf(struct sun55i_pcie_port *pp, pci_dev_t d, int where, int size, ulong *val)
|
|
+{
|
|
+ int ret = PCIBIOS_SUCCESSFUL, type;
|
|
+ u64 busdev;
|
|
+ u64 atu_cpu_addr = pp->cfg0_base;
|
|
+
|
|
+ if (pp->cpu_pcie_addr_quirk)
|
|
+ atu_cpu_addr -= PCIE_CPU_BASE;
|
|
+
|
|
+ busdev = PCIE_ATU_BUS(PCI_BUS(d)) | PCIE_ATU_DEV(PCI_DEV(d)) | PCIE_ATU_FUNC(PCI_FUNC(d));
|
|
+
|
|
+ if (PCI_BUS(d) != 0)
|
|
+ type = PCIE_ATU_TYPE_CFG0;
|
|
+ else
|
|
+ type = PCIE_ATU_TYPE_CFG1;
|
|
+
|
|
+ sun55i_pcie_prog_outbound_atu(pp, PCIE_ATU_INDEX0, type, atu_cpu_addr, busdev, pp->cfg0_size);
|
|
+
|
|
+ ret = sun55i_pcie_cfg_read(pp->va_cfg0_base + where, size, val);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_wr_other_conf(struct sun55i_pcie_port *pp, pci_dev_t d, int where, int size, ulong val)
|
|
+{
|
|
+ int ret = PCIBIOS_SUCCESSFUL, type;
|
|
+ u64 busdev;
|
|
+ u64 atu_cpu_addr = pp->cfg0_base;
|
|
+
|
|
+ if (pp->cpu_pcie_addr_quirk)
|
|
+ atu_cpu_addr -= PCIE_CPU_BASE;
|
|
+
|
|
+ busdev = PCIE_ATU_BUS(PCI_BUS(d)) | PCIE_ATU_DEV(PCI_DEV(d)) | PCIE_ATU_FUNC(PCI_FUNC(d));
|
|
+
|
|
+ if (PCI_BUS(d) != 0)
|
|
+ type = PCIE_ATU_TYPE_CFG0;
|
|
+ else
|
|
+ type = PCIE_ATU_TYPE_CFG1;
|
|
+
|
|
+ sun55i_pcie_prog_outbound_atu(pp, PCIE_ATU_INDEX0, type, atu_cpu_addr, busdev, pp->cfg0_size);
|
|
+
|
|
+ ret = sun55i_pcie_cfg_write(pp->va_cfg0_base + where, size, val);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_rd_own_conf(struct sun55i_pcie_port *pp, int where, int size, ulong *val)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = sun55i_pcie_cfg_read(pp->dbi_base + where, size, val);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_wr_own_conf(struct sun55i_pcie_port *pp, int where, int size, ulong val)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ret = sun55i_pcie_cfg_write(pp->dbi_base + where, size, val);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_read_config(const struct udevice *bus, pci_dev_t bdf,
|
|
+ uint offset, ulong *value,
|
|
+ enum pci_size_t size)
|
|
+{
|
|
+ struct sun55i_pcie *pcie = dev_get_priv(bus);
|
|
+ int ret, size_len = 4;
|
|
+
|
|
+ if (!sun55i_pcie_addr_valid(bdf, pcie->first_busno)) {
|
|
+ debug("- out of range\n");
|
|
+ *value = pci_get_ff(size);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (size == PCI_SIZE_8)
|
|
+ size_len = 1;
|
|
+ else if (size == PCI_SIZE_16)
|
|
+ size_len = 2;
|
|
+ else if (size == PCI_SIZE_32)
|
|
+ size_len = 4;
|
|
+
|
|
+ if (PCI_BUS(bdf) != pcie->first_busno)
|
|
+ ret = sun55i_pcie_rd_other_conf(&pcie->pcie_port, bdf, offset, size_len, value);
|
|
+ else
|
|
+ ret = sun55i_pcie_host_rd_own_conf(&pcie->pcie_port, offset, size_len, value);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_write_config(struct udevice *bus, pci_dev_t bdf,
|
|
+ uint offset, ulong value,
|
|
+ enum pci_size_t size)
|
|
+{
|
|
+ struct sun55i_pcie *pcie = dev_get_priv(bus);
|
|
+ int ret, size_len = 4;
|
|
+
|
|
+ if (!sun55i_pcie_addr_valid(bdf, pcie->first_busno)) {
|
|
+ debug("- out of range\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (size == PCI_SIZE_8)
|
|
+ size_len = 1;
|
|
+ else if (size == PCI_SIZE_16)
|
|
+ size_len = 2;
|
|
+ else if (size == PCI_SIZE_32)
|
|
+ size_len = 4;
|
|
+
|
|
+ if (PCI_BUS(bdf) != 0)
|
|
+ ret = sun55i_pcie_wr_other_conf(&pcie->pcie_port, bdf, offset, size_len, value);
|
|
+ else
|
|
+ ret = sun55i_pcie_host_wr_own_conf(&pcie->pcie_port, offset, size_len, value);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void sun55i_pcie_host_setup_rc(struct sun55i_pcie_port *pp)
|
|
+{
|
|
+ ulong val, i;
|
|
+ phys_addr_t mem_base;
|
|
+ phys_addr_t io_base;
|
|
+ struct sun55i_pcie *pci = to_sun55i_pcie_from_pp(pp);
|
|
+
|
|
+ sun55i_pcie_plat_set_rate(pci);
|
|
+
|
|
+ sun55i_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_0, 0x4);
|
|
+ sun55i_pcie_writel_dbi(pci, PCI_BASE_ADDRESS_1, 0x0);
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCI_INTERRUPT_LINE);
|
|
+ val &= PCIE_INTERRUPT_LINE_MASK;
|
|
+ val |= PCIE_INTERRUPT_LINE_ENABLE;
|
|
+ sun55i_pcie_writel_dbi(pci, PCI_INTERRUPT_LINE, val);
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCI_PRIMARY_BUS);
|
|
+ val &= 0xff000000;
|
|
+ val |= 0x00ff0100;
|
|
+ sun55i_pcie_writel_dbi(pci, PCI_PRIMARY_BUS, val);
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCI_COMMAND);
|
|
+
|
|
+ val &= PCIE_HIGH16_MASK;
|
|
+ val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
|
|
+ PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
|
|
+
|
|
+ sun55i_pcie_writel_dbi(pci, PCI_COMMAND, val);
|
|
+
|
|
+ if (IS_ENABLED(CONFIG_PCI_MSI) && !pp->has_its) {
|
|
+ for (i = 0; i < 8; i++) {
|
|
+ sun55i_pcie_host_wr_own_conf(pp, PCIE_MSI_INTR_ENABLE(i), 4, ~0);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (pp->cpu_pcie_addr_quirk) {
|
|
+ mem_base = pp->mem_base - PCIE_CPU_BASE;
|
|
+ io_base = pp->io_base - PCIE_CPU_BASE;
|
|
+ } else {
|
|
+ mem_base = pp->mem_base;
|
|
+ io_base = pp->io_base;
|
|
+ }
|
|
+
|
|
+ sun55i_pcie_prog_outbound_atu(pp, PCIE_ATU_INDEX1, PCIE_ATU_TYPE_MEM,
|
|
+ mem_base, pp->mem_bus_addr, pp->mem_size);
|
|
+
|
|
+ sun55i_pcie_prog_outbound_atu(pp, PCIE_ATU_INDEX2, PCIE_ATU_TYPE_IO,
|
|
+ io_base, pp->io_bus_addr, pp->io_size);
|
|
+
|
|
+ sun55i_pcie_host_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
|
|
+
|
|
+ sun55i_pcie_dbi_ro_wr_en(pci);
|
|
+
|
|
+ sun55i_pcie_host_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
|
|
+
|
|
+ sun55i_pcie_dbi_ro_wr_dis(pci);
|
|
+
|
|
+ sun55i_pcie_host_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
|
|
+ val |= PORT_LOGIC_SPEED_CHANGE;
|
|
+ sun55i_pcie_host_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_link_up_status(struct sun55i_pcie_port *pp)
|
|
+{
|
|
+ u32 val;
|
|
+ int ret;
|
|
+ struct sun55i_pcie *pcie = to_sun55i_pcie_from_pp(pp);
|
|
+
|
|
+ val = sun55i_pcie_readl(pcie, PCIE_LINK_STAT);
|
|
+
|
|
+ if ((val & RDLH_LINK_UP) && (val & SMLH_LINK_UP))
|
|
+ ret = 1;
|
|
+ else
|
|
+ ret = 0;
|
|
+
|
|
+ printf(" Link Status: 0x%08x\n", val);
|
|
+ printf(" RDLH_LINK_UP: %d\n", !!(val & RDLH_LINK_UP));
|
|
+ printf(" SMLH_LINK_UP: %d\n", !!(val & SMLH_LINK_UP));
|
|
+ printf(" LINK_SPEED: %d\n", (val >> 16) & 0xF);
|
|
+ printf(" LINK_WIDTH: %d\n", (val >> 20) & 0x3F);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_link_up(struct sun55i_pcie_port *pp)
|
|
+{
|
|
+ return sun55i_pcie_host_link_up_status(pp);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_wait_for_link(struct sun55i_pcie_port *pp)
|
|
+{
|
|
+ int retries;
|
|
+
|
|
+ for (retries = 0; retries < LINK_WAIT_MAX_RETRIE; retries++) {
|
|
+ if (sun55i_pcie_host_link_up(pp)) {
|
|
+ printf("pcie link up success\n");
|
|
+ return 0;
|
|
+ }
|
|
+ mdelay(1);
|
|
+ }
|
|
+
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_establish_link(struct sun55i_pcie *pci)
|
|
+{
|
|
+ struct sun55i_pcie_port *pp = &pci->pcie_port;
|
|
+
|
|
+ if (sun55i_pcie_host_link_up(pp)) {
|
|
+ printf("pcie is already link up\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ sun55i_pcie_plat_ltssm_enable(pci);
|
|
+
|
|
+ return sun55i_pcie_host_wait_for_link(pp);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_wait_for_speed_change(struct sun55i_pcie *pci)
|
|
+{
|
|
+ u32 tmp;
|
|
+ unsigned int retries;
|
|
+
|
|
+ for (retries = 0; retries < LINK_WAIT_MAX_RETRIE; retries++) {
|
|
+ tmp = sun55i_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
|
+ if (!(tmp & PORT_LOGIC_SPEED_CHANGE))
|
|
+ return 0;
|
|
+ mdelay(1);
|
|
+ }
|
|
+
|
|
+ printf("Speed change timeout\n");
|
|
+ return -ETIMEDOUT;
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_host_speed_change(struct sun55i_pcie *pci, int gen)
|
|
+{
|
|
+ u32 val;
|
|
+ int ret;
|
|
+ u8 offset;
|
|
+
|
|
+ sun55i_pcie_dbi_ro_wr_en(pci);
|
|
+
|
|
+ offset = sun55i_pcie_plat_find_capability(pci, PCI_CAP_ID_EXP);
|
|
+ if (!offset) {
|
|
+ printf("PCIe: Cannot find PCI Express capability\n");
|
|
+ sun55i_pcie_dbi_ro_wr_dis(pci);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, LINK_CONTROL2_LINK_STATUS2);
|
|
+ val &= ~PCI_EXP_LNKCTL2_TLS;
|
|
+ val |= gen;
|
|
+ sun55i_pcie_writel_dbi(pci, LINK_CONTROL2_LINK_STATUS2, val);
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
|
+ val &= ~PORT_LOGIC_SPEED_CHANGE;
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
|
+
|
|
+ val = sun55i_pcie_readl_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL);
|
|
+ val |= PORT_LOGIC_SPEED_CHANGE;
|
|
+ sun55i_pcie_writel_dbi(pci, PCIE_LINK_WIDTH_SPEED_CONTROL, val);
|
|
+
|
|
+ ret = sun55i_pcie_host_wait_for_speed_change(pci);
|
|
+ if (!ret)
|
|
+ printf("PCIe: Link active at Gen%d\n", gen);
|
|
+ else
|
|
+ printf("PCIe: Link active, but speed change failed (remains Gen1)\n");
|
|
+
|
|
+ sun55i_pcie_dbi_ro_wr_dis(pci);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void sun55i_pcie_host_init(struct udevice *dev)
|
|
+{
|
|
+ struct sun55i_pcie *pci = dev_get_priv(dev);
|
|
+
|
|
+ sun55i_pcie_plat_ltssm_disable(pci);
|
|
+
|
|
+ sun55i_pcie_host_setup_rc(&pci->pcie_port);
|
|
+
|
|
+ sun55i_pcie_host_establish_link(pci);
|
|
+
|
|
+ sun55i_pcie_host_speed_change(pci, pci->link_gen);
|
|
+}
|
|
+
|
|
+static int sun55i_pcie_probe(struct udevice *dev)
|
|
+{
|
|
+ struct sun55i_pcie *pci = dev_get_priv(dev);
|
|
+ struct udevice *ctlr = pci_get_controller(dev);
|
|
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
|
+ const struct sun55i_pcie_of_data *data;
|
|
+ int ret;
|
|
+
|
|
+ data = (const struct sun55i_pcie_of_data *)dev_get_driver_data(dev);
|
|
+ if (!data) {
|
|
+ printf("PCIe: No platform data found\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ret = sun55i_pcie_plat_hw_init(dev);
|
|
+ if (ret) {
|
|
+ printf("PCIe: Hardware init failed with error %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ pci->first_busno = dev->seq_;
|
|
+ pci->dev = dev;
|
|
+
|
|
+ pci->dbi_base = (void __iomem *)phys_to_virt((phys_addr_t)data->dbi_addr);
|
|
+ pci->app_base = (void __iomem *)((char *)pci->dbi_base + PCIE_USER_DEFINED_REGISTER);
|
|
+
|
|
+ printf("PCIe: Disabling DBI write protection...\n");
|
|
+ sun55i_pcie_dbi_ro_wr_en(pci);
|
|
+
|
|
+ pci_set_region(&hose->regions[0],
|
|
+ data->io_addr,
|
|
+ data->io_addr,
|
|
+ data->io_size,
|
|
+ PCI_REGION_IO);
|
|
+
|
|
+ pci_set_region(&hose->regions[1],
|
|
+ data->mem_addr,
|
|
+ data->mem_addr,
|
|
+ data->mem_size,
|
|
+ PCI_REGION_MEM);
|
|
+
|
|
+ hose->region_count = 2;
|
|
+
|
|
+ pci->pcie_port.dbi_base = (void __iomem *)phys_to_virt((phys_addr_t)data->dbi_addr);
|
|
+ pci->pcie_port.cfg0_base = data->cfg_addr;
|
|
+ pci->pcie_port.cfg0_size = data->cfg_size;
|
|
+ pci->pcie_port.io_base = data->io_addr;
|
|
+ pci->pcie_port.io_size = data->io_size;
|
|
+ pci->pcie_port.mem_base = data->mem_addr;
|
|
+ pci->pcie_port.mem_size = data->mem_size;
|
|
+
|
|
+ pci->pcie_port.io_bus_addr = data->io_addr;
|
|
+ pci->pcie_port.mem_bus_addr = data->mem_addr;
|
|
+
|
|
+ if (!pci->lanes)
|
|
+ pci->lanes = data->num_lanes;
|
|
+ if (!pci->link_gen)
|
|
+ pci->link_gen = data->max_link_speed;
|
|
+
|
|
+ pci->pcie_port.cpu_pcie_addr_quirk = true;
|
|
+
|
|
+ pci->pcie_port.va_cfg0_base = phys_to_virt(pci->pcie_port.cfg0_base);
|
|
+
|
|
+ printf("PCIe: DBI region: 0x%08x-0x%08x\n", data->dbi_addr, data->dbi_addr + data->dbi_size);
|
|
+ printf("PCIe: IO region: 0x%08x-0x%08x\n", data->io_addr, data->io_addr + data->io_size);
|
|
+ printf("PCIe: MEM region: 0x%08x-0x%08x\n", data->mem_addr, data->mem_addr + data->mem_size);
|
|
+ printf("PCIe: CFG region: 0x%08x-0x%08x\n", data->cfg_addr, data->cfg_addr + data->cfg_size);
|
|
+ printf("PCIe: Lanes: %d, Max Speed: Gen%d\n", data->num_lanes, data->max_link_speed);
|
|
+
|
|
+ sun55i_pcie_host_init(dev);
|
|
+
|
|
+ sun55i_pcie_dbi_ro_wr_dis(pci);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dm_pci_ops sun55i_pcie_ops = {
|
|
+ .read_config = sun55i_pcie_read_config,
|
|
+ .write_config = sun55i_pcie_write_config,
|
|
+};
|
|
+
|
|
+static const struct sun55i_pcie_of_data sun55i_pcie_rc_v210_of_data = {
|
|
+ .mode = SUN55I_PCIE_RC_TYPE,
|
|
+ .cpu_pcie_addr_quirk = true,
|
|
+
|
|
+ .dbi_addr = 0x04800000,
|
|
+ .dbi_size = 0x480000,
|
|
+ .io_addr = 0x21000000,
|
|
+ .io_size = 0x01000000,
|
|
+ .mem_addr = 0x22000000,
|
|
+ .mem_size = 0x0e000000,
|
|
+ .cfg_addr = 0x20000000,
|
|
+ .cfg_size = 0x01000000,
|
|
+ .num_lanes = 1, /* Default */
|
|
+ .max_link_speed = 2,
|
|
+ .num_ib_windows = 8,
|
|
+ .num_ob_windows = 8,
|
|
+};
|
|
+
|
|
+static const struct udevice_id sun55i_pcie_ids[] = {
|
|
+ {
|
|
+ .compatible = "allwinner,sun55i-pcie-v210-rc",
|
|
+ .data = (ulong)&sun55i_pcie_rc_v210_of_data,
|
|
+ },
|
|
+ { }
|
|
+};
|
|
+
|
|
+U_BOOT_DRIVER(sun55i_pcie) = {
|
|
+ .name = "pcie_dw_sun55i",
|
|
+ .id = UCLASS_PCI,
|
|
+ .of_match = sun55i_pcie_ids,
|
|
+ .ops = &sun55i_pcie_ops,
|
|
+ .probe = sun55i_pcie_probe,
|
|
+ .priv_auto = sizeof(struct sun55i_pcie),
|
|
+};
|
|
diff --git a/drivers/pci/pcie-sun55i.h b/drivers/pci/pcie-sun55i.h
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/pci/pcie-sun55i.h
|
|
@@ -0,0 +1,490 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
+/* Copyright(c) 2020 - 2023 Allwinner Technology Co.,Ltd. All rights reserved. */
|
|
+/*
|
|
+ * Allwinner PCIe controller driver
|
|
+ *
|
|
+ * Copyright (C) 2022 allwinner Co., Ltd.
|
|
+ *
|
|
+ * Author: songjundong <songjundong@allwinnertech.com>
|
|
+ *
|
|
+ * This program is free software; you can redistribute it and/or modify
|
|
+ * it under the terms of the GNU General Public License version 2 as
|
|
+ * published by the Free Software Foundation.
|
|
+ */
|
|
+#ifndef _PCIE_SUN55I_H
|
|
+#define _PCIE_SUN55I_H
|
|
+
|
|
+#include <asm/io.h>
|
|
+#include <asm/gpio.h>
|
|
+#include <clk.h>
|
|
+#include <reset.h>
|
|
+#include <generic-phy.h>
|
|
+#include <pci.h>
|
|
+#include <power-domain.h>
|
|
+#include <power/regulator.h>
|
|
+#include <asm/io.h>
|
|
+#include <asm/gpio.h>
|
|
+#include <errno.h>
|
|
+
|
|
+#define SUN55I_PCIE_MODULE_VERSION "1.0.0"
|
|
+
|
|
+#define SUN55I_PCIE_CFG_SIZE 0x01000000
|
|
+
|
|
+#define SUN55I_CFG_RW_SIZE 0x04
|
|
+
|
|
+#define PCIE_PORT_LINK_CONTROL 0x710
|
|
+#define PORT_LINK_MODE_MASK (0x3f << 16)
|
|
+#define PORT_LINK_MODE_1_LANES (0x1 << 16)
|
|
+#define PORT_LINK_MODE_2_LANES (0x3 << 16)
|
|
+#define PORT_LINK_MODE_4_LANES (0x7 << 16)
|
|
+#define PORT_LINK_LPBK_ENABLE (0x1 << 2)
|
|
+
|
|
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
|
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
|
|
+#define PORT_LOGIC_LINK_WIDTH_MASK (0x1ff << 8)
|
|
+#define PORT_LOGIC_LINK_WIDTH_1_LANES (0x1 << 8)
|
|
+#define PORT_LOGIC_LINK_WIDTH_2_LANES (0x2 << 8)
|
|
+#define PORT_LOGIC_LINK_WIDTH_4_LANES (0x4 << 8)
|
|
+
|
|
+#define PCIE_ATU_VIEWPORT 0x900
|
|
+#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
|
|
+#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
|
|
+#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
|
|
+#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
|
|
+#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
|
|
+
|
|
+#define PCIE_ATU_INDEX0 0x0
|
|
+#define PCIE_ATU_INDEX1 0x1
|
|
+#define PCIE_ATU_INDEX2 0x2
|
|
+#define PCIE_ATU_INDEX3 0x3
|
|
+#define PCIE_ATU_INDEX4 0x4
|
|
+#define PCIE_ATU_INDEX5 0x5
|
|
+#define PCIE_ATU_INDEX6 0x6
|
|
+#define PCIE_ATU_INDEX7 0x7
|
|
+
|
|
+#define PCIE_EP_REBAR_SIZE_32M 0x200
|
|
+
|
|
+#define PCIE_ATU_CR1_OUTBOUND(reg) (0x300000 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
|
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
|
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
|
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
|
+#define PCIE_ATU_CR2_OUTBOUND(reg) (0x300004 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_DMA_BYPASS BIT(27)
|
|
+#define PCIE_ATU_BAR_MODE_ENABLE BIT(30)
|
|
+#define PCIE_ATU_ENABLE BIT(31)
|
|
+
|
|
+#define PCIE_ATU_LOWER_BASE_OUTBOUND(reg) (0x300008 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_UPPER_BASE_OUTBOUND(reg) (0x30000c + ((reg) * 0x200))
|
|
+#define PCIE_ATU_LIMIT_OUTBOUND(reg) (0x300010 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_LOWER_TARGET_OUTBOUND(reg) (0x300014 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_UPPER_TARGET_OUTBOUND(reg) (0x300018 + ((reg) * 0x200))
|
|
+
|
|
+#define PCIE_ATU_CR1_INBOUND(reg) (0x300100 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
|
+#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
|
+#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
|
+#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
|
+#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
|
|
+#define PCIE_ATU_CR2_INBOUND(reg) (0x300104 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_MATCH_MODE BIT(30)
|
|
+#define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19)
|
|
+#define PCIE_ATU_FUNC_NUM(pf) ((pf) << 20)
|
|
+
|
|
+#define PCIE_ATU_LOWER_BASE_INBOUND(reg) (0x300108 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_UPPER_BASE_INBOUND(reg) (0x30010c + ((reg) * 0x200))
|
|
+#define PCIE_ATU_LIMIT_INBOUND(reg) (0x300110 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_LOWER_TARGET_INBOUND(reg) (0x300114 + ((reg) * 0x200))
|
|
+#define PCIE_ATU_UPPER_TARGET_INBOUND(reg) (0x300118 + ((reg) * 0x200))
|
|
+
|
|
+#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
|
|
+#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
|
|
+#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
|
+
|
|
+#define PCIE_MISC_CONTROL_1_CFG 0x8bc
|
|
+#define PCIE_TYPE1_CLASS_CODE_REV_ID_REG 0x08
|
|
+
|
|
+#define PCIE_ADDRESS_ALIGNING (~0x3)
|
|
+#define PCIE_HIGH_16 16
|
|
+#define PCIE_BAR_NUM 6
|
|
+#define PCIE_MEM_FLAGS 0x4
|
|
+#define PCIE_IO_FLAGS 0x1
|
|
+#define PCIE_BAR_REG 0x4
|
|
+#define PCIE_HIGH16_MASK 0xffff0000
|
|
+#define PCIE_LOW16_MASK 0x0000ffff
|
|
+#define PCIE_INTERRUPT_LINE_MASK 0xffff00ff
|
|
+#define PCIE_INTERRUPT_LINE_ENABLE 0x00000100
|
|
+#define PCIE_PRIMARY_BUS_MASK 0xff000000
|
|
+#define PCIE_PRIMARY_BUS_ENABLE 0x00010100
|
|
+#define PCIE_MEMORY_MASK 0xfff00000
|
|
+
|
|
+#define PCIE_CPU_BASE 0x20000000
|
|
+
|
|
+#define PCIE_TYPE0_STATUS_COMMAND_REG 0x4
|
|
+
|
|
+#define PCIE_DBI2_BASE 0x100000
|
|
+#define DBI2_FUNC_OFFSET 0x10000
|
|
+#define BAR_ENABLE 0x1
|
|
+
|
|
+#define RESBAR_CAP_REG 0x4 /* from PCIe spec4.0 7.8.6 */
|
|
+#define RESBAR_SIZE_MASK 0xfffff0
|
|
+#define RESBAR_CTL_REG 0x8
|
|
+#define RESBAR_NEXT_BAR 0x8
|
|
+#define SIZE_OF_1MB 20 /* 2^20 = 0x100000 */
|
|
+
|
|
+#define PCIE_COMBO_PHY_BGR 0x04
|
|
+#define PHY_ACLK_EN BIT(17)
|
|
+#define PHY_HCLK_EN BIT(16)
|
|
+#define PHY_TERSTN BIT(1)
|
|
+#define PHY_PW_UP_RSTN BIT(0)
|
|
+#define PCIE_COMBO_PHY_CTL 0x10
|
|
+#define PHY_USE_SEL BIT(31) /* 0:PCIE; 1:USB3 */
|
|
+#define PHY_CLK_SEL BIT(30) /* 0:internal clk; 1:exteral clk */
|
|
+#define PHY_BIST_EN BIT(16)
|
|
+#define PHY_PIPE_SW BIT(9)
|
|
+#define PHY_PIPE_SEL BIT(8) /* 0:PIPE resetn ctrl by PCIE ctrl; 1:PIPE resetn ctrl by */
|
|
+#define PHY_PIPE_CLK_INVERT BIT(4)
|
|
+#define PHY_FPGA_SYS_RSTN BIT(1) /* for PFGA */
|
|
+#define PHY_RSTN BIT(0)
|
|
+
|
|
+#define NEXT_CAP_PTR_MASK 0xff00
|
|
+#define CAP_ID_MASK 0x00ff
|
|
+
|
|
+/* Error values that may be returned by PCI functions */
|
|
+#define PCIBIOS_SUCCESSFUL 0x00
|
|
+#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
|
|
+#define PCIBIOS_BAD_VENDOR_ID 0x83
|
|
+#define PCIBIOS_DEVICE_NOT_FOUND 0x86
|
|
+#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
|
|
+#define PCIBIOS_SET_FAILED 0x88
|
|
+#define PCIBIOS_BUFFER_TOO_SMALL 0x89
|
|
+
|
|
+/*
|
|
+ * Maximum number of MSI IRQs can be 256 per controller. But keep
|
|
+ * it 32 as of now. Probably we will never need more than 32. If needed,
|
|
+ * then increment it in multiple of 32.
|
|
+ */
|
|
+#define INT_PCI_MSI_NR 32
|
|
+#define MAX_MSI_IRQS 256
|
|
+#define MAX_MSI_IRQS_PER_CTRL 32
|
|
+#define MAX_MSI_CTRLS (MAX_MSI_IRQS / MAX_MSI_IRQS_PER_CTRL)
|
|
+#define MSI_REG_CTRL_BLOCK_SIZE 12
|
|
+
|
|
+/* #define MAX_MSI_IRQS 32 */
|
|
+/* #define MAX_MSI_CTRLS (MAX_MSI_IRQS / 32) */
|
|
+#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C
|
|
+#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17)
|
|
+#define LINK_CONTROL2_LINK_STATUS2 0xa0
|
|
+/* Parameters for the waiting for link up routine */
|
|
+#define LINK_WAIT_MAX_RETRIE 20
|
|
+#define LINK_WAIT_USLEEP_MIN 90000
|
|
+#define LINK_WAIT_USLEEP_MAX 100000
|
|
+#define SPEED_CHANGE_USLEEP_MIN 100
|
|
+#define SPEED_CHANGE_USLEEP_MAX 1000
|
|
+
|
|
+#define PCIE_MSI_ADDR_LO 0x820
|
|
+#define PCIE_MSI_ADDR_HI 0x824
|
|
+#define PCIE_MSI_INTR_ENABLE(reg) (0x828 + ((reg) * 0x0c))
|
|
+/* #define PCIE_MSI_INTR_MASK(reg) (0x82C + ((reg) * 0x0c)) */
|
|
+/* #define PCIE_MSI_INTR_STATUS(reg) (0x830 + ((reg) * 0x0c)) */
|
|
+/* #define PCIE_MSI_INTR_ENABLE 0x828 */
|
|
+#define PCIE_MSI_INTR_MASK 0x82C
|
|
+#define PCIE_MSI_INTR_STATUS 0x830
|
|
+
|
|
+#define PCIE_CTRL_MGMT_BASE 0x900000
|
|
+
|
|
+#define PCIE_USER_DEFINED_REGISTER 0x400000
|
|
+#define PCIE_VER 0x00
|
|
+#define PCIE_ADDR_PAGE_CFG 0x04
|
|
+#define PCIE_AWMISC_CTRL 0x200
|
|
+#define PCIE_ARMISC_CTRL 0x220
|
|
+#define PCIE_LTSSM_CTRL 0xc00
|
|
+#define PCIE_LINK_TRAINING BIT(0) /* 0:disable; 1:enable */
|
|
+#define DEVICE_TYPE_MASK GENMASK(7, 4)
|
|
+#define DEVICE_TYPE_RC BIT(6)
|
|
+#define PCIE_INT_ENABLE_CLR 0xE04 /* BIT(1):RDLH_LINK_MASK; BIT(0):SMLH_LINK_MASK */
|
|
+#define PCIE_LINK_STAT 0xE0C /* BIT(1):RDLH_LINK; BIT(0):SMLH_LINK */
|
|
+#define RDLH_LINK_UP BIT(1)
|
|
+#define SMLH_LINK_UP BIT(0)
|
|
+#define PCIE_LINK_INT_EN (BIT(0) | BIT(1))
|
|
+
|
|
+#define PCIE_PHY_CFG 0x800
|
|
+#define SYS_CLK 0
|
|
+#define PAD_CLK 1
|
|
+#define PCIE_LINK_UP_MASK (0x3<<16)
|
|
+
|
|
+#define PCIE_RC_RP_ATS_BASE 0x400000
|
|
+
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_DISABLED 0x0
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_IO_32BITS 0x1
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_MEM_32BITS 0x4
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_PREFETCH_MEM_32BITS 0x5
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_MEM_64BITS 0x6
|
|
+#define SUN55I_PCIE_BAR_CFG_CTRL_PREFETCH_MEM_64BITS 0x7
|
|
+
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_REG 0x90
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_MMC_OFFSET 17
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_MMC_MASK GENMASK(19, 17)
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_MME_OFFSET 20
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_MME_MASK GENMASK(22, 20)
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_ME BIT(16)
|
|
+#define SUN55I_PCIE_EP_MSI_CTRL_MASK_MSI_CAP BIT(24)
|
|
+#define SUN55I_PCIE_EP_DUMMY_IRQ_ADDR 0x1
|
|
+
|
|
+#define PCIE_PHY_FUNC_CFG (PCIE_CTRL_MGMT_BASE + 0x2c0)
|
|
+#define PCIE_RC_BAR_CONF (PCIE_CTRL_MGMT_BASE + 0x300)
|
|
+
|
|
+//ECC
|
|
+#define PCIE_RASDP_ERR_PROT_CTRL_OFF 0X1F0
|
|
+#define PCIE_RASDP_ERR_INJ_CTRL_OFF 0X204
|
|
+#define PCIE_RASDP_UNCORR_COUNTER_CTRL_OFF 0X1FC
|
|
+#define PCIE_RASDP_UNCORR_COUNTER_REPORT_OFF 0X200
|
|
+#define PCIE_RASDP_UNCORR_ERROR_LOCATION_OFF 0X20C
|
|
+#define PCIE_RASDP_ERROR_MODR_CLEAR_OFF 0X214
|
|
+
|
|
+#define PCIE_RASDP_CORR_COUNTER_CTRL_OFF 0X1F4
|
|
+#define PCIE_RASDP_CORR_COUNTER_REPORT_OFF 0X1F8
|
|
+#define PCIE_RASDP_CORR_ERROR_LOCATION_OFF 0X208
|
|
+
|
|
+#define PCIE_SII_INT_MASK_RES2 0XE10
|
|
+#define PCIE_SII_INT_RES2 0XE18
|
|
+#define APP_PARITY_ERRS2_MASK BIT(12)
|
|
+#define APP_PARITY_ERRS1_MASK BIT(11)
|
|
+#define APP_PARITY_ERRS0_MASK BIT(10)
|
|
+#define SLV_RASDP_ERR_MODE_MASK BIT(9)
|
|
+#define MATR_RASDP_ERR_MODE_MASK BIT(8)
|
|
+#define RASDP_ERR_PENDING (BIT(8) | BIT(9) | BIT(10) | BIT(11) | BIT(12))
|
|
+#define PCIE_SII_INT_RES2_ECC_MASK GENMASK(12, 8)
|
|
+
|
|
+#define PCI_EXP_LNKCTL2_TLS 0x000f
|
|
+#define PCI_EXP_LNKCTL2_TLS_2_5GT 0x0001 /* Supported Speed 2.5GT/s */
|
|
+#define PCI_EXP_LNKCTL2_TLS_5_0GT 0x0002 /* Supported Speed 5GT/s */
|
|
+#define PCI_EXP_LNKCTL2_TLS_8_0GT 0x0003 /* Supported Speed 8GT/s */
|
|
+#define PCI_EXP_LNKCTL2_TLS_16_0GT 0x0004 /* Supported Speed 16GT/s */
|
|
+#define PCI_EXP_LNKCTL2_TLS_32_0GT 0x0005 /* Supported Speed 32GT/s */
|
|
+#define PCI_EXP_LNKCTL2_HASD 0x0020 /* HW Autonomous Speed Disable */
|
|
+
|
|
+#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
|
|
+#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */
|
|
+#define PCI_EXP_LNKCAP_SLS_2_5GB 0x00000001 /* LNKCAP2 SLS Vector bit 0 */
|
|
+#define PCI_EXP_LNKCAP_SLS_5_0GB 0x00000002 /* LNKCAP2 SLS Vector bit 1 */
|
|
+#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */
|
|
+#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */
|
|
+#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */
|
|
+#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */
|
|
+#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* Clock Power Management */
|
|
+#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Surprise Down Error Reporting Capable */
|
|
+#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
|
|
+#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */
|
|
+#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */
|
|
+
|
|
+#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
|
|
+#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
|
|
+#define PCI_EXP_SLTCAP2 52 /* Slot Capabilities 2 */
|
|
+#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
|
|
+#define PCI_EXP_SLTSTA2 58 /* Slot Status 2 */
|
|
+
|
|
+
|
|
+
|
|
+/* Resizable BARs */
|
|
+#define PCI_REBAR_CAP 4 /* capability register */
|
|
+#define PCI_REBAR_CAP_SIZES 0x00FFFFF0 /* supported BAR sizes */
|
|
+#define PCI_REBAR_CTRL 8 /* control register */
|
|
+#define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */
|
|
+#define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */
|
|
+#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */
|
|
+#define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */
|
|
+#define PCI_REBAR_CTRL_BAR_SHIFT 8 /* shift for BAR size */
|
|
+
|
|
+#define PCI_HEADER_TYPE_MASK 0x7f
|
|
+
|
|
+
|
|
+#define PCI_STD_NUM_BARS 6 /* Number of standard BARs */
|
|
+enum sun55i_pcie_device_mode {
|
|
+ SUN55I_PCIE_EP_TYPE,
|
|
+ SUN55I_PCIE_RC_TYPE,
|
|
+};
|
|
+
|
|
+/* See matching string table in pci_speed_string() */
|
|
+enum pci_bus_speed {
|
|
+ PCI_SPEED_33MHz = 0x00,
|
|
+ PCI_SPEED_66MHz = 0x01,
|
|
+ PCI_SPEED_66MHz_PCIX = 0x02,
|
|
+ PCI_SPEED_100MHz_PCIX = 0x03,
|
|
+ PCI_SPEED_133MHz_PCIX = 0x04,
|
|
+ PCI_SPEED_66MHz_PCIX_ECC = 0x05,
|
|
+ PCI_SPEED_100MHz_PCIX_ECC = 0x06,
|
|
+ PCI_SPEED_133MHz_PCIX_ECC = 0x07,
|
|
+ PCI_SPEED_66MHz_PCIX_266 = 0x09,
|
|
+ PCI_SPEED_100MHz_PCIX_266 = 0x0a,
|
|
+ PCI_SPEED_133MHz_PCIX_266 = 0x0b,
|
|
+ AGP_UNKNOWN = 0x0c,
|
|
+ AGP_1X = 0x0d,
|
|
+ AGP_2X = 0x0e,
|
|
+ AGP_4X = 0x0f,
|
|
+ AGP_8X = 0x10,
|
|
+ PCI_SPEED_66MHz_PCIX_533 = 0x11,
|
|
+ PCI_SPEED_100MHz_PCIX_533 = 0x12,
|
|
+ PCI_SPEED_133MHz_PCIX_533 = 0x13,
|
|
+ PCIE_SPEED_2_5GT = 0x14,
|
|
+ PCIE_SPEED_5_0GT = 0x15,
|
|
+ PCIE_SPEED_8_0GT = 0x16,
|
|
+ PCIE_SPEED_16_0GT = 0x17,
|
|
+ PCIE_SPEED_32_0GT = 0x18,
|
|
+ PCI_SPEED_UNKNOWN = 0xff,
|
|
+};
|
|
+
|
|
+struct sun55i_pcie_of_data {
|
|
+ const struct sun55i_pcie_ep_ops *ops;
|
|
+ enum sun55i_pcie_device_mode mode;
|
|
+ u32 func_offset;
|
|
+ bool has_pcie_slv_clk;
|
|
+ bool need_pcie_rst;
|
|
+ bool pcie_slv_clk_400m;
|
|
+ bool has_pcie_its_clk;
|
|
+ bool has_pcie_ecc;
|
|
+ bool cpu_pcie_addr_quirk;
|
|
+
|
|
+ u32 dbi_addr;
|
|
+ u32 dbi_size;
|
|
+ u32 io_addr;
|
|
+ u32 io_size;
|
|
+ u32 mem_addr;
|
|
+ u32 mem_size;
|
|
+ u32 cfg_addr;
|
|
+ u32 cfg_size;
|
|
+ u32 num_lanes;
|
|
+ u32 max_link_speed;
|
|
+ u32 num_ib_windows;
|
|
+ u32 num_ob_windows;
|
|
+};
|
|
+
|
|
+struct sun55i_pcie_ep_func {
|
|
+ struct list_head list;
|
|
+ u8 func_no;
|
|
+ u8 msi_cap;
|
|
+ u8 msix_cap;
|
|
+};
|
|
+
|
|
+struct sun55i_pcie_ep {
|
|
+ struct pci_epc *epc;
|
|
+ struct list_head func_list;
|
|
+ const struct sun55i_pcie_ep_ops *ops;
|
|
+ phys_addr_t phys_base;
|
|
+ size_t addr_size;
|
|
+ size_t page_size;
|
|
+ u8 bar_to_atu[PCI_STD_NUM_BARS];
|
|
+ phys_addr_t *outbound_addr;
|
|
+ u32 num_ib_windows;
|
|
+ u32 num_ob_windows;
|
|
+ unsigned long *ib_window_map;
|
|
+ unsigned long *ob_window_map;
|
|
+ u8 max_functions;
|
|
+ void __iomem *msi_mem;
|
|
+ phys_addr_t msi_mem_phys;
|
|
+ struct pci_bar *epf_bar[PCI_STD_NUM_BARS];
|
|
+};
|
|
+
|
|
+struct sun55i_pcie_ep_ops {
|
|
+ void (*ep_init)(struct sun55i_pcie_ep *ep);
|
|
+ // int (*raise_irq)(struct sun55i_pcie_ep *ep, u8 func_no,
|
|
+ // enum pci_epc_irq_type type, u16 interrupt_num);
|
|
+ const struct pci_epc_features *(*get_features)(struct sun55i_pcie_ep *ep);
|
|
+ unsigned int (*func_conf_select)(struct sun55i_pcie_ep *ep, u8 func_no);
|
|
+};
|
|
+
|
|
+struct sun55i_pcie_port {
|
|
+ struct device *dev;
|
|
+ void __iomem *dbi_base;
|
|
+ u32 cfg0_base;
|
|
+ void __iomem *va_cfg0_base;
|
|
+ fdt_size_t cfg0_size;
|
|
+ resource_size_t io_base;
|
|
+ phys_addr_t io_bus_addr;
|
|
+ u32 io_size;
|
|
+ phys_addr_t mem_base;
|
|
+ phys_addr_t mem_bus_addr;
|
|
+ u32 mem_size;
|
|
+ u32 num_ob_windows;
|
|
+ struct sun55i_pcie_host_ops *ops;
|
|
+ struct sun55i_pcie *pcie;
|
|
+ int msi_irq;
|
|
+ struct irq_domain *irq_domain;
|
|
+ struct irq_domain *msi_domain;
|
|
+ struct pci_host_bridge *bridge;
|
|
+ u8 root_bus_nr;
|
|
+ unsigned long msi_map[BITS_TO_LONGS(INT_PCI_MSI_NR)];
|
|
+ bool has_its;
|
|
+ bool cpu_pcie_addr_quirk;
|
|
+};
|
|
+
|
|
+struct sun55i_pci_edma_chan;
|
|
+
|
|
+struct sun55i_pcie {
|
|
+ struct udevice *dev;
|
|
+ void *dbi_base;
|
|
+ void *app_base;
|
|
+ int link_gen;
|
|
+ int first_busno;
|
|
+ struct sun55i_pcie_port pcie_port;
|
|
+ struct sun55i_pcie_ep ep;
|
|
+
|
|
+ struct clk pcie_aux;
|
|
+ struct reset_ctl pcie_rst;
|
|
+ struct phy phy;
|
|
+ struct udevice *slot_3v3;
|
|
+
|
|
+ struct clk pcie_slv;
|
|
+ struct clk pcie_its;
|
|
+ struct reset_ctl pwrup_rst;
|
|
+ struct reset_ctl pcie_its_rst;
|
|
+ struct dma_trx_obj *dma_obj;
|
|
+ const struct sun55i_pcie_of_data *drvdata;
|
|
+ struct gpio_desc rst_gpio;
|
|
+ struct gpio_desc wake_gpio;
|
|
+ struct gpio_desc switch_gpio;
|
|
+ u32 lanes;
|
|
+ u32 num_edma;
|
|
+ unsigned long *rd_edma_map;
|
|
+ unsigned long *wr_edma_map;
|
|
+ struct sun55i_pci_edma_chan *dma_wr_chn;
|
|
+ struct sun55i_pci_edma_chan *dma_rd_chn;
|
|
+
|
|
+ struct udevice *pcie3v3;
|
|
+};
|
|
+
|
|
+#define to_sun55i_pcie_from_pp(x) \
|
|
+ container_of((x), struct sun55i_pcie, pcie_port)
|
|
+
|
|
+#define to_sun55i_pcie_from_ep(endpoint) \
|
|
+ container_of((endpoint), struct sun55i_pcie, ep)
|
|
+
|
|
+int sun55i_pcie_plat_hw_init(struct udevice *dev);
|
|
+
|
|
+void sun55i_pcie_plat_set_rate(struct sun55i_pcie *pci);
|
|
+void sun55i_pcie_plat_set_mode(struct sun55i_pcie *pci);
|
|
+void sun55i_pcie_plat_ltssm_enable(struct sun55i_pcie *pci);
|
|
+void sun55i_pcie_plat_ltssm_disable(struct sun55i_pcie *pci);
|
|
+
|
|
+int sun55i_pcie_cfg_write(void __iomem *addr, int size, ulong val);
|
|
+int sun55i_pcie_cfg_read(void __iomem *addr, int size, ulong *val);
|
|
+
|
|
+u32 sun55i_pcie_readl(struct sun55i_pcie *pcie, u32 offset);
|
|
+void sun55i_pcie_writel(u32 val, struct sun55i_pcie *pcie, u32 offset);
|
|
+
|
|
+void sun55i_pcie_writel_dbi(struct sun55i_pcie *pci, u32 reg, u32 val);
|
|
+u32 sun55i_pcie_readl_dbi(struct sun55i_pcie *pci, u32 reg);
|
|
+void sun55i_pcie_writew_dbi(struct sun55i_pcie *pci, u32 reg, u16 val);
|
|
+u16 sun55i_pcie_readw_dbi(struct sun55i_pcie *pci, u32 reg);
|
|
+void sun55i_pcie_writeb_dbi(struct sun55i_pcie *pci, u32 reg, u8 val);
|
|
+u8 sun55i_pcie_readb_dbi(struct sun55i_pcie *pci, u32 reg);
|
|
+
|
|
+void sun55i_pcie_dbi_ro_wr_en(struct sun55i_pcie *pci);
|
|
+void sun55i_pcie_dbi_ro_wr_dis(struct sun55i_pcie *pci);
|
|
+
|
|
+u8 sun55i_pcie_plat_find_capability(struct sun55i_pcie *pci, u8 cap);
|
|
+
|
|
+#define SUN55I_PCIE_PHY_BASE 0x04f00000
|
|
+#define SUN55I_PCIE_PORT0_CONF_OFFSET 0x1000
|
|
+
|
|
+#endif /* _PCIE_SUN55I_H */
|
|
--
|
|
Armbian
|
|
|