Commit | Line | Data |
---|---|---|
e2f05d60 EJ |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // Copyright IBM Corp 2019 | |
5b5513b8 EJ |
3 | |
4 | #include <linux/device.h> | |
5679ed99 | 5 | #include <linux/export.h> |
54076cb3 | 6 | #include <linux/hwmon.h> |
c10e753d EJ |
7 | #include <linux/hwmon-sysfs.h> |
8 | #include <linux/jiffies.h> | |
aa195fe4 | 9 | #include <linux/kernel.h> |
c10e753d | 10 | #include <linux/math64.h> |
5679ed99 | 11 | #include <linux/module.h> |
c10e753d | 12 | #include <linux/mutex.h> |
54076cb3 | 13 | #include <linux/sysfs.h> |
c10e753d | 14 | #include <asm/unaligned.h> |
5b5513b8 EJ |
15 | |
16 | #include "common.h" | |
17 | ||
c10e753d EJ |
18 | #define EXTN_FLAG_SENSOR_ID BIT(7) |
19 | ||
df04ced6 EJ |
20 | #define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */ |
21 | ||
22 | #define OCC_STATE_SAFE 4 | |
23 | #define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */ | |
24 | ||
c10e753d EJ |
25 | #define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000) |
26 | ||
27 | #define OCC_TEMP_SENSOR_FAULT 0xFF | |
28 | ||
29 | #define OCC_FRU_TYPE_VRM 3 | |
30 | ||
31 | /* OCC sensor type and version definitions */ | |
32 | ||
33 | struct temp_sensor_1 { | |
34 | u16 sensor_id; | |
35 | u16 value; | |
36 | } __packed; | |
37 | ||
38 | struct temp_sensor_2 { | |
39 | u32 sensor_id; | |
40 | u8 fru_type; | |
41 | u8 value; | |
42 | } __packed; | |
43 | ||
db4919ec EJ |
44 | struct temp_sensor_10 { |
45 | u32 sensor_id; | |
46 | u8 fru_type; | |
47 | u8 value; | |
48 | u8 throttle; | |
49 | u8 reserved; | |
50 | } __packed; | |
51 | ||
c10e753d EJ |
52 | struct freq_sensor_1 { |
53 | u16 sensor_id; | |
54 | u16 value; | |
55 | } __packed; | |
56 | ||
57 | struct freq_sensor_2 { | |
58 | u32 sensor_id; | |
59 | u16 value; | |
60 | } __packed; | |
61 | ||
62 | struct power_sensor_1 { | |
63 | u16 sensor_id; | |
64 | u32 update_tag; | |
65 | u32 accumulator; | |
66 | u16 value; | |
67 | } __packed; | |
68 | ||
69 | struct power_sensor_2 { | |
70 | u32 sensor_id; | |
71 | u8 function_id; | |
72 | u8 apss_channel; | |
73 | u16 reserved; | |
74 | u32 update_tag; | |
75 | u64 accumulator; | |
76 | u16 value; | |
77 | } __packed; | |
78 | ||
79 | struct power_sensor_data { | |
80 | u16 value; | |
81 | u32 update_tag; | |
82 | u64 accumulator; | |
83 | } __packed; | |
84 | ||
85 | struct power_sensor_data_and_time { | |
86 | u16 update_time; | |
87 | u16 value; | |
88 | u32 update_tag; | |
89 | u64 accumulator; | |
90 | } __packed; | |
91 | ||
92 | struct power_sensor_a0 { | |
93 | u32 sensor_id; | |
94 | struct power_sensor_data_and_time system; | |
95 | u32 reserved; | |
96 | struct power_sensor_data_and_time proc; | |
97 | struct power_sensor_data vdd; | |
98 | struct power_sensor_data vdn; | |
99 | } __packed; | |
100 | ||
101 | struct caps_sensor_2 { | |
102 | u16 cap; | |
103 | u16 system_power; | |
104 | u16 n_cap; | |
105 | u16 max; | |
106 | u16 min; | |
107 | u16 user; | |
108 | u8 user_source; | |
109 | } __packed; | |
110 | ||
111 | struct caps_sensor_3 { | |
112 | u16 cap; | |
113 | u16 system_power; | |
114 | u16 n_cap; | |
115 | u16 max; | |
116 | u16 hard_min; | |
117 | u16 soft_min; | |
118 | u16 user; | |
119 | u8 user_source; | |
120 | } __packed; | |
121 | ||
122 | struct extended_sensor { | |
123 | union { | |
124 | u8 name[4]; | |
125 | u32 sensor_id; | |
126 | }; | |
127 | u8 flags; | |
128 | u8 reserved; | |
129 | u8 data[6]; | |
130 | } __packed; | |
131 | ||
5b5513b8 EJ |
132 | static int occ_poll(struct occ *occ) |
133 | { | |
df04ced6 | 134 | int rc; |
908dbf02 | 135 | u8 cmd[7]; |
df04ced6 | 136 | struct occ_poll_response_header *header; |
5b5513b8 EJ |
137 | |
138 | /* big endian */ | |
908dbf02 | 139 | cmd[0] = 0; /* sequence number */ |
5b5513b8 EJ |
140 | cmd[1] = 0; /* cmd type */ |
141 | cmd[2] = 0; /* data length msb */ | |
142 | cmd[3] = 1; /* data length lsb */ | |
143 | cmd[4] = occ->poll_cmd_data; /* data */ | |
908dbf02 EJ |
144 | cmd[5] = 0; /* checksum msb */ |
145 | cmd[6] = 0; /* checksum lsb */ | |
5b5513b8 | 146 | |
c10e753d | 147 | /* mutex should already be locked if necessary */ |
1bbb2809 | 148 | rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp)); |
df04ced6 | 149 | if (rc) { |
b5c46a53 | 150 | occ->last_error = rc; |
df04ced6 EJ |
151 | if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD) |
152 | occ->error = rc; | |
153 | ||
154 | goto done; | |
155 | } | |
156 | ||
157 | /* clear error since communication was successful */ | |
158 | occ->error_count = 0; | |
b5c46a53 | 159 | occ->last_error = 0; |
df04ced6 EJ |
160 | occ->error = 0; |
161 | ||
162 | /* check for safe state */ | |
163 | header = (struct occ_poll_response_header *)occ->resp.data; | |
164 | if (header->occ_state == OCC_STATE_SAFE) { | |
165 | if (occ->last_safe) { | |
166 | if (time_after(jiffies, | |
167 | occ->last_safe + OCC_SAFE_TIMEOUT)) | |
168 | occ->error = -EHOSTDOWN; | |
169 | } else { | |
170 | occ->last_safe = jiffies; | |
171 | } | |
172 | } else { | |
173 | occ->last_safe = 0; | |
174 | } | |
175 | ||
176 | done: | |
177 | occ_sysfs_poll_done(occ); | |
178 | return rc; | |
5b5513b8 EJ |
179 | } |
180 | ||
c10e753d EJ |
181 | static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap) |
182 | { | |
183 | int rc; | |
184 | u8 cmd[8]; | |
1bbb2809 | 185 | u8 resp[8]; |
c10e753d EJ |
186 | __be16 user_power_cap_be = cpu_to_be16(user_power_cap); |
187 | ||
908dbf02 EJ |
188 | cmd[0] = 0; /* sequence number */ |
189 | cmd[1] = 0x22; /* cmd type */ | |
190 | cmd[2] = 0; /* data length msb */ | |
191 | cmd[3] = 2; /* data length lsb */ | |
c10e753d EJ |
192 | |
193 | memcpy(&cmd[4], &user_power_cap_be, 2); | |
194 | ||
908dbf02 EJ |
195 | cmd[6] = 0; /* checksum msb */ |
196 | cmd[7] = 0; /* checksum lsb */ | |
c10e753d EJ |
197 | |
198 | rc = mutex_lock_interruptible(&occ->lock); | |
199 | if (rc) | |
200 | return rc; | |
201 | ||
1bbb2809 | 202 | rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp)); |
c10e753d EJ |
203 | |
204 | mutex_unlock(&occ->lock); | |
205 | ||
206 | return rc; | |
207 | } | |
208 | ||
df04ced6 | 209 | int occ_update_response(struct occ *occ) |
c10e753d EJ |
210 | { |
211 | int rc = mutex_lock_interruptible(&occ->lock); | |
212 | ||
213 | if (rc) | |
214 | return rc; | |
215 | ||
216 | /* limit the maximum rate of polling the OCC */ | |
5216dff2 | 217 | if (time_after(jiffies, occ->next_update)) { |
c10e753d | 218 | rc = occ_poll(occ); |
5216dff2 | 219 | occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; |
b5c46a53 EJ |
220 | } else { |
221 | rc = occ->last_error; | |
c10e753d EJ |
222 | } |
223 | ||
224 | mutex_unlock(&occ->lock); | |
225 | return rc; | |
226 | } | |
227 | ||
228 | static ssize_t occ_show_temp_1(struct device *dev, | |
229 | struct device_attribute *attr, char *buf) | |
230 | { | |
231 | int rc; | |
232 | u32 val = 0; | |
233 | struct temp_sensor_1 *temp; | |
234 | struct occ *occ = dev_get_drvdata(dev); | |
235 | struct occ_sensors *sensors = &occ->sensors; | |
236 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
237 | ||
238 | rc = occ_update_response(occ); | |
239 | if (rc) | |
240 | return rc; | |
241 | ||
242 | temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index; | |
243 | ||
244 | switch (sattr->nr) { | |
245 | case 0: | |
246 | val = get_unaligned_be16(&temp->sensor_id); | |
247 | break; | |
248 | case 1: | |
80830342 AS |
249 | /* |
250 | * If a sensor reading has expired and couldn't be refreshed, | |
251 | * OCC returns 0xFFFF for that sensor. | |
252 | */ | |
253 | if (temp->value == 0xFFFF) | |
254 | return -EREMOTEIO; | |
c10e753d EJ |
255 | val = get_unaligned_be16(&temp->value) * 1000; |
256 | break; | |
257 | default: | |
258 | return -EINVAL; | |
259 | } | |
260 | ||
1f4d4af4 | 261 | return sysfs_emit(buf, "%u\n", val); |
c10e753d EJ |
262 | } |
263 | ||
264 | static ssize_t occ_show_temp_2(struct device *dev, | |
265 | struct device_attribute *attr, char *buf) | |
266 | { | |
267 | int rc; | |
268 | u32 val = 0; | |
269 | struct temp_sensor_2 *temp; | |
270 | struct occ *occ = dev_get_drvdata(dev); | |
271 | struct occ_sensors *sensors = &occ->sensors; | |
272 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
273 | ||
274 | rc = occ_update_response(occ); | |
275 | if (rc) | |
276 | return rc; | |
277 | ||
278 | temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index; | |
279 | ||
280 | switch (sattr->nr) { | |
281 | case 0: | |
282 | val = get_unaligned_be32(&temp->sensor_id); | |
283 | break; | |
284 | case 1: | |
285 | val = temp->value; | |
286 | if (val == OCC_TEMP_SENSOR_FAULT) | |
287 | return -EREMOTEIO; | |
288 | ||
289 | /* | |
290 | * VRM doesn't return temperature, only alarm bit. This | |
291 | * attribute maps to tempX_alarm instead of tempX_input for | |
292 | * VRM | |
293 | */ | |
294 | if (temp->fru_type != OCC_FRU_TYPE_VRM) { | |
295 | /* sensor not ready */ | |
296 | if (val == 0) | |
297 | return -EAGAIN; | |
298 | ||
299 | val *= 1000; | |
300 | } | |
301 | break; | |
302 | case 2: | |
303 | val = temp->fru_type; | |
304 | break; | |
305 | case 3: | |
306 | val = temp->value == OCC_TEMP_SENSOR_FAULT; | |
307 | break; | |
308 | default: | |
309 | return -EINVAL; | |
310 | } | |
311 | ||
1f4d4af4 | 312 | return sysfs_emit(buf, "%u\n", val); |
c10e753d EJ |
313 | } |
314 | ||
db4919ec EJ |
315 | static ssize_t occ_show_temp_10(struct device *dev, |
316 | struct device_attribute *attr, char *buf) | |
317 | { | |
318 | int rc; | |
319 | u32 val = 0; | |
320 | struct temp_sensor_10 *temp; | |
321 | struct occ *occ = dev_get_drvdata(dev); | |
322 | struct occ_sensors *sensors = &occ->sensors; | |
323 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
324 | ||
325 | rc = occ_update_response(occ); | |
326 | if (rc) | |
327 | return rc; | |
328 | ||
329 | temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index; | |
330 | ||
331 | switch (sattr->nr) { | |
332 | case 0: | |
333 | val = get_unaligned_be32(&temp->sensor_id); | |
334 | break; | |
335 | case 1: | |
336 | val = temp->value; | |
337 | if (val == OCC_TEMP_SENSOR_FAULT) | |
338 | return -EREMOTEIO; | |
339 | ||
ffa26000 EJ |
340 | /* sensor not ready */ |
341 | if (val == 0) | |
342 | return -EAGAIN; | |
db4919ec | 343 | |
ffa26000 | 344 | val *= 1000; |
db4919ec EJ |
345 | break; |
346 | case 2: | |
347 | val = temp->fru_type; | |
348 | break; | |
349 | case 3: | |
350 | val = temp->value == OCC_TEMP_SENSOR_FAULT; | |
351 | break; | |
352 | case 4: | |
353 | val = temp->throttle * 1000; | |
354 | break; | |
355 | default: | |
356 | return -EINVAL; | |
357 | } | |
358 | ||
1f4d4af4 | 359 | return sysfs_emit(buf, "%u\n", val); |
db4919ec EJ |
360 | } |
361 | ||
c10e753d EJ |
362 | static ssize_t occ_show_freq_1(struct device *dev, |
363 | struct device_attribute *attr, char *buf) | |
364 | { | |
365 | int rc; | |
366 | u16 val = 0; | |
367 | struct freq_sensor_1 *freq; | |
368 | struct occ *occ = dev_get_drvdata(dev); | |
369 | struct occ_sensors *sensors = &occ->sensors; | |
370 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
371 | ||
372 | rc = occ_update_response(occ); | |
373 | if (rc) | |
374 | return rc; | |
375 | ||
376 | freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index; | |
377 | ||
378 | switch (sattr->nr) { | |
379 | case 0: | |
380 | val = get_unaligned_be16(&freq->sensor_id); | |
381 | break; | |
382 | case 1: | |
383 | val = get_unaligned_be16(&freq->value); | |
384 | break; | |
385 | default: | |
386 | return -EINVAL; | |
387 | } | |
388 | ||
1f4d4af4 | 389 | return sysfs_emit(buf, "%u\n", val); |
c10e753d EJ |
390 | } |
391 | ||
392 | static ssize_t occ_show_freq_2(struct device *dev, | |
393 | struct device_attribute *attr, char *buf) | |
394 | { | |
395 | int rc; | |
396 | u32 val = 0; | |
397 | struct freq_sensor_2 *freq; | |
398 | struct occ *occ = dev_get_drvdata(dev); | |
399 | struct occ_sensors *sensors = &occ->sensors; | |
400 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
401 | ||
402 | rc = occ_update_response(occ); | |
403 | if (rc) | |
404 | return rc; | |
405 | ||
406 | freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index; | |
407 | ||
408 | switch (sattr->nr) { | |
409 | case 0: | |
410 | val = get_unaligned_be32(&freq->sensor_id); | |
411 | break; | |
412 | case 1: | |
413 | val = get_unaligned_be16(&freq->value); | |
414 | break; | |
415 | default: | |
416 | return -EINVAL; | |
417 | } | |
418 | ||
1f4d4af4 | 419 | return sysfs_emit(buf, "%u\n", val); |
c10e753d EJ |
420 | } |
421 | ||
422 | static ssize_t occ_show_power_1(struct device *dev, | |
423 | struct device_attribute *attr, char *buf) | |
424 | { | |
425 | int rc; | |
426 | u64 val = 0; | |
427 | struct power_sensor_1 *power; | |
428 | struct occ *occ = dev_get_drvdata(dev); | |
429 | struct occ_sensors *sensors = &occ->sensors; | |
430 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
431 | ||
432 | rc = occ_update_response(occ); | |
433 | if (rc) | |
434 | return rc; | |
435 | ||
436 | power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index; | |
437 | ||
438 | switch (sattr->nr) { | |
439 | case 0: | |
440 | val = get_unaligned_be16(&power->sensor_id); | |
441 | break; | |
442 | case 1: | |
443 | val = get_unaligned_be32(&power->accumulator) / | |
444 | get_unaligned_be32(&power->update_tag); | |
445 | val *= 1000000ULL; | |
446 | break; | |
447 | case 2: | |
b0407d82 GS |
448 | val = (u64)get_unaligned_be32(&power->update_tag) * |
449 | occ->powr_sample_time_us; | |
c10e753d EJ |
450 | break; |
451 | case 3: | |
452 | val = get_unaligned_be16(&power->value) * 1000000ULL; | |
453 | break; | |
454 | default: | |
455 | return -EINVAL; | |
456 | } | |
457 | ||
1f4d4af4 | 458 | return sysfs_emit(buf, "%llu\n", val); |
c10e753d EJ |
459 | } |
460 | ||
461 | static u64 occ_get_powr_avg(u64 *accum, u32 *samples) | |
462 | { | |
211186ca LY |
463 | u64 divisor = get_unaligned_be32(samples); |
464 | ||
465 | return (divisor == 0) ? 0 : | |
466 | div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor); | |
c10e753d EJ |
467 | } |
468 | ||
469 | static ssize_t occ_show_power_2(struct device *dev, | |
470 | struct device_attribute *attr, char *buf) | |
471 | { | |
472 | int rc; | |
473 | u64 val = 0; | |
474 | struct power_sensor_2 *power; | |
475 | struct occ *occ = dev_get_drvdata(dev); | |
476 | struct occ_sensors *sensors = &occ->sensors; | |
477 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
478 | ||
479 | rc = occ_update_response(occ); | |
480 | if (rc) | |
481 | return rc; | |
482 | ||
483 | power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index; | |
484 | ||
485 | switch (sattr->nr) { | |
486 | case 0: | |
1f4d4af4 GR |
487 | return sysfs_emit(buf, "%u_%u_%u\n", |
488 | get_unaligned_be32(&power->sensor_id), | |
489 | power->function_id, power->apss_channel); | |
c10e753d EJ |
490 | case 1: |
491 | val = occ_get_powr_avg(&power->accumulator, | |
492 | &power->update_tag); | |
493 | break; | |
494 | case 2: | |
b0407d82 GS |
495 | val = (u64)get_unaligned_be32(&power->update_tag) * |
496 | occ->powr_sample_time_us; | |
c10e753d EJ |
497 | break; |
498 | case 3: | |
499 | val = get_unaligned_be16(&power->value) * 1000000ULL; | |
500 | break; | |
501 | default: | |
502 | return -EINVAL; | |
503 | } | |
504 | ||
1f4d4af4 | 505 | return sysfs_emit(buf, "%llu\n", val); |
c10e753d EJ |
506 | } |
507 | ||
508 | static ssize_t occ_show_power_a0(struct device *dev, | |
509 | struct device_attribute *attr, char *buf) | |
510 | { | |
511 | int rc; | |
512 | u64 val = 0; | |
513 | struct power_sensor_a0 *power; | |
514 | struct occ *occ = dev_get_drvdata(dev); | |
515 | struct occ_sensors *sensors = &occ->sensors; | |
516 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
517 | ||
518 | rc = occ_update_response(occ); | |
519 | if (rc) | |
520 | return rc; | |
521 | ||
522 | power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index; | |
523 | ||
524 | switch (sattr->nr) { | |
525 | case 0: | |
1f4d4af4 GR |
526 | return sysfs_emit(buf, "%u_system\n", |
527 | get_unaligned_be32(&power->sensor_id)); | |
c10e753d EJ |
528 | case 1: |
529 | val = occ_get_powr_avg(&power->system.accumulator, | |
530 | &power->system.update_tag); | |
531 | break; | |
532 | case 2: | |
b0407d82 GS |
533 | val = (u64)get_unaligned_be32(&power->system.update_tag) * |
534 | occ->powr_sample_time_us; | |
c10e753d EJ |
535 | break; |
536 | case 3: | |
537 | val = get_unaligned_be16(&power->system.value) * 1000000ULL; | |
538 | break; | |
539 | case 4: | |
1f4d4af4 GR |
540 | return sysfs_emit(buf, "%u_proc\n", |
541 | get_unaligned_be32(&power->sensor_id)); | |
c10e753d EJ |
542 | case 5: |
543 | val = occ_get_powr_avg(&power->proc.accumulator, | |
544 | &power->proc.update_tag); | |
545 | break; | |
546 | case 6: | |
b0407d82 GS |
547 | val = (u64)get_unaligned_be32(&power->proc.update_tag) * |
548 | occ->powr_sample_time_us; | |
c10e753d EJ |
549 | break; |
550 | case 7: | |
551 | val = get_unaligned_be16(&power->proc.value) * 1000000ULL; | |
552 | break; | |
553 | case 8: | |
1f4d4af4 GR |
554 | return sysfs_emit(buf, "%u_vdd\n", |
555 | get_unaligned_be32(&power->sensor_id)); | |
c10e753d EJ |
556 | case 9: |
557 | val = occ_get_powr_avg(&power->vdd.accumulator, | |
558 | &power->vdd.update_tag); | |
559 | break; | |
560 | case 10: | |
b0407d82 GS |
561 | val = (u64)get_unaligned_be32(&power->vdd.update_tag) * |
562 | occ->powr_sample_time_us; | |
c10e753d EJ |
563 | break; |
564 | case 11: | |
565 | val = get_unaligned_be16(&power->vdd.value) * 1000000ULL; | |
566 | break; | |
567 | case 12: | |
1f4d4af4 GR |
568 | return sysfs_emit(buf, "%u_vdn\n", |
569 | get_unaligned_be32(&power->sensor_id)); | |
c10e753d EJ |
570 | case 13: |
571 | val = occ_get_powr_avg(&power->vdn.accumulator, | |
572 | &power->vdn.update_tag); | |
573 | break; | |
574 | case 14: | |
b0407d82 GS |
575 | val = (u64)get_unaligned_be32(&power->vdn.update_tag) * |
576 | occ->powr_sample_time_us; | |
c10e753d EJ |
577 | break; |
578 | case 15: | |
579 | val = get_unaligned_be16(&power->vdn.value) * 1000000ULL; | |
580 | break; | |
581 | default: | |
582 | return -EINVAL; | |
583 | } | |
584 | ||
1f4d4af4 | 585 | return sysfs_emit(buf, "%llu\n", val); |
c10e753d EJ |
586 | } |
587 | ||
588 | static ssize_t occ_show_caps_1_2(struct device *dev, | |
589 | struct device_attribute *attr, char *buf) | |
590 | { | |
591 | int rc; | |
592 | u64 val = 0; | |
593 | struct caps_sensor_2 *caps; | |
594 | struct occ *occ = dev_get_drvdata(dev); | |
595 | struct occ_sensors *sensors = &occ->sensors; | |
596 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
597 | ||
598 | rc = occ_update_response(occ); | |
599 | if (rc) | |
600 | return rc; | |
601 | ||
602 | caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index; | |
603 | ||
604 | switch (sattr->nr) { | |
605 | case 0: | |
1f4d4af4 | 606 | return sysfs_emit(buf, "system\n"); |
c10e753d EJ |
607 | case 1: |
608 | val = get_unaligned_be16(&caps->cap) * 1000000ULL; | |
609 | break; | |
610 | case 2: | |
611 | val = get_unaligned_be16(&caps->system_power) * 1000000ULL; | |
612 | break; | |
613 | case 3: | |
614 | val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; | |
615 | break; | |
616 | case 4: | |
617 | val = get_unaligned_be16(&caps->max) * 1000000ULL; | |
618 | break; | |
619 | case 5: | |
620 | val = get_unaligned_be16(&caps->min) * 1000000ULL; | |
621 | break; | |
622 | case 6: | |
623 | val = get_unaligned_be16(&caps->user) * 1000000ULL; | |
624 | break; | |
625 | case 7: | |
626 | if (occ->sensors.caps.version == 1) | |
627 | return -EINVAL; | |
628 | ||
629 | val = caps->user_source; | |
630 | break; | |
631 | default: | |
632 | return -EINVAL; | |
633 | } | |
634 | ||
1f4d4af4 | 635 | return sysfs_emit(buf, "%llu\n", val); |
c10e753d EJ |
636 | } |
637 | ||
638 | static ssize_t occ_show_caps_3(struct device *dev, | |
639 | struct device_attribute *attr, char *buf) | |
640 | { | |
641 | int rc; | |
642 | u64 val = 0; | |
643 | struct caps_sensor_3 *caps; | |
644 | struct occ *occ = dev_get_drvdata(dev); | |
645 | struct occ_sensors *sensors = &occ->sensors; | |
646 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
647 | ||
648 | rc = occ_update_response(occ); | |
649 | if (rc) | |
650 | return rc; | |
651 | ||
652 | caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index; | |
653 | ||
654 | switch (sattr->nr) { | |
655 | case 0: | |
1f4d4af4 | 656 | return sysfs_emit(buf, "system\n"); |
c10e753d EJ |
657 | case 1: |
658 | val = get_unaligned_be16(&caps->cap) * 1000000ULL; | |
659 | break; | |
660 | case 2: | |
661 | val = get_unaligned_be16(&caps->system_power) * 1000000ULL; | |
662 | break; | |
663 | case 3: | |
664 | val = get_unaligned_be16(&caps->n_cap) * 1000000ULL; | |
665 | break; | |
666 | case 4: | |
667 | val = get_unaligned_be16(&caps->max) * 1000000ULL; | |
668 | break; | |
669 | case 5: | |
670 | val = get_unaligned_be16(&caps->hard_min) * 1000000ULL; | |
671 | break; | |
672 | case 6: | |
673 | val = get_unaligned_be16(&caps->user) * 1000000ULL; | |
674 | break; | |
675 | case 7: | |
676 | val = caps->user_source; | |
677 | break; | |
84dc9e8a EJ |
678 | case 8: |
679 | val = get_unaligned_be16(&caps->soft_min) * 1000000ULL; | |
680 | break; | |
c10e753d EJ |
681 | default: |
682 | return -EINVAL; | |
683 | } | |
684 | ||
1f4d4af4 | 685 | return sysfs_emit(buf, "%llu\n", val); |
c10e753d EJ |
686 | } |
687 | ||
688 | static ssize_t occ_store_caps_user(struct device *dev, | |
689 | struct device_attribute *attr, | |
690 | const char *buf, size_t count) | |
691 | { | |
692 | int rc; | |
693 | u16 user_power_cap; | |
694 | unsigned long long value; | |
695 | struct occ *occ = dev_get_drvdata(dev); | |
696 | ||
697 | rc = kstrtoull(buf, 0, &value); | |
698 | if (rc) | |
699 | return rc; | |
700 | ||
701 | user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */ | |
702 | ||
703 | rc = occ_set_user_power_cap(occ, user_power_cap); | |
704 | if (rc) | |
705 | return rc; | |
706 | ||
707 | return count; | |
708 | } | |
709 | ||
710 | static ssize_t occ_show_extended(struct device *dev, | |
711 | struct device_attribute *attr, char *buf) | |
712 | { | |
713 | int rc; | |
714 | struct extended_sensor *extn; | |
715 | struct occ *occ = dev_get_drvdata(dev); | |
716 | struct occ_sensors *sensors = &occ->sensors; | |
717 | struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); | |
718 | ||
719 | rc = occ_update_response(occ); | |
720 | if (rc) | |
721 | return rc; | |
722 | ||
723 | extn = ((struct extended_sensor *)sensors->extended.data) + | |
724 | sattr->index; | |
725 | ||
726 | switch (sattr->nr) { | |
727 | case 0: | |
1f4d4af4 GR |
728 | if (extn->flags & EXTN_FLAG_SENSOR_ID) { |
729 | rc = sysfs_emit(buf, "%u", | |
730 | get_unaligned_be32(&extn->sensor_id)); | |
731 | } else { | |
732 | rc = sysfs_emit(buf, "%02x%02x%02x%02x\n", | |
733 | extn->name[0], extn->name[1], | |
734 | extn->name[2], extn->name[3]); | |
735 | } | |
c10e753d EJ |
736 | break; |
737 | case 1: | |
1f4d4af4 | 738 | rc = sysfs_emit(buf, "%02x\n", extn->flags); |
c10e753d EJ |
739 | break; |
740 | case 2: | |
1f4d4af4 GR |
741 | rc = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", |
742 | extn->data[0], extn->data[1], extn->data[2], | |
743 | extn->data[3], extn->data[4], extn->data[5]); | |
c10e753d EJ |
744 | break; |
745 | default: | |
746 | return -EINVAL; | |
747 | } | |
748 | ||
749 | return rc; | |
750 | } | |
751 | ||
54076cb3 EJ |
752 | /* |
753 | * Some helper macros to make it easier to define an occ_attribute. Since these | |
754 | * are dynamically allocated, we shouldn't use the existing kernel macros which | |
755 | * stringify the name argument. | |
756 | */ | |
757 | #define ATTR_OCC(_name, _mode, _show, _store) { \ | |
758 | .attr = { \ | |
759 | .name = _name, \ | |
760 | .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \ | |
761 | }, \ | |
762 | .show = _show, \ | |
763 | .store = _store, \ | |
764 | } | |
765 | ||
766 | #define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \ | |
767 | .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \ | |
768 | .index = _index, \ | |
769 | .nr = _nr, \ | |
770 | } | |
771 | ||
772 | #define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \ | |
773 | ((struct sensor_device_attribute_2) \ | |
774 | SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index)) | |
775 | ||
776 | /* | |
777 | * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to | |
778 | * use our own instead of the built-in hwmon attribute types. | |
779 | */ | |
780 | static int occ_setup_sensor_attrs(struct occ *occ) | |
781 | { | |
782 | unsigned int i, s, num_attrs = 0; | |
783 | struct device *dev = occ->bus_dev; | |
784 | struct occ_sensors *sensors = &occ->sensors; | |
785 | struct occ_attribute *attr; | |
786 | struct temp_sensor_2 *temp; | |
787 | ssize_t (*show_temp)(struct device *, struct device_attribute *, | |
788 | char *) = occ_show_temp_1; | |
789 | ssize_t (*show_freq)(struct device *, struct device_attribute *, | |
790 | char *) = occ_show_freq_1; | |
791 | ssize_t (*show_power)(struct device *, struct device_attribute *, | |
792 | char *) = occ_show_power_1; | |
793 | ssize_t (*show_caps)(struct device *, struct device_attribute *, | |
794 | char *) = occ_show_caps_1_2; | |
795 | ||
796 | switch (sensors->temp.version) { | |
797 | case 1: | |
798 | num_attrs += (sensors->temp.num_sensors * 2); | |
799 | break; | |
800 | case 2: | |
801 | num_attrs += (sensors->temp.num_sensors * 4); | |
802 | show_temp = occ_show_temp_2; | |
803 | break; | |
db4919ec EJ |
804 | case 0x10: |
805 | num_attrs += (sensors->temp.num_sensors * 5); | |
806 | show_temp = occ_show_temp_10; | |
807 | break; | |
54076cb3 EJ |
808 | default: |
809 | sensors->temp.num_sensors = 0; | |
810 | } | |
811 | ||
812 | switch (sensors->freq.version) { | |
813 | case 2: | |
814 | show_freq = occ_show_freq_2; | |
df561f66 | 815 | fallthrough; |
54076cb3 EJ |
816 | case 1: |
817 | num_attrs += (sensors->freq.num_sensors * 2); | |
818 | break; | |
819 | default: | |
820 | sensors->freq.num_sensors = 0; | |
821 | } | |
822 | ||
823 | switch (sensors->power.version) { | |
824 | case 2: | |
825 | show_power = occ_show_power_2; | |
df561f66 | 826 | fallthrough; |
54076cb3 EJ |
827 | case 1: |
828 | num_attrs += (sensors->power.num_sensors * 4); | |
829 | break; | |
830 | case 0xA0: | |
831 | num_attrs += (sensors->power.num_sensors * 16); | |
832 | show_power = occ_show_power_a0; | |
833 | break; | |
834 | default: | |
835 | sensors->power.num_sensors = 0; | |
836 | } | |
837 | ||
838 | switch (sensors->caps.version) { | |
839 | case 1: | |
840 | num_attrs += (sensors->caps.num_sensors * 7); | |
841 | break; | |
54076cb3 EJ |
842 | case 2: |
843 | num_attrs += (sensors->caps.num_sensors * 8); | |
844 | break; | |
84dc9e8a EJ |
845 | case 3: |
846 | show_caps = occ_show_caps_3; | |
847 | num_attrs += (sensors->caps.num_sensors * 9); | |
848 | break; | |
54076cb3 EJ |
849 | default: |
850 | sensors->caps.num_sensors = 0; | |
851 | } | |
852 | ||
853 | switch (sensors->extended.version) { | |
854 | case 1: | |
855 | num_attrs += (sensors->extended.num_sensors * 3); | |
856 | break; | |
857 | default: | |
858 | sensors->extended.num_sensors = 0; | |
859 | } | |
860 | ||
861 | occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs, | |
862 | GFP_KERNEL); | |
863 | if (!occ->attrs) | |
864 | return -ENOMEM; | |
865 | ||
866 | /* null-terminated list */ | |
867 | occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) * | |
868 | num_attrs + 1, GFP_KERNEL); | |
869 | if (!occ->group.attrs) | |
870 | return -ENOMEM; | |
871 | ||
872 | attr = occ->attrs; | |
873 | ||
874 | for (i = 0; i < sensors->temp.num_sensors; ++i) { | |
875 | s = i + 1; | |
876 | temp = ((struct temp_sensor_2 *)sensors->temp.data) + i; | |
877 | ||
878 | snprintf(attr->name, sizeof(attr->name), "temp%d_label", s); | |
879 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, | |
880 | 0, i); | |
881 | attr++; | |
882 | ||
ffa26000 | 883 | if (sensors->temp.version == 2 && |
54076cb3 EJ |
884 | temp->fru_type == OCC_FRU_TYPE_VRM) { |
885 | snprintf(attr->name, sizeof(attr->name), | |
886 | "temp%d_alarm", s); | |
887 | } else { | |
888 | snprintf(attr->name, sizeof(attr->name), | |
889 | "temp%d_input", s); | |
890 | } | |
891 | ||
892 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL, | |
893 | 1, i); | |
894 | attr++; | |
895 | ||
896 | if (sensors->temp.version > 1) { | |
897 | snprintf(attr->name, sizeof(attr->name), | |
898 | "temp%d_fru_type", s); | |
899 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
900 | show_temp, NULL, 2, i); | |
901 | attr++; | |
902 | ||
903 | snprintf(attr->name, sizeof(attr->name), | |
904 | "temp%d_fault", s); | |
905 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
906 | show_temp, NULL, 3, i); | |
907 | attr++; | |
db4919ec EJ |
908 | |
909 | if (sensors->temp.version == 0x10) { | |
910 | snprintf(attr->name, sizeof(attr->name), | |
911 | "temp%d_max", s); | |
912 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
913 | show_temp, NULL, | |
914 | 4, i); | |
915 | attr++; | |
916 | } | |
54076cb3 EJ |
917 | } |
918 | } | |
919 | ||
920 | for (i = 0; i < sensors->freq.num_sensors; ++i) { | |
921 | s = i + 1; | |
922 | ||
923 | snprintf(attr->name, sizeof(attr->name), "freq%d_label", s); | |
924 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, | |
925 | 0, i); | |
926 | attr++; | |
927 | ||
928 | snprintf(attr->name, sizeof(attr->name), "freq%d_input", s); | |
929 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL, | |
930 | 1, i); | |
931 | attr++; | |
932 | } | |
933 | ||
934 | if (sensors->power.version == 0xA0) { | |
935 | /* | |
936 | * Special case for many-attribute power sensor. Split it into | |
937 | * a sensor number per power type, emulating several sensors. | |
938 | */ | |
939 | for (i = 0; i < sensors->power.num_sensors; ++i) { | |
940 | unsigned int j; | |
941 | unsigned int nr = 0; | |
942 | ||
943 | s = (i * 4) + 1; | |
944 | ||
945 | for (j = 0; j < 4; ++j) { | |
946 | snprintf(attr->name, sizeof(attr->name), | |
947 | "power%d_label", s); | |
948 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
949 | show_power, NULL, | |
950 | nr++, i); | |
951 | attr++; | |
952 | ||
953 | snprintf(attr->name, sizeof(attr->name), | |
954 | "power%d_average", s); | |
955 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
956 | show_power, NULL, | |
957 | nr++, i); | |
958 | attr++; | |
959 | ||
960 | snprintf(attr->name, sizeof(attr->name), | |
961 | "power%d_average_interval", s); | |
962 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
963 | show_power, NULL, | |
964 | nr++, i); | |
965 | attr++; | |
966 | ||
967 | snprintf(attr->name, sizeof(attr->name), | |
968 | "power%d_input", s); | |
969 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
970 | show_power, NULL, | |
971 | nr++, i); | |
972 | attr++; | |
973 | ||
974 | s++; | |
975 | } | |
976 | } | |
8e6af454 EJ |
977 | |
978 | s = (sensors->power.num_sensors * 4) + 1; | |
54076cb3 EJ |
979 | } else { |
980 | for (i = 0; i < sensors->power.num_sensors; ++i) { | |
981 | s = i + 1; | |
982 | ||
983 | snprintf(attr->name, sizeof(attr->name), | |
984 | "power%d_label", s); | |
985 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
986 | show_power, NULL, 0, i); | |
987 | attr++; | |
988 | ||
989 | snprintf(attr->name, sizeof(attr->name), | |
990 | "power%d_average", s); | |
991 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
992 | show_power, NULL, 1, i); | |
993 | attr++; | |
994 | ||
995 | snprintf(attr->name, sizeof(attr->name), | |
996 | "power%d_average_interval", s); | |
997 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
998 | show_power, NULL, 2, i); | |
999 | attr++; | |
1000 | ||
1001 | snprintf(attr->name, sizeof(attr->name), | |
1002 | "power%d_input", s); | |
1003 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1004 | show_power, NULL, 3, i); | |
1005 | attr++; | |
1006 | } | |
54076cb3 | 1007 | |
54076cb3 | 1008 | s = sensors->power.num_sensors + 1; |
8e6af454 | 1009 | } |
54076cb3 | 1010 | |
8e6af454 | 1011 | if (sensors->caps.num_sensors >= 1) { |
54076cb3 EJ |
1012 | snprintf(attr->name, sizeof(attr->name), "power%d_label", s); |
1013 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1014 | 0, 0); | |
1015 | attr++; | |
1016 | ||
1017 | snprintf(attr->name, sizeof(attr->name), "power%d_cap", s); | |
1018 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1019 | 1, 0); | |
1020 | attr++; | |
1021 | ||
1022 | snprintf(attr->name, sizeof(attr->name), "power%d_input", s); | |
1023 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1024 | 2, 0); | |
1025 | attr++; | |
1026 | ||
1027 | snprintf(attr->name, sizeof(attr->name), | |
1028 | "power%d_cap_not_redundant", s); | |
1029 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1030 | 3, 0); | |
1031 | attr++; | |
1032 | ||
1033 | snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s); | |
1034 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1035 | 4, 0); | |
1036 | attr++; | |
1037 | ||
1038 | snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s); | |
1039 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL, | |
1040 | 5, 0); | |
1041 | attr++; | |
1042 | ||
1043 | snprintf(attr->name, sizeof(attr->name), "power%d_cap_user", | |
1044 | s); | |
1045 | attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps, | |
1046 | occ_store_caps_user, 6, 0); | |
1047 | attr++; | |
1048 | ||
1049 | if (sensors->caps.version > 1) { | |
1050 | snprintf(attr->name, sizeof(attr->name), | |
1051 | "power%d_cap_user_source", s); | |
1052 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1053 | show_caps, NULL, 7, 0); | |
1054 | attr++; | |
84dc9e8a EJ |
1055 | |
1056 | if (sensors->caps.version > 2) { | |
1057 | snprintf(attr->name, sizeof(attr->name), | |
1058 | "power%d_cap_min_soft", s); | |
1059 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1060 | show_caps, NULL, | |
1061 | 8, 0); | |
1062 | attr++; | |
1063 | } | |
54076cb3 EJ |
1064 | } |
1065 | } | |
1066 | ||
1067 | for (i = 0; i < sensors->extended.num_sensors; ++i) { | |
1068 | s = i + 1; | |
1069 | ||
1070 | snprintf(attr->name, sizeof(attr->name), "extn%d_label", s); | |
1071 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1072 | occ_show_extended, NULL, 0, i); | |
1073 | attr++; | |
1074 | ||
1075 | snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s); | |
1076 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1077 | occ_show_extended, NULL, 1, i); | |
1078 | attr++; | |
1079 | ||
1080 | snprintf(attr->name, sizeof(attr->name), "extn%d_input", s); | |
1081 | attr->sensor = OCC_INIT_ATTR(attr->name, 0444, | |
1082 | occ_show_extended, NULL, 2, i); | |
1083 | attr++; | |
1084 | } | |
1085 | ||
1086 | /* put the sensors in the group */ | |
1087 | for (i = 0; i < num_attrs; ++i) { | |
1088 | sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr); | |
1089 | occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr; | |
1090 | } | |
1091 | ||
1092 | return 0; | |
1093 | } | |
1094 | ||
aa195fe4 EJ |
1095 | /* only need to do this once at startup, as OCC won't change sensors on us */ |
1096 | static void occ_parse_poll_response(struct occ *occ) | |
1097 | { | |
1098 | unsigned int i, old_offset, offset = 0, size = 0; | |
1099 | struct occ_sensor *sensor; | |
1100 | struct occ_sensors *sensors = &occ->sensors; | |
1101 | struct occ_response *resp = &occ->resp; | |
1102 | struct occ_poll_response *poll = | |
1103 | (struct occ_poll_response *)&resp->data[0]; | |
1104 | struct occ_poll_response_header *header = &poll->header; | |
1105 | struct occ_sensor_data_block *block = &poll->block; | |
1106 | ||
1107 | dev_info(occ->bus_dev, "OCC found, code level: %.16s\n", | |
1108 | header->occ_code_level); | |
1109 | ||
1110 | for (i = 0; i < header->num_sensor_data_blocks; ++i) { | |
1111 | block = (struct occ_sensor_data_block *)((u8 *)block + offset); | |
1112 | old_offset = offset; | |
1113 | offset = (block->header.num_sensors * | |
1114 | block->header.sensor_length) + sizeof(block->header); | |
1115 | size += offset; | |
1116 | ||
1117 | /* validate all the length/size fields */ | |
1118 | if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) { | |
1119 | dev_warn(occ->bus_dev, "exceeded response buffer\n"); | |
1120 | return; | |
1121 | } | |
1122 | ||
1123 | dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n", | |
1124 | old_offset, offset - 1, block->header.eye_catcher, | |
1125 | block->header.num_sensors); | |
1126 | ||
1127 | /* match sensor block type */ | |
1128 | if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0) | |
1129 | sensor = &sensors->temp; | |
1130 | else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0) | |
1131 | sensor = &sensors->freq; | |
1132 | else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0) | |
1133 | sensor = &sensors->power; | |
1134 | else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0) | |
1135 | sensor = &sensors->caps; | |
1136 | else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0) | |
1137 | sensor = &sensors->extended; | |
1138 | else { | |
1139 | dev_warn(occ->bus_dev, "sensor not supported %.4s\n", | |
1140 | block->header.eye_catcher); | |
1141 | continue; | |
1142 | } | |
1143 | ||
1144 | sensor->num_sensors = block->header.num_sensors; | |
1145 | sensor->version = block->header.sensor_format; | |
1146 | sensor->data = &block->data; | |
1147 | } | |
1148 | ||
1149 | dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size, | |
1150 | sizeof(*header), size + sizeof(*header)); | |
1151 | } | |
1152 | ||
849b0156 | 1153 | int occ_active(struct occ *occ, bool active) |
5b5513b8 | 1154 | { |
849b0156 | 1155 | int rc = mutex_lock_interruptible(&occ->lock); |
c10e753d | 1156 | |
849b0156 | 1157 | if (rc) |
5b5513b8 | 1158 | return rc; |
5b5513b8 | 1159 | |
849b0156 EJ |
1160 | if (active) { |
1161 | if (occ->active) { | |
1162 | rc = -EALREADY; | |
1163 | goto unlock; | |
1164 | } | |
aa195fe4 | 1165 | |
849b0156 EJ |
1166 | occ->error_count = 0; |
1167 | occ->last_safe = 0; | |
54076cb3 | 1168 | |
849b0156 EJ |
1169 | rc = occ_poll(occ); |
1170 | if (rc < 0) { | |
1171 | dev_err(occ->bus_dev, | |
1172 | "failed to get OCC poll response=%02x: %d\n", | |
1173 | occ->resp.return_status, rc); | |
1174 | goto unlock; | |
1175 | } | |
1176 | ||
1177 | occ->active = true; | |
1178 | occ->next_update = jiffies + OCC_UPDATE_FREQUENCY; | |
1179 | occ_parse_poll_response(occ); | |
1180 | ||
1181 | rc = occ_setup_sensor_attrs(occ); | |
1182 | if (rc) { | |
1183 | dev_err(occ->bus_dev, | |
1184 | "failed to setup sensor attrs: %d\n", rc); | |
1185 | goto unlock; | |
1186 | } | |
1187 | ||
1188 | occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev, | |
1189 | "occ", occ, | |
1190 | occ->groups); | |
1191 | if (IS_ERR(occ->hwmon)) { | |
1192 | rc = PTR_ERR(occ->hwmon); | |
1193 | occ->hwmon = NULL; | |
1194 | dev_err(occ->bus_dev, | |
1195 | "failed to register hwmon device: %d\n", rc); | |
1196 | goto unlock; | |
1197 | } | |
1198 | } else { | |
1199 | if (!occ->active) { | |
1200 | rc = -EALREADY; | |
1201 | goto unlock; | |
1202 | } | |
1203 | ||
1204 | if (occ->hwmon) | |
1205 | hwmon_device_unregister(occ->hwmon); | |
1206 | occ->active = false; | |
1207 | occ->hwmon = NULL; | |
54076cb3 EJ |
1208 | } |
1209 | ||
849b0156 EJ |
1210 | unlock: |
1211 | mutex_unlock(&occ->lock); | |
1212 | return rc; | |
1213 | } | |
1214 | ||
1215 | int occ_setup(struct occ *occ) | |
1216 | { | |
1217 | int rc; | |
1218 | ||
1219 | mutex_init(&occ->lock); | |
1220 | occ->groups[0] = &occ->group; | |
1221 | ||
df04ced6 EJ |
1222 | rc = occ_setup_sysfs(occ); |
1223 | if (rc) | |
1224 | dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc); | |
1225 | ||
1226 | return rc; | |
5b5513b8 | 1227 | } |
5679ed99 JD |
1228 | EXPORT_SYMBOL_GPL(occ_setup); |
1229 | ||
849b0156 EJ |
1230 | void occ_shutdown(struct occ *occ) |
1231 | { | |
ac6888ac EJ |
1232 | mutex_lock(&occ->lock); |
1233 | ||
849b0156 EJ |
1234 | occ_shutdown_sysfs(occ); |
1235 | ||
1236 | if (occ->hwmon) | |
1237 | hwmon_device_unregister(occ->hwmon); | |
ac6888ac EJ |
1238 | occ->hwmon = NULL; |
1239 | ||
1240 | mutex_unlock(&occ->lock); | |
849b0156 EJ |
1241 | } |
1242 | EXPORT_SYMBOL_GPL(occ_shutdown); | |
1243 | ||
5679ed99 JD |
1244 | MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>"); |
1245 | MODULE_DESCRIPTION("Common OCC hwmon code"); | |
1246 | MODULE_LICENSE("GPL"); |