From dad6f7897a93b4e1614664dbbcae0f61e57d1ac0 Mon Sep 17 00:00:00 2001 From: Ronald Claveau Date: Thu, 17 Sep 2026 09:59:03 +0200 Subject: [PATCH] thermal: khadas-mcu-fan: Add fan config from platform data Add regulator support Introduce platform name, control register and duty-cycle levels per variant. VIM4 uses a different register and level set (0-100) declared as a list (0, 30, 40, 55, 75, 100) compared to previous boards (0-3). MAX_LEVEL is kept and bumped from 3 to 5 to make the speed change more gradual with the newer MCUs. The cooling device's state is stored as an index into levels[], and khadas_mcu_fan_set_level() writes levels[state] to fan_reg, rather than writing the raw state value as before. Optionally acquire and enable a "fan" regulator supply at probe time and on resume, so boards that gate fan power through a regulator are handled. The regulator is released via a devm_add_action_or_reset() callback. Signed-off-by: Ronald Claveau Reviewed-by: Neil Armstrong Acked-by: Daniel Lezcano Link: https://patch.msgid.link/20260917-add-mcu-fan-khadas-vim4-v12-4-497cd543a148@aliel.fr Signed-off-by: Lee Jones --- drivers/thermal/khadas_mcu_fan.c | 108 ++++++++++++++++++++++++------- 1 file changed, 86 insertions(+), 22 deletions(-) diff --git a/drivers/thermal/khadas_mcu_fan.c b/drivers/thermal/khadas_mcu_fan.c index 21b3d0a71bd0..1da639d05497 100644 --- a/drivers/thermal/khadas_mcu_fan.c +++ b/drivers/thermal/khadas_mcu_fan.c @@ -13,34 +13,33 @@ #include #include #include +#include +#include -#define MAX_LEVEL 3 +#define MAX_LEVEL 5 struct khadas_mcu_fan_ctx { struct khadas_mcu *mcu; + unsigned int fan_reg; unsigned int level; + const unsigned int *levels; + unsigned int nlevels; struct thermal_cooling_device *cdev; + struct regulator *power; }; 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; + return regmap_write(ctx->mcu->regmap, ctx->fan_reg, level); } static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = MAX_LEVEL; + struct khadas_mcu_fan_ctx *ctx = cdev->devdata; + + *state = min_t(unsigned int, MAX_LEVEL, ctx->nlevels - 1); return 0; } @@ -60,14 +59,21 @@ khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) { struct khadas_mcu_fan_ctx *ctx = cdev->devdata; + int ret; - if (state > MAX_LEVEL) + if (state > MAX_LEVEL || state >= ctx->nlevels) return -EINVAL; if (state == ctx->level) return 0; - return khadas_mcu_fan_set_level(ctx, state); + ret = khadas_mcu_fan_set_level(ctx, ctx->levels[state]); + if (ret) + return ret; + + ctx->level = state; + + return 0; } static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { @@ -76,22 +82,71 @@ static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = { .set_cur_state = khadas_mcu_fan_set_cur_state, }; +static void khadas_mcu_fan_regulator_disable(void *data) +{ + struct regulator *power = data; + + regulator_disable(power); +} + +static const unsigned int khadas_mcu_fan_levels[] = { 0, 1, 2, 3 }; + +static const unsigned int khadas_mcu_vim4_fan_levels[] = { 0, 30, 40, 55, 75, 100 }; + static int khadas_mcu_fan_probe(struct platform_device *pdev) { + const struct platform_device_id *id = platform_get_device_id(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; + + if (!dev->of_node) + dev->of_node = of_node_get(dev->parent->of_node); + + if (!id) + return -EINVAL; + ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); if (!ctx) return -ENOMEM; + ctx->mcu = mcu; + switch (id->driver_data) { + case KHADAS_MCU_GENERIC: + ctx->fan_reg = KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG; + ctx->levels = khadas_mcu_fan_levels; + ctx->nlevels = ARRAY_SIZE(khadas_mcu_fan_levels); + break; + case KHADAS_MCU_VIM4: + ctx->fan_reg = KHADAS_MCU_VIM4_FAN_CTRL_REG; + ctx->levels = khadas_mcu_vim4_fan_levels; + ctx->nlevels = ARRAY_SIZE(khadas_mcu_vim4_fan_levels); + break; + default: + return -ENODEV; + } + + ctx->power = devm_regulator_get(dev, "fan"); + if (IS_ERR(ctx->power)) + return PTR_ERR(ctx->power); + + ret = regulator_enable(ctx->power); + if (ret) { + dev_err(dev, "Failed to enable fan power supply: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, khadas_mcu_fan_regulator_disable, ctx->power); + if (ret) + return ret; + platform_set_drvdata(pdev, ctx); - cdev = devm_thermal_of_child_cooling_device_register(dev->parent, - dev->parent->of_node, + cdev = devm_thermal_of_child_cooling_device_register(dev, + dev->of_node, "khadas-mcu-fan", ctx, &khadas_mcu_fan_cooling_ops); if (IS_ERR(cdev)) { @@ -109,21 +164,24 @@ 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); + khadas_mcu_fan_set_level(ctx, ctx->levels[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); + ret = khadas_mcu_fan_set_level(ctx, ctx->levels[0]); if (ret) return ret; - ctx->level = level_save; + ret = regulator_disable(ctx->power); + if (ret) { + khadas_mcu_fan_set_level(ctx, ctx->levels[ctx->level]); + return ret; + } return 0; } @@ -131,8 +189,13 @@ static int khadas_mcu_fan_suspend(struct device *dev) static int khadas_mcu_fan_resume(struct device *dev) { struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev); + int ret; - return khadas_mcu_fan_set_level(ctx, ctx->level); + ret = regulator_enable(ctx->power); + if (ret) + return ret; + + return khadas_mcu_fan_set_level(ctx, ctx->levels[ctx->level]); } #endif @@ -140,7 +203,8 @@ 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", }, + { .name = "khadas-mcu-fan-ctrl", .driver_data = KHADAS_MCU_GENERIC }, + { .name = "khadas-mcu-vim4-fan", .driver_data = KHADAS_MCU_VIM4 }, {}, }; MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);