drivers/pci/controller/dwc/pcie-rcar-gen4.c

Source file repositories/reference/linux-study-clean/drivers/pci/controller/dwc/pcie-rcar-gen4.c

File Facts

System
Linux kernel
Corpus path
drivers/pci/controller/dwc/pcie-rcar-gen4.c
Extension
.c
Size
20512 bytes
Lines
798
Domain
Representative Device Path
Bucket
PCIe NVMe Storage Path
Inferred role
Representative Device Path: implementation source
Status
source implementation candidate

Why This File Exists

Part of the selected hardware vertical slice: PCI discovery, driver binding, NVMe queues, block requests, DMA, interrupts, and completion.

Dependency Surface

Detected Declarations

Annotated Snippet

struct rcar_gen4_pcie_drvdata {
	void (*additional_common_init)(struct rcar_gen4_pcie *rcar);
	int (*ltssm_control)(struct rcar_gen4_pcie *rcar, bool enable);
	enum dw_pcie_device_mode mode;
};

struct rcar_gen4_pcie {
	struct dw_pcie dw;
	void __iomem *base;
	void __iomem *phy_base;
	struct platform_device *pdev;
	const struct rcar_gen4_pcie_drvdata *drvdata;
};
#define to_rcar_gen4_pcie(_dw)	container_of(_dw, struct rcar_gen4_pcie, dw)

/* Common */
static bool rcar_gen4_pcie_link_up(struct dw_pcie *dw)
{
	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
	u32 val, mask;

	val = readl(rcar->base + PCIEINTSTS0);
	mask = RDLH_LINK_UP | SMLH_LINK_UP;

	return (val & mask) == mask;
}

/*
 * Manually initiate the speed change. Return 0 if change succeeded; otherwise
 * -ETIMEDOUT.
 */
static int rcar_gen4_pcie_speed_change(struct dw_pcie *dw)
{
	u32 val;
	int i;

	val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
	val &= ~PORT_LOGIC_SPEED_CHANGE;
	dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);

	val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
	val |= PORT_LOGIC_SPEED_CHANGE;
	dw_pcie_writel_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL, val);

	for (i = 0; i < RCAR_NUM_SPEED_CHANGE_RETRIES; i++) {
		val = dw_pcie_readl_dbi(dw, PCIE_LINK_WIDTH_SPEED_CONTROL);
		if (!(val & PORT_LOGIC_SPEED_CHANGE))
			return 0;
		usleep_range(10000, 11000);
	}

	return -ETIMEDOUT;
}

/*
 * Enable LTSSM of this controller and manually initiate the speed change.
 * Always return 0.
 */
static int rcar_gen4_pcie_start_link(struct dw_pcie *dw)
{
	struct rcar_gen4_pcie *rcar = to_rcar_gen4_pcie(dw);
	int i, changes, ret;

	if (rcar->drvdata->ltssm_control) {
		ret = rcar->drvdata->ltssm_control(rcar, true);
		if (ret)
			return ret;
	}

	/*
	 * Require direct speed change with retrying here if the max_link_speed
	 * is PCIe Gen2 or higher.
	 */
	changes = min_not_zero(dw->max_link_speed, RCAR_MAX_LINK_SPEED) - 1;

	/*
	 * Since dw_pcie_setup_rc() sets it once, PCIe Gen2 will be trained.
	 * So, this needs remaining times for up to PCIe Gen4 if RC mode.
	 */
	if (changes && rcar->drvdata->mode == DW_PCIE_RC_TYPE)
		changes--;

	for (i = 0; i < changes; i++) {
		/* It may not be connected in EP mode yet. So, break the loop */
		if (rcar_gen4_pcie_speed_change(dw))
			break;
	}

	return 0;
}

Annotation

Implementation Notes