int s3c_dma_started(struct s3c2410_dma_chan *chan) { unsigned long flags; local_irq_save(flags); dbg_showchan(chan); /* if we've only loaded one buffer onto the channel, then chec * to see if we have another, and if so, try and load it so when * the first buffer is finished, the new one will be loaded onto * the channel */ if (chan->next != NULL) { if (chan->load_state == S3C_DMALOAD_1LOADED) { if (s3c_dma_waitforload(chan, __LINE__) == 0) { pr_debug("%s: buff not yet loaded, no more todo\n", __FUNCTION__); } else { chan->load_state = S3C_DMALOAD_1RUNNING; s3c_dma_loadbuffer(chan, chan->next); } } else if (chan->load_state == S3C_DMALOAD_1RUNNING) { s3c_dma_loadbuffer(chan, chan->next); } } local_irq_restore(flags); return 0; }
static int s3c2410_dma_dostop(s3c2410_dma_chan_t *chan) { unsigned long tmp; unsigned long flags; pr_debug("%s:\n", __FUNCTION__); dbg_showchan(chan); local_irq_save(flags); s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); tmp |= S3C2410_DMASKTRIG_STOP; dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); #if 0 /* should also clear interrupts, according to WinCE BSP */ tmp = dma_rdreg(chan, S3C2410_DMA_DCON); tmp |= S3C2410_DCON_NORELOAD; dma_wrreg(chan, S3C2410_DMA_DCON, tmp); #endif chan->state = S3C2410_DMA_IDLE; chan->load_state = S3C2410_DMALOAD_NONE; local_irq_restore(flags); return 0; }
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev) { struct s3c2410_dma_chan *chan; unsigned long flags; pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", channel, client->name, dev); local_irq_save(flags); chan = s3c64xx_dma_map_channel(channel); if (chan == NULL) { local_irq_restore(flags); return -EBUSY; } dbg_showchan(chan); chan->client = client; chan->in_use = 1; chan->peripheral = channel; local_irq_restore(flags); /* need to setup */ pr_debug("%s: channel initialised, %p\n", __func__, chan); return chan->number | DMACH_LOW_LEVEL; }
static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan) { u32 config; int timeout; pr_debug("%s: stopping channel\n", __func__); dbg_showchan(chan); config = readl(chan->regs + PL080S_CH_CONFIG); config |= PL080_CONFIG_HALT; writel(config, chan->regs + PL080S_CH_CONFIG); timeout = 1000; do { config = readl(chan->regs + PL080S_CH_CONFIG); pr_debug("%s: %d - config %08x\n", __func__, timeout, config); if (config & PL080_CONFIG_ACTIVE) udelay(10); else break; } while (--timeout > 0); if (config & PL080_CONFIG_ACTIVE) { printk(KERN_ERR "%s: channel still active\n", __func__); return -EFAULT; } config = readl(chan->regs + PL080S_CH_CONFIG); config &= ~PL080_CONFIG_ENABLE; writel(config, chan->regs + PL080S_CH_CONFIG); return 0; }
static int s3c64xx_dma_flush(struct s3c2410_dma_chan *chan) { struct s3c64xx_dma_buff *buff, *next; u32 config; dbg_showchan(chan); pr_debug("%s: flushing channel\n", __func__); config = readl(chan->regs + PL080S_CH_CONFIG); config &= ~PL080_CONFIG_ENABLE; writel(config, chan->regs + PL080S_CH_CONFIG); /* dump all the buffers associated with this channel */ for (buff = chan->curr; buff != NULL; buff = next) { next = buff->next; pr_debug("%s: buff %p (next %p)\n", __func__, buff, buff->next); s3c64xx_dma_bufffdone(chan, buff, S3C2410_RES_ABORT); s3c64xx_dma_freebuff(buff); } chan->curr = chan->next = chan->end = NULL; return 0; }
static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) { unsigned long flags; unsigned long tmp; pr_debug("%s:\n", __func__); dbg_showchan(chan); local_irq_save(flags); s3c2410_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); tmp |= S3C2410_DMASKTRIG_STOP; //tmp &= ~S3C2410_DMASKTRIG_ON; dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); #if 0 /* should also clear interrupts, according to WinCE BSP */ tmp = dma_rdreg(chan, S3C2410_DMA_DCON); tmp |= S3C2410_DCON_NORELOAD; dma_wrreg(chan, S3C2410_DMA_DCON, tmp); #endif /* should stop do this, or should we wait for flush? */ chan->state = S3C2410_DMA_IDLE; chan->load_state = S3C2410_DMALOAD_NONE; local_irq_restore(flags); return 0; }
static int s3c_dma_dostop(struct s3c2410_dma_chan *chan) { unsigned long tmp; unsigned long flags; pr_debug("%s: DMA Channel No : %d\n", __FUNCTION__, chan->number); dbg_showchan(chan); local_irq_save(flags); //Commenting out this function call(as its causing freeze) and even without this it adheres to //ARM Primecell 080's disabling a DMA channel and losing data in the FIFO method //s3c_dma_flush_fifo(chan); s3c_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C_DMAC_CxCONFIGURATION); tmp &= ~S3C_DMACONFIG_CHANNEL_ENABLE; dma_wrreg(chan, S3C_DMAC_CxCONFIGURATION, tmp); pr_debug("%s: S3C_DMAC_CxCONFIGURATION : %08x\n", __FUNCTION__, tmp); chan->state = S3C_DMA_IDLE; chan->load_state = S3C_DMALOAD_NONE; local_irq_restore(flags); return 0; }
static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan) { struct s3c64xx_dmac *dmac = chan->dmac; u32 config; u32 bit = chan->bit; dbg_showchan(chan); pr_debug("%s: clearing interrupts\n", __func__); /* clear interrupts */ writel(bit, dmac->regs + PL080_TC_CLEAR); writel(bit, dmac->regs + PL080_ERR_CLEAR); pr_debug("%s: starting channel\n", __func__); config = readl(chan->regs + PL080S_CH_CONFIG); config |= PL080_CONFIG_ENABLE; config &= ~PL080_CONFIG_HALT; pr_debug("%s: writing config %08x\n", __func__, config); writel(config, chan->regs + PL080S_CH_CONFIG); return 0; }
static int s3c_dma_dostop(struct s3c2410_dma_chan *chan) { unsigned long tmp; unsigned long flags; pr_debug("%s: DMA Channel No : %d\n", __FUNCTION__, chan->number); dbg_showchan(chan); local_irq_save(flags); s3c_dma_flush_fifo(chan); s3c_dma_call_op(chan, S3C2410_DMAOP_STOP); tmp = dma_rdreg(chan, S3C_DMAC_CxCONFIGURATION); tmp &= ~S3C_DMACONFIG_CHANNEL_ENABLE; dma_wrreg(chan, S3C_DMAC_CxCONFIGURATION, tmp); pr_debug("%s: S3C_DMAC_CxCONFIGURATION : %08x\n", __FUNCTION__, tmp); chan->state = S3C_DMA_IDLE; chan->load_state = S3C_DMALOAD_NONE; local_irq_restore(flags); return 0; }
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev) { struct s3c2410_dma_chan *chan; unsigned long flags; int err; pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", channel, client->name, dev); local_irq_save(flags); chan = s3c2410_dma_map_channel(channel); if (chan == NULL) { local_irq_restore(flags); return -EBUSY; } dbg_showchan(chan); chan->client = client; chan->in_use = 1; if (!chan->irq_claimed) { pr_debug("dma%d: %s : requesting irq %d\n", channel, __func__, chan->irq); chan->irq_claimed = 1; local_irq_restore(flags); err = request_irq(chan->irq, s3c2410_dma_irq, IRQF_DISABLED, client->name, (void *)chan); local_irq_save(flags); if (err) { chan->in_use = 0; chan->irq_claimed = 0; local_irq_restore(flags); printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", client->name, chan->irq, chan->number); return err; } chan->irq_enabled = 1; } local_irq_restore(flags); /* need to setup */ pr_debug("%s: channel initialised, %p\n", __func__, chan); return chan->number | DMACH_LOW_LEVEL; }
int s3c2410_dma_request(unsigned int channel, s3c2410_dma_client_t *client, void *dev) { s3c2410_dma_chan_t *chan = &s3c2410_chans[channel]; unsigned long flags; int err; pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", channel, client->name, dev); check_channel(channel); local_irq_save(flags); dbg_showchan(chan); if (chan->in_use) { if (client != chan->client) { printk(KERN_ERR "dma%d: already in use\n", channel); local_irq_restore(flags); return -EBUSY; } else { printk(KERN_ERR "dma%d: client already has channel\n", channel); } } chan->client = client; chan->in_use = 1; if (!chan->irq_claimed) { pr_debug("dma%d: %s : requesting irq %d\n", channel, __FUNCTION__, chan->irq); err = request_irq(chan->irq, s3c2410_dma_irq, SA_INTERRUPT, client->name, (void *)chan); if (err) { chan->in_use = 0; local_irq_restore(flags); printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", client->name, chan->irq, chan->number); return err; } chan->irq_claimed = 1; chan->irq_enabled = 1; } local_irq_restore(flags); /* need to setup */ pr_debug("%s: channel initialised, %p\n", __FUNCTION__, chan); return 0; }
static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) { struct s3c2410_dma_buf *buf, *next; unsigned long flags; pr_debug("%s: chan %p (%d)\n", __func__, chan, chan->number); dbg_showchan(chan); local_irq_save(flags); if (chan->state != S3C2410_DMA_IDLE) { pr_debug("%s: stopping channel...\n", __func__ ); s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); } buf = chan->curr; if (buf == NULL) buf = chan->next; chan->curr = chan->next = chan->end = NULL; if (buf != NULL) { for ( ; buf != NULL; buf = next) { next = buf->next; pr_debug("%s: free buffer %p, next %p\n", __func__, buf, buf->next); s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); s3c2410_dma_freebuf(buf); } } dbg_showregs(chan); s3c2410_dma_waitforstop(chan); #if 0 /* should also clear interrupts, according to WinCE BSP */ { unsigned long tmp; tmp = dma_rdreg(chan, S3C2410_DMA_DCON); tmp |= S3C2410_DCON_NORELOAD; dma_wrreg(chan, S3C2410_DMA_DCON, tmp); } #endif dbg_showregs(chan); local_irq_restore(flags); return 0; }
static int s3c2410_dma_flush(struct s3c2410_dma_chan *chan) { struct s3c2410_dma_buf *buf, *next; unsigned long flags; pr_debug("%s: chan %p (%d)\n", __func__, chan, chan->number); dbg_showchan(chan); local_irq_save(flags); if (chan->state != S3C2410_DMA_IDLE) { pr_debug("%s: stopping channel...\n", __func__ ); s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); } buf = chan->curr; if (buf == NULL) buf = chan->next; chan->curr = chan->next = chan->end = NULL; if (buf != NULL) { for ( ; buf != NULL; buf = next) { next = buf->next; pr_debug("%s: free buffer %p, next %p\n", __func__, buf, buf->next); s3c2410_dma_buffdone(chan, buf, S3C2410_RES_ABORT); s3c2410_dma_freebuf(buf); } } dbg_showregs(chan); s3c2410_dma_waitforstop(chan); dbg_showregs(chan); local_irq_restore(flags); return 0; }
static int s3c_dma_dostop(struct s3c2410_dma_chan *chan) { unsigned long flags; pr_debug("%s: DMA Channel No : %d\n", __FUNCTION__, chan->number); dbg_showchan(chan); local_irq_save(flags); s3c_dma_call_op(chan, S3C2410_DMAOP_STOP); stop_DMA_channel(dma_regaddr(chan->dma_con, S3C_DMAC_DBGSTATUS), chan->number); chan->state = S3C_DMA_IDLE; chan->load_state = S3C_DMALOAD_NONE; local_irq_restore(flags); return 0; }
static irqreturn_t s3c2410_dma_irq(int irq, void *devpw) { struct s3c2410_dma_chan *chan = (struct s3c2410_dma_chan *)devpw; struct s3c2410_dma_buf *buf; buf = chan->curr; dbg_showchan(chan); /* modify the channel state */ switch (chan->load_state) { case S3C2410_DMALOAD_1RUNNING: /* TODO - if we are running only one buffer, we probably * want to reload here, and then worry about the buffer * callback */ chan->load_state = S3C2410_DMALOAD_NONE; break; case S3C2410_DMALOAD_1LOADED: /* iirc, we should go back to NONE loaded here, we * had a buffer, and it was never verified as being * loaded. */ chan->load_state = S3C2410_DMALOAD_NONE; break; case S3C2410_DMALOAD_1LOADED_1RUNNING: /* we'll worry about checking to see if another buffer is * ready after we've called back the owner. This should * ensure we do not wait around too long for the DMA * engine to start the next transfer */ chan->load_state = S3C2410_DMALOAD_1LOADED; break; case S3C2410_DMALOAD_NONE: printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", chan->number); break; default: printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", chan->number, chan->load_state); break; } if (buf != NULL) { /* update the chain to make sure that if we load any more * buffers when we call the callback function, things should * work properly */ chan->curr = buf->next; buf->next = NULL; if (buf->magic != BUF_MAGIC) { printk(KERN_ERR "dma%d: %s: buf %p incorrect magic\n", chan->number, __func__, buf); return IRQ_HANDLED; } s3c2410_dma_buffdone(chan, buf, S3C2410_RES_OK); /* free resouces */ s3c2410_dma_freebuf(buf); } else { } /* only reload if the channel is still running... our buffer done * routine may have altered the state by requesting the dma channel * to stop or shutdown... */ /* todo: check that when the channel is shut-down from inside this * function, we cope with unsetting reload, etc */ if (chan->next != NULL && chan->state != S3C2410_DMA_IDLE) { unsigned long flags; switch (chan->load_state) { case S3C2410_DMALOAD_1RUNNING: /* don't need to do anything for this state */ break; case S3C2410_DMALOAD_NONE: /* can load buffer immediately */ break; case S3C2410_DMALOAD_1LOADED: if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { /* flag error? */ printk(KERN_ERR "dma%d: timeout waiting for load (%s)\n", chan->number, __func__); return IRQ_HANDLED; } break; case S3C2410_DMALOAD_1LOADED_1RUNNING: goto no_load; default: printk(KERN_ERR "dma%d: unknown load_state in irq, %d\n", chan->number, chan->load_state); return IRQ_HANDLED; } local_irq_save(flags); s3c2410_dma_loadbuffer(chan, chan->next); local_irq_restore(flags); } else { s3c2410_dma_lastxfer(chan); /* see if we can stop this channel.. */ if (chan->load_state == S3C2410_DMALOAD_NONE) { pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", chan->number, jiffies); s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_STOP); } } no_load: return IRQ_HANDLED; }
int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size) { struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); struct s3c2410_dma_buf *buf; unsigned long flags; if (chan == NULL) return -EINVAL; pr_debug("%s: id=%p, data=%08x, size=%d\n", __func__, id, (unsigned int)data, size); buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); if (buf == NULL) { pr_debug("%s: out of memory (%ld alloc)\n", __func__, (long)sizeof(*buf)); return -ENOMEM; } //pr_debug("%s: new buffer %p\n", __func__, buf); //dbg_showchan(chan); buf->next = NULL; buf->data = buf->ptr = data; buf->size = size; buf->id = id; buf->magic = BUF_MAGIC; local_irq_save(flags); if (chan->curr == NULL) { /* we've got nothing loaded... */ pr_debug("%s: buffer %p queued onto empty channel\n", __func__, buf); chan->curr = buf; chan->end = buf; chan->next = NULL; } else { pr_debug("dma%d: %s: buffer %p queued onto non-empty channel\n", chan->number, __func__, buf); if (chan->end == NULL) pr_debug("dma%d: %s: %p not empty, and chan->end==NULL?\n", chan->number, __func__, chan); chan->end->next = buf; chan->end = buf; } /* if necessary, update the next buffer field */ if (chan->next == NULL) chan->next = buf; /* check to see if we can load a buffer */ if (chan->state == S3C2410_DMA_RUNNING) { if (chan->load_state == S3C2410_DMALOAD_1LOADED && 1) { if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { printk(KERN_ERR "dma%d: loadbuffer:" "timeout loading buffer\n", chan->number); dbg_showchan(chan); local_irq_restore(flags); return -EINVAL; } } while (s3c2410_dma_canload(chan) && chan->next != NULL) { s3c2410_dma_loadbuffer(chan, chan->next); } } else if (chan->state == S3C2410_DMA_IDLE) { if (chan->flags & S3C2410_DMAF_AUTOSTART) { s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, S3C2410_DMAOP_START); } } local_irq_restore(flags); return 0; }
static int s3c2410_dma_start(struct s3c2410_dma_chan *chan) { unsigned long tmp; unsigned long flags; pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); local_irq_save(flags); if (chan->state == S3C2410_DMA_RUNNING) { pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); local_irq_restore(flags); return 0; } chan->state = S3C2410_DMA_RUNNING; /* check wether there is anything to load, and if not, see * if we can find anything to load */ if (chan->load_state == S3C2410_DMALOAD_NONE) { if (chan->next == NULL) { printk(KERN_ERR "dma%d: channel has nothing loaded\n", chan->number); chan->state = S3C2410_DMA_IDLE; local_irq_restore(flags); return -EINVAL; } s3c2410_dma_loadbuffer(chan, chan->next); } dbg_showchan(chan); /* enable the channel */ if (!chan->irq_enabled) { enable_irq(chan->irq); chan->irq_enabled = 1; } /* start the channel going */ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); tmp &= ~S3C2410_DMASKTRIG_STOP; tmp |= S3C2410_DMASKTRIG_ON; dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); pr_debug("dma%d: %08lx to DMASKTRIG\n", chan->number, tmp); #if 0 /* the dma buffer loads should take care of clearing the AUTO * reloading feature */ tmp = dma_rdreg(chan, S3C2410_DMA_DCON); tmp &= ~S3C2410_DCON_NORELOAD; dma_wrreg(chan, S3C2410_DMA_DCON, tmp); #endif s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); dbg_showchan(chan); /* if we've only loaded one buffer onto the channel, then chec * to see if we have another, and if so, try and load it so when * the first buffer is finished, the new one will be loaded onto * the channel */ if (chan->next != NULL) { if (chan->load_state == S3C2410_DMALOAD_1LOADED) { if (s3c2410_dma_waitforload(chan, __LINE__) == 0) { pr_debug("%s: buff not yet loaded, no more todo\n", __func__); } else { chan->load_state = S3C2410_DMALOAD_1RUNNING; s3c2410_dma_loadbuffer(chan, chan->next); } } else if (chan->load_state == S3C2410_DMALOAD_1RUNNING) { s3c2410_dma_loadbuffer(chan, chan->next); } } local_irq_restore(flags); return 0; }
int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, void *dev) { struct s3c2410_dma_chan *chan; unsigned long flags; int err; pr_debug("DMA CH %d: s3c2410_request_dma: client=%s, dev=%p\n", channel, client->name, dev); local_irq_save(flags); chan = s3c_dma_map_channel(channel); if (chan == NULL) { local_irq_restore(flags); return -EBUSY; } dbg_showchan(chan); chan->client = client; chan->in_use = 1; chan->dma_con->in_use++; if (!chan->irq_claimed) { pr_debug("DMA CH %d: %s : requesting irq %d\n", channel, __FUNCTION__, chan->irq); chan->irq_claimed = 1; local_irq_restore(flags); err = request_irq(chan->irq, s3c_dma_irq, IRQF_SHARED | IRQF_DISABLED, client->name, (void *) chan->dma_con); local_irq_save(flags); if (err) { chan->in_use = 0; chan->irq_claimed = 0; chan->dma_con->in_use--; local_irq_restore(flags); printk(KERN_ERR "%s: cannot get IRQ %d for DMA %d\n", client->name, chan->irq, chan->number); return err; } chan->irq_enabled = 1; /* enable the main dma.. this can be disabled * when main channel use count is 0 */ s3c_enable_dmac(chan->dma_con->number); } s3c_clear_interrupts(chan->dma_con->number, chan->number); local_irq_restore(flags); /* need to setup */ pr_debug("%s: channel initialised, %p, number:%d, index:%d\n", __FUNCTION__, chan, chan->number, chan->index); return 0; }
static irqreturn_t s3c_dma_irq(int irq, void *devpw) { unsigned int channel = 0, dcon_num, i; unsigned long tmp; s3c_dma_controller_t *dma_controller = (s3c_dma_controller_t *) devpw; struct s3c2410_dma_chan *chan=NULL; struct s3c_dma_buf *buf; dcon_num = dma_controller->number; tmp = dma_rdreg(dma_controller, S3C_DMAC_INTSTATUS); pr_debug("# s3c_dma_irq: IRQ status : 0x%x\n", tmp); if (tmp == 0) return IRQ_HANDLED; for (i = 0; i < S3C_CHANNELS_PER_DMA; i++) { if (tmp & 0x01) { pr_debug("# DMAC %d: requestor %d\n", dcon_num, i); channel = i; chan = &s3c_dma_chans[channel + dcon_num * S3C_CHANNELS_PER_DMA]; pr_debug("# DMA CH:%d, index:%d load_state:%d\n", chan->number, chan->index, chan->load_state); buf = chan->curr; dbg_showchan(chan); /* modify the channel state */ switch (chan->load_state) { case S3C_DMALOAD_1RUNNING: /* TODO - if we are running only one buffer, we probably * want to reload here, and then worry about the buffer * callback */ chan->load_state = S3C_DMALOAD_NONE; break; case S3C_DMALOAD_1LOADED: /* iirc, we should go back to NONE loaded here, we * had a buffer, and it was never verified as being * loaded. */ chan->load_state = S3C_DMALOAD_NONE; break; case S3C_DMALOAD_1LOADED_1RUNNING: /* we'll worry about checking to see if another buffer is * ready after we've called back the owner. This should * ensure we do not wait around too long for the DMA * engine to start the next transfer */ chan->load_state = S3C_DMALOAD_1LOADED; break; case S3C_DMALOAD_NONE: printk(KERN_ERR "dma%d: IRQ with no loaded buffer?\n", chan->number); break; default: printk(KERN_ERR "dma%d: IRQ in invalid load_state %d\n", chan->number, chan->load_state); break; } if (buf != NULL) { /* update the chain to make sure that if we load any more * buffers when we call the callback function, things should * work properly */ chan->curr = buf->next; buf->next = NULL; if (buf->magic != BUF_MAGIC) { printk(KERN_ERR "dma CH %d: %s: buf %p incorrect magic\n", chan->number, __FUNCTION__, buf); goto next_channel; } s3c_dma_buffdone(chan, buf, S3C2410_RES_OK); /* free resouces */ s3c_dma_freebuf(buf); } else { } /* only reload if the channel is still running... our buffer done * routine may have altered the state by requesting the dma channel * to stop or shutdown... */ if (chan->next != NULL && chan->state != S3C_DMA_IDLE) { unsigned long flags; switch (chan->load_state) { case S3C_DMALOAD_1RUNNING: /* don't need to do anything for this state */ break; case S3C_DMALOAD_NONE: /* can load buffer immediately */ break; case S3C_DMALOAD_1LOADED: if (s3c_dma_waitforload(chan, __LINE__) == 0) { /* flag error? */ printk(KERN_ERR "dma CH %d: timeout waiting for load\n", chan->number); goto next_channel; } break; case S3C_DMALOAD_1LOADED_1RUNNING: goto next_channel; default: printk(KERN_ERR "dma CH %d: unknown load_state in irq, %d\n", chan->number, chan->load_state); goto next_channel; } local_irq_save(flags); s3c_dma_loadbuffer(chan, chan->next); start_DMA_channel(dma_regaddr(chan->dma_con, S3C_DMAC_DBGSTATUS), chan->number, chan->curr->mcptr, PL330_NON_SECURE_DMA); local_irq_restore(flags); } else { s3c_dma_lastxfer(chan); /* see if we can stop this channel.. */ if (chan->load_state == S3C_DMALOAD_NONE) { pr_debug("# DMA CH %d - (index:%d): end of transfer, stopping channel (%ld)\n", chan->number, chan->index, jiffies); s3c2410_dma_ctrl(chan->index | DMACH_LOW_LEVEL, S3C2410_DMAOP_STOP); } } s3c_clear_interrupts(chan->dma_con->number, chan->number); } next_channel: tmp >>= 1; } return IRQ_HANDLED; }
/* s3c2410_dma_enqueue * * queue an given buffer for dma transfer. * * id the device driver's id information for this buffer * data the physical address of the buffer data * size the size of the buffer in bytes * * If the channel is not running, then the flag S3C2410_DMAF_AUTOSTART * is checked, and if set, the channel is started. If this flag isn't set, * then an error will be returned. * * It is possible to queue more than one DMA buffer onto a channel at * once, and the code will deal with the re-loading of the next buffer * when necessary. */ int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size) { struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); struct s3c_dma_buf *buf; unsigned long flags; pr_debug("%s: id=%p, data=%08x, size=%d\n", __FUNCTION__, id, (unsigned int) data, size); buf = kmem_cache_alloc(dma_kmem, GFP_ATOMIC); if (buf == NULL) { printk(KERN_ERR "dma <%d> no memory for buffer\n", channel); return -ENOMEM; } pr_debug("%s: new buffer %p\n", __FUNCTION__, buf); buf->next = NULL; buf->data = buf->ptr = data; buf->size = size; buf->id = id; buf->magic = BUF_MAGIC; local_irq_save(flags); buf->mcptr_cpu = dma_alloc_coherent(NULL, SIZE_OF_MICRO_CODES, &buf->mcptr, GFP_ATOMIC); if (buf->mcptr_cpu == NULL) { printk(KERN_ERR "%s: failed to allocate memory for micro codes\n", __FUNCTION__); return -ENOMEM; } if (chan->curr == NULL) { /* we've got nothing loaded... */ pr_debug("%s: buffer %p queued onto empty channel\n", __FUNCTION__, buf); chan->curr = buf; chan->end = buf; chan->next = NULL; } else { pr_debug("dma CH %d: %s: buffer %p queued onto non-empty channel\n", chan->number, __FUNCTION__, buf); if (chan->end == NULL) /* In case of flushing */ pr_debug("dma CH %d: %s: %p not empty, and chan->end==NULL?\n", chan->number, __FUNCTION__, chan); else { chan->end->next = buf; chan->end = buf; } } /* if necessary, update the next buffer field */ if (chan->next == NULL) chan->next = buf; /* check to see if we can load a buffer */ if (chan->state == S3C_DMA_RUNNING) { if (chan->load_state == S3C_DMALOAD_1LOADED && 1) { if (s3c_dma_waitforload(chan, __LINE__) == 0) { printk(KERN_ERR "dma CH %d: loadbuffer:" "timeout loading buffer\n", chan->number); dbg_showchan(chan); local_irq_restore(flags); return -EINVAL; } } } else if (chan->state == S3C_DMA_IDLE) { if (chan->flags & S3C2410_DMAF_AUTOSTART) { s3c2410_dma_ctrl(channel, S3C2410_DMAOP_START); } else { pr_debug("loading onto stopped channel\n"); } } local_irq_restore(flags); return 0; }
int s3c2410_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size) { struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); struct s3c64xx_dma_buff *next; struct s3c64xx_dma_buff *buff; struct pl080s_lli *lli; int ret; WARN_ON(!chan); if (!chan) return -EINVAL; buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_KERNEL); if (!buff) { printk(KERN_ERR "%s: no memory for buffer\n", __func__); return -ENOMEM; } lli = dma_pool_alloc(dma_pool, GFP_KERNEL, &buff->lli_dma); if (!lli) { printk(KERN_ERR "%s: no memory for lli\n", __func__); ret = -ENOMEM; goto err_buff; } pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n", __func__, buff, data, lli, (u32)buff->lli_dma, size); buff->lli = lli; buff->pw = id; s3c64xx_dma_fill_lli(chan, lli, data, size); if ((next = chan->next) != NULL) { struct s3c64xx_dma_buff *end = chan->end; struct pl080s_lli *endlli = end->lli; pr_debug("enquing onto channel\n"); end->next = buff; endlli->next_lli = buff->lli_dma; if (chan->flags & S3C2410_DMAF_CIRCULAR) { struct s3c64xx_dma_buff *curr = chan->curr; lli->next_lli = curr->lli_dma; } if (next == chan->curr) { writel(buff->lli_dma, chan->regs + PL080_CH_LLI); chan->next = buff; } show_lli(endlli); chan->end = buff; } else { pr_debug("enquing onto empty channel\n"); chan->curr = buff; chan->next = buff; chan->end = buff; s3c64xx_lli_to_regs(chan, lli); } show_lli(lli); dbg_showchan(chan); dbg_showbuffs(chan); return 0; err_buff: kfree(buff); return ret; }
static int s3c2410_dma_start(s3c2410_dma_chan_t *chan) { unsigned long tmp; unsigned long flags; pr_debug("s3c2410_start_dma: channel=%d\n", chan->number); local_irq_save(flags); if (chan->state == S3C2410_DMA_RUNNING) { pr_debug("s3c2410_start_dma: already running (%d)\n", chan->state); local_irq_restore(flags); return 0; } chan->state = S3C2410_DMA_RUNNING; /* check wether there is anything to load, and if not, see * if we can find anything to load */ if (chan->load_state == S3C2410_DMALOAD_NONE) { if (chan->next == NULL) { printk(KERN_ERR "dma%d: channel has nothing loaded\n", chan->number); chan->state = S3C2410_DMA_IDLE; local_irq_restore(flags); return -EINVAL; } s3c2410_dma_loadbuffer(chan, chan->next); } dbg_showchan(chan); /* enable the channel */ if (!chan->irq_enabled) { enable_irq(chan->irq); chan->irq_enabled = 1; } /* start the channel going */ tmp = dma_rdreg(chan, S3C2410_DMA_DMASKTRIG); tmp &= ~S3C2410_DMASKTRIG_STOP; tmp |= S3C2410_DMASKTRIG_ON; dma_wrreg(chan, S3C2410_DMA_DMASKTRIG, tmp); pr_debug("wrote %08lx to DMASKTRIG\n", tmp); #if 0 /* the dma buffer loads should take care of clearing the AUTO * reloading feature */ tmp = dma_rdreg(chan, S3C2410_DMA_DCON); tmp &= ~S3C2410_DCON_NORELOAD; dma_wrreg(chan, S3C2410_DMA_DCON, tmp); #endif s3c2410_dma_call_op(chan, S3C2410_DMAOP_START); dbg_showchan(chan); local_irq_restore(flags); return 0; }
int imapx200_dma_enqueue(unsigned int channel, void *id, dma_addr_t data, int size) { struct imapx200_dma_chan *chan = imap_dma_lookup_channel(channel); struct imapx200_dma_buff *next; struct imapx200_dma_buff *buff; struct dw_lli *lli; int ret; WARN_ON(!chan); if (!chan) return -EINVAL; buff = kzalloc(sizeof(struct imapx200_dma_buff), GFP_KERNEL); if (!buff) { printk(KERN_ERR "%s: no memory for buffer\n", __func__); return -ENOMEM; } lli = dma_pool_alloc(dma_pool, GFP_KERNEL, &buff->lli_dma); if (!lli) { printk(KERN_ERR "%s: no memory for lli\n", __func__); ret = -ENOMEM; goto err_buff; } pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n", __func__, buff, data, lli, (u32)buff->lli_dma, size); buff->lli = lli; buff->pw = id; imapx200_dma_fill_lli(chan, lli, data, size); if ((next = chan->next) != NULL) { struct imapx200_dma_buff *end = chan->end; struct dw_lli *endlli = end->lli; pr_debug("enquing onto channel\n"); end->next = buff; endlli->llp = buff->lli_dma; if (chan->flags ) { struct imapx200_dma_buff *curr = chan->curr; lli->llp = curr->lli_dma; } if (next == chan->curr) { chan_writel(chan,LLP,buff->lli_dma); chan->next = buff; } chan->end = buff; } else { pr_debug("enquing onto empty channel\n"); chan->curr = buff; chan->next = buff; chan->end = buff; imapx200_lli_to_regs(chan, lli); } dbg_showchan(chan); return 0; err_buff: kfree(buff); return ret; }