Commit a66999a3 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'remotes/lorenzo/pci/brcmstb'

- Make PCIE_BRCMSTB depend on and default to ARCH_BRCMSTB (Jim Quinlan)

- Add DT bindings for 7278, 7216, 7211, and new properties (Jim Quinlan)

- Add bcm7278 register info (Jim Quinlan)

- Add suspend and resume pm_ops (Jim Quinlan)

- Add bcm7278 PERST# support (Jim Quinlan)

- Add control of RESCAL reset (Jim Quinlan)

- Set additional internal memory DMA viewport sizes (Jim Quinlan)

- Accommodate MSI for older chips (Jim Quinlan)

- Set bus max burst size by chip type (Jim Quinlan)

- Add bcm7211, bcm7216, bcm7445, bcm7278 to match list (Jim Quinlan)

* remotes/lorenzo/pci/brcmstb:
  PCI: brcmstb: Add bcm7211, bcm7216, bcm7445, bcm7278 to match list
  PCI: brcmstb: Set bus max burst size by chip type
  PCI: brcmstb: Accommodate MSI for older chips
  PCI: brcmstb: Set additional internal memory DMA viewport sizes
  PCI: brcmstb: Add control of rescal reset
  PCI: brcmstb: Add bcm7278 PERST# support
  PCI: brcmstb: Add suspend and resume pm_ops
  PCI: brcmstb: Add bcm7278 register info
  dt-bindings: PCI: Add bindings for more Brcmstb chips
  PCI: brcmstb: PCIE_BRCMSTB depends on ARCH_BRCMSTB
parents d1640a83 1f66d95e
Loading
Loading
Loading
Loading
+49 −7
Original line number Diff line number Diff line
@@ -9,12 +9,15 @@ title: Brcmstb PCIe Host Controller Device Tree Bindings
maintainers:
  - Nicolas Saenz Julienne <nsaenzjulienne@suse.de>

allOf:
  - $ref: /schemas/pci/pci-bus.yaml#

properties:
  compatible:
    const: brcm,bcm2711-pcie # The Raspberry Pi 4
    items:
      - enum:
          - brcm,bcm2711-pcie # The Raspberry Pi 4
          - brcm,bcm7211-pcie # Broadcom STB version of RPi4
          - brcm,bcm7278-pcie # Broadcom 7278 Arm
          - brcm,bcm7216-pcie # Broadcom 7216 Arm
          - brcm,bcm7445-pcie # Broadcom 7445 Arm

  reg:
    maxItems: 1
@@ -34,10 +37,12 @@ properties:
      - const: msi

  ranges:
    maxItems: 1
    minItems: 1
    maxItems: 4

  dma-ranges:
    maxItems: 1
    minItems: 1
    maxItems: 6

  clocks:
    maxItems: 1
@@ -58,8 +63,31 @@ properties:

  aspm-no-l0s: true

  resets:
    description: for "brcm,bcm7216-pcie", must be a valid reset
      phandle pointing to the RESCAL reset controller provider node.
    $ref: "/schemas/types.yaml#/definitions/phandle"

  reset-names:
    items:
      - const: rescal

  brcm,scb-sizes:
    description: u64 giving the 64bit PCIe memory
      viewport size of a memory controller.  There may be up to
      three controllers, and each size must be a power of two
      with a size greater or equal to the amount of memory the
      controller supports.  Note that each memory controller
      may have two component regions -- base and extended -- so
      this information cannot be deduced from the dma-ranges.
    $ref: /schemas/types.yaml#/definitions/uint64-array
    items:
      minItems: 1
      maxItems: 3

required:
  - reg
  - ranges
  - dma-ranges
  - "#interrupt-cells"
  - interrupts
@@ -68,6 +96,18 @@ required:
  - interrupt-map
  - msi-controller

allOf:
  - $ref: /schemas/pci/pci-bus.yaml#
  - if:
      properties:
        compatible:
          contains:
            const: brcm,bcm7216-pcie
    then:
      required:
        - resets
        - reset-names

unevaluatedProperties: false

