1 /* SPDX-License-Identifier: GPL-2.0-only */
3 #include <acpi/acpigen.h>
4 #include <console/console.h>
5 #include <device/device.h>
6 #include <device/mmio.h>
13 static bool is_curve_valid(struct ec_clevo_it5570e_fan_curve curve
)
16 * Fan curve speeds have to be non-decreasing.
17 * Fan curve temperatures have to be increasing (to avoid division by 0).
18 * This also covers the case when the curve is all zeroes (i.e. not configured).
21 for (int i
= 1; i
< IT5570E_FAN_CURVE_LEN
; i
++) {
22 if (curve
.speed
[i
] < curve
.speed
[i
- 1] ||
23 curve
.temperature
[i
] <= curve
.temperature
[i
- 1])
30 static void write_fan_curve(struct ec_clevo_it5570e_fan_curve curve
, int fan
)
36 for (int i
= 0; i
< IT5570E_FAN_CURVE_LEN
; i
++) {
37 snprintf(fieldname
, 5, "F%dT%d", fan
+ 1, i
+ 1);
38 acpigen_write_store_int_to_namestr(curve
.temperature
[i
], fieldname
);
39 snprintf(fieldname
, 5, "F%dD%d", fan
+ 1, i
+ 1);
40 acpigen_write_store_int_to_namestr(curve
.speed
[i
] * 255 / 100, fieldname
);
44 for (int i
= 0; i
< (IT5570E_FAN_CURVE_LEN
- 1); i
++) {
46 (curve
.speed
[i
+ 1] - curve
.speed
[i
]) /
47 (curve
.temperature
[i
+ 1] - curve
.temperature
[i
]) /
50 snprintf(fieldname
, 5, "F%dR%d", fan
+ 1, i
+ 1);
51 acpigen_write_store_int_to_namestr(ramp
, fieldname
);
55 static void write_fan_opregion(int fan_cnt
)
58 uint8_t flags
= FIELD_ANYACC
| FIELD_LOCK
| FIELD_PRESERVE
;
59 struct opregion opreg
= {
61 .regionspace
= SYSTEMMEMORY
,
62 .regionoffset
= CONFIG_EC_CLEVO_IT5570E_MEM_BASE
+ 0x38c,
63 .regionlen
= fan_cnt
* 14,
66 acpigen_write_opregion(&opreg
);
67 acpigen_emit_ext_op(FIELD_OP
);
68 acpigen_write_len_f();
69 acpigen_emit_namestring(opreg
.name
);
70 acpigen_emit_byte(flags
);
72 for (int fan
= 1; fan
<= fan_cnt
; fan
++) {
74 for (int i
= 1; i
<= IT5570E_FAN_CURVE_LEN
; i
++) {
75 snprintf(fieldname
, 5, "F%dT%d", fan
, i
);
76 acpigen_write_field_name(fieldname
, 8);
80 for (int i
= 1; i
<= IT5570E_FAN_CURVE_LEN
; i
++) {
81 snprintf(fieldname
, 5, "F%dD%d", fan
, i
);
82 acpigen_write_field_name(fieldname
, 8);
86 for (int i
= 1; i
< IT5570E_FAN_CURVE_LEN
; i
++) {
87 snprintf(fieldname
, 5, "F%dR%d", fan
, i
);
88 acpigen_write_field_name(fieldname
, 16);
92 acpigen_pop_len(); /* Field */
97 * The function must exist even if the fan curve isn't enabled in devicetree.
99 void ec_fan_curve_fill_ssdt(const struct device
*dev
)
101 const ec_config_t
*config
= config_of(dev
);
102 const int fan_cnt
= read8p(ECRAM
+ FANC
);
104 acpigen_write_scope(acpi_device_path(dev
));
105 write_fan_opregion(fan_cnt
);
106 acpigen_write_method("SFCV", 0);
108 if (config
->fan_mode
== FAN_MODE_CUSTOM
) {
111 /* Check curve count against fan count from EC */
112 for (int i
= 0; i
< IT5570E_MAX_FAN_CNT
; i
++)
113 if (*config
->fan_curves
[i
].speed
&& *config
->fan_curves
[i
].temperature
)
116 if (curve_cnt
!= fan_cnt
) {
118 "EC: Fan curve count (%d) does not match fan count (%d). "
119 "Check your devicetree!\n", curve_cnt
, fan_cnt
);
125 * Custom mode can only be enabled for all fans or none. Thus, all
126 * custom curves must be valid before custom mode can be enabled.
129 for (int i
= 0; i
< fan_cnt
; i
++) {
130 if (!is_curve_valid(config
->fan_curves
[i
])) {
132 "EC: Fan %d curve invalid. Check your devicetree!\n", i
);
139 acpigen_write_debug_string("EC: Apply custom fan curve");
141 for (int i
= 0; i
< fan_cnt
; i
++)
142 write_fan_curve(config
->fan_curves
[i
], i
);
144 /* Enable custom fan mode */
145 acpigen_write_store_int_to_namestr(0x04, "FDAT");
146 acpigen_emit_namestring("SFCC");
147 acpigen_write_integer(0xd7);
151 acpigen_pop_len(); /* Method */
152 acpigen_pop_len(); /* Scope */