coreboot
coreboot is an Open Source project aimed at replacing the proprietary BIOS found in most computers.
fan_control.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 #include <arch/io.h>
4 #include <console/console.h>
5 #include "../common/fan_control.h"
6 #include "f81803a_hwm.h"
7 
8 static const char msg_err_invalid[] = "Error: invalid";
9 static const char msg_err_wrong_order[] = "Error: wrong order,";
10 static const char msg_err_fan[] = "fan";
11 static const char msg_err_temp_source[] = "temperature source";
12 static const char msg_err_type[] = "type";
13 static const char msg_err_mode[] = "mode";
14 static const char msg_err_rate[] = "change rate";
15 static const char msg_err_frequency[] = "frequency";
16 static const char msg_err_temp_sensor[] = "temperature sensor";
17 static const char msg_err_bondary[] = "boundary";
18 static const char msg_err_section[] = "section";
19 static const char no_msg[] = "";
20 
21 struct cross_ref {
22  int selection;
23  const char *message;
24 };
25 static struct cross_ref msg_table[] = {
33  {0, NULL},
34 };
35 
36 static const char *get_msg(int err)
37 {
38  int i = 0;
39  while (msg_table[i].selection) {
40  if (msg_table[i].selection == err)
41  return msg_table[i].message;
42  i++;
43  }
44  return no_msg;
45 }
46 
47 static int message_invalid_1(int err, u8 fan)
48 {
49  if (err == HWM_STATUS_INVALID_FAN)
50  printk(BIOS_ERR, "%s %s %d!\n", msg_err_invalid, get_msg(err), fan);
51  else
52  printk(BIOS_ERR, "%s Fan %d %s!\n", msg_err_invalid, fan, get_msg(err));
53  return err;
54 }
55 
56 static int message_invalid_2(int err, u8 fan)
57 {
58  switch (err) {
60  printk(BIOS_ERR, "%s fan %d %s value!\n", msg_err_invalid, fan,
62  break;
64  printk(BIOS_ERR, "%s fan %d %s value!\n", msg_err_invalid, fan,
66  break;
68  printk(BIOS_ERR, "%s fan %d %s!\n", msg_err_wrong_order, fan, msg_err_bondary);
69  break;
71  printk(BIOS_ERR, "%s fan %d %s!\n", msg_err_wrong_order, fan, msg_err_section);
72  break;
73  default:
74  break;
75  }
76  return err;
77 }
78 
79 static void write_hwm_reg(u16 address, u8 index, u8 value)
80 {
81  u16 index_add, data_add;
82  index_add = address | 0x0001; /* force odd address */
83  data_add = index_add + 1;
84  outb(index, index_add);
85  outb(value, data_add);
86 }
87 
88 static u8 read_hwm_reg(u16 address, u8 index)
89 {
90  u16 index_add, data_add;
91  index_add = address | 0x0001; /* force odd address */
92  data_add = index_add + 1;
93  outb(index, index_add);
94  return inb(data_add);
95 }
96 
97 static void hwm_reg_modify(u16 address, u8 index, u8 shift, u8 mask,
98  u8 value)
99 {
100  u8 use_mask = mask << shift;
101  u8 use_value = (value & mask) << shift;
102  u8 temp = read_hwm_reg(address, index);
103 
104  temp &= ~use_mask;
105  temp |= use_value;
106  write_hwm_reg(address, index, temp);
107 }
108 
109 /*
110  * Registers 0x94,0x95, 0x96 and 0x9b have 2 versions (banks) selected through
111  * bit 7 of register 0x9f.
112  */
113 static inline void select_hwm_bank(u16 address, u8 value)
114 {
117 }
118 
119 /*
120  * Boundaries and sections must be presented in the same order as in the HWM
121  * registers, that is, from highest value to lowest. This procedure checks for
122  * the correct order.
123  */
124 static int check_value_seq(u8 *values, u8 count)
125 {
126  u8 last_value = CPU_DAMAGE_TEMP;
127  u8 current_value, i;
128  for (i = 0; i < count; i++) {
129  current_value = values[i];
130  if (current_value > CPU_DAMAGE_TEMP)
131  return STATUS_INVALID_VALUE;
132  if (current_value >= last_value)
133  return STATUS_INVALID_ORDER;
134  last_value = current_value;
135  }
136  return HWM_STATUS_SUCCESS;
137 }
138 
139 int set_sensor_type(u16 base_address, external_sensor sensor,
141 {
142  u8 sensor_status = read_hwm_reg(base_address, TP_DIODE_STATUS);
143 
144  printk(BIOS_DEBUG, "%s\n", __func__);
145  switch (sensor) {
146  case EXTERNAL_SENSOR1:
147  if (sensor_status & TP_EXTERNAL_SENSOR1_OPEN) {
148  printk(BIOS_WARNING, "Sensor 1 disconnected!\n");
150  }
151  hwm_reg_modify(base_address, TP_SENSOR_TYPE,
153  break;
154  case EXTERNAL_SENSOR2:
155  if (sensor_status & TP_EXTERNAL_SENSOR2_OPEN) {
156  printk(BIOS_WARNING, "Sensor 2 disconnected!\n");
158  }
159  hwm_reg_modify(base_address, TP_SENSOR_TYPE,
161  break;
162  case IGNORE_SENSOR:
163  break;
164  default:
166  }
167  return HWM_STATUS_SUCCESS;
168 }
169 
170 int set_fan_temperature_source(u16 base_address, u8 fan,
171  fan_temp_source source)
172 {
173  u8 index, high_value, low_value;
174 
175  printk(BIOS_DEBUG, "%s\n", __func__);
176  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
178  index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
179  high_value = (source >> 2) & FAN_BIT_MASK;
180  low_value = source & FAN_TEMP_SEL_LOW_MASK;
181  hwm_reg_modify(base_address, index, FAN_TEMP_SEL_HIGH_SHIFT,
182  FAN_BIT_MASK, high_value);
183  hwm_reg_modify(base_address, index, FAN_TEMP_SEL_LOW_SHIFT,
184  FAN_TEMP_SEL_LOW_MASK, low_value);
185  /*
186  * Fan 1 has a weight mechanism for adjusting for next fan speed. Basically the idea is
187  * to react more aggressively (normally CPU fan) based on how high another temperature
188  * (system, thermistor near the CPU, anything) is. This would be highly platform
189  * dependent, and by setting the weight temperature same as the control temperature.
190  * This code cancels the weight mechanism and make it work with any board. If a board
191  * wants to use the weight mechanism, OEM should implement it after calling the main
192  * HWM programming.
193  */
194  if (fan == FIRST_FAN) {
195  select_hwm_bank(base_address, 1);
196  hwm_reg_modify(base_address, FAN_MODE_REG,
198  select_hwm_bank(base_address, 0);
199  }
200  return HWM_STATUS_SUCCESS;
201 }
202 
203 int set_fan_type_mode(u16 base_address, u8 fan, fan_type type, fan_mode mode)
204 {
205  u8 shift;
206 
207  printk(BIOS_DEBUG, "%s\n", __func__);
208  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
210  select_hwm_bank(base_address, 0);
211  if (type < FAN_TYPE_RESERVED) {
212  shift = FAN_TYPE_SHIFT(fan);
213  hwm_reg_modify(base_address, FAN_TYPE_REG, shift,
215  }
216  if (mode < FAN_MODE_DEFAULT) {
217  shift = FAN_MODE_SHIFT(fan);
218  hwm_reg_modify(base_address, FAN_MODE_REG, shift,
219  FAN_MODE_MASK, mode);
220  }
221  return HWM_STATUS_SUCCESS;
222 }
223 
224 int set_pwm_frequency(u16 base_address, u8 fan, fan_pwm_freq frequency)
225 {
226  u8 shift, index, byte;
227 
228  printk(BIOS_DEBUG, "%s\n", __func__);
229  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
231  byte = read_hwm_reg(base_address, FAN_TYPE_REG);
232  shift = FAN_TYPE_SHIFT(fan);
233  if (((byte >> shift) & FAN_TYPE_PWM_CHECK) == FAN_TYPE_PWM_CHECK) {
234  printk(BIOS_WARNING, "Fan %d not programmed as PWM!\n", fan);
236  }
237  select_hwm_bank(base_address, 1);
238  shift = FAN_FREQ_SEL_ADD_SHIFT(fan);
239  byte = (frequency >> 1) & FAN_BIT_MASK;
240  hwm_reg_modify(base_address, FAN_MODE_REG, shift, FAN_BIT_MASK,
241  byte);
242  select_hwm_bank(base_address, 0);
243  index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
244  byte = frequency & FAN_BIT_MASK;
245  hwm_reg_modify(base_address, index, FAN_PWM_FREQ_SEL_SHIFT,
246  FAN_BIT_MASK, byte);
247  return HWM_STATUS_SUCCESS;
248 }
249 
250 int set_sections(u16 base_address, u8 fan, u8 *boundaries, u8 *sections)
251 {
252  int status, temp;
253  u8 i, index, value;
254 
255  printk(BIOS_DEBUG, "%s\n", __func__);
256  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
258  status = check_value_seq(boundaries,
260  if (status != HWM_STATUS_SUCCESS) {
261  if (status == STATUS_INVALID_VALUE)
264  }
265  status = check_value_seq(sections,
267  if (status != HWM_STATUS_SUCCESS) {
268  if (status == STATUS_INVALID_VALUE)
271  }
272  index = FAN_ADJUST(fan, FAN_BOUND_TEMP);
273  for (i = 0; i < FINTEK_BOUNDARIES_SIZE; i++) {
274  value = boundaries[i];
275  write_hwm_reg(base_address, index, value);
276  index++;
277  }
278  index = FAN_ADJUST(fan, FAN_SECTION_SPEED);
279  for (i = 0; i < FINTEK_SECTIONS_SIZE; i++) {
280  value = sections[i];
281  if (value > 100)
283  temp = (255 * value) / 100;
284  value = (u8) (temp & 0x00ff);
285  write_hwm_reg(base_address, index, value);
286  index++;
287  }
288  return HWM_STATUS_SUCCESS;
289 }
290 
291 int set_fan_speed_change_rate(u16 base_address, u8 fan, fan_rate_up rate_up,
292  fan_rate_down rate_down)
293 {
294  u8 shift, index;
295 
296  printk(BIOS_DEBUG, "%s\n", __func__);
297  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
299 
300  index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
301  shift = FAN_RATE_SHIFT(fan);
302 
303  if (rate_up == FAN_UP_RATE_JUMP) {
304  hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
305  FAN_BIT_MASK, 1);
306  } else {
307  hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
308  FAN_BIT_MASK, 0);
309  if (rate_up < FAN_UP_RATE_DEFAULT) {
310  hwm_reg_modify(base_address, FAN_UP_RATE_REG,
311  shift, FAN_RATE_MASK, rate_up);
312  }
313  }
314 
315  if (rate_down == FAN_DOWN_RATE_JUMP) {
316  hwm_reg_modify(base_address, index, FAN_JUMP_DOWN_SHIFT,
317  FAN_BIT_MASK, 1);
318  } else {
319  hwm_reg_modify(base_address, index, FAN_JUMP_UP_SHIFT,
320  FAN_BIT_MASK, 0);
321  select_hwm_bank(base_address, 0);
322  if (rate_down < FAN_DOWN_RATE_DEFAULT) {
323  hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
324  shift, FAN_RATE_MASK, rate_down);
325  hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
327  FAN_BIT_MASK, 0);
328  }
329  if (rate_down == FAN_DOWN_RATE_SAME_AS_UP) {
330  hwm_reg_modify(base_address, FAN_DOWN_RATE_REG,
332  FAN_BIT_MASK, 1);
333  }
334  }
335  return HWM_STATUS_SUCCESS;
336 }
337 
338 int set_fan_follow(u16 base_address, u8 fan, fan_follow follow)
339 {
340  u8 index;
341 
342  printk(BIOS_DEBUG, "%s\n", __func__);
343  if ((fan < FIRST_FAN) || (fan > LAST_FAN))
345  index = FAN_ADJUST(fan, FAN_TMP_MAPPING);
346  hwm_reg_modify(base_address, index, FAN_INTERPOLATION_SHIFT,
347  FAN_BIT_MASK, follow);
348  return HWM_STATUS_SUCCESS;
349 }
pte_t value
Definition: mmu.c:91
#define printk(level,...)
Definition: stdlib.h:16
u8 inb(u16 port)
void outb(u8 val, u16 port)
#define FAN_BIT_MASK
Definition: f81803a_hwm.h:47
#define FAN_SECTION_SPEED
Definition: f81803a_hwm.h:38
#define FAN_TYPE_SHIFT(fan)
Definition: f81803a_hwm.h:18
#define FAN_MODE_SHIFT(fan)
Definition: f81803a_hwm.h:22
#define TP_EXTERNAL_SENSOR1_OPEN
Definition: f81803a_hwm.h:15
#define FAN_TMP_MAPPING
Definition: f81803a_hwm.h:39
#define TP_DIODE_STATUS
Definition: f81803a_hwm.h:10
#define FAN_TEMP_SEL_LOW_MASK
Definition: f81803a_hwm.h:46
#define STATUS_INVALID_VALUE
Definition: f81803a_hwm.h:51
#define FAN_JUMP_UP_SHIFT
Definition: f81803a_hwm.h:43
#define TP_SENSOR_TYPE
Definition: f81803a_hwm.h:6
#define FAN_ADJUST(fan, start)
Definition: f81803a_hwm.h:49
#define FAN_DOWN_RATE_REG
Definition: f81803a_hwm.h:31
#define FAN_JUMP_DOWN_SHIFT
Definition: f81803a_hwm.h:44
#define FAN_TYPE_MASK
Definition: f81803a_hwm.h:19
#define FAN1_ADJ_SEL_MASK
Definition: f81803a_hwm.h:26
#define FAN_MODE_REG
Definition: f81803a_hwm.h:20
#define FAN_RATE_SHIFT(fan)
Definition: f81803a_hwm.h:29
#define FAN_TYPE_REG
Definition: f81803a_hwm.h:17
#define FAN_TEMP_SEL_HIGH_SHIFT
Definition: f81803a_hwm.h:40
#define FIRST_FAN
Definition: f81803a_hwm.h:54
#define FAN_FUNC_PROG_SEL_SHIFT
Definition: f81803a_hwm.h:35
#define TP_SENSOR_TYPE_MASK
Definition: f81803a_hwm.h:9
#define LAST_FAN
Definition: f81803a_hwm.h:55
#define FAN_MODE_MASK
Definition: f81803a_hwm.h:23
#define FAN_RATE_MASK
Definition: f81803a_hwm.h:30
#define TP_SENSOR1_TYPE_SHIFT
Definition: f81803a_hwm.h:7
#define FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT
Definition: f81803a_hwm.h:32
#define TP_SENSOR2_TYPE_SHIFT
Definition: f81803a_hwm.h:8
#define FAN_PWM_FREQ_SEL_SHIFT
Definition: f81803a_hwm.h:41
#define FAN_FAULT_TIME_REG
Definition: f81803a_hwm.h:34
#define STATUS_INVALID_ORDER
Definition: f81803a_hwm.h:52
#define FAN_UP_RATE_REG
Definition: f81803a_hwm.h:28
#define FAN_FREQ_SEL_ADD_SHIFT(fan)
Definition: f81803a_hwm.h:27
#define TP_EXTERNAL_SENSOR2_OPEN
Definition: f81803a_hwm.h:14
#define FAN1_ADJ_SEL_SHIFT
Definition: f81803a_hwm.h:25
#define FAN_BOUND_TEMP
Definition: f81803a_hwm.h:37
#define FAN_TEMP_SEL_LOW_SHIFT
Definition: f81803a_hwm.h:45
#define FAN_INTERPOLATION_SHIFT
Definition: f81803a_hwm.h:42
int set_sections(u16 base_address, u8 fan, u8 *boundaries, u8 *sections)
Definition: fan_control.c:250
static int message_invalid_1(int err, u8 fan)
Definition: fan_control.c:47
static u8 read_hwm_reg(u16 address, u8 index)
Definition: fan_control.c:88
static void write_hwm_reg(u16 address, u8 index, u8 value)
Definition: fan_control.c:79
static const char msg_err_invalid[]
Definition: fan_control.c:8
int set_fan_speed_change_rate(u16 base_address, u8 fan, fan_rate_up rate_up, fan_rate_down rate_down)
Definition: fan_control.c:291
static const char msg_err_type[]
Definition: fan_control.c:12
static struct cross_ref msg_table[]
Definition: fan_control.c:25
static const char msg_err_section[]
Definition: fan_control.c:18
static const char * get_msg(int err)
Definition: fan_control.c:36
static const char msg_err_wrong_order[]
Definition: fan_control.c:9
static const char no_msg[]
Definition: fan_control.c:19
static const char msg_err_temp_source[]
Definition: fan_control.c:11
static const char msg_err_frequency[]
Definition: fan_control.c:15
int set_pwm_frequency(u16 base_address, u8 fan, fan_pwm_freq frequency)
Definition: fan_control.c:224
static void hwm_reg_modify(u16 address, u8 index, u8 shift, u8 mask, u8 value)
Definition: fan_control.c:97
static const char msg_err_temp_sensor[]
Definition: fan_control.c:16
int set_fan_temperature_source(u16 base_address, u8 fan, fan_temp_source source)
Definition: fan_control.c:170
static const char msg_err_mode[]
Definition: fan_control.c:13
static void select_hwm_bank(u16 address, u8 value)
Definition: fan_control.c:113
static int check_value_seq(u8 *values, u8 count)
Definition: fan_control.c:124
int set_fan_type_mode(u16 base_address, u8 fan, fan_type type, fan_mode mode)
Definition: fan_control.c:203
static int message_invalid_2(int err, u8 fan)
Definition: fan_control.c:56
int set_sensor_type(u16 base_address, external_sensor sensor, temp_sensor_type type)
Definition: fan_control.c:139
static const char msg_err_fan[]
Definition: fan_control.c:10
int set_fan_follow(u16 base_address, u8 fan, fan_follow follow)
Definition: fan_control.c:338
static const char msg_err_bondary[]
Definition: fan_control.c:17
static const char msg_err_rate[]
Definition: fan_control.c:14
#define FINTEK_BOUNDARIES_SIZE
Definition: fan_control.h:113
#define HWM_STATUS_SUCCESS
Definition: fan_control.h:92
fan_type
Definition: fan_control.h:22
@ FAN_TYPE_RESERVED
Definition: fan_control.h:26
#define HWM_STATUS_INVALID_FAN
Definition: fan_control.h:93
#define HWM_STATUS_INVALID_RATE
Definition: fan_control.h:97
fan_pwm_freq
Definition: fan_control.h:38
#define CPU_DAMAGE_TEMP
Definition: fan_control.h:107
fan_follow
Definition: fan_control.h:72
#define HWM_STATUS_INVALID_TEMP_SENSOR
Definition: fan_control.h:99
#define HWM_STATUS_INVALID_FREQUENCY
Definition: fan_control.h:98
fan_rate_down
Definition: fan_control.h:62
@ FAN_DOWN_RATE_SAME_AS_UP
Definition: fan_control.h:68
@ FAN_DOWN_RATE_DEFAULT
Definition: fan_control.h:67
@ FAN_DOWN_RATE_JUMP
Definition: fan_control.h:69
external_sensor
Definition: fan_control.h:8
@ IGNORE_SENSOR
Definition: fan_control.h:9
@ EXTERNAL_SENSOR1
Definition: fan_control.h:10
@ EXTERNAL_SENSOR2
Definition: fan_control.h:11
#define HWM_STATUS_INVALID_TEMP_SOURCE
Definition: fan_control.h:94
#define HWM_STATUS_BOUNDARY_WRONG_ORDER
Definition: fan_control.h:102
#define FINTEK_SECTIONS_SIZE
Definition: fan_control.h:121
#define HWM_STATUS_INVALID_MODE
Definition: fan_control.h:96
fan_rate_up
Definition: fan_control.h:53
@ FAN_UP_RATE_JUMP
Definition: fan_control.h:59
@ FAN_UP_RATE_DEFAULT
Definition: fan_control.h:58
#define HWM_STATUS_INVALID_TYPE
Definition: fan_control.h:95
#define HWM_STATUS_SECTIONS_WRONG_ORDER
Definition: fan_control.h:103
temp_sensor_type
Definition: fan_control.h:16
fan_mode
Definition: fan_control.h:30
@ FAN_MODE_DEFAULT
Definition: fan_control.h:35
#define HWM_STATUS_WARNING_SENSOR_DISCONNECTED
Definition: fan_control.h:104
fan_temp_source
Definition: fan_control.h:45
#define FAN_TYPE_PWM_CHECK
Definition: fan_control.h:28
#define HWM_STATUS_WARNING_FAN_NOT_PWM
Definition: fan_control.h:105
#define HWM_STATUS_INVALID_SECTION_VALUE
Definition: fan_control.h:101
#define HWM_STATUS_INVALID_BOUNDARY_VALUE
Definition: fan_control.h:100
uint64_t address
Definition: fw_cfg_if.h:0
unsigned int type
Definition: edid.c:57
#define BIOS_DEBUG
BIOS_DEBUG - Verbose output.
Definition: loglevel.h:128
#define BIOS_ERR
BIOS_ERR - System in incomplete state.
Definition: loglevel.h:72
#define BIOS_WARNING
BIOS_WARNING - Bad configuration.
Definition: loglevel.h:86
static const int mask[4]
Definition: gpio.c:308
#define NULL
Definition: stddef.h:19
uint16_t u16
Definition: stdint.h:48
uint8_t u8
Definition: stdint.h:45
int selection
Definition: fan_control.c:22
const char * message
Definition: fan_control.c:23
#define count