Beispiel #1
0
/**
 * @brief See ENG-376.
 *
 * We use a mailbox notification from the SVC to solve a race condition
 * involving FCT transmission. When the SVC makes a connection, it sets all the
 * relevant DME parameters as defined in MIPI UniPro 1.6, then pokes the
 * bridge mailbox, telling it that it is safe to send FCTs on a given CPort.
 */
static int mailbox_evt(void)
{
    uint32_t cportid;
    int rc;
    uint32_t e2efc;
    uint32_t val;

    DBG_UNIPRO("mailbox interrupt received\n");

    /*
     * Figure out which CPort to turn on FCT. The desired CPort is always
     * the mailbox value - 1.
     */
    rc = unipro_attr_local_read(TSB_MAILBOX, &cportid, 0);
    if (rc) {
        return rc;
    }
    if (cportid >= cport_count) {
        DBG_UNIPRO("cportid %d in mailbox exceeds count of cports %d\n",
                   cportid, cport_count);
        return -EINVAL;
    }
    cportid--;

    rc = unipro_attr_local_read(T_CPORTFLAGS, &val, cportid);
    if (rc) {
        return rc;
    }
    if (val & CPORT_FLAGS_E2EFC) {
        DBG_UNIPRO("Enabling E2EFC on cport %u\n", cportid);
        if (cportid < 32) {
            e2efc = unipro_read(CPB_RX_E2EFC_EN_0);
            e2efc |= (1 << cportid);
            unipro_write(CPB_RX_E2EFC_EN_0, e2efc);
        } else if (cportid < 64) {
            e2efc = unipro_read(CPB_RX_E2EFC_EN_1);
            e2efc |= (1 << (cportid - 32));
            unipro_write(CPB_RX_E2EFC_EN_1, e2efc);
        }
    }

    configure_connected_cport(cportid);

    /* Acknowledge the mailbox write */
    rc = tsb_unipro_mbox_ack(cportid + 1);
    if (rc) {
        return rc;
    }

    return 0;
}
Beispiel #2
0
static void unipro_evt_handler(enum unipro_event evt)
{
    int retval;

    DBG_UNIPRO("UniPro: event %d.\n", evt);

    switch (evt) {
    case UNIPRO_EVT_MAILBOX:
        mailbox_evt();
        break;

    case UNIPRO_EVT_LUP_DONE:
        if (tsb_get_rev_id() == tsb_rev_es2)
            es2_apply_mphy_fixup();

        retval = unipro_enable_mailbox_irq();
        if (retval) {
            lowsyslog("unipro: failed to enable mailbox irq\n");
        }
        break;
    }

    if (evt_handler) {
        evt_handler(evt);
    }
}
Beispiel #3
0
/**
 * @brief           Send data down to a CPort
 * @return          number of bytes effectively sent (>= 0), or error code (< 0)
 * @param[in]       cportid: cport to send down
 * @param[in]       buf: data buffer
 * @param[in]       len: size of data to send
 * @param[in]       som: "start of message" flag
 */
static int unipro_send_sync(unsigned int cportid,
                            const void *buf, size_t len, bool som)
{
    struct cport *cport;
    uint16_t count;
    uint8_t *tx_buf;

    if (len > CPORT_BUF_SIZE) {
        return -EINVAL;
    }

    cport = cport_handle(cportid);
    if (!cport) {
        return -EINVAL;
    }

    if (!cport->connected) {
        lldbg("CP%d unconnected\n", cport->cportid);
        return -EPIPE;
    }

    DEBUGASSERT(TRANSFER_MODE == 2);

    /*
     * If this is not the start of a new message,
     * message data must be written to first address of CPort Tx Buffer + 1.
     */
    if (!som) {
        tx_buf = cport->tx_buf + sizeof(uint32_t);
    } else {
        tx_buf = cport->tx_buf;
    }

    count = unipro_get_tx_free_buffer_space(cport);
    if (!count) {
        /* No free space in TX FIFO, cannot send anything. */
        DBG_UNIPRO("No free space in CP%d Tx Buffer\n", cportid);
        return 0;
    } else if (count > len) {
        count = len;
    }
    /* Copy message data in CPort Tx FIFO */
    DBG_UNIPRO("Sending %u bytes to CP%d\n", count, cportid);
    memcpy(tx_buf, buf, count);

    return (int) count;
}
static int unipro_dma_xfer(struct unipro_xfer_descriptor *desc,
                           struct dma_channel *channel)
{
    int retval;
    size_t xfer_len;
    void *cport_buf;
    void *xfer_buf;

    xfer_len = unipro_get_tx_free_buffer_space(desc->cport);
    if (!xfer_len)
        return -ENOSPC;

    xfer_len = MIN(desc->remaining_xfer_len, xfer_len);

