diff mbox

[v6,4/4] PCI: designware: Add iATU Unroll feature

Message ID 20160817200022.10418.88555.stgit@bhelgaas-glaptop2.roam.corp.google.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Bjorn Helgaas Aug. 17, 2016, 8 p.m. UTC
From: Joao Pinto <Joao.Pinto@synopsys.com>

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>
---
 drivers/pci/host/pcie-designware.c |   97 ++++++++++++++++++++++++++++++++----
 drivers/pci/host/pcie-designware.h |    1 
 2 files changed, 87 insertions(+), 11 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

kernel test robot Aug. 17, 2016, 8:38 p.m. UTC | #1
Hi Joao,

[auto build test ERROR on pci/next]
[also build test ERROR on v4.8-rc2 next-20160817]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Bjorn-Helgaas/PCI-designware-Return-data-directly-from-dw_pcie_readl_rc/20160818-040531
base:   https://git.kernel.org/pub/scm/linux/kernel/git/helgaas/pci.git next
config: x86_64-randconfig-x015-201633 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-9) 6.1.1 20160705
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All error/warnings (new ones prefixed by >>):

   drivers/pci/host/pcie-designware.c: In function 'dw_pcie_readl_unroll':
>> drivers/pci/host/pcie-designware.c:161:54: error: expected ')' before ';' token
     u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
                                                         ^
>> drivers/pci/host/pcie-designware.c:167:1: error: expected ',' or ';' before '}' token
    }
    ^
>> drivers/pci/host/pcie-designware.c:169:20: error: invalid storage class for function 'dw_pcie_writel_unroll'
    static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
                       ^~~~~~~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c: In function 'dw_pcie_writel_unroll':
   drivers/pci/host/pcie-designware.c:172:54: error: expected ')' before ';' token
     u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
                                                         ^
   drivers/pci/host/pcie-designware.c:178:1: error: expected ',' or ';' before '}' token
    }
    ^
