#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_DEVICE_REGS() \
+       void __iomem *status; \
+       void __iomem *mbox; \
+       void __iomem *memdev
+
+/* See note for 'struct cxl_regs' for the rationale of this organization */
+struct cxl_device_regs {
+       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.
+ */
+struct cxl_regs {
+       union {
+               struct {
+                       CXL_DEVICE_REGS();
+               };
+               struct cxl_device_regs device_regs;
+       };
+};
+
 extern struct bus_type cxl_bus_type;
 #endif /* __CXL_H__ */
 
  */
 
 #define cxl_doorbell_busy(cxlm)                                                \
-       (readl((cxlm)->mbox_regs + CXLDEV_MBOX_CTRL_OFFSET) &                  \
+       (readl((cxlm)->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET) &                  \
         CXLDEV_MBOX_CTRL_DOORBELL)
 
 /* CXL 2.0 - 8.2.8.4 */
 static int __cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm,
                                   struct mbox_cmd *mbox_cmd)
 {
-       void __iomem *payload = cxlm->mbox_regs + CXLDEV_MBOX_PAYLOAD_OFFSET;
+       void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
        u64 cmd_reg, status_reg;
        size_t out_len;
        int rc;
        }
 
        /* #2, #3 */
-       writeq(cmd_reg, cxlm->mbox_regs + CXLDEV_MBOX_CMD_OFFSET);
+       writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
 
        /* #4 */
        dev_dbg(&cxlm->pdev->dev, "Sending command\n");
        writel(CXLDEV_MBOX_CTRL_DOORBELL,
-              cxlm->mbox_regs + CXLDEV_MBOX_CTRL_OFFSET);
+              cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
 
        /* #5 */
        rc = cxl_mem_wait_for_doorbell(cxlm);
        }
 
        /* #6 */
-       status_reg = readq(cxlm->mbox_regs + CXLDEV_MBOX_STATUS_OFFSET);
+       status_reg = readq(cxlm->regs.mbox + CXLDEV_MBOX_STATUS_OFFSET);
        mbox_cmd->return_code =
                FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
 
        }
 
        /* #7 */
-       cmd_reg = readq(cxlm->mbox_regs + CXLDEV_MBOX_CMD_OFFSET);
+       cmd_reg = readq(cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
        out_len = FIELD_GET(CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK, cmd_reg);
 
        /* #8 */
                goto out;
        }
 
-       md_status = readq(cxlm->memdev_regs + CXLMDEV_STATUS_OFFSET);
+       md_status = readq(cxlm->regs.memdev + CXLMDEV_STATUS_OFFSET);
        if (!(md_status & CXLMDEV_MBOX_IF_READY && CXLMDEV_READY(md_status))) {
                dev_err(dev, "mbox: reported doorbell ready, but not mbox ready\n");
                rc = -EBUSY;
        int cap, cap_count;
        u64 cap_array;
 
-       cap_array = readq(cxlm->regs + CXLDEV_CAP_ARRAY_OFFSET);
+       cap_array = readq(cxlm->base + CXLDEV_CAP_ARRAY_OFFSET);
        if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
            CXLDEV_CAP_ARRAY_CAP_ID)
                return -ENODEV;
                u16 cap_id;
 
                cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
-                                  readl(cxlm->regs + cap * 0x10));
-               offset = readl(cxlm->regs + cap * 0x10 + 0x4);
-               register_block = cxlm->regs + offset;
+                                  readl(cxlm->base + cap * 0x10));
+               offset = readl(cxlm->base + cap * 0x10 + 0x4);
+               register_block = cxlm->base + offset;
 
                switch (cap_id) {
                case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
                        dev_dbg(dev, "found Status capability (0x%x)\n", offset);
-                       cxlm->status_regs = register_block;
+                       cxlm->regs.status = register_block;
                        break;
                case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
                        dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
-                       cxlm->mbox_regs = register_block;
+                       cxlm->regs.mbox = register_block;
                        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);
-                       cxlm->memdev_regs = register_block;
+                       cxlm->regs.memdev = register_block;
                        break;
                default:
                        dev_dbg(dev, "Unknown cap ID: %d (0x%x)\n", cap_id, offset);
                }
        }
 
-       if (!cxlm->status_regs || !cxlm->mbox_regs || !cxlm->memdev_regs) {
+       if (!cxlm->regs.status || !cxlm->regs.mbox || !cxlm->regs.memdev) {
                dev_err(dev, "registers not found: %s%s%s\n",
-                       !cxlm->status_regs ? "status " : "",
-                       !cxlm->mbox_regs ? "mbox " : "",
-                       !cxlm->memdev_regs ? "memdev" : "");
+                       !cxlm->regs.status ? "status " : "",
+                       !cxlm->regs.mbox ? "mbox " : "",
+                       !cxlm->regs.memdev ? "memdev" : "");
                return -ENXIO;
        }
 
 
 static int cxl_mem_setup_mailbox(struct cxl_mem *cxlm)
 {
-       const int cap = readl(cxlm->mbox_regs + CXLDEV_MBOX_CAPS_OFFSET);
+       const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
 
        cxlm->payload_size =
                1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);
 
        mutex_init(&cxlm->mbox_mutex);
        cxlm->pdev = pdev;
-       cxlm->regs = regs + offset;
+       cxlm->base = regs + offset;
        cxlm->enabled_cmds =
                devm_kmalloc_array(dev, BITS_TO_LONGS(cxl_cmd_count),
                                   sizeof(unsigned long),
        dev_t devt;
        int rc;
 
+       /* Double check the anonymous union trickery in struct cxl_regs */
+       BUILD_BUG_ON(offsetof(struct cxl_regs, memdev) !=
+                    offsetof(struct cxl_regs, device_regs.memdev));
+
        rc = alloc_chrdev_region(&devt, 0, CXL_MEM_MAX_DEVS, "cxl");
        if (rc)
                return rc;
 
 /**
  * struct cxl_mem - A CXL memory device
  * @pdev: The PCI device associated with this CXL device.
- * @regs: IO mappings to the device's MMIO
- * @status_regs: CXL 2.0 8.2.8.3 Device Status Registers
- * @mbox_regs: CXL 2.0 8.2.8.4 Mailbox Registers
- * @memdev_regs: CXL 2.0 8.2.8.5 Memory Device Registers
+ * @base: IO mappings to the device's MMIO
+ * @cxlmd: Logical memory device chardev / interface
+ * @regs: Parsed register blocks
  * @payload_size: Size of space for payload
  *                (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register)
  * @mbox_mutex: Mutex to synchronize mailbox access.
  */
 struct cxl_mem {
        struct pci_dev *pdev;
-       void __iomem *regs;
+       void __iomem *base;
        struct cxl_memdev *cxlmd;
 
-       void __iomem *status_regs;
-       void __iomem *mbox_regs;
-       void __iomem *memdev_regs;
+       struct cxl_regs regs;
 
        size_t payload_size;
        struct mutex mbox_mutex; /* Protects device mailbox and firmware */