diff options
Diffstat (limited to 'drivers/pci/controller/pcie-brcmstb.c')
| -rw-r--r-- | drivers/pci/controller/pcie-brcmstb.c | 386 | 
1 files changed, 341 insertions, 45 deletions
| diff --git a/drivers/pci/controller/pcie-brcmstb.c b/drivers/pci/controller/pcie-brcmstb.c index 1fc7bd49a7ad..375c0c40bbf8 100644 --- a/drivers/pci/controller/pcie-brcmstb.c +++ b/drivers/pci/controller/pcie-brcmstb.c @@ -24,6 +24,7 @@  #include <linux/pci.h>  #include <linux/pci-ecam.h>  #include <linux/printk.h> +#include <linux/regulator/consumer.h>  #include <linux/reset.h>  #include <linux/sizes.h>  #include <linux/slab.h> @@ -118,6 +119,7 @@  #define PCIE_MISC_HARD_PCIE_HARD_DEBUG					0x4204  #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_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK		0x00800000  #define PCIE_INTR2_CPU_BASE		0x4300 @@ -144,6 +146,9 @@  #define BRCM_INT_PCI_MSI_NR		32  #define BRCM_INT_PCI_MSI_LEGACY_NR	8  #define BRCM_INT_PCI_MSI_SHIFT		0 +#define BRCM_INT_PCI_MSI_MASK		GENMASK(BRCM_INT_PCI_MSI_NR - 1, 0) +#define BRCM_INT_PCI_MSI_LEGACY_MASK	GENMASK(31, \ +						32 - BRCM_INT_PCI_MSI_LEGACY_NR)  /* MSI target addresses */  #define BRCM_MSI_TARGET_ADDR_LT_4GB	0x0fffffffcULL @@ -191,6 +196,8 @@ static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie,  static inline void brcm_pcie_perst_set_4908(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); +static int brcm_pcie_linkup(struct brcm_pcie *pcie); +static int brcm_pcie_add_bus(struct pci_bus *bus);  enum {  	RGR1_SW_INIT_1, @@ -205,6 +212,8 @@ enum {  enum pcie_type {  	GENERIC, +	BCM7425, +	BCM7435,  	BCM4908,  	BCM7278,  	BCM2711, @@ -223,6 +232,12 @@ static const int pcie_offsets[] = {  	[EXT_CFG_DATA]   = 0x9004,  }; +static const int pcie_offsets_bmips_7425[] = { +	[RGR1_SW_INIT_1] = 0x8010, +	[EXT_CFG_INDEX]  = 0x8300, +	[EXT_CFG_DATA]   = 0x8304, +}; +  static const struct pcie_cfg_data generic_cfg = {  	.offsets	= pcie_offsets,  	.type		= GENERIC, @@ -230,6 +245,20 @@ static const struct pcie_cfg_data generic_cfg = {  	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,  }; +static const struct pcie_cfg_data bcm7425_cfg = { +	.offsets	= pcie_offsets_bmips_7425, +	.type		= BCM7425, +	.perst_set	= brcm_pcie_perst_set_generic, +	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, +}; + +static const struct pcie_cfg_data bcm7435_cfg = { +	.offsets	= pcie_offsets, +	.type		= BCM7435, +	.perst_set	= brcm_pcie_perst_set_generic, +	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic, +}; +  static const struct pcie_cfg_data bcm4908_cfg = {  	.offsets	= pcie_offsets,  	.type		= BCM4908, @@ -257,6 +286,14 @@ static const struct pcie_cfg_data bcm2711_cfg = {  	.bridge_sw_init_set = brcm_pcie_bridge_sw_init_set_generic,  }; +struct subdev_regulators { +	unsigned int num_supplies; +	struct regulator_bulk_data supplies[]; +}; + +static int pci_subdev_regulators_add_bus(struct pci_bus *bus); +static void pci_subdev_regulators_remove_bus(struct pci_bus *bus); +  struct brcm_msi {  	struct device		*dev;  	void __iomem		*base; @@ -266,8 +303,7 @@ struct brcm_msi {  	struct mutex		lock; /* guards the alloc/free operations */  	u64			target_addr;  	int			irq; -	/* used indicates which MSI interrupts have been alloc'd */ -	unsigned long		used; +	DECLARE_BITMAP(used, BRCM_INT_PCI_MSI_NR);  	bool			legacy;  	/* Some chips have MSIs in bits [31..24] of a shared register. */  	int			legacy_shift; @@ -295,8 +331,16 @@ struct brcm_pcie {  	u32			hw_rev;  	void			(*perst_set)(struct brcm_pcie *pcie, u32 val);  	void			(*bridge_sw_init_set)(struct brcm_pcie *pcie, u32 val); +	bool			refusal_mode; +	struct subdev_regulators *sr; +	bool			ep_wakeup_capable;  }; +static inline bool is_bmips(const struct brcm_pcie *pcie) +{ +	return pcie->type == BCM7435 || pcie->type == BCM7425; +} +  /*   * This is to convert the size of the inbound "BAR" region to the   * non-linear values of PCIE_X_MISC_RC_BAR[123]_CONFIG_LO.SIZE @@ -406,6 +450,99 @@ static int brcm_pcie_set_ssc(struct brcm_pcie *pcie)  	return ssc && pll ? 0 : -EIO;  } +static void *alloc_subdev_regulators(struct device *dev) +{ +	static const char * const supplies[] = { +		"vpcie3v3", +		"vpcie3v3aux", +		"vpcie12v", +	}; +	const size_t size = sizeof(struct subdev_regulators) +		+ sizeof(struct regulator_bulk_data) * ARRAY_SIZE(supplies); +	struct subdev_regulators *sr; +	int i; + +	sr = devm_kzalloc(dev, size, GFP_KERNEL); +	if (sr) { +		sr->num_supplies = ARRAY_SIZE(supplies); +		for (i = 0; i < ARRAY_SIZE(supplies); i++) +			sr->supplies[i].supply = supplies[i]; +	} + +	return sr; +} + +static int pci_subdev_regulators_add_bus(struct pci_bus *bus) +{ +	struct device *dev = &bus->dev; +	struct subdev_regulators *sr; +	int ret; + +	if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent)) +		return 0; + +	if (dev->driver_data) +		dev_err(dev, "dev.driver_data unexpectedly non-NULL\n"); + +	sr = alloc_subdev_regulators(dev); +	if (!sr) +		return -ENOMEM; + +	dev->driver_data = sr; +	ret = regulator_bulk_get(dev, sr->num_supplies, sr->supplies); +	if (ret) +		return ret; + +	ret = regulator_bulk_enable(sr->num_supplies, sr->supplies); +	if (ret) { +		dev_err(dev, "failed to enable regulators for downstream device\n"); +		return ret; +	} + +	return 0; +} + +static int brcm_pcie_add_bus(struct pci_bus *bus) +{ +	struct device *dev = &bus->dev; +	struct brcm_pcie *pcie = (struct brcm_pcie *) bus->sysdata; +	int ret; + +	if (!dev->of_node || !bus->parent || !pci_is_root_bus(bus->parent)) +		return 0; + +	ret = pci_subdev_regulators_add_bus(bus); +	if (ret) +		return ret; + +	/* Grab the regulators for suspend/resume */ +	pcie->sr = bus->dev.driver_data; + +	/* +	 * If we have failed linkup there is no point to return an error as +	 * currently it will cause a WARNING() from pci_alloc_child_bus(). +	 * We return 0 and turn on the "refusal_mode" so that any further +	 * accesses to the pci_dev just get 0xffffffff +	 */ +	if (brcm_pcie_linkup(pcie) != 0) +		pcie->refusal_mode = true; + +	return 0; +} + +static void pci_subdev_regulators_remove_bus(struct pci_bus *bus) +{ +	struct device *dev = &bus->dev; +	struct subdev_regulators *sr = dev->driver_data; + +	if (!sr || !bus->parent || !pci_is_root_bus(bus->parent)) +		return; + +	if (regulator_bulk_disable(sr->num_supplies, sr->supplies)) +		dev_err(dev, "failed to disable regulators for downstream device\n"); +	dev->driver_data = NULL; +} +  /* Limits operation to a specific generation (1, 2, or 3) */  static void brcm_pcie_set_gen(struct brcm_pcie *pcie, int gen)  { @@ -443,6 +580,9 @@ static void brcm_pcie_set_outbound_win(struct brcm_pcie *pcie,  			  PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_LIMIT_MASK);  	writel(tmp, pcie->base + PCIE_MEM_WIN0_BASE_LIMIT(win)); +	if (is_bmips(pcie)) +		return; +  	/* Write the cpu & limit addr upper bits */  	high_addr_shift =  		HWEIGHT32(PCIE_MISC_CPU_2_PCIE_MEM_WIN0_BASE_LIMIT_BASE_MASK); @@ -534,7 +674,7 @@ static int brcm_msi_alloc(struct brcm_msi *msi)  	int hwirq;  	mutex_lock(&msi->lock); -	hwirq = bitmap_find_free_region(&msi->used, msi->nr, 0); +	hwirq = bitmap_find_free_region(msi->used, msi->nr, 0);  	mutex_unlock(&msi->lock);  	return hwirq; @@ -543,7 +683,7 @@ static int brcm_msi_alloc(struct brcm_msi *msi)  static void brcm_msi_free(struct brcm_msi *msi, unsigned long hwirq)  {  	mutex_lock(&msi->lock); -	bitmap_release_region(&msi->used, hwirq, 0); +	bitmap_release_region(msi->used, hwirq, 0);  	mutex_unlock(&msi->lock);  } @@ -619,7 +759,8 @@ static void brcm_msi_remove(struct brcm_pcie *pcie)  static void brcm_msi_set_regs(struct brcm_msi *msi)  { -	u32 val = __GENMASK(31, msi->legacy_shift); +	u32 val = msi->legacy ? BRCM_INT_PCI_MSI_LEGACY_MASK : +				BRCM_INT_PCI_MSI_MASK;  	writel(val, msi->intr_base + MSI_INT_MASK_CLR);  	writel(val, msi->intr_base + MSI_INT_CLR); @@ -661,6 +802,12 @@ static int brcm_pcie_enable_msi(struct brcm_pcie *pcie)  	msi->irq = irq;  	msi->legacy = pcie->hw_rev < BRCM_PCIE_HW_REV_33; +	/* +	 * Sanity check to make sure that the 'used' bitmap in struct brcm_msi +	 * is large enough. +	 */ +	BUILD_BUG_ON(BRCM_INT_PCI_MSI_LEGACY_NR > BRCM_INT_PCI_MSI_NR); +  	if (msi->legacy) {  		msi->intr_base = msi->base + PCIE_INTR2_CPU_BASE;  		msi->nr = BRCM_INT_PCI_MSI_LEGACY_NR; @@ -711,6 +858,18 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,  	/* Accesses to the RC go right to the RC registers if slot==0 */  	if (pci_is_root_bus(bus))  		return PCI_SLOT(devfn) ? NULL : base + where; +	if (pcie->refusal_mode) { +		/* +		 * At this point we do not have link.  There will be a CPU +		 * abort -- a quirk with this controller --if Linux tries +		 * to read any config-space registers besides those +		 * targeting the host bridge.  To prevent this we hijack +		 * the address to point to a safe access that will return +		 * 0xffffffff. +		 */ +		writel(0xffffffff, base + PCIE_MISC_RC_BAR2_CONFIG_HI); +		return base + PCIE_MISC_RC_BAR2_CONFIG_HI + (where & 0x3); +	}  	/* For devices, write to the config space index register */  	idx = PCIE_ECAM_OFFSET(bus->number, devfn, 0); @@ -718,10 +877,35 @@ static void __iomem *brcm_pcie_map_conf(struct pci_bus *bus, unsigned int devfn,  	return base + PCIE_EXT_CFG_DATA + where;  } +static void __iomem *brcm_pcie_map_conf32(struct pci_bus *bus, unsigned int devfn, +					 int where) +{ +	struct brcm_pcie *pcie = bus->sysdata; +	void __iomem *base = pcie->base; +	int idx; + +	/* Accesses to the RC go right to the RC registers if slot==0 */ +	if (pci_is_root_bus(bus)) +		return PCI_SLOT(devfn) ? NULL : base + (where & ~0x3); + +	/* For devices, write to the config space index register */ +	idx = PCIE_ECAM_OFFSET(bus->number, devfn, (where & ~3)); +	writel(idx, base + IDX_ADDR(pcie)); +	return base + DATA_ADDR(pcie); +} +  static struct pci_ops brcm_pcie_ops = {  	.map_bus = brcm_pcie_map_conf,  	.read = pci_generic_config_read,  	.write = pci_generic_config_write, +	.add_bus = brcm_pcie_add_bus, +	.remove_bus = pci_subdev_regulators_remove_bus, +}; + +static struct pci_ops brcm_pcie_ops32 = { +	.map_bus = brcm_pcie_map_conf32, +	.read = pci_generic_config_read32, +	.write = pci_generic_config_write32,  };  static inline void brcm_pcie_bridge_sw_init_set_generic(struct brcm_pcie *pcie, u32 val) @@ -863,16 +1047,9 @@ static inline int brcm_pcie_get_rc_bar2_size_and_offset(struct brcm_pcie *pcie,  static int brcm_pcie_setup(struct brcm_pcie *pcie)  { -	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);  	u64 rc_bar2_offset, rc_bar2_size;  	void __iomem *base = pcie->base; -	struct device *dev = pcie->dev; -	struct resource_entry *entry; -	bool ssc_good = false; -	struct resource *res; -	int num_out_wins = 0; -	u16 nlw, cls, lnksta; -	int i, ret, memc; +	int ret, memc;  	u32 tmp, burst, aspm_support;  	/* Reset the bridge */ @@ -883,7 +1060,10 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)  	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; +	if (is_bmips(pcie)) +		tmp &= ~PCIE_BMIPS_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK; +	else +		tmp &= ~PCIE_MISC_HARD_PCIE_HARD_DEBUG_SERDES_IDDQ_MASK;  	writel(tmp, base + PCIE_MISC_HARD_PCIE_HARD_DEBUG);  	/* Wait for SerDes to be stable */  	usleep_range(100, 200); @@ -893,8 +1073,10 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)  	 * 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 */ +	if (is_bmips(pcie)) +		burst = 0x1; /* 256 bytes */ +	else if (pcie->type == BCM2711) +		burst = 0x0; /* 128 bytes */  	else if (pcie->type == BCM7278)  		burst = 0x3; /* 512 bytes */  	else @@ -957,6 +1139,40 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)  	if (pcie->gen)  		brcm_pcie_set_gen(pcie, pcie->gen); +	/* Don't advertise L0s capability if 'aspm-no-l0s' */ +	aspm_support = PCIE_LINK_STATE_L1; +	if (!of_property_read_bool(pcie->np, "aspm-no-l0s")) +		aspm_support |= PCIE_LINK_STATE_L0S; +	tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); +	u32p_replace_bits(&tmp, aspm_support, +		PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); +	writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); + +	/* +	 * For config space accesses on the RC, show the right class for +	 * a PCIe-PCIe bridge (the default setting is to be EP mode). +	 */ +	tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3); +	u32p_replace_bits(&tmp, 0x060400, +			  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK); +	writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3); + +	return 0; +} + +static int brcm_pcie_linkup(struct brcm_pcie *pcie) +{ +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); +	struct device *dev = pcie->dev; +	void __iomem *base = pcie->base; +	struct resource_entry *entry; +	struct resource *res; +	int num_out_wins = 0; +	u16 nlw, cls, lnksta; +	bool ssc_good = false; +	u32 tmp; +	int ret, i; +  	/* Unassert the fundamental reset */  	pcie->perst_set(pcie, 0); @@ -988,30 +1204,25 @@ static int brcm_pcie_setup(struct brcm_pcie *pcie)  			return -EINVAL;  		} +		if (is_bmips(pcie)) { +			u64 start = res->start; +			unsigned int j, nwins = resource_size(res) / SZ_128M; + +			/* bmips PCIe outbound windows have a 128MB max size */ +			if (nwins > BRCM_NUM_PCIE_OUT_WINS) +				nwins = BRCM_NUM_PCIE_OUT_WINS; +			for (j = 0; j < nwins; j++, start += SZ_128M) +				brcm_pcie_set_outbound_win(pcie, j, start, +							   start - entry->offset, +							   SZ_128M); +			break; +		}  		brcm_pcie_set_outbound_win(pcie, num_out_wins, res->start,  					   res->start - entry->offset,  					   resource_size(res));  		num_out_wins++;  	} -	/* Don't advertise L0s capability if 'aspm-no-l0s' */ -	aspm_support = PCIE_LINK_STATE_L1; -	if (!of_property_read_bool(pcie->np, "aspm-no-l0s")) -		aspm_support |= PCIE_LINK_STATE_L0S; -	tmp = readl(base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); -	u32p_replace_bits(&tmp, aspm_support, -		PCIE_RC_CFG_PRIV1_LINK_CAPABILITY_ASPM_SUPPORT_MASK); -	writel(tmp, base + PCIE_RC_CFG_PRIV1_LINK_CAPABILITY); - -	/* -	 * For config space accesses on the RC, show the right class for -	 * a PCIe-PCIe bridge (the default setting is to be EP mode). -	 */ -	tmp = readl(base + PCIE_RC_CFG_PRIV1_ID_VAL3); -	u32p_replace_bits(&tmp, 0x060400, -			  PCIE_RC_CFG_PRIV1_ID_VAL3_CLASS_CODE_MASK); -	writel(tmp, base + PCIE_RC_CFG_PRIV1_ID_VAL3); -  	if (pcie->ssc) {  		ret = brcm_pcie_set_ssc(pcie);  		if (ret == 0) @@ -1140,17 +1351,60 @@ static void brcm_pcie_turn_off(struct brcm_pcie *pcie)  	pcie->bridge_sw_init_set(pcie, 1);  } +static int pci_dev_may_wakeup(struct pci_dev *dev, void *data) +{ +	bool *ret = data; + +	if (device_may_wakeup(&dev->dev)) { +		*ret = true; +		dev_info(&dev->dev, "disable cancelled for wake-up device\n"); +	} +	return (int) *ret; +} +  static int brcm_pcie_suspend(struct device *dev)  {  	struct brcm_pcie *pcie = dev_get_drvdata(dev); +	struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie);  	int ret;  	brcm_pcie_turn_off(pcie); -	ret = brcm_phy_stop(pcie); -	reset_control_rearm(pcie->rescal); +	/* +	 * If brcm_phy_stop() returns an error, just dev_err(). If we +	 * return the error it will cause the suspend to fail and this is a +	 * forgivable offense that will probably be erased on resume. +	 */ +	if (brcm_phy_stop(pcie)) +		dev_err(dev, "Could not stop phy for suspend\n"); + +	ret = reset_control_rearm(pcie->rescal); +	if (ret) { +		dev_err(dev, "Could not rearm rescal reset\n"); +		return ret; +	} + +	if (pcie->sr) { +		/* +		 * Now turn off the regulators, but if at least one +		 * downstream device is enabled as a wake-up source, do not +		 * turn off regulators. +		 */ +		pcie->ep_wakeup_capable = false; +		pci_walk_bus(bridge->bus, pci_dev_may_wakeup, +			     &pcie->ep_wakeup_capable); +		if (!pcie->ep_wakeup_capable) { +			ret = regulator_bulk_disable(pcie->sr->num_supplies, +						     pcie->sr->supplies); +			if (ret) { +				dev_err(dev, "Could not turn off regulators\n"); +				reset_control_reset(pcie->rescal); +				return ret; +			} +		} +	}  	clk_disable_unprepare(pcie->clk); -	return ret; +	return 0;  }  static int brcm_pcie_resume(struct device *dev) @@ -1161,11 +1415,32 @@ static int brcm_pcie_resume(struct device *dev)  	int ret;  	base = pcie->base; -	clk_prepare_enable(pcie->clk); +	ret = clk_prepare_enable(pcie->clk); +	if (ret) +		return ret; + +	if (pcie->sr) { +		if (pcie->ep_wakeup_capable) { +			/* +			 * We are resuming from a suspend.  In the suspend we +			 * did not disable the power supplies, so there is +			 * no need to enable them (and falsely increase their +			 * usage count). +			 */ +			pcie->ep_wakeup_capable = false; +		} else { +			ret = regulator_bulk_enable(pcie->sr->num_supplies, +						    pcie->sr->supplies); +			if (ret) { +				dev_err(dev, "Could not turn on regulators\n"); +				goto err_disable_clk; +			} +		} +	}  	ret = reset_control_reset(pcie->rescal);  	if (ret) -		goto err_disable_clk; +		goto err_regulator;  	ret = brcm_phy_start(pcie);  	if (ret) @@ -1186,6 +1461,10 @@ static int brcm_pcie_resume(struct device *dev)  	if (ret)  		goto err_reset; +	ret = brcm_pcie_linkup(pcie); +	if (ret) +		goto err_reset; +  	if (pcie->msi)  		brcm_msi_set_regs(pcie->msi); @@ -1193,6 +1472,9 @@ static int brcm_pcie_resume(struct device *dev)  err_reset:  	reset_control_rearm(pcie->rescal); +err_regulator: +	if (pcie->sr) +		regulator_bulk_disable(pcie->sr->num_supplies, pcie->sr->supplies);  err_disable_clk:  	clk_disable_unprepare(pcie->clk);  	return ret; @@ -1202,8 +1484,10 @@ static void __brcm_pcie_remove(struct brcm_pcie *pcie)  {  	brcm_msi_remove(pcie);  	brcm_pcie_turn_off(pcie); -	brcm_phy_stop(pcie); -	reset_control_rearm(pcie->rescal); +	if (brcm_phy_stop(pcie)) +		dev_err(pcie->dev, "Could not stop phy\n"); +	if (reset_control_rearm(pcie->rescal)) +		dev_err(pcie->dev, "Could not rearm rescal reset\n");  	clk_disable_unprepare(pcie->clk);  } @@ -1226,6 +1510,8 @@ static const struct of_device_id brcm_pcie_match[] = {  	{ .compatible = "brcm,bcm7278-pcie", .data = &bcm7278_cfg },  	{ .compatible = "brcm,bcm7216-pcie", .data = &bcm7278_cfg },  	{ .compatible = "brcm,bcm7445-pcie", .data = &generic_cfg }, +	{ .compatible = "brcm,bcm7435-pcie", .data = &bcm7435_cfg }, +	{ .compatible = "brcm,bcm7425-pcie", .data = &bcm7425_cfg },  	{},  }; @@ -1315,12 +1601,22 @@ static int brcm_pcie_probe(struct platform_device *pdev)  		}  	} -	bridge->ops = &brcm_pcie_ops; +	bridge->ops = pcie->type == BCM7425 ? &brcm_pcie_ops32 : &brcm_pcie_ops;  	bridge->sysdata = pcie;  	platform_set_drvdata(pdev, pcie); -	return pci_host_probe(bridge); +	ret = pci_host_probe(bridge); +	if (!ret && !brcm_pcie_link_up(pcie)) +		ret = -ENODEV; + +	if (ret) { +		brcm_pcie_remove(pdev); +		return ret; +	} + +	return 0; +  fail:  	__brcm_pcie_remove(pcie);  	return ret; @@ -1329,8 +1625,8 @@ fail:  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, +	.suspend_noirq = brcm_pcie_suspend, +	.resume_noirq = brcm_pcie_resume,  };  static struct platform_driver brcm_pcie_driver = { |