summaryrefslogtreecommitdiff
path: root/chip/stm32/usb_power.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/stm32/usb_power.c')
-rw-r--r--chip/stm32/usb_power.c733
1 files changed, 0 insertions, 733 deletions
diff --git a/chip/stm32/usb_power.c b/chip/stm32/usb_power.c
deleted file mode 100644
index 24dbff06cd..0000000000
--- a/chip/stm32/usb_power.c
+++ /dev/null
@@ -1,733 +0,0 @@
-/* Copyright 2016 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.
- */
-
-#include "common.h"
-#include "dma.h"
-#include "hooks.h"
-#include "i2c.h"
-#include "link_defs.h"
-#include "registers.h"
-#include "timer.h"
-#include "usb_descriptor.h"
-#include "usb_power.h"
-#include "util.h"
-
-#define CPRINTS(format, args...) cprints(CC_I2C, format, ## args)
-
-static int usb_power_init_inas(struct usb_power_config const *config);
-static int usb_power_read(struct usb_power_config const *config);
-static int usb_power_write_line(struct usb_power_config const *config);
-
-void usb_power_deferred_rx(struct usb_power_config const *config)
-{
- int rx_count = rx_ep_pending(config->endpoint);
-
- /* Handle an incoming command if available */
- if (rx_count)
- usb_power_read(config);
-}
-
-void usb_power_deferred_tx(struct usb_power_config const *config)
-{
- struct usb_power_state *state = config->state;
-
- if (!tx_ep_is_ready(config->endpoint))
- return;
-
- /* We've replied, set up the next read. */
- if (!rx_ep_is_active(config->endpoint)) {
- /* Remove any active dma region from output buffer */
- state->reports_xmit_active = state->reports_tail;
-
- /* Wait for the next command */
- usb_read_ep(config->endpoint,
- config->ep->out_databuffer_max,
- config->ep->out_databuffer);
- return;
- }
-}
-
-/* Reset stream */
-void usb_power_event(struct usb_power_config const *config,
- enum usb_ep_event evt)
-{
- if (evt != USB_EVENT_RESET)
- return;
-
- config->ep->out_databuffer = config->state->rx_buf;
- config->ep->out_databuffer_max = sizeof(config->state->rx_buf);
- config->ep->in_databuffer = config->state->tx_buf;
- config->ep->in_databuffer_max = sizeof(config->state->tx_buf);
-
- epN_reset(config->endpoint);
-
- /* Flush any queued data */
- hook_call_deferred(config->ep->rx_deferred, 0);
- hook_call_deferred(config->ep->tx_deferred, 0);
-}
-
-
-/* Write one or more power records to USB */
-static int usb_power_write_line(struct usb_power_config const *config)
-{
- struct usb_power_state *state = config->state;
- struct usb_power_report *r = (struct usb_power_report *)(
- state->reports_data_area +
- (USB_POWER_RECORD_SIZE(state->ina_count)
- * state->reports_tail));
- /* status + size + timestamps + power list */
- size_t bytes = USB_POWER_RECORD_SIZE(state->ina_count);
-
- /* Check if queue has active data. */
- if (config->state->reports_head != config->state->reports_tail) {
- int recordcount = 1;
-
- /* We'll concatenate all the upcoming recrds. */
- if (config->state->reports_tail < config->state->reports_head)
- recordcount = config->state->reports_head -
- config->state->reports_tail;
- else
- recordcount = state->max_cached -
- config->state->reports_tail;
-
- state->reports_xmit_active = state->reports_tail;
- state->reports_tail = (state->reports_tail + recordcount) %
- state->max_cached;
-
- usb_write_ep(config->endpoint, bytes * recordcount, r);
- return bytes;
- }
-
- return 0;
-}
-
-
-static int usb_power_state_reset(struct usb_power_config const *config)
-{
- struct usb_power_state *state = config->state;
-
- state->state = USB_POWER_STATE_OFF;
- state->reports_head = 0;
- state->reports_tail = 0;
- state->reports_xmit_active = 0;
-
- CPRINTS("[RESET] STATE -> OFF");
- return USB_POWER_SUCCESS;
-}
-
-
-static int usb_power_state_stop(struct usb_power_config const *config)
-{
- struct usb_power_state *state = config->state;
-
- /* Only a valid transition from CAPTURING */
- if (state->state != USB_POWER_STATE_CAPTURING) {
- CPRINTS("[STOP] Error not capturing.");
- return USB_POWER_ERROR_NOT_CAPTURING;
- }
-
- state->state = USB_POWER_STATE_OFF;
- state->reports_head = 0;
- state->reports_tail = 0;
- state->reports_xmit_active = 0;
- state->stride_bytes = 0;
- CPRINTS("[STOP] STATE: CAPTURING -> OFF");
- return USB_POWER_SUCCESS;
-}
-
-
-
-static int usb_power_state_start(struct usb_power_config const *config,
- union usb_power_command_data *cmd, int count)
-{
- struct usb_power_state *state = config->state;
- int integration_us = cmd->start.integration_us;
- int ret;
-
- if (state->state != USB_POWER_STATE_SETUP) {
- CPRINTS("[START] Error not setup.");
- return USB_POWER_ERROR_NOT_SETUP;
- }
-
- if (count != sizeof(struct usb_power_command_start)) {
- CPRINTS("[START] Error count %d is not %d", (int)count,
- sizeof(struct usb_power_command_start));
- return USB_POWER_ERROR_READ_SIZE;
- }
-
- if (integration_us == 0) {
- CPRINTS("[START] integration_us cannot be 0");
- return USB_POWER_ERROR_UNKNOWN;
- }
-
- /* Calculate the reports array */
- state->stride_bytes = USB_POWER_RECORD_SIZE(state->ina_count);
- state->max_cached = USB_POWER_MAX_CACHED(state->ina_count);
-
- state->integration_us = integration_us;
- ret = usb_power_init_inas(config);
-
- if (ret)
- return USB_POWER_ERROR_INVAL;
-
- state->state = USB_POWER_STATE_CAPTURING;
- CPRINTS("[START] STATE: SETUP -> CAPTURING %dus", integration_us);
-
- /* Find our starting time. */
- config->state->base_time = get_time().val;
-
- hook_call_deferred(config->deferred_cap, state->integration_us);
- return USB_POWER_SUCCESS;
-}
-
-
-static int usb_power_state_settime(struct usb_power_config const *config,
- union usb_power_command_data *cmd, int count)
-{
- if (count != sizeof(struct usb_power_command_settime)) {
- CPRINTS("[SETTIME] Error: count %d is not %d",
- (int)count, sizeof(struct usb_power_command_settime));
- return USB_POWER_ERROR_READ_SIZE;
- }
-
- /* Find the offset between microcontroller clock and host clock. */
- if (cmd->settime.time)
- config->state->wall_offset = cmd->settime.time - get_time().val;
- else
- config->state->wall_offset = 0;
-
- return USB_POWER_SUCCESS;
-}
-
-
-static int usb_power_state_addina(struct usb_power_config const *config,
- union usb_power_command_data *cmd, int count)
-{
- struct usb_power_state *state = config->state;
- struct usb_power_ina_cfg *ina;
- int i;
-
- /* Only valid from OFF or SETUP */
- if ((state->state != USB_POWER_STATE_OFF) &&
- (state->state != USB_POWER_STATE_SETUP)) {
- CPRINTS("[ADDINA] Error incorrect state.");
- return USB_POWER_ERROR_NOT_SETUP;
- }
-
- if (count != sizeof(struct usb_power_command_addina)) {
- CPRINTS("[ADDINA] Error count %d is not %d",
- (int)count, sizeof(struct usb_power_command_addina));
- return USB_POWER_ERROR_READ_SIZE;
- }
-
- if (state->ina_count >= USB_POWER_MAX_READ_COUNT) {
- CPRINTS("[ADDINA] Error INA list full");
- return USB_POWER_ERROR_FULL;
- }
-
- /* Transition to SETUP state if necessary and clear INA data */
- if (state->state == USB_POWER_STATE_OFF) {
- state->state = USB_POWER_STATE_SETUP;
- state->ina_count = 0;
- }
-
- if ((cmd->addina.type < USBP_INA231_POWER) ||
- (cmd->addina.type > USBP_INA231_SHUNTV)) {
- CPRINTS("[ADDINA] Error INA type 0x%x invalid",
- (int)(cmd->addina.type));
- return USB_POWER_ERROR_INVAL;
- }
-
- if (cmd->addina.rs == 0) {
- CPRINTS("[ADDINA] Error INA resistance cannot be zero!");
- return USB_POWER_ERROR_INVAL;
- }
-
- /* Select INA to configure */
- ina = state->ina_cfg + state->ina_count;
-
- ina->port = cmd->addina.port;
- ina->addr_flags = cmd->addina.addr_flags;
- ina->rs = cmd->addina.rs;
- ina->type = cmd->addina.type;
-
- /*
- * INAs can be shared, in that they will have various values
- * (and therefore registers) read from them each cycle, including
- * power, voltage, current. If only a single value is read,
- * we an use i2c_readagain for faster transactions as we don't
- * have to respecify the address.
- */
- ina->shared = 0;
-#ifdef USB_POWER_VERBOSE
- ina->shared = 1;
-#endif
-
- /* Check if shared with previously configured INAs. */
- for (i = 0; i < state->ina_count; i++) {
- struct usb_power_ina_cfg *tmp = state->ina_cfg + i;
-
- if ((tmp->port == ina->port) &&
- (tmp->addr_flags == ina->addr_flags)) {
- ina->shared = 1;
- tmp->shared = 1;
- }
- }
-
- state->ina_count += 1;
- return USB_POWER_SUCCESS;
-}
-
-static int usb_power_read(struct usb_power_config const *config)
-{
- /*
- * If there is a USB packet waiting we process it and generate a
- * response.
- */
- uint8_t count = rx_ep_pending(config->endpoint);
- uint8_t result = USB_POWER_SUCCESS;
- union usb_power_command_data *cmd =
- (union usb_power_command_data *)config->ep->out_databuffer;
-
- struct usb_power_state *state = config->state;
- struct dwc_usb_ep *ep = config->ep;
-
- /* Bytes to return */
- int in_msgsize = 1;
-
- if (count < 2)
- return EC_ERROR_INVAL;
-
- /* State machine. */
- switch (cmd->command) {
- case USB_POWER_CMD_RESET:
- result = usb_power_state_reset(config);
- break;
-
- case USB_POWER_CMD_STOP:
- result = usb_power_state_stop(config);
- break;
-
- case USB_POWER_CMD_START:
- result = usb_power_state_start(config, cmd, count);
- if (result == USB_POWER_SUCCESS) {
- /* Send back actual integration time. */
- ep->in_databuffer[1] =
- (state->integration_us >> 0) & 0xff;
- ep->in_databuffer[2] =
- (state->integration_us >> 8) & 0xff;
- ep->in_databuffer[3] =
- (state->integration_us >> 16) & 0xff;
- ep->in_databuffer[4] =
- (state->integration_us >> 24) & 0xff;
- in_msgsize += 4;
- }
- break;
-
- case USB_POWER_CMD_ADDINA:
- result = usb_power_state_addina(config, cmd, count);
- break;
-
- case USB_POWER_CMD_SETTIME:
- result = usb_power_state_settime(config, cmd, count);
- break;
-
- case USB_POWER_CMD_NEXT:
- if (state->state == USB_POWER_STATE_CAPTURING) {
- int ret;
-
- ret = usb_power_write_line(config);
- if (ret)
- return EC_SUCCESS;
-
- result = USB_POWER_ERROR_BUSY;
- } else {
- CPRINTS("[STOP] Error not capturing.");
- result = USB_POWER_ERROR_NOT_CAPTURING;
- }
- break;
-
- default:
- CPRINTS("[ERROR] Unknown command 0x%04x", (int)cmd->command);
- result = USB_POWER_ERROR_UNKNOWN;
- break;
- }
-
- /* Return result code if applicable. */
- ep->in_databuffer[0] = result;
-
- usb_write_ep(config->endpoint, in_msgsize, ep->in_databuffer);
-
- return EC_SUCCESS;
-}
-
-
-
-/******************************************************************************
- * INA231 interface.
- * List the registers and fields here.
- * TODO(nsanders): combine with the currently incompatible common INA drivers.
- */
-
-#define INA231_REG_CONF 0
-#define INA231_REG_RSHV 1
-#define INA231_REG_BUSV 2
-#define INA231_REG_PWR 3
-#define INA231_REG_CURR 4
-#define INA231_REG_CAL 5
-#define INA231_REG_EN 6
-
-
-#define INA231_CONF_AVG(val) (((int)(val & 0x7)) << 9)
-#define INA231_CONF_BUS_TIME(val) (((int)(val & 0x7)) << 6)
-#define INA231_CONF_SHUNT_TIME(val) (((int)(val & 0x7)) << 3)
-#define INA231_CONF_MODE(val) (((int)(val & 0x7)) << 0)
-#define INA231_MODE_OFF 0x0
-#define INA231_MODE_SHUNT 0x5
-#define INA231_MODE_BUS 0x6
-#define INA231_MODE_BOTH 0x7
-
-int reg_type_mapping(enum usb_power_ina_type ina_type)
-{
- switch (ina_type) {
- case USBP_INA231_POWER:
- return INA231_REG_PWR;
- case USBP_INA231_BUSV:
- return INA231_REG_BUSV;
- case USBP_INA231_CURRENT:
- return INA231_REG_CURR;
- case USBP_INA231_SHUNTV:
- return INA231_REG_RSHV;
-
- default:
- return INA231_REG_CONF;
- }
-}
-
-uint16_t ina2xx_readagain(uint8_t port, uint16_t slave_addr_flags)
-{
- int res;
- uint16_t val;
-
- res = i2c_xfer(port, slave_addr_flags,
- NULL, 0, (uint8_t *)&val, sizeof(uint16_t));
-
- if (res) {
- CPRINTS("INA2XX I2C readagain failed p:%d a:%02x",
- (int)port, (int)I2C_STRIP_FLAGS(slave_addr_flags));
- return 0x0bad;
- }
- return (val >> 8) | ((val & 0xff) << 8);
-}
-
-
-uint16_t ina2xx_read(uint8_t port, uint16_t slave_addr_flags,
- uint8_t reg)
-{
- int res;
- int val;
-
- res = i2c_read16(port, slave_addr_flags, reg, &val);
- if (res) {
- CPRINTS("INA2XX I2C read failed p:%d a:%02x, r:%02x",
- (int)port, (int)I2C_STRIP_FLAGS(slave_addr_flags),
- (int)reg);
- return 0x0bad;
- }
- return (val >> 8) | ((val & 0xff) << 8);
-}
-
-int ina2xx_write(uint8_t port, uint16_t slave_addr_flags,
- uint8_t reg, uint16_t val)
-{
- int res;
- uint16_t be_val = (val >> 8) | ((val & 0xff) << 8);
-
- res = i2c_write16(port, slave_addr_flags, reg, be_val);
- if (res)
- CPRINTS("INA2XX I2C write failed");
- return res;
-}
-
-
-
-/******************************************************************************
- * Background tasks
- *
- * Here we setup the INAs and read them at the specified interval.
- * INA samples are stored in a ringbuffer that can be fetched using the
- * USB commands.
- */
-
-/* INA231 integration and averaging time presets, indexed by register value */
-#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
-static const int average_settings[] = {
- 1, 4, 16, 64, 128, 256, 512, 1024};
-static const int conversion_time_us[] = {
- 140, 204, 332, 588, 1100, 2116, 4156, 8244};
-
-static int usb_power_init_inas(struct usb_power_config const *config)
-{
- struct usb_power_state *state = config->state;
- int i;
- int shunt_time = 0;
- int avg = 0;
- int target_us = state->integration_us;
-
- if (state->state != USB_POWER_STATE_SETUP) {
- CPRINTS("[ERROR] usb_power_init_inas while not SETUP");
- return -1;
- }
-
- /* Find an INA preset integration time less than specified */
- while (shunt_time < (NELEMS(conversion_time_us) - 1)) {
- if (conversion_time_us[shunt_time + 1] > target_us)
- break;
- shunt_time++;
- }
-
- /* Find an averaging setting from the INA presets that fits. */
- while (avg < (NELEMS(average_settings) - 1)) {
- if ((conversion_time_us[shunt_time] *
- average_settings[avg + 1])
- > target_us)
- break;
- avg++;
- }
-
- state->integration_us =
- conversion_time_us[shunt_time] * average_settings[avg];
-
- for (i = 0; i < state->ina_count; i++) {
- int value;
- int ret;
- struct usb_power_ina_cfg *ina = state->ina_cfg + i;
-
-#ifdef USB_POWER_VERBOSE
- {
- int conf, cal;
-
- conf = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_CONF);
- cal = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_CAL);
- CPRINTS("[CAP] %d (%d,0x%02x): conf:%x, cal:%x",
- i, ina->port, I2C_STRIP_FLAGS(ina->addr_flags),
- conf, cal);
- }
-#endif
- /*
- * Calculate INA231 Calibration register
- * CurrentLSB = uA per div = 80mV / (Rsh * 2^15)
- * CurrentLSB 100x uA = 100x 80000000nV / (Rsh mOhm * 0x8000)
- */
- /* TODO: allow voltage readings if no sense resistor. */
- if (ina->rs == 0)
- return -1;
-
- ina->scale = (100 * (80000000 / 0x8000)) / ina->rs;
-
- /*
- * CAL = .00512 / (CurrentLSB * Rsh)
- * CAL = 5120000 / (uA * mOhm)
- */
- if (ina->scale == 0)
- return -1;
- value = (5120000 * 100) / (ina->scale * ina->rs);
- ret = ina2xx_write(ina->port, ina->addr_flags,
- INA231_REG_CAL, value);
- if (ret != EC_SUCCESS) {
- CPRINTS("[CAP] usb_power_init_inas CAL FAIL: %d", ret);
- return ret;
- }
-#ifdef USB_POWER_VERBOSE
- {
- int actual;
-
- actual = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_CAL);
- CPRINTS("[CAP] scale: %d uA/div, %d uW/div, cal:%x act:%x",
- ina->scale / 100, ina->scale*25/100, value, actual);
- }
-#endif
- /* Conversion time, shunt + bus, set average. */
- value = INA231_CONF_MODE(INA231_MODE_BOTH) |
- INA231_CONF_SHUNT_TIME(shunt_time) |
- INA231_CONF_BUS_TIME(shunt_time) |
- INA231_CONF_AVG(avg);
- ret = ina2xx_write(ina->port, ina->addr_flags,
- INA231_REG_CONF, value);
- if (ret != EC_SUCCESS) {
- CPRINTS("[CAP] usb_power_init_inas CONF FAIL: %d", ret);
- return ret;
- }
-#ifdef USB_POWER_VERBOSE
- {
- int actual;
-
- actual = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_CONF);
- CPRINTS("[CAP] %d (%d,0x%02x): conf:%x, act:%x",
- i, ina->port, I2C_STRIP_FLAGS(ina->addr_flags),
- value, actual);
- }
-#endif
-#ifdef USB_POWER_VERBOSE
- {
- int busv_mv =
- (ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_BUSV)
- * 125) / 100;
-
- CPRINTS("[CAP] %d (%d,0x%02x): busv:%dmv",
- i, ina->port, I2C_STRIP_FLAGS(ina->addr_flags),
- busv_mv);
- }
-#endif
- /* Initialize read from power register. This register address
- * will be cached and all ina2xx_readagain() calls will read
- * from the same address.
- */
- ina2xx_read(ina->port, ina->addr_flags,
- reg_type_mapping(ina->type));
-#ifdef USB_POWER_VERBOSE
- CPRINTS("[CAP] %d (%d,0x%02x): type:%d", (int)(ina->type));
-#endif
- }
-
- return EC_SUCCESS;
-}
-
-
-/*
- * Read each INA's power integration measurement.
- *
- * INAs recall the most recent address, so no register access write is
- * necessary, simply read 16 bits from each INA and fill the result into
- * the power record.
- *
- * If the power record ringbuffer is full, fail with USB_POWER_ERROR_OVERFLOW.
- */
-static int usb_power_get_samples(struct usb_power_config const *config)
-{
- uint64_t time = get_time().val;
- struct usb_power_state *state = config->state;
- struct usb_power_report *r = (struct usb_power_report *)(
- state->reports_data_area +
- (USB_POWER_RECORD_SIZE(state->ina_count)
- * state->reports_head));
- struct usb_power_ina_cfg *inas = state->ina_cfg;
- int i;
-
- /* TODO(nsanders): Would we prefer to evict oldest? */
- if (((state->reports_head + 1) % USB_POWER_MAX_CACHED(state->ina_count))
- == state->reports_xmit_active) {
- CPRINTS("Overflow! h:%d a:%d t:%d (%d)",
- state->reports_head, state->reports_xmit_active,
- state->reports_tail,
- USB_POWER_MAX_CACHED(state->ina_count));
- return USB_POWER_ERROR_OVERFLOW;
- }
-
- r->status = USB_POWER_SUCCESS;
- r->size = state->ina_count;
- if (config->state->wall_offset)
- time = time + config->state->wall_offset;
- else
- time -= config->state->base_time;
- r->timestamp = time;
-
- for (i = 0; i < state->ina_count; i++) {
- int regval;
- struct usb_power_ina_cfg *ina = inas + i;
-
- /* Read INA231.
- * ina2xx_read(ina->port, ina->addr, INA231_REG_PWR);
- * Readagain cached this address so we'll save an I2C
- * transaction.
- */
- if (ina->shared)
- regval = ina2xx_read(ina->port, ina->addr_flags,
- reg_type_mapping(ina->type));
- else
- regval = ina2xx_readagain(ina->port,
- ina->addr_flags);
- r->power[i] = regval;
-#ifdef USB_POWER_VERBOSE
- {
- int current;
- int power;
- int voltage;
- int bvoltage;
-
- voltage = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_RSHV);
- bvoltage = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_BUSV);
- current = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_CURR);
- power = ina2xx_read(ina->port, ina->addr_flags,
- INA231_REG_PWR);
- {
- int uV = ((int)voltage * 25) / 10;
- int mV = ((int)bvoltage * 125) / 100;
- int uA = (uV * 1000) / ina->rs;
- int CuA = (((int)current * ina->scale) / 100);
- int uW = (((int)power * ina->scale*25)/100);
-
- CPRINTS("[CAP] %d (%d,0x%02x): %dmV / %dmO = %dmA",
- i, ina->port, I2C_STRIP_FLAGS(ina->addr_flags),
- uV/1000, ina->rs, uA/1000);
- CPRINTS("[CAP] %duV %dmV %duA %dCuA "
- "%duW v:%04x, b:%04x, p:%04x",
- uV, mV, uA, CuA, uW, voltage, bvoltage, power);
- }
- }
-#endif
- }
-
- /* Mark this slot as used. */
- state->reports_head = (state->reports_head + 1) %
- USB_POWER_MAX_CACHED(state->ina_count);
-
- return EC_SUCCESS;
-}
-
-/*
- * This function is called every [interval] uS, and reads the accumulated
- * values of the INAs, and reschedules itself for the next interval.
- *
- * It will stop collecting frames if a ringbuffer overflow is
- * detected, or a stop request is seen..
- */
-void usb_power_deferred_cap(struct usb_power_config const *config)
-{
- int ret;
- uint64_t timeout = get_time().val + config->state->integration_us;
- uint64_t timein;
-
- /* Exit if we have stopped capturing in the meantime. */
- if (config->state->state != USB_POWER_STATE_CAPTURING)
- return;
-
- /* Get samples for this timeslice */
- ret = usb_power_get_samples(config);
- if (ret == USB_POWER_ERROR_OVERFLOW) {
- CPRINTS("[CAP] usb_power_deferred_cap: OVERFLOW");
- return;
- }
-
- /* Calculate time remaining until next slice. */
- timein = get_time().val;
- if (timeout > timein)
- timeout = timeout - timein;
- else
- timeout = 0;
-
- /* Double check if we are still capturing. */
- if (config->state->state == USB_POWER_STATE_CAPTURING)
- hook_call_deferred(config->deferred_cap, timeout);
-}
-