Skip to content

Commit

Permalink
PCI: designware: Add iATU Unroll feature
Browse files Browse the repository at this point in the history
Add support for the new iATU Unroll mechanism that will be used from Core
version 4.80.  The new Cores can support either iATU Unroll or the "old"
iATU method, now called Legacy Mode.  The driver is perfectly capable of
performing well for both.

[bhelgaas: split ATU enable timeout to separate patch]
Signed-off-by: Joao Pinto <jpinto@synopsys.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Joao Pinto authored and bjorn-helgaas committed Aug 17, 2016
1 parent d8bbeb3 commit a0601a4
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 11 deletions.
97 changes: 86 additions & 11 deletions drivers/pci/host/pcie-designware.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,21 @@
#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
#define PCIE_ATU_UPPER_TARGET 0x91C

/*
* iATU Unroll-specific register definitions
* From 4.80 core version the address translation will be made by unroll
*/
#define PCIE_ATU_UNR_REGION_CTRL1 0x00
#define PCIE_ATU_UNR_REGION_CTRL2 0x04
#define PCIE_ATU_UNR_LOWER_BASE 0x08
#define PCIE_ATU_UNR_UPPER_BASE 0x0C
#define PCIE_ATU_UNR_LIMIT 0x10
#define PCIE_ATU_UNR_LOWER_TARGET 0x14
#define PCIE_ATU_UNR_UPPER_TARGET 0x18

/* Register address builder */
#define PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(region) ((0x3 << 20) | (region << 9))

/* PCIe Port Logic registers */
#define PLR_OFFSET 0x700
#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
Expand Down Expand Up @@ -141,6 +156,27 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
writel(val, pp->dbi_base + reg);
}

static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);

if (pp->ops->readl_rc)
return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);

return readl(pp->dbi_base + offset + reg);
}

static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
u32 val, u32 reg)
{
u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);

if (pp->ops->writel_rc)
pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
else
writel(val, pp->dbi_base + offset + reg);
}

static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
u32 *val)
{
Expand All @@ -164,23 +200,49 @@ static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
{
u32 retries, val;

dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
PCIE_ATU_VIEWPORT);
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr), PCIE_ATU_LOWER_BASE);
dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr), PCIE_ATU_UPPER_BASE);
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
PCIE_ATU_LIMIT);
dw_pcie_writel_rc(pp, lower_32_bits(pci_addr), PCIE_ATU_LOWER_TARGET);
dw_pcie_writel_rc(pp, upper_32_bits(pci_addr), PCIE_ATU_UPPER_TARGET);
dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
if (pp->iatu_unroll_enabled) {
dw_pcie_writel_unroll(pp, index,
lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
dw_pcie_writel_unroll(pp, index,
upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
dw_pcie_writel_unroll(pp, index,
lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
dw_pcie_writel_unroll(pp, index,
lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
dw_pcie_writel_unroll(pp, index,
upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
dw_pcie_writel_unroll(pp, index,
type, PCIE_ATU_UNR_REGION_CTRL1);
dw_pcie_writel_unroll(pp, index,
PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
} else {
dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
PCIE_ATU_VIEWPORT);
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
PCIE_ATU_LOWER_BASE);
dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
PCIE_ATU_UPPER_BASE);
dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
PCIE_ATU_LIMIT);
dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
PCIE_ATU_LOWER_TARGET);
dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
PCIE_ATU_UPPER_TARGET);
dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
}

/*
* Make sure ATU enable takes effect before any subsequent config
* and I/O accesses.
*/
for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);
if (pp->iatu_unroll_enabled)
val = dw_pcie_readl_unroll(pp, index,
PCIE_ATU_UNR_REGION_CTRL2);
else
val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);

if (val == PCIE_ATU_ENABLE)
return;

Expand Down Expand Up @@ -445,6 +507,17 @@ static const struct irq_domain_ops msi_domain_ops = {
.map = dw_pcie_msi_map,
};

static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp)
{
u32 val;

val = dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT);
if (val == 0xffffffff)
return 1;

return 0;
}

int dw_pcie_host_init(struct pcie_port *pp)
{
struct device_node *np = pp->dev->of_node;
Expand Down Expand Up @@ -561,6 +634,8 @@ int dw_pcie_host_init(struct pcie_port *pp)
}
}

pp->iatu_unroll_enabled = dw_pcie_iatu_unroll_enabled(pp);

if (pp->ops->host_init)
pp->ops->host_init(pp);

Expand Down
1 change: 1 addition & 0 deletions drivers/pci/host/pcie-designware.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ struct pcie_port {
int msi_irq;
struct irq_domain *irq_domain;
unsigned long msi_data;
u8 iatu_unroll_enabled;
DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
};

Expand Down

0 comments on commit a0601a4

Please sign in to comment.