1227569Sphilip/*-
2301388Sarybchik * Copyright (c) 2009-2016 Solarflare Communications Inc.
3284555Sarybchik * All rights reserved.
4227569Sphilip *
5227569Sphilip * Redistribution and use in source and binary forms, with or without
6284555Sarybchik * modification, are permitted provided that the following conditions are met:
7227569Sphilip *
8284555Sarybchik * 1. Redistributions of source code must retain the above copyright notice,
9284555Sarybchik *    this list of conditions and the following disclaimer.
10284555Sarybchik * 2. Redistributions in binary form must reproduce the above copyright notice,
11284555Sarybchik *    this list of conditions and the following disclaimer in the documentation
12284555Sarybchik *    and/or other materials provided with the distribution.
13284555Sarybchik *
14284555Sarybchik * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15284555Sarybchik * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
16284555Sarybchik * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17284555Sarybchik * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
18284555Sarybchik * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19284555Sarybchik * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20284555Sarybchik * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21284555Sarybchik * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22284555Sarybchik * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23284555Sarybchik * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
24284555Sarybchik * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25284555Sarybchik *
26284555Sarybchik * The views and conclusions contained in the software and documentation are
27284555Sarybchik * those of the authors and should not be interpreted as representing official
28284555Sarybchik * policies, either expressed or implied, of the FreeBSD Project.
29227569Sphilip */
30227569Sphilip
31228078Sphilip#include <sys/cdefs.h>
32228078Sphilip__FBSDID("$FreeBSD: stable/10/sys/dev/sfxge/common/siena_vpd.c 311062 2017-01-02 09:12:06Z arybchik $");
33228078Sphilip
34227569Sphilip#include "efx.h"
35227569Sphilip#include "efx_impl.h"
36227569Sphilip
37227569Sphilip#if EFSYS_OPT_VPD
38227569Sphilip
39227569Sphilip#if EFSYS_OPT_SIENA
40227569Sphilip
41293927Sarybchikstatic	__checkReturn			efx_rc_t
42227569Sphilipsiena_vpd_get_static(
43227569Sphilip	__in				efx_nic_t *enp,
44294002Sarybchik	__in				uint32_t partn,
45227569Sphilip	__deref_out_bcount_opt(*sizep)	caddr_t *svpdp,
46227569Sphilip	__out				size_t *sizep)
47227569Sphilip{
48227569Sphilip	siena_mc_static_config_hdr_t *scfg;
49227569Sphilip	caddr_t svpd;
50227569Sphilip	size_t size;
51227569Sphilip	uint8_t cksum;
52227569Sphilip	unsigned int vpd_offset;
53227569Sphilip	unsigned int vpd_length;
54227569Sphilip	unsigned int hdr_length;
55227569Sphilip	unsigned int pos;
56227569Sphilip	unsigned int region;
57293927Sarybchik	efx_rc_t rc;
58227569Sphilip
59227569Sphilip	EFSYS_ASSERT(partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0 ||
60227569Sphilip		    partn == MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1);
61227569Sphilip
62227569Sphilip	/* Allocate sufficient memory for the entire static cfg area */
63227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, &size)) != 0)
64227569Sphilip		goto fail1;
65227569Sphilip
66227569Sphilip	EFSYS_KMEM_ALLOC(enp->en_esip, size, scfg);
67227569Sphilip	if (scfg == NULL) {
68227569Sphilip		rc = ENOMEM;
69227569Sphilip		goto fail2;
70227569Sphilip	}
71227569Sphilip
72227569Sphilip	if ((rc = siena_nvram_partn_read(enp, partn, 0,
73227569Sphilip	    (caddr_t)scfg, SIENA_NVRAM_CHUNK)) != 0)
74227569Sphilip		goto fail3;
75227569Sphilip
76227569Sphilip	/* Verify the magic number */
77227569Sphilip	if (EFX_DWORD_FIELD(scfg->magic, EFX_DWORD_0) !=
78227569Sphilip	    SIENA_MC_STATIC_CONFIG_MAGIC) {
79227569Sphilip		rc = EINVAL;
80227569Sphilip		goto fail4;
81227569Sphilip	}
82227569Sphilip
83227569Sphilip	/* All future versions of the structure must be backwards compatable */
84227569Sphilip	EFX_STATIC_ASSERT(SIENA_MC_STATIC_CONFIG_VERSION == 0);
85227569Sphilip
86227569Sphilip	hdr_length = EFX_WORD_FIELD(scfg->length, EFX_WORD_0);
87227569Sphilip	vpd_offset = EFX_DWORD_FIELD(scfg->static_vpd_offset, EFX_DWORD_0);
88227569Sphilip	vpd_length = EFX_DWORD_FIELD(scfg->static_vpd_length, EFX_DWORD_0);
89227569Sphilip
90227569Sphilip	/* Verify the hdr doesn't overflow the sector size */
91227569Sphilip	if (hdr_length > size || vpd_offset > size || vpd_length > size ||
92227569Sphilip	    vpd_length + vpd_offset > size) {
93227569Sphilip		rc = EINVAL;
94227569Sphilip		goto fail5;
95227569Sphilip	}
96227569Sphilip
97227569Sphilip	/* Read the remainder of scfg + static vpd */
98227569Sphilip	region = vpd_offset + vpd_length;
99227569Sphilip	if (region > SIENA_NVRAM_CHUNK) {
100227569Sphilip		if ((rc = siena_nvram_partn_read(enp, partn, SIENA_NVRAM_CHUNK,
101227569Sphilip		    (caddr_t)scfg + SIENA_NVRAM_CHUNK,
102227569Sphilip		    region - SIENA_NVRAM_CHUNK)) != 0)
103227569Sphilip			goto fail6;
104227569Sphilip	}
105227569Sphilip
106227569Sphilip	/* Verify checksum */
107227569Sphilip	cksum = 0;
108227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
109227569Sphilip		cksum += ((uint8_t *)scfg)[pos];
110227569Sphilip	if (cksum != 0) {
111227569Sphilip		rc = EINVAL;
112227569Sphilip		goto fail7;
113227569Sphilip	}
114227569Sphilip
115227569Sphilip	if (vpd_length == 0)
116227569Sphilip		svpd = NULL;
117227569Sphilip	else {
118227569Sphilip		/* Copy the vpd data out */
119227569Sphilip		EFSYS_KMEM_ALLOC(enp->en_esip, vpd_length, svpd);
120227569Sphilip		if (svpd == NULL) {
121227569Sphilip			rc = ENOMEM;
122227569Sphilip			goto fail8;
123227569Sphilip		}
124227569Sphilip		memcpy(svpd, (caddr_t)scfg + vpd_offset, vpd_length);
125227569Sphilip	}
126227569Sphilip
127227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
128227569Sphilip
129227569Sphilip	*svpdp = svpd;
130227569Sphilip	*sizep = vpd_length;
131227569Sphilip
132227569Sphilip	return (0);
133227569Sphilip
134227569Sphilipfail8:
135227569Sphilip	EFSYS_PROBE(fail8);
136227569Sphilipfail7:
137227569Sphilip	EFSYS_PROBE(fail7);
138227569Sphilipfail6:
139227569Sphilip	EFSYS_PROBE(fail6);
140227569Sphilipfail5:
141227569Sphilip	EFSYS_PROBE(fail5);
142227569Sphilipfail4:
143227569Sphilip	EFSYS_PROBE(fail4);
144227569Sphilipfail3:
145227569Sphilip	EFSYS_PROBE(fail3);
146227569Sphilip
147227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, scfg);
148227569Sphilip
149284555Sarybchikfail2:
150284555Sarybchik	EFSYS_PROBE(fail2);
151227569Sphilipfail1:
152293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
153227569Sphilip
154227569Sphilip	return (rc);
155227569Sphilip}
156227569Sphilip
157293927Sarybchik	__checkReturn		efx_rc_t
158227569Sphilipsiena_vpd_init(
159227569Sphilip	__in			efx_nic_t *enp)
160227569Sphilip{
161284555Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
162227569Sphilip	caddr_t svpd = NULL;
163311062Sarybchik	unsigned int partn;
164227569Sphilip	size_t size = 0;
165293927Sarybchik	efx_rc_t rc;
166227569Sphilip
167227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
168227569Sphilip
169227569Sphilip	partn = (emip->emi_port == 1)
170227569Sphilip		? MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT0
171227569Sphilip		: MC_CMD_NVRAM_TYPE_STATIC_CFG_PORT1;
172227569Sphilip
173227569Sphilip	/*
174227569Sphilip	 * We need the static VPD sector to present a unified static+dynamic
175227569Sphilip	 * VPD, that is, basically on every read, write, verify cycle. Since
176227569Sphilip	 * it should *never* change we can just cache it here.
177227569Sphilip	 */
178227569Sphilip	if ((rc = siena_vpd_get_static(enp, partn, &svpd, &size)) != 0)
179227569Sphilip		goto fail1;
180227569Sphilip
181227569Sphilip	if (svpd != NULL && size > 0) {
182227569Sphilip		if ((rc = efx_vpd_hunk_verify(svpd, size, NULL)) != 0)
183227569Sphilip			goto fail2;
184227569Sphilip	}
185227569Sphilip
186227569Sphilip	enp->en_u.siena.enu_svpd = svpd;
187227569Sphilip	enp->en_u.siena.enu_svpd_length = size;
188227569Sphilip
189227569Sphilip	return (0);
190227569Sphilip
191227569Sphilipfail2:
192227569Sphilip	EFSYS_PROBE(fail2);
193227569Sphilip
194227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, size, svpd);
195227569Sphilipfail1:
196293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
197227569Sphilip
198227569Sphilip	return (rc);
199227569Sphilip}
200227569Sphilip
201293927Sarybchik	__checkReturn		efx_rc_t
202227569Sphilipsiena_vpd_size(
203227569Sphilip	__in			efx_nic_t *enp,
204227569Sphilip	__out			size_t *sizep)
205227569Sphilip{
206284555Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
207294002Sarybchik	uint32_t partn;
208293927Sarybchik	efx_rc_t rc;
209227569Sphilip
210227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
211227569Sphilip
212227569Sphilip	/*
213227569Sphilip	 * This function returns the total size the user should allocate
214227569Sphilip	 * for all VPD operations. We've already cached the static vpd,
215227569Sphilip	 * so we just need to return an upper bound on the dynamic vpd.
216227569Sphilip	 * Since the dynamic_config structure can change under our feet,
217227569Sphilip	 * (as version numbers are inserted), just be safe and return the
218227569Sphilip	 * total size of the dynamic_config *sector*
219227569Sphilip	 */
220227569Sphilip	partn = (emip->emi_port == 1)
221227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
222227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
223227569Sphilip
224227569Sphilip	if ((rc = siena_nvram_partn_size(enp, partn, sizep)) != 0)
225227569Sphilip		goto fail1;
226227569Sphilip
227227569Sphilip	return (0);
228227569Sphilip
229227569Sphilipfail1:
230293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
231227569Sphilip
232227569Sphilip	return (rc);
233227569Sphilip}
234227569Sphilip
235293927Sarybchik	__checkReturn		efx_rc_t
236227569Sphilipsiena_vpd_read(
237227569Sphilip	__in			efx_nic_t *enp,
238227569Sphilip	__out_bcount(size)	caddr_t data,
239227569Sphilip	__in			size_t size)
240227569Sphilip{
241284555Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
242284555Sarybchik	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
243227569Sphilip	unsigned int vpd_length;
244227569Sphilip	unsigned int vpd_offset;
245227569Sphilip	unsigned int dcfg_partn;
246227569Sphilip	size_t dcfg_size;
247293927Sarybchik	efx_rc_t rc;
248227569Sphilip
249227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
250227569Sphilip
251227569Sphilip	dcfg_partn = (emip->emi_port == 1)
252227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
253227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
254227569Sphilip
255227569Sphilip	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
256227569Sphilip	    B_TRUE, &dcfg, &dcfg_size)) != 0)
257227569Sphilip		goto fail1;
258227569Sphilip
259227569Sphilip	vpd_length = EFX_DWORD_FIELD(dcfg->dynamic_vpd_length, EFX_DWORD_0);
260227569Sphilip	vpd_offset = EFX_DWORD_FIELD(dcfg->dynamic_vpd_offset, EFX_DWORD_0);
261227569Sphilip
262227569Sphilip	if (vpd_length > size) {
263227569Sphilip		rc = EFAULT;	/* Invalid dcfg: header bigger than sector */
264227569Sphilip		goto fail2;
265227569Sphilip	}
266227569Sphilip
267227569Sphilip	EFSYS_ASSERT3U(vpd_length, <=, size);
268227569Sphilip	memcpy(data, (caddr_t)dcfg + vpd_offset, vpd_length);
269227569Sphilip
270227569Sphilip	/* Pad data with all-1s, consistent with update operations */
271227569Sphilip	memset(data + vpd_length, 0xff, size - vpd_length);
272227569Sphilip
273227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
274227569Sphilip
275227569Sphilip	return (0);
276227569Sphilip
277227569Sphilipfail2:
278227569Sphilip	EFSYS_PROBE(fail2);
279227569Sphilip
280227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
281227569Sphilipfail1:
282293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
283227569Sphilip
284227569Sphilip	return (rc);
285227569Sphilip}
286227569Sphilip
287293927Sarybchik	__checkReturn		efx_rc_t
288227569Sphilipsiena_vpd_verify(
289227569Sphilip	__in			efx_nic_t *enp,
290227569Sphilip	__in_bcount(size)	caddr_t data,
291227569Sphilip	__in			size_t size)
292227569Sphilip{
293227569Sphilip	efx_vpd_tag_t stag;
294227569Sphilip	efx_vpd_tag_t dtag;
295227569Sphilip	efx_vpd_keyword_t skey;
296227569Sphilip	efx_vpd_keyword_t dkey;
297227569Sphilip	unsigned int scont;
298227569Sphilip	unsigned int dcont;
299227569Sphilip
300293927Sarybchik	efx_rc_t rc;
301227569Sphilip
302227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
303227569Sphilip
304227569Sphilip	/*
305227569Sphilip	 * Strictly you could take the view that dynamic vpd is optional.
306227569Sphilip	 * Instead, to conform more closely to the read/verify/reinit()
307227569Sphilip	 * paradigm, we require dynamic vpd. siena_vpd_reinit() will
308227569Sphilip	 * reinitialize it as required.
309227569Sphilip	 */
310227569Sphilip	if ((rc = efx_vpd_hunk_verify(data, size, NULL)) != 0)
311227569Sphilip		goto fail1;
312227569Sphilip
313227569Sphilip	/*
314227569Sphilip	 * Verify that there is no duplication between the static and
315227569Sphilip	 * dynamic cfg sectors.
316227569Sphilip	 */
317227569Sphilip	if (enp->en_u.siena.enu_svpd_length == 0)
318227569Sphilip		goto done;
319227569Sphilip
320227569Sphilip	dcont = 0;
321227569Sphilip	_NOTE(CONSTANTCONDITION)
322227569Sphilip	while (1) {
323227569Sphilip		if ((rc = efx_vpd_hunk_next(data, size, &dtag,
324227569Sphilip		    &dkey, NULL, NULL, &dcont)) != 0)
325227569Sphilip			goto fail2;
326227569Sphilip		if (dcont == 0)
327227569Sphilip			break;
328227569Sphilip
329294383Sarybchik		/*
330294383Sarybchik		 * Skip the RV keyword. It should be present in both the static
331294383Sarybchik		 * and dynamic cfg sectors.
332294383Sarybchik		 */
333294383Sarybchik		if (dtag == EFX_VPD_RO && dkey == EFX_VPD_KEYWORD('R', 'V'))
334294383Sarybchik			continue;
335294383Sarybchik
336227569Sphilip		scont = 0;
337227569Sphilip		_NOTE(CONSTANTCONDITION)
338227569Sphilip		while (1) {
339227569Sphilip			if ((rc = efx_vpd_hunk_next(
340227569Sphilip			    enp->en_u.siena.enu_svpd,
341227569Sphilip			    enp->en_u.siena.enu_svpd_length, &stag, &skey,
342227569Sphilip			    NULL, NULL, &scont)) != 0)
343227569Sphilip				goto fail3;
344227569Sphilip			if (scont == 0)
345227569Sphilip				break;
346227569Sphilip
347227569Sphilip			if (stag == dtag && skey == dkey) {
348227569Sphilip				rc = EEXIST;
349227569Sphilip				goto fail4;
350227569Sphilip			}
351227569Sphilip		}
352227569Sphilip	}
353227569Sphilip
354227569Sphilipdone:
355227569Sphilip	return (0);
356227569Sphilip
357227569Sphilipfail4:
358227569Sphilip	EFSYS_PROBE(fail4);
359227569Sphilipfail3:
360227569Sphilip	EFSYS_PROBE(fail3);
361227569Sphilipfail2:
362227569Sphilip	EFSYS_PROBE(fail2);
363227569Sphilipfail1:
364293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
365227569Sphilip
366227569Sphilip	return (rc);
367227569Sphilip}
368227569Sphilip
369293927Sarybchik	__checkReturn		efx_rc_t
370227569Sphilipsiena_vpd_reinit(
371227569Sphilip	__in			efx_nic_t *enp,
372227569Sphilip	__in_bcount(size)	caddr_t data,
373227569Sphilip	__in			size_t size)
374227569Sphilip{
375227569Sphilip	boolean_t wantpid;
376293927Sarybchik	efx_rc_t rc;
377227569Sphilip
378227569Sphilip	/*
379227569Sphilip	 * Only create a PID if the dynamic cfg doesn't have one
380227569Sphilip	 */
381227569Sphilip	if (enp->en_u.siena.enu_svpd_length == 0)
382227569Sphilip		wantpid = B_TRUE;
383227569Sphilip	else {
384227569Sphilip		unsigned int offset;
385227569Sphilip		uint8_t length;
386227569Sphilip
387227569Sphilip		rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
388227569Sphilip				    enp->en_u.siena.enu_svpd_length,
389227569Sphilip				    EFX_VPD_ID, 0, &offset, &length);
390227569Sphilip		if (rc == 0)
391227569Sphilip			wantpid = B_FALSE;
392227569Sphilip		else if (rc == ENOENT)
393227569Sphilip			wantpid = B_TRUE;
394227569Sphilip		else
395227569Sphilip			goto fail1;
396227569Sphilip	}
397227569Sphilip
398227569Sphilip	if ((rc = efx_vpd_hunk_reinit(data, size, wantpid)) != 0)
399227569Sphilip		goto fail2;
400227569Sphilip
401227569Sphilip	return (0);
402227569Sphilip
403227569Sphilipfail2:
404227569Sphilip	EFSYS_PROBE(fail2);
405227569Sphilipfail1:
406293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
407227569Sphilip
408227569Sphilip	return (rc);
409227569Sphilip}
410227569Sphilip
411293927Sarybchik	__checkReturn		efx_rc_t
412227569Sphilipsiena_vpd_get(
413227569Sphilip	__in			efx_nic_t *enp,
414227569Sphilip	__in_bcount(size)	caddr_t data,
415227569Sphilip	__in			size_t size,
416227569Sphilip	__inout			efx_vpd_value_t *evvp)
417227569Sphilip{
418227569Sphilip	unsigned int offset;
419227569Sphilip	uint8_t length;
420293927Sarybchik	efx_rc_t rc;
421227569Sphilip
422227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
423227569Sphilip
424227569Sphilip	/* Attempt to satisfy the request from svpd first */
425227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
426227569Sphilip		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
427227569Sphilip		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
428227569Sphilip		    evvp->evv_keyword, &offset, &length)) == 0) {
429227569Sphilip			evvp->evv_length = length;
430227569Sphilip			memcpy(evvp->evv_value,
431227569Sphilip			    enp->en_u.siena.enu_svpd + offset, length);
432227569Sphilip			return (0);
433227569Sphilip		} else if (rc != ENOENT)
434227569Sphilip			goto fail1;
435227569Sphilip	}
436227569Sphilip
437227569Sphilip	/* And then from the provided data buffer */
438227569Sphilip	if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
439301363Sarybchik	    evvp->evv_keyword, &offset, &length)) != 0) {
440301363Sarybchik		if (rc == ENOENT)
441301363Sarybchik			return (rc);
442301363Sarybchik
443227569Sphilip		goto fail2;
444301363Sarybchik	}
445227569Sphilip
446227569Sphilip	evvp->evv_length = length;
447227569Sphilip	memcpy(evvp->evv_value, data + offset, length);
448227569Sphilip
449227569Sphilip	return (0);
450227569Sphilip
451227569Sphilipfail2:
452227569Sphilip	EFSYS_PROBE(fail2);
453227569Sphilipfail1:
454293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
455227569Sphilip
456227569Sphilip	return (rc);
457227569Sphilip}
458227569Sphilip
459293927Sarybchik	__checkReturn		efx_rc_t
460227569Sphilipsiena_vpd_set(
461227569Sphilip	__in			efx_nic_t *enp,
462227569Sphilip	__in_bcount(size)	caddr_t data,
463227569Sphilip	__in			size_t size,
464227569Sphilip	__in			efx_vpd_value_t *evvp)
465227569Sphilip{
466293927Sarybchik	efx_rc_t rc;
467227569Sphilip
468227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
469227569Sphilip
470227569Sphilip	/* If the provided (tag,keyword) exists in svpd, then it is readonly */
471227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
472227569Sphilip		unsigned int offset;
473227569Sphilip		uint8_t length;
474227569Sphilip
475227569Sphilip		if ((rc = efx_vpd_hunk_get(enp->en_u.siena.enu_svpd,
476227569Sphilip		    enp->en_u.siena.enu_svpd_length, evvp->evv_tag,
477227569Sphilip		    evvp->evv_keyword, &offset, &length)) == 0) {
478227569Sphilip			rc = EACCES;
479227569Sphilip			goto fail1;
480227569Sphilip		}
481227569Sphilip	}
482227569Sphilip
483227569Sphilip	if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
484227569Sphilip		goto fail2;
485227569Sphilip
486227569Sphilip	return (0);
487227569Sphilip
488227569Sphilipfail2:
489227569Sphilip	EFSYS_PROBE(fail2);
490227569Sphilipfail1:
491293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
492227569Sphilip
493227569Sphilip	return (rc);
494227569Sphilip}
495227569Sphilip
496293927Sarybchik	__checkReturn		efx_rc_t
497227569Sphilipsiena_vpd_next(
498227569Sphilip	__in			efx_nic_t *enp,
499227569Sphilip	__in_bcount(size)	caddr_t data,
500227569Sphilip	__in			size_t size,
501227569Sphilip	__out			efx_vpd_value_t *evvp,
502227569Sphilip	__inout			unsigned int *contp)
503227569Sphilip{
504227569Sphilip	_NOTE(ARGUNUSED(enp, data, size, evvp, contp))
505227569Sphilip
506227569Sphilip	return (ENOTSUP);
507227569Sphilip}
508227569Sphilip
509293927Sarybchik	__checkReturn		efx_rc_t
510227569Sphilipsiena_vpd_write(
511227569Sphilip	__in			efx_nic_t *enp,
512227569Sphilip	__in_bcount(size)	caddr_t data,
513227569Sphilip	__in			size_t size)
514227569Sphilip{
515284555Sarybchik	efx_mcdi_iface_t *emip = &(enp->en_mcdi.em_emip);
516284555Sarybchik	siena_mc_dynamic_config_hdr_t *dcfg = NULL;
517227569Sphilip	unsigned int vpd_offset;
518227569Sphilip	unsigned int dcfg_partn;
519227569Sphilip	unsigned int hdr_length;
520227569Sphilip	unsigned int pos;
521227569Sphilip	uint8_t cksum;
522227569Sphilip	size_t partn_size, dcfg_size;
523227569Sphilip	size_t vpd_length;
524293927Sarybchik	efx_rc_t rc;
525227569Sphilip
526227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
527227569Sphilip
528227569Sphilip	/* Determine total length of all tags */
529227569Sphilip	if ((rc = efx_vpd_hunk_length(data, size, &vpd_length)) != 0)
530227569Sphilip		goto fail1;
531227569Sphilip
532227569Sphilip	/* Lock dynamic config sector for write, and read structure only */
533227569Sphilip	dcfg_partn = (emip->emi_port == 1)
534227569Sphilip		? MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT0
535227569Sphilip		: MC_CMD_NVRAM_TYPE_DYNAMIC_CFG_PORT1;
536227569Sphilip
537227569Sphilip	if ((rc = siena_nvram_partn_size(enp, dcfg_partn, &partn_size)) != 0)
538227569Sphilip		goto fail2;
539227569Sphilip
540227569Sphilip	if ((rc = siena_nvram_partn_lock(enp, dcfg_partn)) != 0)
541284555Sarybchik		goto fail3;
542227569Sphilip
543227569Sphilip	if ((rc = siena_nvram_get_dynamic_cfg(enp, dcfg_partn,
544227569Sphilip	    B_FALSE, &dcfg, &dcfg_size)) != 0)
545284555Sarybchik		goto fail4;
546227569Sphilip
547227569Sphilip	hdr_length = EFX_WORD_FIELD(dcfg->length, EFX_WORD_0);
548227569Sphilip
549227569Sphilip	/* Allocated memory should have room for the new VPD */
550227569Sphilip	if (hdr_length + vpd_length > dcfg_size) {
551227569Sphilip		rc = ENOSPC;
552284555Sarybchik		goto fail5;
553227569Sphilip	}
554227569Sphilip
555227569Sphilip	/* Copy in new vpd and update header */
556227569Sphilip	vpd_offset = dcfg_size - vpd_length;
557280535Sarybchik	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_offset, EFX_DWORD_0, vpd_offset);
558227569Sphilip	memcpy((caddr_t)dcfg + vpd_offset, data, vpd_length);
559280535Sarybchik	EFX_POPULATE_DWORD_1(dcfg->dynamic_vpd_length, EFX_DWORD_0, vpd_length);
560227569Sphilip
561227569Sphilip	/* Update the checksum */
562227569Sphilip	cksum = 0;
563227569Sphilip	for (pos = 0; pos < hdr_length; pos++)
564227569Sphilip		cksum += ((uint8_t *)dcfg)[pos];
565227569Sphilip	dcfg->csum.eb_u8[0] -= cksum;
566227569Sphilip
567227569Sphilip	/* Erase and write the new sector */
568227569Sphilip	if ((rc = siena_nvram_partn_erase(enp, dcfg_partn, 0, partn_size)) != 0)
569284555Sarybchik		goto fail6;
570227569Sphilip
571227569Sphilip	/* Write out the new structure to nvram */
572227569Sphilip	if ((rc = siena_nvram_partn_write(enp, dcfg_partn, 0, (caddr_t)dcfg,
573227569Sphilip	    vpd_offset + vpd_length)) != 0)
574284555Sarybchik		goto fail7;
575227569Sphilip
576227569Sphilip	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
577227569Sphilip
578227569Sphilip	siena_nvram_partn_unlock(enp, dcfg_partn);
579227569Sphilip
580227569Sphilip	return (0);
581227569Sphilip
582284555Sarybchikfail7:
583284555Sarybchik	EFSYS_PROBE(fail7);
584284555Sarybchikfail6:
585284555Sarybchik	EFSYS_PROBE(fail6);
586227569Sphilipfail5:
587227569Sphilip	EFSYS_PROBE(fail5);
588284555Sarybchik
589284555Sarybchik	EFSYS_KMEM_FREE(enp->en_esip, dcfg_size, dcfg);
590227569Sphilipfail4:
591227569Sphilip	EFSYS_PROBE(fail4);
592284555Sarybchik
593284555Sarybchik	siena_nvram_partn_unlock(enp, dcfg_partn);
594227569Sphilipfail3:
595227569Sphilip	EFSYS_PROBE(fail3);
596227569Sphilipfail2:
597227569Sphilip	EFSYS_PROBE(fail2);
598227569Sphilipfail1:
599293927Sarybchik	EFSYS_PROBE1(fail1, efx_rc_t, rc);
600227569Sphilip
601227569Sphilip	return (rc);
602227569Sphilip}
603227569Sphilip
604227569Sphilip				void
605227569Sphilipsiena_vpd_fini(
606227569Sphilip	__in			efx_nic_t *enp)
607227569Sphilip{
608227569Sphilip	EFSYS_ASSERT(enp->en_family == EFX_FAMILY_SIENA);
609227569Sphilip
610227569Sphilip	if (enp->en_u.siena.enu_svpd_length > 0) {
611227569Sphilip		EFSYS_KMEM_FREE(enp->en_esip, enp->en_u.siena.enu_svpd_length,
612227569Sphilip				enp->en_u.siena.enu_svpd);
613227569Sphilip
614227569Sphilip		enp->en_u.siena.enu_svpd = NULL;
615227569Sphilip		enp->en_u.siena.enu_svpd_length = 0;
616227569Sphilip	}
617227569Sphilip}
618227569Sphilip
619227569Sphilip#endif	/* EFSYS_OPT_SIENA */
620227569Sphilip
621227569Sphilip#endif	/* EFSYS_OPT_VPD */
622