15e111ed8SAndrew Rybchenko /* SPDX-License-Identifier: BSD-3-Clause
25e111ed8SAndrew Rybchenko *
3*672386c1SAndrew Rybchenko * Copyright(c) 2019-2021 Xilinx, Inc.
45e111ed8SAndrew Rybchenko * Copyright(c) 2012-2019 Solarflare Communications Inc.
55e111ed8SAndrew Rybchenko */
65e111ed8SAndrew Rybchenko
75e111ed8SAndrew Rybchenko #include "efx.h"
85e111ed8SAndrew Rybchenko #include "efx_impl.h"
95e111ed8SAndrew Rybchenko
105e111ed8SAndrew Rybchenko #if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
115e111ed8SAndrew Rybchenko
125e111ed8SAndrew Rybchenko #define SIENA_MCDI_PDU(_emip) \
135e111ed8SAndrew Rybchenko (((emip)->emi_port == 1) \
145e111ed8SAndrew Rybchenko ? MC_SMEM_P0_PDU_OFST >> 2 \
155e111ed8SAndrew Rybchenko : MC_SMEM_P1_PDU_OFST >> 2)
165e111ed8SAndrew Rybchenko
175e111ed8SAndrew Rybchenko #define SIENA_MCDI_DOORBELL(_emip) \
185e111ed8SAndrew Rybchenko (((emip)->emi_port == 1) \
195e111ed8SAndrew Rybchenko ? MC_SMEM_P0_DOORBELL_OFST >> 2 \
205e111ed8SAndrew Rybchenko : MC_SMEM_P1_DOORBELL_OFST >> 2)
215e111ed8SAndrew Rybchenko
225e111ed8SAndrew Rybchenko #define SIENA_MCDI_STATUS(_emip) \
235e111ed8SAndrew Rybchenko (((emip)->emi_port == 1) \
245e111ed8SAndrew Rybchenko ? MC_SMEM_P0_STATUS_OFST >> 2 \
255e111ed8SAndrew Rybchenko : MC_SMEM_P1_STATUS_OFST >> 2)
265e111ed8SAndrew Rybchenko
275e111ed8SAndrew Rybchenko
285e111ed8SAndrew Rybchenko void
siena_mcdi_send_request(__in efx_nic_t * enp,__in_bcount (hdr_len)void * hdrp,__in size_t hdr_len,__in_bcount (sdu_len)void * sdup,__in size_t sdu_len)295e111ed8SAndrew Rybchenko siena_mcdi_send_request(
305e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
315e111ed8SAndrew Rybchenko __in_bcount(hdr_len) void *hdrp,
325e111ed8SAndrew Rybchenko __in size_t hdr_len,
335e111ed8SAndrew Rybchenko __in_bcount(sdu_len) void *sdup,
345e111ed8SAndrew Rybchenko __in size_t sdu_len)
355e111ed8SAndrew Rybchenko {
365e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
375e111ed8SAndrew Rybchenko efx_dword_t dword;
385e111ed8SAndrew Rybchenko unsigned int pdur;
395e111ed8SAndrew Rybchenko unsigned int dbr;
405e111ed8SAndrew Rybchenko unsigned int pos;
415e111ed8SAndrew Rybchenko
425e111ed8SAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
435e111ed8SAndrew Rybchenko
445e111ed8SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
455e111ed8SAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
465e111ed8SAndrew Rybchenko dbr = SIENA_MCDI_DOORBELL(emip);
475e111ed8SAndrew Rybchenko
485e111ed8SAndrew Rybchenko /* Write the header */
495e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
505e111ed8SAndrew Rybchenko dword = *(efx_dword_t *)hdrp;
515e111ed8SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
525e111ed8SAndrew Rybchenko
535e111ed8SAndrew Rybchenko /* Write the payload */
545e111ed8SAndrew Rybchenko for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
555e111ed8SAndrew Rybchenko dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
565e111ed8SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
575e111ed8SAndrew Rybchenko pdur + 1 + (pos >> 2), &dword, B_FALSE);
585e111ed8SAndrew Rybchenko }
595e111ed8SAndrew Rybchenko
605e111ed8SAndrew Rybchenko /* Ring the doorbell */
615e111ed8SAndrew Rybchenko EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
625e111ed8SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
635e111ed8SAndrew Rybchenko }
645e111ed8SAndrew Rybchenko
655e111ed8SAndrew Rybchenko efx_rc_t
siena_mcdi_poll_reboot(__in efx_nic_t * enp)665e111ed8SAndrew Rybchenko siena_mcdi_poll_reboot(
675e111ed8SAndrew Rybchenko __in efx_nic_t *enp)
685e111ed8SAndrew Rybchenko {
695e111ed8SAndrew Rybchenko #if 1
705e111ed8SAndrew Rybchenko /*
715e111ed8SAndrew Rybchenko * XXX Bug 25922, bug 26099: This function is not being used
725e111ed8SAndrew Rybchenko * properly. Until its callers are fixed, it should always
735e111ed8SAndrew Rybchenko * return 0.
745e111ed8SAndrew Rybchenko */
755e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(enp))
765e111ed8SAndrew Rybchenko return (0);
775e111ed8SAndrew Rybchenko #else
785e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
795e111ed8SAndrew Rybchenko unsigned int rebootr;
805e111ed8SAndrew Rybchenko efx_dword_t dword;
815e111ed8SAndrew Rybchenko uint32_t value;
825e111ed8SAndrew Rybchenko
835e111ed8SAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
845e111ed8SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
855e111ed8SAndrew Rybchenko rebootr = SIENA_MCDI_STATUS(emip);
865e111ed8SAndrew Rybchenko
875e111ed8SAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
885e111ed8SAndrew Rybchenko value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
895e111ed8SAndrew Rybchenko
905e111ed8SAndrew Rybchenko if (value == 0)
915e111ed8SAndrew Rybchenko return (0);
925e111ed8SAndrew Rybchenko
935e111ed8SAndrew Rybchenko EFX_ZERO_DWORD(dword);
945e111ed8SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
955e111ed8SAndrew Rybchenko
965e111ed8SAndrew Rybchenko if (value == MC_STATUS_DWORD_ASSERT)
975e111ed8SAndrew Rybchenko return (EINTR);
985e111ed8SAndrew Rybchenko else
995e111ed8SAndrew Rybchenko return (EIO);
1005e111ed8SAndrew Rybchenko #endif
1015e111ed8SAndrew Rybchenko }
1025e111ed8SAndrew Rybchenko
1035e111ed8SAndrew Rybchenko extern __checkReturn boolean_t
siena_mcdi_poll_response(__in efx_nic_t * enp)1045e111ed8SAndrew Rybchenko siena_mcdi_poll_response(
1055e111ed8SAndrew Rybchenko __in efx_nic_t *enp)
1065e111ed8SAndrew Rybchenko {
1075e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1085e111ed8SAndrew Rybchenko efx_dword_t hdr;
1095e111ed8SAndrew Rybchenko unsigned int pdur;
1105e111ed8SAndrew Rybchenko
1115e111ed8SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1125e111ed8SAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
1135e111ed8SAndrew Rybchenko
1145e111ed8SAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
1155e111ed8SAndrew Rybchenko return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
1165e111ed8SAndrew Rybchenko }
1175e111ed8SAndrew Rybchenko
1185e111ed8SAndrew Rybchenko void
siena_mcdi_read_response(__in efx_nic_t * enp,__out_bcount (length)void * bufferp,__in size_t offset,__in size_t length)1195e111ed8SAndrew Rybchenko siena_mcdi_read_response(
1205e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
1215e111ed8SAndrew Rybchenko __out_bcount(length) void *bufferp,
1225e111ed8SAndrew Rybchenko __in size_t offset,
1235e111ed8SAndrew Rybchenko __in size_t length)
1245e111ed8SAndrew Rybchenko {
1255e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1265e111ed8SAndrew Rybchenko unsigned int pdur;
1275e111ed8SAndrew Rybchenko unsigned int pos = 0;
1285e111ed8SAndrew Rybchenko efx_dword_t data;
1295e111ed8SAndrew Rybchenko size_t remaining = length;
1305e111ed8SAndrew Rybchenko
1315e111ed8SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1325e111ed8SAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
1335e111ed8SAndrew Rybchenko
1345e111ed8SAndrew Rybchenko while (remaining > 0) {
1355e111ed8SAndrew Rybchenko size_t chunk = MIN(remaining, sizeof (data));
1365e111ed8SAndrew Rybchenko
1375e111ed8SAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
1385e111ed8SAndrew Rybchenko pdur + ((offset + pos) >> 2), &data, B_FALSE);
1395e111ed8SAndrew Rybchenko memcpy((uint8_t *)bufferp + pos, &data, chunk);
1405e111ed8SAndrew Rybchenko pos += chunk;
1415e111ed8SAndrew Rybchenko remaining -= chunk;
1425e111ed8SAndrew Rybchenko }
1435e111ed8SAndrew Rybchenko }
1445e111ed8SAndrew Rybchenko
1455e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
siena_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * mtp)1465e111ed8SAndrew Rybchenko siena_mcdi_init(
1475e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
1485e111ed8SAndrew Rybchenko __in const efx_mcdi_transport_t *mtp)
1495e111ed8SAndrew Rybchenko {
1505e111ed8SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1515e111ed8SAndrew Rybchenko efx_oword_t oword;
1525e111ed8SAndrew Rybchenko unsigned int portnum;
1535e111ed8SAndrew Rybchenko efx_rc_t rc;
1545e111ed8SAndrew Rybchenko
1555e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(mtp))
1565e111ed8SAndrew Rybchenko
1575e111ed8SAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1585e111ed8SAndrew Rybchenko
1595e111ed8SAndrew Rybchenko /* Determine the port number to use for MCDI */
1605e111ed8SAndrew Rybchenko EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
1615e111ed8SAndrew Rybchenko portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
1625e111ed8SAndrew Rybchenko
1635e111ed8SAndrew Rybchenko if (portnum == 0) {
1645e111ed8SAndrew Rybchenko /* Presumably booted from ROM; only MCDI port 1 will work */
1655e111ed8SAndrew Rybchenko emip->emi_port = 1;
1665e111ed8SAndrew Rybchenko } else if (portnum <= 2) {
1675e111ed8SAndrew Rybchenko emip->emi_port = portnum;
1685e111ed8SAndrew Rybchenko } else {
1695e111ed8SAndrew Rybchenko rc = EINVAL;
1705e111ed8SAndrew Rybchenko goto fail1;
1715e111ed8SAndrew Rybchenko }
1725e111ed8SAndrew Rybchenko
1735e111ed8SAndrew Rybchenko /* Siena BootROM and firmware only support MCDIv1 */
1745e111ed8SAndrew Rybchenko emip->emi_max_version = 1;
1755e111ed8SAndrew Rybchenko
1765e111ed8SAndrew Rybchenko /*
1775e111ed8SAndrew Rybchenko * Wipe the atomic reboot status so subsequent MCDI requests succeed.
1785e111ed8SAndrew Rybchenko * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
1795e111ed8SAndrew Rybchenko * assertion handler.
1805e111ed8SAndrew Rybchenko */
1815e111ed8SAndrew Rybchenko (void) siena_mcdi_poll_reboot(enp);
1825e111ed8SAndrew Rybchenko
1835e111ed8SAndrew Rybchenko return (0);
1845e111ed8SAndrew Rybchenko
1855e111ed8SAndrew Rybchenko fail1:
1865e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
1875e111ed8SAndrew Rybchenko
1885e111ed8SAndrew Rybchenko return (rc);
1895e111ed8SAndrew Rybchenko }
1905e111ed8SAndrew Rybchenko
1915e111ed8SAndrew Rybchenko void
siena_mcdi_fini(__in efx_nic_t * enp)1925e111ed8SAndrew Rybchenko siena_mcdi_fini(
1935e111ed8SAndrew Rybchenko __in efx_nic_t *enp)
1945e111ed8SAndrew Rybchenko {
1955e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(enp))
1965e111ed8SAndrew Rybchenko }
1975e111ed8SAndrew Rybchenko
1985e111ed8SAndrew Rybchenko __checkReturn efx_rc_t
siena_mcdi_feature_supported(__in efx_nic_t * enp,__in efx_mcdi_feature_id_t id,__out boolean_t * supportedp)1995e111ed8SAndrew Rybchenko siena_mcdi_feature_supported(
2005e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
2015e111ed8SAndrew Rybchenko __in efx_mcdi_feature_id_t id,
2025e111ed8SAndrew Rybchenko __out boolean_t *supportedp)
2035e111ed8SAndrew Rybchenko {
2045e111ed8SAndrew Rybchenko efx_rc_t rc;
2055e111ed8SAndrew Rybchenko
2065e111ed8SAndrew Rybchenko EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
2075e111ed8SAndrew Rybchenko
2085e111ed8SAndrew Rybchenko switch (id) {
2095e111ed8SAndrew Rybchenko case EFX_MCDI_FEATURE_FW_UPDATE:
2105e111ed8SAndrew Rybchenko case EFX_MCDI_FEATURE_LINK_CONTROL:
2115e111ed8SAndrew Rybchenko case EFX_MCDI_FEATURE_MACADDR_CHANGE:
2125e111ed8SAndrew Rybchenko case EFX_MCDI_FEATURE_MAC_SPOOFING:
2135e111ed8SAndrew Rybchenko *supportedp = B_TRUE;
2145e111ed8SAndrew Rybchenko break;
2155e111ed8SAndrew Rybchenko default:
2165e111ed8SAndrew Rybchenko rc = ENOTSUP;
2175e111ed8SAndrew Rybchenko goto fail1;
2185e111ed8SAndrew Rybchenko }
2195e111ed8SAndrew Rybchenko
2205e111ed8SAndrew Rybchenko return (0);
2215e111ed8SAndrew Rybchenko
2225e111ed8SAndrew Rybchenko fail1:
2235e111ed8SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2245e111ed8SAndrew Rybchenko
2255e111ed8SAndrew Rybchenko return (rc);
2265e111ed8SAndrew Rybchenko }
2275e111ed8SAndrew Rybchenko
2285e111ed8SAndrew Rybchenko /* Default timeout for MCDI command processing. */
2295e111ed8SAndrew Rybchenko #define SIENA_MCDI_CMD_TIMEOUT_US (10 * 1000 * 1000)
2305e111ed8SAndrew Rybchenko
2315e111ed8SAndrew Rybchenko void
siena_mcdi_get_timeout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * timeoutp)2325e111ed8SAndrew Rybchenko siena_mcdi_get_timeout(
2335e111ed8SAndrew Rybchenko __in efx_nic_t *enp,
2345e111ed8SAndrew Rybchenko __in efx_mcdi_req_t *emrp,
2355e111ed8SAndrew Rybchenko __out uint32_t *timeoutp)
2365e111ed8SAndrew Rybchenko {
2375e111ed8SAndrew Rybchenko _NOTE(ARGUNUSED(enp, emrp))
2385e111ed8SAndrew Rybchenko
2395e111ed8SAndrew Rybchenko *timeoutp = SIENA_MCDI_CMD_TIMEOUT_US;
2405e111ed8SAndrew Rybchenko }
2415e111ed8SAndrew Rybchenko
2425e111ed8SAndrew Rybchenko
2435e111ed8SAndrew Rybchenko #endif /* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
244