mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-27 18:25:05 +00:00
sweetberry: add usb fw update
Port USB firmware update to stm32f4 dwc usb from st usb. This includes usb dwc usb stream inplementation, generic endpoint interfaces, and the sweetberry test case. BUG=chromium:608039 TEST=usb update works BRANCH=None Change-Id: Ia26e4f7e990ee64991468799c99b036f5f32190f Signed-off-by: Nick Sanders <nsanders@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/377520 Reviewed-by: Randall Spangler <rspangler@chromium.org>
This commit is contained in:
@@ -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)))
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 */
|
||||
|
||||
107
chip/stm32/usb_dwc_stream.c
Normal file
107
chip/stm32/usb_dwc_stream.c
Normal file
@@ -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,
|
||||
};
|
||||
236
chip/stm32/usb_dwc_stream.h
Normal file
236
chip/stm32/usb_dwc_stream.h
Normal file
@@ -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 */
|
||||
10
chip/stm32/usb_dwc_update.h
Normal file
10
chip/stm32/usb_dwc_update.h
Normal file
@@ -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 */
|
||||
14
extra/usb_updater/sweetberry.json
Normal file
14
extra/usb_updater/sweetberry.json
Normal file
@@ -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"]
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user