diff options
author | Guenter Roeck <guenter.roeck@ericsson.com> | 2011-09-02 12:58:37 -0400 |
---|---|---|
committer | Guenter Roeck <guenter.roeck@ericsson.com> | 2011-10-24 14:09:42 -0400 |
commit | c3ff9a674c2313d4f28e38d384b18b561b313eb7 (patch) | |
tree | 6c8dea7a9c7f43e471abac7ad56e882b74cc3b8d /drivers/hwmon | |
parent | 3d790287c4e6caa8790421737b1cf8f0a6531559 (diff) |
hwmon: (pmbus/ltc2978) Explicit driver for LTC2978
Provide explicit driver for LTC2978 to enable support for minimum and peak
attributes. Remove ltc2978 chip id from generic pmbus driver.
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>
Reviewed-by: Robert Coulson <robert.coulson@ericsson.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 12 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ltc2978.c | 295 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus.c | 1 |
4 files changed, 307 insertions, 2 deletions
diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index efaf340651a0..c4dcdca25555 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig | |||
@@ -20,7 +20,7 @@ config SENSORS_PMBUS | |||
20 | help | 20 | help |
21 | If you say yes here you get hardware monitoring support for generic | 21 | If you say yes here you get hardware monitoring support for generic |
22 | PMBus devices, including but not limited to ADP4000, BMR450, BMR451, | 22 | PMBus devices, including but not limited to ADP4000, BMR450, BMR451, |
23 | BMR453, BMR454, LTC2978, NCP4200, and NCP4208. | 23 | BMR453, BMR454, NCP4200, and NCP4208. |
24 | 24 | ||
25 | This driver can also be built as a module. If so, the module will | 25 | This driver can also be built as a module. If so, the module will |
26 | be called pmbus. | 26 | be called pmbus. |
@@ -46,6 +46,16 @@ config SENSORS_LM25066 | |||
46 | This driver can also be built as a module. If so, the module will | 46 | This driver can also be built as a module. If so, the module will |
47 | be called lm25066. | 47 | be called lm25066. |
48 | 48 | ||
49 | config SENSORS_LTC2978 | ||
50 | tristate "Linear Technologies LTC2978" | ||
51 | default n | ||
52 | help | ||
53 | If you say yes here you get hardware monitoring support for Linear | ||
54 | Technology LTC2978. | ||
55 | |||
56 | This driver can also be built as a module. If so, the module will | ||
57 | be called ltc2978. | ||
58 | |||
49 | config SENSORS_MAX16064 | 59 | config SENSORS_MAX16064 |
50 | tristate "Maxim MAX16064" | 60 | tristate "Maxim MAX16064" |
51 | default n | 61 | default n |
diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index b9e4fb421f6c..789376c85dbb 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile | |||
@@ -6,6 +6,7 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o | |||
6 | obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o | 6 | obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o |
7 | obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o | 7 | obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o |
8 | obj-$(CONFIG_SENSORS_LM25066) += lm25066.o | 8 | obj-$(CONFIG_SENSORS_LM25066) += lm25066.o |
9 | obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o | ||
9 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o | 10 | obj-$(CONFIG_SENSORS_MAX16064) += max16064.o |
10 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o | 11 | obj-$(CONFIG_SENSORS_MAX34440) += max34440.o |
11 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o | 12 | obj-$(CONFIG_SENSORS_MAX8688) += max8688.o |
diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c new file mode 100644 index 000000000000..02b2e49adb31 --- /dev/null +++ b/drivers/hwmon/pmbus/ltc2978.c | |||
@@ -0,0 +1,295 @@ | |||
1 | /* | ||
2 | * Hardware monitoring driver for LTC2978 | ||
3 | * | ||
4 | * Copyright (c) 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 "pmbus.h" | ||
28 | |||
29 | enum chips { ltc2978 }; | ||
30 | |||
31 | #define LTC2978_MFR_VOUT_PEAK 0xdd | ||
32 | #define LTC2978_MFR_VIN_PEAK 0xde | ||
33 | #define LTC2978_MFR_TEMPERATURE_PEAK 0xdf | ||
34 | #define LTC2978_MFR_SPECIAL_ID 0xe7 | ||
35 | |||
36 | #define LTC2978_MFR_VOUT_MIN 0xfb | ||
37 | #define LTC2978_MFR_VIN_MIN 0xfc | ||
38 | #define LTC2978_MFR_TEMPERATURE_MIN 0xfd | ||
39 | |||
40 | #define LTC2978_ID_REV1 0x0121 | ||
41 | #define LTC2978_ID_REV2 0x0122 | ||
42 | |||
43 | /* | ||
44 | * LTC2978 clears peak data whenever the CLEAR_FAULTS command is executed, which | ||
45 | * happens pretty much each time chip data is updated. Raw peak data therefore | ||
46 | * does not provide much value. To be able to provide useful peak data, keep an | ||
47 | * internal cache of measured peak data, which is only cleared if an explicit | ||
48 | * "clear peak" command is executed for the sensor in question. | ||
49 | */ | ||
50 | struct ltc2978_data { | ||
51 | enum chips id; | ||
52 | int vin_min, vin_max; | ||
53 | int temp_min, temp_max; | ||
54 | int vout_min[8], vout_max[8]; | ||
55 | struct pmbus_driver_info info; | ||
56 | }; | ||
57 | |||
58 | #define to_ltc2978_data(x) container_of(x, struct ltc2978_data, info) | ||
59 | |||
60 | static inline int lin11_to_val(int data) | ||
61 | { | ||
62 | s16 e = ((s16)data) >> 11; | ||
63 | s32 m = (((s16)(data << 5)) >> 5); | ||
64 | |||
65 | /* | ||
66 | * mantissa is 10 bit + sign, exponent adds up to 15 bit. | ||
67 | * Add 6 bit to exponent for maximum accuracy (10 + 15 + 6 = 31). | ||
68 | */ | ||
69 | e += 6; | ||
70 | return (e < 0 ? m >> -e : m << e); | ||
71 | } | ||
72 | |||
73 | static int ltc2978_read_word_data(struct i2c_client *client, int page, int reg) | ||
74 | { | ||
75 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||
76 | struct ltc2978_data *data = to_ltc2978_data(info); | ||
77 | int ret; | ||
78 | |||
79 | switch (reg) { | ||
80 | case PMBUS_VIRT_READ_VIN_MAX: | ||
81 | ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_PEAK); | ||
82 | if (ret >= 0) { | ||
83 | if (lin11_to_val(ret) > lin11_to_val(data->vin_max)) | ||
84 | data->vin_max = ret; | ||
85 | ret = data->vin_max; | ||
86 | } | ||
87 | break; | ||
88 | case PMBUS_VIRT_READ_VOUT_MAX: | ||
89 | ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_PEAK); | ||
90 | if (ret >= 0) { | ||
91 | /* | ||
92 | * VOUT is 16 bit unsigned with fixed exponent, | ||
93 | * so we can compare it directly | ||
94 | */ | ||
95 | if (ret > data->vout_max[page]) | ||
96 | data->vout_max[page] = ret; | ||
97 | ret = data->vout_max[page]; | ||
98 | } | ||
99 | break; | ||
100 | case PMBUS_VIRT_READ_TEMP_MAX: | ||
101 | ret = pmbus_read_word_data(client, page, | ||
102 | LTC2978_MFR_TEMPERATURE_PEAK); | ||
103 | if (ret >= 0) { | ||
104 | if (lin11_to_val(ret) > lin11_to_val(data->temp_max)) | ||
105 | data->temp_max = ret; | ||
106 | ret = data->temp_max; | ||
107 | } | ||
108 | break; | ||
109 | case PMBUS_VIRT_READ_VIN_MIN: | ||
110 | ret = pmbus_read_word_data(client, page, LTC2978_MFR_VIN_MIN); | ||
111 | if (ret >= 0) { | ||
112 | if (lin11_to_val(ret) < lin11_to_val(data->vin_min)) | ||
113 | data->vin_min = ret; | ||
114 | ret = data->vin_min; | ||
115 | } | ||
116 | break; | ||
117 | case PMBUS_VIRT_READ_VOUT_MIN: | ||
118 | ret = pmbus_read_word_data(client, page, LTC2978_MFR_VOUT_MIN); | ||
119 | if (ret >= 0) { | ||
120 | /* | ||
121 | * VOUT_MIN is known to not be supported on some lots | ||
122 | * of LTC2978 revision 1, and will return the maximum | ||
123 | * possible voltage if read. If VOUT_MAX is valid and | ||
124 | * lower than the reading of VOUT_MIN, use it instead. | ||
125 | */ | ||
126 | if (data->vout_max[page] && ret > data->vout_max[page]) | ||
127 | ret = data->vout_max[page]; | ||
128 | if (ret < data->vout_min[page]) | ||
129 | data->vout_min[page] = ret; | ||
130 | ret = data->vout_min[page]; | ||
131 | } | ||
132 | break; | ||
133 | case PMBUS_VIRT_READ_TEMP_MIN: | ||
134 | ret = pmbus_read_word_data(client, page, | ||
135 | LTC2978_MFR_TEMPERATURE_MIN); | ||
136 | if (ret >= 0) { | ||
137 | if (lin11_to_val(ret) | ||
138 | < lin11_to_val(data->temp_min)) | ||
139 | data->temp_min = ret; | ||
140 | ret = data->temp_min; | ||
141 | } | ||
142 | break; | ||
143 | case PMBUS_VIRT_RESET_VOUT_HISTORY: | ||
144 | case PMBUS_VIRT_RESET_VIN_HISTORY: | ||
145 | case PMBUS_VIRT_RESET_TEMP_HISTORY: | ||
146 | ret = 0; | ||
147 | break; | ||
148 | default: | ||
149 | ret = -ENODATA; | ||
150 | break; | ||
151 | } | ||
152 | return ret; | ||
153 | } | ||
154 | |||
155 | static int ltc2978_write_word_data(struct i2c_client *client, int page, | ||
156 | int reg, u16 word) | ||
157 | { | ||
158 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||
159 | struct ltc2978_data *data = to_ltc2978_data(info); | ||
160 | int ret; | ||
161 | |||
162 | switch (reg) { | ||
163 | case PMBUS_VIRT_RESET_VOUT_HISTORY: | ||
164 | data->vout_min[page] = 0xffff; | ||
165 | data->vout_max[page] = 0; | ||
166 | ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); | ||
167 | break; | ||
168 | case PMBUS_VIRT_RESET_VIN_HISTORY: | ||
169 | data->vin_min = 0x7bff; | ||
170 | data->vin_max = 0; | ||
171 | ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); | ||
172 | break; | ||
173 | case PMBUS_VIRT_RESET_TEMP_HISTORY: | ||
174 | data->temp_min = 0x7bff; | ||
175 | data->temp_max = 0x7fff; | ||
176 | ret = pmbus_write_byte(client, page, PMBUS_CLEAR_FAULTS); | ||
177 | break; | ||
178 | default: | ||
179 | ret = -ENODATA; | ||
180 | break; | ||
181 | } | ||
182 | return ret; | ||
183 | } | ||
184 | |||
185 | static const struct i2c_device_id ltc2978_id[] = { | ||
186 | {"ltc2978", ltc2978}, | ||
187 | {} | ||
188 | }; | ||
189 | MODULE_DEVICE_TABLE(i2c, ltc2978_id); | ||
190 | |||
191 | static int ltc2978_probe(struct i2c_client *client, | ||
192 | const struct i2c_device_id *id) | ||
193 | { | ||
194 | int chip_id, ret, i; | ||
195 | struct ltc2978_data *data; | ||
196 | struct pmbus_driver_info *info; | ||
197 | |||
198 | if (!i2c_check_functionality(client->adapter, | ||
199 | I2C_FUNC_SMBUS_READ_WORD_DATA)) | ||
200 | return -ENODEV; | ||
201 | |||
202 | data = kzalloc(sizeof(struct ltc2978_data), GFP_KERNEL); | ||
203 | if (!data) | ||
204 | return -ENOMEM; | ||
205 | |||
206 | chip_id = i2c_smbus_read_word_data(client, LTC2978_MFR_SPECIAL_ID); | ||
207 | if (chip_id < 0) { | ||
208 | ret = chip_id; | ||
209 | goto err_mem; | ||
210 | } | ||
211 | |||
212 | if (chip_id == LTC2978_ID_REV1 || chip_id == LTC2978_ID_REV2) { | ||
213 | data->id = ltc2978; | ||
214 | } else { | ||
215 | dev_err(&client->dev, "Unsupported chip ID 0x%x\n", chip_id); | ||
216 | ret = -ENODEV; | ||
217 | goto err_mem; | ||
218 | } | ||
219 | if (data->id != id->driver_data) | ||
220 | dev_warn(&client->dev, | ||
221 | "Device mismatch: Configured %s, detected %s\n", | ||
222 | id->name, | ||
223 | ltc2978_id[data->id].name); | ||
224 | |||
225 | info = &data->info; | ||
226 | info->read_word_data = ltc2978_read_word_data; | ||
227 | info->write_word_data = ltc2978_write_word_data; | ||
228 | |||
229 | data->vout_min[0] = 0xffff; | ||
230 | data->vin_min = 0x7bff; | ||
231 | data->temp_min = 0x7bff; | ||
232 | data->temp_max = 0x7fff; | ||
233 | |||
234 | switch (id->driver_data) { | ||
235 | case ltc2978: | ||
236 | info->pages = 8; | ||
237 | info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_STATUS_INPUT | ||
238 | | PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | ||
239 | | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; | ||
240 | for (i = 1; i < 8; i++) { | ||
241 | info->func[i] = PMBUS_HAVE_VOUT | ||
242 | | PMBUS_HAVE_STATUS_VOUT; | ||
243 | data->vout_min[i] = 0xffff; | ||
244 | } | ||
245 | break; | ||
246 | default: | ||
247 | ret = -ENODEV; | ||
248 | goto err_mem; | ||
249 | } | ||
250 | |||
251 | ret = pmbus_do_probe(client, id, info); | ||
252 | if (ret) | ||
253 | goto err_mem; | ||
254 | return 0; | ||
255 | |||
256 | err_mem: | ||
257 | kfree(data); | ||
258 | return ret; | ||
259 | } | ||
260 | |||
261 | static int ltc2978_remove(struct i2c_client *client) | ||
262 | { | ||
263 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | ||
264 | const struct ltc2978_data *data = to_ltc2978_data(info); | ||
265 | |||
266 | pmbus_do_remove(client); | ||
267 | kfree(data); | ||
268 | return 0; | ||
269 | } | ||
270 | |||
271 | /* This is the driver that will be inserted */ | ||
272 | static struct i2c_driver ltc2978_driver = { | ||
273 | .driver = { | ||
274 | .name = "ltc2978", | ||
275 | }, | ||
276 | .probe = ltc2978_probe, | ||
277 | .remove = ltc2978_remove, | ||
278 | .id_table = ltc2978_id, | ||
279 | }; | ||
280 | |||
281 | static int __init ltc2978_init(void) | ||
282 | { | ||
283 | return i2c_add_driver(<c2978_driver); | ||
284 | } | ||
285 | |||
286 | static void __exit ltc2978_exit(void) | ||
287 | { | ||
288 | i2c_del_driver(<c2978_driver); | ||
289 | } | ||
290 | |||
291 | MODULE_AUTHOR("Guenter Roeck"); | ||
292 | MODULE_DESCRIPTION("PMBus driver for LTC2978"); | ||
293 | MODULE_LICENSE("GPL"); | ||
294 | module_init(ltc2978_init); | ||
295 | module_exit(ltc2978_exit); | ||
diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index 1dfba4477498..ef5cc1eda0f6 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c | |||
@@ -204,7 +204,6 @@ static const struct i2c_device_id pmbus_id[] = { | |||
204 | {"bmr451", 1}, | 204 | {"bmr451", 1}, |
205 | {"bmr453", 1}, | 205 | {"bmr453", 1}, |
206 | {"bmr454", 1}, | 206 | {"bmr454", 1}, |
207 | {"ltc2978", 8}, | ||
208 | {"ncp4200", 1}, | 207 | {"ncp4200", 1}, |
209 | {"ncp4208", 1}, | 208 | {"ncp4208", 1}, |
210 | {"pmbus", 0}, | 209 | {"pmbus", 0}, |