0001
0002
0003
0004
0005
0006
0007
0008
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
0028 #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10)
0029
0030
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
0074 header->size = TELEM_SIZE(readl(disc_table));
0075
0076
0077
0078
0079
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");