From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Marvin Wewer Date: Thu, 30 Oct 2025 22:33:03 +0000 Subject: PCIe: Add support for Allwinner SUN55I DesignWare PCIe controller Signed-off-by: Marvin Wewer --- 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 + */ + + +#include "pci.h" +#include "pcie-sun55i.h" +#include +#include +#include +#include +#include + +/* 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 +#include +#include +#include +#include +#include +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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