diff options
Diffstat (limited to 'chip/stm32/usb_power.c')
-rw-r--r-- | chip/stm32/usb_power.c | 733 |
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); -} - |