13c838a9fSAndrew Rybchenko /*-
2929c7febSAndrew Rybchenko * Copyright (c) 2012-2016 Solarflare Communications Inc.
33c838a9fSAndrew Rybchenko * All rights reserved.
43c838a9fSAndrew Rybchenko *
53c838a9fSAndrew Rybchenko * Redistribution and use in source and binary forms, with or without
63c838a9fSAndrew Rybchenko * modification, are permitted provided that the following conditions are met:
73c838a9fSAndrew Rybchenko *
83c838a9fSAndrew Rybchenko * 1. Redistributions of source code must retain the above copyright notice,
93c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer.
103c838a9fSAndrew Rybchenko * 2. Redistributions in binary form must reproduce the above copyright notice,
113c838a9fSAndrew Rybchenko * this list of conditions and the following disclaimer in the documentation
123c838a9fSAndrew Rybchenko * and/or other materials provided with the distribution.
133c838a9fSAndrew Rybchenko *
143c838a9fSAndrew Rybchenko * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
153c838a9fSAndrew Rybchenko * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
163c838a9fSAndrew Rybchenko * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
173c838a9fSAndrew Rybchenko * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
183c838a9fSAndrew Rybchenko * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
193c838a9fSAndrew Rybchenko * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
203c838a9fSAndrew Rybchenko * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
213c838a9fSAndrew Rybchenko * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
223c838a9fSAndrew Rybchenko * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
233c838a9fSAndrew Rybchenko * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
243c838a9fSAndrew Rybchenko * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
253c838a9fSAndrew Rybchenko *
263c838a9fSAndrew Rybchenko * The views and conclusions contained in the software and documentation are
273c838a9fSAndrew Rybchenko * those of the authors and should not be interpreted as representing official
283c838a9fSAndrew Rybchenko * policies, either expressed or implied, of the FreeBSD Project.
293c838a9fSAndrew Rybchenko */
303c838a9fSAndrew Rybchenko
313c838a9fSAndrew Rybchenko #include <sys/cdefs.h>
323c838a9fSAndrew Rybchenko #include "efx.h"
333c838a9fSAndrew Rybchenko #include "efx_impl.h"
343c838a9fSAndrew Rybchenko
353c838a9fSAndrew Rybchenko #if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
363c838a9fSAndrew Rybchenko
373c838a9fSAndrew Rybchenko #define SIENA_MCDI_PDU(_emip) \
383c838a9fSAndrew Rybchenko (((emip)->emi_port == 1) \
393c838a9fSAndrew Rybchenko ? MC_SMEM_P0_PDU_OFST >> 2 \
403c838a9fSAndrew Rybchenko : MC_SMEM_P1_PDU_OFST >> 2)
413c838a9fSAndrew Rybchenko
423c838a9fSAndrew Rybchenko #define SIENA_MCDI_DOORBELL(_emip) \
433c838a9fSAndrew Rybchenko (((emip)->emi_port == 1) \
443c838a9fSAndrew Rybchenko ? MC_SMEM_P0_DOORBELL_OFST >> 2 \
453c838a9fSAndrew Rybchenko : MC_SMEM_P1_DOORBELL_OFST >> 2)
463c838a9fSAndrew Rybchenko
473c838a9fSAndrew Rybchenko #define SIENA_MCDI_STATUS(_emip) \
483c838a9fSAndrew Rybchenko (((emip)->emi_port == 1) \
493c838a9fSAndrew Rybchenko ? MC_SMEM_P0_STATUS_OFST >> 2 \
503c838a9fSAndrew Rybchenko : MC_SMEM_P1_STATUS_OFST >> 2)
513c838a9fSAndrew Rybchenko
52fd7501bfSAndrew 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)530a687580SAndrew Rybchenko siena_mcdi_send_request(
540a687580SAndrew Rybchenko __in efx_nic_t *enp,
553222b9deSAndrew Rybchenko __in_bcount(hdr_len) void *hdrp,
560a687580SAndrew Rybchenko __in size_t hdr_len,
573222b9deSAndrew Rybchenko __in_bcount(sdu_len) void *sdup,
580a687580SAndrew Rybchenko __in size_t sdu_len)
590a687580SAndrew Rybchenko {
600a687580SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
610a687580SAndrew Rybchenko efx_dword_t dword;
620a687580SAndrew Rybchenko unsigned int pdur;
630a687580SAndrew Rybchenko unsigned int dbr;
640a687580SAndrew Rybchenko unsigned int pos;
650a687580SAndrew Rybchenko
660a687580SAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
670a687580SAndrew Rybchenko
680a687580SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
690a687580SAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
700a687580SAndrew Rybchenko dbr = SIENA_MCDI_DOORBELL(emip);
710a687580SAndrew Rybchenko
720a687580SAndrew Rybchenko /* Write the header */
730a687580SAndrew Rybchenko EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
740a687580SAndrew Rybchenko dword = *(efx_dword_t *)hdrp;
750a687580SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
760a687580SAndrew Rybchenko
770a687580SAndrew Rybchenko /* Write the payload */
780a687580SAndrew Rybchenko for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
790a687580SAndrew Rybchenko dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
800a687580SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
810a687580SAndrew Rybchenko pdur + 1 + (pos >> 2), &dword, B_FALSE);
820a687580SAndrew Rybchenko }
830a687580SAndrew Rybchenko
840a687580SAndrew Rybchenko /* Ring the doorbell */
850a687580SAndrew Rybchenko EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
860a687580SAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
870a687580SAndrew Rybchenko }
880a687580SAndrew Rybchenko
89460cb568SAndrew Rybchenko efx_rc_t
siena_mcdi_poll_reboot(__in efx_nic_t * enp)903c838a9fSAndrew Rybchenko siena_mcdi_poll_reboot(
913c838a9fSAndrew Rybchenko __in efx_nic_t *enp)
923c838a9fSAndrew Rybchenko {
933c838a9fSAndrew Rybchenko #ifndef EFX_GRACEFUL_MC_REBOOT
943c838a9fSAndrew Rybchenko /*
953c838a9fSAndrew Rybchenko * This function is not being used properly.
963c838a9fSAndrew Rybchenko * Until its callers are fixed, it should always return 0.
973c838a9fSAndrew Rybchenko */
983c838a9fSAndrew Rybchenko _NOTE(ARGUNUSED(enp))
993c838a9fSAndrew Rybchenko return (0);
1003c838a9fSAndrew Rybchenko #else
1013c838a9fSAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1023c838a9fSAndrew Rybchenko unsigned int rebootr;
1033c838a9fSAndrew Rybchenko efx_dword_t dword;
1043c838a9fSAndrew Rybchenko uint32_t value;
1053c838a9fSAndrew Rybchenko
1063c838a9fSAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1073c838a9fSAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
1083c838a9fSAndrew Rybchenko rebootr = SIENA_MCDI_STATUS(emip);
1093c838a9fSAndrew Rybchenko
1103c838a9fSAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
1113c838a9fSAndrew Rybchenko value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
1123c838a9fSAndrew Rybchenko
1133c838a9fSAndrew Rybchenko if (value == 0)
1143c838a9fSAndrew Rybchenko return (0);
1153c838a9fSAndrew Rybchenko
1163c838a9fSAndrew Rybchenko EFX_ZERO_DWORD(dword);
1173c838a9fSAndrew Rybchenko EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
1183c838a9fSAndrew Rybchenko
1193c838a9fSAndrew Rybchenko if (value == MC_STATUS_DWORD_ASSERT)
1203c838a9fSAndrew Rybchenko return (EINTR);
1213c838a9fSAndrew Rybchenko else
1223c838a9fSAndrew Rybchenko return (EIO);
1233c838a9fSAndrew Rybchenko #endif
1243c838a9fSAndrew Rybchenko }
1253c838a9fSAndrew Rybchenko
126548ebee5SAndrew Rybchenko extern __checkReturn boolean_t
siena_mcdi_poll_response(__in efx_nic_t * enp)12706af9686SAndrew Rybchenko siena_mcdi_poll_response(
12806af9686SAndrew Rybchenko __in efx_nic_t *enp)
12906af9686SAndrew Rybchenko {
13006af9686SAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
13106af9686SAndrew Rybchenko efx_dword_t hdr;
13206af9686SAndrew Rybchenko unsigned int pdur;
13306af9686SAndrew Rybchenko
13406af9686SAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
13506af9686SAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
13606af9686SAndrew Rybchenko
13706af9686SAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
13806af9686SAndrew Rybchenko return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
13906af9686SAndrew Rybchenko }
14006af9686SAndrew Rybchenko
14159bfa22aSAndrew Rybchenko void
siena_mcdi_read_response(__in efx_nic_t * enp,__out_bcount (length)void * bufferp,__in size_t offset,__in size_t length)14259bfa22aSAndrew Rybchenko siena_mcdi_read_response(
14359bfa22aSAndrew Rybchenko __in efx_nic_t *enp,
14486ec4b85SAndrew Rybchenko __out_bcount(length) void *bufferp,
14559bfa22aSAndrew Rybchenko __in size_t offset,
14659bfa22aSAndrew Rybchenko __in size_t length)
14759bfa22aSAndrew Rybchenko {
14859bfa22aSAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
14959bfa22aSAndrew Rybchenko unsigned int pdur;
150*b20c54ffSAndrew Rybchenko unsigned int pos = 0;
15159bfa22aSAndrew Rybchenko efx_dword_t data;
152*b20c54ffSAndrew Rybchenko size_t remaining = length;
15359bfa22aSAndrew Rybchenko
15459bfa22aSAndrew Rybchenko EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
15559bfa22aSAndrew Rybchenko pdur = SIENA_MCDI_PDU(emip);
15659bfa22aSAndrew Rybchenko
157*b20c54ffSAndrew Rybchenko while (remaining > 0) {
158*b20c54ffSAndrew Rybchenko size_t chunk = MIN(remaining, sizeof (data));
159*b20c54ffSAndrew Rybchenko
16059bfa22aSAndrew Rybchenko EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
16159bfa22aSAndrew Rybchenko pdur + ((offset + pos) >> 2), &data, B_FALSE);
162*b20c54ffSAndrew Rybchenko memcpy((uint8_t *)bufferp + pos, &data, chunk);
163*b20c54ffSAndrew Rybchenko pos += chunk;
164*b20c54ffSAndrew Rybchenko remaining -= chunk;
16559bfa22aSAndrew Rybchenko }
16659bfa22aSAndrew Rybchenko }
16759bfa22aSAndrew Rybchenko
168460cb568SAndrew Rybchenko __checkReturn efx_rc_t
siena_mcdi_init(__in efx_nic_t * enp,__in const efx_mcdi_transport_t * mtp)1693c838a9fSAndrew Rybchenko siena_mcdi_init(
1703c838a9fSAndrew Rybchenko __in efx_nic_t *enp,
1713c838a9fSAndrew Rybchenko __in const efx_mcdi_transport_t *mtp)
1723c838a9fSAndrew Rybchenko {
1733c838a9fSAndrew Rybchenko efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
1743c838a9fSAndrew Rybchenko efx_oword_t oword;
1753c838a9fSAndrew Rybchenko unsigned int portnum;
176460cb568SAndrew Rybchenko efx_rc_t rc;
1773c838a9fSAndrew Rybchenko
178a92a2133SAndrew Rybchenko _NOTE(ARGUNUSED(mtp))
179a92a2133SAndrew Rybchenko
1803c838a9fSAndrew Rybchenko EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
1813c838a9fSAndrew Rybchenko
1823c838a9fSAndrew Rybchenko /* Determine the port number to use for MCDI */
1833c838a9fSAndrew Rybchenko EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
1843c838a9fSAndrew Rybchenko portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
1853c838a9fSAndrew Rybchenko
1863c838a9fSAndrew Rybchenko if (portnum == 0) {
1873c838a9fSAndrew Rybchenko /* Presumably booted from ROM; only MCDI port 1 will work */
1883c838a9fSAndrew Rybchenko emip->emi_port = 1;
1893c838a9fSAndrew Rybchenko } else if (portnum <= 2) {
1903c838a9fSAndrew Rybchenko emip->emi_port = portnum;
1913c838a9fSAndrew Rybchenko } else {
1923c838a9fSAndrew Rybchenko rc = EINVAL;
1933c838a9fSAndrew Rybchenko goto fail1;
1943c838a9fSAndrew Rybchenko }
1953c838a9fSAndrew Rybchenko
19614d6f73eSAndrew Rybchenko /* Siena BootROM and firmware only support MCDIv1 */
19714d6f73eSAndrew Rybchenko emip->emi_max_version = 1;
19814d6f73eSAndrew Rybchenko
1993c838a9fSAndrew Rybchenko /*
2003c838a9fSAndrew Rybchenko * Wipe the atomic reboot status so subsequent MCDI requests succeed.
2013c838a9fSAndrew Rybchenko * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
2023c838a9fSAndrew Rybchenko * assertion handler.
2033c838a9fSAndrew Rybchenko */
2043c838a9fSAndrew Rybchenko (void) siena_mcdi_poll_reboot(enp);
2053c838a9fSAndrew Rybchenko
2063c838a9fSAndrew Rybchenko return (0);
2073c838a9fSAndrew Rybchenko
2083c838a9fSAndrew Rybchenko fail1:
209460cb568SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
2103c838a9fSAndrew Rybchenko
2113c838a9fSAndrew Rybchenko return (rc);
2123c838a9fSAndrew Rybchenko }
2133c838a9fSAndrew Rybchenko
2143c838a9fSAndrew Rybchenko void
siena_mcdi_fini(__in efx_nic_t * enp)2153c838a9fSAndrew Rybchenko siena_mcdi_fini(
2163c838a9fSAndrew Rybchenko __in efx_nic_t *enp)
2173c838a9fSAndrew Rybchenko {
218a92a2133SAndrew Rybchenko _NOTE(ARGUNUSED(enp))
2193c838a9fSAndrew Rybchenko }
2203c838a9fSAndrew Rybchenko
221460cb568SAndrew 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)222af986c75SAndrew Rybchenko siena_mcdi_feature_supported(
2233c838a9fSAndrew Rybchenko __in efx_nic_t *enp,
224af986c75SAndrew Rybchenko __in efx_mcdi_feature_id_t id,
2253c838a9fSAndrew Rybchenko __out boolean_t *supportedp)
2263c838a9fSAndrew Rybchenko {
227af986c75SAndrew Rybchenko efx_rc_t rc;
228af986c75SAndrew Rybchenko
2293c838a9fSAndrew Rybchenko EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
2303c838a9fSAndrew Rybchenko
231af986c75SAndrew Rybchenko switch (id) {
232af986c75SAndrew Rybchenko case EFX_MCDI_FEATURE_FW_UPDATE:
233af986c75SAndrew Rybchenko case EFX_MCDI_FEATURE_LINK_CONTROL:
234af986c75SAndrew Rybchenko case EFX_MCDI_FEATURE_MACADDR_CHANGE:
235af986c75SAndrew Rybchenko case EFX_MCDI_FEATURE_MAC_SPOOFING:
2363c838a9fSAndrew Rybchenko *supportedp = B_TRUE;
237af986c75SAndrew Rybchenko break;
238af986c75SAndrew Rybchenko default:
239af986c75SAndrew Rybchenko rc = ENOTSUP;
240af986c75SAndrew Rybchenko goto fail1;
2413c838a9fSAndrew Rybchenko }
2423c838a9fSAndrew Rybchenko
2433c838a9fSAndrew Rybchenko return (0);
2443c838a9fSAndrew Rybchenko
245af986c75SAndrew Rybchenko fail1:
246af986c75SAndrew Rybchenko EFSYS_PROBE1(fail1, efx_rc_t, rc);
247d486ce4bSAndrew Rybchenko
248af986c75SAndrew Rybchenko return (rc);
249d486ce4bSAndrew Rybchenko }
250d486ce4bSAndrew Rybchenko
2518a4fcbd4SAndrew Rybchenko /* Default timeout for MCDI command processing. */
2528a4fcbd4SAndrew Rybchenko #define SIENA_MCDI_CMD_TIMEOUT_US (10 * 1000 * 1000)
2538a4fcbd4SAndrew Rybchenko
2548a4fcbd4SAndrew Rybchenko void
siena_mcdi_get_timeout(__in efx_nic_t * enp,__in efx_mcdi_req_t * emrp,__out uint32_t * timeoutp)2558a4fcbd4SAndrew Rybchenko siena_mcdi_get_timeout(
2568a4fcbd4SAndrew Rybchenko __in efx_nic_t *enp,
2578a4fcbd4SAndrew Rybchenko __in efx_mcdi_req_t *emrp,
2588a4fcbd4SAndrew Rybchenko __out uint32_t *timeoutp)
2598a4fcbd4SAndrew Rybchenko {
2608a4fcbd4SAndrew Rybchenko _NOTE(ARGUNUSED(enp, emrp))
2618a4fcbd4SAndrew Rybchenko
2628a4fcbd4SAndrew Rybchenko *timeoutp = SIENA_MCDI_CMD_TIMEOUT_US;
2638a4fcbd4SAndrew Rybchenko }
2648a4fcbd4SAndrew Rybchenko
2653c838a9fSAndrew Rybchenko #endif /* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */
266