Commit e598dd44 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'acpi-x86', 'acpi-fan', 'acpi-soc' and 'acpi-cppc'

Merge changes in the ACPI x86-specific code, ACPI fan driverm ACPI LPSS
(Intel SoC) driver and the ACPI CPPC library for 6.11-rc1:

 - Switch the ACPI x86 utility code and the ACPI LPSS driver to new
   Intel CPU model defines (Tony Luck).

 - Add hwmon interface support to the ACPI fan driver (Armin Wolf).

 - Add sysfs entry for guaranteed performance to the ACPI CPPC library
   and replace a ternary operator with umax() in it (Petr Tesařík,
   Prabhakar Pujeri).

* acpi-x86:
  ACPI: x86: Switch to new Intel CPU model defines

* acpi-fan:
  ACPI: fan: Add hwmon support

* acpi-soc:
  ACPI: LPSS: Switch to new Intel CPU model defines

* acpi-cppc:
  ACPI: CPPC: Replace ternary operator with umax()
  ACPI: CPPC: add sysfs entry for guaranteed performance
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -77,6 +77,7 @@ obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o
obj-$(CONFIG_ACPI_FAN)		+= fan.o
fan-objs			:= fan_core.o
fan-objs			+= fan_attr.o
fan-$(CONFIG_HWMON)		+= fan_hwmon.o

obj-$(CONFIG_ACPI_VIDEO)	+= video.o
obj-$(CONFIG_ACPI_TAD)		+= acpi_tad.o
+3 −1
Original line number Diff line number Diff line
@@ -160,6 +160,7 @@ show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, highest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_nonlinear_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, guaranteed_perf);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, lowest_freq);
show_cppc_data(cppc_get_perf_caps, cppc_perf_caps, nominal_freq);

@@ -196,6 +197,7 @@ static struct attribute *cppc_attrs[] = {
	&highest_perf.attr,
	&lowest_perf.attr,
	&lowest_nonlinear_perf.attr,
	&guaranteed_perf.attr,
	&nominal_perf.attr,
	&nominal_freq.attr,
	&lowest_freq.attr,
@@ -1837,7 +1839,7 @@ static void cppc_find_dmi_mhz(const struct dmi_header *dm, void *private)
	    dm->length >= DMI_ENTRY_PROCESSOR_MIN_LENGTH) {
		u16 val = (u16)get_unaligned((const u16 *)
				(dmi_data + DMI_PROCESSOR_MAX_SPEED));
		*mhz = val > *mhz ? val : *mhz;
		*mhz = umax(val, *mhz);
	}
}

+9 −0
Original line number Diff line number Diff line
@@ -10,6 +10,8 @@
#ifndef _ACPI_FAN_H_
#define _ACPI_FAN_H_

#include <linux/kconfig.h>

#define ACPI_FAN_DEVICE_IDS	\
	{"INT3404", }, /* Fan */ \
	{"INTC1044", }, /* Fan for Tiger Lake generation */ \
@@ -57,4 +59,11 @@ struct acpi_fan {
int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst);
int acpi_fan_create_attributes(struct acpi_device *device);
void acpi_fan_delete_attributes(struct acpi_device *device);

#if IS_REACHABLE(CONFIG_HWMON)
int devm_acpi_fan_create_hwmon(struct acpi_device *device);
#else
static inline int devm_acpi_fan_create_hwmon(struct acpi_device *device) { return 0; };
#endif

#endif
+4 −0
Original line number Diff line number Diff line
@@ -336,6 +336,10 @@ static int acpi_fan_probe(struct platform_device *pdev)
		if (result)
			return result;

		result = devm_acpi_fan_create_hwmon(device);
		if (result)
			return result;

		result = acpi_fan_create_attributes(device);
		if (result)
			return result;
+170 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * hwmon interface for the ACPI Fan driver.
 *
 * Copyright (C) 2024 Armin Wolf <W_Armin@gmx.de>
 */

#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/hwmon.h>
#include <linux/limits.h>
#include <linux/types.h>
#include <linux/units.h>

#include "fan.h"

/* Returned when the ACPI fan does not support speed reporting */
#define FAN_SPEED_UNAVAILABLE	U32_MAX
#define FAN_POWER_UNAVAILABLE	U32_MAX

static struct acpi_fan_fps *acpi_fan_get_current_fps(struct acpi_fan *fan, u64 control)
{
	unsigned int i;

	for (i = 0; i < fan->fps_count; i++) {
		if (fan->fps[i].control == control)
			return &fan->fps[i];
	}

	return NULL;
}

static umode_t acpi_fan_hwmon_is_visible(const void *drvdata, enum hwmon_sensor_types type,
					 u32 attr, int channel)
{
	const struct acpi_fan *fan = drvdata;
	unsigned int i;

	switch (type) {
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
			return 0444;
		case hwmon_fan_target:
			/*
			 * When in fine grain control mode, not every fan control value
			 * has an associated fan performance state.
			 */
			if (fan->fif.fine_grain_ctrl)
				return 0;

			return 0444;
		default:
			return 0;
		}
	case hwmon_power:
		switch (attr) {
		case hwmon_power_input:
			/*
			 * When in fine grain control mode, not every fan control value
			 * has an associated fan performance state.
			 */
			if (fan->fif.fine_grain_ctrl)
				return 0;

			/*
			 * When all fan performance states contain no valid power data,
			 * when the associated attribute should not be created.
			 */
			for (i = 0; i < fan->fps_count; i++) {
				if (fan->fps[i].power != FAN_POWER_UNAVAILABLE)
					return 0444;
			}

			return 0;
		default:
			return 0;
		}
	default:
		return 0;
	}
}

static int acpi_fan_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
			       int channel, long *val)
{
	struct acpi_device *adev = to_acpi_device(dev->parent);
	struct acpi_fan *fan = dev_get_drvdata(dev);
	struct acpi_fan_fps *fps;
	struct acpi_fan_fst fst;
	int ret;

	ret = acpi_fan_get_fst(adev, &fst);
	if (ret < 0)
		return ret;

	switch (type) {
	case hwmon_fan:
		switch (attr) {
		case hwmon_fan_input:
			if (fst.speed == FAN_SPEED_UNAVAILABLE)
				return -ENODEV;

			if (fst.speed > LONG_MAX)
				return -EOVERFLOW;

			*val = fst.speed;
			return 0;
		case hwmon_fan_target:
			fps = acpi_fan_get_current_fps(fan, fst.control);
			if (!fps)
				return -EIO;

			if (fps->speed > LONG_MAX)
				return -EOVERFLOW;

			*val = fps->speed;
			return 0;
		default:
			return -EOPNOTSUPP;
		}
	case hwmon_power:
		switch (attr) {
		case hwmon_power_input:
			fps = acpi_fan_get_current_fps(fan, fst.control);
			if (!fps)
				return -EIO;

			if (fps->power == FAN_POWER_UNAVAILABLE)
				return -ENODEV;

			if (fps->power > LONG_MAX / MICROWATT_PER_MILLIWATT)
				return -EOVERFLOW;

			*val = fps->power * MICROWATT_PER_MILLIWATT;
			return 0;
		default:
			return -EOPNOTSUPP;
		}
	default:
		return -EOPNOTSUPP;
	}
}

static const struct hwmon_ops acpi_fan_hwmon_ops = {
	.is_visible = acpi_fan_hwmon_is_visible,
	.read = acpi_fan_hwmon_read,
};

static const struct hwmon_channel_info * const acpi_fan_hwmon_info[] = {
	HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET),
	HWMON_CHANNEL_INFO(power, HWMON_P_INPUT),
	NULL
};

static const struct hwmon_chip_info acpi_fan_hwmon_chip_info = {
	.ops = &acpi_fan_hwmon_ops,
	.info = acpi_fan_hwmon_info,
};

int devm_acpi_fan_create_hwmon(struct acpi_device *device)
{
	struct acpi_fan *fan = acpi_driver_data(device);
	struct device *hdev;

	hdev = devm_hwmon_device_register_with_info(&device->dev, "acpi_fan", fan,
						    &acpi_fan_hwmon_chip_info, NULL);
	return PTR_ERR_OR_ZERO(hdev);
}
Loading