| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301 |
- /**************************************************************************//**
- * @file ohci.c
- * @version V1.10
- * $Revision: 11 $
- * $Date: 14/10/03 1:54p $
- * @brief USB Host library OHCI (USB 1.1) host controller driver.
- *
- * @note
- * SPDX-License-Identifier: Apache-2.0
- * Copyright (C) 2017 Nuvoton Technology Corp. All rights reserved.
- *****************************************************************************/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include "nuc980.h"
- #include "usb.h"
- #include "hub.h"
- #include "ohci.h"
- /// @cond HIDDEN_SYMBOLS
- //#define TD_debug rt_kprintf
- #define TD_debug(...)
- //#define ED_debug rt_kprintf
- #define ED_debug(...)
- uint8_t _hcca_mem[256] __attribute__((aligned(256)));
- HCCA_T *_hcca;
- ED_T *_Ied[6];
- static ED_T *ed_remove_list;
- static void add_to_ED_remove_list(ED_T *ed)
- {
- ED_T *p;
- ED_debug("add_to_ED_remove_list - 0x%x (0x%x)\n", (int)ed, ed->Info);
- DISABLE_OHCI_IRQ();
- /* check if this ED found in ed_remove_list */
- p = ed_remove_list;
- while (p)
- {
- if (p == ed)
- {
- ENABLE_OHCI_IRQ(); /* This ED found in ed_remove_list */
- return; /* do nothing */
- }
- p = p->next;
- }
- ed->Info |= ED_SKIP; /* ask OHCI controller skip this ED */
- ed->next = ed_remove_list;
- ed_remove_list = ed; /* insert to the head of ed_remove_list */
- ENABLE_OHCI_IRQ();
- _ohci->HcInterruptStatus = USBH_HcInterruptStatus_SF_Msk;
- _ohci->HcInterruptEnable |= USBH_HcInterruptEnable_SF_Msk;
- usbh_delay_ms(2); /* Full speed wait 2 ms is enough */
- }
- static int ohci_reset(void)
- {
- volatile int t0;
- /* Disable HC interrupts */
- _ohci->HcInterruptDisable = USBH_HcInterruptDisable_MIE_Msk;
- /* HC Reset requires max 10 ms delay */
- _ohci->HcControl = 0;
- _ohci->HcCommandStatus = USBH_HcCommandStatus_HCR_Msk;
- usbh_delay_ms(10);
- /* Check if OHCI reset completed? */
- if ((USBH->HcCommandStatus & USBH_HcCommandStatus_HCR_Msk) != 0)
- {
- USB_error("Error! - USB OHCI reset timed out!\n");
- return -1;
- }
- USBH->HcRhStatus = USBH_HcRhStatus_OCI_Msk | USBH_HcRhStatus_LPS_Msk;
- USBH->HcControl = HCFS_RESET;
- usbh_delay_ms(10);
- /* Check if OHCI reset completed? */
- if ((USBH->HcCommandStatus & USBH_HcCommandStatus_HCR_Msk) != 0)
- {
- USB_error("Error! - USB HC reset timed out!\n");
- return -1;
- }
- return 0;
- }
- static void init_hcca_int_table()
- {
- ED_T *ed_p;
- int i, idx, interval;
- memset(_hcca->int_table, 0, sizeof(_hcca->int_table));
- for (i = 5; i >= 0; i--) /* interval = i^2 */
- {
- _Ied[i] = alloc_ohci_ED();
- _Ied[i]->Info = ED_SKIP;
- interval = 0x1 << i;
- for (idx = interval - 1; idx < 32; idx += interval)
- {
- if (_hcca->int_table[idx] == 0) /* is empty list, insert directly */
- {
- _hcca->int_table[idx] = (uint32_t)_Ied[i];
- }
- else
- {
- ed_p = (ED_T *)_hcca->int_table[idx];
- while (1)
- {
- if (ed_p == _Ied[i])
- break; /* already chained by previous visit */
- if (ed_p->NextED == 0) /* reach end of list? */
- {
- ed_p->NextED = (uint32_t)_Ied[i];
- break;
- }
- ed_p = (ED_T *)ed_p->NextED;
- }
- }
- }
- }
- }
- static ED_T *get_int_tree_head_node(int interval)
- {
- int i;
- for (i = 0; i < 5; i++)
- {
- interval >>= 1;
- if (interval == 0)
- return _Ied[i];
- }
- return _Ied[5]; /* for interval >= 32 */
- }
- static int get_ohci_interval(int interval)
- {
- int i, bInterval = 1;
- for (i = 0; i < 5; i++)
- {
- interval >>= 1;
- if (interval == 0)
- return bInterval;
- bInterval *= 2;
- }
- return 32; /* for interval >= 32 */
- }
- static int ohci_init(void)
- {
- uint32_t fminterval;
- volatile int i;
- _hcca = (HCCA_T *)((uint32_t)_hcca_mem | NON_CACHE_MASK);
- if (ohci_reset() < 0)
- return -1;
- ed_remove_list = NULL;
- init_hcca_int_table();
- /* Tell the controller where the control and bulk lists are
- * The lists are empty now. */
- _ohci->HcControlHeadED = 0; /* control ED list head */
- _ohci->HcBulkHeadED = 0; /* bulk ED list head */
- _ohci->HcHCCA = (uint32_t)_hcca; /* HCCA area */
- /* periodic start 90% of frame interval */
- fminterval = 0x2edf; /* 11,999 */
- _ohci->HcPeriodicStart = (fminterval * 9) / 10;
- /* set FSLargestDataPacket, 10,104 for 0x2edf frame interval */
- fminterval |= ((((fminterval - 210) * 6) / 7) << 16);
- _ohci->HcFmInterval = fminterval;
- _ohci->HcLSThreshold = 0x628;
- /* start controller operations */
- _ohci->HcControl = HCFS_OPER | (0x3 << USBH_HcControl_CBSR_Pos);
- #ifdef OHCI_PER_PORT_POWER
- _ohci->HcRhDescriptorB = 0x60000;
- for (i = 0; i < OHCI_PORT_CNT; i++)
- _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_PPS_Msk;
- #else
- _ohci->HcRhDescriptorA = (USBH->HcRhDescriptorA | (1 << 9)) & ~USBH_HcRhDescriptorA_PSM_Msk;
- _ohci->HcRhStatus = USBH_HcRhStatus_LPSC_Msk;
- #endif
- _ohci->HcInterruptEnable = USBH_HcInterruptEnable_MIE_Msk | USBH_HcInterruptEnable_WDH_Msk | USBH_HcInterruptEnable_SF_Msk;
- /* POTPGT delay is bits 24-31, in 20 ms units. */
- usbh_delay_ms(20);
- return 0;
- }
- static void ohci_suspend(void)
- {
- int i;
- for (i = 0; i < OHCI_PORT_CNT; i++)
- {
- /* set port suspend if connected */
- if (_ohci->HcRhPortStatus[i] & 0x1)
- _ohci->HcRhPortStatus[i] = 0x4;
- }
- /* enable Device Remote Wakeup */
- _ohci->HcRhStatus |= USBH_HcRhStatus_DRWE_Msk;
- /* enable USBH RHSC interrupt for system wakeup */
- _ohci->HcInterruptEnable |= USBH_HcInterruptEnable_RHSC_Msk | USBH_HcInterruptEnable_RD_Msk;
- /* set Host Controller enter suspend state */
- _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (3 << USBH_HcControl_HCFS_Pos);
- }
- static void ohci_resume(void)
- {
- int i;
- _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (1 << USBH_HcControl_HCFS_Pos);
- _ohci->HcControl = (USBH->HcControl & ~USBH_HcControl_HCFS_Msk) | (2 << USBH_HcControl_HCFS_Pos);
- for (i = 0; i < OHCI_PORT_CNT; i++)
- {
- if (_ohci->HcRhPortStatus[i] & 0x4)
- _ohci->HcRhPortStatus[i] = 0x8;
- }
- }
- static void ohci_shutdown(void)
- {
- ohci_suspend();
- DISABLE_OHCI_IRQ();
- #ifndef OHCI_PER_PORT_POWER
- _ohci->HcRhStatus = USBH_HcRhStatus_LPS_Msk;
- #endif
- }
- /*
- * Quit current trasnfer via UTR or hardware EP.
- */
- static int ohci_quit_xfer(UTR_T *utr, EP_INFO_T *ep)
- {
- ED_T *ed;
- if (utr != NULL)
- {
- if (utr->ep == NULL)
- return USBH_ERR_NOT_FOUND;
- ed = (ED_T *)(utr->ep->hw_pipe);
- if (!ed)
- return USBH_ERR_NOT_FOUND;
- /* add the endpoint to remove list, it will be removed on the next start of frame */
- add_to_ED_remove_list(ed);
- utr->ep->hw_pipe = NULL;
- }
- if ((ep != NULL) && (ep->hw_pipe != NULL))
- {
- ed = (ED_T *)(ep->hw_pipe);
- /* add the endpoint to remove list, it will be removed on the next start of frame */
- add_to_ED_remove_list(ed);
- ep->hw_pipe = NULL;
- }
- return 0;
- }
- uint32_t ed_make_info(UDEV_T *udev, EP_INFO_T *ep)
- {
- uint32_t info;
- if (ep == NULL) /* is a control endpoint */
- {
- /* control endpoint direction is from TD */
- if (udev->descriptor.bMaxPacketSize0 == 0) /* is 0 if device descriptor still not obtained. */
- {
- if (udev->speed == SPEED_LOW) /* give a default maximum packet size */
- udev->descriptor.bMaxPacketSize0 = 8;
- else
- udev->descriptor.bMaxPacketSize0 = 64;
- }
- info = (udev->descriptor.bMaxPacketSize0 << 16) /* Control endpoint Maximum Packet Size from device descriptor */
- | ED_DIR_BY_TD /* Direction (Get direction From TD) */
- | ED_FORMAT_GENERAL /* General format */
- | (0 << ED_CTRL_EN_Pos); /* Endpoint address 0 */
- }
- else /* Other endpoint direction is from endpoint descriptor */
- {
- info = (ep->wMaxPacketSize << 16); /* Maximum Packet Size from endpoint */
- info |= ((ep->bEndpointAddress & 0xf) << ED_CTRL_EN_Pos); /* Endpoint Number */
- if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_IN)
- info |= ED_DIR_IN;
- else
- info |= ED_DIR_OUT;
- if ((ep->bmAttributes & EP_ATTR_TT_MASK) == EP_ATTR_TT_ISO)
- info |= ED_FORMAT_ISO;
- else
- info |= ED_FORMAT_GENERAL;
- }
- info |= ((udev->speed == SPEED_LOW) ? ED_SPEED_LOW : ED_SPEED_FULL); /* Speed */
- info |= (udev->dev_num); /* Function Address */
- return info;
- }
- static void write_td(TD_T *td, uint32_t info, uint8_t *buff, uint32_t data_len)
- {
- td->Info = info;
- td->CBP = (uint32_t)((!buff || !data_len) ? 0 : buff);
- td->BE = (uint32_t)((!buff || !data_len) ? 0 : (uint32_t)buff + data_len - 1);
- td->buff_start = td->CBP;
- // TD_debug("TD [0x%x]: 0x%x, 0x%x, 0x%x\n", (int)td, td->Info, td->CBP, td->BE);
- }
- static int ohci_ctrl_xfer(UTR_T *utr)
- {
- UDEV_T *udev;
- ED_T *ed;
- TD_T *td_setup, *td_data, *td_status;
- uint32_t info;
- udev = utr->udev;
- /*------------------------------------------------------------------------------------*/
- /* Allocate ED and TDs */
- /*------------------------------------------------------------------------------------*/
- td_setup = alloc_ohci_TD(utr);
- if (utr->data_len > 0)
- td_data = alloc_ohci_TD(utr);
- else
- td_data = NULL;
- td_status = alloc_ohci_TD(utr);
- if (td_status == NULL)
- {
- free_ohci_TD(td_setup);
- if (utr->data_len > 0)
- free_ohci_TD(td_data);
- return USBH_ERR_MEMORY_OUT;
- }
- /* Check if there's any transfer pending on this endpoint... */
- if (udev->ep0.hw_pipe == NULL)
- {
- ed = alloc_ohci_ED();
- if (ed == NULL)
- {
- free_ohci_TD(td_setup);
- free_ohci_TD(td_status);
- if (utr->data_len > 0)
- free_ohci_TD(td_data);
- return USBH_ERR_MEMORY_OUT;
- }
- }
- else
- ed = (ED_T *)udev->ep0.hw_pipe;
- /*------------------------------------------------------------------------------------*/
- /* prepare SETUP stage TD */
- /*------------------------------------------------------------------------------------*/
- info = TD_CC | TD_T_DATA0 | TD_TYPE_CTRL;
- write_td(td_setup, info, (uint8_t *)&utr->setup, 8);
- td_setup->ed = ed;
- /*------------------------------------------------------------------------------------*/
- /* prepare DATA stage TD */
- /*------------------------------------------------------------------------------------*/
- if (utr->data_len > 0)
- {
- if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT)
- info = (TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 | TD_TYPE_CTRL | TD_CTRL_DATA);
- else
- info = (TD_CC | TD_R | TD_DP_IN | TD_T_DATA1 | TD_TYPE_CTRL | TD_CTRL_DATA);
- write_td(td_data, info, utr->buff, utr->data_len);
- td_data->ed = ed;
- td_setup->NextTD = (uint32_t)td_data;
- td_setup->next = td_data;
- td_data->NextTD = (uint32_t)td_status;
- td_data->next = td_status;
- }
- else
- {
- td_setup->NextTD = (uint32_t)td_status;
- td_setup->next = td_status;
- }
- /*------------------------------------------------------------------------------------*/
- /* prepare STATUS stage TD */
- /*------------------------------------------------------------------------------------*/
- ed->Info = ed_make_info(udev, NULL);
- if ((utr->setup.bmRequestType & 0x80) == REQ_TYPE_OUT)
- info = (TD_CC | TD_DP_IN | TD_T_DATA1 | TD_TYPE_CTRL);
- else
- info = (TD_CC | TD_DP_OUT | TD_T_DATA1 | TD_TYPE_CTRL);
- write_td(td_status, info, NULL, 0);
- td_status->ed = ed;
- td_status->NextTD = 0;
- td_status->next = 0;
- /*------------------------------------------------------------------------------------*/
- /* prepare ED */
- /*------------------------------------------------------------------------------------*/
- ed->TailP = 0;
- ed->HeadP = (uint32_t)td_setup;
- ed->Info = ed_make_info(udev, NULL);
- ed->NextED = 0;
- //TD_debug("TD SETUP [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_setup, td_setup->Info, td_setup->CBP, td_setup->BE, td_setup->NextTD);
- //if (td_data)
- // TD_debug("TD DATA [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_data, td_data->Info, td_data->CBP, td_data->BE, td_data->NextTD);
- //TD_debug("TD STATUS [0x%x]: 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td_status, td_status->Info, td_status->CBP, td_status->BE, td_status->NextTD);
- ED_debug("Xfer ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED);
- if (utr->data_len > 0)
- utr->td_cnt = 3;
- else
- utr->td_cnt = 2;
- utr->ep = &udev->ep0; /* driver can find EP from UTR */
- udev->ep0.hw_pipe = (void *)ed; /* driver can find ED from EP */
- /*------------------------------------------------------------------------------------*/
- /* Start transfer */
- /*------------------------------------------------------------------------------------*/
- DISABLE_OHCI_IRQ();
- _ohci->HcControlHeadED = (uint32_t)ed; /* Link ED to OHCI */
- _ohci->HcControl |= USBH_HcControl_CLE_Msk; /* enable control list */
- ENABLE_OHCI_IRQ();
- _ohci->HcCommandStatus = USBH_HcCommandStatus_CLF_Msk; /* start Control list */
- return 0;
- }
- static int ohci_bulk_xfer(UTR_T *utr)
- {
- UDEV_T *udev = utr->udev;
- EP_INFO_T *ep = utr->ep;
- ED_T *ed;
- TD_T *td, *td_p, *td_list = NULL;
- uint32_t info;
- uint32_t data_len, xfer_len;
- int8_t bIsNewED = 0;
- uint8_t *buff;
- /*------------------------------------------------------------------------------------*/
- /* Check if there's uncompleted transfer on this endpoint... */
- /* Prepare ED */
- /*------------------------------------------------------------------------------------*/
- info = ed_make_info(udev, ep);
- /* Check if there's any transfer pending on this endpoint... */
- ed = (ED_T *)_ohci->HcBulkHeadED; /* get the head of bulk endpoint list */
- while (ed != NULL)
- {
- if (ed->Info == info) /* have transfer of this EP not completed? */
- {
- if ((ed->HeadP & 0xFFFFFFF0) != (ed->TailP & 0xFFFFFFF0))
- return USBH_ERR_OHCI_EP_BUSY; /* endpoint is busy */
- else
- break; /* ED already there... */
- }
- ed = (ED_T *)ed->NextED;
- }
- if (ed == NULL)
- {
- bIsNewED = 1;
- ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */
- if (ed == NULL)
- return USBH_ERR_MEMORY_OUT;
- ed->Info = info;
- ed->HeadP = 0;
- ED_debug("Link BULK ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED);
- }
- ep->hw_pipe = (void *)ed;
- /*------------------------------------------------------------------------------------*/
- /* Prepare TDs */
- /*------------------------------------------------------------------------------------*/
- utr->td_cnt = 0;
- data_len = utr->data_len;
- buff = utr->buff;
- do
- {
- if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT)
- info = (TD_CC | TD_R | TD_DP_OUT | TD_TYPE_BULK);
- else
- info = (TD_CC | TD_R | TD_DP_IN | TD_TYPE_BULK);
- info &= ~(1 << 25); /* Data toggle from ED toggleCarry bit */
- if (data_len > 4096) /* maximum transfer length is 4K for each TD */
- xfer_len = 4096;
- else
- xfer_len = data_len; /* remaining data length < 4K */
- td = alloc_ohci_TD(utr); /* allocate a TD */
- if (td == NULL)
- goto mem_out;
- /* fill this TD */
- write_td(td, info, buff, xfer_len);
- td->ed = ed;
- utr->td_cnt++; /* increase TD count, for recalim counter */
- buff += xfer_len; /* advanced buffer pointer */
- data_len -= xfer_len;
- /* chain to end of TD list */
- if (td_list == NULL)
- {
- td_list = td;
- }
- else
- {
- td_p = td_list;
- while (td_p->NextTD != 0)
- td_p = (TD_T *)td_p->NextTD;
- td_p->NextTD = (uint32_t)td;
- }
- }
- while (data_len > 0);
- /*------------------------------------------------------------------------------------*/
- /* Start transfer */
- /*------------------------------------------------------------------------------------*/
- utr->status = 0;
- DISABLE_OHCI_IRQ();
- ed->HeadP = (ed->HeadP & 0x2) | (uint32_t)td_list; /* keep toggleCarry bit */
- if (bIsNewED)
- {
- ed->HeadP = (uint32_t)td_list;
- /* Link ED to OHCI Bulk List */
- ed->NextED = _ohci->HcBulkHeadED;
- _ohci->HcBulkHeadED = (uint32_t)ed;
- }
- ENABLE_OHCI_IRQ();
- _ohci->HcControl |= USBH_HcControl_BLE_Msk; /* enable bulk list */
- _ohci->HcCommandStatus = USBH_HcCommandStatus_BLF_Msk; /* start bulk list */
- return 0;
- mem_out:
- while (td_list != NULL)
- {
- td = td_list;
- td_list = (TD_T *)td_list->NextTD;
- free_ohci_TD(td);
- }
- free_ohci_ED(ed);
- return USBH_ERR_MEMORY_OUT;
- }
- static int ohci_int_xfer(UTR_T *utr)
- {
- UDEV_T *udev = utr->udev;
- EP_INFO_T *ep = utr->ep;
- ED_T *ed, *ied;
- TD_T *td, *td_new;
- uint32_t info;
- int8_t bIsNewED = 0;
- if (utr->data_len > 64) /* USB 1.1 interrupt transfer maximum packet size is 64 */
- return USBH_ERR_INVALID_PARAM;
- td_new = alloc_ohci_TD(utr); /* allocate a TD for dummy TD */
- if (td_new == NULL)
- return USBH_ERR_MEMORY_OUT;
- ied = get_int_tree_head_node(ep->bInterval); /* get head node of this interval */
- /*------------------------------------------------------------------------------------*/
- /* Find if this ED was already in the list */
- /*------------------------------------------------------------------------------------*/
- info = ed_make_info(udev, ep);
- ed = ied;
- while (ed != NULL)
- {
- if (ed->Info == info)
- break; /* Endpoint found */
- ed = (ED_T *)ed->NextED;
- }
- if (ed == NULL) /* ED not found, create it */
- {
- bIsNewED = 1;
- ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */
- if (ed == NULL)
- return USBH_ERR_MEMORY_OUT;
- ed->Info = info;
- ed->HeadP = 0;
- ed->bInterval = ep->bInterval;
- td = alloc_ohci_TD(NULL); /* allocate the initial dummy TD for ED */
- if (td == NULL)
- {
- free_ohci_ED(ed);
- free_ohci_TD(td_new);
- return USBH_ERR_MEMORY_OUT;
- }
- ed->HeadP = (uint32_t)td; /* Let both HeadP and TailP point to dummy TD */
- ed->TailP = ed->HeadP;
- }
- else
- {
- td = (TD_T *)(ed->TailP & ~0xf); /* TailP always point to the dummy TD */
- }
- ep->hw_pipe = (void *)ed;
- /*------------------------------------------------------------------------------------*/
- /* Prepare TD */
- /*------------------------------------------------------------------------------------*/
- if ((ep->bEndpointAddress & EP_ADDR_DIR_MASK) == EP_ADDR_DIR_OUT)
- info = (TD_CC | TD_R | TD_DP_OUT | TD_TYPE_INT);
- else
- info = (TD_CC | TD_R | TD_DP_IN | TD_TYPE_INT);
- /* Keep data toggle */
- info = (info & ~(1 << 25)) | (td->Info & (1 << 25));
- /* fill this TD */
- write_td(td, info, utr->buff, utr->data_len);
- td->ed = ed;
- td->NextTD = (uint32_t)td_new;
- td->utr = utr;
- utr->td_cnt = 1; /* increase TD count, for recalim counter */
- utr->status = 0;
- /*------------------------------------------------------------------------------------*/
- /* Hook ED and TD list to HCCA interrupt table */
- /*------------------------------------------------------------------------------------*/
- DISABLE_OHCI_IRQ();
- ed->TailP = (uint32_t)td_new;
- if (bIsNewED)
- {
- /* Add to list of the same interval */
- ed->NextED = ied->NextED;
- ied->NextED = (uint32_t)ed;
- }
- ENABLE_OHCI_IRQ();
- //printf("Link INT ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED);
- _ohci->HcControl |= USBH_HcControl_PLE_Msk; /* periodic list enable */
- return 0;
- }
- static int ohci_iso_xfer(UTR_T *utr)
- {
- UDEV_T *udev = utr->udev;
- EP_INFO_T *ep = utr->ep;
- ED_T *ed, *ied;
- TD_T *td, *td_list, *last_td;
- int i;
- uint32_t info;
- uint32_t buff_addr;
- int8_t bIsNewED = 0;
- ied = get_int_tree_head_node(ep->bInterval); /* get head node of this interval */
- /*------------------------------------------------------------------------------------*/
- /* Find if this ED was already in the list */
- /*------------------------------------------------------------------------------------*/
- info = ed_make_info(udev, ep);
- ed = ied;
- while (ed != NULL)
- {
- if (ed->Info == info)
- break; /* Endpoint found */
- ed = (ED_T *)ed->NextED;
- }
- if (ed == NULL) /* ED not found, create it */
- {
- bIsNewED = 1;
- ed = alloc_ohci_ED(); /* allocate an Endpoint Descriptor */
- if (ed == NULL)
- return USBH_ERR_MEMORY_OUT;
- ed->Info = info;
- ed->HeadP = 0;
- ed->bInterval = ep->bInterval;
- }
- else
- ep->hw_pipe = (void *)ed;
- /*------------------------------------------------------------------------------------*/
- /* Prepare TDs */
- /*------------------------------------------------------------------------------------*/
- if (utr->bIsoNewSched) /* Is the starting of isochronous streaming? */
- ed->next_sf = _hcca->frame_no + OHCI_ISO_DELAY;
- utr->td_cnt = 0;
- utr->iso_sf = ed->next_sf;
- last_td = NULL;
- td_list = NULL;
- for (i = 0; i < IF_PER_UTR; i++)
- {
- utr->iso_status[i] = USBH_ERR_NOT_ACCESS1;
- td = alloc_ohci_TD(utr); /* allocate a TD */
- if (td == NULL)
- goto mem_out;
- /* fill this TD */
- buff_addr = (uint32_t)(utr->iso_buff[i]);
- td->Info = (TD_CC | TD_TYPE_ISO) | ed->next_sf;
- ed->next_sf += get_ohci_interval(ed->bInterval);
- td->CBP = buff_addr & ~0xFFF;
- td->BE = buff_addr + utr->iso_xlen[i] - 1;
- td->PSW[0] = 0xE000 | (buff_addr & 0xFFF);
- td->ed = ed;
- utr->td_cnt++; /* increase TD count, for recalim counter */
- /* chain to end of TD list */
- if (td_list == NULL)
- td_list = td;
- else
- last_td->NextTD = (uint32_t)td;
- last_td = td;
- };
- /*------------------------------------------------------------------------------------*/
- /* Hook ED and TD list to HCCA interrupt table */
- /*------------------------------------------------------------------------------------*/
- utr->status = 0;
- DISABLE_OHCI_IRQ();
- if ((ed->HeadP & ~0x3) == 0)
- ed->HeadP = (ed->HeadP & 0x2) | (uint32_t)td_list; /* keep toggleCarry bit */
- else
- {
- /* find the tail of TDs under this ED */
- td = (TD_T *)(ed->HeadP & ~0x3);
- while (td->NextTD != 0)
- {
- td = (TD_T *)td->NextTD;
- }
- td->NextTD = (uint32_t)td_list;
- }
- if (bIsNewED)
- {
- /* Add to list of the same interval */
- ed->NextED = ied->NextED;
- ied->NextED = (uint32_t)ed;
- }
- ENABLE_OHCI_IRQ();
- ED_debug("Link ISO ED 0x%x: 0x%x 0x%x 0x%x 0x%x\n", (int)ed, ed->Info, ed->TailP, ed->HeadP, ed->NextED);
- _ohci->HcControl |= USBH_HcControl_PLE_Msk | USBH_HcControl_IE_Msk; /* enable periodic list and isochronous transfer */
- return 0;
- mem_out:
- while (td_list != NULL)
- {
- td = td_list;
- td_list = (TD_T *)td_list->NextTD;
- free_ohci_TD(td);
- }
- free_ohci_ED(ed);
- return USBH_ERR_MEMORY_OUT;
- }
- static UDEV_T *ohci_find_device_by_port(int port)
- {
- UDEV_T *udev;
- udev = g_udev_list;
- while (udev != NULL)
- {
- if ((udev->parent == NULL) && (udev->port_num == port) &&
- ((udev->speed == SPEED_LOW) || (udev->speed == SPEED_FULL)))
- return udev;
- udev = udev->next;
- }
- return NULL;
- }
- static int ohci_rh_port_reset(int port)
- {
- int retry;
- int reset_time;
- uint32_t t0;
- reset_time = usbh_tick_from_millisecond(PORT_RESET_TIME_MS);
- for (retry = 0; retry < PORT_RESET_RETRY; retry++)
- {
- _ohci->HcRhPortStatus[port] = USBH_HcRhPortStatus_PRS_Msk;
- t0 = usbh_get_ticks();
- while (usbh_get_ticks() - t0 < (reset_time) + 1)
- {
- /*
- * If device is disconnected or port enabled, we can stop port reset.
- */
- if (((_ohci->HcRhPortStatus[port] & USBH_HcRhPortStatus_CCS_Msk) == 0) ||
- ((_ohci->HcRhPortStatus[port] & (USBH_HcRhPortStatus_PES_Msk | USBH_HcRhPortStatus_CCS_Msk)) == (USBH_HcRhPortStatus_PES_Msk | USBH_HcRhPortStatus_CCS_Msk)))
- goto port_reset_done;
- }
- reset_time += PORT_RESET_RETRY_INC_MS;
- }
- USB_debug("OHCI port %d - port reset failed!\n", port + 1);
- return USBH_ERR_PORT_RESET;
- port_reset_done:
- if ((_ohci->HcRhPortStatus[port] & USBH_HcRhPortStatus_CCS_Msk) == 0) /* check again if device disconnected */
- {
- _ohci->HcRhPortStatus[port] = USBH_HcRhPortStatus_CSC_Msk; /* clear CSC */
- return USBH_ERR_DISCONNECTED;
- }
- return USBH_OK; /* port reset success */
- }
- static int ohci_rh_polling(void)
- {
- int i, change = 0;
- UDEV_T *udev;
- int ret;
- for (i = 0; i < OHCI_PORT_CNT; i++)
- {
- /* clear unwanted port change status */
- _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_OCIC_Msk | USBH_HcRhPortStatus_PRSC_Msk |
- USBH_HcRhPortStatus_PSSC_Msk | USBH_HcRhPortStatus_PESC_Msk;
- if ((_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_CSC_Msk) == 0)
- continue;
- rt_kprintf("OHCI port%d status change: 0x%x\n", i + 1, _ohci->HcRhPortStatus[i]);
- /*--------------------------------------------------------------------------------*/
- /* connect status change */
- /*--------------------------------------------------------------------------------*/
- _ohci->HcRhPortStatus[i] = USBH_HcRhPortStatus_CSC_Msk; /* clear CSC */
- if (_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_CCS_Msk)
- {
- /*----------------------------------------------------------------------------*/
- /* First of all, check if there's any previously connected device. */
- /*----------------------------------------------------------------------------*/
- while (1)
- {
- udev = ohci_find_device_by_port(i + 1);
- if (udev == NULL)
- break;
- usbh_disconnect_device(udev);
- }
- rt_kprintf("OHCI connect device.\n");
- if (ohci_rh_port_reset(i) != USBH_OK)
- continue;
- /*
- * Port reset success...
- */
- udev = alloc_device();
- if (udev == NULL)
- continue;
- udev->parent = NULL;
- udev->port_num = i + 1;
- if (_ohci->HcRhPortStatus[i] & USBH_HcRhPortStatus_LSDA_Msk)
- udev->speed = SPEED_LOW;
- else
- udev->speed = SPEED_FULL;
- udev->hc_driver = &ohci_driver;
- ret = usbh_connect_device(udev);
- if (ret < 0)
- {
- USB_error("connect_device error! [%d]\n", ret);
- free_device(udev);
- }
- change = 1;
- }
- else
- {
- /*
- * Device disconnected
- */
- rt_kprintf("OHCI disconnect device.\n");
- while (1)
- {
- udev = ohci_find_device_by_port(i + 1);
- if (udev == NULL)
- break;
- usbh_disconnect_device(udev);
- }
- change = 1;
- }
- }
- return change;
- }
- void td_done(TD_T *td)
- {
- UTR_T *utr = td->utr;
- uint32_t info;
- int cc;
- info = td->Info;
- TD_debug("td_done: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", (int)td, td->Info, td->CBP, td->NextTD, td->BE);
- /* ISO ... drivers see per-TD length/status */
- if ((info & TD_TYPE_Msk) == TD_TYPE_ISO)
- {
- uint16_t sf;
- int idx;
- sf = info & 0xFFFF;
- idx = ((sf + 0x10000 - utr->iso_sf) & 0xFFFF) / get_ohci_interval(td->ed->bInterval);
- if (idx >= IF_PER_UTR)
- {
- USB_error("ISO invalid index!! %d, %d\n", sf, utr->iso_sf);
- goto td_out;
- }
- cc = (td->PSW[0] >> 12) & 0xF;
- if (cc == 0xF) /* this frame was not transferred */
- {
- USB_debug("ISO F %d N/A!\n", sf);
- utr->iso_status[idx] = USBH_ERR_SCH_OVERRUN;
- goto td_out;
- }
- if ((cc != 0) && (cc != CC_DATA_UNDERRUN))
- {
- utr->iso_status[idx] = USBH_ERR_CC_NO_ERR - cc;
- goto td_out;
- }
- utr->iso_status[idx] = 0;
- utr->iso_xlen[idx] = td->PSW[0] & 0x7FF;
- }
- else
- {
- cc = TD_CC_GET(info);
- /* short packet is fine */
- if ((cc != CC_NOERROR) && (cc != CC_DATA_UNDERRUN))
- {
- USB_error("TD error, CC = 0x%x\n", cc);
- if (cc == CC_STALL)
- utr->status = USBH_ERR_STALL;
- else
- utr->status = USBH_ERR_TRANSFER;
- }
- switch (info & TD_TYPE_Msk)
- {
- case TD_TYPE_CTRL:
- if (info & TD_CTRL_DATA)
- {
- if (td->CBP == 0)
- utr->xfer_len += td->BE - td->buff_start + 1;
- else
- utr->xfer_len += td->CBP - td->buff_start;
- }
- break;
- case TD_TYPE_BULK:
- case TD_TYPE_INT:
- if (td->CBP == 0)
- utr->xfer_len += td->BE - td->buff_start + 1;
- else
- utr->xfer_len += td->CBP - td->buff_start;
- break;
- }
- }
- td_out:
- utr->td_cnt--;
- /* If all TDs are done, call-back to requester. */
- if (utr->td_cnt == 0)
- {
- utr->bIsTransferDone = 1;
- if (utr->func)
- utr->func(utr);
- }
- }
- /* in IRQ context */
- static void remove_ed()
- {
- ED_T *ed, *ed_p, *ied;
- TD_T *td, *td_next;
- UTR_T *utr;
- int found;
- while (ed_remove_list != NULL)
- {
- ED_debug("Remove ED: 0x%x, %d\n", (int)ed_remove_list, ed_remove_list->bInterval);
- ed_p = ed_remove_list;
- found = 0;
- /*--------------------------------------------------------------------------------*/
- /* Remove endpoint from Control List if found */
- /*--------------------------------------------------------------------------------*/
- if ((ed_p->Info & ED_EP_ADDR_Msk) == 0)
- {
- if (_ohci->HcControlHeadED == (uint32_t)ed_p)
- {
- _ohci->HcControlHeadED = (uint32_t)ed_p->NextED;
- found = 1;
- }
- else
- {
- ed = (ED_T *)_ohci->HcControlHeadED;
- while (ed != NULL)
- {
- if (ed->NextED == (uint32_t)ed_p)
- {
- ed->NextED = ed_p->NextED;
- found = 1;
- }
- ed = (ED_T *)ed->NextED;
- }
- }
- }
- /*--------------------------------------------------------------------------------*/
- /* Remove INT or ISO endpoint from HCCA interrupt table */
- /*--------------------------------------------------------------------------------*/
- else if (ed_p->bInterval > 0)
- {
- ied = get_int_tree_head_node(ed_p->bInterval);
- ed = ied;
- while (ed != NULL)
- {
- if (ed->NextED == (uint32_t)ed_p)
- {
- ed->NextED = ed_p->NextED;
- found = 1;
- break;
- }
- ed = (ED_T *)ed->NextED;
- }
- }
- /*--------------------------------------------------------------------------------*/
- /* Remove endpoint from Bulk List if found */
- /*--------------------------------------------------------------------------------*/
- else
- {
- if (_ohci->HcBulkHeadED == (uint32_t)ed_p)
- {
- ed = (ED_T *)ed_p;
- _ohci->HcBulkHeadED = ed_p->NextED;
- found = 1;
- }
- else
- {
- ed = (ED_T *)_ohci->HcBulkHeadED;
- while (ed != NULL)
- {
- if (ed->NextED == (uint32_t)ed_p)
- {
- ed->NextED = ed_p->NextED;
- found = 1;
- }
- ed = (ED_T *)ed->NextED;
- }
- }
- }
- /*--------------------------------------------------------------------------------*/
- /* Remove and free all TDs under this endpoint */
- /*--------------------------------------------------------------------------------*/
- if (found)
- {
- td = (TD_T *)(ed_p->HeadP & ~0x3);
- if (td != NULL)
- {
- while (td != NULL)
- {
- utr = td->utr;
- td_next = (TD_T *)td->NextTD;
- free_ohci_TD(td);
- td = td_next;
- utr->td_cnt--;
- if (utr->td_cnt == 0)
- {
- utr->status = USBH_ERR_ABORT;
- utr->bIsTransferDone = 1;
- if (utr->func)
- utr->func(utr);
- }
- }
- }
- }
- /*
- * Done. Remove this ED from [ed_remove_list] and free it.
- */
- ed_remove_list = ed_p->next;
- free_ohci_ED(ed_p);
- }
- }
- //static irqreturn_t ohci_irq (struct usb_hcd *hcd)
- //void OHCI_IRQHandler(void)
- void nu_ohci_isr(int vector, void *param)
- {
- TD_T *td, *td_prev, *td_next;
- uint32_t int_sts;
- //if ( nu_sys_usb0_role() != USB0_ID_HOST ) return;
- int_sts = _ohci->HcInterruptStatus;
- //USB_debug("ohci int_sts = 0x%x\n", int_sts);
- if ((_ohci->HcInterruptEnable & USBH_HcInterruptEnable_SF_Msk) &&
- (int_sts & USBH_HcInterruptStatus_SF_Msk))
- {
- int_sts &= ~USBH_HcInterruptStatus_SF_Msk;
- _ohci->HcInterruptDisable = USBH_HcInterruptDisable_SF_Msk;
- remove_ed();
- _ohci->HcInterruptStatus = USBH_HcInterruptStatus_SF_Msk;
- }
- if (int_sts & USBH_HcInterruptStatus_WDH_Msk)
- {
- int_sts &= ~USBH_HcInterruptStatus_WDH_Msk;
- /*
- * reverse done list
- */
- td = (TD_T *)(_hcca->done_head & TD_ADDR_MASK);
- _hcca->done_head = 0;
- td_prev = NULL;
- _ohci->HcInterruptStatus = USBH_HcInterruptStatus_WDH_Msk;
- while (td != NULL)
- {
- //TD_debug("Done list TD 0x%x => 0x%x\n", (int)td, (int)td->NextTD);
- td_next = (TD_T *)(td->NextTD & TD_ADDR_MASK);
- td->NextTD = (uint32_t)td_prev;
- td_prev = td;
- td = td_next;
- }
- td = td_prev; /* first TD of the reversed done list */
- /*
- * reclaim TDs
- */
- while (td != NULL)
- {
- TD_debug("Reclaim TD 0x%x, next 0x%x\n", (int)td, td->NextTD);
- td_next = (TD_T *)td->NextTD;
- td_done(td);
- free_ohci_TD(td);
- td = td_next;
- }
- }
- if (int_sts & USBH_HcInterruptStatus_RHSC_Msk)
- {
- _ohci->HcInterruptDisable = USBH_HcInterruptDisable_RHSC_Msk;
- }
- _ohci->HcInterruptStatus = int_sts;
- }
- #ifdef ENABLE_DEBUG_MSG
- void dump_ohci_int_table()
- {
- int i;
- ED_T *ed;
- for (i = 0; i < 32; i++)
- // for (i = 0; i < 1; i++)
- {
- USB_debug("%02d: ", i);
- ed = (ED_T *)_hcca->int_table[i];
- while (ed != NULL)
- {
- USB_debug("0x%x (0x%x) => ", (int)ed, ed->HeadP);
- ed = (ED_T *)ed->NextED;
- }
- rt_kprintf("0\n");
- }
- }
- void dump_ohci_regs()
- {
- USB_debug("Dump OCHI registers:\n");
- USB_debug(" HcRevision = 0x%x\n", _ohci->HcRevision);
- USB_debug(" HcControl = 0x%x\n", _ohci->HcControl);
- USB_debug(" HcCommandStatus = 0x%x\n", _ohci->HcCommandStatus);
- USB_debug(" HcInterruptStatus = 0x%x\n", _ohci->HcInterruptStatus);
- USB_debug(" HcInterruptEnable = 0x%x\n", _ohci->HcInterruptEnable);
- USB_debug(" HcInterruptDisable = 0x%x\n", _ohci->HcInterruptDisable);
- USB_debug(" HcHCCA = 0x%x\n", _ohci->HcHCCA);
- USB_debug(" HcPeriodCurrentED = 0x%x\n", _ohci->HcPeriodCurrentED);
- USB_debug(" HcControlHeadED = 0x%x\n", _ohci->HcControlHeadED);
- USB_debug(" HcControlCurrentED = 0x%x\n", _ohci->HcControlCurrentED);
- USB_debug(" HcBulkHeadED = 0x%x\n", _ohci->HcBulkHeadED);
- USB_debug(" HcBulkCurrentED = 0x%x\n", _ohci->HcBulkCurrentED);
- USB_debug(" HcDoneHead = 0x%x\n", _ohci->HcDoneHead);
- USB_debug(" HcFmInterval = 0x%x\n", _ohci->HcFmInterval);
- USB_debug(" HcFmRemaining = 0x%x\n", _ohci->HcFmRemaining);
- USB_debug(" HcFmNumber = 0x%x\n", _ohci->HcFmNumber);
- USB_debug(" HcPeriodicStart = 0x%x\n", _ohci->HcPeriodicStart);
- USB_debug(" HcLSThreshold = 0x%x\n", _ohci->HcLSThreshold);
- USB_debug(" HcRhDescriptorA = 0x%x\n", _ohci->HcRhDescriptorA);
- USB_debug(" HcRhDescriptorB = 0x%x\n", _ohci->HcRhDescriptorB);
- USB_debug(" HcRhStatus = 0x%x\n", _ohci->HcRhStatus);
- USB_debug(" HcRhPortStatus0 = 0x%x\n", _ohci->HcRhPortStatus[0]);
- USB_debug(" HcRhPortStatus1 = 0x%x\n", _ohci->HcRhPortStatus[1]);
- USB_debug(" HcPhyControl = 0x%x\n", _ohci->HcPhyControl);
- USB_debug(" HcMiscControl = 0x%x\n", _ohci->HcMiscControl);
- }
- void dump_ohci_ports()
- {
- USB_debug("_ohci port0=0x%x, port1=0x%x\n", _ohci->HcRhPortStatus[0], _ohci->HcRhPortStatus[1]);
- }
- #endif // ENABLE_DEBUG_MSG
- HC_DRV_T ohci_driver =
- {
- ohci_init, /* init */
- ohci_shutdown, /* shutdown */
- ohci_suspend, /* suspend */
- ohci_resume, /* resume */
- ohci_ctrl_xfer, /* ctrl_xfer */
- ohci_bulk_xfer, /* bulk_xfer */
- ohci_int_xfer, /* int_xfer */
- ohci_iso_xfer, /* iso_xfer */
- ohci_quit_xfer, /* quit_xfer */
- ohci_rh_port_reset, /* rthub_port_reset */
- ohci_rh_polling /* rthub_polling */
- };
- /// @endcond HIDDEN_SYMBOLS
- /*** (C) COPYRIGHT 2017 Nuvoton Technology Corp. ***/
|