144c0947bSAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause 2244cfa79SAndrew Rybchenko * 3*a0147be5SAndrew Rybchenko * Copyright(c) 2019-2020 Xilinx, Inc. 4*a0147be5SAndrew Rybchenko * Copyright(c) 2016-2019 Solarflare Communications Inc. 5ba641f20SAndrew Rybchenko * 6ba641f20SAndrew Rybchenko * This software was jointly developed between OKTET Labs (under contract 7ba641f20SAndrew Rybchenko * for Solarflare) and Solarflare Communications, Inc. 8ba641f20SAndrew Rybchenko */ 9ba641f20SAndrew Rybchenko 10ba641f20SAndrew Rybchenko #include <rte_cycles.h> 11ba641f20SAndrew Rybchenko 12ba641f20SAndrew Rybchenko #include "efx.h" 13ba641f20SAndrew Rybchenko #include "efx_mcdi.h" 14ba641f20SAndrew Rybchenko #include "efx_regs_mcdi.h" 15ba641f20SAndrew Rybchenko 16ba641f20SAndrew Rybchenko #include "sfc.h" 17ba641f20SAndrew Rybchenko #include "sfc_log.h" 184650ed44SIvan Malov #include "sfc_ev.h" 19ba641f20SAndrew Rybchenko 20ba641f20SAndrew Rybchenko #define SFC_MCDI_POLL_INTERVAL_MIN_US 10 /* 10us in 1us units */ 21ba641f20SAndrew Rybchenko #define SFC_MCDI_POLL_INTERVAL_MAX_US (US_PER_S / 10) /* 100ms in 1us units */ 22ba641f20SAndrew Rybchenko #define SFC_MCDI_WATCHDOG_INTERVAL_US (10 * US_PER_S) /* 10s in 1us units */ 23ba641f20SAndrew Rybchenko 24ba641f20SAndrew Rybchenko static void 25ba641f20SAndrew Rybchenko sfc_mcdi_timeout(struct sfc_adapter *sa) 26ba641f20SAndrew Rybchenko { 27ba641f20SAndrew Rybchenko sfc_warn(sa, "MC TIMEOUT"); 28ba641f20SAndrew Rybchenko 29ba641f20SAndrew Rybchenko sfc_panic(sa, "MCDI timeout handling is not implemented\n"); 30ba641f20SAndrew Rybchenko } 31ba641f20SAndrew Rybchenko 324650ed44SIvan Malov static inline boolean_t 334650ed44SIvan Malov sfc_mcdi_proxy_event_available(struct sfc_adapter *sa) 344650ed44SIvan Malov { 354650ed44SIvan Malov struct sfc_mcdi *mcdi = &sa->mcdi; 364650ed44SIvan Malov 374650ed44SIvan Malov mcdi->proxy_handle = 0; 384650ed44SIvan Malov mcdi->proxy_result = ETIMEDOUT; 394650ed44SIvan Malov sfc_ev_mgmt_qpoll(sa); 404650ed44SIvan Malov if (mcdi->proxy_result != ETIMEDOUT) 414650ed44SIvan Malov return B_TRUE; 424650ed44SIvan Malov 434650ed44SIvan Malov return B_FALSE; 444650ed44SIvan Malov } 454650ed44SIvan Malov 46ba641f20SAndrew Rybchenko static void 474650ed44SIvan Malov sfc_mcdi_poll(struct sfc_adapter *sa, boolean_t proxy) 48ba641f20SAndrew Rybchenko { 49ba641f20SAndrew Rybchenko efx_nic_t *enp; 50ba641f20SAndrew Rybchenko unsigned int delay_total; 51ba641f20SAndrew Rybchenko unsigned int delay_us; 52ba641f20SAndrew Rybchenko boolean_t aborted __rte_unused; 53ba641f20SAndrew Rybchenko 54ba641f20SAndrew Rybchenko delay_total = 0; 55ba641f20SAndrew Rybchenko delay_us = SFC_MCDI_POLL_INTERVAL_MIN_US; 56ba641f20SAndrew Rybchenko enp = sa->nic; 57ba641f20SAndrew Rybchenko 58ba641f20SAndrew Rybchenko do { 594650ed44SIvan Malov boolean_t poll_completed; 604650ed44SIvan Malov 614650ed44SIvan Malov poll_completed = (proxy) ? sfc_mcdi_proxy_event_available(sa) : 624650ed44SIvan Malov efx_mcdi_request_poll(enp); 634650ed44SIvan Malov if (poll_completed) 64ba641f20SAndrew Rybchenko return; 65ba641f20SAndrew Rybchenko 66ba641f20SAndrew Rybchenko if (delay_total > SFC_MCDI_WATCHDOG_INTERVAL_US) { 674650ed44SIvan Malov if (!proxy) { 68ba641f20SAndrew Rybchenko aborted = efx_mcdi_request_abort(enp); 69ba641f20SAndrew Rybchenko SFC_ASSERT(aborted); 70ba641f20SAndrew Rybchenko sfc_mcdi_timeout(sa); 714650ed44SIvan Malov } 724650ed44SIvan Malov 73ba641f20SAndrew Rybchenko return; 74ba641f20SAndrew Rybchenko } 75ba641f20SAndrew Rybchenko 76ba641f20SAndrew Rybchenko rte_delay_us(delay_us); 77ba641f20SAndrew Rybchenko 78ba641f20SAndrew Rybchenko delay_total += delay_us; 79ba641f20SAndrew Rybchenko 80ba641f20SAndrew Rybchenko /* Exponentially back off the poll frequency */ 81ba641f20SAndrew Rybchenko RTE_BUILD_BUG_ON(SFC_MCDI_POLL_INTERVAL_MAX_US > UINT_MAX / 2); 82ba641f20SAndrew Rybchenko delay_us *= 2; 83ba641f20SAndrew Rybchenko if (delay_us > SFC_MCDI_POLL_INTERVAL_MAX_US) 84ba641f20SAndrew Rybchenko delay_us = SFC_MCDI_POLL_INTERVAL_MAX_US; 85ba641f20SAndrew Rybchenko 86ba641f20SAndrew Rybchenko } while (1); 87ba641f20SAndrew Rybchenko } 88ba641f20SAndrew Rybchenko 89ba641f20SAndrew Rybchenko static void 90ba641f20SAndrew Rybchenko sfc_mcdi_execute(void *arg, efx_mcdi_req_t *emrp) 91ba641f20SAndrew Rybchenko { 92ba641f20SAndrew Rybchenko struct sfc_adapter *sa = (struct sfc_adapter *)arg; 93ba641f20SAndrew Rybchenko struct sfc_mcdi *mcdi = &sa->mcdi; 944650ed44SIvan Malov uint32_t proxy_handle; 95ba641f20SAndrew Rybchenko 96ba641f20SAndrew Rybchenko rte_spinlock_lock(&mcdi->lock); 97ba641f20SAndrew Rybchenko 98ba641f20SAndrew Rybchenko SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 99ba641f20SAndrew Rybchenko 100ba641f20SAndrew Rybchenko efx_mcdi_request_start(sa->nic, emrp, B_FALSE); 1014650ed44SIvan Malov sfc_mcdi_poll(sa, B_FALSE); 1024650ed44SIvan Malov 1034650ed44SIvan Malov if (efx_mcdi_get_proxy_handle(sa->nic, emrp, &proxy_handle) == 0) { 1044650ed44SIvan Malov /* 1054650ed44SIvan Malov * Authorization is required for the MCDI request; 1064650ed44SIvan Malov * wait for an MCDI proxy response event to bring 1074650ed44SIvan Malov * a non-zero proxy handle (should be the same as 1084650ed44SIvan Malov * the value obtained above) and operation status 1094650ed44SIvan Malov */ 1104650ed44SIvan Malov sfc_mcdi_poll(sa, B_TRUE); 1114650ed44SIvan Malov 1124650ed44SIvan Malov if ((mcdi->proxy_handle != 0) && 1134650ed44SIvan Malov (mcdi->proxy_handle != proxy_handle)) { 1144650ed44SIvan Malov sfc_err(sa, "Unexpected MCDI proxy event"); 1154650ed44SIvan Malov emrp->emr_rc = EFAULT; 1164650ed44SIvan Malov } else if (mcdi->proxy_result == 0) { 1174650ed44SIvan Malov /* 1184650ed44SIvan Malov * Authorization succeeded; re-issue the original 1194650ed44SIvan Malov * request and poll for an ordinary MCDI response 1204650ed44SIvan Malov */ 1214650ed44SIvan Malov efx_mcdi_request_start(sa->nic, emrp, B_FALSE); 1224650ed44SIvan Malov sfc_mcdi_poll(sa, B_FALSE); 1234650ed44SIvan Malov } else { 1244650ed44SIvan Malov emrp->emr_rc = mcdi->proxy_result; 1254650ed44SIvan Malov sfc_err(sa, "MCDI proxy authorization failed " 1264650ed44SIvan Malov "(handle=%08x, result=%d)", 1274650ed44SIvan Malov proxy_handle, mcdi->proxy_result); 1284650ed44SIvan Malov } 1294650ed44SIvan Malov } 130ba641f20SAndrew Rybchenko 131ba641f20SAndrew Rybchenko rte_spinlock_unlock(&mcdi->lock); 132ba641f20SAndrew Rybchenko } 133ba641f20SAndrew Rybchenko 134ba641f20SAndrew Rybchenko static void 135ba641f20SAndrew Rybchenko sfc_mcdi_ev_cpl(void *arg) 136ba641f20SAndrew Rybchenko { 137ba641f20SAndrew Rybchenko struct sfc_adapter *sa = (struct sfc_adapter *)arg; 138ba641f20SAndrew Rybchenko struct sfc_mcdi *mcdi __rte_unused; 139ba641f20SAndrew Rybchenko 140ba641f20SAndrew Rybchenko mcdi = &sa->mcdi; 141ba641f20SAndrew Rybchenko SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 142ba641f20SAndrew Rybchenko 143ba641f20SAndrew Rybchenko /* MCDI is polled, completions are not expected */ 144ba641f20SAndrew Rybchenko SFC_ASSERT(0); 145ba641f20SAndrew Rybchenko } 146ba641f20SAndrew Rybchenko 147ba641f20SAndrew Rybchenko static void 148ba641f20SAndrew Rybchenko sfc_mcdi_exception(void *arg, efx_mcdi_exception_t eme) 149ba641f20SAndrew Rybchenko { 150ba641f20SAndrew Rybchenko struct sfc_adapter *sa = (struct sfc_adapter *)arg; 151ba641f20SAndrew Rybchenko 152ba641f20SAndrew Rybchenko sfc_warn(sa, "MC %s", 153ba641f20SAndrew Rybchenko (eme == EFX_MCDI_EXCEPTION_MC_REBOOT) ? "REBOOT" : 154ba641f20SAndrew Rybchenko (eme == EFX_MCDI_EXCEPTION_MC_BADASSERT) ? "BADASSERT" : "UNKNOWN"); 155ba641f20SAndrew Rybchenko 156e77f9f19SAndrew Rybchenko sfc_schedule_restart(sa); 157ba641f20SAndrew Rybchenko } 158ba641f20SAndrew Rybchenko 1593e3b2e4cSAndrew Rybchenko #define SFC_MCDI_LOG_BUF_SIZE 128 1603e3b2e4cSAndrew Rybchenko 1613e3b2e4cSAndrew Rybchenko static size_t 1623e3b2e4cSAndrew Rybchenko sfc_mcdi_do_log(const struct sfc_adapter *sa, 1633e3b2e4cSAndrew Rybchenko char *buffer, void *data, size_t data_size, 1643e3b2e4cSAndrew Rybchenko size_t pfxsize, size_t position) 1653e3b2e4cSAndrew Rybchenko { 1663e3b2e4cSAndrew Rybchenko uint32_t *words = data; 1673e3b2e4cSAndrew Rybchenko /* Space separator plus 2 characters per byte */ 1683e3b2e4cSAndrew Rybchenko const size_t word_str_space = 1 + 2 * sizeof(*words); 1693e3b2e4cSAndrew Rybchenko size_t i; 1703e3b2e4cSAndrew Rybchenko 1713e3b2e4cSAndrew Rybchenko for (i = 0; i < data_size; i += sizeof(*words)) { 1723e3b2e4cSAndrew Rybchenko if (position + word_str_space >= 1733e3b2e4cSAndrew Rybchenko SFC_MCDI_LOG_BUF_SIZE) { 1743e3b2e4cSAndrew Rybchenko /* Flush at SFC_MCDI_LOG_BUF_SIZE with backslash 1753e3b2e4cSAndrew Rybchenko * at the end which is required by netlogdecode. 1763e3b2e4cSAndrew Rybchenko */ 1773e3b2e4cSAndrew Rybchenko buffer[position] = '\0'; 178a6fae8f9SIvan Malov sfc_log_mcdi(sa, "%s \\", buffer); 1793e3b2e4cSAndrew Rybchenko /* Preserve prefix for the next log message */ 1803e3b2e4cSAndrew Rybchenko position = pfxsize; 1813e3b2e4cSAndrew Rybchenko } 1823e3b2e4cSAndrew Rybchenko position += snprintf(buffer + position, 1833e3b2e4cSAndrew Rybchenko SFC_MCDI_LOG_BUF_SIZE - position, 1843e3b2e4cSAndrew Rybchenko " %08x", *words); 1853e3b2e4cSAndrew Rybchenko words++; 1863e3b2e4cSAndrew Rybchenko } 1873e3b2e4cSAndrew Rybchenko return position; 1883e3b2e4cSAndrew Rybchenko } 1893e3b2e4cSAndrew Rybchenko 1903e3b2e4cSAndrew Rybchenko static void 1913e3b2e4cSAndrew Rybchenko sfc_mcdi_logger(void *arg, efx_log_msg_t type, 1923e3b2e4cSAndrew Rybchenko void *header, size_t header_size, 1933e3b2e4cSAndrew Rybchenko void *data, size_t data_size) 1943e3b2e4cSAndrew Rybchenko { 1953e3b2e4cSAndrew Rybchenko struct sfc_adapter *sa = (struct sfc_adapter *)arg; 1963e3b2e4cSAndrew Rybchenko char buffer[SFC_MCDI_LOG_BUF_SIZE]; 1973e3b2e4cSAndrew Rybchenko size_t pfxsize; 1983e3b2e4cSAndrew Rybchenko size_t start; 1993e3b2e4cSAndrew Rybchenko 200a6fae8f9SIvan Malov /* 201a6fae8f9SIvan Malov * Unlike the other cases, MCDI logging implies more onerous work 202a6fae8f9SIvan Malov * needed to produce a message. If the dynamic log level prevents 203a6fae8f9SIvan Malov * the end result from being printed, the CPU time will be wasted. 204a6fae8f9SIvan Malov * 205a6fae8f9SIvan Malov * To avoid wasting time, the actual level is examined in advance. 206a6fae8f9SIvan Malov */ 207a6fae8f9SIvan Malov if (rte_log_get_level(sa->mcdi.logtype) < (int)SFC_LOG_LEVEL_MCDI) 2083e3b2e4cSAndrew Rybchenko return; 2093e3b2e4cSAndrew Rybchenko 210a6fae8f9SIvan Malov /* The format including prefix added by sfc_log_mcdi() is the format 2113e3b2e4cSAndrew Rybchenko * consumed by the Solarflare netlogdecode tool. 2123e3b2e4cSAndrew Rybchenko */ 2133e3b2e4cSAndrew Rybchenko pfxsize = snprintf(buffer, sizeof(buffer), "MCDI RPC %s:", 2143e3b2e4cSAndrew Rybchenko type == EFX_LOG_MCDI_REQUEST ? "REQ" : 2153e3b2e4cSAndrew Rybchenko type == EFX_LOG_MCDI_RESPONSE ? "RESP" : "???"); 2163e3b2e4cSAndrew Rybchenko start = sfc_mcdi_do_log(sa, buffer, header, header_size, 2173e3b2e4cSAndrew Rybchenko pfxsize, pfxsize); 2183e3b2e4cSAndrew Rybchenko start = sfc_mcdi_do_log(sa, buffer, data, data_size, pfxsize, start); 2193e3b2e4cSAndrew Rybchenko if (start != pfxsize) { 2203e3b2e4cSAndrew Rybchenko buffer[start] = '\0'; 221a6fae8f9SIvan Malov sfc_log_mcdi(sa, "%s", buffer); 2223e3b2e4cSAndrew Rybchenko } 2233e3b2e4cSAndrew Rybchenko } 2243e3b2e4cSAndrew Rybchenko 2254650ed44SIvan Malov static void 2264650ed44SIvan Malov sfc_mcdi_ev_proxy_response(void *arg, uint32_t handle, efx_rc_t result) 2274650ed44SIvan Malov { 2284650ed44SIvan Malov struct sfc_adapter *sa = (struct sfc_adapter *)arg; 2294650ed44SIvan Malov struct sfc_mcdi *mcdi = &sa->mcdi; 2304650ed44SIvan Malov 2314650ed44SIvan Malov mcdi->proxy_handle = handle; 2324650ed44SIvan Malov mcdi->proxy_result = result; 2334650ed44SIvan Malov } 2344650ed44SIvan Malov 235ba641f20SAndrew Rybchenko int 236ba641f20SAndrew Rybchenko sfc_mcdi_init(struct sfc_adapter *sa) 237ba641f20SAndrew Rybchenko { 238ba641f20SAndrew Rybchenko struct sfc_mcdi *mcdi; 239ba641f20SAndrew Rybchenko size_t max_msg_size; 240ba641f20SAndrew Rybchenko efx_mcdi_transport_t *emtp; 241ba641f20SAndrew Rybchenko int rc; 242ba641f20SAndrew Rybchenko 243ba641f20SAndrew Rybchenko sfc_log_init(sa, "entry"); 244ba641f20SAndrew Rybchenko 245ba641f20SAndrew Rybchenko mcdi = &sa->mcdi; 246ba641f20SAndrew Rybchenko 247ba641f20SAndrew Rybchenko SFC_ASSERT(mcdi->state == SFC_MCDI_UNINITIALIZED); 248ba641f20SAndrew Rybchenko 249ba641f20SAndrew Rybchenko rte_spinlock_init(&mcdi->lock); 250ba641f20SAndrew Rybchenko 251ba641f20SAndrew Rybchenko mcdi->state = SFC_MCDI_INITIALIZED; 252ba641f20SAndrew Rybchenko 253ba641f20SAndrew Rybchenko max_msg_size = sizeof(uint32_t) + MCDI_CTL_SDU_LEN_MAX_V2; 254ba641f20SAndrew Rybchenko rc = sfc_dma_alloc(sa, "mcdi", 0, max_msg_size, sa->socket_id, 255ba641f20SAndrew Rybchenko &mcdi->mem); 256ba641f20SAndrew Rybchenko if (rc != 0) 257ba641f20SAndrew Rybchenko goto fail_dma_alloc; 258ba641f20SAndrew Rybchenko 2591d3c7f9cSAndrew Rybchenko mcdi->logtype = sfc_register_logtype(&sa->priv.shared->pci_addr, 260e2c3639aSAndrew Rybchenko SFC_LOGTYPE_MCDI_STR, 261a6fae8f9SIvan Malov RTE_LOG_NOTICE); 2623e3b2e4cSAndrew Rybchenko 263ba641f20SAndrew Rybchenko emtp = &mcdi->transport; 264ba641f20SAndrew Rybchenko emtp->emt_context = sa; 265ba641f20SAndrew Rybchenko emtp->emt_dma_mem = &mcdi->mem; 266ba641f20SAndrew Rybchenko emtp->emt_execute = sfc_mcdi_execute; 267ba641f20SAndrew Rybchenko emtp->emt_ev_cpl = sfc_mcdi_ev_cpl; 268ba641f20SAndrew Rybchenko emtp->emt_exception = sfc_mcdi_exception; 2693e3b2e4cSAndrew Rybchenko emtp->emt_logger = sfc_mcdi_logger; 2704650ed44SIvan Malov emtp->emt_ev_proxy_response = sfc_mcdi_ev_proxy_response; 271ba641f20SAndrew Rybchenko 272ba641f20SAndrew Rybchenko sfc_log_init(sa, "init MCDI"); 273ba641f20SAndrew Rybchenko rc = efx_mcdi_init(sa->nic, emtp); 274ba641f20SAndrew Rybchenko if (rc != 0) 275ba641f20SAndrew Rybchenko goto fail_mcdi_init; 276ba641f20SAndrew Rybchenko 277ba641f20SAndrew Rybchenko return 0; 278ba641f20SAndrew Rybchenko 279ba641f20SAndrew Rybchenko fail_mcdi_init: 280ba641f20SAndrew Rybchenko memset(emtp, 0, sizeof(*emtp)); 281ba641f20SAndrew Rybchenko sfc_dma_free(sa, &mcdi->mem); 282ba641f20SAndrew Rybchenko 283ba641f20SAndrew Rybchenko fail_dma_alloc: 284ba641f20SAndrew Rybchenko mcdi->state = SFC_MCDI_UNINITIALIZED; 285ba641f20SAndrew Rybchenko return rc; 286ba641f20SAndrew Rybchenko } 287ba641f20SAndrew Rybchenko 288ba641f20SAndrew Rybchenko void 289ba641f20SAndrew Rybchenko sfc_mcdi_fini(struct sfc_adapter *sa) 290ba641f20SAndrew Rybchenko { 291ba641f20SAndrew Rybchenko struct sfc_mcdi *mcdi; 292ba641f20SAndrew Rybchenko efx_mcdi_transport_t *emtp; 293ba641f20SAndrew Rybchenko 294ba641f20SAndrew Rybchenko sfc_log_init(sa, "entry"); 295ba641f20SAndrew Rybchenko 296ba641f20SAndrew Rybchenko mcdi = &sa->mcdi; 297ba641f20SAndrew Rybchenko emtp = &mcdi->transport; 298ba641f20SAndrew Rybchenko 299ba641f20SAndrew Rybchenko rte_spinlock_lock(&mcdi->lock); 300ba641f20SAndrew Rybchenko 301ba641f20SAndrew Rybchenko SFC_ASSERT(mcdi->state == SFC_MCDI_INITIALIZED); 302ba641f20SAndrew Rybchenko mcdi->state = SFC_MCDI_UNINITIALIZED; 303ba641f20SAndrew Rybchenko 304ba641f20SAndrew Rybchenko sfc_log_init(sa, "fini MCDI"); 305ba641f20SAndrew Rybchenko efx_mcdi_fini(sa->nic); 306ba641f20SAndrew Rybchenko memset(emtp, 0, sizeof(*emtp)); 307ba641f20SAndrew Rybchenko 308ba641f20SAndrew Rybchenko rte_spinlock_unlock(&mcdi->lock); 309ba641f20SAndrew Rybchenko 310ba641f20SAndrew Rybchenko sfc_dma_free(sa, &mcdi->mem); 311ba641f20SAndrew Rybchenko } 312