18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * mtu3_qmu.c - Queue Management Unit driver for device controller 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2016 MediaTek Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Chunfeng Yun <chunfeng.yun@mediatek.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci/* 118c2ecf20Sopenharmony_ci * Queue Management Unit (QMU) is designed to unload SW effort 128c2ecf20Sopenharmony_ci * to serve DMA interrupts. 138c2ecf20Sopenharmony_ci * By preparing General Purpose Descriptor (GPD) and Buffer Descriptor (BD), 148c2ecf20Sopenharmony_ci * SW links data buffers and triggers QMU to send / receive data to 158c2ecf20Sopenharmony_ci * host / from device at a time. 168c2ecf20Sopenharmony_ci * And now only GPD is supported. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * For more detailed information, please refer to QMU Programming Guide 198c2ecf20Sopenharmony_ci */ 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#include <linux/dmapool.h> 228c2ecf20Sopenharmony_ci#include <linux/iopoll.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include "mtu3.h" 258c2ecf20Sopenharmony_ci#include "mtu3_trace.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define QMU_CHECKSUM_LEN 16 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define GPD_FLAGS_HWO BIT(0) 308c2ecf20Sopenharmony_ci#define GPD_FLAGS_BDP BIT(1) 318c2ecf20Sopenharmony_ci#define GPD_FLAGS_BPS BIT(2) 328c2ecf20Sopenharmony_ci#define GPD_FLAGS_ZLP BIT(6) 338c2ecf20Sopenharmony_ci#define GPD_FLAGS_IOC BIT(7) 348c2ecf20Sopenharmony_ci#define GET_GPD_HWO(gpd) (le32_to_cpu((gpd)->dw0_info) & GPD_FLAGS_HWO) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define GPD_RX_BUF_LEN_OG(x) (((x) & 0xffff) << 16) 378c2ecf20Sopenharmony_ci#define GPD_RX_BUF_LEN_EL(x) (((x) & 0xfffff) << 12) 388c2ecf20Sopenharmony_ci#define GPD_RX_BUF_LEN(mtu, x) \ 398c2ecf20Sopenharmony_ci({ \ 408c2ecf20Sopenharmony_ci typeof(x) x_ = (x); \ 418c2ecf20Sopenharmony_ci ((mtu)->gen2cp) ? GPD_RX_BUF_LEN_EL(x_) : GPD_RX_BUF_LEN_OG(x_); \ 428c2ecf20Sopenharmony_ci}) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define GPD_DATA_LEN_OG(x) ((x) & 0xffff) 458c2ecf20Sopenharmony_ci#define GPD_DATA_LEN_EL(x) ((x) & 0xfffff) 468c2ecf20Sopenharmony_ci#define GPD_DATA_LEN(mtu, x) \ 478c2ecf20Sopenharmony_ci({ \ 488c2ecf20Sopenharmony_ci typeof(x) x_ = (x); \ 498c2ecf20Sopenharmony_ci ((mtu)->gen2cp) ? GPD_DATA_LEN_EL(x_) : GPD_DATA_LEN_OG(x_); \ 508c2ecf20Sopenharmony_ci}) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define GPD_EXT_FLAG_ZLP BIT(29) 538c2ecf20Sopenharmony_ci#define GPD_EXT_NGP_OG(x) (((x) & 0xf) << 20) 548c2ecf20Sopenharmony_ci#define GPD_EXT_BUF_OG(x) (((x) & 0xf) << 16) 558c2ecf20Sopenharmony_ci#define GPD_EXT_NGP_EL(x) (((x) & 0xf) << 28) 568c2ecf20Sopenharmony_ci#define GPD_EXT_BUF_EL(x) (((x) & 0xf) << 24) 578c2ecf20Sopenharmony_ci#define GPD_EXT_NGP(mtu, x) \ 588c2ecf20Sopenharmony_ci({ \ 598c2ecf20Sopenharmony_ci typeof(x) x_ = (x); \ 608c2ecf20Sopenharmony_ci ((mtu)->gen2cp) ? GPD_EXT_NGP_EL(x_) : GPD_EXT_NGP_OG(x_); \ 618c2ecf20Sopenharmony_ci}) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define GPD_EXT_BUF(mtu, x) \ 648c2ecf20Sopenharmony_ci({ \ 658c2ecf20Sopenharmony_ci typeof(x) x_ = (x); \ 668c2ecf20Sopenharmony_ci ((mtu)->gen2cp) ? GPD_EXT_BUF_EL(x_) : GPD_EXT_BUF_OG(x_); \ 678c2ecf20Sopenharmony_ci}) 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define HILO_GEN64(hi, lo) (((u64)(hi) << 32) + (lo)) 708c2ecf20Sopenharmony_ci#define HILO_DMA(hi, lo) \ 718c2ecf20Sopenharmony_ci ((dma_addr_t)HILO_GEN64((le32_to_cpu(hi)), (le32_to_cpu(lo)))) 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic dma_addr_t read_txq_cur_addr(void __iomem *mbase, u8 epnum) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 txcpr; 768c2ecf20Sopenharmony_ci u32 txhiar; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci txcpr = mtu3_readl(mbase, USB_QMU_TQCPR(epnum)); 798c2ecf20Sopenharmony_ci txhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return HILO_DMA(QMU_CUR_GPD_ADDR_HI(txhiar), txcpr); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic dma_addr_t read_rxq_cur_addr(void __iomem *mbase, u8 epnum) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci u32 rxcpr; 878c2ecf20Sopenharmony_ci u32 rxhiar; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci rxcpr = mtu3_readl(mbase, USB_QMU_RQCPR(epnum)); 908c2ecf20Sopenharmony_ci rxhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci return HILO_DMA(QMU_CUR_GPD_ADDR_HI(rxhiar), rxcpr); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistatic void write_txq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci u32 tqhiar; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_TQSAR(epnum), 1008c2ecf20Sopenharmony_ci cpu_to_le32(lower_32_bits(dma))); 1018c2ecf20Sopenharmony_ci tqhiar = mtu3_readl(mbase, USB_QMU_TQHIAR(epnum)); 1028c2ecf20Sopenharmony_ci tqhiar &= ~QMU_START_ADDR_HI_MSK; 1038c2ecf20Sopenharmony_ci tqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); 1048c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_TQHIAR(epnum), tqhiar); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic void write_rxq_start_addr(void __iomem *mbase, u8 epnum, dma_addr_t dma) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci u32 rqhiar; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_RQSAR(epnum), 1128c2ecf20Sopenharmony_ci cpu_to_le32(lower_32_bits(dma))); 1138c2ecf20Sopenharmony_ci rqhiar = mtu3_readl(mbase, USB_QMU_RQHIAR(epnum)); 1148c2ecf20Sopenharmony_ci rqhiar &= ~QMU_START_ADDR_HI_MSK; 1158c2ecf20Sopenharmony_ci rqhiar |= QMU_START_ADDR_HI(upper_32_bits(dma)); 1168c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_RQHIAR(epnum), rqhiar); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic struct qmu_gpd *gpd_dma_to_virt(struct mtu3_gpd_ring *ring, 1208c2ecf20Sopenharmony_ci dma_addr_t dma_addr) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci dma_addr_t dma_base = ring->dma; 1238c2ecf20Sopenharmony_ci struct qmu_gpd *gpd_head = ring->start; 1248c2ecf20Sopenharmony_ci u32 offset = (dma_addr - dma_base) / sizeof(*gpd_head); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci if (offset >= MAX_GPD_NUM) 1278c2ecf20Sopenharmony_ci return NULL; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return gpd_head + offset; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic dma_addr_t gpd_virt_to_dma(struct mtu3_gpd_ring *ring, 1338c2ecf20Sopenharmony_ci struct qmu_gpd *gpd) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci dma_addr_t dma_base = ring->dma; 1368c2ecf20Sopenharmony_ci struct qmu_gpd *gpd_head = ring->start; 1378c2ecf20Sopenharmony_ci u32 offset; 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci offset = gpd - gpd_head; 1408c2ecf20Sopenharmony_ci if (offset >= MAX_GPD_NUM) 1418c2ecf20Sopenharmony_ci return 0; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return dma_base + (offset * sizeof(*gpd)); 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void gpd_ring_init(struct mtu3_gpd_ring *ring, struct qmu_gpd *gpd) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci ring->start = gpd; 1498c2ecf20Sopenharmony_ci ring->enqueue = gpd; 1508c2ecf20Sopenharmony_ci ring->dequeue = gpd; 1518c2ecf20Sopenharmony_ci ring->end = gpd + MAX_GPD_NUM - 1; 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic void reset_gpd_list(struct mtu3_ep *mep) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 1578c2ecf20Sopenharmony_ci struct qmu_gpd *gpd = ring->start; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (gpd) { 1608c2ecf20Sopenharmony_ci gpd->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); 1618c2ecf20Sopenharmony_ci gpd_ring_init(ring, gpd); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ciint mtu3_gpd_ring_alloc(struct mtu3_ep *mep) 1668c2ecf20Sopenharmony_ci{ 1678c2ecf20Sopenharmony_ci struct qmu_gpd *gpd; 1688c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* software own all gpds as default */ 1718c2ecf20Sopenharmony_ci gpd = dma_pool_zalloc(mep->mtu->qmu_gpd_pool, GFP_ATOMIC, &ring->dma); 1728c2ecf20Sopenharmony_ci if (gpd == NULL) 1738c2ecf20Sopenharmony_ci return -ENOMEM; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci gpd_ring_init(ring, gpd); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return 0; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_civoid mtu3_gpd_ring_free(struct mtu3_ep *mep) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci dma_pool_free(mep->mtu->qmu_gpd_pool, 1858c2ecf20Sopenharmony_ci ring->start, ring->dma); 1868c2ecf20Sopenharmony_ci memset(ring, 0, sizeof(*ring)); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_civoid mtu3_qmu_resume(struct mtu3_ep *mep) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci struct mtu3 *mtu = mep->mtu; 1928c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 1938c2ecf20Sopenharmony_ci int epnum = mep->epnum; 1948c2ecf20Sopenharmony_ci u32 offset; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci offset = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci mtu3_writel(mbase, offset, QMU_Q_RESUME); 1998c2ecf20Sopenharmony_ci if (!(mtu3_readl(mbase, offset) & QMU_Q_ACTIVE)) 2008c2ecf20Sopenharmony_ci mtu3_writel(mbase, offset, QMU_Q_RESUME); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic struct qmu_gpd *advance_enq_gpd(struct mtu3_gpd_ring *ring) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci if (ring->enqueue < ring->end) 2068c2ecf20Sopenharmony_ci ring->enqueue++; 2078c2ecf20Sopenharmony_ci else 2088c2ecf20Sopenharmony_ci ring->enqueue = ring->start; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ring->enqueue; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* @dequeue may be NULL if ring is unallocated or freed */ 2148c2ecf20Sopenharmony_cistatic struct qmu_gpd *advance_deq_gpd(struct mtu3_gpd_ring *ring) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci if (ring->dequeue < ring->end) 2178c2ecf20Sopenharmony_ci ring->dequeue++; 2188c2ecf20Sopenharmony_ci else 2198c2ecf20Sopenharmony_ci ring->dequeue = ring->start; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci return ring->dequeue; 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci/* check if a ring is emtpy */ 2258c2ecf20Sopenharmony_cistatic int gpd_ring_empty(struct mtu3_gpd_ring *ring) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct qmu_gpd *enq = ring->enqueue; 2288c2ecf20Sopenharmony_ci struct qmu_gpd *next; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci if (ring->enqueue < ring->end) 2318c2ecf20Sopenharmony_ci next = enq + 1; 2328c2ecf20Sopenharmony_ci else 2338c2ecf20Sopenharmony_ci next = ring->start; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* one gpd is reserved to simplify gpd preparation */ 2368c2ecf20Sopenharmony_ci return next == ring->dequeue; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ciint mtu3_prepare_transfer(struct mtu3_ep *mep) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci return gpd_ring_empty(&mep->gpd_ring); 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic int mtu3_prepare_tx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct qmu_gpd *enq; 2478c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 2488c2ecf20Sopenharmony_ci struct qmu_gpd *gpd = ring->enqueue; 2498c2ecf20Sopenharmony_ci struct usb_request *req = &mreq->request; 2508c2ecf20Sopenharmony_ci struct mtu3 *mtu = mep->mtu; 2518c2ecf20Sopenharmony_ci dma_addr_t enq_dma; 2528c2ecf20Sopenharmony_ci u32 ext_addr; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci gpd->dw0_info = 0; /* SW own it */ 2558c2ecf20Sopenharmony_ci gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); 2568c2ecf20Sopenharmony_ci ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); 2578c2ecf20Sopenharmony_ci gpd->dw3_info = cpu_to_le32(GPD_DATA_LEN(mtu, req->length)); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* get the next GPD */ 2608c2ecf20Sopenharmony_ci enq = advance_enq_gpd(ring); 2618c2ecf20Sopenharmony_ci enq_dma = gpd_virt_to_dma(ring, enq); 2628c2ecf20Sopenharmony_ci dev_dbg(mep->mtu->dev, "TX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", 2638c2ecf20Sopenharmony_ci mep->epnum, gpd, enq, &enq_dma); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); 2668c2ecf20Sopenharmony_ci gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); 2678c2ecf20Sopenharmony_ci ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); 2688c2ecf20Sopenharmony_ci gpd->dw0_info = cpu_to_le32(ext_addr); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (req->zero) { 2718c2ecf20Sopenharmony_ci if (mtu->gen2cp) 2728c2ecf20Sopenharmony_ci gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_ZLP); 2738c2ecf20Sopenharmony_ci else 2748c2ecf20Sopenharmony_ci gpd->dw3_info |= cpu_to_le32(GPD_EXT_FLAG_ZLP); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* prevent reorder, make sure GPD's HWO is set last */ 2788c2ecf20Sopenharmony_ci mb(); 2798c2ecf20Sopenharmony_ci gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci mreq->gpd = gpd; 2828c2ecf20Sopenharmony_ci trace_mtu3_prepare_gpd(mep, gpd); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_cistatic int mtu3_prepare_rx_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct qmu_gpd *enq; 2908c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 2918c2ecf20Sopenharmony_ci struct qmu_gpd *gpd = ring->enqueue; 2928c2ecf20Sopenharmony_ci struct usb_request *req = &mreq->request; 2938c2ecf20Sopenharmony_ci struct mtu3 *mtu = mep->mtu; 2948c2ecf20Sopenharmony_ci dma_addr_t enq_dma; 2958c2ecf20Sopenharmony_ci u32 ext_addr; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci gpd->dw0_info = 0; /* SW own it */ 2988c2ecf20Sopenharmony_ci gpd->buffer = cpu_to_le32(lower_32_bits(req->dma)); 2998c2ecf20Sopenharmony_ci ext_addr = GPD_EXT_BUF(mtu, upper_32_bits(req->dma)); 3008c2ecf20Sopenharmony_ci gpd->dw0_info = cpu_to_le32(GPD_RX_BUF_LEN(mtu, req->length)); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* get the next GPD */ 3038c2ecf20Sopenharmony_ci enq = advance_enq_gpd(ring); 3048c2ecf20Sopenharmony_ci enq_dma = gpd_virt_to_dma(ring, enq); 3058c2ecf20Sopenharmony_ci dev_dbg(mep->mtu->dev, "RX-EP%d queue gpd=%p, enq=%p, qdma=%pad\n", 3068c2ecf20Sopenharmony_ci mep->epnum, gpd, enq, &enq_dma); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci enq->dw0_info &= cpu_to_le32(~GPD_FLAGS_HWO); 3098c2ecf20Sopenharmony_ci gpd->next_gpd = cpu_to_le32(lower_32_bits(enq_dma)); 3108c2ecf20Sopenharmony_ci ext_addr |= GPD_EXT_NGP(mtu, upper_32_bits(enq_dma)); 3118c2ecf20Sopenharmony_ci gpd->dw3_info = cpu_to_le32(ext_addr); 3128c2ecf20Sopenharmony_ci /* prevent reorder, make sure GPD's HWO is set last */ 3138c2ecf20Sopenharmony_ci mb(); 3148c2ecf20Sopenharmony_ci gpd->dw0_info |= cpu_to_le32(GPD_FLAGS_IOC | GPD_FLAGS_HWO); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci mreq->gpd = gpd; 3178c2ecf20Sopenharmony_ci trace_mtu3_prepare_gpd(mep, gpd); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_civoid mtu3_insert_gpd(struct mtu3_ep *mep, struct mtu3_request *mreq) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (mep->is_in) 3268c2ecf20Sopenharmony_ci mtu3_prepare_tx_gpd(mep, mreq); 3278c2ecf20Sopenharmony_ci else 3288c2ecf20Sopenharmony_ci mtu3_prepare_rx_gpd(mep, mreq); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ciint mtu3_qmu_start(struct mtu3_ep *mep) 3328c2ecf20Sopenharmony_ci{ 3338c2ecf20Sopenharmony_ci struct mtu3 *mtu = mep->mtu; 3348c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 3358c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 3368c2ecf20Sopenharmony_ci u8 epnum = mep->epnum; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (mep->is_in) { 3398c2ecf20Sopenharmony_ci /* set QMU start address */ 3408c2ecf20Sopenharmony_ci write_txq_start_addr(mbase, epnum, ring->dma); 3418c2ecf20Sopenharmony_ci mtu3_setbits(mbase, MU3D_EP_TXCR0(epnum), TX_DMAREQEN); 3428c2ecf20Sopenharmony_ci /* send zero length packet according to ZLP flag in GPD */ 3438c2ecf20Sopenharmony_ci mtu3_setbits(mbase, U3D_QCR1, QMU_TX_ZLP(epnum)); 3448c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_TQERRIESR0, 3458c2ecf20Sopenharmony_ci QMU_TX_LEN_ERR(epnum) | QMU_TX_CS_ERR(epnum)); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (mtu3_readl(mbase, USB_QMU_TQCSR(epnum)) & QMU_Q_ACTIVE) { 3488c2ecf20Sopenharmony_ci dev_warn(mtu->dev, "Tx %d Active Now!\n", epnum); 3498c2ecf20Sopenharmony_ci return 0; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_TQCSR(epnum), QMU_Q_START); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci } else { 3548c2ecf20Sopenharmony_ci write_rxq_start_addr(mbase, epnum, ring->dma); 3558c2ecf20Sopenharmony_ci mtu3_setbits(mbase, MU3D_EP_RXCR0(epnum), RX_DMAREQEN); 3568c2ecf20Sopenharmony_ci /* don't expect ZLP */ 3578c2ecf20Sopenharmony_ci mtu3_clrbits(mbase, U3D_QCR3, QMU_RX_ZLP(epnum)); 3588c2ecf20Sopenharmony_ci /* move to next GPD when receive ZLP */ 3598c2ecf20Sopenharmony_ci mtu3_setbits(mbase, U3D_QCR3, QMU_RX_COZ(epnum)); 3608c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_RQERRIESR0, 3618c2ecf20Sopenharmony_ci QMU_RX_LEN_ERR(epnum) | QMU_RX_CS_ERR(epnum)); 3628c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_RQERRIESR1, QMU_RX_ZLP_ERR(epnum)); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci if (mtu3_readl(mbase, USB_QMU_RQCSR(epnum)) & QMU_Q_ACTIVE) { 3658c2ecf20Sopenharmony_ci dev_warn(mtu->dev, "Rx %d Active Now!\n", epnum); 3668c2ecf20Sopenharmony_ci return 0; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci mtu3_writel(mbase, USB_QMU_RQCSR(epnum), QMU_Q_START); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci return 0; 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci/* may called in atomic context */ 3758c2ecf20Sopenharmony_civoid mtu3_qmu_stop(struct mtu3_ep *mep) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct mtu3 *mtu = mep->mtu; 3788c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 3798c2ecf20Sopenharmony_ci int epnum = mep->epnum; 3808c2ecf20Sopenharmony_ci u32 value = 0; 3818c2ecf20Sopenharmony_ci u32 qcsr; 3828c2ecf20Sopenharmony_ci int ret; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci qcsr = mep->is_in ? USB_QMU_TQCSR(epnum) : USB_QMU_RQCSR(epnum); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci if (!(mtu3_readl(mbase, qcsr) & QMU_Q_ACTIVE)) { 3878c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s's qmu is inactive now!\n", mep->name); 3888c2ecf20Sopenharmony_ci return; 3898c2ecf20Sopenharmony_ci } 3908c2ecf20Sopenharmony_ci mtu3_writel(mbase, qcsr, QMU_Q_STOP); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(mbase + qcsr, value, 3938c2ecf20Sopenharmony_ci !(value & QMU_Q_ACTIVE), 1, 1000); 3948c2ecf20Sopenharmony_ci if (ret) { 3958c2ecf20Sopenharmony_ci dev_err(mtu->dev, "stop %s's qmu failed\n", mep->name); 3968c2ecf20Sopenharmony_ci return; 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s's qmu stop now!\n", mep->name); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_civoid mtu3_qmu_flush(struct mtu3_ep *mep) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci dev_dbg(mep->mtu->dev, "%s flush QMU %s\n", __func__, 4068c2ecf20Sopenharmony_ci ((mep->is_in) ? "TX" : "RX")); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /*Stop QMU */ 4098c2ecf20Sopenharmony_ci mtu3_qmu_stop(mep); 4108c2ecf20Sopenharmony_ci reset_gpd_list(mep); 4118c2ecf20Sopenharmony_ci} 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci/* 4148c2ecf20Sopenharmony_ci * QMU can't transfer zero length packet directly (a hardware limit 4158c2ecf20Sopenharmony_ci * on old SoCs), so when needs to send ZLP, we intentionally trigger 4168c2ecf20Sopenharmony_ci * a length error interrupt, and in the ISR sends a ZLP by BMU. 4178c2ecf20Sopenharmony_ci */ 4188c2ecf20Sopenharmony_cistatic void qmu_tx_zlp_error_handler(struct mtu3 *mtu, u8 epnum) 4198c2ecf20Sopenharmony_ci{ 4208c2ecf20Sopenharmony_ci struct mtu3_ep *mep = mtu->in_eps + epnum; 4218c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 4228c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 4238c2ecf20Sopenharmony_ci struct qmu_gpd *gpd_current = NULL; 4248c2ecf20Sopenharmony_ci struct mtu3_request *mreq; 4258c2ecf20Sopenharmony_ci dma_addr_t cur_gpd_dma; 4268c2ecf20Sopenharmony_ci u32 txcsr = 0; 4278c2ecf20Sopenharmony_ci int ret; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci mreq = next_request(mep); 4308c2ecf20Sopenharmony_ci if (mreq && mreq->request.length != 0) 4318c2ecf20Sopenharmony_ci return; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci cur_gpd_dma = read_txq_cur_addr(mbase, epnum); 4348c2ecf20Sopenharmony_ci gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci if (GPD_DATA_LEN(mtu, le32_to_cpu(gpd_current->dw3_info)) != 0) { 4378c2ecf20Sopenharmony_ci dev_err(mtu->dev, "TX EP%d buffer length error(!=0)\n", epnum); 4388c2ecf20Sopenharmony_ci return; 4398c2ecf20Sopenharmony_ci } 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s send ZLP for req=%p\n", __func__, mreq); 4428c2ecf20Sopenharmony_ci trace_mtu3_zlp_exp_gpd(mep, gpd_current); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci mtu3_clrbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci ret = readl_poll_timeout_atomic(mbase + MU3D_EP_TXCR0(mep->epnum), 4478c2ecf20Sopenharmony_ci txcsr, !(txcsr & TX_FIFOFULL), 1, 1000); 4488c2ecf20Sopenharmony_ci if (ret) { 4498c2ecf20Sopenharmony_ci dev_err(mtu->dev, "%s wait for fifo empty fail\n", __func__); 4508c2ecf20Sopenharmony_ci return; 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_TXPKTRDY); 4538c2ecf20Sopenharmony_ci /* prevent reorder, make sure GPD's HWO is set last */ 4548c2ecf20Sopenharmony_ci mb(); 4558c2ecf20Sopenharmony_ci /* by pass the current GDP */ 4568c2ecf20Sopenharmony_ci gpd_current->dw0_info |= cpu_to_le32(GPD_FLAGS_BPS | GPD_FLAGS_HWO); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci /*enable DMAREQEN, switch back to QMU mode */ 4598c2ecf20Sopenharmony_ci mtu3_setbits(mbase, MU3D_EP_TXCR0(mep->epnum), TX_DMAREQEN); 4608c2ecf20Sopenharmony_ci mtu3_qmu_resume(mep); 4618c2ecf20Sopenharmony_ci} 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci/* 4648c2ecf20Sopenharmony_ci * NOTE: request list maybe is already empty as following case: 4658c2ecf20Sopenharmony_ci * queue_tx --> qmu_interrupt(clear interrupt pending, schedule tasklet)--> 4668c2ecf20Sopenharmony_ci * queue_tx --> process_tasklet(meanwhile, the second one is transferred, 4678c2ecf20Sopenharmony_ci * tasklet process both of them)-->qmu_interrupt for second one. 4688c2ecf20Sopenharmony_ci * To avoid upper case, put qmu_done_tx in ISR directly to process it. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_cistatic void qmu_done_tx(struct mtu3 *mtu, u8 epnum) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci struct mtu3_ep *mep = mtu->in_eps + epnum; 4738c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 4748c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 4758c2ecf20Sopenharmony_ci struct qmu_gpd *gpd = ring->dequeue; 4768c2ecf20Sopenharmony_ci struct qmu_gpd *gpd_current = NULL; 4778c2ecf20Sopenharmony_ci struct usb_request *request = NULL; 4788c2ecf20Sopenharmony_ci struct mtu3_request *mreq; 4798c2ecf20Sopenharmony_ci dma_addr_t cur_gpd_dma; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci /*transfer phy address got from QMU register to virtual address */ 4828c2ecf20Sopenharmony_ci cur_gpd_dma = read_txq_cur_addr(mbase, epnum); 4838c2ecf20Sopenharmony_ci gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", 4868c2ecf20Sopenharmony_ci __func__, epnum, gpd, gpd_current, ring->enqueue); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci while (gpd && gpd != gpd_current && !GET_GPD_HWO(gpd)) { 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci mreq = next_request(mep); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (mreq == NULL || mreq->gpd != gpd) { 4938c2ecf20Sopenharmony_ci dev_err(mtu->dev, "no correct TX req is found\n"); 4948c2ecf20Sopenharmony_ci break; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci request = &mreq->request; 4988c2ecf20Sopenharmony_ci request->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); 4998c2ecf20Sopenharmony_ci trace_mtu3_complete_gpd(mep, gpd); 5008c2ecf20Sopenharmony_ci mtu3_req_complete(mep, request, 0); 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_ci gpd = advance_deq_gpd(ring); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n", 5068c2ecf20Sopenharmony_ci __func__, epnum, ring->dequeue, ring->enqueue); 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci} 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic void qmu_done_rx(struct mtu3 *mtu, u8 epnum) 5118c2ecf20Sopenharmony_ci{ 5128c2ecf20Sopenharmony_ci struct mtu3_ep *mep = mtu->out_eps + epnum; 5138c2ecf20Sopenharmony_ci struct mtu3_gpd_ring *ring = &mep->gpd_ring; 5148c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 5158c2ecf20Sopenharmony_ci struct qmu_gpd *gpd = ring->dequeue; 5168c2ecf20Sopenharmony_ci struct qmu_gpd *gpd_current = NULL; 5178c2ecf20Sopenharmony_ci struct usb_request *req = NULL; 5188c2ecf20Sopenharmony_ci struct mtu3_request *mreq; 5198c2ecf20Sopenharmony_ci dma_addr_t cur_gpd_dma; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci cur_gpd_dma = read_rxq_cur_addr(mbase, epnum); 5228c2ecf20Sopenharmony_ci gpd_current = gpd_dma_to_virt(ring, cur_gpd_dma); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s EP%d, last=%p, current=%p, enq=%p\n", 5258c2ecf20Sopenharmony_ci __func__, epnum, gpd, gpd_current, ring->enqueue); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci while (gpd && gpd != gpd_current && !GET_GPD_HWO(gpd)) { 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci mreq = next_request(mep); 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (mreq == NULL || mreq->gpd != gpd) { 5328c2ecf20Sopenharmony_ci dev_err(mtu->dev, "no correct RX req is found\n"); 5338c2ecf20Sopenharmony_ci break; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci req = &mreq->request; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci req->actual = GPD_DATA_LEN(mtu, le32_to_cpu(gpd->dw3_info)); 5388c2ecf20Sopenharmony_ci trace_mtu3_complete_gpd(mep, gpd); 5398c2ecf20Sopenharmony_ci mtu3_req_complete(mep, req, 0); 5408c2ecf20Sopenharmony_ci 5418c2ecf20Sopenharmony_ci gpd = advance_deq_gpd(ring); 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "%s EP%d, deq=%p, enq=%p, complete\n", 5458c2ecf20Sopenharmony_ci __func__, epnum, ring->dequeue, ring->enqueue); 5468c2ecf20Sopenharmony_ci} 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_cistatic void qmu_done_isr(struct mtu3 *mtu, u32 done_status) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci int i; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci for (i = 1; i < mtu->num_eps; i++) { 5538c2ecf20Sopenharmony_ci if (done_status & QMU_RX_DONE_INT(i)) 5548c2ecf20Sopenharmony_ci qmu_done_rx(mtu, i); 5558c2ecf20Sopenharmony_ci if (done_status & QMU_TX_DONE_INT(i)) 5568c2ecf20Sopenharmony_ci qmu_done_tx(mtu, i); 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci} 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_cistatic void qmu_exception_isr(struct mtu3 *mtu, u32 qmu_status) 5618c2ecf20Sopenharmony_ci{ 5628c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 5638c2ecf20Sopenharmony_ci u32 errval; 5648c2ecf20Sopenharmony_ci int i; 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci if ((qmu_status & RXQ_CSERR_INT) || (qmu_status & RXQ_LENERR_INT)) { 5678c2ecf20Sopenharmony_ci errval = mtu3_readl(mbase, U3D_RQERRIR0); 5688c2ecf20Sopenharmony_ci for (i = 1; i < mtu->num_eps; i++) { 5698c2ecf20Sopenharmony_ci if (errval & QMU_RX_CS_ERR(i)) 5708c2ecf20Sopenharmony_ci dev_err(mtu->dev, "Rx %d CS error!\n", i); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci if (errval & QMU_RX_LEN_ERR(i)) 5738c2ecf20Sopenharmony_ci dev_err(mtu->dev, "RX %d Length error\n", i); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_RQERRIR0, errval); 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci if (qmu_status & RXQ_ZLPERR_INT) { 5798c2ecf20Sopenharmony_ci errval = mtu3_readl(mbase, U3D_RQERRIR1); 5808c2ecf20Sopenharmony_ci for (i = 1; i < mtu->num_eps; i++) { 5818c2ecf20Sopenharmony_ci if (errval & QMU_RX_ZLP_ERR(i)) 5828c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "RX EP%d Recv ZLP\n", i); 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_RQERRIR1, errval); 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci if ((qmu_status & TXQ_CSERR_INT) || (qmu_status & TXQ_LENERR_INT)) { 5888c2ecf20Sopenharmony_ci errval = mtu3_readl(mbase, U3D_TQERRIR0); 5898c2ecf20Sopenharmony_ci for (i = 1; i < mtu->num_eps; i++) { 5908c2ecf20Sopenharmony_ci if (errval & QMU_TX_CS_ERR(i)) 5918c2ecf20Sopenharmony_ci dev_err(mtu->dev, "Tx %d checksum error!\n", i); 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci if (errval & QMU_TX_LEN_ERR(i)) 5948c2ecf20Sopenharmony_ci qmu_tx_zlp_error_handler(mtu, i); 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_TQERRIR0, errval); 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci} 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ciirqreturn_t mtu3_qmu_isr(struct mtu3 *mtu) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci void __iomem *mbase = mtu->mac_base; 6038c2ecf20Sopenharmony_ci u32 qmu_status; 6048c2ecf20Sopenharmony_ci u32 qmu_done_status; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci /* U3D_QISAR1 is read update */ 6078c2ecf20Sopenharmony_ci qmu_status = mtu3_readl(mbase, U3D_QISAR1); 6088c2ecf20Sopenharmony_ci qmu_status &= mtu3_readl(mbase, U3D_QIER1); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci qmu_done_status = mtu3_readl(mbase, U3D_QISAR0); 6118c2ecf20Sopenharmony_ci qmu_done_status &= mtu3_readl(mbase, U3D_QIER0); 6128c2ecf20Sopenharmony_ci mtu3_writel(mbase, U3D_QISAR0, qmu_done_status); /* W1C */ 6138c2ecf20Sopenharmony_ci dev_dbg(mtu->dev, "=== QMUdone[tx=%x, rx=%x] QMUexp[%x] ===\n", 6148c2ecf20Sopenharmony_ci (qmu_done_status & 0xFFFF), qmu_done_status >> 16, 6158c2ecf20Sopenharmony_ci qmu_status); 6168c2ecf20Sopenharmony_ci trace_mtu3_qmu_isr(qmu_done_status, qmu_status); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci if (qmu_done_status) 6198c2ecf20Sopenharmony_ci qmu_done_isr(mtu, qmu_done_status); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci if (qmu_status) 6228c2ecf20Sopenharmony_ci qmu_exception_isr(mtu, qmu_status); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ciint mtu3_qmu_init(struct mtu3 *mtu) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci compiletime_assert(QMU_GPD_SIZE == 16, "QMU_GPD size SHOULD be 16B"); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci mtu->qmu_gpd_pool = dma_pool_create("QMU_GPD", mtu->dev, 6338c2ecf20Sopenharmony_ci QMU_GPD_RING_SIZE, QMU_GPD_SIZE, 0); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (!mtu->qmu_gpd_pool) 6368c2ecf20Sopenharmony_ci return -ENOMEM; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci return 0; 6398c2ecf20Sopenharmony_ci} 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_civoid mtu3_qmu_exit(struct mtu3 *mtu) 6428c2ecf20Sopenharmony_ci{ 6438c2ecf20Sopenharmony_ci dma_pool_destroy(mtu->qmu_gpd_pool); 6448c2ecf20Sopenharmony_ci} 645