summaryrefslogtreecommitdiff
path: root/driver/ina3221.c
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 /driver/ina3221.c
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>
Diffstat (limited to 'driver/ina3221.c')
-rw-r--r--driver/ina3221.c162
1 files changed, 162 insertions, 0 deletions
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