diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 14 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/ucd9000.c | 278 |
3 files changed, 292 insertions, 1 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 07c4a860dffe..d00fd39f6ee3 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig | |||
@@ -857,12 +857,24 @@ config SENSORS_MAX8688 | |||
857 | This driver can also be built as a module. If so, the module will | 857 | This driver can also be built as a module. If so, the module will |
858 | be called max8688. | 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 | config SENSORS_UCD9200 | 871 | config SENSORS_UCD9200 |
861 | tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" | 872 | tristate "TI UCD9220, UCD9222, UCD9224, UCD9240, UCD9244, UCD9246, UCD9248" |
862 | default n | 873 | default n |
863 | help | 874 | help |
864 | If you say yes here you get hardware monitoring support for TI | 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 | This driver can also be built as a module. If so, the module will | 879 | This driver can also be built as a module. If so, the module will |
868 | be called ucd9200. | 880 | be called ucd9200. |
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 1afe02961dec..c4a269b06925 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile | |||
@@ -123,6 +123,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o | |||
123 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o | 123 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o |
124 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o | 124 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o |
125 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o | 125 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o |
126 | obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o | ||
126 | obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o | 127 | obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o |
127 | 128 | ||
128 | ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG | 129 | ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG |
diff --git a/drivers/hwmon/ucd9000.c b/drivers/hwmon/ucd9000.c new file mode 100644 index 000000000000..ace1c7319734 --- /dev/null +++ b/drivers/hwmon/ucd9000.c | |||
@@ -0,0 +1,278 @@ | |||
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); | ||