1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Intel PMC SSRAM TELEMETRY PCI Driver 4 * 5 * Copyright (c) 2023, Intel Corporation. 6 */ 7 8 #include <linux/cleanup.h> 9 #include <linux/intel_vsec.h> 10 #include <linux/pci.h> 11 #include <linux/types.h> 12 #include <linux/io-64-nonatomic-lo-hi.h> 13 14 #include "core.h" 15 #include "ssram_telemetry.h" 16 17 #define SSRAM_HDR_SIZE 0x100 18 #define SSRAM_PWRM_OFFSET 0x14 19 #define SSRAM_DVSEC_OFFSET 0x1C 20 #define SSRAM_DVSEC_SIZE 0x10 21 #define SSRAM_PCH_OFFSET 0x60 22 #define SSRAM_IOE_OFFSET 0x68 23 #define SSRAM_DEVID_OFFSET 0x70 24 25 DEFINE_FREE(pmc_ssram_telemetry_iounmap, void __iomem *, if (_T) iounmap(_T)) 26 27 static struct pmc_ssram_telemetry *pmc_ssram_telems; 28 static bool device_probed; 29 30 static int 31 pmc_ssram_telemetry_add_pmt(struct pci_dev *pcidev, u64 ssram_base, void __iomem *ssram) 32 { 33 struct intel_vsec_platform_info info = {}; 34 struct intel_vsec_header *headers[2] = {}; 35 struct intel_vsec_header header; 36 void __iomem *dvsec; 37 u32 dvsec_offset; 38 u32 table, hdr; 39 40 dvsec_offset = readl(ssram + SSRAM_DVSEC_OFFSET); 41 dvsec = ioremap(ssram_base + dvsec_offset, SSRAM_DVSEC_SIZE); 42 if (!dvsec) 43 return -ENOMEM; 44 45 hdr = readl(dvsec + PCI_DVSEC_HEADER1); 46 header.id = readw(dvsec + PCI_DVSEC_HEADER2); 47 header.rev = PCI_DVSEC_HEADER1_REV(hdr); 48 header.length = PCI_DVSEC_HEADER1_LEN(hdr); 49 header.num_entries = readb(dvsec + INTEL_DVSEC_ENTRIES); 50 header.entry_size = readb(dvsec + INTEL_DVSEC_SIZE); 51 52 table = readl(dvsec + INTEL_DVSEC_TABLE); 53 header.tbir = INTEL_DVSEC_TABLE_BAR(table); 54 header.offset = INTEL_DVSEC_TABLE_OFFSET(table); 55 iounmap(dvsec); 56 57 headers[0] = &header; 58 info.caps = VSEC_CAP_TELEMETRY; 59 info.headers = headers; 60 info.base_addr = ssram_base; 61 info.parent = &pcidev->dev; 62 63 return intel_vsec_register(pcidev, &info); 64 } 65 66 static inline u64 get_base(void __iomem *addr, u32 offset) 67 { 68 return lo_hi_readq(addr + offset) & GENMASK_ULL(63, 3); 69 } 70 71 static int 72 pmc_ssram_telemetry_get_pmc(struct pci_dev *pcidev, unsigned int pmc_idx, u32 offset) 73 { 74 void __iomem __free(pmc_ssram_telemetry_iounmap) *tmp_ssram = NULL; 75 void __iomem __free(pmc_ssram_telemetry_iounmap) *ssram = NULL; 76 u64 ssram_base, pwrm_base; 77 u16 devid; 78 79 ssram_base = pci_resource_start(pcidev, 0); 80 tmp_ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); 81 if (!tmp_ssram) 82 return -ENOMEM; 83 84 if (pmc_idx != PMC_IDX_MAIN) { 85 /* 86 * The secondary PMC BARS (which are behind hidden PCI devices) 87 * are read from fixed offsets in MMIO of the primary PMC BAR. 88 * If a device is not present, the value will be 0. 89 */ 90 ssram_base = get_base(tmp_ssram, offset); 91 if (!ssram_base) 92 return 0; 93 94 ssram = ioremap(ssram_base, SSRAM_HDR_SIZE); 95 if (!ssram) 96 return -ENOMEM; 97 98 } else { 99 ssram = no_free_ptr(tmp_ssram); 100 } 101 102 pwrm_base = get_base(ssram, SSRAM_PWRM_OFFSET); 103 devid = readw(ssram + SSRAM_DEVID_OFFSET); 104 105 pmc_ssram_telems[pmc_idx].devid = devid; 106 pmc_ssram_telems[pmc_idx].base_addr = pwrm_base; 107 108 /* Find and register and PMC telemetry entries */ 109 return pmc_ssram_telemetry_add_pmt(pcidev, ssram_base, ssram); 110 } 111 112 /** 113 * pmc_ssram_telemetry_get_pmc_info() - Get a PMC devid and base_addr information 114 * @pmc_idx: Index of the PMC 115 * @pmc_ssram_telemetry: pmc_ssram_telemetry structure to store the PMC information 116 * 117 * Return: 118 * * 0 - Success 119 * * -EAGAIN - Probe function has not finished yet. Try again. 120 * * -EINVAL - Invalid pmc_idx 121 * * -ENODEV - PMC device is not available 122 */ 123 int pmc_ssram_telemetry_get_pmc_info(unsigned int pmc_idx, 124 struct pmc_ssram_telemetry *pmc_ssram_telemetry) 125 { 126 /* 127 * PMCs are discovered in probe function. If this function is called before 128 * probe function complete, the result would be invalid. Use device_probed 129 * variable to avoid this case. Return -EAGAIN to inform the consumer to call 130 * again later. 131 */ 132 if (!device_probed) 133 return -EAGAIN; 134 135 /* 136 * Memory barrier is used to ensure the correct read order between 137 * device_probed variable and PMC info. 138 */ 139 smp_rmb(); 140 if (pmc_idx >= MAX_NUM_PMC) 141 return -EINVAL; 142 143 if (!pmc_ssram_telems || !pmc_ssram_telems[pmc_idx].devid) 144 return -ENODEV; 145 146 pmc_ssram_telemetry->devid = pmc_ssram_telems[pmc_idx].devid; 147 pmc_ssram_telemetry->base_addr = pmc_ssram_telems[pmc_idx].base_addr; 148 return 0; 149 } 150 EXPORT_SYMBOL_GPL(pmc_ssram_telemetry_get_pmc_info); 151 152 static int intel_pmc_ssram_telemetry_probe(struct pci_dev *pcidev, const struct pci_device_id *id) 153 { 154 int ret; 155 156 pmc_ssram_telems = devm_kzalloc(&pcidev->dev, sizeof(*pmc_ssram_telems) * MAX_NUM_PMC, 157 GFP_KERNEL); 158 if (!pmc_ssram_telems) { 159 ret = -ENOMEM; 160 goto probe_finish; 161 } 162 163 ret = pcim_enable_device(pcidev); 164 if (ret) { 165 dev_dbg(&pcidev->dev, "failed to enable PMC SSRAM device\n"); 166 goto probe_finish; 167 } 168 169 ret = pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_MAIN, 0); 170 if (ret) 171 goto probe_finish; 172 173 pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_IOE, SSRAM_IOE_OFFSET); 174 pmc_ssram_telemetry_get_pmc(pcidev, PMC_IDX_PCH, SSRAM_PCH_OFFSET); 175 176 probe_finish: 177 /* 178 * Memory barrier is used to ensure the correct write order between PMC info 179 * and device_probed variable. 180 */ 181 smp_wmb(); 182 device_probed = true; 183 return ret; 184 } 185 186 static const struct pci_device_id intel_pmc_ssram_telemetry_pci_ids[] = { 187 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_MTL_SOCM) }, 188 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCS) }, 189 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_ARL_SOCM) }, 190 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_LNL_SOCM) }, 191 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDH) }, 192 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PMC_DEVID_PTL_PCDP) }, 193 { } 194 }; 195 MODULE_DEVICE_TABLE(pci, intel_pmc_ssram_telemetry_pci_ids); 196 197 static struct pci_driver intel_pmc_ssram_telemetry_driver = { 198 .name = "intel_pmc_ssram_telemetry", 199 .id_table = intel_pmc_ssram_telemetry_pci_ids, 200 .probe = intel_pmc_ssram_telemetry_probe, 201 }; 202 module_pci_driver(intel_pmc_ssram_telemetry_driver); 203 204 MODULE_IMPORT_NS("INTEL_VSEC"); 205 MODULE_AUTHOR("Xi Pardee <xi.pardee@intel.com>"); 206 MODULE_DESCRIPTION("Intel PMC SSRAM Telemetry driver"); 207 MODULE_LICENSE("GPL"); 208