/* * This file is part of the libopencm3 project. * * Copyright (C) 2013 Alexandru Gagniuc * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ /* * TODO list: * * 1) Driver works by reading and writing to the FIFOs one byte at a time. It * has no knowledge of DMA. * 2) Double-buffering is supported. How can we take advantage of it to speed * up endpoint transfers. * 3) No benchmarks as to the endpoint's performance has been done. */ /* * The following are resources referenced in comments: * [1] http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/t/238784.aspx */ #include #include #include #include #include "../../lib/usb/usb_private.h" #include #define MAX_FIFO_RAM (4 * 1024) const struct _usbd_driver lm4f_usb_driver; static inline void lm4f_usb_soft_disconnect(void) { USB_POWER &= ~USB_POWER_SOFTCONN; } static inline void lm4f_usb_soft_connect(void) { USB_POWER |= USB_POWER_SOFTCONN; } static void lm4f_set_address(usbd_device *usbd_dev, u8 addr) { (void)usbd_dev; USB_FADDR = addr & USB_FADDR_FUNCADDR_MASK; } static void lm4f_ep_setup(usbd_device *usbd_dev, u8 addr, u8 type, u16 max_size, void (*callback) (usbd_device *usbd_dev, u8 ep)) { (void)usbd_dev; (void)type; u8 reg8; u16 fifo_size; const bool dir_tx = addr & 0x80; const u8 ep = addr & 0x0f; /* * We do not mess with the maximum packet size, but we can only allocate * the FIFO in power-of-two increments. */ if (max_size > 1024) { fifo_size = 2048; reg8 = USB_FIFOSZ_SIZE_2048; } else if (max_size > 512) { fifo_size = 1024; reg8 = USB_FIFOSZ_SIZE_1024; } else if (max_size > 256) { fifo_size = 512; reg8 = USB_FIFOSZ_SIZE_512; } else if (max_size > 128) { fifo_size = 256; reg8 = USB_FIFOSZ_SIZE_256; } else if (max_size > 64) { fifo_size = 128; reg8 = USB_FIFOSZ_SIZE_128; } else if (max_size > 32) { fifo_size = 64; reg8 = USB_FIFOSZ_SIZE_64; } else if (max_size > 16) { fifo_size = 32; reg8 = USB_FIFOSZ_SIZE_32; } else if (max_size > 8) { fifo_size = 16; reg8 = USB_FIFOSZ_SIZE_16; } else { fifo_size = 8; reg8 = USB_FIFOSZ_SIZE_8; } /* Endpoint 0 is more special */ if (addr == 0) { USB_EPIDX = 0; if (reg8 > USB_FIFOSZ_SIZE_64) reg8 = USB_FIFOSZ_SIZE_64; /* The RX and TX FIFOs are shared for EP0 */ USB_RXFIFOSZ = reg8; USB_TXFIFOSZ = reg8; /* * Regardless of how much we allocate, the first 64 bytes * are always reserved for EP0. */ usbd_dev->fifo_mem_top_ep0 = 64; return; } /* Are we out of FIFO space? */ if (usbd_dev->fifo_mem_top + fifo_size > MAX_FIFO_RAM) return; USB_EPIDX = addr & USB_EPIDX_MASK; /* FIXME: What about double buffering? */ if (dir_tx) { USB_TXMAXP(ep) = max_size; USB_TXFIFOSZ = reg8; USB_TXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3); if (callback) { usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_IN] = (void *)callback; } if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) USB_TXCSRH(ep) |= USB_TXCSRH_ISO; else USB_TXCSRH(ep) &= ~USB_TXCSRH_ISO; } else { USB_RXMAXP(ep) = max_size; USB_RXFIFOSZ = reg8; USB_RXFIFOADD = ((usbd_dev->fifo_mem_top) >> 3); if (callback) { usbd_dev->user_callback_ctr[ep][USB_TRANSACTION_OUT] = (void *)callback; } if (type == USB_ENDPOINT_ATTR_ISOCHRONOUS) USB_RXCSRH(ep) |= USB_RXCSRH_ISO; else USB_RXCSRH(ep) &= ~USB_RXCSRH_ISO; } usbd_dev->fifo_mem_top += fifo_size; } static void lm4f_endpoints_reset(usbd_device *usbd_dev) { /* * The core resets the endpoints automatically on reset. * The first 64 bytes are always reserved for EP0 */ usbd_dev->fifo_mem_top = 64; } static void lm4f_ep_stall_set(usbd_device *usbd_dev, u8 addr, u8 stall) { (void)usbd_dev; const u8 ep = addr & 0x0f; const bool dir_tx = addr & 0x80; if (ep == 0) { if (stall) USB_CSRL0 |= USB_CSRL0_STALL; else USB_CSRL0 &= ~USB_CSRL0_STALL; return; } if (dir_tx) { if (stall) (USB_TXCSRL(ep)) |= USB_TXCSRL_STALL; else (USB_TXCSRL(ep)) &= ~USB_TXCSRL_STALL; } else { if (stall) (USB_RXCSRL(ep)) |= USB_RXCSRL_STALL; else (USB_RXCSRL(ep)) &= ~USB_RXCSRL_STALL; } } static u8 lm4f_ep_stall_get(usbd_device *usbd_dev, u8 addr) { (void)usbd_dev; const u8 ep = addr & 0x0f; const bool dir_tx = addr & 0x80; if (ep == 0) { return (USB_CSRL0 & USB_CSRL0_STALLED); } if (dir_tx) return (USB_TXCSRL(ep) & USB_TXCSRL_STALLED); else return (USB_RXCSRL(ep) & USB_RXCSRL_STALLED); } static void lm4f_ep_nak_set(usbd_device *usbd_dev, u8 addr, u8 nak) { (void)usbd_dev; (void)addr; (void)nak; /* NAK's are handled automatically by hardware. Move along. */ } static u16 lm4f_ep_write_packet(usbd_device *usbd_dev, u8 addr, const void *buf, u16 len) { const u8 ep = addr & 0xf; u16 i; (void)usbd_dev; /* Don't touch the FIFO if there is still a packet being transmitted */ if (ep == 0 && (USB_CSRL0 & USB_CSRL0_TXRDY)) { return 0; } else if (USB_TXCSRL(ep) & USB_TXCSRL_TXRDY) { return 0; } /* * For some reason, using 16 or 32-bit transfers to the FIFO does not * work well. */ for (i = 0; i < len; i++) USB_FIFO8(ep) = ((u8 *)buf)[i]; if (ep == 0) { /* * EP0 is very special. We should only set DATAEND when we * transmit the last packet in the transaction. A transaction * that is a multiple of 64 bytes will end with a zero-length * packet, so our check is sane. */ if (len != 64) USB_CSRL0 |= USB_CSRL0_TXRDY | USB_CSRL0_DATAEND; else USB_CSRL0 |= USB_CSRL0_TXRDY; } else { USB_TXCSRL(ep) |= USB_TXCSRL_TXRDY; } return i; } static u16 lm4f_ep_read_packet(usbd_device *usbd_dev, u8 addr, void *buf, u16 len) { (void)usbd_dev; u8 * buffy = buf; u16 rlen; u8 ep = addr & 0xf; u16 fifoin = USB_RXCOUNT(ep); rlen = (fifoin > len) ? len : fifoin; for (len = 0; len < rlen; len++) buffy[len] = USB_FIFO8(ep); if (ep == 0) { /* * Clear RXRDY * Datasheet says that DATAEND must also be set when clearing * RXRDY. We don't do that. If did this when transmitting a * packet larger than 64 bytes, only the first 64 bytes would * be transmitted, followed by a handshake. The host would only * get 64 bytes, seeing it as a malformed packet. Usually, we * would not get past enumeration. */ USB_CSRL0 |= USB_CSRL0_RXRDYC; } else { USB_RXCSRL(ep) &= ~USB_RXCSRL_RXRDY; } return rlen; } static void lm4f_poll(usbd_device *usbd_dev) { void (*tx_cb)(usbd_device *usbd_dev, u8 ea); void (*rx_cb)(usbd_device *usbd_dev, u8 ea); int i; /* * The initial state of these registers might change, as we process the * interrupt, but we need the initial state in order to decide how to * handle events. */ const u8 usb_is = USB_IS; const u8 usb_rxis = USB_RXIS; const u8 usb_txis = USB_TXIS; const u8 usb_csrl0 = USB_CSRL0; if ((usb_is & USB_IM_SUSPEND) && (usbd_dev->user_callback_suspend)) usbd_dev->user_callback_suspend(); if ((usb_is & USB_IM_RESUME) && (usbd_dev->user_callback_resume)) usbd_dev->user_callback_resume(); if (usb_is & USB_IM_RESET) _usbd_reset(usbd_dev); if ((usb_is & USB_IM_SOF) && (usbd_dev->user_callback_sof)) usbd_dev->user_callback_sof(); if (usb_txis & USB_EP0) { /* * The EP0 bit in USB_TXIS is special. It tells us that * something happened on EP0, but does not tell us what. This * bit does not necessarily tell us that a packet was * transmitted, so we have to go through all the possibilities * to figure out exactly what did. Only after we've exhausted * all other possibilities, can we assume this is a EPO * "transmit complete" interrupt. */ if (usb_csrl0 & USB_CSRL0_RXRDY) { enum _usbd_transaction type; type = (usbd_dev->control_state.state != DATA_OUT && usbd_dev->control_state.state != LAST_DATA_OUT) ? USB_TRANSACTION_SETUP : USB_TRANSACTION_OUT ; if (usbd_dev->user_callback_ctr[0][type]) usbd_dev->user_callback_ctr[0][type] (usbd_dev, 0); } else { tx_cb = usbd_dev->user_callback_ctr[0][USB_TRANSACTION_IN]; /* * EP0 bit in TXIS is set not only when a packet is * finished transmitting, but also when RXRDY is set, or * when we set TXRDY to transmit a packet. If any of * those are the case, then we do not want to call our * IN callback, since the state machine will be in the * wrong state, and we'll just stall our control * endpoint. * In fact, the only way to know if it's time to call * our TX callback is to know what to expect. The * hardware does not tell us what sort of transaction * this is. We need to work with the state machine to * figure it all out. See [1] for details. */ if ((usbd_dev->control_state.state != DATA_IN) && (usbd_dev->control_state.state != LAST_DATA_IN) && (usbd_dev->control_state.state != STATUS_IN)) return; if (tx_cb) tx_cb (usbd_dev, 0); } } /* See which interrupt occurred */ for (i = 1; i < 8; i++) { tx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_IN]; rx_cb = usbd_dev->user_callback_ctr[i][USB_TRANSACTION_OUT]; if ( (usb_txis & (1 << i)) && tx_cb) tx_cb(usbd_dev, i); if ( (usb_rxis & (1 << i)) && rx_cb) rx_cb(usbd_dev, i); } } static void lm4f_disconnect(usbd_device *usbd_dev, bool disconnected) { (void)usbd_dev; /* * This is all it takes: * usbd_disconnect(dev, 1) followed by usbd_disconnect(dev, 0) * causes the device to re-enumerate and re-configure properly. */ if (disconnected) lm4f_usb_soft_disconnect(); else lm4f_usb_soft_connect(); } /* * A static struct works as long as we have only one USB peripheral. If we * meet LM4Fs with more than one USB, then we need to rework this approach. */ static struct _usbd_device usbd_dev; /** Initialize the USB device controller hardware of the LM4F. */ static usbd_device *lm4f_usbd_init(void) { int i; /* Start the USB clock */ periph_clock_enable(RCC_USB0); /* Enable the USB PLL interrupts - used to assert PLL lock */ SYSCTL_IMC |= (SYSCTL_IMC_USBPLLLIM | SYSCTL_IMC_PLLLIM); rcc_usb_pll_on(); /* Make sure we're disconnected. We'll reconnect later */ lm4f_usb_soft_disconnect(); /* Software reset USB */ SYSCTL_SRUSB = 1; for (i = 0; i < 1000; i++) __asm__("nop"); SYSCTL_SRUSB = 0; /* * Wait for the PLL to lock before soft connecting * This will result in a deadlock if the system clock is not setup * correctly (clock from main oscillator). */ /* Wait for it */ i = 0; while ( (SYSCTL_RIS & SYSCTL_RIS_USBPLLLRIS) == 0) { i ++; if (i > 0xffff) { return 0; } } /* Now connect to USB */ lm4f_usb_soft_connect(); /* No FIFO allocated yet, but the first 64 bytes are still reserved */ usbd_dev.fifo_mem_top = 64; return &usbd_dev; } /* What is this thing even good for */ #define RX_FIFO_SIZE 512 const struct _usbd_driver lm4f_usb_driver = { .init = lm4f_usbd_init, .set_address = lm4f_set_address, .ep_setup = lm4f_ep_setup, .ep_reset = lm4f_endpoints_reset, .ep_stall_set = lm4f_ep_stall_set, .ep_stall_get = lm4f_ep_stall_get, .ep_nak_set = lm4f_ep_nak_set, .ep_write_packet = lm4f_ep_write_packet, .ep_read_packet = lm4f_ep_read_packet, .poll = lm4f_poll, .disconnect = lm4f_disconnect, .base_address = USB_BASE, .set_address_before_status = false, .rx_fifo_size = RX_FIFO_SIZE, };