On Mon, Jan 31, 2022 at 10:25 AM Jonathan Cameron <Jonathan.Cameron@xxxxxxxxxx> wrote: > > On Sun, 23 Jan 2022 16:31:08 -0800 > Dan Williams <dan.j.williams@xxxxxxxxx> wrote: > > > From: Ben Widawsky <ben.widawsky@xxxxxxxxx> > > > > Before CXL 2.0 HDM Decoder Capability mechanisms can be utilized in a > > device the driver must determine that the device is ready for CXL.mem > > operation and that platform firmware, or some other agent, has > > established an active decode via the legacy CXL 1.1 decoder mechanism. > > > > This legacy mechanism is defined in the CXL DVSEC as a set of range > > registers and status bits that take time to settle after a reset. > > > > Validate the CXL memory decode setup via the DVSEC and cache it for > > later consideration by the cxl_mem driver (to be added). Failure to > > validate is not fatal to the cxl_pci driver since that is only providing > > CXL command support over PCI.mmio, and might be needed to rectify CXL > > DVSEC validation problems. > > > > Any potential ranges that the device is already claiming via DVSEC need > > to be reconciled with the dynamic provisioning ranges provided by > > platform firmware (like ACPI CEDT.CFMWS). Leave that reconciliation to > > the cxl_mem driver. > > > > Reported-by: kernel test robot <lkp@xxxxxxxxx> > > Signed-off-by: Ben Widawsky <ben.widawsky@xxxxxxxxx> > > [djbw: clarify changelog] > > [djbw: shorten defines] > > [djbw: change precise spin wait to generous msleep] > > Signed-off-by: Dan Williams <dan.j.williams@xxxxxxxxx> > > The name change from previous patch wants cleaning up and a few > more trivial suggestions inline. > > Thanks, > > Jonathan > > > --- > > drivers/cxl/cxlmem.h | 18 +++++++- > > drivers/cxl/cxlpci.h | 15 ++++++ > > drivers/cxl/pci.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++-- > > 3 files changed, 142 insertions(+), 7 deletions(-) > > > > diff --git a/drivers/cxl/cxlmem.h b/drivers/cxl/cxlmem.h > > index cedc6d3c0448..00f55f4066b9 100644 > > --- a/drivers/cxl/cxlmem.h > > +++ b/drivers/cxl/cxlmem.h > > @@ -89,6 +89,18 @@ struct cxl_mbox_cmd { > > */ > > #define CXL_CAPACITY_MULTIPLIER SZ_256M > > > > +/** > > + * struct cxl_endpoint_dvsec_info - Cached DVSEC info > > + * @mem_enabled: cached value of mem_enabled in the DVSEC, PCIE_DEVICE > > + * @ranges: Number of active HDM ranges this device uses. > > + * @dvsec_range: cached attributes of the ranges in the DVSEC, PCIE_DEVICE > > + */ > > +struct cxl_endpoint_dvsec_info { > > + bool mem_enabled; > > + int ranges; > > + struct range dvsec_range[2]; > > +}; > > + > > /** > > * struct cxl_dev_state - The driver device state > > * > > @@ -98,7 +110,7 @@ struct cxl_mbox_cmd { > > * > > * @dev: The device associated with this CXL state > > * @regs: Parsed register blocks > > - * @device_dvsec: Offset to the PCIe device DVSEC > > + * @cxl_dvsec: Offset to the PCIe device DVSEC > > So soon? Call it this in the previous patch! Whoops, yes, rebase mistake. > > > * @payload_size: Size of space for payload > > * (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register) > > * @lsa_size: Size of Label Storage Area > > @@ -118,6 +130,7 @@ struct cxl_mbox_cmd { > > * @next_volatile_bytes: volatile capacity change pending device reset > > * @next_persistent_bytes: persistent capacity change pending device reset > > * @component_reg_phys: register base of component registers > > + * @info: Cached DVSEC information about the device. > > * @mbox_send: @dev specific transport for transmitting mailbox commands > > * > > * See section 8.2.9.5.2 Capacity Configuration and Label Storage for > > @@ -127,7 +140,7 @@ struct cxl_dev_state { > > struct device *dev; > > > > struct cxl_regs regs; > > - int device_dvsec; > > + int cxl_dvsec; > > > > size_t payload_size; > > size_t lsa_size; > > @@ -149,6 +162,7 @@ struct cxl_dev_state { > > u64 next_persistent_bytes; > > > > resource_size_t component_reg_phys; > > + struct cxl_endpoint_dvsec_info info; > > > > int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd); > > }; > > diff --git a/drivers/cxl/cxlpci.h b/drivers/cxl/cxlpci.h > > index 766de340c4ce..2c29d26af7f8 100644 > > --- a/drivers/cxl/cxlpci.h > > +++ b/drivers/cxl/cxlpci.h > > @@ -16,7 +16,20 @@ > > #define PCI_DVSEC_VENDOR_ID_CXL 0x1E98 > > > > /* CXL 2.0 8.1.3: PCIe DVSEC for CXL Device */ > > -#define CXL_DVSEC_PCIE_DEVICE 0 > > +#define CXL_DVSEC 0 > > +#define CXL_DVSEC_CAP_OFFSET 0xA > > +#define CXL_DVSEC_MEM_CAPABLE BIT(2) > > +#define CXL_DVSEC_HDM_COUNT_MASK GENMASK(5, 4) > > +#define CXL_DVSEC_CTRL_OFFSET 0xC > > +#define CXL_DVSEC_MEM_ENABLE BIT(2) > > +#define CXL_DVSEC_RANGE_SIZE_HIGH(i) (0x18 + (i * 0x10)) > > +#define CXL_DVSEC_RANGE_SIZE_LOW(i) (0x1C + (i * 0x10)) > > +#define CXL_DVSEC_MEM_INFO_VALID BIT(0) > > +#define CXL_DVSEC_MEM_ACTIVE BIT(1) > > +#define CXL_DVSEC_MEM_SIZE_LOW_MASK GENMASK(31, 28) > > +#define CXL_DVSEC_RANGE_BASE_HIGH(i) (0x20 + (i * 0x10)) > > +#define CXL_DVSEC_RANGE_BASE_LOW(i) (0x24 + (i * 0x10)) > > +#define CXL_DVSEC_MEM_BASE_LOW_MASK GENMASK(31, 28) > > > > /* CXL 2.0 8.1.4: Non-CXL Function Map DVSEC */ > > #define CXL_DVSEC_FUNCTION_MAP 2 > > diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c > > index 76de39b90351..5c43886dc2af 100644 > > --- a/drivers/cxl/pci.c > > +++ b/drivers/cxl/pci.c > > @@ -386,6 +386,110 @@ static int cxl_setup_regs(struct pci_dev *pdev, enum cxl_regloc_type type, > > return rc; > > } > > > > +static int wait_for_valid(struct cxl_dev_state *cxlds) > > +{ > > + struct pci_dev *pdev = to_pci_dev(cxlds->dev); > > + int d = cxlds->cxl_dvsec, rc; > > + u32 val; > > + > > + /* > > + * Memory_Info_Valid: When set, indicates that the CXL Range 1 Size high > > + * and Size Low registers are valid. Must be set within 1 second of > > + * deassertion of reset to CXL device. Likely it is already set by the > > + * time this runs, but otherwise give a 1.5 second timeout in case of > > + * clock skew. > > + */ > > + rc = pci_read_config_dword(pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(0), &val); > > + if (rc) > > + return rc; > > + > > + if (val & CXL_DVSEC_MEM_INFO_VALID) > > + return 0; > > + > > + msleep(1500); > > + > > + rc = pci_read_config_dword(pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(0), &val); > > + if (rc) > > + return rc; > > + > > + if (val & CXL_DVSEC_MEM_INFO_VALID) > > + return 0; > > Prefer a blank line here. Sure. > > > + return -ETIMEDOUT; > > +} > > + > > +static int cxl_dvsec_ranges(struct cxl_dev_state *cxlds) > > +{ > > + struct cxl_endpoint_dvsec_info *info = &cxlds->info; > > + struct pci_dev *pdev = to_pci_dev(cxlds->dev); > > + int d = cxlds->cxl_dvsec; > > + int hdm_count, rc, i; > > + u16 cap, ctrl; > > + > > + rc = pci_read_config_word(pdev, d + CXL_DVSEC_CAP_OFFSET, &cap); > > + if (rc) > > + return rc; > > trivial but I'd like a blank line here as I find that slightly easier > to parse after to many code reviews... Done. > > > + rc = pci_read_config_word(pdev, d + CXL_DVSEC_CTRL_OFFSET, &ctrl); > > + if (rc) > > + return rc; > > + > > + if (!(cap & CXL_DVSEC_MEM_CAPABLE)) > > + return -ENXIO; > > + > > + /* > > + * It is not allowed by spec for MEM.capable to be set and have 0 HDM > > + * decoders. As this driver is for a spec defined class code which must > > + * be CXL.mem capable, there is no point in continuing. > > Comment should probably also talk about why > 2 not allowed. Changed to: /* * It is not allowed by spec for MEM.capable to be set and have 0 legacy * HDM decoders (values > 2 are also undefined as of CXL 2.0). As this * driver is for a spec defined class code which must be CXL.mem * capable, there is no point in continuing to enable CXL.mem. */ > > > + */ > > + hdm_count = FIELD_GET(CXL_DVSEC_HDM_COUNT_MASK, cap); > > + if (!hdm_count || hdm_count > 2) > > + return -EINVAL; > > + > > + rc = wait_for_valid(cxlds); > > + if (rc) > > + return rc; > > + > > + info->mem_enabled = FIELD_GET(CXL_DVSEC_MEM_ENABLE, ctrl); > > + > > + for (i = 0; i < hdm_count; i++) { > > + u64 base, size; > > + u32 temp; > > + > > + rc = pci_read_config_dword( > > + pdev, d + CXL_DVSEC_RANGE_SIZE_HIGH(i), &temp); > > + if (rc) > > + break; > > return rc; would be cleaner for these than break. > Saves the minor review effort of going to look for what is done in the > exit path (nothing :) Done. I had considered just dropping the error checking altogether since the PCI core is not this paranoid, but might as well keep it at this point. > > > + size = (u64)temp << 32; > > + > > + rc = pci_read_config_dword( > > + pdev, d + CXL_DVSEC_RANGE_SIZE_LOW(i), &temp); > > + if (rc) > > + break; > > + size |= temp & CXL_DVSEC_MEM_SIZE_LOW_MASK; > > + > > + rc = pci_read_config_dword( > > + pdev, d + CXL_DVSEC_RANGE_BASE_HIGH(i), &temp); > > + if (rc) > > + break; > > + base = (u64)temp << 32; > > + > > + rc = pci_read_config_dword( > > + pdev, d + CXL_DVSEC_RANGE_BASE_LOW(i), &temp); > > + if (rc) > > + break; > > + base |= temp & CXL_DVSEC_MEM_BASE_LOW_MASK; > > + > > + info->dvsec_range[i] = (struct range) { > > + .start = base, > > + .end = base + size - 1 > > + }; > > + > > + if (size) > > + info->ranges++; > > + } > > + > > + return rc; > > +} > > + > > static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) > > { > > struct cxl_register_map map; > > @@ -408,10 +512,9 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) > > if (IS_ERR(cxlds)) > > return PTR_ERR(cxlds); > > > > - cxlds->device_dvsec = pci_find_dvsec_capability(pdev, > > - PCI_DVSEC_VENDOR_ID_CXL, > > - CXL_DVSEC_PCIE_DEVICE); > > - if (!cxlds->device_dvsec) { > > + cxlds->cxl_dvsec = pci_find_dvsec_capability( > > + pdev, PCI_DVSEC_VENDOR_ID_CXL, CXL_DVSEC); > > + if (!cxlds->cxl_dvsec) { > > I'm guessing a rebase went astray given this only came in one patch earlier. Yes, sorry about that. > > > dev_err(&pdev->dev, > > "Device DVSEC not present. Expect limited functionality.\n"); > > return -ENXIO; > > @@ -452,6 +555,11 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) > > if (rc) > > return rc; > > > > + rc = cxl_dvsec_ranges(cxlds); > > + if (rc) > > + dev_err(&pdev->dev, > > + "Failed to get DVSEC range information (%d)\n", rc); > > + > > cxlmd = devm_cxl_add_memdev(cxlds); > > if (IS_ERR(cxlmd)) > > return PTR_ERR(cxlmd); > > >