examples:
@@ -93,7 +133,9 @@ examples:
                    msi-parent = <&pcie0>;
                    msi-controller;
                    ranges = <0x02000000 0x0 0xf8000000 0x6 0x00000000 0x0 0x04000000>;
                    dma-ranges = <0x02000000 0x0 0x00000000 0x0 0x00000000 0x0 0x80000000>;
                    dma-ranges = <0x42000000 0x1 0x00000000 0x0 0x40000000 0x0 0x80000000>,
                                 <0x42000000 0x1 0x80000000 0x3 0x00000000 0x0 0x80000000>;
                    brcm,enable-ssc;
                    brcm,scb-sizes =  <0x0000000080000000 0x0000000080000000>;
            };
    };
+2 −1
Original line number Diff line number Diff line
@@ -270,9 +270,10 @@ config VMD

config PCIE_BRCMSTB
	tristate "Broadcom Brcmstb PCIe host controller"
	depends on ARCH_BCM2835 || COMPILE_TEST
	depends on ARCH_BRCMSTB || ARCH_BCM2835 || COMPILE_TEST
	depends on OF
	depends on PCI_MSI_IRQ_DOMAIN
	default ARCH_BRCMSTB
	help
	  Say Y here to enable PCIe host controller support for
	  Broadcom STB based SoCs, like the Raspberry Pi 4.
+381 −63
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/of_platform.h>
#include <linux/pci.h>
#include <linux/printk.h>
#include <linux/reset.h>
#include <linux/sizes.h>
#include <linux/slab.h>
#include <linux/string.h>
@@ -54,8 +55,11 @@
#define  PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK		0x1000
#define  PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK	0x2000
#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK	0x300000
#define  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128		0x0

#define  PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK		0xf8000000
#define  PCIE_MISC_MISC_CTRL_SCB1_SIZE_MASK		0x07c00000
#define  PCIE_MISC_MISC_CTRL_SCB2_SIZE_MASK		0x0000001f
#define  SCB_SIZE_MASK(x) PCIE_MISC_MISC_CTRL_SCB ## x ## _SIZE_MASK

#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_LO		0x400c
#define PCIE_MEM_WIN0_LO(win)	\
@@ -79,10 +83,12 @@
#define PCIE_MISC_MSI_BAR_CONFIG_HI			0x4048

#define PCIE_MISC_MSI_DATA_CONFIG			0x404c
#define  PCIE_MISC_MSI_DATA_CONFIG_VAL			0xffe06540
#define  PCIE_MISC_MSI_DATA_CONFIG_VAL_32		0xffe06540
#define  PCIE_MISC_MSI_DATA_CONFIG_VAL_8		0xfff86540

#define PCIE_MISC_PCIE_CTRL				0x4064
#define  PCIE_MISC_PCIE_CTRL_PCIE_L23_REQUEST_MASK	0x1
#define PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK		0x4

#define PCIE_MISC_PCIE_STATUS				0x4068
#define  PCIE_MISC_PCIE_STATUS_PCIE_PORT_MASK		0x80
@@ -90,6 +96,9 @@
#define  PCIE_MISC_PCIE_STATUS_PCIE_PHYLINKUP_MASK	0x10
#define  PCIE_MISC_PCIE_STATUS_PCIE_LINK_IN_L23_MASK	0x40

#define PCIE_MISC_REVISION				0x406c
#define  BRCM_PCIE_HW_REV_33				0x0303

#define PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT		0x4070
#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK	0xfff00000
#define  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK	0xfff0
@@ -110,10 +119,14 @@
#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_CLKREQ_DEBUG_ENABLE_MASK	0x2
#define  PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x08000000

#define PCIE_MSI_INTR2_STATUS				0x4500
#define PCIE_MSI_INTR2_CLR				0x4508
#define PCIE_MSI_INTR2_MASK_SET				0x4510
#define PCIE_MSI_INTR2_MASK_CLR				0x4514

#define PCIE_INTR2_CPU_BASE		0x4300
#define PCIE_MSI_INTR2_BASE		0x4500
/* Offsets from PCIE_INTR2_CPU_BASE and PCIE_MSI_INTR2_BASE */
#define  MSI_INT_STATUS			0x0
#define  MSI_INT_CLR			0x8
#define  MSI_INT_MASK_SET		0x10
#define  MSI_INT_MASK_CLR		0x14

