xref: /openbmc/linux/drivers/hwmon/occ/common.c (revision 2c2be25f)
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>
132c2be25fSEddie 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 */
149d0fef244SEddie 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];
186d0fef244SEddie 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 
203d0fef244SEddie 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;
6792b8d17ddSEddie James 	case 8:
6802b8d17ddSEddie James 		val = get_unaligned_be16(&caps->soft_min) * 1000000ULL;
6812b8d17ddSEddie 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 {
7336a4a063fSAndy 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:
7406a4a063fSAndy 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;
8422b8d17ddSEddie James 	case 3:
8432b8d17ddSEddie James 		show_caps = occ_show_caps_3;
8442b8d17ddSEddie James 		num_attrs += (sensors->caps.num_sensors * 9);
8452b8d17ddSEddie 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++;
10522b8d17ddSEddie James 
10532b8d17ddSEddie James 			if (sensors->caps.version > 2) {
10542b8d17ddSEddie James 				snprintf(attr->name, sizeof(attr->name),
10552b8d17ddSEddie James 					 "power%d_cap_min_soft", s);
10562b8d17ddSEddie James 				attr->sensor = OCC_INIT_ATTR(attr->name, 0444,
10572b8d17ddSEddie James 							     show_caps, NULL,
10582b8d17ddSEddie James 							     8, 0);
10592b8d17ddSEddie James 				attr++;
10602b8d17ddSEddie 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)1150cf8e9f21SEddie James int occ_active(struct occ *occ, bool active)
11515b5513b8SEddie James {
1152cf8e9f21SEddie James 	int rc = mutex_lock_interruptible(&occ->lock);
11535b5513b8SEddie James 
1154cf8e9f21SEddie James 	if (rc)
11555b5513b8SEddie James 		return rc;
1156cf8e9f21SEddie James 
1157cf8e9f21SEddie James 	if (active) {
1158cf8e9f21SEddie James 		if (occ->active) {
1159cf8e9f21SEddie James 			rc = -EALREADY;
1160cf8e9f21SEddie James 			goto unlock;
1161cf8e9f21SEddie James 		}
1162cf8e9f21SEddie James 
1163cf8e9f21SEddie James 		occ->error_count = 0;
1164cf8e9f21SEddie James 		occ->last_safe = 0;
1165cf8e9f21SEddie James 
1166cf8e9f21SEddie James 		rc = occ_poll(occ);
1167cf8e9f21SEddie 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);
1171cf8e9f21SEddie James 			goto unlock;
11725b5513b8SEddie James 		}
11735b5513b8SEddie James 
1174cf8e9f21SEddie 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) {
1180cf8e9f21SEddie James 			dev_err(occ->bus_dev,
1181cf8e9f21SEddie James 				"failed to setup sensor attrs: %d\n", rc);
1182cf8e9f21SEddie James 			goto unlock;
1183cf8e9f21SEddie James 		}
1184cf8e9f21SEddie James 
1185cf8e9f21SEddie James 		occ->hwmon = hwmon_device_register_with_groups(occ->bus_dev,
1186cf8e9f21SEddie James 							       "occ", occ,
1187cf8e9f21SEddie James 							       occ->groups);
1188cf8e9f21SEddie James 		if (IS_ERR(occ->hwmon)) {
1189cf8e9f21SEddie James 			rc = PTR_ERR(occ->hwmon);
1190cf8e9f21SEddie James 			occ->hwmon = NULL;
1191cf8e9f21SEddie James 			dev_err(occ->bus_dev,
1192cf8e9f21SEddie James 				"failed to register hwmon device: %d\n", rc);
1193cf8e9f21SEddie James 			goto unlock;
1194cf8e9f21SEddie James 		}
1195cf8e9f21SEddie James 	} else {
1196cf8e9f21SEddie James 		if (!occ->active) {
1197cf8e9f21SEddie James 			rc = -EALREADY;
1198cf8e9f21SEddie James 			goto unlock;
1199cf8e9f21SEddie James 		}
1200cf8e9f21SEddie James 
1201cf8e9f21SEddie James 		if (occ->hwmon)
1202cf8e9f21SEddie James 			hwmon_device_unregister(occ->hwmon);
1203cf8e9f21SEddie James 		occ->active = false;
1204cf8e9f21SEddie James 		occ->hwmon = NULL;
1205cf8e9f21SEddie James 	}
1206cf8e9f21SEddie James 
1207cf8e9f21SEddie James unlock:
1208cf8e9f21SEddie James 	mutex_unlock(&occ->lock);
120954076cb3SEddie James 	return rc;
121054076cb3SEddie James }
121154076cb3SEddie James 
occ_setup(struct occ * occ)1212cf8e9f21SEddie James int occ_setup(struct occ *occ)
1213cf8e9f21SEddie James {
1214cf8e9f21SEddie James 	int rc;
1215cf8e9f21SEddie James 
1216cf8e9f21SEddie James 	mutex_init(&occ->lock);
1217cf8e9f21SEddie James 	occ->groups[0] = &occ->group;
121854076cb3SEddie James 
1219df04ced6SEddie James 	rc = occ_setup_sysfs(occ);
12202c2be25fSEddie James 	if (rc) {
1221df04ced6SEddie James 		dev_err(occ->bus_dev, "failed to setup sysfs: %d\n", rc);
12222c2be25fSEddie James 		return rc;
12232c2be25fSEddie James 	}
12242c2be25fSEddie James 
12252c2be25fSEddie James 	if (!device_property_read_bool(occ->bus_dev, "ibm,no-poll-on-init")) {
12262c2be25fSEddie James 		rc = occ_active(occ, true);
12272c2be25fSEddie James 		if (rc)
12282c2be25fSEddie James 			occ_shutdown_sysfs(occ);
12292c2be25fSEddie James 	}
1230df04ced6SEddie James 
1231df04ced6SEddie James 	return rc;
12325b5513b8SEddie James }
12335679ed99SJean Delvare EXPORT_SYMBOL_GPL(occ_setup);
12345679ed99SJean Delvare 
occ_shutdown(struct occ * occ)1235cf8e9f21SEddie James void occ_shutdown(struct occ *occ)
1236cf8e9f21SEddie James {
12378ee07641SEddie James 	mutex_lock(&occ->lock);
12388ee07641SEddie James 
1239cf8e9f21SEddie James 	occ_shutdown_sysfs(occ);
1240cf8e9f21SEddie James 
1241cf8e9f21SEddie James 	if (occ->hwmon)
1242cf8e9f21SEddie James 		hwmon_device_unregister(occ->hwmon);
12438ee07641SEddie James 	occ->hwmon = NULL;
12448ee07641SEddie James 
12458ee07641SEddie James 	mutex_unlock(&occ->lock);
1246cf8e9f21SEddie James }
1247cf8e9f21SEddie James EXPORT_SYMBOL_GPL(occ_shutdown);
1248cf8e9f21SEddie 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