siena_nvram.c revision 294250
1227569Sphilip/*-
2283514Sarybchik * Copyright (c) 2009-2015 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: head/sys/dev/sfxge/common/siena_nvram.c 294250 2016-01-18 06:14:43Z 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
102283514Sarybchik		if ((rc = efx_mcdi_nvram_read(enp, partn, offset,
103283514Sarybchik			    data, chunk)) != 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
173227569Sphilip				void
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
189283514Sarybchik	if ((rc = efx_mcdi_nvram_update_finish(enp, partn, reboot)) != 0) {
190227569Sphilip		goto fail1;
191227569Sphilip	}
192227569Sphilip
193227569Sphilip	return;
194227569Sphilip
195227569Sphilipfail1:
196291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
197227569Sphilip}
198227569Sphilip
199227569Sphilip#endif	/* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
200227569Sphilip
201227569Sphilip#if EFSYS_OPT_NVRAM
202227569Sphilip
203227569Sphiliptypedef struct siena_parttbl_entry_s {
204227569Sphilip	unsigned int		partn;
205227569Sphilip	unsigned int		port;
206227569Sphilip	efx_nvram_type_t	nvtype;
207227569Sphilip} siena_parttbl_entry_t;
208227569Sphilip
209227569Sphilipstatic siena_parttbl_entry_t siena_parttbl[] = {
210227569Sphilip	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	1, EFX_NVRAM_NULLPHY},
211227569Sphilip	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	2, EFX_NVRAM_NULLPHY},
212227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW,		1, EFX_NVRAM_MC_FIRMWARE},
213227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW,		2, EFX_NVRAM_MC_FIRMWARE},
214227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	1, EFX_NVRAM_MC_GOLDEN},
215227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	2, EFX_NVRAM_MC_GOLDEN},
216227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM,		1, EFX_NVRAM_BOOTROM},
217227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM,		2, EFX_NVRAM_BOOTROM},
218227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0,	1, EFX_NVRAM_BOOTROM_CFG},
219227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1,	2, EFX_NVRAM_BOOTROM_CFG},
220227569Sphilip	{MC_CMD_NVRAM_TYPE_PHY_PORT0,		1, EFX_NVRAM_PHY},
221227569Sphilip	{MC_CMD_NVRAM_TYPE_PHY_PORT1,		2, EFX_NVRAM_PHY},
222279173Sarybchik	{MC_CMD_NVRAM_TYPE_FPGA,		1, EFX_NVRAM_FPGA},
223279173Sarybchik	{MC_CMD_NVRAM_TYPE_FPGA,		2, EFX_NVRAM_FPGA},
224279173Sarybchik	{MC_CMD_NVRAM_TYPE_FPGA_BACKUP,		1, EFX_NVRAM_FPGA_BACKUP},
225279173Sarybchik	{MC_CMD_NVRAM_TYPE_FPGA_BACKUP,		2, EFX_NVRAM_FPGA_BACKUP},
226279173Sarybchik	{MC_CMD_NVRAM_TYPE_FC_FW,		1, EFX_NVRAM_FCFW},
227279173Sarybchik	{MC_CMD_NVRAM_TYPE_FC_FW,		2, EFX_NVRAM_FCFW},
228279173Sarybchik	{MC_CMD_NVRAM_TYPE_CPLD,		1, EFX_NVRAM_CPLD},
229279173Sarybchik	{MC_CMD_NVRAM_TYPE_CPLD,		2, EFX_NVRAM_CPLD},
230293900Sarybchik	{MC_CMD_NVRAM_TYPE_LICENSE,		1, EFX_NVRAM_LICENSE},
231293900Sarybchik	{MC_CMD_NVRAM_TYPE_LICENSE,		2, EFX_NVRAM_LICENSE}
232227569Sphilip};
233227569Sphilip
234293810Sarybchik	__checkReturn		efx_rc_t
235293810Sarybchiksiena_nvram_type_to_partn(
236227569Sphilip	__in			efx_nic_t *enp,
237293810Sarybchik	__in			efx_nvram_type_t type,
238293810Sarybchik	__out			uint32_t *partnp)
239227569Sphilip{
240283514Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
241283514Sarybchik	unsigned int i;
242227569Sphilip
243227569Sphilip	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
244293810Sarybchik	EFSYS_ASSERT(partnp != NULL);
245227569Sphilip
246283514Sarybchik	for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
247293810Sarybchik		siena_parttbl_entry_t *entry = &siena_parttbl[i];
248283514Sarybchik
249293810Sarybchik		if (entry->port == emip->emi_port && entry->nvtype == type) {
250293810Sarybchik			*partnp = entry->partn;
251293810Sarybchik			return (0);
252293810Sarybchik		}
253227569Sphilip	}
254227569Sphilip
255293810Sarybchik	return (ENOTSUP);
256227569Sphilip}
257227569Sphilip
258293810Sarybchik
259227569Sphilip#if EFSYS_OPT_DIAG
260227569Sphilip
261291436Sarybchik	__checkReturn		efx_rc_t
262227569Sphilipsiena_nvram_test(
263227569Sphilip	__in			efx_nic_t *enp)
264227569Sphilip{
265283514Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
266227569Sphilip	siena_parttbl_entry_t *entry;
267283514Sarybchik	unsigned int i;
268291436Sarybchik	efx_rc_t rc;
269227569Sphilip
270227569Sphilip	/*
271227569Sphilip	 * Iterate over the list of supported partition types
272227569Sphilip	 * applicable to *this* port
273227569Sphilip	 */
274283514Sarybchik	for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
275283514Sarybchik		entry = &siena_parttbl[i];
276283514Sarybchik
277227569Sphilip		if (entry->port != emip->emi_port ||
278227569Sphilip		    !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn)))
279227569Sphilip			continue;
280227569Sphilip
281283514Sarybchik		if ((rc = efx_mcdi_nvram_test(enp, entry->partn)) != 0) {
282227569Sphilip			goto fail1;
283227569Sphilip		}
284227569Sphilip	}
285227569Sphilip
286227569Sphilip	return (0);
287227569Sphilip
288227569Sphilipfail1:
289291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
290227569Sphilip
291227569Sphilip	return (rc);
292227569Sphilip}
293227569Sphilip
294227569Sphilip#endif	/* EFSYS_OPT_DIAG */
295227569Sphilip
296227569Sphilip
297227569Sphilip#define	SIENA_DYNAMIC_CFG_SIZE(_nitems)					\
298227569Sphilip	(sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) *		\
299227569Sphilip	sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0])))
300227569Sphilip
301291436Sarybchik	__checkReturn		efx_rc_t
302227569Sphilipsiena_nvram_get_dynamic_cfg(
303227569Sphilip	__in			efx_nic_t *enp,
304293770Sarybchik	__in			uint32_t partn,
305227569Sphilip	__in			boolean_t vpd,
306227569Sphilip	__out			siena_mc_dynamic_config_hdr_t **dcfgp,
307227569Sphilip	__out			size_t *sizep)
308227569Sphilip{
309283514Sarybchik	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
310227569Sphilip	size_t size;
311227569Sphilip	uint8_t cksum;
312227569Sphilip	unsigned int vpd_offset;
313227569Sphilip	unsigned int vpd_length;
314227569Sphilip	unsigned int hdr_length;
315227569Sphilip	unsigned int nversions;
316227569Sphilip	unsigned int pos;
317227569Sphilip	unsigned int region;
318291436Sarybchik	efx_rc_t rc;
319227569Sphilip
320227569Sphilip	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 ||
321227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1);
322227569Sphilip
323227569Sphilip	/*
324227569Sphilip	 * Allocate sufficient memory for the entire dynamiccfg area, even
325227569Sphilip	 * if we're not actually going to read in the VPD.
326227569Sphilip	 */
327227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
328227569Sphilip		goto fail1;
329227569Sphilip
330227569Sphilip	EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg);
331227569Sphilip	if (dcfg == NULL) {
332227569Sphilip		rc = ENOMEM;
333227569Sphilip		goto fail2;
334227569Sphilip	}
335227569Sphilip
336227569Sphilip	if ((rc = siena_nvram_partn_read(enp, partn, 0,
337227569Sphilip	    (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0)
338227569Sphilip		goto fail3;
339227569Sphilip
340227569Sphilip	/* Verify the magic */
341227569Sphilip	if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0)
342227569Sphilip	    != SIENA_MC_DYNAMIC_CONFIG_MAGIC)
343227569Sphilip		goto invalid1;
344227569Sphilip
345227569Sphilip	/* All future versions of the structure must be backwards compatable */
346227569Sphilip	EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0);
347227569Sphilip
348227569Sphilip	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
349227569Sphilip	nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
350227569Sphilip	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
351227569Sphilip	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
352227569Sphilip
353227569Sphilip	/* Verify the hdr doesn't overflow the partn size */
354227569Sphilip	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
355227569Sphilip	    vpd_length + vpd_offset > size)
356227569Sphilip		goto invalid2;
357227569Sphilip
358227569Sphilip	/* Verify the header has room for all it's versions */
359227569Sphilip	if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) ||
360227569Sphilip	    hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions))
361227569Sphilip		goto invalid3;
362227569Sphilip
363227569Sphilip	/*
364227569Sphilip	 * Read the remaining portion of the dcfg, either including
365227569Sphilip	 * the whole of VPD (there is no vpd length in this structure,
366227569Sphilip	 * so we have to parse each tag), or just the dcfg header itself
367227569Sphilip	 */
368227569Sphilip	region = vpd ? vpd_offset + vpd_length : hdr_length;
369227569Sphilip	if (region > SIENA_NVRAM_CHUNK) {
370227569Sphilip		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
371227569Sphilip		    (caddr_t)dcfg + SIENA_NVRAM_CHUNK,
372227569Sphilip		    region - SIENA_NVRAM_CHUNK)) != 0)
373227569Sphilip			goto fail4;
374227569Sphilip	}
375227569Sphilip
376227569Sphilip	/* Verify checksum */
377227569Sphilip	cksum = 0;
378227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
379227569Sphilip		cksum += ((uint8_t *)dcfg)[pos];
380227569Sphilip	if (cksum != 0)
381227569Sphilip		goto invalid4;
382227569Sphilip
383227569Sphilip	goto done;
384227569Sphilip
385227569Sphilipinvalid4:
386227569Sphilip	EFSYS_PROBE(invalid4);
387227569Sphilipinvalid3:
388227569Sphilip	EFSYS_PROBE(invalid3);
389227569Sphilipinvalid2:
390227569Sphilip	EFSYS_PROBE(invalid2);
391227569Sphilipinvalid1:
392227569Sphilip	EFSYS_PROBE(invalid1);
393227569Sphilip
394227569Sphilip	/*
395227569Sphilip	 * Construct a new "null" dcfg, with an empty version vector,
396227569Sphilip	 * and an empty VPD chunk trailing. This has the neat side effect
397227569Sphilip	 * of testing the exception paths in the write path.
398227569Sphilip	 */
399227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->magic,
400227569Sphilip			    EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC);
401227569Sphilip	EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg));
402227569Sphilip	EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0,
403227569Sphilip			    SIENA_MC_DYNAMIC_CONFIG_VERSION);
404227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
405227569Sphilip			    EFX_DWORD_0, sizeof (*dcfg));
406227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0);
407227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0);
408227569Sphilip
409227569Sphilipdone:
410227569Sphilip	*dcfgp = dcfg;
411227569Sphilip	*sizep = size;
412227569Sphilip
413227569Sphilip	return (0);
414227569Sphilip
415227569Sphilipfail4:
416227569Sphilip	EFSYS_PROBE(fail4);
417227569Sphilipfail3:
418227569Sphilip	EFSYS_PROBE(fail3);
419227569Sphilip
420227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, dcfg);
421227569Sphilip
422283514Sarybchikfail2:
423283514Sarybchik	EFSYS_PROBE(fail2);
424227569Sphilipfail1:
425291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
426227569Sphilip
427227569Sphilip	return (rc);
428227569Sphilip}
429227569Sphilip
430291436Sarybchik	__checkReturn		efx_rc_t
431227569Sphilipsiena_nvram_get_subtype(
432227569Sphilip	__in			efx_nic_t *enp,
433293770Sarybchik	__in			uint32_t partn,
434227569Sphilip	__out			uint32_t *subtypep)
435227569Sphilip{
436227569Sphilip	efx_mcdi_req_t req;
437283514Sarybchik	uint8_t payload[MAX(MC_CMD_GET_BOARD_CFG_IN_LEN,
438283514Sarybchik			    MC_CMD_GET_BOARD_CFG_OUT_LENMAX)];
439227569Sphilip	efx_word_t *fw_list;
440291436Sarybchik	efx_rc_t rc;
441227569Sphilip
442283514Sarybchik	(void) memset(payload, 0, sizeof (payload));
443227569Sphilip	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
444283514Sarybchik	req.emr_in_buf = payload;
445283514Sarybchik	req.emr_in_length = MC_CMD_GET_BOARD_CFG_IN_LEN;
446283514Sarybchik	req.emr_out_buf = payload;
447283514Sarybchik	req.emr_out_length = MC_CMD_GET_BOARD_CFG_OUT_LENMAX;
448227569Sphilip
449227569Sphilip	efx_mcdi_execute(enp, &req);
450227569Sphilip
451227569Sphilip	if (req.emr_rc != 0) {
452227569Sphilip		rc = req.emr_rc;
453227569Sphilip		goto fail1;
454227569Sphilip	}
455227569Sphilip
456278941Sarybchik	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LENMIN) {
457227569Sphilip		rc = EMSGSIZE;
458227569Sphilip		goto fail2;
459227569Sphilip	}
460227569Sphilip
461278941Sarybchik	if (req.emr_out_length_used <
462278941Sarybchik	    MC_CMD_GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST_OFST +
463279141Sarybchik	    (partn + 1) * sizeof (efx_word_t)) {
464278941Sarybchik		rc = ENOENT;
465278941Sarybchik		goto fail3;
466278941Sarybchik	}
467278941Sarybchik
468227569Sphilip	fw_list = MCDI_OUT2(req, efx_word_t,
469227569Sphilip			    GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
470227569Sphilip	*subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0);
471227569Sphilip
472227569Sphilip	return (0);
473227569Sphilip
474278941Sarybchikfail3:
475278941Sarybchik	EFSYS_PROBE(fail3);
476227569Sphilipfail2:
477227569Sphilip	EFSYS_PROBE(fail2);
478227569Sphilipfail1:
479291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
480227569Sphilip
481227569Sphilip	return (rc);
482227569Sphilip}
483227569Sphilip
484291436Sarybchik	__checkReturn		efx_rc_t
485227569Sphilipsiena_nvram_get_version(
486227569Sphilip	__in			efx_nic_t *enp,
487227569Sphilip	__in			efx_nvram_type_t type,
488227569Sphilip	__out			uint32_t *subtypep,
489227569Sphilip	__out_ecount(4)		uint16_t version[4])
490227569Sphilip{
491227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg;
492227569Sphilip	siena_parttbl_entry_t *entry;
493293770Sarybchik	uint32_t dcfg_partn;
494293770Sarybchik	uint32_t partn;
495283514Sarybchik	unsigned int i;
496291436Sarybchik	efx_rc_t rc;
497227569Sphilip
498293810Sarybchik	if ((rc = siena_nvram_type_to_partn(enp, type, &partn)) != 0)
499227569Sphilip		goto fail1;
500227569Sphilip
501227569Sphilip	if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
502227569Sphilip		rc = ENOTSUP;
503227569Sphilip		goto fail2;
504227569Sphilip	}
505227569Sphilip
506227569Sphilip	if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0)
507227569Sphilip		goto fail3;
508227569Sphilip
509227569Sphilip	/*
510227569Sphilip	 * Some partitions are accessible from both ports (for instance BOOTROM)
511227569Sphilip	 * Find the highest version reported by all dcfg structures on ports
512227569Sphilip	 * that have access to this partition.
513227569Sphilip	 */
514227569Sphilip	version[0] = version[1] = version[2] = version[3] = 0;
515283514Sarybchik	for (i = 0; i < EFX_ARRAY_SIZE(siena_parttbl); i++) {
516227569Sphilip		unsigned int nitems;
517227569Sphilip		uint16_t temp[4];
518227569Sphilip		size_t length;
519227569Sphilip
520283514Sarybchik		entry = &siena_parttbl[i];
521227569Sphilip		if (entry->partn != partn)
522227569Sphilip			continue;
523227569Sphilip
524227569Sphilip		dcfg_partn = (entry->port == 1)
525227569Sphilip			? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
526227569Sphilip			: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
527227569Sphilip		/*
528227569Sphilip		 * Ingore missing partitions on port 2, assuming they're due
529227569Sphilip		 * to to running on a single port part.
530227569Sphilip		 */
531227569Sphilip		if ((1 << dcfg_partn) &  ~enp->en_u.siena.enu_partn_mask) {
532227569Sphilip			if (entry->port == 2)
533227569Sphilip				continue;
534227569Sphilip		}
535227569Sphilip
536227569Sphilip		if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
537227569Sphilip		    B_FALSE, &dcfg, &length)) != 0)
538227569Sphilip			goto fail4;
539227569Sphilip
540227569Sphilip		nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items,
541227569Sphilip			    EFX_DWORD_0);
542227569Sphilip		if (nitems < entry->partn)
543227569Sphilip			goto done;
544227569Sphilip
545227569Sphilip		temp[0] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_w,
546227569Sphilip			    EFX_WORD_0);
547227569Sphilip		temp[1] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_x,
548227569Sphilip			    EFX_WORD_0);
549227569Sphilip		temp[2] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_y,
550227569Sphilip			    EFX_WORD_0);
551227569Sphilip		temp[3] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_z,
552227569Sphilip			    EFX_WORD_0);
553227569Sphilip		if (memcmp(version, temp, sizeof (temp)) < 0)
554227569Sphilip			memcpy(version, temp, sizeof (temp));
555227569Sphilip
556227569Sphilip	done:
557227569Sphilip		EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
558227569Sphilip	}
559227569Sphilip
560227569Sphilip	return (0);
561227569Sphilip
562227569Sphilipfail4:
563227569Sphilip	EFSYS_PROBE(fail4);
564227569Sphilipfail3:
565227569Sphilip	EFSYS_PROBE(fail3);
566227569Sphilipfail2:
567227569Sphilip	EFSYS_PROBE(fail2);
568227569Sphilipfail1:
569291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
570227569Sphilip
571227569Sphilip	return (rc);
572227569Sphilip}
573227569Sphilip
574291436Sarybchik	__checkReturn		efx_rc_t
575294080Sarybchiksiena_nvram_partn_rw_start(
576227569Sphilip	__in			efx_nic_t *enp,
577294080Sarybchik	__in			uint32_t partn,
578227569Sphilip	__out			size_t *chunk_sizep)
579227569Sphilip{
580291436Sarybchik	efx_rc_t rc;
581227569Sphilip
582294080Sarybchik	if ((rc = siena_nvram_partn_lock(enp, partn)) != 0)
583227569Sphilip		goto fail1;
584227569Sphilip
585227569Sphilip	if (chunk_sizep != NULL)
586227569Sphilip		*chunk_sizep = SIENA_NVRAM_CHUNK;
587227569Sphilip
588227569Sphilip	return (0);
589227569Sphilip
590227569Sphilipfail1:
591291436Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
592227569Sphilip
593227569Sphilip	return (rc);
594227569Sphilip}
595227569Sphilip
596227569Sphilip				void
597294250Sarybchiksiena_nvram_partn_rw_finish(
598227569Sphilip	__in			efx_nic_t *enp,
599294250Sarybchik	__in			uint32_t partn)
600227569Sphilip{
601294250Sarybchik	siena_nvram_partn_unlock(enp, partn);
602227569Sphilip}
603227569Sphilip
604291436Sarybchik	__checkReturn		efx_rc_t
605227569Sphilipsiena_nvram_set_version(
606227569Sphilip	__in			efx_nic_t *enp,
607227569Sphilip	__in			efx_nvram_type_t type,
608283514Sarybchik	__in_ecount(4)		uint16_t version[4])
609227569Sphilip{
610293810Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
611227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
612293810Sarybchik	siena_mc_fw_version_t *fwverp;
613293810Sarybchik	uint32_t dcfg_partn, partn;
614293810Sarybchik	size_t dcfg_size;
615227569Sphilip	unsigned int hdr_length;
616227569Sphilip	unsigned int vpd_length;
617227569Sphilip	unsigned int vpd_offset;
618227569Sphilip	unsigned int nitems;
619227569Sphilip	unsigned int required_hdr_length;
620227569Sphilip	unsigned int pos;
621227569Sphilip	uint8_t cksum;
622227569Sphilip	uint32_t subtype;
623227569Sphilip	size_t length;
624291436Sarybchik	efx_rc_t rc;
625227569Sphilip
626293810Sarybchik	if ((rc = siena_nvram_type_to_partn(enp, type, &partn)) != 0)
627227569Sphilip		goto fail1;
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)
634227569Sphilip		goto fail2;
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