linux/drivers/cxl/core/regs.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* Copyright(c) 2020 Intel Corporation. */
   3#include <linux/io-64-nonatomic-lo-hi.h>
   4#include <linux/device.h>
   5#include <linux/slab.h>
   6#include <linux/pci.h>
   7#include <cxlmem.h>
   8
   9/**
  10 * DOC: cxl registers
  11 *
  12 * CXL device capabilities are enumerated by PCI DVSEC (Designated
  13 * Vendor-specific) and / or descriptors provided by platform firmware.
  14 * They can be defined as a set like the device and component registers
  15 * mandated by CXL Section 8.1.12.2 Memory Device PCIe Capabilities and
  16 * Extended Capabilities, or they can be individual capabilities
  17 * appended to bridged and endpoint devices.
  18 *
  19 * Provide common infrastructure for enumerating and mapping these
  20 * discrete capabilities.
  21 */
  22
  23/**
  24 * cxl_probe_component_regs() - Detect CXL Component register blocks
  25 * @dev: Host device of the @base mapping
  26 * @base: Mapping containing the HDM Decoder Capability Header
  27 * @map: Map object describing the register block information found
  28 *
  29 * See CXL 2.0 8.2.4 Component Register Layout and Definition
  30 * See CXL 2.0 8.2.5.5 CXL Device Register Interface
  31 *
  32 * Probe for component register information and return it in map object.
  33 */
  34void cxl_probe_component_regs(struct device *dev, void __iomem *base,
  35                              struct cxl_component_reg_map *map)
  36{
  37        int cap, cap_count;
  38        u64 cap_array;
  39
  40        *map = (struct cxl_component_reg_map) { 0 };
  41
  42        /*
  43         * CXL.cache and CXL.mem registers are at offset 0x1000 as defined in
  44         * CXL 2.0 8.2.4 Table 141.
  45         */
  46        base += CXL_CM_OFFSET;
  47
  48        cap_array = readq(base + CXL_CM_CAP_HDR_OFFSET);
  49
  50        if (FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, cap_array) != CM_CAP_HDR_CAP_ID) {
  51                dev_err(dev,
  52                        "Couldn't locate the CXL.cache and CXL.mem capability array header./n");
  53                return;
  54        }
  55
  56        /* It's assumed that future versions will be backward compatible */
  57        cap_count = FIELD_GET(CXL_CM_CAP_HDR_ARRAY_SIZE_MASK, cap_array);
  58
  59        for (cap = 1; cap <= cap_count; cap++) {
  60                void __iomem *register_block;
  61                u32 hdr;
  62                int decoder_cnt;
  63                u16 cap_id, offset;
  64                u32 length;
  65
  66                hdr = readl(base + cap * 0x4);
  67
  68                cap_id = FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, hdr);
  69                offset = FIELD_GET(CXL_CM_CAP_PTR_MASK, hdr);
  70                register_block = base + offset;
  71
  72                switch (cap_id) {
  73                case CXL_CM_CAP_CAP_ID_HDM:
  74                        dev_dbg(dev, "found HDM decoder capability (0x%x)\n",
  75                                offset);
  76
  77                        hdr = readl(register_block);
  78
  79                        decoder_cnt = cxl_hdm_decoder_count(hdr);
  80                        length = 0x20 * decoder_cnt + 0x10;
  81
  82                        map->hdm_decoder.valid = true;
  83                        map->hdm_decoder.offset = CXL_CM_OFFSET + offset;
  84                        map->hdm_decoder.size = length;
  85                        break;
  86                default:
  87                        dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
  88                                offset);
  89                        break;
  90                }
  91        }
  92}
  93EXPORT_SYMBOL_GPL(cxl_probe_component_regs);
  94
  95/**
  96 * cxl_probe_device_regs() - Detect CXL Device register blocks
  97 * @dev: Host device of the @base mapping
  98 * @base: Mapping of CXL 2.0 8.2.8 CXL Device Register Interface
  99 * @map: Map object describing the register block information found
 100 *
 101 * Probe for device register information and return it in map object.
 102 */
 103void cxl_probe_device_regs(struct device *dev, void __iomem *base,
 104                           struct cxl_device_reg_map *map)
 105{
 106        int cap, cap_count;
 107        u64 cap_array;
 108
 109        *map = (struct cxl_device_reg_map){ 0 };
 110
 111        cap_array = readq(base + CXLDEV_CAP_ARRAY_OFFSET);
 112        if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
 113            CXLDEV_CAP_ARRAY_CAP_ID)
 114                return;
 115
 116        cap_count = FIELD_GET(CXLDEV_CAP_ARRAY_COUNT_MASK, cap_array);
 117
 118        for (cap = 1; cap <= cap_count; cap++) {
 119                u32 offset, length;
 120                u16 cap_id;
 121
 122                cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
 123                                   readl(base + cap * 0x10));
 124                offset = readl(base + cap * 0x10 + 0x4);
 125                length = readl(base + cap * 0x10 + 0x8);
 126
 127                switch (cap_id) {
 128                case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
 129                        dev_dbg(dev, "found Status capability (0x%x)\n", offset);
 130
 131                        map->status.valid = true;
 132                        map->status.offset = offset;
 133                        map->status.size = length;
 134                        break;
 135                case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
 136                        dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
 137                        map->mbox.valid = true;
 138                        map->mbox.offset = offset;
 139                        map->mbox.size = length;
 140                        break;
 141                case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX:
 142                        dev_dbg(dev, "found Secondary Mailbox capability (0x%x)\n", offset);
 143                        break;
 144                case CXLDEV_CAP_CAP_ID_MEMDEV:
 145                        dev_dbg(dev, "found Memory Device capability (0x%x)\n", offset);
 146                        map->memdev.valid = true;
 147                        map->memdev.offset = offset;
 148                        map->memdev.size = length;
 149                        break;
 150                default:
 151                        if (cap_id >= 0x8000)
 152                                dev_dbg(dev, "Vendor cap ID: %#x offset: %#x\n", cap_id, offset);
 153                        else
 154                                dev_dbg(dev, "Unknown cap ID: %#x offset: %#x\n", cap_id, offset);
 155                        break;
 156                }
 157        }
 158}
 159EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
 160
 161static void __iomem *devm_cxl_iomap_block(struct device *dev,
 162                                          resource_size_t addr,
 163                                          resource_size_t length)
 164{
 165        void __iomem *ret_val;
 166        struct resource *res;
 167
 168        res = devm_request_mem_region(dev, addr, length, dev_name(dev));
 169        if (!res) {
 170                resource_size_t end = addr + length - 1;
 171
 172                dev_err(dev, "Failed to request region %pa-%pa\n", &addr, &end);
 173                return NULL;
 174        }
 175
 176        ret_val = devm_ioremap(dev, addr, length);
 177        if (!ret_val)
 178                dev_err(dev, "Failed to map region %pr\n", res);
 179
 180        return ret_val;
 181}
 182
 183int cxl_map_component_regs(struct pci_dev *pdev,
 184                           struct cxl_component_regs *regs,
 185                           struct cxl_register_map *map)
 186{
 187        struct device *dev = &pdev->dev;
 188        resource_size_t phys_addr;
 189        resource_size_t length;
 190
 191        phys_addr = pci_resource_start(pdev, map->barno);
 192        phys_addr += map->block_offset;
 193
 194        phys_addr += map->component_map.hdm_decoder.offset;
 195        length = map->component_map.hdm_decoder.size;
 196        regs->hdm_decoder = devm_cxl_iomap_block(dev, phys_addr, length);
 197        if (!regs->hdm_decoder)
 198                return -ENOMEM;
 199
 200        return 0;
 201}
 202EXPORT_SYMBOL_GPL(cxl_map_component_regs);
 203
 204int cxl_map_device_regs(struct pci_dev *pdev,
 205                        struct cxl_device_regs *regs,
 206                        struct cxl_register_map *map)
 207{
 208        struct device *dev = &pdev->dev;
 209        resource_size_t phys_addr;
 210
 211        phys_addr = pci_resource_start(pdev, map->barno);
 212        phys_addr += map->block_offset;
 213
 214        if (map->device_map.status.valid) {
 215                resource_size_t addr;
 216                resource_size_t length;
 217
 218                addr = phys_addr + map->device_map.status.offset;
 219                length = map->device_map.status.size;
 220                regs->status = devm_cxl_iomap_block(dev, addr, length);
 221                if (!regs->status)
 222                        return -ENOMEM;
 223        }
 224
 225        if (map->device_map.mbox.valid) {
 226                resource_size_t addr;
 227                resource_size_t length;
 228
 229                addr = phys_addr + map->device_map.mbox.offset;
 230                length = map->device_map.mbox.size;
 231                regs->mbox = devm_cxl_iomap_block(dev, addr, length);
 232                if (!regs->mbox)
 233                        return -ENOMEM;
 234        }
 235
 236        if (map->device_map.memdev.valid) {
 237                resource_size_t addr;
 238                resource_size_t length;
 239
 240                addr = phys_addr + map->device_map.memdev.offset;
 241                length = map->device_map.memdev.size;
 242                regs->memdev = devm_cxl_iomap_block(dev, addr, length);
 243                if (!regs->memdev)
 244                        return -ENOMEM;
 245        }
 246
 247        return 0;
 248}
 249EXPORT_SYMBOL_GPL(cxl_map_device_regs);
 250