Commit 352948c4 authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/host-iproc' into next

* pci/host-iproc:
  PCI: iproc: Clean up whitespace
  PCI: iproc: Rename PCI_EXP_CAP to IPROC_PCI_EXP_CAP
  PCI: iproc: Add 500ms delay during device shutdown
  PCI: iproc: Work around Stingray CRS defects
  PCI: iproc: Factor out memory-mapped config access address calculation
  PCI: iproc: Remove unused struct iproc_pcie *pcie
parents 8a21881a ef685b34
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -317,7 +317,6 @@ static void iproc_msi_handler(struct irq_desc *desc)
	struct irq_chip *chip = irq_desc_get_chip(desc);
	struct iproc_msi_grp *grp;
	struct iproc_msi *msi;
	struct iproc_pcie *pcie;
	u32 eq, head, tail, nr_events;
	unsigned long hwirq;
	int virq;
@@ -326,7 +325,6 @@ static void iproc_msi_handler(struct irq_desc *desc)

	grp = irq_desc_get_handler_data(desc);
	msi = grp->msi;
	pcie = msi->pcie;
	eq = grp->eq;

	/*
+8 −0
Original line number Diff line number Diff line
@@ -134,6 +134,13 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
	return iproc_pcie_remove(pcie);
}

static void iproc_pcie_pltfm_shutdown(struct platform_device *pdev)
{
	struct iproc_pcie *pcie = platform_get_drvdata(pdev);

	iproc_pcie_shutdown(pcie);
}

static struct platform_driver iproc_pcie_pltfm_driver = {
	.driver = {
		.name = "iproc-pcie",
@@ -141,6 +148,7 @@ static struct platform_driver iproc_pcie_pltfm_driver = {
	},
	.probe = iproc_pcie_pltfm_probe,
	.remove = iproc_pcie_pltfm_remove,
	.shutdown = iproc_pcie_pltfm_shutdown,
};
module_platform_driver(iproc_pcie_pltfm_driver);

+250 −150
Original line number Diff line number Diff line
@@ -68,6 +68,9 @@
#define APB_ERR_EN_SHIFT		0
#define APB_ERR_EN			BIT(APB_ERR_EN_SHIFT)

#define CFG_RETRY_STATUS		0xffff0001
#define CFG_RETRY_STATUS_TIMEOUT_US	500000 /* 500 milliseconds */

/* derive the enum index of the outbound/inbound mapping registers */
#define MAP_REG(base_reg, index)	((base_reg) + (index) * 2)

@@ -90,7 +93,7 @@
#define IMAP_VALID_SHIFT		0
#define IMAP_VALID			BIT(IMAP_VALID_SHIFT)

#define PCI_EXP_CAP			0xac
#define IPROC_PCI_EXP_CAP		0xac

#define IPROC_PCIE_REG_INVALID		0xffff

@@ -448,18 +451,112 @@ static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus,
	}
}

static void __iomem *iproc_pcie_map_ep_cfg_reg(struct iproc_pcie *pcie,
					       unsigned int busno,
					       unsigned int slot,
					       unsigned int fn,
					       int where)
{
	u16 offset;
	u32 val;

	/* EP device access */
	val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
		(slot << CFG_ADDR_DEV_NUM_SHIFT) |
		(fn << CFG_ADDR_FUNC_NUM_SHIFT) |
		(where & CFG_ADDR_REG_NUM_MASK) |
		(1 & CFG_ADDR_CFG_TYPE_MASK);

	iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
	offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);

	if (iproc_pcie_reg_is_invalid(offset))
		return NULL;

	return (pcie->base + offset);
}

