mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	Upstream Linux kernel uses for mpc8548-based PCIe controllers compatible string "fsl,mpc8548-pcie". So change U-Boot fsl PCIe driver and all DTS files to use "fsl,mpc8548-pcie" instead of "fsl,pcie-mpc8548" to be compatible with Linux kernel. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Priyanka Jain <priyanka.jain@nxp.com>
		
			
				
	
	
		
			671 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			671 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+ OR X11
 | |
| /*
 | |
|  * Copyright 2019 NXP
 | |
|  *
 | |
|  * PCIe DM U-Boot driver for Freescale PowerPC SoCs
 | |
|  * Author: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| #include <dm.h>
 | |
| #include <malloc.h>
 | |
| #include <mapmem.h>
 | |
| #include <pci.h>
 | |
| #include <asm/fsl_pci.h>
 | |
| #include <asm/fsl_serdes.h>
 | |
| #include <asm/global_data.h>
 | |
| #include <asm/io.h>
 | |
| #include <linux/delay.h>
 | |
| #include "pcie_fsl.h"
 | |
| #include <dm/device_compat.h>
 | |
| 
 | |
| LIST_HEAD(fsl_pcie_list);
 | |
| 
 | |
| static int fsl_pcie_link_up(struct fsl_pcie *pcie);
 | |
| 
 | |
| static int fsl_pcie_addr_valid(struct fsl_pcie *pcie, pci_dev_t bdf)
 | |
| {
 | |
| 	struct udevice *bus = pcie->bus;
 | |
| 
 | |
| 	if (!pcie->enabled)
 | |
| 		return -ENXIO;
 | |
| 
 | |
| 	if (PCI_BUS(bdf) < dev_seq(bus))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (PCI_BUS(bdf) > dev_seq(bus) && (!fsl_pcie_link_up(pcie) || pcie->mode))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (PCI_BUS(bdf) == dev_seq(bus) && (PCI_DEV(bdf) > 0 || PCI_FUNC(bdf) > 0))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	if (PCI_BUS(bdf) == (dev_seq(bus) + 1) && (PCI_DEV(bdf) > 0))
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_read_config(const struct udevice *bus, pci_dev_t bdf,
 | |
| 				uint offset, ulong *valuep,
 | |
| 				enum pci_size_t size)
 | |
| {
 | |
| 	struct fsl_pcie *pcie = dev_get_priv(bus);
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	u32 val;
 | |
| 
 | |
| 	if (fsl_pcie_addr_valid(pcie, bdf)) {
 | |
| 		*valuep = pci_get_ff(size);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	val = PCI_CONF1_EXT_ADDRESS(PCI_BUS(bdf) - dev_seq(bus),
 | |
| 				    PCI_DEV(bdf), PCI_FUNC(bdf),
 | |
| 				    offset);
 | |
| 	out_be32(®s->cfg_addr, val);
 | |
| 
 | |
| 	sync();
 | |
| 
 | |
| 	switch (size) {
 | |
| 	case PCI_SIZE_8:
 | |
| 		*valuep = in_8((u8 *)®s->cfg_data + (offset & 3));
 | |
| 		break;
 | |
| 	case PCI_SIZE_16:
 | |
| 		*valuep = in_le16((u16 *)((u8 *)®s->cfg_data +
 | |
| 			  (offset & 2)));
 | |
| 		break;
 | |
| 	case PCI_SIZE_32:
 | |
| 		*valuep = in_le32(®s->cfg_data);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_write_config(struct udevice *bus, pci_dev_t bdf,
 | |
| 				 uint offset, ulong value,
 | |
| 				 enum pci_size_t size)
 | |
| {
 | |
| 	struct fsl_pcie *pcie = dev_get_priv(bus);
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	u32 val;
 | |
| 	u8 val_8;
 | |
| 	u16 val_16;
 | |
| 	u32 val_32;
 | |
| 
 | |
| 	if (fsl_pcie_addr_valid(pcie, bdf))
 | |
| 		return 0;
 | |
| 
 | |
| 	val = PCI_CONF1_EXT_ADDRESS(PCI_BUS(bdf) - dev_seq(bus),
 | |
| 				    PCI_DEV(bdf), PCI_FUNC(bdf),
 | |
| 				    offset);
 | |
| 	out_be32(®s->cfg_addr, val);
 | |
| 
 | |
| 	sync();
 | |
| 
 | |
| 	switch (size) {
 | |
| 	case PCI_SIZE_8:
 | |
| 		val_8 = value;
 | |
| 		out_8((u8 *)®s->cfg_data + (offset & 3), val_8);
 | |
| 		break;
 | |
| 	case PCI_SIZE_16:
 | |
| 		val_16 = value;
 | |
| 		out_le16((u16 *)((u8 *)®s->cfg_data + (offset & 2)), val_16);
 | |
| 		break;
 | |
| 	case PCI_SIZE_32:
 | |
| 		val_32 = value;
 | |
| 		out_le32(®s->cfg_data, val_32);
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_read_config(struct fsl_pcie *pcie, uint offset,
 | |
| 				     ulong *valuep, enum pci_size_t size)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct udevice *bus = pcie->bus;
 | |
| 
 | |
| 	ret = fsl_pcie_read_config(bus, PCI_BDF(dev_seq(bus), 0, 0),
 | |
| 				   offset, valuep, size);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_write_config(struct fsl_pcie *pcie, uint offset,
 | |
| 				      ulong value, enum pci_size_t size)
 | |
| {
 | |
| 	struct udevice *bus = pcie->bus;
 | |
| 
 | |
| 	return fsl_pcie_write_config(bus, PCI_BDF(dev_seq(bus), 0, 0),
 | |
| 				     offset, value, size);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_read_config_byte(struct fsl_pcie *pcie, uint offset,
 | |
| 					  u8 *valuep)
 | |
| {
 | |
| 	ulong val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_8);
 | |
| 	*valuep = val;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_read_config_word(struct fsl_pcie *pcie, uint offset,
 | |
| 					  u16 *valuep)
 | |
| {
 | |
| 	ulong val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_16);
 | |
| 	*valuep = val;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_read_config_dword(struct fsl_pcie *pcie, uint offset,
 | |
| 					   u32 *valuep)
 | |
| {
 | |
| 	ulong val;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_32);
 | |
| 	*valuep = val;
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_write_config_byte(struct fsl_pcie *pcie, uint offset,
 | |
| 					   u8 value)
 | |
| {
 | |
| 	return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_8);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_write_config_word(struct fsl_pcie *pcie, uint offset,
 | |
| 					   u16 value)
 | |
| {
 | |
| 	return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_16);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_hose_write_config_dword(struct fsl_pcie *pcie, uint offset,
 | |
| 					    u32 value)
 | |
| {
 | |
| 	return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_32);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_link_up(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	u16 ltssm;
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
 | |
| 		ltssm = (in_be32(®s->pex_csr0)
 | |
| 			& PEX_CSR0_LTSSM_MASK) >> PEX_CSR0_LTSSM_SHIFT;
 | |
| 		return ltssm == LTSSM_L0_REV3;
 | |
| 	}
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_word(pcie, PCI_LTSSM, <ssm);
 | |
| 
 | |
| 	return ltssm == LTSSM_L0;
 | |
| }
 | |
| 
 | |
| static bool fsl_pcie_is_agent(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	u8 header_type;
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_byte(pcie, PCI_HEADER_TYPE, &header_type);
 | |
| 
 | |
| 	return (header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_setup_law(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	struct pci_region *io, *mem, *pref;
 | |
| 
 | |
| 	pci_get_regions(pcie->bus, &io, &mem, &pref);
 | |
| 
 | |
| 	if (mem)
 | |
| 		set_next_law(mem->phys_start,
 | |
| 			     law_size_bits(mem->size),
 | |
| 			     pcie->law_trgt_if);
 | |
| 
 | |
| 	if (io)
 | |
| 		set_next_law(io->phys_start,
 | |
| 			     law_size_bits(io->size),
 | |
| 			     pcie->law_trgt_if);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void fsl_pcie_config_ready(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
 | |
| 		setbits_be32(®s->config, FSL_PCIE_V3_CFG_RDY);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	fsl_pcie_hose_write_config_byte(pcie, FSL_PCIE_CFG_RDY, 0x1);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_setup_outbound_win(struct fsl_pcie *pcie, int idx,
 | |
| 				       int type, u64 phys, u64 bus_addr,
 | |
| 				       pci_size_t size)
 | |
| {
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	pot_t *po = ®s->pot[idx];
 | |
| 	u32 war, sz;
 | |
| 
 | |
| 	if (idx < 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	out_be32(&po->powbar, phys >> 12);
 | |
| 	out_be32(&po->potar, bus_addr >> 12);
 | |
| #ifdef CONFIG_SYS_PCI_64BIT
 | |
| 	out_be32(&po->potear, bus_addr >> 44);
 | |
| #else
 | |
| 	out_be32(&po->potear, 0);
 | |
| #endif
 | |
| 
 | |
| 	sz = (__ilog2_u64((u64)size) - 1);
 | |
| 	war = POWAR_EN | sz;
 | |
| 
 | |
| 	if (type == PCI_REGION_IO)
 | |
| 		war |= POWAR_IO_READ | POWAR_IO_WRITE;
 | |
| 	else
 | |
| 		war |= POWAR_MEM_READ | POWAR_MEM_WRITE;
 | |
| 
 | |
| 	out_be32(&po->powar, war);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_setup_inbound_win(struct fsl_pcie *pcie, int idx,
 | |
| 				      bool pf, u64 phys, u64 bus_addr,
 | |
| 				      pci_size_t size)
 | |
| {
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	pit_t *pi = ®s->pit[idx];
 | |
| 	u32 sz = (__ilog2_u64(size) - 1);
 | |
| 	u32 flag = PIWAR_LOCAL;
 | |
| 
 | |
| 	if (idx < 0)
 | |
| 		return -EINVAL;
 | |
| 
 | |
| 	out_be32(&pi->pitar, phys >> 12);
 | |
| 	out_be32(&pi->piwbar, bus_addr >> 12);
 | |
| 
 | |
| #ifdef CONFIG_SYS_PCI_64BIT
 | |
| 	out_be32(&pi->piwbear, bus_addr >> 44);
 | |
| #else
 | |
| 	out_be32(&pi->piwbear, 0);
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_SYS_FSL_ERRATUM_A005434
 | |
| 	flag = 0;
 | |
| #endif
 | |
| 
 | |
| 	flag |= PIWAR_EN | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
 | |
| 	if (pf)
 | |
| 		flag |= PIWAR_PF;
 | |
| 	out_be32(&pi->piwar, flag | sz);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_setup_outbound_wins(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	struct pci_region *io, *mem, *pref;
 | |
| 	int idx = 1; /* skip 0 */
 | |
