1227569Sphilip/*- 2300607Sarybchik * Copyright (c) 2009-2016 Solarflare Communications Inc. 3283514Sarybchik * All rights reserved. 4227569Sphilip * 5227569Sphilip * Redistribution and use in source and binary forms, with or without 6283514Sarybchik * modification, are permitted provided that the following conditions are met: 7227569Sphilip * 8283514Sarybchik * 1. Redistributions of source code must retain the above copyright notice, 9283514Sarybchik * this list of conditions and the following disclaimer. 10283514Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice, 11283514Sarybchik * this list of conditions and the following disclaimer in the documentation 12283514Sarybchik * and/or other materials provided with the distribution. 13283514Sarybchik * 14283514Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15283514Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 16283514Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 17283514Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 18283514Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 19283514Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 20283514Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 21283514Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 22283514Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 23283514Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24283514Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25283514Sarybchik * 26283514Sarybchik * The views and conclusions contained in the software and documentation are 27283514Sarybchik * those of the authors and should not be interpreted as representing official 28283514Sarybchik * policies, either expressed or implied, of the FreeBSD Project. 29227569Sphilip */ 30227569Sphilip 31228078Sphilip#include <sys/cdefs.h> 32228078Sphilip__FBSDID("$FreeBSD: stable/11/sys/dev/sfxge/common/siena_nvram.c 342445 2018-12-25 07:27:45Z arybchik $"); 33228078Sphilip 34227569Sphilip#include "efx.h" 35227569Sphilip#include "efx_impl.h" 36227569Sphilip 37227569Sphilip#if EFSYS_OPT_SIENA 38227569Sphilip 39227569Sphilip#if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM 40227569Sphilip 41291436Sarybchik __checkReturn efx_rc_t 42227569Sphilipsiena_nvram_partn_size( 43227569Sphilip __in efx_nic_t *enp, 44293770Sarybchik __in uint32_t partn, 45227569Sphilip __out size_t *sizep) 46227569Sphilip{ 47291436Sarybchik efx_rc_t rc; 48227569Sphilip 49227569Sphilip if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) { 50227569Sphilip rc = ENOTSUP; 51227569Sphilip goto fail1; 52227569Sphilip } 53227569Sphilip 54291746Sarybchik if ((rc = efx_mcdi_nvram_info(enp, partn, sizep, 55291746Sarybchik NULL, NULL, NULL)) != 0) { 56227569Sphilip goto fail2; 57227569Sphilip } 58227569Sphilip 59227569Sphilip return (0); 60227569Sphilip 61227569Sphilipfail2: 62227569Sphilip EFSYS_PROBE(fail2); 63227569Sphilipfail1: 64291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 65227569Sphilip 66227569Sphilip return (rc); 67227569Sphilip} 68227569Sphilip 69291436Sarybchik __checkReturn efx_rc_t 70227569Sphilipsiena_nvram_partn_lock( 71227569Sphilip __in efx_nic_t *enp, 72293770Sarybchik __in uint32_t partn) 73227569Sphilip{ 74291436Sarybchik efx_rc_t rc; 75227569Sphilip 76283514Sarybchik if ((rc = efx_mcdi_nvram_update_start(enp, partn)) != 0) { 77227569Sphilip goto fail1; 78227569Sphilip } 79227569Sphilip 80227569Sphilip return (0); 81227569Sphilip 82227569Sphilipfail1: 83291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 84227569Sphilip 85227569Sphilip return (rc); 86227569Sphilip} 87227569Sphilip 88291436Sarybchik __checkReturn efx_rc_t 89227569Sphilipsiena_nvram_partn_read( 90227569Sphilip __in efx_nic_t *enp, 91293770Sarybchik __in uint32_t partn, 92227569Sphilip __in unsigned int offset, 93227569Sphilip __out_bcount(size) caddr_t data, 94227569Sphilip __in size_t size) 95227569Sphilip{ 96227569Sphilip size_t chunk; 97291436Sarybchik efx_rc_t rc; 98227569Sphilip 99227569Sphilip while (size > 0) { 100227569Sphilip chunk = MIN(size, SIENA_NVRAM_CHUNK); 101227569Sphilip 102294309Sarybchik if ((rc = efx_mcdi_nvram_read(enp, partn, offset, data, chunk, 103294309Sarybchik MC_CMD_NVRAM_READ_IN_V2_DEFAULT)) != 0) { 104227569Sphilip goto fail1; 105227569Sphilip } 106227569Sphilip 107227569Sphilip size -= chunk; 108227569Sphilip data += chunk; 109227569Sphilip offset += chunk; 110227569Sphilip } 111227569Sphilip 112227569Sphilip return (0); 113227569Sphilip 114227569Sphilipfail1: 115291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 116227569Sphilip 117227569Sphilip return (rc); 118227569Sphilip} 119227569Sphilip 120291436Sarybchik __checkReturn efx_rc_t 121227569Sphilipsiena_nvram_partn_erase( 122227569Sphilip __in efx_nic_t *enp, 123293770Sarybchik __in uint32_t partn, 124227569Sphilip __in unsigned int offset, 125227569Sphilip __in size_t size) 126227569Sphilip{ 127291436Sarybchik efx_rc_t rc; 128227569Sphilip 129283514Sarybchik if ((rc = efx_mcdi_nvram_erase(enp, partn, offset, size)) != 0) { 130227569Sphilip goto fail1; 131227569Sphilip } 132227569Sphilip 133227569Sphilip return (0); 134227569Sphilip 135227569Sphilipfail1: 136291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 137227569Sphilip 138227569Sphilip return (rc); 139227569Sphilip} 140227569Sphilip 141291436Sarybchik __checkReturn efx_rc_t 142227569Sphilipsiena_nvram_partn_write( 143227569Sphilip __in efx_nic_t *enp, 144293770Sarybchik __in uint32_t partn, 145227569Sphilip __in unsigned int offset, 146227569Sphilip __out_bcount(size) caddr_t data, 147227569Sphilip __in size_t size) 148227569Sphilip{ 149227569Sphilip size_t chunk; 150291436Sarybchik efx_rc_t rc; 151227569Sphilip 152227569Sphilip while (size > 0) { 153227569Sphilip chunk = MIN(size, SIENA_NVRAM_CHUNK); 154227569Sphilip 155283514Sarybchik if ((rc = efx_mcdi_nvram_write(enp, partn, offset, 156283514Sarybchik data, chunk)) != 0) { 157227569Sphilip goto fail1; 158227569Sphilip } 159227569Sphilip 160227569Sphilip size -= chunk; 161227569Sphilip data += chunk; 162227569Sphilip offset += chunk; 163227569Sphilip } 164227569Sphilip 165227569Sphilip return (0); 166227569Sphilip 167227569Sphilipfail1: 168291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 169227569Sphilip 170227569Sphilip return (rc); 171227569Sphilip} 172227569Sphilip 173311481Sarybchik __checkReturn efx_rc_t 174227569Sphilipsiena_nvram_partn_unlock( 175227569Sphilip __in efx_nic_t *enp, 176293770Sarybchik __in uint32_t partn) 177227569Sphilip{ 178283514Sarybchik boolean_t reboot; 179291436Sarybchik efx_rc_t rc; 180227569Sphilip 181227569Sphilip /* 182227569Sphilip * Reboot into the new image only for PHYs. The driver has to 183227569Sphilip * explicitly cope with an MC reboot after a firmware update. 184227569Sphilip */ 185227569Sphilip reboot = (partn == MC_CMD_NVRAM_TYPE_PHY_PORT0 || 186227569Sphilip partn == MC_CMD_NVRAM_TYPE_PHY_PORT1 || 187227569Sphilip partn == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO); 188227569Sphilip 189311481Sarybchik rc = efx_mcdi_nvram_update_finish(enp, partn, reboot, NULL); 190311481Sarybchik if (rc != 0) 191227569Sphilip goto fail1; 192227569Sphilip 193311481Sarybchik return (0); 194227569Sphilip 195227569Sphilipfail1: 196291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 197311481Sarybchik 198311481Sarybchik return (rc); 199227569Sphilip} 200227569Sphilip 201227569Sphilip#endif /* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */ 202227569Sphilip 203227569Sphilip#if EFSYS_OPT_NVRAM 204227569Sphilip 205227569Sphiliptypedef struct siena_parttbl_entry_s { 206227569Sphilip unsigned int partn; 207227569Sphilip unsigned int port; 208227569Sphilip efx_nvram_type_t nvtype; 209227569Sphilip} siena_parttbl_entry_t; 210227569Sphilip 211227569Sphilipstatic siena_parttbl_entry_t siena_parttbl[] = { 212227569Sphilip {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO, 1, EFX_NVRAM_NULLPHY}, 213227569Sphilip {MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO, 2, EFX_NVRAM_NULLPHY}, 214227569Sphilip {MC_CMD_NVRAM_TYPE_MC_FW, 1, EFX_NVRAM_MC_FIRMWARE}, 215227569Sphilip {MC_CMD_NVRAM_TYPE_MC_FW, 2, EFX_NVRAM_MC_FIRMWARE}, 216227569Sphilip {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP, 1, EFX_NVRAM_MC_GOLDEN}, 217227569Sphilip {MC_CMD_NVRAM_TYPE_MC_FW_BACKUP, 2, EFX_NVRAM_MC_GOLDEN}, 218227569Sphilip {MC_CMD_NVRAM_TYPE_EXP_ROM, 1, EFX_NVRAM_BOOTROM}, 219227569Sphilip {MC_CMD_NVRAM_TYPE_EXP_ROM, 2, EFX_NVRAM_BOOTROM}, 220227569Sphilip {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0, 1, EFX_NVRAM_BOOTROM_CFG}, 221227569Sphilip {MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1, 2, EFX_NVRAM_BOOTROM_CFG}, 222227569Sphilip {MC_CMD_NVRAM_TYPE_PHY_PORT0, 1, EFX_NVRAM_PHY}, 223227569Sphilip {MC_CMD_NVRAM_TYPE_PHY_PORT1, 2, EFX_NVRAM_PHY}, 224279173Sarybchik {MC_CMD_NVRAM_TYPE_FPGA, 1, EFX_NVRAM_FPGA}, 225279173Sarybchik {MC_CMD_NVRAM_TYPE_FPGA, 2, EFX_NVRAM_FPGA}, 226279173Sarybchik {MC_CMD_NVRAM_TYPE_FPGA_BACKUP, 1, EFX_NVRAM_FPGA_BACKUP}, 227279173Sarybchik {MC_CMD_NVRAM_TYPE_FPGA_BACKUP, 2, EFX_NVRAM_FPGA_BACKUP}, 228279173Sarybchik {MC_CMD_NVRAM_TYPE_FC_FW, 1, EFX_NVRAM_FCFW}, 229279173Sarybchik {MC_CMD_NVRAM_TYPE_FC_FW, 2, EFX_NVRAM_FCFW}, 230279173Sarybchik {MC_CMD_NVRAM_TYPE_CPLD, 1, EFX_NVRAM_CPLD}, 231279173Sarybchik {MC_CMD_NVRAM_TYPE_CPLD, 2, EFX_NVRAM_CPLD}, 232293900Sarybchik {MC_CMD_NVRAM_TYPE_LICENSE, 1, EFX_NVRAM_LICENSE}, 233293900Sarybchik {MC_CMD_NVRAM_TYPE_LICENSE, 2, EFX_NVRAM_LICENSE} 234227569Sphilip}; 235227569Sphilip 236293810Sarybchik __checkReturn efx_rc_t 237293810Sarybchiksiena_nvram_type_to_partn( 238227569Sphilip __in efx_nic_t *enp, 239293810Sarybchik __in efx_nvram_type_t type, 240293810Sarybchik __out uint32_t *partnp) 241227569Sphilip{ 242283514Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 243283514Sarybchik unsigned int i; 244227569Sphilip 245227569Sphilip EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES); 246293810Sarybchik EFSYS_ASSERT(partnp != NULL); 247227569Sphilip 248283514Sarybchik for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) { 249293810Sarybchik siena_parttbl_entry_t *entry = &siena_parttbl[i]; 250283514Sarybchik 251293810Sarybchik if (entry->port == emip->emi_port && entry->nvtype == type) { 252293810Sarybchik *partnp = entry->partn; 253293810Sarybchik return (0); 254293810Sarybchik } 255227569Sphilip } 256227569Sphilip 257293810Sarybchik return (ENOTSUP); 258227569Sphilip} 259227569Sphilip 260293810Sarybchik 261227569Sphilip#if EFSYS_OPT_DIAG 262227569Sphilip 263291436Sarybchik __checkReturn efx_rc_t 264227569Sphilipsiena_nvram_test( 265227569Sphilip __in efx_nic_t *enp) 266227569Sphilip{ 267283514Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 268227569Sphilip siena_parttbl_entry_t *entry; 269283514Sarybchik unsigned int i; 270291436Sarybchik efx_rc_t rc; 271227569Sphilip 272227569Sphilip /* 273227569Sphilip * Iterate over the list of supported partition types 274227569Sphilip * applicable to *this* port 275227569Sphilip */ 276283514Sarybchik for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) { 277283514Sarybchik entry = &siena_parttbl[i]; 278283514Sarybchik 279227569Sphilip if (entry->port != emip->emi_port || 280227569Sphilip !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn))) 281227569Sphilip continue; 282227569Sphilip 283283514Sarybchik if ((rc = efx_mcdi_nvram_test(enp, entry->partn)) != 0) { 284227569Sphilip goto fail1; 285227569Sphilip } 286227569Sphilip } 287227569Sphilip 288227569Sphilip return (0); 289227569Sphilip 290227569Sphilipfail1: 291291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 292227569Sphilip 293227569Sphilip return (rc); 294227569Sphilip} 295227569Sphilip 296227569Sphilip#endif /* EFSYS_OPT_DIAG */ 297227569Sphilip 298227569Sphilip 299227569Sphilip#define SIENA_DYNAMIC_CFG_SIZE(_nitems) \ 300227569Sphilip (sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) * \ 301227569Sphilip sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0]))) 302227569Sphilip 303291436Sarybchik __checkReturn efx_rc_t 304227569Sphilipsiena_nvram_get_dynamic_cfg( 305227569Sphilip __in efx_nic_t *enp, 306293770Sarybchik __in uint32_t partn, 307227569Sphilip __in boolean_t vpd, 308227569Sphilip __out siena_mc_dynamic_config_hdr_t **dcfgp, 309227569Sphilip __out size_t *sizep) 310227569Sphilip{ 311283514Sarybchik siena_mc_dynamic_config_hdr_t *dcfg = NULL; 312227569Sphilip size_t size; 313227569Sphilip uint8_t cksum; 314227569Sphilip unsigned int vpd_offset; 315227569Sphilip unsigned int vpd_length; 316227569Sphilip unsigned int hdr_length; 317227569Sphilip unsigned int nversions; 318227569Sphilip unsigned int pos; 319227569Sphilip unsigned int region; 320291436Sarybchik efx_rc_t rc; 321227569Sphilip 322227569Sphilip EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 || 323227569Sphilip partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1); 324227569Sphilip 325227569Sphilip /* 326227569Sphilip * Allocate sufficient memory for the entire dynamiccfg area, even 327227569Sphilip * if we're not actually going to read in the VPD. 328227569Sphilip */ 329227569Sphilip if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0) 330227569Sphilip goto fail1; 331227569Sphilip 332227569Sphilip EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg); 333227569Sphilip if (dcfg == NULL) { 334227569Sphilip rc = ENOMEM; 335227569Sphilip goto fail2; 336227569Sphilip } 337227569Sphilip 338227569Sphilip if ((rc = siena_nvram_partn_read(enp, partn, 0, 339227569Sphilip (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0) 340227569Sphilip goto fail3; 341227569Sphilip 342227569Sphilip /* Verify the magic */ 343227569Sphilip if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0) 344227569Sphilip != SIENA_MC_DYNAMIC_CONFIG_MAGIC) 345227569Sphilip goto invalid1; 346227569Sphilip 347298955Spfg /* All future versions of the structure must be backwards compatible */ 348227569Sphilip EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0); 349227569Sphilip 350227569Sphilip hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0); 351227569Sphilip nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0); 352227569Sphilip vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0); 353227569Sphilip vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0); 354227569Sphilip 355227569Sphilip /* Verify the hdr doesn't overflow the partn size */ 356227569Sphilip if (hdr_length > size || vpd_offset > size || vpd_length > size || 357227569Sphilip vpd_length + vpd_offset > size) 358227569Sphilip goto invalid2; 359227569Sphilip 360227569Sphilip /* Verify the header has room for all it's versions */ 361227569Sphilip if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) || 362227569Sphilip hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions)) 363227569Sphilip goto invalid3; 364227569Sphilip 365227569Sphilip /* 366227569Sphilip * Read the remaining portion of the dcfg, either including 367227569Sphilip * the whole of VPD (there is no vpd length in this structure, 368227569Sphilip * so we have to parse each tag), or just the dcfg header itself 369227569Sphilip */ 370227569Sphilip region = vpd ? vpd_offset + vpd_length : hdr_length; 371227569Sphilip if (region > SIENA_NVRAM_CHUNK) { 372227569Sphilip if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK, 373227569Sphilip (caddr_t)dcfg + SIENA_NVRAM_CHUNK, 374227569Sphilip region - SIENA_NVRAM_CHUNK)) != 0) 375227569Sphilip goto fail4; 376227569Sphilip } 377227569Sphilip 378227569Sphilip /* Verify checksum */ 379227569Sphilip cksum = 0; 380227569Sphilip for (pos = 0; pos < hdr_length; pos++) 381227569Sphilip cksum += ((uint8_t *)dcfg)[pos]; 382227569Sphilip if (cksum != 0) 383227569Sphilip goto invalid4; 384227569Sphilip 385227569Sphilip goto done; 386227569Sphilip 387227569Sphilipinvalid4: 388227569Sphilip EFSYS_PROBE(invalid4); 389227569Sphilipinvalid3: 390227569Sphilip EFSYS_PROBE(invalid3); 391227569Sphilipinvalid2: 392227569Sphilip EFSYS_PROBE(invalid2); 393227569Sphilipinvalid1: 394227569Sphilip EFSYS_PROBE(invalid1); 395227569Sphilip 396227569Sphilip /* 397227569Sphilip * Construct a new "null" dcfg, with an empty version vector, 398227569Sphilip * and an empty VPD chunk trailing. This has the neat side effect 399227569Sphilip * of testing the exception paths in the write path. 400227569Sphilip */ 401227569Sphilip EFX_POPULATE_DWORD_1(dcfg->magic, 402227569Sphilip EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC); 403227569Sphilip EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg)); 404227569Sphilip EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0, 405227569Sphilip SIENA_MC_DYNAMIC_CONFIG_VERSION); 406227569Sphilip EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset, 407227569Sphilip EFX_DWORD_0, sizeof (*dcfg)); 408227569Sphilip EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0); 409227569Sphilip EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0); 410227569Sphilip 411227569Sphilipdone: 412227569Sphilip *dcfgp = dcfg; 413227569Sphilip *sizep = size; 414227569Sphilip 415227569Sphilip return (0); 416227569Sphilip 417227569Sphilipfail4: 418227569Sphilip EFSYS_PROBE(fail4); 419227569Sphilipfail3: 420227569Sphilip EFSYS_PROBE(fail3); 421227569Sphilip 422227569Sphilip EFSYS_KMEM_FREE(enp->en_esip, size, dcfg); 423227569Sphilip 424283514Sarybchikfail2: 425283514Sarybchik EFSYS_PROBE(fail2); 426227569Sphilipfail1: 427291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 428227569Sphilip 429227569Sphilip return (rc); 430227569Sphilip} 431227569Sphilip 432291436Sarybchik __checkReturn efx_rc_t 433227569Sphilipsiena_nvram_get_subtype( 434227569Sphilip __in efx_nic_t *enp, 435293770Sarybchik __in uint32_t partn, 436227569Sphilip __out uint32_t *subtypep) 437227569Sphilip{ 438227569Sphilip efx_mcdi_req_t req; 439342445Sarybchik EFX_MCDI_DECLARE_BUF(payload, MC_CMD_GET_BOARD_CFG_IN_LEN, 440342445Sarybchik MC_CMD_GET_BOARD_CFG_OUT_LENMAX); 441227569Sphilip efx_word_t *fw_list; 442291436Sarybchik efx_rc_t rc; 443227569Sphilip 444227569Sphilip req.emr_cmd = MC_CMD_GET_BOARD_CFG; 445283514Sarybchik req.emr_in_buf = payload; 446283514Sarybchik req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN; 447283514Sarybchik req.emr_out_buf = payload; 448283514Sarybchik req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMAX; 449227569Sphilip 450227569Sphilip efx_mcdi_execute(enp, &req); 451227569Sphilip 452227569Sphilip if (req.emr_rc != 0) { 453227569Sphilip rc = req.emr_rc; 454227569Sphilip goto fail1; 455227569Sphilip } 456227569Sphilip 457278941Sarybchik if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) { 458227569Sphilip rc = EMSGSIZE; 459227569Sphilip goto fail2; 460227569Sphilip } 461227569Sphilip 462278941Sarybchik if (req.emr_out_length_used < 463278941Sarybchik MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST + 464279141Sarybchik (partn + 1) * sizeof (efx_word_t)) { 465278941Sarybchik rc = ENOENT; 466278941Sarybchik goto fail3; 467278941Sarybchik } 468278941Sarybchik 469227569Sphilip fw_list = MCDI_OUT2(req, efx_word_t, 470227569Sphilip GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST); 471227569Sphilip *subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0); 472227569Sphilip 473227569Sphilip return (0); 474227569Sphilip 475278941Sarybchikfail3: 476278941Sarybchik EFSYS_PROBE(fail3); 477227569Sphilipfail2: 478227569Sphilip EFSYS_PROBE(fail2); 479227569Sphilipfail1: 480291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 481227569Sphilip 482227569Sphilip return (rc); 483227569Sphilip} 484227569Sphilip 485291436Sarybchik __checkReturn efx_rc_t 486294251Sarybchiksiena_nvram_partn_get_version( 487227569Sphilip __in efx_nic_t *enp, 488294251Sarybchik __in uint32_t partn, 489227569Sphilip __out uint32_t *subtypep, 490227569Sphilip __out_ecount(4) uint16_t version[4]) 491227569Sphilip{ 492227569Sphilip siena_mc_dynamic_config_hdr_t *dcfg; 493227569Sphilip siena_parttbl_entry_t *entry; 494293770Sarybchik uint32_t dcfg_partn; 495283514Sarybchik unsigned int i; 496291436Sarybchik efx_rc_t rc; 497227569Sphilip 498227569Sphilip if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) { 499227569Sphilip rc = ENOTSUP; 500294251Sarybchik goto fail1; 501227569Sphilip } 502227569Sphilip 503227569Sphilip if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0) 504294251Sarybchik goto fail2; 505227569Sphilip 506227569Sphilip /* 507227569Sphilip * Some partitions are accessible from both ports (for instance BOOTROM) 508227569Sphilip * Find the highest version reported by all dcfg structures on ports 509227569Sphilip * that have access to this partition. 510227569Sphilip */ 511227569Sphilip version[0] = version[1] = version[2] = version[3] = 0; 512283514Sarybchik for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) { 513294251Sarybchik siena_mc_fw_version_t *verp; 514227569Sphilip unsigned int nitems; 515227569Sphilip uint16_t temp[4]; 516227569Sphilip size_t length; 517227569Sphilip 518283514Sarybchik entry = &siena_parttbl[i]; 519227569Sphilip if (entry->partn != partn) 520227569Sphilip continue; 521227569Sphilip 522227569Sphilip dcfg_partn = (entry->port == 1) 523227569Sphilip ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 524227569Sphilip : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1; 525227569Sphilip /* 526227569Sphilip * Ingore missing partitions on port 2, assuming they're due 527330446Seadler * to running on a single port part. 528227569Sphilip */ 529227569Sphilip if ((1 << dcfg_partn) & ~enp->en_u.siena.enu_partn_mask) { 530227569Sphilip if (entry->port == 2) 531227569Sphilip continue; 532227569Sphilip } 533227569Sphilip 534227569Sphilip if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn, 535227569Sphilip B_FALSE, &dcfg, &length)) != 0) 536294251Sarybchik goto fail3; 537227569Sphilip 538227569Sphilip nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, 539227569Sphilip EFX_DWORD_0); 540227569Sphilip if (nitems < entry->partn) 541227569Sphilip goto done; 542227569Sphilip 543294251Sarybchik verp = &dcfg->fw_version[partn]; 544294251Sarybchik temp[0] = EFX_WORD_FIELD(verp->version_w, EFX_WORD_0); 545294251Sarybchik temp[1] = EFX_WORD_FIELD(verp->version_x, EFX_WORD_0); 546294251Sarybchik temp[2] = EFX_WORD_FIELD(verp->version_y, EFX_WORD_0); 547294251Sarybchik temp[3] = EFX_WORD_FIELD(verp->version_z, EFX_WORD_0); 548227569Sphilip if (memcmp(version, temp, sizeof (temp)) < 0) 549227569Sphilip memcpy(version, temp, sizeof (temp)); 550227569Sphilip 551294251Sarybchikdone: 552227569Sphilip EFSYS_KMEM_FREE(enp->en_esip, length, dcfg); 553227569Sphilip } 554227569Sphilip 555227569Sphilip return (0); 556227569Sphilip 557227569Sphilipfail3: 558227569Sphilip EFSYS_PROBE(fail3); 559227569Sphilipfail2: 560227569Sphilip EFSYS_PROBE(fail2); 561227569Sphilipfail1: 562291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 563227569Sphilip 564227569Sphilip return (rc); 565227569Sphilip} 566227569Sphilip 567291436Sarybchik __checkReturn efx_rc_t 568294080Sarybchiksiena_nvram_partn_rw_start( 569227569Sphilip __in efx_nic_t *enp, 570294080Sarybchik __in uint32_t partn, 571227569Sphilip __out size_t *chunk_sizep) 572227569Sphilip{ 573291436Sarybchik efx_rc_t rc; 574227569Sphilip 575294080Sarybchik if ((rc = siena_nvram_partn_lock(enp, partn)) != 0) 576227569Sphilip goto fail1; 577227569Sphilip 578227569Sphilip if (chunk_sizep != NULL) 579227569Sphilip *chunk_sizep = SIENA_NVRAM_CHUNK; 580227569Sphilip 581227569Sphilip return (0); 582227569Sphilip 583227569Sphilipfail1: 584291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 585227569Sphilip 586227569Sphilip return (rc); 587227569Sphilip} 588227569Sphilip 589311481Sarybchik __checkReturn efx_rc_t 590294250Sarybchiksiena_nvram_partn_rw_finish( 591227569Sphilip __in efx_nic_t *enp, 592294250Sarybchik __in uint32_t partn) 593227569Sphilip{ 594311481Sarybchik efx_rc_t rc; 595311481Sarybchik 596311481Sarybchik if ((rc = siena_nvram_partn_unlock(enp, partn)) != 0) 597311481Sarybchik goto fail1; 598311481Sarybchik 599311481Sarybchik return (0); 600311481Sarybchik 601311481Sarybchikfail1: 602311481Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 603311481Sarybchik 604311481Sarybchik return (rc); 605227569Sphilip} 606227569Sphilip 607291436Sarybchik __checkReturn efx_rc_t 608294252Sarybchiksiena_nvram_partn_set_version( 609227569Sphilip __in efx_nic_t *enp, 610294252Sarybchik __in uint32_t partn, 611283514Sarybchik __in_ecount(4) uint16_t version[4]) 612227569Sphilip{ 613293810Sarybchik efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip); 614227569Sphilip siena_mc_dynamic_config_hdr_t *dcfg = NULL; 615293810Sarybchik siena_mc_fw_version_t *fwverp; 616294252Sarybchik uint32_t dcfg_partn; 617293810Sarybchik size_t dcfg_size; 618227569Sphilip unsigned int hdr_length; 619227569Sphilip unsigned int vpd_length; 620227569Sphilip unsigned int vpd_offset; 621227569Sphilip unsigned int nitems; 622227569Sphilip unsigned int required_hdr_length; 623227569Sphilip unsigned int pos; 624227569Sphilip uint8_t cksum; 625227569Sphilip uint32_t subtype; 626227569Sphilip size_t length; 627291436Sarybchik efx_rc_t rc; 628227569Sphilip 629293810Sarybchik dcfg_partn = (emip->emi_port == 1) 630227569Sphilip ? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 631227569Sphilip : MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1; 632227569Sphilip 633293810Sarybchik if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &dcfg_size)) != 0) 634294252Sarybchik goto fail1; 635227569Sphilip 636227569Sphilip if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0) 637227569Sphilip goto fail2; 638227569Sphilip 639227569Sphilip if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn, 640227569Sphilip B_TRUE, &dcfg, &length)) != 0) 641227569Sphilip goto fail3; 642227569Sphilip 643227569Sphilip hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0); 644227569Sphilip nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0); 645227569Sphilip vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0); 646227569Sphilip vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0); 647227569Sphilip 648227569Sphilip /* 649227569Sphilip * NOTE: This function will blatt any fields trailing the version 650227569Sphilip * vector, or the VPD chunk. 651227569Sphilip */ 652293810Sarybchik required_hdr_length = SIENA_DYNAMIC_CFG_SIZE(partn + 1); 653227569Sphilip if (required_hdr_length + vpd_length > length) { 654227569Sphilip rc = ENOSPC; 655227569Sphilip goto fail4; 656227569Sphilip } 657227569Sphilip 658227569Sphilip if (vpd_offset < required_hdr_length) { 659227569Sphilip (void) memmove((caddr_t)dcfg + required_hdr_length, 660227569Sphilip (caddr_t)dcfg + vpd_offset, vpd_length); 661227569Sphilip vpd_offset = required_hdr_length; 662227569Sphilip EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset, 663227569Sphilip EFX_DWORD_0, vpd_offset); 664227569Sphilip } 665227569Sphilip 666227569Sphilip if (hdr_length < required_hdr_length) { 667227569Sphilip (void) memset((caddr_t)dcfg + hdr_length, 0, 668227569Sphilip required_hdr_length - hdr_length); 669227569Sphilip hdr_length = required_hdr_length; 670227569Sphilip EFX_POPULATE_WORD_1(dcfg->length, 671227569Sphilip EFX_WORD_0, hdr_length); 672227569Sphilip } 673227569Sphilip 674227569Sphilip /* Get the subtype to insert into the fw_subtype array */ 675293810Sarybchik if ((rc = siena_nvram_get_subtype(enp, partn, &subtype)) != 0) 676227569Sphilip goto fail5; 677227569Sphilip 678227569Sphilip /* Fill out the new version */ 679293810Sarybchik fwverp = &dcfg->fw_version[partn]; 680293810Sarybchik EFX_POPULATE_DWORD_1(fwverp->fw_subtype, EFX_DWORD_0, subtype); 681293810Sarybchik EFX_POPULATE_WORD_1(fwverp->version_w, EFX_WORD_0, version[0]); 682293810Sarybchik EFX_POPULATE_WORD_1(fwverp->version_x, EFX_WORD_0, version[1]); 683293810Sarybchik EFX_POPULATE_WORD_1(fwverp->version_y, EFX_WORD_0, version[2]); 684293810Sarybchik EFX_POPULATE_WORD_1(fwverp->version_z, EFX_WORD_0, version[3]); 685227569Sphilip 686227569Sphilip /* Update the version count */ 687293810Sarybchik if (nitems < partn + 1) { 688293810Sarybchik nitems = partn + 1; 689227569Sphilip EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, 690227569Sphilip EFX_DWORD_0, nitems); 691227569Sphilip } 692227569Sphilip 693227569Sphilip /* Update the checksum */ 694227569Sphilip cksum = 0; 695227569Sphilip for (pos = 0; pos < hdr_length; pos++) 696227569Sphilip cksum += ((uint8_t *)dcfg)[pos]; 697227569Sphilip dcfg->csum.eb_u8[0] -= cksum; 698227569Sphilip 699227569Sphilip /* Erase and write the new partition */ 700293810Sarybchik if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, dcfg_size)) != 0) 701227569Sphilip goto fail6; 702227569Sphilip 703227569Sphilip /* Write out the new structure to nvram */ 704227569Sphilip if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0, 705227569Sphilip (caddr_t)dcfg, vpd_offset + vpd_length)) != 0) 706227569Sphilip goto fail7; 707227569Sphilip 708227569Sphilip EFSYS_KMEM_FREE(enp->en_esip, length, dcfg); 709227569Sphilip 710227569Sphilip siena_nvram_partn_unlock(enp, dcfg_partn); 711227569Sphilip 712227569Sphilip return (0); 713227569Sphilip 714227569Sphilipfail7: 715227569Sphilip EFSYS_PROBE(fail7); 716227569Sphilipfail6: 717227569Sphilip EFSYS_PROBE(fail6); 718227569Sphilipfail5: 719227569Sphilip EFSYS_PROBE(fail5); 720227569Sphilipfail4: 721227569Sphilip EFSYS_PROBE(fail4); 722227569Sphilip 723227569Sphilip EFSYS_KMEM_FREE(enp->en_esip, length, dcfg); 724227569Sphilipfail3: 725227569Sphilip EFSYS_PROBE(fail3); 726227569Sphilipfail2: 727227569Sphilip EFSYS_PROBE(fail2); 728227569Sphilipfail1: 729291436Sarybchik EFSYS_PROBE1(fail1, efx_rc_t, rc); 730227569Sphilip 731227569Sphilip return (rc); 732227569Sphilip} 733227569Sphilip 734227569Sphilip#endif /* EFSYS_OPT_NVRAM */ 735227569Sphilip 736227569Sphilip#endif /* EFSYS_OPT_SIENA */ 737