perf probe: Fix memory leak when synthesizing SDT probes
[linux-2.6-microblaze.git] / drivers / thermal / khadas_mcu_fan.c
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
19 struct khadas_mcu_fan_ctx {
20         struct khadas_mcu *mcu;
21         unsigned int level;
22         struct thermal_cooling_device *cdev;
23 };
24
25 static 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
40 static 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
48 static 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
58 static int
59 khadas_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
73 static 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
79 static 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         thermal_cdev_update(cdev);
104
105         return 0;
106 }
107
108 static void khadas_mcu_fan_shutdown(struct platform_device *pdev)
109 {
110         struct khadas_mcu_fan_ctx *ctx = platform_get_drvdata(pdev);
111
112         khadas_mcu_fan_set_level(ctx, 0);
113 }
114
115 #ifdef CONFIG_PM_SLEEP
116 static int khadas_mcu_fan_suspend(struct device *dev)
117 {
118         struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
119         unsigned int level_save = ctx->level;
120         int ret;
121
122         ret = khadas_mcu_fan_set_level(ctx, 0);
123         if (ret)
124                 return ret;
125
126         ctx->level = level_save;
127
128         return 0;
129 }
130
131 static int khadas_mcu_fan_resume(struct device *dev)
132 {
133         struct khadas_mcu_fan_ctx *ctx = dev_get_drvdata(dev);
134
135         return khadas_mcu_fan_set_level(ctx, ctx->level);
136 }
137 #endif
138
139 static SIMPLE_DEV_PM_OPS(khadas_mcu_fan_pm, khadas_mcu_fan_suspend,
140                          khadas_mcu_fan_resume);
141
142 static const struct platform_device_id khadas_mcu_fan_id_table[] = {
143         { .name = "khadas-mcu-fan-ctrl", },
144         {},
145 };
146 MODULE_DEVICE_TABLE(platform, khadas_mcu_fan_id_table);
147
148 static struct platform_driver khadas_mcu_fan_driver = {
149         .probe          = khadas_mcu_fan_probe,
150         .shutdown       = khadas_mcu_fan_shutdown,
151         .driver = {
152                 .name           = "khadas-mcu-fan-ctrl",
153                 .pm             = &khadas_mcu_fan_pm,
154         },
155         .id_table       = khadas_mcu_fan_id_table,
156 };
157
158 module_platform_driver(khadas_mcu_fan_driver);
159
160 MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
161 MODULE_DESCRIPTION("Khadas MCU FAN driver");
162 MODULE_LICENSE("GPL");