/** * @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; }
static int irq_unipro(int irq, void *context) { int rc; uint32_t val; tsb_irq_clear_pending(TSB_IRQ_UNIPRO); if (unipro_read(LUP_INT_BEF) & UNIPRO_LUP_DONE) { unipro_write(LUP_INT_BEF, UNIPRO_LUP_DONE); unipro_evt_handler(UNIPRO_EVT_LUP_DONE); } /* * Clear the initial interrupt */ rc = unipro_attr_local_read(TSB_INTERRUPTSTATUS, &val, 0); if (rc) { goto done; } if (val & TSB_INTERRUPTSTATUS_MAILBOX) { unipro_evt_handler(UNIPRO_EVT_MAILBOX); } done: return 0; }
static int es3_tsb_unipro_set_init_status(uint32_t val) { int retval; uint32_t status; /* * See SW-1228 * -> https://projectara.atlassian.net/browse/SW-1228?focusedCommentId=28889&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-28889 */ if (val & INIT_STATUS_ERROR_CODE_MASK) { lowsyslog("%s() cannot override the init status error code (init status: %u).\n", __func__, val); return -EINVAL; } retval = unipro_attr_local_read(TSB_DME_ES3_INIT_STATUS, &status, UNIPRO_SELINDEX_NULL); if (retval) { lowsyslog("init-status read failed: retval = %d\n", retval); return retval; } /* preserve error code: see SW-1228 */ val |= status & INIT_STATUS_ERROR_CODE_MASK; retval = unipro_attr_local_write(TSB_DME_ES3_INIT_STATUS, val, UNIPRO_SELINDEX_NULL); if (retval) { lowsyslog("init-status write failed: retval = %d\n", retval); return retval; } return 0; }
static int unipro_enable_mailbox_irq(void) { int retval; uint32_t val; retval = unipro_attr_local_read(TSB_INTERRUPTENABLE, &val, UNIPRO_SELINDEX_NULL); if (retval) { return retval; } return unipro_attr_local_write(TSB_INTERRUPTENABLE, val | TSB_INTERRUPTSTATUS_MAILBOX, UNIPRO_SELINDEX_NULL); }
unsigned int unipro_cport_count(void) { uint32_t num_cports; int retval; if (tsb_get_rev_id() == tsb_rev_es2) { /* T_NUMCPORTS is incorrect on es2 */ /* * Reduce the run-time CPort count to what's available on the * GPBridges, unless we can determine that we're running on an * APBridge. */ return ((tsb_get_product_id() == tsb_pid_apbridge) ? ES2_APBRIDGE_CPORT_MAX : ES2_GPBRIDGE_CPORT_MAX); } retval = unipro_attr_local_read(T_NUMCPORTS, &num_cports, 0); if (retval) { lowsyslog("unipro: cannot determine number of cports\n"); return 0; } return num_cports; }
/* IMPORTANT: * The UniPro event handler callback runs in the context of the UniPro IRQ. */ static void unipro_evt_handler(enum unipro_event evt) { uint32_t rc; uint32_t v = 0; int err; llvdbg("event=%d\n", evt); switch (evt) { case UNIPRO_EVT_LUP_DONE: svc_send_event(SVC_EVENT_UNIPRO_LINK_UP, 0, 0, 0); break; case UNIPRO_EVT_LINK_LOST: rc = unipro_attr_local_read(TSB_DME_LINKLOSTIND, &v, 0); llvdbg("rc=%d, lost_ind=%x\n", rc, v); if (v & 0x1) { svc_send_event(SVC_EVENT_UNIPRO_LINK_DOWN, (void *)(unsigned int)0, (void *)(unsigned int)0, (void *)(unsigned int)0); } break; case UNIPRO_EVT_PWRMODE: if (g_svc.gearbox) { uint32_t pmi_val0 = 0; rc = unipro_attr_read(TSB_DME_POWERMODEIND, &pmi_val0, 0, 0); llvdbg("rc=%d, pmi0=%x\n", rc, pmi_val0); uint32_t pmi_val1 = 0; rc = unipro_attr_read(TSB_DME_POWERMODEIND, &pmi_val1, 0, 1); llvdbg("rc=%d, pmi1=%x\n", rc, pmi_val1); if (pmi_val0 == 2 && pmi_val1 == 4) { llvdbg("Powermode change successful\n"); err = 0; } else { llvdbg("Powermode change failed\n"); err = -1; } svc_send_event(SVC_EVENT_GEAR_SHIFT_DONE, (void *)(unsigned int)err, (void *)(unsigned int)0, (void *)(unsigned int)0); } break; case UNIPRO_EVT_PHY_ERROR: rc = unipro_attr_local_read(TSB_DME_ERRORPHYIND, &v, 0); v &= 1; if (v) { llvdbg("rc=%d, phy err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.phy_lane_err, v); } break; case UNIPRO_EVT_PA_ERROR: rc = unipro_attr_local_read(TSB_DME_ERRORPAIND, &v, 0); v &= 0x3; if (v) { llvdbg("rc=%d, pa err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.pa_lane_reset_tx, v); } break; case UNIPRO_EVT_D_ERROR: rc = unipro_attr_local_read(TSB_DME_ERRORDIND, &v, 0); v &= 0x7fff; if (v) { llvdbg("rc=%d, d err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.d_nac_received, v); } break; case UNIPRO_EVT_N_ERROR: rc = unipro_attr_local_read(TSB_DME_ERRORNIND, &v, 0); v &= 0x7; if (v) { llvdbg("rc=%d, n err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.n_unsupported_header_type, v); } break; case UNIPRO_EVT_T_ERROR: rc = unipro_attr_local_read(TSB_DME_ERRORTIND, &v, 0); v &= 0x7f; if (v) { llvdbg("rc=%d, t err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.t_unsupported_header_type, v); } break; case UNIPRO_EVT_PAINIT_ERROR: { bool lost = false; rc = unipro_attr_local_read(TSB_DME_ERRORPHYIND, &v, 0); v &= 0x1; if (v) { llvdbg("rc=%d, phy err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.phy_lane_err, v); lost = true; } rc = unipro_attr_local_read(TSB_DME_ERRORPAIND, &v, 0); v &= 0x3; if (v) { llvdbg("rc=%d, pa err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.pa_lane_reset_tx, v); lost = true; } rc = unipro_attr_local_read(TSB_DME_ERRORDIND, &v, 0); v &= 0x7fff; if (v) { llvdbg("rc=%d, d err=%x\n", rc, v); _svc_bitmask_to_stats(&g_unipro_stats.d_nac_received, v); lost = true; } if (lost) { llvdbg("Link lost\n"); svc_send_event(SVC_EVENT_UNIPRO_LINK_DOWN, (void *)(unsigned int)0, (void *)(unsigned int)0, (void *)(unsigned int)0); } break; } case UNIPRO_EVT_MAILBOX: { v = TSB_MAIL_RESET; rc = unipro_attr_local_read(TSB_MAILBOX, &v, 0); llvdbg("rc=%d, mbox_val=%d\n", rc, v); if (rc) { } #if CONFIG_UNIPRO_P2P_APBA if (v == TSB_MAIL_READY_OTHER) { llvdbg("Mod detected\n"); svc_send_event(SVC_EVENT_MOD_DETECTED, (void *)(unsigned int)0, (void *)(unsigned int)0, (void *)(unsigned int)0); } else { // TODO: Handle error case. llvdbg("Mod NOT detected\n"); } #elif CONFIG_UNIPRO_P2P_APBE llvdbg("Connected cport %d\n", v-1); svc_send_event(SVC_EVENT_CPORTS_DONE, (void *)(v-1), 0, 0); #endif break; } default: break; } vdbg("done\n"); }