10f06157eSDan Williams // SPDX-License-Identifier: GPL-2.0-only
20f06157eSDan Williams /* Copyright(c) 2020 Intel Corporation. */
30f06157eSDan Williams #include <linux/io-64-nonatomic-lo-hi.h>
40f06157eSDan Williams #include <linux/device.h>
50f06157eSDan Williams #include <linux/slab.h>
60f06157eSDan Williams #include <linux/pci.h>
70f06157eSDan Williams #include <cxlmem.h>
8af9cae9fSDan Williams #include <cxlpci.h>
91ad3f701SJonathan Cameron #include <pmu.h>
100f06157eSDan Williams
11fa89248eSRobert Richter #include "core.h"
12fa89248eSRobert Richter
130f06157eSDan Williams /**
142b922a9dSDan Williams * DOC: cxl registers
152b922a9dSDan Williams *
162b922a9dSDan Williams * CXL device capabilities are enumerated by PCI DVSEC (Designated
172b922a9dSDan Williams * Vendor-specific) and / or descriptors provided by platform firmware.
182b922a9dSDan Williams * They can be defined as a set like the device and component registers
192b922a9dSDan Williams * mandated by CXL Section 8.1.12.2 Memory Device PCIe Capabilities and
202b922a9dSDan Williams * Extended Capabilities, or they can be individual capabilities
212b922a9dSDan Williams * appended to bridged and endpoint devices.
222b922a9dSDan Williams *
232b922a9dSDan Williams * Provide common infrastructure for enumerating and mapping these
242b922a9dSDan Williams * discrete capabilities.
252b922a9dSDan Williams */
262b922a9dSDan Williams
272b922a9dSDan Williams /**
280f06157eSDan Williams * cxl_probe_component_regs() - Detect CXL Component register blocks
290f06157eSDan Williams * @dev: Host device of the @base mapping
300f06157eSDan Williams * @base: Mapping containing the HDM Decoder Capability Header
310f06157eSDan Williams * @map: Map object describing the register block information found
320f06157eSDan Williams *
330f06157eSDan Williams * See CXL 2.0 8.2.4 Component Register Layout and Definition
340f06157eSDan Williams * See CXL 2.0 8.2.5.5 CXL Device Register Interface
350f06157eSDan Williams *
360f06157eSDan Williams * Probe for component register information and return it in map object.
370f06157eSDan Williams */
cxl_probe_component_regs(struct device * dev,void __iomem * base,struct cxl_component_reg_map * map)380f06157eSDan Williams void cxl_probe_component_regs(struct device *dev, void __iomem *base,
390f06157eSDan Williams struct cxl_component_reg_map *map)
400f06157eSDan Williams {
410f06157eSDan Williams int cap, cap_count;
4274b0fe80SJonathan Cameron u32 cap_array;
430f06157eSDan Williams
440f06157eSDan Williams *map = (struct cxl_component_reg_map) { 0 };
450f06157eSDan Williams
460f06157eSDan Williams /*
470f06157eSDan Williams * CXL.cache and CXL.mem registers are at offset 0x1000 as defined in
480f06157eSDan Williams * CXL 2.0 8.2.4 Table 141.
490f06157eSDan Williams */
500f06157eSDan Williams base += CXL_CM_OFFSET;
510f06157eSDan Williams
5274b0fe80SJonathan Cameron cap_array = readl(base + CXL_CM_CAP_HDR_OFFSET);
530f06157eSDan Williams
540f06157eSDan Williams if (FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, cap_array) != CM_CAP_HDR_CAP_ID) {
550f06157eSDan Williams dev_err(dev,
56d621bc2eSDan Williams "Couldn't locate the CXL.cache and CXL.mem capability array header.\n");
570f06157eSDan Williams return;
580f06157eSDan Williams }
590f06157eSDan Williams
600f06157eSDan Williams /* It's assumed that future versions will be backward compatible */
610f06157eSDan Williams cap_count = FIELD_GET(CXL_CM_CAP_HDR_ARRAY_SIZE_MASK, cap_array);
620f06157eSDan Williams
630f06157eSDan Williams for (cap = 1; cap <= cap_count; cap++) {
640f06157eSDan Williams void __iomem *register_block;
65af2dfef8SDan Williams struct cxl_reg_map *rmap;
660f06157eSDan Williams u16 cap_id, offset;
67af2dfef8SDan Williams u32 length, hdr;
680f06157eSDan Williams
690f06157eSDan Williams hdr = readl(base + cap * 0x4);
700f06157eSDan Williams
710f06157eSDan Williams cap_id = FIELD_GET(CXL_CM_CAP_HDR_ID_MASK, hdr);
720f06157eSDan Williams offset = FIELD_GET(CXL_CM_CAP_PTR_MASK, hdr);
730f06157eSDan Williams register_block = base + offset;
74af2dfef8SDan Williams hdr = readl(register_block);
750f06157eSDan Williams
76af2dfef8SDan Williams rmap = NULL;
770f06157eSDan Williams switch (cap_id) {
78af2dfef8SDan Williams case CXL_CM_CAP_CAP_ID_HDM: {
79af2dfef8SDan Williams int decoder_cnt;
80af2dfef8SDan Williams
810f06157eSDan Williams dev_dbg(dev, "found HDM decoder capability (0x%x)\n",
820f06157eSDan Williams offset);
830f06157eSDan Williams
840f06157eSDan Williams decoder_cnt = cxl_hdm_decoder_count(hdr);
850f06157eSDan Williams length = 0x20 * decoder_cnt + 0x10;
86af2dfef8SDan Williams rmap = &map->hdm_decoder;
870f06157eSDan Williams break;
88af2dfef8SDan Williams }
89bd09626bSDan Williams case CXL_CM_CAP_CAP_ID_RAS:
90bd09626bSDan Williams dev_dbg(dev, "found RAS capability (0x%x)\n",
91bd09626bSDan Williams offset);
92bd09626bSDan Williams length = CXL_RAS_CAPABILITY_LENGTH;
93bd09626bSDan Williams rmap = &map->ras;
940f06157eSDan Williams break;
950f06157eSDan Williams default:
960f06157eSDan Williams dev_dbg(dev, "Unknown CM cap ID: %d (0x%x)\n", cap_id,
970f06157eSDan Williams offset);
980f06157eSDan Williams break;
990f06157eSDan Williams }
100af2dfef8SDan Williams
101af2dfef8SDan Williams if (!rmap)
102af2dfef8SDan Williams continue;
103af2dfef8SDan Williams rmap->valid = true;
104a1554e9cSDan Williams rmap->id = cap_id;
105af2dfef8SDan Williams rmap->offset = CXL_CM_OFFSET + offset;
106af2dfef8SDan Williams rmap->size = length;
1070f06157eSDan Williams }
1080f06157eSDan Williams }
109affec782SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_probe_component_regs, CXL);
1100f06157eSDan Williams
1110f06157eSDan Williams /**
1120f06157eSDan Williams * cxl_probe_device_regs() - Detect CXL Device register blocks
1130f06157eSDan Williams * @dev: Host device of the @base mapping
1140f06157eSDan Williams * @base: Mapping of CXL 2.0 8.2.8 CXL Device Register Interface
1150f06157eSDan Williams * @map: Map object describing the register block information found
1160f06157eSDan Williams *
1170f06157eSDan Williams * Probe for device register information and return it in map object.
1180f06157eSDan Williams */
cxl_probe_device_regs(struct device * dev,void __iomem * base,struct cxl_device_reg_map * map)1190f06157eSDan Williams void cxl_probe_device_regs(struct device *dev, void __iomem *base,
1200f06157eSDan Williams struct cxl_device_reg_map *map)
1210f06157eSDan Williams {
1220f06157eSDan Williams int cap, cap_count;
1230f06157eSDan Williams u64 cap_array;
1240f06157eSDan Williams
1250f06157eSDan Williams *map = (struct cxl_device_reg_map){ 0 };
1260f06157eSDan Williams
1270f06157eSDan Williams cap_array = readq(base + CXLDEV_CAP_ARRAY_OFFSET);
1280f06157eSDan Williams if (FIELD_GET(CXLDEV_CAP_ARRAY_ID_MASK, cap_array) !=
1290f06157eSDan Williams CXLDEV_CAP_ARRAY_CAP_ID)
1300f06157eSDan Williams return;
1310f06157eSDan Williams
1320f06157eSDan Williams cap_count = FIELD_GET(CXLDEV_CAP_ARRAY_COUNT_MASK, cap_array);
1330f06157eSDan Williams
1340f06157eSDan Williams for (cap = 1; cap <= cap_count; cap++) {
135af2dfef8SDan Williams struct cxl_reg_map *rmap;
1360f06157eSDan Williams u32 offset, length;
1370f06157eSDan Williams u16 cap_id;
1380f06157eSDan Williams
1390f06157eSDan Williams cap_id = FIELD_GET(CXLDEV_CAP_HDR_CAP_ID_MASK,
1400f06157eSDan Williams readl(base + cap * 0x10));
1410f06157eSDan Williams offset = readl(base + cap * 0x10 + 0x4);
1420f06157eSDan Williams length = readl(base + cap * 0x10 + 0x8);
1430f06157eSDan Williams
144af2dfef8SDan Williams rmap = NULL;
1450f06157eSDan Williams switch (cap_id) {
1460f06157eSDan Williams case CXLDEV_CAP_CAP_ID_DEVICE_STATUS:
1470f06157eSDan Williams dev_dbg(dev, "found Status capability (0x%x)\n", offset);
148af2dfef8SDan Williams rmap = &map->status;
1490f06157eSDan Williams break;
1500f06157eSDan Williams case CXLDEV_CAP_CAP_ID_PRIMARY_MAILBOX:
1510f06157eSDan Williams dev_dbg(dev, "found Mailbox capability (0x%x)\n", offset);
152af2dfef8SDan Williams rmap = &map->mbox;
1530f06157eSDan Williams break;
1540f06157eSDan Williams case CXLDEV_CAP_CAP_ID_SECONDARY_MAILBOX:
1550f06157eSDan Williams dev_dbg(dev, "found Secondary Mailbox capability (0x%x)\n", offset);
1560f06157eSDan Williams break;
1570f06157eSDan Williams case CXLDEV_CAP_CAP_ID_MEMDEV:
1580f06157eSDan Williams dev_dbg(dev, "found Memory Device capability (0x%x)\n", offset);
159af2dfef8SDan Williams rmap = &map->memdev;
1600f06157eSDan Williams break;
1610f06157eSDan Williams default:
1620f06157eSDan Williams if (cap_id >= 0x8000)
1630f06157eSDan Williams dev_dbg(dev, "Vendor cap ID: %#x offset: %#x\n", cap_id, offset);
1640f06157eSDan Williams else
1650f06157eSDan Williams dev_dbg(dev, "Unknown cap ID: %#x offset: %#x\n", cap_id, offset);
1660f06157eSDan Williams break;
1670f06157eSDan Williams }
168af2dfef8SDan Williams
169af2dfef8SDan Williams if (!rmap)
170af2dfef8SDan Williams continue;
171af2dfef8SDan Williams rmap->valid = true;
172a1554e9cSDan Williams rmap->id = cap_id;
173af2dfef8SDan Williams rmap->offset = offset;
174af2dfef8SDan Williams rmap->size = length;
1750f06157eSDan Williams }
1760f06157eSDan Williams }
177affec782SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_probe_device_regs, CXL);
1780f06157eSDan Williams
devm_cxl_iomap_block(struct device * dev,resource_size_t addr,resource_size_t length)179d17d0540SDan Williams void __iomem *devm_cxl_iomap_block(struct device *dev, resource_size_t addr,
1800f06157eSDan Williams resource_size_t length)
1810f06157eSDan Williams {
1820f06157eSDan Williams void __iomem *ret_val;
1830f06157eSDan Williams struct resource *res;
1840f06157eSDan Williams
1853bb80da5SRobert Richter if (WARN_ON_ONCE(addr == CXL_RESOURCE_NONE))
1863bb80da5SRobert Richter return NULL;
1873bb80da5SRobert Richter
1880f06157eSDan Williams res = devm_request_mem_region(dev, addr, length, dev_name(dev));
1890f06157eSDan Williams if (!res) {
1900f06157eSDan Williams resource_size_t end = addr + length - 1;
1910f06157eSDan Williams
1920f06157eSDan Williams dev_err(dev, "Failed to request region %pa-%pa\n", &addr, &end);
1930f06157eSDan Williams return NULL;
1940f06157eSDan Williams }
1950f06157eSDan Williams
1960f06157eSDan Williams ret_val = devm_ioremap(dev, addr, length);
1970f06157eSDan Williams if (!ret_val)
1980f06157eSDan Williams dev_err(dev, "Failed to map region %pr\n", res);
1990f06157eSDan Williams
2000f06157eSDan Williams return ret_val;
2010f06157eSDan Williams }
2020f06157eSDan Williams
cxl_map_component_regs(const struct cxl_register_map * map,struct cxl_component_regs * regs,unsigned long map_mask)2030c0df631SDan Williams int cxl_map_component_regs(const struct cxl_register_map *map,
20457340804SRobert Richter struct cxl_component_regs *regs,
20557340804SRobert Richter unsigned long map_mask)
2060f06157eSDan Williams {
2070fc37ec1SRobert Richter struct device *host = map->host;
208a1554e9cSDan Williams struct mapinfo {
209688baac1SDan Williams const struct cxl_reg_map *rmap;
210a1554e9cSDan Williams void __iomem **addr;
211a1554e9cSDan Williams } mapinfo[] = {
212a1554e9cSDan Williams { &map->component_map.hdm_decoder, ®s->hdm_decoder },
213bd09626bSDan Williams { &map->component_map.ras, ®s->ras },
214a1554e9cSDan Williams };
215a1554e9cSDan Williams int i;
216a1554e9cSDan Williams
217a1554e9cSDan Williams for (i = 0; i < ARRAY_SIZE(mapinfo); i++) {
218a1554e9cSDan Williams struct mapinfo *mi = &mapinfo[i];
2190f06157eSDan Williams resource_size_t phys_addr;
2200f06157eSDan Williams resource_size_t length;
2210f06157eSDan Williams
222a1554e9cSDan Williams if (!mi->rmap->valid)
223a1554e9cSDan Williams continue;
224a1554e9cSDan Williams if (!test_bit(mi->rmap->id, &map_mask))
225a1554e9cSDan Williams continue;
226a1554e9cSDan Williams phys_addr = map->resource + mi->rmap->offset;
227a1554e9cSDan Williams length = mi->rmap->size;
2280fc37ec1SRobert Richter *(mi->addr) = devm_cxl_iomap_block(host, phys_addr, length);
229a1554e9cSDan Williams if (!*(mi->addr))
2300f06157eSDan Williams return -ENOMEM;
231a1554e9cSDan Williams }
2320f06157eSDan Williams
2330f06157eSDan Williams return 0;
2340f06157eSDan Williams }
235affec782SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
2360f06157eSDan Williams
cxl_map_device_regs(const struct cxl_register_map * map,struct cxl_device_regs * regs)2370c0df631SDan Williams int cxl_map_device_regs(const struct cxl_register_map *map,
23857340804SRobert Richter struct cxl_device_regs *regs)
2390f06157eSDan Williams {
2400fc37ec1SRobert Richter struct device *host = map->host;
2416c7f4f1eSDan Williams resource_size_t phys_addr = map->resource;
2421191ca10SDan Williams struct mapinfo {
243688baac1SDan Williams const struct cxl_reg_map *rmap;
2441191ca10SDan Williams void __iomem **addr;
2451191ca10SDan Williams } mapinfo[] = {
2461191ca10SDan Williams { &map->device_map.status, ®s->status, },
2471191ca10SDan Williams { &map->device_map.mbox, ®s->mbox, },
2481191ca10SDan Williams { &map->device_map.memdev, ®s->memdev, },
2491191ca10SDan Williams };
2501191ca10SDan Williams int i;
2510f06157eSDan Williams
2521191ca10SDan Williams for (i = 0; i < ARRAY_SIZE(mapinfo); i++) {
2531191ca10SDan Williams struct mapinfo *mi = &mapinfo[i];
2540f06157eSDan Williams resource_size_t length;
2550f06157eSDan Williams resource_size_t addr;
2560f06157eSDan Williams
2571191ca10SDan Williams if (!mi->rmap->valid)
2581191ca10SDan Williams continue;
2590f06157eSDan Williams
2601191ca10SDan Williams addr = phys_addr + mi->rmap->offset;
2611191ca10SDan Williams length = mi->rmap->size;
2620fc37ec1SRobert Richter *(mi->addr) = devm_cxl_iomap_block(host, addr, length);
2631191ca10SDan Williams if (!*(mi->addr))
2640f06157eSDan Williams return -ENOMEM;
2650f06157eSDan Williams }
2660f06157eSDan Williams
2670f06157eSDan Williams return 0;
2680f06157eSDan Williams }
269affec782SDan Williams EXPORT_SYMBOL_NS_GPL(cxl_map_device_regs, CXL);
270303ebc1bSBen Widawsky
cxl_decode_regblock(struct pci_dev * pdev,u32 reg_lo,u32 reg_hi,struct cxl_register_map * map)2716c7f4f1eSDan Williams static bool cxl_decode_regblock(struct pci_dev *pdev, u32 reg_lo, u32 reg_hi,
272303ebc1bSBen Widawsky struct cxl_register_map *map)
273303ebc1bSBen Widawsky {
274*eb0ef411SDave Jiang u8 reg_type = FIELD_GET(CXL_DVSEC_REG_LOCATOR_BLOCK_ID_MASK, reg_lo);
2756c7f4f1eSDan Williams int bar = FIELD_GET(CXL_DVSEC_REG_LOCATOR_BIR_MASK, reg_lo);
2766c7f4f1eSDan Williams u64 offset = ((u64)reg_hi << 32) |
277303ebc1bSBen Widawsky (reg_lo & CXL_DVSEC_REG_LOCATOR_BLOCK_OFF_LOW_MASK);
2786c7f4f1eSDan Williams
2796c7f4f1eSDan Williams if (offset > pci_resource_len(pdev, bar)) {
2806c7f4f1eSDan Williams dev_warn(&pdev->dev,
2816c7f4f1eSDan Williams "BAR%d: %pr: too small (offset: %pa, type: %d)\n", bar,
282*eb0ef411SDave Jiang &pdev->resource[bar], &offset, reg_type);
2836c7f4f1eSDan Williams return false;
2846c7f4f1eSDan Williams }
2856c7f4f1eSDan Williams
286*eb0ef411SDave Jiang map->reg_type = reg_type;
2876c7f4f1eSDan Williams map->resource = pci_resource_start(pdev, bar) + offset;
2886c7f4f1eSDan Williams map->max_size = pci_resource_len(pdev, bar) - offset;
2896c7f4f1eSDan Williams return true;
290303ebc1bSBen Widawsky }
291303ebc1bSBen Widawsky
292303ebc1bSBen Widawsky /**
293d717d7f3SJonathan Cameron * cxl_find_regblock_instance() - Locate a register block by type / index
294303ebc1bSBen Widawsky * @pdev: The CXL PCI device to enumerate.
295303ebc1bSBen Widawsky * @type: Register Block Indicator id
296303ebc1bSBen Widawsky * @map: Enumeration output, clobbered on error
297d717d7f3SJonathan Cameron * @index: Index into which particular instance of a regblock wanted in the
298d717d7f3SJonathan Cameron * order found in register locator DVSEC.
299303ebc1bSBen Widawsky *
300303ebc1bSBen Widawsky * Return: 0 if register block enumerated, negative error code otherwise
301303ebc1bSBen Widawsky *
302303ebc1bSBen Widawsky * A CXL DVSEC may point to one or more register blocks, search for them
303d717d7f3SJonathan Cameron * by @type and @index.
304303ebc1bSBen Widawsky */
cxl_find_regblock_instance(struct pci_dev * pdev,enum cxl_regloc_type type,struct cxl_register_map * map,int index)305d717d7f3SJonathan Cameron int cxl_find_regblock_instance(struct pci_dev *pdev, enum cxl_regloc_type type,
306d717d7f3SJonathan Cameron struct cxl_register_map *map, int index)
307303ebc1bSBen Widawsky {
308303ebc1bSBen Widawsky u32 regloc_size, regblocks;
309d717d7f3SJonathan Cameron int instance = 0;
310303ebc1bSBen Widawsky int regloc, i;
311303ebc1bSBen Widawsky
31257340804SRobert Richter *map = (struct cxl_register_map) {
3130fc37ec1SRobert Richter .host = &pdev->dev,
31457340804SRobert Richter .resource = CXL_RESOURCE_NONE,
31557340804SRobert Richter };
31657340804SRobert Richter
317303ebc1bSBen Widawsky regloc = pci_find_dvsec_capability(pdev, PCI_DVSEC_VENDOR_ID_CXL,
318303ebc1bSBen Widawsky CXL_DVSEC_REG_LOCATOR);
319303ebc1bSBen Widawsky if (!regloc)
320303ebc1bSBen Widawsky return -ENXIO;
321303ebc1bSBen Widawsky
322303ebc1bSBen Widawsky pci_read_config_dword(pdev, regloc + PCI_DVSEC_HEADER1, ®loc_size);
323303ebc1bSBen Widawsky regloc_size = FIELD_GET(PCI_DVSEC_HEADER1_LENGTH_MASK, regloc_size);
324303ebc1bSBen Widawsky
325303ebc1bSBen Widawsky regloc += CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET;
326303ebc1bSBen Widawsky regblocks = (regloc_size - CXL_DVSEC_REG_LOCATOR_BLOCK1_OFFSET) / 8;
327303ebc1bSBen Widawsky
328303ebc1bSBen Widawsky for (i = 0; i < regblocks; i++, regloc += 8) {
329303ebc1bSBen Widawsky u32 reg_lo, reg_hi;
330303ebc1bSBen Widawsky
331303ebc1bSBen Widawsky pci_read_config_dword(pdev, regloc, ®_lo);
332303ebc1bSBen Widawsky pci_read_config_dword(pdev, regloc + 4, ®_hi);
333303ebc1bSBen Widawsky
3346c7f4f1eSDan Williams if (!cxl_decode_regblock(pdev, reg_lo, reg_hi, map))
3356c7f4f1eSDan Williams continue;
336303ebc1bSBen Widawsky
337d717d7f3SJonathan Cameron if (map->reg_type == type) {
338d717d7f3SJonathan Cameron if (index == instance)
339303ebc1bSBen Widawsky return 0;
340d717d7f3SJonathan Cameron instance++;
341d717d7f3SJonathan Cameron }
342303ebc1bSBen Widawsky }
343303ebc1bSBen Widawsky
3446c7f4f1eSDan Williams map->resource = CXL_RESOURCE_NONE;
345303ebc1bSBen Widawsky return -ENODEV;
346303ebc1bSBen Widawsky }
347d717d7f3SJonathan Cameron EXPORT_SYMBOL_NS_GPL(cxl_find_regblock_instance, CXL);
348d717d7f3SJonathan Cameron
349d717d7f3SJonathan Cameron /**
350d717d7f3SJonathan Cameron * cxl_find_regblock() - Locate register blocks by type
351d717d7f3SJonathan Cameron * @pdev: The CXL PCI device to enumerate.
352d717d7f3SJonathan Cameron * @type: Register Block Indicator id
353d717d7f3SJonathan Cameron * @map: Enumeration output, clobbered on error
354d717d7f3SJonathan Cameron *
355d717d7f3SJonathan Cameron * Return: 0 if register block enumerated, negative error code otherwise
356d717d7f3SJonathan Cameron *
357d717d7f3SJonathan Cameron * A CXL DVSEC may point to one or more register blocks, search for them
358d717d7f3SJonathan Cameron * by @type.
359d717d7f3SJonathan Cameron */
cxl_find_regblock(struct pci_dev * pdev,enum cxl_regloc_type type,struct cxl_register_map * map)360d717d7f3SJonathan Cameron int cxl_find_regblock(struct pci_dev *pdev, enum cxl_regloc_type type,
361d717d7f3SJonathan Cameron struct cxl_register_map *map)
362d717d7f3SJonathan Cameron {
363d717d7f3SJonathan Cameron return cxl_find_regblock_instance(pdev, type, map, 0);
364d717d7f3SJonathan Cameron }
365303ebc1bSBen Widawsky EXPORT_SYMBOL_NS_GPL(cxl_find_regblock, CXL);
366d5b1a271SRobert Richter
367d717d7f3SJonathan Cameron /**
368d717d7f3SJonathan Cameron * cxl_count_regblock() - Count instances of a given regblock type.
369d717d7f3SJonathan Cameron * @pdev: The CXL PCI device to enumerate.
370d717d7f3SJonathan Cameron * @type: Register Block Indicator id
371d717d7f3SJonathan Cameron *
372d717d7f3SJonathan Cameron * Some regblocks may be repeated. Count how many instances.
373d717d7f3SJonathan Cameron *
374d717d7f3SJonathan Cameron * Return: count of matching regblocks.
375d717d7f3SJonathan Cameron */
cxl_count_regblock(struct pci_dev * pdev,enum cxl_regloc_type type)376d717d7f3SJonathan Cameron int cxl_count_regblock(struct pci_dev *pdev, enum cxl_regloc_type type)
377d717d7f3SJonathan Cameron {
378d717d7f3SJonathan Cameron struct cxl_register_map map;
379d717d7f3SJonathan Cameron int rc, count = 0;
380d717d7f3SJonathan Cameron
381d717d7f3SJonathan Cameron while (1) {
382d717d7f3SJonathan Cameron rc = cxl_find_regblock_instance(pdev, type, &map, count);
383d717d7f3SJonathan Cameron if (rc)
384d717d7f3SJonathan Cameron return count;
385d717d7f3SJonathan Cameron count++;
386d717d7f3SJonathan Cameron }
387d717d7f3SJonathan Cameron }
388d717d7f3SJonathan Cameron EXPORT_SYMBOL_NS_GPL(cxl_count_regblock, CXL);
389d717d7f3SJonathan Cameron
cxl_map_pmu_regs(struct pci_dev * pdev,struct cxl_pmu_regs * regs,struct cxl_register_map * map)3901ad3f701SJonathan Cameron int cxl_map_pmu_regs(struct pci_dev *pdev, struct cxl_pmu_regs *regs,
3911ad3f701SJonathan Cameron struct cxl_register_map *map)
3921ad3f701SJonathan Cameron {
3931ad3f701SJonathan Cameron struct device *dev = &pdev->dev;
3941ad3f701SJonathan Cameron resource_size_t phys_addr;
3951ad3f701SJonathan Cameron
3961ad3f701SJonathan Cameron phys_addr = map->resource;
3971ad3f701SJonathan Cameron regs->pmu = devm_cxl_iomap_block(dev, phys_addr, CXL_PMU_REGMAP_SIZE);
3981ad3f701SJonathan Cameron if (!regs->pmu)
3991ad3f701SJonathan Cameron return -ENOMEM;
4001ad3f701SJonathan Cameron
4011ad3f701SJonathan Cameron return 0;
4021ad3f701SJonathan Cameron }
4031ad3f701SJonathan Cameron EXPORT_SYMBOL_NS_GPL(cxl_map_pmu_regs, CXL);
4041ad3f701SJonathan Cameron
cxl_map_regblock(struct cxl_register_map * map)405d076bb8cSTerry Bowman static int cxl_map_regblock(struct cxl_register_map *map)
406d076bb8cSTerry Bowman {
4070fc37ec1SRobert Richter struct device *host = map->host;
408d076bb8cSTerry Bowman
409d076bb8cSTerry Bowman map->base = ioremap(map->resource, map->max_size);
410d076bb8cSTerry Bowman if (!map->base) {
4110fc37ec1SRobert Richter dev_err(host, "failed to map registers\n");
412d076bb8cSTerry Bowman return -ENOMEM;
413d076bb8cSTerry Bowman }
414d076bb8cSTerry Bowman
4150fc37ec1SRobert Richter dev_dbg(host, "Mapped CXL Memory Device resource %pa\n", &map->resource);
416d076bb8cSTerry Bowman return 0;
417d076bb8cSTerry Bowman }
418d076bb8cSTerry Bowman
cxl_unmap_regblock(struct cxl_register_map * map)419d076bb8cSTerry Bowman static void cxl_unmap_regblock(struct cxl_register_map *map)
420d076bb8cSTerry Bowman {
421d076bb8cSTerry Bowman iounmap(map->base);
422d076bb8cSTerry Bowman map->base = NULL;
423d076bb8cSTerry Bowman }
424d076bb8cSTerry Bowman
cxl_probe_regs(struct cxl_register_map * map)425d076bb8cSTerry Bowman static int cxl_probe_regs(struct cxl_register_map *map)
426d076bb8cSTerry Bowman {
427d076bb8cSTerry Bowman struct cxl_component_reg_map *comp_map;
428d076bb8cSTerry Bowman struct cxl_device_reg_map *dev_map;
4290fc37ec1SRobert Richter struct device *host = map->host;
430d076bb8cSTerry Bowman void __iomem *base = map->base;
431d076bb8cSTerry Bowman
432d076bb8cSTerry Bowman switch (map->reg_type) {
433d076bb8cSTerry Bowman case CXL_REGLOC_RBI_COMPONENT:
434d076bb8cSTerry Bowman comp_map = &map->component_map;
4350fc37ec1SRobert Richter cxl_probe_component_regs(host, base, comp_map);
4360fc37ec1SRobert Richter dev_dbg(host, "Set up component registers\n");
437d076bb8cSTerry Bowman break;
438d076bb8cSTerry Bowman case CXL_REGLOC_RBI_MEMDEV:
439d076bb8cSTerry Bowman dev_map = &map->device_map;
4400fc37ec1SRobert Richter cxl_probe_device_regs(host, base, dev_map);
441d076bb8cSTerry Bowman if (!dev_map->status.valid || !dev_map->mbox.valid ||
442d076bb8cSTerry Bowman !dev_map->memdev.valid) {
4430fc37ec1SRobert Richter dev_err(host, "registers not found: %s%s%s\n",
444d076bb8cSTerry Bowman !dev_map->status.valid ? "status " : "",
445d076bb8cSTerry Bowman !dev_map->mbox.valid ? "mbox " : "",
446d076bb8cSTerry Bowman !dev_map->memdev.valid ? "memdev " : "");
447d076bb8cSTerry Bowman return -ENXIO;
448d076bb8cSTerry Bowman }
449d076bb8cSTerry Bowman
4500fc37ec1SRobert Richter dev_dbg(host, "Probing device registers...\n");
451d076bb8cSTerry Bowman break;
452d076bb8cSTerry Bowman default:
453d076bb8cSTerry Bowman break;
454d076bb8cSTerry Bowman }
455d076bb8cSTerry Bowman
456d076bb8cSTerry Bowman return 0;
457d076bb8cSTerry Bowman }
458d076bb8cSTerry Bowman
cxl_setup_regs(struct cxl_register_map * map)459d076bb8cSTerry Bowman int cxl_setup_regs(struct cxl_register_map *map)
460d076bb8cSTerry Bowman {
461d076bb8cSTerry Bowman int rc;
462d076bb8cSTerry Bowman
463d076bb8cSTerry Bowman rc = cxl_map_regblock(map);
464d076bb8cSTerry Bowman if (rc)
465d076bb8cSTerry Bowman return rc;
466d076bb8cSTerry Bowman
467d076bb8cSTerry Bowman rc = cxl_probe_regs(map);
468d076bb8cSTerry Bowman cxl_unmap_regblock(map);
469d076bb8cSTerry Bowman
470d076bb8cSTerry Bowman return rc;
471d076bb8cSTerry Bowman }
472d076bb8cSTerry Bowman EXPORT_SYMBOL_NS_GPL(cxl_setup_regs, CXL);
473d076bb8cSTerry Bowman
__rcrb_to_component(struct device * dev,struct cxl_rcrb_info * ri,enum cxl_rcrb which)47406193378SDan Williams resource_size_t __rcrb_to_component(struct device *dev, struct cxl_rcrb_info *ri,
475d5b1a271SRobert Richter enum cxl_rcrb which)
476d5b1a271SRobert Richter {
477d5b1a271SRobert Richter resource_size_t component_reg_phys;
47806193378SDan Williams resource_size_t rcrb = ri->base;
479397cd265SDan Williams void __iomem *addr;
480d5b1a271SRobert Richter u32 bar0, bar1;
481d5b1a271SRobert Richter u16 cmd;
482d5b1a271SRobert Richter u32 id;
483d5b1a271SRobert Richter
484d5b1a271SRobert Richter if (which == CXL_RCRB_UPSTREAM)
485d5b1a271SRobert Richter rcrb += SZ_4K;
486d5b1a271SRobert Richter
487d5b1a271SRobert Richter /*
488d5b1a271SRobert Richter * RCRB's BAR[0..1] point to component block containing CXL
489d5b1a271SRobert Richter * subsystem component registers. MEMBAR extraction follows
490d5b1a271SRobert Richter * the PCI Base spec here, esp. 64 bit extraction and memory
491d5b1a271SRobert Richter * ranges alignment (6.0, 7.5.1.2.1).
492d5b1a271SRobert Richter */
493d5b1a271SRobert Richter if (!request_mem_region(rcrb, SZ_4K, "CXL RCRB"))
494d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
495d5b1a271SRobert Richter addr = ioremap(rcrb, SZ_4K);
496d5b1a271SRobert Richter if (!addr) {
497d5b1a271SRobert Richter dev_err(dev, "Failed to map region %pr\n", addr);
498d5b1a271SRobert Richter release_mem_region(rcrb, SZ_4K);
499d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
500d5b1a271SRobert Richter }
501d5b1a271SRobert Richter
502d5b1a271SRobert Richter id = readl(addr + PCI_VENDOR_ID);
503d5b1a271SRobert Richter cmd = readw(addr + PCI_COMMAND);
504d5b1a271SRobert Richter bar0 = readl(addr + PCI_BASE_ADDRESS_0);
505d5b1a271SRobert Richter bar1 = readl(addr + PCI_BASE_ADDRESS_1);
506d5b1a271SRobert Richter iounmap(addr);
507d5b1a271SRobert Richter release_mem_region(rcrb, SZ_4K);
508d5b1a271SRobert Richter
509d5b1a271SRobert Richter /*
510d5b1a271SRobert Richter * Sanity check, see CXL 3.0 Figure 9-8 CXL Device that Does Not
511d5b1a271SRobert Richter * Remap Upstream Port and Component Registers
512d5b1a271SRobert Richter */
513d5b1a271SRobert Richter if (id == U32_MAX) {
514d5b1a271SRobert Richter if (which == CXL_RCRB_DOWNSTREAM)
515d5b1a271SRobert Richter dev_err(dev, "Failed to access Downstream Port RCRB\n");
516d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
517d5b1a271SRobert Richter }
518d5b1a271SRobert Richter if (!(cmd & PCI_COMMAND_MEMORY))
519d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
520d5b1a271SRobert Richter /* The RCRB is a Memory Window, and the MEM_TYPE_1M bit is obsolete */
521d5b1a271SRobert Richter if (bar0 & (PCI_BASE_ADDRESS_MEM_TYPE_1M | PCI_BASE_ADDRESS_SPACE_IO))
522d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
523d5b1a271SRobert Richter
524d5b1a271SRobert Richter component_reg_phys = bar0 & PCI_BASE_ADDRESS_MEM_MASK;
525d5b1a271SRobert Richter if (bar0 & PCI_BASE_ADDRESS_MEM_TYPE_64)
526d5b1a271SRobert Richter component_reg_phys |= ((u64)bar1) << 32;
527d5b1a271SRobert Richter
528d5b1a271SRobert Richter if (!component_reg_phys)
529d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
530d5b1a271SRobert Richter
531d5b1a271SRobert Richter /* MEMBAR is block size (64k) aligned. */
532d5b1a271SRobert Richter if (!IS_ALIGNED(component_reg_phys, CXL_COMPONENT_REG_BLOCK_SIZE))
533d5b1a271SRobert Richter return CXL_RESOURCE_NONE;
534d5b1a271SRobert Richter
535d5b1a271SRobert Richter return component_reg_phys;
536d5b1a271SRobert Richter }
537eb4663b0SRobert Richter
cxl_rcd_component_reg_phys(struct device * dev,struct cxl_dport * dport)538eb4663b0SRobert Richter resource_size_t cxl_rcd_component_reg_phys(struct device *dev,
539eb4663b0SRobert Richter struct cxl_dport *dport)
540eb4663b0SRobert Richter {
541eb4663b0SRobert Richter if (!dport->rch)
542eb4663b0SRobert Richter return CXL_RESOURCE_NONE;
54306193378SDan Williams return __rcrb_to_component(dev, &dport->rcrb, CXL_RCRB_UPSTREAM);
544eb4663b0SRobert Richter }
545eb4663b0SRobert Richter EXPORT_SYMBOL_NS_GPL(cxl_rcd_component_reg_phys, CXL);
546