From 849500d0f226e9aa81cbfddf7c09c0f83a7568f9 Mon Sep 17 00:00:00 2001 From: Chris Johns Date: Wed, 16 Aug 2017 15:10:33 +1000 Subject: dev/i2c: Add I2C device support for FPGA Slave, LM25066A, TMP112, ADS1113, ADS1114 and ADS1115 Closes #3101. --- cpukit/dev/i2c/fpga-i2c-slave.c | 132 +++++++++++++ cpukit/dev/i2c/ti-ads-16bit-adc.c | 239 ++++++++++++++++++++++++ cpukit/dev/i2c/ti-lm25066a.c | 376 ++++++++++++++++++++++++++++++++++++++ cpukit/dev/i2c/ti-tmp112.c | 229 +++++++++++++++++++++++ 4 files changed, 976 insertions(+) create mode 100644 cpukit/dev/i2c/fpga-i2c-slave.c create mode 100644 cpukit/dev/i2c/ti-ads-16bit-adc.c create mode 100644 cpukit/dev/i2c/ti-lm25066a.c create mode 100644 cpukit/dev/i2c/ti-tmp112.c (limited to 'cpukit/dev/i2c') diff --git a/cpukit/dev/i2c/fpga-i2c-slave.c b/cpukit/dev/i2c/fpga-i2c-slave.c new file mode 100644 index 0000000000..cf00a57b41 --- /dev/null +++ b/cpukit/dev/i2c/fpga-i2c-slave.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2016-2017 Chris Johns + * All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +/* + * I2C slave for testing: + * https://github.com/oetr/FPGA-I2C-Slave + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include +#include + +typedef struct { + i2c_dev base; + uint16_t address; + size_t size; +} fpga_i2c_slave; + +static ssize_t +fpga_i2c_slave_read(i2c_dev* base, void* buf, size_t n, off_t offset) +{ + fpga_i2c_slave* dev = (fpga_i2c_slave*) base; + off_t avail = dev->size - offset; + uint8_t* in = buf; + size_t todo; + + if (avail <= 0) { + return 0; + } + + if (n > avail) { + n = (size_t) avail; + } + + todo = n; + + while (todo > 0) { + /* + * Limit the transfer size so that it can be stored in 8-bits. This may + * help some bus controllers. + */ + uint16_t cur = (uint16_t) (todo < 255 ? todo : 255); + i2c_msg msgs = { + .addr = dev->base.address, + .flags = I2C_M_RD, + .buf = in, + .len = cur + }; + int err; + err = i2c_bus_transfer(dev->base.bus, &msgs, 1); + if (err != 0) { + return err; + } + todo -= cur; + in += cur; + } + + return (ssize_t) n; +} + +static ssize_t +fpga_i2c_slave_write(i2c_dev* base, const void* buf, size_t n, off_t offset) +{ + fpga_i2c_slave* dev = (fpga_i2c_slave*) base; + off_t avail = dev->size - offset; + const uint8_t* out = buf; + size_t todo; + + if (avail <= 0) { + return 0; + } + + if (n > avail) { + n = (size_t) avail; + } + + todo = n; + + while (todo > 0) { + /* + * Limit the transfer size so that it can be stored in 8-bits. This may + * help some bus controllers. + */ + uint16_t cur = (uint16_t) (todo < 255 ? todo : 255); + i2c_msg msgs = { + .addr = dev->base.address, + .flags = 0, + .buf = RTEMS_DECONST(uint8_t*, out), + .len = cur + }; + int err; + err = i2c_bus_transfer(dev->base.bus, &msgs, 1); + if (err != 0) { + return err; + } + todo -= cur; + out += cur; + } + + return (ssize_t) n; +} + +int +i2c_dev_register_fpga_i2c_slave(const char* bus_path, + const char* dev_path, + uint16_t address, + size_t size) + +{ + fpga_i2c_slave* dev; + + dev = (fpga_i2c_slave*) + i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address); + if (dev == NULL) { + return -1; + } + + dev->base.read = fpga_i2c_slave_read; + dev->base.write = fpga_i2c_slave_write; + dev->size = size; + + return i2c_dev_register(&dev->base, dev_path); +} diff --git a/cpukit/dev/i2c/ti-ads-16bit-adc.c b/cpukit/dev/i2c/ti-ads-16bit-adc.c new file mode 100644 index 0000000000..35e6a7eb63 --- /dev/null +++ b/cpukit/dev/i2c/ti-ads-16bit-adc.c @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2016-2016 Chris Johns + * All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include +#include + +/* + * Registers. + */ +#define ADS_CONVERSION (0) +#define ADS_CONFIG (1) +#define ADS_LO_THRESH (2) +#define ADS_HI_THRESH (3) + +/* + * Configuration register. + */ +#define CFG_OS_NOT_CONVERTING (1 << 15) /* read */ +#define CFG_OS_START_SSHOT (1 << 15) /* write */ +#define CFG_MUX_BASE (12) +#define CFG_MUX_MASK (7) +#define CFG_PGA_BASE (9) +#define CFG_PGA_MASK (7) +#define CFG_MODE_BASE (8) +#define CFG_MODE_MASK (1) +#define CFG_DATA_RATE_BASE (5) +#define CFG_DATA_RATE_MASK (7) +#define CFG_COMP_BASE (0) +#define CFG_COMP_MASK (0x1f) +#define CFG_COMP_MODE_ACTIVE_LOW (0 << 4) +#define CFG_COMP_MODE_ACTIVE_HIGH (1 << 4) + +#define CFG_MODE_CONT (0 << CFG_MODE_BASE) +#define CFG_MODE_SSHOT (1 << CFG_MODE_BASE) + +typedef struct { + i2c_dev base; + ti_ads_adc device; + uint32_t poll_wait_period; + int reg; + uint16_t config_shadow; +} ti_ads; + +static int +ti_ads_reg_write(ti_ads* dev, int reg, uint16_t value) +{ + uint8_t out[3]; + i2c_msg msgs[1] = { + { + .addr = dev->base.address, + .flags = 0, + .len = (uint16_t) sizeof(out), + .buf = &out[0] + } + }; + out[0] = (uint8_t) reg; + out[1] = (uint8_t) (value >> 8); + out[2] = (uint8_t) value; + return i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); +} + +static int +ti_ads_reg_read(ti_ads* dev, int reg, uint16_t* value) +{ + uint8_t in[2] = { 0, 0 }; + uint8_t out[1] = { (uint8_t) reg }; + i2c_msg msgs[2] = { + { + .addr = dev->base.address, + .flags = 0, + .len = (uint16_t) sizeof(out), + .buf = &out[0] + }, { + .addr = dev->base.address, + .flags = I2C_M_RD, + .len = (uint16_t) sizeof(in), + .buf = &in[0] + } + }; + int err; + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + *value = (((uint16_t) in[0]) << 8) | in[1]; + return err; +} + +static int +ti_ads_set_config(ti_ads* dev, uint16_t value, int base, uint16_t mask) +{ + int err; + dev->config_shadow &= ~(mask << base); + dev->config_shadow |= (value & mask) << base; + err = ti_ads_reg_write(dev, ADS_CONFIG, dev->config_shadow); + return err; +} + +static int +ti_ads_sample(ti_ads* dev, uint16_t* value) +{ + int err; + if ((dev->config_shadow & CFG_MODE_SSHOT) == CFG_MODE_SSHOT) { + i2c_bus_obtain(dev->base.bus); + err = ti_ads_reg_write(dev, + ADS_CONFIG, + dev->config_shadow | CFG_OS_START_SSHOT); + if (err == 0) { + uint16_t config = 0; + while (err == 0 && (config & CFG_OS_NOT_CONVERTING) == 0) { + err = ti_ads_reg_read(dev, ADS_CONFIG, &config); + if (dev->poll_wait_period && (config & CFG_OS_NOT_CONVERTING) == 0) + usleep(dev->poll_wait_period); + } + err = ti_ads_reg_read(dev, ADS_CONVERSION, value); + } + i2c_bus_release(dev->base.bus); + } + else { + err = ti_ads_reg_read(dev, ADS_CONVERSION, value); + } + return err; +} + +static int +ti_ads_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg) +{ + ti_ads* dev = (ti_ads*) iic_dev; + uint16_t value; + int err; + + switch (command) { + case TI_ADS_ADC_GET_CONVERSION: + value = 0; + err = ti_ads_sample(dev, &value); + if (err == 0) + *((uint16_t*) arg) = value; + break; + case TI_ADS_ADC_SET_MUX: + if (dev->device == TI_ADS1115) { + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_set_config(dev, value, CFG_MUX_BASE, CFG_MUX_MASK); + } + else { + err = -ENOTTY; + } + break; + case TI_ADS_ADC_SET_MODE: + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_set_config(dev, value, CFG_MODE_BASE, CFG_MODE_MASK); + break; + case TI_ADS_ADC_SET_PGA: + if (dev->device == TI_ADS1114 || dev->device == TI_ADS1115) { + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_set_config(dev, value, CFG_PGA_BASE, CFG_PGA_MASK); + } + else { + err = -ENOTTY; + } + break; + case TI_ADS_ADC_SET_COMP: + if (dev->device == TI_ADS1114 || dev->device == TI_ADS1115) { + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_set_config(dev, value, CFG_COMP_BASE, CFG_COMP_MASK); + } + else { + err = -ENOTTY; + } + break; + case TI_ADS_ADC_SET_LO_THRESH: + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_reg_write(dev, ADS_LO_THRESH, value); + break; + case TI_ADS_ADC_SET_HI_THRESH: + value = (uint16_t)(uintptr_t) arg; + err = ti_ads_reg_write(dev, ADS_HI_THRESH, value); + break; + case TI_ADS_ADC_SET_CONV_WAIT: + dev->poll_wait_period = (uint32_t)(uintptr_t) arg; + err = 0; + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +int +i2c_dev_register_ti_ads_adc(const char* bus_path, + const char* dev_path, + uint16_t address, + ti_ads_adc device) +{ + ti_ads* dev; + + dev = (ti_ads*) + i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address); + if (dev == NULL) { + return -1; + } + + dev->base.ioctl = ti_ads_ioctl; + dev->device = device; + dev->config_shadow = 0; + + switch (device) { + default: + errno = EIO; + return -1; + case TI_ADS1115: + dev->config_shadow |= ((TI_ADS_MUX_ApA0_AnA1 << CFG_MUX_BASE) | + (TI_ADS_PGA_FS_2_048V << CFG_PGA_BASE) | + ((TI_ADS_COMP_MODE_HYSTERESIS | + TI_ADS_COMP_POL_ACTIVE_LOW | + TI_ADS_COMP_LAT_NON_LATCHING | + TI_ADS_COMP_QUE_DISABLE_COMP) << CFG_COMP_BASE)); + /* FALLTHRU */ + case TI_ADS1114: + dev->config_shadow |= TI_ADS_PGA_FS_2_048V << CFG_PGA_BASE; + /* FALLTHRU */ + case TI_ADS1113: + dev->config_shadow |= (CFG_MODE_SSHOT | + (TI_ADS_DATARATE_8SPS << CFG_DATA_RATE_BASE)); + break; + } + + return i2c_dev_register(&dev->base, dev_path); +} diff --git a/cpukit/dev/i2c/ti-lm25066a.c b/cpukit/dev/i2c/ti-lm25066a.c new file mode 100644 index 0000000000..3f8c32ff56 --- /dev/null +++ b/cpukit/dev/i2c/ti-lm25066a.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2016-2017 Chris Johns + * All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include +#include + +/* + * Commands. + * + * The commands are listed in Table 1 in the datasheet. These are the mapped to + * command ids in the intreface. Commands have different data size, a mix of + * read and write amd formats. + * + * The formats for values is listed in Table 41 in the datasheet. + */ + +#define CMD_RD (1 << 0) +#define CMD_WR (1 << 1) +#define CMD_RW (CMD_RD | CMD_WR) +#define CMD_STR (1 << 2) +#define CMD_VAL_ (1 << 3) +#define CMD_BLK_ (1 << 4) +#define CMD_INDEX_BASE (16UL) +#define CMD_INDEX_MASK (0xfUL) +#define CMD_VAL(l) (CMD_VAL_ | (((uint32_t) (l)) << CMD_INDEX_BASE)) +#define CMD_BLK(l) (CMD_BLK_ | (((uint32_t) (l)) << CMD_INDEX_BASE)) +#define CMD_INDEX(f) ((((uint32_t) (f)) >> CMD_INDEX_BASE) & CMD_INDEX_MASK) + +/* + * Number of bits in the ADC. + */ +#define ADC_BITS (12) + +/* + * Blocks of values that can be read. + */ +static const ti_lm25066a_cmd block_read[] = +{ + TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ, + TI_LM25066A_MFR_READ_IIN, + TI_LM25066A_READ_VOUT, + TI_LM25066A_READ_VIN, + TI_LM25066A_MFR_READ_PIN, + TI_LM25066A_READ_TEMPERATURE_1 +}; + +static const ti_lm25066a_cmd avg_block_read[] = +{ + TI_LM25066A_MFR_DIAGNOSTIC_WORD_READ, + TI_LM25066A_MFR_READ_AVG_IIN, + TI_LM25066A_MFR_READ_AVG_VOUT, + TI_LM25066A_MFR_READ_AVG_VIN, + TI_LM25066A_MFR_READ_AVG_PIN, + TI_LM25066A_READ_TEMPERATURE_1 +}; + +static const ti_lm25066a_cmd* blocks[] = +{ + block_read, + avg_block_read +}; + +typedef struct +{ + uint8_t cmd; + uint16_t size; + uint32_t flags; +} ti_lm25066a_i2c_cmd; + +/* + * Table 1. + */ +static const ti_lm25066a_i2c_cmd commands[] = +{ + { 0x01, 1, CMD_RW }, /* OPERATION */ + { 0x03, 0, CMD_WR }, /* CLEAR_FAULTS */ + { 0x19, 1, CMD_RD }, /* CAPABILITY */ + { 0x43, 2, CMD_RW | CMD_VAL(1) }, /* VOUT_UV_WARN_LIMIT */ + { 0x4f, 2, CMD_RW | CMD_VAL(5) }, /* OT_FAULT_LIMIT */ + { 0x51, 2, CMD_RW | CMD_VAL(5) }, /* OT_WARN_LIMIT */ + { 0x57, 2, CMD_RD | CMD_VAL(0) }, /* VIN_OV_WARN_LIMIT */ + { 0x58, 2, CMD_RW | CMD_VAL(0) }, /* VIN_UV_WARN_LIMIT */ + { 0x78, 1, CMD_RD }, /* STATUS_BYTE */ + { 0x79, 2, CMD_RD }, /* STATUS_WORD */ + { 0x7a, 1, CMD_RD }, /* STATUS_VOUT */ + { 0x7c, 1, CMD_RD }, /* STATUS_INPUT */ + { 0x7d, 1, CMD_RD }, /* STATUS_TEMPERATURE */ + { 0x7e, 1, CMD_RD }, /* STATUS_CML */ + { 0x80, 1, CMD_RD }, /* STATUS_MFR_SPECIFIC */ + { 0x88, 2, CMD_RD | CMD_VAL(0) }, /* READ_VIN */ + { 0x8b, 2, CMD_RD | CMD_VAL(1) }, /* READ_VOUT */ + { 0x8d, 2, CMD_RD | CMD_VAL(5) }, /* READ_TEMPERATURE_1 */ + { 0x99, 4, CMD_RD | CMD_STR }, /* MFR_ID */ + { 0x9a, 9, CMD_RD | CMD_STR }, /* MFR_MODEL */ + { 0x9b, 2, CMD_RD | CMD_STR }, /* MFR_REVISION */ + { 0xd0, 2, CMD_RD | CMD_VAL(2) }, /* MFR_READ_VAUX */ + { 0xd1, 2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_IIN */ + { 0xd2, 2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_PIN */ + { 0xd3, 2, CMD_RW | CMD_VAL(3) }, /* MFR_IIN_OC_WARN_LIMIT */ + { 0xd4, 2, CMD_RW | CMD_VAL(4) }, /* MFR_PIN_OP_WARN_LIMIT */ + { 0xd5, 2, CMD_RD | CMD_VAL(4) }, /* MFR_PIN_PEAK */ + { 0xd6, 0, CMD_WR }, /* MFR_CLEAR_PIN_PEAK */ + { 0xd7, 1, CMD_RW }, /* MFR_GATE_MASK */ + { 0xd8, 2, CMD_RW }, /* MFR_ALERT_MASK */ + { 0xd9, 1, CMD_RD }, /* MFR_DEVICE_SETUP */ + { 0xda, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLOCK_READ */ + { 0xdb, 1, CMD_RW }, /* MFR_SAMPLES_FOR_AVG */ + { 0xdc, 2, CMD_RD | CMD_VAL(0) }, /* MFR_READ_AVG_VIN */ + { 0xdd, 2, CMD_RD | CMD_VAL(1) }, /* MFR_READ_AVG_VOUT */ + { 0xde, 2, CMD_RD | CMD_VAL(3) }, /* MFR_READ_AVG_IIN */ + { 0xdf, 2, CMD_RD | CMD_VAL(4) }, /* MFR_READ_AVG_PIN */ + { 0xe0, 13, CMD_RD | CMD_BLK(0) }, /* MFR_BLACK_BOX_READ */ + { 0xe1, 2, CMD_RD }, /* MFR_DIAGNOSTIC_WORD_READ */ + { 0xe2, 13, CMD_RD | CMD_BLK(1) }, /* MFR_AVG_BLOCK_READ */ +}; + +#define IO_CMDS (sizeof(commands) / sizeof(commands[0])) +#define IO_INTS (6) +#define IO_MESSAGE_SIZE ((sizeof(uint16_t) * IO_INTS) + 2) + +typedef struct { + i2c_dev base; + uint8_t pointer; + uint16_t config_shadow; + const ti_lm25066a_conversion* conversions; + int scale; + uint8_t buffer[IO_MESSAGE_SIZE]; +} ti_lm25066a; + +/* + * Convert a value using table 41 in the data sheet. + */ +static int +ti_lm25066a_io_convert_to_real(ti_lm25066a* dev, uint16_t word, int conversion) +{ + const ti_lm25066a_conversion* const con = &dev->conversions[conversion]; + uint32_t u = word; + int value; + value = ((int) (u & ((1 << ADC_BITS) - 1))) * dev->scale; + value = ((value * con->R) - con->b) / con->m; + return value; +} + +static void +ti_lm25066a_io_convert_block(ti_lm25066a* dev, + const uint16_t* words, + int* values, + int block) +{ + const ti_lm25066a_cmd* cmds = blocks[block]; + int c; + /* + * A block is always 6 values. + */ + values[0] = words[0]; + for (c = 1; c < 6; ++c) { + const ti_lm25066a_i2c_cmd* cmd = &commands[cmds[c]]; + if ((cmd->flags & CMD_VAL_) != 0) { + values[c] = + ti_lm25066a_io_convert_to_real(dev, words[c], CMD_INDEX(cmd->flags)); + } else { + values[c] = words[c]; + } + } +} + +static int +ti_lm25066a_io_read(ti_lm25066a* dev, ti_lm25066a_io* io) +{ + const ti_lm25066a_i2c_cmd* cmd; + uint8_t out[1]; + uint8_t* in = dev->buffer; + i2c_msg msgs[2]; + int err; + + if (io->cmd >= IO_CMDS) + return -EIO; + + cmd = &commands[io->cmd]; + + if ((cmd->flags & CMD_RD) == 0) + return -EIO; + + out[0] = cmd->cmd; + msgs[0].addr = dev->base.address; + msgs[0].flags = 0; + msgs[0].len = (uint16_t) sizeof(out); + msgs[0].buf = &out[0]; + msgs[1].addr = dev->base.address; + msgs[1].flags = I2C_M_RD; + msgs[1].len = (uint16_t) cmd->size; + msgs[1].buf = &in[0]; + + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + if (err != 0) + return err; + + err = -EIO; + + switch (io->type) { + case TI_LM25066A_8BIT: + io->data.u8 = in[0]; + err = 0; + break; + case TI_LM25066A_16BIT: + io->data.u16 = (((uint16_t) in[0]) << 8) | in[1]; + err = 0; + break; + case TI_LM25066A_VALUE: + if ((cmd->flags & CMD_VAL_) != 0) { + uint16_t data = ((uint16_t) in[1]) << 8 | in[0]; + io->data.value = + ti_lm25066a_io_convert_to_real(dev, data, CMD_INDEX(cmd->flags)); + } + else { + io->data.value = ((uint16_t) in[1]) << 8 | in[0];; + } + err = 0; + break; + case TI_LM25066A_VALUES: + if ((cmd->flags & CMD_BLK_) != 0) { + uint16_t data[in[0] / 2]; + uint16_t* d = &data[0]; + uint8_t* i = &in[1]; + int w; + for (w = 0; w < (in[0] / 2); ++w, ++d, i += 2) { + *d = (((uint16_t) i[1]) << 8) | i[0] ; + } + ti_lm25066a_io_convert_block(dev, + &data[0], + &io->data.values[0], + CMD_INDEX(cmd->flags)); + err = 0; + } + break; + case TI_LM25066A_STRING: + memcpy(&io->data.string[0], &in[0], cmd->size); + err = 0; + break; + case TI_LM25066A_RAW: + memcpy(io->data.raw, &in[0], cmd->size); + err = 0; + break; + default: + break; + } + + return err; +} + +static int +ti_lm25066a_io_write(ti_lm25066a* dev, ti_lm25066a_io* io) +{ + const ti_lm25066a_i2c_cmd* cmd; + uint8_t* out = dev->buffer; + uint16_t length = 0; + int err; + + if (io->cmd >= IO_CMDS) + return -EIO; + + cmd = &commands[io->cmd]; + + if ((cmd->flags & CMD_WR) == 0) + return -EIO; + + out[0] = cmd->cmd; + + if (cmd->size == 0) { + out[1] = 0; + length = 1; + } + else { + switch (io->type) { + case TI_LM25066A_8BIT: + out[1] = io->data.u8; + length = 1 + 1; + break; + case TI_LM25066A_16BIT: + out[1] = io->data.u16 >> 8; + out[2] = io->data.u16; + length = 2 + 1; + break; + case TI_LM25066A_VALUE: + break; + case TI_LM25066A_VALUES: + break; + case TI_LM25066A_STRING: + break; + case TI_LM25066A_RAW: + memcpy(&out[1], io->data.raw, cmd->size); + length = cmd->size + 1; + err = 0; + break; + default: + break; + } + } + + if (length == 0) + err = -EIO; + else { + i2c_msg msgs[1] = { + { + .addr = dev->base.address, + .flags = 0, + .len = length, + .buf = &out[0] + } + }; + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + } + + return err; +} + +static int +ti_lm25066a_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg) +{ + ti_lm25066a* dev = (ti_lm25066a*) iic_dev; + ti_lm25066a_io* io = (ti_lm25066a_io*) arg; + int err; + + switch (command) { + case TI_LM25066A_GET: + err = ti_lm25066a_io_read(dev, io); + break; + case TI_LM25066A_SET: + err = ti_lm25066a_io_write(dev, io); + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +int +i2c_dev_register_ti_lm25066a(const char* bus_path, + const char* dev_path, + uint16_t address, + const ti_lm25066a_conversion* const conversions, + const int decimal_points) +{ + ti_lm25066a* dev; + int i; + + dev = (ti_lm25066a*) + i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address); + if (dev == NULL) { + return -1; + } + + dev->base.ioctl = ti_lm25066a_ioctl; + dev->pointer = -1; + dev->config_shadow = 0; + dev->conversions = conversions; + dev->scale = 1; + for (i = 0; i < decimal_points; ++i) + dev->scale *= 10; + + return i2c_dev_register(&dev->base, dev_path); +} diff --git a/cpukit/dev/i2c/ti-tmp112.c b/cpukit/dev/i2c/ti-tmp112.c new file mode 100644 index 0000000000..0886a4de9c --- /dev/null +++ b/cpukit/dev/i2c/ti-tmp112.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2016-2017 Chris Johns + * All rights reserved. + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#if HAVE_CONFIG_H + #include "config.h" +#endif + +#include + +#include +#include + +/* + * Registers. + */ +#define TMP_TEMPERATURE (0) +#define TMP_CONFIG (1) +#define TMP_LOW_TEMP (2) +#define TMP_HIGH_TEMP (3) + +/* + * Configuration register. + */ +#define CFG_ONE_SHOT_BASE (15) +#define CFG_SHUTDOWN_BASE (8) +#define CFG_EXTENDED_MODE_BASE (4) + +#define CFG_ONE_SHOT (1 << CFG_ONE_SHOT_BASE) +#define CFG_SHUTDOWN (1 << CFG_SHUTDOWN_BASE) +#define CFG_EXTENDED_MODE (1 << CFG_EXTENDED_MODE_BASE) + +typedef struct { + i2c_dev base; + uint8_t pointer; + uint16_t config_shadow; +} ti_tmp112; + +static int +ti_tmp112_reg_write(ti_tmp112* dev, int reg, uint16_t value) +{ + uint8_t out[3]; + i2c_msg msgs[1] = { + { + .addr = dev->base.address, + .flags = 0, + .len = (uint16_t) sizeof(out), + .buf = &out[0] + } + }; + int err; + if (dev->pointer == reg) { + out[0] = (uint8_t) (value >> 8); + out[1] = (uint8_t) value; + msgs[0].len = 2; + } + else { + out[0] = reg; + out[1] = (uint8_t) (value >> 8); + out[2] = (uint8_t) value; + } + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + if (err == 0) + dev->pointer = reg; + else + dev->pointer = -1; + return err; +} + +static int +ti_tmp112_reg_read(ti_tmp112* dev, int reg, uint16_t* value) +{ + uint8_t in[2] = { 0, 0 }; + int err; + if (dev->pointer == reg) { + i2c_msg msgs[1] = { + { + .addr = dev->base.address, + .flags = I2C_M_RD, + .len = (uint16_t) sizeof(in), + .buf = &in[0] + } + }; + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + } + else { + uint8_t out[1] = { (uint8_t) reg }; + i2c_msg msgs[2] = { + { + .addr = dev->base.address, + .flags = 0, + .len = (uint16_t) sizeof(out), + .buf = &out[0] + }, { + .addr = dev->base.address, + .flags = I2C_M_RD, + .len = (uint16_t) sizeof(in), + .buf = &in[0] + } + }; + err = i2c_bus_transfer(dev->base.bus, &msgs[0], RTEMS_ARRAY_SIZE(msgs)); + if (err == 0) + dev->pointer = reg; + else + dev->pointer = -1; + } + *value = (((uint16_t) in[0]) << 8) | in[1]; + return err; +} + +static int +ti_tmp112_set_config(ti_tmp112* dev, uint16_t value) +{ + int err; + dev->config_shadow = value; + err = ti_tmp112_reg_write(dev, TMP_CONFIG, dev->config_shadow); + return err; +} + +static int +ti_tmp112_get_temp(ti_tmp112* dev, int* temp, bool raw) +{ + uint16_t value = 0; + int err; + + *temp = 0; + + if ((dev->config_shadow & CFG_SHUTDOWN) == CFG_SHUTDOWN) { + i2c_bus_obtain(dev->base.bus); + err = ti_tmp112_reg_write(dev, + TMP_CONFIG, + dev->config_shadow | CFG_ONE_SHOT); + if (err == 0) { + uint16_t config = 0; + while (err == 0 && (config & CFG_ONE_SHOT) == 0) + err = ti_tmp112_reg_read(dev, TMP_CONFIG, &config); + err = ti_tmp112_reg_read(dev, TMP_TEMPERATURE, &value); + } + i2c_bus_release(dev->base.bus); + } + else { + err = ti_tmp112_reg_read(dev, TMP_TEMPERATURE, &value); + } + + if (err == 0) { + if (raw) { + *temp = (int) value; + } + else { + int bits = 12; + uint32_t u; + if ((dev->config_shadow & CFG_EXTENDED_MODE) != 0) + bits = 13; + u = value >> ((sizeof(value) * 8) - bits); + *temp = (int) (u & ((1 << bits) - 1)); + if ((u & (1 << (bits - 1))) != 0) + *temp |= ~((1 << bits) - 1); + *temp = *temp * 625; + } + } + + return err; +} + +static int +ti_tmp112_ioctl(i2c_dev* iic_dev, ioctl_command_t command, void* arg) +{ + ti_tmp112* dev = (ti_tmp112*) iic_dev; + uint16_t v16; + int vint; + int err; + + switch (command) { + case TI_TMP112_GET_TEMP: + vint = 0; + err = ti_tmp112_get_temp(dev, &vint, false); + if (err == 0) + *((int*) arg) = vint; + break; + case TI_TMP112_GET_TEMP_RAW: + vint = 0; + err = ti_tmp112_get_temp(dev, &vint, true); + if (err == 0) + *((uint16_t*) arg) = (uint16_t) vint; + break; + case TI_TMP112_SET_CONFIG: + v16 = (uint16_t)(uintptr_t) arg; + err = ti_tmp112_set_config(dev, v16); + break; + case TI_TMP112_SET_LOW_TEMP: + v16 = (uint16_t)(uintptr_t) arg; + err = ti_tmp112_reg_write(dev, TMP_LOW_TEMP, v16); + break; + case TI_TMP112_SET_HIGH_TEMP: + v16 = (uint16_t)(uintptr_t) arg; + err = ti_tmp112_reg_write(dev, TMP_HIGH_TEMP, v16); + break; + default: + err = -ENOTTY; + break; + } + + return err; +} + +int +i2c_dev_register_ti_tmp112(const char* bus_path, + const char* dev_path, + uint16_t address) +{ + ti_tmp112* dev; + + dev = (ti_tmp112*) + i2c_dev_alloc_and_init(sizeof(*dev), bus_path, address); + if (dev == NULL) { + return -1; + } + + dev->base.ioctl = ti_tmp112_ioctl; + dev->pointer = -1; + dev->config_shadow = 0x60a0; + + return i2c_dev_register(&dev->base, dev_path); +} -- cgit v1.2.3