1e2f05d60SEddie James // SPDX-License-Identifier: GPL-2.0+
2e2f05d60SEddie James // Copyright IBM Corp 2019
35b5513b8SEddie James
45b5513b8SEddie James #include <linux/device.h>
55679ed99SJean Delvare #include <linux/export.h>
654076cb3SEddie James #include <linux/hwmon.h>
7c10e753dSEddie James #include <linux/hwmon-sysfs.h>
8c10e753dSEddie James #include <linux/jiffies.h>
9aa195fe4SEddie James #include <linux/kernel.h>
10c10e753dSEddie James #include <linux/math64.h>
115679ed99SJean Delvare #include <linux/module.h>
12c10e753dSEddie James #include <linux/mutex.h>
13*89a286fbSEddie James #include <linux/property.h>
1454076cb3SEddie James #include <linux/sysfs.h>
15c10e753dSEddie James #include <asm/unaligned.h>
165b5513b8SEddie James
175b5513b8SEddie James #include "common.h"
185b5513b8SEddie James
19c10e753dSEddie James #define EXTN_FLAG_SENSOR_ID BIT(7)
20c10e753dSEddie James
21df04ced6SEddie James #define OCC_ERROR_COUNT_THRESHOLD 2 /* required by OCC spec */
22df04ced6SEddie James
23df04ced6SEddie James #define OCC_STATE_SAFE 4
24df04ced6SEddie James #define OCC_SAFE_TIMEOUT msecs_to_jiffies(60000) /* 1 min */
25df04ced6SEddie James
26c10e753dSEddie James #define OCC_UPDATE_FREQUENCY msecs_to_jiffies(1000)
27c10e753dSEddie James
28c10e753dSEddie James #define OCC_TEMP_SENSOR_FAULT 0xFF
29c10e753dSEddie James
30c10e753dSEddie James #define OCC_FRU_TYPE_VRM 3
31c10e753dSEddie James
32c10e753dSEddie James /* OCC sensor type and version definitions */
33c10e753dSEddie James
34c10e753dSEddie James struct temp_sensor_1 {
35c10e753dSEddie James u16 sensor_id;
36c10e753dSEddie James u16 value;
37c10e753dSEddie James } __packed;
38c10e753dSEddie James
39c10e753dSEddie James struct temp_sensor_2 {
40c10e753dSEddie James u32 sensor_id;
41c10e753dSEddie James u8 fru_type;
42c10e753dSEddie James u8 value;
43c10e753dSEddie James } __packed;
44c10e753dSEddie James
45db4919ecSEddie James struct temp_sensor_10 {
46db4919ecSEddie James u32 sensor_id;
47db4919ecSEddie James u8 fru_type;
48db4919ecSEddie James u8 value;
49db4919ecSEddie James u8 throttle;
50db4919ecSEddie James u8 reserved;
51db4919ecSEddie James } __packed;
52db4919ecSEddie James
53c10e753dSEddie James struct freq_sensor_1 {
54c10e753dSEddie James u16 sensor_id;
55c10e753dSEddie James u16 value;
56c10e753dSEddie James } __packed;
57c10e753dSEddie James
58c10e753dSEddie James struct freq_sensor_2 {
59c10e753dSEddie James u32 sensor_id;
60c10e753dSEddie James u16 value;
61c10e753dSEddie James } __packed;
62c10e753dSEddie James
63c10e753dSEddie James struct power_sensor_1 {
64c10e753dSEddie James u16 sensor_id;
65c10e753dSEddie James u32 update_tag;
66c10e753dSEddie James u32 accumulator;
67c10e753dSEddie James u16 value;
68c10e753dSEddie James } __packed;
69c10e753dSEddie James
70c10e753dSEddie James struct power_sensor_2 {
71c10e753dSEddie James u32 sensor_id;
72c10e753dSEddie James u8 function_id;
73c10e753dSEddie James u8 apss_channel;
74c10e753dSEddie James u16 reserved;
75c10e753dSEddie James u32 update_tag;
76c10e753dSEddie James u64 accumulator;
77c10e753dSEddie James u16 value;
78c10e753dSEddie James } __packed;
79c10e753dSEddie James
80c10e753dSEddie James struct power_sensor_data {
81c10e753dSEddie James u16 value;
82c10e753dSEddie James u32 update_tag;
83c10e753dSEddie James u64 accumulator;
84c10e753dSEddie James } __packed;
85c10e753dSEddie James
86c10e753dSEddie James struct power_sensor_data_and_time {
87c10e753dSEddie James u16 update_time;
88c10e753dSEddie James u16 value;
89c10e753dSEddie James u32 update_tag;
90c10e753dSEddie James u64 accumulator;
91c10e753dSEddie James } __packed;
92c10e753dSEddie James
93c10e753dSEddie James struct power_sensor_a0 {
94c10e753dSEddie James u32 sensor_id;
95c10e753dSEddie James struct power_sensor_data_and_time system;
96c10e753dSEddie James u32 reserved;
97c10e753dSEddie James struct power_sensor_data_and_time proc;
98c10e753dSEddie James struct power_sensor_data vdd;
99c10e753dSEddie James struct power_sensor_data vdn;
100c10e753dSEddie James } __packed;
101c10e753dSEddie James
102c10e753dSEddie James struct caps_sensor_2 {
103c10e753dSEddie James u16 cap;
104c10e753dSEddie James u16 system_power;
105c10e753dSEddie James u16 n_cap;
106c10e753dSEddie James u16 max;
107c10e753dSEddie James u16 min;
108c10e753dSEddie James u16 user;
109c10e753dSEddie James u8 user_source;
110c10e753dSEddie James } __packed;
111c10e753dSEddie James
112c10e753dSEddie James struct caps_sensor_3 {
113c10e753dSEddie James u16 cap;
114c10e753dSEddie James u16 system_power;
115c10e753dSEddie James u16 n_cap;
116c10e753dSEddie James u16 max;
117c10e753dSEddie James u16 hard_min;
118c10e753dSEddie James u16 soft_min;
119c10e753dSEddie James u16 user;
120c10e753dSEddie James u8 user_source;
121c10e753dSEddie James } __packed;
122c10e753dSEddie James
123c10e753dSEddie James struct extended_sensor {
124c10e753dSEddie James union {
125c10e753dSEddie James u8 name[4];
126c10e753dSEddie James u32 sensor_id;
127c10e753dSEddie James };
128c10e753dSEddie James u8 flags;
129c10e753dSEddie James u8 reserved;
130c10e753dSEddie James u8 data[6];
131c10e753dSEddie James } __packed;
132c10e753dSEddie James
occ_poll(struct occ * occ)1335b5513b8SEddie James static int occ_poll(struct occ *occ)
1345b5513b8SEddie James {
135df04ced6SEddie James int rc;
136908dbf02SEddie James u8 cmd[7];
137df04ced6SEddie James struct occ_poll_response_header *header;
1385b5513b8SEddie James
1395b5513b8SEddie James /* big endian */
140908dbf02SEddie James cmd[0] = 0; /* sequence number */
1415b5513b8SEddie James cmd[1] = 0; /* cmd type */
1425b5513b8SEddie James cmd[2] = 0; /* data length msb */
1435b5513b8SEddie James cmd[3] = 1; /* data length lsb */
1445b5513b8SEddie James cmd[4] = occ->poll_cmd_data; /* data */
145908dbf02SEddie James cmd[5] = 0; /* checksum msb */
146908dbf02SEddie James cmd[6] = 0; /* checksum lsb */
1475b5513b8SEddie James
148c10e753dSEddie James /* mutex should already be locked if necessary */
1491bbb2809SEddie James rc = occ->send_cmd(occ, cmd, sizeof(cmd), &occ->resp, sizeof(occ->resp));
150df04ced6SEddie James if (rc) {
151b5c46a53SEddie James occ->last_error = rc;
152df04ced6SEddie James if (occ->error_count++ > OCC_ERROR_COUNT_THRESHOLD)
153df04ced6SEddie James occ->error = rc;
154df04ced6SEddie James
155df04ced6SEddie James goto done;
156df04ced6SEddie James }
157df04ced6SEddie James
158df04ced6SEddie James /* clear error since communication was successful */
159df04ced6SEddie James occ->error_count = 0;
160b5c46a53SEddie James occ->last_error = 0;
161df04ced6SEddie James occ->error = 0;
162df04ced6SEddie James
163df04ced6SEddie James /* check for safe state */
164df04ced6SEddie James header = (struct occ_poll_response_header *)occ->resp.data;
165df04ced6SEddie James if (header->occ_state == OCC_STATE_SAFE) {
166df04ced6SEddie James if (occ->last_safe) {
167df04ced6SEddie James if (time_after(jiffies,
168df04ced6SEddie James occ->last_safe + OCC_SAFE_TIMEOUT))
169df04ced6SEddie James occ->error = -EHOSTDOWN;
170df04ced6SEddie James } else {
171df04ced6SEddie James occ->last_safe = jiffies;
172df04ced6SEddie James }
173df04ced6SEddie James } else {
174df04ced6SEddie James occ->last_safe = 0;
175df04ced6SEddie James }
176df04ced6SEddie James
177df04ced6SEddie James done:
178df04ced6SEddie James occ_sysfs_poll_done(occ);
179df04ced6SEddie James return rc;
1805b5513b8SEddie James }
1815b5513b8SEddie James
occ_set_user_power_cap(struct occ * occ,u16 user_power_cap)182c10e753dSEddie James static int occ_set_user_power_cap(struct occ *occ, u16 user_power_cap)
183c10e753dSEddie James {
184c10e753dSEddie James int rc;
185c10e753dSEddie James u8 cmd[8];
1861bbb2809SEddie James u8 resp[8];
187c10e753dSEddie James __be16 user_power_cap_be = cpu_to_be16(user_power_cap);
188c10e753dSEddie James
189908dbf02SEddie James cmd[0] = 0; /* sequence number */
190908dbf02SEddie James cmd[1] = 0x22; /* cmd type */
191908dbf02SEddie James cmd[2] = 0; /* data length msb */
192908dbf02SEddie James cmd[3] = 2; /* data length lsb */
193c10e753dSEddie James
194c10e753dSEddie James memcpy(&cmd[4], &user_power_cap_be, 2);
195c10e753dSEddie James
196908dbf02SEddie James cmd[6] = 0; /* checksum msb */
197908dbf02SEddie James cmd[7] = 0; /* checksum lsb */
198c10e753dSEddie James
199c10e753dSEddie James rc = mutex_lock_interruptible(&occ->lock);
200c10e753dSEddie James if (rc)
201c10e753dSEddie James return rc;
202c10e753dSEddie James
2031bbb2809SEddie James rc = occ->send_cmd(occ, cmd, sizeof(cmd), resp, sizeof(resp));
204c10e753dSEddie James
205c10e753dSEddie James mutex_unlock(&occ->lock);
206c10e753dSEddie James
207c10e753dSEddie James return rc;
208c10e753dSEddie James }
209c10e753dSEddie James
occ_update_response(struct occ * occ)210df04ced6SEddie James int occ_update_response(struct occ *occ)
211c10e753dSEddie James {
212c10e753dSEddie James int rc = mutex_lock_interruptible(&occ->lock);
213c10e753dSEddie James
214c10e753dSEddie James if (rc)
215c10e753dSEddie James return rc;
216c10e753dSEddie James
217c10e753dSEddie James /* limit the maximum rate of polling the OCC */
2185216dff2SEddie James if (time_after(jiffies, occ->next_update)) {
219c10e753dSEddie James rc = occ_poll(occ);
2205216dff2SEddie James occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
221b5c46a53SEddie James } else {
222b5c46a53SEddie James rc = occ->last_error;
223c10e753dSEddie James }
224c10e753dSEddie James
225c10e753dSEddie James mutex_unlock(&occ->lock);
226c10e753dSEddie James return rc;
227c10e753dSEddie James }
228c10e753dSEddie James
occ_show_temp_1(struct device * dev,struct device_attribute * attr,char * buf)229c10e753dSEddie James static ssize_t occ_show_temp_1(struct device *dev,
230c10e753dSEddie James struct device_attribute *attr, char *buf)
231c10e753dSEddie James {
232c10e753dSEddie James int rc;
233c10e753dSEddie James u32 val = 0;
234c10e753dSEddie James struct temp_sensor_1 *temp;
235c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
236c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
237c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
238c10e753dSEddie James
239c10e753dSEddie James rc = occ_update_response(occ);
240c10e753dSEddie James if (rc)
241c10e753dSEddie James return rc;
242c10e753dSEddie James
243c10e753dSEddie James temp = ((struct temp_sensor_1 *)sensors->temp.data) + sattr->index;
244c10e753dSEddie James
245c10e753dSEddie James switch (sattr->nr) {
246c10e753dSEddie James case 0:
247c10e753dSEddie James val = get_unaligned_be16(&temp->sensor_id);
248c10e753dSEddie James break;
249c10e753dSEddie James case 1:
25080830342SAlexander Soldatov /*
25180830342SAlexander Soldatov * If a sensor reading has expired and couldn't be refreshed,
25280830342SAlexander Soldatov * OCC returns 0xFFFF for that sensor.
25380830342SAlexander Soldatov */
25480830342SAlexander Soldatov if (temp->value == 0xFFFF)
25580830342SAlexander Soldatov return -EREMOTEIO;
256c10e753dSEddie James val = get_unaligned_be16(&temp->value) * 1000;
257c10e753dSEddie James break;
258c10e753dSEddie James default:
259c10e753dSEddie James return -EINVAL;
260c10e753dSEddie James }
261c10e753dSEddie James
2621f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val);
263c10e753dSEddie James }
264c10e753dSEddie James
occ_show_temp_2(struct device * dev,struct device_attribute * attr,char * buf)265c10e753dSEddie James static ssize_t occ_show_temp_2(struct device *dev,
266c10e753dSEddie James struct device_attribute *attr, char *buf)
267c10e753dSEddie James {
268c10e753dSEddie James int rc;
269c10e753dSEddie James u32 val = 0;
270c10e753dSEddie James struct temp_sensor_2 *temp;
271c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
272c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
273c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
274c10e753dSEddie James
275c10e753dSEddie James rc = occ_update_response(occ);
276c10e753dSEddie James if (rc)
277c10e753dSEddie James return rc;
278c10e753dSEddie James
279c10e753dSEddie James temp = ((struct temp_sensor_2 *)sensors->temp.data) + sattr->index;
280c10e753dSEddie James
281c10e753dSEddie James switch (sattr->nr) {
282c10e753dSEddie James case 0:
283c10e753dSEddie James val = get_unaligned_be32(&temp->sensor_id);
284c10e753dSEddie James break;
285c10e753dSEddie James case 1:
286c10e753dSEddie James val = temp->value;
287c10e753dSEddie James if (val == OCC_TEMP_SENSOR_FAULT)
288c10e753dSEddie James return -EREMOTEIO;
289c10e753dSEddie James
290c10e753dSEddie James /*
291c10e753dSEddie James * VRM doesn't return temperature, only alarm bit. This
292c10e753dSEddie James * attribute maps to tempX_alarm instead of tempX_input for
293c10e753dSEddie James * VRM
294c10e753dSEddie James */
295c10e753dSEddie James if (temp->fru_type != OCC_FRU_TYPE_VRM) {
296c10e753dSEddie James /* sensor not ready */
297c10e753dSEddie James if (val == 0)
298c10e753dSEddie James return -EAGAIN;
299c10e753dSEddie James
300c10e753dSEddie James val *= 1000;
301c10e753dSEddie James }
302c10e753dSEddie James break;
303c10e753dSEddie James case 2:
304c10e753dSEddie James val = temp->fru_type;
305c10e753dSEddie James break;
306c10e753dSEddie James case 3:
307c10e753dSEddie James val = temp->value == OCC_TEMP_SENSOR_FAULT;
308c10e753dSEddie James break;
309c10e753dSEddie James default:
310c10e753dSEddie James return -EINVAL;
311c10e753dSEddie James }
312c10e753dSEddie James
3131f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val);
314c10e753dSEddie James }
315c10e753dSEddie James
occ_show_temp_10(struct device * dev,struct device_attribute * attr,char * buf)316db4919ecSEddie James static ssize_t occ_show_temp_10(struct device *dev,
317db4919ecSEddie James struct device_attribute *attr, char *buf)
318db4919ecSEddie James {
319db4919ecSEddie James int rc;
320db4919ecSEddie James u32 val = 0;
321db4919ecSEddie James struct temp_sensor_10 *temp;
322db4919ecSEddie James struct occ *occ = dev_get_drvdata(dev);
323db4919ecSEddie James struct occ_sensors *sensors = &occ->sensors;
324db4919ecSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
325db4919ecSEddie James
326db4919ecSEddie James rc = occ_update_response(occ);
327db4919ecSEddie James if (rc)
328db4919ecSEddie James return rc;
329db4919ecSEddie James
330db4919ecSEddie James temp = ((struct temp_sensor_10 *)sensors->temp.data) + sattr->index;
331db4919ecSEddie James
332db4919ecSEddie James switch (sattr->nr) {
333db4919ecSEddie James case 0:
334db4919ecSEddie James val = get_unaligned_be32(&temp->sensor_id);
335db4919ecSEddie James break;
336db4919ecSEddie James case 1:
337db4919ecSEddie James val = temp->value;
338db4919ecSEddie James if (val == OCC_TEMP_SENSOR_FAULT)
339db4919ecSEddie James return -EREMOTEIO;
340db4919ecSEddie James
341db4919ecSEddie James /* sensor not ready */
342db4919ecSEddie James if (val == 0)
343db4919ecSEddie James return -EAGAIN;
344db4919ecSEddie James
345db4919ecSEddie James val *= 1000;
346db4919ecSEddie James break;
347db4919ecSEddie James case 2:
348db4919ecSEddie James val = temp->fru_type;
349db4919ecSEddie James break;
350db4919ecSEddie James case 3:
351db4919ecSEddie James val = temp->value == OCC_TEMP_SENSOR_FAULT;
352db4919ecSEddie James break;
353db4919ecSEddie James case 4:
354db4919ecSEddie James val = temp->throttle * 1000;
355db4919ecSEddie James break;
356db4919ecSEddie James default:
357db4919ecSEddie James return -EINVAL;
358db4919ecSEddie James }
359db4919ecSEddie James
3601f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val);
361db4919ecSEddie James }
362db4919ecSEddie James
occ_show_freq_1(struct device * dev,struct device_attribute * attr,char * buf)363c10e753dSEddie James static ssize_t occ_show_freq_1(struct device *dev,
364c10e753dSEddie James struct device_attribute *attr, char *buf)
365c10e753dSEddie James {
366c10e753dSEddie James int rc;
367c10e753dSEddie James u16 val = 0;
368c10e753dSEddie James struct freq_sensor_1 *freq;
369c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
370c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
371c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
372c10e753dSEddie James
373c10e753dSEddie James rc = occ_update_response(occ);
374c10e753dSEddie James if (rc)
375c10e753dSEddie James return rc;
376c10e753dSEddie James
377c10e753dSEddie James freq = ((struct freq_sensor_1 *)sensors->freq.data) + sattr->index;
378c10e753dSEddie James
379c10e753dSEddie James switch (sattr->nr) {
380c10e753dSEddie James case 0:
381c10e753dSEddie James val = get_unaligned_be16(&freq->sensor_id);
382c10e753dSEddie James break;
383c10e753dSEddie James case 1:
384c10e753dSEddie James val = get_unaligned_be16(&freq->value);
385c10e753dSEddie James break;
386c10e753dSEddie James default:
387c10e753dSEddie James return -EINVAL;
388c10e753dSEddie James }
389c10e753dSEddie James
3901f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val);
391c10e753dSEddie James }
392c10e753dSEddie James
occ_show_freq_2(struct device * dev,struct device_attribute * attr,char * buf)393c10e753dSEddie James static ssize_t occ_show_freq_2(struct device *dev,
394c10e753dSEddie James struct device_attribute *attr, char *buf)
395c10e753dSEddie James {
396c10e753dSEddie James int rc;
397c10e753dSEddie James u32 val = 0;
398c10e753dSEddie James struct freq_sensor_2 *freq;
399c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
400c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
401c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
402c10e753dSEddie James
403c10e753dSEddie James rc = occ_update_response(occ);
404c10e753dSEddie James if (rc)
405c10e753dSEddie James return rc;
406c10e753dSEddie James
407c10e753dSEddie James freq = ((struct freq_sensor_2 *)sensors->freq.data) + sattr->index;
408c10e753dSEddie James
409c10e753dSEddie James switch (sattr->nr) {
410c10e753dSEddie James case 0:
411c10e753dSEddie James val = get_unaligned_be32(&freq->sensor_id);
412c10e753dSEddie James break;
413c10e753dSEddie James case 1:
414c10e753dSEddie James val = get_unaligned_be16(&freq->value);
415c10e753dSEddie James break;
416c10e753dSEddie James default:
417c10e753dSEddie James return -EINVAL;
418c10e753dSEddie James }
419c10e753dSEddie James
4201f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u\n", val);
421c10e753dSEddie James }
422c10e753dSEddie James
occ_show_power_1(struct device * dev,struct device_attribute * attr,char * buf)423c10e753dSEddie James static ssize_t occ_show_power_1(struct device *dev,
424c10e753dSEddie James struct device_attribute *attr, char *buf)
425c10e753dSEddie James {
426c10e753dSEddie James int rc;
427c10e753dSEddie James u64 val = 0;
428c10e753dSEddie James struct power_sensor_1 *power;
429c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
430c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
431c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
432c10e753dSEddie James
433c10e753dSEddie James rc = occ_update_response(occ);
434c10e753dSEddie James if (rc)
435c10e753dSEddie James return rc;
436c10e753dSEddie James
437c10e753dSEddie James power = ((struct power_sensor_1 *)sensors->power.data) + sattr->index;
438c10e753dSEddie James
439c10e753dSEddie James switch (sattr->nr) {
440c10e753dSEddie James case 0:
441c10e753dSEddie James val = get_unaligned_be16(&power->sensor_id);
442c10e753dSEddie James break;
443c10e753dSEddie James case 1:
444c10e753dSEddie James val = get_unaligned_be32(&power->accumulator) /
445c10e753dSEddie James get_unaligned_be32(&power->update_tag);
446c10e753dSEddie James val *= 1000000ULL;
447c10e753dSEddie James break;
448c10e753dSEddie James case 2:
449b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->update_tag) *
450c10e753dSEddie James occ->powr_sample_time_us;
451c10e753dSEddie James break;
452c10e753dSEddie James case 3:
453c10e753dSEddie James val = get_unaligned_be16(&power->value) * 1000000ULL;
454c10e753dSEddie James break;
455c10e753dSEddie James default:
456c10e753dSEddie James return -EINVAL;
457c10e753dSEddie James }
458c10e753dSEddie James
4591f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val);
460c10e753dSEddie James }
461c10e753dSEddie James
occ_get_powr_avg(u64 * accum,u32 * samples)462c10e753dSEddie James static u64 occ_get_powr_avg(u64 *accum, u32 *samples)
463c10e753dSEddie James {
464211186caSLei YU u64 divisor = get_unaligned_be32(samples);
465211186caSLei YU
466211186caSLei YU return (divisor == 0) ? 0 :
467211186caSLei YU div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor);
468c10e753dSEddie James }
469c10e753dSEddie James
occ_show_power_2(struct device * dev,struct device_attribute * attr,char * buf)470c10e753dSEddie James static ssize_t occ_show_power_2(struct device *dev,
471c10e753dSEddie James struct device_attribute *attr, char *buf)
472c10e753dSEddie James {
473c10e753dSEddie James int rc;
474c10e753dSEddie James u64 val = 0;
475c10e753dSEddie James struct power_sensor_2 *power;
476c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
477c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
478c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
479c10e753dSEddie James
480c10e753dSEddie James rc = occ_update_response(occ);
481c10e753dSEddie James if (rc)
482c10e753dSEddie James return rc;
483c10e753dSEddie James
484c10e753dSEddie James power = ((struct power_sensor_2 *)sensors->power.data) + sattr->index;
485c10e753dSEddie James
486c10e753dSEddie James switch (sattr->nr) {
487c10e753dSEddie James case 0:
4881f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_%u_%u\n",
489c10e753dSEddie James get_unaligned_be32(&power->sensor_id),
490c10e753dSEddie James power->function_id, power->apss_channel);
491c10e753dSEddie James case 1:
492c10e753dSEddie James val = occ_get_powr_avg(&power->accumulator,
493c10e753dSEddie James &power->update_tag);
494c10e753dSEddie James break;
495c10e753dSEddie James case 2:
496b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->update_tag) *
497c10e753dSEddie James occ->powr_sample_time_us;
498c10e753dSEddie James break;
499c10e753dSEddie James case 3:
500c10e753dSEddie James val = get_unaligned_be16(&power->value) * 1000000ULL;
501c10e753dSEddie James break;
502c10e753dSEddie James default:
503c10e753dSEddie James return -EINVAL;
504c10e753dSEddie James }
505c10e753dSEddie James
5061f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val);
507c10e753dSEddie James }
508c10e753dSEddie James
occ_show_power_a0(struct device * dev,struct device_attribute * attr,char * buf)509c10e753dSEddie James static ssize_t occ_show_power_a0(struct device *dev,
510c10e753dSEddie James struct device_attribute *attr, char *buf)
511c10e753dSEddie James {
512c10e753dSEddie James int rc;
513c10e753dSEddie James u64 val = 0;
514c10e753dSEddie James struct power_sensor_a0 *power;
515c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
516c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
517c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
518c10e753dSEddie James
519c10e753dSEddie James rc = occ_update_response(occ);
520c10e753dSEddie James if (rc)
521c10e753dSEddie James return rc;
522c10e753dSEddie James
523c10e753dSEddie James power = ((struct power_sensor_a0 *)sensors->power.data) + sattr->index;
524c10e753dSEddie James
525c10e753dSEddie James switch (sattr->nr) {
526c10e753dSEddie James case 0:
5271f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_system\n",
528c10e753dSEddie James get_unaligned_be32(&power->sensor_id));
529c10e753dSEddie James case 1:
530c10e753dSEddie James val = occ_get_powr_avg(&power->system.accumulator,
531c10e753dSEddie James &power->system.update_tag);
532c10e753dSEddie James break;
533c10e753dSEddie James case 2:
534b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->system.update_tag) *
535c10e753dSEddie James occ->powr_sample_time_us;
536c10e753dSEddie James break;
537c10e753dSEddie James case 3:
538c10e753dSEddie James val = get_unaligned_be16(&power->system.value) * 1000000ULL;
539c10e753dSEddie James break;
540c10e753dSEddie James case 4:
5411f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_proc\n",
542c10e753dSEddie James get_unaligned_be32(&power->sensor_id));
543c10e753dSEddie James case 5:
544c10e753dSEddie James val = occ_get_powr_avg(&power->proc.accumulator,
545c10e753dSEddie James &power->proc.update_tag);
546c10e753dSEddie James break;
547c10e753dSEddie James case 6:
548b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->proc.update_tag) *
549c10e753dSEddie James occ->powr_sample_time_us;
550c10e753dSEddie James break;
551c10e753dSEddie James case 7:
552c10e753dSEddie James val = get_unaligned_be16(&power->proc.value) * 1000000ULL;
553c10e753dSEddie James break;
554c10e753dSEddie James case 8:
5551f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_vdd\n",
556c10e753dSEddie James get_unaligned_be32(&power->sensor_id));
557c10e753dSEddie James case 9:
558c10e753dSEddie James val = occ_get_powr_avg(&power->vdd.accumulator,
559c10e753dSEddie James &power->vdd.update_tag);
560c10e753dSEddie James break;
561c10e753dSEddie James case 10:
562b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->vdd.update_tag) *
563c10e753dSEddie James occ->powr_sample_time_us;
564c10e753dSEddie James break;
565c10e753dSEddie James case 11:
566c10e753dSEddie James val = get_unaligned_be16(&power->vdd.value) * 1000000ULL;
567c10e753dSEddie James break;
568c10e753dSEddie James case 12:
5691f4d4af4SGuenter Roeck return sysfs_emit(buf, "%u_vdn\n",
570c10e753dSEddie James get_unaligned_be32(&power->sensor_id));
571c10e753dSEddie James case 13:
572c10e753dSEddie James val = occ_get_powr_avg(&power->vdn.accumulator,
573c10e753dSEddie James &power->vdn.update_tag);
574c10e753dSEddie James break;
575c10e753dSEddie James case 14:
576b0407d82SGustavo A. R. Silva val = (u64)get_unaligned_be32(&power->vdn.update_tag) *
577c10e753dSEddie James occ->powr_sample_time_us;
578c10e753dSEddie James break;
579c10e753dSEddie James case 15:
580c10e753dSEddie James val = get_unaligned_be16(&power->vdn.value) * 1000000ULL;
581c10e753dSEddie James break;
582c10e753dSEddie James default:
583c10e753dSEddie James return -EINVAL;
584c10e753dSEddie James }
585c10e753dSEddie James
5861f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val);
587c10e753dSEddie James }
588c10e753dSEddie James
occ_show_caps_1_2(struct device * dev,struct device_attribute * attr,char * buf)589c10e753dSEddie James static ssize_t occ_show_caps_1_2(struct device *dev,
590c10e753dSEddie James struct device_attribute *attr, char *buf)
591c10e753dSEddie James {
592c10e753dSEddie James int rc;
593c10e753dSEddie James u64 val = 0;
594c10e753dSEddie James struct caps_sensor_2 *caps;
595c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
596c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
597c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
598c10e753dSEddie James
599c10e753dSEddie James rc = occ_update_response(occ);
600c10e753dSEddie James if (rc)
601c10e753dSEddie James return rc;
602c10e753dSEddie James
603c10e753dSEddie James caps = ((struct caps_sensor_2 *)sensors->caps.data) + sattr->index;
604c10e753dSEddie James
605c10e753dSEddie James switch (sattr->nr) {
606c10e753dSEddie James case 0:
6071f4d4af4SGuenter Roeck return sysfs_emit(buf, "system\n");
608c10e753dSEddie James case 1:
609c10e753dSEddie James val = get_unaligned_be16(&caps->cap) * 1000000ULL;
610c10e753dSEddie James break;
611c10e753dSEddie James case 2:
612c10e753dSEddie James val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
613c10e753dSEddie James break;
614c10e753dSEddie James case 3:
615c10e753dSEddie James val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
616c10e753dSEddie James break;
617c10e753dSEddie James case 4:
618c10e753dSEddie James val = get_unaligned_be16(&caps->max) * 1000000ULL;
619c10e753dSEddie James break;
620c10e753dSEddie James case 5:
621c10e753dSEddie James val = get_unaligned_be16(&caps->min) * 1000000ULL;
622c10e753dSEddie James break;
623c10e753dSEddie James case 6:
624c10e753dSEddie James val = get_unaligned_be16(&caps->user) * 1000000ULL;
625c10e753dSEddie James break;
626c10e753dSEddie James case 7:
627c10e753dSEddie James if (occ->sensors.caps.version == 1)
628c10e753dSEddie James return -EINVAL;
629c10e753dSEddie James
630c10e753dSEddie James val = caps->user_source;
631c10e753dSEddie James break;
632c10e753dSEddie James default:
633c10e753dSEddie James return -EINVAL;
634c10e753dSEddie James }
635c10e753dSEddie James
6361f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val);
637c10e753dSEddie James }
638c10e753dSEddie James
occ_show_caps_3(struct device * dev,struct device_attribute * attr,char * buf)639c10e753dSEddie James static ssize_t occ_show_caps_3(struct device *dev,
640c10e753dSEddie James struct device_attribute *attr, char *buf)
641c10e753dSEddie James {
642c10e753dSEddie James int rc;
643c10e753dSEddie James u64 val = 0;
644c10e753dSEddie James struct caps_sensor_3 *caps;
645c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
646c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
647c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
648c10e753dSEddie James
649c10e753dSEddie James rc = occ_update_response(occ);
650c10e753dSEddie James if (rc)
651c10e753dSEddie James return rc;
652c10e753dSEddie James
653c10e753dSEddie James caps = ((struct caps_sensor_3 *)sensors->caps.data) + sattr->index;
654c10e753dSEddie James
655c10e753dSEddie James switch (sattr->nr) {
656c10e753dSEddie James case 0:
6571f4d4af4SGuenter Roeck return sysfs_emit(buf, "system\n");
658c10e753dSEddie James case 1:
659c10e753dSEddie James val = get_unaligned_be16(&caps->cap) * 1000000ULL;
660c10e753dSEddie James break;
661c10e753dSEddie James case 2:
662c10e753dSEddie James val = get_unaligned_be16(&caps->system_power) * 1000000ULL;
663c10e753dSEddie James break;
664c10e753dSEddie James case 3:
665c10e753dSEddie James val = get_unaligned_be16(&caps->n_cap) * 1000000ULL;
666c10e753dSEddie James break;
667c10e753dSEddie James case 4:
668c10e753dSEddie James val = get_unaligned_be16(&caps->max) * 1000000ULL;
669c10e753dSEddie James break;
670c10e753dSEddie James case 5:
671c10e753dSEddie James val = get_unaligned_be16(&caps->hard_min) * 1000000ULL;
672c10e753dSEddie James break;
673c10e753dSEddie James case 6:
674c10e753dSEddie James val = get_unaligned_be16(&caps->user) * 1000000ULL;
675c10e753dSEddie James break;
676c10e753dSEddie James case 7:
677c10e753dSEddie James val = caps->user_source;
678c10e753dSEddie James break;
67984dc9e8aSEddie James case 8:
68084dc9e8aSEddie James val = get_unaligned_be16(&caps->soft_min) * 1000000ULL;
68184dc9e8aSEddie James break;
682c10e753dSEddie James default:
683c10e753dSEddie James return -EINVAL;
684c10e753dSEddie James }
685c10e753dSEddie James
6861f4d4af4SGuenter Roeck return sysfs_emit(buf, "%llu\n", val);
687c10e753dSEddie James }
688c10e753dSEddie James
occ_store_caps_user(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)689c10e753dSEddie James static ssize_t occ_store_caps_user(struct device *dev,
690c10e753dSEddie James struct device_attribute *attr,
691c10e753dSEddie James const char *buf, size_t count)
692c10e753dSEddie James {
693c10e753dSEddie James int rc;
694c10e753dSEddie James u16 user_power_cap;
695c10e753dSEddie James unsigned long long value;
696c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
697c10e753dSEddie James
698c10e753dSEddie James rc = kstrtoull(buf, 0, &value);
699c10e753dSEddie James if (rc)
700c10e753dSEddie James return rc;
701c10e753dSEddie James
702c10e753dSEddie James user_power_cap = div64_u64(value, 1000000ULL); /* microwatt to watt */
703c10e753dSEddie James
704c10e753dSEddie James rc = occ_set_user_power_cap(occ, user_power_cap);
705c10e753dSEddie James if (rc)
706c10e753dSEddie James return rc;
707c10e753dSEddie James
708c10e753dSEddie James return count;
709c10e753dSEddie James }
710c10e753dSEddie James
occ_show_extended(struct device * dev,struct device_attribute * attr,char * buf)711c10e753dSEddie James static ssize_t occ_show_extended(struct device *dev,
712c10e753dSEddie James struct device_attribute *attr, char *buf)
713c10e753dSEddie James {
714c10e753dSEddie James int rc;
715c10e753dSEddie James struct extended_sensor *extn;
716c10e753dSEddie James struct occ *occ = dev_get_drvdata(dev);
717c10e753dSEddie James struct occ_sensors *sensors = &occ->sensors;
718c10e753dSEddie James struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr);
719c10e753dSEddie James
720c10e753dSEddie James rc = occ_update_response(occ);
721c10e753dSEddie James if (rc)
722c10e753dSEddie James return rc;
723c10e753dSEddie James
724c10e753dSEddie James extn = ((struct extended_sensor *)sensors->extended.data) +
725c10e753dSEddie James sattr->index;
726c10e753dSEddie James
727c10e753dSEddie James switch (sattr->nr) {
728c10e753dSEddie James case 0:
7291f4d4af4SGuenter Roeck if (extn->flags & EXTN_FLAG_SENSOR_ID) {
7301f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%u",
731c10e753dSEddie James get_unaligned_be32(&extn->sensor_id));
7321f4d4af4SGuenter Roeck } else {
73303009a60SAndy Shevchenko rc = sysfs_emit(buf, "%4phN\n", extn->name);
7341f4d4af4SGuenter Roeck }
735c10e753dSEddie James break;
736c10e753dSEddie James case 1:
7371f4d4af4SGuenter Roeck rc = sysfs_emit(buf, "%02x\n", extn->flags);
738c10e753dSEddie James break;
739c10e753dSEddie James case 2:
74003009a60SAndy Shevchenko rc = sysfs_emit(buf, "%6phN\n", extn->data);
741c10e753dSEddie James break;
742c10e753dSEddie James default:
743c10e753dSEddie James return -EINVAL;
744c10e753dSEddie James }
745c10e753dSEddie James
746c10e753dSEddie James return rc;
747c10e753dSEddie James }
748c10e753dSEddie James
74954076cb3SEddie James /*
75054076cb3SEddie James * Some helper macros to make it easier to define an occ_attribute. Since these
75154076cb3SEddie James * are dynamically allocated, we shouldn't use the existing kernel macros which
75254076cb3SEddie James * stringify the name argument.
75354076cb3SEddie James */
75454076cb3SEddie James #define ATTR_OCC(_name, _mode, _show, _store) { \
75554076cb3SEddie James .attr = { \
75654076cb3SEddie James .name = _name, \
75754076cb3SEddie James .mode = VERIFY_OCTAL_PERMISSIONS(_mode), \
75854076cb3SEddie James }, \
75954076cb3SEddie James .show = _show, \
76054076cb3SEddie James .store = _store, \
76154076cb3SEddie James }
76254076cb3SEddie James
76354076cb3SEddie James #define SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index) { \
76454076cb3SEddie James .dev_attr = ATTR_OCC(_name, _mode, _show, _store), \
76554076cb3SEddie James .index = _index, \
76654076cb3SEddie James .nr = _nr, \
76754076cb3SEddie James }
76854076cb3SEddie James
76954076cb3SEddie James #define OCC_INIT_ATTR(_name, _mode, _show, _store, _nr, _index) \
77054076cb3SEddie James ((struct sensor_device_attribute_2) \
77154076cb3SEddie James SENSOR_ATTR_OCC(_name, _mode, _show, _store, _nr, _index))
77254076cb3SEddie James
77354076cb3SEddie James /*
77454076cb3SEddie James * Allocate and instatiate sensor_device_attribute_2s. It's most efficient to
77554076cb3SEddie James * use our own instead of the built-in hwmon attribute types.
77654076cb3SEddie James */
occ_setup_sensor_attrs(struct occ * occ)77754076cb3SEddie James static int occ_setup_sensor_attrs(struct occ *occ)
77854076cb3SEddie James {
77954076cb3SEddie James unsigned int i, s, num_attrs = 0;
78054076cb3SEddie James struct device *dev = occ->bus_dev;
78154076cb3SEddie James struct occ_sensors *sensors = &occ->sensors;
78254076cb3SEddie James struct occ_attribute *attr;
78354076cb3SEddie James struct temp_sensor_2 *temp;
78454076cb3SEddie James ssize_t (*show_temp)(struct device *, struct device_attribute *,
78554076cb3SEddie James char *) = occ_show_temp_1;
78654076cb3SEddie James ssize_t (*show_freq)(struct device *, struct device_attribute *,
78754076cb3SEddie James char *) = occ_show_freq_1;
78854076cb3SEddie James ssize_t (*show_power)(struct device *, struct device_attribute *,
78954076cb3SEddie James char *) = occ_show_power_1;
79054076cb3SEddie James ssize_t (*show_caps)(struct device *, struct device_attribute *,
79154076cb3SEddie James char *) = occ_show_caps_1_2;
79254076cb3SEddie James
79354076cb3SEddie James switch (sensors->temp.version) {
79454076cb3SEddie James case 1:
79554076cb3SEddie James num_attrs += (sensors->temp.num_sensors * 2);
79654076cb3SEddie James break;
79754076cb3SEddie James case 2:
79854076cb3SEddie James num_attrs += (sensors->temp.num_sensors * 4);
79954076cb3SEddie James show_temp = occ_show_temp_2;
80054076cb3SEddie James break;
801db4919ecSEddie James case 0x10:
802db4919ecSEddie James num_attrs += (sensors->temp.num_sensors * 5);
803db4919ecSEddie James show_temp = occ_show_temp_10;
804db4919ecSEddie James break;
80554076cb3SEddie James default:
80654076cb3SEddie James sensors->temp.num_sensors = 0;
80754076cb3SEddie James }
80854076cb3SEddie James
80954076cb3SEddie James switch (sensors->freq.version) {
81054076cb3SEddie James case 2:
81154076cb3SEddie James show_freq = occ_show_freq_2;
812df561f66SGustavo A. R. Silva fallthrough;
81354076cb3SEddie James case 1:
81454076cb3SEddie James num_attrs += (sensors->freq.num_sensors * 2);
81554076cb3SEddie James break;
81654076cb3SEddie James default:
81754076cb3SEddie James sensors->freq.num_sensors = 0;
81854076cb3SEddie James }
81954076cb3SEddie James
82054076cb3SEddie James switch (sensors->power.version) {
82154076cb3SEddie James case 2:
82254076cb3SEddie James show_power = occ_show_power_2;
823df561f66SGustavo A. R. Silva fallthrough;
82454076cb3SEddie James case 1:
82554076cb3SEddie James num_attrs += (sensors->power.num_sensors * 4);
82654076cb3SEddie James break;
82754076cb3SEddie James case 0xA0:
82854076cb3SEddie James num_attrs += (sensors->power.num_sensors * 16);
82954076cb3SEddie James show_power = occ_show_power_a0;
83054076cb3SEddie James break;
83154076cb3SEddie James default:
83254076cb3SEddie James sensors->power.num_sensors = 0;
83354076cb3SEddie James }
83454076cb3SEddie James
83554076cb3SEddie James switch (sensors->caps.version) {
83654076cb3SEddie James case 1:
83754076cb3SEddie James num_attrs += (sensors->caps.num_sensors * 7);
83854076cb3SEddie James break;
83954076cb3SEddie James case 2:
84054076cb3SEddie James num_attrs += (sensors->caps.num_sensors * 8);
84154076cb3SEddie James break;
84284dc9e8aSEddie James case 3:
84384dc9e8aSEddie James show_caps = occ_show_caps_3;
84484dc9e8aSEddie James num_attrs += (sensors->caps.num_sensors * 9);
84584dc9e8aSEddie James break;
84654076cb3SEddie James default:
84754076cb3SEddie James sensors->caps.num_sensors = 0;
84854076cb3SEddie James }
84954076cb3SEddie James
85054076cb3SEddie James switch (sensors->extended.version) {
85154076cb3SEddie James case 1:
85254076cb3SEddie James num_attrs += (sensors->extended.num_sensors * 3);
85354076cb3SEddie James break;
85454076cb3SEddie James default:
85554076cb3SEddie James sensors->extended.num_sensors = 0;
85654076cb3SEddie James }
85754076cb3SEddie James
85854076cb3SEddie James occ->attrs = devm_kzalloc(dev, sizeof(*occ->attrs) * num_attrs,
85954076cb3SEddie James GFP_KERNEL);
86054076cb3SEddie James if (!occ->attrs)
86154076cb3SEddie James return -ENOMEM;
86254076cb3SEddie James
86354076cb3SEddie James /* null-terminated list */
86454076cb3SEddie James occ->group.attrs = devm_kzalloc(dev, sizeof(*occ->group.attrs) *
86554076cb3SEddie James num_attrs + 1, GFP_KERNEL);
86654076cb3SEddie James if (!occ->group.attrs)
86754076cb3SEddie James return -ENOMEM;
86854076cb3SEddie James
86954076cb3SEddie James attr = occ->attrs;
87054076cb3SEddie James
87154076cb3SEddie James for (i = 0; i < sensors->temp.num_sensors; ++i) {
87254076cb3SEddie James s = i + 1;
87354076cb3SEddie James temp = ((struct temp_sensor_2 *)sensors->temp.data) + i;
87454076cb3SEddie James
87554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "temp%d_label", s);
87654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
87754076cb3SEddie James 0, i);
87854076cb3SEddie James attr++;
87954076cb3SEddie James
880ffa26000SEddie James if (sensors->temp.version == 2 &&
88154076cb3SEddie James temp->fru_type == OCC_FRU_TYPE_VRM) {
88254076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
88354076cb3SEddie James "temp%d_alarm", s);
88454076cb3SEddie James } else {
88554076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
88654076cb3SEddie James "temp%d_input", s);
88754076cb3SEddie James }
88854076cb3SEddie James
88954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_temp, NULL,
89054076cb3SEddie James 1, i);
89154076cb3SEddie James attr++;
89254076cb3SEddie James
89354076cb3SEddie James if (sensors->temp.version > 1) {
89454076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
89554076cb3SEddie James "temp%d_fru_type", s);
89654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
89754076cb3SEddie James show_temp, NULL, 2, i);
89854076cb3SEddie James attr++;
89954076cb3SEddie James
90054076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
90154076cb3SEddie James "temp%d_fault", s);
90254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
90354076cb3SEddie James show_temp, NULL, 3, i);
90454076cb3SEddie James attr++;
905db4919ecSEddie James
906db4919ecSEddie James if (sensors->temp.version == 0x10) {
907db4919ecSEddie James snprintf(attr->name, sizeof(attr->name),
908db4919ecSEddie James "temp%d_max", s);
909db4919ecSEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
910db4919ecSEddie James show_temp, NULL,
911db4919ecSEddie James 4, i);
912db4919ecSEddie James attr++;
913db4919ecSEddie James }
91454076cb3SEddie James }
91554076cb3SEddie James }
91654076cb3SEddie James
91754076cb3SEddie James for (i = 0; i < sensors->freq.num_sensors; ++i) {
91854076cb3SEddie James s = i + 1;
91954076cb3SEddie James
92054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "freq%d_label", s);
92154076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
92254076cb3SEddie James 0, i);
92354076cb3SEddie James attr++;
92454076cb3SEddie James
92554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "freq%d_input", s);
92654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_freq, NULL,
92754076cb3SEddie James 1, i);
92854076cb3SEddie James attr++;
92954076cb3SEddie James }
93054076cb3SEddie James
93154076cb3SEddie James if (sensors->power.version == 0xA0) {
93254076cb3SEddie James /*
93354076cb3SEddie James * Special case for many-attribute power sensor. Split it into
93454076cb3SEddie James * a sensor number per power type, emulating several sensors.
93554076cb3SEddie James */
93654076cb3SEddie James for (i = 0; i < sensors->power.num_sensors; ++i) {
93754076cb3SEddie James unsigned int j;
93854076cb3SEddie James unsigned int nr = 0;
93954076cb3SEddie James
94054076cb3SEddie James s = (i * 4) + 1;
94154076cb3SEddie James
94254076cb3SEddie James for (j = 0; j < 4; ++j) {
94354076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
94454076cb3SEddie James "power%d_label", s);
94554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
94654076cb3SEddie James show_power, NULL,
94754076cb3SEddie James nr++, i);
94854076cb3SEddie James attr++;
94954076cb3SEddie James
95054076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
95154076cb3SEddie James "power%d_average", s);
95254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
95354076cb3SEddie James show_power, NULL,
95454076cb3SEddie James nr++, i);
95554076cb3SEddie James attr++;
95654076cb3SEddie James
95754076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
95854076cb3SEddie James "power%d_average_interval", s);
95954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
96054076cb3SEddie James show_power, NULL,
96154076cb3SEddie James nr++, i);
96254076cb3SEddie James attr++;
96354076cb3SEddie James
96454076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
96554076cb3SEddie James "power%d_input", s);
96654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
96754076cb3SEddie James show_power, NULL,
96854076cb3SEddie James nr++, i);
96954076cb3SEddie James attr++;
97054076cb3SEddie James
97154076cb3SEddie James s++;
97254076cb3SEddie James }
97354076cb3SEddie James }
9748e6af454SEddie James
9758e6af454SEddie James s = (sensors->power.num_sensors * 4) + 1;
97654076cb3SEddie James } else {
97754076cb3SEddie James for (i = 0; i < sensors->power.num_sensors; ++i) {
97854076cb3SEddie James s = i + 1;
97954076cb3SEddie James
98054076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
98154076cb3SEddie James "power%d_label", s);
98254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
98354076cb3SEddie James show_power, NULL, 0, i);
98454076cb3SEddie James attr++;
98554076cb3SEddie James
98654076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
98754076cb3SEddie James "power%d_average", s);
98854076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
98954076cb3SEddie James show_power, NULL, 1, i);
99054076cb3SEddie James attr++;
99154076cb3SEddie James
99254076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
99354076cb3SEddie James "power%d_average_interval", s);
99454076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
99554076cb3SEddie James show_power, NULL, 2, i);
99654076cb3SEddie James attr++;
99754076cb3SEddie James
99854076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
99954076cb3SEddie James "power%d_input", s);
100054076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
100154076cb3SEddie James show_power, NULL, 3, i);
100254076cb3SEddie James attr++;
100354076cb3SEddie James }
10048e6af454SEddie James
10058e6af454SEddie James s = sensors->power.num_sensors + 1;
100654076cb3SEddie James }
100754076cb3SEddie James
100854076cb3SEddie James if (sensors->caps.num_sensors >= 1) {
100954076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_label", s);
101054076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
101154076cb3SEddie James 0, 0);
101254076cb3SEddie James attr++;
101354076cb3SEddie James
101454076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap", s);
101554076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
101654076cb3SEddie James 1, 0);
101754076cb3SEddie James attr++;
101854076cb3SEddie James
101954076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_input", s);
102054076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
102154076cb3SEddie James 2, 0);
102254076cb3SEddie James attr++;
102354076cb3SEddie James
102454076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
102554076cb3SEddie James "power%d_cap_not_redundant", s);
102654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
102754076cb3SEddie James 3, 0);
102854076cb3SEddie James attr++;
102954076cb3SEddie James
103054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_max", s);
103154076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
103254076cb3SEddie James 4, 0);
103354076cb3SEddie James attr++;
103454076cb3SEddie James
103554076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_min", s);
103654076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444, show_caps, NULL,
103754076cb3SEddie James 5, 0);
103854076cb3SEddie James attr++;
103954076cb3SEddie James
104054076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "power%d_cap_user",
104154076cb3SEddie James s);
104254076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0644, show_caps,
104354076cb3SEddie James occ_store_caps_user, 6, 0);
104454076cb3SEddie James attr++;
104554076cb3SEddie James
104654076cb3SEddie James if (sensors->caps.version > 1) {
104754076cb3SEddie James snprintf(attr->name, sizeof(attr->name),
104854076cb3SEddie James "power%d_cap_user_source", s);
104954076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
105054076cb3SEddie James show_caps, NULL, 7, 0);
105154076cb3SEddie James attr++;
105284dc9e8aSEddie James
105384dc9e8aSEddie James if (sensors->caps.version > 2) {
105484dc9e8aSEddie James snprintf(attr->name, sizeof(attr->name),
105584dc9e8aSEddie James "power%d_cap_min_soft", s);
105684dc9e8aSEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
105784dc9e8aSEddie James show_caps, NULL,
105884dc9e8aSEddie James 8, 0);
105984dc9e8aSEddie James attr++;
106084dc9e8aSEddie James }
106154076cb3SEddie James }
106254076cb3SEddie James }
106354076cb3SEddie James
106454076cb3SEddie James for (i = 0; i < sensors->extended.num_sensors; ++i) {
106554076cb3SEddie James s = i + 1;
106654076cb3SEddie James
106754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_label", s);
106854076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
106954076cb3SEddie James occ_show_extended, NULL, 0, i);
107054076cb3SEddie James attr++;
107154076cb3SEddie James
107254076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_flags", s);
107354076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
107454076cb3SEddie James occ_show_extended, NULL, 1, i);
107554076cb3SEddie James attr++;
107654076cb3SEddie James
107754076cb3SEddie James snprintf(attr->name, sizeof(attr->name), "extn%d_input", s);
107854076cb3SEddie James attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
107954076cb3SEddie James occ_show_extended, NULL, 2, i);
108054076cb3SEddie James attr++;
108154076cb3SEddie James }
108254076cb3SEddie James
108354076cb3SEddie James /* put the sensors in the group */
108454076cb3SEddie James for (i = 0; i < num_attrs; ++i) {
108554076cb3SEddie James sysfs_attr_init(&occ->attrs[i].sensor.dev_attr.attr);
108654076cb3SEddie James occ->group.attrs[i] = &occ->attrs[i].sensor.dev_attr.attr;
108754076cb3SEddie James }
108854076cb3SEddie James
108954076cb3SEddie James return 0;
109054076cb3SEddie James }
109154076cb3SEddie James
1092aa195fe4SEddie James /* only need to do this once at startup, as OCC won't change sensors on us */
occ_parse_poll_response(struct occ * occ)1093aa195fe4SEddie James static void occ_parse_poll_response(struct occ *occ)
1094aa195fe4SEddie James {
1095aa195fe4SEddie James unsigned int i, old_offset, offset = 0, size = 0;
1096aa195fe4SEddie James struct occ_sensor *sensor;
1097aa195fe4SEddie James struct occ_sensors *sensors = &occ->sensors;
1098aa195fe4SEddie James struct occ_response *resp = &occ->resp;
1099aa195fe4SEddie James struct occ_poll_response *poll =
1100aa195fe4SEddie James (struct occ_poll_response *)&resp->data[0];
1101aa195fe4SEddie James struct occ_poll_response_header *header = &poll->header;
1102aa195fe4SEddie James struct occ_sensor_data_block *block = &poll->block;
1103aa195fe4SEddie James
1104aa195fe4SEddie James dev_info(occ->bus_dev, "OCC found, code level: %.16s\n",
1105aa195fe4SEddie James header->occ_code_level);
1106aa195fe4SEddie James
1107aa195fe4SEddie James for (i = 0; i < header->num_sensor_data_blocks; ++i) {
1108aa195fe4SEddie James block = (struct occ_sensor_data_block *)((u8 *)block + offset);
1109aa195fe4SEddie James old_offset = offset;
1110aa195fe4SEddie James offset = (block->header.num_sensors *
1111aa195fe4SEddie James block->header.sensor_length) + sizeof(block->header);
1112aa195fe4SEddie James size += offset;
1113aa195fe4SEddie James
1114aa195fe4SEddie James /* validate all the length/size fields */
1115aa195fe4SEddie James if ((size + sizeof(*header)) >= OCC_RESP_DATA_BYTES) {
1116aa195fe4SEddie James dev_warn(occ->bus_dev, "exceeded response buffer\n");
1117aa195fe4SEddie James return;
1118aa195fe4SEddie James }
1119aa195fe4SEddie James
1120aa195fe4SEddie James dev_dbg(occ->bus_dev, " %04x..%04x: %.4s (%d sensors)\n",
1121aa195fe4SEddie James old_offset, offset - 1, block->header.eye_catcher,
1122aa195fe4SEddie James block->header.num_sensors);
1123aa195fe4SEddie James
1124aa195fe4SEddie James /* match sensor block type */
1125aa195fe4SEddie James if (strncmp(block->header.eye_catcher, "TEMP", 4) == 0)
1126aa195fe4SEddie James sensor = &sensors->temp;
1127aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "FREQ", 4) == 0)
1128aa195fe4SEddie James sensor = &sensors->freq;
1129aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "POWR", 4) == 0)
1130aa195fe4SEddie James sensor = &sensors->power;
1131aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "CAPS", 4) == 0)
1132aa195fe4SEddie James sensor = &sensors->caps;
1133aa195fe4SEddie James else if (strncmp(block->header.eye_catcher, "EXTN", 4) == 0)
1134aa195fe4SEddie James sensor = &sensors->extended;
1135aa195fe4SEddie James else {
1136aa195fe4SEddie James dev_warn(occ->bus_dev, "sensor not supported %.4s\n",
1137aa195fe4SEddie James block->header.eye_catcher);
1138aa195fe4SEddie James continue;
1139aa195fe4SEddie James }
1140aa195fe4SEddie James
1141aa195fe4SEddie James sensor->num_sensors = block->header.num_sensors;
1142aa195fe4SEddie James sensor->version = block->header.sensor_format;
1143aa195fe4SEddie James sensor->data = &block->data;
1144aa195fe4SEddie James }
1145aa195fe4SEddie James
1146aa195fe4SEddie James dev_dbg(occ->bus_dev, "Max resp size: %u+%zd=%zd\n", size,
1147aa195fe4SEddie James sizeof(*header), size + sizeof(*header));
1148aa195fe4SEddie James }
1149aa195fe4SEddie James
occ_active(struct occ * occ,bool active)1150849b0156SEddie James int occ_active(struct occ *occ, bool active)
11515b5513b8SEddie James {
1152849b0156SEddie James int rc = mutex_lock_interruptible(&occ->lock);
11535b5513b8SEddie James
1154849b0156SEddie James if (rc)
11555b5513b8SEddie James return rc;
1156849b0156SEddie James
1157849b0156SEddie James if (active) {
1158849b0156SEddie James if (occ->active) {
1159849b0156SEddie James rc = -EALREADY;
1160849b0156SEddie James goto unlock;
1161849b0156SEddie James }
1162849b0156SEddie James
1163849b0156SEddie James occ->error_count = 0;
1164849b0156SEddie James occ->last_safe = 0;
1165849b0156SEddie James
1166849b0156SEddie James rc = occ_poll(occ);
1167849b0156SEddie James if (rc < 0) {
116838483e8fSEddie James dev_err(occ->bus_dev,
116938483e8fSEddie James "failed to get OCC poll response=%02x: %d\n",
117038483e8fSEddie James occ->resp.return_status, rc);
1171849b0156SEddie James goto unlock;
11725b5513b8SEddie James }
11735b5513b8SEddie James
1174849b0156SEddie James occ->active = true;
11755216dff2SEddie James occ->next_update = jiffies + OCC_UPDATE_FREQUENCY;
1176aa195fe4SEddie James occ_parse_poll_response(occ);
1177aa195fe4SEddie James
117854076cb3SEddie James rc = occ_setup_sensor_attrs(occ);
117954076cb3SEddie James if (rc) {
1180849b0156SEddie James dev_err(occ->bus_dev,
1181849b0156SEddie James "failed to setup sensor attrs: %d\n", rc);
1182849b0156SEddie James goto unlock;
1183849b0156SEddie James }
1184849b0156SEddie James
1185849b0156SEddie James occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
1186849b0156SEddie James "occ", occ,
1187849b0156SEddie James occ->groups);
1188849b0156SEddie James if (IS_ERR(occ->hwmon)) {
1189849b0156SEddie James rc = PTR_ERR(occ->hwmon);
1190849b0156SEddie James occ->hwmon = NULL;
1191849b0156SEddie James dev_err(occ->bus_dev,
1192849b0156SEddie James "failed to register hwmon device: %d\n", rc);
1193849b0156SEddie James goto unlock;
1194849b0156SEddie James }
1195849b0156SEddie James } else {
1196849b0156SEddie James if (!occ->active) {
1197849b0156SEddie James rc = -EALREADY;
1198849b0156SEddie James goto unlock;
1199849b0156SEddie James }
1200849b0156SEddie James
1201849b0156SEddie James if (occ->hwmon)
1202849b0156SEddie James hwmon_device_unregister(occ->hwmon);
1203849b0156SEddie James occ->active = false;
1204849b0156SEddie James occ->hwmon = NULL;
1205849b0156SEddie James }
1206849b0156SEddie James
1207849b0156SEddie James unlock:
1208849b0156SEddie James mutex_unlock(&occ->lock);
120954076cb3SEddie James return rc;
121054076cb3SEddie James }
121154076cb3SEddie James
occ_setup(struct occ * occ)1212849b0156SEddie James int occ_setup(struct occ *occ)
1213849b0156SEddie James {
1214849b0156SEddie James int rc;
1215849b0156SEddie James
1216849b0156SEddie James mutex_init(&occ->lock);
1217849b0156SEddie James occ->groups[0] = &occ->group;
121854076cb3SEddie James
1219df04ced6SEddie James rc = occ_setup_sysfs(occ);
1220*89a286fbSEddie James if (rc) {
1221df04ced6SEddie James dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
1222*89a286fbSEddie James return rc;
1223*89a286fbSEddie James }
1224*89a286fbSEddie James
1225*89a286fbSEddie James if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) {
1226*89a286fbSEddie James rc = occ_active(occ, true);
1227*89a286fbSEddie James if (rc)
1228*89a286fbSEddie James occ_shutdown_sysfs(occ);
1229*89a286fbSEddie James }
1230df04ced6SEddie James
1231df04ced6SEddie James return rc;
12325b5513b8SEddie James }
12335679ed99SJean Delvare EXPORT_SYMBOL_GPL(occ_setup);
12345679ed99SJean Delvare
occ_shutdown(struct occ * occ)1235849b0156SEddie James void occ_shutdown(struct occ *occ)
1236849b0156SEddie James {
1237ac6888acSEddie James mutex_lock(&occ->lock);
1238ac6888acSEddie James
1239849b0156SEddie James occ_shutdown_sysfs(occ);
1240849b0156SEddie James
1241849b0156SEddie James if (occ->hwmon)
1242849b0156SEddie James hwmon_device_unregister(occ->hwmon);
1243ac6888acSEddie James occ->hwmon = NULL;
1244ac6888acSEddie James
1245ac6888acSEddie James mutex_unlock(&occ->lock);
1246849b0156SEddie James }
1247849b0156SEddie James EXPORT_SYMBOL_GPL(occ_shutdown);
1248849b0156SEddie James
12495679ed99SJean Delvare MODULE_AUTHOR("Eddie James <eajames@linux.ibm.com>");
12505679ed99SJean Delvare MODULE_DESCRIPTION("Common OCC hwmon code");
12515679ed99SJean Delvare MODULE_LICENSE("GPL");
1252