1 /*
   2  * Copyright 2009 Solarflare Communications Inc.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
  14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  23  * SUCH DAMAGE.
  24  */
  25 
  26 /*
  27  * Falcon conviniently uses an EEPROM to store it's VPD configuration,
  28  * and it stores the VPD contents in native VPD format. This code does
  29  * not cope with the presence of an RW block in VPD at all.
  30  */
  31 
  32 #include "efsys.h"
  33 #include "efx.h"
  34 #include "falcon_nvram.h"
  35 #include "efx_types.h"
  36 #include "efx_impl.h"
  37 
  38 #if EFSYS_OPT_FALCON
  39 
  40 #if EFSYS_OPT_VPD
  41 
  42 typedef struct {
  43         size_t          fvpdd_base;
  44         size_t          fvpdd_size;
  45         boolean_t       fvpdd_writable;
  46 } falcon_vpd_dimension_t;
  47 
  48 static                          void
  49 falcon_vpd_dimension(
  50         __in                    efx_nic_t *enp,
  51         __out                   falcon_vpd_dimension_t *dimp)
  52 {
  53         efx_oword_t oword;
  54 
  55 #if EFSYS_OPT_FALCON_NIC_CFG_OVERRIDE
  56         if (enp->en_u.falcon.enu_forced_cfg != NULL) {
  57                 memcpy(&oword, (enp->en_u.falcon.enu_forced_cfg
  58                                 + EE_VPD_CFG0_REG_SF_OFST), sizeof (oword));
  59         }
  60         else
  61 #endif  /* EFSYS_OPT_FALCON_NIC_CFG_OVERRIDE */
  62         {
  63                 EFX_BAR_READO(enp, FR_AB_EE_VPD_CFG0_REG, &oword);
  64         }
  65 
  66         dimp->fvpdd_base = EFX_OWORD_FIELD(oword, FRF_AB_EE_VPD_BASE);
  67         dimp->fvpdd_size = EFX_OWORD_FIELD(oword, FRF_AB_EE_VPD_LENGTH);
  68         if (dimp->fvpdd_size != 0)
  69                 /* Non-zero "lengths" are actually maximum dword offsets */
  70                 dimp->fvpdd_size += 4;
  71         dimp->fvpdd_writable =
  72                 EFX_OWORD_FIELD(oword, FRF_AB_EE_VPDW_LENGTH) != 0;
  73 }
  74 
  75         __checkReturn           int
  76 falcon_vpd_size(
  77         __in                    efx_nic_t *enp,
  78         __out                   size_t *sizep)
  79 {
  80         falcon_vpd_dimension_t dim;
  81         int rc;
  82 
  83         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
  84 
  85         falcon_vpd_dimension(enp, &dim);
  86         if (dim.fvpdd_size == 0 || dim.fvpdd_writable) {
  87                 rc = ENOTSUP;
  88                 goto fail1;
  89         }
  90 
  91         *sizep = dim.fvpdd_size;
  92 
  93         return (0);
  94 
  95 fail1:
  96         EFSYS_PROBE1(fail1, int, rc);
  97 
  98         *sizep = 0;
  99 
 100         return (rc);
 101 }
 102 
 103         __checkReturn           int
 104 falcon_vpd_read(
 105         __in                    efx_nic_t *enp,
 106         __out_bcount(size)      caddr_t data,
 107         __in                    size_t size)
 108 {
 109         falcon_vpd_dimension_t dim;
 110         int rc;
 111 
 112         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 113 
 114         falcon_vpd_dimension(enp, &dim);
 115         if (size < dim.fvpdd_size || size == 0) {
 116                 rc = ENOTSUP;
 117                 goto fail1;
 118         }
 119 
 120         if ((rc = falcon_spi_dev_read(enp, FALCON_SPI_EEPROM,
 121                     dim.fvpdd_base, data, size)) != 0)
 122                 goto fail2;
 123 
 124         return (0);
 125 
 126 fail2:
 127         EFSYS_PROBE(fail2);
 128 fail1:
 129         EFSYS_PROBE1(fail1, int, rc);
 130 
 131         return (rc);
 132 }
 133 
 134         __checkReturn           int
 135 falcon_vpd_verify(
 136         __in                    efx_nic_t *enp,
 137         __in_bcount(size)       caddr_t data,
 138         __in                    size_t size)
 139 {
 140         falcon_vpd_dimension_t dim;
 141         boolean_t cksummed;
 142         int rc;
 143 
 144         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 145 
 146         falcon_vpd_dimension(enp, &dim);
 147         EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
 148         EFSYS_ASSERT(!dim.fvpdd_writable);
 149 
 150         if ((rc = efx_vpd_hunk_verify(data, size, &cksummed)) != 0)
 151                 goto fail1;
 152 
 153         if (!cksummed) {
 154                 rc = EFAULT;
 155                 goto fail2;
 156         }
 157 
 158         return (0);
 159 
 160 fail2:
 161         EFSYS_PROBE(fail2);
 162 fail1:
 163         EFSYS_PROBE1(fail1, int, rc);
 164 
 165         return (rc);
 166 }
 167 
 168         __checkReturn           int
 169 falcon_vpd_get(
 170         __in                    efx_nic_t *enp,
 171         __in_bcount(size)       caddr_t data,
 172         __in                    size_t size,
 173         __inout                 efx_vpd_value_t *evvp)
 174 {
 175         falcon_vpd_dimension_t dim;
 176         unsigned int offset;
 177         uint8_t length;
 178         int rc;
 179 
 180         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 181 
 182         if (evvp->evv_tag != EFX_VPD_ID && evvp->evv_tag != EFX_VPD_RO) {
 183                 rc = EINVAL;
 184                 goto fail1;
 185         }
 186 
 187         falcon_vpd_dimension(enp, &dim);
 188         EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
 189         EFSYS_ASSERT(!dim.fvpdd_writable);
 190 
 191         if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
 192             evvp->evv_keyword, &offset, &length)) != 0)
 193                 goto fail2;
 194 
 195         /* Copy out */
 196         evvp->evv_length = length;
 197         memcpy(evvp->evv_value, data + offset, length);
 198 
 199         return (0);
 200 
 201 fail2:
 202         EFSYS_PROBE(fail2);
 203 fail1:
 204         EFSYS_PROBE1(fail1, int, rc);
 205 
 206         return (rc);
 207 }
 208 
 209         __checkReturn           int
 210 falcon_vpd_set(
 211         __in                    efx_nic_t *enp,
 212         __in_bcount(size)       caddr_t data,
 213         __in                    size_t size,
 214         __in                    efx_vpd_value_t *evvp)
 215 {
 216         falcon_vpd_dimension_t dim;
 217         int rc;
 218 
 219         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 220 
 221         falcon_vpd_dimension(enp, &dim);
 222         EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
 223 
 224         if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
 225                 goto fail1;
 226 
 227 fail1:
 228         EFSYS_PROBE1(fail1, int, rc);
 229 
 230         return (rc);
 231 }
 232 
 233         __checkReturn           int
 234 falcon_vpd_next(
 235         __in                    efx_nic_t *enp,
 236         __in_bcount(size)       caddr_t data,
 237         __in                    size_t size,
 238         __out                   efx_vpd_value_t *evvp,
 239         __inout                 unsigned int *contp)
 240 {
 241         falcon_vpd_dimension_t dim;
 242         unsigned int offset;
 243         int rc;
 244 
 245         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 246 
 247         falcon_vpd_dimension(enp, &dim);
 248         EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
 249         EFSYS_ASSERT(!dim.fvpdd_writable);
 250 
 251         /* Find the (tag, keyword) */
 252         if ((rc = efx_vpd_hunk_next(data, size, &evvp->evv_tag,
 253             &evvp->evv_keyword, &offset, &evvp->evv_length, contp)) != 0)
 254                 goto fail1;
 255 
 256         /* Copyout */
 257         memcpy(evvp->evv_value, data + offset, evvp->evv_length);
 258 
 259         return (0);
 260 
 261 fail1:
 262         EFSYS_PROBE1(fail1, int, rc);
 263 
 264         return (rc);
 265 }
 266 
 267         __checkReturn           int
 268 falcon_vpd_write(
 269         __in                    efx_nic_t *enp,
 270         __in_bcount(size)       caddr_t data,
 271         __in                    size_t size)
 272 {
 273         falcon_vpd_dimension_t dim;
 274         int rc;
 275 
 276         EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
 277 
 278         falcon_vpd_dimension(enp, &dim);
 279         if (dim.fvpdd_size != size) {
 280                 /* User hasn't provided sufficient data */
 281                 rc = EINVAL;
 282                 goto fail1;
 283         }
 284 
 285         if ((rc = falcon_spi_dev_write(enp, FALCON_SPI_EEPROM,
 286                     dim.fvpdd_base, data, dim.fvpdd_size)) != 0)
 287                 goto fail2;
 288 
 289         return (0);
 290 
 291 fail2:
 292         EFSYS_PROBE(fail2);
 293 fail1:
 294         EFSYS_PROBE1(fail1, int, rc);
 295 
 296         return (rc);
 297 }
 298 
 299 #endif  /* EFSYS_OPT_FALCON */
 300 
 301 #endif  /* EFSYS_OPT_VPD */