diff --git a/board/sweetberry/board.c b/board/sweetberry/board.c index 5c29b553ee..7f1018d315 100644 --- a/board/sweetberry/board.c +++ b/board/sweetberry/board.c @@ -14,10 +14,12 @@ #include "registers.h" #include "stm32-dma.h" #include "task.h" +#include "update_fw.h" #include "usb_descriptor.h" #include "util.h" #include "usb_dwc_hw.h" #include "usb_dwc_console.h" +#include "usb_dwc_update.h" /****************************************************************************** * Define the strings used in our USB descriptors. @@ -29,6 +31,7 @@ const void *const usb_strings[] = { [USB_STR_SERIALNO] = USB_STRING_DESC("1234-a"), [USB_STR_VERSION] = USB_STRING_DESC(CROS_EC_VERSION32), [USB_STR_CONSOLE_NAME] = USB_STRING_DESC("Sweetberry EC Shell"), + [USB_STR_UPDATE_NAME] = USB_STRING_DESC("Firmware update"), }; BUILD_ASSERT(ARRAY_SIZE(usb_strings) == USB_STR_COUNT); @@ -37,6 +40,7 @@ struct dwc_usb usb_ctl = { .ep = { &ep0_ctl, &ep_console_ctl, + &usb_update_ep_ctl, }, .speed = USB_SPEED_FS, .phy_type = USB_PHY_ULPI, @@ -57,6 +61,25 @@ const struct i2c_port_t i2c_ports[] = { }; const unsigned int i2c_ports_used = ARRAY_SIZE(i2c_ports); +/****************************************************************************** + * Support firmware upgrade over USB. We can update whichever section is not + * the current section. + */ + +/* + * This array defines possible sections available for the firmware update. + * The section which does not map the current executing code is picked as the + * valid update area. The values are offsets into the flash space. + */ +const struct section_descriptor board_rw_sections[] = { + {CONFIG_RO_MEM_OFF, + CONFIG_RO_MEM_OFF + CONFIG_RO_SIZE}, + {CONFIG_RW_MEM_OFF, + CONFIG_RW_MEM_OFF + CONFIG_RW_SIZE}, +}; +const struct section_descriptor * const rw_sections = board_rw_sections; +const int num_rw_sections = ARRAY_SIZE(board_rw_sections); + #define GPIO_SET_HS(bank, number) \ (STM32_GPIO_OSPEEDR(GPIO_##bank) |= (0x3 << ((number) * 2))) diff --git a/board/sweetberry/board.h b/board/sweetberry/board.h index 4bb8a13535..b73d5ad188 100644 --- a/board/sweetberry/board.h +++ b/board/sweetberry/board.h @@ -40,6 +40,8 @@ #define CONFIG_USB #define CONFIG_USB_PID 0x5020 #define CONFIG_USB_CONSOLE +#define CONFIG_STREAM_USB +#define CONFIG_USB_UPDATE #undef CONFIG_USB_MAXPOWER_MA #define CONFIG_USB_MAXPOWER_MA 100 @@ -48,13 +50,15 @@ #define DEFAULT_SERIALNO "Uninitialized" /* USB interface indexes (use define rather than enum to expand them) */ -#define USB_IFACE_CONSOLE 0 -#define USB_IFACE_COUNT 1 +#define USB_IFACE_CONSOLE 0 +#define USB_IFACE_UPDATE 1 +#define USB_IFACE_COUNT 2 /* USB endpoint indexes (use define rather than enum to expand them) */ -#define USB_EP_CONTROL 0 -#define USB_EP_CONSOLE 1 -#define USB_EP_COUNT 2 +#define USB_EP_CONTROL 0 +#define USB_EP_CONSOLE 1 +#define USB_EP_UPDATE 2 +#define USB_EP_COUNT 3 /* This is not actually a Chromium EC so disable some features. */ #undef CONFIG_WATCHDOG_HELP @@ -85,6 +89,7 @@ enum usb_strings { USB_STR_SERIALNO, USB_STR_VERSION, USB_STR_CONSOLE_NAME, + USB_STR_UPDATE_NAME, USB_STR_COUNT }; diff --git a/chip/stm32/usb-stream.h b/chip/stm32/usb-stream.h index 1d6b015371..785ee3e9a8 100644 --- a/chip/stm32/usb-stream.h +++ b/chip/stm32/usb-stream.h @@ -5,6 +5,10 @@ #ifndef __CROS_EC_USB_STREAM_H #define __CROS_EC_USB_STREAM_H +#if defined(CHIP_FAMILY_STM32F4) +#include "usb_dwc_stream.h" +#else + /* STM32 USB STREAM driver for Chrome EC */ #include "compile_time_macros.h" @@ -238,4 +242,5 @@ void usb_stream_tx(struct usb_stream_config const *config); void usb_stream_rx(struct usb_stream_config const *config); void usb_stream_reset(struct usb_stream_config const *config); +#endif /* defined(CHIP_FAMILY_STM32F4) */ #endif /* __CROS_EC_USB_STREAM_H */ diff --git a/chip/stm32/usb_dwc.c b/chip/stm32/usb_dwc.c index 628a0c1406..5af53b9446 100644 --- a/chip/stm32/usb_dwc.c +++ b/chip/stm32/usb_dwc.c @@ -173,14 +173,17 @@ struct dwc_usb_ep ep0_ctl = { .max_packet = USB_MAX_PACKET_SIZE, .tx_fifo = 0, .out_pending = 0, + .out_expected = 0, .out_data = 0, .out_databuffer = ep0_setup_buf, .out_databuffer_max = sizeof(ep0_setup_buf), + .rx_deferred = 0, .in_packets = 0, .in_pending = 0, .in_data = 0, .in_databuffer = ep0_in_buf, .in_databuffer_max = sizeof(ep0_in_buf), + .tx_deferred = 0, }; /* Overall device state (USB 2.0 spec, section 9.1.1). @@ -193,6 +196,178 @@ static enum { } device_state; static uint8_t configuration_value; + +/* True if the HW Rx/OUT FIFO is currently listening. */ +int rx_ep_is_active(uint32_t ep_num) +{ + return (GR_USB_DOEPCTL(ep_num) & DXEPCTL_EPENA) ? 1 : 0; +} + +/* Number of bytes the HW Rx/OUT FIFO has for us. + * + * @param ep_num USB endpoint + * + * @returns number of bytes ready, zero if none. + */ +int rx_ep_pending(uint32_t ep_num) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + + return ep->out_pending; +} + +/* True if the Tx/IN FIFO can take some bytes from us. */ +int tx_ep_is_ready(uint32_t ep_num) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + int ready; + + /* Is the tx hw idle? */ + ready = !(GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA); + + /* Is there no pending data? */ + ready &= (ep->in_pending == 0); + return ready; +} + +/* Write packets of data IN to the host. + * + * This function uses DMA, so the *data write buffer + * must persist until the write completion event. + * + * @param ep_num USB endpoint to write + * @param len number of bytes to write + * @param data pointer of data to write + * + * @return bytes written + */ +int usb_write_ep(uint32_t ep_num, int len, void *data) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + + /* We will send as many packets as necessary, including a final + * packet of < USB_MAX_PACKET_SIZE (maybe zero length) + */ + ep->in_packets = (len + USB_MAX_PACKET_SIZE)/USB_MAX_PACKET_SIZE; + ep->in_pending = len; + ep->in_data = data; + + GR_USB_DIEPTSIZ(ep_num) = 0; + + GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(ep->in_packets); + GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_XFERSIZE(len); + GR_USB_DIEPDMA(ep_num) = (uint32_t)ep->in_data; + + + /* We're sending this much. + * TODO: we should support sending more than one packet. + */ + ep->in_pending -= len; + ep->in_packets -= ep->in_packets; + ep->in_data += len; + + /* We are ready to enable this endpoint to start transferring data. */ + GR_USB_DIEPCTL(ep_num) |= DXEPCTL_CNAK | DXEPCTL_EPENA; + return len; +} + +/* Tx/IN interrupt handler */ +void usb_epN_tx(uint32_t ep_num) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + + uint32_t dieptsiz = GR_USB_DIEPTSIZ(ep_num); + + /* clear the Tx/IN interrupts */ + GR_USB_DIEPINT(ep_num) = 0xffffffff; + + /* Let's assume this is actually true. */ + ep->in_packets = 0; + ep->in_pending = dieptsiz & GC_USB_DIEPTSIZ1_XFERSIZE_MASK; + + if (ep->tx_deferred) + hook_call_deferred(ep->tx_deferred, 0); +} + +/* Read a packet of data OUT from the host. + * + * This function uses DMA, so the *data write buffer + * must persist until the read completion event. + * + * @param ep_num USB endpoint to read + * @param len number of bytes to read + * @param data pointer of data to read + * + * @return EC_SUCCESS on success + */ +int usb_read_ep(uint32_t ep_num, int len, void *data) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + int packets = (len + USB_MAX_PACKET_SIZE)/USB_MAX_PACKET_SIZE; + + ep->out_data = data; + ep->out_pending = 0; + ep->out_expected = len; + + GR_USB_DOEPTSIZ(ep_num) = 0; + GR_USB_DOEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(packets); + GR_USB_DOEPTSIZ(ep_num) |= DXEPTSIZ_XFERSIZE(len); + GR_USB_DOEPDMA(ep_num) = (uint32_t)ep->out_data; + + GR_USB_DOEPCTL(ep_num) |= DXEPCTL_CNAK | DXEPCTL_EPENA; + return EC_SUCCESS; +} + +/* Rx/OUT endpoint interrupt handler */ +void usb_epN_rx(uint32_t ep_num) +{ + struct dwc_usb_ep *ep = usb_ctl.ep[ep_num]; + + /* Still receiving data. Let's wait. */ + if (rx_ep_is_active(ep_num)) + return; + + /* Bytes received decrement DOEPTSIZ XFERSIZE */ + if (GR_USB_DOEPINT(ep_num) & DOEPINT_XFERCOMPL) { + if (ep->out_expected > 0) { + ep->out_pending = + ep->out_expected - + (GR_USB_DOEPTSIZ(ep_num) & + GC_USB_DOEPTSIZ1_XFERSIZE_MASK); + } else { + CPRINTF("usb_ep%d_rx: unexpected RX DOEPTSIZ %08x\n", + ep_num, GR_USB_DOEPTSIZ(ep_num)); + ep->out_pending = 0; + } + ep->out_expected = 0; + GR_USB_DOEPTSIZ(ep_num) = 0; + } + + /* clear the RX/OUT interrupts */ + GR_USB_DOEPINT(ep_num) = 0xffffffff; + + if (ep->rx_deferred) + hook_call_deferred(ep->rx_deferred, 0); +} + +/* Reset endpoint HW block. */ +void epN_reset(uint32_t ep_num) +{ + GR_USB_DOEPCTL(ep_num) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) | + DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK; + GR_USB_DIEPCTL(ep_num) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) | + DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | + DXEPCTL_TXFNUM(ep_num); + GR_USB_DAINTMSK |= DAINT_INEP(ep_num) | + DAINT_OUTEP(ep_num); +} + + +/****************************************************************************** + * Internal and EP0 functions. + */ + + static void flush_all_fifos(void) { /* Flush all FIFOs according to Section 2.1.1.2 */ @@ -213,9 +388,9 @@ int send_in_packet(uint32_t ep_num) return -1; } - GR_USB_DIEPTSIZ(0) = 0; + GR_USB_DIEPTSIZ(ep_num) = 0; - GR_USB_DIEPTSIZ(0) |= DXEPTSIZ_PKTCNT(1); + GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(1); GR_USB_DIEPTSIZ(0) |= DXEPTSIZ_XFERSIZE(len); GR_USB_DIEPDMA(0) = (uint32_t)ep->in_data; @@ -415,6 +590,9 @@ static int handle_setup_with_in_stage(enum table_case tc, case USB_DT_DEVICE_QUALIFIER: /* We're not high speed */ return -1; + case USB_DT_DEBUG: + /* Not supported */ + return -1; default: report_error(type); return -1; @@ -1122,6 +1300,24 @@ void usb_release(void) enable_sleep(SLEEP_MASK_USB_DEVICE); } +/* Print USB info and stats */ +static void usb_info(void) +{ + struct dwc_usb *usb = &usb_ctl; + int i; + + CPRINTF("USB settings: %s%s%s\n", + usb->speed == USB_SPEED_FS ? "FS " : "HS ", + usb->phy_type == USB_PHY_INTERNAL ? "Internal Phy " : "ULPI ", + usb->dma_en ? "DMA " : ""); + + for (i = 0; i < USB_EP_COUNT; i++) { + CPRINTF("Endpoint %d activity: %s%s\n", i, + rx_ep_is_active(i) ? "RX " : "", + tx_ep_is_ready(i) ? "" : "TX "); + } +} + static int command_usb(int argc, char **argv) { if (argc > 1) { @@ -1129,12 +1325,15 @@ static int command_usb(int argc, char **argv) usb_init(); else if (!strcasecmp("off", argv[1])) usb_release(); + else if (!strcasecmp("info", argv[1])) + usb_info(); + return EC_SUCCESS; } - return EC_SUCCESS; + return EC_ERROR_PARAM1; } DECLARE_CONSOLE_COMMAND(usb, command_usb, - "[on|off|a|b]", + "[on|off|info]", "Get/set the USB connection state and PHY selection"); #ifdef CONFIG_USB_SERIALNO diff --git a/chip/stm32/usb_dwc_console.c b/chip/stm32/usb_dwc_console.c index 9e2e895ddb..b462df6a56 100644 --- a/chip/stm32/usb_dwc_console.c +++ b/chip/stm32/usb_dwc_console.c @@ -28,23 +28,23 @@ static int is_readonly; /* USB-Serial descriptors */ const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) = { - .bLength = USB_DT_INTERFACE_SIZE, - .bDescriptorType = USB_DT_INTERFACE, - .bInterfaceNumber = USB_IFACE_CONSOLE, - .bAlternateSetting = 0, - .bNumEndpoints = 2, - .bInterfaceClass = USB_CLASS_VENDOR_SPEC, - .bInterfaceSubClass = USB_SUBCLASS_GOOGLE_SERIAL, - .bInterfaceProtocol = USB_PROTOCOL_GOOGLE_SERIAL, - .iInterface = USB_STR_CONSOLE_NAME, + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = USB_IFACE_CONSOLE, + .bAlternateSetting = 0, + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .bInterfaceSubClass = USB_SUBCLASS_GOOGLE_SERIAL, + .bInterfaceProtocol = USB_PROTOCOL_GOOGLE_SERIAL, + .iInterface = USB_STR_CONSOLE_NAME, }; const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 0) = { - .bLength = USB_DT_ENDPOINT_SIZE, - .bDescriptorType = USB_DT_ENDPOINT, - .bEndpointAddress = 0x80 | USB_EP_CONSOLE, - .bmAttributes = 0x02 /* Bulk IN */, - .wMaxPacketSize = USB_MAX_PACKET_SIZE, - .bInterval = 10 + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0x80 | USB_EP_CONSOLE, + .bmAttributes = 0x02 /* Bulk IN */, + .wMaxPacketSize = USB_MAX_PACKET_SIZE, + .bInterval = 10, }; const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 1) = { .bLength = USB_DT_ENDPOINT_SIZE, @@ -218,14 +218,8 @@ static void con_ep_tx(void) static void ep_reset(void) { - GR_USB_DOEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) | - DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | - DXEPCTL_CNAK | DXEPCTL_EPENA; - GR_USB_DIEPCTL(USB_EP_CONSOLE) = DXEPCTL_MPS(USB_MAX_PACKET_SIZE) | - DXEPCTL_USBACTEP | DXEPCTL_EPTYPE_BULK | - DXEPCTL_TXFNUM(USB_EP_CONSOLE); - GR_USB_DAINTMSK |= DAINT_INEP(USB_EP_CONSOLE) | - DAINT_OUTEP(USB_EP_CONSOLE); + epN_reset(USB_EP_CONSOLE); + is_reset = 1; /* Flush any queued data */ diff --git a/chip/stm32/usb_dwc_hw.h b/chip/stm32/usb_dwc_hw.h index 36361ab371..01ff3b7e77 100644 --- a/chip/stm32/usb_dwc_hw.h +++ b/chip/stm32/usb_dwc_hw.h @@ -30,6 +30,56 @@ struct usb_setup_packet; /* EP0 Interface handler callbacks */ static int (*usb_iface_request[]) (struct usb_setup_packet *req); + +/* True if the HW Rx/OUT FIFO is currently listening. */ +int rx_ep_is_active(uint32_t ep_num); + +/* Number of bytes the HW Rx/OUT FIFO has for us. + * + * @param ep_num USB endpoint + * + * @returns number of bytes ready, zero if none. + */ +int rx_ep_pending(uint32_t ep_num); + +/* True if the Tx/IN FIFO can take some bytes from us. */ +int tx_ep_is_ready(uint32_t ep_num); + +/* Write packets of data IN to the host. + * + * This function uses DMA, so the *data write buffer + * must persist until the write completion event. + * + * @param ep_num USB endpoint to write + * @param len number of bytes to write + * @param data pointer of data to write + * + * @return bytes written + */ +int usb_write_ep(uint32_t ep_num, int len, void *data); + +/* Read a packet of data OUT from the host. + * + * This function uses DMA, so the *data write buffer + * must persist until the read completion event. + * + * @param ep_num USB endpoint to read + * @param len number of bytes to read + * @param data pointer of data to read + * + * @return EC_SUCCESS on success + */ +int usb_read_ep(uint32_t ep_num, int len, void *data); + +/* Tx/IN interrupt handler */ +void usb_epN_tx(uint32_t ep_num); + +/* Rx/OUT endpoint interrupt handler */ +void usb_epN_rx(uint32_t ep_num); + +/* Reset endpoint HW block. */ +void epN_reset(uint32_t ep_num); + /* * Declare any interface-specific control request handlers. These Setup packets * arrive on the control endpoint (EP0), but are handled by the interface code. diff --git a/chip/stm32/usb_dwc_registers.h b/chip/stm32/usb_dwc_registers.h index 0e29f6aad1..9d656df22d 100644 --- a/chip/stm32/usb_dwc_registers.h +++ b/chip/stm32/usb_dwc_registers.h @@ -14,15 +14,18 @@ struct dwc_usb_ep { int tx_fifo; int out_pending; + int out_expected; uint8_t *out_data; uint8_t *out_databuffer; int out_databuffer_max; + const struct deferred_data *rx_deferred; int in_packets; int in_pending; uint8_t *in_data; uint8_t *in_databuffer; int in_databuffer_max; + const struct deferred_data *tx_deferred; }; /* USB state */ diff --git a/chip/stm32/usb_dwc_stream.c b/chip/stm32/usb_dwc_stream.c new file mode 100644 index 0000000000..d7ec6fb5b8 --- /dev/null +++ b/chip/stm32/usb_dwc_stream.c @@ -0,0 +1,107 @@ +/* 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 "registers.h" +#include "timer.h" +#include "usb_dwc_stream.h" +#include "util.h" + +#include "console.h" +#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) + +/* + * This function tries to shove new bytes from the USB host into the queue for + * consumption elsewhere. It is invoked either by a HW interrupt (telling us we + * have new bytes from the USB host), or by whoever is reading bytes out of the + * other end of the queue (telling us that there's now more room in the queue + * if we still have bytes to shove in there). + */ +int rx_stream_handler(struct usb_stream_config const *config) +{ + int rx_count = rx_ep_pending(config->endpoint); + + /* If we have some, try to shove them into the queue */ + if (rx_count) { + size_t added = QUEUE_ADD_UNITS( + config->producer.queue, config->rx_ram, + rx_count); + if (added != rx_count) { + CPRINTF("rx_stream_handler: failed ep%d " + "queue %d bytes, accepted %d\n", + config->endpoint, rx_count, added); + } + } + + if (!rx_ep_is_active(config->endpoint)) + usb_read_ep(config->endpoint, config->rx_size, config->rx_ram); + + return rx_count; +} + +/* Try to send some bytes to the host */ +int tx_stream_handler(struct usb_stream_config const *config) +{ + size_t count; + + if (!*(config->is_reset)) + return 0; + if (!tx_ep_is_ready(config->endpoint)) + return 0; + + count = QUEUE_REMOVE_UNITS(config->consumer.queue, config->tx_ram, + config->tx_size); + if (count) + usb_write_ep(config->endpoint, count, config->tx_ram); + + return count; +} + +/* Reset stream */ +void usb_stream_reset(struct usb_stream_config const *config) +{ + epN_reset(config->endpoint); + + *(config->is_reset) = 1; + + /* Flush any queued data */ + hook_call_deferred(config->deferred_tx, 0); + hook_call_deferred(config->deferred_rx, 0); +} + +static void usb_read(struct producer const *producer, size_t count) +{ + struct usb_stream_config const *config = + DOWNCAST(producer, struct usb_stream_config, producer); + + hook_call_deferred(config->deferred_rx, 0); +} + +static void usb_written(struct consumer const *consumer, size_t count) +{ + struct usb_stream_config const *config = + DOWNCAST(consumer, struct usb_stream_config, consumer); + + hook_call_deferred(config->deferred_tx, 0); +} + +static void usb_flush(struct consumer const *consumer) +{ + struct usb_stream_config const *config = + DOWNCAST(consumer, struct usb_stream_config, consumer); + + while (queue_count(consumer->queue)) { + tx_stream_handler(config); + usleep(10); + } +} + +struct producer_ops const usb_stream_producer_ops = { + .read = usb_read, +}; + +struct consumer_ops const usb_stream_consumer_ops = { + .written = usb_written, + .flush = usb_flush, +}; diff --git a/chip/stm32/usb_dwc_stream.h b/chip/stm32/usb_dwc_stream.h new file mode 100644 index 0000000000..df1311f03f --- /dev/null +++ b/chip/stm32/usb_dwc_stream.h @@ -0,0 +1,236 @@ +/* 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_DWC_STREAM_H +#define __CROS_EC_USB_DWC_STREAM_H + +/* USB STREAM driver for Chrome EC */ + +#include "compile_time_macros.h" +#include "consumer.h" +#include "hooks.h" +#include "registers.h" +#include "producer.h" +#include "queue.h" +#include "usb_descriptor.h" +#include "usb_dwc_hw.h" + +/* + * Compile time Per-USB stream configuration stored in flash. Instances of this + * structure are provided by the user of the USB stream. This structure binds + * together all information required to operate a USB stream. + */ +struct usb_stream_config { + /* + * Endpoint index, and pointers to the USB packet RAM buffers. + */ + int endpoint; + struct dwc_usb_ep *ep; + + int *is_reset; + int *overflow; + + /* + * Deferred function to call to handle USB and Queue request. + */ + const struct deferred_data *deferred_tx; + const struct deferred_data *deferred_rx; + + int tx_size; + int rx_size; + + uint8_t *tx_ram; + uint8_t *rx_ram; + + struct consumer consumer; + struct producer producer; +}; + +/* + * These function tables are defined by the USB Stream driver and are used to + * initialize the consumer and producer in the usb_stream_config. + */ +extern struct consumer_ops const usb_stream_consumer_ops; +extern struct producer_ops const usb_stream_producer_ops; + + +/* + * Convenience macro for defining USB streams and their associated state and + * buffers. + * + * NAME is used to construct the names of the packet RAM buffers, trampoline + * functions, usb_stream_state struct, and usb_stream_config struct, the + * latter is just called NAME. + * + * INTERFACE is the index of the USB interface to associate with this + * stream. + * + * INTERFACE_CLASS, INTERFACE_SUBCLASS, INTERFACE_PROTOCOL are the + * .bInterfaceClass, .bInterfaceSubClass, and .bInterfaceProtocol fields + * respectively in the USB interface descriptor. + * + * INTERFACE_NAME is the index of the USB string descriptor (iInterface). + * + * ENDPOINT is the index of the USB bulk endpoint used for receiving and + * transmitting bytes. + * + * RX_SIZE and TX_SIZE are the number of bytes of USB packet RAM to allocate + * for the RX and TX packets respectively. The valid values for these + * parameters are dictated by the USB peripheral. + * + * RX_QUEUE and TX_QUEUE are the names of the RX and TX queues that this driver + * should write to and read from respectively. + */ + +/* + * The following assertions can not be made because they require access to + * non-const fields, but should be kept in mind. + * + * BUILD_ASSERT(RX_QUEUE.buffer_units >= RX_SIZE); + * BUILD_ASSERT(TX_QUEUE.buffer_units >= TX_SIZE); + * BUILD_ASSERT(RX_QUEUE.unit_bytes == 1); + * BUILD_ASSERT(TX_QUEUE.unit_bytes == 1); + */ +#define USB_STREAM_CONFIG_FULL(NAME, \ + INTERFACE, \ + INTERFACE_CLASS, \ + INTERFACE_SUBCLASS, \ + INTERFACE_PROTOCOL, \ + INTERFACE_NAME, \ + ENDPOINT, \ + RX_SIZE, \ + TX_SIZE, \ + RX_QUEUE, \ + TX_QUEUE) \ + \ + static uint8_t CONCAT2(NAME, _buf_rx_)[RX_SIZE]; \ + static uint8_t CONCAT2(NAME, _buf_tx_)[TX_SIZE]; \ + static int CONCAT2(NAME, _is_reset_); \ + static int CONCAT2(NAME, _overflow_); \ + 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_)); \ + struct usb_stream_config const NAME = { \ + .endpoint = ENDPOINT, \ + .is_reset = &CONCAT2(NAME, _is_reset_), \ + .overflow = &CONCAT2(NAME, _overflow_), \ + .deferred_tx = &CONCAT2(NAME, _deferred_tx__data), \ + .deferred_rx = &CONCAT2(NAME, _deferred_rx__data), \ + .tx_size = TX_SIZE, \ + .rx_size = RX_SIZE, \ + .tx_ram = CONCAT2(NAME, _buf_tx_), \ + .rx_ram = CONCAT2(NAME, _buf_rx_), \ + .consumer = { \ + .queue = &TX_QUEUE, \ + .ops = &usb_stream_consumer_ops, \ + }, \ + .producer = { \ + .queue = &RX_QUEUE, \ + .ops = &usb_stream_producer_ops, \ + }, \ + }; \ + 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 = INTERFACE_CLASS, \ + .bInterfaceSubClass = INTERFACE_SUBCLASS, \ + .bInterfaceProtocol = INTERFACE_PROTOCOL, \ + .iInterface = INTERFACE_NAME, \ + }; \ + 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 = TX_SIZE, \ + .bInterval = 10, \ + }; \ + 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 = RX_SIZE, \ + .bInterval = 0, \ + }; \ + static void CONCAT2(NAME, _deferred_tx_)(void) \ + { tx_stream_handler(&NAME); } \ + static void CONCAT2(NAME, _deferred_rx_)(void) \ + { rx_stream_handler(&NAME); } \ + 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_reset)(void) \ + { \ + usb_stream_reset(&NAME); \ + } \ + struct dwc_usb_ep CONCAT2(NAME, _ep_ctl) = { \ + .max_packet = USB_MAX_PACKET_SIZE, \ + .tx_fifo = ENDPOINT, \ + .out_pending = 0, \ + .out_expected = 0, \ + .out_data = 0, \ + .out_databuffer = CONCAT2(NAME, _buf_rx_), \ + .out_databuffer_max = RX_SIZE, \ + .rx_deferred = &CONCAT2(NAME, _deferred_rx__data), \ + .in_packets = 0, \ + .in_pending = 0, \ + .in_data = 0, \ + .in_databuffer = CONCAT2(NAME, _buf_tx_), \ + .in_databuffer_max = TX_SIZE, \ + .tx_deferred = &CONCAT2(NAME, _deferred_tx__data), \ + }; \ + USB_DECLARE_EP(ENDPOINT, \ + CONCAT2(NAME, _ep_tx), \ + CONCAT2(NAME, _ep_rx), \ + CONCAT2(NAME, _ep_reset)); + +/* This is a short version for declaring Google serial endpoints */ +#define USB_STREAM_CONFIG(NAME, \ + INTERFACE, \ + INTERFACE_NAME, \ + ENDPOINT, \ + RX_SIZE, \ + TX_SIZE, \ + RX_QUEUE, \ + TX_QUEUE) \ + USB_STREAM_CONFIG_FULL(NAME, \ + INTERFACE, \ + USB_CLASS_VENDOR_SPEC, \ + USB_SUBCLASS_GOOGLE_SERIAL, \ + USB_PROTOCOL_GOOGLE_SERIAL, \ + INTERFACE_NAME, \ + ENDPOINT, \ + RX_SIZE, \ + TX_SIZE, \ + RX_QUEUE, \ + TX_QUEUE) + +/* + * Handle USB and Queue request in a deferred callback. + */ +int rx_stream_handler(struct usb_stream_config const *config); +int tx_stream_handler(struct usb_stream_config const *config); + +/* + * These functions are used by the trampoline functions defined above to + * connect USB endpoint events with the generic USB stream driver. + */ +void usb_stream_tx(struct usb_stream_config const *config); +void usb_stream_rx(struct usb_stream_config const *config); +void usb_stream_reset(struct usb_stream_config const *config); + +#endif /* __CROS_EC_USB_STREAM_H */ diff --git a/chip/stm32/usb_dwc_update.h b/chip/stm32/usb_dwc_update.h new file mode 100644 index 0000000000..6d79f3aca9 --- /dev/null +++ b/chip/stm32/usb_dwc_update.h @@ -0,0 +1,10 @@ +/* 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_STM32_USB_DWC_UPDATE_H +#define __CROS_EC_STM32_USB_DWC_UPDATE_H + +extern struct dwc_usb_ep usb_update_ep_ctl; + +#endif /* __CROS_EC_STM32_USB_DWC_UPDATE_H */ diff --git a/extra/usb_updater/sweetberry.json b/extra/usb_updater/sweetberry.json new file mode 100644 index 0000000000..121ca0baf9 --- /dev/null +++ b/extra/usb_updater/sweetberry.json @@ -0,0 +1,14 @@ +{ + "Comment": "This file describes the updateable sections of the flash.", + "board": "sweetberry", + "vid": "0x18d1", + "pid": "0x5020", + "Comment on flash": "This is the base address of writeable flash", + "flash": "0x8000000", + "Comment on region format": "name: [baseoffset, length]", + "regions": { + "RW": ["0x10000", "0x10000"], + "PSTATE": ["0xc000", "0x4000"], + "RO": ["0x0000", "0xc000"] + } +}