| 
 | |
| 	pci_get_regions(pcie->bus, &io, &mem, &pref);
 | |
| 
 | |
| 	if (io)
 | |
| 		/* ATU : OUTBOUND : IO */
 | |
| 		fsl_pcie_setup_outbound_win(pcie, idx++,
 | |
| 					    PCI_REGION_IO,
 | |
| 					    io->phys_start,
 | |
| 					    io->bus_start,
 | |
| 					    io->size);
 | |
| 
 | |
| 	if (mem)
 | |
| 		/* ATU : OUTBOUND : MEM */
 | |
| 		fsl_pcie_setup_outbound_win(pcie, idx++,
 | |
| 					    PCI_REGION_MEM,
 | |
| 					    mem->phys_start,
 | |
| 					    mem->bus_start,
 | |
| 					    mem->size);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_setup_inbound_wins(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS;
 | |
| 	pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS;
 | |
| 	u64 sz = min((u64)gd->ram_size, (1ull << 32));
 | |
| 	pci_size_t pci_sz;
 | |
| 	int idx;
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_2_2)
 | |
| 		idx = 2;
 | |
| 	else
 | |
| 		idx = 3;
 | |
| 
 | |
| 	pci_sz = 1ull << __ilog2_u64(sz);
 | |
| 
 | |
