static int qtnf_ep_fw_load(struct qtnf_pcie_pearl_state *ps, const u8 *fw, u32 fw_size) { int blk_size = QTN_PCIE_FW_BUFSZ - sizeof(struct qtnf_pearl_fw_hdr); int blk_count = fw_size / blk_size + ((fw_size % blk_size) ? 1 : 0); const u8 *pblk = fw; int threshold = 0; int blk = 0; int len; pr_debug("FW upload started: fw_addr=0x%p size=%d\n", fw, fw_size); while (blk < blk_count) { if (++threshold > 10000) { pr_err("FW upload failed: too many retries\n"); return -ETIMEDOUT; } len = qtnf_ep_fw_send(ps->base.pdev, fw_size, blk, pblk, fw); if (len <= 0) continue; if (!((blk + 1) & QTN_PCIE_FW_DLMASK) || (blk == (blk_count - 1))) { qtnf_set_state(&ps->bda->bda_rc_state, QTN_RC_FW_SYNC); if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_SYNC, QTN_FW_DL_TIMEOUT_MS)) { pr_err("FW upload failed: SYNC timed out\n"); return -ETIMEDOUT; } qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_SYNC); if (qtnf_is_state(&ps->bda->bda_ep_state, QTN_EP_FW_RETRY)) { if (blk == (blk_count - 1)) { int last_round = blk_count & QTN_PCIE_FW_DLMASK; blk -= last_round; pblk -= ((last_round - 1) * blk_size + len); } else { blk -= QTN_PCIE_FW_DLMASK; pblk -= QTN_PCIE_FW_DLMASK * blk_size; } qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_RETRY); pr_warn("FW upload retry: block #%d\n", blk); continue; } qtnf_pearl_data_tx_reclaim(ps); } pblk += len; blk++; } pr_debug("FW upload completed: totally sent %d blocks\n", blk); return 0; }
static void qtnf_pearl_fw_work_handler(struct work_struct *work) { struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work); struct qtnf_pcie_pearl_state *ps = (void *)get_bus_priv(bus); u32 state = QTN_RC_FW_LOADRDY | QTN_RC_FW_QLINK; const char *fwname = QTN_PCI_PEARL_FW_NAME; struct pci_dev *pdev = ps->base.pdev; const struct firmware *fw; int ret; if (ps->base.flashboot) { state |= QTN_RC_FW_FLASHBOOT; } else { ret = request_firmware(&fw, fwname, &pdev->dev); if (ret < 0) { pr_err("failed to get firmware %s\n", fwname); goto fw_load_exit; } } qtnf_set_state(&ps->bda->bda_rc_state, state); if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY, QTN_FW_DL_TIMEOUT_MS)) { pr_err("card is not ready\n"); if (!ps->base.flashboot) release_firmware(fw); goto fw_load_exit; } qtnf_clear_state(&ps->bda->bda_ep_state, QTN_EP_FW_LOADRDY); if (ps->base.flashboot) { pr_info("booting firmware from flash\n"); } else { pr_info("starting firmware upload: %s\n", fwname); ret = qtnf_ep_fw_load(ps, fw->data, fw->size); release_firmware(fw); if (ret) { pr_err("firmware upload error\n"); goto fw_load_exit; } } if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_DONE, QTN_FW_DL_TIMEOUT_MS)) { pr_err("firmware bringup timed out\n"); goto fw_load_exit; } if (qtnf_poll_state(&ps->bda->bda_ep_state, QTN_EP_FW_QLINK_DONE, QTN_FW_QLINK_TIMEOUT_MS)) { pr_err("firmware runtime failure\n"); goto fw_load_exit; } pr_info("firmware is up and running\n"); ret = qtnf_pcie_fw_boot_done(bus); if (ret) goto fw_load_exit; qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats); qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats); fw_load_exit: put_device(&pdev->dev); }
static void qtnf_fw_work_handler(struct work_struct *work) { struct qtnf_bus *bus = container_of(work, struct qtnf_bus, fw_work); struct qtnf_pcie_bus_priv *priv = (void *)get_bus_priv(bus); struct pci_dev *pdev = priv->pdev; const struct firmware *fw; int ret; u32 state = QTN_RC_FW_LOADRDY | QTN_RC_FW_QLINK; if (flashboot) { state |= QTN_RC_FW_FLASHBOOT; } else { ret = request_firmware(&fw, bus->fwname, &pdev->dev); if (ret < 0) { pr_err("failed to get firmware %s\n", bus->fwname); goto fw_load_fail; } } qtnf_set_state(&priv->bda->bda_rc_state, state); if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY, QTN_FW_DL_TIMEOUT_MS)) { pr_err("card is not ready\n"); if (!flashboot) release_firmware(fw); goto fw_load_fail; } qtnf_clear_state(&priv->bda->bda_ep_state, QTN_EP_FW_LOADRDY); if (flashboot) { pr_info("booting firmware from flash\n"); } else { pr_info("starting firmware upload: %s\n", bus->fwname); ret = qtnf_ep_fw_load(priv, fw->data, fw->size); release_firmware(fw); if (ret) { pr_err("firmware upload error\n"); goto fw_load_fail; } } if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_DONE, QTN_FW_DL_TIMEOUT_MS)) { pr_err("firmware bringup timed out\n"); goto fw_load_fail; } bus->fw_state = QTNF_FW_STATE_FW_DNLD_DONE; pr_info("firmware is up and running\n"); if (qtnf_poll_state(&priv->bda->bda_ep_state, QTN_EP_FW_QLINK_DONE, QTN_FW_QLINK_TIMEOUT_MS)) { pr_err("firmware runtime failure\n"); goto fw_load_fail; } ret = qtnf_core_attach(bus); if (ret) { pr_err("failed to attach core\n"); goto fw_load_fail; } qtnf_debugfs_init(bus, DRV_NAME); qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show); qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show); qtnf_debugfs_add_entry(bus, "hdp_stats", qtnf_dbg_hdp_stats); qtnf_debugfs_add_entry(bus, "irq_stats", qtnf_dbg_irq_stats); qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats); goto fw_load_exit; fw_load_fail: bus->fw_state = QTNF_FW_STATE_DETACHED; fw_load_exit: complete(&bus->firmware_init_complete); put_device(&pdev->dev); }