1 | /* |
---|
2 | * Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org> |
---|
3 | * All rights reserved. |
---|
4 | * |
---|
5 | * The license and distribution terms for this file may be |
---|
6 | * found in the file LICENSE in this distribution or at |
---|
7 | * http://www.rtems.org/license/LICENSE. |
---|
8 | */ |
---|
9 | |
---|
10 | #if HAVE_CONFIG_H |
---|
11 | #include "config.h" |
---|
12 | #endif |
---|
13 | |
---|
14 | #include <stdio.h> |
---|
15 | #include <string.h> |
---|
16 | |
---|
17 | #include <dev/i2c/i2c.h> |
---|
18 | #include <dev/i2c/ti-lm25066a.h> |
---|
19 | |
---|
20 | /* |
---|
21 | * Commands. |
---|
22 | * |
---|
23 | * The commands are listed in Table 1 in the datasheet. These are the mapped to |
---|
24 | * command ids in the intreface. Commands have different data size, a mix of |
---|
25 | * read and write amd formats. |
---|
26 | * |
---|
27 | * The formats for values is listed in Table 41 in the datasheet. |
---|
28 | */ |
---|
29 | |
---|
30 | #define CMD_RD (1 << 0) |
---|
31 | #define CMD_WR (1 << 1) |
---|
32 | #define CMD_RW (CMD_RD | CMD_WR) |
---|
33 | #define CMD_STR (1 << 2) |
---|
34 | #define CMD_VAL_ (1 << 3) |
---|
35 | #define CMD_BLK_ (1 << 4) |
---|
36 | #define CMD_INDEX_BASE (16UL) |
---|
37 | #define CMD_INDEX_MASK (0xfUL) |
---|
38 | #define CMD_VAL(l) (CMD_VAL_ | (((uint32_t) (l)) << CMD_INDEX_BASE)) |
---|
39 | #define CMD_BLK(l) (CMD_BLK_ | (((uint32_t) (l)) << CMD_INDEX_BASE)) |
---|
40 | #define CMD_INDEX(f) ((((uint32_t) (f)) >> CMD_INDEX_BASE) & CMD_INDEX_MASK) |
---|
41 | |
---|
42 | /* |
---|
43 | * Number of bits in the ADC. |
---|
44 | */ |
---|
45 | #define ADC_BITS (12) |
---|
46 | |
---|
47 | /* |
---|
48 | * Blocks of values that can be read. |
---|
49 | */ |
---|
50 | static const ti_lm25066a_cmd block_read[] = |
---|
51 | { |
---|
52 | TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ, |
---|
53 | TI_LM25066A_MFR_READ_IIN, |
---|
54 | TI_LM25066A_READ_VOUT, |
---|
55 | TI_LM25066A_READ_VIN, |
---|
56 | TI_LM25066A_MFR_READ_PIN, |
---|
57 | TI_LM25066A_READ_TEMPERATURE_1 |
---|
58 | }; |
---|
59 | |
---|
60 | static const ti_lm25066a_cmd avg_block_read[] = |
---|
61 | { |
---|
62 | TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ, |
---|
63 | TI_LM25066A_MFR_READ_AVG_IIN, |
---|
64 | TI_LM25066A_MFR_READ_AVG_VOUT, |
---|
65 | TI_LM25066A_MFR_READ_AVG_VIN, |
---|
66 | TI_LM25066A_MFR_READ_AVG_PIN, |
---|
67 | TI_LM25066A_READ_TEMPERATURE_1 |
---|
68 | }; |
---|
69 | |
---|
70 | static const ti_lm25066a_cmd* blocks[] = |
---|
71 | { |
---|
72 | block_read, |
---|
73 | avg_block_read |
---|
74 | }; |
---|
75 | |
---|
76 | typedef struct |
---|
77 | { |
---|
78 | uint8_t cmd; |
---|
79 | uint16_t size; |
---|
80 | uint32_t flags; |
---|
81 | } ti_lm25066a_i2c_cmd; |
---|
82 | |
---|
83 | /* |
---|
84 | * Table 1. |
---|
85 | */ |
---|
86 | static const ti_lm25066a_i2c_cmd commands[] = |
---|
87 | { |
---|
88 | { 0x01, 1, CMD_RW }, /* OPERATION */ |
---|
89 | { 0x03, 0, CMD_WR }, /* CLEAR_FAULTS */ |
---|
90 | { 0x19, 1, CMD_RD }, /* CAPABILITY */ |
---|
91 | { 0x43, 2, CMD_RW | CMD_VAL(1) }, /* VOUT_UV_WARN_LIMIT */ |
---|
92 | { 0x4f, 2, CMD_RW | CMD_VAL(5) }, /* OT_FAULT_LIMIT */ |
---|
93 | { 0x51, 2, CMD_RW | CMD_VAL(5) }, /* OT_WARN_LIMIT */ |
---|
94 | { 0x57, 2, CMD_RD | CMD_VAL(0) }, /* VIN_OV_WARN_LIMIT */ |
---|
95 | { 0x58, 2, CMD_RW | CMD_VAL(0) }, /* VIN_UV_WARN_LIMIT */ |
---|
96 | { 0x78, 1, CMD_RD }, /* STATUS_BYTE */ |
---|
97 | { 0x79, 2, CMD_RD }, /* STATUS_WORD */ |
---|
98 | { 0x7a, 1, CMD_RD }, /* STATUS_VOUT */ |
---|
99 | { 0x7c, 1, CMD_RD }, /* STATUS_INPUT */ |
---|
100 | { 0x7d, 1, CMD_RD }, /* STATUS_TEMPERATURE */ |
---|
101 | { 0x7e, 1, CMD_RD }, /* STATUS_CML */ |
---|
102 | { 0x80, 1, CMD_RD }, /* STATUS_MFR_SPECIFIC */ |
---|
103 | { 0x88, 2, CMD_RD | CMD_VAL(0) }, /* READ_VIN */ |
---|
104 | { 0x8b, 2, CMD_RD | CMD_VAL(1) }, /* READ_VOUT */ |
---|
105 | { 0x8d, 2, CMD_RD | CMD_VAL(5) }, /* READ_TEMPERATURE_1 */ |
---|
106 | { 0x99, 4, CMD_RD | CMD_STR }, /* MFR_ID */ |
---|
107 | { 0x9a, 9, CMD_RD | CMD_STR }, /* MFR_MODEL */ |
---|
108 | { 0x9b, 2, CMD_RD | CMD_STR }, /* MFR_REVISION */ |
---|
109 | { 0xd0, 2, CMD_RD | CMD_VAL(2) }, /* MFR_READ_VAUX */ |
---|
110 | { 0xd1, 2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_IIN */ |
---|
111 | { 0xd2, 2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_PIN */ |
---|
112 | { 0xd3, 2, CMD_RW | CMD_VAL(3) }, /* MFR_IIN_OC_WARN_LIMIT */ |
---|
113 | { 0xd4, 2, CMD_RW | CMD_VAL(4) }, /* MFR_PIN_OP_WARN_LIMIT */ |
---|
114 | { 0xd5, 2, CMD_RD | CMD_VAL(4) }, /* MFR_PIN_PEAK */ |
---|
115 | { 0xd6, 0, CMD_WR }, /* MFR_CLEAR_PIN_PEAK */ |
---|
116 | { 0xd7, 1, CMD_RW }, /* MFR_GATE_MASK */ |
---|
117 | { 0xd8, 2, CMD_RW }, /* MFR_ALERT_MASK */ |
---|
118 | { 0xd9, 1, CMD_RD }, /* MFR_DEVICE_SETUP */ |
---|
119 | { 0xda, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLOCK_READ */ |
---|
120 | { 0xdb, 1, CMD_RW }, /* MFR_SAMPLES_FOR_AVG */ |
---|
121 | { 0xdc, 2, CMD_RD | CMD_VAL(0) }, /* MFR_READ_AVG_VIN */ |
---|
122 | { 0xdd, 2, CMD_RD | CMD_VAL(1) }, /* MFR_READ_AVG_VOUT */ |
---|
123 | { 0xde, 2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_AVG_IIN */ |
---|
124 | { 0xdf, 2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_AVG_PIN */ |
---|
125 | { 0xe0, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLACK_BOX_READ */ |
---|
126 | { 0xe1, 2, CMD_RD }, /* MFR_DIAGNOSTIC_WORD_READ */ |
---|
127 | { 0xe2, 13, CMD_RD | CMD_BLK(1) }, /* MFR_AVG_BLOCK_READ */ |
---|
128 | }; |
---|
129 | |
---|
130 | #define IO_CMDS (sizeof(commands) / sizeof(commands[0])) |
---|
131 | #define IO_INTS (6) |
---|
132 | #define IO_MESSAGE_SIZE ((sizeof(uint16_t) * IO_INTS) + 2) |
---|
133 | |
---|
134 | typedef struct { |
---|
135 | i2c_dev base; |
---|
136 | uint8_t pointer; |
---|
137 | uint16_t config_shadow; |
---|
138 | const ti_lm25066a_conversion* conversions; |
---|
139 | int scale; |
---|
140 | uint8_t buffer[IO_MESSAGE_SIZE]; |
---|
141 | } ti_lm25066a; |
---|
142 | |
---|
143 | /* |
---|
144 | * Convert a value using table 41 in the data sheet. |
---|
145 | */ |
---|
146 | static int |
---|
147 | ti_lm25066a_io_convert_to_real(ti_lm25066a* dev, uint16_t word, int conversion) |
---|
148 | { |
---|
149 | const ti_lm25066a_conversion* const con = &dev->conversions[conversion]; |
---|
150 | uint32_t u = word; |
---|
151 | int value; |
---|
152 | value = ((int) (u & ((1 << ADC_BITS) - 1))) * dev->scale; |
---|
153 | value = ((value * con->R) - con->b) / con->m; |
---|
154 | return value; |
---|
155 | } |
---|
156 | |
---|
157 | static void |
---|
158 | ti_lm25066a_io_convert_block(ti_lm25066a* dev, |
---|
159 | const uint16_t* words, |
---|
160 | int* values, |
---|
161 | int block) |
---|
162 | { |
---|
163 | const ti_lm25066a_cmd* cmds = blocks[block]; |
---|
164 | int c; |
---|
165 | /* |
---|
166 | * A block is always 6 values. |
---|
167 | */ |
---|
168 | values[0] = words[0]; |
---|
169 | for (c = 1; c < 6; ++c) { |
---|
170 | const ti_lm25066a_i2c_cmd* cmd = &commands[cmds[c]]; |
---|
171 | if ((cmd->flags & CMD_VAL_) != 0) { |
---|
172 | values[c] = |
---|
173 | ti_lm25066a_io_convert_to_real(dev, words[c], CMD_INDEX(cmd->flags)); |
---|
174 | } else { |
---|
175 | values[c] = words[c]; |
---|
176 | } |
---|
177 | } |
---|
178 | } |
---|
179 | |
---|
180 | static int |
---|
181 | ti_lm25066a_io_read(ti_lm25066a* dev, ti_lm25066a_io* io) |
---|
182 | { |
---|
183 | const ti_lm25066a_i2c_cmd* cmd; |
---|
184 | uint8_t out[1]; |
---|
185 | uint8_t* in = dev->buffer; |
---|
186 | i2c_msg msgs[2]; |
---|
187 | int err; |
---|
188 | |
---|
189 | if (io->cmd >= IO_CMDS) |
---|
190 | return -EIO; |
---|
191 | |
---|
192 | cmd = &commands[io->cmd]; |
---|
193 | |
---|
194 | if ((cmd->flags & CMD_RD) == 0) |
---|
195 | return -EIO; |
---|
196 | |
---|
197 | out[0] = cmd->cmd; |
---|
198 | msgs[0].addr = dev->base.address; |
---|
199 | msgs[0].flags = 0; |
---|
200 | msgs[0].len = (uint16_t) sizeof(out); |
---|
201 | msgs[0].buf = &out[0]; |
---|
202 | msgs[1].addr = dev->base.address; |
---|
203 | msgs[1].flags = I2C_M_RD; |
---|
204 | msgs[1].len = (uint16_t) cmd->size; |
---|
205 | msgs[1].buf = &in[0]; |
---|
206 | |
---|
207 | err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); |
---|
208 | if (err != 0) |
---|
209 | return err; |
---|
210 | |
---|
211 | err = -EIO; |
---|
212 | |
---|
213 | switch (io->type) { |
---|
214 | case TI_LM25066A_8BIT: |
---|
215 | io->data.u8 = in[0]; |
---|
216 | err = 0; |
---|
217 | break; |
---|
218 | case TI_LM25066A_16BIT: |
---|
219 | io->data.u16 = (((uint16_t) in[0]) << 8) | in[1]; |
---|
220 | err = 0; |
---|
221 | break; |
---|
222 | case TI_LM25066A_VALUE: |
---|
223 | if ((cmd->flags & CMD_VAL_) != 0) { |
---|
224 | uint16_t data = ((uint16_t) in[1]) << 8 | in[0]; |
---|
225 | io->data.value = |
---|
226 | ti_lm25066a_io_convert_to_real(dev, data, CMD_INDEX(cmd->flags)); |
---|
227 | } |
---|
228 | else { |
---|
229 | io->data.value = ((uint16_t) in[1]) << 8 | in[0];; |
---|
230 | } |
---|
231 | err = 0; |
---|
232 | break; |
---|
233 | case TI_LM25066A_VALUES: |
---|
234 | if ((cmd->flags & CMD_BLK_) != 0) { |
---|
235 | uint16_t data[in[0] / 2]; |
---|
236 | uint16_t* d = &data[0]; |
---|
237 | uint8_t* i = &in[1]; |
---|
238 | int w; |
---|
239 | for (w = 0; w < (in[0] / 2); ++w, ++d, i += 2) { |
---|
240 | *d = (((uint16_t) i[1]) << 8) | i[0] ; |
---|
241 | } |
---|
242 | ti_lm25066a_io_convert_block(dev, |
---|
243 | &data[0], |
---|
244 | &io->data.values[0], |
---|
245 | CMD_INDEX(cmd->flags)); |
---|
246 | err = 0; |
---|
247 | } |
---|
248 | break; |
---|
249 | case TI_LM25066A_STRING: |
---|
250 | memcpy(&io->data.string[0], &in[0], cmd->size); |
---|
251 | err = 0; |
---|
252 | break; |
---|
253 | case TI_LM25066A_RAW: |
---|
254 | memcpy(io->data.raw, &in[0], cmd->size); |
---|
255 | err = 0; |
---|
256 | break; |
---|
257 | default: |
---|
258 | break; |
---|
259 | } |
---|
260 | |
---|
261 | return err; |
---|
262 | } |
---|
263 | |
---|
264 | static int |
---|
265 | ti_lm25066a_io_write(ti_lm25066a* dev, ti_lm25066a_io* io) |
---|
266 | { |
---|
267 | const ti_lm25066a_i2c_cmd* cmd; |
---|
268 | uint8_t* out = dev->buffer; |
---|
269 | uint16_t length = 0; |
---|
270 | int err; |
---|
271 | |
---|
272 | if (io->cmd >= IO_CMDS) |
---|
273 | return -EIO; |
---|
274 | |
---|
275 | cmd = &commands[io->cmd]; |
---|
276 | |
---|
277 | if ((cmd->flags & CMD_WR) == 0) |
---|
278 | return -EIO; |
---|
279 | |
---|
280 | out[0] = cmd->cmd; |
---|
281 | |
---|
282 | if (cmd->size == 0) { |
---|
283 | out[1] = 0; |
---|
284 | length = 1; |
---|
285 | } |
---|
286 | else { |
---|
287 | switch (io->type) { |
---|
288 | case TI_LM25066A_8BIT: |
---|
289 | out[1] = io->data.u8; |
---|
290 | length = 1 + 1; |
---|
291 | break; |
---|
292 | case TI_LM25066A_16BIT: |
---|
293 | out[1] = io->data.u16 >> 8; |
---|
294 | out[2] = io->data.u16; |
---|
295 | length = 2 + 1; |
---|
296 | break; |
---|
297 | case TI_LM25066A_VALUE: |
---|
298 | break; |
---|
299 | case TI_LM25066A_VALUES: |
---|
300 | break; |
---|
301 | case TI_LM25066A_STRING: |
---|
302 | break; |
---|
303 | case TI_LM25066A_RAW: |
---|
304 | memcpy(&out[1], io->data.raw, cmd->size); |
---|
305 | length = cmd->size + 1; |
---|
306 | err = 0; |
---|
307 | break; |
---|
308 | default: |
---|
309 | break; |
---|
310 | } |
---|
311 | } |
---|
312 | |
---|
313 | if (length == 0) |
---|
314 | err = -EIO; |
---|
315 | else { |
---|
316 | i2c_msg msgs[1] = { |
---|
317 | { |
---|
318 | .addr = dev->base.address, |
---|
319 | .flags = 0, |
---|
320 | .len = length, |
---|
321 | .buf = &out[0] |
---|
322 | } |
---|
323 | }; |
---|
324 | err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); |
---|
325 | } |
---|
326 | |
---|
327 | return err; |
---|
328 | } |
---|
329 | |
---|
330 | static int |
---|
331 | ti_lm25066a_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg) |
---|
332 | { |
---|
333 | ti_lm25066a* dev = (ti_lm25066a*) iic_dev; |
---|
334 | ti_lm25066a_io* io = (ti_lm25066a_io*) arg; |
---|
335 | int err; |
---|
336 | |
---|
337 | switch (command) { |
---|
338 | case TI_LM25066A_GET: |
---|
339 | err = ti_lm25066a_io_read(dev, io); |
---|
340 | break; |
---|
341 | case TI_LM25066A_SET: |
---|
342 | err = ti_lm25066a_io_write(dev, io); |
---|
343 | break; |
---|
344 | default: |
---|
345 | err = -ENOTTY; |
---|
346 | break; |
---|
347 | } |
---|
348 | |
---|
349 | return err; |
---|
350 | } |
---|
351 | |
---|
352 | int |
---|
353 | i2c_dev_register_ti_lm25066a(const char* bus_path, |
---|
354 | const char* dev_path, |
---|
355 | uint16_t address, |
---|
356 | const ti_lm25066a_conversion* const conversions, |
---|
357 | const int decimal_points) |
---|
358 | { |
---|
359 | ti_lm25066a* dev; |
---|
360 | int i; |
---|
361 | |
---|
362 | dev = (ti_lm25066a*) |
---|
363 | i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address); |
---|
364 | if (dev == NULL) { |
---|
365 | return -1; |
---|
366 | } |
---|
367 | |
---|
368 | dev->base.ioctl = ti_lm25066a_ioctl; |
---|
369 | dev->pointer = -1; |
---|
370 | dev->config_shadow = 0; |
---|
371 | dev->conversions = conversions; |
---|
372 | dev->scale = 1; |
---|
373 | for (i = 0; i < decimal_points; ++i) |
---|
374 | dev->scale *= 10; |
---|
375 | |
---|
376 | return i2c_dev_register(&dev->base, dev_path); |
---|
377 | } |
---|