mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	pci: layerscape: Split the EP and RC driver
Split the RC and EP driver, and reimplement the EP driver base on the EP framework. Signed-off-by: Xiaowei Bao <xiaowei.bao@nxp.com> Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> [Rebased] Signed-off-by: Priyanka Jain <priyanka.jain@nxp.com>
This commit is contained in:
		
				
					committed by
					
						 Priyanka Jain
						Priyanka Jain
					
				
			
			
				
	
			
			
			
						parent
						
							be2c7d764a
						
					
				
				
					commit
					118e58e26e
				
			| @@ -33,7 +33,7 @@ obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o | ||||
| obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o | ||||
| obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o | ||||
| obj-$(CONFIG_PCIE_FSL) += pcie_fsl.o pcie_fsl_fixup.o | ||||
| obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o | ||||
| obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o pcie_layerscape_rc.o pcie_layerscape_ep.o | ||||
| obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape_fixup.o pcie_layerscape_fixup_common.o | ||||
| obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \ | ||||
| 				pcie_layerscape_gen4_fixup.o \ | ||||
|   | ||||
| @@ -1,18 +1,15 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0+ | ||||
| /* | ||||
|  * Copyright 2017-2019 NXP | ||||
|  * Copyright 2017-2020 NXP | ||||
|  * Copyright 2014-2015 Freescale Semiconductor, Inc. | ||||
|  * Layerscape PCIe driver | ||||
|  */ | ||||
|  | ||||
| #include <common.h> | ||||
| #include <log.h> | ||||
| #include <asm/arch/fsl_serdes.h> | ||||
| #include <pci.h> | ||||
| #include <asm/io.h> | ||||
| #include <errno.h> | ||||
| #include <malloc.h> | ||||
| #include <dm.h> | ||||
| #if defined(CONFIG_FSL_LSCH2) || defined(CONFIG_FSL_LSCH3) || \ | ||||
| 	defined(CONFIG_ARM) | ||||
| #include <asm/arch/clock.h> | ||||
| @@ -23,18 +20,17 @@ DECLARE_GLOBAL_DATA_PTR; | ||||
|  | ||||
| LIST_HEAD(ls_pcie_list); | ||||
|  | ||||
| static unsigned int dbi_readl(struct ls_pcie *pcie, unsigned int offset) | ||||
| unsigned int dbi_readl(struct ls_pcie *pcie, unsigned int offset) | ||||
| { | ||||
| 	return in_le32(pcie->dbi + offset); | ||||
| } | ||||
|  | ||||
| static void dbi_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| 		       unsigned int offset) | ||||
| void dbi_writel(struct ls_pcie *pcie, unsigned int value, unsigned int offset) | ||||
| { | ||||
| 	out_le32(pcie->dbi + offset, value); | ||||
| } | ||||
|  | ||||
| static unsigned int ctrl_readl(struct ls_pcie *pcie, unsigned int offset) | ||||
| unsigned int ctrl_readl(struct ls_pcie *pcie, unsigned int offset) | ||||
| { | ||||
| 	if (pcie->big_endian) | ||||
| 		return in_be32(pcie->ctrl + offset); | ||||
| @@ -42,8 +38,8 @@ static unsigned int ctrl_readl(struct ls_pcie *pcie, unsigned int offset) | ||||
| 		return in_le32(pcie->ctrl + offset); | ||||
| } | ||||
|  | ||||
| static void ctrl_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| 			unsigned int offset) | ||||
| void ctrl_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| 		 unsigned int offset) | ||||
| { | ||||
| 	if (pcie->big_endian) | ||||
| 		out_be32(pcie->ctrl + offset, value); | ||||
| @@ -51,6 +47,26 @@ static void ctrl_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| 		out_le32(pcie->ctrl + offset, value); | ||||
| } | ||||
|  | ||||
| void ls_pcie_dbi_ro_wr_en(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 reg, val; | ||||
|  | ||||
| 	reg = PCIE_MISC_CONTROL_1_OFF; | ||||
| 	val = dbi_readl(pcie, reg); | ||||
| 	val |= PCIE_DBI_RO_WR_EN; | ||||
| 	dbi_writel(pcie, val, reg); | ||||
| } | ||||
|  | ||||
| void ls_pcie_dbi_ro_wr_dis(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 reg, val; | ||||
|  | ||||
| 	reg = PCIE_MISC_CONTROL_1_OFF; | ||||
| 	val = dbi_readl(pcie, reg); | ||||
| 	val &= ~PCIE_DBI_RO_WR_EN; | ||||
| 	dbi_writel(pcie, val, reg); | ||||
| } | ||||
|  | ||||
| static int ls_pcie_ltssm(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 state; | ||||
| @@ -67,7 +83,7 @@ static int ls_pcie_ltssm(struct ls_pcie *pcie) | ||||
| 	return state; | ||||
| } | ||||
|  | ||||
| static int ls_pcie_link_up(struct ls_pcie *pcie) | ||||
| int ls_pcie_link_up(struct ls_pcie *pcie) | ||||
| { | ||||
| 	int ltssm; | ||||
|  | ||||
| @@ -78,22 +94,8 @@ static int ls_pcie_link_up(struct ls_pcie *pcie) | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| static void ls_pcie_cfg0_set_busdev(struct ls_pcie *pcie, u32 busdev) | ||||
| { | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, | ||||
| 		   PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, busdev, PCIE_ATU_LOWER_TARGET); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_cfg1_set_busdev(struct ls_pcie *pcie, u32 busdev) | ||||
| { | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, | ||||
| 		   PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, busdev, PCIE_ATU_LOWER_TARGET); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_atu_outbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| 				      u64 phys, u64 bus_addr, pci_size_t size) | ||||
| void ls_pcie_atu_outbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| 			      u64 phys, u64 bus_addr, pci_size_t size) | ||||
| { | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | idx, PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, (u32)phys, PCIE_ATU_LOWER_BASE); | ||||
| @@ -106,18 +108,18 @@ static void ls_pcie_atu_outbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| } | ||||
|  | ||||
| /* Use bar match mode and MEM type as default */ | ||||
| static void ls_pcie_atu_inbound_set(struct ls_pcie *pcie, int idx, | ||||
| 				     int bar, u64 phys) | ||||
| void ls_pcie_atu_inbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| 			     int bar, u64 phys) | ||||
| { | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_INBOUND | idx, PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, (u32)phys, PCIE_ATU_LOWER_TARGET); | ||||
| 	dbi_writel(pcie, phys >> 32, PCIE_ATU_UPPER_TARGET); | ||||
| 	dbi_writel(pcie, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1); | ||||
| 	dbi_writel(pcie, type, PCIE_ATU_CR1); | ||||
| 	dbi_writel(pcie, PCIE_ATU_ENABLE | PCIE_ATU_BAR_MODE_ENABLE | | ||||
| 		   PCIE_ATU_BAR_NUM(bar), PCIE_ATU_CR2); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_dump_atu(struct ls_pcie *pcie) | ||||
| void ls_pcie_dump_atu(struct ls_pcie *pcie) | ||||
| { | ||||
| 	int i; | ||||
|  | ||||
| @@ -134,431 +136,10 @@ static void ls_pcie_dump_atu(struct ls_pcie *pcie) | ||||
| 		debug("\tUPPER BUS  0x%08x\n", | ||||
| 		      dbi_readl(pcie, PCIE_ATU_UPPER_TARGET)); | ||||
| 		debug("\tLIMIT      0x%08x\n", | ||||
| 		      readl(pcie->dbi + PCIE_ATU_LIMIT)); | ||||
| 		      dbi_readl(pcie, PCIE_ATU_LIMIT)); | ||||
| 		debug("\tCR1        0x%08x\n", | ||||
| 		      dbi_readl(pcie, PCIE_ATU_CR1)); | ||||
| 		debug("\tCR2        0x%08x\n", | ||||
| 		      dbi_readl(pcie, PCIE_ATU_CR2)); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_atu(struct ls_pcie *pcie) | ||||
| { | ||||
| 	struct pci_region *io, *mem, *pref; | ||||
| 	unsigned long long offset = 0; | ||||
| 	int idx = 0; | ||||
| 	uint svr; | ||||
|  | ||||
| 	svr = get_svr(); | ||||
| 	if (((svr >> SVR_VAR_PER_SHIFT) & SVR_LS102XA_MASK) == SVR_LS102XA) { | ||||
| 		offset = LS1021_PCIE_SPACE_OFFSET + | ||||
| 			 LS1021_PCIE_SPACE_SIZE * pcie->idx; | ||||
| 	} | ||||
|  | ||||
| 	/* ATU 0 : OUTBOUND : CFG0 */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, PCIE_ATU_REGION_INDEX0, | ||||
| 				 PCIE_ATU_TYPE_CFG0, | ||||
| 				 pcie->cfg_res.start + offset, | ||||
| 				 0, | ||||
| 				 fdt_resource_size(&pcie->cfg_res) / 2); | ||||
| 	/* ATU 1 : OUTBOUND : CFG1 */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, PCIE_ATU_REGION_INDEX1, | ||||
| 				 PCIE_ATU_TYPE_CFG1, | ||||
| 				 pcie->cfg_res.start + offset + | ||||
| 				 fdt_resource_size(&pcie->cfg_res) / 2, | ||||
| 				 0, | ||||
| 				 fdt_resource_size(&pcie->cfg_res) / 2); | ||||
|  | ||||
| 	pci_get_regions(pcie->bus, &io, &mem, &pref); | ||||
| 	idx = PCIE_ATU_REGION_INDEX1 + 1; | ||||
|  | ||||
| 	/* Fix the pcie memory map for LS2088A series SoCs */ | ||||
| 	svr = (svr >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| 	if (svr == SVR_LS2088A || svr == SVR_LS2084A || | ||||
| 	    svr == SVR_LS2048A || svr == SVR_LS2044A || | ||||
| 	    svr == SVR_LS2081A || svr == SVR_LS2041A) { | ||||
| 		if (io) | ||||
| 			io->phys_start = (io->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		if (mem) | ||||
| 			mem->phys_start = (mem->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		if (pref) | ||||
| 			pref->phys_start = (pref->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 	} | ||||
|  | ||||
| 	if (io) | ||||
| 		/* ATU : OUTBOUND : IO */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_IO, | ||||
| 					 io->phys_start + offset, | ||||
| 					 io->bus_start, | ||||
| 					 io->size); | ||||
|  | ||||
| 	if (mem) | ||||
| 		/* ATU : OUTBOUND : MEM */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_MEM, | ||||
| 					 mem->phys_start + offset, | ||||
| 					 mem->bus_start, | ||||
| 					 mem->size); | ||||
|  | ||||
| 	if (pref) | ||||
| 		/* ATU : OUTBOUND : pref */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_MEM, | ||||
| 					 pref->phys_start + offset, | ||||
| 					 pref->bus_start, | ||||
| 					 pref->size); | ||||
|  | ||||
| 	ls_pcie_dump_atu(pcie); | ||||
| } | ||||
|  | ||||
| /* Return 0 if the address is valid, -errno if not valid */ | ||||
| static int ls_pcie_addr_valid(struct ls_pcie *pcie, pci_dev_t bdf) | ||||
| { | ||||
| 	struct udevice *bus = pcie->bus; | ||||
|  | ||||
| 	if (pcie->mode == PCI_HEADER_TYPE_NORMAL) | ||||
| 		return -ENODEV; | ||||
|  | ||||
| 	if (!pcie->enabled) | ||||
| 		return -ENXIO; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) < bus->seq) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if ((PCI_BUS(bdf) > bus->seq) && (!ls_pcie_link_up(pcie))) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) <= (bus->seq + 1) && (PCI_DEV(bdf) > 0)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int ls_pcie_conf_address(const struct udevice *bus, pci_dev_t bdf, | ||||
| 			 uint offset, void **paddress) | ||||
| { | ||||
| 	struct ls_pcie *pcie = dev_get_priv(bus); | ||||
| 	u32 busdev; | ||||
|  | ||||
| 	if (ls_pcie_addr_valid(pcie, bdf)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) == bus->seq) { | ||||
| 		*paddress = pcie->dbi + offset; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	busdev = PCIE_ATU_BUS(PCI_BUS(bdf) - bus->seq) | | ||||
| 		 PCIE_ATU_DEV(PCI_DEV(bdf)) | | ||||
| 		 PCIE_ATU_FUNC(PCI_FUNC(bdf)); | ||||
|  | ||||
| 	if (PCI_BUS(bdf) == bus->seq + 1) { | ||||
| 		ls_pcie_cfg0_set_busdev(pcie, busdev); | ||||
| 		*paddress = pcie->cfg0 + offset; | ||||
| 	} else { | ||||
| 		ls_pcie_cfg1_set_busdev(pcie, busdev); | ||||
| 		*paddress = pcie->cfg1 + offset; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int ls_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, | ||||
| 			       uint offset, ulong *valuep, | ||||
| 			       enum pci_size_t size) | ||||
| { | ||||
| 	return pci_generic_mmap_read_config(bus, ls_pcie_conf_address, | ||||
| 					    bdf, offset, valuep, size); | ||||
| } | ||||
|  | ||||
| static int ls_pcie_write_config(struct udevice *bus, pci_dev_t bdf, | ||||
| 				uint offset, ulong value, | ||||
| 				enum pci_size_t size) | ||||
| { | ||||
| 	return pci_generic_mmap_write_config(bus, ls_pcie_conf_address, | ||||
| 					     bdf, offset, value, size); | ||||
| } | ||||
|  | ||||
| /* Clear multi-function bit */ | ||||
| static void ls_pcie_clear_multifunction(struct ls_pcie *pcie) | ||||
| { | ||||
| 	writeb(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); | ||||
| } | ||||
|  | ||||
| /* Fix class value */ | ||||
| static void ls_pcie_fix_class(struct ls_pcie *pcie) | ||||
| { | ||||
| 	writew(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); | ||||
| } | ||||
|  | ||||
| /* Drop MSG TLP except for Vendor MSG */ | ||||
| static void ls_pcie_drop_msg_tlp(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 val; | ||||
|  | ||||
| 	val = dbi_readl(pcie, PCIE_STRFMR1); | ||||
| 	val &= 0xDFFFFFFF; | ||||
| 	dbi_writel(pcie, val, PCIE_STRFMR1); | ||||
| } | ||||
|  | ||||
| /* Disable all bars in RC mode */ | ||||
| static void ls_pcie_disable_bars(struct ls_pcie *pcie) | ||||
| { | ||||
| 	dbi_writel(pcie, 0, PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_0); | ||||
| 	dbi_writel(pcie, 0, PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_1); | ||||
| 	dbi_writel(pcie, 0xfffffffe, PCIE_CS2_OFFSET + PCI_ROM_ADDRESS1); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_ctrl(struct ls_pcie *pcie) | ||||
| { | ||||
| 	ls_pcie_setup_atu(pcie); | ||||
|  | ||||
| 	dbi_writel(pcie, 1, PCIE_DBI_RO_WR_EN); | ||||
| 	ls_pcie_fix_class(pcie); | ||||
| 	ls_pcie_clear_multifunction(pcie); | ||||
| 	ls_pcie_drop_msg_tlp(pcie); | ||||
| 	dbi_writel(pcie, 0, PCIE_DBI_RO_WR_EN); | ||||
|  | ||||
| 	ls_pcie_disable_bars(pcie); | ||||
| 	pcie->stream_id_cur = 0; | ||||
| } | ||||
|  | ||||
| static void ls_pcie_ep_setup_atu(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u64 phys = CONFIG_SYS_PCI_EP_MEMORY_BASE; | ||||
|  | ||||
| 	/* ATU 0 : INBOUND : map BAR0 */ | ||||
| 	ls_pcie_atu_inbound_set(pcie, 0, 0, phys); | ||||
| 	/* ATU 1 : INBOUND : map BAR1 */ | ||||
| 	phys += PCIE_BAR1_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 1, 1, phys); | ||||
| 	/* ATU 2 : INBOUND : map BAR2 */ | ||||
| 	phys += PCIE_BAR2_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 2, 2, phys); | ||||
| 	/* ATU 3 : INBOUND : map BAR4 */ | ||||
| 	phys = CONFIG_SYS_PCI_EP_MEMORY_BASE + PCIE_BAR4_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 3, 4, phys); | ||||
|  | ||||
| 	/* ATU 0 : OUTBOUND : map MEM */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, 0, | ||||
| 				 PCIE_ATU_TYPE_MEM, | ||||
| 				 pcie->cfg_res.start, | ||||
| 				 0, | ||||
| 				 CONFIG_SYS_PCI_MEMORY_SIZE); | ||||
| } | ||||
|  | ||||
| /* BAR0 and BAR1 are 32bit BAR2 and BAR4 are 64bit */ | ||||
| static void ls_pcie_ep_setup_bar(void *bar_base, int bar, u32 size) | ||||
| { | ||||
| 	/* The least inbound window is 4KiB */ | ||||
| 	if (size < 4 * 1024) | ||||
| 		return; | ||||
|  | ||||
| 	switch (bar) { | ||||
| 	case 0: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_0); | ||||
| 		break; | ||||
| 	case 1: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_1); | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_2); | ||||
| 		writel(0, bar_base + PCI_BASE_ADDRESS_3); | ||||
| 		break; | ||||
| 	case 4: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_4); | ||||
| 		writel(0, bar_base + PCI_BASE_ADDRESS_5); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void ls_pcie_ep_setup_bars(void *bar_base) | ||||
| { | ||||
| 	/* BAR0 - 32bit - 4K configuration */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 0, PCIE_BAR0_SIZE); | ||||
| 	/* BAR1 - 32bit - 8K MSIX*/ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 1, PCIE_BAR1_SIZE); | ||||
| 	/* BAR2 - 64bit - 4K MEM desciptor */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 2, PCIE_BAR2_SIZE); | ||||
| 	/* BAR4 - 64bit - 1M MEM*/ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 4, PCIE_BAR4_SIZE); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_ep_enable_cfg(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 config; | ||||
|  | ||||
| 	config = ctrl_readl(pcie,  PCIE_PF_CONFIG); | ||||
| 	config |= PCIE_CONFIG_READY; | ||||
| 	ctrl_writel(pcie, config, PCIE_PF_CONFIG); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_ep(struct ls_pcie *pcie) | ||||
| { | ||||
| 	u32 sriov; | ||||
|  | ||||
| 	sriov = readl(pcie->dbi + PCIE_SRIOV); | ||||
| 	if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV) { | ||||
| 		int pf, vf; | ||||
|  | ||||
| 		for (pf = 0; pf < PCIE_PF_NUM; pf++) { | ||||
| 			for (vf = 0; vf <= PCIE_VF_NUM; vf++) { | ||||
| 				ctrl_writel(pcie, PCIE_LCTRL0_VAL(pf, vf), | ||||
| 					    PCIE_PF_VF_CTRL); | ||||
|  | ||||
| 				ls_pcie_ep_setup_bars(pcie->dbi); | ||||
| 				ls_pcie_ep_setup_atu(pcie); | ||||
| 			} | ||||
| 		} | ||||
| 		/* Disable CFG2 */ | ||||
| 		ctrl_writel(pcie, 0, PCIE_PF_VF_CTRL); | ||||
| 	} else { | ||||
| 		ls_pcie_ep_setup_bars(pcie->dbi + PCIE_NO_SRIOV_BAR_BASE); | ||||
| 		ls_pcie_ep_setup_atu(pcie); | ||||
| 	} | ||||
|  | ||||
| 	ls_pcie_ep_enable_cfg(pcie); | ||||
| } | ||||
|  | ||||
| static int ls_pcie_probe(struct udevice *dev) | ||||
| { | ||||
| 	struct ls_pcie *pcie = dev_get_priv(dev); | ||||
| 	const void *fdt = gd->fdt_blob; | ||||
| 	int node = dev_of_offset(dev); | ||||
| 	u16 link_sta; | ||||
| 	uint svr; | ||||
| 	int ret; | ||||
| 	fdt_size_t cfg_size; | ||||
|  | ||||
| 	pcie->bus = dev; | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "dbi", &pcie->dbi_res); | ||||
| 	if (ret) { | ||||
| 		printf("ls-pcie: resource \"dbi\" not found\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	pcie->idx = (pcie->dbi_res.start - PCIE_SYS_BASE_ADDR) / PCIE_CCSR_SIZE; | ||||
|  | ||||
| 	list_add(&pcie->list, &ls_pcie_list); | ||||
|  | ||||
| 	pcie->enabled = is_serdes_configured(PCIE_SRDS_PRTCL(pcie->idx)); | ||||
| 	if (!pcie->enabled) { | ||||
| 		printf("PCIe%d: %s disabled\n", pcie->idx, dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	pcie->dbi = map_physmem(pcie->dbi_res.start, | ||||
| 				fdt_resource_size(&pcie->dbi_res), | ||||
| 				MAP_NOCACHE); | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "lut", &pcie->lut_res); | ||||
| 	if (!ret) | ||||
| 		pcie->lut = map_physmem(pcie->lut_res.start, | ||||
| 					fdt_resource_size(&pcie->lut_res), | ||||
| 					MAP_NOCACHE); | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "ctrl", &pcie->ctrl_res); | ||||
| 	if (!ret) | ||||
| 		pcie->ctrl = map_physmem(pcie->ctrl_res.start, | ||||
| 					 fdt_resource_size(&pcie->ctrl_res), | ||||
| 					 MAP_NOCACHE); | ||||
| 	if (!pcie->ctrl) | ||||
| 		pcie->ctrl = pcie->lut; | ||||
|  | ||||
| 	if (!pcie->ctrl) { | ||||
| 		printf("%s: NOT find CTRL\n", dev->name); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "config", &pcie->cfg_res); | ||||
| 	if (ret) { | ||||
| 		printf("%s: resource \"config\" not found\n", dev->name); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Fix the pcie memory map address and PF control registers address | ||||
| 	 * for LS2088A series SoCs | ||||
| 	 */ | ||||
| 	svr = get_svr(); | ||||
| 	svr = (svr >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| 	if (svr == SVR_LS2088A || svr == SVR_LS2084A || | ||||
| 	    svr == SVR_LS2048A || svr == SVR_LS2044A || | ||||
| 	    svr == SVR_LS2081A || svr == SVR_LS2041A) { | ||||
| 		cfg_size = fdt_resource_size(&pcie->cfg_res); | ||||
| 		pcie->cfg_res.start = LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		pcie->cfg_res.end = pcie->cfg_res.start + cfg_size; | ||||
| 		pcie->ctrl = pcie->lut + 0x40000; | ||||
| 	} | ||||
|  | ||||
| 	pcie->cfg0 = map_physmem(pcie->cfg_res.start, | ||||
| 				 fdt_resource_size(&pcie->cfg_res), | ||||
| 				 MAP_NOCACHE); | ||||
| 	pcie->cfg1 = pcie->cfg0 + fdt_resource_size(&pcie->cfg_res) / 2; | ||||
|  | ||||
| 	pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); | ||||
|  | ||||
| 	debug("%s dbi:%lx lut:%lx ctrl:0x%lx cfg0:0x%lx, big-endian:%d\n", | ||||
| 	      dev->name, (unsigned long)pcie->dbi, (unsigned long)pcie->lut, | ||||
| 	      (unsigned long)pcie->ctrl, (unsigned long)pcie->cfg0, | ||||
| 	      pcie->big_endian); | ||||
|  | ||||
| 	pcie->mode = readb(pcie->dbi + PCI_HEADER_TYPE) & 0x7f; | ||||
|  | ||||
| 	if (pcie->mode == PCI_HEADER_TYPE_NORMAL) { | ||||
| 		printf("PCIe%u: %s %s", pcie->idx, dev->name, "Endpoint"); | ||||
| 			ls_pcie_setup_ep(pcie); | ||||
| 	} else { | ||||
| 		printf("PCIe%u: %s %s", pcie->idx, dev->name, "Root Complex"); | ||||
| 			ls_pcie_setup_ctrl(pcie); | ||||
| 	} | ||||
|  | ||||
| 	if (!ls_pcie_link_up(pcie)) { | ||||
| 		/* Let the user know there's no PCIe link */ | ||||
| 		printf(": no link\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Print the negotiated PCIe link width */ | ||||
| 	link_sta = readw(pcie->dbi + PCIE_LINK_STA); | ||||
| 	printf(": x%d gen%d\n", (link_sta & PCIE_LINK_WIDTH_MASK) >> 4, | ||||
| 	       link_sta & PCIE_LINK_SPEED_MASK); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static const struct dm_pci_ops ls_pcie_ops = { | ||||
| 	.read_config	= ls_pcie_read_config, | ||||
| 	.write_config	= ls_pcie_write_config, | ||||
| }; | ||||
|  | ||||
| static const struct udevice_id ls_pcie_ids[] = { | ||||
| 	{ .compatible = "fsl,ls-pcie" }, | ||||
| 	{ } | ||||
| }; | ||||
|  | ||||
| U_BOOT_DRIVER(pci_layerscape) = { | ||||
| 	.name = "pci_layerscape", | ||||
| 	.id = UCLASS_PCI, | ||||
| 	.of_match = ls_pcie_ids, | ||||
| 	.ops = &ls_pcie_ops, | ||||
| 	.probe	= ls_pcie_probe, | ||||
| 	.priv_auto_alloc_size = sizeof(struct ls_pcie), | ||||
| }; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0+ */ | ||||
| /* | ||||
|  * Copyright 2017-2019 NXP | ||||
|  * Copyright 2017-2020 NXP | ||||
|  * Copyright 2014-2015 Freescale Semiconductor, Inc. | ||||
|  * Layerscape PCIe driver | ||||
|  */ | ||||
| @@ -60,7 +60,8 @@ | ||||
| /* DBI registers */ | ||||
| #define PCIE_SRIOV		0x178 | ||||
| #define PCIE_STRFMR1		0x71c /* Symbol Timer & Filter Mask Register1 */ | ||||
| #define PCIE_DBI_RO_WR_EN	0x8bc | ||||
| #define PCIE_DBI_RO_WR_EN		BIT(0) | ||||
| #define PCIE_MISC_CONTROL_1_OFF         0x8BC | ||||
|  | ||||
| #define PCIE_LINK_CAP		0x7c | ||||
| #define PCIE_LINK_SPEED_MASK	0xf | ||||
| @@ -82,7 +83,7 @@ | ||||
| 				 PCIE_LCTRL0_CFG2_ENABLE) | ||||
|  | ||||
| #define PCIE_NO_SRIOV_BAR_BASE	0x1000 | ||||
|  | ||||
| #define FSL_PCIE_EP_MIN_APERTURE        4096     /* 4 Kbytes */ | ||||
| #define PCIE_PF_NUM		2 | ||||
| #define PCIE_VF_NUM		64 | ||||
|  | ||||
| @@ -129,25 +130,52 @@ | ||||
| #define LS1021_LTSSM_STATE_SHIFT	20 | ||||
|  | ||||
| struct ls_pcie { | ||||
| 	void __iomem *dbi; | ||||
| 	void __iomem *lut; | ||||
| 	void __iomem *ctrl; | ||||
| 	int idx; | ||||
| 	bool big_endian; | ||||
| 	int mode; | ||||
| }; | ||||
|  | ||||
| struct ls_pcie_rc { | ||||
| 	struct ls_pcie *pcie; | ||||
| 	struct list_head list; | ||||
| 	struct udevice *bus; | ||||
| 	struct fdt_resource dbi_res; | ||||
| 	struct fdt_resource lut_res; | ||||
| 	struct fdt_resource ctrl_res; | ||||
| 	struct fdt_resource cfg_res; | ||||
| 	void __iomem *dbi; | ||||
| 	void __iomem *lut; | ||||
| 	void __iomem *ctrl; | ||||
| 	void __iomem *cfg0; | ||||
| 	void __iomem *cfg1; | ||||
| 	bool big_endian; | ||||
| 	bool enabled; | ||||
| 	int next_lut_index; | ||||
| 	int stream_id_cur; | ||||
| 	int mode; | ||||
| }; | ||||
|  | ||||
| struct ls_pcie_ep { | ||||
| 	struct fdt_resource addr_res; | ||||
| 	struct ls_pcie *pcie; | ||||
| 	struct udevice *bus; | ||||
| 	void __iomem *addr; | ||||
| 	u32 num_ib_wins; | ||||
| 	u32 num_ob_wins; | ||||
| 	u8 max_functions; | ||||
| }; | ||||
|  | ||||
| extern struct list_head ls_pcie_list; | ||||
|  | ||||
| unsigned int dbi_readl(struct ls_pcie *pcie, unsigned int offset); | ||||
| void dbi_writel(struct ls_pcie *pcie, unsigned int value, unsigned int offset); | ||||
| unsigned int ctrl_readl(struct ls_pcie *pcie, unsigned int offset); | ||||
| void ctrl_writel(struct ls_pcie *pcie, unsigned int value, unsigned int offset); | ||||
| void ls_pcie_atu_outbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| 			      u64 phys, u64 bus_addr, pci_size_t size); | ||||
| void ls_pcie_atu_inbound_set(struct ls_pcie *pcie, int idx, int type, | ||||
| 			     int bar, u64 phys); | ||||
| void ls_pcie_dump_atu(struct ls_pcie *pcie); | ||||
| int ls_pcie_link_up(struct ls_pcie *pcie); | ||||
| void ls_pcie_dbi_ro_wr_en(struct ls_pcie *pcie); | ||||
| void ls_pcie_dbi_ro_wr_dis(struct ls_pcie *pcie); | ||||
|  | ||||
| #endif /* _PCIE_LAYERSCAPE_H_ */ | ||||
|   | ||||
							
								
								
									
										241
									
								
								drivers/pci/pcie_layerscape_ep.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								drivers/pci/pcie_layerscape_ep.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0+ | ||||
| /* | ||||
|  * Copyright 2020 NXP | ||||
|  * Layerscape PCIe EP driver | ||||
|  */ | ||||
|  | ||||
| #include <common.h> | ||||
| #include <dm.h> | ||||
| #include <dm/devres.h> | ||||
| #include <errno.h> | ||||
| #include <pci_ep.h> | ||||
| #include <asm/io.h> | ||||
| #include <linux/sizes.h> | ||||
| #include <linux/log2.h> | ||||
| #include "pcie_layerscape.h" | ||||
|  | ||||
| DECLARE_GLOBAL_DATA_PTR; | ||||
|  | ||||
| static void ls_pcie_ep_enable_cfg(struct ls_pcie_ep *pcie_ep) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_ep->pcie; | ||||
| 	u32 config; | ||||
|  | ||||
| 	config = ctrl_readl(pcie,  PCIE_PF_CONFIG); | ||||
| 	config |= PCIE_CONFIG_READY; | ||||
| 	ctrl_writel(pcie, config, PCIE_PF_CONFIG); | ||||
| } | ||||
|  | ||||
| static int ls_ep_set_bar(struct udevice *dev, uint fn, struct pci_bar *ep_bar) | ||||
| { | ||||
| 	struct ls_pcie_ep *pcie_ep = dev_get_priv(dev); | ||||
| 	struct ls_pcie *pcie = pcie_ep->pcie; | ||||
| 	dma_addr_t bar_phys = ep_bar->phys_addr; | ||||
| 	enum pci_barno bar = ep_bar->barno; | ||||
| 	u32 reg = PCI_BASE_ADDRESS_0 + (4 * bar); | ||||
| 	int flags = ep_bar->flags; | ||||
| 	int type, idx; | ||||
| 	u64 size; | ||||
|  | ||||
| 	idx  = bar; | ||||
| 	/* BAR size is 2^(aperture + 11) */ | ||||
| 	size = max_t(size_t, ep_bar->size, FSL_PCIE_EP_MIN_APERTURE); | ||||
|  | ||||
| 	if (!(flags & PCI_BASE_ADDRESS_SPACE)) | ||||
| 		type = PCIE_ATU_TYPE_MEM; | ||||
| 	else | ||||
| 		type = PCIE_ATU_TYPE_IO; | ||||
|  | ||||
| 	ls_pcie_atu_inbound_set(pcie, idx, bar, bar_phys, type); | ||||
|  | ||||
| 	dbi_writel(pcie, lower_32_bits(size - 1), reg + PCIE_NO_SRIOV_BAR_BASE); | ||||
| 	dbi_writel(pcie, flags, reg); | ||||
|  | ||||
| 	if (flags & PCI_BASE_ADDRESS_MEM_TYPE_64) { | ||||
| 		dbi_writel(pcie, upper_32_bits(size - 1), | ||||
| 			   reg + 4 + PCIE_NO_SRIOV_BAR_BASE); | ||||
| 		dbi_writel(pcie, 0, reg + 4); | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static struct pci_ep_ops ls_pcie_ep_ops = { | ||||
| 	.set_bar = ls_ep_set_bar, | ||||
| }; | ||||
|  | ||||
| static void ls_pcie_ep_setup_atu(struct ls_pcie_ep *pcie_ep) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_ep->pcie; | ||||
| 	u64 phys = CONFIG_SYS_PCI_EP_MEMORY_BASE; | ||||
|  | ||||
| 	/* ATU 0 : INBOUND : map BAR0 */ | ||||
| 	ls_pcie_atu_inbound_set(pcie, 0, PCIE_ATU_TYPE_MEM, 0, phys); | ||||
| 	/* ATU 1 : INBOUND : map BAR1 */ | ||||
| 	phys += PCIE_BAR1_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 1, PCIE_ATU_TYPE_MEM, 1, phys); | ||||
| 	/* ATU 2 : INBOUND : map BAR2 */ | ||||
| 	phys += PCIE_BAR2_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 2, PCIE_ATU_TYPE_MEM, 2, phys); | ||||
| 	/* ATU 3 : INBOUND : map BAR4 */ | ||||
| 	phys = CONFIG_SYS_PCI_EP_MEMORY_BASE + PCIE_BAR4_SIZE; | ||||
| 	ls_pcie_atu_inbound_set(pcie, 3, PCIE_ATU_TYPE_MEM, 4, phys); | ||||
|  | ||||
| 	/* ATU 0 : OUTBOUND : map MEM */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, 0, | ||||
| 				 PCIE_ATU_TYPE_MEM, | ||||
| 				 pcie_ep->addr_res.start, | ||||
| 				 0, | ||||
| 				 CONFIG_SYS_PCI_MEMORY_SIZE); | ||||
| } | ||||
|  | ||||
| /* BAR0 and BAR1 are 32bit BAR2 and BAR4 are 64bit */ | ||||
| static void ls_pcie_ep_setup_bar(void *bar_base, int bar, u32 size) | ||||
| { | ||||
| 	/* The least inbound window is 4KiB */ | ||||
| 	if (size < 4 * 1024) | ||||
| 		return; | ||||
|  | ||||
| 	switch (bar) { | ||||
| 	case 0: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_0); | ||||
| 		break; | ||||
| 	case 1: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_1); | ||||
| 		break; | ||||
| 	case 2: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_2); | ||||
| 		writel(0, bar_base + PCI_BASE_ADDRESS_3); | ||||
| 		break; | ||||
| 	case 4: | ||||
| 		writel(size - 1, bar_base + PCI_BASE_ADDRESS_4); | ||||
| 		writel(0, bar_base + PCI_BASE_ADDRESS_5); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void ls_pcie_ep_setup_bars(void *bar_base) | ||||
| { | ||||
| 	/* BAR0 - 32bit - 4K configuration */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 0, PCIE_BAR0_SIZE); | ||||
| 	/* BAR1 - 32bit - 8K MSIX */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 1, PCIE_BAR1_SIZE); | ||||
| 	/* BAR2 - 64bit - 4K MEM descriptor */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 2, PCIE_BAR2_SIZE); | ||||
| 	/* BAR4 - 64bit - 1M MEM */ | ||||
| 	ls_pcie_ep_setup_bar(bar_base, 4, PCIE_BAR4_SIZE); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_ep(struct ls_pcie_ep *pcie_ep) | ||||
| { | ||||
| 	u32 sriov; | ||||
| 	struct ls_pcie *pcie = pcie_ep->pcie; | ||||
|  | ||||
| 	sriov = readl(pcie->dbi + PCIE_SRIOV); | ||||
| 	if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV) { | ||||
| 		int pf, vf; | ||||
|  | ||||
| 		for (pf = 0; pf < PCIE_PF_NUM; pf++) { | ||||
| 			for (vf = 0; vf <= PCIE_VF_NUM; vf++) { | ||||
| 				ctrl_writel(pcie, PCIE_LCTRL0_VAL(pf, vf), | ||||
| 					    PCIE_PF_VF_CTRL); | ||||
|  | ||||
| 				ls_pcie_ep_setup_bars(pcie->dbi); | ||||
| 				ls_pcie_ep_setup_atu(pcie_ep); | ||||
| 			} | ||||
| 		} | ||||
| 		/* Disable CFG2 */ | ||||
| 		ctrl_writel(pcie, 0, PCIE_PF_VF_CTRL); | ||||
| 	} else { | ||||
| 		ls_pcie_ep_setup_bars(pcie->dbi + PCIE_NO_SRIOV_BAR_BASE); | ||||
| 		ls_pcie_ep_setup_atu(pcie_ep); | ||||
| 	} | ||||
|  | ||||
| 	ls_pcie_ep_enable_cfg(pcie_ep); | ||||
| } | ||||
|  | ||||
| static int ls_pcie_ep_probe(struct udevice *dev) | ||||
| { | ||||
| 	struct ls_pcie_ep *pcie_ep = dev_get_priv(dev); | ||||
| 	struct ls_pcie *pcie; | ||||
| 	u16 link_sta; | ||||
| 	int ret; | ||||
|  | ||||
| 	pcie = devm_kmalloc(dev, sizeof(*pcie), GFP_KERNEL); | ||||
| 	if (!pcie) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	pcie_ep->pcie = pcie; | ||||
|  | ||||
| 	pcie->dbi = (void __iomem *)devfdt_get_addr_index(dev, 0); | ||||
| 	if (!pcie->dbi) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	pcie->ctrl = (void __iomem *)devfdt_get_addr_index(dev, 1); | ||||
| 	if (!pcie->ctrl) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	ret = fdt_get_named_resource(gd->fdt_blob, dev_of_offset(dev), | ||||
| 				     "reg", "reg-names", | ||||
| 				     "addr_space", &pcie_ep->addr_res); | ||||
| 	if (ret) { | ||||
| 		printf("%s: resource \"addr_space\" not found\n", dev->name); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	pcie->idx = ((unsigned long)pcie->dbi - PCIE_SYS_BASE_ADDR) / | ||||
| 		    PCIE_CCSR_SIZE; | ||||
|  | ||||
| 	pcie->big_endian = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), | ||||
| 					   "big-endian"); | ||||
|  | ||||
| 	pcie->mode = readb(pcie->dbi + PCI_HEADER_TYPE) & 0x7f; | ||||
| 	if (pcie->mode != PCI_HEADER_TYPE_NORMAL) | ||||
| 		return 0; | ||||
|  | ||||
| 	pcie_ep->max_functions = fdtdec_get_int(gd->fdt_blob, | ||||
| 						dev_of_offset(dev), | ||||
| 						"max-functions", 1); | ||||
| 	pcie_ep->num_ib_wins = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | ||||
| 					      "num-ib-windows", 8); | ||||
| 	pcie_ep->num_ob_wins = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), | ||||
| 					      "num-ob-windows", 8); | ||||
|  | ||||
| 	printf("PCIe%u: %s %s", pcie->idx, dev->name, "Endpoint"); | ||||
| 	ls_pcie_setup_ep(pcie_ep); | ||||
|  | ||||
| 	if (!ls_pcie_link_up(pcie)) { | ||||
| 		/* Let the user know there's no PCIe link */ | ||||
| 		printf(": no link\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Print the negotiated PCIe link width */ | ||||
| 	link_sta = readw(pcie->dbi + PCIE_LINK_STA); | ||||
| 	printf(": x%d gen%d\n", (link_sta & PCIE_LINK_WIDTH_MASK) >> 4, | ||||
| 	       link_sta & PCIE_LINK_SPEED_MASK); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int ls_pcie_ep_remove(struct udevice *dev) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| const struct udevice_id ls_pcie_ep_ids[] = { | ||||
| 	{ .compatible = "fsl,ls-pcie-ep" }, | ||||
| 	{ } | ||||
| }; | ||||
|  | ||||
| U_BOOT_DRIVER(pci_layerscape_ep) = { | ||||
| 	.name = "pci_layerscape_ep", | ||||
| 	.id	= UCLASS_PCI_EP, | ||||
| 	.of_match = ls_pcie_ep_ids, | ||||
| 	.ops = &ls_pcie_ep_ops, | ||||
| 	.probe = ls_pcie_ep_probe, | ||||
| 	.remove = ls_pcie_ep_remove, | ||||
| 	.priv_auto_alloc_size = sizeof(struct ls_pcie_ep), | ||||
| }; | ||||
| @@ -25,17 +25,19 @@ | ||||
| /* | ||||
|  * Return next available LUT index. | ||||
|  */ | ||||
| static int ls_pcie_next_lut_index(struct ls_pcie *pcie) | ||||
| static int ls_pcie_next_lut_index(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	if (pcie->next_lut_index < PCIE_LUT_ENTRY_COUNT) | ||||
| 		return pcie->next_lut_index++; | ||||
| 	if (pcie_rc->next_lut_index < PCIE_LUT_ENTRY_COUNT) | ||||
| 		return pcie_rc->next_lut_index++; | ||||
| 	else | ||||
| 		return -ENOSPC;  /* LUT is full */ | ||||
| } | ||||
|  | ||||
| static void lut_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| static void lut_writel(struct ls_pcie_rc *pcie_rc, unsigned int value, | ||||
| 		       unsigned int offset) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	if (pcie->big_endian) | ||||
| 		out_be32(pcie->lut + offset, value); | ||||
| 	else | ||||
| @@ -45,12 +47,12 @@ static void lut_writel(struct ls_pcie *pcie, unsigned int value, | ||||
| /* | ||||
|  * Program a single LUT entry | ||||
|  */ | ||||
| static void ls_pcie_lut_set_mapping(struct ls_pcie *pcie, int index, u32 devid, | ||||
| 				    u32 streamid) | ||||
| static void ls_pcie_lut_set_mapping(struct ls_pcie_rc *pcie_rc, int index, | ||||
| 				    u32 devid, u32 streamid) | ||||
| { | ||||
| 	/* leave mask as all zeroes, want to match all bits */ | ||||
| 	lut_writel(pcie, devid << 16, PCIE_LUT_UDR(index)); | ||||
| 	lut_writel(pcie, streamid | PCIE_LUT_ENABLE, PCIE_LUT_LDR(index)); | ||||
| 	lut_writel(pcie_rc, devid << 16, PCIE_LUT_UDR(index)); | ||||
| 	lut_writel(pcie_rc, streamid | PCIE_LUT_ENABLE, PCIE_LUT_LDR(index)); | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -61,7 +63,8 @@ static void ls_pcie_lut_set_mapping(struct ls_pcie *pcie, int index, u32 devid, | ||||
|  *      msi-map = <[devid] [phandle-to-msi-ctrl] [stream-id] [count] | ||||
|  *                 [devid] [phandle-to-msi-ctrl] [stream-id] [count]>; | ||||
|  */ | ||||
| static void fdt_pcie_set_msi_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| static void fdt_pcie_set_msi_map_entry_ls(void *blob, | ||||
| 					  struct ls_pcie_rc *pcie_rc, | ||||
| 					  u32 devid, u32 streamid) | ||||
| { | ||||
| 	u32 *prop; | ||||
| @@ -69,10 +72,11 @@ static void fdt_pcie_set_msi_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| 	int nodeoffset; | ||||
| 	uint svr; | ||||
| 	char *compat = NULL; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	/* find pci controller node */ | ||||
| 	nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie", | ||||
| 						   pcie->dbi_res.start); | ||||
| 						   pcie_rc->dbi_res.start); | ||||
| 	if (nodeoffset < 0) { | ||||
| #ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */ | ||||
| 		svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| @@ -84,7 +88,7 @@ static void fdt_pcie_set_msi_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| 			compat = CONFIG_FSL_PCIE_COMPAT; | ||||
| 		if (compat) | ||||
| 			nodeoffset = fdt_node_offset_by_compat_reg(blob, | ||||
| 					compat, pcie->dbi_res.start); | ||||
| 					compat, pcie_rc->dbi_res.start); | ||||
| #endif | ||||
| 		if (nodeoffset < 0) | ||||
| 			return; | ||||
| @@ -114,7 +118,8 @@ static void fdt_pcie_set_msi_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
|  *      iommu-map = <[devid] [phandle-to-iommu-ctrl] [stream-id] [count] | ||||
|  *                 [devid] [phandle-to-iommu-ctrl] [stream-id] [count]>; | ||||
|  */ | ||||
| static void fdt_pcie_set_iommu_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| static void fdt_pcie_set_iommu_map_entry_ls(void *blob, | ||||
| 					    struct ls_pcie_rc *pcie_rc, | ||||
| 					    u32 devid, u32 streamid) | ||||
| { | ||||
| 	u32 *prop; | ||||
| @@ -123,10 +128,11 @@ static void fdt_pcie_set_iommu_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| 	int lenp; | ||||
| 	uint svr; | ||||
| 	char *compat = NULL; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	/* find pci controller node */ | ||||
| 	nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie", | ||||
| 						   pcie->dbi_res.start); | ||||
| 						   pcie_rc->dbi_res.start); | ||||
| 	if (nodeoffset < 0) { | ||||
| #ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */ | ||||
| 		svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| @@ -139,7 +145,7 @@ static void fdt_pcie_set_iommu_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
|  | ||||
| 		if (compat) | ||||
| 			nodeoffset = fdt_node_offset_by_compat_reg(blob, | ||||
| 						compat, pcie->dbi_res.start); | ||||
| 						compat, pcie_rc->dbi_res.start); | ||||
| #endif | ||||
| 		if (nodeoffset < 0) | ||||
| 			return; | ||||
| @@ -170,7 +176,7 @@ static void fdt_pcie_set_iommu_map_entry_ls(void *blob, struct ls_pcie *pcie, | ||||
| static void fdt_fixup_pcie_ls(void *blob) | ||||
| { | ||||
| 	struct udevice *dev, *bus; | ||||
| 	struct ls_pcie *pcie; | ||||
| 	struct ls_pcie_rc *pcie_rc; | ||||
| 	int streamid; | ||||
| 	int index; | ||||
| 	pci_dev_t bdf; | ||||
| @@ -181,17 +187,18 @@ static void fdt_fixup_pcie_ls(void *blob) | ||||
| 	     pci_find_next_device(&dev)) { | ||||
| 		for (bus = dev; device_is_on_pci_bus(bus);) | ||||
| 			bus = bus->parent; | ||||
| 		pcie = dev_get_priv(bus); | ||||
| 		pcie_rc = dev_get_priv(bus); | ||||
|  | ||||
| 		streamid = pcie_next_streamid(pcie->stream_id_cur, pcie->idx); | ||||
| 		streamid = pcie_next_streamid(pcie_rc->stream_id_cur, | ||||
| 					      pcie_rc->pcie->idx); | ||||
| 		if (streamid < 0) { | ||||
| 			debug("ERROR: no stream ids free\n"); | ||||
| 			continue; | ||||
| 		} else { | ||||
| 			pcie->stream_id_cur++; | ||||
| 			pcie_rc->stream_id_cur++; | ||||
| 		} | ||||
|  | ||||
| 		index = ls_pcie_next_lut_index(pcie); | ||||
| 		index = ls_pcie_next_lut_index(pcie_rc); | ||||
| 		if (index < 0) { | ||||
| 			debug("ERROR: no LUT indexes free\n"); | ||||
| 			continue; | ||||
| @@ -200,27 +207,28 @@ static void fdt_fixup_pcie_ls(void *blob) | ||||
| 		/* the DT fixup must be relative to the hose first_busno */ | ||||
| 		bdf = dm_pci_get_bdf(dev) - PCI_BDF(bus->seq, 0, 0); | ||||
| 		/* map PCI b.d.f to streamID in LUT */ | ||||
| 		ls_pcie_lut_set_mapping(pcie, index, bdf >> 8, | ||||
| 		ls_pcie_lut_set_mapping(pcie_rc, index, bdf >> 8, | ||||
| 					streamid); | ||||
| 		/* update msi-map in device tree */ | ||||
| 		fdt_pcie_set_msi_map_entry_ls(blob, pcie, bdf >> 8, | ||||
| 		fdt_pcie_set_msi_map_entry_ls(blob, pcie_rc, bdf >> 8, | ||||
| 					      streamid); | ||||
| 		/* update iommu-map in device tree */ | ||||
| 		fdt_pcie_set_iommu_map_entry_ls(blob, pcie, bdf >> 8, | ||||
| 		fdt_pcie_set_iommu_map_entry_ls(blob, pcie_rc, bdf >> 8, | ||||
| 						streamid); | ||||
| 	} | ||||
| 	pcie_board_fix_fdt(blob); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| static void ft_pcie_rc_fix(void *blob, struct ls_pcie *pcie) | ||||
| static void ft_pcie_rc_fix(void *blob, struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	int off; | ||||
| 	uint svr; | ||||
| 	char *compat = NULL; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	off = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie", | ||||
| 					    pcie->dbi_res.start); | ||||
| 					    pcie_rc->dbi_res.start); | ||||
| 	if (off < 0) { | ||||
| #ifdef CONFIG_FSL_PCIE_COMPAT /* Compatible with older version of dts node */ | ||||
| 		svr = (get_svr() >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| @@ -232,46 +240,47 @@ static void ft_pcie_rc_fix(void *blob, struct ls_pcie *pcie) | ||||
| 			compat = CONFIG_FSL_PCIE_COMPAT; | ||||
| 		if (compat) | ||||
| 			off = fdt_node_offset_by_compat_reg(blob, | ||||
| 					compat, pcie->dbi_res.start); | ||||
| 					compat, pcie_rc->dbi_res.start); | ||||
| #endif | ||||
| 		if (off < 0) | ||||
| 			return; | ||||
| 	} | ||||
|  | ||||
| 	if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE) | ||||
| 	if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_BRIDGE) | ||||
| 		fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); | ||||
| 	else | ||||
| 		fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); | ||||
| } | ||||
|  | ||||
| static void ft_pcie_ep_fix(void *blob, struct ls_pcie *pcie) | ||||
| static void ft_pcie_ep_fix(void *blob, struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	int off; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	off = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_EP_COMPAT, | ||||
| 					    pcie->dbi_res.start); | ||||
| 					    pcie_rc->dbi_res.start); | ||||
| 	if (off < 0) | ||||
| 		return; | ||||
|  | ||||
| 	if (pcie->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL) | ||||
| 	if (pcie_rc->enabled && pcie->mode == PCI_HEADER_TYPE_NORMAL) | ||||
| 		fdt_set_node_status(blob, off, FDT_STATUS_OKAY, 0); | ||||
| 	else | ||||
| 		fdt_set_node_status(blob, off, FDT_STATUS_DISABLED, 0); | ||||
| } | ||||
|  | ||||
| static void ft_pcie_ls_setup(void *blob, struct ls_pcie *pcie) | ||||
| static void ft_pcie_ls_setup(void *blob, struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	ft_pcie_ep_fix(blob, pcie); | ||||
| 	ft_pcie_rc_fix(blob, pcie); | ||||
| 	ft_pcie_ep_fix(blob, pcie_rc); | ||||
| 	ft_pcie_rc_fix(blob, pcie_rc); | ||||
| } | ||||
|  | ||||
| /* Fixup Kernel DT for PCIe */ | ||||
| void ft_pci_setup_ls(void *blob, struct bd_info *bd) | ||||
| { | ||||
| 	struct ls_pcie *pcie; | ||||
| 	struct ls_pcie_rc *pcie_rc; | ||||
|  | ||||
| 	list_for_each_entry(pcie, &ls_pcie_list, list) | ||||
| 		ft_pcie_ls_setup(blob, pcie); | ||||
| 	list_for_each_entry(pcie_rc, &ls_pcie_list, list) | ||||
| 		ft_pcie_ls_setup(blob, pcie_rc); | ||||
|  | ||||
| #if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2) | ||||
| 	fdt_fixup_pcie_ls(blob); | ||||
|   | ||||
							
								
								
									
										379
									
								
								drivers/pci/pcie_layerscape_rc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										379
									
								
								drivers/pci/pcie_layerscape_rc.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,379 @@ | ||||
| // SPDX-License-Identifier: GPL-2.0+ | ||||
| /* | ||||
|  * Copyright 2020 NXP | ||||
|  * Layerscape PCIe driver | ||||
|  */ | ||||
|  | ||||
| #include <common.h> | ||||
| #include <asm/arch/fsl_serdes.h> | ||||
| #include <pci.h> | ||||
| #include <asm/io.h> | ||||
| #include <errno.h> | ||||
| #include <malloc.h> | ||||
| #include <dm.h> | ||||
| #include <dm/devres.h> | ||||
| #if defined(CONFIG_FSL_LSCH2) || defined(CONFIG_FSL_LSCH3) || \ | ||||
| 	defined(CONFIG_ARM) | ||||
| #include <asm/arch/clock.h> | ||||
| #endif | ||||
| #include "pcie_layerscape.h" | ||||
|  | ||||
| DECLARE_GLOBAL_DATA_PTR; | ||||
|  | ||||
| static void ls_pcie_cfg0_set_busdev(struct ls_pcie_rc *pcie_rc, u32 busdev) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0, | ||||
| 		   PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, busdev, PCIE_ATU_LOWER_TARGET); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_cfg1_set_busdev(struct ls_pcie_rc *pcie_rc, u32 busdev) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1, | ||||
| 		   PCIE_ATU_VIEWPORT); | ||||
| 	dbi_writel(pcie, busdev, PCIE_ATU_LOWER_TARGET); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_atu(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct pci_region *io, *mem, *pref; | ||||
| 	unsigned long long offset = 0; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
| 	int idx = 0; | ||||
| 	uint svr; | ||||
|  | ||||
| 	svr = get_svr(); | ||||
| 	if (((svr >> SVR_VAR_PER_SHIFT) & SVR_LS102XA_MASK) == SVR_LS102XA) { | ||||
| 		offset = LS1021_PCIE_SPACE_OFFSET + | ||||
| 			 LS1021_PCIE_SPACE_SIZE * pcie->idx; | ||||
| 	} | ||||
|  | ||||
| 	/* ATU 0 : OUTBOUND : CFG0 */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, PCIE_ATU_REGION_INDEX0, | ||||
| 				 PCIE_ATU_TYPE_CFG0, | ||||
| 				 pcie_rc->cfg_res.start + offset, | ||||
| 				 0, | ||||
| 				 fdt_resource_size(&pcie_rc->cfg_res) / 2); | ||||
| 	/* ATU 1 : OUTBOUND : CFG1 */ | ||||
| 	ls_pcie_atu_outbound_set(pcie, PCIE_ATU_REGION_INDEX1, | ||||
| 				 PCIE_ATU_TYPE_CFG1, | ||||
| 				 pcie_rc->cfg_res.start + offset + | ||||
| 				 fdt_resource_size(&pcie_rc->cfg_res) / 2, | ||||
| 				 0, | ||||
| 				 fdt_resource_size(&pcie_rc->cfg_res) / 2); | ||||
|  | ||||
| 	pci_get_regions(pcie_rc->bus, &io, &mem, &pref); | ||||
| 	idx = PCIE_ATU_REGION_INDEX1 + 1; | ||||
|  | ||||
| 	/* Fix the pcie memory map for LS2088A series SoCs */ | ||||
| 	svr = (svr >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| 	if (svr == SVR_LS2088A || svr == SVR_LS2084A || | ||||
| 	    svr == SVR_LS2048A || svr == SVR_LS2044A || | ||||
| 	    svr == SVR_LS2081A || svr == SVR_LS2041A) { | ||||
| 		if (io) | ||||
| 			io->phys_start = (io->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		if (mem) | ||||
| 			mem->phys_start = (mem->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		if (pref) | ||||
| 			pref->phys_start = (pref->phys_start & | ||||
| 					 (PCIE_PHYS_SIZE - 1)) + | ||||
| 					 LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 	} | ||||
|  | ||||
| 	if (io) | ||||
| 		/* ATU : OUTBOUND : IO */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_IO, | ||||
| 					 io->phys_start + offset, | ||||
| 					 io->bus_start, | ||||
| 					 io->size); | ||||
|  | ||||
| 	if (mem) | ||||
| 		/* ATU : OUTBOUND : MEM */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_MEM, | ||||
| 					 mem->phys_start + offset, | ||||
| 					 mem->bus_start, | ||||
| 					 mem->size); | ||||
|  | ||||
| 	if (pref) | ||||
| 		/* ATU : OUTBOUND : pref */ | ||||
| 		ls_pcie_atu_outbound_set(pcie, idx++, | ||||
| 					 PCIE_ATU_TYPE_MEM, | ||||
| 					 pref->phys_start + offset, | ||||
| 					 pref->bus_start, | ||||
| 					 pref->size); | ||||
|  | ||||
| 	ls_pcie_dump_atu(pcie); | ||||
| } | ||||
|  | ||||
| /* Return 0 if the address is valid, -errno if not valid */ | ||||
| static int ls_pcie_addr_valid(struct ls_pcie_rc *pcie_rc, pci_dev_t bdf) | ||||
| { | ||||
| 	struct udevice *bus = pcie_rc->bus; | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	if (pcie->mode == PCI_HEADER_TYPE_NORMAL) | ||||
| 		return -ENODEV; | ||||
|  | ||||
| 	if (!pcie_rc->enabled) | ||||
| 		return -ENXIO; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) < bus->seq) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if ((PCI_BUS(bdf) > bus->seq) && (!ls_pcie_link_up(pcie))) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) <= (bus->seq + 1) && (PCI_DEV(bdf) > 0)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int ls_pcie_conf_address(const struct udevice *bus, pci_dev_t bdf, | ||||
| 			 uint offset, void **paddress) | ||||
| { | ||||
| 	struct ls_pcie_rc *pcie_rc = dev_get_priv(bus); | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
| 	u32 busdev; | ||||
|  | ||||
| 	if (ls_pcie_addr_valid(pcie_rc, bdf)) | ||||
| 		return -EINVAL; | ||||
|  | ||||
| 	if (PCI_BUS(bdf) == bus->seq) { | ||||
| 		*paddress = pcie->dbi + offset; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	busdev = PCIE_ATU_BUS(PCI_BUS(bdf) - bus->seq) | | ||||
| 		 PCIE_ATU_DEV(PCI_DEV(bdf)) | | ||||
| 		 PCIE_ATU_FUNC(PCI_FUNC(bdf)); | ||||
|  | ||||
| 	if (PCI_BUS(bdf) == bus->seq + 1) { | ||||
| 		ls_pcie_cfg0_set_busdev(pcie_rc, busdev); | ||||
| 		*paddress = pcie_rc->cfg0 + offset; | ||||
| 	} else { | ||||
| 		ls_pcie_cfg1_set_busdev(pcie_rc, busdev); | ||||
| 		*paddress = pcie_rc->cfg1 + offset; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int ls_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, | ||||
| 			       uint offset, ulong *valuep, | ||||
| 			       enum pci_size_t size) | ||||
| { | ||||
| 	return pci_generic_mmap_read_config(bus, ls_pcie_conf_address, | ||||
| 					    bdf, offset, valuep, size); | ||||
| } | ||||
|  | ||||
| static int ls_pcie_write_config(struct udevice *bus, pci_dev_t bdf, | ||||
| 				uint offset, ulong value, | ||||
| 				enum pci_size_t size) | ||||
| { | ||||
| 	return pci_generic_mmap_write_config(bus, ls_pcie_conf_address, | ||||
| 					     bdf, offset, value, size); | ||||
| } | ||||
|  | ||||
| /* Clear multi-function bit */ | ||||
| static void ls_pcie_clear_multifunction(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	writeb(PCI_HEADER_TYPE_BRIDGE, pcie->dbi + PCI_HEADER_TYPE); | ||||
| } | ||||
|  | ||||
| /* Fix class value */ | ||||
| static void ls_pcie_fix_class(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	writew(PCI_CLASS_BRIDGE_PCI, pcie->dbi + PCI_CLASS_DEVICE); | ||||
| } | ||||
|  | ||||
| /* Drop MSG TLP except for Vendor MSG */ | ||||
| static void ls_pcie_drop_msg_tlp(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
| 	u32 val; | ||||
|  | ||||
| 	val = dbi_readl(pcie, PCIE_STRFMR1); | ||||
| 	val &= 0xDFFFFFFF; | ||||
| 	dbi_writel(pcie, val, PCIE_STRFMR1); | ||||
| } | ||||
|  | ||||
| /* Disable all bars in RC mode */ | ||||
| static void ls_pcie_disable_bars(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	dbi_writel(pcie, 0, PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_0); | ||||
| 	dbi_writel(pcie, 0, PCIE_CS2_OFFSET + PCI_BASE_ADDRESS_1); | ||||
| 	dbi_writel(pcie, 0xfffffffe, PCIE_CS2_OFFSET + PCI_ROM_ADDRESS1); | ||||
| } | ||||
|  | ||||
| static void ls_pcie_setup_ctrl(struct ls_pcie_rc *pcie_rc) | ||||
| { | ||||
| 	struct ls_pcie *pcie = pcie_rc->pcie; | ||||
|  | ||||
| 	ls_pcie_setup_atu(pcie_rc); | ||||
|  | ||||
| 	ls_pcie_dbi_ro_wr_en(pcie); | ||||
| 	ls_pcie_fix_class(pcie_rc); | ||||
| 	ls_pcie_clear_multifunction(pcie_rc); | ||||
| 	ls_pcie_drop_msg_tlp(pcie_rc); | ||||
| 	ls_pcie_dbi_ro_wr_dis(pcie); | ||||
|  | ||||
| 	ls_pcie_disable_bars(pcie_rc); | ||||
| 	pcie_rc->stream_id_cur = 0; | ||||
| } | ||||
|  | ||||
| static int ls_pcie_probe(struct udevice *dev) | ||||
| { | ||||
| 	struct ls_pcie_rc *pcie_rc = dev_get_priv(dev); | ||||
| 	const void *fdt = gd->fdt_blob; | ||||
| 	int node = dev_of_offset(dev); | ||||
| 	struct ls_pcie *pcie; | ||||
| 	u16 link_sta; | ||||
| 	uint svr; | ||||
| 	int ret; | ||||
| 	fdt_size_t cfg_size; | ||||
|  | ||||
| 	pcie_rc->bus = dev; | ||||
|  | ||||
| 	pcie = devm_kmalloc(dev, sizeof(*pcie), GFP_KERNEL); | ||||
| 	if (!pcie) | ||||
| 		return -ENOMEM; | ||||
|  | ||||
| 	pcie_rc->pcie = pcie; | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "dbi", &pcie_rc->dbi_res); | ||||
| 	if (ret) { | ||||
| 		printf("ls-pcie: resource \"dbi\" not found\n"); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	pcie->idx = (pcie_rc->dbi_res.start - PCIE_SYS_BASE_ADDR) / | ||||
| 		    PCIE_CCSR_SIZE; | ||||
|  | ||||
| 	list_add(&pcie_rc->list, &ls_pcie_list); | ||||
|  | ||||
| 	pcie_rc->enabled = is_serdes_configured(PCIE_SRDS_PRTCL(pcie->idx)); | ||||
| 	if (!pcie_rc->enabled) { | ||||
| 		printf("PCIe%d: %s disabled\n", pcie->idx, dev->name); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	pcie->dbi = map_physmem(pcie_rc->dbi_res.start, | ||||
| 				fdt_resource_size(&pcie_rc->dbi_res), | ||||
| 				MAP_NOCACHE); | ||||
|  | ||||
| 	pcie->mode = readb(pcie->dbi + PCI_HEADER_TYPE) & 0x7f; | ||||
| 	if (pcie->mode == PCI_HEADER_TYPE_NORMAL) | ||||
| 		return 0; | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "lut", &pcie_rc->lut_res); | ||||
| 	if (!ret) | ||||
| 		pcie->lut = map_physmem(pcie_rc->lut_res.start, | ||||
| 					fdt_resource_size(&pcie_rc->lut_res), | ||||
| 					MAP_NOCACHE); | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "ctrl", &pcie_rc->ctrl_res); | ||||
| 	if (!ret) | ||||
| 		pcie->ctrl = map_physmem(pcie_rc->ctrl_res.start, | ||||
| 					 fdt_resource_size(&pcie_rc->ctrl_res), | ||||
| 					 MAP_NOCACHE); | ||||
| 	if (!pcie->ctrl) | ||||
| 		pcie->ctrl = pcie->lut; | ||||
|  | ||||
| 	if (!pcie->ctrl) { | ||||
| 		printf("%s: NOT find CTRL\n", dev->name); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", | ||||
| 				     "config", &pcie_rc->cfg_res); | ||||
| 	if (ret) { | ||||
| 		printf("%s: resource \"config\" not found\n", dev->name); | ||||
| 		return ret; | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| 	 * Fix the pcie memory map address and PF control registers address | ||||
| 	 * for LS2088A series SoCs | ||||
| 	 */ | ||||
| 	svr = get_svr(); | ||||
| 	svr = (svr >> SVR_VAR_PER_SHIFT) & 0xFFFFFE; | ||||
| 	if (svr == SVR_LS2088A || svr == SVR_LS2084A || | ||||
| 	    svr == SVR_LS2048A || svr == SVR_LS2044A || | ||||
| 	    svr == SVR_LS2081A || svr == SVR_LS2041A) { | ||||
| 		cfg_size = fdt_resource_size(&pcie_rc->cfg_res); | ||||
| 		pcie_rc->cfg_res.start = LS2088A_PCIE1_PHYS_ADDR + | ||||
| 					 LS2088A_PCIE_PHYS_SIZE * pcie->idx; | ||||
| 		pcie_rc->cfg_res.end = pcie_rc->cfg_res.start + cfg_size; | ||||
| 		pcie->ctrl = pcie->lut + 0x40000; | ||||
| 	} | ||||
|  | ||||
| 	pcie_rc->cfg0 = map_physmem(pcie_rc->cfg_res.start, | ||||
| 				    fdt_resource_size(&pcie_rc->cfg_res), | ||||
| 				    MAP_NOCACHE); | ||||
| 	pcie_rc->cfg1 = pcie_rc->cfg0 + | ||||
| 			fdt_resource_size(&pcie_rc->cfg_res) / 2; | ||||
|  | ||||
| 	pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); | ||||
|  | ||||
| 	debug("%s dbi:%lx lut:%lx ctrl:0x%lx cfg0:0x%lx, big-endian:%d\n", | ||||
| 	      dev->name, (unsigned long)pcie->dbi, (unsigned long)pcie->lut, | ||||
| 	      (unsigned long)pcie->ctrl, (unsigned long)pcie_rc->cfg0, | ||||
| 	      pcie->big_endian); | ||||
|  | ||||
| 	printf("PCIe%u: %s %s", pcie->idx, dev->name, "Root Complex"); | ||||
| 	ls_pcie_setup_ctrl(pcie_rc); | ||||
|  | ||||
| 	if (!ls_pcie_link_up(pcie)) { | ||||
| 		/* Let the user know there's no PCIe link */ | ||||
| 		printf(": no link\n"); | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Print the negotiated PCIe link width */ | ||||
| 	link_sta = readw(pcie->dbi + PCIE_LINK_STA); | ||||
| 	printf(": x%d gen%d\n", (link_sta & PCIE_LINK_WIDTH_MASK) >> 4, | ||||
| 	       link_sta & PCIE_LINK_SPEED_MASK); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static const struct dm_pci_ops ls_pcie_ops = { | ||||
| 	.read_config	= ls_pcie_read_config, | ||||
| 	.write_config	= ls_pcie_write_config, | ||||
| }; | ||||
|  | ||||
| static const struct udevice_id ls_pcie_ids[] = { | ||||
| 	{ .compatible = "fsl,ls-pcie" }, | ||||
| 	{ } | ||||
| }; | ||||
|  | ||||
| U_BOOT_DRIVER(pci_layerscape) = { | ||||
| 	.name = "pci_layerscape", | ||||
| 	.id = UCLASS_PCI, | ||||
| 	.of_match = ls_pcie_ids, | ||||
| 	.ops = &ls_pcie_ops, | ||||
| 	.probe	= ls_pcie_probe, | ||||
| 	.priv_auto_alloc_size = sizeof(struct ls_pcie_rc), | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user