#define PCIE_EXT_CFG_DATA				0x8000

@@ -122,13 +135,19 @@
#define  PCIE_EXT_SLOT_SHIFT				15
#define  PCIE_EXT_FUNC_SHIFT				12

#define PCIE_RGR1_SW_INIT_1				0x9210
#define  PCIE_RGR1_SW_INIT_1_PERST_MASK			0x1
#define  PCIE_RGR1_SW_INIT_1_INIT_MASK			0x2
#define  PCIE_RGR1_SW_INIT_1_PERST_SHIFT		0x0

#define RGR1_SW_INIT_1_INIT_GENERIC_MASK		0x2
#define RGR1_SW_INIT_1_INIT_GENERIC_SHIFT		0x1
#define RGR1_SW_INIT_1_INIT_7278_MASK			0x1
#define RGR1_SW_INIT_1_INIT_7278_SHIFT			0x0

/* PCIe parameters */
#define BRCM_NUM_PCIE_OUT_WINS		0x4
#define BRCM_INT_PCI_MSI_NR		32
#define BRCM_INT_PCI_MSI_LEGACY_NR	8
#define BRCM_INT_PCI_MSI_SHIFT		0

/* MSI target adresses */
#define BRCM_MSI_TARGET_ADDR_LT_4GB	0x0fffffffcULL
@@ -153,6 +172,85 @@
#define SSC_STATUS_OFFSET		0x1
#define SSC_STATUS_SSC_MASK		0x400
#define SSC_STATUS_PLL_LOCK_MASK	0x800
#define PCIE_BRCM_MAX_MEMC		3

#define IDX_ADDR(pcie)			(pcie->reg_offsets[EXT_CFG_INDEX])
#define DATA_ADDR(pcie)			(pcie->reg_offsets[EXT_CFG_DATA])
#define PCIE_RGR1_SW_INIT_1(pcie)	(pcie->reg_offsets[RGR1_SW_INIT_1])

/* Rescal registers */
#define PCIE_DVT_PMU_PCIE_PHY_CTRL				0xc700
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS			0x3
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK		0x4
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT	0x2
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK		0x2
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT		0x1
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK		0x1
#define  PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT		0x0

/* Forward declarations */
struct brcm_pcie;
static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val);
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val);

enum {
	RGR1_SW_INIT_1,
	EXT_CFG_INDEX,
	EXT_CFG_DATA,
};

enum {
	RGR1_SW_INIT_1_INIT_MASK,
	RGR1_SW_INIT_1_INIT_SHIFT,
};

enum pcie_type {
	GENERIC,
	BCM7278,
	BCM2711,
};

