Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Intel Platform Monitory Technology Telemetry driver
  4 *
  5 * Copyright (c) 2020, Intel Corporation.
  6 * All Rights Reserved.
  7 *
  8 * Author: "David E. Box" <david.e.box@linux.intel.com>
  9 */
 10
 11#include <linux/kernel.h>
 12#include <linux/module.h>
 13#include <linux/pci.h>
 14#include <linux/slab.h>
 15#include <linux/uaccess.h>
 16#include <linux/overflow.h>
 17
 18#include "intel_pmt_class.h"
 19
 20#define TELEM_DEV_NAME		"pmt_telemetry"
 21
 22#define TELEM_SIZE_OFFSET	0x0
 23#define TELEM_GUID_OFFSET	0x4
 24#define TELEM_BASE_OFFSET	0x8
 25#define TELEM_ACCESS(v)		((v) & GENMASK(3, 0))
 26/* size is in bytes */
 27#define TELEM_SIZE(v)		(((v) & GENMASK(27, 12)) >> 10)
 28
 29/* Used by client hardware to identify a fixed telemetry entry*/
 30#define TELEM_CLIENT_FIXED_BLOCK_GUID	0x10000000
 31
 32struct pmt_telem_priv {
 33	int				num_entries;
 34	struct intel_pmt_entry		entry[];
 35};
 36
 37static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry,
 38				      struct device *dev)
 39{
 40	u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET);
 41
 42	if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID)
 43		return false;
 44
 45	return intel_pmt_is_early_client_hw(dev);
 46}
 47
 48static int pmt_telem_header_decode(struct intel_pmt_entry *entry,
 49				   struct intel_pmt_header *header,
 50				   struct device *dev)
 51{
 52	void __iomem *disc_table = entry->disc_table;
 53
 54	if (pmt_telem_region_overlaps(entry, dev))
 55		return 1;
 56
 57	header->access_type = TELEM_ACCESS(readl(disc_table));
 58	header->guid = readl(disc_table + TELEM_GUID_OFFSET);
 59	header->base_offset = readl(disc_table + TELEM_BASE_OFFSET);
 60
 61	/* Size is measured in DWORDS, but accessor returns bytes */
 62	header->size = TELEM_SIZE(readl(disc_table));
 63
 64	return 0;
 65}
 66
 67static DEFINE_XARRAY_ALLOC(telem_array);
 68static struct intel_pmt_namespace pmt_telem_ns = {
 69	.name = "telem",
 70	.xa = &telem_array,
 71	.pmt_header_decode = pmt_telem_header_decode,
 72};
 73
 74static int pmt_telem_remove(struct platform_device *pdev)
 75{
 76	struct pmt_telem_priv *priv = platform_get_drvdata(pdev);
 77	int i;
 78
 79	for (i = 0; i < priv->num_entries; i++)
 80		intel_pmt_dev_destroy(&priv->entry[i], &pmt_telem_ns);
 81
 82	return 0;
 83}
 84
 85static int pmt_telem_probe(struct platform_device *pdev)
 86{
 87	struct pmt_telem_priv *priv;
 88	size_t size;
 89	int i, ret;
 90
 91	size = struct_size(priv, entry, pdev->num_resources);
 92	priv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
 93	if (!priv)
 94		return -ENOMEM;
 95
 96	platform_set_drvdata(pdev, priv);
 97
 98	for (i = 0; i < pdev->num_resources; i++) {
 99		struct intel_pmt_entry *entry = &priv->entry[i];
100
101		ret = intel_pmt_dev_create(entry, &pmt_telem_ns, pdev, i);
102		if (ret < 0)
103			goto abort_probe;
104		if (ret)
105			continue;
106
107		priv->num_entries++;
108	}
109
110	return 0;
111abort_probe:
112	pmt_telem_remove(pdev);
113	return ret;
114}
115
116static struct platform_driver pmt_telem_driver = {
117	.driver = {
118		.name   = TELEM_DEV_NAME,
119	},
120	.remove = pmt_telem_remove,
121	.probe  = pmt_telem_probe,
122};
123
124static int __init pmt_telem_init(void)
125{
126	return platform_driver_register(&pmt_telem_driver);
127}
128module_init(pmt_telem_init);
129
130static void __exit pmt_telem_exit(void)
131{
132	platform_driver_unregister(&pmt_telem_driver);
133	xa_destroy(&telem_array);
134}
135module_exit(pmt_telem_exit);
136
137MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
138MODULE_DESCRIPTION("Intel PMT Telemetry driver");
139MODULE_ALIAS("platform:" TELEM_DEV_NAME);
140MODULE_LICENSE("GPL v2");