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

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
...@@ -317,7 +317,6 @@ static void iproc_msi_handler(struct irq_desc *desc) ...@@ -317,7 +317,6 @@ static void iproc_msi_handler(struct irq_desc *desc)
struct irq_chip *chip = irq_desc_get_chip(desc); struct irq_chip *chip = irq_desc_get_chip(desc);
struct iproc_msi_grp *grp; struct iproc_msi_grp *grp;
struct iproc_msi *msi; struct iproc_msi *msi;
struct iproc_pcie *pcie;
u32 eq, head, tail, nr_events; u32 eq, head, tail, nr_events;
unsigned long hwirq; unsigned long hwirq;
int virq; int virq;
...@@ -326,7 +325,6 @@ static void iproc_msi_handler(struct irq_desc *desc) ...@@ -326,7 +325,6 @@ static void iproc_msi_handler(struct irq_desc *desc)
grp = irq_desc_get_handler_data(desc); grp = irq_desc_get_handler_data(desc);
msi = grp->msi; msi = grp->msi;
pcie = msi->pcie;
eq = grp->eq; eq = grp->eq;
/* /*
......
...@@ -134,6 +134,13 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev) ...@@ -134,6 +134,13 @@ static int iproc_pcie_pltfm_remove(struct platform_device *pdev)
return iproc_pcie_remove(pcie); 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 = { static struct platform_driver iproc_pcie_pltfm_driver = {
.driver = { .driver = {
.name = "iproc-pcie", .name = "iproc-pcie",
...@@ -141,6 +148,7 @@ static struct platform_driver iproc_pcie_pltfm_driver = { ...@@ -141,6 +148,7 @@ static struct platform_driver iproc_pcie_pltfm_driver = {
}, },
.probe = iproc_pcie_pltfm_probe, .probe = iproc_pcie_pltfm_probe,
.remove = iproc_pcie_pltfm_remove, .remove = iproc_pcie_pltfm_remove,
.shutdown = iproc_pcie_pltfm_shutdown,
}; };
module_platform_driver(iproc_pcie_pltfm_driver); module_platform_driver(iproc_pcie_pltfm_driver);
......
...@@ -68,6 +68,9 @@ ...@@ -68,6 +68,9 @@
#define APB_ERR_EN_SHIFT 0 #define APB_ERR_EN_SHIFT 0
#define APB_ERR_EN BIT(APB_ERR_EN_SHIFT) #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 */ /* derive the enum index of the outbound/inbound mapping registers */
#define MAP_REG(base_reg, index) ((base_reg) + (index) * 2) #define MAP_REG(base_reg, index) ((base_reg) + (index) * 2)
...@@ -90,7 +93,7 @@ ...@@ -90,7 +93,7 @@
#define IMAP_VALID_SHIFT 0 #define IMAP_VALID_SHIFT 0
#define IMAP_VALID BIT(IMAP_VALID_SHIFT) #define IMAP_VALID BIT(IMAP_VALID_SHIFT)
#define PCI_EXP_CAP 0xac #define IPROC_PCI_EXP_CAP 0xac
#define IPROC_PCIE_REG_INVALID 0xffff #define IPROC_PCIE_REG_INVALID 0xffff
...@@ -448,18 +451,112 @@ static inline void iproc_pcie_apb_err_disable(struct pci_bus *bus, ...@@ -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 * Note access to the configuration registers are protected at the higher layer
* by 'pci_lock' in drivers/pci/access.c * by 'pci_lock' in drivers/pci/access.c
*/ */
static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie,
int busno, int busno, unsigned int devfn,
unsigned int devfn,
int where) int where)
{ {
unsigned slot = PCI_SLOT(devfn); unsigned slot = PCI_SLOT(devfn);
unsigned fn = PCI_FUNC(devfn); unsigned fn = PCI_FUNC(devfn);
u32 val;
u16 offset; u16 offset;
/* root complex access */ /* root complex access */
...@@ -484,18 +581,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie, ...@@ -484,18 +581,7 @@ static void __iomem *iproc_pcie_map_cfg_bus(struct iproc_pcie *pcie,
if (slot > 0) if (slot > 0)
return NULL; return NULL;
/* EP device access */ return iproc_pcie_map_ep_cfg_reg(pcie, busno, slot, fn, where);
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);
} }
static void __iomem *iproc_pcie_bus_map_cfg_bus(struct pci_bus *bus, 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, ...@@ -554,8 +640,12 @@ static int iproc_pcie_config_read32(struct pci_bus *bus, unsigned int devfn,
int where, int size, u32 *val) int where, int size, u32 *val)
{ {
int ret; int ret;
struct iproc_pcie *pcie = iproc_data(bus);
iproc_pcie_apb_err_disable(bus, true); 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); ret = pci_generic_config_read32(bus, devfn, where, size, val);
iproc_pcie_apb_err_disable(bus, false); iproc_pcie_apb_err_disable(bus, false);
...@@ -580,7 +670,7 @@ static struct pci_ops iproc_pcie_ops = { ...@@ -580,7 +670,7 @@ static struct pci_ops iproc_pcie_ops = {
.write = iproc_pcie_config_write32, .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; u32 val;
...@@ -592,26 +682,33 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie) ...@@ -592,26 +682,33 @@ static void iproc_pcie_reset(struct iproc_pcie *pcie)
if (pcie->ep_is_internal) if (pcie->ep_is_internal)
return; return;
/* if (assert) {
* Select perst_b signal as reset source. Put the device into reset,
* and then bring it out of reset
*/
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL); val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST & val &= ~EP_PERST_SOURCE_SELECT & ~EP_MODE_SURVIVE_PERST &
~RC_PCIE_RST_OUTPUT; ~RC_PCIE_RST_OUTPUT;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
udelay(250); udelay(250);
} else {
val = iproc_pcie_read_reg(pcie, IPROC_PCIE_CLK_CTRL);
val |= RC_PCIE_RST_OUTPUT; val |= RC_PCIE_RST_OUTPUT;
iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val); iproc_pcie_write_reg(pcie, IPROC_PCIE_CLK_CTRL, val);
msleep(100); 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) static int iproc_pcie_check_link(struct iproc_pcie *pcie)
{ {
struct device *dev = pcie->dev; struct device *dev = pcie->dev;
u32 hdr_type, link_ctrl, link_status, class, val; u32 hdr_type, link_ctrl, link_status, class, val;
u16 pos = PCI_EXP_CAP;
bool link_is_active = false; bool link_is_active = false;
/* /*
...@@ -646,7 +743,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie) ...@@ -646,7 +743,7 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie)
4, class); 4, class);
/* check link status to see if link is active */ /* 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); 2, &link_status);
if (link_status & PCI_EXP_LNKSTA_NLW) if (link_status & PCI_EXP_LNKSTA_NLW)
link_is_active = true; link_is_active = true;
...@@ -657,19 +754,19 @@ static int iproc_pcie_check_link(struct iproc_pcie *pcie) ...@@ -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_GEN2 0x2
#define PCI_TARGET_LINK_SPEED_GEN1 0x1 #define PCI_TARGET_LINK_SPEED_GEN1 0x1
iproc_pci_raw_config_read32(pcie, 0, iproc_pci_raw_config_read32(pcie, 0,
pos + PCI_EXP_LNKCTL2, 4, IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2,
&link_ctrl); 4, &link_ctrl);
if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) == if ((link_ctrl & PCI_TARGET_LINK_SPEED_MASK) ==
PCI_TARGET_LINK_SPEED_GEN2) { PCI_TARGET_LINK_SPEED_GEN2) {
link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK; link_ctrl &= ~PCI_TARGET_LINK_SPEED_MASK;
link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1; link_ctrl |= PCI_TARGET_LINK_SPEED_GEN1;
iproc_pci_raw_config_write32(pcie, 0, iproc_pci_raw_config_write32(pcie, 0,
pos + PCI_EXP_LNKCTL2, IPROC_PCI_EXP_CAP + PCI_EXP_LNKCTL2,
4, link_ctrl); 4, link_ctrl);
msleep(100); msleep(100);
iproc_pci_raw_config_read32(pcie, 0, iproc_pci_raw_config_read32(pcie, 0,
pos + PCI_EXP_LNKSTA, IPROC_PCI_EXP_CAP + PCI_EXP_LNKSTA,
2, &link_status); 2, &link_status);
if (link_status & PCI_EXP_LNKSTA_NLW) if (link_status & PCI_EXP_LNKSTA_NLW)
link_is_active = true; link_is_active = true;
...@@ -1223,6 +1320,8 @@ static int iproc_pcie_rev_init(struct iproc_pcie *pcie) ...@@ -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.nr_regions = ARRAY_SIZE(paxb_v2_ib_map);
pcie->ib_map = paxb_v2_ib_map; pcie->ib_map = paxb_v2_ib_map;
pcie->need_msi_steer = true; pcie->need_msi_steer = true;
dev_warn(dev, "reads of config registers that contain %#x return incorrect data\n",
CFG_RETRY_STATUS);
break; break;
case IPROC_PCIE_PAXC: case IPROC_PCIE_PAXC:
regs = iproc_pcie_reg_paxc; regs = iproc_pcie_reg_paxc;
...@@ -1286,7 +1385,8 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res) ...@@ -1286,7 +1385,8 @@ int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res)
goto err_exit_phy; 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) { if (pcie->need_ob_cfg) {
ret = iproc_pcie_map_ranges(pcie, res); ret = iproc_pcie_map_ranges(pcie, res);
......
...@@ -110,6 +110,7 @@ struct iproc_pcie { ...@@ -110,6 +110,7 @@ struct iproc_pcie {
int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res); int iproc_pcie_setup(struct iproc_pcie *pcie, struct list_head *res);
int iproc_pcie_remove(struct iproc_pcie *pcie); int iproc_pcie_remove(struct iproc_pcie *pcie);
int iproc_pcie_shutdown(struct iproc_pcie *pcie);
#ifdef CONFIG_PCIE_IPROC_MSI #ifdef CONFIG_PCIE_IPROC_MSI
int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node); int iproc_msi_init(struct iproc_pcie *pcie, struct device_node *node);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment