Re: [PATCH v2 1/2] pcie-designware: add iATU Unroll feature

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On 7/25/2016 9:44 PM, Bjorn Helgaas wrote:
> On Fri, Jul 22, 2016 at 02:29:38PM +0100, Joao Pinto wrote:
>> This patch adds the support to the new iATU mechanism that will be used
>> from Core version 4.80, which is called iATU Unroll.
>> The new Cores can support the iATU Unroll or support the "old" iATU 
>> method now called Legacy Mode. The driver is perfectly capable of
>> performing well for both.
>>
>> In order to make sure that the iATU is really enabled a for loop was
>> introduced in dw_pcie_prog_outbound_atu() to improve reliability.
>>
>> This patch also moves the sleep definitions to the *.c file like
>> suggested by Jisheng Zhang in a previous patch.
>>
>> Signed-off-by: Joao Pinto <jpinto@xxxxxxxxxxxx>
>> ---
>> changes v1->v2:
>> - Patch was breaking kernel build due to bad definition in macro funtion.
>> - Rebased on top of pci/next.
>>
>>  drivers/pci/host/pcie-designware.c | 157 +++++++++++++++++++++++++++++++++----
>>  drivers/pci/host/pcie-designware.h |   6 +-
>>  2 files changed, 144 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
>> index 12afce1..9135725 100644
>> --- a/drivers/pci/host/pcie-designware.c
>> +++ b/drivers/pci/host/pcie-designware.c
>> @@ -26,7 +26,15 @@
>>  
>>  #include "pcie-designware.h"
>>  
>> -/* Synopsis specific PCIE configuration registers */
>> +/* Parameters for the waiting for link up routine */
>> +#define LINK_WAIT_MAX_RETRIES		10
>> +#define LINK_WAIT_MAX_ATU_RETRIES	5
>> +#define LINK_WAIT_USLEEP_MIN		90000
>> +#define LINK_WAIT_USLEEP_MAX		100000
>> +#define LINK_WAIT_IATU_MIN		9000
>> +#define LINK_WAIT_IATU_MAX		10000
> 
> Add an introductory patch that moves definitions from
> drivers/pci/host/pcie-designware.h to here, then add the new
> ATU/unroll-related things in this patch.

Makes sense! I will do that!

> 
>> +/* Synopsys specific PCIE configuration registers */
>>  #define PCIE_PORT_LINK_CONTROL		0x710
>>  #define PORT_LINK_MODE_MASK		(0x3f << 16)
>>  #define PORT_LINK_MODE_1_LANES		(0x1 << 16)
>> @@ -58,6 +66,7 @@
>>  #define PCIE_ATU_TYPE_IO		(0x2 << 0)
>>  #define PCIE_ATU_TYPE_CFG0		(0x4 << 0)
>>  #define PCIE_ATU_TYPE_CFG1		(0x5 << 0)
>> +
>>  #define PCIE_ATU_CR2			0x908
>>  #define PCIE_ATU_ENABLE			(0x1 << 31)
>>  #define PCIE_ATU_BAR_MODE_ENABLE	(0x1 << 30)
>> @@ -75,6 +84,37 @@
>>  #define PCIE_PHY_DEBUG_R1		(PLR_OFFSET + 0x2c)
>>  #define PCIE_PHY_DEBUG_R1_LINK_UP	0x00000010
>>  
>> +/*
>> + * iatu unroll specific registers and definitions
>> + * From 4.80 Core version the address translation will be made by unroll
>> + */
>> +
>> +/* Registers */
>> +#define PCIE_ATU_UNR_REGION_CTRL1	0x00
>> +#define PCIE_ATU_UNR_REGION_CTRL2	0x01
>> +#define PCIE_ATU_UNR_LOWER_BASE		0x02
>> +#define PCIE_ATU_UNR_UPPER_BASE		0x03
>> +#define PCIE_ATU_UNR_LIMIT		0x04
>> +#define PCIE_ATU_UNR_LOWER_TARGET	0x05
>> +#define PCIE_ATU_UNR_UPPER_TARGET	0x06
>> +#define PCIE_ATU_UNR_REGION_CTRL3	0x07
>> +#define PCIE_ATU_UNR_UPPR_LIMIT		0x08
> 
> Last two items aren't used.

I can take them off, but don't you think it is useful to include them for future
applications?

> 
>> +/* register address builder */
>> +#define PCIE_GET_ATU_INB_UNR_REG_ADDR(region, register)		\
>> +					((0x3 << 20) | (region << 9) |	\
>> +					(0x1 << 8) | (register << 2))
>> +
>> +#define PCIE_GET_ATU_OUTB_UNR_REG_ADDR(region, register)		\
>> +					((0x3 << 20) | (region << 9) |	\
>> +					(register << 2))
>> +
>> +/* translation types */
>> +#define PCIE_TRANSL_INB			0x1
>> +#define PCIE_TRANSL_OUTB		0x2
>> +
>> +/* end of Unroll specific */
>> +
>>  static struct pci_ops dw_pcie_ops;
>>  
>>  int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
>> @@ -131,6 +171,38 @@ static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
>>  		writel(val, pp->dbi_base + reg);
>>  }
>>  
>> +static inline void dw_pcie_readl_unroll(struct pcie_port *pp, u32 type,
>> +						u32 index, u32 reg, u32 *val)
>> +{
>> +	u32 reg_addr = 0;
>> +
>> +	if (type == PCIE_TRANSL_OUTB)
>> +		reg_addr = PCIE_GET_ATU_OUTB_UNR_REG_ADDR(index, reg);
>> +	else if  (type == PCIE_TRANSL_INB)
>> +		reg_addr = PCIE_GET_ATU_INB_UNR_REG_ADDR(index, reg);
> 
> PCIE_TRANSL_INB is never used.  In fact, dw_pcie_readl_unroll() is
> *always* called with PCIE_TRANSL_OUTB, so it's not clear what the
> value of passing it as an argument is.

I included The Inbound translation mechanism because you can have Inbound or
Outbound translation in the iATU. The old mechanism also had Inbound properties
that are not used in the code. I suggest we keep the Inbound mechanism to avoid
future rework. Agree?

> 
>> +
>> +	if (pp->ops->readl_rc)
>> +		pp->ops->readl_rc(pp, pp->dbi_base + reg_addr, val);
>> +	else
>> +		*val = readl(pp->dbi_base + reg_addr);
>> +}
>> +
>> +static inline void dw_pcie_writel_unroll(struct pcie_port *pp, u32 type,
>> +						u32 index, u32 val, u32 reg)
>> +{
>> +	u32 reg_addr = 0;
>> +
>> +	if (type == PCIE_TRANSL_OUTB)
>> +		reg_addr = PCIE_GET_ATU_OUTB_UNR_REG_ADDR(index, reg);
>> +	else if  (type == PCIE_TRANSL_INB)
>> +		reg_addr = PCIE_GET_ATU_INB_UNR_REG_ADDR(index, reg);
>> +
>> +	if (pp->ops->writel_rc)
>> +		pp->ops->writel_rc(pp, val, pp->dbi_base + reg_addr);
>> +	else
>> +		writel(val, pp->dbi_base + reg_addr);
>> +}
>> +
>>  static int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
>>  			       u32 *val)
>>  {
>> @@ -152,24 +224,66 @@ static int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
>>  static void dw_pcie_prog_outbound_atu(struct pcie_port *pp, int index,
>>  		int type, u64 cpu_addr, u64 pci_addr, u32 size)
>>  {
>> -	u32 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);
>> +	u32 val = 0;
>> +	u32 retries = 0;
>> +
>> +	if (pp->iatu_unroll_status) {
>> +		/* outbound translation using Unroll feature*/
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			lower_32_bits(cpu_addr), PCIE_ATU_UNR_LOWER_BASE);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			upper_32_bits(cpu_addr), PCIE_ATU_UNR_UPPER_BASE);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			lower_32_bits(cpu_addr + size - 1), PCIE_ATU_UNR_LIMIT);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			lower_32_bits(pci_addr), PCIE_ATU_UNR_LOWER_TARGET);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			upper_32_bits(pci_addr), PCIE_ATU_UNR_UPPER_TARGET);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			type, PCIE_ATU_UNR_REGION_CTRL1);
>> +		dw_pcie_writel_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			PCIE_ATU_ENABLE, PCIE_ATU_UNR_REGION_CTRL2);
>> +		dw_pcie_readl_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +			PCIE_ATU_UNR_REGION_CTRL2, &val);
>> +	} else {
>> +		/* outbound translation using legacy mechanism */
>> +		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);
>> +		dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
>> +	}
>>  
>>  	/*
>>  	 * Make sure ATU enable takes effect before any subsequent config
>>  	 * and I/O accesses.
>>  	 */
> 
> Move the PCIE_ATU_UNR_REGION_CTRL2 and PCIE_ATU_CR2 reads down here so
> everything related to the retry loop is right here.  You can probably
> even restructure the loop so there's only one call without having to
> repeat it above then again inside the loop.