struct pcie_cfg_data {
	const int *offsets;
	const enum pcie_type type;
	void (*perst_set)(struct brcm_pcie *pcie, u32 val);
	void (*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
};

static const int pcie_offsets[] = {
	[RGR1_SW_INIT_1] = 0x9210,
	[EXT_CFG_INDEX]  = 0x9000,
	[EXT_CFG_DATA]   = 0x9004,
};

static const struct pcie_cfg_data generic_cfg = {
	.offsets	= pcie_offsets,
	.type		= GENERIC,
	.perst_set	= brcm_pcie_perst_set_generic,
	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
};

static const int pcie_offset_bcm7278[] = {
	[RGR1_SW_INIT_1] = 0xc010,
	[EXT_CFG_INDEX] = 0x9000,
	[EXT_CFG_DATA] = 0x9004,
};

static const struct pcie_cfg_data bcm7278_cfg = {
	.offsets	= pcie_offset_bcm7278,
	.type		= BCM7278,
	.perst_set	= brcm_pcie_perst_set_7278,
	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_7278,
};

static const struct pcie_cfg_data bcm2711_cfg = {
	.offsets	= pcie_offsets,
	.type		= BCM2711,
	.perst_set	= brcm_pcie_perst_set_generic,
	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,
};

struct brcm_msi {
	struct device		*dev;
@@ -165,6 +263,12 @@ struct brcm_msi {
	int			irq;
	/* used indicates which MSI interrupts have been alloc'd */
	unsigned long		used;
	bool			legacy;
	/* Some chips have MSIs in bits [31..24] of a shared register. */
	int			legacy_shift;
	int			nr; /* No. of MSI available, depends on chip */
	/* This is the base pointer for interrupt status/set/clr regs */
	void __iomem		*intr_base;
};

/* Internal PCIe Host Controller Information.*/
@@ -177,6 +281,14 @@ struct brcm_pcie {
	int			gen;
	u64			msi_target_addr;
	struct brcm_msi		*msi;
	const int		*reg_offsets;
	enum pcie_type		type;
	struct reset_control	*rescal;
	int			num_memc;
	u64			memc_size[PCIE_BRCM_MAX_MEMC];
	u32			hw_rev;
	void			(*perst_set)(struct brcm_pcie *pcie, u32 val);
	void			(*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val);
};

/*
@@ -367,8 +479,10 @@ static void brcm_pcie_msi_isr(struct irq_desc *desc)
	msi = irq_desc_get_handler_data(desc);
	dev = msi->dev;

	status = readl(msi->base + PCIE_MSI_INTR2_STATUS);
	for_each_set_bit(bit, &status, BRCM_INT_PCI_MSI_NR) {
	status = readl(msi->intr_base + MSI_INT_STATUS);
	status >>= msi->legacy_shift;

	for_each_set_bit(bit, &status, msi->nr) {
		virq = irq_find_mapping(msi->inner_domain, bit);
		if (virq)
			generic_handle_irq(virq);
@@ -385,7 +499,7 @@ static void brcm_msi_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)

	msg->address_lo = lower_32_bits(msi->target_addr);
	msg->address_hi = upper_32_bits(msi->target_addr);
	msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL) | data->hwirq;
	msg->data = (0xffff & PCIE_MISC_MSI_DATA_CONFIG_VAL_32) | data->hwirq;
}

static int brcm_msi_set_affinity(struct irq_data *irq_data,
@@ -397,8 +511,9 @@ static int brcm_msi_set_affinity(struct irq_data *irq_data,
static void brcm_msi_ack_irq(struct irq_data *data)
{
	struct brcm_msi *msi = irq_data_get_irq_chip_data(data);
	const int shift_amt = data->hwirq + msi->legacy_shift;

	writel(1 << data->hwirq, msi->base + PCIE_MSI_INTR2_CLR);
	writel(1 << shift_amt, msi->intr_base + MSI_INT_CLR);
}


@@ -414,7 +529,7 @@ static int brcm_msi_alloc(struct brcm_msi *msi)
	int hwirq;

	mutex_lock(&msi->lock);
	hwirq = bitmap_find_free_region(&msi->used, BRCM_INT_PCI_MSI_NR, 0);
	hwirq = bitmap_find_free_region(&msi->used, msi->nr, 0);
	mutex_unlock(&msi->lock);

	return hwirq;
@@ -463,8 +578,7 @@ static int brcm_allocate_domains(struct brcm_msi *msi)
	struct fwnode_handle *fwnode = of_node_to_fwnode(msi->np);
	struct device *dev = msi->dev;

	msi->inner_domain = irq_domain_add_linear(NULL, BRCM_INT_PCI_MSI_NR,
						  &msi_domain_ops, msi);
	msi->inner_domain = irq_domain_add_linear(NULL, msi->nr, &msi_domain_ops, msi);
	if (!msi->inner_domain) {
		dev_err(dev, "failed to create IRQ domain\n");
		return -ENOMEM;
@@ -501,7 +615,10 @@ static void brcm_msi_remove(struct brcm_pcie *pcie)

static void brcm_msi_set_regs(struct brcm_msi *msi)
{
	writel(0xffffffff, msi->base + PCIE_MSI_INTR2_MASK_CLR);
	u32 val = __GENMASK(31, msi->legacy_shift);

	writel(val, msi->intr_base + MSI_INT_MASK_CLR);
	writel(val, msi->intr_base + MSI_INT_CLR);

	/*
	 * The 0 bit of PCIE_MISC_MSI_BAR_CONFIG_LO is repurposed to MSI
@@ -512,8 +629,8 @@ static void brcm_msi_set_regs(struct brcm_msi *msi)
	writel(upper_32_bits(msi->target_addr),
	       msi->base + PCIE_MISC_MSI_BAR_CONFIG_HI);

	writel(PCIE_MISC_MSI_DATA_CONFIG_VAL,
	       msi->base + PCIE_MISC_MSI_DATA_CONFIG);
	val = msi->legacy ? PCIE_MISC_MSI_DATA_CONFIG_VAL_8 : PCIE_MISC_MSI_DATA_CONFIG_VAL_32;
	writel(val, msi->base + PCIE_MISC_MSI_DATA_CONFIG);
}

static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
@@ -538,6 +655,17 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)
	msi->np = pcie->np;
	msi->target_addr = pcie->msi_target_addr;
	msi->irq = irq;
	msi->legacy = pcie->hw_rev < BRCM_PCIE_HW_REV_33;

	if (msi->legacy) {
		msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE;
		msi->nr = BRCM_INT_PCI_MSI_LEGACY_NR;
		msi->legacy_shift = 24;
	} else {
		msi->intr_base = msi->base + PCIE_MSI_INTR2_BASE;
		msi->nr = BRCM_INT_PCI_MSI_NR;
		msi->legacy_shift = 0;
	}

	ret = brcm_allocate_domains(msi);
	if (ret)
@@ -601,22 +729,43 @@ static struct pci_ops brcm_pcie_ops = {
	.write = pci_generic_config_write,
};

static inline void brcm_pcie_bridge_sw_init_set(struct brcm_pcie *pcie, u32 val)
static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val)
{
	u32 tmp, mask =  RGR1_SW_INIT_1_INIT_GENERIC_MASK;
	u32 shift = RGR1_SW_INIT_1_INIT_GENERIC_SHIFT;

	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
	tmp = (tmp & ~mask) | ((val << shift) & mask);
	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}

static inline void brcm_pcie_bridge_sw_init_set_7278(struct brcm_pcie *pcie, u32 val)
{
	u32 tmp, mask =  RGR1_SW_INIT_1_INIT_7278_MASK;
	u32 shift = RGR1_SW_INIT_1_INIT_7278_SHIFT;

	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
	tmp = (tmp & ~mask) | ((val << shift) & mask);
	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}

static inline void brcm_pcie_perst_set_7278(struct brcm_pcie *pcie, u32 val)
{
	u32 tmp;

	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
	u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_INIT_MASK);
	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
	/* Perst bit has moved and assert value is 0 */
	tmp = readl(pcie->base + PCIE_MISC_PCIE_CTRL);
	u32p_replace_bits(&tmp, !val, PCIE_MISC_PCIE_CTRL_PCIE_PERSTB_MASK);
	writel(tmp, pcie->base +  PCIE_MISC_PCIE_CTRL);
}

static inline void brcm_pcie_perst_set(struct brcm_pcie *pcie, u32 val)
static inline void brcm_pcie_perst_set_generic(struct brcm_pcie *pcie, u32 val)
{
	u32 tmp;

	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1);
	tmp = readl(pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
	u32p_replace_bits(&tmp, val, PCIE_RGR1_SW_INIT_1_PERST_MASK);
	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1);
	writel(tmp, pcie->base + PCIE_RGR1_SW_INIT_1(pcie));
}

