linux/drivers/thermal/khadas_mcu_fan.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Khadas MCU Controlled FAN driver
   4 *
   5 * Copyright (C) 2020 BayLibre SAS
   6 * Author(s): Neil Armstrong <narmstrong@baylibre.com>
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/of.h>
  11#include <linux/platform_device.h>
  12#include <linux/mfd/khadas-mcu.h>
  13#include <linux/regmap.h>
  14#include <linux/sysfs.h>
  15#include <linux/thermal.h>
  16
  17#define MAX_LEVEL 3
  18
  19struct khadas_mcu_fan_ctx {
  20        struct khadas_mcu *mcu;
  21        unsigned int level;
  22        struct thermal_cooling_device *cdev;
  23};
  24
  25static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
  26                                    unsigned int level)
  27{
  28        int ret;
  29
  30        ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
  31                           level);
  32        if (ret)
  33                return ret;
  34
  35        ctx->level = level;
  36
  37        return 0;
  38}
  39
  40static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
  41                                        unsigned long *state)
  42{
  43        *state = MAX_LEVEL;
  44
  45        return 0;
  46}
  47
  48static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev,
  49                                        unsigned long *state)
  50{
  51        struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
  52
  53        *state = ctx->level;
  54
  55        return 0;
  56}
  57
  58static int
  59khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
  60                             unsigned long state)
  61{
  62        struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
  63
  64        if (state > MAX_LEVEL)
  65                return -EINVAL;
  66
  67        if (state == ctx->level)
  68                return 0;
  69
  70        return khadas_mcu_fan_set_level(ctx, state);
  71}
  72
  73static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
  74        .get_max_state = khadas_mcu_fan_get_max_state,
  75        .get_cur_state = khadas_mcu_fan_get_cur_state,
  76        .set_cur_state = khadas_mcu_fan_set_cur_state,
  77};
  78
  79static int khadas_mcu_fan_probe(struct platform_device *pdev)
  80{
  81        struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
  82        struct thermal_cooling_device *cdev;
  83        struct device *dev = &pdev->dev;
  84        struct khadas_mcu_fan_ctx *ctx;
  85        int ret;
  86
  87        ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
  88        if (!ctx)
  89                return -ENOMEM;
  90        ctx->mcu = mcu;
  91        platform_set_drvdata(pdev, ctx);
  92
  93        cdev = devm_thermal_of_cooling_device_register(dev->parent,
  94                        dev->parent->of_node, "khadas-mcu-fan", ctx,
  95                        &khadas_mcu_fan_cooling_ops);
  96        if (IS_ERR(cdev)) {
  97                ret = PTR_ERR(cdev);
  98                dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n",
  99                        ret);
 100                return ret;
 101        }
 102        ctx->cdev = cdev;
 103
 104        return 0;
 105}
 106
 107static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
 108{
 109        struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
 110
 111        khadas_mcu_fan_set_level(ctx, 0);
 112}
 113
 114#ifdef CONFIG_PM_SLEEP
 115static int khadas_mcu_fan_suspend(struct device *dev)
 116{
 117        struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
 118        unsigned int level_save = ctx->level;
 119        int ret;
 120
 121        ret = khadas_mcu_fan_set_level(ctx, 0);
 122        if (ret)
 123                return ret;
 124
 125        ctx->level = level_save;
 126
 127        return 0;
 128}
 129
 130static int khadas_mcu_fan_resume(struct device *dev)
 131{
 132        struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
 133
 134        return khadas_mcu_fan_set_level(ctx, ctx->level);
 135}
 136#endif
 137
 138static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
 139                         khadas_mcu_fan_resume);
 140
 141static const struct platform_device_id khadas_mcu_fan_id_table[] = {
 142        { .name = "khadas-mcu-fan-ctrl", },
 143        {},
 144};
 145MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
 146
 147static struct platform_driver khadas_mcu_fan_driver = {
 148        .probe          = khadas_mcu_fan_probe,
 149        .shutdown       = khadas_mcu_fan_shutdown,
 150        .driver = {
 151                .name           = "khadas-mcu-fan-ctrl",
 152                .pm             = &khadas_mcu_fan_pm,
 153        },
 154        .id_table       = khadas_mcu_fan_id_table,
 155};
 156
 157module_platform_driver(khadas_mcu_fan_driver);
 158
 159MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
 160MODULE_DESCRIPTION("Khadas MCU FAN driver");
 161MODULE_LICENSE("GPL");
 162