>> drivers/pci/host/pcie-designware.c:180:12: error: invalid storage class for function 'dw_pcie_rd_own_conf'
    static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
               ^~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:189:12: error: invalid storage class for function 'dw_pcie_wr_own_conf'
    static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
               ^~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:198:13: error: invalid storage class for function 'dw_pcie_prog_outbound_atu'
    static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
                ^~~~~~~~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:304:13: error: invalid storage class for function 'dw_pcie_msi_clear_irq'
    static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
                ^~~~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:315:13: error: invalid storage class for function 'clear_irq_range'
    static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
                ^~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:332:13: error: invalid storage class for function 'dw_pcie_msi_set_irq'
    static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
                ^~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:343:12: error: invalid storage class for function 'assign_irq'
    static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
               ^~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:387:13: error: invalid storage class for function 'dw_msi_setup_msg'
    static void dw_msi_setup_msg(struct pcie_port *pp, unsigned int irq, u32 pos)
                ^~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:408:12: error: invalid storage class for function 'dw_msi_setup_irq'
    static int dw_msi_setup_irq(struct msi_controller *chip, struct pci_dev *pdev,
               ^~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:426:12: error: invalid storage class for function 'dw_msi_setup_irqs'
    static int dw_msi_setup_irqs(struct msi_controller *chip, struct pci_dev *pdev,
               ^~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:453:13: error: invalid storage class for function 'dw_msi_teardown_irq'
    static void dw_msi_teardown_irq(struct msi_controller *chip, unsigned int irq)
                ^~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:463:15: error: initializer element is not constant
     .setup_irq = dw_msi_setup_irq,
                  ^~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:463:15: note: (near initialization for 'dw_pcie_msi_chip.setup_irq')
   drivers/pci/host/pcie-designware.c:464:16: error: initializer element is not constant
     .setup_irqs = dw_msi_setup_irqs,
                   ^~~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:464:16: note: (near initialization for 'dw_pcie_msi_chip.setup_irqs')
   drivers/pci/host/pcie-designware.c:465:18: error: initializer element is not constant
     .teardown_irq = dw_msi_teardown_irq,
                     ^~~~~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:465:18: note: (near initialization for 'dw_pcie_msi_chip.teardown_irq')
>> drivers/pci/host/pcie-designware.c:497:12: error: invalid storage class for function 'dw_pcie_msi_map'
    static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
               ^~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:507:9: error: initializer element is not constant
     .map = dw_pcie_msi_map,
            ^~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:507:9: note: (near initialization for 'msi_domain_ops.map')
>> drivers/pci/host/pcie-designware.c:510:11: error: invalid storage class for function 'dw_pcie_iatu_unroll_enabled'
    static u8 dw_pcie_iatu_unroll_enabled(struct pcie_port *pp)
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c: In function 'dw_pcie_iatu_unroll_enabled':
>> drivers/pci/host/pcie-designware.c:514:2: error: too many arguments to function 'dw_pcie_readl_rc'
     dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT, &val);
     ^~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c:143:19: note: declared here
    static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
                      ^~~~~~~~~~~~~~~~
   drivers/pci/host/pcie-designware.c: In function 'dw_pcie_writel_unroll':
>> drivers/pci/host/pcie-designware.c:678:12: error: invalid storage class for function 'dw_pcie_rd_other_conf'
    static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
               ^~~~~~~~~~~~~~~~~~~~~
>> drivers/pci/host/pcie-designware.c:715:12: error: invalid storage class for function 'dw_pcie_wr_other_conf'
    static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
               ^~~~~~~~~~~~~~~~~~~~~

vim +161 drivers/pci/host/pcie-designware.c

   137		else
   138			return PCIBIOS_BAD_REGISTER_NUMBER;
   139	
   140		return PCIBIOS_SUCCESSFUL;
   141	}
   142	
 > 143	static inline u32 dw_pcie_readl_rc(struct pcie_port *pp, u32 reg)
   144	{
   145		if (pp->ops->readl_rc)
   146			return pp->ops->readl_rc(pp, pp->dbi_base + reg);
   147	
   148		return readl(pp->dbi_base + reg);
   149	}
   150	
   151	static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
   152	{
   153		if (pp->ops->writel_rc)
   154			pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
   155		else
   156			writel(val, pp->dbi_base + reg);
   157	}
   158	
   159	static inline u32 dw_pcie_readl_unroll(struct pcie_port *pp, u32 index, u32 reg)
   160	{
 > 161		u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
   162	
   163		if (pp->ops->readl_rc)
   164			return pp->ops->readl_rc(pp, pp->dbi_base + offset + reg);
   165	
   166		return readl(pp->dbi_base + offset + reg);
 > 167	}
   168	
 > 169	static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 index,
   170						 u32 val, u32 reg)
   171	{
 > 172		u32 offset = PCIE_GET_ATU_OUTB_UNR_REG_OFFSET(index);
   173	
   174		if (pp->ops->writel_rc)
   175			pp->ops->writel_rc(pp, val, pp->dbi_base + offset + reg);
   176		else
   177			writel(val, pp->dbi_base + offset + reg);
 > 178	}
   179	
 > 180	static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
   181				       u32 *val)
   182	{
   183		if (pp->ops->rd_own_conf)
   184			return pp->ops->rd_own_conf(pp, where, size, val);
   185	
   186		return dw_pcie_cfg_read(pp->dbi_base + where, size, val);
   187	}
   188	
 > 189	static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
   190				       u32 val)
   191	{
   192		if (pp->ops->wr_own_conf)
   193			return pp->ops->wr_own_conf(pp, where, size, val);
   194	
   195		return dw_pcie_cfg_write(pp->dbi_base + where, size, val);
   196	}
   197	
 > 198	static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
   199			int type, u64 cpu_addr, u64 pci_addr, u32 size)
   200	{
   201		u32 retries, val;
   202	
   203		if (pp->iatu_unroll_enabled) {
   204			dw_pcie_writel_unroll(pp, index,
   205				lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
   206			dw_pcie_writel_unroll(pp, index,
   207				upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
   208			dw_pcie_writel_unroll(pp, index,
   209				lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
   210			dw_pcie_writel_unroll(pp, index,
   211				lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
   212			dw_pcie_writel_unroll(pp, index,
   213				upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
   214			dw_pcie_writel_unroll(pp, index,
   215				type, PCIE_ATU_UNR_REGION_CTRL1);
   216			dw_pcie_writel_unroll(pp, index,
   217				PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
   218		} else {
   219			dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | index,
   220							PCIE_ATU_VIEWPORT);
   221			dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr),
   222							PCIE_ATU_LOWER_BASE);
   223			dw_pcie_writel_rc(pp, upper_32_bits(cpu_addr),
   224							PCIE_ATU_UPPER_BASE);
   225			dw_pcie_writel_rc(pp, lower_32_bits(cpu_addr + size - 1),
   226							PCIE_ATU_LIMIT);
   227			dw_pcie_writel_rc(pp, lower_32_bits(pci_addr),
   228							PCIE_ATU_LOWER_TARGET);
   229			dw_pcie_writel_rc(pp, upper_32_bits(pci_addr),
   230							PCIE_ATU_UPPER_TARGET);
   231			dw_pcie_writel_rc(pp, type, PCIE_ATU_CR1);
   232			dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
   233		}
   234	
   235		/*
   236		 * Make sure ATU enable takes effect before any subsequent config
   237		 * and I/O accesses.
   238		 */
   239		for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) {
   240			if (pp->iatu_unroll_enabled)
   241				val = dw_pcie_readl_unroll(pp, index,
   242							   PCIE_ATU_UNR_REGION_CTRL2);
   243			else
   244				val = dw_pcie_readl_rc(pp, PCIE_ATU_CR2);
   245	
   246			if (val == PCIE_ATU_ENABLE)
   247				return;
   248	
   249			usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
   250		}
   251		dev_err(pp->dev, "iATU is not being enabled\n");
   252	}
   253	
   254	static struct irq_chip dw_msi_irq_chip = {
   255		.name = "PCI-MSI",
   256		.irq_enable = pci_msi_unmask_irq,
   257		.irq_disable = pci_msi_mask_irq,
   258		.irq_mask = pci_msi_mask_irq,
   259		.irq_unmask = pci_msi_unmask_irq,
   260	};
   261	
   262	/* MSI int handler */
   263	irqreturn_t dw_handle_msi_irq(struct pcie_port *pp)
   264	{
   265		unsigned long val;
   266		int i, pos, irq;
   267		irqreturn_t ret = IRQ_NONE;
   268	
   269		for (i = 0; i < MAX_MSI_CTRLS; i++) {
   270			dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_STATUS + i * 12, 4,
   271					(u32 *)&val);
   272			if (val) {
   273				ret = IRQ_HANDLED;
   274				pos = 0;
   275				while ((pos = find_next_bit(&val, 32, pos)) != 32) {
   276					irq = irq_find_mapping(pp->irq_domain,
   277							i * 32 + pos);
   278					dw_pcie_wr_own_conf(pp,
   279							PCIE_MSI_INTR0_STATUS + i * 12,
   280							4, 1 << pos);
   281					generic_handle_irq(irq);
   282					pos++;
   283				}
   284			}
   285		}
   286	
   287		return ret;
   288	}
   289	
   290	void dw_pcie_msi_init(struct pcie_port *pp)
   291	{
   292		u64 msi_target;
   293	
   294		pp->msi_data = __get_free_pages(GFP_KERNEL, 0);
   295		msi_target = virt_to_phys((void *)pp->msi_data);
   296	
   297		/* program the msi_data */
   298		dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_LO, 4,
   299				    (u32)(msi_target & 0xffffffff));
   300		dw_pcie_wr_own_conf(pp, PCIE_MSI_ADDR_HI, 4,
   301				    (u32)(msi_target >> 32 & 0xffffffff));
   302	}
   303	
 > 304	static void dw_pcie_msi_clear_irq(struct pcie_port *pp, int irq)
   305	{
   306		unsigned int res, bit, val;
   307	
   308		res = (irq / 32) * 12;
   309		bit = irq % 32;
   310		dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
   311		val &= ~(1 << bit);
   312		dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
   313	}
   314	
 > 315	static void clear_irq_range(struct pcie_port *pp, unsigned int irq_base,
   316				    unsigned int nvec, unsigned int pos)
   317	{
   318		unsigned int i;
   319	
   320		for (i = 0; i < nvec; i++) {
   321			irq_set_msi_desc_off(irq_base, i, NULL);
   322			/* Disable corresponding interrupt on MSI controller */
   323			if (pp->ops->msi_clear_irq)
   324				pp->ops->msi_clear_irq(pp, pos + i);
   325			else
   326				dw_pcie_msi_clear_irq(pp, pos + i);
   327		}
   328	
   329		bitmap_release_region(pp->msi_irq_in_use, pos, order_base_2(nvec));
   330	}
   331	
 > 332	static void dw_pcie_msi_set_irq(struct pcie_port *pp, int irq)
   333	{
   334		unsigned int res, bit, val;
   335	
   336		res = (irq / 32) * 12;
   337		bit = irq % 32;
   338		dw_pcie_rd_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, &val);
   339		val |= 1 << bit;
   340		dw_pcie_wr_own_conf(pp, PCIE_MSI_INTR0_ENABLE + res, 4, val);
   341	}
   342	
 > 343	static int assign_irq(int no_irqs, struct msi_desc *desc, int *pos)
   344	{
   345		int irq, pos0, i;
   346		struct pcie_port *pp = (struct pcie_port *) msi_desc_to_pci_sysdata(desc);

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Tim Harvey Nov. 16, 2016, 12:22 a.m. UTC | #2
On Wed, Aug 17, 2016 at 1:00 PM, Bjorn Helgaas <bhelgaas@google.com> wrote:
> From: Joao Pinto <Joao.Pinto@synopsys.com>
>
> 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>
> ---
>  drivers/pci/host/pcie-designware.c |   97 ++++++++++++++++++++++++++++++++----
>  drivers/pci/host/pcie-designware.h |    1
>  2 files changed, 87 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
> index 947fac3..e489b18 100644
> --- a/drivers/pci/host/pcie-designware.c
> +++ b/drivers/pci/host/pcie-designware.c
> @@ -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)
> @@ -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)
>  {
> @@ -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;
>
> @@ -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 = 0;
> +
> +       dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT, &val);
> +       if (val == 0xffffffff)
> +               return 1;
> +
> +       return 0;
> +}
> +
>  int dw_pcie_host_init(struct pcie_port *pp)
>  {
>         struct device_node *np = pp->dev->of_node;
> @@ -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);
>
> diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
> index 285e1ed..e74cc4a 100644
> --- a/drivers/pci/host/pcie-designware.h
> +++ b/drivers/pci/host/pcie-designware.h
> @@ -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);
>  };
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

