Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
lm25066.c
Go to the documentation of this file.
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 
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;
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 -ENXIO;
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,
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 = -ENXIO;
89  break;
90  }
91  goto done;
92  }
93 
94  switch (reg) {
95  case PMBUS_READ_IIN:
97  break;
98  case PMBUS_READ_PIN:
100  break;
102  ret = pmbus_read_word_data(client, 0,
104  break;
106  ret = pmbus_read_word_data(client, 0,
108  break;
110  ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_VIN);
111  break;
114  break;
116  ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_IIN);
117  break;
119  ret = pmbus_read_word_data(client, 0, LM25066_READ_AVG_PIN);
120  break;
123  break;
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 -ENXIO;
142 
143  switch (reg) {
145  ret = pmbus_write_word_data(client, 0,
147  word);
148  break;
150  ret = pmbus_write_word_data(client, 0,
152  word);
153  break;
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_write_byte(struct i2c_client *client, int page, u8 value)
165 {
166  if (page > 1)
167  return -ENXIO;
168 
169  if (page <= 0)
170  return pmbus_write_byte(client, page, value);
171 
172  return 0;
173 }
174 
175 static int lm25066_probe(struct i2c_client *client,
176  const struct i2c_device_id *id)
177 {
178  int config;
179  struct lm25066_data *data;
180  struct pmbus_driver_info *info;
181 
182  if (!i2c_check_functionality(client->adapter,
184  return -ENODEV;
185 
186  data = devm_kzalloc(&client->dev, sizeof(struct lm25066_data),
187  GFP_KERNEL);
188  if (!data)
189  return -ENOMEM;
190 
192  if (config < 0)
193  return config;
194 
195  data->id = id->driver_data;
196  info = &data->info;
197 
198  info->pages = 2;
199  info->format[PSC_VOLTAGE_IN] = direct;
200  info->format[PSC_VOLTAGE_OUT] = direct;
201  info->format[PSC_CURRENT_IN] = direct;
202  info->format[PSC_TEMPERATURE] = direct;
203  info->format[PSC_POWER] = direct;
204 
205  info->m[PSC_TEMPERATURE] = 16;
206  info->b[PSC_TEMPERATURE] = 0;
207  info->R[PSC_TEMPERATURE] = 0;
208 
209  info->func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT
212  info->func[1] = PMBUS_HAVE_VOUT;
213 
214  info->read_word_data = lm25066_read_word_data;
215  info->write_word_data = lm25066_write_word_data;
216  info->write_byte = lm25066_write_byte;
217 
218  switch (id->driver_data) {
219  case lm25066:
220  info->m[PSC_VOLTAGE_IN] = 22070;
221  info->b[PSC_VOLTAGE_IN] = 0;
222  info->R[PSC_VOLTAGE_IN] = -2;
223  info->m[PSC_VOLTAGE_OUT] = 22070;
224  info->b[PSC_VOLTAGE_OUT] = 0;
225  info->R[PSC_VOLTAGE_OUT] = -2;
226 
227  if (config & LM25066_DEV_SETUP_CL) {
228  info->m[PSC_CURRENT_IN] = 6852;
229  info->b[PSC_CURRENT_IN] = 0;
230  info->R[PSC_CURRENT_IN] = -2;
231  info->m[PSC_POWER] = 369;
232  info->b[PSC_POWER] = 0;
233  info->R[PSC_POWER] = -2;
234  } else {
235  info->m[PSC_CURRENT_IN] = 13661;
236  info->b[PSC_CURRENT_IN] = 0;
237  info->R[PSC_CURRENT_IN] = -2;
238  info->m[PSC_POWER] = 736;
239  info->b[PSC_POWER] = 0;
240  info->R[PSC_POWER] = -2;
241  }
242  break;
243  case lm5064:
244  info->m[PSC_VOLTAGE_IN] = 22075;
245  info->b[PSC_VOLTAGE_IN] = 0;
246  info->R[PSC_VOLTAGE_IN] = -2;
247  info->m[PSC_VOLTAGE_OUT] = 22075;
248  info->b[PSC_VOLTAGE_OUT] = 0;
249  info->R[PSC_VOLTAGE_OUT] = -2;
250 
251  if (config & LM25066_DEV_SETUP_CL) {
252  info->m[PSC_CURRENT_IN] = 6713;
253  info->b[PSC_CURRENT_IN] = 0;
254  info->R[PSC_CURRENT_IN] = -2;
255  info->m[PSC_POWER] = 3619;
256  info->b[PSC_POWER] = 0;
257  info->R[PSC_POWER] = -3;
258  } else {
259  info->m[PSC_CURRENT_IN] = 13426;
260  info->b[PSC_CURRENT_IN] = 0;
261  info->R[PSC_CURRENT_IN] = -2;
262  info->m[PSC_POWER] = 7238;
263  info->b[PSC_POWER] = 0;
264  info->R[PSC_POWER] = -3;
265  }
266  break;
267  case lm5066:
268  info->m[PSC_VOLTAGE_IN] = 4587;
269  info->b[PSC_VOLTAGE_IN] = 0;
270  info->R[PSC_VOLTAGE_IN] = -2;
271  info->m[PSC_VOLTAGE_OUT] = 4587;
272  info->b[PSC_VOLTAGE_OUT] = 0;
273  info->R[PSC_VOLTAGE_OUT] = -2;
274 
275  if (config & LM25066_DEV_SETUP_CL) {
276  info->m[PSC_CURRENT_IN] = 10753;
277  info->b[PSC_CURRENT_IN] = 0;
278  info->R[PSC_CURRENT_IN] = -2;
279  info->m[PSC_POWER] = 1204;
280  info->b[PSC_POWER] = 0;
281  info->R[PSC_POWER] = -3;
282  } else {
283  info->m[PSC_CURRENT_IN] = 5405;
284  info->b[PSC_CURRENT_IN] = 0;
285  info->R[PSC_CURRENT_IN] = -2;
286  info->m[PSC_POWER] = 605;
287  info->b[PSC_POWER] = 0;
288  info->R[PSC_POWER] = -3;
289  }
290  break;
291  default:
292  return -ENODEV;
293  }
294 
295  return pmbus_do_probe(client, id, info);
296 }
297 
298 static const struct i2c_device_id lm25066_id[] = {
299  {"lm25066", lm25066},
300  {"lm5064", lm5064},
301  {"lm5066", lm5066},
302  { }
303 };
304 
305 MODULE_DEVICE_TABLE(i2c, lm25066_id);
306 
307 /* This is the driver that will be inserted */
308 static struct i2c_driver lm25066_driver = {
309  .driver = {
310  .name = "lm25066",
311  },
312  .probe = lm25066_probe,
313  .remove = pmbus_do_remove,
314  .id_table = lm25066_id,
315 };
316 
317 module_i2c_driver(lm25066_driver);
318 
319 MODULE_AUTHOR("Guenter Roeck");
320 MODULE_DESCRIPTION("PMBus driver for LM25066/LM5064/LM5066");
321 MODULE_LICENSE("GPL");