static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
@@ -624,22 +773,44 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,
							u64 *rc_bar2_offset)
{
	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);
	struct device *dev = pcie->dev;
	struct resource_entry *entry;
	struct device *dev = pcie->dev;
	u64 lowest_pcie_addr = ~(u64)0;
	int ret, i = 0;
	u64 size = 0;

	entry = resource_list_first_type(&bridge->dma_ranges, IORESOURCE_MEM);
	if (!entry)
		return -ENODEV;
	resource_list_for_each_entry(entry, &bridge->dma_ranges) {
		u64 pcie_beg = entry->res->start - entry->offset;

		size += entry->res->end - entry->res->start + 1;
		if (pcie_beg < lowest_pcie_addr)
			lowest_pcie_addr = pcie_beg;
	}

	/*
	 * The controller expects the inbound window offset to be calculated as
	 * the difference between PCIe's address space and CPU's. The offset
	 * provided by the firmware is calculated the opposite way, so we
	 * negate it.
	 */
	*rc_bar2_offset = -entry->offset;
	*rc_bar2_size = 1ULL << fls64(entry->res->end - entry->res->start);
	if (lowest_pcie_addr == ~(u64)0) {
		dev_err(dev, "DT node has no dma-ranges\n");
		return -EINVAL;
	}

	ret = of_property_read_variable_u64_array(pcie->np, "brcm,scb-sizes", pcie->memc_size, 1,
						  PCIE_BRCM_MAX_MEMC);

	if (ret <= 0) {
		/* Make an educated guess */
		pcie->num_memc = 1;
		pcie->memc_size[0] = 1ULL << fls64(size - 1);
	} else {
		pcie->num_memc = ret;
	}

	/* Each memc is viewed through a "port" that is a power of 2 */
	for (i = 0, size = 0; i < pcie->num_memc; i++)
		size += pcie->memc_size[i];

	/* System memory starts at this address in PCIe-space */
	*rc_bar2_offset = lowest_pcie_addr;
	/* The sum of all memc views must also be a power of 2 */
	*rc_bar2_size = 1ULL << fls64(size - 1);

	/*
	 * We validate the inbound memory view even though we should trust
@@ -691,22 +862,19 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
	void __iomem *base = pcie->base;
	struct device *dev = pcie->dev;
	struct resource_entry *entry;
	unsigned int scb_size_val;
	bool ssc_good = false;
	struct resource *res;
	int num_out_wins = 0;
	u16 nlw, cls, lnksta;
	int i, ret;
	u32 tmp, aspm_support;
	int i, ret, memc;
	u32 tmp, burst, aspm_support;

	/* Reset the bridge */
	brcm_pcie_bridge_sw_init_set(pcie, 1);
	brcm_pcie_perst_set(pcie, 1);

	pcie->bridge_sw_init_set(pcie, 1);
	usleep_range(100, 200);

	/* Take the bridge out of reset */
	brcm_pcie_bridge_sw_init_set(pcie, 0);
	pcie->bridge_sw_init_set(pcie, 0);

	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
	tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;
