brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · e947d84 Raw
257 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * processor thermal device interface for reading workload type hints4 * from the user space. The hints are provided by the firmware.5 *6 * Operation:7 * When user space enables workload type prediction:8 * - Use mailbox to:9 *	Configure notification delay10 *	Enable processor thermal device interrupt11 *12 * - The predicted workload type can be read from MMIO:13 *	Offset 0x5B18 shows if there was an interrupt14 *	active for change in workload type and also15 *	predicted workload type.16 *17 * Two interface functions are provided to call when there is a18 * thermal device interrupt:19 * - proc_thermal_check_wt_intr():20 *     Check if the interrupt is for change in workload type. Called from21 *     interrupt context.22 *23 * - proc_thermal_wt_intr_callback():24 *     Callback for interrupt processing in thread context. This involves25 *	sending notification to user space that there is a change in the26 *     workload type.27 *28 * Copyright (c) 2023, Intel Corporation.29 */30 31#include <linux/bitfield.h>32#include <linux/pci.h>33#include "processor_thermal_device.h"34 35#define SOC_WT				GENMASK_ULL(47, 40)36 37#define SOC_WT_PREDICTION_INT_ENABLE_BIT	2338 39#define SOC_WT_PREDICTION_INT_ACTIVE	BIT(2)40 41/*42 * Closest possible to 1 Second is 1024 ms with programmed time delay43 * of 0x0A.44 */45static u8 notify_delay = 0x0A;46static u16 notify_delay_ms = 1024;47 48static DEFINE_MUTEX(wt_lock);49static u8 wt_enable;50 51/* Show current predicted workload type index */52static ssize_t workload_type_index_show(struct device *dev,53					struct device_attribute *attr,54					char *buf)55{56	struct proc_thermal_device *proc_priv;57	struct pci_dev *pdev = to_pci_dev(dev);58	u64 status = 0;59	int wt;60 61	mutex_lock(&wt_lock);62	if (!wt_enable) {63		mutex_unlock(&wt_lock);64		return -ENODATA;65	}66 67	proc_priv = pci_get_drvdata(pdev);68 69	status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);70 71	mutex_unlock(&wt_lock);72 73	wt = FIELD_GET(SOC_WT, status);74 75	return sysfs_emit(buf, "%d\n", wt);76}77 78static DEVICE_ATTR_RO(workload_type_index);79 80static ssize_t workload_hint_enable_show(struct device *dev,81					 struct device_attribute *attr,82					 char *buf)83{84	return sysfs_emit(buf, "%d\n", wt_enable);85}86 87static ssize_t workload_hint_enable_store(struct device *dev,88					  struct device_attribute *attr,89					  const char *buf, size_t size)90{91	struct pci_dev *pdev = to_pci_dev(dev);92	u8 mode;93	int ret;94 95	if (kstrtou8(buf, 10, &mode) || mode > 1)96		return -EINVAL;97 98	mutex_lock(&wt_lock);99 100	if (mode)101		ret = processor_thermal_mbox_interrupt_config(pdev, true,102							      SOC_WT_PREDICTION_INT_ENABLE_BIT,103							      notify_delay);104	else105		ret = processor_thermal_mbox_interrupt_config(pdev, false,106							      SOC_WT_PREDICTION_INT_ENABLE_BIT, 0);107 108	if (ret)109		goto ret_enable_store;110 111	ret = size;112	wt_enable = mode;113 114ret_enable_store:115	mutex_unlock(&wt_lock);116 117	return ret;118}119 120static DEVICE_ATTR_RW(workload_hint_enable);121 122static ssize_t notification_delay_ms_show(struct device *dev,123					  struct device_attribute *attr,124					  char *buf)125{126	return sysfs_emit(buf, "%u\n", notify_delay_ms);127}128 129static ssize_t notification_delay_ms_store(struct device *dev,130					   struct device_attribute *attr,131					   const char *buf, size_t size)132{133	struct pci_dev *pdev = to_pci_dev(dev);134	u16 new_tw;135	int ret;136	u8 tm;137 138	/*139	 * Time window register value:140	 * Formula: (1 + x/4) * power(2,y)141	 * x = 2 msbs, that is [30:29] y = 5 [28:24]142	 * in INTR_CONFIG register.143	 * The result will be in milli seconds.144	 * Here, just keep x = 0, and just change y.145	 * First round up the user value to power of 2 and146	 * then take log2, to get "y" value to program.147	 */148	ret = kstrtou16(buf, 10, &new_tw);149	if (ret)150		return ret;151 152	if (!new_tw)153		return -EINVAL;154 155	new_tw = roundup_pow_of_two(new_tw);156	tm = ilog2(new_tw);157	if (tm > 31)158		return -EINVAL;159 160	mutex_lock(&wt_lock);161 162	/* If the workload hint was already enabled, then update with the new delay */163	if (wt_enable)164		ret = processor_thermal_mbox_interrupt_config(pdev, true,165							      SOC_WT_PREDICTION_INT_ENABLE_BIT,166							      tm);167 168	if (!ret) {169		ret = size;170		notify_delay = tm;171		notify_delay_ms = new_tw;172	}173 174	mutex_unlock(&wt_lock);175 176	return ret;177}178 179static DEVICE_ATTR_RW(notification_delay_ms);180 181static struct attribute *workload_hint_attrs[] = {182	&dev_attr_workload_type_index.attr,183	&dev_attr_workload_hint_enable.attr,184	&dev_attr_notification_delay_ms.attr,185	NULL186};187 188static const struct attribute_group workload_hint_attribute_group = {189	.attrs = workload_hint_attrs,190	.name = "workload_hint"191};192 193/*194 * Callback to check if the interrupt for prediction is active.195 * Caution: Called from the interrupt context.196 */197bool proc_thermal_check_wt_intr(struct proc_thermal_device *proc_priv)198{199	u64 int_status;200 201	int_status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);202	if (int_status & SOC_WT_PREDICTION_INT_ACTIVE)203		return true;204 205	return false;206}207EXPORT_SYMBOL_NS_GPL(proc_thermal_check_wt_intr, INT340X_THERMAL);208 209/* Callback to notify user space */210void proc_thermal_wt_intr_callback(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)211{212	u64 status;213 214	status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);215	if (!(status & SOC_WT_PREDICTION_INT_ACTIVE))216		return;217 218	sysfs_notify(&pdev->dev.kobj, "workload_hint", "workload_type_index");219}220EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_intr_callback, INT340X_THERMAL);221 222static bool workload_hint_created;223 224int proc_thermal_wt_hint_add(struct pci_dev *pdev, struct proc_thermal_device *proc_priv)225{226	int ret;227 228	ret = sysfs_create_group(&pdev->dev.kobj, &workload_hint_attribute_group);229	if (ret)230		return ret;231 232	workload_hint_created = true;233 234	return 0;235}236EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_add, INT340X_THERMAL);237 238void proc_thermal_wt_hint_remove(struct pci_dev *pdev)239{240	mutex_lock(&wt_lock);241	if (wt_enable)242		processor_thermal_mbox_interrupt_config(pdev, false,243							SOC_WT_PREDICTION_INT_ENABLE_BIT,244							0);245	mutex_unlock(&wt_lock);246 247	if (workload_hint_created)248		sysfs_remove_group(&pdev->dev.kobj, &workload_hint_attribute_group);249 250	workload_hint_created = false;251}252EXPORT_SYMBOL_NS_GPL(proc_thermal_wt_hint_remove, INT340X_THERMAL);253 254MODULE_IMPORT_NS(INT340X_THERMAL);255MODULE_LICENSE("GPL");256MODULE_DESCRIPTION("Processor Thermal Work Load type hint Interface");257