minix/drivers/usbd/hcd/musb/musb_core.c
Kees Jongenburger ed3391fd23 arm:adding the usbd source code.
Change-Id: Ia3c50a8c5e11bf20100354de266913112cc236f9

http://gerrit.minix3.org/#/c/2689/
2014-07-28 17:05:38 +02:00

570 lines
16 KiB
C
Executable file

/*
* Implementation of low level MUSB core logic (variant independent)
*/
#include <string.h> /* memcpy */
#include <time.h> /* nanosleep */
#include <usb/hcd_common.h>
#include <usb/hcd_interface.h>
#include <usb/usb_common.h>
#include "musb_core.h"
#include "musb_regs.h"
/*===========================================================================*
* Local defines *
*===========================================================================*/
#define HCD_COPYBUF_BYTES 64 /* Stack allocated, must be multiple of 4 */
#define HCD_COPYBUF_WORDS (HCD_COPYBUF_BYTES/4)
/*===========================================================================*
* Local prototypes *
*===========================================================================*/
static void musb_set_state(musb_core_config *);
static int musb_check_rxpktrdy(void *);
static void musb_in_stage_cleanup(void *);
static void musb_clear_rxpktrdy(void *);
static void musb_clear_statuspkt(void *);
static int musb_get_count(void *);
static void musb_read_fifo(void *, hcd_reg1 *, int, int);
/*===========================================================================*
* *
* MUSB core implementation *
* *
*===========================================================================*/
/*===========================================================================*
* musb_set_state *
*===========================================================================*/
static void
musb_set_state(musb_core_config * cfg)
{
void * r;
hcd_reg1 idx;
DEBUG_DUMP;
r = cfg->regs;
/* Set EP and address to be used in next MUSB command */
/* Set EP by selecting INDEX */
idx = HCD_RD1(r, MUSB_REG_INDEX);
HCD_CLR(idx, 0x0F);
HCD_SET(idx, cfg->ep & 0x0F);
HCD_WR1(r, MUSB_REG_INDEX, idx);
/* Use device with address 'cfg->addr' */
HCD_WR2(r, MUSB_REG_RXFUNCADDR, cfg->addr);
HCD_WR2(r, MUSB_REG_TXFUNCADDR, cfg->addr);
}
/*===========================================================================*
* musb_check_rxpktrdy *
*===========================================================================*/
static int
musb_check_rxpktrdy(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Check for RXPKTRDY */
if (host_csr0 & MUSB_VAL_HOST_CSR0_RXPKTRDY)
return EXIT_SUCCESS;
return EXIT_FAILURE;
}
/*===========================================================================*
* musb_in_stage_cleanup *
*===========================================================================*/
static void
musb_in_stage_cleanup(void * cfg)
{
DEBUG_DUMP;
musb_clear_rxpktrdy(cfg);
musb_clear_statuspkt(cfg);
}
/*===========================================================================*
* musb_clear_rxpktrdy *
*===========================================================================*/
static void
musb_clear_rxpktrdy(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Clear RXPKTRDY to signal receive completion */
HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_RXPKTRDY);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_clear_statuspkt *
*===========================================================================*/
static void
musb_clear_statuspkt(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Clear STATUSPKT to signal status packet completion */
HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_STATUSPKT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_get_count *
*===========================================================================*/
static int
musb_get_count(void * cfg)
{
void * r;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Reserved part returns zero so no need to generalize
* this return for MUSB_REG_RXCOUNT */
return (int)(HCD_RD2(r, MUSB_REG_COUNT0));
}
/*===========================================================================*
* musb_read_fifo *
*===========================================================================*/
static void
musb_read_fifo(void * cfg, hcd_reg1 * output, int size, int fifo_num)
{
void * r;
hcd_reg4 * word;
hcd_reg4 copy_buf[HCD_COPYBUF_WORDS];
hcd_addr fifo_addr;
int limit;
int idx;
DEBUG_DUMP;
USB_ASSERT((fifo_num >= 0) && (fifo_num <= 4), "Wrong FIFO number");
r = ((musb_core_config *)cfg)->regs;
fifo_addr = MUSB_REG_FIFO0 + (fifo_num * MUSB_REG_FIFO_LEN);
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Read full words from MUSB FIFO */
while (size > 0) {
/* Largest amount of bytes that can be copied at a time */
limit = (size < HCD_COPYBUF_BYTES) ? size : HCD_COPYBUF_BYTES;
/* Start copying into that */
word = copy_buf;
/* Read words from FIFO into copy_buf */
for (idx = 0; idx < limit; idx += sizeof(*word))
*word++ = HCD_RD4(r, fifo_addr);
/* Copy and shift */
memcpy(output, copy_buf, limit);
output += limit;
size -= limit;
}
}
/*===========================================================================*
* musb_core_start *
*===========================================================================*/
void
musb_core_start(void * cfg)
{
void * r;
hcd_reg1 devctl;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Enable all interrupts valid for host */
HCD_WR1(r, MUSB_REG_INTRUSBE,
MUSB_VAL_INTRUSBE_SUSPEND |
MUSB_VAL_INTRUSBE_RESUME |
MUSB_VAL_INTRUSBE_RESET_BABBLE |
/* MUSB_VAL_INTRUSBE_SOF | */
MUSB_VAL_INTRUSBE_CONN |
MUSB_VAL_INTRUSBE_DISCON |
MUSB_VAL_INTRUSBE_SESSREQ |
MUSB_VAL_INTRUSBE_VBUSERR);
/* Start session */
devctl = HCD_RD1(r, MUSB_REG_DEVCTL);
HCD_SET(devctl, MUSB_VAL_DEVCTL_SESSION);
HCD_WR1(r, MUSB_REG_DEVCTL, devctl);
}
/*===========================================================================*
* musb_core_stop *
*===========================================================================*/
void
musb_core_stop(void * cfg)
{
void * r;
hcd_reg1 devctl;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* TODO: add hardware interrupt disable */
/* Stop session */
devctl = HCD_RD1(r, MUSB_REG_DEVCTL);
HCD_CLR(devctl, MUSB_VAL_DEVCTL_SESSION);
HCD_WR1(r, MUSB_REG_DEVCTL, devctl);
}
/*===========================================================================*
* musb_ep0_config *
*===========================================================================*/
void
musb_ep0_config(void * cfg)
{
void * r;
hcd_reg1 host_type0;
hcd_reg2 intrtxe;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set parameters temporarily */
musb_setup_device((musb_core_config *)cfg,
HCD_DEFAULT_EP,
HCD_DEFAULT_ADDR);
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Set high speed for EP0 */
host_type0 = HCD_RD1(r, MUSB_REG_HOST_TYPE0);
HCD_CLR(host_type0, MUSB_VAL_HOST_TYPE0_MASK);
HCD_SET(host_type0, MUSB_VAL_HOST_TYPE0_HIGH_SPEED);
HCD_WR1(r, MUSB_REG_HOST_TYPE0, host_type0);
/* Enable EP interrupt */
intrtxe = HCD_RD2(r, MUSB_REG_INTRTXE);
HCD_SET(intrtxe, MUSB_VAL_INTRTXE_EP0);
HCD_WR2(r, MUSB_REG_INTRTXE, intrtxe);
}
/*===========================================================================*
* *
* HCD interface implementation *
* *
*===========================================================================*/
/*===========================================================================*
* musb_setup_device *
*===========================================================================*/
void
musb_setup_device(void * cfg, hcd_reg1 ep, hcd_reg1 addr)
{
DEBUG_DUMP;
/* Assign */
((musb_core_config *)cfg)->ep = ep;
((musb_core_config *)cfg)->addr = addr;
}
/*===========================================================================*
* musb_reset_device *
*===========================================================================*/
void
musb_reset_device(void * cfg)
{
void * r;
hcd_reg1 power;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Write reset bit and high speed negotiation wait for at least
* 20ms for reset, clear reset bit and wait for device */
power = HCD_RD1(r, MUSB_REG_POWER);
HCD_SET(power, MUSB_VAL_POWER_RESET | MUSB_VAL_POWER_HSEN);
HCD_WR1(r, MUSB_REG_POWER, power);
{
/* Sleep 25ms */
struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(25)};
nanosleep(&nanotm, NULL);
}
power = HCD_RD1(r, MUSB_REG_POWER);
HCD_CLR(power, MUSB_VAL_POWER_RESET);
HCD_WR1(r, MUSB_REG_POWER, power);
{
/* Sleep 25ms */
struct timespec nanotm = {0, HCD_NANOSLEEP_MSEC(25)};
nanosleep(&nanotm, NULL);
}
}
/*===========================================================================*
* musb_setup_stage *
*===========================================================================*/
void
musb_setup_stage(void * cfg, hcd_ctrlrequest * setup)
{
void * r;
char * setup_byte;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
setup_byte = (char*)setup;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* TODO: check for ongoing transmission */
/* Put USB setup data into corresponding FIFO */
HCD_WR4(r, MUSB_REG_FIFO0, HCD_8TO32(&setup_byte[0]));
HCD_WR4(r, MUSB_REG_FIFO0, HCD_8TO32(&setup_byte[sizeof(hcd_reg4)]));
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Send actual packet from FIFO */
HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_TXPKTRDY |
MUSB_VAL_HOST_CSR0_SETUPPKT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_in_data_stage *
*===========================================================================*/
void
musb_in_data_stage(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Request IN DATA stage */
HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_REQPKT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_out_data_stage *
*===========================================================================*/
void
musb_out_data_stage(void * cfg)
{
DEBUG_DUMP;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* TODO: not needed for enumeration but will be needed later */
((void)cfg);
USB_MSG("NOT IMPLEMENTED");
}
/*===========================================================================*
* musb_in_status_stage *
*===========================================================================*/
void
musb_in_status_stage(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Request IN STATUS stage */
HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_REQPKT |
MUSB_VAL_HOST_CSR0_STATUSPKT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_out_status_stage *
*===========================================================================*/
void
musb_out_status_stage(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Request OUT STATUS stage */
HCD_SET(host_csr0, MUSB_VAL_HOST_CSR0_TXPKTRDY |
MUSB_VAL_HOST_CSR0_STATUSPKT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
}
/*===========================================================================*
* musb_read_data *
*===========================================================================*/
int
musb_read_data(void * cfg, hcd_reg1 * buffer, int buffer_num)
{
int count0;
DEBUG_DUMP;
/* Check if anything received at all */
if (EXIT_SUCCESS != musb_check_rxpktrdy(cfg)) {
USB_MSG("RXPKTRDY not set when receiving");
return HCD_READ_ERR;
}
/* Number of bytes received at EP0 */
count0 = musb_get_count(cfg);
/* Read from given FIFO */
if ((NULL != buffer) && (count0 > 0))
musb_read_fifo(cfg, buffer, count0, buffer_num);
/* Cleanup after reading */
musb_in_stage_cleanup(cfg);
return count0;
}
/*===========================================================================*
* musb_check_error *
*===========================================================================*/
int
musb_check_error(void * cfg)
{
void * r;
hcd_reg2 host_csr0;
DEBUG_DUMP;
r = ((musb_core_config *)cfg)->regs;
/* Set EP and device address to be used in this command */
musb_set_state((musb_core_config *)cfg);
/* Get control status register for EP 0 */
host_csr0 = HCD_RD2(r, MUSB_REG_HOST_CSR0);
/* Check for common errors */
if (host_csr0 & MUSB_VAL_HOST_CSR0_ERROR) {
USB_MSG("HOST_CSR0 ERROR: %04X", host_csr0);
HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_ERROR);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
return EXIT_FAILURE;
}
if (host_csr0 & MUSB_VAL_HOST_CSR0_RXSTALL) {
USB_MSG("HOST_CSR0 STALL: %04X", host_csr0);
HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_RXSTALL);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
return EXIT_FAILURE;
}
if (host_csr0 & MUSB_VAL_HOST_CSR0_NAK_TIMEOUT) {
USB_MSG("HOST_CSR0 NAK_TIMEOUT: %04X", host_csr0);
HCD_CLR(host_csr0, MUSB_VAL_HOST_CSR0_NAK_TIMEOUT);
HCD_WR2(r, MUSB_REG_HOST_CSR0, host_csr0);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}