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 
khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx * ctx,unsigned int level)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 
khadas_mcu_fan_get_max_state(struct thermal_cooling_device * cdev,unsigned long * state)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 
khadas_mcu_fan_get_cur_state(struct thermal_cooling_device * cdev,unsigned long * state)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
khadas_mcu_fan_set_cur_state(struct thermal_cooling_device * cdev,unsigned long state)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 
khadas_mcu_fan_probe(struct platform_device * pdev)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 
1045772717eSNeil Armstrong 	return 0;
1055772717eSNeil Armstrong }
1065772717eSNeil Armstrong 
khadas_mcu_fan_shutdown(struct platform_device * pdev)1075772717eSNeil Armstrong static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
1085772717eSNeil Armstrong {
1095772717eSNeil Armstrong 	struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
1105772717eSNeil Armstrong 
1115772717eSNeil Armstrong 	khadas_mcu_fan_set_level(ctx, 0);
1125772717eSNeil Armstrong }
1135772717eSNeil Armstrong 
1145772717eSNeil Armstrong #ifdef CONFIG_PM_SLEEP
khadas_mcu_fan_suspend(struct device * dev)1155772717eSNeil Armstrong static int khadas_mcu_fan_suspend(struct device *dev)
1165772717eSNeil Armstrong {
1175772717eSNeil Armstrong 	struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
1185772717eSNeil Armstrong 	unsigned int level_save = ctx->level;
1195772717eSNeil Armstrong 	int ret;
1205772717eSNeil Armstrong 
1215772717eSNeil Armstrong 	ret = khadas_mcu_fan_set_level(ctx, 0);
1225772717eSNeil Armstrong 	if (ret)
1235772717eSNeil Armstrong 		return ret;
1245772717eSNeil Armstrong 
1255772717eSNeil Armstrong 	ctx->level = level_save;
1265772717eSNeil Armstrong 
1275772717eSNeil Armstrong 	return 0;
1285772717eSNeil Armstrong }
1295772717eSNeil Armstrong 
khadas_mcu_fan_resume(struct device * dev)1305772717eSNeil Armstrong static int khadas_mcu_fan_resume(struct device *dev)
1315772717eSNeil Armstrong {
1325772717eSNeil Armstrong 	struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
1335772717eSNeil Armstrong 
1345772717eSNeil Armstrong 	return khadas_mcu_fan_set_level(ctx, ctx->level);
1355772717eSNeil Armstrong }
1365772717eSNeil Armstrong #endif
1375772717eSNeil Armstrong 
1385772717eSNeil Armstrong static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
1395772717eSNeil Armstrong 			 khadas_mcu_fan_resume);
1405772717eSNeil Armstrong 
1415772717eSNeil Armstrong static const struct platform_device_id khadas_mcu_fan_id_table[] = {
1425772717eSNeil Armstrong 	{ .name = "khadas-mcu-fan-ctrl", },
1435772717eSNeil Armstrong 	{},
1445772717eSNeil Armstrong };
1455772717eSNeil Armstrong MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
1465772717eSNeil Armstrong 
1475772717eSNeil Armstrong static struct platform_driver khadas_mcu_fan_driver = {
1485772717eSNeil Armstrong 	.probe		= khadas_mcu_fan_probe,
1495772717eSNeil Armstrong 	.shutdown	= khadas_mcu_fan_shutdown,
1505772717eSNeil Armstrong 	.driver	= {
1515772717eSNeil Armstrong 		.name		= "khadas-mcu-fan-ctrl",
1525772717eSNeil Armstrong 		.pm		= &khadas_mcu_fan_pm,
1535772717eSNeil Armstrong 	},
1545772717eSNeil Armstrong 	.id_table	= khadas_mcu_fan_id_table,
1555772717eSNeil Armstrong };
1565772717eSNeil Armstrong 
1575772717eSNeil Armstrong module_platform_driver(khadas_mcu_fan_driver);
1585772717eSNeil Armstrong 
1595772717eSNeil Armstrong MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
1605772717eSNeil Armstrong MODULE_DESCRIPTION("Khadas MCU FAN driver");
1615772717eSNeil Armstrong MODULE_LICENSE("GPL");
162