478a4261ca
[ci:skip-build]: already built successfully in CI
283 lines
7.1 KiB
Diff
283 lines
7.1 KiB
Diff
From e2b6dc5c513c9407def9bcbcdb9d4d7435225c77 Mon Sep 17 00:00:00 2001
|
|
From: Andrey Smirnov <andrew.smirnov@gmail.com>
|
|
Date: Sat, 19 Feb 2022 16:09:45 -0800
|
|
Subject: [PATCH 05/21] hwmon: Add driver for Steam Deck's EC sensors
|
|
|
|
Add driver for sensors exposed by EC firmware on Steam Deck hardware.
|
|
|
|
(cherry picked from commit 6917aac77bee6185ae3920b936cdbe7876118c0b)
|
|
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
|
|
---
|
|
drivers/hwmon/Kconfig | 11 ++
|
|
drivers/hwmon/Makefile | 1 +
|
|
drivers/hwmon/steamdeck-hwmon.c | 224 ++++++++++++++++++++++++++++++++
|
|
3 files changed, 236 insertions(+)
|
|
create mode 100644 drivers/hwmon/steamdeck-hwmon.c
|
|
|
|
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
|
|
index 83945397b6eb..6219ebfef801 100644
|
|
--- a/drivers/hwmon/Kconfig
|
|
+++ b/drivers/hwmon/Kconfig
|
|
@@ -2036,6 +2036,17 @@ config SENSORS_SCH5636
|
|
This driver can also be built as a module. If so, the module
|
|
will be called sch5636.
|
|
|
|
+config SENSORS_STEAMDECK
|
|
+ tristate "Steam Deck EC sensors"
|
|
+ depends on MFD_STEAMDECK
|
|
+ help
|
|
+ If you say yes here you get support for the hardware
|
|
+ monitoring features exposed by EC firmware on Steam Deck
|
|
+ devices
|
|
+
|
|
+ This driver can also be built as a module. If so, the module
|
|
+ will be called steamdeck-hwmon.
|
|
+
|
|
config SENSORS_STTS751
|
|
tristate "ST Microelectronics STTS751"
|
|
depends on I2C
|
|
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
|
|
index 5c31808f6378..34a4851866cb 100644
|
|
--- a/drivers/hwmon/Makefile
|
|
+++ b/drivers/hwmon/Makefile
|
|
@@ -206,6 +206,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
|
|
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
|
|
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
|
|
obj-$(CONFIG_SENSORS_SPARX5) += sparx5-temp.o
|
|
+obj-$(CONFIG_SENSORS_STEAMDECK) += steamdeck-hwmon.o
|
|
obj-$(CONFIG_SENSORS_STTS751) += stts751.o
|
|
obj-$(CONFIG_SENSORS_SURFACE_FAN)+= surface_fan.o
|
|
obj-$(CONFIG_SENSORS_SY7636A) += sy7636a-hwmon.o
|
|
diff --git a/drivers/hwmon/steamdeck-hwmon.c b/drivers/hwmon/steamdeck-hwmon.c
|
|
new file mode 100644
|
|
index 000000000000..fab9e9460bd4
|
|
--- /dev/null
|
|
+++ b/drivers/hwmon/steamdeck-hwmon.c
|
|
@@ -0,0 +1,224 @@
|
|
+// SPDX-License-Identifier: GPL-2.0+
|
|
+/*
|
|
+ * Steam Deck EC sensors driver
|
|
+ *
|
|
+ * Copyright (C) 2021-2022 Valve Corporation
|
|
+ */
|
|
+
|
|
+#include <linux/acpi.h>
|
|
+#include <linux/hwmon.h>
|
|
+#include <linux/platform_device.h>
|
|
+
|
|
+#define STEAMDECK_HWMON_NAME "steamdeck-hwmon"
|
|
+
|
|
+struct steamdeck_hwmon {
|
|
+ struct acpi_device *adev;
|
|
+};
|
|
+
|
|
+static long
|
|
+steamdeck_hwmon_get(struct steamdeck_hwmon *sd, const char *method)
|
|
+{
|
|
+ unsigned long long val;
|
|
+ if (ACPI_FAILURE(acpi_evaluate_integer(sd->adev->handle,
|
|
+ (char *)method, NULL, &val)))
|
|
+ return -EIO;
|
|
+
|
|
+ return val;
|
|
+}
|
|
+
|
|
+static int
|
|
+steamdeck_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
|
|
+ u32 attr, int channel, long *out)
|
|
+{
|
|
+ struct steamdeck_hwmon *sd = dev_get_drvdata(dev);
|
|
+
|
|
+ switch (type) {
|
|
+ case hwmon_curr:
|
|
+ if (attr != hwmon_curr_input)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ *out = steamdeck_hwmon_get(sd, "PDAM");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ break;
|
|
+ case hwmon_in:
|
|
+ if (attr != hwmon_in_input)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ *out = steamdeck_hwmon_get(sd, "PDVL");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ break;
|
|
+ case hwmon_temp:
|
|
+ if (attr != hwmon_temp_input)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ *out = steamdeck_hwmon_get(sd, "BATT");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ /*
|
|
+ * Assuming BATT returns deg C we need to mutiply it
|
|
+ * by 1000 to convert to mC
|
|
+ */
|
|
+ *out *= 1000;
|
|
+ break;
|
|
+ case hwmon_fan:
|
|
+ switch (attr) {
|
|
+ case hwmon_fan_input:
|
|
+ *out = steamdeck_hwmon_get(sd, "FANR");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ break;
|
|
+ case hwmon_fan_target:
|
|
+ *out = steamdeck_hwmon_get(sd, "FSSR");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ break;
|
|
+ case hwmon_fan_fault:
|
|
+ *out = steamdeck_hwmon_get(sd, "FANC");
|
|
+ if (*out < 0)
|
|
+ return *out;
|
|
+ /*
|
|
+ * FANC (Fan check):
|
|
+ * 0: Abnormal
|
|
+ * 1: Normal
|
|
+ */
|
|
+ *out = !*out;
|
|
+ break;
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+steamdeck_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
|
|
+ u32 attr, int channel, const char **str)
|
|
+{
|
|
+ switch (type) {
|
|
+ /*
|
|
+ * These two aren't, strictly speaking, measured. EC
|
|
+ * firmware just reports what PD negotiation resulted
|
|
+ * in.
|
|
+ */
|
|
+ case hwmon_curr:
|
|
+ *str = "PD Contract Current";
|
|
+ break;
|
|
+ case hwmon_in:
|
|
+ *str = "PD Contract Voltage";
|
|
+ break;
|
|
+ case hwmon_temp:
|
|
+ *str = "Battery Temp";
|
|
+ break;
|
|
+ case hwmon_fan:
|
|
+ *str = "System Fan";
|
|
+ break;
|
|
+ default:
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+steamdeck_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
|
|
+ u32 attr, int channel, long val)
|
|
+{
|
|
+ struct steamdeck_hwmon *sd = dev_get_drvdata(dev);
|
|
+
|
|
+ if (type != hwmon_fan ||
|
|
+ attr != hwmon_fan_target)
|
|
+ return -EOPNOTSUPP;
|
|
+
|
|
+ val = clamp_val(val, 0, 7300);
|
|
+
|
|
+ if (ACPI_FAILURE(acpi_execute_simple_method(sd->adev->handle,
|
|
+ "FANS", val)))
|
|
+ return -EIO;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static umode_t
|
|
+steamdeck_hwmon_is_visible(const void *data, enum hwmon_sensor_types type,
|
|
+ u32 attr, int channel)
|
|
+{
|
|
+ if (type == hwmon_fan &&
|
|
+ attr == hwmon_fan_target)
|
|
+ return 0644;
|
|
+
|
|
+ return 0444;
|
|
+}
|
|
+
|
|
+static const struct hwmon_channel_info *steamdeck_hwmon_info[] = {
|
|
+ HWMON_CHANNEL_INFO(in,
|
|
+ HWMON_I_INPUT | HWMON_I_LABEL),
|
|
+ HWMON_CHANNEL_INFO(curr,
|
|
+ HWMON_C_INPUT | HWMON_C_LABEL),
|
|
+ HWMON_CHANNEL_INFO(temp,
|
|
+ HWMON_T_INPUT | HWMON_T_LABEL),
|
|
+ HWMON_CHANNEL_INFO(fan,
|
|
+ HWMON_F_INPUT | HWMON_F_LABEL |
|
|
+ HWMON_F_TARGET | HWMON_F_FAULT),
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static const struct hwmon_ops steamdeck_hwmon_ops = {
|
|
+ .is_visible = steamdeck_hwmon_is_visible,
|
|
+ .read = steamdeck_hwmon_read,
|
|
+ .read_string = steamdeck_hwmon_read_string,
|
|
+ .write = steamdeck_hwmon_write,
|
|
+};
|
|
+
|
|
+static const struct hwmon_chip_info steamdeck_hwmon_chip_info = {
|
|
+ .ops = &steamdeck_hwmon_ops,
|
|
+ .info = steamdeck_hwmon_info,
|
|
+};
|
|
+
|
|
+static int steamdeck_hwmon_probe(struct platform_device *pdev)
|
|
+{
|
|
+ struct device *dev = &pdev->dev;
|
|
+ struct steamdeck_hwmon *sd;
|
|
+ struct device *hwmon;
|
|
+
|
|
+ sd = devm_kzalloc(dev, sizeof(*sd), GFP_KERNEL);
|
|
+ if (!sd)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ sd->adev = ACPI_COMPANION(dev->parent);
|
|
+ hwmon = devm_hwmon_device_register_with_info(dev,
|
|
+ "steamdeck_hwmon",
|
|
+ sd,
|
|
+ &steamdeck_hwmon_chip_info,
|
|
+ NULL);
|
|
+ if (IS_ERR(hwmon)) {
|
|
+ dev_err(dev, "Failed to register HWMON device");
|
|
+ return PTR_ERR(hwmon);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct platform_device_id steamdeck_hwmon_id_table[] = {
|
|
+ { .name = STEAMDECK_HWMON_NAME },
|
|
+ {}
|
|
+};
|
|
+MODULE_DEVICE_TABLE(platform, steamdeck_hwmon_id_table);
|
|
+
|
|
+static struct platform_driver steamdeck_hwmon_driver = {
|
|
+ .probe = steamdeck_hwmon_probe,
|
|
+ .driver = {
|
|
+ .name = STEAMDECK_HWMON_NAME,
|
|
+ },
|
|
+ .id_table = steamdeck_hwmon_id_table,
|
|
+};
|
|
+module_platform_driver(steamdeck_hwmon_driver);
|
|
+
|
|
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
|
|
+MODULE_DESCRIPTION("Steam Deck EC sensors driver");
|
|
+MODULE_LICENSE("GPL");
|
|
--
|
|
2.44.0
|
|
|