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)) 26*ba7e421eSDavid E. Box #define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) 27e184b1e5SDavid E. Box /* size is in bytes */ 28e184b1e5SDavid E. Box #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) 29e184b1e5SDavid E. Box 30e184b1e5SDavid E. Box /* Used by client hardware to identify a fixed telemetry entry*/ 31e184b1e5SDavid E. Box #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 32e184b1e5SDavid E. Box 33*ba7e421eSDavid E. Box enum telem_type { 34*ba7e421eSDavid E. Box TELEM_TYPE_PUNIT = 0, 35*ba7e421eSDavid E. Box TELEM_TYPE_CRASHLOG, 36*ba7e421eSDavid E. Box TELEM_TYPE_PUNIT_FIXED, 37*ba7e421eSDavid E. Box }; 38*ba7e421eSDavid E. Box 39e184b1e5SDavid E. Box struct pmt_telem_priv { 40e184b1e5SDavid E. Box int num_entries; 41e184b1e5SDavid E. Box struct intel_pmt_entry entry[]; 42e184b1e5SDavid E. Box }; 43e184b1e5SDavid E. Box 44e184b1e5SDavid E. Box static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, 45e184b1e5SDavid E. Box struct device *dev) 46e184b1e5SDavid E. Box { 47e184b1e5SDavid E. Box u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); 48e184b1e5SDavid E. Box 49*ba7e421eSDavid E. Box if (intel_pmt_is_early_client_hw(dev)) { 50*ba7e421eSDavid E. Box u32 type = TELEM_TYPE(readl(entry->disc_table)); 51e184b1e5SDavid E. Box 52*ba7e421eSDavid E. Box if ((type == TELEM_TYPE_PUNIT_FIXED) || 53*ba7e421eSDavid E. Box (guid == TELEM_CLIENT_FIXED_BLOCK_GUID)) 54*ba7e421eSDavid E. Box return true; 55*ba7e421eSDavid E. Box } 56*ba7e421eSDavid E. Box 57*ba7e421eSDavid E. Box return false; 58e184b1e5SDavid E. Box } 59e184b1e5SDavid E. Box 60e184b1e5SDavid E. Box static int pmt_telem_header_decode(struct intel_pmt_entry *entry, 61e184b1e5SDavid E. Box struct intel_pmt_header *header, 62e184b1e5SDavid E. Box struct device *dev) 63e184b1e5SDavid E. Box { 64e184b1e5SDavid E. Box void __iomem *disc_table = entry->disc_table; 65e184b1e5SDavid E. Box 66e184b1e5SDavid E. Box if (pmt_telem_region_overlaps(entry, dev)) 67e184b1e5SDavid E. Box return 1; 68e184b1e5SDavid E. Box 69e184b1e5SDavid E. Box header->access_type = TELEM_ACCESS(readl(disc_table)); 70e184b1e5SDavid E. Box header->guid = readl(disc_table + TELEM_GUID_OFFSET); 71e184b1e5SDavid E. Box header->base_offset = readl(disc_table + TELEM_BASE_OFFSET); 72e184b1e5SDavid E. Box 73e184b1e5SDavid E. Box /* Size is measured in DWORDS, but accessor returns bytes */ 74e184b1e5SDavid E. Box header->size = TELEM_SIZE(readl(disc_table)); 75e184b1e5SDavid E. Box 76ef195e8aSDavid E. Box /* 77ef195e8aSDavid E. Box * Some devices may expose non-functioning entries that are 78ef195e8aSDavid E. Box * reserved for future use. They have zero size. Do not fail 79ef195e8aSDavid E. Box * probe for these. Just ignore them. 80ef195e8aSDavid E. Box */ 81ef195e8aSDavid E. Box if (header->size == 0) 82ef195e8aSDavid E. Box return 1; 83ef195e8aSDavid E. Box 84e184b1e5SDavid E. Box return 0; 85e184b1e5SDavid E. Box } 86e184b1e5SDavid E. Box 87e184b1e5SDavid E. Box static DEFINE_XARRAY_ALLOC(telem_array); 88e184b1e5SDavid E. Box static struct intel_pmt_namespace pmt_telem_ns = { 89e184b1e5SDavid E. Box .name = "telem", 90e184b1e5SDavid E. Box .xa = &telem_array, 91e184b1e5SDavid E. Box .pmt_header_decode = pmt_telem_header_decode, 92e184b1e5SDavid E. Box }; 93e184b1e5SDavid E. Box 94a3c8f906SDavid E. Box static void pmt_telem_remove(struct auxiliary_device *auxdev) 95e184b1e5SDavid E. Box { 96a3c8f906SDavid E. Box struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev); 97e184b1e5SDavid E. Box int i; 98e184b1e5SDavid E. Box 99e184b1e5SDavid E. Box for (i = 0; i < priv->num_entries; i++) 100e184b1e5SDavid E. Box intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns); 101e184b1e5SDavid E. Box } 102e184b1e5SDavid E. Box 103a3c8f906SDavid E. Box static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 104e184b1e5SDavid E. Box { 105a3c8f906SDavid E. Box struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev); 106e184b1e5SDavid E. Box struct pmt_telem_priv *priv; 107e184b1e5SDavid E. Box size_t size; 108e184b1e5SDavid E. Box int i, ret; 109e184b1e5SDavid E. Box 110a3c8f906SDavid E. Box size = struct_size(priv, entry, intel_vsec_dev->num_resources); 111a3c8f906SDavid E. Box priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL); 112e184b1e5SDavid E. Box if (!priv) 113e184b1e5SDavid E. Box return -ENOMEM; 114e184b1e5SDavid E. Box 115a3c8f906SDavid E. Box auxiliary_set_drvdata(auxdev, priv); 116e184b1e5SDavid E. Box 117a3c8f906SDavid E. Box for (i = 0; i < intel_vsec_dev->num_resources; i++) { 1182cdfa0c2SPrarit Bhargava struct intel_pmt_entry *entry = &priv->entry[priv->num_entries]; 119e184b1e5SDavid E. Box 120a3c8f906SDavid E. Box ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i); 121e184b1e5SDavid E. Box if (ret < 0) 122e184b1e5SDavid E. Box goto abort_probe; 123e184b1e5SDavid E. Box if (ret) 124e184b1e5SDavid E. Box continue; 125e184b1e5SDavid E. Box 126e184b1e5SDavid E. Box priv->num_entries++; 127e184b1e5SDavid E. Box } 128e184b1e5SDavid E. Box 129e184b1e5SDavid E. Box return 0; 130e184b1e5SDavid E. Box abort_probe: 131a3c8f906SDavid E. Box pmt_telem_remove(auxdev); 132e184b1e5SDavid E. Box return ret; 133e184b1e5SDavid E. Box } 134e184b1e5SDavid E. Box 135a3c8f906SDavid E. Box static const struct auxiliary_device_id pmt_telem_id_table[] = { 136a3c8f906SDavid E. Box { .name = "intel_vsec.telemetry" }, 137a3c8f906SDavid E. Box {} 138a3c8f906SDavid E. Box }; 139a3c8f906SDavid E. Box MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table); 140a3c8f906SDavid E. Box 141a3c8f906SDavid E. Box static struct auxiliary_driver pmt_telem_aux_driver = { 142a3c8f906SDavid E. Box .id_table = pmt_telem_id_table, 143e184b1e5SDavid E. Box .remove = pmt_telem_remove, 144e184b1e5SDavid E. Box .probe = pmt_telem_probe, 145e184b1e5SDavid E. Box }; 146e184b1e5SDavid E. Box 147e184b1e5SDavid E. Box static int __init pmt_telem_init(void) 148e184b1e5SDavid E. Box { 149a3c8f906SDavid E. Box return auxiliary_driver_register(&pmt_telem_aux_driver); 150e184b1e5SDavid E. Box } 151e184b1e5SDavid E. Box module_init(pmt_telem_init); 152e184b1e5SDavid E. Box 153e184b1e5SDavid E. Box static void __exit pmt_telem_exit(void) 154e184b1e5SDavid E. Box { 155a3c8f906SDavid E. Box auxiliary_driver_unregister(&pmt_telem_aux_driver); 156e184b1e5SDavid E. Box xa_destroy(&telem_array); 157e184b1e5SDavid E. Box } 158e184b1e5SDavid E. Box module_exit(pmt_telem_exit); 159e184b1e5SDavid E. Box 160e184b1e5SDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 161e184b1e5SDavid E. Box MODULE_DESCRIPTION("Intel PMT Telemetry driver"); 162e184b1e5SDavid E. Box MODULE_LICENSE("GPL v2"); 163