2021-05-28 03:59:14

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3 0/5] Map register blocks individually

From: Ira Weiny <[email protected]>

Changes for v3:
From Jonathan:
Add Reviews. Thanks!
Add back the kernel doc comment and enhance it a bit
Update commit messages with more details.
Remove CXL_REGLOC_RBI_MAX
Dan
Add pcim_enable_device() back in because it is needed
and is not incompatible with pci_release_mem_regions()
like I originally though.
Change cxl_ioremap_block() to devm_cxl_iomap_block()

Add kernel doc comment for cxl_probe_component_regs()
Update HDM patch to devm_cxl_iomap_block() call.

Some hardware implementations mix component and device registers into the same
BAR and the driver stack is going to have independent mapping implementations
for those 2 cases. Furthermore, it will be nice to have finer grained mappings
should user space want to map some register blocks.

Unfortunately, the information for the register blocks is contained inside the
BARs themselves. Which means the BAR must be mapped, probed, and unmapped
prior to the registers being mapped individually.

The series starts by introducing the helper function
cxl_decode_register_block(). Then breaks out region reservation and register
mapping. Separates mapping the registers into a probe stage and mapping stage.
The probe stage creates a list of register blocks which is then iterated to map
the individual register blocks.

Once mapping is performed in 2 steps the pci device management is removed and
the resource reservation can be done per register block as well.

Finally, mapping the HDM decoder register block is added.


Ben Widawsky (1):
cxl/pci: Add HDM decoder capabilities

Ira Weiny (4):
cxl/mem: Introduce cxl_decode_register_block()
cxl/mem: Reserve all device regions at once
cxl/mem: Map registers based on capabilities
cxl/mem: Reserve individual register block regions

drivers/cxl/core.c | 193 ++++++++++++++++++++++++++++++++++++++++++---
drivers/cxl/cxl.h | 98 ++++++++++++++++++++---
drivers/cxl/pci.c | 164 ++++++++++++++++++++++++++++++--------
3 files changed, 402 insertions(+), 53 deletions(-)

--
2.28.0.rc0.12.gb6a658bd00c9


2021-05-28 03:59:43

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3 4/5] cxl/mem: Reserve individual register block regions

From: Ira Weiny <[email protected]>

Some hardware implementations mix component and device registers into
the same BAR and the driver stack is going to need independent mapping
implementations for those 2 cases. Furthermore, it will be nice to have
finer grained mappings should user space want to map some register
blocks.

Now that individual register blocks are mapped; those blocks regions
should be reserved individually to fully separate the register blocks.

Release the 'global' memory reservation and create individual register
block region reservations through devm.

NOTE: pci_release_mem_regions() is still compatible with
pcim_enable_device() because it removes the automatic region release
when called. So preserve the pcim_enable_device() so that the pcim
interface can be called if needed.

Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>

---
Changes for V3
Dan
Add pcim_enable_device() back in because it is needed
and is not incompatible with pci_release_mem_regions()
like I originally though.
Change cxl_ioremap_block() to devm_cxl_iomap_block()
Jonathan
Update commit message with more details.
---
drivers/cxl/core.c | 36 ++++++++++++++++++++++++++++++++----
drivers/cxl/pci.c | 2 ++
2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/drivers/cxl/core.c b/drivers/cxl/core.c
index ab8d6bce59c2..2b5aac6f76f2 100644
--- a/drivers/cxl/core.c
+++ b/drivers/cxl/core.c
@@ -82,11 +82,33 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
}
EXPORT_SYMBOL_GPL(cxl_probe_device_regs);

+static void __iomem *devm_cxl_iomap_block(struct pci_dev *pdev,
+ resource_size_t addr,
+ resource_size_t length)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *ret_val;
+ struct resource *res;
+
+ res = devm_request_mem_region(dev, addr, length, pci_name(pdev));
+ if (!res) {
+ dev_err(dev, "Failed to request region %#llx-%#llx\n",
+ addr, addr+length);
+ return NULL;
+ }
+
+ ret_val = devm_ioremap(dev, addr, length);
+ if (!ret_val)
+ dev_err(dev, "Failed to map region %#llx-%#llx\n",
+ addr, addr+length);
+
+ return ret_val;
+}
+
int cxl_map_device_regs(struct pci_dev *pdev,
struct cxl_device_regs *regs,
struct cxl_register_map *map)
{
- struct device *dev = &pdev->dev;
resource_size_t phys_addr;

phys_addr = pci_resource_start(pdev, map->barno);
@@ -98,7 +120,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.status.offset;
length = map->device_map.status.size;
- regs->status = devm_ioremap(dev, addr, length);
+ regs->status = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->status)
+ return -ENOMEM;
}

if (map->device_map.mbox.valid) {
@@ -107,7 +131,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.mbox.offset;
length = map->device_map.mbox.size;
- regs->mbox = devm_ioremap(dev, addr, length);
+ regs->mbox = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->mbox)
+ return -ENOMEM;
}

if (map->device_map.memdev.valid) {
@@ -116,7 +142,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.memdev.offset;
length = map->device_map.memdev.size;
- regs->memdev = devm_ioremap(dev, addr, length);
+ regs->memdev = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->memdev)
+ return -ENOMEM;
}

return 0;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 3ffd5fad74b4..e1a2dbc2886b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1110,6 +1110,8 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
goto free_maps;
}

+ pci_release_mem_regions(pdev);
+
list_for_each_entry(map, &register_maps, list) {
ret = cxl_map_regs(cxlm, map);
if (ret)
--
2.28.0.rc0.12.gb6a658bd00c9

2021-05-28 04:07:57

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3 3/5] cxl/mem: Map registers based on capabilities

From: Ira Weiny <[email protected]>

The information required to map registers based on capabilities is
contained within the bars themselves. This means the bar must be mapped
to read the information needed and then unmapped to map the individual
parts of the BAR based on capabilities.

