Skip to content
Permalink
Browse files
cxl/mem: Find device capabilities
CXL devices contain an array of capabilities that describe the
interactions software can interact with the device, or firmware running
on the device. A CXL compliant device must implement the device status
and the mailbox capability. A CXL compliant memory device must implement
the memory device capability.

Each of the capabilities can [will] provide an offset within the MMIO
region for interacting with the CXL device.

Signed-off-by: Ben Widawsky <ben.widawsky@intel.com>
  • Loading branch information
bwidawsk authored and intel-lab-lkp committed Nov 11, 2020
1 parent 2b4b3b0 commit 03316f5eab47413b046c08f6b977867dfade36f9
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 4 deletions.
@@ -0,0 +1,89 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright(c) 2020 Intel Corporation. All rights reserved.

#ifndef __CXL_H__
#define __CXL_H__

/* Device */
#define CXLDEV_CAP_ARRAY_REG 0x0
#define CXLDEV_CAP_ARRAY_CAP_ID 0
#define CXLDEV_CAP_ARRAY_ID(x) ((x) & 0xffff)
#define CXLDEV_CAP_ARRAY_COUNT(x) (((x) >> 32) & 0xffff)

#define CXL_CAPABILITIES_CAP_ID_DEVICE_STATUS 1
#define CXL_CAPABILITIES_CAP_ID_PRIMARY_MAILBOX 2
#define CXL_CAPABILITIES_CAP_ID_SECONDARY_MAILBOX 3
#define CXL_CAPABILITIES_CAP_ID_MEMDEV 0x4000

/* Mailbox */
#define CXLDEV_MB_CAPS 0x00
#define CXLDEV_MB_CAP_PAYLOAD_SIZE(cap) ((cap) & 0x1F)
#define CXLDEV_MB_CTRL 0x04
#define CXLDEV_MB_CMD 0x08
#define CXLDEV_MB_STATUS 0x10
#define CXLDEV_MB_BG_CMD_STATUS 0x18

struct cxl_mem {
struct pci_dev *pdev;
void __iomem *regs;

/* Cap 0000h */
struct {
void __iomem *regs;
} status;

/* Cap 0002h */
struct {
void __iomem *regs;
size_t payload_size;
} mbox;

/* Cap 0040h */
struct {
void __iomem *regs;
} mem;
};

#define cxl_reg(type) \
static inline void cxl_write_##type##_reg32(struct cxl_mem *cxlm, \
u32 reg, u32 value) \
{ \
void __iomem *reg_addr = READ_ONCE(cxlm->type.regs); \
writel(value, reg_addr + reg); \
} \
static inline void cxl_write_##type##_reg64(struct cxl_mem *cxlm, \
u32 reg, u64 value) \
{ \
void __iomem *reg_addr = READ_ONCE(cxlm->type.regs); \
writeq(value, reg_addr + reg); \
} \
static inline u32 cxl_read_##type##_reg32(struct cxl_mem *cxlm, \
u32 reg) \
{ \
void __iomem *reg_addr = READ_ONCE(cxlm->type.regs); \
return readl(reg_addr + reg); \
} \
static inline u64 cxl_read_##type##_reg64(struct cxl_mem *cxlm, \
u32 reg) \
{ \
void __iomem *reg_addr = READ_ONCE(cxlm->type.regs); \
return readq(reg_addr + reg); \
}

cxl_reg(status)
cxl_reg(mbox)

static inline u32 __cxl_raw_read_reg32(struct cxl_mem *cxlm, u32 reg)
{
void __iomem *reg_addr = READ_ONCE(cxlm->regs);

return readl(reg_addr + reg);
}

static inline u64 __cxl_raw_read_reg64(struct cxl_mem *cxlm, u32 reg)
{
void __iomem *reg_addr = READ_ONCE(cxlm->regs);

return readq(reg_addr + reg);
}
#endif /* __CXL_H__ */
@@ -5,11 +5,57 @@
#include <linux/io.h>
#include "acpi.h"
#include "pci.h"
#include "cxl.h"

struct cxl_mem {
struct pci_dev *pdev;
void __iomem *regs;
};
static int cxl_mem_setup_regs(struct cxl_mem *cxlm)
{
u64 cap_array;
int cap;

cap_array = __cxl_raw_read_reg64(cxlm, CXLDEV_CAP_ARRAY_REG);
if (CXLDEV_CAP_ARRAY_ID(cap_array) != CXLDEV_CAP_ARRAY_CAP_ID)
return -ENODEV;

for (cap = 1; cap <= CXLDEV_CAP_ARRAY_COUNT(cap_array); cap++) {
void *__iomem register_block;
u32 offset;
u16 cap_id;

cap_id = __cxl_raw_read_reg32(cxlm, cap * 0x10) & 0xffff;
offset = __cxl_raw_read_reg32(cxlm, cap * 0x10 + 0x4);
register_block = cxlm->regs + offset;

switch (cap_id) {
case CXL_CAPABILITIES_CAP_ID_DEVICE_STATUS:
dev_dbg(&cxlm->pdev->dev, "found Status capability\n");
cxlm->status.regs = register_block;
break;
case CXL_CAPABILITIES_CAP_ID_PRIMARY_MAILBOX:
dev_dbg(&cxlm->pdev->dev,
"found Mailbox capability\n");
cxlm->mbox.regs = register_block;
cxlm->mbox.payload_size = CXLDEV_MB_CAP_PAYLOAD_SIZE(cap_id);
break;
case CXL_CAPABILITIES_CAP_ID_SECONDARY_MAILBOX:
dev_dbg(&cxlm->pdev->dev,
"found UNSUPPORTED Secondary Mailbox capability\n");
break;
case CXL_CAPABILITIES_CAP_ID_MEMDEV:
dev_dbg(&cxlm->pdev->dev,
"found Memory Device capability\n");
cxlm->mem.regs = register_block;
break;
default:
dev_err(&cxlm->pdev->dev, "Unknown cap ID: %d\n", cap_id);
return -ENXIO;
}
}

if (!cxlm->status.regs || !cxlm->mbox.regs || !cxlm->mem.regs)
return -ENXIO;

return 0;
}

static struct cxl_mem *cxl_mem_create(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi)
{
@@ -110,6 +156,10 @@ static int cxl_mem_probe(struct pci_dev *pdev, const struct pci_device_id *id)
if (IS_ERR(cxlm))
return -ENXIO;

rc = cxl_mem_setup_regs(cxlm);
if (rc)
return rc;

pci_set_drvdata(pdev, cxlm);

return 0;

0 comments on commit 03316f5

Please sign in to comment.