162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * TUSB6010 USB 2.0 OTG Dual Role controller OMAP DMA interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Nokia Corporation 662306a36Sopenharmony_ci * Tony Lindgren <tony@atomide.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/usb.h> 1262306a36Sopenharmony_ci#include <linux/platform_device.h> 1362306a36Sopenharmony_ci#include <linux/dma-mapping.h> 1462306a36Sopenharmony_ci#include <linux/slab.h> 1562306a36Sopenharmony_ci#include <linux/dmaengine.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "musb_core.h" 1862306a36Sopenharmony_ci#include "tusb6010.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define to_chdat(c) ((struct tusb_omap_dma_ch *)(c)->private_data) 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci#define MAX_DMAREQ 5 /* REVISIT: Really 6, but req5 not OK */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_cistruct tusb_dma_data { 2562306a36Sopenharmony_ci s8 dmareq; 2662306a36Sopenharmony_ci struct dma_chan *chan; 2762306a36Sopenharmony_ci}; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistruct tusb_omap_dma_ch { 3062306a36Sopenharmony_ci struct musb *musb; 3162306a36Sopenharmony_ci void __iomem *tbase; 3262306a36Sopenharmony_ci unsigned long phys_offset; 3362306a36Sopenharmony_ci int epnum; 3462306a36Sopenharmony_ci u8 tx; 3562306a36Sopenharmony_ci struct musb_hw_ep *hw_ep; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci struct tusb_dma_data *dma_data; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci dma_addr_t dma_addr; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci u32 len; 4462306a36Sopenharmony_ci u16 packet_sz; 4562306a36Sopenharmony_ci u16 transfer_packet_sz; 4662306a36Sopenharmony_ci u32 transfer_len; 4762306a36Sopenharmony_ci u32 completed_len; 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistruct tusb_omap_dma { 5162306a36Sopenharmony_ci struct dma_controller controller; 5262306a36Sopenharmony_ci void __iomem *tbase; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci struct tusb_dma_data dma_pool[MAX_DMAREQ]; 5562306a36Sopenharmony_ci unsigned multichannel:1; 5662306a36Sopenharmony_ci}; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Allocate dmareq0 to the current channel unless it's already taken 6062306a36Sopenharmony_ci */ 6162306a36Sopenharmony_cistatic inline int tusb_omap_use_shared_dmareq(struct tusb_omap_dma_ch *chdat) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (reg != 0) { 6662306a36Sopenharmony_ci dev_dbg(chdat->musb->controller, "ep%i dmareq0 is busy for ep%i\n", 6762306a36Sopenharmony_ci chdat->epnum, reg & 0xf); 6862306a36Sopenharmony_ci return -EAGAIN; 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci if (chdat->tx) 7262306a36Sopenharmony_ci reg = (1 << 4) | chdat->epnum; 7362306a36Sopenharmony_ci else 7462306a36Sopenharmony_ci reg = chdat->epnum; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return 0; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic inline void tusb_omap_free_shared_dmareq(struct tusb_omap_dma_ch *chdat) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if ((reg & 0xf) != chdat->epnum) { 8662306a36Sopenharmony_ci printk(KERN_ERR "ep%i trying to release dmareq0 for ep%i\n", 8762306a36Sopenharmony_ci chdat->epnum, reg & 0xf); 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci } 9062306a36Sopenharmony_ci musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, 0); 9162306a36Sopenharmony_ci} 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* 9462306a36Sopenharmony_ci * See also musb_dma_completion in plat_uds.c and musb_g_[tx|rx]() in 9562306a36Sopenharmony_ci * musb_gadget.c. 9662306a36Sopenharmony_ci */ 9762306a36Sopenharmony_cistatic void tusb_omap_dma_cb(void *data) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct dma_channel *channel = (struct dma_channel *)data; 10062306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat = to_chdat(channel); 10162306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; 10262306a36Sopenharmony_ci struct musb *musb = chdat->musb; 10362306a36Sopenharmony_ci struct device *dev = musb->controller; 10462306a36Sopenharmony_ci struct musb_hw_ep *hw_ep = chdat->hw_ep; 10562306a36Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 10662306a36Sopenharmony_ci void __iomem *mbase = musb->mregs; 10762306a36Sopenharmony_ci unsigned long remaining, flags, pio; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci spin_lock_irqsave(&musb->lock, flags); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci dev_dbg(musb->controller, "ep%i %s dma callback\n", 11262306a36Sopenharmony_ci chdat->epnum, chdat->tx ? "tx" : "rx"); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (chdat->tx) 11562306a36Sopenharmony_ci remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET); 11662306a36Sopenharmony_ci else 11762306a36Sopenharmony_ci remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci remaining = TUSB_EP_CONFIG_XFR_SIZE(remaining); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci /* HW issue #10: XFR_SIZE may get corrupt on DMA (both async & sync) */ 12262306a36Sopenharmony_ci if (unlikely(remaining > chdat->transfer_len)) { 12362306a36Sopenharmony_ci dev_dbg(musb->controller, "Corrupt %s XFR_SIZE: 0x%08lx\n", 12462306a36Sopenharmony_ci chdat->tx ? "tx" : "rx", remaining); 12562306a36Sopenharmony_ci remaining = 0; 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci channel->actual_len = chdat->transfer_len - remaining; 12962306a36Sopenharmony_ci pio = chdat->len - channel->actual_len; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci dev_dbg(musb->controller, "DMA remaining %lu/%u\n", remaining, chdat->transfer_len); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Transfer remaining 1 - 31 bytes */ 13462306a36Sopenharmony_ci if (pio > 0 && pio < 32) { 13562306a36Sopenharmony_ci u8 *buf; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci dev_dbg(musb->controller, "Using PIO for remaining %lu bytes\n", pio); 13862306a36Sopenharmony_ci buf = phys_to_virt((u32)chdat->dma_addr) + chdat->transfer_len; 13962306a36Sopenharmony_ci if (chdat->tx) { 14062306a36Sopenharmony_ci dma_unmap_single(dev, chdat->dma_addr, 14162306a36Sopenharmony_ci chdat->transfer_len, 14262306a36Sopenharmony_ci DMA_TO_DEVICE); 14362306a36Sopenharmony_ci musb_write_fifo(hw_ep, pio, buf); 14462306a36Sopenharmony_ci } else { 14562306a36Sopenharmony_ci dma_unmap_single(dev, chdat->dma_addr, 14662306a36Sopenharmony_ci chdat->transfer_len, 14762306a36Sopenharmony_ci DMA_FROM_DEVICE); 14862306a36Sopenharmony_ci musb_read_fifo(hw_ep, pio, buf); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci channel->actual_len += pio; 15162306a36Sopenharmony_ci } 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!tusb_dma->multichannel) 15462306a36Sopenharmony_ci tusb_omap_free_shared_dmareq(chdat); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci channel->status = MUSB_DMA_STATUS_FREE; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci musb_dma_completion(musb, chdat->epnum, chdat->tx); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* We must terminate short tx transfers manually by setting TXPKTRDY. 16162306a36Sopenharmony_ci * REVISIT: This same problem may occur with other MUSB dma as well. 16262306a36Sopenharmony_ci * Easy to test with g_ether by pinging the MUSB board with ping -s54. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci if ((chdat->transfer_len < chdat->packet_sz) 16562306a36Sopenharmony_ci || (chdat->transfer_len % chdat->packet_sz != 0)) { 16662306a36Sopenharmony_ci u16 csr; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci if (chdat->tx) { 16962306a36Sopenharmony_ci dev_dbg(musb->controller, "terminating short tx packet\n"); 17062306a36Sopenharmony_ci musb_ep_select(mbase, chdat->epnum); 17162306a36Sopenharmony_ci csr = musb_readw(hw_ep->regs, MUSB_TXCSR); 17262306a36Sopenharmony_ci csr |= MUSB_TXCSR_MODE | MUSB_TXCSR_TXPKTRDY 17362306a36Sopenharmony_ci | MUSB_TXCSR_P_WZC_BITS; 17462306a36Sopenharmony_ci musb_writew(hw_ep->regs, MUSB_TXCSR, csr); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci spin_unlock_irqrestore(&musb->lock, flags); 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic int tusb_omap_dma_program(struct dma_channel *channel, u16 packet_sz, 18262306a36Sopenharmony_ci u8 rndis_mode, dma_addr_t dma_addr, u32 len) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat = to_chdat(channel); 18562306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma = chdat->tusb_dma; 18662306a36Sopenharmony_ci struct musb *musb = chdat->musb; 18762306a36Sopenharmony_ci struct device *dev = musb->controller; 18862306a36Sopenharmony_ci struct musb_hw_ep *hw_ep = chdat->hw_ep; 18962306a36Sopenharmony_ci void __iomem *mbase = musb->mregs; 19062306a36Sopenharmony_ci void __iomem *ep_conf = hw_ep->conf; 19162306a36Sopenharmony_ci dma_addr_t fifo_addr = hw_ep->fifo_sync; 19262306a36Sopenharmony_ci u32 dma_remaining; 19362306a36Sopenharmony_ci u16 csr; 19462306a36Sopenharmony_ci u32 psize; 19562306a36Sopenharmony_ci struct tusb_dma_data *dma_data; 19662306a36Sopenharmony_ci struct dma_async_tx_descriptor *dma_desc; 19762306a36Sopenharmony_ci struct dma_slave_config dma_cfg; 19862306a36Sopenharmony_ci enum dma_transfer_direction dma_dir; 19962306a36Sopenharmony_ci u32 port_window; 20062306a36Sopenharmony_ci int ret; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (unlikely(dma_addr & 0x1) || (len < 32) || (len > packet_sz)) 20362306a36Sopenharmony_ci return false; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci /* 20662306a36Sopenharmony_ci * HW issue #10: Async dma will eventually corrupt the XFR_SIZE 20762306a36Sopenharmony_ci * register which will cause missed DMA interrupt. We could try to 20862306a36Sopenharmony_ci * use a timer for the callback, but it is unsafe as the XFR_SIZE 20962306a36Sopenharmony_ci * register is corrupt, and we won't know if the DMA worked. 21062306a36Sopenharmony_ci */ 21162306a36Sopenharmony_ci if (dma_addr & 0x2) 21262306a36Sopenharmony_ci return false; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * Because of HW issue #10, it seems like mixing sync DMA and async 21662306a36Sopenharmony_ci * PIO access can confuse the DMA. Make sure XFR_SIZE is reset before 21762306a36Sopenharmony_ci * using the channel for DMA. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci if (chdat->tx) 22062306a36Sopenharmony_ci dma_remaining = musb_readl(ep_conf, TUSB_EP_TX_OFFSET); 22162306a36Sopenharmony_ci else 22262306a36Sopenharmony_ci dma_remaining = musb_readl(ep_conf, TUSB_EP_RX_OFFSET); 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci dma_remaining = TUSB_EP_CONFIG_XFR_SIZE(dma_remaining); 22562306a36Sopenharmony_ci if (dma_remaining) { 22662306a36Sopenharmony_ci dev_dbg(musb->controller, "Busy %s dma, not using: %08x\n", 22762306a36Sopenharmony_ci chdat->tx ? "tx" : "rx", dma_remaining); 22862306a36Sopenharmony_ci return false; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci chdat->transfer_len = len & ~0x1f; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci if (len < packet_sz) 23462306a36Sopenharmony_ci chdat->transfer_packet_sz = chdat->transfer_len; 23562306a36Sopenharmony_ci else 23662306a36Sopenharmony_ci chdat->transfer_packet_sz = packet_sz; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci dma_data = chdat->dma_data; 23962306a36Sopenharmony_ci if (!tusb_dma->multichannel) { 24062306a36Sopenharmony_ci if (tusb_omap_use_shared_dmareq(chdat) != 0) { 24162306a36Sopenharmony_ci dev_dbg(musb->controller, "could not get dma for ep%i\n", chdat->epnum); 24262306a36Sopenharmony_ci return false; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci if (dma_data->dmareq < 0) { 24562306a36Sopenharmony_ci /* REVISIT: This should get blocked earlier, happens 24662306a36Sopenharmony_ci * with MSC ErrorRecoveryTest 24762306a36Sopenharmony_ci */ 24862306a36Sopenharmony_ci WARN_ON(1); 24962306a36Sopenharmony_ci return false; 25062306a36Sopenharmony_ci } 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci chdat->packet_sz = packet_sz; 25462306a36Sopenharmony_ci chdat->len = len; 25562306a36Sopenharmony_ci channel->actual_len = 0; 25662306a36Sopenharmony_ci chdat->dma_addr = dma_addr; 25762306a36Sopenharmony_ci channel->status = MUSB_DMA_STATUS_BUSY; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* Since we're recycling dma areas, we need to clean or invalidate */ 26062306a36Sopenharmony_ci if (chdat->tx) { 26162306a36Sopenharmony_ci dma_dir = DMA_MEM_TO_DEV; 26262306a36Sopenharmony_ci dma_map_single(dev, phys_to_virt(dma_addr), len, 26362306a36Sopenharmony_ci DMA_TO_DEVICE); 26462306a36Sopenharmony_ci } else { 26562306a36Sopenharmony_ci dma_dir = DMA_DEV_TO_MEM; 26662306a36Sopenharmony_ci dma_map_single(dev, phys_to_virt(dma_addr), len, 26762306a36Sopenharmony_ci DMA_FROM_DEVICE); 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci memset(&dma_cfg, 0, sizeof(dma_cfg)); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Use 16-bit transfer if dma_addr is not 32-bit aligned */ 27362306a36Sopenharmony_ci if ((dma_addr & 0x3) == 0) { 27462306a36Sopenharmony_ci dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 27562306a36Sopenharmony_ci dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 27662306a36Sopenharmony_ci port_window = 8; 27762306a36Sopenharmony_ci } else { 27862306a36Sopenharmony_ci dma_cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 27962306a36Sopenharmony_ci dma_cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; 28062306a36Sopenharmony_ci port_window = 16; 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci fifo_addr = hw_ep->fifo_async; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dev_dbg(musb->controller, 28662306a36Sopenharmony_ci "ep%i %s dma: %pad len: %u(%u) packet_sz: %i(%i)\n", 28762306a36Sopenharmony_ci chdat->epnum, chdat->tx ? "tx" : "rx", &dma_addr, 28862306a36Sopenharmony_ci chdat->transfer_len, len, chdat->transfer_packet_sz, packet_sz); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci dma_cfg.src_addr = fifo_addr; 29162306a36Sopenharmony_ci dma_cfg.dst_addr = fifo_addr; 29262306a36Sopenharmony_ci dma_cfg.src_port_window_size = port_window; 29362306a36Sopenharmony_ci dma_cfg.src_maxburst = port_window; 29462306a36Sopenharmony_ci dma_cfg.dst_port_window_size = port_window; 29562306a36Sopenharmony_ci dma_cfg.dst_maxburst = port_window; 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci ret = dmaengine_slave_config(dma_data->chan, &dma_cfg); 29862306a36Sopenharmony_ci if (ret) { 29962306a36Sopenharmony_ci dev_err(musb->controller, "DMA slave config failed: %d\n", ret); 30062306a36Sopenharmony_ci return false; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci dma_desc = dmaengine_prep_slave_single(dma_data->chan, dma_addr, 30462306a36Sopenharmony_ci chdat->transfer_len, dma_dir, 30562306a36Sopenharmony_ci DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 30662306a36Sopenharmony_ci if (!dma_desc) { 30762306a36Sopenharmony_ci dev_err(musb->controller, "DMA prep_slave_single failed\n"); 30862306a36Sopenharmony_ci return false; 30962306a36Sopenharmony_ci } 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci dma_desc->callback = tusb_omap_dma_cb; 31262306a36Sopenharmony_ci dma_desc->callback_param = channel; 31362306a36Sopenharmony_ci dmaengine_submit(dma_desc); 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci dev_dbg(musb->controller, 31662306a36Sopenharmony_ci "ep%i %s using %i-bit %s dma from %pad to %pad\n", 31762306a36Sopenharmony_ci chdat->epnum, chdat->tx ? "tx" : "rx", 31862306a36Sopenharmony_ci dma_cfg.src_addr_width * 8, 31962306a36Sopenharmony_ci ((dma_addr & 0x3) == 0) ? "sync" : "async", 32062306a36Sopenharmony_ci (dma_dir == DMA_MEM_TO_DEV) ? &dma_addr : &fifo_addr, 32162306a36Sopenharmony_ci (dma_dir == DMA_MEM_TO_DEV) ? &fifo_addr : &dma_addr); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * Prepare MUSB for DMA transfer 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci musb_ep_select(mbase, chdat->epnum); 32762306a36Sopenharmony_ci if (chdat->tx) { 32862306a36Sopenharmony_ci csr = musb_readw(hw_ep->regs, MUSB_TXCSR); 32962306a36Sopenharmony_ci csr |= (MUSB_TXCSR_AUTOSET | MUSB_TXCSR_DMAENAB 33062306a36Sopenharmony_ci | MUSB_TXCSR_DMAMODE | MUSB_TXCSR_MODE); 33162306a36Sopenharmony_ci csr &= ~MUSB_TXCSR_P_UNDERRUN; 33262306a36Sopenharmony_ci musb_writew(hw_ep->regs, MUSB_TXCSR, csr); 33362306a36Sopenharmony_ci } else { 33462306a36Sopenharmony_ci csr = musb_readw(hw_ep->regs, MUSB_RXCSR); 33562306a36Sopenharmony_ci csr |= MUSB_RXCSR_DMAENAB; 33662306a36Sopenharmony_ci csr &= ~(MUSB_RXCSR_AUTOCLEAR | MUSB_RXCSR_DMAMODE); 33762306a36Sopenharmony_ci musb_writew(hw_ep->regs, MUSB_RXCSR, 33862306a36Sopenharmony_ci csr | MUSB_RXCSR_P_WZC_BITS); 33962306a36Sopenharmony_ci } 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Start DMA transfer */ 34262306a36Sopenharmony_ci dma_async_issue_pending(dma_data->chan); 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci if (chdat->tx) { 34562306a36Sopenharmony_ci /* Send transfer_packet_sz packets at a time */ 34662306a36Sopenharmony_ci psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); 34762306a36Sopenharmony_ci psize &= ~0x7ff; 34862306a36Sopenharmony_ci psize |= chdat->transfer_packet_sz; 34962306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_TX_OFFSET, 35262306a36Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); 35362306a36Sopenharmony_ci } else { 35462306a36Sopenharmony_ci /* Receive transfer_packet_sz packets at a time */ 35562306a36Sopenharmony_ci psize = musb_readl(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET); 35662306a36Sopenharmony_ci psize &= ~(0x7ff << 16); 35762306a36Sopenharmony_ci psize |= (chdat->transfer_packet_sz << 16); 35862306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_MAX_PACKET_SIZE_OFFSET, psize); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci musb_writel(ep_conf, TUSB_EP_RX_OFFSET, 36162306a36Sopenharmony_ci TUSB_EP_CONFIG_XFR_SIZE(chdat->transfer_len)); 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return true; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int tusb_omap_dma_abort(struct dma_channel *channel) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat = to_chdat(channel); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci if (chdat->dma_data) 37262306a36Sopenharmony_ci dmaengine_terminate_all(chdat->dma_data->chan); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci channel->status = MUSB_DMA_STATUS_FREE; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci return 0; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic inline int tusb_omap_dma_allocate_dmareq(struct tusb_omap_dma_ch *chdat) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci u32 reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); 38262306a36Sopenharmony_ci int i, dmareq_nr = -1; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci for (i = 0; i < MAX_DMAREQ; i++) { 38562306a36Sopenharmony_ci int cur = (reg & (0xf << (i * 5))) >> (i * 5); 38662306a36Sopenharmony_ci if (cur == 0) { 38762306a36Sopenharmony_ci dmareq_nr = i; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci if (dmareq_nr == -1) 39362306a36Sopenharmony_ci return -EAGAIN; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci reg |= (chdat->epnum << (dmareq_nr * 5)); 39662306a36Sopenharmony_ci if (chdat->tx) 39762306a36Sopenharmony_ci reg |= ((1 << 4) << (dmareq_nr * 5)); 39862306a36Sopenharmony_ci musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci chdat->dma_data = &chdat->tusb_dma->dma_pool[dmareq_nr]; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return 0; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic inline void tusb_omap_dma_free_dmareq(struct tusb_omap_dma_ch *chdat) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci u32 reg; 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci if (!chdat || !chdat->dma_data || chdat->dma_data->dmareq < 0) 41062306a36Sopenharmony_ci return; 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci reg = musb_readl(chdat->tbase, TUSB_DMA_EP_MAP); 41362306a36Sopenharmony_ci reg &= ~(0x1f << (chdat->dma_data->dmareq * 5)); 41462306a36Sopenharmony_ci musb_writel(chdat->tbase, TUSB_DMA_EP_MAP, reg); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci chdat->dma_data = NULL; 41762306a36Sopenharmony_ci} 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_cistatic struct dma_channel *dma_channel_pool[MAX_DMAREQ]; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic struct dma_channel * 42262306a36Sopenharmony_citusb_omap_dma_allocate(struct dma_controller *c, 42362306a36Sopenharmony_ci struct musb_hw_ep *hw_ep, 42462306a36Sopenharmony_ci u8 tx) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci int ret, i; 42762306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma; 42862306a36Sopenharmony_ci struct musb *musb; 42962306a36Sopenharmony_ci struct dma_channel *channel = NULL; 43062306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat = NULL; 43162306a36Sopenharmony_ci struct tusb_dma_data *dma_data = NULL; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci tusb_dma = container_of(c, struct tusb_omap_dma, controller); 43462306a36Sopenharmony_ci musb = tusb_dma->controller.musb; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci /* REVISIT: Why does dmareq5 not work? */ 43762306a36Sopenharmony_ci if (hw_ep->epnum == 0) { 43862306a36Sopenharmony_ci dev_dbg(musb->controller, "Not allowing DMA for ep0 %s\n", tx ? "tx" : "rx"); 43962306a36Sopenharmony_ci return NULL; 44062306a36Sopenharmony_ci } 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci for (i = 0; i < MAX_DMAREQ; i++) { 44362306a36Sopenharmony_ci struct dma_channel *ch = dma_channel_pool[i]; 44462306a36Sopenharmony_ci if (ch->status == MUSB_DMA_STATUS_UNKNOWN) { 44562306a36Sopenharmony_ci ch->status = MUSB_DMA_STATUS_FREE; 44662306a36Sopenharmony_ci channel = ch; 44762306a36Sopenharmony_ci chdat = ch->private_data; 44862306a36Sopenharmony_ci break; 44962306a36Sopenharmony_ci } 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci if (!channel) 45362306a36Sopenharmony_ci return NULL; 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci chdat->musb = tusb_dma->controller.musb; 45662306a36Sopenharmony_ci chdat->tbase = tusb_dma->tbase; 45762306a36Sopenharmony_ci chdat->hw_ep = hw_ep; 45862306a36Sopenharmony_ci chdat->epnum = hw_ep->epnum; 45962306a36Sopenharmony_ci chdat->completed_len = 0; 46062306a36Sopenharmony_ci chdat->tusb_dma = tusb_dma; 46162306a36Sopenharmony_ci if (tx) 46262306a36Sopenharmony_ci chdat->tx = 1; 46362306a36Sopenharmony_ci else 46462306a36Sopenharmony_ci chdat->tx = 0; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci channel->max_len = 0x7fffffff; 46762306a36Sopenharmony_ci channel->desired_mode = 0; 46862306a36Sopenharmony_ci channel->actual_len = 0; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (!chdat->dma_data) { 47162306a36Sopenharmony_ci if (tusb_dma->multichannel) { 47262306a36Sopenharmony_ci ret = tusb_omap_dma_allocate_dmareq(chdat); 47362306a36Sopenharmony_ci if (ret != 0) 47462306a36Sopenharmony_ci goto free_dmareq; 47562306a36Sopenharmony_ci } else { 47662306a36Sopenharmony_ci chdat->dma_data = &tusb_dma->dma_pool[0]; 47762306a36Sopenharmony_ci } 47862306a36Sopenharmony_ci } 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci dma_data = chdat->dma_data; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci dev_dbg(musb->controller, "ep%i %s dma: %s dmareq%i\n", 48362306a36Sopenharmony_ci chdat->epnum, 48462306a36Sopenharmony_ci chdat->tx ? "tx" : "rx", 48562306a36Sopenharmony_ci tusb_dma->multichannel ? "shared" : "dedicated", 48662306a36Sopenharmony_ci dma_data->dmareq); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return channel; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cifree_dmareq: 49162306a36Sopenharmony_ci tusb_omap_dma_free_dmareq(chdat); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci dev_dbg(musb->controller, "ep%i: Could not get a DMA channel\n", chdat->epnum); 49462306a36Sopenharmony_ci channel->status = MUSB_DMA_STATUS_UNKNOWN; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci return NULL; 49762306a36Sopenharmony_ci} 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_cistatic void tusb_omap_dma_release(struct dma_channel *channel) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat = to_chdat(channel); 50262306a36Sopenharmony_ci struct musb *musb = chdat->musb; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci dev_dbg(musb->controller, "Release for ep%i\n", chdat->epnum); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci channel->status = MUSB_DMA_STATUS_UNKNOWN; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci dmaengine_terminate_sync(chdat->dma_data->chan); 50962306a36Sopenharmony_ci tusb_omap_dma_free_dmareq(chdat); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci channel = NULL; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_civoid tusb_dma_controller_destroy(struct dma_controller *c) 51562306a36Sopenharmony_ci{ 51662306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma; 51762306a36Sopenharmony_ci int i; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci tusb_dma = container_of(c, struct tusb_omap_dma, controller); 52062306a36Sopenharmony_ci for (i = 0; i < MAX_DMAREQ; i++) { 52162306a36Sopenharmony_ci struct dma_channel *ch = dma_channel_pool[i]; 52262306a36Sopenharmony_ci if (ch) { 52362306a36Sopenharmony_ci kfree(ch->private_data); 52462306a36Sopenharmony_ci kfree(ch); 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Free up the DMA channels */ 52862306a36Sopenharmony_ci if (tusb_dma && tusb_dma->dma_pool[i].chan) 52962306a36Sopenharmony_ci dma_release_channel(tusb_dma->dma_pool[i].chan); 53062306a36Sopenharmony_ci } 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci kfree(tusb_dma); 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tusb_dma_controller_destroy); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int tusb_omap_allocate_dma_pool(struct tusb_omap_dma *tusb_dma) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct musb *musb = tusb_dma->controller.musb; 53962306a36Sopenharmony_ci int i; 54062306a36Sopenharmony_ci int ret = 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci for (i = 0; i < MAX_DMAREQ; i++) { 54362306a36Sopenharmony_ci struct tusb_dma_data *dma_data = &tusb_dma->dma_pool[i]; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* 54662306a36Sopenharmony_ci * Request DMA channels: 54762306a36Sopenharmony_ci * - one channel in case of non multichannel mode 54862306a36Sopenharmony_ci * - MAX_DMAREQ number of channels in multichannel mode 54962306a36Sopenharmony_ci */ 55062306a36Sopenharmony_ci if (i == 0 || tusb_dma->multichannel) { 55162306a36Sopenharmony_ci char ch_name[8]; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci sprintf(ch_name, "dmareq%d", i); 55462306a36Sopenharmony_ci dma_data->chan = dma_request_chan(musb->controller, 55562306a36Sopenharmony_ci ch_name); 55662306a36Sopenharmony_ci if (IS_ERR(dma_data->chan)) { 55762306a36Sopenharmony_ci dev_err(musb->controller, 55862306a36Sopenharmony_ci "Failed to request %s\n", ch_name); 55962306a36Sopenharmony_ci ret = PTR_ERR(dma_data->chan); 56062306a36Sopenharmony_ci goto dma_error; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci dma_data->dmareq = i; 56462306a36Sopenharmony_ci } else { 56562306a36Sopenharmony_ci dma_data->dmareq = -1; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci return 0; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cidma_error: 57262306a36Sopenharmony_ci for (; i >= 0; i--) { 57362306a36Sopenharmony_ci struct tusb_dma_data *dma_data = &tusb_dma->dma_pool[i]; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci if (dma_data->dmareq >= 0) 57662306a36Sopenharmony_ci dma_release_channel(dma_data->chan); 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return ret; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistruct dma_controller * 58362306a36Sopenharmony_citusb_dma_controller_create(struct musb *musb, void __iomem *base) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci void __iomem *tbase = musb->ctrl_base; 58662306a36Sopenharmony_ci struct tusb_omap_dma *tusb_dma; 58762306a36Sopenharmony_ci int i; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci /* REVISIT: Get dmareq lines used from board-*.c */ 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci musb_writel(musb->ctrl_base, TUSB_DMA_INT_MASK, 0x7fffffff); 59262306a36Sopenharmony_ci musb_writel(musb->ctrl_base, TUSB_DMA_EP_MAP, 0); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci musb_writel(tbase, TUSB_DMA_REQ_CONF, 59562306a36Sopenharmony_ci TUSB_DMA_REQ_CONF_BURST_SIZE(2) 59662306a36Sopenharmony_ci | TUSB_DMA_REQ_CONF_DMA_REQ_EN(0x3f) 59762306a36Sopenharmony_ci | TUSB_DMA_REQ_CONF_DMA_REQ_ASSER(2)); 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci tusb_dma = kzalloc(sizeof(struct tusb_omap_dma), GFP_KERNEL); 60062306a36Sopenharmony_ci if (!tusb_dma) 60162306a36Sopenharmony_ci goto out; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci tusb_dma->controller.musb = musb; 60462306a36Sopenharmony_ci tusb_dma->tbase = musb->ctrl_base; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci tusb_dma->controller.channel_alloc = tusb_omap_dma_allocate; 60762306a36Sopenharmony_ci tusb_dma->controller.channel_release = tusb_omap_dma_release; 60862306a36Sopenharmony_ci tusb_dma->controller.channel_program = tusb_omap_dma_program; 60962306a36Sopenharmony_ci tusb_dma->controller.channel_abort = tusb_omap_dma_abort; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (musb->tusb_revision >= TUSB_REV_30) 61262306a36Sopenharmony_ci tusb_dma->multichannel = 1; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci for (i = 0; i < MAX_DMAREQ; i++) { 61562306a36Sopenharmony_ci struct dma_channel *ch; 61662306a36Sopenharmony_ci struct tusb_omap_dma_ch *chdat; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci ch = kzalloc(sizeof(struct dma_channel), GFP_KERNEL); 61962306a36Sopenharmony_ci if (!ch) 62062306a36Sopenharmony_ci goto cleanup; 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci dma_channel_pool[i] = ch; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci chdat = kzalloc(sizeof(struct tusb_omap_dma_ch), GFP_KERNEL); 62562306a36Sopenharmony_ci if (!chdat) 62662306a36Sopenharmony_ci goto cleanup; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci ch->status = MUSB_DMA_STATUS_UNKNOWN; 62962306a36Sopenharmony_ci ch->private_data = chdat; 63062306a36Sopenharmony_ci } 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (tusb_omap_allocate_dma_pool(tusb_dma)) 63362306a36Sopenharmony_ci goto cleanup; 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci return &tusb_dma->controller; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cicleanup: 63862306a36Sopenharmony_ci musb_dma_controller_destroy(&tusb_dma->controller); 63962306a36Sopenharmony_ciout: 64062306a36Sopenharmony_ci return NULL; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(tusb_dma_controller_create); 643