// SPDX-License-Identifier: GPL-2.0-only /* * intel-tpmi : Driver to enumerate TPMI features and create devices * * Copyright (c) 2023, Intel Corporation. * All Rights Reserved. * * The TPMI (Topology Aware Register and PM Capsule Interface) provides a * flexible, extendable and PCIe enumerable MMIO interface for PM features. * * For example Intel RAPL (Running Average Power Limit) provides a MMIO * interface using TPMI. This has advantage over traditional MSR * (Model Specific Register) interface, where a thread needs to be scheduled * on the target CPU to read or write. Also the RAPL features vary between * CPU models, and hence lot of model specific code. Here TPMI provides an * architectural interface by providing hierarchical tables and fields, * which will not need any model specific implementation. * * The TPMI interface uses a PCI VSEC structure to expose the location of * MMIO region. * * This VSEC structure is present in the PCI configuration space of the * Intel Out-of-Band (OOB) device, which is handled by the Intel VSEC * driver. The Intel VSEC driver parses VSEC structures present in the PCI * configuration space of the given device and creates an auxiliary device * object for each of them. In particular, it creates an auxiliary device * object representing TPMI that can be bound by an auxiliary driver. * * This TPMI driver will bind to the TPMI auxiliary device object created * by the Intel VSEC driver. * * The TPMI specification defines a PFS (PM Feature Structure) table. * This table is present in the TPMI MMIO region. The starting address * of PFS is derived from the tBIR (Bar Indicator Register) and "Address" * field from the VSEC header. * * Each TPMI PM feature has one entry in the PFS with a unique TPMI * ID and its access details. The TPMI driver creates device nodes * for the supported PM features. * * The names of the devices created by the TPMI driver start with the * "intel_vsec.tpmi-" prefix which is followed by a specific name of the * given PM feature (for example, "intel_vsec.tpmi-rapl.0"). * * The device nodes are create by using interface "intel_vsec_add_aux()" * provided by the Intel VSEC driver. */ #include <linux/auxiliary_bus.h> #include <linux/bitfield.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/intel_tpmi.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/security.h> #include <linux/sizes.h> #include <linux/string_helpers.h> #include "vsec.h" /** * struct intel_tpmi_pfs_entry - TPMI PM Feature Structure (PFS) entry * @tpmi_id: TPMI feature identifier (what the feature is and its data format). * @num_entries: Number of feature interface instances present in the PFS. * This represents the maximum number of Power domains in the SoC. * @entry_size: Interface instance entry size in 32-bit words. * @cap_offset: Offset from the PM_Features base address to the base of the PM VSEC * register bank in KB. * @attribute: Feature attribute: 0=BIOS. 1=OS. 2-3=Reserved. * @reserved: Bits for use in the future. * * Represents one TPMI feature entry data in the PFS retrieved as is * from the hardware. */ struct intel_tpmi_pfs_entry { … } __packed; /** * struct intel_tpmi_pm_feature - TPMI PM Feature information for a TPMI ID * @pfs_header: PFS header retireved from the hardware. * @vsec_offset: Starting MMIO address for this feature in bytes. Essentially * this offset = "Address" from VSEC header + PFS Capability * offset for this feature entry. * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device * * Represents TPMI instance information for one TPMI ID. */ struct intel_tpmi_pm_feature { … }; /** * struct intel_tpmi_info - TPMI information for all IDs in an instance * @tpmi_features: Pointer to a list of TPMI feature instances * @vsec_dev: Pointer to intel_vsec_device structure for this TPMI device * @feature_count: Number of TPMI of TPMI instances pointed by tpmi_features * @pfs_start: Start of PFS offset for the TPMI instances in this device * @plat_info: Stores platform info which can be used by the client drivers * @tpmi_control_mem: Memory mapped IO for getting control information * @dbgfs_dir: debugfs entry pointer * * Stores the information for all TPMI devices enumerated from a single PCI device. */ struct intel_tpmi_info { … }; /** * struct tpmi_info_header - CPU package ID to PCI device mapping information * @fn: PCI function number * @dev: PCI device number * @bus: PCI bus number * @pkg: CPU Package id * @segment: PCI segment id * @partition: Package Partition id * @cdie_mask: Bitmap of compute dies in the current partition * @reserved: Reserved for future use * @lock: When set to 1 the register is locked and becomes read-only * until next reset. Not for use by the OS driver. * * The structure to read hardware provided mapping information. */ struct tpmi_info_header { … } __packed; /** * struct tpmi_feature_state - Structure to read hardware state of a feature * @enabled: Enable state of a feature, 1: enabled, 0: disabled * @reserved_1: Reserved for future use * @write_blocked: Writes are blocked means all write operations are ignored * @read_blocked: Reads are blocked means will read 0xFFs * @pcs_select: Interface used by out of band software, not used in OS * @reserved_2: Reserved for future use * @id: TPMI ID of the feature * @reserved_3: Reserved for future use * @locked: When set to 1, OS can't change this register. * * The structure is used to read hardware state of a TPMI feature. This * information is used for debug and restricting operations for this feature. */ struct tpmi_feature_state { … } __packed; /* * The size from hardware is in u32 units. This size is from a trusted hardware, * but better to verify for pre silicon platforms. Set size to 0, when invalid. */ #define TPMI_GET_SINGLE_ENTRY_SIZE(pfs) … /* Used during auxbus device creation */ static DEFINE_IDA(intel_vsec_tpmi_ida); struct intel_tpmi_plat_info *tpmi_get_platform_data(struct auxiliary_device *auxdev) { … } EXPORT_SYMBOL_NS_GPL(…); int tpmi_get_resource_count(struct auxiliary_device *auxdev) { … } EXPORT_SYMBOL_NS_GPL(…); struct resource *tpmi_get_resource_at_index(struct auxiliary_device *auxdev, int index) { … } EXPORT_SYMBOL_NS_GPL(…); /* TPMI Control Interface */ #define TPMI_CONTROL_STATUS_OFFSET … #define TPMI_COMMAND_OFFSET … #define TMPI_CONTROL_DATA_VAL_OFFSET … /* * Spec is calling for max 1 seconds to get ownership at the worst * case. Read at 10 ms timeouts and repeat up to 1 second. */ #define TPMI_CONTROL_TIMEOUT_US … #define TPMI_CONTROL_TIMEOUT_MAX_US … #define TPMI_RB_TIMEOUT_US … #define TPMI_RB_TIMEOUT_MAX_US … /* TPMI Control status register defines */ #define TPMI_CONTROL_STATUS_RB … #define TPMI_CONTROL_STATUS_OWNER … #define TPMI_OWNER_NONE … #define TPMI_OWNER_IN_BAND … #define TPMI_CONTROL_STATUS_CPL … #define TPMI_CONTROL_STATUS_RESULT … #define TPMI_CONTROL_STATUS_LEN … #define TPMI_CMD_PKT_LEN … #define TPMI_CMD_STATUS_SUCCESS … /* TPMI command data registers */ #define TMPI_CONTROL_DATA_CMD … #define TPMI_CONTROL_DATA_VAL_FEATURE … /* Command to send via control interface */ #define TPMI_CONTROL_GET_STATE_CMD … #define TPMI_CONTROL_CMD_MASK … #define TPMI_CMD_LEN_MASK … /* Mutex to complete get feature status without interruption */ static DEFINE_MUTEX(tpmi_dev_lock); static int tpmi_wait_for_owner(struct intel_tpmi_info *tpmi_info, u8 owner) { … } static int tpmi_read_feature_status(struct intel_tpmi_info *tpmi_info, int feature_id, struct tpmi_feature_state *feature_state) { … } int tpmi_get_feature_status(struct auxiliary_device *auxdev, int feature_id, bool *read_blocked, bool *write_blocked) { … } EXPORT_SYMBOL_NS_GPL(…); struct dentry *tpmi_get_debugfs_dir(struct auxiliary_device *auxdev) { … } EXPORT_SYMBOL_NS_GPL(…); static int tpmi_pfs_dbg_show(struct seq_file *s, void *unused) { … } DEFINE_SHOW_ATTRIBUTE(…); #define MEM_DUMP_COLUMN_COUNT … static int tpmi_mem_dump_show(struct seq_file *s, void *unused) { … } DEFINE_SHOW_ATTRIBUTE(…); static ssize_t mem_write(struct file *file, const char __user *userbuf, size_t len, loff_t *ppos) { … } static int mem_write_show(struct seq_file *s, void *unused) { … } static int mem_write_open(struct inode *inode, struct file *file) { … } static const struct file_operations mem_write_ops = …; #define tpmi_to_dev(info) … static void tpmi_dbgfs_register(struct intel_tpmi_info *tpmi_info) { … } static void tpmi_set_control_base(struct auxiliary_device *auxdev, struct intel_tpmi_info *tpmi_info, struct intel_tpmi_pm_feature *pfs) { … } static const char *intel_tpmi_name(enum intel_tpmi_id id) { … } /* String Length for tpmi-"feature_name(upto 8 bytes)" */ #define TPMI_FEATURE_NAME_LEN … static int tpmi_create_device(struct intel_tpmi_info *tpmi_info, struct intel_tpmi_pm_feature *pfs, u64 pfs_start) { … } static int tpmi_create_devices(struct intel_tpmi_info *tpmi_info) { … } #define TPMI_INFO_BUS_INFO_OFFSET … #define TPMI_INFO_MAJOR_VERSION … #define TPMI_INFO_MINOR_VERSION … static int tpmi_process_info(struct intel_tpmi_info *tpmi_info, struct intel_tpmi_pm_feature *pfs) { … } static int tpmi_fetch_pfs_header(struct intel_tpmi_pm_feature *pfs, u64 start, int size) { … } #define TPMI_CAP_OFFSET_UNIT … static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) { … } static int tpmi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) { … } static void tpmi_remove(struct auxiliary_device *auxdev) { … } static const struct auxiliary_device_id tpmi_id_table[] = …; MODULE_DEVICE_TABLE(auxiliary, tpmi_id_table); static struct auxiliary_driver tpmi_aux_driver = …; module_auxiliary_driver(…) …; MODULE_IMPORT_NS(…); MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;