aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorGuenter Roeck <guenter.roeck@ericsson.com>2011-03-09 01:01:39 -0500
committerGuenter Roeck <guenter.roeck@ericsson.com>2011-05-19 11:19:40 -0400
commit0c0a0615163e025eb978547969467012529e01d5 (patch)
tree5a30c5a7ba79426be50c8bb573fe3784badc1aa4 /drivers
parent84fc4585c152f34a1a90a03b7abb88a2e1218a42 (diff)
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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/Kconfig14
-rw-r--r--drivers/hwmon/Makefile1
-rw-r--r--drivers/hwmon/ucd9000.c278
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
860config 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
860config SENSORS_UCD9200 871config 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
123obj-$(CONFIG_SENSORS_MAX16064) += max16064.o 123obj-$(CONFIG_SENSORS_MAX16064) += max16064.o
124obj-$(CONFIG_SENSORS_MAX34440) += max34440.o 124obj-$(CONFIG_SENSORS_MAX34440) += max34440.o
125obj-$(CONFIG_SENSORS_MAX8688) += max8688.o 125obj-$(CONFIG_SENSORS_MAX8688) += max8688.o
126obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o
126obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o 127obj-$(CONFIG_SENSORS_UCD9200) += ucd9200.o
127 128
128ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG 129ccflags-$(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
31enum 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
49struct 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
55static 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
70static 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
111static const struct i2c_device_id ucd9000_id[] = {
112 {"ucd9000", ucd9000},
113 {"ucd90120", ucd90120},
114 {"ucd90124", ucd90124},
115 {"ucd9090", ucd9090},
116 {"ucd90910", ucd90910},
117 {}
118};
119MODULE_DEVICE_TABLE(i2c, ucd9000_id);
120
121static 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
237out:
238 kfree(data);
239 return ret;
240}
241
242static 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 */
255static 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
264static int __init ucd9000_init(void)
265{
266 return i2c_add_driver(&ucd9000_driver);
267}
268
269static void __exit ucd9000_exit(void)
270{
271 i2c_del_driver(&ucd9000_driver);
272}
273
274MODULE_AUTHOR("Guenter Roeck");
275MODULE_DESCRIPTION("PMBus driver for TI UCD90xxx");
276MODULE_LICENSE("GPL");
277module_init(ucd9000_init);
278module_exit(ucd9000_exit);