@@ -714,11 +882,22 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
	/* Wait for SerDes to be stable */
	usleep_range(100, 200);

	/*
	 * SCB_MAX_BURST_SIZE is a two bit field.  For GENERIC chips it
	 * is encoded as 0=128, 1=256, 2=512, 3=Rsvd, for BCM7278 it
	 * is encoded as 0=Rsvd, 1=128, 2=256, 3=512.
	 */
	if (pcie->type == BCM2711)
		burst = 0x0; /* 128B */
	else if (pcie->type == BCM7278)
		burst = 0x3; /* 512 bytes */
	else
		burst = 0x2; /* 512 bytes */

	/* Set SCB_MAX_BURST_SIZE, CFG_READ_UR_MODE, SCB_ACCESS_EN */
	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_SCB_ACCESS_EN_MASK);
	u32p_replace_bits(&tmp, 1, PCIE_MISC_MISC_CTRL_CFG_READ_UR_MODE_MASK);
	u32p_replace_bits(&tmp, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_128,
			  PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
	u32p_replace_bits(&tmp, burst, PCIE_MISC_MISC_CTRL_MAX_BURST_SIZE_MASK);
	writel(tmp, base + PCIE_MISC_MISC_CTRL);

	ret = brcm_pcie_get_rc_bar2_size_and_offset(pcie, &rc_bar2_size,
@@ -733,11 +912,17 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
	writel(upper_32_bits(rc_bar2_offset),
	       base + PCIE_MISC_RC_BAR2_CONFIG_HI);

	scb_size_val = rc_bar2_size ?
		       ilog2(rc_bar2_size) - 15 : 0xf; /* 0xf is 1GB */
	tmp = readl(base + PCIE_MISC_MISC_CTRL);
	u32p_replace_bits(&tmp, scb_size_val,
			  PCIE_MISC_MISC_CTRL_SCB0_SIZE_MASK);
	for (memc = 0; memc < pcie->num_memc; memc++) {
		u32 scb_size_val = ilog2(pcie->memc_size[memc]) - 15;

		if (memc == 0)
			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(0));
		else if (memc == 1)
			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(1));
		else if (memc == 2)
			u32p_replace_bits(&tmp, scb_size_val, SCB_SIZE_MASK(2));
	}
	writel(tmp, base + PCIE_MISC_MISC_CTRL);

	/*
@@ -762,17 +947,11 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)
	tmp &= ~PCIE_MISC_RC_BAR3_CONFIG_LO_SIZE_MASK;
	writel(tmp, base + PCIE_MISC_RC_BAR3_CONFIG_LO);

	/* Mask all interrupts since we are not handling any yet */
	writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_MASK_SET);

	/* clear any interrupts we find on boot */
	writel(0xffffffff, pcie->base + PCIE_MSI_INTR2_CLR);

	if (pcie->gen)
		brcm_pcie_set_gen(pcie, pcie->gen);

	/* Unassert the fundamental reset */
	brcm_pcie_perst_set(pcie, 0);
	pcie->perst_set(pcie, 0);

	/*
	 * Give the RC/EP time to wake up, before trying to configure RC.
@@ -884,6 +1063,52 @@ static void brcm_pcie_enter_l23(struct brcm_pcie *pcie)
		dev_err(pcie->dev, "failed to enter low-power link state\n");
}

static int brcm_phy_cntl(struct brcm_pcie *pcie, const int start)
{
	static const u32 shifts[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_SHIFT,
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_SHIFT,
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_SHIFT,};
	static const u32 masks[PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS] = {
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_PWRDN_MASK,
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_RESET_MASK,
		PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_DIG_RESET_MASK,};
	const int beg = start ? 0 : PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS - 1;
	const int end = start ? PCIE_DVT_PMU_PCIE_PHY_CTRL_DAST_NFLDS : -1;
	u32 tmp, combined_mask = 0;
	u32 val;
	void __iomem *base = pcie->base;
	int i, ret;

	for (i = beg; i != end; start ? i++ : i--) {
		val = start ? BIT_MASK(shifts[i]) : 0;
		tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
		tmp = (tmp & ~masks[i]) | (val & masks[i]);
		writel(tmp, base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
		usleep_range(50, 200);
		combined_mask |= masks[i];
	}

	tmp = readl(base + PCIE_DVT_PMU_PCIE_PHY_CTRL);
	val = start ? combined_mask : 0;

	ret = (tmp & combined_mask) == val ? 0 : -EIO;
	if (ret)
		dev_err(pcie->dev, "failed to %s phy\n", (start ? "start" : "stop"));

	return ret;
}

static inline int brcm_phy_start(struct brcm_pcie *pcie)
{
	return pcie->rescal ? brcm_phy_cntl(pcie, 1) : 0;
}

static inline int brcm_phy_stop(struct brcm_pcie *pcie)
{
	return pcie->rescal ? brcm_phy_cntl(pcie, 0) : 0;
}

static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
{
	void __iomem *base = pcie->base;
@@ -892,7 +1117,7 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
	if (brcm_pcie_link_up(pcie))
		brcm_pcie_enter_l23(pcie);
	/* Assert fundamental reset */
	brcm_pcie_perst_set(pcie, 1);
	pcie->perst_set(pcie, 1);

	/* Deassert request for L23 in case it was asserted */
	tmp = readl(base + PCIE_MISC_PCIE_CTRL);
