1f48ad614SDennis Dalessandro /* 2f48ad614SDennis Dalessandro * Copyright(c) 2015, 2016 Intel Corporation. 3f48ad614SDennis Dalessandro * 4f48ad614SDennis Dalessandro * This file is provided under a dual BSD/GPLv2 license. When using or 5f48ad614SDennis Dalessandro * redistributing this file, you may do so under either license. 6f48ad614SDennis Dalessandro * 7f48ad614SDennis Dalessandro * GPL LICENSE SUMMARY 8f48ad614SDennis Dalessandro * 9f48ad614SDennis Dalessandro * This program is free software; you can redistribute it and/or modify 10f48ad614SDennis Dalessandro * it under the terms of version 2 of the GNU General Public License as 11f48ad614SDennis Dalessandro * published by the Free Software Foundation. 12f48ad614SDennis Dalessandro * 13f48ad614SDennis Dalessandro * This program is distributed in the hope that it will be useful, but 14f48ad614SDennis Dalessandro * WITHOUT ANY WARRANTY; without even the implied warranty of 15f48ad614SDennis Dalessandro * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16f48ad614SDennis Dalessandro * General Public License for more details. 17f48ad614SDennis Dalessandro * 18f48ad614SDennis Dalessandro * BSD LICENSE 19f48ad614SDennis Dalessandro * 20f48ad614SDennis Dalessandro * Redistribution and use in source and binary forms, with or without 21f48ad614SDennis Dalessandro * modification, are permitted provided that the following conditions 22f48ad614SDennis Dalessandro * are met: 23f48ad614SDennis Dalessandro * 24f48ad614SDennis Dalessandro * - Redistributions of source code must retain the above copyright 25f48ad614SDennis Dalessandro * notice, this list of conditions and the following disclaimer. 26f48ad614SDennis Dalessandro * - Redistributions in binary form must reproduce the above copyright 27f48ad614SDennis Dalessandro * notice, this list of conditions and the following disclaimer in 28f48ad614SDennis Dalessandro * the documentation and/or other materials provided with the 29f48ad614SDennis Dalessandro * distribution. 30f48ad614SDennis Dalessandro * - Neither the name of Intel Corporation nor the names of its 31f48ad614SDennis Dalessandro * contributors may be used to endorse or promote products derived 32f48ad614SDennis Dalessandro * from this software without specific prior written permission. 33f48ad614SDennis Dalessandro * 34f48ad614SDennis Dalessandro * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 35f48ad614SDennis Dalessandro * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 36f48ad614SDennis Dalessandro * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 37f48ad614SDennis Dalessandro * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 38f48ad614SDennis Dalessandro * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 39f48ad614SDennis Dalessandro * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 40f48ad614SDennis Dalessandro * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 41f48ad614SDennis Dalessandro * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 42f48ad614SDennis Dalessandro * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 43f48ad614SDennis Dalessandro * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 44f48ad614SDennis Dalessandro * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 45f48ad614SDennis Dalessandro * 46f48ad614SDennis Dalessandro */ 47f48ad614SDennis Dalessandro #include <linux/delay.h> 48f48ad614SDennis Dalessandro #include "hfi.h" 49f48ad614SDennis Dalessandro #include "common.h" 50f48ad614SDennis Dalessandro #include "eprom.h" 51f48ad614SDennis Dalessandro 52e2113752SDean Luick /* 53e2113752SDean Luick * The EPROM is logically divided into three partitions: 54e2113752SDean Luick * partition 0: the first 128K, visible from PCI ROM BAR 55e2113752SDean Luick * partition 1: 4K config file (sector size) 56e2113752SDean Luick * partition 2: the rest 57e2113752SDean Luick */ 58e2113752SDean Luick #define P0_SIZE (128 * 1024) 59e2113752SDean Luick #define P1_SIZE (4 * 1024) 60e2113752SDean Luick #define P1_START P0_SIZE 61e2113752SDean Luick #define P2_START (P0_SIZE + P1_SIZE) 62e2113752SDean Luick 63e2113752SDean Luick /* controller page size, in bytes */ 64e2113752SDean Luick #define EP_PAGE_SIZE 256 65e2113752SDean Luick #define EP_PAGE_MASK (EP_PAGE_SIZE - 1) 66e2113752SDean Luick #define EP_PAGE_DWORDS (EP_PAGE_SIZE / sizeof(u32)) 67e2113752SDean Luick 68e2113752SDean Luick /* controller commands */ 69f48ad614SDennis Dalessandro #define CMD_SHIFT 24 70e2113752SDean Luick #define CMD_NOP (0) 71e2113752SDean Luick #define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr) 72f48ad614SDennis Dalessandro #define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT)) 73f48ad614SDennis Dalessandro 74f48ad614SDennis Dalessandro /* controller interface speeds */ 75f48ad614SDennis Dalessandro #define EP_SPEED_FULL 0x2 /* full speed */ 76f48ad614SDennis Dalessandro 77f48ad614SDennis Dalessandro /* 78f48ad614SDennis Dalessandro * How long to wait for the EPROM to become available, in ms. 79f48ad614SDennis Dalessandro * The spec 32 Mb EPROM takes around 40s to erase then write. 80f48ad614SDennis Dalessandro * Double it for safety. 81f48ad614SDennis Dalessandro */ 82f48ad614SDennis Dalessandro #define EPROM_TIMEOUT 80000 /* ms */ 83e2113752SDean Luick 84e2113752SDean Luick /* 85e2113752SDean Luick * Read a 256 byte (64 dword) EPROM page. 86e2113752SDean Luick * All callers have verified the offset is at a page boundary. 87e2113752SDean Luick */ 88e2113752SDean Luick static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result) 89e2113752SDean Luick { 90e2113752SDean Luick int i; 91e2113752SDean Luick 92e2113752SDean Luick write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset)); 93e2113752SDean Luick for (i = 0; i < EP_PAGE_DWORDS; i++) 94e2113752SDean Luick result[i] = (u32)read_csr(dd, ASIC_EEP_DATA); 95e2113752SDean Luick write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */ 96e2113752SDean Luick } 97e2113752SDean Luick 98e2113752SDean Luick /* 99e2113752SDean Luick * Read length bytes starting at offset from the start of the EPROM. 100e2113752SDean Luick */ 101e2113752SDean Luick static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, void *dest) 102e2113752SDean Luick { 103e2113752SDean Luick u32 buffer[EP_PAGE_DWORDS]; 104e2113752SDean Luick u32 end; 105e2113752SDean Luick u32 start_offset; 106e2113752SDean Luick u32 read_start; 107e2113752SDean Luick u32 bytes; 108e2113752SDean Luick 109e2113752SDean Luick if (len == 0) 110e2113752SDean Luick return 0; 111e2113752SDean Luick 112e2113752SDean Luick end = start + len; 113e2113752SDean Luick 114e2113752SDean Luick /* 115e2113752SDean Luick * Make sure the read range is not outside of the controller read 116e2113752SDean Luick * command address range. Note that '>' is correct below - the end 117e2113752SDean Luick * of the range is OK if it stops at the limit, but no higher. 118e2113752SDean Luick */ 119e2113752SDean Luick if (end > (1 << CMD_SHIFT)) 120e2113752SDean Luick return -EINVAL; 121e2113752SDean Luick 122e2113752SDean Luick /* read the first partial page */ 123e2113752SDean Luick start_offset = start & EP_PAGE_MASK; 124e2113752SDean Luick if (start_offset) { 125e2113752SDean Luick /* partial starting page */ 126e2113752SDean Luick 127e2113752SDean Luick /* align and read the page that contains the start */ 128e2113752SDean Luick read_start = start & ~EP_PAGE_MASK; 129e2113752SDean Luick read_page(dd, read_start, buffer); 130e2113752SDean Luick 131e2113752SDean Luick /* the rest of the page is available data */ 132e2113752SDean Luick bytes = EP_PAGE_SIZE - start_offset; 133e2113752SDean Luick 134e2113752SDean Luick if (len <= bytes) { 135e2113752SDean Luick /* end is within this page */ 136e2113752SDean Luick memcpy(dest, (u8 *)buffer + start_offset, len); 137e2113752SDean Luick return 0; 138e2113752SDean Luick } 139e2113752SDean Luick 140e2113752SDean Luick memcpy(dest, (u8 *)buffer + start_offset, bytes); 141e2113752SDean Luick 142e2113752SDean Luick start += bytes; 143e2113752SDean Luick len -= bytes; 144e2113752SDean Luick dest += bytes; 145e2113752SDean Luick } 146e2113752SDean Luick /* start is now page aligned */ 147e2113752SDean Luick 148e2113752SDean Luick /* read whole pages */ 149e2113752SDean Luick while (len >= EP_PAGE_SIZE) { 150e2113752SDean Luick read_page(dd, start, buffer); 151e2113752SDean Luick memcpy(dest, buffer, EP_PAGE_SIZE); 152e2113752SDean Luick 153e2113752SDean Luick start += EP_PAGE_SIZE; 154e2113752SDean Luick len -= EP_PAGE_SIZE; 155e2113752SDean Luick dest += EP_PAGE_SIZE; 156e2113752SDean Luick } 157e2113752SDean Luick 158e2113752SDean Luick /* read the last partial page */ 159e2113752SDean Luick if (len) { 160e2113752SDean Luick read_page(dd, start, buffer); 161e2113752SDean Luick memcpy(dest, buffer, len); 162e2113752SDean Luick } 163e2113752SDean Luick 164e2113752SDean Luick return 0; 165e2113752SDean Luick } 166e2113752SDean Luick 167f48ad614SDennis Dalessandro /* 168f48ad614SDennis Dalessandro * Initialize the EPROM handler. 169f48ad614SDennis Dalessandro */ 170f48ad614SDennis Dalessandro int eprom_init(struct hfi1_devdata *dd) 171f48ad614SDennis Dalessandro { 172f48ad614SDennis Dalessandro int ret = 0; 173f48ad614SDennis Dalessandro 174f48ad614SDennis Dalessandro /* only the discrete chip has an EPROM */ 175f48ad614SDennis Dalessandro if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0) 176f48ad614SDennis Dalessandro return 0; 177f48ad614SDennis Dalessandro 178f48ad614SDennis Dalessandro /* 179f48ad614SDennis Dalessandro * It is OK if both HFIs reset the EPROM as long as they don't 180f48ad614SDennis Dalessandro * do it at the same time. 181f48ad614SDennis Dalessandro */ 182f48ad614SDennis Dalessandro ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 183f48ad614SDennis Dalessandro if (ret) { 184f48ad614SDennis Dalessandro dd_dev_err(dd, 185f48ad614SDennis Dalessandro "%s: unable to acquire EPROM resource, no EPROM support\n", 186f48ad614SDennis Dalessandro __func__); 187f48ad614SDennis Dalessandro goto done_asic; 188f48ad614SDennis Dalessandro } 189f48ad614SDennis Dalessandro 190f48ad614SDennis Dalessandro /* reset EPROM to be sure it is in a good state */ 191f48ad614SDennis Dalessandro 192f48ad614SDennis Dalessandro /* set reset */ 193f48ad614SDennis Dalessandro write_csr(dd, ASIC_EEP_CTL_STAT, ASIC_EEP_CTL_STAT_EP_RESET_SMASK); 194f48ad614SDennis Dalessandro /* clear reset, set speed */ 195f48ad614SDennis Dalessandro write_csr(dd, ASIC_EEP_CTL_STAT, 196f48ad614SDennis Dalessandro EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT); 197f48ad614SDennis Dalessandro 198f48ad614SDennis Dalessandro /* wake the device with command "release powerdown NoID" */ 199f48ad614SDennis Dalessandro write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID); 200f48ad614SDennis Dalessandro 201f48ad614SDennis Dalessandro dd->eprom_available = true; 202f48ad614SDennis Dalessandro release_chip_resource(dd, CR_EPROM); 203f48ad614SDennis Dalessandro done_asic: 204f48ad614SDennis Dalessandro return ret; 205f48ad614SDennis Dalessandro } 206107ffbc5SDean Luick 207*753b19afSJan Sokolowski /* magic character sequence that begins an image */ 208*753b19afSJan Sokolowski #define IMAGE_START_MAGIC "APO=" 209*753b19afSJan Sokolowski 210*753b19afSJan Sokolowski /* magic character sequence that might trail an image */ 211107ffbc5SDean Luick #define IMAGE_TRAIL_MAGIC "egamiAPO" 212107ffbc5SDean Luick 21362aeddbfSDean Luick /* EPROM file types */ 21462aeddbfSDean Luick #define HFI1_EFT_PLATFORM_CONFIG 2 21562aeddbfSDean Luick 21662aeddbfSDean Luick /* segment size - 128 KiB */ 21762aeddbfSDean Luick #define SEG_SIZE (128 * 1024) 21862aeddbfSDean Luick 21962aeddbfSDean Luick struct hfi1_eprom_footer { 22062aeddbfSDean Luick u32 oprom_size; /* size of the oprom, in bytes */ 22162aeddbfSDean Luick u16 num_table_entries; 22262aeddbfSDean Luick u16 version; /* version of this footer */ 22362aeddbfSDean Luick u32 magic; /* must be last */ 22462aeddbfSDean Luick }; 22562aeddbfSDean Luick 22662aeddbfSDean Luick struct hfi1_eprom_table_entry { 22762aeddbfSDean Luick u32 type; /* file type */ 22862aeddbfSDean Luick u32 offset; /* file offset from start of EPROM */ 22962aeddbfSDean Luick u32 size; /* file size, in bytes */ 23062aeddbfSDean Luick }; 23162aeddbfSDean Luick 23262aeddbfSDean Luick /* 23362aeddbfSDean Luick * Calculate the max number of table entries that will fit within a directory 23462aeddbfSDean Luick * buffer of size 'dir_size'. 23562aeddbfSDean Luick */ 23662aeddbfSDean Luick #define MAX_TABLE_ENTRIES(dir_size) \ 23762aeddbfSDean Luick (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ 23862aeddbfSDean Luick sizeof(struct hfi1_eprom_table_entry)) 23962aeddbfSDean Luick 24062aeddbfSDean Luick #define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ 24162aeddbfSDean Luick (sizeof(struct hfi1_eprom_table_entry) * (n))) 24262aeddbfSDean Luick 24362aeddbfSDean Luick #define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) 24462aeddbfSDean Luick #define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') 24562aeddbfSDean Luick #define FOOTER_VERSION 1 24662aeddbfSDean Luick 247107ffbc5SDean Luick /* 248107ffbc5SDean Luick * Read all of partition 1. The actual file is at the front. Adjust 249107ffbc5SDean Luick * the returned size if a trailing image magic is found. 250107ffbc5SDean Luick */ 251107ffbc5SDean Luick static int read_partition_platform_config(struct hfi1_devdata *dd, void **data, 252107ffbc5SDean Luick u32 *size) 253107ffbc5SDean Luick { 254107ffbc5SDean Luick void *buffer; 255107ffbc5SDean Luick void *p; 256*753b19afSJan Sokolowski u32 length; 257107ffbc5SDean Luick int ret; 258107ffbc5SDean Luick 259107ffbc5SDean Luick buffer = kmalloc(P1_SIZE, GFP_KERNEL); 260107ffbc5SDean Luick if (!buffer) 261107ffbc5SDean Luick return -ENOMEM; 262107ffbc5SDean Luick 263107ffbc5SDean Luick ret = read_length(dd, P1_START, P1_SIZE, buffer); 264107ffbc5SDean Luick if (ret) { 265107ffbc5SDean Luick kfree(buffer); 266107ffbc5SDean Luick return ret; 267107ffbc5SDean Luick } 268107ffbc5SDean Luick 269*753b19afSJan Sokolowski /* config partition is valid only if it starts with IMAGE_START_MAGIC */ 270*753b19afSJan Sokolowski if (memcmp(buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) { 271bc5214eeSJan Sokolowski kfree(buffer); 272bc5214eeSJan Sokolowski return -ENOENT; 273bc5214eeSJan Sokolowski } 274107ffbc5SDean Luick 275*753b19afSJan Sokolowski /* scan for image magic that may trail the actual data */ 276*753b19afSJan Sokolowski p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE); 277*753b19afSJan Sokolowski if (p) 278*753b19afSJan Sokolowski length = p - buffer; 279*753b19afSJan Sokolowski else 280*753b19afSJan Sokolowski length = P1_SIZE; 281*753b19afSJan Sokolowski 282107ffbc5SDean Luick *data = buffer; 283*753b19afSJan Sokolowski *size = length; 284107ffbc5SDean Luick return 0; 285107ffbc5SDean Luick } 286107ffbc5SDean Luick 287107ffbc5SDean Luick /* 28862aeddbfSDean Luick * The segment magic has been checked. There is a footer and table of 28962aeddbfSDean Luick * contents present. 29062aeddbfSDean Luick * 29162aeddbfSDean Luick * directory is a u32 aligned buffer of size EP_PAGE_SIZE. 29262aeddbfSDean Luick */ 29362aeddbfSDean Luick static int read_segment_platform_config(struct hfi1_devdata *dd, 29462aeddbfSDean Luick void *directory, void **data, u32 *size) 29562aeddbfSDean Luick { 29662aeddbfSDean Luick struct hfi1_eprom_footer *footer; 29762aeddbfSDean Luick struct hfi1_eprom_table_entry *table; 29862aeddbfSDean Luick struct hfi1_eprom_table_entry *entry; 29962aeddbfSDean Luick void *buffer = NULL; 30062aeddbfSDean Luick void *table_buffer = NULL; 30162aeddbfSDean Luick int ret, i; 30262aeddbfSDean Luick u32 directory_size; 30362aeddbfSDean Luick u32 seg_base, seg_offset; 30462aeddbfSDean Luick u32 bytes_available, ncopied, to_copy; 30562aeddbfSDean Luick 30662aeddbfSDean Luick /* the footer is at the end of the directory */ 30762aeddbfSDean Luick footer = (struct hfi1_eprom_footer *) 30862aeddbfSDean Luick (directory + EP_PAGE_SIZE - sizeof(*footer)); 30962aeddbfSDean Luick 31062aeddbfSDean Luick /* make sure the structure version is supported */ 31162aeddbfSDean Luick if (footer->version != FOOTER_VERSION) 31262aeddbfSDean Luick return -EINVAL; 31362aeddbfSDean Luick 31462aeddbfSDean Luick /* oprom size cannot be larger than a segment */ 31562aeddbfSDean Luick if (footer->oprom_size >= SEG_SIZE) 31662aeddbfSDean Luick return -EINVAL; 31762aeddbfSDean Luick 31862aeddbfSDean Luick /* the file table must fit in a segment with the oprom */ 31962aeddbfSDean Luick if (footer->num_table_entries > 32062aeddbfSDean Luick MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) 32162aeddbfSDean Luick return -EINVAL; 32262aeddbfSDean Luick 32362aeddbfSDean Luick /* find the file table start, which precedes the footer */ 32462aeddbfSDean Luick directory_size = DIRECTORY_SIZE(footer->num_table_entries); 32562aeddbfSDean Luick if (directory_size <= EP_PAGE_SIZE) { 32662aeddbfSDean Luick /* the file table fits into the directory buffer handed in */ 32762aeddbfSDean Luick table = (struct hfi1_eprom_table_entry *) 32862aeddbfSDean Luick (directory + EP_PAGE_SIZE - directory_size); 32962aeddbfSDean Luick } else { 33062aeddbfSDean Luick /* need to allocate and read more */ 33162aeddbfSDean Luick table_buffer = kmalloc(directory_size, GFP_KERNEL); 33262aeddbfSDean Luick if (!table_buffer) 33362aeddbfSDean Luick return -ENOMEM; 33462aeddbfSDean Luick ret = read_length(dd, SEG_SIZE - directory_size, 33562aeddbfSDean Luick directory_size, table_buffer); 33662aeddbfSDean Luick if (ret) 33762aeddbfSDean Luick goto done; 33862aeddbfSDean Luick table = table_buffer; 33962aeddbfSDean Luick } 34062aeddbfSDean Luick 34162aeddbfSDean Luick /* look for the platform configuration file in the table */ 34262aeddbfSDean Luick for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { 34362aeddbfSDean Luick if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { 34462aeddbfSDean Luick entry = &table[i]; 34562aeddbfSDean Luick break; 34662aeddbfSDean Luick } 34762aeddbfSDean Luick } 34862aeddbfSDean Luick if (!entry) { 34962aeddbfSDean Luick ret = -ENOENT; 35062aeddbfSDean Luick goto done; 35162aeddbfSDean Luick } 35262aeddbfSDean Luick 35362aeddbfSDean Luick /* 35462aeddbfSDean Luick * Sanity check on the configuration file size - it should never 35562aeddbfSDean Luick * be larger than 4 KiB. 35662aeddbfSDean Luick */ 35762aeddbfSDean Luick if (entry->size > (4 * 1024)) { 35862aeddbfSDean Luick dd_dev_err(dd, "Bad configuration file size 0x%x\n", 35962aeddbfSDean Luick entry->size); 36062aeddbfSDean Luick ret = -EINVAL; 36162aeddbfSDean Luick goto done; 36262aeddbfSDean Luick } 36362aeddbfSDean Luick 36462aeddbfSDean Luick /* check for bogus offset and size that wrap when added together */ 36562aeddbfSDean Luick if (entry->offset + entry->size < entry->offset) { 36662aeddbfSDean Luick dd_dev_err(dd, 36762aeddbfSDean Luick "Bad configuration file start + size 0x%x+0x%x\n", 36862aeddbfSDean Luick entry->offset, entry->size); 36962aeddbfSDean Luick ret = -EINVAL; 37062aeddbfSDean Luick goto done; 37162aeddbfSDean Luick } 37262aeddbfSDean Luick 37362aeddbfSDean Luick /* allocate the buffer to return */ 37462aeddbfSDean Luick buffer = kmalloc(entry->size, GFP_KERNEL); 37562aeddbfSDean Luick if (!buffer) { 37662aeddbfSDean Luick ret = -ENOMEM; 37762aeddbfSDean Luick goto done; 37862aeddbfSDean Luick } 37962aeddbfSDean Luick 38062aeddbfSDean Luick /* 38162aeddbfSDean Luick * Extract the file by looping over segments until it is fully read. 38262aeddbfSDean Luick */ 38362aeddbfSDean Luick seg_offset = entry->offset % SEG_SIZE; 38462aeddbfSDean Luick seg_base = entry->offset - seg_offset; 38562aeddbfSDean Luick ncopied = 0; 38662aeddbfSDean Luick while (ncopied < entry->size) { 38762aeddbfSDean Luick /* calculate data bytes available in this segment */ 38862aeddbfSDean Luick 38962aeddbfSDean Luick /* start with the bytes from the current offset to the end */ 39062aeddbfSDean Luick bytes_available = SEG_SIZE - seg_offset; 39162aeddbfSDean Luick /* subtract off footer and table from segment 0 */ 39262aeddbfSDean Luick if (seg_base == 0) { 39362aeddbfSDean Luick /* 39462aeddbfSDean Luick * Sanity check: should not have a starting point 39562aeddbfSDean Luick * at or within the directory. 39662aeddbfSDean Luick */ 39762aeddbfSDean Luick if (bytes_available <= directory_size) { 39862aeddbfSDean Luick dd_dev_err(dd, 39962aeddbfSDean Luick "Bad configuration file - offset 0x%x within footer+table\n", 40062aeddbfSDean Luick entry->offset); 40162aeddbfSDean Luick ret = -EINVAL; 40262aeddbfSDean Luick goto done; 40362aeddbfSDean Luick } 40462aeddbfSDean Luick bytes_available -= directory_size; 40562aeddbfSDean Luick } 40662aeddbfSDean Luick 40762aeddbfSDean Luick /* calculate bytes wanted */ 40862aeddbfSDean Luick to_copy = entry->size - ncopied; 40962aeddbfSDean Luick 41062aeddbfSDean Luick /* max out at the available bytes in this segment */ 41162aeddbfSDean Luick if (to_copy > bytes_available) 41262aeddbfSDean Luick to_copy = bytes_available; 41362aeddbfSDean Luick 41462aeddbfSDean Luick /* 41562aeddbfSDean Luick * Read from the EPROM. 41662aeddbfSDean Luick * 41762aeddbfSDean Luick * The sanity check for entry->offset is done in read_length(). 41862aeddbfSDean Luick * The EPROM offset is validated against what the hardware 41962aeddbfSDean Luick * addressing supports. In addition, if the offset is larger 42062aeddbfSDean Luick * than the actual EPROM, it silently wraps. It will work 42162aeddbfSDean Luick * fine, though the reader may not get what they expected 42262aeddbfSDean Luick * from the EPROM. 42362aeddbfSDean Luick */ 42462aeddbfSDean Luick ret = read_length(dd, seg_base + seg_offset, to_copy, 42562aeddbfSDean Luick buffer + ncopied); 42662aeddbfSDean Luick if (ret) 42762aeddbfSDean Luick goto done; 42862aeddbfSDean Luick 42962aeddbfSDean Luick ncopied += to_copy; 43062aeddbfSDean Luick 43162aeddbfSDean Luick /* set up for next segment */ 43262aeddbfSDean Luick seg_offset = footer->oprom_size; 43362aeddbfSDean Luick seg_base += SEG_SIZE; 43462aeddbfSDean Luick } 43562aeddbfSDean Luick 43662aeddbfSDean Luick /* success */ 43762aeddbfSDean Luick ret = 0; 43862aeddbfSDean Luick *data = buffer; 43962aeddbfSDean Luick *size = entry->size; 44062aeddbfSDean Luick 44162aeddbfSDean Luick done: 44262aeddbfSDean Luick kfree(table_buffer); 44362aeddbfSDean Luick if (ret) 44462aeddbfSDean Luick kfree(buffer); 44562aeddbfSDean Luick return ret; 44662aeddbfSDean Luick } 44762aeddbfSDean Luick 44862aeddbfSDean Luick /* 449107ffbc5SDean Luick * Read the platform configuration file from the EPROM. 450107ffbc5SDean Luick * 451107ffbc5SDean Luick * On success, an allocated buffer containing the data and its size are 452107ffbc5SDean Luick * returned. It is up to the caller to free this buffer. 453107ffbc5SDean Luick * 454107ffbc5SDean Luick * Return value: 455107ffbc5SDean Luick * 0 - success 456107ffbc5SDean Luick * -ENXIO - no EPROM is available 457107ffbc5SDean Luick * -EBUSY - not able to acquire access to the EPROM 458107ffbc5SDean Luick * -ENOENT - no recognizable file written 459107ffbc5SDean Luick * -ENOMEM - buffer could not be allocated 46062aeddbfSDean Luick * -EINVAL - invalid EPROM contentents found 461107ffbc5SDean Luick */ 462107ffbc5SDean Luick int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) 463107ffbc5SDean Luick { 464107ffbc5SDean Luick u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */ 465107ffbc5SDean Luick int ret; 466107ffbc5SDean Luick 467107ffbc5SDean Luick if (!dd->eprom_available) 468107ffbc5SDean Luick return -ENXIO; 469107ffbc5SDean Luick 470107ffbc5SDean Luick ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 471107ffbc5SDean Luick if (ret) 472107ffbc5SDean Luick return -EBUSY; 473107ffbc5SDean Luick 47462aeddbfSDean Luick /* read the last page of the segment for the EPROM format magic */ 47562aeddbfSDean Luick ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); 476107ffbc5SDean Luick if (ret) 477107ffbc5SDean Luick goto done; 478107ffbc5SDean Luick 47962aeddbfSDean Luick /* last dword of the segment contains a magic value */ 48062aeddbfSDean Luick if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { 48162aeddbfSDean Luick /* segment format */ 48262aeddbfSDean Luick ret = read_segment_platform_config(dd, directory, data, size); 48362aeddbfSDean Luick } else { 484107ffbc5SDean Luick /* partition format */ 485107ffbc5SDean Luick ret = read_partition_platform_config(dd, data, size); 486107ffbc5SDean Luick } 487107ffbc5SDean Luick 488107ffbc5SDean Luick done: 489107ffbc5SDean Luick release_chip_resource(dd, CR_EPROM); 490107ffbc5SDean Luick return ret; 491107ffbc5SDean Luick } 492