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