/* 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. */ #ifndef __CROS_EC_USB_POWER_H #define __CROS_EC_USB_POWER_H /* Power monitoring USB interface for Chrome EC */ #include "compile_time_macros.h" #include "hooks.h" #include "usb_descriptor.h" #include "usb_hw.h" /* * Command: * +--------------+-----------------------------------+ * | command : 2B | | * +--------------+-----------------------------------+ * * command: 2 bytes * * reset: 0x0000 * * +--------+ * | 0x0000 | * +--------+ * * stop: 0x0001 * * +--------+ * | 0x0001 | * +--------+ * * addina: 0x0002 * * +--------+--------------------------+-------------+--------------+-----------+-------------+--------+ * | 0x0002 | 1B: 4b: extender 4b: bus | 1B:INA type | 1B: INA addr | 1B: extra | 4B: voltage | 4B: Rs | * +--------+--------------------------+-------------+--------------+-----------+-------------+--------+ * * start: 0x0003 * * +--------+----------------------+ * | 0x0003 | 4B: integration time | * +--------+----------------------+ * * next: 0x0004 * * +--------+ * | 0x0004 | * +--------+ * * settime: 0x0005 * * +--------+---------------------+ * | 0x0005 | 8B: Wall clock time | * +--------+---------------------+ * * * * Response: * +-------------+----------+----------------+------------------+ * | status : 1B | size: 1B | timestamp : 8B | payload : <= 58B | Pad to multiple of 4 byte. * +-------------+----------+----------------+------------------+ * * status: 1 byte status * 0x00: Success * 0x01: I2C Error * 0x02: Overflow * This can happen if data acquisition is faster than USB reads. * 0x03: No configuration set. * 0x04: No active capture. * 0x05: Timeout. * 0x06: Busy, outgoing queue is empty. * 0x07: Size, command length is incorrect for command type.. * 0x08: More INAs specified than board limit. * 0x80: Unknown error * * size: 1 byte incoming INA reads count * * timestamp: 4 byte timestamp associated with these samples * * read payload: up to 58 bytes of data, 29x INA reads of current * */ enum usb_power_error { USB_POWER_SUCCESS = 0x00, USB_POWER_ERROR_I2C = 0x01, USB_POWER_ERROR_OVERFLOW = 0x02, USB_POWER_ERROR_NOT_SETUP = 0x03, USB_POWER_ERROR_NOT_CAPTURING = 0x04, USB_POWER_ERROR_TIMEOUT = 0x05, USB_POWER_ERROR_BUSY = 0x06, USB_POWER_ERROR_READ_SIZE = 0x07, USB_POWER_ERROR_FULL = 0x08, USB_POWER_ERROR_UNKNOWN = 0x80, }; enum usb_power_command { USB_POWER_CMD_RESET = 0x0000, USB_POWER_CMD_STOP = 0x0001, USB_POWER_CMD_ADDINA = 0x0002, USB_POWER_CMD_START = 0x0003, USB_POWER_CMD_NEXT = 0x0004, USB_POWER_CMD_SETTIME = 0x0005, }; enum usb_power_states { USB_POWER_STATE_OFF = 0, USB_POWER_STATE_SETUP, USB_POWER_STATE_CAPTURING, }; #define USB_POWER_MAX_READ_COUNT 64 #define USB_POWER_MIN_CACHED 10 struct usb_power_ina_cfg { /* * Relevant config for INA usage. */ /* i2c bus. TODO(nsanders): specify what kind of index. */ int port; /* 7-bit i2c addr */ int addr; /* Base voltage. mV */ int mv; /* Shunt resistor. mOhm */ int rs; /* uA per div as reported from INA */ int scale; }; struct __attribute__ ((__packed__)) usb_power_report { uint8_t status; uint8_t size; uint64_t timestamp; uint16_t power[USB_POWER_MAX_READ_COUNT]; }; /* Must be 4 byte aligned */ #define USB_POWER_RECORD_SIZE(ina_count) \ ((((sizeof(struct usb_power_report) \ - (sizeof(uint16_t) * USB_POWER_MAX_READ_COUNT) \ + (sizeof(uint16_t) * (ina_count))) + 3) / 4) * 4) #define USB_POWER_DATA_SIZE \ (sizeof(struct usb_power_report) * (USB_POWER_MIN_CACHED + 1)) #define USB_POWER_MAX_CACHED(ina_count) \ (USB_POWER_DATA_SIZE / USB_POWER_RECORD_SIZE(ina_count)) struct usb_power_state { /* * The power data acquisition must be setup, then started, in order to * return data. * States are OFF, SETUP, and CAPTURING. */ int state; struct usb_power_ina_cfg ina_cfg[USB_POWER_MAX_READ_COUNT]; int ina_count; int integration_us; /* Start of sampling. */ uint64_t base_time; /* Offset between microcontroller timestamp and host wall clock. */ uint64_t wall_offset; /* Cached power reports for sending on USB. */ /* Actual backing data for variable sized record queue. */ uint8_t reports_data_area[USB_POWER_DATA_SIZE]; /* Size of power report struct for this config. */ int stride_bytes; /* Max power records storeable in this config */ int max_cached; struct usb_power_report *reports; /* Head and tail pointers for output ringbuffer */ /* Head adds newly probed power data. */ int reports_head; /* Tail contains oldest records not yet sent to USB */ int reports_tail; /* Xmit_active -> tail is active usb DMA */ int reports_xmit_active; /* Pointers to RAM. */ uint8_t rx_buf[USB_MAX_PACKET_SIZE]; uint8_t tx_buf[USB_MAX_PACKET_SIZE * 4]; }; /* * Compile time Per-USB gpio configuration stored in flash. Instances of this * structure are provided by the user of the USB gpio. This structure binds * together all information required to operate a USB gpio. */ struct usb_power_config { /* In RAM state of the USB power interface. */ struct usb_power_state *state; /* USB endpoint state.*/ struct dwc_usb_ep *ep; /* Interface and endpoint indicies. */ int interface; int endpoint; /* Deferred function to call to handle power request. */ const struct deferred_data *deferred; const struct deferred_data *deferred_cap; }; struct __attribute__ ((__packed__)) usb_power_command_start { uint16_t command; uint32_t integration_us; }; struct __attribute__ ((__packed__)) usb_power_command_addina { uint16_t command; uint8_t port; uint8_t type; uint8_t addr; uint8_t extra; uint32_t rs; }; struct __attribute__ ((__packed__)) usb_power_command_settime { uint16_t command; uint64_t time; }; union usb_power_command_data { uint16_t command; struct usb_power_command_start start; struct usb_power_command_addina addina; struct usb_power_command_settime settime; }; /* * Convenience macro for defining a USB INA Power driver. * * NAME is used to construct the names of the trampoline functions and the * usb_power_config struct, the latter is just called NAME. * * INTERFACE is the index of the USB interface to associate with this * driver. * * ENDPOINT is the index of the USB bulk endpoint used for receiving and * transmitting bytes. */ #define USB_POWER_CONFIG(NAME, \ INTERFACE, \ ENDPOINT) \ static void CONCAT2(NAME, _deferred_tx_)(void); \ DECLARE_DEFERRED(CONCAT2(NAME, _deferred_tx_)); \ static void CONCAT2(NAME, _deferred_rx_)(void); \ DECLARE_DEFERRED(CONCAT2(NAME, _deferred_rx_)); \ static void CONCAT2(NAME, _deferred_cap_)(void); \ DECLARE_DEFERRED(CONCAT2(NAME, _deferred_cap_)); \ struct usb_power_state CONCAT2(NAME, _state_) = { \ .state = USB_POWER_STATE_OFF, \ .ina_count = 0, \ .integration_us = 0, \ .reports_head = 0, \ .reports_tail = 0, \ .wall_offset = 0, \ }; \ static struct dwc_usb_ep CONCAT2(NAME, _ep_ctl) = { \ .max_packet = USB_MAX_PACKET_SIZE, \ .tx_fifo = ENDPOINT, \ .out_pending = 0, \ .out_data = 0, \ .out_databuffer = 0, \ .out_databuffer_max = 0, \ .rx_deferred = &CONCAT2(NAME, _deferred_rx__data), \ .in_packets = 0, \ .in_pending = 0, \ .in_data = 0, \ .in_databuffer = 0, \ .in_databuffer_max = 0, \ .tx_deferred = &CONCAT2(NAME, _deferred_tx__data), \ }; \ struct usb_power_config const NAME = { \ .state = &CONCAT2(NAME, _state_), \ .ep = &CONCAT2(NAME, _ep_ctl), \ .interface = INTERFACE, \ .endpoint = ENDPOINT, \ .deferred_cap = &CONCAT2(NAME, _deferred_cap__data), \ }; \ const struct usb_interface_descriptor \ USB_IFACE_DESC(INTERFACE) = { \ .bLength = USB_DT_INTERFACE_SIZE, \ .bDescriptorType = USB_DT_INTERFACE, \ .bInterfaceNumber = INTERFACE, \ .bAlternateSetting = 0, \ .bNumEndpoints = 2, \ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, \ .bInterfaceSubClass = USB_SUBCLASS_GOOGLE_POWER, \ .bInterfaceProtocol = USB_PROTOCOL_GOOGLE_POWER, \ .iInterface = 0, \ }; \ const struct usb_endpoint_descriptor \ USB_EP_DESC(INTERFACE, 0) = { \ .bLength = USB_DT_ENDPOINT_SIZE, \ .bDescriptorType = USB_DT_ENDPOINT, \ .bEndpointAddress = 0x80 | ENDPOINT, \ .bmAttributes = 0x02 /* Bulk IN */, \ .wMaxPacketSize = USB_MAX_PACKET_SIZE, \ .bInterval = 1, \ }; \ const struct usb_endpoint_descriptor \ USB_EP_DESC(INTERFACE, 1) = { \ .bLength = USB_DT_ENDPOINT_SIZE, \ .bDescriptorType = USB_DT_ENDPOINT, \ .bEndpointAddress = ENDPOINT, \ .bmAttributes = 0x02 /* Bulk OUT */, \ .wMaxPacketSize = USB_MAX_PACKET_SIZE, \ .bInterval = 0, \ }; \ static void CONCAT2(NAME, _ep_tx_) (void) { usb_epN_tx(ENDPOINT); } \ static void CONCAT2(NAME, _ep_rx_) (void) { usb_epN_rx(ENDPOINT); } \ static void CONCAT2(NAME, _ep_event_)(enum usb_ep_event evt) \ { \ usb_power_event(&NAME, evt); \ } \ USB_DECLARE_EP(ENDPOINT, \ CONCAT2(NAME, _ep_tx_), \ CONCAT2(NAME, _ep_rx_), \ CONCAT2(NAME, _ep_event_)); \ static void CONCAT2(NAME, _deferred_tx_)(void) \ { usb_power_deferred_tx(&NAME); } \ static void CONCAT2(NAME, _deferred_rx_)(void) \ { usb_power_deferred_rx(&NAME); } \ static void CONCAT2(NAME, _deferred_cap_)(void) \ { usb_power_deferred_cap(&NAME); } /* * Handle power request in a deferred callback. */ void usb_power_deferred_rx(struct usb_power_config const *config); void usb_power_deferred_tx(struct usb_power_config const *config); void usb_power_deferred_cap(struct usb_power_config const *config); /* * These functions are used by the trampoline functions defined above to * connect USB endpoint events with the generic USB GPIO driver. */ void usb_power_tx(struct usb_power_config const *config); void usb_power_rx(struct usb_power_config const *config); void usb_power_event(struct usb_power_config const *config, enum usb_ep_event evt); #endif /* __CROS_EC_USB_DWC_POWER_H */