Change cxl_setup_device_regs() to return a new cxl_register_map, change
the name to cxl_probe_device_regs(). Allocate and place
cxl_register_maps on a list while processing all of the specified
register blocks.

After probing all the register blocks go back and map smaller registers
blocks based on their capabilities and dispose of the cxl_register_maps.

NOTE: pci_iomap() is not managed automatically via pcim_enable_device()
so be careful to call pci_iounmap() correctly.

Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>

---
Changes for V3
From Jonathan:
Add back the kernel doc comment and enhance it a bit
---
drivers/cxl/core.c | 73 +++++++++++++++++++++++++----
drivers/cxl/cxl.h | 33 ++++++++++++--
drivers/cxl/pci.c | 111 ++++++++++++++++++++++++++++++++++++---------
3 files changed, 181 insertions(+), 36 deletions(-)

diff --git a/drivers/cxl/core.c b/drivers/cxl/core.c
index 38979c97158d..ab8d6bce59c2 100644
--- a/drivers/cxl/core.c
+++ b/drivers/cxl/core.c
@@ -3,6 +3,7 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include "cxl.h"

/**
@@ -13,18 +14,20 @@
*/

/**
- * cxl_setup_device_regs() - Detect CXL Device register blocks
+ * cxl_probe_device_regs() - Detect CXL Device register blocks
* @dev: Host device of the @base mapping
* @base: Mapping of CXL 2.0 8.2.8 CXL Device Register Interface
- * @regs: Base pointers for device register blocks (see CXL_DEVICE_REGS())
+ * @map: Map object describing the register block information found
+ *
+ * Probe for device register information and return it in map object.
*/
-void cxl_setup_device_regs(struct device *dev, void __iomem *base,
- struct cxl_device_regs *regs)
+void cxl_probe_device_regs(struct device *dev, void __iomem *base,
+ struct cxl_device_reg_map *map)
{
int cap, cap_count;
u64 cap_array;

- *regs = (struct cxl_device_regs) { 0 };
+ *map = (struct cxl_device_reg_map){ 0 };

cap_array = readq(base + CXLDEV_CAP_ARRAY_OFFSET);
if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
@@ -35,29 +38,38 @@ void cxl_setup_device_regs(struct device *dev, void __iomem *base,

for (cap = 1; cap <= cap_count; cap++) {
void __iomem *register_block;
- u32 offset;
+ u32 offset, length;
u16 cap_id;

cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
readl(base + cap * 0x10));
offset = readl(base + cap * 0x10 + 0x4);
+ length = readl(base + cap * 0x10 + 0x8);
+
register_block = base + offset;

switch (cap_id) {
case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
dev_dbg(dev, "found Status capability (0x%x)\n", offset);
- regs->status = register_block;
+
+ map->status.valid = true;
+ map->status.offset = offset;
+ map->status.size = length;
break;
case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
- regs->mbox = register_block;
+ map->mbox.valid = true;
+ map->mbox.offset = offset;
+ map->mbox.size = length;
break;
case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX:
dev_dbg(dev, "found Secondary Mailbox capability (0x%x)\n", offset);
break;
case CXLDEV_CAP_CAP_ID_MEMDEV:
dev_dbg(dev, "found Memory Device capability (0x%x)\n", offset);
- regs->memdev = register_block;
+ map->memdev.valid = true;
+ map->memdev.offset = offset;
+ map->memdev.size = length;
break;
default:
if (cap_id >= 0x8000)
@@ -68,7 +80,48 @@ void cxl_setup_device_regs(struct device *dev, void __iomem *base,
}
}
}
-EXPORT_SYMBOL_GPL(cxl_setup_device_regs);
+EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
+
+int cxl_map_device_regs(struct pci_dev *pdev,
+ struct cxl_device_regs *regs,
+ struct cxl_register_map *map)
+{
+ struct device *dev = &pdev->dev;
+ resource_size_t phys_addr;
+
+ phys_addr = pci_resource_start(pdev, map->barno);
+ phys_addr += map->block_offset;
+
+ if (map->device_map.status.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.status.offset;
+ length = map->device_map.status.size;
+ regs->status = devm_ioremap(dev, addr, length);
+ }
+
+ if (map->device_map.mbox.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.mbox.offset;
+ length = map->device_map.mbox.size;
+ regs->mbox = devm_ioremap(dev, addr, length);
+ }
+
+ if (map->device_map.memdev.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.memdev.offset;
+ length = map->device_map.memdev.size;
+ regs->memdev = devm_ioremap(dev, addr, length);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_map_device_regs);

struct bus_type cxl_bus_type = {
.name = "cxl",
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index d49e0cb679fa..ae4b4c96c6b5 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -53,9 +53,7 @@ struct cxl_device_regs {
/*
* Note, the anonymous union organization allows for per
* register-block-type helper routines, without requiring block-type
- * agnostic code to include the prefix. I.e.
- * cxl_setup_device_regs(&cxlm->regs.dev) vs readl(cxlm->regs.mbox).
- * The specificity reads naturally from left-to-right.
+ * agnostic code to include the prefix.
*/
struct cxl_regs {
union {
@@ -66,8 +64,33 @@ struct cxl_regs {
};
};

-void cxl_setup_device_regs(struct device *dev, void __iomem *base,
- struct cxl_device_regs *regs);
+struct cxl_reg_map {
+ bool valid;
+ unsigned long offset;
+ unsigned long size;
+};
+
+struct cxl_device_reg_map {
+ struct cxl_reg_map status;
+ struct cxl_reg_map mbox;
+ struct cxl_reg_map memdev;
+};
+
+struct cxl_register_map {
+ struct list_head list;
+ u64 block_offset;
+ u8 reg_type;
+ u8 barno;
+ union {
+ struct cxl_device_reg_map device_map;
+ };
+};
+
+void cxl_probe_device_regs(struct device *dev, void __iomem *base,
+ struct cxl_device_reg_map *map);
+int cxl_map_device_regs(struct pci_dev *pdev,
+ struct cxl_device_regs *regs,
+ struct cxl_register_map *map);

extern struct bus_type cxl_bus_type;
#endif /* __CXL_H__ */
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 33fc6e1634e3..3ffd5fad74b4 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/sizes.h>
#include <linux/mutex.h>
+#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/pci.h>
@@ -936,7 +937,7 @@ static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
return IOMEM_ERR_PTR(-ENXIO);
}

- addr = pcim_iomap(pdev, bar, 0);
+ addr = pci_iomap(pdev, bar, 0);
if (!addr) {
dev_err(dev, "failed to map registers\n");
return addr;
@@ -945,7 +946,12 @@ static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %#llx\n",
bar, offset);

- return pcim_iomap_table(pdev)[bar] + offset;
+ return addr;
+}
+
+static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
+{
+ pci_iounmap(cxlm->pdev, base);
}

static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
@@ -971,6 +977,52 @@ static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
return 0;
}

+static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
+ struct cxl_register_map *map)
+{
+ struct pci_dev *pdev = cxlm->pdev;
+ struct device *dev = &pdev->dev;
+ struct cxl_device_reg_map *dev_map;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_MEMDEV:
+ dev_map = &map->device_map;
+ cxl_probe_device_regs(dev, base, dev_map);
+ if (!dev_map->status.valid || !dev_map->mbox.valid ||
+ !dev_map->memdev.valid) {
+ dev_err(dev, "registers not found: %s%s%s\n",
+ !dev_map->status.valid ? "status " : "",
+ !dev_map->mbox.valid ? "status " : "",
+ !dev_map->memdev.valid ? "status " : "");
+ return -ENXIO;
+ }
+
+ dev_dbg(dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
+{
+ struct pci_dev *pdev = cxlm->pdev;
+ struct device *dev = &pdev->dev;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_MEMDEV:
+ cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
+ dev_dbg(dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
u8 *bar, u64 *offset, u8 *reg_type)
{
@@ -991,12 +1043,14 @@ static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
*/
static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
{
- struct cxl_regs *regs = &cxlm->regs;
struct pci_dev *pdev = cxlm->pdev;
struct device *dev = &pdev->dev;
u32 regloc_size, regblocks;
void __iomem *base;
int regloc, i;
+ struct cxl_register_map *map, *n;
+ LIST_HEAD(register_maps);
+ int ret = 0;

regloc = cxl_mem_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_OFFSET);
if (!regloc) {
@@ -1020,7 +1074,14 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
u64 offset;
u8 bar;

- /* "register low and high" contain other bits */
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ ret = -ENOMEM;
+ goto free_maps;
+ }
+
+ list_add(&map->list, &register_maps);
+
pci_read_config_dword(pdev, regloc, &reg_lo);
pci_read_config_dword(pdev, regloc + 4, &reg_hi);

@@ -1030,30 +1091,38 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
dev_dbg(dev, "Found register block in bar %u @ 0x%llx of type %u\n",
bar, offset, reg_type);

- if (reg_type == CXL_REGLOC_RBI_MEMDEV) {
- base = cxl_mem_map_regblock(cxlm, bar, offset);
- if (!base)
- return -ENOMEM;
- break;
+ base = cxl_mem_map_regblock(cxlm, bar, offset);
+ if (!base) {
+ ret = -ENOMEM;
+ goto free_maps;
}
- }

- if (i == regblocks) {
- dev_err(dev, "Missing register locator for device registers\n");
- return -ENXIO;
+ map->barno = bar;
+ map->block_offset = offset;
+ map->reg_type = reg_type;
+
+ ret = cxl_probe_regs(cxlm, base + offset, map);
+
+ /* Always unmap the regblock regardless of probe success */
+ cxl_mem_unmap_regblock(cxlm, base);
+
+ if (ret)
+ goto free_maps;
}

- cxl_setup_device_regs(dev, base, &regs->device_regs);
+ list_for_each_entry(map, &register_maps, list) {
+ ret = cxl_map_regs(cxlm, map);
+ if (ret)
+ goto free_maps;
+ }

- if (!regs->status || !regs->mbox || !regs->memdev) {
- dev_err(dev, "registers not found: %s%s%s\n",
- !regs->status ? "status " : "",
- !regs->mbox ? "mbox " : "",
- !regs->memdev ? "memdev" : "");
- return -ENXIO;
+free_maps:
+ list_for_each_entry_safe(map, n, &register_maps, list) {
+ list_del(&map->list);
+ kfree(map);
}

- return 0;
+ return ret;
}

static struct cxl_memdev *to_cxl_memdev(struct device *dev)
--
2.28.0.rc0.12.gb6a658bd00c9

2021-05-28 04:27:34

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3 5/5] cxl/pci: Add HDM decoder capabilities

From: Ben Widawsky <[email protected]>

An HDM decoder is defined in the CXL 2.0 specification as a mechanism
that allow devices and upstream ports to claim memory address ranges and
participate in interleave sets. HDM decoder registers are within the
component register block defined in CXL 2.0 8.2.3 CXL 2.0 Component
Registers as part of the CXL.cache and CXL.mem subregion.

The Component Register Block is found via the Register Locator DVSEC
in a similar fashion to how the CXL Device Register Block is found. The
primary difference is the capability id size of the Component Register
Block is a single DWORD instead of 4 DWORDS.

It's now possible to configure a CXL type 3 device's HDM decoder. Such
programming is expected for CXL devices with persistent memory, and hot
plugged CXL devices that participate in CXL.mem with volatile memory.

Add probe and mapping functions for the component register blocks.

Reviewed-by: Jonathan Cameron <[email protected]>
Co-developed-by: Ira Weiny <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>
Co-developed-by: Vishal Verma <[email protected]>
Signed-off-by: Vishal Verma <[email protected]>
Signed-off-by: Ben Widawsky <[email protected]>

---
Changes for V3:
Johnathan
Remove CXL_REGLOC_RBI_MAX
Add kernel doc comment for cxl_probe_component_regs()
Update to devm_cxl_iomap_block() call.
---
drivers/cxl/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++
drivers/cxl/cxl.h | 65 +++++++++++++++++++++++++++++---
drivers/cxl/pci.c | 15 ++++++++
3 files changed, 166 insertions(+), 6 deletions(-)

diff --git a/drivers/cxl/core.c b/drivers/cxl/core.c
index 2b5aac6f76f2..f41a38e87606 100644
--- a/drivers/cxl/core.c
+++ b/drivers/cxl/core.c
@@ -13,6 +13,78 @@
* point for cross-device interleave coordination through cxl ports.
*/

+/**
+ * cxl_probe_component_regs() - Detect CXL Component register blocks
+ * @dev: Host device of the @base mapping
+ * @base: Mapping containing the HDM Decoder Capability Header
+ * @map: Map object describing the register block information found
+ *
+ * See CXL 2.0 8.2.4 Component Register Layout and Definition
+ * See CXL 2.0 8.2.5.5 CXL Device Register Interface
+ *
+ * Probe for component register information and return it in map object.
+ */
+void cxl_probe_component_regs(struct device *dev, void __iomem *base,
+ struct cxl_component_reg_map *map)
+{
+ int cap, cap_count;
+ u64 cap_array;
+
+ *map = (struct cxl_component_reg_map) { 0 };
+
+ /*
+ * CXL.cache and CXL.mem registers are at offset 0x1000 as defined in
+ * CXL 2.0 8.2.4 Table 141.
+ */
+ base += CXL_CM_OFFSET;
+
+ cap_array = readq(base + CXL_CM_CAP_HDR_OFFSET);
+
+ if (FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, cap_array) != CM_CAP_HDR_CAP_ID) {
+ dev_err(dev,
+ "Couldn't locate the CXL.cache and CXL.mem capability array header./n");
+ return;
+ }
+
+ /* It's assumed that future versions will be backward compatible */
+ cap_count = FIELD_GET(CXL_CM_CAP_HDR_ARRAY_SIZE_MASK, cap_array);
+
+ for (cap = 1; cap <= cap_count; cap++) {
+ void __iomem *register_block;
+ u32 hdr;
+ int decoder_cnt;
+ u16 cap_id, offset;
+ u32 length;
+
+ hdr = readl(base + cap * 0x4);
+
+ cap_id = FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, hdr);
+ offset = FIELD_GET(CXL_CM_CAP_PTR_MASK, hdr);
+ register_block = base + offset;
+
+ switch (cap_id) {
+ case CXL_CM_CAP_CAP_ID_HDM:
+ dev_dbg(dev, "found HDM decoder capability (0x%x)\n",
+ offset);
+
+ hdr = readl(register_block);
+
+ decoder_cnt = FIELD_GET(CXL_HDM_DECODER_COUNT_MASK, hdr);
+ length = 0x20 * decoder_cnt + 0x10;
+
+ map->hdm_decoder.valid = true;
+ map->hdm_decoder.offset = offset;
+ map->hdm_decoder.size = length;
+ break;
+ default:
+ dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
+ offset);
+ break;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(cxl_probe_component_regs);
+
/**
* cxl_probe_device_regs() - Detect CXL Device register blocks
* @dev: Host device of the @base mapping
@@ -105,6 +177,26 @@ static void __iomem *devm_cxl_iomap_block(struct pci_dev *pdev,
return ret_val;
}

+int cxl_map_component_regs(struct pci_dev *pdev,
+ struct cxl_component_regs *regs,
+ struct cxl_register_map *map)
+{
+ resource_size_t phys_addr;
+ resource_size_t length;
+
+ phys_addr = pci_resource_start(pdev, map->barno);
+ phys_addr += map->block_offset;
+
+ phys_addr += map->component_map.hdm_decoder.offset;
+ length = map->component_map.hdm_decoder.size;
+ regs->hdm_decoder = devm_cxl_iomap_block(pdev, phys_addr, length);
+ if (!regs->hdm_decoder)
+ return -ENOMEM;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_map_component_regs);
+
int cxl_map_device_regs(struct pci_dev *pdev,
struct cxl_device_regs *regs,
struct cxl_register_map *map)
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index ae4b4c96c6b5..2c47e9cffd44 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -8,6 +8,31 @@
#include <linux/bitops.h>
#include <linux/io.h>

+/* CXL 2.0 8.2.5 CXL.cache and CXL.mem Registers*/
+#define CXL_CM_OFFSET 0x1000
+#define CXL_CM_CAP_HDR_OFFSET 0x0
+#define CXL_CM_CAP_HDR_ID_MASK GENMASK(15, 0)
+#define CM_CAP_HDR_CAP_ID 1
+#define CXL_CM_CAP_HDR_VERSION_MASK GENMASK(19, 16)
+#define CM_CAP_HDR_CAP_VERSION 1
+#define CXL_CM_CAP_HDR_CACHE_MEM_VERSION_MASK GENMASK(23, 20)
+#define CM_CAP_HDR_CACHE_MEM_VERSION 1
+#define CXL_CM_CAP_HDR_ARRAY_SIZE_MASK GENMASK(31, 24)
+#define CXL_CM_CAP_PTR_MASK GENMASK(31, 20)
+
+#define CXL_CM_CAP_CAP_ID_HDM 0x5
+#define CXL_CM_CAP_CAP_HDM_VERSION 1
+
+/* HDM decoders CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure */
+#define CXL_HDM_DECODER_CAP_OFFSET 0x0
+#define CXL_HDM_DECODER_COUNT_MASK GENMASK(3, 0)
+#define CXL_HDM_DECODER_TARGET_COUNT_MASK GENMASK(7, 4)
+#define CXL_HDM_DECODER0_BASE_LOW_OFFSET 0x10
+#define CXL_HDM_DECODER0_BASE_HIGH_OFFSET 0x14
+#define CXL_HDM_DECODER0_SIZE_LOW_OFFSET 0x18
+#define CXL_HDM_DECODER0_SIZE_HIGH_OFFSET 0x1c
+#define CXL_HDM_DECODER0_CTRL_OFFSET 0x20
+
/* CXL 2.0 8.2.8.1 Device Capabilities Array Register */
#define CXLDEV_CAP_ARRAY_OFFSET 0x0
#define CXLDEV_CAP_ARRAY_CAP_ID 0
@@ -34,18 +59,30 @@
#define CXLDEV_MBOX_BG_CMD_STATUS_OFFSET 0x18
#define CXLDEV_MBOX_PAYLOAD_OFFSET 0x20

-/*
- * CXL_DEVICE_REGS - Common set of CXL Device register block base pointers
- * @status: CXL 2.0 8.2.8.3 Device Status Registers
- * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers
- * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers
- */
+#define CXL_COMPONENT_REGS() \
+ void __iomem *hdm_decoder
+
#define CXL_DEVICE_REGS() \
void __iomem *status; \
void __iomem *mbox; \
void __iomem *memdev

/* See note for 'struct cxl_regs' for the rationale of this organization */
+/*
+ * CXL_COMPONENT_REGS - Common set of CXL Component register block base pointers
+ * @hdm_decoder: CXL 2.0 8.2.5.12 CXL HDM Decoder Capability Structure
+ */
+struct cxl_component_regs {
+ CXL_COMPONENT_REGS();
+};
+
+/* See note for 'struct cxl_regs' for the rationale of this organization */
+/*
+ * CXL_DEVICE_REGS - Common set of CXL Device register block base pointers
+ * @status: CXL 2.0 8.2.8.3 Device Status Registers
+ * @mbox: CXL 2.0 8.2.8.4 Mailbox Registers
+ * @memdev: CXL 2.0 8.2.8.5 Memory Device Registers
+ */
struct cxl_device_regs {
CXL_DEVICE_REGS();
};
@@ -56,6 +93,12 @@ struct cxl_device_regs {
* agnostic code to include the prefix.
*/
struct cxl_regs {
+ union {
+ struct {
+ CXL_COMPONENT_REGS();
+ };
+ struct cxl_component_regs component;
+ };
union {
struct {
CXL_DEVICE_REGS();
@@ -70,6 +113,10 @@ struct cxl_reg_map {
unsigned long size;
};

+struct cxl_component_reg_map {
+ struct cxl_reg_map hdm_decoder;
+};
+
struct cxl_device_reg_map {
struct cxl_reg_map status;
struct cxl_reg_map mbox;
@@ -82,12 +129,18 @@ struct cxl_register_map {
u8 reg_type;
u8 barno;
union {
+ struct cxl_component_reg_map component_map;
struct cxl_device_reg_map device_map;
};
};

+void cxl_probe_component_regs(struct device *dev, void __iomem *base,
+ struct cxl_component_reg_map *map);
void cxl_probe_device_regs(struct device *dev, void __iomem *base,
struct cxl_device_reg_map *map);
+int cxl_map_component_regs(struct pci_dev *pdev,
+ struct cxl_component_regs *regs,
+ struct cxl_register_map *map);
int cxl_map_device_regs(struct pci_dev *pdev,
struct cxl_device_regs *regs,
struct cxl_register_map *map);
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index e1a2dbc2886b..5a1705b52278 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -982,9 +982,20 @@ static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
{
struct pci_dev *pdev = cxlm->pdev;
struct device *dev = &pdev->dev;
+ struct cxl_component_reg_map *comp_map;
struct cxl_device_reg_map *dev_map;

switch (map->reg_type) {
+ case CXL_REGLOC_RBI_COMPONENT:
+ comp_map = &map->component_map;
+ cxl_probe_component_regs(dev, base, comp_map);
+ if (!comp_map->hdm_decoder.valid) {
+ dev_err(dev, "HDM decoder registers not found\n");
+ return -ENXIO;
+ }
+
+ dev_dbg(dev, "Set up component registers\n");
+ break;
case CXL_REGLOC_RBI_MEMDEV:
dev_map = &map->device_map;
cxl_probe_device_regs(dev, base, dev_map);
@@ -1012,6 +1023,10 @@ static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
struct device *dev = &pdev->dev;

switch (map->reg_type) {
+ case CXL_REGLOC_RBI_COMPONENT:
+ cxl_map_component_regs(pdev, &cxlm->regs.component, map);
+ dev_dbg(dev, "Mapping component registers...\n");
+ break;
case CXL_REGLOC_RBI_MEMDEV:
cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
dev_dbg(dev, "Probing device registers...\n");
--
2.28.0.rc0.12.gb6a658bd00c9

2021-06-04 00:53:35

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3.1] cxl/mem: Map registers based on capabilities

From: Ira Weiny <[email protected]>

The information required to map registers based on capabilities is
contained within the bars themselves. This means the bar must be mapped
to read the information needed and then unmapped to map the individual
parts of the BAR based on capabilities.

Change cxl_setup_device_regs() to return a new cxl_register_map, change
the name to cxl_probe_device_regs(). Allocate and place
cxl_register_maps on a list while processing all of the specified
register blocks.

After probing all the register blocks go back and map smaller registers
blocks based on their capabilities and dispose of the cxl_register_maps.

NOTE: pci_iomap() is not managed automatically via pcim_enable_device()
so be careful to call pci_iounmap() correctly.

Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Dan Williams <[email protected]>

---
Changes for V3.1
Fix 0-day

>> drivers/cxl/core.c:40:17: warning: variable 'register_block' set but not used [-Wunused-but-set-variable]
40 | void __iomem *register_block;
---
drivers/cxl/core.c | 74 +++++++++++++++++++++++++-----
drivers/cxl/cxl.h | 33 ++++++++++++--
drivers/cxl/pci.c | 111 ++++++++++++++++++++++++++++++++++++---------
3 files changed, 180 insertions(+), 38 deletions(-)

diff --git a/drivers/cxl/core.c b/drivers/cxl/core.c
index 38979c97158d..f836aaab03e0 100644
--- a/drivers/cxl/core.c
+++ b/drivers/cxl/core.c
@@ -3,6 +3,7 @@
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include "cxl.h"

/**
@@ -13,18 +14,20 @@
*/

/**
- * cxl_setup_device_regs() - Detect CXL Device register blocks
+ * cxl_probe_device_regs() - Detect CXL Device register blocks
* @dev: Host device of the @base mapping
* @base: Mapping of CXL 2.0 8.2.8 CXL Device Register Interface
- * @regs: Base pointers for device register blocks (see CXL_DEVICE_REGS())
+ * @map: Map object describing the register block information found
+ *
+ * Probe for device register information and return it in map object.
*/
-void cxl_setup_device_regs(struct device *dev, void __iomem *base,
- struct cxl_device_regs *regs)
+void cxl_probe_device_regs(struct device *dev, void __iomem *base,
+ struct cxl_device_reg_map *map)
{
int cap, cap_count;
u64 cap_array;

- *regs = (struct cxl_device_regs) { 0 };
+ *map = (struct cxl_device_reg_map){ 0 };

cap_array = readq(base + CXLDEV_CAP_ARRAY_OFFSET);
if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
@@ -34,30 +37,36 @@ void cxl_setup_device_regs(struct device *dev, void __iomem *base,
cap_count = FIELD_GET(CXLDEV_CAP_ARRAY_COUNT_MASK, cap_array);

for (cap = 1; cap <= cap_count; cap++) {
- void __iomem *register_block;
- u32 offset;
+ u32 offset, length;
u16 cap_id;

cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
readl(base + cap * 0x10));
offset = readl(base + cap * 0x10 + 0x4);
- register_block = base + offset;
+ length = readl(base + cap * 0x10 + 0x8);

switch (cap_id) {
case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
dev_dbg(dev, "found Status capability (0x%x)\n", offset);
- regs->status = register_block;
+
+ map->status.valid = true;
+ map->status.offset = offset;
+ map->status.size = length;
break;
case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
- regs->mbox = register_block;
+ map->mbox.valid = true;
+ map->mbox.offset = offset;
+ map->mbox.size = length;
break;
case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX:
dev_dbg(dev, "found Secondary Mailbox capability (0x%x)\n", offset);
break;
case CXLDEV_CAP_CAP_ID_MEMDEV:
dev_dbg(dev, "found Memory Device capability (0x%x)\n", offset);
- regs->memdev = register_block;
+ map->memdev.valid = true;
+ map->memdev.offset = offset;
+ map->memdev.size = length;
break;
default:
if (cap_id >= 0x8000)
@@ -68,7 +77,48 @@ void cxl_setup_device_regs(struct device *dev, void __iomem *base,
}
}
}
-EXPORT_SYMBOL_GPL(cxl_setup_device_regs);
+EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
+
+int cxl_map_device_regs(struct pci_dev *pdev,
+ struct cxl_device_regs *regs,
+ struct cxl_register_map *map)
+{
+ struct device *dev = &pdev->dev;
+ resource_size_t phys_addr;
+
+ phys_addr = pci_resource_start(pdev, map->barno);
+ phys_addr += map->block_offset;
+
+ if (map->device_map.status.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.status.offset;
+ length = map->device_map.status.size;
+ regs->status = devm_ioremap(dev, addr, length);
+ }
+
+ if (map->device_map.mbox.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.mbox.offset;
+ length = map->device_map.mbox.size;
+ regs->mbox = devm_ioremap(dev, addr, length);
+ }
+
+ if (map->device_map.memdev.valid) {
+ resource_size_t addr;
+ resource_size_t length;
+
+ addr = phys_addr + map->device_map.memdev.offset;
+ length = map->device_map.memdev.size;
+ regs->memdev = devm_ioremap(dev, addr, length);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cxl_map_device_regs);

struct bus_type cxl_bus_type = {
.name = "cxl",
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index d49e0cb679fa..ae4b4c96c6b5 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -53,9 +53,7 @@ struct cxl_device_regs {
/*
* Note, the anonymous union organization allows for per
* register-block-type helper routines, without requiring block-type
- * agnostic code to include the prefix. I.e.
- * cxl_setup_device_regs(&cxlm->regs.dev) vs readl(cxlm->regs.mbox).
- * The specificity reads naturally from left-to-right.
+ * agnostic code to include the prefix.
*/
struct cxl_regs {
union {
@@ -66,8 +64,33 @@ struct cxl_regs {
};
};

-void cxl_setup_device_regs(struct device *dev, void __iomem *base,
- struct cxl_device_regs *regs);
+struct cxl_reg_map {
+ bool valid;
+ unsigned long offset;
+ unsigned long size;
+};
+
+struct cxl_device_reg_map {
+ struct cxl_reg_map status;
+ struct cxl_reg_map mbox;
+ struct cxl_reg_map memdev;
+};
+
+struct cxl_register_map {
+ struct list_head list;
+ u64 block_offset;
+ u8 reg_type;
+ u8 barno;
+ union {
+ struct cxl_device_reg_map device_map;
+ };
+};
+
+void cxl_probe_device_regs(struct device *dev, void __iomem *base,
+ struct cxl_device_reg_map *map);
+int cxl_map_device_regs(struct pci_dev *pdev,
+ struct cxl_device_regs *regs,
+ struct cxl_register_map *map);

extern struct bus_type cxl_bus_type;
#endif /* __CXL_H__ */
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 33fc6e1634e3..3ffd5fad74b4 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/sizes.h>
#include <linux/mutex.h>
+#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/idr.h>
#include <linux/pci.h>
@@ -936,7 +937,7 @@ static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
return IOMEM_ERR_PTR(-ENXIO);
}

- addr = pcim_iomap(pdev, bar, 0);
+ addr = pci_iomap(pdev, bar, 0);
if (!addr) {
dev_err(dev, "failed to map registers\n");
return addr;
@@ -945,7 +946,12 @@ static void __iomem *cxl_mem_map_regblock(struct cxl_mem *cxlm,
dev_dbg(dev, "Mapped CXL Memory Device resource bar %u @ %#llx\n",
bar, offset);

- return pcim_iomap_table(pdev)[bar] + offset;
+ return addr;
+}
+
+static void cxl_mem_unmap_regblock(struct cxl_mem *cxlm, void __iomem *base)
+{
+ pci_iounmap(cxlm->pdev, base);
}

static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
@@ -971,6 +977,52 @@ static int cxl_mem_dvsec(struct pci_dev *pdev, int dvsec)
return 0;
}

+static int cxl_probe_regs(struct cxl_mem *cxlm, void __iomem *base,
+ struct cxl_register_map *map)
+{
+ struct pci_dev *pdev = cxlm->pdev;
+ struct device *dev = &pdev->dev;
+ struct cxl_device_reg_map *dev_map;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_MEMDEV:
+ dev_map = &map->device_map;
+ cxl_probe_device_regs(dev, base, dev_map);
+ if (!dev_map->status.valid || !dev_map->mbox.valid ||
+ !dev_map->memdev.valid) {
+ dev_err(dev, "registers not found: %s%s%s\n",
+ !dev_map->status.valid ? "status " : "",
+ !dev_map->mbox.valid ? "status " : "",
+ !dev_map->memdev.valid ? "status " : "");
+ return -ENXIO;
+ }
+
+ dev_dbg(dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
+{
+ struct pci_dev *pdev = cxlm->pdev;
+ struct device *dev = &pdev->dev;
+
+ switch (map->reg_type) {
+ case CXL_REGLOC_RBI_MEMDEV:
+ cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
+ dev_dbg(dev, "Probing device registers...\n");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
u8 *bar, u64 *offset, u8 *reg_type)
{
@@ -991,12 +1043,14 @@ static void cxl_decode_register_block(u32 reg_lo, u32 reg_hi,
*/
static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
{
- struct cxl_regs *regs = &cxlm->regs;
struct pci_dev *pdev = cxlm->pdev;
struct device *dev = &pdev->dev;
u32 regloc_size, regblocks;
void __iomem *base;
int regloc, i;
+ struct cxl_register_map *map, *n;
+ LIST_HEAD(register_maps);
+ int ret = 0;

regloc = cxl_mem_dvsec(pdev, PCI_DVSEC_ID_CXL_REGLOC_OFFSET);
if (!regloc) {
@@ -1020,7 +1074,14 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
u64 offset;
u8 bar;

- /* "register low and high" contain other bits */
+ map = kzalloc(sizeof(*map), GFP_KERNEL);
+ if (!map) {
+ ret = -ENOMEM;
+ goto free_maps;
+ }
+
+ list_add(&map->list, &register_maps);
+
pci_read_config_dword(pdev, regloc, &reg_lo);
pci_read_config_dword(pdev, regloc + 4, &reg_hi);

@@ -1030,30 +1091,38 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
dev_dbg(dev, "Found register block in bar %u @ 0x%llx of type %u\n",
bar, offset, reg_type);

- if (reg_type == CXL_REGLOC_RBI_MEMDEV) {
- base = cxl_mem_map_regblock(cxlm, bar, offset);
- if (!base)
- return -ENOMEM;
- break;
+ base = cxl_mem_map_regblock(cxlm, bar, offset);
+ if (!base) {
+ ret = -ENOMEM;
+ goto free_maps;
}
- }

- if (i == regblocks) {
- dev_err(dev, "Missing register locator for device registers\n");
- return -ENXIO;
+ map->barno = bar;
+ map->block_offset = offset;
+ map->reg_type = reg_type;
+
+ ret = cxl_probe_regs(cxlm, base + offset, map);
+
+ /* Always unmap the regblock regardless of probe success */
+ cxl_mem_unmap_regblock(cxlm, base);
+
+ if (ret)
+ goto free_maps;
}

- cxl_setup_device_regs(dev, base, &regs->device_regs);
+ list_for_each_entry(map, &register_maps, list) {
+ ret = cxl_map_regs(cxlm, map);
+ if (ret)
+ goto free_maps;
+ }

- if (!regs->status || !regs->mbox || !regs->memdev) {
- dev_err(dev, "registers not found: %s%s%s\n",
- !regs->status ? "status " : "",
- !regs->mbox ? "mbox " : "",
- !regs->memdev ? "memdev" : "");
- return -ENXIO;
+free_maps:
+ list_for_each_entry_safe(map, n, &register_maps, list) {
+ list_del(&map->list);
+ kfree(map);
}

- return 0;
+ return ret;
}

static struct cxl_memdev *to_cxl_memdev(struct device *dev)
--
2.28.0.rc0.12.gb6a658bd00c9

2021-06-04 00:55:12

by Ira Weiny

[permalink] [raw]
Subject: [PATCH V3.1] cxl/mem: Reserve individual register block regions

From: Ira Weiny <[email protected]>

Some hardware implementations mix component and device registers into
the same BAR and the driver stack is going to need independent mapping
implementations for those 2 cases. Furthermore, it will be nice to have
finer grained mappings should user space want to map some register
blocks.

Now that individual register blocks are mapped; those blocks regions
should be reserved individually to fully separate the register blocks.

Release the 'global' memory reservation and create individual register
block region reservations through devm.

NOTE: pci_release_mem_regions() is still compatible with
pcim_enable_device() because it removes the automatic region release
when called. So preserve the pcim_enable_device() so that the pcim
interface can be called if needed.

Reviewed-by: Jonathan Cameron <[email protected]>
Signed-off-by: Ira Weiny <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Dan Williams <[email protected]>

---
Changes for v3.1

Fix 0-day warnings:

>> drivers/cxl/core.c:96:4: warning: format specifies type 'unsigned long long' but the argument has type 'resource_size_t' (aka 'unsigned int') [-Wformat]
addr, addr+length);
---
drivers/cxl/core.c | 36 ++++++++++++++++++++++++++++++++----
drivers/cxl/pci.c | 2 ++
2 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/drivers/cxl/core.c b/drivers/cxl/core.c
index f836aaab03e0..c1efa11207b5 100644
--- a/drivers/cxl/core.c
+++ b/drivers/cxl/core.c
@@ -79,11 +79,33 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
}
EXPORT_SYMBOL_GPL(cxl_probe_device_regs);

+static void __iomem *devm_cxl_iomap_block(struct pci_dev *pdev,
+ resource_size_t addr,
+ resource_size_t length)
+{
+ struct device *dev = &pdev->dev;
+ void __iomem *ret_val;
+ struct resource *res;
+
+ res = devm_request_mem_region(dev, addr, length, pci_name(pdev));
+ if (!res) {
+ resource_size_t end = addr + length - 1;
+
+ dev_err(dev, "Failed to request region %pa-%pa\n", &addr, &end);
+ return NULL;
+ }
+
+ ret_val = devm_ioremap(dev, addr, length);
+ if (!ret_val)
+ dev_err(dev, "Failed to map region %pr\n", res);
+
+ return ret_val;
+}
+
int cxl_map_device_regs(struct pci_dev *pdev,
struct cxl_device_regs *regs,
struct cxl_register_map *map)
{
- struct device *dev = &pdev->dev;
resource_size_t phys_addr;

phys_addr = pci_resource_start(pdev, map->barno);
@@ -95,7 +117,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.status.offset;
length = map->device_map.status.size;
- regs->status = devm_ioremap(dev, addr, length);
+ regs->status = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->status)
+ return -ENOMEM;
}

if (map->device_map.mbox.valid) {
@@ -104,7 +128,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.mbox.offset;
length = map->device_map.mbox.size;
- regs->mbox = devm_ioremap(dev, addr, length);
+ regs->mbox = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->mbox)
+ return -ENOMEM;
}

if (map->device_map.memdev.valid) {
@@ -113,7 +139,9 @@ int cxl_map_device_regs(struct pci_dev *pdev,

addr = phys_addr + map->device_map.memdev.offset;
length = map->device_map.memdev.size;
- regs->memdev = devm_ioremap(dev, addr, length);
+ regs->memdev = devm_cxl_iomap_block(pdev, addr, length);
+ if (!regs->memdev)
+ return -ENOMEM;
}

return 0;
diff --git a/drivers/cxl/pci.c b/drivers/cxl/pci.c
index 3ffd5fad74b4..e1a2dbc2886b 100644
--- a/drivers/cxl/pci.c
+++ b/drivers/cxl/pci.c
@@ -1110,6 +1110,8 @@ static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
goto free_maps;
}

+ pci_release_mem_regions(pdev);
+
list_for_each_entry(map, &register_maps, list) {
ret = cxl_map_regs(cxlm, map);
if (ret)
--
2.28.0.rc0.12.gb6a658bd00c9

2021-06-06 00:34:52

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH V3.1] cxl/mem: Map registers based on capabilities

On Thu, Jun 3, 2021 at 5:51 PM <[email protected]> wrote:
>
> From: Ira Weiny <[email protected]>
>
> The information required to map registers based on capabilities is
> contained within the bars themselves. This means the bar must be mapped
> to read the information needed and then unmapped to map the individual
> parts of the BAR based on capabilities.
>
> Change cxl_setup_device_regs() to return a new cxl_register_map, change
> the name to cxl_probe_device_regs(). Allocate and place
> cxl_register_maps on a list while processing all of the specified
> register blocks.
>
> After probing all the register blocks go back and map smaller registers
> blocks based on their capabilities and dispose of the cxl_register_maps.
>
> NOTE: pci_iomap() is not managed automatically via pcim_enable_device()
> so be careful to call pci_iounmap() correctly.
>
> Reviewed-by: Jonathan Cameron <[email protected]>
> Signed-off-by: Ira Weiny <[email protected]>
> Link: https://lore.kernel.org/r/[email protected]
> Signed-off-by: Dan Williams <[email protected]>
>
> ---
> Changes for V3.1
> Fix 0-day
>
> >> drivers/cxl/core.c:40:17: warning: variable 'register_block' set but not used [-Wunused-but-set-variable]
> 40 | void __iomem *register_block;

Just a note for next time if you want to be friendly to "b4 am" using
maintainers. Roll the version and include the patch number in the
resend, i.e.: in this case: "[PATCH v4 3/5] cxl/mem: Map registers
based on capabilities". I was able to manually create a bundle in the
proper order on patchwork for the two "3.1" updates.