From da35d7af335a5be3ae5033a44f0e8b536c38dce7 Mon Sep 17 00:00:00 2001 From: Wolfgang Wallner Date: Tue, 23 Mar 2021 09:54:58 +0100 Subject: [PATCH 1/6] dt-bindings: fsp: Fix Apollo Lake FSP-S devicetree bindings An entry is missing in the FSP-S devicetree bindings, and as a result the description for the next few following entries is off by one line. Signed-off-by: Wolfgang Wallner Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- .../fsp/fsp2/apollolake/fsp-s.txt | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-s.txt b/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-s.txt index b605ed0056..dc8e3251a3 100644 --- a/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-s.txt +++ b/doc/device-tree-bindings/fsp/fsp2/apollolake/fsp-s.txt @@ -258,19 +258,20 @@ Optional properties: - fsps,pcie-rp-clk-req-detect: CLKREQ# Detection - fsps,advanced-error-reportingt: Advanced Error Reporting - fsps,pme-interrupt: PME Interrupt -- fsps,fatal-error-report: URR -- fsps,no-fatal-error-report: FER -- fsps,correctable-error-report: NFER -- fsps,system-error-on-fatal-error: CER -- fsps,system-error-on-non-fatal-error: SEFE -- fsps,system-error-on-correctable-error: SENFE -- fsps,pcie-rp-speed: SECE -- fsps,physical-slot-number: PCIe Speed +- fsps,unsupported-request-report: URR +- fsps,fatal-error-report: FER +- fsps,no-fatal-error-report: NFER +- fsps,correctable-error-report: CER +- fsps,system-error-on-fatal-error: SEFE +- fsps,system-error-on-non-fatal-error: SENFE +- fsps,system-error-on-correctable-error: SECE +- fsps,pcie-rp-speed: PCIe Speed +- fsps,physical-slot-number: Physical Slot Number 0: Auto (default) 1: Gen1 2: Gen2 3: Gen3 -- fsps,pcie-rp-completion-timeout: Physical Slot Number +- fsps,pcie-rp-completion-timeout: CTO 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 (default) - fsps,enable-ptm: PTM Support - fsps,pcie-rp-aspm: ASPM From 21296b093e90d9dbe99b7b23bc9dcad79d824034 Mon Sep 17 00:00:00 2001 From: Wolfgang Wallner Date: Tue, 23 Mar 2021 10:39:09 +0100 Subject: [PATCH 2/6] x86: mtrr: Fix function descriptions Fix copy/paste errors in the descriptions of mtrr_close () and mtrr_set(). Signed-off-by: Wolfgang Wallner Reviewed-by: Simon Glass Reviewed-by: Bin Meng --- arch/x86/include/asm/mtrr.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/include/asm/mtrr.h b/arch/x86/include/asm/mtrr.h index 3a98aacdef..384672e93f 100644 --- a/arch/x86/include/asm/mtrr.h +++ b/arch/x86/include/asm/mtrr.h @@ -103,7 +103,7 @@ struct mtrr_info { void mtrr_open(struct mtrr_state *state, bool do_caches); /** - * mtrr_open() - Clean up after adjusting MTRRs, and enable them + * mtrr_close() - Clean up after adjusting MTRRs, and enable them * * This uses the structure containing information returned from mtrr_open(). * @@ -170,7 +170,7 @@ void mtrr_read_all(struct mtrr_info *info); int mtrr_set_valid(int cpu_select, int reg, bool valid); /** - * mtrr_set() - Set the valid flag for a selected MTRR and CPU(s) + * mtrr_set() - Set the base address and mask for a selected MTRR and CPU(s) * * @cpu_select: Selected CPUs (either a CPU number or MP_SELECT_...) * @reg: MTRR register to write (0-7) From dfadb946f65b97c4607ccb8be64fca16672e9ea2 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 25 Mar 2021 15:49:18 +0100 Subject: [PATCH 3/6] pci: add common Designware PCIe functions With the introduction of pcie_dw_rockchip, and need to support the DW PCIe in the Amlogic AXG & G12 SoCs, most of the DW PCIe helpers would be duplicated. This introduce a "common" DW PCIe helpers file with common code merged from the dw_ti and dw_rockchip drivers and adapted to fit with the upcoming dw_meson. The following changes will switch the dw_ti and dw_rockchip to use these helpers. Signed-off-by: Neil Armstrong Tested-by: Green Wan [bmeng: remove the blank line at EOF of drivers/pci/pcie_dw_common.c] Signed-off-by: Bin Meng --- drivers/pci/Kconfig | 4 + drivers/pci/Makefile | 1 + drivers/pci/pcie_dw_common.c | 365 +++++++++++++++++++++++++++++++++++ drivers/pci/pcie_dw_common.h | 155 +++++++++++++++ 4 files changed, 525 insertions(+) create mode 100644 drivers/pci/pcie_dw_common.c create mode 100644 drivers/pci/pcie_dw_common.h diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index ba41787f64..ab5a5e7ed6 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -258,6 +258,10 @@ config PCI_MVEBU Say Y here if you want to enable PCIe controller support on Armada XP/38x SoCs. +config PCIE_DW_COMMON + bool + select DM_PCI + config PCI_KEYSTONE bool "TI Keystone PCIe controller" depends on DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 5ed94bc95c..e3ca8b27e4 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -45,6 +45,7 @@ obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \ obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o obj-$(CONFIG_PCI_PHYTIUM) += pcie_phytium.o obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o +obj-$(CONFIG_PCIE_DW_COMMON) += pcie_dw_common.o obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o diff --git a/drivers/pci/pcie_dw_common.c b/drivers/pci/pcie_dw_common.c new file mode 100644 index 0000000000..785fd3aad0 --- /dev/null +++ b/drivers/pci/pcie_dw_common.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 BayLibre, SAS + * Author: Neil Armstrong + * + * Copyright (c) 2021 Rockchip, Inc. + * + * Copyright (C) 2018 Texas Instruments, Inc + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pcie_dw_common.h" + +int pcie_dw_get_link_speed(struct pcie_dw *pci) +{ + return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & + PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF; +} + +int pcie_dw_get_link_width(struct pcie_dw *pci) +{ + return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & + PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF; +} + +static void dw_pcie_writel_ob_unroll(struct pcie_dw *pci, u32 index, u32 reg, + u32 val) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + void __iomem *base = pci->atu_base; + + writel(val, base + offset + reg); +} + +static u32 dw_pcie_readl_ob_unroll(struct pcie_dw *pci, u32 index, u32 reg) +{ + u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); + void __iomem *base = pci->atu_base; + + return readl(base + offset + reg); +} + +/** + * pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses + * + * @pcie: Pointer to the PCI controller state + * @index: ATU region index + * @type: ATU accsess type + * @cpu_addr: the physical address for the translation entry + * @pci_addr: the pcie bus address for the translation entry + * @size: the size of the translation entry + * + * Return: 0 is successful and -1 is failure + */ +int pcie_dw_prog_outbound_atu_unroll(struct pcie_dw *pci, int index, + int type, u64 cpu_addr, + u64 pci_addr, u32 size) +{ + u32 retries, val; + + dev_dbg(pci->dev, "ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n", + index, type, cpu_addr, pci_addr, size); + + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, + lower_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, + upper_32_bits(cpu_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, + lower_32_bits(cpu_addr + size - 1)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, + lower_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, + upper_32_bits(pci_addr)); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, + type); + dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, + PCIE_ATU_ENABLE); + + /* + * Make sure ATU enable takes effect before any subsequent config + * and I/O accesses. + */ + for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { + val = dw_pcie_readl_ob_unroll(pci, index, + PCIE_ATU_UNR_REGION_CTRL2); + if (val & PCIE_ATU_ENABLE) + return 0; + + udelay(LINK_WAIT_IATU); + } + dev_err(pci->dev, "outbound iATU is not being enabled\n"); + + return -1; +} + +/** + * set_cfg_address() - Configure the PCIe controller config space access + * + * @pcie: Pointer to the PCI controller state + * @d: PCI device to access + * @where: Offset in the configuration space + * + * Configures the PCIe controller to access the configuration space of + * a specific PCIe device and returns the address to use for this + * access. + * + * Return: Address that can be used to access the configation space + * of the requested device / offset + */ +static uintptr_t set_cfg_address(struct pcie_dw *pcie, + pci_dev_t d, uint where) +{ + int bus = PCI_BUS(d) - pcie->first_busno; + uintptr_t va_address; + u32 atu_type; + int ret; + + /* Use dbi_base for own configuration read and write */ + if (!bus) { + va_address = (uintptr_t)pcie->dbi_base; + goto out; + } + + if (bus == 1) + /* + * For local bus whose primary bus number is root bridge, + * change TLP Type field to 4. + */ + atu_type = PCIE_ATU_TYPE_CFG0; + else + /* Otherwise, change TLP Type field to 5. */ + atu_type = PCIE_ATU_TYPE_CFG1; + + /* + * Not accessing root port configuration space? + * Region #0 is used for Outbound CFG space access. + * Direction = Outbound + * Region Index = 0 + */ + d = PCI_MASK_BUS(d); + d = PCI_ADD_BUS(bus, d); + ret = pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, + atu_type, (u64)pcie->cfg_base, + d << 8, pcie->cfg_size); + if (ret) + return (uintptr_t)ret; + + va_address = (uintptr_t)pcie->cfg_base; + +out: + va_address += where & ~0x3; + + return va_address; +} + +/** + * pcie_dw_addr_valid() - Check for valid bus address + * + * @d: The PCI device to access + * @first_busno: Bus number of the PCIe controller root complex + * + * Return 1 (true) if the PCI device can be accessed by this controller. + * + * Return: 1 on valid, 0 on invalid + */ +static int pcie_dw_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; +} + +/** + * pcie_dw_read_config() - Read from configuration space + * + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @valuep: A pointer at which to store the read value + * @size: Indicates the size of access to perform + * + * Read a value of size @size from offset @offset within the configuration + * space of the device identified by the bus, device & function numbers in @bdf + * on the PCI bus @bus. + * + * Return: 0 on success + */ +int pcie_dw_read_config(const struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct pcie_dw *pcie = dev_get_priv(bus); + uintptr_t va_address; + ulong value; + + dev_dbg(pcie->dev, "PCIE CFG read: bdf=%2x:%2x:%2x ", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + + if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { + debug("- out of range\n"); + *valuep = pci_get_ff(size); + return 0; + } + + va_address = set_cfg_address(pcie, bdf, offset); + + value = readl(va_address); + + debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); + *valuep = pci_conv_32_to_size(value, offset, size); + + return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); +} + +/** + * pcie_dw_write_config() - Write to configuration space + * + * @bus: Pointer to the PCI bus + * @bdf: Identifies the PCIe device to access + * @offset: The offset into the device's configuration space + * @value: The value to write + * @size: Indicates the size of access to perform + * + * Write the value @value of size @size from offset @offset within the + * configuration space of the device identified by the bus, device & function + * numbers in @bdf on the PCI bus @bus. + * + * Return: 0 on success + */ +int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct pcie_dw *pcie = dev_get_priv(bus); + uintptr_t va_address; + ulong old; + + dev_dbg(pcie->dev, "PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", + PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); + dev_dbg(pcie->dev, "(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); + + if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { + debug("- out of range\n"); + return 0; + } + + va_address = set_cfg_address(pcie, bdf, offset); + + old = readl(va_address); + value = pci_conv_size_to_32(old, value, offset, size); + writel(value, va_address); + + return pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, + PCIE_ATU_TYPE_IO, pcie->io.phys_start, + pcie->io.bus_start, pcie->io.size); +} + +/** + * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion + * + * @pcie: Pointer to the PCI controller state + * + * Configure the host BARs of the PCIe controller root port so that + * PCI(e) devices may access the system memory. + */ +void pcie_dw_setup_host(struct pcie_dw *pci) +{ + struct udevice *ctlr = pci_get_controller(pci->dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + u32 ret; + + if (!pci->atu_base) + pci->atu_base = pci->dbi_base + DEFAULT_DBI_ATU_OFFSET; + + /* setup RC BARs */ + writel(PCI_BASE_ADDRESS_MEM_TYPE_64, + pci->dbi_base + PCI_BASE_ADDRESS_0); + writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1); + + /* setup interrupt pins */ + clrsetbits_le32(pci->dbi_base + PCI_INTERRUPT_LINE, + 0xff00, 0x100); + + /* setup bus numbers */ + clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS, + 0xffffff, 0x00ff0100); + + /* setup command register */ + clrsetbits_le32(pci->dbi_base + PCI_PRIMARY_BUS, + 0xffff, + PCI_COMMAND_IO | PCI_COMMAND_MEMORY | + PCI_COMMAND_MASTER | PCI_COMMAND_SERR); + + /* Enable write permission for the DBI read-only register */ + dw_pcie_dbi_write_enable(pci, true); + /* program correct class for RC */ + writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE); + /* Better disable write permission right after the update */ + dw_pcie_dbi_write_enable(pci, false); + + setbits_le32(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL, + PORT_LOGIC_SPEED_CHANGE); + + for (ret = 0; ret < hose->region_count; ret++) { + if (hose->regions[ret].flags == PCI_REGION_IO) { + pci->io.phys_start = hose->regions[ret].phys_start; /* IO base */ + pci->io.bus_start = hose->regions[ret].bus_start; /* IO_bus_addr */ + pci->io.size = hose->regions[ret].size; /* IO size */ + } else if (hose->regions[ret].flags == PCI_REGION_MEM) { + pci->mem.phys_start = hose->regions[ret].phys_start; /* MEM base */ + pci->mem.bus_start = hose->regions[ret].bus_start; /* MEM_bus_addr */ + pci->mem.size = hose->regions[ret].size; /* MEM size */ + } else if (hose->regions[ret].flags == PCI_REGION_PREFETCH) { + pci->prefetch.phys_start = hose->regions[ret].phys_start; /* PREFETCH base */ + pci->prefetch.bus_start = hose->regions[ret].bus_start; /* PREFETCH_bus_addr */ + pci->prefetch.size = hose->regions[ret].size; /* PREFETCH size */ + } else if (hose->regions[ret].flags == PCI_REGION_SYS_MEMORY) { + pci->cfg_base = (void *)(pci->io.phys_start - pci->io.size); + pci->cfg_size = pci->io.size; + } else { + dev_err(pci->dev, "invalid flags type!\n"); + } + } + + dev_dbg(pci->dev, "Config space: [0x%p - 0x%p, size 0x%llx]\n", + pci->cfg_base, pci->cfg_base + pci->cfg_size, + pci->cfg_size); + + dev_dbg(pci->dev, "IO space: [0x%llx - 0x%llx, size 0x%lx]\n", + pci->io.phys_start, pci->io.phys_start + pci->io.size, + pci->io.size); + + dev_dbg(pci->dev, "IO bus: [0x%lx - 0x%lx, size 0x%lx]\n", + pci->io.bus_start, pci->io.bus_start + pci->io.size, + pci->io.size); + + dev_dbg(pci->dev, "MEM space: [0x%llx - 0x%llx, size 0x%lx]\n", + pci->mem.phys_start, pci->mem.phys_start + pci->mem.size, + pci->mem.size); + + dev_dbg(pci->dev, "MEM bus: [0x%lx - 0x%lx, size 0x%lx]\n", + pci->mem.bus_start, pci->mem.bus_start + pci->mem.size, + pci->mem.size); + + if (pci->prefetch.size) { + dev_dbg(pci->dev, "PREFETCH space: [0x%llx - 0x%llx, size 0x%lx]\n", + pci->prefetch.phys_start, pci->prefetch.phys_start + pci->prefetch.size, + pci->prefetch.size); + + dev_dbg(pci->dev, "PREFETCH bus: [0x%lx - 0x%lx, size 0x%lx]\n", + pci->prefetch.bus_start, pci->prefetch.bus_start + pci->prefetch.size, + pci->prefetch.size); + } +} diff --git a/drivers/pci/pcie_dw_common.h b/drivers/pci/pcie_dw_common.h new file mode 100644 index 0000000000..6b701645af --- /dev/null +++ b/drivers/pci/pcie_dw_common.h @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2021 BayLibre, SAS + * Author: Neil Armstrong + * + * Copyright (c) 2021 Rockchip, Inc. + * + * Copyright (C) 2018 Texas Instruments, Inc + */ + +#ifndef PCIE_DW_COMMON_H +#define PCIE_DW_COMMON_H + +#define DEFAULT_DBI_ATU_OFFSET (0x3 << 20) + +/* PCI DBICS registers */ +#define PCIE_LINK_STATUS_REG 0x80 +#define PCIE_LINK_STATUS_SPEED_OFF 16 +#define PCIE_LINK_STATUS_SPEED_MASK (0xf << PCIE_LINK_STATUS_SPEED_OFF) +#define PCIE_LINK_STATUS_WIDTH_OFF 20 +#define PCIE_LINK_STATUS_WIDTH_MASK (0xf << PCIE_LINK_STATUS_WIDTH_OFF) + +/* + * iATU Unroll-specific register definitions + * From 4.80 core version the address translation will be made by unroll. + * The registers are offset from atu_base + */ +#define PCIE_ATU_UNR_REGION_CTRL1 0x00 +#define PCIE_ATU_UNR_REGION_CTRL2 0x04 +#define PCIE_ATU_UNR_LOWER_BASE 0x08 +#define PCIE_ATU_UNR_UPPER_BASE 0x0c +#define PCIE_ATU_UNR_LIMIT 0x10 +#define PCIE_ATU_UNR_LOWER_TARGET 0x14 +#define PCIE_ATU_UNR_UPPER_TARGET 0x18 + +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) +#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_ENABLE (0x1 << 31) +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) + +/* Register address builder */ +#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((region) << 9) + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU_US 10000 + +/* PCI DBICS registers */ +#define PCIE_LINK_STATUS_REG 0x80 +#define PCIE_LINK_STATUS_SPEED_OFF 16 +#define PCIE_LINK_STATUS_SPEED_MASK (0xf << PCIE_LINK_STATUS_SPEED_OFF) +#define PCIE_LINK_STATUS_WIDTH_OFF 20 +#define PCIE_LINK_STATUS_WIDTH_MASK (0xf << PCIE_LINK_STATUS_WIDTH_OFF) + +#define PCIE_LINK_CAPABILITY 0x7c +#define PCIE_LINK_CTL_2 0xa0 +#define TARGET_LINK_SPEED_MASK 0xf +#define LINK_SPEED_GEN_1 0x1 +#define LINK_SPEED_GEN_2 0x2 +#define LINK_SPEED_GEN_3 0x3 + +/* Synopsys-specific PCIe configuration registers */ +#define PCIE_PORT_LINK_CONTROL 0x710 +#define PORT_LINK_DLL_LINK_EN BIT(5) +#define PORT_LINK_FAST_LINK_MODE BIT(7) +#define PORT_LINK_MODE_MASK GENMASK(21, 16) +#define PORT_LINK_MODE(n) FIELD_PREP(PORT_LINK_MODE_MASK, n) +#define PORT_LINK_MODE_1_LANES PORT_LINK_MODE(0x1) +#define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3) +#define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7) +#define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf) + +#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80C +#define PORT_LOGIC_N_FTS_MASK GENMASK(7, 0) +#define PORT_LOGIC_SPEED_CHANGE BIT(17) +#define PORT_LOGIC_LINK_WIDTH_MASK GENMASK(12, 8) +#define PORT_LOGIC_LINK_WIDTH(n) FIELD_PREP(PORT_LOGIC_LINK_WIDTH_MASK, n) +#define PORT_LOGIC_LINK_WIDTH_1_LANES PORT_LOGIC_LINK_WIDTH(0x1) +#define PORT_LOGIC_LINK_WIDTH_2_LANES PORT_LOGIC_LINK_WIDTH(0x2) +#define PORT_LOGIC_LINK_WIDTH_4_LANES PORT_LOGIC_LINK_WIDTH(0x4) +#define PORT_LOGIC_LINK_WIDTH_8_LANES PORT_LOGIC_LINK_WIDTH(0x8) + +#define PCIE_MISC_CONTROL_1_OFF 0x8bc +#define PCIE_DBI_RO_WR_EN BIT(0) + +/* Parameters for the waiting for iATU enabled routine */ +#define LINK_WAIT_MAX_IATU_RETRIES 5 +#define LINK_WAIT_IATU 10000 + +/** + * struct pcie_dw - DW PCIe controller state + * + * @dbi_base: The base address of dbi register space + * @cfg_base: The base address of configuration space + * @atu_base: The base address of ATU space + * @cfg_size: The size of the configuration space which is needed + * as it gets written into the PCIE_ATU_LIMIT register + * @first_busno: This driver supports multiple PCIe controllers. + * first_busno stores the bus number of the PCIe root-port + * number which may vary depending on the PCIe setup + * (PEX switches etc). + * @io: The IO space for EP's BAR + * @mem: The memory space for EP's BAR + * @prefetch: The prefetch space for EP's BAR + */ +struct pcie_dw { + struct udevice *dev; + void __iomem *dbi_base; + void __iomem *cfg_base; + void __iomem *atu_base; + fdt_size_t cfg_size; + + int first_busno; + + /* IO, MEM & PREFETCH PCI regions */ + struct pci_region io; + struct pci_region mem; + struct pci_region prefetch; +}; + +int pcie_dw_get_link_speed(struct pcie_dw *pci); + +int pcie_dw_get_link_width(struct pcie_dw *pci); + +int pcie_dw_prog_outbound_atu_unroll(struct pcie_dw *pci, int index, int type, u64 cpu_addr, + u64 pci_addr, u32 size); + +int pcie_dw_read_config(const struct udevice *bus, pci_dev_t bdf, uint offset, ulong *valuep, + enum pci_size_t size); + +int pcie_dw_write_config(struct udevice *bus, pci_dev_t bdf, uint offset, ulong value, + enum pci_size_t size); + +static inline void dw_pcie_dbi_write_enable(struct pcie_dw *pci, bool en) +{ + u32 val; + + val = readl(pci->dbi_base + PCIE_MISC_CONTROL_1_OFF); + if (en) + val |= PCIE_DBI_RO_WR_EN; + else + val &= ~PCIE_DBI_RO_WR_EN; + writel(val, pci->dbi_base + PCIE_MISC_CONTROL_1_OFF); +} + +void pcie_dw_setup_host(struct pcie_dw *pci); + +#endif From 1a031829673f0f93f93b718fe1f873e9d1ca82c7 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 25 Mar 2021 15:49:19 +0100 Subject: [PATCH 4/6] pci: pcie_dw_ti: migrate to common Designware PCIe functions Migrate the dw_ti driver to use the common DW PCIe helpers. Signed-off-by: Neil Armstrong --- drivers/pci/Kconfig | 2 +- drivers/pci/pcie_dw_ti.c | 444 ++++----------------------------------- 2 files changed, 38 insertions(+), 408 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index ab5a5e7ed6..318d8fa37d 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -264,7 +264,7 @@ config PCIE_DW_COMMON config PCI_KEYSTONE bool "TI Keystone PCIe controller" - depends on DM_PCI + select PCIE_DW_COMMON help Say Y here if you want to enable PCI controller support on AM654 SoC. diff --git a/drivers/pci/pcie_dw_ti.c b/drivers/pci/pcie_dw_ti.c index 33a5c3cc20..4195a02de3 100644 --- a/drivers/pci/pcie_dw_ti.c +++ b/drivers/pci/pcie_dw_ti.c @@ -19,19 +19,13 @@ #include #include +#include "pcie_dw_common.h" + DECLARE_GLOBAL_DATA_PTR; #define PCIE_VENDORID_MASK GENMASK(15, 0) #define PCIE_DEVICEID_SHIFT 16 -/* PCI DBICS registers */ -#define PCIE_CONFIG_BAR0 0x10 -#define PCIE_LINK_STATUS_REG 0x80 -#define PCIE_LINK_STATUS_SPEED_OFF 16 -#define PCIE_LINK_STATUS_SPEED_MASK (0xf << PCIE_LINK_STATUS_SPEED_OFF) -#define PCIE_LINK_STATUS_WIDTH_OFF 20 -#define PCIE_LINK_STATUS_WIDTH_MASK (0xf << PCIE_LINK_STATUS_WIDTH_OFF) - #define PCIE_LINK_CAPABILITY 0x7c #define PCIE_LINK_CTL_2 0xa0 #define TARGET_LINK_SPEED_MASK 0xf @@ -47,46 +41,12 @@ DECLARE_GLOBAL_DATA_PTR; #define PORT_LOGIC_LTSSM_STATE_MASK 0x1f #define PORT_LOGIC_LTSSM_STATE_L0 0x11 -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80c -#define PORT_LOGIC_SPEED_CHANGE (0x1 << 17) - #define PCIE_LINK_UP_TIMEOUT_MS 100 -/* - * iATU Unroll-specific register definitions - * From 4.80 core version the address translation will be made by unroll. - * The registers are offset from atu_base - */ -#define PCIE_ATU_UNR_REGION_CTRL1 0x00 -#define PCIE_ATU_UNR_REGION_CTRL2 0x04 -#define PCIE_ATU_UNR_LOWER_BASE 0x08 -#define PCIE_ATU_UNR_UPPER_BASE 0x0c -#define PCIE_ATU_UNR_LIMIT 0x10 -#define PCIE_ATU_UNR_LOWER_TARGET 0x14 -#define PCIE_ATU_UNR_UPPER_TARGET 0x18 - -#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) -#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) -#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_ENABLE (0x1 << 31) -#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) -#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) -#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) -#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) - -/* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((region) << 9) - /* Offsets from App base */ #define PCIE_CMD_STATUS 0x04 #define LTSSM_EN_VAL BIT(0) -/* Parameters for the waiting for iATU enabled routine */ -#define LINK_WAIT_MAX_IATU_RETRIES 5 -#define LINK_WAIT_IATU 10000 #define AM654_PCIE_DEV_TYPE_MASK 0x3 #define EP 0x0 @@ -96,29 +56,13 @@ DECLARE_GLOBAL_DATA_PTR; /** * struct pcie_dw_ti - TI DW PCIe controller state * + * @pci: The common PCIe DW structure * @app_base: The base address of application register space - * @dbics_base: The base address of dbics register space - * @cfg_base: The base address of configuration space - * @atu_base: The base address of ATU space - * @cfg_size: The size of the configuration space which is needed - * as it gets written into the PCIE_ATU_LIMIT register - * @first_busno: This driver supports multiple PCIe controllers. - * first_busno stores the bus number of the PCIe root-port - * number which may vary depending on the PCIe setup - * (PEX switches etc). */ struct pcie_dw_ti { + /* Must be first member of the struct */ + struct pcie_dw dw; void *app_base; - void *dbi_base; - void *cfg_base; - void *atu_base; - fdt_size_t cfg_size; - int first_busno; - struct udevice *dev; - - /* IO and MEM PCI regions */ - struct pci_region io; - struct pci_region mem; }; enum dw_pcie_device_mode { @@ -128,261 +72,6 @@ enum dw_pcie_device_mode { DW_PCIE_RC_TYPE, }; -static int pcie_dw_get_link_speed(struct pcie_dw_ti *pci) -{ - return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & - PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF; -} - -static int pcie_dw_get_link_width(struct pcie_dw_ti *pci) -{ - return (readl(pci->dbi_base + PCIE_LINK_STATUS_REG) & - PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF; -} - -static void dw_pcie_writel_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg, - u32 val) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - void __iomem *base = pci->atu_base; - - writel(val, base + offset + reg); -} - -static u32 dw_pcie_readl_ob_unroll(struct pcie_dw_ti *pci, u32 index, u32 reg) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - void __iomem *base = pci->atu_base; - - return readl(base + offset + reg); -} - -/** - * pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses - * - * @pcie: Pointer to the PCI controller state - * @index: ATU region index - * @type: ATU accsess type - * @cpu_addr: the physical address for the translation entry - * @pci_addr: the pcie bus address for the translation entry - * @size: the size of the translation entry - */ -static void pcie_dw_prog_outbound_atu_unroll(struct pcie_dw_ti *pci, int index, - int type, u64 cpu_addr, - u64 pci_addr, u32 size) -{ - u32 retries, val; - - debug("ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n", - index, type, cpu_addr, pci_addr, size); - - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, - lower_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, - upper_32_bits(cpu_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(pci_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(pci_addr)); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, - type); - dw_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = dw_pcie_readl_ob_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); - if (val & PCIE_ATU_ENABLE) - return; - - udelay(LINK_WAIT_IATU); - } - dev_err(pci->dev, "outbound iATU is not being enabled\n"); -} - -/** - * set_cfg_address() - Configure the PCIe controller config space access - * - * @pcie: Pointer to the PCI controller state - * @d: PCI device to access - * @where: Offset in the configuration space - * - * Configures the PCIe controller to access the configuration space of - * a specific PCIe device and returns the address to use for this - * access. - * - * Return: Address that can be used to access the configation space - * of the requested device / offset - */ -static uintptr_t set_cfg_address(struct pcie_dw_ti *pcie, - pci_dev_t d, uint where) -{ - int bus = PCI_BUS(d) - pcie->first_busno; - uintptr_t va_address; - u32 atu_type; - - /* Use dbi_base for own configuration read and write */ - if (!bus) { - va_address = (uintptr_t)pcie->dbi_base; - goto out; - } - - if (bus == 1) - /* For local bus, change TLP Type field to 4. */ - atu_type = PCIE_ATU_TYPE_CFG0; - else - /* Otherwise, change TLP Type field to 5. */ - atu_type = PCIE_ATU_TYPE_CFG1; - - /* - * Not accessing root port configuration space? - * Region #0 is used for Outbound CFG space access. - * Direction = Outbound - * Region Index = 0 - */ - d = PCI_MASK_BUS(d); - d = PCI_ADD_BUS(bus, d); - pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, - atu_type, (u64)pcie->cfg_base, - d << 8, pcie->cfg_size); - - va_address = (uintptr_t)pcie->cfg_base; - -out: - va_address += where & ~0x3; - - return va_address; -} - -/** - * pcie_dw_addr_valid() - Check for valid bus address - * - * @d: The PCI device to access - * @first_busno: Bus number of the PCIe controller root complex - * - * Return 1 (true) if the PCI device can be accessed by this controller. - * - * Return: 1 on valid, 0 on invalid - */ -static int pcie_dw_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; -} - -/** - * pcie_dw_ti_read_config() - Read from configuration space - * - * @bus: Pointer to the PCI bus - * @bdf: Identifies the PCIe device to access - * @offset: The offset into the device's configuration space - * @valuep: A pointer at which to store the read value - * @size: Indicates the size of access to perform - * - * Read a value of size @size from offset @offset within the configuration - * space of the device identified by the bus, device & function numbers in @bdf - * on the PCI bus @bus. - * - * Return: 0 on success - */ -static int pcie_dw_ti_read_config(const struct udevice *bus, pci_dev_t bdf, - uint offset, ulong *valuep, - enum pci_size_t size) -{ - struct pcie_dw_ti *pcie = dev_get_priv(bus); - uintptr_t va_address; - ulong value; - - debug("PCIE CFG read: bdf=%2x:%2x:%2x ", - PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - - if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { - debug("- out of range\n"); - *valuep = pci_get_ff(size); - return 0; - } - - va_address = set_cfg_address(pcie, bdf, offset); - - value = readl(va_address); - - debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); - *valuep = pci_conv_32_to_size(value, offset, size); - - pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pcie->io.phys_start, - pcie->io.bus_start, pcie->io.size); - - return 0; -} - -/** - * pcie_dw_ti_write_config() - Write to configuration space - * - * @bus: Pointer to the PCI bus - * @bdf: Identifies the PCIe device to access - * @offset: The offset into the device's configuration space - * @value: The value to write - * @size: Indicates the size of access to perform - * - * Write the value @value of size @size from offset @offset within the - * configuration space of the device identified by the bus, device & function - * numbers in @bdf on the PCI bus @bus. - * - * Return: 0 on success - */ -static int pcie_dw_ti_write_config(struct udevice *bus, pci_dev_t bdf, - uint offset, ulong value, - enum pci_size_t size) -{ - struct pcie_dw_ti *pcie = dev_get_priv(bus); - uintptr_t va_address; - ulong old; - - debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ", - PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); - - if (!pcie_dw_addr_valid(bdf, pcie->first_busno)) { - debug("- out of range\n"); - return 0; - } - - va_address = set_cfg_address(pcie, bdf, offset); - - old = readl(va_address); - value = pci_conv_size_to_32(old, value, offset, size); - writel(value, va_address); - - pcie_dw_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, pcie->io.phys_start, - pcie->io.bus_start, pcie->io.size); - - return 0; -} - -static inline void dw_pcie_dbi_write_enable(struct pcie_dw_ti *pci, bool en) -{ - u32 val; - - val = readl(pci->dbi_base + PCIE_MISC_CONTROL_1_OFF); - if (en) - val |= PCIE_DBI_RO_WR_EN; - else - val &= ~PCIE_DBI_RO_WR_EN; - writel(val, pci->dbi_base + PCIE_MISC_CONTROL_1_OFF); -} - /** * pcie_dw_configure() - Configure link capabilities and speed * @@ -395,19 +84,19 @@ static void pcie_dw_configure(struct pcie_dw_ti *pci, u32 cap_speed) { u32 val; - dw_pcie_dbi_write_enable(pci, true); + dw_pcie_dbi_write_enable(&pci->dw, true); - val = readl(pci->dbi_base + PCIE_LINK_CAPABILITY); + val = readl(pci->dw.dbi_base + PCIE_LINK_CAPABILITY); val &= ~TARGET_LINK_SPEED_MASK; val |= cap_speed; - writel(val, pci->dbi_base + PCIE_LINK_CAPABILITY); + writel(val, pci->dw.dbi_base + PCIE_LINK_CAPABILITY); - val = readl(pci->dbi_base + PCIE_LINK_CTL_2); + val = readl(pci->dw.dbi_base + PCIE_LINK_CTL_2); val &= ~TARGET_LINK_SPEED_MASK; val |= cap_speed; - writel(val, pci->dbi_base + PCIE_LINK_CTL_2); + writel(val, pci->dw.dbi_base + PCIE_LINK_CTL_2); - dw_pcie_dbi_write_enable(pci, false); + dw_pcie_dbi_write_enable(&pci->dw, false); } /** @@ -421,7 +110,7 @@ static int is_link_up(struct pcie_dw_ti *pci) { u32 val; - val = readl(pci->dbi_base + PCIE_PORT_DEBUG0); + val = readl(pci->dw.dbi_base + PCIE_PORT_DEBUG0); val &= PORT_LOGIC_LTSSM_STATE_MASK; return (val == PORT_LOGIC_LTSSM_STATE_L0); @@ -477,56 +166,6 @@ static int pcie_dw_ti_pcie_link_up(struct pcie_dw_ti *pci, u32 cap_speed) return 1; } -/** - * pcie_dw_setup_host() - Setup the PCIe controller for RC opertaion - * - * @pcie: Pointer to the PCI controller state - * - * Configure the host BARs of the PCIe controller root port so that - * PCI(e) devices may access the system memory. - */ -static void pcie_dw_setup_host(struct pcie_dw_ti *pci) -{ - u32 val; - - /* setup RC BARs */ - writel(PCI_BASE_ADDRESS_MEM_TYPE_64, - pci->dbi_base + PCI_BASE_ADDRESS_0); - writel(0x0, pci->dbi_base + PCI_BASE_ADDRESS_1); - - /* setup interrupt pins */ - dw_pcie_dbi_write_enable(pci, true); - val = readl(pci->dbi_base + PCI_INTERRUPT_LINE); - val &= 0xffff00ff; - val |= 0x00000100; - writel(val, pci->dbi_base + PCI_INTERRUPT_LINE); - dw_pcie_dbi_write_enable(pci, false); - - /* setup bus numbers */ - val = readl(pci->dbi_base + PCI_PRIMARY_BUS); - val &= 0xff000000; - val |= 0x00ff0100; - writel(val, pci->dbi_base + PCI_PRIMARY_BUS); - - /* setup command register */ - val = readl(pci->dbi_base + PCI_COMMAND); - val &= 0xffff0000; - val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR; - writel(val, pci->dbi_base + PCI_COMMAND); - - /* Enable write permission for the DBI read-only register */ - dw_pcie_dbi_write_enable(pci, true); - /* program correct class for RC */ - writew(PCI_CLASS_BRIDGE_PCI, pci->dbi_base + PCI_CLASS_DEVICE); - /* Better disable write permission right after the update */ - dw_pcie_dbi_write_enable(pci, false); - - val = readl(pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); - val |= PORT_LOGIC_SPEED_CHANGE; - writel(val, pci->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); -} - static int pcie_am654_set_mode(struct pcie_dw_ti *pci, enum dw_pcie_device_mode mode) { @@ -535,7 +174,7 @@ static int pcie_am654_set_mode(struct pcie_dw_ti *pci, u32 mask; int ret; - syscon = syscon_regmap_lookup_by_phandle(pci->dev, + syscon = syscon_regmap_lookup_by_phandle(pci->dw.dev, "ti,syscon-pcie-mode"); if (IS_ERR(syscon)) return 0; @@ -550,13 +189,13 @@ static int pcie_am654_set_mode(struct pcie_dw_ti *pci, val = EP; break; default: - dev_err(pci->dev, "INVALID device type %d\n", mode); + dev_err(pci->dw.dev, "INVALID device type %d\n", mode); return -EINVAL; } ret = regmap_update_bits(syscon, 0, mask, val); if (ret) { - dev_err(pci->dev, "failed to set pcie mode\n"); + dev_err(pci->dw.dev, "failed to set pcie mode\n"); return ret; } @@ -569,7 +208,7 @@ static int pcie_dw_init_id(struct pcie_dw_ti *pci) unsigned int id; int ret; - devctrl_regs = syscon_regmap_lookup_by_phandle(pci->dev, + devctrl_regs = syscon_regmap_lookup_by_phandle(pci->dw.dev, "ti,syscon-pcie-id"); if (IS_ERR(devctrl_regs)) return PTR_ERR(devctrl_regs); @@ -578,10 +217,10 @@ static int pcie_dw_init_id(struct pcie_dw_ti *pci) if (ret) return ret; - dw_pcie_dbi_write_enable(pci, true); - writew(id & PCIE_VENDORID_MASK, pci->dbi_base + PCI_VENDOR_ID); - writew(id >> PCIE_DEVICEID_SHIFT, pci->dbi_base + PCI_DEVICE_ID); - dw_pcie_dbi_write_enable(pci, false); + dw_pcie_dbi_write_enable(&pci->dw, true); + writew(id & PCIE_VENDORID_MASK, pci->dw.dbi_base + PCI_VENDOR_ID); + writew(id >> PCIE_DEVICEID_SHIFT, pci->dw.dbi_base + PCI_DEVICE_ID); + dw_pcie_dbi_write_enable(&pci->dw, false); return 0; } @@ -635,10 +274,10 @@ static int pcie_dw_ti_probe(struct udevice *dev) generic_phy_init(&phy1); generic_phy_power_on(&phy1); - pci->first_busno = dev_seq(dev); - pci->dev = dev; + pci->dw.first_busno = dev_seq(dev); + pci->dw.dev = dev; - pcie_dw_setup_host(pci); + pcie_dw_setup_host(&pci->dw); pcie_dw_init_id(pci); if (device_is_compatible(dev, "ti,am654-pcie-rc")) @@ -650,23 +289,14 @@ static int pcie_dw_ti_probe(struct udevice *dev) } printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev_seq(dev), - pcie_dw_get_link_speed(pci), - pcie_dw_get_link_width(pci), + pcie_dw_get_link_speed(&pci->dw), + pcie_dw_get_link_width(&pci->dw), hose->first_busno); - /* Store the IO and MEM windows settings for future use by the ATU */ - pci->io.phys_start = hose->regions[0].phys_start; /* IO base */ - pci->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */ - pci->io.size = hose->regions[0].size; /* IO size */ - - pci->mem.phys_start = hose->regions[1].phys_start; /* MEM base */ - pci->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */ - pci->mem.size = hose->regions[1].size; /* MEM size */ - - pcie_dw_prog_outbound_atu_unroll(pci, PCIE_ATU_REGION_INDEX0, + pcie_dw_prog_outbound_atu_unroll(&pci->dw, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, - pci->mem.phys_start, - pci->mem.bus_start, pci->mem.size); + pci->dw.mem.phys_start, + pci->dw.mem.bus_start, pci->dw.mem.size); return 0; } @@ -687,19 +317,19 @@ static int pcie_dw_ti_of_to_plat(struct udevice *dev) struct pcie_dw_ti *pcie = dev_get_priv(dev); /* Get the controller base address */ - pcie->dbi_base = (void *)dev_read_addr_name(dev, "dbics"); - if ((fdt_addr_t)pcie->dbi_base == FDT_ADDR_T_NONE) + pcie->dw.dbi_base = (void *)dev_read_addr_name(dev, "dbics"); + if ((fdt_addr_t)pcie->dw.dbi_base == FDT_ADDR_T_NONE) return -EINVAL; /* Get the config space base address and size */ - pcie->cfg_base = (void *)dev_read_addr_size_name(dev, "config", - &pcie->cfg_size); - if ((fdt_addr_t)pcie->cfg_base == FDT_ADDR_T_NONE) + pcie->dw.cfg_base = (void *)dev_read_addr_size_name(dev, "config", + &pcie->dw.cfg_size); + if ((fdt_addr_t)pcie->dw.cfg_base == FDT_ADDR_T_NONE) return -EINVAL; /* Get the iATU base address and size */ - pcie->atu_base = (void *)dev_read_addr_name(dev, "atu"); - if ((fdt_addr_t)pcie->atu_base == FDT_ADDR_T_NONE) + pcie->dw.atu_base = (void *)dev_read_addr_name(dev, "atu"); + if ((fdt_addr_t)pcie->dw.atu_base == FDT_ADDR_T_NONE) return -EINVAL; /* Get the app base address and size */ @@ -711,8 +341,8 @@ static int pcie_dw_ti_of_to_plat(struct udevice *dev) } static const struct dm_pci_ops pcie_dw_ti_ops = { - .read_config = pcie_dw_ti_read_config, - .write_config = pcie_dw_ti_write_config, + .read_config = pcie_dw_read_config, + .write_config = pcie_dw_write_config, }; static const struct udevice_id pcie_dw_ti_ids[] = { From c90f3d0e70568e65ca356ea519231629ddf459ed Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 25 Mar 2021 15:49:20 +0100 Subject: [PATCH 5/6] pci: pcie_dw_rockchip: migrate to common Designware PCIe functions Migrate the dw_rockchip driver to use the common DW PCIe helpers. Signed-off-by: Neil Armstrong --- drivers/pci/Kconfig | 2 +- drivers/pci/pcie_dw_rockchip.c | 472 ++------------------------------- 2 files changed, 30 insertions(+), 444 deletions(-) diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 318d8fa37d..cacfc4bd25 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -289,7 +289,7 @@ config PCIE_ROCKCHIP config PCIE_DW_ROCKCHIP bool "Rockchip DesignWare based PCIe controller" depends on ARCH_ROCKCHIP - select DM_PCI + select PCIE_DW_COMMON select PHY_ROCKCHIP_SNPS_PCIE3 help Say Y here if you want to enable DW PCIe controller support on diff --git a/drivers/pci/pcie_dw_rockchip.c b/drivers/pci/pcie_dw_rockchip.c index 77f1a1b48f..bc22af4230 100644 --- a/drivers/pci/pcie_dw_rockchip.c +++ b/drivers/pci/pcie_dw_rockchip.c @@ -22,39 +22,26 @@ #include #include +#include "pcie_dw_common.h" + DECLARE_GLOBAL_DATA_PTR; /** * struct rk_pcie - RK DW PCIe controller state * * @vpcie3v3: The 3.3v power supply for slot - * @dbi_base: The base address of dwc core regs * @apb_base: The base address of vendor regs - * @cfg_base: The base address of config header space - * @cfg_size: The size of the configuration space which is needed - * as it gets written into the PCIE_ATU_LIMIT register - * @first_busno: This driver supports multiple PCIe controllers. - * first_busno stores the bus number of the PCIe root-port - * number which may vary depending on the PCIe setup - * (PEX switches etc). * @rst_gpio: The #PERST signal for slot - * @io: The IO space for EP's BAR - * @mem: The memory space for EP's BAR */ struct rk_pcie { - struct udevice *dev; + /* Must be first member of the struct */ + struct pcie_dw dw; struct udevice *vpcie3v3; - void *dbi_base; void *apb_base; - void *cfg_base; - fdt_size_t cfg_size; struct phy phy; struct clk_bulk clks; - int first_busno; struct reset_ctl_bulk rsts; struct gpio_desc rst_gpio; - struct pci_region io; - struct pci_region mem; }; /* Parameters for the waiting for iATU enabled routine */ @@ -73,59 +60,6 @@ struct rk_pcie { #define PCIE_CLIENT_DBG_TRANSITION_DATA 0xffff0000 #define PCIE_CLIENT_DBF_EN 0xffff0003 -/* PCI DBICS registers */ -#define PCIE_LINK_STATUS_REG 0x80 -#define PCIE_LINK_STATUS_SPEED_OFF 16 -#define PCIE_LINK_STATUS_SPEED_MASK (0xf << PCIE_LINK_STATUS_SPEED_OFF) -#define PCIE_LINK_STATUS_WIDTH_OFF 20 -#define PCIE_LINK_STATUS_WIDTH_MASK (0xf << PCIE_LINK_STATUS_WIDTH_OFF) - -#define PCIE_LINK_CAPABILITY 0x7c -#define PCIE_LINK_CTL_2 0xa0 -#define TARGET_LINK_SPEED_MASK 0xf -#define LINK_SPEED_GEN_1 0x1 -#define LINK_SPEED_GEN_2 0x2 -#define LINK_SPEED_GEN_3 0x3 - -#define PCIE_MISC_CONTROL_1_OFF 0x8bc -#define PCIE_DBI_RO_WR_EN BIT(0) - -#define PCIE_LINK_WIDTH_SPEED_CONTROL 0x80c -#define PORT_LOGIC_SPEED_CHANGE BIT(17) - -/* - * iATU Unroll-specific register definitions - * From 4.80 core version the address translation will be made by unroll. - * The registers are offset from atu_base - */ -#define PCIE_ATU_UNR_REGION_CTRL1 0x00 -#define PCIE_ATU_UNR_REGION_CTRL2 0x04 -#define PCIE_ATU_UNR_LOWER_BASE 0x08 -#define PCIE_ATU_UNR_UPPER_BASE 0x0c -#define PCIE_ATU_UNR_LIMIT 0x10 -#define PCIE_ATU_UNR_LOWER_TARGET 0x14 -#define PCIE_ATU_UNR_UPPER_TARGET 0x18 - -#define PCIE_ATU_REGION_INDEX1 (0x1 << 0) -#define PCIE_ATU_REGION_INDEX0 (0x0 << 0) -#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_ENABLE (0x1 << 31) -#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30) -#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24) -#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19) -#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16) - -/* Register address builder */ -#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) \ - ((0x3 << 20) | ((region) << 9)) - -/* Parameters for the waiting for iATU enabled routine */ -#define LINK_WAIT_MAX_IATU_RETRIES 5 -#define LINK_WAIT_IATU_US 10000 - /* Parameters for the waiting for #perst signal */ #define PERST_WAIT_MS 1000 @@ -175,7 +109,7 @@ static u32 __rk_pcie_read_apb(struct rk_pcie *rk_pcie, void __iomem *base, ret = rk_pcie_read(base + reg, size, &val); if (ret) - dev_err(rk_pcie->dev, "Read APB address failed\n"); + dev_err(rk_pcie->dw.dev, "Read APB address failed\n"); return val; } @@ -187,7 +121,7 @@ static void __rk_pcie_write_apb(struct rk_pcie *rk_pcie, void __iomem *base, ret = rk_pcie_write(base + reg, size, val); if (ret) - dev_err(rk_pcie->dev, "Write APB address failed\n"); + dev_err(rk_pcie->dw.dev, "Write APB address failed\n"); } /** @@ -214,91 +148,6 @@ static inline void rk_pcie_writel_apb(struct rk_pcie *rk_pcie, u32 reg, __rk_pcie_write_apb(rk_pcie, rk_pcie->apb_base, reg, 0x4, val); } -static int rk_pcie_get_link_speed(struct rk_pcie *rk_pcie) -{ - return (readl(rk_pcie->dbi_base + PCIE_LINK_STATUS_REG) & - PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF; -} - -static int rk_pcie_get_link_width(struct rk_pcie *rk_pcie) -{ - return (readl(rk_pcie->dbi_base + PCIE_LINK_STATUS_REG) & - PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF; -} - -static void rk_pcie_writel_ob_unroll(struct rk_pcie *rk_pcie, u32 index, - u32 reg, u32 val) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - void __iomem *base = rk_pcie->dbi_base; - - writel(val, base + offset + reg); -} - -static u32 rk_pcie_readl_ob_unroll(struct rk_pcie *rk_pcie, u32 index, u32 reg) -{ - u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index); - void __iomem *base = rk_pcie->dbi_base; - - return readl(base + offset + reg); -} - -static inline void rk_pcie_dbi_write_enable(struct rk_pcie *rk_pcie, bool en) -{ - u32 val; - - val = readl(rk_pcie->dbi_base + PCIE_MISC_CONTROL_1_OFF); - - if (en) - val |= PCIE_DBI_RO_WR_EN; - else - val &= ~PCIE_DBI_RO_WR_EN; - writel(val, rk_pcie->dbi_base + PCIE_MISC_CONTROL_1_OFF); -} - -/** - * rockchip_pcie_setup_host() - Setup the PCIe controller for RC opertaion - * - * @rk_pcie: Pointer to the PCI controller state - * - * Configure the host BARs of the PCIe controller root port so that - * PCI(e) devices may access the system memory. - */ -static void rk_pcie_setup_host(struct rk_pcie *rk_pcie) -{ - u32 val; - - rk_pcie_dbi_write_enable(rk_pcie, true); - - /* setup RC BARs */ - writel(PCI_BASE_ADDRESS_MEM_TYPE_64, - rk_pcie->dbi_base + PCI_BASE_ADDRESS_0); - writel(0x0, rk_pcie->dbi_base + PCI_BASE_ADDRESS_1); - - /* setup interrupt pins */ - clrsetbits_le32(rk_pcie->dbi_base + PCI_INTERRUPT_LINE, - 0xff00, 0x100); - - /* setup bus numbers */ - clrsetbits_le32(rk_pcie->dbi_base + PCI_PRIMARY_BUS, - 0xffffff, 0x00ff0100); - - /* setup command register */ - clrsetbits_le32(rk_pcie->dbi_base + PCI_PRIMARY_BUS, - 0xffff, - PCI_COMMAND_IO | PCI_COMMAND_MEMORY | - PCI_COMMAND_MASTER | PCI_COMMAND_SERR); - - /* program correct class for RC */ - writew(PCI_CLASS_BRIDGE_PCI, rk_pcie->dbi_base + PCI_CLASS_DEVICE); - /* Better disable write permission right after the update */ - - setbits_le32(rk_pcie->dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL, - PORT_LOGIC_SPEED_CHANGE) - - rk_pcie_dbi_write_enable(rk_pcie, false); -} - /** * rk_pcie_configure() - Configure link capabilities and speed * @@ -311,242 +160,15 @@ static void rk_pcie_configure(struct rk_pcie *pci, u32 cap_speed) { u32 val; - rk_pcie_dbi_write_enable(pci, true); + dw_pcie_dbi_write_enable(&pci->dw, true); - clrsetbits_le32(pci->dbi_base + PCIE_LINK_CAPABILITY, + clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CAPABILITY, TARGET_LINK_SPEED_MASK, cap_speed); - clrsetbits_le32(pci->dbi_base + PCIE_LINK_CTL_2, + clrsetbits_le32(pci->dw.dbi_base + PCIE_LINK_CTL_2, TARGET_LINK_SPEED_MASK, cap_speed); - rk_pcie_dbi_write_enable(pci, false); -} - -/** - * rk_pcie_dw_prog_outbound_atu_unroll() - Configure ATU for outbound accesses - * - * @rk_pcie: Pointer to the PCI controller state - * @index: ATU region index - * @type: ATU accsess type - * @cpu_addr: the physical address for the translation entry - * @pci_addr: the pcie bus address for the translation entry - * @size: the size of the translation entry - * - * Return: 0 is successful and -1 is failure - */ -static int rk_pcie_prog_outbound_atu_unroll(struct rk_pcie *pci, int index, - int type, u64 cpu_addr, - u64 pci_addr, u32 size) -{ - u32 retries, val; - - dev_dbg(pci->dev, "ATU programmed with: index: %d, type: %d, cpu addr: %8llx, pci addr: %8llx, size: %8x\n", - index, type, cpu_addr, pci_addr, size); - - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_BASE, - lower_32_bits(cpu_addr)); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_BASE, - upper_32_bits(cpu_addr)); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LIMIT, - lower_32_bits(cpu_addr + size - 1)); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_LOWER_TARGET, - lower_32_bits(pci_addr)); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_UPPER_TARGET, - upper_32_bits(pci_addr)); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL1, - type); - rk_pcie_writel_ob_unroll(pci, index, PCIE_ATU_UNR_REGION_CTRL2, - PCIE_ATU_ENABLE); - - /* - * Make sure ATU enable takes effect before any subsequent config - * and I/O accesses. - */ - for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { - val = rk_pcie_readl_ob_unroll(pci, index, - PCIE_ATU_UNR_REGION_CTRL2); - if (val & PCIE_ATU_ENABLE) - return 0; - - udelay(LINK_WAIT_IATU_US); - } - dev_err(pci->dev, "outbound iATU is not being enabled\n"); - - return -1; -} - -/** - * rk_pcie_dw_addr_valid() - Check for valid bus address - * - * @d: The PCI device to access - * @first_busno: Bus number of the PCIe controller root complex - * - * Return 1 (true) if the PCI device can be accessed by this controller. - * - * Return: 1 on valid, 0 on invalid - */ -static int rk_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; -} - -/** - * set_cfg_address() - Configure the PCIe controller config space access - * - * @rk_pcie: Pointer to the PCI controller state - * @d: PCI device to access - * @where: Offset in the configuration space - * - * Configures the PCIe controller to access the configuration space of - * a specific PCIe device and returns the address to use for this - * access. - * - * Return: Address that can be used to access the configation space - * of the requested device / offset - */ -static uintptr_t set_cfg_address(struct rk_pcie *pcie, - pci_dev_t d, uint where) -{ - int rel_bus = PCI_BUS(d) - pcie->first_busno; - uintptr_t va_address; - u32 atu_type; - int ret; - - /* Use dbi_base for own configuration read and write */ - if (!rel_bus) { - va_address = (uintptr_t)pcie->dbi_base; - goto out; - } - - if (rel_bus == 1) - /* - * For local bus whose primary bus number is root bridge, - * change TLP Type field to 4. - */ - atu_type = PCIE_ATU_TYPE_CFG0; - else - /* Otherwise, change TLP Type field to 5. */ - atu_type = PCIE_ATU_TYPE_CFG1; - - /* - * Not accessing root port configuration space? - * Region #0 is used for Outbound CFG space access. - * Direction = Outbound - * Region Index = 0 - */ - d = PCI_MASK_BUS(d); - d = PCI_ADD_BUS(rel_bus, d); - ret = rk_pcie_prog_outbound_atu_unroll(pcie, PCIE_ATU_REGION_INDEX1, - atu_type, (u64)pcie->cfg_base, - d << 8, pcie->cfg_size); - if (ret) - return (uintptr_t)ret; - - va_address = (uintptr_t)pcie->cfg_base; - -out: - va_address += where & ~0x3; - - return va_address; -} - -/** - * rockchip_pcie_rd_conf() - Read from configuration space - * - * @bus: Pointer to the PCI bus - * @bdf: Identifies the PCIe device to access - * @offset: The offset into the device's configuration space - * @valuep: A pointer at which to store the read value - * @size: Indicates the size of access to perform - * - * Read a value of size @size from offset @offset within the configuration - * space of the device identified by the bus, device & function numbers in @bdf - * on the PCI bus @bus. - * - * Return: 0 on success - */ -static int rockchip_pcie_rd_conf(const struct udevice *bus, pci_dev_t bdf, - uint offset, ulong *valuep, - enum pci_size_t size) -{ - struct rk_pcie *pcie = dev_get_priv(bus); - uintptr_t va_address; - ulong value; - - debug("PCIE CFG read: bdf=%2x:%2x:%2x\n", - PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - - if (!rk_pcie_addr_valid(bdf, pcie->first_busno)) { - debug("- out of range\n"); - *valuep = pci_get_ff(size); - return 0; - } - - va_address = set_cfg_address(pcie, bdf, offset); - - value = readl(va_address); - - debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); - *valuep = pci_conv_32_to_size(value, offset, size); - - return rk_pcie_prog_outbound_atu_unroll(pcie, - PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, - pcie->io.phys_start, - pcie->io.bus_start, - pcie->io.size); -} - -/** - * rockchip_pcie_wr_conf() - Write to configuration space - * - * @bus: Pointer to the PCI bus - * @bdf: Identifies the PCIe device to access - * @offset: The offset into the device's configuration space - * @value: The value to write - * @size: Indicates the size of access to perform - * - * Write the value @value of size @size from offset @offset within the - * configuration space of the device identified by the bus, device & function - * numbers in @bdf on the PCI bus @bus. - * - * Return: 0 on success - */ -static int rockchip_pcie_wr_conf(struct udevice *bus, pci_dev_t bdf, - uint offset, ulong value, - enum pci_size_t size) -{ - struct rk_pcie *pcie = dev_get_priv(bus); - uintptr_t va_address; - ulong old; - - debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d)\n", - PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf)); - debug("(addr,val)=(0x%04x, 0x%08lx)\n", offset, value); - - if (!rk_pcie_addr_valid(bdf, pcie->first_busno)) { - debug("- out of range\n"); - return 0; - } - - va_address = set_cfg_address(pcie, bdf, offset); - - old = readl(va_address); - value = pci_conv_size_to_32(old, value, offset, size); - writel(value, va_address); - - return rk_pcie_prog_outbound_atu_unroll(pcie, - PCIE_ATU_REGION_INDEX1, - PCIE_ATU_TYPE_IO, - pcie->io.phys_start, - pcie->io.bus_start, - pcie->io.size); - + dw_pcie_dbi_write_enable(&pci->dw, false); } static void rk_pcie_enable_debug(struct rk_pcie *rk_pcie) @@ -642,19 +264,19 @@ static int rk_pcie_link_up(struct rk_pcie *priv, u32 cap_speed) for (retries = 0; retries < 5; retries++) { if (is_link_up(priv)) { - dev_info(priv->dev, "PCIe Link up, LTSSM is 0x%x\n", + dev_info(priv->dw.dev, "PCIe Link up, LTSSM is 0x%x\n", rk_pcie_readl_apb(priv, PCIE_CLIENT_LTSSM_STATUS)); rk_pcie_debug_dump(priv); return 0; } - dev_info(priv->dev, "PCIe Linking... LTSSM is 0x%x\n", + dev_info(priv->dw.dev, "PCIe Linking... LTSSM is 0x%x\n", rk_pcie_readl_apb(priv, PCIE_CLIENT_LTSSM_STATUS)); rk_pcie_debug_dump(priv); msleep(1000); } - dev_err(priv->dev, "PCIe-%d Link Fail\n", dev_seq(priv->dev)); + dev_err(priv->dw.dev, "PCIe-%d Link Fail\n", dev_seq(priv->dw.dev)); /* Link maybe in Gen switch recovery but we need to wait more 1s */ msleep(1000); return -EIO; @@ -670,7 +292,7 @@ static int rockchip_pcie_init_port(struct udevice *dev) if (priv->vpcie3v3) { ret = regulator_set_value(priv->vpcie3v3, 3300000); if (ret) { - dev_err(priv->dev, "failed to enable vpcie3v3 (ret=%d)\n", + dev_err(priv->dw.dev, "failed to enable vpcie3v3 (ret=%d)\n", ret); return ret; } @@ -709,7 +331,7 @@ static int rockchip_pcie_init_port(struct udevice *dev) /* Set RC mode */ rk_pcie_writel_apb(priv, 0x0, 0xf00040); - rk_pcie_setup_host(priv); + pcie_dw_setup_host(&priv->dw); ret = rk_pcie_link_up(priv, LINK_SPEED_GEN_3); if (ret < 0) @@ -733,11 +355,11 @@ static int rockchip_pcie_parse_dt(struct udevice *dev) struct rk_pcie *priv = dev_get_priv(dev); int ret; - priv->dbi_base = (void *)dev_read_addr_index(dev, 0); - if (!priv->dbi_base) + priv->dw.dbi_base = (void *)dev_read_addr_index(dev, 0); + if (!priv->dw.dbi_base) return -ENODEV; - dev_dbg(dev, "DBI address is 0x%p\n", priv->dbi_base); + dev_dbg(dev, "DBI address is 0x%p\n", priv->dw.dbi_base); priv->apb_base = (void *)dev_read_addr_index(dev, 1); if (!priv->apb_base) @@ -795,10 +417,10 @@ static int rockchip_pcie_probe(struct udevice *dev) struct rk_pcie *priv = dev_get_priv(dev); struct udevice *ctlr = pci_get_controller(dev); struct pci_controller *hose = dev_get_uclass_priv(ctlr); - int reti = 0; + int ret = 0; - priv->first_busno = dev_seq(dev); - priv->dev = dev; + priv->dw.first_busno = dev_seq(dev); + priv->dw.dev = dev; ret = rockchip_pcie_parse_dt(dev); if (ret) @@ -809,58 +431,22 @@ static int rockchip_pcie_probe(struct udevice *dev) return ret; dev_info(dev, "PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", - dev_seq(dev), rk_pcie_get_link_speed(priv), - rk_pcie_get_link_width(priv), + dev_seq(dev), pcie_dw_get_link_speed(&priv->dw), + pcie_dw_get_link_width(&priv->dw), hose->first_busno); - for (ret = 0; ret < hose->region_count; ret++) { - if (hose->regions[ret].flags == PCI_REGION_IO) { - priv->io.phys_start = hose->regions[ret].phys_start; /* IO base */ - priv->io.bus_start = hose->regions[ret].bus_start; /* IO_bus_addr */ - priv->io.size = hose->regions[ret].size; /* IO size */ - } else if (hose->regions[ret].flags == PCI_REGION_MEM) { - priv->mem.phys_start = hose->regions[ret].phys_start; /* MEM base */ - priv->mem.bus_start = hose->regions[ret].bus_start; /* MEM_bus_addr */ - priv->mem.size = hose->regions[ret].size; /* MEM size */ - } else if (hose->regions[ret].flags == PCI_REGION_SYS_MEMORY) { - priv->cfg_base = (void *)(priv->io.phys_start - priv->io.size); - priv->cfg_size = priv->io.size; - } else { - dev_err(dev, "invalid flags type!\n"); - } - } - dev_dbg(dev, "Config space: [0x%p - 0x%p, size 0x%llx]\n", - priv->cfg_base, priv->cfg_base + priv->cfg_size, - priv->cfg_size); - - dev_dbg(dev, "IO space: [0x%llx - 0x%llx, size 0x%lx]\n", - priv->io.phys_start, priv->io.phys_start + priv->io.size, - priv->io.size); - - dev_dbg(dev, "IO bus: [0x%lx - 0x%lx, size 0x%lx]\n", - priv->io.bus_start, priv->io.bus_start + priv->io.size, - priv->io.size); - - dev_dbg(dev, "MEM space: [0x%llx - 0x%llx, size 0x%lx]\n", - priv->mem.phys_start, priv->mem.phys_start + priv->mem.size, - priv->mem.size); - - dev_dbg(dev, "MEM bus: [0x%lx - 0x%lx, size 0x%lx]\n", - priv->mem.bus_start, priv->mem.bus_start + priv->mem.size, - priv->mem.size); - - return rk_pcie_prog_outbound_atu_unroll(priv, + return pcie_dw_prog_outbound_atu_unroll(&priv->dw, PCIE_ATU_REGION_INDEX0, PCIE_ATU_TYPE_MEM, - priv->mem.phys_start, - priv->mem.bus_start, - priv->mem.size); + priv->dw.mem.phys_start, + priv->dw.mem.bus_start, + priv->dw.mem.size); } static const struct dm_pci_ops rockchip_pcie_ops = { - .read_config = rockchip_pcie_rd_conf, - .write_config = rockchip_pcie_wr_conf, + .read_config = pcie_dw_read_config, + .write_config = pcie_dw_write_config, }; static const struct udevice_id rockchip_pcie_ids[] = { From 2c32c701ea6cc04881589c09cc229ee77acae723 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 25 Mar 2021 15:49:21 +0100 Subject: [PATCH 6/6] pci: add Amlogic Meson Designware PCIe controller Add support for the DW PCIe controller found in the Amlogic Meson AXG and G12 (G12A, G12B, SM1) SoCs. This uses the common DW PCIe helpers introducted previously. Signed-off-by: Neil Armstrong --- drivers/pci/Kconfig | 8 + drivers/pci/Makefile | 1 + drivers/pci/pcie_dw_meson.c | 459 ++++++++++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+) create mode 100644 drivers/pci/pcie_dw_meson.c diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index cacfc4bd25..cdcdd8f456 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -276,6 +276,14 @@ config PCIE_MEDIATEK Say Y here if you want to enable Gen2 PCIe controller, which could be found on MT7623 SoC family. +config PCIE_DW_MESON + bool "Amlogic Meson DesignWare based PCIe controller" + depends on ARCH_MESON + select PCIE_DW_COMMON + help + Say Y here if you want to enable DW PCIe controller support on + Amlogic SoCs. + config PCIE_ROCKCHIP bool "Enable Rockchip PCIe driver" depends on ARCH_ROCKCHIP diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index e3ca8b27e4..96d61821fe 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -50,5 +50,6 @@ obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o obj-$(CONFIG_PCIE_DW_ROCKCHIP) += pcie_dw_rockchip.o +obj-$(CONFIG_PCIE_DW_MESON) += pcie_dw_meson.o obj-$(CONFIG_PCI_BRCMSTB) += pcie_brcmstb.o obj-$(CONFIG_PCI_OCTEONTX) += pci_octeontx.o diff --git a/drivers/pci/pcie_dw_meson.c b/drivers/pci/pcie_dw_meson.c new file mode 100644 index 0000000000..0525ecbea6 --- /dev/null +++ b/drivers/pci/pcie_dw_meson.c @@ -0,0 +1,459 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Amlogic DesignWare based PCIe host controller driver + * + * Copyright (c) 2021 BayLibre, SAS + * Author: Neil Armstrong + * + * Based on pcie_dw_rockchip.c + * Copyright (c) 2021 Rockchip, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pcie_dw_common.h" + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct meson_pcie - Amlogic Meson DW PCIe controller state + * + * @pci: The common PCIe DW structure + * @meson_cfg_base: The base address of vendor regs + * @phy + * @clk_port + * @clk_general + * @clk_pclk + * @rsts + * @rst_gpio: The #PERST signal for slot + */ +struct meson_pcie { + /* Must be first member of the struct */ + struct pcie_dw dw; + void *meson_cfg_base; + struct phy phy; + struct clk clk_port; + struct clk clk_general; + struct clk clk_pclk; + struct reset_ctl_bulk rsts; + struct gpio_desc rst_gpio; +}; + +#define PCI_EXP_DEVCTL_PAYLOAD 0x00e0 /* Max_Payload_Size */ + +#define PCIE_CAP_MAX_PAYLOAD_SIZE(x) ((x) << 5) +#define PCIE_CAP_MAX_READ_REQ_SIZE(x) ((x) << 12) + +/* PCIe specific config registers */ +#define PCIE_CFG0 0x0 +#define APP_LTSSM_ENABLE BIT(7) + +#define PCIE_CFG_STATUS12 0x30 +#define IS_SMLH_LINK_UP(x) ((x) & (1 << 6)) +#define IS_RDLH_LINK_UP(x) ((x) & (1 << 16)) +#define IS_LTSSM_UP(x) ((((x) >> 10) & 0x1f) == 0x11) + +#define PCIE_CFG_STATUS17 0x44 +#define PM_CURRENT_STATE(x) (((x) >> 7) & 0x1) + +#define WAIT_LINKUP_TIMEOUT 4000 +#define PORT_CLK_RATE 100000000UL +#define MAX_PAYLOAD_SIZE 256 +#define MAX_READ_REQ_SIZE 256 +#define PCIE_RESET_DELAY 500 +#define PCIE_SHARED_RESET 1 +#define PCIE_NORMAL_RESET 0 + +enum pcie_data_rate { + PCIE_GEN1, + PCIE_GEN2, + PCIE_GEN3, + PCIE_GEN4 +}; + +/* Parameters for the waiting for #perst signal */ +#define PERST_WAIT_US 1000000 + +static inline u32 meson_cfg_readl(struct meson_pcie *priv, u32 reg) +{ + return readl(priv->meson_cfg_base + reg); +} + +static inline void meson_cfg_writel(struct meson_pcie *priv, u32 val, u32 reg) +{ + writel(val, priv->meson_cfg_base + reg); +} + +/** + * meson_pcie_configure() - Configure link + * + * @meson_pcie: Pointer to the PCI controller state + * + * Configure the link mode and width + */ +static void meson_pcie_configure(struct meson_pcie *priv) +{ + u32 val; + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + val &= ~PORT_LINK_FAST_LINK_MODE; + val |= PORT_LINK_DLL_LINK_EN; + val &= ~PORT_LINK_MODE_MASK; + val |= PORT_LINK_MODE_1_LANES; + writel(val, priv->dw.dbi_base + PCIE_PORT_LINK_CONTROL); + + val = readl(priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + val &= ~PORT_LOGIC_LINK_WIDTH_MASK; + val |= PORT_LOGIC_LINK_WIDTH_1_LANES; + writel(val, priv->dw.dbi_base + PCIE_LINK_WIDTH_SPEED_CONTROL); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static inline void meson_pcie_enable_ltssm(struct meson_pcie *priv) +{ + u32 val; + + val = meson_cfg_readl(priv, PCIE_CFG0); + val |= APP_LTSSM_ENABLE; + meson_cfg_writel(priv, val, PCIE_CFG0); +} + +static int meson_pcie_wait_link_up(struct meson_pcie *priv) +{ + u32 speed_okay = 0; + u32 cnt = 0; + u32 state12, state17, smlh_up, ltssm_up, rdlh_up; + + do { + state12 = meson_cfg_readl(priv, PCIE_CFG_STATUS12); + state17 = meson_cfg_readl(priv, PCIE_CFG_STATUS17); + smlh_up = IS_SMLH_LINK_UP(state12); + rdlh_up = IS_RDLH_LINK_UP(state12); + ltssm_up = IS_LTSSM_UP(state12); + + if (PM_CURRENT_STATE(state17) < PCIE_GEN3) + speed_okay = 1; + + if (smlh_up) + debug("%s: smlh_link_up is on\n", __func__); + if (rdlh_up) + debug("%s: rdlh_link_up is on\n", __func__); + if (ltssm_up) + debug("%s: ltssm_up is on\n", __func__); + if (speed_okay) + debug("%s: speed_okay\n", __func__); + + if (smlh_up && rdlh_up && ltssm_up && speed_okay) + return 0; + + cnt++; + + udelay(10); + } while (cnt < WAIT_LINKUP_TIMEOUT); + + printf("%s: error: wait linkup timeout\n", __func__); + return -EIO; +} + +/** + * meson_pcie_link_up() - Wait for the link to come up + * + * @meson_pcie: Pointer to the PCI controller state + * @cap_speed: Desired link speed + * + * Return: 1 (true) for active line and negative (false) for no link (timeout) + */ +static int meson_pcie_link_up(struct meson_pcie *priv, u32 cap_speed) +{ + /* DW link configurations */ + meson_pcie_configure(priv); + + /* Reset the device */ + if (dm_gpio_is_valid(&priv->rst_gpio)) { + dm_gpio_set_value(&priv->rst_gpio, 1); + /* + * Minimal is 100ms from spec but we see + * some wired devices need much more, such as 600ms. + * Add a enough delay to cover all cases. + */ + udelay(PERST_WAIT_US); + dm_gpio_set_value(&priv->rst_gpio, 0); + } + + /* Enable LTSSM */ + meson_pcie_enable_ltssm(priv); + + return meson_pcie_wait_link_up(priv); +} + +static int meson_size_to_payload(int size) +{ + /* + * dwc supports 2^(val+7) payload size, which val is 0~5 default to 1. + * So if input size is not 2^order alignment or less than 2^7 or bigger + * than 2^12, just set to default size 2^(1+7). + */ + if (!is_power_of_2(size) || size < 128 || size > 4096) { + debug("%s: payload size %d, set to default 256\n", __func__, size); + return 1; + } + + return fls(size) - 8; +} + +static void meson_set_max_payload(struct meson_pcie *priv, int size) +{ + u32 val; + u16 offset = dm_pci_find_capability(priv->dw.dev, PCI_CAP_ID_EXP); + int max_payload_size = meson_size_to_payload(size); + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + val &= ~PCI_EXP_DEVCTL_PAYLOAD; + writel(val, priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + val |= PCIE_CAP_MAX_PAYLOAD_SIZE(max_payload_size); + writel(val, priv->dw.dbi_base + PCI_EXP_DEVCTL); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static void meson_set_max_rd_req_size(struct meson_pcie *priv, int size) +{ + u32 val; + u16 offset = dm_pci_find_capability(priv->dw.dev, PCI_CAP_ID_EXP); + int max_rd_req_size = meson_size_to_payload(size); + + dw_pcie_dbi_write_enable(&priv->dw, true); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + val &= ~PCI_EXP_DEVCTL_PAYLOAD; + writel(val, priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + + val = readl(priv->dw.dbi_base + offset + PCI_EXP_DEVCTL); + val |= PCIE_CAP_MAX_READ_REQ_SIZE(max_rd_req_size); + writel(val, priv->dw.dbi_base + PCI_EXP_DEVCTL); + + dw_pcie_dbi_write_enable(&priv->dw, false); +} + +static int meson_pcie_init_port(struct udevice *dev) +{ + int ret; + struct meson_pcie *priv = dev_get_priv(dev); + + ret = generic_phy_init(&priv->phy); + if (ret) { + dev_err(dev, "failed to init phy (ret=%d)\n", ret); + return ret; + } + + ret = generic_phy_power_on(&priv->phy); + if (ret) { + dev_err(dev, "failed to power on phy (ret=%d)\n", ret); + goto err_exit_phy; + } + + ret = generic_phy_reset(&priv->phy); + if (ret) { + dev_err(dev, "failed to reset phy (ret=%d)\n", ret); + goto err_exit_phy; + } + + ret = reset_assert_bulk(&priv->rsts); + if (ret) { + dev_err(dev, "failed to assert resets (ret=%d)\n", ret); + goto err_power_off_phy; + } + + udelay(PCIE_RESET_DELAY); + + ret = reset_deassert_bulk(&priv->rsts); + if (ret) { + dev_err(dev, "failed to deassert resets (ret=%d)\n", ret); + goto err_power_off_phy; + } + + udelay(PCIE_RESET_DELAY); + + ret = clk_set_rate(&priv->clk_port, PORT_CLK_RATE); + if (ret) { + dev_err(dev, "failed to set port clk rate (ret=%d)\n", ret); + goto err_deassert_bulk; + } + + ret = clk_enable(&priv->clk_general); + if (ret) { + dev_err(dev, "failed to enable clk general (ret=%d)\n", ret); + goto err_deassert_bulk; + } + + ret = clk_enable(&priv->clk_pclk); + if (ret) { + dev_err(dev, "failed to enable pclk (ret=%d)\n", ret); + goto err_deassert_bulk; + } + + meson_set_max_payload(priv, MAX_PAYLOAD_SIZE); + meson_set_max_rd_req_size(priv, MAX_READ_REQ_SIZE); + + pcie_dw_setup_host(&priv->dw); + + ret = meson_pcie_link_up(priv, LINK_SPEED_GEN_2); + if (ret < 0) + goto err_link_up; + + return 0; +err_link_up: + clk_disable(&priv->clk_port); + clk_disable(&priv->clk_general); + clk_disable(&priv->clk_pclk); +err_deassert_bulk: + reset_assert_bulk(&priv->rsts); +err_power_off_phy: + generic_phy_power_off(&priv->phy); +err_exit_phy: + generic_phy_exit(&priv->phy); + + return ret; +} + +static int meson_pcie_parse_dt(struct udevice *dev) +{ + struct meson_pcie *priv = dev_get_priv(dev); + int ret; + + priv->dw.dbi_base = (void *)dev_read_addr_index(dev, 0); + if (!priv->dw.dbi_base) + return -ENODEV; + + dev_dbg(dev, "ELBI address is 0x%p\n", priv->dw.dbi_base); + + priv->meson_cfg_base = (void *)dev_read_addr_index(dev, 1); + if (!priv->meson_cfg_base) + return -ENODEV; + + dev_dbg(dev, "CFG address is 0x%p\n", priv->meson_cfg_base); + + ret = gpio_request_by_name(dev, "reset-gpios", 0, + &priv->rst_gpio, GPIOD_IS_OUT); + if (ret) { + dev_err(dev, "failed to find reset-gpios property\n"); + return ret; + } + + ret = reset_get_bulk(dev, &priv->rsts); + if (ret) { + dev_err(dev, "Can't get reset: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "port", &priv->clk_port); + if (ret) { + dev_err(dev, "Can't get port clock: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "general", &priv->clk_general); + if (ret) { + dev_err(dev, "Can't get port clock: %d\n", ret); + return ret; + } + + ret = clk_get_by_name(dev, "pclk", &priv->clk_pclk); + if (ret) { + dev_err(dev, "Can't get port clock: %d\n", ret); + return ret; + } + + ret = generic_phy_get_by_index(dev, 0, &priv->phy); + if (ret) { + dev_err(dev, "failed to get pcie phy (ret=%d)\n", ret); + return ret; + } + + return 0; +} + +/** + * meson_pcie_probe() - Probe the PCIe bus for active link + * + * @dev: A pointer to the device being operated on + * + * Probe for an active link on the PCIe bus and configure the controller + * to enable this port. + * + * Return: 0 on success, else -ENODEV + */ +static int meson_pcie_probe(struct udevice *dev) +{ + struct meson_pcie *priv = dev_get_priv(dev); + struct udevice *ctlr = pci_get_controller(dev); + struct pci_controller *hose = dev_get_uclass_priv(ctlr); + int ret = 0; + + priv->dw.first_busno = dev_seq(dev); + priv->dw.dev = dev; + + ret = meson_pcie_parse_dt(dev); + if (ret) + return ret; + + ret = meson_pcie_init_port(dev); + if (ret) { + dm_gpio_free(dev, &priv->rst_gpio); + return ret; + } + + printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", + dev_seq(dev), pcie_dw_get_link_speed(&priv->dw), + pcie_dw_get_link_width(&priv->dw), + hose->first_busno); + + return pcie_dw_prog_outbound_atu_unroll(&priv->dw, + PCIE_ATU_REGION_INDEX0, + PCIE_ATU_TYPE_MEM, + priv->dw.mem.phys_start, + priv->dw.mem.bus_start, + priv->dw.mem.size); +} + +static const struct dm_pci_ops meson_pcie_ops = { + .read_config = pcie_dw_read_config, + .write_config = pcie_dw_write_config, +}; + +static const struct udevice_id meson_pcie_ids[] = { + { .compatible = "amlogic,axg-pcie" }, + { .compatible = "amlogic,g12a-pcie" }, + { } +}; + +U_BOOT_DRIVER(meson_dw_pcie) = { + .name = "pcie_dw_meson", + .id = UCLASS_PCI, + .of_match = meson_pcie_ids, + .ops = &meson_pcie_ops, + .probe = meson_pcie_probe, + .priv_auto = sizeof(struct meson_pcie), +};