summaryrefslogtreecommitdiff
path: root/include/fan.h
blob: 29552e0e6a1c799e5cc14380786f5aefb7b3f109 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/* Copyright 2013 The ChromiumOS Authors
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

/* Fan control module for Chrome EC */

#ifndef __CROS_EC_FAN_H
#define __CROS_EC_FAN_H

#include "config.h"

#include <stdbool.h>
#include <stdint.h>

#ifdef CONFIG_ZEPHYR
#ifdef CONFIG_PLATFORM_EC_FAN

#include <zephyr/devicetree.h>
#define NODE_ID_AND_COMMA(node_id) node_id,
enum fan_channel {
#if DT_NODE_EXISTS(DT_INST(0, cros_ec_fans))
	DT_FOREACH_CHILD(DT_INST(0, cros_ec_fans), NODE_ID_AND_COMMA)
#endif /* cros_ec_fans */
		FAN_CH_COUNT
};

BUILD_ASSERT(FAN_CH_COUNT == CONFIG_PLATFORM_EC_NUM_FANS);

#endif /* CONFIG_PLATFORM_EC_FAN */
#endif /* CONFIG_ZEPHYR */

/**
 * STOPPED means not spinning.
 *
 * When setting fan rpm, some implementations in chip layer (npcx and it83xx)
 * is to adjust fan pwm duty steps by steps. In this period, fan_status will
 * be marked as CHANGING. After change is done, fan_status will become LOCKED.
 *
 * In the period of changing pwm duty, if it's trying to increase/decrease duty
 * even when duty is already in upper/lower bound. Then this action won't work,
 * and fan_status will be marked as FRUSTRATED.
 *
 * For other implementations in chip layer (mchp), there is no
 * changing period. So they don't have CHANGING status.
 * Just return status as LOCKED in normal spinning case, return STOPPED when
 * not spinning, return FRUSTRATED when the related flags (which is read from
 * chip's register) is set.
 */
enum fan_status {
	FAN_STATUS_STOPPED = 0,
	FAN_STATUS_CHANGING = 1,
	FAN_STATUS_LOCKED = 2,
	FAN_STATUS_FRUSTRATED = 3
};

/* Fan mode */
enum fan_mode {
	/* FAN rpm mode */
	FAN_RPM = 0,
	/* FAN duty mode */
	FAN_DUTY,
};

struct fan_conf {
	unsigned int flags;
	/* Hardware channel number (the meaning is chip-specific) */
	int ch;
	/* Active-high power_good input GPIO, or -1 if none */
	int pgood_gpio;
	/* Active-high power_enable output GPIO, or -1 if none */
	int enable_gpio;
};

struct fan_rpm {
	/* rpm_min is to keep turning. rpm_start is to begin turning */
	int rpm_min;
	int rpm_start;
	int rpm_max;
	uint8_t rpm_deviation;
};

/* Characteristic of each physical fan */
struct fan_t {
	const struct fan_conf *conf;
	const struct fan_rpm *rpm;
};

/* Fan status data structure */
struct fan_data {
	/* Fan mode */
	enum fan_mode current_fan_mode;
	/* Actual rpm */
	int rpm_actual;
	/* Previous rpm */
	int rpm_pre;
	/* Target rpm */
	int rpm_target;
	/* Fan config flags */
	unsigned int flags;
	/* Automatic fan status */
	enum fan_status auto_status;
	/* Current PWM duty cycle percentage */
	int pwm_percent;
	/* Whether the PWM channel is enabled */
	bool pwm_enabled;
};

/* Values for .flags field */
/*   Enable automatic RPM control using tach input */
#define FAN_USE_RPM_MODE BIT(0)
/*   Require a higher duty cycle to start up than to keep running */
#define FAN_USE_FAST_START BIT(1)

/* The list of fans is instantiated in board.c. */
#ifdef CONFIG_FAN_DYNAMIC
extern struct fan_t fans[];
#else
extern const struct fan_t fans[];
#endif

/* For convenience */
#define FAN_CH(fan) fans[fan].conf->ch
/* Calculate temp_ratio as a macro. common/thermal.c defines the same
 * function, but it cannot be used at file scope.
 */
#define THERMAL_FAN_PERCENT(low, high, cur)                   \
	(((low) < (cur) && (cur) < (high)) ?                  \
		 (100 * ((cur) - (low)) / ((high) - (low))) : \
		 ((cur) <= (low) ? 0 : 100))
/* Convert a temperature in centigrade to a temp_ratio, assuming constants
 * temp_fan_off, temp_fan_max, already in Kelvin.  Helpful for fan tables.
 */
