]>
Commit | Line | Data |
---|---|---|
77241056 | 1 | /* |
05d6ac1d | 2 | * Copyright(c) 2015, 2016 Intel Corporation. |
77241056 MM |
3 | * |
4 | * This file is provided under a dual BSD/GPLv2 license. When using or | |
5 | * redistributing this file, you may do so under either license. | |
6 | * | |
7 | * GPL LICENSE SUMMARY | |
8 | * | |
77241056 MM |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of version 2 of the GNU General Public License as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * BSD LICENSE | |
19 | * | |
77241056 MM |
20 | * Redistribution and use in source and binary forms, with or without |
21 | * modification, are permitted provided that the following conditions | |
22 | * are met: | |
23 | * | |
24 | * - Redistributions of source code must retain the above copyright | |
25 | * notice, this list of conditions and the following disclaimer. | |
26 | * - Redistributions in binary form must reproduce the above copyright | |
27 | * notice, this list of conditions and the following disclaimer in | |
28 | * the documentation and/or other materials provided with the | |
29 | * distribution. | |
30 | * - Neither the name of Intel Corporation nor the names of its | |
31 | * contributors may be used to endorse or promote products derived | |
32 | * from this software without specific prior written permission. | |
33 | * | |
34 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
35 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
36 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
37 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
38 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
39 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
40 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
41 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
42 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
43 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
44 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
45 | * | |
46 | */ | |
47 | #include <linux/delay.h> | |
48 | #include "hfi.h" | |
49 | #include "common.h" | |
50 | #include "eprom.h" | |
51 | ||
e2113752 DL |
52 | /* |
53 | * The EPROM is logically divided into three partitions: | |
54 | * partition 0: the first 128K, visible from PCI ROM BAR | |
55 | * partition 1: 4K config file (sector size) | |
56 | * partition 2: the rest | |
57 | */ | |
58 | #define P0_SIZE (128 * 1024) | |
59 | #define P1_SIZE (4 * 1024) | |
60 | #define P1_START P0_SIZE | |
61 | #define P2_START (P0_SIZE + P1_SIZE) | |
62 | ||
63 | /* controller page size, in bytes */ | |
64 | #define EP_PAGE_SIZE 256 | |
65 | #define EP_PAGE_MASK (EP_PAGE_SIZE - 1) | |
66 | #define EP_PAGE_DWORDS (EP_PAGE_SIZE / sizeof(u32)) | |
67 | ||
68 | /* controller commands */ | |
77241056 | 69 | #define CMD_SHIFT 24 |
e2113752 DL |
70 | #define CMD_NOP (0) |
71 | #define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr) | |
77241056 MM |
72 | #define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT)) |
73 | ||
74 | /* controller interface speeds */ | |
75 | #define EP_SPEED_FULL 0x2 /* full speed */ | |
76 | ||
77241056 | 77 | /* |
60c70828 DL |
78 | * How long to wait for the EPROM to become available, in ms. |
79 | * The spec 32 Mb EPROM takes around 40s to erase then write. | |
80 | * Double it for safety. | |
77241056 | 81 | */ |
60c70828 | 82 | #define EPROM_TIMEOUT 80000 /* ms */ |
e2113752 DL |
83 | |
84 | /* | |
85 | * Read a 256 byte (64 dword) EPROM page. | |
86 | * All callers have verified the offset is at a page boundary. | |
87 | */ | |
88 | static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result) | |
89 | { | |
90 | int i; | |
91 | ||
92 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset)); | |
93 | for (i = 0; i < EP_PAGE_DWORDS; i++) | |
94 | result[i] = (u32)read_csr(dd, ASIC_EEP_DATA); | |
95 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */ | |
96 | } | |
97 | ||
98 | /* | |
99 | * Read length bytes starting at offset from the start of the EPROM. | |
100 | */ | |
101 | static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, void *dest) | |
102 | { | |
103 | u32 buffer[EP_PAGE_DWORDS]; | |
104 | u32 end; | |
105 | u32 start_offset; | |
106 | u32 read_start; | |
107 | u32 bytes; | |
108 | ||
109 | if (len == 0) | |
110 | return 0; | |
111 | ||
112 | end = start + len; | |
113 | ||
114 | /* | |
115 | * Make sure the read range is not outside of the controller read | |
116 | * command address range. Note that '>' is correct below - the end | |
117 | * of the range is OK if it stops at the limit, but no higher. | |
118 | */ | |
119 | if (end > (1 << CMD_SHIFT)) | |
120 | return -EINVAL; | |
121 | ||
122 | /* read the first partial page */ | |
123 | start_offset = start & EP_PAGE_MASK; | |
124 | if (start_offset) { | |
125 | /* partial starting page */ | |
126 | ||
127 | /* align and read the page that contains the start */ | |
128 | read_start = start & ~EP_PAGE_MASK; | |
129 | read_page(dd, read_start, buffer); | |
130 | ||
131 | /* the rest of the page is available data */ | |
132 | bytes = EP_PAGE_SIZE - start_offset; | |
133 | ||
134 | if (len <= bytes) { | |
135 | /* end is within this page */ | |
136 | memcpy(dest, (u8 *)buffer + start_offset, len); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | memcpy(dest, (u8 *)buffer + start_offset, bytes); | |
141 | ||
142 | start += bytes; | |
143 | len -= bytes; | |
144 | dest += bytes; | |
145 | } | |
146 | /* start is now page aligned */ | |
147 | ||
148 | /* read whole pages */ | |
149 | while (len >= EP_PAGE_SIZE) { | |
150 | read_page(dd, start, buffer); | |
151 | memcpy(dest, buffer, EP_PAGE_SIZE); | |
152 | ||
153 | start += EP_PAGE_SIZE; | |
154 | len -= EP_PAGE_SIZE; | |
155 | dest += EP_PAGE_SIZE; | |
156 | } | |
157 | ||
158 | /* read the last partial page */ | |
159 | if (len) { | |
160 | read_page(dd, start, buffer); | |
161 | memcpy(dest, buffer, len); | |
162 | } | |
163 | ||
164 | return 0; | |
165 | } | |
166 | ||
77241056 MM |
167 | /* |
168 | * Initialize the EPROM handler. | |
169 | */ | |
170 | int eprom_init(struct hfi1_devdata *dd) | |
171 | { | |
172 | int ret = 0; | |
173 | ||
60c70828 | 174 | /* only the discrete chip has an EPROM */ |
77241056 MM |
175 | if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0) |
176 | return 0; | |
177 | ||
77241056 | 178 | /* |
60c70828 DL |
179 | * It is OK if both HFIs reset the EPROM as long as they don't |
180 | * do it at the same time. | |
77241056 | 181 | */ |
60c70828 | 182 | ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); |
77241056 MM |
183 | if (ret) { |
184 | dd_dev_err(dd, | |
60c70828 | 185 | "%s: unable to acquire EPROM resource, no EPROM support\n", |
17fb4f29 | 186 | __func__); |
77241056 MM |
187 | goto done_asic; |
188 | } | |
189 | ||
190 | /* reset EPROM to be sure it is in a good state */ | |
191 | ||
192 | /* set reset */ | |
17fb4f29 | 193 | write_csr(dd, ASIC_EEP_CTL_STAT, ASIC_EEP_CTL_STAT_EP_RESET_SMASK); |
77241056 MM |
194 | /* clear reset, set speed */ |
195 | write_csr(dd, ASIC_EEP_CTL_STAT, | |
17fb4f29 | 196 | EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT); |
77241056 MM |
197 | |
198 | /* wake the device with command "release powerdown NoID" */ | |
199 | write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID); | |
200 | ||
e154f127 | 201 | dd->eprom_available = true; |
60c70828 | 202 | release_chip_resource(dd, CR_EPROM); |
77241056 | 203 | done_asic: |
77241056 MM |
204 | return ret; |
205 | } | |
107ffbc5 | 206 | |
753b19af JS |
207 | /* magic character sequence that begins an image */ |
208 | #define IMAGE_START_MAGIC "APO=" | |
209 | ||
210 | /* magic character sequence that might trail an image */ | |
107ffbc5 DL |
211 | #define IMAGE_TRAIL_MAGIC "egamiAPO" |
212 | ||
62aeddbf DL |
213 | /* EPROM file types */ |
214 | #define HFI1_EFT_PLATFORM_CONFIG 2 | |
215 | ||
216 | /* segment size - 128 KiB */ | |
217 | #define SEG_SIZE (128 * 1024) | |
218 | ||
219 | struct hfi1_eprom_footer { | |
220 | u32 oprom_size; /* size of the oprom, in bytes */ | |
221 | u16 num_table_entries; | |
222 | u16 version; /* version of this footer */ | |
223 | u32 magic; /* must be last */ | |
224 | }; | |
225 | ||
226 | struct hfi1_eprom_table_entry { | |
227 | u32 type; /* file type */ | |
228 | u32 offset; /* file offset from start of EPROM */ | |
229 | u32 size; /* file size, in bytes */ | |
230 | }; | |
231 | ||
232 | /* | |
233 | * Calculate the max number of table entries that will fit within a directory | |
234 | * buffer of size 'dir_size'. | |
235 | */ | |
236 | #define MAX_TABLE_ENTRIES(dir_size) \ | |
237 | (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ | |
238 | sizeof(struct hfi1_eprom_table_entry)) | |
239 | ||
240 | #define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ | |
241 | (sizeof(struct hfi1_eprom_table_entry) * (n))) | |
242 | ||
243 | #define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) | |
244 | #define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') | |
245 | #define FOOTER_VERSION 1 | |
246 | ||
107ffbc5 DL |
247 | /* |
248 | * Read all of partition 1. The actual file is at the front. Adjust | |
249 | * the returned size if a trailing image magic is found. | |
250 | */ | |
251 | static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, | |
252 | u32 *size) | |
253 | { | |
254 | void *buffer; | |
255 | void *p; | |
753b19af | 256 | u32 length; |
107ffbc5 DL |
257 | int ret; |
258 | ||
259 | buffer = kmalloc(P1_SIZE, GFP_KERNEL); | |
260 | if (!buffer) | |
261 | return -ENOMEM; | |
262 | ||
263 | ret = read_length(dd, P1_START, P1_SIZE, buffer); | |
264 | if (ret) { | |
265 | kfree(buffer); | |
266 | return ret; | |
267 | } | |
268 | ||
753b19af JS |
269 | /* config partition is valid only if it starts with IMAGE_START_MAGIC */ |
270 | if (memcmp(buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) { | |
bc5214ee JS |
271 | kfree(buffer); |
272 | return -ENOENT; | |
273 | } | |
107ffbc5 | 274 | |
753b19af JS |
275 | /* scan for image magic that may trail the actual data */ |
276 | p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE); | |
277 | if (p) | |
278 | length = p - buffer; | |
279 | else | |
280 | length = P1_SIZE; | |
281 | ||
107ffbc5 | 282 | *data = buffer; |
753b19af | 283 | *size = length; |
107ffbc5 DL |
284 | return 0; |
285 | } | |
286 | ||
62aeddbf DL |
287 | /* |
288 | * The segment magic has been checked. There is a footer and table of | |
289 | * contents present. | |
290 | * | |
291 | * directory is a u32 aligned buffer of size EP_PAGE_SIZE. | |
292 | */ | |
293 | static int read_segment_platform_config(struct hfi1_devdata *dd, | |
294 | void *directory, void **data, u32 *size) | |
295 | { | |
296 | struct hfi1_eprom_footer *footer; | |
297 | struct hfi1_eprom_table_entry *table; | |
298 | struct hfi1_eprom_table_entry *entry; | |
299 | void *buffer = NULL; | |
300 | void *table_buffer = NULL; | |
301 | int ret, i; | |
302 | u32 directory_size; | |
303 | u32 seg_base, seg_offset; | |
304 | u32 bytes_available, ncopied, to_copy; | |
305 | ||
306 | /* the footer is at the end of the directory */ | |
307 | footer = (struct hfi1_eprom_footer *) | |
308 | (directory + EP_PAGE_SIZE - sizeof(*footer)); | |
309 | ||
310 | /* make sure the structure version is supported */ | |
311 | if (footer->version != FOOTER_VERSION) | |
312 | return -EINVAL; | |
313 | ||
314 | /* oprom size cannot be larger than a segment */ | |
315 | if (footer->oprom_size >= SEG_SIZE) | |
316 | return -EINVAL; | |
317 | ||
318 | /* the file table must fit in a segment with the oprom */ | |
319 | if (footer->num_table_entries > | |
320 | MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) | |
321 | return -EINVAL; | |
322 | ||
323 | /* find the file table start, which precedes the footer */ | |
324 | directory_size = DIRECTORY_SIZE(footer->num_table_entries); | |
325 | if (directory_size <= EP_PAGE_SIZE) { | |
326 | /* the file table fits into the directory buffer handed in */ | |
327 | table = (struct hfi1_eprom_table_entry *) | |
328 | (directory + EP_PAGE_SIZE - directory_size); | |
329 | } else { | |
330 | /* need to allocate and read more */ | |
331 | table_buffer = kmalloc(directory_size, GFP_KERNEL); | |
332 | if (!table_buffer) | |
333 | return -ENOMEM; | |
334 | ret = read_length(dd, SEG_SIZE - directory_size, | |
335 | directory_size, table_buffer); | |
336 | if (ret) | |
337 | goto done; | |
338 | table = table_buffer; | |
339 | } | |
340 | ||
341 | /* look for the platform configuration file in the table */ | |
342 | for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { | |
343 | if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { | |
344 | entry = &table[i]; | |
345 | break; | |
346 | } | |
347 | } | |
348 | if (!entry) { | |
349 | ret = -ENOENT; | |
350 | goto done; | |
351 | } | |
352 | ||
353 | /* | |
354 | * Sanity check on the configuration file size - it should never | |
355 | * be larger than 4 KiB. | |
356 | */ | |
357 | if (entry->size > (4 * 1024)) { | |
358 | dd_dev_err(dd, "Bad configuration file size 0x%x\n", | |
359 | entry->size); | |
360 | ret = -EINVAL; | |
361 | goto done; | |
362 | } | |
363 | ||
364 | /* check for bogus offset and size that wrap when added together */ | |
365 | if (entry->offset + entry->size < entry->offset) { | |
366 | dd_dev_err(dd, | |
367 | "Bad configuration file start + size 0x%x+0x%x\n", | |
368 | entry->offset, entry->size); | |
369 | ret = -EINVAL; | |
370 | goto done; | |
371 | } | |
372 | ||
373 | /* allocate the buffer to return */ | |
374 | buffer = kmalloc(entry->size, GFP_KERNEL); | |
375 | if (!buffer) { | |
376 | ret = -ENOMEM; | |
377 | goto done; | |
378 | } | |
379 | ||
380 | /* | |
381 | * Extract the file by looping over segments until it is fully read. | |
382 | */ | |
383 | seg_offset = entry->offset % SEG_SIZE; | |
384 | seg_base = entry->offset - seg_offset; | |
385 | ncopied = 0; | |
386 | while (ncopied < entry->size) { | |
387 | /* calculate data bytes available in this segment */ | |
388 | ||
389 | /* start with the bytes from the current offset to the end */ | |
390 | bytes_available = SEG_SIZE - seg_offset; | |
391 | /* subtract off footer and table from segment 0 */ | |
392 | if (seg_base == 0) { | |
393 | /* | |
394 | * Sanity check: should not have a starting point | |
395 | * at or within the directory. | |
396 | */ | |
397 | if (bytes_available <= directory_size) { | |
398 | dd_dev_err(dd, | |
399 | "Bad configuration file - offset 0x%x within footer+table\n", | |
400 | entry->offset); | |
401 | ret = -EINVAL; | |
402 | goto done; | |
403 | } | |
404 | bytes_available -= directory_size; | |
405 | } | |
406 | ||
407 | /* calculate bytes wanted */ | |
408 | to_copy = entry->size - ncopied; | |
409 | ||
410 | /* max out at the available bytes in this segment */ | |
411 | if (to_copy > bytes_available) | |
412 | to_copy = bytes_available; | |
413 | ||
414 | /* | |
415 | * Read from the EPROM. | |
416 | * | |
417 | * The sanity check for entry->offset is done in read_length(). | |
418 | * The EPROM offset is validated against what the hardware | |
419 | * addressing supports. In addition, if the offset is larger | |
420 | * than the actual EPROM, it silently wraps. It will work | |
421 | * fine, though the reader may not get what they expected | |
422 | * from the EPROM. | |
423 | */ | |
424 | ret = read_length(dd, seg_base + seg_offset, to_copy, | |
425 | buffer + ncopied); | |
426 | if (ret) | |
427 | goto done; | |
428 | ||
429 | ncopied += to_copy; | |
430 | ||
431 | /* set up for next segment */ | |
432 | seg_offset = footer->oprom_size; | |
433 | seg_base += SEG_SIZE; | |
434 | } | |
435 | ||
436 | /* success */ | |
437 | ret = 0; | |
438 | *data = buffer; | |
439 | *size = entry->size; | |
440 | ||
441 | done: | |
442 | kfree(table_buffer); | |
443 | if (ret) | |
444 | kfree(buffer); | |
445 | return ret; | |
446 | } | |
447 | ||
107ffbc5 DL |
448 | /* |
449 | * Read the platform configuration file from the EPROM. | |
450 | * | |
451 | * On success, an allocated buffer containing the data and its size are | |
452 | * returned. It is up to the caller to free this buffer. | |
453 | * | |
454 | * Return value: | |
455 | * 0 - success | |
456 | * -ENXIO - no EPROM is available | |
457 | * -EBUSY - not able to acquire access to the EPROM | |
458 | * -ENOENT - no recognizable file written | |
459 | * -ENOMEM - buffer could not be allocated | |
62aeddbf | 460 | * -EINVAL - invalid EPROM contentents found |
107ffbc5 DL |
461 | */ |
462 | int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) | |
463 | { | |
464 | u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */ | |
465 | int ret; | |
466 | ||
467 | if (!dd->eprom_available) | |
468 | return -ENXIO; | |
469 | ||
470 | ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); | |
471 | if (ret) | |
472 | return -EBUSY; | |
473 | ||
62aeddbf DL |
474 | /* read the last page of the segment for the EPROM format magic */ |
475 | ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); | |
107ffbc5 DL |
476 | if (ret) |
477 | goto done; | |
478 | ||
62aeddbf DL |
479 | /* last dword of the segment contains a magic value */ |
480 | if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { | |
481 | /* segment format */ | |
482 | ret = read_segment_platform_config(dd, directory, data, size); | |
483 | } else { | |
107ffbc5 DL |
484 | /* partition format */ |
485 | ret = read_partition_platform_config(dd, data, size); | |
107ffbc5 DL |
486 | } |
487 | ||
107ffbc5 DL |
488 | done: |
489 | release_chip_resource(dd, CR_EPROM); | |
490 | return ret; | |
491 | } |