@@ -905,13 +1130,66 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)
	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);

	/* Shutdown PCIe bridge */
	brcm_pcie_bridge_sw_init_set(pcie, 1);
	pcie->bridge_sw_init_set(pcie, 1);
}

static int brcm_pcie_suspend(struct device *dev)
{
	struct brcm_pcie *pcie = dev_get_drvdata(dev);
	int ret;

	brcm_pcie_turn_off(pcie);
	ret = brcm_phy_stop(pcie);
	clk_disable_unprepare(pcie->clk);

	return ret;
}

static int brcm_pcie_resume(struct device *dev)
{
	struct brcm_pcie *pcie = dev_get_drvdata(dev);
	void __iomem *base;
	u32 tmp;
	int ret;

	base = pcie->base;
	clk_prepare_enable(pcie->clk);

	ret = brcm_phy_start(pcie);
	if (ret)
		goto err;

	/* Take bridge out of reset so we can access the SERDES reg */
	pcie->bridge_sw_init_set(pcie, 0);

	/* SERDES_IDDQ = 0 */
	tmp = readl(base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);
	u32p_replace_bits(&tmp, 0, PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK);
	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);

	/* wait for serdes to be stable */
	udelay(100);

	ret = brcm_pcie_setup(pcie);
	if (ret)
		goto err;

	if (pcie->msi)
		brcm_msi_set_regs(pcie->msi);

	return 0;

