Linux Audio

Check our new training course

Loading...
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Processor thermal device module for registering and processing
  4 * power floor. When the hardware reduces the power to the minimum
  5 * possible, the power floor is notified via an interrupt.
  6 *
  7 * Operation:
  8 * When user space enables power floor reporting:
  9 * - Use mailbox to:
 10 *	Enable processor thermal device interrupt
 11 *
 12 * - Current status of power floor is read from offset 0x5B18
 13 *   bit 39.
 14 *
 15 * Two interface functions are provided to call when there is a
 16 * thermal device interrupt:
 17 * - proc_thermal_power_floor_intr():
 18 *	Check if the interrupt is for change in power floor.
 19 *	Called from interrupt context.
 20 *
 21 * - proc_thermal_power_floor_intr_callback():
 22 *	Callback for interrupt processing in thread context. This involves
 23 *	sending notification to user space that there is a change in the
 24 *	power floor status.
 25 *
 26 * Copyright (c) 2023, Intel Corporation.
 27 */
 28
 29#include <linux/pci.h>
 30#include "processor_thermal_device.h"
 31
 32#define SOC_POWER_FLOOR_STATUS		BIT(39)
 33#define SOC_POWER_FLOOR_SHIFT		39
 34
 35#define SOC_POWER_FLOOR_INT_ENABLE_BIT	31
 36#define SOC_POWER_FLOOR_INT_ACTIVE	BIT(3)
 37
 38int proc_thermal_read_power_floor_status(struct proc_thermal_device *proc_priv)
 39{
 40	u64 status = 0;
 41
 42	status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
 43	return (status & SOC_POWER_FLOOR_STATUS) >> SOC_POWER_FLOOR_SHIFT;
 44}
 45EXPORT_SYMBOL_NS_GPL(proc_thermal_read_power_floor_status, INT340X_THERMAL);
 46
 47static bool enable_state;
 48static DEFINE_MUTEX(pf_lock);
 49
 50int proc_thermal_power_floor_set_state(struct proc_thermal_device *proc_priv, bool enable)
 51{
 52	int ret = 0;
 53
 54	mutex_lock(&pf_lock);
 55	if (enable_state == enable)
 56		goto pf_unlock;
 57
 58	/*
 59	 * Time window parameter is not applicable to power floor interrupt configuration.
 60	 * Hence use -1 for time window.
 61	 */
 62	ret = processor_thermal_mbox_interrupt_config(to_pci_dev(proc_priv->dev), enable,
 63						      SOC_POWER_FLOOR_INT_ENABLE_BIT, -1);
 64	if (!ret)
 65		enable_state = enable;
 66
 67pf_unlock:
 68	mutex_unlock(&pf_lock);
 69
 70	return ret;
 71}
 72EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_set_state, INT340X_THERMAL);
 73
 74bool proc_thermal_power_floor_get_state(struct proc_thermal_device *proc_priv)
 75{
 76	return enable_state;
 77}
 78EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_get_state, INT340X_THERMAL);
 79
 80/**
 81 * proc_thermal_check_power_floor_intr() - Check power floor interrupt.
 82 * @proc_priv: Processor thermal device instance.
 83 *
 84 * Callback to check if the interrupt for power floor is active.
 85 *
 86 * Context: Called from interrupt context.
 87 *
 88 * Return: true if power floor is active, false when not active.
 89 */
 90bool proc_thermal_check_power_floor_intr(struct proc_thermal_device *proc_priv)
 91{
 92	u64 int_status;
 93
 94	int_status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
 95	return !!(int_status & SOC_POWER_FLOOR_INT_ACTIVE);
 96}
 97EXPORT_SYMBOL_NS_GPL(proc_thermal_check_power_floor_intr, INT340X_THERMAL);
 98
 99/**
100 * proc_thermal_power_floor_intr_callback() - Process power floor notification
101 * @pdev:	PCI device instance
102 * @proc_priv: Processor thermal device instance.
103 *
104 * Check if the power floor interrupt is active, if active send notification to
105 * user space for the attribute "power_limits", so that user can read the attribute
106 * and take action.
107 *
108 * Context: Called from interrupt thread context.
109 *
110 * Return: None.
111 */
112void proc_thermal_power_floor_intr_callback(struct pci_dev *pdev,
113					    struct proc_thermal_device *proc_priv)
114{
115	u64 status;
116
117	status = readq(proc_priv->mmio_base + SOC_WT_RES_INT_STATUS_OFFSET);
118	if (!(status & SOC_POWER_FLOOR_INT_ACTIVE))
119		return;
120
121	sysfs_notify(&pdev->dev.kobj, "power_limits", "power_floor_status");
122}
123EXPORT_SYMBOL_NS_GPL(proc_thermal_power_floor_intr_callback, INT340X_THERMAL);
124
125MODULE_IMPORT_NS(INT340X_THERMAL);
126MODULE_LICENSE("GPL");