diff options
Diffstat (limited to 'drivers/cxl/core/pci.c')
| -rw-r--r-- | drivers/cxl/core/pci.c | 113 | 
1 files changed, 100 insertions, 13 deletions
diff --git a/drivers/cxl/core/pci.c b/drivers/cxl/core/pci.c index bdbd907884ce..67f4ab6daa34 100644 --- a/drivers/cxl/core/pci.c +++ b/drivers/cxl/core/pci.c @@ -101,23 +101,57 @@ int devm_cxl_port_enumerate_dports(struct cxl_port *port)  }  EXPORT_SYMBOL_NS_GPL(devm_cxl_port_enumerate_dports, CXL); -/* - * Wait up to @media_ready_timeout for the device to report memory - * active. - */ -int cxl_await_media_ready(struct cxl_dev_state *cxlds) +static int cxl_dvsec_mem_range_valid(struct cxl_dev_state *cxlds, int id) +{ +	struct pci_dev *pdev = to_pci_dev(cxlds->dev); +	int d = cxlds->cxl_dvsec; +	bool valid = false; +	int rc, i; +	u32 temp; + +	if (id > CXL_DVSEC_RANGE_MAX) +		return -EINVAL; + +	/* Check MEM INFO VALID bit first, give up after 1s */ +	i = 1; +	do { +		rc = pci_read_config_dword(pdev, +					   d + CXL_DVSEC_RANGE_SIZE_LOW(id), +					   &temp); +		if (rc) +			return rc; + +		valid = FIELD_GET(CXL_DVSEC_MEM_INFO_VALID, temp); +		if (valid) +			break; +		msleep(1000); +	} while (i--); + +	if (!valid) { +		dev_err(&pdev->dev, +			"Timeout awaiting memory range %d valid after 1s.\n", +			id); +		return -ETIMEDOUT; +	} + +	return 0; +} + +static int cxl_dvsec_mem_range_active(struct cxl_dev_state *cxlds, int id)  {  	struct pci_dev *pdev = to_pci_dev(cxlds->dev);  	int d = cxlds->cxl_dvsec;  	bool active = false; -	u64 md_status;  	int rc, i; +	u32 temp; -	for (i = media_ready_timeout; i; i--) { -		u32 temp; +	if (id > CXL_DVSEC_RANGE_MAX) +		return -EINVAL; +	/* Check MEM ACTIVE bit, up to 60s timeout by default */ +	for (i = media_ready_timeout; i; i--) {  		rc = pci_read_config_dword( -			pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(0), &temp); +			pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(id), &temp);  		if (rc)  			return rc; @@ -134,6 +168,39 @@ int cxl_await_media_ready(struct cxl_dev_state *cxlds)  		return -ETIMEDOUT;  	} +	return 0; +} + +/* + * Wait up to @media_ready_timeout for the device to report memory + * active. + */ +int cxl_await_media_ready(struct cxl_dev_state *cxlds) +{ +	struct pci_dev *pdev = to_pci_dev(cxlds->dev); +	int d = cxlds->cxl_dvsec; +	int rc, i, hdm_count; +	u64 md_status; +	u16 cap; + +	rc = pci_read_config_word(pdev, +				  d + CXL_DVSEC_CAP_OFFSET, &cap); +	if (rc) +		return rc; + +	hdm_count = FIELD_GET(CXL_DVSEC_HDM_COUNT_MASK, cap); +	for (i = 0; i < hdm_count; i++) { +		rc = cxl_dvsec_mem_range_valid(cxlds, i); +		if (rc) +			return rc; +	} + +	for (i = 0; i < hdm_count; i++) { +		rc = cxl_dvsec_mem_range_active(cxlds, i); +		if (rc) +			return rc; +	} +  	md_status = readq(cxlds->regs.memdev + CXLMDEV_STATUS_OFFSET);  	if (!CXLMDEV_READY(md_status))  		return -EIO; @@ -241,17 +308,36 @@ static void disable_hdm(void *_cxlhdm)  	       hdm + CXL_HDM_DECODER_CTRL_OFFSET);  } -static int devm_cxl_enable_hdm(struct device *host, struct cxl_hdm *cxlhdm) +int devm_cxl_enable_hdm(struct cxl_port *port, struct cxl_hdm *cxlhdm)  { -	void __iomem *hdm = cxlhdm->regs.hdm_decoder; +	void __iomem *hdm;  	u32 global_ctrl; +	/* +	 * If the hdm capability was not mapped there is nothing to enable and +	 * the caller is responsible for what happens next.  For example, +	 * emulate a passthrough decoder. +	 */ +	if (IS_ERR(cxlhdm)) +		return 0; + +	hdm = cxlhdm->regs.hdm_decoder;  	global_ctrl = readl(hdm + CXL_HDM_DECODER_CTRL_OFFSET); + +	/* +	 * If the HDM decoder capability was enabled on entry, skip +	 * registering disable_hdm() since this decode capability may be +	 * owned by platform firmware. +	 */ +	if (global_ctrl & CXL_HDM_DECODER_ENABLE) +		return 0; +  	writel(global_ctrl | CXL_HDM_DECODER_ENABLE,  	       hdm + CXL_HDM_DECODER_CTRL_OFFSET); -	return devm_add_action_or_reset(host, disable_hdm, cxlhdm); +	return devm_add_action_or_reset(&port->dev, disable_hdm, cxlhdm);  } +EXPORT_SYMBOL_NS_GPL(devm_cxl_enable_hdm, CXL);  int cxl_dvsec_rr_decode(struct device *dev, int d,  			struct cxl_endpoint_dvsec_info *info) @@ -425,7 +511,7 @@ int cxl_hdm_decode_init(struct cxl_dev_state *cxlds, struct cxl_hdm *cxlhdm,  	if (info->mem_enabled)  		return 0; -	rc = devm_cxl_enable_hdm(&port->dev, cxlhdm); +	rc = devm_cxl_enable_hdm(port, cxlhdm);  	if (rc)  		return rc; @@ -571,6 +657,7 @@ void read_cdat_data(struct cxl_port *port)  		/* Don't leave table data allocated on error */  		devm_kfree(dev, cdat_table);  		dev_err(dev, "CDAT data read error\n"); +		return;  	}  	port->cdat.table = cdat_table + sizeof(__le32);  | 
