1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
|
/* 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:
*
* Commands are a 16 bit value, with optional command dependent data.
* +--------------+-----------------------------------+
* | command : 2B | |
* +--------------+-----------------------------------+
*
* Responses are an 8 bit status value, with optional data.
* +----------+-----------------------------------+
* | res : 1B | |
* +----------+-----------------------------------+
*
* reset: 0x0000
* +--------+
* | 0x0000 |
* +--------+
*
* stop: 0x0001
* +--------+
* | 0x0001 |
* +--------+
*
* addina: 0x0002
* +--------+--------------------------+-------------+--------------+-----------+--------+
* | 0x0002 | 1B: 4b: extender 4b: bus | 1B:INA type | 1B: INA addr | 1B:
*extra | 4B: Rs |
* +--------+--------------------------+-------------+--------------+-----------+--------+
*
* start: 0x0003
* +--------+----------------------+
* | 0x0003 | 4B: integration time |
* +--------+----------------------+
*
* start response:
* +-------------+-----------------------------+
* | status : 1B | Actual integration time: 4B |
* +-------------+-----------------------------+
*
* next: 0x0004
* +--------+
* | 0x0004 |
* +--------+
*
* next response:
* +-------------+----------+----------------+----------------------------+
* | status : 1B | size: 1B | timestamp : 8B | payload : may span packets |
* +-------------+----------+----------------+----------------------------+
*
* settime: 0x0005
* +--------+---------------------+
* | 0x0005 | 8B: Wall clock time |
* +--------+---------------------+
*
*
* 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.
* 0x09: Invalid input, eg. invalid INA type.
* 0x80: Unknown error
*
* size: 1 byte incoming INA reads count
*
* timestamp: 4 byte timestamp associated with these samples
*
*/
/* 8b status field. */
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_INVAL = 0x09,
USB_POWER_ERROR_UNKNOWN = 0x80,
};
/* 16b command field. */
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,
};
/* Addina "INA Type" field. */
enum usb_power_ina_type {
USBP_INA231_POWER = 0x01,
USBP_INA231_BUSV = 0x02,
USBP_INA231_CURRENT = 0x03,
USBP_INA231_SHUNTV = 0x04,
};
/* Internal state machine values */
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 */
uint16_t addr_flags;
/* Base voltage. mV */
int mv;
/* Shunt resistor. mOhm */
int rs;
/* uA per div as reported from INA */
int scale;
/* Is this power, shunt voltage, bus voltage, or current? */
int type;
/* Is this INA returning the one value only and can use readagain? */
int shared;
};
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_flags;
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 */
|