u-boot/drivers/pci/pci_mvebu.c

670 lines
20 KiB
C
Raw Normal View History

// 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 <log.h>
#include <malloc.h>
#include <asm/global_data.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/bitops.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))
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
#define PCIE_EXP_ROM_BAR_OFF 0x0030
#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)
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
#define PCIE_CONF_ADDR(b, d, f, reg) \
(PCIE_CONF_BUS(b) | PCIE_CONF_DEV(d) | \
PCIE_CONF_FUNC(f) | 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_CTRL_RC_MODE BIT(1)
#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;
struct resource io;
u32 port;
u32 lane;
int devfn;
u32 lane_mask;
int first_busno;
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
int sec_busno;
char name[16];
unsigned int mem_target;
unsigned int mem_attr;
unsigned int io_target;
unsigned int io_attr;
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
u32 cfgcache[0x34 - 0x10];
};
/*
* 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;
static void __iomem *mvebu_pcie_iobase = (void __iomem *)MBUS_PCI_IO_BASE;
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 inline struct mvebu_pcie *hose_to_pcie(struct pci_controller *hose)
{
return container_of(hose, struct mvebu_pcie, hose);
}
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
static bool mvebu_pcie_valid_addr(struct mvebu_pcie *pcie,
int busno, int dev, int func)
{
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/* On primary bus is only one PCI Bridge */
if (busno == pcie->first_busno && (dev != 0 || func != 0))
return false;
/* Access to other buses is possible when link is up */
if (busno != pcie->first_busno && !mvebu_pcie_link_up(pcie))
return false;
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/* On secondary bus can be only one PCIe device */
if (busno == pcie->sec_busno && dev != 0)
return false;
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
return true;
}
static int mvebu_pcie_read_config(const struct udevice *bus, pci_dev_t bdf,
uint offset, ulong *valuep,
enum pci_size_t size)
{
struct mvebu_pcie *pcie = dev_get_plat(bus);
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
int busno = PCI_BUS(bdf) - dev_seq(bus);
u32 addr, data;
debug("PCIE CFG read: (b,d,f)=(%2d,%2d,%2d) ",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
if (!mvebu_pcie_valid_addr(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) {
debug("- out of range\n");
*valuep = pci_get_ff(size);
return 0;
}
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/*
* mvebu has different internal registers mapped into PCI config space
* in range 0x10-0x34 for PCI bridge, so do not access PCI config space
* for this range and instead read content from driver virtual cfgcache
*/
if (busno == pcie->first_busno && offset >= 0x10 && offset < 0x34) {
data = pcie->cfgcache[(offset - 0x10) / 4];
debug("(addr,size,val)=(0x%04x, %d, 0x%08x) from cfgcache\n",
offset, size, data);
*valuep = pci_conv_32_to_size(data, offset, size);
return 0;
} else if (busno == pcie->first_busno &&
(offset & ~3) == PCI_ROM_ADDRESS1) {
/* mvebu has Expansion ROM Base Address (0x38) at offset 0x30 */
offset -= PCI_ROM_ADDRESS1 - PCIE_EXP_ROM_BAR_OFF;
}
/*
* PCI bridge is device 0 at primary bus but mvebu has it mapped on
* secondary bus with device number 1.
*/
if (busno == pcie->first_busno)
addr = PCIE_CONF_ADDR(pcie->sec_busno, 1, 0, offset);
else
addr = PCIE_CONF_ADDR(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
/* write address */
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
writel(addr, pcie->base + PCIE_CONF_ADDR_OFF);
/* read data */
switch (size) {
case PCI_SIZE_8:
data = readb(pcie->base + PCIE_CONF_DATA_OFF + (offset & 3));
break;
case PCI_SIZE_16:
data = readw(pcie->base + PCIE_CONF_DATA_OFF + (offset & 2));
break;
case PCI_SIZE_32:
data = readl(pcie->base + PCIE_CONF_DATA_OFF);
break;
default:
return -EINVAL;
}
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
if (busno == pcie->first_busno &&
(offset & ~3) == (PCI_HEADER_TYPE & ~3)) {
/*
* Change Header Type of PCI Bridge device to Type 1
* (0x01, used by PCI Bridges) because mvebu reports
* Type 0 (0x00, used by Upstream and Endpoint devices).
*/
data = pci_conv_size_to_32(data, 0, offset, size);
data &= ~0x007f0000;
data |= PCI_HEADER_TYPE_BRIDGE << 16;
data = pci_conv_32_to_size(data, offset, size);
}
debug("(addr,size,val)=(0x%04x, %d, 0x%08x)\n", offset, size, data);
*valuep = data;
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_plat(bus);
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
int busno = PCI_BUS(bdf) - dev_seq(bus);
u32 addr, data;
debug("PCIE CFG write: (b,d,f)=(%2d,%2d,%2d) ",
PCI_BUS(bdf), PCI_DEV(bdf), PCI_FUNC(bdf));
debug("(addr,size,val)=(0x%04x, %d, 0x%08lx)\n", offset, size, value);
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
if (!mvebu_pcie_valid_addr(pcie, busno, PCI_DEV(bdf), PCI_FUNC(bdf))) {
debug("- out of range\n");
return 0;
}
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/*
* mvebu has different internal registers mapped into PCI config space
* in range 0x10-0x34 for PCI bridge, so do not access PCI config space
* for this range and instead write content to driver virtual cfgcache
*/
if (busno == pcie->first_busno && offset >= 0x10 && offset < 0x34) {
debug("Writing to cfgcache only\n");
data = pcie->cfgcache[(offset - 0x10) / 4];
data = pci_conv_size_to_32(data, value, offset, size);
/* mvebu PCI bridge does not have configurable bars */
if ((offset & ~3) == PCI_BASE_ADDRESS_0 ||
(offset & ~3) == PCI_BASE_ADDRESS_1)
data = 0x0;
pcie->cfgcache[(offset - 0x10) / 4] = data;
/* mvebu has its own way how to set PCI primary bus number */
if (offset == PCI_PRIMARY_BUS) {
pcie->first_busno = data & 0xff;
debug("Primary bus number was changed to %d\n",
pcie->first_busno);
}
/* mvebu has its own way how to set PCI secondary bus number */
if (offset == PCI_SECONDARY_BUS ||
(offset == PCI_PRIMARY_BUS && size != PCI_SIZE_8)) {
pcie->sec_busno = (data >> 8) & 0xff;
mvebu_pcie_set_local_bus_nr(pcie, pcie->sec_busno);
debug("Secondary bus number was changed to %d\n",
pcie->sec_busno);
}
return 0;
} else if (busno == pcie->first_busno &&
(offset & ~3) == PCI_ROM_ADDRESS1) {
/* mvebu has Expansion ROM Base Address (0x38) at offset 0x30 */
offset -= PCI_ROM_ADDRESS1 - PCIE_EXP_ROM_BAR_OFF;
}
/*
* PCI bridge is device 0 at primary bus but mvebu has it mapped on
* secondary bus with device number 1.
*/
if (busno == pcie->first_busno)
addr = PCIE_CONF_ADDR(pcie->sec_busno, 1, 0, offset);
else
addr = PCIE_CONF_ADDR(busno, PCI_DEV(bdf), PCI_FUNC(bdf), offset);
/* write address */
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
writel(addr, pcie->base + PCIE_CONF_ADDR_OFF);
/* write data */
switch (size) {
case PCI_SIZE_8:
writeb(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 3));
break;
case PCI_SIZE_16:
writew(value, pcie->base + PCIE_CONF_DATA_OFF + (offset & 2));
break;
case PCI_SIZE_32:
writel(value, pcie->base + PCIE_CONF_DATA_OFF);
break;
default:
return -EINVAL;
}
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_plat(dev);
struct udevice *ctlr = pci_get_controller(dev);
struct pci_controller *hose = dev_get_uclass_priv(ctlr);
u32 reg;
/* Setup PCIe controller to Root Complex mode */
reg = readl(pcie->base + PCIE_CTRL_OFF);
reg |= PCIE_CTRL_RC_MODE;
writel(reg, pcie->base + PCIE_CTRL_OFF);
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/*
* Change Class Code of PCI Bridge device to PCI Bridge (0x600400)
* because default value is Memory controller (0x508000) which
* U-Boot cannot recognize as P2P Bridge.
*
* Note that this mvebu PCI Bridge does not have compliant Type 1
* Configuration Space. Header Type is reported as Type 0 and in
* range 0x10-0x34 it has aliased internal mvebu registers 0x10-0x34
* (e.g. PCIE_BAR_LO_OFF) and register 0x38 is reserved.
*
* Driver for this range redirects access to virtual cfgcache[] buffer
* which avoids changing internal mvebu registers. And changes Header
* Type response value to Type 1.
*/
reg = readl(pcie->base + PCIE_DEV_REV_OFF);
reg &= ~0xffffff00;
reg |= (PCI_CLASS_BRIDGE_PCI << 8) << 8;
writel(reg, pcie->base + PCIE_DEV_REV_OFF);
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/*
* mvebu uses local bus number and local device number to determinate
* type of config request. Type 0 is used if target bus number equals
* local bus number and target device number differs from local device
* number. Type 1 is used if target bus number differs from local bus
* number. And when target bus number equals local bus number and
* target device equals local device number then request is routed to
* PCI Bridge which represent local PCIe Root Port.
*
* It means that PCI primary and secondary buses shares one bus number
* which is configured via local bus number. Determination if config
* request should go to primary or secondary bus is done based on local
* device number.
*
* PCIe is point-to-point bus, so at secondary bus is always exactly one
* device with number 0. So set local device number to 1, it would not
* conflict with any device on secondary bus number and will ensure that
* accessing secondary bus and all buses behind secondary would work
* automatically and correctly. Therefore this configuration of local
* device number implies that setting of local bus number configures
* secondary bus number. Set it to 0 as U-Boot CONFIG_PCI_PNP code will
* later configure it via config write requests to the correct value.
* mvebu_pcie_write_config() catches config write requests which tries
* to change primary/secondary bus number and correctly updates local
* bus number based on new secondary bus number.
*
* With this configuration is PCI Bridge available at secondary bus as
* device number 1. But it must be available at primary bus as device
* number 0. So in mvebu_pcie_read_config() and mvebu_pcie_write_config()
* functions rewrite address to the real one when accessing primary bus.
*/
mvebu_pcie_set_local_bus_nr(pcie, 0);
mvebu_pcie_set_local_dev_nr(pcie, 1);
pcie->mem.start = (u32)mvebu_pcie_membase;
pcie->mem.end = pcie->mem.start + MBUS_PCI_MEM_SIZE - 1;
mvebu_pcie_membase += MBUS_PCI_MEM_SIZE;
if (mvebu_mbus_add_window_by_id(pcie->mem_target, pcie->mem_attr,
(phys_addr_t)pcie->mem.start,
MBUS_PCI_MEM_SIZE)) {
printf("PCIe unable to add mbus window for mem at %08x+%08x\n",
(u32)pcie->mem.start, MBUS_PCI_MEM_SIZE);
}
pcie->io.start = (u32)mvebu_pcie_iobase;
pcie->io.end = pcie->io.start + MBUS_PCI_IO_SIZE - 1;
mvebu_pcie_iobase += MBUS_PCI_IO_SIZE;
if (mvebu_mbus_add_window_by_id(pcie->io_target, pcie->io_attr,
(phys_addr_t)pcie->io.start,
MBUS_PCI_IO_SIZE)) {
printf("PCIe unable to add mbus window for IO at %08x+%08x\n",
(u32)pcie->io.start, MBUS_PCI_IO_SIZE);
}
/* Setup windows and configure host bridge */
mvebu_pcie_setup_wins(pcie);
/* PCI memory space */
pci_set_region(hose->regions + 0, pcie->mem.start,
pcie->mem.start, MBUS_PCI_MEM_SIZE, PCI_REGION_MEM);
pci_set_region(hose->regions + 1,
0, 0,
gd->ram_size,
PCI_REGION_MEM | PCI_REGION_SYS_MEMORY);
pci_set_region(hose->regions + 2, pcie->io.start,
pcie->io.start, MBUS_PCI_IO_SIZE, PCI_REGION_IO);
hose->region_count = 3;
/* 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));
pci: pci_mvebu: Properly configure and use PCI Bridge (PCIe Root Port) The mysterious "Memory controller" PCI device which is present in PCI config space is improperly configured and crippled PCI Bridge which acts as PCIe Root Port for endpoint PCIe card. This PCI Bridge reports in PCI config space incorrect Class Code (Memory Controller) and incorrect Header Type (Type 0). It looks like HW bug in mvebu PCIe controller but apparently it can be changed via mvebu registers to correct values. The worst thing is that this PCI Bridge is crippled and its PCI config registers in range 0x10-0x34 alias access to internal mvebu registers which have different functionality as PCI Bridge registers. Moreover, configuration of PCI primary and secondary bus numbers (registers 0x18 and 0x19) is done via totally different mvebu registers via totally strange method and cannot be done via PCI Bridge config space. Due to above fact about PCI config range 0x10-0x34, allocate a private cfgcache[] buffer in the driver, to which PCI config access requests to the 0x10-0x34 space will be redirected in mvebu_pcie_read_config() and mvebu_pcie_write_config() functions. Function mvebu_pcie_write_config() will also catch writes to PCI_PRIMARY_BUS (0x18) and PCI_SECONDARY_BUS (0x19) registers and set PCI Bridge primary and secondary bus numbers via mvebu's own method. Also, Expansion ROM Base Address register (0x38) is available, but at different offset 0x30. So recalculate register offset before accessing PCI config space. After these steps U-Boot sees working PCI Bridge and CONFIG_PCI_PNP code can finally start enumerating all PCIe devices correctly, even with more complicated PCI topology. So update also mvebu_pcie_valid_addr() function to reflect state of the real device topology. Each PCIe port is de-facto isolated and every PCI Bridge which is part of PCIe Root Complex is also isolated, so put them on separate PCI buses as (local) device 0. U-Boot already supports enumerating separate PCI buses, real (HW) bus number can be retrieved by "PCI_BUS(bdf) - dev_seq(bus)" code, so update config read/write functions to properly handle more complicated tree topologies (e.g. when a PCIe switch with multiple PCI buses is connected to the PCIe port). Local bus number and local device number on mvebu are used for determining which config request type is used (Type 0 vs Type 1). On normal non-broken PCIe hardware it is done by primary and secondary bus numbers. So correctly translate settings between these numbers to ensure that correct config requests are sent over the PCIe bus. As bus numbers are correctly re-configured, it does not make sense to print some initial bogus configuration during probe, so remove this debug code. Signed-off-by: Pali Rohár <pali@kernel.org> Reviewed-by: Marek Behún <marek.behun@nic.cz> Reviewed-by: Stefan Roese <sr@denx.de>
2021-10-22 14:22:10 +00:00
/* PCI Bridge support 32-bit I/O and 64-bit prefetch mem addressing */
pcie->cfgcache[(PCI_IO_BASE - 0x10) / 4] =
PCI_IO_RANGE_TYPE_32 | (PCI_IO_RANGE_TYPE_32 << 8);
pcie->cfgcache[(PCI_PREF_MEMORY_BASE - 0x10) / 4] =
PCI_PREF_RANGE_TYPE_64 | (PCI_PREF_RANGE_TYPE_64 << 16);
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_of_to_plat(struct udevice *dev)
{
struct mvebu_pcie *pcie = dev_get_plat(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;
}
ret = mvebu_get_tgt_attr(dev_ofnode(dev->parent), pcie->devfn,
IORESOURCE_IO,
&pcie->io_target, &pcie->io_attr);
if (ret < 0) {
printf("%s: cannot get tgt/attr for IO 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;
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,
.of_to_plat = mvebu_pcie_of_to_plat,
.plat_auto = 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 pci 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(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,
};