]>
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 DL |
206 | |
207 | /* magic character sequence that trails an image */ | |
208 | #define IMAGE_TRAIL_MAGIC "egamiAPO" | |
209 | ||
62aeddbf DL |
210 | /* EPROM file types */ |
211 | #define HFI1_EFT_PLATFORM_CONFIG 2 | |
212 | ||
213 | /* segment size - 128 KiB */ | |
214 | #define SEG_SIZE (128 * 1024) | |
215 | ||
216 | struct hfi1_eprom_footer { | |
217 | u32 oprom_size; /* size of the oprom, in bytes */ | |
218 | u16 num_table_entries; | |
219 | u16 version; /* version of this footer */ | |
220 | u32 magic; /* must be last */ | |
221 | }; | |
222 | ||
223 | struct hfi1_eprom_table_entry { | |
224 | u32 type; /* file type */ | |
225 | u32 offset; /* file offset from start of EPROM */ | |
226 | u32 size; /* file size, in bytes */ | |
227 | }; | |
228 | ||
229 | /* | |
230 | * Calculate the max number of table entries that will fit within a directory | |
231 | * buffer of size 'dir_size'. | |
232 | */ | |
233 | #define MAX_TABLE_ENTRIES(dir_size) \ | |
234 | (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ | |
235 | sizeof(struct hfi1_eprom_table_entry)) | |
236 | ||
237 | #define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ | |
238 | (sizeof(struct hfi1_eprom_table_entry) * (n))) | |
239 | ||
240 | #define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) | |
241 | #define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') | |
242 | #define FOOTER_VERSION 1 | |
243 | ||
107ffbc5 DL |
244 | /* |
245 | * Read all of partition 1. The actual file is at the front. Adjust | |
246 | * the returned size if a trailing image magic is found. | |
247 | */ | |
248 | static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, | |
249 | u32 *size) | |
250 | { | |
251 | void *buffer; | |
252 | void *p; | |
107ffbc5 DL |
253 | int ret; |
254 | ||
255 | buffer = kmalloc(P1_SIZE, GFP_KERNEL); | |
256 | if (!buffer) | |
257 | return -ENOMEM; | |
258 | ||
259 | ret = read_length(dd, P1_START, P1_SIZE, buffer); | |
260 | if (ret) { | |
261 | kfree(buffer); | |
262 | return ret; | |
263 | } | |
264 | ||
265 | /* scan for image magic that may trail the actual data */ | |
266 | p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE); | |
bc5214ee JS |
267 | if (!p) { |
268 | kfree(buffer); | |
269 | return -ENOENT; | |
270 | } | |
107ffbc5 DL |
271 | |
272 | *data = buffer; | |
bc5214ee | 273 | *size = p - buffer; |
107ffbc5 DL |
274 | return 0; |
275 | } | |
276 | ||
62aeddbf DL |
277 | /* |
278 | * The segment magic has been checked. There is a footer and table of | |
279 | * contents present. | |
280 | * | |
281 | * directory is a u32 aligned buffer of size EP_PAGE_SIZE. | |
282 | */ | |
283 | static int read_segment_platform_config(struct hfi1_devdata *dd, | |
284 | void *directory, void **data, u32 *size) | |
285 | { | |
286 | struct hfi1_eprom_footer *footer; | |
287 | struct hfi1_eprom_table_entry *table; | |
288 | struct hfi1_eprom_table_entry *entry; | |
289 | void *buffer = NULL; | |
290 | void *table_buffer = NULL; | |
291 | int ret, i; | |
292 | u32 directory_size; | |
293 | u32 seg_base, seg_offset; | |
294 | u32 bytes_available, ncopied, to_copy; | |
295 | ||
296 | /* the footer is at the end of the directory */ | |
297 | footer = (struct hfi1_eprom_footer *) | |
298 | (directory + EP_PAGE_SIZE - sizeof(*footer)); | |
299 | ||
300 | /* make sure the structure version is supported */ | |
301 | if (footer->version != FOOTER_VERSION) | |
302 | return -EINVAL; | |
303 | ||
304 | /* oprom size cannot be larger than a segment */ | |
305 | if (footer->oprom_size >= SEG_SIZE) | |
306 | return -EINVAL; | |
307 | ||
308 | /* the file table must fit in a segment with the oprom */ | |
309 | if (footer->num_table_entries > | |
310 | MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) | |
311 | return -EINVAL; | |
312 | ||
313 | /* find the file table start, which precedes the footer */ | |
314 | directory_size = DIRECTORY_SIZE(footer->num_table_entries); | |
315 | if (directory_size <= EP_PAGE_SIZE) { | |
316 | /* the file table fits into the directory buffer handed in */ | |
317 | table = (struct hfi1_eprom_table_entry *) | |
318 | (directory + EP_PAGE_SIZE - directory_size); | |
319 | } else { | |
320 | /* need to allocate and read more */ | |
321 | table_buffer = kmalloc(directory_size, GFP_KERNEL); | |
322 | if (!table_buffer) | |
323 | return -ENOMEM; | |
324 | ret = read_length(dd, SEG_SIZE - directory_size, | |
325 | directory_size, table_buffer); | |
326 | if (ret) | |
327 | goto done; | |
328 | table = table_buffer; | |
329 | } | |
330 | ||
331 | /* look for the platform configuration file in the table */ | |
332 | for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { | |
333 | if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { | |
334 | entry = &table[i]; | |
335 | break; | |
336 | } | |
337 | } | |
338 | if (!entry) { | |
339 | ret = -ENOENT; | |
340 | goto done; | |
341 | } | |
342 | ||
343 | /* | |
344 | * Sanity check on the configuration file size - it should never | |
345 | * be larger than 4 KiB. | |
346 | */ | |
347 | if (entry->size > (4 * 1024)) { | |
348 | dd_dev_err(dd, "Bad configuration file size 0x%x\n", | |
349 | entry->size); | |
350 | ret = -EINVAL; | |
351 | goto done; | |
352 | } | |
353 | ||
354 | /* check for bogus offset and size that wrap when added together */ | |
355 | if (entry->offset + entry->size < entry->offset) { | |
356 | dd_dev_err(dd, | |
357 | "Bad configuration file start + size 0x%x+0x%x\n", | |
358 | entry->offset, entry->size); | |
359 | ret = -EINVAL; | |
360 | goto done; | |
361 | } | |
362 | ||
363 | /* allocate the buffer to return */ | |
364 | buffer = kmalloc(entry->size, GFP_KERNEL); | |
365 | if (!buffer) { | |
366 | ret = -ENOMEM; | |
367 | goto done; | |
368 | } | |
369 | ||
370 | /* | |
371 | * Extract the file by looping over segments until it is fully read. | |
372 | */ | |
373 | seg_offset = entry->offset % SEG_SIZE; | |
374 | seg_base = entry->offset - seg_offset; | |
375 | ncopied = 0; | |
376 | while (ncopied < entry->size) { | |
377 | /* calculate data bytes available in this segment */ | |
378 | ||
379 | /* start with the bytes from the current offset to the end */ | |
380 | bytes_available = SEG_SIZE - seg_offset; | |
381 | /* subtract off footer and table from segment 0 */ | |
382 | if (seg_base == 0) { | |
383 | /* | |
384 | * Sanity check: should not have a starting point | |
385 | * at or within the directory. | |
386 | */ | |
387 | if (bytes_available <= directory_size) { | |
388 | dd_dev_err(dd, | |
389 | "Bad configuration file - offset 0x%x within footer+table\n", | |
390 | entry->offset); | |
391 | ret = -EINVAL; | |
392 | goto done; | |
393 | } | |
394 | bytes_available -= directory_size; | |
395 | } | |
396 | ||
397 | /* calculate bytes wanted */ | |
398 | to_copy = entry->size - ncopied; | |
399 | ||
400 | /* max out at the available bytes in this segment */ | |
401 | if (to_copy > bytes_available) | |
402 | to_copy = bytes_available; | |
403 | ||
404 | /* | |
405 | * Read from the EPROM. | |
406 | * | |
407 | * The sanity check for entry->offset is done in read_length(). | |
408 | * The EPROM offset is validated against what the hardware | |
409 | * addressing supports. In addition, if the offset is larger | |
410 | * than the actual EPROM, it silently wraps. It will work | |
411 | * fine, though the reader may not get what they expected | |
412 | * from the EPROM. | |
413 | */ | |
414 | ret = read_length(dd, seg_base + seg_offset, to_copy, | |
415 | buffer + ncopied); | |
416 | if (ret) | |
417 | goto done; | |
418 | ||
419 | ncopied += to_copy; | |
420 | ||
421 | /* set up for next segment */ | |
422 | seg_offset = footer->oprom_size; | |
423 | seg_base += SEG_SIZE; | |
424 | } | |
425 | ||
426 | /* success */ | |
427 | ret = 0; | |
428 | *data = buffer; | |
429 | *size = entry->size; | |
430 | ||
431 | done: | |
432 | kfree(table_buffer); | |
433 | if (ret) | |
434 | kfree(buffer); | |
435 | return ret; | |
436 | } | |
437 | ||
107ffbc5 DL |
438 | /* |
439 | * Read the platform configuration file from the EPROM. | |
440 | * | |
441 | * On success, an allocated buffer containing the data and its size are | |
442 | * returned. It is up to the caller to free this buffer. | |
443 | * | |
444 | * Return value: | |
445 | * 0 - success | |
446 | * -ENXIO - no EPROM is available | |
447 | * -EBUSY - not able to acquire access to the EPROM | |
448 | * -ENOENT - no recognizable file written | |
449 | * -ENOMEM - buffer could not be allocated | |
62aeddbf | 450 | * -EINVAL - invalid EPROM contentents found |
107ffbc5 DL |
451 | */ |
452 | int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) | |
453 | { | |
454 | u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */ | |
455 | int ret; | |
456 | ||
457 | if (!dd->eprom_available) | |
458 | return -ENXIO; | |
459 | ||
460 | ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); | |
461 | if (ret) | |
462 | return -EBUSY; | |
463 | ||
62aeddbf DL |
464 | /* read the last page of the segment for the EPROM format magic */ |
465 | ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); | |
107ffbc5 DL |
466 | if (ret) |
467 | goto done; | |
468 | ||
62aeddbf DL |
469 | /* last dword of the segment contains a magic value */ |
470 | if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { | |
471 | /* segment format */ | |
472 | ret = read_segment_platform_config(dd, directory, data, size); | |
473 | } else { | |
107ffbc5 DL |
474 | /* partition format */ |
475 | ret = read_partition_platform_config(dd, data, size); | |
107ffbc5 DL |
476 | } |
477 | ||
107ffbc5 DL |
478 | done: |
479 | release_chip_resource(dd, CR_EPROM); | |
480 | return ret; | |
481 | } |