1080 lines
31 KiB
C
1080 lines
31 KiB
C
|
/***************************************************************************
|
||
|
*
|
||
|
* Copyright 2015-2019 BES.
|
||
|
* All rights reserved. All unpublished rights reserved.
|
||
|
*
|
||
|
* No part of this work may be used or reproduced in any form or by any
|
||
|
* means, or stored in a database or retrieval system, without prior written
|
||
|
* permission of BES.
|
||
|
*
|
||
|
* Use of this work is governed by a license granted by BES.
|
||
|
* This work contains confidential and proprietary information of
|
||
|
* BES. which is protected by copyright, trade secret,
|
||
|
* trademark and other intellectual property rights.
|
||
|
*
|
||
|
****************************************************************************/
|
||
|
#ifdef CHIP_HAS_USB
|
||
|
|
||
|
#include "plat_addr_map.h"
|
||
|
#include "reg_usb.h"
|
||
|
#include "hal_usbhost.h"
|
||
|
#include "hal_trace.h"
|
||
|
#include "hal_cmu.h"
|
||
|
#include "hal_timer.h"
|
||
|
#include "hal_sysfreq.h"
|
||
|
#include "string.h"
|
||
|
#include "cmsis_nvic.h"
|
||
|
#include "hwtimer_list.h"
|
||
|
#include "pmu.h"
|
||
|
|
||
|
#define MAX_CHAN_NUM 10
|
||
|
#define MAX_EP_NUM 16
|
||
|
|
||
|
#define MAX_XFER_SIZE (USBC_HCTSIZN_XFERSIZE_MASK >> USBC_HCTSIZN_XFERSIZE_SHIFT)
|
||
|
#define MAX_XFER_PKT (USBC_HCTSIZN_PKTCNT_MASK >> USBC_HCTSIZN_PKTCNT_SHIFT)
|
||
|
|
||
|
#define HAL_USBC_PHY_FREQ_MHZ 48
|
||
|
#define HAL_USBC_PHY_FREQ_MHZ_HS 60
|
||
|
#define HAL_USBC_HPRT_WC_MASK \
|
||
|
(USBC_HPRT_PRTSUSP | USBC_HPRT_PRTOVRCURRCHNG | USBC_HPRT_PRTENCHNG | USBC_HPRT_PRTENA | USBC_HPRT_PRTCONNDET)
|
||
|
|
||
|
#define HAL_USBHOST_TIMEOUT_TICKS MS_TO_TICKS(200)
|
||
|
|
||
|
#define USBHOST_TRACE(n, mask, str, ...) { if (usbhost_trmask & (1 << mask)) { TRACE(n, str, ##__VA_ARGS__); } }
|
||
|
#define USBHOST_FUNC_ENTRY_TRACE(mask) { if (usbhost_trmask & (1 << mask)) { FUNC_ENTRY_TRACE(); } }
|
||
|
|
||
|
enum HAL_USBHOST_CHAN_STATE_T {
|
||
|
HAL_USBHOST_CHAN_IDLE,
|
||
|
HAL_USBHOST_CHAN_ALLOC,
|
||
|
HAL_USBHOST_CHAN_INIT,
|
||
|
HAL_USBHOST_CHAN_XFER,
|
||
|
|
||
|
HAL_USBHOST_CHAN_QTY
|
||
|
};
|
||
|
|
||
|
struct HAL_USBHOST_CHAN_DESC_T {
|
||
|
enum HAL_USBHOST_CHAN_STATE_T state;
|
||
|
uint32_t start_xfer_size;
|
||
|
uint16_t start_pkt_cnt;
|
||
|
uint8_t err_cnt;
|
||
|
struct HAL_USBHOST_CHAN_TYPE_T type;
|
||
|
struct HAL_USBHOST_XFER_T xfer;
|
||
|
};
|
||
|
|
||
|
static const uint32_t usbhost_trmask = (1 << 0); //~0UL; //(1 << 3) | (1 << 4);
|
||
|
|
||
|
static struct USBC_T * const usbc = (struct USBC_T *)USB_BASE;
|
||
|
|
||
|
static HAL_USBHOST_PORT_HANDLER port_handler;
|
||
|
static HAL_USBHOST_DELAY_FUNC delay_func;
|
||
|
|
||
|
static struct HAL_USBHOST_CHAN_DESC_T chan_desc[MAX_CHAN_NUM];
|
||
|
|
||
|
static struct HAL_USBHOST_SETUP_XFER_T setup_xfer;
|
||
|
|
||
|
static volatile bool in_setup = false;
|
||
|
|
||
|
static bool usbhost_opened = false;
|
||
|
|
||
|
static enum HAL_USBHOST_SETUP_STAGE_T cur_setup_stage;
|
||
|
|
||
|
static HWTIMER_ID usbhost_timer[MAX_CHAN_NUM];
|
||
|
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
static HAL_USBHOST_PLUG_HANDLER plug_handler;
|
||
|
#endif
|
||
|
|
||
|
static uint32_t hal_usbhost_get_xfer_size(uint8_t chan, int complete);
|
||
|
static void hal_usbhost_irq_handler(void);
|
||
|
static void hal_usbhost_timeout(void *param);
|
||
|
|
||
|
void hal_usbhost_halt_chan(uint8_t chan)
|
||
|
{
|
||
|
uint32_t mask;
|
||
|
|
||
|
USBHOST_TRACE(2,17, "%s: %d", __FUNCTION__, chan);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
hwtimer_stop(usbhost_timer[chan]);
|
||
|
|
||
|
mask = usbc->HCSR[chan].HCINTMSKn;
|
||
|
usbc->HCSR[chan].HCINTMSKn = 0;
|
||
|
if ((usbc->HCSR[chan].HCCHARn & USBC_HCCHARN_CHENA) == 0) {
|
||
|
goto _exit;
|
||
|
}
|
||
|
usbc->HCSR[chan].HCINTn = USBC_HCINTN_CHHLTD;
|
||
|
usbc->HCSR[chan].HCCHARn |= USBC_HCCHARN_CHENA | USBC_HCCHARN_CHDIS;
|
||
|
while ((usbc->HCSR[chan].HCINTn & USBC_HCINTN_CHHLTD) == 0);
|
||
|
|
||
|
_exit:
|
||
|
usbc->HCSR[chan].HCINTn = ~0UL;
|
||
|
usbc->HCSR[chan].HCINTMSKn = mask;
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_soft_reset(void)
|
||
|
{
|
||
|
usbc->GRSTCTL |= USBC_CSFTRST;
|
||
|
while ((usbc->GRSTCTL & USBC_CSFTRST) != 0);
|
||
|
while ((usbc->GRSTCTL & USBC_AHBIDLE) == 0);
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_init_phy(void)
|
||
|
{
|
||
|
usbc->GUSBCFG |= USBC_FORCEHSTMODE | USBC_ULPIAUTORES | USBC_ULPIFSLS |
|
||
|
USBC_PHYSEL | USBC_ULPI_UTMI_SEL;
|
||
|
usbc->GUSBCFG &= ~(USBC_FSINTF | USBC_PHYIF | USBC_USBTRDTIM_MASK);
|
||
|
// USBC_USBTRDTIM(9) if AHB bus is 26M
|
||
|
usbc->GUSBCFG |= USBC_USBTRDTIM(5);
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_open(HAL_USBHOST_PORT_HANDLER port_cb, HAL_USBHOST_DELAY_FUNC delay_fn)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(16);
|
||
|
|
||
|
for (i = 0; i < MAX_CHAN_NUM; i++) {
|
||
|
chan_desc[i].state = HAL_USBHOST_CHAN_IDLE;
|
||
|
}
|
||
|
|
||
|
hal_sysfreq_req(HAL_SYSFREQ_USER_USB, HAL_CMU_FREQ_52M);
|
||
|
|
||
|
hal_cmu_usb_set_host_mode();
|
||
|
hal_cmu_usb_clock_enable();
|
||
|
|
||
|
hal_usbhost_soft_reset();
|
||
|
hal_usbhost_init_phy();
|
||
|
// Reset after selecting PHY
|
||
|
hal_usbhost_soft_reset();
|
||
|
// Some core cfg (except for PHY selection) will also be reset during soft reset
|
||
|
hal_usbhost_init_phy();
|
||
|
|
||
|
#ifdef USB_HIGH_SPEED
|
||
|
usbc->HCFG = USBC_HCFG_FSLSPCLKSEL(0);
|
||
|
#else
|
||
|
usbc->HCFG = USBC_HCFG_FSLSSUPP | USBC_HCFG_FSLSPCLKSEL(1);
|
||
|
#endif
|
||
|
|
||
|
usbc->HPRT = USBC_HPRT_PRTPWR;
|
||
|
|
||
|
// Clear previous interrupts
|
||
|
usbc->GINTMSK = 0;
|
||
|
usbc->GINTSTS = ~0UL;
|
||
|
usbc->HAINTMSK = 0;
|
||
|
for (i = 0; i < MAX_CHAN_NUM; i++) {
|
||
|
usbc->HCSR[i].HCINTMSKn = 0;
|
||
|
usbc->HCSR[i].HCINTn = ~0UL;
|
||
|
}
|
||
|
usbc->GINTMSK = USBC_PRTINT | USBC_HCHINT | USBC_DISCONNINT;
|
||
|
|
||
|
// Enable DMA mode
|
||
|
// Burst size 16 words
|
||
|
usbc->GAHBCFG = USBC_DMAEN | USBC_HBSTLEN(7);
|
||
|
usbc->GAHBCFG |= USBC_GLBLINTRMSK;
|
||
|
|
||
|
port_handler = port_cb;
|
||
|
delay_func = delay_fn;
|
||
|
|
||
|
usbhost_opened = true;
|
||
|
|
||
|
NVIC_SetVector(USB_IRQn, (uint32_t)hal_usbhost_irq_handler);
|
||
|
NVIC_SetPriority(USB_IRQn, IRQ_PRIORITY_NORMAL);
|
||
|
NVIC_ClearPendingIRQ(USB_IRQn);
|
||
|
NVIC_EnableIRQ(USB_IRQn);
|
||
|
|
||
|
//usbc->TPORTDBG1 = 0x11;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void hal_usbhost_close(void)
|
||
|
{
|
||
|
uint8_t chan;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(15);
|
||
|
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
pmu_usb_disable_pin_status_check();
|
||
|
#endif
|
||
|
|
||
|
NVIC_DisableIRQ(USB_IRQn);
|
||
|
|
||
|
usbhost_opened = false;
|
||
|
|
||
|
hal_cmu_usb_clock_disable();
|
||
|
|
||
|
for (chan = 0; chan < MAX_CHAN_NUM; chan++) {
|
||
|
hal_usbhost_free_chan(chan);
|
||
|
}
|
||
|
|
||
|
hal_sysfreq_req(HAL_SYSFREQ_USER_USB, HAL_CMU_FREQ_32K);
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_delay(uint32_t ms)
|
||
|
{
|
||
|
if (delay_func) {
|
||
|
delay_func(ms);
|
||
|
} else {
|
||
|
hal_sys_timer_delay(MS_TO_TICKS(ms));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void hal_usbhost_port_reset(uint32_t ms)
|
||
|
{
|
||
|
int lock;
|
||
|
|
||
|
USBHOST_TRACE(2,14, "%s: %d", __FUNCTION__, ms);
|
||
|
|
||
|
lock = int_lock();
|
||
|
usbc->HPRT = (usbc->HPRT & ~HAL_USBC_HPRT_WC_MASK) | USBC_HPRT_PRTRST;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
hal_usbhost_delay(ms);
|
||
|
|
||
|
lock = int_lock();
|
||
|
usbc->HPRT = (usbc->HPRT & ~HAL_USBC_HPRT_WC_MASK) & ~USBC_HPRT_PRTRST;
|
||
|
int_unlock(lock);
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_port_suspend(void)
|
||
|
{
|
||
|
int lock;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(22);
|
||
|
|
||
|
lock = int_lock();
|
||
|
usbc->HPRT = (usbc->HPRT & ~HAL_USBC_HPRT_WC_MASK) | USBC_HPRT_PRTSUSP;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
hal_usbhost_delay(3);
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_port_resume(void)
|
||
|
{
|
||
|
int lock;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(22);
|
||
|
|
||
|
lock = int_lock();
|
||
|
usbc->HPRT = (usbc->HPRT & ~HAL_USBC_HPRT_WC_MASK) | USBC_HPRT_PRTRES;
|
||
|
int_unlock(lock);
|
||
|
|
||
|
hal_usbhost_delay(20);
|
||
|
|
||
|
lock = int_lock();
|
||
|
usbc->HPRT = (usbc->HPRT & ~HAL_USBC_HPRT_WC_MASK) & ~USBC_HPRT_PRTRES;
|
||
|
int_unlock(lock);
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_get_chan(uint8_t *chan)
|
||
|
{
|
||
|
int i;
|
||
|
uint32_t lock;
|
||
|
|
||
|
lock = int_lock();
|
||
|
for (i = 0; i < MAX_CHAN_NUM; i++) {
|
||
|
if (chan_desc[i].state == HAL_USBHOST_CHAN_IDLE) {
|
||
|
chan_desc[i].state = HAL_USBHOST_CHAN_ALLOC;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
int_unlock(lock);
|
||
|
|
||
|
USBHOST_TRACE(2,13, "%s: %d", __FUNCTION__, i);
|
||
|
|
||
|
if (i == MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
*chan = i;
|
||
|
|
||
|
ASSERT(usbhost_timer[i] == NULL, "%s: Prev hwtimer not released: 0x%08x", __FUNCTION__, (uint32_t)usbhost_timer[i]);
|
||
|
usbhost_timer[i] = hwtimer_alloc(hal_usbhost_timeout, (void *)(uint32_t)i);
|
||
|
if (usbhost_timer[i] == NULL) {
|
||
|
USBHOST_TRACE(2,0, "%s: WARNING: Failed to alloc hwtimer for chan=%d", __FUNCTION__, i);
|
||
|
// Continue even if usbhost_timer is null
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_free_chan(uint8_t chan)
|
||
|
{
|
||
|
USBHOST_TRACE(2,12, "%s: %d", __FUNCTION__, chan);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
hwtimer_stop(usbhost_timer[chan]);
|
||
|
hwtimer_free(usbhost_timer[chan]);
|
||
|
usbhost_timer[chan] = NULL;
|
||
|
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_IDLE;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_init_chan(uint8_t chan, const struct HAL_USBHOST_CHAN_TYPE_T *type)
|
||
|
{
|
||
|
USBHOST_TRACE(7,11, "%s: chan=%d mps=%d ep=%d in=%d type=%d addr=%d",
|
||
|
__FUNCTION__, chan, type->mps, type->ep_num, type->ep_in, type->ep_type, type->dev_addr);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_ALLOC) {
|
||
|
return 2;
|
||
|
}
|
||
|
if (usbc->HCSR[chan].HCCHARn & USBC_HCCHARN_CHENA) {
|
||
|
return 3;
|
||
|
}
|
||
|
if ((type->dev_addr & (USBC_HCCHARN_DEVADDR_MASK >> USBC_HCCHARN_DEVADDR_SHIFT)) != type->dev_addr) {
|
||
|
return 4;
|
||
|
}
|
||
|
if (type->ep_num >= MAX_EP_NUM) {
|
||
|
return 5;
|
||
|
}
|
||
|
if (type->ep_type >= HAL_USBHOST_EP_QTY) {
|
||
|
return 6;
|
||
|
}
|
||
|
if ((type->mps & (USBC_HCCHARN_MPS_MASK >> USBC_HCCHARN_MPS_SHIFT)) != type->mps) {
|
||
|
return 7;
|
||
|
}
|
||
|
if (type->mps == 0) {
|
||
|
return 8;
|
||
|
}
|
||
|
|
||
|
memcpy(&chan_desc[chan].type, type, sizeof(chan_desc[chan].type));
|
||
|
chan_desc[chan].err_cnt = 0;
|
||
|
|
||
|
usbc->HCSR[chan].HCINTMSKn = 0;
|
||
|
usbc->HCSR[chan].HCINTn = ~0UL;
|
||
|
|
||
|
usbc->HCSR[chan].HCCHARn = USBC_HCCHARN_MPS(type->mps) | USBC_HCCHARN_EPNUM(type->ep_num) |
|
||
|
(type->ep_in ? USBC_HCCHARN_EPDIR : 0) | USBC_HCCHARN_EPTYPE(type->ep_type) |
|
||
|
USBC_HCCHARN_DEVADDR(type->dev_addr);
|
||
|
|
||
|
usbc->HCSR[chan].HCINTMSKn = USBC_HCINTN_AHBERR | USBC_HCINTN_CHHLTD;
|
||
|
usbc->HAINTMSK |= (1 << chan);
|
||
|
usbc->GINTMSK |= USBC_HCHINT;
|
||
|
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_update_chan_dev_addr(uint8_t chan, uint8_t dev_addr)
|
||
|
{
|
||
|
USBHOST_TRACE(3,10, "%s: chan=%d dev_addr=%d", __FUNCTION__, chan, dev_addr);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_INIT) {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
chan_desc[chan].type.dev_addr = dev_addr;
|
||
|
|
||
|
usbc->HCSR[chan].HCCHARn = SET_BITFIELD(usbc->HCSR[chan].HCCHARn, USBC_HCCHARN_DEVADDR, dev_addr);
|
||
|
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_update_chan_mps(uint8_t chan, uint16_t mps)
|
||
|
{
|
||
|
USBHOST_TRACE(3,9, "%s: chan=%d mps=%d", __FUNCTION__, chan, mps);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_INIT) {
|
||
|
return 2;
|
||
|
}
|
||
|
|
||
|
chan_desc[chan].type.mps = mps;
|
||
|
|
||
|
usbc->HCSR[chan].HCCHARn = SET_BITFIELD(usbc->HCSR[chan].HCCHARn, USBC_HCCHARN_MPS, mps);
|
||
|
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_start_xfer(uint8_t chan, const struct HAL_USBHOST_XFER_T *xfer)
|
||
|
{
|
||
|
uint32_t max_periodic_len;
|
||
|
uint32_t pkt_cnt;
|
||
|
uint32_t size;
|
||
|
enum HAL_USBHOST_PID_TYPE_T pid;
|
||
|
uint8_t multi_cnt;
|
||
|
|
||
|
USBHOST_TRACE(5,7, "%s: chan=%d size=%u mc=%d pid=%d",
|
||
|
__FUNCTION__, chan, xfer->size, xfer->multi_cnt, xfer->pid);
|
||
|
|
||
|
if (chan >= MAX_CHAN_NUM) {
|
||
|
return 1;
|
||
|
}
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_INIT) {
|
||
|
return 2;
|
||
|
}
|
||
|
if (usbc->HCSR[chan].HCCHARn & USBC_HCCHARN_CHENA) {
|
||
|
return 3;
|
||
|
}
|
||
|
if (((uint32_t)xfer->buf & 0x3) != 0) {
|
||
|
return 4;
|
||
|
}
|
||
|
if (chan_desc[chan].type.ep_type == HAL_USBHOST_EP_ISO ||
|
||
|
chan_desc[chan].type.ep_type == HAL_USBHOST_EP_INT) {
|
||
|
max_periodic_len = xfer->multi_cnt * chan_desc[chan].type.mps;
|
||
|
if (max_periodic_len < xfer->size) {
|
||
|
return 5;
|
||
|
}
|
||
|
} else {
|
||
|
if (xfer->size > MAX_XFER_SIZE) {
|
||
|
return 6;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pkt_cnt = (xfer->size + (chan_desc[chan].type.mps - 1)) / chan_desc[chan].type.mps;
|
||
|
if (pkt_cnt > MAX_XFER_PKT) {
|
||
|
return 7;
|
||
|
}
|
||
|
if (pkt_cnt == 0) {
|
||
|
pkt_cnt = 1;
|
||
|
}
|
||
|
|
||
|
if (chan_desc[chan].type.ep_in) {
|
||
|
size = pkt_cnt * chan_desc[chan].type.mps;
|
||
|
} else {
|
||
|
size = xfer->size;
|
||
|
}
|
||
|
|
||
|
chan_desc[chan].start_xfer_size = size;
|
||
|
chan_desc[chan].start_pkt_cnt = pkt_cnt;
|
||
|
memcpy(&chan_desc[chan].xfer, xfer, sizeof(chan_desc[chan].xfer));
|
||
|
|
||
|
pid = xfer->pid;
|
||
|
if (pid == HAL_USBHOST_PID_AUTO) {
|
||
|
pid = GET_BITFIELD(usbc->HCSR[chan].HCTSIZn, USBC_HCTSIZN_PID);
|
||
|
}
|
||
|
multi_cnt = xfer->multi_cnt;
|
||
|
|
||
|
if (chan_desc[chan].type.ep_type == HAL_USBHOST_EP_ISO ||
|
||
|
chan_desc[chan].type.ep_type == HAL_USBHOST_EP_INT) {
|
||
|
multi_cnt = pkt_cnt;
|
||
|
|
||
|
if (chan_desc[chan].type.ep_type == HAL_USBHOST_EP_ISO) {
|
||
|
pid = HAL_USBHOST_PID_DATA0; // Full speed
|
||
|
}
|
||
|
|
||
|
if (usbc->HFNUM & 0x1) {
|
||
|
usbc->HCSR[chan].HCCHARn &= ~USBC_HCCHARN_ODDFRM;
|
||
|
} else {
|
||
|
usbc->HCSR[chan].HCCHARn |= USBC_HCCHARN_ODDFRM;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_XFER;
|
||
|
|
||
|
usbc->HCSR[chan].HCTSIZn = USBC_HCTSIZN_PID(pid) | USBC_HCTSIZN_PKTCNT(pkt_cnt) | USBC_HCTSIZN_XFERSIZE(size);
|
||
|
usbc->HCSR[chan].HCDMAn = (uint32_t)xfer->buf;
|
||
|
|
||
|
usbc->HCSR[chan].HCINTn = ~0UL;
|
||
|
|
||
|
usbc->HCSR[chan].HCCHARn = SET_BITFIELD(usbc->HCSR[chan].HCCHARn, USBC_HCCHARN_EC, multi_cnt);
|
||
|
usbc->HCSR[chan].HCCHARn &= ~USBC_HCCHARN_CHDIS;
|
||
|
usbc->HCSR[chan].HCCHARn |= USBC_HCCHARN_CHENA;
|
||
|
|
||
|
if (xfer->handler) {
|
||
|
hwtimer_start(usbhost_timer[chan], HAL_USBHOST_TIMEOUT_TICKS);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_setup_xfer_handler(uint8_t chan, uint8_t *buf, uint32_t len, enum HAL_USBHOST_XFER_ERR_T error)
|
||
|
{
|
||
|
int ret;
|
||
|
struct HAL_USBHOST_XFER_T xfer;
|
||
|
enum HAL_USBHOST_SETUP_STAGE_T handler_stage;
|
||
|
uint8_t handler_chan;
|
||
|
|
||
|
ret = 1;
|
||
|
if (error != HAL_USBHOST_XFER_ERR_NONE) {
|
||
|
goto _exit;
|
||
|
}
|
||
|
|
||
|
USBHOST_TRACE(5,6, "%s: chan=%d cur=%d next=%d error=%d",
|
||
|
__FUNCTION__, chan, cur_setup_stage, setup_xfer.next_stage, error);
|
||
|
|
||
|
handler_stage = setup_xfer.next_stage;
|
||
|
handler_chan = HAL_USBHOST_CHAN_NONE;
|
||
|
|
||
|
switch (handler_stage) {
|
||
|
case HAL_USBHOST_SETUP_DATA_IN:
|
||
|
case HAL_USBHOST_SETUP_DATA_OUT:
|
||
|
xfer.buf = setup_xfer.data_buf;
|
||
|
xfer.size = setup_xfer.setup_pkt.wLength;
|
||
|
xfer.pid = HAL_USBHOST_PID_DATA1;
|
||
|
xfer.multi_cnt = 0;
|
||
|
xfer.handler = hal_usbhost_setup_xfer_handler;
|
||
|
if (handler_stage == HAL_USBHOST_SETUP_DATA_IN) {
|
||
|
handler_chan = setup_xfer.chan_in;
|
||
|
setup_xfer.next_stage = HAL_USBHOST_SETUP_STATUS_OUT;
|
||
|
} else {
|
||
|
handler_chan = setup_xfer.chan_out;
|
||
|
setup_xfer.next_stage = HAL_USBHOST_SETUP_STATUS_IN;
|
||
|
}
|
||
|
ret = hal_usbhost_start_xfer(handler_chan, &xfer);
|
||
|
if (ret) {
|
||
|
goto _exit;
|
||
|
}
|
||
|
break;
|
||
|
case HAL_USBHOST_SETUP_STATUS_IN:
|
||
|
case HAL_USBHOST_SETUP_STATUS_OUT:
|
||
|
if (cur_setup_stage == HAL_USBHOST_SETUP_DATA_IN) {
|
||
|
if (setup_xfer.setup_pkt.wLength != len) {
|
||
|
USBHOST_TRACE(2,0, "Invalid setup data length: %d expect=%d", len, setup_xfer.setup_pkt.wLength);
|
||
|
// Update received len
|
||
|
setup_xfer.setup_pkt.wLength = len;
|
||
|
}
|
||
|
}
|
||
|
xfer.buf = NULL;
|
||
|
xfer.size = 0;
|
||
|
xfer.pid = HAL_USBHOST_PID_DATA1;
|
||
|
xfer.multi_cnt = 0;
|
||
|
xfer.handler = hal_usbhost_setup_xfer_handler;
|
||
|
if (handler_stage == HAL_USBHOST_SETUP_STATUS_IN) {
|
||
|
handler_chan = setup_xfer.chan_in;
|
||
|
} else {
|
||
|
handler_chan = setup_xfer.chan_out;
|
||
|
}
|
||
|
setup_xfer.next_stage = HAL_USBHOST_SETUP_DONE;
|
||
|
ret = hal_usbhost_start_xfer(handler_chan, &xfer);
|
||
|
if (ret) {
|
||
|
goto _exit;
|
||
|
}
|
||
|
break;
|
||
|
case HAL_USBHOST_SETUP_DONE:
|
||
|
ret = 0;
|
||
|
goto _exit;
|
||
|
break;
|
||
|
default:
|
||
|
ASSERT(false, "Invalid setup next stage %d for chan %d", setup_xfer.next_stage, chan);
|
||
|
ret = 1;
|
||
|
goto _exit;
|
||
|
}
|
||
|
|
||
|
cur_setup_stage = handler_stage;
|
||
|
|
||
|
return;
|
||
|
|
||
|
_exit:
|
||
|
if (error != HAL_USBHOST_XFER_ERR_NONE) {
|
||
|
setup_xfer.next_stage = HAL_USBHOST_SETUP_ERROR;
|
||
|
}
|
||
|
|
||
|
{
|
||
|
struct HAL_USBHOST_SETUP_XFER_T setup;
|
||
|
|
||
|
memcpy(&setup, &setup_xfer, sizeof(setup));
|
||
|
in_setup = false;
|
||
|
if (setup.handler) {
|
||
|
setup.handler(&setup, ret);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
int hal_usbhost_start_setup_xfer(const struct HAL_USBHOST_SETUP_XFER_T *setup, uint32_t *recv_len)
|
||
|
{
|
||
|
int ret;
|
||
|
struct HAL_USBHOST_XFER_T xfer;
|
||
|
|
||
|
USBHOST_TRACE(7,6, "%s: out=%d in=%d type=0x%02x req=0x%02x wlen=%d next_stage=%d",
|
||
|
__FUNCTION__, setup->chan_out, setup->chan_in, setup->setup_pkt.bmRequestType,
|
||
|
setup->setup_pkt.bRequest, setup->setup_pkt.wLength, setup->next_stage);
|
||
|
|
||
|
if (setup->next_stage >= HAL_USBHOST_SETUP_STATUS_OUT) {
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (in_setup) {
|
||
|
return -2;
|
||
|
}
|
||
|
in_setup = true;
|
||
|
|
||
|
memcpy(&setup_xfer, setup, sizeof(setup_xfer));
|
||
|
cur_setup_stage = HAL_USBHOST_SETUP_STAGE_QTY;
|
||
|
|
||
|
xfer.buf = (uint8_t *)&setup_xfer.setup_pkt;
|
||
|
xfer.size = sizeof(setup_xfer.setup_pkt);
|
||
|
xfer.pid = HAL_USBHOST_PID_SETUP;
|
||
|
xfer.multi_cnt = 0;
|
||
|
xfer.handler = hal_usbhost_setup_xfer_handler;
|
||
|
|
||
|
ret = hal_usbhost_start_xfer(setup_xfer.chan_out, &xfer);
|
||
|
if (ret) {
|
||
|
in_setup = false;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
if (setup->handler == NULL) {
|
||
|
while (in_setup);
|
||
|
if (setup_xfer.next_stage != HAL_USBHOST_SETUP_DONE) {
|
||
|
return -3;
|
||
|
}
|
||
|
if (recv_len) {
|
||
|
if (setup->next_stage == HAL_USBHOST_SETUP_DATA_IN) {
|
||
|
*recv_len = setup_xfer.setup_pkt.wLength;
|
||
|
} else {
|
||
|
*recv_len = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_stop_all_chans(void)
|
||
|
{
|
||
|
int i;
|
||
|
uint32_t xfer;
|
||
|
HAL_USBHOST_XFER_COMPL_HANDLER handler;
|
||
|
uint8_t *buf;
|
||
|
|
||
|
usbc->HAINTMSK = 0;
|
||
|
|
||
|
for (i = 0; i < MAX_CHAN_NUM; i++) {
|
||
|
hal_usbhost_halt_chan(i);
|
||
|
|
||
|
if (chan_desc[i].state != HAL_USBHOST_CHAN_XFER) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (chan_desc[i].xfer.handler) {
|
||
|
// TODO: Check whether HCTSIZn is reset after channel is halted
|
||
|
xfer = hal_usbhost_get_xfer_size(i, 0);
|
||
|
|
||
|
// Reset the chan_desc to INIT state so that it can be reused in callback
|
||
|
handler = chan_desc[i].xfer.handler;
|
||
|
buf = chan_desc[i].xfer.buf;
|
||
|
chan_desc[i].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
handler(i, buf, xfer, HAL_USBHOST_XFER_ERR_DISCONN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
usbc->HAINT = ~0UL;
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_disconn_handler(void)
|
||
|
{
|
||
|
USBHOST_FUNC_ENTRY_TRACE(5);
|
||
|
|
||
|
usbc->GINTMSK &= ~USBC_HCHINT;
|
||
|
|
||
|
hal_usbhost_stop_all_chans();
|
||
|
|
||
|
if (port_handler) {
|
||
|
port_handler(HAL_USBHOST_PORT_DISCONN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_alloc_fifo(void)
|
||
|
{
|
||
|
// FIFO configuration should be started after port enabled, or 60 ms after soft reset
|
||
|
|
||
|
hal_usbhost_stop_all_chans();
|
||
|
|
||
|
// RX FIFO Calculation
|
||
|
// -------------------
|
||
|
// DATA Packets + Status Info : (MPS / 4 + 1) * m
|
||
|
// OutEp XFER COMPL : 1 * m
|
||
|
// NAK/NYET Handling : 1 * outEpNum
|
||
|
|
||
|
#define RXFIFOSIZE (2 * (MAX_USBHOST_PACKET_SIZE / 4 + 1 + 1) + USBHOST_EPNUM)
|
||
|
#define TXFIFOSIZE (2 * (MAX_USBHOST_PACKET_SIZE / 4))
|
||
|
|
||
|
// Rx Fifo Size (and init fifo_addr)
|
||
|
usbc->GRXFSIZ = USBC_RXFDEP(RXFIFOSIZE);
|
||
|
|
||
|
// EP0 / Non-periodic Tx Fifo Size
|
||
|
usbc->GNPTXFSIZ = USBC_NPTXFSTADDR(RXFIFOSIZE) | USBC_NPTXFDEPS(TXFIFOSIZE);
|
||
|
|
||
|
// Flush all FIFOs
|
||
|
usbc->GRSTCTL = USBC_TXFNUM(0x10) | USBC_TXFFLSH | USBC_RXFFLSH;
|
||
|
while ((usbc->GRSTCTL & (USBC_TXFFLSH | USBC_RXFFLSH)) != 0);
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_port_handler(void)
|
||
|
{
|
||
|
uint32_t prt;
|
||
|
uint32_t speed;
|
||
|
enum HAL_USBHOST_PORT_EVENT_T event;
|
||
|
|
||
|
prt = usbc->HPRT;
|
||
|
// USBC_HPRT_PRTENA also controls the port status
|
||
|
usbc->HPRT = prt & ~(USBC_HPRT_PRTENA | USBC_HPRT_PRTSUSP);
|
||
|
|
||
|
USBHOST_TRACE(2,4, "%s: 0x%08x", __FUNCTION__, prt);
|
||
|
|
||
|
if (prt & USBC_HPRT_PRTCONNDET) {
|
||
|
if (port_handler) {
|
||
|
port_handler(HAL_USBHOST_PORT_CONN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (prt & USBC_HPRT_PRTENCHNG) {
|
||
|
if (prt & USBC_HPRT_PRTENA) {
|
||
|
speed = GET_BITFIELD(usbc->HPRT,USBC_HPRT_PRTSPD);
|
||
|
if (speed == 1 ||
|
||
|
#ifdef USB_HIGH_SPEED
|
||
|
speed == 0 ||
|
||
|
#endif
|
||
|
0) {
|
||
|
#ifdef USB_HIGH_SPEED
|
||
|
if (speed == 0) {
|
||
|
// High speed
|
||
|
usbc->HFIR = SET_BITFIELD(usbc->HFIR, USBC_HFIR_FRINT, 125 * HAL_USBC_PHY_FREQ_MHZ_HS);
|
||
|
event = HAL_USBHOST_PORT_EN_HS;
|
||
|
} else
|
||
|
#else
|
||
|
{
|
||
|
// Full speed
|
||
|
usbc->HFIR = SET_BITFIELD(usbc->HFIR, USBC_HFIR_FRINT, 1000 * HAL_USBC_PHY_FREQ_MHZ);
|
||
|
event = HAL_USBHOST_PORT_EN_FS;
|
||
|
}
|
||
|
#endif
|
||
|
// Config FIFOs
|
||
|
hal_usbhost_alloc_fifo();
|
||
|
// Notify upper layer
|
||
|
if (port_handler) {
|
||
|
port_handler(event);
|
||
|
}
|
||
|
} else {
|
||
|
// High (0) or low (2) speed not supported
|
||
|
if (port_handler) {
|
||
|
port_handler(HAL_USBHOST_PORT_EN_BAD);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_retry_chan(uint8_t chan, uint32_t size)
|
||
|
{
|
||
|
uint32_t pkt_cnt;
|
||
|
|
||
|
USBHOST_TRACE(3,20, "%s: chan=%d size=%u", __FUNCTION__, chan, size);
|
||
|
|
||
|
#if 0
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_XFER) {
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
hal_usbhost_halt_chan(chan);
|
||
|
|
||
|
pkt_cnt = (size + chan_desc[chan].type.mps - 1) / chan_desc[chan].type.mps;
|
||
|
if (pkt_cnt == 0) {
|
||
|
pkt_cnt = 1;
|
||
|
}
|
||
|
|
||
|
usbc->HCSR[chan].HCTSIZn = (usbc->HCSR[chan].HCTSIZn & ~(USBC_HCTSIZN_PKTCNT_MASK | USBC_HCTSIZN_XFERSIZE_MASK)) |
|
||
|
USBC_HCTSIZN_PKTCNT(pkt_cnt) | USBC_HCTSIZN_XFERSIZE(size);
|
||
|
usbc->HCSR[chan].HCDMAn = (uint32_t)chan_desc[chan].xfer.buf + (chan_desc[chan].start_xfer_size - size);
|
||
|
|
||
|
usbc->HCSR[chan].HCINTn = ~0UL;
|
||
|
|
||
|
//usbc->HCSR[chan].HCCHARn = SET_BITFIELD(usbc->HCSR[chan].HCCHARn, USBC_HCCHARN_EC, multi_cnt);
|
||
|
usbc->HCSR[chan].HCCHARn &= ~USBC_HCCHARN_CHDIS;
|
||
|
usbc->HCSR[chan].HCCHARn |= USBC_HCCHARN_CHENA;
|
||
|
}
|
||
|
|
||
|
static uint32_t hal_usbhost_get_xfer_size(uint8_t chan, int complete)
|
||
|
{
|
||
|
uint32_t xfer;
|
||
|
|
||
|
if (complete) {
|
||
|
if (chan_desc[chan].type.ep_in) {
|
||
|
xfer = GET_BITFIELD(usbc->HCSR[chan].HCTSIZn, USBC_HCTSIZN_XFERSIZE);
|
||
|
if (chan_desc[chan].start_xfer_size > xfer) {
|
||
|
xfer = chan_desc[chan].start_xfer_size - xfer;
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
}
|
||
|
} else {
|
||
|
xfer = chan_desc[chan].start_xfer_size;
|
||
|
}
|
||
|
} else {
|
||
|
xfer = GET_BITFIELD(usbc->HCSR[chan].HCTSIZn, USBC_HCTSIZN_PKTCNT);
|
||
|
if (chan_desc[chan].start_pkt_cnt > xfer) {
|
||
|
xfer = (chan_desc[chan].start_pkt_cnt - xfer) * chan_desc[chan].type.mps;
|
||
|
} else {
|
||
|
xfer = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return xfer;
|
||
|
}
|
||
|
|
||
|
static enum HAL_USBHOST_XFER_ERR_T hal_usbhost_get_xfer_error(uint32_t irq)
|
||
|
{
|
||
|
if (irq & USBC_HCINTN_XFERCOMPL) return HAL_USBHOST_XFER_ERR_NONE;
|
||
|
if (irq & USBC_HCINTN_AHBERR) return HAL_USBHOST_XFER_ERR_AHB;
|
||
|
if (irq & USBC_HCINTN_STALL) return HAL_USBHOST_XFER_ERR_STALL;
|
||
|
if (irq & USBC_HCINTN_XACTERR) return HAL_USBHOST_XFER_ERR_TRANSACTION;
|
||
|
if (irq & USBC_HCINTN_BBLERR) return HAL_USBHOST_XFER_ERR_BABBLE;
|
||
|
if (irq & USBC_HCINTN_FRMOVRUN) return HAL_USBHOST_XFER_ERR_FRAME_OVERRUN;
|
||
|
if (irq & USBC_HCINTN_DATATGLERR) return HAL_USBHOST_XFER_ERR_DATA_TOGGLE;
|
||
|
|
||
|
return HAL_USBHOST_XFER_ERR_QTY;
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_chan_n_handler(uint8_t chan)
|
||
|
{
|
||
|
uint32_t raw_irq;
|
||
|
uint32_t irq;
|
||
|
uint32_t xfer;
|
||
|
enum HAL_USBHOST_XFER_ERR_T error;
|
||
|
HAL_USBHOST_XFER_COMPL_HANDLER handler;
|
||
|
uint8_t *buf;
|
||
|
|
||
|
USBHOST_TRACE(2,3, "%s: %d", __FUNCTION__, chan);
|
||
|
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_XFER) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
raw_irq = usbc->HCSR[chan].HCINTn;
|
||
|
usbc->HCSR[chan].HCINTn = raw_irq;
|
||
|
irq = raw_irq & usbc->HCSR[chan].HCINTMSKn;
|
||
|
|
||
|
xfer = hal_usbhost_get_xfer_size(chan, (raw_irq & USBC_HCINTN_XFERCOMPL));
|
||
|
|
||
|
USBHOST_TRACE(4,18, "%s: chan=%d HCINTn=0x%08x xfer=%u", __FUNCTION__, chan, raw_irq, xfer);
|
||
|
|
||
|
error = HAL_USBHOST_XFER_ERR_QTY;
|
||
|
|
||
|
if (chan_desc[chan].type.ep_type == HAL_USBHOST_EP_BULK ||
|
||
|
chan_desc[chan].type.ep_type == HAL_USBHOST_EP_CTRL) {
|
||
|
if (chan_desc[chan].type.ep_in) {
|
||
|
if (raw_irq & USBC_HCINTN_CHHLTD) {
|
||
|
if (raw_irq & (USBC_HCINTN_XFERCOMPL | USBC_HCINTN_STALL | USBC_HCINTN_BBLERR)) {
|
||
|
chan_desc[chan].err_cnt = 0;
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~(USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_DATATGLERR);
|
||
|
error = hal_usbhost_get_xfer_error(raw_irq);
|
||
|
} else if (raw_irq & USBC_HCINTN_XACTERR) {
|
||
|
if (chan_desc[chan].err_cnt >= 2) {
|
||
|
error = HAL_USBHOST_XFER_ERR_TRANSACTION;
|
||
|
} else {
|
||
|
chan_desc[chan].err_cnt++;
|
||
|
usbc->HCSR[chan].HCINTMSKn |= USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_DATATGLERR;
|
||
|
hal_usbhost_retry_chan(chan, chan_desc[chan].start_xfer_size - xfer);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
} else if (raw_irq & (USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_DATATGLERR)) {
|
||
|
chan_desc[chan].err_cnt = 0;
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~(USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_DATATGLERR);
|
||
|
return;
|
||
|
}
|
||
|
} else {
|
||
|
if (raw_irq & USBC_HCINTN_CHHLTD) {
|
||
|
if (raw_irq & (USBC_HCINTN_XFERCOMPL | USBC_HCINTN_STALL)) {
|
||
|
chan_desc[chan].err_cnt = 0;
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~(USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_NYET);
|
||
|
error = hal_usbhost_get_xfer_error(raw_irq);
|
||
|
} else if (raw_irq & USBC_HCINTN_XACTERR) {
|
||
|
if (chan_desc[chan].err_cnt >= 2) {
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~(USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_NYET);
|
||
|
error = HAL_USBHOST_XFER_ERR_TRANSACTION;
|
||
|
} else {
|
||
|
chan_desc[chan].err_cnt++;
|
||
|
usbc->HCSR[chan].HCINTMSKn |= (USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_NYET);
|
||
|
hal_usbhost_retry_chan(chan, chan_desc[chan].start_xfer_size - xfer);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
} else if (raw_irq & (USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_NYET)) {
|
||
|
chan_desc[chan].err_cnt = 0;
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~(USBC_HCINTN_ACK | USBC_HCINTN_NAK | USBC_HCINTN_NYET);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (error == HAL_USBHOST_XFER_ERR_QTY) {
|
||
|
error = hal_usbhost_get_xfer_error(raw_irq);
|
||
|
}
|
||
|
|
||
|
if (error == HAL_USBHOST_XFER_ERR_QTY) {
|
||
|
// Unknown IRQ
|
||
|
usbc->HCSR[chan].HCINTMSKn &= ~irq;
|
||
|
USBHOST_TRACE(3,19, "%s: Got unknown IRQ chan=%d irq=0x%08x", __FUNCTION__, chan, irq);
|
||
|
} else {
|
||
|
// Stop xfer timer
|
||
|
hwtimer_stop(usbhost_timer[chan]);
|
||
|
|
||
|
// Reset the chan_desc to INIT state so that it can be reused in callback
|
||
|
handler = chan_desc[chan].xfer.handler;
|
||
|
buf = chan_desc[chan].xfer.buf;
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
if (error != HAL_USBHOST_XFER_ERR_NONE) {
|
||
|
usbc->HAINTMSK &= ~(1 << chan);
|
||
|
if ((raw_irq & USBC_HCINTN_CHHLTD) == 0) {
|
||
|
hal_usbhost_halt_chan(chan);
|
||
|
}
|
||
|
}
|
||
|
if (handler) {
|
||
|
handler(chan, buf, xfer, error);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_chan_handler(void)
|
||
|
{
|
||
|
uint8_t i;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(2);
|
||
|
|
||
|
for (i = 0; i < MAX_CHAN_NUM; i++) {
|
||
|
if (usbc->HAINT & (1 << i)) {
|
||
|
hal_usbhost_chan_n_handler(i);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_irq_handler(void)
|
||
|
{
|
||
|
uint32_t status;
|
||
|
|
||
|
// Store interrupt flag and reset it
|
||
|
status = usbc->GINTSTS;
|
||
|
usbc->GINTSTS = status;
|
||
|
|
||
|
status &= (usbc->GINTMSK & (USBC_PRTINT | USBC_HCHINT | USBC_DISCONNINT));
|
||
|
|
||
|
USBHOST_TRACE(2,1, "%s: 0x%08x", __FUNCTION__, status);
|
||
|
|
||
|
if (status & USBC_DISCONNINT) {
|
||
|
hal_usbhost_disconn_handler();
|
||
|
return;
|
||
|
}
|
||
|
if (status & USBC_PRTINT) {
|
||
|
hal_usbhost_port_handler();
|
||
|
}
|
||
|
if (status & USBC_HCHINT) {
|
||
|
hal_usbhost_chan_handler();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void hal_usbhost_timeout(void *param)
|
||
|
{
|
||
|
uint8_t chan = (uint8_t)(uint32_t)param;
|
||
|
uint32_t xfer;
|
||
|
HAL_USBHOST_XFER_COMPL_HANDLER handler;
|
||
|
uint8_t *buf;
|
||
|
|
||
|
USBHOST_TRACE(2,21, "%s: %d", __FUNCTION__, chan);
|
||
|
|
||
|
if (chan_desc[chan].state != HAL_USBHOST_CHAN_XFER) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
hal_usbhost_halt_chan(chan);
|
||
|
|
||
|
if (chan_desc[chan].xfer.handler) {
|
||
|
// TODO: Check whether HCTSIZn is reset after channel is halted
|
||
|
xfer = hal_usbhost_get_xfer_size(chan, 0);
|
||
|
|
||
|
// Reset the chan_desc to INIT state so that it can be reused in callback
|
||
|
handler = chan_desc[chan].xfer.handler;
|
||
|
buf = chan_desc[chan].xfer.buf;
|
||
|
chan_desc[chan].state = HAL_USBHOST_CHAN_INIT;
|
||
|
|
||
|
handler(chan, buf, xfer, HAL_USBHOST_XFER_ERR_TIMEOUT);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
static void hal_usbhost_pin_status_change(enum PMU_USB_PIN_CHK_STATUS_T status)
|
||
|
{
|
||
|
USBHOST_TRACE(2,24, "%s: %d", __FUNCTION__, status);
|
||
|
|
||
|
if (plug_handler) {
|
||
|
if (status == PMU_USB_PIN_CHK_DEV_CONN) {
|
||
|
plug_handler(HAL_USBHOST_PLUG_IN);
|
||
|
} else if (status == PMU_USB_PIN_CHK_DEV_DISCONN) {
|
||
|
plug_handler(HAL_USBHOST_PLUG_OUT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void hal_usbhost_detect(enum HAL_USBHOST_PLUG_STATUS_T status, HAL_USBHOST_PLUG_HANDLER handler)
|
||
|
{
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
enum PMU_USB_PIN_CHK_STATUS_T pmu_status;
|
||
|
|
||
|
USBHOST_FUNC_ENTRY_TRACE(23);
|
||
|
|
||
|
if (status == HAL_USBHOST_PLUG_IN) {
|
||
|
pmu_status = PMU_USB_PIN_CHK_DEV_CONN;
|
||
|
} else if (status == HAL_USBHOST_PLUG_OUT) {
|
||
|
pmu_status = PMU_USB_PIN_CHK_DEV_DISCONN;
|
||
|
} else {
|
||
|
pmu_status = PMU_USB_PIN_CHK_NONE;
|
||
|
}
|
||
|
|
||
|
plug_handler = handler;
|
||
|
if (handler && pmu_status != PMU_USB_PIN_CHK_NONE) {
|
||
|
pmu_usb_config_pin_status_check(pmu_status, hal_usbhost_pin_status_change, true);
|
||
|
} else {
|
||
|
pmu_usb_disable_pin_status_check();
|
||
|
}
|
||
|
#else
|
||
|
ASSERT(false, "No aux usb pin status check support");
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void hal_usbhost_sleep(void)
|
||
|
{
|
||
|
USBHOST_FUNC_ENTRY_TRACE(22);
|
||
|
|
||
|
if (usbhost_opened) {
|
||
|
hal_usbhost_port_suspend();
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
hal_cmu_clock_disable(HAL_CMU_MOD_H_USBC);
|
||
|
hal_cmu_clock_disable(HAL_CMU_MOD_O_USB);
|
||
|
pmu_usb_config_pin_status_check(PMU_USB_PIN_CHK_DEV_DISCONN, hal_usbhost_pin_status_change, true);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void hal_usbhost_wakeup(void)
|
||
|
{
|
||
|
USBHOST_FUNC_ENTRY_TRACE(22);
|
||
|
|
||
|
if (usbhost_opened) {
|
||
|
#ifdef PMU_USB_PIN_CHECK
|
||
|
pmu_usb_disable_pin_status_check();
|
||
|
hal_cmu_clock_enable(HAL_CMU_MOD_H_USBC);
|
||
|
hal_cmu_clock_enable(HAL_CMU_MOD_O_USB);
|
||
|
#endif
|
||
|
hal_usbhost_port_resume();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // CHIP_HAS_USB
|