From: Keith Packard Date: Tue, 2 Oct 2018 19:46:19 +0000 (-0700) Subject: altos/stm32f4: Add start of stm32f413 USB support X-Git-Tag: 1.9~27^2~20 X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=805d218ff50e6c38345c0d4e46f568009132633e;hp=4a246b4cb5ee8e8894ea3492187a08e1e4f719bb altos/stm32f4: Add start of stm32f413 USB support This code doesn't work yet. Signed-off-by: Keith Packard --- diff --git a/src/stm32f4/ao_usb_gen.c b/src/stm32f4/ao_usb_gen.c new file mode 100644 index 00000000..760afad9 --- /dev/null +++ b/src/stm32f4/ao_usb_gen.c @@ -0,0 +1,712 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao_usb_gen.h" + +static uint8_t ao_usb_ep0_state; + +/* Pending EP0 IN data */ +static const uint8_t *ao_usb_ep0_in_data; /* Remaining data */ +static uint8_t ao_usb_ep0_in_len; /* Remaining amount */ + +/* Temp buffer for smaller EP0 in data */ +static uint8_t ao_usb_ep0_in_buf[2]; + +/* Pending EP0 OUT data */ +static uint8_t *ao_usb_ep0_out_data; +static uint8_t ao_usb_ep0_out_len; + +/* System ram shadow of USB buffer; writing individual bytes is + * too much of a pain (sigh) */ +static uint8_t ao_usb_tx_buffer[AO_USB_IN_SIZE]; +static uint8_t ao_usb_tx_count; + +static uint8_t ao_usb_rx_buffer[AO_USB_OUT_SIZE]; +static uint8_t ao_usb_rx_count, ao_usb_rx_pos; + +/* + * End point register indices + */ + +#define AO_USB_CONTROL_EPR 0 +#define AO_USB_INT_EPR 1 +#define AO_USB_OUT_EPR 2 +#define AO_USB_IN_EPR 3 + +/* Marks when we don't need to send an IN packet. + * This happens only when the last IN packet is not full, + * otherwise the host will expect to keep seeing packets. + * Send a zero-length packet as required + */ +static uint8_t ao_usb_in_flushed; + +/* Marks when we have delivered an IN packet to the hardware + * and it has not been received yet. ao_sleep on this address + * to wait for it to be delivered. + */ +static uint8_t ao_usb_in_pending; + +/* Marks when an OUT packet has been received by the hardware + * but not pulled to the shadow buffer. + */ +static uint8_t ao_usb_out_avail; +uint8_t ao_usb_running; +static uint8_t ao_usb_configuration; + +static uint8_t ao_usb_address; +static uint8_t ao_usb_address_pending; + +/* + * Set current device address and mark the + * interface as active + */ +static void +ao_usb_set_address(uint8_t address) +{ + ao_usb_dev_set_address(address); + ao_usb_address_pending = 0; +} + +#define TX_DBG 0 +#define RX_DBG 0 + +#if TX_DBG +#define _tx_dbg0(msg) _dbg(__LINE__,msg,0) +#define _tx_dbg1(msg,value) _dbg(__LINE__,msg,value) +#else +#define _tx_dbg0(msg) +#define _tx_dbg1(msg,value) +#endif + +#if RX_DBG +#define _rx_dbg0(msg) _dbg(__LINE__,msg,0) +#define _rx_dbg1(msg,value) _dbg(__LINE__,msg,value) +#else +#define _rx_dbg0(msg) +#define _rx_dbg1(msg,value) +#endif + +#if TX_DBG || RX_DBG +static void _dbg(int line, char *msg, uint32_t value); +#endif + +/* + * Set just endpoint 0, for use during startup + */ + +static void +ao_usb_set_ep0(void) +{ + ao_usb_dev_ep0_init(); + + ao_usb_set_address(0); + + ao_usb_running = 0; + + /* Reset our internal state + */ + + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_data = NULL; + ao_usb_ep0_in_len = 0; + + ao_usb_ep0_out_data = 0; + ao_usb_ep0_out_len = 0; +} + +static void +ao_usb_set_configuration(void) +{ +#if 0 + /* Set up the INT end point */ + ao_usb_bdt[AO_USB_INT_EPR].single.addr_tx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_INT_EPR].single.count_tx = 0; + ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_sram_addr += AO_USB_INT_SIZE; + + ao_usb_init_ep(AO_USB_INT_EPR, + AO_USB_INT_EP); + + /* Set up the OUT end point */ + ao_usb_bdt[AO_USB_OUT_EPR].single.addr_rx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_OUT_EPR].single.count_rx = ((1 << STM_USB_BDT_COUNT_RX_BL_SIZE) | + (((AO_USB_OUT_SIZE / 32) - 1) << STM_USB_BDT_COUNT_RX_NUM_BLOCK)); + ao_usb_out_rx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_sram_addr += AO_USB_OUT_SIZE; + + ao_usb_init_ep(AO_USB_OUT_EPR, + AO_USB_OUT_EP, + STM_USB_EPR_EP_TYPE_BULK, + STM_USB_EPR_STAT_RX_VALID, + STM_USB_EPR_STAT_TX_DISABLED); + + /* Set up the IN end point */ + ao_usb_bdt[AO_USB_IN_EPR].single.addr_tx = ao_usb_sram_addr; + ao_usb_bdt[AO_USB_IN_EPR].single.count_tx = 0; + ao_usb_in_tx_buffer = ao_usb_packet_buffer_addr(ao_usb_sram_addr); + ao_usb_sram_addr += AO_USB_IN_SIZE; + + ao_usb_init_ep(AO_USB_IN_EPR, + AO_USB_IN_EP, + STM_USB_EPR_EP_TYPE_BULK, + STM_USB_EPR_STAT_RX_DISABLED, + STM_USB_EPR_STAT_TX_NAK); +#endif + + ao_usb_in_flushed = 0; + ao_usb_in_pending = 0; + ao_wakeup(&ao_usb_in_pending); + + ao_usb_out_avail = 0; + ao_usb_configuration = 0; + + ao_usb_running = 1; + ao_wakeup(&ao_usb_running); +} + +static uint16_t control_count; +static uint16_t in_count; +static uint16_t out_count; +#if USB_DEBUG +static uint16_t int_count; +static uint16_t reset_count; +#endif + +/* Send an IN data packet */ +static void +ao_usb_ep0_flush(void) +{ + uint8_t this_len; + + /* Check to see if the endpoint is still busy */ + if (ao_usb_dev_ep0_in_busy()) { + return; + } + + this_len = ao_usb_ep0_in_len; + if (this_len > AO_USB_CONTROL_SIZE) + this_len = AO_USB_CONTROL_SIZE; + + if (this_len < AO_USB_CONTROL_SIZE) + ao_usb_ep0_state = AO_USB_EP0_IDLE; + + ao_usb_ep0_in_len -= this_len; + + ao_usb_dev_ep0_in(ao_usb_ep0_in_data, this_len); + ao_usb_ep0_in_data += this_len; +} + +/* Read data from the ep0 OUT fifo */ +static void +ao_usb_ep0_fill(void) +{ + uint16_t len; + + len = ao_usb_dev_ep0_out(ao_usb_ep0_out_data, ao_usb_ep0_out_len); + ao_usb_ep0_out_len -= len; + ao_usb_ep0_out_data += len; +} + +static void +ao_usb_ep0_in_reset(void) +{ + ao_usb_ep0_in_data = ao_usb_ep0_in_buf; + ao_usb_ep0_in_len = 0; +} + +static void +ao_usb_ep0_in_queue_byte(uint8_t a) +{ + if (ao_usb_ep0_in_len < sizeof (ao_usb_ep0_in_buf)) + ao_usb_ep0_in_buf[ao_usb_ep0_in_len++] = a; +} + +static void +ao_usb_ep0_in_set(const uint8_t *data, uint8_t len) +{ + ao_usb_ep0_in_data = data; + ao_usb_ep0_in_len = len; +} + +static void +ao_usb_ep0_out_set(uint8_t *data, uint8_t len) +{ + ao_usb_ep0_out_data = data; + ao_usb_ep0_out_len = len; +} + +static void +ao_usb_ep0_in_start(uint16_t max) +{ + /* Don't send more than asked for */ + if (ao_usb_ep0_in_len > max) + ao_usb_ep0_in_len = max; + + ao_usb_dev_ep0_in(ao_usb_ep0_in_data, ao_usb_ep0_in_len); +} + +struct ao_usb_line_coding ao_usb_line_coding = {115200, 0, 0, 8}; + +/* Walk through the list of descriptors and find a match + */ +static void +ao_usb_get_descriptor(uint16_t value, uint16_t length) +{ + const uint8_t *descriptor; + uint8_t type = value >> 8; + uint8_t index = value; + + descriptor = ao_usb_descriptors; + while (descriptor[0] != 0) { + if (descriptor[1] == type && index-- == 0) { + uint8_t len; + if (type == AO_USB_DESC_CONFIGURATION) + len = descriptor[2]; + else + len = descriptor[0]; + if (len > length) + len = length; + ao_usb_ep0_in_set(descriptor, len); + break; + } + descriptor += descriptor[0]; + } +} + +static void +ao_usb_ep0_setup(void) +{ + uint16_t setup_len; + + /* Pull the setup packet out of the fifo */ + setup_len = ao_usb_dev_ep0_out(&ao_usb_setup, 8); + if (setup_len != 8) { + return; + } + + if ((ao_usb_setup.dir_type_recip & AO_USB_DIR_IN) || ao_usb_setup.length == 0) + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + else + ao_usb_ep0_state = AO_USB_EP0_DATA_OUT; + + ao_usb_ep0_in_reset(); + + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_TYPE_MASK) { + case AO_USB_TYPE_STANDARD: + switch(ao_usb_setup.dir_type_recip & AO_USB_SETUP_RECIP_MASK) { + case AO_USB_RECIP_DEVICE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_ADDRESS: + ao_usb_address = ao_usb_setup.value; + ao_usb_address_pending = 1; + break; + case AO_USB_REQ_GET_DESCRIPTOR: + ao_usb_get_descriptor(ao_usb_setup.value, ao_usb_setup.length); + break; + case AO_USB_REQ_GET_CONFIGURATION: + ao_usb_ep0_in_queue_byte(ao_usb_configuration); + break; + case AO_USB_REQ_SET_CONFIGURATION: + ao_usb_configuration = ao_usb_setup.value; + ao_usb_set_configuration(); + break; + } + break; + case AO_USB_RECIP_INTERFACE: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_GET_INTERFACE: + ao_usb_ep0_in_queue_byte(0); + break; + case AO_USB_REQ_SET_INTERFACE: + break; + } + break; + case AO_USB_RECIP_ENDPOINT: + switch(ao_usb_setup.request) { + case AO_USB_REQ_GET_STATUS: + ao_usb_ep0_in_queue_byte(0); + ao_usb_ep0_in_queue_byte(0); + break; + } + break; + } + break; + case AO_USB_TYPE_CLASS: + switch (ao_usb_setup.request) { + case AO_USB_SET_LINE_CODING: + ao_usb_ep0_out_set((uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_GET_LINE_CODING: + ao_usb_ep0_in_set((const uint8_t *) &ao_usb_line_coding, 7); + break; + case AO_USB_SET_CONTROL_LINE_STATE: + break; + } + break; + } + + /* If we're not waiting to receive data from the host, + * queue an IN response + */ + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_in_start(ao_usb_setup.length); +} + +static void +ao_usb_ep0_handle(uint8_t receive) +{ + if (receive & AO_USB_EP0_GOT_RESET) { + ao_usb_set_ep0(); + return; + } + if (receive & AO_USB_EP0_GOT_SETUP) { + ao_usb_ep0_setup(); + } + if (receive & AO_USB_EP0_GOT_RX_DATA) { + if (ao_usb_ep0_state == AO_USB_EP0_DATA_OUT) { + ao_usb_ep0_fill(); + if (ao_usb_ep0_out_len == 0) { + ao_usb_ep0_state = AO_USB_EP0_DATA_IN; + ao_usb_ep0_in_start(0); + } + } + } + if (receive & AO_USB_EP0_GOT_TX_ACK) { +#if HAS_FLIGHT && AO_USB_FORCE_IDLE + ao_flight_force_idle = 1; +#endif + /* Wait until the IN packet is received from addr 0 + * before assigning our local address + */ + if (ao_usb_address_pending) + ao_usb_set_address(ao_usb_address); + if (ao_usb_ep0_state == AO_USB_EP0_DATA_IN) + ao_usb_ep0_flush(); + } +} + +void +ao_usb_ep0_interrupt(uint8_t mask) +{ + if (mask) { + ++control_count; + ao_usb_ep0_handle(mask); + } +} + +void +ao_usb_in_interrupt(uint32_t mask) +{ + if (mask & (1 << AO_USB_IN_EPR)) { + ++in_count; + _tx_dbg1("TX ISR", epr); + ao_usb_in_pending = 0; + ao_wakeup(&ao_usb_in_pending); + } +} + +void +ao_usb_out_interrupt(uint32_t mask) +{ + if (mask & (1 << AO_USB_OUT_EPR)) { + ++out_count; + _rx_dbg1("RX ISR", epr); + ao_usb_out_avail = 1; + _rx_dbg0("out avail set"); + ao_wakeup(AO_USB_OUT_SLEEP_ADDR); + _rx_dbg0("stdin awoken"); + } +} + +void +ao_usb_int_interrupt(uint32_t mask) +{ + (void) mask; +} + +void +stm_usb_fs_wkup(void) +{ + /* USB wakeup, just clear the bit for now */ +// stm_usb.istr &= ~(1 << STM_USB_ISTR_WKUP); +} + +/* Queue the current IN buffer for transmission */ +static void +_ao_usb_in_send(void) +{ + _tx_dbg0("in_send start"); + + while (ao_usb_in_pending) + ao_sleep(&ao_usb_in_pending); + + ao_usb_in_pending = 1; + if (ao_usb_tx_count != AO_USB_IN_SIZE) + ao_usb_in_flushed = 1; + + ao_usb_dev_ep_in(AO_USB_IN_EPR, ao_usb_tx_buffer, ao_usb_tx_count); + ao_usb_tx_count = 0; + + _tx_dbg0("in_send end"); +} + +/* Wait for a free IN buffer. Interrupts are blocked */ +static void +_ao_usb_in_wait(void) +{ + for (;;) { + /* Check if the current buffer is writable */ + if (ao_usb_tx_count < AO_USB_IN_SIZE) + break; + + _tx_dbg0("in_wait top"); + /* Wait for an IN buffer to be ready */ + while (ao_usb_in_pending) + ao_sleep(&ao_usb_in_pending); + _tx_dbg0("in_wait bottom"); + } +} + +void +ao_usb_flush(void) +{ + if (!ao_usb_running) + return; + + /* Anytime we've sent a character since + * the last time we flushed, we'll need + * to send a packet -- the only other time + * we would send a packet is when that + * packet was full, in which case we now + * want to send an empty packet + */ + ao_arch_block_interrupts(); + while (!ao_usb_in_flushed) { + _tx_dbg0("flush top"); + _ao_usb_in_send(); + _tx_dbg0("flush end"); + } + ao_arch_release_interrupts(); +} + +void +ao_usb_putchar(char c) +{ + if (!ao_usb_running) + return; + + ao_arch_block_interrupts(); + _ao_usb_in_wait(); + + ao_usb_in_flushed = 0; + ao_usb_tx_buffer[ao_usb_tx_count++] = (uint8_t) c; + + /* Send the packet when full */ + if (ao_usb_tx_count == AO_USB_IN_SIZE) { + _tx_dbg0("putchar full"); + _ao_usb_in_send(); + _tx_dbg0("putchar flushed"); + } + ao_arch_release_interrupts(); +} + +static void +_ao_usb_out_recv(void) +{ + _rx_dbg0("out_recv top"); + ao_usb_out_avail = 0; + + ao_usb_rx_count = ao_usb_dev_ep_out(AO_USB_OUT_EPR, ao_usb_rx_buffer, sizeof (ao_usb_rx_buffer)); + + _rx_dbg1("out_recv count", ao_usb_rx_count); + + ao_usb_rx_pos = 0; +} + +int +_ao_usb_pollchar(void) +{ + uint8_t c; + + if (!ao_usb_running) + return AO_READ_AGAIN; + + for (;;) { + if (ao_usb_rx_pos != ao_usb_rx_count) + break; + + _rx_dbg0("poll check"); + /* Check to see if a packet has arrived */ + if (!ao_usb_out_avail) { + _rx_dbg0("poll none"); + return AO_READ_AGAIN; + } + _ao_usb_out_recv(); + } + + /* Pull a character out of the fifo */ + c = ao_usb_rx_buffer[ao_usb_rx_pos++]; + return c; +} + +char +ao_usb_getchar(void) +{ + int c; + + ao_arch_block_interrupts(); + while ((c = _ao_usb_pollchar()) == AO_READ_AGAIN) + ao_sleep(AO_USB_OUT_SLEEP_ADDR); + ao_arch_release_interrupts(); + return c; +} + +#ifndef HAS_USB_DISABLE +#define HAS_USB_DISABLE 1 +#endif + +#if HAS_USB_DISABLE +void +ao_usb_disable(void) +{ + ao_usb_dev_disable(); +} +#endif + +void +ao_usb_enable(void) +{ + ao_usb_dev_enable(); + + ao_usb_configuration = 0; +} + +#if USB_ECHO +struct ao_task ao_usb_echo_task; + +static void +ao_usb_echo(void) +{ + char c; + + for (;;) { + c = ao_usb_getchar(); + ao_usb_putchar(c); + ao_usb_flush(); + } +} +#endif + +#if USB_DEBUG +static void +ao_usb_irq(void) +{ + printf ("control: %d out: %d in: %d int: %d reset: %d\n", + control_count, out_count, in_count, int_count, reset_count); +} + +const struct ao_cmds ao_usb_cmds[] = { + { ao_usb_irq, "I\0Show USB interrupt counts" }, + { 0, NULL } +}; +#endif + +void +ao_usb_init(void) +{ + ao_usb_enable(); + + ao_usb_ep0_state = AO_USB_EP0_IDLE; +#if USB_ECHO + ao_add_task(&ao_usb_echo_task, ao_usb_echo, "usb echo"); +#endif +#if USB_DEBUG + ao_cmd_register(&ao_usb_cmds[0]); +#endif +#if !USB_ECHO +#if USE_USB_STDIO + ao_add_stdio(_ao_usb_pollchar, ao_usb_putchar, ao_usb_flush); +#endif +#endif +} + +#if TX_DBG || RX_DBG + +struct ao_usb_dbg { + int line; + char *msg; + uint32_t value; + uint32_t prival; +#if TX_DBG + uint16_t in_count; + uint32_t in_epr; + uint32_t in_pending; + uint32_t tx_count; + uint32_t in_flushed; +#endif +#if RX_DBG + uint8_t rx_count; + uint8_t rx_pos; + uint8_t out_avail; + uint32_t out_epr; +#endif +}; + +#define NUM_USB_DBG 16 + +static struct ao_usb_dbg dbg[NUM_USB_DBG]; +static int dbg_i; + +static void _dbg(int line, char *msg, uint32_t value) +{ + uint32_t prival; + dbg[dbg_i].line = line; + dbg[dbg_i].msg = msg; + dbg[dbg_i].value = value; +#if AO_NONMASK_INTERRUPT + asm("mrs %0,basepri" : "=&r" (prival)); +#else + asm("mrs %0,primask" : "=&r" (prival)); +#endif + dbg[dbg_i].prival = prival; +#if TX_DBG + dbg[dbg_i].in_count = in_count; + dbg[dbg_i].in_epr = stm_usb.epr[AO_USB_IN_EPR]; + dbg[dbg_i].in_pending = ao_usb_in_pending; + dbg[dbg_i].tx_count = ao_usb_tx_count; + dbg[dbg_i].in_flushed = ao_usb_in_flushed; +#endif +#if RX_DBG + dbg[dbg_i].rx_count = ao_usb_rx_count; + dbg[dbg_i].rx_pos = ao_usb_rx_pos; + dbg[dbg_i].out_avail = ao_usb_out_avail; + dbg[dbg_i].out_epr = stm_usb.epr[AO_USB_OUT_EPR]; +#endif + if (++dbg_i == NUM_USB_DBG) + dbg_i = 0; +} +#endif diff --git a/src/stm32f4/ao_usb_gen.h b/src/stm32f4/ao_usb_gen.h new file mode 100644 index 00000000..b059ddb7 --- /dev/null +++ b/src/stm32f4/ao_usb_gen.h @@ -0,0 +1,110 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#ifndef _AO_USB_GEN_H_ +#define _AO_USB_GEN_H_ + +#include "ao.h" +#include "ao_usb.h" +#include "ao_product.h" +#include + +#define USB_ECHO 0 + +#ifndef USE_USB_STDIO +#define USE_USB_STDIO 1 +#endif + +#if USE_USB_STDIO +#define AO_USB_OUT_SLEEP_ADDR (&ao_stdin_ready) +#else +#define AO_USB_OUT_SLEEP_ADDR (&ao_usb_out_avail) +#endif + +struct ao_usb_setup { + uint8_t dir_type_recip; + uint8_t request; + uint16_t value; + uint16_t index; + uint16_t length; +} ao_usb_setup; + +#define AO_USB_EP0_GOT_RESET 1 +#define AO_USB_EP0_GOT_SETUP 2 +#define AO_USB_EP0_GOT_RX_DATA 4 +#define AO_USB_EP0_GOT_TX_ACK 8 + +/* + * End point register indices + */ + +#define AO_USB_CONTROL_EPR 0 +#define AO_USB_INT_EPR 1 +#define AO_USB_OUT_EPR 2 +#define AO_USB_IN_EPR 3 + +/* Device interfaces required */ + +/* Queue IN bytes to EP0 */ +void +ao_usb_dev_ep0_init(void); + +void +ao_usb_dev_ep0_in(const void *data, uint16_t len); + +bool +ao_usb_dev_ep0_in_busy(void); + +/* Receive OUT bytes from EP0 */ +uint16_t +ao_usb_dev_ep0_out(void *data, uint16_t len); + +/* Set device address */ +void +ao_usb_dev_set_address(uint8_t address); + +void +ao_usb_dev_enable(void); + +void +ao_usb_dev_disable(void); + +void +ao_usb_dev_init(void); + +/* Queue IN bytes to EPn */ +void +ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len); + +bool +ao_usb_dev_ep_in_busy(uint8_t ep); + +/* Receive OUT bytes from EPn */ +uint16_t +ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len); + + +/* General interfaces provided */ + +void +ao_usb_ep0_interrupt(uint8_t mask); + +void +ao_usb_in_interrupt(uint32_t mask); + +void +ao_usb_out_interrupt(uint32_t mask); + +#endif /* _AO_USB_GEN_H_ */ + diff --git a/src/stm32f4/ao_usb_stm32f4.c b/src/stm32f4/ao_usb_stm32f4.c new file mode 100644 index 00000000..e65c3656 --- /dev/null +++ b/src/stm32f4/ao_usb_stm32f4.c @@ -0,0 +1,1144 @@ +/* + * Copyright © 2018 Keith Packard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ + +#include "ao_usb_gen.h" + +static uint32_t grxstsp; + +static inline uint8_t +grxstsp_enum(void) +{ + return (grxstsp >> STM_USB_GRXSTSP_EPNUM) & STM_USB_GRXSTSP_EPNUM_MASK; +} + +static inline uint8_t +grxstsp_pktsts(void) +{ + return (grxstsp >> STM_USB_GRXSTSP_PKTSTS) & STM_USB_GRXSTSP_PKTSTS_MASK; +} + +static inline uint16_t +grxstsp_bcnt(void) +{ + return (grxstsp >> STM_USB_GRXSTSP_BCNT) & STM_USB_GRXSTSP_BCNT_MASK; +} + +static void +ao_usb_dev_ep_out_start(uint8_t ep) +{ + stm_usb.doep[ep].doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) | + (3 << STM_USB_DOEPTSIZ_STUPCNT) | + (24 << STM_USB_DOEPTSIZ_XFRSIZ)); + +// stm_usb.doep[ep].doepctl |= (1 << STM_USB_DOEPCTL_EPENA); +} + +static void +ao_usb_mask_in_bits(vuint32_t *addr, uint32_t shift, uint32_t mask, uint32_t bits) +{ + uint32_t value; + + value = *addr; + value &= ~(mask << shift); + value |= (bits << shift); + *addr = value; +} + +static void +ao_usb_activate_ep0(void) +{ + stm_usb.diep[0].diepctl = ((0 << STM_USB_DIEPCTL_TXFNUM) | + (0 << STM_USB_DIEPCTL_STALL) | + (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP) | + (1 << STM_USB_DIEPCTL_USBAEP) | + (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ)); + stm_usb.doep[0].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) | + (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP) | + (1 << STM_USB_DOEPCTL_USBAEP) | + (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ)); +} + +#if 0 +static void +ao_usb_activate_in(int epnum) +{ + stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_IEPM)); + stm_usb.diep[epnum].diepctl = ((epnum << STM_USB_DIEPCTL_TXFNUM) | + (0 << STM_USB_DIEPCTL_STALL) | + (STM_USB_DIEPCTL_EPTYP_BULK << STM_USB_DIEPCTL_EPTYP) | + (1 << STM_USB_DIEPCTL_USBAEP) | + (64 << STM_USB_DIEPCTL_MPSIZ)); +} + +static void +ao_usb_activate_out(int epnum) +{ + stm_usb.daintmsk |= (1 << (epnum + STM_USB_DAINTMSK_OEPM)); + stm_usb.doep[epnum].doepctl = ((0 << STM_USB_DOEPCTL_SNPM) | + (STM_USB_DOEPCTL_EPTYP_BULK << STM_USB_DOEPCTL_EPTYP) | + (1 << STM_USB_DOEPCTL_USBAEP) | + (64 << STM_USB_DOEPCTL_MPSIZ)); +} +#endif + +static void +ao_usb_enum_done(void) +{ + /* Set turn-around delay. 6 is for high hclk (> 32MHz) */ + ao_usb_mask_in_bits(&stm_usb.gusbcfg, STM_USB_GUSBCFG_TRDT, STM_USB_GUSBCFG_TRDT_MASK, 6); + + ao_usb_activate_ep0(); +} + +static void +ao_usb_flush_tx_fifo(uint32_t fifo) +{ + stm_usb.grstctl = ((1 << STM_USB_GRSTCTL_TXFFLSH) | + (fifo << STM_USB_GRSTCTL_TXFNUM)); + while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_TXFFLSH)) != 0) + ao_arch_nop(); +} + +static void +ao_usb_flush_rx_fifo(void) +{ + stm_usb.grstctl = (1 << STM_USB_GRSTCTL_RXFFLSH); + while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_RXFFLSH)) != 0) + ao_arch_nop(); +} + +/* reset and enable EP0 */ +void +ao_usb_dev_ep0_init(void) +{ + uint32_t diepctl; + + /* Flush TX fifo */ + ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL); + + /* Clear interrupts */ + for (int i = 0; i < 6; i++) { + stm_usb.diep[i].diepint = 0xfffffffful; + stm_usb.doep[i].doepint = 0xfffffffful; + } + stm_usb.daint = 0xfffffffful; + + /* Enable EP0 in/out interrupts */ + /* 2. Unmask interrupt bits */ + stm_usb.daintmsk |= ((1 << (STM_USB_DAINTMSK_IEPM + 0)) | + (1 << (STM_USB_DAINTMSK_OEPM + 0))); + + stm_usb.doepmsk |= ((1 << STM_USB_DOEPMSK_STUPM) | + (1 << STM_USB_DOEPMSK_EPDM) | + (1 << STM_USB_DOEPMSK_XFRCM)); + stm_usb.diepmsk |= ((1 << STM_USB_DIEPMSK_TOM) | + (1 << STM_USB_DIEPMSK_XFRCM) | + (1 << STM_USB_DIEPMSK_EPDM)); + + /* 1. Set NAK bit for all OUT endpoints */ + stm_usb.doep[0].doepctl |= (1 << STM_USB_DOEPCTL_CNAK); + for (int i = 1; i < 6; i++) + stm_usb.doep[i].doepctl |= (1 << STM_USB_DOEPCTL_SNAK); + + /* 3. Setup FIFO ram allocation */ + + /* XXX make principled decisions here */ + stm_usb.grxfsiz = 0x80; + + stm_usb.dieptxf0 = ((0x40 << STM_USB_DIEPTXF0_TX0FD) | /* size = 256 bytes */ + (0x80 << STM_USB_DIEPTXF0_TX0FSA)); /* start address = 0x80 */ + + /* 4. Program OUT endpoint 0 to receive a SETUP packet */ + + uint32_t doeptsiz; + + doeptsiz = ((1 << STM_USB_DOEPTSIZ_PKTCNT) | + (0x40 << STM_USB_DOEPTSIZ_XFRSIZ) | + (1 << STM_USB_DOEPTSIZ_STUPCNT)); + + stm_usb.doep[0].doeptsiz = doeptsiz; + + /* Program MPSIZ field to set maximum packet size */ + + diepctl = ((0 << STM_USB_DIEPCTL_EPENA ) | + (0 << STM_USB_DIEPCTL_EPDIS ) | + (0 << STM_USB_DIEPCTL_SNAK ) | + (0 << STM_USB_DIEPCTL_CNAK ) | + (0 << STM_USB_DIEPCTL_TXFNUM) | + (0 << STM_USB_DIEPCTL_STALL ) | + (STM_USB_DIEPCTL_EPTYP_CONTROL << STM_USB_DIEPCTL_EPTYP ) | + (0 << STM_USB_DIEPCTL_NAKSTS ) | + (0 << STM_USB_DIEPCTL_EONUM ) | + (1 << STM_USB_DIEPCTL_USBAEP ) | + (STM_USB_DIEPCTL_MPSIZ0_64 << STM_USB_DIEPCTL_MPSIZ)); + + stm_usb.diep[0].diepctl = diepctl; + + uint32_t doepctl; + + doepctl = ((0 << STM_USB_DOEPCTL_EPENA ) | + (0 << STM_USB_DOEPCTL_EPDIS ) | + (0 << STM_USB_DOEPCTL_SNAK ) | + (0 << STM_USB_DOEPCTL_CNAK ) | + (0 << STM_USB_DOEPCTL_STALL ) | + (0 << STM_USB_DOEPCTL_SNPM ) | + (STM_USB_DOEPCTL_EPTYP_CONTROL << STM_USB_DOEPCTL_EPTYP ) | + (0 << STM_USB_DOEPCTL_NAKSTS ) | + (1 << STM_USB_DOEPCTL_USBAEP ) | + (STM_USB_DOEPCTL_MPSIZ0_64 << STM_USB_DOEPCTL_MPSIZ)); + + stm_usb.doep[0].doepctl = doepctl; + + /* Clear interrupts */ + stm_usb.diep[0].diepint = 0xffffffff; + stm_usb.doep[0].doepint = 0xffffffff; + + ao_usb_dev_ep_out_start(0); +} + +void +ao_usb_dev_ep0_in(const void *data, uint16_t len) +{ + return ao_usb_dev_ep_in(0, data, len); +} + +bool +ao_usb_dev_ep0_in_busy(void) +{ + return false; +} + +uint16_t +ao_usb_dev_ep0_out(void *data, uint16_t len) +{ + return ao_usb_dev_ep_out(0, data, len); +} + +/* Queue IN bytes to EPn */ +void +ao_usb_dev_ep_in(uint8_t ep, const void *data, uint16_t len) +{ + int l = len; + + while (l > 0) { + stm_usb.dfifo[ep].fifo = *((__packed uint32_t *) data); + l -= 4; + data += 4; + } + + /* Set the IN data size */ + stm_usb.diep[ep].dieptsiz = ((1 << STM_USB_DIEPTSIZ_PKTCNT) | + (len << STM_USB_DIEPTSIZ_XFRSIZ)); + + /* Enable the TX empty interrupt */ + stm_usb.diepempmsk |= (1 << ep); + + /* Enable the endpoint to queue the packet for transmission */ + stm_usb.diep[ep].diepctl |= (1 << STM_USB_DIEPCTL_EPENA); +} + +bool +ao_usb_dev_ep_in_busy(uint8_t ep) +{ + (void) ep; + return false; +} + +/* Receive OUT bytes from EPn */ +uint16_t +ao_usb_dev_ep_out(uint8_t ep, void *data, uint16_t len) +{ + uint16_t received; + int l = len; + uint32_t t; + + if (grxstsp_enum() != ep) + return 0; + + received = grxstsp_bcnt(); + if (received > len) + received = len; + + while (l >= 4) { + *((__packed uint32_t *) data) = stm_usb.dfifo[0].fifo; + l -= 4; + data += 4; + } + + if (l != 0) { + t = stm_usb.dfifo[0].fifo; + memcpy(data, &t, l); + } + + ao_usb_dev_ep_out_start(ep); + return received; +} + +void +ao_usb_dev_set_address(uint8_t address) +{ + uint32_t dcfg; + + dcfg = stm_usb.dcfg; + + dcfg &= ~(STM_USB_DCFG_DAD_MASK << STM_USB_DCFG_DAD); + dcfg |= address & STM_USB_DCFG_DAD_MASK; + stm_usb.dcfg = dcfg; +} + +static void +ao_usb_core_reset(void) +{ + /* Wait for AHB master IDLE state. */ + while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_AHBIDL)) == 0) + ao_arch_nop(); + + + /* Core soft reset */ + stm_usb.grstctl |= (1 << STM_USB_GRSTCTL_CSRST); + + /* Wait for reset to complete */ + + while ((stm_usb.grstctl & (1 << STM_USB_GRSTCTL_CSRST)) != 0) + ao_arch_nop(); +} + +static void +ao_usb_core_init(void) +{ + /* Enable embedded PHY */ + stm_usb.gusbcfg |= (1 << STM_USB_GUSBCFG_PHYSEL); + + /* Core reset */ + ao_usb_core_reset(); + + /* Deactivate power down */ + stm_usb.gccfg = (1 << STM_USB_GCCFG_PWRDWN); +} + +static void +ao_usb_delay(uint32_t ms) +{ + AO_TICK_TYPE now = ao_time(); + AO_TICK_TYPE then = now + AO_MS_TO_TICKS(ms); + + while ((int16_t) (then - ao_time()) > 0) + ao_arch_nop(); +} + +static void +ao_usb_set_device_mode(void) +{ + uint32_t gusbcfg; + + gusbcfg = stm_usb.gusbcfg; + gusbcfg &= ~((1 << STM_USB_GUSBCFG_FHMOD) | + (1 << STM_USB_GUSBCFG_FDMOD)); + gusbcfg |= (1 << STM_USB_GUSBCFG_FDMOD); + stm_usb.gusbcfg = gusbcfg; + ao_usb_delay(50); +} + +static void +ao_usb_device_init(void) +{ + /* deactivate vbus sensing */ + stm_usb.gccfg &= ~(1 << STM_USB_GCCFG_VBDEN); + + /* Force device mode */ + stm_usb.gotgctl |= ((1 << STM_USB_GOTGCTL_BVALOEN) | + (1 << STM_USB_GOTGCTL_BVALOVAL)); + + /* Restart the phy clock */ + stm_usb.pcgcctl = 0; + + /* Device mode configuration */ + stm_usb.dcfg |= (STM_USB_DCFG_PFIVL_80 << STM_USB_DCFG_PFIVL); + + /* Set full speed phy */ + stm_usb.dcfg |= (STM_USB_DCFG_DSPD_FULL_SPEED << STM_USB_DCFG_DSPD); + + /* Flush the fifos */ + ao_usb_flush_tx_fifo(STM_USB_GRSTCTL_TXFNUM_ALL); + ao_usb_flush_rx_fifo(); + + /* Clear all pending device interrupts */ + stm_usb.diepmsk = 0; + stm_usb.doepmsk = 0; + stm_usb.daint = 0xffffffffUL; + stm_usb.daintmsk = 0; + + /* Reset all endpoints */ + for (int i = 0; i < 6; i++) { + + /* Reset IN endpoint */ + if (stm_usb.diep[i].diepctl & (1 << STM_USB_DIEPCTL_EPENA)) + stm_usb.diep[i].diepctl = ((1 << STM_USB_DIEPCTL_EPDIS) | + (1 << STM_USB_DIEPCTL_SNAK)); + else + stm_usb.diep[i].diepctl = 0; + stm_usb.diep[i].dieptsiz = 0; + stm_usb.diep[i].diepint = 0xfffffffful; + + /* Reset OUT endpoint */ + if (stm_usb.doep[i].doepctl & (1 << STM_USB_DOEPCTL_EPENA)) + stm_usb.doep[i].doepctl = ((1 << STM_USB_DOEPCTL_EPDIS) | + (1 << STM_USB_DOEPCTL_SNAK)); + else + stm_usb.doep[i].doepctl = 0; + + stm_usb.doep[i].doeptsiz = 0; + stm_usb.doep[i].doepint = 0xfffffffful; + } + + /* Disable all interrupts */ + stm_usb.gintmsk = 0; + + /* Clear pending interrupts */ + stm_usb.gintsts = 0xfffffffful; + + /* Enable core interrupts */ + stm_usb.gintmsk = ((1 << STM_USB_GINTMSK_WUIM ) | + (0 << STM_USB_GINTMSK_SRQIM ) | + (0 << STM_USB_GINTMSK_DISCINT ) | + (0 << STM_USB_GINTMSK_CIDSCHGM ) | + (0 << STM_USB_GINTMSK_LPMINTM ) | + (0 << STM_USB_GINTMSK_PTXFEM ) | + (0 << STM_USB_GINTMSK_HCIM) | + (0 << STM_USB_GINTMSK_PRTIM ) | + (0 << STM_USB_GINTMSK_RSTDETM ) | + (1 << STM_USB_GINTMSK_IISOOXFRM ) | + (1 << STM_USB_GINTMSK_IISOIXFRM ) | + (1 << STM_USB_GINTMSK_OEPINT) | + (1 << STM_USB_GINTMSK_IEPINT) | + (0 << STM_USB_GINTMSK_EOPFM ) | + (0 << STM_USB_GINTMSK_ISOODRPM ) | + (1 << STM_USB_GINTMSK_ENUMDNEM) | + (1 << STM_USB_GINTMSK_USBRST) | + (1 << STM_USB_GINTMSK_USBSUSPM ) | + (0 << STM_USB_GINTMSK_ESUSPM ) | + (0 << STM_USB_GINTMSK_GONAKEFFM ) | + (0 << STM_USB_GINTMSK_GINAKEFFM ) | + (0 << STM_USB_GINTMSK_NPTXFEM ) | + (0 << STM_USB_GINTMSK_RXFLVLM) | + (0 << STM_USB_GINTMSK_SOFM ) | + (0 << STM_USB_GINTMSK_OTGINT ) | + (0 << STM_USB_GINTMSK_MMISM)); +} + +static void +ao_usb_device_connect(void) +{ + /* Enable pull-up/pull-down */ + stm_usb.dctl &= ~(1 << STM_USB_DCTL_SDIS); + ao_usb_delay(20); +} + +#if 0 +static void +ao_usb_device_disconnect(void) +{ + /* Disable pull-up/pull-down */ + stm_usb.dctl |= (1 << STM_USB_DCTL_SDIS); + ao_usb_delay(20); +} +#endif + +void +ao_usb_dev_enable(void) +{ + ao_arch_block_interrupts(); + + /* Configure GPIOs */ + ao_enable_port(&stm_gpioa); +#if 0 + stm_afr_set(&stm_gpioa, 8, STM_AFR_AF10); /* USB_FS_SOF */ + stm_afr_set(&stm_gpioa, 9, STM_AFR_AF10); /* USB_FS_VBUS */ + stm_afr_set(&stm_gpioa, 10, STM_AFR_AF10); /* USB_FS_ID */ +#endif + stm_afr_set(&stm_gpioa, 11, STM_AFR_AF10); + stm_ospeedr_set(&stm_gpioa, 11, STM_OSPEEDR_HIGH); + stm_pupdr_set(&stm_gpioa, 11, STM_PUPDR_NONE); + stm_afr_set(&stm_gpioa, 12, STM_AFR_AF10); + stm_ospeedr_set(&stm_gpioa, 12, STM_OSPEEDR_HIGH); + stm_pupdr_set(&stm_gpioa, 12, STM_PUPDR_NONE); + + /* Power on USB */ + stm_rcc.ahb2enr |= (1 << STM_RCC_AHB2ENR_OTGFSEN); + + /* Route interrupts */ + stm_nvic_set_priority(STM_ISR_OTG_FS_POS, AO_STM_NVIC_LOW_PRIORITY); + stm_nvic_set_enable(STM_ISR_OTG_FS_POS); + + /* Core init */ + ao_usb_core_init(); + + /* Set device mode */ + ao_usb_set_device_mode(); + + /* Reset FIFO allocations */ + for (int i = 1; i < 16; i++) + stm_usb.dieptxf[i-1] = 0x0; + + ao_usb_device_init(); + + /* Connect */ + ao_usb_device_connect(); +} + +void +ao_usb_dev_disable(void) +{ + stm_usb.gusbcfg = ((1 << STM_USB_GUSBCFG_FDMOD) | + (0 << STM_USB_GUSBCFG_FHMOD) | + (6 << STM_USB_GUSBCFG_TRDT) | + (0 << STM_USB_GUSBCFG_HNPCAP) | + (0 << STM_USB_GUSBCFG_SRPCAP) | + (1 << STM_USB_GUSBCFG_PHYSEL) | + (0 << STM_USB_GUSBCFG_TOCAL)); + + stm_usb.gahbcfg = ((0 << STM_USB_GAHBCFG_PTXFELVL) | + (1 << STM_USB_GAHBCFG_TXFELVL) | + (0 << STM_USB_GAHBCFG_GINTMSK)); + + stm_usb.dctl = ((0 << STM_USB_DCTL_POPRGDNE) | + (1 << STM_USB_DCTL_SDIS)); + + stm_rcc.ahb2enr &= ~(1 << STM_RCC_AHB2ENR_OTGFSEN); +} + +void +stm_otg_fs_isr(void) +{ + uint32_t gintsts = stm_usb.gintsts; + uint8_t ep0_receive = 0; + uint32_t out_interrupt = 0; + uint32_t in_interrupt = 0; + + /* Clear all received interrupts */ + stm_usb.gintsts = gintsts; + + if (gintsts & (1 << STM_USB_GINTSTS_USBRST)) { + ep0_receive |= AO_USB_EP0_GOT_RESET; + } + + if (gintsts & (1 << STM_USB_GINTSTS_ENUMDNE)) { + ao_usb_enum_done(); + } + + if (gintsts & ((1 << STM_USB_GINTSTS_OEPINT) | + (1 << STM_USB_GINTSTS_IEPINT))) + { + uint32_t daint = stm_usb.daint; + uint32_t oepint = (daint >> STM_USB_DAINT_OEPINT) & STM_USB_DAINT_OEPINT_MASK; + uint32_t iepint = (daint >> STM_USB_DAINT_IEPINT) & STM_USB_DAINT_IEPINT_MASK; + + for (int ep = 0; ep < 6; ep++) { + if (gintsts & (1 << STM_USB_GINTSTS_OEPINT)) { + if (oepint & (1 << ep)) { + uint32_t doepint = stm_usb.doep[ep].doepint; + + stm_usb.doep[ep].doepint = doepint; + if (doepint & (1 << STM_USB_DOEPINT_XFRC)) { + if (ep == 0) + ep0_receive |= AO_USB_EP0_GOT_SETUP; + else + out_interrupt |= (1 << ep); + } + grxstsp = stm_usb.grxstsp; + } + } + + if (gintsts & (1 << STM_USB_GINTSTS_IEPINT)) { + if (iepint & (1 << ep)) { + uint32_t diepint = stm_usb.diep[ep].diepint; + + stm_usb.diep[ep].diepint = diepint; + if (diepint & (1 << STM_USB_DIEPINT_XFRC)) { + if (ep == 0) + ep0_receive |= AO_USB_EP0_GOT_TX_ACK; + else + in_interrupt |= (1 << ep); + } + } + } + } + } else { + grxstsp = 0; + } + + if (ep0_receive) + ao_usb_ep0_interrupt(ep0_receive); + + if (out_interrupt) + ao_usb_out_interrupt(out_interrupt); + + if (in_interrupt) + ao_usb_in_interrupt(in_interrupt); +} + +/* + + running before plugging in at first packet + gotgctl = 0x04cd0000, 0x04c10000, 0x04cd0000 ************* + + CURMOD = 0 + OTGVER = 0 + BSVLD = 1 BSVLD = 0 + ASVLD = 1 ASVLD = 0 + DBCT = 0 + CIDSTS = 1 + + gotgint = 0x00100000, 0x00100000, 0x00100000 + + IDCHNG = 1 + + gahbcfg = 0x1, 0x1, 0x00000001 + + TXFELVL = 0 trigger half empty + GINTMSK = 1 interrupts enabled + + gusbcfg = 0x40001840, 0x40001440 0x40001840 ************* + + FDMOD = 1 force device mode + FHMOD = 0 + TRDT = 6 5 6 + HNPCAP = 0 + SRPCAP = 0 + PHYSEL = 1 + TOCAL = 0 + + grstctl = 0x80000040, 0x80000000 0x80000400 *********** + + AHBIDL = 1 + TXFNUM = 1 TXFNUM = 0 TXFNUM = 0x20 (flush all) + TXFFLSH = 0 + RXFFLSH = 0 + FCRST = 0 + PSRST = 0 + CSRST = 0 + + gintsts = 0x0480b43a, 0x04008022 0x04888438 *********** + + WKUPINT = 0 0 + SRQINT = 0 0 + DISCINT = 0 0 + CIDSCHG = 0 0 + LPMINT = 0 0 + PTXFE = 1 PTXFE = 1 PTXFE = 1 + HCINT = 0 + HPRTINT = 0 + RSTDET = 1 RSTDET = 0 RSTDET = 1 + IPXFER = 0 + IISOIXFR = 0 + OEPINT = 0 OEPINT = 1 + IEPINT = 0 + EOPF = 1 EOPF = 1 EOPF = 1 + ISOODRP = 0 + ENUMDNE = 1 + USBRST = 1 + USBSUSP = 0 + ESUSP = 1 ESUSP = 1 + GONAKEFF = 0 + GINAKEFF = 0 + NPTXFE = 1 NPTXFE = 1 NPTXFE = 1 + RXFLVL = 1 RXFLVL = 1 + SOF = 1 SOF = 1 + OTGINT = 0 + MMIS = 1 MMIS = 1 MMIS = 0 + CMOD = 0 + + gintmsk = 0xc03c3814, 0xc03c3814, + + WUIM = 1 + SRQIM = 1 + DISCINT = 0 + CIDSCHGM = 0 + LPMINTM = 0 + PTXFEM = 0 + HCIM = 0 + PRTIM = 0 + RSTDETM = 0 + IISOOXFRM = 1 + IISOIXFRM = 1 + OEPINT = 1 + IEPINT = 1 + EOPFM = 0 + ISOODRPM = 0 + ENUMDNEM = 1 + USBRST = 1 + USBSUSPM = 1 + ESUSPM =0 + GONAKEFFM = 0 + GINAKEFFM = 0 + NPTXFEM = 0 + RXFLVLM = 1 + SOFM = 0 + OTGINT = 1 + MMISM = 0 + + grxstsr = 0xac0080, 0x0 0x14c0080 *************** + + STSPHST = 0 STSPHST = 0 + FRMNUM = 5 FRMNUM = 10 + PKTSTS = 6 -- SETUP data packet PKTSTS = 6 -- SETUP data packet + DPID = 0 DPID = 0 + BCNT = 8 BCNT = 8 + EPNUM = 0 EPNUM = 0 + + grxstsp = 0xac0080, 0x0 0x14c0080 + + (same) + + grxfsiz = 0x80, 0x80 0x80 + + RXFD = 128 512 bytes + + dieptxf0 = 0x00400080, 0x00400080 0x00400080 + + TX0FD = 64 256 bytes + TX0FSA = 0x80 + + gccfg = 0x21fff0, 0x21fff0 0x21fff0 + + VBDEN = 1 + SDEN = 0 + PDEN = 0 + DCDEN = 0 + BCDEN = 0 + PWRDN = 1 + PS2DET = 0 + SDET = 0 + PDET = 0 + DCDET = 0 + + cid = 0x2000, 0x2000 0x2000 + + PRODUCT_ID = 0x2000 + + glpmcfg = 0x0, 0x0 0x0 + + ENBESL = 0 + LPMRCNTTST = 0 + SNDLPM = 0 + LPMRCNT = 0 + LPMCHIDX = 0 + L1RSMOK = 0 + SLPSTS = 0 + LPMRSP = 0 + L1DSEN = 0 + BESLTHRS = 0 + L1SSEN = 0 + REMWAKE = 0 + BESL = 0 + LPMACK = 0 + LPMEN = 0 + + dieptxf = {0x8000c0, 0x0, 0x0, 0x0, 0x0}, {0x8000c0, 0x0, 0x0, 0x0, 0x0}, {0x8000c0, 0x0, 0x0, 0x0, 0x0}, + + INEXPTXFD 0 = 0x80 512 bytes + INEXPTXSA 0 = 0xc0 + + dcfg = 0x82000b3, 0x8200003, 0x8200003 + + ERRATIM = 0 + PFIVL = 0 + DAD = 0xb DAD = 0x0 DAD = 0 + NZLSOHSK = 0 + DSPD = 3 Full speed USB 1.1 + + dctl = 0x0, 0x0 0x0 + + DSBESLRJCT = 0 + POPRGDNE = 0 + CGONAK = 0 + SGONAK = 0 + CGINAK = 0 + SGINAK = 0 + TCTL = 0 + GONSTS = 0 + GINSTS = 0 + SDIS = 0 + RWUSIG = 0 + + dsts = 0x0043ff06, 0x00000006 0x00400c06 + + DEVLNSTS = 1 (D+ low, D- high) + FNSOF = 0x3ff FNSOF = 0xc + EERR = 0 + ENUMSPD = 3 Full speed ENUMSPD = 3 + SUSPSTS = 0 SUSPSTS = 0 + + diepmsk = 0xb, 0x0 0xb + + NAKM = 0 + TXFURM = 0 + INEPNEM = 0 + INEPNMM = 0 + ITTXFEMSK = 0 + TOM = 1 + EPDM = 1 + XFRCM = 1 + + doepmsk = 0x2b, 0x0 0x2b + + NYETMSK = 0 + NAKMSK = 0 + BERRM =0 + OUTPKTERRM = 0 + STSPHSRXM = 1 + OTEPDM = 0 + STUPM = 1 + EPDM = 1 + XFRCM = 1 + + daint = 0x0, 0x0 0x10000 + + daintmsk = 0x30003, 0x0 0x10001 + + OEPM = 0x3 endpoints 0 and 1 OEPM = 0x1 endpoint 0 + IEPM = 0x3 endpoints 0 and 1 IEPM = 0x1 endpoint 0 + + dvbusdis = 0x17d7, 0x17d7 0x17d7 + + VBUSDT = 0x17d7 reset value + + dvbuspulse = 0x5b8, 0x5b8 0x5b8 + + DVBUSP = 0x5b8 reset value + + diepempmsk = 0x0, 0x0 0x0 + + INEPTXFEM = 0 no endpoints + + diep = {{ + diepctl = 0x28000, + + EPENA = 0 + EPDIS = 0 + SNAK = 0 + CNAK = 0 + TXFNUM = 0 + STALL = 0 + EPTYP = 0 + NAKSTS = 1 + USBAEP = 1 + MPSIZ = 0 64 bytes + + diepint = 0x20c0, + + NAK = 1 + PKTDRPSTS = 0 + TXFIFOUDRN = 0 + TXFE = 1 + INEPNE = 1 + ITTXFE = 0 + TOC = 0 + EPDISD = 0 + XFRC = 0 + + dieptsiz = 0x0, + + PKTCNT = 0 + XFRSIZ = 0 + + dtxfsts = 0x40, + + INEPTFSAV = 0x40 256 bytes available + + }, { + diepctl = 0x00490040, + + EPENA = 0 + EPDIS = 0 + SODDFRM = 0 + SD0PID = 0 + SNAK = 0 + CNAK = 0 + TXFNUM = 1 + STALL = 0 + EPTYP = 2 bulk + NAKSTS = 0 + EONUM = 1 + USBAEP = 0 + MPSIZ = 64 256 bytes + + diepint = 0x2090, + + NAK = 1 + PKTDRPSTS = 0 + TXFIFOUDRN = 0 + TXFE = 1 + INEPNE = 0 + INPENM = 0 + ITTXFE = 1 + TOC = 0 + EPDISD = 0 + XFRC = 0 + + dieptsiz = 0x0, + + MCNT = 0 + PKTCNT = 0 + XFRSIZ = 0 + + dtxfsts = 0x80, + + INEPTFSAV = 0x80 512 bytes available + + }, { + diepctl = 0x0, + pad_04 = 0x0, + diepint = 0x80, + pad_0c = 0x0, + dieptsiz = 0x0, + pad_14 = 0x43425355, + dtxfsts = 0x40, + pad_1c = 0x400000 + }, { + diepctl = 0x0, + pad_04 = 0x0, + diepint = 0x80, + pad_0c = 0x0, + dieptsiz = 0x0, + pad_14 = 0x43425355, + dtxfsts = 0x40, + pad_1c = 0x400000 + }, { + diepctl = 0x0, + pad_04 = 0x0, + diepint = 0x80, + pad_0c = 0x0, + dieptsiz = 0x0, + pad_14 = 0x43425355, + dtxfsts = 0x40, + pad_1c = 0x400000 + }, { + diepctl = 0x0, + pad_04 = 0x0, + diepint = 0x80, + pad_0c = 0x0, + dieptsiz = 0x0, + pad_14 = 0x43425355, + dtxfsts = 0x40, + pad_1c = 0x400000 + }}, + + doep = {{ + doepctl = 0x80028000, 0x00008000, 0x28000 + + EPENA = 1 EPENA = 0 EPENA = 0 + EPDIS = 0 + SNAK =0 + CNAK = 0 + STALL = 0 + SNPM = 0 + EPTYP = 0 + NAKSTS = 1 NAKSTS = 0 NAKSTS = 1 + USPAEP = 1 USPAEP = 1 + MPSIZ = 0 64 bytes MPSIZ = 0 + + doepint = 0x8010, 0x0 0x8008 + + NYET = 0 + NAK = 0 + BERR = 0 + OUTPKTERR = 0 + STSPHSRX = 0 + OTEPDIS = 1 + STUP = 0 STUP = 1 + EPDISD = 0 + XFRC = 0 + + doeptsiz = 0x38, 0x0 0x20080008 + + STPCNT = 0 1 packet STPCNT = 1 + PKTCNT = 0 PKTCNT = 1 + XFRSIZ = 0x38 56 bytes (64 - 8) XFRSIZ = 8 + + }, { + doepctl = 0x800b0040, + + EPENA = 1 + EPDIS = 0 + SD1PID = 0 + SD0PID = 0 + SNAK = 0 + CNAK = 0 + STALL = 0 + SNPM =0 + EPTYP = 2 Bulk + NAKSTS = 1 + EONUM = 1 + USBAEP = 0 + MPSIZ = 0x40 64 bytes + + doepint = 0x0, + doeptsiz = 0x21, + + RXDPID = 0 + PKTCNT = 0 + XFRSIZ = 0x21 33 bytes ? + + }, { + doepctl = 0x0, + pad_04 = 0x0, + doepint = 0x0, + pad_0c = 0x0, + doeptsiz = 0x0, + pad_14 = 0x43425355, + pad_18 = 0x40, + pad_1c = 0x400000 + }, { + doepctl = 0x0, + pad_04 = 0x0, + doepint = 0x0, + pad_0c = 0x0, + doeptsiz = 0x0, + pad_14 = 0x43425355, + pad_18 = 0x40, + pad_1c = 0x400000 + }, { + doepctl = 0x0, + pad_04 = 0x0, + doepint = 0x0, + pad_0c = 0x0, + doeptsiz = 0x0, + pad_14 = 0x43425355, + pad_18 = 0x40, + pad_1c = 0x400000 + }, { + doepctl = 0x0, + pad_04 = 0x0, + doepint = 0x0, + pad_0c = 0x0, + doeptsiz = 0x0, + pad_14 = 0x43425355, + pad_18 = 0x40, + pad_1c = 0x400000 + }}, + + pcgcctl = 0x0, 0x0, 0x0 + + SUSP = 0 + PHYSLEEP = 0 + ENL1GTG = 0 + PHYSUSP = 0 + GATEHCLK = 0 + STPPCLK = 0 + + dfifo = {{ + fifo = 0x1000680, + + + Clock configuration: + +$5 = { + cr = 0x0f077d83, + + PLLI2SRDY = 1 + PLLI2SON = 1 + PLLRDY = 1 + PLLON = 1 + CSSON = 0 + HSEBYP = 1 + HSERDY = 1 + HSEON = 1 + HSICAL = 0x7d + HSITRIM = 0x10 + HSIRDY = 1 + HSION = 1 + + pllcfgr = 0x27403208, + + PLLR = 2 + PLLQ = 7 + PLLSRC = 1 HSE + PLLP = 0 2 + PLLN = 0xc8 200 + PLLM = 8 + + clk_pllin = 8000000 / 8 = 1000000 + vco = 1000000 * 200 = 200000000 + clk_pll1p = 200000000 / 2 = 100000000 (100MHz) + clk_pll1q = 200000000 / 7 = ??? + clk_pll1r = 200000000 / 2 = 100000000 (100MHz) + + cfgr = 0x0000100a, + cir = 0x00000000, + ahb1rstr = 0x0, + ahb2rstr = 0x0, + ahb3rstr = 0x0, + pad_1c = 0x0, + apb1rstr = 0x0, + apb2rstr = 0x0, + pad_28 = 0x0, + pad_2c = 0x0, + ahb1enr = 0x40107f, + ahb2enr = 0x80, + ahbdnr = 0x3, + pad_3c = 0x0, + apb1enr = 0x11000410, + apb2enr = 0xc800, + pad_48 = 0x0, + pad_4c = 0x0, + ahb1lpenr = 0x6390ff, + ahb2lpenr = 0xd0, + ahb3lpenr = 0x3, + pad_5c = 0x0, + apb1lpenr = 0xfffecfff, + apb2lpenr = 0x357f9f3, + pad_68 = 0x0, + pad_6c = 0x0, + bdcr = 0x8200, + csr = 0x1e000003, + pad_78 = 0x0, + pad_7c = 0x0, + sscgr = 0x0, + plli2scfgr = 0x44003008, + + PLLI2SR = 4 + PLLI2SQ = 4 + PLLI2SSRC = 0 HSE (due to PLLSRC) + PLLI2SN = 0xc0 192 + PLLI2SM = 8 + + clk_plli2sin = 8000000 / 8 = 1000000 + vcoi2s = 1000000 * 192 = 192000000 + ck_pl2q = 192000000 / 4 = 48000000 + ck_pl2r = 192000000 / 4 = 48000000 + + pad_88 = 0x0, + dckcfgr = 0x0, + + + ckgatenr = 0x0, + + All clock gates enabled + + dckcfgr2 = 0x08000000 + + LPTIMER1SEL = 0 APB + CKSDIOSEL = 0 CK_48MHz + CK48MSEL = 1 PLLI2S_Q + I2CFMP1SEL = 0 APB +} + + + +*/