Commit 0c0a0615163e025eb978547969467012529e01d5
1 parent
84fc4585c1
Exists in
master
and in
7 other branches
hwmon: (pmbus) Support for TI UCD90xxx series Sequencer and System Health Controllers
Hardware monitoring support for TI UCD90120, UCD90124, UCD9090, and UCD90910 Sequencer and System Health Controllers. Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com> Reviewed-by: Tom Grennan <tom.grennan@ericsson.com>
Showing 4 changed files with 402 additions and 1 deletions Side-by-side Diff
Documentation/hwmon/ucd9000
1 | +Kernel driver ucd9000 | |
2 | +===================== | |
3 | + | |
4 | +Supported chips: | |
5 | + * TI UCD90120, UCD90124, UCD9090, and UCD90910 | |
6 | + Prefixes: 'ucd90120', 'ucd90124', 'ucd9090', 'ucd90910' | |
7 | + Addresses scanned: - | |
8 | + Datasheets: | |
9 | + http://focus.ti.com/lit/ds/symlink/ucd90120.pdf | |
10 | + http://focus.ti.com/lit/ds/symlink/ucd90124.pdf | |
11 | + http://focus.ti.com/lit/ds/symlink/ucd9090.pdf | |
12 | + http://focus.ti.com/lit/ds/symlink/ucd90910.pdf | |
13 | + | |
14 | +Author: Guenter Roeck <guenter.roeck@ericsson.com> | |
15 | + | |
16 | + | |
17 | +Description | |
18 | +----------- | |
19 | + | |
20 | +From datasheets: | |
21 | + | |
22 | +The UCD90120 Power Supply Sequencer and System Health Monitor monitors and | |
23 | +sequences up to 12 independent voltage rails. The device integrates a 12-bit | |
24 | +ADC with a 2.5V internal reference for monitoring up to 13 power supply voltage, | |
25 | +current, or temperature inputs. | |
26 | + | |
27 | +The UCD90124 is a 12-rail PMBus/I2C addressable power-supply sequencer and | |
28 | +system-health monitor. The device integrates a 12-bit ADC for monitoring up to | |
29 | +13 power-supply voltage, current, or temperature inputs. Twenty-six GPIO pins | |
30 | +can be used for power supply enables, power-on reset signals, external | |
31 | +interrupts, cascading, or other system functions. Twelve of these pins offer PWM | |
32 | +functionality. Using these pins, the UCD90124 offers support for fan control, | |
33 | +margining, and general-purpose PWM functions. | |
34 | + | |
35 | +The UCD9090 is a 10-rail PMBus/I2C addressable power-supply sequencer and | |
36 | +monitor. The device integrates a 12-bit ADC for monitoring up to 10 power-supply | |
37 | +voltage inputs. Twenty-three GPIO pins can be used for power supply enables, | |
38 | +power-on reset signals, external interrupts, cascading, or other system | |
39 | +functions. Ten of these pins offer PWM functionality. Using these pins, the | |
40 | +UCD9090 offers support for margining, and general-purpose PWM functions. | |
41 | + | |
42 | +The UCD90910 is a ten-rail I2C / PMBus addressable power-supply sequencer and | |
43 | +system-health monitor. The device integrates a 12-bit ADC for monitoring up to | |
44 | +13 power-supply voltage, current, or temperature inputs. | |
45 | + | |
46 | +This driver is a client driver to the core PMBus driver. Please see | |
47 | +Documentation/hwmon/pmbus for details on PMBus client drivers. | |
48 | + | |
49 | + | |
50 | +Usage Notes | |
51 | +----------- | |
52 | + | |
53 | +This driver does not auto-detect devices. You will have to instantiate the | |
54 | +devices explicitly. Please see Documentation/i2c/instantiating-devices for | |
55 | +details. | |
56 | + | |
57 | + | |
58 | +Platform data support | |
59 | +--------------------- | |
60 | + | |
61 | +The driver supports standard PMBus driver platform data. Please see | |
62 | +Documentation/hwmon/pmbus for details. | |
63 | + | |
64 | + | |
65 | +Sysfs entries | |
66 | +------------- | |
67 | + | |
68 | +The following attributes are supported. Limits are read-write; all other | |
69 | +attributes are read-only. | |
70 | + | |
71 | +in[1-12]_label "vout[1-12]". | |
72 | +in[1-12]_input Measured voltage. From READ_VOUT register. | |
73 | +in[1-12]_min Minumum Voltage. From VOUT_UV_WARN_LIMIT register. | |
74 | +in[1-12]_max Maximum voltage. From VOUT_OV_WARN_LIMIT register. | |
75 | +in[1-12]_lcrit Critical minumum Voltage. VOUT_UV_FAULT_LIMIT register. | |
76 | +in[1-12]_crit Critical maximum voltage. From VOUT_OV_FAULT_LIMIT register. | |
77 | +in[1-12]_min_alarm Voltage low alarm. From VOLTAGE_UV_WARNING status. | |
78 | +in[1-12]_max_alarm Voltage high alarm. From VOLTAGE_OV_WARNING status. | |
79 | +in[1-12]_lcrit_alarm Voltage critical low alarm. From VOLTAGE_UV_FAULT status. | |
80 | +in[1-12]_crit_alarm Voltage critical high alarm. From VOLTAGE_OV_FAULT status. | |
81 | + | |
82 | +curr[1-12]_label "iout[1-12]". | |
83 | +curr[1-12]_input Measured current. From READ_IOUT register. | |
84 | +curr[1-12]_max Maximum current. From IOUT_OC_WARN_LIMIT register. | |
85 | +curr[1-12]_lcrit Critical minumum output current. From IOUT_UC_FAULT_LIMIT | |
86 | + register. | |
87 | +curr[1-12]_crit Critical maximum current. From IOUT_OC_FAULT_LIMIT register. | |
88 | +curr[1-12]_max_alarm Current high alarm. From IOUT_OC_WARNING status. | |
89 | +curr[1-12]_crit_alarm Current critical high alarm. From IOUT_OC_FAULT status. | |
90 | + | |
91 | + For each attribute index, either voltage or current is | |
92 | + reported, but not both. If voltage or current is | |
93 | + reported depends on the chip configuration. | |
94 | + | |
95 | +temp[1-2]_input Measured temperatures. From READ_TEMPERATURE_1 and | |
96 | + READ_TEMPERATURE_2 registers. | |
97 | +temp[1-2]_max Maximum temperature. From OT_WARN_LIMIT register. | |
98 | +temp[1-2]_crit Critical high temperature. From OT_FAULT_LIMIT register. | |
99 | +temp[1-2]_max_alarm Temperature high alarm. | |
100 | +temp[1-2]_crit_alarm Temperature critical high alarm. | |
101 | + | |
102 | +fan[1-4]_input Fan RPM. | |
103 | +fan[1-4]_alarm Fan alarm. | |
104 | +fan[1-4]_fault Fan fault. | |
105 | + | |
106 | + Fan attributes are only available on chips supporting | |
107 | + fan control (UCD90124, UCD90910). Attribute files are | |
108 | + created only for enabled fans. | |
109 | + Note that even though UCD90910 supports up to 10 fans, | |
110 | + only up to four fans are currently supported. |
drivers/hwmon/Kconfig
... | ... | @@ -857,12 +857,24 @@ |
857 | 857 | This driver can also be built as a module. If so, the module will |
858 | 858 | be called max8688. |
859 | 859 | |
860 | +config SENSORS_UCD9000 | |
861 | + tristate "TI UCD90120, UCD90124, UCD9090, UCD90910" | |
862 | + default n | |
863 | + help | |
864 | + If you say yes here you get hardware monitoring support for TI | |
865 | + UCD90120, UCD90124, UCD9090, UCD90910 Sequencer and System Health | |
866 | + Controllers. | |
867 | + | |
868 | + This driver can also be built as a module. If so, the module will | |
869 | + be called ucd9000. | |
870 | + | |
860 | 871 | config SENSORS_UCD9200 |
861 | 872 | tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" |
862 | 873 | default n |
863 | 874 | help |
864 | 875 | If you say yes here you get hardware monitoring support for TI |
865 | - UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248. | |
876 | + UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, and UCD9248 | |
877 | + Digital PWM System Controllers. | |
866 | 878 | |
867 | 879 | This driver can also be built as a module. If so, the module will |
868 | 880 | be called ucd9200. |
drivers/hwmon/Makefile
... | ... | @@ -123,6 +123,7 @@ |
123 | 123 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o |
124 | 124 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o |
125 | 125 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o |
126 | +obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o | |
126 | 127 | obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o |
127 | 128 | |
128 | 129 | ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG |
drivers/hwmon/ucd9000.c
1 | +/* | |
2 | + * Hardware monitoring driver for UCD90xxx Sequencer and System Health | |
3 | + * Controller series | |
4 | + * | |
5 | + * Copyright (C) 2011 Ericsson AB. | |
6 | + * | |
7 | + * This program is free software; you can redistribute it and/or modify | |
8 | + * it under the terms of the GNU General Public License as published by | |
9 | + * the Free Software Foundation; either version 2 of the License, or | |
10 | + * (at your option) any later version. | |
11 | + * | |
12 | + * This program is distributed in the hope that it will be useful, | |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + * GNU General Public License for more details. | |
16 | + * | |
17 | + * You should have received a copy of the GNU General Public License | |
18 | + * along with this program; if not, write to the Free Software | |
19 | + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | + */ | |
21 | + | |
22 | +#include <linux/kernel.h> | |
23 | +#include <linux/module.h> | |
24 | +#include <linux/init.h> | |
25 | +#include <linux/err.h> | |
26 | +#include <linux/slab.h> | |
27 | +#include <linux/i2c.h> | |
28 | +#include <linux/i2c/pmbus.h> | |
29 | +#include "pmbus.h" | |
30 | + | |
31 | +enum chips { ucd9000, ucd90120, ucd90124, ucd9090, ucd90910 }; | |
32 | + | |
33 | +#define UCD9000_MONITOR_CONFIG 0xd5 | |
34 | +#define UCD9000_NUM_PAGES 0xd6 | |
35 | +#define UCD9000_FAN_CONFIG_INDEX 0xe7 | |
36 | +#define UCD9000_FAN_CONFIG 0xe8 | |
37 | +#define UCD9000_DEVICE_ID 0xfd | |
38 | + | |
39 | +#define UCD9000_MON_TYPE(x) (((x) >> 5) & 0x07) | |
40 | +#define UCD9000_MON_PAGE(x) ((x) & 0x0f) | |
41 | + | |
42 | +#define UCD9000_MON_VOLTAGE 1 | |
43 | +#define UCD9000_MON_TEMPERATURE 2 | |
44 | +#define UCD9000_MON_CURRENT 3 | |
45 | +#define UCD9000_MON_VOLTAGE_HW 4 | |
46 | + | |
47 | +#define UCD9000_NUM_FAN 4 | |
48 | + | |
49 | +struct ucd9000_data { | |
50 | + u8 fan_data[UCD9000_NUM_FAN][I2C_SMBUS_BLOCK_MAX]; | |
51 | + struct pmbus_driver_info info; | |
52 | +}; | |
53 | +#define to_ucd9000_data(_info) container_of(_info, struct ucd9000_data, info) | |
54 | + | |
55 | +static int ucd9000_get_fan_config(struct i2c_client *client, int fan) | |
56 | +{ | |
57 | + int fan_config = 0; | |
58 | + struct ucd9000_data *data | |
59 | + = to_ucd9000_data(pmbus_get_driver_info(client)); | |
60 | + | |
61 | + if (data->fan_data[fan][3] & 1) | |
62 | + fan_config |= PB_FAN_2_INSTALLED; /* Use lower bit position */ | |
63 | + | |
64 | + /* Pulses/revolution */ | |
65 | + fan_config |= (data->fan_data[fan][3] & 0x06) >> 1; | |
66 | + | |
67 | + return fan_config; | |
68 | +} | |
69 | + | |
70 | +static int ucd9000_read_byte_data(struct i2c_client *client, int page, int reg) | |
71 | +{ | |
72 | + int ret = 0; | |
73 | + int fan_config; | |
74 | + | |
75 | + switch (reg) { | |
76 | + case PMBUS_FAN_CONFIG_12: | |
77 | + if (page) | |
78 | + return -EINVAL; | |
79 | + | |
80 | + ret = ucd9000_get_fan_config(client, 0); | |
81 | + if (ret < 0) | |
82 | + return ret; | |
83 | + fan_config = ret << 4; | |
84 | + ret = ucd9000_get_fan_config(client, 1); | |
85 | + if (ret < 0) | |
86 | + return ret; | |
87 | + fan_config |= ret; | |
88 | + ret = fan_config; | |
89 | + break; | |
90 | + case PMBUS_FAN_CONFIG_34: | |
91 | + if (page) | |
92 | + return -EINVAL; | |
93 | + | |
94 | + ret = ucd9000_get_fan_config(client, 2); | |
95 | + if (ret < 0) | |
96 | + return ret; | |
97 | + fan_config = ret << 4; | |
98 | + ret = ucd9000_get_fan_config(client, 3); | |
99 | + if (ret < 0) | |
100 | + return ret; | |
101 | + fan_config |= ret; | |
102 | + ret = fan_config; | |
103 | + break; | |
104 | + default: | |
105 | + ret = -ENODATA; | |
106 | + break; | |
107 | + } | |
108 | + return ret; | |
109 | +} | |
110 | + | |
111 | +static const struct i2c_device_id ucd9000_id[] = { | |
112 | + {"ucd9000", ucd9000}, | |
113 | + {"ucd90120", ucd90120}, | |
114 | + {"ucd90124", ucd90124}, | |
115 | + {"ucd9090", ucd9090}, | |
116 | + {"ucd90910", ucd90910}, | |
117 | + {} | |
118 | +}; | |
119 | +MODULE_DEVICE_TABLE(i2c, ucd9000_id); | |
120 | + | |
121 | +static int ucd9000_probe(struct i2c_client *client, | |
122 | + const struct i2c_device_id *id) | |
123 | +{ | |
124 | + u8 block_buffer[I2C_SMBUS_BLOCK_MAX + 1]; | |
125 | + struct ucd9000_data *data; | |
126 | + struct pmbus_driver_info *info; | |
127 | + const struct i2c_device_id *mid; | |
128 | + int i, ret; | |
129 | + | |
130 | + if (!i2c_check_functionality(client->adapter, | |
131 | + I2C_FUNC_SMBUS_BYTE_DATA | | |
132 | + I2C_FUNC_SMBUS_BLOCK_DATA)) | |
133 | + return -ENODEV; | |
134 | + | |
135 | + ret = i2c_smbus_read_block_data(client, UCD9000_DEVICE_ID, | |
136 | + block_buffer); | |
137 | + if (ret < 0) { | |
138 | + dev_err(&client->dev, "Failed to read device ID\n"); | |
139 | + return ret; | |
140 | + } | |
141 | + block_buffer[ret] = '\0'; | |
142 | + dev_info(&client->dev, "Device ID %s\n", block_buffer); | |
143 | + | |
144 | + mid = NULL; | |
145 | + for (i = 0; i < ARRAY_SIZE(ucd9000_id); i++) { | |
146 | + mid = &ucd9000_id[i]; | |
147 | + if (!strncasecmp(mid->name, block_buffer, strlen(mid->name))) | |
148 | + break; | |
149 | + } | |
150 | + if (!mid || !strlen(mid->name)) { | |
151 | + dev_err(&client->dev, "Unsupported device\n"); | |
152 | + return -ENODEV; | |
153 | + } | |
154 | + | |
155 | + if (id->driver_data != ucd9000 && id->driver_data != mid->driver_data) | |
156 | + dev_notice(&client->dev, | |
157 | + "Device mismatch: Configured %s, detected %s\n", | |
158 | + id->name, mid->name); | |
159 | + | |
160 | + data = kzalloc(sizeof(struct ucd9000_data), GFP_KERNEL); | |
161 | + if (!data) | |
162 | + return -ENOMEM; | |
163 | + info = &data->info; | |
164 | + | |
165 | + ret = i2c_smbus_read_byte_data(client, UCD9000_NUM_PAGES); | |
166 | + if (ret < 0) { | |
167 | + dev_err(&client->dev, | |
168 | + "Failed to read number of active pages\n"); | |
169 | + goto out; | |
170 | + } | |
171 | + info->pages = ret; | |
172 | + if (!info->pages) { | |
173 | + dev_err(&client->dev, "No pages configured\n"); | |
174 | + ret = -ENODEV; | |
175 | + goto out; | |
176 | + } | |
177 | + | |
178 | + /* The internal temperature sensor is always active */ | |
179 | + info->func[0] = PMBUS_HAVE_TEMP; | |
180 | + | |
181 | + /* Everything else is configurable */ | |
182 | + ret = i2c_smbus_read_block_data(client, UCD9000_MONITOR_CONFIG, | |
183 | + block_buffer); | |
184 | + if (ret <= 0) { | |
185 | + dev_err(&client->dev, "Failed to read configuration data\n"); | |
186 | + ret = -ENODEV; | |
187 | + goto out; | |
188 | + } | |
189 | + for (i = 0; i < ret; i++) { | |
190 | + int page = UCD9000_MON_PAGE(block_buffer[i]); | |
191 | + | |
192 | + if (page >= info->pages) | |
193 | + continue; | |
194 | + | |
195 | + switch (UCD9000_MON_TYPE(block_buffer[i])) { | |
196 | + case UCD9000_MON_VOLTAGE: | |
197 | + case UCD9000_MON_VOLTAGE_HW: | |
198 | + info->func[page] |= PMBUS_HAVE_VOUT | |
199 | + | PMBUS_HAVE_STATUS_VOUT; | |
200 | + break; | |
201 | + case UCD9000_MON_TEMPERATURE: | |
202 | + info->func[page] |= PMBUS_HAVE_TEMP2 | |
203 | + | PMBUS_HAVE_STATUS_TEMP; | |
204 | + break; | |
205 | + case UCD9000_MON_CURRENT: | |
206 | + info->func[page] |= PMBUS_HAVE_IOUT | |
207 | + | PMBUS_HAVE_STATUS_IOUT; | |
208 | + break; | |
209 | + default: | |
210 | + break; | |
211 | + } | |
212 | + } | |
213 | + | |
214 | + /* Fan configuration */ | |
215 | + if (mid->driver_data == ucd90124) { | |
216 | + for (i = 0; i < UCD9000_NUM_FAN; i++) { | |
217 | + i2c_smbus_write_byte_data(client, | |
218 | + UCD9000_FAN_CONFIG_INDEX, i); | |
219 | + ret = i2c_smbus_read_block_data(client, | |
220 | + UCD9000_FAN_CONFIG, | |
221 | + data->fan_data[i]); | |
222 | + if (ret < 0) | |
223 | + goto out; | |
224 | + } | |
225 | + i2c_smbus_write_byte_data(client, UCD9000_FAN_CONFIG_INDEX, 0); | |
226 | + | |
227 | + info->read_byte_data = ucd9000_read_byte_data; | |
228 | + info->func[0] |= PMBUS_HAVE_FAN12 | PMBUS_HAVE_STATUS_FAN12 | |
229 | + | PMBUS_HAVE_FAN34 | PMBUS_HAVE_STATUS_FAN34; | |
230 | + } | |
231 | + | |
232 | + ret = pmbus_do_probe(client, mid, info); | |
233 | + if (ret < 0) | |
234 | + goto out; | |
235 | + return 0; | |
236 | + | |
237 | +out: | |
238 | + kfree(data); | |
239 | + return ret; | |
240 | +} | |
241 | + | |
242 | +static int ucd9000_remove(struct i2c_client *client) | |
243 | +{ | |
244 | + int ret; | |
245 | + struct ucd9000_data *data; | |
246 | + | |
247 | + data = to_ucd9000_data(pmbus_get_driver_info(client)); | |
248 | + ret = pmbus_do_remove(client); | |
249 | + kfree(data); | |
250 | + return ret; | |
251 | +} | |
252 | + | |
253 | + | |
254 | +/* This is the driver that will be inserted */ | |
255 | +static struct i2c_driver ucd9000_driver = { | |
256 | + .driver = { | |
257 | + .name = "ucd9000", | |
258 | + }, | |
259 | + .probe = ucd9000_probe, | |
260 | + .remove = ucd9000_remove, | |
261 | + .id_table = ucd9000_id, | |
262 | +}; | |
263 | + | |
264 | +static int __init ucd9000_init(void) | |
265 | +{ | |
266 | + return i2c_add_driver(&ucd9000_driver); | |
267 | +} | |
268 | + | |
269 | +static void __exit ucd9000_exit(void) | |
270 | +{ | |
271 | + i2c_del_driver(&ucd9000_driver); | |
272 | +} | |
273 | + | |
274 | +MODULE_AUTHOR("Guenter Roeck"); | |
275 | +MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx"); | |
276 | +MODULE_LICENSE("GPL"); | |
277 | +module_init(ucd9000_init); | |
278 | +module_exit(ucd9000_exit); |