📄 omap_udc.c
字号:
if (!ep->double_buf) break; ep->fnf = 1; } if (ep_stat & UDC_EP_HALTED) break; if (ep_stat & FIFO_FULL) avail = ep->ep.maxpacket; else { avail = UDC_RXFSTAT_REG; ep->fnf = ep->double_buf; } count = read_packet(buf, req, avail); /* partial packet reads may not be errors */ if (count < ep->ep.maxpacket) { is_last = 1; /* overflowed this request? flush extra data */ if (count != avail) { req->req.status = -EOVERFLOW; avail -= count; while (avail--) (void) *(volatile u8 *)&UDC_DATA_REG; } } else if (req->req.length == req->req.actual) is_last = 1; else is_last = 0; if (!ep->bEndpointAddress) break; if (is_last) done(ep, req, 0); break; } return is_last;}/*-------------------------------------------------------------------------*/static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start){ dma_addr_t end; /* IN-DMA needs this on fault/cancel paths, so 15xx misreports * the last transfer's bytecount by more than a FIFO's worth. */ if (cpu_is_omap15xx()) return 0;#ifdef CONFIG_ARCH_OMAP24XX end = omap_readw(OMAP_DMA4_CSAC_REG(ep->lch));#else end = omap_readw(OMAP_DMA_CSAC(ep->lch));#endif if (end == ep->dma_counter) return 0; end |= start & (0xffff << 16); if (end < start) end += 0x10000; return end - start;}#ifdef CONFIG_ARCH_OMAP24XX#define DMA_DEST_LAST(x) OMAP_DMA4_CDAC_REG(x)#else#define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ ? OMAP_DMA_CSAC(x) /* really: CPC */ \ : OMAP_DMA_CDAC(x))#endifstatic u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start){ dma_addr_t end; end = omap_readw(DMA_DEST_LAST(ep->lch)); if (end == ep->dma_counter) return 0; end |= start & (0xffff << 16); if (cpu_is_omap15xx()) end++; if (end < start) end += 0x10000; return end - start;}/* Each USB transfer request using DMA maps to one or more DMA transfers. * When DMA completion isn't request completion, the UDC continues with * the next DMA transfer for that USB transfer. */static void next_in_dma(struct omap_ep *ep, struct omap_req *req){ u16 txdma_ctrl; unsigned length = req->req.length - req->req.actual;#ifndef CONFIG_ARCH_OMAP24XX const int sync_mode = cpu_is_omap15xx() ? OMAP_DMA_SYNC_FRAME : OMAP_DMA_SYNC_ELEMENT;#endif /* measure length in either bytes or packets */ if (((cpu_is_omap16xx() || cpu_is_omap2420()) && length <= (UDC_TXN_TSC + 1)) || (cpu_is_omap15xx() && length < ep->maxpacket)) { txdma_ctrl = UDC_TXN_EOT | length;#ifdef CONFIG_ARCH_OMAP24XX // hw sync, sender of DMA requests is src/* extern void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode, int dma_trigger, int src_or_dst_synch); // source synch(1) or destination synch(0)*/ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, length, 1, OMAP_DMA_SYNC_ELEMENT, (OMAP_DMA_USB_W2FC_TX0 + 1 - 1 + ep->dma_channel), 0);#else/* extern void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode);*/ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, length, 1, sync_mode);#endif } else { length = min(length / ep->maxpacket, (unsigned) UDC_TXN_TSC + 1); txdma_ctrl = length;#ifdef CONFIG_ARCH_OMAP24XX omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, ep->ep.maxpacket, length, OMAP_DMA_SYNC_ELEMENT, (OMAP_DMA_USB_W2FC_TX0 + 1 - 1 + ep->dma_channel), 0);#else omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, ep->ep.maxpacket, length, sync_mode);#endif length *= ep->maxpacket; }#ifdef CONFIG_ARCH_OMAP24XX omap_set_dma_src_params(ep->lch, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 0, 0);#else omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);#endif omap_start_dma(ep->lch);#ifdef CONFIG_ARCH_OMAP24XX ep->dma_counter = omap_readw(OMAP_DMA4_CSAC_REG(ep->lch));#else ep->dma_counter = omap_readw(OMAP_DMA_CSAC(ep->lch));#endif UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel); UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl; req->dma_bytes = length;}static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status){ if (status == 0) { req->req.actual += req->dma_bytes; /* return if this request needs to send data or zlp */ if (req->req.actual < req->req.length) return; if (req->req.zero && req->dma_bytes != 0 && (req->req.actual % ep->maxpacket) == 0) return; } else req->req.actual += dma_src_len(ep, req->req.dma + req->req.actual); /* tx completion */ omap_stop_dma(ep->lch); UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel); done(ep, req, status);}static void next_out_dma(struct omap_ep *ep, struct omap_req *req){ unsigned packets; /* NOTE: we filtered out "short reads" before, so we know * the buffer has only whole numbers of packets. */ /* set up this DMA transfer, enable the fifo, start */ packets = (req->req.length - req->req.actual) / ep->ep.maxpacket; packets = min(packets, (unsigned)UDC_RXN_TC + 1); req->dma_bytes = packets * ep->ep.maxpacket;#ifdef CONFIG_ARCH_OMAP24XX/* extern void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode, int dma_trigger, int src_or_dst_synch); // source synch(1) or destination synch(0) //dma_trigger - hardware trigger number for hardware sychronized channel*/ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, ep->ep.maxpacket, packets, OMAP_DMA_SYNC_ELEMENT, (OMAP_DMA_USB_W2FC_RX0 +1 - 1 + ep->dma_channel), 0); // (OMAP_DMA_USB_W2FC_RX0 - 1), 1);/* extern void omap_set_dma_dest_params(int lch, int dest_amode, int dest_start, int dst_ei, int dst_fi);*/ omap_set_dma_dest_params(ep->lch, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, 0, 0);#else/* extern void omap_set_dma_transfer_params(int lch, int data_type, int elem_count, int frame_count, int sync_mode);*/ omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, ep->ep.maxpacket, packets, OMAP_DMA_SYNC_ELEMENT);/* extern void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, unsigned long dest_start);*/ omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual);#endif ep->dma_counter = omap_readw(DMA_DEST_LAST(ep->lch)); UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel); UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf); UDC_CTRL_REG = UDC_SET_FIFO_EN; omap_start_dma(ep->lch);}static voidfinish_out_dma(struct omap_ep *ep, struct omap_req *req, int status){ u16 count; if (status == 0) ep->dma_counter = (u16) (req->req.dma + req->req.actual); count = dma_dest_len(ep, req->req.dma + req->req.actual); count += req->req.actual; if (count <= req->req.length) req->req.actual = count; if (count != req->dma_bytes || status) omap_stop_dma(ep->lch); /* if this wasn't short, request may need another transfer */ else if (req->req.actual < req->req.length) return; /* rx completion */ UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel); done(ep, req, status);}static void dma_irq(struct omap_udc *udc, u16 irq_src){ u16 dman_stat = UDC_DMAN_STAT_REG; struct omap_ep *ep; struct omap_req *req; /* IN dma: tx to host */ if (irq_src & UDC_TXN_DONE) { ep = &udc->ep[16 + UDC_DMA_TX_SRC(dman_stat)]; ep->irqs++; /* can see TXN_DONE after dma abort */ if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); finish_in_dma(ep, req, 0); } UDC_IRQ_SRC_REG = UDC_TXN_DONE; if (!list_empty (&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_in_dma(ep, req); } } /* OUT dma: rx from host */ if (irq_src & UDC_RXN_EOT) { ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; ep->irqs++; /* can see RXN_EOT after dma abort */ if (!list_empty(&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); finish_out_dma(ep, req, 0); } UDC_IRQ_SRC_REG = UDC_RXN_EOT; if (!list_empty (&ep->queue)) { req = container_of(ep->queue.next, struct omap_req, queue); next_out_dma(ep, req); } } if (irq_src & UDC_RXN_CNT) { ep = &udc->ep[UDC_DMA_RX_SRC(dman_stat)]; ep->irqs++; /* omap15xx does this unasked... */ VDBG("%s, RX_CNT irq?\n", ep->ep.name); UDC_IRQ_SRC_REG = UDC_RXN_CNT; }}static void dma_error(int lch, u16 ch_status, void *data){ struct omap_ep *ep = data; /* if ch_status & OMAP_DMA_DROP_IRQ ... */ /* if ch_status & OMAP_DMA_TOUT_IRQ ... */ ERR("%s dma error, lch %d status %02x\n", ep->ep.name, lch, ch_status); /* complete current transfer ... */}static void dma_channel_claim(struct omap_ep *ep, unsigned channel){ u16 reg; int status, restart, is_in; is_in = ep->bEndpointAddress & USB_DIR_IN; if (is_in) reg = UDC_TXDMA_CFG_REG; else reg = UDC_RXDMA_CFG_REG; reg |= 1 << 12; /* "pulse" activated */ ep->dma_channel = 0; ep->lch = -1; if (channel == 0 || channel > 3) { if ((reg & 0x0f00) == 0) channel = 3; else if ((reg & 0x00f0) == 0) channel = 2; else if ((reg & 0x000f) == 0) /* preferred for ISO */ channel = 1; else { status = -EMLINK; goto just_restart; } } reg |= (0x0f & ep->bEndpointAddress) << (4 * (channel - 1)); ep->dma_channel = channel; if (is_in) { status = omap_request_dma(OMAP_DMA_USB_W2FC_TX0 - 1 + channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { UDC_TXDMA_CFG_REG = reg;#ifdef CONFIG_ARCH_OMAP24XX/* extern void omap_set_dma_dest_params(int lch, int dest_amode, int dest_start, int dst_ei, int dst_fi);*/ omap_set_dma_dest_params(ep->lch, OMAP_DMA_AMODE_CONSTANT, (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), 1, 1);#else/* extern void omap_set_dma_dest_params(int lch, int dest_port, int dest_amode, unsigned long dest_start);*/ omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG));#endif } } else {/* extern int omap_request_dma(int dev_id, const char *dev_name, void (* callback)(int lch, u16 ch_status, void *data), void *data, int *dma_ch_out);*/ status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel, ep->ep.name, dma_error, ep, &ep->lch); if (status == 0) { UDC_RXDMA_CFG_REG = reg;#ifdef CONFIG_ARCH_OMAP24XX/* extern void omap_set_dma_src_params(int lch, int src_amode, int src_start,int src_ei, int src_fi );*/ omap_set_dma_src_params(ep->lch, OMAP_DMA_AMODE_CONSTANT, (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), 1, 1);#else/* extern void omap_set_dma_src_params(int lch, int src_port, int src_amode, unsigned long src_start);*/ omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG));#endif } } if (status) ep->dma_channel = 0; else { ep->has_dma = 1;#ifdef CONFIG_ARCH_OMAP24XX omap_disable_dma_irq(ep->lch);#else omap_disable_dma_irq(ep->lch, OMAP_DMA_BLOCK_IRQ);#endif /* channel type P: hw synch (fifo) */#ifdef CONFIG_ARCH_OMAP24XX // writel(2, OMAP_DMA_LCH_CTRL_REG(ep->lch));#else if (!cpu_is_omap15xx()) omap_writew(2, OMAP_DMA_LCH_CTRL(ep->lch));#endif }just_restart: /* restart any queue, even if the claim failed */ restart = !ep->stopped && !list_empty(&ep->queue); if (status) DBG("%s no dma channel: %d%s\n", ep->ep.name, status, restart ? " (restart)" : ""); else DBG("%s claimed %cxdma%d lch %d%s\n", ep->ep.name, is_in ? 't' : 'r', ep->dma_channel - 1, ep->lch, restart ? " (restart)" : ""); if (restart) { struct omap_req *req; req = container_of(ep->queue.next, struct omap_req, queue); if (ep->has_dma) (is_in ? next_in_dma : next_out_dma)(ep, req); else { use_ep(ep, UDC_EP_SEL); (is_in ? write_fifo : read_fifo)(ep, req); deselect_ep(); if (!is_in) { UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } /* IN: 6 wait states before it'll tx */ } }}static void dma_channel_release(struct omap_ep *ep){ int shift = 4 * (ep->dma_channel - 1); u16 mask = 0x0f << shift; struct omap_req *req; int active; /* abort any active usb transfer request */ if (!list_empty(&ep->queue)) req = container_of(ep->queue.next, struct omap_req, queue); else req = 0;#ifdef CONFIG_ARCH_OMAP24XX active = ((1 << 7) & readl(OMAP_DMA4_CCR_REG(ep->lch))) != 0;#else active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0;#endif DBG("%s release %s %cxdma%d %p\n", ep->ep.name, active ? "active" : "idle", (ep->bEndpointAddress & USB_DIR_IN) ? 't' : 'r', ep->dma_channel - 1, req); /* wait till current packet DMA finishes, and fifo empties */ if (ep->bEndpointAddress & USB_DIR_IN) { UDC_TXDMA_CFG_REG &= ~mask; if (req) { finish_in_dma(ep, req, -ECONNRESET); /* clear FIFO; hosts probably won't empty it */ use_ep(ep, UDC_EP_SEL); UDC_CTRL_REG = UDC_CLR_EP; deselect_ep(); } while (UDC_TXDMA_CFG_REG & mask) udelay(10); } else { UDC_RXDMA_CFG_REG &= ~mask; /* dma empties the fifo */ while (UDC_RXDMA_CFG_REG & mask) udelay(10); if (req) finish_out_dma(ep, req, -ECONNRESET); } omap_free_dma(ep->lch); ep->dma_channel = 0; ep->lch = -1; /* has_dma still set, till endpoint is fully quiesced */}/*-------------------------------------------------------------------------*/static int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -