Skip to content

Commit ea197ea

Browse files
spandruvadarafaeljw
authored andcommitted
thermal: intel: int340x_thermal: New IOCTLs for Passive v2 table
Export Passive version 2 table similar to the way _TRT and _ART tables via IOCTLs. This removes need for binary utility to read ACPI Passive 2 table by providing open source support. This table already has open source implementation in the user space thermald, when the table is part of data vault exported by the int3400 sysfs. This table is supported in some older platforms before Ice Lake generation. Passive 2 tables contain multiple entries. Each entry has following fields: * Source: Named Reference (String). This is the source device for temperature. * Target: Named Reference (String). This is the target device to control. * Priority: Priority of this device compared to others. * SamplingPeriod: Time Period in 1/10 of seconds unit. * PassiveTemp: Passive Temperature in 1/10 of Kelvin. * SourceDomain: Domain for the source (00:Processor, others reserved). * ControlKnob: Type of control knob (00:Power Limit 1, others: reserved) * Limit: The target state to set on reaching passive temperature. This can be a string "max", "min" or a power limit value. * LimitStepSize: Step size during activation. * UnLimitStepSize: Step size during deactivation. * Reserved1: Reserved Three IOCTLs are added similar to IOCTLs for reading TRT: ACPI_THERMAL_GET_PSVT_COUNT: Number of passive 2 entries. ACPI_THERMAL_GET_PSVT_LEN: Total return data size (count x each passive 2 entry size). ACPI_THERMAL_GET_PSVT: Get the data as an array of objects with passive 2 entries. This change is based on original development done by: Todd Brandt <[email protected]> Signed-off-by: Srinivas Pandruvada <[email protected]> [ rjw: Changelog and subject edits ] Signed-off-by: Rafael J. Wysocki <[email protected]>
1 parent 858fd16 commit ea197ea

File tree

2 files changed

+275
-0
lines changed

2 files changed

+275
-0
lines changed

drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,151 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp,
203203
}
204204
EXPORT_SYMBOL(acpi_parse_art);
205205

206+
/*
207+
* acpi_parse_psvt - Passive Table (PSVT) for passive cooling
208+
*
209+
* @handle: ACPI handle of the device which contains PSVT
210+
* @psvt_count: the number of valid entries resulted from parsing PSVT
211+
* @psvtp: pointer to array of psvt entries
212+
*
213+
*/
214+
static int acpi_parse_psvt(acpi_handle handle, int *psvt_count, struct psvt **psvtp)
215+
{
216+
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
217+
int nr_bad_entries = 0, revision = 0;
218+
union acpi_object *p;
219+
acpi_status status;
220+
int i, result = 0;
221+
struct psvt *psvts;
222+
223+
if (!acpi_has_method(handle, "PSVT"))
224+
return -ENODEV;
225+
226+
status = acpi_evaluate_object(handle, "PSVT", NULL, &buffer);
227+
if (ACPI_FAILURE(status))
228+
return -ENODEV;
229+
230+
p = buffer.pointer;
231+
if (!p || (p->type != ACPI_TYPE_PACKAGE)) {
232+
result = -EFAULT;
233+
goto end;
234+
}
235+
236+
/* first package is the revision number */
237+
if (p->package.count > 0) {
238+
union acpi_object *prev = &(p->package.elements[0]);
239+
240+
if (prev->type == ACPI_TYPE_INTEGER)
241+
revision = (int)prev->integer.value;
242+
} else {
243+
result = -EFAULT;
244+
goto end;
245+
}
246+
247+
/* Support only version 2 */
248+
if (revision != 2) {
249+
result = -EFAULT;
250+
goto end;
251+
}
252+
253+
*psvt_count = p->package.count - 1;
254+
if (!*psvt_count) {
255+
result = -EFAULT;
256+
goto end;
257+
}
258+
259+
psvts = kcalloc(*psvt_count, sizeof(*psvts), GFP_KERNEL);
260+
if (!psvts) {
261+
result = -ENOMEM;
262+
goto end;
263+
}
264+
265+
/* Start index is 1 because the first package is the revision number */
266+
for (i = 1; i < p->package.count; i++) {
267+
struct acpi_buffer psvt_int_format = { sizeof("RRNNNNNNNNNN"), "RRNNNNNNNNNN" };
268+
struct acpi_buffer psvt_str_format = { sizeof("RRNNNNNSNNNN"), "RRNNNNNSNNNN" };
269+
union acpi_object *package = &(p->package.elements[i]);
270+
struct psvt *psvt = &psvts[i - 1 - nr_bad_entries];
271+
struct acpi_buffer *psvt_format = &psvt_int_format;
272+
struct acpi_buffer element = { 0, NULL };
273+
union acpi_object *knob;
274+
struct acpi_device *res;
275+
struct psvt *psvt_ptr;
276+
277+
element.length = ACPI_ALLOCATE_BUFFER;
278+
element.pointer = NULL;
279+
280+
if (package->package.count >= ACPI_NR_PSVT_ELEMENTS) {
281+
knob = &(package->package.elements[ACPI_PSVT_CONTROL_KNOB]);
282+
} else {
283+
nr_bad_entries++;
284+
pr_info("PSVT package %d is invalid, ignored\n", i);
285+
continue;
286+
}
287+
288+
if (knob->type == ACPI_TYPE_STRING) {
289+
psvt_format = &psvt_str_format;
290+
if (knob->string.length > ACPI_LIMIT_STR_MAX_LEN - 1) {
291+
pr_info("PSVT package %d limit string len exceeds max\n", i);
292+
knob->string.length = ACPI_LIMIT_STR_MAX_LEN - 1;
293+
}
294+
}
295+
296+
status = acpi_extract_package(&(p->package.elements[i]), psvt_format, &element);
297+
if (ACPI_FAILURE(status)) {
298+
nr_bad_entries++;
299+
pr_info("PSVT package %d is invalid, ignored\n", i);
300+
continue;
301+
}
302+
303+
psvt_ptr = (struct psvt *)element.pointer;
304+
305+
memcpy(psvt, psvt_ptr, sizeof(*psvt));
306+
307+
/* The limit element can be string or U64 */
308+
psvt->control_knob_type = (u64)knob->type;
309+
310+
if (knob->type == ACPI_TYPE_STRING) {
311+
memset(&psvt->limit, 0, sizeof(u64));
312+
strncpy(psvt->limit.string, psvt_ptr->limit.str_ptr, knob->string.length);
313+
} else {
314+
psvt->limit.integer = psvt_ptr->limit.integer;
315+
}
316+
317+
kfree(element.pointer);
318+
319+
res = acpi_fetch_acpi_dev(psvt->source);
320+
if (!res) {
321+
nr_bad_entries++;
322+
pr_info("Failed to get source ACPI device\n");
323+
continue;
324+
}
325+
326+
res = acpi_fetch_acpi_dev(psvt->target);
327+
if (!res) {
328+
nr_bad_entries++;
329+
pr_info("Failed to get target ACPI device\n");
330+
continue;
331+
}
332+
}
333+
334+
/* don't count bad entries */
335+
*psvt_count -= nr_bad_entries;
336+
337+
if (!*psvt_count) {
338+
result = -EFAULT;
339+
kfree(psvts);
340+
goto end;
341+
}
342+
343+
*psvtp = psvts;
344+
345+
return 0;
346+
347+
end:
348+
kfree(buffer.pointer);
349+
return result;
350+
}
206351

207352
/* get device name from acpi handle */
208353
static void get_single_name(acpi_handle handle, char *name)
@@ -289,6 +434,57 @@ static int fill_trt(char __user *ubuf)
289434
return ret;
290435
}
291436

437+
static int fill_psvt(char __user *ubuf)
438+
{
439+
int i, ret, count, psvt_len;
440+
union psvt_object *psvt_user;
441+
struct psvt *psvts;
442+
443+
ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
444+
if (ret)
445+
return ret;
446+
447+
psvt_len = count * sizeof(*psvt_user);
448+
449+
psvt_user = kzalloc(psvt_len, GFP_KERNEL);
450+
if (!psvt_user) {
451+
ret = -ENOMEM;
452+
goto free_psvt;
453+
}
454+
455+
/* now fill in user psvt data */
456+
for (i = 0; i < count; i++) {
457+
/* userspace psvt needs device name instead of acpi reference */
458+
get_single_name(psvts[i].source, psvt_user[i].source_device);
459+
get_single_name(psvts[i].target, psvt_user[i].target_device);
460+
461+
psvt_user[i].priority = psvts[i].priority;
462+
psvt_user[i].sample_period = psvts[i].sample_period;
463+
psvt_user[i].passive_temp = psvts[i].passive_temp;
464+
psvt_user[i].source_domain = psvts[i].source_domain;
465+
psvt_user[i].control_knob = psvts[i].control_knob;
466+
psvt_user[i].step_size = psvts[i].step_size;
467+
psvt_user[i].limit_coeff = psvts[i].limit_coeff;
468+
psvt_user[i].unlimit_coeff = psvts[i].unlimit_coeff;
469+
psvt_user[i].control_knob_type = psvts[i].control_knob_type;
470+
if (psvt_user[i].control_knob_type == ACPI_TYPE_STRING)
471+
strncpy(psvt_user[i].limit.string, psvts[i].limit.string,
472+
ACPI_LIMIT_STR_MAX_LEN);
473+
else
474+
psvt_user[i].limit.integer = psvts[i].limit.integer;
475+
476+
}
477+
478+
if (copy_to_user(ubuf, psvt_user, psvt_len))
479+
ret = -EFAULT;
480+
481+
kfree(psvt_user);
482+
483+
free_psvt:
484+
kfree(psvts);
485+
return ret;
486+
}
487+
292488
static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
293489
unsigned long __arg)
294490
{
@@ -298,6 +494,7 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
298494
char __user *arg = (void __user *)__arg;
299495
struct trt *trts = NULL;
300496
struct art *arts = NULL;
497+
struct psvt *psvts;
301498

302499
switch (cmd) {
303500
case ACPI_THERMAL_GET_TRT_COUNT:
@@ -336,6 +533,27 @@ static long acpi_thermal_rel_ioctl(struct file *f, unsigned int cmd,
336533
case ACPI_THERMAL_GET_ART:
337534
return fill_art(arg);
338535

536+
case ACPI_THERMAL_GET_PSVT_COUNT:
537+
ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
538+
if (!ret) {
539+
kfree(psvts);
540+
return put_user(count, (unsigned long __user *)__arg);
541+
}
542+
return ret;
543+
544+
case ACPI_THERMAL_GET_PSVT_LEN:
545+
/* total length of the data retrieved (count * PSVT entry size) */
546+
ret = acpi_parse_psvt(acpi_thermal_rel_handle, &count, &psvts);
547+
length = count * sizeof(union psvt_object);
548+
if (!ret) {
549+
kfree(psvts);
550+
return put_user(length, (unsigned long __user *)__arg);
551+
}
552+
return ret;
553+
554+
case ACPI_THERMAL_GET_PSVT:
555+
return fill_psvt(arg);
556+
339557
default:
340558
return -ENOTTY;
341559
}

drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,16 @@
1414
#define ACPI_THERMAL_GET_TRT _IOR(ACPI_THERMAL_MAGIC, 5, unsigned long)
1515
#define ACPI_THERMAL_GET_ART _IOR(ACPI_THERMAL_MAGIC, 6, unsigned long)
1616

17+
/*
18+
* ACPI_THERMAL_GET_PSVT_COUNT = Number of PSVT entries
19+
* ACPI_THERMAL_GET_PSVT_LEN = Total return data size (PSVT count x each
20+
* PSVT entry size)
21+
* ACPI_THERMAL_GET_PSVT = Get the data as an array of psvt_objects
22+
*/
23+
#define ACPI_THERMAL_GET_PSVT_LEN _IOR(ACPI_THERMAL_MAGIC, 7, unsigned long)
24+
#define ACPI_THERMAL_GET_PSVT_COUNT _IOR(ACPI_THERMAL_MAGIC, 8, unsigned long)
25+
#define ACPI_THERMAL_GET_PSVT _IOR(ACPI_THERMAL_MAGIC, 9, unsigned long)
26+
1727
struct art {
1828
acpi_handle source;
1929
acpi_handle target;
@@ -43,6 +53,32 @@ struct trt {
4353
u64 reserved4;
4454
} __packed;
4555

56+
#define ACPI_NR_PSVT_ELEMENTS 12
57+
#define ACPI_PSVT_CONTROL_KNOB 7
58+
#define ACPI_LIMIT_STR_MAX_LEN 8
59+
60+
struct psvt {
61+
acpi_handle source;
62+
acpi_handle target;
63+
u64 priority;
64+
u64 sample_period;
65+
u64 passive_temp;
66+
u64 source_domain;
67+
u64 control_knob;
68+
union {
69+
/* For limit_type = ACPI_TYPE_INTEGER */
70+
u64 integer;
71+
/* For limit_type = ACPI_TYPE_STRING */
72+
char string[ACPI_LIMIT_STR_MAX_LEN];
73+
char *str_ptr;
74+
} limit;
75+
u64 step_size;
76+
u64 limit_coeff;
77+
u64 unlimit_coeff;
78+
/* Spec calls this field reserved, so we borrow it for type info */
79+
u64 control_knob_type; /* ACPI_TYPE_STRING or ACPI_TYPE_INTEGER */
80+
} __packed;
81+
4682
#define ACPI_NR_ART_ELEMENTS 13
4783
/* for usrspace */
4884
union art_object {
@@ -77,6 +113,27 @@ union trt_object {
77113
u64 __data[8];
78114
};
79115

116+
union psvt_object {
117+
struct {
118+
char source_device[8];
119+
char target_device[8];
120+
u64 priority;
121+
u64 sample_period;
122+
u64 passive_temp;
123+
u64 source_domain;
124+
u64 control_knob;
125+
union {
126+
u64 integer;
127+
char string[ACPI_LIMIT_STR_MAX_LEN];
128+
} limit;
129+
u64 step_size;
130+
u64 limit_coeff;
131+
u64 unlimit_coeff;
132+
u64 control_knob_type;
133+
};
134+
u64 __data[ACPI_NR_PSVT_ELEMENTS];
135+
};
136+
80137
#ifdef __KERNEL__
81138
int acpi_thermal_rel_misc_device_add(acpi_handle handle);
82139
int acpi_thermal_rel_misc_device_remove(acpi_handle handle);

0 commit comments

Comments
 (0)