0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/module.h>
0010 #include <linux/of.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/mfd/khadas-mcu.h>
0013 #include <linux/regmap.h>
0014 #include <linux/sysfs.h>
0015 #include <linux/thermal.h>
0016
0017 #define MAX_LEVEL 3
0018
0019 struct khadas_mcu_fan_ctx {
0020 struct khadas_mcu *mcu;
0021 unsigned int level;
0022 struct thermal_cooling_device *cdev;
0023 };
0024
0025 static int khadas_mcu_fan_set_level(struct khadas_mcu_fan_ctx *ctx,
0026 unsigned int level)
0027 {
0028 int ret;
0029
0030 ret = regmap_write(ctx->mcu->regmap, KHADAS_MCU_CMD_FAN_STATUS_CTRL_REG,
0031 level);
0032 if (ret)
0033 return ret;
0034
0035 ctx->level = level;
0036
0037 return 0;
0038 }
0039
0040 static int khadas_mcu_fan_get_max_state(struct thermal_cooling_device *cdev,
0041 unsigned long *state)
0042 {
0043 *state = MAX_LEVEL;
0044
0045 return 0;
0046 }
0047
0048 static int khadas_mcu_fan_get_cur_state(struct thermal_cooling_device *cdev,
0049 unsigned long *state)
0050 {
0051 struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
0052
0053 *state = ctx->level;
0054
0055 return 0;
0056 }
0057
0058 static int
0059 khadas_mcu_fan_set_cur_state(struct thermal_cooling_device *cdev,
0060 unsigned long state)
0061 {
0062 struct khadas_mcu_fan_ctx *ctx = cdev->devdata;
0063
0064 if (state > MAX_LEVEL)
0065 return -EINVAL;
0066
0067 if (state == ctx->level)
0068 return 0;
0069
0070 return khadas_mcu_fan_set_level(ctx, state);
0071 }
0072
0073 static const struct thermal_cooling_device_ops khadas_mcu_fan_cooling_ops = {
0074 .get_max_state = khadas_mcu_fan_get_max_state,
0075 .get_cur_state = khadas_mcu_fan_get_cur_state,
0076 .set_cur_state = khadas_mcu_fan_set_cur_state,
0077 };
0078
0079 static int khadas_mcu_fan_probe(struct platform_device *pdev)
0080 {
0081 struct khadas_mcu *mcu = dev_get_drvdata(pdev->dev.parent);
0082 struct thermal_cooling_device *cdev;
0083 struct device *dev = &pdev->dev;
0084 struct khadas_mcu_fan_ctx *ctx;
0085 int ret;
0086
0087 ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
0088 if (!ctx)
0089 return -ENOMEM;
0090 ctx->mcu = mcu;
0091 platform_set_drvdata(pdev, ctx);
0092
0093 cdev = devm_thermal_of_cooling_device_register(dev->parent,
0094 dev->parent->of_node, "khadas-mcu-fan", ctx,
0095 &khadas_mcu_fan_cooling_ops);
0096 if (IS_ERR(cdev)) {
0097 ret = PTR_ERR(cdev);
0098 dev_err(dev, "Failed to register khadas-mcu-fan as cooling device: %d\n",
0099 ret);
0100 return ret;
0101 }
0102 ctx->cdev = cdev;
0103
0104 return 0;
0105 }
0106
0107 static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
0108 {
0109 struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
0110
0111 khadas_mcu_fan_set_level(ctx, 0);
0112 }
0113
0114 #ifdef CONFIG_PM_SLEEP
0115 static int khadas_mcu_fan_suspend(struct device *dev)
0116 {
0117 struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
0118 unsigned int level_save = ctx->level;
0119 int ret;
0120
0121 ret = khadas_mcu_fan_set_level(ctx, 0);
0122 if (ret)
0123 return ret;
0124
0125 ctx->level = level_save;
0126
0127 return 0;
0128 }
0129
0130 static int khadas_mcu_fan_resume(struct device *dev)
0131 {
0132 struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
0133
0134 return khadas_mcu_fan_set_level(ctx, ctx->level);
0135 }
0136 #endif
0137
0138 static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
0139 khadas_mcu_fan_resume);
0140
0141 static const struct platform_device_id khadas_mcu_fan_id_table[] = {
0142 { .name = "khadas-mcu-fan-ctrl", },
0143 {},
0144 };
0145 MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
0146
0147 static struct platform_driver khadas_mcu_fan_driver = {
0148 .probe = khadas_mcu_fan_probe,
0149 .shutdown = khadas_mcu_fan_shutdown,
0150 .driver = {
0151 .name = "khadas-mcu-fan-ctrl",
0152 .pm = &khadas_mcu_fan_pm,
0153 },
0154 .id_table = khadas_mcu_fan_id_table,
0155 };
0156
0157 module_platform_driver(khadas_mcu_fan_driver);
0158
0159 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
0160 MODULE_DESCRIPTION("Khadas MCU FAN driver");
0161 MODULE_LICENSE("GPL");