xref: /openbmc/linux/drivers/hwmon/pmbus/pmbus_core.c (revision d48dc0bd339730a773f55d6d1fe9c9d1a49aa4a5)
174ba9207SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
29d2ecfb7SGuenter Roeck /*
39d2ecfb7SGuenter Roeck  * Hardware monitoring driver for PMBus devices
49d2ecfb7SGuenter Roeck  *
59d2ecfb7SGuenter Roeck  * Copyright (c) 2010, 2011 Ericsson AB.
6aebcbbfcSGuenter Roeck  * Copyright (c) 2012 Guenter Roeck
79d2ecfb7SGuenter Roeck  */
89d2ecfb7SGuenter Roeck 
91e069dfdSEdward A. James #include <linux/debugfs.h>
109d2ecfb7SGuenter Roeck #include <linux/kernel.h>
11bd467e4eSRobert Lippert #include <linux/math64.h>
129d2ecfb7SGuenter Roeck #include <linux/module.h>
139d2ecfb7SGuenter Roeck #include <linux/init.h>
149d2ecfb7SGuenter Roeck #include <linux/err.h>
159d2ecfb7SGuenter Roeck #include <linux/slab.h>
169d2ecfb7SGuenter Roeck #include <linux/i2c.h>
179d2ecfb7SGuenter Roeck #include <linux/hwmon.h>
189d2ecfb7SGuenter Roeck #include <linux/hwmon-sysfs.h>
194ba1bb12SWolfram Sang #include <linux/pmbus.h>
20ddbb4db4SAlan Tull #include <linux/regulator/driver.h>
21ddbb4db4SAlan Tull #include <linux/regulator/machine.h>
223aa74796SEduardo Valentin #include <linux/of.h>
233aa74796SEduardo Valentin #include <linux/thermal.h>
249d2ecfb7SGuenter Roeck #include "pmbus.h"
259d2ecfb7SGuenter Roeck 
269d2ecfb7SGuenter Roeck /*
2785cfb3a8SGuenter Roeck  * Number of additional attribute pointers to allocate
2885cfb3a8SGuenter Roeck  * with each call to krealloc
299d2ecfb7SGuenter Roeck  */
3085cfb3a8SGuenter Roeck #define PMBUS_ATTR_ALLOC_SIZE	32
312bd05bf4SGuenter Roeck #define PMBUS_NAME_SIZE		24
322bd05bf4SGuenter Roeck 
339d2ecfb7SGuenter Roeck struct pmbus_sensor {
34e1e081a7SGuenter Roeck 	struct pmbus_sensor *next;
352bd05bf4SGuenter Roeck 	char name[PMBUS_NAME_SIZE];	/* sysfs sensor name */
36e1e081a7SGuenter Roeck 	struct device_attribute attribute;
379d2ecfb7SGuenter Roeck 	u8 page;		/* page number */
3816358542SGuenter Roeck 	u8 phase;		/* phase number, 0xff for all phases */
396f183d33SGuenter Roeck 	u16 reg;		/* register */
409d2ecfb7SGuenter Roeck 	enum pmbus_sensor_classes class;	/* sensor class */
419d2ecfb7SGuenter Roeck 	bool update;		/* runtime sensor update needed */
42d206636eSAndrew Jeffery 	bool convert;		/* Whether or not to apply linear/vid/direct */
439d2ecfb7SGuenter Roeck 	int data;		/* Sensor data.
449d2ecfb7SGuenter Roeck 				   Negative if there was a read error */
459d2ecfb7SGuenter Roeck };
46e1e081a7SGuenter Roeck #define to_pmbus_sensor(_attr) \
47e1e081a7SGuenter Roeck 	container_of(_attr, struct pmbus_sensor, attribute)
489d2ecfb7SGuenter Roeck 
499d2ecfb7SGuenter Roeck struct pmbus_boolean {
502bd05bf4SGuenter Roeck 	char name[PMBUS_NAME_SIZE];	/* sysfs boolean name */
519d2ecfb7SGuenter Roeck 	struct sensor_device_attribute attribute;
52663834f3SGuenter Roeck 	struct pmbus_sensor *s1;
53663834f3SGuenter Roeck 	struct pmbus_sensor *s2;
549d2ecfb7SGuenter Roeck };
55663834f3SGuenter Roeck #define to_pmbus_boolean(_attr) \
56663834f3SGuenter Roeck 	container_of(_attr, struct pmbus_boolean, attribute)
579d2ecfb7SGuenter Roeck 
589d2ecfb7SGuenter Roeck struct pmbus_label {
592bd05bf4SGuenter Roeck 	char name[PMBUS_NAME_SIZE];	/* sysfs label name */
600328461eSGuenter Roeck 	struct device_attribute attribute;
612bd05bf4SGuenter Roeck 	char label[PMBUS_NAME_SIZE];	/* label */
629d2ecfb7SGuenter Roeck };
630328461eSGuenter Roeck #define to_pmbus_label(_attr) \
640328461eSGuenter Roeck 	container_of(_attr, struct pmbus_label, attribute)
659d2ecfb7SGuenter Roeck 
66a919ba06SGuenter Roeck /* Macros for converting between sensor index and register/page/status mask */
67a919ba06SGuenter Roeck 
68a919ba06SGuenter Roeck #define PB_STATUS_MASK	0xffff
69a919ba06SGuenter Roeck #define PB_REG_SHIFT	16
70a919ba06SGuenter Roeck #define PB_REG_MASK	0x3ff
71a919ba06SGuenter Roeck #define PB_PAGE_SHIFT	26
72a919ba06SGuenter Roeck #define PB_PAGE_MASK	0x3f
73a919ba06SGuenter Roeck 
74a919ba06SGuenter Roeck #define pb_reg_to_index(page, reg, mask)	(((page) << PB_PAGE_SHIFT) | \
75a919ba06SGuenter Roeck 						 ((reg) << PB_REG_SHIFT) | (mask))
76a919ba06SGuenter Roeck 
77a919ba06SGuenter Roeck #define pb_index_to_page(index)			(((index) >> PB_PAGE_SHIFT) & PB_PAGE_MASK)
78a919ba06SGuenter Roeck #define pb_index_to_reg(index)			(((index) >> PB_REG_SHIFT) & PB_REG_MASK)
79a919ba06SGuenter Roeck #define pb_index_to_mask(index)			((index) & PB_STATUS_MASK)
80a919ba06SGuenter Roeck 
819d2ecfb7SGuenter Roeck struct pmbus_data {
820328461eSGuenter Roeck 	struct device *dev;
839d2ecfb7SGuenter Roeck 	struct device *hwmon_dev;
84ffe36eb5SNaresh Solanki 	struct regulator_dev **rdevs;
859d2ecfb7SGuenter Roeck 
869d2ecfb7SGuenter Roeck 	u32 flags;		/* from platform data */
879d2ecfb7SGuenter Roeck 
881bc085e9SPatryk Biel 	u8 revision;	/* The PMBus revision the device is compliant with */
891bc085e9SPatryk Biel 
90daa436e6SGuenter Roeck 	int exponent[PMBUS_PAGES];
91daa436e6SGuenter Roeck 				/* linear mode: exponent for output voltages */
929d2ecfb7SGuenter Roeck 
939d2ecfb7SGuenter Roeck 	const struct pmbus_driver_info *info;
949d2ecfb7SGuenter Roeck 
959d2ecfb7SGuenter Roeck 	int max_attributes;
969d2ecfb7SGuenter Roeck 	int num_attributes;
979d2ecfb7SGuenter Roeck 	struct attribute_group group;
98991d6799Skrzysztof.adamski@nokia.com 	const struct attribute_group **groups;
991e069dfdSEdward A. James 	struct dentry *debugfs;		/* debugfs device directory */
1009d2ecfb7SGuenter Roeck 
1019d2ecfb7SGuenter Roeck 	struct pmbus_sensor *sensors;
1029d2ecfb7SGuenter Roeck 
1039d2ecfb7SGuenter Roeck 	struct mutex update_lock;
104cbcdec62SEdward A. James 
105c159be9eSEdward A. James 	bool has_status_word;		/* device uses STATUS_WORD register */
106cbcdec62SEdward A. James 	int (*read_status)(struct i2c_client *client, int page);
1079d2ecfb7SGuenter Roeck 
108d86f3c9bSGuenter Roeck 	s16 currpage;	/* current page, -1 for unknown/unset */
109d86f3c9bSGuenter Roeck 	s16 currphase;	/* current phase, 0xff for all, -1 for unknown/unset */
11007fb7627SMårten Lindahl 
11107fb7627SMårten Lindahl 	int vout_low[PMBUS_PAGES];	/* voltage low margin */
11207fb7627SMårten Lindahl 	int vout_high[PMBUS_PAGES];	/* voltage high margin */
1139d2ecfb7SGuenter Roeck };
1149d2ecfb7SGuenter Roeck 
1151e069dfdSEdward A. James struct pmbus_debugfs_entry {
1161e069dfdSEdward A. James 	struct i2c_client *client;
1171e069dfdSEdward A. James 	u8 page;
1181e069dfdSEdward A. James 	u8 reg;
1191e069dfdSEdward A. James };
1201e069dfdSEdward A. James 
121d206636eSAndrew Jeffery static const int pmbus_fan_rpm_mask[] = {
122d206636eSAndrew Jeffery 	PB_FAN_1_RPM,
123d206636eSAndrew Jeffery 	PB_FAN_2_RPM,
124d206636eSAndrew Jeffery 	PB_FAN_1_RPM,
125d206636eSAndrew Jeffery 	PB_FAN_2_RPM,
126d206636eSAndrew Jeffery };
127d206636eSAndrew Jeffery 
128d206636eSAndrew Jeffery static const int pmbus_fan_config_registers[] = {
129d206636eSAndrew Jeffery 	PMBUS_FAN_CONFIG_12,
130d206636eSAndrew Jeffery 	PMBUS_FAN_CONFIG_12,
131d206636eSAndrew Jeffery 	PMBUS_FAN_CONFIG_34,
132d206636eSAndrew Jeffery 	PMBUS_FAN_CONFIG_34
133d206636eSAndrew Jeffery };
134d206636eSAndrew Jeffery 
135d206636eSAndrew Jeffery static const int pmbus_fan_command_registers[] = {
136d206636eSAndrew Jeffery 	PMBUS_FAN_COMMAND_1,
137d206636eSAndrew Jeffery 	PMBUS_FAN_COMMAND_2,
138d206636eSAndrew Jeffery 	PMBUS_FAN_COMMAND_3,
139d206636eSAndrew Jeffery 	PMBUS_FAN_COMMAND_4,
140d206636eSAndrew Jeffery };
141d206636eSAndrew Jeffery 
pmbus_clear_cache(struct i2c_client * client)142ce603b18SGuenter Roeck void pmbus_clear_cache(struct i2c_client *client)
143ce603b18SGuenter Roeck {
144ce603b18SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
145a919ba06SGuenter Roeck 	struct pmbus_sensor *sensor;
146ce603b18SGuenter Roeck 
147a919ba06SGuenter Roeck 	for (sensor = data->sensors; sensor; sensor = sensor->next)
148a919ba06SGuenter Roeck 		sensor->data = -ENODATA;
149ce603b18SGuenter Roeck }
150b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_clear_cache, PMBUS);
151ce603b18SGuenter Roeck 
pmbus_set_update(struct i2c_client * client,u8 reg,bool update)152d3e33067SErik Rosen void pmbus_set_update(struct i2c_client *client, u8 reg, bool update)
153d3e33067SErik Rosen {
154d3e33067SErik Rosen 	struct pmbus_data *data = i2c_get_clientdata(client);
155d3e33067SErik Rosen 	struct pmbus_sensor *sensor;
156d3e33067SErik Rosen 
157d3e33067SErik Rosen 	for (sensor = data->sensors; sensor; sensor = sensor->next)
158d3e33067SErik Rosen 		if (sensor->reg == reg)
159d3e33067SErik Rosen 			sensor->update = update;
160d3e33067SErik Rosen }
161b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_set_update, PMBUS);
162d3e33067SErik Rosen 
pmbus_set_page(struct i2c_client * client,int page,int phase)16343f33b6eSGuenter Roeck int pmbus_set_page(struct i2c_client *client, int page, int phase)
1649d2ecfb7SGuenter Roeck {
1659d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
166464df6faSAndrew Jeffery 	int rv;
1679d2ecfb7SGuenter Roeck 
16816358542SGuenter Roeck 	if (page < 0)
169464df6faSAndrew Jeffery 		return 0;
170464df6faSAndrew Jeffery 
17116358542SGuenter Roeck 	if (!(data->info->func[page] & PMBUS_PAGE_VIRTUAL) &&
17216358542SGuenter Roeck 	    data->info->pages > 1 && page != data->currpage) {
173ce842a5aSAndrew Jeffery 		dev_dbg(&client->dev, "Want page %u, %u cached\n", page,
174ce842a5aSAndrew Jeffery 			data->currpage);
175ce842a5aSAndrew Jeffery 
1769d2ecfb7SGuenter Roeck 		rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page);
177ce842a5aSAndrew Jeffery 		if (rv < 0) {
178ce842a5aSAndrew Jeffery 			rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE,
179ce842a5aSAndrew Jeffery 						       page);
180ce842a5aSAndrew Jeffery 			dev_dbg(&client->dev,
181ce842a5aSAndrew Jeffery 				"Failed to set page %u, performed one-shot retry %s: %d\n",
182ce842a5aSAndrew Jeffery 				page, rv ? "and failed" : "with success", rv);
183464df6faSAndrew Jeffery 			if (rv < 0)
1849d2ecfb7SGuenter Roeck 				return rv;
185ce842a5aSAndrew Jeffery 		}
186464df6faSAndrew Jeffery 
187464df6faSAndrew Jeffery 		rv = i2c_smbus_read_byte_data(client, PMBUS_PAGE);
188464df6faSAndrew Jeffery 		if (rv < 0)
189464df6faSAndrew Jeffery 			return rv;
190464df6faSAndrew Jeffery 
191464df6faSAndrew Jeffery 		if (rv != page)
192464df6faSAndrew Jeffery 			return -EIO;
193464df6faSAndrew Jeffery 	}
194464df6faSAndrew Jeffery 	data->currpage = page;
195464df6faSAndrew Jeffery 
19616358542SGuenter Roeck 	if (data->info->phases[page] && data->currphase != phase &&
19716358542SGuenter Roeck 	    !(data->info->func[page] & PMBUS_PHASE_VIRTUAL)) {
19816358542SGuenter Roeck 		rv = i2c_smbus_write_byte_data(client, PMBUS_PHASE,
19916358542SGuenter Roeck 					       phase);
20016358542SGuenter Roeck 		if (rv)
20116358542SGuenter Roeck 			return rv;
20216358542SGuenter Roeck 	}
20316358542SGuenter Roeck 	data->currphase = phase;
20416358542SGuenter Roeck 
205464df6faSAndrew Jeffery 	return 0;
2069d2ecfb7SGuenter Roeck }
207b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_set_page, PMBUS);
2089d2ecfb7SGuenter Roeck 
pmbus_write_byte(struct i2c_client * client,int page,u8 value)20903e9bd8dSGuenter Roeck int pmbus_write_byte(struct i2c_client *client, int page, u8 value)
2109d2ecfb7SGuenter Roeck {
2119d2ecfb7SGuenter Roeck 	int rv;
2129d2ecfb7SGuenter Roeck 
21343f33b6eSGuenter Roeck 	rv = pmbus_set_page(client, page, 0xff);
2149d2ecfb7SGuenter Roeck 	if (rv < 0)
2159d2ecfb7SGuenter Roeck 		return rv;
2169d2ecfb7SGuenter Roeck 
2179d2ecfb7SGuenter Roeck 	return i2c_smbus_write_byte(client, value);
2189d2ecfb7SGuenter Roeck }
219b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_write_byte, PMBUS);
2209d2ecfb7SGuenter Roeck 
221044cd3a5SGuenter Roeck /*
222044cd3a5SGuenter Roeck  * _pmbus_write_byte() is similar to pmbus_write_byte(), but checks if
22384fb029fSLABBE Corentin  * a device specific mapping function exists and calls it if necessary.
224044cd3a5SGuenter Roeck  */
_pmbus_write_byte(struct i2c_client * client,int page,u8 value)225044cd3a5SGuenter Roeck static int _pmbus_write_byte(struct i2c_client *client, int page, u8 value)
226044cd3a5SGuenter Roeck {
227044cd3a5SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
228044cd3a5SGuenter Roeck 	const struct pmbus_driver_info *info = data->info;
229044cd3a5SGuenter Roeck 	int status;
230044cd3a5SGuenter Roeck 
231044cd3a5SGuenter Roeck 	if (info->write_byte) {
232044cd3a5SGuenter Roeck 		status = info->write_byte(client, page, value);
233044cd3a5SGuenter Roeck 		if (status != -ENODATA)
234044cd3a5SGuenter Roeck 			return status;
235044cd3a5SGuenter Roeck 	}
236044cd3a5SGuenter Roeck 	return pmbus_write_byte(client, page, value);
237044cd3a5SGuenter Roeck }
238044cd3a5SGuenter Roeck 
pmbus_write_word_data(struct i2c_client * client,int page,u8 reg,u16 word)2396dcf2fb5SEdward A. James int pmbus_write_word_data(struct i2c_client *client, int page, u8 reg,
2406dcf2fb5SEdward A. James 			  u16 word)
2419d2ecfb7SGuenter Roeck {
2429d2ecfb7SGuenter Roeck 	int rv;
2439d2ecfb7SGuenter Roeck 
24443f33b6eSGuenter Roeck 	rv = pmbus_set_page(client, page, 0xff);
2459d2ecfb7SGuenter Roeck 	if (rv < 0)
2469d2ecfb7SGuenter Roeck 		return rv;
2479d2ecfb7SGuenter Roeck 
2489d2ecfb7SGuenter Roeck 	return i2c_smbus_write_word_data(client, reg, word);
2499d2ecfb7SGuenter Roeck }
250b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_write_word_data, PMBUS);
25146243f3aSGuenter Roeck 
252d206636eSAndrew Jeffery 
pmbus_write_virt_reg(struct i2c_client * client,int page,int reg,u16 word)253d206636eSAndrew Jeffery static int pmbus_write_virt_reg(struct i2c_client *client, int page, int reg,
254d206636eSAndrew Jeffery 				u16 word)
255d206636eSAndrew Jeffery {
256d206636eSAndrew Jeffery 	int bit;
257d206636eSAndrew Jeffery 	int id;
258d206636eSAndrew Jeffery 	int rv;
259d206636eSAndrew Jeffery 
260d206636eSAndrew Jeffery 	switch (reg) {
261d206636eSAndrew Jeffery 	case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
262d206636eSAndrew Jeffery 		id = reg - PMBUS_VIRT_FAN_TARGET_1;
263d206636eSAndrew Jeffery 		bit = pmbus_fan_rpm_mask[id];
264d206636eSAndrew Jeffery 		rv = pmbus_update_fan(client, page, id, bit, bit, word);
265d206636eSAndrew Jeffery 		break;
266d206636eSAndrew Jeffery 	default:
267d206636eSAndrew Jeffery 		rv = -ENXIO;
268d206636eSAndrew Jeffery 		break;
269d206636eSAndrew Jeffery 	}
270d206636eSAndrew Jeffery 
271d206636eSAndrew Jeffery 	return rv;
272d206636eSAndrew Jeffery }
273d206636eSAndrew Jeffery 
27446243f3aSGuenter Roeck /*
27546243f3aSGuenter Roeck  * _pmbus_write_word_data() is similar to pmbus_write_word_data(), but checks if
27646243f3aSGuenter Roeck  * a device specific mapping function exists and calls it if necessary.
27746243f3aSGuenter Roeck  */
_pmbus_write_word_data(struct i2c_client * client,int page,int reg,u16 word)27846243f3aSGuenter Roeck static int _pmbus_write_word_data(struct i2c_client *client, int page, int reg,
27946243f3aSGuenter Roeck 				  u16 word)
28046243f3aSGuenter Roeck {
28146243f3aSGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
28246243f3aSGuenter Roeck 	const struct pmbus_driver_info *info = data->info;
28346243f3aSGuenter Roeck 	int status;
28446243f3aSGuenter Roeck 
28546243f3aSGuenter Roeck 	if (info->write_word_data) {
28646243f3aSGuenter Roeck 		status = info->write_word_data(client, page, reg, word);
28746243f3aSGuenter Roeck 		if (status != -ENODATA)
28846243f3aSGuenter Roeck 			return status;
28946243f3aSGuenter Roeck 	}
290d206636eSAndrew Jeffery 
29146243f3aSGuenter Roeck 	if (reg >= PMBUS_VIRT_BASE)
292d206636eSAndrew Jeffery 		return pmbus_write_virt_reg(client, page, reg, word);
293d206636eSAndrew Jeffery 
29446243f3aSGuenter Roeck 	return pmbus_write_word_data(client, page, reg, word);
29546243f3aSGuenter Roeck }
2969d2ecfb7SGuenter Roeck 
2975de3e13fSMårten Lindahl /*
2985de3e13fSMårten Lindahl  * _pmbus_write_byte_data() is similar to pmbus_write_byte_data(), but checks if
2995de3e13fSMårten Lindahl  * a device specific mapping function exists and calls it if necessary.
3005de3e13fSMårten Lindahl  */
_pmbus_write_byte_data(struct i2c_client * client,int page,int reg,u8 value)3015de3e13fSMårten Lindahl static int _pmbus_write_byte_data(struct i2c_client *client, int page, int reg, u8 value)
3025de3e13fSMårten Lindahl {
3035de3e13fSMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
3045de3e13fSMårten Lindahl 	const struct pmbus_driver_info *info = data->info;
3055de3e13fSMårten Lindahl 	int status;
3065de3e13fSMårten Lindahl 
3075de3e13fSMårten Lindahl 	if (info->write_byte_data) {
3085de3e13fSMårten Lindahl 		status = info->write_byte_data(client, page, reg, value);
3095de3e13fSMårten Lindahl 		if (status != -ENODATA)
3105de3e13fSMårten Lindahl 			return status;
3115de3e13fSMårten Lindahl 	}
3125de3e13fSMårten Lindahl 	return pmbus_write_byte_data(client, page, reg, value);
3135de3e13fSMårten Lindahl }
3145de3e13fSMårten Lindahl 
315f0a5c839SMårten Lindahl /*
316f0a5c839SMårten Lindahl  * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if
317f0a5c839SMårten Lindahl  * a device specific mapping function exists and calls it if necessary.
318f0a5c839SMårten Lindahl  */
_pmbus_read_byte_data(struct i2c_client * client,int page,int reg)319f0a5c839SMårten Lindahl static int _pmbus_read_byte_data(struct i2c_client *client, int page, int reg)
320f0a5c839SMårten Lindahl {
321f0a5c839SMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
322f0a5c839SMårten Lindahl 	const struct pmbus_driver_info *info = data->info;
323f0a5c839SMårten Lindahl 	int status;
324f0a5c839SMårten Lindahl 
325f0a5c839SMårten Lindahl 	if (info->read_byte_data) {
326f0a5c839SMårten Lindahl 		status = info->read_byte_data(client, page, reg);
327f0a5c839SMårten Lindahl 		if (status != -ENODATA)
328f0a5c839SMårten Lindahl 			return status;
329f0a5c839SMårten Lindahl 	}
330f0a5c839SMårten Lindahl 	return pmbus_read_byte_data(client, page, reg);
331f0a5c839SMårten Lindahl }
332f0a5c839SMårten Lindahl 
pmbus_update_fan(struct i2c_client * client,int page,int id,u8 config,u8 mask,u16 command)333d206636eSAndrew Jeffery int pmbus_update_fan(struct i2c_client *client, int page, int id,
334d206636eSAndrew Jeffery 		     u8 config, u8 mask, u16 command)
335d206636eSAndrew Jeffery {
336d206636eSAndrew Jeffery 	int from;
337d206636eSAndrew Jeffery 	int rv;
338d206636eSAndrew Jeffery 	u8 to;
339d206636eSAndrew Jeffery 
340f0a5c839SMårten Lindahl 	from = _pmbus_read_byte_data(client, page,
341d206636eSAndrew Jeffery 				    pmbus_fan_config_registers[id]);
342d206636eSAndrew Jeffery 	if (from < 0)
343d206636eSAndrew Jeffery 		return from;
344d206636eSAndrew Jeffery 
345d206636eSAndrew Jeffery 	to = (from & ~mask) | (config & mask);
346d206636eSAndrew Jeffery 	if (to != from) {
3475de3e13fSMårten Lindahl 		rv = _pmbus_write_byte_data(client, page,
348d206636eSAndrew Jeffery 					   pmbus_fan_config_registers[id], to);
349d206636eSAndrew Jeffery 		if (rv < 0)
350d206636eSAndrew Jeffery 			return rv;
351d206636eSAndrew Jeffery 	}
352d206636eSAndrew Jeffery 
353d206636eSAndrew Jeffery 	return _pmbus_write_word_data(client, page,
354d206636eSAndrew Jeffery 				      pmbus_fan_command_registers[id], command);
355d206636eSAndrew Jeffery }
356b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_update_fan, PMBUS);
357d206636eSAndrew Jeffery 
pmbus_read_word_data(struct i2c_client * client,int page,int phase,u8 reg)35843f33b6eSGuenter Roeck int pmbus_read_word_data(struct i2c_client *client, int page, int phase, u8 reg)
3599d2ecfb7SGuenter Roeck {
3609d2ecfb7SGuenter Roeck 	int rv;
3619d2ecfb7SGuenter Roeck 
36243f33b6eSGuenter Roeck 	rv = pmbus_set_page(client, page, phase);
3639d2ecfb7SGuenter Roeck 	if (rv < 0)
3649d2ecfb7SGuenter Roeck 		return rv;
3659d2ecfb7SGuenter Roeck 
3669d2ecfb7SGuenter Roeck 	return i2c_smbus_read_word_data(client, reg);
3679d2ecfb7SGuenter Roeck }
368b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_read_word_data, PMBUS);
3699d2ecfb7SGuenter Roeck 
pmbus_read_virt_reg(struct i2c_client * client,int page,int reg)370d206636eSAndrew Jeffery static int pmbus_read_virt_reg(struct i2c_client *client, int page, int reg)
371d206636eSAndrew Jeffery {
372d206636eSAndrew Jeffery 	int rv;
373d206636eSAndrew Jeffery 	int id;
374d206636eSAndrew Jeffery 
375d206636eSAndrew Jeffery 	switch (reg) {
376d206636eSAndrew Jeffery 	case PMBUS_VIRT_FAN_TARGET_1 ... PMBUS_VIRT_FAN_TARGET_4:
377d206636eSAndrew Jeffery 		id = reg - PMBUS_VIRT_FAN_TARGET_1;
378d206636eSAndrew Jeffery 		rv = pmbus_get_fan_rate_device(client, page, id, rpm);
379d206636eSAndrew Jeffery 		break;
380d206636eSAndrew Jeffery 	default:
381d206636eSAndrew Jeffery 		rv = -ENXIO;
382d206636eSAndrew Jeffery 		break;
383d206636eSAndrew Jeffery 	}
384d206636eSAndrew Jeffery 
385d206636eSAndrew Jeffery 	return rv;
386d206636eSAndrew Jeffery }
387d206636eSAndrew Jeffery 
38846243f3aSGuenter Roeck /*
38946243f3aSGuenter Roeck  * _pmbus_read_word_data() is similar to pmbus_read_word_data(), but checks if
39046243f3aSGuenter Roeck  * a device specific mapping function exists and calls it if necessary.
39146243f3aSGuenter Roeck  */
_pmbus_read_word_data(struct i2c_client * client,int page,int phase,int reg)39243f33b6eSGuenter Roeck static int _pmbus_read_word_data(struct i2c_client *client, int page,
39343f33b6eSGuenter Roeck 				 int phase, int reg)
39446243f3aSGuenter Roeck {
39546243f3aSGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
39646243f3aSGuenter Roeck 	const struct pmbus_driver_info *info = data->info;
39746243f3aSGuenter Roeck 	int status;
39846243f3aSGuenter Roeck 
39946243f3aSGuenter Roeck 	if (info->read_word_data) {
40043f33b6eSGuenter Roeck 		status = info->read_word_data(client, page, phase, reg);
40146243f3aSGuenter Roeck 		if (status != -ENODATA)
40246243f3aSGuenter Roeck 			return status;
40346243f3aSGuenter Roeck 	}
404d206636eSAndrew Jeffery 
4056f183d33SGuenter Roeck 	if (reg >= PMBUS_VIRT_BASE)
406d206636eSAndrew Jeffery 		return pmbus_read_virt_reg(client, page, reg);
407d206636eSAndrew Jeffery 
40843f33b6eSGuenter Roeck 	return pmbus_read_word_data(client, page, phase, reg);
40943f33b6eSGuenter Roeck }
41043f33b6eSGuenter Roeck 
41143f33b6eSGuenter Roeck /* Same as above, but without phase parameter, for use in check functions */
__pmbus_read_word_data(struct i2c_client * client,int page,int reg)41243f33b6eSGuenter Roeck static int __pmbus_read_word_data(struct i2c_client *client, int page, int reg)
41343f33b6eSGuenter Roeck {
41443f33b6eSGuenter Roeck 	return _pmbus_read_word_data(client, page, 0xff, reg);
41546243f3aSGuenter Roeck }
41646243f3aSGuenter Roeck 
pmbus_read_byte_data(struct i2c_client * client,int page,u8 reg)4179c1ed894SGuenter Roeck int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg)
4189d2ecfb7SGuenter Roeck {
4199d2ecfb7SGuenter Roeck 	int rv;
4209d2ecfb7SGuenter Roeck 
42143f33b6eSGuenter Roeck 	rv = pmbus_set_page(client, page, 0xff);
4229d2ecfb7SGuenter Roeck 	if (rv < 0)
4239d2ecfb7SGuenter Roeck 		return rv;
4249d2ecfb7SGuenter Roeck 
4259d2ecfb7SGuenter Roeck 	return i2c_smbus_read_byte_data(client, reg);
4269d2ecfb7SGuenter Roeck }
427b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_read_byte_data, PMBUS);
4289d2ecfb7SGuenter Roeck 
pmbus_write_byte_data(struct i2c_client * client,int page,u8 reg,u8 value)42911c11998SAlan Tull int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value)
43011c11998SAlan Tull {
43111c11998SAlan Tull 	int rv;
43211c11998SAlan Tull 
43343f33b6eSGuenter Roeck 	rv = pmbus_set_page(client, page, 0xff);
43411c11998SAlan Tull 	if (rv < 0)
43511c11998SAlan Tull 		return rv;
43611c11998SAlan Tull 
43711c11998SAlan Tull 	return i2c_smbus_write_byte_data(client, reg, value);
43811c11998SAlan Tull }
439b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_write_byte_data, PMBUS);
44011c11998SAlan Tull 
pmbus_update_byte_data(struct i2c_client * client,int page,u8 reg,u8 mask,u8 value)44111c11998SAlan Tull int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg,
44211c11998SAlan Tull 			   u8 mask, u8 value)
44311c11998SAlan Tull {
44411c11998SAlan Tull 	unsigned int tmp;
44511c11998SAlan Tull 	int rv;
44611c11998SAlan Tull 
447f0a5c839SMårten Lindahl 	rv = _pmbus_read_byte_data(client, page, reg);
44811c11998SAlan Tull 	if (rv < 0)
44911c11998SAlan Tull 		return rv;
45011c11998SAlan Tull 
45111c11998SAlan Tull 	tmp = (rv & ~mask) | (value & mask);
45211c11998SAlan Tull 
45311c11998SAlan Tull 	if (tmp != rv)
4545de3e13fSMårten Lindahl 		rv = _pmbus_write_byte_data(client, page, reg, tmp);
45511c11998SAlan Tull 
45611c11998SAlan Tull 	return rv;
45711c11998SAlan Tull }
458b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_update_byte_data, PMBUS);
45911c11998SAlan Tull 
pmbus_read_block_data(struct i2c_client * client,int page,u8 reg,char * data_buf)4608a85007cSAdam Wujek static int pmbus_read_block_data(struct i2c_client *client, int page, u8 reg,
4618a85007cSAdam Wujek 				 char *data_buf)
4628a85007cSAdam Wujek {
4638a85007cSAdam Wujek 	int rv;
4648a85007cSAdam Wujek 
4658a85007cSAdam Wujek 	rv = pmbus_set_page(client, page, 0xff);
4668a85007cSAdam Wujek 	if (rv < 0)
4678a85007cSAdam Wujek 		return rv;
4688a85007cSAdam Wujek 
4698a85007cSAdam Wujek 	return i2c_smbus_read_block_data(client, reg, data_buf);
4708a85007cSAdam Wujek }
4718a85007cSAdam Wujek 
pmbus_find_sensor(struct pmbus_data * data,int page,int reg)472d206636eSAndrew Jeffery static struct pmbus_sensor *pmbus_find_sensor(struct pmbus_data *data, int page,
473d206636eSAndrew Jeffery 					      int reg)
474d206636eSAndrew Jeffery {
475d206636eSAndrew Jeffery 	struct pmbus_sensor *sensor;
476d206636eSAndrew Jeffery 
477d206636eSAndrew Jeffery 	for (sensor = data->sensors; sensor; sensor = sensor->next) {
478d206636eSAndrew Jeffery 		if (sensor->page == page && sensor->reg == reg)
479d206636eSAndrew Jeffery 			return sensor;
480d206636eSAndrew Jeffery 	}
481d206636eSAndrew Jeffery 
482d206636eSAndrew Jeffery 	return ERR_PTR(-EINVAL);
483d206636eSAndrew Jeffery }
484d206636eSAndrew Jeffery 
pmbus_get_fan_rate(struct i2c_client * client,int page,int id,enum pmbus_fan_mode mode,bool from_cache)485d206636eSAndrew Jeffery static int pmbus_get_fan_rate(struct i2c_client *client, int page, int id,
486d206636eSAndrew Jeffery 			      enum pmbus_fan_mode mode,
487d206636eSAndrew Jeffery 			      bool from_cache)
488d206636eSAndrew Jeffery {
489d206636eSAndrew Jeffery 	struct pmbus_data *data = i2c_get_clientdata(client);
490d206636eSAndrew Jeffery 	bool want_rpm, have_rpm;
491d206636eSAndrew Jeffery 	struct pmbus_sensor *s;
492d206636eSAndrew Jeffery 	int config;
493d206636eSAndrew Jeffery 	int reg;
494d206636eSAndrew Jeffery 
495d206636eSAndrew Jeffery 	want_rpm = (mode == rpm);
496d206636eSAndrew Jeffery 
497d206636eSAndrew Jeffery 	if (from_cache) {
498d206636eSAndrew Jeffery 		reg = want_rpm ? PMBUS_VIRT_FAN_TARGET_1 : PMBUS_VIRT_PWM_1;
499d206636eSAndrew Jeffery 		s = pmbus_find_sensor(data, page, reg + id);
500d206636eSAndrew Jeffery 		if (IS_ERR(s))
501d206636eSAndrew Jeffery 			return PTR_ERR(s);
502d206636eSAndrew Jeffery 
503d206636eSAndrew Jeffery 		return s->data;
504d206636eSAndrew Jeffery 	}
505d206636eSAndrew Jeffery 
506f0a5c839SMårten Lindahl 	config = _pmbus_read_byte_data(client, page,
507d206636eSAndrew Jeffery 				      pmbus_fan_config_registers[id]);
508d206636eSAndrew Jeffery 	if (config < 0)
509d206636eSAndrew Jeffery 		return config;
510d206636eSAndrew Jeffery 
511d206636eSAndrew Jeffery 	have_rpm = !!(config & pmbus_fan_rpm_mask[id]);
512d206636eSAndrew Jeffery 	if (want_rpm == have_rpm)
51343f33b6eSGuenter Roeck 		return pmbus_read_word_data(client, page, 0xff,
514d206636eSAndrew Jeffery 					    pmbus_fan_command_registers[id]);
515d206636eSAndrew Jeffery 
516d206636eSAndrew Jeffery 	/* Can't sensibly map between RPM and PWM, just return zero */
517d206636eSAndrew Jeffery 	return 0;
518d206636eSAndrew Jeffery }
519d206636eSAndrew Jeffery 
pmbus_get_fan_rate_device(struct i2c_client * client,int page,int id,enum pmbus_fan_mode mode)520d206636eSAndrew Jeffery int pmbus_get_fan_rate_device(struct i2c_client *client, int page, int id,
521d206636eSAndrew Jeffery 			      enum pmbus_fan_mode mode)
522d206636eSAndrew Jeffery {
523d206636eSAndrew Jeffery 	return pmbus_get_fan_rate(client, page, id, mode, false);
524d206636eSAndrew Jeffery }
525b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_device, PMBUS);
526d206636eSAndrew Jeffery 
pmbus_get_fan_rate_cached(struct i2c_client * client,int page,int id,enum pmbus_fan_mode mode)527d206636eSAndrew Jeffery int pmbus_get_fan_rate_cached(struct i2c_client *client, int page, int id,
528d206636eSAndrew Jeffery 			      enum pmbus_fan_mode mode)
529d206636eSAndrew Jeffery {
530d206636eSAndrew Jeffery 	return pmbus_get_fan_rate(client, page, id, mode, true);
531d206636eSAndrew Jeffery }
532b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_get_fan_rate_cached, PMBUS);
533d206636eSAndrew Jeffery 
pmbus_clear_fault_page(struct i2c_client * client,int page)5349d2ecfb7SGuenter Roeck static void pmbus_clear_fault_page(struct i2c_client *client, int page)
5359d2ecfb7SGuenter Roeck {
536044cd3a5SGuenter Roeck 	_pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS);
5379d2ecfb7SGuenter Roeck }
5389d2ecfb7SGuenter Roeck 
pmbus_clear_faults(struct i2c_client * client)5399d2ecfb7SGuenter Roeck void pmbus_clear_faults(struct i2c_client *client)
5409d2ecfb7SGuenter Roeck {
5419d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
5429d2ecfb7SGuenter Roeck 	int i;
5439d2ecfb7SGuenter Roeck 
5449d2ecfb7SGuenter Roeck 	for (i = 0; i < data->info->pages; i++)
5459d2ecfb7SGuenter Roeck 		pmbus_clear_fault_page(client, i);
5469d2ecfb7SGuenter Roeck }
547b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_clear_faults, PMBUS);
5489d2ecfb7SGuenter Roeck 
pmbus_check_status_cml(struct i2c_client * client)5499c1ed894SGuenter Roeck static int pmbus_check_status_cml(struct i2c_client *client)
5509d2ecfb7SGuenter Roeck {
55116c6d01fSGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
5529d2ecfb7SGuenter Roeck 	int status, status2;
5539d2ecfb7SGuenter Roeck 
554cbcdec62SEdward A. James 	status = data->read_status(client, -1);
5559d2ecfb7SGuenter Roeck 	if (status < 0 || (status & PB_STATUS_CML)) {
556da8e48abSGuenter Roeck 		status2 = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
5579d2ecfb7SGuenter Roeck 		if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND))
558179144a0SGuenter Roeck 			return -EIO;
5599d2ecfb7SGuenter Roeck 	}
5609d2ecfb7SGuenter Roeck 	return 0;
5619d2ecfb7SGuenter Roeck }
5629d2ecfb7SGuenter Roeck 
pmbus_check_register(struct i2c_client * client,int (* func)(struct i2c_client * client,int page,int reg),int page,int reg)563f880b12cSGuenter Roeck static bool pmbus_check_register(struct i2c_client *client,
564f880b12cSGuenter Roeck 				 int (*func)(struct i2c_client *client,
565f880b12cSGuenter Roeck 					     int page, int reg),
566f880b12cSGuenter Roeck 				 int page, int reg)
5679d2ecfb7SGuenter Roeck {
5689d2ecfb7SGuenter Roeck 	int rv;
5699d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
5709d2ecfb7SGuenter Roeck 
571f880b12cSGuenter Roeck 	rv = func(client, page, reg);
5729d2ecfb7SGuenter Roeck 	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
5739c1ed894SGuenter Roeck 		rv = pmbus_check_status_cml(client);
57486c908d9SErik Rosen 	if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
57586c908d9SErik Rosen 		data->read_status(client, -1);
57614cf45f2SPatrick Rudolph 	if (reg < PMBUS_VIRT_BASE)
5779c1ed894SGuenter Roeck 		pmbus_clear_fault_page(client, -1);
5789d2ecfb7SGuenter Roeck 	return rv >= 0;
5799d2ecfb7SGuenter Roeck }
580f880b12cSGuenter Roeck 
pmbus_check_status_register(struct i2c_client * client,int page)581cbcdec62SEdward A. James static bool pmbus_check_status_register(struct i2c_client *client, int page)
582cbcdec62SEdward A. James {
583cbcdec62SEdward A. James 	int status;
584cbcdec62SEdward A. James 	struct pmbus_data *data = i2c_get_clientdata(client);
585cbcdec62SEdward A. James 
586cbcdec62SEdward A. James 	status = data->read_status(client, page);
587cbcdec62SEdward A. James 	if (status >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK) &&
588cbcdec62SEdward A. James 	    (status & PB_STATUS_CML)) {
589cbcdec62SEdward A. James 		status = _pmbus_read_byte_data(client, -1, PMBUS_STATUS_CML);
590cbcdec62SEdward A. James 		if (status < 0 || (status & PB_CML_FAULT_INVALID_COMMAND))
591cbcdec62SEdward A. James 			status = -EIO;
592cbcdec62SEdward A. James 	}
593cbcdec62SEdward A. James 
594cbcdec62SEdward A. James 	pmbus_clear_fault_page(client, -1);
595cbcdec62SEdward A. James 	return status >= 0;
596cbcdec62SEdward A. James }
597cbcdec62SEdward A. James 
pmbus_check_byte_register(struct i2c_client * client,int page,int reg)598f880b12cSGuenter Roeck bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg)
599f880b12cSGuenter Roeck {
600f880b12cSGuenter Roeck 	return pmbus_check_register(client, _pmbus_read_byte_data, page, reg);
601f880b12cSGuenter Roeck }
602b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_check_byte_register, PMBUS);
6039d2ecfb7SGuenter Roeck 
pmbus_check_word_register(struct i2c_client * client,int page,int reg)6049d2ecfb7SGuenter Roeck bool pmbus_check_word_register(struct i2c_client *client, int page, int reg)
6059d2ecfb7SGuenter Roeck {
60643f33b6eSGuenter Roeck 	return pmbus_check_register(client, __pmbus_read_word_data, page, reg);
6079d2ecfb7SGuenter Roeck }
608b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_check_word_register, PMBUS);
6099d2ecfb7SGuenter Roeck 
pmbus_check_block_register(struct i2c_client * client,int page,int reg)6100356d778SRen Zhijie static bool __maybe_unused pmbus_check_block_register(struct i2c_client *client,
6110356d778SRen Zhijie 						      int page, int reg)
6128a85007cSAdam Wujek {
6138a85007cSAdam Wujek 	int rv;
6148a85007cSAdam Wujek 	struct pmbus_data *data = i2c_get_clientdata(client);
6158a85007cSAdam Wujek 	char data_buf[I2C_SMBUS_BLOCK_MAX + 2];
6168a85007cSAdam Wujek 
6178a85007cSAdam Wujek 	rv = pmbus_read_block_data(client, page, reg, data_buf);
6188a85007cSAdam Wujek 	if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK))
6198a85007cSAdam Wujek 		rv = pmbus_check_status_cml(client);
6208a85007cSAdam Wujek 	if (rv < 0 && (data->flags & PMBUS_READ_STATUS_AFTER_FAILED_CHECK))
6218a85007cSAdam Wujek 		data->read_status(client, -1);
6228a85007cSAdam Wujek 	pmbus_clear_fault_page(client, -1);
6238a85007cSAdam Wujek 	return rv >= 0;
6248a85007cSAdam Wujek }
6258a85007cSAdam Wujek 
pmbus_get_driver_info(struct i2c_client * client)6269d2ecfb7SGuenter Roeck const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client)
6279d2ecfb7SGuenter Roeck {
6289d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
6299d2ecfb7SGuenter Roeck 
6309d2ecfb7SGuenter Roeck 	return data->info;
6319d2ecfb7SGuenter Roeck }
632b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_get_driver_info, PMBUS);
6339d2ecfb7SGuenter Roeck 
pmbus_get_status(struct i2c_client * client,int page,int reg)634a919ba06SGuenter Roeck static int pmbus_get_status(struct i2c_client *client, int page, int reg)
6359d2ecfb7SGuenter Roeck {
6369d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
637a919ba06SGuenter Roeck 	int status;
6389d2ecfb7SGuenter Roeck 
639a919ba06SGuenter Roeck 	switch (reg) {
640a919ba06SGuenter Roeck 	case PMBUS_STATUS_WORD:
641a919ba06SGuenter Roeck 		status = data->read_status(client, page);
642a919ba06SGuenter Roeck 		break;
643a919ba06SGuenter Roeck 	default:
644a919ba06SGuenter Roeck 		status = _pmbus_read_byte_data(client, page, reg);
645a919ba06SGuenter Roeck 		break;
646f880b12cSGuenter Roeck 	}
647a919ba06SGuenter Roeck 	if (status < 0)
6489d2ecfb7SGuenter Roeck 		pmbus_clear_faults(client);
649a919ba06SGuenter Roeck 	return status;
6509d2ecfb7SGuenter Roeck }
651a919ba06SGuenter Roeck 
pmbus_update_sensor_data(struct i2c_client * client,struct pmbus_sensor * sensor)652a919ba06SGuenter Roeck static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sensor *sensor)
653a919ba06SGuenter Roeck {
654a919ba06SGuenter Roeck 	if (sensor->data < 0 || sensor->update)
655a919ba06SGuenter Roeck 		sensor->data = _pmbus_read_word_data(client, sensor->page,
656a919ba06SGuenter Roeck 						     sensor->phase, sensor->reg);
6579d2ecfb7SGuenter Roeck }
6589d2ecfb7SGuenter Roeck 
6599d2ecfb7SGuenter Roeck /*
6604036a48eSGuenter Roeck  * Convert ieee754 sensor values to milli- or micro-units
6614036a48eSGuenter Roeck  * depending on sensor type.
6624036a48eSGuenter Roeck  *
6634036a48eSGuenter Roeck  * ieee754 data format:
6644036a48eSGuenter Roeck  *	bit 15:		sign
6654036a48eSGuenter Roeck  *	bit 10..14:	exponent
6664036a48eSGuenter Roeck  *	bit 0..9:	mantissa
6674036a48eSGuenter Roeck  * exponent=0:
6684036a48eSGuenter Roeck  *	v=(−1)^signbit * 2^(−14) * 0.significantbits
6694036a48eSGuenter Roeck  * exponent=1..30:
6704036a48eSGuenter Roeck  *	v=(−1)^signbit * 2^(exponent - 15) * 1.significantbits
6714036a48eSGuenter Roeck  * exponent=31:
6724036a48eSGuenter Roeck  *	v=NaN
6734036a48eSGuenter Roeck  *
6744036a48eSGuenter Roeck  * Add the number mantissa bits into the calculations for simplicity.
6754036a48eSGuenter Roeck  * To do that, add '10' to the exponent. By doing that, we can just add
6764036a48eSGuenter Roeck  * 0x400 to normal values and get the expected result.
6774036a48eSGuenter Roeck  */
pmbus_reg2data_ieee754(struct pmbus_data * data,struct pmbus_sensor * sensor)6784036a48eSGuenter Roeck static long pmbus_reg2data_ieee754(struct pmbus_data *data,
6794036a48eSGuenter Roeck 				   struct pmbus_sensor *sensor)
6804036a48eSGuenter Roeck {
6814036a48eSGuenter Roeck 	int exponent;
6824036a48eSGuenter Roeck 	bool sign;
6834036a48eSGuenter Roeck 	long val;
6844036a48eSGuenter Roeck 
6854036a48eSGuenter Roeck 	/* only support half precision for now */
6864036a48eSGuenter Roeck 	sign = sensor->data & 0x8000;
6874036a48eSGuenter Roeck 	exponent = (sensor->data >> 10) & 0x1f;
6884036a48eSGuenter Roeck 	val = sensor->data & 0x3ff;
6894036a48eSGuenter Roeck 
6904036a48eSGuenter Roeck 	if (exponent == 0) {			/* subnormal */
6914036a48eSGuenter Roeck 		exponent = -(14 + 10);
6924036a48eSGuenter Roeck 	} else if (exponent ==  0x1f) {		/* NaN, convert to min/max */
6934036a48eSGuenter Roeck 		exponent = 0;
6944036a48eSGuenter Roeck 		val = 65504;
6954036a48eSGuenter Roeck 	} else {
6964036a48eSGuenter Roeck 		exponent -= (15 + 10);		/* normal */
6974036a48eSGuenter Roeck 		val |= 0x400;
6984036a48eSGuenter Roeck 	}
6994036a48eSGuenter Roeck 
7004036a48eSGuenter Roeck 	/* scale result to milli-units for all sensors except fans */
7014036a48eSGuenter Roeck 	if (sensor->class != PSC_FAN)
7024036a48eSGuenter Roeck 		val = val * 1000L;
7034036a48eSGuenter Roeck 
7044036a48eSGuenter Roeck 	/* scale result to micro-units for power sensors */
7054036a48eSGuenter Roeck 	if (sensor->class == PSC_POWER)
7064036a48eSGuenter Roeck 		val = val * 1000L;
7074036a48eSGuenter Roeck 
7084036a48eSGuenter Roeck 	if (exponent >= 0)
7094036a48eSGuenter Roeck 		val <<= exponent;
7104036a48eSGuenter Roeck 	else
7114036a48eSGuenter Roeck 		val >>= -exponent;
7124036a48eSGuenter Roeck 
7134036a48eSGuenter Roeck 	if (sign)
7144036a48eSGuenter Roeck 		val = -val;
7154036a48eSGuenter Roeck 
7164036a48eSGuenter Roeck 	return val;
7174036a48eSGuenter Roeck }
7184036a48eSGuenter Roeck 
7194036a48eSGuenter Roeck /*
7209d2ecfb7SGuenter Roeck  * Convert linear sensor values to milli- or micro-units
7219d2ecfb7SGuenter Roeck  * depending on sensor type.
7229d2ecfb7SGuenter Roeck  */
pmbus_reg2data_linear(struct pmbus_data * data,struct pmbus_sensor * sensor)7238ba75b20SJosh Lehan static s64 pmbus_reg2data_linear(struct pmbus_data *data,
7249d2ecfb7SGuenter Roeck 				 struct pmbus_sensor *sensor)
7259d2ecfb7SGuenter Roeck {
7269d2ecfb7SGuenter Roeck 	s16 exponent;
7279d2ecfb7SGuenter Roeck 	s32 mantissa;
7288ba75b20SJosh Lehan 	s64 val;
7299d2ecfb7SGuenter Roeck 
7309d2ecfb7SGuenter Roeck 	if (sensor->class == PSC_VOLTAGE_OUT) {	/* LINEAR16 */
731daa436e6SGuenter Roeck 		exponent = data->exponent[sensor->page];
7329d2ecfb7SGuenter Roeck 		mantissa = (u16) sensor->data;
7339d2ecfb7SGuenter Roeck 	} else {				/* LINEAR11 */
7341af1f531SGuenter Roeck 		exponent = ((s16)sensor->data) >> 11;
7351af1f531SGuenter Roeck 		mantissa = ((s16)((sensor->data & 0x7ff) << 5)) >> 5;
7369d2ecfb7SGuenter Roeck 	}
7379d2ecfb7SGuenter Roeck 
7389d2ecfb7SGuenter Roeck 	val = mantissa;
7399d2ecfb7SGuenter Roeck 
7409d2ecfb7SGuenter Roeck 	/* scale result to milli-units for all sensors except fans */
7419d2ecfb7SGuenter Roeck 	if (sensor->class != PSC_FAN)
7428ba75b20SJosh Lehan 		val = val * 1000LL;
7439d2ecfb7SGuenter Roeck 
7449d2ecfb7SGuenter Roeck 	/* scale result to micro-units for power sensors */
7459d2ecfb7SGuenter Roeck 	if (sensor->class == PSC_POWER)
7468ba75b20SJosh Lehan 		val = val * 1000LL;
7479d2ecfb7SGuenter Roeck 
7489d2ecfb7SGuenter Roeck 	if (exponent >= 0)
7499d2ecfb7SGuenter Roeck 		val <<= exponent;
7509d2ecfb7SGuenter Roeck 	else
7519d2ecfb7SGuenter Roeck 		val >>= -exponent;
7529d2ecfb7SGuenter Roeck 
7539d2ecfb7SGuenter Roeck 	return val;
7549d2ecfb7SGuenter Roeck }
7559d2ecfb7SGuenter Roeck 
7569d2ecfb7SGuenter Roeck /*
7579d2ecfb7SGuenter Roeck  * Convert direct sensor values to milli- or micro-units
7589d2ecfb7SGuenter Roeck  * depending on sensor type.
7599d2ecfb7SGuenter Roeck  */
pmbus_reg2data_direct(struct pmbus_data * data,struct pmbus_sensor * sensor)7608ba75b20SJosh Lehan static s64 pmbus_reg2data_direct(struct pmbus_data *data,
7619d2ecfb7SGuenter Roeck 				 struct pmbus_sensor *sensor)
7629d2ecfb7SGuenter Roeck {
763bd467e4eSRobert Lippert 	s64 b, val = (s16)sensor->data;
764bd467e4eSRobert Lippert 	s32 m, R;
7659d2ecfb7SGuenter Roeck 
7669d2ecfb7SGuenter Roeck 	m = data->info->m[sensor->class];
7679d2ecfb7SGuenter Roeck 	b = data->info->b[sensor->class];
7689d2ecfb7SGuenter Roeck 	R = data->info->R[sensor->class];
7699d2ecfb7SGuenter Roeck 
7709d2ecfb7SGuenter Roeck 	if (m == 0)
7719d2ecfb7SGuenter Roeck 		return 0;
7729d2ecfb7SGuenter Roeck 
7739d2ecfb7SGuenter Roeck 	/* X = 1/m * (Y * 10^-R - b) */
7749d2ecfb7SGuenter Roeck 	R = -R;
7759d2ecfb7SGuenter Roeck 	/* scale result to milli-units for everything but fans */
776d206636eSAndrew Jeffery 	if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
7779d2ecfb7SGuenter Roeck 		R += 3;
7789d2ecfb7SGuenter Roeck 		b *= 1000;
7799d2ecfb7SGuenter Roeck 	}
7809d2ecfb7SGuenter Roeck 
7819d2ecfb7SGuenter Roeck 	/* scale result to micro-units for power sensors */
7829d2ecfb7SGuenter Roeck 	if (sensor->class == PSC_POWER) {
7839d2ecfb7SGuenter Roeck 		R += 3;
7849d2ecfb7SGuenter Roeck 		b *= 1000;
7859d2ecfb7SGuenter Roeck 	}
7869d2ecfb7SGuenter Roeck 
7879d2ecfb7SGuenter Roeck 	while (R > 0) {
7889d2ecfb7SGuenter Roeck 		val *= 10;
7899d2ecfb7SGuenter Roeck 		R--;
7909d2ecfb7SGuenter Roeck 	}
7919d2ecfb7SGuenter Roeck 	while (R < 0) {
792bd467e4eSRobert Lippert 		val = div_s64(val + 5LL, 10L);  /* round closest */
7939d2ecfb7SGuenter Roeck 		R++;
7949d2ecfb7SGuenter Roeck 	}
7959d2ecfb7SGuenter Roeck 
796bd467e4eSRobert Lippert 	val = div_s64(val - b, m);
7978ba75b20SJosh Lehan 	return val;
7989d2ecfb7SGuenter Roeck }
7999d2ecfb7SGuenter Roeck 
8001061d851SGuenter Roeck /*
8011061d851SGuenter Roeck  * Convert VID sensor values to milli- or micro-units
8021061d851SGuenter Roeck  * depending on sensor type.
8031061d851SGuenter Roeck  */
pmbus_reg2data_vid(struct pmbus_data * data,struct pmbus_sensor * sensor)8048ba75b20SJosh Lehan static s64 pmbus_reg2data_vid(struct pmbus_data *data,
8051061d851SGuenter Roeck 			      struct pmbus_sensor *sensor)
8061061d851SGuenter Roeck {
8071061d851SGuenter Roeck 	long val = sensor->data;
808068c2270SGuenter Roeck 	long rv = 0;
8091061d851SGuenter Roeck 
810b9fa0a3aSVadim Pasternak 	switch (data->info->vrm_version[sensor->page]) {
811068c2270SGuenter Roeck 	case vr11:
812068c2270SGuenter Roeck 		if (val >= 0x02 && val <= 0xb2)
813068c2270SGuenter Roeck 			rv = DIV_ROUND_CLOSEST(160000 - (val - 2) * 625, 100);
814068c2270SGuenter Roeck 		break;
815068c2270SGuenter Roeck 	case vr12:
816068c2270SGuenter Roeck 		if (val >= 0x01)
817068c2270SGuenter Roeck 			rv = 250 + (val - 1) * 5;
818068c2270SGuenter Roeck 		break;
819d4977c08SVadim Pasternak 	case vr13:
820d4977c08SVadim Pasternak 		if (val >= 0x01)
821d4977c08SVadim Pasternak 			rv = 500 + (val - 1) * 10;
822d4977c08SVadim Pasternak 		break;
8239d72340bSVadim Pasternak 	case imvp9:
8249d72340bSVadim Pasternak 		if (val >= 0x01)
8259d72340bSVadim Pasternak 			rv = 200 + (val - 1) * 10;
8269d72340bSVadim Pasternak 		break;
8279d72340bSVadim Pasternak 	case amd625mv:
8289d72340bSVadim Pasternak 		if (val >= 0x0 && val <= 0xd8)
8299d72340bSVadim Pasternak 			rv = DIV_ROUND_CLOSEST(155000 - val * 625, 100);
8309d72340bSVadim Pasternak 		break;
831068c2270SGuenter Roeck 	}
832068c2270SGuenter Roeck 	return rv;
8331061d851SGuenter Roeck }
8341061d851SGuenter Roeck 
pmbus_reg2data(struct pmbus_data * data,struct pmbus_sensor * sensor)8358ba75b20SJosh Lehan static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor)
8369d2ecfb7SGuenter Roeck {
8378ba75b20SJosh Lehan 	s64 val;
8389d2ecfb7SGuenter Roeck 
839d206636eSAndrew Jeffery 	if (!sensor->convert)
840d206636eSAndrew Jeffery 		return sensor->data;
841d206636eSAndrew Jeffery 
8421061d851SGuenter Roeck 	switch (data->info->format[sensor->class]) {
8431061d851SGuenter Roeck 	case direct:
8449d2ecfb7SGuenter Roeck 		val = pmbus_reg2data_direct(data, sensor);
8451061d851SGuenter Roeck 		break;
8461061d851SGuenter Roeck 	case vid:
8471061d851SGuenter Roeck 		val = pmbus_reg2data_vid(data, sensor);
8481061d851SGuenter Roeck 		break;
8494036a48eSGuenter Roeck 	case ieee754:
8504036a48eSGuenter Roeck 		val = pmbus_reg2data_ieee754(data, sensor);
8514036a48eSGuenter Roeck 		break;
8521061d851SGuenter Roeck 	case linear:
8531061d851SGuenter Roeck 	default:
8549d2ecfb7SGuenter Roeck 		val = pmbus_reg2data_linear(data, sensor);
8551061d851SGuenter Roeck 		break;
8561061d851SGuenter Roeck 	}
8579d2ecfb7SGuenter Roeck 	return val;
8589d2ecfb7SGuenter Roeck }
8599d2ecfb7SGuenter Roeck 
8604036a48eSGuenter Roeck #define MAX_IEEE_MANTISSA	(0x7ff * 1000)
8614036a48eSGuenter Roeck #define MIN_IEEE_MANTISSA	(0x400 * 1000)
8624036a48eSGuenter Roeck 
pmbus_data2reg_ieee754(struct pmbus_data * data,struct pmbus_sensor * sensor,long val)8634036a48eSGuenter Roeck static u16 pmbus_data2reg_ieee754(struct pmbus_data *data,
8644036a48eSGuenter Roeck 				  struct pmbus_sensor *sensor, long val)
8654036a48eSGuenter Roeck {
8664036a48eSGuenter Roeck 	u16 exponent = (15 + 10);
8674036a48eSGuenter Roeck 	long mantissa;
8684036a48eSGuenter Roeck 	u16 sign = 0;
8694036a48eSGuenter Roeck 
8704036a48eSGuenter Roeck 	/* simple case */
8714036a48eSGuenter Roeck 	if (val == 0)
8724036a48eSGuenter Roeck 		return 0;
8734036a48eSGuenter Roeck 
8744036a48eSGuenter Roeck 	if (val < 0) {
8754036a48eSGuenter Roeck 		sign = 0x8000;
8764036a48eSGuenter Roeck 		val = -val;
8774036a48eSGuenter Roeck 	}
8784036a48eSGuenter Roeck 
8794036a48eSGuenter Roeck 	/* Power is in uW. Convert to mW before converting. */
8804036a48eSGuenter Roeck 	if (sensor->class == PSC_POWER)
8814036a48eSGuenter Roeck 		val = DIV_ROUND_CLOSEST(val, 1000L);
8824036a48eSGuenter Roeck 
8834036a48eSGuenter Roeck 	/*
8844036a48eSGuenter Roeck 	 * For simplicity, convert fan data to milli-units
8854036a48eSGuenter Roeck 	 * before calculating the exponent.
8864036a48eSGuenter Roeck 	 */
8874036a48eSGuenter Roeck 	if (sensor->class == PSC_FAN)
8884036a48eSGuenter Roeck 		val = val * 1000;
8894036a48eSGuenter Roeck 
8904036a48eSGuenter Roeck 	/* Reduce large mantissa until it fits into 10 bit */
8914036a48eSGuenter Roeck 	while (val > MAX_IEEE_MANTISSA && exponent < 30) {
8924036a48eSGuenter Roeck 		exponent++;
8934036a48eSGuenter Roeck 		val >>= 1;
8944036a48eSGuenter Roeck 	}
8954036a48eSGuenter Roeck 	/*
8964036a48eSGuenter Roeck 	 * Increase small mantissa to generate valid 'normal'
8974036a48eSGuenter Roeck 	 * number
8984036a48eSGuenter Roeck 	 */
8994036a48eSGuenter Roeck 	while (val < MIN_IEEE_MANTISSA && exponent > 1) {
9004036a48eSGuenter Roeck 		exponent--;
9014036a48eSGuenter Roeck 		val <<= 1;
9024036a48eSGuenter Roeck 	}
9034036a48eSGuenter Roeck 
9044036a48eSGuenter Roeck 	/* Convert mantissa from milli-units to units */
9054036a48eSGuenter Roeck 	mantissa = DIV_ROUND_CLOSEST(val, 1000);
9064036a48eSGuenter Roeck 
9074036a48eSGuenter Roeck 	/*
9084036a48eSGuenter Roeck 	 * Ensure that the resulting number is within range.
9094036a48eSGuenter Roeck 	 * Valid range is 0x400..0x7ff, where bit 10 reflects
9104036a48eSGuenter Roeck 	 * the implied high bit in normalized ieee754 numbers.
9114036a48eSGuenter Roeck 	 * Set the range to 0x400..0x7ff to reflect this.
9124036a48eSGuenter Roeck 	 * The upper bit is then removed by the mask against
9134036a48eSGuenter Roeck 	 * 0x3ff in the final assignment.
9144036a48eSGuenter Roeck 	 */
9154036a48eSGuenter Roeck 	if (mantissa > 0x7ff)
9164036a48eSGuenter Roeck 		mantissa = 0x7ff;
9174036a48eSGuenter Roeck 	else if (mantissa < 0x400)
9184036a48eSGuenter Roeck 		mantissa = 0x400;
9194036a48eSGuenter Roeck 
9204036a48eSGuenter Roeck 	/* Convert to sign, 5 bit exponent, 10 bit mantissa */
9214036a48eSGuenter Roeck 	return sign | (mantissa & 0x3ff) | ((exponent << 10) & 0x7c00);
9224036a48eSGuenter Roeck }
9234036a48eSGuenter Roeck 
9244036a48eSGuenter Roeck #define MAX_LIN_MANTISSA	(1023 * 1000)
9254036a48eSGuenter Roeck #define MIN_LIN_MANTISSA	(511 * 1000)
9269d2ecfb7SGuenter Roeck 
pmbus_data2reg_linear(struct pmbus_data * data,struct pmbus_sensor * sensor,s64 val)9279d2ecfb7SGuenter Roeck static u16 pmbus_data2reg_linear(struct pmbus_data *data,
9288ba75b20SJosh Lehan 				 struct pmbus_sensor *sensor, s64 val)
9299d2ecfb7SGuenter Roeck {
9309d2ecfb7SGuenter Roeck 	s16 exponent = 0, mantissa;
9319d2ecfb7SGuenter Roeck 	bool negative = false;
9329d2ecfb7SGuenter Roeck 
9339d2ecfb7SGuenter Roeck 	/* simple case */
9349d2ecfb7SGuenter Roeck 	if (val == 0)
9359d2ecfb7SGuenter Roeck 		return 0;
9369d2ecfb7SGuenter Roeck 
937daa436e6SGuenter Roeck 	if (sensor->class == PSC_VOLTAGE_OUT) {
9389d2ecfb7SGuenter Roeck 		/* LINEAR16 does not support negative voltages */
9399d2ecfb7SGuenter Roeck 		if (val < 0)
9409d2ecfb7SGuenter Roeck 			return 0;
9419d2ecfb7SGuenter Roeck 
9429d2ecfb7SGuenter Roeck 		/*
9439d2ecfb7SGuenter Roeck 		 * For a static exponents, we don't have a choice
9449d2ecfb7SGuenter Roeck 		 * but to adjust the value to it.
9459d2ecfb7SGuenter Roeck 		 */
946daa436e6SGuenter Roeck 		if (data->exponent[sensor->page] < 0)
947daa436e6SGuenter Roeck 			val <<= -data->exponent[sensor->page];
9489d2ecfb7SGuenter Roeck 		else
949daa436e6SGuenter Roeck 			val >>= data->exponent[sensor->page];
9508ba75b20SJosh Lehan 		val = DIV_ROUND_CLOSEST_ULL(val, 1000);
9518ba75b20SJosh Lehan 		return clamp_val(val, 0, 0xffff);
9529d2ecfb7SGuenter Roeck 	}
9539d2ecfb7SGuenter Roeck 
9549d2ecfb7SGuenter Roeck 	if (val < 0) {
9559d2ecfb7SGuenter Roeck 		negative = true;
9569d2ecfb7SGuenter Roeck 		val = -val;
9579d2ecfb7SGuenter Roeck 	}
9589d2ecfb7SGuenter Roeck 
9599d2ecfb7SGuenter Roeck 	/* Power is in uW. Convert to mW before converting. */
960daa436e6SGuenter Roeck 	if (sensor->class == PSC_POWER)
9618ba75b20SJosh Lehan 		val = DIV_ROUND_CLOSEST_ULL(val, 1000);
9629d2ecfb7SGuenter Roeck 
9639d2ecfb7SGuenter Roeck 	/*
9649d2ecfb7SGuenter Roeck 	 * For simplicity, convert fan data to milli-units
9659d2ecfb7SGuenter Roeck 	 * before calculating the exponent.
9669d2ecfb7SGuenter Roeck 	 */
967daa436e6SGuenter Roeck 	if (sensor->class == PSC_FAN)
9688ba75b20SJosh Lehan 		val = val * 1000LL;
9699d2ecfb7SGuenter Roeck 
9709d2ecfb7SGuenter Roeck 	/* Reduce large mantissa until it fits into 10 bit */
9714036a48eSGuenter Roeck 	while (val >= MAX_LIN_MANTISSA && exponent < 15) {
9729d2ecfb7SGuenter Roeck 		exponent++;
9739d2ecfb7SGuenter Roeck 		val >>= 1;
9749d2ecfb7SGuenter Roeck 	}
9759d2ecfb7SGuenter Roeck 	/* Increase small mantissa to improve precision */
9764036a48eSGuenter Roeck 	while (val < MIN_LIN_MANTISSA && exponent > -15) {
9779d2ecfb7SGuenter Roeck 		exponent--;
9789d2ecfb7SGuenter Roeck 		val <<= 1;
9799d2ecfb7SGuenter Roeck 	}
9809d2ecfb7SGuenter Roeck 
9819d2ecfb7SGuenter Roeck 	/* Convert mantissa from milli-units to units */
9828ba75b20SJosh Lehan 	mantissa = clamp_val(DIV_ROUND_CLOSEST_ULL(val, 1000), 0, 0x3ff);
9839d2ecfb7SGuenter Roeck 
9849d2ecfb7SGuenter Roeck 	/* restore sign */
9859d2ecfb7SGuenter Roeck 	if (negative)
9869d2ecfb7SGuenter Roeck 		mantissa = -mantissa;
9879d2ecfb7SGuenter Roeck 
9889d2ecfb7SGuenter Roeck 	/* Convert to 5 bit exponent, 11 bit mantissa */
9899d2ecfb7SGuenter Roeck 	return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800);
9909d2ecfb7SGuenter Roeck }
9919d2ecfb7SGuenter Roeck 
pmbus_data2reg_direct(struct pmbus_data * data,struct pmbus_sensor * sensor,s64 val)9929d2ecfb7SGuenter Roeck static u16 pmbus_data2reg_direct(struct pmbus_data *data,
9938ba75b20SJosh Lehan 				 struct pmbus_sensor *sensor, s64 val)
9949d2ecfb7SGuenter Roeck {
9958ba75b20SJosh Lehan 	s64 b;
996bd467e4eSRobert Lippert 	s32 m, R;
9979d2ecfb7SGuenter Roeck 
998daa436e6SGuenter Roeck 	m = data->info->m[sensor->class];
999daa436e6SGuenter Roeck 	b = data->info->b[sensor->class];
1000daa436e6SGuenter Roeck 	R = data->info->R[sensor->class];
10019d2ecfb7SGuenter Roeck 
10029d2ecfb7SGuenter Roeck 	/* Power is in uW. Adjust R and b. */
1003daa436e6SGuenter Roeck 	if (sensor->class == PSC_POWER) {
10049d2ecfb7SGuenter Roeck 		R -= 3;
10059d2ecfb7SGuenter Roeck 		b *= 1000;
10069d2ecfb7SGuenter Roeck 	}
10079d2ecfb7SGuenter Roeck 
10089d2ecfb7SGuenter Roeck 	/* Calculate Y = (m * X + b) * 10^R */
1009d206636eSAndrew Jeffery 	if (!(sensor->class == PSC_FAN || sensor->class == PSC_PWM)) {
10109d2ecfb7SGuenter Roeck 		R -= 3;		/* Adjust R and b for data in milli-units */
10119d2ecfb7SGuenter Roeck 		b *= 1000;
10129d2ecfb7SGuenter Roeck 	}
10138ba75b20SJosh Lehan 	val = val * m + b;
10149d2ecfb7SGuenter Roeck 
10159d2ecfb7SGuenter Roeck 	while (R > 0) {
10168ba75b20SJosh Lehan 		val *= 10;
10179d2ecfb7SGuenter Roeck 		R--;
10189d2ecfb7SGuenter Roeck 	}
10199d2ecfb7SGuenter Roeck 	while (R < 0) {
10208ba75b20SJosh Lehan 		val = div_s64(val + 5LL, 10L);  /* round closest */
10219d2ecfb7SGuenter Roeck 		R++;
10229d2ecfb7SGuenter Roeck 	}
10239d2ecfb7SGuenter Roeck 
10248ba75b20SJosh Lehan 	return (u16)clamp_val(val, S16_MIN, S16_MAX);
10259d2ecfb7SGuenter Roeck }
10269d2ecfb7SGuenter Roeck 
pmbus_data2reg_vid(struct pmbus_data * data,struct pmbus_sensor * sensor,s64 val)10271061d851SGuenter Roeck static u16 pmbus_data2reg_vid(struct pmbus_data *data,
10288ba75b20SJosh Lehan 			      struct pmbus_sensor *sensor, s64 val)
10291061d851SGuenter Roeck {
10302a844c14SGuenter Roeck 	val = clamp_val(val, 500, 1600);
10311061d851SGuenter Roeck 
10328ba75b20SJosh Lehan 	return 2 + DIV_ROUND_CLOSEST_ULL((1600LL - val) * 100LL, 625);
10331061d851SGuenter Roeck }
10341061d851SGuenter Roeck 
pmbus_data2reg(struct pmbus_data * data,struct pmbus_sensor * sensor,s64 val)10359d2ecfb7SGuenter Roeck static u16 pmbus_data2reg(struct pmbus_data *data,
10368ba75b20SJosh Lehan 			  struct pmbus_sensor *sensor, s64 val)
10379d2ecfb7SGuenter Roeck {
10389d2ecfb7SGuenter Roeck 	u16 regval;
10399d2ecfb7SGuenter Roeck 
1040d206636eSAndrew Jeffery 	if (!sensor->convert)
1041d206636eSAndrew Jeffery 		return val;
1042d206636eSAndrew Jeffery 
1043daa436e6SGuenter Roeck 	switch (data->info->format[sensor->class]) {
10441061d851SGuenter Roeck 	case direct:
1045daa436e6SGuenter Roeck 		regval = pmbus_data2reg_direct(data, sensor, val);
10461061d851SGuenter Roeck 		break;
10471061d851SGuenter Roeck 	case vid:
1048daa436e6SGuenter Roeck 		regval = pmbus_data2reg_vid(data, sensor, val);
10491061d851SGuenter Roeck 		break;
10504036a48eSGuenter Roeck 	case ieee754:
10514036a48eSGuenter Roeck 		regval = pmbus_data2reg_ieee754(data, sensor, val);
10524036a48eSGuenter Roeck 		break;
10531061d851SGuenter Roeck 	case linear:
10541061d851SGuenter Roeck 	default:
1055daa436e6SGuenter Roeck 		regval = pmbus_data2reg_linear(data, sensor, val);
10561061d851SGuenter Roeck 		break;
10571061d851SGuenter Roeck 	}
10589d2ecfb7SGuenter Roeck 	return regval;
10599d2ecfb7SGuenter Roeck }
10609d2ecfb7SGuenter Roeck 
10619d2ecfb7SGuenter Roeck /*
10629d2ecfb7SGuenter Roeck  * Return boolean calculated from converted data.
1063663834f3SGuenter Roeck  * <index> defines a status register index and mask.
1064663834f3SGuenter Roeck  * The mask is in the lower 8 bits, the register index is in bits 8..23.
10659d2ecfb7SGuenter Roeck  *
1066663834f3SGuenter Roeck  * The associated pmbus_boolean structure contains optional pointers to two
1067663834f3SGuenter Roeck  * sensor attributes. If specified, those attributes are compared against each
1068663834f3SGuenter Roeck  * other to determine if a limit has been exceeded.
10699d2ecfb7SGuenter Roeck  *
1070663834f3SGuenter Roeck  * If the sensor attribute pointers are NULL, the function returns true if
1071663834f3SGuenter Roeck  * (status[reg] & mask) is true.
1072663834f3SGuenter Roeck  *
1073663834f3SGuenter Roeck  * If sensor attribute pointers are provided, a comparison against a specified
1074663834f3SGuenter Roeck  * limit has to be performed to determine the boolean result.
10759d2ecfb7SGuenter Roeck  * In this case, the function returns true if v1 >= v2 (where v1 and v2 are
1076663834f3SGuenter Roeck  * sensor values referenced by sensor attribute pointers s1 and s2).
10779d2ecfb7SGuenter Roeck  *
10789d2ecfb7SGuenter Roeck  * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>.
10799d2ecfb7SGuenter Roeck  * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>.
10809d2ecfb7SGuenter Roeck  *
10819d2ecfb7SGuenter Roeck  * If a negative value is stored in any of the referenced registers, this value
10829d2ecfb7SGuenter Roeck  * reflects an error code which will be returned.
10839d2ecfb7SGuenter Roeck  */
pmbus_get_boolean(struct i2c_client * client,struct pmbus_boolean * b,int index)1084a919ba06SGuenter Roeck static int pmbus_get_boolean(struct i2c_client *client, struct pmbus_boolean *b,
1085663834f3SGuenter Roeck 			     int index)
10869d2ecfb7SGuenter Roeck {
1087a919ba06SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
1088663834f3SGuenter Roeck 	struct pmbus_sensor *s1 = b->s1;
1089663834f3SGuenter Roeck 	struct pmbus_sensor *s2 = b->s2;
1090a919ba06SGuenter Roeck 	u16 mask = pb_index_to_mask(index);
1091a919ba06SGuenter Roeck 	u8 page = pb_index_to_page(index);
1092a919ba06SGuenter Roeck 	u16 reg = pb_index_to_reg(index);
1093d7ee1115SGuenter Roeck 	int ret, status;
1094a66a6eb9SEdward A. James 	u16 regval;
10959d2ecfb7SGuenter Roeck 
1096a919ba06SGuenter Roeck 	mutex_lock(&data->update_lock);
1097a919ba06SGuenter Roeck 	status = pmbus_get_status(client, page, reg);
1098a919ba06SGuenter Roeck 	if (status < 0) {
1099a919ba06SGuenter Roeck 		ret = status;
1100a919ba06SGuenter Roeck 		goto unlock;
1101a919ba06SGuenter Roeck 	}
1102a919ba06SGuenter Roeck 
1103a919ba06SGuenter Roeck 	if (s1)
1104a919ba06SGuenter Roeck 		pmbus_update_sensor_data(client, s1);
1105a919ba06SGuenter Roeck 	if (s2)
1106a919ba06SGuenter Roeck 		pmbus_update_sensor_data(client, s2);
11079d2ecfb7SGuenter Roeck 
11089d2ecfb7SGuenter Roeck 	regval = status & mask;
110935f165f0SVikash Chandola 	if (regval) {
11101bc085e9SPatryk Biel 		if (data->revision >= PMBUS_REV_12) {
11115de3e13fSMårten Lindahl 			ret = _pmbus_write_byte_data(client, page, reg, regval);
111235f165f0SVikash Chandola 			if (ret)
111335f165f0SVikash Chandola 				goto unlock;
11141bc085e9SPatryk Biel 		} else {
11151bc085e9SPatryk Biel 			pmbus_clear_fault_page(client, page);
11161bc085e9SPatryk Biel 		}
11171bc085e9SPatryk Biel 
111835f165f0SVikash Chandola 	}
111904e216d1SGuenter Roeck 	if (s1 && s2) {
11208ba75b20SJosh Lehan 		s64 v1, v2;
11219d2ecfb7SGuenter Roeck 
1122a919ba06SGuenter Roeck 		if (s1->data < 0) {
1123a919ba06SGuenter Roeck 			ret = s1->data;
1124a919ba06SGuenter Roeck 			goto unlock;
1125a919ba06SGuenter Roeck 		}
1126a919ba06SGuenter Roeck 		if (s2->data < 0) {
1127a919ba06SGuenter Roeck 			ret = s2->data;
1128a919ba06SGuenter Roeck 			goto unlock;
1129a919ba06SGuenter Roeck 		}
11309d2ecfb7SGuenter Roeck 
1131663834f3SGuenter Roeck 		v1 = pmbus_reg2data(data, s1);
1132663834f3SGuenter Roeck 		v2 = pmbus_reg2data(data, s2);
1133d7ee1115SGuenter Roeck 		ret = !!(regval && v1 >= v2);
113404e216d1SGuenter Roeck 	} else {
113504e216d1SGuenter Roeck 		ret = !!regval;
11369d2ecfb7SGuenter Roeck 	}
1137a919ba06SGuenter Roeck unlock:
1138a919ba06SGuenter Roeck 	mutex_unlock(&data->update_lock);
1139d7ee1115SGuenter Roeck 	return ret;
11409d2ecfb7SGuenter Roeck }
11419d2ecfb7SGuenter Roeck 
pmbus_show_boolean(struct device * dev,struct device_attribute * da,char * buf)11429d2ecfb7SGuenter Roeck static ssize_t pmbus_show_boolean(struct device *dev,
11439d2ecfb7SGuenter Roeck 				  struct device_attribute *da, char *buf)
11449d2ecfb7SGuenter Roeck {
11459d2ecfb7SGuenter Roeck 	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1146663834f3SGuenter Roeck 	struct pmbus_boolean *boolean = to_pmbus_boolean(attr);
1147a919ba06SGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev->parent);
11489d2ecfb7SGuenter Roeck 	int val;
11499d2ecfb7SGuenter Roeck 
1150a919ba06SGuenter Roeck 	val = pmbus_get_boolean(client, boolean, attr->index);
1151d7ee1115SGuenter Roeck 	if (val < 0)
1152d7ee1115SGuenter Roeck 		return val;
11531f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", val);
11549d2ecfb7SGuenter Roeck }
11559d2ecfb7SGuenter Roeck 
pmbus_show_sensor(struct device * dev,struct device_attribute * devattr,char * buf)11569d2ecfb7SGuenter Roeck static ssize_t pmbus_show_sensor(struct device *dev,
1157e1e081a7SGuenter Roeck 				 struct device_attribute *devattr, char *buf)
11589d2ecfb7SGuenter Roeck {
1159a919ba06SGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev->parent);
1160e1e081a7SGuenter Roeck 	struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
1161a919ba06SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
116218e8db7fSRobert Hancock 	ssize_t ret;
11639d2ecfb7SGuenter Roeck 
116418e8db7fSRobert Hancock 	mutex_lock(&data->update_lock);
1165a919ba06SGuenter Roeck 	pmbus_update_sensor_data(client, sensor);
11669d2ecfb7SGuenter Roeck 	if (sensor->data < 0)
116718e8db7fSRobert Hancock 		ret = sensor->data;
116818e8db7fSRobert Hancock 	else
11691f4d4af4SGuenter Roeck 		ret = sysfs_emit(buf, "%lld\n", pmbus_reg2data(data, sensor));
117018e8db7fSRobert Hancock 	mutex_unlock(&data->update_lock);
117118e8db7fSRobert Hancock 	return ret;
11729d2ecfb7SGuenter Roeck }
11739d2ecfb7SGuenter Roeck 
pmbus_set_sensor(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)11749d2ecfb7SGuenter Roeck static ssize_t pmbus_set_sensor(struct device *dev,
11759d2ecfb7SGuenter Roeck 				struct device_attribute *devattr,
11769d2ecfb7SGuenter Roeck 				const char *buf, size_t count)
11779d2ecfb7SGuenter Roeck {
1178c3b7cddcSGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev->parent);
11799d2ecfb7SGuenter Roeck 	struct pmbus_data *data = i2c_get_clientdata(client);
1180e1e081a7SGuenter Roeck 	struct pmbus_sensor *sensor = to_pmbus_sensor(devattr);
11819d2ecfb7SGuenter Roeck 	ssize_t rv = count;
11828ba75b20SJosh Lehan 	s64 val;
11839d2ecfb7SGuenter Roeck 	int ret;
11849d2ecfb7SGuenter Roeck 	u16 regval;
11859d2ecfb7SGuenter Roeck 
11868ba75b20SJosh Lehan 	if (kstrtos64(buf, 10, &val) < 0)
11879d2ecfb7SGuenter Roeck 		return -EINVAL;
11889d2ecfb7SGuenter Roeck 
11899d2ecfb7SGuenter Roeck 	mutex_lock(&data->update_lock);
1190daa436e6SGuenter Roeck 	regval = pmbus_data2reg(data, sensor, val);
119146243f3aSGuenter Roeck 	ret = _pmbus_write_word_data(client, sensor->page, sensor->reg, regval);
11929d2ecfb7SGuenter Roeck 	if (ret < 0)
11939d2ecfb7SGuenter Roeck 		rv = ret;
11949d2ecfb7SGuenter Roeck 	else
11951ae5aaf5SErik Rosen 		sensor->data = -ENODATA;
11969d2ecfb7SGuenter Roeck 	mutex_unlock(&data->update_lock);
11979d2ecfb7SGuenter Roeck 	return rv;
11989d2ecfb7SGuenter Roeck }
11999d2ecfb7SGuenter Roeck 
pmbus_show_label(struct device * dev,struct device_attribute * da,char * buf)12009d2ecfb7SGuenter Roeck static ssize_t pmbus_show_label(struct device *dev,
12019d2ecfb7SGuenter Roeck 				struct device_attribute *da, char *buf)
12029d2ecfb7SGuenter Roeck {
12030328461eSGuenter Roeck 	struct pmbus_label *label = to_pmbus_label(da);
12049d2ecfb7SGuenter Roeck 
12051f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%s\n", label->label);
12060328461eSGuenter Roeck }
12070328461eSGuenter Roeck 
pmbus_add_attribute(struct pmbus_data * data,struct attribute * attr)120885cfb3a8SGuenter Roeck static int pmbus_add_attribute(struct pmbus_data *data, struct attribute *attr)
120985cfb3a8SGuenter Roeck {
121085cfb3a8SGuenter Roeck 	if (data->num_attributes >= data->max_attributes - 1) {
12116975404fSDavid Woodhouse 		int new_max_attrs = data->max_attributes + PMBUS_ATTR_ALLOC_SIZE;
1212c5f75484SJames Clark 		void *new_attrs = devm_krealloc_array(data->dev, data->group.attrs,
1213c5f75484SJames Clark 						      new_max_attrs, sizeof(void *),
12146975404fSDavid Woodhouse 						      GFP_KERNEL);
12156975404fSDavid Woodhouse 		if (!new_attrs)
121685cfb3a8SGuenter Roeck 			return -ENOMEM;
12176975404fSDavid Woodhouse 		data->group.attrs = new_attrs;
12186975404fSDavid Woodhouse 		data->max_attributes = new_max_attrs;
121985cfb3a8SGuenter Roeck 	}
122085cfb3a8SGuenter Roeck 
122185cfb3a8SGuenter Roeck 	data->group.attrs[data->num_attributes++] = attr;
122285cfb3a8SGuenter Roeck 	data->group.attrs[data->num_attributes] = NULL;
122385cfb3a8SGuenter Roeck 	return 0;
122485cfb3a8SGuenter Roeck }
122585cfb3a8SGuenter Roeck 
pmbus_dev_attr_init(struct device_attribute * dev_attr,const char * name,umode_t mode,ssize_t (* show)(struct device * dev,struct device_attribute * attr,char * buf),ssize_t (* store)(struct device * dev,struct device_attribute * attr,const char * buf,size_t count))12260328461eSGuenter Roeck static void pmbus_dev_attr_init(struct device_attribute *dev_attr,
12270328461eSGuenter Roeck 				const char *name,
12280328461eSGuenter Roeck 				umode_t mode,
12290328461eSGuenter Roeck 				ssize_t (*show)(struct device *dev,
12300328461eSGuenter Roeck 						struct device_attribute *attr,
12310328461eSGuenter Roeck 						char *buf),
12320328461eSGuenter Roeck 				ssize_t (*store)(struct device *dev,
12330328461eSGuenter Roeck 						 struct device_attribute *attr,
12340328461eSGuenter Roeck 						 const char *buf, size_t count))
12350328461eSGuenter Roeck {
12360328461eSGuenter Roeck 	sysfs_attr_init(&dev_attr->attr);
12370328461eSGuenter Roeck 	dev_attr->attr.name = name;
12380328461eSGuenter Roeck 	dev_attr->attr.mode = mode;
12390328461eSGuenter Roeck 	dev_attr->show = show;
12400328461eSGuenter Roeck 	dev_attr->store = store;
12419d2ecfb7SGuenter Roeck }
12429d2ecfb7SGuenter Roeck 
pmbus_attr_init(struct sensor_device_attribute * a,const char * name,umode_t mode,ssize_t (* show)(struct device * dev,struct device_attribute * attr,char * buf),ssize_t (* store)(struct device * dev,struct device_attribute * attr,const char * buf,size_t count),int idx)1243973018b1SGuenter Roeck static void pmbus_attr_init(struct sensor_device_attribute *a,
1244973018b1SGuenter Roeck 			    const char *name,
1245973018b1SGuenter Roeck 			    umode_t mode,
1246973018b1SGuenter Roeck 			    ssize_t (*show)(struct device *dev,
1247973018b1SGuenter Roeck 					    struct device_attribute *attr,
1248973018b1SGuenter Roeck 					    char *buf),
1249973018b1SGuenter Roeck 			    ssize_t (*store)(struct device *dev,
1250973018b1SGuenter Roeck 					     struct device_attribute *attr,
1251973018b1SGuenter Roeck 					     const char *buf, size_t count),
1252973018b1SGuenter Roeck 			    int idx)
1253973018b1SGuenter Roeck {
12540328461eSGuenter Roeck 	pmbus_dev_attr_init(&a->dev_attr, name, mode, show, store);
1255973018b1SGuenter Roeck 	a->index = idx;
1256973018b1SGuenter Roeck }
12579d2ecfb7SGuenter Roeck 
pmbus_add_boolean(struct pmbus_data * data,const char * name,const char * type,int seq,struct pmbus_sensor * s1,struct pmbus_sensor * s2,u8 page,u16 reg,u16 mask)12580328461eSGuenter Roeck static int pmbus_add_boolean(struct pmbus_data *data,
12599d2ecfb7SGuenter Roeck 			     const char *name, const char *type, int seq,
1260663834f3SGuenter Roeck 			     struct pmbus_sensor *s1,
1261663834f3SGuenter Roeck 			     struct pmbus_sensor *s2,
1262a919ba06SGuenter Roeck 			     u8 page, u16 reg, u16 mask)
12639d2ecfb7SGuenter Roeck {
12649d2ecfb7SGuenter Roeck 	struct pmbus_boolean *boolean;
1265973018b1SGuenter Roeck 	struct sensor_device_attribute *a;
12669d2ecfb7SGuenter Roeck 
126704e216d1SGuenter Roeck 	if (WARN((s1 && !s2) || (!s1 && s2), "Bad s1/s2 parameters\n"))
126804e216d1SGuenter Roeck 		return -EINVAL;
126904e216d1SGuenter Roeck 
12700328461eSGuenter Roeck 	boolean = devm_kzalloc(data->dev, sizeof(*boolean), GFP_KERNEL);
12710328461eSGuenter Roeck 	if (!boolean)
12720328461eSGuenter Roeck 		return -ENOMEM;
12730328461eSGuenter Roeck 
1274973018b1SGuenter Roeck 	a = &boolean->attribute;
12759d2ecfb7SGuenter Roeck 
12769d2ecfb7SGuenter Roeck 	snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s",
12779d2ecfb7SGuenter Roeck 		 name, seq, type);
1278663834f3SGuenter Roeck 	boolean->s1 = s1;
1279663834f3SGuenter Roeck 	boolean->s2 = s2;
12803cf10282SGuenter Roeck 	pmbus_attr_init(a, boolean->name, 0444, pmbus_show_boolean, NULL,
1281a919ba06SGuenter Roeck 			pb_reg_to_index(page, reg, mask));
12820328461eSGuenter Roeck 
128385cfb3a8SGuenter Roeck 	return pmbus_add_attribute(data, &a->dev_attr.attr);
12849d2ecfb7SGuenter Roeck }
12859d2ecfb7SGuenter Roeck 
12863aa74796SEduardo Valentin /* of thermal for pmbus temperature sensors */
12873aa74796SEduardo Valentin struct pmbus_thermal_data {
12883aa74796SEduardo Valentin 	struct pmbus_data *pmbus_data;
12893aa74796SEduardo Valentin 	struct pmbus_sensor *sensor;
12903aa74796SEduardo Valentin };
12913aa74796SEduardo Valentin 
pmbus_thermal_get_temp(struct thermal_zone_device * tz,int * temp)1292613ed3f6SDaniel Lezcano static int pmbus_thermal_get_temp(struct thermal_zone_device *tz, int *temp)
12933aa74796SEduardo Valentin {
12940ce637a5SDaniel Lezcano 	struct pmbus_thermal_data *tdata = thermal_zone_device_priv(tz);
12953aa74796SEduardo Valentin 	struct pmbus_sensor *sensor = tdata->sensor;
12963aa74796SEduardo Valentin 	struct pmbus_data *pmbus_data = tdata->pmbus_data;
12973aa74796SEduardo Valentin 	struct i2c_client *client = to_i2c_client(pmbus_data->dev);
12983aa74796SEduardo Valentin 	struct device *dev = pmbus_data->hwmon_dev;
12993aa74796SEduardo Valentin 	int ret = 0;
13003aa74796SEduardo Valentin 
13013aa74796SEduardo Valentin 	if (!dev) {
13023aa74796SEduardo Valentin 		/* May not even get to hwmon yet */
13033aa74796SEduardo Valentin 		*temp = 0;
13043aa74796SEduardo Valentin 		return 0;
13053aa74796SEduardo Valentin 	}
13063aa74796SEduardo Valentin 
13073aa74796SEduardo Valentin 	mutex_lock(&pmbus_data->update_lock);
13083aa74796SEduardo Valentin 	pmbus_update_sensor_data(client, sensor);
13093aa74796SEduardo Valentin 	if (sensor->data < 0)
13103aa74796SEduardo Valentin 		ret = sensor->data;
13113aa74796SEduardo Valentin 	else
13123aa74796SEduardo Valentin 		*temp = (int)pmbus_reg2data(pmbus_data, sensor);
13133aa74796SEduardo Valentin 	mutex_unlock(&pmbus_data->update_lock);
13143aa74796SEduardo Valentin 
13153aa74796SEduardo Valentin 	return ret;
13163aa74796SEduardo Valentin }
13173aa74796SEduardo Valentin 
1318613ed3f6SDaniel Lezcano static const struct thermal_zone_device_ops pmbus_thermal_ops = {
13193aa74796SEduardo Valentin 	.get_temp = pmbus_thermal_get_temp,
13203aa74796SEduardo Valentin };
13213aa74796SEduardo Valentin 
pmbus_thermal_add_sensor(struct pmbus_data * pmbus_data,struct pmbus_sensor * sensor,int index)13223aa74796SEduardo Valentin static int pmbus_thermal_add_sensor(struct pmbus_data *pmbus_data,
13233aa74796SEduardo Valentin 				    struct pmbus_sensor *sensor, int index)
13243aa74796SEduardo Valentin {
13253aa74796SEduardo Valentin 	struct device *dev = pmbus_data->dev;
13263aa74796SEduardo Valentin 	struct pmbus_thermal_data *tdata;
13273aa74796SEduardo Valentin 	struct thermal_zone_device *tzd;
13283aa74796SEduardo Valentin 
13293aa74796SEduardo Valentin 	tdata = devm_kzalloc(dev, sizeof(*tdata), GFP_KERNEL);
13303aa74796SEduardo Valentin 	if (!tdata)
13313aa74796SEduardo Valentin 		return -ENOMEM;
13323aa74796SEduardo Valentin 
13333aa74796SEduardo Valentin 	tdata->sensor = sensor;
13343aa74796SEduardo Valentin 	tdata->pmbus_data = pmbus_data;
13353aa74796SEduardo Valentin 
1336613ed3f6SDaniel Lezcano 	tzd = devm_thermal_of_zone_register(dev, index, tdata,
13373aa74796SEduardo Valentin 					    &pmbus_thermal_ops);
13383aa74796SEduardo Valentin 	/*
13393aa74796SEduardo Valentin 	 * If CONFIG_THERMAL_OF is disabled, this returns -ENODEV,
13403aa74796SEduardo Valentin 	 * so ignore that error but forward any other error.
13413aa74796SEduardo Valentin 	 */
13423aa74796SEduardo Valentin 	if (IS_ERR(tzd) && (PTR_ERR(tzd) != -ENODEV))
13433aa74796SEduardo Valentin 		return PTR_ERR(tzd);
13443aa74796SEduardo Valentin 
13453aa74796SEduardo Valentin 	return 0;
13463aa74796SEduardo Valentin }
13473aa74796SEduardo Valentin 
pmbus_add_sensor(struct pmbus_data * data,const char * name,const char * type,int seq,int page,int phase,int reg,enum pmbus_sensor_classes class,bool update,bool readonly,bool convert)1348663834f3SGuenter Roeck static struct pmbus_sensor *pmbus_add_sensor(struct pmbus_data *data,
13499d2ecfb7SGuenter Roeck 					     const char *name, const char *type,
135016358542SGuenter Roeck 					     int seq, int page, int phase,
135116358542SGuenter Roeck 					     int reg,
1352663834f3SGuenter Roeck 					     enum pmbus_sensor_classes class,
1353d206636eSAndrew Jeffery 					     bool update, bool readonly,
1354d206636eSAndrew Jeffery 					     bool convert)
13559d2ecfb7SGuenter Roeck {
13569d2ecfb7SGuenter Roeck 	struct pmbus_sensor *sensor;
1357e1e081a7SGuenter Roeck 	struct device_attribute *a;
13589d2ecfb7SGuenter Roeck 
1359e1e081a7SGuenter Roeck 	sensor = devm_kzalloc(data->dev, sizeof(*sensor), GFP_KERNEL);
1360e1e081a7SGuenter Roeck 	if (!sensor)
1361e1e081a7SGuenter Roeck 		return NULL;
1362973018b1SGuenter Roeck 	a = &sensor->attribute;
1363973018b1SGuenter Roeck 
1364d206636eSAndrew Jeffery 	if (type)
13659d2ecfb7SGuenter Roeck 		snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s",
13669d2ecfb7SGuenter Roeck 			 name, seq, type);
1367d206636eSAndrew Jeffery 	else
1368d206636eSAndrew Jeffery 		snprintf(sensor->name, sizeof(sensor->name), "%s%d",
1369d206636eSAndrew Jeffery 			 name, seq);
1370d206636eSAndrew Jeffery 
13719e347728SGuenter Roeck 	if (data->flags & PMBUS_WRITE_PROTECTED)
13729e347728SGuenter Roeck 		readonly = true;
13739e347728SGuenter Roeck 
13749d2ecfb7SGuenter Roeck 	sensor->page = page;
137516358542SGuenter Roeck 	sensor->phase = phase;
13769d2ecfb7SGuenter Roeck 	sensor->reg = reg;
13779d2ecfb7SGuenter Roeck 	sensor->class = class;
13789d2ecfb7SGuenter Roeck 	sensor->update = update;
1379d206636eSAndrew Jeffery 	sensor->convert = convert;
1380a919ba06SGuenter Roeck 	sensor->data = -ENODATA;
1381e1e081a7SGuenter Roeck 	pmbus_dev_attr_init(a, sensor->name,
13823cf10282SGuenter Roeck 			    readonly ? 0444 : 0644,
1383e1e081a7SGuenter Roeck 			    pmbus_show_sensor, pmbus_set_sensor);
1384973018b1SGuenter Roeck 
138585cfb3a8SGuenter Roeck 	if (pmbus_add_attribute(data, &a->attr))
138685cfb3a8SGuenter Roeck 		return NULL;
138785cfb3a8SGuenter Roeck 
1388e1e081a7SGuenter Roeck 	sensor->next = data->sensors;
1389e1e081a7SGuenter Roeck 	data->sensors = sensor;
1390663834f3SGuenter Roeck 
13913aa74796SEduardo Valentin 	/* temperature sensors with _input values are registered with thermal */
13923aa74796SEduardo Valentin 	if (class == PSC_TEMPERATURE && strcmp(type, "input") == 0)
13933aa74796SEduardo Valentin 		pmbus_thermal_add_sensor(data, sensor, seq);
13943aa74796SEduardo Valentin 
1395663834f3SGuenter Roeck 	return sensor;
13969d2ecfb7SGuenter Roeck }
13979d2ecfb7SGuenter Roeck 
pmbus_add_label(struct pmbus_data * data,const char * name,int seq,const char * lstring,int index,int phase)13980328461eSGuenter Roeck static int pmbus_add_label(struct pmbus_data *data,
13999d2ecfb7SGuenter Roeck 			   const char *name, int seq,
140016358542SGuenter Roeck 			   const char *lstring, int index, int phase)
14019d2ecfb7SGuenter Roeck {
14029d2ecfb7SGuenter Roeck 	struct pmbus_label *label;
14030328461eSGuenter Roeck 	struct device_attribute *a;
14049d2ecfb7SGuenter Roeck 
14050328461eSGuenter Roeck 	label = devm_kzalloc(data->dev, sizeof(*label), GFP_KERNEL);
14060328461eSGuenter Roeck 	if (!label)
14070328461eSGuenter Roeck 		return -ENOMEM;
14080328461eSGuenter Roeck 
1409973018b1SGuenter Roeck 	a = &label->attribute;
1410973018b1SGuenter Roeck 
14119d2ecfb7SGuenter Roeck 	snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq);
141216358542SGuenter Roeck 	if (!index) {
141316358542SGuenter Roeck 		if (phase == 0xff)
141416358542SGuenter Roeck 			strncpy(label->label, lstring,
141516358542SGuenter Roeck 				sizeof(label->label) - 1);
14169d2ecfb7SGuenter Roeck 		else
141716358542SGuenter Roeck 			snprintf(label->label, sizeof(label->label), "%s.%d",
141816358542SGuenter Roeck 				 lstring, phase);
141916358542SGuenter Roeck 	} else {
142016358542SGuenter Roeck 		if (phase == 0xff)
142116358542SGuenter Roeck 			snprintf(label->label, sizeof(label->label), "%s%d",
142216358542SGuenter Roeck 				 lstring, index);
142316358542SGuenter Roeck 		else
142416358542SGuenter Roeck 			snprintf(label->label, sizeof(label->label), "%s%d.%d",
142516358542SGuenter Roeck 				 lstring, index, phase);
142616358542SGuenter Roeck 	}
14279d2ecfb7SGuenter Roeck 
14283cf10282SGuenter Roeck 	pmbus_dev_attr_init(a, label->name, 0444, pmbus_show_label, NULL);
142985cfb3a8SGuenter Roeck 	return pmbus_add_attribute(data, &a->attr);
14309d2ecfb7SGuenter Roeck }
14319d2ecfb7SGuenter Roeck 
14329d2ecfb7SGuenter Roeck /*
14339d2ecfb7SGuenter Roeck  * Search for attributes. Allocate sensors, booleans, and labels as needed.
14349d2ecfb7SGuenter Roeck  */
14359d2ecfb7SGuenter Roeck 
14369d2ecfb7SGuenter Roeck /*
14379d2ecfb7SGuenter Roeck  * The pmbus_limit_attr structure describes a single limit attribute
14389d2ecfb7SGuenter Roeck  * and its associated alarm attribute.
14399d2ecfb7SGuenter Roeck  */
14409d2ecfb7SGuenter Roeck struct pmbus_limit_attr {
14416f183d33SGuenter Roeck 	u16 reg;		/* Limit register */
1442f880b12cSGuenter Roeck 	u16 sbit;		/* Alarm attribute status bit */
14436f183d33SGuenter Roeck 	bool update;		/* True if register needs updates */
144440257b95SGuenter Roeck 	bool low;		/* True if low limit; for limits with compare
144540257b95SGuenter Roeck 				   functions only */
14469d2ecfb7SGuenter Roeck 	const char *attr;	/* Attribute name */
14479d2ecfb7SGuenter Roeck 	const char *alarm;	/* Alarm attribute name */
14489d2ecfb7SGuenter Roeck };
14499d2ecfb7SGuenter Roeck 
14509d2ecfb7SGuenter Roeck /*
14519d2ecfb7SGuenter Roeck  * The pmbus_sensor_attr structure describes one sensor attribute. This
14529d2ecfb7SGuenter Roeck  * description includes a reference to the associated limit attributes.
14539d2ecfb7SGuenter Roeck  */
14549d2ecfb7SGuenter Roeck struct pmbus_sensor_attr {
1455aebcbbfcSGuenter Roeck 	u16 reg;			/* sensor register */
1456a66a6eb9SEdward A. James 	u16 gbit;			/* generic status bit */
1457f880b12cSGuenter Roeck 	u8 nlimit;			/* # of limit registers */
14589d2ecfb7SGuenter Roeck 	enum pmbus_sensor_classes class;/* sensor class */
14599d2ecfb7SGuenter Roeck 	const char *label;		/* sensor label */
14609d2ecfb7SGuenter Roeck 	bool paged;			/* true if paged sensor */
14619d2ecfb7SGuenter Roeck 	bool update;			/* true if update needed */
14629d2ecfb7SGuenter Roeck 	bool compare;			/* true if compare function needed */
14639d2ecfb7SGuenter Roeck 	u32 func;			/* sensor mask */
14649d2ecfb7SGuenter Roeck 	u32 sfunc;			/* sensor status mask */
1465a919ba06SGuenter Roeck 	int sreg;			/* status register */
14669d2ecfb7SGuenter Roeck 	const struct pmbus_limit_attr *limit;/* limit registers */
14679d2ecfb7SGuenter Roeck };
14689d2ecfb7SGuenter Roeck 
14699d2ecfb7SGuenter Roeck /*
14709d2ecfb7SGuenter Roeck  * Add a set of limit attributes and, if supported, the associated
14719d2ecfb7SGuenter Roeck  * alarm attributes.
14720328461eSGuenter Roeck  * returns 0 if no alarm register found, 1 if an alarm register was found,
14730328461eSGuenter Roeck  * < 0 on errors.
14749d2ecfb7SGuenter Roeck  */
pmbus_add_limit_attrs(struct i2c_client * client,struct pmbus_data * data,const struct pmbus_driver_info * info,const char * name,int index,int page,struct pmbus_sensor * base,const struct pmbus_sensor_attr * attr)14750328461eSGuenter Roeck static int pmbus_add_limit_attrs(struct i2c_client *client,
14769d2ecfb7SGuenter Roeck 				 struct pmbus_data *data,
14779d2ecfb7SGuenter Roeck 				 const struct pmbus_driver_info *info,
14789d2ecfb7SGuenter Roeck 				 const char *name, int index, int page,
1479663834f3SGuenter Roeck 				 struct pmbus_sensor *base,
14809d2ecfb7SGuenter Roeck 				 const struct pmbus_sensor_attr *attr)
14819d2ecfb7SGuenter Roeck {
14829d2ecfb7SGuenter Roeck 	const struct pmbus_limit_attr *l = attr->limit;
14839d2ecfb7SGuenter Roeck 	int nlimit = attr->nlimit;
14840328461eSGuenter Roeck 	int have_alarm = 0;
1485663834f3SGuenter Roeck 	int i, ret;
1486663834f3SGuenter Roeck 	struct pmbus_sensor *curr;
14879d2ecfb7SGuenter Roeck 
14889d2ecfb7SGuenter Roeck 	for (i = 0; i < nlimit; i++) {
14899d2ecfb7SGuenter Roeck 		if (pmbus_check_word_register(client, page, l->reg)) {
1490663834f3SGuenter Roeck 			curr = pmbus_add_sensor(data, name, l->attr, index,
149116358542SGuenter Roeck 						page, 0xff, l->reg, attr->class,
14926f183d33SGuenter Roeck 						attr->update || l->update,
1493d206636eSAndrew Jeffery 						false, true);
1494e1e081a7SGuenter Roeck 			if (!curr)
1495e1e081a7SGuenter Roeck 				return -ENOMEM;
14966f183d33SGuenter Roeck 			if (l->sbit && (info->func[page] & attr->sfunc)) {
1497663834f3SGuenter Roeck 				ret = pmbus_add_boolean(data, name,
14989d2ecfb7SGuenter Roeck 					l->alarm, index,
1499663834f3SGuenter Roeck 					attr->compare ?  l->low ? curr : base
1500663834f3SGuenter Roeck 						      : NULL,
1501663834f3SGuenter Roeck 					attr->compare ? l->low ? base : curr
1502663834f3SGuenter Roeck 						      : NULL,
1503a919ba06SGuenter Roeck 					page, attr->sreg, l->sbit);
15040328461eSGuenter Roeck 				if (ret)
15050328461eSGuenter Roeck 					return ret;
15060328461eSGuenter Roeck 				have_alarm = 1;
15079d2ecfb7SGuenter Roeck 			}
15089d2ecfb7SGuenter Roeck 		}
15099d2ecfb7SGuenter Roeck 		l++;
15109d2ecfb7SGuenter Roeck 	}
15119d2ecfb7SGuenter Roeck 	return have_alarm;
15129d2ecfb7SGuenter Roeck }
15139d2ecfb7SGuenter Roeck 
pmbus_add_sensor_attrs_one(struct i2c_client * client,struct pmbus_data * data,const struct pmbus_driver_info * info,const char * name,int index,int page,int phase,const struct pmbus_sensor_attr * attr,bool paged)15140328461eSGuenter Roeck static int pmbus_add_sensor_attrs_one(struct i2c_client *client,
15159d2ecfb7SGuenter Roeck 				      struct pmbus_data *data,
15169d2ecfb7SGuenter Roeck 				      const struct pmbus_driver_info *info,
15179d2ecfb7SGuenter Roeck 				      const char *name,
151816358542SGuenter Roeck 				      int index, int page, int phase,
15194a60570dSRobert Hancock 				      const struct pmbus_sensor_attr *attr,
15204a60570dSRobert Hancock 				      bool paged)
15219d2ecfb7SGuenter Roeck {
1522663834f3SGuenter Roeck 	struct pmbus_sensor *base;
1523c159be9eSEdward A. James 	bool upper = !!(attr->gbit & 0xff00);	/* need to check STATUS_WORD */
15240328461eSGuenter Roeck 	int ret;
15259d2ecfb7SGuenter Roeck 
15260328461eSGuenter Roeck 	if (attr->label) {
15270328461eSGuenter Roeck 		ret = pmbus_add_label(data, name, index, attr->label,
152816358542SGuenter Roeck 				      paged ? page + 1 : 0, phase);
15290328461eSGuenter Roeck 		if (ret)
15300328461eSGuenter Roeck 			return ret;
15310328461eSGuenter Roeck 	}
153216358542SGuenter Roeck 	base = pmbus_add_sensor(data, name, "input", index, page, phase,
153316358542SGuenter Roeck 				attr->reg, attr->class, true, true, true);
1534e1e081a7SGuenter Roeck 	if (!base)
1535e1e081a7SGuenter Roeck 		return -ENOMEM;
153616358542SGuenter Roeck 	/* No limit and alarm attributes for phase specific sensors */
153716358542SGuenter Roeck 	if (attr->sfunc && phase == 0xff) {
15380328461eSGuenter Roeck 		ret = pmbus_add_limit_attrs(client, data, info, name,
1539663834f3SGuenter Roeck 					    index, page, base, attr);
15400328461eSGuenter Roeck 		if (ret < 0)
15410328461eSGuenter Roeck 			return ret;
15429d2ecfb7SGuenter Roeck 		/*
15439d2ecfb7SGuenter Roeck 		 * Add generic alarm attribute only if there are no individual
1544c6bfb767SGuenter Roeck 		 * alarm attributes, if there is a global alarm bit, and if
1545c159be9eSEdward A. James 		 * the generic status register (word or byte, depending on
1546c159be9eSEdward A. James 		 * which global bit is set) for this page is accessible.
15479d2ecfb7SGuenter Roeck 		 */
15480328461eSGuenter Roeck 		if (!ret && attr->gbit &&
1549906ace80SJiapeng Zhong 		    (!upper || data->has_status_word) &&
1550cbcdec62SEdward A. James 		    pmbus_check_status_register(client, page)) {
1551663834f3SGuenter Roeck 			ret = pmbus_add_boolean(data, name, "alarm", index,
1552663834f3SGuenter Roeck 						NULL, NULL,
1553a919ba06SGuenter Roeck 						page, PMBUS_STATUS_WORD,
15549d2ecfb7SGuenter Roeck 						attr->gbit);
15550328461eSGuenter Roeck 			if (ret)
15560328461eSGuenter Roeck 				return ret;
15579d2ecfb7SGuenter Roeck 		}
15589d2ecfb7SGuenter Roeck 	}
15590328461eSGuenter Roeck 	return 0;
15600328461eSGuenter Roeck }
15619d2ecfb7SGuenter Roeck 
pmbus_sensor_is_paged(const struct pmbus_driver_info * info,const struct pmbus_sensor_attr * attr)15624a60570dSRobert Hancock static bool pmbus_sensor_is_paged(const struct pmbus_driver_info *info,
15634a60570dSRobert Hancock 				  const struct pmbus_sensor_attr *attr)
15644a60570dSRobert Hancock {
15654a60570dSRobert Hancock 	int p;
15664a60570dSRobert Hancock 
15674a60570dSRobert Hancock 	if (attr->paged)
15684a60570dSRobert Hancock 		return true;
15694a60570dSRobert Hancock 
15704a60570dSRobert Hancock 	/*
15714a60570dSRobert Hancock 	 * Some attributes may be present on more than one page despite
15724a60570dSRobert Hancock 	 * not being marked with the paged attribute. If that is the case,
15734a60570dSRobert Hancock 	 * then treat the sensor as being paged and add the page suffix to the
15744a60570dSRobert Hancock 	 * attribute name.
15754a60570dSRobert Hancock 	 * We don't just add the paged attribute to all such attributes, in
15764a60570dSRobert Hancock 	 * order to maintain the un-suffixed labels in the case where the
15774a60570dSRobert Hancock 	 * attribute is only on page 0.
15784a60570dSRobert Hancock 	 */
15794a60570dSRobert Hancock 	for (p = 1; p < info->pages; p++) {
15804a60570dSRobert Hancock 		if (info->func[p] & attr->func)
15814a60570dSRobert Hancock 			return true;
15824a60570dSRobert Hancock 	}
15834a60570dSRobert Hancock 	return false;
15844a60570dSRobert Hancock }
15854a60570dSRobert Hancock 
pmbus_add_sensor_attrs(struct i2c_client * client,struct pmbus_data * data,const char * name,const struct pmbus_sensor_attr * attrs,int nattrs)15860328461eSGuenter Roeck static int pmbus_add_sensor_attrs(struct i2c_client *client,
15879d2ecfb7SGuenter Roeck 				  struct pmbus_data *data,
15889d2ecfb7SGuenter Roeck 				  const char *name,
15899d2ecfb7SGuenter Roeck 				  const struct pmbus_sensor_attr *attrs,
15909d2ecfb7SGuenter Roeck 				  int nattrs)
15919d2ecfb7SGuenter Roeck {
15929d2ecfb7SGuenter Roeck 	const struct pmbus_driver_info *info = data->info;
15939d2ecfb7SGuenter Roeck 	int index, i;
15940328461eSGuenter Roeck 	int ret;
15959d2ecfb7SGuenter Roeck 
15969d2ecfb7SGuenter Roeck 	index = 1;
15979d2ecfb7SGuenter Roeck 	for (i = 0; i < nattrs; i++) {
15989d2ecfb7SGuenter Roeck 		int page, pages;
15994a60570dSRobert Hancock 		bool paged = pmbus_sensor_is_paged(info, attrs);
16009d2ecfb7SGuenter Roeck 
16014a60570dSRobert Hancock 		pages = paged ? info->pages : 1;
16029d2ecfb7SGuenter Roeck 		for (page = 0; page < pages; page++) {
16035e86f128SErik Rosen 			if (info->func[page] & attrs->func) {
16040328461eSGuenter Roeck 				ret = pmbus_add_sensor_attrs_one(client, data, info,
16050328461eSGuenter Roeck 								 name, index, page,
160616358542SGuenter Roeck 								 0xff, attrs, paged);
16070328461eSGuenter Roeck 				if (ret)
16080328461eSGuenter Roeck 					return ret;
16099d2ecfb7SGuenter Roeck 				index++;
16105e86f128SErik Rosen 			}
161116358542SGuenter Roeck 			if (info->phases[page]) {
161216358542SGuenter Roeck 				int phase;
161316358542SGuenter Roeck 
161416358542SGuenter Roeck 				for (phase = 0; phase < info->phases[page];
161516358542SGuenter Roeck 				     phase++) {
161616358542SGuenter Roeck 					if (!(info->pfunc[phase] & attrs->func))
161716358542SGuenter Roeck 						continue;
161816358542SGuenter Roeck 					ret = pmbus_add_sensor_attrs_one(client,
161916358542SGuenter Roeck 						data, info, name, index, page,
162016358542SGuenter Roeck 						phase, attrs, paged);
162116358542SGuenter Roeck 					if (ret)
162216358542SGuenter Roeck 						return ret;
162316358542SGuenter Roeck 					index++;
162416358542SGuenter Roeck 				}
162516358542SGuenter Roeck 			}
16269d2ecfb7SGuenter Roeck 		}
16279d2ecfb7SGuenter Roeck 		attrs++;
16289d2ecfb7SGuenter Roeck 	}
16290328461eSGuenter Roeck 	return 0;
16309d2ecfb7SGuenter Roeck }
16319d2ecfb7SGuenter Roeck 
16329d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr vin_limit_attrs[] = {
16339d2ecfb7SGuenter Roeck 	{
16349d2ecfb7SGuenter Roeck 		.reg = PMBUS_VIN_UV_WARN_LIMIT,
16359d2ecfb7SGuenter Roeck 		.attr = "min",
16369d2ecfb7SGuenter Roeck 		.alarm = "min_alarm",
16379d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_UV_WARNING,
16389d2ecfb7SGuenter Roeck 	}, {
16399d2ecfb7SGuenter Roeck 		.reg = PMBUS_VIN_UV_FAULT_LIMIT,
16409d2ecfb7SGuenter Roeck 		.attr = "lcrit",
16419d2ecfb7SGuenter Roeck 		.alarm = "lcrit_alarm",
1642a5436af5SBrandon Wyman 		.sbit = PB_VOLTAGE_UV_FAULT | PB_VOLTAGE_VIN_OFF,
16439d2ecfb7SGuenter Roeck 	}, {
16449d2ecfb7SGuenter Roeck 		.reg = PMBUS_VIN_OV_WARN_LIMIT,
16459d2ecfb7SGuenter Roeck 		.attr = "max",
16469d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
16479d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_OV_WARNING,
16489d2ecfb7SGuenter Roeck 	}, {
16499d2ecfb7SGuenter Roeck 		.reg = PMBUS_VIN_OV_FAULT_LIMIT,
16509d2ecfb7SGuenter Roeck 		.attr = "crit",
16519d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
16529d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_OV_FAULT,
16536f183d33SGuenter Roeck 	}, {
16546f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VIN_AVG,
16556f183d33SGuenter Roeck 		.update = true,
16566f183d33SGuenter Roeck 		.attr = "average",
16576f183d33SGuenter Roeck 	}, {
16586f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VIN_MIN,
16596f183d33SGuenter Roeck 		.update = true,
16606f183d33SGuenter Roeck 		.attr = "lowest",
16616f183d33SGuenter Roeck 	}, {
16626f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VIN_MAX,
16636f183d33SGuenter Roeck 		.update = true,
16646f183d33SGuenter Roeck 		.attr = "highest",
16656f183d33SGuenter Roeck 	}, {
16666f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_VIN_HISTORY,
16676f183d33SGuenter Roeck 		.attr = "reset_history",
1668787c095eSZbigniew Lukwinski 	}, {
1669787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_VIN_MIN,
1670787c095eSZbigniew Lukwinski 		.attr = "rated_min",
1671787c095eSZbigniew Lukwinski 	}, {
1672787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_VIN_MAX,
1673787c095eSZbigniew Lukwinski 		.attr = "rated_max",
16749d2ecfb7SGuenter Roeck 	},
16759d2ecfb7SGuenter Roeck };
16769d2ecfb7SGuenter Roeck 
1677aebcbbfcSGuenter Roeck static const struct pmbus_limit_attr vmon_limit_attrs[] = {
1678aebcbbfcSGuenter Roeck 	{
1679aebcbbfcSGuenter Roeck 		.reg = PMBUS_VIRT_VMON_UV_WARN_LIMIT,
1680aebcbbfcSGuenter Roeck 		.attr = "min",
1681aebcbbfcSGuenter Roeck 		.alarm = "min_alarm",
1682aebcbbfcSGuenter Roeck 		.sbit = PB_VOLTAGE_UV_WARNING,
1683aebcbbfcSGuenter Roeck 	}, {
1684aebcbbfcSGuenter Roeck 		.reg = PMBUS_VIRT_VMON_UV_FAULT_LIMIT,
1685aebcbbfcSGuenter Roeck 		.attr = "lcrit",
1686aebcbbfcSGuenter Roeck 		.alarm = "lcrit_alarm",
1687aebcbbfcSGuenter Roeck 		.sbit = PB_VOLTAGE_UV_FAULT,
1688aebcbbfcSGuenter Roeck 	}, {
1689aebcbbfcSGuenter Roeck 		.reg = PMBUS_VIRT_VMON_OV_WARN_LIMIT,
1690aebcbbfcSGuenter Roeck 		.attr = "max",
1691aebcbbfcSGuenter Roeck 		.alarm = "max_alarm",
1692aebcbbfcSGuenter Roeck 		.sbit = PB_VOLTAGE_OV_WARNING,
1693aebcbbfcSGuenter Roeck 	}, {
1694aebcbbfcSGuenter Roeck 		.reg = PMBUS_VIRT_VMON_OV_FAULT_LIMIT,
1695aebcbbfcSGuenter Roeck 		.attr = "crit",
1696aebcbbfcSGuenter Roeck 		.alarm = "crit_alarm",
1697aebcbbfcSGuenter Roeck 		.sbit = PB_VOLTAGE_OV_FAULT,
1698aebcbbfcSGuenter Roeck 	}
1699aebcbbfcSGuenter Roeck };
1700aebcbbfcSGuenter Roeck 
17019d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr vout_limit_attrs[] = {
17029d2ecfb7SGuenter Roeck 	{
17039d2ecfb7SGuenter Roeck 		.reg = PMBUS_VOUT_UV_WARN_LIMIT,
17049d2ecfb7SGuenter Roeck 		.attr = "min",
17059d2ecfb7SGuenter Roeck 		.alarm = "min_alarm",
17069d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_UV_WARNING,
17079d2ecfb7SGuenter Roeck 	}, {
17089d2ecfb7SGuenter Roeck 		.reg = PMBUS_VOUT_UV_FAULT_LIMIT,
17099d2ecfb7SGuenter Roeck 		.attr = "lcrit",
17109d2ecfb7SGuenter Roeck 		.alarm = "lcrit_alarm",
17119d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_UV_FAULT,
17129d2ecfb7SGuenter Roeck 	}, {
17139d2ecfb7SGuenter Roeck 		.reg = PMBUS_VOUT_OV_WARN_LIMIT,
17149d2ecfb7SGuenter Roeck 		.attr = "max",
17159d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
17169d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_OV_WARNING,
17179d2ecfb7SGuenter Roeck 	}, {
17189d2ecfb7SGuenter Roeck 		.reg = PMBUS_VOUT_OV_FAULT_LIMIT,
17199d2ecfb7SGuenter Roeck 		.attr = "crit",
17209d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
17219d2ecfb7SGuenter Roeck 		.sbit = PB_VOLTAGE_OV_FAULT,
17226f183d33SGuenter Roeck 	}, {
17236f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VOUT_AVG,
17246f183d33SGuenter Roeck 		.update = true,
17256f183d33SGuenter Roeck 		.attr = "average",
17266f183d33SGuenter Roeck 	}, {
17276f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VOUT_MIN,
17286f183d33SGuenter Roeck 		.update = true,
17296f183d33SGuenter Roeck 		.attr = "lowest",
17306f183d33SGuenter Roeck 	}, {
17316f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_VOUT_MAX,
17326f183d33SGuenter Roeck 		.update = true,
17336f183d33SGuenter Roeck 		.attr = "highest",
17346f183d33SGuenter Roeck 	}, {
17356f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_VOUT_HISTORY,
17366f183d33SGuenter Roeck 		.attr = "reset_history",
1737787c095eSZbigniew Lukwinski 	}, {
1738787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_VOUT_MIN,
1739787c095eSZbigniew Lukwinski 		.attr = "rated_min",
1740787c095eSZbigniew Lukwinski 	}, {
1741787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_VOUT_MAX,
1742787c095eSZbigniew Lukwinski 		.attr = "rated_max",
1743787c095eSZbigniew Lukwinski 	},
17449d2ecfb7SGuenter Roeck };
17459d2ecfb7SGuenter Roeck 
17469d2ecfb7SGuenter Roeck static const struct pmbus_sensor_attr voltage_attributes[] = {
17479d2ecfb7SGuenter Roeck 	{
17489d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_VIN,
17499d2ecfb7SGuenter Roeck 		.class = PSC_VOLTAGE_IN,
17509d2ecfb7SGuenter Roeck 		.label = "vin",
17519d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_VIN,
17529d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_INPUT,
1753a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_INPUT,
17549d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_VIN_UV,
17559d2ecfb7SGuenter Roeck 		.limit = vin_limit_attrs,
17569d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(vin_limit_attrs),
17579d2ecfb7SGuenter Roeck 	}, {
1758aebcbbfcSGuenter Roeck 		.reg = PMBUS_VIRT_READ_VMON,
1759aebcbbfcSGuenter Roeck 		.class = PSC_VOLTAGE_IN,
1760aebcbbfcSGuenter Roeck 		.label = "vmon",
1761aebcbbfcSGuenter Roeck 		.func = PMBUS_HAVE_VMON,
1762aebcbbfcSGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_VMON,
1763a919ba06SGuenter Roeck 		.sreg = PMBUS_VIRT_STATUS_VMON,
1764aebcbbfcSGuenter Roeck 		.limit = vmon_limit_attrs,
1765aebcbbfcSGuenter Roeck 		.nlimit = ARRAY_SIZE(vmon_limit_attrs),
1766aebcbbfcSGuenter Roeck 	}, {
17679d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_VCAP,
17689d2ecfb7SGuenter Roeck 		.class = PSC_VOLTAGE_IN,
17699d2ecfb7SGuenter Roeck 		.label = "vcap",
17709d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_VCAP,
17719d2ecfb7SGuenter Roeck 	}, {
17729d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_VOUT,
17739d2ecfb7SGuenter Roeck 		.class = PSC_VOLTAGE_OUT,
17749d2ecfb7SGuenter Roeck 		.label = "vout",
17759d2ecfb7SGuenter Roeck 		.paged = true,
17769d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_VOUT,
17779d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_VOUT,
1778a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_VOUT,
17799d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_VOUT_OV,
17809d2ecfb7SGuenter Roeck 		.limit = vout_limit_attrs,
17819d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(vout_limit_attrs),
17829d2ecfb7SGuenter Roeck 	}
17839d2ecfb7SGuenter Roeck };
17849d2ecfb7SGuenter Roeck 
17859d2ecfb7SGuenter Roeck /* Current attributes */
17869d2ecfb7SGuenter Roeck 
17879d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr iin_limit_attrs[] = {
17889d2ecfb7SGuenter Roeck 	{
17899d2ecfb7SGuenter Roeck 		.reg = PMBUS_IIN_OC_WARN_LIMIT,
17909d2ecfb7SGuenter Roeck 		.attr = "max",
17919d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
17929d2ecfb7SGuenter Roeck 		.sbit = PB_IIN_OC_WARNING,
17939d2ecfb7SGuenter Roeck 	}, {
17949d2ecfb7SGuenter Roeck 		.reg = PMBUS_IIN_OC_FAULT_LIMIT,
17959d2ecfb7SGuenter Roeck 		.attr = "crit",
17969d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
17979d2ecfb7SGuenter Roeck 		.sbit = PB_IIN_OC_FAULT,
17986f183d33SGuenter Roeck 	}, {
17996f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IIN_AVG,
18006f183d33SGuenter Roeck 		.update = true,
18016f183d33SGuenter Roeck 		.attr = "average",
18026f183d33SGuenter Roeck 	}, {
18036f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IIN_MIN,
18046f183d33SGuenter Roeck 		.update = true,
18056f183d33SGuenter Roeck 		.attr = "lowest",
18066f183d33SGuenter Roeck 	}, {
18076f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IIN_MAX,
18086f183d33SGuenter Roeck 		.update = true,
18096f183d33SGuenter Roeck 		.attr = "highest",
18106f183d33SGuenter Roeck 	}, {
18116f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_IIN_HISTORY,
18126f183d33SGuenter Roeck 		.attr = "reset_history",
1813787c095eSZbigniew Lukwinski 	}, {
1814787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_IIN_MAX,
1815787c095eSZbigniew Lukwinski 		.attr = "rated_max",
1816787c095eSZbigniew Lukwinski 	},
18179d2ecfb7SGuenter Roeck };
18189d2ecfb7SGuenter Roeck 
18199d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr iout_limit_attrs[] = {
18209d2ecfb7SGuenter Roeck 	{
18219d2ecfb7SGuenter Roeck 		.reg = PMBUS_IOUT_OC_WARN_LIMIT,
18229d2ecfb7SGuenter Roeck 		.attr = "max",
18239d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
18249d2ecfb7SGuenter Roeck 		.sbit = PB_IOUT_OC_WARNING,
18259d2ecfb7SGuenter Roeck 	}, {
18269d2ecfb7SGuenter Roeck 		.reg = PMBUS_IOUT_UC_FAULT_LIMIT,
18279d2ecfb7SGuenter Roeck 		.attr = "lcrit",
18289d2ecfb7SGuenter Roeck 		.alarm = "lcrit_alarm",
18299d2ecfb7SGuenter Roeck 		.sbit = PB_IOUT_UC_FAULT,
18309d2ecfb7SGuenter Roeck 	}, {
18319d2ecfb7SGuenter Roeck 		.reg = PMBUS_IOUT_OC_FAULT_LIMIT,
18329d2ecfb7SGuenter Roeck 		.attr = "crit",
18339d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
18349d2ecfb7SGuenter Roeck 		.sbit = PB_IOUT_OC_FAULT,
18356f183d33SGuenter Roeck 	}, {
18366f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IOUT_AVG,
18376f183d33SGuenter Roeck 		.update = true,
18386f183d33SGuenter Roeck 		.attr = "average",
18396f183d33SGuenter Roeck 	}, {
18406f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IOUT_MIN,
18416f183d33SGuenter Roeck 		.update = true,
18426f183d33SGuenter Roeck 		.attr = "lowest",
18436f183d33SGuenter Roeck 	}, {
18446f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_IOUT_MAX,
18456f183d33SGuenter Roeck 		.update = true,
18466f183d33SGuenter Roeck 		.attr = "highest",
18476f183d33SGuenter Roeck 	}, {
18486f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_IOUT_HISTORY,
18496f183d33SGuenter Roeck 		.attr = "reset_history",
1850787c095eSZbigniew Lukwinski 	}, {
1851787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_IOUT_MAX,
1852787c095eSZbigniew Lukwinski 		.attr = "rated_max",
1853787c095eSZbigniew Lukwinski 	},
18549d2ecfb7SGuenter Roeck };
18559d2ecfb7SGuenter Roeck 
18569d2ecfb7SGuenter Roeck static const struct pmbus_sensor_attr current_attributes[] = {
18579d2ecfb7SGuenter Roeck 	{
18589d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_IIN,
18599d2ecfb7SGuenter Roeck 		.class = PSC_CURRENT_IN,
18609d2ecfb7SGuenter Roeck 		.label = "iin",
18619d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_IIN,
18629d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_INPUT,
1863a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_INPUT,
1864c159be9eSEdward A. James 		.gbit = PB_STATUS_INPUT,
18659d2ecfb7SGuenter Roeck 		.limit = iin_limit_attrs,
18669d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(iin_limit_attrs),
18679d2ecfb7SGuenter Roeck 	}, {
18689d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_IOUT,
18699d2ecfb7SGuenter Roeck 		.class = PSC_CURRENT_OUT,
18709d2ecfb7SGuenter Roeck 		.label = "iout",
18719d2ecfb7SGuenter Roeck 		.paged = true,
18729d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_IOUT,
18739d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_IOUT,
1874a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_IOUT,
18759d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_IOUT_OC,
18769d2ecfb7SGuenter Roeck 		.limit = iout_limit_attrs,
18779d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(iout_limit_attrs),
18789d2ecfb7SGuenter Roeck 	}
18799d2ecfb7SGuenter Roeck };
18809d2ecfb7SGuenter Roeck 
18819d2ecfb7SGuenter Roeck /* Power attributes */
18829d2ecfb7SGuenter Roeck 
18839d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr pin_limit_attrs[] = {
18849d2ecfb7SGuenter Roeck 	{
18859d2ecfb7SGuenter Roeck 		.reg = PMBUS_PIN_OP_WARN_LIMIT,
18869d2ecfb7SGuenter Roeck 		.attr = "max",
18879d2ecfb7SGuenter Roeck 		.alarm = "alarm",
18889d2ecfb7SGuenter Roeck 		.sbit = PB_PIN_OP_WARNING,
18896f183d33SGuenter Roeck 	}, {
18906f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_PIN_AVG,
18916f183d33SGuenter Roeck 		.update = true,
18926f183d33SGuenter Roeck 		.attr = "average",
18936f183d33SGuenter Roeck 	}, {
189448065a13SGuenter Roeck 		.reg = PMBUS_VIRT_READ_PIN_MIN,
189548065a13SGuenter Roeck 		.update = true,
189648065a13SGuenter Roeck 		.attr = "input_lowest",
189748065a13SGuenter Roeck 	}, {
18986f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_PIN_MAX,
18996f183d33SGuenter Roeck 		.update = true,
19006f183d33SGuenter Roeck 		.attr = "input_highest",
19016f183d33SGuenter Roeck 	}, {
19026f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_PIN_HISTORY,
19036f183d33SGuenter Roeck 		.attr = "reset_history",
1904787c095eSZbigniew Lukwinski 	}, {
1905787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_PIN_MAX,
1906787c095eSZbigniew Lukwinski 		.attr = "rated_max",
1907787c095eSZbigniew Lukwinski 	},
19089d2ecfb7SGuenter Roeck };
19099d2ecfb7SGuenter Roeck 
19109d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr pout_limit_attrs[] = {
19119d2ecfb7SGuenter Roeck 	{
19129d2ecfb7SGuenter Roeck 		.reg = PMBUS_POUT_MAX,
19139d2ecfb7SGuenter Roeck 		.attr = "cap",
19149d2ecfb7SGuenter Roeck 		.alarm = "cap_alarm",
19159d2ecfb7SGuenter Roeck 		.sbit = PB_POWER_LIMITING,
19169d2ecfb7SGuenter Roeck 	}, {
19179d2ecfb7SGuenter Roeck 		.reg = PMBUS_POUT_OP_WARN_LIMIT,
19189d2ecfb7SGuenter Roeck 		.attr = "max",
19199d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
19209d2ecfb7SGuenter Roeck 		.sbit = PB_POUT_OP_WARNING,
19219d2ecfb7SGuenter Roeck 	}, {
19229d2ecfb7SGuenter Roeck 		.reg = PMBUS_POUT_OP_FAULT_LIMIT,
19239d2ecfb7SGuenter Roeck 		.attr = "crit",
19249d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
19259d2ecfb7SGuenter Roeck 		.sbit = PB_POUT_OP_FAULT,
192660b873e3SGuenter Roeck 	}, {
192760b873e3SGuenter Roeck 		.reg = PMBUS_VIRT_READ_POUT_AVG,
192860b873e3SGuenter Roeck 		.update = true,
192960b873e3SGuenter Roeck 		.attr = "average",
193060b873e3SGuenter Roeck 	}, {
193148065a13SGuenter Roeck 		.reg = PMBUS_VIRT_READ_POUT_MIN,
193248065a13SGuenter Roeck 		.update = true,
193348065a13SGuenter Roeck 		.attr = "input_lowest",
193448065a13SGuenter Roeck 	}, {
193560b873e3SGuenter Roeck 		.reg = PMBUS_VIRT_READ_POUT_MAX,
193660b873e3SGuenter Roeck 		.update = true,
193760b873e3SGuenter Roeck 		.attr = "input_highest",
193860b873e3SGuenter Roeck 	}, {
193960b873e3SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_POUT_HISTORY,
194060b873e3SGuenter Roeck 		.attr = "reset_history",
1941787c095eSZbigniew Lukwinski 	}, {
1942787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_POUT_MAX,
1943787c095eSZbigniew Lukwinski 		.attr = "rated_max",
1944787c095eSZbigniew Lukwinski 	},
19459d2ecfb7SGuenter Roeck };
19469d2ecfb7SGuenter Roeck 
19479d2ecfb7SGuenter Roeck static const struct pmbus_sensor_attr power_attributes[] = {
19489d2ecfb7SGuenter Roeck 	{
19499d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_PIN,
19509d2ecfb7SGuenter Roeck 		.class = PSC_POWER,
19519d2ecfb7SGuenter Roeck 		.label = "pin",
19529d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_PIN,
19539d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_INPUT,
1954a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_INPUT,
1955c159be9eSEdward A. James 		.gbit = PB_STATUS_INPUT,
19569d2ecfb7SGuenter Roeck 		.limit = pin_limit_attrs,
19579d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(pin_limit_attrs),
19589d2ecfb7SGuenter Roeck 	}, {
19599d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_POUT,
19609d2ecfb7SGuenter Roeck 		.class = PSC_POWER,
19619d2ecfb7SGuenter Roeck 		.label = "pout",
19629d2ecfb7SGuenter Roeck 		.paged = true,
19639d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_POUT,
19649d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_IOUT,
1965a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_IOUT,
19669d2ecfb7SGuenter Roeck 		.limit = pout_limit_attrs,
19679d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(pout_limit_attrs),
19689d2ecfb7SGuenter Roeck 	}
19699d2ecfb7SGuenter Roeck };
19709d2ecfb7SGuenter Roeck 
19719d2ecfb7SGuenter Roeck /* Temperature atributes */
19729d2ecfb7SGuenter Roeck 
19739d2ecfb7SGuenter Roeck static const struct pmbus_limit_attr temp_limit_attrs[] = {
19749d2ecfb7SGuenter Roeck 	{
19759d2ecfb7SGuenter Roeck 		.reg = PMBUS_UT_WARN_LIMIT,
197640257b95SGuenter Roeck 		.low = true,
19779d2ecfb7SGuenter Roeck 		.attr = "min",
19789d2ecfb7SGuenter Roeck 		.alarm = "min_alarm",
19799d2ecfb7SGuenter Roeck 		.sbit = PB_TEMP_UT_WARNING,
19809d2ecfb7SGuenter Roeck 	}, {
19819d2ecfb7SGuenter Roeck 		.reg = PMBUS_UT_FAULT_LIMIT,
198240257b95SGuenter Roeck 		.low = true,
19839d2ecfb7SGuenter Roeck 		.attr = "lcrit",
19849d2ecfb7SGuenter Roeck 		.alarm = "lcrit_alarm",
19859d2ecfb7SGuenter Roeck 		.sbit = PB_TEMP_UT_FAULT,
19869d2ecfb7SGuenter Roeck 	}, {
19879d2ecfb7SGuenter Roeck 		.reg = PMBUS_OT_WARN_LIMIT,
19889d2ecfb7SGuenter Roeck 		.attr = "max",
19899d2ecfb7SGuenter Roeck 		.alarm = "max_alarm",
19909d2ecfb7SGuenter Roeck 		.sbit = PB_TEMP_OT_WARNING,
19919d2ecfb7SGuenter Roeck 	}, {
19929d2ecfb7SGuenter Roeck 		.reg = PMBUS_OT_FAULT_LIMIT,
19939d2ecfb7SGuenter Roeck 		.attr = "crit",
19949d2ecfb7SGuenter Roeck 		.alarm = "crit_alarm",
19959d2ecfb7SGuenter Roeck 		.sbit = PB_TEMP_OT_FAULT,
19966f183d33SGuenter Roeck 	}, {
19976f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP_MIN,
19986f183d33SGuenter Roeck 		.attr = "lowest",
19996f183d33SGuenter Roeck 	}, {
200060b873e3SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP_AVG,
200160b873e3SGuenter Roeck 		.attr = "average",
200260b873e3SGuenter Roeck 	}, {
20036f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP_MAX,
20046f183d33SGuenter Roeck 		.attr = "highest",
20056f183d33SGuenter Roeck 	}, {
20066f183d33SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_TEMP_HISTORY,
20076f183d33SGuenter Roeck 		.attr = "reset_history",
2008787c095eSZbigniew Lukwinski 	}, {
2009787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_MAX_TEMP_1,
2010787c095eSZbigniew Lukwinski 		.attr = "rated_max",
2011787c095eSZbigniew Lukwinski 	},
20126f183d33SGuenter Roeck };
20136f183d33SGuenter Roeck 
20143d790287SGuenter Roeck static const struct pmbus_limit_attr temp_limit_attrs2[] = {
20153d790287SGuenter Roeck 	{
20163d790287SGuenter Roeck 		.reg = PMBUS_UT_WARN_LIMIT,
20173d790287SGuenter Roeck 		.low = true,
20183d790287SGuenter Roeck 		.attr = "min",
20193d790287SGuenter Roeck 		.alarm = "min_alarm",
20203d790287SGuenter Roeck 		.sbit = PB_TEMP_UT_WARNING,
20213d790287SGuenter Roeck 	}, {
20223d790287SGuenter Roeck 		.reg = PMBUS_UT_FAULT_LIMIT,
20233d790287SGuenter Roeck 		.low = true,
20243d790287SGuenter Roeck 		.attr = "lcrit",
20253d790287SGuenter Roeck 		.alarm = "lcrit_alarm",
20263d790287SGuenter Roeck 		.sbit = PB_TEMP_UT_FAULT,
20273d790287SGuenter Roeck 	}, {
20283d790287SGuenter Roeck 		.reg = PMBUS_OT_WARN_LIMIT,
20293d790287SGuenter Roeck 		.attr = "max",
20303d790287SGuenter Roeck 		.alarm = "max_alarm",
20313d790287SGuenter Roeck 		.sbit = PB_TEMP_OT_WARNING,
20323d790287SGuenter Roeck 	}, {
20333d790287SGuenter Roeck 		.reg = PMBUS_OT_FAULT_LIMIT,
20343d790287SGuenter Roeck 		.attr = "crit",
20353d790287SGuenter Roeck 		.alarm = "crit_alarm",
20363d790287SGuenter Roeck 		.sbit = PB_TEMP_OT_FAULT,
20373d790287SGuenter Roeck 	}, {
20383d790287SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP2_MIN,
20393d790287SGuenter Roeck 		.attr = "lowest",
20403d790287SGuenter Roeck 	}, {
204160b873e3SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP2_AVG,
204260b873e3SGuenter Roeck 		.attr = "average",
204360b873e3SGuenter Roeck 	}, {
20443d790287SGuenter Roeck 		.reg = PMBUS_VIRT_READ_TEMP2_MAX,
20453d790287SGuenter Roeck 		.attr = "highest",
20463d790287SGuenter Roeck 	}, {
20473d790287SGuenter Roeck 		.reg = PMBUS_VIRT_RESET_TEMP2_HISTORY,
20483d790287SGuenter Roeck 		.attr = "reset_history",
2049787c095eSZbigniew Lukwinski 	}, {
2050787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_MAX_TEMP_2,
2051787c095eSZbigniew Lukwinski 		.attr = "rated_max",
2052787c095eSZbigniew Lukwinski 	},
20533d790287SGuenter Roeck };
20543d790287SGuenter Roeck 
20553d790287SGuenter Roeck static const struct pmbus_limit_attr temp_limit_attrs3[] = {
20566f183d33SGuenter Roeck 	{
20576f183d33SGuenter Roeck 		.reg = PMBUS_UT_WARN_LIMIT,
205840257b95SGuenter Roeck 		.low = true,
20596f183d33SGuenter Roeck 		.attr = "min",
20606f183d33SGuenter Roeck 		.alarm = "min_alarm",
20616f183d33SGuenter Roeck 		.sbit = PB_TEMP_UT_WARNING,
20626f183d33SGuenter Roeck 	}, {
20636f183d33SGuenter Roeck 		.reg = PMBUS_UT_FAULT_LIMIT,
206440257b95SGuenter Roeck 		.low = true,
20656f183d33SGuenter Roeck 		.attr = "lcrit",
20666f183d33SGuenter Roeck 		.alarm = "lcrit_alarm",
20676f183d33SGuenter Roeck 		.sbit = PB_TEMP_UT_FAULT,
20686f183d33SGuenter Roeck 	}, {
20696f183d33SGuenter Roeck 		.reg = PMBUS_OT_WARN_LIMIT,
20706f183d33SGuenter Roeck 		.attr = "max",
20716f183d33SGuenter Roeck 		.alarm = "max_alarm",
20726f183d33SGuenter Roeck 		.sbit = PB_TEMP_OT_WARNING,
20736f183d33SGuenter Roeck 	}, {
20746f183d33SGuenter Roeck 		.reg = PMBUS_OT_FAULT_LIMIT,
20756f183d33SGuenter Roeck 		.attr = "crit",
20766f183d33SGuenter Roeck 		.alarm = "crit_alarm",
20776f183d33SGuenter Roeck 		.sbit = PB_TEMP_OT_FAULT,
2078787c095eSZbigniew Lukwinski 	}, {
2079787c095eSZbigniew Lukwinski 		.reg = PMBUS_MFR_MAX_TEMP_3,
2080787c095eSZbigniew Lukwinski 		.attr = "rated_max",
2081787c095eSZbigniew Lukwinski 	},
20829d2ecfb7SGuenter Roeck };
20839d2ecfb7SGuenter Roeck 
20849d2ecfb7SGuenter Roeck static const struct pmbus_sensor_attr temp_attributes[] = {
20859d2ecfb7SGuenter Roeck 	{
20869d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_TEMPERATURE_1,
20879d2ecfb7SGuenter Roeck 		.class = PSC_TEMPERATURE,
20889d2ecfb7SGuenter Roeck 		.paged = true,
20899d2ecfb7SGuenter Roeck 		.update = true,
20909d2ecfb7SGuenter Roeck 		.compare = true,
20919d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_TEMP,
20929d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_TEMP,
2093a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_TEMPERATURE,
20949d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_TEMPERATURE,
20959d2ecfb7SGuenter Roeck 		.limit = temp_limit_attrs,
20969d2ecfb7SGuenter Roeck 		.nlimit = ARRAY_SIZE(temp_limit_attrs),
20979d2ecfb7SGuenter Roeck 	}, {
20989d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_TEMPERATURE_2,
20999d2ecfb7SGuenter Roeck 		.class = PSC_TEMPERATURE,
21009d2ecfb7SGuenter Roeck 		.paged = true,
21019d2ecfb7SGuenter Roeck 		.update = true,
21029d2ecfb7SGuenter Roeck 		.compare = true,
21039d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_TEMP2,
21049d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_TEMP,
2105a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_TEMPERATURE,
21069d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_TEMPERATURE,
21073d790287SGuenter Roeck 		.limit = temp_limit_attrs2,
21083d790287SGuenter Roeck 		.nlimit = ARRAY_SIZE(temp_limit_attrs2),
21099d2ecfb7SGuenter Roeck 	}, {
21109d2ecfb7SGuenter Roeck 		.reg = PMBUS_READ_TEMPERATURE_3,
21119d2ecfb7SGuenter Roeck 		.class = PSC_TEMPERATURE,
21129d2ecfb7SGuenter Roeck 		.paged = true,
21139d2ecfb7SGuenter Roeck 		.update = true,
21149d2ecfb7SGuenter Roeck 		.compare = true,
21159d2ecfb7SGuenter Roeck 		.func = PMBUS_HAVE_TEMP3,
21169d2ecfb7SGuenter Roeck 		.sfunc = PMBUS_HAVE_STATUS_TEMP,
2117a919ba06SGuenter Roeck 		.sreg = PMBUS_STATUS_TEMPERATURE,
21189d2ecfb7SGuenter Roeck 		.gbit = PB_STATUS_TEMPERATURE,
21193d790287SGuenter Roeck 		.limit = temp_limit_attrs3,
21203d790287SGuenter Roeck 		.nlimit = ARRAY_SIZE(temp_limit_attrs3),
21219d2ecfb7SGuenter Roeck 	}
21229d2ecfb7SGuenter Roeck };
21239d2ecfb7SGuenter Roeck 
21249d2ecfb7SGuenter Roeck static const int pmbus_fan_registers[] = {
21259d2ecfb7SGuenter Roeck 	PMBUS_READ_FAN_SPEED_1,
21269d2ecfb7SGuenter Roeck 	PMBUS_READ_FAN_SPEED_2,
21279d2ecfb7SGuenter Roeck 	PMBUS_READ_FAN_SPEED_3,
21289d2ecfb7SGuenter Roeck 	PMBUS_READ_FAN_SPEED_4
21299d2ecfb7SGuenter Roeck };
21309d2ecfb7SGuenter Roeck 
21319d2ecfb7SGuenter Roeck static const int pmbus_fan_status_registers[] = {
21329d2ecfb7SGuenter Roeck 	PMBUS_STATUS_FAN_12,
21339d2ecfb7SGuenter Roeck 	PMBUS_STATUS_FAN_12,
21349d2ecfb7SGuenter Roeck 	PMBUS_STATUS_FAN_34,
21359d2ecfb7SGuenter Roeck 	PMBUS_STATUS_FAN_34
21369d2ecfb7SGuenter Roeck };
21379d2ecfb7SGuenter Roeck 
21389d2ecfb7SGuenter Roeck static const u32 pmbus_fan_flags[] = {
21399d2ecfb7SGuenter Roeck 	PMBUS_HAVE_FAN12,
21409d2ecfb7SGuenter Roeck 	PMBUS_HAVE_FAN12,
21419d2ecfb7SGuenter Roeck 	PMBUS_HAVE_FAN34,
21429d2ecfb7SGuenter Roeck 	PMBUS_HAVE_FAN34
21439d2ecfb7SGuenter Roeck };
21449d2ecfb7SGuenter Roeck 
21459d2ecfb7SGuenter Roeck static const u32 pmbus_fan_status_flags[] = {
21469d2ecfb7SGuenter Roeck 	PMBUS_HAVE_STATUS_FAN12,
21479d2ecfb7SGuenter Roeck 	PMBUS_HAVE_STATUS_FAN12,
21489d2ecfb7SGuenter Roeck 	PMBUS_HAVE_STATUS_FAN34,
21499d2ecfb7SGuenter Roeck 	PMBUS_HAVE_STATUS_FAN34
21509d2ecfb7SGuenter Roeck };
21519d2ecfb7SGuenter Roeck 
21529d2ecfb7SGuenter Roeck /* Fans */
2153d206636eSAndrew Jeffery 
2154d206636eSAndrew Jeffery /* Precondition: FAN_CONFIG_x_y and FAN_COMMAND_x must exist for the fan ID */
pmbus_add_fan_ctrl(struct i2c_client * client,struct pmbus_data * data,int index,int page,int id,u8 config)2155d206636eSAndrew Jeffery static int pmbus_add_fan_ctrl(struct i2c_client *client,
2156d206636eSAndrew Jeffery 		struct pmbus_data *data, int index, int page, int id,
2157d206636eSAndrew Jeffery 		u8 config)
2158d206636eSAndrew Jeffery {
2159d206636eSAndrew Jeffery 	struct pmbus_sensor *sensor;
2160d206636eSAndrew Jeffery 
2161d206636eSAndrew Jeffery 	sensor = pmbus_add_sensor(data, "fan", "target", index, page,
2162b4c8af4cSJan Kundrát 				  0xff, PMBUS_VIRT_FAN_TARGET_1 + id, PSC_FAN,
2163d206636eSAndrew Jeffery 				  false, false, true);
2164d206636eSAndrew Jeffery 
2165d206636eSAndrew Jeffery 	if (!sensor)
2166d206636eSAndrew Jeffery 		return -ENOMEM;
2167d206636eSAndrew Jeffery 
2168d206636eSAndrew Jeffery 	if (!((data->info->func[page] & PMBUS_HAVE_PWM12) ||
2169d206636eSAndrew Jeffery 			(data->info->func[page] & PMBUS_HAVE_PWM34)))
2170d206636eSAndrew Jeffery 		return 0;
2171d206636eSAndrew Jeffery 
2172d206636eSAndrew Jeffery 	sensor = pmbus_add_sensor(data, "pwm", NULL, index, page,
2173b4c8af4cSJan Kundrát 				  0xff, PMBUS_VIRT_PWM_1 + id, PSC_PWM,
2174d206636eSAndrew Jeffery 				  false, false, true);
2175d206636eSAndrew Jeffery 
2176d206636eSAndrew Jeffery 	if (!sensor)
2177d206636eSAndrew Jeffery 		return -ENOMEM;
2178d206636eSAndrew Jeffery 
2179d206636eSAndrew Jeffery 	sensor = pmbus_add_sensor(data, "pwm", "enable", index, page,
2180b4c8af4cSJan Kundrát 				  0xff, PMBUS_VIRT_PWM_ENABLE_1 + id, PSC_PWM,
2181d206636eSAndrew Jeffery 				  true, false, false);
2182d206636eSAndrew Jeffery 
2183d206636eSAndrew Jeffery 	if (!sensor)
2184d206636eSAndrew Jeffery 		return -ENOMEM;
2185d206636eSAndrew Jeffery 
2186d206636eSAndrew Jeffery 	return 0;
2187d206636eSAndrew Jeffery }
2188d206636eSAndrew Jeffery 
pmbus_add_fan_attributes(struct i2c_client * client,struct pmbus_data * data)21890328461eSGuenter Roeck static int pmbus_add_fan_attributes(struct i2c_client *client,
21909d2ecfb7SGuenter Roeck 				    struct pmbus_data *data)
21919d2ecfb7SGuenter Roeck {
21929d2ecfb7SGuenter Roeck 	const struct pmbus_driver_info *info = data->info;
21939d2ecfb7SGuenter Roeck 	int index = 1;
21949d2ecfb7SGuenter Roeck 	int page;
21950328461eSGuenter Roeck 	int ret;
21969d2ecfb7SGuenter Roeck 
21979d2ecfb7SGuenter Roeck 	for (page = 0; page < info->pages; page++) {
21989d2ecfb7SGuenter Roeck 		int f;
21999d2ecfb7SGuenter Roeck 
22009d2ecfb7SGuenter Roeck 		for (f = 0; f < ARRAY_SIZE(pmbus_fan_registers); f++) {
22019d2ecfb7SGuenter Roeck 			int regval;
22029d2ecfb7SGuenter Roeck 
22039d2ecfb7SGuenter Roeck 			if (!(info->func[page] & pmbus_fan_flags[f]))
22049d2ecfb7SGuenter Roeck 				break;
22059d2ecfb7SGuenter Roeck 
22069d2ecfb7SGuenter Roeck 			if (!pmbus_check_word_register(client, page,
22079d2ecfb7SGuenter Roeck 						       pmbus_fan_registers[f]))
22089d2ecfb7SGuenter Roeck 				break;
22099d2ecfb7SGuenter Roeck 
22109d2ecfb7SGuenter Roeck 			/*
22119d2ecfb7SGuenter Roeck 			 * Skip fan if not installed.
22129d2ecfb7SGuenter Roeck 			 * Each fan configuration register covers multiple fans,
22139d2ecfb7SGuenter Roeck 			 * so we have to do some magic.
22149d2ecfb7SGuenter Roeck 			 */
22159d2ecfb7SGuenter Roeck 			regval = _pmbus_read_byte_data(client, page,
22169d2ecfb7SGuenter Roeck 				pmbus_fan_config_registers[f]);
22179d2ecfb7SGuenter Roeck 			if (regval < 0 ||
22189d2ecfb7SGuenter Roeck 			    (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4)))))
22199d2ecfb7SGuenter Roeck 				continue;
22209d2ecfb7SGuenter Roeck 
2221e1e081a7SGuenter Roeck 			if (pmbus_add_sensor(data, "fan", "input", index,
2222b4c8af4cSJan Kundrát 					     page, 0xff, pmbus_fan_registers[f],
2223d206636eSAndrew Jeffery 					     PSC_FAN, true, true, true) == NULL)
2224e1e081a7SGuenter Roeck 				return -ENOMEM;
22259d2ecfb7SGuenter Roeck 
2226d206636eSAndrew Jeffery 			/* Fan control */
2227d206636eSAndrew Jeffery 			if (pmbus_check_word_register(client, page,
2228d206636eSAndrew Jeffery 					pmbus_fan_command_registers[f])) {
2229d206636eSAndrew Jeffery 				ret = pmbus_add_fan_ctrl(client, data, index,
2230d206636eSAndrew Jeffery 							 page, f, regval);
2231d206636eSAndrew Jeffery 				if (ret < 0)
2232d206636eSAndrew Jeffery 					return ret;
2233d206636eSAndrew Jeffery 			}
2234d206636eSAndrew Jeffery 
22359d2ecfb7SGuenter Roeck 			/*
22369d2ecfb7SGuenter Roeck 			 * Each fan status register covers multiple fans,
22379d2ecfb7SGuenter Roeck 			 * so we have to do some magic.
22389d2ecfb7SGuenter Roeck 			 */
22399d2ecfb7SGuenter Roeck 			if ((info->func[page] & pmbus_fan_status_flags[f]) &&
22409d2ecfb7SGuenter Roeck 			    pmbus_check_byte_register(client,
22419d2ecfb7SGuenter Roeck 					page, pmbus_fan_status_registers[f])) {
2242a919ba06SGuenter Roeck 				int reg;
22439d2ecfb7SGuenter Roeck 
22449d2ecfb7SGuenter Roeck 				if (f > 1)	/* fan 3, 4 */
2245a919ba06SGuenter Roeck 					reg = PMBUS_STATUS_FAN_34;
22469d2ecfb7SGuenter Roeck 				else
2247a919ba06SGuenter Roeck 					reg = PMBUS_STATUS_FAN_12;
2248663834f3SGuenter Roeck 				ret = pmbus_add_boolean(data, "fan",
2249a919ba06SGuenter Roeck 					"alarm", index, NULL, NULL, page, reg,
22509d2ecfb7SGuenter Roeck 					PB_FAN_FAN1_WARNING >> (f & 1));
22510328461eSGuenter Roeck 				if (ret)
22520328461eSGuenter Roeck 					return ret;
2253663834f3SGuenter Roeck 				ret = pmbus_add_boolean(data, "fan",
2254a919ba06SGuenter Roeck 					"fault", index, NULL, NULL, page, reg,
22559d2ecfb7SGuenter Roeck 					PB_FAN_FAN1_FAULT >> (f & 1));
22560328461eSGuenter Roeck 				if (ret)
22570328461eSGuenter Roeck 					return ret;
22589d2ecfb7SGuenter Roeck 			}
22599d2ecfb7SGuenter Roeck 			index++;
22609d2ecfb7SGuenter Roeck 		}
22619d2ecfb7SGuenter Roeck 	}
22620328461eSGuenter Roeck 	return 0;
22639d2ecfb7SGuenter Roeck }
22649d2ecfb7SGuenter Roeck 
226549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) struct pmbus_samples_attr {
226649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	int reg;
226749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	char *name;
226849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) };
226949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
227049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) struct pmbus_samples_reg {
227149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	int page;
227249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct pmbus_samples_attr *attr;
227349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct device_attribute dev_attr;
227449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) };
227549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
227649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) static struct pmbus_samples_attr pmbus_samples_registers[] = {
227749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	{
227849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.reg = PMBUS_VIRT_SAMPLES,
227949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.name = "samples",
228049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}, {
228149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.reg = PMBUS_VIRT_IN_SAMPLES,
228249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.name = "in_samples",
228349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}, {
228449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.reg = PMBUS_VIRT_CURR_SAMPLES,
228549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.name = "curr_samples",
228649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}, {
228749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.reg = PMBUS_VIRT_POWER_SAMPLES,
228849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.name = "power_samples",
228949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}, {
229049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.reg = PMBUS_VIRT_TEMP_SAMPLES,
229149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		.name = "temp_samples",
229249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}
229349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) };
229449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
229549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) #define to_samples_reg(x) container_of(x, struct pmbus_samples_reg, dev_attr)
229649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
pmbus_show_samples(struct device * dev,struct device_attribute * devattr,char * buf)229749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) static ssize_t pmbus_show_samples(struct device *dev,
229849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 				  struct device_attribute *devattr, char *buf)
229949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) {
230049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	int val;
230149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct i2c_client *client = to_i2c_client(dev->parent);
230249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct pmbus_samples_reg *reg = to_samples_reg(devattr);
230318e8db7fSRobert Hancock 	struct pmbus_data *data = i2c_get_clientdata(client);
230449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
230518e8db7fSRobert Hancock 	mutex_lock(&data->update_lock);
230643f33b6eSGuenter Roeck 	val = _pmbus_read_word_data(client, reg->page, 0xff, reg->attr->reg);
230718e8db7fSRobert Hancock 	mutex_unlock(&data->update_lock);
230849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	if (val < 0)
230949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		return val;
231049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
23111f4d4af4SGuenter Roeck 	return sysfs_emit(buf, "%d\n", val);
231249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) }
231349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
pmbus_set_samples(struct device * dev,struct device_attribute * devattr,const char * buf,size_t count)231449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) static ssize_t pmbus_set_samples(struct device *dev,
231549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 				 struct device_attribute *devattr,
231649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 				 const char *buf, size_t count)
231749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) {
231849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	int ret;
231949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	long val;
232049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct i2c_client *client = to_i2c_client(dev->parent);
232149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct pmbus_samples_reg *reg = to_samples_reg(devattr);
232238463721SAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct pmbus_data *data = i2c_get_clientdata(client);
232349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
232449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	if (kstrtol(buf, 0, &val) < 0)
232549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		return -EINVAL;
232649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
232738463721SAdamski, Krzysztof (Nokia - PL/Wroclaw) 	mutex_lock(&data->update_lock);
232849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	ret = _pmbus_write_word_data(client, reg->page, reg->attr->reg, val);
232938463721SAdamski, Krzysztof (Nokia - PL/Wroclaw) 	mutex_unlock(&data->update_lock);
233049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
233149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	return ret ? : count;
233249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) }
233349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
pmbus_add_samples_attr(struct pmbus_data * data,int page,struct pmbus_samples_attr * attr)233449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) static int pmbus_add_samples_attr(struct pmbus_data *data, int page,
233549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 				  struct pmbus_samples_attr *attr)
233649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) {
233749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	struct pmbus_samples_reg *reg;
233849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
233949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	reg = devm_kzalloc(data->dev, sizeof(*reg), GFP_KERNEL);
234049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	if (!reg)
234149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		return -ENOMEM;
234249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
234349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	reg->attr = attr;
234449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	reg->page = page;
234549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
234649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	pmbus_dev_attr_init(&reg->dev_attr, attr->name, 0644,
234749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 			    pmbus_show_samples, pmbus_set_samples);
234849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
234949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	return pmbus_add_attribute(data, &reg->dev_attr.attr);
235049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) }
235149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
pmbus_add_samples_attributes(struct i2c_client * client,struct pmbus_data * data)235249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) static int pmbus_add_samples_attributes(struct i2c_client *client,
235349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 					struct pmbus_data *data)
235449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) {
235549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	const struct pmbus_driver_info *info = data->info;
235649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	int s;
235749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
235849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	if (!(info->func[0] & PMBUS_HAVE_SAMPLES))
235949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		return 0;
236049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
236149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	for (s = 0; s < ARRAY_SIZE(pmbus_samples_registers); s++) {
236249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		struct pmbus_samples_attr *attr;
236349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		int ret;
236449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
236549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		attr = &pmbus_samples_registers[s];
236649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		if (!pmbus_check_word_register(client, 0, attr->reg))
236749c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 			continue;
236849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
236949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		ret = pmbus_add_samples_attr(data, 0, attr);
237049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		if (ret)
237149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 			return ret;
237249c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	}
237349c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
237449c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	return 0;
237549c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) }
237649c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
pmbus_find_attributes(struct i2c_client * client,struct pmbus_data * data)23770328461eSGuenter Roeck static int pmbus_find_attributes(struct i2c_client *client,
23789d2ecfb7SGuenter Roeck 				 struct pmbus_data *data)
23799d2ecfb7SGuenter Roeck {
23800328461eSGuenter Roeck 	int ret;
23810328461eSGuenter Roeck 
23829d2ecfb7SGuenter Roeck 	/* Voltage sensors */
23830328461eSGuenter Roeck 	ret = pmbus_add_sensor_attrs(client, data, "in", voltage_attributes,
23849d2ecfb7SGuenter Roeck 				     ARRAY_SIZE(voltage_attributes));
23850328461eSGuenter Roeck 	if (ret)
23860328461eSGuenter Roeck 		return ret;
23879d2ecfb7SGuenter Roeck 
23889d2ecfb7SGuenter Roeck 	/* Current sensors */
23890328461eSGuenter Roeck 	ret = pmbus_add_sensor_attrs(client, data, "curr", current_attributes,
23909d2ecfb7SGuenter Roeck 				     ARRAY_SIZE(current_attributes));
23910328461eSGuenter Roeck 	if (ret)
23920328461eSGuenter Roeck 		return ret;
23939d2ecfb7SGuenter Roeck 
23949d2ecfb7SGuenter Roeck 	/* Power sensors */
23950328461eSGuenter Roeck 	ret = pmbus_add_sensor_attrs(client, data, "power", power_attributes,
23969d2ecfb7SGuenter Roeck 				     ARRAY_SIZE(power_attributes));
23970328461eSGuenter Roeck 	if (ret)
23980328461eSGuenter Roeck 		return ret;
23999d2ecfb7SGuenter Roeck 
24009d2ecfb7SGuenter Roeck 	/* Temperature sensors */
24010328461eSGuenter Roeck 	ret = pmbus_add_sensor_attrs(client, data, "temp", temp_attributes,
24029d2ecfb7SGuenter Roeck 				     ARRAY_SIZE(temp_attributes));
24030328461eSGuenter Roeck 	if (ret)
24040328461eSGuenter Roeck 		return ret;
24059d2ecfb7SGuenter Roeck 
24069d2ecfb7SGuenter Roeck 	/* Fans */
24070328461eSGuenter Roeck 	ret = pmbus_add_fan_attributes(client, data);
240849c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	if (ret)
240949c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 		return ret;
241049c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 
241149c4455dSAdamski, Krzysztof (Nokia - PL/Wroclaw) 	ret = pmbus_add_samples_attributes(client, data);
24120328461eSGuenter Roeck 	return ret;
24139d2ecfb7SGuenter Roeck }
24149d2ecfb7SGuenter Roeck 
24159d2ecfb7SGuenter Roeck /*
2416e8e00c83SErik Rosen  * The pmbus_class_attr_map structure maps one sensor class to
2417e8e00c83SErik Rosen  * it's corresponding sensor attributes array.
2418e8e00c83SErik Rosen  */
2419e8e00c83SErik Rosen struct pmbus_class_attr_map {
2420e8e00c83SErik Rosen 	enum pmbus_sensor_classes class;
2421e8e00c83SErik Rosen 	int nattr;
2422e8e00c83SErik Rosen 	const struct pmbus_sensor_attr *attr;
2423e8e00c83SErik Rosen };
2424e8e00c83SErik Rosen 
2425e8e00c83SErik Rosen static const struct pmbus_class_attr_map class_attr_map[] = {
2426e8e00c83SErik Rosen 	{
2427e8e00c83SErik Rosen 		.class = PSC_VOLTAGE_IN,
2428e8e00c83SErik Rosen 		.attr = voltage_attributes,
2429e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(voltage_attributes),
2430e8e00c83SErik Rosen 	}, {
2431e8e00c83SErik Rosen 		.class = PSC_VOLTAGE_OUT,
2432e8e00c83SErik Rosen 		.attr = voltage_attributes,
2433e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(voltage_attributes),
2434e8e00c83SErik Rosen 	}, {
2435e8e00c83SErik Rosen 		.class = PSC_CURRENT_IN,
2436e8e00c83SErik Rosen 		.attr = current_attributes,
2437e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(current_attributes),
2438e8e00c83SErik Rosen 	}, {
2439e8e00c83SErik Rosen 		.class = PSC_CURRENT_OUT,
2440e8e00c83SErik Rosen 		.attr = current_attributes,
2441e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(current_attributes),
2442e8e00c83SErik Rosen 	}, {
2443e8e00c83SErik Rosen 		.class = PSC_POWER,
2444e8e00c83SErik Rosen 		.attr = power_attributes,
2445e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(power_attributes),
2446e8e00c83SErik Rosen 	}, {
2447e8e00c83SErik Rosen 		.class = PSC_TEMPERATURE,
2448e8e00c83SErik Rosen 		.attr = temp_attributes,
2449e8e00c83SErik Rosen 		.nattr = ARRAY_SIZE(temp_attributes),
2450e8e00c83SErik Rosen 	}
2451e8e00c83SErik Rosen };
2452e8e00c83SErik Rosen 
2453e8e00c83SErik Rosen /*
2454e8e00c83SErik Rosen  * Read the coefficients for direct mode.
2455e8e00c83SErik Rosen  */
pmbus_read_coefficients(struct i2c_client * client,struct pmbus_driver_info * info,const struct pmbus_sensor_attr * attr)2456e8e00c83SErik Rosen static int pmbus_read_coefficients(struct i2c_client *client,
2457e8e00c83SErik Rosen 				   struct pmbus_driver_info *info,
2458e8e00c83SErik Rosen 				   const struct pmbus_sensor_attr *attr)
2459e8e00c83SErik Rosen {
2460e8e00c83SErik Rosen 	int rv;
2461e8e00c83SErik Rosen 	union i2c_smbus_data data;
2462e8e00c83SErik Rosen 	enum pmbus_sensor_classes class = attr->class;
2463e8e00c83SErik Rosen 	s8 R;
2464e8e00c83SErik Rosen 	s16 m, b;
2465e8e00c83SErik Rosen 
2466e8e00c83SErik Rosen 	data.block[0] = 2;
2467e8e00c83SErik Rosen 	data.block[1] = attr->reg;
2468e8e00c83SErik Rosen 	data.block[2] = 0x01;
2469e8e00c83SErik Rosen 
2470e8e00c83SErik Rosen 	rv = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
2471e8e00c83SErik Rosen 			    I2C_SMBUS_WRITE, PMBUS_COEFFICIENTS,
2472e8e00c83SErik Rosen 			    I2C_SMBUS_BLOCK_PROC_CALL, &data);
2473e8e00c83SErik Rosen 
2474e8e00c83SErik Rosen 	if (rv < 0)
2475e8e00c83SErik Rosen 		return rv;
2476e8e00c83SErik Rosen 
2477e8e00c83SErik Rosen 	if (data.block[0] != 5)
2478e8e00c83SErik Rosen 		return -EIO;
2479e8e00c83SErik Rosen 
2480e8e00c83SErik Rosen 	m = data.block[1] | (data.block[2] << 8);
2481e8e00c83SErik Rosen 	b = data.block[3] | (data.block[4] << 8);
2482e8e00c83SErik Rosen 	R = data.block[5];
2483e8e00c83SErik Rosen 	info->m[class] = m;
2484e8e00c83SErik Rosen 	info->b[class] = b;
2485e8e00c83SErik Rosen 	info->R[class] = R;
2486e8e00c83SErik Rosen 
2487e8e00c83SErik Rosen 	return rv;
2488e8e00c83SErik Rosen }
2489e8e00c83SErik Rosen 
pmbus_init_coefficients(struct i2c_client * client,struct pmbus_driver_info * info)2490e8e00c83SErik Rosen static int pmbus_init_coefficients(struct i2c_client *client,
2491e8e00c83SErik Rosen 				   struct pmbus_driver_info *info)
2492e8e00c83SErik Rosen {
2493e8e00c83SErik Rosen 	int i, n, ret = -EINVAL;
2494e8e00c83SErik Rosen 	const struct pmbus_class_attr_map *map;
2495e8e00c83SErik Rosen 	const struct pmbus_sensor_attr *attr;
2496e8e00c83SErik Rosen 
2497e8e00c83SErik Rosen 	for (i = 0; i < ARRAY_SIZE(class_attr_map); i++) {
2498e8e00c83SErik Rosen 		map = &class_attr_map[i];
2499e8e00c83SErik Rosen 		if (info->format[map->class] != direct)
2500e8e00c83SErik Rosen 			continue;
2501e8e00c83SErik Rosen 		for (n = 0; n < map->nattr; n++) {
2502e8e00c83SErik Rosen 			attr = &map->attr[n];
2503e8e00c83SErik Rosen 			if (map->class != attr->class)
2504e8e00c83SErik Rosen 				continue;
2505e8e00c83SErik Rosen 			ret = pmbus_read_coefficients(client, info, attr);
2506e8e00c83SErik Rosen 			if (ret >= 0)
2507e8e00c83SErik Rosen 				break;
2508e8e00c83SErik Rosen 		}
2509e8e00c83SErik Rosen 		if (ret < 0) {
2510e8e00c83SErik Rosen 			dev_err(&client->dev,
2511e8e00c83SErik Rosen 				"No coefficients found for sensor class %d\n",
2512e8e00c83SErik Rosen 				map->class);
2513e8e00c83SErik Rosen 			return -EINVAL;
2514e8e00c83SErik Rosen 		}
2515e8e00c83SErik Rosen 	}
2516e8e00c83SErik Rosen 
2517e8e00c83SErik Rosen 	return 0;
2518e8e00c83SErik Rosen }
2519e8e00c83SErik Rosen 
2520e8e00c83SErik Rosen /*
25219d2ecfb7SGuenter Roeck  * Identify chip parameters.
25229d2ecfb7SGuenter Roeck  * This function is called for all chips.
25239d2ecfb7SGuenter Roeck  */
pmbus_identify_common(struct i2c_client * client,struct pmbus_data * data,int page)25249d2ecfb7SGuenter Roeck static int pmbus_identify_common(struct i2c_client *client,
2525daa436e6SGuenter Roeck 				 struct pmbus_data *data, int page)
25269d2ecfb7SGuenter Roeck {
25271af1f531SGuenter Roeck 	int vout_mode = -1;
25289d2ecfb7SGuenter Roeck 
2529daa436e6SGuenter Roeck 	if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE))
2530daa436e6SGuenter Roeck 		vout_mode = _pmbus_read_byte_data(client, page,
2531daa436e6SGuenter Roeck 						  PMBUS_VOUT_MODE);
25329d2ecfb7SGuenter Roeck 	if (vout_mode >= 0 && vout_mode != 0xff) {
25339d2ecfb7SGuenter Roeck 		/*
25349d2ecfb7SGuenter Roeck 		 * Not all chips support the VOUT_MODE command,
25359d2ecfb7SGuenter Roeck 		 * so a failure to read it is not an error.
25369d2ecfb7SGuenter Roeck 		 */
25379d2ecfb7SGuenter Roeck 		switch (vout_mode >> 5) {
25389d2ecfb7SGuenter Roeck 		case 0:	/* linear mode      */
25391061d851SGuenter Roeck 			if (data->info->format[PSC_VOLTAGE_OUT] != linear)
25409d2ecfb7SGuenter Roeck 				return -ENODEV;
25419d2ecfb7SGuenter Roeck 
2542daa436e6SGuenter Roeck 			data->exponent[page] = ((s8)(vout_mode << 3)) >> 3;
25439d2ecfb7SGuenter Roeck 			break;
25441061d851SGuenter Roeck 		case 1: /* VID mode         */
25451061d851SGuenter Roeck 			if (data->info->format[PSC_VOLTAGE_OUT] != vid)
25461061d851SGuenter Roeck 				return -ENODEV;
25471061d851SGuenter Roeck 			break;
25489d2ecfb7SGuenter Roeck 		case 2:	/* direct mode      */
25491061d851SGuenter Roeck 			if (data->info->format[PSC_VOLTAGE_OUT] != direct)
25509d2ecfb7SGuenter Roeck 				return -ENODEV;
25519d2ecfb7SGuenter Roeck 			break;
25524036a48eSGuenter Roeck 		case 3:	/* ieee 754 half precision */
25534036a48eSGuenter Roeck 			if (data->info->format[PSC_VOLTAGE_OUT] != ieee754)
25544036a48eSGuenter Roeck 				return -ENODEV;
25554036a48eSGuenter Roeck 			break;
25569d2ecfb7SGuenter Roeck 		default:
25579d2ecfb7SGuenter Roeck 			return -ENODEV;
25589d2ecfb7SGuenter Roeck 		}
25599d2ecfb7SGuenter Roeck 	}
25609d2ecfb7SGuenter Roeck 
25619d2ecfb7SGuenter Roeck 	return 0;
25629d2ecfb7SGuenter Roeck }
25639d2ecfb7SGuenter Roeck 
pmbus_read_status_byte(struct i2c_client * client,int page)2564cbcdec62SEdward A. James static int pmbus_read_status_byte(struct i2c_client *client, int page)
2565cbcdec62SEdward A. James {
2566cbcdec62SEdward A. James 	return _pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE);
2567cbcdec62SEdward A. James }
2568cbcdec62SEdward A. James 
pmbus_read_status_word(struct i2c_client * client,int page)2569cbcdec62SEdward A. James static int pmbus_read_status_word(struct i2c_client *client, int page)
2570cbcdec62SEdward A. James {
257143f33b6eSGuenter Roeck 	return _pmbus_read_word_data(client, page, 0xff, PMBUS_STATUS_WORD);
2572cbcdec62SEdward A. James }
2573cbcdec62SEdward A. James 
2574f30ce040SGuenter Roeck /* PEC attribute support */
2575f30ce040SGuenter Roeck 
pec_show(struct device * dev,struct device_attribute * dummy,char * buf)2576f30ce040SGuenter Roeck static ssize_t pec_show(struct device *dev, struct device_attribute *dummy,
2577f30ce040SGuenter Roeck 			char *buf)
2578f30ce040SGuenter Roeck {
2579f30ce040SGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev);
2580f30ce040SGuenter Roeck 
2581f30ce040SGuenter Roeck 	return sysfs_emit(buf, "%d\n", !!(client->flags & I2C_CLIENT_PEC));
2582f30ce040SGuenter Roeck }
2583f30ce040SGuenter Roeck 
pec_store(struct device * dev,struct device_attribute * dummy,const char * buf,size_t count)2584f30ce040SGuenter Roeck static ssize_t pec_store(struct device *dev, struct device_attribute *dummy,
2585f30ce040SGuenter Roeck 			 const char *buf, size_t count)
2586f30ce040SGuenter Roeck {
2587f30ce040SGuenter Roeck 	struct i2c_client *client = to_i2c_client(dev);
2588f30ce040SGuenter Roeck 	bool enable;
2589f30ce040SGuenter Roeck 	int err;
2590f30ce040SGuenter Roeck 
2591f30ce040SGuenter Roeck 	err = kstrtobool(buf, &enable);
2592f30ce040SGuenter Roeck 	if (err < 0)
2593f30ce040SGuenter Roeck 		return err;
2594f30ce040SGuenter Roeck 
2595f30ce040SGuenter Roeck 	if (enable)
2596f30ce040SGuenter Roeck 		client->flags |= I2C_CLIENT_PEC;
2597f30ce040SGuenter Roeck 	else
2598f30ce040SGuenter Roeck 		client->flags &= ~I2C_CLIENT_PEC;
2599f30ce040SGuenter Roeck 
2600f30ce040SGuenter Roeck 	return count;
2601f30ce040SGuenter Roeck }
2602f30ce040SGuenter Roeck 
2603f30ce040SGuenter Roeck static DEVICE_ATTR_RW(pec);
2604f30ce040SGuenter Roeck 
pmbus_remove_pec(void * dev)2605f30ce040SGuenter Roeck static void pmbus_remove_pec(void *dev)
2606f30ce040SGuenter Roeck {
2607f30ce040SGuenter Roeck 	device_remove_file(dev, &dev_attr_pec);
2608f30ce040SGuenter Roeck }
2609f30ce040SGuenter Roeck 
pmbus_init_common(struct i2c_client * client,struct pmbus_data * data,struct pmbus_driver_info * info)261016c6d01fSGuenter Roeck static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data,
261116c6d01fSGuenter Roeck 			     struct pmbus_driver_info *info)
261216c6d01fSGuenter Roeck {
261316c6d01fSGuenter Roeck 	struct device *dev = &client->dev;
2614daa436e6SGuenter Roeck 	int page, ret;
261516c6d01fSGuenter Roeck 
261616c6d01fSGuenter Roeck 	/*
2617d1baf7a3SAdam Wujek 	 * Figure out if PEC is enabled before accessing any other register.
2618d1baf7a3SAdam Wujek 	 * Make sure PEC is disabled, will be enabled later if needed.
2619d1baf7a3SAdam Wujek 	 */
2620d1baf7a3SAdam Wujek 	client->flags &= ~I2C_CLIENT_PEC;
2621d1baf7a3SAdam Wujek 
2622d1baf7a3SAdam Wujek 	/* Enable PEC if the controller and bus supports it */
2623d1baf7a3SAdam Wujek 	if (!(data->flags & PMBUS_NO_CAPABILITY)) {
2624d1baf7a3SAdam Wujek 		ret = i2c_smbus_read_byte_data(client, PMBUS_CAPABILITY);
2625d1baf7a3SAdam Wujek 		if (ret >= 0 && (ret & PB_CAPABILITY_ERROR_CHECK)) {
2626d1baf7a3SAdam Wujek 			if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_PEC))
2627d1baf7a3SAdam Wujek 				client->flags |= I2C_CLIENT_PEC;
2628d1baf7a3SAdam Wujek 		}
2629d1baf7a3SAdam Wujek 	}
2630d1baf7a3SAdam Wujek 
2631d1baf7a3SAdam Wujek 	/*
2632cbcdec62SEdward A. James 	 * Some PMBus chips don't support PMBUS_STATUS_WORD, so try
2633cbcdec62SEdward A. James 	 * to use PMBUS_STATUS_BYTE instead if that is the case.
263416c6d01fSGuenter Roeck 	 * Bail out if both registers are not supported.
263516c6d01fSGuenter Roeck 	 */
2636cbcdec62SEdward A. James 	data->read_status = pmbus_read_status_word;
263716c6d01fSGuenter Roeck 	ret = i2c_smbus_read_word_data(client, PMBUS_STATUS_WORD);
263816c6d01fSGuenter Roeck 	if (ret < 0 || ret == 0xffff) {
2639cbcdec62SEdward A. James 		data->read_status = pmbus_read_status_byte;
2640cbcdec62SEdward A. James 		ret = i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE);
2641cbcdec62SEdward A. James 		if (ret < 0 || ret == 0xff) {
264216c6d01fSGuenter Roeck 			dev_err(dev, "PMBus status register not found\n");
264316c6d01fSGuenter Roeck 			return -ENODEV;
264416c6d01fSGuenter Roeck 		}
2645c159be9eSEdward A. James 	} else {
2646c159be9eSEdward A. James 		data->has_status_word = true;
264716c6d01fSGuenter Roeck 	}
264816c6d01fSGuenter Roeck 
26499e347728SGuenter Roeck 	/*
26509e347728SGuenter Roeck 	 * Check if the chip is write protected. If it is, we can not clear
26519e347728SGuenter Roeck 	 * faults, and we should not try it. Also, in that case, writes into
26529e347728SGuenter Roeck 	 * limit registers need to be disabled.
26539e347728SGuenter Roeck 	 */
2654dbc0860fSErik Rosen 	if (!(data->flags & PMBUS_NO_WRITE_PROTECT)) {
26559e347728SGuenter Roeck 		ret = i2c_smbus_read_byte_data(client, PMBUS_WRITE_PROTECT);
26569e347728SGuenter Roeck 		if (ret > 0 && (ret & PB_WP_ANY))
26579e347728SGuenter Roeck 			data->flags |= PMBUS_WRITE_PROTECTED | PMBUS_SKIP_STATUS_CHECK;
2658dbc0860fSErik Rosen 	}
26599e347728SGuenter Roeck 
26601bc085e9SPatryk Biel 	ret = i2c_smbus_read_byte_data(client, PMBUS_REVISION);
26611bc085e9SPatryk Biel 	if (ret >= 0)
26621bc085e9SPatryk Biel 		data->revision = ret;
26631bc085e9SPatryk Biel 
2664e7c6a556SDmitry Bazhenov 	if (data->info->pages)
266516c6d01fSGuenter Roeck 		pmbus_clear_faults(client);
2666e7c6a556SDmitry Bazhenov 	else
2667e7c6a556SDmitry Bazhenov 		pmbus_clear_fault_page(client, -1);
266816c6d01fSGuenter Roeck 
266916c6d01fSGuenter Roeck 	if (info->identify) {
267016c6d01fSGuenter Roeck 		ret = (*info->identify)(client, info);
267116c6d01fSGuenter Roeck 		if (ret < 0) {
267216c6d01fSGuenter Roeck 			dev_err(dev, "Chip identification failed\n");
267316c6d01fSGuenter Roeck 			return ret;
267416c6d01fSGuenter Roeck 		}
267516c6d01fSGuenter Roeck 	}
267616c6d01fSGuenter Roeck 
267716c6d01fSGuenter Roeck 	if (info->pages <= 0 || info->pages > PMBUS_PAGES) {
267816c6d01fSGuenter Roeck 		dev_err(dev, "Bad number of PMBus pages: %d\n", info->pages);
267916c6d01fSGuenter Roeck 		return -ENODEV;
268016c6d01fSGuenter Roeck 	}
268116c6d01fSGuenter Roeck 
2682daa436e6SGuenter Roeck 	for (page = 0; page < info->pages; page++) {
2683daa436e6SGuenter Roeck 		ret = pmbus_identify_common(client, data, page);
268416c6d01fSGuenter Roeck 		if (ret < 0) {
268516c6d01fSGuenter Roeck 			dev_err(dev, "Failed to identify chip capabilities\n");
268616c6d01fSGuenter Roeck 			return ret;
268716c6d01fSGuenter Roeck 		}
2688daa436e6SGuenter Roeck 	}
2689e8e00c83SErik Rosen 
2690e8e00c83SErik Rosen 	if (data->flags & PMBUS_USE_COEFFICIENTS_CMD) {
2691e8e00c83SErik Rosen 		if (!i2c_check_functionality(client->adapter,
2692e8e00c83SErik Rosen 					     I2C_FUNC_SMBUS_BLOCK_PROC_CALL))
2693e8e00c83SErik Rosen 			return -ENODEV;
2694e8e00c83SErik Rosen 
2695e8e00c83SErik Rosen 		ret = pmbus_init_coefficients(client, info);
2696e8e00c83SErik Rosen 		if (ret < 0)
2697e8e00c83SErik Rosen 			return ret;
2698e8e00c83SErik Rosen 	}
2699e8e00c83SErik Rosen 
2700f30ce040SGuenter Roeck 	if (client->flags & I2C_CLIENT_PEC) {
2701f30ce040SGuenter Roeck 		/*
2702f30ce040SGuenter Roeck 		 * If I2C_CLIENT_PEC is set here, both the I2C adapter and the
2703f30ce040SGuenter Roeck 		 * chip support PEC. Add 'pec' attribute to client device to let
2704f30ce040SGuenter Roeck 		 * the user control it.
2705f30ce040SGuenter Roeck 		 */
2706f30ce040SGuenter Roeck 		ret = device_create_file(dev, &dev_attr_pec);
2707f30ce040SGuenter Roeck 		if (ret)
2708f30ce040SGuenter Roeck 			return ret;
2709f30ce040SGuenter Roeck 		ret = devm_add_action_or_reset(dev, pmbus_remove_pec, dev);
2710f30ce040SGuenter Roeck 		if (ret)
2711f30ce040SGuenter Roeck 			return ret;
2712f30ce040SGuenter Roeck 	}
2713f30ce040SGuenter Roeck 
271416c6d01fSGuenter Roeck 	return 0;
271516c6d01fSGuenter Roeck }
271616c6d01fSGuenter Roeck 
2717f74f06f4SPatrick Rudolph /* A PMBus status flag and the corresponding REGULATOR_ERROR_* and REGULATOR_EVENTS_* flag */
27187ab0da3aSNaresh Solanki struct pmbus_status_assoc {
2719f74f06f4SPatrick Rudolph 	int pflag, rflag, eflag;
27207ab0da3aSNaresh Solanki };
27217ab0da3aSNaresh Solanki 
27227ab0da3aSNaresh Solanki /* PMBus->regulator bit mappings for a PMBus status register */
27237ab0da3aSNaresh Solanki struct pmbus_status_category {
27247ab0da3aSNaresh Solanki 	int func;
27257ab0da3aSNaresh Solanki 	int reg;
27267ab0da3aSNaresh Solanki 	const struct pmbus_status_assoc *bits; /* zero-terminated */
27277ab0da3aSNaresh Solanki };
27287ab0da3aSNaresh Solanki 
27297ab0da3aSNaresh Solanki static const struct pmbus_status_category __maybe_unused pmbus_status_flag_map[] = {
27307ab0da3aSNaresh Solanki 	{
27317ab0da3aSNaresh Solanki 		.func = PMBUS_HAVE_STATUS_VOUT,
27327ab0da3aSNaresh Solanki 		.reg = PMBUS_STATUS_VOUT,
27337ab0da3aSNaresh Solanki 		.bits = (const struct pmbus_status_assoc[]) {
2734f74f06f4SPatrick Rudolph 			{ PB_VOLTAGE_UV_WARNING, REGULATOR_ERROR_UNDER_VOLTAGE_WARN,
2735f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_UNDER_VOLTAGE_WARN },
2736f74f06f4SPatrick Rudolph 			{ PB_VOLTAGE_UV_FAULT,   REGULATOR_ERROR_UNDER_VOLTAGE,
2737f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_UNDER_VOLTAGE },
2738f74f06f4SPatrick Rudolph 			{ PB_VOLTAGE_OV_WARNING, REGULATOR_ERROR_OVER_VOLTAGE_WARN,
2739f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_VOLTAGE_WARN },
2740f74f06f4SPatrick Rudolph 			{ PB_VOLTAGE_OV_FAULT,   REGULATOR_ERROR_REGULATION_OUT,
2741f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_VOLTAGE_WARN },
27427ab0da3aSNaresh Solanki 			{ },
27437ab0da3aSNaresh Solanki 		},
27447ab0da3aSNaresh Solanki 	}, {
27457ab0da3aSNaresh Solanki 		.func = PMBUS_HAVE_STATUS_IOUT,
27467ab0da3aSNaresh Solanki 		.reg = PMBUS_STATUS_IOUT,
27477ab0da3aSNaresh Solanki 		.bits = (const struct pmbus_status_assoc[]) {
2748f74f06f4SPatrick Rudolph 			{ PB_IOUT_OC_WARNING,   REGULATOR_ERROR_OVER_CURRENT_WARN,
2749f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_CURRENT_WARN },
2750f74f06f4SPatrick Rudolph 			{ PB_IOUT_OC_FAULT,     REGULATOR_ERROR_OVER_CURRENT,
2751f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_CURRENT },
2752f74f06f4SPatrick Rudolph 			{ PB_IOUT_OC_LV_FAULT,  REGULATOR_ERROR_OVER_CURRENT,
2753f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_CURRENT },
27547ab0da3aSNaresh Solanki 			{ },
27557ab0da3aSNaresh Solanki 		},
27567ab0da3aSNaresh Solanki 	}, {
27577ab0da3aSNaresh Solanki 		.func = PMBUS_HAVE_STATUS_TEMP,
27587ab0da3aSNaresh Solanki 		.reg = PMBUS_STATUS_TEMPERATURE,
27597ab0da3aSNaresh Solanki 		.bits = (const struct pmbus_status_assoc[]) {
2760f74f06f4SPatrick Rudolph 			{ PB_TEMP_OT_WARNING,    REGULATOR_ERROR_OVER_TEMP_WARN,
2761f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_TEMP_WARN },
2762f74f06f4SPatrick Rudolph 			{ PB_TEMP_OT_FAULT,      REGULATOR_ERROR_OVER_TEMP,
2763f74f06f4SPatrick Rudolph 			REGULATOR_EVENT_OVER_TEMP },
27647ab0da3aSNaresh Solanki 			{ },
27657ab0da3aSNaresh Solanki 		},
27667ab0da3aSNaresh Solanki 	},
27677ab0da3aSNaresh Solanki };
27687ab0da3aSNaresh Solanki 
_pmbus_is_enabled(struct i2c_client * client,u8 page)27690bd66784SPatrick Rudolph static int _pmbus_is_enabled(struct i2c_client *client, u8 page)
2770ddbb4db4SAlan Tull {
2771ddbb4db4SAlan Tull 	int ret;
2772ddbb4db4SAlan Tull 
2773f0a5c839SMårten Lindahl 	ret = _pmbus_read_byte_data(client, page, PMBUS_OPERATION);
2774686d303eSPatrick Rudolph 
2775ddbb4db4SAlan Tull 	if (ret < 0)
2776ddbb4db4SAlan Tull 		return ret;
2777ddbb4db4SAlan Tull 
2778ddbb4db4SAlan Tull 	return !!(ret & PB_OPERATION_CONTROL_ON);
2779ddbb4db4SAlan Tull }
2780ddbb4db4SAlan Tull 
pmbus_is_enabled(struct i2c_client * client,u8 page)27810bd66784SPatrick Rudolph static int __maybe_unused pmbus_is_enabled(struct i2c_client *client, u8 page)
2782df5f6b6aSNaresh Solanki {
2783df5f6b6aSNaresh Solanki 	struct pmbus_data *data = i2c_get_clientdata(client);
2784df5f6b6aSNaresh Solanki 	int ret;
2785df5f6b6aSNaresh Solanki 
2786df5f6b6aSNaresh Solanki 	mutex_lock(&data->update_lock);
27870bd66784SPatrick Rudolph 	ret = _pmbus_is_enabled(client, page);
2788df5f6b6aSNaresh Solanki 	mutex_unlock(&data->update_lock);
2789df5f6b6aSNaresh Solanki 
279055aab08fSPatrick Rudolph 	return ret;
2791df5f6b6aSNaresh Solanki }
2792df5f6b6aSNaresh Solanki 
2793f469bde9SPatrick Rudolph #define to_dev_attr(_dev_attr) \
2794f469bde9SPatrick Rudolph 	container_of(_dev_attr, struct device_attribute, attr)
2795f469bde9SPatrick Rudolph 
pmbus_notify(struct pmbus_data * data,int page,int reg,int flags)2796f469bde9SPatrick Rudolph static void pmbus_notify(struct pmbus_data *data, int page, int reg, int flags)
2797f469bde9SPatrick Rudolph {
2798f469bde9SPatrick Rudolph 	int i;
2799f469bde9SPatrick Rudolph 
2800f469bde9SPatrick Rudolph 	for (i = 0; i < data->num_attributes; i++) {
2801f469bde9SPatrick Rudolph 		struct device_attribute *da = to_dev_attr(data->group.attrs[i]);
2802f469bde9SPatrick Rudolph 		struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
2803f469bde9SPatrick Rudolph 		int index = attr->index;
2804f469bde9SPatrick Rudolph 		u16 smask = pb_index_to_mask(index);
2805f469bde9SPatrick Rudolph 		u8 spage = pb_index_to_page(index);
2806f469bde9SPatrick Rudolph 		u16 sreg = pb_index_to_reg(index);
2807f469bde9SPatrick Rudolph 
2808f469bde9SPatrick Rudolph 		if (reg == sreg && page == spage && (smask & flags)) {
2809f469bde9SPatrick Rudolph 			dev_dbg(data->dev, "sysfs notify: %s", da->attr.name);
2810f469bde9SPatrick Rudolph 			sysfs_notify(&data->dev->kobj, NULL, da->attr.name);
2811f469bde9SPatrick Rudolph 			kobject_uevent(&data->dev->kobj, KOBJ_CHANGE);
2812f469bde9SPatrick Rudolph 			flags &= ~smask;
2813f469bde9SPatrick Rudolph 		}
2814f469bde9SPatrick Rudolph 
2815f469bde9SPatrick Rudolph 		if (!flags)
2816f469bde9SPatrick Rudolph 			break;
2817f469bde9SPatrick Rudolph 	}
2818f469bde9SPatrick Rudolph }
2819f469bde9SPatrick Rudolph 
_pmbus_get_flags(struct pmbus_data * data,u8 page,unsigned int * flags,unsigned int * event,bool notify)2820f469bde9SPatrick Rudolph static int _pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
2821f74f06f4SPatrick Rudolph 			   unsigned int *event, bool notify)
2822df5f6b6aSNaresh Solanki {
2823df5f6b6aSNaresh Solanki 	int i, status;
2824df5f6b6aSNaresh Solanki 	const struct pmbus_status_category *cat;
2825df5f6b6aSNaresh Solanki 	const struct pmbus_status_assoc *bit;
2826df5f6b6aSNaresh Solanki 	struct device *dev = data->dev;
2827df5f6b6aSNaresh Solanki 	struct i2c_client *client = to_i2c_client(dev);
2828df5f6b6aSNaresh Solanki 	int func = data->info->func[page];
2829df5f6b6aSNaresh Solanki 
2830df5f6b6aSNaresh Solanki 	*flags = 0;
2831f74f06f4SPatrick Rudolph 	*event = 0;
2832df5f6b6aSNaresh Solanki 
2833df5f6b6aSNaresh Solanki 	for (i = 0; i < ARRAY_SIZE(pmbus_status_flag_map); i++) {
2834df5f6b6aSNaresh Solanki 		cat = &pmbus_status_flag_map[i];
2835df5f6b6aSNaresh Solanki 		if (!(func & cat->func))
2836df5f6b6aSNaresh Solanki 			continue;
2837df5f6b6aSNaresh Solanki 
2838df5f6b6aSNaresh Solanki 		status = _pmbus_read_byte_data(client, page, cat->reg);
2839df5f6b6aSNaresh Solanki 		if (status < 0)
2840df5f6b6aSNaresh Solanki 			return status;
2841df5f6b6aSNaresh Solanki 
2842f74f06f4SPatrick Rudolph 		for (bit = cat->bits; bit->pflag; bit++)
2843f74f06f4SPatrick Rudolph 			if (status & bit->pflag) {
2844df5f6b6aSNaresh Solanki 				*flags |= bit->rflag;
2845f74f06f4SPatrick Rudolph 				*event |= bit->eflag;
2846df5f6b6aSNaresh Solanki 			}
2847f469bde9SPatrick Rudolph 
2848f469bde9SPatrick Rudolph 		if (notify && status)
2849f469bde9SPatrick Rudolph 			pmbus_notify(data, page, cat->reg, status);
2850f469bde9SPatrick Rudolph 
2851df5f6b6aSNaresh Solanki 	}
2852df5f6b6aSNaresh Solanki 
2853df5f6b6aSNaresh Solanki 	/*
2854df5f6b6aSNaresh Solanki 	 * Map what bits of STATUS_{WORD,BYTE} we can to REGULATOR_ERROR_*
2855df5f6b6aSNaresh Solanki 	 * bits.  Some of the other bits are tempting (especially for cases
2856df5f6b6aSNaresh Solanki 	 * where we don't have the relevant PMBUS_HAVE_STATUS_*
2857df5f6b6aSNaresh Solanki 	 * functionality), but there's an unfortunate ambiguity in that
2858df5f6b6aSNaresh Solanki 	 * they're defined as indicating a fault *or* a warning, so we can't
2859df5f6b6aSNaresh Solanki 	 * easily determine whether to report REGULATOR_ERROR_<foo> or
2860df5f6b6aSNaresh Solanki 	 * REGULATOR_ERROR_<foo>_WARN.
2861df5f6b6aSNaresh Solanki 	 */
2862df5f6b6aSNaresh Solanki 	status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
2863df5f6b6aSNaresh Solanki 	if (status < 0)
2864df5f6b6aSNaresh Solanki 		return status;
2865df5f6b6aSNaresh Solanki 
28660bd66784SPatrick Rudolph 	if (_pmbus_is_enabled(client, page)) {
2867f74f06f4SPatrick Rudolph 		if (status & PB_STATUS_OFF) {
2868df5f6b6aSNaresh Solanki 			*flags |= REGULATOR_ERROR_FAIL;
2869f74f06f4SPatrick Rudolph 			*event |= REGULATOR_EVENT_FAIL;
2870f74f06f4SPatrick Rudolph 		}
2871df5f6b6aSNaresh Solanki 
2872f74f06f4SPatrick Rudolph 		if (status & PB_STATUS_POWER_GOOD_N) {
2873df5f6b6aSNaresh Solanki 			*flags |= REGULATOR_ERROR_REGULATION_OUT;
2874f74f06f4SPatrick Rudolph 			*event |= REGULATOR_EVENT_REGULATION_OUT;
2875f74f06f4SPatrick Rudolph 		}
2876df5f6b6aSNaresh Solanki 	}
2877df5f6b6aSNaresh Solanki 	/*
2878df5f6b6aSNaresh Solanki 	 * Unlike most other status bits, PB_STATUS_{IOUT_OC,VOUT_OV} are
2879df5f6b6aSNaresh Solanki 	 * defined strictly as fault indicators (not warnings).
2880df5f6b6aSNaresh Solanki 	 */
2881f74f06f4SPatrick Rudolph 	if (status & PB_STATUS_IOUT_OC) {
2882df5f6b6aSNaresh Solanki 		*flags |= REGULATOR_ERROR_OVER_CURRENT;
2883f74f06f4SPatrick Rudolph 		*event |= REGULATOR_EVENT_OVER_CURRENT;
2884f74f06f4SPatrick Rudolph 	}
2885f74f06f4SPatrick Rudolph 	if (status & PB_STATUS_VOUT_OV) {
2886df5f6b6aSNaresh Solanki 		*flags |= REGULATOR_ERROR_REGULATION_OUT;
2887f74f06f4SPatrick Rudolph 		*event |= REGULATOR_EVENT_FAIL;
2888f74f06f4SPatrick Rudolph 	}
2889df5f6b6aSNaresh Solanki 
2890df5f6b6aSNaresh Solanki 	/*
2891df5f6b6aSNaresh Solanki 	 * If we haven't discovered any thermal faults or warnings via
2892df5f6b6aSNaresh Solanki 	 * PMBUS_STATUS_TEMPERATURE, map PB_STATUS_TEMPERATURE to a warning as
2893df5f6b6aSNaresh Solanki 	 * a (conservative) best-effort interpretation.
2894df5f6b6aSNaresh Solanki 	 */
2895df5f6b6aSNaresh Solanki 	if (!(*flags & (REGULATOR_ERROR_OVER_TEMP | REGULATOR_ERROR_OVER_TEMP_WARN)) &&
2896f74f06f4SPatrick Rudolph 	    (status & PB_STATUS_TEMPERATURE)) {
2897df5f6b6aSNaresh Solanki 		*flags |= REGULATOR_ERROR_OVER_TEMP_WARN;
2898f74f06f4SPatrick Rudolph 		*event |= REGULATOR_EVENT_OVER_TEMP_WARN;
2899f74f06f4SPatrick Rudolph 	}
2900f74f06f4SPatrick Rudolph 
2901df5f6b6aSNaresh Solanki 
2902df5f6b6aSNaresh Solanki 	return 0;
2903df5f6b6aSNaresh Solanki }
2904df5f6b6aSNaresh Solanki 
pmbus_get_flags(struct pmbus_data * data,u8 page,unsigned int * flags,unsigned int * event,bool notify)2905f469bde9SPatrick Rudolph static int __maybe_unused pmbus_get_flags(struct pmbus_data *data, u8 page, unsigned int *flags,
2906f74f06f4SPatrick Rudolph 					  unsigned int *event, bool notify)
2907df5f6b6aSNaresh Solanki {
2908df5f6b6aSNaresh Solanki 	int ret;
2909df5f6b6aSNaresh Solanki 
2910df5f6b6aSNaresh Solanki 	mutex_lock(&data->update_lock);
2911f74f06f4SPatrick Rudolph 	ret = _pmbus_get_flags(data, page, flags, event, notify);
2912df5f6b6aSNaresh Solanki 	mutex_unlock(&data->update_lock);
2913df5f6b6aSNaresh Solanki 
2914df5f6b6aSNaresh Solanki 	return ret;
2915df5f6b6aSNaresh Solanki }
2916df5f6b6aSNaresh Solanki 
2917df5f6b6aSNaresh Solanki #if IS_ENABLED(CONFIG_REGULATOR)
pmbus_regulator_is_enabled(struct regulator_dev * rdev)2918df5f6b6aSNaresh Solanki static int pmbus_regulator_is_enabled(struct regulator_dev *rdev)
2919df5f6b6aSNaresh Solanki {
29200bd66784SPatrick Rudolph 	struct device *dev = rdev_get_dev(rdev);
29210bd66784SPatrick Rudolph 	struct i2c_client *client = to_i2c_client(dev->parent);
29220bd66784SPatrick Rudolph 
29230bd66784SPatrick Rudolph 	return pmbus_is_enabled(client, rdev_get_id(rdev));
2924df5f6b6aSNaresh Solanki }
2925df5f6b6aSNaresh Solanki 
_pmbus_regulator_on_off(struct regulator_dev * rdev,bool enable)2926ddbb4db4SAlan Tull static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable)
2927ddbb4db4SAlan Tull {
2928ddbb4db4SAlan Tull 	struct device *dev = rdev_get_dev(rdev);
2929ddbb4db4SAlan Tull 	struct i2c_client *client = to_i2c_client(dev->parent);
2930686d303eSPatrick Rudolph 	struct pmbus_data *data = i2c_get_clientdata(client);
2931ddbb4db4SAlan Tull 	u8 page = rdev_get_id(rdev);
2932686d303eSPatrick Rudolph 	int ret;
2933ddbb4db4SAlan Tull 
2934686d303eSPatrick Rudolph 	mutex_lock(&data->update_lock);
2935686d303eSPatrick Rudolph 	ret = pmbus_update_byte_data(client, page, PMBUS_OPERATION,
2936ddbb4db4SAlan Tull 				     PB_OPERATION_CONTROL_ON,
2937ddbb4db4SAlan Tull 				     enable ? PB_OPERATION_CONTROL_ON : 0);
2938686d303eSPatrick Rudolph 	mutex_unlock(&data->update_lock);
2939686d303eSPatrick Rudolph 
2940686d303eSPatrick Rudolph 	return ret;
2941ddbb4db4SAlan Tull }
2942ddbb4db4SAlan Tull 
pmbus_regulator_enable(struct regulator_dev * rdev)2943ddbb4db4SAlan Tull static int pmbus_regulator_enable(struct regulator_dev *rdev)
2944ddbb4db4SAlan Tull {
2945ddbb4db4SAlan Tull 	return _pmbus_regulator_on_off(rdev, 1);
2946ddbb4db4SAlan Tull }
2947ddbb4db4SAlan Tull 
pmbus_regulator_disable(struct regulator_dev * rdev)2948ddbb4db4SAlan Tull static int pmbus_regulator_disable(struct regulator_dev *rdev)
2949ddbb4db4SAlan Tull {
2950ddbb4db4SAlan Tull 	return _pmbus_regulator_on_off(rdev, 0);
2951ddbb4db4SAlan Tull }
2952ddbb4db4SAlan Tull 
pmbus_regulator_get_error_flags(struct regulator_dev * rdev,unsigned int * flags)2953e0f0307aSZev Weiss static int pmbus_regulator_get_error_flags(struct regulator_dev *rdev, unsigned int *flags)
2954e0f0307aSZev Weiss {
2955e0f0307aSZev Weiss 	struct device *dev = rdev_get_dev(rdev);
2956e0f0307aSZev Weiss 	struct i2c_client *client = to_i2c_client(dev->parent);
2957e0f0307aSZev Weiss 	struct pmbus_data *data = i2c_get_clientdata(client);
2958f74f06f4SPatrick Rudolph 	int event;
2959e0f0307aSZev Weiss 
2960f74f06f4SPatrick Rudolph 	return pmbus_get_flags(data, rdev_get_id(rdev), flags, &event, false);
2961e0f0307aSZev Weiss }
2962e0f0307aSZev Weiss 
pmbus_regulator_get_status(struct regulator_dev * rdev)2963c05f477cSPatrick Rudolph static int pmbus_regulator_get_status(struct regulator_dev *rdev)
2964c05f477cSPatrick Rudolph {
2965c05f477cSPatrick Rudolph 	struct device *dev = rdev_get_dev(rdev);
2966c05f477cSPatrick Rudolph 	struct i2c_client *client = to_i2c_client(dev->parent);
2967c05f477cSPatrick Rudolph 	struct pmbus_data *data = i2c_get_clientdata(client);
2968c05f477cSPatrick Rudolph 	u8 page = rdev_get_id(rdev);
2969c05f477cSPatrick Rudolph 	int status, ret;
2970b84000f2SGuenter Roeck 	int event;
2971c05f477cSPatrick Rudolph 
2972c05f477cSPatrick Rudolph 	mutex_lock(&data->update_lock);
2973c05f477cSPatrick Rudolph 	status = pmbus_get_status(client, page, PMBUS_STATUS_WORD);
2974c05f477cSPatrick Rudolph 	if (status < 0) {
2975c05f477cSPatrick Rudolph 		ret = status;
2976c05f477cSPatrick Rudolph 		goto unlock;
2977c05f477cSPatrick Rudolph 	}
2978c05f477cSPatrick Rudolph 
2979c05f477cSPatrick Rudolph 	if (status & PB_STATUS_OFF) {
2980c05f477cSPatrick Rudolph 		ret = REGULATOR_STATUS_OFF;
2981c05f477cSPatrick Rudolph 		goto unlock;
2982c05f477cSPatrick Rudolph 	}
2983c05f477cSPatrick Rudolph 
2984c05f477cSPatrick Rudolph 	/* If regulator is ON & reports power good then return ON */
2985c05f477cSPatrick Rudolph 	if (!(status & PB_STATUS_POWER_GOOD_N)) {
2986c05f477cSPatrick Rudolph 		ret = REGULATOR_STATUS_ON;
2987c05f477cSPatrick Rudolph 		goto unlock;
2988c05f477cSPatrick Rudolph 	}
2989c05f477cSPatrick Rudolph 
2990b84000f2SGuenter Roeck 	ret = _pmbus_get_flags(data, rdev_get_id(rdev), &status, &event, false);
2991c05f477cSPatrick Rudolph 	if (ret)
2992c05f477cSPatrick Rudolph 		goto unlock;
2993c05f477cSPatrick Rudolph 
2994c05f477cSPatrick Rudolph 	if (status & (REGULATOR_ERROR_UNDER_VOLTAGE | REGULATOR_ERROR_OVER_CURRENT |
2995c05f477cSPatrick Rudolph 	   REGULATOR_ERROR_REGULATION_OUT | REGULATOR_ERROR_FAIL | REGULATOR_ERROR_OVER_TEMP)) {
2996c05f477cSPatrick Rudolph 		ret = REGULATOR_STATUS_ERROR;
2997c05f477cSPatrick Rudolph 		goto unlock;
2998c05f477cSPatrick Rudolph 	}
2999c05f477cSPatrick Rudolph 
3000c05f477cSPatrick Rudolph 	ret = REGULATOR_STATUS_UNDEFINED;
3001c05f477cSPatrick Rudolph 
3002c05f477cSPatrick Rudolph unlock:
3003c05f477cSPatrick Rudolph 	mutex_unlock(&data->update_lock);
3004c05f477cSPatrick Rudolph 	return ret;
3005c05f477cSPatrick Rudolph }
3006c05f477cSPatrick Rudolph 
pmbus_regulator_get_low_margin(struct i2c_client * client,int page)300707fb7627SMårten Lindahl static int pmbus_regulator_get_low_margin(struct i2c_client *client, int page)
300807fb7627SMårten Lindahl {
300907fb7627SMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
301007fb7627SMårten Lindahl 	struct pmbus_sensor s = {
301107fb7627SMårten Lindahl 		.page = page,
301207fb7627SMårten Lindahl 		.class = PSC_VOLTAGE_OUT,
301307fb7627SMårten Lindahl 		.convert = true,
301407fb7627SMårten Lindahl 		.data = -1,
301507fb7627SMårten Lindahl 	};
301607fb7627SMårten Lindahl 
3017ed359056SVincent Whitchurch 	if (data->vout_low[page] < 0) {
301807fb7627SMårten Lindahl 		if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MIN))
301907fb7627SMårten Lindahl 			s.data = _pmbus_read_word_data(client, page, 0xff,
302007fb7627SMårten Lindahl 						       PMBUS_MFR_VOUT_MIN);
302107fb7627SMårten Lindahl 		if (s.data < 0) {
302207fb7627SMårten Lindahl 			s.data = _pmbus_read_word_data(client, page, 0xff,
302307fb7627SMårten Lindahl 						       PMBUS_VOUT_MARGIN_LOW);
302407fb7627SMårten Lindahl 			if (s.data < 0)
302507fb7627SMårten Lindahl 				return s.data;
302607fb7627SMårten Lindahl 		}
302707fb7627SMårten Lindahl 		data->vout_low[page] = pmbus_reg2data(data, &s);
302807fb7627SMårten Lindahl 	}
302907fb7627SMårten Lindahl 
303007fb7627SMårten Lindahl 	return data->vout_low[page];
303107fb7627SMårten Lindahl }
303207fb7627SMårten Lindahl 
pmbus_regulator_get_high_margin(struct i2c_client * client,int page)303307fb7627SMårten Lindahl static int pmbus_regulator_get_high_margin(struct i2c_client *client, int page)
303407fb7627SMårten Lindahl {
303507fb7627SMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
303607fb7627SMårten Lindahl 	struct pmbus_sensor s = {
303707fb7627SMårten Lindahl 		.page = page,
303807fb7627SMårten Lindahl 		.class = PSC_VOLTAGE_OUT,
303907fb7627SMårten Lindahl 		.convert = true,
304007fb7627SMårten Lindahl 		.data = -1,
304107fb7627SMårten Lindahl 	};
304207fb7627SMårten Lindahl 
3043ed359056SVincent Whitchurch 	if (data->vout_high[page] < 0) {
304407fb7627SMårten Lindahl 		if (pmbus_check_word_register(client, page, PMBUS_MFR_VOUT_MAX))
304507fb7627SMårten Lindahl 			s.data = _pmbus_read_word_data(client, page, 0xff,
304607fb7627SMårten Lindahl 						       PMBUS_MFR_VOUT_MAX);
304707fb7627SMårten Lindahl 		if (s.data < 0) {
304807fb7627SMårten Lindahl 			s.data = _pmbus_read_word_data(client, page, 0xff,
304907fb7627SMårten Lindahl 						       PMBUS_VOUT_MARGIN_HIGH);
305007fb7627SMårten Lindahl 			if (s.data < 0)
305107fb7627SMårten Lindahl 				return s.data;
305207fb7627SMårten Lindahl 		}
305307fb7627SMårten Lindahl 		data->vout_high[page] = pmbus_reg2data(data, &s);
305407fb7627SMårten Lindahl 	}
305507fb7627SMårten Lindahl 
305607fb7627SMårten Lindahl 	return data->vout_high[page];
305707fb7627SMårten Lindahl }
305807fb7627SMårten Lindahl 
pmbus_regulator_get_voltage(struct regulator_dev * rdev)305928bf22efSMårten Lindahl static int pmbus_regulator_get_voltage(struct regulator_dev *rdev)
306028bf22efSMårten Lindahl {
306128bf22efSMårten Lindahl 	struct device *dev = rdev_get_dev(rdev);
306228bf22efSMårten Lindahl 	struct i2c_client *client = to_i2c_client(dev->parent);
306328bf22efSMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
306428bf22efSMårten Lindahl 	struct pmbus_sensor s = {
306528bf22efSMårten Lindahl 		.page = rdev_get_id(rdev),
306628bf22efSMårten Lindahl 		.class = PSC_VOLTAGE_OUT,
306728bf22efSMårten Lindahl 		.convert = true,
306828bf22efSMårten Lindahl 	};
306928bf22efSMårten Lindahl 
307028bf22efSMårten Lindahl 	s.data = _pmbus_read_word_data(client, s.page, 0xff, PMBUS_READ_VOUT);
307128bf22efSMårten Lindahl 	if (s.data < 0)
307228bf22efSMårten Lindahl 		return s.data;
307328bf22efSMårten Lindahl 
307428bf22efSMårten Lindahl 	return (int)pmbus_reg2data(data, &s) * 1000; /* unit is uV */
307528bf22efSMårten Lindahl }
307628bf22efSMårten Lindahl 
pmbus_regulator_set_voltage(struct regulator_dev * rdev,int min_uv,int max_uv,unsigned int * selector)307728bf22efSMårten Lindahl static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv,
307828bf22efSMårten Lindahl 				       int max_uv, unsigned int *selector)
307928bf22efSMårten Lindahl {
308028bf22efSMårten Lindahl 	struct device *dev = rdev_get_dev(rdev);
308128bf22efSMårten Lindahl 	struct i2c_client *client = to_i2c_client(dev->parent);
308228bf22efSMårten Lindahl 	struct pmbus_data *data = i2c_get_clientdata(client);
308328bf22efSMårten Lindahl 	struct pmbus_sensor s = {
308428bf22efSMårten Lindahl 		.page = rdev_get_id(rdev),
308528bf22efSMårten Lindahl 		.class = PSC_VOLTAGE_OUT,
308628bf22efSMårten Lindahl 		.convert = true,
308728bf22efSMårten Lindahl 		.data = -1,
308828bf22efSMårten Lindahl 	};
308928bf22efSMårten Lindahl 	int val = DIV_ROUND_CLOSEST(min_uv, 1000); /* convert to mV */
309028bf22efSMårten Lindahl 	int low, high;
309128bf22efSMårten Lindahl 
309228bf22efSMårten Lindahl 	*selector = 0;
309328bf22efSMårten Lindahl 
309407fb7627SMårten Lindahl 	low = pmbus_regulator_get_low_margin(client, s.page);
309507fb7627SMårten Lindahl 	if (low < 0)
309607fb7627SMårten Lindahl 		return low;
309728bf22efSMårten Lindahl 
309807fb7627SMårten Lindahl 	high = pmbus_regulator_get_high_margin(client, s.page);
309907fb7627SMårten Lindahl 	if (high < 0)
310007fb7627SMårten Lindahl 		return high;
310128bf22efSMårten Lindahl 
310228bf22efSMårten Lindahl 	/* Make sure we are within margins */
310328bf22efSMårten Lindahl 	if (low > val)
310428bf22efSMårten Lindahl 		val = low;
310528bf22efSMårten Lindahl 	if (high < val)
310628bf22efSMårten Lindahl 		val = high;
310728bf22efSMårten Lindahl 
310828bf22efSMårten Lindahl 	val = pmbus_data2reg(data, &s, val);
310928bf22efSMårten Lindahl 
311028bf22efSMårten Lindahl 	return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val);
311128bf22efSMårten Lindahl }
311228bf22efSMårten Lindahl 
pmbus_regulator_list_voltage(struct regulator_dev * rdev,unsigned int selector)31132a20db9bSMårten Lindahl static int pmbus_regulator_list_voltage(struct regulator_dev *rdev,
31142a20db9bSMårten Lindahl 					 unsigned int selector)
31152a20db9bSMårten Lindahl {
31162a20db9bSMårten Lindahl 	struct device *dev = rdev_get_dev(rdev);
31172a20db9bSMårten Lindahl 	struct i2c_client *client = to_i2c_client(dev->parent);
31182a20db9bSMårten Lindahl 	int val, low, high;
31192a20db9bSMårten Lindahl 
31202a20db9bSMårten Lindahl 	if (selector >= rdev->desc->n_voltages ||
31212a20db9bSMårten Lindahl 	    selector < rdev->desc->linear_min_sel)
31222a20db9bSMårten Lindahl 		return -EINVAL;
31232a20db9bSMårten Lindahl 
31242a20db9bSMårten Lindahl 	selector -= rdev->desc->linear_min_sel;
31252a20db9bSMårten Lindahl 	val = DIV_ROUND_CLOSEST(rdev->desc->min_uV +
31262a20db9bSMårten Lindahl 				(rdev->desc->uV_step * selector), 1000); /* convert to mV */
31272a20db9bSMårten Lindahl 
31282a20db9bSMårten Lindahl 	low = pmbus_regulator_get_low_margin(client, rdev_get_id(rdev));
31292a20db9bSMårten Lindahl 	if (low < 0)
31302a20db9bSMårten Lindahl 		return low;
31312a20db9bSMårten Lindahl 
31322a20db9bSMårten Lindahl 	high = pmbus_regulator_get_high_margin(client, rdev_get_id(rdev));
31332a20db9bSMårten Lindahl 	if (high < 0)
31342a20db9bSMårten Lindahl 		return high;
31352a20db9bSMårten Lindahl 
31362a20db9bSMårten Lindahl 	if (val >= low && val <= high)
31372a20db9bSMårten Lindahl 		return val * 1000; /* unit is uV */
31382a20db9bSMårten Lindahl 
31392a20db9bSMårten Lindahl 	return 0;
31402a20db9bSMårten Lindahl }
31412a20db9bSMårten Lindahl 
3142a07d7311SAxel Lin const struct regulator_ops pmbus_regulator_ops = {
3143ddbb4db4SAlan Tull 	.enable = pmbus_regulator_enable,
3144ddbb4db4SAlan Tull 	.disable = pmbus_regulator_disable,
3145ddbb4db4SAlan Tull 	.is_enabled = pmbus_regulator_is_enabled,
3146e0f0307aSZev Weiss 	.get_error_flags = pmbus_regulator_get_error_flags,
3147c05f477cSPatrick Rudolph 	.get_status = pmbus_regulator_get_status,
314828bf22efSMårten Lindahl 	.get_voltage = pmbus_regulator_get_voltage,
314928bf22efSMårten Lindahl 	.set_voltage = pmbus_regulator_set_voltage,
31502a20db9bSMårten Lindahl 	.list_voltage = pmbus_regulator_list_voltage,
3151ddbb4db4SAlan Tull };
3152b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_regulator_ops, PMBUS);
3153ddbb4db4SAlan Tull 
pmbus_regulator_register(struct pmbus_data * data)3154ddbb4db4SAlan Tull static int pmbus_regulator_register(struct pmbus_data *data)
3155ddbb4db4SAlan Tull {
3156ddbb4db4SAlan Tull 	struct device *dev = data->dev;
3157ddbb4db4SAlan Tull 	const struct pmbus_driver_info *info = data->info;
3158ddbb4db4SAlan Tull 	const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
3159ddbb4db4SAlan Tull 	int i;
3160ddbb4db4SAlan Tull 
3161ffe36eb5SNaresh Solanki 	data->rdevs = devm_kzalloc(dev, sizeof(struct regulator_dev *) * info->num_regulators,
3162ffe36eb5SNaresh Solanki 				   GFP_KERNEL);
3163ffe36eb5SNaresh Solanki 	if (!data->rdevs)
3164ffe36eb5SNaresh Solanki 		return -ENOMEM;
3165ffe36eb5SNaresh Solanki 
3166ddbb4db4SAlan Tull 	for (i = 0; i < info->num_regulators; i++) {
3167ddbb4db4SAlan Tull 		struct regulator_config config = { };
3168ddbb4db4SAlan Tull 
3169ddbb4db4SAlan Tull 		config.dev = dev;
3170ddbb4db4SAlan Tull 		config.driver_data = data;
3171ddbb4db4SAlan Tull 
3172ddbb4db4SAlan Tull 		if (pdata && pdata->reg_init_data)
3173ddbb4db4SAlan Tull 			config.init_data = &pdata->reg_init_data[i];
3174ddbb4db4SAlan Tull 
3175ffe36eb5SNaresh Solanki 		data->rdevs[i] = devm_regulator_register(dev, &info->reg_desc[i],
3176ddbb4db4SAlan Tull 							 &config);
3177ffe36eb5SNaresh Solanki 		if (IS_ERR(data->rdevs[i]))
3178ffe36eb5SNaresh Solanki 			return dev_err_probe(dev, PTR_ERR(data->rdevs[i]),
317909e52d17SChristophe JAILLET 					     "Failed to register %s regulator\n",
3180ddbb4db4SAlan Tull 					     info->reg_desc[i].name);
3181ddbb4db4SAlan Tull 	}
3182ddbb4db4SAlan Tull 
3183ddbb4db4SAlan Tull 	return 0;
3184ddbb4db4SAlan Tull }
31857a0c7b9fSNaresh Solanki 
pmbus_regulator_notify(struct pmbus_data * data,int page,int event)31867a0c7b9fSNaresh Solanki static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
31877a0c7b9fSNaresh Solanki {
31887a0c7b9fSNaresh Solanki 		int j;
31897a0c7b9fSNaresh Solanki 
31907a0c7b9fSNaresh Solanki 		for (j = 0; j < data->info->num_regulators; j++) {
31917a0c7b9fSNaresh Solanki 			if (page == rdev_get_id(data->rdevs[j])) {
31927a0c7b9fSNaresh Solanki 				regulator_notifier_call_chain(data->rdevs[j], event, NULL);
31937a0c7b9fSNaresh Solanki 				break;
31947a0c7b9fSNaresh Solanki 			}
31957a0c7b9fSNaresh Solanki 		}
31967a0c7b9fSNaresh Solanki 		return 0;
31977a0c7b9fSNaresh Solanki }
3198ddbb4db4SAlan Tull #else
pmbus_regulator_register(struct pmbus_data * data)3199ddbb4db4SAlan Tull static int pmbus_regulator_register(struct pmbus_data *data)
3200ddbb4db4SAlan Tull {
3201ddbb4db4SAlan Tull 	return 0;
3202ddbb4db4SAlan Tull }
32037a0c7b9fSNaresh Solanki 
pmbus_regulator_notify(struct pmbus_data * data,int page,int event)32047a0c7b9fSNaresh Solanki static int pmbus_regulator_notify(struct pmbus_data *data, int page, int event)
32057a0c7b9fSNaresh Solanki {
32067a0c7b9fSNaresh Solanki 		return 0;
32077a0c7b9fSNaresh Solanki }
3208ddbb4db4SAlan Tull #endif
3209ddbb4db4SAlan Tull 
pmbus_write_smbalert_mask(struct i2c_client * client,u8 page,u8 reg,u8 val)3210221819caSPatrick Rudolph static int pmbus_write_smbalert_mask(struct i2c_client *client, u8 page, u8 reg, u8 val)
3211221819caSPatrick Rudolph {
32124ffcf789SJerome Brunet 	int ret;
32134ffcf789SJerome Brunet 
32144ffcf789SJerome Brunet 	ret = _pmbus_write_word_data(client, page, PMBUS_SMBALERT_MASK, reg | (val << 8));
32154ffcf789SJerome Brunet 
32164ffcf789SJerome Brunet 	/*
32174ffcf789SJerome Brunet 	 * Clear fault systematically in case writing PMBUS_SMBALERT_MASK
32184ffcf789SJerome Brunet 	 * is not supported by the chip.
32194ffcf789SJerome Brunet 	 */
32204ffcf789SJerome Brunet 	pmbus_clear_fault_page(client, page);
32214ffcf789SJerome Brunet 
32224ffcf789SJerome Brunet 	return ret;
3223221819caSPatrick Rudolph }
3224221819caSPatrick Rudolph 
pmbus_fault_handler(int irq,void * pdata)3225221819caSPatrick Rudolph static irqreturn_t pmbus_fault_handler(int irq, void *pdata)
3226221819caSPatrick Rudolph {
3227221819caSPatrick Rudolph 	struct pmbus_data *data = pdata;
3228221819caSPatrick Rudolph 	struct i2c_client *client = to_i2c_client(data->dev);
3229f74f06f4SPatrick Rudolph 
3230f74f06f4SPatrick Rudolph 	int i, status, event;
3231221819caSPatrick Rudolph 	mutex_lock(&data->update_lock);
32327a0c7b9fSNaresh Solanki 	for (i = 0; i < data->info->pages; i++) {
3233f74f06f4SPatrick Rudolph 		_pmbus_get_flags(data, i, &status, &event, true);
3234221819caSPatrick Rudolph 
32357a0c7b9fSNaresh Solanki 		if (event)
32367a0c7b9fSNaresh Solanki 			pmbus_regulator_notify(data, i, event);
32377a0c7b9fSNaresh Solanki 	}
32387a0c7b9fSNaresh Solanki 
3239221819caSPatrick Rudolph 	pmbus_clear_faults(client);
3240221819caSPatrick Rudolph 	mutex_unlock(&data->update_lock);
3241221819caSPatrick Rudolph 
3242221819caSPatrick Rudolph 	return IRQ_HANDLED;
3243221819caSPatrick Rudolph }
3244221819caSPatrick Rudolph 
pmbus_irq_setup(struct i2c_client * client,struct pmbus_data * data)3245221819caSPatrick Rudolph static int pmbus_irq_setup(struct i2c_client *client, struct pmbus_data *data)
3246221819caSPatrick Rudolph {
3247221819caSPatrick Rudolph 	struct device *dev = &client->dev;
3248221819caSPatrick Rudolph 	const struct pmbus_status_category *cat;
3249221819caSPatrick Rudolph 	const struct pmbus_status_assoc *bit;
3250221819caSPatrick Rudolph 	int i, j, err, func;
3251221819caSPatrick Rudolph 	u8 mask;
3252221819caSPatrick Rudolph 
3253221819caSPatrick Rudolph 	static const u8 misc_status[] = {PMBUS_STATUS_CML, PMBUS_STATUS_OTHER,
3254221819caSPatrick Rudolph 					 PMBUS_STATUS_MFR_SPECIFIC, PMBUS_STATUS_FAN_12,
3255221819caSPatrick Rudolph 					 PMBUS_STATUS_FAN_34};
3256221819caSPatrick Rudolph 
3257221819caSPatrick Rudolph 	if (!client->irq)
3258221819caSPatrick Rudolph 		return 0;
3259221819caSPatrick Rudolph 
3260221819caSPatrick Rudolph 	for (i = 0; i < data->info->pages; i++) {
3261221819caSPatrick Rudolph 		func = data->info->func[i];
3262221819caSPatrick Rudolph 
3263221819caSPatrick Rudolph 		for (j = 0; j < ARRAY_SIZE(pmbus_status_flag_map); j++) {
3264221819caSPatrick Rudolph 			cat = &pmbus_status_flag_map[j];
3265221819caSPatrick Rudolph 			if (!(func & cat->func))
3266221819caSPatrick Rudolph 				continue;
3267221819caSPatrick Rudolph 			mask = 0;
3268221819caSPatrick Rudolph 			for (bit = cat->bits; bit->pflag; bit++)
3269221819caSPatrick Rudolph 				mask |= bit->pflag;
3270221819caSPatrick Rudolph 
3271221819caSPatrick Rudolph 			err = pmbus_write_smbalert_mask(client, i, cat->reg, ~mask);
3272221819caSPatrick Rudolph 			if (err)
3273221819caSPatrick Rudolph 				dev_dbg_once(dev, "Failed to set smbalert for reg 0x%02x\n",
3274221819caSPatrick Rudolph 					     cat->reg);
3275221819caSPatrick Rudolph 		}
3276221819caSPatrick Rudolph 
3277221819caSPatrick Rudolph 		for (j = 0; j < ARRAY_SIZE(misc_status); j++)
3278221819caSPatrick Rudolph 			pmbus_write_smbalert_mask(client, i, misc_status[j], 0xff);
3279221819caSPatrick Rudolph 	}
3280221819caSPatrick Rudolph 
3281221819caSPatrick Rudolph 	/* Register notifiers */
3282ab3e0041SGuenter Roeck 	err = devm_request_threaded_irq(dev, client->irq, NULL, pmbus_fault_handler,
3283ab3e0041SGuenter Roeck 					IRQF_ONESHOT, "pmbus-irq", data);
3284221819caSPatrick Rudolph 	if (err) {
3285221819caSPatrick Rudolph 		dev_err(dev, "failed to request an irq %d\n", err);
3286221819caSPatrick Rudolph 		return err;
3287221819caSPatrick Rudolph 	}
3288221819caSPatrick Rudolph 
3289221819caSPatrick Rudolph 	return 0;
3290221819caSPatrick Rudolph }
3291221819caSPatrick Rudolph 
32921e069dfdSEdward A. James static struct dentry *pmbus_debugfs_dir;	/* pmbus debugfs directory */
32931e069dfdSEdward A. James 
32941e069dfdSEdward A. James #if IS_ENABLED(CONFIG_DEBUG_FS)
pmbus_debugfs_get(void * data,u64 * val)32951e069dfdSEdward A. James static int pmbus_debugfs_get(void *data, u64 *val)
32961e069dfdSEdward A. James {
32971e069dfdSEdward A. James 	int rc;
32981e069dfdSEdward A. James 	struct pmbus_debugfs_entry *entry = data;
3299a7ac3718SEddie James 	struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
33001e069dfdSEdward A. James 
3301a7ac3718SEddie James 	rc = mutex_lock_interruptible(&pdata->update_lock);
3302a7ac3718SEddie James 	if (rc)
3303a7ac3718SEddie James 		return rc;
33041e069dfdSEdward A. James 	rc = _pmbus_read_byte_data(entry->client, entry->page, entry->reg);
3305a7ac3718SEddie James 	mutex_unlock(&pdata->update_lock);
33061e069dfdSEdward A. James 	if (rc < 0)
33071e069dfdSEdward A. James 		return rc;
33081e069dfdSEdward A. James 
33091e069dfdSEdward A. James 	*val = rc;
33101e069dfdSEdward A. James 
33111e069dfdSEdward A. James 	return 0;
33121e069dfdSEdward A. James }
33131e069dfdSEdward A. James DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops, pmbus_debugfs_get, NULL,
33141e069dfdSEdward A. James 			 "0x%02llx\n");
33151e069dfdSEdward A. James 
pmbus_debugfs_get_status(void * data,u64 * val)33161e069dfdSEdward A. James static int pmbus_debugfs_get_status(void *data, u64 *val)
33171e069dfdSEdward A. James {
33181e069dfdSEdward A. James 	int rc;
33191e069dfdSEdward A. James 	struct pmbus_debugfs_entry *entry = data;
33201e069dfdSEdward A. James 	struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
33211e069dfdSEdward A. James 
3322a7ac3718SEddie James 	rc = mutex_lock_interruptible(&pdata->update_lock);
3323a7ac3718SEddie James 	if (rc)
3324a7ac3718SEddie James 		return rc;
33251e069dfdSEdward A. James 	rc = pdata->read_status(entry->client, entry->page);
3326a7ac3718SEddie James 	mutex_unlock(&pdata->update_lock);
33271e069dfdSEdward A. James 	if (rc < 0)
33281e069dfdSEdward A. James 		return rc;
33291e069dfdSEdward A. James 
33301e069dfdSEdward A. James 	*val = rc;
33311e069dfdSEdward A. James 
33321e069dfdSEdward A. James 	return 0;
33331e069dfdSEdward A. James }
33341e069dfdSEdward A. James DEFINE_DEBUGFS_ATTRIBUTE(pmbus_debugfs_ops_status, pmbus_debugfs_get_status,
33351e069dfdSEdward A. James 			 NULL, "0x%04llx\n");
33361e069dfdSEdward A. James 
pmbus_debugfs_mfr_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)33376fd58423SAdam Wujek static ssize_t pmbus_debugfs_mfr_read(struct file *file, char __user *buf,
33386fd58423SAdam Wujek 				       size_t count, loff_t *ppos)
33396fd58423SAdam Wujek {
33406fd58423SAdam Wujek 	int rc;
33416fd58423SAdam Wujek 	struct pmbus_debugfs_entry *entry = file->private_data;
3342a7ac3718SEddie James 	struct pmbus_data *pdata = i2c_get_clientdata(entry->client);
33436fd58423SAdam Wujek 	char data[I2C_SMBUS_BLOCK_MAX + 2] = { 0 };
33446fd58423SAdam Wujek 
3345a7ac3718SEddie James 	rc = mutex_lock_interruptible(&pdata->update_lock);
3346a7ac3718SEddie James 	if (rc)
3347a7ac3718SEddie James 		return rc;
33486fd58423SAdam Wujek 	rc = pmbus_read_block_data(entry->client, entry->page, entry->reg,
33496fd58423SAdam Wujek 				   data);
3350a7ac3718SEddie James 	mutex_unlock(&pdata->update_lock);
33516fd58423SAdam Wujek 	if (rc < 0)
33526fd58423SAdam Wujek 		return rc;
33536fd58423SAdam Wujek 
33546fd58423SAdam Wujek 	/* Add newline at the end of a read data */
33556fd58423SAdam Wujek 	data[rc] = '\n';
33566fd58423SAdam Wujek 
33576fd58423SAdam Wujek 	/* Include newline into the length */
33586fd58423SAdam Wujek 	rc += 1;
33596fd58423SAdam Wujek 
33606fd58423SAdam Wujek 	return simple_read_from_buffer(buf, count, ppos, data, rc);
33616fd58423SAdam Wujek }
33626fd58423SAdam Wujek 
33636fd58423SAdam Wujek static const struct file_operations pmbus_debugfs_ops_mfr = {
33646fd58423SAdam Wujek 	.llseek = noop_llseek,
33656fd58423SAdam Wujek 	.read = pmbus_debugfs_mfr_read,
33666fd58423SAdam Wujek 	.write = NULL,
33676fd58423SAdam Wujek 	.open = simple_open,
33686fd58423SAdam Wujek };
33696fd58423SAdam Wujek 
pmbus_remove_debugfs(void * data)33703bce071aSBartosz Golaszewski static void pmbus_remove_debugfs(void *data)
33713bce071aSBartosz Golaszewski {
33723bce071aSBartosz Golaszewski 	struct dentry *entry = data;
33733bce071aSBartosz Golaszewski 
33743bce071aSBartosz Golaszewski 	debugfs_remove_recursive(entry);
33753bce071aSBartosz Golaszewski }
33763bce071aSBartosz Golaszewski 
pmbus_init_debugfs(struct i2c_client * client,struct pmbus_data * data)33771e069dfdSEdward A. James static int pmbus_init_debugfs(struct i2c_client *client,
33781e069dfdSEdward A. James 			      struct pmbus_data *data)
33791e069dfdSEdward A. James {
33801e069dfdSEdward A. James 	int i, idx = 0;
33811e069dfdSEdward A. James 	char name[PMBUS_NAME_SIZE];
33821e069dfdSEdward A. James 	struct pmbus_debugfs_entry *entries;
33831e069dfdSEdward A. James 
33841e069dfdSEdward A. James 	if (!pmbus_debugfs_dir)
33851e069dfdSEdward A. James 		return -ENODEV;
33861e069dfdSEdward A. James 
33871e069dfdSEdward A. James 	/*
33881e069dfdSEdward A. James 	 * Create the debugfs directory for this device. Use the hwmon device
33891e069dfdSEdward A. James 	 * name to avoid conflicts (hwmon numbers are globally unique).
33901e069dfdSEdward A. James 	 */
33911e069dfdSEdward A. James 	data->debugfs = debugfs_create_dir(dev_name(data->hwmon_dev),
33921e069dfdSEdward A. James 					   pmbus_debugfs_dir);
33931e069dfdSEdward A. James 	if (IS_ERR_OR_NULL(data->debugfs)) {
33941e069dfdSEdward A. James 		data->debugfs = NULL;
33951e069dfdSEdward A. James 		return -ENODEV;
33961e069dfdSEdward A. James 	}
33971e069dfdSEdward A. James 
33986fd58423SAdam Wujek 	/*
33996fd58423SAdam Wujek 	 * Allocate the max possible entries we need.
3400*d48dc0bdSNinad Palsule 	 * 7 entries device-specific
34016fd58423SAdam Wujek 	 * 10 entries page-specific
34026fd58423SAdam Wujek 	 */
3403a86854d0SKees Cook 	entries = devm_kcalloc(data->dev,
3404*d48dc0bdSNinad Palsule 			       7 + data->info->pages * 10, sizeof(*entries),
34051e069dfdSEdward A. James 			       GFP_KERNEL);
34061e069dfdSEdward A. James 	if (!entries)
34071e069dfdSEdward A. James 		return -ENOMEM;
34081e069dfdSEdward A. James 
34096fd58423SAdam Wujek 	/*
34106fd58423SAdam Wujek 	 * Add device-specific entries.
34116fd58423SAdam Wujek 	 * Please note that the PMBUS standard allows all registers to be
34126fd58423SAdam Wujek 	 * page-specific.
34136fd58423SAdam Wujek 	 * To reduce the number of debugfs entries for devices with many pages
34146fd58423SAdam Wujek 	 * assume that values of the following registers are the same for all
34156fd58423SAdam Wujek 	 * pages and report values only for page 0.
34166fd58423SAdam Wujek 	 */
3417*d48dc0bdSNinad Palsule 	if (pmbus_check_byte_register(client, 0, PMBUS_REVISION)) {
3418*d48dc0bdSNinad Palsule 		entries[idx].client = client;
3419*d48dc0bdSNinad Palsule 		entries[idx].page = 0;
3420*d48dc0bdSNinad Palsule 		entries[idx].reg = PMBUS_REVISION;
3421*d48dc0bdSNinad Palsule 		debugfs_create_file("revision", 0444, data->debugfs,
3422*d48dc0bdSNinad Palsule 				    &entries[idx++],
3423*d48dc0bdSNinad Palsule 				    &pmbus_debugfs_ops);
3424*d48dc0bdSNinad Palsule 	}
3425*d48dc0bdSNinad Palsule 
34266fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_ID)) {
34276fd58423SAdam Wujek 		entries[idx].client = client;
34286fd58423SAdam Wujek 		entries[idx].page = 0;
34296fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_ID;
34306fd58423SAdam Wujek 		debugfs_create_file("mfr_id", 0444, data->debugfs,
34316fd58423SAdam Wujek 				    &entries[idx++],
34326fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34336fd58423SAdam Wujek 	}
34346fd58423SAdam Wujek 
34356fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_MODEL)) {
34366fd58423SAdam Wujek 		entries[idx].client = client;
34376fd58423SAdam Wujek 		entries[idx].page = 0;
34386fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_MODEL;
34396fd58423SAdam Wujek 		debugfs_create_file("mfr_model", 0444, data->debugfs,
34406fd58423SAdam Wujek 				    &entries[idx++],
34416fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34426fd58423SAdam Wujek 	}
34436fd58423SAdam Wujek 
34446fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_REVISION)) {
34456fd58423SAdam Wujek 		entries[idx].client = client;
34466fd58423SAdam Wujek 		entries[idx].page = 0;
34476fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_REVISION;
34486fd58423SAdam Wujek 		debugfs_create_file("mfr_revision", 0444, data->debugfs,
34496fd58423SAdam Wujek 				    &entries[idx++],
34506fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34516fd58423SAdam Wujek 	}
34526fd58423SAdam Wujek 
34536fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_LOCATION)) {
34546fd58423SAdam Wujek 		entries[idx].client = client;
34556fd58423SAdam Wujek 		entries[idx].page = 0;
34566fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_LOCATION;
34576fd58423SAdam Wujek 		debugfs_create_file("mfr_location", 0444, data->debugfs,
34586fd58423SAdam Wujek 				    &entries[idx++],
34596fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34606fd58423SAdam Wujek 	}
34616fd58423SAdam Wujek 
34626fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_DATE)) {
34636fd58423SAdam Wujek 		entries[idx].client = client;
34646fd58423SAdam Wujek 		entries[idx].page = 0;
34656fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_DATE;
34666fd58423SAdam Wujek 		debugfs_create_file("mfr_date", 0444, data->debugfs,
34676fd58423SAdam Wujek 				    &entries[idx++],
34686fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34696fd58423SAdam Wujek 	}
34706fd58423SAdam Wujek 
34716fd58423SAdam Wujek 	if (pmbus_check_block_register(client, 0, PMBUS_MFR_SERIAL)) {
34726fd58423SAdam Wujek 		entries[idx].client = client;
34736fd58423SAdam Wujek 		entries[idx].page = 0;
34746fd58423SAdam Wujek 		entries[idx].reg = PMBUS_MFR_SERIAL;
34756fd58423SAdam Wujek 		debugfs_create_file("mfr_serial", 0444, data->debugfs,
34766fd58423SAdam Wujek 				    &entries[idx++],
34776fd58423SAdam Wujek 				    &pmbus_debugfs_ops_mfr);
34786fd58423SAdam Wujek 	}
34796fd58423SAdam Wujek 
34806fd58423SAdam Wujek 	/* Add page specific entries */
34811e069dfdSEdward A. James 	for (i = 0; i < data->info->pages; ++i) {
34821e069dfdSEdward A. James 		/* Check accessibility of status register if it's not page 0 */
34831e069dfdSEdward A. James 		if (!i || pmbus_check_status_register(client, i)) {
34841e069dfdSEdward A. James 			/* No need to set reg as we have special read op. */
34851e069dfdSEdward A. James 			entries[idx].client = client;
34861e069dfdSEdward A. James 			entries[idx].page = i;
34871e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d", i);
34881e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
34891e069dfdSEdward A. James 					    &entries[idx++],
34901e069dfdSEdward A. James 					    &pmbus_debugfs_ops_status);
34911e069dfdSEdward A. James 		}
34921e069dfdSEdward A. James 
34931e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_VOUT) {
34941e069dfdSEdward A. James 			entries[idx].client = client;
34951e069dfdSEdward A. James 			entries[idx].page = i;
34961e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_VOUT;
34971e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_vout", i);
34981e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
34991e069dfdSEdward A. James 					    &entries[idx++],
35001e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35011e069dfdSEdward A. James 		}
35021e069dfdSEdward A. James 
35031e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_IOUT) {
35041e069dfdSEdward A. James 			entries[idx].client = client;
35051e069dfdSEdward A. James 			entries[idx].page = i;
35061e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_IOUT;
35071e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_iout", i);
35081e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35091e069dfdSEdward A. James 					    &entries[idx++],
35101e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35111e069dfdSEdward A. James 		}
35121e069dfdSEdward A. James 
35131e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_INPUT) {
35141e069dfdSEdward A. James 			entries[idx].client = client;
35151e069dfdSEdward A. James 			entries[idx].page = i;
35161e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_INPUT;
35171e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_input", i);
35181e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35191e069dfdSEdward A. James 					    &entries[idx++],
35201e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35211e069dfdSEdward A. James 		}
35221e069dfdSEdward A. James 
35231e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_TEMP) {
35241e069dfdSEdward A. James 			entries[idx].client = client;
35251e069dfdSEdward A. James 			entries[idx].page = i;
35261e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_TEMPERATURE;
35271e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_temp", i);
35281e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35291e069dfdSEdward A. James 					    &entries[idx++],
35301e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35311e069dfdSEdward A. James 		}
35321e069dfdSEdward A. James 
35331e069dfdSEdward A. James 		if (pmbus_check_byte_register(client, i, PMBUS_STATUS_CML)) {
35341e069dfdSEdward A. James 			entries[idx].client = client;
35351e069dfdSEdward A. James 			entries[idx].page = i;
35361e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_CML;
35371e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_cml", i);
35381e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35391e069dfdSEdward A. James 					    &entries[idx++],
35401e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35411e069dfdSEdward A. James 		}
35421e069dfdSEdward A. James 
35431e069dfdSEdward A. James 		if (pmbus_check_byte_register(client, i, PMBUS_STATUS_OTHER)) {
35441e069dfdSEdward A. James 			entries[idx].client = client;
35451e069dfdSEdward A. James 			entries[idx].page = i;
35461e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_OTHER;
35471e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_other", i);
35481e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35491e069dfdSEdward A. James 					    &entries[idx++],
35501e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35511e069dfdSEdward A. James 		}
35521e069dfdSEdward A. James 
35531e069dfdSEdward A. James 		if (pmbus_check_byte_register(client, i,
35541e069dfdSEdward A. James 					      PMBUS_STATUS_MFR_SPECIFIC)) {
35551e069dfdSEdward A. James 			entries[idx].client = client;
35561e069dfdSEdward A. James 			entries[idx].page = i;
35571e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_MFR_SPECIFIC;
35581e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_mfr", i);
35591e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35601e069dfdSEdward A. James 					    &entries[idx++],
35611e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35621e069dfdSEdward A. James 		}
35631e069dfdSEdward A. James 
35641e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN12) {
35651e069dfdSEdward A. James 			entries[idx].client = client;
35661e069dfdSEdward A. James 			entries[idx].page = i;
35671e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_FAN_12;
35681e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan12", i);
35691e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35701e069dfdSEdward A. James 					    &entries[idx++],
35711e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35721e069dfdSEdward A. James 		}
35731e069dfdSEdward A. James 
35741e069dfdSEdward A. James 		if (data->info->func[i] & PMBUS_HAVE_STATUS_FAN34) {
35751e069dfdSEdward A. James 			entries[idx].client = client;
35761e069dfdSEdward A. James 			entries[idx].page = i;
35771e069dfdSEdward A. James 			entries[idx].reg = PMBUS_STATUS_FAN_34;
35781e069dfdSEdward A. James 			scnprintf(name, PMBUS_NAME_SIZE, "status%d_fan34", i);
35791e069dfdSEdward A. James 			debugfs_create_file(name, 0444, data->debugfs,
35801e069dfdSEdward A. James 					    &entries[idx++],
35811e069dfdSEdward A. James 					    &pmbus_debugfs_ops);
35821e069dfdSEdward A. James 		}
35831e069dfdSEdward A. James 	}
35841e069dfdSEdward A. James 
35853bce071aSBartosz Golaszewski 	return devm_add_action_or_reset(data->dev,
35863bce071aSBartosz Golaszewski 					pmbus_remove_debugfs, data->debugfs);
35871e069dfdSEdward A. James }
35881e069dfdSEdward A. James #else
pmbus_init_debugfs(struct i2c_client * client,struct pmbus_data * data)35891e069dfdSEdward A. James static int pmbus_init_debugfs(struct i2c_client *client,
35901e069dfdSEdward A. James 			      struct pmbus_data *data)
35911e069dfdSEdward A. James {
35921e069dfdSEdward A. James 	return 0;
35931e069dfdSEdward A. James }
35941e069dfdSEdward A. James #endif	/* IS_ENABLED(CONFIG_DEBUG_FS) */
35951e069dfdSEdward A. James 
pmbus_do_probe(struct i2c_client * client,struct pmbus_driver_info * info)3596dd431939SStephen Kitt int pmbus_do_probe(struct i2c_client *client, struct pmbus_driver_info *info)
35979d2ecfb7SGuenter Roeck {
3598c2a58351SGuenter Roeck 	struct device *dev = &client->dev;
3599a8b3a3a5SJingoo Han 	const struct pmbus_platform_data *pdata = dev_get_platdata(dev);
36009d2ecfb7SGuenter Roeck 	struct pmbus_data *data;
3601991d6799Skrzysztof.adamski@nokia.com 	size_t groups_num = 0;
36029d2ecfb7SGuenter Roeck 	int ret;
3603ed359056SVincent Whitchurch 	int i;
3604f807e8beSChris Packham 	char *name;
36059d2ecfb7SGuenter Roeck 
360677493ef6SGuenter Roeck 	if (!info)
36079d2ecfb7SGuenter Roeck 		return -ENODEV;
36089d2ecfb7SGuenter Roeck 
36099d2ecfb7SGuenter Roeck 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE
36109d2ecfb7SGuenter Roeck 				     | I2C_FUNC_SMBUS_BYTE_DATA
36119d2ecfb7SGuenter Roeck 				     | I2C_FUNC_SMBUS_WORD_DATA))
36129d2ecfb7SGuenter Roeck 		return -ENODEV;
36139d2ecfb7SGuenter Roeck 
3614c2a58351SGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
361577493ef6SGuenter Roeck 	if (!data)
36169d2ecfb7SGuenter Roeck 		return -ENOMEM;
36179d2ecfb7SGuenter Roeck 
3618991d6799Skrzysztof.adamski@nokia.com 	if (info->groups)
3619991d6799Skrzysztof.adamski@nokia.com 		while (info->groups[groups_num])
3620991d6799Skrzysztof.adamski@nokia.com 			groups_num++;
3621991d6799Skrzysztof.adamski@nokia.com 
3622991d6799Skrzysztof.adamski@nokia.com 	data->groups = devm_kcalloc(dev, groups_num + 2, sizeof(void *),
3623991d6799Skrzysztof.adamski@nokia.com 				    GFP_KERNEL);
3624991d6799Skrzysztof.adamski@nokia.com 	if (!data->groups)
3625991d6799Skrzysztof.adamski@nokia.com 		return -ENOMEM;
3626991d6799Skrzysztof.adamski@nokia.com 
36279d2ecfb7SGuenter Roeck 	i2c_set_clientdata(client, data);
36289d2ecfb7SGuenter Roeck 	mutex_init(&data->update_lock);
36290328461eSGuenter Roeck 	data->dev = dev;
36309d2ecfb7SGuenter Roeck 
36319d2ecfb7SGuenter Roeck 	if (pdata)
36329d2ecfb7SGuenter Roeck 		data->flags = pdata->flags;
36339d2ecfb7SGuenter Roeck 	data->info = info;
3634d86f3c9bSGuenter Roeck 	data->currpage = -1;
3635d86f3c9bSGuenter Roeck 	data->currphase = -1;
36369d2ecfb7SGuenter Roeck 
3637ed359056SVincent Whitchurch 	for (i = 0; i < ARRAY_SIZE(data->vout_low); i++) {
3638ed359056SVincent Whitchurch 		data->vout_low[i] = -1;
3639ed359056SVincent Whitchurch 		data->vout_high[i] = -1;
3640ed359056SVincent Whitchurch 	}
3641ed359056SVincent Whitchurch 
364216c6d01fSGuenter Roeck 	ret = pmbus_init_common(client, data, info);
364316c6d01fSGuenter Roeck 	if (ret < 0)
36448b313ca7SGuenter Roeck 		return ret;
36459d2ecfb7SGuenter Roeck 
36460328461eSGuenter Roeck 	ret = pmbus_find_attributes(client, data);
36470328461eSGuenter Roeck 	if (ret)
3648bb19133fSBartosz Golaszewski 		return ret;
36499d2ecfb7SGuenter Roeck 
36509d2ecfb7SGuenter Roeck 	/*
36519d2ecfb7SGuenter Roeck 	 * If there are no attributes, something is wrong.
36529d2ecfb7SGuenter Roeck 	 * Bail out instead of trying to register nothing.
36539d2ecfb7SGuenter Roeck 	 */
36549d2ecfb7SGuenter Roeck 	if (!data->num_attributes) {
3655c2a58351SGuenter Roeck 		dev_err(dev, "No attributes found\n");
3656bb19133fSBartosz Golaszewski 		return -ENODEV;
36579d2ecfb7SGuenter Roeck 	}
36589d2ecfb7SGuenter Roeck 
3659f807e8beSChris Packham 	name = devm_kstrdup(dev, client->name, GFP_KERNEL);
3660f807e8beSChris Packham 	if (!name)
3661f807e8beSChris Packham 		return -ENOMEM;
3662f807e8beSChris Packham 	strreplace(name, '-', '_');
3663f807e8beSChris Packham 
3664c3b7cddcSGuenter Roeck 	data->groups[0] = &data->group;
3665991d6799Skrzysztof.adamski@nokia.com 	memcpy(data->groups + 1, info->groups, sizeof(void *) * groups_num);
3666bb19133fSBartosz Golaszewski 	data->hwmon_dev = devm_hwmon_device_register_with_groups(dev,
3667f807e8beSChris Packham 					name, data, data->groups);
36689d2ecfb7SGuenter Roeck 	if (IS_ERR(data->hwmon_dev)) {
3669c2a58351SGuenter Roeck 		dev_err(dev, "Failed to register hwmon device\n");
3670bb19133fSBartosz Golaszewski 		return PTR_ERR(data->hwmon_dev);
36719d2ecfb7SGuenter Roeck 	}
3672ddbb4db4SAlan Tull 
3673ddbb4db4SAlan Tull 	ret = pmbus_regulator_register(data);
3674ddbb4db4SAlan Tull 	if (ret)
3675bb19133fSBartosz Golaszewski 		return ret;
3676ddbb4db4SAlan Tull 
3677221819caSPatrick Rudolph 	ret = pmbus_irq_setup(client, data);
3678221819caSPatrick Rudolph 	if (ret)
3679221819caSPatrick Rudolph 		return ret;
3680221819caSPatrick Rudolph 
36811e069dfdSEdward A. James 	ret = pmbus_init_debugfs(client, data);
36821e069dfdSEdward A. James 	if (ret)
36831e069dfdSEdward A. James 		dev_warn(dev, "Failed to register debugfs\n");
36841e069dfdSEdward A. James 
36859d2ecfb7SGuenter Roeck 	return 0;
36869d2ecfb7SGuenter Roeck }
3687b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_do_probe, PMBUS);
36889d2ecfb7SGuenter Roeck 
pmbus_get_debugfs_dir(struct i2c_client * client)3689eb6489b6SEdward A. James struct dentry *pmbus_get_debugfs_dir(struct i2c_client *client)
3690eb6489b6SEdward A. James {
3691eb6489b6SEdward A. James 	struct pmbus_data *data = i2c_get_clientdata(client);
3692eb6489b6SEdward A. James 
3693eb6489b6SEdward A. James 	return data->debugfs;
3694eb6489b6SEdward A. James }
3695b94ca77eSGuenter Roeck EXPORT_SYMBOL_NS_GPL(pmbus_get_debugfs_dir, PMBUS);
3696eb6489b6SEdward A. James 
pmbus_lock_interruptible(struct i2c_client * client)3697a7ac3718SEddie James int pmbus_lock_interruptible(struct i2c_client *client)
3698a7ac3718SEddie James {
3699a7ac3718SEddie James 	struct pmbus_data *data = i2c_get_clientdata(client);
3700a7ac3718SEddie James 
3701a7ac3718SEddie James 	return mutex_lock_interruptible(&data->update_lock);
3702a7ac3718SEddie James }
3703a7ac3718SEddie James EXPORT_SYMBOL_NS_GPL(pmbus_lock_interruptible, PMBUS);
3704a7ac3718SEddie James 
pmbus_unlock(struct i2c_client * client)3705a7ac3718SEddie James void pmbus_unlock(struct i2c_client *client)
3706a7ac3718SEddie James {
3707a7ac3718SEddie James 	struct pmbus_data *data = i2c_get_clientdata(client);
3708a7ac3718SEddie James 
3709a7ac3718SEddie James 	mutex_unlock(&data->update_lock);
3710a7ac3718SEddie James }
3711a7ac3718SEddie James EXPORT_SYMBOL_NS_GPL(pmbus_unlock, PMBUS);
3712a7ac3718SEddie James 
pmbus_core_init(void)37131e069dfdSEdward A. James static int __init pmbus_core_init(void)
37141e069dfdSEdward A. James {
37151e069dfdSEdward A. James 	pmbus_debugfs_dir = debugfs_create_dir("pmbus", NULL);
37161e069dfdSEdward A. James 	if (IS_ERR(pmbus_debugfs_dir))
37171e069dfdSEdward A. James 		pmbus_debugfs_dir = NULL;
37181e069dfdSEdward A. James 
37191e069dfdSEdward A. James 	return 0;
37201e069dfdSEdward A. James }
37211e069dfdSEdward A. James 
pmbus_core_exit(void)37221e069dfdSEdward A. James static void __exit pmbus_core_exit(void)
37231e069dfdSEdward A. James {
37241e069dfdSEdward A. James 	debugfs_remove_recursive(pmbus_debugfs_dir);
37251e069dfdSEdward A. James }
37261e069dfdSEdward A. James 
37271e069dfdSEdward A. James module_init(pmbus_core_init);
37281e069dfdSEdward A. James module_exit(pmbus_core_exit);
37291e069dfdSEdward A. James 
37309d2ecfb7SGuenter Roeck MODULE_AUTHOR("Guenter Roeck");
37319d2ecfb7SGuenter Roeck MODULE_DESCRIPTION("PMBus core driver");
37329d2ecfb7SGuenter Roeck MODULE_LICENSE("GPL");
3733