err:
	clk_disable_unprepare(pcie->clk);
	return ret;
}

static void __brcm_pcie_remove(struct brcm_pcie *pcie)
{
	brcm_msi_remove(pcie);
	brcm_pcie_turn_off(pcie);
	brcm_phy_stop(pcie);
	reset_control_assert(pcie->rescal);
	clk_disable_unprepare(pcie->clk);
}

@@ -927,11 +1205,21 @@ static int brcm_pcie_remove(struct platform_device *pdev)
	return 0;
}

static const struct of_device_id brcm_pcie_match[] = {
	{ .compatible = "brcm,bcm2711-pcie", .data = &bcm2711_cfg },
	{ .compatible = "brcm,bcm7211-pcie", .data = &generic_cfg },
	{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },
	{ .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg },
	{ .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg },
	{},
};

static int brcm_pcie_probe(struct platform_device *pdev)
{
	struct device_node *np = pdev->dev.of_node, *msi_np;
	struct pci_host_bridge *bridge;
	struct device_node *fw_np;
	const struct pcie_cfg_data *data;
	struct brcm_pcie *pcie;
	int ret;

@@ -953,9 +1241,19 @@ static int brcm_pcie_probe(struct platform_device *pdev)
	if (!bridge)
		return -ENOMEM;

	data = of_device_get_match_data(&pdev->dev);
	if (!data) {
		pr_err("failed to look up compatible string\n");
		return -EINVAL;
	}

	pcie = pci_host_bridge_priv(bridge);
	pcie->dev = &pdev->dev;
	pcie->np = np;
	pcie->reg_offsets = data->offsets;
	pcie->type = data->type;
	pcie->perst_set = data->perst_set;
	pcie->bridge_sw_init_set = data->bridge_sw_init_set;

	pcie->base = devm_platform_ioremap_resource(pdev, 0);
	if (IS_ERR(pcie->base))
@@ -975,11 +1273,29 @@ static int brcm_pcie_probe(struct platform_device *pdev)
		dev_err(&pdev->dev, "could not enable clock\n");
		return ret;
	}
	pcie->rescal = devm_reset_control_get_optional_shared(&pdev->dev, "rescal");
	if (IS_ERR(pcie->rescal)) {
		clk_disable_unprepare(pcie->clk);
		return PTR_ERR(pcie->rescal);
	}

	ret = reset_control_deassert(pcie->rescal);
	if (ret)
		dev_err(&pdev->dev, "failed to deassert 'rescal'\n");

	ret = brcm_phy_start(pcie);
	if (ret) {
		reset_control_assert(pcie->rescal);
		clk_disable_unprepare(pcie->clk);
		return ret;
	}

	ret = brcm_pcie_setup(pcie);
	if (ret)
		goto fail;

	pcie->hw_rev = readl(pcie->base + PCIE_MISC_REVISION);

	msi_np = of_parse_phandle(pcie->np, "msi-parent", 0);
	if (pci_msi_enabled() && msi_np == pcie->np) {
		ret = brcm_pcie_enable_msi(pcie);
@@ -1000,18 +1316,20 @@ static int brcm_pcie_probe(struct platform_device *pdev)
	return ret;
}

static const struct of_device_id brcm_pcie_match[] = {
	{ .compatible = "brcm,bcm2711-pcie" },
	{},
};
MODULE_DEVICE_TABLE(of, brcm_pcie_match);

static const struct dev_pm_ops brcm_pcie_pm_ops = {
	.suspend = brcm_pcie_suspend,
	.resume = brcm_pcie_resume,
};

static struct platform_driver brcm_pcie_driver = {
	.probe = brcm_pcie_probe,
	.remove = brcm_pcie_remove,
	.driver = {
		.name = "brcm-pcie",
		.of_match_table = brcm_pcie_match,
		.pm = &brcm_pcie_pm_ops,
	},
};
module_platform_driver(brcm_pcie_driver);