15772717eSNeil Armstrong // SPDX-License-Identifier: GPL-2.0-or-later 25772717eSNeil Armstrong /* 35772717eSNeil Armstrong * Khadas MCU Controlled FAN driver 45772717eSNeil Armstrong * 55772717eSNeil Armstrong * Copyright (C) 2020 BayLibre SAS 65772717eSNeil Armstrong * Author(s): Neil Armstrong <narmstrong@baylibre.com> 75772717eSNeil Armstrong */ 85772717eSNeil Armstrong 95772717eSNeil Armstrong #include <linux/module.h> 105772717eSNeil Armstrong #include <linux/of.h> 115772717eSNeil Armstrong #include <linux/platform_device.h> 125772717eSNeil Armstrong #include <linux/mfd/khadas-mcu.h> 135772717eSNeil Armstrong #include <linux/regmap.h> 145772717eSNeil Armstrong #include <linux/sysfs.h> 155772717eSNeil Armstrong #include <linux/thermal.h> 165772717eSNeil Armstrong 175772717eSNeil Armstrong #define MAX_LEVEL 3 185772717eSNeil Armstrong 195772717eSNeil Armstrong struct khadas_mcu_fan_ctx { 205772717eSNeil Armstrong struct khadas_mcu *mcu; 215772717eSNeil Armstrong unsigned int level; 225772717eSNeil Armstrong struct thermal_cooling_device *cdev; 235772717eSNeil Armstrong }; 245772717eSNeil Armstrong 255772717eSNeil Armstrong static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx, 265772717eSNeil Armstrong unsigned int level) 275772717eSNeil Armstrong { 285772717eSNeil Armstrong int ret; 295772717eSNeil Armstrong 305772717eSNeil Armstrong ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG, 315772717eSNeil Armstrong level); 325772717eSNeil Armstrong if (ret) 335772717eSNeil Armstrong return ret; 345772717eSNeil Armstrong 355772717eSNeil Armstrong ctx->level = level; 365772717eSNeil Armstrong 375772717eSNeil Armstrong return 0; 385772717eSNeil Armstrong } 395772717eSNeil Armstrong 405772717eSNeil Armstrong static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, 415772717eSNeil Armstrong unsigned long *state) 425772717eSNeil Armstrong { 435772717eSNeil Armstrong *state = MAX_LEVEL; 445772717eSNeil Armstrong 455772717eSNeil Armstrong return 0; 465772717eSNeil Armstrong } 475772717eSNeil Armstrong 485772717eSNeil Armstrong static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev, 495772717eSNeil Armstrong unsigned long *state) 505772717eSNeil Armstrong { 515772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx = cdev->devdata; 525772717eSNeil Armstrong 535772717eSNeil Armstrong *state = ctx->level; 545772717eSNeil Armstrong 555772717eSNeil Armstrong return 0; 565772717eSNeil Armstrong } 575772717eSNeil Armstrong 585772717eSNeil Armstrong static int 595772717eSNeil Armstrong khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, 605772717eSNeil Armstrong unsigned long state) 615772717eSNeil Armstrong { 625772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx = cdev->devdata; 635772717eSNeil Armstrong 645772717eSNeil Armstrong if (state > MAX_LEVEL) 655772717eSNeil Armstrong return -EINVAL; 665772717eSNeil Armstrong 675772717eSNeil Armstrong if (state == ctx->level) 685772717eSNeil Armstrong return 0; 695772717eSNeil Armstrong 705772717eSNeil Armstrong return khadas_mcu_fan_set_level(ctx, state); 715772717eSNeil Armstrong } 725772717eSNeil Armstrong 735772717eSNeil Armstrong static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { 745772717eSNeil Armstrong .get_max_state = khadas_mcu_fan_get_max_state, 755772717eSNeil Armstrong .get_cur_state = khadas_mcu_fan_get_cur_state, 765772717eSNeil Armstrong .set_cur_state = khadas_mcu_fan_set_cur_state, 775772717eSNeil Armstrong }; 785772717eSNeil Armstrong 795772717eSNeil Armstrong static int khadas_mcu_fan_probe(struct platform_device *pdev) 805772717eSNeil Armstrong { 815772717eSNeil Armstrong struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent); 825772717eSNeil Armstrong struct thermal_cooling_device *cdev; 835772717eSNeil Armstrong struct device *dev = &pdev->dev; 845772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx; 855772717eSNeil Armstrong int ret; 865772717eSNeil Armstrong 875772717eSNeil Armstrong ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 885772717eSNeil Armstrong if (!ctx) 895772717eSNeil Armstrong return -ENOMEM; 905772717eSNeil Armstrong ctx->mcu = mcu; 915772717eSNeil Armstrong platform_set_drvdata(pdev, ctx); 925772717eSNeil Armstrong 935772717eSNeil Armstrong cdev = devm_thermal_of_cooling_device_register(dev->parent, 945772717eSNeil Armstrong dev->parent->of_node, "khadas-mcu-fan", ctx, 955772717eSNeil Armstrong &khadas_mcu_fan_cooling_ops); 965772717eSNeil Armstrong if (IS_ERR(cdev)) { 975772717eSNeil Armstrong ret = PTR_ERR(cdev); 985772717eSNeil Armstrong dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n", 995772717eSNeil Armstrong ret); 1005772717eSNeil Armstrong return ret; 1015772717eSNeil Armstrong } 1025772717eSNeil Armstrong ctx->cdev = cdev; 1035772717eSNeil Armstrong thermal_cdev_update(cdev); 1045772717eSNeil Armstrong 1055772717eSNeil Armstrong return 0; 1065772717eSNeil Armstrong } 1075772717eSNeil Armstrong 1085772717eSNeil Armstrong static void khadas_mcu_fan_shutdown(struct platform_device *pdev) 1095772717eSNeil Armstrong { 1105772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev); 1115772717eSNeil Armstrong 1125772717eSNeil Armstrong khadas_mcu_fan_set_level(ctx, 0); 1135772717eSNeil Armstrong } 1145772717eSNeil Armstrong 1155772717eSNeil Armstrong #ifdef CONFIG_PM_SLEEP 1165772717eSNeil Armstrong static int khadas_mcu_fan_suspend(struct device *dev) 1175772717eSNeil Armstrong { 1185772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev); 1195772717eSNeil Armstrong unsigned int level_save = ctx->level; 1205772717eSNeil Armstrong int ret; 1215772717eSNeil Armstrong 1225772717eSNeil Armstrong ret = khadas_mcu_fan_set_level(ctx, 0); 1235772717eSNeil Armstrong if (ret) 1245772717eSNeil Armstrong return ret; 1255772717eSNeil Armstrong 1265772717eSNeil Armstrong ctx->level = level_save; 1275772717eSNeil Armstrong 1285772717eSNeil Armstrong return 0; 1295772717eSNeil Armstrong } 1305772717eSNeil Armstrong 1315772717eSNeil Armstrong static int khadas_mcu_fan_resume(struct device *dev) 1325772717eSNeil Armstrong { 1335772717eSNeil Armstrong struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev); 1345772717eSNeil Armstrong 1355772717eSNeil Armstrong return khadas_mcu_fan_set_level(ctx, ctx->level); 1365772717eSNeil Armstrong } 1375772717eSNeil Armstrong #endif 1385772717eSNeil Armstrong 1395772717eSNeil Armstrong static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend, 1405772717eSNeil Armstrong khadas_mcu_fan_resume); 1415772717eSNeil Armstrong 1425772717eSNeil Armstrong static const struct platform_device_id khadas_mcu_fan_id_table[] = { 1435772717eSNeil Armstrong { .name = "khadas-mcu-fan-ctrl", }, 1445772717eSNeil Armstrong {}, 1455772717eSNeil Armstrong }; 1465772717eSNeil Armstrong MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table); 1475772717eSNeil Armstrong 1485772717eSNeil Armstrong static struct platform_driver khadas_mcu_fan_driver = { 1495772717eSNeil Armstrong .probe = khadas_mcu_fan_probe, 1505772717eSNeil Armstrong .shutdown = khadas_mcu_fan_shutdown, 1515772717eSNeil Armstrong .driver = { 1525772717eSNeil Armstrong .name = "khadas-mcu-fan-ctrl", 1535772717eSNeil Armstrong .pm = &khadas_mcu_fan_pm, 1545772717eSNeil Armstrong }, 1555772717eSNeil Armstrong .id_table = khadas_mcu_fan_id_table, 1565772717eSNeil Armstrong }; 1575772717eSNeil Armstrong 1585772717eSNeil Armstrong module_platform_driver(khadas_mcu_fan_driver); 1595772717eSNeil Armstrong 1605772717eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>"); 1615772717eSNeil Armstrong MODULE_DESCRIPTION("Khadas MCU FAN driver"); 1625772717eSNeil Armstrong MODULE_LICENSE("GPL"); 163