1 /* SPDX-License-Identifier: GPL-2.0-only */
4 #include <console/console.h>
5 #include "../common/fan_control.h"
6 #include "f81803a_hwm.h"
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
[] = "";
25 static struct cross_ref msg_table
[] = {
26 {HWM_STATUS_INVALID_FAN
, msg_err_fan
},
27 {HWM_STATUS_INVALID_TEMP_SOURCE
, msg_err_temp_source
},
28 {HWM_STATUS_INVALID_TYPE
, msg_err_type
},
29 {HWM_STATUS_INVALID_MODE
, msg_err_mode
},
30 {HWM_STATUS_INVALID_RATE
, msg_err_rate
},
31 {HWM_STATUS_INVALID_FREQUENCY
, msg_err_frequency
},
32 {HWM_STATUS_INVALID_TEMP_SENSOR
, msg_err_temp_sensor
},
36 static const char *get_msg(int err
)
39 while (msg_table
[i
].selection
) {
40 if (msg_table
[i
].selection
== err
)
41 return msg_table
[i
].message
;
47 static int message_invalid_1(int err
, u8 fan
)
49 if (err
== HWM_STATUS_INVALID_FAN
)
50 printk(BIOS_ERR
, "%s %s %d!\n", msg_err_invalid
, get_msg(err
), fan
);
52 printk(BIOS_ERR
, "%s Fan %d %s!\n", msg_err_invalid
, fan
, get_msg(err
));
56 static int message_invalid_2(int err
, u8 fan
)
59 case HWM_STATUS_INVALID_BOUNDARY_VALUE
:
60 printk(BIOS_ERR
, "%s fan %d %s value!\n", msg_err_invalid
, fan
,
63 case HWM_STATUS_INVALID_SECTION_VALUE
:
64 printk(BIOS_ERR
, "%s fan %d %s value!\n", msg_err_invalid
, fan
,
67 case HWM_STATUS_BOUNDARY_WRONG_ORDER
:
68 printk(BIOS_ERR
, "%s fan %d %s!\n", msg_err_wrong_order
, fan
, msg_err_bondary
);
70 case HWM_STATUS_SECTIONS_WRONG_ORDER
:
71 printk(BIOS_ERR
, "%s fan %d %s!\n", msg_err_wrong_order
, fan
, msg_err_section
);
79 static void write_hwm_reg(u16 address
, u8 index
, u8 value
)
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
);
88 static u8
read_hwm_reg(u16 address
, u8 index
)
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
);
97 static void hwm_reg_modify(u16 address
, u8 index
, u8 shift
, u8 mask
,
100 u8 use_mask
= mask
<< shift
;
101 u8 use_value
= (value
& mask
) << shift
;
102 u8 temp
= read_hwm_reg(address
, index
);
106 write_hwm_reg(address
, index
, temp
);
110 * Registers 0x94,0x95, 0x96 and 0x9b have 2 versions (banks) selected through
111 * bit 7 of register 0x9f.
113 static inline void select_hwm_bank(u16 address
, u8 value
)
115 hwm_reg_modify(address
, FAN_FAULT_TIME_REG
, FAN_FUNC_PROG_SEL_SHIFT
,
116 FAN_BIT_MASK
, value
);
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
124 static int check_value_seq(u8
*values
, u8 count
)
126 u8 last_value
= CPU_DAMAGE_TEMP
;
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
;
136 return HWM_STATUS_SUCCESS
;
139 int set_sensor_type(u16 base_address
, external_sensor sensor
,
140 temp_sensor_type type
)
142 u8 sensor_status
= read_hwm_reg(base_address
, TP_DIODE_STATUS
);
144 printk(BIOS_DEBUG
, "%s\n", __func__
);
146 case EXTERNAL_SENSOR1
:
147 if (sensor_status
& TP_EXTERNAL_SENSOR1_OPEN
) {
148 printk(BIOS_WARNING
, "Sensor 1 disconnected!\n");
149 return HWM_STATUS_WARNING_SENSOR_DISCONNECTED
;
151 hwm_reg_modify(base_address
, TP_SENSOR_TYPE
,
152 TP_SENSOR1_TYPE_SHIFT
, TP_SENSOR_TYPE_MASK
, type
);
154 case EXTERNAL_SENSOR2
:
155 if (sensor_status
& TP_EXTERNAL_SENSOR2_OPEN
) {
156 printk(BIOS_WARNING
, "Sensor 2 disconnected!\n");
157 return HWM_STATUS_WARNING_SENSOR_DISCONNECTED
;
159 hwm_reg_modify(base_address
, TP_SENSOR_TYPE
,
160 TP_SENSOR2_TYPE_SHIFT
, TP_SENSOR_TYPE_MASK
, type
);
165 return message_invalid_1(HWM_STATUS_INVALID_TEMP_SENSOR
, 0);
167 return HWM_STATUS_SUCCESS
;
170 int set_fan_temperature_source(u16 base_address
, u8 fan
,
171 fan_temp_source source
)
173 u8 index
, high_value
, low_value
;
175 printk(BIOS_DEBUG
, "%s\n", __func__
);
176 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
177 return message_invalid_1(HWM_STATUS_INVALID_FAN
, 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
);
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
194 if (fan
== FIRST_FAN
) {
195 select_hwm_bank(base_address
, 1);
196 hwm_reg_modify(base_address
, FAN_MODE_REG
,
197 FAN1_ADJ_SEL_SHIFT
, FAN1_ADJ_SEL_MASK
, source
);
198 select_hwm_bank(base_address
, 0);
200 return HWM_STATUS_SUCCESS
;
203 int set_fan_type_mode(u16 base_address
, u8 fan
, fan_type type
, fan_mode mode
)
207 printk(BIOS_DEBUG
, "%s\n", __func__
);
208 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
209 return message_invalid_1(HWM_STATUS_INVALID_FAN
, 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
,
214 FAN_TYPE_MASK
, type
);
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
);
221 return HWM_STATUS_SUCCESS
;
224 int set_pwm_frequency(u16 base_address
, u8 fan
, fan_pwm_freq frequency
)
226 u8 shift
, index
, byte
;
228 printk(BIOS_DEBUG
, "%s\n", __func__
);
229 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
230 return message_invalid_1(HWM_STATUS_INVALID_FAN
, 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
);
235 return HWM_STATUS_WARNING_FAN_NOT_PWM
;
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
,
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
,
247 return HWM_STATUS_SUCCESS
;
250 int set_sections(u16 base_address
, u8 fan
, u8
*boundaries
, u8
*sections
)
255 printk(BIOS_DEBUG
, "%s\n", __func__
);
256 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
257 return message_invalid_1(HWM_STATUS_INVALID_FAN
, fan
);
258 status
= check_value_seq(boundaries
,
259 FINTEK_BOUNDARIES_SIZE
);
260 if (status
!= HWM_STATUS_SUCCESS
) {
261 if (status
== STATUS_INVALID_VALUE
)
262 return message_invalid_2(HWM_STATUS_INVALID_BOUNDARY_VALUE
, fan
);
263 return message_invalid_2(HWM_STATUS_BOUNDARY_WRONG_ORDER
, fan
);
265 status
= check_value_seq(sections
,
266 FINTEK_SECTIONS_SIZE
);
267 if (status
!= HWM_STATUS_SUCCESS
) {
268 if (status
== STATUS_INVALID_VALUE
)
269 return message_invalid_2(HWM_STATUS_INVALID_SECTION_VALUE
, fan
);
270 return message_invalid_2(HWM_STATUS_SECTIONS_WRONG_ORDER
, fan
);
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
);
278 index
= FAN_ADJUST(fan
, FAN_SECTION_SPEED
);
279 for (i
= 0; i
< FINTEK_SECTIONS_SIZE
; i
++) {
282 return message_invalid_2(HWM_STATUS_INVALID_SECTION_VALUE
, fan
);
283 temp
= (255 * value
) / 100;
284 value
= (u8
) (temp
& 0x00ff);
285 write_hwm_reg(base_address
, index
, value
);
288 return HWM_STATUS_SUCCESS
;
291 int set_fan_speed_change_rate(u16 base_address
, u8 fan
, fan_rate_up rate_up
,
292 fan_rate_down rate_down
)
296 printk(BIOS_DEBUG
, "%s\n", __func__
);
297 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
298 return message_invalid_1(HWM_STATUS_INVALID_FAN
, fan
);
300 index
= FAN_ADJUST(fan
, FAN_TMP_MAPPING
);
301 shift
= FAN_RATE_SHIFT(fan
);
303 if (rate_up
== FAN_UP_RATE_JUMP
) {
304 hwm_reg_modify(base_address
, index
, FAN_JUMP_UP_SHIFT
,
307 hwm_reg_modify(base_address
, index
, FAN_JUMP_UP_SHIFT
,
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
);
315 if (rate_down
== FAN_DOWN_RATE_JUMP
) {
316 hwm_reg_modify(base_address
, index
, FAN_JUMP_DOWN_SHIFT
,
319 hwm_reg_modify(base_address
, index
, FAN_JUMP_UP_SHIFT
,
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
,
326 FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT
,
329 if (rate_down
== FAN_DOWN_RATE_SAME_AS_UP
) {
330 hwm_reg_modify(base_address
, FAN_DOWN_RATE_REG
,
331 FAN_DOWN_RATE_DIFF_FROM_UP_SHIFT
,
335 return HWM_STATUS_SUCCESS
;
338 int set_fan_follow(u16 base_address
, u8 fan
, fan_follow follow
)
342 printk(BIOS_DEBUG
, "%s\n", __func__
);
343 if ((fan
< FIRST_FAN
) || (fan
> LAST_FAN
))
344 return message_invalid_1(HWM_STATUS_INVALID_FAN
, 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
;