diff options
author | Andrew McRae <amcrae@google.com> | 2019-11-08 15:47:56 +1100 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2019-11-14 04:31:44 +0000 |
commit | 86a7e2f4b791ec02da9403781b21ae19e3e6cb1e (patch) | |
tree | b084704c52b7ecb05d424e53c8cbad9ccca4fe27 /driver | |
parent | a4972e187c6ce582aa54dbfce6039fd2239e4bbd (diff) | |
download | chrome-ec-86a7e2f4b791ec02da9403781b21ae19e3e6cb1e.tar.gz |
ec: Add driver for TI INA3221 voltage sensors.
Add driver for TI INA3221 voltage sensors.
Puff has several of these devices, and the EC has access to them.
BRANCH=none
BUG=b:144132145
TEST=EC buildall, tests
Change-Id: I37efd6ce7f154339f002c633e5daf6a18fef05aa
Signed-off-by: Andrew McRae <amcrae@google.com>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1903629
Reviewed-by: Shelley Chen <shchen@chromium.org>
Reviewed-by: Andrew McRae <amcrae@chromium.org>
Tested-by: Andrew McRae <amcrae@chromium.org>
Commit-Queue: Andrew McRae <amcrae@chromium.org>
Diffstat (limited to 'driver')
-rw-r--r-- | driver/build.mk | 1 | ||||
-rw-r--r-- | driver/ina3221.c | 162 | ||||
-rw-r--r-- | driver/ina3221.h | 55 |
3 files changed, 218 insertions, 0 deletions
diff --git a/driver/build.mk b/driver/build.mk index e912c3d33a..7bc59eb331 100644 --- a/driver/build.mk +++ b/driver/build.mk @@ -85,6 +85,7 @@ driver-$(CONFIG_IO_EXPANDER_NCT38XX)+=ioexpander_nct38xx.o # Current/Power monitor driver-$(CONFIG_INA219)$(CONFIG_INA231)+=ina2xx.o +driver-$(CONFIG_INA3221)+=ina3221.o # LED drivers driver-$(CONFIG_LED_DRIVER_DS2413)+=led/ds2413.o diff --git a/driver/ina3221.c b/driver/ina3221.c new file mode 100644 index 0000000000..5b89f9694e --- /dev/null +++ b/driver/ina3221.c @@ -0,0 +1,162 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * TI INA3221 Power monitor driver. + */ + +#include "console.h" +#include "hooks.h" +#include "i2c.h" +#include "system.h" +#include "timer.h" +#include "ina3221.h" +#include "util.h" + +/* Console output macros */ +#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args) + +const static uint8_t ina3221_reg_map[INA3221_CHAN_COUNT][INA3221_MAX_REG] = { +{ 1, 2, 7, 8 }, /* Chan 1 */ +{ 3, 4, 9, 10 }, /* Chan 2 */ +{ 5, 6, 11, 12 } /* Chan 3 */ +}; + +static uint16_t ina3221_read(unsigned int unit, uint8_t reg) +{ + int res; + int val; + + res = i2c_read16(ina3221[unit].port, ina3221[unit].address, + reg, &val); + if (res) { + CPRINTS("INA3221 I2C read failed"); + return 0x0bad; + } + return (val >> 8) | ((val & 0xff) << 8); +} + +static uint16_t ina3221_chan_read(unsigned int unit, enum ina3221_channel chan, + enum ina3221_register reg) +{ + if (chan >= INA3221_CHAN_COUNT || reg >= INA3221_MAX_REG) { + CPRINTS("INA3221 Bad channel or register value"); + return 0x0bad; + } + return ina3221_read(unit, ina3221_reg_map[chan][reg]); +} + +static int ina3221_write(unsigned int unit, uint8_t reg, uint16_t val) +{ + int res; + uint16_t be_val = (val >> 8) | ((val & 0xff) << 8); + + res = i2c_write16(ina3221[unit].port, ina3221[unit].address, + reg, be_val); + if (res) + CPRINTS("INA3221 I2C write failed"); + return res; +} + +static void ina3221_init(void) +{ + unsigned int unit; + + for (unit = 0; unit < ina3221_count; unit++) { + uint16_t conf = INA3221_CONFIG_BASE; + int chan; + + for (chan = 0; chan < INA3221_CHAN_COUNT; chan++) { + /* Only initialise if channel is used */ + if (ina3221[unit].name[chan] != NULL) + conf |= 0x4000 >> chan; + } + ina3221_write(unit, INA3221_REG_CONFIG, conf); + } +} + +DECLARE_HOOK(HOOK_INIT, ina3221_init, HOOK_PRIO_INIT_EXTPOWER + 1); + +#ifdef CONFIG_CMD_INA +static void ina3221_dump(unsigned int unit) +{ + uint16_t cfg; + int16_t sv[INA3221_CHAN_COUNT]; + uint16_t bv[INA3221_CHAN_COUNT]; + uint16_t crit[INA3221_CHAN_COUNT]; + uint16_t warn[INA3221_CHAN_COUNT]; + uint16_t mask; + int chan; + + cfg = ina3221_read(unit, INA3221_REG_CONFIG); + for (chan = 0; chan < INA3221_CHAN_COUNT; chan++) { + if (ina3221[unit].name[chan] != NULL) { + sv[chan] = ina3221_chan_read(unit, chan, + INA3221_SHUNT_VOLT); + bv[chan] = ina3221_chan_read(unit, chan, + INA3221_BUS_VOLT); + crit[chan] = ina3221_chan_read(unit, chan, + INA3221_CRITICAL); + warn[chan] = ina3221_chan_read(unit, chan, + INA3221_WARNING); + } + } + mask = ina3221_read(unit, INA3221_REG_MASK); + + ccprintf("Unit %d, address: %04x\n", unit, ina3221[unit].address); + ccprintf("Configuration : %04x\n", cfg); + for (chan = 0; chan < INA3221_CHAN_COUNT; chan++) { + if (ina3221[unit].name[chan] != NULL) { + ccprintf("%d: %s:\n", chan, ina3221[unit].name[chan]); + ccprintf(" Shunt voltage: %04x => %d uV\n", + sv[chan], INA3221_SHUNT_UV((int)sv[chan])); + ccprintf(" Bus voltage : %04x => %d mV\n", + bv[chan], INA3221_BUS_MV((int)bv[chan])); + ccprintf(" Warning : %04x\n", warn[chan]); + ccprintf(" Critical : %04x\n", crit[chan]); + } + } + ccprintf("Mask/Enable : %04x\n", mask); +} + +/*****************************************************************************/ +/* Console commands */ + +static int command_ina(int argc, char **argv) +{ + char *e; + unsigned int unit; + uint16_t val; + + if (argc < 2) + return EC_ERROR_PARAM_COUNT; + + unit = strtoi(argv[1], &e, 10); + if (*e || unit >= ina3221_count) + return EC_ERROR_PARAM1; + + if (argc == 2) { /* dump all registers */ + ina3221_dump(unit); + return EC_SUCCESS; + } else if (argc == 4) { + val = strtoi(argv[3], &e, 16); + if (*e) + return EC_ERROR_PARAM3; + + if (!strcasecmp(argv[2], "config")) { + ina3221_write(unit, INA3221_REG_CONFIG, val); + } else if (!strcasecmp(argv[2], "mask")) { + ina3221_write(unit, INA3221_REG_MASK, val); + } else { + ccprintf("Invalid register: %s\n", argv[1]); + return EC_ERROR_INVAL; + } + return EC_SUCCESS; + } + + return EC_ERROR_INVAL; +} +DECLARE_CONSOLE_COMMAND(ina, command_ina, + "<index> [config|mask <val>]", + "INA3221 voltage sensing"); +#endif diff --git a/driver/ina3221.h b/driver/ina3221.h new file mode 100644 index 0000000000..4d8c8211b4 --- /dev/null +++ b/driver/ina3221.h @@ -0,0 +1,55 @@ +/* Copyright 2019 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * TI INA3221 Current/Power monitor driver. + */ + +#ifndef __CROS_EC_INA3221_H +#define __CROS_EC_INA3221_H + +#define INA3221_REG_CONFIG 0x00 +#define INA3221_REG_MASK 0x0F + +/* + * Common bits are: + * Reset + * average = 1 + * conversion time = 1.1 ms + * mode = shunt and bus, continuous. + */ +#define INA3221_CONFIG_BASE 0x8127 + +/* Bus voltage: lower 3 bits clear, LSB = 8 mV */ +#define INA3221_BUS_MV(reg) (reg) +/* Shunt voltage: lower 3 bits clear, LSB = 40 uV */ +#define INA3221_SHUNT_UV(reg) ((reg) * (40/8)) + +enum ina3221_channel { + INA3221_CHAN_1 = 0, + INA3221_CHAN_2 = 1, + INA3221_CHAN_3 = 2, + INA3221_CHAN_COUNT = 3 +}; + +/* Registers for each channel */ +enum ina3221_register { + INA3221_SHUNT_VOLT = 0, + INA3221_BUS_VOLT = 1, + INA3221_CRITICAL = 2, + INA3221_WARNING = 3, + INA3221_MAX_REG = 4 +}; + +/* Configuration table - defined in board file. */ +struct ina3221_t { + int port; /* I2C port index */ + uint8_t address; /* I2C address */ + const char *name[INA3221_CHAN_COUNT]; /* Channel names */ +}; + +/* External config in board file */ +extern const struct ina3221_t ina3221[]; +extern const unsigned int ina3221_count; + +#endif /* __CROS_EC_INA3221_H */ |