| 	dev_dbg(pcie->bus, "R0 bus_start: %llx phys_start: %llx size: %llx\n",
 | |
| 		(u64)bus_start, (u64)phys_start, (u64)sz);
 | |
| 
 | |
| 	/* if we aren't an exact power of two match, pci_sz is smaller
 | |
| 	 * round it up to the next power of two.  We report the actual
 | |
| 	 * size to pci region tracking.
 | |
| 	 */
 | |
| 	if (pci_sz != sz)
 | |
| 		sz = 2ull << __ilog2_u64(sz);
 | |
| 
 | |
| 	fsl_pcie_setup_inbound_win(pcie, idx--, true,
 | |
| 				   CONFIG_SYS_PCI_MEMORY_PHYS,
 | |
| 				   CONFIG_SYS_PCI_MEMORY_BUS, sz);
 | |
| #if defined(CONFIG_PHYS_64BIT) && defined(CONFIG_SYS_PCI_64BIT)
 | |
| 	/*
 | |
| 	 * On 64-bit capable systems, set up a mapping for all of DRAM
 | |
| 	 * in high pci address space.
 | |
| 	 */
 | |
| 	pci_sz = 1ull << __ilog2_u64(gd->ram_size);
 | |
| 	/* round up to the next largest power of two */
 | |
