From 8e633385912494ff5e871ec4c264d3a7db46fb98 Mon Sep 17 00:00:00 2001 From: Marcin Bukat Date: Thu, 18 Jul 2013 23:55:35 +0200 Subject: hwstub rk27xx port Change-Id: I85ac57117911544b65ccd56eb16303e30be67cab --- utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c | 313 ++++++++++++++++++++++++++++++ 1 file changed, 313 insertions(+) create mode 100644 utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c (limited to 'utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c') diff --git a/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c b/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c new file mode 100644 index 0000000000..6a9293334a --- /dev/null +++ b/utils/hwstub/stub/rk27xx/usb_drv_rk27xx.c @@ -0,0 +1,313 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * + * Copyright (C) 2011 by Marcin Bukat + * Copyright (C) 2012 by Amaury Pouly + * + * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "usb_drv.h" +#include "config.h" +#include "memory.h" +#include "target.h" +#include "rk27xx.h" + +typedef volatile uint32_t reg32; + +#define USB_FULL_SPEED 0 +#define USB_HIGH_SPEED 1 + +/* max allowed packet size definitions */ +#define CTL_MAX_SIZE 64 + +struct endpoint_t { + const int type; /* EP type */ + const int dir; /* DIR_IN/DIR_OUT */ + const unsigned int intr_mask; + bool allocated; /* flag to mark EPs taken */ + volatile void *buf; /* tx/rx buffer address */ + volatile int len; /* size of the transfer (bytes) */ + volatile int cnt; /* number of bytes transfered/received */ +}; + +static struct endpoint_t ctrlep[2] = { + {USB_ENDPOINT_XFER_CONTROL, DIR_OUT, 0, true, NULL, 0, 0}, + {USB_ENDPOINT_XFER_CONTROL, DIR_IN, 0, true, NULL, 0, 0} +}; + +volatile bool setup_data_valid = false; +static volatile uint32_t setup_data[2]; + +static volatile bool usb_drv_send_done = false; + +void usb_drv_configure_endpoint(int ep_num, int type) +{ + /* not needed as we use EP0 only */ + (void)ep_num; + (void)type; +} + +int usb_drv_recv_setup(struct usb_ctrlrequest *req) +{ + while (!setup_data_valid) + ; + + memcpy(req, (void *)setup_data, sizeof(struct usb_ctrlrequest)); + setup_data_valid = false; + return 0; +} + +static void setup_irq_handler(void) +{ + /* copy setup data from packet */ + setup_data[0] = SETUP1; + setup_data[1] = SETUP2; + + /* ack upper layer we have setup data */ + setup_data_valid = true; +} + +/* service ep0 IN transaction */ +static void ctr_write(void) +{ + int xfer_size = MIN(ctrlep[DIR_IN].cnt, CTL_MAX_SIZE); + + while (TX0BUF & TXFULL) /* TX0FULL flag */ + ; + + TX0STAT = xfer_size; /* size of the transfer */ + TX0DMALM_IADDR = (uint32_t)ctrlep[DIR_IN].buf; /* local buffer address */ + TX0DMAINCTL = DMA_START; /* start DMA */ + TX0CON &= ~TXNAK; /* clear NAK */ + + /* Decrement by max packet size is intentional. + * This way if we have final packet short one we will get negative len + * after transfer, which in turn indicates we *don't* need to send + * zero length packet. If the final packet is max sized packet we will + * get zero len after transfer which indicates we need to send + * zero length packet to signal host end of the transfer. + */ + ctrlep[DIR_IN].cnt -= CTL_MAX_SIZE; + ctrlep[DIR_IN].buf += xfer_size; +} + +static void ctr_read(void) +{ + int xfer_size = RX0STAT & 0xffff; + + /* clear NAK bit */ + RX0CON &= ~RXNAK; + + ctrlep[DIR_OUT].cnt -= xfer_size; + ctrlep[DIR_OUT].buf += xfer_size; + + RX0DMAOUTLMADDR = (uint32_t)ctrlep[DIR_OUT].buf; /* buffer address */ + RX0DMACTLO = DMA_START; /* start DMA */ +} + +static void udc_phy_reset(void) +{ + DEV_CTL |= SOFT_POR; + target_mdelay(10); /* min 10ms */ + DEV_CTL &= ~SOFT_POR; +} + +static void udc_soft_connect(void) +{ + DEV_CTL |= CSR_DONE | + DEV_SOFT_CN | + DEV_SELF_PWR; +} + +/* return port speed */ +int usb_drv_port_speed(void) +{ + return ((DEV_INFO & DEV_SPEED) ? USB_FULL_SPEED : USB_HIGH_SPEED); +} + +/* Set the address (usually it's in a register). + * There is a problem here: some controller want the address to be set between + * control out and ack and some want to wait for the end of the transaction. + * In the first case, you need to write some code special code when getting + * setup packets and ignore this function (have a look at other drives) + */ +void usb_drv_set_address(int address) +{ + (void)address; + /* UDC sets this automaticaly */ +} + +int usb_drv_send(int endpoint, void *ptr, int length) +{ + (void)endpoint; + struct endpoint_t *ep = &ctrlep[DIR_IN]; + + ep->buf = ptr; + ep->len = ep->cnt = length; + + ctr_write(); + + /* wait for transfer to end */ + while(!usb_drv_send_done) + ; + + usb_drv_send_done = false; + + return 0; +} + +/* Setup a receive transfer. (non blocking) */ +int usb_drv_recv(int endpoint, void* ptr, int length) +{ + (void)endpoint; + struct endpoint_t *ep = &ctrlep[DIR_OUT]; + + ep->buf = ptr; + ep->len = ep->cnt = length; + + /* clear NAK bit */ + RX0CON &= ~RXNAK; + RX0DMAOUTLMADDR = (uint32_t)ptr; /* buffer address */ + RX0DMACTLO = DMA_START; /* start DMA */ + + return 0; +} + +/* Stall the endpoint. Usually set a flag in the controller */ +void usb_drv_stall(int endpoint, bool stall, bool in) +{ + /* ctrl only anyway */ + (void)endpoint; + + if(in) + { + if(stall) + TX0CON |= TXSTALL; + else + TX0CON &= ~TXSTALL; + } + else + { + if (stall) + RX0CON |= RXSTALL; + else + RX0CON &= ~RXSTALL; /* doc says Auto clear by UDC 2.0 */ + } +} + +/* one time init (once per connection) - basicaly enable usb core */ +void usb_drv_init(void) +{ + udc_phy_reset(); + target_mdelay(10); /* wait at least 10ms */ + udc_soft_connect(); + + EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ + EN_RESUME_INTR | /* Enable Resume Irq */ + EN_USBRST_INTR | /* Enable USB Reset Irq */ + EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ + EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ + EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ + + INTCON = UDC_INTHIGH_ACT | /* interrupt high active */ + UDC_INTEN; /* enable EP0 irqs */ +} + +/* turn off usb core */ +void usb_drv_exit(void) +{ + /* udc module reset */ + SCU_RSTCFG |= (1<<1); + target_udelay(10); + SCU_RSTCFG &= ~(1<<1); +} + +/* UDC ISR function */ +void INT_UDC(void) +{ + uint32_t txstat, rxstat; + + /* read what caused UDC irq */ + uint32_t intsrc = INT2FLAG & 0x7fffff; + + if (intsrc & USBRST_INTR) /* usb reset */ + { + EN_INT = EN_SUSP_INTR | /* Enable Suspend Irq */ + EN_RESUME_INTR | /* Enable Resume Irq */ + EN_USBRST_INTR | /* Enable USB Reset Irq */ + EN_OUT0_INTR | /* Enable OUT Token receive Irq EP0 */ + EN_IN0_INTR | /* Enable IN Token transmit Irq EP0 */ + EN_SETUP_INTR; /* Enable SETUP Packet Receive Irq */ + + TX0CON = TXACKINTEN | /* Set as one to enable the EP0 tx irq */ + TXNAK; /* Set as one to response NAK handshake */ + + RX0CON = RXACKINTEN | + RXEPEN | /* Endpoint 0 Enable. When cleared the + * endpoint does not respond to an SETUP + * or OUT token */ + RXNAK; /* Set as one to response NAK handshake */ + } + + if (intsrc & SETUP_INTR) /* setup interrupt */ + { + setup_irq_handler(); + } + + if (intsrc & IN0_INTR) /* ep0 in interrupt */ + { + txstat = TX0STAT; /* read clears flags */ + + /* TODO handle errors */ + if (txstat & TXACK) /* check TxACK flag */ + { + if (ctrlep[DIR_IN].cnt > 0) + { + /* we still have data to send */ + ctr_write(); + } + else + { + if (ctrlep[DIR_IN].cnt == 0) + ctr_write(); + + /* final ack received */ + usb_drv_send_done = true; + } + } + } + + if (intsrc & OUT0_INTR) /* ep0 out interrupt */ + { + rxstat = RX0STAT; + + /* TODO handle errors */ + if (rxstat & RXACK) /* RxACK */ + { + if (ctrlep[DIR_OUT].cnt > 0) + ctr_read(); + } + } + + if (intsrc & RESUME_INTR) /* usb resume */ + { + TX0CON |= TXCLR; /* TxClr */ + TX0CON &= ~TXCLR; + + RX0CON |= RXCLR; /* RxClr */ + RX0CON &= ~RXCLR; + } +} -- cgit v1.2.3