18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Copyright(c) 2017 - 2019 Pensando Systems, Inc */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/printk.h> 58c2ecf20Sopenharmony_ci#include <linux/dynamic_debug.h> 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 88c2ecf20Sopenharmony_ci#include <linux/utsname.h> 98c2ecf20Sopenharmony_ci#include <generated/utsrelease.h> 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "ionic.h" 128c2ecf20Sopenharmony_ci#include "ionic_bus.h" 138c2ecf20Sopenharmony_ci#include "ionic_lif.h" 148c2ecf20Sopenharmony_ci#include "ionic_debugfs.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(IONIC_DRV_DESCRIPTION); 178c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pensando Systems, Inc"); 188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic const char *ionic_error_to_str(enum ionic_status_code code) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci switch (code) { 238c2ecf20Sopenharmony_ci case IONIC_RC_SUCCESS: 248c2ecf20Sopenharmony_ci return "IONIC_RC_SUCCESS"; 258c2ecf20Sopenharmony_ci case IONIC_RC_EVERSION: 268c2ecf20Sopenharmony_ci return "IONIC_RC_EVERSION"; 278c2ecf20Sopenharmony_ci case IONIC_RC_EOPCODE: 288c2ecf20Sopenharmony_ci return "IONIC_RC_EOPCODE"; 298c2ecf20Sopenharmony_ci case IONIC_RC_EIO: 308c2ecf20Sopenharmony_ci return "IONIC_RC_EIO"; 318c2ecf20Sopenharmony_ci case IONIC_RC_EPERM: 328c2ecf20Sopenharmony_ci return "IONIC_RC_EPERM"; 338c2ecf20Sopenharmony_ci case IONIC_RC_EQID: 348c2ecf20Sopenharmony_ci return "IONIC_RC_EQID"; 358c2ecf20Sopenharmony_ci case IONIC_RC_EQTYPE: 368c2ecf20Sopenharmony_ci return "IONIC_RC_EQTYPE"; 378c2ecf20Sopenharmony_ci case IONIC_RC_ENOENT: 388c2ecf20Sopenharmony_ci return "IONIC_RC_ENOENT"; 398c2ecf20Sopenharmony_ci case IONIC_RC_EINTR: 408c2ecf20Sopenharmony_ci return "IONIC_RC_EINTR"; 418c2ecf20Sopenharmony_ci case IONIC_RC_EAGAIN: 428c2ecf20Sopenharmony_ci return "IONIC_RC_EAGAIN"; 438c2ecf20Sopenharmony_ci case IONIC_RC_ENOMEM: 448c2ecf20Sopenharmony_ci return "IONIC_RC_ENOMEM"; 458c2ecf20Sopenharmony_ci case IONIC_RC_EFAULT: 468c2ecf20Sopenharmony_ci return "IONIC_RC_EFAULT"; 478c2ecf20Sopenharmony_ci case IONIC_RC_EBUSY: 488c2ecf20Sopenharmony_ci return "IONIC_RC_EBUSY"; 498c2ecf20Sopenharmony_ci case IONIC_RC_EEXIST: 508c2ecf20Sopenharmony_ci return "IONIC_RC_EEXIST"; 518c2ecf20Sopenharmony_ci case IONIC_RC_EINVAL: 528c2ecf20Sopenharmony_ci return "IONIC_RC_EINVAL"; 538c2ecf20Sopenharmony_ci case IONIC_RC_ENOSPC: 548c2ecf20Sopenharmony_ci return "IONIC_RC_ENOSPC"; 558c2ecf20Sopenharmony_ci case IONIC_RC_ERANGE: 568c2ecf20Sopenharmony_ci return "IONIC_RC_ERANGE"; 578c2ecf20Sopenharmony_ci case IONIC_RC_BAD_ADDR: 588c2ecf20Sopenharmony_ci return "IONIC_RC_BAD_ADDR"; 598c2ecf20Sopenharmony_ci case IONIC_RC_DEV_CMD: 608c2ecf20Sopenharmony_ci return "IONIC_RC_DEV_CMD"; 618c2ecf20Sopenharmony_ci case IONIC_RC_ENOSUPP: 628c2ecf20Sopenharmony_ci return "IONIC_RC_ENOSUPP"; 638c2ecf20Sopenharmony_ci case IONIC_RC_ERROR: 648c2ecf20Sopenharmony_ci return "IONIC_RC_ERROR"; 658c2ecf20Sopenharmony_ci case IONIC_RC_ERDMA: 668c2ecf20Sopenharmony_ci return "IONIC_RC_ERDMA"; 678c2ecf20Sopenharmony_ci case IONIC_RC_EBAD_FW: 688c2ecf20Sopenharmony_ci return "IONIC_RC_EBAD_FW"; 698c2ecf20Sopenharmony_ci default: 708c2ecf20Sopenharmony_ci return "IONIC_RC_UNKNOWN"; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int ionic_error_to_errno(enum ionic_status_code code) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci switch (code) { 778c2ecf20Sopenharmony_ci case IONIC_RC_SUCCESS: 788c2ecf20Sopenharmony_ci return 0; 798c2ecf20Sopenharmony_ci case IONIC_RC_EVERSION: 808c2ecf20Sopenharmony_ci case IONIC_RC_EQTYPE: 818c2ecf20Sopenharmony_ci case IONIC_RC_EQID: 828c2ecf20Sopenharmony_ci case IONIC_RC_EINVAL: 838c2ecf20Sopenharmony_ci case IONIC_RC_ENOSUPP: 848c2ecf20Sopenharmony_ci return -EINVAL; 858c2ecf20Sopenharmony_ci case IONIC_RC_EPERM: 868c2ecf20Sopenharmony_ci return -EPERM; 878c2ecf20Sopenharmony_ci case IONIC_RC_ENOENT: 888c2ecf20Sopenharmony_ci return -ENOENT; 898c2ecf20Sopenharmony_ci case IONIC_RC_EAGAIN: 908c2ecf20Sopenharmony_ci return -EAGAIN; 918c2ecf20Sopenharmony_ci case IONIC_RC_ENOMEM: 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci case IONIC_RC_EFAULT: 948c2ecf20Sopenharmony_ci return -EFAULT; 958c2ecf20Sopenharmony_ci case IONIC_RC_EBUSY: 968c2ecf20Sopenharmony_ci return -EBUSY; 978c2ecf20Sopenharmony_ci case IONIC_RC_EEXIST: 988c2ecf20Sopenharmony_ci return -EEXIST; 998c2ecf20Sopenharmony_ci case IONIC_RC_ENOSPC: 1008c2ecf20Sopenharmony_ci return -ENOSPC; 1018c2ecf20Sopenharmony_ci case IONIC_RC_ERANGE: 1028c2ecf20Sopenharmony_ci return -ERANGE; 1038c2ecf20Sopenharmony_ci case IONIC_RC_BAD_ADDR: 1048c2ecf20Sopenharmony_ci return -EFAULT; 1058c2ecf20Sopenharmony_ci case IONIC_RC_EOPCODE: 1068c2ecf20Sopenharmony_ci case IONIC_RC_EINTR: 1078c2ecf20Sopenharmony_ci case IONIC_RC_DEV_CMD: 1088c2ecf20Sopenharmony_ci case IONIC_RC_ERROR: 1098c2ecf20Sopenharmony_ci case IONIC_RC_ERDMA: 1108c2ecf20Sopenharmony_ci case IONIC_RC_EIO: 1118c2ecf20Sopenharmony_ci default: 1128c2ecf20Sopenharmony_ci return -EIO; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistatic const char *ionic_opcode_to_str(enum ionic_cmd_opcode opcode) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci switch (opcode) { 1198c2ecf20Sopenharmony_ci case IONIC_CMD_NOP: 1208c2ecf20Sopenharmony_ci return "IONIC_CMD_NOP"; 1218c2ecf20Sopenharmony_ci case IONIC_CMD_INIT: 1228c2ecf20Sopenharmony_ci return "IONIC_CMD_INIT"; 1238c2ecf20Sopenharmony_ci case IONIC_CMD_RESET: 1248c2ecf20Sopenharmony_ci return "IONIC_CMD_RESET"; 1258c2ecf20Sopenharmony_ci case IONIC_CMD_IDENTIFY: 1268c2ecf20Sopenharmony_ci return "IONIC_CMD_IDENTIFY"; 1278c2ecf20Sopenharmony_ci case IONIC_CMD_GETATTR: 1288c2ecf20Sopenharmony_ci return "IONIC_CMD_GETATTR"; 1298c2ecf20Sopenharmony_ci case IONIC_CMD_SETATTR: 1308c2ecf20Sopenharmony_ci return "IONIC_CMD_SETATTR"; 1318c2ecf20Sopenharmony_ci case IONIC_CMD_PORT_IDENTIFY: 1328c2ecf20Sopenharmony_ci return "IONIC_CMD_PORT_IDENTIFY"; 1338c2ecf20Sopenharmony_ci case IONIC_CMD_PORT_INIT: 1348c2ecf20Sopenharmony_ci return "IONIC_CMD_PORT_INIT"; 1358c2ecf20Sopenharmony_ci case IONIC_CMD_PORT_RESET: 1368c2ecf20Sopenharmony_ci return "IONIC_CMD_PORT_RESET"; 1378c2ecf20Sopenharmony_ci case IONIC_CMD_PORT_GETATTR: 1388c2ecf20Sopenharmony_ci return "IONIC_CMD_PORT_GETATTR"; 1398c2ecf20Sopenharmony_ci case IONIC_CMD_PORT_SETATTR: 1408c2ecf20Sopenharmony_ci return "IONIC_CMD_PORT_SETATTR"; 1418c2ecf20Sopenharmony_ci case IONIC_CMD_LIF_INIT: 1428c2ecf20Sopenharmony_ci return "IONIC_CMD_LIF_INIT"; 1438c2ecf20Sopenharmony_ci case IONIC_CMD_LIF_RESET: 1448c2ecf20Sopenharmony_ci return "IONIC_CMD_LIF_RESET"; 1458c2ecf20Sopenharmony_ci case IONIC_CMD_LIF_IDENTIFY: 1468c2ecf20Sopenharmony_ci return "IONIC_CMD_LIF_IDENTIFY"; 1478c2ecf20Sopenharmony_ci case IONIC_CMD_LIF_SETATTR: 1488c2ecf20Sopenharmony_ci return "IONIC_CMD_LIF_SETATTR"; 1498c2ecf20Sopenharmony_ci case IONIC_CMD_LIF_GETATTR: 1508c2ecf20Sopenharmony_ci return "IONIC_CMD_LIF_GETATTR"; 1518c2ecf20Sopenharmony_ci case IONIC_CMD_RX_MODE_SET: 1528c2ecf20Sopenharmony_ci return "IONIC_CMD_RX_MODE_SET"; 1538c2ecf20Sopenharmony_ci case IONIC_CMD_RX_FILTER_ADD: 1548c2ecf20Sopenharmony_ci return "IONIC_CMD_RX_FILTER_ADD"; 1558c2ecf20Sopenharmony_ci case IONIC_CMD_RX_FILTER_DEL: 1568c2ecf20Sopenharmony_ci return "IONIC_CMD_RX_FILTER_DEL"; 1578c2ecf20Sopenharmony_ci case IONIC_CMD_Q_IDENTIFY: 1588c2ecf20Sopenharmony_ci return "IONIC_CMD_Q_IDENTIFY"; 1598c2ecf20Sopenharmony_ci case IONIC_CMD_Q_INIT: 1608c2ecf20Sopenharmony_ci return "IONIC_CMD_Q_INIT"; 1618c2ecf20Sopenharmony_ci case IONIC_CMD_Q_CONTROL: 1628c2ecf20Sopenharmony_ci return "IONIC_CMD_Q_CONTROL"; 1638c2ecf20Sopenharmony_ci case IONIC_CMD_RDMA_RESET_LIF: 1648c2ecf20Sopenharmony_ci return "IONIC_CMD_RDMA_RESET_LIF"; 1658c2ecf20Sopenharmony_ci case IONIC_CMD_RDMA_CREATE_EQ: 1668c2ecf20Sopenharmony_ci return "IONIC_CMD_RDMA_CREATE_EQ"; 1678c2ecf20Sopenharmony_ci case IONIC_CMD_RDMA_CREATE_CQ: 1688c2ecf20Sopenharmony_ci return "IONIC_CMD_RDMA_CREATE_CQ"; 1698c2ecf20Sopenharmony_ci case IONIC_CMD_RDMA_CREATE_ADMINQ: 1708c2ecf20Sopenharmony_ci return "IONIC_CMD_RDMA_CREATE_ADMINQ"; 1718c2ecf20Sopenharmony_ci case IONIC_CMD_FW_DOWNLOAD: 1728c2ecf20Sopenharmony_ci return "IONIC_CMD_FW_DOWNLOAD"; 1738c2ecf20Sopenharmony_ci case IONIC_CMD_FW_CONTROL: 1748c2ecf20Sopenharmony_ci return "IONIC_CMD_FW_CONTROL"; 1758c2ecf20Sopenharmony_ci case IONIC_CMD_FW_DOWNLOAD_V1: 1768c2ecf20Sopenharmony_ci return "IONIC_CMD_FW_DOWNLOAD_V1"; 1778c2ecf20Sopenharmony_ci case IONIC_CMD_FW_CONTROL_V1: 1788c2ecf20Sopenharmony_ci return "IONIC_CMD_FW_CONTROL_V1"; 1798c2ecf20Sopenharmony_ci case IONIC_CMD_VF_GETATTR: 1808c2ecf20Sopenharmony_ci return "IONIC_CMD_VF_GETATTR"; 1818c2ecf20Sopenharmony_ci case IONIC_CMD_VF_SETATTR: 1828c2ecf20Sopenharmony_ci return "IONIC_CMD_VF_SETATTR"; 1838c2ecf20Sopenharmony_ci default: 1848c2ecf20Sopenharmony_ci return "DEVCMD_UNKNOWN"; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void ionic_adminq_flush(struct ionic_lif *lif) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct ionic_queue *q = &lif->adminqcq->q; 1918c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci spin_lock(&lif->adminq_lock); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci while (q->tail_idx != q->head_idx) { 1968c2ecf20Sopenharmony_ci desc_info = &q->info[q->tail_idx]; 1978c2ecf20Sopenharmony_ci memset(desc_info->desc, 0, sizeof(union ionic_adminq_cmd)); 1988c2ecf20Sopenharmony_ci desc_info->cb = NULL; 1998c2ecf20Sopenharmony_ci desc_info->cb_arg = NULL; 2008c2ecf20Sopenharmony_ci q->tail_idx = (q->tail_idx + 1) & (q->num_descs - 1); 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci spin_unlock(&lif->adminq_lock); 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic int ionic_adminq_check_err(struct ionic_lif *lif, 2068c2ecf20Sopenharmony_ci struct ionic_admin_ctx *ctx, 2078c2ecf20Sopenharmony_ci bool timeout) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci struct net_device *netdev = lif->netdev; 2108c2ecf20Sopenharmony_ci const char *opcode_str; 2118c2ecf20Sopenharmony_ci const char *status_str; 2128c2ecf20Sopenharmony_ci int err = 0; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (ctx->comp.comp.status || timeout) { 2158c2ecf20Sopenharmony_ci opcode_str = ionic_opcode_to_str(ctx->cmd.cmd.opcode); 2168c2ecf20Sopenharmony_ci status_str = ionic_error_to_str(ctx->comp.comp.status); 2178c2ecf20Sopenharmony_ci err = timeout ? -ETIMEDOUT : 2188c2ecf20Sopenharmony_ci ionic_error_to_errno(ctx->comp.comp.status); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci netdev_err(netdev, "%s (%d) failed: %s (%d)\n", 2218c2ecf20Sopenharmony_ci opcode_str, ctx->cmd.cmd.opcode, 2228c2ecf20Sopenharmony_ci timeout ? "TIMEOUT" : status_str, err); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci if (timeout) 2258c2ecf20Sopenharmony_ci ionic_adminq_flush(lif); 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return err; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic void ionic_adminq_cb(struct ionic_queue *q, 2328c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info, 2338c2ecf20Sopenharmony_ci struct ionic_cq_info *cq_info, void *cb_arg) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct ionic_admin_ctx *ctx = cb_arg; 2368c2ecf20Sopenharmony_ci struct ionic_admin_comp *comp; 2378c2ecf20Sopenharmony_ci struct device *dev; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (!ctx) 2408c2ecf20Sopenharmony_ci return; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci comp = cq_info->cq_desc; 2438c2ecf20Sopenharmony_ci dev = &q->lif->netdev->dev; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci memcpy(&ctx->comp, comp, sizeof(*comp)); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci dev_dbg(dev, "comp admin queue command:\n"); 2488c2ecf20Sopenharmony_ci dynamic_hex_dump("comp ", DUMP_PREFIX_OFFSET, 16, 1, 2498c2ecf20Sopenharmony_ci &ctx->comp, sizeof(ctx->comp), true); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci complete_all(&ctx->work); 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int ionic_adminq_post(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) 2558c2ecf20Sopenharmony_ci{ 2568c2ecf20Sopenharmony_ci struct ionic_desc_info *desc_info; 2578c2ecf20Sopenharmony_ci struct ionic_queue *q; 2588c2ecf20Sopenharmony_ci int err = 0; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (!lif->adminqcq) 2618c2ecf20Sopenharmony_ci return -EIO; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci q = &lif->adminqcq->q; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci spin_lock(&lif->adminq_lock); 2668c2ecf20Sopenharmony_ci if (!ionic_q_has_space(q, 1)) { 2678c2ecf20Sopenharmony_ci err = -ENOSPC; 2688c2ecf20Sopenharmony_ci goto err_out; 2698c2ecf20Sopenharmony_ci } 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci err = ionic_heartbeat_check(lif->ionic); 2728c2ecf20Sopenharmony_ci if (err) 2738c2ecf20Sopenharmony_ci goto err_out; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci desc_info = &q->info[q->head_idx]; 2768c2ecf20Sopenharmony_ci memcpy(desc_info->desc, &ctx->cmd, sizeof(ctx->cmd)); 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci dev_dbg(&lif->netdev->dev, "post admin queue command:\n"); 2798c2ecf20Sopenharmony_ci dynamic_hex_dump("cmd ", DUMP_PREFIX_OFFSET, 16, 1, 2808c2ecf20Sopenharmony_ci &ctx->cmd, sizeof(ctx->cmd), true); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci ionic_q_post(q, true, ionic_adminq_cb, ctx); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_cierr_out: 2858c2ecf20Sopenharmony_ci spin_unlock(&lif->adminq_lock); 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci return err; 2888c2ecf20Sopenharmony_ci} 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ciint ionic_adminq_post_wait(struct ionic_lif *lif, struct ionic_admin_ctx *ctx) 2918c2ecf20Sopenharmony_ci{ 2928c2ecf20Sopenharmony_ci struct net_device *netdev = lif->netdev; 2938c2ecf20Sopenharmony_ci unsigned long remaining; 2948c2ecf20Sopenharmony_ci const char *name; 2958c2ecf20Sopenharmony_ci int err; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci err = ionic_adminq_post(lif, ctx); 2988c2ecf20Sopenharmony_ci if (err) { 2998c2ecf20Sopenharmony_ci if (!test_bit(IONIC_LIF_F_FW_RESET, lif->state)) { 3008c2ecf20Sopenharmony_ci name = ionic_opcode_to_str(ctx->cmd.cmd.opcode); 3018c2ecf20Sopenharmony_ci netdev_err(netdev, "Posting of %s (%d) failed: %d\n", 3028c2ecf20Sopenharmony_ci name, ctx->cmd.cmd.opcode, err); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci return err; 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci remaining = wait_for_completion_timeout(&ctx->work, 3088c2ecf20Sopenharmony_ci HZ * (ulong)DEVCMD_TIMEOUT); 3098c2ecf20Sopenharmony_ci return ionic_adminq_check_err(lif, ctx, (remaining == 0)); 3108c2ecf20Sopenharmony_ci} 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_cistatic void ionic_dev_cmd_clean(struct ionic *ionic) 3138c2ecf20Sopenharmony_ci{ 3148c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci iowrite32(0, &idev->dev_cmd_regs->doorbell); 3178c2ecf20Sopenharmony_ci memset_io(&idev->dev_cmd_regs->cmd, 0, sizeof(idev->dev_cmd_regs->cmd)); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ciint ionic_dev_cmd_wait(struct ionic *ionic, unsigned long max_seconds) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 3238c2ecf20Sopenharmony_ci unsigned long start_time; 3248c2ecf20Sopenharmony_ci unsigned long max_wait; 3258c2ecf20Sopenharmony_ci unsigned long duration; 3268c2ecf20Sopenharmony_ci int opcode; 3278c2ecf20Sopenharmony_ci int hb = 0; 3288c2ecf20Sopenharmony_ci int done; 3298c2ecf20Sopenharmony_ci int err; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* Wait for dev cmd to complete, retrying if we get EAGAIN, 3328c2ecf20Sopenharmony_ci * but don't wait any longer than max_seconds. 3338c2ecf20Sopenharmony_ci */ 3348c2ecf20Sopenharmony_ci max_wait = jiffies + (max_seconds * HZ); 3358c2ecf20Sopenharmony_citry_again: 3368c2ecf20Sopenharmony_ci opcode = idev->opcode; 3378c2ecf20Sopenharmony_ci start_time = jiffies; 3388c2ecf20Sopenharmony_ci do { 3398c2ecf20Sopenharmony_ci done = ionic_dev_cmd_done(idev); 3408c2ecf20Sopenharmony_ci if (done) 3418c2ecf20Sopenharmony_ci break; 3428c2ecf20Sopenharmony_ci usleep_range(100, 200); 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci /* Don't check the heartbeat on FW_CONTROL commands as they are 3458c2ecf20Sopenharmony_ci * notorious for interrupting the firmware's heartbeat update. 3468c2ecf20Sopenharmony_ci */ 3478c2ecf20Sopenharmony_ci if (opcode != IONIC_CMD_FW_CONTROL) 3488c2ecf20Sopenharmony_ci hb = ionic_heartbeat_check(ionic); 3498c2ecf20Sopenharmony_ci } while (!done && !hb && time_before(jiffies, max_wait)); 3508c2ecf20Sopenharmony_ci duration = jiffies - start_time; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci dev_dbg(ionic->dev, "DEVCMD %s (%d) done=%d took %ld secs (%ld jiffies)\n", 3538c2ecf20Sopenharmony_ci ionic_opcode_to_str(opcode), opcode, 3548c2ecf20Sopenharmony_ci done, duration / HZ, duration); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci if (!done && hb) { 3578c2ecf20Sopenharmony_ci /* It is possible (but unlikely) that FW was busy and missed a 3588c2ecf20Sopenharmony_ci * heartbeat check but is still alive and will process this 3598c2ecf20Sopenharmony_ci * request, so don't clean the dev_cmd in this case. 3608c2ecf20Sopenharmony_ci */ 3618c2ecf20Sopenharmony_ci dev_warn(ionic->dev, "DEVCMD %s (%d) failed - FW halted\n", 3628c2ecf20Sopenharmony_ci ionic_opcode_to_str(opcode), opcode); 3638c2ecf20Sopenharmony_ci return -ENXIO; 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci if (!done && !time_before(jiffies, max_wait)) { 3678c2ecf20Sopenharmony_ci ionic_dev_cmd_clean(ionic); 3688c2ecf20Sopenharmony_ci dev_warn(ionic->dev, "DEVCMD %s (%d) timeout after %ld secs\n", 3698c2ecf20Sopenharmony_ci ionic_opcode_to_str(opcode), opcode, max_seconds); 3708c2ecf20Sopenharmony_ci return -ETIMEDOUT; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci err = ionic_dev_cmd_status(&ionic->idev); 3748c2ecf20Sopenharmony_ci if (err) { 3758c2ecf20Sopenharmony_ci if (err == IONIC_RC_EAGAIN && 3768c2ecf20Sopenharmony_ci time_before(jiffies, (max_wait - HZ))) { 3778c2ecf20Sopenharmony_ci dev_dbg(ionic->dev, "DEV_CMD %s (%d), %s (%d) retrying...\n", 3788c2ecf20Sopenharmony_ci ionic_opcode_to_str(opcode), opcode, 3798c2ecf20Sopenharmony_ci ionic_error_to_str(err), err); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci iowrite32(0, &idev->dev_cmd_regs->done); 3828c2ecf20Sopenharmony_ci msleep(1000); 3838c2ecf20Sopenharmony_ci iowrite32(1, &idev->dev_cmd_regs->doorbell); 3848c2ecf20Sopenharmony_ci goto try_again; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!(opcode == IONIC_CMD_FW_CONTROL && err == IONIC_RC_EAGAIN)) 3888c2ecf20Sopenharmony_ci dev_err(ionic->dev, "DEV_CMD %s (%d) error, %s (%d) failed\n", 3898c2ecf20Sopenharmony_ci ionic_opcode_to_str(opcode), opcode, 3908c2ecf20Sopenharmony_ci ionic_error_to_str(err), err); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return ionic_error_to_errno(err); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci ionic_dev_cmd_clean(ionic); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciint ionic_setup(struct ionic *ionic) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci int err; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci err = ionic_dev_setup(ionic); 4058c2ecf20Sopenharmony_ci if (err) 4068c2ecf20Sopenharmony_ci return err; 4078c2ecf20Sopenharmony_ci ionic_reset(ionic); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci return 0; 4108c2ecf20Sopenharmony_ci} 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ciint ionic_identify(struct ionic *ionic) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct ionic_identity *ident = &ionic->ident; 4158c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 4168c2ecf20Sopenharmony_ci size_t sz; 4178c2ecf20Sopenharmony_ci int err; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci memset(ident, 0, sizeof(*ident)); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ident->drv.os_type = cpu_to_le32(IONIC_OS_TYPE_LINUX); 4228c2ecf20Sopenharmony_ci strncpy(ident->drv.driver_ver_str, UTS_RELEASE, 4238c2ecf20Sopenharmony_ci sizeof(ident->drv.driver_ver_str) - 1); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci sz = min(sizeof(ident->drv), sizeof(idev->dev_cmd_regs->data)); 4288c2ecf20Sopenharmony_ci memcpy_toio(&idev->dev_cmd_regs->data, &ident->drv, sz); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci ionic_dev_cmd_identify(idev, IONIC_IDENTITY_VERSION_1); 4318c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 4328c2ecf20Sopenharmony_ci if (!err) { 4338c2ecf20Sopenharmony_ci sz = min(sizeof(ident->dev), sizeof(idev->dev_cmd_regs->data)); 4348c2ecf20Sopenharmony_ci memcpy_fromio(&ident->dev, &idev->dev_cmd_regs->data, sz); 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (err) { 4398c2ecf20Sopenharmony_ci dev_err(ionic->dev, "Cannot identify ionic: %dn", err); 4408c2ecf20Sopenharmony_ci goto err_out; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci err = ionic_lif_identify(ionic, IONIC_LIF_TYPE_CLASSIC, 4448c2ecf20Sopenharmony_ci &ionic->ident.lif); 4458c2ecf20Sopenharmony_ci if (err) { 4468c2ecf20Sopenharmony_ci dev_err(ionic->dev, "Cannot identify LIFs: %d\n", err); 4478c2ecf20Sopenharmony_ci goto err_out; 4488c2ecf20Sopenharmony_ci } 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci return 0; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_cierr_out: 4538c2ecf20Sopenharmony_ci return err; 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ciint ionic_init(struct ionic *ionic) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 4598c2ecf20Sopenharmony_ci int err; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 4628c2ecf20Sopenharmony_ci ionic_dev_cmd_init(idev); 4638c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 4648c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return err; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciint ionic_reset(struct ionic *ionic) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 4728c2ecf20Sopenharmony_ci int err; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 4758c2ecf20Sopenharmony_ci ionic_dev_cmd_reset(idev); 4768c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 4778c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return err; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ciint ionic_port_identify(struct ionic *ionic) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci struct ionic_identity *ident = &ionic->ident; 4858c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 4868c2ecf20Sopenharmony_ci size_t sz; 4878c2ecf20Sopenharmony_ci int err; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci ionic_dev_cmd_port_identify(idev); 4928c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 4938c2ecf20Sopenharmony_ci if (!err) { 4948c2ecf20Sopenharmony_ci sz = min(sizeof(ident->port), sizeof(idev->dev_cmd_regs->data)); 4958c2ecf20Sopenharmony_ci memcpy_fromio(&ident->port, &idev->dev_cmd_regs->data, sz); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return err; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ciint ionic_port_init(struct ionic *ionic) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct ionic_identity *ident = &ionic->ident; 5068c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 5078c2ecf20Sopenharmony_ci size_t sz; 5088c2ecf20Sopenharmony_ci int err; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci if (!idev->port_info) { 5118c2ecf20Sopenharmony_ci idev->port_info_sz = ALIGN(sizeof(*idev->port_info), PAGE_SIZE); 5128c2ecf20Sopenharmony_ci idev->port_info = dma_alloc_coherent(ionic->dev, 5138c2ecf20Sopenharmony_ci idev->port_info_sz, 5148c2ecf20Sopenharmony_ci &idev->port_info_pa, 5158c2ecf20Sopenharmony_ci GFP_KERNEL); 5168c2ecf20Sopenharmony_ci if (!idev->port_info) { 5178c2ecf20Sopenharmony_ci dev_err(ionic->dev, "Failed to allocate port info\n"); 5188c2ecf20Sopenharmony_ci return -ENOMEM; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci } 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci sz = min(sizeof(ident->port.config), sizeof(idev->dev_cmd_regs->data)); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci memcpy_toio(&idev->dev_cmd_regs->data, &ident->port.config, sz); 5278c2ecf20Sopenharmony_ci ionic_dev_cmd_port_init(idev); 5288c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci ionic_dev_cmd_port_state(&ionic->idev, IONIC_PORT_ADMIN_STATE_UP); 5318c2ecf20Sopenharmony_ci (void)ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 5348c2ecf20Sopenharmony_ci if (err) { 5358c2ecf20Sopenharmony_ci dev_err(ionic->dev, "Failed to init port\n"); 5368c2ecf20Sopenharmony_ci dma_free_coherent(ionic->dev, idev->port_info_sz, 5378c2ecf20Sopenharmony_ci idev->port_info, idev->port_info_pa); 5388c2ecf20Sopenharmony_ci idev->port_info = NULL; 5398c2ecf20Sopenharmony_ci idev->port_info_pa = 0; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return err; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_ciint ionic_port_reset(struct ionic *ionic) 5468c2ecf20Sopenharmony_ci{ 5478c2ecf20Sopenharmony_ci struct ionic_dev *idev = &ionic->idev; 5488c2ecf20Sopenharmony_ci int err; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci if (!idev->port_info) 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci mutex_lock(&ionic->dev_cmd_lock); 5548c2ecf20Sopenharmony_ci ionic_dev_cmd_port_reset(idev); 5558c2ecf20Sopenharmony_ci err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 5568c2ecf20Sopenharmony_ci mutex_unlock(&ionic->dev_cmd_lock); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci dma_free_coherent(ionic->dev, idev->port_info_sz, 5598c2ecf20Sopenharmony_ci idev->port_info, idev->port_info_pa); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci idev->port_info = NULL; 5628c2ecf20Sopenharmony_ci idev->port_info_pa = 0; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (err) 5658c2ecf20Sopenharmony_ci dev_err(ionic->dev, "Failed to reset port\n"); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci return err; 5688c2ecf20Sopenharmony_ci} 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic int __init ionic_init_module(void) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci int ret; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci ionic_debugfs_create(); 5758c2ecf20Sopenharmony_ci ret = ionic_bus_register_driver(); 5768c2ecf20Sopenharmony_ci if (ret) 5778c2ecf20Sopenharmony_ci ionic_debugfs_destroy(); 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci return ret; 5808c2ecf20Sopenharmony_ci} 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_cistatic void __exit ionic_cleanup_module(void) 5838c2ecf20Sopenharmony_ci{ 5848c2ecf20Sopenharmony_ci ionic_bus_unregister_driver(); 5858c2ecf20Sopenharmony_ci ionic_debugfs_destroy(); 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci pr_info("%s removed\n", IONIC_DRV_NAME); 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cimodule_init(ionic_init_module); 5918c2ecf20Sopenharmony_cimodule_exit(ionic_cleanup_module); 592