Merge tag 'pci-v5.10-changes' of git://git.kernel.org/pub/scm/linux/kernel/git/helgaa...
[linux-2.6-microblaze.git] / drivers / pci / controller / pcie-brcmstb.c
index bac63d0..bea8689 100644 (file)
@@ -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>
 #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)  \
 #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
@@ -88,6 +94,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
 #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
 
 #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
 #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;
@@ -163,6 +261,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.*/
@@ -175,6 +279,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);
 };
 
 /*
@@ -365,8 +477,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);
@@ -383,7 +497,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,
@@ -395,8 +509,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);
 }
 
 
@@ -412,7 +527,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;
@@ -461,8 +576,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;
@@ -499,7 +613,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
@@ -510,8 +627,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)
@@ -536,6 +653,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)
@@ -599,22 +727,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,
@@ -622,22 +771,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
@@ -689,22 +860,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;
@@ -712,11 +880,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,
@@ -731,11 +910,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);
 
        /*
@@ -760,17 +945,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.
@@ -882,6 +1061,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;
@@ -890,7 +1115,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);
@@ -903,13 +1128,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);
 }
 
@@ -925,10 +1203,20 @@ 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;
+       const struct pcie_cfg_data *data;
        struct brcm_pcie *pcie;
        int ret;
 
@@ -936,9 +1224,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))
@@ -958,11 +1256,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);
@@ -983,18 +1299,20 @@ fail:
        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);