/* Copyright (c) 2011,2012 Simon Schubert <2@0x2c.org>. * Modifications by Jacob Alexander 2014 * * 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 3 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, see . */ // ----- Defines ----- #define usb_xfer_info USB_STAT_t // ----- Local Includes ----- #include "mchck.h" #include "usb-internal.h" // ----- Functions ----- /** * Kinetis USB driver notes: * We need to manually maintain the DATA0/1 toggling for the SIE. * SETUP transactions always start with a DATA0. * * The SIE internally uses pingpong (double) buffering, which is * easily confused with DATA0/DATA1 toggling, and I think even the * Freescale docs confuse the two notions. When BD->DTS is set, * BD->DATA01 will be used to verify/discard incoming DATAx and it * will set the DATAx PID for outgoing tokens. This is not described * as such in the Freescale Kinetis docs, but the Microchip PIC32 OTG * docs are more clear on this; it seems that both Freescale and * Microchip use different versions of the same USB OTG IP core. * * http://ww1.microchip.com/downloads/en/DeviceDoc/61126F.pdf * * Clear CTL->TOKEN_BUSY after SETUP tokens. */ static struct USB_BD_t bdt[USB_MAX_EP * 2 *2] __attribute__((section(".usbdescriptortable"))); static struct USB_BD_t * usb_get_bd(struct usbd_ep_pipe_state_t *s) { return (&bdt[(s->ep_num << 2) | (s->ep_dir << 1) | s->pingpong]); } static struct USB_BD_t * usb_get_bd_stat(struct USB_STAT_t *stat) { return (((void *)(uintptr_t)bdt + (stat->raw << 1))); } void *usb_get_xfer_data(struct usb_xfer_info *i) { return (usb_get_bd_stat(i)->addr); } enum usb_tok_pid usb_get_xfer_pid(struct usb_xfer_info *i) { return (usb_get_bd_stat(i)->tok_pid); } int usb_get_xfer_ep(struct usb_xfer_info *i) { return (i->ep); } enum usb_ep_dir usb_get_xfer_dir(struct usb_xfer_info *i) { return (i->dir); } void usb_enable_xfers(void) { USB0.ctl.raw = ((struct USB_CTL_t){ .txd_suspend = 0, .usben = 1 }).raw; } void usb_set_addr(int addr) { USB0.addr.raw = addr; } void usb_pipe_stall(struct usbd_ep_pipe_state_t *s) { volatile struct USB_BD_t *bd = usb_get_bd(s); bd->raw = ((struct USB_BD_BITS_t){ .stall = 1, .own = 1 }).raw; } void usb_pipe_unstall(struct usbd_ep_pipe_state_t *s) { volatile struct USB_BD_t *bd = usb_get_bd(s); struct USB_BD_BITS_t state = { .raw = bd->raw }; if (state.own && state.stall) bd->raw = 0; } void usb_pipe_enable(struct usbd_ep_pipe_state_t *s) { USB0.endpt[s->ep_num].raw |= ((struct USB_ENDPT_t){ .eptxen = s->ep_dir == USB_EP_TX, .eprxen = s->ep_dir == USB_EP_RX, .ephshk = 1, /* XXX ISO */ .epctldis = s->ep_num != 0 }).raw; } void usb_pipe_disable(struct usbd_ep_pipe_state_t *s) { USB0.endpt[s->ep_num].raw &= ~((struct USB_ENDPT_t){ .eptxen = s->ep_dir == USB_EP_TX, .eprxen = s->ep_dir == USB_EP_RX, .epctldis = 1 }).raw; } size_t usb_ep_get_transfer_size(struct usbd_ep_pipe_state_t *s) { struct USB_BD_t *bd = usb_get_bd(s); return (bd->bc); } void usb_queue_next(struct usbd_ep_pipe_state_t *s, void *addr, size_t len) { volatile struct USB_BD_t *bd = usb_get_bd(s); bd->addr = addr; /* damn you bitfield problems */ bd->raw = ((struct USB_BD_BITS_t){ .dts = 1, .own = 1, .data01 = s->data01, .bc = len, }).raw; } static void usb_reset(void) { /* reset pingpong state */ /* For some obscure reason, we need to use or here. */ USB0.ctl.raw |= ((struct USB_CTL_t){ .txd_suspend = 1, .oddrst = 1, }).raw; /* clear all interrupt bits - not sure if needed */ USB0.istat.raw = 0xff; USB0.errstat.raw = 0xff; USB0.otgistat.raw = 0xff; /* zap also BDT pingpong & queued transactions */ memset(bdt, 0, sizeof(bdt)); USB0.addr.raw = 0; usb_restart(); USB0.ctl.raw = ((struct USB_CTL_t){ .txd_suspend = 0, .usben = 1 }).raw; /* we're only interested in reset and transfers */ USB0.inten.raw = ((struct USB_ISTAT_t){ .tokdne = 1, .usbrst = 1, .stall = 1, .sleep = 1, }).raw; USB0.usbtrc0.usbresmen = 0; USB0.usbctrl.susp = 0; } void usb_enable(void) { SIM.sopt2.usbsrc = 1; /* usb from mcg */ SIM.scgc4.usbotg = 1; /* enable usb clock */ /* reset module - not sure if needed */ USB0.usbtrc0.raw = ((struct USB_USBTRC0_t){ .usbreset = 1, .usbresmen = 1 }).raw; while (USB0.usbtrc0.usbreset) /* NOTHING */; USB0.bdtpage1 = (uintptr_t)bdt >> 8; USB0.bdtpage2 = (uintptr_t)bdt >> 16; USB0.bdtpage3 = (uintptr_t)bdt >> 24; USB0.control.raw = ((struct USB_CONTROL_t){ .dppullupnonotg = 1 /* enable pullup */ }).raw; USB0.usbctrl.raw = 0; /* resume peripheral & disable pulldowns */ usb_reset(); /* this will start usb processing */ /* really only one thing we want */ USB0.inten.raw = ((struct USB_ISTAT_t){ .usbrst = 1, }).raw; /** * Suspend transceiver now - we'll wake up at reset again. */ // TODO - Possible removal USB0.usbctrl.susp = 1; USB0.usbtrc0.usbresmen = 1; } void USB0_Handler(void) { struct USB_ISTAT_t stat = {.raw = USB0.istat.raw }; if (stat.usbrst) { usb_reset(); return; } if (stat.stall) { /* XXX need more work for non-0 ep */ volatile struct USB_BD_t *bd = usb_get_bd(&usb.ep_state[0].rx); if (bd->stall) usb_setup_control(); } if (stat.tokdne) { struct usb_xfer_info stat = USB0.stat; usb_handle_transaction(&stat); } if (stat.sleep) { USB0.inten.sleep = 0; USB0.inten.resume = 1; USB0.usbctrl.susp = 1; USB0.usbtrc0.usbresmen = 1; /** * Clear interrupts now so that we can detect a fresh * resume later on. */ USB0.istat.raw = stat.raw; const struct usbd_config *c = usb_get_config_data(-1); if (c && c->suspend) c->suspend(); } /** * XXX it is unclear whether we will receive a synchronous * resume interrupt if we were in sleep. This code assumes we * do. */ if (stat.resume || USB0.usbtrc0.usb_resume_int) { USB0.inten.resume = 0; USB0.inten.sleep = 1; USB0.usbtrc0.usbresmen = 0; USB0.usbctrl.susp = 0; const struct usbd_config *c = usb_get_config_data(-1); if (c && c->resume) c->resume(); stat.resume = 1; /* always clear bit */ } USB0.istat.raw = stat.raw; } void usb_poll(void) { USB0_Handler(); } int usb_tx_serialno(size_t reqlen) { struct usb_desc_string_t *d; const size_t nregs = 3; /** * actually 4, but UIDH is 0xffffffff. Also our output buffer * is only 64 bytes, and 128 bit + desc header exceeds this by * 2 bytes. */ const size_t len = nregs * 4 * 2 * 2 + sizeof(*d); d = usb_ep0_tx_inplace_prepare(len); if (d == NULL) return (-1); d->bLength = len; d->bDescriptorType = USB_DESC_STRING; size_t bufpos = 0; for (size_t reg = 0; reg < nregs; ++reg) { /* registers run MSW first */ uint32_t val = (&SIM.uidmh)[reg]; for (size_t bits = 32; bits > 0; bits -= 4, val <<= 4) { int nibble = val >> 28; if (nibble > 9) nibble += 'a' - '9' - 1; ((char16_t *)d->bString)[bufpos++] = nibble + '0'; } } usb_ep0_tx(d, len, reqlen, NULL, NULL); return (0); }