18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci* Filename: core.c
48c2ecf20Sopenharmony_ci*
58c2ecf20Sopenharmony_ci* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
68c2ecf20Sopenharmony_ci*	Philip Kelleher <pjk1939@linux.vnet.ibm.com>
78c2ecf20Sopenharmony_ci*
88c2ecf20Sopenharmony_ci* (C) Copyright 2013 IBM Corporation
98c2ecf20Sopenharmony_ci*/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/pci.h>
168c2ecf20Sopenharmony_ci#include <linux/reboot.h>
178c2ecf20Sopenharmony_ci#include <linux/slab.h>
188c2ecf20Sopenharmony_ci#include <linux/bitops.h>
198c2ecf20Sopenharmony_ci#include <linux/delay.h>
208c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
218c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <linux/genhd.h>
248c2ecf20Sopenharmony_ci#include <linux/idr.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include "rsxx_priv.h"
278c2ecf20Sopenharmony_ci#include "rsxx_cfg.h"
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define NO_LEGACY 0
308c2ecf20Sopenharmony_ci#define SYNC_START_TIMEOUT (10 * 60) /* 10 minutes */
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IBM Flash Adapter 900GB Full Height Device Driver");
338c2ecf20Sopenharmony_ciMODULE_AUTHOR("Joshua Morris/Philip Kelleher, IBM");
348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
358c2ecf20Sopenharmony_ciMODULE_VERSION(DRIVER_VERSION);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_cistatic unsigned int force_legacy = NO_LEGACY;
388c2ecf20Sopenharmony_cimodule_param(force_legacy, uint, 0444);
398c2ecf20Sopenharmony_ciMODULE_PARM_DESC(force_legacy, "Force the use of legacy type PCI interrupts");
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic unsigned int sync_start = 1;
428c2ecf20Sopenharmony_cimodule_param(sync_start, uint, 0444);
438c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sync_start, "On by Default: Driver load will not complete "
448c2ecf20Sopenharmony_ci			     "until the card startup has completed.");
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic DEFINE_IDA(rsxx_disk_ida);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* --------------------Debugfs Setup ------------------- */
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cistatic int rsxx_attr_pci_regs_show(struct seq_file *m, void *p)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = m->private;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	seq_printf(m, "HWID		0x%08x\n",
558c2ecf20Sopenharmony_ci					ioread32(card->regmap + HWID));
568c2ecf20Sopenharmony_ci	seq_printf(m, "SCRATCH		0x%08x\n",
578c2ecf20Sopenharmony_ci					ioread32(card->regmap + SCRATCH));
588c2ecf20Sopenharmony_ci	seq_printf(m, "IER		0x%08x\n",
598c2ecf20Sopenharmony_ci					ioread32(card->regmap + IER));
608c2ecf20Sopenharmony_ci	seq_printf(m, "IPR		0x%08x\n",
618c2ecf20Sopenharmony_ci					ioread32(card->regmap + IPR));
628c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_CMD		0x%08x\n",
638c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_CMD));
648c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_ADD		0x%08x\n",
658c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_ADD));
668c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_CNT		0x%08x\n",
678c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_CNT));
688c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_STAT	0x%08x\n",
698c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_STAT));
708c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA0	0x%08x\n",
718c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA0));
728c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA1	0x%08x\n",
738c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA1));
748c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA2	0x%08x\n",
758c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA2));
768c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA3	0x%08x\n",
778c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA3));
788c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA4	0x%08x\n",
798c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA4));
808c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA5	0x%08x\n",
818c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA5));
828c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA6	0x%08x\n",
838c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA6));
848c2ecf20Sopenharmony_ci	seq_printf(m, "CREG_DATA7	0x%08x\n",
858c2ecf20Sopenharmony_ci					ioread32(card->regmap + CREG_DATA7));
868c2ecf20Sopenharmony_ci	seq_printf(m, "INTR_COAL	0x%08x\n",
878c2ecf20Sopenharmony_ci					ioread32(card->regmap + INTR_COAL));
888c2ecf20Sopenharmony_ci	seq_printf(m, "HW_ERROR		0x%08x\n",
898c2ecf20Sopenharmony_ci					ioread32(card->regmap + HW_ERROR));
908c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG0		0x%08x\n",
918c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG0));
928c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG1		0x%08x\n",
938c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG1));
948c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG2		0x%08x\n",
958c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG2));
968c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG3		0x%08x\n",
978c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG3));
988c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG4		0x%08x\n",
998c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG4));
1008c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG5		0x%08x\n",
1018c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG5));
1028c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG6		0x%08x\n",
1038c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG6));
1048c2ecf20Sopenharmony_ci	seq_printf(m, "DEBUG7		0x%08x\n",
1058c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_DEBUG7));
1068c2ecf20Sopenharmony_ci	seq_printf(m, "RECONFIG		0x%08x\n",
1078c2ecf20Sopenharmony_ci					ioread32(card->regmap + PCI_RECONFIG));
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	return 0;
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_cistatic int rsxx_attr_stats_show(struct seq_file *m, void *p)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = m->private;
1158c2ecf20Sopenharmony_ci	int i;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
1188c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d CRC Errors	= %d\n",
1198c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.crc_errors);
1208c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Hard Errors	= %d\n",
1218c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.hard_errors);
1228c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Soft Errors	= %d\n",
1238c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.soft_errors);
1248c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Writes Issued	= %d\n",
1258c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.writes_issued);
1268c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Writes Failed	= %d\n",
1278c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.writes_failed);
1288c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Reads Issued	= %d\n",
1298c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.reads_issued);
1308c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Reads Failed	= %d\n",
1318c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.reads_failed);
1328c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Reads Retried	= %d\n",
1338c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.reads_retried);
1348c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Discards Issued	= %d\n",
1358c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.discards_issued);
1368c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d Discards Failed	= %d\n",
1378c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.discards_failed);
1388c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d DMA SW Errors	= %d\n",
1398c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.dma_sw_err);
1408c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d DMA HW Faults	= %d\n",
1418c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.dma_hw_fault);
1428c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d DMAs Cancelled	= %d\n",
1438c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.dma_cancelled);
1448c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d SW Queue Depth	= %d\n",
1458c2ecf20Sopenharmony_ci				i, card->ctrl[i].stats.sw_q_depth);
1468c2ecf20Sopenharmony_ci		seq_printf(m, "Ctrl %d HW Queue Depth	= %d\n",
1478c2ecf20Sopenharmony_ci			i, atomic_read(&card->ctrl[i].stats.hw_q_depth));
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return 0;
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic int rsxx_attr_stats_open(struct inode *inode, struct file *file)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return single_open(file, rsxx_attr_stats_show, inode->i_private);
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cistatic int rsxx_attr_pci_regs_open(struct inode *inode, struct file *file)
1598c2ecf20Sopenharmony_ci{
1608c2ecf20Sopenharmony_ci	return single_open(file, rsxx_attr_pci_regs_show, inode->i_private);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cistatic ssize_t rsxx_cram_read(struct file *fp, char __user *ubuf,
1648c2ecf20Sopenharmony_ci			      size_t cnt, loff_t *ppos)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = file_inode(fp)->i_private;
1678c2ecf20Sopenharmony_ci	char *buf;
1688c2ecf20Sopenharmony_ci	int st;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	buf = kzalloc(cnt, GFP_KERNEL);
1718c2ecf20Sopenharmony_ci	if (!buf)
1728c2ecf20Sopenharmony_ci		return -ENOMEM;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	st = rsxx_creg_read(card, CREG_ADD_CRAM + (u32)*ppos, cnt, buf, 1);
1758c2ecf20Sopenharmony_ci	if (!st) {
1768c2ecf20Sopenharmony_ci		if (copy_to_user(ubuf, buf, cnt))
1778c2ecf20Sopenharmony_ci			st = -EFAULT;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci	kfree(buf);
1808c2ecf20Sopenharmony_ci	if (st)
1818c2ecf20Sopenharmony_ci		return st;
1828c2ecf20Sopenharmony_ci	*ppos += cnt;
1838c2ecf20Sopenharmony_ci	return cnt;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic ssize_t rsxx_cram_write(struct file *fp, const char __user *ubuf,
1878c2ecf20Sopenharmony_ci			       size_t cnt, loff_t *ppos)
1888c2ecf20Sopenharmony_ci{
1898c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = file_inode(fp)->i_private;
1908c2ecf20Sopenharmony_ci	char *buf;
1918c2ecf20Sopenharmony_ci	ssize_t st;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	buf = memdup_user(ubuf, cnt);
1948c2ecf20Sopenharmony_ci	if (IS_ERR(buf))
1958c2ecf20Sopenharmony_ci		return PTR_ERR(buf);
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	st = rsxx_creg_write(card, CREG_ADD_CRAM + (u32)*ppos, cnt, buf, 1);
1988c2ecf20Sopenharmony_ci	kfree(buf);
1998c2ecf20Sopenharmony_ci	if (st)
2008c2ecf20Sopenharmony_ci		return st;
2018c2ecf20Sopenharmony_ci	*ppos += cnt;
2028c2ecf20Sopenharmony_ci	return cnt;
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_cram_fops = {
2068c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2078c2ecf20Sopenharmony_ci	.read		= rsxx_cram_read,
2088c2ecf20Sopenharmony_ci	.write		= rsxx_cram_write,
2098c2ecf20Sopenharmony_ci};
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_stats_fops = {
2128c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2138c2ecf20Sopenharmony_ci	.open		= rsxx_attr_stats_open,
2148c2ecf20Sopenharmony_ci	.read		= seq_read,
2158c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
2168c2ecf20Sopenharmony_ci	.release	= single_release,
2178c2ecf20Sopenharmony_ci};
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic const struct file_operations debugfs_pci_regs_fops = {
2208c2ecf20Sopenharmony_ci	.owner		= THIS_MODULE,
2218c2ecf20Sopenharmony_ci	.open		= rsxx_attr_pci_regs_open,
2228c2ecf20Sopenharmony_ci	.read		= seq_read,
2238c2ecf20Sopenharmony_ci	.llseek		= seq_lseek,
2248c2ecf20Sopenharmony_ci	.release	= single_release,
2258c2ecf20Sopenharmony_ci};
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cistatic void rsxx_debugfs_dev_new(struct rsxx_cardinfo *card)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct dentry *debugfs_stats;
2308c2ecf20Sopenharmony_ci	struct dentry *debugfs_pci_regs;
2318c2ecf20Sopenharmony_ci	struct dentry *debugfs_cram;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	card->debugfs_dir = debugfs_create_dir(card->gendisk->disk_name, NULL);
2348c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(card->debugfs_dir))
2358c2ecf20Sopenharmony_ci		goto failed_debugfs_dir;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	debugfs_stats = debugfs_create_file("stats", 0444,
2388c2ecf20Sopenharmony_ci					    card->debugfs_dir, card,
2398c2ecf20Sopenharmony_ci					    &debugfs_stats_fops);
2408c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(debugfs_stats))
2418c2ecf20Sopenharmony_ci		goto failed_debugfs_stats;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	debugfs_pci_regs = debugfs_create_file("pci_regs", 0444,
2448c2ecf20Sopenharmony_ci					       card->debugfs_dir, card,
2458c2ecf20Sopenharmony_ci					       &debugfs_pci_regs_fops);
2468c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(debugfs_pci_regs))
2478c2ecf20Sopenharmony_ci		goto failed_debugfs_pci_regs;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	debugfs_cram = debugfs_create_file("cram", 0644,
2508c2ecf20Sopenharmony_ci					   card->debugfs_dir, card,
2518c2ecf20Sopenharmony_ci					   &debugfs_cram_fops);
2528c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(debugfs_cram))
2538c2ecf20Sopenharmony_ci		goto failed_debugfs_cram;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	return;
2568c2ecf20Sopenharmony_cifailed_debugfs_cram:
2578c2ecf20Sopenharmony_ci	debugfs_remove(debugfs_pci_regs);
2588c2ecf20Sopenharmony_cifailed_debugfs_pci_regs:
2598c2ecf20Sopenharmony_ci	debugfs_remove(debugfs_stats);
2608c2ecf20Sopenharmony_cifailed_debugfs_stats:
2618c2ecf20Sopenharmony_ci	debugfs_remove(card->debugfs_dir);
2628c2ecf20Sopenharmony_cifailed_debugfs_dir:
2638c2ecf20Sopenharmony_ci	card->debugfs_dir = NULL;
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci/*----------------- Interrupt Control & Handling -------------------*/
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic void rsxx_mask_interrupts(struct rsxx_cardinfo *card)
2698c2ecf20Sopenharmony_ci{
2708c2ecf20Sopenharmony_ci	card->isr_mask = 0;
2718c2ecf20Sopenharmony_ci	card->ier_mask = 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void __enable_intr(unsigned int *mask, unsigned int intr)
2758c2ecf20Sopenharmony_ci{
2768c2ecf20Sopenharmony_ci	*mask |= intr;
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic void __disable_intr(unsigned int *mask, unsigned int intr)
2808c2ecf20Sopenharmony_ci{
2818c2ecf20Sopenharmony_ci	*mask &= ~intr;
2828c2ecf20Sopenharmony_ci}
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci/*
2858c2ecf20Sopenharmony_ci * NOTE: Disabling the IER will disable the hardware interrupt.
2868c2ecf20Sopenharmony_ci * Disabling the ISR will disable the software handling of the ISR bit.
2878c2ecf20Sopenharmony_ci *
2888c2ecf20Sopenharmony_ci * Enable/Disable interrupt functions assume the card->irq_lock
2898c2ecf20Sopenharmony_ci * is held by the caller.
2908c2ecf20Sopenharmony_ci */
2918c2ecf20Sopenharmony_civoid rsxx_enable_ier(struct rsxx_cardinfo *card, unsigned int intr)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	if (unlikely(card->halt) ||
2948c2ecf20Sopenharmony_ci	    unlikely(card->eeh_state))
2958c2ecf20Sopenharmony_ci		return;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	__enable_intr(&card->ier_mask, intr);
2988c2ecf20Sopenharmony_ci	iowrite32(card->ier_mask, card->regmap + IER);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_civoid rsxx_disable_ier(struct rsxx_cardinfo *card, unsigned int intr)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	if (unlikely(card->eeh_state))
3048c2ecf20Sopenharmony_ci		return;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	__disable_intr(&card->ier_mask, intr);
3078c2ecf20Sopenharmony_ci	iowrite32(card->ier_mask, card->regmap + IER);
3088c2ecf20Sopenharmony_ci}
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_civoid rsxx_enable_ier_and_isr(struct rsxx_cardinfo *card,
3118c2ecf20Sopenharmony_ci				 unsigned int intr)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	if (unlikely(card->halt) ||
3148c2ecf20Sopenharmony_ci	    unlikely(card->eeh_state))
3158c2ecf20Sopenharmony_ci		return;
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	__enable_intr(&card->isr_mask, intr);
3188c2ecf20Sopenharmony_ci	__enable_intr(&card->ier_mask, intr);
3198c2ecf20Sopenharmony_ci	iowrite32(card->ier_mask, card->regmap + IER);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_civoid rsxx_disable_ier_and_isr(struct rsxx_cardinfo *card,
3228c2ecf20Sopenharmony_ci				  unsigned int intr)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	if (unlikely(card->eeh_state))
3258c2ecf20Sopenharmony_ci		return;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	__disable_intr(&card->isr_mask, intr);
3288c2ecf20Sopenharmony_ci	__disable_intr(&card->ier_mask, intr);
3298c2ecf20Sopenharmony_ci	iowrite32(card->ier_mask, card->regmap + IER);
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic irqreturn_t rsxx_isr(int irq, void *pdata)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pdata;
3358c2ecf20Sopenharmony_ci	unsigned int isr;
3368c2ecf20Sopenharmony_ci	int handled = 0;
3378c2ecf20Sopenharmony_ci	int reread_isr;
3388c2ecf20Sopenharmony_ci	int i;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	spin_lock(&card->irq_lock);
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	do {
3438c2ecf20Sopenharmony_ci		reread_isr = 0;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci		if (unlikely(card->eeh_state))
3468c2ecf20Sopenharmony_ci			break;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci		isr = ioread32(card->regmap + ISR);
3498c2ecf20Sopenharmony_ci		if (isr == 0xffffffff) {
3508c2ecf20Sopenharmony_ci			/*
3518c2ecf20Sopenharmony_ci			 * A few systems seem to have an intermittent issue
3528c2ecf20Sopenharmony_ci			 * where PCI reads return all Fs, but retrying the read
3538c2ecf20Sopenharmony_ci			 * a little later will return as expected.
3548c2ecf20Sopenharmony_ci			 */
3558c2ecf20Sopenharmony_ci			dev_info(CARD_TO_DEV(card),
3568c2ecf20Sopenharmony_ci				"ISR = 0xFFFFFFFF, retrying later\n");
3578c2ecf20Sopenharmony_ci			break;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci		isr &= card->isr_mask;
3618c2ecf20Sopenharmony_ci		if (!isr)
3628c2ecf20Sopenharmony_ci			break;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci		for (i = 0; i < card->n_targets; i++) {
3658c2ecf20Sopenharmony_ci			if (isr & CR_INTR_DMA(i)) {
3668c2ecf20Sopenharmony_ci				if (card->ier_mask & CR_INTR_DMA(i)) {
3678c2ecf20Sopenharmony_ci					rsxx_disable_ier(card, CR_INTR_DMA(i));
3688c2ecf20Sopenharmony_ci					reread_isr = 1;
3698c2ecf20Sopenharmony_ci				}
3708c2ecf20Sopenharmony_ci				queue_work(card->ctrl[i].done_wq,
3718c2ecf20Sopenharmony_ci					   &card->ctrl[i].dma_done_work);
3728c2ecf20Sopenharmony_ci				handled++;
3738c2ecf20Sopenharmony_ci			}
3748c2ecf20Sopenharmony_ci		}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (isr & CR_INTR_CREG) {
3778c2ecf20Sopenharmony_ci			queue_work(card->creg_ctrl.creg_wq,
3788c2ecf20Sopenharmony_ci				   &card->creg_ctrl.done_work);
3798c2ecf20Sopenharmony_ci			handled++;
3808c2ecf20Sopenharmony_ci		}
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci		if (isr & CR_INTR_EVENT) {
3838c2ecf20Sopenharmony_ci			queue_work(card->event_wq, &card->event_work);
3848c2ecf20Sopenharmony_ci			rsxx_disable_ier_and_isr(card, CR_INTR_EVENT);
3858c2ecf20Sopenharmony_ci			handled++;
3868c2ecf20Sopenharmony_ci		}
3878c2ecf20Sopenharmony_ci	} while (reread_isr);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	spin_unlock(&card->irq_lock);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	return handled ? IRQ_HANDLED : IRQ_NONE;
3928c2ecf20Sopenharmony_ci}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci/*----------------- Card Event Handler -------------------*/
3958c2ecf20Sopenharmony_cistatic const char * const rsxx_card_state_to_str(unsigned int state)
3968c2ecf20Sopenharmony_ci{
3978c2ecf20Sopenharmony_ci	static const char * const state_strings[] = {
3988c2ecf20Sopenharmony_ci		"Unknown", "Shutdown", "Starting", "Formatting",
3998c2ecf20Sopenharmony_ci		"Uninitialized", "Good", "Shutting Down",
4008c2ecf20Sopenharmony_ci		"Fault", "Read Only Fault", "dStroying"
4018c2ecf20Sopenharmony_ci	};
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci	return state_strings[ffs(state)];
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic void card_state_change(struct rsxx_cardinfo *card,
4078c2ecf20Sopenharmony_ci			      unsigned int new_state)
4088c2ecf20Sopenharmony_ci{
4098c2ecf20Sopenharmony_ci	int st;
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci	dev_info(CARD_TO_DEV(card),
4128c2ecf20Sopenharmony_ci		"card state change detected.(%s -> %s)\n",
4138c2ecf20Sopenharmony_ci		rsxx_card_state_to_str(card->state),
4148c2ecf20Sopenharmony_ci		rsxx_card_state_to_str(new_state));
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	card->state = new_state;
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	/* Don't attach DMA interfaces if the card has an invalid config */
4198c2ecf20Sopenharmony_ci	if (!card->config_valid)
4208c2ecf20Sopenharmony_ci		return;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	switch (new_state) {
4238c2ecf20Sopenharmony_ci	case CARD_STATE_RD_ONLY_FAULT:
4248c2ecf20Sopenharmony_ci		dev_crit(CARD_TO_DEV(card),
4258c2ecf20Sopenharmony_ci			"Hardware has entered read-only mode!\n");
4268c2ecf20Sopenharmony_ci		/*
4278c2ecf20Sopenharmony_ci		 * Fall through so the DMA devices can be attached and
4288c2ecf20Sopenharmony_ci		 * the user can attempt to pull off their data.
4298c2ecf20Sopenharmony_ci		 */
4308c2ecf20Sopenharmony_ci		fallthrough;
4318c2ecf20Sopenharmony_ci	case CARD_STATE_GOOD:
4328c2ecf20Sopenharmony_ci		st = rsxx_get_card_size8(card, &card->size8);
4338c2ecf20Sopenharmony_ci		if (st)
4348c2ecf20Sopenharmony_ci			dev_err(CARD_TO_DEV(card),
4358c2ecf20Sopenharmony_ci				"Failed attaching DMA devices\n");
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (card->config_valid)
4388c2ecf20Sopenharmony_ci			set_capacity(card->gendisk, card->size8 >> 9);
4398c2ecf20Sopenharmony_ci		break;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	case CARD_STATE_FAULT:
4428c2ecf20Sopenharmony_ci		dev_crit(CARD_TO_DEV(card),
4438c2ecf20Sopenharmony_ci			"Hardware Fault reported!\n");
4448c2ecf20Sopenharmony_ci		fallthrough;
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	/* Everything else, detach DMA interface if it's attached. */
4478c2ecf20Sopenharmony_ci	case CARD_STATE_SHUTDOWN:
4488c2ecf20Sopenharmony_ci	case CARD_STATE_STARTING:
4498c2ecf20Sopenharmony_ci	case CARD_STATE_FORMATTING:
4508c2ecf20Sopenharmony_ci	case CARD_STATE_UNINITIALIZED:
4518c2ecf20Sopenharmony_ci	case CARD_STATE_SHUTTING_DOWN:
4528c2ecf20Sopenharmony_ci	/*
4538c2ecf20Sopenharmony_ci	 * dStroy is a term coined by marketing to represent the low level
4548c2ecf20Sopenharmony_ci	 * secure erase.
4558c2ecf20Sopenharmony_ci	 */
4568c2ecf20Sopenharmony_ci	case CARD_STATE_DSTROYING:
4578c2ecf20Sopenharmony_ci		set_capacity(card->gendisk, 0);
4588c2ecf20Sopenharmony_ci		break;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci}
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_cistatic void card_event_handler(struct work_struct *work)
4638c2ecf20Sopenharmony_ci{
4648c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card;
4658c2ecf20Sopenharmony_ci	unsigned int state;
4668c2ecf20Sopenharmony_ci	unsigned long flags;
4678c2ecf20Sopenharmony_ci	int st;
4688c2ecf20Sopenharmony_ci
4698c2ecf20Sopenharmony_ci	card = container_of(work, struct rsxx_cardinfo, event_work);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	if (unlikely(card->halt))
4728c2ecf20Sopenharmony_ci		return;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	/*
4758c2ecf20Sopenharmony_ci	 * Enable the interrupt now to avoid any weird race conditions where a
4768c2ecf20Sopenharmony_ci	 * state change might occur while rsxx_get_card_state() is
4778c2ecf20Sopenharmony_ci	 * processing a returned creg cmd.
4788c2ecf20Sopenharmony_ci	 */
4798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->irq_lock, flags);
4808c2ecf20Sopenharmony_ci	rsxx_enable_ier_and_isr(card, CR_INTR_EVENT);
4818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->irq_lock, flags);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	st = rsxx_get_card_state(card, &state);
4848c2ecf20Sopenharmony_ci	if (st) {
4858c2ecf20Sopenharmony_ci		dev_info(CARD_TO_DEV(card),
4868c2ecf20Sopenharmony_ci			"Failed reading state after event.\n");
4878c2ecf20Sopenharmony_ci		return;
4888c2ecf20Sopenharmony_ci	}
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	if (card->state != state)
4918c2ecf20Sopenharmony_ci		card_state_change(card, state);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	if (card->creg_ctrl.creg_stats.stat & CREG_STAT_LOG_PENDING)
4948c2ecf20Sopenharmony_ci		rsxx_read_hw_log(card);
4958c2ecf20Sopenharmony_ci}
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci/*----------------- Card Operations -------------------*/
4988c2ecf20Sopenharmony_cistatic int card_shutdown(struct rsxx_cardinfo *card)
4998c2ecf20Sopenharmony_ci{
5008c2ecf20Sopenharmony_ci	unsigned int state;
5018c2ecf20Sopenharmony_ci	signed long start;
5028c2ecf20Sopenharmony_ci	const int timeout = msecs_to_jiffies(120000);
5038c2ecf20Sopenharmony_ci	int st;
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	/* We can't issue a shutdown if the card is in a transition state */
5068c2ecf20Sopenharmony_ci	start = jiffies;
5078c2ecf20Sopenharmony_ci	do {
5088c2ecf20Sopenharmony_ci		st = rsxx_get_card_state(card, &state);
5098c2ecf20Sopenharmony_ci		if (st)
5108c2ecf20Sopenharmony_ci			return st;
5118c2ecf20Sopenharmony_ci	} while (state == CARD_STATE_STARTING &&
5128c2ecf20Sopenharmony_ci		 (jiffies - start < timeout));
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	if (state == CARD_STATE_STARTING)
5158c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	/* Only issue a shutdown if we need to */
5188c2ecf20Sopenharmony_ci	if ((state != CARD_STATE_SHUTTING_DOWN) &&
5198c2ecf20Sopenharmony_ci	    (state != CARD_STATE_SHUTDOWN)) {
5208c2ecf20Sopenharmony_ci		st = rsxx_issue_card_cmd(card, CARD_CMD_SHUTDOWN);
5218c2ecf20Sopenharmony_ci		if (st)
5228c2ecf20Sopenharmony_ci			return st;
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	start = jiffies;
5268c2ecf20Sopenharmony_ci	do {
5278c2ecf20Sopenharmony_ci		st = rsxx_get_card_state(card, &state);
5288c2ecf20Sopenharmony_ci		if (st)
5298c2ecf20Sopenharmony_ci			return st;
5308c2ecf20Sopenharmony_ci	} while (state != CARD_STATE_SHUTDOWN &&
5318c2ecf20Sopenharmony_ci		 (jiffies - start < timeout));
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	if (state != CARD_STATE_SHUTDOWN)
5348c2ecf20Sopenharmony_ci		return -ETIMEDOUT;
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci	return 0;
5378c2ecf20Sopenharmony_ci}
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_cistatic int rsxx_eeh_frozen(struct pci_dev *dev)
5408c2ecf20Sopenharmony_ci{
5418c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pci_get_drvdata(dev);
5428c2ecf20Sopenharmony_ci	int i;
5438c2ecf20Sopenharmony_ci	int st;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	dev_warn(&dev->dev, "IBM Flash Adapter PCI: preparing for slot reset.\n");
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	card->eeh_state = 1;
5488c2ecf20Sopenharmony_ci	rsxx_mask_interrupts(card);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci	/*
5518c2ecf20Sopenharmony_ci	 * We need to guarantee that the write for eeh_state and masking
5528c2ecf20Sopenharmony_ci	 * interrupts does not become reordered. This will prevent a possible
5538c2ecf20Sopenharmony_ci	 * race condition with the EEH code.
5548c2ecf20Sopenharmony_ci	 */
5558c2ecf20Sopenharmony_ci	wmb();
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_ci	pci_disable_device(dev);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci	st = rsxx_eeh_save_issued_dmas(card);
5608c2ecf20Sopenharmony_ci	if (st)
5618c2ecf20Sopenharmony_ci		return st;
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	rsxx_eeh_save_issued_creg(card);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
5668c2ecf20Sopenharmony_ci		if (card->ctrl[i].status.buf)
5678c2ecf20Sopenharmony_ci			dma_free_coherent(&card->dev->dev,
5688c2ecf20Sopenharmony_ci					  STATUS_BUFFER_SIZE8,
5698c2ecf20Sopenharmony_ci					  card->ctrl[i].status.buf,
5708c2ecf20Sopenharmony_ci					  card->ctrl[i].status.dma_addr);
5718c2ecf20Sopenharmony_ci		if (card->ctrl[i].cmd.buf)
5728c2ecf20Sopenharmony_ci			dma_free_coherent(&card->dev->dev,
5738c2ecf20Sopenharmony_ci					  COMMAND_BUFFER_SIZE8,
5748c2ecf20Sopenharmony_ci					  card->ctrl[i].cmd.buf,
5758c2ecf20Sopenharmony_ci					  card->ctrl[i].cmd.dma_addr);
5768c2ecf20Sopenharmony_ci	}
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	return 0;
5798c2ecf20Sopenharmony_ci}
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_cistatic void rsxx_eeh_failure(struct pci_dev *dev)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pci_get_drvdata(dev);
5848c2ecf20Sopenharmony_ci	int i;
5858c2ecf20Sopenharmony_ci	int cnt = 0;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	dev_err(&dev->dev, "IBM Flash Adapter PCI: disabling failed card.\n");
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	card->eeh_state = 1;
5908c2ecf20Sopenharmony_ci	card->halt = 1;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
5938c2ecf20Sopenharmony_ci		spin_lock_bh(&card->ctrl[i].queue_lock);
5948c2ecf20Sopenharmony_ci		cnt = rsxx_cleanup_dma_queue(&card->ctrl[i],
5958c2ecf20Sopenharmony_ci					     &card->ctrl[i].queue,
5968c2ecf20Sopenharmony_ci					     COMPLETE_DMA);
5978c2ecf20Sopenharmony_ci		spin_unlock_bh(&card->ctrl[i].queue_lock);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci		cnt += rsxx_dma_cancel(&card->ctrl[i]);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci		if (cnt)
6028c2ecf20Sopenharmony_ci			dev_info(CARD_TO_DEV(card),
6038c2ecf20Sopenharmony_ci				"Freed %d queued DMAs on channel %d\n",
6048c2ecf20Sopenharmony_ci				cnt, card->ctrl[i].id);
6058c2ecf20Sopenharmony_ci	}
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_cistatic int rsxx_eeh_fifo_flush_poll(struct rsxx_cardinfo *card)
6098c2ecf20Sopenharmony_ci{
6108c2ecf20Sopenharmony_ci	unsigned int status;
6118c2ecf20Sopenharmony_ci	int iter = 0;
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	/* We need to wait for the hardware to reset */
6148c2ecf20Sopenharmony_ci	while (iter++ < 10) {
6158c2ecf20Sopenharmony_ci		status = ioread32(card->regmap + PCI_RECONFIG);
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci		if (status & RSXX_FLUSH_BUSY) {
6188c2ecf20Sopenharmony_ci			ssleep(1);
6198c2ecf20Sopenharmony_ci			continue;
6208c2ecf20Sopenharmony_ci		}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		if (status & RSXX_FLUSH_TIMEOUT)
6238c2ecf20Sopenharmony_ci			dev_warn(CARD_TO_DEV(card), "HW: flash controller timeout\n");
6248c2ecf20Sopenharmony_ci		return 0;
6258c2ecf20Sopenharmony_ci	}
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	/* Hardware failed resetting itself. */
6288c2ecf20Sopenharmony_ci	return -1;
6298c2ecf20Sopenharmony_ci}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic pci_ers_result_t rsxx_error_detected(struct pci_dev *dev,
6328c2ecf20Sopenharmony_ci					    pci_channel_state_t error)
6338c2ecf20Sopenharmony_ci{
6348c2ecf20Sopenharmony_ci	int st;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (dev->revision < RSXX_EEH_SUPPORT)
6378c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_NONE;
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (error == pci_channel_io_perm_failure) {
6408c2ecf20Sopenharmony_ci		rsxx_eeh_failure(dev);
6418c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	st = rsxx_eeh_frozen(dev);
6458c2ecf20Sopenharmony_ci	if (st) {
6468c2ecf20Sopenharmony_ci		dev_err(&dev->dev, "Slot reset setup failed\n");
6478c2ecf20Sopenharmony_ci		rsxx_eeh_failure(dev);
6488c2ecf20Sopenharmony_ci		return PCI_ERS_RESULT_DISCONNECT;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_NEED_RESET;
6528c2ecf20Sopenharmony_ci}
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_cistatic pci_ers_result_t rsxx_slot_reset(struct pci_dev *dev)
6558c2ecf20Sopenharmony_ci{
6568c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pci_get_drvdata(dev);
6578c2ecf20Sopenharmony_ci	unsigned long flags;
6588c2ecf20Sopenharmony_ci	int i;
6598c2ecf20Sopenharmony_ci	int st;
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci	dev_warn(&dev->dev,
6628c2ecf20Sopenharmony_ci		"IBM Flash Adapter PCI: recovering from slot reset.\n");
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	st = pci_enable_device(dev);
6658c2ecf20Sopenharmony_ci	if (st)
6668c2ecf20Sopenharmony_ci		goto failed_hw_setup;
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	pci_set_master(dev);
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci	st = rsxx_eeh_fifo_flush_poll(card);
6718c2ecf20Sopenharmony_ci	if (st)
6728c2ecf20Sopenharmony_ci		goto failed_hw_setup;
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	rsxx_dma_queue_reset(card);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
6778c2ecf20Sopenharmony_ci		st = rsxx_hw_buffers_init(dev, &card->ctrl[i]);
6788c2ecf20Sopenharmony_ci		if (st)
6798c2ecf20Sopenharmony_ci			goto failed_hw_buffers_init;
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	if (card->config_valid)
6838c2ecf20Sopenharmony_ci		rsxx_dma_configure(card);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci	/* Clears the ISR register from spurious interrupts */
6868c2ecf20Sopenharmony_ci	st = ioread32(card->regmap + ISR);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	card->eeh_state = 0;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->irq_lock, flags);
6918c2ecf20Sopenharmony_ci	if (card->n_targets & RSXX_MAX_TARGETS)
6928c2ecf20Sopenharmony_ci		rsxx_enable_ier_and_isr(card, CR_INTR_ALL_G);
6938c2ecf20Sopenharmony_ci	else
6948c2ecf20Sopenharmony_ci		rsxx_enable_ier_and_isr(card, CR_INTR_ALL_C);
6958c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->irq_lock, flags);
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_ci	rsxx_kick_creg_queue(card);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
7008c2ecf20Sopenharmony_ci		spin_lock(&card->ctrl[i].queue_lock);
7018c2ecf20Sopenharmony_ci		if (list_empty(&card->ctrl[i].queue)) {
7028c2ecf20Sopenharmony_ci			spin_unlock(&card->ctrl[i].queue_lock);
7038c2ecf20Sopenharmony_ci			continue;
7048c2ecf20Sopenharmony_ci		}
7058c2ecf20Sopenharmony_ci		spin_unlock(&card->ctrl[i].queue_lock);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		queue_work(card->ctrl[i].issue_wq,
7088c2ecf20Sopenharmony_ci				&card->ctrl[i].issue_dma_work);
7098c2ecf20Sopenharmony_ci	}
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "IBM Flash Adapter PCI: recovery complete.\n");
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_RECOVERED;
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cifailed_hw_buffers_init:
7168c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
7178c2ecf20Sopenharmony_ci		if (card->ctrl[i].status.buf)
7188c2ecf20Sopenharmony_ci			dma_free_coherent(&card->dev->dev,
7198c2ecf20Sopenharmony_ci					  STATUS_BUFFER_SIZE8,
7208c2ecf20Sopenharmony_ci					  card->ctrl[i].status.buf,
7218c2ecf20Sopenharmony_ci					  card->ctrl[i].status.dma_addr);
7228c2ecf20Sopenharmony_ci		if (card->ctrl[i].cmd.buf)
7238c2ecf20Sopenharmony_ci			dma_free_coherent(&card->dev->dev,
7248c2ecf20Sopenharmony_ci					  COMMAND_BUFFER_SIZE8,
7258c2ecf20Sopenharmony_ci					  card->ctrl[i].cmd.buf,
7268c2ecf20Sopenharmony_ci					  card->ctrl[i].cmd.dma_addr);
7278c2ecf20Sopenharmony_ci	}
7288c2ecf20Sopenharmony_cifailed_hw_setup:
7298c2ecf20Sopenharmony_ci	rsxx_eeh_failure(dev);
7308c2ecf20Sopenharmony_ci	return PCI_ERS_RESULT_DISCONNECT;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci/*----------------- Driver Initialization & Setup -------------------*/
7358c2ecf20Sopenharmony_ci/* Returns:   0 if the driver is compatible with the device
7368c2ecf20Sopenharmony_ci	     -1 if the driver is NOT compatible with the device */
7378c2ecf20Sopenharmony_cistatic int rsxx_compatibility_check(struct rsxx_cardinfo *card)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	unsigned char pci_rev;
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	pci_read_config_byte(card->dev, PCI_REVISION_ID, &pci_rev);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (pci_rev > RS70_PCI_REV_SUPPORTED)
7448c2ecf20Sopenharmony_ci		return -1;
7458c2ecf20Sopenharmony_ci	return 0;
7468c2ecf20Sopenharmony_ci}
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_cistatic int rsxx_pci_probe(struct pci_dev *dev,
7498c2ecf20Sopenharmony_ci					const struct pci_device_id *id)
7508c2ecf20Sopenharmony_ci{
7518c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card;
7528c2ecf20Sopenharmony_ci	int st;
7538c2ecf20Sopenharmony_ci	unsigned int sync_timeout;
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci	dev_info(&dev->dev, "PCI-Flash SSD discovered\n");
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(*card), GFP_KERNEL);
7588c2ecf20Sopenharmony_ci	if (!card)
7598c2ecf20Sopenharmony_ci		return -ENOMEM;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	card->dev = dev;
7628c2ecf20Sopenharmony_ci	pci_set_drvdata(dev, card);
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	st = ida_alloc(&rsxx_disk_ida, GFP_KERNEL);
7658c2ecf20Sopenharmony_ci	if (st < 0)
7668c2ecf20Sopenharmony_ci		goto failed_ida_get;
7678c2ecf20Sopenharmony_ci	card->disk_id = st;
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	st = pci_enable_device(dev);
7708c2ecf20Sopenharmony_ci	if (st)
7718c2ecf20Sopenharmony_ci		goto failed_enable;
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci	pci_set_master(dev);
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	st = dma_set_mask(&dev->dev, DMA_BIT_MASK(64));
7768c2ecf20Sopenharmony_ci	if (st) {
7778c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card),
7788c2ecf20Sopenharmony_ci			"No usable DMA configuration,aborting\n");
7798c2ecf20Sopenharmony_ci		goto failed_dma_mask;
7808c2ecf20Sopenharmony_ci	}
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	st = pci_request_regions(dev, DRIVER_NAME);
7838c2ecf20Sopenharmony_ci	if (st) {
7848c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card),
7858c2ecf20Sopenharmony_ci			"Failed to request memory region\n");
7868c2ecf20Sopenharmony_ci		goto failed_request_regions;
7878c2ecf20Sopenharmony_ci	}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ci	if (pci_resource_len(dev, 0) == 0) {
7908c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card), "BAR0 has length 0!\n");
7918c2ecf20Sopenharmony_ci		st = -ENOMEM;
7928c2ecf20Sopenharmony_ci		goto failed_iomap;
7938c2ecf20Sopenharmony_ci	}
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	card->regmap = pci_iomap(dev, 0, 0);
7968c2ecf20Sopenharmony_ci	if (!card->regmap) {
7978c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card), "Failed to map BAR0\n");
7988c2ecf20Sopenharmony_ci		st = -ENOMEM;
7998c2ecf20Sopenharmony_ci		goto failed_iomap;
8008c2ecf20Sopenharmony_ci	}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	spin_lock_init(&card->irq_lock);
8038c2ecf20Sopenharmony_ci	card->halt = 0;
8048c2ecf20Sopenharmony_ci	card->eeh_state = 0;
8058c2ecf20Sopenharmony_ci
8068c2ecf20Sopenharmony_ci	spin_lock_irq(&card->irq_lock);
8078c2ecf20Sopenharmony_ci	rsxx_disable_ier_and_isr(card, CR_INTR_ALL);
8088c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->irq_lock);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	if (!force_legacy) {
8118c2ecf20Sopenharmony_ci		st = pci_enable_msi(dev);
8128c2ecf20Sopenharmony_ci		if (st)
8138c2ecf20Sopenharmony_ci			dev_warn(CARD_TO_DEV(card),
8148c2ecf20Sopenharmony_ci				"Failed to enable MSI\n");
8158c2ecf20Sopenharmony_ci	}
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	st = request_irq(dev->irq, rsxx_isr, IRQF_SHARED,
8188c2ecf20Sopenharmony_ci			 DRIVER_NAME, card);
8198c2ecf20Sopenharmony_ci	if (st) {
8208c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card),
8218c2ecf20Sopenharmony_ci			"Failed requesting IRQ%d\n", dev->irq);
8228c2ecf20Sopenharmony_ci		goto failed_irq;
8238c2ecf20Sopenharmony_ci	}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ci	/************* Setup Processor Command Interface *************/
8268c2ecf20Sopenharmony_ci	st = rsxx_creg_setup(card);
8278c2ecf20Sopenharmony_ci	if (st) {
8288c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card), "Failed to setup creg interface.\n");
8298c2ecf20Sopenharmony_ci		goto failed_creg_setup;
8308c2ecf20Sopenharmony_ci	}
8318c2ecf20Sopenharmony_ci
8328c2ecf20Sopenharmony_ci	spin_lock_irq(&card->irq_lock);
8338c2ecf20Sopenharmony_ci	rsxx_enable_ier_and_isr(card, CR_INTR_CREG);
8348c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->irq_lock);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	st = rsxx_compatibility_check(card);
8378c2ecf20Sopenharmony_ci	if (st) {
8388c2ecf20Sopenharmony_ci		dev_warn(CARD_TO_DEV(card),
8398c2ecf20Sopenharmony_ci			"Incompatible driver detected. Please update the driver.\n");
8408c2ecf20Sopenharmony_ci		st = -EINVAL;
8418c2ecf20Sopenharmony_ci		goto failed_compatiblity_check;
8428c2ecf20Sopenharmony_ci	}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	/************* Load Card Config *************/
8458c2ecf20Sopenharmony_ci	st = rsxx_load_config(card);
8468c2ecf20Sopenharmony_ci	if (st)
8478c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card),
8488c2ecf20Sopenharmony_ci			"Failed loading card config\n");
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	/************* Setup DMA Engine *************/
8518c2ecf20Sopenharmony_ci	st = rsxx_get_num_targets(card, &card->n_targets);
8528c2ecf20Sopenharmony_ci	if (st)
8538c2ecf20Sopenharmony_ci		dev_info(CARD_TO_DEV(card),
8548c2ecf20Sopenharmony_ci			"Failed reading the number of DMA targets\n");
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	card->ctrl = kcalloc(card->n_targets, sizeof(*card->ctrl),
8578c2ecf20Sopenharmony_ci			     GFP_KERNEL);
8588c2ecf20Sopenharmony_ci	if (!card->ctrl) {
8598c2ecf20Sopenharmony_ci		st = -ENOMEM;
8608c2ecf20Sopenharmony_ci		goto failed_dma_setup;
8618c2ecf20Sopenharmony_ci	}
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	st = rsxx_dma_setup(card);
8648c2ecf20Sopenharmony_ci	if (st) {
8658c2ecf20Sopenharmony_ci		dev_info(CARD_TO_DEV(card),
8668c2ecf20Sopenharmony_ci			"Failed to setup DMA engine\n");
8678c2ecf20Sopenharmony_ci		goto failed_dma_setup;
8688c2ecf20Sopenharmony_ci	}
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/************* Setup Card Event Handler *************/
8718c2ecf20Sopenharmony_ci	card->event_wq = create_singlethread_workqueue(DRIVER_NAME"_event");
8728c2ecf20Sopenharmony_ci	if (!card->event_wq) {
8738c2ecf20Sopenharmony_ci		dev_err(CARD_TO_DEV(card), "Failed card event setup.\n");
8748c2ecf20Sopenharmony_ci		st = -ENOMEM;
8758c2ecf20Sopenharmony_ci		goto failed_event_handler;
8768c2ecf20Sopenharmony_ci	}
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	INIT_WORK(&card->event_work, card_event_handler);
8798c2ecf20Sopenharmony_ci
8808c2ecf20Sopenharmony_ci	st = rsxx_setup_dev(card);
8818c2ecf20Sopenharmony_ci	if (st)
8828c2ecf20Sopenharmony_ci		goto failed_create_dev;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	rsxx_get_card_state(card, &card->state);
8858c2ecf20Sopenharmony_ci
8868c2ecf20Sopenharmony_ci	dev_info(CARD_TO_DEV(card),
8878c2ecf20Sopenharmony_ci		"card state: %s\n",
8888c2ecf20Sopenharmony_ci		rsxx_card_state_to_str(card->state));
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	/*
8918c2ecf20Sopenharmony_ci	 * Now that the DMA Engine and devices have been setup,
8928c2ecf20Sopenharmony_ci	 * we can enable the event interrupt(it kicks off actions in
8938c2ecf20Sopenharmony_ci	 * those layers so we couldn't enable it right away.)
8948c2ecf20Sopenharmony_ci	 */
8958c2ecf20Sopenharmony_ci	spin_lock_irq(&card->irq_lock);
8968c2ecf20Sopenharmony_ci	rsxx_enable_ier_and_isr(card, CR_INTR_EVENT);
8978c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->irq_lock);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (card->state == CARD_STATE_SHUTDOWN) {
9008c2ecf20Sopenharmony_ci		st = rsxx_issue_card_cmd(card, CARD_CMD_STARTUP);
9018c2ecf20Sopenharmony_ci		if (st)
9028c2ecf20Sopenharmony_ci			dev_crit(CARD_TO_DEV(card),
9038c2ecf20Sopenharmony_ci				"Failed issuing card startup\n");
9048c2ecf20Sopenharmony_ci		if (sync_start) {
9058c2ecf20Sopenharmony_ci			sync_timeout = SYNC_START_TIMEOUT;
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_ci			dev_info(CARD_TO_DEV(card),
9088c2ecf20Sopenharmony_ci				 "Waiting for card to startup\n");
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_ci			do {
9118c2ecf20Sopenharmony_ci				ssleep(1);
9128c2ecf20Sopenharmony_ci				sync_timeout--;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci				rsxx_get_card_state(card, &card->state);
9158c2ecf20Sopenharmony_ci			} while (sync_timeout &&
9168c2ecf20Sopenharmony_ci				(card->state == CARD_STATE_STARTING));
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci			if (card->state == CARD_STATE_STARTING) {
9198c2ecf20Sopenharmony_ci				dev_warn(CARD_TO_DEV(card),
9208c2ecf20Sopenharmony_ci					 "Card startup timed out\n");
9218c2ecf20Sopenharmony_ci				card->size8 = 0;
9228c2ecf20Sopenharmony_ci			} else {
9238c2ecf20Sopenharmony_ci				dev_info(CARD_TO_DEV(card),
9248c2ecf20Sopenharmony_ci					"card state: %s\n",
9258c2ecf20Sopenharmony_ci					rsxx_card_state_to_str(card->state));
9268c2ecf20Sopenharmony_ci				st = rsxx_get_card_size8(card, &card->size8);
9278c2ecf20Sopenharmony_ci				if (st)
9288c2ecf20Sopenharmony_ci					card->size8 = 0;
9298c2ecf20Sopenharmony_ci			}
9308c2ecf20Sopenharmony_ci		}
9318c2ecf20Sopenharmony_ci	} else if (card->state == CARD_STATE_GOOD ||
9328c2ecf20Sopenharmony_ci		   card->state == CARD_STATE_RD_ONLY_FAULT) {
9338c2ecf20Sopenharmony_ci		st = rsxx_get_card_size8(card, &card->size8);
9348c2ecf20Sopenharmony_ci		if (st)
9358c2ecf20Sopenharmony_ci			card->size8 = 0;
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	rsxx_attach_dev(card);
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_ci	/************* Setup Debugfs *************/
9418c2ecf20Sopenharmony_ci	rsxx_debugfs_dev_new(card);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	return 0;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_cifailed_create_dev:
9468c2ecf20Sopenharmony_ci	destroy_workqueue(card->event_wq);
9478c2ecf20Sopenharmony_ci	card->event_wq = NULL;
9488c2ecf20Sopenharmony_cifailed_event_handler:
9498c2ecf20Sopenharmony_ci	rsxx_dma_destroy(card);
9508c2ecf20Sopenharmony_cifailed_dma_setup:
9518c2ecf20Sopenharmony_cifailed_compatiblity_check:
9528c2ecf20Sopenharmony_ci	destroy_workqueue(card->creg_ctrl.creg_wq);
9538c2ecf20Sopenharmony_ci	card->creg_ctrl.creg_wq = NULL;
9548c2ecf20Sopenharmony_cifailed_creg_setup:
9558c2ecf20Sopenharmony_ci	spin_lock_irq(&card->irq_lock);
9568c2ecf20Sopenharmony_ci	rsxx_disable_ier_and_isr(card, CR_INTR_ALL);
9578c2ecf20Sopenharmony_ci	spin_unlock_irq(&card->irq_lock);
9588c2ecf20Sopenharmony_ci	free_irq(dev->irq, card);
9598c2ecf20Sopenharmony_ci	if (!force_legacy)
9608c2ecf20Sopenharmony_ci		pci_disable_msi(dev);
9618c2ecf20Sopenharmony_cifailed_irq:
9628c2ecf20Sopenharmony_ci	pci_iounmap(dev, card->regmap);
9638c2ecf20Sopenharmony_cifailed_iomap:
9648c2ecf20Sopenharmony_ci	pci_release_regions(dev);
9658c2ecf20Sopenharmony_cifailed_request_regions:
9668c2ecf20Sopenharmony_cifailed_dma_mask:
9678c2ecf20Sopenharmony_ci	pci_disable_device(dev);
9688c2ecf20Sopenharmony_cifailed_enable:
9698c2ecf20Sopenharmony_ci	ida_free(&rsxx_disk_ida, card->disk_id);
9708c2ecf20Sopenharmony_cifailed_ida_get:
9718c2ecf20Sopenharmony_ci	kfree(card);
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_ci	return st;
9748c2ecf20Sopenharmony_ci}
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_cistatic void rsxx_pci_remove(struct pci_dev *dev)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pci_get_drvdata(dev);
9798c2ecf20Sopenharmony_ci	unsigned long flags;
9808c2ecf20Sopenharmony_ci	int st;
9818c2ecf20Sopenharmony_ci	int i;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci	if (!card)
9848c2ecf20Sopenharmony_ci		return;
9858c2ecf20Sopenharmony_ci
9868c2ecf20Sopenharmony_ci	dev_info(CARD_TO_DEV(card),
9878c2ecf20Sopenharmony_ci		"Removing PCI-Flash SSD.\n");
9888c2ecf20Sopenharmony_ci
9898c2ecf20Sopenharmony_ci	rsxx_detach_dev(card);
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
9928c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->irq_lock, flags);
9938c2ecf20Sopenharmony_ci		rsxx_disable_ier_and_isr(card, CR_INTR_DMA(i));
9948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->irq_lock, flags);
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	st = card_shutdown(card);
9988c2ecf20Sopenharmony_ci	if (st)
9998c2ecf20Sopenharmony_ci		dev_crit(CARD_TO_DEV(card), "Shutdown failed!\n");
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	/* Sync outstanding event handlers. */
10028c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->irq_lock, flags);
10038c2ecf20Sopenharmony_ci	rsxx_disable_ier_and_isr(card, CR_INTR_EVENT);
10048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->irq_lock, flags);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	cancel_work_sync(&card->event_work);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	destroy_workqueue(card->event_wq);
10098c2ecf20Sopenharmony_ci	rsxx_destroy_dev(card);
10108c2ecf20Sopenharmony_ci	rsxx_dma_destroy(card);
10118c2ecf20Sopenharmony_ci	destroy_workqueue(card->creg_ctrl.creg_wq);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->irq_lock, flags);
10148c2ecf20Sopenharmony_ci	rsxx_disable_ier_and_isr(card, CR_INTR_ALL);
10158c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->irq_lock, flags);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/* Prevent work_structs from re-queuing themselves. */
10188c2ecf20Sopenharmony_ci	card->halt = 1;
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	debugfs_remove_recursive(card->debugfs_dir);
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci	free_irq(dev->irq, card);
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (!force_legacy)
10258c2ecf20Sopenharmony_ci		pci_disable_msi(dev);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	rsxx_creg_destroy(card);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	pci_iounmap(dev, card->regmap);
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	pci_disable_device(dev);
10328c2ecf20Sopenharmony_ci	pci_release_regions(dev);
10338c2ecf20Sopenharmony_ci
10348c2ecf20Sopenharmony_ci	ida_free(&rsxx_disk_ida, card->disk_id);
10358c2ecf20Sopenharmony_ci	kfree(card);
10368c2ecf20Sopenharmony_ci}
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_cistatic int rsxx_pci_suspend(struct pci_dev *dev, pm_message_t state)
10398c2ecf20Sopenharmony_ci{
10408c2ecf20Sopenharmony_ci	/* We don't support suspend at this time. */
10418c2ecf20Sopenharmony_ci	return -ENOSYS;
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_cistatic void rsxx_pci_shutdown(struct pci_dev *dev)
10458c2ecf20Sopenharmony_ci{
10468c2ecf20Sopenharmony_ci	struct rsxx_cardinfo *card = pci_get_drvdata(dev);
10478c2ecf20Sopenharmony_ci	unsigned long flags;
10488c2ecf20Sopenharmony_ci	int i;
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci	if (!card)
10518c2ecf20Sopenharmony_ci		return;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	dev_info(CARD_TO_DEV(card), "Shutting down PCI-Flash SSD.\n");
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci	rsxx_detach_dev(card);
10568c2ecf20Sopenharmony_ci
10578c2ecf20Sopenharmony_ci	for (i = 0; i < card->n_targets; i++) {
10588c2ecf20Sopenharmony_ci		spin_lock_irqsave(&card->irq_lock, flags);
10598c2ecf20Sopenharmony_ci		rsxx_disable_ier_and_isr(card, CR_INTR_DMA(i));
10608c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&card->irq_lock, flags);
10618c2ecf20Sopenharmony_ci	}
10628c2ecf20Sopenharmony_ci
10638c2ecf20Sopenharmony_ci	card_shutdown(card);
10648c2ecf20Sopenharmony_ci}
10658c2ecf20Sopenharmony_ci
10668c2ecf20Sopenharmony_cistatic const struct pci_error_handlers rsxx_err_handler = {
10678c2ecf20Sopenharmony_ci	.error_detected = rsxx_error_detected,
10688c2ecf20Sopenharmony_ci	.slot_reset     = rsxx_slot_reset,
10698c2ecf20Sopenharmony_ci};
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_cistatic const struct pci_device_id rsxx_pci_ids[] = {
10728c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_FS70_FLASH)},
10738c2ecf20Sopenharmony_ci	{PCI_DEVICE(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_FS80_FLASH)},
10748c2ecf20Sopenharmony_ci	{0,},
10758c2ecf20Sopenharmony_ci};
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, rsxx_pci_ids);
10788c2ecf20Sopenharmony_ci
10798c2ecf20Sopenharmony_cistatic struct pci_driver rsxx_pci_driver = {
10808c2ecf20Sopenharmony_ci	.name		= DRIVER_NAME,
10818c2ecf20Sopenharmony_ci	.id_table	= rsxx_pci_ids,
10828c2ecf20Sopenharmony_ci	.probe		= rsxx_pci_probe,
10838c2ecf20Sopenharmony_ci	.remove		= rsxx_pci_remove,
10848c2ecf20Sopenharmony_ci	.suspend	= rsxx_pci_suspend,
10858c2ecf20Sopenharmony_ci	.shutdown	= rsxx_pci_shutdown,
10868c2ecf20Sopenharmony_ci	.err_handler    = &rsxx_err_handler,
10878c2ecf20Sopenharmony_ci};
10888c2ecf20Sopenharmony_ci
10898c2ecf20Sopenharmony_cistatic int __init rsxx_core_init(void)
10908c2ecf20Sopenharmony_ci{
10918c2ecf20Sopenharmony_ci	int st;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	st = rsxx_dev_init();
10948c2ecf20Sopenharmony_ci	if (st)
10958c2ecf20Sopenharmony_ci		return st;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci	st = rsxx_dma_init();
10988c2ecf20Sopenharmony_ci	if (st)
10998c2ecf20Sopenharmony_ci		goto dma_init_failed;
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	st = rsxx_creg_init();
11028c2ecf20Sopenharmony_ci	if (st)
11038c2ecf20Sopenharmony_ci		goto creg_init_failed;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	return pci_register_driver(&rsxx_pci_driver);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_cicreg_init_failed:
11088c2ecf20Sopenharmony_ci	rsxx_dma_cleanup();
11098c2ecf20Sopenharmony_cidma_init_failed:
11108c2ecf20Sopenharmony_ci	rsxx_dev_cleanup();
11118c2ecf20Sopenharmony_ci
11128c2ecf20Sopenharmony_ci	return st;
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_cistatic void __exit rsxx_core_cleanup(void)
11168c2ecf20Sopenharmony_ci{
11178c2ecf20Sopenharmony_ci	pci_unregister_driver(&rsxx_pci_driver);
11188c2ecf20Sopenharmony_ci	rsxx_creg_cleanup();
11198c2ecf20Sopenharmony_ci	rsxx_dma_cleanup();
11208c2ecf20Sopenharmony_ci	rsxx_dev_cleanup();
11218c2ecf20Sopenharmony_ci}
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_cimodule_init(rsxx_core_init);
11248c2ecf20Sopenharmony_cimodule_exit(rsxx_core_cleanup);
1125