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
26228100Sphilip#include <sys/cdefs.h>
27228100Sphilip__FBSDID("$FreeBSD$");
28228100Sphilip
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_VPD
36227569Sphilip
37227569Sphilip#if EFSYS_OPT_SIENA
38227569Sphilip
39227569Sphilipstatic	__checkReturn			int
40227569Sphilipsiena_vpd_get_static(
41227569Sphilip	__in				efx_nic_t *enp,
42227569Sphilip	__in				unsigned int partn,
43227569Sphilip	__deref_out_bcount_opt(*sizep)	caddr_t *svpdp,
44227569Sphilip	__out				size_t *sizep)
45227569Sphilip{
46227569Sphilip	siena_mc_static_config_hdr_t *scfg;
47227569Sphilip	caddr_t svpd;
48227569Sphilip	size_t size;
49227569Sphilip	uint8_t cksum;
50227569Sphilip	unsigned int vpd_offset;
51227569Sphilip	unsigned int vpd_length;
52227569Sphilip	unsigned int hdr_length;
53227569Sphilip	unsigned int pos;
54227569Sphilip	unsigned int region;
55227569Sphilip	int rc;
56227569Sphilip
57227569Sphilip	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 ||
58227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1);
59227569Sphilip
60227569Sphilip	/* Allocate sufficient memory for the entire static cfg area */
61227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
62227569Sphilip		goto fail1;
63227569Sphilip
64227569Sphilip	EFSYS_KMEM_ALLOC(enp->en_esip, size, scfg);
65227569Sphilip	if (scfg == NULL) {
66227569Sphilip		rc = ENOMEM;
67227569Sphilip		goto fail2;
68227569Sphilip	}
69227569Sphilip
70227569Sphilip	if ((rc = siena_nvram_partn_read(enp, partn, 0,
71227569Sphilip	    (caddr_t)scfg, SIENA_NVRAM_CHUNK)) != 0)
72227569Sphilip		goto fail3;
73227569Sphilip
74227569Sphilip	/* Verify the magic number */
75227569Sphilip	if (EFX_DWORD_FIELD(scfg->magic, EFX_DWORD_0) !=
76227569Sphilip	    SIENA_MC_STATIC_CONFIG_MAGIC) {
77227569Sphilip		rc = EINVAL;
78227569Sphilip		goto fail4;
79227569Sphilip	}
80227569Sphilip
81227569Sphilip	/* All future versions of the structure must be backwards compatable */
82227569Sphilip	EFX_STATIC_ASSERT(SIENA_MC_STATIC_CONFIG_VERSION == 0);
83227569Sphilip
84227569Sphilip	hdr_length = EFX_WORD_FIELD(scfg->length, EFX_WORD_0);
85227569Sphilip	vpd_offset = EFX_DWORD_FIELD(scfg->static_vpd_offset, EFX_DWORD_0);
86227569Sphilip	vpd_length = EFX_DWORD_FIELD(scfg->static_vpd_length, EFX_DWORD_0);
87227569Sphilip
88227569Sphilip	/* Verify the hdr doesn't overflow the sector size */
89227569Sphilip	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
90227569Sphilip	    vpd_length + vpd_offset > size) {
91227569Sphilip		rc = EINVAL;
92227569Sphilip		goto fail5;
93227569Sphilip	}
94227569Sphilip
95227569Sphilip	/* Read the remainder of scfg + static vpd */
96227569Sphilip	region = vpd_offset + vpd_length;
97227569Sphilip	if (region > SIENA_NVRAM_CHUNK) {
98227569Sphilip		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
99227569Sphilip		    (caddr_t)scfg + SIENA_NVRAM_CHUNK,
100227569Sphilip		    region - SIENA_NVRAM_CHUNK)) != 0)
101227569Sphilip			goto fail6;
102227569Sphilip	}
103227569Sphilip
104227569Sphilip	/* Verify checksum */
105227569Sphilip	cksum = 0;
106227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
107227569Sphilip		cksum += ((uint8_t *)scfg)[pos];
108227569Sphilip	if (cksum != 0) {
109227569Sphilip		rc = EINVAL;
110227569Sphilip		goto fail7;
111227569Sphilip	}
112227569Sphilip
113227569Sphilip	if (vpd_length == 0)
114227569Sphilip		svpd = NULL;
115227569Sphilip	else {
116227569Sphilip		/* Copy the vpd data out */
117227569Sphilip		EFSYS_KMEM_ALLOC(enp->en_esip, vpd_length, svpd);
118227569Sphilip		if (svpd == NULL) {
119227569Sphilip			rc = ENOMEM;
120227569Sphilip			goto fail8;
121227569Sphilip		}
122227569Sphilip		memcpy(svpd, (caddr_t)scfg + vpd_offset, vpd_length);
123227569Sphilip	}
124227569Sphilip
125227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
126227569Sphilip
127227569Sphilip	*svpdp = svpd;
128227569Sphilip	*sizep = vpd_length;
129227569Sphilip
130227569Sphilip	return (0);
131227569Sphilip
132227569Sphilipfail8:
133227569Sphilip	EFSYS_PROBE(fail8);
134227569Sphilipfail7:
135227569Sphilip	EFSYS_PROBE(fail7);
136227569Sphilipfail6:
137227569Sphilip	EFSYS_PROBE(fail6);
138227569Sphilipfail5:
139227569Sphilip	EFSYS_PROBE(fail5);
140227569Sphilipfail4:
141227569Sphilip	EFSYS_PROBE(fail4);
142227569Sphilipfail3:
143227569Sphilip	EFSYS_PROBE(fail3);
144227569Sphilipfail2:
145227569Sphilip	EFSYS_PROBE(fail2);
146227569Sphilip
147227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
148227569Sphilip
149227569Sphilipfail1:
150227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
151227569Sphilip
152227569Sphilip	return (rc);
153227569Sphilip}
154227569Sphilip
155227569Sphilip	__checkReturn		int
156227569Sphilipsiena_vpd_init(
157227569Sphilip	__in			efx_nic_t *enp)
158227569Sphilip{
159227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
160227569Sphilip	caddr_t svpd = NULL;
161227569Sphilip	unsigned partn;
162227569Sphilip	size_t size = 0;
163227569Sphilip	int rc;
164227569Sphilip
165227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
166227569Sphilip
167227569Sphilip	partn = (emip->emi_port == 1)
168227569Sphilip		? MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0
169227569Sphilip		: MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1;
170227569Sphilip
171227569Sphilip	/*
172227569Sphilip	 * We need the static VPD sector to present a unified static+dynamic
173227569Sphilip	 * VPD, that is, basically on every read, write, verify cycle. Since
174227569Sphilip	 * it should *never* change we can just cache it here.
175227569Sphilip	 */
176227569Sphilip	if ((rc = siena_vpd_get_static(enp, partn, &svpd, &size)) != 0)
177227569Sphilip		goto fail1;
178227569Sphilip
179227569Sphilip	if (svpd != NULL && size > 0) {
180227569Sphilip		if ((rc = efx_vpd_hunk_verify(svpd, size, NULL)) != 0)
181227569Sphilip			goto fail2;
182227569Sphilip	}
183227569Sphilip
184227569Sphilip	enp->en_u.siena.enu_svpd = svpd;
185227569Sphilip	enp->en_u.siena.enu_svpd_length = size;
186227569Sphilip
187227569Sphilip	return (0);
188227569Sphilip
189227569Sphilipfail2:
190227569Sphilip	EFSYS_PROBE(fail2);
191227569Sphilip
192227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, svpd);
193227569Sphilipfail1:
194227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
195227569Sphilip
196227569Sphilip	return (rc);
197227569Sphilip}
198227569Sphilip
199227569Sphilip	__checkReturn		int
200227569Sphilipsiena_vpd_size(
201227569Sphilip	__in			efx_nic_t *enp,
202227569Sphilip	__out			size_t *sizep)
203227569Sphilip{
204227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
205227569Sphilip	unsigned int partn;
206227569Sphilip	int rc;
207227569Sphilip
208227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
209227569Sphilip
210227569Sphilip	/*
211227569Sphilip	 * This function returns the total size the user should allocate
212227569Sphilip	 * for all VPD operations. We've already cached the static vpd,
213227569Sphilip	 * so we just need to return an upper bound on the dynamic vpd.
214227569Sphilip	 * Since the dynamic_config structure can change under our feet,
215227569Sphilip	 * (as version numbers are inserted), just be safe and return the
216227569Sphilip	 * total size of the dynamic_config *sector*
217227569Sphilip	 */
218227569Sphilip	partn = (emip->emi_port == 1)
219227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
220227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
221227569Sphilip
222227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, sizep)) != 0)
223227569Sphilip		goto fail1;
224227569Sphilip
225227569Sphilip	return (0);
226227569Sphilip
227227569Sphilipfail1:
228227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
229227569Sphilip
230227569Sphilip	return (rc);
231227569Sphilip}
232227569Sphilip
233227569Sphilip	__checkReturn		int
234227569Sphilipsiena_vpd_read(
235227569Sphilip	__in			efx_nic_t *enp,
236227569Sphilip	__out_bcount(size)	caddr_t data,
237227569Sphilip	__in			size_t size)
238227569Sphilip{
239227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
240227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg;
241227569Sphilip	unsigned int vpd_length;
242227569Sphilip	unsigned int vpd_offset;
243227569Sphilip	unsigned int dcfg_partn;
244227569Sphilip	size_t dcfg_size;
245227569Sphilip	int rc;
246227569Sphilip
247227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
248227569Sphilip
249227569Sphilip	dcfg_partn = (emip->emi_port == 1)
250227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
251227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
252227569Sphilip
253227569Sphilip	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
254227569Sphilip	    B_TRUE, &dcfg, &dcfg_size)) != 0)
255227569Sphilip		goto fail1;
256227569Sphilip
257227569Sphilip	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
258227569Sphilip	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
259227569Sphilip
260227569Sphilip	if (vpd_length > size) {
261227569Sphilip		rc = EFAULT;	/* Invalid dcfg: header bigger than sector */
262227569Sphilip		goto fail2;
263227569Sphilip	}
264227569Sphilip
265227569Sphilip	EFSYS_ASSERT3U(vpd_length, <=, size);
266227569Sphilip	memcpy(data, (caddr_t)dcfg + vpd_offset, vpd_length);
267227569Sphilip
268227569Sphilip	/* Pad data with all-1s, consistent with update operations */
269227569Sphilip	memset(data + vpd_length, 0xff, size - vpd_length);
270227569Sphilip
271227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
272227569Sphilip
273227569Sphilip	return (0);
274227569Sphilip
275227569Sphilipfail2:
276227569Sphilip	EFSYS_PROBE(fail2);
277227569Sphilip
278227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
279227569Sphilipfail1:
280227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
281227569Sphilip
282227569Sphilip	return (rc);
283227569Sphilip}
284227569Sphilip
285227569Sphilip	__checkReturn		int
286227569Sphilipsiena_vpd_verify(
287227569Sphilip	__in			efx_nic_t *enp,
288227569Sphilip	__in_bcount(size)	caddr_t data,
289227569Sphilip	__in			size_t size)
290227569Sphilip{
291227569Sphilip	efx_vpd_tag_t stag;
292227569Sphilip	efx_vpd_tag_t dtag;
293227569Sphilip	efx_vpd_keyword_t skey;
294227569Sphilip	efx_vpd_keyword_t dkey;
295227569Sphilip	unsigned int scont;
296227569Sphilip	unsigned int dcont;
297227569Sphilip
298227569Sphilip	int rc;
299227569Sphilip
300227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
301227569Sphilip
302227569Sphilip	/*
303227569Sphilip	 * Strictly you could take the view that dynamic vpd is optional.
304227569Sphilip	 * Instead, to conform more closely to the read/verify/reinit()
305227569Sphilip	 * paradigm, we require dynamic vpd. siena_vpd_reinit() will
306227569Sphilip	 * reinitialize it as required.
307227569Sphilip	 */
308227569Sphilip	if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
309227569Sphilip		goto fail1;
310227569Sphilip
311227569Sphilip	/*
312227569Sphilip	 * Verify that there is no duplication between the static and
313227569Sphilip	 * dynamic cfg sectors.
314227569Sphilip	 */
315227569Sphilip	if (enp->en_u.siena.enu_svpd_length == 0)
316227569Sphilip		goto done;
317227569Sphilip
318227569Sphilip	dcont = 0;
319227569Sphilip	_NOTE(CONSTANTCONDITION)
320227569Sphilip	while (1) {
321227569Sphilip		if ((rc = efx_vpd_hunk_next(data, size, &dtag,
322227569Sphilip		    &dkey, NULL, NULL, &dcont)) != 0)
323227569Sphilip			goto fail2;
324227569Sphilip		if (dcont == 0)
325227569Sphilip			break;
326227569Sphilip
327227569Sphilip		scont = 0;
328227569Sphilip		_NOTE(CONSTANTCONDITION)
329227569Sphilip		while (1) {
330227569Sphilip			if ((rc = efx_vpd_hunk_next(
331227569Sphilip			    enp->en_u.siena.enu_svpd,
332227569Sphilip			    enp->en_u.siena.enu_svpd_length, &stag, &skey,
333227569Sphilip			    NULL, NULL, &scont)) != 0)
334227569Sphilip				goto fail3;
335227569Sphilip			if (scont == 0)
336227569Sphilip				break;
337227569Sphilip
338227569Sphilip			if (stag == dtag && skey == dkey) {
339227569Sphilip				rc = EEXIST;
340227569Sphilip				goto fail4;
341227569Sphilip			}
342227569Sphilip		}
343227569Sphilip	}
344227569Sphilip
345227569Sphilipdone:
346227569Sphilip	return (0);
347227569Sphilip
348227569Sphilipfail4:
349227569Sphilip	EFSYS_PROBE(fail4);
350227569Sphilipfail3:
351227569Sphilip	EFSYS_PROBE(fail3);
352227569Sphilipfail2:
353227569Sphilip	EFSYS_PROBE(fail2);
354227569Sphilipfail1:
355227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
356227569Sphilip
357227569Sphilip	return (rc);
358227569Sphilip}
359227569Sphilip
360227569Sphilip	__checkReturn		int
361227569Sphilipsiena_vpd_reinit(
362227569Sphilip	__in			efx_nic_t *enp,
363227569Sphilip	__in_bcount(size)	caddr_t data,
364227569Sphilip	__in			size_t size)
365227569Sphilip{
366227569Sphilip	boolean_t wantpid;
367227569Sphilip	int rc;
368227569Sphilip
369227569Sphilip	/*
370227569Sphilip	 * Only create a PID if the dynamic cfg doesn't have one
371227569Sphilip	 */
372227569Sphilip	if (enp->en_u.siena.enu_svpd_length == 0)
373227569Sphilip		wantpid = B_TRUE;
374227569Sphilip	else {
375227569Sphilip		unsigned int offset;
376227569Sphilip		uint8_t length;
377227569Sphilip
378227569Sphilip		rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
379227569Sphilip				    enp->en_u.siena.enu_svpd_length,
380227569Sphilip				    EFX_VPD_ID, 0, &offset, &length);
381227569Sphilip		if (rc == 0)
382227569Sphilip			wantpid = B_FALSE;
383227569Sphilip		else if (rc == ENOENT)
384227569Sphilip			wantpid = B_TRUE;
385227569Sphilip		else
386227569Sphilip			goto fail1;
387227569Sphilip	}
388227569Sphilip
389227569Sphilip	if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
390227569Sphilip		goto fail2;
391227569Sphilip
392227569Sphilip	return (0);
393227569Sphilip
394227569Sphilipfail2:
395227569Sphilip	EFSYS_PROBE(fail2);
396227569Sphilipfail1:
397227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
398227569Sphilip
399227569Sphilip	return (rc);
400227569Sphilip}
401227569Sphilip
402227569Sphilip	__checkReturn		int
403227569Sphilipsiena_vpd_get(
404227569Sphilip	__in			efx_nic_t *enp,
405227569Sphilip	__in_bcount(size)	caddr_t data,
406227569Sphilip	__in			size_t size,
407227569Sphilip	__inout			efx_vpd_value_t *evvp)
408227569Sphilip{
409227569Sphilip	unsigned int offset;
410227569Sphilip	uint8_t length;
411227569Sphilip	int rc;
412227569Sphilip
413227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
414227569Sphilip
415227569Sphilip	/* Attempt to satisfy the request from svpd first */
416227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
417227569Sphilip		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
418227569Sphilip		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
419227569Sphilip		    evvp->evv_keyword, &offset, &length)) == 0) {
420227569Sphilip			evvp->evv_length = length;
421227569Sphilip			memcpy(evvp->evv_value,
422227569Sphilip			    enp->en_u.siena.enu_svpd + offset, length);
423227569Sphilip			return (0);
424227569Sphilip		} else if (rc != ENOENT)
425227569Sphilip			goto fail1;
426227569Sphilip	}
427227569Sphilip
428227569Sphilip	/* And then from the provided data buffer */
429227569Sphilip	if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
430227569Sphilip	    evvp->evv_keyword, &offset, &length)) != 0)
431227569Sphilip		goto fail2;
432227569Sphilip
433227569Sphilip	evvp->evv_length = length;
434227569Sphilip	memcpy(evvp->evv_value, data + offset, length);
435227569Sphilip
436227569Sphilip	return (0);
437227569Sphilip
438227569Sphilipfail2:
439227569Sphilip	EFSYS_PROBE(fail2);
440227569Sphilipfail1:
441227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
442227569Sphilip
443227569Sphilip	return (rc);
444227569Sphilip}
445227569Sphilip
446227569Sphilip	__checkReturn		int
447227569Sphilipsiena_vpd_set(
448227569Sphilip	__in			efx_nic_t *enp,
449227569Sphilip	__in_bcount(size)	caddr_t data,
450227569Sphilip	__in			size_t size,
451227569Sphilip	__in			efx_vpd_value_t *evvp)
452227569Sphilip{
453227569Sphilip	int rc;
454227569Sphilip
455227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
456227569Sphilip
457227569Sphilip	/* If the provided (tag,keyword) exists in svpd, then it is readonly */
458227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
459227569Sphilip		unsigned int offset;
460227569Sphilip		uint8_t length;
461227569Sphilip
462227569Sphilip		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
463227569Sphilip		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
464227569Sphilip		    evvp->evv_keyword, &offset, &length)) == 0) {
465227569Sphilip			rc = EACCES;
466227569Sphilip			goto fail1;
467227569Sphilip		}
468227569Sphilip	}
469227569Sphilip
470227569Sphilip	if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
471227569Sphilip		goto fail2;
472227569Sphilip
473227569Sphilip	return (0);
474227569Sphilip
475227569Sphilipfail2:
476227569Sphilip	EFSYS_PROBE(fail2);
477227569Sphilipfail1:
478227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
479227569Sphilip
480227569Sphilip	return (rc);
481227569Sphilip}
482227569Sphilip
483227569Sphilip	__checkReturn		int
484227569Sphilipsiena_vpd_next(
485227569Sphilip	__in			efx_nic_t *enp,
486227569Sphilip	__in_bcount(size)	caddr_t data,
487227569Sphilip	__in			size_t size,
488227569Sphilip	__out			efx_vpd_value_t *evvp,
489227569Sphilip	__inout			unsigned int *contp)
490227569Sphilip{
491227569Sphilip	_NOTE(ARGUNUSED(enp, data, size, evvp, contp))
492227569Sphilip
493227569Sphilip	return (ENOTSUP);
494227569Sphilip}
495227569Sphilip
496227569Sphilip	__checkReturn		int
497227569Sphilipsiena_vpd_write(
498227569Sphilip	__in			efx_nic_t *enp,
499227569Sphilip	__in_bcount(size)	caddr_t data,
500227569Sphilip	__in			size_t size)
501227569Sphilip{
502227569Sphilip	efx_mcdi_iface_t *emip = &(enp->en_u.siena.enu_mip);
503227569Sphilip	siena_mc_dynamic_config_hdr_t *dcfg;
504227569Sphilip	unsigned int vpd_offset;
505227569Sphilip	unsigned int dcfg_partn;
506227569Sphilip	unsigned int hdr_length;
507227569Sphilip	unsigned int pos;
508227569Sphilip	uint8_t cksum;
509227569Sphilip	size_t partn_size, dcfg_size;
510227569Sphilip	size_t vpd_length;
511227569Sphilip	int rc;
512227569Sphilip
513227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
514227569Sphilip
515227569Sphilip	/* Determine total length of all tags */
516227569Sphilip	if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
517227569Sphilip		goto fail1;
518227569Sphilip
519227569Sphilip	/* Lock dynamic config sector for write, and read structure only */
520227569Sphilip	dcfg_partn = (emip->emi_port == 1)
521227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
522227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
523227569Sphilip
524227569Sphilip	if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
525227569Sphilip		goto fail2;
526227569Sphilip
527227569Sphilip	if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
528227569Sphilip		goto fail2;
529227569Sphilip
530227569Sphilip	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
531227569Sphilip	    B_FALSE, &dcfg, &dcfg_size)) != 0)
532227569Sphilip		goto fail3;
533227569Sphilip
534227569Sphilip	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
535227569Sphilip
536227569Sphilip	/* Allocated memory should have room for the new VPD */
537227569Sphilip	if (hdr_length + vpd_length > dcfg_size) {
538227569Sphilip		rc = ENOSPC;
539227569Sphilip		goto fail3;
540227569Sphilip	}
541227569Sphilip
542227569Sphilip	/* Copy in new vpd and update header */
543227569Sphilip	vpd_offset = dcfg_size - vpd_length;
544227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset,
545227569Sphilip			     EFX_DWORD_0, vpd_offset);
546227569Sphilip	memcpy((caddr_t)dcfg + vpd_offset, data, vpd_length);
547227569Sphilip	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length,
548227569Sphilip			    EFX_DWORD_0, vpd_length);
549227569Sphilip
550227569Sphilip	/* Update the checksum */
551227569Sphilip	cksum = 0;
552227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
553227569Sphilip		cksum += ((uint8_t *)dcfg)[pos];
554227569Sphilip	dcfg->csum.eb_u8[0] -= cksum;
555227569Sphilip
556227569Sphilip	/* Erase and write the new sector */
557227569Sphilip	if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
558227569Sphilip		goto fail4;
559227569Sphilip
560227569Sphilip	/* Write out the new structure to nvram */
561227569Sphilip	if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0, (caddr_t)dcfg,
562227569Sphilip	    vpd_offset + vpd_length)) != 0)
563227569Sphilip		goto fail5;
564227569Sphilip
565227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
566227569Sphilip
567227569Sphilip	siena_nvram_partn_unlock(enp, dcfg_partn);
568227569Sphilip
569227569Sphilip	return (0);
570227569Sphilip
571227569Sphilipfail5:
572227569Sphilip	EFSYS_PROBE(fail5);
573227569Sphilipfail4:
574227569Sphilip	EFSYS_PROBE(fail4);
575227569Sphilipfail3:
576227569Sphilip	EFSYS_PROBE(fail3);
577227569Sphilip
578227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
579227569Sphilipfail2:
580227569Sphilip	EFSYS_PROBE(fail2);
581227569Sphilip
582227569Sphilip	siena_nvram_partn_unlock(enp, dcfg_partn);
583227569Sphilipfail1:
584227569Sphilip	EFSYS_PROBE1(fail1, int, rc);
585227569Sphilip
586227569Sphilip	return (rc);
587227569Sphilip}
588227569Sphilip
589227569Sphilip				void
590227569Sphilipsiena_vpd_fini(
591227569Sphilip	__in			efx_nic_t *enp)
592227569Sphilip{
593227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
594227569Sphilip
595227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
596227569Sphilip		EFSYS_KMEM_FREE(enp->en_esip, enp->en_u.siena.enu_svpd_length,
597227569Sphilip				enp->en_u.siena.enu_svpd);
598227569Sphilip
599227569Sphilip		enp->en_u.siena.enu_svpd = NULL;
600227569Sphilip		enp->en_u.siena.enu_svpd_length = 0;
601227569Sphilip	}
602227569Sphilip}
603227569Sphilip
604227569Sphilip#endif	/* EFSYS_OPT_SIENA */
605227569Sphilip
606227569Sphilip#endif	/* EFSYS_OPT_VPD */
607