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