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(®->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, ®->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