brintos

brintos / linux-shallow public Read only

0
0
Text · 11.2 KiB · f93a160 Raw
451 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/*3 * Copyright(c) 2015, 2016 Intel Corporation.4 */5 6#include <linux/delay.h>7#include "hfi.h"8#include "common.h"9#include "eprom.h"10 11/*12 * The EPROM is logically divided into three partitions:13 *	partition 0: the first 128K, visible from PCI ROM BAR14 *	partition 1: 4K config file (sector size)15 *	partition 2: the rest16 */17#define P0_SIZE (128 * 1024)18#define P1_SIZE   (4 * 1024)19#define P1_START P0_SIZE20#define P2_START (P0_SIZE + P1_SIZE)21 22/* controller page size, in bytes */23#define EP_PAGE_SIZE 25624#define EP_PAGE_MASK (EP_PAGE_SIZE - 1)25#define EP_PAGE_DWORDS (EP_PAGE_SIZE / sizeof(u32))26 27/* controller commands */28#define CMD_SHIFT 2429#define CMD_NOP			    (0)30#define CMD_READ_DATA(addr)	    ((0x03 << CMD_SHIFT) | addr)31#define CMD_RELEASE_POWERDOWN_NOID  ((0xab << CMD_SHIFT))32 33/* controller interface speeds */34#define EP_SPEED_FULL 0x2	/* full speed */35 36/*37 * How long to wait for the EPROM to become available, in ms.38 * The spec 32 Mb EPROM takes around 40s to erase then write.39 * Double it for safety.40 */41#define EPROM_TIMEOUT 80000 /* ms */42 43/*44 * Read a 256 byte (64 dword) EPROM page.45 * All callers have verified the offset is at a page boundary.46 */47static void read_page(struct hfi1_devdata *dd, u32 offset, u32 *result)48{49	int i;50 51	write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_READ_DATA(offset));52	for (i = 0; i < EP_PAGE_DWORDS; i++)53		result[i] = (u32)read_csr(dd, ASIC_EEP_DATA);54	write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_NOP); /* close open page */55}56 57/*58 * Read length bytes starting at offset from the start of the EPROM.59 */60static int read_length(struct hfi1_devdata *dd, u32 start, u32 len, void *dest)61{62	u32 buffer[EP_PAGE_DWORDS];63	u32 end;64	u32 start_offset;65	u32 read_start;66	u32 bytes;67 68	if (len == 0)69		return 0;70 71	end = start + len;72 73	/*74	 * Make sure the read range is not outside of the controller read75	 * command address range.  Note that '>' is correct below - the end76	 * of the range is OK if it stops at the limit, but no higher.77	 */78	if (end > (1 << CMD_SHIFT))79		return -EINVAL;80 81	/* read the first partial page */82	start_offset = start & EP_PAGE_MASK;83	if (start_offset) {84		/* partial starting page */85 86		/* align and read the page that contains the start */87		read_start = start & ~EP_PAGE_MASK;88		read_page(dd, read_start, buffer);89 90		/* the rest of the page is available data */91		bytes = EP_PAGE_SIZE - start_offset;92 93		if (len <= bytes) {94			/* end is within this page */95			memcpy(dest, (u8 *)buffer + start_offset, len);96			return 0;97		}98 99		memcpy(dest, (u8 *)buffer + start_offset, bytes);100 101		start += bytes;102		len -= bytes;103		dest += bytes;104	}105	/* start is now page aligned */106 107	/* read whole pages */108	while (len >= EP_PAGE_SIZE) {109		read_page(dd, start, buffer);110		memcpy(dest, buffer, EP_PAGE_SIZE);111 112		start += EP_PAGE_SIZE;113		len -= EP_PAGE_SIZE;114		dest += EP_PAGE_SIZE;115	}116 117	/* read the last partial page */118	if (len) {119		read_page(dd, start, buffer);120		memcpy(dest, buffer, len);121	}122 123	return 0;124}125 126/*127 * Initialize the EPROM handler.128 */129int eprom_init(struct hfi1_devdata *dd)130{131	int ret = 0;132 133	/* only the discrete chip has an EPROM */134	if (dd->pcidev->device != PCI_DEVICE_ID_INTEL0)135		return 0;136 137	/*138	 * It is OK if both HFIs reset the EPROM as long as they don't139	 * do it at the same time.140	 */141	ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT);142	if (ret) {143		dd_dev_err(dd,144			   "%s: unable to acquire EPROM resource, no EPROM support\n",145			   __func__);146		goto done_asic;147	}148 149	/* reset EPROM to be sure it is in a good state */150 151	/* set reset */152	write_csr(dd, ASIC_EEP_CTL_STAT, ASIC_EEP_CTL_STAT_EP_RESET_SMASK);153	/* clear reset, set speed */154	write_csr(dd, ASIC_EEP_CTL_STAT,155		  EP_SPEED_FULL << ASIC_EEP_CTL_STAT_RATE_SPI_SHIFT);156 157	/* wake the device with command "release powerdown NoID" */158	write_csr(dd, ASIC_EEP_ADDR_CMD, CMD_RELEASE_POWERDOWN_NOID);159 160	dd->eprom_available = true;161	release_chip_resource(dd, CR_EPROM);162done_asic:163	return ret;164}165 166/* magic character sequence that begins an image */167#define IMAGE_START_MAGIC "APO="168 169/* magic character sequence that might trail an image */170#define IMAGE_TRAIL_MAGIC "egamiAPO"171 172/* EPROM file types */173#define HFI1_EFT_PLATFORM_CONFIG 2174 175/* segment size - 128 KiB */176#define SEG_SIZE (128 * 1024)177 178struct hfi1_eprom_footer {179	u32 oprom_size;		/* size of the oprom, in bytes */180	u16 num_table_entries;181	u16 version;		/* version of this footer */182	u32 magic;		/* must be last */183};184 185struct hfi1_eprom_table_entry {186	u32 type;		/* file type */187	u32 offset;		/* file offset from start of EPROM */188	u32 size;		/* file size, in bytes */189};190 191/*192 * Calculate the max number of table entries that will fit within a directory193 * buffer of size 'dir_size'.194 */195#define MAX_TABLE_ENTRIES(dir_size) \196	(((dir_size) - sizeof(struct hfi1_eprom_footer)) / \197		sizeof(struct hfi1_eprom_table_entry))198 199#define DIRECTORY_SIZE(n) (sizeof(struct hfi1_eprom_footer) + \200	(sizeof(struct hfi1_eprom_table_entry) * (n)))201 202#define MAGIC4(a, b, c, d) ((d) << 24 | (c) << 16 | (b) << 8 | (a))203#define FOOTER_MAGIC MAGIC4('e', 'p', 'r', 'm')204#define FOOTER_VERSION 1205 206/*207 * Read all of partition 1.  The actual file is at the front.  Adjust208 * the returned size if a trailing image magic is found.209 */210static int read_partition_platform_config(struct hfi1_devdata *dd, void **data,211					  u32 *size)212{213	void *buffer;214	void *p;215	u32 length;216	int ret;217 218	buffer = kmalloc(P1_SIZE, GFP_KERNEL);219	if (!buffer)220		return -ENOMEM;221 222	ret = read_length(dd, P1_START, P1_SIZE, buffer);223	if (ret) {224		kfree(buffer);225		return ret;226	}227 228	/* config partition is valid only if it starts with IMAGE_START_MAGIC */229	if (memcmp(buffer, IMAGE_START_MAGIC, strlen(IMAGE_START_MAGIC))) {230		kfree(buffer);231		return -ENOENT;232	}233 234	/* scan for image magic that may trail the actual data */235	p = strnstr(buffer, IMAGE_TRAIL_MAGIC, P1_SIZE);236	if (p)237		length = p - buffer;238	else239		length = P1_SIZE;240 241	*data = buffer;242	*size = length;243	return 0;244}245 246/*247 * The segment magic has been checked.  There is a footer and table of248 * contents present.249 *250 * directory is a u32 aligned buffer of size EP_PAGE_SIZE.251 */252static int read_segment_platform_config(struct hfi1_devdata *dd,253					void *directory, void **data, u32 *size)254{255	struct hfi1_eprom_footer *footer;256	struct hfi1_eprom_table_entry *table;257	struct hfi1_eprom_table_entry *entry;258	void *buffer = NULL;259	void *table_buffer = NULL;260	int ret, i;261	u32 directory_size;262	u32 seg_base, seg_offset;263	u32 bytes_available, ncopied, to_copy;264 265	/* the footer is at the end of the directory */266	footer = (struct hfi1_eprom_footer *)267			(directory + EP_PAGE_SIZE - sizeof(*footer));268 269	/* make sure the structure version is supported */270	if (footer->version != FOOTER_VERSION)271		return -EINVAL;272 273	/* oprom size cannot be larger than a segment */274	if (footer->oprom_size >= SEG_SIZE)275		return -EINVAL;276 277	/* the file table must fit in a segment with the oprom */278	if (footer->num_table_entries >279			MAX_TABLE_ENTRIES(SEG_SIZE - footer->oprom_size))280		return -EINVAL;281 282	/* find the file table start, which precedes the footer */283	directory_size = DIRECTORY_SIZE(footer->num_table_entries);284	if (directory_size <= EP_PAGE_SIZE) {285		/* the file table fits into the directory buffer handed in */286		table = (struct hfi1_eprom_table_entry *)287				(directory + EP_PAGE_SIZE - directory_size);288	} else {289		/* need to allocate and read more */290		table_buffer = kmalloc(directory_size, GFP_KERNEL);291		if (!table_buffer)292			return -ENOMEM;293		ret = read_length(dd, SEG_SIZE - directory_size,294				  directory_size, table_buffer);295		if (ret)296			goto done;297		table = table_buffer;298	}299 300	/* look for the platform configuration file in the table */301	for (entry = NULL, i = 0; i < footer->num_table_entries; i++) {302		if (table[i].type == HFI1_EFT_PLATFORM_CONFIG) {303			entry = &table[i];304			break;305		}306	}307	if (!entry) {308		ret = -ENOENT;309		goto done;310	}311 312	/*313	 * Sanity check on the configuration file size - it should never314	 * be larger than 4 KiB.315	 */316	if (entry->size > (4 * 1024)) {317		dd_dev_err(dd, "Bad configuration file size 0x%x\n",318			   entry->size);319		ret = -EINVAL;320		goto done;321	}322 323	/* check for bogus offset and size that wrap when added together */324	if (entry->offset + entry->size < entry->offset) {325		dd_dev_err(dd,326			   "Bad configuration file start + size 0x%x+0x%x\n",327			   entry->offset, entry->size);328		ret = -EINVAL;329		goto done;330	}331 332	/* allocate the buffer to return */333	buffer = kmalloc(entry->size, GFP_KERNEL);334	if (!buffer) {335		ret = -ENOMEM;336		goto done;337	}338 339	/*340	 * Extract the file by looping over segments until it is fully read.341	 */342	seg_offset = entry->offset % SEG_SIZE;343	seg_base = entry->offset - seg_offset;344	ncopied = 0;345	while (ncopied < entry->size) {346		/* calculate data bytes available in this segment */347 348		/* start with the bytes from the current offset to the end */349		bytes_available = SEG_SIZE - seg_offset;350		/* subtract off footer and table from segment 0 */351		if (seg_base == 0) {352			/*353			 * Sanity check: should not have a starting point354			 * at or within the directory.355			 */356			if (bytes_available <= directory_size) {357				dd_dev_err(dd,358					   "Bad configuration file - offset 0x%x within footer+table\n",359					   entry->offset);360				ret = -EINVAL;361				goto done;362			}363			bytes_available -= directory_size;364		}365 366		/* calculate bytes wanted */367		to_copy = entry->size - ncopied;368 369		/* max out at the available bytes in this segment */370		if (to_copy > bytes_available)371			to_copy = bytes_available;372 373		/*374		 * Read from the EPROM.375		 *376		 * The sanity check for entry->offset is done in read_length().377		 * The EPROM offset is validated against what the hardware378		 * addressing supports.  In addition, if the offset is larger379		 * than the actual EPROM, it silently wraps.  It will work380		 * fine, though the reader may not get what they expected381		 * from the EPROM.382		 */383		ret = read_length(dd, seg_base + seg_offset, to_copy,384				  buffer + ncopied);385		if (ret)386			goto done;387 388		ncopied += to_copy;389 390		/* set up for next segment */391		seg_offset = footer->oprom_size;392		seg_base += SEG_SIZE;393	}394 395	/* success */396	ret = 0;397	*data = buffer;398	*size = entry->size;399 400done:401	kfree(table_buffer);402	if (ret)403		kfree(buffer);404	return ret;405}406 407/*408 * Read the platform configuration file from the EPROM.409 *410 * On success, an allocated buffer containing the data and its size are411 * returned.  It is up to the caller to free this buffer.412 *413 * Return value:414 *   0	      - success415 *   -ENXIO   - no EPROM is available416 *   -EBUSY   - not able to acquire access to the EPROM417 *   -ENOENT  - no recognizable file written418 *   -ENOMEM  - buffer could not be allocated419 *   -EINVAL  - invalid EPROM contentents found420 */421int eprom_read_platform_config(struct hfi1_devdata *dd, void **data, u32 *size)422{423	u32 directory[EP_PAGE_DWORDS]; /* aligned buffer */424	int ret;425 426	if (!dd->eprom_available)427		return -ENXIO;428 429	ret = acquire_chip_resource(dd, CR_EPROM, EPROM_TIMEOUT);430	if (ret)431		return -EBUSY;432 433	/* read the last page of the segment for the EPROM format magic */434	ret = read_length(dd, SEG_SIZE - EP_PAGE_SIZE, EP_PAGE_SIZE, directory);435	if (ret)436		goto done;437 438	/* last dword of the segment contains a magic value */439	if (directory[EP_PAGE_DWORDS - 1] == FOOTER_MAGIC) {440		/* segment format */441		ret = read_segment_platform_config(dd, directory, data, size);442	} else {443		/* partition format */444		ret = read_partition_platform_config(dd, data, size);445	}446 447done:448	release_chip_resource(dd, CR_EPROM);449	return ret;450}451