Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Intel Platform Monitory Technology Telemetry driver
0004  *
0005  * Copyright (c) 2020, Intel Corporation.
0006  * All Rights Reserved.
0007  *
0008  * Author: "David E. Box" <david.e.box@linux.intel.com>
0009  */
0010 
0011 #include <linux/auxiliary_bus.h>
0012 #include <linux/kernel.h>
0013 #include <linux/module.h>
0014 #include <linux/pci.h>
0015 #include <linux/slab.h>
0016 #include <linux/uaccess.h>
0017 #include <linux/overflow.h>
0018 
0019 #include "../vsec.h"
0020 #include "class.h"
0021 
0022 #define TELEM_SIZE_OFFSET   0x0
0023 #define TELEM_GUID_OFFSET   0x4
0024 #define TELEM_BASE_OFFSET   0x8
0025 #define TELEM_ACCESS(v)     ((v) & GENMASK(3, 0))
0026 #define TELEM_TYPE(v)       (((v) & GENMASK(7, 4)) >> 4)
0027 /* size is in bytes */
0028 #define TELEM_SIZE(v)       (((v) & GENMASK(27, 12)) >> 10)
0029 
0030 /* Used by client hardware to identify a fixed telemetry entry*/
0031 #define TELEM_CLIENT_FIXED_BLOCK_GUID   0x10000000
0032 
0033 enum telem_type {
0034     TELEM_TYPE_PUNIT = 0,
0035     TELEM_TYPE_CRASHLOG,
0036     TELEM_TYPE_PUNIT_FIXED,
0037 };
0038 
0039 struct pmt_telem_priv {
0040     int             num_entries;
0041     struct intel_pmt_entry      entry[];
0042 };
0043 
0044 static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
0045                       struct device *dev)
0046 {
0047     u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
0048 
0049     if (intel_pmt_is_early_client_hw(dev)) {
0050         u32 type = TELEM_TYPE(readl(entry->disc_table));
0051 
0052         if ((type == TELEM_TYPE_PUNIT_FIXED) ||
0053             (guid == TELEM_CLIENT_FIXED_BLOCK_GUID))
0054             return true;
0055     }
0056 
0057     return false;
0058 }
0059 
0060 static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
0061                    struct intel_pmt_header *header,
0062                    struct device *dev)
0063 {
0064     void __iomem *disc_table = entry->disc_table;
0065 
0066     if (pmt_telem_region_overlaps(entry, dev))
0067         return 1;
0068 
0069     header->access_type = TELEM_ACCESS(readl(disc_table));
0070     header->guid = readl(disc_table + TELEM_GUID_OFFSET);
0071     header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
0072 
0073     /* Size is measured in DWORDS, but accessor returns bytes */
0074     header->size = TELEM_SIZE(readl(disc_table));
0075 
0076     /*
0077      * Some devices may expose non-functioning entries that are
0078      * reserved for future use. They have zero size. Do not fail
0079      * probe for these. Just ignore them.
0080      */
0081     if (header->size == 0)
0082         return 1;
0083 
0084     return 0;
0085 }
0086 
0087 static DEFINE_XARRAY_ALLOC(telem_array);
0088 static struct intel_pmt_namespace pmt_telem_ns = {
0089     .name = "telem",
0090     .xa = &telem_array,
0091     .pmt_header_decode = pmt_telem_header_decode,
0092 };
0093 
0094 static void pmt_telem_remove(struct auxiliary_device *auxdev)
0095 {
0096     struct pmt_telem_priv *priv = auxiliary_get_drvdata(auxdev);
0097     int i;
0098 
0099     for (i = 0; i < priv->num_entries; i++)
0100         intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns);
0101 }
0102 
0103 static int pmt_telem_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
0104 {
0105     struct intel_vsec_device *intel_vsec_dev = auxdev_to_ivdev(auxdev);
0106     struct pmt_telem_priv *priv;
0107     size_t size;
0108     int i, ret;
0109 
0110     size = struct_size(priv, entry, intel_vsec_dev->num_resources);
0111     priv = devm_kzalloc(&auxdev->dev, size, GFP_KERNEL);
0112     if (!priv)
0113         return -ENOMEM;
0114 
0115     auxiliary_set_drvdata(auxdev, priv);
0116 
0117     for (i = 0; i < intel_vsec_dev->num_resources; i++) {
0118         struct intel_pmt_entry *entry = &priv->entry[priv->num_entries];
0119 
0120         ret = intel_pmt_dev_create(entry, &pmt_telem_ns, intel_vsec_dev, i);
0121         if (ret < 0)
0122             goto abort_probe;
0123         if (ret)
0124             continue;
0125 
0126         priv->num_entries++;
0127     }
0128 
0129     return 0;
0130 abort_probe:
0131     pmt_telem_remove(auxdev);
0132     return ret;
0133 }
0134 
0135 static const struct auxiliary_device_id pmt_telem_id_table[] = {
0136     { .name = "intel_vsec.telemetry" },
0137     {}
0138 };
0139 MODULE_DEVICE_TABLE(auxiliary, pmt_telem_id_table);
0140 
0141 static struct auxiliary_driver pmt_telem_aux_driver = {
0142     .id_table   = pmt_telem_id_table,
0143     .remove     = pmt_telem_remove,
0144     .probe      = pmt_telem_probe,
0145 };
0146 
0147 static int __init pmt_telem_init(void)
0148 {
0149     return auxiliary_driver_register(&pmt_telem_aux_driver);
0150 }
0151 module_init(pmt_telem_init);
0152 
0153 static void __exit pmt_telem_exit(void)
0154 {
0155     auxiliary_driver_unregister(&pmt_telem_aux_driver);
0156     xa_destroy(&telem_array);
0157 }
0158 module_exit(pmt_telem_exit);
0159 
0160 MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
0161 MODULE_DESCRIPTION("Intel PMT Telemetry driver");
0162 MODULE_LICENSE("GPL v2");