1 /*
   2  * Copyright (c) 2012-2015 Solarflare Communications Inc.
   3  * All rights reserved.
   4  *
   5  * Redistribution and use in source and binary forms, with or without
   6  * modification, are permitted provided that the following conditions are met:
   7  *
   8  * 1. Redistributions of source code must retain the above copyright notice,
   9  *    this list of conditions and the following disclaimer.
  10  * 2. Redistributions in binary form must reproduce the above copyright notice,
  11  *    this list of conditions and the following disclaimer in the documentation
  12  *    and/or other materials provided with the distribution.
  13  *
  14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  15  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  16  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  17  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  21  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  22  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
  23  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  24  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25  *
  26  * The views and conclusions contained in the software and documentation are
  27  * those of the authors and should not be interpreted as representing official
  28  * policies, either expressed or implied, of the FreeBSD Project.
  29  */
  30 
  31 #include "efx.h"
  32 #include "efx_impl.h"
  33 
  34 #if EFSYS_OPT_SIENA && EFSYS_OPT_MCDI
  35 
  36 #define SIENA_MCDI_PDU(_emip)                   \
  37         (((emip)->emi_port == 1)             \
  38         ? MC_SMEM_P0_PDU_OFST >> 2                \
  39         : MC_SMEM_P1_PDU_OFST >> 2)
  40 
  41 #define SIENA_MCDI_DOORBELL(_emip)              \
  42         (((emip)->emi_port == 1)             \
  43         ? MC_SMEM_P0_DOORBELL_OFST >> 2           \
  44         : MC_SMEM_P1_DOORBELL_OFST >> 2)
  45 
  46 #define SIENA_MCDI_STATUS(_emip)                \
  47         (((emip)->emi_port == 1)             \
  48         ? MC_SMEM_P0_STATUS_OFST >> 2             \
  49         : MC_SMEM_P1_STATUS_OFST >> 2)
  50 
  51 
  52                         void
  53 siena_mcdi_send_request(
  54         __in            efx_nic_t *enp,
  55         __in            void *hdrp,
  56         __in            size_t hdr_len,
  57         __in            void *sdup,
  58         __in            size_t sdu_len)
  59 {
  60         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
  61         efx_dword_t dword;
  62         unsigned int pdur;
  63         unsigned int dbr;
  64         unsigned int pos;
  65 
  66         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
  67 
  68         EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
  69         pdur = SIENA_MCDI_PDU(emip);
  70         dbr = SIENA_MCDI_DOORBELL(emip);
  71 
  72         /* Write the header */
  73         EFSYS_ASSERT3U(hdr_len, ==, sizeof (efx_dword_t));
  74         dword = *(efx_dword_t *)hdrp;
  75         EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, pdur, &dword, B_TRUE);
  76 
  77         /* Write the payload */
  78         for (pos = 0; pos < sdu_len; pos += sizeof (efx_dword_t)) {
  79                 dword = *(efx_dword_t *)((uint8_t *)sdup + pos);
  80                 EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM,
  81                     pdur + 1 + (pos >> 2), &dword, B_FALSE);
  82         }
  83 
  84         /* Ring the doorbell */
  85         EFX_POPULATE_DWORD_1(dword, EFX_DWORD_0, 0xd004be11);
  86         EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, dbr, &dword, B_FALSE);
  87 }
  88 
  89                         efx_rc_t
  90 siena_mcdi_poll_reboot(
  91         __in            efx_nic_t *enp)
  92 {
  93 #if 1
  94         /*
  95          * XXX Bug 25922, bug 26099: This function is not being used
  96          * properly.  Until its callers are fixed, it should always
  97          * return 0.
  98          */
  99         _NOTE(ARGUNUSED(enp))
 100         return (0);
 101 #else
 102         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 103         unsigned int rebootr;
 104         efx_dword_t dword;
 105         uint32_t value;
 106 
 107         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
 108         EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
 109         rebootr = SIENA_MCDI_STATUS(emip);
 110 
 111         EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
 112         value = EFX_DWORD_FIELD(dword, EFX_DWORD_0);
 113 
 114         if (value == 0)
 115                 return (0);
 116 
 117         EFX_ZERO_DWORD(dword);
 118         EFX_BAR_TBL_WRITED(enp, FR_CZ_MC_TREG_SMEM, rebootr, &dword, B_FALSE);
 119 
 120         if (value == MC_STATUS_DWORD_ASSERT)
 121                 return (EINTR);
 122         else
 123                 return (EIO);
 124 #endif
 125 }
 126 
 127 extern  __checkReturn   boolean_t
 128 siena_mcdi_poll_response(
 129         __in            efx_nic_t *enp)
 130 {
 131         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 132         efx_dword_t hdr;
 133         unsigned int pdur;
 134 
 135         EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
 136         pdur = SIENA_MCDI_PDU(emip);
 137 
 138         EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM, pdur, &hdr, B_FALSE);
 139         return (EFX_DWORD_FIELD(hdr, MCDI_HEADER_RESPONSE) ? B_TRUE : B_FALSE);
 140 }
 141 
 142                         void
 143 siena_mcdi_read_response(
 144         __in                    efx_nic_t *enp,
 145         __out_bcount(length)    void *bufferp,
 146         __in                    size_t offset,
 147         __in                    size_t length)
 148 {
 149         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 150         unsigned int pdur;
 151         unsigned int pos;
 152         efx_dword_t data;
 153 
 154         EFSYS_ASSERT(emip->emi_port == 1 || emip->emi_port == 2);
 155         pdur = SIENA_MCDI_PDU(emip);
 156 
 157         for (pos = 0; pos < length; pos += sizeof (efx_dword_t)) {
 158                 EFX_BAR_TBL_READD(enp, FR_CZ_MC_TREG_SMEM,
 159                     pdur + ((offset + pos) >> 2), &data, B_FALSE);
 160                 (void) memcpy((uint8_t *)bufferp + pos, &data,
 161                     MIN(sizeof (data), length - pos));
 162         }
 163 }
 164 
 165         __checkReturn   efx_rc_t
 166 siena_mcdi_init(
 167         __in            efx_nic_t *enp,
 168         __in            const efx_mcdi_transport_t *mtp)
 169 {
 170         efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
 171         efx_oword_t oword;
 172         unsigned int portnum;
 173         efx_rc_t rc;
 174 
 175         _NOTE(ARGUNUSED(mtp));
 176 
 177         EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
 178 
 179         /* Determine the port number to use for MCDI */
 180         EFX_BAR_READO(enp, FR_AZ_CS_DEBUG_REG, &oword);
 181         portnum = EFX_OWORD_FIELD(oword, FRF_CZ_CS_PORT_NUM);
 182 
 183         if (portnum == 0) {
 184                 /* Presumably booted from ROM; only MCDI port 1 will work */
 185                 emip->emi_port = 1;
 186         } else if (portnum <= 2) {
 187                 emip->emi_port = portnum;
 188         } else {
 189                 rc = EINVAL;
 190                 goto fail1;
 191         }
 192 
 193         /* Siena BootROM and firmware only support MCDIv1 */
 194         emip->emi_max_version = 1;
 195 
 196         /*
 197          * Wipe the atomic reboot status so subsequent MCDI requests succeed.
 198          * BOOT_STATUS is preserved so eno_nic_probe() can boot out of the
 199          * assertion handler.
 200          */
 201         (void) siena_mcdi_poll_reboot(enp);
 202 
 203         return (0);
 204 
 205 fail1:
 206         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 207 
 208         return (rc);
 209 }
 210 
 211                         void
 212 siena_mcdi_fini(
 213         __in            efx_nic_t *enp)
 214 {
 215         _NOTE(ARGUNUSED(enp));
 216 }
 217 
 218         __checkReturn   efx_rc_t
 219 siena_mcdi_feature_supported(
 220         __in            efx_nic_t *enp,
 221         __in            efx_mcdi_feature_id_t id,
 222         __out           boolean_t *supportedp)
 223 {
 224         efx_rc_t rc;
 225 
 226         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_SIENA);
 227 
 228         switch (id) {
 229         case EFX_MCDI_FEATURE_FW_UPDATE:
 230         case EFX_MCDI_FEATURE_LINK_CONTROL:
 231         case EFX_MCDI_FEATURE_MACADDR_CHANGE:
 232         case EFX_MCDI_FEATURE_MAC_SPOOFING:
 233                 *supportedp = B_TRUE;
 234                 break;
 235         default:
 236                 rc = ENOTSUP;
 237                 goto fail1;
 238         }
 239 
 240         return (0);
 241 
 242 fail1:
 243         EFSYS_PROBE1(fail1, efx_rc_t, rc);
 244 
 245         return (rc);
 246 }
 247 
 248 #endif  /* EFSYS_OPT_SIENA && EFSYS_OPT_MCDI */