10c45e76fSMichael J. Ruhl // SPDX-License-Identifier: GPL-2.0 20c45e76fSMichael J. Ruhl /* Copyright © 2024 Intel Corporation */ 30c45e76fSMichael J. Ruhl #include <linux/bitfield.h> 40c45e76fSMichael J. Ruhl #include <linux/bits.h> 50c45e76fSMichael J. Ruhl #include <linux/cleanup.h> 60c45e76fSMichael J. Ruhl #include <linux/errno.h> 70c45e76fSMichael J. Ruhl #include <linux/intel_vsec.h> 80c45e76fSMichael J. Ruhl #include <linux/module.h> 90c45e76fSMichael J. Ruhl #include <linux/mutex.h> 100c45e76fSMichael J. Ruhl #include <linux/pci.h> 110c45e76fSMichael J. Ruhl #include <linux/types.h> 120c45e76fSMichael J. Ruhl 130c45e76fSMichael J. Ruhl #include "xe_device.h" 140c45e76fSMichael J. Ruhl #include "xe_device_types.h" 150c45e76fSMichael J. Ruhl #include "xe_drv.h" 160c45e76fSMichael J. Ruhl #include "xe_mmio.h" 170c45e76fSMichael J. Ruhl #include "xe_platform_types.h" 180c45e76fSMichael J. Ruhl #include "xe_pm.h" 190c45e76fSMichael J. Ruhl #include "xe_vsec.h" 200c45e76fSMichael J. Ruhl 210c45e76fSMichael J. Ruhl #include "regs/xe_pmt.h" 220c45e76fSMichael J. Ruhl 230c45e76fSMichael J. Ruhl /* PMT GUID value for BMG devices. NOTE: this is NOT a PCI id */ 240c45e76fSMichael J. Ruhl #define BMG_DEVICE_ID 0xE2F8 250c45e76fSMichael J. Ruhl 260c45e76fSMichael J. Ruhl static struct intel_vsec_header bmg_telemetry = { 270c45e76fSMichael J. Ruhl .length = 0x10, 280c45e76fSMichael J. Ruhl .id = VSEC_ID_TELEMETRY, 290c45e76fSMichael J. Ruhl .num_entries = 2, 300c45e76fSMichael J. Ruhl .entry_size = 4, 310c45e76fSMichael J. Ruhl .tbir = 0, 320c45e76fSMichael J. Ruhl .offset = BMG_DISCOVERY_OFFSET, 330c45e76fSMichael J. Ruhl }; 340c45e76fSMichael J. Ruhl 350c45e76fSMichael J. Ruhl static struct intel_vsec_header bmg_punit_crashlog = { 360c45e76fSMichael J. Ruhl .length = 0x10, 370c45e76fSMichael J. Ruhl .id = VSEC_ID_CRASHLOG, 380c45e76fSMichael J. Ruhl .num_entries = 1, 390c45e76fSMichael J. Ruhl .entry_size = 4, 400c45e76fSMichael J. Ruhl .tbir = 0, 410c45e76fSMichael J. Ruhl .offset = BMG_DISCOVERY_OFFSET + 0x60, 420c45e76fSMichael J. Ruhl }; 430c45e76fSMichael J. Ruhl 440c45e76fSMichael J. Ruhl static struct intel_vsec_header bmg_oobmsm_crashlog = { 450c45e76fSMichael J. Ruhl .length = 0x10, 460c45e76fSMichael J. Ruhl .id = VSEC_ID_CRASHLOG, 470c45e76fSMichael J. Ruhl .num_entries = 1, 480c45e76fSMichael J. Ruhl .entry_size = 4, 490c45e76fSMichael J. Ruhl .tbir = 0, 500c45e76fSMichael J. Ruhl .offset = BMG_DISCOVERY_OFFSET + 0x78, 510c45e76fSMichael J. Ruhl }; 520c45e76fSMichael J. Ruhl 530c45e76fSMichael J. Ruhl static struct intel_vsec_header *bmg_capabilities[] = { 540c45e76fSMichael J. Ruhl &bmg_telemetry, 550c45e76fSMichael J. Ruhl &bmg_punit_crashlog, 560c45e76fSMichael J. Ruhl &bmg_oobmsm_crashlog, 570c45e76fSMichael J. Ruhl NULL 580c45e76fSMichael J. Ruhl }; 590c45e76fSMichael J. Ruhl 600c45e76fSMichael J. Ruhl enum xe_vsec { 610c45e76fSMichael J. Ruhl XE_VSEC_UNKNOWN = 0, 620c45e76fSMichael J. Ruhl XE_VSEC_BMG, 630c45e76fSMichael J. Ruhl }; 640c45e76fSMichael J. Ruhl 650c45e76fSMichael J. Ruhl static struct intel_vsec_platform_info xe_vsec_info[] = { 660c45e76fSMichael J. Ruhl [XE_VSEC_BMG] = { 670c45e76fSMichael J. Ruhl .caps = VSEC_CAP_TELEMETRY | VSEC_CAP_CRASHLOG, 680c45e76fSMichael J. Ruhl .headers = bmg_capabilities, 690c45e76fSMichael J. Ruhl }, 700c45e76fSMichael J. Ruhl { } 710c45e76fSMichael J. Ruhl }; 720c45e76fSMichael J. Ruhl 730c45e76fSMichael J. Ruhl /* 740c45e76fSMichael J. Ruhl * The GUID will have the following bits to decode: 750c45e76fSMichael J. Ruhl * [0:3] - {Telemetry space iteration number (0,1,..)} 760c45e76fSMichael J. Ruhl * [4:7] - Segment (SEGMENT_INDEPENDENT-0, Client-1, Server-2) 770c45e76fSMichael J. Ruhl * [8:11] - SOC_SKU 780c45e76fSMichael J. Ruhl * [12:27] – Device ID – changes for each down bin SKU’s 790c45e76fSMichael J. Ruhl * [28:29] - Capability Type (Crashlog-0, Telemetry Aggregator-1, Watcher-2) 800c45e76fSMichael J. Ruhl * [30:31] - Record-ID (0-PUNIT, 1-OOBMSM_0, 2-OOBMSM_1) 810c45e76fSMichael J. Ruhl */ 820c45e76fSMichael J. Ruhl #define GUID_TELEM_ITERATION GENMASK(3, 0) 830c45e76fSMichael J. Ruhl #define GUID_SEGMENT GENMASK(7, 4) 840c45e76fSMichael J. Ruhl #define GUID_SOC_SKU GENMASK(11, 8) 850c45e76fSMichael J. Ruhl #define GUID_DEVICE_ID GENMASK(27, 12) 860c45e76fSMichael J. Ruhl #define GUID_CAP_TYPE GENMASK(29, 28) 870c45e76fSMichael J. Ruhl #define GUID_RECORD_ID GENMASK(31, 30) 880c45e76fSMichael J. Ruhl 890c45e76fSMichael J. Ruhl #define PUNIT_TELEMETRY_OFFSET 0x0200 900c45e76fSMichael J. Ruhl #define PUNIT_WATCHER_OFFSET 0x14A0 910c45e76fSMichael J. Ruhl #define OOBMSM_0_WATCHER_OFFSET 0x18D8 920c45e76fSMichael J. Ruhl #define OOBMSM_1_TELEMETRY_OFFSET 0x1000 930c45e76fSMichael J. Ruhl 940c45e76fSMichael J. Ruhl enum record_id { 950c45e76fSMichael J. Ruhl PUNIT, 960c45e76fSMichael J. Ruhl OOBMSM_0, 970c45e76fSMichael J. Ruhl OOBMSM_1, 980c45e76fSMichael J. Ruhl }; 990c45e76fSMichael J. Ruhl 1000c45e76fSMichael J. Ruhl enum capability { 1010c45e76fSMichael J. Ruhl CRASHLOG, 1020c45e76fSMichael J. Ruhl TELEMETRY, 1030c45e76fSMichael J. Ruhl WATCHER, 1040c45e76fSMichael J. Ruhl }; 1050c45e76fSMichael J. Ruhl 1060c45e76fSMichael J. Ruhl static int xe_guid_decode(u32 guid, int *index, u32 *offset) 1070c45e76fSMichael J. Ruhl { 1080c45e76fSMichael J. Ruhl u32 record_id = FIELD_GET(GUID_RECORD_ID, guid); 1090c45e76fSMichael J. Ruhl u32 cap_type = FIELD_GET(GUID_CAP_TYPE, guid); 1100c45e76fSMichael J. Ruhl u32 device_id = FIELD_GET(GUID_DEVICE_ID, guid); 1110c45e76fSMichael J. Ruhl 1120c45e76fSMichael J. Ruhl if (device_id != BMG_DEVICE_ID) 1130c45e76fSMichael J. Ruhl return -ENODEV; 1140c45e76fSMichael J. Ruhl 1150c45e76fSMichael J. Ruhl if (cap_type > WATCHER) 1160c45e76fSMichael J. Ruhl return -EINVAL; 1170c45e76fSMichael J. Ruhl 1180c45e76fSMichael J. Ruhl *offset = 0; 1190c45e76fSMichael J. Ruhl 1200c45e76fSMichael J. Ruhl if (cap_type == CRASHLOG) { 1210c45e76fSMichael J. Ruhl *index = record_id == PUNIT ? 2 : 4; 1220c45e76fSMichael J. Ruhl return 0; 1230c45e76fSMichael J. Ruhl } 1240c45e76fSMichael J. Ruhl 1250c45e76fSMichael J. Ruhl switch (record_id) { 1260c45e76fSMichael J. Ruhl case PUNIT: 1270c45e76fSMichael J. Ruhl *index = 0; 1280c45e76fSMichael J. Ruhl if (cap_type == TELEMETRY) 1290c45e76fSMichael J. Ruhl *offset = PUNIT_TELEMETRY_OFFSET; 1300c45e76fSMichael J. Ruhl else 1310c45e76fSMichael J. Ruhl *offset = PUNIT_WATCHER_OFFSET; 1320c45e76fSMichael J. Ruhl break; 1330c45e76fSMichael J. Ruhl 1340c45e76fSMichael J. Ruhl case OOBMSM_0: 1350c45e76fSMichael J. Ruhl *index = 1; 1360c45e76fSMichael J. Ruhl if (cap_type == WATCHER) 1370c45e76fSMichael J. Ruhl *offset = OOBMSM_0_WATCHER_OFFSET; 1380c45e76fSMichael J. Ruhl break; 1390c45e76fSMichael J. Ruhl 1400c45e76fSMichael J. Ruhl case OOBMSM_1: 1410c45e76fSMichael J. Ruhl *index = 1; 1420c45e76fSMichael J. Ruhl if (cap_type == TELEMETRY) 1430c45e76fSMichael J. Ruhl *offset = OOBMSM_1_TELEMETRY_OFFSET; 1440c45e76fSMichael J. Ruhl break; 1450c45e76fSMichael J. Ruhl default: 1460c45e76fSMichael J. Ruhl return -EINVAL; 1470c45e76fSMichael J. Ruhl } 1480c45e76fSMichael J. Ruhl 1490c45e76fSMichael J. Ruhl return 0; 1500c45e76fSMichael J. Ruhl } 1510c45e76fSMichael J. Ruhl 1520c45e76fSMichael J. Ruhl static int xe_pmt_telem_read(struct pci_dev *pdev, u32 guid, u64 *data, loff_t user_offset, 1530c45e76fSMichael J. Ruhl u32 count) 1540c45e76fSMichael J. Ruhl { 1550c45e76fSMichael J. Ruhl struct xe_device *xe = pdev_to_xe_device(pdev); 1560c45e76fSMichael J. Ruhl void __iomem *telem_addr = xe->mmio.regs + BMG_TELEMETRY_OFFSET; 1570c45e76fSMichael J. Ruhl u32 mem_region; 1580c45e76fSMichael J. Ruhl u32 offset; 1590c45e76fSMichael J. Ruhl int ret; 1600c45e76fSMichael J. Ruhl 1610c45e76fSMichael J. Ruhl ret = xe_guid_decode(guid, &mem_region, &offset); 1620c45e76fSMichael J. Ruhl if (ret) 1630c45e76fSMichael J. Ruhl return ret; 1640c45e76fSMichael J. Ruhl 1650c45e76fSMichael J. Ruhl telem_addr += offset + user_offset; 1660c45e76fSMichael J. Ruhl 1670c45e76fSMichael J. Ruhl guard(mutex)(&xe->pmt.lock); 1680c45e76fSMichael J. Ruhl 1690c45e76fSMichael J. Ruhl /* indicate that we are not at an appropriate power level */ 1700c45e76fSMichael J. Ruhl if (!xe_pm_runtime_get_if_active(xe)) 1710c45e76fSMichael J. Ruhl return -ENODATA; 1720c45e76fSMichael J. Ruhl 1730c45e76fSMichael J. Ruhl /* set SoC re-mapper index register based on GUID memory region */ 1740c45e76fSMichael J. Ruhl xe_mmio_rmw32(xe_root_tile_mmio(xe), SG_REMAP_INDEX1, SG_REMAP_BITS, 1750c45e76fSMichael J. Ruhl REG_FIELD_PREP(SG_REMAP_BITS, mem_region)); 1760c45e76fSMichael J. Ruhl 1770c45e76fSMichael J. Ruhl memcpy_fromio(data, telem_addr, count); 1780c45e76fSMichael J. Ruhl xe_pm_runtime_put(xe); 1790c45e76fSMichael J. Ruhl 1800c45e76fSMichael J. Ruhl return count; 1810c45e76fSMichael J. Ruhl } 1820c45e76fSMichael J. Ruhl 1835cc1ccb6SMichael J. Ruhl static struct pmt_callbacks xe_pmt_cb = { 1840c45e76fSMichael J. Ruhl .read_telem = xe_pmt_telem_read, 1850c45e76fSMichael J. Ruhl }; 1860c45e76fSMichael J. Ruhl 1870c45e76fSMichael J. Ruhl static const int vsec_platforms[] = { 1880c45e76fSMichael J. Ruhl [XE_BATTLEMAGE] = XE_VSEC_BMG, 1890c45e76fSMichael J. Ruhl }; 1900c45e76fSMichael J. Ruhl 1910c45e76fSMichael J. Ruhl static enum xe_vsec get_platform_info(struct xe_device *xe) 1920c45e76fSMichael J. Ruhl { 1930c45e76fSMichael J. Ruhl if (xe->info.platform > XE_BATTLEMAGE) 1940c45e76fSMichael J. Ruhl return XE_VSEC_UNKNOWN; 1950c45e76fSMichael J. Ruhl 1960c45e76fSMichael J. Ruhl return vsec_platforms[xe->info.platform]; 1970c45e76fSMichael J. Ruhl } 1980c45e76fSMichael J. Ruhl 1990c45e76fSMichael J. Ruhl /** 2000c45e76fSMichael J. Ruhl * xe_vsec_init - Initialize resources and add intel_vsec auxiliary 2010c45e76fSMichael J. Ruhl * interface 2020c45e76fSMichael J. Ruhl * @xe: valid xe instance 2030c45e76fSMichael J. Ruhl */ 2040c45e76fSMichael J. Ruhl void xe_vsec_init(struct xe_device *xe) 2050c45e76fSMichael J. Ruhl { 2060c45e76fSMichael J. Ruhl struct intel_vsec_platform_info *info; 2070c45e76fSMichael J. Ruhl struct device *dev = xe->drm.dev; 2080c45e76fSMichael J. Ruhl struct pci_dev *pdev = to_pci_dev(dev); 2090c45e76fSMichael J. Ruhl enum xe_vsec platform; 2100c45e76fSMichael J. Ruhl 2110c45e76fSMichael J. Ruhl platform = get_platform_info(xe); 2120c45e76fSMichael J. Ruhl if (platform == XE_VSEC_UNKNOWN) 2130c45e76fSMichael J. Ruhl return; 2140c45e76fSMichael J. Ruhl 2150c45e76fSMichael J. Ruhl info = &xe_vsec_info[platform]; 2160c45e76fSMichael J. Ruhl if (!info->headers) 2170c45e76fSMichael J. Ruhl return; 2180c45e76fSMichael J. Ruhl 2190c45e76fSMichael J. Ruhl switch (platform) { 2200c45e76fSMichael J. Ruhl case XE_VSEC_BMG: 2210c45e76fSMichael J. Ruhl info->priv_data = &xe_pmt_cb; 2220c45e76fSMichael J. Ruhl break; 2230c45e76fSMichael J. Ruhl default: 2240c45e76fSMichael J. Ruhl break; 2250c45e76fSMichael J. Ruhl } 2260c45e76fSMichael J. Ruhl 2270c45e76fSMichael J. Ruhl /* 2280c45e76fSMichael J. Ruhl * Register a VSEC. Cleanup is handled using device managed 2290c45e76fSMichael J. Ruhl * resources. 2300c45e76fSMichael J. Ruhl */ 2310c45e76fSMichael J. Ruhl intel_vsec_register(pdev, info); 2320c45e76fSMichael J. Ruhl } 233*bc893280SRodrigo Vivi MODULE_IMPORT_NS("INTEL_VSEC"); 234