diff options
Diffstat (limited to 'chip/stm32/usb_dwc.c')
-rw-r--r-- | chip/stm32/usb_dwc.c | 1423 |
1 files changed, 0 insertions, 1423 deletions
diff --git a/chip/stm32/usb_dwc.c b/chip/stm32/usb_dwc.c deleted file mode 100644 index f4ee89f1f0..0000000000 --- a/chip/stm32/usb_dwc.c +++ /dev/null @@ -1,1423 +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 "clock.h" -#include "common.h" -#include "config.h" -#include "console.h" -#include "flash.h" -#include "gpio.h" -#include "hooks.h" -#include "link_defs.h" -#include "registers.h" -#include "usb_hw.h" -#include "system.h" -#include "task.h" -#include "timer.h" -#include "util.h" -#include "usb_descriptor.h" -#include "watchdog.h" - - -/****************************************************************************/ -/* Debug output */ - -/* Console output macro */ -#define CPRINTS(format, args...) cprints(CC_USB, format, ## args) -#define CPRINTF(format, args...) cprintf(CC_USB, format, ## args) - -/* TODO: Something unexpected happened. Figure out how to report & fix it. */ -#define report_error(val) \ - CPRINTS("Unhandled USB event at %s line %d: 0x%x", \ - __FILE__, __LINE__, val) - - -/****************************************************************************/ -/* Standard USB stuff */ - -#ifdef CONFIG_USB_BOS -/* v2.10 (vs 2.00) BOS Descriptor provided */ -#define USB_DEV_BCDUSB 0x0210 -#else -#define USB_DEV_BCDUSB 0x0200 -#endif - -#ifndef USB_DEV_CLASS -#define USB_DEV_CLASS USB_CLASS_PER_INTERFACE -#endif - -#ifndef CONFIG_USB_BCD_DEV -#define CONFIG_USB_BCD_DEV 0x0100 /* 1.00 */ -#endif - -#ifndef CONFIG_USB_SERIALNO -#define USB_STR_SERIALNO 0 -#else -static int usb_load_serial(void); -#endif - - -/* USB Standard Device Descriptor */ -static const struct usb_device_descriptor dev_desc = { - .bLength = USB_DT_DEVICE_SIZE, - .bDescriptorType = USB_DT_DEVICE, - .bcdUSB = USB_DEV_BCDUSB, - .bDeviceClass = USB_DEV_CLASS, - .bDeviceSubClass = 0x00, - .bDeviceProtocol = 0x00, - .bMaxPacketSize0 = USB_MAX_PACKET_SIZE, - .idVendor = USB_VID_GOOGLE, - .idProduct = CONFIG_USB_PID, - .bcdDevice = CONFIG_USB_BCD_DEV, - .iManufacturer = USB_STR_VENDOR, - .iProduct = USB_STR_PRODUCT, - .iSerialNumber = USB_STR_SERIALNO, - .bNumConfigurations = 1 -}; - -/* USB Configuration Descriptor */ -const struct usb_config_descriptor USB_CONF_DESC(conf) = { - .bLength = USB_DT_CONFIG_SIZE, - .bDescriptorType = USB_DT_CONFIGURATION, - .wTotalLength = 0x0BAD, /* number of returned bytes, set at runtime */ - .bNumInterfaces = USB_IFACE_COUNT, - .bConfigurationValue = 1, /* Caution: hard-coded value */ - .iConfiguration = USB_STR_VERSION, - .bmAttributes = 0x80 /* Reserved bit */ -#ifdef CONFIG_USB_SELF_POWERED /* bus or self powered */ - | 0x40 -#endif -#ifdef CONFIG_USB_REMOTE_WAKEUP - | 0x20 -#endif - , - .bMaxPower = (CONFIG_USB_MAXPOWER_MA / 2), -}; - -const uint8_t usb_string_desc[] = { - 4, /* Descriptor size */ - USB_DT_STRING, - 0x09, 0x04 /* LangID = 0x0409: U.S. English */ -}; - -/****************************************************************************/ -/* Packet-handling stuff, specific to this SoC */ - -/* Some internal state to keep track of what's going on */ -static enum { - WAITING_FOR_SETUP_PACKET, - DATA_STAGE_IN, - NO_DATA_STAGE, -} what_am_i_doing; - -#ifdef DEBUG_ME -static const char * const wat[3] = { - [WAITING_FOR_SETUP_PACKET] = "wait_for_setup", - [DATA_STAGE_IN] = "data_in", - [NO_DATA_STAGE] = "no_data", -}; -#endif - -/* Programmer's Guide, Table 10-7 */ -enum table_case { - BAD_0, - TABLE_CASE_COMPLETE, - TABLE_CASE_SETUP, - TABLE_CASE_WTF, - TABLE_CASE_D, - TABLE_CASE_E, - BAD_6, - BAD_7, -}; - -static enum table_case decode_table_10_7(uint32_t doepint) -{ - enum table_case val = BAD_0; - - /* Bits: SI, SPD, IOC */ - if (doepint & DOEPINT_XFERCOMPL) - val += 1; - if (doepint & DOEPINT_SETUP) - val += 2; - return val; -} - -/* For STATUS/OUT: Use two DMA descriptors, each with one-packet buffers */ -#define NUM_OUT_BUFFERS 2 -static uint8_t __aligned(4) ep0_setup_buf[USB_MAX_PACKET_SIZE]; - -/* For IN: Several DMA descriptors, all pointing into one large buffer, so that - * we can return the configuration descriptor as one big blob. - */ -#define NUM_IN_PACKETS_AT_ONCE 4 -#define IN_BUF_SIZE (NUM_IN_PACKETS_AT_ONCE * USB_MAX_PACKET_SIZE) -static uint8_t __aligned(4) ep0_in_buf[IN_BUF_SIZE]; - -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). - * We only need a few, though. - */ -static enum { - DS_DEFAULT, - DS_ADDRESS, - DS_CONFIGURED, -} 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]; - - if (GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA) { - CPRINTS("usb_write_ep ep%d: FAIL: tx already in progress!", - ep_num); - return 0; - } - - /* 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 - 1) / 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 could support longer multi-dma transfers here. */ - 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); - - if (GR_USB_DIEPCTL(ep_num) & DXEPCTL_EPENA) { - CPRINTS("usb_epN_tx ep%d: tx still active.", ep_num); - return; - } - - /* clear the Tx/IN interrupts */ - GR_USB_DIEPINT(ep_num) = 0xffffffff; - - /* - * Let's assume this is actually true. - * We could support multi-dma transfers here. - */ - 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 - 1) / 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 */ - GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH - | GRSTCTL_RXFFLSH; - while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) - ; -} - -int send_in_packet(uint32_t ep_num) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[ep_num]; - int len = MIN(USB_MAX_PACKET_SIZE, ep->in_pending); - - if (ep->in_packets == 0) { - report_error(ep_num); - return -1; - } - - GR_USB_DIEPTSIZ(ep_num) = 0; - - GR_USB_DIEPTSIZ(ep_num) |= DXEPTSIZ_PKTCNT(1); - GR_USB_DIEPTSIZ(0) |= DXEPTSIZ_XFERSIZE(len); - GR_USB_DIEPDMA(0) = (uint32_t)ep->in_data; - - - /* We're sending this much. */ - ep->in_pending -= len; - ep->in_packets -= 1; - ep->in_data += len; - - /* We are ready to enable this endpoint to start transferring data. */ - return len; -} - - -/* Load the EP0 IN FIFO buffer with some data (zero-length works too). Returns - * len, or negative on error. - */ -int initialize_in_transfer(const void *source, uint32_t len) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[0]; - -#ifdef CONFIG_USB_DWC_FS - /* FS OTG port does not support DMA or external phy */ - ASSERT(!(usb->dma_en)); - ASSERT(usb->phy_type == USB_PHY_INTERNAL); - ASSERT(usb->speed == USB_SPEED_FS); - ASSERT(usb->irq == STM32_IRQ_OTG_FS); -#else - /* HS OTG port requires an external phy to support HS */ - ASSERT(!((usb->phy_type == USB_PHY_INTERNAL) && - (usb->speed == USB_SPEED_HS))); - ASSERT(usb->irq == STM32_IRQ_OTG_HS); -#endif - - /* Copy the data into our FIFO buffer */ - if (len >= IN_BUF_SIZE) { - report_error(len); - return -1; - } - - /* Stage data in DMA buffer. */ - memcpy(ep->in_databuffer, source, len); - ep->in_data = ep->in_databuffer; - - /* 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; - - send_in_packet(0); - return len; -} - -/* Prepare the EP0 OUT FIFO buffer to accept some data. Returns len, or - * negative on error. - */ -int accept_out_fifo(uint32_t len) -{ - /* TODO: This is not yet implemented */ - report_error(len); - return -1; -} - -/* The next packet from the host should be a Setup packet. Get ready for it. */ -static void expect_setup_packet(void) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[0]; - - what_am_i_doing = WAITING_FOR_SETUP_PACKET; - ep->out_data = ep->out_databuffer; - - /* We don't care about IN packets right now, only OUT. */ - GR_USB_DAINTMSK |= DAINT_OUTEP(0); - GR_USB_DAINTMSK &= ~DAINT_INEP(0); - - GR_USB_DOEPTSIZ(0) = 0; - GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_PKTCNT(1); - GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_XFERSIZE(0x18); - GR_USB_DOEPTSIZ(0) |= DXEPTSIZ_SUPCNT(1); - GR_USB_DOEPCTL(0) = DXEPCTL_USBACTEP | DXEPCTL_EPENA; - GR_USB_DOEPDMA(0) = (uint32_t)ep->out_data; -} - -/* We're complaining about something by stalling both IN and OUT packets, - * but a SETUP packet will get through anyway, so prepare for it. - */ -static void stall_both_fifos(void) -{ - what_am_i_doing = WAITING_FOR_SETUP_PACKET; - /* We don't care about IN packets right now, only OUT. */ - GR_USB_DAINTMSK |= DAINT_OUTEP(0); - GR_USB_DAINTMSK &= ~DAINT_INEP(0); - - GR_USB_DOEPCTL(0) |= DXEPCTL_STALL; - GR_USB_DIEPCTL(0) |= DXEPCTL_STALL; - expect_setup_packet(); -} - -/* The TX FIFO buffer is loaded. Start the Data phase. */ -static void expect_data_phase_in(enum table_case tc) -{ - what_am_i_doing = DATA_STAGE_IN; - - /* Send the reply (data phase in) */ - if (tc == TABLE_CASE_SETUP) - GR_USB_DIEPCTL(0) |= DXEPCTL_USBACTEP | - DXEPCTL_CNAK | DXEPCTL_EPENA; - else - GR_USB_DIEPCTL(0) |= DXEPCTL_EPENA; - - /* We'll receive an empty packet back as a ack, I guess. */ - if (tc == TABLE_CASE_SETUP) - GR_USB_DOEPCTL(0) |= DXEPCTL_CNAK | DXEPCTL_EPENA; - else - GR_USB_DOEPCTL(0) |= DXEPCTL_EPENA; - - /* Get an interrupt when either IN or OUT arrives */ - GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); - -} - -static void expect_data_phase_out(enum table_case tc) -{ - /* TODO: This is not yet supported */ - report_error(tc); - expect_setup_packet(); -} - -/* No Data phase, just Status phase (which is IN, since Setup is OUT) */ -static void expect_status_phase_in(enum table_case tc) -{ - what_am_i_doing = NO_DATA_STAGE; - - /* Expect a zero-length IN for the Status phase */ - (void) initialize_in_transfer(0, 0); - - /* Blindly following instructions here, too. */ - if (tc == TABLE_CASE_SETUP) - GR_USB_DIEPCTL(0) |= DXEPCTL_USBACTEP - | DXEPCTL_CNAK | DXEPCTL_EPENA; - else - GR_USB_DIEPCTL(0) |= DXEPCTL_EPENA; - - /* Get an interrupt when either IN or OUT arrives */ - GR_USB_DAINTMSK |= (DAINT_OUTEP(0) | DAINT_INEP(0)); -} - -/* Handle a Setup packet that expects us to send back data in reply. Return the - * length of the data we're returning, or negative to indicate an error. - */ -static int handle_setup_with_in_stage(enum table_case tc, - struct usb_setup_packet *req) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[0]; - - const void *data = 0; - uint32_t len = 0; - int ugly_hack = 0; - static const uint16_t zero; /* == 0 */ - - switch (req->bRequest) { - case USB_REQ_GET_DESCRIPTOR: { - uint8_t type = req->wValue >> 8; - uint8_t idx = req->wValue & 0xff; - - switch (type) { - case USB_DT_DEVICE: - data = &dev_desc; - len = sizeof(dev_desc); - break; - case USB_DT_CONFIGURATION: - data = __usb_desc; - len = USB_DESC_SIZE; - ugly_hack = 1; /* see below */ - break; -#ifdef CONFIG_USB_BOS - case USB_DT_BOS: - data = bos_ctx.descp; - len = bos_ctx.size; - break; -#endif - case USB_DT_STRING: - if (idx >= USB_STR_COUNT) - return -1; -#ifdef CONFIG_USB_SERIALNO - if (idx == USB_STR_SERIALNO) - data = (uint8_t *)usb_serialno_desc; - else -#endif - data = usb_strings[idx]; - len = *(uint8_t *)data; - break; - 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; - } - break; - } - case USB_REQ_GET_STATUS: { - /* TODO: Device Status: Remote Wakeup? Self Powered? */ - data = &zero; - len = sizeof(zero); - break; - } - case USB_REQ_GET_CONFIGURATION: - data = &configuration_value; - len = sizeof(configuration_value); - break; - - case USB_REQ_SYNCH_FRAME: - /* Unimplemented */ - return -1; - - default: - report_error(req->bRequest); - return -1; - } - - /* Don't send back more than we were asked for. */ - len = MIN(req->wLength, len); - - /* Prepare the TX FIFO. If we haven't preallocated enough room in the - * TX FIFO for the largest reply, we'll have to stall. This is a bug in - * our code, but detecting it easily at compile time is related to the - * ugly_hack directly below. - */ - if (initialize_in_transfer(data, len) < 0) - return -1; - - if (ugly_hack) { - /* - * TODO: Somebody figure out how to fix this, please. - * - * The USB configuration descriptor request is unique in that - * it not only returns the configuration descriptor, but also - * all the interface descriptors and all their endpoint - * descriptors as one enormous blob. We've set up some macros - * so we can declare and implement separate interfaces in - * separate files just by compiling them, and all the relevant - * descriptors are sorted and bundled up by the linker. But the - * total length of the entire blob needs to appear in the first - * configuration descriptor struct and because we don't know - * that value until after linking, it can't be initialized as a - * constant. So we have to compute it at run-time and shove it - * in here, which also means that we have to copy the whole - * blob into our TX FIFO buffer so that it's mutable. Otherwise - * we could just point at it (or pretty much any other constant - * struct that we wanted to send to the host). Bah. - */ - struct usb_config_descriptor *cfg = - (struct usb_config_descriptor *)ep->in_databuffer; - /* set the real descriptor size */ - cfg->wTotalLength = USB_DESC_SIZE; - } - - return len; -} - -/* Handle a Setup that comes with additional data for us. */ -static int handle_setup_with_out_stage(enum table_case tc, - struct usb_setup_packet *req) -{ - /* TODO: We don't support any of these. We should. */ - report_error(-1); - return -1; -} - -/* Some Setup packets don't have a data stage at all. */ -static int handle_setup_with_no_data_stage(enum table_case tc, - struct usb_setup_packet *req) -{ - uint8_t set_addr; - - switch (req->bRequest) { - case USB_REQ_SET_ADDRESS: - /* - * Set the address after the IN packet handshake. - * - * From the USB 2.0 spec, section 9.4.6: - * - * As noted elsewhere, requests actually may result in - * up to three stages. In the first stage, the Setup - * packet is sent to the device. In the optional second - * stage, data is transferred between the host and the - * device. In the final stage, status is transferred - * between the host and the device. The direction of - * data and status transfer depends on whether the host - * is sending data to the device or the device is - * sending data to the host. The Status stage transfer - * is always in the opposite direction of the Data - * stage. If there is no Data stage, the Status stage - * is from the device to the host. - * - * Stages after the initial Setup packet assume the - * same device address as the Setup packet. The USB - * device does not change its device address until - * after the Status stage of this request is completed - * successfully. Note that this is a difference between - * this request and all other requests. For all other - * requests, the operation indicated must be completed - * before the Status stage - */ - set_addr = req->wValue & 0xff; - /* - * NOTE: Now that we've said that, we don't do it. The - * hardware for this SoC knows that an IN packet will - * be following the SET ADDRESS, so it waits until it - * sees that happen before the address change takes - * effect. If we wait until after the IN packet to - * change the register, the hardware gets confused and - * doesn't respond to anything. - */ - GWRITE_FIELD(USB, DCFG, DEVADDR, set_addr); - CPRINTS("SETAD 0x%02x (%d)", set_addr, set_addr); - device_state = DS_ADDRESS; - break; - - case USB_REQ_SET_CONFIGURATION: - switch (req->wValue) { - case 0: - configuration_value = req->wValue; - device_state = DS_ADDRESS; - break; - case 1: /* Caution: Only one config descriptor TODAY */ - /* TODO: All endpoints set to DATA0 toggle state */ - configuration_value = req->wValue; - device_state = DS_CONFIGURED; - break; - default: - /* Nope. That's a paddlin. */ - report_error(-1); - return -1; - } - break; - - case USB_REQ_CLEAR_FEATURE: - case USB_REQ_SET_FEATURE: - /* TODO: Handle DEVICE_REMOTE_WAKEUP, ENDPOINT_HALT? */ - break; - - default: - /* Anything else is unsupported */ - report_error(-1); - return -1; - } - - /* No data to transfer, go straight to the Status phase. */ - return 0; -} - -/* Dispatch an incoming Setup packet according to its type */ -static void handle_setup(enum table_case tc) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[0]; - struct usb_setup_packet *req = - (struct usb_setup_packet *)ep->out_databuffer; - int data_phase_in = req->bmRequestType & USB_DIR_IN; - int data_phase_out = !data_phase_in && req->wLength; - int bytes = -1; /* default is to stall */ - - if (0 == (req->bmRequestType & (USB_TYPE_MASK | USB_RECIP_MASK))) { - /* Standard Device requests */ - if (data_phase_in) - bytes = handle_setup_with_in_stage(tc, req); - else if (data_phase_out) - bytes = handle_setup_with_out_stage(tc, req); - else - bytes = handle_setup_with_no_data_stage(tc, req); - } else if (USB_RECIP_INTERFACE == - (req->bmRequestType & USB_RECIP_MASK)) { - /* Interface-specific requests */ - uint8_t iface = req->wIndex & 0xff; - - if (iface < USB_IFACE_COUNT) - bytes = usb_iface_request[iface](req); - } else { - /* Something we need to add support for? */ - report_error(-1); - } - - /* We say "no" to unsupported and intentionally unhandled requests by - * stalling the Data and/or Status stage. - */ - if (bytes < 0) { - /* Stall both IN and OUT. SETUP will come through anyway. */ - stall_both_fifos(); - } else { - if (data_phase_in) - expect_data_phase_in(tc); - else if (data_phase_out) - expect_data_phase_out(tc); - else - expect_status_phase_in(tc); - } -} - -/* This handles both IN and OUT interrupts for EP0 */ -static void ep0_interrupt(uint32_t intr_on_out, uint32_t intr_on_in) -{ - struct dwc_usb *usb = &usb_ctl; - struct dwc_usb_ep *ep = usb->ep[0]; - uint32_t doepint, diepint; - enum table_case tc; - int out_complete, out_setup, in_complete; - - /* Determine the interrupt cause and clear the bits quickly, but only - * if they really apply. I don't think they're trustworthy if we didn't - * actually get an interrupt. - */ - doepint = GR_USB_DOEPINT(0) & GR_USB_DOEPMSK; - if (intr_on_out) - GR_USB_DOEPINT(0) = doepint; - diepint = GR_USB_DIEPINT(0) & GR_USB_DIEPMSK; - if (intr_on_in) - GR_USB_DIEPINT(0) = diepint; - - out_complete = doepint & DOEPINT_XFERCOMPL; - out_setup = doepint & DOEPINT_SETUP; - in_complete = diepint & DIEPINT_XFERCOMPL; - - /* Decode the situation according to Table 10-7 */ - tc = decode_table_10_7(doepint); - - switch (what_am_i_doing) { - case WAITING_FOR_SETUP_PACKET: - if (out_setup) - handle_setup(tc); - else - report_error(-1); - break; - - case DATA_STAGE_IN: - if (intr_on_in && in_complete) { - /* A packet is sent. Should we send another? */ - if (ep->in_packets > 0) { - /* Send another packet. */ - send_in_packet(0); - expect_data_phase_in(tc); - } - } - - /* But we should ignore the OUT endpoint if we didn't actually - * get an OUT interrupt. - */ - if (!intr_on_out) - break; - - if (out_setup) { - /* The first IN packet has been seen. Keep going. */ - break; - } - if (out_complete) { - /* We've handled the Status phase. All done. */ - expect_setup_packet(); - break; - } - - /* Anything else should be ignorable. Right? */ - break; - - case NO_DATA_STAGE: - if (intr_on_in && in_complete) { - /* We are not expecting an empty packet in - * return for our empty packet. - */ - expect_setup_packet(); - } - - /* Done unless we got an OUT interrupt */ - if (!intr_on_out) - break; - - if (out_setup) { - report_error(-1); - break; - } - - /* Anything else means get ready for a Setup packet */ - report_error(-1); - expect_setup_packet(); - break; - } -} - -/****************************************************************************/ -/* USB device initialization and shutdown routines */ - -/* - * DATA FIFO Setup. There is an internal SPRAM used to buffer the IN/OUT - * packets and track related state without hammering the AHB and system RAM - * during USB transactions. We have to specify where and how much of that SPRAM - * to use for what. - * - * See Programmer's Guide chapter 2, "Calculating FIFO Size". - * We're using Dedicated TxFIFO Operation, without enabling thresholding. - * - * Section 2.1.1.2, page 30: RXFIFO size is the same as for Shared FIFO, which - * is Section 2.1.1.1, page 28. This is also the same as Method 2 on page 45. - * - * We support up to 3 control EPs, no periodic IN EPs, up to 16 TX EPs. Max - * data packet size is 64 bytes. Total SPRAM available is 1024 slots. - */ -#define MAX_CONTROL_EPS 3 -#define MAX_NORMAL_EPS 16 -#define FIFO_RAM_DEPTH 1024 -/* - * Device RX FIFO size is thus: - * (4 * 3 + 6) + 2 * ((64 / 4) + 1) + (2 * 16) + 1 == 85 - */ -#define RXFIFO_SIZE ((4 * MAX_CONTROL_EPS + 6) + \ - 2 * ((USB_MAX_PACKET_SIZE / 4) + 1) + \ - (2 * MAX_NORMAL_EPS) + 1) -/* - * Device TX FIFO size is 2 * (64 / 4) == 32 for each IN EP (Page 46). - */ -#define TXFIFO_SIZE (2 * (USB_MAX_PACKET_SIZE / 4)) -/* - * We need 4 slots per endpoint direction for endpoint status stuff (Table 2-1, - * unconfigurable). - */ -#define EP_STATUS_SIZE (4 * MAX_NORMAL_EPS * 2) -/* - * Make sure all that fits. - */ -BUILD_ASSERT(RXFIFO_SIZE + TXFIFO_SIZE * MAX_NORMAL_EPS + EP_STATUS_SIZE < - FIFO_RAM_DEPTH); - - -/* Now put those constants into the correct registers */ -static void setup_data_fifos(void) -{ - int i; - - /* Programmer's Guide, p31 */ - GR_USB_GRXFSIZ = RXFIFO_SIZE; /* RXFIFO */ - GR_USB_GNPTXFSIZ = (TXFIFO_SIZE << 16) | RXFIFO_SIZE; /* TXFIFO 0 */ - - /* TXFIFO 1..15 */ - for (i = 1; i < MAX_NORMAL_EPS; i++) - GR_USB_DIEPTXF(i) = ((TXFIFO_SIZE << 16) | - (RXFIFO_SIZE + i * TXFIFO_SIZE)); - - /* - * TODO: The Programmer's Guide is confusing about when or whether to - * flush the FIFOs. Section 2.1.1.2 (p31) just says to flush. Section - * 2.2.2 (p55) says to stop all the FIFOs first, then flush. Section - * 7.5.4 (p162) says that flushing the RXFIFO at reset is not - * recommended at all. - * - * I'm also unclear on whether or not the individual EPs are expected - * to be disabled already (DIEPCTLn/DOEPCTLn.EPENA == 0), and if so, - * whether by firmware or hardware. - */ - - /* Flush all FIFOs according to Section 2.1.1.2 */ - GR_USB_GRSTCTL = GRSTCTL_TXFNUM(0x10) | GRSTCTL_TXFFLSH - | GRSTCTL_RXFFLSH; - while (GR_USB_GRSTCTL & (GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH)) - ; /* TODO: timeout 100ms */ -} - -static void usb_init_endpoints(void) -{ - int ep; - - /* Prepare to receive packets on EP0 */ - expect_setup_packet(); - - /* Reset the other endpoints */ - for (ep = 1; ep < USB_EP_COUNT; ep++) - usb_ep_event[ep](USB_EVENT_RESET); -} - -static void usb_reset(void) -{ - /* Clear our internal state */ - device_state = DS_DEFAULT; - configuration_value = 0; - - /* Clear the device address */ - GWRITE_FIELD(USB, DCFG, DEVADDR, 0); - - /* Reinitialize all the endpoints */ - usb_init_endpoints(); -} - -static void usb_resetdet(void) -{ - /* TODO: Same as normal reset, right? I think we only get this if we're - * suspended (sleeping) and the host resets us. Try it and see. - */ - usb_reset(); -} - -static void usb_enumdone(void) -{ - /* We can change to HS here. We will not go to HS today */ - GR_USB_DCTL |= DCTL_CGOUTNAK; -} - - -void usb_interrupt(void) -{ - uint32_t status = GR_USB_GINTSTS & GR_USB_GINTMSK; - uint32_t oepint = status & GINTSTS(OEPINT); - uint32_t iepint = status & GINTSTS(IEPINT); - int ep; - - if (status & GINTSTS(ENUMDONE)) - usb_enumdone(); - - if (status & GINTSTS(RESETDET)) - usb_resetdet(); - - if (status & GINTSTS(USBRST)) - usb_reset(); - - /* Endpoint interrupts */ - if (oepint || iepint) { - /* Note: It seems that the DAINT bits are only trustworthy for - * identifying interrupts when selected by the corresponding - * OEPINT and IEPINT bits from GINTSTS. - */ - uint32_t daint = GR_USB_DAINT; - - /* EP0 has a combined IN/OUT handler. Only call it once, but - * let it know which direction(s) had an interrupt. - */ - if (daint & (DAINT_OUTEP(0) | DAINT_INEP(0))) { - uint32_t intr_on_out = (oepint && - (daint & DAINT_OUTEP(0))); - uint32_t intr_on_in = (iepint && - (daint & DAINT_INEP(0))); - ep0_interrupt(intr_on_out, intr_on_in); - } - - /* Invoke the unidirectional IN and OUT functions for the other - * endpoints. Each handler must clear their own bits in - * DIEPINTn/DOEPINTn. - */ - for (ep = 1; ep < USB_EP_COUNT; ep++) { - if (oepint && (daint & DAINT_OUTEP(ep))) - usb_ep_rx[ep](); - if (iepint && (daint & DAINT_INEP(ep))) - usb_ep_tx[ep](); - } - } - - GR_USB_GINTSTS = status; -} -DECLARE_IRQ(STM32_IRQ_OTG_FS, usb_interrupt, 1); -DECLARE_IRQ(STM32_IRQ_OTG_HS, usb_interrupt, 1); - -static void usb_softreset(void) -{ - int timeout; - - CPRINTS("%s", __func__); - - /* Wait for bus idle */ - timeout = 10000; - while (!(GR_USB_GRSTCTL & GRSTCTL_AHBIDLE) && timeout-- > 0) - ; - - /* Reset and wait for clear */ - GR_USB_GRSTCTL = GRSTCTL_CSFTRST; - timeout = 10000; - while ((GR_USB_GRSTCTL & GRSTCTL_CSFTRST) && timeout-- > 0) - ; - if (GR_USB_GRSTCTL & GRSTCTL_CSFTRST) { - CPRINTF("USB: reset failed\n"); - return; - } - - /* Some more idle? */ - timeout = 10000; - while (!(GR_USB_GRSTCTL & GRSTCTL_AHBIDLE) && timeout-- > 0) - ; - - if (!timeout) { - CPRINTF("USB: reset timeout\n"); - return; - } - /* TODO: Wait 3 PHY clocks before returning */ -} - -void usb_connect(void) -{ - GR_USB_DCTL &= ~DCTL_SFTDISCON; -} - -void usb_disconnect(void) -{ - GR_USB_DCTL |= DCTL_SFTDISCON; - - device_state = DS_DEFAULT; - configuration_value = 0; -} - -void usb_reset_init_phy(void) -{ - struct dwc_usb *usb = &usb_ctl; - - if (usb->phy_type == USB_PHY_ULPI) { - GR_USB_GCCFG &= ~GCCFG_PWRDWN; - GR_USB_GUSBCFG &= ~(GUSBCFG_TSDPS | - GUSBCFG_ULPIFSLS | GUSBCFG_PHYSEL); - GR_USB_GUSBCFG &= ~(GUSBCFG_ULPIEVBUSD | GUSBCFG_ULPIEVBUSI); - /* No suspend */ - GR_USB_GUSBCFG |= GUSBCFG_ULPICSM | GUSBCFG_ULPIAR; - - usb_softreset(); - } else { - GR_USB_GUSBCFG |= GUSBCFG_PHYSEL; - usb_softreset(); - GR_USB_GCCFG |= GCCFG_PWRDWN; - } -} - -void usb_init(void) -{ - int i; - struct dwc_usb *usb = &usb_ctl; - - CPRINTS("%s", __func__); - -#ifdef CONFIG_USB_SERIALNO - usb_load_serial(); -#endif - - /* USB is in use */ - disable_sleep(SLEEP_MASK_USB_DEVICE); - - /* Enable clocks */ - clock_enable_module(MODULE_USB, 0); - clock_enable_module(MODULE_USB, 1); - - /* TODO(crbug.com/496888): set up pinmux */ - gpio_config_module(MODULE_USB, 1); - - /* Make sure interrupts are disabled */ - GR_USB_GINTMSK = 0; - GR_USB_DAINTMSK = 0; - GR_USB_DIEPMSK = 0; - GR_USB_DOEPMSK = 0; - - /* Full-Speed Serial PHY */ - usb_reset_init_phy(); - - /* Global + DMA configuration */ - GR_USB_GAHBCFG = GAHBCFG_GLB_INTR_EN; - GR_USB_GAHBCFG |= GAHBCFG_HBSTLEN_INCR4; - if (usb->dma_en) - GR_USB_GAHBCFG |= GAHBCFG_DMA_EN; - - /* Device only, no SRP */ - GR_USB_GUSBCFG |= GUSBCFG_FDMOD; - GR_USB_GUSBCFG |= GUSBCFG_SRPCAP | GUSBCFG_HNPCAP; - - GR_USB_GCCFG &= ~GCCFG_VBDEN; - GR_USB_GOTGCTL |= GOTGCTL_BVALOEN; - GR_USB_GOTGCTL |= GOTGCTL_BVALOVAL; - - GR_USB_PCGCCTL = 0; - - if (usb->phy_type == USB_PHY_ULPI) { - /* TODO(nsanders): add HS support like so. - * GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK) - * | DCFG_DEVSPD_HSULPI; - */ - GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK) - | DCFG_DEVSPD_FSULPI; - } else { - GR_USB_DCFG = (GR_USB_DCFG & ~GC_USB_DCFG_DEVSPD_MASK) - | DCFG_DEVSPD_FS48; - } - - GR_USB_DCFG |= DCFG_NZLSOHSK; - - flush_all_fifos(); - - /* Clear pending interrupts again */ - GR_USB_GINTMSK = 0; - GR_USB_DIEPMSK = 0; - GR_USB_DOEPMSK = 0; - GR_USB_DAINT = 0xffffffff; - GR_USB_DAINTMSK = 0; - - /* TODO: What about the AHB Burst Length Field? It's 0 now. */ - GR_USB_GAHBCFG |= GAHBCFG_TXFELVL | GAHBCFG_PTXFELVL; - - /* Device only, no SRP */ - GR_USB_GUSBCFG |= GUSBCFG_FDMOD - | GUSBCFG_TOUTCAL(7) - /* FIXME: Magic number! 14 is for 15MHz! Use 9 for 30MHz */ - | GUSBCFG_USBTRDTIM(14); - - /* Be in disconnected state until we are ready */ - usb_disconnect(); - - /* If we've restored a nonzero device address, update our state. */ - if (GR_USB_DCFG & GC_USB_DCFG_DEVADDR_MASK) { - /* Caution: We only have one config TODAY, so there's no real - * difference between DS_CONFIGURED and DS_ADDRESS. - */ - device_state = DS_CONFIGURED; - configuration_value = 1; - } else { - device_state = DS_DEFAULT; - configuration_value = 0; - } - - /* Now that DCFG.DesDMA is accurate, prepare the FIFOs */ - setup_data_fifos(); - - usb_init_endpoints(); - - /* Clear any pending interrupts */ - for (i = 0; i < 16; i++) { - GR_USB_DIEPINT(i) = 0xffffffff; - GR_USB_DIEPTSIZ(i) = 0; - GR_USB_DOEPINT(i) = 0xffffffff; - GR_USB_DOEPTSIZ(i) = 0; - } - - if (usb->dma_en) { - GR_USB_DTHRCTL = DTHRCTL_TXTHRLEN_6 | DTHRCTL_RXTHRLEN_6; - GR_USB_DTHRCTL |= DTHRCTL_RXTHREN | DTHRCTL_ISOTHREN - | DTHRCTL_NONISOTHREN; - i = GR_USB_DTHRCTL; - } - - GR_USB_GINTSTS = 0xFFFFFFFF; - - GR_USB_GAHBCFG |= GAHBCFG_GLB_INTR_EN | GAHBCFG_TXFELVL - | GAHBCFG_PTXFELVL; - - if (!(usb->dma_en)) - GR_USB_GINTMSK |= GINTMSK(RXFLVL); - - /* Unmask some endpoint interrupt causes */ - GR_USB_DIEPMSK = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK; - GR_USB_DOEPMSK = DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK | - DOEPMSK_SETUPMSK; - - /* Enable interrupt handlers */ - task_enable_irq(usb->irq); - - /* Allow USB interrupts to come in */ - GR_USB_GINTMSK |= - /* NAK bits that must be cleared by the DCTL register */ - GINTMSK(GOUTNAKEFF) | GINTMSK(GINNAKEFF) | - /* Initialization events */ - GINTMSK(USBRST) | GINTMSK(ENUMDONE) | - /* Reset detected while suspended. Need to wake up. */ - GINTMSK(RESETDET) | /* TODO: Do we need this? */ - /* Idle, Suspend detected. Should go to sleep. */ - GINTMSK(ERLYSUSP) | GINTMSK(USBSUSP); - - GR_USB_GINTMSK |= - /* Endpoint activity, cleared by the DOEPINT/DIEPINT regs */ - GINTMSK(OEPINT) | GINTMSK(IEPINT); - - /* Device registers have been setup */ - GR_USB_DCTL |= DCTL_PWRONPRGDONE; - udelay(10); - GR_USB_DCTL &= ~DCTL_PWRONPRGDONE; - - /* Clear global NAKs */ - GR_USB_DCTL |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; - -#ifndef CONFIG_USB_INHIBIT_CONNECT - /* Indicate our presence to the USB host */ - usb_connect(); -#endif -} -#ifndef CONFIG_USB_INHIBIT_INIT -DECLARE_HOOK(HOOK_INIT, usb_init, HOOK_PRIO_DEFAULT); -#endif - -void usb_release(void) -{ - struct dwc_usb *usb = &usb_ctl; - - /* signal disconnect to host */ - usb_disconnect(); - - /* disable interrupt handlers */ - task_disable_irq(usb->irq); - - /* disable clocks */ - clock_enable_module(MODULE_USB, 0); - /* TODO: pin-mux */ - - /* USB is off, so sleep whenever */ - 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) { - if (!strcasecmp("on", argv[1])) - usb_init(); - else if (!strcasecmp("off", argv[1])) - usb_release(); - else if (!strcasecmp("info", argv[1])) - usb_info(); - return EC_SUCCESS; - } - - return EC_ERROR_PARAM1; -} -DECLARE_CONSOLE_COMMAND(usb, command_usb, - "[on|off|info]", - "Get/set the USB connection state and PHY selection"); - -#ifdef CONFIG_USB_SERIALNO -/* This will be subbed into USB_STR_SERIALNO. */ -struct usb_string_desc *usb_serialno_desc = - USB_WR_STRING_DESC(DEFAULT_SERIALNO); - -/* Update serial number */ -static int usb_set_serial(const char *serialno) -{ - struct usb_string_desc *sd = usb_serialno_desc; - int i; - - if (!serialno) - return EC_ERROR_INVAL; - - /* Convert into unicode usb string desc. */ - for (i = 0; i < CONFIG_SERIALNO_LEN; i++) { - sd->_data[i] = serialno[i]; - if (serialno[i] == 0) - break; - } - /* Count wchars (w/o null terminator) plus size & type bytes. */ - sd->_len = (i * 2) + 2; - sd->_type = USB_DT_STRING; - - return EC_SUCCESS; -} - -/* Retrieve serial number from pstate flash. */ -static int usb_load_serial(void) -{ - const char *serialno; - int rv; - - serialno = board_read_serial(); - if (!serialno) - return EC_ERROR_ACCESS_DENIED; - - rv = usb_set_serial(serialno); - return rv; -} - -/* Save serial number into pstate region. */ -static int usb_save_serial(const char *serialno) -{ - int rv; - - if (!serialno) - return EC_ERROR_INVAL; - - /* Save this new serial number to flash. */ - rv = board_write_serial(serialno); - if (rv) - return rv; - - /* Load this new serial number to memory. */ - rv = usb_load_serial(); - return rv; -} - -static int command_serialno(int argc, char **argv) -{ - struct usb_string_desc *sd = usb_serialno_desc; - char buf[CONFIG_SERIALNO_LEN]; - int rv = EC_SUCCESS; - int i; - - if (argc != 1) { - if ((strcasecmp(argv[1], "set") == 0) && - (argc == 3)) { - ccprintf("Saving serial number\n"); - rv = usb_save_serial(argv[2]); - } else if ((strcasecmp(argv[1], "load") == 0) && - (argc == 2)) { - ccprintf("Loading serial number\n"); - rv = usb_load_serial(); - } else - return EC_ERROR_INVAL; - } - - for (i = 0; i < CONFIG_SERIALNO_LEN; i++) - buf[i] = sd->_data[i]; - ccprintf("Serial number: %s\n", buf); - return rv; -} - -DECLARE_CONSOLE_COMMAND(serialno, command_serialno, - "load/set [value]", - "Read and write USB serial number"); -#endif /* CONFIG_USB_SERIALNO */ |