| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893 |
- #ifdef USE_SSUSB_QMU
- #include <linux/dma-mapping.h>
- /* #include "mu3d_hal_osal.h" */
- /* #define _MTK_QMU_DRV_EXT_ */
- #include "mu3d_hal_qmu_drv.h"
- /* #undef _MTK_QMU_DRV_EXT_ */
- #include "mu3d_hal_usb_drv.h"
- #include "mu3d_hal_hw.h"
- #include "ssusb_io.h"
- static struct ssusb_gpd_range Rx_gpd_List[15];
- static struct ssusb_gpd_range Tx_gpd_List[15];
- /**
- * get_bd - get a null gpd
- * @args - arg1: dir, arg2: ep number
- */
- static struct ssusb_gpd *get_next_gpd(USB_DIR dir, u32 num)
- {
- struct ssusb_gpd *ptr;
- if (dir == USB_RX) {
- ptr = Rx_gpd_List[num].next;
- /* qmu_dbg(K_DEBUG, "[RX]""GPD List[%d]->Next=%p\n", num, Rx_gpd_List[num].next); */
- Rx_gpd_List[num].next = Rx_gpd_List[num].next + 1;
- /* qmu_dbg(K_DEBUG, "[Rx]""GPD List[%d]->Start=%p, Next=%p, End=%p\n", */
- /* num, Rx_gpd_List[num].start, Rx_gpd_List[num].next, Rx_gpd_List[num].end); */
- if (Rx_gpd_List[num].next > Rx_gpd_List[num].end)
- Rx_gpd_List[num].next = Rx_gpd_List[num].start;
- } else {
- ptr = Tx_gpd_List[num].next;
- /* qmu_dbg(K_DEBUG, "[TX]""GPD List[%d]->Next=%p\n", num, Tx_gpd_List[num].next); */
- Tx_gpd_List[num].next = Tx_gpd_List[num].next + 1;
- /* qmu_dbg(K_DEBUG, "[TX]""GPD List[%d]->Start=%p, next=%p, end=%p\n", */
- /* num, Tx_gpd_List[num].start, Tx_gpd_List[num].next, Tx_gpd_List[num].end); */
- if (Tx_gpd_List[num].next > Tx_gpd_List[num].end)
- Tx_gpd_List[num].next = Tx_gpd_List[num].start;
- }
- return ptr;
- }
- static struct ssusb_gpd *mu3d_get_gpd_from_dma(USB_DIR dir, int num, dma_addr_t gpd_dma_addr)
- {
- dma_addr_t dma_base = (dir == USB_RX) ? Rx_gpd_List[num].dma : Tx_gpd_List[num].dma;
- struct ssusb_gpd *gpd_head =
- (dir == USB_RX) ? Rx_gpd_List[num].start : Tx_gpd_List[num].start;
- unsigned int i = (gpd_dma_addr - dma_base) / sizeof(struct ssusb_gpd);
- /* if equal, overflow infact, should not use, only for compare */
- if (i > MAX_GPD_NUM)
- return NULL;
- return gpd_head + i;
- }
- static dma_addr_t mu3d_gpd_virt_to_dma(USB_DIR dir, int num, struct ssusb_gpd *ptr)
- {
- dma_addr_t dma_base = (dir == USB_RX) ? Rx_gpd_List[num].dma : Tx_gpd_List[num].dma;
- struct ssusb_gpd *gpd_head =
- (dir == USB_RX) ? Rx_gpd_List[num].start : Tx_gpd_List[num].start;
- unsigned int offset;
- if (!ptr)
- return 0;
- offset = ptr - gpd_head;
- if (offset > MAX_GPD_NUM)
- return 0;
- return dma_base + (offset * sizeof(*ptr));
- }
- /**
- * init_gpd_list - initialize gpd management list
- * @args - arg1: dir, arg2: ep number, arg3: gpd virtual addr, arg4: gpd ioremap addr, arg5: gpd number
- */
- static void init_gpd_list(USB_DIR dir, int num, struct ssusb_gpd *ptr, dma_addr_t io_ptr, u32 size)
- {
- if (dir == USB_RX) {
- Rx_gpd_List[num].start = ptr;
- Rx_gpd_List[num].enqueue = ptr;
- Rx_gpd_List[num].dequeue = ptr;
- Rx_gpd_List[num].end = ptr + size - 1; /* the first overflow one */
- Rx_gpd_List[num].next = ptr + 1;
- qmu_dbg(K_DEBUG, "Rx_gpd_List[%d].start=%p, next=%p, end=%p\n",
- num, Rx_gpd_List[num].start, Rx_gpd_List[num].next, Rx_gpd_List[num].end);
- qmu_dbg(K_DEBUG, "virtual start=%p, end=%p\n", ptr, ptr + size);
- qmu_dbg(K_DEBUG, "dma addr start=%#lx, end=%#lx\n", (unsigned long)io_ptr,
- (unsigned long)(io_ptr + size * sizeof(*ptr)));
- qmu_dbg(K_DEBUG, "dma addr start=%#lx, end=%#lx\n",
- (unsigned long)mu3d_gpd_virt_to_dma(dir, num, ptr),
- (unsigned long)mu3d_gpd_virt_to_dma(dir, num, (ptr + size)));
- } else {
- Tx_gpd_List[num].start = ptr;
- Tx_gpd_List[num].enqueue = ptr;
- Tx_gpd_List[num].dequeue = ptr;
- Tx_gpd_List[num].end = ptr + size - 1;
- Tx_gpd_List[num].next = ptr + 1;
- qmu_dbg(K_DEBUG, "Tx_gpd_List[%d].start=%p, next=%p, end=%p\n",
- num, Tx_gpd_List[num].start, Tx_gpd_List[num].next, Tx_gpd_List[num].end);
- /* qmu_dbg(K_INFO, "Tx_gpd_Offset[%d]=0x%08X\n", num, Tx_gpd_Offset[num]); */
- qmu_dbg(K_DEBUG, "virtual start=%p, end=%p\n", ptr, ptr + size);
- qmu_dbg(K_DEBUG, "dma addr start=%#lx, end=%#lx\n", (unsigned long)io_ptr,
- (unsigned long)(io_ptr + size * sizeof(*ptr)));
- qmu_dbg(K_DEBUG, "dma addr start=%#lx, end=%#lx\n",
- (unsigned long)mu3d_gpd_virt_to_dma(dir, num, ptr),
- (unsigned long)mu3d_gpd_virt_to_dma(dir, num, (ptr + size)));
- }
- }
- static void reset_gpd_list(USB_DIR dir, int num)
- {
- if (dir == USB_RX) {
- Rx_gpd_List[num].enqueue = Rx_gpd_List[num].start;
- Rx_gpd_List[num].dequeue = Rx_gpd_List[num].enqueue;
- Rx_gpd_List[num].next = Rx_gpd_List[num].enqueue + 1;
- } else {
- Tx_gpd_List[num].enqueue = Tx_gpd_List[num].start;
- Tx_gpd_List[num].dequeue = Tx_gpd_List[num].enqueue;
- Tx_gpd_List[num].next = Tx_gpd_List[num].enqueue + 1;
- }
- }
- /**
- * free_gpd - free gpd management list
- * @args - arg1: dir, arg2: ep number
- */
- static void free_gpd(USB_DIR dir, int num)
- {
- if (dir == USB_RX)
- memset(Rx_gpd_List[num].start, 0, MAX_GPD_NUM * sizeof(struct ssusb_gpd));
- else
- memset(Tx_gpd_List[num].start, 0, MAX_GPD_NUM * sizeof(struct ssusb_gpd));
- }
- /**
- * mu3d_hal_alloc_qmu_mem - allocate gpd and bd memory for all ep
- *
- */
- void mu3d_hal_alloc_qmu_mem(struct musb *musb)
- {
- u32 i, size;
- struct ssusb_gpd *ptr;
- for (i = 1; i <= MAX_QMU_EP; i++) {
- /* Allocate Rx GPD */
- size = sizeof(struct ssusb_gpd) * MAX_GPD_NUM;
- ptr =
- (struct ssusb_gpd *)dma_alloc_coherent(musb->controller, size,
- &Rx_gpd_List[i].dma, GFP_KERNEL);
- memset(ptr, 0, size);
- init_gpd_list(USB_RX, i, ptr, Rx_gpd_List[i].dma, MAX_GPD_NUM);
- qmu_dbg(K_DEBUG, "ALLOC RX GPD End [%d] Virtual Mem=%p, DMA addr=%#lx\n", i,
- Rx_gpd_List[i].enqueue, (unsigned long)Rx_gpd_List[i].dma);
- TGPD_CLR_FLAGS_HWO(Rx_gpd_List[i].enqueue);
- /* Allocate Tx GPD */
- size = sizeof(struct ssusb_gpd) * MAX_GPD_NUM;
- ptr =
- (struct ssusb_gpd *)dma_alloc_coherent(musb->controller, size,
- &Tx_gpd_List[i].dma, GFP_KERNEL);
- memset(ptr, 0, size);
- init_gpd_list(USB_TX, i, ptr, Tx_gpd_List[i].dma, MAX_GPD_NUM);
- qmu_dbg(K_DEBUG, "ALLOC TX GPD End [%d] Virtual Mem=%p, DMA addr=%#lx\n", i,
- Tx_gpd_List[i].enqueue, (unsigned long)Tx_gpd_List[i].dma);
- TGPD_CLR_FLAGS_HWO(Tx_gpd_List[i].enqueue);
- }
- }
- void mu3d_hal_free_qmu_mem(struct musb *musb)
- {
- u32 i, size;
- for (i = 1; i <= MAX_QMU_EP; i++) {
- size = sizeof(struct ssusb_gpd) * MAX_GPD_NUM;
- if (Rx_gpd_List[i].start) {
- dma_free_coherent(musb->controller, size, Rx_gpd_List[i].start,
- Rx_gpd_List[i].dma);
- Rx_gpd_List[i].start = NULL;
- }
- if (Tx_gpd_List[i].start) {
- dma_free_coherent(musb->controller, size, Tx_gpd_List[i].start,
- Tx_gpd_List[i].dma);
- Tx_gpd_List[i].start = NULL;
- }
- }
- }
- /**
- * mu3d_hal_init_qmu - initialize qmu
- *
- */
- void mu3d_hal_init_qmu(struct musb *musb)
- {
- void __iomem *mbase = musb->mac_base;
- u32 i;
- /* u32 QCR = 0; */
- /* Initialize QMU Tx/Rx start address. */
- for (i = 1; i <= MAX_QMU_EP; i++) {
- qmu_dbg(K_INFO, "==EP[%d]==Start addr RXQ=%#lx, TXQ=%#lx\n", i,
- (unsigned long)Rx_gpd_List[i].dma, (unsigned long)Tx_gpd_List[i].dma);
- mu3d_writel(mbase, USB_QMU_RQSAR(i), Rx_gpd_List[i].dma);
- mu3d_writel(mbase, USB_QMU_TQSAR(i), Tx_gpd_List[i].dma);
- reset_gpd_list(USB_RX, i);
- reset_gpd_list(USB_TX, i);
- }
- /* Enable QMU interrupt. */
- mu3d_writel(mbase, U3D_QIESR1, TXQ_EMPTY_IESR | TXQ_CSERR_IESR | TXQ_LENERR_IESR |
- RXQ_EMPTY_IESR | RXQ_CSERR_IESR | RXQ_LENERR_IESR | RXQ_ZLPERR_IESR);
- mu3d_writel(mbase, U3D_EPIESR, EP0ISR);
- }
- /**
- * mu3d_hal_cal_checksum - calculate check sum
- * @args - arg1: data buffer, arg2: data length
- */
- static noinline u8 mu3d_hal_cal_checksum(u8 *data, int len)
- {
- u8 *uDataPtr, ckSum;
- int i;
- *(data + 1) = 0x0;
- uDataPtr = data;
- ckSum = 0;
- /* For ALPS01572117, we found calculated QMU check sum is wrong. (Dump memory value directly.) */
- /* After check this function, we did not find any flaw. Still cannot find how to get this wrong value. */
- /* Maybe it is a memory corruption or complier problem. Add "noinline" and "mb();" to prevent this problem. */
- mb();
- for (i = 0; i < len; i++)
- ckSum += *(uDataPtr + i);
- return 0xFF - ckSum;
- }
- /**
- * mu3d_hal_resume_qmu - resume qmu function
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_resume_qmu(struct musb *musb, int q_num, USB_DIR dir)
- {
- void __iomem *mbase = musb->mac_base;
- if (dir == USB_TX) {
- mu3d_writel(mbase, USB_QMU_TQCSR(q_num), QMU_Q_RESUME);
- if (!mu3d_readl(mbase, USB_QMU_TQCSR(q_num))) {
- qmu_dbg(K_WARNIN, "[ERROR]" "%s TQCSR[%d]=%x\n", __func__, q_num,
- mu3d_readl(mbase, USB_QMU_TQCSR(q_num)));
- mu3d_writel(mbase, USB_QMU_TQCSR(q_num), QMU_Q_RESUME);
- qmu_dbg(K_WARNIN, "[ERROR]" "%s TQCSR[%d]=%x\n", __func__, q_num,
- mu3d_readl(mbase, USB_QMU_TQCSR(q_num)));
- }
- } else if (dir == USB_RX) {
- mu3d_writel(mbase, USB_QMU_RQCSR(q_num), QMU_Q_RESUME);
- if (!mu3d_readl(mbase, USB_QMU_RQCSR(q_num))) {
- qmu_dbg(K_WARNIN, "[ERROR]" "%s RQCSR[%d]=%x\n", __func__, q_num,
- mu3d_readl(mbase, USB_QMU_RQCSR(q_num)));
- mu3d_writel(mbase, USB_QMU_RQCSR(q_num), QMU_Q_RESUME);
- qmu_dbg(K_WARNIN, "[ERROR]" "%s RQCSR[%d]=%x\n", __func__, q_num,
- mu3d_readl(mbase, USB_QMU_RQCSR(q_num)));
- }
- } else {
- qmu_dbg(K_ERR, "%s wrong direction!!!\n", __func__);
- BUG_ON(1);
- }
- }
- /**
- * mu3d_hal_prepare_tx_gpd - prepare tx gpd/bd
- * @args - arg1: gpd address, arg2: data buffer address, arg3: data length, arg4: ep number, arg5: with bd or not, arg6: write hwo bit or not, arg7: write ioc bit or not
- */
- static struct ssusb_gpd *mu3d_hal_prepare_tx_gpd(struct ssusb_gpd *gpd, dma_addr_t pBuf,
- u32 data_len, u8 ep_num, u8 _is_bdp, u8 isHWO,
- u8 ioc, u8 bps, u8 zlp)
- {
- struct ssusb_gpd *enq;
- /*Set actual data point to "DATA Buffer" */
- TGPD_SET_DATA(gpd, pBuf);
- /*Clear "BDP(Buffer Descriptor Present)" flag */
- TGPD_CLR_FORMAT_BDP(gpd);
- /*
- * "Data Buffer Length" =
- * 0 (If data length > GPD buffer length, use BDs),
- * data_len (If data length < GPD buffer length, only use GPD)
- */
- TGPD_SET_BUF_LEN(gpd, data_len);
- /*"GPD extension length" = 0. Does not use GPD EXT!! */
- TGPD_SET_EXT_LEN(gpd, 0);
- if (zlp)
- TGPD_SET_FORMAT_ZLP(gpd);
- else
- TGPD_CLR_FORMAT_ZLP(gpd);
- /*Default: bps=false */
- TGPD_CLR_FORMAT_BPS(gpd);
- /*Default: ioc=true */
- TGPD_SET_FORMAT_IOC(gpd);
- /*Get the next GPD */
- Tx_gpd_List[ep_num].enqueue = get_next_gpd(USB_TX, ep_num);
- enq = Tx_gpd_List[ep_num].enqueue;
- qmu_dbg(K_DEBUG, "[TX]" "Tx_gpd_end[%d]=%p\n", ep_num, enq);
- /*Initialize the new GPD */
- memset(enq, 0, sizeof(struct ssusb_gpd));
- /*Clear "HWO(Hardware Own)" flag */
- TGPD_CLR_FLAGS_HWO(enq);
- /*Set "Next GDP pointer" as the next GPD */
- TGPD_SET_NEXT(gpd, mu3d_gpd_virt_to_dma(USB_TX, ep_num, enq));
- /*Default: isHWO=true */
- TGPD_SET_CHKSUM(gpd, CHECKSUM_LENGTH); /*Set GPD Checksum */
- TGPD_SET_FLAGS_HWO(gpd); /*Set HWO flag */
- return gpd;
- }
- /**
- * mu3d_hal_prepare_rx_gpd - prepare rx gpd/bd
- * @args - arg1: gpd address, arg2: data buffer address, arg3: data length, arg4: ep number, arg5: with bd or not, arg6: write hwo bit or not, arg7: write ioc bit or not
- */
- static struct ssusb_gpd *mu3d_hal_prepare_rx_gpd(struct ssusb_gpd *gpd, dma_addr_t pBuf,
- u32 data_len, u8 ep_num, u8 _is_bdp, u8 isHWO,
- u8 ioc, u8 bps, u32 cMaxPacketSize)
- {
- struct ssusb_gpd *enq;
- qmu_dbg(K_DEBUG, "[RX]" "%s gpd=%p, epnum=%d, len=%d\n", __func__, gpd, ep_num, data_len);
- /*Set actual data point to "DATA Buffer" */
- TGPD_SET_DATA(gpd, pBuf);
- /*Clear "BDP(Buffer Descriptor Present)" flag */
- TGPD_CLR_FORMAT_BDP(gpd);
- /*
- * Set "Allow Data Buffer Length" =
- * 0 (If data length > GPD buffer length, use BDs),
- * data_len (If data length < GPD buffer length, only use GPD)
- */
- TGPD_SET_DATA_BUF_LEN(gpd, data_len);
- /*Set "Transferred Data Length" = 0 */
- TGPD_SET_BUF_LEN(gpd, 0);
- /*Default: bps=false */
- TGPD_CLR_FORMAT_BPS(gpd);
- /*Default: ioc=true */
- TGPD_SET_FORMAT_IOC(gpd);
- /*Get the next GPD */
- Rx_gpd_List[ep_num].enqueue = get_next_gpd(USB_RX, ep_num);
- enq = Rx_gpd_List[ep_num].enqueue;
- qmu_dbg(K_DEBUG, "[RX]" "Rx_gpd_end[%d]=%p gpd=%p\n", ep_num, enq, gpd);
- /* BUG_ON(!check_next_gpd(gpd, Rx_gpd_end[ep_num])); */
- /*Initialize the new GPD */
- memset(enq, 0, sizeof(struct ssusb_gpd));
- /*Clear "HWO(Hardware Own)" flag */
- TGPD_CLR_FLAGS_HWO(enq);
- /*Set Next GDP pointer to the next GPD */
- TGPD_SET_NEXT(gpd, mu3d_gpd_virt_to_dma(USB_RX, ep_num, enq));
- /*Default: isHWO=true */
- TGPD_SET_CHKSUM(gpd, CHECKSUM_LENGTH); /*Set GPD Checksum */
- TGPD_SET_FLAGS_HWO(gpd); /*Set HWO flag */
- return gpd;
- }
- /**
- * mu3d_hal_insert_transfer_gpd - insert new gpd/bd
- * @args - arg1: ep number, arg2: dir, arg3: data buffer, arg4: data length, arg5: write hwo bit or not, arg6: write ioc bit or not
- */
- void mu3d_hal_insert_transfer_gpd(int ep_num, USB_DIR dir, dma_addr_t buf,
- u32 count, u8 isHWO, u8 ioc, u8 bps, u8 zlp, u32 maxp)
- {
- struct ssusb_gpd *gpd;
- if (dir == USB_TX) {
- gpd = Tx_gpd_List[ep_num].enqueue;
- mu3d_hal_prepare_tx_gpd(gpd, buf, count, ep_num, IS_BDP, isHWO, ioc, bps, zlp);
- } else if (dir == USB_RX) {
- gpd = Rx_gpd_List[ep_num].enqueue;
- mu3d_hal_prepare_rx_gpd(gpd, buf, count, ep_num, IS_BDP, isHWO, ioc, bps, maxp);
- }
- }
- /**
- * mu3d_hal_start_qmu - start qmu function (QMU flow : mu3d_hal_init_qmu ->mu3d_hal_start_qmu -> mu3d_hal_insert_transfer_gpd -> mu3d_hal_resume_qmu)
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_start_qmu(struct musb *musb, int q_num, USB_DIR dir)
- {
- void __iomem *mbase = musb->mac_base;
- /* u32 QCR; */
- u32 txcsr;
- u32 rxcsr;
- if (dir == USB_TX) {
- txcsr = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, q_num); /* & 0xFFFEFFFF; */
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, q_num, txcsr | TX_DMAREQEN);
- mu3d_setmsk(mbase, U3D_QCR0, QMU_TX_CS_EN(q_num));
- #if (TXZLP == HW_MODE)
- mu3d_clrmsk(mbase, U3D_QCR1, QMU_TX_ZLP(q_num));
- mu3d_setmsk(mbase, U3D_QCR2, QMU_TX_ZLP(q_num));
- #elif (TXZLP == GPD_MODE)
- mu3d_setmsk(mbase, U3D_QCR1, QMU_TX_ZLP(q_num));
- #endif
- mu3d_setmsk(mbase, U3D_QEMIESR, QMU_TX_EMPTY(q_num));
- mu3d_writel(mbase, U3D_TQERRIESR0, QMU_TX_LEN_ERR(q_num) | QMU_TX_CS_ERR(q_num));
- qmu_dbg(K_DEBUG, "USB_QMU_TQCSR:0x%08X\n", mu3d_readl(mbase, USB_QMU_TQCSR(q_num)));
- if (mu3d_readl(mbase, USB_QMU_TQCSR(q_num)) & QMU_Q_ACTIVE) {
- qmu_dbg(K_INFO, "Tx %d Active Now!\n", q_num);
- return;
- }
- mu3d_writel(mbase, USB_QMU_TQCSR(q_num), QMU_Q_START);
- qmu_dbg(K_DEBUG, "USB_QMU_TQCSR:0x%08X\n", mu3d_readl(mbase, USB_QMU_TQCSR(q_num)));
- } else if (dir == USB_RX) {
- rxcsr = mu3d_xcsr_readl(mbase, U3D_RX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_RX1CSR0, q_num, rxcsr | (RX_DMAREQEN));
- mu3d_setmsk(mbase, U3D_QCR0, QMU_RX_CS_EN(q_num));
- #ifdef CFG_RX_ZLP_EN
- mu3d_setmsk(mbase, U3D_QCR3, QMU_RX_ZLP(q_num));
- #else
- mu3d_clrmsk(mbase, U3D_QCR3, QMU_RX_ZLP(q_num));
- #endif
- #ifdef CFG_RX_COZ_EN
- mu3d_setmsk(mbase, U3D_QCR3, QMU_RX_COZ(q_num));
- #else
- mu3d_clrmsk(mbase, U3D_QCR3, QMU_RX_COZ(q_num));
- #endif
- mu3d_setmsk(mbase, U3D_QEMIESR, QMU_RX_EMPTY(q_num));
- mu3d_writel(mbase, U3D_RQERRIESR0, QMU_RX_LEN_ERR(q_num) | QMU_RX_CS_ERR(q_num));
- mu3d_writel(mbase, U3D_RQERRIESR1, QMU_RX_EP_ERR(q_num) | QMU_RX_ZLP_ERR(q_num));
- qmu_dbg(K_DEBUG, "USB_QMU_RQCSR:0x%08X\n", mu3d_readl(mbase, USB_QMU_RQCSR(q_num)));
- if (mu3d_readl(mbase, USB_QMU_RQCSR(q_num)) & QMU_Q_ACTIVE) {
- qmu_dbg(K_INFO, "Rx %d Active Now!\n", q_num);
- return;
- }
- mu3d_writel(mbase, USB_QMU_RQCSR(q_num), QMU_Q_START);
- qmu_dbg(K_DEBUG, "USB_QMU_RQCSR:0x%08X\n", mu3d_readl(mbase, USB_QMU_RQCSR(q_num)));
- }
- #if (CHECKSUM_TYPE == CS_16B)
- mu3d_setmsk(mbase, U3D_QCR0, CS16B_EN);
- #else
- mu3d_clrmsk(mbase, U3D_QCR0, CS16B_EN);
- #endif
- }
- /**
- * mu3d_hal_stop_qmu - stop qmu function (after qmu stop, fifo should be flushed)
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_stop_qmu(struct musb *musb, int q_num, USB_DIR dir)
- {
- void __iomem *mbase = musb->mac_base;
- if (dir == USB_TX) {
- if (!(mu3d_readl(mbase, USB_QMU_TQCSR(q_num)) & (QMU_Q_ACTIVE))) {
- qmu_dbg(K_DEBUG, "Tx%d inActive Now!\n", q_num);
- return;
- }
- mu3d_writel(mbase, USB_QMU_TQCSR(q_num), QMU_Q_STOP);
- while ((mu3d_readl(mbase, USB_QMU_TQCSR(q_num)) & (QMU_Q_ACTIVE)))
- ;
- qmu_dbg(K_CRIT, "Tx%d stop Now!\n", q_num);
- } else if (dir == USB_RX) {
- if (!(mu3d_readl(mbase, USB_QMU_RQCSR(q_num)) & QMU_Q_ACTIVE)) {
- qmu_dbg(K_DEBUG, "Rx%d inActive Now!\n", q_num);
- return;
- }
- mu3d_writel(mbase, USB_QMU_RQCSR(q_num), QMU_Q_STOP);
- while ((mu3d_readl(mbase, USB_QMU_RQCSR(q_num)) & (QMU_Q_ACTIVE)))
- ;
- qmu_dbg(K_CRIT, "Rx%d stop now!\n", q_num);
- }
- }
- /**
- * mu3d_hal_send_stall - send stall
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_send_stall(struct musb *musb, int q_num, USB_DIR dir)
- {
- void __iomem *mbase = musb->mac_base;
- u32 tmp;
- if (dir == USB_TX) {
- tmp = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, q_num, tmp | TX_SENDSTALL);
- while (!(mu3d_xcsr_readl(mbase, U3D_TX1CSR0, q_num) & TX_SENTSTALL))
- ;
- tmp = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, q_num, tmp | TX_SENTSTALL);
- tmp = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, q_num, tmp & (~TX_SENDSTALL));
- } else if (dir == USB_RX) {
- tmp = mu3d_xcsr_readl(mbase, U3D_RX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_RX1CSR0, q_num, tmp | RX_SENDSTALL);
- while (!(mu3d_xcsr_readl(mbase, U3D_RX1CSR0, q_num) & RX_SENTSTALL))
- ;
- tmp = mu3d_xcsr_readl(mbase, U3D_RX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_RX1CSR0, q_num, tmp | RX_SENTSTALL);
- tmp = mu3d_xcsr_readl(mbase, U3D_RX1CSR0, q_num);
- mu3d_xcsr_writel(mbase, U3D_RX1CSR0, q_num, tmp & (~RX_SENDSTALL));
- }
- qmu_dbg(K_CRIT, "%s %s-EP[%d] sent stall\n", __func__, ((dir == USB_TX) ? "TX" : "RX"),
- q_num);
- }
- /**
- * mu3d_hal_restart_qmu - clear toggle(or sequence) number and start qmu
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_restart_qmu(struct musb *musb, int q_num, USB_DIR dir)
- {
- void __iomem *mbase = musb->mac_base;
- u32 ep_rst;
- qmu_dbg(K_CRIT, "%s : Rest %s-EP[%d]\n", __func__, ((dir == USB_TX) ? "TX" : "RX"), q_num);
- if (dir == USB_TX) {
- ep_rst = BIT16 << q_num;
- mu3d_writel(mbase, U3D_EP_RST, ep_rst);
- mdelay(1);
- mu3d_writel(mbase, U3D_EP_RST, 0);
- } else {
- ep_rst = 1 << q_num;
- mu3d_writel(mbase, U3D_EP_RST, ep_rst);
- mdelay(1);
- mu3d_writel(mbase, U3D_EP_RST, 0);
- }
- mu3d_hal_start_qmu(musb, q_num, dir);
- }
- /**
- * flush_qmu - stop qmu and align qmu start ptr t0 current ptr
- * @args - arg1: ep number, arg2: dir
- */
- void mu3d_hal_flush_qmu(struct musb *musb, int q_num, USB_DIR dir)
- {
- qmu_dbg(K_CRIT, "%s flush QMU %s\n", __func__, ((dir == USB_TX) ? "TX" : "RX"));
- /*Stop QMU */
- mu3d_hal_stop_qmu(musb, q_num, dir);
- free_gpd(dir, q_num);
- reset_gpd_list(dir, q_num);
- }
- /*
- * 1. Find the last gpd HW has executed and update Tx_gpd_last[]
- * 2. Set the flag for txstate to know that TX has been completed
- * caller:qmu_interrupt after getting QMU done interrupt and TX is raised
- *
- * NOTE: request list maybe is already empty as following case:
- * queue_tx --> qmu_interrupt(clear int pending, schedule tasklet)-->
- * queue_tx --> process_tasklet(at the same time, the second one tx over,
- * tasklet process both of them)-->qmu_interrupt for second one.
- * To avoid upper case, put qmu_done_tx in ISR directly to process it.
- */
- void qmu_done_tx(struct musb *musb, u8 ep_num, unsigned long flags)
- {
- struct ssusb_gpd *gpd = Tx_gpd_List[ep_num].dequeue;
- struct ssusb_gpd *gpd_current = NULL;
- struct musb_ep *musb_ep = &musb->endpoints[ep_num].ep_in;
- void __iomem *mbase = musb->mac_base;
- dma_addr_t gpd_dma = mu3d_readl(mbase, USB_QMU_TQCPR(ep_num));
- struct usb_request *request = NULL;
- struct musb_request *req;
- /* trying to give_back the request to gadget driver. */
- req = next_request(musb_ep);
- if (req)
- request = &req->request;
- else
- return;
- /*Transfer PHY addr got from QMU register to VIR addr */
- gpd_current = mu3d_get_gpd_from_dma(USB_TX, ep_num, gpd_dma);
- /*
- gpd or Last gdp_current
- | |
- |-> GPD1 --> GPD2 --> GPD3 --> GPD4 --> GPD5 -|
- |----------------------------------------------|
- */
- qmu_dbg(K_DEBUG, "[TXD]" "%s EP%d, Last=%p, Current=%p, End=%p\n",
- __func__, ep_num, gpd, gpd_current, Tx_gpd_List[ep_num].enqueue);
- /*gpd_current should at least point to the next GPD to the previous last one. */
- if (gpd == gpd_current) {
- qmu_dbg(K_ERR, "[TXD][warn] %s gpd(%p) == gpd_current(%p)\n", __func__, gpd,
- gpd_current);
- return;
- }
- if (TGPD_IS_FLAGS_HWO(gpd)) {
- qmu_dbg(K_DEBUG, "[TXD][ERROR] %s HWO=1, CPR=%x\n", __func__,
- mu3d_readl(mbase, USB_QMU_TQCPR(ep_num)));
- BUG_ON(1);
- }
- while (gpd != gpd_current && !TGPD_IS_FLAGS_HWO(gpd)) {
- if (!TGPD_GET_NEXT(gpd)) {
- qmu_dbg(K_ERR, "[TXD][ERROR]" "Next GPD is null!!\n");
- /* BUG_ON(1); */
- break;
- }
- gpd_dma = (dma_addr_t) TGPD_GET_NEXT(gpd);
- gpd = mu3d_get_gpd_from_dma(USB_TX, ep_num, gpd_dma);
- Tx_gpd_List[ep_num].dequeue = gpd;
- musb_g_giveback(musb_ep, request, 0);
- req = next_request(musb_ep);
- request = &req->request;
- }
- if (gpd != gpd_current && TGPD_IS_FLAGS_HWO(gpd)) {
- qmu_dbg(K_ERR, "[TXD][ERROR]" "EP%d TQCSR=%x, TQSAR=%x, TQCPR=%x\n",
- ep_num, mu3d_readl(mbase, USB_QMU_TQCSR(ep_num)),
- mu3d_readl(mbase, USB_QMU_TQSAR(ep_num)),
- mu3d_readl(mbase, USB_QMU_TQCPR(ep_num)));
- }
- qmu_dbg(K_DEBUG, "[TXD]" "%s EP%d, Last=%p, End=%p, complete\n", __func__,
- ep_num, Tx_gpd_List[ep_num].dequeue, Tx_gpd_List[ep_num].enqueue);
- if (req != NULL) {
- if (request->length == 0) {
- u32 txcsr = 0;
- qmu_dbg(K_DEBUG, "[TXD]" "==Send ZLP== %p\n", req);
- /* NEED to add timeout process */
- while (!(mu3d_xcsr_readl(mbase, U3D_TX1CSR0, req->epnum) & TX_FIFOFULL)) {
- txcsr = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, req->epnum);
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, req->epnum,
- txcsr & ~TX_DMAREQEN);
- txcsr = mu3d_xcsr_readl(mbase, U3D_TX1CSR0, req->epnum);
- mu3d_xcsr_writel(mbase, U3D_TX1CSR0, req->epnum,
- txcsr | TX_TXPKTRDY);
- break;
- }
- qmu_dbg(K_DEBUG,
- "[TXD]" "Giveback ZLP of EP%d, actual:%d, length:%d %p\n",
- req->epnum, request->actual, request->length, request);
- musb_g_giveback(musb_ep, request, 0);
- }
- }
- }
- /*
- When receiving RXQ done interrupt, qmu_interrupt calls this function.
- 1. Traverse GPD/BD data structures to count actual transferred length.
- 2. Set the done flag to notify rxstate_qmu() to report status to upper gadget driver.
- ported from proc_qmu_rx() from test driver.
- caller:qmu_interrupt after getting QMU done interrupt and TX is raised
- */
- void qmu_done_rx(struct musb *musb, u8 ep_num, unsigned long flags)
- {
- struct ssusb_gpd *gpd = Rx_gpd_List[ep_num].dequeue;
- struct ssusb_gpd *gpd_current = NULL;
- struct musb_ep *musb_ep = &musb->endpoints[ep_num].ep_out;
- void __iomem *mbase = musb->mac_base;
- dma_addr_t gpd_dma = (mu3d_readl(mbase, USB_QMU_RQCPR(ep_num)));
- struct usb_request *request = NULL;
- struct musb_request *req;
- /* trying to give_back the request to gadget driver. */
- req = next_request(musb_ep);
- if (req)
- request = &req->request;
- else
- return;
- /*Transfer PHY addr got from QMU register to VIR addr */
- gpd_current = mu3d_get_gpd_from_dma(USB_RX, ep_num, gpd_dma);
- qmu_dbg(K_DEBUG, "[RXD]" "%s EP%d, Last=%p, Current=%p, End=%p\n",
- __func__, ep_num, gpd, gpd_current, Rx_gpd_List[ep_num].enqueue);
- /*gpd_current should at least point to the next GPD to the previous last one. */
- if (gpd == gpd_current) {
- qmu_dbg(K_ERR, "[RXD][ERROR]" "%s gpd(%p) == gpd_current(%p)\n", __func__, gpd,
- gpd_current);
- qmu_dbg(K_ERR, "[RXD][ERROR]" "EP%d RQCSR=%x, RQSAR=%x, RQCPR=%x, RQLDPR=%x\n",
- ep_num, mu3d_readl(mbase, USB_QMU_RQCSR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQSAR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQCPR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQLDPR(ep_num)));
- return;
- }
- if (!gpd || !gpd_current)
- return;
- if (TGPD_IS_FLAGS_HWO(gpd)) {
- qmu_dbg(K_ERR, "[RXD][ERROR]" "HWO=1!!\n");
- BUG_ON(1);
- }
- while (gpd != gpd_current && !TGPD_IS_FLAGS_HWO(gpd)) {
- u32 rcv_len = (u32) TGPD_GET_BUF_LEN(gpd);
- u32 buf_len = (u32) TGPD_GET_DATA_BUF_LEN(gpd);
- if (rcv_len > buf_len)
- qmu_dbg(K_ERR, "[RXD][ERROR]" "%s rcv(%d) > buf(%d) AUK!?\n", __func__,
- rcv_len, buf_len);
- qmu_dbg(K_DEBUG,
- "[RXD]" "gpd=%p ->HWO=%d, Next_GPD=%x, RcvLen=%d, BufLen=%d, pBuf=%#x\n",
- gpd, TGPD_GET_FLAG(gpd), TGPD_GET_NEXT(gpd), rcv_len, buf_len,
- TGPD_GET_DATA(gpd));
- request->actual += rcv_len;
- if (!TGPD_GET_NEXT(gpd) || !TGPD_GET_DATA(gpd)) {
- qmu_dbg(K_ERR, "[RXD][ERROR]" "%s EP%d ,gpd=%p\n", __func__, ep_num, gpd);
- BUG_ON(1);
- }
- gpd_dma = (dma_addr_t) TGPD_GET_NEXT(gpd);
- gpd = mu3d_get_gpd_from_dma(USB_RX, ep_num, gpd_dma);
- if (!gpd) {
- qmu_dbg(K_ERR, "[RXD][ERROR]" "%s EP%d ,gpd=%p\n", __func__, ep_num, gpd);
- BUG_ON(1);
- }
- Rx_gpd_List[ep_num].dequeue = gpd;
- musb_g_giveback(musb_ep, request, 0);
- req = next_request(musb_ep);
- request = &req->request;
- }
- if (gpd != gpd_current && TGPD_IS_FLAGS_HWO(gpd)) {
- qmu_dbg(K_ERR, "[RXD][ERROR]" "gpd=%p\n", gpd);
- qmu_dbg(K_ERR, "[RXD][ERROR]" "EP%d RQCSR=%x, RQSAR=%x, RQCPR=%x, RQLDPR=%x\n",
- ep_num, mu3d_readl(mbase, USB_QMU_RQCSR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQSAR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQCPR(ep_num)),
- mu3d_readl(mbase, USB_QMU_RQLDPR(ep_num)));
- }
- qmu_dbg(K_DEBUG, "[RXD]" "%s EP%d, Last=%p, End=%p, complete\n", __func__,
- ep_num, Rx_gpd_List[ep_num].dequeue, Rx_gpd_List[ep_num].enqueue);
- }
- void qmu_done_tasklet(unsigned long data)
- {
- unsigned int qmu_val;
- unsigned int i;
- unsigned long flags;
- struct musb *musb = (struct musb *)data;
- spin_lock_irqsave(&musb->lock, flags);
- qmu_val = musb->qmu_done_intr;
- musb->qmu_done_intr = 0;
- for (i = 1; i <= MAX_QMU_EP; i++) {
- if (qmu_val & QMU_RX_DONE(i))
- qmu_done_rx(musb, i, flags);
- if (qmu_val & QMU_TX_DONE(i))
- qmu_done_tx(musb, i, flags);
- }
- spin_unlock_irqrestore(&musb->lock, flags);
- }
- void qmu_exception_interrupt(struct musb *musb, u32 wQmuVal)
- {
- void __iomem *mbase = musb->mac_base;
- u32 wErrVal;
- int i = (int)wQmuVal;
- if (wQmuVal & RXQ_CSERR_INT)
- qmu_dbg(K_ERR, "==Rx %d checksum error==\n", i);
- if (wQmuVal & RXQ_LENERR_INT)
- qmu_dbg(K_ERR, "==Rx %d length error==\n", i);
- if (wQmuVal & TXQ_CSERR_INT)
- qmu_dbg(K_ERR, "==Tx %d checksum error==\n", i);
- if (wQmuVal & TXQ_LENERR_INT)
- qmu_dbg(K_ERR, "==Tx %d length error==\n", i);
- if ((wQmuVal & RXQ_CSERR_INT) || (wQmuVal & RXQ_LENERR_INT)) {
- wErrVal = mu3d_readl(mbase, U3D_RQERRIR0);
- qmu_dbg(K_DEBUG, "Rx Queue error in QMU mode![0x%x]\r\n", (unsigned int)wErrVal);
- for (i = 1; i <= MAX_QMU_EP; i++) {
- if (wErrVal & QMU_RX_CS_ERR(i))
- qmu_dbg(K_ERR, "Rx %d CS error!\r\n", i);
- if (wErrVal & QMU_RX_LEN_ERR(i))
- qmu_dbg(K_ERR, "RX EP%d Recv Length error\n", i);
- }
- mu3d_writel(mbase, U3D_RQERRIR0, wErrVal);
- }
- if (wQmuVal & RXQ_ZLPERR_INT) {
- wErrVal = mu3d_readl(mbase, U3D_RQERRIR1);
- qmu_dbg(K_DEBUG, "Rx Queue error in QMU mode![0x%x]\r\n", (unsigned int)wErrVal);
- for (i = 1; i <= MAX_QMU_EP; i++) {
- if (wErrVal & QMU_RX_ZLP_ERR(i)) {
- /*FIXME: should _NOT_ got this error. But now just accept. */
- qmu_dbg(K_INFO, "RX EP%d Recv ZLP\n", i);
- }
- }
- mu3d_writel(mbase, U3D_RQERRIR1, wErrVal);
- }
- if ((wQmuVal & TXQ_CSERR_INT) || (wQmuVal & TXQ_LENERR_INT)) {
- wErrVal = mu3d_readl(mbase, U3D_TQERRIR0);
- qmu_dbg(K_DEBUG, "Tx Queue error in QMU mode![0x%x]\r\n", (unsigned int)wErrVal);
- for (i = 1; i <= MAX_QMU_EP; i++) {
- if (wErrVal & QMU_TX_CS_ERR(i))
- qmu_dbg(K_ERR, "Tx %d checksum error!\r\n", i);
- if (wErrVal & QMU_TX_LEN_ERR(i))
- qmu_dbg(K_ERR, "Tx %d buffer length error!\r\n", i);
- }
- mu3d_writel(mbase, U3D_TQERRIR0, wErrVal);
- }
- if ((wQmuVal & RXQ_EMPTY_INT) || (wQmuVal & TXQ_EMPTY_INT)) {
- u32 wEmptyVal = mu3d_readl(mbase, U3D_QEMIR);
- qmu_dbg(K_DEBUG, "%s Empty in QMU mode![0x%x]\r\n",
- (wQmuVal & TXQ_EMPTY_INT) ? "TX" : "RX", wEmptyVal);
- mu3d_writel(mbase, U3D_QEMIR, wEmptyVal);
- }
- }
- #endif
|