xref: /openbmc/linux/drivers/cxl/core/regs.c (revision aeddf9a2731de8235b2b433533d06ee7dc73d233)
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, &regs->hdm_decoder },
213bd09626bSDan Williams 		{ &map->component_map.ras, &regs->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, &regs->status, },
2471191ca10SDan Williams 		{ &map->device_map.mbox, &regs->mbox, },
2481191ca10SDan Williams 		{ &map->device_map.memdev, &regs->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, &regloc_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, &reg_lo);
332303ebc1bSBen Widawsky 		pci_read_config_dword(pdev, regloc + 4, &reg_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