|
@@ -764,6 +764,67 @@ static void term_afu(struct cxlflash_cfg *cfg)
|
|
|
pr_debug("%s: returning\n", __func__);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * notify_shutdown() - notifies device of pending shutdown
|
|
|
+ * @cfg: Internal structure associated with the host.
|
|
|
+ * @wait: Whether to wait for shutdown processing to complete.
|
|
|
+ *
|
|
|
+ * This function will notify the AFU that the adapter is being shutdown
|
|
|
+ * and will wait for shutdown processing to complete if wait is true.
|
|
|
+ * This notification should flush pending I/Os to the device and halt
|
|
|
+ * further I/Os until the next AFU reset is issued and device restarted.
|
|
|
+ */
|
|
|
+static void notify_shutdown(struct cxlflash_cfg *cfg, bool wait)
|
|
|
+{
|
|
|
+ struct afu *afu = cfg->afu;
|
|
|
+ struct device *dev = &cfg->dev->dev;
|
|
|
+ struct sisl_global_map __iomem *global = &afu->afu_map->global;
|
|
|
+ struct dev_dependent_vals *ddv;
|
|
|
+ u64 reg, status;
|
|
|
+ int i, retry_cnt = 0;
|
|
|
+
|
|
|
+ ddv = (struct dev_dependent_vals *)cfg->dev_id->driver_data;
|
|
|
+ if (!(ddv->flags & CXLFLASH_NOTIFY_SHUTDOWN))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Notify AFU */
|
|
|
+ for (i = 0; i < NUM_FC_PORTS; i++) {
|
|
|
+ reg = readq_be(&global->fc_regs[i][FC_CONFIG2 / 8]);
|
|
|
+ reg |= SISL_FC_SHUTDOWN_NORMAL;
|
|
|
+ writeq_be(reg, &global->fc_regs[i][FC_CONFIG2 / 8]);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!wait)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* Wait up to 1.5 seconds for shutdown processing to complete */
|
|
|
+ for (i = 0; i < NUM_FC_PORTS; i++) {
|
|
|
+ retry_cnt = 0;
|
|
|
+ while (true) {
|
|
|
+ status = readq_be(&global->fc_regs[i][FC_STATUS / 8]);
|
|
|
+ if (status & SISL_STATUS_SHUTDOWN_COMPLETE)
|
|
|
+ break;
|
|
|
+ if (++retry_cnt >= MC_RETRY_CNT) {
|
|
|
+ dev_dbg(dev, "%s: port %d shutdown processing "
|
|
|
+ "not yet completed\n", __func__, i);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ msleep(100 * retry_cnt);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * cxlflash_shutdown() - shutdown handler
|
|
|
+ * @pdev: PCI device associated with the host.
|
|
|
+ */
|
|
|
+static void cxlflash_shutdown(struct pci_dev *pdev)
|
|
|
+{
|
|
|
+ struct cxlflash_cfg *cfg = pci_get_drvdata(pdev);
|
|
|
+
|
|
|
+ notify_shutdown(cfg, false);
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* cxlflash_remove() - PCI entry point to tear down host
|
|
|
* @pdev: PCI device associated with the host.
|
|
@@ -785,6 +846,9 @@ static void cxlflash_remove(struct pci_dev *pdev)
|
|
|
cfg->tmf_slock);
|
|
|
spin_unlock_irqrestore(&cfg->tmf_slock, lock_flags);
|
|
|
|
|
|
+ /* Notify AFU and wait for shutdown processing to complete */
|
|
|
+ notify_shutdown(cfg, true);
|
|
|
+
|
|
|
cfg->state = STATE_FAILTERM;
|
|
|
cxlflash_stop_term_user_contexts(cfg);
|
|
|
|
|
@@ -2336,7 +2400,7 @@ static struct scsi_host_template driver_template = {
|
|
|
static struct dev_dependent_vals dev_corsa_vals = { CXLFLASH_MAX_SECTORS,
|
|
|
0ULL };
|
|
|
static struct dev_dependent_vals dev_flash_gt_vals = { CXLFLASH_MAX_SECTORS,
|
|
|
- 0ULL };
|
|
|
+ CXLFLASH_NOTIFY_SHUTDOWN };
|
|
|
|
|
|
/*
|
|
|
* PCI device binding table
|
|
@@ -2613,6 +2677,7 @@ static struct pci_driver cxlflash_driver = {
|
|
|
.id_table = cxlflash_pci_table,
|
|
|
.probe = cxlflash_probe,
|
|
|
.remove = cxlflash_remove,
|
|
|
+ .shutdown = cxlflash_shutdown,
|
|
|
.err_handler = &cxlflash_err_handler,
|
|
|
};
|
|
|
|