summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew McRae <amcrae@google.com>2019-11-08 15:47:56 +1100
committerCommit Bot <commit-bot@chromium.org>2019-11-14 04:31:44 +0000
commit86a7e2f4b791ec02da9403781b21ae19e3e6cb1e (patch)
treeb084704c52b7ecb05d424e53c8cbad9ccca4fe27
parenta4972e187c6ce582aa54dbfce6039fd2239e4bbd (diff)
downloadchrome-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>
-rw-r--r--driver/build.mk1
-rw-r--r--driver/ina3221.c162
-rw-r--r--driver/ina3221.h55
-rw-r--r--include/config.h6
4 files changed, 222 insertions, 2 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 */
diff --git a/include/config.h b/include/config.h
index f7b2ee83e5..e92a20f411 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2315,11 +2315,13 @@
/* Current/Power monitor */
/*
- * Compile driver for INA219 or INA231. These two flags may not be both
- * defined.
+ * Compile driver for INA219 or INA231 or INA3221.
+ * Only one of these may be defined (if any).
*/
#undef CONFIG_INA219
#undef CONFIG_INA231
+#undef CONFIG_INA3221
+
/*****************************************************************************/
/* Inductive charging */