in bcm-flexrm-mailbox.c [985:1082]
static int flexrm_new_request(struct flexrm_ring *ring,
struct brcm_message *batch_msg,
struct brcm_message *msg)
{
void *next;
unsigned long flags;
u32 val, count, nhcnt;
u32 read_offset, write_offset;
bool exit_cleanup = false;
int ret = 0, reqid;
/* Do sanity check on message */
if (!flexrm_sanity_check(msg))
return -EIO;
msg->error = 0;
/* If no requests possible then save data pointer and goto done. */
spin_lock_irqsave(&ring->lock, flags);
reqid = bitmap_find_free_region(ring->requests_bmap,
RING_MAX_REQ_COUNT, 0);
spin_unlock_irqrestore(&ring->lock, flags);
if (reqid < 0)
return -ENOSPC;
ring->requests[reqid] = msg;
/* Do DMA mappings for the message */
ret = flexrm_dma_map(ring->mbox->dev, msg);
if (ret < 0) {
ring->requests[reqid] = NULL;
spin_lock_irqsave(&ring->lock, flags);
bitmap_release_region(ring->requests_bmap, reqid, 0);
spin_unlock_irqrestore(&ring->lock, flags);
return ret;
}
/* Determine current HW BD read offset */
read_offset = readl_relaxed(ring->regs + RING_BD_READ_PTR);
val = readl_relaxed(ring->regs + RING_BD_START_ADDR);
read_offset *= RING_DESC_SIZE;
read_offset += (u32)(BD_START_ADDR_DECODE(val) - ring->bd_dma_base);
/*
* Number required descriptors = number of non-header descriptors +
* number of header descriptors +
* 1x null descriptor
*/
nhcnt = flexrm_estimate_nonheader_desc_count(msg);
count = flexrm_estimate_header_desc_count(nhcnt) + nhcnt + 1;
/* Check for available descriptor space. */
write_offset = ring->bd_write_offset;
while (count) {
if (!flexrm_is_next_table_desc(ring->bd_base + write_offset))
count--;
write_offset += RING_DESC_SIZE;
if (write_offset == RING_BD_SIZE)
write_offset = 0x0;
if (write_offset == read_offset)
break;
}
if (count) {
ret = -ENOSPC;
exit_cleanup = true;
goto exit;
}
/* Write descriptors to ring */
next = flexrm_write_descs(msg, nhcnt, reqid,
ring->bd_base + ring->bd_write_offset,
RING_BD_TOGGLE_VALID(ring->bd_write_offset),
ring->bd_base, ring->bd_base + RING_BD_SIZE);
if (IS_ERR(next)) {
ret = PTR_ERR(next);
exit_cleanup = true;
goto exit;
}
/* Save ring BD write offset */
ring->bd_write_offset = (unsigned long)(next - ring->bd_base);
/* Increment number of messages sent */
atomic_inc_return(&ring->msg_send_count);
exit:
/* Update error status in message */
msg->error = ret;
/* Cleanup if we failed */
if (exit_cleanup) {
flexrm_dma_unmap(ring->mbox->dev, msg);
ring->requests[reqid] = NULL;
spin_lock_irqsave(&ring->lock, flags);
bitmap_release_region(ring->requests_bmap, reqid, 0);
spin_unlock_irqrestore(&ring->lock, flags);
}
return ret;
}