#define TEMP_TO_RATIO(temp_c) \
	(THERMAL_FAN_PERCENT((temp_fan_off), (temp_fan_max), (C_TO_K(temp_c))))

/**
 * Set the amount of active cooling needed. The thermal control task will call
 * this frequently, and the fan control logic will attempt to provide it.
 *
 * @param fan   Fan number (index into fans[])
 * @param pct   Percentage of cooling effort needed (0 - 100)
 */
void fan_set_percent_needed(int fan, int pct);

/**
 * Convert temp_ratio (temperature as a percentage of the ec_thermal_config
 * .temp_fan_off to .temp_fan_max range, also cooling effort needed) into a
 * target fan RPM.
 * The default implementation should be sufficient for most needs, but
 * individual boards may provide a custom version if needed (see config.h).
 *
 * @param fan          Fan number (index into fans[])
 * @param temp_ratio   Temperature as fraction of temp_fan_off to
 *                     temp_fan_max range, expressed as a percent ([0,100]).
 * Return              Target RPM for fan
 */
int fan_percent_to_rpm(int fan, int temp_ratio);
/* Data structure to hold a tuple of parameters for one sensor and one fan. */
struct fan_step_1_1 {
	/* lowest temp_ratio (exclusive) to apply this rpm when decreasing.
	 * Use this rpm until temp_ratio falls to or below this threshold.
	 */
	int decreasing_temp_ratio_threshold;
	/* lowest temp_ratio (inclusive) to apply this rpm when increasing.
	 * Use this rpm when temp_ratio exceeds this threshold.
	 */
	int increasing_temp_ratio_threshold;
	int rpm;
};
/**
 * Convert temp_ratio (temperature as a percentage of the ec_thermal_config
 * .temp_fan_off to .temp_fan_max range) into a target fan RPM.
 *
 * This function adapts the most popular custom version of fan_percent_to_rpm,
 * which provides hysteresis to reduce temperature/fan speed oscillations.
 *
 * To refactor to this, convert the fan_step-based fan_table to fan_step_1_1 by
 * removing the first (.rpm = 0) element and using
 * decreasing/increasing_temp_ratio_threshold for off/on respectively.
 * See example in ../test/fan.c.
 *
 * @param fan_table        Pointer to ordered array of fan_step_1_1 structs.
 *                         There is no need to have any element with .rpm = 0.
 *                         Function assumes 0 when temp_ratio is below the
 *                         thresholds in the index-0 element.
 * @param num_fan_levels   Size of fan_table
 * @param fan_index        Fan number (index into fans[])
 * @param temp_ratio       Temperature as fraction of temp_fan_off to
 *                         temp_fan_max range, expressed as a percent ([0,100]).
 * @param on_change        Pointer to function to be run when the target fan
 *                         rpm changes, such as ezkinil board_print_temps().
 * Return                  Target RPM for fan
 */
int temp_ratio_to_rpm_hysteresis(const struct fan_step_1_1 *fan_table,
				 int num_fan_levels, int fan_index,
				 int temp_ratio, void (*on_change)(void));

/**
 * These functions require chip-specific implementations.
 */

/* Enable/Disable the fan controller */
void fan_set_enabled(int ch, int enabled);
int fan_get_enabled(int ch);

/* Fixed pwm duty cycle (0-100%) */
void fan_set_duty(int ch, int percent);
int fan_get_duty(int ch);

/* Enable/Disable automatic RPM control using tach feedback */
void fan_set_rpm_mode(int ch, int rpm_mode);
int fan_get_rpm_mode(int ch);

/* Set the target for the automatic RPM control */
void fan_set_rpm_target(int ch, int rpm);
int fan_get_rpm_actual(int ch);
int fan_get_rpm_target(int ch);

/* Is the fan stalled when it shouldn't be? */
int fan_is_stalled(int ch);

enum fan_status fan_get_status(int ch);

/* Initialize the HW according to the desired flags */
void fan_channel_setup(int ch, unsigned int flags);

int fan_get_count(void);

void fan_set_count(int count);

int is_thermal_control_enabled(int idx);

#ifdef CONFIG_ZEPHYR
extern struct fan_data fan_data[];

/**
 * This function sets PWM duty based on target RPM.
 *
 * The target and current RPM values in fan_data entry that
 * corresponds to selected fan has to be updated before this
 * function is called.
 *
 * @param ch    Fan number (index into fan_data[] and fans[])
 * Return       Fan status (see fan_status enum definition)
 */
enum fan_status board_override_fan_control_duty(int ch);
#endif

#endif /* __CROS_EC_FAN_H */