18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2015, 2016 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci#include <linux/delay.h> 488c2ecf20Sopenharmony_ci#include "hfi.h" 498c2ecf20Sopenharmony_ci#include "common.h" 508c2ecf20Sopenharmony_ci#include "eprom.h" 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * The EPROM is logically divided into three partitions: 548c2ecf20Sopenharmony_ci * partition 0: the first 128K, visible from PCI ROM BAR 558c2ecf20Sopenharmony_ci * partition 1: 4K config file (sector size) 568c2ecf20Sopenharmony_ci * partition 2: the rest 578c2ecf20Sopenharmony_ci */ 588c2ecf20Sopenharmony_ci#define P0_SIZE (128 * 1024) 598c2ecf20Sopenharmony_ci#define P1_SIZE (4 * 1024) 608c2ecf20Sopenharmony_ci#define P1_START P0_SIZE 618c2ecf20Sopenharmony_ci#define P2_START (P0_SIZE + P1_SIZE) 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/* controller page size, in bytes */ 648c2ecf20Sopenharmony_ci#define EP_PAGE_SIZE 256 658c2ecf20Sopenharmony_ci#define EP_PAGE_MASK (EP_PAGE_SIZE - 1) 668c2ecf20Sopenharmony_ci#define EP_PAGE_DWORDS (EP_PAGE_SIZE / sizeof(u32)) 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* controller commands */ 698c2ecf20Sopenharmony_ci#define CMD_SHIFT 24 708c2ecf20Sopenharmony_ci#define CMD_NOP (0) 718c2ecf20Sopenharmony_ci#define CMD_READ_DATA(addr) ((0x03 << CMD_SHIFT) | addr) 728c2ecf20Sopenharmony_ci#define CMD_RELEASE_POWERDOWN_NOID ((0xab << CMD_SHIFT)) 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci/* controller interface speeds */ 758c2ecf20Sopenharmony_ci#define EP_SPEED_FULL 0x2 /* full speed */ 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci/* 788c2ecf20Sopenharmony_ci * How long to wait for the EPROM to become available, in ms. 798c2ecf20Sopenharmony_ci * The spec 32 Mb EPROM takes around 40s to erase then write. 808c2ecf20Sopenharmony_ci * Double it for safety. 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci#define EPROM_TIMEOUT 80000 /* ms */ 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci/* 858c2ecf20Sopenharmony_ci * Read a 256 byte (64 dword) EPROM page. 868c2ecf20Sopenharmony_ci * All callers have verified the offset is at a page boundary. 878c2ecf20Sopenharmony_ci */ 888c2ecf20Sopenharmony_cistatic void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci int i; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset)); 938c2ecf20Sopenharmony_ci for (i = 0; i < EP_PAGE_DWORDS; i++) 948c2ecf20Sopenharmony_ci result[i] = (u32)read_csr(dd, ASIC_EEP_DATA); 958c2ecf20Sopenharmony_ci write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */ 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci/* 998c2ecf20Sopenharmony_ci * Read length bytes starting at offset from the start of the EPROM. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_cistatic int read_length(struct hfi1_devdata *dd, u32 start, u32 len, void *dest) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci u32 buffer[EP_PAGE_DWORDS]; 1048c2ecf20Sopenharmony_ci u32 end; 1058c2ecf20Sopenharmony_ci u32 start_offset; 1068c2ecf20Sopenharmony_ci u32 read_start; 1078c2ecf20Sopenharmony_ci u32 bytes; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci if (len == 0) 1108c2ecf20Sopenharmony_ci return 0; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci end = start + len; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* 1158c2ecf20Sopenharmony_ci * Make sure the read range is not outside of the controller read 1168c2ecf20Sopenharmony_ci * command address range. Note that '>' is correct below - the end 1178c2ecf20Sopenharmony_ci * of the range is OK if it stops at the limit, but no higher. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci if (end > (1 << CMD_SHIFT)) 1208c2ecf20Sopenharmony_ci return -EINVAL; 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci /* read the first partial page */ 1238c2ecf20Sopenharmony_ci start_offset = start & EP_PAGE_MASK; 1248c2ecf20Sopenharmony_ci if (start_offset) { 1258c2ecf20Sopenharmony_ci /* partial starting page */ 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* align and read the page that contains the start */ 1288c2ecf20Sopenharmony_ci read_start = start & ~EP_PAGE_MASK; 1298c2ecf20Sopenharmony_ci read_page(dd, read_start, buffer); 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* the rest of the page is available data */ 1328c2ecf20Sopenharmony_ci bytes = EP_PAGE_SIZE - start_offset; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (len <= bytes) { 1358c2ecf20Sopenharmony_ci /* end is within this page */ 1368c2ecf20Sopenharmony_ci memcpy(dest, (u8 *)buffer + start_offset, len); 1378c2ecf20Sopenharmony_ci return 0; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci memcpy(dest, (u8 *)buffer + start_offset, bytes); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci start += bytes; 1438c2ecf20Sopenharmony_ci len -= bytes; 1448c2ecf20Sopenharmony_ci dest += bytes; 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci /* start is now page aligned */ 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* read whole pages */ 1498c2ecf20Sopenharmony_ci while (len >= EP_PAGE_SIZE) { 1508c2ecf20Sopenharmony_ci read_page(dd, start, buffer); 1518c2ecf20Sopenharmony_ci memcpy(dest, buffer, EP_PAGE_SIZE); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci start += EP_PAGE_SIZE; 1548c2ecf20Sopenharmony_ci len -= EP_PAGE_SIZE; 1558c2ecf20Sopenharmony_ci dest += EP_PAGE_SIZE; 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* read the last partial page */ 1598c2ecf20Sopenharmony_ci if (len) { 1608c2ecf20Sopenharmony_ci read_page(dd, start, buffer); 1618c2ecf20Sopenharmony_ci memcpy(dest, buffer, len); 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return 0; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * Initialize the EPROM handler. 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciint eprom_init(struct hfi1_devdata *dd) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci int ret = 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* only the discrete chip has an EPROM */ 1758c2ecf20Sopenharmony_ci if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0) 1768c2ecf20Sopenharmony_ci return 0; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* 1798c2ecf20Sopenharmony_ci * It is OK if both HFIs reset the EPROM as long as they don't 1808c2ecf20Sopenharmony_ci * do it at the same time. 1818c2ecf20Sopenharmony_ci */ 1828c2ecf20Sopenharmony_ci ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 1838c2ecf20Sopenharmony_ci if (ret) { 1848c2ecf20Sopenharmony_ci dd_dev_err(dd, 1858c2ecf20Sopenharmony_ci "%s: unable to acquire EPROM resource, no EPROM support\n", 1868c2ecf20Sopenharmony_ci __func__); 1878c2ecf20Sopenharmony_ci goto done_asic; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* reset EPROM to be sure it is in a good state */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* set reset */ 1938c2ecf20Sopenharmony_ci write_csr(dd, ASIC_EEP_CTL_STAT, ASIC_EEP_CTL_STAT_EP_RESET_SMASK); 1948c2ecf20Sopenharmony_ci /* clear reset, set speed */ 1958c2ecf20Sopenharmony_ci write_csr(dd, ASIC_EEP_CTL_STAT, 1968c2ecf20Sopenharmony_ci EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT); 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci /* wake the device with command "release powerdown NoID" */ 1998c2ecf20Sopenharmony_ci write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci dd->eprom_available = true; 2028c2ecf20Sopenharmony_ci release_chip_resource(dd, CR_EPROM); 2038c2ecf20Sopenharmony_cidone_asic: 2048c2ecf20Sopenharmony_ci return ret; 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci/* magic character sequence that begins an image */ 2088c2ecf20Sopenharmony_ci#define IMAGE_START_MAGIC "APO=" 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci/* magic character sequence that might trail an image */ 2118c2ecf20Sopenharmony_ci#define IMAGE_TRAIL_MAGIC "egamiAPO" 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* EPROM file types */ 2148c2ecf20Sopenharmony_ci#define HFI1_EFT_PLATFORM_CONFIG 2 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci/* segment size - 128 KiB */ 2178c2ecf20Sopenharmony_ci#define SEG_SIZE (128 * 1024) 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistruct hfi1_eprom_footer { 2208c2ecf20Sopenharmony_ci u32 oprom_size; /* size of the oprom, in bytes */ 2218c2ecf20Sopenharmony_ci u16 num_table_entries; 2228c2ecf20Sopenharmony_ci u16 version; /* version of this footer */ 2238c2ecf20Sopenharmony_ci u32 magic; /* must be last */ 2248c2ecf20Sopenharmony_ci}; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistruct hfi1_eprom_table_entry { 2278c2ecf20Sopenharmony_ci u32 type; /* file type */ 2288c2ecf20Sopenharmony_ci u32 offset; /* file offset from start of EPROM */ 2298c2ecf20Sopenharmony_ci u32 size; /* file size, in bytes */ 2308c2ecf20Sopenharmony_ci}; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci/* 2338c2ecf20Sopenharmony_ci * Calculate the max number of table entries that will fit within a directory 2348c2ecf20Sopenharmony_ci * buffer of size 'dir_size'. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_ci#define MAX_TABLE_ENTRIES(dir_size) \ 2378c2ecf20Sopenharmony_ci (((dir_size) - sizeof(struct hfi1_eprom_footer)) / \ 2388c2ecf20Sopenharmony_ci sizeof(struct hfi1_eprom_table_entry)) 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci#define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \ 2418c2ecf20Sopenharmony_ci (sizeof(struct hfi1_eprom_table_entry) * (n))) 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci#define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a)) 2448c2ecf20Sopenharmony_ci#define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm') 2458c2ecf20Sopenharmony_ci#define FOOTER_VERSION 1 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci/* 2488c2ecf20Sopenharmony_ci * Read all of partition 1. The actual file is at the front. Adjust 2498c2ecf20Sopenharmony_ci * the returned size if a trailing image magic is found. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_cistatic int read_partition_platform_config(struct hfi1_devdata *dd, void **data, 2528c2ecf20Sopenharmony_ci u32 *size) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci void *buffer; 2558c2ecf20Sopenharmony_ci void *p; 2568c2ecf20Sopenharmony_ci u32 length; 2578c2ecf20Sopenharmony_ci int ret; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci buffer = kmalloc(P1_SIZE, GFP_KERNEL); 2608c2ecf20Sopenharmony_ci if (!buffer) 2618c2ecf20Sopenharmony_ci return -ENOMEM; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci ret = read_length(dd, P1_START, P1_SIZE, buffer); 2648c2ecf20Sopenharmony_ci if (ret) { 2658c2ecf20Sopenharmony_ci kfree(buffer); 2668c2ecf20Sopenharmony_ci return ret; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci /* config partition is valid only if it starts with IMAGE_START_MAGIC */ 2708c2ecf20Sopenharmony_ci if (memcmp(buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) { 2718c2ecf20Sopenharmony_ci kfree(buffer); 2728c2ecf20Sopenharmony_ci return -ENOENT; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci /* scan for image magic that may trail the actual data */ 2768c2ecf20Sopenharmony_ci p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE); 2778c2ecf20Sopenharmony_ci if (p) 2788c2ecf20Sopenharmony_ci length = p - buffer; 2798c2ecf20Sopenharmony_ci else 2808c2ecf20Sopenharmony_ci length = P1_SIZE; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci *data = buffer; 2838c2ecf20Sopenharmony_ci *size = length; 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* 2888c2ecf20Sopenharmony_ci * The segment magic has been checked. There is a footer and table of 2898c2ecf20Sopenharmony_ci * contents present. 2908c2ecf20Sopenharmony_ci * 2918c2ecf20Sopenharmony_ci * directory is a u32 aligned buffer of size EP_PAGE_SIZE. 2928c2ecf20Sopenharmony_ci */ 2938c2ecf20Sopenharmony_cistatic int read_segment_platform_config(struct hfi1_devdata *dd, 2948c2ecf20Sopenharmony_ci void *directory, void **data, u32 *size) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct hfi1_eprom_footer *footer; 2978c2ecf20Sopenharmony_ci struct hfi1_eprom_table_entry *table; 2988c2ecf20Sopenharmony_ci struct hfi1_eprom_table_entry *entry; 2998c2ecf20Sopenharmony_ci void *buffer = NULL; 3008c2ecf20Sopenharmony_ci void *table_buffer = NULL; 3018c2ecf20Sopenharmony_ci int ret, i; 3028c2ecf20Sopenharmony_ci u32 directory_size; 3038c2ecf20Sopenharmony_ci u32 seg_base, seg_offset; 3048c2ecf20Sopenharmony_ci u32 bytes_available, ncopied, to_copy; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* the footer is at the end of the directory */ 3078c2ecf20Sopenharmony_ci footer = (struct hfi1_eprom_footer *) 3088c2ecf20Sopenharmony_ci (directory + EP_PAGE_SIZE - sizeof(*footer)); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci /* make sure the structure version is supported */ 3118c2ecf20Sopenharmony_ci if (footer->version != FOOTER_VERSION) 3128c2ecf20Sopenharmony_ci return -EINVAL; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* oprom size cannot be larger than a segment */ 3158c2ecf20Sopenharmony_ci if (footer->oprom_size >= SEG_SIZE) 3168c2ecf20Sopenharmony_ci return -EINVAL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* the file table must fit in a segment with the oprom */ 3198c2ecf20Sopenharmony_ci if (footer->num_table_entries > 3208c2ecf20Sopenharmony_ci MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size)) 3218c2ecf20Sopenharmony_ci return -EINVAL; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci /* find the file table start, which precedes the footer */ 3248c2ecf20Sopenharmony_ci directory_size = DIRECTORY_SIZE(footer->num_table_entries); 3258c2ecf20Sopenharmony_ci if (directory_size <= EP_PAGE_SIZE) { 3268c2ecf20Sopenharmony_ci /* the file table fits into the directory buffer handed in */ 3278c2ecf20Sopenharmony_ci table = (struct hfi1_eprom_table_entry *) 3288c2ecf20Sopenharmony_ci (directory + EP_PAGE_SIZE - directory_size); 3298c2ecf20Sopenharmony_ci } else { 3308c2ecf20Sopenharmony_ci /* need to allocate and read more */ 3318c2ecf20Sopenharmony_ci table_buffer = kmalloc(directory_size, GFP_KERNEL); 3328c2ecf20Sopenharmony_ci if (!table_buffer) 3338c2ecf20Sopenharmony_ci return -ENOMEM; 3348c2ecf20Sopenharmony_ci ret = read_length(dd, SEG_SIZE - directory_size, 3358c2ecf20Sopenharmony_ci directory_size, table_buffer); 3368c2ecf20Sopenharmony_ci if (ret) 3378c2ecf20Sopenharmony_ci goto done; 3388c2ecf20Sopenharmony_ci table = table_buffer; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* look for the platform configuration file in the table */ 3428c2ecf20Sopenharmony_ci for (entry = NULL, i = 0; i < footer->num_table_entries; i++) { 3438c2ecf20Sopenharmony_ci if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) { 3448c2ecf20Sopenharmony_ci entry = &table[i]; 3458c2ecf20Sopenharmony_ci break; 3468c2ecf20Sopenharmony_ci } 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci if (!entry) { 3498c2ecf20Sopenharmony_ci ret = -ENOENT; 3508c2ecf20Sopenharmony_ci goto done; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* 3548c2ecf20Sopenharmony_ci * Sanity check on the configuration file size - it should never 3558c2ecf20Sopenharmony_ci * be larger than 4 KiB. 3568c2ecf20Sopenharmony_ci */ 3578c2ecf20Sopenharmony_ci if (entry->size > (4 * 1024)) { 3588c2ecf20Sopenharmony_ci dd_dev_err(dd, "Bad configuration file size 0x%x\n", 3598c2ecf20Sopenharmony_ci entry->size); 3608c2ecf20Sopenharmony_ci ret = -EINVAL; 3618c2ecf20Sopenharmony_ci goto done; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* check for bogus offset and size that wrap when added together */ 3658c2ecf20Sopenharmony_ci if (entry->offset + entry->size < entry->offset) { 3668c2ecf20Sopenharmony_ci dd_dev_err(dd, 3678c2ecf20Sopenharmony_ci "Bad configuration file start + size 0x%x+0x%x\n", 3688c2ecf20Sopenharmony_ci entry->offset, entry->size); 3698c2ecf20Sopenharmony_ci ret = -EINVAL; 3708c2ecf20Sopenharmony_ci goto done; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* allocate the buffer to return */ 3748c2ecf20Sopenharmony_ci buffer = kmalloc(entry->size, GFP_KERNEL); 3758c2ecf20Sopenharmony_ci if (!buffer) { 3768c2ecf20Sopenharmony_ci ret = -ENOMEM; 3778c2ecf20Sopenharmony_ci goto done; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* 3818c2ecf20Sopenharmony_ci * Extract the file by looping over segments until it is fully read. 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_ci seg_offset = entry->offset % SEG_SIZE; 3848c2ecf20Sopenharmony_ci seg_base = entry->offset - seg_offset; 3858c2ecf20Sopenharmony_ci ncopied = 0; 3868c2ecf20Sopenharmony_ci while (ncopied < entry->size) { 3878c2ecf20Sopenharmony_ci /* calculate data bytes available in this segment */ 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* start with the bytes from the current offset to the end */ 3908c2ecf20Sopenharmony_ci bytes_available = SEG_SIZE - seg_offset; 3918c2ecf20Sopenharmony_ci /* subtract off footer and table from segment 0 */ 3928c2ecf20Sopenharmony_ci if (seg_base == 0) { 3938c2ecf20Sopenharmony_ci /* 3948c2ecf20Sopenharmony_ci * Sanity check: should not have a starting point 3958c2ecf20Sopenharmony_ci * at or within the directory. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ci if (bytes_available <= directory_size) { 3988c2ecf20Sopenharmony_ci dd_dev_err(dd, 3998c2ecf20Sopenharmony_ci "Bad configuration file - offset 0x%x within footer+table\n", 4008c2ecf20Sopenharmony_ci entry->offset); 4018c2ecf20Sopenharmony_ci ret = -EINVAL; 4028c2ecf20Sopenharmony_ci goto done; 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci bytes_available -= directory_size; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* calculate bytes wanted */ 4088c2ecf20Sopenharmony_ci to_copy = entry->size - ncopied; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* max out at the available bytes in this segment */ 4118c2ecf20Sopenharmony_ci if (to_copy > bytes_available) 4128c2ecf20Sopenharmony_ci to_copy = bytes_available; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci /* 4158c2ecf20Sopenharmony_ci * Read from the EPROM. 4168c2ecf20Sopenharmony_ci * 4178c2ecf20Sopenharmony_ci * The sanity check for entry->offset is done in read_length(). 4188c2ecf20Sopenharmony_ci * The EPROM offset is validated against what the hardware 4198c2ecf20Sopenharmony_ci * addressing supports. In addition, if the offset is larger 4208c2ecf20Sopenharmony_ci * than the actual EPROM, it silently wraps. It will work 4218c2ecf20Sopenharmony_ci * fine, though the reader may not get what they expected 4228c2ecf20Sopenharmony_ci * from the EPROM. 4238c2ecf20Sopenharmony_ci */ 4248c2ecf20Sopenharmony_ci ret = read_length(dd, seg_base + seg_offset, to_copy, 4258c2ecf20Sopenharmony_ci buffer + ncopied); 4268c2ecf20Sopenharmony_ci if (ret) 4278c2ecf20Sopenharmony_ci goto done; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci ncopied += to_copy; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci /* set up for next segment */ 4328c2ecf20Sopenharmony_ci seg_offset = footer->oprom_size; 4338c2ecf20Sopenharmony_ci seg_base += SEG_SIZE; 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci /* success */ 4378c2ecf20Sopenharmony_ci ret = 0; 4388c2ecf20Sopenharmony_ci *data = buffer; 4398c2ecf20Sopenharmony_ci *size = entry->size; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cidone: 4428c2ecf20Sopenharmony_ci kfree(table_buffer); 4438c2ecf20Sopenharmony_ci if (ret) 4448c2ecf20Sopenharmony_ci kfree(buffer); 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci} 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci/* 4498c2ecf20Sopenharmony_ci * Read the platform configuration file from the EPROM. 4508c2ecf20Sopenharmony_ci * 4518c2ecf20Sopenharmony_ci * On success, an allocated buffer containing the data and its size are 4528c2ecf20Sopenharmony_ci * returned. It is up to the caller to free this buffer. 4538c2ecf20Sopenharmony_ci * 4548c2ecf20Sopenharmony_ci * Return value: 4558c2ecf20Sopenharmony_ci * 0 - success 4568c2ecf20Sopenharmony_ci * -ENXIO - no EPROM is available 4578c2ecf20Sopenharmony_ci * -EBUSY - not able to acquire access to the EPROM 4588c2ecf20Sopenharmony_ci * -ENOENT - no recognizable file written 4598c2ecf20Sopenharmony_ci * -ENOMEM - buffer could not be allocated 4608c2ecf20Sopenharmony_ci * -EINVAL - invalid EPROM contentents found 4618c2ecf20Sopenharmony_ci */ 4628c2ecf20Sopenharmony_ciint eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */ 4658c2ecf20Sopenharmony_ci int ret; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci if (!dd->eprom_available) 4688c2ecf20Sopenharmony_ci return -ENXIO; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT); 4718c2ecf20Sopenharmony_ci if (ret) 4728c2ecf20Sopenharmony_ci return -EBUSY; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* read the last page of the segment for the EPROM format magic */ 4758c2ecf20Sopenharmony_ci ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory); 4768c2ecf20Sopenharmony_ci if (ret) 4778c2ecf20Sopenharmony_ci goto done; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci /* last dword of the segment contains a magic value */ 4808c2ecf20Sopenharmony_ci if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) { 4818c2ecf20Sopenharmony_ci /* segment format */ 4828c2ecf20Sopenharmony_ci ret = read_segment_platform_config(dd, directory, data, size); 4838c2ecf20Sopenharmony_ci } else { 4848c2ecf20Sopenharmony_ci /* partition format */ 4858c2ecf20Sopenharmony_ci ret = read_partition_platform_config(dd, data, size); 4868c2ecf20Sopenharmony_ci } 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cidone: 4898c2ecf20Sopenharmony_ci release_chip_resource(dd, CR_EPROM); 4908c2ecf20Sopenharmony_ci return ret; 4918c2ecf20Sopenharmony_ci} 492