static struct unipro_xfer_descriptor *pick_tx_descriptor(void) { struct unipro_xfer_descriptor *desc; int i; for (i = 0; i < unipro_cport_count(); i++) { struct cport *cport = cport_handle(i); if (!cport) continue; if (list_is_empty(&cport->tx_fifo)) continue; desc = containerof(cport->tx_fifo.next, struct unipro_xfer_descriptor, list); if (desc->channel) continue; if (!unipro_get_tx_free_buffer_space(desc->cport)) continue; return desc; } return NULL; }
/** * @brief send data down a CPort * @param cportid cport to send down * @param buf data buffer * @param len size of data to send * @param 0 on success, <0 on error */ int unipro_send(unsigned int cportid, const void *buf, size_t len) { int ret, sent; bool som; struct cport *cport; if (len > CPORT_BUF_SIZE) { return -EINVAL; } cport = cport_handle(cportid); if (!cport) { return -EINVAL; } if (cport->pending_reset) { return -EPIPE; } for (som = true, sent = 0; sent < len;) { ret = unipro_send_sync(cportid, buf + sent, len - sent, som); if (ret < 0) { return ret; } else if (ret == 0) { continue; } sent += ret; som = false; } unipro_set_eom_flag(cport); return 0; }
/** * @brief Send data buffer(s) on CPort whenever ready. * Ensure that TX queues are reinspected until * all CPorts have no work available. * Then suspend again until new data is available. */ static void *unipro_tx_worker(void *data) { int i; bool is_busy; int retval; unsigned int cport_count = unipro_cport_count(); while (1) { /* Block until a buffer is pending on any CPort */ sem_wait(&worker.tx_fifo_lock); do { is_busy = false; for (i = 0; i < cport_count; i++) { /* Browse all CPorts sending any pending buffers */ retval = unipro_send_tx_buffer(cport_handle(i)); if (retval == -EBUSY) { /* * Buffer only partially sent, have to try again for * remaining part. */ is_busy = true; } } } while (is_busy); /* exit when CPort(s) current pending buffer sent */ } return NULL; }
int unipro_driver_unregister(unsigned int cportid) { struct cport *cport = cport_handle(cportid); if (!cport) { return -ENODEV; } cport->driver = NULL; return 0; }
/** * @brief Enable EOM interrupt on cport */ static void enable_int(unsigned int cportid) { struct cport *cport; unsigned int irqn; cport = cport_handle(cportid); if (!cport) { return; } irqn = cportid_to_irqn(cportid); enable_rx_interrupt(cport); irq_attach(irqn, irq_rx_eom); up_enable_irq(irqn); }
int unipro_unpause_rx(unsigned int cportid) { struct cport *cport; cport = cport_handle(cportid); if (!cport || !cport->connected) { return -EINVAL; } /* Restart the flow of received data */ unipro_write(REG_RX_PAUSE_SIZE_00 + (cport->cportid * sizeof(uint32_t)), (1 << 31) | CPORT_BUF_SIZE); return 0; }
void unipro_switch_rxbuf(unsigned int cportid, void *buffer) { uint32_t ahm_address = AHM_ADDRESS_00; struct cport *cport = cport_handle(cportid); if (!cport) return; if (cportid < CPORTID_CDSI0) ahm_address += (cportid * sizeof(uint32_t)); else ahm_address += ((cportid - 2) * sizeof(uint32_t)); cport->rx_buf = buffer; unipro_write(ahm_address, (uint32_t) buffer); }
/** * @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; }
/** * @brief send data over UniPro asynchronously (not blocking) * @return 0 on success, <0 otherwise * @param[in] cportid: target CPort ID * @param[in] buf: data buffer * @param[in] len: data buffer length (in bytes) * @param[in] callback: function called upon Tx completion * @param[in] priv: optional argument passed to callback */ int unipro_send_async(unsigned int cportid, const void *buf, size_t len, unipro_send_completion_t callback, void *priv) { struct cport *cport; struct unipro_buffer *buffer; irqstate_t flags; if (len > CPORT_BUF_SIZE) { return -EINVAL; } cport = cport_handle(cportid); if (!cport) { return -EINVAL; } if (cport->pending_reset) { return -EPIPE; } if (!cport->connected) { lldbg("CP%u unconnected\n", cport->cportid); return -EPIPE; } DEBUGASSERT(TRANSFER_MODE == 2); buffer = zalloc(sizeof(*buffer)); if (!buffer) { return -ENOMEM; } list_init(&buffer->list); buffer->som = true; buffer->len = len; buffer->callback = callback; buffer->priv = priv; buffer->data = buf; flags = irqsave(); list_add(&cport->tx_fifo, &buffer->list); irqrestore(flags); sem_post(&worker.tx_fifo_lock); return 0; }
/** * @brief Register a driver with the unipro core * @param drv unipro driver to register * @param cportid cport number to associate this driver to * @return 0 on success, <0 on error */ int unipro_driver_register(struct unipro_driver *driver, unsigned int cportid) { struct cport *cport = cport_handle(cportid); if (!cport) { return -ENODEV; } if (cport->driver) { lldbg("ERROR: Already registered by: %s\n", cport->driver->name); return -EEXIST; } cport->driver = driver; lldbg("Registered driver %s on %sconnected CP%u\n", cport->driver->name, cport->connected ? "" : "un", cport->cportid); return 0; }
int unipro_send_async(unsigned int cportid, const void *buf, size_t len, unipro_send_completion_t callback, void *priv) { struct cport *cport; struct unipro_xfer_descriptor *desc; irqstate_t flags; cport = cport_handle(cportid); if (!cport) { lowsyslog("unipro: invalid cport id: %u, dropping message...\n", cportid); return -EINVAL; } if (cport->pending_reset) { return -EPIPE; } desc = zalloc(sizeof(*desc)); if (!desc) return -ENOMEM; desc->data = buf; desc->len = len; desc->data_offset = 0; desc->callback = callback; desc->priv = priv; desc->cport = cport; list_init(&desc->list); flags = irqsave(); list_add(&cport->tx_fifo, &desc->list); irqrestore(flags); sem_post(&worker.tx_fifo_lock); return 0; }
static struct unipro_xfer_descriptor *pick_tx_descriptor(unsigned int cportid) { struct unipro_xfer_descriptor *desc; unsigned int cport_count = unipro_cport_count(); int i; for (i = 0; i < cport_count; i++, cportid++) { struct cport *cport; cportid = cportid % cport_count; cport = cport_handle(cportid); if (!cport) continue; if (list_is_empty(&cport->tx_fifo)) { if (cport->pending_reset) { unipro_flush_cport(cport); } continue; } if (cport->pending_reset) { unipro_flush_cport(cport); } desc = containerof(cport->tx_fifo.next, struct unipro_xfer_descriptor, list); if (desc->channel) continue; if (!unipro_get_tx_free_buffer_space(desc->cport)) continue; return desc; } return NULL; }
int unipro_reset_cport(unsigned int cportid, cport_reset_completion_cb_t cb, void *priv) { struct cport *cport; cport = cport_handle(cportid); if (!cport) return -EINVAL; if (cport->pending_reset || cport->reset_completion_cb || cport->reset_completion_cb_priv) { return -EINPROGRESS; } cport->reset_completion_cb_priv = priv; cport->reset_completion_cb = cb; cport->pending_reset = true; unipro_reset_notify(cportid); return 0; }
/** * @brief Initialize one UniPro cport */ static int unipro_init_cport(unsigned int cportid) { struct cport *cport = cport_handle(cportid); if (!cport) { return -EINVAL; } if (cport->connected) return 0; _unipro_reset_cport(cportid); atomic_init(&cport->inflight_buf_count, 0); #if defined(CONFIG_APBRIDGEA) cport->max_inflight_buf_count = 1; #else cport->max_inflight_buf_count = CONFIG_TSB_UNIPRO_MAX_INFLIGHT_BUFCOUNT; #endif cport->switch_buf_on_free = false; cport->rx_buf = unipro_rxbuf_alloc(cportid); if (!cport->rx_buf) { lowsyslog("unipro: couldn't allocate initial buffer for CP%u\n", cportid); return -ENOMEM; } unipro_switch_rxbuf(cportid, cport->rx_buf); #ifdef UNIPRO_DEBUG unipro_info(); #endif return 0; }
/** * @brief Enable a CPort that has a connected connection. */ static int configure_connected_cport(unsigned int cportid) { int ret = 0; struct cport *cport; unsigned int rc; irqstate_t flags; cport = cport_handle(cportid); if (!cport) { return -EINVAL; } rc = cport_get_status(cport); switch (rc) { case CPORT_STATUS_CONNECTED: cport->connected = 1; /* * Clear any pending EOM interrupts, then enable them. */ flags = irqsave(); clear_int(cportid); enable_int(cportid); irqrestore(flags); /* Start the flow of received data */ unipro_write(REG_RX_PAUSE_SIZE_00 + (cportid * sizeof(uint32_t)), (1 << 31) | CPORT_BUF_SIZE); break; case CPORT_STATUS_UNCONNECTED: ret = -ENOTCONN; break; default: lldbg("Unexpected status: CP%u: status: 0x%u\n", cportid, rc); ret = -EIO; } return ret; }
/** * @brief UniPro debug dump */ static void dump_regs(void) { uint32_t val; unsigned int i; #define DBG_ATTR(attr) do { \ (void)unipro_attr_local_read(attr, &val, 0); \ lldbg(" [%s]: 0x%x\n", #attr, val); \ } while (0); #define DBG_CPORT_ATTR(attr, cportid) do { \ unipro_attr_local_read(attr, &val, cportid); \ lldbg(" [%s]: 0x%x\n", #attr, val); \ } while (0); #define REG_DBG(reg) do { \ val = unipro_read(reg); \ lldbg(" [%s]: 0x%x\n", #reg, val); \ } while (0) lldbg("DME Attributes\n"); lldbg("========================================\n"); DBG_ATTR(PA_ACTIVETXDATALANES); DBG_ATTR(PA_ACTIVERXDATALANES); DBG_ATTR(PA_TXGEAR); DBG_ATTR(PA_TXTERMINATION); DBG_ATTR(PA_HSSERIES); DBG_ATTR(PA_PWRMODE); DBG_ATTR(PA_ACTIVERXDATALANES); DBG_ATTR(PA_RXGEAR); DBG_ATTR(PA_RXTERMINATION); DBG_ATTR(PA_PWRMODEUSERDATA0); DBG_ATTR(N_DEVICEID); DBG_ATTR(N_DEVICEID_VALID); DBG_ATTR(DME_DDBL1_REVISION); DBG_ATTR(DME_DDBL1_LEVEL); DBG_ATTR(DME_DDBL1_DEVICECLASS); DBG_ATTR(DME_DDBL1_MANUFACTURERID); DBG_ATTR(DME_DDBL1_PRODUCTID); DBG_ATTR(DME_DDBL1_LENGTH); DBG_ATTR(TSB_DME_DDBL2_A); DBG_ATTR(TSB_DME_DDBL2_B); DBG_ATTR(TSB_MAILBOX); DBG_ATTR(TSB_MAXSEGMENTCONFIG); DBG_ATTR(TSB_DME_POWERMODEIND); lldbg("Unipro Interrupt Info:\n"); lldbg("========================================\n"); REG_DBG(UNIPRO_INT_EN); REG_DBG(AHM_RX_EOM_INT_EN_0); REG_DBG(AHM_RX_EOM_INT_EN_1); REG_DBG(UNIPRO_INT_BEF); REG_DBG(AHS_TIMEOUT_INT_BEF_0); REG_DBG(AHS_TIMEOUT_INT_BEF_1); REG_DBG(AHM_HRESP_ERR_INT_BEF_0); REG_DBG(AHM_HRESP_ERR_INT_BEF_1); REG_DBG(CPB_RX_E2EFC_RSLT_ERR_INT_BEF_0); REG_DBG(CPB_RX_E2EFC_RSLT_ERR_INT_BEF_1); REG_DBG(CPB_TX_RSLTCODE_ERR_INT_BEF_0); REG_DBG(CPB_TX_RSLTCODE_ERR_INT_BEF_1); REG_DBG(CPB_RX_MSGST_ERR_INT_BEF_0); REG_DBG(CPB_RX_MSGST_ERR_INT_BEF_1); REG_DBG(LUP_INT_BEF); REG_DBG(A2D_ATTRACS_INT_BEF); REG_DBG(AHM_RX_EOM_INT_BEF_0); REG_DBG(AHM_RX_EOM_INT_BEF_1); REG_DBG(AHM_RX_EOM_INT_BEF_2); REG_DBG(AHM_RX_EOT_INT_BEF_0); REG_DBG(AHM_RX_EOT_INT_BEF_1); lldbg("Unipro Registers:\n"); lldbg("========================================\n"); REG_DBG(AHM_MODE_CTRL_0); if (tsb_get_product_id() == tsb_pid_apbridge) { REG_DBG(AHM_MODE_CTRL_1); REG_DBG(AHM_MODE_CTRL_2); } REG_DBG(AHM_ADDRESS_00); REG_DBG(REG_RX_PAUSE_SIZE_00); REG_DBG(CPB_RX_TRANSFERRED_DATA_SIZE_00); REG_DBG(CPB_TX_BUFFER_SPACE_00); REG_DBG(CPB_TX_RESULTCODE_0); REG_DBG(AHS_HRESP_MODE_0); REG_DBG(AHS_TIMEOUT_00); REG_DBG(CPB_TX_E2EFC_EN_0); REG_DBG(CPB_TX_E2EFC_EN_1); REG_DBG(CPB_RX_E2EFC_EN_0); REG_DBG(CPB_RX_E2EFC_EN_1); REG_DBG(CPORT_STATUS_0); REG_DBG(CPORT_STATUS_1); REG_DBG(CPORT_STATUS_2); lldbg("Connected CPorts:\n"); lldbg("========================================\n"); for (i = 0; i < cport_count; i++) { struct cport *cport = cport_handle(i); if (!cport) { continue; } val = cport_get_status(cport); if (val == CPORT_STATUS_CONNECTED) { lldbg("CPORT %u:\n", i); DBG_CPORT_ATTR(T_PEERDEVICEID, i); DBG_CPORT_ATTR(T_PEERCPORTID, i); DBG_CPORT_ATTR(T_TRAFFICCLASS, i); DBG_CPORT_ATTR(T_CPORTFLAGS, i); DBG_CPORT_ATTR(T_LOCALBUFFERSPACE, i); DBG_CPORT_ATTR(T_PEERBUFFERSPACE, i); DBG_CPORT_ATTR(T_CREDITSTOSEND, i); DBG_CPORT_ATTR(T_RXTOKENVALUE, i); DBG_CPORT_ATTR(T_TXTOKENVALUE, i); DBG_CPORT_ATTR(T_CONNECTIONSTATE, i); } } lldbg("NVIC:\n"); lldbg("========================================\n"); tsb_dumpnvic(); }