summary refs log tree commit diff
path: root/drivers/mfd
diff options
context:
space:
mode:
authorAndrey Smirnov <andrew.smirnov@gmail.com>2022-02-19 16:08:36 -0800
committerCristian Ciocaltea <cristian.ciocaltea@collabora.com>2022-12-12 17:57:14 +0200
commit6c44fb6bf2f595033b5a7fad398d9af19d03bf40 (patch)
treef82135133da074cdef35bca98aedbf93f45666f7 /drivers/mfd
parent830b3c68c1fb1e9176028d02ef86f3cf76aa2476 (diff)
downloadlinux-6c44fb6bf2f595033b5a7fad398d9af19d03bf40.tar.gz
mfd: Add MFD core driver for Steam Deck
Add MFD core driver for Steam Deck. Doesn't really do much so far
besides instantiating a number of MFD cells that implement all the
interesting functionality.

(cherry picked from commit 5f534c2d6ebdefccb9c024eb0f013bc1c0c622d9)
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Diffstat (limited to 'drivers/mfd')
-rw-r--r--drivers/mfd/Kconfig11
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/steamdeck.c127
3 files changed, 140 insertions, 0 deletions
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 8b93856de432..af335d9150e9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2260,5 +2260,16 @@ config MFD_RSMU_SPI
 	  Additional drivers must be enabled in order to use the functionality
 	  of the device.
 
+config MFD_STEAMDECK
+	tristate "Valve Steam Deck"
+	select MFD_CORE
+	depends on ACPI
+	depends on X86_64 || COMPILE_TEST
+	help
+	  This driver registers various MFD cells that expose aspects
+	  of Steam Deck specific ACPI functionality.
+
+	  Say N here, unless you are running on Steam Deck hardware.
+
 endmenu
 endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 7ed3ef4a698c..d01254ef0106 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -280,3 +280,5 @@ rsmu-i2c-objs			:= rsmu_core.o rsmu_i2c.o
 rsmu-spi-objs			:= rsmu_core.o rsmu_spi.o
 obj-$(CONFIG_MFD_RSMU_I2C)	+= rsmu-i2c.o
 obj-$(CONFIG_MFD_RSMU_SPI)	+= rsmu-spi.o
+
+obj-$(CONFIG_MFD_STEAMDECK)	+= steamdeck.o
diff --git a/drivers/mfd/steamdeck.c b/drivers/mfd/steamdeck.c
new file mode 100644
index 000000000000..0e504b3c2796
--- /dev/null
+++ b/drivers/mfd/steamdeck.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Steam Deck EC MFD core driver
+ *
+ * Copyright (C) 2021-2022 Valve Corporation
+ *
+ */
+
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+
+#define STEAMDECK_STA_OK			\
+	(ACPI_STA_DEVICE_ENABLED |		\
+	 ACPI_STA_DEVICE_PRESENT |		\
+	 ACPI_STA_DEVICE_FUNCTIONING)
+
+struct steamdeck {
+	struct acpi_device *adev;
+	struct device *dev;
+};
+
+#define STEAMDECK_ATTR_RO(_name, _method)				\
+	static ssize_t _name##_show(struct device *dev,			\
+				    struct device_attribute *attr,	\
+				    char *buf)				\
+	{								\
+		struct steamdeck *sd = dev_get_drvdata(dev);		\
+		unsigned long long val;					\
+									\
+		if (ACPI_FAILURE(acpi_evaluate_integer(			\
+					 sd->adev->handle,		\
+					 _method, NULL, &val)))		\
+			return -EIO;					\
+									\
+		return sysfs_emit(buf, "%llu\n", val);			\
+	}								\
+	static DEVICE_ATTR_RO(_name)
+
+STEAMDECK_ATTR_RO(firmware_version, "PDFW");
+STEAMDECK_ATTR_RO(board_id, "BOID");
+
+static struct attribute *steamdeck_attrs[] = {
+	&dev_attr_firmware_version.attr,
+	&dev_attr_board_id.attr,
+	NULL
+};
+
+ATTRIBUTE_GROUPS(steamdeck);
+
+static const struct mfd_cell steamdeck_cells[] = {
+	{ .name = "steamdeck-hwmon"  },
+	{ .name = "steamdeck-leds"   },
+	{ .name = "steamdeck-extcon" },
+};
+
+static void steamdeck_remove_sysfs_groups(void *data)
+{
+	struct steamdeck *sd = data;
+
+	sysfs_remove_groups(&sd->dev->kobj, steamdeck_groups);
+}
+
+static int steamdeck_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	unsigned long long sta;
+	struct steamdeck *sd;
+	acpi_status status;
+	int ret;
+
+	sd = devm_kzalloc(dev, sizeof(*sd), GFP_KERNEL);
+	if (!sd)
+		return -ENOMEM;
+	sd->adev = ACPI_COMPANION(dev);
+	sd->dev = dev;
+	platform_set_drvdata(pdev, sd);
+
+	status = acpi_evaluate_integer(sd->adev->handle, "_STA",
+				       NULL, &sta);
+	if (ACPI_FAILURE(status)) {
+		dev_err(dev, "Status check failed (0x%x)\n", status);
+		return -EINVAL;
+	}
+
+	if ((sta & STEAMDECK_STA_OK) != STEAMDECK_STA_OK) {
+		dev_err(dev, "Device is not ready\n");
+		return -EINVAL;
+	}
+
+	ret = sysfs_create_groups(&dev->kobj, steamdeck_groups);
+	if (ret) {
+		dev_err(dev, "Failed to create sysfs group\n");
+		return ret;
+	}
+
+	ret = devm_add_action_or_reset(dev, steamdeck_remove_sysfs_groups,
+				       sd);
+	if (ret) {
+		dev_err(dev, "Failed to register devres action\n");
+		return ret;
+	}
+
+	return devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE,
+				    steamdeck_cells, ARRAY_SIZE(steamdeck_cells),
+				    NULL, 0, NULL);
+}
+
+static const struct acpi_device_id steamdeck_device_ids[] = {
+	{ "VLV0100", 0 },
+	{ "", 0 },
+};
+MODULE_DEVICE_TABLE(acpi, steamdeck_device_ids);
+
+static struct platform_driver steamdeck_driver = {
+	.probe = steamdeck_probe,
+	.driver = {
+		.name = "steamdeck",
+		.acpi_match_table = steamdeck_device_ids,
+	},
+};
+module_platform_driver(steamdeck_driver);
+
+MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
+MODULE_DESCRIPTION("Steam Deck EC MFD core driver");
+MODULE_LICENSE("GPL");