2011-02-14 17:01:11 +02:00
|
|
|
/*
|
2011-02-14 17:48:42 +02:00
|
|
|
* fw/usb/atu2.c - Chip-specific driver for Atmel ATxxxU2 USB chips
|
2011-02-14 17:01:11 +02:00
|
|
|
*
|
2014-02-24 22:59:49 +02:00
|
|
|
* Written 2008-2011, 2013-2014 by Werner Almesberger
|
|
|
|
* Copyright 2008-2011, 2013-2014 Werner Almesberger
|
2011-02-14 17:01:11 +02:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Known issues:
|
|
|
|
* - no suspend/resume
|
2011-02-14 17:54:29 +02:00
|
|
|
* - we don't call back after failed transmissions,
|
|
|
|
* - we don't reset the EP buffer after failed receptions
|
|
|
|
* - enumeration often encounters an error -71 (from which it recovers)
|
2011-02-14 17:01:11 +02:00
|
|
|
*/
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
#include <stdbool.h>
|
2011-02-14 17:01:11 +02:00
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
#define F_CPU 8000000UL
|
|
|
|
#include <util/delay.h>
|
|
|
|
|
|
|
|
#include <avr/io.h>
|
2011-06-11 17:06:18 +03:00
|
|
|
#include <avr/interrupt.h>
|
2011-02-14 17:01:11 +02:00
|
|
|
#include "usb.h"
|
2014-02-24 22:59:49 +02:00
|
|
|
#include "board.h"
|
2011-02-14 17:01:11 +02:00
|
|
|
|
|
|
|
|
|
|
|
#ifndef NULL
|
|
|
|
#define NULL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
#define BUG_ON(cond) do { if (cond) panic(); } while (0)
|
|
|
|
#else
|
|
|
|
#define BUG_ON(cond)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
struct ep_descr eps[NUM_EPS];
|
|
|
|
|
|
|
|
|
|
|
|
static uint16_t usb_read_word(void)
|
|
|
|
{
|
|
|
|
uint8_t low;
|
|
|
|
|
|
|
|
low = UEDATX;
|
|
|
|
return low | UEDATX << 8;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void enable_addr(void *user)
|
|
|
|
{
|
|
|
|
while (!(UEINTX & (1 << TXINI)));
|
|
|
|
UDADDR |= 1 << ADDEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
void set_addr(uint8_t addr)
|
2011-02-14 17:01:11 +02:00
|
|
|
{
|
|
|
|
UDADDR = addr;
|
|
|
|
usb_send(&eps[0], NULL, 0, enable_addr, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-11 20:17:44 +03:00
|
|
|
void usb_ep_change(struct ep_descr *ep)
|
|
|
|
{
|
|
|
|
if (ep->state == EP_TX) {
|
|
|
|
UENUM = ep-eps;
|
|
|
|
UEIENX |= 1 << TXINE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
static bool ep_setup(void)
|
2011-02-14 17:01:11 +02:00
|
|
|
{
|
|
|
|
struct setup_request setup;
|
|
|
|
|
|
|
|
BUG_ON(UEBCLX < 8);
|
|
|
|
|
|
|
|
setup.bmRequestType = UEDATX;
|
|
|
|
setup.bRequest = UEDATX;
|
|
|
|
setup.wValue = usb_read_word();
|
|
|
|
setup.wIndex = usb_read_word();
|
|
|
|
setup.wLength = usb_read_word();
|
|
|
|
|
|
|
|
if (!handle_setup(&setup))
|
|
|
|
return 0;
|
|
|
|
if (!(setup.bmRequestType & 0x80) && eps[0].state == EP_IDLE)
|
|
|
|
usb_send(&eps[0], NULL, 0, NULL, NULL);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-30 00:14:11 +02:00
|
|
|
static bool ep_rx(struct ep_descr *ep)
|
2011-02-14 17:01:11 +02:00
|
|
|
{
|
|
|
|
uint8_t size;
|
|
|
|
|
|
|
|
size = UEBCLX;
|
|
|
|
if (size > ep->end-ep->buf)
|
|
|
|
return 0;
|
|
|
|
while (size--)
|
|
|
|
*ep->buf++ = UEDATX;
|
|
|
|
if (ep->buf == ep->end) {
|
|
|
|
ep->state = EP_IDLE;
|
|
|
|
if (ep->callback)
|
|
|
|
ep->callback(ep->user);
|
2011-06-20 20:34:08 +03:00
|
|
|
// if (ep == &eps[0])
|
2011-02-14 17:01:11 +02:00
|
|
|
usb_send(ep, NULL, 0, NULL, NULL);
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void ep_tx(struct ep_descr *ep)
|
|
|
|
{
|
|
|
|
uint8_t size = ep->end-ep->buf;
|
|
|
|
uint8_t left;
|
|
|
|
|
|
|
|
if (size > ep->size)
|
|
|
|
size = ep->size;
|
|
|
|
for (left = size; left; left--)
|
|
|
|
UEDATX = *ep->buf++;
|
|
|
|
if (size == ep->size)
|
|
|
|
return;
|
|
|
|
ep->state = EP_IDLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void handle_ep(int n)
|
|
|
|
{
|
|
|
|
struct ep_descr *ep = eps+n;
|
2011-06-20 20:34:08 +03:00
|
|
|
uint8_t mask;
|
2011-02-14 17:01:11 +02:00
|
|
|
|
|
|
|
UENUM = n;
|
|
|
|
if (UEINTX & (1 << RXSTPI)) {
|
|
|
|
/* @@@ EP_RX. EP_TX: cancel */
|
2011-03-11 22:45:45 +02:00
|
|
|
ep->state = EP_IDLE;
|
2011-02-14 17:01:11 +02:00
|
|
|
if (!ep_setup())
|
|
|
|
goto stall;
|
2011-06-11 13:21:17 +03:00
|
|
|
UEINTX = ~(1 << RXSTPI);
|
2011-02-14 17:01:11 +02:00
|
|
|
}
|
|
|
|
if (UEINTX & (1 << RXOUTI)) {
|
|
|
|
/* @@ EP_TX: cancel */
|
|
|
|
if (ep->state != EP_RX)
|
|
|
|
goto stall;
|
|
|
|
if (!ep_rx(ep))
|
|
|
|
goto stall;
|
2011-06-11 13:21:17 +03:00
|
|
|
/* @@@ gcc 4.5.2 wants this cast */
|
|
|
|
UEINTX = (uint8_t) ~(1 << RXOUTI | 1 << FIFOCON);
|
2011-02-14 17:01:11 +02:00
|
|
|
}
|
|
|
|
if (UEINTX & (1 << STALLEDI)) {
|
|
|
|
ep->state = EP_IDLE;
|
2011-06-11 13:21:17 +03:00
|
|
|
UEINTX = ~(1 << STALLEDI);
|
2011-02-14 17:01:11 +02:00
|
|
|
}
|
|
|
|
if (UEINTX & (1 << TXINI)) {
|
2011-03-11 22:45:45 +02:00
|
|
|
/* @@ EP_RX: cancel (?) */
|
2011-02-14 17:01:11 +02:00
|
|
|
if (ep->state == EP_TX) {
|
|
|
|
ep_tx(ep);
|
2011-06-20 20:34:08 +03:00
|
|
|
mask = 1 << TXINI;
|
|
|
|
if (n)
|
|
|
|
mask |= 1 << FIFOCON;
|
|
|
|
UEINTX = ~mask;
|
2011-02-14 17:01:11 +02:00
|
|
|
if (ep->state == EP_IDLE && ep->callback)
|
|
|
|
ep->callback(ep->user);
|
2011-06-11 20:17:44 +03:00
|
|
|
} else {
|
|
|
|
UEIENX &= ~(1 << TXINE);
|
2011-02-14 17:01:11 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
|
|
|
|
stall:
|
2011-06-11 13:21:17 +03:00
|
|
|
UEINTX = ~(1 << RXSTPI | 1 << RXOUTI | 1 << STALLEDI);
|
2011-02-14 17:01:11 +02:00
|
|
|
ep->state = EP_IDLE;
|
|
|
|
UECONX |= 1 << STALLRQ;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-05-10 03:13:41 +03:00
|
|
|
static void ep_init(void)
|
|
|
|
{
|
|
|
|
UENUM = 0;
|
|
|
|
UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
|
|
|
|
UECFG0X = 0; /* control, direction is ignored */
|
|
|
|
UECFG1X = 3 << EPSIZE0; /* 64 bytes */
|
|
|
|
UECFG1X |= 1 << ALLOC;
|
|
|
|
|
|
|
|
while (!(UESTA0X & (1 << CFGOK)));
|
|
|
|
|
2011-06-11 17:06:18 +03:00
|
|
|
UEIENX =
|
|
|
|
(1 << RXSTPE) | (1 << RXOUTE) | (1 << STALLEDE) | (1 << TXINE);
|
|
|
|
|
2011-05-10 03:13:41 +03:00
|
|
|
eps[0].state = EP_IDLE;
|
|
|
|
eps[0].size = 64;
|
2011-06-20 20:34:08 +03:00
|
|
|
|
2011-06-26 12:59:13 +03:00
|
|
|
#ifndef BOOT_LOADER
|
|
|
|
|
2011-06-20 20:34:08 +03:00
|
|
|
UENUM = 1;
|
|
|
|
UECONX = (1 << RSTDT) | (1 << EPEN); /* enable */
|
|
|
|
UECFG0X = (1 << EPTYPE1) | (1 << EPDIR); /* bulk IN */
|
|
|
|
UECFG1X = 3 << EPSIZE0; /* 64 bytes */
|
|
|
|
UECFG1X |= 1 << ALLOC;
|
|
|
|
|
|
|
|
while (!(UESTA0X & (1 << CFGOK)));
|
|
|
|
|
|
|
|
UEIENX = (1 << STALLEDE) | (1 << TXINE);
|
|
|
|
|
|
|
|
eps[1].state = EP_IDLE;
|
|
|
|
eps[1].size = 64;
|
2011-06-26 12:59:13 +03:00
|
|
|
|
|
|
|
#endif
|
2011-05-10 03:13:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-06-11 17:06:18 +03:00
|
|
|
ISR(USB_GEN_vect)
|
2011-02-14 17:01:11 +02:00
|
|
|
{
|
2011-06-11 17:06:18 +03:00
|
|
|
uint8_t flags;
|
2011-02-14 17:01:11 +02:00
|
|
|
|
2011-03-11 22:52:03 +02:00
|
|
|
flags = UDINT;
|
2011-05-10 03:13:41 +03:00
|
|
|
if (flags & (1 << EORSTI)) {
|
2011-03-11 22:52:03 +02:00
|
|
|
if (user_reset)
|
|
|
|
user_reset();
|
2011-05-10 03:13:41 +03:00
|
|
|
ep_init();
|
2011-06-11 13:21:17 +03:00
|
|
|
UDINT = ~(1 << EORSTI);
|
2011-03-11 22:52:03 +02:00
|
|
|
}
|
2011-06-11 17:06:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ISR(USB_COM_vect)
|
|
|
|
{
|
|
|
|
uint8_t flags, i;
|
|
|
|
|
2011-02-14 17:01:11 +02:00
|
|
|
flags = UEINT;
|
|
|
|
for (i = 0; i != NUM_EPS; i++)
|
2011-06-11 17:06:18 +03:00
|
|
|
if (flags & (1 << i))
|
2011-02-14 17:01:11 +02:00
|
|
|
handle_ep(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-03-11 22:52:03 +02:00
|
|
|
void usb_reset(void)
|
|
|
|
{
|
|
|
|
UDCON |= 1 << DETACH; /* detach the pull-up */
|
|
|
|
_delay_ms(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-02-14 17:01:11 +02:00
|
|
|
void usb_init(void)
|
|
|
|
{
|
|
|
|
USBCON |= 1 << FRZCLK; /* freeze the clock */
|
|
|
|
|
|
|
|
/* enable the PLL and wait for it to lock */
|
2015-05-24 15:37:38 +03:00
|
|
|
#ifdef ATUSB
|
2011-02-14 17:01:11 +02:00
|
|
|
PLLCSR &= ~(1 << PLLP2 | 1 << PLLP1 | 1 << PLLP0);
|
2015-05-24 15:37:38 +03:00
|
|
|
#endif
|
|
|
|
#ifdef RZUSB
|
|
|
|
/* TODO sheet page 50 For Atmel AT90USB128x only. Do not use with Atmel AT90USB64x. */
|
|
|
|
/* FOR 8 XTAL Mhz only!!! */
|
|
|
|
PLLCSR = ((1 << PLLP1) | (1 << PLLP0));
|
|
|
|
#endif
|
2011-02-14 17:01:11 +02:00
|
|
|
PLLCSR |= 1 << PLLE;
|
|
|
|
while (!(PLLCSR & (1 << PLOCK)));
|
|
|
|
|
2015-05-24 15:37:38 +03:00
|
|
|
#ifdef ATUSB
|
2011-02-14 17:01:11 +02:00
|
|
|
USBCON &= ~(1 << USBE); /* reset the controller */
|
|
|
|
USBCON |= 1 << USBE;
|
2015-05-24 15:37:38 +03:00
|
|
|
#endif
|
|
|
|
#ifdef RZUSB
|
|
|
|
UHWCON |= (1 << UVREGE);
|
|
|
|
|
|
|
|
USBCON &= ~((1 << USBE) | (1 << OTGPADE)); /* reset the controller */
|
|
|
|
USBCON |= ((1 << USBE) | (1 << OTGPADE));
|
|
|
|
#endif
|
2011-02-14 17:01:11 +02:00
|
|
|
|
|
|
|
USBCON &= ~(1 << FRZCLK); /* thaw the clock */
|
|
|
|
|
|
|
|
UDCON &= ~(1 << DETACH); /* attach the pull-up */
|
2011-06-11 17:06:18 +03:00
|
|
|
UDIEN = 1 << EORSTE; /* enable device interrupts */
|
2015-03-17 16:30:42 +02:00
|
|
|
// UDCON |= 1 << RSTCPU; /* reset CPU on bus reset */
|
2011-02-14 17:01:11 +02:00
|
|
|
|
|
|
|
ep_init();
|
|
|
|
}
|