thermal: Add support for the MCU controlled FAN on Khadas boards
authorNeil Armstrong <narmstrong@baylibre.com>
Wed, 24 Jun 2020 16:15:27 +0000 (18:15 +0200)
committerLee Jones <lee.jones@linaro.org>
Thu, 25 Jun 2020 06:44:20 +0000 (07:44 +0100)
The new Khadas VIM2 and VIM3 boards controls the cooling fan via the
on-board microcontroller.

This implements the FAN control as thermal devices and as cell of the Khadas
MCU MFD driver.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Lee Jones <lee.jones@linaro.org>
drivers/thermal/Kconfig
drivers/thermal/Makefile
drivers/thermal/khadas_mcu_fan.c [new file with mode: 0644]

index 3eb2348e52427495bb36a0fc1b6bb34453a319a7..0125561488c9e03e6119b04cfd6d13552b5724f1 100644 (file)
@@ -500,4 +500,15 @@ config SPRD_THERMAL
        help
          Support for the Spreadtrum thermal sensor driver in the Linux thermal
          framework.
+
+config KHADAS_MCU_FAN_THERMAL
+       tristate "Khadas MCU controller FAN cooling support"
+       depends on OF || COMPILE_TEST
+       depends on MFD_KHADAS_MCU
+       select MFD_CORE
+       select REGMAP
+       help
+         If you say yes here you get support for the FAN controlled
+         by the Microcontroller found on the Khadas VIM boards.
+
 endif
index 0c8b84a09b9aa7555b811ebe0da5a9b898ced8de..4b6aabaa7e313833e31b642d013aadbc056608e5 100644 (file)
@@ -61,3 +61,4 @@ obj-$(CONFIG_ZX2967_THERMAL)  += zx2967_thermal.o
 obj-$(CONFIG_UNIPHIER_THERMAL) += uniphier_thermal.o
 obj-$(CONFIG_AMLOGIC_THERMAL)     += amlogic_thermal.o
 obj-$(CONFIG_SPRD_THERMAL)     += sprd_thermal.o
+obj-$(CONFIG_KHADAS_MCU_FAN_THERMAL)   += khadas_mcu_fan.o
diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c
new file mode 100644 (file)
index 0000000..9eadd2d
--- /dev/null
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Khadas MCU Controlled FAN driver
+ *
+ * Copyright (C) 2020 BayLibre SAS
+ * Author(s): Neil Armstrong <narmstrong@baylibre.com>
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/khadas-mcu.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+
+#define MAX_LEVEL 3
+
+struct khadas_mcu_fan_ctx {
+       struct khadas_mcu *mcu;
+       unsigned int level;
+       struct thermal_cooling_device *cdev;
+};
+
+static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
+                                   unsigned int level)
+{
+       int ret;
+
+       ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
+                          level);
+       if (ret)
+               return ret;
+
+       ctx->level = level;
+
+       return 0;
+}
+
+static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
+                                       unsigned long *state)
+{
+       *state = MAX_LEVEL;
+
+       return 0;
+}
+
+static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev,
+                                       unsigned long *state)
+{
+       struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
+
+       *state = ctx->level;
+
+       return 0;
+}
+
+static int
+khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
+                            unsigned long state)
+{
+       struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
+
+       if (state > MAX_LEVEL)
+               return -EINVAL;
+
+       if (state == ctx->level)
+               return 0;
+
+       return khadas_mcu_fan_set_level(ctx, state);
+}
+
+static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
+       .get_max_state = khadas_mcu_fan_get_max_state,
+       .get_cur_state = khadas_mcu_fan_get_cur_state,
+       .set_cur_state = khadas_mcu_fan_set_cur_state,
+};
+
+static int khadas_mcu_fan_probe(struct platform_device *pdev)
+{
+       struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
+       struct thermal_cooling_device *cdev;
+       struct device *dev = &pdev->dev;
+       struct khadas_mcu_fan_ctx *ctx;
+       int ret;
+
+       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+       ctx->mcu = mcu;
+       platform_set_drvdata(pdev, ctx);
+
+       cdev = devm_thermal_of_cooling_device_register(dev->parent,
+                       dev->parent->of_node, "khadas-mcu-fan", ctx,
+                       &khadas_mcu_fan_cooling_ops);
+       if (IS_ERR(cdev)) {
+               ret = PTR_ERR(cdev);
+               dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n",
+                       ret);
+               return ret;
+       }
+       ctx->cdev = cdev;
+       thermal_cdev_update(cdev);
+
+       return 0;
+}
+
+static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
+{
+       struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
+
+       khadas_mcu_fan_set_level(ctx, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int khadas_mcu_fan_suspend(struct device *dev)
+{
+       struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
+       unsigned int level_save = ctx->level;
+       int ret;
+
+       ret = khadas_mcu_fan_set_level(ctx, 0);
+       if (ret)
+               return ret;
+
+       ctx->level = level_save;
+
+       return 0;
+}
+
+static int khadas_mcu_fan_resume(struct device *dev)
+{
+       struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
+
+       return khadas_mcu_fan_set_level(ctx, ctx->level);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
+                        khadas_mcu_fan_resume);
+
+static const struct platform_device_id khadas_mcu_fan_id_table[] = {
+       { .name = "khadas-mcu-fan-ctrl", },
+       {},
+};
+MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
+
+static struct platform_driver khadas_mcu_fan_driver = {
+       .probe          = khadas_mcu_fan_probe,
+       .shutdown       = khadas_mcu_fan_shutdown,
+       .driver = {
+               .name           = "khadas-mcu-fan-ctrl",
+               .pm             = &khadas_mcu_fan_pm,
+       },
+       .id_table       = khadas_mcu_fan_id_table,
+};
+
+module_platform_driver(khadas_mcu_fan_driver);
+
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
+MODULE_DESCRIPTION("Khadas MCU FAN driver");
+MODULE_LICENSE("GPL");