1 /*
2 * Copyright 2009 Solarflare Communications Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26 /*
27 * Falcon conviniently uses an EEPROM to store it's VPD configuration,
28 * and it stores the VPD contents in native VPD format. This code does
29 * not cope with the presence of an RW block in VPD at all.
30 */
31
32 #include "efsys.h"
33 #include "efx.h"
34 #include "falcon_nvram.h"
35 #include "efx_types.h"
36 #include "efx_impl.h"
37
38 #if EFSYS_OPT_FALCON
39
40 #if EFSYS_OPT_VPD
41
42 typedef struct {
43 size_t fvpdd_base;
44 size_t fvpdd_size;
45 boolean_t fvpdd_writable;
46 } falcon_vpd_dimension_t;
47
48 static void
49 falcon_vpd_dimension(
50 __in efx_nic_t *enp,
51 __out falcon_vpd_dimension_t *dimp)
52 {
53 efx_oword_t oword;
54
55 #if EFSYS_OPT_FALCON_NIC_CFG_OVERRIDE
56 if (enp->en_u.falcon.enu_forced_cfg != NULL) {
57 memcpy(&oword, (enp->en_u.falcon.enu_forced_cfg
58 + EE_VPD_CFG0_REG_SF_OFST), sizeof (oword));
59 }
60 else
61 #endif /* EFSYS_OPT_FALCON_NIC_CFG_OVERRIDE */
62 {
63 EFX_BAR_READO(enp, FR_AB_EE_VPD_CFG0_REG, &oword);
64 }
65
66 dimp->fvpdd_base = EFX_OWORD_FIELD(oword, FRF_AB_EE_VPD_BASE);
67 dimp->fvpdd_size = EFX_OWORD_FIELD(oword, FRF_AB_EE_VPD_LENGTH);
68 if (dimp->fvpdd_size != 0)
69 /* Non-zero "lengths" are actually maximum dword offsets */
70 dimp->fvpdd_size += 4;
71 dimp->fvpdd_writable =
72 EFX_OWORD_FIELD(oword, FRF_AB_EE_VPDW_LENGTH) != 0;
73 }
74
75 __checkReturn int
76 falcon_vpd_size(
77 __in efx_nic_t *enp,
78 __out size_t *sizep)
79 {
80 falcon_vpd_dimension_t dim;
81 int rc;
82
83 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
84
85 falcon_vpd_dimension(enp, &dim);
86 if (dim.fvpdd_size == 0 || dim.fvpdd_writable) {
87 rc = ENOTSUP;
88 goto fail1;
89 }
90
91 *sizep = dim.fvpdd_size;
92
93 return (0);
94
95 fail1:
96 EFSYS_PROBE1(fail1, int, rc);
97
98 *sizep = 0;
99
100 return (rc);
101 }
102
103 __checkReturn int
104 falcon_vpd_read(
105 __in efx_nic_t *enp,
106 __out_bcount(size) caddr_t data,
107 __in size_t size)
108 {
109 falcon_vpd_dimension_t dim;
110 int rc;
111
112 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
113
114 falcon_vpd_dimension(enp, &dim);
115 if (size < dim.fvpdd_size || size == 0) {
116 rc = ENOTSUP;
117 goto fail1;
118 }
119
120 if ((rc = falcon_spi_dev_read(enp, FALCON_SPI_EEPROM,
121 dim.fvpdd_base, data, size)) != 0)
122 goto fail2;
123
124 return (0);
125
126 fail2:
127 EFSYS_PROBE(fail2);
128 fail1:
129 EFSYS_PROBE1(fail1, int, rc);
130
131 return (rc);
132 }
133
134 __checkReturn int
135 falcon_vpd_verify(
136 __in efx_nic_t *enp,
137 __in_bcount(size) caddr_t data,
138 __in size_t size)
139 {
140 falcon_vpd_dimension_t dim;
141 boolean_t cksummed;
142 int rc;
143
144 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
145
146 falcon_vpd_dimension(enp, &dim);
147 EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
148 EFSYS_ASSERT(!dim.fvpdd_writable);
149
150 if ((rc = efx_vpd_hunk_verify(data, size, &cksummed)) != 0)
151 goto fail1;
152
153 if (!cksummed) {
154 rc = EFAULT;
155 goto fail2;
156 }
157
158 return (0);
159
160 fail2:
161 EFSYS_PROBE(fail2);
162 fail1:
163 EFSYS_PROBE1(fail1, int, rc);
164
165 return (rc);
166 }
167
168 __checkReturn int
169 falcon_vpd_get(
170 __in efx_nic_t *enp,
171 __in_bcount(size) caddr_t data,
172 __in size_t size,
173 __inout efx_vpd_value_t *evvp)
174 {
175 falcon_vpd_dimension_t dim;
176 unsigned int offset;
177 uint8_t length;
178 int rc;
179
180 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
181
182 if (evvp->evv_tag != EFX_VPD_ID && evvp->evv_tag != EFX_VPD_RO) {
183 rc = EINVAL;
184 goto fail1;
185 }
186
187 falcon_vpd_dimension(enp, &dim);
188 EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
189 EFSYS_ASSERT(!dim.fvpdd_writable);
190
191 if ((rc = efx_vpd_hunk_get(data, size, evvp->evv_tag,
192 evvp->evv_keyword, &offset, &length)) != 0)
193 goto fail2;
194
195 /* Copy out */
196 evvp->evv_length = length;
197 memcpy(evvp->evv_value, data + offset, length);
198
199 return (0);
200
201 fail2:
202 EFSYS_PROBE(fail2);
203 fail1:
204 EFSYS_PROBE1(fail1, int, rc);
205
206 return (rc);
207 }
208
209 __checkReturn int
210 falcon_vpd_set(
211 __in efx_nic_t *enp,
212 __in_bcount(size) caddr_t data,
213 __in size_t size,
214 __in efx_vpd_value_t *evvp)
215 {
216 falcon_vpd_dimension_t dim;
217 int rc;
218
219 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
220
221 falcon_vpd_dimension(enp, &dim);
222 EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
223
224 if ((rc = efx_vpd_hunk_set(data, size, evvp)) != 0)
225 goto fail1;
226
227 fail1:
228 EFSYS_PROBE1(fail1, int, rc);
229
230 return (rc);
231 }
232
233 __checkReturn int
234 falcon_vpd_next(
235 __in efx_nic_t *enp,
236 __in_bcount(size) caddr_t data,
237 __in size_t size,
238 __out efx_vpd_value_t *evvp,
239 __inout unsigned int *contp)
240 {
241 falcon_vpd_dimension_t dim;
242 unsigned int offset;
243 int rc;
244
245 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
246
247 falcon_vpd_dimension(enp, &dim);
248 EFSYS_ASSERT3U(dim.fvpdd_size, <=, size);
249 EFSYS_ASSERT(!dim.fvpdd_writable);
250
251 /* Find the (tag, keyword) */
252 if ((rc = efx_vpd_hunk_next(data, size, &evvp->evv_tag,
253 &evvp->evv_keyword, &offset, &evvp->evv_length, contp)) != 0)
254 goto fail1;
255
256 /* Copyout */
257 memcpy(evvp->evv_value, data + offset, evvp->evv_length);
258
259 return (0);
260
261 fail1:
262 EFSYS_PROBE1(fail1, int, rc);
263
264 return (rc);
265 }
266
267 __checkReturn int
268 falcon_vpd_write(
269 __in efx_nic_t *enp,
270 __in_bcount(size) caddr_t data,
271 __in size_t size)
272 {
273 falcon_vpd_dimension_t dim;
274 int rc;
275
276 EFSYS_ASSERT3U(enp->en_family, ==, EFX_FAMILY_FALCON);
277
278 falcon_vpd_dimension(enp, &dim);
279 if (dim.fvpdd_size != size) {
280 /* User hasn't provided sufficient data */
281 rc = EINVAL;
282 goto fail1;
283 }
284
285 if ((rc = falcon_spi_dev_write(enp, FALCON_SPI_EEPROM,
286 dim.fvpdd_base, data, dim.fvpdd_size)) != 0)
287 goto fail2;
288
289 return (0);
290
291 fail2:
292 EFSYS_PROBE(fail2);
293 fail1:
294 EFSYS_PROBE1(fail1, int, rc);
295
296 return (rc);
297 }
298
299 #endif /* EFSYS_OPT_FALCON */
300
301 #endif /* EFSYS_OPT_VPD */