/* Copyright 2019 The ChromiumOS Authors * 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 "ina3221.h" #include "system.h" #include "timer.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, const 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, " [config|mask ]", "INA3221 voltage sensing"); #endif