brintos

brintos / linux-shallow public Read only

0
0
Text · 10.8 KiB · dab3af8 Raw
385 lines · c
1// SPDX-License-Identifier: GPL-2.02 3#include <linux/bitfield.h>4#include <linux/of.h>5#include <linux/firmware.h>6#include <linux/crc-itu-t.h>7#include <linux/nvmem-consumer.h>8 9#include <linux/unaligned.h>10 11#include "aquantia.h"12 13#define UP_RESET_SLEEP		10014 15/* addresses of memory segments in the phy */16#define DRAM_BASE_ADDR		0x3FFE000017#define IRAM_BASE_ADDR		0x4000000018 19/* firmware image format constants */20#define VERSION_STRING_SIZE		0x4021#define VERSION_STRING_OFFSET		0x020022/* primary offset is written at an offset from the start of the fw blob */23#define PRIMARY_OFFSET_OFFSET		0x824/* primary offset needs to be then added to a base offset */25#define PRIMARY_OFFSET_SHIFT		1226#define PRIMARY_OFFSET(x)		((x) << PRIMARY_OFFSET_SHIFT)27#define HEADER_OFFSET			0x30028 29struct aqr_fw_header {30	u32 padding;31	u8 iram_offset[3];32	u8 iram_size[3];33	u8 dram_offset[3];34	u8 dram_size[3];35} __packed;36 37enum aqr_fw_src {38	AQR_FW_SRC_NVMEM = 0,39	AQR_FW_SRC_FS,40};41 42static const char * const aqr_fw_src_string[] = {43	[AQR_FW_SRC_NVMEM] = "NVMEM",44	[AQR_FW_SRC_FS] = "FS",45};46 47/* AQR firmware doesn't have fixed offsets for iram and dram section48 * but instead provide an header with the offset to use on reading49 * and parsing the firmware.50 *51 * AQR firmware can't be trusted and each offset is validated to be52 * not negative and be in the size of the firmware itself.53 */54static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)55{56	return offset + get_size <= size;57}58 59static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)60{61	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))62		return -EINVAL;63 64	*value = get_unaligned_be16(data + offset);65 66	return 0;67}68 69static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)70{71	if (!aqr_fw_validate_get(size, offset, sizeof(u16)))72		return -EINVAL;73 74	*value = get_unaligned_le16(data + offset);75 76	return 0;77}78 79static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)80{81	if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))82		return -EINVAL;83 84	*value = get_unaligned_le24(data + offset);85 86	return 0;87}88 89/* load data into the phy's memory */90static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,91			      const u8 *data, size_t len)92{93	u16 crc = 0, up_crc;94	size_t pos;95 96	phy_write_mmd(phydev, MDIO_MMD_VEND1,97		      VEND1_GLOBAL_MAILBOX_INTERFACE1,98		      VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);99	phy_write_mmd(phydev, MDIO_MMD_VEND1,100		      VEND1_GLOBAL_MAILBOX_INTERFACE3,101		      VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));102	phy_write_mmd(phydev, MDIO_MMD_VEND1,103		      VEND1_GLOBAL_MAILBOX_INTERFACE4,104		      VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));105 106	/* We assume and enforce the size to be word aligned.107	 * If a firmware that is not word aligned is found, please report upstream.108	 */109	for (pos = 0; pos < len; pos += sizeof(u32)) {110		u8 crc_data[4];111		u32 word;112 113		/* FW data is always stored in little-endian */114		word = get_unaligned_le32((const u32 *)(data + pos));115 116		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,117			      VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));118		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,119			      VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));120 121		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,122			      VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |123			      VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);124 125		/* Word is swapped internally and MAILBOX CRC is calculated126		 * using big-endian order. Mimic what the PHY does to have a127		 * matching CRC...128		 */129		crc_data[0] = word >> 24;130		crc_data[1] = word >> 16;131		crc_data[2] = word >> 8;132		crc_data[3] = word;133 134		/* ...calculate CRC as we load data... */135		crc = crc_itu_t(crc, crc_data, sizeof(crc_data));136	}137	/* ...gets CRC from MAILBOX after we have loaded the entire section... */138	up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);139	/* ...and make sure it does match our calculated CRC */140	if (crc != up_crc) {141		phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",142			   crc, up_crc);143		return -EINVAL;144	}145 146	return 0;147}148 149static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,150		       enum aqr_fw_src fw_src)151{152	u16 calculated_crc, read_crc, read_primary_offset;153	u32 iram_offset = 0, iram_size = 0;154	u32 dram_offset = 0, dram_size = 0;155	char version[VERSION_STRING_SIZE];156	u32 primary_offset = 0;157	int ret;158 159	/* extract saved CRC at the end of the fw160	 * CRC is saved in big-endian as PHY is BE161	 */162	ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);163	if (ret) {164		phydev_err(phydev, "bad firmware CRC in firmware\n");165		return ret;166	}167	calculated_crc = crc_itu_t(0, data, size - sizeof(u16));168	if (read_crc != calculated_crc) {169		phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",170			   read_crc, calculated_crc);171		return -EINVAL;172	}173 174	/* Get the primary offset to extract DRAM and IRAM sections. */175	ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);176	if (ret) {177		phydev_err(phydev, "bad primary offset in firmware\n");178		return ret;179	}180	primary_offset = PRIMARY_OFFSET(read_primary_offset);181 182	/* Find the DRAM and IRAM sections within the firmware file.183	 * Make sure the fw_header is correctly in the firmware.184	 */185	if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,186				 sizeof(struct aqr_fw_header))) {187		phydev_err(phydev, "bad fw_header in firmware\n");188		return -EINVAL;189	}190 191	/* offset are in LE and values needs to be converted to cpu endian */192	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +193			      offsetof(struct aqr_fw_header, iram_offset),194			      size, &iram_offset);195	if (ret) {196		phydev_err(phydev, "bad iram offset in firmware\n");197		return ret;198	}199	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +200			      offsetof(struct aqr_fw_header, iram_size),201			      size, &iram_size);202	if (ret) {203		phydev_err(phydev, "invalid iram size in firmware\n");204		return ret;205	}206	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +207			      offsetof(struct aqr_fw_header, dram_offset),208			      size, &dram_offset);209	if (ret) {210		phydev_err(phydev, "bad dram offset in firmware\n");211		return ret;212	}213	ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +214			      offsetof(struct aqr_fw_header, dram_size),215			      size, &dram_size);216	if (ret) {217		phydev_err(phydev, "invalid dram size in firmware\n");218		return ret;219	}220 221	/* Increment the offset with the primary offset.222	 * Validate iram/dram offset and size.223	 */224	iram_offset += primary_offset;225	if (iram_size % sizeof(u32)) {226		phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");227		return -EINVAL;228	}229	if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {230		phydev_err(phydev, "invalid iram offset for iram size\n");231		return -EINVAL;232	}233 234	dram_offset += primary_offset;235	if (dram_size % sizeof(u32)) {236		phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");237		return -EINVAL;238	}239	if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {240		phydev_err(phydev, "invalid iram offset for iram size\n");241		return -EINVAL;242	}243 244	phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",245		   primary_offset, iram_offset, iram_size, dram_offset, dram_size);246 247	if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,248				 VERSION_STRING_SIZE)) {249		phydev_err(phydev, "invalid version in firmware\n");250		return -EINVAL;251	}252	strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,253		VERSION_STRING_SIZE);254	if (version[0] == '\0') {255		phydev_err(phydev, "invalid version in firmware\n");256		return -EINVAL;257	}258	phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,259		    aqr_fw_src_string[fw_src]);260 261	/* stall the microcprocessor */262	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,263		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);264 265	phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",266		   DRAM_BASE_ADDR, dram_offset, dram_size);267	ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,268				 dram_size);269	if (ret)270		return ret;271 272	phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",273		   IRAM_BASE_ADDR, iram_offset, iram_size);274	ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,275				 iram_size);276	if (ret)277		return ret;278 279	/* make sure soft reset and low power mode are clear */280	phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,281			   VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);282 283	/* Release the microprocessor. UP_RESET must be held for 100 usec. */284	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,285		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |286		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |287		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);288	usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);289 290	phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,291		      VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);292 293	return 0;294}295 296static int aqr_firmware_load_nvmem(struct phy_device *phydev)297{298	struct nvmem_cell *cell;299	size_t size;300	u8 *buf;301	int ret;302 303	cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");304	if (IS_ERR(cell))305		return PTR_ERR(cell);306 307	buf = nvmem_cell_read(cell, &size);308	if (IS_ERR(buf)) {309		ret = PTR_ERR(buf);310		goto exit;311	}312 313	ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);314	if (ret)315		phydev_err(phydev, "firmware loading failed: %d\n", ret);316 317	kfree(buf);318exit:319	nvmem_cell_put(cell);320 321	return ret;322}323 324static int aqr_firmware_load_fs(struct phy_device *phydev)325{326	struct device *dev = &phydev->mdio.dev;327	const struct firmware *fw;328	const char *fw_name;329	int ret;330 331	ret = of_property_read_string(dev->of_node, "firmware-name",332				      &fw_name);333	if (ret)334		return ret;335 336	ret = request_firmware(&fw, fw_name, dev);337	if (ret) {338		phydev_err(phydev, "failed to find FW file %s (%d)\n",339			   fw_name, ret);340		return ret;341	}342 343	ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);344	if (ret)345		phydev_err(phydev, "firmware loading failed: %d\n", ret);346 347	release_firmware(fw);348 349	return ret;350}351 352int aqr_firmware_load(struct phy_device *phydev)353{354	int ret;355 356	/* Check if the firmware is not already loaded by polling357	 * the current version returned by the PHY.358	 */359	ret = aqr_wait_reset_complete(phydev);360	switch (ret) {361	case 0:362		/* Some firmware is loaded => do nothing */363		return 0;364	case -ETIMEDOUT:365		/* VEND1_GLOBAL_FW_ID still reads 0 after 2 seconds of polling.366		 * We don't have full confidence that no firmware is loaded (in367		 * theory it might just not have loaded yet), but we will368		 * assume that, and load a new image.369		 */370		ret = aqr_firmware_load_nvmem(phydev);371		if (!ret)372			return ret;373 374		ret = aqr_firmware_load_fs(phydev);375		if (ret)376			return ret;377		break;378	default:379		/* PHY read error, propagate it to the caller */380		return ret;381	}382 383	return 0;384}385