static int imapx200_dma_stop(struct imapx200_dma_chan *chan) { struct imapx200_dmac *dmac= chan->dmac; u32 config; int timeout; pr_debug("%s: stopping channel\n", __func__); config = chan_readl(chan,CFG_LO); config |= DWC_CFGL_CH_SUSP; chan_writel(chan,CFG_LO,config); timeout = 1000; do { config = chan_readl(chan,CFG_LO); pr_debug("%s: %d - config %08x\n", __func__, timeout, config); if (config & DWC_CFGL_CH_SUSP) udelay(10); else break; } while (--timeout > 0); if (config & DWC_CFGL_CH_SUSP) { printk(KERN_ERR "%s: channel still active\n", __func__); return -EFAULT; } dma_clear_bit(dmac,CH_EN,chan->bit); return 0; }
static void dbg_showchan(struct imapx200_dma_chan *chan) { pr_debug("DMA%d: %08x->%08x L %08x CTL_L0 %08x, CTL_H0%08x \n", chan->number, chan_readl(chan,SAR), chan_readl(chan,DAR), chan_readl(chan,LLP), chan_readl(chan,CTL_LO), chan_readl(chan,CTL_HI) ); }
static inline void dma_chan_irq(struct hsu_dma_chan *chan) { struct uart_hsu_port *up = chan->uport; unsigned long flags; u32 int_sts; spin_lock_irqsave(&up->port.lock, flags); if (!up->use_dma || !up->running) goto exit; /* * No matter what situation, need read clear the IRQ status * There is a bug, see Errata 5, HSD 2900918 */ int_sts = chan_readl(chan, HSU_CH_SR); /* Rx channel */ if (chan->dirt == DMA_FROM_DEVICE) hsu_dma_rx(up, int_sts); /* Tx channel */ if (chan->dirt == DMA_TO_DEVICE) { chan_writel(chan, HSU_CH_CR, 0x0); up->dma_tx_on = 0; hsu_dma_tx(up); } exit: spin_unlock_irqrestore(&up->port.lock, flags); return; }
/* This is always called in spinlock protected mode, so * modify timeout timer is safe here */ void hsu_dma_rx(struct uart_hsu_port *up, u32 int_sts) { struct hsu_dma_buffer *dbuf = &up->rxbuf; struct hsu_dma_chan *chan = up->rxc; struct uart_port *port = &up->port; struct tty_struct *tty = port->state->port.tty; int count; if (!tty) return; /* * First need to know how many is already transferred, * then check if its a timeout DMA irq, and return * the trail bytes out, push them up and reenable the * channel */ /* Timeout IRQ, need wait some time, see Errata 2 */ if (int_sts & 0xf00) udelay(2); /* Stop the channel */ chan_writel(chan, HSU_CH_CR, 0x0); count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; if (!count) { /* Restart the channel before we leave */ chan_writel(chan, HSU_CH_CR, 0x3); return; } dma_sync_single_for_cpu(port->dev, dbuf->dma_addr, dbuf->dma_size, DMA_FROM_DEVICE); /* * Head will only wrap around when we recycle * the DMA buffer, and when that happens, we * explicitly set tail to 0. So head will * always be greater than tail. */ tty_insert_flip_string(tty, dbuf->buf, count); port->icount.rx += count; dma_sync_single_for_device(up->port.dev, dbuf->dma_addr, dbuf->dma_size, DMA_FROM_DEVICE); /* Reprogram the channel */ chan_writel(chan, HSU_CH_D0SAR, dbuf->dma_addr); chan_writel(chan, HSU_CH_D0TSR, dbuf->dma_size); chan_writel(chan, HSU_CH_DCR, 0x1 | (0x1 << 8) | (0x1 << 16) | (0x1 << 24) /* timeout bit, see HSU Errata 1 */ ); tty_flip_buffer_push(tty); chan_writel(chan, HSU_CH_CR, 0x3); }
int imapx200_dma_getposition(unsigned int channel, dma_addr_t *src, dma_addr_t *dst) { struct imapx200_dma_chan *chan = imap_dma_lookup_channel(channel); WARN_ON(!chan); if (!chan) return -EINVAL; if (src != NULL) *src = chan_readl(chan,SAR); if (dst != NULL) *dst = chan_readl(chan,DAR); return 0; }
static void hsu_dma_rx_timeout(unsigned long data) { struct hsu_dma_chan *chan = (void *)data; struct uart_hsu_port *up = chan->uport; struct hsu_dma_buffer *dbuf = &up->rxbuf; int count = 0; unsigned long flags; spin_lock_irqsave(&up->port.lock, flags); count = chan_readl(chan, HSU_CH_D0SAR) - dbuf->dma_addr; if (!count) { mod_timer(&chan->rx_timer, jiffies + HSU_DMA_TIMEOUT_CHECK_FREQ); goto exit; } hsu_dma_rx(up, 0); exit: spin_unlock_irqrestore(&up->port.lock, flags); }
static void imapx200_dma_fill_lli(struct imapx200_dma_chan *chan, struct dw_lli *lli, dma_addr_t data, int size) { dma_addr_t src, dst; u32 control0, control1; control0 = chan_readl(chan,CTL_LO); switch (chan->source) { case IMAPX200_DMA_P2M: src = chan->dev_addr; dst = data; control0 |= DWC_CTLL_DST_WIDTH(chan->hw_width); control0 |= DWC_CTLL_SRC_WIDTH(2); control0 |= DWC_CTLL_DST_INC; break; case IMAPX200_DMA_M2P: src = data; dst = chan->dev_addr; control0 |= DWC_CTLL_DST_WIDTH(2); control0 |= DWC_CTLL_SRC_WIDTH(chan->hw_width); control0 |= DWC_CTLL_SRC_INC; break; default: BUG(); } /* note, we do not currently setup any of the burst controls */ control1 = size >> chan->hw_width; /* size in no of xfers */ lli->sar = src; lli->dar = dst; lli->llp = 0; lli->ctllo = control0; lli->ctlhi = control1; }
static ssize_t dma_show_regs(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { struct hsu_dma_chan *chan = file->private_data; char *buf; u32 len = 0; ssize_t ret; buf = kzalloc(HSU_REGS_BUFSIZE, GFP_KERNEL); if (!buf) return 0; len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "MFD HSU DMA channel [%d] regs:\n", chan->id); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "=================================\n"); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "CR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_CR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "DCR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_DCR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "BSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_BSR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "MOTSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_MOTSR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0SAR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D0TSR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1SAR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D1TSR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2SAR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D2TSR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0SAR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3SAR)); len += snprintf(buf + len, HSU_REGS_BUFSIZE - len, "D0TSR: \t\t0x%08x\n", chan_readl(chan, HSU_CH_D3TSR)); if (len > HSU_REGS_BUFSIZE) len = HSU_REGS_BUFSIZE; ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); kfree(buf); return ret; }
int imapx200_dma_devconfig(int channel, enum imapx200_dmafc source, unsigned long devaddr) { struct imapx200_dma_chan *chan = imap_dma_lookup_channel(channel); u32 peripheral; u32 ctlx = 0; u32 intrx = 0; u32 cfg_hi = 0; u32 cfg_lo = 0; pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n", __func__, channel, source, devaddr, chan); WARN_ON(!chan); if (!chan) return -EINVAL; peripheral = (chan->peripheral & 0x7); chan->source = source; chan->dev_addr = devaddr; pr_debug("%s: peripheral %d\n", __func__, peripheral); ctlx = chan_readl(chan,CTL_LO); pr_debug("devconfig_1\n"); ctlx &= ~DWC_CTLL_FC_MASK; cfg_hi = chan_readl(chan,CFG_HI); pr_debug("CFG_HI is %x\n", cfg_hi); cfg_lo = chan_readl(chan,CFG_LO); pr_debug("devconfig_3\n"); switch (source) { case IMAPX200_DMA_M2M: ctlx |= DWC_CTLL_FC_M2M; break; case IMAPX200_DMA_M2P: ctlx |= DWC_CTLL_FC_M2P; cfg_lo &= ~DWC_CFGL_HS_DST; cfg_hi |= DWC_CFGH_DST_PER(chan->client->handshake); break; case IMAPX200_DMA_P2M: ctlx |= DWC_CTLL_FC_P2M; cfg_lo &= ~DWC_CFGL_HS_SRC; cfg_hi |= DWC_CFGH_SRC_PER(chan->client->handshake); break; default: printk(KERN_ERR "%s: bad source\n", __func__); return -EINVAL; } /*set dma flow control bit*/ chan_writel(chan,CTL_LO,ctlx); pr_debug("devconfig_4\n"); chan_writel(chan,CFG_LO,cfg_lo); pr_debug("devconfig_5\n"); chan_writel(chan,CFG_HI,cfg_hi); // cfg_hi = chan_readl(chan,CFG_HI); // pr_debug("CFG_HI is %x\n", cfg_hi); /* allow TC and ERR interrupts */ intrx = 1<<(chan->number); pr_debug("devconfig_6\n"); dma_set_bit(chan->dmac,MASK.XFER,intrx); pr_debug("devconfig_7\n"); dma_set_bit(chan->dmac,MASK.BLOCK,intrx); dma_set_bit(chan->dmac,MASK.ERROR,intrx); pr_debug("devconfig_8\n"); return 0; }