| 	if (gd->ram_size > pci_sz)
 | |
| 		pci_sz = 1ull << (__ilog2_u64(gd->ram_size) + 1);
 | |
| 
 | |
| 	dev_dbg(pcie->bus, "R64 bus_start: %llx phys_start: %llx size: %llx\n",
 | |
| 		(u64)CONFIG_SYS_PCI64_MEMORY_BUS,
 | |
| 		(u64)CONFIG_SYS_PCI_MEMORY_PHYS, (u64)pci_sz);
 | |
| 
 | |
| 	fsl_pcie_setup_inbound_win(pcie, idx--, true,
 | |
| 				   CONFIG_SYS_PCI_MEMORY_PHYS,
 | |
| 				   CONFIG_SYS_PCI64_MEMORY_BUS, pci_sz);
 | |
| #endif
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_init_atmu(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	fsl_pcie_setup_outbound_wins(pcie);
 | |
| 	fsl_pcie_setup_inbound_wins(pcie);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void fsl_pcie_dbi_read_only_reg_write_enable(struct fsl_pcie *pcie,
 | |
| 						    bool enable)
 | |
| {
 | |
| 	u32 val;
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_dword(pcie, DBI_RO_WR_EN, &val);
 | |
| 	if (enable)
 | |
| 		val |= 1;
 | |
| 	else
 | |
| 		val &= ~1;
 | |
| 	fsl_pcie_hose_write_config_dword(pcie, DBI_RO_WR_EN, val);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_init_port(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	u32 val_32;
 | |
| 	u16 val_16;
 | |
| 
 | |
| 	fsl_pcie_init_atmu(pcie);
 | |
| 
 | |
| #ifdef CONFIG_FSL_PCIE_DISABLE_ASPM
 | |
| 	val_32 = 0;
 | |
| 	fsl_pcie_hose_read_config_dword(pcie, PCI_LCR, &val_32);
 | |
| 	val_32 &= ~0x03;
 | |
| 	fsl_pcie_hose_write_config_dword(pcie, PCI_LCR, val_32);
 | |
| 	udelay(1);
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_FSL_PCIE_RESET
 | |
| 	u16 ltssm;
 | |
| 	int i;
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
 | |
| 		/* assert PCIe reset */
 | |
| 		setbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 		(void)in_be32(®s->pdb_stat);
 | |
| 		udelay(1000);
 | |
| 		/* clear PCIe reset */
 | |
| 		clrbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 		asm("sync;isync");
 | |
| 		for (i = 0; i < 100 && !fsl_pcie_link_up(pcie); i++)
 | |
| 			udelay(1000);
 | |
| 	} else {
 | |
| 		fsl_pcie_hose_read_config_word(pcie, PCI_LTSSM, <ssm);
 | |
| 		if (ltssm == 1) {
 | |
| 			/* assert PCIe reset */
 | |
| 			setbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 			(void)in_be32(®s->pdb_stat);
 | |
| 			udelay(100);
 | |
| 			/* clear PCIe reset */
 | |
| 			clrbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 			asm("sync;isync");
 | |
| 			for (i = 0; i < 100 &&
 | |
| 			     !fsl_pcie_link_up(pcie); i++)
 | |
| 				udelay(1000);
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| #ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003
 | |
| 	if (!fsl_pcie_link_up(pcie)) {
 | |
| 		serdes_corenet_t *srds_regs;
 | |
| 
 | |
| 		srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
 | |
| 		val_32 = in_be32(&srds_regs->srdspccr0);
 | |
| 
 | |
| 		if ((val_32 >> 28) == 3) {
 | |
| 			int i;
 | |
| 
 | |
| 			out_be32(&srds_regs->srdspccr0, 2 << 28);
 | |
| 			setbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 			in_be32(®s->pdb_stat);
 | |
| 			udelay(100);
 | |
| 			clrbits_be32(®s->pdb_stat, 0x08000000);
 | |
| 			asm("sync;isync");
 | |
| 			for (i = 0; i < 100 && !fsl_pcie_link_up(pcie); i++)
 | |
| 				udelay(1000);
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * The Read-Only Write Enable bit defaults to 1 instead of 0.
 | |
| 	 * Set to 0 to protect the read-only registers.
 | |
| 	 */
 | |
| #ifdef CONFIG_SYS_FSL_ERRATUM_A007815
 | |
| 	fsl_pcie_dbi_read_only_reg_write_enable(pcie, false);
 | |
| #endif
 | |
| 
 | |
| 	/*
 | |
| 	 * Enable All Error Interrupts except
 | |
| 	 * - Master abort (pci)
 | |
| 	 * - Master PERR (pci)
 | |
| 	 * - ICCA (PCIe)
 | |
| 	 */
 | |
| 	out_be32(®s->peer, ~0x20140);
 | |
| 
 | |
| 	/* set URR, FER, NFER (but not CER) */
 | |
| 	fsl_pcie_hose_read_config_dword(pcie, PCI_DCR, &val_32);
 | |
| 	val_32 |= 0xf000e;
 | |
| 	fsl_pcie_hose_write_config_dword(pcie, PCI_DCR, val_32);
 | |
| 
 | |
| 	/* Clear all error indications */
 | |
| 	out_be32(®s->pme_msg_det, 0xffffffff);
 | |
| 	out_be32(®s->pme_msg_int_en, 0xffffffff);
 | |
| 	out_be32(®s->pedr, 0xffffffff);
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_word(pcie, PCI_DSR, &val_16);
 | |
| 	if (val_16)
 | |
| 		fsl_pcie_hose_write_config_word(pcie, PCI_DSR, 0xffff);
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_word(pcie, PCI_SEC_STATUS, &val_16);
 | |
| 	if (val_16)
 | |
| 		fsl_pcie_hose_write_config_word(pcie, PCI_SEC_STATUS, 0xffff);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_fixup_classcode(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	u32 classcode_reg;
 | |
| 	u32 val;
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
 | |
| 		classcode_reg = PCI_CLASS_REVISION;
 | |
| 		fsl_pcie_dbi_read_only_reg_write_enable(pcie, true);
 | |
| 	} else {
 | |
| 		classcode_reg = CSR_CLASSCODE;
 | |
| 	}
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_dword(pcie, classcode_reg, &val);
 | |
| 	val &= 0xff;
 | |
| 	val |= PCI_CLASS_BRIDGE_PCI_NORMAL << 8;
 | |
| 	fsl_pcie_hose_write_config_dword(pcie, classcode_reg, val);
 | |
| 
 | |
| 	if (pcie->block_rev >= PEX_IP_BLK_REV_3_0)
 | |
| 		fsl_pcie_dbi_read_only_reg_write_enable(pcie, false);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_init_rc(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	return fsl_pcie_fixup_classcode(pcie);
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_init_ep(struct fsl_pcie *pcie)
 | |
| {
 | |
| 	fsl_pcie_config_ready(pcie);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_probe(struct udevice *dev)
 | |
| {
 | |
| 	struct fsl_pcie *pcie = dev_get_priv(dev);
 | |
| 	ccsr_fsl_pci_t *regs = pcie->regs;
 | |
| 	u16 val_16;
 | |
| 
 | |
| 	pcie->bus = dev;
 | |
| 	pcie->block_rev = in_be32(®s->block_rev1);
 | |
| 
 | |
| 	list_add(&pcie->list, &fsl_pcie_list);
 | |
| 	pcie->enabled = is_serdes_configured(PCIE1 + pcie->idx);
 | |
| 	if (!pcie->enabled) {
 | |
| 		printf("PCIe%d: %s disabled\n", pcie->idx, dev->name);
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	fsl_pcie_setup_law(pcie);
 | |
| 
 | |
| 	pcie->mode = fsl_pcie_is_agent(pcie);
 | |
| 
 | |
| 	fsl_pcie_init_port(pcie);
 | |
| 
 | |
| 	printf("PCIe%d: %s ", pcie->idx, dev->name);
 | |
| 
 | |
| 	if (pcie->mode) {
 | |
| 		printf("Endpoint");
 | |
| 		fsl_pcie_init_ep(pcie);
 | |
| 	} else {
 | |
| 		printf("Root Complex");
 | |
| 		fsl_pcie_init_rc(pcie);
 | |
| 	}
 | |
| 
 | |
| 	if (!fsl_pcie_link_up(pcie)) {
 | |
| 		printf(": %s\n", pcie->mode ? "undetermined link" : "no link");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	fsl_pcie_hose_read_config_word(pcie, PCI_LSR, &val_16);
 | |
| 	printf(": x%d gen%d\n", (val_16 & 0x3f0) >> 4, (val_16 & 0xf));
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static int fsl_pcie_of_to_plat(struct udevice *dev)
 | |
| {
 | |
| 	struct fsl_pcie *pcie = dev_get_priv(dev);
 | |
| 	struct fsl_pcie_data *info;
 | |
| 	int ret;
 | |
| 
 | |
| 	pcie->regs = dev_remap_addr(dev);
 | |
| 	if (!pcie->regs) {
 | |
| 		pr_err("\"reg\" resource not found\n");
 | |
| 		return -EINVAL;
 | |
| 	}
 | |
| 
 | |
| 	ret = dev_read_u32(dev, "law_trgt_if", &pcie->law_trgt_if);
 | |
| 	if (ret < 0) {
 | |
| 		pr_err("\"law_trgt_if\" not found\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	info = (struct fsl_pcie_data *)dev_get_driver_data(dev);
 | |
| 	pcie->info = info;
 | |
| 	pcie->idx = abs((u32)(dev_read_addr(dev) & info->block_offset_mask) -
 | |
| 		    info->block_offset) / info->stride;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static const struct dm_pci_ops fsl_pcie_ops = {
 | |
| 	.read_config	= fsl_pcie_read_config,
 | |
| 	.write_config	= fsl_pcie_write_config,
 | |
| };
 | |
| 
 | |
| static struct fsl_pcie_data p1_p2_data = {
 | |
| 	.block_offset = 0xa000,
 | |
| 	.block_offset_mask = 0xffff,
 | |
| 	.stride = 0x1000,
 | |
| };
 | |
| 
 | |
| static struct fsl_pcie_data p2041_data = {
 | |
| 	.block_offset = 0x200000,
 | |
| 	.block_offset_mask = 0x3fffff,
 | |
| 	.stride = 0x1000,
 | |
| };
 | |
| 
 | |
| static struct fsl_pcie_data t2080_data = {
 | |
| 	.block_offset = 0x240000,
 | |
| 	.block_offset_mask = 0x3fffff,
 | |
| 	.stride = 0x10000,
 | |
| };
 | |
| 
 | |
| static const struct udevice_id fsl_pcie_ids[] = {
 | |
| 	{ .compatible = "fsl,mpc8548-pcie", .data = (ulong)&p1_p2_data },
 | |
| 	{ .compatible = "fsl,pcie-p1_p2", .data = (ulong)&p1_p2_data },
 | |
| 	{ .compatible = "fsl,pcie-p2041", .data = (ulong)&p2041_data },
 | |
| 	{ .compatible = "fsl,pcie-p3041", .data = (ulong)&p2041_data },
 | |
| 	{ .compatible = "fsl,pcie-p4080", .data = (ulong)&p2041_data },
 | |
| 	{ .compatible = "fsl,pcie-p5040", .data = (ulong)&p2041_data },
 | |
| 	{ .compatible = "fsl,pcie-t102x", .data = (ulong)&t2080_data },
 | |
| 	{ .compatible = "fsl,pcie-t104x", .data = (ulong)&t2080_data },
 | |
| 	{ .compatible = "fsl,pcie-t2080", .data = (ulong)&t2080_data },
 | |
| 	{ .compatible = "fsl,pcie-t4240", .data = (ulong)&t2080_data },
 | |
| 	{ }
 | |
| };
 | |
| 
 | |
| U_BOOT_DRIVER(fsl_pcie) = {
 | |
| 	.name = "fsl_pcie",
 | |
| 	.id = UCLASS_PCI,
 | |
| 	.of_match = fsl_pcie_ids,
 | |
| 	.ops = &fsl_pcie_ops,
 | |
| 	.of_to_plat = fsl_pcie_of_to_plat,
 | |
| 	.probe = fsl_pcie_probe,
 | |
| 	.priv_auto	= sizeof(struct fsl_pcie),
 | |
| };
 |