Commit | Line | Data |
---|---|---|
03e9bd8d GR |
1 | /* |
2 | * Hardware monitoring driver for LM25066 / LM5064 / LM5066 | |
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 { lm25066, lm5064, lm5066 }; | |
30 | ||
31 | #define LM25066_READ_VAUX 0xd0 | |
32 | #define LM25066_MFR_READ_IIN 0xd1 | |
33 | #define LM25066_MFR_READ_PIN 0xd2 | |
34 | #define LM25066_MFR_IIN_OC_WARN_LIMIT 0xd3 | |
35 | #define LM25066_MFR_PIN_OP_WARN_LIMIT 0xd4 | |
36 | #define LM25066_READ_PIN_PEAK 0xd5 | |
37 | #define LM25066_CLEAR_PIN_PEAK 0xd6 | |
38 | #define LM25066_DEVICE_SETUP 0xd9 | |
39 | #define LM25066_READ_AVG_VIN 0xdc | |
40 | #define LM25066_READ_AVG_VOUT 0xdd | |
41 | #define LM25066_READ_AVG_IIN 0xde | |
42 | #define LM25066_READ_AVG_PIN 0xdf | |
43 | ||
44 | #define LM25066_DEV_SETUP_CL (1 << 4) /* Current limit */ | |
45 | ||
46 | struct lm25066_data { | |
47 | int id; | |
48 | struct pmbus_driver_info info; | |
49 | }; | |
50 | ||
51 | #define to_lm25066_data(x) container_of(x, struct lm25066_data, info) | |
52 | ||
53 | static int lm25066_read_word_data(struct i2c_client *client, int page, int reg) | |
54 | { | |
55 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
56 | const struct lm25066_data *data = to_lm25066_data(info); | |
57 | int ret; | |
58 | ||
59 | if (page > 1) | |
60 | return -EINVAL; | |
61 | ||
62 | /* Map READ_VAUX into READ_VOUT register on page 1 */ | |
63 | if (page == 1) { | |
64 | switch (reg) { | |
65 | case PMBUS_READ_VOUT: | |
66 | ret = pmbus_read_word_data(client, 0, | |
67 | LM25066_READ_VAUX); | |
68 | if (ret < 0) | |
69 | break; | |
70 | /* Adjust returned value to match VOUT coefficients */ | |
71 | switch (data->id) { | |
72 | case lm25066: | |
73 | /* VOUT: 4.54 mV VAUX: 283.2 uV LSB */ | |
74 | ret = DIV_ROUND_CLOSEST(ret * 2832, 45400); | |
75 | break; | |
76 | case lm5064: | |
77 | /* VOUT: 4.53 mV VAUX: 700 uV LSB */ | |
78 | ret = DIV_ROUND_CLOSEST(ret * 70, 453); | |
79 | break; | |
80 | case lm5066: | |
81 | /* VOUT: 2.18 mV VAUX: 725 uV LSB */ | |
82 | ret = DIV_ROUND_CLOSEST(ret * 725, 2180); | |
83 | break; | |
84 | } | |
85 | break; | |
86 | default: | |
87 | /* No other valid registers on page 1 */ | |
88 | ret = -EINVAL; | |
89 | break; | |
90 | } | |
91 | goto done; | |
92 | } | |
93 | ||
94 | switch (reg) { | |
95 | case PMBUS_READ_IIN: | |
96 | ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_IIN); | |
97 | break; | |
98 | case PMBUS_READ_PIN: | |
99 | ret = pmbus_read_word_data(client, 0, LM25066_MFR_READ_PIN); | |
100 | break; | |
101 | case PMBUS_IIN_OC_WARN_LIMIT: | |
102 | ret = pmbus_read_word_data(client, 0, | |
103 | LM25066_MFR_IIN_OC_WARN_LIMIT); | |
104 | break; | |
105 | case PMBUS_PIN_OP_WARN_LIMIT: | |
106 | ret = pmbus_read_word_data(client, 0, | |
107 | LM25066_MFR_PIN_OP_WARN_LIMIT); | |
108 | break; | |
109 | case PMBUS_VIRT_READ_VIN_AVG: | |
110 | ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN); | |
111 | break; | |
112 | case PMBUS_VIRT_READ_VOUT_AVG: | |
113 | ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VOUT); | |
114 | break; | |
115 | case PMBUS_VIRT_READ_IIN_AVG: | |
116 | ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN); | |
117 | break; | |
118 | case PMBUS_VIRT_READ_PIN_AVG: | |
119 | ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN); | |
120 | break; | |
121 | case PMBUS_VIRT_READ_PIN_MAX: | |
122 | ret = pmbus_read_word_data(client, 0, LM25066_READ_PIN_PEAK); | |
123 | break; | |
124 | case PMBUS_VIRT_RESET_PIN_HISTORY: | |
125 | ret = 0; | |
126 | break; | |
127 | default: | |
128 | ret = -ENODATA; | |
129 | break; | |
130 | } | |
131 | done: | |
132 | return ret; | |
133 | } | |
134 | ||
135 | static int lm25066_write_word_data(struct i2c_client *client, int page, int reg, | |
136 | u16 word) | |
137 | { | |
138 | int ret; | |
139 | ||
140 | if (page > 1) | |
141 | return -EINVAL; | |
142 | ||
143 | switch (reg) { | |
144 | case PMBUS_IIN_OC_WARN_LIMIT: | |
145 | ret = pmbus_write_word_data(client, 0, | |
146 | LM25066_MFR_IIN_OC_WARN_LIMIT, | |
147 | word); | |
148 | break; | |
149 | case PMBUS_PIN_OP_WARN_LIMIT: | |
150 | ret = pmbus_write_word_data(client, 0, | |
151 | LM25066_MFR_PIN_OP_WARN_LIMIT, | |
152 | word); | |
153 | break; | |
154 | case PMBUS_VIRT_RESET_PIN_HISTORY: | |
155 | ret = pmbus_write_byte(client, 0, LM25066_CLEAR_PIN_PEAK); | |
156 | break; | |
157 | default: | |
158 | ret = -ENODATA; | |
159 | break; | |
160 | } | |
161 | return ret; | |
162 | } | |
163 | ||
164 | static int lm25066_probe(struct i2c_client *client, | |
165 | const struct i2c_device_id *id) | |
166 | { | |
167 | int config; | |
168 | int ret; | |
169 | struct lm25066_data *data; | |
170 | struct pmbus_driver_info *info; | |
171 | ||
172 | if (!i2c_check_functionality(client->adapter, | |
173 | I2C_FUNC_SMBUS_READ_BYTE_DATA)) | |
174 | return -ENODEV; | |
175 | ||
176 | data = kzalloc(sizeof(struct lm25066_data), GFP_KERNEL); | |
177 | if (!data) | |
178 | return -ENOMEM; | |
179 | ||
180 | config = i2c_smbus_read_byte_data(client, LM25066_DEVICE_SETUP); | |
181 | if (config < 0) { | |
182 | ret = config; | |
183 | goto err_mem; | |
184 | } | |
185 | ||
186 | data->id = id->driver_data; | |
187 | info = &data->info; | |
188 | ||
189 | info->pages = 2; | |
190 | info->format[PSC_VOLTAGE_IN] = direct; | |
191 | info->format[PSC_VOLTAGE_OUT] = direct; | |
192 | info->format[PSC_CURRENT_IN] = direct; | |
193 | info->format[PSC_TEMPERATURE] = direct; | |
194 | info->format[PSC_POWER] = direct; | |
195 | ||
196 | info->m[PSC_TEMPERATURE] = 16; | |
197 | info->b[PSC_TEMPERATURE] = 0; | |
198 | info->R[PSC_TEMPERATURE] = 0; | |
199 | ||
200 | info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | |
201 | | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_PIN | PMBUS_HAVE_IIN | |
202 | | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP; | |
203 | info->func[1] = PMBUS_HAVE_VOUT; | |
204 | ||
205 | info->read_word_data = lm25066_read_word_data; | |
206 | info->write_word_data = lm25066_write_word_data; | |
207 | ||
208 | switch (id->driver_data) { | |
209 | case lm25066: | |
210 | info->m[PSC_VOLTAGE_IN] = 22070; | |
211 | info->b[PSC_VOLTAGE_IN] = 0; | |
212 | info->R[PSC_VOLTAGE_IN] = -2; | |
213 | info->m[PSC_VOLTAGE_OUT] = 22070; | |
214 | info->b[PSC_VOLTAGE_OUT] = 0; | |
215 | info->R[PSC_VOLTAGE_OUT] = -2; | |
216 | ||
217 | if (config & LM25066_DEV_SETUP_CL) { | |
218 | info->m[PSC_CURRENT_IN] = 6852; | |
219 | info->b[PSC_CURRENT_IN] = 0; | |
220 | info->R[PSC_CURRENT_IN] = -2; | |
221 | info->m[PSC_POWER] = 369; | |
222 | info->b[PSC_POWER] = 0; | |
223 | info->R[PSC_POWER] = -2; | |
224 | } else { | |
225 | info->m[PSC_CURRENT_IN] = 13661; | |
226 | info->b[PSC_CURRENT_IN] = 0; | |
227 | info->R[PSC_CURRENT_IN] = -2; | |
228 | info->m[PSC_POWER] = 736; | |
229 | info->b[PSC_POWER] = 0; | |
230 | info->R[PSC_POWER] = -2; | |
231 | } | |
232 | break; | |
233 | case lm5064: | |
234 | info->m[PSC_VOLTAGE_IN] = 22075; | |
235 | info->b[PSC_VOLTAGE_IN] = 0; | |
236 | info->R[PSC_VOLTAGE_IN] = -2; | |
237 | info->m[PSC_VOLTAGE_OUT] = 22075; | |
238 | info->b[PSC_VOLTAGE_OUT] = 0; | |
239 | info->R[PSC_VOLTAGE_OUT] = -2; | |
240 | ||
241 | if (config & LM25066_DEV_SETUP_CL) { | |
242 | info->m[PSC_CURRENT_IN] = 6713; | |
243 | info->b[PSC_CURRENT_IN] = 0; | |
244 | info->R[PSC_CURRENT_IN] = -2; | |
245 | info->m[PSC_POWER] = 3619; | |
246 | info->b[PSC_POWER] = 0; | |
247 | info->R[PSC_POWER] = -3; | |
248 | } else { | |
249 | info->m[PSC_CURRENT_IN] = 13426; | |
250 | info->b[PSC_CURRENT_IN] = 0; | |
251 | info->R[PSC_CURRENT_IN] = -2; | |
252 | info->m[PSC_POWER] = 7238; | |
253 | info->b[PSC_POWER] = 0; | |
254 | info->R[PSC_POWER] = -3; | |
255 | } | |
256 | break; | |
257 | case lm5066: | |
258 | info->m[PSC_VOLTAGE_IN] = 4587; | |
259 | info->b[PSC_VOLTAGE_IN] = 0; | |
260 | info->R[PSC_VOLTAGE_IN] = -2; | |
261 | info->m[PSC_VOLTAGE_OUT] = 4587; | |
262 | info->b[PSC_VOLTAGE_OUT] = 0; | |
263 | info->R[PSC_VOLTAGE_OUT] = -2; | |
264 | ||
265 | if (config & LM25066_DEV_SETUP_CL) { | |
266 | info->m[PSC_CURRENT_IN] = 10753; | |
267 | info->b[PSC_CURRENT_IN] = 0; | |
268 | info->R[PSC_CURRENT_IN] = -2; | |
269 | info->m[PSC_POWER] = 1204; | |
270 | info->b[PSC_POWER] = 0; | |
271 | info->R[PSC_POWER] = -3; | |
272 | } else { | |
273 | info->m[PSC_CURRENT_IN] = 5405; | |
274 | info->b[PSC_CURRENT_IN] = 0; | |
275 | info->R[PSC_CURRENT_IN] = -2; | |
276 | info->m[PSC_POWER] = 605; | |
277 | info->b[PSC_POWER] = 0; | |
278 | info->R[PSC_POWER] = -3; | |
279 | } | |
280 | break; | |
281 | default: | |
282 | ret = -ENODEV; | |
283 | goto err_mem; | |
284 | } | |
285 | ||
286 | ret = pmbus_do_probe(client, id, info); | |
287 | if (ret) | |
288 | goto err_mem; | |
289 | return 0; | |
290 | ||
291 | err_mem: | |
292 | kfree(data); | |
293 | return ret; | |
294 | } | |
295 | ||
296 | static int lm25066_remove(struct i2c_client *client) | |
297 | { | |
298 | const struct pmbus_driver_info *info = pmbus_get_driver_info(client); | |
299 | const struct lm25066_data *data = to_lm25066_data(info); | |
300 | int ret; | |
301 | ||
302 | ret = pmbus_do_remove(client); | |
303 | kfree(data); | |
304 | return ret; | |
305 | } | |
306 | ||
307 | static const struct i2c_device_id lm25066_id[] = { | |
308 | {"lm25066", lm25066}, | |
309 | {"lm5064", lm5064}, | |
310 | {"lm5066", lm5066}, | |
311 | { } | |
312 | }; | |
313 | ||
314 | MODULE_DEVICE_TABLE(i2c, lm25066_id); | |
315 | ||
316 | /* This is the driver that will be inserted */ | |
317 | static struct i2c_driver lm25066_driver = { | |
318 | .driver = { | |
319 | .name = "lm25066", | |
320 | }, | |
321 | .probe = lm25066_probe, | |
322 | .remove = lm25066_remove, | |
323 | .id_table = lm25066_id, | |
324 | }; | |
325 | ||
326 | static int __init lm25066_init(void) | |
327 | { | |
328 | return i2c_add_driver(&lm25066_driver); | |
329 | } | |
330 | ||
331 | static void __exit lm25066_exit(void) | |
332 | { | |
333 | i2c_del_driver(&lm25066_driver); | |
334 | } | |
335 | ||
336 | MODULE_AUTHOR("Guenter Roeck"); | |
337 | MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066"); | |
338 | MODULE_LICENSE("GPL"); | |
339 | module_init(lm25066_init); | |
340 | module_exit(lm25066_exit); |