f947eb6e32b6c4244304debb1d16ea2c65540488
[openwrt/staging/pepe2k.git] /
1 From 97abcfc5219149ff7d4883b295c80257f0315b5e Mon Sep 17 00:00:00 2001
2 From: Anson Huang <Anson.Huang@nxp.com>
3 Date: Wed, 7 Aug 2019 08:40:59 +0800
4 Subject: [PATCH] thermal: Add generic device cooling support
5
6 To compatible with previous implementation, add generic device
7 cooling support, each thermal zone will register a cooling
8 device, and when temperature exceed passive trip, the device
9 cooling driver will send out a system wide notification, each
10 device supporting cooling will need to register device cooling
11 and takes action when passive trip is exceeded;
12
13 Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
14 [rebase]
15 Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com>
16 ---
17 drivers/thermal/Kconfig | 7 ++
18 drivers/thermal/Makefile | 1 +
19 drivers/thermal/device_cooling.c | 152 +++++++++++++++++++++++++++++++++++++++
20 include/linux/device_cooling.h | 45 ++++++++++++
21 4 files changed, 205 insertions(+)
22 create mode 100644 drivers/thermal/device_cooling.c
23 create mode 100644 include/linux/device_cooling.h
24
25 --- a/drivers/thermal/Kconfig
26 +++ b/drivers/thermal/Kconfig
27 @@ -233,6 +233,13 @@ config IMX_THERMAL
28 cpufreq is used as the cooling device to throttle CPUs when the
29 passive trip is crossed.
30
31 +config DEVICE_THERMAL
32 + tristate "generic device cooling support"
33 + help
34 + Support for device cooling.
35 + It supports notification of crossing passive trip for devices,
36 + devices need to do their own actions to cool down the SOC.
37 +
38 config MAX77620_THERMAL
39 tristate "Temperature sensor driver for Maxim MAX77620 PMIC"
40 depends on MFD_MAX77620
41 --- a/drivers/thermal/Makefile
42 +++ b/drivers/thermal/Makefile
43 @@ -41,6 +41,7 @@ obj-$(CONFIG_DB8500_THERMAL) += db8500_t
44 obj-$(CONFIG_ARMADA_THERMAL) += armada_thermal.o
45 obj-$(CONFIG_TANGO_THERMAL) += tango_thermal.o
46 obj-$(CONFIG_IMX_THERMAL) += imx_thermal.o
47 +obj-$(CONFIG_DEVICE_THERMAL) += device_cooling.o
48 obj-$(CONFIG_MAX77620_THERMAL) += max77620_thermal.o
49 obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
50 obj-$(CONFIG_DA9062_THERMAL) += da9062-thermal.o
51 --- /dev/null
52 +++ b/drivers/thermal/device_cooling.c
53 @@ -0,0 +1,152 @@
54 +/*
55 + * Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
56 + *
57 + * This program is free software; you can redistribute it and/or modify
58 + * it under the terms of the GNU General Public License version 2 as
59 + * published by the Free Software Foundation.
60 + *
61 + */
62 +
63 +#include <linux/module.h>
64 +#include <linux/thermal.h>
65 +#include <linux/err.h>
66 +#include <linux/slab.h>
67 +
68 +struct devfreq_cooling_device {
69 + int id;
70 + struct thermal_cooling_device *cool_dev;
71 + unsigned int devfreq_state;
72 +};
73 +
74 +static DEFINE_IDR(devfreq_idr);
75 +static DEFINE_MUTEX(devfreq_cooling_lock);
76 +
77 +#define MAX_STATE 1
78 +
79 +static BLOCKING_NOTIFIER_HEAD(devfreq_cooling_chain_head);
80 +
81 +int register_devfreq_cooling_notifier(struct notifier_block *nb)
82 +{
83 + return blocking_notifier_chain_register(
84 + &devfreq_cooling_chain_head, nb);
85 +}
86 +EXPORT_SYMBOL_GPL(register_devfreq_cooling_notifier);
87 +
88 +int unregister_devfreq_cooling_notifier(struct notifier_block *nb)
89 +{
90 + return blocking_notifier_chain_unregister(
91 + &devfreq_cooling_chain_head, nb);
92 +}
93 +EXPORT_SYMBOL_GPL(unregister_devfreq_cooling_notifier);
94 +
95 +static int devfreq_cooling_notifier_call_chain(unsigned long val)
96 +{
97 + return (blocking_notifier_call_chain(
98 + &devfreq_cooling_chain_head, val, NULL)
99 + == NOTIFY_BAD) ? -EINVAL : 0;
100 +}
101 +
102 +static int devfreq_set_cur_state(struct thermal_cooling_device *cdev,
103 + unsigned long state)
104 +{
105 + struct devfreq_cooling_device *devfreq_device = cdev->devdata;
106 + int ret;
107 +
108 + ret = devfreq_cooling_notifier_call_chain(state);
109 + if (ret)
110 + return -EINVAL;
111 +
112 + devfreq_device->devfreq_state = state;
113 +
114 + return 0;
115 +}
116 +
117 +static int devfreq_get_max_state(struct thermal_cooling_device *cdev,
118 + unsigned long *state)
119 +{
120 + *state = MAX_STATE;
121 +
122 + return 0;
123 +}
124 +
125 +static int devfreq_get_cur_state(struct thermal_cooling_device *cdev,
126 + unsigned long *state)
127 +{
128 + struct devfreq_cooling_device *devfreq_device = cdev->devdata;
129 +
130 + *state = devfreq_device->devfreq_state;
131 +
132 + return 0;
133 +}
134 +
135 +static struct thermal_cooling_device_ops const devfreq_cooling_ops = {
136 + .get_max_state = devfreq_get_max_state,
137 + .get_cur_state = devfreq_get_cur_state,
138 + .set_cur_state = devfreq_set_cur_state,
139 +};
140 +
141 +static int get_idr(struct idr *idr, int *id)
142 +{
143 + int ret;
144 +
145 + mutex_lock(&devfreq_cooling_lock);
146 + ret = idr_alloc(idr, NULL, 0, 0, GFP_KERNEL);
147 + mutex_unlock(&devfreq_cooling_lock);
148 + if (unlikely(ret < 0))
149 + return ret;
150 + *id = ret;
151 +
152 + return 0;
153 +}
154 +
155 +static void release_idr(struct idr *idr, int id)
156 +{
157 + mutex_lock(&devfreq_cooling_lock);
158 + idr_remove(idr, id);
159 + mutex_unlock(&devfreq_cooling_lock);
160 +}
161 +
162 +struct thermal_cooling_device *devfreq_cooling_register(void)
163 +{
164 + struct thermal_cooling_device *cool_dev;
165 + struct devfreq_cooling_device *devfreq_dev = NULL;
166 + char dev_name[THERMAL_NAME_LENGTH];
167 + int ret = 0;
168 +
169 + devfreq_dev = kzalloc(sizeof(struct devfreq_cooling_device),
170 + GFP_KERNEL);
171 + if (!devfreq_dev)
172 + return ERR_PTR(-ENOMEM);
173 +
174 + ret = get_idr(&devfreq_idr, &devfreq_dev->id);
175 + if (ret) {
176 + kfree(devfreq_dev);
177 + return ERR_PTR(-EINVAL);
178 + }
179 +
180 + snprintf(dev_name, sizeof(dev_name), "thermal-devfreq-%d",
181 + devfreq_dev->id);
182 +
183 + cool_dev = thermal_cooling_device_register(dev_name, devfreq_dev,
184 + &devfreq_cooling_ops);
185 + if (!cool_dev) {
186 + release_idr(&devfreq_idr, devfreq_dev->id);
187 + kfree(devfreq_dev);
188 + return ERR_PTR(-EINVAL);
189 + }
190 + devfreq_dev->cool_dev = cool_dev;
191 + devfreq_dev->devfreq_state = 0;
192 +
193 + return cool_dev;
194 +}
195 +EXPORT_SYMBOL_GPL(devfreq_cooling_register);
196 +
197 +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
198 +{
199 + struct devfreq_cooling_device *devfreq_dev = cdev->devdata;
200 +
201 + thermal_cooling_device_unregister(devfreq_dev->cool_dev);
202 + release_idr(&devfreq_idr, devfreq_dev->id);
203 + kfree(devfreq_dev);
204 +}
205 +EXPORT_SYMBOL_GPL(devfreq_cooling_unregister);
206 --- /dev/null
207 +++ b/include/linux/device_cooling.h
208 @@ -0,0 +1,45 @@
209 +/*
210 + * Copyright (C) 2013-2015 Freescale Semiconductor, Inc.
211 + *
212 + * This program is free software; you can redistribute it and/or modify
213 + * it under the terms of the GNU General Public License version 2 as
214 + * published by the Free Software Foundation.
215 + *
216 + */
217 +
218 +#ifndef __DEVICE_THERMAL_H__
219 +#define __DEVICE_THERMAL_H__
220 +
221 +#include <linux/thermal.h>
222 +
223 +#ifdef CONFIG_DEVICE_THERMAL
224 +int register_devfreq_cooling_notifier(struct notifier_block *nb);
225 +int unregister_devfreq_cooling_notifier(struct notifier_block *nb);
226 +struct thermal_cooling_device *devfreq_cooling_register(void);
227 +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev);
228 +#else
229 +static inline
230 +int register_devfreq_cooling_notifier(struct notifier_block *nb)
231 +{
232 + return 0;
233 +}
234 +
235 +static inline
236 +int unregister_devfreq_cooling_notifier(struct notifier_block *nb)
237 +{
238 + return 0;
239 +}
240 +
241 +static inline
242 +struct thermal_cooling_device *devfreq_cooling_register(void)
243 +{
244 + return NULL;
245 +}
246 +
247 +static inline
248 +void devfreq_cooling_unregister(struct thermal_cooling_device *cdev)
249 +{
250 + return;
251 +}
252 +#endif
253 +#endif /* __DEVICE_THERMAL_H__ */