    desc->channel = channel;
    desc->dma_arg.unipro_tx_arg.next_transfer_len = xfer_len;
    if (xfer_len == desc->remaining_xfer_len)
        desc->dma_arg.unipro_tx_arg.eom_addr = CPORT_EOM_BIT(desc->cport);

    DBG_UNIPRO("xfer: chan=%u, len=%zu\n", channel->id, xfer_len);

    cport_buf = desc->cport->tx_buf;
    xfer_buf = (void*) desc->data;

    /* resuming a paused xfer */
    if (desc->remaining_xfer_len != desc->len) {
        cport_buf = (char*) cport_buf + 1; /* skip the start byte */

        /* move buffer offset to the beginning of the remaning bytes to xfer */
        xfer_buf = (char*) xfer_buf + (desc->len - desc->remaining_xfer_len);
    }

    retval = device_dma_transfer(unipro_dma.dev, channel->id, xfer_buf,
                                 cport_buf, xfer_len, &desc->dma_arg);
    if (retval) {
        lowsyslog("unipro: failed to start DMA transfer: %d\n", retval);
        return retval;
    }

    return 0;
}
Beispiel #5
0
/**
 * @brief RX EOM interrupt handler
 * @param irq irq number
 * @param context register context (unused)
 */
static int irq_rx_eom(int irq, void *context) {
    struct cport *cport = irqn_to_cport(irq);
    void *data = cport->rx_buf;
    uint32_t transferred_size;
    (void)context;
    void *newbuf;

    clear_rx_interrupt(cport);

    if (!cport->driver) {
        lldbg("dropping message on cport %hu where no driver is registered\n",
              cport->cportid);
        return -ENODEV;
    }

    transferred_size = unipro_read(CPB_RX_TRANSFERRED_DATA_SIZE_00 +
                                   (cport->cportid * sizeof(uint32_t)));
    DBG_UNIPRO("cport: %u driver: %s size=%u payload=0x%x\n",
                cport->cportid,
                cport->driver->name, transferred_size,
                data);

    if (cport->driver->rx_handler) {
        newbuf = unipro_rxbuf_alloc(cport->cportid);
        if (newbuf) {
            unipro_switch_rxbuf(cport->cportid, newbuf);
            unipro_unpause_rx(cport->cportid);
        } else {
            cport->switch_buf_on_free = true;
        }

        cport->driver->rx_handler(cport->cportid, data,
                                  (size_t)transferred_size);
    }

    return 0;
}
Beispiel #6
0
static int unipro_dma_xfer(struct unipro_xfer_descriptor *desc,
                           struct dma_channel *channel)
{
    int retval;
    size_t xfer_len;
    void *cport_buf;
    void *xfer_buf;
    struct device_dma_op *dma_op = NULL;

    if (tsb_get_rev_id() == tsb_rev_es2) {
        xfer_len = unipro_get_tx_free_buffer_space(desc->cport);
        if (!xfer_len)
            return -ENOSPC;

        xfer_len = MIN(desc->len - desc->data_offset, xfer_len);
    } else {
        DEBUGASSERT(desc->data_offset == 0);

        xfer_len = desc->len;
    }

    desc->channel = channel;
    retval = device_dma_op_alloc(unipro_dma.dev, 1, 0, &dma_op);
    if (retval != OK) {
        lowsyslog("unipro: failed allocate a DMA op, retval = %d.\n", retval);
        return retval;
    }

    dma_op->callback = (void *) unipro_dma_tx_callback;
    dma_op->callback_arg = desc;
    dma_op->callback_events = DEVICE_DMA_CALLBACK_EVENT_COMPLETE;
    if (tsb_get_rev_id() != tsb_rev_es2) {
       dma_op->callback_events |=  DEVICE_DMA_CALLBACK_EVENT_START;
    }
    dma_op->sg_count = 1;
    dma_op->sg[0].len = xfer_len;

    DBG_UNIPRO("xfer: chan=%u, len=%zu\n", channel->id, xfer_len);

    cport_buf = desc->cport->tx_buf;
    xfer_buf = (void*) desc->data;

    /* resuming a paused xfer */
    if (desc->data_offset != 0) {
        cport_buf = (char*) cport_buf + sizeof(uint64_t); /* skip the first DWORD */

        /* move buffer offset to the beginning of the remaning bytes to xfer */
        xfer_buf = (char*) xfer_buf + desc->data_offset;
    }

    dma_op->sg[0].src_addr = (off_t) xfer_buf;
    dma_op->sg[0].dst_addr = (off_t) cport_buf;

    desc->data_offset += xfer_len;

    retval = device_dma_enqueue(unipro_dma.dev, channel->chan, dma_op);
    if (retval) {
        lowsyslog("unipro: failed to start DMA transfer: %d\n", retval);
        return retval;
    }

    return 0;
}