Right. I will do that!

> 
>> -	dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
>> +	for (retries = 0; retries < LINK_WAIT_MAX_ATU_RETRIES; retries++) {
>> +
>> +		if (val == PCIE_ATU_ENABLE)
>> +			break;
>> +
>> +		usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX);
>> +
>> +		if (pp->iatu_unroll_status) {
>> +			dw_pcie_readl_unroll(pp, PCIE_TRANSL_OUTB, index,
>> +				PCIE_ATU_UNR_REGION_CTRL2, &val);
>> +		} else {
>> +			dw_pcie_readl_rc(pp, PCIE_ATU_CR2, &val);
>> +		}
>> +	}
> 
> This retry loop is not strictly related to unroll, so it could be done
> in a separate patch.

Right. I will do that!

> 
>> +	dev_dbg(pp->dev, "iATU is not being enabled\n");
>>  }
>>  
>>  static struct irq_chip dw_msi_irq_chip = {
>> @@ -428,6 +542,18 @@ static const struct irq_domain_ops msi_domain_ops = {
>>  	.map = dw_pcie_msi_map,
>>  };
>>  
>> +static void dw_pcie_get_atu_mode(struct pcie_port *pp)
>> +{
>> +	u32 val = 0;
>> +
>> +	/* Check if the iATU unroll is enabled or not */
> 
> Refer to "iATU" consistently in comments.  Several occurrences above
> are "iatu", and below there's "ATU".

Right. I will do that!

> 
>> +	dw_pcie_readl_rc(pp, PCIE_ATU_VIEWPORT, &val);
>> +
>> +	pp->iatu_unroll_status = 0; /* disabled - legacy is used */
>> +	if (val == 0xFFFFFFFF)
>> +		pp->iatu_unroll_status = 1; /* enabled */
>> +}
>> +
>>  int dw_pcie_host_init(struct pcie_port *pp)
>>  {
>>  	struct device_node *np = pp->dev->of_node;
>> @@ -544,6 +670,9 @@ int dw_pcie_host_init(struct pcie_port *pp)
>>  		}
>>  	}
>>  
>> +	/* get ATU mode */
>> +	dw_pcie_get_atu_mode(pp);
> 
> Comment is superfluous since it only repeats the function name.  I
> prefer the style of:
> 
>   pp->iatu_unroll = dw_pcie_iatu_unroll_supported(pp);
> 
> so the assignment isn't buried inside the function.
> 

Makes sense.

>>  	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 f437f9b..354a981 100644
>> --- a/drivers/pci/host/pcie-designware.h
>> +++ b/drivers/pci/host/pcie-designware.h
>> @@ -22,11 +22,6 @@
>>  #define MAX_MSI_IRQS			32
>>  #define MAX_MSI_CTRLS			(MAX_MSI_IRQS / 32)
>>  
>> -/* Parameters for the waiting for link up routine */
>> -#define LINK_WAIT_MAX_RETRIES		10
>> -#define LINK_WAIT_USLEEP_MIN		90000
>> -#define LINK_WAIT_USLEEP_MAX		100000
>> -
>>  struct pcie_port {
>>  	struct device		*dev;
>>  	u8			root_bus_nr;
>> @@ -53,6 +48,7 @@ struct pcie_port {
>>  	int			msi_irq;
>>  	struct irq_domain	*irq_domain;
>>  	unsigned long		msi_data;
>> +	u8			iatu_unroll_status;
>>  	DECLARE_BITMAP(msi_irq_in_use, MAX_MSI_IRQS);
>>  };
>>  
>> -- 
>> 1.8.1.5
>>

Thanks for the review!

Joao

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



[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux