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