brintos

brintos / linux-shallow public Read only

0
0
Text · 8.9 KiB · 495ef4e Raw
383 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright(c) 2023 Advanced Micro Devices, Inc */3 4#include <linux/errno.h>5#include <linux/pci.h>6#include <linux/utsname.h>7 8#include "core.h"9 10int pdsc_err_to_errno(enum pds_core_status_code code)11{12	switch (code) {13	case PDS_RC_SUCCESS:14		return 0;15	case PDS_RC_EVERSION:16	case PDS_RC_EQTYPE:17	case PDS_RC_EQID:18	case PDS_RC_EINVAL:19	case PDS_RC_ENOSUPP:20		return -EINVAL;21	case PDS_RC_EPERM:22		return -EPERM;23	case PDS_RC_ENOENT:24		return -ENOENT;25	case PDS_RC_EAGAIN:26		return -EAGAIN;27	case PDS_RC_ENOMEM:28		return -ENOMEM;29	case PDS_RC_EFAULT:30		return -EFAULT;31	case PDS_RC_EBUSY:32		return -EBUSY;33	case PDS_RC_EEXIST:34		return -EEXIST;35	case PDS_RC_EVFID:36		return -ENODEV;37	case PDS_RC_ECLIENT:38		return -ECHILD;39	case PDS_RC_ENOSPC:40		return -ENOSPC;41	case PDS_RC_ERANGE:42		return -ERANGE;43	case PDS_RC_BAD_ADDR:44		return -EFAULT;45	case PDS_RC_BAD_PCI:46		return -ENXIO;47	case PDS_RC_EOPCODE:48	case PDS_RC_EINTR:49	case PDS_RC_DEV_CMD:50	case PDS_RC_ERROR:51	case PDS_RC_ERDMA:52	case PDS_RC_EIO:53	default:54		return -EIO;55	}56}57 58bool pdsc_is_fw_running(struct pdsc *pdsc)59{60	if (!pdsc->info_regs)61		return false;62 63	pdsc->fw_status = ioread8(&pdsc->info_regs->fw_status);64	pdsc->last_fw_time = jiffies;65	pdsc->last_hb = ioread32(&pdsc->info_regs->fw_heartbeat);66 67	/* Firmware is useful only if the running bit is set and68	 * fw_status != 0xff (bad PCI read)69	 */70	return (pdsc->fw_status != PDS_RC_BAD_PCI) &&71		(pdsc->fw_status & PDS_CORE_FW_STS_F_RUNNING);72}73 74bool pdsc_is_fw_good(struct pdsc *pdsc)75{76	bool fw_running = pdsc_is_fw_running(pdsc);77	u8 gen;78 79	/* Make sure to update the cached fw_status by calling80	 * pdsc_is_fw_running() before getting the generation81	 */82	gen = pdsc->fw_status & PDS_CORE_FW_STS_F_GENERATION;83 84	return fw_running && gen == pdsc->fw_generation;85}86 87static u8 pdsc_devcmd_status(struct pdsc *pdsc)88{89	return ioread8(&pdsc->cmd_regs->comp.status);90}91 92static bool pdsc_devcmd_done(struct pdsc *pdsc)93{94	return ioread32(&pdsc->cmd_regs->done) & PDS_CORE_DEV_CMD_DONE;95}96 97static void pdsc_devcmd_dbell(struct pdsc *pdsc)98{99	iowrite32(0, &pdsc->cmd_regs->done);100	iowrite32(1, &pdsc->cmd_regs->doorbell);101}102 103static void pdsc_devcmd_clean(struct pdsc *pdsc)104{105	iowrite32(0, &pdsc->cmd_regs->doorbell);106	memset_io(&pdsc->cmd_regs->cmd, 0, sizeof(pdsc->cmd_regs->cmd));107}108 109static const char *pdsc_devcmd_str(int opcode)110{111	switch (opcode) {112	case PDS_CORE_CMD_NOP:113		return "PDS_CORE_CMD_NOP";114	case PDS_CORE_CMD_IDENTIFY:115		return "PDS_CORE_CMD_IDENTIFY";116	case PDS_CORE_CMD_RESET:117		return "PDS_CORE_CMD_RESET";118	case PDS_CORE_CMD_INIT:119		return "PDS_CORE_CMD_INIT";120	case PDS_CORE_CMD_FW_DOWNLOAD:121		return "PDS_CORE_CMD_FW_DOWNLOAD";122	case PDS_CORE_CMD_FW_CONTROL:123		return "PDS_CORE_CMD_FW_CONTROL";124	default:125		return "PDS_CORE_CMD_UNKNOWN";126	}127}128 129static int pdsc_devcmd_wait(struct pdsc *pdsc, u8 opcode, int max_seconds)130{131	struct device *dev = pdsc->dev;132	unsigned long start_time;133	unsigned long max_wait;134	unsigned long duration;135	int timeout = 0;136	bool running;137	int done = 0;138	int err = 0;139	int status;140 141	start_time = jiffies;142	max_wait = start_time + (max_seconds * HZ);143 144	while (!done && !timeout) {145		running = pdsc_is_fw_running(pdsc);146		if (!running)147			break;148 149		done = pdsc_devcmd_done(pdsc);150		if (done)151			break;152 153		timeout = time_after(jiffies, max_wait);154		if (timeout)155			break;156 157		usleep_range(100, 200);158	}159	duration = jiffies - start_time;160 161	if (done && duration > HZ)162		dev_dbg(dev, "DEVCMD %d %s after %ld secs\n",163			opcode, pdsc_devcmd_str(opcode), duration / HZ);164 165	if ((!done || timeout) && running) {166		dev_err(dev, "DEVCMD %d %s timeout, done %d timeout %d max_seconds=%d\n",167			opcode, pdsc_devcmd_str(opcode), done, timeout,168			max_seconds);169		err = -ETIMEDOUT;170		pdsc_devcmd_clean(pdsc);171	}172 173	status = pdsc_devcmd_status(pdsc);174	err = pdsc_err_to_errno(status);175	if (err && err != -EAGAIN)176		dev_err(dev, "DEVCMD %d %s failed, status=%d err %d %pe\n",177			opcode, pdsc_devcmd_str(opcode), status, err,178			ERR_PTR(err));179 180	return err;181}182 183int pdsc_devcmd_locked(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,184		       union pds_core_dev_comp *comp, int max_seconds)185{186	int err;187 188	if (!pdsc->cmd_regs)189		return -ENXIO;190 191	memcpy_toio(&pdsc->cmd_regs->cmd, cmd, sizeof(*cmd));192	pdsc_devcmd_dbell(pdsc);193	err = pdsc_devcmd_wait(pdsc, cmd->opcode, max_seconds);194 195	if ((err == -ENXIO || err == -ETIMEDOUT) && pdsc->wq)196		queue_work(pdsc->wq, &pdsc->health_work);197	else198		memcpy_fromio(comp, &pdsc->cmd_regs->comp, sizeof(*comp));199 200	return err;201}202 203int pdsc_devcmd(struct pdsc *pdsc, union pds_core_dev_cmd *cmd,204		union pds_core_dev_comp *comp, int max_seconds)205{206	int err;207 208	mutex_lock(&pdsc->devcmd_lock);209	err = pdsc_devcmd_locked(pdsc, cmd, comp, max_seconds);210	mutex_unlock(&pdsc->devcmd_lock);211 212	return err;213}214 215int pdsc_devcmd_init(struct pdsc *pdsc)216{217	union pds_core_dev_comp comp = {};218	union pds_core_dev_cmd cmd = {219		.opcode = PDS_CORE_CMD_INIT,220	};221 222	return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);223}224 225int pdsc_devcmd_reset(struct pdsc *pdsc)226{227	union pds_core_dev_comp comp = {};228	union pds_core_dev_cmd cmd = {229		.reset.opcode = PDS_CORE_CMD_RESET,230	};231 232	if (!pdsc_is_fw_running(pdsc))233		return 0;234 235	return pdsc_devcmd(pdsc, &cmd, &comp, pdsc->devcmd_timeout);236}237 238static int pdsc_devcmd_identify_locked(struct pdsc *pdsc)239{240	union pds_core_dev_comp comp = {};241	union pds_core_dev_cmd cmd = {242		.identify.opcode = PDS_CORE_CMD_IDENTIFY,243		.identify.ver = PDS_CORE_IDENTITY_VERSION_1,244	};245 246	return pdsc_devcmd_locked(pdsc, &cmd, &comp, pdsc->devcmd_timeout);247}248 249static void pdsc_init_devinfo(struct pdsc *pdsc)250{251	pdsc->dev_info.asic_type = ioread8(&pdsc->info_regs->asic_type);252	pdsc->dev_info.asic_rev = ioread8(&pdsc->info_regs->asic_rev);253	pdsc->fw_generation = PDS_CORE_FW_STS_F_GENERATION &254			      ioread8(&pdsc->info_regs->fw_status);255 256	memcpy_fromio(pdsc->dev_info.fw_version,257		      pdsc->info_regs->fw_version,258		      PDS_CORE_DEVINFO_FWVERS_BUFLEN);259	pdsc->dev_info.fw_version[PDS_CORE_DEVINFO_FWVERS_BUFLEN] = 0;260 261	memcpy_fromio(pdsc->dev_info.serial_num,262		      pdsc->info_regs->serial_num,263		      PDS_CORE_DEVINFO_SERIAL_BUFLEN);264	pdsc->dev_info.serial_num[PDS_CORE_DEVINFO_SERIAL_BUFLEN] = 0;265 266	dev_dbg(pdsc->dev, "fw_version %s\n", pdsc->dev_info.fw_version);267}268 269static int pdsc_identify(struct pdsc *pdsc)270{271	struct pds_core_drv_identity drv = {};272	size_t sz;273	int err;274	int n;275 276	drv.drv_type = cpu_to_le32(PDS_DRIVER_LINUX);277	/* Catching the return quiets a Wformat-truncation complaint */278	n = snprintf(drv.driver_ver_str, sizeof(drv.driver_ver_str),279		     "%s %s", PDS_CORE_DRV_NAME, utsname()->release);280	if (n > sizeof(drv.driver_ver_str))281		dev_dbg(pdsc->dev, "release name truncated, don't care\n");282 283	/* Next let's get some info about the device284	 * We use the devcmd_lock at this level in order to285	 * get safe access to the cmd_regs->data before anyone286	 * else can mess it up287	 */288	mutex_lock(&pdsc->devcmd_lock);289 290	sz = min_t(size_t, sizeof(drv), sizeof(pdsc->cmd_regs->data));291	memcpy_toio(&pdsc->cmd_regs->data, &drv, sz);292 293	err = pdsc_devcmd_identify_locked(pdsc);294	if (!err) {295		sz = min_t(size_t, sizeof(pdsc->dev_ident),296			   sizeof(pdsc->cmd_regs->data));297		memcpy_fromio(&pdsc->dev_ident, &pdsc->cmd_regs->data, sz);298	}299	mutex_unlock(&pdsc->devcmd_lock);300 301	if (err) {302		dev_err(pdsc->dev, "Cannot identify device: %pe\n",303			ERR_PTR(err));304		return err;305	}306 307	if (isprint(pdsc->dev_info.fw_version[0]) &&308	    isascii(pdsc->dev_info.fw_version[0]))309		dev_info(pdsc->dev, "FW: %.*s\n",310			 (int)(sizeof(pdsc->dev_info.fw_version) - 1),311			 pdsc->dev_info.fw_version);312	else313		dev_info(pdsc->dev, "FW: (invalid string) 0x%02x 0x%02x 0x%02x 0x%02x ...\n",314			 (u8)pdsc->dev_info.fw_version[0],315			 (u8)pdsc->dev_info.fw_version[1],316			 (u8)pdsc->dev_info.fw_version[2],317			 (u8)pdsc->dev_info.fw_version[3]);318 319	return 0;320}321 322void pdsc_dev_uninit(struct pdsc *pdsc)323{324	if (pdsc->intr_info) {325		int i;326 327		for (i = 0; i < pdsc->nintrs; i++)328			pdsc_intr_free(pdsc, i);329 330		kfree(pdsc->intr_info);331		pdsc->intr_info = NULL;332		pdsc->nintrs = 0;333	}334 335	pci_free_irq_vectors(pdsc->pdev);336}337 338int pdsc_dev_init(struct pdsc *pdsc)339{340	unsigned int nintrs;341	int err;342 343	/* Initial init and reset of device */344	pdsc_init_devinfo(pdsc);345	pdsc->devcmd_timeout = PDS_CORE_DEVCMD_TIMEOUT;346 347	err = pdsc_devcmd_reset(pdsc);348	if (err)349		return err;350 351	err = pdsc_identify(pdsc);352	if (err)353		return err;354 355	pdsc_debugfs_add_ident(pdsc);356 357	/* Now we can reserve interrupts */358	nintrs = le32_to_cpu(pdsc->dev_ident.nintrs);359	nintrs = min_t(unsigned int, num_online_cpus(), nintrs);360 361	/* Get intr_info struct array for tracking */362	pdsc->intr_info = kcalloc(nintrs, sizeof(*pdsc->intr_info), GFP_KERNEL);363	if (!pdsc->intr_info)364		return -ENOMEM;365 366	err = pci_alloc_irq_vectors(pdsc->pdev, nintrs, nintrs, PCI_IRQ_MSIX);367	if (err != nintrs) {368		dev_err(pdsc->dev, "Can't get %d intrs from OS: %pe\n",369			nintrs, ERR_PTR(err));370		err = -ENOSPC;371		goto err_out;372	}373	pdsc->nintrs = nintrs;374 375	return 0;376 377err_out:378	kfree(pdsc->intr_info);379	pdsc->intr_info = NULL;380 381	return err;382}383