usb: arc: implement batched request api

Change-Id: I9d677286589a336d7258cf2c9d3c7d2847243dfa
This commit is contained in:
mojyack 2025-12-19 17:13:57 +09:00
parent f84003fa40
commit 03060090c9
2 changed files with 197 additions and 5 deletions

View file

@ -1362,8 +1362,12 @@ Lyre prototype 1 */
//#define USB_HAS_INTERRUPT -- seems to be broken
#endif /* CONFIG_USBOTG */
#if CONFIG_USBOTG == USBOTG_ARC
#define USB_BATCH_SLOTS 16
#else
#define USB_BATCH_NON_NATIVE
#define USB_BATCH_SLOTS 1
#endif
/* define the class drivers to enable */
#ifdef BOOTLOADER

View file

@ -362,6 +362,7 @@ static const unsigned int pipe2mask[USB_NUM_ENDPOINTS*2] = {
/*-------------------------------------------------------------------------*/
static void transfer_completed(void);
static void control_received(void);
static void sof_received(void);
static int prime_transfer(int ep_num, void* ptr, int len, bool send, bool wait);
static void prepare_td(struct transfer_descriptor* td,
struct transfer_descriptor* previous_td, void *ptr, int len,int pipe);
@ -415,6 +416,14 @@ static void usb_drv_reset(void)
#endif
}
static void td_set_buf_ptr(struct transfer_descriptor* td, const void* ptr){
td->buff_ptr0 = (unsigned int)ptr;
td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
}
/* One-time driver startup init */
void usb_drv_startup(void)
{
@ -544,6 +553,12 @@ void usb_drv_int(void)
if (status & USBSTS_PORT_CHANGE) {
REG_USBSTS = USBSTS_PORT_CHANGE;
}
/* sof */
if (status & USBSTS_SOF) {
REG_USBSTS = USBSTS_SOF;
sof_received();
}
}
bool usb_drv_stalled(int endpoint,bool in)
@ -664,6 +679,183 @@ void usb_drv_set_test_mode(int mode)
REG_USBCMD |= USBCMD_RUN;
}
/* batched request api */
static struct transfer_descriptor batch_td_array[USB_BATCH_SLOTS] USB_DEVBSS_ATTR __attribute__((aligned(32)));
static uint8_t batch_ep = 0;
static uint8_t batch_write_cursor;
static bool batch_stopped;
static usb_drv_batch_get_more batch_get_more;
static int ep_to_pipe_index(uint8_t ep) {
const int ep_num = EP_NUM(ep);
const int ep_dir = EP_DIR(ep);
const int pipe = ep_num * 2 + (ep_dir == DIR_IN ? 1 : 0);
return pipe;
}
int usb_drv_batch_init(int ep, usb_drv_batch_get_more get_more) {
logf("batch init");
if(batch_ep != 0) {
logf("batch function not available");
return -1;
}
batch_ep = ep;
batch_get_more = get_more;
return 0;
}
int usb_drv_batch_deinit() {
logf("batch deinit");
usb_drv_batch_stop();
batch_ep = 0;
return 0;
}
/* returns whether priming is needed */
static bool batch_fill_tds(void) {
while(!(batch_td_array[batch_write_cursor].size_ioc_sts & DTD_STATUS_ACTIVE)) {
/* batch_get_more may call batch_stop() through:
* - batch_get_more()
* - pcm_play_dma_complete_callback()
* - sink_stop()
* - usb_drv_batch_stop() */
const void* ptr;
size_t len;
batch_get_more(&ptr, &len);
if(len == 0 || batch_stopped) {
return false;
}
struct transfer_descriptor* td = &batch_td_array[batch_write_cursor];
td_set_buf_ptr(td, ptr);
td->size_ioc_sts = (len << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE;
batch_write_cursor = (batch_write_cursor + 1) % USB_BATCH_SLOTS;
}
return true;
}
int usb_drv_batch_start(void) {
logf("batch start");
/* reset variables */
batch_write_cursor = 0;
batch_stopped = false;
/* chain tds */
memset(batch_td_array, 0, sizeof(struct transfer_descriptor) * USB_BATCH_SLOTS);
for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
struct transfer_descriptor* td = &batch_td_array[i];
td->next_td_ptr = (unsigned int)&batch_td_array[(i + 1) % USB_BATCH_SLOTS];
}
/* configure queue head */
const int pipe = ep_to_pipe_index(batch_ep);
struct queue_head* const qh = &qh_array[pipe];
qh->curr_dtd_ptr = (unsigned int)&batch_td_array[0]; /* or error check in sof_received may read random location */
qh->dtd.next_td_ptr = (unsigned int)&batch_td_array[0];
qh->dtd.size_ioc_sts = 0;
/* pull initial buffers */
batch_fill_tds();
/* monitor sof */
REG_USBINTR |= USBINTR_SOF_EN;
return 0;
}
int usb_drv_batch_stop(void) {
batch_stopped = true;
/* disable sof interrupt */
REG_USBINTR &= ~USBINTR_SOF_EN;
/* break the chain */
/* terminating qh->dtd.next_td_ptr is not reliable */
for(int i = 0; i < USB_BATCH_SLOTS; i += 1) {
struct transfer_descriptor* td = &batch_td_array[i];
td->next_td_ptr = DTD_NEXT_TERMINATE;
}
/* flush endpoint */
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
REG_ENDPTFLUSH = mask;
while (REG_ENDPTFLUSH & mask);
return 0;
}
#if defined(LOGF_ENABLE) && defined(ROCKBOX_HAS_LOGF)
void usb_drv_dump_regs(void) {
logf("==== register dump %ld ====", current_tick);
logf("USBSTS 0x%08X", REG_USBSTS);
logf("ENDPTSETUPSTAT 0x%08X", REG_ENDPTSETUPSTAT);
logf("ENDPTPRIME 0x%08X", REG_ENDPTPRIME);
logf("ENDPTFLUSH 0x%08X", REG_ENDPTFLUSH);
logf("ENDPTSTATUS 0x%08X", REG_ENDPTSTATUS);
logf("ENDPTCOMPLETE 0x%08X", REG_ENDPTCOMPLETE);
logf("ENDPTCTRL0 0x%08X", REG_ENDPTCTRL0);
logf("ENDPTCTRL1 0x%08X", REG_ENDPTCTRL1);
logf("ENDPTCTRL2 0x%08X", REG_ENDPTCTRL2);
}
static void dump_td_array(struct queue_head* qh, struct transfer_descriptor* tds, size_t size) {
void* current = (void*)qh->curr_dtd_ptr;
void* next = (void*)qh->dtd.next_td_ptr;
logf("==== td dump %ld n=%p c=%p ====", current_tick, next, current);
for(int i = 0; i < size; i += 1) {
int len = (tds[i].size_ioc_sts & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS;
char sts[] = {
&tds[i] == current ? 'c' : ' ',
&tds[i] == next ? 'n' : ' ',
i == batch_write_cursor ? 'w' : ' ',
'\0',
};
logf("%s td[%02d] status=0x%08X len=%d", sts, i, tds[i].size_ioc_sts, len);
}
}
void usb_drv_dump_tds(int ep) {
const int pipe = ep_to_pipe_index(ep);
struct queue_head* const qh = &qh_array[pipe];
dump_td_array(qh, &td_array[pipe * NUM_TDS_PER_EP], NUM_TDS_PER_EP);
}
void usb_drv_batch_dump_tds(void) {
const int pipe = ep_to_pipe_index(batch_ep);
struct queue_head* const qh = &qh_array[pipe];
dump_td_array(qh, batch_td_array, USB_BATCH_SLOTS);
}
#endif
static void sof_received(void) {
if(batch_ep == 0) {
/* should not happen */
return;
}
/* error recovery */
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
struct queue_head* const qh = &qh_array[pipe];
struct transfer_descriptor* const td = (void*)qh->curr_dtd_ptr;
if(td->size_ioc_sts & DTD_ERROR_MASK) {
logf("td error status=0x%08X", td->size_ioc_sts);
REG_ENDPTPRIME |= mask;
}
/* do refill */
if(batch_fill_tds()) {
const int pipe = ep_to_pipe_index(batch_ep);
const unsigned int mask = pipe2mask[pipe];
REG_ENDPTPRIME |= mask;
}
}
/*-------------------------------------------------------------------------*/
/* manual: 32.14.5.2 */
@ -854,11 +1046,7 @@ static void prepare_td(struct transfer_descriptor* td,
td->next_td_ptr = DTD_NEXT_TERMINATE;
td->size_ioc_sts = (len<< DTD_LENGTH_BIT_POS) |
DTD_STATUS_ACTIVE | DTD_IOC;
td->buff_ptr0 = (unsigned int)ptr;
td->buff_ptr1 = ((unsigned int)ptr & 0xfffff000) + 0x1000;
td->buff_ptr2 = ((unsigned int)ptr & 0xfffff000) + 0x2000;
td->buff_ptr3 = ((unsigned int)ptr & 0xfffff000) + 0x3000;
td->buff_ptr4 = ((unsigned int)ptr & 0xfffff000) + 0x4000;
td_set_buf_ptr(td, ptr);
td->reserved |= DTD_RESERVED_LENGTH_MASK & len;
td->reserved |= DTD_RESERVED_IN_USE;
td->reserved |= (pipe << DTD_RESERVED_PIPE_OFFSET);