Commit 442aba78728e77e03172ba83e905b9aff96febcf
1 parent
06923f8442
Exists in
master
and in
4 other branches
hwmon: PMBus device driver
This driver adds support for hardware monitoring features of various PMBus devices. Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com> Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
Showing 6 changed files with 2218 additions and 0 deletions Side-by-side Diff
drivers/hwmon/Kconfig
... | ... | @@ -756,6 +756,31 @@ |
756 | 756 | These devices are hard to detect and rarely found on mainstream |
757 | 757 | hardware. If unsure, say N. |
758 | 758 | |
759 | +config PMBUS | |
760 | + tristate "PMBus support" | |
761 | + depends on I2C && EXPERIMENTAL | |
762 | + default n | |
763 | + help | |
764 | + Say yes here if you want to enable PMBus support. | |
765 | + | |
766 | + This driver can also be built as a module. If so, the module will | |
767 | + be called pmbus_core. | |
768 | + | |
769 | +if PMBUS | |
770 | + | |
771 | +config SENSORS_PMBUS | |
772 | + tristate "Generic PMBus devices" | |
773 | + default n | |
774 | + help | |
775 | + If you say yes here you get hardware monitoring support for generic | |
776 | + PMBus devices, including but not limited to BMR450, BMR451, BMR453, | |
777 | + BMR454, and LTC2978. | |
778 | + | |
779 | + This driver can also be built as a module. If so, the module will | |
780 | + be called pmbus. | |
781 | + | |
782 | +endif # PMBUS | |
783 | + | |
759 | 784 | config SENSORS_SHT15 |
760 | 785 | tristate "Sensiron humidity and temperature sensors. SHT15 and compat." |
761 | 786 | depends on GENERIC_GPIO |
drivers/hwmon/Makefile
... | ... | @@ -114,6 +114,10 @@ |
114 | 114 | obj-$(CONFIG_SENSORS_WM831X) += wm831x-hwmon.o |
115 | 115 | obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o |
116 | 116 | |
117 | +# PMBus drivers | |
118 | +obj-$(CONFIG_PMBUS) += pmbus_core.o | |
119 | +obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o | |
120 | + | |
117 | 121 | ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) |
118 | 122 | EXTRA_CFLAGS += -DDEBUG |
119 | 123 | endif |
drivers/hwmon/pmbus.c
1 | +/* | |
2 | + * Hardware monitoring driver for PMBus devices | |
3 | + * | |
4 | + * Copyright (c) 2010, 2011 Ericsson AB. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#include <linux/kernel.h> | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/err.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include <linux/mutex.h> | |
27 | +#include <linux/i2c.h> | |
28 | +#include "pmbus.h" | |
29 | + | |
30 | +/* | |
31 | + * Find sensor groups and status registers on each page. | |
32 | + */ | |
33 | +static void pmbus_find_sensor_groups(struct i2c_client *client, | |
34 | + struct pmbus_driver_info *info) | |
35 | +{ | |
36 | + int page; | |
37 | + | |
38 | + /* Sensors detected on page 0 only */ | |
39 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_VIN)) | |
40 | + info->func[0] |= PMBUS_HAVE_VIN; | |
41 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_VCAP)) | |
42 | + info->func[0] |= PMBUS_HAVE_VCAP; | |
43 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_IIN)) | |
44 | + info->func[0] |= PMBUS_HAVE_IIN; | |
45 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_PIN)) | |
46 | + info->func[0] |= PMBUS_HAVE_PIN; | |
47 | + if (info->func[0] | |
48 | + && pmbus_check_byte_register(client, 0, PMBUS_STATUS_INPUT)) | |
49 | + info->func[0] |= PMBUS_HAVE_STATUS_INPUT; | |
50 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_1)) { | |
51 | + info->func[0] |= PMBUS_HAVE_FAN12; | |
52 | + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_12)) | |
53 | + info->func[0] |= PMBUS_HAVE_STATUS_FAN12; | |
54 | + } | |
55 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_FAN_SPEED_3)) { | |
56 | + info->func[0] |= PMBUS_HAVE_FAN34; | |
57 | + if (pmbus_check_byte_register(client, 0, PMBUS_STATUS_FAN_34)) | |
58 | + info->func[0] |= PMBUS_HAVE_STATUS_FAN34; | |
59 | + } | |
60 | + if (pmbus_check_word_register(client, 0, PMBUS_READ_TEMPERATURE_1)) { | |
61 | + info->func[0] |= PMBUS_HAVE_TEMP; | |
62 | + if (pmbus_check_byte_register(client, 0, | |
63 | + PMBUS_STATUS_TEMPERATURE)) | |
64 | + info->func[0] |= PMBUS_HAVE_STATUS_TEMP; | |
65 | + } | |
66 | + | |
67 | + /* Sensors detected on all pages */ | |
68 | + for (page = 0; page < info->pages; page++) { | |
69 | + if (pmbus_check_word_register(client, page, PMBUS_READ_VOUT)) { | |
70 | + info->func[page] |= PMBUS_HAVE_VOUT; | |
71 | + if (pmbus_check_byte_register(client, page, | |
72 | + PMBUS_STATUS_VOUT)) | |
73 | + info->func[page] |= PMBUS_HAVE_STATUS_VOUT; | |
74 | + } | |
75 | + if (pmbus_check_word_register(client, page, PMBUS_READ_IOUT)) { | |
76 | + info->func[page] |= PMBUS_HAVE_IOUT; | |
77 | + if (pmbus_check_byte_register(client, 0, | |
78 | + PMBUS_STATUS_IOUT)) | |
79 | + info->func[page] |= PMBUS_HAVE_STATUS_IOUT; | |
80 | + } | |
81 | + if (pmbus_check_word_register(client, page, PMBUS_READ_POUT)) | |
82 | + info->func[page] |= PMBUS_HAVE_POUT; | |
83 | + } | |
84 | +} | |
85 | + | |
86 | +/* | |
87 | + * Identify chip parameters. | |
88 | + */ | |
89 | +static int pmbus_identify(struct i2c_client *client, | |
90 | + struct pmbus_driver_info *info) | |
91 | +{ | |
92 | + if (!info->pages) { | |
93 | + /* | |
94 | + * Check if the PAGE command is supported. If it is, | |
95 | + * keep setting the page number until it fails or until the | |
96 | + * maximum number of pages has been reached. Assume that | |
97 | + * this is the number of pages supported by the chip. | |
98 | + */ | |
99 | + if (pmbus_check_byte_register(client, 0, PMBUS_PAGE)) { | |
100 | + int page; | |
101 | + | |
102 | + for (page = 1; page < PMBUS_PAGES; page++) { | |
103 | + if (pmbus_set_page(client, page) < 0) | |
104 | + break; | |
105 | + } | |
106 | + pmbus_set_page(client, 0); | |
107 | + info->pages = page; | |
108 | + } else { | |
109 | + info->pages = 1; | |
110 | + } | |
111 | + } | |
112 | + | |
113 | + /* | |
114 | + * We should check if the COEFFICIENTS register is supported. | |
115 | + * If it is, and the chip is configured for direct mode, we can read | |
116 | + * the coefficients from the chip, one set per group of sensor | |
117 | + * registers. | |
118 | + * | |
119 | + * To do this, we will need access to a chip which actually supports the | |
120 | + * COEFFICIENTS command, since the command is too complex to implement | |
121 | + * without testing it. | |
122 | + */ | |
123 | + | |
124 | + /* Try to find sensor groups */ | |
125 | + pmbus_find_sensor_groups(client, info); | |
126 | + | |
127 | + return 0; | |
128 | +} | |
129 | + | |
130 | +static int pmbus_probe(struct i2c_client *client, | |
131 | + const struct i2c_device_id *id) | |
132 | +{ | |
133 | + struct pmbus_driver_info *info; | |
134 | + int ret; | |
135 | + | |
136 | + info = kzalloc(sizeof(struct pmbus_driver_info), GFP_KERNEL); | |
137 | + if (!info) | |
138 | + return -ENOMEM; | |
139 | + | |
140 | + info->pages = id->driver_data; | |
141 | + info->identify = pmbus_identify; | |
142 | + | |
143 | + ret = pmbus_do_probe(client, id, info); | |
144 | + if (ret < 0) | |
145 | + goto out; | |
146 | + return 0; | |
147 | + | |
148 | +out: | |
149 | + kfree(info); | |
150 | + return ret; | |
151 | +} | |
152 | + | |
153 | +static int pmbus_remove(struct i2c_client *client) | |
154 | +{ | |
155 | + int ret; | |
156 | + const struct pmbus_driver_info *info; | |
157 | + | |
158 | + info = pmbus_get_driver_info(client); | |
159 | + ret = pmbus_do_remove(client); | |
160 | + kfree(info); | |
161 | + return ret; | |
162 | +} | |
163 | + | |
164 | +/* | |
165 | + * Use driver_data to set the number of pages supported by the chip. | |
166 | + */ | |
167 | +static const struct i2c_device_id pmbus_id[] = { | |
168 | + {"bmr450", 1}, | |
169 | + {"bmr451", 1}, | |
170 | + {"bmr453", 1}, | |
171 | + {"bmr454", 1}, | |
172 | + {"ltc2978", 8}, | |
173 | + {"pmbus", 0}, | |
174 | + {} | |
175 | +}; | |
176 | + | |
177 | +MODULE_DEVICE_TABLE(i2c, pmbus_id); | |
178 | + | |
179 | +/* This is the driver that will be inserted */ | |
180 | +static struct i2c_driver pmbus_driver = { | |
181 | + .driver = { | |
182 | + .name = "pmbus", | |
183 | + }, | |
184 | + .probe = pmbus_probe, | |
185 | + .remove = pmbus_remove, | |
186 | + .id_table = pmbus_id, | |
187 | +}; | |
188 | + | |
189 | +static int __init pmbus_init(void) | |
190 | +{ | |
191 | + return i2c_add_driver(&pmbus_driver); | |
192 | +} | |
193 | + | |
194 | +static void __exit pmbus_exit(void) | |
195 | +{ | |
196 | + i2c_del_driver(&pmbus_driver); | |
197 | +} | |
198 | + | |
199 | +MODULE_AUTHOR("Guenter Roeck"); | |
200 | +MODULE_DESCRIPTION("Generic PMBus driver"); | |
201 | +MODULE_LICENSE("GPL"); | |
202 | +module_init(pmbus_init); | |
203 | +module_exit(pmbus_exit); |
drivers/hwmon/pmbus.h
1 | +/* | |
2 | + * pmbus.h - Common defines and structures for PMBus devices | |
3 | + * | |
4 | + * Copyright (c) 2010, 2011 Ericsson AB. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#ifndef PMBUS_H | |
22 | +#define PMBUS_H | |
23 | + | |
24 | +/* | |
25 | + * Registers | |
26 | + */ | |
27 | +#define PMBUS_PAGE 0x00 | |
28 | +#define PMBUS_OPERATION 0x01 | |
29 | +#define PMBUS_ON_OFF_CONFIG 0x02 | |
30 | +#define PMBUS_CLEAR_FAULTS 0x03 | |
31 | +#define PMBUS_PHASE 0x04 | |
32 | + | |
33 | +#define PMBUS_CAPABILITY 0x19 | |
34 | +#define PMBUS_QUERY 0x1A | |
35 | + | |
36 | +#define PMBUS_VOUT_MODE 0x20 | |
37 | +#define PMBUS_VOUT_COMMAND 0x21 | |
38 | +#define PMBUS_VOUT_TRIM 0x22 | |
39 | +#define PMBUS_VOUT_CAL_OFFSET 0x23 | |
40 | +#define PMBUS_VOUT_MAX 0x24 | |
41 | +#define PMBUS_VOUT_MARGIN_HIGH 0x25 | |
42 | +#define PMBUS_VOUT_MARGIN_LOW 0x26 | |
43 | +#define PMBUS_VOUT_TRANSITION_RATE 0x27 | |
44 | +#define PMBUS_VOUT_DROOP 0x28 | |
45 | +#define PMBUS_VOUT_SCALE_LOOP 0x29 | |
46 | +#define PMBUS_VOUT_SCALE_MONITOR 0x2A | |
47 | + | |
48 | +#define PMBUS_COEFFICIENTS 0x30 | |
49 | +#define PMBUS_POUT_MAX 0x31 | |
50 | + | |
51 | +#define PMBUS_FAN_CONFIG_12 0x3A | |
52 | +#define PMBUS_FAN_COMMAND_1 0x3B | |
53 | +#define PMBUS_FAN_COMMAND_2 0x3C | |
54 | +#define PMBUS_FAN_CONFIG_34 0x3D | |
55 | +#define PMBUS_FAN_COMMAND_3 0x3E | |
56 | +#define PMBUS_FAN_COMMAND_4 0x3F | |
57 | + | |
58 | +#define PMBUS_VOUT_OV_FAULT_LIMIT 0x40 | |
59 | +#define PMBUS_VOUT_OV_FAULT_RESPONSE 0x41 | |
60 | +#define PMBUS_VOUT_OV_WARN_LIMIT 0x42 | |
61 | +#define PMBUS_VOUT_UV_WARN_LIMIT 0x43 | |
62 | +#define PMBUS_VOUT_UV_FAULT_LIMIT 0x44 | |
63 | +#define PMBUS_VOUT_UV_FAULT_RESPONSE 0x45 | |
64 | +#define PMBUS_IOUT_OC_FAULT_LIMIT 0x46 | |
65 | +#define PMBUS_IOUT_OC_FAULT_RESPONSE 0x47 | |
66 | +#define PMBUS_IOUT_OC_LV_FAULT_LIMIT 0x48 | |
67 | +#define PMBUS_IOUT_OC_LV_FAULT_RESPONSE 0x49 | |
68 | +#define PMBUS_IOUT_OC_WARN_LIMIT 0x4A | |
69 | +#define PMBUS_IOUT_UC_FAULT_LIMIT 0x4B | |
70 | +#define PMBUS_IOUT_UC_FAULT_RESPONSE 0x4C | |
71 | + | |
72 | +#define PMBUS_OT_FAULT_LIMIT 0x4F | |
73 | +#define PMBUS_OT_FAULT_RESPONSE 0x50 | |
74 | +#define PMBUS_OT_WARN_LIMIT 0x51 | |
75 | +#define PMBUS_UT_WARN_LIMIT 0x52 | |
76 | +#define PMBUS_UT_FAULT_LIMIT 0x53 | |
77 | +#define PMBUS_UT_FAULT_RESPONSE 0x54 | |
78 | +#define PMBUS_VIN_OV_FAULT_LIMIT 0x55 | |
79 | +#define PMBUS_VIN_OV_FAULT_RESPONSE 0x56 | |
80 | +#define PMBUS_VIN_OV_WARN_LIMIT 0x57 | |
81 | +#define PMBUS_VIN_UV_WARN_LIMIT 0x58 | |
82 | +#define PMBUS_VIN_UV_FAULT_LIMIT 0x59 | |
83 | + | |
84 | +#define PMBUS_IIN_OC_FAULT_LIMIT 0x5B | |
85 | +#define PMBUS_IIN_OC_WARN_LIMIT 0x5D | |
86 | + | |
87 | +#define PMBUS_POUT_OP_FAULT_LIMIT 0x68 | |
88 | +#define PMBUS_POUT_OP_WARN_LIMIT 0x6A | |
89 | +#define PMBUS_PIN_OP_WARN_LIMIT 0x6B | |
90 | + | |
91 | +#define PMBUS_STATUS_BYTE 0x78 | |
92 | +#define PMBUS_STATUS_WORD 0x79 | |
93 | +#define PMBUS_STATUS_VOUT 0x7A | |
94 | +#define PMBUS_STATUS_IOUT 0x7B | |
95 | +#define PMBUS_STATUS_INPUT 0x7C | |
96 | +#define PMBUS_STATUS_TEMPERATURE 0x7D | |
97 | +#define PMBUS_STATUS_CML 0x7E | |
98 | +#define PMBUS_STATUS_OTHER 0x7F | |
99 | +#define PMBUS_STATUS_MFR_SPECIFIC 0x80 | |
100 | +#define PMBUS_STATUS_FAN_12 0x81 | |
101 | +#define PMBUS_STATUS_FAN_34 0x82 | |
102 | + | |
103 | +#define PMBUS_READ_VIN 0x88 | |
104 | +#define PMBUS_READ_IIN 0x89 | |
105 | +#define PMBUS_READ_VCAP 0x8A | |
106 | +#define PMBUS_READ_VOUT 0x8B | |
107 | +#define PMBUS_READ_IOUT 0x8C | |
108 | +#define PMBUS_READ_TEMPERATURE_1 0x8D | |
109 | +#define PMBUS_READ_TEMPERATURE_2 0x8E | |
110 | +#define PMBUS_READ_TEMPERATURE_3 0x8F | |
111 | +#define PMBUS_READ_FAN_SPEED_1 0x90 | |
112 | +#define PMBUS_READ_FAN_SPEED_2 0x91 | |
113 | +#define PMBUS_READ_FAN_SPEED_3 0x92 | |
114 | +#define PMBUS_READ_FAN_SPEED_4 0x93 | |
115 | +#define PMBUS_READ_DUTY_CYCLE 0x94 | |
116 | +#define PMBUS_READ_FREQUENCY 0x95 | |
117 | +#define PMBUS_READ_POUT 0x96 | |
118 | +#define PMBUS_READ_PIN 0x97 | |
119 | + | |
120 | +#define PMBUS_REVISION 0x98 | |
121 | +#define PMBUS_MFR_ID 0x99 | |
122 | +#define PMBUS_MFR_MODEL 0x9A | |
123 | +#define PMBUS_MFR_REVISION 0x9B | |
124 | +#define PMBUS_MFR_LOCATION 0x9C | |
125 | +#define PMBUS_MFR_DATE 0x9D | |
126 | +#define PMBUS_MFR_SERIAL 0x9E | |
127 | + | |
128 | +/* | |
129 | + * CAPABILITY | |
130 | + */ | |
131 | +#define PB_CAPABILITY_SMBALERT (1<<4) | |
132 | +#define PB_CAPABILITY_ERROR_CHECK (1<<7) | |
133 | + | |
134 | +/* | |
135 | + * VOUT_MODE | |
136 | + */ | |
137 | +#define PB_VOUT_MODE_MODE_MASK 0xe0 | |
138 | +#define PB_VOUT_MODE_PARAM_MASK 0x1f | |
139 | + | |
140 | +#define PB_VOUT_MODE_LINEAR 0x00 | |
141 | +#define PB_VOUT_MODE_VID 0x20 | |
142 | +#define PB_VOUT_MODE_DIRECT 0x40 | |
143 | + | |
144 | +/* | |
145 | + * Fan configuration | |
146 | + */ | |
147 | +#define PB_FAN_2_PULSE_MASK ((1 << 0) | (1 << 1)) | |
148 | +#define PB_FAN_2_RPM (1 << 2) | |
149 | +#define PB_FAN_2_INSTALLED (1 << 3) | |
150 | +#define PB_FAN_1_PULSE_MASK ((1 << 4) | (1 << 5)) | |
151 | +#define PB_FAN_1_RPM (1 << 6) | |
152 | +#define PB_FAN_1_INSTALLED (1 << 7) | |
153 | + | |
154 | +/* | |
155 | + * STATUS_BYTE, STATUS_WORD (lower) | |
156 | + */ | |
157 | +#define PB_STATUS_NONE_ABOVE (1<<0) | |
158 | +#define PB_STATUS_CML (1<<1) | |
159 | +#define PB_STATUS_TEMPERATURE (1<<2) | |
160 | +#define PB_STATUS_VIN_UV (1<<3) | |
161 | +#define PB_STATUS_IOUT_OC (1<<4) | |
162 | +#define PB_STATUS_VOUT_OV (1<<5) | |
163 | +#define PB_STATUS_OFF (1<<6) | |
164 | +#define PB_STATUS_BUSY (1<<7) | |
165 | + | |
166 | +/* | |
167 | + * STATUS_WORD (upper) | |
168 | + */ | |
169 | +#define PB_STATUS_UNKNOWN (1<<8) | |
170 | +#define PB_STATUS_OTHER (1<<9) | |
171 | +#define PB_STATUS_FANS (1<<10) | |
172 | +#define PB_STATUS_POWER_GOOD_N (1<<11) | |
173 | +#define PB_STATUS_WORD_MFR (1<<12) | |
174 | +#define PB_STATUS_INPUT (1<<13) | |
175 | +#define PB_STATUS_IOUT_POUT (1<<14) | |
176 | +#define PB_STATUS_VOUT (1<<15) | |
177 | + | |
178 | +/* | |
179 | + * STATUS_IOUT | |
180 | + */ | |
181 | +#define PB_POUT_OP_WARNING (1<<0) | |
182 | +#define PB_POUT_OP_FAULT (1<<1) | |
183 | +#define PB_POWER_LIMITING (1<<2) | |
184 | +#define PB_CURRENT_SHARE_FAULT (1<<3) | |
185 | +#define PB_IOUT_UC_FAULT (1<<4) | |
186 | +#define PB_IOUT_OC_WARNING (1<<5) | |
187 | +#define PB_IOUT_OC_LV_FAULT (1<<6) | |
188 | +#define PB_IOUT_OC_FAULT (1<<7) | |
189 | + | |
190 | +/* | |
191 | + * STATUS_VOUT, STATUS_INPUT | |
192 | + */ | |
193 | +#define PB_VOLTAGE_UV_FAULT (1<<4) | |
194 | +#define PB_VOLTAGE_UV_WARNING (1<<5) | |
195 | +#define PB_VOLTAGE_OV_WARNING (1<<6) | |
196 | +#define PB_VOLTAGE_OV_FAULT (1<<7) | |
197 | + | |
198 | +/* | |
199 | + * STATUS_INPUT | |
200 | + */ | |
201 | +#define PB_PIN_OP_WARNING (1<<0) | |
202 | +#define PB_IIN_OC_WARNING (1<<1) | |
203 | +#define PB_IIN_OC_FAULT (1<<2) | |
204 | + | |
205 | +/* | |
206 | + * STATUS_TEMPERATURE | |
207 | + */ | |
208 | +#define PB_TEMP_UT_FAULT (1<<4) | |
209 | +#define PB_TEMP_UT_WARNING (1<<5) | |
210 | +#define PB_TEMP_OT_WARNING (1<<6) | |
211 | +#define PB_TEMP_OT_FAULT (1<<7) | |
212 | + | |
213 | +/* | |
214 | + * STATUS_FAN | |
215 | + */ | |
216 | +#define PB_FAN_AIRFLOW_WARNING (1<<0) | |
217 | +#define PB_FAN_AIRFLOW_FAULT (1<<1) | |
218 | +#define PB_FAN_FAN2_SPEED_OVERRIDE (1<<2) | |
219 | +#define PB_FAN_FAN1_SPEED_OVERRIDE (1<<3) | |
220 | +#define PB_FAN_FAN2_WARNING (1<<4) | |
221 | +#define PB_FAN_FAN1_WARNING (1<<5) | |
222 | +#define PB_FAN_FAN2_FAULT (1<<6) | |
223 | +#define PB_FAN_FAN1_FAULT (1<<7) | |
224 | + | |
225 | +/* | |
226 | + * CML_FAULT_STATUS | |
227 | + */ | |
228 | +#define PB_CML_FAULT_OTHER_MEM_LOGIC (1<<0) | |
229 | +#define PB_CML_FAULT_OTHER_COMM (1<<1) | |
230 | +#define PB_CML_FAULT_PROCESSOR (1<<3) | |
231 | +#define PB_CML_FAULT_MEMORY (1<<4) | |
232 | +#define PB_CML_FAULT_PACKET_ERROR (1<<5) | |
233 | +#define PB_CML_FAULT_INVALID_DATA (1<<6) | |
234 | +#define PB_CML_FAULT_INVALID_COMMAND (1<<7) | |
235 | + | |
236 | +enum pmbus_sensor_classes { | |
237 | + PSC_VOLTAGE_IN = 0, | |
238 | + PSC_VOLTAGE_OUT, | |
239 | + PSC_CURRENT_IN, | |
240 | + PSC_CURRENT_OUT, | |
241 | + PSC_POWER, | |
242 | + PSC_TEMPERATURE, | |
243 | + PSC_FAN, | |
244 | + PSC_NUM_CLASSES /* Number of power sensor classes */ | |
245 | +}; | |
246 | + | |
247 | +#define PMBUS_PAGES 32 /* Per PMBus specification */ | |
248 | + | |
249 | +/* Functionality bit mask */ | |
250 | +#define PMBUS_HAVE_VIN (1 << 0) | |
251 | +#define PMBUS_HAVE_VCAP (1 << 1) | |
252 | +#define PMBUS_HAVE_VOUT (1 << 2) | |
253 | +#define PMBUS_HAVE_IIN (1 << 3) | |
254 | +#define PMBUS_HAVE_IOUT (1 << 4) | |
255 | +#define PMBUS_HAVE_PIN (1 << 5) | |
256 | +#define PMBUS_HAVE_POUT (1 << 6) | |
257 | +#define PMBUS_HAVE_FAN12 (1 << 7) | |
258 | +#define PMBUS_HAVE_FAN34 (1 << 8) | |
259 | +#define PMBUS_HAVE_TEMP (1 << 9) | |
260 | +#define PMBUS_HAVE_TEMP2 (1 << 10) | |
261 | +#define PMBUS_HAVE_TEMP3 (1 << 11) | |
262 | +#define PMBUS_HAVE_STATUS_VOUT (1 << 12) | |
263 | +#define PMBUS_HAVE_STATUS_IOUT (1 << 13) | |
264 | +#define PMBUS_HAVE_STATUS_INPUT (1 << 14) | |
265 | +#define PMBUS_HAVE_STATUS_TEMP (1 << 15) | |
266 | +#define PMBUS_HAVE_STATUS_FAN12 (1 << 16) | |
267 | +#define PMBUS_HAVE_STATUS_FAN34 (1 << 17) | |
268 | + | |
269 | +struct pmbus_driver_info { | |
270 | + int pages; /* Total number of pages */ | |
271 | + bool direct[PSC_NUM_CLASSES]; | |
272 | + /* true if device uses direct data format | |
273 | + for the given sensor class */ | |
274 | + /* | |
275 | + * Support one set of coefficients for each sensor type | |
276 | + * Used for chips providing data in direct mode. | |
277 | + */ | |
278 | + int m[PSC_NUM_CLASSES]; /* mantissa for direct data format */ | |
279 | + int b[PSC_NUM_CLASSES]; /* offset */ | |
280 | + int R[PSC_NUM_CLASSES]; /* exponent */ | |
281 | + | |
282 | + u32 func[PMBUS_PAGES]; /* Functionality, per page */ | |
283 | + /* | |
284 | + * The get_status function maps manufacturing specific status values | |
285 | + * into PMBus standard status values. | |
286 | + * This function is optional and only necessary if chip specific status | |
287 | + * register values have to be mapped into standard PMBus status register | |
288 | + * values. | |
289 | + */ | |
290 | + int (*get_status)(struct i2c_client *client, int page, int reg); | |
291 | + /* | |
292 | + * The identify function determines supported PMBus functionality. | |
293 | + * This function is only necessary if a chip driver supports multiple | |
294 | + * chips, and the chip functionality is not pre-determined. | |
295 | + */ | |
296 | + int (*identify)(struct i2c_client *client, | |
297 | + struct pmbus_driver_info *info); | |
298 | +}; | |
299 | + | |
300 | +/* Function declarations */ | |
301 | + | |
302 | +int pmbus_set_page(struct i2c_client *client, u8 page); | |
303 | +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); | |
304 | +void pmbus_clear_faults(struct i2c_client *client); | |
305 | +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); | |
306 | +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); | |
307 | +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, | |
308 | + struct pmbus_driver_info *info); | |
309 | +int pmbus_do_remove(struct i2c_client *client); | |
310 | +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client | |
311 | + *client); | |
312 | + | |
313 | +#endif /* PMBUS_H */ |
drivers/hwmon/pmbus_core.c
Changes suppressed. Click to show
1 | +/* | |
2 | + * Hardware monitoring driver for PMBus devices | |
3 | + * | |
4 | + * Copyright (c) 2010, 2011 Ericsson AB. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#include <linux/kernel.h> | |
22 | +#include <linux/module.h> | |
23 | +#include <linux/init.h> | |
24 | +#include <linux/err.h> | |
25 | +#include <linux/slab.h> | |
26 | +#include <linux/i2c.h> | |
27 | +#include <linux/hwmon.h> | |
28 | +#include <linux/hwmon-sysfs.h> | |
29 | +#include <linux/delay.h> | |
30 | +#include <linux/i2c/pmbus.h> | |
31 | +#include "pmbus.h" | |
32 | + | |
33 | +/* | |
34 | + * Constants needed to determine number of sensors, booleans, and labels. | |
35 | + */ | |
36 | +#define PMBUS_MAX_INPUT_SENSORS 11 /* 6*volt, 3*curr, 2*power */ | |
37 | +#define PMBUS_VOUT_SENSORS_PER_PAGE 5 /* input, min, max, lcrit, | |
38 | + crit */ | |
39 | +#define PMBUS_IOUT_SENSORS_PER_PAGE 4 /* input, min, max, crit */ | |
40 | +#define PMBUS_POUT_SENSORS_PER_PAGE 4 /* input, cap, max, crit */ | |
41 | +#define PMBUS_MAX_SENSORS_PER_FAN 1 /* input */ | |
42 | +#define PMBUS_MAX_SENSORS_PER_TEMP 5 /* input, min, max, lcrit, | |
43 | + crit */ | |
44 | + | |
45 | +#define PMBUS_MAX_INPUT_BOOLEANS 7 /* v: min_alarm, max_alarm, | |
46 | + lcrit_alarm, crit_alarm; | |
47 | + c: alarm, crit_alarm; | |
48 | + p: crit_alarm */ | |
49 | +#define PMBUS_VOUT_BOOLEANS_PER_PAGE 4 /* min_alarm, max_alarm, | |
50 | + lcrit_alarm, crit_alarm */ | |
51 | +#define PMBUS_IOUT_BOOLEANS_PER_PAGE 3 /* alarm, lcrit_alarm, | |
52 | + crit_alarm */ | |
53 | +#define PMBUS_POUT_BOOLEANS_PER_PAGE 2 /* alarm, crit_alarm */ | |
54 | +#define PMBUS_MAX_BOOLEANS_PER_FAN 2 /* alarm, fault */ | |
55 | +#define PMBUS_MAX_BOOLEANS_PER_TEMP 4 /* min_alarm, max_alarm, | |
56 | + lcrit_alarm, crit_alarm */ | |
57 | + | |
58 | +#define PMBUS_MAX_INPUT_LABELS 4 /* vin, vcap, iin, pin */ | |
59 | + | |
60 | +/* | |
61 | + * status, status_vout, status_iout, status_fans, and status_temp | |
62 | + * are paged. status_input and status_fan34 are unpaged. | |
63 | + * status_fan34 is a special case to handle a second set of fans | |
64 | + * on page 0. | |
65 | + */ | |
66 | +#define PB_NUM_STATUS_REG (PMBUS_PAGES * 5 + 2) | |
67 | + | |
68 | +/* | |
69 | + * Index into status register array, per status register group | |
70 | + */ | |
71 | +#define PB_STATUS_BASE 0 | |
72 | +#define PB_STATUS_VOUT_BASE (PB_STATUS_BASE + PMBUS_PAGES) | |
73 | +#define PB_STATUS_IOUT_BASE (PB_STATUS_VOUT_BASE + PMBUS_PAGES) | |
74 | +#define PB_STATUS_FAN_BASE (PB_STATUS_IOUT_BASE + PMBUS_PAGES) | |
75 | +#define PB_STATUS_FAN34_BASE (PB_STATUS_FAN_BASE + PMBUS_PAGES) | |
76 | +#define PB_STATUS_INPUT_BASE (PB_STATUS_FAN34_BASE + 1) | |
77 | +#define PB_STATUS_TEMP_BASE (PB_STATUS_INPUT_BASE + 1) | |
78 | + | |
79 | +struct pmbus_sensor { | |
80 | + char name[I2C_NAME_SIZE]; /* sysfs sensor name */ | |
81 | + struct sensor_device_attribute attribute; | |
82 | + u8 page; /* page number */ | |
83 | + u8 reg; /* register */ | |
84 | + enum pmbus_sensor_classes class; /* sensor class */ | |
85 | + bool update; /* runtime sensor update needed */ | |
86 | + int data; /* Sensor data. | |
87 | + Negative if there was a read error */ | |
88 | +}; | |
89 | + | |
90 | +struct pmbus_boolean { | |
91 | + char name[I2C_NAME_SIZE]; /* sysfs boolean name */ | |
92 | + struct sensor_device_attribute attribute; | |
93 | +}; | |
94 | + | |
95 | +struct pmbus_label { | |
96 | + char name[I2C_NAME_SIZE]; /* sysfs label name */ | |
97 | + struct sensor_device_attribute attribute; | |
98 | + char label[I2C_NAME_SIZE]; /* label */ | |
99 | +}; | |
100 | + | |
101 | +struct pmbus_data { | |
102 | + struct device *hwmon_dev; | |
103 | + | |
104 | + u32 flags; /* from platform data */ | |
105 | + | |
106 | + int exponent; /* linear mode: exponent for output voltages */ | |
107 | + | |
108 | + const struct pmbus_driver_info *info; | |
109 | + | |
110 | + int max_attributes; | |
111 | + int num_attributes; | |
112 | + struct attribute **attributes; | |
113 | + struct attribute_group group; | |
114 | + | |
115 | + /* | |
116 | + * Sensors cover both sensor and limit registers. | |
117 | + */ | |
118 | + int max_sensors; | |
119 | + int num_sensors; | |
120 | + struct pmbus_sensor *sensors; | |
121 | + /* | |
122 | + * Booleans are used for alarms. | |
123 | + * Values are determined from status registers. | |
124 | + */ | |
125 | + int max_booleans; | |
126 | + int num_booleans; | |
127 | + struct pmbus_boolean *booleans; | |
128 | + /* | |
129 | + * Labels are used to map generic names (e.g., "in1") | |
130 | + * to PMBus specific names (e.g., "vin" or "vout1"). | |
131 | + */ | |
132 | + int max_labels; | |
133 | + int num_labels; | |
134 | + struct pmbus_label *labels; | |
135 | + | |
136 | + struct mutex update_lock; | |
137 | + bool valid; | |
138 | + unsigned long last_updated; /* in jiffies */ | |
139 | + | |
140 | + /* | |
141 | + * A single status register covers multiple attributes, | |
142 | + * so we keep them all together. | |
143 | + */ | |
144 | + u8 status_bits; | |
145 | + u8 status[PB_NUM_STATUS_REG]; | |
146 | + | |
147 | + u8 currpage; | |
148 | +}; | |
149 | + | |
150 | +int pmbus_set_page(struct i2c_client *client, u8 page) | |
151 | +{ | |
152 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
153 | + int rv = 0; | |
154 | + int newpage; | |
155 | + | |
156 | + if (page != data->currpage) { | |
157 | + rv = i2c_smbus_write_byte_data(client, PMBUS_PAGE, page); | |
158 | + newpage = i2c_smbus_read_byte_data(client, PMBUS_PAGE); | |
159 | + if (newpage != page) | |
160 | + rv = -EINVAL; | |
161 | + else | |
162 | + data->currpage = page; | |
163 | + } | |
164 | + return rv; | |
165 | +} | |
166 | +EXPORT_SYMBOL_GPL(pmbus_set_page); | |
167 | + | |
168 | +static int pmbus_write_byte(struct i2c_client *client, u8 page, u8 value) | |
169 | +{ | |
170 | + int rv; | |
171 | + | |
172 | + rv = pmbus_set_page(client, page); | |
173 | + if (rv < 0) | |
174 | + return rv; | |
175 | + | |
176 | + return i2c_smbus_write_byte(client, value); | |
177 | +} | |
178 | + | |
179 | +static int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, | |
180 | + u16 word) | |
181 | +{ | |
182 | + int rv; | |
183 | + | |
184 | + rv = pmbus_set_page(client, page); | |
185 | + if (rv < 0) | |
186 | + return rv; | |
187 | + | |
188 | + return i2c_smbus_write_word_data(client, reg, word); | |
189 | +} | |
190 | + | |
191 | +int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg) | |
192 | +{ | |
193 | + int rv; | |
194 | + | |
195 | + rv = pmbus_set_page(client, page); | |
196 | + if (rv < 0) | |
197 | + return rv; | |
198 | + | |
199 | + return i2c_smbus_read_word_data(client, reg); | |
200 | +} | |
201 | +EXPORT_SYMBOL_GPL(pmbus_read_word_data); | |
202 | + | |
203 | +static int pmbus_read_byte_data(struct i2c_client *client, u8 page, u8 reg) | |
204 | +{ | |
205 | + int rv; | |
206 | + | |
207 | + rv = pmbus_set_page(client, page); | |
208 | + if (rv < 0) | |
209 | + return rv; | |
210 | + | |
211 | + return i2c_smbus_read_byte_data(client, reg); | |
212 | +} | |
213 | + | |
214 | +static void pmbus_clear_fault_page(struct i2c_client *client, int page) | |
215 | +{ | |
216 | + pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); | |
217 | +} | |
218 | + | |
219 | +void pmbus_clear_faults(struct i2c_client *client) | |
220 | +{ | |
221 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
222 | + int i; | |
223 | + | |
224 | + for (i = 0; i < data->info->pages; i++) | |
225 | + pmbus_clear_fault_page(client, i); | |
226 | +} | |
227 | +EXPORT_SYMBOL_GPL(pmbus_clear_faults); | |
228 | + | |
229 | +static int pmbus_check_status_cml(struct i2c_client *client, int page) | |
230 | +{ | |
231 | + int status, status2; | |
232 | + | |
233 | + status = pmbus_read_byte_data(client, page, PMBUS_STATUS_BYTE); | |
234 | + if (status < 0 || (status & PB_STATUS_CML)) { | |
235 | + status2 = pmbus_read_byte_data(client, page, PMBUS_STATUS_CML); | |
236 | + if (status2 < 0 || (status2 & PB_CML_FAULT_INVALID_COMMAND)) | |
237 | + return -EINVAL; | |
238 | + } | |
239 | + return 0; | |
240 | +} | |
241 | + | |
242 | +bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg) | |
243 | +{ | |
244 | + int rv; | |
245 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
246 | + | |
247 | + rv = pmbus_read_byte_data(client, page, reg); | |
248 | + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) | |
249 | + rv = pmbus_check_status_cml(client, page); | |
250 | + pmbus_clear_fault_page(client, page); | |
251 | + return rv >= 0; | |
252 | +} | |
253 | +EXPORT_SYMBOL_GPL(pmbus_check_byte_register); | |
254 | + | |
255 | +bool pmbus_check_word_register(struct i2c_client *client, int page, int reg) | |
256 | +{ | |
257 | + int rv; | |
258 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
259 | + | |
260 | + rv = pmbus_read_word_data(client, page, reg); | |
261 | + if (rv >= 0 && !(data->flags & PMBUS_SKIP_STATUS_CHECK)) | |
262 | + rv = pmbus_check_status_cml(client, page); | |
263 | + pmbus_clear_fault_page(client, page); | |
264 | + return rv >= 0; | |
265 | +} | |
266 | +EXPORT_SYMBOL_GPL(pmbus_check_word_register); | |
267 | + | |
268 | +const struct pmbus_driver_info *pmbus_get_driver_info(struct i2c_client *client) | |
269 | +{ | |
270 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
271 | + | |
272 | + return data->info; | |
273 | +} | |
274 | +EXPORT_SYMBOL_GPL(pmbus_get_driver_info); | |
275 | + | |
276 | +static int pmbus_get_status(struct i2c_client *client, int page, int reg) | |
277 | +{ | |
278 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
279 | + const struct pmbus_driver_info *info = data->info; | |
280 | + int status; | |
281 | + | |
282 | + if (info->get_status) { | |
283 | + status = info->get_status(client, page, reg); | |
284 | + if (status != -ENODATA) | |
285 | + return status; | |
286 | + } | |
287 | + return pmbus_read_byte_data(client, page, reg); | |
288 | +} | |
289 | + | |
290 | +static struct pmbus_data *pmbus_update_device(struct device *dev) | |
291 | +{ | |
292 | + struct i2c_client *client = to_i2c_client(dev); | |
293 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
294 | + const struct pmbus_driver_info *info = data->info; | |
295 | + | |
296 | + mutex_lock(&data->update_lock); | |
297 | + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { | |
298 | + int i; | |
299 | + | |
300 | + for (i = 0; i < info->pages; i++) | |
301 | + data->status[PB_STATUS_BASE + i] | |
302 | + = pmbus_read_byte_data(client, i, | |
303 | + PMBUS_STATUS_BYTE); | |
304 | + for (i = 0; i < info->pages; i++) { | |
305 | + if (!(info->func[i] & PMBUS_HAVE_STATUS_VOUT)) | |
306 | + continue; | |
307 | + data->status[PB_STATUS_VOUT_BASE + i] | |
308 | + = pmbus_get_status(client, i, PMBUS_STATUS_VOUT); | |
309 | + } | |
310 | + for (i = 0; i < info->pages; i++) { | |
311 | + if (!(info->func[i] & PMBUS_HAVE_STATUS_IOUT)) | |
312 | + continue; | |
313 | + data->status[PB_STATUS_IOUT_BASE + i] | |
314 | + = pmbus_get_status(client, i, PMBUS_STATUS_IOUT); | |
315 | + } | |
316 | + for (i = 0; i < info->pages; i++) { | |
317 | + if (!(info->func[i] & PMBUS_HAVE_STATUS_TEMP)) | |
318 | + continue; | |
319 | + data->status[PB_STATUS_TEMP_BASE + i] | |
320 | + = pmbus_get_status(client, i, | |
321 | + PMBUS_STATUS_TEMPERATURE); | |
322 | + } | |
323 | + for (i = 0; i < info->pages; i++) { | |
324 | + if (!(info->func[i] & PMBUS_HAVE_STATUS_FAN12)) | |
325 | + continue; | |
326 | + data->status[PB_STATUS_FAN_BASE + i] | |
327 | + = pmbus_get_status(client, i, PMBUS_STATUS_FAN_12); | |
328 | + } | |
329 | + | |
330 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) | |
331 | + data->status[PB_STATUS_INPUT_BASE] | |
332 | + = pmbus_get_status(client, 0, PMBUS_STATUS_INPUT); | |
333 | + | |
334 | + if (info->func[0] & PMBUS_HAVE_STATUS_FAN34) | |
335 | + data->status[PB_STATUS_FAN34_BASE] | |
336 | + = pmbus_get_status(client, 0, PMBUS_STATUS_FAN_34); | |
337 | + | |
338 | + for (i = 0; i < data->num_sensors; i++) { | |
339 | + struct pmbus_sensor *sensor = &data->sensors[i]; | |
340 | + | |
341 | + if (!data->valid || sensor->update) | |
342 | + sensor->data | |
343 | + = pmbus_read_word_data(client, sensor->page, | |
344 | + sensor->reg); | |
345 | + } | |
346 | + pmbus_clear_faults(client); | |
347 | + data->last_updated = jiffies; | |
348 | + data->valid = 1; | |
349 | + } | |
350 | + mutex_unlock(&data->update_lock); | |
351 | + return data; | |
352 | +} | |
353 | + | |
354 | +/* | |
355 | + * Convert linear sensor values to milli- or micro-units | |
356 | + * depending on sensor type. | |
357 | + */ | |
358 | +static int pmbus_reg2data_linear(struct pmbus_data *data, | |
359 | + struct pmbus_sensor *sensor) | |
360 | +{ | |
361 | + s16 exponent, mantissa; | |
362 | + long val; | |
363 | + | |
364 | + if (sensor->class == PSC_VOLTAGE_OUT) { | |
365 | + exponent = data->exponent; | |
366 | + mantissa = (s16) sensor->data; | |
367 | + } else { | |
368 | + exponent = (sensor->data >> 11) & 0x001f; | |
369 | + mantissa = sensor->data & 0x07ff; | |
370 | + | |
371 | + if (exponent > 0x0f) | |
372 | + exponent |= 0xffe0; /* sign extend exponent */ | |
373 | + if (mantissa > 0x03ff) | |
374 | + mantissa |= 0xf800; /* sign extend mantissa */ | |
375 | + } | |
376 | + | |
377 | + val = mantissa; | |
378 | + | |
379 | + /* scale result to milli-units for all sensors except fans */ | |
380 | + if (sensor->class != PSC_FAN) | |
381 | + val = val * 1000L; | |
382 | + | |
383 | + /* scale result to micro-units for power sensors */ | |
384 | + if (sensor->class == PSC_POWER) | |
385 | + val = val * 1000L; | |
386 | + | |
387 | + if (exponent >= 0) | |
388 | + val <<= exponent; | |
389 | + else | |
390 | + val >>= -exponent; | |
391 | + | |
392 | + return (int)val; | |
393 | +} | |
394 | + | |
395 | +/* | |
396 | + * Convert direct sensor values to milli- or micro-units | |
397 | + * depending on sensor type. | |
398 | + */ | |
399 | +static int pmbus_reg2data_direct(struct pmbus_data *data, | |
400 | + struct pmbus_sensor *sensor) | |
401 | +{ | |
402 | + long val = (s16) sensor->data; | |
403 | + long m, b, R; | |
404 | + | |
405 | + m = data->info->m[sensor->class]; | |
406 | + b = data->info->b[sensor->class]; | |
407 | + R = data->info->R[sensor->class]; | |
408 | + | |
409 | + if (m == 0) | |
410 | + return 0; | |
411 | + | |
412 | + /* X = 1/m * (Y * 10^-R - b) */ | |
413 | + R = -R; | |
414 | + /* scale result to milli-units for everything but fans */ | |
415 | + if (sensor->class != PSC_FAN) { | |
416 | + R += 3; | |
417 | + b *= 1000; | |
418 | + } | |
419 | + | |
420 | + /* scale result to micro-units for power sensors */ | |
421 | + if (sensor->class == PSC_POWER) { | |
422 | + R += 3; | |
423 | + b *= 1000; | |
424 | + } | |
425 | + | |
426 | + while (R > 0) { | |
427 | + val *= 10; | |
428 | + R--; | |
429 | + } | |
430 | + while (R < 0) { | |
431 | + val = DIV_ROUND_CLOSEST(val, 10); | |
432 | + R++; | |
433 | + } | |
434 | + | |
435 | + return (int)((val - b) / m); | |
436 | +} | |
437 | + | |
438 | +static int pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) | |
439 | +{ | |
440 | + int val; | |
441 | + | |
442 | + if (data->info->direct[sensor->class]) | |
443 | + val = pmbus_reg2data_direct(data, sensor); | |
444 | + else | |
445 | + val = pmbus_reg2data_linear(data, sensor); | |
446 | + | |
447 | + return val; | |
448 | +} | |
449 | + | |
450 | +#define MAX_MANTISSA (1023 * 1000) | |
451 | +#define MIN_MANTISSA (511 * 1000) | |
452 | + | |
453 | +static u16 pmbus_data2reg_linear(struct pmbus_data *data, | |
454 | + enum pmbus_sensor_classes class, long val) | |
455 | +{ | |
456 | + s16 exponent = 0, mantissa = 0; | |
457 | + bool negative = false; | |
458 | + | |
459 | + /* simple case */ | |
460 | + if (val == 0) | |
461 | + return 0; | |
462 | + | |
463 | + if (val < 0) { | |
464 | + negative = true; | |
465 | + val = -val; | |
466 | + } | |
467 | + | |
468 | + if (class == PSC_VOLTAGE_OUT) { | |
469 | + /* | |
470 | + * For a static exponents, we don't have a choice | |
471 | + * but to adjust the value to it. | |
472 | + */ | |
473 | + if (data->exponent < 0) | |
474 | + val <<= -data->exponent; | |
475 | + else | |
476 | + val >>= data->exponent; | |
477 | + val = DIV_ROUND_CLOSEST(val, 1000); | |
478 | + if (val > 0x7fff) | |
479 | + val = 0x7fff; | |
480 | + return negative ? -val : val; | |
481 | + } | |
482 | + | |
483 | + /* Power is in uW. Convert to mW before converting. */ | |
484 | + if (class == PSC_POWER) | |
485 | + val = DIV_ROUND_CLOSEST(val, 1000L); | |
486 | + | |
487 | + /* | |
488 | + * For simplicity, convert fan data to milli-units | |
489 | + * before calculating the exponent. | |
490 | + */ | |
491 | + if (class == PSC_FAN) | |
492 | + val = val * 1000; | |
493 | + | |
494 | + /* Reduce large mantissa until it fits into 10 bit */ | |
495 | + while (val >= MAX_MANTISSA && exponent < 15) { | |
496 | + exponent++; | |
497 | + val >>= 1; | |
498 | + } | |
499 | + /* Increase small mantissa to improve precision */ | |
500 | + while (val < MIN_MANTISSA && exponent > -15) { | |
501 | + exponent--; | |
502 | + val <<= 1; | |
503 | + } | |
504 | + | |
505 | + /* Convert mantissa from milli-units to units */ | |
506 | + mantissa = DIV_ROUND_CLOSEST(val, 1000); | |
507 | + | |
508 | + /* Ensure that resulting number is within range */ | |
509 | + if (mantissa > 0x3ff) | |
510 | + mantissa = 0x3ff; | |
511 | + | |
512 | + /* restore sign */ | |
513 | + if (negative) | |
514 | + mantissa = -mantissa; | |
515 | + | |
516 | + /* Convert to 5 bit exponent, 11 bit mantissa */ | |
517 | + return (mantissa & 0x7ff) | ((exponent << 11) & 0xf800); | |
518 | +} | |
519 | + | |
520 | +static u16 pmbus_data2reg_direct(struct pmbus_data *data, | |
521 | + enum pmbus_sensor_classes class, long val) | |
522 | +{ | |
523 | + long m, b, R; | |
524 | + | |
525 | + m = data->info->m[class]; | |
526 | + b = data->info->b[class]; | |
527 | + R = data->info->R[class]; | |
528 | + | |
529 | + /* Power is in uW. Adjust R and b. */ | |
530 | + if (class == PSC_POWER) { | |
531 | + R -= 3; | |
532 | + b *= 1000; | |
533 | + } | |
534 | + | |
535 | + /* Calculate Y = (m * X + b) * 10^R */ | |
536 | + if (class != PSC_FAN) { | |
537 | + R -= 3; /* Adjust R and b for data in milli-units */ | |
538 | + b *= 1000; | |
539 | + } | |
540 | + val = val * m + b; | |
541 | + | |
542 | + while (R > 0) { | |
543 | + val *= 10; | |
544 | + R--; | |
545 | + } | |
546 | + while (R < 0) { | |
547 | + val = DIV_ROUND_CLOSEST(val, 10); | |
548 | + R++; | |
549 | + } | |
550 | + | |
551 | + return val; | |
552 | +} | |
553 | + | |
554 | +static u16 pmbus_data2reg(struct pmbus_data *data, | |
555 | + enum pmbus_sensor_classes class, long val) | |
556 | +{ | |
557 | + u16 regval; | |
558 | + | |
559 | + if (data->info->direct[class]) | |
560 | + regval = pmbus_data2reg_direct(data, class, val); | |
561 | + else | |
562 | + regval = pmbus_data2reg_linear(data, class, val); | |
563 | + | |
564 | + return regval; | |
565 | +} | |
566 | + | |
567 | +/* | |
568 | + * Return boolean calculated from converted data. | |
569 | + * <index> defines a status register index and mask, and optionally | |
570 | + * two sensor indexes. | |
571 | + * The upper half-word references the two sensors, | |
572 | + * two sensor indices. | |
573 | + * The upper half-word references the two optional sensors, | |
574 | + * the lower half word references status register and mask. | |
575 | + * The function returns true if (status[reg] & mask) is true and, | |
576 | + * if specified, if v1 >= v2. | |
577 | + * To determine if an object exceeds upper limits, specify <v, limit>. | |
578 | + * To determine if an object exceeds lower limits, specify <limit, v>. | |
579 | + * | |
580 | + * For booleans created with pmbus_add_boolean_reg(), only the lower 16 bits of | |
581 | + * index are set. s1 and s2 (the sensor index values) are zero in this case. | |
582 | + * The function returns true if (status[reg] & mask) is true. | |
583 | + * | |
584 | + * If the boolean was created with pmbus_add_boolean_cmp(), a comparison against | |
585 | + * a specified limit has to be performed to determine the boolean result. | |
586 | + * In this case, the function returns true if v1 >= v2 (where v1 and v2 are | |
587 | + * sensor values referenced by sensor indices s1 and s2). | |
588 | + * | |
589 | + * To determine if an object exceeds upper limits, specify <s1,s2> = <v,limit>. | |
590 | + * To determine if an object exceeds lower limits, specify <s1,s2> = <limit,v>. | |
591 | + * | |
592 | + * If a negative value is stored in any of the referenced registers, this value | |
593 | + * reflects an error code which will be returned. | |
594 | + */ | |
595 | +static int pmbus_get_boolean(struct pmbus_data *data, int index, int *val) | |
596 | +{ | |
597 | + u8 s1 = (index >> 24) & 0xff; | |
598 | + u8 s2 = (index >> 16) & 0xff; | |
599 | + u8 reg = (index >> 8) & 0xff; | |
600 | + u8 mask = index & 0xff; | |
601 | + int status; | |
602 | + u8 regval; | |
603 | + | |
604 | + status = data->status[reg]; | |
605 | + if (status < 0) | |
606 | + return status; | |
607 | + | |
608 | + regval = status & mask; | |
609 | + if (!s1 && !s2) | |
610 | + *val = !!regval; | |
611 | + else { | |
612 | + int v1, v2; | |
613 | + struct pmbus_sensor *sensor1, *sensor2; | |
614 | + | |
615 | + sensor1 = &data->sensors[s1]; | |
616 | + if (sensor1->data < 0) | |
617 | + return sensor1->data; | |
618 | + sensor2 = &data->sensors[s2]; | |
619 | + if (sensor2->data < 0) | |
620 | + return sensor2->data; | |
621 | + | |
622 | + v1 = pmbus_reg2data(data, sensor1); | |
623 | + v2 = pmbus_reg2data(data, sensor2); | |
624 | + *val = !!(regval && v1 >= v2); | |
625 | + } | |
626 | + return 0; | |
627 | +} | |
628 | + | |
629 | +static ssize_t pmbus_show_boolean(struct device *dev, | |
630 | + struct device_attribute *da, char *buf) | |
631 | +{ | |
632 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
633 | + struct pmbus_data *data = pmbus_update_device(dev); | |
634 | + int val; | |
635 | + int err; | |
636 | + | |
637 | + err = pmbus_get_boolean(data, attr->index, &val); | |
638 | + if (err) | |
639 | + return err; | |
640 | + return snprintf(buf, PAGE_SIZE, "%d\n", val); | |
641 | +} | |
642 | + | |
643 | +static ssize_t pmbus_show_sensor(struct device *dev, | |
644 | + struct device_attribute *da, char *buf) | |
645 | +{ | |
646 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
647 | + struct pmbus_data *data = pmbus_update_device(dev); | |
648 | + struct pmbus_sensor *sensor; | |
649 | + | |
650 | + sensor = &data->sensors[attr->index]; | |
651 | + if (sensor->data < 0) | |
652 | + return sensor->data; | |
653 | + | |
654 | + return snprintf(buf, PAGE_SIZE, "%d\n", pmbus_reg2data(data, sensor)); | |
655 | +} | |
656 | + | |
657 | +static ssize_t pmbus_set_sensor(struct device *dev, | |
658 | + struct device_attribute *devattr, | |
659 | + const char *buf, size_t count) | |
660 | +{ | |
661 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | |
662 | + struct i2c_client *client = to_i2c_client(dev); | |
663 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
664 | + struct pmbus_sensor *sensor = &data->sensors[attr->index]; | |
665 | + ssize_t rv = count; | |
666 | + long val = 0; | |
667 | + int ret; | |
668 | + u16 regval; | |
669 | + | |
670 | + if (strict_strtol(buf, 10, &val) < 0) | |
671 | + return -EINVAL; | |
672 | + | |
673 | + mutex_lock(&data->update_lock); | |
674 | + regval = pmbus_data2reg(data, sensor->class, val); | |
675 | + ret = pmbus_write_word_data(client, sensor->page, sensor->reg, regval); | |
676 | + if (ret < 0) | |
677 | + rv = ret; | |
678 | + else | |
679 | + data->sensors[attr->index].data = regval; | |
680 | + mutex_unlock(&data->update_lock); | |
681 | + return rv; | |
682 | +} | |
683 | + | |
684 | +static ssize_t pmbus_show_label(struct device *dev, | |
685 | + struct device_attribute *da, char *buf) | |
686 | +{ | |
687 | + struct i2c_client *client = to_i2c_client(dev); | |
688 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
689 | + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); | |
690 | + | |
691 | + return snprintf(buf, PAGE_SIZE, "%s\n", | |
692 | + data->labels[attr->index].label); | |
693 | +} | |
694 | + | |
695 | +#define PMBUS_ADD_ATTR(data, _name, _idx, _mode, _type, _show, _set) \ | |
696 | +do { \ | |
697 | + struct sensor_device_attribute *a \ | |
698 | + = &data->_type##s[data->num_##_type##s].attribute; \ | |
699 | + BUG_ON(data->num_attributes >= data->max_attributes); \ | |
700 | + a->dev_attr.attr.name = _name; \ | |
701 | + a->dev_attr.attr.mode = _mode; \ | |
702 | + a->dev_attr.show = _show; \ | |
703 | + a->dev_attr.store = _set; \ | |
704 | + a->index = _idx; \ | |
705 | + data->attributes[data->num_attributes] = &a->dev_attr.attr; \ | |
706 | + data->num_attributes++; \ | |
707 | +} while (0) | |
708 | + | |
709 | +#define PMBUS_ADD_GET_ATTR(data, _name, _type, _idx) \ | |
710 | + PMBUS_ADD_ATTR(data, _name, _idx, S_IRUGO, _type, \ | |
711 | + pmbus_show_##_type, NULL) | |
712 | + | |
713 | +#define PMBUS_ADD_SET_ATTR(data, _name, _type, _idx) \ | |
714 | + PMBUS_ADD_ATTR(data, _name, _idx, S_IWUSR | S_IRUGO, _type, \ | |
715 | + pmbus_show_##_type, pmbus_set_##_type) | |
716 | + | |
717 | +static void pmbus_add_boolean(struct pmbus_data *data, | |
718 | + const char *name, const char *type, int seq, | |
719 | + int idx) | |
720 | +{ | |
721 | + struct pmbus_boolean *boolean; | |
722 | + | |
723 | + BUG_ON(data->num_booleans >= data->max_booleans); | |
724 | + | |
725 | + boolean = &data->booleans[data->num_booleans]; | |
726 | + | |
727 | + snprintf(boolean->name, sizeof(boolean->name), "%s%d_%s", | |
728 | + name, seq, type); | |
729 | + PMBUS_ADD_GET_ATTR(data, boolean->name, boolean, idx); | |
730 | + data->num_booleans++; | |
731 | +} | |
732 | + | |
733 | +static void pmbus_add_boolean_reg(struct pmbus_data *data, | |
734 | + const char *name, const char *type, | |
735 | + int seq, int reg, int bit) | |
736 | +{ | |
737 | + pmbus_add_boolean(data, name, type, seq, (reg << 8) | bit); | |
738 | +} | |
739 | + | |
740 | +static void pmbus_add_boolean_cmp(struct pmbus_data *data, | |
741 | + const char *name, const char *type, | |
742 | + int seq, int i1, int i2, int reg, int mask) | |
743 | +{ | |
744 | + pmbus_add_boolean(data, name, type, seq, | |
745 | + (i1 << 24) | (i2 << 16) | (reg << 8) | mask); | |
746 | +} | |
747 | + | |
748 | +static void pmbus_add_sensor(struct pmbus_data *data, | |
749 | + const char *name, const char *type, int seq, | |
750 | + int page, int reg, enum pmbus_sensor_classes class, | |
751 | + bool update) | |
752 | +{ | |
753 | + struct pmbus_sensor *sensor; | |
754 | + | |
755 | + BUG_ON(data->num_sensors >= data->max_sensors); | |
756 | + | |
757 | + sensor = &data->sensors[data->num_sensors]; | |
758 | + snprintf(sensor->name, sizeof(sensor->name), "%s%d_%s", | |
759 | + name, seq, type); | |
760 | + sensor->page = page; | |
761 | + sensor->reg = reg; | |
762 | + sensor->class = class; | |
763 | + sensor->update = update; | |
764 | + if (update) | |
765 | + PMBUS_ADD_GET_ATTR(data, sensor->name, sensor, | |
766 | + data->num_sensors); | |
767 | + else | |
768 | + PMBUS_ADD_SET_ATTR(data, sensor->name, sensor, | |
769 | + data->num_sensors); | |
770 | + data->num_sensors++; | |
771 | +} | |
772 | + | |
773 | +static void pmbus_add_label(struct pmbus_data *data, | |
774 | + const char *name, int seq, | |
775 | + const char *lstring, int index) | |
776 | +{ | |
777 | + struct pmbus_label *label; | |
778 | + | |
779 | + BUG_ON(data->num_labels >= data->max_labels); | |
780 | + | |
781 | + label = &data->labels[data->num_labels]; | |
782 | + snprintf(label->name, sizeof(label->name), "%s%d_label", name, seq); | |
783 | + if (!index) | |
784 | + strncpy(label->label, lstring, sizeof(label->label) - 1); | |
785 | + else | |
786 | + snprintf(label->label, sizeof(label->label), "%s%d", lstring, | |
787 | + index); | |
788 | + | |
789 | + PMBUS_ADD_GET_ATTR(data, label->name, label, data->num_labels); | |
790 | + data->num_labels++; | |
791 | +} | |
792 | + | |
793 | +static const int pmbus_temp_registers[] = { | |
794 | + PMBUS_READ_TEMPERATURE_1, | |
795 | + PMBUS_READ_TEMPERATURE_2, | |
796 | + PMBUS_READ_TEMPERATURE_3 | |
797 | +}; | |
798 | + | |
799 | +static const int pmbus_fan_registers[] = { | |
800 | + PMBUS_READ_FAN_SPEED_1, | |
801 | + PMBUS_READ_FAN_SPEED_2, | |
802 | + PMBUS_READ_FAN_SPEED_3, | |
803 | + PMBUS_READ_FAN_SPEED_4 | |
804 | +}; | |
805 | + | |
806 | +static const int pmbus_fan_config_registers[] = { | |
807 | + PMBUS_FAN_CONFIG_12, | |
808 | + PMBUS_FAN_CONFIG_12, | |
809 | + PMBUS_FAN_CONFIG_34, | |
810 | + PMBUS_FAN_CONFIG_34 | |
811 | +}; | |
812 | + | |
813 | +static const int pmbus_fan_status_registers[] = { | |
814 | + PMBUS_STATUS_FAN_12, | |
815 | + PMBUS_STATUS_FAN_12, | |
816 | + PMBUS_STATUS_FAN_34, | |
817 | + PMBUS_STATUS_FAN_34 | |
818 | +}; | |
819 | + | |
820 | +/* | |
821 | + * Determine maximum number of sensors, booleans, and labels. | |
822 | + * To keep things simple, only make a rough high estimate. | |
823 | + */ | |
824 | +static void pmbus_find_max_attr(struct i2c_client *client, | |
825 | + struct pmbus_data *data) | |
826 | +{ | |
827 | + const struct pmbus_driver_info *info = data->info; | |
828 | + int page, max_sensors, max_booleans, max_labels; | |
829 | + | |
830 | + max_sensors = PMBUS_MAX_INPUT_SENSORS; | |
831 | + max_booleans = PMBUS_MAX_INPUT_BOOLEANS; | |
832 | + max_labels = PMBUS_MAX_INPUT_LABELS; | |
833 | + | |
834 | + for (page = 0; page < info->pages; page++) { | |
835 | + if (info->func[page] & PMBUS_HAVE_VOUT) { | |
836 | + max_sensors += PMBUS_VOUT_SENSORS_PER_PAGE; | |
837 | + max_booleans += PMBUS_VOUT_BOOLEANS_PER_PAGE; | |
838 | + max_labels++; | |
839 | + } | |
840 | + if (info->func[page] & PMBUS_HAVE_IOUT) { | |
841 | + max_sensors += PMBUS_IOUT_SENSORS_PER_PAGE; | |
842 | + max_booleans += PMBUS_IOUT_BOOLEANS_PER_PAGE; | |
843 | + max_labels++; | |
844 | + } | |
845 | + if (info->func[page] & PMBUS_HAVE_POUT) { | |
846 | + max_sensors += PMBUS_POUT_SENSORS_PER_PAGE; | |
847 | + max_booleans += PMBUS_POUT_BOOLEANS_PER_PAGE; | |
848 | + max_labels++; | |
849 | + } | |
850 | + if (info->func[page] & PMBUS_HAVE_FAN12) { | |
851 | + if (page == 0) { | |
852 | + max_sensors += | |
853 | + ARRAY_SIZE(pmbus_fan_registers) * | |
854 | + PMBUS_MAX_SENSORS_PER_FAN; | |
855 | + max_booleans += | |
856 | + ARRAY_SIZE(pmbus_fan_registers) * | |
857 | + PMBUS_MAX_BOOLEANS_PER_FAN; | |
858 | + } else { | |
859 | + max_sensors += PMBUS_MAX_SENSORS_PER_FAN; | |
860 | + max_booleans += PMBUS_MAX_BOOLEANS_PER_FAN; | |
861 | + } | |
862 | + } | |
863 | + if (info->func[page] & PMBUS_HAVE_TEMP) { | |
864 | + if (page == 0) { | |
865 | + max_sensors += | |
866 | + ARRAY_SIZE(pmbus_temp_registers) * | |
867 | + PMBUS_MAX_SENSORS_PER_TEMP; | |
868 | + max_booleans += | |
869 | + ARRAY_SIZE(pmbus_temp_registers) * | |
870 | + PMBUS_MAX_BOOLEANS_PER_TEMP; | |
871 | + } else { | |
872 | + max_sensors += PMBUS_MAX_SENSORS_PER_TEMP; | |
873 | + max_booleans += PMBUS_MAX_BOOLEANS_PER_TEMP; | |
874 | + } | |
875 | + } | |
876 | + } | |
877 | + data->max_sensors = max_sensors; | |
878 | + data->max_booleans = max_booleans; | |
879 | + data->max_labels = max_labels; | |
880 | + data->max_attributes = max_sensors + max_booleans + max_labels; | |
881 | +} | |
882 | + | |
883 | +/* | |
884 | + * Search for attributes. Allocate sensors, booleans, and labels as needed. | |
885 | + */ | |
886 | +static void pmbus_find_attributes(struct i2c_client *client, | |
887 | + struct pmbus_data *data) | |
888 | +{ | |
889 | + const struct pmbus_driver_info *info = data->info; | |
890 | + int page, i0, i1, in_index; | |
891 | + | |
892 | + /* | |
893 | + * Input voltage sensors | |
894 | + */ | |
895 | + in_index = 1; | |
896 | + if (info->func[0] & PMBUS_HAVE_VIN) { | |
897 | + bool have_alarm = false; | |
898 | + | |
899 | + i0 = data->num_sensors; | |
900 | + pmbus_add_label(data, "in", in_index, "vin", 0); | |
901 | + pmbus_add_sensor(data, "in", "input", in_index, | |
902 | + 0, PMBUS_READ_VIN, PSC_VOLTAGE_IN, true); | |
903 | + if (pmbus_check_word_register(client, 0, | |
904 | + PMBUS_VIN_UV_WARN_LIMIT)) { | |
905 | + i1 = data->num_sensors; | |
906 | + pmbus_add_sensor(data, "in", "min", in_index, | |
907 | + 0, PMBUS_VIN_UV_WARN_LIMIT, | |
908 | + PSC_VOLTAGE_IN, false); | |
909 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { | |
910 | + pmbus_add_boolean_reg(data, "in", "min_alarm", | |
911 | + in_index, | |
912 | + PB_STATUS_INPUT_BASE, | |
913 | + PB_VOLTAGE_UV_WARNING); | |
914 | + have_alarm = true; | |
915 | + } | |
916 | + } | |
917 | + if (pmbus_check_word_register(client, 0, | |
918 | + PMBUS_VIN_UV_FAULT_LIMIT)) { | |
919 | + i1 = data->num_sensors; | |
920 | + pmbus_add_sensor(data, "in", "lcrit", in_index, | |
921 | + 0, PMBUS_VIN_UV_FAULT_LIMIT, | |
922 | + PSC_VOLTAGE_IN, false); | |
923 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { | |
924 | + pmbus_add_boolean_reg(data, "in", "lcrit_alarm", | |
925 | + in_index, | |
926 | + PB_STATUS_INPUT_BASE, | |
927 | + PB_VOLTAGE_UV_FAULT); | |
928 | + have_alarm = true; | |
929 | + } | |
930 | + } | |
931 | + if (pmbus_check_word_register(client, 0, | |
932 | + PMBUS_VIN_OV_WARN_LIMIT)) { | |
933 | + i1 = data->num_sensors; | |
934 | + pmbus_add_sensor(data, "in", "max", in_index, | |
935 | + 0, PMBUS_VIN_OV_WARN_LIMIT, | |
936 | + PSC_VOLTAGE_IN, false); | |
937 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { | |
938 | + pmbus_add_boolean_reg(data, "in", "max_alarm", | |
939 | + in_index, | |
940 | + PB_STATUS_INPUT_BASE, | |
941 | + PB_VOLTAGE_OV_WARNING); | |
942 | + have_alarm = true; | |
943 | + } | |
944 | + } | |
945 | + if (pmbus_check_word_register(client, 0, | |
946 | + PMBUS_VIN_OV_FAULT_LIMIT)) { | |
947 | + i1 = data->num_sensors; | |
948 | + pmbus_add_sensor(data, "in", "crit", in_index, | |
949 | + 0, PMBUS_VIN_OV_FAULT_LIMIT, | |
950 | + PSC_VOLTAGE_IN, false); | |
951 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { | |
952 | + pmbus_add_boolean_reg(data, "in", "crit_alarm", | |
953 | + in_index, | |
954 | + PB_STATUS_INPUT_BASE, | |
955 | + PB_VOLTAGE_OV_FAULT); | |
956 | + have_alarm = true; | |
957 | + } | |
958 | + } | |
959 | + /* | |
960 | + * Add generic alarm attribute only if there are no individual | |
961 | + * attributes. | |
962 | + */ | |
963 | + if (!have_alarm) | |
964 | + pmbus_add_boolean_reg(data, "in", "alarm", | |
965 | + in_index, | |
966 | + PB_STATUS_BASE, | |
967 | + PB_STATUS_VIN_UV); | |
968 | + in_index++; | |
969 | + } | |
970 | + if (info->func[0] & PMBUS_HAVE_VCAP) { | |
971 | + pmbus_add_label(data, "in", in_index, "vcap", 0); | |
972 | + pmbus_add_sensor(data, "in", "input", in_index, 0, | |
973 | + PMBUS_READ_VCAP, PSC_VOLTAGE_IN, true); | |
974 | + in_index++; | |
975 | + } | |
976 | + | |
977 | + /* | |
978 | + * Output voltage sensors | |
979 | + */ | |
980 | + for (page = 0; page < info->pages; page++) { | |
981 | + bool have_alarm = false; | |
982 | + | |
983 | + if (!(info->func[page] & PMBUS_HAVE_VOUT)) | |
984 | + continue; | |
985 | + | |
986 | + i0 = data->num_sensors; | |
987 | + pmbus_add_label(data, "in", in_index, "vout", page + 1); | |
988 | + pmbus_add_sensor(data, "in", "input", in_index, page, | |
989 | + PMBUS_READ_VOUT, PSC_VOLTAGE_OUT, true); | |
990 | + if (pmbus_check_word_register(client, page, | |
991 | + PMBUS_VOUT_UV_WARN_LIMIT)) { | |
992 | + i1 = data->num_sensors; | |
993 | + pmbus_add_sensor(data, "in", "min", in_index, page, | |
994 | + PMBUS_VOUT_UV_WARN_LIMIT, | |
995 | + PSC_VOLTAGE_OUT, false); | |
996 | + if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) { | |
997 | + pmbus_add_boolean_reg(data, "in", "min_alarm", | |
998 | + in_index, | |
999 | + PB_STATUS_VOUT_BASE + | |
1000 | + page, | |
1001 | + PB_VOLTAGE_UV_WARNING); | |
1002 | + have_alarm = true; | |
1003 | + } | |
1004 | + } | |
1005 | + if (pmbus_check_word_register(client, page, | |
1006 | + PMBUS_VOUT_UV_FAULT_LIMIT)) { | |
1007 | + i1 = data->num_sensors; | |
1008 | + pmbus_add_sensor(data, "in", "lcrit", in_index, page, | |
1009 | + PMBUS_VOUT_UV_FAULT_LIMIT, | |
1010 | + PSC_VOLTAGE_OUT, false); | |
1011 | + if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) { | |
1012 | + pmbus_add_boolean_reg(data, "in", "lcrit_alarm", | |
1013 | + in_index, | |
1014 | + PB_STATUS_VOUT_BASE + | |
1015 | + page, | |
1016 | + PB_VOLTAGE_UV_FAULT); | |
1017 | + have_alarm = true; | |
1018 | + } | |
1019 | + } | |
1020 | + if (pmbus_check_word_register(client, page, | |
1021 | + PMBUS_VOUT_OV_WARN_LIMIT)) { | |
1022 | + i1 = data->num_sensors; | |
1023 | + pmbus_add_sensor(data, "in", "max", in_index, page, | |
1024 | + PMBUS_VOUT_OV_WARN_LIMIT, | |
1025 | + PSC_VOLTAGE_OUT, false); | |
1026 | + if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) { | |
1027 | + pmbus_add_boolean_reg(data, "in", "max_alarm", | |
1028 | + in_index, | |
1029 | + PB_STATUS_VOUT_BASE + | |
1030 | + page, | |
1031 | + PB_VOLTAGE_OV_WARNING); | |
1032 | + have_alarm = true; | |
1033 | + } | |
1034 | + } | |
1035 | + if (pmbus_check_word_register(client, page, | |
1036 | + PMBUS_VOUT_OV_FAULT_LIMIT)) { | |
1037 | + i1 = data->num_sensors; | |
1038 | + pmbus_add_sensor(data, "in", "crit", in_index, page, | |
1039 | + PMBUS_VOUT_OV_FAULT_LIMIT, | |
1040 | + PSC_VOLTAGE_OUT, false); | |
1041 | + if (info->func[page] & PMBUS_HAVE_STATUS_VOUT) { | |
1042 | + pmbus_add_boolean_reg(data, "in", "crit_alarm", | |
1043 | + in_index, | |
1044 | + PB_STATUS_VOUT_BASE + | |
1045 | + page, | |
1046 | + PB_VOLTAGE_OV_FAULT); | |
1047 | + have_alarm = true; | |
1048 | + } | |
1049 | + } | |
1050 | + /* | |
1051 | + * Add generic alarm attribute only if there are no individual | |
1052 | + * attributes. | |
1053 | + */ | |
1054 | + if (!have_alarm) | |
1055 | + pmbus_add_boolean_reg(data, "in", "alarm", | |
1056 | + in_index, | |
1057 | + PB_STATUS_BASE + page, | |
1058 | + PB_STATUS_VOUT_OV); | |
1059 | + in_index++; | |
1060 | + } | |
1061 | + | |
1062 | + /* | |
1063 | + * Current sensors | |
1064 | + */ | |
1065 | + | |
1066 | + /* | |
1067 | + * Input current sensors | |
1068 | + */ | |
1069 | + in_index = 1; | |
1070 | + if (info->func[0] & PMBUS_HAVE_IIN) { | |
1071 | + i0 = data->num_sensors; | |
1072 | + pmbus_add_label(data, "curr", in_index, "iin", 0); | |
1073 | + pmbus_add_sensor(data, "curr", "input", in_index, | |
1074 | + 0, PMBUS_READ_IIN, PSC_CURRENT_IN, true); | |
1075 | + if (pmbus_check_word_register(client, 0, | |
1076 | + PMBUS_IIN_OC_WARN_LIMIT)) { | |
1077 | + i1 = data->num_sensors; | |
1078 | + pmbus_add_sensor(data, "curr", "max", in_index, | |
1079 | + 0, PMBUS_IIN_OC_WARN_LIMIT, | |
1080 | + PSC_CURRENT_IN, false); | |
1081 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) { | |
1082 | + pmbus_add_boolean_reg(data, "curr", "max_alarm", | |
1083 | + in_index, | |
1084 | + PB_STATUS_INPUT_BASE, | |
1085 | + PB_IIN_OC_WARNING); | |
1086 | + } | |
1087 | + } | |
1088 | + if (pmbus_check_word_register(client, 0, | |
1089 | + PMBUS_IIN_OC_FAULT_LIMIT)) { | |
1090 | + i1 = data->num_sensors; | |
1091 | + pmbus_add_sensor(data, "curr", "crit", in_index, | |
1092 | + 0, PMBUS_IIN_OC_FAULT_LIMIT, | |
1093 | + PSC_CURRENT_IN, false); | |
1094 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) | |
1095 | + pmbus_add_boolean_reg(data, "curr", | |
1096 | + "crit_alarm", | |
1097 | + in_index, | |
1098 | + PB_STATUS_INPUT_BASE, | |
1099 | + PB_IIN_OC_FAULT); | |
1100 | + } | |
1101 | + in_index++; | |
1102 | + } | |
1103 | + | |
1104 | + /* | |
1105 | + * Output current sensors | |
1106 | + */ | |
1107 | + for (page = 0; page < info->pages; page++) { | |
1108 | + bool have_alarm = false; | |
1109 | + | |
1110 | + if (!(info->func[page] & PMBUS_HAVE_IOUT)) | |
1111 | + continue; | |
1112 | + | |
1113 | + i0 = data->num_sensors; | |
1114 | + pmbus_add_label(data, "curr", in_index, "iout", page + 1); | |
1115 | + pmbus_add_sensor(data, "curr", "input", in_index, page, | |
1116 | + PMBUS_READ_IOUT, PSC_CURRENT_OUT, true); | |
1117 | + if (pmbus_check_word_register(client, page, | |
1118 | + PMBUS_IOUT_OC_WARN_LIMIT)) { | |
1119 | + i1 = data->num_sensors; | |
1120 | + pmbus_add_sensor(data, "curr", "max", in_index, page, | |
1121 | + PMBUS_IOUT_OC_WARN_LIMIT, | |
1122 | + PSC_CURRENT_OUT, false); | |
1123 | + if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { | |
1124 | + pmbus_add_boolean_reg(data, "curr", "max_alarm", | |
1125 | + in_index, | |
1126 | + PB_STATUS_IOUT_BASE + | |
1127 | + page, PB_IOUT_OC_WARNING); | |
1128 | + have_alarm = true; | |
1129 | + } | |
1130 | + } | |
1131 | + if (pmbus_check_word_register(client, page, | |
1132 | + PMBUS_IOUT_UC_FAULT_LIMIT)) { | |
1133 | + i1 = data->num_sensors; | |
1134 | + pmbus_add_sensor(data, "curr", "lcrit", in_index, page, | |
1135 | + PMBUS_IOUT_UC_FAULT_LIMIT, | |
1136 | + PSC_CURRENT_OUT, false); | |
1137 | + if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { | |
1138 | + pmbus_add_boolean_reg(data, "curr", | |
1139 | + "lcrit_alarm", | |
1140 | + in_index, | |
1141 | + PB_STATUS_IOUT_BASE + | |
1142 | + page, PB_IOUT_UC_FAULT); | |
1143 | + have_alarm = true; | |
1144 | + } | |
1145 | + } | |
1146 | + if (pmbus_check_word_register(client, page, | |
1147 | + PMBUS_IOUT_OC_FAULT_LIMIT)) { | |
1148 | + i1 = data->num_sensors; | |
1149 | + pmbus_add_sensor(data, "curr", "crit", in_index, page, | |
1150 | + PMBUS_IOUT_OC_FAULT_LIMIT, | |
1151 | + PSC_CURRENT_OUT, false); | |
1152 | + if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) { | |
1153 | + pmbus_add_boolean_reg(data, "curr", | |
1154 | + "crit_alarm", | |
1155 | + in_index, | |
1156 | + PB_STATUS_IOUT_BASE + | |
1157 | + page, PB_IOUT_OC_FAULT); | |
1158 | + have_alarm = true; | |
1159 | + } | |
1160 | + } | |
1161 | + /* | |
1162 | + * Add generic alarm attribute only if there are no individual | |
1163 | + * attributes. | |
1164 | + */ | |
1165 | + if (!have_alarm) | |
1166 | + pmbus_add_boolean_reg(data, "curr", "alarm", | |
1167 | + in_index, | |
1168 | + PB_STATUS_BASE + page, | |
1169 | + PB_STATUS_IOUT_OC); | |
1170 | + in_index++; | |
1171 | + } | |
1172 | + | |
1173 | + /* | |
1174 | + * Power sensors | |
1175 | + */ | |
1176 | + /* | |
1177 | + * Input Power sensors | |
1178 | + */ | |
1179 | + in_index = 1; | |
1180 | + if (info->func[0] & PMBUS_HAVE_PIN) { | |
1181 | + i0 = data->num_sensors; | |
1182 | + pmbus_add_label(data, "power", in_index, "pin", 0); | |
1183 | + pmbus_add_sensor(data, "power", "input", in_index, | |
1184 | + 0, PMBUS_READ_PIN, PSC_POWER, true); | |
1185 | + if (pmbus_check_word_register(client, 0, | |
1186 | + PMBUS_PIN_OP_WARN_LIMIT)) { | |
1187 | + i1 = data->num_sensors; | |
1188 | + pmbus_add_sensor(data, "power", "max", in_index, | |
1189 | + 0, PMBUS_PIN_OP_WARN_LIMIT, PSC_POWER, | |
1190 | + false); | |
1191 | + if (info->func[0] & PMBUS_HAVE_STATUS_INPUT) | |
1192 | + pmbus_add_boolean_reg(data, "power", | |
1193 | + "alarm", | |
1194 | + in_index, | |
1195 | + PB_STATUS_INPUT_BASE, | |
1196 | + PB_PIN_OP_WARNING); | |
1197 | + } | |
1198 | + in_index++; | |
1199 | + } | |
1200 | + | |
1201 | + /* | |
1202 | + * Output Power sensors | |
1203 | + */ | |
1204 | + for (page = 0; page < info->pages; page++) { | |
1205 | + bool need_alarm = false; | |
1206 | + | |
1207 | + if (!(info->func[page] & PMBUS_HAVE_POUT)) | |
1208 | + continue; | |
1209 | + | |
1210 | + i0 = data->num_sensors; | |
1211 | + pmbus_add_label(data, "power", in_index, "pout", page + 1); | |
1212 | + pmbus_add_sensor(data, "power", "input", in_index, page, | |
1213 | + PMBUS_READ_POUT, PSC_POWER, true); | |
1214 | + /* | |
1215 | + * Per hwmon sysfs API, power_cap is to be used to limit output | |
1216 | + * power. | |
1217 | + * We have two registers related to maximum output power, | |
1218 | + * PMBUS_POUT_MAX and PMBUS_POUT_OP_WARN_LIMIT. | |
1219 | + * PMBUS_POUT_MAX matches the powerX_cap attribute definition. | |
1220 | + * There is no attribute in the API to match | |
1221 | + * PMBUS_POUT_OP_WARN_LIMIT. We use powerX_max for now. | |
1222 | + */ | |
1223 | + if (pmbus_check_word_register(client, page, PMBUS_POUT_MAX)) { | |
1224 | + i1 = data->num_sensors; | |
1225 | + pmbus_add_sensor(data, "power", "cap", in_index, page, | |
1226 | + PMBUS_POUT_MAX, PSC_POWER, false); | |
1227 | + need_alarm = true; | |
1228 | + } | |
1229 | + if (pmbus_check_word_register(client, page, | |
1230 | + PMBUS_POUT_OP_WARN_LIMIT)) { | |
1231 | + i1 = data->num_sensors; | |
1232 | + pmbus_add_sensor(data, "power", "max", in_index, page, | |
1233 | + PMBUS_POUT_OP_WARN_LIMIT, PSC_POWER, | |
1234 | + false); | |
1235 | + need_alarm = true; | |
1236 | + } | |
1237 | + if (need_alarm && (info->func[page] & PMBUS_HAVE_STATUS_IOUT)) | |
1238 | + pmbus_add_boolean_reg(data, "power", "alarm", | |
1239 | + in_index, | |
1240 | + PB_STATUS_IOUT_BASE + page, | |
1241 | + PB_POUT_OP_WARNING | |
1242 | + | PB_POWER_LIMITING); | |
1243 | + | |
1244 | + if (pmbus_check_word_register(client, page, | |
1245 | + PMBUS_POUT_OP_FAULT_LIMIT)) { | |
1246 | + i1 = data->num_sensors; | |
1247 | + pmbus_add_sensor(data, "power", "crit", in_index, page, | |
1248 | + PMBUS_POUT_OP_FAULT_LIMIT, PSC_POWER, | |
1249 | + false); | |
1250 | + if (info->func[page] & PMBUS_HAVE_STATUS_IOUT) | |
1251 | + pmbus_add_boolean_reg(data, "power", | |
1252 | + "crit_alarm", | |
1253 | + in_index, | |
1254 | + PB_STATUS_IOUT_BASE | |
1255 | + + page, | |
1256 | + PB_POUT_OP_FAULT); | |
1257 | + } | |
1258 | + in_index++; | |
1259 | + } | |
1260 | + | |
1261 | + /* | |
1262 | + * Temperature sensors | |
1263 | + */ | |
1264 | + in_index = 1; | |
1265 | + for (page = 0; page < info->pages; page++) { | |
1266 | + int t, temps; | |
1267 | + | |
1268 | + if (!(info->func[page] & PMBUS_HAVE_TEMP)) | |
1269 | + continue; | |
1270 | + | |
1271 | + temps = page ? 1 : ARRAY_SIZE(pmbus_temp_registers); | |
1272 | + for (t = 0; t < temps; t++) { | |
1273 | + bool have_alarm = false; | |
1274 | + | |
1275 | + if (!pmbus_check_word_register | |
1276 | + (client, page, pmbus_temp_registers[t])) | |
1277 | + break; | |
1278 | + | |
1279 | + i0 = data->num_sensors; | |
1280 | + pmbus_add_sensor(data, "temp", "input", in_index, page, | |
1281 | + pmbus_temp_registers[t], | |
1282 | + PSC_TEMPERATURE, true); | |
1283 | + | |
1284 | + /* | |
1285 | + * PMBus provides only one status register for TEMP1-3. | |
1286 | + * Thus, we can not use the status register to determine | |
1287 | + * which of the three sensors actually caused an alarm. | |
1288 | + * Always compare current temperature against the limit | |
1289 | + * registers to determine alarm conditions for a | |
1290 | + * specific sensor. | |
1291 | + */ | |
1292 | + if (pmbus_check_word_register | |
1293 | + (client, page, PMBUS_UT_WARN_LIMIT)) { | |
1294 | + i1 = data->num_sensors; | |
1295 | + pmbus_add_sensor(data, "temp", "min", in_index, | |
1296 | + page, PMBUS_UT_WARN_LIMIT, | |
1297 | + PSC_TEMPERATURE, false); | |
1298 | + if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { | |
1299 | + pmbus_add_boolean_cmp(data, "temp", | |
1300 | + "min_alarm", in_index, i1, i0, | |
1301 | + PB_STATUS_TEMP_BASE + page, | |
1302 | + PB_TEMP_UT_WARNING); | |
1303 | + have_alarm = true; | |
1304 | + } | |
1305 | + } | |
1306 | + if (pmbus_check_word_register(client, page, | |
1307 | + PMBUS_UT_FAULT_LIMIT)) { | |
1308 | + i1 = data->num_sensors; | |
1309 | + pmbus_add_sensor(data, "temp", "lcrit", | |
1310 | + in_index, page, | |
1311 | + PMBUS_UT_FAULT_LIMIT, | |
1312 | + PSC_TEMPERATURE, false); | |
1313 | + if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { | |
1314 | + pmbus_add_boolean_cmp(data, "temp", | |
1315 | + "lcrit_alarm", in_index, i1, i0, | |
1316 | + PB_STATUS_TEMP_BASE + page, | |
1317 | + PB_TEMP_UT_FAULT); | |
1318 | + have_alarm = true; | |
1319 | + } | |
1320 | + } | |
1321 | + if (pmbus_check_word_register | |
1322 | + (client, page, PMBUS_OT_WARN_LIMIT)) { | |
1323 | + i1 = data->num_sensors; | |
1324 | + pmbus_add_sensor(data, "temp", "max", in_index, | |
1325 | + page, PMBUS_OT_WARN_LIMIT, | |
1326 | + PSC_TEMPERATURE, false); | |
1327 | + if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { | |
1328 | + pmbus_add_boolean_cmp(data, "temp", | |
1329 | + "max_alarm", in_index, i0, i1, | |
1330 | + PB_STATUS_TEMP_BASE + page, | |
1331 | + PB_TEMP_OT_WARNING); | |
1332 | + have_alarm = true; | |
1333 | + } | |
1334 | + } | |
1335 | + if (pmbus_check_word_register(client, page, | |
1336 | + PMBUS_OT_FAULT_LIMIT)) { | |
1337 | + i1 = data->num_sensors; | |
1338 | + pmbus_add_sensor(data, "temp", "crit", in_index, | |
1339 | + page, PMBUS_OT_FAULT_LIMIT, | |
1340 | + PSC_TEMPERATURE, false); | |
1341 | + if (info->func[page] & PMBUS_HAVE_STATUS_TEMP) { | |
1342 | + pmbus_add_boolean_cmp(data, "temp", | |
1343 | + "crit_alarm", in_index, i0, i1, | |
1344 | + PB_STATUS_TEMP_BASE + page, | |
1345 | + PB_TEMP_OT_FAULT); | |
1346 | + have_alarm = true; | |
1347 | + } | |
1348 | + } | |
1349 | + /* | |
1350 | + * Last resort - we were not able to create any alarm | |
1351 | + * registers. Report alarm for all sensors using the | |
1352 | + * status register temperature alarm bit. | |
1353 | + */ | |
1354 | + if (!have_alarm) | |
1355 | + pmbus_add_boolean_reg(data, "temp", "alarm", | |
1356 | + in_index, | |
1357 | + PB_STATUS_BASE + page, | |
1358 | + PB_STATUS_TEMPERATURE); | |
1359 | + in_index++; | |
1360 | + } | |
1361 | + } | |
1362 | + | |
1363 | + /* | |
1364 | + * Fans | |
1365 | + */ | |
1366 | + in_index = 1; | |
1367 | + for (page = 0; page < info->pages; page++) { | |
1368 | + int fans, f; | |
1369 | + | |
1370 | + if (!(info->func[page] & PMBUS_HAVE_FAN12)) | |
1371 | + continue; | |
1372 | + | |
1373 | + fans = page ? 1 : ARRAY_SIZE(pmbus_fan_registers); | |
1374 | + for (f = 0; f < fans; f++) { | |
1375 | + int regval; | |
1376 | + | |
1377 | + if (!pmbus_check_word_register(client, page, | |
1378 | + pmbus_fan_registers[f]) | |
1379 | + || !pmbus_check_byte_register(client, page, | |
1380 | + pmbus_fan_config_registers[f])) | |
1381 | + break; | |
1382 | + | |
1383 | + /* | |
1384 | + * Skip fan if not installed. | |
1385 | + * Each fan configuration register covers multiple fans, | |
1386 | + * so we have to do some magic. | |
1387 | + */ | |
1388 | + regval = pmbus_read_byte_data(client, page, | |
1389 | + pmbus_fan_config_registers[f]); | |
1390 | + if (regval < 0 || | |
1391 | + (!(regval & (PB_FAN_1_INSTALLED >> ((f & 1) * 4))))) | |
1392 | + continue; | |
1393 | + | |
1394 | + i0 = data->num_sensors; | |
1395 | + pmbus_add_sensor(data, "fan", "input", in_index, page, | |
1396 | + pmbus_fan_registers[f], PSC_FAN, true); | |
1397 | + | |
1398 | + /* | |
1399 | + * Each fan status register covers multiple fans, | |
1400 | + * so we have to do some magic. | |
1401 | + */ | |
1402 | + if (pmbus_check_byte_register | |
1403 | + (client, page, pmbus_fan_status_registers[f])) { | |
1404 | + int base; | |
1405 | + | |
1406 | + if (f > 1) /* fan 3, 4 */ | |
1407 | + base = PB_STATUS_FAN34_BASE; | |
1408 | + else | |
1409 | + base = PB_STATUS_FAN_BASE + page; | |
1410 | + pmbus_add_boolean_reg(data, "fan", "alarm", | |
1411 | + in_index, base, | |
1412 | + PB_FAN_FAN1_WARNING >> (f & 1)); | |
1413 | + pmbus_add_boolean_reg(data, "fan", "fault", | |
1414 | + in_index, base, | |
1415 | + PB_FAN_FAN1_FAULT >> (f & 1)); | |
1416 | + } | |
1417 | + in_index++; | |
1418 | + } | |
1419 | + } | |
1420 | +} | |
1421 | + | |
1422 | +/* | |
1423 | + * Identify chip parameters. | |
1424 | + * This function is called for all chips. | |
1425 | + */ | |
1426 | +static int pmbus_identify_common(struct i2c_client *client, | |
1427 | + struct pmbus_data *data) | |
1428 | +{ | |
1429 | + int vout_mode, exponent; | |
1430 | + | |
1431 | + vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); | |
1432 | + if (vout_mode >= 0) { | |
1433 | + /* | |
1434 | + * Not all chips support the VOUT_MODE command, | |
1435 | + * so a failure to read it is not an error. | |
1436 | + */ | |
1437 | + switch (vout_mode >> 5) { | |
1438 | + case 0: /* linear mode */ | |
1439 | + if (data->info->direct[PSC_VOLTAGE_OUT]) | |
1440 | + return -ENODEV; | |
1441 | + | |
1442 | + exponent = vout_mode & 0x1f; | |
1443 | + /* and sign-extend it */ | |
1444 | + if (exponent & 0x10) | |
1445 | + exponent |= ~0x1f; | |
1446 | + data->exponent = exponent; | |
1447 | + break; | |
1448 | + case 2: /* direct mode */ | |
1449 | + if (!data->info->direct[PSC_VOLTAGE_OUT]) | |
1450 | + return -ENODEV; | |
1451 | + break; | |
1452 | + default: | |
1453 | + return -ENODEV; | |
1454 | + } | |
1455 | + } | |
1456 | + | |
1457 | + /* Determine maximum number of sensors, booleans, and labels */ | |
1458 | + pmbus_find_max_attr(client, data); | |
1459 | + pmbus_clear_fault_page(client, 0); | |
1460 | + return 0; | |
1461 | +} | |
1462 | + | |
1463 | +int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, | |
1464 | + struct pmbus_driver_info *info) | |
1465 | +{ | |
1466 | + const struct pmbus_platform_data *pdata = client->dev.platform_data; | |
1467 | + struct pmbus_data *data; | |
1468 | + int ret; | |
1469 | + | |
1470 | + if (!info) { | |
1471 | + dev_err(&client->dev, "Missing chip information"); | |
1472 | + return -ENODEV; | |
1473 | + } | |
1474 | + | |
1475 | + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_BYTE | |
1476 | + | I2C_FUNC_SMBUS_BYTE_DATA | |
1477 | + | I2C_FUNC_SMBUS_WORD_DATA)) | |
1478 | + return -ENODEV; | |
1479 | + | |
1480 | + data = kzalloc(sizeof(*data), GFP_KERNEL); | |
1481 | + if (!data) { | |
1482 | + dev_err(&client->dev, "No memory to allocate driver data\n"); | |
1483 | + return -ENOMEM; | |
1484 | + } | |
1485 | + | |
1486 | + i2c_set_clientdata(client, data); | |
1487 | + mutex_init(&data->update_lock); | |
1488 | + | |
1489 | + /* | |
1490 | + * Bail out if status register or PMBus revision register | |
1491 | + * does not exist. | |
1492 | + */ | |
1493 | + if (i2c_smbus_read_byte_data(client, PMBUS_STATUS_BYTE) < 0 | |
1494 | + || i2c_smbus_read_byte_data(client, PMBUS_REVISION) < 0) { | |
1495 | + dev_err(&client->dev, | |
1496 | + "Status or revision register not found\n"); | |
1497 | + ret = -ENODEV; | |
1498 | + goto out_data; | |
1499 | + } | |
1500 | + | |
1501 | + if (pdata) | |
1502 | + data->flags = pdata->flags; | |
1503 | + data->info = info; | |
1504 | + | |
1505 | + pmbus_clear_faults(client); | |
1506 | + | |
1507 | + if (info->identify) { | |
1508 | + ret = (*info->identify)(client, info); | |
1509 | + if (ret < 0) { | |
1510 | + dev_err(&client->dev, "Chip identification failed\n"); | |
1511 | + goto out_data; | |
1512 | + } | |
1513 | + } | |
1514 | + | |
1515 | + if (info->pages <= 0 || info->pages > PMBUS_PAGES) { | |
1516 | + dev_err(&client->dev, "Bad number of PMBus pages: %d\n", | |
1517 | + info->pages); | |
1518 | + ret = -EINVAL; | |
1519 | + goto out_data; | |
1520 | + } | |
1521 | + /* | |
1522 | + * Bail out if more than one page was configured, but we can not | |
1523 | + * select the highest page. This is an indication that the wrong | |
1524 | + * chip type was selected. Better bail out now than keep | |
1525 | + * returning errors later on. | |
1526 | + */ | |
1527 | + if (info->pages > 1 && pmbus_set_page(client, info->pages - 1) < 0) { | |
1528 | + dev_err(&client->dev, "Failed to select page %d\n", | |
1529 | + info->pages - 1); | |
1530 | + ret = -EINVAL; | |
1531 | + goto out_data; | |
1532 | + } | |
1533 | + | |
1534 | + ret = pmbus_identify_common(client, data); | |
1535 | + if (ret < 0) { | |
1536 | + dev_err(&client->dev, "Failed to identify chip capabilities\n"); | |
1537 | + goto out_data; | |
1538 | + } | |
1539 | + | |
1540 | + ret = -ENOMEM; | |
1541 | + data->sensors = kzalloc(sizeof(struct pmbus_sensor) * data->max_sensors, | |
1542 | + GFP_KERNEL); | |
1543 | + if (!data->sensors) { | |
1544 | + dev_err(&client->dev, "No memory to allocate sensor data\n"); | |
1545 | + goto out_data; | |
1546 | + } | |
1547 | + | |
1548 | + data->booleans = kzalloc(sizeof(struct pmbus_boolean) | |
1549 | + * data->max_booleans, GFP_KERNEL); | |
1550 | + if (!data->booleans) { | |
1551 | + dev_err(&client->dev, "No memory to allocate boolean data\n"); | |
1552 | + goto out_sensors; | |
1553 | + } | |
1554 | + | |
1555 | + data->labels = kzalloc(sizeof(struct pmbus_label) * data->max_labels, | |
1556 | + GFP_KERNEL); | |
1557 | + if (!data->labels) { | |
1558 | + dev_err(&client->dev, "No memory to allocate label data\n"); | |
1559 | + goto out_booleans; | |
1560 | + } | |
1561 | + | |
1562 | + data->attributes = kzalloc(sizeof(struct attribute *) | |
1563 | + * data->max_attributes, GFP_KERNEL); | |
1564 | + if (!data->attributes) { | |
1565 | + dev_err(&client->dev, "No memory to allocate attribute data\n"); | |
1566 | + goto out_labels; | |
1567 | + } | |
1568 | + | |
1569 | + pmbus_find_attributes(client, data); | |
1570 | + | |
1571 | + /* | |
1572 | + * If there are no attributes, something is wrong. | |
1573 | + * Bail out instead of trying to register nothing. | |
1574 | + */ | |
1575 | + if (!data->num_attributes) { | |
1576 | + dev_err(&client->dev, "No attributes found\n"); | |
1577 | + ret = -ENODEV; | |
1578 | + goto out_attributes; | |
1579 | + } | |
1580 | + | |
1581 | + /* Register sysfs hooks */ | |
1582 | + data->group.attrs = data->attributes; | |
1583 | + ret = sysfs_create_group(&client->dev.kobj, &data->group); | |
1584 | + if (ret) { | |
1585 | + dev_err(&client->dev, "Failed to create sysfs entries\n"); | |
1586 | + goto out_attributes; | |
1587 | + } | |
1588 | + data->hwmon_dev = hwmon_device_register(&client->dev); | |
1589 | + if (IS_ERR(data->hwmon_dev)) { | |
1590 | + ret = PTR_ERR(data->hwmon_dev); | |
1591 | + dev_err(&client->dev, "Failed to register hwmon device\n"); | |
1592 | + goto out_hwmon_device_register; | |
1593 | + } | |
1594 | + return 0; | |
1595 | + | |
1596 | +out_hwmon_device_register: | |
1597 | + sysfs_remove_group(&client->dev.kobj, &data->group); | |
1598 | +out_attributes: | |
1599 | + kfree(data->attributes); | |
1600 | +out_labels: | |
1601 | + kfree(data->labels); | |
1602 | +out_booleans: | |
1603 | + kfree(data->booleans); | |
1604 | +out_sensors: | |
1605 | + kfree(data->sensors); | |
1606 | +out_data: | |
1607 | + kfree(data); | |
1608 | + return ret; | |
1609 | +} | |
1610 | +EXPORT_SYMBOL_GPL(pmbus_do_probe); | |
1611 | + | |
1612 | +int pmbus_do_remove(struct i2c_client *client) | |
1613 | +{ | |
1614 | + struct pmbus_data *data = i2c_get_clientdata(client); | |
1615 | + hwmon_device_unregister(data->hwmon_dev); | |
1616 | + sysfs_remove_group(&client->dev.kobj, &data->group); | |
1617 | + kfree(data->attributes); | |
1618 | + kfree(data->labels); | |
1619 | + kfree(data->booleans); | |
1620 | + kfree(data->sensors); | |
1621 | + kfree(data); | |
1622 | + return 0; | |
1623 | +} | |
1624 | +EXPORT_SYMBOL_GPL(pmbus_do_remove); | |
1625 | + | |
1626 | +MODULE_AUTHOR("Guenter Roeck"); | |
1627 | +MODULE_DESCRIPTION("PMBus core driver"); | |
1628 | +MODULE_LICENSE("GPL"); |
include/linux/i2c/pmbus.h
1 | +/* | |
2 | + * Hardware monitoring driver for PMBus devices | |
3 | + * | |
4 | + * Copyright (c) 2010, 2011 Ericsson AB. | |
5 | + * | |
6 | + * This program is free software; you can redistribute it and/or modify | |
7 | + * it under the terms of the GNU General Public License as published by | |
8 | + * the Free Software Foundation; either version 2 of the License, or | |
9 | + * (at your option) any later version. | |
10 | + * | |
11 | + * This program is distributed in the hope that it will be useful, | |
12 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + * GNU General Public License for more details. | |
15 | + * | |
16 | + * You should have received a copy of the GNU General Public License | |
17 | + * along with this program; if not, write to the Free Software | |
18 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | + */ | |
20 | + | |
21 | +#ifndef _PMBUS_H_ | |
22 | +#define _PMBUS_H_ | |
23 | + | |
24 | +/* flags */ | |
25 | + | |
26 | +/* | |
27 | + * PMBUS_SKIP_STATUS_CHECK | |
28 | + * | |
29 | + * During register detection, skip checking the status register for | |
30 | + * communication or command errors. | |
31 | + * | |
32 | + * Some PMBus chips respond with valid data when trying to read an unsupported | |
33 | + * register. For such chips, checking the status register is mandatory when | |
34 | + * trying to determine if a chip register exists or not. | |
35 | + * Other PMBus chips don't support the STATUS_CML register, or report | |
36 | + * communication errors for no explicable reason. For such chips, checking | |
37 | + * the status register must be disabled. | |
38 | + */ | |
39 | +#define PMBUS_SKIP_STATUS_CHECK (1 << 0) | |
40 | + | |
41 | +struct pmbus_platform_data { | |
42 | + u32 flags; /* Device specific flags */ | |
43 | +}; | |
44 | + | |
45 | +#endif /* _PMBUS_H_ */ |