GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
menuconfig PCI
|
||||
bool "PCI support"
|
||||
default y if PPC
|
||||
help
|
||||
Enable support for PCI (Peripheral Interconnect Bus), a type of bus
|
||||
used on some devices to allow the CPU to communicate with its
|
||||
peripherals.
|
||||
|
||||
if PCI
|
||||
|
||||
config DM_PCI
|
||||
bool "Enable driver model for PCI"
|
||||
depends on DM
|
||||
help
|
||||
Use driver model for PCI. Driver model is the new method for
|
||||
orgnising devices in U-Boot. For PCI, driver model keeps track of
|
||||
available PCI devices, allows scanning of PCI buses and provides
|
||||
device configuration support.
|
||||
|
||||
config DM_PCI_COMPAT
|
||||
bool "Enable compatible functions for PCI"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Enable compatibility functions for PCI so that old code can be used
|
||||
with CONFIG_DM_PCI enabled. This should be used as an interim
|
||||
measure when porting a board to use driver model for PCI. Once the
|
||||
board is fully supported, this option should be disabled.
|
||||
|
||||
config PCI_AARDVARK
|
||||
bool "Enable Aardvark PCIe driver"
|
||||
default n
|
||||
depends on DM_PCI
|
||||
depends on ARMADA_3700
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Armada37x0 SoCs. The PCIe controller on Armada37x0 is based on
|
||||
Aardvark hardware.
|
||||
|
||||
config PCI_PNP
|
||||
bool "Enable Plug & Play support for PCI"
|
||||
depends on PCI || DM_PCI
|
||||
default y
|
||||
help
|
||||
Enable PCI memory and I/O space resource allocation and assignment.
|
||||
|
||||
config PCIE_ECAM_GENERIC
|
||||
bool "Generic ECAM-based PCI host controller support"
|
||||
default n
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable support for generic ECAM-based
|
||||
PCIe host controllers, such as the one emulated by QEMU.
|
||||
|
||||
config PCI_PHYTIUM
|
||||
bool "Phytium PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Phytium SoCs.
|
||||
|
||||
config PCIE_DW_MVEBU
|
||||
bool "Enable Armada-8K PCIe driver (DesignWare core)"
|
||||
depends on DM_PCI
|
||||
depends on ARMADA_8K
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Armada-8K SoCs. The PCIe controller on Armada-8K is based on
|
||||
DesignWare hardware.
|
||||
|
||||
config PCIE_FSL
|
||||
bool "FSL PowerPC PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on FSL
|
||||
PowerPC MPC85xx, MPC86xx, B series, P series and T series SoCs.
|
||||
This driver does not support SRIO_PCIE_BOOT feature.
|
||||
|
||||
config PCI_MPC85XX
|
||||
bool "MPC85XX PowerPC PCI support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable PCI controller support on FSL
|
||||
PowerPC MPC85xx SoC.
|
||||
|
||||
config PCI_RCAR_GEN2
|
||||
bool "Renesas RCar Gen2 PCIe driver"
|
||||
depends on DM_PCI
|
||||
depends on RCAR_32
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Renesas RCar Gen2 SoCs. The PCIe controller on RCar Gen2 is
|
||||
also used to access EHCI USB controller on the SoC.
|
||||
|
||||
config PCI_RCAR_GEN3
|
||||
bool "Renesas RCar Gen3 PCIe driver"
|
||||
depends on DM_PCI
|
||||
depends on RCAR_GEN3
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Renesas RCar Gen3 SoCs.
|
||||
|
||||
config PCI_SANDBOX
|
||||
bool "Sandbox PCI support"
|
||||
depends on SANDBOX && DM_PCI
|
||||
help
|
||||
Support PCI on sandbox, as an emulated bus. This permits testing of
|
||||
PCI feature such as bus scanning, device configuration and device
|
||||
access. The available (emulated) devices are defined statically in
|
||||
the device tree but the normal PCI scan technique is used to find
|
||||
then.
|
||||
|
||||
config PCI_TEGRA
|
||||
bool "Tegra PCI support"
|
||||
depends on TEGRA
|
||||
depends on (TEGRA186 && POWER_DOMAIN) || (!TEGRA186)
|
||||
help
|
||||
Enable support for the PCIe controller found on some generations of
|
||||
Tegra. Tegra20 has 2 root ports with a total of 4 lanes, Tegra30 has
|
||||
3 root ports with a total of 6 lanes and Tegra124 has 2 root ports
|
||||
with a total of 5 lanes. Some boards require this for Ethernet
|
||||
support to work (e.g. beaver, jetson-tk1).
|
||||
|
||||
config PCI_XILINX
|
||||
bool "Xilinx AXI Bridge for PCI Express"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Enable support for the Xilinx AXI bridge for PCI express, an IP block
|
||||
which can be used on some generations of Xilinx FPGAs.
|
||||
|
||||
config PCIE_LAYERSCAPE
|
||||
bool "Layerscape PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Support Layerscape PCIe. The Layerscape SoC may have one or several
|
||||
PCIe controllers. The PCIe may works in RC or EP mode according to
|
||||
RCW[HOST_AGT_PEX] setting.
|
||||
|
||||
config PCIE_LAYERSCAPE_GEN4
|
||||
bool "Layerscape Gen4 PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Support PCIe Gen4 on NXP Layerscape SoCs, which may have one or
|
||||
several PCIe controllers. The PCIe controller can work in RC or
|
||||
EP mode according to RCW[HOST_AGT_PEX] setting.
|
||||
|
||||
config PCIE_INTEL_FPGA
|
||||
bool "Intel FPGA PCIe support"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on Intel
|
||||
FPGA, example Stratix 10.
|
||||
|
||||
config PCI_MVEBU
|
||||
bool "Enable Armada XP/38x PCIe driver"
|
||||
depends on ARCH_MVEBU
|
||||
select DM_PCI
|
||||
select MISC
|
||||
help
|
||||
Say Y here if you want to enable PCIe controller support on
|
||||
Armada XP/38x SoCs.
|
||||
|
||||
config PCI_KEYSTONE
|
||||
bool "TI Keystone PCIe controller"
|
||||
depends on DM_PCI
|
||||
help
|
||||
Say Y here if you want to enable PCI controller support on AM654 SoC.
|
||||
|
||||
config PCIE_MEDIATEK
|
||||
bool "MediaTek PCIe Gen2 controller"
|
||||
depends on DM_PCI
|
||||
depends on ARCH_MEDIATEK
|
||||
help
|
||||
Say Y here if you want to enable Gen2 PCIe controller,
|
||||
which could be found on MT7623 SoC family.
|
||||
|
||||
endif
|
||||
@@ -0,0 +1,44 @@
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# (C) Copyright 2000-2007
|
||||
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
|
||||
ifneq ($(CONFIG_DM_PCI),)
|
||||
obj-$(CONFIG_DM_VIDEO) += pci_rom.o
|
||||
obj-$(CONFIG_PCI) += pci-uclass.o pci_auto.o
|
||||
obj-$(CONFIG_DM_PCI_COMPAT) += pci_compat.o
|
||||
obj-$(CONFIG_PCI_SANDBOX) += pci_sandbox.o
|
||||
obj-$(CONFIG_SANDBOX) += pci-emul-uclass.o
|
||||
obj-$(CONFIG_X86) += pci_x86.o pci_rom.o
|
||||
else
|
||||
obj-$(CONFIG_PCI) += pci.o pci_auto_old.o
|
||||
endif
|
||||
obj-$(CONFIG_PCI) += pci_auto_common.o pci_common.o
|
||||
|
||||
obj-$(CONFIG_PCIE_ECAM_GENERIC) += pcie_ecam_generic.o
|
||||
obj-$(CONFIG_FSL_PCI_INIT) += fsl_pci_init.o
|
||||
obj-$(CONFIG_PCI_INDIRECT_BRIDGE) += pci_indirect.o
|
||||
obj-$(CONFIG_PCI_GT64120) += pci_gt64120.o
|
||||
obj-$(CONFIG_PCI_MPC85XX) += pci_mpc85xx.o
|
||||
obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
|
||||
obj-$(CONFIG_PCIE_IMX) += pcie_imx.o
|
||||
obj-$(CONFIG_FTPCI100) += pci_ftpci100.o
|
||||
obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o
|
||||
obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
|
||||
obj-$(CONFIG_PCI_RCAR_GEN3) += pci-rcar-gen3.o
|
||||
obj-$(CONFIG_SH4_PCI) += pci_sh4.o
|
||||
obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
|
||||
obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o
|
||||
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_fixup.o
|
||||
obj-$(CONFIG_PCIE_LAYERSCAPE_GEN4) += pcie_layerscape_gen4.o \
|
||||
pcie_layerscape_gen4_fixup.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_PCI_KEYSTONE) += pcie_dw_ti.o
|
||||
obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
|
||||
@@ -0,0 +1,927 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2007-2012 Freescale Semiconductor, Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <env.h>
|
||||
#include <malloc.h>
|
||||
#include <asm/fsl_serdes.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/*
|
||||
* PCI/PCIE Controller initialization for mpc85xx/mpc86xx soc's
|
||||
*
|
||||
* Initialize controller and call the common driver/pci pci_hose_scan to
|
||||
* scan for bridges and devices.
|
||||
*
|
||||
* Hose fields which need to be pre-initialized by board specific code:
|
||||
* regions[]
|
||||
* first_busno
|
||||
*
|
||||
* Fields updated:
|
||||
* last_busno
|
||||
*/
|
||||
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/fsl_pci.h>
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_BUS
|
||||
#define CONFIG_SYS_PCI_MEMORY_BUS 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_PHYS
|
||||
#define CONFIG_SYS_PCI_MEMORY_PHYS 0
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SYS_PCI_64BIT) && !defined(CONFIG_SYS_PCI64_MEMORY_BUS)
|
||||
#define CONFIG_SYS_PCI64_MEMORY_BUS (64ull*1024*1024*1024)
|
||||
#endif
|
||||
|
||||
/* Setup one inbound ATMU window.
|
||||
*
|
||||
* We let the caller decide what the window size should be
|
||||
*/
|
||||
static void set_inbound_window(volatile pit_t *pi,
|
||||
struct pci_region *r,
|
||||
u64 size)
|
||||
{
|
||||
u32 sz = (__ilog2_u64(size) - 1);
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A005434
|
||||
u32 flag = 0;
|
||||
#else
|
||||
u32 flag = PIWAR_LOCAL;
|
||||
#endif
|
||||
|
||||
flag |= PIWAR_EN | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP;
|
||||
|
||||
out_be32(&pi->pitar, r->phys_start >> 12);
|
||||
out_be32(&pi->piwbar, r->bus_start >> 12);
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
out_be32(&pi->piwbear, r->bus_start >> 44);
|
||||
#else
|
||||
out_be32(&pi->piwbear, 0);
|
||||
#endif
|
||||
if (r->flags & PCI_REGION_PREFETCH)
|
||||
flag |= PIWAR_PF;
|
||||
out_be32(&pi->piwar, flag | sz);
|
||||
}
|
||||
|
||||
int fsl_setup_hose(struct pci_controller *hose, unsigned long addr)
|
||||
{
|
||||
volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *) addr;
|
||||
|
||||
/* Reset hose to make sure its in a clean state */
|
||||
memset(hose, 0, sizeof(struct pci_controller));
|
||||
|
||||
pci_setup_indirect(hose, (u32)&pci->cfg_addr, (u32)&pci->cfg_data);
|
||||
|
||||
return fsl_is_pci_agent(hose);
|
||||
}
|
||||
|
||||
static int fsl_pci_setup_inbound_windows(struct pci_controller *hose,
|
||||
u64 out_lo, u8 pcie_cap,
|
||||
volatile pit_t *pi)
|
||||
{
|
||||
struct pci_region *r = hose->regions + hose->region_count;
|
||||
u64 sz = min((u64)gd->ram_size, (1ull << 32));
|
||||
|
||||
phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS;
|
||||
pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS;
|
||||
pci_size_t pci_sz;
|
||||
|
||||
/* we have no space available for inbound memory mapping */
|
||||
if (bus_start > out_lo) {
|
||||
printf ("no space for inbound mapping of memory\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* limit size */
|
||||
if ((bus_start + sz) > out_lo) {
|
||||
sz = out_lo - bus_start;
|
||||
debug ("limiting size to %llx\n", sz);
|
||||
}
|
||||
|
||||
pci_sz = 1ull << __ilog2_u64(sz);
|
||||
/*
|
||||
* we can overlap inbound/outbound windows on PCI-E since RX & TX
|
||||
* links a separate
|
||||
*/
|
||||
if ((pcie_cap == PCI_CAP_ID_EXP) && (pci_sz < sz)) {
|
||||
debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n",
|
||||
(u64)bus_start, (u64)phys_start, (u64)sz);
|
||||
pci_set_region(r, bus_start, phys_start, sz,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY |
|
||||
PCI_REGION_PREFETCH);
|
||||
|
||||
/* 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);
|
||||
|
||||
set_inbound_window(pi--, r++, sz);
|
||||
sz = 0; /* make sure we dont set the R2 window */
|
||||
} else {
|
||||
debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n",
|
||||
(u64)bus_start, (u64)phys_start, (u64)pci_sz);
|
||||
pci_set_region(r, bus_start, phys_start, pci_sz,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY |
|
||||
PCI_REGION_PREFETCH);
|
||||
set_inbound_window(pi--, r++, pci_sz);
|
||||
|
||||
sz -= pci_sz;
|
||||
bus_start += pci_sz;
|
||||
phys_start += pci_sz;
|
||||
|
||||
pci_sz = 1ull << __ilog2_u64(sz);
|
||||
if (sz) {
|
||||
debug ("R1 bus_start: %llx phys_start: %llx size: %llx\n",
|
||||
(u64)bus_start, (u64)phys_start, (u64)pci_sz);
|
||||
pci_set_region(r, bus_start, phys_start, pci_sz,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY |
|
||||
PCI_REGION_PREFETCH);
|
||||
set_inbound_window(pi--, r++, pci_sz);
|
||||
sz -= pci_sz;
|
||||
bus_start += pci_sz;
|
||||
phys_start += pci_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);
|
||||
debug ("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);
|
||||
pci_set_region(r,
|
||||
CONFIG_SYS_PCI64_MEMORY_BUS,
|
||||
CONFIG_SYS_PCI_MEMORY_PHYS,
|
||||
pci_sz,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY |
|
||||
PCI_REGION_PREFETCH);
|
||||
set_inbound_window(pi--, r++, pci_sz);
|
||||
#else
|
||||
pci_sz = 1ull << __ilog2_u64(sz);
|
||||
if (sz) {
|
||||
debug ("R2 bus_start: %llx phys_start: %llx size: %llx\n",
|
||||
(u64)bus_start, (u64)phys_start, (u64)pci_sz);
|
||||
pci_set_region(r, bus_start, phys_start, pci_sz,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY |
|
||||
PCI_REGION_PREFETCH);
|
||||
sz -= pci_sz;
|
||||
bus_start += pci_sz;
|
||||
phys_start += pci_sz;
|
||||
set_inbound_window(pi--, r++, pci_sz);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PHYS_64BIT
|
||||
if (sz && (((u64)gd->ram_size) < (1ull << 32)))
|
||||
printf("Was not able to map all of memory via "
|
||||
"inbound windows -- %lld remaining\n", sz);
|
||||
#endif
|
||||
|
||||
hose->region_count = r - hose->regions;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER
|
||||
static void fsl_pcie_boot_master(pit_t *pi)
|
||||
{
|
||||
/* configure inbound window for slave's u-boot image */
|
||||
debug("PCIEBOOT - MASTER: Inbound window for slave's image; "
|
||||
"Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n",
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS,
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE);
|
||||
struct pci_region r_inbound;
|
||||
u32 sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE)
|
||||
- 1;
|
||||
pci_set_region(&r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS1,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS,
|
||||
sz_inbound,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
set_inbound_window(pi--, &r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE);
|
||||
|
||||
/* configure inbound window for slave's u-boot image */
|
||||
debug("PCIEBOOT - MASTER: Inbound window for slave's image; "
|
||||
"Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n",
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS,
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE);
|
||||
pci_set_region(&r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_BUS2,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_MEM_PHYS,
|
||||
sz_inbound,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
set_inbound_window(pi--, &r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_IMAGE_SIZE);
|
||||
|
||||
/* configure inbound window for slave's ucode and ENV */
|
||||
debug("PCIEBOOT - MASTER: Inbound window for slave's "
|
||||
"ucode and ENV; "
|
||||
"Local = 0x%llx, Bus = 0x%llx, Size = 0x%x\n",
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS,
|
||||
(u64)CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS,
|
||||
CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE);
|
||||
sz_inbound = __ilog2_u64(CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE)
|
||||
- 1;
|
||||
pci_set_region(&r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_BUS,
|
||||
CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_MEM_PHYS,
|
||||
sz_inbound,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
set_inbound_window(pi--, &r_inbound,
|
||||
CONFIG_SRIO_PCIE_BOOT_UCODE_ENV_SIZE);
|
||||
}
|
||||
|
||||
static void fsl_pcie_boot_master_release_slave(int port)
|
||||
{
|
||||
unsigned long release_addr;
|
||||
|
||||
/* now release slave's core 0 */
|
||||
switch (port) {
|
||||
case 1:
|
||||
release_addr = CONFIG_SYS_PCIE1_MEM_VIRT
|
||||
+ CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET;
|
||||
break;
|
||||
#ifdef CONFIG_SYS_PCIE2_MEM_VIRT
|
||||
case 2:
|
||||
release_addr = CONFIG_SYS_PCIE2_MEM_VIRT
|
||||
+ CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET;
|
||||
break;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_PCIE3_MEM_VIRT
|
||||
case 3:
|
||||
release_addr = CONFIG_SYS_PCIE3_MEM_VIRT
|
||||
+ CONFIG_SRIO_PCIE_BOOT_BRR_OFFSET;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
release_addr = 0;
|
||||
break;
|
||||
}
|
||||
if (release_addr != 0) {
|
||||
out_be32((void *)release_addr,
|
||||
CONFIG_SRIO_PCIE_BOOT_RELEASE_MASK);
|
||||
debug("PCIEBOOT - MASTER: "
|
||||
"Release slave successfully! Now the slave should start up!\n");
|
||||
} else {
|
||||
debug("PCIEBOOT - MASTER: "
|
||||
"Release slave failed!\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void fsl_pci_init(struct pci_controller *hose, struct fsl_pci_info *pci_info)
|
||||
{
|
||||
u32 cfg_addr = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_addr;
|
||||
u32 cfg_data = (u32)&((ccsr_fsl_pci_t *)pci_info->regs)->cfg_data;
|
||||
u16 temp16;
|
||||
u32 temp32;
|
||||
u32 block_rev;
|
||||
int enabled, r, inbound = 0;
|
||||
u16 ltssm;
|
||||
u8 temp8, pcie_cap;
|
||||
int pcie_cap_pos;
|
||||
int pci_dcr;
|
||||
int pci_dsr;
|
||||
int pci_lsr;
|
||||
|
||||
#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM)
|
||||
int pci_lcr;
|
||||
#endif
|
||||
|
||||
volatile ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *)cfg_addr;
|
||||
struct pci_region *reg = hose->regions + hose->region_count;
|
||||
pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0);
|
||||
|
||||
/* Initialize ATMU registers based on hose regions and flags */
|
||||
volatile pot_t *po = &pci->pot[1]; /* skip 0 */
|
||||
volatile pit_t *pi;
|
||||
|
||||
u64 out_hi = 0, out_lo = -1ULL;
|
||||
u32 pcicsrbar, pcicsrbar_sz;
|
||||
|
||||
pci_setup_indirect(hose, cfg_addr, cfg_data);
|
||||
|
||||
#ifdef PEX_CCB_DIV
|
||||
/* Configure the PCIE controller core clock ratio */
|
||||
pci_hose_write_config_dword(hose, dev, 0x440,
|
||||
((gd->bus_clk / 1000000) *
|
||||
(16 / PEX_CCB_DIV)) / 333);
|
||||
#endif
|
||||
block_rev = in_be32(&pci->block_rev1);
|
||||
if (PEX_IP_BLK_REV_2_2 <= block_rev) {
|
||||
pi = &pci->pit[2]; /* 0xDC0 */
|
||||
} else {
|
||||
pi = &pci->pit[3]; /* 0xDE0 */
|
||||
}
|
||||
|
||||
/* Handle setup of outbound windows first */
|
||||
for (r = 0; r < hose->region_count; r++) {
|
||||
unsigned long flags = hose->regions[r].flags;
|
||||
u32 sz = (__ilog2_u64((u64)hose->regions[r].size) - 1);
|
||||
|
||||
flags &= PCI_REGION_SYS_MEMORY|PCI_REGION_TYPE;
|
||||
if (flags != PCI_REGION_SYS_MEMORY) {
|
||||
u64 start = hose->regions[r].bus_start;
|
||||
u64 end = start + hose->regions[r].size;
|
||||
|
||||
out_be32(&po->powbar, hose->regions[r].phys_start >> 12);
|
||||
out_be32(&po->potar, start >> 12);
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
out_be32(&po->potear, start >> 44);
|
||||
#else
|
||||
out_be32(&po->potear, 0);
|
||||
#endif
|
||||
if (hose->regions[r].flags & PCI_REGION_IO) {
|
||||
out_be32(&po->powar, POWAR_EN | sz |
|
||||
POWAR_IO_READ | POWAR_IO_WRITE);
|
||||
} else {
|
||||
out_be32(&po->powar, POWAR_EN | sz |
|
||||
POWAR_MEM_READ | POWAR_MEM_WRITE);
|
||||
out_lo = min(start, out_lo);
|
||||
out_hi = max(end, out_hi);
|
||||
}
|
||||
po++;
|
||||
}
|
||||
}
|
||||
debug("Outbound memory range: %llx:%llx\n", out_lo, out_hi);
|
||||
|
||||
/* setup PCSRBAR/PEXCSRBAR */
|
||||
pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, 0xffffffff);
|
||||
pci_hose_read_config_dword (hose, dev, PCI_BASE_ADDRESS_0, &pcicsrbar_sz);
|
||||
pcicsrbar_sz = ~pcicsrbar_sz + 1;
|
||||
|
||||
if (out_hi < (0x100000000ull - pcicsrbar_sz) ||
|
||||
(out_lo > 0x100000000ull))
|
||||
pcicsrbar = 0x100000000ull - pcicsrbar_sz;
|
||||
else
|
||||
pcicsrbar = (out_lo - pcicsrbar_sz) & -pcicsrbar_sz;
|
||||
pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, pcicsrbar);
|
||||
|
||||
out_lo = min(out_lo, (u64)pcicsrbar);
|
||||
|
||||
debug("PCICSRBAR @ 0x%x\n", pcicsrbar);
|
||||
|
||||
pci_set_region(reg++, pcicsrbar, CONFIG_SYS_CCSRBAR_PHYS,
|
||||
pcicsrbar_sz, PCI_REGION_SYS_MEMORY);
|
||||
hose->region_count++;
|
||||
|
||||
/* see if we are a PCIe or PCI controller */
|
||||
pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP);
|
||||
pci_dcr = pcie_cap_pos + 0x08;
|
||||
pci_dsr = pcie_cap_pos + 0x0a;
|
||||
pci_lsr = pcie_cap_pos + 0x12;
|
||||
|
||||
pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap);
|
||||
|
||||
#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER
|
||||
/* boot from PCIE --master */
|
||||
char *s = env_get("bootmaster");
|
||||
char pcie[6];
|
||||
sprintf(pcie, "PCIE%d", pci_info->pci_num);
|
||||
|
||||
if (s && (strcmp(s, pcie) == 0)) {
|
||||
debug("PCIEBOOT - MASTER: Master port [ %d ] for pcie boot.\n",
|
||||
pci_info->pci_num);
|
||||
fsl_pcie_boot_master((pit_t *)pi);
|
||||
} else {
|
||||
/* inbound */
|
||||
inbound = fsl_pci_setup_inbound_windows(hose,
|
||||
out_lo, pcie_cap, pi);
|
||||
}
|
||||
#else
|
||||
/* inbound */
|
||||
inbound = fsl_pci_setup_inbound_windows(hose, out_lo, pcie_cap, pi);
|
||||
#endif
|
||||
|
||||
for (r = 0; r < hose->region_count; r++)
|
||||
debug("PCI reg:%d %016llx:%016llx %016llx %08lx\n", r,
|
||||
(u64)hose->regions[r].phys_start,
|
||||
(u64)hose->regions[r].bus_start,
|
||||
(u64)hose->regions[r].size,
|
||||
hose->regions[r].flags);
|
||||
|
||||
pci_register_hose(hose);
|
||||
pciauto_config_init(hose); /* grab pci_{mem,prefetch,io} */
|
||||
hose->current_busno = hose->first_busno;
|
||||
|
||||
out_be32(&pci->pedr, 0xffffffff); /* Clear any errors */
|
||||
out_be32(&pci->peer, ~0x20140); /* Enable All Error Interrupts except
|
||||
* - Master abort (pci)
|
||||
* - Master PERR (pci)
|
||||
* - ICCA (PCIe)
|
||||
*/
|
||||
pci_hose_read_config_dword(hose, dev, pci_dcr, &temp32);
|
||||
temp32 |= 0xf000e; /* set URR, FER, NFER (but not CER) */
|
||||
pci_hose_write_config_dword(hose, dev, pci_dcr, temp32);
|
||||
|
||||
#if defined(CONFIG_FSL_PCIE_DISABLE_ASPM)
|
||||
pci_lcr = pcie_cap_pos + 0x10;
|
||||
temp32 = 0;
|
||||
pci_hose_read_config_dword(hose, dev, pci_lcr, &temp32);
|
||||
temp32 &= ~0x03; /* Disable ASPM */
|
||||
pci_hose_write_config_dword(hose, dev, pci_lcr, temp32);
|
||||
udelay(1);
|
||||
#endif
|
||||
if (pcie_cap == PCI_CAP_ID_EXP) {
|
||||
if (block_rev >= PEX_IP_BLK_REV_3_0) {
|
||||
#define PEX_CSR0_LTSSM_MASK 0xFC
|
||||
#define PEX_CSR0_LTSSM_SHIFT 2
|
||||
ltssm = (in_be32(&pci->pex_csr0)
|
||||
& PEX_CSR0_LTSSM_MASK) >> PEX_CSR0_LTSSM_SHIFT;
|
||||
enabled = (ltssm == 0x11) ? 1 : 0;
|
||||
#ifdef CONFIG_FSL_PCIE_RESET
|
||||
int i;
|
||||
/* assert PCIe reset */
|
||||
setbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
(void) in_be32(&pci->pdb_stat);
|
||||
udelay(1000);
|
||||
/* clear PCIe reset */
|
||||
clrbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
asm("sync;isync");
|
||||
for (i = 0; i < 100 && ltssm < PCI_LTSSM_L0; i++) {
|
||||
pci_hose_read_config_word(hose, dev, PCI_LTSSM,
|
||||
<ssm);
|
||||
udelay(1000);
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
/* pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm); */
|
||||
/* enabled = ltssm >= PCI_LTSSM_L0; */
|
||||
pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm);
|
||||
enabled = ltssm >= PCI_LTSSM_L0;
|
||||
|
||||
#ifdef CONFIG_FSL_PCIE_RESET
|
||||
if (ltssm == 1) {
|
||||
int i;
|
||||
debug("....PCIe link error. " "LTSSM=0x%02x.", ltssm);
|
||||
/* assert PCIe reset */
|
||||
setbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
(void) in_be32(&pci->pdb_stat);
|
||||
udelay(100);
|
||||
debug(" Asserting PCIe reset @%p = %x\n",
|
||||
&pci->pdb_stat, in_be32(&pci->pdb_stat));
|
||||
/* clear PCIe reset */
|
||||
clrbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
asm("sync;isync");
|
||||
for (i=0; i<100 && ltssm < PCI_LTSSM_L0; i++) {
|
||||
pci_hose_read_config_word(hose, dev, PCI_LTSSM,
|
||||
<ssm);
|
||||
udelay(1000);
|
||||
debug("....PCIe link error. "
|
||||
"LTSSM=0x%02x.\n", ltssm);
|
||||
}
|
||||
enabled = ltssm >= PCI_LTSSM_L0;
|
||||
|
||||
/* we need to re-write the bar0 since a reset will
|
||||
* clear it
|
||||
*/
|
||||
pci_hose_write_config_dword(hose, dev,
|
||||
PCI_BASE_ADDRESS_0, pcicsrbar);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003
|
||||
if (enabled == 0) {
|
||||
serdes_corenet_t *srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
|
||||
temp32 = in_be32(&srds_regs->srdspccr0);
|
||||
|
||||
if ((temp32 >> 28) == 3) {
|
||||
int i;
|
||||
|
||||
out_be32(&srds_regs->srdspccr0, 2 << 28);
|
||||
setbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
in_be32(&pci->pdb_stat);
|
||||
udelay(100);
|
||||
clrbits_be32(&pci->pdb_stat, 0x08000000);
|
||||
asm("sync;isync");
|
||||
for (i=0; i < 100 && ltssm < PCI_LTSSM_L0; i++) {
|
||||
pci_hose_read_config_word(hose, dev, PCI_LTSSM, <ssm);
|
||||
udelay(1000);
|
||||
}
|
||||
enabled = ltssm >= PCI_LTSSM_L0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!enabled) {
|
||||
/* Let the user know there's no PCIe link for root
|
||||
* complex. for endpoint, the link may not setup, so
|
||||
* print undetermined.
|
||||
*/
|
||||
if (fsl_is_pci_agent(hose))
|
||||
printf("undetermined, regs @ 0x%lx\n", pci_info->regs);
|
||||
else
|
||||
printf("no link, regs @ 0x%lx\n", pci_info->regs);
|
||||
hose->last_busno = hose->first_busno;
|
||||
return;
|
||||
}
|
||||
|
||||
out_be32(&pci->pme_msg_det, 0xffffffff);
|
||||
out_be32(&pci->pme_msg_int_en, 0xffffffff);
|
||||
|
||||
/* Print the negotiated PCIe link width */
|
||||
pci_hose_read_config_word(hose, dev, pci_lsr, &temp16);
|
||||
printf("x%d gen%d, regs @ 0x%lx\n", (temp16 & 0x3f0) >> 4,
|
||||
(temp16 & 0xf), pci_info->regs);
|
||||
|
||||
hose->current_busno++; /* Start scan with secondary */
|
||||
pciauto_prescan_setup_bridge(hose, dev, hose->current_busno);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_ERRATUM_A007815
|
||||
/* The Read-Only Write Enable bit defaults to 1 instead of 0.
|
||||
* Set to 0 to protect the read-only registers.
|
||||
*/
|
||||
clrbits_be32(&pci->dbi_ro_wr_en, 0x01);
|
||||
#endif
|
||||
|
||||
/* Use generic setup_device to initialize standard pci regs,
|
||||
* but do not allocate any windows since any BAR found (such
|
||||
* as PCSRBAR) is not in this cpu's memory space.
|
||||
*/
|
||||
pciauto_setup_device(hose, dev, 0, hose->pci_mem,
|
||||
hose->pci_prefetch, hose->pci_io);
|
||||
|
||||
if (inbound) {
|
||||
pci_hose_read_config_word(hose, dev, PCI_COMMAND, &temp16);
|
||||
pci_hose_write_config_word(hose, dev, PCI_COMMAND,
|
||||
temp16 | PCI_COMMAND_MEMORY);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PCI_NOSCAN
|
||||
if (!fsl_is_pci_agent(hose)) {
|
||||
debug(" Scanning PCI bus %02x\n",
|
||||
hose->current_busno);
|
||||
hose->last_busno = pci_hose_scan_bus(hose, hose->current_busno);
|
||||
} else {
|
||||
debug(" Not scanning PCI bus %02x. PI=%x\n",
|
||||
hose->current_busno, temp8);
|
||||
hose->last_busno = hose->current_busno;
|
||||
}
|
||||
|
||||
/* if we are PCIe - update limit regs and subordinate busno
|
||||
* for the virtual P2P bridge
|
||||
*/
|
||||
if (pcie_cap == PCI_CAP_ID_EXP) {
|
||||
pciauto_postscan_setup_bridge(hose, dev, hose->last_busno);
|
||||
}
|
||||
#else
|
||||
hose->last_busno = hose->current_busno;
|
||||
#endif
|
||||
|
||||
/* Clear all error indications */
|
||||
if (pcie_cap == PCI_CAP_ID_EXP)
|
||||
out_be32(&pci->pme_msg_det, 0xffffffff);
|
||||
out_be32(&pci->pedr, 0xffffffff);
|
||||
|
||||
pci_hose_read_config_word(hose, dev, pci_dsr, &temp16);
|
||||
if (temp16) {
|
||||
pci_hose_write_config_word(hose, dev, pci_dsr, 0xffff);
|
||||
}
|
||||
|
||||
pci_hose_read_config_word (hose, dev, PCI_SEC_STATUS, &temp16);
|
||||
if (temp16) {
|
||||
pci_hose_write_config_word(hose, dev, PCI_SEC_STATUS, 0xffff);
|
||||
}
|
||||
}
|
||||
|
||||
int fsl_is_pci_agent(struct pci_controller *hose)
|
||||
{
|
||||
int pcie_cap_pos;
|
||||
u8 pcie_cap;
|
||||
pci_dev_t dev = PCI_BDF(hose->first_busno, 0, 0);
|
||||
|
||||
pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP);
|
||||
pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap);
|
||||
if (pcie_cap == PCI_CAP_ID_EXP) {
|
||||
u8 header_type;
|
||||
|
||||
pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE,
|
||||
&header_type);
|
||||
return (header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL;
|
||||
} else {
|
||||
u8 prog_if;
|
||||
|
||||
pci_hose_read_config_byte(hose, dev, PCI_CLASS_PROG, &prog_if);
|
||||
/* Programming Interface (PCI_CLASS_PROG)
|
||||
* 0 == pci host or pcie root-complex,
|
||||
* 1 == pci agent or pcie end-point
|
||||
*/
|
||||
return (prog_if == FSL_PROG_IF_AGENT);
|
||||
}
|
||||
}
|
||||
|
||||
int fsl_pci_init_port(struct fsl_pci_info *pci_info,
|
||||
struct pci_controller *hose, int busno)
|
||||
{
|
||||
volatile ccsr_fsl_pci_t *pci;
|
||||
struct pci_region *r;
|
||||
pci_dev_t dev = PCI_BDF(busno,0,0);
|
||||
int pcie_cap_pos;
|
||||
u8 pcie_cap;
|
||||
|
||||
pci = (ccsr_fsl_pci_t *) pci_info->regs;
|
||||
|
||||
/* on non-PCIe controllers we don't have pme_msg_det so this code
|
||||
* should do nothing since the read will return 0
|
||||
*/
|
||||
if (in_be32(&pci->pme_msg_det)) {
|
||||
out_be32(&pci->pme_msg_det, 0xffffffff);
|
||||
debug (" with errors. Clearing. Now 0x%08x",
|
||||
pci->pme_msg_det);
|
||||
}
|
||||
|
||||
r = hose->regions + hose->region_count;
|
||||
|
||||
/* outbound memory */
|
||||
pci_set_region(r++,
|
||||
pci_info->mem_bus,
|
||||
pci_info->mem_phys,
|
||||
pci_info->mem_size,
|
||||
PCI_REGION_MEM);
|
||||
|
||||
/* outbound io */
|
||||
pci_set_region(r++,
|
||||
pci_info->io_bus,
|
||||
pci_info->io_phys,
|
||||
pci_info->io_size,
|
||||
PCI_REGION_IO);
|
||||
|
||||
hose->region_count = r - hose->regions;
|
||||
hose->first_busno = busno;
|
||||
|
||||
fsl_pci_init(hose, pci_info);
|
||||
|
||||
if (fsl_is_pci_agent(hose)) {
|
||||
fsl_pci_config_unlock(hose);
|
||||
hose->last_busno = hose->first_busno;
|
||||
#ifdef CONFIG_SRIO_PCIE_BOOT_MASTER
|
||||
} else {
|
||||
/* boot from PCIE --master releases slave's core 0 */
|
||||
char *s = env_get("bootmaster");
|
||||
char pcie[6];
|
||||
sprintf(pcie, "PCIE%d", pci_info->pci_num);
|
||||
|
||||
if (s && (strcmp(s, pcie) == 0))
|
||||
fsl_pcie_boot_master_release_slave(pci_info->pci_num);
|
||||
#endif
|
||||
}
|
||||
|
||||
pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP);
|
||||
pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap);
|
||||
printf("PCI%s%x: Bus %02x - %02x\n", pcie_cap == PCI_CAP_ID_EXP ?
|
||||
"e" : "", pci_info->pci_num,
|
||||
hose->first_busno, hose->last_busno);
|
||||
return(hose->last_busno + 1);
|
||||
}
|
||||
|
||||
/* Enable inbound PCI config cycles for agent/endpoint interface */
|
||||
void fsl_pci_config_unlock(struct pci_controller *hose)
|
||||
{
|
||||
pci_dev_t dev = PCI_BDF(hose->first_busno,0,0);
|
||||
int pcie_cap_pos;
|
||||
u8 pcie_cap;
|
||||
u16 pbfr;
|
||||
|
||||
if (!fsl_is_pci_agent(hose))
|
||||
return;
|
||||
|
||||
pcie_cap_pos = pci_hose_find_capability(hose, dev, PCI_CAP_ID_EXP);
|
||||
pci_hose_read_config_byte(hose, dev, pcie_cap_pos, &pcie_cap);
|
||||
if (pcie_cap != 0x0) {
|
||||
ccsr_fsl_pci_t *pci = (ccsr_fsl_pci_t *)hose->cfg_addr;
|
||||
u32 block_rev = in_be32(&pci->block_rev1);
|
||||
/* PCIe - set CFG_READY bit of Configuration Ready Register */
|
||||
if (block_rev >= PEX_IP_BLK_REV_3_0)
|
||||
setbits_be32(&pci->config, FSL_PCIE_V3_CFG_RDY);
|
||||
else
|
||||
pci_hose_write_config_byte(hose, dev,
|
||||
FSL_PCIE_CFG_RDY, 0x1);
|
||||
} else {
|
||||
/* PCI - clear ACL bit of PBFR */
|
||||
pci_hose_read_config_word(hose, dev, FSL_PCI_PBFR, &pbfr);
|
||||
pbfr &= ~0x20;
|
||||
pci_hose_write_config_word(hose, dev, FSL_PCI_PBFR, pbfr);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PCIE1) || defined(CONFIG_PCIE2) || \
|
||||
defined(CONFIG_PCIE3) || defined(CONFIG_PCIE4)
|
||||
int fsl_configure_pcie(struct fsl_pci_info *info,
|
||||
struct pci_controller *hose,
|
||||
const char *connected, int busno)
|
||||
{
|
||||
int is_endpoint;
|
||||
|
||||
set_next_law(info->mem_phys, law_size_bits(info->mem_size), info->law);
|
||||
set_next_law(info->io_phys, law_size_bits(info->io_size), info->law);
|
||||
|
||||
is_endpoint = fsl_setup_hose(hose, info->regs);
|
||||
printf("PCIe%u: %s", info->pci_num,
|
||||
is_endpoint ? "Endpoint" : "Root Complex");
|
||||
if (connected)
|
||||
printf(" of %s", connected);
|
||||
puts(", ");
|
||||
|
||||
return fsl_pci_init_port(info, hose, busno);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FSL_CORENET)
|
||||
#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2
|
||||
#define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR3_PCIE1
|
||||
#define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR3_PCIE2
|
||||
#define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR3_PCIE3
|
||||
#define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR3_PCIE4
|
||||
#else
|
||||
#define _DEVDISR_PCIE1 FSL_CORENET_DEVDISR_PCIE1
|
||||
#define _DEVDISR_PCIE2 FSL_CORENET_DEVDISR_PCIE2
|
||||
#define _DEVDISR_PCIE3 FSL_CORENET_DEVDISR_PCIE3
|
||||
#define _DEVDISR_PCIE4 FSL_CORENET_DEVDISR_PCIE4
|
||||
#endif
|
||||
#define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR
|
||||
#elif defined(CONFIG_MPC85xx)
|
||||
#define _DEVDISR_PCIE1 MPC85xx_DEVDISR_PCIE
|
||||
#define _DEVDISR_PCIE2 MPC85xx_DEVDISR_PCIE2
|
||||
#define _DEVDISR_PCIE3 MPC85xx_DEVDISR_PCIE3
|
||||
#define _DEVDISR_PCIE4 0
|
||||
#define CONFIG_SYS_MPC8xxx_GUTS_ADDR CONFIG_SYS_MPC85xx_GUTS_ADDR
|
||||
#elif defined(CONFIG_MPC86xx)
|
||||
#define _DEVDISR_PCIE1 MPC86xx_DEVDISR_PCIE1
|
||||
#define _DEVDISR_PCIE2 MPC86xx_DEVDISR_PCIE2
|
||||
#define _DEVDISR_PCIE3 0
|
||||
#define _DEVDISR_PCIE4 0
|
||||
#define CONFIG_SYS_MPC8xxx_GUTS_ADDR \
|
||||
(&((immap_t *)CONFIG_SYS_IMMR)->im_gur)
|
||||
#else
|
||||
#error "No defines for DEVDISR_PCIE"
|
||||
#endif
|
||||
|
||||
/* Implement a dummy function for those platforms w/o SERDES */
|
||||
static const char *__board_serdes_name(enum srds_prtcl device)
|
||||
{
|
||||
switch (device) {
|
||||
#ifdef CONFIG_SYS_PCIE1_NAME
|
||||
case PCIE1:
|
||||
return CONFIG_SYS_PCIE1_NAME;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_PCIE2_NAME
|
||||
case PCIE2:
|
||||
return CONFIG_SYS_PCIE2_NAME;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_PCIE3_NAME
|
||||
case PCIE3:
|
||||
return CONFIG_SYS_PCIE3_NAME;
|
||||
#endif
|
||||
#ifdef CONFIG_SYS_PCIE4_NAME
|
||||
case PCIE4:
|
||||
return CONFIG_SYS_PCIE4_NAME;
|
||||
#endif
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
__attribute__((weak, alias("__board_serdes_name"))) const char *
|
||||
board_serdes_name(enum srds_prtcl device);
|
||||
|
||||
static u32 devdisr_mask[] = {
|
||||
_DEVDISR_PCIE1,
|
||||
_DEVDISR_PCIE2,
|
||||
_DEVDISR_PCIE3,
|
||||
_DEVDISR_PCIE4,
|
||||
};
|
||||
|
||||
int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev,
|
||||
struct fsl_pci_info *pci_info)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
int num = dev - PCIE1;
|
||||
|
||||
hose = calloc(1, sizeof(struct pci_controller));
|
||||
if (!hose)
|
||||
return busno;
|
||||
|
||||
if (is_serdes_configured(dev) && !(devdisr & devdisr_mask[num])) {
|
||||
busno = fsl_configure_pcie(pci_info, hose,
|
||||
board_serdes_name(dev), busno);
|
||||
} else {
|
||||
printf("PCIe%d: disabled\n", num + 1);
|
||||
}
|
||||
|
||||
return busno;
|
||||
}
|
||||
|
||||
int fsl_pcie_init_board(int busno)
|
||||
{
|
||||
struct fsl_pci_info pci_info;
|
||||
ccsr_gur_t *gur = (void *)CONFIG_SYS_MPC8xxx_GUTS_ADDR;
|
||||
u32 devdisr;
|
||||
u32 *addr;
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_QORIQ_CHASSIS2
|
||||
addr = &gur->devdisr3;
|
||||
#else
|
||||
addr = &gur->devdisr;
|
||||
#endif
|
||||
devdisr = in_be32(addr);
|
||||
|
||||
#ifdef CONFIG_PCIE1
|
||||
SET_STD_PCIE_INFO(pci_info, 1);
|
||||
busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE1, &pci_info);
|
||||
#else
|
||||
setbits_be32(addr, _DEVDISR_PCIE1); /* disable */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE2
|
||||
SET_STD_PCIE_INFO(pci_info, 2);
|
||||
busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE2, &pci_info);
|
||||
#else
|
||||
setbits_be32(addr, _DEVDISR_PCIE2); /* disable */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE3
|
||||
SET_STD_PCIE_INFO(pci_info, 3);
|
||||
busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE3, &pci_info);
|
||||
#else
|
||||
setbits_be32(addr, _DEVDISR_PCIE3); /* disable */
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCIE4
|
||||
SET_STD_PCIE_INFO(pci_info, 4);
|
||||
busno = fsl_pcie_init_ctrl(busno, devdisr, PCIE4, &pci_info);
|
||||
#else
|
||||
setbits_be32(addr, _DEVDISR_PCIE4); /* disable */
|
||||
#endif
|
||||
|
||||
return busno;
|
||||
}
|
||||
#else
|
||||
int fsl_pcie_init_ctrl(int busno, u32 devdisr, enum srds_prtcl dev,
|
||||
struct fsl_pci_info *pci_info)
|
||||
{
|
||||
return busno;
|
||||
}
|
||||
|
||||
int fsl_pcie_init_board(int busno)
|
||||
{
|
||||
return busno;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF_BOARD_SETUP
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
|
||||
void ft_fsl_pci_setup(void *blob, const char *pci_compat,
|
||||
unsigned long ctrl_addr)
|
||||
{
|
||||
int off;
|
||||
u32 bus_range[2];
|
||||
phys_addr_t p_ctrl_addr = (phys_addr_t)ctrl_addr;
|
||||
struct pci_controller *hose;
|
||||
|
||||
hose = find_hose_by_cfg_addr((void *)(ctrl_addr));
|
||||
|
||||
/* convert ctrl_addr to true physical address */
|
||||
p_ctrl_addr = (phys_addr_t)ctrl_addr - CONFIG_SYS_CCSRBAR;
|
||||
p_ctrl_addr += CONFIG_SYS_CCSRBAR_PHYS;
|
||||
|
||||
off = fdt_node_offset_by_compat_reg(blob, pci_compat, p_ctrl_addr);
|
||||
|
||||
if (off < 0)
|
||||
return;
|
||||
|
||||
/* We assume a cfg_addr not being set means we didn't setup the controller */
|
||||
if ((hose == NULL) || (hose->cfg_addr == NULL)) {
|
||||
fdt_del_node(blob, off);
|
||||
} else {
|
||||
bus_range[0] = 0;
|
||||
bus_range[1] = hose->last_busno - hose->first_busno;
|
||||
fdt_setprop(blob, off, "bus-range", &bus_range[0], 2*4);
|
||||
fdt_pci_dma_ranges(blob, off, hose);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,688 @@
|
||||
/*
|
||||
* ***************************************************************************
|
||||
* Copyright (C) 2015 Marvell International Ltd.
|
||||
* ***************************************************************************
|
||||
* This program is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 2 of the License, or any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* ***************************************************************************
|
||||
*/
|
||||
/* pcie_advk.c
|
||||
*
|
||||
* Ported from Linux driver - driver/pci/host/pci-aardvark.c
|
||||
*
|
||||
* Author: Victor Gu <xigu@marvell.com>
|
||||
* Hezi Shahmoon <hezi.shahmoon@marvell.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
/* PCIe core registers */
|
||||
#define PCIE_CORE_CMD_STATUS_REG 0x4
|
||||
#define PCIE_CORE_CMD_IO_ACCESS_EN BIT(0)
|
||||
#define PCIE_CORE_CMD_MEM_ACCESS_EN BIT(1)
|
||||
#define PCIE_CORE_CMD_MEM_IO_REQ_EN BIT(2)
|
||||
#define PCIE_CORE_DEV_CTRL_STATS_REG 0xc8
|
||||
#define PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE (0 << 4)
|
||||
#define PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE (0 << 11)
|
||||
#define PCIE_CORE_LINK_CTRL_STAT_REG 0xd0
|
||||
#define PCIE_CORE_LINK_TRAINING BIT(5)
|
||||
#define PCIE_CORE_ERR_CAPCTL_REG 0x118
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX BIT(5)
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN BIT(6)
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHECK BIT(7)
|
||||
#define PCIE_CORE_ERR_CAPCTL_ECRC_CHECK_RCV BIT(8)
|
||||
|
||||
/* PIO registers base address and register offsets */
|
||||
#define PIO_BASE_ADDR 0x4000
|
||||
#define PIO_CTRL (PIO_BASE_ADDR + 0x0)
|
||||
#define PIO_CTRL_TYPE_MASK GENMASK(3, 0)
|
||||
#define PIO_CTRL_ADDR_WIN_DISABLE BIT(24)
|
||||
#define PIO_STAT (PIO_BASE_ADDR + 0x4)
|
||||
#define PIO_COMPLETION_STATUS_SHIFT 7
|
||||
#define PIO_COMPLETION_STATUS_MASK GENMASK(9, 7)
|
||||
#define PIO_COMPLETION_STATUS_OK 0
|
||||
#define PIO_COMPLETION_STATUS_UR 1
|
||||
#define PIO_COMPLETION_STATUS_CRS 2
|
||||
#define PIO_COMPLETION_STATUS_CA 4
|
||||
#define PIO_NON_POSTED_REQ BIT(10)
|
||||
#define PIO_ERR_STATUS BIT(11)
|
||||
#define PIO_ADDR_LS (PIO_BASE_ADDR + 0x8)
|
||||
#define PIO_ADDR_MS (PIO_BASE_ADDR + 0xc)
|
||||
#define PIO_WR_DATA (PIO_BASE_ADDR + 0x10)
|
||||
#define PIO_WR_DATA_STRB (PIO_BASE_ADDR + 0x14)
|
||||
#define PIO_RD_DATA (PIO_BASE_ADDR + 0x18)
|
||||
#define PIO_START (PIO_BASE_ADDR + 0x1c)
|
||||
#define PIO_ISR (PIO_BASE_ADDR + 0x20)
|
||||
|
||||
/* Aardvark Control registers */
|
||||
#define CONTROL_BASE_ADDR 0x4800
|
||||
#define PCIE_CORE_CTRL0_REG (CONTROL_BASE_ADDR + 0x0)
|
||||
#define PCIE_GEN_SEL_MSK 0x3
|
||||
#define PCIE_GEN_SEL_SHIFT 0x0
|
||||
#define SPEED_GEN_1 0
|
||||
#define SPEED_GEN_2 1
|
||||
#define SPEED_GEN_3 2
|
||||
#define IS_RC_MSK 1
|
||||
#define IS_RC_SHIFT 2
|
||||
#define LANE_CNT_MSK 0x18
|
||||
#define LANE_CNT_SHIFT 0x3
|
||||
#define LANE_COUNT_1 (0 << LANE_CNT_SHIFT)
|
||||
#define LANE_COUNT_2 (1 << LANE_CNT_SHIFT)
|
||||
#define LANE_COUNT_4 (2 << LANE_CNT_SHIFT)
|
||||
#define LANE_COUNT_8 (3 << LANE_CNT_SHIFT)
|
||||
#define LINK_TRAINING_EN BIT(6)
|
||||
#define PCIE_CORE_CTRL2_REG (CONTROL_BASE_ADDR + 0x8)
|
||||
#define PCIE_CORE_CTRL2_RESERVED 0x7
|
||||
#define PCIE_CORE_CTRL2_TD_ENABLE BIT(4)
|
||||
#define PCIE_CORE_CTRL2_STRICT_ORDER_ENABLE BIT(5)
|
||||
#define PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE BIT(6)
|
||||
|
||||
/* LMI registers base address and register offsets */
|
||||
#define LMI_BASE_ADDR 0x6000
|
||||
#define CFG_REG (LMI_BASE_ADDR + 0x0)
|
||||
#define LTSSM_SHIFT 24
|
||||
#define LTSSM_MASK 0x3f
|
||||
#define LTSSM_L0 0x10
|
||||
|
||||
/* PCIe core controller registers */
|
||||
#define CTRL_CORE_BASE_ADDR 0x18000
|
||||
#define CTRL_CONFIG_REG (CTRL_CORE_BASE_ADDR + 0x0)
|
||||
#define CTRL_MODE_SHIFT 0x0
|
||||
#define CTRL_MODE_MASK 0x1
|
||||
#define PCIE_CORE_MODE_DIRECT 0x0
|
||||
#define PCIE_CORE_MODE_COMMAND 0x1
|
||||
|
||||
/* Transaction types */
|
||||
#define PCIE_CONFIG_RD_TYPE0 0x8
|
||||
#define PCIE_CONFIG_RD_TYPE1 0x9
|
||||
#define PCIE_CONFIG_WR_TYPE0 0xa
|
||||
#define PCIE_CONFIG_WR_TYPE1 0xb
|
||||
|
||||
/* PCI_BDF shifts 8bit, so we need extra 4bit shift */
|
||||
#define PCIE_BDF(dev) (dev << 4)
|
||||
#define PCIE_CONF_BUS(bus) (((bus) & 0xff) << 20)
|
||||
#define PCIE_CONF_DEV(dev) (((dev) & 0x1f) << 15)
|
||||
#define PCIE_CONF_FUNC(fun) (((fun) & 0x7) << 12)
|
||||
#define PCIE_CONF_REG(reg) ((reg) & 0xffc)
|
||||
#define PCIE_CONF_ADDR(bus, devfn, where) \
|
||||
(PCIE_CONF_BUS(bus) | PCIE_CONF_DEV(PCI_SLOT(devfn)) | \
|
||||
PCIE_CONF_FUNC(PCI_FUNC(devfn)) | PCIE_CONF_REG(where))
|
||||
|
||||
/* PCIe Retries & Timeout definitions */
|
||||
#define MAX_RETRIES 10
|
||||
#define PIO_WAIT_TIMEOUT 100
|
||||
#define LINK_WAIT_TIMEOUT 100000
|
||||
|
||||
#define CFG_RD_UR_VAL 0xFFFFFFFF
|
||||
#define CFG_RD_CRS_VAL 0xFFFF0001
|
||||
|
||||
/**
|
||||
* struct pcie_advk - Advk PCIe controller state
|
||||
*
|
||||
* @reg_base: The base address of the register space.
|
||||
* @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).
|
||||
* @device: The pointer to PCI uclass device.
|
||||
*/
|
||||
struct pcie_advk {
|
||||
void *base;
|
||||
int first_busno;
|
||||
struct udevice *dev;
|
||||
};
|
||||
|
||||
static inline void advk_writel(struct pcie_advk *pcie, uint val, uint reg)
|
||||
{
|
||||
writel(val, pcie->base + reg);
|
||||
}
|
||||
|
||||
static inline uint advk_readl(struct pcie_advk *pcie, uint reg)
|
||||
{
|
||||
return readl(pcie->base + reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_addr_valid() - Check for valid bus address
|
||||
*
|
||||
* @bdf: The PCI device to access
|
||||
* @first_busno: Bus number of the PCIe controller root complex
|
||||
*
|
||||
* Return: 1 on valid, 0 on invalid
|
||||
*/
|
||||
static int pcie_advk_addr_valid(pci_dev_t bdf, int first_busno)
|
||||
{
|
||||
/*
|
||||
* In PCIE-E only a single device (0) can exist
|
||||
* on the local bus. Beyound the local bus, there might be
|
||||
* a Switch and everything is possible.
|
||||
*/
|
||||
if ((PCI_BUS(bdf) == first_busno) && (PCI_DEV(bdf) > 0))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_wait_pio() - Wait for PIO access to be accomplished
|
||||
*
|
||||
* @pcie: The PCI device to access
|
||||
*
|
||||
* Wait up to 1 micro second for PIO access to be accomplished.
|
||||
*
|
||||
* Return 1 (true) if PIO access is accomplished.
|
||||
* Return 0 (false) if PIO access is timed out.
|
||||
*/
|
||||
static int pcie_advk_wait_pio(struct pcie_advk *pcie)
|
||||
{
|
||||
uint start, isr;
|
||||
uint count;
|
||||
|
||||
for (count = 0; count < MAX_RETRIES; count++) {
|
||||
start = advk_readl(pcie, PIO_START);
|
||||
isr = advk_readl(pcie, PIO_ISR);
|
||||
if (!start && isr)
|
||||
return 1;
|
||||
/*
|
||||
* Do not check the PIO state too frequently,
|
||||
* 100us delay is appropriate.
|
||||
*/
|
||||
udelay(PIO_WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
dev_err(pcie->dev, "config read/write timed out\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_check_pio_status() - Validate PIO status and get the read result
|
||||
*
|
||||
* @pcie: Pointer to the PCI bus
|
||||
* @read: Read from or write to configuration space - true(read) false(write)
|
||||
* @read_val: Pointer to the read result, only valid when read is true
|
||||
*
|
||||
*/
|
||||
static int pcie_advk_check_pio_status(struct pcie_advk *pcie,
|
||||
bool read,
|
||||
uint *read_val)
|
||||
{
|
||||
uint reg;
|
||||
unsigned int status;
|
||||
char *strcomp_status, *str_posted;
|
||||
|
||||
reg = advk_readl(pcie, PIO_STAT);
|
||||
status = (reg & PIO_COMPLETION_STATUS_MASK) >>
|
||||
PIO_COMPLETION_STATUS_SHIFT;
|
||||
|
||||
switch (status) {
|
||||
case PIO_COMPLETION_STATUS_OK:
|
||||
if (reg & PIO_ERR_STATUS) {
|
||||
strcomp_status = "COMP_ERR";
|
||||
break;
|
||||
}
|
||||
/* Get the read result */
|
||||
if (read)
|
||||
*read_val = advk_readl(pcie, PIO_RD_DATA);
|
||||
/* No error */
|
||||
strcomp_status = NULL;
|
||||
break;
|
||||
case PIO_COMPLETION_STATUS_UR:
|
||||
if (read) {
|
||||
/* For reading, UR is not an error status. */
|
||||
*read_val = CFG_RD_UR_VAL;
|
||||
strcomp_status = NULL;
|
||||
} else {
|
||||
strcomp_status = "UR";
|
||||
}
|
||||
break;
|
||||
case PIO_COMPLETION_STATUS_CRS:
|
||||
if (read) {
|
||||
/* For reading, CRS is not an error status. */
|
||||
*read_val = CFG_RD_CRS_VAL;
|
||||
strcomp_status = NULL;
|
||||
} else {
|
||||
strcomp_status = "CRS";
|
||||
}
|
||||
break;
|
||||
case PIO_COMPLETION_STATUS_CA:
|
||||
strcomp_status = "CA";
|
||||
break;
|
||||
default:
|
||||
strcomp_status = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!strcomp_status)
|
||||
return 0;
|
||||
|
||||
if (reg & PIO_NON_POSTED_REQ)
|
||||
str_posted = "Non-posted";
|
||||
else
|
||||
str_posted = "Posted";
|
||||
|
||||
dev_err(pcie->dev, "%s PIO Response Status: %s, %#x @ %#x\n",
|
||||
str_posted, strcomp_status, reg,
|
||||
advk_readl(pcie, PIO_ADDR_LS));
|
||||
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_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_advk_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct pcie_advk *pcie = dev_get_priv(bus);
|
||||
uint reg;
|
||||
int ret;
|
||||
|
||||
dev_dbg(pcie->dev, "PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
|
||||
|
||||
if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) {
|
||||
dev_dbg(pcie->dev, "- out of range\n");
|
||||
*valuep = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start PIO */
|
||||
advk_writel(pcie, 0, PIO_START);
|
||||
advk_writel(pcie, 1, PIO_ISR);
|
||||
|
||||
/* Program the control register */
|
||||
reg = advk_readl(pcie, PIO_CTRL);
|
||||
reg &= ~PIO_CTRL_TYPE_MASK;
|
||||
if (PCI_BUS(bdf) == pcie->first_busno)
|
||||
reg |= PCIE_CONFIG_RD_TYPE0;
|
||||
else
|
||||
reg |= PCIE_CONFIG_RD_TYPE1;
|
||||
advk_writel(pcie, reg, PIO_CTRL);
|
||||
|
||||
/* Program the address registers */
|
||||
reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset);
|
||||
advk_writel(pcie, reg, PIO_ADDR_LS);
|
||||
advk_writel(pcie, 0, PIO_ADDR_MS);
|
||||
|
||||
/* Start the transfer */
|
||||
advk_writel(pcie, 1, PIO_START);
|
||||
|
||||
if (!pcie_advk_wait_pio(pcie))
|
||||
return -EINVAL;
|
||||
|
||||
/* Check PIO status and get the read result */
|
||||
ret = pcie_advk_check_pio_status(pcie, true, ®);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08x)\n",
|
||||
offset, size, reg);
|
||||
*valuep = pci_conv_32_to_size(reg, offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_calc_datastrobe() - Calculate data strobe
|
||||
*
|
||||
* @offset: The offset into the device's configuration space
|
||||
* @size: Indicates the size of access to perform
|
||||
*
|
||||
* Calculate data strobe according to offset and size
|
||||
*
|
||||
*/
|
||||
static uint pcie_calc_datastrobe(uint offset, enum pci_size_t size)
|
||||
{
|
||||
uint bytes, data_strobe;
|
||||
|
||||
switch (size) {
|
||||
case PCI_SIZE_8:
|
||||
bytes = 1;
|
||||
break;
|
||||
case PCI_SIZE_16:
|
||||
bytes = 2;
|
||||
break;
|
||||
default:
|
||||
bytes = 4;
|
||||
}
|
||||
|
||||
data_strobe = GENMASK(bytes - 1, 0) << (offset & 0x3);
|
||||
|
||||
return data_strobe;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_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_advk_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct pcie_advk *pcie = dev_get_priv(bus);
|
||||
uint reg;
|
||||
|
||||
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,size,val)=(0x%04x, %d, 0x%08lx)\n",
|
||||
offset, size, value);
|
||||
|
||||
if (!pcie_advk_addr_valid(bdf, pcie->first_busno)) {
|
||||
dev_dbg(pcie->dev, "- out of range\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start PIO */
|
||||
advk_writel(pcie, 0, PIO_START);
|
||||
advk_writel(pcie, 1, PIO_ISR);
|
||||
|
||||
/* Program the control register */
|
||||
reg = advk_readl(pcie, PIO_CTRL);
|
||||
reg &= ~PIO_CTRL_TYPE_MASK;
|
||||
if (PCI_BUS(bdf) == pcie->first_busno)
|
||||
reg |= PCIE_CONFIG_WR_TYPE0;
|
||||
else
|
||||
reg |= PCIE_CONFIG_WR_TYPE1;
|
||||
advk_writel(pcie, reg, PIO_CTRL);
|
||||
|
||||
/* Program the address registers */
|
||||
reg = PCIE_BDF(bdf) | PCIE_CONF_REG(offset);
|
||||
advk_writel(pcie, reg, PIO_ADDR_LS);
|
||||
advk_writel(pcie, 0, PIO_ADDR_MS);
|
||||
dev_dbg(pcie->dev, "\tPIO req. - addr = 0x%08x\n", reg);
|
||||
|
||||
/* Program the data register */
|
||||
reg = pci_conv_size_to_32(0, value, offset, size);
|
||||
advk_writel(pcie, reg, PIO_WR_DATA);
|
||||
dev_dbg(pcie->dev, "\tPIO req. - val = 0x%08x\n", reg);
|
||||
|
||||
/* Program the data strobe */
|
||||
reg = pcie_calc_datastrobe(offset, size);
|
||||
advk_writel(pcie, reg, PIO_WR_DATA_STRB);
|
||||
dev_dbg(pcie->dev, "\tPIO req. - strb = 0x%02x\n", reg);
|
||||
|
||||
/* Start the transfer */
|
||||
advk_writel(pcie, 1, PIO_START);
|
||||
|
||||
if (!pcie_advk_wait_pio(pcie)) {
|
||||
dev_dbg(pcie->dev, "- wait pio timeout\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Check PIO status */
|
||||
pcie_advk_check_pio_status(pcie, false, ®);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_link_up() - Check if PCIe link is up or not
|
||||
*
|
||||
* @pcie: The PCI device to access
|
||||
*
|
||||
* Return 1 (true) on link up.
|
||||
* Return 0 (false) on link down.
|
||||
*/
|
||||
static int pcie_advk_link_up(struct pcie_advk *pcie)
|
||||
{
|
||||
u32 val, ltssm_state;
|
||||
|
||||
val = advk_readl(pcie, CFG_REG);
|
||||
ltssm_state = (val >> LTSSM_SHIFT) & LTSSM_MASK;
|
||||
return ltssm_state >= LTSSM_L0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_wait_for_link() - Wait for link training to be accomplished
|
||||
*
|
||||
* @pcie: The PCI device to access
|
||||
*
|
||||
* Wait up to 1 second for link training to be accomplished.
|
||||
*
|
||||
* Return 1 (true) if link training ends up with link up success.
|
||||
* Return 0 (false) if link training ends up with link up failure.
|
||||
*/
|
||||
static int pcie_advk_wait_for_link(struct pcie_advk *pcie)
|
||||
{
|
||||
int retries;
|
||||
|
||||
/* check if the link is up or not */
|
||||
for (retries = 0; retries < MAX_RETRIES; retries++) {
|
||||
if (pcie_advk_link_up(pcie)) {
|
||||
printf("PCIE-%d: Link up\n", pcie->first_busno);
|
||||
return 0;
|
||||
}
|
||||
|
||||
udelay(LINK_WAIT_TIMEOUT);
|
||||
}
|
||||
|
||||
printf("PCIE-%d: Link down\n", pcie->first_busno);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_setup_hw() - PCIe initailzation
|
||||
*
|
||||
* @pcie: The PCI device to access
|
||||
*
|
||||
* Return: 0 on success
|
||||
*/
|
||||
static int pcie_advk_setup_hw(struct pcie_advk *pcie)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
/* Set to Direct mode */
|
||||
reg = advk_readl(pcie, CTRL_CONFIG_REG);
|
||||
reg &= ~(CTRL_MODE_MASK << CTRL_MODE_SHIFT);
|
||||
reg |= ((PCIE_CORE_MODE_DIRECT & CTRL_MODE_MASK) << CTRL_MODE_SHIFT);
|
||||
advk_writel(pcie, reg, CTRL_CONFIG_REG);
|
||||
|
||||
/* Set PCI global control register to RC mode */
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
|
||||
reg |= (IS_RC_MSK << IS_RC_SHIFT);
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/* Set Advanced Error Capabilities and Control PF0 register */
|
||||
reg = PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX |
|
||||
PCIE_CORE_ERR_CAPCTL_ECRC_CHK_TX_EN |
|
||||
PCIE_CORE_ERR_CAPCTL_ECRC_CHECK |
|
||||
PCIE_CORE_ERR_CAPCTL_ECRC_CHECK_RCV;
|
||||
advk_writel(pcie, reg, PCIE_CORE_ERR_CAPCTL_REG);
|
||||
|
||||
/* Set PCIe Device Control and Status 1 PF0 register */
|
||||
reg = PCIE_CORE_DEV_CTRL_STATS_RELAX_ORDER_DISABLE |
|
||||
PCIE_CORE_DEV_CTRL_STATS_SNOOP_DISABLE;
|
||||
advk_writel(pcie, reg, PCIE_CORE_DEV_CTRL_STATS_REG);
|
||||
|
||||
/* Program PCIe Control 2 to disable strict ordering */
|
||||
reg = PCIE_CORE_CTRL2_RESERVED |
|
||||
PCIE_CORE_CTRL2_TD_ENABLE;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
|
||||
|
||||
/* Set GEN2 */
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
|
||||
reg &= ~PCIE_GEN_SEL_MSK;
|
||||
reg |= SPEED_GEN_2;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/* Set lane X1 */
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
|
||||
reg &= ~LANE_CNT_MSK;
|
||||
reg |= LANE_COUNT_1;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/* Enable link training */
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL0_REG);
|
||||
reg |= LINK_TRAINING_EN;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL0_REG);
|
||||
|
||||
/*
|
||||
* Enable AXI address window location generation:
|
||||
* When it is enabled, the default outbound window
|
||||
* configurations (Default User Field: 0xD0074CFC)
|
||||
* are used to transparent address translation for
|
||||
* the outbound transactions. Thus, PCIe address
|
||||
* windows are not required.
|
||||
*/
|
||||
reg = advk_readl(pcie, PCIE_CORE_CTRL2_REG);
|
||||
reg |= PCIE_CORE_CTRL2_ADDRWIN_MAP_ENABLE;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CTRL2_REG);
|
||||
|
||||
/*
|
||||
* Bypass the address window mapping for PIO:
|
||||
* Since PIO access already contains all required
|
||||
* info over AXI interface by PIO registers, the
|
||||
* address window is not required.
|
||||
*/
|
||||
reg = advk_readl(pcie, PIO_CTRL);
|
||||
reg |= PIO_CTRL_ADDR_WIN_DISABLE;
|
||||
advk_writel(pcie, reg, PIO_CTRL);
|
||||
|
||||
/* Start link training */
|
||||
reg = advk_readl(pcie, PCIE_CORE_LINK_CTRL_STAT_REG);
|
||||
reg |= PCIE_CORE_LINK_TRAINING;
|
||||
advk_writel(pcie, reg, PCIE_CORE_LINK_CTRL_STAT_REG);
|
||||
|
||||
/* Wait for PCIe link up */
|
||||
if (pcie_advk_wait_for_link(pcie))
|
||||
return -ENXIO;
|
||||
|
||||
reg = advk_readl(pcie, PCIE_CORE_CMD_STATUS_REG);
|
||||
reg |= PCIE_CORE_CMD_MEM_ACCESS_EN |
|
||||
PCIE_CORE_CMD_IO_ACCESS_EN |
|
||||
PCIE_CORE_CMD_MEM_IO_REQ_EN;
|
||||
advk_writel(pcie, reg, PCIE_CORE_CMD_STATUS_REG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_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 pcie_advk_probe(struct udevice *dev)
|
||||
{
|
||||
struct pcie_advk *pcie = dev_get_priv(dev);
|
||||
|
||||
#ifdef CONFIG_DM_GPIO
|
||||
struct gpio_desc reset_gpio;
|
||||
|
||||
gpio_request_by_name(dev, "reset-gpio", 0, &reset_gpio,
|
||||
GPIOD_IS_OUT);
|
||||
/*
|
||||
* Issue reset to add-in card through the dedicated GPIO.
|
||||
* Some boards are connecting the card reset pin to common system
|
||||
* reset wire and others are using separate GPIO port.
|
||||
* In the last case we have to release a reset of the addon card
|
||||
* using this GPIO.
|
||||
*
|
||||
* FIX-ME:
|
||||
* The PCIe RESET signal is not supposed to be released along
|
||||
* with the SOC RESET signal. It should be lowered as early as
|
||||
* possible before PCIe PHY initialization. Moreover, the PCIe
|
||||
* clock should be gated as well.
|
||||
*/
|
||||
if (dm_gpio_is_valid(&reset_gpio)) {
|
||||
dev_dbg(pcie->dev, "Toggle PCIE Reset GPIO ...\n");
|
||||
dm_gpio_set_value(&reset_gpio, 0);
|
||||
mdelay(200);
|
||||
dm_gpio_set_value(&reset_gpio, 1);
|
||||
}
|
||||
#else
|
||||
dev_dbg(pcie->dev, "PCIE Reset on GPIO support is missing\n");
|
||||
#endif /* CONFIG_DM_GPIO */
|
||||
|
||||
pcie->first_busno = dev->seq;
|
||||
pcie->dev = pci_get_controller(dev);
|
||||
|
||||
return pcie_advk_setup_hw(pcie);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_advk_ofdata_to_platdata() - Translate from DT to device state
|
||||
*
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pcie_advk_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct pcie_advk *pcie = dev_get_priv(dev);
|
||||
|
||||
/* Get the register base address */
|
||||
pcie->base = (void *)dev_read_addr_index(dev, 0);
|
||||
if ((fdt_addr_t)pcie->base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pcie_advk_ops = {
|
||||
.read_config = pcie_advk_read_config,
|
||||
.write_config = pcie_advk_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pcie_advk_ids[] = {
|
||||
{ .compatible = "marvell,armada-37xx-pcie" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_advk) = {
|
||||
.name = "pcie_advk",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pcie_advk_ids,
|
||||
.ops = &pcie_advk_ops,
|
||||
.ofdata_to_platdata = pcie_advk_ofdata_to_platdata,
|
||||
.probe = pcie_advk_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct pcie_advk),
|
||||
};
|
||||
@@ -0,0 +1,131 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include <pci.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
struct sandbox_pci_emul_priv {
|
||||
int dev_count;
|
||||
};
|
||||
|
||||
int sandbox_pci_get_emul(struct udevice *bus, pci_dev_t find_devfn,
|
||||
struct udevice **containerp, struct udevice **emulp)
|
||||
{
|
||||
struct pci_emul_uc_priv *upriv;
|
||||
struct udevice *dev;
|
||||
int ret;
|
||||
|
||||
*containerp = NULL;
|
||||
ret = pci_bus_find_devfn(bus, PCI_MASK_BUS(find_devfn), &dev);
|
||||
if (ret) {
|
||||
debug("%s: Could not find emulator for dev %x\n", __func__,
|
||||
find_devfn);
|
||||
return ret;
|
||||
}
|
||||
*containerp = dev;
|
||||
|
||||
ret = uclass_get_device_by_phandle(UCLASS_PCI_EMUL, dev, "sandbox,emul",
|
||||
emulp);
|
||||
if (!ret) {
|
||||
upriv = dev_get_uclass_priv(*emulp);
|
||||
|
||||
upriv->client = dev;
|
||||
} else if (device_get_uclass_id(dev) != UCLASS_PCI_GENERIC) {
|
||||
/*
|
||||
* See commit 4345998ae9df,
|
||||
* "pci: sandbox: Support dynamically binding device driver"
|
||||
*/
|
||||
*emulp = dev;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sandbox_pci_get_client(struct udevice *emul, struct udevice **devp)
|
||||
{
|
||||
struct pci_emul_uc_priv *upriv = dev_get_uclass_priv(emul);
|
||||
|
||||
if (!upriv->client)
|
||||
return -ENOENT;
|
||||
*devp = upriv->client;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint sandbox_pci_read_bar(u32 barval, int type, uint size)
|
||||
{
|
||||
u32 result;
|
||||
|
||||
result = barval;
|
||||
if (result == 0xffffffff) {
|
||||
if (type == PCI_BASE_ADDRESS_SPACE_IO) {
|
||||
result = (~(size - 1) &
|
||||
PCI_BASE_ADDRESS_IO_MASK) |
|
||||
PCI_BASE_ADDRESS_SPACE_IO;
|
||||
} else {
|
||||
result = (~(size - 1) &
|
||||
PCI_BASE_ADDRESS_MEM_MASK) |
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sandbox_pci_emul_post_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_pci_emul_priv *priv = dev->uclass->priv;
|
||||
|
||||
priv->dev_count++;
|
||||
sandbox_set_enable_pci_map(true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sandbox_pci_emul_pre_remove(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_pci_emul_priv *priv = dev->uclass->priv;
|
||||
|
||||
priv->dev_count--;
|
||||
sandbox_set_enable_pci_map(priv->dev_count > 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UCLASS_DRIVER(pci_emul) = {
|
||||
.id = UCLASS_PCI_EMUL,
|
||||
.name = "pci_emul",
|
||||
.post_probe = sandbox_pci_emul_post_probe,
|
||||
.pre_remove = sandbox_pci_emul_pre_remove,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_pci_emul_priv),
|
||||
.per_device_auto_alloc_size = sizeof(struct pci_emul_uc_priv),
|
||||
};
|
||||
|
||||
/*
|
||||
* This uclass is a child of the pci bus. Its platdata is not defined here so
|
||||
* is defined by its parent, UCLASS_PCI, which uses struct pci_child_platdata.
|
||||
* See per_child_platdata_auto_alloc_size in UCLASS_DRIVER(pci).
|
||||
*/
|
||||
UCLASS_DRIVER(pci_emul_parent) = {
|
||||
.id = UCLASS_PCI_EMUL_PARENT,
|
||||
.name = "pci_emul_parent",
|
||||
.post_bind = dm_scan_fdt_dev,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_emul_parent_ids[] = {
|
||||
{ .compatible = "sandbox,pci-emul-parent" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_emul_parent_drv) = {
|
||||
.name = "pci_emul_parent_drv",
|
||||
.id = UCLASS_PCI_EMUL_PARENT,
|
||||
.of_match = pci_emul_parent_ids,
|
||||
};
|
||||
@@ -0,0 +1,263 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RCar Gen2 PCIEC driver
|
||||
*
|
||||
* Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
|
||||
/* AHB-PCI Bridge PCI communication registers */
|
||||
#define RCAR_AHBPCI_PCICOM_OFFSET 0x800
|
||||
|
||||
#define RCAR_PCIAHB_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x00)
|
||||
#define RCAR_PCIAHB_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x04)
|
||||
#define RCAR_PCIAHB_PREFETCH0 0x0
|
||||
#define RCAR_PCIAHB_PREFETCH4 0x1
|
||||
#define RCAR_PCIAHB_PREFETCH8 0x2
|
||||
#define RCAR_PCIAHB_PREFETCH16 0x3
|
||||
|
||||
#define RCAR_AHBPCI_WIN1_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x10)
|
||||
#define RCAR_AHBPCI_WIN2_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x14)
|
||||
#define RCAR_AHBPCI_WIN_CTR_MEM (3 << 1)
|
||||
#define RCAR_AHBPCI_WIN_CTR_CFG (5 << 1)
|
||||
#define RCAR_AHBPCI_WIN1_HOST BIT(30)
|
||||
#define RCAR_AHBPCI_WIN1_DEVICE BIT(31)
|
||||
|
||||
#define RCAR_PCI_INT_ENABLE_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x20)
|
||||
#define RCAR_PCI_INT_STATUS_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x24)
|
||||
#define RCAR_PCI_INT_SIGTABORT BIT(0)
|
||||
#define RCAR_PCI_INT_SIGRETABORT BIT(1)
|
||||
#define RCAR_PCI_INT_REMABORT BIT(2)
|
||||
#define RCAR_PCI_INT_PERR BIT(3)
|
||||
#define RCAR_PCI_INT_SIGSERR BIT(4)
|
||||
#define RCAR_PCI_INT_RESERR BIT(5)
|
||||
#define RCAR_PCI_INT_WIN1ERR BIT(12)
|
||||
#define RCAR_PCI_INT_WIN2ERR BIT(13)
|
||||
#define RCAR_PCI_INT_A BIT(16)
|
||||
#define RCAR_PCI_INT_B BIT(17)
|
||||
#define RCAR_PCI_INT_PME BIT(19)
|
||||
#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT | \
|
||||
RCAR_PCI_INT_SIGRETABORT | \
|
||||
RCAR_PCI_INT_SIGRETABORT | \
|
||||
RCAR_PCI_INT_REMABORT | \
|
||||
RCAR_PCI_INT_PERR | \
|
||||
RCAR_PCI_INT_SIGSERR | \
|
||||
RCAR_PCI_INT_RESERR | \
|
||||
RCAR_PCI_INT_WIN1ERR | \
|
||||
RCAR_PCI_INT_WIN2ERR)
|
||||
|
||||
#define RCAR_AHB_BUS_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x30)
|
||||
#define RCAR_AHB_BUS_MMODE_HTRANS BIT(0)
|
||||
#define RCAR_AHB_BUS_MMODE_BYTE_BURST BIT(1)
|
||||
#define RCAR_AHB_BUS_MMODE_WR_INCR BIT(2)
|
||||
#define RCAR_AHB_BUS_MMODE_HBUS_REQ BIT(7)
|
||||
#define RCAR_AHB_BUS_SMODE_READYCTR BIT(17)
|
||||
#define RCAR_AHB_BUS_MODE (RCAR_AHB_BUS_MMODE_HTRANS | \
|
||||
RCAR_AHB_BUS_MMODE_BYTE_BURST | \
|
||||
RCAR_AHB_BUS_MMODE_WR_INCR | \
|
||||
RCAR_AHB_BUS_MMODE_HBUS_REQ | \
|
||||
RCAR_AHB_BUS_SMODE_READYCTR)
|
||||
|
||||
#define RCAR_USBCTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x34)
|
||||
#define RCAR_USBCTR_USBH_RST BIT(0)
|
||||
#define RCAR_USBCTR_PCICLK_MASK BIT(1)
|
||||
#define RCAR_USBCTR_PLL_RST BIT(2)
|
||||
#define RCAR_USBCTR_DIRPD BIT(8)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN2_EN BIT(9)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN1_256M (0 << 10)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN1_512M (1 << 10)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN1_1G (2 << 10)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN1_2G (3 << 10)
|
||||
#define RCAR_USBCTR_PCIAHB_WIN1_MASK (3 << 10)
|
||||
|
||||
#define RCAR_PCI_ARBITER_CTR_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x40)
|
||||
#define RCAR_PCI_ARBITER_PCIREQ0 BIT(0)
|
||||
#define RCAR_PCI_ARBITER_PCIREQ1 BIT(1)
|
||||
#define RCAR_PCI_ARBITER_PCIBP_MODE BIT(12)
|
||||
|
||||
#define RCAR_PCI_UNIT_REV_REG (RCAR_AHBPCI_PCICOM_OFFSET + 0x48)
|
||||
|
||||
struct rcar_gen2_pci_priv {
|
||||
fdt_addr_t cfg_base;
|
||||
fdt_addr_t mem_base;
|
||||
};
|
||||
|
||||
static int rcar_gen2_pci_addr_valid(pci_dev_t d, uint offset)
|
||||
{
|
||||
u32 slot;
|
||||
|
||||
if (PCI_FUNC(d))
|
||||
return -EINVAL;
|
||||
|
||||
/* Only one EHCI/OHCI device built-in */
|
||||
slot = PCI_DEV(d);
|
||||
if (slot != 1 && slot != 2)
|
||||
return -EINVAL;
|
||||
|
||||
/* bridge logic only has registers to 0x40 */
|
||||
if (slot == 0x0 && offset >= 0x40)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 get_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset)
|
||||
{
|
||||
struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return priv->cfg_base + (PCI_DEV(bdf) >> 1) * 0x100 + (offset & ~3);
|
||||
}
|
||||
|
||||
static u32 setup_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset)
|
||||
{
|
||||
struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
|
||||
u32 reg;
|
||||
|
||||
reg = PCI_DEV(bdf) ? RCAR_AHBPCI_WIN1_DEVICE : RCAR_AHBPCI_WIN1_HOST;
|
||||
reg |= RCAR_AHBPCI_WIN_CTR_CFG;
|
||||
writel(reg, priv->cfg_base + RCAR_AHBPCI_WIN1_CTR_REG);
|
||||
|
||||
return get_bus_address(dev, bdf, offset);
|
||||
}
|
||||
|
||||
static int rcar_gen2_pci_read_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong *value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
u32 addr, reg;
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen2_pci_addr_valid(bdf, offset);
|
||||
if (ret) {
|
||||
*value = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr = get_bus_address(dev, bdf, offset);
|
||||
reg = readl(addr);
|
||||
*value = pci_conv_32_to_size(reg, offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen2_pci_write_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
u32 addr, reg, old;
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen2_pci_addr_valid(bdf, offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
addr = get_bus_address(dev, bdf, offset);
|
||||
|
||||
old = readl(addr);
|
||||
reg = pci_conv_size_to_32(old, value, offset, size);
|
||||
writel(reg, addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen2_pci_probe(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
|
||||
struct clk pci_clk;
|
||||
u32 devad;
|
||||
int ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &pci_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&pci_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Clock & Reset & Direct Power Down */
|
||||
clrsetbits_le32(priv->cfg_base + RCAR_USBCTR_REG,
|
||||
RCAR_USBCTR_DIRPD | RCAR_USBCTR_PCICLK_MASK |
|
||||
RCAR_USBCTR_USBH_RST,
|
||||
RCAR_USBCTR_PCIAHB_WIN1_1G);
|
||||
clrbits_le32(priv->cfg_base + RCAR_USBCTR_REG, RCAR_USBCTR_PLL_RST);
|
||||
|
||||
/* AHB-PCI Bridge Communication Registers */
|
||||
writel(RCAR_AHB_BUS_MODE, priv->cfg_base + RCAR_AHB_BUS_CTR_REG);
|
||||
writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | RCAR_PCIAHB_PREFETCH16,
|
||||
priv->cfg_base + RCAR_PCIAHB_WIN1_CTR_REG);
|
||||
writel(0xf0000000 | RCAR_PCIAHB_PREFETCH16,
|
||||
priv->cfg_base + RCAR_PCIAHB_WIN2_CTR_REG);
|
||||
writel(priv->mem_base | RCAR_AHBPCI_WIN_CTR_MEM,
|
||||
priv->cfg_base + RCAR_AHBPCI_WIN2_CTR_REG);
|
||||
setbits_le32(priv->cfg_base + RCAR_PCI_ARBITER_CTR_REG,
|
||||
RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
|
||||
RCAR_PCI_ARBITER_PCIBP_MODE);
|
||||
|
||||
/* PCI Configuration Registers for AHBPCI */
|
||||
devad = setup_bus_address(dev, PCI_BDF(0, 0, 0), 0);
|
||||
writel(priv->cfg_base + 0x800, devad + PCI_BASE_ADDRESS_0);
|
||||
writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, devad + PCI_BASE_ADDRESS_1);
|
||||
writel(0xf0000000, devad + PCI_BASE_ADDRESS_2);
|
||||
writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
|
||||
PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
|
||||
devad + PCI_COMMAND);
|
||||
|
||||
/* PCI Configuration Registers for OHCI */
|
||||
devad = setup_bus_address(dev, PCI_BDF(0, 1, 0), 0);
|
||||
writel(priv->mem_base + 0x0, devad + PCI_BASE_ADDRESS_0);
|
||||
writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
|
||||
PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
|
||||
devad + PCI_COMMAND);
|
||||
|
||||
/* PCI Configuration Registers for EHCI */
|
||||
devad = setup_bus_address(dev, PCI_BDF(0, 2, 0), 0);
|
||||
writel(priv->mem_base + 0x1000, devad + PCI_BASE_ADDRESS_0);
|
||||
writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
|
||||
PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
|
||||
devad + PCI_COMMAND);
|
||||
|
||||
/* Enable PCI interrupt */
|
||||
setbits_le32(priv->cfg_base + RCAR_PCI_INT_ENABLE_REG,
|
||||
RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen2_pci_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->cfg_base = devfdt_get_addr_index(dev, 0);
|
||||
priv->mem_base = devfdt_get_addr_index(dev, 1);
|
||||
if (!priv->cfg_base || !priv->mem_base)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops rcar_gen2_pci_ops = {
|
||||
.read_config = rcar_gen2_pci_read_config,
|
||||
.write_config = rcar_gen2_pci_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id rcar_gen2_pci_ids[] = {
|
||||
{ .compatible = "renesas,pci-rcar-gen2" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rcar_gen2_pci) = {
|
||||
.name = "rcar_gen2_pci",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = rcar_gen2_pci_ids,
|
||||
.ops = &rcar_gen2_pci_ops,
|
||||
.probe = rcar_gen2_pci_probe,
|
||||
.ofdata_to_platdata = rcar_gen2_pci_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct rcar_gen2_pci_priv),
|
||||
};
|
||||
@@ -0,0 +1,411 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Renesas RCar Gen3 PCIEC driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 Marek Vasut <marek.vasut@gmail.com>
|
||||
*
|
||||
* Based on Linux PCIe driver for Renesas R-Car SoCs
|
||||
* Copyright (C) 2014 Renesas Electronics Europe Ltd
|
||||
*
|
||||
* Based on:
|
||||
* arch/sh/drivers/pci/pcie-sh7786.c
|
||||
* arch/sh/drivers/pci/ops-sh7786.c
|
||||
* Copyright (C) 2009 - 2011 Paul Mundt
|
||||
*
|
||||
* Author: Phil Edworthy <phil.edworthy@renesas.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <asm/io.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
#include <wait_bit.h>
|
||||
|
||||
#define PCIECAR 0x000010
|
||||
#define PCIECCTLR 0x000018
|
||||
#define CONFIG_SEND_ENABLE BIT(31)
|
||||
#define TYPE0 (0 << 8)
|
||||
#define TYPE1 BIT(8)
|
||||
#define PCIECDR 0x000020
|
||||
#define PCIEMSR 0x000028
|
||||
#define PCIEINTXR 0x000400
|
||||
#define PCIEPHYSR 0x0007f0
|
||||
#define PHYRDY BIT(0)
|
||||
#define PCIEMSITXR 0x000840
|
||||
|
||||
/* Transfer control */
|
||||
#define PCIETCTLR 0x02000
|
||||
#define CFINIT 1
|
||||
#define PCIETSTR 0x02004
|
||||
#define DATA_LINK_ACTIVE 1
|
||||
#define PCIEERRFR 0x02020
|
||||
#define UNSUPPORTED_REQUEST BIT(4)
|
||||
#define PCIEMSIFR 0x02044
|
||||
#define PCIEMSIALR 0x02048
|
||||
#define MSIFE 1
|
||||
#define PCIEMSIAUR 0x0204c
|
||||
#define PCIEMSIIER 0x02050
|
||||
|
||||
/* root port address */
|
||||
#define PCIEPRAR(x) (0x02080 + ((x) * 0x4))
|
||||
|
||||
/* local address reg & mask */
|
||||
#define PCIELAR(x) (0x02200 + ((x) * 0x20))
|
||||
#define PCIELAMR(x) (0x02208 + ((x) * 0x20))
|
||||
#define LAM_PREFETCH BIT(3)
|
||||
#define LAM_64BIT BIT(2)
|
||||
#define LAR_ENABLE BIT(1)
|
||||
|
||||
/* PCIe address reg & mask */
|
||||
#define PCIEPALR(x) (0x03400 + ((x) * 0x20))
|
||||
#define PCIEPAUR(x) (0x03404 + ((x) * 0x20))
|
||||
#define PCIEPAMR(x) (0x03408 + ((x) * 0x20))
|
||||
#define PCIEPTCTLR(x) (0x0340c + ((x) * 0x20))
|
||||
#define PAR_ENABLE BIT(31)
|
||||
#define IO_SPACE BIT(8)
|
||||
|
||||
/* Configuration */
|
||||
#define PCICONF(x) (0x010000 + ((x) * 0x4))
|
||||
#define PMCAP(x) (0x010040 + ((x) * 0x4))
|
||||
#define EXPCAP(x) (0x010070 + ((x) * 0x4))
|
||||
#define VCCAP(x) (0x010100 + ((x) * 0x4))
|
||||
|
||||
/* link layer */
|
||||
#define IDSETR1 0x011004
|
||||
#define TLCTLR 0x011048
|
||||
#define MACSR 0x011054
|
||||
#define SPCHGFIN BIT(4)
|
||||
#define SPCHGFAIL BIT(6)
|
||||
#define SPCHGSUC BIT(7)
|
||||
#define LINK_SPEED (0xf << 16)
|
||||
#define LINK_SPEED_2_5GTS (1 << 16)
|
||||
#define LINK_SPEED_5_0GTS (2 << 16)
|
||||
#define MACCTLR 0x011058
|
||||
#define SPEED_CHANGE BIT(24)
|
||||
#define SCRAMBLE_DISABLE BIT(27)
|
||||
#define MACS2R 0x011078
|
||||
#define MACCGSPSETR 0x011084
|
||||
#define SPCNGRSN BIT(31)
|
||||
|
||||
/* R-Car H1 PHY */
|
||||
#define H1_PCIEPHYADRR 0x04000c
|
||||
#define WRITE_CMD BIT(16)
|
||||
#define PHY_ACK BIT(24)
|
||||
#define RATE_POS 12
|
||||
#define LANE_POS 8
|
||||
#define ADR_POS 0
|
||||
#define H1_PCIEPHYDOUTR 0x040014
|
||||
|
||||
/* R-Car Gen2 PHY */
|
||||
#define GEN2_PCIEPHYADDR 0x780
|
||||
#define GEN2_PCIEPHYDATA 0x784
|
||||
#define GEN2_PCIEPHYCTRL 0x78c
|
||||
|
||||
#define INT_PCI_MSI_NR 32
|
||||
|
||||
#define RCONF(x) (PCICONF(0) + (x))
|
||||
#define RPMCAP(x) (PMCAP(0) + (x))
|
||||
#define REXPCAP(x) (EXPCAP(0) + (x))
|
||||
#define RVCCAP(x) (VCCAP(0) + (x))
|
||||
|
||||
#define PCIE_CONF_BUS(b) (((b) & 0xff) << 24)
|
||||
#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 19)
|
||||
#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 16)
|
||||
|
||||
#define RCAR_PCI_MAX_RESOURCES 4
|
||||
#define MAX_NR_INBOUND_MAPS 6
|
||||
|
||||
#define PCI_EXP_FLAGS 2 /* Capabilities register */
|
||||
#define PCI_EXP_FLAGS_TYPE 0x00f0 /* Device/Port type */
|
||||
#define PCI_EXP_TYPE_ROOT_PORT 0x4 /* Root Port */
|
||||
#define PCI_EXP_LNKCAP 12 /* Link Capabilities */
|
||||
#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */
|
||||
#define PCI_EXP_SLTCAP 20 /* Slot Capabilities */
|
||||
#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */
|
||||
|
||||
enum {
|
||||
RCAR_PCI_ACCESS_READ,
|
||||
RCAR_PCI_ACCESS_WRITE,
|
||||
};
|
||||
|
||||
struct rcar_gen3_pcie_priv {
|
||||
fdt_addr_t regs;
|
||||
};
|
||||
|
||||
static void rcar_rmw32(struct udevice *dev, int where, u32 mask, u32 data)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
int shift = 8 * (where & 3);
|
||||
|
||||
clrsetbits_le32(priv->regs + (where & ~3),
|
||||
mask << shift, data << shift);
|
||||
}
|
||||
|
||||
static u32 rcar_read_conf(struct udevice *dev, int where)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
int shift = 8 * (where & 3);
|
||||
|
||||
return readl(priv->regs + (where & ~3)) >> shift;
|
||||
}
|
||||
|
||||
static int rcar_pcie_config_access(struct udevice *udev,
|
||||
unsigned char access_type,
|
||||
pci_dev_t bdf, int where, ulong *data)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(udev);
|
||||
u32 reg = where & ~3;
|
||||
|
||||
/* Clear errors */
|
||||
clrbits_le32(priv->regs + PCIEERRFR, 0);
|
||||
|
||||
/* Set the PIO address */
|
||||
writel((bdf << 8) | reg, priv->regs + PCIECAR);
|
||||
|
||||
/* Enable the configuration access */
|
||||
if (!PCI_BUS(bdf))
|
||||
writel(CONFIG_SEND_ENABLE | TYPE0, priv->regs + PCIECCTLR);
|
||||
else
|
||||
writel(CONFIG_SEND_ENABLE | TYPE1, priv->regs + PCIECCTLR);
|
||||
|
||||
/* Check for errors */
|
||||
if (readl(priv->regs + PCIEERRFR) & UNSUPPORTED_REQUEST)
|
||||
return -ENODEV;
|
||||
|
||||
/* Check for master and target aborts */
|
||||
if (rcar_read_conf(udev, RCONF(PCI_STATUS)) &
|
||||
(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT))
|
||||
return -ENODEV;
|
||||
|
||||
if (access_type == RCAR_PCI_ACCESS_READ)
|
||||
*data = readl(priv->regs + PCIECDR);
|
||||
else
|
||||
writel(*data, priv->regs + PCIECDR);
|
||||
|
||||
/* Disable the configuration access */
|
||||
writel(0, priv->regs + PCIECCTLR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_addr_valid(pci_dev_t d, uint where)
|
||||
{
|
||||
u32 slot;
|
||||
|
||||
if (PCI_FUNC(d))
|
||||
return -EINVAL;
|
||||
|
||||
slot = PCI_DEV(d);
|
||||
if (slot != 1)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_read_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint where, ulong *val,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
ulong reg;
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen3_pcie_addr_valid(bdf, where);
|
||||
if (ret) {
|
||||
*val = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = rcar_pcie_config_access(dev, RCAR_PCI_ACCESS_READ,
|
||||
bdf, where, ®);
|
||||
if (ret != 0)
|
||||
reg = 0xffffffffUL;
|
||||
|
||||
*val = pci_conv_32_to_size(reg, where, size);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_write_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint where, ulong val,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
ulong data;
|
||||
int ret;
|
||||
|
||||
ret = rcar_gen3_pcie_addr_valid(bdf, where);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data = pci_conv_32_to_size(val, where, size);
|
||||
|
||||
ret = rcar_pcie_config_access(dev, RCAR_PCI_ACCESS_WRITE,
|
||||
bdf, where, &data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_wait_for_phyrdy(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
|
||||
return wait_for_bit_le32((void *)priv->regs + PCIEPHYSR, PHYRDY,
|
||||
true, 50, false);
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_wait_for_dl(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
|
||||
return wait_for_bit_le32((void *)priv->regs + PCIETSTR,
|
||||
DATA_LINK_ACTIVE, true, 50, false);
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_hw_init(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Begin initialization */
|
||||
writel(0, priv->regs + PCIETCTLR);
|
||||
|
||||
/* Set mode */
|
||||
writel(1, priv->regs + PCIEMSR);
|
||||
|
||||
ret = rcar_gen3_pcie_wait_for_phyrdy(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Initial header for port config space is type 1, set the device
|
||||
* class to match. Hardware takes care of propagating the IDSETR
|
||||
* settings, so there is no need to bother with a quirk.
|
||||
*/
|
||||
writel(PCI_CLASS_BRIDGE_PCI << 16, priv->regs + IDSETR1);
|
||||
|
||||
/*
|
||||
* Setup Secondary Bus Number & Subordinate Bus Number, even though
|
||||
* they aren't used, to avoid bridge being detected as broken.
|
||||
*/
|
||||
rcar_rmw32(dev, RCONF(PCI_SECONDARY_BUS), 0xff, 1);
|
||||
rcar_rmw32(dev, RCONF(PCI_SUBORDINATE_BUS), 0xff, 1);
|
||||
|
||||
/* Initialize default capabilities. */
|
||||
rcar_rmw32(dev, REXPCAP(0), 0xff, PCI_CAP_ID_EXP);
|
||||
rcar_rmw32(dev, REXPCAP(PCI_EXP_FLAGS),
|
||||
PCI_EXP_FLAGS_TYPE, PCI_EXP_TYPE_ROOT_PORT << 4);
|
||||
rcar_rmw32(dev, RCONF(PCI_HEADER_TYPE), 0x7f,
|
||||
PCI_HEADER_TYPE_BRIDGE);
|
||||
|
||||
/* Enable data link layer active state reporting */
|
||||
rcar_rmw32(dev, REXPCAP(PCI_EXP_LNKCAP),
|
||||
PCI_EXP_LNKCAP_DLLLARC, PCI_EXP_LNKCAP_DLLLARC);
|
||||
|
||||
/* Write out the physical slot number = 0 */
|
||||
rcar_rmw32(dev, REXPCAP(PCI_EXP_SLTCAP),
|
||||
PCI_EXP_SLTCAP_PSN, 0);
|
||||
|
||||
/* Set the completion timer timeout to the maximum 50ms. */
|
||||
rcar_rmw32(dev, TLCTLR + 1, 0x3f, 50);
|
||||
|
||||
/* Terminate list of capabilities (Next Capability Offset=0) */
|
||||
rcar_rmw32(dev, RVCCAP(0), 0xfff00000, 0);
|
||||
|
||||
/* Finish initialization - establish a PCI Express link */
|
||||
writel(CFINIT, priv->regs + PCIETCTLR);
|
||||
|
||||
return rcar_gen3_pcie_wait_for_dl(dev);
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_probe(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
struct pci_controller *hose = dev_get_uclass_priv(dev);
|
||||
struct clk pci_clk;
|
||||
u32 mask;
|
||||
int i, cnt, ret;
|
||||
|
||||
ret = clk_get_by_index(dev, 0, &pci_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(&pci_clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < hose->region_count; i++) {
|
||||
if (hose->regions[i].flags != PCI_REGION_SYS_MEMORY)
|
||||
continue;
|
||||
|
||||
if (hose->regions[i].phys_start == 0)
|
||||
continue;
|
||||
|
||||
mask = (hose->regions[i].size - 1) & ~0xf;
|
||||
mask |= LAR_ENABLE;
|
||||
writel(hose->regions[i].phys_start, priv->regs + PCIEPRAR(0));
|
||||
writel(hose->regions[i].phys_start, priv->regs + PCIELAR(0));
|
||||
writel(mask, priv->regs + PCIELAMR(0));
|
||||
break;
|
||||
}
|
||||
|
||||
writel(0, priv->regs + PCIEPRAR(4));
|
||||
writel(0, priv->regs + PCIELAR(4));
|
||||
writel(0, priv->regs + PCIELAMR(4));
|
||||
|
||||
ret = rcar_gen3_pcie_hw_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0, cnt = 0; i < hose->region_count; i++) {
|
||||
if (hose->regions[i].flags == PCI_REGION_SYS_MEMORY)
|
||||
continue;
|
||||
|
||||
writel(0, priv->regs + PCIEPTCTLR(cnt));
|
||||
writel((hose->regions[i].size - 1) & ~0x7f,
|
||||
priv->regs + PCIEPAMR(cnt));
|
||||
writel(upper_32_bits(hose->regions[i].phys_start),
|
||||
priv->regs + PCIEPAUR(cnt));
|
||||
writel(lower_32_bits(hose->regions[i].phys_start),
|
||||
priv->regs + PCIEPALR(cnt));
|
||||
mask = PAR_ENABLE;
|
||||
if (hose->regions[i].flags == PCI_REGION_IO)
|
||||
mask |= IO_SPACE;
|
||||
writel(mask, priv->regs + PCIEPTCTLR(cnt));
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rcar_gen3_pcie_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct rcar_gen3_pcie_priv *priv = dev_get_platdata(dev);
|
||||
|
||||
priv->regs = devfdt_get_addr_index(dev, 0);
|
||||
if (!priv->regs)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops rcar_gen3_pcie_ops = {
|
||||
.read_config = rcar_gen3_pcie_read_config,
|
||||
.write_config = rcar_gen3_pcie_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id rcar_gen3_pcie_ids[] = {
|
||||
{ .compatible = "renesas,pcie-rcar-gen3" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(rcar_gen3_pcie) = {
|
||||
.name = "rcar_gen3_pcie",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = rcar_gen3_pcie_ids,
|
||||
.ops = &rcar_gen3_pcie_ops,
|
||||
.probe = rcar_gen3_pcie_probe,
|
||||
.ofdata_to_platdata = rcar_gen3_pcie_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct rcar_gen3_pcie_priv),
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,583 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Andreas Heppel <aheppel@sysgo.de>
|
||||
*
|
||||
* (C) Copyright 2002, 2003
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Old PCI routines
|
||||
*
|
||||
* Do not change this file. Instead, convert your board to use CONFIG_DM_PCI
|
||||
* and change pci-uclass.c.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
|
||||
#include <command.h>
|
||||
#include <env.h>
|
||||
#include <errno.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <pci.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
#define PCI_HOSE_OP(rw, size, type) \
|
||||
int pci_hose_##rw##_config_##size(struct pci_controller *hose, \
|
||||
pci_dev_t dev, \
|
||||
int offset, type value) \
|
||||
{ \
|
||||
return hose->rw##_##size(hose, dev, offset, value); \
|
||||
}
|
||||
|
||||
PCI_HOSE_OP(read, byte, u8 *)
|
||||
PCI_HOSE_OP(read, word, u16 *)
|
||||
PCI_HOSE_OP(read, dword, u32 *)
|
||||
PCI_HOSE_OP(write, byte, u8)
|
||||
PCI_HOSE_OP(write, word, u16)
|
||||
PCI_HOSE_OP(write, dword, u32)
|
||||
|
||||
#define PCI_OP(rw, size, type, error_code) \
|
||||
int pci_##rw##_config_##size(pci_dev_t dev, int offset, type value) \
|
||||
{ \
|
||||
struct pci_controller *hose = pci_bus_to_hose(PCI_BUS(dev)); \
|
||||
\
|
||||
if (!hose) \
|
||||
{ \
|
||||
error_code; \
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
return pci_hose_##rw##_config_##size(hose, dev, offset, value); \
|
||||
}
|
||||
|
||||
PCI_OP(read, byte, u8 *, *value = 0xff)
|
||||
PCI_OP(read, word, u16 *, *value = 0xffff)
|
||||
PCI_OP(read, dword, u32 *, *value = 0xffffffff)
|
||||
PCI_OP(write, byte, u8, )
|
||||
PCI_OP(write, word, u16, )
|
||||
PCI_OP(write, dword, u32, )
|
||||
|
||||
#define PCI_READ_VIA_DWORD_OP(size, type, off_mask) \
|
||||
int pci_hose_read_config_##size##_via_dword(struct pci_controller *hose,\
|
||||
pci_dev_t dev, \
|
||||
int offset, type val) \
|
||||
{ \
|
||||
u32 val32; \
|
||||
\
|
||||
if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0) { \
|
||||
*val = -1; \
|
||||
return -1; \
|
||||
} \
|
||||
\
|
||||
*val = (val32 >> ((offset & (int)off_mask) * 8)); \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
#define PCI_WRITE_VIA_DWORD_OP(size, type, off_mask, val_mask) \
|
||||
int pci_hose_write_config_##size##_via_dword(struct pci_controller *hose,\
|
||||
pci_dev_t dev, \
|
||||
int offset, type val) \
|
||||
{ \
|
||||
u32 val32, mask, ldata, shift; \
|
||||
\
|
||||
if (pci_hose_read_config_dword(hose, dev, offset & 0xfc, &val32) < 0)\
|
||||
return -1; \
|
||||
\
|
||||
shift = ((offset & (int)off_mask) * 8); \
|
||||
ldata = (((unsigned long)val) & val_mask) << shift; \
|
||||
mask = val_mask << shift; \
|
||||
val32 = (val32 & ~mask) | ldata; \
|
||||
\
|
||||
if (pci_hose_write_config_dword(hose, dev, offset & 0xfc, val32) < 0)\
|
||||
return -1; \
|
||||
\
|
||||
return 0; \
|
||||
}
|
||||
|
||||
PCI_READ_VIA_DWORD_OP(byte, u8 *, 0x03)
|
||||
PCI_READ_VIA_DWORD_OP(word, u16 *, 0x02)
|
||||
PCI_WRITE_VIA_DWORD_OP(byte, u8, 0x03, 0x000000ff)
|
||||
PCI_WRITE_VIA_DWORD_OP(word, u16, 0x02, 0x0000ffff)
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
static struct pci_controller* hose_head;
|
||||
|
||||
struct pci_controller *pci_get_hose_head(void)
|
||||
{
|
||||
if (gd->hose)
|
||||
return gd->hose;
|
||||
|
||||
return hose_head;
|
||||
}
|
||||
|
||||
void pci_register_hose(struct pci_controller* hose)
|
||||
{
|
||||
struct pci_controller **phose = &hose_head;
|
||||
|
||||
while(*phose)
|
||||
phose = &(*phose)->next;
|
||||
|
||||
hose->next = NULL;
|
||||
|
||||
*phose = hose;
|
||||
}
|
||||
|
||||
struct pci_controller *pci_bus_to_hose(int bus)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
|
||||
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
||||
if (bus >= hose->first_busno && bus <= hose->last_busno)
|
||||
return hose;
|
||||
}
|
||||
|
||||
printf("pci_bus_to_hose() failed\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct pci_controller *find_hose_by_cfg_addr(void *cfg_addr)
|
||||
{
|
||||
struct pci_controller *hose;
|
||||
|
||||
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
||||
if (hose->cfg_addr == cfg_addr)
|
||||
return hose;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int pci_last_busno(void)
|
||||
{
|
||||
struct pci_controller *hose = pci_get_hose_head();
|
||||
|
||||
if (!hose)
|
||||
return -1;
|
||||
|
||||
while (hose->next)
|
||||
hose = hose->next;
|
||||
|
||||
return hose->last_busno;
|
||||
}
|
||||
|
||||
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
|
||||
{
|
||||
struct pci_controller * hose;
|
||||
pci_dev_t bdf;
|
||||
int bus;
|
||||
|
||||
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
|
||||
for (bus = hose->first_busno; bus <= hose->last_busno; bus++) {
|
||||
bdf = pci_hose_find_devices(hose, bus, ids, &index);
|
||||
if (bdf != -1)
|
||||
return bdf;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int pci_hose_config_device(struct pci_controller *hose, pci_dev_t dev,
|
||||
ulong io, pci_addr_t mem, ulong command)
|
||||
{
|
||||
u32 bar_response;
|
||||
unsigned int old_command;
|
||||
pci_addr_t bar_value;
|
||||
pci_size_t bar_size;
|
||||
unsigned char pin;
|
||||
int bar, found_mem64;
|
||||
|
||||
debug("PCI Config: I/O=0x%lx, Memory=0x%llx, Command=0x%lx\n", io,
|
||||
(u64)mem, command);
|
||||
|
||||
pci_hose_write_config_dword(hose, dev, PCI_COMMAND, 0);
|
||||
|
||||
for (bar = PCI_BASE_ADDRESS_0; bar <= PCI_BASE_ADDRESS_5; bar += 4) {
|
||||
pci_hose_write_config_dword(hose, dev, bar, 0xffffffff);
|
||||
pci_hose_read_config_dword(hose, dev, bar, &bar_response);
|
||||
|
||||
if (!bar_response)
|
||||
continue;
|
||||
|
||||
found_mem64 = 0;
|
||||
|
||||
/* Check the BAR type and set our address mask */
|
||||
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
|
||||
bar_size = ~(bar_response & PCI_BASE_ADDRESS_IO_MASK) + 1;
|
||||
/* round up region base address to a multiple of size */
|
||||
io = ((io - 1) | (bar_size - 1)) + 1;
|
||||
bar_value = io;
|
||||
/* compute new region base address */
|
||||
io = io + bar_size;
|
||||
} else {
|
||||
if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
u32 bar_response_upper;
|
||||
u64 bar64;
|
||||
pci_hose_write_config_dword(hose, dev, bar + 4,
|
||||
0xffffffff);
|
||||
pci_hose_read_config_dword(hose, dev, bar + 4,
|
||||
&bar_response_upper);
|
||||
|
||||
bar64 = ((u64)bar_response_upper << 32) | bar_response;
|
||||
|
||||
bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1;
|
||||
found_mem64 = 1;
|
||||
} else {
|
||||
bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1);
|
||||
}
|
||||
|
||||
/* round up region base address to multiple of size */
|
||||
mem = ((mem - 1) | (bar_size - 1)) + 1;
|
||||
bar_value = mem;
|
||||
/* compute new region base address */
|
||||
mem = mem + bar_size;
|
||||
}
|
||||
|
||||
/* Write it out and update our limit */
|
||||
pci_hose_write_config_dword (hose, dev, bar, (u32)bar_value);
|
||||
|
||||
if (found_mem64) {
|
||||
bar += 4;
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
pci_hose_write_config_dword(hose, dev, bar,
|
||||
(u32)(bar_value >> 32));
|
||||
#else
|
||||
pci_hose_write_config_dword(hose, dev, bar, 0x00000000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
/* Configure Cache Line Size Register */
|
||||
pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE, 0x08);
|
||||
|
||||
/* Configure Latency Timer */
|
||||
pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
|
||||
|
||||
/* Disable interrupt line, if device says it wants to use interrupts */
|
||||
pci_hose_read_config_byte(hose, dev, PCI_INTERRUPT_PIN, &pin);
|
||||
if (pin != 0) {
|
||||
pci_hose_write_config_byte(hose, dev, PCI_INTERRUPT_LINE,
|
||||
PCI_INTERRUPT_LINE_DISABLE);
|
||||
}
|
||||
|
||||
pci_hose_read_config_dword(hose, dev, PCI_COMMAND, &old_command);
|
||||
pci_hose_write_config_dword(hose, dev, PCI_COMMAND,
|
||||
(old_command & 0xffff0000) | command);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
struct pci_config_table *pci_find_config(struct pci_controller *hose,
|
||||
unsigned short class,
|
||||
unsigned int vendor,
|
||||
unsigned int device,
|
||||
unsigned int bus,
|
||||
unsigned int dev,
|
||||
unsigned int func)
|
||||
{
|
||||
struct pci_config_table *table;
|
||||
|
||||
for (table = hose->config_table; table && table->vendor; table++) {
|
||||
if ((table->vendor == PCI_ANY_ID || table->vendor == vendor) &&
|
||||
(table->device == PCI_ANY_ID || table->device == device) &&
|
||||
(table->class == PCI_ANY_ID || table->class == class) &&
|
||||
(table->bus == PCI_ANY_ID || table->bus == bus) &&
|
||||
(table->dev == PCI_ANY_ID || table->dev == dev) &&
|
||||
(table->func == PCI_ANY_ID || table->func == func)) {
|
||||
return table;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void pci_cfgfunc_config_device(struct pci_controller *hose,
|
||||
pci_dev_t dev,
|
||||
struct pci_config_table *entry)
|
||||
{
|
||||
pci_hose_config_device(hose, dev, entry->priv[0], entry->priv[1],
|
||||
entry->priv[2]);
|
||||
}
|
||||
|
||||
void pci_cfgfunc_do_nothing(struct pci_controller *hose,
|
||||
pci_dev_t dev, struct pci_config_table *entry)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* HJF: Changed this to return int. I think this is required
|
||||
* to get the correct result when scanning bridges
|
||||
*/
|
||||
extern int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev);
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
__weak int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
|
||||
{
|
||||
if (dev == PCI_BDF(hose->first_busno, 0, 0))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_PCI_SCAN_SHOW */
|
||||
|
||||
int pci_hose_scan_bus(struct pci_controller *hose, int bus)
|
||||
{
|
||||
unsigned int sub_bus, found_multi = 0;
|
||||
unsigned short vendor, device, class;
|
||||
unsigned char header_type;
|
||||
#ifndef CONFIG_PCI_PNP
|
||||
struct pci_config_table *cfg;
|
||||
#endif
|
||||
pci_dev_t dev;
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
static int indent = 0;
|
||||
#endif
|
||||
|
||||
sub_bus = bus;
|
||||
|
||||
for (dev = PCI_BDF(bus,0,0);
|
||||
dev < PCI_BDF(bus, PCI_MAX_PCI_DEVICES - 1,
|
||||
PCI_MAX_PCI_FUNCTIONS - 1);
|
||||
dev += PCI_BDF(0, 0, 1)) {
|
||||
|
||||
if (pci_skip_dev(hose, dev))
|
||||
continue;
|
||||
|
||||
if (PCI_FUNC(dev) && !found_multi)
|
||||
continue;
|
||||
|
||||
pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &header_type);
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_VENDOR_ID, &vendor);
|
||||
|
||||
if (vendor == 0xffff || vendor == 0x0000)
|
||||
continue;
|
||||
|
||||
if (!PCI_FUNC(dev))
|
||||
found_multi = header_type & 0x80;
|
||||
|
||||
debug("PCI Scan: Found Bus %d, Device %d, Function %d\n",
|
||||
PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_DEVICE_ID, &device);
|
||||
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
|
||||
|
||||
#ifdef CONFIG_PCI_FIXUP_DEV
|
||||
board_pci_fixup_dev(hose, dev, vendor, device, class);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
indent++;
|
||||
|
||||
/* Print leading space, including bus indentation */
|
||||
printf("%*c", indent + 1, ' ');
|
||||
|
||||
if (pci_print_dev(hose, dev)) {
|
||||
printf("%02x:%02x.%-*x - %04x:%04x - %s\n",
|
||||
PCI_BUS(dev), PCI_DEV(dev), 6 - indent, PCI_FUNC(dev),
|
||||
vendor, device, pci_class_str(class >> 8));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_PNP
|
||||
sub_bus = max((unsigned int)pciauto_config_device(hose, dev),
|
||||
sub_bus);
|
||||
#else
|
||||
cfg = pci_find_config(hose, class, vendor, device,
|
||||
PCI_BUS(dev), PCI_DEV(dev), PCI_FUNC(dev));
|
||||
if (cfg) {
|
||||
cfg->config_device(hose, dev, cfg);
|
||||
sub_bus = max(sub_bus,
|
||||
(unsigned int)hose->current_busno);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
indent--;
|
||||
#endif
|
||||
|
||||
if (hose->fixup_irq)
|
||||
hose->fixup_irq(hose, dev);
|
||||
}
|
||||
|
||||
return sub_bus;
|
||||
}
|
||||
|
||||
int pci_hose_scan(struct pci_controller *hose)
|
||||
{
|
||||
#if defined(CONFIG_PCI_BOOTDELAY)
|
||||
char *s;
|
||||
int i;
|
||||
|
||||
if (!gd->pcidelay_done) {
|
||||
/* wait "pcidelay" ms (if defined)... */
|
||||
s = env_get("pcidelay");
|
||||
if (s) {
|
||||
int val = simple_strtoul(s, NULL, 10);
|
||||
for (i = 0; i < val; i++)
|
||||
udelay(1000);
|
||||
}
|
||||
gd->pcidelay_done = 1;
|
||||
}
|
||||
#endif /* CONFIG_PCI_BOOTDELAY */
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
puts("PCI:\n");
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Start scan at current_busno.
|
||||
* PCIe will start scan at first_busno+1.
|
||||
*/
|
||||
/* For legacy support, ensure current >= first */
|
||||
if (hose->first_busno > hose->current_busno)
|
||||
hose->current_busno = hose->first_busno;
|
||||
#ifdef CONFIG_PCI_PNP
|
||||
pciauto_config_init(hose);
|
||||
#endif
|
||||
return pci_hose_scan_bus(hose, hose->current_busno);
|
||||
}
|
||||
|
||||
void pci_init(void)
|
||||
{
|
||||
hose_head = NULL;
|
||||
|
||||
/* allow env to disable pci init/enum */
|
||||
if (env_get("pcidisable") != NULL)
|
||||
return;
|
||||
|
||||
/* now call board specific pci_init()... */
|
||||
pci_init_board();
|
||||
}
|
||||
|
||||
/* Returns the address of the requested capability structure within the
|
||||
* device's PCI configuration space or 0 in case the device does not
|
||||
* support it.
|
||||
* */
|
||||
int pci_hose_find_capability(struct pci_controller *hose, pci_dev_t dev,
|
||||
int cap)
|
||||
{
|
||||
int pos;
|
||||
u8 hdr_type;
|
||||
|
||||
pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &hdr_type);
|
||||
|
||||
pos = pci_hose_find_cap_start(hose, dev, hdr_type & 0x7F);
|
||||
|
||||
if (pos)
|
||||
pos = pci_find_cap(hose, dev, pos, cap);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Find the header pointer to the Capabilities*/
|
||||
int pci_hose_find_cap_start(struct pci_controller *hose, pci_dev_t dev,
|
||||
u8 hdr_type)
|
||||
{
|
||||
u16 status;
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_STATUS, &status);
|
||||
|
||||
if (!(status & PCI_STATUS_CAP_LIST))
|
||||
return 0;
|
||||
|
||||
switch (hdr_type) {
|
||||
case PCI_HEADER_TYPE_NORMAL:
|
||||
case PCI_HEADER_TYPE_BRIDGE:
|
||||
return PCI_CAPABILITY_LIST;
|
||||
case PCI_HEADER_TYPE_CARDBUS:
|
||||
return PCI_CB_CAPABILITY_LIST;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int pci_find_cap(struct pci_controller *hose, pci_dev_t dev, int pos, int cap)
|
||||
{
|
||||
int ttl = PCI_FIND_CAP_TTL;
|
||||
u8 id;
|
||||
u8 next_pos;
|
||||
|
||||
while (ttl--) {
|
||||
pci_hose_read_config_byte(hose, dev, pos, &next_pos);
|
||||
if (next_pos < CAP_START_POS)
|
||||
break;
|
||||
next_pos &= ~3;
|
||||
pos = (int) next_pos;
|
||||
pci_hose_read_config_byte(hose, dev,
|
||||
pos + PCI_CAP_LIST_ID, &id);
|
||||
if (id == 0xff)
|
||||
break;
|
||||
if (id == cap)
|
||||
return pos;
|
||||
pos += PCI_CAP_LIST_NEXT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_find_next_ext_capability - Find an extended capability
|
||||
*
|
||||
* Returns the address of the next matching extended capability structure
|
||||
* within the device's PCI configuration space or 0 if the device does
|
||||
* not support it. Some capabilities can occur several times, e.g., the
|
||||
* vendor-specific capability, and this provides a way to find them all.
|
||||
*/
|
||||
int pci_find_next_ext_capability(struct pci_controller *hose, pci_dev_t dev,
|
||||
int start, int cap)
|
||||
{
|
||||
u32 header;
|
||||
int ttl, pos = PCI_CFG_SPACE_SIZE;
|
||||
|
||||
/* minimum 8 bytes per capability */
|
||||
ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8;
|
||||
|
||||
if (start)
|
||||
pos = start;
|
||||
|
||||
pci_hose_read_config_dword(hose, dev, pos, &header);
|
||||
if (header == 0xffffffff || header == 0)
|
||||
return 0;
|
||||
|
||||
while (ttl-- > 0) {
|
||||
if (PCI_EXT_CAP_ID(header) == cap && pos != start)
|
||||
return pos;
|
||||
|
||||
pos = PCI_EXT_CAP_NEXT(header);
|
||||
if (pos < PCI_CFG_SPACE_SIZE)
|
||||
break;
|
||||
|
||||
pci_hose_read_config_dword(hose, dev, pos, &header);
|
||||
if (header == 0xffffffff || header == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_hose_find_ext_capability - Find an extended capability
|
||||
*
|
||||
* Returns the address of the requested extended capability structure
|
||||
* within the device's PCI configuration space or 0 if the device does
|
||||
* not support it.
|
||||
*/
|
||||
int pci_hose_find_ext_capability(struct pci_controller *hose, pci_dev_t dev,
|
||||
int cap)
|
||||
{
|
||||
return pci_find_next_ext_capability(hose, dev, 0, cap);
|
||||
}
|
||||
@@ -0,0 +1,395 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PCI autoconfiguration library
|
||||
*
|
||||
* Author: Matt Porter <mporter@mvista.com>
|
||||
*
|
||||
* Copyright 2000 MontaVista Software Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
|
||||
/* the user can define CONFIG_SYS_PCI_CACHE_LINE_SIZE to avoid problems */
|
||||
#ifndef CONFIG_SYS_PCI_CACHE_LINE_SIZE
|
||||
#define CONFIG_SYS_PCI_CACHE_LINE_SIZE 8
|
||||
#endif
|
||||
|
||||
void dm_pciauto_setup_device(struct udevice *dev, int bars_num,
|
||||
struct pci_region *mem,
|
||||
struct pci_region *prefetch, struct pci_region *io,
|
||||
bool enum_only)
|
||||
{
|
||||
u32 bar_response;
|
||||
pci_size_t bar_size;
|
||||
u16 cmdstat = 0;
|
||||
int bar, bar_nr = 0;
|
||||
u8 header_type;
|
||||
int rom_addr;
|
||||
pci_addr_t bar_value;
|
||||
struct pci_region *bar_res = NULL;
|
||||
int found_mem64 = 0;
|
||||
u16 class;
|
||||
|
||||
dm_pci_read_config16(dev, PCI_COMMAND, &cmdstat);
|
||||
cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) |
|
||||
PCI_COMMAND_MASTER;
|
||||
|
||||
for (bar = PCI_BASE_ADDRESS_0;
|
||||
bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) {
|
||||
int ret = 0;
|
||||
|
||||
/* Tickle the BAR and get the response */
|
||||
if (!enum_only)
|
||||
dm_pci_write_config32(dev, bar, 0xffffffff);
|
||||
dm_pci_read_config32(dev, bar, &bar_response);
|
||||
|
||||
/* If BAR is not implemented go to the next BAR */
|
||||
if (!bar_response)
|
||||
continue;
|
||||
|
||||
found_mem64 = 0;
|
||||
|
||||
/* Check the BAR type and set our address mask */
|
||||
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
|
||||
bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK))
|
||||
& 0xffff) + 1;
|
||||
if (!enum_only)
|
||||
bar_res = io;
|
||||
|
||||
debug("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ",
|
||||
bar_nr, (unsigned long long)bar_size);
|
||||
} else {
|
||||
if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
u32 bar_response_upper;
|
||||
u64 bar64;
|
||||
|
||||
if (!enum_only) {
|
||||
dm_pci_write_config32(dev, bar + 4,
|
||||
0xffffffff);
|
||||
}
|
||||
dm_pci_read_config32(dev, bar + 4,
|
||||
&bar_response_upper);
|
||||
|
||||
bar64 = ((u64)bar_response_upper << 32) |
|
||||
bar_response;
|
||||
|
||||
bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK)
|
||||
+ 1;
|
||||
if (!enum_only)
|
||||
found_mem64 = 1;
|
||||
} else {
|
||||
bar_size = (u32)(~(bar_response &
|
||||
PCI_BASE_ADDRESS_MEM_MASK) + 1);
|
||||
}
|
||||
if (!enum_only) {
|
||||
if (prefetch && (bar_response &
|
||||
PCI_BASE_ADDRESS_MEM_PREFETCH)) {
|
||||
bar_res = prefetch;
|
||||
} else {
|
||||
bar_res = mem;
|
||||
}
|
||||
}
|
||||
|
||||
debug("PCI Autoconfig: BAR %d, %s, size=0x%llx, ",
|
||||
bar_nr, bar_res == prefetch ? "Prf" : "Mem",
|
||||
(unsigned long long)bar_size);
|
||||
}
|
||||
|
||||
if (!enum_only) {
|
||||
ret = pciauto_region_allocate(bar_res, bar_size,
|
||||
&bar_value, found_mem64);
|
||||
if (ret)
|
||||
printf("PCI: Failed autoconfig bar %x\n", bar);
|
||||
}
|
||||
if (!enum_only && !ret) {
|
||||
/* Write it out and update our limit */
|
||||
dm_pci_write_config32(dev, bar, (u32)bar_value);
|
||||
|
||||
if (found_mem64) {
|
||||
bar += 4;
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
dm_pci_write_config32(dev, bar,
|
||||
(u32)(bar_value >> 32));
|
||||
#else
|
||||
/*
|
||||
* If we are a 64-bit decoder then increment to
|
||||
* the upper 32 bits of the bar and force it to
|
||||
* locate in the lower 4GB of memory.
|
||||
*/
|
||||
dm_pci_write_config32(dev, bar, 0x00000000);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ?
|
||||
PCI_COMMAND_IO : PCI_COMMAND_MEMORY;
|
||||
|
||||
debug("\n");
|
||||
|
||||
bar_nr++;
|
||||
}
|
||||
|
||||
if (!enum_only) {
|
||||
/* Configure the expansion ROM address */
|
||||
dm_pci_read_config8(dev, PCI_HEADER_TYPE, &header_type);
|
||||
header_type &= 0x7f;
|
||||
if (header_type != PCI_HEADER_TYPE_CARDBUS) {
|
||||
rom_addr = (header_type == PCI_HEADER_TYPE_NORMAL) ?
|
||||
PCI_ROM_ADDRESS : PCI_ROM_ADDRESS1;
|
||||
dm_pci_write_config32(dev, rom_addr, 0xfffffffe);
|
||||
dm_pci_read_config32(dev, rom_addr, &bar_response);
|
||||
if (bar_response) {
|
||||
bar_size = -(bar_response & ~1);
|
||||
debug("PCI Autoconfig: ROM, size=%#x, ",
|
||||
(unsigned int)bar_size);
|
||||
if (pciauto_region_allocate(mem, bar_size,
|
||||
&bar_value,
|
||||
false) == 0) {
|
||||
dm_pci_write_config32(dev, rom_addr,
|
||||
bar_value);
|
||||
}
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* PCI_COMMAND_IO must be set for VGA device */
|
||||
dm_pci_read_config16(dev, PCI_CLASS_DEVICE, &class);
|
||||
if (class == PCI_CLASS_DISPLAY_VGA)
|
||||
cmdstat |= PCI_COMMAND_IO;
|
||||
|
||||
dm_pci_write_config16(dev, PCI_COMMAND, cmdstat);
|
||||
dm_pci_write_config8(dev, PCI_CACHE_LINE_SIZE,
|
||||
CONFIG_SYS_PCI_CACHE_LINE_SIZE);
|
||||
dm_pci_write_config8(dev, PCI_LATENCY_TIMER, 0x80);
|
||||
}
|
||||
|
||||
void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
u16 cmdstat, prefechable_64;
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *ctlr_hose = dev_get_uclass_priv(ctlr);
|
||||
|
||||
pci_mem = ctlr_hose->pci_mem;
|
||||
pci_prefetch = ctlr_hose->pci_prefetch;
|
||||
pci_io = ctlr_hose->pci_io;
|
||||
|
||||
dm_pci_read_config16(dev, PCI_COMMAND, &cmdstat);
|
||||
dm_pci_read_config16(dev, PCI_PREF_MEMORY_BASE, &prefechable_64);
|
||||
prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK;
|
||||
|
||||
/* Configure bus number registers */
|
||||
dm_pci_write_config8(dev, PCI_PRIMARY_BUS,
|
||||
PCI_BUS(dm_pci_get_bdf(dev)) - ctlr->seq);
|
||||
dm_pci_write_config8(dev, PCI_SECONDARY_BUS, sub_bus - ctlr->seq);
|
||||
dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, 0xff);
|
||||
|
||||
if (pci_mem) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_mem, 0x100000);
|
||||
|
||||
/*
|
||||
* Set up memory and I/O filter limits, assume 32-bit
|
||||
* I/O space
|
||||
*/
|
||||
dm_pci_write_config16(dev, PCI_MEMORY_BASE,
|
||||
(pci_mem->bus_lower & 0xfff00000) >> 16);
|
||||
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
|
||||
if (pci_prefetch) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_prefetch, 0x100000);
|
||||
|
||||
/*
|
||||
* Set up memory and I/O filter limits, assume 32-bit
|
||||
* I/O space
|
||||
*/
|
||||
dm_pci_write_config16(dev, PCI_PREF_MEMORY_BASE,
|
||||
(pci_prefetch->bus_lower & 0xfff00000) >> 16);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64)
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
dm_pci_write_config32(dev, PCI_PREF_BASE_UPPER32,
|
||||
pci_prefetch->bus_lower >> 32);
|
||||
#else
|
||||
dm_pci_write_config32(dev, PCI_PREF_BASE_UPPER32, 0x0);
|
||||
#endif
|
||||
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
} else {
|
||||
/* We don't support prefetchable memory for now, so disable */
|
||||
dm_pci_write_config16(dev, PCI_PREF_MEMORY_BASE, 0x1000);
|
||||
dm_pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT, 0x0);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64) {
|
||||
dm_pci_write_config16(dev, PCI_PREF_BASE_UPPER32, 0x0);
|
||||
dm_pci_write_config16(dev, PCI_PREF_LIMIT_UPPER32, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
if (pci_io) {
|
||||
/* Round I/O allocator to 4KB boundary */
|
||||
pciauto_region_align(pci_io, 0x1000);
|
||||
|
||||
dm_pci_write_config8(dev, PCI_IO_BASE,
|
||||
(pci_io->bus_lower & 0x0000f000) >> 8);
|
||||
dm_pci_write_config16(dev, PCI_IO_BASE_UPPER16,
|
||||
(pci_io->bus_lower & 0xffff0000) >> 16);
|
||||
|
||||
cmdstat |= PCI_COMMAND_IO;
|
||||
}
|
||||
|
||||
/* Enable memory and I/O accesses, enable bus master */
|
||||
dm_pci_write_config16(dev, PCI_COMMAND, cmdstat | PCI_COMMAND_MASTER);
|
||||
}
|
||||
|
||||
void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *ctlr_hose = dev_get_uclass_priv(ctlr);
|
||||
|
||||
pci_mem = ctlr_hose->pci_mem;
|
||||
pci_prefetch = ctlr_hose->pci_prefetch;
|
||||
pci_io = ctlr_hose->pci_io;
|
||||
|
||||
/* Configure bus number registers */
|
||||
dm_pci_write_config8(dev, PCI_SUBORDINATE_BUS, sub_bus - ctlr->seq);
|
||||
|
||||
if (pci_mem) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_mem, 0x100000);
|
||||
|
||||
dm_pci_write_config16(dev, PCI_MEMORY_LIMIT,
|
||||
(pci_mem->bus_lower - 1) >> 16);
|
||||
}
|
||||
|
||||
if (pci_prefetch) {
|
||||
u16 prefechable_64;
|
||||
|
||||
dm_pci_read_config16(dev, PCI_PREF_MEMORY_LIMIT,
|
||||
&prefechable_64);
|
||||
prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK;
|
||||
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_prefetch, 0x100000);
|
||||
|
||||
dm_pci_write_config16(dev, PCI_PREF_MEMORY_LIMIT,
|
||||
(pci_prefetch->bus_lower - 1) >> 16);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64)
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
dm_pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32,
|
||||
(pci_prefetch->bus_lower - 1) >> 32);
|
||||
#else
|
||||
dm_pci_write_config32(dev, PCI_PREF_LIMIT_UPPER32, 0x0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (pci_io) {
|
||||
/* Round I/O allocator to 4KB boundary */
|
||||
pciauto_region_align(pci_io, 0x1000);
|
||||
|
||||
dm_pci_write_config8(dev, PCI_IO_LIMIT,
|
||||
((pci_io->bus_lower - 1) & 0x0000f000) >> 8);
|
||||
dm_pci_write_config16(dev, PCI_IO_LIMIT_UPPER16,
|
||||
((pci_io->bus_lower - 1) & 0xffff0000) >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* HJF: Changed this to return int. I think this is required
|
||||
* to get the correct result when scanning bridges
|
||||
*/
|
||||
int dm_pciauto_config_device(struct udevice *dev)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
unsigned int sub_bus = PCI_BUS(dm_pci_get_bdf(dev));
|
||||
unsigned short class;
|
||||
bool enum_only = false;
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *ctlr_hose = dev_get_uclass_priv(ctlr);
|
||||
int n;
|
||||
|
||||
#ifdef CONFIG_PCI_ENUM_ONLY
|
||||
enum_only = true;
|
||||
#endif
|
||||
|
||||
pci_mem = ctlr_hose->pci_mem;
|
||||
pci_prefetch = ctlr_hose->pci_prefetch;
|
||||
pci_io = ctlr_hose->pci_io;
|
||||
|
||||
dm_pci_read_config16(dev, PCI_CLASS_DEVICE, &class);
|
||||
|
||||
switch (class) {
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
debug("PCI Autoconfig: Found P2P bridge, device %d\n",
|
||||
PCI_DEV(dm_pci_get_bdf(dev)));
|
||||
|
||||
dm_pciauto_setup_device(dev, 2, pci_mem, pci_prefetch, pci_io,
|
||||
enum_only);
|
||||
|
||||
n = dm_pci_hose_probe_bus(dev);
|
||||
if (n < 0)
|
||||
return n;
|
||||
sub_bus = (unsigned int)n;
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
/*
|
||||
* just do a minimal setup of the bridge,
|
||||
* let the OS take care of the rest
|
||||
*/
|
||||
dm_pciauto_setup_device(dev, 0, pci_mem, pci_prefetch, pci_io,
|
||||
enum_only);
|
||||
|
||||
debug("PCI Autoconfig: Found P2CardBus bridge, device %d\n",
|
||||
PCI_DEV(dm_pci_get_bdf(dev)));
|
||||
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_PCIAUTO_SKIP_HOST_BRIDGE)
|
||||
case PCI_CLASS_BRIDGE_OTHER:
|
||||
debug("PCI Autoconfig: Skipping bridge device %d\n",
|
||||
PCI_DEV(dm_pci_get_bdf(dev)));
|
||||
break;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_MPC834X) && !defined(CONFIG_TARGET_VME8349) && \
|
||||
!defined(CONFIG_TARGET_CADDY2)
|
||||
case PCI_CLASS_BRIDGE_OTHER:
|
||||
/*
|
||||
* The host/PCI bridge 1 seems broken in 8349 - it presents
|
||||
* itself as 'PCI_CLASS_BRIDGE_OTHER' and appears as an _agent_
|
||||
* device claiming resources io/mem/irq.. we only allow for
|
||||
* the PIMMR window to be allocated (BAR0 - 1MB size)
|
||||
*/
|
||||
debug("PCI Autoconfig: Broken bridge found, only minimal config\n");
|
||||
dm_pciauto_setup_device(dev, 0, hose->pci_mem,
|
||||
hose->pci_prefetch, hose->pci_io,
|
||||
enum_only);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */
|
||||
debug("PCI AutoConfig: Found PowerPC device\n");
|
||||
/* fall through */
|
||||
|
||||
default:
|
||||
dm_pciauto_setup_device(dev, 6, pci_mem, pci_prefetch, pci_io,
|
||||
enum_only);
|
||||
break;
|
||||
}
|
||||
|
||||
return sub_bus;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PCI auto-configuration library
|
||||
*
|
||||
* Author: Matt Porter <mporter@mvista.com>
|
||||
*
|
||||
* Copyright 2000 MontaVista Software Inc.
|
||||
*
|
||||
* Modifications for driver model:
|
||||
* Copyright 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
|
||||
void pciauto_region_init(struct pci_region *res)
|
||||
{
|
||||
/*
|
||||
* Avoid allocating PCI resources from address 0 -- this is illegal
|
||||
* according to PCI 2.1 and moreover, this is known to cause Linux IDE
|
||||
* drivers to fail. Use a reasonable starting value of 0x1000 instead
|
||||
* if the bus start address is below 0x1000.
|
||||
*/
|
||||
res->bus_lower = res->bus_start < 0x1000 ? 0x1000 : res->bus_start;
|
||||
}
|
||||
|
||||
void pciauto_region_align(struct pci_region *res, pci_size_t size)
|
||||
{
|
||||
res->bus_lower = ((res->bus_lower - 1) | (size - 1)) + 1;
|
||||
}
|
||||
|
||||
int pciauto_region_allocate(struct pci_region *res, pci_size_t size,
|
||||
pci_addr_t *bar, bool supports_64bit)
|
||||
{
|
||||
pci_addr_t addr;
|
||||
|
||||
if (!res) {
|
||||
debug("No resource\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
addr = ((res->bus_lower - 1) | (size - 1)) + 1;
|
||||
|
||||
if (addr - res->bus_start + size > res->size) {
|
||||
debug("No room in resource, avail start=%llx / size=%llx, "
|
||||
"need=%llx\n", (unsigned long long)res->bus_lower,
|
||||
(unsigned long long)res->size, (unsigned long long)size);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (upper_32_bits(addr) && !supports_64bit) {
|
||||
debug("Cannot assign 64-bit address to 32-bit-only resource\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
res->bus_lower = addr + size;
|
||||
|
||||
debug("address=0x%llx bus_lower=0x%llx\n", (unsigned long long)addr,
|
||||
(unsigned long long)res->bus_lower);
|
||||
|
||||
*bar = addr;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
*bar = (pci_addr_t)-1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void pciauto_show_region(const char *name, struct pci_region *region)
|
||||
{
|
||||
pciauto_region_init(region);
|
||||
debug("PCI Autoconfig: Bus %s region: [%llx-%llx],\n"
|
||||
"\t\tPhysical Memory [%llx-%llxx]\n", name,
|
||||
(unsigned long long)region->bus_start,
|
||||
(unsigned long long)(region->bus_start + region->size - 1),
|
||||
(unsigned long long)region->phys_start,
|
||||
(unsigned long long)(region->phys_start + region->size - 1));
|
||||
}
|
||||
|
||||
void pciauto_config_init(struct pci_controller *hose)
|
||||
{
|
||||
int i;
|
||||
|
||||
hose->pci_io = NULL;
|
||||
hose->pci_mem = NULL;
|
||||
hose->pci_prefetch = NULL;
|
||||
|
||||
for (i = 0; i < hose->region_count; i++) {
|
||||
switch (hose->regions[i].flags) {
|
||||
case PCI_REGION_IO:
|
||||
if (!hose->pci_io ||
|
||||
hose->pci_io->size < hose->regions[i].size)
|
||||
hose->pci_io = hose->regions + i;
|
||||
break;
|
||||
case PCI_REGION_MEM:
|
||||
if (!hose->pci_mem ||
|
||||
hose->pci_mem->size < hose->regions[i].size)
|
||||
hose->pci_mem = hose->regions + i;
|
||||
break;
|
||||
case (PCI_REGION_MEM | PCI_REGION_PREFETCH):
|
||||
if (!hose->pci_prefetch ||
|
||||
hose->pci_prefetch->size < hose->regions[i].size)
|
||||
hose->pci_prefetch = hose->regions + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (hose->pci_mem)
|
||||
pciauto_show_region("Memory", hose->pci_mem);
|
||||
if (hose->pci_prefetch)
|
||||
pciauto_show_region("Prefetchable Mem", hose->pci_prefetch);
|
||||
if (hose->pci_io)
|
||||
pciauto_show_region("I/O", hose->pci_io);
|
||||
}
|
||||
@@ -0,0 +1,404 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* PCI autoconfiguration library (legacy version, do not change)
|
||||
*
|
||||
* Author: Matt Porter <mporter@mvista.com>
|
||||
*
|
||||
* Copyright 2000 MontaVista Software Inc.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
|
||||
/*
|
||||
* Do not change this file. Instead, convert your board to use CONFIG_DM_PCI
|
||||
* and change pci_auto.c.
|
||||
*/
|
||||
|
||||
/* the user can define CONFIG_SYS_PCI_CACHE_LINE_SIZE to avoid problems */
|
||||
#ifndef CONFIG_SYS_PCI_CACHE_LINE_SIZE
|
||||
#define CONFIG_SYS_PCI_CACHE_LINE_SIZE 8
|
||||
#endif
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
|
||||
void pciauto_setup_device(struct pci_controller *hose,
|
||||
pci_dev_t dev, int bars_num,
|
||||
struct pci_region *mem,
|
||||
struct pci_region *prefetch,
|
||||
struct pci_region *io)
|
||||
{
|
||||
u32 bar_response;
|
||||
pci_size_t bar_size;
|
||||
u16 cmdstat = 0;
|
||||
int bar, bar_nr = 0;
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
u8 header_type;
|
||||
int rom_addr;
|
||||
pci_addr_t bar_value;
|
||||
struct pci_region *bar_res;
|
||||
int found_mem64 = 0;
|
||||
#endif
|
||||
u16 class;
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);
|
||||
cmdstat = (cmdstat & ~(PCI_COMMAND_IO | PCI_COMMAND_MEMORY)) | PCI_COMMAND_MASTER;
|
||||
|
||||
for (bar = PCI_BASE_ADDRESS_0;
|
||||
bar < PCI_BASE_ADDRESS_0 + (bars_num * 4); bar += 4) {
|
||||
/* Tickle the BAR and get the response */
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
pci_hose_write_config_dword(hose, dev, bar, 0xffffffff);
|
||||
#endif
|
||||
pci_hose_read_config_dword(hose, dev, bar, &bar_response);
|
||||
|
||||
/* If BAR is not implemented go to the next BAR */
|
||||
if (!bar_response)
|
||||
continue;
|
||||
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
found_mem64 = 0;
|
||||
#endif
|
||||
|
||||
/* Check the BAR type and set our address mask */
|
||||
if (bar_response & PCI_BASE_ADDRESS_SPACE) {
|
||||
bar_size = ((~(bar_response & PCI_BASE_ADDRESS_IO_MASK))
|
||||
& 0xffff) + 1;
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
bar_res = io;
|
||||
#endif
|
||||
|
||||
debug("PCI Autoconfig: BAR %d, I/O, size=0x%llx, ",
|
||||
bar_nr, (unsigned long long)bar_size);
|
||||
} else {
|
||||
if ((bar_response & PCI_BASE_ADDRESS_MEM_TYPE_MASK) ==
|
||||
PCI_BASE_ADDRESS_MEM_TYPE_64) {
|
||||
u32 bar_response_upper;
|
||||
u64 bar64;
|
||||
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
pci_hose_write_config_dword(hose, dev, bar + 4,
|
||||
0xffffffff);
|
||||
#endif
|
||||
pci_hose_read_config_dword(hose, dev, bar + 4,
|
||||
&bar_response_upper);
|
||||
|
||||
bar64 = ((u64)bar_response_upper << 32) | bar_response;
|
||||
|
||||
bar_size = ~(bar64 & PCI_BASE_ADDRESS_MEM_MASK) + 1;
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
found_mem64 = 1;
|
||||
#endif
|
||||
} else {
|
||||
bar_size = (u32)(~(bar_response & PCI_BASE_ADDRESS_MEM_MASK) + 1);
|
||||
}
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
if (prefetch && (bar_response & PCI_BASE_ADDRESS_MEM_PREFETCH))
|
||||
bar_res = prefetch;
|
||||
else
|
||||
bar_res = mem;
|
||||
|
||||
debug("PCI Autoconfig: BAR %d, %s, size=0x%llx, ",
|
||||
bar_nr, bar_res == prefetch ? "Prf" : "Mem",
|
||||
(unsigned long long)bar_size);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
if (pciauto_region_allocate(bar_res, bar_size,
|
||||
&bar_value, found_mem64) == 0) {
|
||||
/* Write it out and update our limit */
|
||||
pci_hose_write_config_dword(hose, dev, bar, (u32)bar_value);
|
||||
|
||||
if (found_mem64) {
|
||||
bar += 4;
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
pci_hose_write_config_dword(hose, dev, bar, (u32)(bar_value>>32));
|
||||
#else
|
||||
/*
|
||||
* If we are a 64-bit decoder then increment to the
|
||||
* upper 32 bits of the bar and force it to locate
|
||||
* in the lower 4GB of memory.
|
||||
*/
|
||||
pci_hose_write_config_dword(hose, dev, bar, 0x00000000);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
cmdstat |= (bar_response & PCI_BASE_ADDRESS_SPACE) ?
|
||||
PCI_COMMAND_IO : PCI_COMMAND_MEMORY;
|
||||
|
||||
debug("\n");
|
||||
|
||||
bar_nr++;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PCI_ENUM_ONLY
|
||||
/* Configure the expansion ROM address */
|
||||
pci_hose_read_config_byte(hose, dev, PCI_HEADER_TYPE, &header_type);
|
||||
header_type &= 0x7f;
|
||||
if (header_type != PCI_HEADER_TYPE_CARDBUS) {
|
||||
rom_addr = (header_type == PCI_HEADER_TYPE_NORMAL) ?
|
||||
PCI_ROM_ADDRESS : PCI_ROM_ADDRESS1;
|
||||
pci_hose_write_config_dword(hose, dev, rom_addr, 0xfffffffe);
|
||||
pci_hose_read_config_dword(hose, dev, rom_addr, &bar_response);
|
||||
if (bar_response) {
|
||||
bar_size = -(bar_response & ~1);
|
||||
debug("PCI Autoconfig: ROM, size=%#x, ",
|
||||
(unsigned int)bar_size);
|
||||
if (pciauto_region_allocate(mem, bar_size,
|
||||
&bar_value, false) == 0) {
|
||||
pci_hose_write_config_dword(hose, dev, rom_addr,
|
||||
bar_value);
|
||||
}
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
debug("\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* PCI_COMMAND_IO must be set for VGA device */
|
||||
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
|
||||
if (class == PCI_CLASS_DISPLAY_VGA)
|
||||
cmdstat |= PCI_COMMAND_IO;
|
||||
|
||||
pci_hose_write_config_word(hose, dev, PCI_COMMAND, cmdstat);
|
||||
pci_hose_write_config_byte(hose, dev, PCI_CACHE_LINE_SIZE,
|
||||
CONFIG_SYS_PCI_CACHE_LINE_SIZE);
|
||||
pci_hose_write_config_byte(hose, dev, PCI_LATENCY_TIMER, 0x80);
|
||||
}
|
||||
|
||||
void pciauto_prescan_setup_bridge(struct pci_controller *hose,
|
||||
pci_dev_t dev, int sub_bus)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
u16 cmdstat, prefechable_64;
|
||||
|
||||
pci_mem = hose->pci_mem;
|
||||
pci_prefetch = hose->pci_prefetch;
|
||||
pci_io = hose->pci_io;
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_COMMAND, &cmdstat);
|
||||
pci_hose_read_config_word(hose, dev, PCI_PREF_MEMORY_BASE,
|
||||
&prefechable_64);
|
||||
prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK;
|
||||
|
||||
/* Configure bus number registers */
|
||||
pci_hose_write_config_byte(hose, dev, PCI_PRIMARY_BUS,
|
||||
PCI_BUS(dev) - hose->first_busno);
|
||||
pci_hose_write_config_byte(hose, dev, PCI_SECONDARY_BUS,
|
||||
sub_bus - hose->first_busno);
|
||||
pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS, 0xff);
|
||||
|
||||
if (pci_mem) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_mem, 0x100000);
|
||||
|
||||
/* Set up memory and I/O filter limits, assume 32-bit I/O space */
|
||||
pci_hose_write_config_word(hose, dev, PCI_MEMORY_BASE,
|
||||
(pci_mem->bus_lower & 0xfff00000) >> 16);
|
||||
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
}
|
||||
|
||||
if (pci_prefetch) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_prefetch, 0x100000);
|
||||
|
||||
/* Set up memory and I/O filter limits, assume 32-bit I/O space */
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE,
|
||||
(pci_prefetch->bus_lower & 0xfff00000) >> 16);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64)
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
pci_hose_write_config_dword(hose, dev,
|
||||
PCI_PREF_BASE_UPPER32,
|
||||
pci_prefetch->bus_lower >> 32);
|
||||
#else
|
||||
pci_hose_write_config_dword(hose, dev,
|
||||
PCI_PREF_BASE_UPPER32,
|
||||
0x0);
|
||||
#endif
|
||||
|
||||
cmdstat |= PCI_COMMAND_MEMORY;
|
||||
} else {
|
||||
/* We don't support prefetchable memory for now, so disable */
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_BASE, 0x1000);
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT, 0x0);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64) {
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_BASE_UPPER32, 0x0);
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_LIMIT_UPPER32, 0x0);
|
||||
}
|
||||
}
|
||||
|
||||
if (pci_io) {
|
||||
/* Round I/O allocator to 4KB boundary */
|
||||
pciauto_region_align(pci_io, 0x1000);
|
||||
|
||||
pci_hose_write_config_byte(hose, dev, PCI_IO_BASE,
|
||||
(pci_io->bus_lower & 0x0000f000) >> 8);
|
||||
pci_hose_write_config_word(hose, dev, PCI_IO_BASE_UPPER16,
|
||||
(pci_io->bus_lower & 0xffff0000) >> 16);
|
||||
|
||||
cmdstat |= PCI_COMMAND_IO;
|
||||
}
|
||||
|
||||
/* Enable memory and I/O accesses, enable bus master */
|
||||
pci_hose_write_config_word(hose, dev, PCI_COMMAND,
|
||||
cmdstat | PCI_COMMAND_MASTER);
|
||||
}
|
||||
|
||||
void pciauto_postscan_setup_bridge(struct pci_controller *hose,
|
||||
pci_dev_t dev, int sub_bus)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
|
||||
pci_mem = hose->pci_mem;
|
||||
pci_prefetch = hose->pci_prefetch;
|
||||
pci_io = hose->pci_io;
|
||||
|
||||
/* Configure bus number registers */
|
||||
pci_hose_write_config_byte(hose, dev, PCI_SUBORDINATE_BUS,
|
||||
sub_bus - hose->first_busno);
|
||||
|
||||
if (pci_mem) {
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_mem, 0x100000);
|
||||
|
||||
pci_hose_write_config_word(hose, dev, PCI_MEMORY_LIMIT,
|
||||
(pci_mem->bus_lower - 1) >> 16);
|
||||
}
|
||||
|
||||
if (pci_prefetch) {
|
||||
u16 prefechable_64;
|
||||
|
||||
pci_hose_read_config_word(hose, dev,
|
||||
PCI_PREF_MEMORY_LIMIT,
|
||||
&prefechable_64);
|
||||
prefechable_64 &= PCI_PREF_RANGE_TYPE_MASK;
|
||||
|
||||
/* Round memory allocator to 1MB boundary */
|
||||
pciauto_region_align(pci_prefetch, 0x100000);
|
||||
|
||||
pci_hose_write_config_word(hose, dev, PCI_PREF_MEMORY_LIMIT,
|
||||
(pci_prefetch->bus_lower - 1) >> 16);
|
||||
if (prefechable_64 == PCI_PREF_RANGE_TYPE_64)
|
||||
#ifdef CONFIG_SYS_PCI_64BIT
|
||||
pci_hose_write_config_dword(hose, dev,
|
||||
PCI_PREF_LIMIT_UPPER32,
|
||||
(pci_prefetch->bus_lower - 1) >> 32);
|
||||
#else
|
||||
pci_hose_write_config_dword(hose, dev,
|
||||
PCI_PREF_LIMIT_UPPER32,
|
||||
0x0);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (pci_io) {
|
||||
/* Round I/O allocator to 4KB boundary */
|
||||
pciauto_region_align(pci_io, 0x1000);
|
||||
|
||||
pci_hose_write_config_byte(hose, dev, PCI_IO_LIMIT,
|
||||
((pci_io->bus_lower - 1) & 0x0000f000) >> 8);
|
||||
pci_hose_write_config_word(hose, dev, PCI_IO_LIMIT_UPPER16,
|
||||
((pci_io->bus_lower - 1) & 0xffff0000) >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HJF: Changed this to return int. I think this is required
|
||||
* to get the correct result when scanning bridges
|
||||
*/
|
||||
int pciauto_config_device(struct pci_controller *hose, pci_dev_t dev)
|
||||
{
|
||||
struct pci_region *pci_mem;
|
||||
struct pci_region *pci_prefetch;
|
||||
struct pci_region *pci_io;
|
||||
unsigned int sub_bus = PCI_BUS(dev);
|
||||
unsigned short class;
|
||||
int n;
|
||||
|
||||
pci_mem = hose->pci_mem;
|
||||
pci_prefetch = hose->pci_prefetch;
|
||||
pci_io = hose->pci_io;
|
||||
|
||||
pci_hose_read_config_word(hose, dev, PCI_CLASS_DEVICE, &class);
|
||||
|
||||
switch (class) {
|
||||
case PCI_CLASS_BRIDGE_PCI:
|
||||
debug("PCI Autoconfig: Found P2P bridge, device %d\n",
|
||||
PCI_DEV(dev));
|
||||
|
||||
pciauto_setup_device(hose, dev, 2, pci_mem,
|
||||
pci_prefetch, pci_io);
|
||||
|
||||
/* Passing in current_busno allows for sibling P2P bridges */
|
||||
hose->current_busno++;
|
||||
pciauto_prescan_setup_bridge(hose, dev, hose->current_busno);
|
||||
/*
|
||||
* need to figure out if this is a subordinate bridge on the bus
|
||||
* to be able to properly set the pri/sec/sub bridge registers.
|
||||
*/
|
||||
n = pci_hose_scan_bus(hose, hose->current_busno);
|
||||
|
||||
/* figure out the deepest we've gone for this leg */
|
||||
sub_bus = max((unsigned int)n, sub_bus);
|
||||
pciauto_postscan_setup_bridge(hose, dev, sub_bus);
|
||||
|
||||
sub_bus = hose->current_busno;
|
||||
break;
|
||||
|
||||
case PCI_CLASS_BRIDGE_CARDBUS:
|
||||
/*
|
||||
* just do a minimal setup of the bridge,
|
||||
* let the OS take care of the rest
|
||||
*/
|
||||
pciauto_setup_device(hose, dev, 0, pci_mem,
|
||||
pci_prefetch, pci_io);
|
||||
|
||||
debug("PCI Autoconfig: Found P2CardBus bridge, device %d\n",
|
||||
PCI_DEV(dev));
|
||||
|
||||
hose->current_busno++;
|
||||
break;
|
||||
|
||||
#if defined(CONFIG_PCIAUTO_SKIP_HOST_BRIDGE)
|
||||
case PCI_CLASS_BRIDGE_OTHER:
|
||||
debug("PCI Autoconfig: Skipping bridge device %d\n",
|
||||
PCI_DEV(dev));
|
||||
break;
|
||||
#endif
|
||||
#if defined(CONFIG_ARCH_MPC834X) && !defined(CONFIG_TARGET_VME8349) && \
|
||||
!defined(CONFIG_TARGET_CADDY2)
|
||||
case PCI_CLASS_BRIDGE_OTHER:
|
||||
/*
|
||||
* The host/PCI bridge 1 seems broken in 8349 - it presents
|
||||
* itself as 'PCI_CLASS_BRIDGE_OTHER' and appears as an _agent_
|
||||
* device claiming resources io/mem/irq.. we only allow for
|
||||
* the PIMMR window to be allocated (BAR0 - 1MB size)
|
||||
*/
|
||||
debug("PCI Autoconfig: Broken bridge found, only minimal config\n");
|
||||
pciauto_setup_device(hose, dev, 0, hose->pci_mem,
|
||||
hose->pci_prefetch, hose->pci_io);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PCI_CLASS_PROCESSOR_POWERPC: /* an agent or end-point */
|
||||
debug("PCI AutoConfig: Found PowerPC device\n");
|
||||
|
||||
default:
|
||||
pciauto_setup_device(hose, dev, 6, pci_mem,
|
||||
pci_prefetch, pci_io);
|
||||
break;
|
||||
}
|
||||
|
||||
return sub_bus;
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
*
|
||||
* (C) Copyright 2001 Sysgo Real-Time Solutions, GmbH <www.elinos.com>
|
||||
* Andreas Heppel <aheppel@sysgo.de>
|
||||
*
|
||||
* (C) Copyright 2002, 2003
|
||||
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <env.h>
|
||||
#include <errno.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
const char *pci_class_str(u8 class)
|
||||
{
|
||||
switch (class) {
|
||||
case PCI_CLASS_NOT_DEFINED:
|
||||
return "Build before PCI Rev2.0";
|
||||
break;
|
||||
case PCI_BASE_CLASS_STORAGE:
|
||||
return "Mass storage controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_NETWORK:
|
||||
return "Network controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_DISPLAY:
|
||||
return "Display controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_MULTIMEDIA:
|
||||
return "Multimedia device";
|
||||
break;
|
||||
case PCI_BASE_CLASS_MEMORY:
|
||||
return "Memory controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_BRIDGE:
|
||||
return "Bridge device";
|
||||
break;
|
||||
case PCI_BASE_CLASS_COMMUNICATION:
|
||||
return "Simple comm. controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_SYSTEM:
|
||||
return "Base system peripheral";
|
||||
break;
|
||||
case PCI_BASE_CLASS_INPUT:
|
||||
return "Input device";
|
||||
break;
|
||||
case PCI_BASE_CLASS_DOCKING:
|
||||
return "Docking station";
|
||||
break;
|
||||
case PCI_BASE_CLASS_PROCESSOR:
|
||||
return "Processor";
|
||||
break;
|
||||
case PCI_BASE_CLASS_SERIAL:
|
||||
return "Serial bus controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_INTELLIGENT:
|
||||
return "Intelligent controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_SATELLITE:
|
||||
return "Satellite controller";
|
||||
break;
|
||||
case PCI_BASE_CLASS_CRYPT:
|
||||
return "Cryptographic device";
|
||||
break;
|
||||
case PCI_BASE_CLASS_SIGNAL_PROCESSING:
|
||||
return "DSP";
|
||||
break;
|
||||
case PCI_CLASS_OTHERS:
|
||||
return "Does not fit any class";
|
||||
break;
|
||||
default:
|
||||
return "???";
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
__weak int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
|
||||
{
|
||||
/*
|
||||
* Check if pci device should be skipped in configuration
|
||||
*/
|
||||
if (dev == PCI_BDF(hose->first_busno, 0, 0)) {
|
||||
#if defined(CONFIG_PCI_CONFIG_HOST_BRIDGE) /* don't skip host bridge */
|
||||
/*
|
||||
* Only skip configuration if "pciconfighost" is not set
|
||||
*/
|
||||
if (env_get("pciconfighost") == NULL)
|
||||
return 1;
|
||||
#else
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_DM_PCI) || defined(CONFIG_DM_PCI_COMPAT)
|
||||
/* Get a virtual address associated with a BAR region */
|
||||
void *pci_map_bar(pci_dev_t pdev, int bar, int flags)
|
||||
{
|
||||
pci_addr_t pci_bus_addr;
|
||||
u32 bar_response;
|
||||
|
||||
/* read BAR address */
|
||||
pci_read_config_dword(pdev, bar, &bar_response);
|
||||
pci_bus_addr = (pci_addr_t)(bar_response & ~0xf);
|
||||
|
||||
/*
|
||||
* Pass "0" as the length argument to pci_bus_to_virt. The arg
|
||||
* isn't actualy used on any platform because u-boot assumes a static
|
||||
* linear mapping. In the future, this could read the BAR size
|
||||
* and pass that as the size if needed.
|
||||
*/
|
||||
return pci_bus_to_virt(pdev, pci_bus_addr, flags, 0, MAP_NOCACHE);
|
||||
}
|
||||
|
||||
void pci_write_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum,
|
||||
u32 addr_and_ctrl)
|
||||
{
|
||||
int bar;
|
||||
|
||||
bar = PCI_BASE_ADDRESS_0 + barnum * 4;
|
||||
pci_hose_write_config_dword(hose, dev, bar, addr_and_ctrl);
|
||||
}
|
||||
|
||||
u32 pci_read_bar32(struct pci_controller *hose, pci_dev_t dev, int barnum)
|
||||
{
|
||||
u32 addr;
|
||||
int bar;
|
||||
|
||||
bar = PCI_BASE_ADDRESS_0 + barnum * 4;
|
||||
pci_hose_read_config_dword(hose, dev, bar, &addr);
|
||||
if (addr & PCI_BASE_ADDRESS_SPACE_IO)
|
||||
return addr & PCI_BASE_ADDRESS_IO_MASK;
|
||||
else
|
||||
return addr & PCI_BASE_ADDRESS_MEM_MASK;
|
||||
}
|
||||
|
||||
int __pci_hose_bus_to_phys(struct pci_controller *hose,
|
||||
pci_addr_t bus_addr,
|
||||
unsigned long flags,
|
||||
unsigned long skip_mask,
|
||||
phys_addr_t *pa)
|
||||
{
|
||||
struct pci_region *res;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hose->region_count; i++) {
|
||||
res = &hose->regions[i];
|
||||
|
||||
if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
|
||||
continue;
|
||||
|
||||
if (res->flags & skip_mask)
|
||||
continue;
|
||||
|
||||
if (bus_addr >= res->bus_start &&
|
||||
(bus_addr - res->bus_start) < res->size) {
|
||||
*pa = (bus_addr - res->bus_start + res->phys_start);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
phys_addr_t pci_hose_bus_to_phys(struct pci_controller *hose,
|
||||
pci_addr_t bus_addr,
|
||||
unsigned long flags)
|
||||
{
|
||||
phys_addr_t phys_addr = 0;
|
||||
int ret;
|
||||
|
||||
if (!hose) {
|
||||
puts("pci_hose_bus_to_phys: invalid hose\n");
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* if PCI_REGION_MEM is set we do a two pass search with preference
|
||||
* on matches that don't have PCI_REGION_SYS_MEMORY set
|
||||
*/
|
||||
if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
|
||||
ret = __pci_hose_bus_to_phys(hose, bus_addr,
|
||||
flags, PCI_REGION_SYS_MEMORY, &phys_addr);
|
||||
if (!ret)
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
ret = __pci_hose_bus_to_phys(hose, bus_addr, flags, 0, &phys_addr);
|
||||
|
||||
if (ret)
|
||||
puts("pci_hose_bus_to_phys: invalid physical address\n");
|
||||
|
||||
return phys_addr;
|
||||
}
|
||||
|
||||
int __pci_hose_phys_to_bus(struct pci_controller *hose,
|
||||
phys_addr_t phys_addr,
|
||||
unsigned long flags,
|
||||
unsigned long skip_mask,
|
||||
pci_addr_t *ba)
|
||||
{
|
||||
struct pci_region *res;
|
||||
pci_addr_t bus_addr;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < hose->region_count; i++) {
|
||||
res = &hose->regions[i];
|
||||
|
||||
if (((res->flags ^ flags) & PCI_REGION_TYPE) != 0)
|
||||
continue;
|
||||
|
||||
if (res->flags & skip_mask)
|
||||
continue;
|
||||
|
||||
bus_addr = phys_addr - res->phys_start + res->bus_start;
|
||||
|
||||
if (bus_addr >= res->bus_start &&
|
||||
(bus_addr - res->bus_start) < res->size) {
|
||||
*ba = bus_addr;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* pci_hose_phys_to_bus(): Convert physical address to bus address
|
||||
* @hose: PCI hose of the root PCI controller
|
||||
* @phys_addr: physical address to convert
|
||||
* @flags: flags of pci regions
|
||||
* @return bus address if OK, 0 on error
|
||||
*/
|
||||
pci_addr_t pci_hose_phys_to_bus(struct pci_controller *hose,
|
||||
phys_addr_t phys_addr,
|
||||
unsigned long flags)
|
||||
{
|
||||
pci_addr_t bus_addr = 0;
|
||||
int ret;
|
||||
|
||||
if (!hose) {
|
||||
puts("pci_hose_phys_to_bus: invalid hose\n");
|
||||
return bus_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* if PCI_REGION_MEM is set we do a two pass search with preference
|
||||
* on matches that don't have PCI_REGION_SYS_MEMORY set
|
||||
*/
|
||||
if ((flags & PCI_REGION_TYPE) == PCI_REGION_MEM) {
|
||||
ret = __pci_hose_phys_to_bus(hose, phys_addr,
|
||||
flags, PCI_REGION_SYS_MEMORY, &bus_addr);
|
||||
if (!ret)
|
||||
return bus_addr;
|
||||
}
|
||||
|
||||
ret = __pci_hose_phys_to_bus(hose, phys_addr, flags, 0, &bus_addr);
|
||||
|
||||
if (ret)
|
||||
puts("pci_hose_phys_to_bus: invalid physical address\n");
|
||||
|
||||
return bus_addr;
|
||||
}
|
||||
|
||||
pci_dev_t pci_find_device(unsigned int vendor, unsigned int device, int index)
|
||||
{
|
||||
struct pci_device_id ids[2] = { {}, {0, 0} };
|
||||
|
||||
ids[0].vendor = vendor;
|
||||
ids[0].device = device;
|
||||
|
||||
return pci_find_devices(ids, index);
|
||||
}
|
||||
|
||||
pci_dev_t pci_hose_find_devices(struct pci_controller *hose, int busnum,
|
||||
struct pci_device_id *ids, int *indexp)
|
||||
{
|
||||
int found_multi = 0;
|
||||
u16 vendor, device;
|
||||
u8 header_type;
|
||||
pci_dev_t bdf;
|
||||
int i;
|
||||
|
||||
for (bdf = PCI_BDF(busnum, 0, 0);
|
||||
bdf < PCI_BDF(busnum + 1, 0, 0);
|
||||
bdf += PCI_BDF(0, 0, 1)) {
|
||||
if (pci_skip_dev(hose, bdf))
|
||||
continue;
|
||||
|
||||
if (!PCI_FUNC(bdf)) {
|
||||
pci_read_config_byte(bdf, PCI_HEADER_TYPE,
|
||||
&header_type);
|
||||
found_multi = header_type & 0x80;
|
||||
} else {
|
||||
if (!found_multi)
|
||||
continue;
|
||||
}
|
||||
|
||||
pci_read_config_word(bdf, PCI_VENDOR_ID, &vendor);
|
||||
pci_read_config_word(bdf, PCI_DEVICE_ID, &device);
|
||||
|
||||
for (i = 0; ids[i].vendor != 0; i++) {
|
||||
if (vendor == ids[i].vendor &&
|
||||
device == ids[i].device) {
|
||||
if ((*indexp) <= 0)
|
||||
return bdf;
|
||||
|
||||
(*indexp)--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
pci_dev_t pci_find_class(uint find_class, int index)
|
||||
{
|
||||
int bus;
|
||||
int devnum;
|
||||
pci_dev_t bdf;
|
||||
uint32_t class;
|
||||
|
||||
for (bus = 0; bus <= pci_last_busno(); bus++) {
|
||||
for (devnum = 0; devnum < PCI_MAX_PCI_DEVICES - 1; devnum++) {
|
||||
pci_read_config_dword(PCI_BDF(bus, devnum, 0),
|
||||
PCI_CLASS_REVISION, &class);
|
||||
if (class >> 16 == 0xffff)
|
||||
continue;
|
||||
|
||||
for (bdf = PCI_BDF(bus, devnum, 0);
|
||||
bdf <= PCI_BDF(bus, devnum,
|
||||
PCI_MAX_PCI_FUNCTIONS - 1);
|
||||
bdf += PCI_BDF(0, 0, 1)) {
|
||||
pci_read_config_dword(bdf, PCI_CLASS_REVISION,
|
||||
&class);
|
||||
class >>= 8;
|
||||
|
||||
if (class != find_class)
|
||||
continue;
|
||||
/*
|
||||
* Decrement the index. We want to return the
|
||||
* correct device, so index is 0 for the first
|
||||
* matching device, 1 for the second, etc.
|
||||
*/
|
||||
if (index) {
|
||||
index--;
|
||||
continue;
|
||||
}
|
||||
/* Return index'th controller. */
|
||||
return bdf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif /* !CONFIG_DM_PCI || CONFIG_DM_PCI_COMPAT */
|
||||
@@ -0,0 +1,52 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Compatibility functions for pre-driver-model code
|
||||
*
|
||||
* Copyright (C) 2014 Google, Inc
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <pci.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include "pci_internal.h"
|
||||
|
||||
#define PCI_HOSE_OP(rw, name, size, type) \
|
||||
int pci_hose_##rw##_config_##name(struct pci_controller *hose, \
|
||||
pci_dev_t dev, \
|
||||
int offset, type value) \
|
||||
{ \
|
||||
return pci_##rw##_config##size(dev, offset, value); \
|
||||
}
|
||||
|
||||
PCI_HOSE_OP(read, byte, 8, u8 *)
|
||||
PCI_HOSE_OP(read, word, 16, u16 *)
|
||||
PCI_HOSE_OP(read, dword, 32, u32 *)
|
||||
PCI_HOSE_OP(write, byte, 8, u8)
|
||||
PCI_HOSE_OP(write, word, 16, u16)
|
||||
PCI_HOSE_OP(write, dword, 32, u32)
|
||||
|
||||
pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
|
||||
{
|
||||
struct udevice *dev;
|
||||
|
||||
if (pci_find_device_id(ids, index, &dev))
|
||||
return -1;
|
||||
return dm_pci_get_bdf(dev);
|
||||
}
|
||||
|
||||
struct pci_controller *pci_bus_to_hose(int busnum)
|
||||
{
|
||||
struct udevice *bus;
|
||||
int ret;
|
||||
|
||||
ret = pci_get_bus(busnum, &bus);
|
||||
if (ret) {
|
||||
debug("%s: Cannot get bus %d: ret=%d\n", __func__, busnum, ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dev_get_uclass_priv(pci_get_controller(bus));
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Faraday FTPCI100 PCI Bridge Controller Device Driver Implementation
|
||||
*
|
||||
* Copyright (C) 2011 Andes Technology Corporation
|
||||
* Gavin Guo, Andes Technology Corporation <gavinguo@andestech.com>
|
||||
* Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com>
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <malloc.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <faraday/ftpci100.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
#include <asm/types.h> /* u32, u16.... used by pci.h */
|
||||
|
||||
struct ftpci100_data {
|
||||
unsigned int reg_base;
|
||||
unsigned int io_base;
|
||||
unsigned int mem_base;
|
||||
unsigned int mmio_base;
|
||||
unsigned int ndevs;
|
||||
};
|
||||
|
||||
static struct pci_config devs[FTPCI100_MAX_FUNCTIONS];
|
||||
static struct pci_controller local_hose;
|
||||
|
||||
static void setup_pci_bar(unsigned int bus, unsigned int dev, unsigned func,
|
||||
unsigned char header, struct ftpci100_data *priv)
|
||||
{
|
||||
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
||||
unsigned int i, tmp32, bar_no, iovsmem = 1;
|
||||
pci_dev_t dev_nu;
|
||||
|
||||
/* A device is present, add an entry to the array */
|
||||
devs[priv->ndevs].bus = bus;
|
||||
devs[priv->ndevs].dev = dev;
|
||||
devs[priv->ndevs].func = func;
|
||||
|
||||
dev_nu = PCI_BDF(bus, dev, func);
|
||||
|
||||
if ((header & 0x7f) == 0x01)
|
||||
/* PCI-PCI Bridge */
|
||||
bar_no = 2;
|
||||
else
|
||||
bar_no = 6;
|
||||
|
||||
/* Allocate address spaces by configuring BARs */
|
||||
for (i = 0; i < bar_no; i++) {
|
||||
pci_hose_write_config_dword(hose, dev_nu,
|
||||
PCI_BASE_ADDRESS_0 + i * 4, 0xffffffff);
|
||||
pci_hose_read_config_dword(hose, dev_nu,
|
||||
PCI_BASE_ADDRESS_0 + i * 4, &tmp32);
|
||||
|
||||
if (tmp32 == 0x0)
|
||||
continue;
|
||||
|
||||
/* IO space */
|
||||
if (tmp32 & 0x1) {
|
||||
iovsmem = 0;
|
||||
unsigned int size_mask = ~(tmp32 & 0xfffffffc);
|
||||
|
||||
if (priv->io_base & size_mask)
|
||||
priv->io_base = (priv->io_base & ~size_mask) + \
|
||||
size_mask + 1;
|
||||
|
||||
devs[priv->ndevs].bar[i].addr = priv->io_base;
|
||||
devs[priv->ndevs].bar[i].size = size_mask + 1;
|
||||
|
||||
pci_hose_write_config_dword(hose, dev_nu,
|
||||
PCI_BASE_ADDRESS_0 + i * 4,
|
||||
priv->io_base);
|
||||
|
||||
debug("Allocated IO address 0x%X-" \
|
||||
"0x%X for Bus %d, Device %d, Function %d\n",
|
||||
priv->io_base,
|
||||
priv->io_base + size_mask, bus, dev, func);
|
||||
|
||||
priv->io_base += size_mask + 1;
|
||||
} else {
|
||||
/* Memory space */
|
||||
unsigned int is_64bit = ((tmp32 & 0x6) == 0x4);
|
||||
unsigned int is_pref = tmp32 & 0x8;
|
||||
unsigned int size_mask = ~(tmp32 & 0xfffffff0);
|
||||
unsigned int alloc_base;
|
||||
unsigned int *addr_mem_base;
|
||||
|
||||
if (is_pref)
|
||||
addr_mem_base = &priv->mem_base;
|
||||
else
|
||||
addr_mem_base = &priv->mmio_base;
|
||||
|
||||
alloc_base = *addr_mem_base;
|
||||
|
||||
if (alloc_base & size_mask)
|
||||
alloc_base = (alloc_base & ~size_mask) \
|
||||
+ size_mask + 1;
|
||||
|
||||
pci_hose_write_config_dword(hose, dev_nu,
|
||||
PCI_BASE_ADDRESS_0 + i * 4, alloc_base);
|
||||
|
||||
debug("Allocated %s address 0x%X-" \
|
||||
"0x%X for Bus %d, Device %d, Function %d\n",
|
||||
is_pref ? "MEM" : "MMIO", alloc_base,
|
||||
alloc_base + size_mask, bus, dev, func);
|
||||
|
||||
devs[priv->ndevs].bar[i].addr = alloc_base;
|
||||
devs[priv->ndevs].bar[i].size = size_mask + 1;
|
||||
|
||||
debug("BAR address BAR size\n");
|
||||
debug("%010x %08d\n",
|
||||
devs[priv->ndevs].bar[0].addr,
|
||||
devs[priv->ndevs].bar[0].size);
|
||||
|
||||
alloc_base += size_mask + 1;
|
||||
*addr_mem_base = alloc_base;
|
||||
|
||||
if (is_64bit) {
|
||||
i++;
|
||||
pci_hose_write_config_dword(hose, dev_nu,
|
||||
PCI_BASE_ADDRESS_0 + i * 4, 0x0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Enable Bus Master, Memory Space, and IO Space */
|
||||
pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
|
||||
pci_hose_write_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, 0x08);
|
||||
pci_hose_read_config_dword(hose, dev_nu, PCI_CACHE_LINE_SIZE, &tmp32);
|
||||
|
||||
pci_hose_read_config_dword(hose, dev_nu, PCI_COMMAND, &tmp32);
|
||||
|
||||
tmp32 &= 0xffff;
|
||||
|
||||
if (iovsmem == 0)
|
||||
tmp32 |= 0x5;
|
||||
else
|
||||
tmp32 |= 0x6;
|
||||
|
||||
pci_hose_write_config_dword(hose, dev_nu, PCI_COMMAND, tmp32);
|
||||
}
|
||||
|
||||
static void pci_bus_scan(struct ftpci100_data *priv)
|
||||
{
|
||||
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
||||
unsigned int bus, dev, func;
|
||||
pci_dev_t dev_nu;
|
||||
unsigned int data32;
|
||||
unsigned int tmp;
|
||||
unsigned char header;
|
||||
unsigned char int_pin;
|
||||
unsigned int niobars;
|
||||
unsigned int nmbars;
|
||||
|
||||
priv->ndevs = 1;
|
||||
|
||||
nmbars = 0;
|
||||
niobars = 0;
|
||||
|
||||
for (bus = 0; bus < MAX_BUS_NUM; bus++)
|
||||
for (dev = 0; dev < MAX_DEV_NUM; dev++)
|
||||
for (func = 0; func < MAX_FUN_NUM; func++) {
|
||||
dev_nu = PCI_BDF(bus, dev, func);
|
||||
pci_hose_read_config_dword(hose, dev_nu,
|
||||
PCI_VENDOR_ID, &data32);
|
||||
|
||||
/*
|
||||
* some broken boards return 0 or ~0,
|
||||
* if a slot is empty.
|
||||
*/
|
||||
if (data32 == 0xffffffff ||
|
||||
data32 == 0x00000000 ||
|
||||
data32 == 0x0000ffff ||
|
||||
data32 == 0xffff0000)
|
||||
continue;
|
||||
|
||||
pci_hose_read_config_dword(hose, dev_nu,
|
||||
PCI_HEADER_TYPE, &tmp);
|
||||
header = (unsigned char)tmp;
|
||||
setup_pci_bar(bus, dev, func, header, priv);
|
||||
|
||||
devs[priv->ndevs].v_id = (u16)(data32 & \
|
||||
0x0000ffff);
|
||||
|
||||
devs[priv->ndevs].d_id = (u16)((data32 & \
|
||||
0xffff0000) >> 16);
|
||||
|
||||
/* Figure out what INTX# line the card uses */
|
||||
pci_hose_read_config_byte(hose, dev_nu,
|
||||
PCI_INTERRUPT_PIN, &int_pin);
|
||||
|
||||
/* assign the appropriate irq line */
|
||||
if (int_pin > PCI_IRQ_LINES) {
|
||||
printf("more irq lines than expect\n");
|
||||
} else if (int_pin != 0) {
|
||||
/* This device uses an interrupt line */
|
||||
devs[priv->ndevs].pin = int_pin;
|
||||
}
|
||||
|
||||
pci_hose_read_config_dword(hose, dev_nu,
|
||||
PCI_CLASS_DEVICE, &data32);
|
||||
|
||||
debug("%06d %03d %03d " \
|
||||
"%04d %08x %08x " \
|
||||
"%03d %08x %06d %08x\n",
|
||||
priv->ndevs, devs[priv->ndevs].bus,
|
||||
devs[priv->ndevs].dev,
|
||||
devs[priv->ndevs].func,
|
||||
devs[priv->ndevs].d_id,
|
||||
devs[priv->ndevs].v_id,
|
||||
devs[priv->ndevs].pin,
|
||||
devs[priv->ndevs].bar[0].addr,
|
||||
devs[priv->ndevs].bar[0].size,
|
||||
data32 >> 8);
|
||||
|
||||
priv->ndevs++;
|
||||
}
|
||||
}
|
||||
|
||||
static void ftpci_preinit(struct ftpci100_data *priv)
|
||||
{
|
||||
struct ftpci100_ahbc *ftpci100;
|
||||
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
||||
u32 pci_config_addr;
|
||||
u32 pci_config_data;
|
||||
|
||||
priv->reg_base = CONFIG_FTPCI100_BASE;
|
||||
priv->io_base = CONFIG_FTPCI100_BASE + CONFIG_FTPCI100_IO_SIZE;
|
||||
priv->mmio_base = CONFIG_FTPCI100_MEM_BASE;
|
||||
priv->mem_base = CONFIG_FTPCI100_MEM_BASE + CONFIG_FTPCI100_MEM_SIZE;
|
||||
|
||||
ftpci100 = (struct ftpci100_ahbc *)priv->reg_base;
|
||||
|
||||
pci_config_addr = (u32) &ftpci100->conf;
|
||||
pci_config_data = (u32) &ftpci100->data;
|
||||
|
||||
/* print device name */
|
||||
printf("FTPCI100\n");
|
||||
|
||||
/* dump basic configuration */
|
||||
debug("%s: Config addr is %08X, data port is %08X\n",
|
||||
__func__, pci_config_addr, pci_config_data);
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(hose->regions + 0,
|
||||
CONFIG_PCI_MEM_BUS,
|
||||
CONFIG_PCI_MEM_PHYS,
|
||||
CONFIG_PCI_MEM_SIZE,
|
||||
PCI_REGION_MEM);
|
||||
hose->region_count++;
|
||||
|
||||
/* PCI IO space */
|
||||
pci_set_region(hose->regions + 1,
|
||||
CONFIG_PCI_IO_BUS,
|
||||
CONFIG_PCI_IO_PHYS,
|
||||
CONFIG_PCI_IO_SIZE,
|
||||
PCI_REGION_IO);
|
||||
hose->region_count++;
|
||||
|
||||
#if defined(CONFIG_PCI_SYS_BUS)
|
||||
/* PCI System Memory space */
|
||||
pci_set_region(hose->regions + 2,
|
||||
CONFIG_PCI_SYS_BUS,
|
||||
CONFIG_PCI_SYS_PHYS,
|
||||
CONFIG_PCI_SYS_SIZE,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
hose->region_count++;
|
||||
#endif
|
||||
|
||||
/* setup indirect read/write function */
|
||||
pci_setup_indirect(hose, pci_config_addr, pci_config_data);
|
||||
|
||||
/* register hose */
|
||||
pci_register_hose(hose);
|
||||
}
|
||||
|
||||
void pci_ftpci_init(void)
|
||||
{
|
||||
struct ftpci100_data *priv = NULL;
|
||||
struct pci_controller *hose = (struct pci_controller *)&local_hose;
|
||||
pci_dev_t bridge_num;
|
||||
|
||||
struct pci_device_id bridge_ids[] = {
|
||||
{FTPCI100_BRIDGE_VENDORID, FTPCI100_BRIDGE_DEVICEID},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
priv = malloc(sizeof(struct ftpci100_data));
|
||||
|
||||
if (!priv) {
|
||||
printf("%s(): failed to malloc priv\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(priv, 0, sizeof(struct ftpci100_data));
|
||||
|
||||
ftpci_preinit(priv);
|
||||
|
||||
debug("Device bus dev func deviceID vendorID pin address" \
|
||||
" size class\n");
|
||||
|
||||
pci_bus_scan(priv);
|
||||
|
||||
/*
|
||||
* Setup the PCI Bridge Window to 1GB,
|
||||
* it will cause USB OHCI Host controller Unrecoverable Error
|
||||
* if it is not set.
|
||||
*/
|
||||
bridge_num = pci_find_devices(bridge_ids, 0);
|
||||
if (bridge_num == -1) {
|
||||
printf("PCI Bridge not found\n");
|
||||
return;
|
||||
}
|
||||
pci_hose_write_config_dword(hose, bridge_num, PCI_MEM_BASE_SIZE1,
|
||||
FTPCI100_BASE_ADR_SIZE(1024));
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
|
||||
*
|
||||
* Based on the Linux implementation.
|
||||
* Copyright (C) 1999, 2000, 2004 MIPS Technologies, Inc.
|
||||
* Authors: Carsten Langgaard <carstenl@mips.com>
|
||||
* Maciej W. Rozycki <macro@mips.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <gt64120.h>
|
||||
#include <pci.h>
|
||||
#include <pci_gt64120.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
#define PCI_ACCESS_READ 0
|
||||
#define PCI_ACCESS_WRITE 1
|
||||
|
||||
struct gt64120_regs {
|
||||
u8 unused_000[0xc18];
|
||||
u32 intrcause;
|
||||
u8 unused_c1c[0x0dc];
|
||||
u32 pci0_cfgaddr;
|
||||
u32 pci0_cfgdata;
|
||||
};
|
||||
|
||||
struct gt64120_pci_controller {
|
||||
struct pci_controller hose;
|
||||
struct gt64120_regs *regs;
|
||||
};
|
||||
|
||||
static inline struct gt64120_pci_controller *
|
||||
hose_to_gt64120(struct pci_controller *hose)
|
||||
{
|
||||
return container_of(hose, struct gt64120_pci_controller, hose);
|
||||
}
|
||||
|
||||
#define GT_INTRCAUSE_ABORT_BITS \
|
||||
(GT_INTRCAUSE_MASABORT0_BIT | GT_INTRCAUSE_TARABORT0_BIT)
|
||||
|
||||
static int gt_config_access(struct gt64120_pci_controller *gt,
|
||||
unsigned char access_type, pci_dev_t bdf,
|
||||
int where, u32 *data)
|
||||
{
|
||||
unsigned int bus = PCI_BUS(bdf);
|
||||
unsigned int dev = PCI_DEV(bdf);
|
||||
unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf);
|
||||
u32 intr;
|
||||
u32 addr;
|
||||
u32 val;
|
||||
|
||||
if (bus == 0 && dev >= 31) {
|
||||
/* Because of a bug in the galileo (for slot 31). */
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (access_type == PCI_ACCESS_WRITE)
|
||||
debug("PCI WR %02x:%02x.%x reg:%02d data:%08x\n",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data);
|
||||
|
||||
/* Clear cause register bits */
|
||||
writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause);
|
||||
|
||||
addr = GT_PCI0_CFGADDR_CONFIGEN_BIT;
|
||||
addr |= bus << GT_PCI0_CFGADDR_BUSNUM_SHF;
|
||||
addr |= devfn << GT_PCI0_CFGADDR_FUNCTNUM_SHF;
|
||||
addr |= (where / 4) << GT_PCI0_CFGADDR_REGNUM_SHF;
|
||||
|
||||
/* Setup address */
|
||||
writel(addr, >->regs->pci0_cfgaddr);
|
||||
|
||||
if (access_type == PCI_ACCESS_WRITE) {
|
||||
if (bus == 0 && dev == 0) {
|
||||
/*
|
||||
* The Galileo system controller is acting
|
||||
* differently than other devices.
|
||||
*/
|
||||
val = *data;
|
||||
} else {
|
||||
val = cpu_to_le32(*data);
|
||||
}
|
||||
|
||||
writel(val, >->regs->pci0_cfgdata);
|
||||
} else {
|
||||
val = readl(>->regs->pci0_cfgdata);
|
||||
|
||||
if (bus == 0 && dev == 0) {
|
||||
/*
|
||||
* The Galileo system controller is acting
|
||||
* differently than other devices.
|
||||
*/
|
||||
*data = val;
|
||||
} else {
|
||||
*data = le32_to_cpu(val);
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for master or target abort */
|
||||
intr = readl(>->regs->intrcause);
|
||||
if (intr & GT_INTRCAUSE_ABORT_BITS) {
|
||||
/* Error occurred, clear abort bits */
|
||||
writel(~GT_INTRCAUSE_ABORT_BITS, >->regs->intrcause);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (access_type == PCI_ACCESS_READ)
|
||||
debug("PCI RD %02x:%02x.%x reg:%02d data:%08x\n",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf), where, *data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gt_read_config_dword(struct pci_controller *hose, pci_dev_t dev,
|
||||
int where, u32 *value)
|
||||
{
|
||||
struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
|
||||
|
||||
*value = 0xffffffff;
|
||||
return gt_config_access(gt, PCI_ACCESS_READ, dev, where, value);
|
||||
}
|
||||
|
||||
static int gt_write_config_dword(struct pci_controller *hose, pci_dev_t dev,
|
||||
int where, u32 value)
|
||||
{
|
||||
struct gt64120_pci_controller *gt = hose_to_gt64120(hose);
|
||||
u32 data = value;
|
||||
|
||||
return gt_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data);
|
||||
}
|
||||
|
||||
void gt64120_pci_init(void *regs, unsigned long sys_bus, unsigned long sys_phys,
|
||||
unsigned long sys_size, unsigned long mem_bus,
|
||||
unsigned long mem_phys, unsigned long mem_size,
|
||||
unsigned long io_bus, unsigned long io_phys,
|
||||
unsigned long io_size)
|
||||
{
|
||||
static struct gt64120_pci_controller global_gt;
|
||||
struct gt64120_pci_controller *gt;
|
||||
struct pci_controller *hose;
|
||||
|
||||
gt = &global_gt;
|
||||
gt->regs = regs;
|
||||
|
||||
hose = >->hose;
|
||||
|
||||
hose->first_busno = 0;
|
||||
hose->last_busno = 0;
|
||||
|
||||
/* System memory space */
|
||||
pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size,
|
||||
PCI_REGION_MEM);
|
||||
|
||||
/* PCI I/O space */
|
||||
pci_set_region(&hose->regions[2], io_bus, io_phys, io_size,
|
||||
PCI_REGION_IO);
|
||||
|
||||
hose->region_count = 3;
|
||||
|
||||
pci_set_ops(hose,
|
||||
pci_hose_read_config_byte_via_dword,
|
||||
pci_hose_read_config_word_via_dword,
|
||||
gt_read_config_dword,
|
||||
pci_hose_write_config_byte_via_dword,
|
||||
pci_hose_write_config_word_via_dword,
|
||||
gt_write_config_dword);
|
||||
|
||||
pci_register_hose(hose);
|
||||
hose->last_busno = pci_hose_scan(hose);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Support for indirect PCI bridges.
|
||||
*
|
||||
* Copyright (C) 1998 Gabriel Paubert.
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#if !defined(__I386__) && !defined(CONFIG_DM_PCI)
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <pci.h>
|
||||
|
||||
#define cfg_read(val, addr, type, op) *val = op((type)(addr))
|
||||
#define cfg_write(val, addr, type, op) op((type *)(addr), (val))
|
||||
|
||||
#if defined(CONFIG_E500) || defined(CONFIG_MPC86xx)
|
||||
#define INDIRECT_PCI_OP(rw, size, type, op, mask) \
|
||||
static int \
|
||||
indirect_##rw##_config_##size(struct pci_controller *hose, \
|
||||
pci_dev_t dev, int offset, type val) \
|
||||
{ \
|
||||
u32 b, d,f; \
|
||||
b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \
|
||||
b = b - hose->first_busno; \
|
||||
dev = PCI_BDF(b, d, f); \
|
||||
*(hose->cfg_addr) = dev | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000; \
|
||||
sync(); \
|
||||
cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \
|
||||
return 0; \
|
||||
}
|
||||
#else
|
||||
#define INDIRECT_PCI_OP(rw, size, type, op, mask) \
|
||||
static int \
|
||||
indirect_##rw##_config_##size(struct pci_controller *hose, \
|
||||
pci_dev_t dev, int offset, type val) \
|
||||
{ \
|
||||
u32 b, d,f; \
|
||||
b = PCI_BUS(dev); d = PCI_DEV(dev); f = PCI_FUNC(dev); \
|
||||
b = b - hose->first_busno; \
|
||||
dev = PCI_BDF(b, d, f); \
|
||||
out_le32(hose->cfg_addr, dev | (offset & 0xfc) | 0x80000000); \
|
||||
cfg_##rw(val, hose->cfg_data + (offset & mask), type, op); \
|
||||
return 0; \
|
||||
}
|
||||
#endif
|
||||
|
||||
INDIRECT_PCI_OP(read, byte, u8 *, in_8, 3)
|
||||
INDIRECT_PCI_OP(read, word, u16 *, in_le16, 2)
|
||||
INDIRECT_PCI_OP(read, dword, u32 *, in_le32, 0)
|
||||
INDIRECT_PCI_OP(write, byte, u8, out_8, 3)
|
||||
INDIRECT_PCI_OP(write, word, u16, out_le16, 2)
|
||||
INDIRECT_PCI_OP(write, dword, u32, out_le32, 0)
|
||||
|
||||
void pci_setup_indirect(struct pci_controller* hose, u32 cfg_addr, u32 cfg_data)
|
||||
{
|
||||
pci_set_ops(hose,
|
||||
indirect_read_config_byte,
|
||||
indirect_read_config_word,
|
||||
indirect_read_config_dword,
|
||||
indirect_write_config_byte,
|
||||
indirect_write_config_word,
|
||||
indirect_write_config_dword);
|
||||
|
||||
hose->cfg_addr = (unsigned int *) cfg_addr;
|
||||
hose->cfg_data = (unsigned char *) cfg_data;
|
||||
}
|
||||
|
||||
#endif /* !__I386__ */
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Internal PCI functions, not exported outside drivers/pci
|
||||
*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#ifndef __pci_internal_h
|
||||
#define __pci_internal_h
|
||||
|
||||
/**
|
||||
* dm_pciauto_prescan_setup_bridge() - Set up a bridge for scanning
|
||||
*
|
||||
* This gets a bridge ready so that its downstream devices can be scanned.
|
||||
* It sets up the bus number and memory range registers. Once the scan is
|
||||
* completed, dm_pciauto_postscan_setup_bridge() should be called.
|
||||
*
|
||||
* @dev: Bridge device to be scanned
|
||||
* @sub_bus: Bus number of the 'other side' of the bridge
|
||||
*/
|
||||
void dm_pciauto_prescan_setup_bridge(struct udevice *dev, int sub_bus);
|
||||
|
||||
/**
|
||||
* dm_pciauto_postscan_setup_bridge() - Finish set up of a bridge after scanning
|
||||
*
|
||||
* This should be called after a bus scan is complete. It adjusts the memory
|
||||
* ranges to fit with the devices actually found on the other side (downstream)
|
||||
* of the bridge.
|
||||
*
|
||||
* @dev: Bridge device that was scanned
|
||||
* @sub_bus: Bus number of the 'other side' of the bridge
|
||||
*/
|
||||
void dm_pciauto_postscan_setup_bridge(struct udevice *dev, int sub_bus);
|
||||
|
||||
/**
|
||||
* dm_pciauto_config_device() - Configure a PCI device ready for use
|
||||
*
|
||||
* If the device is a bridge, downstream devices will be probed.
|
||||
*
|
||||
* @dev: Device to configure
|
||||
* @return the maximum PCI bus number found by this device. If there are no
|
||||
* bridges, this just returns the device's bus number. If the device is a
|
||||
* bridge then it will return a larger number, depending on the devices on
|
||||
* that bridge. On error, returns a -ve error number.
|
||||
*/
|
||||
int dm_pciauto_config_device(struct udevice *dev);
|
||||
|
||||
/**
|
||||
* pci_get_bus() - Get a pointer to a bus, given its number
|
||||
*
|
||||
* This looks up a PCI bus based on its bus number. The bus is probed if
|
||||
* necessary.
|
||||
*
|
||||
* @busnum: PCI bus number to look up
|
||||
* @busp: Returns PCI bus on success
|
||||
* @return 0 on success, or -ve error
|
||||
*/
|
||||
int pci_get_bus(int busnum, struct udevice **busp);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* (C) Copyright 2019
|
||||
* Heiko Schocher, DENX Software Engineering, hs@denx.de.
|
||||
*
|
||||
*/
|
||||
#include <common.h>
|
||||
#include <asm/cpm_85xx.h>
|
||||
#include <pci.h>
|
||||
#include <dm.h>
|
||||
#include <asm/fsl_law.h>
|
||||
|
||||
struct mpc85xx_pci_priv {
|
||||
void __iomem *cfg_addr;
|
||||
void __iomem *cfg_data;
|
||||
};
|
||||
|
||||
static int mpc85xx_pci_dm_read_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong *value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct mpc85xx_pci_priv *priv = dev_get_priv(dev);
|
||||
u32 addr;
|
||||
|
||||
addr = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000;
|
||||
out_be32(priv->cfg_addr, addr);
|
||||
sync();
|
||||
*value = pci_conv_32_to_size(in_le32(priv->cfg_data), offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc85xx_pci_dm_write_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct mpc85xx_pci_priv *priv = dev_get_priv(dev);
|
||||
u32 addr;
|
||||
|
||||
addr = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000;
|
||||
out_be32(priv->cfg_addr, addr);
|
||||
sync();
|
||||
out_le32(priv->cfg_data, pci_conv_size_to_32(0, value, offset, size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
mpc85xx_pci_dm_setup_laws(struct pci_region *io, struct pci_region *mem,
|
||||
struct pci_region *pre)
|
||||
{
|
||||
/*
|
||||
* Unfortunately we have defines for this addresse,
|
||||
* as we have to setup the TLB, and at this stage
|
||||
* we have no access to DT ... may we check here
|
||||
* if the value in the define is the same ?
|
||||
*/
|
||||
if (mem)
|
||||
set_next_law(mem->phys_start, law_size_bits(mem->size),
|
||||
LAW_TRGT_IF_PCI);
|
||||
if (io)
|
||||
set_next_law(io->phys_start, law_size_bits(io->size),
|
||||
LAW_TRGT_IF_PCI);
|
||||
if (pre)
|
||||
set_next_law(pre->phys_start, law_size_bits(pre->size),
|
||||
LAW_TRGT_IF_PCI);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc85xx_pci_dm_probe(struct udevice *dev)
|
||||
{
|
||||
struct mpc85xx_pci_priv *priv = dev_get_priv(dev);
|
||||
struct pci_region *io;
|
||||
struct pci_region *mem;
|
||||
struct pci_region *pre;
|
||||
int count;
|
||||
ccsr_pcix_t *pcix;
|
||||
|
||||
count = pci_get_regions(dev, &io, &mem, &pre);
|
||||
if (count != 2) {
|
||||
printf("%s: wrong count of regions %d only 2 allowed\n",
|
||||
__func__, count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mpc85xx_pci_dm_setup_laws(io, mem, pre);
|
||||
|
||||
pcix = priv->cfg_addr;
|
||||
/* BAR 1: memory */
|
||||
out_be32(&pcix->potar1, (mem->bus_start >> 12) & 0x000fffff);
|
||||
out_be32(&pcix->potear1, 0);
|
||||
out_be32(&pcix->powbar1, (mem->phys_start >> 12) & 0x000fffff);
|
||||
out_be32(&pcix->powbear1, 0);
|
||||
out_be32(&pcix->powar1, (POWAR_EN | POWAR_MEM_READ |
|
||||
POWAR_MEM_WRITE | (__ilog2(mem->size) - 1)));
|
||||
|
||||
/* BAR 1: IO */
|
||||
out_be32(&pcix->potar2, (io->bus_start >> 12) & 0x000fffff);
|
||||
out_be32(&pcix->potear2, 0);
|
||||
out_be32(&pcix->powbar2, (io->phys_start >> 12) & 0x000fffff);
|
||||
out_be32(&pcix->powbear2, 0);
|
||||
out_be32(&pcix->powar2, (POWAR_EN | POWAR_IO_READ |
|
||||
POWAR_IO_WRITE | (__ilog2(io->size) - 1)));
|
||||
|
||||
out_be32(&pcix->pitar1, 0);
|
||||
out_be32(&pcix->piwbar1, 0);
|
||||
out_be32(&pcix->piwar1, (PIWAR_EN | PIWAR_PF | PIWAR_LOCAL |
|
||||
PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP | PIWAR_MEM_2G));
|
||||
|
||||
out_be32(&pcix->powar3, 0);
|
||||
out_be32(&pcix->powar4, 0);
|
||||
out_be32(&pcix->piwar2, 0);
|
||||
out_be32(&pcix->piwar3, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc85xx_pci_dm_remove(struct udevice *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mpc85xx_pci_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct mpc85xx_pci_priv *priv = dev_get_priv(dev);
|
||||
fdt_addr_t addr;
|
||||
|
||||
addr = devfdt_get_addr_index(dev, 0);
|
||||
if (addr == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
priv->cfg_addr = (void __iomem *)addr;
|
||||
addr += 4;
|
||||
priv->cfg_data = (void __iomem *)addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops mpc85xx_pci_ops = {
|
||||
.read_config = mpc85xx_pci_dm_read_config,
|
||||
.write_config = mpc85xx_pci_dm_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id mpc85xx_pci_ids[] = {
|
||||
{ .compatible = "fsl,mpc8540-pci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mpc85xx_pci) = {
|
||||
.name = "mpc85xx_pci",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = mpc85xx_pci_ids,
|
||||
.ops = &mpc85xx_pci_ops,
|
||||
.probe = mpc85xx_pci_dm_probe,
|
||||
.remove = mpc85xx_pci_dm_remove,
|
||||
.ofdata_to_platdata = mpc85xx_pci_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct mpc85xx_pci_priv),
|
||||
};
|
||||
@@ -0,0 +1,124 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2013 Imagination Technologies
|
||||
* Author: Paul Burton <paul.burton@mips.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <msc01.h>
|
||||
#include <pci.h>
|
||||
#include <pci_msc01.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define PCI_ACCESS_READ 0
|
||||
#define PCI_ACCESS_WRITE 1
|
||||
|
||||
struct msc01_pci_controller {
|
||||
struct pci_controller hose;
|
||||
void *base;
|
||||
};
|
||||
|
||||
static inline struct msc01_pci_controller *
|
||||
hose_to_msc01(struct pci_controller *hose)
|
||||
{
|
||||
return container_of(hose, struct msc01_pci_controller, hose);
|
||||
}
|
||||
|
||||
static int msc01_config_access(struct msc01_pci_controller *msc01,
|
||||
unsigned char access_type, pci_dev_t bdf,
|
||||
int where, u32 *data)
|
||||
{
|
||||
const u32 aborts = MSC01_PCI_INTSTAT_MA_MSK | MSC01_PCI_INTSTAT_TA_MSK;
|
||||
void *intstat = msc01->base + MSC01_PCI_INTSTAT_OFS;
|
||||
void *cfgdata = msc01->base + MSC01_PCI_CFGDATA_OFS;
|
||||
unsigned int bus = PCI_BUS(bdf);
|
||||
unsigned int dev = PCI_DEV(bdf);
|
||||
unsigned int devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf);
|
||||
|
||||
/* clear abort status */
|
||||
__raw_writel(aborts, intstat);
|
||||
|
||||
/* setup address */
|
||||
__raw_writel((bus << MSC01_PCI_CFGADDR_BNUM_SHF) |
|
||||
(dev << MSC01_PCI_CFGADDR_DNUM_SHF) |
|
||||
(devfn << MSC01_PCI_CFGADDR_FNUM_SHF) |
|
||||
((where / 4) << MSC01_PCI_CFGADDR_RNUM_SHF),
|
||||
msc01->base + MSC01_PCI_CFGADDR_OFS);
|
||||
|
||||
/* perform access */
|
||||
if (access_type == PCI_ACCESS_WRITE)
|
||||
__raw_writel(*data, cfgdata);
|
||||
else
|
||||
*data = __raw_readl(cfgdata);
|
||||
|
||||
/* check for aborts */
|
||||
if (__raw_readl(intstat) & aborts) {
|
||||
/* clear abort status */
|
||||
__raw_writel(aborts, intstat);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int msc01_read_config_dword(struct pci_controller *hose, pci_dev_t dev,
|
||||
int where, u32 *value)
|
||||
{
|
||||
struct msc01_pci_controller *msc01 = hose_to_msc01(hose);
|
||||
|
||||
*value = 0xffffffff;
|
||||
return msc01_config_access(msc01, PCI_ACCESS_READ, dev, where, value);
|
||||
}
|
||||
|
||||
static int msc01_write_config_dword(struct pci_controller *hose, pci_dev_t dev,
|
||||
int where, u32 value)
|
||||
{
|
||||
struct msc01_pci_controller *gt = hose_to_msc01(hose);
|
||||
u32 data = value;
|
||||
|
||||
return msc01_config_access(gt, PCI_ACCESS_WRITE, dev, where, &data);
|
||||
}
|
||||
|
||||
void msc01_pci_init(void *base, unsigned long sys_bus, unsigned long sys_phys,
|
||||
unsigned long sys_size, unsigned long mem_bus,
|
||||
unsigned long mem_phys, unsigned long mem_size,
|
||||
unsigned long io_bus, unsigned long io_phys,
|
||||
unsigned long io_size)
|
||||
{
|
||||
static struct msc01_pci_controller global_msc01;
|
||||
struct msc01_pci_controller *msc01;
|
||||
struct pci_controller *hose;
|
||||
|
||||
msc01 = &global_msc01;
|
||||
msc01->base = base;
|
||||
|
||||
hose = &msc01->hose;
|
||||
|
||||
hose->first_busno = 0;
|
||||
hose->last_busno = 0;
|
||||
|
||||
/* System memory space */
|
||||
pci_set_region(&hose->regions[0], sys_bus, sys_phys, sys_size,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(&hose->regions[1], mem_bus, mem_phys, mem_size,
|
||||
PCI_REGION_MEM);
|
||||
|
||||
/* PCI I/O space */
|
||||
pci_set_region(&hose->regions[2], io_bus, io_phys, io_size,
|
||||
PCI_REGION_IO);
|
||||
|
||||
hose->region_count = 3;
|
||||
|
||||
pci_set_ops(hose,
|
||||
pci_hose_read_config_byte_via_dword,
|
||||
pci_hose_read_config_word_via_dword,
|
||||
msc01_read_config_dword,
|
||||
pci_hose_write_config_byte_via_dword,
|
||||
pci_hose_write_config_word_via_dword,
|
||||
msc01_write_config_dword);
|
||||
|
||||
pci_register_hose(hose);
|
||||
hose->last_busno = pci_hose_scan(hose);
|
||||
}
|
||||
@@ -0,0 +1,519 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* PCIe driver for Marvell MVEBU SoCs
|
||||
*
|
||||
* Based on Barebox drivers/pci/pci-mvebu.c
|
||||
*
|
||||
* Ported to U-Boot by:
|
||||
* Anton Schubert <anton.schubert@gmx.de>
|
||||
* Stefan Roese <sr@denx.de>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/lists.h>
|
||||
#include <dm/of_access.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/arch/cpu.h>
|
||||
#include <asm/arch/soc.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/mbus.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* PCIe unit register offsets */
|
||||
#define SELECT(x, n) ((x >> n) & 1UL)
|
||||
|
||||
#define PCIE_DEV_ID_OFF 0x0000
|
||||
#define PCIE_CMD_OFF 0x0004
|
||||
#define PCIE_DEV_REV_OFF 0x0008
|
||||
#define PCIE_BAR_LO_OFF(n) (0x0010 + ((n) << 3))
|
||||
#define PCIE_BAR_HI_OFF(n) (0x0014 + ((n) << 3))
|
||||
#define PCIE_CAPAB_OFF 0x0060
|
||||
#define PCIE_CTRL_STAT_OFF 0x0068
|
||||
#define PCIE_HEADER_LOG_4_OFF 0x0128
|
||||
#define PCIE_BAR_CTRL_OFF(n) (0x1804 + (((n) - 1) * 4))
|
||||
#define PCIE_WIN04_CTRL_OFF(n) (0x1820 + ((n) << 4))
|
||||
#define PCIE_WIN04_BASE_OFF(n) (0x1824 + ((n) << 4))
|
||||
#define PCIE_WIN04_REMAP_OFF(n) (0x182c + ((n) << 4))
|
||||
#define PCIE_WIN5_CTRL_OFF 0x1880
|
||||
#define PCIE_WIN5_BASE_OFF 0x1884
|
||||
#define PCIE_WIN5_REMAP_OFF 0x188c
|
||||
#define PCIE_CONF_ADDR_OFF 0x18f8
|
||||
#define PCIE_CONF_ADDR_EN BIT(31)
|
||||
#define PCIE_CONF_REG(r) ((((r) & 0xf00) << 16) | ((r) & 0xfc))
|
||||
#define PCIE_CONF_BUS(b) (((b) & 0xff) << 16)
|
||||
#define PCIE_CONF_DEV(d) (((d) & 0x1f) << 11)
|
||||
#define PCIE_CONF_FUNC(f) (((f) & 0x7) << 8)
|
||||
#define PCIE_CONF_ADDR(dev, reg) \
|
||||
(PCIE_CONF_BUS(PCI_BUS(dev)) | PCIE_CONF_DEV(PCI_DEV(dev)) | \
|
||||
PCIE_CONF_FUNC(PCI_FUNC(dev)) | PCIE_CONF_REG(reg) | \
|
||||
PCIE_CONF_ADDR_EN)
|
||||
#define PCIE_CONF_DATA_OFF 0x18fc
|
||||
#define PCIE_MASK_OFF 0x1910
|
||||
#define PCIE_MASK_ENABLE_INTS (0xf << 24)
|
||||
#define PCIE_CTRL_OFF 0x1a00
|
||||
#define PCIE_CTRL_X1_MODE BIT(0)
|
||||
#define PCIE_STAT_OFF 0x1a04
|
||||
#define PCIE_STAT_BUS (0xff << 8)
|
||||
#define PCIE_STAT_DEV (0x1f << 16)
|
||||
#define PCIE_STAT_LINK_DOWN BIT(0)
|
||||
#define PCIE_DEBUG_CTRL 0x1a60
|
||||
#define PCIE_DEBUG_SOFT_RESET BIT(20)
|
||||
|
||||
struct mvebu_pcie {
|
||||
struct pci_controller hose;
|
||||
void __iomem *base;
|
||||
void __iomem *membase;
|
||||
struct resource mem;
|
||||
void __iomem *iobase;
|
||||
u32 port;
|
||||
u32 lane;
|
||||
int devfn;
|
||||
u32 lane_mask;
|
||||
pci_dev_t dev;
|
||||
char name[16];
|
||||
unsigned int mem_target;
|
||||
unsigned int mem_attr;
|
||||
};
|
||||
|
||||
/*
|
||||
* MVEBU PCIe controller needs MEMORY and I/O BARs to be mapped
|
||||
* into SoCs address space. Each controller will map 128M of MEM
|
||||
* and 64K of I/O space when registered.
|
||||
*/
|
||||
static void __iomem *mvebu_pcie_membase = (void __iomem *)MBUS_PCI_MEM_BASE;
|
||||
#define PCIE_MEM_SIZE (128 << 20)
|
||||
|
||||
static inline bool mvebu_pcie_link_up(struct mvebu_pcie *pcie)
|
||||
{
|
||||
u32 val;
|
||||
val = readl(pcie->base + PCIE_STAT_OFF);
|
||||
return !(val & PCIE_STAT_LINK_DOWN);
|
||||
}
|
||||
|
||||
static void mvebu_pcie_set_local_bus_nr(struct mvebu_pcie *pcie, int busno)
|
||||
{
|
||||
u32 stat;
|
||||
|
||||
stat = readl(pcie->base + PCIE_STAT_OFF);
|
||||
stat &= ~PCIE_STAT_BUS;
|
||||
stat |= busno << 8;
|
||||
writel(stat, pcie->base + PCIE_STAT_OFF);
|
||||
}
|
||||
|
||||
static void mvebu_pcie_set_local_dev_nr(struct mvebu_pcie *pcie, int devno)
|
||||
{
|
||||
u32 stat;
|
||||
|
||||
stat = readl(pcie->base + PCIE_STAT_OFF);
|
||||
stat &= ~PCIE_STAT_DEV;
|
||||
stat |= devno << 16;
|
||||
writel(stat, pcie->base + PCIE_STAT_OFF);
|
||||
}
|
||||
|
||||
static int mvebu_pcie_get_local_bus_nr(struct mvebu_pcie *pcie)
|
||||
{
|
||||
u32 stat;
|
||||
|
||||
stat = readl(pcie->base + PCIE_STAT_OFF);
|
||||
return (stat & PCIE_STAT_BUS) >> 8;
|
||||
}
|
||||
|
||||
static int mvebu_pcie_get_local_dev_nr(struct mvebu_pcie *pcie)
|
||||
{
|
||||
u32 stat;
|
||||
|
||||
stat = readl(pcie->base + PCIE_STAT_OFF);
|
||||
return (stat & PCIE_STAT_DEV) >> 16;
|
||||
}
|
||||
|
||||
static inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose)
|
||||
{
|
||||
return container_of(hose, struct mvebu_pcie, hose);
|
||||
}
|
||||
|
||||
static int mvebu_pcie_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct mvebu_pcie *pcie = dev_get_platdata(bus);
|
||||
int local_bus = PCI_BUS(pcie->dev);
|
||||
int local_dev = PCI_DEV(pcie->dev);
|
||||
u32 reg;
|
||||
u32 data;
|
||||
|
||||
debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
|
||||
|
||||
/* Only allow one other device besides the local one on the local bus */
|
||||
if (PCI_BUS(bdf) == local_bus && PCI_DEV(bdf) != local_dev) {
|
||||
if (local_dev == 0 && PCI_DEV(bdf) != 1) {
|
||||
debug("- out of range\n");
|
||||
/*
|
||||
* If local dev is 0, the first other dev can
|
||||
* only be 1
|
||||
*/
|
||||
*valuep = pci_get_ff(size);
|
||||
return 0;
|
||||
} else if (local_dev != 0 && PCI_DEV(bdf) != 0) {
|
||||
debug("- out of range\n");
|
||||
/*
|
||||
* If local dev is not 0, the first other dev can
|
||||
* only be 0
|
||||
*/
|
||||
*valuep = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* write address */
|
||||
reg = PCIE_CONF_ADDR(bdf, offset);
|
||||
writel(reg, pcie->base + PCIE_CONF_ADDR_OFF);
|
||||
data = readl(pcie->base + PCIE_CONF_DATA_OFF);
|
||||
debug("(addr,val)=(0x%04x, 0x%08x)\n", offset, data);
|
||||
*valuep = pci_conv_32_to_size(data, offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_pcie_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct mvebu_pcie *pcie = dev_get_platdata(bus);
|
||||
int local_bus = PCI_BUS(pcie->dev);
|
||||
int local_dev = PCI_DEV(pcie->dev);
|
||||
u32 data;
|
||||
|
||||
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);
|
||||
|
||||
/* Only allow one other device besides the local one on the local bus */
|
||||
if (PCI_BUS(bdf) == local_bus && PCI_DEV(bdf) != local_dev) {
|
||||
if (local_dev == 0 && PCI_DEV(bdf) != 1) {
|
||||
/*
|
||||
* If local dev is 0, the first other dev can
|
||||
* only be 1
|
||||
*/
|
||||
return 0;
|
||||
} else if (local_dev != 0 && PCI_DEV(bdf) != 0) {
|
||||
/*
|
||||
* If local dev is not 0, the first other dev can
|
||||
* only be 0
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
writel(PCIE_CONF_ADDR(bdf, offset), pcie->base + PCIE_CONF_ADDR_OFF);
|
||||
data = pci_conv_size_to_32(0, value, offset, size);
|
||||
writel(data, pcie->base + PCIE_CONF_DATA_OFF);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup PCIE BARs and Address Decode Wins:
|
||||
* BAR[0,2] -> disabled, BAR[1] -> covers all DRAM banks
|
||||
* WIN[0-3] -> DRAM bank[0-3]
|
||||
*/
|
||||
static void mvebu_pcie_setup_wins(struct mvebu_pcie *pcie)
|
||||
{
|
||||
const struct mbus_dram_target_info *dram = mvebu_mbus_dram_info();
|
||||
u32 size;
|
||||
int i;
|
||||
|
||||
/* First, disable and clear BARs and windows. */
|
||||
for (i = 1; i < 3; i++) {
|
||||
writel(0, pcie->base + PCIE_BAR_CTRL_OFF(i));
|
||||
writel(0, pcie->base + PCIE_BAR_LO_OFF(i));
|
||||
writel(0, pcie->base + PCIE_BAR_HI_OFF(i));
|
||||
}
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
writel(0, pcie->base + PCIE_WIN04_CTRL_OFF(i));
|
||||
writel(0, pcie->base + PCIE_WIN04_BASE_OFF(i));
|
||||
writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i));
|
||||
}
|
||||
|
||||
writel(0, pcie->base + PCIE_WIN5_CTRL_OFF);
|
||||
writel(0, pcie->base + PCIE_WIN5_BASE_OFF);
|
||||
writel(0, pcie->base + PCIE_WIN5_REMAP_OFF);
|
||||
|
||||
/* Setup windows for DDR banks. Count total DDR size on the fly. */
|
||||
size = 0;
|
||||
for (i = 0; i < dram->num_cs; i++) {
|
||||
const struct mbus_dram_window *cs = dram->cs + i;
|
||||
|
||||
writel(cs->base & 0xffff0000,
|
||||
pcie->base + PCIE_WIN04_BASE_OFF(i));
|
||||
writel(0, pcie->base + PCIE_WIN04_REMAP_OFF(i));
|
||||
writel(((cs->size - 1) & 0xffff0000) |
|
||||
(cs->mbus_attr << 8) |
|
||||
(dram->mbus_dram_target_id << 4) | 1,
|
||||
pcie->base + PCIE_WIN04_CTRL_OFF(i));
|
||||
|
||||
size += cs->size;
|
||||
}
|
||||
|
||||
/* Round up 'size' to the nearest power of two. */
|
||||
if ((size & (size - 1)) != 0)
|
||||
size = 1 << fls(size);
|
||||
|
||||
/* Setup BAR[1] to all DRAM banks. */
|
||||
writel(dram->cs[0].base | 0xc, pcie->base + PCIE_BAR_LO_OFF(1));
|
||||
writel(0, pcie->base + PCIE_BAR_HI_OFF(1));
|
||||
writel(((size - 1) & 0xffff0000) | 0x1,
|
||||
pcie->base + PCIE_BAR_CTRL_OFF(1));
|
||||
}
|
||||
|
||||
static int mvebu_pcie_probe(struct udevice *dev)
|
||||
{
|
||||
struct mvebu_pcie *pcie = dev_get_platdata(dev);
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
||||
static int bus;
|
||||
u32 reg;
|
||||
|
||||
debug("%s: PCIe %d.%d - up, base %08x\n", __func__,
|
||||
pcie->port, pcie->lane, (u32)pcie->base);
|
||||
|
||||
/* Read Id info and local bus/dev */
|
||||
debug("direct conf read %08x, local bus %d, local dev %d\n",
|
||||
readl(pcie->base), mvebu_pcie_get_local_bus_nr(pcie),
|
||||
mvebu_pcie_get_local_dev_nr(pcie));
|
||||
|
||||
mvebu_pcie_set_local_bus_nr(pcie, bus);
|
||||
mvebu_pcie_set_local_dev_nr(pcie, 0);
|
||||
pcie->dev = PCI_BDF(bus, 0, 0);
|
||||
|
||||
pcie->mem.start = (u32)mvebu_pcie_membase;
|
||||
pcie->mem.end = pcie->mem.start + PCIE_MEM_SIZE - 1;
|
||||
mvebu_pcie_membase += PCIE_MEM_SIZE;
|
||||
|
||||
if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr,
|
||||
(phys_addr_t)pcie->mem.start,
|
||||
PCIE_MEM_SIZE)) {
|
||||
printf("PCIe unable to add mbus window for mem at %08x+%08x\n",
|
||||
(u32)pcie->mem.start, PCIE_MEM_SIZE);
|
||||
}
|
||||
|
||||
/* Setup windows and configure host bridge */
|
||||
mvebu_pcie_setup_wins(pcie);
|
||||
|
||||
/* Master + slave enable. */
|
||||
reg = readl(pcie->base + PCIE_CMD_OFF);
|
||||
reg |= PCI_COMMAND_MEMORY;
|
||||
reg |= PCI_COMMAND_MASTER;
|
||||
reg |= BIT(10); /* disable interrupts */
|
||||
writel(reg, pcie->base + PCIE_CMD_OFF);
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(hose->regions + 0, pcie->mem.start,
|
||||
pcie->mem.start, PCIE_MEM_SIZE, PCI_REGION_MEM);
|
||||
pci_set_region(hose->regions + 1,
|
||||
0, 0,
|
||||
gd->ram_size,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
hose->region_count = 2;
|
||||
|
||||
/* Set BAR0 to internal registers */
|
||||
writel(SOC_REGS_PHY_BASE, pcie->base + PCIE_BAR_LO_OFF(0));
|
||||
writel(0, pcie->base + PCIE_BAR_HI_OFF(0));
|
||||
|
||||
bus++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mvebu_pcie_port_parse_dt(ofnode node, struct mvebu_pcie *pcie)
|
||||
{
|
||||
const u32 *addr;
|
||||
int len;
|
||||
|
||||
addr = ofnode_get_property(node, "assigned-addresses", &len);
|
||||
if (!addr) {
|
||||
pr_err("property \"assigned-addresses\" not found");
|
||||
return -FDT_ERR_NOTFOUND;
|
||||
}
|
||||
|
||||
pcie->base = (void *)(fdt32_to_cpu(addr[2]) + SOC_REGS_PHY_BASE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define DT_FLAGS_TO_TYPE(flags) (((flags) >> 24) & 0x03)
|
||||
#define DT_TYPE_IO 0x1
|
||||
#define DT_TYPE_MEM32 0x2
|
||||
#define DT_CPUADDR_TO_TARGET(cpuaddr) (((cpuaddr) >> 56) & 0xFF)
|
||||
#define DT_CPUADDR_TO_ATTR(cpuaddr) (((cpuaddr) >> 48) & 0xFF)
|
||||
|
||||
static int mvebu_get_tgt_attr(ofnode node, int devfn,
|
||||
unsigned long type,
|
||||
unsigned int *tgt,
|
||||
unsigned int *attr)
|
||||
{
|
||||
const int na = 3, ns = 2;
|
||||
const __be32 *range;
|
||||
int rlen, nranges, rangesz, pna, i;
|
||||
|
||||
*tgt = -1;
|
||||
*attr = -1;
|
||||
|
||||
range = ofnode_get_property(node, "ranges", &rlen);
|
||||
if (!range)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Linux uses of_n_addr_cells() to get the number of address cells
|
||||
* here. Currently this function is only available in U-Boot when
|
||||
* CONFIG_OF_LIVE is enabled. Until this is enabled for MVEBU in
|
||||
* general, lets't hardcode the "pna" value in the U-Boot code.
|
||||
*/
|
||||
pna = 2; /* hardcoded for now because of lack of of_n_addr_cells() */
|
||||
rangesz = pna + na + ns;
|
||||
nranges = rlen / sizeof(__be32) / rangesz;
|
||||
|
||||
for (i = 0; i < nranges; i++, range += rangesz) {
|
||||
u32 flags = of_read_number(range, 1);
|
||||
u32 slot = of_read_number(range + 1, 1);
|
||||
u64 cpuaddr = of_read_number(range + na, pna);
|
||||
unsigned long rtype;
|
||||
|
||||
if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_IO)
|
||||
rtype = IORESOURCE_IO;
|
||||
else if (DT_FLAGS_TO_TYPE(flags) == DT_TYPE_MEM32)
|
||||
rtype = IORESOURCE_MEM;
|
||||
else
|
||||
continue;
|
||||
|
||||
/*
|
||||
* The Linux code used PCI_SLOT() here, which expects devfn
|
||||
* in bits 7..0. PCI_DEV() in U-Boot is similar to PCI_SLOT(),
|
||||
* only expects devfn in 15..8, where its saved in this driver.
|
||||
*/
|
||||
if (slot == PCI_DEV(devfn) && type == rtype) {
|
||||
*tgt = DT_CPUADDR_TO_TARGET(cpuaddr);
|
||||
*attr = DT_CPUADDR_TO_ATTR(cpuaddr);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int mvebu_pcie_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct mvebu_pcie *pcie = dev_get_platdata(dev);
|
||||
int ret = 0;
|
||||
|
||||
/* Get port number, lane number and memory target / attr */
|
||||
if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-port",
|
||||
&pcie->port)) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (ofnode_read_u32(dev_ofnode(dev), "marvell,pcie-lane", &pcie->lane))
|
||||
pcie->lane = 0;
|
||||
|
||||
sprintf(pcie->name, "pcie%d.%d", pcie->port, pcie->lane);
|
||||
|
||||
/* pci_get_devfn() returns devfn in bits 15..8, see PCI_DEV usage */
|
||||
pcie->devfn = pci_get_devfn(dev);
|
||||
if (pcie->devfn < 0) {
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn,
|
||||
IORESOURCE_MEM,
|
||||
&pcie->mem_target, &pcie->mem_attr);
|
||||
if (ret < 0) {
|
||||
printf("%s: cannot get tgt/attr for mem window\n", pcie->name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Parse PCIe controller register base from DT */
|
||||
ret = mvebu_pcie_port_parse_dt(dev_ofnode(dev), pcie);
|
||||
if (ret < 0)
|
||||
goto err;
|
||||
|
||||
/* Check link and skip ports that have no link */
|
||||
if (!mvebu_pcie_link_up(pcie)) {
|
||||
debug("%s: %s - down\n", __func__, pcie->name);
|
||||
ret = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops mvebu_pcie_ops = {
|
||||
.read_config = mvebu_pcie_read_config,
|
||||
.write_config = mvebu_pcie_write_config,
|
||||
};
|
||||
|
||||
static struct driver pcie_mvebu_drv = {
|
||||
.name = "pcie_mvebu",
|
||||
.id = UCLASS_PCI,
|
||||
.ops = &mvebu_pcie_ops,
|
||||
.probe = mvebu_pcie_probe,
|
||||
.ofdata_to_platdata = mvebu_pcie_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct mvebu_pcie),
|
||||
};
|
||||
|
||||
/*
|
||||
* Use a MISC device to bind the n instances (child nodes) of the
|
||||
* PCIe base controller in UCLASS_PCI.
|
||||
*/
|
||||
static int mvebu_pcie_bind(struct udevice *parent)
|
||||
{
|
||||
struct mvebu_pcie *pcie;
|
||||
struct uclass_driver *drv;
|
||||
struct udevice *dev;
|
||||
ofnode subnode;
|
||||
|
||||
/* Lookup eth driver */
|
||||
drv = lists_uclass_lookup(UCLASS_PCI);
|
||||
if (!drv) {
|
||||
puts("Cannot find PCI driver\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
ofnode_for_each_subnode(subnode, dev_ofnode(parent)) {
|
||||
if (!ofnode_is_available(subnode))
|
||||
continue;
|
||||
|
||||
pcie = calloc(1, sizeof(*pcie));
|
||||
if (!pcie)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Create child device UCLASS_PCI and bind it */
|
||||
device_bind_ofnode(parent, &pcie_mvebu_drv, pcie->name, pcie,
|
||||
subnode, &dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mvebu_pcie_ids[] = {
|
||||
{ .compatible = "marvell,armada-xp-pcie" },
|
||||
{ .compatible = "marvell,armada-370-pcie" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_mvebu_base) = {
|
||||
.name = "pcie_mvebu_base",
|
||||
.id = UCLASS_MISC,
|
||||
.of_match = mvebu_pcie_ids,
|
||||
.bind = mvebu_pcie_bind,
|
||||
};
|
||||
@@ -0,0 +1,383 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (C) 2014 Google, Inc
|
||||
*
|
||||
* From coreboot, originally based on the Linux kernel (drivers/pci/pci.c).
|
||||
*
|
||||
* Modifications are:
|
||||
* Copyright (C) 2003-2004 Linux Networx
|
||||
* (Written by Eric Biederman <ebiederman@lnxi.com> for Linux Networx)
|
||||
* Copyright (C) 2003-2006 Ronald G. Minnich <rminnich@gmail.com>
|
||||
* Copyright (C) 2004-2005 Li-Ta Lo <ollie@lanl.gov>
|
||||
* Copyright (C) 2005-2006 Tyan
|
||||
* (Written by Yinghai Lu <yhlu@tyan.com> for Tyan)
|
||||
* Copyright (C) 2005-2009 coresystems GmbH
|
||||
* (Written by Stefan Reinauer <stepan@coresystems.de> for coresystems GmbH)
|
||||
*
|
||||
* PCI Bus Services, see include/linux/pci.h for further explanation.
|
||||
*
|
||||
* Copyright 1993 -- 1997 Drew Eckhardt, Frederic Potter,
|
||||
* David Mosberger-Tang
|
||||
*
|
||||
* Copyright 1997 -- 1999 Martin Mares <mj@atrey.karlin.mff.cuni.cz>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <bios_emul.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <malloc.h>
|
||||
#include <pci.h>
|
||||
#include <pci_rom.h>
|
||||
#include <vbe.h>
|
||||
#include <video.h>
|
||||
#include <video_fb.h>
|
||||
#include <linux/screen_info.h>
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
#include <acpi_s3.h>
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
#endif
|
||||
|
||||
__weak bool board_should_run_oprom(struct udevice *dev)
|
||||
{
|
||||
#if defined(CONFIG_X86) && defined(CONFIG_HAVE_ACPI_RESUME)
|
||||
if (gd->arch.prev_sleep_state == ACPI_S3) {
|
||||
if (IS_ENABLED(CONFIG_S3_VGA_ROM_RUN))
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
__weak bool board_should_load_oprom(struct udevice *dev)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
__weak uint32_t board_map_oprom_vendev(uint32_t vendev)
|
||||
{
|
||||
return vendev;
|
||||
}
|
||||
|
||||
static int pci_rom_probe(struct udevice *dev, struct pci_rom_header **hdrp)
|
||||
{
|
||||
struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
|
||||
struct pci_rom_header *rom_header;
|
||||
struct pci_rom_data *rom_data;
|
||||
u16 rom_vendor, rom_device;
|
||||
u32 rom_class;
|
||||
u32 vendev;
|
||||
u32 mapped_vendev;
|
||||
u32 rom_address;
|
||||
|
||||
vendev = pplat->vendor << 16 | pplat->device;
|
||||
mapped_vendev = board_map_oprom_vendev(vendev);
|
||||
if (vendev != mapped_vendev)
|
||||
debug("Device ID mapped to %#08x\n", mapped_vendev);
|
||||
|
||||
#ifdef CONFIG_VGA_BIOS_ADDR
|
||||
rom_address = CONFIG_VGA_BIOS_ADDR;
|
||||
#else
|
||||
|
||||
dm_pci_read_config32(dev, PCI_ROM_ADDRESS, &rom_address);
|
||||
if (rom_address == 0x00000000 || rom_address == 0xffffffff) {
|
||||
debug("%s: rom_address=%x\n", __func__, rom_address);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* Enable expansion ROM address decoding. */
|
||||
dm_pci_write_config32(dev, PCI_ROM_ADDRESS,
|
||||
rom_address | PCI_ROM_ADDRESS_ENABLE);
|
||||
#endif
|
||||
debug("Option ROM address %x\n", rom_address);
|
||||
rom_header = (struct pci_rom_header *)(unsigned long)rom_address;
|
||||
|
||||
debug("PCI expansion ROM, signature %#04x, INIT size %#04x, data ptr %#04x\n",
|
||||
le16_to_cpu(rom_header->signature),
|
||||
rom_header->size * 512, le16_to_cpu(rom_header->data));
|
||||
|
||||
if (le16_to_cpu(rom_header->signature) != PCI_ROM_HDR) {
|
||||
printf("Incorrect expansion ROM header signature %04x\n",
|
||||
le16_to_cpu(rom_header->signature));
|
||||
#ifndef CONFIG_VGA_BIOS_ADDR
|
||||
/* Disable expansion ROM address decoding */
|
||||
dm_pci_write_config32(dev, PCI_ROM_ADDRESS, rom_address);
|
||||
#endif
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rom_data = (((void *)rom_header) + le16_to_cpu(rom_header->data));
|
||||
rom_vendor = le16_to_cpu(rom_data->vendor);
|
||||
rom_device = le16_to_cpu(rom_data->device);
|
||||
|
||||
debug("PCI ROM image, vendor ID %04x, device ID %04x,\n",
|
||||
rom_vendor, rom_device);
|
||||
|
||||
/* If the device id is mapped, a mismatch is expected */
|
||||
if ((pplat->vendor != rom_vendor || pplat->device != rom_device) &&
|
||||
(vendev == mapped_vendev)) {
|
||||
printf("ID mismatch: vendor ID %04x, device ID %04x\n",
|
||||
rom_vendor, rom_device);
|
||||
/* Continue anyway */
|
||||
}
|
||||
|
||||
rom_class = (le16_to_cpu(rom_data->class_hi) << 8) | rom_data->class_lo;
|
||||
debug("PCI ROM image, Class Code %06x, Code Type %02x\n",
|
||||
rom_class, rom_data->type);
|
||||
|
||||
if (pplat->class != rom_class) {
|
||||
debug("Class Code mismatch ROM %06x, dev %06x\n",
|
||||
rom_class, pplat->class);
|
||||
}
|
||||
*hdrp = rom_header;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_rom_load() - Load a ROM image and return a pointer to it
|
||||
*
|
||||
* @rom_header: Pointer to ROM image
|
||||
* @ram_headerp: Returns a pointer to the image in RAM
|
||||
* @allocedp: Returns true if @ram_headerp was allocated and needs
|
||||
* to be freed
|
||||
* @return 0 if OK, -ve on error. Note that @allocedp is set up regardless of
|
||||
* the error state. Even if this function returns an error, it may have
|
||||
* allocated memory.
|
||||
*/
|
||||
static int pci_rom_load(struct pci_rom_header *rom_header,
|
||||
struct pci_rom_header **ram_headerp, bool *allocedp)
|
||||
{
|
||||
struct pci_rom_data *rom_data;
|
||||
unsigned int rom_size;
|
||||
unsigned int image_size = 0;
|
||||
void *target;
|
||||
|
||||
*allocedp = false;
|
||||
do {
|
||||
/* Get next image, until we see an x86 version */
|
||||
rom_header = (struct pci_rom_header *)((void *)rom_header +
|
||||
image_size);
|
||||
|
||||
rom_data = (struct pci_rom_data *)((void *)rom_header +
|
||||
le16_to_cpu(rom_header->data));
|
||||
|
||||
image_size = le16_to_cpu(rom_data->ilen) * 512;
|
||||
} while ((rom_data->type != 0) && (rom_data->indicator == 0));
|
||||
|
||||
if (rom_data->type != 0)
|
||||
return -EACCES;
|
||||
|
||||
rom_size = rom_header->size * 512;
|
||||
|
||||
#ifdef PCI_VGA_RAM_IMAGE_START
|
||||
target = (void *)PCI_VGA_RAM_IMAGE_START;
|
||||
#else
|
||||
target = (void *)malloc(rom_size);
|
||||
if (!target)
|
||||
return -ENOMEM;
|
||||
*allocedp = true;
|
||||
#endif
|
||||
if (target != rom_header) {
|
||||
ulong start = get_timer(0);
|
||||
|
||||
debug("Copying VGA ROM Image from %p to %p, 0x%x bytes\n",
|
||||
rom_header, target, rom_size);
|
||||
memcpy(target, rom_header, rom_size);
|
||||
if (memcmp(target, rom_header, rom_size)) {
|
||||
printf("VGA ROM copy failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
debug("Copy took %lums\n", get_timer(start));
|
||||
}
|
||||
*ram_headerp = target;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct vbe_mode_info mode_info;
|
||||
|
||||
void setup_video(struct screen_info *screen_info)
|
||||
{
|
||||
struct vesa_mode_info *vesa = &mode_info.vesa;
|
||||
|
||||
/* Sanity test on VESA parameters */
|
||||
if (!vesa->x_resolution || !vesa->y_resolution)
|
||||
return;
|
||||
|
||||
screen_info->orig_video_isVGA = VIDEO_TYPE_VLFB;
|
||||
|
||||
screen_info->lfb_width = vesa->x_resolution;
|
||||
screen_info->lfb_height = vesa->y_resolution;
|
||||
screen_info->lfb_depth = vesa->bits_per_pixel;
|
||||
screen_info->lfb_linelength = vesa->bytes_per_scanline;
|
||||
screen_info->lfb_base = vesa->phys_base_ptr;
|
||||
screen_info->lfb_size =
|
||||
ALIGN(screen_info->lfb_linelength * screen_info->lfb_height,
|
||||
65536);
|
||||
screen_info->lfb_size >>= 16;
|
||||
screen_info->red_size = vesa->red_mask_size;
|
||||
screen_info->red_pos = vesa->red_mask_pos;
|
||||
screen_info->green_size = vesa->green_mask_size;
|
||||
screen_info->green_pos = vesa->green_mask_pos;
|
||||
screen_info->blue_size = vesa->blue_mask_size;
|
||||
screen_info->blue_pos = vesa->blue_mask_pos;
|
||||
screen_info->rsvd_size = vesa->reserved_mask_size;
|
||||
screen_info->rsvd_pos = vesa->reserved_mask_pos;
|
||||
}
|
||||
|
||||
int dm_pci_run_vga_bios(struct udevice *dev, int (*int15_handler)(void),
|
||||
int exec_method)
|
||||
{
|
||||
struct pci_child_platdata *pplat = dev_get_parent_platdata(dev);
|
||||
struct pci_rom_header *rom = NULL, *ram = NULL;
|
||||
int vesa_mode = -1;
|
||||
bool emulate, alloced;
|
||||
int ret;
|
||||
|
||||
/* Only execute VGA ROMs */
|
||||
if (((pplat->class >> 8) ^ PCI_CLASS_DISPLAY_VGA) & 0xff00) {
|
||||
debug("%s: Class %#x, should be %#x\n", __func__, pplat->class,
|
||||
PCI_CLASS_DISPLAY_VGA);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!board_should_load_oprom(dev))
|
||||
return log_msg_ret("Should not load OPROM", -ENXIO);
|
||||
|
||||
ret = pci_rom_probe(dev, &rom);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = pci_rom_load(rom, &ram, &alloced);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!board_should_run_oprom(dev)) {
|
||||
ret = -ENXIO;
|
||||
goto err;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_FRAMEBUFFER_SET_VESA_MODE) && \
|
||||
defined(CONFIG_FRAMEBUFFER_VESA_MODE)
|
||||
vesa_mode = CONFIG_FRAMEBUFFER_VESA_MODE;
|
||||
#endif
|
||||
debug("Selected vesa mode %#x\n", vesa_mode);
|
||||
|
||||
if (exec_method & PCI_ROM_USE_NATIVE) {
|
||||
#ifdef CONFIG_X86
|
||||
emulate = false;
|
||||
#else
|
||||
if (!(exec_method & PCI_ROM_ALLOW_FALLBACK)) {
|
||||
printf("BIOS native execution is only available on x86\n");
|
||||
ret = -ENOSYS;
|
||||
goto err;
|
||||
}
|
||||
emulate = true;
|
||||
#endif
|
||||
} else {
|
||||
#ifdef CONFIG_BIOSEMU
|
||||
emulate = true;
|
||||
#else
|
||||
if (!(exec_method & PCI_ROM_ALLOW_FALLBACK)) {
|
||||
printf("BIOS emulation not available - see CONFIG_BIOSEMU\n");
|
||||
ret = -ENOSYS;
|
||||
goto err;
|
||||
}
|
||||
emulate = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (emulate) {
|
||||
#ifdef CONFIG_BIOSEMU
|
||||
BE_VGAInfo *info;
|
||||
|
||||
ret = biosemu_setup(dev, &info);
|
||||
if (ret)
|
||||
goto err;
|
||||
biosemu_set_interrupt_handler(0x15, int15_handler);
|
||||
ret = biosemu_run(dev, (uchar *)ram, 1 << 16, info,
|
||||
true, vesa_mode, &mode_info);
|
||||
if (ret)
|
||||
goto err;
|
||||
#endif
|
||||
} else {
|
||||
#if defined(CONFIG_X86) && (CONFIG_IS_ENABLED(X86_32BIT_INIT) || CONFIG_TPL)
|
||||
bios_set_interrupt_handler(0x15, int15_handler);
|
||||
|
||||
bios_run_on_x86(dev, (unsigned long)ram, vesa_mode,
|
||||
&mode_info);
|
||||
#endif
|
||||
}
|
||||
debug("Final vesa mode %#x\n", mode_info.video_mode);
|
||||
ret = 0;
|
||||
|
||||
err:
|
||||
if (alloced)
|
||||
free(ram);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DM_VIDEO
|
||||
int vbe_setup_video_priv(struct vesa_mode_info *vesa,
|
||||
struct video_priv *uc_priv,
|
||||
struct video_uc_platdata *plat)
|
||||
{
|
||||
if (!vesa->x_resolution)
|
||||
return log_msg_ret("No x resolution", -ENXIO);
|
||||
uc_priv->xsize = vesa->x_resolution;
|
||||
uc_priv->ysize = vesa->y_resolution;
|
||||
uc_priv->line_length = vesa->bytes_per_scanline;
|
||||
switch (vesa->bits_per_pixel) {
|
||||
case 32:
|
||||
case 24:
|
||||
uc_priv->bpix = VIDEO_BPP32;
|
||||
break;
|
||||
case 16:
|
||||
uc_priv->bpix = VIDEO_BPP16;
|
||||
break;
|
||||
default:
|
||||
return -EPROTONOSUPPORT;
|
||||
}
|
||||
plat->base = vesa->phys_base_ptr;
|
||||
plat->size = vesa->bytes_per_scanline * vesa->y_resolution;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int vbe_setup_video(struct udevice *dev, int (*int15_handler)(void))
|
||||
{
|
||||
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
|
||||
struct video_priv *uc_priv = dev_get_uclass_priv(dev);
|
||||
int ret;
|
||||
|
||||
/* If we are running from EFI or coreboot, this can't work */
|
||||
if (!ll_boot_init()) {
|
||||
printf("Not available (previous bootloader prevents it)\n");
|
||||
return -EPERM;
|
||||
}
|
||||
bootstage_start(BOOTSTAGE_ID_ACCUM_LCD, "vesa display");
|
||||
ret = dm_pci_run_vga_bios(dev, int15_handler, PCI_ROM_USE_NATIVE |
|
||||
PCI_ROM_ALLOW_FALLBACK);
|
||||
bootstage_accum(BOOTSTAGE_ID_ACCUM_LCD);
|
||||
if (ret) {
|
||||
debug("failed to run video BIOS: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = vbe_setup_video_priv(&mode_info.vesa, uc_priv, plat);
|
||||
if (ret) {
|
||||
debug("No video mode configured\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
printf("Video: %dx%dx%d\n", uc_priv->xsize, uc_priv->ysize,
|
||||
mode_info.vesa.bits_per_pixel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,136 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2014 Google, Inc
|
||||
* Written by Simon Glass <sjg@chromium.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <fdtdec.h>
|
||||
#include <pci.h>
|
||||
|
||||
#define FDT_DEV_INFO_CELLS 4
|
||||
#define FDT_DEV_INFO_SIZE (FDT_DEV_INFO_CELLS * sizeof(u32))
|
||||
|
||||
#define SANDBOX_PCI_DEVFN(d, f) ((d << 3) | f)
|
||||
|
||||
struct sandbox_pci_priv {
|
||||
struct {
|
||||
u16 vendor;
|
||||
u16 device;
|
||||
} vendev[256];
|
||||
};
|
||||
|
||||
static int sandbox_pci_write_config(struct udevice *bus, pci_dev_t devfn,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_emul_ops *ops;
|
||||
struct udevice *container, *emul;
|
||||
int ret;
|
||||
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &container, &emul);
|
||||
if (ret)
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
ops = pci_get_emul_ops(emul);
|
||||
if (!ops || !ops->write_config)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->write_config(emul, offset, value, size);
|
||||
}
|
||||
|
||||
static int sandbox_pci_read_config(struct udevice *bus, pci_dev_t devfn,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct dm_pci_emul_ops *ops;
|
||||
struct udevice *container, *emul;
|
||||
struct sandbox_pci_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
/* Prepare the default response */
|
||||
*valuep = pci_get_ff(size);
|
||||
ret = sandbox_pci_get_emul(bus, devfn, &container, &emul);
|
||||
if (ret) {
|
||||
if (!container) {
|
||||
u16 vendor, device;
|
||||
|
||||
devfn = SANDBOX_PCI_DEVFN(PCI_DEV(devfn),
|
||||
PCI_FUNC(devfn));
|
||||
vendor = priv->vendev[devfn].vendor;
|
||||
device = priv->vendev[devfn].device;
|
||||
if (offset == PCI_VENDOR_ID && vendor)
|
||||
*valuep = vendor;
|
||||
else if (offset == PCI_DEVICE_ID && device)
|
||||
*valuep = device;
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
return ret == -ENODEV ? 0 : ret;
|
||||
}
|
||||
}
|
||||
ops = pci_get_emul_ops(emul);
|
||||
if (!ops || !ops->read_config)
|
||||
return -ENOSYS;
|
||||
|
||||
return ops->read_config(emul, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int sandbox_pci_probe(struct udevice *dev)
|
||||
{
|
||||
struct sandbox_pci_priv *priv = dev_get_priv(dev);
|
||||
const fdt32_t *cell;
|
||||
u8 pdev, pfn, devfn;
|
||||
int len;
|
||||
|
||||
cell = ofnode_get_property(dev_ofnode(dev), "sandbox,dev-info", &len);
|
||||
if (!cell)
|
||||
return 0;
|
||||
|
||||
if ((len % FDT_DEV_INFO_SIZE) == 0) {
|
||||
int num = len / FDT_DEV_INFO_SIZE;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
debug("dev info #%d: %02x %02x %04x %04x\n", i,
|
||||
fdt32_to_cpu(cell[0]), fdt32_to_cpu(cell[1]),
|
||||
fdt32_to_cpu(cell[2]), fdt32_to_cpu(cell[3]));
|
||||
|
||||
pdev = fdt32_to_cpu(cell[0]);
|
||||
pfn = fdt32_to_cpu(cell[1]);
|
||||
if (pdev > 31 || pfn > 7)
|
||||
continue;
|
||||
devfn = SANDBOX_PCI_DEVFN(pdev, pfn);
|
||||
priv->vendev[devfn].vendor = fdt32_to_cpu(cell[2]);
|
||||
priv->vendev[devfn].device = fdt32_to_cpu(cell[3]);
|
||||
|
||||
cell += FDT_DEV_INFO_CELLS;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops sandbox_pci_ops = {
|
||||
.read_config = sandbox_pci_read_config,
|
||||
.write_config = sandbox_pci_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id sandbox_pci_ids[] = {
|
||||
{ .compatible = "sandbox,pci" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_sandbox) = {
|
||||
.name = "pci_sandbox",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = sandbox_pci_ids,
|
||||
.ops = &sandbox_pci_ops,
|
||||
.probe = sandbox_pci_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct sandbox_pci_priv),
|
||||
|
||||
/* Attach an emulator if we can */
|
||||
.child_post_bind = dm_scan_fdt_dev,
|
||||
.per_child_platdata_auto_alloc_size =
|
||||
sizeof(struct pci_child_platdata),
|
||||
};
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* SH4 PCI Controller (PCIC) for U-Boot.
|
||||
* (C) Dustin McIntire (dustin@sensoria.com)
|
||||
* (C) 2007,2008 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
|
||||
* (C) 2008 Yusuke Goda <goda.yusuke@renesas.com>
|
||||
*
|
||||
* u-boot/arch/sh/cpu/sh4/pci-sh4.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pci.h>
|
||||
#include <pci.h>
|
||||
|
||||
int pci_sh4_init(struct pci_controller *hose)
|
||||
{
|
||||
hose->first_busno = 0;
|
||||
hose->region_count = 0;
|
||||
hose->last_busno = 0xff;
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(hose->regions + 0,
|
||||
CONFIG_PCI_MEM_BUS,
|
||||
CONFIG_PCI_MEM_PHYS,
|
||||
CONFIG_PCI_MEM_SIZE,
|
||||
PCI_REGION_MEM);
|
||||
hose->region_count++;
|
||||
|
||||
/* PCI IO space */
|
||||
pci_set_region(hose->regions + 1,
|
||||
CONFIG_PCI_IO_BUS,
|
||||
CONFIG_PCI_IO_PHYS,
|
||||
CONFIG_PCI_IO_SIZE,
|
||||
PCI_REGION_IO);
|
||||
hose->region_count++;
|
||||
|
||||
#if defined(CONFIG_PCI_SYS_BUS)
|
||||
/* PCI System Memory space */
|
||||
pci_set_region(hose->regions + 2,
|
||||
CONFIG_PCI_SYS_BUS,
|
||||
CONFIG_PCI_SYS_PHYS,
|
||||
CONFIG_PCI_SYS_SIZE,
|
||||
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
hose->region_count++;
|
||||
#endif
|
||||
|
||||
udelay(1000);
|
||||
|
||||
pci_set_ops(hose,
|
||||
pci_hose_read_config_byte_via_dword,
|
||||
pci_hose_read_config_word_via_dword,
|
||||
pci_sh4_read_config_dword,
|
||||
pci_hose_write_config_byte_via_dword,
|
||||
pci_hose_write_config_word_via_dword,
|
||||
pci_sh4_write_config_dword);
|
||||
|
||||
pci_register_hose(hose);
|
||||
|
||||
udelay(1000);
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
printf("PCI: Bus Dev VenId DevId Class Int\n");
|
||||
#endif
|
||||
hose->last_busno = pci_hose_scan(hose);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_skip_dev(struct pci_controller *hose, pci_dev_t dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
int pci_print_dev(struct pci_controller *hose, pci_dev_t dev)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
#endif /* CONFIG_PCI_SCAN_SHOW */
|
||||
@@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* SH7751 PCI Controller (PCIC) for U-Boot.
|
||||
* (C) Dustin McIntire (dustin@sensoria.com)
|
||||
* (C) 2007,2008 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/pci.h>
|
||||
|
||||
/* Register addresses and such */
|
||||
#define SH7751_BCR1 (vu_long *)0xFF800000
|
||||
#define SH7751_BCR2 (vu_short *)0xFF800004
|
||||
#define SH7751_WCR1 (vu_long *)0xFF800008
|
||||
#define SH7751_WCR2 (vu_long *)0xFF80000C
|
||||
#define SH7751_WCR3 (vu_long *)0xFF800010
|
||||
#define SH7751_MCR (vu_long *)0xFF800014
|
||||
#define SH7751_BCR3 (vu_short *)0xFF800050
|
||||
#define SH7751_PCICONF0 (vu_long *)0xFE200000
|
||||
#define SH7751_PCICONF1 (vu_long *)0xFE200004
|
||||
#define SH7751_PCICONF2 (vu_long *)0xFE200008
|
||||
#define SH7751_PCICONF3 (vu_long *)0xFE20000C
|
||||
#define SH7751_PCICONF4 (vu_long *)0xFE200010
|
||||
#define SH7751_PCICONF5 (vu_long *)0xFE200014
|
||||
#define SH7751_PCICONF6 (vu_long *)0xFE200018
|
||||
#define SH7751_PCICR (vu_long *)0xFE200100
|
||||
#define SH7751_PCILSR0 (vu_long *)0xFE200104
|
||||
#define SH7751_PCILSR1 (vu_long *)0xFE200108
|
||||
#define SH7751_PCILAR0 (vu_long *)0xFE20010C
|
||||
#define SH7751_PCILAR1 (vu_long *)0xFE200110
|
||||
#define SH7751_PCIMBR (vu_long *)0xFE2001C4
|
||||
#define SH7751_PCIIOBR (vu_long *)0xFE2001C8
|
||||
#define SH7751_PCIPINT (vu_long *)0xFE2001CC
|
||||
#define SH7751_PCIPINTM (vu_long *)0xFE2001D0
|
||||
#define SH7751_PCICLKR (vu_long *)0xFE2001D4
|
||||
#define SH7751_PCIBCR1 (vu_long *)0xFE2001E0
|
||||
#define SH7751_PCIBCR2 (vu_long *)0xFE2001E4
|
||||
#define SH7751_PCIWCR1 (vu_long *)0xFE2001E8
|
||||
#define SH7751_PCIWCR2 (vu_long *)0xFE2001EC
|
||||
#define SH7751_PCIWCR3 (vu_long *)0xFE2001F0
|
||||
#define SH7751_PCIMCR (vu_long *)0xFE2001F4
|
||||
#define SH7751_PCIBCR3 (vu_long *)0xFE2001F8
|
||||
|
||||
#define BCR1_BREQEN 0x00080000
|
||||
#define PCI_SH7751_ID 0x35051054
|
||||
#define PCI_SH7751R_ID 0x350E1054
|
||||
#define SH7751_PCICONF1_WCC 0x00000080
|
||||
#define SH7751_PCICONF1_PER 0x00000040
|
||||
#define SH7751_PCICONF1_BUM 0x00000004
|
||||
#define SH7751_PCICONF1_MES 0x00000002
|
||||
#define SH7751_PCICONF1_CMDS 0x000000C6
|
||||
#define SH7751_PCI_HOST_BRIDGE 0x6
|
||||
#define SH7751_PCICR_PREFIX 0xa5000000
|
||||
#define SH7751_PCICR_PRST 0x00000002
|
||||
#define SH7751_PCICR_CFIN 0x00000001
|
||||
#define SH7751_PCIPINT_D3 0x00000002
|
||||
#define SH7751_PCIPINT_D0 0x00000001
|
||||
#define SH7751_PCICLKR_PREFIX 0xa5000000
|
||||
|
||||
#define SH7751_PCI_MEM_BASE 0xFD000000
|
||||
#define SH7751_PCI_MEM_SIZE 0x01000000
|
||||
#define SH7751_PCI_IO_BASE 0xFE240000
|
||||
#define SH7751_PCI_IO_SIZE 0x00040000
|
||||
|
||||
#define SH7751_PCIPAR (vu_long *)0xFE2001C0
|
||||
#define SH7751_PCIPDR (vu_long *)0xFE200220
|
||||
|
||||
#define p4_in(addr) (*addr)
|
||||
#define p4_out(data, addr) (*addr) = (data)
|
||||
|
||||
static int sh7751_pci_addr_valid(pci_dev_t d, uint offset)
|
||||
{
|
||||
if (PCI_FUNC(d))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 get_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset)
|
||||
{
|
||||
return BIT(31) | (PCI_DEV(bdf) << 8) | (offset & ~3);
|
||||
}
|
||||
|
||||
static int sh7751_pci_read_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong *value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
u32 addr, reg;
|
||||
int ret;
|
||||
|
||||
ret = sh7751_pci_addr_valid(bdf, offset);
|
||||
if (ret) {
|
||||
*value = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
addr = get_bus_address(dev, bdf, offset);
|
||||
p4_out(addr, SH7751_PCIPAR);
|
||||
reg = p4_in(SH7751_PCIPDR);
|
||||
*value = pci_conv_32_to_size(reg, offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh7751_pci_write_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
u32 addr, reg, old;
|
||||
int ret;
|
||||
|
||||
ret = sh7751_pci_addr_valid(bdf, offset);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
addr = get_bus_address(dev, bdf, offset);
|
||||
p4_out(addr, SH7751_PCIPAR);
|
||||
old = p4_in(SH7751_PCIPDR);
|
||||
reg = pci_conv_size_to_32(old, value, offset, size);
|
||||
p4_out(reg, SH7751_PCIPDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sh7751_pci_probe(struct udevice *dev)
|
||||
{
|
||||
/* Double-check that we're a 7751 or 7751R chip */
|
||||
if (p4_in(SH7751_PCICONF0) != PCI_SH7751_ID
|
||||
&& p4_in(SH7751_PCICONF0) != PCI_SH7751R_ID) {
|
||||
printf("PCI: Unknown PCI host bridge.\n");
|
||||
return 1;
|
||||
}
|
||||
printf("PCI: SH7751 PCI host bridge found.\n");
|
||||
|
||||
/* Double-check some BSC config settings */
|
||||
/* (Area 3 non-MPX 32-bit, PCI bus pins) */
|
||||
if ((p4_in(SH7751_BCR1) & 0x20008) == 0x20000) {
|
||||
printf("SH7751_BCR1 value is wrong(0x%08X)\n",
|
||||
(unsigned int)p4_in(SH7751_BCR1));
|
||||
return 2;
|
||||
}
|
||||
if ((p4_in(SH7751_BCR2) & 0xC0) != 0xC0) {
|
||||
printf("SH7751_BCR2 value is wrong(0x%08X)\n",
|
||||
(unsigned int)p4_in(SH7751_BCR2));
|
||||
return 3;
|
||||
}
|
||||
if (p4_in(SH7751_BCR2) & 0x01) {
|
||||
printf("SH7751_BCR2 value is wrong(0x%08X)\n",
|
||||
(unsigned int)p4_in(SH7751_BCR2));
|
||||
return 4;
|
||||
}
|
||||
|
||||
/* Force BREQEN in BCR1 to allow PCIC access */
|
||||
p4_out((p4_in(SH7751_BCR1) | BCR1_BREQEN), SH7751_BCR1);
|
||||
|
||||
/* Toggle PCI reset pin */
|
||||
p4_out((SH7751_PCICR_PREFIX | SH7751_PCICR_PRST), SH7751_PCICR);
|
||||
udelay(32);
|
||||
p4_out(SH7751_PCICR_PREFIX, SH7751_PCICR);
|
||||
|
||||
/* Set cmd bits: WCC, PER, BUM, MES */
|
||||
/* (Addr/Data stepping, Parity enabled, Bus Master, Memory enabled) */
|
||||
p4_out(0xfb900047, SH7751_PCICONF1); /* K.Kino */
|
||||
|
||||
/* Define this host as the host bridge */
|
||||
p4_out((SH7751_PCI_HOST_BRIDGE << 24), SH7751_PCICONF2);
|
||||
|
||||
/* Force PCI clock(s) on */
|
||||
p4_out(0, SH7751_PCICLKR);
|
||||
p4_out(0x03, SH7751_PCICLKR);
|
||||
|
||||
/* Clear powerdown IRQs, also mask them (unused) */
|
||||
p4_out((SH7751_PCIPINT_D0 | SH7751_PCIPINT_D3), SH7751_PCIPINT);
|
||||
p4_out(0, SH7751_PCIPINTM);
|
||||
|
||||
p4_out(0xab000001, SH7751_PCICONF4);
|
||||
|
||||
/* Set up target memory mappings (for external DMA access) */
|
||||
/* Map both P0 and P2 range to Area 3 RAM for ease of use */
|
||||
p4_out(CONFIG_SYS_SDRAM_SIZE - 0x100000, SH7751_PCILSR0);
|
||||
p4_out(CONFIG_SYS_SDRAM_BASE & 0x1FF00000, SH7751_PCILAR0);
|
||||
p4_out(CONFIG_SYS_SDRAM_BASE & 0xFFF00000, SH7751_PCICONF5);
|
||||
|
||||
p4_out(0, SH7751_PCILSR1);
|
||||
p4_out(0, SH7751_PCILAR1);
|
||||
p4_out(0xd0000000, SH7751_PCICONF6);
|
||||
|
||||
/* Map memory window to same address on PCI bus */
|
||||
p4_out(SH7751_PCI_MEM_BASE, SH7751_PCIMBR);
|
||||
|
||||
/* Map IO window to same address on PCI bus */
|
||||
p4_out(SH7751_PCI_IO_BASE, SH7751_PCIIOBR);
|
||||
|
||||
/* set BREQEN */
|
||||
p4_out(inl(SH7751_BCR1) | 0x00080000, SH7751_BCR1);
|
||||
|
||||
/* Copy BSC registers into PCI BSC */
|
||||
p4_out(inl(SH7751_BCR1), SH7751_PCIBCR1);
|
||||
p4_out(inw(SH7751_BCR2), SH7751_PCIBCR2);
|
||||
p4_out(inw(SH7751_BCR3), SH7751_PCIBCR3);
|
||||
p4_out(inl(SH7751_WCR1), SH7751_PCIWCR1);
|
||||
p4_out(inl(SH7751_WCR2), SH7751_PCIWCR2);
|
||||
p4_out(inl(SH7751_WCR3), SH7751_PCIWCR3);
|
||||
p4_out(inl(SH7751_MCR), SH7751_PCIMCR);
|
||||
|
||||
/* Finally, set central function init complete */
|
||||
p4_out((SH7751_PCICR_PREFIX | SH7751_PCICR_CFIN), SH7751_PCICR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops sh7751_pci_ops = {
|
||||
.read_config = sh7751_pci_read_config,
|
||||
.write_config = sh7751_pci_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id sh7751_pci_ids[] = {
|
||||
{ .compatible = "renesas,pci-sh7751" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sh7751_pci) = {
|
||||
.name = "sh7751_pci",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = sh7751_pci_ids,
|
||||
.ops = &sh7751_pci_ops,
|
||||
.probe = sh7751_pci_probe,
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* SH7780 PCI Controller (PCIC) for U-Boot.
|
||||
* (C) Dustin McIntire (dustin@sensoria.com)
|
||||
* (C) 2007,2008 Nobuhiro Iwamatsu <iwamatsu@nigauri.org>
|
||||
* (C) 2008 Yusuke Goda <goda.yusuke@renesas.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include <pci.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define SH7780_VENDOR_ID 0x1912
|
||||
#define SH7780_DEVICE_ID 0x0002
|
||||
#define SH7780_PCICR_PREFIX 0xA5000000
|
||||
#define SH7780_PCICR_PFCS 0x00000800
|
||||
#define SH7780_PCICR_FTO 0x00000400
|
||||
#define SH7780_PCICR_PFE 0x00000200
|
||||
#define SH7780_PCICR_TBS 0x00000100
|
||||
#define SH7780_PCICR_ARBM 0x00000040
|
||||
#define SH7780_PCICR_IOCS 0x00000004
|
||||
#define SH7780_PCICR_PRST 0x00000002
|
||||
#define SH7780_PCICR_CFIN 0x00000001
|
||||
|
||||
#define p4_in(addr) (*(vu_long *)addr)
|
||||
#define p4_out(data, addr) (*(vu_long *)addr) = (data)
|
||||
#define p4_inw(addr) (*(vu_short *)addr)
|
||||
#define p4_outw(data, addr) (*(vu_short *)addr) = (data)
|
||||
|
||||
int pci_sh4_read_config_dword(struct pci_controller *hose,
|
||||
pci_dev_t dev, int offset, u32 *value)
|
||||
{
|
||||
u32 par_data = 0x80000000 | dev;
|
||||
|
||||
p4_out(par_data | (offset & 0xfc), SH7780_PCIPAR);
|
||||
*value = p4_in(SH7780_PCIPDR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_sh4_write_config_dword(struct pci_controller *hose,
|
||||
pci_dev_t dev, int offset, u32 value)
|
||||
{
|
||||
u32 par_data = 0x80000000 | dev;
|
||||
|
||||
p4_out(par_data | (offset & 0xfc), SH7780_PCIPAR);
|
||||
p4_out(value, SH7780_PCIPDR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_sh7780_init(struct pci_controller *hose)
|
||||
{
|
||||
p4_out(0x01, SH7780_PCIECR);
|
||||
|
||||
if (p4_inw(SH7780_PCIVID) != SH7780_VENDOR_ID
|
||||
&& p4_inw(SH7780_PCIDID) != SH7780_DEVICE_ID) {
|
||||
printf("PCI: Unknown PCI host bridge.\n");
|
||||
return -1;
|
||||
}
|
||||
printf("PCI: SH7780 PCI host bridge found.\n");
|
||||
|
||||
/* Toggle PCI reset pin */
|
||||
p4_out((SH7780_PCICR_PREFIX | SH7780_PCICR_PRST), SH7780_PCICR);
|
||||
udelay(100000);
|
||||
p4_out(SH7780_PCICR_PREFIX, SH7780_PCICR);
|
||||
p4_outw(0x0047, SH7780_PCICMD);
|
||||
|
||||
p4_out(CONFIG_SH7780_PCI_LSR, SH7780_PCILSR0);
|
||||
p4_out(CONFIG_SH7780_PCI_LAR, SH7780_PCILAR0);
|
||||
p4_out(0x00000000, SH7780_PCILSR1);
|
||||
p4_out(0, SH7780_PCILAR1);
|
||||
p4_out(CONFIG_SH7780_PCI_BAR, SH7780_PCIMBAR0);
|
||||
p4_out(0x00000000, SH7780_PCIMBAR1);
|
||||
|
||||
p4_out(0xFD000000, SH7780_PCIMBR0);
|
||||
p4_out(0x00FC0000, SH7780_PCIMBMR0);
|
||||
|
||||
/* if use Operand Cache then enable PCICSCR Soonp bits. */
|
||||
p4_out(0x08000000, SH7780_PCICSAR0);
|
||||
p4_out(0x0000001B, SH7780_PCICSCR0); /* Snoop bit :On */
|
||||
|
||||
p4_out((SH7780_PCICR_PREFIX | SH7780_PCICR_CFIN | SH7780_PCICR_ARBM
|
||||
| SH7780_PCICR_FTO | SH7780_PCICR_PFCS | SH7780_PCICR_PFE),
|
||||
SH7780_PCICR);
|
||||
|
||||
pci_sh4_init(hose);
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/pci.h>
|
||||
|
||||
static int _pci_x86_read_config(struct udevice *bus, pci_dev_t bdf, uint offset,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
return pci_x86_read_config(bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int _pci_x86_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value, enum pci_size_t size)
|
||||
{
|
||||
return pci_x86_write_config(bdf, offset, value, size);
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pci_x86_ops = {
|
||||
.read_config = _pci_x86_read_config,
|
||||
.write_config = _pci_x86_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_x86_ids[] = {
|
||||
{ .compatible = "pci-x86" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_x86) = {
|
||||
.name = "pci_x86",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pci_x86_ids,
|
||||
.ops = &pci_x86_ops,
|
||||
};
|
||||
@@ -0,0 +1,582 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2015 Marvell International Ltd.
|
||||
*
|
||||
* Copyright (C) 2016 Stefan Roese <sr@denx.de>
|
||||
*
|
||||
* Based on:
|
||||
* - drivers/pci/pcie_imx.c
|
||||
* - drivers/pci/pci_mvebu.c
|
||||
* - drivers/pci/pcie_xilinx.c
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/gpio.h>
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
/* PCI Config space 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)
|
||||
|
||||
/* Resizable bar capability registers */
|
||||
#define RESIZABLE_BAR_CAP 0x250
|
||||
#define RESIZABLE_BAR_CTL0 0x254
|
||||
#define RESIZABLE_BAR_CTL1 0x258
|
||||
|
||||
/* iATU registers */
|
||||
#define PCIE_ATU_VIEWPORT 0x900
|
||||
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
|
||||
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
|
||||
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
|
||||
#define PCIE_ATU_CR1 0x904
|
||||
#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE (0x1 << 31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
|
||||
#define PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define PCIE_ATU_UPPER_BASE 0x910
|
||||
#define PCIE_ATU_LIMIT 0x914
|
||||
#define PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
|
||||
#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
|
||||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
#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_GEN3_RELATED 0x890
|
||||
#define GEN3_EQU_DISABLE (1 << 16)
|
||||
#define GEN3_ZRXDC_NON_COMP (1 << 0)
|
||||
|
||||
#define PCIE_GEN3_EQU_CTRL 0x8A8
|
||||
#define GEN3_EQU_EVAL_2MS_DISABLE (1 << 5)
|
||||
|
||||
#define PCIE_ROOT_COMPLEX_MODE_MASK (0xF << 4)
|
||||
|
||||
#define PCIE_LINK_UP_TIMEOUT_MS 100
|
||||
|
||||
#define PCIE_GLOBAL_CONTROL 0x8000
|
||||
#define PCIE_APP_LTSSM_EN (1 << 2)
|
||||
#define PCIE_DEVICE_TYPE_OFFSET (4)
|
||||
#define PCIE_DEVICE_TYPE_MASK (0xF)
|
||||
#define PCIE_DEVICE_TYPE_EP (0x0) /* Endpoint */
|
||||
#define PCIE_DEVICE_TYPE_LEP (0x1) /* Legacy endpoint */
|
||||
#define PCIE_DEVICE_TYPE_RC (0x4) /* Root complex */
|
||||
|
||||
#define PCIE_GLOBAL_STATUS 0x8008
|
||||
#define PCIE_GLB_STS_RDLH_LINK_UP (1 << 1)
|
||||
#define PCIE_GLB_STS_PHY_LINK_UP (1 << 9)
|
||||
|
||||
#define PCIE_ARCACHE_TRC 0x8050
|
||||
#define PCIE_AWCACHE_TRC 0x8054
|
||||
#define ARCACHE_SHAREABLE_CACHEABLE 0x3511
|
||||
#define AWCACHE_SHAREABLE_CACHEABLE 0x5311
|
||||
|
||||
#define LINK_SPEED_GEN_1 0x1
|
||||
#define LINK_SPEED_GEN_2 0x2
|
||||
#define LINK_SPEED_GEN_3 0x3
|
||||
|
||||
/**
|
||||
* struct pcie_dw_mvebu - MVEBU DW PCIe controller state
|
||||
*
|
||||
* @ctrl_base: The base address of the register space
|
||||
* @cfg_base: The base address of the configuration 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_mvebu {
|
||||
void *ctrl_base;
|
||||
void *cfg_base;
|
||||
fdt_size_t cfg_size;
|
||||
int first_busno;
|
||||
|
||||
/* IO and MEM PCI regions */
|
||||
struct pci_region io;
|
||||
struct pci_region mem;
|
||||
};
|
||||
|
||||
static int pcie_dw_get_link_speed(const void *regs_base)
|
||||
{
|
||||
return (readl(regs_base + PCIE_LINK_STATUS_REG) &
|
||||
PCIE_LINK_STATUS_SPEED_MASK) >> PCIE_LINK_STATUS_SPEED_OFF;
|
||||
}
|
||||
|
||||
static int pcie_dw_get_link_width(const void *regs_base)
|
||||
{
|
||||
return (readl(regs_base + PCIE_LINK_STATUS_REG) &
|
||||
PCIE_LINK_STATUS_WIDTH_MASK) >> PCIE_LINK_STATUS_WIDTH_OFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_prog_outbound_atu() - 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(struct pcie_dw_mvebu *pcie, int index,
|
||||
int type, u64 cpu_addr, u64 pci_addr,
|
||||
u32 size)
|
||||
{
|
||||
writel(PCIE_ATU_REGION_OUTBOUND | index,
|
||||
pcie->ctrl_base + PCIE_ATU_VIEWPORT);
|
||||
writel(lower_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_LOWER_BASE);
|
||||
writel(upper_32_bits(cpu_addr), pcie->ctrl_base + PCIE_ATU_UPPER_BASE);
|
||||
writel(lower_32_bits(cpu_addr + size - 1),
|
||||
pcie->ctrl_base + PCIE_ATU_LIMIT);
|
||||
writel(lower_32_bits(pci_addr),
|
||||
pcie->ctrl_base + PCIE_ATU_LOWER_TARGET);
|
||||
writel(upper_32_bits(pci_addr),
|
||||
pcie->ctrl_base + PCIE_ATU_UPPER_TARGET);
|
||||
writel(type, pcie->ctrl_base + PCIE_ATU_CR1);
|
||||
writel(PCIE_ATU_ENABLE, pcie->ctrl_base + PCIE_ATU_CR2);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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_mvebu *pcie,
|
||||
pci_dev_t d, uint where)
|
||||
{
|
||||
uintptr_t va_address;
|
||||
u32 atu_type;
|
||||
|
||||
/*
|
||||
* Region #0 is used for Outbound CFG space access.
|
||||
* Direction = Outbound
|
||||
* Region Index = 0
|
||||
*/
|
||||
|
||||
if (PCI_BUS(d) == (pcie->first_busno + 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;
|
||||
|
||||
if (PCI_BUS(d) == pcie->first_busno) {
|
||||
/* Accessing root port configuration space. */
|
||||
va_address = (uintptr_t)pcie->ctrl_base;
|
||||
} else {
|
||||
d = PCI_MASK_BUS(d) | (PCI_BUS(d) - pcie->first_busno);
|
||||
pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX0,
|
||||
atu_type, (u64)pcie->cfg_base,
|
||||
d << 8, pcie->cfg_size);
|
||||
va_address = (uintptr_t)pcie->cfg_base;
|
||||
}
|
||||
|
||||
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_mvebu_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_mvebu_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct pcie_dw_mvebu *pcie = dev_get_priv(bus);
|
||||
uintptr_t va_address;
|
||||
ulong value;
|
||||
|
||||
debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ",
|
||||
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(pcie, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pcie->io.phys_start,
|
||||
pcie->io.bus_start, pcie->io.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_mvebu_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_mvebu_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct pcie_dw_mvebu *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(pcie, PCIE_ATU_REGION_INDEX0,
|
||||
PCIE_ATU_TYPE_IO, pcie->io.phys_start,
|
||||
pcie->io.bus_start, pcie->io.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_configure() - Configure link capabilities and speed
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
* @cap_speed: The capabilities and speed to configure
|
||||
*
|
||||
* Configure the link capabilities and speed in the PCIe root complex.
|
||||
*/
|
||||
static void pcie_dw_configure(const void *regs_base, u32 cap_speed)
|
||||
{
|
||||
/*
|
||||
* TODO (shadi@marvell.com, sr@denx.de):
|
||||
* Need to read the serdes speed from the dts and according to it
|
||||
* configure the PCIe gen
|
||||
*/
|
||||
|
||||
/* Set link to GEN 3 */
|
||||
clrsetbits_le32(regs_base + PCIE_LINK_CTL_2,
|
||||
TARGET_LINK_SPEED_MASK, cap_speed);
|
||||
clrsetbits_le32(regs_base + PCIE_LINK_CAPABILITY,
|
||||
TARGET_LINK_SPEED_MASK, cap_speed);
|
||||
setbits_le32(regs_base + PCIE_GEN3_EQU_CTRL, GEN3_EQU_EVAL_2MS_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_link_up() - Return the link state
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
*
|
||||
* Return: 1 (true) for active line and 0 (false) for no link
|
||||
*/
|
||||
static int is_link_up(const void *regs_base)
|
||||
{
|
||||
u32 mask = PCIE_GLB_STS_RDLH_LINK_UP | PCIE_GLB_STS_PHY_LINK_UP;
|
||||
u32 reg;
|
||||
|
||||
reg = readl(regs_base + PCIE_GLOBAL_STATUS);
|
||||
if ((reg & mask) == mask)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_link_up() - Wait for the link to come up
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
*
|
||||
* Return: 1 (true) for active line and 0 (false) for no link (timeout)
|
||||
*/
|
||||
static int wait_link_up(const void *regs_base)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = get_timer(0) + PCIE_LINK_UP_TIMEOUT_MS;
|
||||
while (!is_link_up(regs_base)) {
|
||||
if (get_timer(0) > timeout)
|
||||
return 0;
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_mvebu_pcie_link_up() - Configure the PCIe root port
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
* @cap_speed: The capabilities and speed to configure
|
||||
*
|
||||
* Configure the PCIe controller root complex depending on the
|
||||
* requested link capabilities and speed.
|
||||
*
|
||||
* Return: 1 (true) for active line and 0 (false) for no link
|
||||
*/
|
||||
static int pcie_dw_mvebu_pcie_link_up(const void *regs_base, u32 cap_speed)
|
||||
{
|
||||
if (!is_link_up(regs_base)) {
|
||||
/* Disable LTSSM state machine to enable configuration */
|
||||
clrbits_le32(regs_base + PCIE_GLOBAL_CONTROL,
|
||||
PCIE_APP_LTSSM_EN);
|
||||
}
|
||||
|
||||
clrsetbits_le32(regs_base + PCIE_GLOBAL_CONTROL,
|
||||
PCIE_DEVICE_TYPE_MASK << PCIE_DEVICE_TYPE_OFFSET,
|
||||
PCIE_DEVICE_TYPE_RC << PCIE_DEVICE_TYPE_OFFSET);
|
||||
|
||||
/* Set the PCIe master AXI attributes */
|
||||
writel(ARCACHE_SHAREABLE_CACHEABLE, regs_base + PCIE_ARCACHE_TRC);
|
||||
writel(AWCACHE_SHAREABLE_CACHEABLE, regs_base + PCIE_AWCACHE_TRC);
|
||||
|
||||
/* DW pre link configurations */
|
||||
pcie_dw_configure(regs_base, cap_speed);
|
||||
|
||||
if (!is_link_up(regs_base)) {
|
||||
/* Configuration done. Start LTSSM */
|
||||
setbits_le32(regs_base + PCIE_GLOBAL_CONTROL,
|
||||
PCIE_APP_LTSSM_EN);
|
||||
}
|
||||
|
||||
/* Check that link was established */
|
||||
if (!wait_link_up(regs_base))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Link can be established in Gen 1. still need to wait
|
||||
* till MAC nagaotiation is completed
|
||||
*/
|
||||
udelay(100);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_set_host_bars() - Configure the host BARs
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
*
|
||||
* Configure the host BARs of the PCIe controller root port so that
|
||||
* PCI(e) devices may access the system memory.
|
||||
*/
|
||||
static void pcie_dw_set_host_bars(const void *regs_base)
|
||||
{
|
||||
u32 size = gd->ram_size;
|
||||
u64 max_size;
|
||||
u32 reg;
|
||||
u32 bar0;
|
||||
|
||||
/* Verify the maximal BAR size */
|
||||
reg = readl(regs_base + RESIZABLE_BAR_CAP);
|
||||
max_size = 1ULL << (5 + (reg + (1 << 4)));
|
||||
|
||||
if (size > max_size) {
|
||||
size = max_size;
|
||||
printf("Warning: PCIe BARs can't map all DRAM space\n");
|
||||
}
|
||||
|
||||
/* Set the BAR base and size towards DDR */
|
||||
bar0 = CONFIG_SYS_SDRAM_BASE & ~0xf;
|
||||
bar0 |= PCI_BASE_ADDRESS_MEM_TYPE_32;
|
||||
writel(CONFIG_SYS_SDRAM_BASE, regs_base + PCIE_CONFIG_BAR0);
|
||||
|
||||
reg = ((size >> 20) - 1) << 12;
|
||||
writel(size, regs_base + RESIZABLE_BAR_CTL0);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_mvebu_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 pcie_dw_mvebu_probe(struct udevice *dev)
|
||||
{
|
||||
struct pcie_dw_mvebu *pcie = dev_get_priv(dev);
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
||||
#ifdef CONFIG_DM_GPIO
|
||||
struct gpio_desc reset_gpio;
|
||||
|
||||
gpio_request_by_name(dev, "marvell,reset-gpio", 0, &reset_gpio,
|
||||
GPIOD_IS_OUT);
|
||||
/*
|
||||
* Issue reset to add-in card trough the dedicated GPIO.
|
||||
* Some boards are connecting the card reset pin to common system
|
||||
* reset wire and others are using separate GPIO port.
|
||||
* In the last case we have to release a reset of the addon card
|
||||
* using this GPIO.
|
||||
*/
|
||||
if (dm_gpio_is_valid(&reset_gpio)) {
|
||||
dm_gpio_set_value(&reset_gpio, 1); /* assert */
|
||||
mdelay(200);
|
||||
dm_gpio_set_value(&reset_gpio, 0); /* de-assert */
|
||||
mdelay(200);
|
||||
}
|
||||
#else
|
||||
debug("PCIE Reset on GPIO support is missing\n");
|
||||
#endif /* CONFIG_DM_GPIO */
|
||||
|
||||
pcie->first_busno = dev->seq;
|
||||
|
||||
/* Don't register host if link is down */
|
||||
if (!pcie_dw_mvebu_pcie_link_up(pcie->ctrl_base, LINK_SPEED_GEN_3)) {
|
||||
printf("PCIE-%d: Link down\n", dev->seq);
|
||||
} else {
|
||||
printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev->seq,
|
||||
pcie_dw_get_link_speed(pcie->ctrl_base),
|
||||
pcie_dw_get_link_width(pcie->ctrl_base),
|
||||
hose->first_busno);
|
||||
}
|
||||
|
||||
/* Store the IO and MEM windows settings for future use by the ATU */
|
||||
pcie->io.phys_start = hose->regions[0].phys_start; /* IO base */
|
||||
pcie->io.bus_start = hose->regions[0].bus_start; /* IO_bus_addr */
|
||||
pcie->io.size = hose->regions[0].size; /* IO size */
|
||||
|
||||
pcie->mem.phys_start = hose->regions[1].phys_start; /* MEM base */
|
||||
pcie->mem.bus_start = hose->regions[1].bus_start; /* MEM_bus_addr */
|
||||
pcie->mem.size = hose->regions[1].size; /* MEM size */
|
||||
|
||||
pcie_dw_prog_outbound_atu(pcie, PCIE_ATU_REGION_INDEX1,
|
||||
PCIE_ATU_TYPE_MEM, pcie->mem.phys_start,
|
||||
pcie->mem.bus_start, pcie->mem.size);
|
||||
|
||||
/* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
|
||||
clrsetbits_le32(pcie->ctrl_base + PCI_CLASS_REVISION,
|
||||
0xffff << 16, PCI_CLASS_BRIDGE_PCI << 16);
|
||||
|
||||
pcie_dw_set_host_bars(pcie->ctrl_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_mvebu_ofdata_to_platdata() - Translate from DT to device state
|
||||
*
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pcie_dw_mvebu_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct pcie_dw_mvebu *pcie = dev_get_priv(dev);
|
||||
|
||||
/* Get the controller base address */
|
||||
pcie->ctrl_base = (void *)devfdt_get_addr_index(dev, 0);
|
||||
if ((fdt_addr_t)pcie->ctrl_base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the config space base address and size */
|
||||
pcie->cfg_base = (void *)devfdt_get_addr_size_index(dev, 1,
|
||||
&pcie->cfg_size);
|
||||
if ((fdt_addr_t)pcie->cfg_base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pcie_dw_mvebu_ops = {
|
||||
.read_config = pcie_dw_mvebu_read_config,
|
||||
.write_config = pcie_dw_mvebu_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pcie_dw_mvebu_ids[] = {
|
||||
{ .compatible = "marvell,armada8k-pcie" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_dw_mvebu) = {
|
||||
.name = "pcie_dw_mvebu",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pcie_dw_mvebu_ids,
|
||||
.ops = &pcie_dw_mvebu_ops,
|
||||
.ofdata_to_platdata = pcie_dw_mvebu_ofdata_to_platdata,
|
||||
.probe = pcie_dw_mvebu_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct pcie_dw_mvebu),
|
||||
};
|
||||
@@ -0,0 +1,725 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2018 Texas Instruments, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <generic-phy.h>
|
||||
#include <power-domain.h>
|
||||
#include <regmap.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm-generic/gpio.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
|
||||
#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 PLR_OFFSET 0x700
|
||||
#define PCIE_PORT_DEBUG0 (PLR_OFFSET + 0x28)
|
||||
#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
|
||||
#define LEG_EP 0x1
|
||||
#define RC 0x2
|
||||
|
||||
/**
|
||||
* struct pcie_dw_ti - TI DW PCIe controller state
|
||||
*
|
||||
* @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 {
|
||||
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 {
|
||||
DW_PCIE_UNKNOWN_TYPE,
|
||||
DW_PCIE_EP_TYPE,
|
||||
DW_PCIE_LEG_EP_TYPE,
|
||||
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(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
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
* @cap_speed: The capabilities and speed to configure
|
||||
*
|
||||
* Configure the link capabilities and speed in the PCIe root complex.
|
||||
*/
|
||||
static void pcie_dw_configure(struct pcie_dw_ti *pci, u32 cap_speed)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
dw_pcie_dbi_write_enable(pci, true);
|
||||
|
||||
val = readl(pci->dbi_base + PCIE_LINK_CAPABILITY);
|
||||
val &= ~TARGET_LINK_SPEED_MASK;
|
||||
val |= cap_speed;
|
||||
writel(val, pci->dbi_base + PCIE_LINK_CAPABILITY);
|
||||
|
||||
val = readl(pci->dbi_base + PCIE_LINK_CTL_2);
|
||||
val &= ~TARGET_LINK_SPEED_MASK;
|
||||
val |= cap_speed;
|
||||
writel(val, pci->dbi_base + PCIE_LINK_CTL_2);
|
||||
|
||||
dw_pcie_dbi_write_enable(pci, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* is_link_up() - Return the link state
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe DBICS registers
|
||||
*
|
||||
* Return: 1 (true) for active line and 0 (false) for no link
|
||||
*/
|
||||
static int is_link_up(struct pcie_dw_ti *pci)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = readl(pci->dbi_base + PCIE_PORT_DEBUG0);
|
||||
val &= PORT_LOGIC_LTSSM_STATE_MASK;
|
||||
|
||||
return (val == PORT_LOGIC_LTSSM_STATE_L0);
|
||||
}
|
||||
|
||||
/**
|
||||
* wait_link_up() - Wait for the link to come up
|
||||
*
|
||||
* @regs_base: A pointer to the PCIe controller registers
|
||||
*
|
||||
* Return: 1 (true) for active line and 0 (false) for no link (timeout)
|
||||
*/
|
||||
static int wait_link_up(struct pcie_dw_ti *pci)
|
||||
{
|
||||
unsigned long timeout;
|
||||
|
||||
timeout = get_timer(0) + PCIE_LINK_UP_TIMEOUT_MS;
|
||||
while (!is_link_up(pci)) {
|
||||
if (get_timer(0) > timeout)
|
||||
return 0;
|
||||
};
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int pcie_dw_ti_pcie_link_up(struct pcie_dw_ti *pci, u32 cap_speed)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
if (is_link_up(pci)) {
|
||||
printf("PCI Link already up before configuration!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* DW pre link configurations */
|
||||
pcie_dw_configure(pci, cap_speed);
|
||||
|
||||
/* Initiate link training */
|
||||
val = readl(pci->app_base + PCIE_CMD_STATUS);
|
||||
val |= LTSSM_EN_VAL;
|
||||
writel(val, pci->app_base + PCIE_CMD_STATUS);
|
||||
|
||||
/* Check that link was established */
|
||||
if (!wait_link_up(pci))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Link can be established in Gen 1. still need to wait
|
||||
* till MAC nagaotiation is completed
|
||||
*/
|
||||
udelay(100);
|
||||
|
||||
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)
|
||||
{
|
||||
struct regmap *syscon;
|
||||
u32 val;
|
||||
u32 mask;
|
||||
int ret;
|
||||
|
||||
syscon = syscon_regmap_lookup_by_phandle(pci->dev,
|
||||
"ti,syscon-pcie-mode");
|
||||
if (IS_ERR(syscon))
|
||||
return 0;
|
||||
|
||||
mask = AM654_PCIE_DEV_TYPE_MASK;
|
||||
|
||||
switch (mode) {
|
||||
case DW_PCIE_RC_TYPE:
|
||||
val = RC;
|
||||
break;
|
||||
case DW_PCIE_EP_TYPE:
|
||||
val = EP;
|
||||
break;
|
||||
default:
|
||||
dev_err(pci->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");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_dw_init_id(struct pcie_dw_ti *pci)
|
||||
{
|
||||
struct regmap *devctrl_regs;
|
||||
unsigned int id;
|
||||
int ret;
|
||||
|
||||
devctrl_regs = syscon_regmap_lookup_by_phandle(pci->dev,
|
||||
"ti,syscon-pcie-id");
|
||||
if (IS_ERR(devctrl_regs))
|
||||
return PTR_ERR(devctrl_regs);
|
||||
|
||||
ret = regmap_read(devctrl_regs, 0, &id);
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_ti_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 pcie_dw_ti_probe(struct udevice *dev)
|
||||
{
|
||||
struct pcie_dw_ti *pci = dev_get_priv(dev);
|
||||
struct udevice *ctlr = pci_get_controller(dev);
|
||||
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
|
||||
struct power_domain pci_pwrdmn;
|
||||
struct phy phy0, phy1;
|
||||
int ret;
|
||||
|
||||
ret = power_domain_get_by_index(dev, &pci_pwrdmn, 0);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get power domain\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = power_domain_on(&pci_pwrdmn);
|
||||
if (ret) {
|
||||
dev_err(dev, "Power domain on failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = generic_phy_get_by_name(dev, "pcie-phy0", &phy0);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to get phy0");
|
||||
return ret;
|
||||
}
|
||||
generic_phy_reset(&phy0);
|
||||
generic_phy_init(&phy0);
|
||||
generic_phy_power_on(&phy0);
|
||||
|
||||
ret = generic_phy_get_by_name(dev, "pcie-phy1", &phy1);
|
||||
if (ret) {
|
||||
dev_err(dev, "Unable to get phy1");
|
||||
return ret;
|
||||
}
|
||||
generic_phy_reset(&phy1);
|
||||
generic_phy_init(&phy1);
|
||||
generic_phy_power_on(&phy1);
|
||||
|
||||
pci->first_busno = dev->seq;
|
||||
pci->dev = dev;
|
||||
|
||||
pcie_dw_setup_host(pci);
|
||||
pcie_dw_init_id(pci);
|
||||
|
||||
if (device_is_compatible(dev, "ti,am654-pcie-rc"))
|
||||
pcie_am654_set_mode(pci, DW_PCIE_RC_TYPE);
|
||||
|
||||
if (!pcie_dw_ti_pcie_link_up(pci, LINK_SPEED_GEN_2)) {
|
||||
printf("PCIE-%d: Link down\n", dev->seq);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
printf("PCIE-%d: Link up (Gen%d-x%d, Bus%d)\n", dev->seq,
|
||||
pcie_dw_get_link_speed(pci),
|
||||
pcie_dw_get_link_width(pci),
|
||||
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_ATU_TYPE_MEM,
|
||||
pci->mem.phys_start,
|
||||
pci->mem.bus_start, pci->mem.size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_dw_ti_ofdata_to_platdata() - Translate from DT to device state
|
||||
*
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pcie_dw_ti_ofdata_to_platdata(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)
|
||||
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)
|
||||
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)
|
||||
return -EINVAL;
|
||||
|
||||
/* Get the app base address and size */
|
||||
pcie->app_base = (void *)dev_read_addr_name(dev, "app");
|
||||
if ((fdt_addr_t)pcie->app_base == FDT_ADDR_T_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pcie_dw_ti_ops = {
|
||||
.read_config = pcie_dw_ti_read_config,
|
||||
.write_config = pcie_dw_ti_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pcie_dw_ti_ids[] = {
|
||||
{ .compatible = "ti,am654-pcie-rc" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_dw_ti) = {
|
||||
.name = "pcie_dw_ti",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pcie_dw_ti_ids,
|
||||
.ops = &pcie_dw_ti_ops,
|
||||
.ofdata_to_platdata = pcie_dw_ti_ofdata_to_platdata,
|
||||
.probe = pcie_dw_ti_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct pcie_dw_ti),
|
||||
};
|
||||
@@ -0,0 +1,142 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Generic PCIE host provided by e.g. QEMU
|
||||
*
|
||||
* Heavily based on drivers/pci/pcie_xilinx.c
|
||||
*
|
||||
* Copyright (C) 2016 Imagination Technologies
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* struct generic_ecam_pcie - generic_ecam PCIe controller state
|
||||
* @cfg_base: The base address of memory mapped configuration space
|
||||
*/
|
||||
struct generic_ecam_pcie {
|
||||
void *cfg_base;
|
||||
};
|
||||
|
||||
/**
|
||||
* pci_generic_ecam_conf_address() - Calculate the address of a config access
|
||||
* @bus: Pointer to the PCI bus
|
||||
* @bdf: Identifies the PCIe device to access
|
||||
* @offset: The offset into the device's configuration space
|
||||
* @paddress: Pointer to the pointer to write the calculates address to
|
||||
*
|
||||
* Calculates the address that should be accessed to perform a PCIe
|
||||
* configuration space access for a given device identified by the PCIe
|
||||
* controller device @pcie and the bus, device & function numbers in @bdf. If
|
||||
* access to the device is not valid then the function will return an error
|
||||
* code. Otherwise the address to access will be written to the pointer pointed
|
||||
* to by @paddress.
|
||||
*/
|
||||
static int pci_generic_ecam_conf_address(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, void **paddress)
|
||||
{
|
||||
struct generic_ecam_pcie *pcie = dev_get_priv(bus);
|
||||
void *addr;
|
||||
|
||||
addr = pcie->cfg_base;
|
||||
addr += PCI_BUS(bdf) << 20;
|
||||
addr += PCI_DEV(bdf) << 15;
|
||||
addr += PCI_FUNC(bdf) << 12;
|
||||
addr += offset;
|
||||
*paddress = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_generic_ecam_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.
|
||||
*/
|
||||
static int pci_generic_ecam_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
return pci_generic_mmap_read_config(bus, pci_generic_ecam_conf_address,
|
||||
bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_generic_ecam_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.
|
||||
*/
|
||||
static int pci_generic_ecam_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, pci_generic_ecam_conf_address,
|
||||
bdf, offset, value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_generic_ecam_ofdata_to_platdata() - Translate from DT to device state
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pci_generic_ecam_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct generic_ecam_pcie *pcie = dev_get_priv(dev);
|
||||
struct fdt_resource reg_res;
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
int err;
|
||||
|
||||
err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
|
||||
0, ®_res);
|
||||
if (err < 0) {
|
||||
pr_err("\"reg\" resource not found\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pcie->cfg_base = map_physmem(reg_res.start,
|
||||
fdt_resource_size(®_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pci_generic_ecam_ops = {
|
||||
.read_config = pci_generic_ecam_read_config,
|
||||
.write_config = pci_generic_ecam_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_generic_ecam_ids[] = {
|
||||
{ .compatible = "pci-host-ecam-generic" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_generic_ecam) = {
|
||||
.name = "pci_generic_ecam",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pci_generic_ecam_ids,
|
||||
.ops = &pci_generic_ecam_ops,
|
||||
.ofdata_to_platdata = pci_generic_ecam_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct generic_ecam_pcie),
|
||||
};
|
||||
@@ -0,0 +1,653 @@
|
||||
// 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/io.h>
|
||||
#include "pcie_fsl.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) < bus->seq)
|
||||
return -EINVAL;
|
||||
|
||||
if (PCI_BUS(bdf) > bus->seq && (!fsl_pcie_link_up(pcie) || pcie->mode))
|
||||
return -EINVAL;
|
||||
|
||||
if (PCI_BUS(bdf) == bus->seq && (PCI_DEV(bdf) > 0 || PCI_FUNC(bdf) > 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (PCI_BUS(bdf) == (bus->seq + 1) && (PCI_DEV(bdf) > 0))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fsl_pcie_read_config(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;
|
||||
}
|
||||
|
||||
bdf = bdf - PCI_BDF(bus->seq, 0, 0);
|
||||
val = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000;
|
||||
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;
|
||||
|
||||
bdf = bdf - PCI_BDF(bus->seq, 0, 0);
|
||||
val = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000;
|
||||
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(bus->seq, 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(bus->seq, 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 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
|
||||
clrbits_be32(®s->dbi_ro_wr_en, 0x01);
|
||||
#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)
|
||||
{
|
||||
ccsr_fsl_pci_t *regs = pcie->regs;
|
||||
u32 classcode_reg;
|
||||
u32 val;
|
||||
|
||||
if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
|
||||
classcode_reg = PCI_CLASS_REVISION;
|
||||
setbits_be32(®s->dbi_ro_wr_en, 0x01);
|
||||
} else {
|
||||
classcode_reg = CSR_CLASSCODE;
|
||||
}
|
||||
|
||||
fsl_pcie_hose_read_config_dword(pcie, classcode_reg, &val);
|
||||
val &= 0xff;
|
||||
val |= PCI_CLASS_BRIDGE_PCI << 16;
|
||||
fsl_pcie_hose_write_config_dword(pcie, classcode_reg, val);
|
||||
|
||||
if (pcie->block_rev >= PEX_IP_BLK_REV_3_0)
|
||||
clrbits_be32(®s->dbi_ro_wr_en, 0x01);
|
||||
|
||||
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_ofdata_to_platdata(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,pcie-mpc8548", .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,
|
||||
.ofdata_to_platdata = fsl_pcie_ofdata_to_platdata,
|
||||
.probe = fsl_pcie_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct fsl_pcie),
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* PCIe DM U-Boot driver for Freescale PowerPC SoCs
|
||||
* Author: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
|
||||
*/
|
||||
|
||||
#ifndef _PCIE_FSL_H_
|
||||
#define _PCIE_FSL_H_
|
||||
|
||||
/* GPEX CSR */
|
||||
#define CSR_CLASSCODE 0x474
|
||||
|
||||
#ifdef CONFIG_SYS_FSL_PCI_VER_3_X
|
||||
#define FSL_PCIE_CAP_ID 0x70
|
||||
#else
|
||||
#define FSL_PCIE_CAP_ID 0x4c
|
||||
#endif
|
||||
/* PCIe Device Control Register */
|
||||
#define PCI_DCR (FSL_PCIE_CAP_ID + 0x08)
|
||||
/* PCIe Device Status Register */
|
||||
#define PCI_DSR (FSL_PCIE_CAP_ID + 0x0a)
|
||||
/* PCIe Link Control Register */
|
||||
#define PCI_LCR (FSL_PCIE_CAP_ID + 0x10)
|
||||
/* PCIe Link Status Register */
|
||||
#define PCI_LSR (FSL_PCIE_CAP_ID + 0x12)
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_BUS
|
||||
#define CONFIG_SYS_PCI_MEMORY_BUS 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_PHYS
|
||||
#define CONFIG_SYS_PCI_MEMORY_PHYS 0
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SYS_PCI_64BIT) && !defined(CONFIG_SYS_PCI64_MEMORY_BUS)
|
||||
#define CONFIG_SYS_PCI64_MEMORY_BUS (64ull * 1024 * 1024 * 1024)
|
||||
#endif
|
||||
|
||||
#define PEX_CSR0_LTSSM_MASK 0xFC
|
||||
#define PEX_CSR0_LTSSM_SHIFT 2
|
||||
#define LTSSM_L0_REV3 0x11
|
||||
#define LTSSM_L0 0x16
|
||||
|
||||
struct fsl_pcie_data {
|
||||
u32 block_offset; /* Offset from CCSR of 1st controller */
|
||||
u32 block_offset_mask; /* Mask out the CCSR base */
|
||||
u32 stride; /* Offset stride between controllers */
|
||||
};
|
||||
|
||||
struct fsl_pcie {
|
||||
int idx;
|
||||
struct udevice *bus;
|
||||
void __iomem *regs;
|
||||
u32 law_trgt_if; /* LAW target ID */
|
||||
u32 block_rev; /* IP block revision */
|
||||
bool mode; /* RC&EP mode flag */
|
||||
bool enabled; /* Enable status */
|
||||
struct list_head list;
|
||||
struct fsl_pcie_data *info;
|
||||
};
|
||||
|
||||
extern struct list_head fsl_pcie_list;
|
||||
|
||||
#endif /* _PCIE_FSL_H_ */
|
||||
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+ OR X11
|
||||
/*
|
||||
* Copyright 2019 NXP
|
||||
*
|
||||
* PCIe Kernel DT fixup of DM U-Boot driver for Freescale PowerPC SoCs
|
||||
* Author: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#ifdef CONFIG_OF_BOARD_SETUP
|
||||
#include <dm.h>
|
||||
#include <fdt_support.h>
|
||||
#include <asm/fsl_pci.h>
|
||||
#include <linux/libfdt.h>
|
||||
#include "pcie_fsl.h"
|
||||
|
||||
static void ft_fsl_pcie_setup(void *blob, struct fsl_pcie *pcie)
|
||||
{
|
||||
struct pci_controller *hose = dev_get_uclass_priv(pcie->bus);
|
||||
fdt_addr_t regs_addr;
|
||||
int off;
|
||||
|
||||
regs_addr = dev_read_addr(pcie->bus);
|
||||
off = fdt_node_offset_by_compat_reg(blob, FSL_PCIE_COMPAT, regs_addr);
|
||||
if (off < 0) {
|
||||
printf("%s: Fail to find PCIe node@0x%pa\n",
|
||||
FSL_PCIE_COMPAT, ®s_addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hose || !pcie->enabled)
|
||||
fdt_del_node(blob, off);
|
||||
else
|
||||
fdt_pci_dma_ranges(blob, off, hose);
|
||||
}
|
||||
|
||||
/* Fixup Kernel DT for PCIe */
|
||||
void pci_of_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
struct fsl_pcie *pcie;
|
||||
|
||||
list_for_each_entry(pcie, &fsl_pcie_list, list)
|
||||
ft_fsl_pcie_setup(blob, pcie);
|
||||
}
|
||||
|
||||
#else
|
||||
void pci_of_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,832 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Freescale i.MX6 PCI Express Root-Complex driver
|
||||
*
|
||||
* Copyright (C) 2013 Marek Vasut <marex@denx.de>
|
||||
*
|
||||
* Based on upstream Linux kernel driver:
|
||||
* pci-imx6.c: Sean Cross <xobs@kosagi.com>
|
||||
* pcie-designware.c: Jingoo Han <jg1.han@samsung.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <init.h>
|
||||
#include <pci.h>
|
||||
#include <asm/arch/clock.h>
|
||||
#include <asm/arch/iomux.h>
|
||||
#include <asm/arch/crm_regs.h>
|
||||
#include <asm/gpio.h>
|
||||
#include <asm/io.h>
|
||||
#include <dm.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <errno.h>
|
||||
#include <asm/arch/sys_proto.h>
|
||||
|
||||
#define PCI_ACCESS_READ 0
|
||||
#define PCI_ACCESS_WRITE 1
|
||||
|
||||
#ifdef CONFIG_MX6SX
|
||||
#define MX6_DBI_ADDR 0x08ffc000
|
||||
#define MX6_IO_ADDR 0x08000000
|
||||
#define MX6_MEM_ADDR 0x08100000
|
||||
#define MX6_ROOT_ADDR 0x08f00000
|
||||
#else
|
||||
#define MX6_DBI_ADDR 0x01ffc000
|
||||
#define MX6_IO_ADDR 0x01000000
|
||||
#define MX6_MEM_ADDR 0x01100000
|
||||
#define MX6_ROOT_ADDR 0x01f00000
|
||||
#endif
|
||||
#define MX6_DBI_SIZE 0x4000
|
||||
#define MX6_IO_SIZE 0x100000
|
||||
#define MX6_MEM_SIZE 0xe00000
|
||||
#define MX6_ROOT_SIZE 0xfc000
|
||||
|
||||
/* PCIe Port Logic registers (memory-mapped) */
|
||||
#define PL_OFFSET 0x700
|
||||
#define PCIE_PL_PFLR (PL_OFFSET + 0x08)
|
||||
#define PCIE_PL_PFLR_LINK_STATE_MASK (0x3f << 16)
|
||||
#define PCIE_PL_PFLR_FORCE_LINK (1 << 15)
|
||||
#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
|
||||
#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
|
||||
#define PCIE_PHY_DEBUG_R1_LINK_UP (1 << 4)
|
||||
#define PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING (1 << 29)
|
||||
|
||||
#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
|
||||
#define PCIE_PHY_CTRL_DATA_LOC 0
|
||||
#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
|
||||
#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
|
||||
#define PCIE_PHY_CTRL_WR_LOC 18
|
||||
#define PCIE_PHY_CTRL_RD_LOC 19
|
||||
|
||||
#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
|
||||
#define PCIE_PHY_STAT_DATA_LOC 0
|
||||
#define PCIE_PHY_STAT_ACK_LOC 16
|
||||
|
||||
/* PHY registers (not memory-mapped) */
|
||||
#define PCIE_PHY_RX_ASIC_OUT 0x100D
|
||||
|
||||
#define PHY_RX_OVRD_IN_LO 0x1005
|
||||
#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
|
||||
#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
|
||||
|
||||
#define PCIE_PHY_PUP_REQ (1 << 7)
|
||||
|
||||
/* iATU registers */
|
||||
#define PCIE_ATU_VIEWPORT 0x900
|
||||
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
|
||||
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
|
||||
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
|
||||
#define PCIE_ATU_CR1 0x904
|
||||
#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE (0x1 << 31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
|
||||
#define PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define PCIE_ATU_UPPER_BASE 0x910
|
||||
#define PCIE_ATU_LIMIT 0x914
|
||||
#define PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
|
||||
#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
|
||||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
struct imx_pcie_priv {
|
||||
void __iomem *dbi_base;
|
||||
void __iomem *cfg_base;
|
||||
};
|
||||
|
||||
/*
|
||||
* PHY access functions
|
||||
*/
|
||||
static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
|
||||
{
|
||||
u32 val;
|
||||
u32 max_iterations = 10;
|
||||
u32 wait_counter = 0;
|
||||
|
||||
do {
|
||||
val = readl(dbi_base + PCIE_PHY_STAT);
|
||||
val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
|
||||
wait_counter++;
|
||||
|
||||
if (val == exp_val)
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
} while (wait_counter < max_iterations);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
val = addr << PCIE_PHY_CTRL_DATA_LOC;
|
||||
writel(val, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
|
||||
writel(val, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
ret = pcie_phy_poll_ack(dbi_base, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = addr << PCIE_PHY_CTRL_DATA_LOC;
|
||||
writel(val, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
ret = pcie_phy_poll_ack(dbi_base, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
|
||||
static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
|
||||
{
|
||||
u32 val, phy_ctl;
|
||||
int ret;
|
||||
|
||||
ret = pcie_phy_wait_ack(dbi_base, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* assert Read signal */
|
||||
phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
|
||||
writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
ret = pcie_phy_poll_ack(dbi_base, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val = readl(dbi_base + PCIE_PHY_STAT);
|
||||
*data = val & 0xffff;
|
||||
|
||||
/* deassert Read signal */
|
||||
writel(0x00, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
ret = pcie_phy_poll_ack(dbi_base, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
|
||||
{
|
||||
u32 var;
|
||||
int ret;
|
||||
|
||||
/* write addr */
|
||||
/* cap addr */
|
||||
ret = pcie_phy_wait_ack(dbi_base, addr);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
var = data << PCIE_PHY_CTRL_DATA_LOC;
|
||||
writel(var, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
/* capture data */
|
||||
var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
|
||||
writel(var, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
ret = pcie_phy_poll_ack(dbi_base, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* deassert cap data */
|
||||
var = data << PCIE_PHY_CTRL_DATA_LOC;
|
||||
writel(var, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
/* wait for ack de-assertion */
|
||||
ret = pcie_phy_poll_ack(dbi_base, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* assert wr signal */
|
||||
var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
|
||||
writel(var, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
/* wait for ack */
|
||||
ret = pcie_phy_poll_ack(dbi_base, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* deassert wr signal */
|
||||
var = data << PCIE_PHY_CTRL_DATA_LOC;
|
||||
writel(var, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
/* wait for ack de-assertion */
|
||||
ret = pcie_phy_poll_ack(dbi_base, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
writel(0x0, dbi_base + PCIE_PHY_CTRL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6_pcie_link_up(struct imx_pcie_priv *priv)
|
||||
{
|
||||
u32 rc, ltssm;
|
||||
int rx_valid, temp;
|
||||
|
||||
/* link is debug bit 36, debug register 1 starts at bit 32 */
|
||||
rc = readl(priv->dbi_base + PCIE_PHY_DEBUG_R1);
|
||||
if ((rc & PCIE_PHY_DEBUG_R1_LINK_UP) &&
|
||||
!(rc & PCIE_PHY_DEBUG_R1_LINK_IN_TRAINING))
|
||||
return -EAGAIN;
|
||||
|
||||
/*
|
||||
* From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
|
||||
* Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
|
||||
* If (MAC/LTSSM.state == Recovery.RcvrLock)
|
||||
* && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
|
||||
* to gen2 is stuck
|
||||
*/
|
||||
pcie_phy_read(priv->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
|
||||
ltssm = readl(priv->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;
|
||||
|
||||
if (rx_valid & 0x01)
|
||||
return 0;
|
||||
|
||||
if (ltssm != 0x0d)
|
||||
return 0;
|
||||
|
||||
printf("transition to gen2 is stuck, reset PHY!\n");
|
||||
|
||||
pcie_phy_read(priv->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
|
||||
temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
|
||||
pcie_phy_write(priv->dbi_base, PHY_RX_OVRD_IN_LO, temp);
|
||||
|
||||
udelay(3000);
|
||||
|
||||
pcie_phy_read(priv->dbi_base, PHY_RX_OVRD_IN_LO, &temp);
|
||||
temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN | PHY_RX_OVRD_IN_LO_RX_PLL_EN);
|
||||
pcie_phy_write(priv->dbi_base, PHY_RX_OVRD_IN_LO, temp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iATU region setup
|
||||
*/
|
||||
static int imx_pcie_regions_setup(struct imx_pcie_priv *priv)
|
||||
{
|
||||
/*
|
||||
* i.MX6 defines 16MB in the AXI address map for PCIe.
|
||||
*
|
||||
* That address space excepted the pcie registers is
|
||||
* split and defined into different regions by iATU,
|
||||
* with sizes and offsets as follows:
|
||||
*
|
||||
* 0x0100_0000 --- 0x010F_FFFF 1MB IORESOURCE_IO
|
||||
* 0x0110_0000 --- 0x01EF_FFFF 14MB IORESOURCE_MEM
|
||||
* 0x01F0_0000 --- 0x01FF_FFFF 1MB Cfg + Registers
|
||||
*/
|
||||
|
||||
/* CMD reg:I/O space, MEM space, and Bus Master Enable */
|
||||
setbits_le32(priv->dbi_base + PCI_COMMAND,
|
||||
PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
|
||||
|
||||
/* Set the CLASS_REV of RC CFG header to PCI_CLASS_BRIDGE_PCI */
|
||||
setbits_le32(priv->dbi_base + PCI_CLASS_REVISION,
|
||||
PCI_CLASS_BRIDGE_PCI << 16);
|
||||
|
||||
/* Region #0 is used for Outbound CFG space access. */
|
||||
writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
|
||||
|
||||
writel(lower_32_bits((uintptr_t)priv->cfg_base),
|
||||
priv->dbi_base + PCIE_ATU_LOWER_BASE);
|
||||
writel(upper_32_bits((uintptr_t)priv->cfg_base),
|
||||
priv->dbi_base + PCIE_ATU_UPPER_BASE);
|
||||
writel(lower_32_bits((uintptr_t)priv->cfg_base + MX6_ROOT_SIZE),
|
||||
priv->dbi_base + PCIE_ATU_LIMIT);
|
||||
|
||||
writel(0, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
|
||||
writel(0, priv->dbi_base + PCIE_ATU_UPPER_TARGET);
|
||||
writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
|
||||
writel(PCIE_ATU_ENABLE, priv->dbi_base + PCIE_ATU_CR2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* PCI Express accessors
|
||||
*/
|
||||
static void __iomem *get_bus_address(struct imx_pcie_priv *priv,
|
||||
pci_dev_t d, int where)
|
||||
{
|
||||
void __iomem *va_address;
|
||||
|
||||
/* Reconfigure Region #0 */
|
||||
writel(0, priv->dbi_base + PCIE_ATU_VIEWPORT);
|
||||
|
||||
if (PCI_BUS(d) < 2)
|
||||
writel(PCIE_ATU_TYPE_CFG0, priv->dbi_base + PCIE_ATU_CR1);
|
||||
else
|
||||
writel(PCIE_ATU_TYPE_CFG1, priv->dbi_base + PCIE_ATU_CR1);
|
||||
|
||||
if (PCI_BUS(d) == 0) {
|
||||
va_address = priv->dbi_base;
|
||||
} else {
|
||||
writel(d << 8, priv->dbi_base + PCIE_ATU_LOWER_TARGET);
|
||||
va_address = priv->cfg_base;
|
||||
}
|
||||
|
||||
va_address += (where & ~0x3);
|
||||
|
||||
return va_address;
|
||||
}
|
||||
|
||||
static int imx_pcie_addr_valid(pci_dev_t d)
|
||||
{
|
||||
if ((PCI_BUS(d) == 0) && (PCI_DEV(d) > 1))
|
||||
return -EINVAL;
|
||||
if ((PCI_BUS(d) == 1) && (PCI_DEV(d) > 0))
|
||||
return -EINVAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Replace the original ARM DABT handler with a simple jump-back one.
|
||||
*
|
||||
* The problem here is that if we have a PCIe bridge attached to this PCIe
|
||||
* controller, but no PCIe device is connected to the bridges' downstream
|
||||
* port, the attempt to read/write from/to the config space will produce
|
||||
* a DABT. This is a behavior of the controller and can not be disabled
|
||||
* unfortuatelly.
|
||||
*
|
||||
* To work around the problem, we backup the current DABT handler address
|
||||
* and replace it with our own DABT handler, which only bounces right back
|
||||
* into the code.
|
||||
*/
|
||||
static void imx_pcie_fix_dabt_handler(bool set)
|
||||
{
|
||||
extern uint32_t *_data_abort;
|
||||
uint32_t *data_abort_addr = (uint32_t *)&_data_abort;
|
||||
|
||||
static const uint32_t data_abort_bounce_handler = 0xe25ef004;
|
||||
uint32_t data_abort_bounce_addr = (uint32_t)&data_abort_bounce_handler;
|
||||
|
||||
static uint32_t data_abort_backup;
|
||||
|
||||
if (set) {
|
||||
data_abort_backup = *data_abort_addr;
|
||||
*data_abort_addr = data_abort_bounce_addr;
|
||||
} else {
|
||||
*data_abort_addr = data_abort_backup;
|
||||
}
|
||||
}
|
||||
|
||||
static int imx_pcie_read_cfg(struct imx_pcie_priv *priv, pci_dev_t d,
|
||||
int where, u32 *val)
|
||||
{
|
||||
void __iomem *va_address;
|
||||
int ret;
|
||||
|
||||
ret = imx_pcie_addr_valid(d);
|
||||
if (ret) {
|
||||
*val = 0xffffffff;
|
||||
return 0;
|
||||
}
|
||||
|
||||
va_address = get_bus_address(priv, d, where);
|
||||
|
||||
/*
|
||||
* Read the PCIe config space. We must replace the DABT handler
|
||||
* here in case we got data abort from the PCIe controller, see
|
||||
* imx_pcie_fix_dabt_handler() description. Note that writing the
|
||||
* "val" with valid value is also imperative here as in case we
|
||||
* did got DABT, the val would contain random value.
|
||||
*/
|
||||
imx_pcie_fix_dabt_handler(true);
|
||||
writel(0xffffffff, val);
|
||||
*val = readl(va_address);
|
||||
imx_pcie_fix_dabt_handler(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_pcie_write_cfg(struct imx_pcie_priv *priv, pci_dev_t d,
|
||||
int where, u32 val)
|
||||
{
|
||||
void __iomem *va_address = NULL;
|
||||
int ret;
|
||||
|
||||
ret = imx_pcie_addr_valid(d);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
va_address = get_bus_address(priv, d, where);
|
||||
|
||||
/*
|
||||
* Write the PCIe config space. We must replace the DABT handler
|
||||
* here in case we got data abort from the PCIe controller, see
|
||||
* imx_pcie_fix_dabt_handler() description.
|
||||
*/
|
||||
imx_pcie_fix_dabt_handler(true);
|
||||
writel(val, va_address);
|
||||
imx_pcie_fix_dabt_handler(false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initial bus setup
|
||||
*/
|
||||
static int imx6_pcie_assert_core_reset(struct imx_pcie_priv *priv,
|
||||
bool prepare_for_boot)
|
||||
{
|
||||
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
|
||||
|
||||
if (is_mx6dqp())
|
||||
setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_PCIE_SW_RST);
|
||||
|
||||
#if defined(CONFIG_MX6SX)
|
||||
struct gpc *gpc_regs = (struct gpc *)GPC_BASE_ADDR;
|
||||
|
||||
/* SSP_EN is not used on MX6SX anymore */
|
||||
setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_TEST_POWERDOWN);
|
||||
/* Force PCIe PHY reset */
|
||||
setbits_le32(&iomuxc_regs->gpr[5], IOMUXC_GPR5_PCIE_BTNRST);
|
||||
/* Power up PCIe PHY */
|
||||
setbits_le32(&gpc_regs->cntr, PCIE_PHY_PUP_REQ);
|
||||
#else
|
||||
/*
|
||||
* If the bootloader already enabled the link we need some special
|
||||
* handling to get the core back into a state where it is safe to
|
||||
* touch it for configuration. As there is no dedicated reset signal
|
||||
* wired up for MX6QDL, we need to manually force LTSSM into "detect"
|
||||
* state before completely disabling LTSSM, which is a prerequisite
|
||||
* for core configuration.
|
||||
*
|
||||
* If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong
|
||||
* indication that the bootloader activated the link.
|
||||
*/
|
||||
if (is_mx6dq() && prepare_for_boot) {
|
||||
u32 val, gpr1, gpr12;
|
||||
|
||||
gpr1 = readl(&iomuxc_regs->gpr[1]);
|
||||
gpr12 = readl(&iomuxc_regs->gpr[12]);
|
||||
if ((gpr1 & IOMUXC_GPR1_PCIE_REF_CLK_EN) &&
|
||||
(gpr12 & IOMUXC_GPR12_PCIE_CTL_2)) {
|
||||
val = readl(priv->dbi_base + PCIE_PL_PFLR);
|
||||
val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
|
||||
val |= PCIE_PL_PFLR_FORCE_LINK;
|
||||
|
||||
imx_pcie_fix_dabt_handler(true);
|
||||
writel(val, priv->dbi_base + PCIE_PL_PFLR);
|
||||
imx_pcie_fix_dabt_handler(false);
|
||||
|
||||
gpr12 &= ~IOMUXC_GPR12_PCIE_CTL_2;
|
||||
writel(val, &iomuxc_regs->gpr[12]);
|
||||
}
|
||||
}
|
||||
setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN);
|
||||
clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6_pcie_init_phy(void)
|
||||
{
|
||||
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
|
||||
|
||||
clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE);
|
||||
|
||||
clrsetbits_le32(&iomuxc_regs->gpr[12],
|
||||
IOMUXC_GPR12_DEVICE_TYPE_MASK,
|
||||
IOMUXC_GPR12_DEVICE_TYPE_RC);
|
||||
clrsetbits_le32(&iomuxc_regs->gpr[12],
|
||||
IOMUXC_GPR12_LOS_LEVEL_MASK,
|
||||
IOMUXC_GPR12_LOS_LEVEL_9);
|
||||
|
||||
#ifdef CONFIG_MX6SX
|
||||
clrsetbits_le32(&iomuxc_regs->gpr[12],
|
||||
IOMUXC_GPR12_RX_EQ_MASK,
|
||||
IOMUXC_GPR12_RX_EQ_2);
|
||||
#endif
|
||||
|
||||
writel((0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1_OFFSET) |
|
||||
(0x0 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_3P5DB_OFFSET) |
|
||||
(20 << IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_6DB_OFFSET) |
|
||||
(127 << IOMUXC_GPR8_PCS_TX_SWING_FULL_OFFSET) |
|
||||
(127 << IOMUXC_GPR8_PCS_TX_SWING_LOW_OFFSET),
|
||||
&iomuxc_regs->gpr[8]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int imx6_pcie_toggle_power(void)
|
||||
{
|
||||
#ifdef CONFIG_PCIE_IMX_POWER_GPIO
|
||||
gpio_request(CONFIG_PCIE_IMX_POWER_GPIO, "pcie_power");
|
||||
gpio_direction_output(CONFIG_PCIE_IMX_POWER_GPIO, 0);
|
||||
mdelay(20);
|
||||
gpio_set_value(CONFIG_PCIE_IMX_POWER_GPIO, 1);
|
||||
mdelay(20);
|
||||
gpio_free(CONFIG_PCIE_IMX_POWER_GPIO);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int imx6_pcie_toggle_reset(void)
|
||||
{
|
||||
/*
|
||||
* See 'PCI EXPRESS BASE SPECIFICATION, REV 3.0, SECTION 6.6.1'
|
||||
* for detailed understanding of the PCIe CR reset logic.
|
||||
*
|
||||
* The PCIe #PERST reset line _MUST_ be connected, otherwise your
|
||||
* design does not conform to the specification. You must wait at
|
||||
* least 20 ms after de-asserting the #PERST so the EP device can
|
||||
* do self-initialisation.
|
||||
*
|
||||
* In case your #PERST pin is connected to a plain GPIO pin of the
|
||||
* CPU, you can define CONFIG_PCIE_IMX_PERST_GPIO in your board's
|
||||
* configuration file and the condition below will handle the rest
|
||||
* of the reset toggling.
|
||||
*
|
||||
* In case your #PERST toggling logic is more complex, for example
|
||||
* connected via CPLD or somesuch, you can override this function
|
||||
* in your board file and implement reset logic as needed. You must
|
||||
* not forget to wait at least 20 ms after de-asserting #PERST in
|
||||
* this case either though.
|
||||
*
|
||||
* In case your #PERST line of the PCIe EP device is not connected
|
||||
* at all, your design is broken and you should fix your design,
|
||||
* otherwise you will observe problems like for example the link
|
||||
* not coming up after rebooting the system back from running Linux
|
||||
* that uses the PCIe as well OR the PCIe link might not come up in
|
||||
* Linux at all in the first place since it's in some non-reset
|
||||
* state due to being previously used in U-Boot.
|
||||
*/
|
||||
#ifdef CONFIG_PCIE_IMX_PERST_GPIO
|
||||
gpio_request(CONFIG_PCIE_IMX_PERST_GPIO, "pcie_reset");
|
||||
gpio_direction_output(CONFIG_PCIE_IMX_PERST_GPIO, 0);
|
||||
mdelay(20);
|
||||
gpio_set_value(CONFIG_PCIE_IMX_PERST_GPIO, 1);
|
||||
mdelay(20);
|
||||
gpio_free(CONFIG_PCIE_IMX_PERST_GPIO);
|
||||
#else
|
||||
puts("WARNING: Make sure the PCIe #PERST line is connected!\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx6_pcie_deassert_core_reset(void)
|
||||
{
|
||||
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
|
||||
|
||||
imx6_pcie_toggle_power();
|
||||
|
||||
enable_pcie_clock();
|
||||
|
||||
if (is_mx6dqp())
|
||||
clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_PCIE_SW_RST);
|
||||
|
||||
/*
|
||||
* Wait for the clock to settle a bit, when the clock are sourced
|
||||
* from the CPU, we need about 30 ms to settle.
|
||||
*/
|
||||
mdelay(50);
|
||||
|
||||
#if defined(CONFIG_MX6SX)
|
||||
/* SSP_EN is not used on MX6SX anymore */
|
||||
clrbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_TEST_POWERDOWN);
|
||||
/* Clear PCIe PHY reset bit */
|
||||
clrbits_le32(&iomuxc_regs->gpr[5], IOMUXC_GPR5_PCIE_BTNRST);
|
||||
#else
|
||||
/* Enable PCIe */
|
||||
clrbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_TEST_POWERDOWN);
|
||||
setbits_le32(&iomuxc_regs->gpr[1], IOMUXC_GPR1_REF_SSP_EN);
|
||||
#endif
|
||||
|
||||
imx6_pcie_toggle_reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_pcie_link_up(struct imx_pcie_priv *priv)
|
||||
{
|
||||
struct iomuxc *iomuxc_regs = (struct iomuxc *)IOMUXC_BASE_ADDR;
|
||||
uint32_t tmp;
|
||||
int count = 0;
|
||||
|
||||
imx6_pcie_assert_core_reset(priv, false);
|
||||
imx6_pcie_init_phy();
|
||||
imx6_pcie_deassert_core_reset();
|
||||
|
||||
imx_pcie_regions_setup(priv);
|
||||
|
||||
/*
|
||||
* By default, the subordinate is set equally to the secondary
|
||||
* bus (0x01) when the RC boots.
|
||||
* This means that theoretically, only bus 1 is reachable from the RC.
|
||||
* Force the PCIe RC subordinate to 0xff, otherwise no downstream
|
||||
* devices will be detected if the enumeration is applied strictly.
|
||||
*/
|
||||
tmp = readl(priv->dbi_base + 0x18);
|
||||
tmp |= (0xff << 16);
|
||||
writel(tmp, priv->dbi_base + 0x18);
|
||||
|
||||
/*
|
||||
* FIXME: Force the PCIe RC to Gen1 operation
|
||||
* The RC must be forced into Gen1 mode before bringing the link
|
||||
* up, otherwise no downstream devices are detected. After the
|
||||
* link is up, a managed Gen1->Gen2 transition can be initiated.
|
||||
*/
|
||||
tmp = readl(priv->dbi_base + 0x7c);
|
||||
tmp &= ~0xf;
|
||||
tmp |= 0x1;
|
||||
writel(tmp, priv->dbi_base + 0x7c);
|
||||
|
||||
/* LTSSM enable, starting link. */
|
||||
setbits_le32(&iomuxc_regs->gpr[12], IOMUXC_GPR12_APPS_LTSSM_ENABLE);
|
||||
|
||||
while (!imx6_pcie_link_up(priv)) {
|
||||
udelay(10);
|
||||
count++;
|
||||
if (count >= 4000) {
|
||||
#ifdef CONFIG_PCI_SCAN_SHOW
|
||||
puts("PCI: pcie phy link never came up\n");
|
||||
#endif
|
||||
debug("DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
|
||||
readl(priv->dbi_base + PCIE_PHY_DEBUG_R0),
|
||||
readl(priv->dbi_base + PCIE_PHY_DEBUG_R1));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if !CONFIG_IS_ENABLED(DM_PCI)
|
||||
static struct imx_pcie_priv imx_pcie_priv = {
|
||||
.dbi_base = (void __iomem *)MX6_DBI_ADDR,
|
||||
.cfg_base = (void __iomem *)MX6_ROOT_ADDR,
|
||||
};
|
||||
|
||||
static struct imx_pcie_priv *priv = &imx_pcie_priv;
|
||||
|
||||
static int imx_pcie_read_config(struct pci_controller *hose, pci_dev_t d,
|
||||
int where, u32 *val)
|
||||
{
|
||||
struct imx_pcie_priv *priv = hose->priv_data;
|
||||
|
||||
return imx_pcie_read_cfg(priv, d, where, val);
|
||||
}
|
||||
|
||||
static int imx_pcie_write_config(struct pci_controller *hose, pci_dev_t d,
|
||||
int where, u32 val)
|
||||
{
|
||||
struct imx_pcie_priv *priv = hose->priv_data;
|
||||
|
||||
return imx_pcie_write_cfg(priv, d, where, val);
|
||||
}
|
||||
|
||||
void imx_pcie_init(void)
|
||||
{
|
||||
/* Static instance of the controller. */
|
||||
static struct pci_controller pcc;
|
||||
struct pci_controller *hose = &pcc;
|
||||
int ret;
|
||||
|
||||
memset(&pcc, 0, sizeof(pcc));
|
||||
|
||||
hose->priv_data = priv;
|
||||
|
||||
/* PCI I/O space */
|
||||
pci_set_region(&hose->regions[0],
|
||||
MX6_IO_ADDR, MX6_IO_ADDR,
|
||||
MX6_IO_SIZE, PCI_REGION_IO);
|
||||
|
||||
/* PCI memory space */
|
||||
pci_set_region(&hose->regions[1],
|
||||
MX6_MEM_ADDR, MX6_MEM_ADDR,
|
||||
MX6_MEM_SIZE, PCI_REGION_MEM);
|
||||
|
||||
/* System memory space */
|
||||
pci_set_region(&hose->regions[2],
|
||||
MMDC0_ARB_BASE_ADDR, MMDC0_ARB_BASE_ADDR,
|
||||
0xefffffff, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
|
||||
|
||||
hose->region_count = 3;
|
||||
|
||||
pci_set_ops(hose,
|
||||
pci_hose_read_config_byte_via_dword,
|
||||
pci_hose_read_config_word_via_dword,
|
||||
imx_pcie_read_config,
|
||||
pci_hose_write_config_byte_via_dword,
|
||||
pci_hose_write_config_word_via_dword,
|
||||
imx_pcie_write_config);
|
||||
|
||||
/* Start the controller. */
|
||||
ret = imx_pcie_link_up(priv);
|
||||
|
||||
if (!ret) {
|
||||
pci_register_hose(hose);
|
||||
hose->last_busno = pci_hose_scan(hose);
|
||||
}
|
||||
}
|
||||
|
||||
void imx_pcie_remove(void)
|
||||
{
|
||||
imx6_pcie_assert_core_reset(priv, true);
|
||||
}
|
||||
|
||||
/* Probe function. */
|
||||
void pci_init_board(void)
|
||||
{
|
||||
imx_pcie_init();
|
||||
}
|
||||
#else
|
||||
static int imx_pcie_dm_read_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong *value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct imx_pcie_priv *priv = dev_get_priv(dev);
|
||||
u32 tmpval;
|
||||
int ret;
|
||||
|
||||
ret = imx_pcie_read_cfg(priv, bdf, offset, &tmpval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*value = pci_conv_32_to_size(tmpval, offset, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_pcie_dm_write_config(struct udevice *dev, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct imx_pcie_priv *priv = dev_get_priv(dev);
|
||||
u32 tmpval, newval;
|
||||
int ret;
|
||||
|
||||
ret = imx_pcie_read_cfg(priv, bdf, offset, &tmpval);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
newval = pci_conv_size_to_32(tmpval, value, offset, size);
|
||||
return imx_pcie_write_cfg(priv, bdf, offset, newval);
|
||||
}
|
||||
|
||||
static int imx_pcie_dm_probe(struct udevice *dev)
|
||||
{
|
||||
struct imx_pcie_priv *priv = dev_get_priv(dev);
|
||||
|
||||
return imx_pcie_link_up(priv);
|
||||
}
|
||||
|
||||
static int imx_pcie_dm_remove(struct udevice *dev)
|
||||
{
|
||||
struct imx_pcie_priv *priv = dev_get_priv(dev);
|
||||
|
||||
imx6_pcie_assert_core_reset(priv, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int imx_pcie_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct imx_pcie_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->dbi_base = (void __iomem *)devfdt_get_addr_index(dev, 0);
|
||||
priv->cfg_base = (void __iomem *)devfdt_get_addr_index(dev, 1);
|
||||
if (!priv->dbi_base || !priv->cfg_base)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops imx_pcie_ops = {
|
||||
.read_config = imx_pcie_dm_read_config,
|
||||
.write_config = imx_pcie_dm_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id imx_pcie_ids[] = {
|
||||
{ .compatible = "fsl,imx6q-pcie" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(imx_pcie) = {
|
||||
.name = "imx_pcie",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = imx_pcie_ids,
|
||||
.ops = &imx_pcie_ops,
|
||||
.probe = imx_pcie_dm_probe,
|
||||
.remove = imx_pcie_dm_remove,
|
||||
.ofdata_to_platdata = imx_pcie_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct imx_pcie_priv),
|
||||
.flags = DM_FLAG_OS_PREPARE,
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,434 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Intel FPGA PCIe host controller driver
|
||||
*
|
||||
* Copyright (C) 2013-2018 Intel Corporation. All rights reserved
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define RP_TX_REG0 0x2000
|
||||
#define RP_TX_CNTRL 0x2004
|
||||
#define RP_TX_SOP BIT(0)
|
||||
#define RP_TX_EOP BIT(1)
|
||||
#define RP_RXCPL_STATUS 0x200C
|
||||
#define RP_RXCPL_SOP BIT(0)
|
||||
#define RP_RXCPL_EOP BIT(1)
|
||||
#define RP_RXCPL_REG 0x2008
|
||||
#define P2A_INT_STATUS 0x3060
|
||||
#define P2A_INT_STS_ALL 0xf
|
||||
#define P2A_INT_ENABLE 0x3070
|
||||
#define RP_CAP_OFFSET 0x70
|
||||
|
||||
/* TLP configuration type 0 and 1 */
|
||||
#define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */
|
||||
#define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */
|
||||
#define TLP_FMTTYPE_CFGRD1 0x05 /* Configuration Read Type 1 */
|
||||
#define TLP_FMTTYPE_CFGWR1 0x45 /* Configuration Write Type 1 */
|
||||
#define TLP_PAYLOAD_SIZE 0x01
|
||||
#define TLP_READ_TAG 0x1d
|
||||
#define TLP_WRITE_TAG 0x10
|
||||
#define RP_DEVFN 0
|
||||
|
||||
#define RP_CFG_ADDR(pcie, reg) \
|
||||
((pcie->hip_base) + (reg) + (1 << 20))
|
||||
#define RP_SECONDARY(pcie) \
|
||||
readb(RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS))
|
||||
#define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn))
|
||||
|
||||
#define TLP_CFGRD_DW0(pcie, bus) \
|
||||
((((bus > RP_SECONDARY(pcie)) ? TLP_FMTTYPE_CFGRD1 \
|
||||
: TLP_FMTTYPE_CFGRD0) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
|
||||
#define TLP_CFGWR_DW0(pcie, bus) \
|
||||
((((bus > RP_SECONDARY(pcie)) ? TLP_FMTTYPE_CFGWR1 \
|
||||
: TLP_FMTTYPE_CFGWR0) << 24) | \
|
||||
TLP_PAYLOAD_SIZE)
|
||||
|
||||
#define TLP_CFG_DW1(pcie, tag, be) \
|
||||
(((TLP_REQ_ID(pcie->first_busno, RP_DEVFN)) << 16) | (tag << 8) | (be))
|
||||
#define TLP_CFG_DW2(bus, dev, fn, offset) \
|
||||
(((bus) << 24) | ((dev) << 19) | ((fn) << 16) | (offset))
|
||||
|
||||
#define TLP_COMP_STATUS(s) (((s) >> 13) & 7)
|
||||
#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff)
|
||||
#define TLP_HDR_SIZE 3
|
||||
#define TLP_LOOP 20000
|
||||
#define DWORD_MASK 3
|
||||
|
||||
#define IS_ROOT_PORT(pcie, bdf) \
|
||||
((PCI_BUS(bdf) == pcie->first_busno) ? true : false)
|
||||
|
||||
#define PCI_EXP_LNKSTA 18 /* Link Status */
|
||||
#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */
|
||||
|
||||
/**
|
||||
* struct intel_fpga_pcie - Intel FPGA PCIe controller state
|
||||
* @bus: Pointer to the PCI bus
|
||||
* @cra_base: The base address of CRA register space
|
||||
* @hip_base: The base address of Rootport configuration space
|
||||
* @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.
|
||||
*/
|
||||
struct intel_fpga_pcie {
|
||||
struct udevice *bus;
|
||||
void __iomem *cra_base;
|
||||
void __iomem *hip_base;
|
||||
int first_busno;
|
||||
};
|
||||
|
||||
/**
|
||||
* Intel FPGA PCIe port uses BAR0 of RC's configuration space as the
|
||||
* translation from PCI bus to native BUS. Entire DDR region is mapped
|
||||
* into PCIe space using these registers, so it can be reached by DMA from
|
||||
* EP devices.
|
||||
* The BAR0 of bridge should be hidden during enumeration to avoid the
|
||||
* sizing and resource allocation by PCIe core.
|
||||
*/
|
||||
static bool intel_fpga_pcie_hide_rc_bar(struct intel_fpga_pcie *pcie,
|
||||
pci_dev_t bdf, int offset)
|
||||
{
|
||||
if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) == 0 &&
|
||||
PCI_FUNC(bdf) == 0 && offset == PCI_BASE_ADDRESS_0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline void cra_writel(struct intel_fpga_pcie *pcie, const u32 value,
|
||||
const u32 reg)
|
||||
{
|
||||
writel(value, pcie->cra_base + reg);
|
||||
}
|
||||
|
||||
static inline u32 cra_readl(struct intel_fpga_pcie *pcie, const u32 reg)
|
||||
{
|
||||
return readl(pcie->cra_base + reg);
|
||||
}
|
||||
|
||||
static bool intel_fpga_pcie_link_up(struct intel_fpga_pcie *pcie)
|
||||
{
|
||||
return !!(readw(RP_CFG_ADDR(pcie, RP_CAP_OFFSET + PCI_EXP_LNKSTA))
|
||||
& PCI_EXP_LNKSTA_DLLLA);
|
||||
}
|
||||
|
||||
static bool intel_fpga_pcie_addr_valid(struct intel_fpga_pcie *pcie,
|
||||
pci_dev_t bdf)
|
||||
{
|
||||
/* If there is no link, then there is no device */
|
||||
if (!IS_ROOT_PORT(pcie, bdf) && !intel_fpga_pcie_link_up(pcie))
|
||||
return false;
|
||||
|
||||
/* access only one slot on each root port */
|
||||
if (IS_ROOT_PORT(pcie, bdf) && PCI_DEV(bdf) > 0)
|
||||
return false;
|
||||
|
||||
if ((PCI_BUS(bdf) == pcie->first_busno + 1) && PCI_DEV(bdf) > 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void tlp_write_tx(struct intel_fpga_pcie *pcie, u32 reg0, u32 ctrl)
|
||||
{
|
||||
cra_writel(pcie, reg0, RP_TX_REG0);
|
||||
cra_writel(pcie, ctrl, RP_TX_CNTRL);
|
||||
}
|
||||
|
||||
static int tlp_read_packet(struct intel_fpga_pcie *pcie, u32 *value)
|
||||
{
|
||||
int i;
|
||||
u32 ctrl;
|
||||
u32 comp_status;
|
||||
u32 dw[4];
|
||||
u32 count = 0;
|
||||
|
||||
for (i = 0; i < TLP_LOOP; i++) {
|
||||
ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
|
||||
if (!(ctrl & RP_RXCPL_SOP))
|
||||
continue;
|
||||
|
||||
/* read first DW */
|
||||
dw[count++] = cra_readl(pcie, RP_RXCPL_REG);
|
||||
|
||||
/* Poll for EOP */
|
||||
for (i = 0; i < TLP_LOOP; i++) {
|
||||
ctrl = cra_readl(pcie, RP_RXCPL_STATUS);
|
||||
dw[count++] = cra_readl(pcie, RP_RXCPL_REG);
|
||||
if (ctrl & RP_RXCPL_EOP) {
|
||||
comp_status = TLP_COMP_STATUS(dw[1]);
|
||||
if (comp_status) {
|
||||
*value = pci_get_ff(PCI_SIZE_32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (value &&
|
||||
TLP_BYTE_COUNT(dw[1]) == sizeof(u32) &&
|
||||
count >= 3)
|
||||
*value = dw[3];
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
udelay(5);
|
||||
}
|
||||
|
||||
dev_err(pcie->dev, "read TLP packet timed out\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static void tlp_write_packet(struct intel_fpga_pcie *pcie, u32 *headers,
|
||||
u32 data)
|
||||
{
|
||||
tlp_write_tx(pcie, headers[0], RP_TX_SOP);
|
||||
|
||||
tlp_write_tx(pcie, headers[1], 0);
|
||||
|
||||
tlp_write_tx(pcie, headers[2], 0);
|
||||
|
||||
tlp_write_tx(pcie, data, RP_TX_EOP);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_read(struct intel_fpga_pcie *pcie, pci_dev_t bdf,
|
||||
int offset, u8 byte_en, u32 *value)
|
||||
{
|
||||
u32 headers[TLP_HDR_SIZE];
|
||||
u8 busno = PCI_BUS(bdf);
|
||||
|
||||
headers[0] = TLP_CFGRD_DW0(pcie, busno);
|
||||
headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en);
|
||||
headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
|
||||
|
||||
tlp_write_packet(pcie, headers, 0);
|
||||
|
||||
return tlp_read_packet(pcie, value);
|
||||
}
|
||||
|
||||
static int tlp_cfg_dword_write(struct intel_fpga_pcie *pcie, pci_dev_t bdf,
|
||||
int offset, u8 byte_en, u32 value)
|
||||
{
|
||||
u32 headers[TLP_HDR_SIZE];
|
||||
u8 busno = PCI_BUS(bdf);
|
||||
|
||||
headers[0] = TLP_CFGWR_DW0(pcie, busno);
|
||||
headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en);
|
||||
headers[2] = TLP_CFG_DW2(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
|
||||
|
||||
tlp_write_packet(pcie, headers, value);
|
||||
|
||||
return tlp_read_packet(pcie, NULL);
|
||||
}
|
||||
|
||||
int intel_fpga_rp_conf_addr(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, void **paddress)
|
||||
{
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(bus);
|
||||
|
||||
*paddress = RP_CFG_ADDR(pcie, offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_fpga_pcie_rp_rd_conf(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
return pci_generic_mmap_read_config(bus, intel_fpga_rp_conf_addr,
|
||||
bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int intel_fpga_pcie_rp_wr_conf(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
int ret;
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(bus);
|
||||
|
||||
ret = pci_generic_mmap_write_config(bus, intel_fpga_rp_conf_addr,
|
||||
bdf, offset, value, size);
|
||||
if (!ret) {
|
||||
/* Monitor changes to PCI_PRIMARY_BUS register on root port
|
||||
* and update local copy of root bus number accordingly.
|
||||
*/
|
||||
if (offset == PCI_PRIMARY_BUS)
|
||||
pcie->first_busno = (u8)(value);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static u8 pcie_get_byte_en(uint offset, enum pci_size_t size)
|
||||
{
|
||||
switch (size) {
|
||||
case PCI_SIZE_8:
|
||||
return 1 << (offset & 3);
|
||||
case PCI_SIZE_16:
|
||||
return 3 << (offset & 3);
|
||||
default:
|
||||
return 0xf;
|
||||
}
|
||||
}
|
||||
|
||||
static int _pcie_intel_fpga_read_config(struct intel_fpga_pcie *pcie,
|
||||
pci_dev_t bdf, uint offset,
|
||||
ulong *valuep, enum pci_size_t size)
|
||||
{
|
||||
int ret;
|
||||
u32 data;
|
||||
u8 byte_en;
|
||||
|
||||
/* Uses memory mapped method to read rootport config registers */
|
||||
if (IS_ROOT_PORT(pcie, bdf))
|
||||
return intel_fpga_pcie_rp_rd_conf(pcie->bus, bdf,
|
||||
offset, valuep, size);
|
||||
|
||||
byte_en = pcie_get_byte_en(offset, size);
|
||||
ret = tlp_cfg_dword_read(pcie, bdf, offset & ~DWORD_MASK,
|
||||
byte_en, &data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08x)\n",
|
||||
offset, size, data);
|
||||
*valuep = pci_conv_32_to_size(data, offset, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _pcie_intel_fpga_write_config(struct intel_fpga_pcie *pcie,
|
||||
pci_dev_t bdf, uint offset,
|
||||
ulong value, enum pci_size_t size)
|
||||
{
|
||||
u32 data;
|
||||
u8 byte_en;
|
||||
|
||||
dev_dbg(pcie->dev, "PCIE CFG write: (b.d.f)=(%02d.%02d.%02d)\n",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
|
||||
dev_dbg(pcie->dev, "(addr,size,val)=(0x%04x, %d, 0x%08lx)\n",
|
||||
offset, size, value);
|
||||
|
||||
/* Uses memory mapped method to read rootport config registers */
|
||||
if (IS_ROOT_PORT(pcie, bdf))
|
||||
return intel_fpga_pcie_rp_wr_conf(pcie->bus, bdf, offset,
|
||||
value, size);
|
||||
|
||||
byte_en = pcie_get_byte_en(offset, size);
|
||||
data = pci_conv_size_to_32(0, value, offset, size);
|
||||
|
||||
return tlp_cfg_dword_write(pcie, bdf, offset & ~DWORD_MASK,
|
||||
byte_en, data);
|
||||
}
|
||||
|
||||
static int pcie_intel_fpga_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(bus);
|
||||
|
||||
dev_dbg(pcie->dev, "PCIE CFG read: (b.d.f)=(%02d.%02d.%02d)\n",
|
||||
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
|
||||
|
||||
if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset)) {
|
||||
*valuep = (u32)pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!intel_fpga_pcie_addr_valid(pcie, bdf)) {
|
||||
*valuep = (u32)pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return _pcie_intel_fpga_read_config(pcie, bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int pcie_intel_fpga_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(bus);
|
||||
|
||||
if (intel_fpga_pcie_hide_rc_bar(pcie, bdf, offset))
|
||||
return 0;
|
||||
|
||||
if (!intel_fpga_pcie_addr_valid(pcie, bdf))
|
||||
return 0;
|
||||
|
||||
return _pcie_intel_fpga_write_config(pcie, bdf, offset, value,
|
||||
size);
|
||||
}
|
||||
|
||||
static int pcie_intel_fpga_probe(struct udevice *dev)
|
||||
{
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(dev);
|
||||
|
||||
pcie->bus = pci_get_controller(dev);
|
||||
pcie->first_busno = dev->seq;
|
||||
|
||||
/* clear all interrupts */
|
||||
cra_writel(pcie, P2A_INT_STS_ALL, P2A_INT_STATUS);
|
||||
/* disable all interrupts */
|
||||
cra_writel(pcie, 0, P2A_INT_ENABLE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcie_intel_fpga_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct intel_fpga_pcie *pcie = dev_get_priv(dev);
|
||||
struct fdt_resource reg_res;
|
||||
int node = dev_of_offset(dev);
|
||||
int ret;
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names",
|
||||
"Cra", ®_res);
|
||||
if (ret) {
|
||||
dev_err(dev, "resource \"Cra\" not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcie->cra_base = map_physmem(reg_res.start,
|
||||
fdt_resource_size(®_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
ret = fdt_get_named_resource(gd->fdt_blob, node, "reg", "reg-names",
|
||||
"Hip", ®_res);
|
||||
if (ret) {
|
||||
dev_err(dev, "resource \"Hip\" not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcie->hip_base = map_physmem(reg_res.start,
|
||||
fdt_resource_size(®_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pcie_intel_fpga_ops = {
|
||||
.read_config = pcie_intel_fpga_read_config,
|
||||
.write_config = pcie_intel_fpga_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pcie_intel_fpga_ids[] = {
|
||||
{ .compatible = "altr,pcie-root-port-2.0" },
|
||||
{},
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_intel_fpga) = {
|
||||
.name = "pcie_intel_fpga",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pcie_intel_fpga_ids,
|
||||
.ops = &pcie_intel_fpga_ops,
|
||||
.ofdata_to_platdata = pcie_intel_fpga_ofdata_to_platdata,
|
||||
.probe = pcie_intel_fpga_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct intel_fpga_pcie),
|
||||
};
|
||||
@@ -0,0 +1,573 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2017 NXP
|
||||
* Copyright 2014-2015 Freescale Semiconductor, Inc.
|
||||
* 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>
|
||||
#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;
|
||||
|
||||
LIST_HEAD(ls_pcie_list);
|
||||
|
||||
static 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)
|
||||
{
|
||||
out_le32(pcie->dbi + offset, value);
|
||||
}
|
||||
|
||||
static unsigned int ctrl_readl(struct ls_pcie *pcie, unsigned int offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
return in_be32(pcie->ctrl + offset);
|
||||
else
|
||||
return in_le32(pcie->ctrl + offset);
|
||||
}
|
||||
|
||||
static void ctrl_writel(struct ls_pcie *pcie, unsigned int value,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
out_be32(pcie->ctrl + offset, value);
|
||||
else
|
||||
out_le32(pcie->ctrl + offset, value);
|
||||
}
|
||||
|
||||
static int ls_pcie_ltssm(struct ls_pcie *pcie)
|
||||
{
|
||||
u32 state;
|
||||
uint svr;
|
||||
|
||||
svr = get_svr();
|
||||
if (((svr >> SVR_VAR_PER_SHIFT) & SVR_LS102XA_MASK) == SVR_LS102XA) {
|
||||
state = ctrl_readl(pcie, LS1021_PEXMSCPORTSR(pcie->idx));
|
||||
state = (state >> LS1021_LTSSM_STATE_SHIFT) & LTSSM_STATE_MASK;
|
||||
} else {
|
||||
state = ctrl_readl(pcie, PCIE_PF_DBG) & LTSSM_STATE_MASK;
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int ls_pcie_link_up(struct ls_pcie *pcie)
|
||||
{
|
||||
int ltssm;
|
||||
|
||||
ltssm = ls_pcie_ltssm(pcie);
|
||||
if (ltssm < LTSSM_PCIE_L0)
|
||||
return 0;
|
||||
|
||||
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)
|
||||
{
|
||||
dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | idx, PCIE_ATU_VIEWPORT);
|
||||
dbi_writel(pcie, (u32)phys, PCIE_ATU_LOWER_BASE);
|
||||
dbi_writel(pcie, phys >> 32, PCIE_ATU_UPPER_BASE);
|
||||
dbi_writel(pcie, (u32)phys + size - 1, PCIE_ATU_LIMIT);
|
||||
dbi_writel(pcie, (u32)bus_addr, PCIE_ATU_LOWER_TARGET);
|
||||
dbi_writel(pcie, bus_addr >> 32, PCIE_ATU_UPPER_TARGET);
|
||||
dbi_writel(pcie, type, PCIE_ATU_CR1);
|
||||
dbi_writel(pcie, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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, 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)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < PCIE_ATU_REGION_NUM; i++) {
|
||||
dbi_writel(pcie, PCIE_ATU_REGION_OUTBOUND | i,
|
||||
PCIE_ATU_VIEWPORT);
|
||||
debug("iATU%d:\n", i);
|
||||
debug("\tLOWER PHYS 0x%08x\n",
|
||||
dbi_readl(pcie, PCIE_ATU_LOWER_BASE));
|
||||
debug("\tUPPER PHYS 0x%08x\n",
|
||||
dbi_readl(pcie, PCIE_ATU_UPPER_BASE));
|
||||
debug("\tLOWER BUS 0x%08x\n",
|
||||
dbi_readl(pcie, PCIE_ATU_LOWER_TARGET));
|
||||
debug("\tUPPER BUS 0x%08x\n",
|
||||
dbi_readl(pcie, PCIE_ATU_UPPER_TARGET));
|
||||
debug("\tLIMIT 0x%08x\n",
|
||||
readl(pcie->dbi + 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(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(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)
|
||||
{
|
||||
u32 sriov;
|
||||
|
||||
sriov = in_le32(pcie->dbi + PCIE_SRIOV);
|
||||
|
||||
/*
|
||||
* TODO: For PCIe controller with SRIOV, the method to disable bars
|
||||
* is different and more complex, so will add later.
|
||||
*/
|
||||
if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV)
|
||||
return;
|
||||
|
||||
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, 0, 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);
|
||||
}
|
||||
|
||||
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),
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2017 NXP
|
||||
* Copyright 2014-2015 Freescale Semiconductor, Inc.
|
||||
* Layerscape PCIe driver
|
||||
*/
|
||||
|
||||
#ifndef _PCIE_LAYERSCAPE_H_
|
||||
#define _PCIE_LAYERSCAPE_H_
|
||||
#include <pci.h>
|
||||
#include <dm.h>
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_BUS
|
||||
#define CONFIG_SYS_PCI_MEMORY_BUS CONFIG_SYS_SDRAM_BASE
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_PHYS
|
||||
#define CONFIG_SYS_PCI_MEMORY_PHYS CONFIG_SYS_SDRAM_BASE
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_SIZE
|
||||
#define CONFIG_SYS_PCI_MEMORY_SIZE (2 * 1024 * 1024 * 1024UL) /* 2G */
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_EP_MEMORY_BASE
|
||||
#define CONFIG_SYS_PCI_EP_MEMORY_BASE CONFIG_SYS_LOAD_ADDR
|
||||
#endif
|
||||
|
||||
#define PCIE_PHYS_SIZE 0x200000000
|
||||
#define LS2088A_PCIE_PHYS_SIZE 0x800000000
|
||||
#define LS2088A_PCIE1_PHYS_ADDR 0x2000000000
|
||||
|
||||
/* iATU registers */
|
||||
#define PCIE_ATU_VIEWPORT 0x900
|
||||
#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
|
||||
#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
|
||||
#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
|
||||
#define PCIE_ATU_REGION_INDEX3 (0x3 << 0)
|
||||
#define PCIE_ATU_REGION_NUM 6
|
||||
#define PCIE_ATU_CR1 0x904
|
||||
#define PCIE_ATU_TYPE_MEM (0x0 << 0)
|
||||
#define PCIE_ATU_TYPE_IO (0x2 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
|
||||
#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
|
||||
#define PCIE_ATU_CR2 0x908
|
||||
#define PCIE_ATU_ENABLE (0x1 << 31)
|
||||
#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
|
||||
#define PCIE_ATU_BAR_NUM(bar) ((bar) << 8)
|
||||
#define PCIE_ATU_LOWER_BASE 0x90C
|
||||
#define PCIE_ATU_UPPER_BASE 0x910
|
||||
#define PCIE_ATU_LIMIT 0x914
|
||||
#define PCIE_ATU_LOWER_TARGET 0x918
|
||||
#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
|
||||
#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
|
||||
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
|
||||
#define PCIE_ATU_UPPER_TARGET 0x91C
|
||||
|
||||
/* DBI registers */
|
||||
#define PCIE_SRIOV 0x178
|
||||
#define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
|
||||
#define PCIE_DBI_RO_WR_EN 0x8bc
|
||||
|
||||
#define PCIE_LINK_CAP 0x7c
|
||||
#define PCIE_LINK_SPEED_MASK 0xf
|
||||
#define PCIE_LINK_WIDTH_MASK 0x3f0
|
||||
#define PCIE_LINK_STA 0x82
|
||||
|
||||
#define LTSSM_STATE_MASK 0x3f
|
||||
#define LTSSM_PCIE_L0 0x11 /* L0 state */
|
||||
|
||||
#define PCIE_DBI_SIZE 0x100000 /* 1M */
|
||||
|
||||
#define PCIE_LCTRL0_CFG2_ENABLE (1 << 31)
|
||||
#define PCIE_LCTRL0_VF(vf) ((vf) << 22)
|
||||
#define PCIE_LCTRL0_PF(pf) ((pf) << 16)
|
||||
#define PCIE_LCTRL0_VF_ACTIVE (1 << 21)
|
||||
#define PCIE_LCTRL0_VAL(pf, vf) (PCIE_LCTRL0_PF(pf) | \
|
||||
PCIE_LCTRL0_VF(vf) | \
|
||||
((vf) == 0 ? 0 : PCIE_LCTRL0_VF_ACTIVE) | \
|
||||
PCIE_LCTRL0_CFG2_ENABLE)
|
||||
|
||||
#define PCIE_NO_SRIOV_BAR_BASE 0x1000
|
||||
|
||||
#define PCIE_PF_NUM 2
|
||||
#define PCIE_VF_NUM 64
|
||||
|
||||
#define PCIE_BAR0_SIZE (4 * 1024) /* 4K */
|
||||
#define PCIE_BAR1_SIZE (8 * 1024) /* 8K for MSIX */
|
||||
#define PCIE_BAR2_SIZE (4 * 1024) /* 4K */
|
||||
#define PCIE_BAR4_SIZE (1 * 1024 * 1024) /* 1M */
|
||||
|
||||
/* LUT registers */
|
||||
#define PCIE_LUT_UDR(n) (0x800 + (n) * 8)
|
||||
#define PCIE_LUT_LDR(n) (0x804 + (n) * 8)
|
||||
#define PCIE_LUT_ENABLE (1 << 31)
|
||||
#define PCIE_LUT_ENTRY_COUNT 32
|
||||
|
||||
/* PF Controll registers */
|
||||
#define PCIE_PF_CONFIG 0x14
|
||||
#define PCIE_PF_VF_CTRL 0x7F8
|
||||
#define PCIE_PF_DBG 0x7FC
|
||||
#define PCIE_CONFIG_READY (1 << 0)
|
||||
|
||||
#define PCIE_SRDS_PRTCL(idx) (PCIE1 + (idx))
|
||||
#define PCIE_SYS_BASE_ADDR 0x3400000
|
||||
#define PCIE_CCSR_SIZE 0x0100000
|
||||
|
||||
/* CS2 */
|
||||
#define PCIE_CS2_OFFSET 0x1000 /* For PCIe without SR-IOV */
|
||||
|
||||
#define SVR_LS102XA 0
|
||||
#define SVR_VAR_PER_SHIFT 8
|
||||
#define SVR_LS102XA_MASK 0x700
|
||||
#define SVR_LS2088A 0x870900
|
||||
#define SVR_LS2084A 0x870910
|
||||
#define SVR_LS2048A 0x870920
|
||||
#define SVR_LS2044A 0x870930
|
||||
#define SVR_LS2081A 0x870918
|
||||
#define SVR_LS2041A 0x870914
|
||||
|
||||
/* LS1021a PCIE space */
|
||||
#define LS1021_PCIE_SPACE_OFFSET 0x4000000000ULL
|
||||
#define LS1021_PCIE_SPACE_SIZE 0x0800000000ULL
|
||||
|
||||
/* LS1021a PEX1/2 Misc Ports Status Register */
|
||||
#define LS1021_PEXMSCPORTSR(pex_idx) (0x94 + (pex_idx) * 4)
|
||||
#define LS1021_LTSSM_STATE_SHIFT 20
|
||||
|
||||
struct ls_pcie {
|
||||
int idx;
|
||||
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 mode;
|
||||
};
|
||||
|
||||
extern struct list_head ls_pcie_list;
|
||||
|
||||
#endif /* _PCIE_LAYERSCAPE_H_ */
|
||||
@@ -0,0 +1,290 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2017 NXP
|
||||
* Copyright 2014-2015 Freescale Semiconductor, Inc.
|
||||
* Layerscape PCIe driver
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <asm/arch/fsl_serdes.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#ifdef CONFIG_OF_BOARD_SETUP
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
#ifdef CONFIG_ARM
|
||||
#include <asm/arch/clock.h>
|
||||
#endif
|
||||
#include "pcie_layerscape.h"
|
||||
|
||||
#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
|
||||
/*
|
||||
* Return next available LUT index.
|
||||
*/
|
||||
static int ls_pcie_next_lut_index(struct ls_pcie *pcie)
|
||||
{
|
||||
if (pcie->next_lut_index < PCIE_LUT_ENTRY_COUNT)
|
||||
return pcie->next_lut_index++;
|
||||
else
|
||||
return -ENOSPC; /* LUT is full */
|
||||
}
|
||||
|
||||
/* returns the next available streamid for pcie, -errno if failed */
|
||||
static int ls_pcie_next_streamid(void)
|
||||
{
|
||||
static int next_stream_id = FSL_PEX_STREAM_ID_START;
|
||||
|
||||
if (next_stream_id > FSL_PEX_STREAM_ID_END)
|
||||
return -EINVAL;
|
||||
|
||||
return next_stream_id++;
|
||||
}
|
||||
|
||||
static void lut_writel(struct ls_pcie *pcie, unsigned int value,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
out_be32(pcie->lut + offset, value);
|
||||
else
|
||||
out_le32(pcie->lut + offset, value);
|
||||
}
|
||||
|
||||
/*
|
||||
* Program a single LUT entry
|
||||
*/
|
||||
static void ls_pcie_lut_set_mapping(struct ls_pcie *pcie, 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));
|
||||
}
|
||||
|
||||
/*
|
||||
* An msi-map is a property to be added to the pci controller
|
||||
* node. It is a table, where each entry consists of 4 fields
|
||||
* e.g.:
|
||||
*
|
||||
* 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(void *blob, struct ls_pcie *pcie,
|
||||
u32 devid, u32 streamid)
|
||||
{
|
||||
u32 *prop;
|
||||
u32 phandle;
|
||||
int nodeoffset;
|
||||
uint svr;
|
||||
char *compat = NULL;
|
||||
|
||||
/* find pci controller node */
|
||||
nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
|
||||
pcie->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;
|
||||
if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
|
||||
svr == SVR_LS2048A || svr == SVR_LS2044A ||
|
||||
svr == SVR_LS2081A || svr == SVR_LS2041A)
|
||||
compat = "fsl,ls2088a-pcie";
|
||||
else
|
||||
compat = CONFIG_FSL_PCIE_COMPAT;
|
||||
if (compat)
|
||||
nodeoffset = fdt_node_offset_by_compat_reg(blob,
|
||||
compat, pcie->dbi_res.start);
|
||||
#endif
|
||||
if (nodeoffset < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* get phandle to MSI controller */
|
||||
prop = (u32 *)fdt_getprop(blob, nodeoffset, "msi-parent", 0);
|
||||
if (prop == NULL) {
|
||||
debug("\n%s: ERROR: missing msi-parent: PCIe%d\n",
|
||||
__func__, pcie->idx);
|
||||
return;
|
||||
}
|
||||
phandle = fdt32_to_cpu(*prop);
|
||||
|
||||
/* set one msi-map row */
|
||||
fdt_appendprop_u32(blob, nodeoffset, "msi-map", devid);
|
||||
fdt_appendprop_u32(blob, nodeoffset, "msi-map", phandle);
|
||||
fdt_appendprop_u32(blob, nodeoffset, "msi-map", streamid);
|
||||
fdt_appendprop_u32(blob, nodeoffset, "msi-map", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* An iommu-map is a property to be added to the pci controller
|
||||
* node. It is a table, where each entry consists of 4 fields
|
||||
* e.g.:
|
||||
*
|
||||
* 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(void *blob, struct ls_pcie *pcie,
|
||||
u32 devid, u32 streamid)
|
||||
{
|
||||
u32 *prop;
|
||||
u32 iommu_map[4];
|
||||
int nodeoffset;
|
||||
int lenp;
|
||||
uint svr;
|
||||
char *compat = NULL;
|
||||
|
||||
/* find pci controller node */
|
||||
nodeoffset = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
|
||||
pcie->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;
|
||||
if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
|
||||
svr == SVR_LS2048A || svr == SVR_LS2044A ||
|
||||
svr == SVR_LS2081A || svr == SVR_LS2041A)
|
||||
compat = "fsl,ls2088a-pcie";
|
||||
else
|
||||
compat = CONFIG_FSL_PCIE_COMPAT;
|
||||
|
||||
if (compat)
|
||||
nodeoffset = fdt_node_offset_by_compat_reg(blob,
|
||||
compat, pcie->dbi_res.start);
|
||||
#endif
|
||||
if (nodeoffset < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
/* get phandle to iommu controller */
|
||||
prop = fdt_getprop_w(blob, nodeoffset, "iommu-map", &lenp);
|
||||
if (prop == NULL) {
|
||||
debug("\n%s: ERROR: missing iommu-map: PCIe%d\n",
|
||||
__func__, pcie->idx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set iommu-map row */
|
||||
iommu_map[0] = cpu_to_fdt32(devid);
|
||||
iommu_map[1] = *++prop;
|
||||
iommu_map[2] = cpu_to_fdt32(streamid);
|
||||
iommu_map[3] = cpu_to_fdt32(1);
|
||||
|
||||
if (devid == 0) {
|
||||
fdt_setprop_inplace(blob, nodeoffset, "iommu-map",
|
||||
iommu_map, 16);
|
||||
} else {
|
||||
fdt_appendprop(blob, nodeoffset, "iommu-map", iommu_map, 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void fdt_fixup_pcie(void *blob)
|
||||
{
|
||||
struct udevice *dev, *bus;
|
||||
struct ls_pcie *pcie;
|
||||
int streamid;
|
||||
int index;
|
||||
pci_dev_t bdf;
|
||||
|
||||
/* Scan all known buses */
|
||||
for (pci_find_first_device(&dev);
|
||||
dev;
|
||||
pci_find_next_device(&dev)) {
|
||||
for (bus = dev; device_is_on_pci_bus(bus);)
|
||||
bus = bus->parent;
|
||||
pcie = dev_get_priv(bus);
|
||||
|
||||
streamid = ls_pcie_next_streamid();
|
||||
if (streamid < 0) {
|
||||
debug("ERROR: no stream ids free\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
index = ls_pcie_next_lut_index(pcie);
|
||||
if (index < 0) {
|
||||
debug("ERROR: no LUT indexes free\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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,
|
||||
streamid);
|
||||
/* update msi-map in device tree */
|
||||
fdt_pcie_set_msi_map_entry(blob, pcie, bdf >> 8,
|
||||
streamid);
|
||||
/* update iommu-map in device tree */
|
||||
fdt_pcie_set_iommu_map_entry(blob, pcie, bdf >> 8,
|
||||
streamid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ft_pcie_rc_fix(void *blob, struct ls_pcie *pcie)
|
||||
{
|
||||
int off;
|
||||
uint svr;
|
||||
char *compat = NULL;
|
||||
|
||||
off = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie",
|
||||
pcie->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;
|
||||
if (svr == SVR_LS2088A || svr == SVR_LS2084A ||
|
||||
svr == SVR_LS2048A || svr == SVR_LS2044A ||
|
||||
svr == SVR_LS2081A || svr == SVR_LS2041A)
|
||||
compat = "fsl,ls2088a-pcie";
|
||||
else
|
||||
compat = CONFIG_FSL_PCIE_COMPAT;
|
||||
if (compat)
|
||||
off = fdt_node_offset_by_compat_reg(blob,
|
||||
compat, pcie->dbi_res.start);
|
||||
#endif
|
||||
if (off < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcie->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)
|
||||
{
|
||||
int off;
|
||||
|
||||
off = fdt_node_offset_by_compat_reg(blob, "fsl,ls-pcie-ep",
|
||||
pcie->dbi_res.start);
|
||||
if (off < 0)
|
||||
return;
|
||||
|
||||
if (pcie->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)
|
||||
{
|
||||
ft_pcie_ep_fix(blob, pcie);
|
||||
ft_pcie_rc_fix(blob, pcie);
|
||||
}
|
||||
|
||||
/* Fixup Kernel DT for PCIe */
|
||||
void ft_pci_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
struct ls_pcie *pcie;
|
||||
|
||||
list_for_each_entry(pcie, &ls_pcie_list, list)
|
||||
ft_pcie_ls_setup(blob, pcie);
|
||||
|
||||
#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
|
||||
fdt_fixup_pcie(blob);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* !CONFIG_OF_BOARD_SETUP */
|
||||
void ft_pci_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,572 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+ OR X11
|
||||
/*
|
||||
* Copyright 2018-2019 NXP
|
||||
*
|
||||
* PCIe Gen4 driver for NXP Layerscape SoCs
|
||||
* Author: Hou Zhiqiang <Minder.Hou@gmail.com>
|
||||
*/
|
||||
|
||||
#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 <linux/sizes.h>
|
||||
|
||||
#include "pcie_layerscape_gen4.h"
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
LIST_HEAD(ls_pcie_g4_list);
|
||||
|
||||
static u64 bar_size[4] = {
|
||||
PCIE_BAR0_SIZE,
|
||||
PCIE_BAR1_SIZE,
|
||||
PCIE_BAR2_SIZE,
|
||||
PCIE_BAR4_SIZE
|
||||
};
|
||||
|
||||
static int ls_pcie_g4_ltssm(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
u32 state;
|
||||
|
||||
state = pf_ctrl_readl(pcie, PCIE_LTSSM_STA) & LTSSM_STATE_MASK;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_link_up(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
int ltssm;
|
||||
|
||||
ltssm = ls_pcie_g4_ltssm(pcie);
|
||||
if (ltssm != LTSSM_PCIE_L0)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_enable_cfg(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
ccsr_writel(pcie, GPEX_CFG_READY, PCIE_CONFIG_READY);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_cfg_set_target(struct ls_pcie_g4 *pcie, u32 target)
|
||||
{
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(0), target);
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(0), 0);
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_outbound_win_set(struct ls_pcie_g4 *pcie, int idx,
|
||||
int type, u64 phys, u64 bus_addr,
|
||||
pci_size_t size)
|
||||
{
|
||||
u32 val;
|
||||
u32 size_h, size_l;
|
||||
|
||||
if (idx >= PAB_WINS_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
size_h = upper_32_bits(~(size - 1));
|
||||
size_l = lower_32_bits(~(size - 1));
|
||||
|
||||
val = ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(idx));
|
||||
val &= ~((AXI_AMAP_CTRL_TYPE_MASK << AXI_AMAP_CTRL_TYPE_SHIFT) |
|
||||
(AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT) |
|
||||
AXI_AMAP_CTRL_EN);
|
||||
val |= ((type & AXI_AMAP_CTRL_TYPE_MASK) << AXI_AMAP_CTRL_TYPE_SHIFT) |
|
||||
((size_l >> AXI_AMAP_CTRL_SIZE_SHIFT) <<
|
||||
AXI_AMAP_CTRL_SIZE_SHIFT) | AXI_AMAP_CTRL_EN;
|
||||
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_CTRL(idx), val);
|
||||
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_AXI_WIN(idx), lower_32_bits(phys));
|
||||
ccsr_writel(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(idx), upper_32_bits(phys));
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr));
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr));
|
||||
ccsr_writel(pcie, PAB_EXT_AXI_AMAP_SIZE(idx), size_h);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_rc_inbound_win_set(struct ls_pcie_g4 *pcie, int idx,
|
||||
int type, u64 phys, u64 bus_addr,
|
||||
pci_size_t size)
|
||||
{
|
||||
u32 val;
|
||||
pci_size_t win_size = ~(size - 1);
|
||||
|
||||
val = ccsr_readl(pcie, PAB_PEX_AMAP_CTRL(idx));
|
||||
|
||||
val &= ~(PEX_AMAP_CTRL_TYPE_MASK << PEX_AMAP_CTRL_TYPE_SHIFT);
|
||||
val &= ~(PEX_AMAP_CTRL_EN_MASK << PEX_AMAP_CTRL_EN_SHIFT);
|
||||
val = (val | (type << PEX_AMAP_CTRL_TYPE_SHIFT));
|
||||
val = (val | (1 << PEX_AMAP_CTRL_EN_SHIFT));
|
||||
|
||||
ccsr_writel(pcie, PAB_PEX_AMAP_CTRL(idx),
|
||||
val | lower_32_bits(win_size));
|
||||
|
||||
ccsr_writel(pcie, PAB_EXT_PEX_AMAP_SIZE(idx), upper_32_bits(win_size));
|
||||
ccsr_writel(pcie, PAB_PEX_AMAP_AXI_WIN(idx), lower_32_bits(phys));
|
||||
ccsr_writel(pcie, PAB_EXT_PEX_AMAP_AXI_WIN(idx), upper_32_bits(phys));
|
||||
ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr));
|
||||
ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_dump_wins(struct ls_pcie_g4 *pcie, int wins)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < wins; i++) {
|
||||
debug("APIO Win%d:\n", i);
|
||||
debug("\tLOWER PHYS: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(i)));
|
||||
debug("\tUPPER PHYS: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(i)));
|
||||
debug("\tLOWER BUS: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_L(i)));
|
||||
debug("\tUPPER BUS: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(i)));
|
||||
debug("\tSIZE: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i)) &
|
||||
(AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT));
|
||||
debug("\tEXT_SIZE: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_EXT_AXI_AMAP_SIZE(i)));
|
||||
debug("\tPARAM: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(i)));
|
||||
debug("\tCTRL: 0x%08x\n",
|
||||
ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i)));
|
||||
}
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_setup_wins(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
struct pci_region *io, *mem, *pref;
|
||||
int idx = 1;
|
||||
|
||||
/* INBOUND WIN */
|
||||
ls_pcie_g4_rc_inbound_win_set(pcie, 0, IB_TYPE_MEM_F, 0, 0, SIZE_1T);
|
||||
|
||||
/* OUTBOUND WIN 0: CFG */
|
||||
ls_pcie_g4_outbound_win_set(pcie, 0, PAB_AXI_TYPE_CFG,
|
||||
pcie->cfg_res.start, 0,
|
||||
fdt_resource_size(&pcie->cfg_res));
|
||||
|
||||
pci_get_regions(pcie->bus, &io, &mem, &pref);
|
||||
|
||||
if (io)
|
||||
/* OUTBOUND WIN: IO */
|
||||
ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_IO,
|
||||
io->phys_start, io->bus_start,
|
||||
io->size);
|
||||
|
||||
if (mem)
|
||||
/* OUTBOUND WIN: MEM */
|
||||
ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_MEM,
|
||||
mem->phys_start, mem->bus_start,
|
||||
mem->size);
|
||||
|
||||
if (pref)
|
||||
/* OUTBOUND WIN: perf MEM */
|
||||
ls_pcie_g4_outbound_win_set(pcie, idx++, PAB_AXI_TYPE_MEM,
|
||||
pref->phys_start, pref->bus_start,
|
||||
pref->size);
|
||||
|
||||
ls_pcie_g4_dump_wins(pcie, idx);
|
||||
}
|
||||
|
||||
/* Return 0 if the address is valid, -errno if not valid */
|
||||
static int ls_pcie_g4_addr_valid(struct ls_pcie_g4 *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_g4_link_up(pcie)))
|
||||
return -EINVAL;
|
||||
|
||||
if (PCI_BUS(bdf) <= (bus->seq + 1) && (PCI_DEV(bdf) > 0))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *ls_pcie_g4_conf_address(struct ls_pcie_g4 *pcie, pci_dev_t bdf,
|
||||
int offset)
|
||||
{
|
||||
struct udevice *bus = pcie->bus;
|
||||
u32 target;
|
||||
|
||||
if (PCI_BUS(bdf) == bus->seq) {
|
||||
if (offset < INDIRECT_ADDR_BNDRY) {
|
||||
ccsr_set_page(pcie, 0);
|
||||
return pcie->ccsr + offset;
|
||||
}
|
||||
|
||||
ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset));
|
||||
return pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset);
|
||||
}
|
||||
|
||||
target = PAB_TARGET_BUS(PCI_BUS(bdf) - bus->seq) |
|
||||
PAB_TARGET_DEV(PCI_DEV(bdf)) |
|
||||
PAB_TARGET_FUNC(PCI_FUNC(bdf));
|
||||
|
||||
ls_pcie_g4_cfg_set_target(pcie, target);
|
||||
|
||||
return pcie->cfg + offset;
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct ls_pcie_g4 *pcie = dev_get_priv(bus);
|
||||
void *address;
|
||||
int ret = 0;
|
||||
|
||||
if (ls_pcie_g4_addr_valid(pcie, bdf)) {
|
||||
*valuep = pci_get_ff(size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
address = ls_pcie_g4_conf_address(pcie, bdf, offset);
|
||||
|
||||
switch (size) {
|
||||
case PCI_SIZE_8:
|
||||
*valuep = readb(address);
|
||||
break;
|
||||
case PCI_SIZE_16:
|
||||
*valuep = readw(address);
|
||||
break;
|
||||
case PCI_SIZE_32:
|
||||
*valuep = readl(address);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_write_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong value,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
struct ls_pcie_g4 *pcie = dev_get_priv(bus);
|
||||
void *address;
|
||||
|
||||
if (ls_pcie_g4_addr_valid(pcie, bdf))
|
||||
return 0;
|
||||
|
||||
address = ls_pcie_g4_conf_address(pcie, bdf, offset);
|
||||
|
||||
switch (size) {
|
||||
case PCI_SIZE_8:
|
||||
writeb(value, address);
|
||||
return 0;
|
||||
case PCI_SIZE_16:
|
||||
writew(value, address);
|
||||
return 0;
|
||||
case PCI_SIZE_32:
|
||||
writel(value, address);
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_setup_ctrl(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* Fix class code */
|
||||
val = ccsr_readl(pcie, GPEX_CLASSCODE);
|
||||
val &= ~(GPEX_CLASSCODE_MASK << GPEX_CLASSCODE_SHIFT);
|
||||
val |= PCI_CLASS_BRIDGE_PCI << GPEX_CLASSCODE_SHIFT;
|
||||
ccsr_writel(pcie, GPEX_CLASSCODE, val);
|
||||
|
||||
/* Enable APIO and Memory/IO/CFG Wins */
|
||||
val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0));
|
||||
val |= APIO_EN | MEM_WIN_EN | IO_WIN_EN | CFG_WIN_EN;
|
||||
ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val);
|
||||
|
||||
ls_pcie_g4_setup_wins(pcie);
|
||||
|
||||
pcie->stream_id_cur = 0;
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_inbound_win_set(struct ls_pcie_g4 *pcie, int pf,
|
||||
int bar, u64 phys)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
/* PF BAR1 is for MSI-X and only need to enable */
|
||||
if (bar == 1) {
|
||||
ccsr_writel(pcie, PAB_PEX_BAR_AMAP(pf, bar), BAR_AMAP_EN);
|
||||
return;
|
||||
}
|
||||
|
||||
val = upper_32_bits(phys);
|
||||
ccsr_writel(pcie, PAB_EXT_PEX_BAR_AMAP(pf, bar), val);
|
||||
val = lower_32_bits(phys) | BAR_AMAP_EN;
|
||||
ccsr_writel(pcie, PAB_PEX_BAR_AMAP(pf, bar), val);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_setup_wins(struct ls_pcie_g4 *pcie, int pf)
|
||||
{
|
||||
u64 phys;
|
||||
int bar;
|
||||
u32 val;
|
||||
|
||||
if ((!pcie->sriov_support && pf > LS_G4_PF0) || pf > LS_G4_PF1)
|
||||
return;
|
||||
|
||||
phys = CONFIG_SYS_PCI_EP_MEMORY_BASE + PCIE_BAR_SIZE * 4 * pf;
|
||||
for (bar = 0; bar < PF_BAR_NUM; bar++) {
|
||||
ls_pcie_g4_ep_inbound_win_set(pcie, pf, bar, phys);
|
||||
phys += PCIE_BAR_SIZE;
|
||||
}
|
||||
|
||||
/* OUTBOUND: map MEM */
|
||||
ls_pcie_g4_outbound_win_set(pcie, pf, PAB_AXI_TYPE_MEM,
|
||||
pcie->cfg_res.start +
|
||||
CONFIG_SYS_PCI_MEMORY_SIZE * pf, 0x0,
|
||||
CONFIG_SYS_PCI_MEMORY_SIZE);
|
||||
|
||||
val = ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(pf));
|
||||
val &= ~FUNC_NUM_PCIE_MASK;
|
||||
val |= pf;
|
||||
ccsr_writel(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(pf), val);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_enable_bar(struct ls_pcie_g4 *pcie, int pf,
|
||||
int bar, bool vf_bar, bool enable)
|
||||
{
|
||||
u32 val;
|
||||
u32 bar_pos = BAR_POS(bar, pf, vf_bar);
|
||||
|
||||
val = ccsr_readl(pcie, GPEX_BAR_ENABLE);
|
||||
if (enable)
|
||||
val |= 1 << bar_pos;
|
||||
else
|
||||
val &= ~(1 << bar_pos);
|
||||
ccsr_writel(pcie, GPEX_BAR_ENABLE, val);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_set_bar_size(struct ls_pcie_g4 *pcie, int pf,
|
||||
int bar, bool vf_bar, u64 size)
|
||||
{
|
||||
u32 bar_pos = BAR_POS(bar, pf, vf_bar);
|
||||
u32 mask_l = lower_32_bits(~(size - 1));
|
||||
u32 mask_h = upper_32_bits(~(size - 1));
|
||||
|
||||
ccsr_writel(pcie, GPEX_BAR_SELECT, bar_pos);
|
||||
ccsr_writel(pcie, GPEX_BAR_SIZE_LDW, mask_l);
|
||||
ccsr_writel(pcie, GPEX_BAR_SIZE_UDW, mask_h);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_setup_bar(struct ls_pcie_g4 *pcie, int pf,
|
||||
int bar, bool vf_bar, u64 size)
|
||||
{
|
||||
bool en = size ? true : false;
|
||||
|
||||
ls_pcie_g4_ep_enable_bar(pcie, pf, bar, vf_bar, en);
|
||||
ls_pcie_g4_ep_set_bar_size(pcie, pf, bar, vf_bar, size);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_ep_setup_bars(struct ls_pcie_g4 *pcie, int pf)
|
||||
{
|
||||
int bar;
|
||||
|
||||
/* Setup PF BARs */
|
||||
for (bar = 0; bar < PF_BAR_NUM; bar++)
|
||||
ls_pcie_g4_ep_setup_bar(pcie, pf, bar, false, bar_size[bar]);
|
||||
|
||||
if (!pcie->sriov_support)
|
||||
return;
|
||||
|
||||
/* Setup VF BARs */
|
||||
for (bar = 0; bar < VF_BAR_NUM; bar++)
|
||||
ls_pcie_g4_ep_setup_bar(pcie, pf, bar, true, bar_size[bar]);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_set_sriov(struct ls_pcie_g4 *pcie, int pf)
|
||||
{
|
||||
unsigned int val;
|
||||
|
||||
val = ccsr_readl(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf));
|
||||
val &= ~(TTL_VF_MASK << TTL_VF_SHIFT);
|
||||
val |= PCIE_VF_NUM << TTL_VF_SHIFT;
|
||||
val &= ~(INI_VF_MASK << INI_VF_SHIFT);
|
||||
val |= PCIE_VF_NUM << INI_VF_SHIFT;
|
||||
ccsr_writel(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf), val);
|
||||
|
||||
val = ccsr_readl(pcie, PCIE_SRIOV_VF_OFFSET_STRIDE);
|
||||
val += PCIE_VF_NUM * pf - pf;
|
||||
ccsr_writel(pcie, GPEX_SRIOV_VF_OFFSET_STRIDE(pf), val);
|
||||
}
|
||||
|
||||
static void ls_pcie_g4_setup_ep(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
u32 pf, sriov;
|
||||
u32 val;
|
||||
int i;
|
||||
|
||||
/* Enable APIO and Memory Win */
|
||||
val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0));
|
||||
val |= APIO_EN | MEM_WIN_EN;
|
||||
ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val);
|
||||
|
||||
sriov = ccsr_readl(pcie, PCIE_SRIOV_CAPABILITY);
|
||||
if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV)
|
||||
pcie->sriov_support = 1;
|
||||
|
||||
pf = pcie->sriov_support ? PCIE_PF_NUM : 1;
|
||||
|
||||
for (i = 0; i < pf; i++) {
|
||||
ls_pcie_g4_ep_setup_bars(pcie, i);
|
||||
ls_pcie_g4_ep_setup_wins(pcie, i);
|
||||
if (pcie->sriov_support)
|
||||
ls_pcie_g4_set_sriov(pcie, i);
|
||||
}
|
||||
|
||||
ls_pcie_g4_ep_enable_cfg(pcie);
|
||||
ls_pcie_g4_dump_wins(pcie, pf);
|
||||
}
|
||||
|
||||
static int ls_pcie_g4_probe(struct udevice *dev)
|
||||
{
|
||||
struct ls_pcie_g4 *pcie = dev_get_priv(dev);
|
||||
const void *fdt = gd->fdt_blob;
|
||||
int node = dev_of_offset(dev);
|
||||
u32 link_ctrl_sta;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
pcie->bus = dev;
|
||||
|
||||
ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
|
||||
"ccsr", &pcie->ccsr_res);
|
||||
if (ret) {
|
||||
printf("ls-pcie-g4: resource \"ccsr\" not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcie->idx = (pcie->ccsr_res.start - PCIE_SYS_BASE_ADDR) /
|
||||
PCIE_CCSR_SIZE;
|
||||
|
||||
list_add(&pcie->list, &ls_pcie_g4_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->ccsr = map_physmem(pcie->ccsr_res.start,
|
||||
fdt_resource_size(&pcie->ccsr_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pcie->cfg = map_physmem(pcie->cfg_res.start,
|
||||
fdt_resource_size(&pcie->cfg_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
ret = fdt_get_named_resource(fdt, node, "reg", "reg-names",
|
||||
"lut", &pcie->lut_res);
|
||||
if (ret) {
|
||||
printf("ls-pcie-g4: resource \"lut\" not found\n");
|
||||
return 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",
|
||||
"pf_ctrl", &pcie->pf_ctrl_res);
|
||||
if (ret) {
|
||||
printf("ls-pcie-g4: resource \"pf_ctrl\" not found\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pcie->pf_ctrl = map_physmem(pcie->pf_ctrl_res.start,
|
||||
fdt_resource_size(&pcie->pf_ctrl_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian");
|
||||
|
||||
debug("%s ccsr:%lx, cfg:0x%lx, big-endian:%d\n",
|
||||
dev->name, (unsigned long)pcie->ccsr, (unsigned long)pcie->cfg,
|
||||
pcie->big_endian);
|
||||
|
||||
pcie->mode = readb(pcie->ccsr + PCI_HEADER_TYPE) & 0x7f;
|
||||
|
||||
if (pcie->mode == PCI_HEADER_TYPE_NORMAL) {
|
||||
printf("PCIe%u: %s %s", pcie->idx, dev->name, "Endpoint");
|
||||
ls_pcie_g4_setup_ep(pcie);
|
||||
} else {
|
||||
printf("PCIe%u: %s %s", pcie->idx, dev->name, "Root Complex");
|
||||
ls_pcie_g4_setup_ctrl(pcie);
|
||||
}
|
||||
|
||||
/* Enable Amba & PEX PIO */
|
||||
val = ccsr_readl(pcie, PAB_CTRL);
|
||||
val |= PAB_CTRL_APIO_EN | PAB_CTRL_PPIO_EN;
|
||||
ccsr_writel(pcie, PAB_CTRL, val);
|
||||
|
||||
val = ccsr_readl(pcie, PAB_PEX_PIO_CTRL(0));
|
||||
val |= PPIO_EN;
|
||||
ccsr_writel(pcie, PAB_PEX_PIO_CTRL(0), val);
|
||||
|
||||
if (!ls_pcie_g4_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_ctrl_sta = ccsr_readl(pcie, PCIE_LINK_CTRL_STA);
|
||||
printf(": x%d gen%d\n",
|
||||
(link_ctrl_sta >> PCIE_LINK_WIDTH_SHIFT & PCIE_LINK_WIDTH_MASK),
|
||||
(link_ctrl_sta >> PCIE_LINK_SPEED_SHIFT) & PCIE_LINK_SPEED_MASK);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops ls_pcie_g4_ops = {
|
||||
.read_config = ls_pcie_g4_read_config,
|
||||
.write_config = ls_pcie_g4_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id ls_pcie_g4_ids[] = {
|
||||
{ .compatible = "fsl,lx2160a-pcie" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_layerscape_gen4) = {
|
||||
.name = "pcie_layerscape_gen4",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = ls_pcie_g4_ids,
|
||||
.ops = &ls_pcie_g4_ops,
|
||||
.probe = ls_pcie_g4_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct ls_pcie_g4),
|
||||
};
|
||||
@@ -0,0 +1,264 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0+ */
|
||||
/*
|
||||
* Copyright 2018-2019 NXP
|
||||
*
|
||||
* PCIe Gen4 driver for NXP Layerscape SoCs
|
||||
* Author: Hou Zhiqiang <Minder.Hou@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef _PCIE_LAYERSCAPE_GEN4_H_
|
||||
#define _PCIE_LAYERSCAPE_GEN4_H_
|
||||
#include <pci.h>
|
||||
#include <dm.h>
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_MEMORY_SIZE
|
||||
#define CONFIG_SYS_PCI_MEMORY_SIZE (4 * 1024 * 1024 * 1024ULL)
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SYS_PCI_EP_MEMORY_BASE
|
||||
#define CONFIG_SYS_PCI_EP_MEMORY_BASE CONFIG_SYS_LOAD_ADDR
|
||||
#endif
|
||||
|
||||
#define PCIE_PF_NUM 2
|
||||
#define PCIE_VF_NUM 32
|
||||
|
||||
#define LS_G4_PF0 0
|
||||
#define LS_G4_PF1 1
|
||||
#define PF_BAR_NUM 4
|
||||
#define VF_BAR_NUM 4
|
||||
#define PCIE_BAR_SIZE (8 * 1024) /* 8K */
|
||||
#define PCIE_BAR0_SIZE PCIE_BAR_SIZE
|
||||
#define PCIE_BAR1_SIZE PCIE_BAR_SIZE
|
||||
#define PCIE_BAR2_SIZE PCIE_BAR_SIZE
|
||||
#define PCIE_BAR4_SIZE PCIE_BAR_SIZE
|
||||
#define SIZE_1T (1024 * 1024 * 1024 * 1024ULL)
|
||||
|
||||
/* GPEX CSR */
|
||||
#define GPEX_CLASSCODE 0x474
|
||||
#define GPEX_CLASSCODE_SHIFT 16
|
||||
#define GPEX_CLASSCODE_MASK 0xffff
|
||||
|
||||
#define GPEX_CFG_READY 0x4b0
|
||||
#define PCIE_CONFIG_READY BIT(0)
|
||||
|
||||
#define GPEX_BAR_ENABLE 0x4d4
|
||||
#define GPEX_BAR_SIZE_LDW 0x4d8
|
||||
#define GPEX_BAR_SIZE_UDW 0x4dC
|
||||
#define GPEX_BAR_SELECT 0x4e0
|
||||
|
||||
#define BAR_POS(bar, pf, vf_bar) \
|
||||
((bar) + (pf) * PF_BAR_NUM + (vf_bar) * PCIE_PF_NUM * PF_BAR_NUM)
|
||||
|
||||
#define GPEX_SRIOV_INIT_VFS_TOTAL_VF(pf) (0x644 + (pf) * 4)
|
||||
#define TTL_VF_MASK 0xffff
|
||||
#define TTL_VF_SHIFT 16
|
||||
#define INI_VF_MASK 0xffff
|
||||
#define INI_VF_SHIFT 0
|
||||
#define GPEX_SRIOV_VF_OFFSET_STRIDE(pf) (0x704 + (pf) * 4)
|
||||
|
||||
/* PAB CSR */
|
||||
#define PAB_CTRL 0x808
|
||||
#define PAB_CTRL_APIO_EN BIT(0)
|
||||
#define PAB_CTRL_PPIO_EN BIT(1)
|
||||
#define PAB_CTRL_MAX_BRST_LEN_SHIFT 4
|
||||
#define PAB_CTRL_MAX_BRST_LEN_MASK 0x3
|
||||
#define PAB_CTRL_PAGE_SEL_SHIFT 13
|
||||
#define PAB_CTRL_PAGE_SEL_MASK 0x3f
|
||||
#define PAB_CTRL_FUNC_SEL_SHIFT 19
|
||||
#define PAB_CTRL_FUNC_SEL_MASK 0x1ff
|
||||
|
||||
#define PAB_RST_CTRL 0x820
|
||||
#define PAB_BR_STAT 0x80c
|
||||
|
||||
/* AXI PIO Engines */
|
||||
#define PAB_AXI_PIO_CTRL(idx) (0x840 + 0x10 * (idx))
|
||||
#define APIO_EN BIT(0)
|
||||
#define MEM_WIN_EN BIT(1)
|
||||
#define IO_WIN_EN BIT(2)
|
||||
#define CFG_WIN_EN BIT(3)
|
||||
#define PAB_AXI_PIO_STAT(idx) (0x844 + 0x10 * (idx))
|
||||
#define PAB_AXI_PIO_SL_CMD_STAT(idx) (0x848 + 0x10 * (idx))
|
||||
#define PAB_AXI_PIO_SL_ADDR_STAT(idx) (0x84c + 0x10 * (idx))
|
||||
#define PAB_AXI_PIO_SL_EXT_ADDR_STAT(idx) (0xb8a0 + 0x4 * (idx))
|
||||
|
||||
/* PEX PIO Engines */
|
||||
#define PAB_PEX_PIO_CTRL(idx) (0x8c0 + 0x10 * (idx))
|
||||
#define PPIO_EN BIT(0)
|
||||
#define PAB_PEX_PIO_STAT(idx) (0x8c4 + 0x10 * (idx))
|
||||
#define PAB_PEX_PIO_MT_STAT(idx) (0x8c8 + 0x10 * (idx))
|
||||
|
||||
#define INDIRECT_ADDR_BNDRY 0xc00
|
||||
#define PAGE_IDX_SHIFT 10
|
||||
#define PAGE_ADDR_MASK 0x3ff
|
||||
|
||||
#define OFFSET_TO_PAGE_IDX(off) \
|
||||
(((off) >> PAGE_IDX_SHIFT) & PAB_CTRL_PAGE_SEL_MASK)
|
||||
|
||||
#define OFFSET_TO_PAGE_ADDR(off) \
|
||||
(((off) & PAGE_ADDR_MASK) | INDIRECT_ADDR_BNDRY)
|
||||
|
||||
/* APIO WINs */
|
||||
#define PAB_AXI_AMAP_CTRL(idx) (0xba0 + 0x10 * (idx))
|
||||
#define PAB_EXT_AXI_AMAP_SIZE(idx) (0xbaf0 + 0x4 * (idx))
|
||||
#define PAB_AXI_AMAP_AXI_WIN(idx) (0xba4 + 0x10 * (idx))
|
||||
#define PAB_EXT_AXI_AMAP_AXI_WIN(idx) (0x80a0 + 0x4 * (idx))
|
||||
#define PAB_AXI_AMAP_PEX_WIN_L(idx) (0xba8 + 0x10 * (idx))
|
||||
#define PAB_AXI_AMAP_PEX_WIN_H(idx) (0xbac + 0x10 * (idx))
|
||||
#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x4 * (idx))
|
||||
#define FUNC_NUM_PCIE_MASK GENMASK(7, 0)
|
||||
|
||||
#define AXI_AMAP_CTRL_EN BIT(0)
|
||||
#define AXI_AMAP_CTRL_TYPE_SHIFT 1
|
||||
#define AXI_AMAP_CTRL_TYPE_MASK 0x3
|
||||
#define AXI_AMAP_CTRL_SIZE_SHIFT 10
|
||||
#define AXI_AMAP_CTRL_SIZE_MASK 0x3fffff
|
||||
|
||||
#define PAB_TARGET_BUS(x) (((x) & 0xff) << 24)
|
||||
#define PAB_TARGET_DEV(x) (((x) & 0x1f) << 19)
|
||||
#define PAB_TARGET_FUNC(x) (((x) & 0x7) << 16)
|
||||
|
||||
#define PAB_AXI_TYPE_CFG 0x00
|
||||
#define PAB_AXI_TYPE_IO 0x01
|
||||
#define PAB_AXI_TYPE_MEM 0x02
|
||||
#define PAB_AXI_TYPE_ATOM 0x03
|
||||
|
||||
#define PAB_WINS_NUM 256
|
||||
|
||||
/* PPIO WINs RC mode */
|
||||
#define PAB_PEX_AMAP_CTRL(idx) (0x4ba0 + 0x10 * (idx))
|
||||
#define PAB_EXT_PEX_AMAP_SIZE(idx) (0xbef0 + 0x04 * (idx))
|
||||
#define PAB_PEX_AMAP_AXI_WIN(idx) (0x4ba4 + 0x10 * (idx))
|
||||
#define PAB_EXT_PEX_AMAP_AXI_WIN(idx) (0xb4a0 + 0x04 * (idx))
|
||||
#define PAB_PEX_AMAP_PEX_WIN_L(idx) (0x4ba8 + 0x10 * (idx))
|
||||
#define PAB_PEX_AMAP_PEX_WIN_H(idx) (0x4bac + 0x10 * (idx))
|
||||
|
||||
#define IB_TYPE_MEM_F 0x2
|
||||
#define IB_TYPE_MEM_NF 0x3
|
||||
|
||||
#define PEX_AMAP_CTRL_TYPE_SHIFT 0x1
|
||||
#define PEX_AMAP_CTRL_EN_SHIFT 0x0
|
||||
#define PEX_AMAP_CTRL_TYPE_MASK 0x3
|
||||
#define PEX_AMAP_CTRL_EN_MASK 0x1
|
||||
|
||||
/* PPIO WINs EP mode */
|
||||
#define PAB_PEX_BAR_AMAP(pf, bar) \
|
||||
(0x1ba0 + 0x20 * (pf) + 4 * (bar))
|
||||
#define BAR_AMAP_EN BIT(0)
|
||||
#define PAB_EXT_PEX_BAR_AMAP(pf, bar) \
|
||||
(0x84a0 + 0x20 * (pf) + 4 * (bar))
|
||||
|
||||
/* CCSR registers */
|
||||
#define PCIE_LINK_CTRL_STA 0x5c
|
||||
#define PCIE_LINK_SPEED_SHIFT 16
|
||||
#define PCIE_LINK_SPEED_MASK 0x0f
|
||||
#define PCIE_LINK_WIDTH_SHIFT 20
|
||||
#define PCIE_LINK_WIDTH_MASK 0x3f
|
||||
#define PCIE_SRIOV_CAPABILITY 0x2a0
|
||||
#define PCIE_SRIOV_VF_OFFSET_STRIDE 0x2b4
|
||||
|
||||
/* LUT registers */
|
||||
#define PCIE_LUT_UDR(n) (0x800 + (n) * 8)
|
||||
#define PCIE_LUT_LDR(n) (0x804 + (n) * 8)
|
||||
#define PCIE_LUT_ENABLE BIT(31)
|
||||
#define PCIE_LUT_ENTRY_COUNT 32
|
||||
|
||||
/* PF control registers */
|
||||
#define PCIE_LTSSM_STA 0x7fc
|
||||
#define LTSSM_STATE_MASK 0x7f
|
||||
#define LTSSM_PCIE_L0 0x2d /* L0 state */
|
||||
|
||||
#define PCIE_SRDS_PRTCL(idx) (PCIE1 + (idx))
|
||||
#define PCIE_SYS_BASE_ADDR 0x3400000
|
||||
#define PCIE_CCSR_SIZE 0x0100000
|
||||
|
||||
struct ls_pcie_g4 {
|
||||
int idx;
|
||||
struct list_head list;
|
||||
struct udevice *bus;
|
||||
struct fdt_resource ccsr_res;
|
||||
struct fdt_resource cfg_res;
|
||||
struct fdt_resource lut_res;
|
||||
struct fdt_resource pf_ctrl_res;
|
||||
void __iomem *ccsr;
|
||||
void __iomem *cfg;
|
||||
void __iomem *lut;
|
||||
void __iomem *pf_ctrl;
|
||||
bool big_endian;
|
||||
bool enabled;
|
||||
int next_lut_index;
|
||||
struct pci_controller hose;
|
||||
int stream_id_cur;
|
||||
int mode;
|
||||
int sriov_support;
|
||||
};
|
||||
|
||||
extern struct list_head ls_pcie_g4_list;
|
||||
|
||||
static inline void lut_writel(struct ls_pcie_g4 *pcie, unsigned int value,
|
||||
unsigned int offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
out_be32(pcie->lut + offset, value);
|
||||
else
|
||||
out_le32(pcie->lut + offset, value);
|
||||
}
|
||||
|
||||
static inline u32 lut_readl(struct ls_pcie_g4 *pcie, unsigned int offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
return in_be32(pcie->lut + offset);
|
||||
else
|
||||
return in_le32(pcie->lut + offset);
|
||||
}
|
||||
|
||||
static inline void ccsr_set_page(struct ls_pcie_g4 *pcie, u8 pg_idx)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = in_le32(pcie->ccsr + PAB_CTRL);
|
||||
val &= ~(PAB_CTRL_PAGE_SEL_MASK << PAB_CTRL_PAGE_SEL_SHIFT);
|
||||
val |= (pg_idx & PAB_CTRL_PAGE_SEL_MASK) << PAB_CTRL_PAGE_SEL_SHIFT;
|
||||
|
||||
out_le32(pcie->ccsr + PAB_CTRL, val);
|
||||
}
|
||||
|
||||
static inline unsigned int ccsr_readl(struct ls_pcie_g4 *pcie, u32 offset)
|
||||
{
|
||||
if (offset < INDIRECT_ADDR_BNDRY) {
|
||||
ccsr_set_page(pcie, 0);
|
||||
return in_le32(pcie->ccsr + offset);
|
||||
}
|
||||
|
||||
ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset));
|
||||
return in_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset));
|
||||
}
|
||||
|
||||
static inline void ccsr_writel(struct ls_pcie_g4 *pcie, u32 offset, u32 value)
|
||||
{
|
||||
if (offset < INDIRECT_ADDR_BNDRY) {
|
||||
ccsr_set_page(pcie, 0);
|
||||
out_le32(pcie->ccsr + offset, value);
|
||||
} else {
|
||||
ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset));
|
||||
out_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset), value);
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned int pf_ctrl_readl(struct ls_pcie_g4 *pcie, u32 offset)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
return in_be32(pcie->pf_ctrl + offset);
|
||||
else
|
||||
return in_le32(pcie->pf_ctrl + offset);
|
||||
}
|
||||
|
||||
static inline void pf_ctrl_writel(struct ls_pcie_g4 *pcie, u32 offset,
|
||||
u32 value)
|
||||
{
|
||||
if (pcie->big_endian)
|
||||
out_be32(pcie->pf_ctrl + offset, value);
|
||||
else
|
||||
out_le32(pcie->pf_ctrl + offset, value);
|
||||
}
|
||||
|
||||
#endif /* _PCIE_LAYERSCAPE_GEN4_H_ */
|
||||
@@ -0,0 +1,249 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+ OR X11
|
||||
/*
|
||||
* Copyright 2018-2019 NXP
|
||||
*
|
||||
* PCIe Gen4 driver for NXP Layerscape SoCs
|
||||
* Author: Hou Zhiqiang <Minder.Hou@gmail.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <pci.h>
|
||||
#include <asm/arch/fsl_serdes.h>
|
||||
#include <asm/io.h>
|
||||
#include <errno.h>
|
||||
#ifdef CONFIG_OF_BOARD_SETUP
|
||||
#include <linux/libfdt.h>
|
||||
#include <fdt_support.h>
|
||||
#ifdef CONFIG_ARM
|
||||
#include <asm/arch/clock.h>
|
||||
#endif
|
||||
#include "pcie_layerscape_gen4.h"
|
||||
|
||||
#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
|
||||
/*
|
||||
* Return next available LUT index.
|
||||
*/
|
||||
static int ls_pcie_g4_next_lut_index(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
if (pcie->next_lut_index < PCIE_LUT_ENTRY_COUNT)
|
||||
return pcie->next_lut_index++;
|
||||
|
||||
return -ENOSPC; /* LUT is full */
|
||||
}
|
||||
|
||||
/* returns the next available streamid for pcie, -errno if failed */
|
||||
static int ls_pcie_g4_next_streamid(struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
int stream_id = pcie->stream_id_cur;
|
||||
|
||||
if (stream_id > FSL_PEX_STREAM_ID_NUM)
|
||||
return -EINVAL;
|
||||
|
||||
pcie->stream_id_cur++;
|
||||
|
||||
return stream_id | ((pcie->idx + 1) << 11);
|
||||
}
|
||||
|
||||
/*
|
||||
* Program a single LUT entry
|
||||
*/
|
||||
static void ls_pcie_g4_lut_set_mapping(struct ls_pcie_g4 *pcie, 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));
|
||||
}
|
||||
|
||||
/*
|
||||
* An msi-map is a property to be added to the pci controller
|
||||
* node. It is a table, where each entry consists of 4 fields
|
||||
* e.g.:
|
||||
*
|
||||
* 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(void *blob, struct ls_pcie_g4 *pcie,
|
||||
u32 devid, u32 streamid)
|
||||
{
|
||||
u32 *prop;
|
||||
u32 phandle;
|
||||
int nodeoff;
|
||||
|
||||
#ifdef CONFIG_FSL_PCIE_COMPAT
|
||||
nodeoff = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT,
|
||||
pcie->ccsr_res.start);
|
||||
#else
|
||||
#error "No CONFIG_FSL_PCIE_COMPAT defined"
|
||||
#endif
|
||||
if (nodeoff < 0) {
|
||||
debug("%s: ERROR: failed to find pcie compatiable\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get phandle to MSI controller */
|
||||
prop = (u32 *)fdt_getprop(blob, nodeoff, "msi-parent", 0);
|
||||
if (!prop) {
|
||||
debug("\n%s: ERROR: missing msi-parent: PCIe%d\n",
|
||||
__func__, pcie->idx);
|
||||
return;
|
||||
}
|
||||
phandle = fdt32_to_cpu(*prop);
|
||||
|
||||
/* set one msi-map row */
|
||||
fdt_appendprop_u32(blob, nodeoff, "msi-map", devid);
|
||||
fdt_appendprop_u32(blob, nodeoff, "msi-map", phandle);
|
||||
fdt_appendprop_u32(blob, nodeoff, "msi-map", streamid);
|
||||
fdt_appendprop_u32(blob, nodeoff, "msi-map", 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* An iommu-map is a property to be added to the pci controller
|
||||
* node. It is a table, where each entry consists of 4 fields
|
||||
* e.g.:
|
||||
*
|
||||
* 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(void *blob, struct ls_pcie_g4 *pcie,
|
||||
u32 devid, u32 streamid)
|
||||
{
|
||||
u32 *prop;
|
||||
u32 iommu_map[4];
|
||||
int nodeoff;
|
||||
int lenp;
|
||||
|
||||
#ifdef CONFIG_FSL_PCIE_COMPAT
|
||||
nodeoff = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT,
|
||||
pcie->ccsr_res.start);
|
||||
#else
|
||||
#error "No CONFIG_FSL_PCIE_COMPAT defined"
|
||||
#endif
|
||||
if (nodeoff < 0) {
|
||||
debug("%s: ERROR: failed to find pcie compatiable\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* get phandle to iommu controller */
|
||||
prop = fdt_getprop_w(blob, nodeoff, "iommu-map", &lenp);
|
||||
if (!prop) {
|
||||
debug("\n%s: ERROR: missing iommu-map: PCIe%d\n",
|
||||
__func__, pcie->idx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* set iommu-map row */
|
||||
iommu_map[0] = cpu_to_fdt32(devid);
|
||||
iommu_map[1] = *++prop;
|
||||
iommu_map[2] = cpu_to_fdt32(streamid);
|
||||
iommu_map[3] = cpu_to_fdt32(1);
|
||||
|
||||
if (devid == 0)
|
||||
fdt_setprop_inplace(blob, nodeoff, "iommu-map", iommu_map, 16);
|
||||
else
|
||||
fdt_appendprop(blob, nodeoff, "iommu-map", iommu_map, 16);
|
||||
}
|
||||
|
||||
static void fdt_fixup_pcie(void *blob)
|
||||
{
|
||||
struct udevice *dev, *bus;
|
||||
struct ls_pcie_g4 *pcie;
|
||||
int streamid;
|
||||
int index;
|
||||
pci_dev_t bdf;
|
||||
|
||||
/* Scan all known buses */
|
||||
for (pci_find_first_device(&dev); dev; pci_find_next_device(&dev)) {
|
||||
for (bus = dev; device_is_on_pci_bus(bus);)
|
||||
bus = bus->parent;
|
||||
pcie = dev_get_priv(bus);
|
||||
|
||||
streamid = ls_pcie_g4_next_streamid(pcie);
|
||||
if (streamid < 0) {
|
||||
debug("ERROR: no stream ids free\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
index = ls_pcie_g4_next_lut_index(pcie);
|
||||
if (index < 0) {
|
||||
debug("ERROR: no LUT indexes free\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* 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_g4_lut_set_mapping(pcie, index, bdf >> 8, streamid);
|
||||
/* update msi-map in device tree */
|
||||
fdt_pcie_set_msi_map_entry(blob, pcie, bdf >> 8, streamid);
|
||||
/* update iommu-map in device tree */
|
||||
fdt_pcie_set_iommu_map_entry(blob, pcie, bdf >> 8, streamid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ft_pcie_ep_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
int off;
|
||||
|
||||
off = fdt_node_offset_by_compat_reg(blob, "fsl,lx2160a-pcie-ep",
|
||||
pcie->ccsr_res.start);
|
||||
|
||||
if (off < 0) {
|
||||
debug("%s: ERROR: failed to find pcie compatiable\n",
|
||||
__func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcie->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_rc_layerscape_gen4_fix(void *blob, struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
int off;
|
||||
|
||||
#ifdef CONFIG_FSL_PCIE_COMPAT
|
||||
off = fdt_node_offset_by_compat_reg(blob, CONFIG_FSL_PCIE_COMPAT,
|
||||
pcie->ccsr_res.start);
|
||||
#else
|
||||
#error "No CONFIG_FSL_PCIE_COMPAT defined"
|
||||
#endif
|
||||
if (off < 0) {
|
||||
debug("%s: ERROR: failed to find pcie compatiable\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pcie->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_layerscape_gen4_setup(void *blob, struct ls_pcie_g4 *pcie)
|
||||
{
|
||||
ft_pcie_rc_layerscape_gen4_fix(blob, pcie);
|
||||
ft_pcie_ep_layerscape_gen4_fix(blob, pcie);
|
||||
}
|
||||
|
||||
/* Fixup Kernel DT for PCIe */
|
||||
void ft_pci_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
struct ls_pcie_g4 *pcie;
|
||||
|
||||
list_for_each_entry(pcie, &ls_pcie_g4_list, list)
|
||||
ft_pcie_layerscape_gen4_setup(blob, pcie);
|
||||
|
||||
#if defined(CONFIG_FSL_LSCH3) || defined(CONFIG_FSL_LSCH2)
|
||||
fdt_fixup_pcie(blob);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else /* !CONFIG_OF_BOARD_SETUP */
|
||||
void ft_pci_setup(void *blob, bd_t *bd)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,279 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* MediaTek PCIe host controller driver.
|
||||
*
|
||||
* Copyright (c) 2017-2019 MediaTek Inc.
|
||||
* Author: Ryder Lee <ryder.lee@mediatek.com>
|
||||
* Honghui Zhang <honghui.zhang@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <generic-phy.h>
|
||||
#include <pci.h>
|
||||
#include <reset.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
/* PCIe shared registers */
|
||||
#define PCIE_SYS_CFG 0x00
|
||||
#define PCIE_INT_ENABLE 0x0c
|
||||
#define PCIE_CFG_ADDR 0x20
|
||||
#define PCIE_CFG_DATA 0x24
|
||||
|
||||
/* PCIe per port registers */
|
||||
#define PCIE_BAR0_SETUP 0x10
|
||||
#define PCIE_CLASS 0x34
|
||||
#define PCIE_LINK_STATUS 0x50
|
||||
|
||||
#define PCIE_PORT_INT_EN(x) BIT(20 + (x))
|
||||
#define PCIE_PORT_PERST(x) BIT(1 + (x))
|
||||
#define PCIE_PORT_LINKUP BIT(0)
|
||||
#define PCIE_BAR_MAP_MAX GENMASK(31, 16)
|
||||
|
||||
#define PCIE_BAR_ENABLE BIT(0)
|
||||
#define PCIE_REVISION_ID BIT(0)
|
||||
#define PCIE_CLASS_CODE (0x60400 << 8)
|
||||
#define PCIE_CONF_REG(regn) (((regn) & GENMASK(7, 2)) | \
|
||||
((((regn) >> 8) & GENMASK(3, 0)) << 24))
|
||||
#define PCIE_CONF_ADDR(regn, bdf) \
|
||||
(PCIE_CONF_REG(regn) | (bdf))
|
||||
|
||||
/* MediaTek specific configuration registers */
|
||||
#define PCIE_FTS_NUM 0x70c
|
||||
#define PCIE_FTS_NUM_MASK GENMASK(15, 8)
|
||||
#define PCIE_FTS_NUM_L0(x) ((x) & 0xff << 8)
|
||||
|
||||
#define PCIE_FC_CREDIT 0x73c
|
||||
#define PCIE_FC_CREDIT_MASK (GENMASK(31, 31) | GENMASK(28, 16))
|
||||
#define PCIE_FC_CREDIT_VAL(x) ((x) << 16)
|
||||
|
||||
struct mtk_pcie_port {
|
||||
void __iomem *base;
|
||||
struct list_head list;
|
||||
struct mtk_pcie *pcie;
|
||||
struct reset_ctl reset;
|
||||
struct clk sys_ck;
|
||||
struct phy phy;
|
||||
u32 slot;
|
||||
};
|
||||
|
||||
struct mtk_pcie {
|
||||
void __iomem *base;
|
||||
struct clk free_ck;
|
||||
struct list_head ports;
|
||||
};
|
||||
|
||||
static int mtk_pcie_config_address(struct udevice *udev, pci_dev_t bdf,
|
||||
uint offset, void **paddress)
|
||||
{
|
||||
struct mtk_pcie *pcie = dev_get_priv(udev);
|
||||
|
||||
writel(PCIE_CONF_ADDR(offset, bdf), pcie->base + PCIE_CFG_ADDR);
|
||||
*paddress = pcie->base + PCIE_CFG_DATA + (offset & 3);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pcie_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
return pci_generic_mmap_read_config(bus, mtk_pcie_config_address,
|
||||
bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
static int mtk_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, mtk_pcie_config_address,
|
||||
bdf, offset, value, size);
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops mtk_pcie_ops = {
|
||||
.read_config = mtk_pcie_read_config,
|
||||
.write_config = mtk_pcie_write_config,
|
||||
};
|
||||
|
||||
static void mtk_pcie_port_free(struct mtk_pcie_port *port)
|
||||
{
|
||||
list_del(&port->list);
|
||||
free(port);
|
||||
}
|
||||
|
||||
static int mtk_pcie_startup_port(struct mtk_pcie_port *port)
|
||||
{
|
||||
struct mtk_pcie *pcie = port->pcie;
|
||||
u32 slot = PCI_DEV(port->slot << 11);
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
/* assert port PERST_N */
|
||||
setbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot));
|
||||
/* de-assert port PERST_N */
|
||||
clrbits_le32(pcie->base + PCIE_SYS_CFG, PCIE_PORT_PERST(port->slot));
|
||||
|
||||
/* 100ms timeout value should be enough for Gen1/2 training */
|
||||
err = readl_poll_timeout(port->base + PCIE_LINK_STATUS, val,
|
||||
!!(val & PCIE_PORT_LINKUP), 100000);
|
||||
if (err)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
/* disable interrupt */
|
||||
clrbits_le32(pcie->base + PCIE_INT_ENABLE,
|
||||
PCIE_PORT_INT_EN(port->slot));
|
||||
|
||||
/* map to all DDR region. We need to set it before cfg operation. */
|
||||
writel(PCIE_BAR_MAP_MAX | PCIE_BAR_ENABLE,
|
||||
port->base + PCIE_BAR0_SETUP);
|
||||
|
||||
/* configure class code and revision ID */
|
||||
writel(PCIE_CLASS_CODE | PCIE_REVISION_ID, port->base + PCIE_CLASS);
|
||||
|
||||
/* configure FC credit */
|
||||
writel(PCIE_CONF_ADDR(PCIE_FC_CREDIT, slot),
|
||||
pcie->base + PCIE_CFG_ADDR);
|
||||
clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FC_CREDIT_MASK,
|
||||
PCIE_FC_CREDIT_VAL(0x806c));
|
||||
|
||||
/* configure RC FTS number to 250 when it leaves L0s */
|
||||
writel(PCIE_CONF_ADDR(PCIE_FTS_NUM, slot), pcie->base + PCIE_CFG_ADDR);
|
||||
clrsetbits_le32(pcie->base + PCIE_CFG_DATA, PCIE_FTS_NUM_MASK,
|
||||
PCIE_FTS_NUM_L0(0x50));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_pcie_enable_port(struct mtk_pcie_port *port)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = clk_enable(&port->sys_ck);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = reset_assert(&port->reset);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = reset_deassert(&port->reset);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = generic_phy_init(&port->phy);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
err = generic_phy_power_on(&port->phy);
|
||||
if (err)
|
||||
goto exit;
|
||||
|
||||
if (!mtk_pcie_startup_port(port))
|
||||
return;
|
||||
|
||||
pr_err("Port%d link down\n", port->slot);
|
||||
exit:
|
||||
mtk_pcie_port_free(port);
|
||||
}
|
||||
|
||||
static int mtk_pcie_parse_port(struct udevice *dev, u32 slot)
|
||||
{
|
||||
struct mtk_pcie *pcie = dev_get_priv(dev);
|
||||
struct mtk_pcie_port *port;
|
||||
char name[10];
|
||||
int err;
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(name, sizeof(name), "port%d", slot);
|
||||
port->base = dev_remap_addr_name(dev, name);
|
||||
if (!port->base)
|
||||
return -ENOENT;
|
||||
|
||||
snprintf(name, sizeof(name), "sys_ck%d", slot);
|
||||
err = clk_get_by_name(dev, name, &port->sys_ck);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = reset_get_by_index(dev, slot, &port->reset);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = generic_phy_get_by_index(dev, slot, &port->phy);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
port->slot = slot;
|
||||
port->pcie = pcie;
|
||||
|
||||
INIT_LIST_HEAD(&port->list);
|
||||
list_add_tail(&port->list, &pcie->ports);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_pcie_probe(struct udevice *dev)
|
||||
{
|
||||
struct mtk_pcie *pcie = dev_get_priv(dev);
|
||||
struct mtk_pcie_port *port, *tmp;
|
||||
ofnode subnode;
|
||||
int err;
|
||||
|
||||
INIT_LIST_HEAD(&pcie->ports);
|
||||
|
||||
pcie->base = dev_remap_addr_name(dev, "subsys");
|
||||
if (!pcie->base)
|
||||
return -ENOENT;
|
||||
|
||||
err = clk_get_by_name(dev, "free_ck", &pcie->free_ck);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* enable top level clock */
|
||||
err = clk_enable(&pcie->free_ck);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
dev_for_each_subnode(subnode, dev) {
|
||||
struct fdt_pci_addr addr;
|
||||
u32 slot = 0;
|
||||
|
||||
if (!ofnode_is_available(subnode))
|
||||
continue;
|
||||
|
||||
err = ofnode_read_pci_addr(subnode, 0, "reg", &addr);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
slot = PCI_DEV(addr.phys_hi);
|
||||
|
||||
err = mtk_pcie_parse_port(dev, slot);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
/* enable each port, and then check link status */
|
||||
list_for_each_entry_safe(port, tmp, &pcie->ports, list)
|
||||
mtk_pcie_enable_port(port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mtk_pcie_ids[] = {
|
||||
{ .compatible = "mediatek,mt7623-pcie", },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_mediatek) = {
|
||||
.name = "pcie_mediatek",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = mtk_pcie_ids,
|
||||
.ops = &mtk_pcie_ops,
|
||||
.probe = mtk_pcie_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct mtk_pcie),
|
||||
};
|
||||
@@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Phytium PCIE host driver
|
||||
*
|
||||
* Heavily based on drivers/pci/pcie_xilinx.c
|
||||
*
|
||||
* Copyright (C) 2019
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* struct phytium_pcie - phytium PCIe controller state
|
||||
* @cfg_base: The base address of memory mapped configuration space
|
||||
*/
|
||||
struct phytium_pcie {
|
||||
void *cfg_base;
|
||||
};
|
||||
|
||||
/*
|
||||
* phytium_pci_skip_dev()
|
||||
* @parent: Identifies the PCIe device to access
|
||||
*
|
||||
* Checks whether the parent of the PCIe device is bridge
|
||||
*
|
||||
* Return: true if it is bridge, else false.
|
||||
*/
|
||||
static int phytium_pci_skip_dev(pci_dev_t parent)
|
||||
{
|
||||
unsigned char pos, id;
|
||||
unsigned long addr = 0x40000000;
|
||||
unsigned short capreg;
|
||||
unsigned char port_type;
|
||||
|
||||
addr += PCI_BUS(parent) << 20;
|
||||
addr += PCI_DEV(parent) << 15;
|
||||
addr += PCI_FUNC(parent) << 12;
|
||||
|
||||
pos = 0x34;
|
||||
while (1) {
|
||||
pos = readb(addr + pos);
|
||||
if (pos < 0x40)
|
||||
break;
|
||||
pos &= ~3;
|
||||
id = readb(addr + pos);
|
||||
if (id == 0xff)
|
||||
break;
|
||||
if (id == 0x10) {
|
||||
capreg = readw(addr + pos + 2);
|
||||
port_type = (capreg >> 4) & 0xf;
|
||||
if (port_type == 0x6 || port_type == 0x4)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_phytium_conf_address() - Calculate the address of a config access
|
||||
* @bus: Pointer to the PCI bus
|
||||
* @bdf: Identifies the PCIe device to access
|
||||
* @offset: The offset into the device's configuration space
|
||||
* @paddress: Pointer to the pointer to write the calculates address to
|
||||
*
|
||||
* Calculates the address that should be accessed to perform a PCIe
|
||||
* configuration space access for a given device identified by the PCIe
|
||||
* controller device @pcie and the bus, device & function numbers in @bdf. If
|
||||
* access to the device is not valid then the function will return an error
|
||||
* code. Otherwise the address to access will be written to the pointer pointed
|
||||
* to by @paddress.
|
||||
*/
|
||||
static int pci_phytium_conf_address(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset,
|
||||
void **paddress)
|
||||
{
|
||||
struct phytium_pcie *pcie = dev_get_priv(bus);
|
||||
void *addr;
|
||||
pci_dev_t bdf_parent;
|
||||
|
||||
unsigned int bus_no = PCI_BUS(bdf);
|
||||
unsigned int dev_no = PCI_DEV(bdf);
|
||||
|
||||
bdf_parent = PCI_BDF((bus_no - 1), 0, 0);
|
||||
|
||||
addr = pcie->cfg_base;
|
||||
addr += PCI_BUS(bdf) << 20;
|
||||
addr += PCI_DEV(bdf) << 15;
|
||||
addr += PCI_FUNC(bdf) << 12;
|
||||
|
||||
if (bus_no > 0 && dev_no > 0) {
|
||||
if ((readb(addr + PCI_HEADER_TYPE) & 0x7f) !=
|
||||
PCI_HEADER_TYPE_BRIDGE)
|
||||
return -ENODEV;
|
||||
if (phytium_pci_skip_dev(bdf_parent))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
addr += offset;
|
||||
*paddress = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_phytium_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.
|
||||
*/
|
||||
static int pci_phytium_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
return pci_generic_mmap_read_config(bus, pci_phytium_conf_address,
|
||||
bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_phytium_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.
|
||||
*/
|
||||
static int pci_phytium_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, pci_phytium_conf_address,
|
||||
bdf, offset, value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_phytium_ofdata_to_platdata() - Translate from DT to device state
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pci_phytium_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct phytium_pcie *pcie = dev_get_priv(dev);
|
||||
struct fdt_resource reg_res;
|
||||
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
|
||||
int err;
|
||||
|
||||
err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
|
||||
0, ®_res);
|
||||
if (err < 0) {
|
||||
pr_err("\"reg\" resource not found\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pcie->cfg_base = map_physmem(reg_res.start,
|
||||
fdt_resource_size(®_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pci_phytium_ops = {
|
||||
.read_config = pci_phytium_read_config,
|
||||
.write_config = pci_phytium_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pci_phytium_ids[] = {
|
||||
{ .compatible = "phytium,pcie-host-1.0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pci_phytium) = {
|
||||
.name = "pci_phytium",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pci_phytium_ids,
|
||||
.ops = &pci_phytium_ops,
|
||||
.ofdata_to_platdata = pci_phytium_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct phytium_pcie),
|
||||
};
|
||||
@@ -0,0 +1,178 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Xilinx AXI Bridge for PCI Express Driver
|
||||
*
|
||||
* Copyright (C) 2016 Imagination Technologies
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <pci.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
/**
|
||||
* struct xilinx_pcie - Xilinx PCIe controller state
|
||||
* @cfg_base: The base address of memory mapped configuration space
|
||||
*/
|
||||
struct xilinx_pcie {
|
||||
void *cfg_base;
|
||||
};
|
||||
|
||||
/* Register definitions */
|
||||
#define XILINX_PCIE_REG_PSCR 0x144
|
||||
#define XILINX_PCIE_REG_PSCR_LNKUP BIT(11)
|
||||
|
||||
/**
|
||||
* pcie_xilinx_link_up() - Check whether the PCIe link is up
|
||||
* @pcie: Pointer to the PCI controller state
|
||||
*
|
||||
* Checks whether the PCIe link for the given device is up or down.
|
||||
*
|
||||
* Return: true if the link is up, else false
|
||||
*/
|
||||
static bool pcie_xilinx_link_up(struct xilinx_pcie *pcie)
|
||||
{
|
||||
uint32_t pscr = __raw_readl(pcie->cfg_base + XILINX_PCIE_REG_PSCR);
|
||||
|
||||
return pscr & XILINX_PCIE_REG_PSCR_LNKUP;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_xilinx_config_address() - Calculate the address of a config access
|
||||
* @udev: Pointer to the PCI bus
|
||||
* @bdf: Identifies the PCIe device to access
|
||||
* @offset: The offset into the device's configuration space
|
||||
* @paddress: Pointer to the pointer to write the calculates address to
|
||||
*
|
||||
* Calculates the address that should be accessed to perform a PCIe
|
||||
* configuration space access for a given device identified by the PCIe
|
||||
* controller device @pcie and the bus, device & function numbers in @bdf. If
|
||||
* access to the device is not valid then the function will return an error
|
||||
* code. Otherwise the address to access will be written to the pointer pointed
|
||||
* to by @paddress.
|
||||
*
|
||||
* Return: 0 on success, else -ENODEV
|
||||
*/
|
||||
static int pcie_xilinx_config_address(struct udevice *udev, pci_dev_t bdf,
|
||||
uint offset, void **paddress)
|
||||
{
|
||||
struct xilinx_pcie *pcie = dev_get_priv(udev);
|
||||
unsigned int bus = PCI_BUS(bdf);
|
||||
unsigned int dev = PCI_DEV(bdf);
|
||||
unsigned int func = PCI_FUNC(bdf);
|
||||
void *addr;
|
||||
|
||||
if ((bus > 0) && !pcie_xilinx_link_up(pcie))
|
||||
return -ENODEV;
|
||||
|
||||
/*
|
||||
* Busses 0 (host-PCIe bridge) & 1 (its immediate child) are
|
||||
* limited to a single device each.
|
||||
*/
|
||||
if ((bus < 2) && (dev > 0))
|
||||
return -ENODEV;
|
||||
|
||||
addr = pcie->cfg_base;
|
||||
addr += bus << 20;
|
||||
addr += dev << 15;
|
||||
addr += func << 12;
|
||||
addr += offset;
|
||||
*paddress = addr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_xilinx_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, else -ENODEV or -EINVAL
|
||||
*/
|
||||
static int pcie_xilinx_read_config(struct udevice *bus, pci_dev_t bdf,
|
||||
uint offset, ulong *valuep,
|
||||
enum pci_size_t size)
|
||||
{
|
||||
return pci_generic_mmap_read_config(bus, pcie_xilinx_config_address,
|
||||
bdf, offset, valuep, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_xilinx_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, else -ENODEV or -EINVAL
|
||||
*/
|
||||
static int pcie_xilinx_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, pcie_xilinx_config_address,
|
||||
bdf, offset, value, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* pcie_xilinx_ofdata_to_platdata() - Translate from DT to device state
|
||||
* @dev: A pointer to the device being operated on
|
||||
*
|
||||
* Translate relevant data from the device tree pertaining to device @dev into
|
||||
* state that the driver will later make use of. This state is stored in the
|
||||
* device's private data structure.
|
||||
*
|
||||
* Return: 0 on success, else -EINVAL
|
||||
*/
|
||||
static int pcie_xilinx_ofdata_to_platdata(struct udevice *dev)
|
||||
{
|
||||
struct xilinx_pcie *pcie = dev_get_priv(dev);
|
||||
struct fdt_resource reg_res;
|
||||
DECLARE_GLOBAL_DATA_PTR;
|
||||
int err;
|
||||
|
||||
err = fdt_get_resource(gd->fdt_blob, dev_of_offset(dev), "reg",
|
||||
0, ®_res);
|
||||
if (err < 0) {
|
||||
pr_err("\"reg\" resource not found\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pcie->cfg_base = map_physmem(reg_res.start,
|
||||
fdt_resource_size(®_res),
|
||||
MAP_NOCACHE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_pci_ops pcie_xilinx_ops = {
|
||||
.read_config = pcie_xilinx_read_config,
|
||||
.write_config = pcie_xilinx_write_config,
|
||||
};
|
||||
|
||||
static const struct udevice_id pcie_xilinx_ids[] = {
|
||||
{ .compatible = "xlnx,axi-pcie-host-1.00.a" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(pcie_xilinx) = {
|
||||
.name = "pcie_xilinx",
|
||||
.id = UCLASS_PCI,
|
||||
.of_match = pcie_xilinx_ids,
|
||||
.ops = &pcie_xilinx_ops,
|
||||
.ofdata_to_platdata = pcie_xilinx_ofdata_to_platdata,
|
||||
.priv_auto_alloc_size = sizeof(struct xilinx_pcie),
|
||||
};
|
||||
Reference in New Issue
Block a user