diff options
Diffstat (limited to 'drivers/pci/controller/pcie-cadence.c')
| -rw-r--r-- | drivers/pci/controller/pcie-cadence.c | 126 | 
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/pci/controller/pcie-cadence.c b/drivers/pci/controller/pcie-cadence.c new file mode 100644 index 000000000000..138d113eb45d --- /dev/null +++ b/drivers/pci/controller/pcie-cadence.c @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017 Cadence +// Cadence PCIe controller driver. +// Author: Cyrille Pitchen <[email protected]> + +#include <linux/kernel.h> + +#include "pcie-cadence.h" + +void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 fn, +				   u32 r, bool is_io, +				   u64 cpu_addr, u64 pci_addr, size_t size) +{ +	/* +	 * roundup_pow_of_two() returns an unsigned long, which is not suited +	 * for 64bit values. +	 */ +	u64 sz = 1ULL << fls64(size - 1); +	int nbits = ilog2(sz); +	u32 addr0, addr1, desc0, desc1; + +	if (nbits < 8) +		nbits = 8; + +	/* Set the PCI address */ +	addr0 = CDNS_PCIE_AT_OB_REGION_PCI_ADDR0_NBITS(nbits) | +		(lower_32_bits(pci_addr) & GENMASK(31, 8)); +	addr1 = upper_32_bits(pci_addr); + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), addr0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), addr1); + +	/* Set the PCIe header descriptor */ +	if (is_io) +		desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_IO; +	else +		desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_MEM; +	desc1 = 0; + +	/* +	 * Whatever Bit [23] is set or not inside DESC0 register of the outbound +	 * PCIe descriptor, the PCI function number must be set into +	 * Bits [26:24] of DESC0 anyway. +	 * +	 * In Root Complex mode, the function number is always 0 but in Endpoint +	 * mode, the PCIe controller may support more than one function. This +	 * function number needs to be set properly into the outbound PCIe +	 * descriptor. +	 * +	 * Besides, setting Bit [23] is mandatory when in Root Complex mode: +	 * then the driver must provide the bus, resp. device, number in +	 * Bits [7:0] of DESC1, resp. Bits[31:27] of DESC0. Like the function +	 * number, the device number is always 0 in Root Complex mode. +	 * +	 * However when in Endpoint mode, we can clear Bit [23] of DESC0, hence +	 * the PCIe controller will use the captured values for the bus and +	 * device numbers. +	 */ +	if (pcie->is_rc) { +		/* The device and function numbers are always 0. */ +		desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | +			 CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); +		desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); +	} else { +		/* +		 * Use captured values for bus and device numbers but still +		 * need to set the function number. +		 */ +		desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); +	} + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); + +	/* Set the CPU address */ +	cpu_addr -= pcie->mem_res->start; +	addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | +		(lower_32_bits(cpu_addr) & GENMASK(31, 8)); +	addr1 = upper_32_bits(cpu_addr); + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); +} + +void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, u8 fn, +						  u32 r, u64 cpu_addr) +{ +	u32 addr0, addr1, desc0, desc1; + +	desc0 = CDNS_PCIE_AT_OB_REGION_DESC0_TYPE_NORMAL_MSG; +	desc1 = 0; + +	/* See cdns_pcie_set_outbound_region() comments above. */ +	if (pcie->is_rc) { +		desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_HARDCODED_RID | +			 CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(0); +		desc1 |= CDNS_PCIE_AT_OB_REGION_DESC1_BUS(pcie->bus); +	} else { +		desc0 |= CDNS_PCIE_AT_OB_REGION_DESC0_DEVFN(fn); +	} + +	/* Set the CPU address */ +	cpu_addr -= pcie->mem_res->start; +	addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(17) | +		(lower_32_bits(cpu_addr) & GENMASK(31, 8)); +	addr1 = upper_32_bits(cpu_addr); + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), desc0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), addr0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), addr1); +} + +void cdns_pcie_reset_outbound_region(struct cdns_pcie *pcie, u32 r) +{ +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR0(r), 0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(r), 0); + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC0(r), 0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), 0); + +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR0(r), 0); +	cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_CPU_ADDR1(r), 0); +}  |