Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:11

0001 /*
0002  * Copyright (c) 2016-2017 Chris Johns <chrisj@rtems.org>
0003  * All rights reserved.
0004  *
0005  * The license and distribution terms for this file may be
0006  * found in the file LICENSE in this distribution or at
0007  * http://www.rtems.org/license/LICENSE.
0008  */
0009 
0010 #ifdef HAVE_CONFIG_H
0011 #include "config.h"
0012 #endif
0013 
0014 #include <stdio.h>
0015 #include <string.h>
0016 
0017 #include <dev/i2c/i2c.h>
0018 #include <dev/i2c/ti-lm25066a.h>
0019 
0020 /*
0021  * Commands.
0022  *
0023  * The commands are listed in Table 1 in the datasheet. These are the mapped to
0024  * command ids in the intreface. Commands have different data size, a mix of
0025  * read and write amd formats.
0026  *
0027  * The formats for values is listed in Table 41 in the datasheet.
0028  */
0029 
0030 #define CMD_RD         (1 << 0)
0031 #define CMD_WR         (1 << 1)
0032 #define CMD_RW         (CMD_RD | CMD_WR)
0033 #define CMD_STR        (1 << 2)
0034 #define CMD_VAL_       (1 << 3)
0035 #define CMD_BLK_       (1 << 4)
0036 #define CMD_INDEX_BASE (16UL)
0037 #define CMD_INDEX_MASK (0xfUL)
0038 #define CMD_VAL(l)     (CMD_VAL_ | (((uint32_t) (l)) << CMD_INDEX_BASE))
0039 #define CMD_BLK(l)     (CMD_BLK_ | (((uint32_t) (l)) << CMD_INDEX_BASE))
0040 #define CMD_INDEX(f)   ((((uint32_t) (f)) >> CMD_INDEX_BASE) & CMD_INDEX_MASK)
0041 
0042 /*
0043  * Number of bits in the ADC.
0044  */
0045 #define ADC_BITS (12)
0046 
0047 /*
0048  * Blocks of values that can be read.
0049  */
0050 static const ti_lm25066a_cmd block_read[] =
0051 {
0052   TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ,
0053   TI_LM25066A_MFR_READ_IIN,
0054   TI_LM25066A_READ_VOUT,
0055   TI_LM25066A_READ_VIN,
0056   TI_LM25066A_MFR_READ_PIN,
0057   TI_LM25066A_READ_TEMPERATURE_1
0058 };
0059 
0060 static const ti_lm25066a_cmd avg_block_read[] =
0061 {
0062   TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ,
0063   TI_LM25066A_MFR_READ_AVG_IIN,
0064   TI_LM25066A_MFR_READ_AVG_VOUT,
0065   TI_LM25066A_MFR_READ_AVG_VIN,
0066   TI_LM25066A_MFR_READ_AVG_PIN,
0067   TI_LM25066A_READ_TEMPERATURE_1
0068 };
0069 
0070 static const ti_lm25066a_cmd* blocks[] =
0071 {
0072   block_read,
0073   avg_block_read
0074 };
0075 
0076 typedef struct
0077 {
0078   uint8_t  cmd;
0079   uint16_t size;
0080   uint32_t flags;
0081 } ti_lm25066a_i2c_cmd;
0082 
0083 /*
0084  * Table 1.
0085  */
0086 static const ti_lm25066a_i2c_cmd commands[] =
0087 {
0088   { 0x01,  1, CMD_RW              }, /* OPERATION */
0089   { 0x03,  0, CMD_WR              }, /* CLEAR_FAULTS */
0090   { 0x19,  1, CMD_RD              }, /* CAPABILITY */
0091   { 0x43,  2, CMD_RW | CMD_VAL(1) }, /* VOUT_UV_WARN_LIMIT */
0092   { 0x4f,  2, CMD_RW | CMD_VAL(5) }, /* OT_FAULT_LIMIT */
0093   { 0x51,  2, CMD_RW | CMD_VAL(5) }, /* OT_WARN_LIMIT */
0094   { 0x57,  2, CMD_RD | CMD_VAL(0) }, /* VIN_OV_WARN_LIMIT */
0095   { 0x58,  2, CMD_RW | CMD_VAL(0) }, /* VIN_UV_WARN_LIMIT */
0096   { 0x78,  1, CMD_RD              }, /* STATUS_BYTE */
0097   { 0x79,  2, CMD_RD              }, /* STATUS_WORD */
0098   { 0x7a,  1, CMD_RD              }, /* STATUS_VOUT */
0099   { 0x7c,  1, CMD_RD              }, /* STATUS_INPUT */
0100   { 0x7d,  1, CMD_RD              }, /* STATUS_TEMPERATURE */
0101   { 0x7e,  1, CMD_RD              }, /* STATUS_CML */
0102   { 0x80,  1, CMD_RD              }, /* STATUS_MFR_SPECIFIC */
0103   { 0x88,  2, CMD_RD | CMD_VAL(0) }, /* READ_VIN */
0104   { 0x8b,  2, CMD_RD | CMD_VAL(1) }, /* READ_VOUT */
0105   { 0x8d,  2, CMD_RD | CMD_VAL(5) }, /* READ_TEMPERATURE_1 */
0106   { 0x99,  4, CMD_RD | CMD_STR    }, /* MFR_ID */
0107   { 0x9a,  9, CMD_RD | CMD_STR    }, /* MFR_MODEL */
0108   { 0x9b,  2, CMD_RD | CMD_STR    }, /* MFR_REVISION */
0109   { 0xd0,  2, CMD_RD | CMD_VAL(2) }, /* MFR_READ_VAUX */
0110   { 0xd1,  2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_IIN */
0111   { 0xd2,  2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_PIN */
0112   { 0xd3,  2, CMD_RW | CMD_VAL(3) }, /* MFR_IIN_OC_WARN_LIMIT */
0113   { 0xd4,  2, CMD_RW | CMD_VAL(4) }, /* MFR_PIN_OP_WARN_LIMIT */
0114   { 0xd5,  2, CMD_RD | CMD_VAL(4) }, /* MFR_PIN_PEAK */
0115   { 0xd6,  0, CMD_WR              }, /* MFR_CLEAR_PIN_PEAK */
0116   { 0xd7,  1, CMD_RW              }, /* MFR_GATE_MASK */
0117   { 0xd8,  2, CMD_RW              }, /* MFR_ALERT_MASK */
0118   { 0xd9,  1, CMD_RD              }, /* MFR_DEVICE_SETUP */
0119   { 0xda, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLOCK_READ */
0120   { 0xdb,  1, CMD_RW              }, /* MFR_SAMPLES_FOR_AVG */
0121   { 0xdc,  2, CMD_RD | CMD_VAL(0) }, /* MFR_READ_AVG_VIN */
0122   { 0xdd,  2, CMD_RD | CMD_VAL(1) }, /* MFR_READ_AVG_VOUT */
0123   { 0xde,  2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_AVG_IIN */
0124   { 0xdf,  2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_AVG_PIN */
0125   { 0xe0, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLACK_BOX_READ */
0126   { 0xe1,  2, CMD_RD              }, /* MFR_DIAGNOSTIC_WORD_READ */
0127   { 0xe2, 13, CMD_RD | CMD_BLK(1) }, /* MFR_AVG_BLOCK_READ */
0128 };
0129 
0130 #define IO_CMDS          (sizeof(commands) / sizeof(commands[0]))
0131 #define IO_INTS          (6)
0132 #define IO_MESSAGE_SIZE  ((sizeof(uint16_t) * IO_INTS) + 2)
0133 
0134 typedef struct {
0135   i2c_dev                       base;
0136   uint8_t                       pointer;
0137   uint16_t                      config_shadow;
0138   const ti_lm25066a_conversion* conversions;
0139   int                           scale;
0140   uint8_t                       buffer[IO_MESSAGE_SIZE];
0141 } ti_lm25066a;
0142 
0143 /*
0144  * Convert a value using table 41 in the data sheet.
0145  */
0146 static int
0147 ti_lm25066a_io_convert_to_real(ti_lm25066a* dev, uint16_t word, int conversion)
0148 {
0149   const ti_lm25066a_conversion* const con = &dev->conversions[conversion];
0150   uint32_t                            u = word;
0151   int                                 value;
0152   value = ((int) (u & ((1 << ADC_BITS) - 1))) * dev->scale;
0153   value = ((value * con->R) - con->b) / con->m;
0154   return value;
0155 }
0156 
0157 static void
0158 ti_lm25066a_io_convert_block(ti_lm25066a*    dev,
0159                              const uint16_t* words,
0160                              int*            values,
0161                              int             block)
0162 {
0163   const ti_lm25066a_cmd* cmds = blocks[block];
0164   int                    c;
0165   /*
0166    * A block is always 6 values.
0167    */
0168   values[0] = words[0];
0169   for (c = 1; c < 6; ++c) {
0170     const ti_lm25066a_i2c_cmd* cmd = &commands[cmds[c]];
0171     if ((cmd->flags & CMD_VAL_) != 0) {
0172       values[c] =
0173         ti_lm25066a_io_convert_to_real(dev, words[c], CMD_INDEX(cmd->flags));
0174     } else {
0175       values[c] = words[c];
0176     }
0177   }
0178 }
0179 
0180 static int
0181 ti_lm25066a_io_read(ti_lm25066a* dev, ti_lm25066a_io* io)
0182 {
0183   const ti_lm25066a_i2c_cmd* cmd;
0184   uint8_t                    out[1];
0185   uint8_t*                   in = dev->buffer;
0186   i2c_msg                    msgs[2];
0187   int                        err;
0188 
0189   if (io->cmd >= IO_CMDS)
0190     return -EIO;
0191 
0192   cmd = &commands[io->cmd];
0193 
0194   if ((cmd->flags & CMD_RD) == 0)
0195     return -EIO;
0196 
0197   out[0] = cmd->cmd;
0198   msgs[0].addr = dev->base.address;
0199   msgs[0].flags = 0;
0200   msgs[0].len = (uint16_t) sizeof(out);
0201   msgs[0].buf = &out[0];
0202   msgs[1].addr = dev->base.address;
0203   msgs[1].flags = I2C_M_RD;
0204   msgs[1].len = (uint16_t) cmd->size;
0205   msgs[1].buf = &in[0];
0206 
0207   err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
0208   if (err != 0)
0209     return err;
0210 
0211   err = -EIO;
0212 
0213   switch (io->type) {
0214     case TI_LM25066A_8BIT:
0215       io->data.u8 = in[0];
0216       err = 0;
0217       break;
0218     case TI_LM25066A_16BIT:
0219       io->data.u16 = (((uint16_t) in[0]) << 8) | in[1];
0220       err = 0;
0221       break;
0222     case TI_LM25066A_VALUE:
0223       if ((cmd->flags & CMD_VAL_) != 0) {
0224         uint16_t data = ((uint16_t) in[1]) << 8 | in[0];
0225         io->data.value =
0226           ti_lm25066a_io_convert_to_real(dev, data, CMD_INDEX(cmd->flags));
0227       }
0228       else {
0229         io->data.value = ((uint16_t) in[1]) << 8 | in[0];;
0230       }
0231       err = 0;
0232       break;
0233     case TI_LM25066A_VALUES:
0234       if ((cmd->flags & CMD_BLK_) != 0) {
0235         uint16_t  data[in[0] / 2];
0236         uint16_t* d = &data[0];
0237         uint8_t*  i = &in[1];
0238         int       w;
0239         for (w = 0; w < (in[0] / 2); ++w, ++d, i += 2) {
0240           *d = (((uint16_t) i[1]) << 8) | i[0] ;
0241         }
0242         ti_lm25066a_io_convert_block(dev,
0243                                      &data[0],
0244                                      &io->data.values[0],
0245                                      CMD_INDEX(cmd->flags));
0246         err = 0;
0247       }
0248       break;
0249     case TI_LM25066A_STRING:
0250       memcpy(&io->data.string[0], &in[0], cmd->size);
0251       err = 0;
0252       break;
0253     case TI_LM25066A_RAW:
0254       memcpy(io->data.raw, &in[0], cmd->size);
0255       err = 0;
0256       break;
0257     default:
0258       break;
0259   }
0260 
0261   return err;
0262 }
0263 
0264 static int
0265 ti_lm25066a_io_write(ti_lm25066a* dev, ti_lm25066a_io* io)
0266 {
0267   const ti_lm25066a_i2c_cmd* cmd;
0268   uint8_t*                   out = dev->buffer;
0269   uint16_t                   length = 0;
0270   int                        err;
0271 
0272   if (io->cmd >= IO_CMDS)
0273     return -EIO;
0274 
0275   cmd = &commands[io->cmd];
0276 
0277   if ((cmd->flags & CMD_WR) == 0)
0278     return -EIO;
0279 
0280   out[0] = cmd->cmd;
0281 
0282   if (cmd->size == 0) {
0283     out[1] = 0;
0284     length = 1;
0285   }
0286   else {
0287     switch (io->type) {
0288       case TI_LM25066A_8BIT:
0289         out[1] = io->data.u8;
0290         length = 1 + 1;
0291         break;
0292       case TI_LM25066A_16BIT:
0293         out[1] = io->data.u16 >> 8;
0294         out[2] = io->data.u16;
0295         length = 2 + 1;
0296         break;
0297       case TI_LM25066A_VALUE:
0298         break;
0299       case TI_LM25066A_VALUES:
0300         break;
0301       case TI_LM25066A_STRING:
0302         break;
0303       case TI_LM25066A_RAW:
0304         memcpy(&out[1], io->data.raw, cmd->size);
0305         length = cmd->size + 1;
0306         err = 0;
0307       break;
0308       default:
0309         break;
0310     }
0311   }
0312 
0313   if (length == 0)
0314     err = -EIO;
0315   else {
0316     i2c_msg msgs[1] = {
0317       {
0318         .addr = dev->base.address,
0319         .flags = 0,
0320         .len = length,
0321         .buf = &out[0]
0322       }
0323     };
0324     err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs));
0325   }
0326 
0327   return err;
0328 }
0329 
0330 static int
0331 ti_lm25066a_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg)
0332 {
0333   ti_lm25066a*    dev = (ti_lm25066a*) iic_dev;
0334   ti_lm25066a_io* io = (ti_lm25066a_io*) arg;
0335   int        err;
0336 
0337   switch (command) {
0338     case TI_LM25066A_GET:
0339       err = ti_lm25066a_io_read(dev, io);
0340       break;
0341     case TI_LM25066A_SET:
0342       err = ti_lm25066a_io_write(dev, io);
0343       break;
0344     default:
0345       err = -ENOTTY;
0346       break;
0347   }
0348 
0349   return err;
0350 }
0351 
0352 int
0353 i2c_dev_register_ti_lm25066a(const char*                         bus_path,
0354                              const char*                         dev_path,
0355                              uint16_t                            address,
0356                              const ti_lm25066a_conversion* const conversions,
0357                              const int                           decimal_points)
0358 {
0359   ti_lm25066a* dev;
0360   int          i;
0361 
0362   dev = (ti_lm25066a*)
0363     i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address);
0364   if (dev == NULL) {
0365     return -1;
0366   }
0367 
0368   dev->base.ioctl = ti_lm25066a_ioctl;
0369   dev->pointer = -1;
0370   dev->config_shadow = 0;
0371   dev->conversions = conversions;
0372   dev->scale = 1;
0373   for (i = 0; i < decimal_points; ++i)
0374     dev->scale *= 10;
0375 
0376   return i2c_dev_register(&dev->base, dev_path);
0377 }