blob: 5b89f9694e380048da2e0964ed980e0295016eca [file] [log] [blame]
/* 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