int cpdma_chan_submit(struct cpdma_chan *chan, void *token, void *data, int len, int directed) { struct cpdma_ctlr *ctlr = chan->ctlr; struct cpdma_desc __iomem *desc; dma_addr_t buffer; unsigned long flags; u32 mode; int ret = 0; spin_lock_irqsave(&chan->lock, flags); if (chan->state == CPDMA_STATE_TEARDOWN) { ret = -EINVAL; goto unlock_ret; } desc = cpdma_desc_alloc(ctlr->pool, 1, is_rx_chan(chan)); if (!desc) { chan->stats.desc_alloc_fail++; ret = -ENOMEM; goto unlock_ret; } if (len < ctlr->params.min_packet_size) { len = ctlr->params.min_packet_size; chan->stats.runt_transmit_buff++; } buffer = dma_map_single(ctlr->dev, data, len, chan->dir); ret = dma_mapping_error(ctlr->dev, buffer); if (ret) { cpdma_desc_free(ctlr->pool, desc, 1); ret = -EINVAL; goto unlock_ret; } mode = CPDMA_DESC_OWNER | CPDMA_DESC_SOP | CPDMA_DESC_EOP; cpdma_desc_to_port(chan, mode, directed); desc_write(desc, hw_next, 0); desc_write(desc, hw_buffer, buffer); desc_write(desc, hw_len, len); desc_write(desc, hw_mode, mode | len); desc_write(desc, sw_token, token); desc_write(desc, sw_buffer, buffer); desc_write(desc, sw_len, len); __cpdma_chan_submit(chan, desc); if (chan->state == CPDMA_STATE_ACTIVE && chan->rxfree) chan_write(chan, rxfree, 1); chan->count++; unlock_ret: spin_unlock_irqrestore(&chan->lock, flags); return ret; }
struct cpdma_chan *cpdma_chan_create(struct cpdma_ctlr *ctlr, int chan_num, cpdma_handler_fn handler) { struct cpdma_chan *chan; int ret, offset = (chan_num % CPDMA_MAX_CHANNELS) * 4; unsigned long flags; if (__chan_linear(chan_num) >= ctlr->num_chan) return NULL; ret = -ENOMEM; chan = kzalloc(sizeof(*chan), GFP_KERNEL); if (!chan) goto err_chan_alloc; spin_lock_irqsave(&ctlr->lock, flags); ret = -EBUSY; if (ctlr->channels[chan_num]) goto err_chan_busy; chan->ctlr = ctlr; chan->state = CPDMA_STATE_IDLE; chan->chan_num = chan_num; chan->handler = handler; if (is_rx_chan(chan)) { chan->hdp = ctlr->params.rxhdp + offset; chan->cp = ctlr->params.rxcp + offset; chan->rxfree = ctlr->params.rxfree + offset; chan->int_set = CPDMA_RXINTMASKSET; chan->int_clear = CPDMA_RXINTMASKCLEAR; chan->td = CPDMA_RXTEARDOWN; chan->dir = DMA_FROM_DEVICE; } else { chan->hdp = ctlr->params.txhdp + offset; chan->cp = ctlr->params.txcp + offset; chan->int_set = CPDMA_TXINTMASKSET; chan->int_clear = CPDMA_TXINTMASKCLEAR; chan->td = CPDMA_TXTEARDOWN; chan->dir = DMA_TO_DEVICE; } chan->mask = BIT(chan_linear(chan)); spin_lock_init(&chan->lock); ctlr->channels[chan_num] = chan; spin_unlock_irqrestore(&ctlr->lock, flags); return chan; err_chan_busy: spin_unlock_irqrestore(&ctlr->lock, flags); kfree(chan); err_chan_alloc: return ERR_PTR(ret); }
int cpdma_chan_dump(struct cpdma_chan *chan) { unsigned long flags; struct device *dev = chan->ctlr->dev; spin_lock_irqsave(&chan->lock, flags); dev_info(dev, "channel %d (%s %d) state %s", chan->chan_num, is_rx_chan(chan) ? "rx" : "tx", chan_linear(chan), cpdma_state_str[chan->state]); dev_info(dev, "\thdp: %x\n", chan_read(chan, hdp)); dev_info(dev, "\tcp: %x\n", chan_read(chan, cp)); if (chan->rxfree) { dev_info(dev, "\trxfree: %x\n", chan_read(chan, rxfree)); } dev_info(dev, "\tstats head_enqueue: %d\n", chan->stats.head_enqueue); dev_info(dev, "\tstats tail_enqueue: %d\n", chan->stats.tail_enqueue); dev_info(dev, "\tstats pad_enqueue: %d\n", chan->stats.pad_enqueue); dev_info(dev, "\tstats misqueued: %d\n", chan->stats.misqueued); dev_info(dev, "\tstats desc_alloc_fail: %d\n", chan->stats.desc_alloc_fail); dev_info(dev, "\tstats pad_alloc_fail: %d\n", chan->stats.pad_alloc_fail); dev_info(dev, "\tstats runt_receive_buff: %d\n", chan->stats.runt_receive_buff); dev_info(dev, "\tstats runt_transmit_buff: %d\n", chan->stats.runt_transmit_buff); dev_info(dev, "\tstats empty_dequeue: %d\n", chan->stats.empty_dequeue); dev_info(dev, "\tstats busy_dequeue: %d\n", chan->stats.busy_dequeue); dev_info(dev, "\tstats good_dequeue: %d\n", chan->stats.good_dequeue); dev_info(dev, "\tstats requeue: %d\n", chan->stats.requeue); dev_info(dev, "\tstats teardown_dequeue: %d\n", chan->stats.teardown_dequeue); spin_unlock_irqrestore(&chan->lock, flags); return 0; }