static unsigned int iproc_pcie_cfg_retry(void __iomem *cfg_data_p)
{
	int timeout = CFG_RETRY_STATUS_TIMEOUT_US;
	unsigned int data;

	/*
	 * As per PCIe spec r3.1, sec 2.3.2, CRS Software Visibility only
	 * affects config reads of the Vendor ID.  For config writes or any
	 * other config reads, the Root may automatically reissue the
	 * configuration request again as a new request.
	 *
	 * For config reads, this hardware returns CFG_RETRY_STATUS data
	 * when it receives a CRS completion, regardless of the address of
	 * the read or the CRS Software Visibility Enable bit.  As a
	 * partial workaround for this, we retry in software any read that
	 * returns CFG_RETRY_STATUS.
	 *
	 * Note that a non-Vendor ID config register may have a value of
	 * CFG_RETRY_STATUS.  If we read that, we can't distinguish it from
	 * a CRS completion, so we will incorrectly retry the read and
	 * eventually return the wrong data (0xffffffff).
	 */
	data = readl(cfg_data_p);
	while (data == CFG_RETRY_STATUS && timeout--) {
		udelay(1);
		data = readl(cfg_data_p);
	}

	if (data == CFG_RETRY_STATUS)
		data = 0xffffffff;

	return data;
}

static int iproc_pcie_config_read(struct pci_bus *bus, unsigned int devfn,
				  int where, int size, u32 *val)
{
	struct iproc_pcie *pcie = iproc_data(bus);
	unsigned int slot = PCI_SLOT(devfn);
	unsigned int fn = PCI_FUNC(devfn);
	unsigned int busno = bus->number;
	void __iomem *cfg_data_p;
	unsigned int data;
	int ret;

	/* root complex access */
	if (busno == 0) {
		ret = pci_generic_config_read32(bus, devfn, where, size, val);
		if (ret != PCIBIOS_SUCCESSFUL)
			return ret;

		/* Don't advertise CRS SV support */
		if ((where & ~0x3) == IPROC_PCI_EXP_CAP + PCI_EXP_RTCTL)
			*val &= ~(PCI_EXP_RTCAP_CRSVIS << 16);
		return PCIBIOS_SUCCESSFUL;
	}

	cfg_data_p = iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);

	if (!cfg_data_p)
		return PCIBIOS_DEVICE_NOT_FOUND;

	data = iproc_pcie_cfg_retry(cfg_data_p);

	*val = data;
	if (size <= 2)
		*val = (data >> (8 * (where & 3))) & ((1 << (size * 8)) - 1);

	return PCIBIOS_SUCCESSFUL;
}

/**
 * Note access to the configuration registers are protected at the higher layer
 * by 'pci_lock' in drivers/pci/access.c
 */
static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie,
					    int busno,
					    unsigned int devfn,
					    int busno, unsigned int devfn,
					    int where)
{
	unsigned slot = PCI_SLOT(devfn);
	unsigned fn = PCI_FUNC(devfn);
	u32 val;
	u16 offset;

	/* root complex access */
@@ -484,18 +581,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie,
		if (slot > 0)
			return NULL;

	/* EP device access */
	val = (busno << CFG_ADDR_BUS_NUM_SHIFT) |
		(slot << CFG_ADDR_DEV_NUM_SHIFT) |
		(fn << CFG_ADDR_FUNC_NUM_SHIFT) |
		(where & CFG_ADDR_REG_NUM_MASK) |
		(1 & CFG_ADDR_CFG_TYPE_MASK);
	iproc_pcie_write_reg(pcie, IPROC_PCIE_CFG_ADDR, val);
	offset = iproc_pcie_reg_offset(pcie, IPROC_PCIE_CFG_DATA);
	if (iproc_pcie_reg_is_invalid(offset))
		return NULL;
	else
		return (pcie->base + offset);
	return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);
}

static void __iomem *iproc_pcie_bus_map_cfg_bus(struct pci_bus *bus,
@@ -554,8 +640,12 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
				    int where, int size, u32 *val)
{
	int ret;
	struct iproc_pcie *pcie = iproc_data(bus);

	iproc_pcie_apb_err_disable(bus, true);
	if (pcie->type == IPROC_PCIE_PAXB_V2)
		ret = iproc_pcie_config_read(bus, devfn, where, size, val);
	else
		ret = pci_generic_config_read32(bus, devfn, where, size, val);
	iproc_pcie_apb_err_disable(bus, false);

@@ -580,7 +670,7 @@ static struct pci_ops iproc_pcie_ops = {
	.write = iproc_pcie_config_write32,
};

static void iproc_pcie_reset(struct iproc_pcie *pcie)
static void iproc_pcie_perst_ctrl(struct iproc_pcie *pcie, bool assert)
{
	u32 val;

@@ -592,26 +682,33 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
	if (pcie->ep_is_internal)
		return;

	/*
	 * Select perst_b signal as reset source. Put the device into reset,
	 * and then bring it out of reset
	 */
	if (assert) {
		val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
		val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
			~RC_PCIE_RST_OUTPUT;
		iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
		udelay(250);

	} else {
		val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
		val |= RC_PCIE_RST_OUTPUT;
		iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
		msleep(100);
	}
}

int iproc_pcie_shutdown(struct iproc_pcie *pcie)
{
	iproc_pcie_perst_ctrl(pcie, true);
	msleep(500);

	return 0;
}
EXPORT_SYMBOL_GPL(iproc_pcie_shutdown);

static int iproc_pcie_check_link(struct iproc_pcie *pcie)
{
	struct device *dev = pcie->dev;
	u32 hdr_type, link_ctrl, link_status, class, val;
	u16 pos = PCI_EXP_CAP;
	bool link_is_active = false;

	/*
@@ -646,7 +743,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie)
				     4, class);

	/* check link status to see if link is active */
	iproc_pci_raw_config_read32(pcie, 0, pos + PCI_EXP_LNKSTA,
	iproc_pci_raw_config_read32(pcie, 0, IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA,
				    2, &link_status);
	if (link_status & PCI_EXP_LNKSTA_NLW)
		link_is_active = true;
@@ -657,19 +754,19 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie)
#define PCI_TARGET_LINK_SPEED_GEN2	0x2
#define PCI_TARGET_LINK_SPEED_GEN1	0x1
		iproc_pci_raw_config_read32(pcie, 0,
					  pos + PCI_EXP_LNKCTL2, 4,
					  &link_ctrl);
					    IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2,
					    4, &link_ctrl);
		if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) ==
		    PCI_TARGET_LINK_SPEED_GEN2) {
			link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK;
			link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1;
			iproc_pci_raw_config_write32(pcie, 0,
						pos + PCI_EXP_LNKCTL2,
					IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2,
					4, link_ctrl);
			msleep(100);

			iproc_pci_raw_config_read32(pcie, 0,
						pos + PCI_EXP_LNKSTA,
					IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA,
					2, &link_status);
			if (link_status & PCI_EXP_LNKSTA_NLW)
				link_is_active = true;
@@ -1223,6 +1320,8 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie)
		pcie->ib.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
		pcie->ib_map = paxb_v2_ib_map;
		pcie->need_msi_steer = true;
		dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n",
			 CFG_RETRY_STATUS);
		break;
	case IPROC_PCIE_PAXC:
		regs = iproc_pcie_reg_paxc;
@@ -1286,7 +1385,8 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
		goto err_exit_phy;
	}

	iproc_pcie_reset(pcie);
	iproc_pcie_perst_ctrl(pcie, true);
	iproc_pcie_perst_ctrl(pcie, false);

	if (pcie->need_ob_cfg) {
		ret = iproc_pcie_map_ranges(pcie, res);
+1 −0
Original line number Diff line number Diff line
@@ -110,6 +110,7 @@ struct iproc_pcie {

int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
int iproc_pcie_remove(struct iproc_pcie *pcie);
int iproc_pcie_shutdown(struct iproc_pcie *pcie);

#ifdef CONFIG_PCIE_IPROC_MSI
int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);