1e184b1e5SDavid E. Box // SPDX-License-Identifier: GPL-2.0 2e184b1e5SDavid E. Box /* 3e184b1e5SDavid E. Box * Intel Platform Monitory Technology Telemetry driver 4e184b1e5SDavid E. Box * 5e184b1e5SDavid E. Box * Copyright (c) 2020, Intel Corporation. 6e184b1e5SDavid E. Box * All Rights Reserved. 7e184b1e5SDavid E. Box * 8e184b1e5SDavid E. Box * Author: "David E. Box" <david.e.box@linux.intel.com> 9e184b1e5SDavid E. Box */ 10e184b1e5SDavid E. Box 11a3c8f906SDavid E. Box #include <linux/auxiliary_bus.h> 12e184b1e5SDavid E. Box #include <linux/kernel.h> 13e184b1e5SDavid E. Box #include <linux/module.h> 14e184b1e5SDavid E. Box #include <linux/pci.h> 15e184b1e5SDavid E. Box #include <linux/slab.h> 16e184b1e5SDavid E. Box #include <linux/uaccess.h> 17e184b1e5SDavid E. Box #include <linux/overflow.h> 18e184b1e5SDavid E. Box 19a3c8f906SDavid E. Box #include "../vsec.h" 20e184b1e5SDavid E. Box #include "class.h" 21e184b1e5SDavid E. Box 22e184b1e5SDavid E. Box #define TELEM_SIZE_OFFSET 0x0 23e184b1e5SDavid E. Box #define TELEM_GUID_OFFSET 0x4 24e184b1e5SDavid E. Box #define TELEM_BASE_OFFSET 0x8 25e184b1e5SDavid E. Box #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) 26e184b1e5SDavid E. Box /* size is in bytes */ 27e184b1e5SDavid E. Box #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) 28e184b1e5SDavid E. Box 29e184b1e5SDavid E. Box /* Used by client hardware to identify a fixed telemetry entry*/ 30e184b1e5SDavid E. Box #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 31e184b1e5SDavid E. Box 32e184b1e5SDavid E. Box struct pmt_telem_priv { 33e184b1e5SDavid E. Box int num_entries; 34e184b1e5SDavid E. Box struct intel_pmt_entry entry[]; 35e184b1e5SDavid E. Box }; 36e184b1e5SDavid E. Box 37e184b1e5SDavid E. Box static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, 38e184b1e5SDavid E. Box struct device *dev) 39e184b1e5SDavid E. Box { 40e184b1e5SDavid E. Box u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); 41e184b1e5SDavid E. Box 42e184b1e5SDavid E. Box if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) 43e184b1e5SDavid E. Box return false; 44e184b1e5SDavid E. Box 45e184b1e5SDavid E. Box return intel_pmt_is_early_client_hw(dev); 46e184b1e5SDavid E. Box } 47e184b1e5SDavid E. Box 48e184b1e5SDavid E. Box static int pmt_telem_header_decode(struct intel_pmt_entry *entry, 49e184b1e5SDavid E. Box struct intel_pmt_header *header, 50e184b1e5SDavid E. Box struct device *dev) 51e184b1e5SDavid E. Box { 52e184b1e5SDavid E. Box void __iomem *disc_table = entry->disc_table; 53e184b1e5SDavid E. Box 54e184b1e5SDavid E. Box if (pmt_telem_region_overlaps(entry, dev)) 55e184b1e5SDavid E. Box return 1; 56e184b1e5SDavid E. Box 57e184b1e5SDavid E. Box header->access_type = TELEM_ACCESS(readl(disc_table)); 58e184b1e5SDavid E. Box header->guid = readl(disc_table + TELEM_GUID_OFFSET); 59e184b1e5SDavid E. Box header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); 60e184b1e5SDavid E. Box 61e184b1e5SDavid E. Box /* Size is measured in DWORDS, but accessor returns bytes */ 62e184b1e5SDavid E. Box header->size = TELEM_SIZE(readl(disc_table)); 63e184b1e5SDavid E. Box 64ef195e8aSDavid E. Box /* 65ef195e8aSDavid E. Box * Some devices may expose non-functioning entries that are 66ef195e8aSDavid E. Box * reserved for future use. They have zero size. Do not fail 67ef195e8aSDavid E. Box * probe for these. Just ignore them. 68ef195e8aSDavid E. Box */ 69ef195e8aSDavid E. Box if (header->size == 0) 70ef195e8aSDavid E. Box return 1; 71ef195e8aSDavid E. Box 72e184b1e5SDavid E. Box return 0; 73e184b1e5SDavid E. Box } 74e184b1e5SDavid E. Box 75e184b1e5SDavid E. Box static DEFINE_XARRAY_ALLOC(telem_array); 76e184b1e5SDavid E. Box static struct intel_pmt_namespace pmt_telem_ns = { 77e184b1e5SDavid E. Box .name = "telem", 78e184b1e5SDavid E. Box .xa = &telem_array, 79e184b1e5SDavid E. Box .pmt_header_decode = pmt_telem_header_decode, 80e184b1e5SDavid E. Box }; 81e184b1e5SDavid E. Box 82a3c8f906SDavid E. Box static void pmt_telem_remove(struct auxiliary_device *auxdev) 83e184b1e5SDavid E. Box { 84a3c8f906SDavid E. Box struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); 85e184b1e5SDavid E. Box int i; 86e184b1e5SDavid E. Box 87e184b1e5SDavid E. Box for (i = 0; i < priv->num_entries; i++) 88e184b1e5SDavid E. Box intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); 89e184b1e5SDavid E. Box } 90e184b1e5SDavid E. Box 91a3c8f906SDavid E. Box static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 92e184b1e5SDavid E. Box { 93a3c8f906SDavid E. Box struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); 94e184b1e5SDavid E. Box struct pmt_telem_priv *priv; 95e184b1e5SDavid E. Box size_t size; 96e184b1e5SDavid E. Box int i, ret; 97e184b1e5SDavid E. Box 98a3c8f906SDavid E. Box size = struct_size(priv, entry, intel_vsec_dev->num_resources); 99a3c8f906SDavid E. Box priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); 100e184b1e5SDavid E. Box if (!priv) 101e184b1e5SDavid E. Box return -ENOMEM; 102e184b1e5SDavid E. Box 103a3c8f906SDavid E. Box auxiliary_set_drvdata(auxdev, priv); 104e184b1e5SDavid E. Box 105a3c8f906SDavid E. Box for (i = 0; i < intel_vsec_dev->num_resources; i++) { 106*2cdfa0c2SPrarit Bhargava struct intel_pmt_entry *entry = &priv->entry[priv->num_entries]; 107e184b1e5SDavid E. Box 108a3c8f906SDavid E. Box ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); 109e184b1e5SDavid E. Box if (ret < 0) 110e184b1e5SDavid E. Box goto abort_probe; 111e184b1e5SDavid E. Box if (ret) 112e184b1e5SDavid E. Box continue; 113e184b1e5SDavid E. Box 114e184b1e5SDavid E. Box priv->num_entries++; 115e184b1e5SDavid E. Box } 116e184b1e5SDavid E. Box 117e184b1e5SDavid E. Box return 0; 118e184b1e5SDavid E. Box abort_probe: 119a3c8f906SDavid E. Box pmt_telem_remove(auxdev); 120e184b1e5SDavid E. Box return ret; 121e184b1e5SDavid E. Box } 122e184b1e5SDavid E. Box 123a3c8f906SDavid E. Box static const struct auxiliary_device_id pmt_telem_id_table[] = { 124a3c8f906SDavid E. Box { .name = "intel_vsec.telemetry" }, 125a3c8f906SDavid E. Box {} 126a3c8f906SDavid E. Box }; 127a3c8f906SDavid E. Box MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table); 128a3c8f906SDavid E. Box 129a3c8f906SDavid E. Box static struct auxiliary_driver pmt_telem_aux_driver = { 130a3c8f906SDavid E. Box .id_table = pmt_telem_id_table, 131e184b1e5SDavid E. Box .remove = pmt_telem_remove, 132e184b1e5SDavid E. Box .probe = pmt_telem_probe, 133e184b1e5SDavid E. Box }; 134e184b1e5SDavid E. Box 135e184b1e5SDavid E. Box static int __init pmt_telem_init(void) 136e184b1e5SDavid E. Box { 137a3c8f906SDavid E. Box return auxiliary_driver_register(&pmt_telem_aux_driver); 138e184b1e5SDavid E. Box } 139e184b1e5SDavid E. Box module_init(pmt_telem_init); 140e184b1e5SDavid E. Box 141e184b1e5SDavid E. Box static void __exit pmt_telem_exit(void) 142e184b1e5SDavid E. Box { 143a3c8f906SDavid E. Box auxiliary_driver_unregister(&pmt_telem_aux_driver); 144e184b1e5SDavid E. Box xa_destroy(&telem_array); 145e184b1e5SDavid E. Box } 146e184b1e5SDavid E. Box module_exit(pmt_telem_exit); 147e184b1e5SDavid E. Box 148e184b1e5SDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 149e184b1e5SDavid E. Box MODULE_DESCRIPTION("Intel PMT Telemetry driver"); 150e184b1e5SDavid E. Box MODULE_LICENSE("GPL v2"); 151