1227569Sphilip/*-
2227569Sphilip * Copyright 2009 Solarflare Communications Inc.  All rights reserved.
3227569Sphilip *
4227569Sphilip * Redistribution and use in source and binary forms, with or without
5227569Sphilip * modification, are permitted provided that the following conditions
6227569Sphilip * are met:
7227569Sphilip * 1. Redistributions of source code must retain the above copyright
8227569Sphilip *    notice, this list of conditions and the following disclaimer.
9227569Sphilip * 2. Redistributions in binary form must reproduce the above copyright
10227569Sphilip *    notice, this list of conditions and the following disclaimer in the
11227569Sphilip *    documentation and/or other materials provided with the distribution.
12227569Sphilip *
13227569Sphilip * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14227569Sphilip * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15227569Sphilip * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16227569Sphilip * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17227569Sphilip * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18227569Sphilip * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19227569Sphilip * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20227569Sphilip * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21227569Sphilip * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22227569Sphilip * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23227569Sphilip * SUCH DAMAGE.
24227569Sphilip */
25227569Sphilip
26228078Sphilip#include <sys/cdefs.h>
27228078Sphilip__FBSDID("$FreeBSD$");
28228078Sphilip
29227569Sphilip#include "efsys.h"
30227569Sphilip#include "efx.h"
31227569Sphilip#include "efx_types.h"
32227569Sphilip#include "efx_regs.h"
33227569Sphilip#include "efx_impl.h"
34227569Sphilip
35227569Sphilip#if EFSYS_OPT_SIENA
36227569Sphilip
37227569Sphilip#if EFSYS_OPT_VPD || EFSYS_OPT_NVRAM
38227569Sphilip
39227569Sphilip	__checkReturn		int
40227569Sphilipsiena_nvram_partn_size(
41227569Sphilip	__in			efx_nic_t *enp,
42227569Sphilip	__in			unsigned int partn,
43227569Sphilip	__out			size_t *sizep)
44227569Sphilip{
45227569Sphilip	efx_mcdi_req_t req;
46227569Sphilip	uint8_t payload[MAX(MC_CMD_NVRAM_INFO_IN_LEN,
47227569Sphilip			    MC_CMD_NVRAM_INFO_OUT_LEN)];
48227569Sphilip	int rc;
49227569Sphilip
50227569Sphilip	if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
51227569Sphilip		rc = ENOTSUP;
52227569Sphilip		goto fail1;
53227569Sphilip	}
54227569Sphilip
55227569Sphilip	req.emr_cmd = MC_CMD_NVRAM_INFO;
56227569Sphilip	req.emr_in_buf = payload;
57227569Sphilip	req.emr_in_length = MC_CMD_NVRAM_INFO_IN_LEN;
58227569Sphilip	req.emr_out_buf = payload;
59227569Sphilip	req.emr_out_length = MC_CMD_NVRAM_INFO_OUT_LEN;
60227569Sphilip
61227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_INFO_IN_TYPE, partn);
62227569Sphilip
63227569Sphilip	efx_mcdi_execute(enp, &req);
64227569Sphilip
65227569Sphilip	if (req.emr_rc != 0) {
66227569Sphilip		rc = req.emr_rc;
67227569Sphilip		goto fail2;
68227569Sphilip	}
69227569Sphilip
70227569Sphilip	if (req.emr_out_length_used < MC_CMD_NVRAM_INFO_OUT_LEN) {
71227569Sphilip		rc = EMSGSIZE;
72227569Sphilip		goto fail3;
73227569Sphilip	}
74227569Sphilip
75227569Sphilip	*sizep = MCDI_OUT_DWORD(req, NVRAM_INFO_OUT_SIZE);
76227569Sphilip
77227569Sphilip	return (0);
78227569Sphilip
79227569Sphilipfail3:
80227569Sphilip	EFSYS_PROBE(fail3);
81227569Sphilipfail2:
82227569Sphilip	EFSYS_PROBE(fail2);
83227569Sphilipfail1:
84227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
85227569Sphilip
86227569Sphilip	return (rc);
87227569Sphilip}
88227569Sphilip
89227569Sphilip	__checkReturn		int
90227569Sphilipsiena_nvram_partn_lock(
91227569Sphilip	__in			efx_nic_t *enp,
92227569Sphilip	__in			unsigned int partn)
93227569Sphilip{
94227569Sphilip	efx_mcdi_req_t req;
95227569Sphilip	uint8_t payload[MC_CMD_NVRAM_UPDATE_START_IN_LEN];
96227569Sphilip	int rc;
97227569Sphilip
98227569Sphilip	req.emr_cmd = MC_CMD_NVRAM_UPDATE_START;
99227569Sphilip	req.emr_in_buf = payload;
100227569Sphilip	req.emr_in_length = MC_CMD_NVRAM_UPDATE_START_IN_LEN;
101227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_START_OUT_LEN == 0);
102227569Sphilip	req.emr_out_buf = NULL;
103227569Sphilip	req.emr_out_length = 0;
104227569Sphilip
105227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_START_IN_TYPE, partn);
106227569Sphilip
107227569Sphilip	efx_mcdi_execute(enp, &req);
108227569Sphilip
109227569Sphilip	if (req.emr_rc != 0) {
110227569Sphilip		rc = req.emr_rc;
111227569Sphilip		goto fail1;
112227569Sphilip	}
113227569Sphilip
114227569Sphilip	return (0);
115227569Sphilip
116227569Sphilipfail1:
117227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
118227569Sphilip
119227569Sphilip	return (rc);
120227569Sphilip}
121227569Sphilip
122227569Sphilip	__checkReturn		int
123227569Sphilipsiena_nvram_partn_read(
124227569Sphilip	__in			efx_nic_t *enp,
125227569Sphilip	__in			unsigned int partn,
126227569Sphilip	__in			unsigned int offset,
127227569Sphilip	__out_bcount(size)	caddr_t data,
128227569Sphilip	__in			size_t size)
129227569Sphilip{
130227569Sphilip	efx_mcdi_req_t req;
131227569Sphilip	uint8_t payload[MAX(MC_CMD_NVRAM_READ_IN_LEN,
132227569Sphilip			    MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK))];
133227569Sphilip	size_t chunk;
134227569Sphilip	int rc;
135227569Sphilip
136227569Sphilip	while (size > 0) {
137227569Sphilip		chunk = MIN(size, SIENA_NVRAM_CHUNK);
138227569Sphilip
139227569Sphilip		req.emr_cmd = MC_CMD_NVRAM_READ;
140227569Sphilip		req.emr_in_buf = payload;
141227569Sphilip		req.emr_in_length = MC_CMD_NVRAM_READ_IN_LEN;
142227569Sphilip		req.emr_out_buf = payload;
143227569Sphilip		req.emr_out_length =
144227569Sphilip			MC_CMD_NVRAM_READ_OUT_LEN(SIENA_NVRAM_CHUNK);
145227569Sphilip
146227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_TYPE, partn);
147227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_OFFSET, offset);
148227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_READ_IN_LENGTH, chunk);
149227569Sphilip
150227569Sphilip		efx_mcdi_execute(enp, &req);
151227569Sphilip
152227569Sphilip		if (req.emr_rc != 0) {
153227569Sphilip			rc = req.emr_rc;
154227569Sphilip			goto fail1;
155227569Sphilip		}
156227569Sphilip
157227569Sphilip		if (req.emr_out_length_used <
158227569Sphilip		    MC_CMD_NVRAM_READ_OUT_LEN(chunk)) {
159227569Sphilip			rc = EMSGSIZE;
160227569Sphilip			goto fail2;
161227569Sphilip		}
162227569Sphilip
163227569Sphilip		memcpy(data,
164227569Sphilip		    MCDI_OUT2(req, uint8_t, NVRAM_READ_OUT_READ_BUFFER),
165227569Sphilip		    chunk);
166227569Sphilip
167227569Sphilip		size -= chunk;
168227569Sphilip		data += chunk;
169227569Sphilip		offset += chunk;
170227569Sphilip	}
171227569Sphilip
172227569Sphilip	return (0);
173227569Sphilip
174227569Sphilipfail2:
175227569Sphilip	EFSYS_PROBE(fail2);
176227569Sphilipfail1:
177227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
178227569Sphilip
179227569Sphilip	return (rc);
180227569Sphilip}
181227569Sphilip
182227569Sphilip	__checkReturn		int
183227569Sphilipsiena_nvram_partn_erase(
184227569Sphilip	__in			efx_nic_t *enp,
185227569Sphilip	__in			unsigned int partn,
186227569Sphilip	__in			unsigned int offset,
187227569Sphilip	__in			size_t size)
188227569Sphilip{
189227569Sphilip	efx_mcdi_req_t req;
190227569Sphilip	uint8_t payload[MC_CMD_NVRAM_ERASE_IN_LEN];
191227569Sphilip	int rc;
192227569Sphilip
193227569Sphilip	req.emr_cmd = MC_CMD_NVRAM_ERASE;
194227569Sphilip	req.emr_in_buf = payload;
195227569Sphilip	req.emr_in_length = MC_CMD_NVRAM_ERASE_IN_LEN;
196227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_NVRAM_ERASE_OUT_LEN == 0);
197227569Sphilip	req.emr_out_buf = NULL;
198227569Sphilip	req.emr_out_length = 0;
199227569Sphilip
200227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_TYPE, partn);
201227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_OFFSET, offset);
202227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_ERASE_IN_LENGTH, size);
203227569Sphilip
204227569Sphilip	efx_mcdi_execute(enp, &req);
205227569Sphilip
206227569Sphilip	if (req.emr_rc != 0) {
207227569Sphilip		rc = req.emr_rc;
208227569Sphilip		goto fail1;
209227569Sphilip	}
210227569Sphilip
211227569Sphilip	return (0);
212227569Sphilip
213227569Sphilipfail1:
214227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
215227569Sphilip
216227569Sphilip	return (rc);
217227569Sphilip}
218227569Sphilip
219227569Sphilip	__checkReturn		int
220227569Sphilipsiena_nvram_partn_write(
221227569Sphilip	__in			efx_nic_t *enp,
222227569Sphilip	__in			unsigned int partn,
223227569Sphilip	__in			unsigned int offset,
224227569Sphilip	__out_bcount(size)	caddr_t data,
225227569Sphilip	__in			size_t size)
226227569Sphilip{
227227569Sphilip	efx_mcdi_req_t req;
228227569Sphilip	uint8_t payload[MC_CMD_NVRAM_WRITE_IN_LEN(SIENA_NVRAM_CHUNK)];
229227569Sphilip	size_t chunk;
230227569Sphilip	int rc;
231227569Sphilip
232227569Sphilip	while (size > 0) {
233227569Sphilip		chunk = MIN(size, SIENA_NVRAM_CHUNK);
234227569Sphilip
235227569Sphilip		req.emr_cmd = MC_CMD_NVRAM_WRITE;
236227569Sphilip		req.emr_in_buf = payload;
237227569Sphilip		req.emr_in_length = MC_CMD_NVRAM_WRITE_IN_LEN(chunk);
238227569Sphilip		EFX_STATIC_ASSERT(MC_CMD_NVRAM_WRITE_OUT_LEN == 0);
239227569Sphilip		req.emr_out_buf = NULL;
240227569Sphilip		req.emr_out_length = 0;
241227569Sphilip
242227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_TYPE, partn);
243227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_OFFSET, offset);
244227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_WRITE_IN_LENGTH, chunk);
245227569Sphilip
246227569Sphilip		memcpy(MCDI_IN2(req, uint8_t, NVRAM_WRITE_IN_WRITE_BUFFER),
247227569Sphilip		    data, chunk);
248227569Sphilip
249227569Sphilip		efx_mcdi_execute(enp, &req);
250227569Sphilip
251227569Sphilip		if (req.emr_rc != 0) {
252227569Sphilip			rc = req.emr_rc;
253227569Sphilip			goto fail1;
254227569Sphilip		}
255227569Sphilip
256227569Sphilip		size -= chunk;
257227569Sphilip		data += chunk;
258227569Sphilip		offset += chunk;
259227569Sphilip	}
260227569Sphilip
261227569Sphilip	return (0);
262227569Sphilip
263227569Sphilipfail1:
264227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
265227569Sphilip
266227569Sphilip	return (rc);
267227569Sphilip}
268227569Sphilip
269227569Sphilip				void
270227569Sphilipsiena_nvram_partn_unlock(
271227569Sphilip	__in			efx_nic_t *enp,
272227569Sphilip	__in			unsigned int partn)
273227569Sphilip{
274227569Sphilip	efx_mcdi_req_t req;
275227569Sphilip	uint8_t payload[MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN];
276227569Sphilip	uint32_t reboot;
277227569Sphilip	int rc;
278227569Sphilip
279227569Sphilip	req.emr_cmd = MC_CMD_NVRAM_UPDATE_FINISH;
280227569Sphilip	req.emr_in_buf = payload;
281227569Sphilip	req.emr_in_length = MC_CMD_NVRAM_UPDATE_FINISH_IN_LEN;
282227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_NVRAM_UPDATE_FINISH_OUT_LEN == 0);
283227569Sphilip	req.emr_out_buf = NULL;
284227569Sphilip	req.emr_out_length = 0;
285227569Sphilip
286227569Sphilip	/*
287227569Sphilip	 * Reboot into the new image only for PHYs. The driver has to
288227569Sphilip	 * explicitly cope with an MC reboot after a firmware update.
289227569Sphilip	 */
290227569Sphilip	reboot = (partn == MC_CMD_NVRAM_TYPE_PHY_PORT0 ||
291227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_PHY_PORT1 ||
292227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO);
293227569Sphilip
294227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_TYPE, partn);
295227569Sphilip	MCDI_IN_SET_DWORD(req, NVRAM_UPDATE_FINISH_IN_REBOOT, reboot);
296227569Sphilip
297227569Sphilip	efx_mcdi_execute(enp, &req);
298227569Sphilip
299227569Sphilip	if (req.emr_rc != 0) {
300227569Sphilip		rc = req.emr_rc;
301227569Sphilip		goto fail1;
302227569Sphilip	}
303227569Sphilip
304227569Sphilip	return;
305227569Sphilip
306227569Sphilipfail1:
307227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
308227569Sphilip}
309227569Sphilip
310227569Sphilip#endif	/* EFSYS_OPT_VPD || EFSYS_OPT_NVRAM */
311227569Sphilip
312227569Sphilip#if EFSYS_OPT_NVRAM
313227569Sphilip
314227569Sphiliptypedef struct siena_parttbl_entry_s {
315227569Sphilip	unsigned int		partn;
316227569Sphilip	unsigned int		port;
317227569Sphilip	efx_nvram_type_t	nvtype;
318227569Sphilip} siena_parttbl_entry_t;
319227569Sphilip
320227569Sphilipstatic siena_parttbl_entry_t siena_parttbl[] = {
321227569Sphilip	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	1, EFX_NVRAM_NULLPHY},
322227569Sphilip	{MC_CMD_NVRAM_TYPE_DISABLED_CALLISTO,	2, EFX_NVRAM_NULLPHY},
323227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW,		1, EFX_NVRAM_MC_FIRMWARE},
324227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW,		2, EFX_NVRAM_MC_FIRMWARE},
325227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	1, EFX_NVRAM_MC_GOLDEN},
326227569Sphilip	{MC_CMD_NVRAM_TYPE_MC_FW_BACKUP,	2, EFX_NVRAM_MC_GOLDEN},
327227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM,		1, EFX_NVRAM_BOOTROM},
328227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM,		2, EFX_NVRAM_BOOTROM},
329227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT0,	1, EFX_NVRAM_BOOTROM_CFG},
330227569Sphilip	{MC_CMD_NVRAM_TYPE_EXP_ROM_CFG_PORT1,	2, EFX_NVRAM_BOOTROM_CFG},
331227569Sphilip	{MC_CMD_NVRAM_TYPE_PHY_PORT0,		1, EFX_NVRAM_PHY},
332227569Sphilip	{MC_CMD_NVRAM_TYPE_PHY_PORT1,		2, EFX_NVRAM_PHY},
333227569Sphilip	{0, 0, 0},
334227569Sphilip};
335227569Sphilip
336227569Sphilipstatic	__checkReturn		siena_parttbl_entry_t *
337227569Sphilipsiena_parttbl_entry(
338227569Sphilip	__in			efx_nic_t *enp,
339227569Sphilip	__in			efx_nvram_type_t type)
340227569Sphilip{
341227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
342227569Sphilip	siena_parttbl_entry_t *entry;
343227569Sphilip
344227569Sphilip	EFSYS_ASSERT3U(type, <, EFX_NVRAM_NTYPES);
345227569Sphilip
346227569Sphilip	for (entry = siena_parttbl; entry->port > 0; ++entry) {
347227569Sphilip		if (entry->port == emip->emi_port && entry->nvtype == type)
348227569Sphilip			return (entry);
349227569Sphilip	}
350227569Sphilip
351227569Sphilip	return (NULL);
352227569Sphilip}
353227569Sphilip
354227569Sphilip#if EFSYS_OPT_DIAG
355227569Sphilip
356227569Sphilip	__checkReturn		int
357227569Sphilipsiena_nvram_test(
358227569Sphilip	__in			efx_nic_t *enp)
359227569Sphilip{
360227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
361227569Sphilip	siena_parttbl_entry_t *entry;
362227569Sphilip	efx_mcdi_req_t req;
363227569Sphilip	uint8_t payload[MAX(MC_CMD_NVRAM_TEST_IN_LEN,
364227569Sphilip			    MC_CMD_NVRAM_TEST_OUT_LEN)];
365227569Sphilip	int result;
366227569Sphilip	int rc;
367227569Sphilip
368227569Sphilip	req.emr_cmd = MC_CMD_NVRAM_TEST;
369227569Sphilip	req.emr_in_buf = payload;
370227569Sphilip	req.emr_in_length = MC_CMD_NVRAM_TEST_IN_LEN;
371227569Sphilip	req.emr_out_buf = payload;
372227569Sphilip	req.emr_out_length = MC_CMD_NVRAM_TEST_OUT_LEN;
373227569Sphilip
374227569Sphilip	/*
375227569Sphilip	 * Iterate over the list of supported partition types
376227569Sphilip	 * applicable to *this* port
377227569Sphilip	 */
378227569Sphilip	for (entry = siena_parttbl; entry->port > 0; ++entry) {
379227569Sphilip		if (entry->port != emip->emi_port ||
380227569Sphilip		    !(enp->en_u.siena.enu_partn_mask & (1 << entry->partn)))
381227569Sphilip			continue;
382227569Sphilip
383227569Sphilip		MCDI_IN_SET_DWORD(req, NVRAM_TEST_IN_TYPE, entry->partn);
384227569Sphilip
385227569Sphilip		efx_mcdi_execute(enp, &req);
386227569Sphilip
387227569Sphilip		if (req.emr_rc != 0) {
388227569Sphilip			rc = req.emr_rc;
389227569Sphilip			goto fail1;
390227569Sphilip		}
391227569Sphilip
392227569Sphilip		if (req.emr_out_length_used < MC_CMD_NVRAM_TEST_OUT_LEN) {
393227569Sphilip			rc = EMSGSIZE;
394227569Sphilip			goto fail2;
395227569Sphilip		}
396227569Sphilip
397227569Sphilip		result = MCDI_OUT_DWORD(req, NVRAM_TEST_OUT_RESULT);
398227569Sphilip		if (result == MC_CMD_NVRAM_TEST_FAIL) {
399227569Sphilip
400227569Sphilip			EFSYS_PROBE1(nvram_test_failure, int, entry->partn);
401227569Sphilip
402227569Sphilip			rc = (EINVAL);
403227569Sphilip			goto fail3;
404227569Sphilip		}
405227569Sphilip	}
406227569Sphilip
407227569Sphilip	return (0);
408227569Sphilip
409227569Sphilipfail3:
410227569Sphilip	EFSYS_PROBE(fail3);
411227569Sphilipfail2:
412227569Sphilip	EFSYS_PROBE(fail2);
413227569Sphilipfail1:
414227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
415227569Sphilip
416227569Sphilip	return (rc);
417227569Sphilip}
418227569Sphilip
419227569Sphilip#endif	/* EFSYS_OPT_DIAG */
420227569Sphilip
421227569Sphilip	__checkReturn		int
422227569Sphilipsiena_nvram_size(
423227569Sphilip	__in			efx_nic_t *enp,
424227569Sphilip	__in			efx_nvram_type_t type,
425227569Sphilip	__out			size_t *sizep)
426227569Sphilip{
427227569Sphilip	siena_parttbl_entry_t *entry;
428227569Sphilip	int rc;
429227569Sphilip
430227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
431227569Sphilip		rc = ENOTSUP;
432227569Sphilip		goto fail1;
433227569Sphilip	}
434227569Sphilip
435227569Sphilip	if ((rc = siena_nvram_partn_size(enp, entry->partn, sizep)) != 0)
436227569Sphilip		goto fail2;
437227569Sphilip
438227569Sphilip	return (0);
439227569Sphilip
440227569Sphilipfail2:
441227569Sphilip	EFSYS_PROBE(fail2);
442227569Sphilipfail1:
443227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
444227569Sphilip
445227569Sphilip	*sizep = 0;
446227569Sphilip
447227569Sphilip	return (rc);
448227569Sphilip}
449227569Sphilip
450227569Sphilip#define	SIENA_DYNAMIC_CFG_SIZE(_nitems)					\
451227569Sphilip	(sizeof (siena_mc_dynamic_config_hdr_t) + ((_nitems) *		\
452227569Sphilip	sizeof (((siena_mc_dynamic_config_hdr_t *)NULL)->fw_version[0])))
453227569Sphilip
454227569Sphilip	__checkReturn		int
455227569Sphilipsiena_nvram_get_dynamic_cfg(
456227569Sphilip	__in			efx_nic_t *enp,
457227569Sphilip	__in			unsigned int partn,
458227569Sphilip	__in			boolean_t vpd,
459227569Sphilip	__out			siena_mc_dynamic_config_hdr_t **dcfgp,
460227569Sphilip	__out			size_t *sizep)
461227569Sphilip{
462227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg;
463227569Sphilip	size_t size;
464227569Sphilip	uint8_t cksum;
465227569Sphilip	unsigned int vpd_offset;
466227569Sphilip	unsigned int vpd_length;
467227569Sphilip	unsigned int hdr_length;
468227569Sphilip	unsigned int nversions;
469227569Sphilip	unsigned int pos;
470227569Sphilip	unsigned int region;
471227569Sphilip	int rc;
472227569Sphilip
473227569Sphilip	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0 ||
474227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1);
475227569Sphilip
476227569Sphilip	/*
477227569Sphilip	 * Allocate sufficient memory for the entire dynamiccfg area, even
478227569Sphilip	 * if we're not actually going to read in the VPD.
479227569Sphilip	 */
480227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
481227569Sphilip		goto fail1;
482227569Sphilip
483227569Sphilip	EFSYS_KMEM_ALLOC(enp->en_esip, size, dcfg);
484227569Sphilip	if (dcfg == NULL) {
485227569Sphilip		rc = ENOMEM;
486227569Sphilip		goto fail2;
487227569Sphilip	}
488227569Sphilip
489227569Sphilip	if ((rc = siena_nvram_partn_read(enp, partn, 0,
490227569Sphilip	    (caddr_t)dcfg, SIENA_NVRAM_CHUNK)) != 0)
491227569Sphilip		goto fail3;
492227569Sphilip
493227569Sphilip	/* Verify the magic */
494227569Sphilip	if (EFX_DWORD_FIELD(dcfg->magic, EFX_DWORD_0)
495227569Sphilip	    != SIENA_MC_DYNAMIC_CONFIG_MAGIC)
496227569Sphilip		goto invalid1;
497227569Sphilip
498227569Sphilip	/* All future versions of the structure must be backwards compatable */
499227569Sphilip	EFX_STATIC_ASSERT(SIENA_MC_DYNAMIC_CONFIG_VERSION == 0);
500227569Sphilip
501227569Sphilip	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
502227569Sphilip	nversions = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
503227569Sphilip	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
504227569Sphilip	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
505227569Sphilip
506227569Sphilip	/* Verify the hdr doesn't overflow the partn size */
507227569Sphilip	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
508227569Sphilip	    vpd_length + vpd_offset > size)
509227569Sphilip		goto invalid2;
510227569Sphilip
511227569Sphilip	/* Verify the header has room for all it's versions */
512227569Sphilip	if (hdr_length < SIENA_DYNAMIC_CFG_SIZE(0) ||
513227569Sphilip	    hdr_length < SIENA_DYNAMIC_CFG_SIZE(nversions))
514227569Sphilip		goto invalid3;
515227569Sphilip
516227569Sphilip	/*
517227569Sphilip	 * Read the remaining portion of the dcfg, either including
518227569Sphilip	 * the whole of VPD (there is no vpd length in this structure,
519227569Sphilip	 * so we have to parse each tag), or just the dcfg header itself
520227569Sphilip	 */
521227569Sphilip	region = vpd ? vpd_offset + vpd_length : hdr_length;
522227569Sphilip	if (region > SIENA_NVRAM_CHUNK) {
523227569Sphilip		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
524227569Sphilip		    (caddr_t)dcfg + SIENA_NVRAM_CHUNK,
525227569Sphilip		    region - SIENA_NVRAM_CHUNK)) != 0)
526227569Sphilip			goto fail4;
527227569Sphilip	}
528227569Sphilip
529227569Sphilip	/* Verify checksum */
530227569Sphilip	cksum = 0;
531227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
532227569Sphilip		cksum += ((uint8_t *)dcfg)[pos];
533227569Sphilip	if (cksum != 0)
534227569Sphilip		goto invalid4;
535227569Sphilip
536227569Sphilip	goto done;
537227569Sphilip
538227569Sphilipinvalid4:
539227569Sphilip	EFSYS_PROBE(invalid4);
540227569Sphilipinvalid3:
541227569Sphilip	EFSYS_PROBE(invalid3);
542227569Sphilipinvalid2:
543227569Sphilip	EFSYS_PROBE(invalid2);
544227569Sphilipinvalid1:
545227569Sphilip	EFSYS_PROBE(invalid1);
546227569Sphilip
547227569Sphilip	/*
548227569Sphilip	 * Construct a new "null" dcfg, with an empty version vector,
549227569Sphilip	 * and an empty VPD chunk trailing. This has the neat side effect
550227569Sphilip	 * of testing the exception paths in the write path.
551227569Sphilip	 */
552227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->magic,
553227569Sphilip			    EFX_DWORD_0, SIENA_MC_DYNAMIC_CONFIG_MAGIC);
554227569Sphilip	EFX_POPULATE_WORD_1(dcfg->length, EFX_WORD_0, sizeof (*dcfg));
555227569Sphilip	EFX_POPULATE_BYTE_1(dcfg->version, EFX_BYTE_0,
556227569Sphilip			    SIENA_MC_DYNAMIC_CONFIG_VERSION);
557227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
558227569Sphilip			    EFX_DWORD_0, sizeof (*dcfg));
559227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, 0);
560227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items, EFX_DWORD_0, 0);
561227569Sphilip
562227569Sphilipdone:
563227569Sphilip	*dcfgp = dcfg;
564227569Sphilip	*sizep = size;
565227569Sphilip
566227569Sphilip	return (0);
567227569Sphilip
568227569Sphilipfail4:
569227569Sphilip	EFSYS_PROBE(fail4);
570227569Sphilipfail3:
571227569Sphilip	EFSYS_PROBE(fail3);
572227569Sphilipfail2:
573227569Sphilip	EFSYS_PROBE(fail2);
574227569Sphilip
575227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, dcfg);
576227569Sphilip
577227569Sphilipfail1:
578227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
579227569Sphilip
580227569Sphilip	return (rc);
581227569Sphilip}
582227569Sphilip
583227569Sphilipstatic	__checkReturn		int
584227569Sphilipsiena_nvram_get_subtype(
585227569Sphilip	__in			efx_nic_t *enp,
586227569Sphilip	__in			unsigned int partn,
587227569Sphilip	__out			uint32_t *subtypep)
588227569Sphilip{
589227569Sphilip	efx_mcdi_req_t req;
590227569Sphilip	uint8_t outbuf[MC_CMD_GET_BOARD_CFG_OUT_LEN];
591227569Sphilip	efx_word_t *fw_list;
592227569Sphilip	int rc;
593227569Sphilip
594227569Sphilip	req.emr_cmd = MC_CMD_GET_BOARD_CFG;
595227569Sphilip	EFX_STATIC_ASSERT(MC_CMD_GET_BOARD_CFG_IN_LEN == 0);
596227569Sphilip	req.emr_in_buf = NULL;
597227569Sphilip	req.emr_in_length = 0;
598227569Sphilip	req.emr_out_buf = outbuf;
599227569Sphilip	req.emr_out_length = sizeof (outbuf);
600227569Sphilip
601227569Sphilip	efx_mcdi_execute(enp, &req);
602227569Sphilip
603227569Sphilip	if (req.emr_rc != 0) {
604227569Sphilip		rc = req.emr_rc;
605227569Sphilip		goto fail1;
606227569Sphilip	}
607227569Sphilip
608227569Sphilip	if (req.emr_out_length_used < MC_CMD_GET_BOARD_CFG_OUT_LEN) {
609227569Sphilip		rc = EMSGSIZE;
610227569Sphilip		goto fail2;
611227569Sphilip	}
612227569Sphilip
613227569Sphilip	fw_list = MCDI_OUT2(req, efx_word_t,
614227569Sphilip			    GET_BOARD_CFG_OUT_FW_SUBTYPE_LIST);
615227569Sphilip	*subtypep = EFX_WORD_FIELD(fw_list[partn], EFX_WORD_0);
616227569Sphilip
617227569Sphilip	return (0);
618227569Sphilip
619227569Sphilipfail2:
620227569Sphilip	EFSYS_PROBE(fail2);
621227569Sphilipfail1:
622227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
623227569Sphilip
624227569Sphilip	return (rc);
625227569Sphilip}
626227569Sphilip
627227569Sphilip	__checkReturn		int
628227569Sphilipsiena_nvram_get_version(
629227569Sphilip	__in			efx_nic_t *enp,
630227569Sphilip	__in			efx_nvram_type_t type,
631227569Sphilip	__out			uint32_t *subtypep,
632227569Sphilip	__out_ecount(4)		uint16_t version[4])
633227569Sphilip{
634227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg;
635227569Sphilip	siena_parttbl_entry_t *entry;
636227569Sphilip	unsigned int dcfg_partn;
637227569Sphilip	unsigned int partn;
638227569Sphilip	int rc;
639227569Sphilip
640227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
641227569Sphilip		rc = ENOTSUP;
642227569Sphilip		goto fail1;
643227569Sphilip	}
644227569Sphilip	partn = entry->partn;
645227569Sphilip
646227569Sphilip	if ((1 << partn) & ~enp->en_u.siena.enu_partn_mask) {
647227569Sphilip		rc = ENOTSUP;
648227569Sphilip		goto fail2;
649227569Sphilip	}
650227569Sphilip
651227569Sphilip	if ((rc = siena_nvram_get_subtype(enp, partn, subtypep)) != 0)
652227569Sphilip		goto fail3;
653227569Sphilip
654227569Sphilip	/*
655227569Sphilip	 * Some partitions are accessible from both ports (for instance BOOTROM)
656227569Sphilip	 * Find the highest version reported by all dcfg structures on ports
657227569Sphilip	 * that have access to this partition.
658227569Sphilip	 */
659227569Sphilip	version[0] = version[1] = version[2] = version[3] = 0;
660227569Sphilip	for (entry = siena_parttbl; entry->port > 0; ++entry) {
661227569Sphilip		unsigned int nitems;
662227569Sphilip		uint16_t temp[4];
663227569Sphilip		size_t length;
664227569Sphilip
665227569Sphilip		if (entry->partn != partn)
666227569Sphilip			continue;
667227569Sphilip
668227569Sphilip		dcfg_partn = (entry->port == 1)
669227569Sphilip			? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
670227569Sphilip			: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
671227569Sphilip		/*
672227569Sphilip		 * Ingore missing partitions on port 2, assuming they're due
673227569Sphilip		 * to to running on a single port part.
674227569Sphilip		 */
675227569Sphilip		if ((1 << dcfg_partn) &  ~enp->en_u.siena.enu_partn_mask) {
676227569Sphilip			if (entry->port == 2)
677227569Sphilip				continue;
678227569Sphilip		}
679227569Sphilip
680227569Sphilip		if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
681227569Sphilip		    B_FALSE, &dcfg, &length)) != 0)
682227569Sphilip			goto fail4;
683227569Sphilip
684227569Sphilip		nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items,
685227569Sphilip			    EFX_DWORD_0);
686227569Sphilip		if (nitems < entry->partn)
687227569Sphilip			goto done;
688227569Sphilip
689227569Sphilip		temp[0] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_w,
690227569Sphilip			    EFX_WORD_0);
691227569Sphilip		temp[1] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_x,
692227569Sphilip			    EFX_WORD_0);
693227569Sphilip		temp[2] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_y,
694227569Sphilip			    EFX_WORD_0);
695227569Sphilip		temp[3] = EFX_WORD_FIELD(dcfg->fw_version[partn].version_z,
696227569Sphilip			    EFX_WORD_0);
697227569Sphilip		if (memcmp(version, temp, sizeof (temp)) < 0)
698227569Sphilip			memcpy(version, temp, sizeof (temp));
699227569Sphilip
700227569Sphilip	done:
701227569Sphilip		EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
702227569Sphilip	}
703227569Sphilip
704227569Sphilip	return (0);
705227569Sphilip
706227569Sphilipfail4:
707227569Sphilip	EFSYS_PROBE(fail4);
708227569Sphilipfail3:
709227569Sphilip	EFSYS_PROBE(fail3);
710227569Sphilipfail2:
711227569Sphilip	EFSYS_PROBE(fail2);
712227569Sphilipfail1:
713227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
714227569Sphilip
715227569Sphilip	return (rc);
716227569Sphilip}
717227569Sphilip
718227569Sphilip	__checkReturn		int
719227569Sphilipsiena_nvram_rw_start(
720227569Sphilip	__in			efx_nic_t *enp,
721227569Sphilip	__in			efx_nvram_type_t type,
722227569Sphilip	__out			size_t *chunk_sizep)
723227569Sphilip{
724227569Sphilip	siena_parttbl_entry_t *entry;
725227569Sphilip	int rc;
726227569Sphilip
727227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
728227569Sphilip		rc = ENOTSUP;
729227569Sphilip		goto fail1;
730227569Sphilip	}
731227569Sphilip
732227569Sphilip	if ((rc = siena_nvram_partn_lock(enp, entry->partn)) != 0)
733227569Sphilip		goto fail2;
734227569Sphilip
735227569Sphilip	if (chunk_sizep != NULL)
736227569Sphilip		*chunk_sizep = SIENA_NVRAM_CHUNK;
737227569Sphilip
738227569Sphilip	return (0);
739227569Sphilip
740227569Sphilipfail2:
741227569Sphilip	EFSYS_PROBE(fail2);
742227569Sphilipfail1:
743227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
744227569Sphilip
745227569Sphilip	return (rc);
746227569Sphilip}
747227569Sphilip
748227569Sphilip	__checkReturn		int
749227569Sphilipsiena_nvram_read_chunk(
750227569Sphilip	__in			efx_nic_t *enp,
751227569Sphilip	__in			efx_nvram_type_t type,
752227569Sphilip	__in			unsigned int offset,
753227569Sphilip	__out_bcount(size)	caddr_t data,
754227569Sphilip	__in			size_t size)
755227569Sphilip{
756227569Sphilip	siena_parttbl_entry_t *entry;
757227569Sphilip	int rc;
758227569Sphilip
759227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
760227569Sphilip		rc = ENOTSUP;
761227569Sphilip		goto fail1;
762227569Sphilip	}
763227569Sphilip
764227569Sphilip	if ((rc = siena_nvram_partn_read(enp, entry->partn,
765227569Sphilip	    offset, data, size)) != 0)
766227569Sphilip		goto fail2;
767227569Sphilip
768227569Sphilip	return (0);
769227569Sphilip
770227569Sphilipfail2:
771227569Sphilip	EFSYS_PROBE(fail2);
772227569Sphilipfail1:
773227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
774227569Sphilip
775227569Sphilip	return (rc);
776227569Sphilip}
777227569Sphilip
778227569Sphilip	__checkReturn		int
779227569Sphilipsiena_nvram_erase(
780227569Sphilip	__in			efx_nic_t *enp,
781227569Sphilip	__in			efx_nvram_type_t type)
782227569Sphilip{
783227569Sphilip	siena_parttbl_entry_t *entry;
784227569Sphilip	size_t size;
785227569Sphilip	int rc;
786227569Sphilip
787227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
788227569Sphilip		rc = ENOTSUP;
789227569Sphilip		goto fail1;
790227569Sphilip	}
791227569Sphilip
792227569Sphilip	if ((rc = siena_nvram_partn_size(enp, entry->partn, &size)) != 0)
793227569Sphilip		goto fail2;
794227569Sphilip
795227569Sphilip	if ((rc = siena_nvram_partn_erase(enp, entry->partn, 0, size)) != 0)
796227569Sphilip		goto fail3;
797227569Sphilip
798227569Sphilip	return (0);
799227569Sphilip
800227569Sphilipfail3:
801227569Sphilip	EFSYS_PROBE(fail3);
802227569Sphilipfail2:
803227569Sphilip	EFSYS_PROBE(fail2);
804227569Sphilipfail1:
805227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
806227569Sphilip
807227569Sphilip	return (rc);
808227569Sphilip}
809227569Sphilip
810227569Sphilip	__checkReturn		int
811227569Sphilipsiena_nvram_write_chunk(
812227569Sphilip	__in			efx_nic_t *enp,
813227569Sphilip	__in			efx_nvram_type_t type,
814227569Sphilip	__in			unsigned int offset,
815227569Sphilip	__in_bcount(size)	caddr_t data,
816227569Sphilip	__in			size_t size)
817227569Sphilip{
818227569Sphilip	siena_parttbl_entry_t *entry;
819227569Sphilip	int rc;
820227569Sphilip
821227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
822227569Sphilip		rc = ENOTSUP;
823227569Sphilip		goto fail1;
824227569Sphilip	}
825227569Sphilip
826227569Sphilip	if ((rc = siena_nvram_partn_write(enp, entry->partn,
827227569Sphilip	    offset, data, size)) != 0)
828227569Sphilip		goto fail2;
829227569Sphilip
830227569Sphilip	return (0);
831227569Sphilip
832227569Sphilipfail2:
833227569Sphilip	EFSYS_PROBE(fail2);
834227569Sphilipfail1:
835227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
836227569Sphilip
837227569Sphilip	return (rc);
838227569Sphilip}
839227569Sphilip
840227569Sphilip				void
841227569Sphilipsiena_nvram_rw_finish(
842227569Sphilip	__in			efx_nic_t *enp,
843227569Sphilip	__in			efx_nvram_type_t type)
844227569Sphilip{
845227569Sphilip	siena_parttbl_entry_t *entry;
846227569Sphilip
847227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) != NULL)
848227569Sphilip		siena_nvram_partn_unlock(enp, entry->partn);
849227569Sphilip}
850227569Sphilip
851227569Sphilip	__checkReturn		int
852227569Sphilipsiena_nvram_set_version(
853227569Sphilip	__in			efx_nic_t *enp,
854227569Sphilip	__in			efx_nvram_type_t type,
855227569Sphilip	__out			uint16_t version[4])
856227569Sphilip{
857227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
858227569Sphilip	siena_parttbl_entry_t *entry;
859227569Sphilip	unsigned int dcfg_partn;
860227569Sphilip	size_t partn_size;
861227569Sphilip	unsigned int hdr_length;
862227569Sphilip	unsigned int vpd_length;
863227569Sphilip	unsigned int vpd_offset;
864227569Sphilip	unsigned int nitems;
865227569Sphilip	unsigned int required_hdr_length;
866227569Sphilip	unsigned int pos;
867227569Sphilip	uint8_t cksum;
868227569Sphilip	uint32_t subtype;
869227569Sphilip	size_t length;
870227569Sphilip	int rc;
871227569Sphilip
872227569Sphilip	if ((entry = siena_parttbl_entry(enp, type)) == NULL) {
873227569Sphilip		rc = ENOTSUP;
874227569Sphilip		goto fail1;
875227569Sphilip	}
876227569Sphilip
877227569Sphilip	dcfg_partn = (entry->port == 1)
878227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
879227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
880227569Sphilip
881227569Sphilip	if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
882227569Sphilip		goto fail2;
883227569Sphilip
884227569Sphilip	if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
885227569Sphilip		goto fail2;
886227569Sphilip
887227569Sphilip	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
888227569Sphilip	    B_TRUE, &dcfg, &length)) != 0)
889227569Sphilip		goto fail3;
890227569Sphilip
891227569Sphilip	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
892227569Sphilip	nitems = EFX_DWORD_FIELD(dcfg->num_fw_version_items, EFX_DWORD_0);
893227569Sphilip	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
894227569Sphilip	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
895227569Sphilip
896227569Sphilip	/*
897227569Sphilip	 * NOTE: This function will blatt any fields trailing the version
898227569Sphilip	 * vector, or the VPD chunk.
899227569Sphilip	 */
900227569Sphilip	required_hdr_length = SIENA_DYNAMIC_CFG_SIZE(entry->partn + 1);
901227569Sphilip	if (required_hdr_length + vpd_length > length) {
902227569Sphilip		rc = ENOSPC;
903227569Sphilip		goto fail4;
904227569Sphilip	}
905227569Sphilip
906227569Sphilip	if (vpd_offset < required_hdr_length) {
907227569Sphilip		(void) memmove((caddr_t)dcfg + required_hdr_length,
908227569Sphilip			(caddr_t)dcfg + vpd_offset, vpd_length);
909227569Sphilip		vpd_offset = required_hdr_length;
910227569Sphilip		EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
911227569Sphilip				    EFX_DWORD_0, vpd_offset);
912227569Sphilip	}
913227569Sphilip
914227569Sphilip	if (hdr_length < required_hdr_length) {
915227569Sphilip		(void) memset((caddr_t)dcfg + hdr_length, 0,
916227569Sphilip			required_hdr_length - hdr_length);
917227569Sphilip		hdr_length = required_hdr_length;
918227569Sphilip		EFX_POPULATE_WORD_1(dcfg->length,
919227569Sphilip				    EFX_WORD_0, hdr_length);
920227569Sphilip	}
921227569Sphilip
922227569Sphilip	/* Get the subtype to insert into the fw_subtype array */
923227569Sphilip	if ((rc = siena_nvram_get_subtype(enp, entry->partn, &subtype)) != 0)
924227569Sphilip		goto fail5;
925227569Sphilip
926227569Sphilip	/* Fill out the new version */
927227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->fw_version[entry->partn].fw_subtype,
928227569Sphilip			    EFX_DWORD_0, subtype);
929227569Sphilip	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_w,
930227569Sphilip			    EFX_WORD_0, version[0]);
931227569Sphilip	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_x,
932227569Sphilip			    EFX_WORD_0, version[1]);
933227569Sphilip	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_y,
934227569Sphilip			    EFX_WORD_0, version[2]);
935227569Sphilip	EFX_POPULATE_WORD_1(dcfg->fw_version[entry->partn].version_z,
936227569Sphilip			    EFX_WORD_0, version[3]);
937227569Sphilip
938227569Sphilip	/* Update the version count */
939227569Sphilip	if (nitems < entry->partn + 1) {
940227569Sphilip		nitems = entry->partn + 1;
941227569Sphilip		EFX_POPULATE_DWORD_1(dcfg->num_fw_version_items,
942227569Sphilip				    EFX_DWORD_0, nitems);
943227569Sphilip	}
944227569Sphilip
945227569Sphilip	/* Update the checksum */
946227569Sphilip	cksum = 0;
947227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
948227569Sphilip		cksum += ((uint8_t *)dcfg)[pos];
949227569Sphilip	dcfg->csum.eb_u8[0] -= cksum;
950227569Sphilip
951227569Sphilip	/* Erase and write the new partition */
952227569Sphilip	if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
953227569Sphilip		goto fail6;
954227569Sphilip
955227569Sphilip	/* Write out the new structure to nvram */
956227569Sphilip	if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0,
957227569Sphilip	    (caddr_t)dcfg, vpd_offset + vpd_length)) != 0)
958227569Sphilip		goto fail7;
959227569Sphilip
960227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
961227569Sphilip
962227569Sphilip	siena_nvram_partn_unlock(enp, dcfg_partn);
963227569Sphilip
964227569Sphilip	return (0);
965227569Sphilip
966227569Sphilipfail7:
967227569Sphilip	EFSYS_PROBE(fail7);
968227569Sphilipfail6:
969227569Sphilip	EFSYS_PROBE(fail6);
970227569Sphilipfail5:
971227569Sphilip	EFSYS_PROBE(fail5);
972227569Sphilipfail4:
973227569Sphilip	EFSYS_PROBE(fail4);
974227569Sphilip
975227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, length, dcfg);
976227569Sphilipfail3:
977227569Sphilip	EFSYS_PROBE(fail3);
978227569Sphilipfail2:
979227569Sphilip	EFSYS_PROBE(fail2);
980227569Sphilipfail1:
981227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
982227569Sphilip
983227569Sphilip	return (rc);
984227569Sphilip}
985227569Sphilip
986227569Sphilip#endif	/* EFSYS_OPT_NVRAM */
987227569Sphilip
988227569Sphilip#endif	/* EFSYS_OPT_SIENA */
989