Bjorn / Joao,

This patch (from commit a0601a47053714eecec726aea5ebcd829f817497) is
causing a kernel regression in 4.9. I can no longer boot the kernel on
boards that have a PCIe switch downstream from the IMX6 (ie many of
the Gateworks Ventana boards) unless PCI is enabled in the bootloader
(which is not recommended as that causes PCI link in the kernel to
occasionally fail).

Regards,

Tim
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Fabio Estevam Nov. 16, 2016, 12:30 a.m. UTC | #3
Hi Tim,

On Tue, Nov 15, 2016 at 10:22 PM, Tim Harvey <tharvey@gateworks.com> wrote:

> Bjorn / Joao,
>
> This patch (from commit a0601a47053714eecec726aea5ebcd829f817497) is
> causing a kernel regression in 4.9. I can no longer boot the kernel on
> boards that have a PCIe switch downstream from the IMX6 (ie many of
> the Gateworks Ventana boards) unless PCI is enabled in the bootloader
> (which is not recommended as that causes PCI link in the kernel to
> occasionally fail).

This has been fixed in 4.9-rc4 by the following commit:

commit 416379f9ebded501eda882e6af0a7aafc1866700
Author: Niklas Cassel <niklas.cassel@axis.com>
Date:   Fri Oct 14 23:54:55 2016 +0200

    PCI: designware: Check for iATU unroll support after initializing host

    dw_pcie_iatu_unroll_enabled() reads a dbi_base register.  Reading any
    dbi_base register before pp->ops->host_init has been called causes
    "imprecise external abort" on platforms like ARTPEC-6, where the PCIe
    module is disabled at boot and first enabled in pp->ops->host_init.  Move
    dw_pcie_iatu_unroll_enabled() to dw_pcie_setup_rc(), since it is after
    pp->ops->host_init, but before pp->iatu_unroll_enabled is actually used.

    Fixes: a0601a470537 ("PCI: designware: Add iATU Unroll feature")
    Tested-by: James Le Cuirot <chewi@gentoo.org>
    Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
    Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
    Acked-by: Joao Pinto <jpinto@synopsys.com>
    Acked-by: Olof Johansson <olof@lixom.net>
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Tim Harvey Nov. 16, 2016, 4:15 p.m. UTC | #4
On Tue, Nov 15, 2016 at 4:30 PM, Fabio Estevam <festevam@gmail.com> wrote:
> Hi Tim,
>
> On Tue, Nov 15, 2016 at 10:22 PM, Tim Harvey <tharvey@gateworks.com> wrote:
>
>> Bjorn / Joao,
>>
>> This patch (from commit a0601a47053714eecec726aea5ebcd829f817497) is
>> causing a kernel regression in 4.9. I can no longer boot the kernel on
>> boards that have a PCIe switch downstream from the IMX6 (ie many of
>> the Gateworks Ventana boards) unless PCI is enabled in the bootloader
>> (which is not recommended as that causes PCI link in the kernel to
>> occasionally fail).
>
> This has been fixed in 4.9-rc4 by the following commit:
>
> commit 416379f9ebded501eda882e6af0a7aafc1866700
> Author: Niklas Cassel <niklas.cassel@axis.com>
> Date:   Fri Oct 14 23:54:55 2016 +0200
>
>     PCI: designware: Check for iATU unroll support after initializing host
>
>     dw_pcie_iatu_unroll_enabled() reads a dbi_base register.  Reading any
>     dbi_base register before pp->ops->host_init has been called causes
>     "imprecise external abort" on platforms like ARTPEC-6, where the PCIe
>     module is disabled at boot and first enabled in pp->ops->host_init.  Move
>     dw_pcie_iatu_unroll_enabled() to dw_pcie_setup_rc(), since it is after
>     pp->ops->host_init, but before pp->iatu_unroll_enabled is actually used.
>
>     Fixes: a0601a470537 ("PCI: designware: Add iATU Unroll feature")
>     Tested-by: James Le Cuirot <chewi@gentoo.org>
>     Signed-off-by: Niklas Cassel <niklas.cassel@axis.com>
>     Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
>     Acked-by: Joao Pinto <jpinto@synopsys.com>
>     Acked-by: Olof Johansson <olof@lixom.net>

Fabio,

That does indeed resolve the issue!

Thanks,

Tim
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 947fac3..e489b18 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -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)
@@ -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)
 {
@@ -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;
 
@@ -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 = 0;
+
+	dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT, &val);
+	if (val == 0xffffffff)
+		return 1;
+
+	return 0;
+}
+
 int dw_pcie_host_init(struct pcie_port *pp)
 {
 	struct device_node *np = pp->dev->of_node;
@@ -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);
 
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index 285e1ed..e74cc4a 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -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);
 };