18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for the Micron P320 SSD 48c2ecf20Sopenharmony_ci * Copyright (C) 2011 Micron Technology, Inc. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Portions of this code were derived from works subjected to the 78c2ecf20Sopenharmony_ci * following copyright: 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Integrated Device Technology, Inc. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <linux/pci.h> 128c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 138c2ecf20Sopenharmony_ci#include <linux/ata.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/hdreg.h> 168c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 178c2ecf20Sopenharmony_ci#include <linux/random.h> 188c2ecf20Sopenharmony_ci#include <linux/smp.h> 198c2ecf20Sopenharmony_ci#include <linux/compat.h> 208c2ecf20Sopenharmony_ci#include <linux/fs.h> 218c2ecf20Sopenharmony_ci#include <linux/module.h> 228c2ecf20Sopenharmony_ci#include <linux/genhd.h> 238c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 248c2ecf20Sopenharmony_ci#include <linux/blk-mq.h> 258c2ecf20Sopenharmony_ci#include <linux/bio.h> 268c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 278c2ecf20Sopenharmony_ci#include <linux/idr.h> 288c2ecf20Sopenharmony_ci#include <linux/kthread.h> 298c2ecf20Sopenharmony_ci#include <../drivers/ata/ahci.h> 308c2ecf20Sopenharmony_ci#include <linux/export.h> 318c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 328c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 338c2ecf20Sopenharmony_ci#include <linux/numa.h> 348c2ecf20Sopenharmony_ci#include "mtip32xx.h" 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define HW_CMD_SLOT_SZ (MTIP_MAX_COMMAND_SLOTS * 32) 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* DMA region containing RX Fis, Identify, RLE10, and SMART buffers */ 398c2ecf20Sopenharmony_ci#define AHCI_RX_FIS_SZ 0x100 408c2ecf20Sopenharmony_ci#define AHCI_RX_FIS_OFFSET 0x0 418c2ecf20Sopenharmony_ci#define AHCI_IDFY_SZ ATA_SECT_SIZE 428c2ecf20Sopenharmony_ci#define AHCI_IDFY_OFFSET 0x400 438c2ecf20Sopenharmony_ci#define AHCI_SECTBUF_SZ ATA_SECT_SIZE 448c2ecf20Sopenharmony_ci#define AHCI_SECTBUF_OFFSET 0x800 458c2ecf20Sopenharmony_ci#define AHCI_SMARTBUF_SZ ATA_SECT_SIZE 468c2ecf20Sopenharmony_ci#define AHCI_SMARTBUF_OFFSET 0xC00 478c2ecf20Sopenharmony_ci/* 0x100 + 0x200 + 0x200 + 0x200 is smaller than 4k but we pad it out */ 488c2ecf20Sopenharmony_ci#define BLOCK_DMA_ALLOC_SZ 4096 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci/* DMA region containing command table (should be 8192 bytes) */ 518c2ecf20Sopenharmony_ci#define AHCI_CMD_SLOT_SZ sizeof(struct mtip_cmd_hdr) 528c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_SZ (MTIP_MAX_COMMAND_SLOTS * AHCI_CMD_SLOT_SZ) 538c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_OFFSET 0x0 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/* DMA region per command (contains header and SGL) */ 568c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_HDR_SZ 0x80 578c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_HDR_OFFSET 0x0 588c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_SGL_SZ (MTIP_MAX_SG * sizeof(struct mtip_cmd_sg)) 598c2ecf20Sopenharmony_ci#define AHCI_CMD_TBL_SGL_OFFSET AHCI_CMD_TBL_HDR_SZ 608c2ecf20Sopenharmony_ci#define CMD_DMA_ALLOC_SZ (AHCI_CMD_TBL_SGL_SZ + AHCI_CMD_TBL_HDR_SZ) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci#define HOST_CAP_NZDMA (1 << 19) 648c2ecf20Sopenharmony_ci#define HOST_HSORG 0xFC 658c2ecf20Sopenharmony_ci#define HSORG_DISABLE_SLOTGRP_INTR (1<<24) 668c2ecf20Sopenharmony_ci#define HSORG_DISABLE_SLOTGRP_PXIS (1<<16) 678c2ecf20Sopenharmony_ci#define HSORG_HWREV 0xFF00 688c2ecf20Sopenharmony_ci#define HSORG_STYLE 0x8 698c2ecf20Sopenharmony_ci#define HSORG_SLOTGROUPS 0x7 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define PORT_COMMAND_ISSUE 0x38 728c2ecf20Sopenharmony_ci#define PORT_SDBV 0x7C 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci#define PORT_OFFSET 0x100 758c2ecf20Sopenharmony_ci#define PORT_MEM_SIZE 0x80 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define PORT_IRQ_ERR \ 788c2ecf20Sopenharmony_ci (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | PORT_IRQ_CONNECT | \ 798c2ecf20Sopenharmony_ci PORT_IRQ_PHYRDY | PORT_IRQ_UNK_FIS | PORT_IRQ_BAD_PMP | \ 808c2ecf20Sopenharmony_ci PORT_IRQ_TF_ERR | PORT_IRQ_HBUS_DATA_ERR | PORT_IRQ_IF_NONFATAL | \ 818c2ecf20Sopenharmony_ci PORT_IRQ_OVERFLOW) 828c2ecf20Sopenharmony_ci#define PORT_IRQ_LEGACY \ 838c2ecf20Sopenharmony_ci (PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) 848c2ecf20Sopenharmony_ci#define PORT_IRQ_HANDLED \ 858c2ecf20Sopenharmony_ci (PORT_IRQ_SDB_FIS | PORT_IRQ_LEGACY | \ 868c2ecf20Sopenharmony_ci PORT_IRQ_TF_ERR | PORT_IRQ_IF_ERR | \ 878c2ecf20Sopenharmony_ci PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY) 888c2ecf20Sopenharmony_ci#define DEF_PORT_IRQ \ 898c2ecf20Sopenharmony_ci (PORT_IRQ_ERR | PORT_IRQ_LEGACY | PORT_IRQ_SDB_FIS) 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* product numbers */ 928c2ecf20Sopenharmony_ci#define MTIP_PRODUCT_UNKNOWN 0x00 938c2ecf20Sopenharmony_ci#define MTIP_PRODUCT_ASICFPGA 0x11 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci/* Device instance number, incremented each time a device is probed. */ 968c2ecf20Sopenharmony_cistatic int instance; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic struct list_head online_list; 998c2ecf20Sopenharmony_cistatic struct list_head removing_list; 1008c2ecf20Sopenharmony_cistatic spinlock_t dev_lock; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci/* 1038c2ecf20Sopenharmony_ci * Global variable used to hold the major block device number 1048c2ecf20Sopenharmony_ci * allocated in mtip_init(). 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_cistatic int mtip_major; 1078c2ecf20Sopenharmony_cistatic struct dentry *dfs_parent; 1088c2ecf20Sopenharmony_cistatic struct dentry *dfs_device_status; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic u32 cpu_use[NR_CPUS]; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic DEFINE_IDA(rssd_index_ida); 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int mtip_block_initialize(struct driver_data *dd); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 1178c2ecf20Sopenharmony_cistruct mtip_compat_ide_task_request_s { 1188c2ecf20Sopenharmony_ci __u8 io_ports[8]; 1198c2ecf20Sopenharmony_ci __u8 hob_ports[8]; 1208c2ecf20Sopenharmony_ci ide_reg_valid_t out_flags; 1218c2ecf20Sopenharmony_ci ide_reg_valid_t in_flags; 1228c2ecf20Sopenharmony_ci int data_phase; 1238c2ecf20Sopenharmony_ci int req_cmd; 1248c2ecf20Sopenharmony_ci compat_ulong_t out_size; 1258c2ecf20Sopenharmony_ci compat_ulong_t in_size; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci#endif 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * This function check_for_surprise_removal is called 1318c2ecf20Sopenharmony_ci * while card is removed from the system and it will 1328c2ecf20Sopenharmony_ci * read the vendor id from the configuration space 1338c2ecf20Sopenharmony_ci * 1348c2ecf20Sopenharmony_ci * @pdev Pointer to the pci_dev structure. 1358c2ecf20Sopenharmony_ci * 1368c2ecf20Sopenharmony_ci * return value 1378c2ecf20Sopenharmony_ci * true if device removed, else false 1388c2ecf20Sopenharmony_ci */ 1398c2ecf20Sopenharmony_cistatic bool mtip_check_surprise_removal(struct pci_dev *pdev) 1408c2ecf20Sopenharmony_ci{ 1418c2ecf20Sopenharmony_ci u16 vendor_id = 0; 1428c2ecf20Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci if (dd->sr) 1458c2ecf20Sopenharmony_ci return true; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci /* Read the vendorID from the configuration space */ 1488c2ecf20Sopenharmony_ci pci_read_config_word(pdev, 0x00, &vendor_id); 1498c2ecf20Sopenharmony_ci if (vendor_id == 0xFFFF) { 1508c2ecf20Sopenharmony_ci dd->sr = true; 1518c2ecf20Sopenharmony_ci if (dd->queue) 1528c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_DEAD, dd->queue); 1538c2ecf20Sopenharmony_ci else 1548c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 1558c2ecf20Sopenharmony_ci "%s: dd->queue is NULL\n", __func__); 1568c2ecf20Sopenharmony_ci return true; /* device removed */ 1578c2ecf20Sopenharmony_ci } 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci return false; /* device present */ 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic struct mtip_cmd *mtip_cmd_from_tag(struct driver_data *dd, 1638c2ecf20Sopenharmony_ci unsigned int tag) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci struct blk_mq_hw_ctx *hctx = dd->queue->queue_hw_ctx[0]; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return blk_mq_rq_to_pdu(blk_mq_tag_to_rq(hctx->tags, tag)); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci/* 1718c2ecf20Sopenharmony_ci * Reset the HBA (without sleeping) 1728c2ecf20Sopenharmony_ci * 1738c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 1748c2ecf20Sopenharmony_ci * 1758c2ecf20Sopenharmony_ci * return value 1768c2ecf20Sopenharmony_ci * 0 The reset was successful. 1778c2ecf20Sopenharmony_ci * -1 The HBA Reset bit did not clear. 1788c2ecf20Sopenharmony_ci */ 1798c2ecf20Sopenharmony_cistatic int mtip_hba_reset(struct driver_data *dd) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci unsigned long timeout; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci /* Set the reset bit */ 1848c2ecf20Sopenharmony_ci writel(HOST_RESET, dd->mmio + HOST_CTL); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci /* Flush */ 1878c2ecf20Sopenharmony_ci readl(dd->mmio + HOST_CTL); 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * Spin for up to 10 seconds waiting for reset acknowledgement. Spec 1918c2ecf20Sopenharmony_ci * is 1 sec but in LUN failure conditions, up to 10 secs are required 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(10000); 1948c2ecf20Sopenharmony_ci do { 1958c2ecf20Sopenharmony_ci mdelay(10); 1968c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) 1978c2ecf20Sopenharmony_ci return -1; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci } while ((readl(dd->mmio + HOST_CTL) & HOST_RESET) 2008c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)); 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (readl(dd->mmio + HOST_CTL) & HOST_RESET) 2038c2ecf20Sopenharmony_ci return -1; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return 0; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * Issue a command to the hardware. 2108c2ecf20Sopenharmony_ci * 2118c2ecf20Sopenharmony_ci * Set the appropriate bit in the s_active and Command Issue hardware 2128c2ecf20Sopenharmony_ci * registers, causing hardware command processing to begin. 2138c2ecf20Sopenharmony_ci * 2148c2ecf20Sopenharmony_ci * @port Pointer to the port structure. 2158c2ecf20Sopenharmony_ci * @tag The tag of the command to be issued. 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * return value 2188c2ecf20Sopenharmony_ci * None 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic inline void mtip_issue_ncq_command(struct mtip_port *port, int tag) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci int group = tag >> 5; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* guard SACT and CI registers */ 2258c2ecf20Sopenharmony_ci spin_lock(&port->cmd_issue_lock[group]); 2268c2ecf20Sopenharmony_ci writel((1 << MTIP_TAG_BIT(tag)), 2278c2ecf20Sopenharmony_ci port->s_active[MTIP_TAG_INDEX(tag)]); 2288c2ecf20Sopenharmony_ci writel((1 << MTIP_TAG_BIT(tag)), 2298c2ecf20Sopenharmony_ci port->cmd_issue[MTIP_TAG_INDEX(tag)]); 2308c2ecf20Sopenharmony_ci spin_unlock(&port->cmd_issue_lock[group]); 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci/* 2348c2ecf20Sopenharmony_ci * Enable/disable the reception of FIS 2358c2ecf20Sopenharmony_ci * 2368c2ecf20Sopenharmony_ci * @port Pointer to the port data structure 2378c2ecf20Sopenharmony_ci * @enable 1 to enable, 0 to disable 2388c2ecf20Sopenharmony_ci * 2398c2ecf20Sopenharmony_ci * return value 2408c2ecf20Sopenharmony_ci * Previous state: 1 enabled, 0 disabled 2418c2ecf20Sopenharmony_ci */ 2428c2ecf20Sopenharmony_cistatic int mtip_enable_fis(struct mtip_port *port, int enable) 2438c2ecf20Sopenharmony_ci{ 2448c2ecf20Sopenharmony_ci u32 tmp; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* enable FIS reception */ 2478c2ecf20Sopenharmony_ci tmp = readl(port->mmio + PORT_CMD); 2488c2ecf20Sopenharmony_ci if (enable) 2498c2ecf20Sopenharmony_ci writel(tmp | PORT_CMD_FIS_RX, port->mmio + PORT_CMD); 2508c2ecf20Sopenharmony_ci else 2518c2ecf20Sopenharmony_ci writel(tmp & ~PORT_CMD_FIS_RX, port->mmio + PORT_CMD); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* Flush */ 2548c2ecf20Sopenharmony_ci readl(port->mmio + PORT_CMD); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci return (((tmp & PORT_CMD_FIS_RX) == PORT_CMD_FIS_RX)); 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci/* 2608c2ecf20Sopenharmony_ci * Enable/disable the DMA engine 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * @port Pointer to the port data structure 2638c2ecf20Sopenharmony_ci * @enable 1 to enable, 0 to disable 2648c2ecf20Sopenharmony_ci * 2658c2ecf20Sopenharmony_ci * return value 2668c2ecf20Sopenharmony_ci * Previous state: 1 enabled, 0 disabled. 2678c2ecf20Sopenharmony_ci */ 2688c2ecf20Sopenharmony_cistatic int mtip_enable_engine(struct mtip_port *port, int enable) 2698c2ecf20Sopenharmony_ci{ 2708c2ecf20Sopenharmony_ci u32 tmp; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci /* enable FIS reception */ 2738c2ecf20Sopenharmony_ci tmp = readl(port->mmio + PORT_CMD); 2748c2ecf20Sopenharmony_ci if (enable) 2758c2ecf20Sopenharmony_ci writel(tmp | PORT_CMD_START, port->mmio + PORT_CMD); 2768c2ecf20Sopenharmony_ci else 2778c2ecf20Sopenharmony_ci writel(tmp & ~PORT_CMD_START, port->mmio + PORT_CMD); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci readl(port->mmio + PORT_CMD); 2808c2ecf20Sopenharmony_ci return (((tmp & PORT_CMD_START) == PORT_CMD_START)); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * Enables the port DMA engine and FIS reception. 2858c2ecf20Sopenharmony_ci * 2868c2ecf20Sopenharmony_ci * return value 2878c2ecf20Sopenharmony_ci * None 2888c2ecf20Sopenharmony_ci */ 2898c2ecf20Sopenharmony_cistatic inline void mtip_start_port(struct mtip_port *port) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci /* Enable FIS reception */ 2928c2ecf20Sopenharmony_ci mtip_enable_fis(port, 1); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci /* Enable the DMA engine */ 2958c2ecf20Sopenharmony_ci mtip_enable_engine(port, 1); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci/* 2998c2ecf20Sopenharmony_ci * Deinitialize a port by disabling port interrupts, the DMA engine, 3008c2ecf20Sopenharmony_ci * and FIS reception. 3018c2ecf20Sopenharmony_ci * 3028c2ecf20Sopenharmony_ci * @port Pointer to the port structure 3038c2ecf20Sopenharmony_ci * 3048c2ecf20Sopenharmony_ci * return value 3058c2ecf20Sopenharmony_ci * None 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cistatic inline void mtip_deinit_port(struct mtip_port *port) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci /* Disable interrupts on this port */ 3108c2ecf20Sopenharmony_ci writel(0, port->mmio + PORT_IRQ_MASK); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* Disable the DMA engine */ 3138c2ecf20Sopenharmony_ci mtip_enable_engine(port, 0); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci /* Disable FIS reception */ 3168c2ecf20Sopenharmony_ci mtip_enable_fis(port, 0); 3178c2ecf20Sopenharmony_ci} 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci/* 3208c2ecf20Sopenharmony_ci * Initialize a port. 3218c2ecf20Sopenharmony_ci * 3228c2ecf20Sopenharmony_ci * This function deinitializes the port by calling mtip_deinit_port() and 3238c2ecf20Sopenharmony_ci * then initializes it by setting the command header and RX FIS addresses, 3248c2ecf20Sopenharmony_ci * clearing the SError register and any pending port interrupts before 3258c2ecf20Sopenharmony_ci * re-enabling the default set of port interrupts. 3268c2ecf20Sopenharmony_ci * 3278c2ecf20Sopenharmony_ci * @port Pointer to the port structure. 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * return value 3308c2ecf20Sopenharmony_ci * None 3318c2ecf20Sopenharmony_ci */ 3328c2ecf20Sopenharmony_cistatic void mtip_init_port(struct mtip_port *port) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci int i; 3358c2ecf20Sopenharmony_ci mtip_deinit_port(port); 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci /* Program the command list base and FIS base addresses */ 3388c2ecf20Sopenharmony_ci if (readl(port->dd->mmio + HOST_CAP) & HOST_CAP_64) { 3398c2ecf20Sopenharmony_ci writel((port->command_list_dma >> 16) >> 16, 3408c2ecf20Sopenharmony_ci port->mmio + PORT_LST_ADDR_HI); 3418c2ecf20Sopenharmony_ci writel((port->rxfis_dma >> 16) >> 16, 3428c2ecf20Sopenharmony_ci port->mmio + PORT_FIS_ADDR_HI); 3438c2ecf20Sopenharmony_ci set_bit(MTIP_PF_HOST_CAP_64, &port->flags); 3448c2ecf20Sopenharmony_ci } 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci writel(port->command_list_dma & 0xFFFFFFFF, 3478c2ecf20Sopenharmony_ci port->mmio + PORT_LST_ADDR); 3488c2ecf20Sopenharmony_ci writel(port->rxfis_dma & 0xFFFFFFFF, port->mmio + PORT_FIS_ADDR); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci /* Clear SError */ 3518c2ecf20Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_ERR), port->mmio + PORT_SCR_ERR); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* reset the completed registers.*/ 3548c2ecf20Sopenharmony_ci for (i = 0; i < port->dd->slot_groups; i++) 3558c2ecf20Sopenharmony_ci writel(0xFFFFFFFF, port->completed[i]); 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci /* Clear any pending interrupts for this port */ 3588c2ecf20Sopenharmony_ci writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci /* Clear any pending interrupts on the HBA. */ 3618c2ecf20Sopenharmony_ci writel(readl(port->dd->mmio + HOST_IRQ_STAT), 3628c2ecf20Sopenharmony_ci port->dd->mmio + HOST_IRQ_STAT); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Enable port interrupts */ 3658c2ecf20Sopenharmony_ci writel(DEF_PORT_IRQ, port->mmio + PORT_IRQ_MASK); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci/* 3698c2ecf20Sopenharmony_ci * Restart a port 3708c2ecf20Sopenharmony_ci * 3718c2ecf20Sopenharmony_ci * @port Pointer to the port data structure. 3728c2ecf20Sopenharmony_ci * 3738c2ecf20Sopenharmony_ci * return value 3748c2ecf20Sopenharmony_ci * None 3758c2ecf20Sopenharmony_ci */ 3768c2ecf20Sopenharmony_cistatic void mtip_restart_port(struct mtip_port *port) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci unsigned long timeout; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* Disable the DMA engine */ 3818c2ecf20Sopenharmony_ci mtip_enable_engine(port, 0); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* Chip quirk: wait up to 500ms for PxCMD.CR == 0 */ 3848c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 3858c2ecf20Sopenharmony_ci while ((readl(port->mmio + PORT_CMD) & PORT_CMD_LIST_ON) 3868c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)) 3878c2ecf20Sopenharmony_ci ; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 3908c2ecf20Sopenharmony_ci return; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* 3938c2ecf20Sopenharmony_ci * Chip quirk: escalate to hba reset if 3948c2ecf20Sopenharmony_ci * PxCMD.CR not clear after 500 ms 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ci if (readl(port->mmio + PORT_CMD) & PORT_CMD_LIST_ON) { 3978c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 3988c2ecf20Sopenharmony_ci "PxCMD.CR not clear, escalating reset\n"); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci if (mtip_hba_reset(port->dd)) 4018c2ecf20Sopenharmony_ci dev_err(&port->dd->pdev->dev, 4028c2ecf20Sopenharmony_ci "HBA reset escalation failed.\n"); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci /* 30 ms delay before com reset to quiesce chip */ 4058c2ecf20Sopenharmony_ci mdelay(30); 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "Issuing COM reset\n"); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci /* Set PxSCTL.DET */ 4118c2ecf20Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_CTL) | 4128c2ecf20Sopenharmony_ci 1, port->mmio + PORT_SCR_CTL); 4138c2ecf20Sopenharmony_ci readl(port->mmio + PORT_SCR_CTL); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* Wait 1 ms to quiesce chip function */ 4168c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1); 4178c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) 4188c2ecf20Sopenharmony_ci ; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 4218c2ecf20Sopenharmony_ci return; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci /* Clear PxSCTL.DET */ 4248c2ecf20Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_CTL) & ~1, 4258c2ecf20Sopenharmony_ci port->mmio + PORT_SCR_CTL); 4268c2ecf20Sopenharmony_ci readl(port->mmio + PORT_SCR_CTL); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci /* Wait 500 ms for bit 0 of PORT_SCR_STS to be set */ 4298c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 4308c2ecf20Sopenharmony_ci while (((readl(port->mmio + PORT_SCR_STAT) & 0x01) == 0) 4318c2ecf20Sopenharmony_ci && time_before(jiffies, timeout)) 4328c2ecf20Sopenharmony_ci ; 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 4358c2ecf20Sopenharmony_ci return; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if ((readl(port->mmio + PORT_SCR_STAT) & 0x01) == 0) 4388c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 4398c2ecf20Sopenharmony_ci "COM reset failed\n"); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci mtip_init_port(port); 4428c2ecf20Sopenharmony_ci mtip_start_port(port); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci} 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_cistatic int mtip_device_reset(struct driver_data *dd) 4478c2ecf20Sopenharmony_ci{ 4488c2ecf20Sopenharmony_ci int rv = 0; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(dd->pdev)) 4518c2ecf20Sopenharmony_ci return 0; 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci if (mtip_hba_reset(dd) < 0) 4548c2ecf20Sopenharmony_ci rv = -EFAULT; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci mdelay(1); 4578c2ecf20Sopenharmony_ci mtip_init_port(dd->port); 4588c2ecf20Sopenharmony_ci mtip_start_port(dd->port); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci /* Enable interrupts on the HBA. */ 4618c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 4628c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 4638c2ecf20Sopenharmony_ci return rv; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci/* 4678c2ecf20Sopenharmony_ci * Helper function for tag logging 4688c2ecf20Sopenharmony_ci */ 4698c2ecf20Sopenharmony_cistatic void print_tags(struct driver_data *dd, 4708c2ecf20Sopenharmony_ci char *msg, 4718c2ecf20Sopenharmony_ci unsigned long *tagbits, 4728c2ecf20Sopenharmony_ci int cnt) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci unsigned char tagmap[128]; 4758c2ecf20Sopenharmony_ci int group, tagmap_len = 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci memset(tagmap, 0, sizeof(tagmap)); 4788c2ecf20Sopenharmony_ci for (group = SLOTBITS_IN_LONGS; group > 0; group--) 4798c2ecf20Sopenharmony_ci tagmap_len += sprintf(tagmap + tagmap_len, "%016lX ", 4808c2ecf20Sopenharmony_ci tagbits[group-1]); 4818c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 4828c2ecf20Sopenharmony_ci "%d command(s) %s: tagmap [%s]", cnt, msg, tagmap); 4838c2ecf20Sopenharmony_ci} 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_cistatic int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, 4868c2ecf20Sopenharmony_ci dma_addr_t buffer_dma, unsigned int sectors); 4878c2ecf20Sopenharmony_cistatic int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, 4888c2ecf20Sopenharmony_ci struct smart_attr *attrib); 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_cistatic void mtip_complete_command(struct mtip_cmd *cmd, blk_status_t status) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct request *req = blk_mq_rq_from_pdu(cmd); 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_ci cmd->status = status; 4958c2ecf20Sopenharmony_ci if (likely(!blk_should_fake_timeout(req->q))) 4968c2ecf20Sopenharmony_ci blk_mq_complete_request(req); 4978c2ecf20Sopenharmony_ci} 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/* 5008c2ecf20Sopenharmony_ci * Handle an error. 5018c2ecf20Sopenharmony_ci * 5028c2ecf20Sopenharmony_ci * @dd Pointer to the DRIVER_DATA structure. 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * return value 5058c2ecf20Sopenharmony_ci * None 5068c2ecf20Sopenharmony_ci */ 5078c2ecf20Sopenharmony_cistatic void mtip_handle_tfe(struct driver_data *dd) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci int group, tag, bit, reissue, rv; 5108c2ecf20Sopenharmony_ci struct mtip_port *port; 5118c2ecf20Sopenharmony_ci struct mtip_cmd *cmd; 5128c2ecf20Sopenharmony_ci u32 completed; 5138c2ecf20Sopenharmony_ci struct host_to_dev_fis *fis; 5148c2ecf20Sopenharmony_ci unsigned long tagaccum[SLOTBITS_IN_LONGS]; 5158c2ecf20Sopenharmony_ci unsigned int cmd_cnt = 0; 5168c2ecf20Sopenharmony_ci unsigned char *buf; 5178c2ecf20Sopenharmony_ci char *fail_reason = NULL; 5188c2ecf20Sopenharmony_ci int fail_all_ncq_write = 0, fail_all_ncq_cmds = 0; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, "Taskfile error\n"); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci port = dd->port; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { 5258c2ecf20Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); 5268c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n"); 5278c2ecf20Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_IOERR); 5288c2ecf20Sopenharmony_ci return; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci /* clear the tag accumulator */ 5328c2ecf20Sopenharmony_ci memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Loop through all the groups */ 5358c2ecf20Sopenharmony_ci for (group = 0; group < dd->slot_groups; group++) { 5368c2ecf20Sopenharmony_ci completed = readl(port->completed[group]); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, "g=%u, comp=%x\n", group, completed); 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci /* clear completed status register in the hardware.*/ 5418c2ecf20Sopenharmony_ci writel(completed, port->completed[group]); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci /* Process successfully completed commands */ 5448c2ecf20Sopenharmony_ci for (bit = 0; bit < 32 && completed; bit++) { 5458c2ecf20Sopenharmony_ci if (!(completed & (1<<bit))) 5468c2ecf20Sopenharmony_ci continue; 5478c2ecf20Sopenharmony_ci tag = (group << 5) + bit; 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci /* Skip the internal command slot */ 5508c2ecf20Sopenharmony_ci if (tag == MTIP_TAG_INTERNAL) 5518c2ecf20Sopenharmony_ci continue; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, tag); 5548c2ecf20Sopenharmony_ci mtip_complete_command(cmd, 0); 5558c2ecf20Sopenharmony_ci set_bit(tag, tagaccum); 5568c2ecf20Sopenharmony_ci cmd_cnt++; 5578c2ecf20Sopenharmony_ci } 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci print_tags(dd, "completed (TFE)", tagaccum, cmd_cnt); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci /* Restart the port */ 5638c2ecf20Sopenharmony_ci mdelay(20); 5648c2ecf20Sopenharmony_ci mtip_restart_port(port); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Trying to determine the cause of the error */ 5678c2ecf20Sopenharmony_ci rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, 5688c2ecf20Sopenharmony_ci dd->port->log_buf, 5698c2ecf20Sopenharmony_ci dd->port->log_buf_dma, 1); 5708c2ecf20Sopenharmony_ci if (rv) { 5718c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 5728c2ecf20Sopenharmony_ci "Error in READ LOG EXT (10h) command\n"); 5738c2ecf20Sopenharmony_ci /* non-critical error, don't fail the load */ 5748c2ecf20Sopenharmony_ci } else { 5758c2ecf20Sopenharmony_ci buf = (unsigned char *)dd->port->log_buf; 5768c2ecf20Sopenharmony_ci if (buf[259] & 0x1) { 5778c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 5788c2ecf20Sopenharmony_ci "Write protect bit is set.\n"); 5798c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); 5808c2ecf20Sopenharmony_ci fail_all_ncq_write = 1; 5818c2ecf20Sopenharmony_ci fail_reason = "write protect"; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci if (buf[288] == 0xF7) { 5848c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 5858c2ecf20Sopenharmony_ci "Exceeded Tmax, drive in thermal shutdown.\n"); 5868c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); 5878c2ecf20Sopenharmony_ci fail_all_ncq_cmds = 1; 5888c2ecf20Sopenharmony_ci fail_reason = "thermal shutdown"; 5898c2ecf20Sopenharmony_ci } 5908c2ecf20Sopenharmony_ci if (buf[288] == 0xBF) { 5918c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); 5928c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 5938c2ecf20Sopenharmony_ci "Drive indicates rebuild has failed. Secure erase required.\n"); 5948c2ecf20Sopenharmony_ci fail_all_ncq_cmds = 1; 5958c2ecf20Sopenharmony_ci fail_reason = "rebuild failed"; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci /* clear the tag accumulator */ 6008c2ecf20Sopenharmony_ci memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* Loop through all the groups */ 6038c2ecf20Sopenharmony_ci for (group = 0; group < dd->slot_groups; group++) { 6048c2ecf20Sopenharmony_ci for (bit = 0; bit < 32; bit++) { 6058c2ecf20Sopenharmony_ci reissue = 1; 6068c2ecf20Sopenharmony_ci tag = (group << 5) + bit; 6078c2ecf20Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, tag); 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci fis = (struct host_to_dev_fis *)cmd->command; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci /* Should re-issue? */ 6128c2ecf20Sopenharmony_ci if (tag == MTIP_TAG_INTERNAL || 6138c2ecf20Sopenharmony_ci fis->command == ATA_CMD_SET_FEATURES) 6148c2ecf20Sopenharmony_ci reissue = 0; 6158c2ecf20Sopenharmony_ci else { 6168c2ecf20Sopenharmony_ci if (fail_all_ncq_cmds || 6178c2ecf20Sopenharmony_ci (fail_all_ncq_write && 6188c2ecf20Sopenharmony_ci fis->command == ATA_CMD_FPDMA_WRITE)) { 6198c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 6208c2ecf20Sopenharmony_ci " Fail: %s w/tag %d [%s].\n", 6218c2ecf20Sopenharmony_ci fis->command == ATA_CMD_FPDMA_WRITE ? 6228c2ecf20Sopenharmony_ci "write" : "read", 6238c2ecf20Sopenharmony_ci tag, 6248c2ecf20Sopenharmony_ci fail_reason != NULL ? 6258c2ecf20Sopenharmony_ci fail_reason : "unknown"); 6268c2ecf20Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_MEDIUM); 6278c2ecf20Sopenharmony_ci continue; 6288c2ecf20Sopenharmony_ci } 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci /* 6328c2ecf20Sopenharmony_ci * First check if this command has 6338c2ecf20Sopenharmony_ci * exceeded its retries. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci if (reissue && (cmd->retries-- > 0)) { 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci set_bit(tag, tagaccum); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Re-issue the command. */ 6408c2ecf20Sopenharmony_ci mtip_issue_ncq_command(port, tag); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci continue; 6438c2ecf20Sopenharmony_ci } 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* Retire a command that will not be reissued */ 6468c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 6478c2ecf20Sopenharmony_ci "retiring tag %d\n", tag); 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_IOERR); 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt); 6538c2ecf20Sopenharmony_ci} 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci/* 6568c2ecf20Sopenharmony_ci * Handle a set device bits interrupt 6578c2ecf20Sopenharmony_ci */ 6588c2ecf20Sopenharmony_cistatic inline void mtip_workq_sdbfx(struct mtip_port *port, int group, 6598c2ecf20Sopenharmony_ci u32 completed) 6608c2ecf20Sopenharmony_ci{ 6618c2ecf20Sopenharmony_ci struct driver_data *dd = port->dd; 6628c2ecf20Sopenharmony_ci int tag, bit; 6638c2ecf20Sopenharmony_ci struct mtip_cmd *command; 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (!completed) { 6668c2ecf20Sopenharmony_ci WARN_ON_ONCE(!completed); 6678c2ecf20Sopenharmony_ci return; 6688c2ecf20Sopenharmony_ci } 6698c2ecf20Sopenharmony_ci /* clear completed status register in the hardware.*/ 6708c2ecf20Sopenharmony_ci writel(completed, port->completed[group]); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci /* Process completed commands. */ 6738c2ecf20Sopenharmony_ci for (bit = 0; (bit < 32) && completed; bit++) { 6748c2ecf20Sopenharmony_ci if (completed & 0x01) { 6758c2ecf20Sopenharmony_ci tag = (group << 5) | bit; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci /* skip internal command slot. */ 6788c2ecf20Sopenharmony_ci if (unlikely(tag == MTIP_TAG_INTERNAL)) 6798c2ecf20Sopenharmony_ci continue; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci command = mtip_cmd_from_tag(dd, tag); 6828c2ecf20Sopenharmony_ci mtip_complete_command(command, 0); 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci completed >>= 1; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci /* If last, re-enable interrupts */ 6888c2ecf20Sopenharmony_ci if (atomic_dec_return(&dd->irq_workers_active) == 0) 6898c2ecf20Sopenharmony_ci writel(0xffffffff, dd->mmio + HOST_IRQ_STAT); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * Process legacy pio and d2h interrupts 6948c2ecf20Sopenharmony_ci */ 6958c2ecf20Sopenharmony_cistatic inline void mtip_process_legacy(struct driver_data *dd, u32 port_stat) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 6988c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) && cmd) { 7018c2ecf20Sopenharmony_ci int group = MTIP_TAG_INDEX(MTIP_TAG_INTERNAL); 7028c2ecf20Sopenharmony_ci int status = readl(port->cmd_issue[group]); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (!(status & (1 << MTIP_TAG_BIT(MTIP_TAG_INTERNAL)))) 7058c2ecf20Sopenharmony_ci mtip_complete_command(cmd, 0); 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_ci/* 7108c2ecf20Sopenharmony_ci * Demux and handle errors 7118c2ecf20Sopenharmony_ci */ 7128c2ecf20Sopenharmony_cistatic inline void mtip_process_errors(struct driver_data *dd, u32 port_stat) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_CONNECT)) { 7158c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 7168c2ecf20Sopenharmony_ci "Clearing PxSERR.DIAG.x\n"); 7178c2ecf20Sopenharmony_ci writel((1 << 26), dd->port->mmio + PORT_SCR_ERR); 7188c2ecf20Sopenharmony_ci } 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_PHYRDY)) { 7218c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 7228c2ecf20Sopenharmony_ci "Clearing PxSERR.DIAG.n\n"); 7238c2ecf20Sopenharmony_ci writel((1 << 16), dd->port->mmio + PORT_SCR_ERR); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci if (unlikely(port_stat & ~PORT_IRQ_HANDLED)) { 7278c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 7288c2ecf20Sopenharmony_ci "Port stat errors %x unhandled\n", 7298c2ecf20Sopenharmony_ci (port_stat & ~PORT_IRQ_HANDLED)); 7308c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(dd->pdev)) 7318c2ecf20Sopenharmony_ci return; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci if (likely(port_stat & (PORT_IRQ_TF_ERR | PORT_IRQ_IF_ERR))) { 7348c2ecf20Sopenharmony_ci set_bit(MTIP_PF_EH_ACTIVE_BIT, &dd->port->flags); 7358c2ecf20Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic inline irqreturn_t mtip_handle_irq(struct driver_data *data) 7408c2ecf20Sopenharmony_ci{ 7418c2ecf20Sopenharmony_ci struct driver_data *dd = (struct driver_data *) data; 7428c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 7438c2ecf20Sopenharmony_ci u32 hba_stat, port_stat; 7448c2ecf20Sopenharmony_ci int rv = IRQ_NONE; 7458c2ecf20Sopenharmony_ci int do_irq_enable = 1, i, workers; 7468c2ecf20Sopenharmony_ci struct mtip_work *twork; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci hba_stat = readl(dd->mmio + HOST_IRQ_STAT); 7498c2ecf20Sopenharmony_ci if (hba_stat) { 7508c2ecf20Sopenharmony_ci rv = IRQ_HANDLED; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Acknowledge the interrupt status on the port.*/ 7538c2ecf20Sopenharmony_ci port_stat = readl(port->mmio + PORT_IRQ_STAT); 7548c2ecf20Sopenharmony_ci if (unlikely(port_stat == 0xFFFFFFFF)) { 7558c2ecf20Sopenharmony_ci mtip_check_surprise_removal(dd->pdev); 7568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 7578c2ecf20Sopenharmony_ci } 7588c2ecf20Sopenharmony_ci writel(port_stat, port->mmio + PORT_IRQ_STAT); 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci /* Demux port status */ 7618c2ecf20Sopenharmony_ci if (likely(port_stat & PORT_IRQ_SDB_FIS)) { 7628c2ecf20Sopenharmony_ci do_irq_enable = 0; 7638c2ecf20Sopenharmony_ci WARN_ON_ONCE(atomic_read(&dd->irq_workers_active) != 0); 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci /* Start at 1: group zero is always local? */ 7668c2ecf20Sopenharmony_ci for (i = 0, workers = 0; i < MTIP_MAX_SLOT_GROUPS; 7678c2ecf20Sopenharmony_ci i++) { 7688c2ecf20Sopenharmony_ci twork = &dd->work[i]; 7698c2ecf20Sopenharmony_ci twork->completed = readl(port->completed[i]); 7708c2ecf20Sopenharmony_ci if (twork->completed) 7718c2ecf20Sopenharmony_ci workers++; 7728c2ecf20Sopenharmony_ci } 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci atomic_set(&dd->irq_workers_active, workers); 7758c2ecf20Sopenharmony_ci if (workers) { 7768c2ecf20Sopenharmony_ci for (i = 1; i < MTIP_MAX_SLOT_GROUPS; i++) { 7778c2ecf20Sopenharmony_ci twork = &dd->work[i]; 7788c2ecf20Sopenharmony_ci if (twork->completed) 7798c2ecf20Sopenharmony_ci queue_work_on( 7808c2ecf20Sopenharmony_ci twork->cpu_binding, 7818c2ecf20Sopenharmony_ci dd->isr_workq, 7828c2ecf20Sopenharmony_ci &twork->work); 7838c2ecf20Sopenharmony_ci } 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci if (likely(dd->work[0].completed)) 7868c2ecf20Sopenharmony_ci mtip_workq_sdbfx(port, 0, 7878c2ecf20Sopenharmony_ci dd->work[0].completed); 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci } else { 7908c2ecf20Sopenharmony_ci /* 7918c2ecf20Sopenharmony_ci * Chip quirk: SDB interrupt but nothing 7928c2ecf20Sopenharmony_ci * to complete 7938c2ecf20Sopenharmony_ci */ 7948c2ecf20Sopenharmony_ci do_irq_enable = 1; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_ERR)) { 7998c2ecf20Sopenharmony_ci if (unlikely(mtip_check_surprise_removal(dd->pdev))) { 8008c2ecf20Sopenharmony_ci /* don't proceed further */ 8018c2ecf20Sopenharmony_ci return IRQ_HANDLED; 8028c2ecf20Sopenharmony_ci } 8038c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 8048c2ecf20Sopenharmony_ci &dd->dd_flag)) 8058c2ecf20Sopenharmony_ci return rv; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci mtip_process_errors(dd, port_stat & PORT_IRQ_ERR); 8088c2ecf20Sopenharmony_ci } 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_LEGACY)) 8118c2ecf20Sopenharmony_ci mtip_process_legacy(dd, port_stat & PORT_IRQ_LEGACY); 8128c2ecf20Sopenharmony_ci } 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci /* acknowledge interrupt */ 8158c2ecf20Sopenharmony_ci if (unlikely(do_irq_enable)) 8168c2ecf20Sopenharmony_ci writel(hba_stat, dd->mmio + HOST_IRQ_STAT); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci return rv; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci/* 8228c2ecf20Sopenharmony_ci * HBA interrupt subroutine. 8238c2ecf20Sopenharmony_ci * 8248c2ecf20Sopenharmony_ci * @irq IRQ number. 8258c2ecf20Sopenharmony_ci * @instance Pointer to the driver data structure. 8268c2ecf20Sopenharmony_ci * 8278c2ecf20Sopenharmony_ci * return value 8288c2ecf20Sopenharmony_ci * IRQ_HANDLED A HBA interrupt was pending and handled. 8298c2ecf20Sopenharmony_ci * IRQ_NONE This interrupt was not for the HBA. 8308c2ecf20Sopenharmony_ci */ 8318c2ecf20Sopenharmony_cistatic irqreturn_t mtip_irq_handler(int irq, void *instance) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct driver_data *dd = instance; 8348c2ecf20Sopenharmony_ci 8358c2ecf20Sopenharmony_ci return mtip_handle_irq(dd); 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic void mtip_issue_non_ncq_command(struct mtip_port *port, int tag) 8398c2ecf20Sopenharmony_ci{ 8408c2ecf20Sopenharmony_ci writel(1 << MTIP_TAG_BIT(tag), port->cmd_issue[MTIP_TAG_INDEX(tag)]); 8418c2ecf20Sopenharmony_ci} 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_cistatic bool mtip_pause_ncq(struct mtip_port *port, 8448c2ecf20Sopenharmony_ci struct host_to_dev_fis *fis) 8458c2ecf20Sopenharmony_ci{ 8468c2ecf20Sopenharmony_ci unsigned long task_file_data; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci task_file_data = readl(port->mmio+PORT_TFDATA); 8498c2ecf20Sopenharmony_ci if ((task_file_data & 1)) 8508c2ecf20Sopenharmony_ci return false; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (fis->command == ATA_CMD_SEC_ERASE_PREP) { 8538c2ecf20Sopenharmony_ci port->ic_pause_timer = jiffies; 8548c2ecf20Sopenharmony_ci return true; 8558c2ecf20Sopenharmony_ci } else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) && 8568c2ecf20Sopenharmony_ci (fis->features == 0x03)) { 8578c2ecf20Sopenharmony_ci set_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); 8588c2ecf20Sopenharmony_ci port->ic_pause_timer = jiffies; 8598c2ecf20Sopenharmony_ci return true; 8608c2ecf20Sopenharmony_ci } else if ((fis->command == ATA_CMD_SEC_ERASE_UNIT) || 8618c2ecf20Sopenharmony_ci ((fis->command == 0xFC) && 8628c2ecf20Sopenharmony_ci (fis->features == 0x27 || fis->features == 0x72 || 8638c2ecf20Sopenharmony_ci fis->features == 0x62 || fis->features == 0x26))) { 8648c2ecf20Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 8658c2ecf20Sopenharmony_ci clear_bit(MTIP_DDF_REBUILD_FAILED_BIT, &port->dd->dd_flag); 8668c2ecf20Sopenharmony_ci /* Com reset after secure erase or lowlevel format */ 8678c2ecf20Sopenharmony_ci mtip_restart_port(port); 8688c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); 8698c2ecf20Sopenharmony_ci return false; 8708c2ecf20Sopenharmony_ci } 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci return false; 8738c2ecf20Sopenharmony_ci} 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistatic bool mtip_commands_active(struct mtip_port *port) 8768c2ecf20Sopenharmony_ci{ 8778c2ecf20Sopenharmony_ci unsigned int active; 8788c2ecf20Sopenharmony_ci unsigned int n; 8798c2ecf20Sopenharmony_ci 8808c2ecf20Sopenharmony_ci /* 8818c2ecf20Sopenharmony_ci * Ignore s_active bit 0 of array element 0. 8828c2ecf20Sopenharmony_ci * This bit will always be set 8838c2ecf20Sopenharmony_ci */ 8848c2ecf20Sopenharmony_ci active = readl(port->s_active[0]) & 0xFFFFFFFE; 8858c2ecf20Sopenharmony_ci for (n = 1; n < port->dd->slot_groups; n++) 8868c2ecf20Sopenharmony_ci active |= readl(port->s_active[n]); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return active != 0; 8898c2ecf20Sopenharmony_ci} 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci/* 8928c2ecf20Sopenharmony_ci * Wait for port to quiesce 8938c2ecf20Sopenharmony_ci * 8948c2ecf20Sopenharmony_ci * @port Pointer to port data structure 8958c2ecf20Sopenharmony_ci * @timeout Max duration to wait (ms) 8968c2ecf20Sopenharmony_ci * 8978c2ecf20Sopenharmony_ci * return value 8988c2ecf20Sopenharmony_ci * 0 Success 8998c2ecf20Sopenharmony_ci * -EBUSY Commands still active 9008c2ecf20Sopenharmony_ci */ 9018c2ecf20Sopenharmony_cistatic int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci unsigned long to; 9048c2ecf20Sopenharmony_ci bool active = true; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci blk_mq_quiesce_queue(port->dd->queue); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci to = jiffies + msecs_to_jiffies(timeout); 9098c2ecf20Sopenharmony_ci do { 9108c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags) && 9118c2ecf20Sopenharmony_ci test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { 9128c2ecf20Sopenharmony_ci msleep(20); 9138c2ecf20Sopenharmony_ci continue; /* svc thd is actively issuing commands */ 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci msleep(100); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(port->dd->pdev)) 9198c2ecf20Sopenharmony_ci goto err_fault; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci active = mtip_commands_active(port); 9228c2ecf20Sopenharmony_ci if (!active) 9238c2ecf20Sopenharmony_ci break; 9248c2ecf20Sopenharmony_ci } while (time_before(jiffies, to)); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(port->dd->queue); 9278c2ecf20Sopenharmony_ci return active ? -EBUSY : 0; 9288c2ecf20Sopenharmony_cierr_fault: 9298c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(port->dd->queue); 9308c2ecf20Sopenharmony_ci return -EFAULT; 9318c2ecf20Sopenharmony_ci} 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_cistruct mtip_int_cmd { 9348c2ecf20Sopenharmony_ci int fis_len; 9358c2ecf20Sopenharmony_ci dma_addr_t buffer; 9368c2ecf20Sopenharmony_ci int buf_len; 9378c2ecf20Sopenharmony_ci u32 opts; 9388c2ecf20Sopenharmony_ci}; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci/* 9418c2ecf20Sopenharmony_ci * Execute an internal command and wait for the completion. 9428c2ecf20Sopenharmony_ci * 9438c2ecf20Sopenharmony_ci * @port Pointer to the port data structure. 9448c2ecf20Sopenharmony_ci * @fis Pointer to the FIS that describes the command. 9458c2ecf20Sopenharmony_ci * @fis_len Length in WORDS of the FIS. 9468c2ecf20Sopenharmony_ci * @buffer DMA accessible for command data. 9478c2ecf20Sopenharmony_ci * @buf_len Length, in bytes, of the data buffer. 9488c2ecf20Sopenharmony_ci * @opts Command header options, excluding the FIS length 9498c2ecf20Sopenharmony_ci * and the number of PRD entries. 9508c2ecf20Sopenharmony_ci * @timeout Time in ms to wait for the command to complete. 9518c2ecf20Sopenharmony_ci * 9528c2ecf20Sopenharmony_ci * return value 9538c2ecf20Sopenharmony_ci * 0 Command completed successfully. 9548c2ecf20Sopenharmony_ci * -EFAULT The buffer address is not correctly aligned. 9558c2ecf20Sopenharmony_ci * -EBUSY Internal command or other IO in progress. 9568c2ecf20Sopenharmony_ci * -EAGAIN Time out waiting for command to complete. 9578c2ecf20Sopenharmony_ci */ 9588c2ecf20Sopenharmony_cistatic int mtip_exec_internal_command(struct mtip_port *port, 9598c2ecf20Sopenharmony_ci struct host_to_dev_fis *fis, 9608c2ecf20Sopenharmony_ci int fis_len, 9618c2ecf20Sopenharmony_ci dma_addr_t buffer, 9628c2ecf20Sopenharmony_ci int buf_len, 9638c2ecf20Sopenharmony_ci u32 opts, 9648c2ecf20Sopenharmony_ci unsigned long timeout) 9658c2ecf20Sopenharmony_ci{ 9668c2ecf20Sopenharmony_ci struct mtip_cmd *int_cmd; 9678c2ecf20Sopenharmony_ci struct driver_data *dd = port->dd; 9688c2ecf20Sopenharmony_ci struct request *rq; 9698c2ecf20Sopenharmony_ci struct mtip_int_cmd icmd = { 9708c2ecf20Sopenharmony_ci .fis_len = fis_len, 9718c2ecf20Sopenharmony_ci .buffer = buffer, 9728c2ecf20Sopenharmony_ci .buf_len = buf_len, 9738c2ecf20Sopenharmony_ci .opts = opts 9748c2ecf20Sopenharmony_ci }; 9758c2ecf20Sopenharmony_ci int rv = 0; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci /* Make sure the buffer is 8 byte aligned. This is asic specific. */ 9788c2ecf20Sopenharmony_ci if (buffer & 0x00000007) { 9798c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, "SG buffer is not 8 byte aligned\n"); 9808c2ecf20Sopenharmony_ci return -EFAULT; 9818c2ecf20Sopenharmony_ci } 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(dd->pdev)) 9848c2ecf20Sopenharmony_ci return -EFAULT; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci rq = blk_mq_alloc_request(dd->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_RESERVED); 9878c2ecf20Sopenharmony_ci if (IS_ERR(rq)) { 9888c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME "Unable to allocate tag for PIO cmd\n"); 9898c2ecf20Sopenharmony_ci return -EFAULT; 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci if (fis->command == ATA_CMD_SEC_ERASE_PREP) 9958c2ecf20Sopenharmony_ci set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci if (fis->command != ATA_CMD_STANDBYNOW1) { 10008c2ecf20Sopenharmony_ci /* wait for io to complete if non atomic */ 10018c2ecf20Sopenharmony_ci if (mtip_quiesce_io(port, MTIP_QUIESCE_IO_TIMEOUT_MS) < 0) { 10028c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, "Failed to quiesce IO\n"); 10038c2ecf20Sopenharmony_ci blk_mq_free_request(rq); 10048c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 10058c2ecf20Sopenharmony_ci wake_up_interruptible(&port->svc_wait); 10068c2ecf20Sopenharmony_ci return -EBUSY; 10078c2ecf20Sopenharmony_ci } 10088c2ecf20Sopenharmony_ci } 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* Copy the command to the command table */ 10118c2ecf20Sopenharmony_ci int_cmd = blk_mq_rq_to_pdu(rq); 10128c2ecf20Sopenharmony_ci int_cmd->icmd = &icmd; 10138c2ecf20Sopenharmony_ci memcpy(int_cmd->command, fis, fis_len*4); 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci rq->timeout = timeout; 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci /* insert request and run queue */ 10188c2ecf20Sopenharmony_ci blk_execute_rq(rq->q, NULL, rq, true); 10198c2ecf20Sopenharmony_ci 10208c2ecf20Sopenharmony_ci if (int_cmd->status) { 10218c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, "Internal command [%02X] failed %d\n", 10228c2ecf20Sopenharmony_ci fis->command, int_cmd->status); 10238c2ecf20Sopenharmony_ci rv = -EIO; 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(dd->pdev) || 10268c2ecf20Sopenharmony_ci test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 10278c2ecf20Sopenharmony_ci &dd->dd_flag)) { 10288c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 10298c2ecf20Sopenharmony_ci "Internal command [%02X] wait returned due to SR\n", 10308c2ecf20Sopenharmony_ci fis->command); 10318c2ecf20Sopenharmony_ci rv = -ENXIO; 10328c2ecf20Sopenharmony_ci goto exec_ic_exit; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci mtip_device_reset(dd); /* recover from timeout issue */ 10358c2ecf20Sopenharmony_ci rv = -EAGAIN; 10368c2ecf20Sopenharmony_ci goto exec_ic_exit; 10378c2ecf20Sopenharmony_ci } 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci if (readl(port->cmd_issue[MTIP_TAG_INDEX(MTIP_TAG_INTERNAL)]) 10408c2ecf20Sopenharmony_ci & (1 << MTIP_TAG_BIT(MTIP_TAG_INTERNAL))) { 10418c2ecf20Sopenharmony_ci rv = -ENXIO; 10428c2ecf20Sopenharmony_ci if (!test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) { 10438c2ecf20Sopenharmony_ci mtip_device_reset(dd); 10448c2ecf20Sopenharmony_ci rv = -EAGAIN; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci } 10478c2ecf20Sopenharmony_ciexec_ic_exit: 10488c2ecf20Sopenharmony_ci /* Clear the allocated and active bits for the internal command. */ 10498c2ecf20Sopenharmony_ci blk_mq_free_request(rq); 10508c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 10518c2ecf20Sopenharmony_ci if (rv >= 0 && mtip_pause_ncq(port, fis)) { 10528c2ecf20Sopenharmony_ci /* NCQ paused */ 10538c2ecf20Sopenharmony_ci return rv; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci wake_up_interruptible(&port->svc_wait); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci return rv; 10588c2ecf20Sopenharmony_ci} 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci/* 10618c2ecf20Sopenharmony_ci * Byte-swap ATA ID strings. 10628c2ecf20Sopenharmony_ci * 10638c2ecf20Sopenharmony_ci * ATA identify data contains strings in byte-swapped 16-bit words. 10648c2ecf20Sopenharmony_ci * They must be swapped (on all architectures) to be usable as C strings. 10658c2ecf20Sopenharmony_ci * This function swaps bytes in-place. 10668c2ecf20Sopenharmony_ci * 10678c2ecf20Sopenharmony_ci * @buf The buffer location of the string 10688c2ecf20Sopenharmony_ci * @len The number of bytes to swap 10698c2ecf20Sopenharmony_ci * 10708c2ecf20Sopenharmony_ci * return value 10718c2ecf20Sopenharmony_ci * None 10728c2ecf20Sopenharmony_ci */ 10738c2ecf20Sopenharmony_cistatic inline void ata_swap_string(u16 *buf, unsigned int len) 10748c2ecf20Sopenharmony_ci{ 10758c2ecf20Sopenharmony_ci int i; 10768c2ecf20Sopenharmony_ci for (i = 0; i < (len/2); i++) 10778c2ecf20Sopenharmony_ci be16_to_cpus(&buf[i]); 10788c2ecf20Sopenharmony_ci} 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_cistatic void mtip_set_timeout(struct driver_data *dd, 10818c2ecf20Sopenharmony_ci struct host_to_dev_fis *fis, 10828c2ecf20Sopenharmony_ci unsigned int *timeout, u8 erasemode) 10838c2ecf20Sopenharmony_ci{ 10848c2ecf20Sopenharmony_ci switch (fis->command) { 10858c2ecf20Sopenharmony_ci case ATA_CMD_DOWNLOAD_MICRO: 10868c2ecf20Sopenharmony_ci *timeout = 120000; /* 2 minutes */ 10878c2ecf20Sopenharmony_ci break; 10888c2ecf20Sopenharmony_ci case ATA_CMD_SEC_ERASE_UNIT: 10898c2ecf20Sopenharmony_ci case 0xFC: 10908c2ecf20Sopenharmony_ci if (erasemode) 10918c2ecf20Sopenharmony_ci *timeout = ((*(dd->port->identify + 90) * 2) * 60000); 10928c2ecf20Sopenharmony_ci else 10938c2ecf20Sopenharmony_ci *timeout = ((*(dd->port->identify + 89) * 2) * 60000); 10948c2ecf20Sopenharmony_ci break; 10958c2ecf20Sopenharmony_ci case ATA_CMD_STANDBYNOW1: 10968c2ecf20Sopenharmony_ci *timeout = 120000; /* 2 minutes */ 10978c2ecf20Sopenharmony_ci break; 10988c2ecf20Sopenharmony_ci case 0xF7: 10998c2ecf20Sopenharmony_ci case 0xFA: 11008c2ecf20Sopenharmony_ci *timeout = 60000; /* 60 seconds */ 11018c2ecf20Sopenharmony_ci break; 11028c2ecf20Sopenharmony_ci case ATA_CMD_SMART: 11038c2ecf20Sopenharmony_ci *timeout = 15000; /* 15 seconds */ 11048c2ecf20Sopenharmony_ci break; 11058c2ecf20Sopenharmony_ci default: 11068c2ecf20Sopenharmony_ci *timeout = MTIP_IOCTL_CMD_TIMEOUT_MS; 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci} 11108c2ecf20Sopenharmony_ci 11118c2ecf20Sopenharmony_ci/* 11128c2ecf20Sopenharmony_ci * Request the device identity information. 11138c2ecf20Sopenharmony_ci * 11148c2ecf20Sopenharmony_ci * If a user space buffer is not specified, i.e. is NULL, the 11158c2ecf20Sopenharmony_ci * identify information is still read from the drive and placed 11168c2ecf20Sopenharmony_ci * into the identify data buffer (@e port->identify) in the 11178c2ecf20Sopenharmony_ci * port data structure. 11188c2ecf20Sopenharmony_ci * When the identify buffer contains valid identify information @e 11198c2ecf20Sopenharmony_ci * port->identify_valid is non-zero. 11208c2ecf20Sopenharmony_ci * 11218c2ecf20Sopenharmony_ci * @port Pointer to the port structure. 11228c2ecf20Sopenharmony_ci * @user_buffer A user space buffer where the identify data should be 11238c2ecf20Sopenharmony_ci * copied. 11248c2ecf20Sopenharmony_ci * 11258c2ecf20Sopenharmony_ci * return value 11268c2ecf20Sopenharmony_ci * 0 Command completed successfully. 11278c2ecf20Sopenharmony_ci * -EFAULT An error occurred while coping data to the user buffer. 11288c2ecf20Sopenharmony_ci * -1 Command failed. 11298c2ecf20Sopenharmony_ci */ 11308c2ecf20Sopenharmony_cistatic int mtip_get_identify(struct mtip_port *port, void __user *user_buffer) 11318c2ecf20Sopenharmony_ci{ 11328c2ecf20Sopenharmony_ci int rv = 0; 11338c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 11368c2ecf20Sopenharmony_ci return -EFAULT; 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_ci /* Build the FIS. */ 11398c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 11408c2ecf20Sopenharmony_ci fis.type = 0x27; 11418c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 11428c2ecf20Sopenharmony_ci fis.command = ATA_CMD_ID_ATA; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci /* Set the identify information as invalid. */ 11458c2ecf20Sopenharmony_ci port->identify_valid = 0; 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci /* Clear the identify information. */ 11488c2ecf20Sopenharmony_ci memset(port->identify, 0, sizeof(u16) * ATA_ID_WORDS); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci /* Execute the command. */ 11518c2ecf20Sopenharmony_ci if (mtip_exec_internal_command(port, 11528c2ecf20Sopenharmony_ci &fis, 11538c2ecf20Sopenharmony_ci 5, 11548c2ecf20Sopenharmony_ci port->identify_dma, 11558c2ecf20Sopenharmony_ci sizeof(u16) * ATA_ID_WORDS, 11568c2ecf20Sopenharmony_ci 0, 11578c2ecf20Sopenharmony_ci MTIP_INT_CMD_TIMEOUT_MS) 11588c2ecf20Sopenharmony_ci < 0) { 11598c2ecf20Sopenharmony_ci rv = -1; 11608c2ecf20Sopenharmony_ci goto out; 11618c2ecf20Sopenharmony_ci } 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci /* 11648c2ecf20Sopenharmony_ci * Perform any necessary byte-swapping. Yes, the kernel does in fact 11658c2ecf20Sopenharmony_ci * perform field-sensitive swapping on the string fields. 11668c2ecf20Sopenharmony_ci * See the kernel use of ata_id_string() for proof of this. 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ci#ifdef __LITTLE_ENDIAN 11698c2ecf20Sopenharmony_ci ata_swap_string(port->identify + 27, 40); /* model string*/ 11708c2ecf20Sopenharmony_ci ata_swap_string(port->identify + 23, 8); /* firmware string*/ 11718c2ecf20Sopenharmony_ci ata_swap_string(port->identify + 10, 20); /* serial# string*/ 11728c2ecf20Sopenharmony_ci#else 11738c2ecf20Sopenharmony_ci { 11748c2ecf20Sopenharmony_ci int i; 11758c2ecf20Sopenharmony_ci for (i = 0; i < ATA_ID_WORDS; i++) 11768c2ecf20Sopenharmony_ci port->identify[i] = le16_to_cpu(port->identify[i]); 11778c2ecf20Sopenharmony_ci } 11788c2ecf20Sopenharmony_ci#endif 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci /* Check security locked state */ 11818c2ecf20Sopenharmony_ci if (port->identify[128] & 0x4) 11828c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 11838c2ecf20Sopenharmony_ci else 11848c2ecf20Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci /* Set the identify buffer as valid. */ 11878c2ecf20Sopenharmony_ci port->identify_valid = 1; 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci if (user_buffer) { 11908c2ecf20Sopenharmony_ci if (copy_to_user( 11918c2ecf20Sopenharmony_ci user_buffer, 11928c2ecf20Sopenharmony_ci port->identify, 11938c2ecf20Sopenharmony_ci ATA_ID_WORDS * sizeof(u16))) { 11948c2ecf20Sopenharmony_ci rv = -EFAULT; 11958c2ecf20Sopenharmony_ci goto out; 11968c2ecf20Sopenharmony_ci } 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ciout: 12008c2ecf20Sopenharmony_ci return rv; 12018c2ecf20Sopenharmony_ci} 12028c2ecf20Sopenharmony_ci 12038c2ecf20Sopenharmony_ci/* 12048c2ecf20Sopenharmony_ci * Issue a standby immediate command to the device. 12058c2ecf20Sopenharmony_ci * 12068c2ecf20Sopenharmony_ci * @port Pointer to the port structure. 12078c2ecf20Sopenharmony_ci * 12088c2ecf20Sopenharmony_ci * return value 12098c2ecf20Sopenharmony_ci * 0 Command was executed successfully. 12108c2ecf20Sopenharmony_ci * -1 An error occurred while executing the command. 12118c2ecf20Sopenharmony_ci */ 12128c2ecf20Sopenharmony_cistatic int mtip_standby_immediate(struct mtip_port *port) 12138c2ecf20Sopenharmony_ci{ 12148c2ecf20Sopenharmony_ci int rv; 12158c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 12168c2ecf20Sopenharmony_ci unsigned long start; 12178c2ecf20Sopenharmony_ci unsigned int timeout; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci /* Build the FIS. */ 12208c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 12218c2ecf20Sopenharmony_ci fis.type = 0x27; 12228c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 12238c2ecf20Sopenharmony_ci fis.command = ATA_CMD_STANDBYNOW1; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &timeout, 0); 12268c2ecf20Sopenharmony_ci 12278c2ecf20Sopenharmony_ci start = jiffies; 12288c2ecf20Sopenharmony_ci rv = mtip_exec_internal_command(port, 12298c2ecf20Sopenharmony_ci &fis, 12308c2ecf20Sopenharmony_ci 5, 12318c2ecf20Sopenharmony_ci 0, 12328c2ecf20Sopenharmony_ci 0, 12338c2ecf20Sopenharmony_ci 0, 12348c2ecf20Sopenharmony_ci timeout); 12358c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME "Time taken to complete standby cmd: %d ms\n", 12368c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - start)); 12378c2ecf20Sopenharmony_ci if (rv) 12388c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 12398c2ecf20Sopenharmony_ci "STANDBY IMMEDIATE command failed.\n"); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci return rv; 12428c2ecf20Sopenharmony_ci} 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci/* 12458c2ecf20Sopenharmony_ci * Issue a READ LOG EXT command to the device. 12468c2ecf20Sopenharmony_ci * 12478c2ecf20Sopenharmony_ci * @port pointer to the port structure. 12488c2ecf20Sopenharmony_ci * @page page number to fetch 12498c2ecf20Sopenharmony_ci * @buffer pointer to buffer 12508c2ecf20Sopenharmony_ci * @buffer_dma dma address corresponding to @buffer 12518c2ecf20Sopenharmony_ci * @sectors page length to fetch, in sectors 12528c2ecf20Sopenharmony_ci * 12538c2ecf20Sopenharmony_ci * return value 12548c2ecf20Sopenharmony_ci * @rv return value from mtip_exec_internal_command() 12558c2ecf20Sopenharmony_ci */ 12568c2ecf20Sopenharmony_cistatic int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, 12578c2ecf20Sopenharmony_ci dma_addr_t buffer_dma, unsigned int sectors) 12588c2ecf20Sopenharmony_ci{ 12598c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 12628c2ecf20Sopenharmony_ci fis.type = 0x27; 12638c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 12648c2ecf20Sopenharmony_ci fis.command = ATA_CMD_READ_LOG_EXT; 12658c2ecf20Sopenharmony_ci fis.sect_count = sectors & 0xFF; 12668c2ecf20Sopenharmony_ci fis.sect_cnt_ex = (sectors >> 8) & 0xFF; 12678c2ecf20Sopenharmony_ci fis.lba_low = page; 12688c2ecf20Sopenharmony_ci fis.lba_mid = 0; 12698c2ecf20Sopenharmony_ci fis.device = ATA_DEVICE_OBS; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci memset(buffer, 0, sectors * ATA_SECT_SIZE); 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci return mtip_exec_internal_command(port, 12748c2ecf20Sopenharmony_ci &fis, 12758c2ecf20Sopenharmony_ci 5, 12768c2ecf20Sopenharmony_ci buffer_dma, 12778c2ecf20Sopenharmony_ci sectors * ATA_SECT_SIZE, 12788c2ecf20Sopenharmony_ci 0, 12798c2ecf20Sopenharmony_ci MTIP_INT_CMD_TIMEOUT_MS); 12808c2ecf20Sopenharmony_ci} 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci/* 12838c2ecf20Sopenharmony_ci * Issue a SMART READ DATA command to the device. 12848c2ecf20Sopenharmony_ci * 12858c2ecf20Sopenharmony_ci * @port pointer to the port structure. 12868c2ecf20Sopenharmony_ci * @buffer pointer to buffer 12878c2ecf20Sopenharmony_ci * @buffer_dma dma address corresponding to @buffer 12888c2ecf20Sopenharmony_ci * 12898c2ecf20Sopenharmony_ci * return value 12908c2ecf20Sopenharmony_ci * @rv return value from mtip_exec_internal_command() 12918c2ecf20Sopenharmony_ci */ 12928c2ecf20Sopenharmony_cistatic int mtip_get_smart_data(struct mtip_port *port, u8 *buffer, 12938c2ecf20Sopenharmony_ci dma_addr_t buffer_dma) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 12988c2ecf20Sopenharmony_ci fis.type = 0x27; 12998c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 13008c2ecf20Sopenharmony_ci fis.command = ATA_CMD_SMART; 13018c2ecf20Sopenharmony_ci fis.features = 0xD0; 13028c2ecf20Sopenharmony_ci fis.sect_count = 1; 13038c2ecf20Sopenharmony_ci fis.lba_mid = 0x4F; 13048c2ecf20Sopenharmony_ci fis.lba_hi = 0xC2; 13058c2ecf20Sopenharmony_ci fis.device = ATA_DEVICE_OBS; 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci return mtip_exec_internal_command(port, 13088c2ecf20Sopenharmony_ci &fis, 13098c2ecf20Sopenharmony_ci 5, 13108c2ecf20Sopenharmony_ci buffer_dma, 13118c2ecf20Sopenharmony_ci ATA_SECT_SIZE, 13128c2ecf20Sopenharmony_ci 0, 13138c2ecf20Sopenharmony_ci 15000); 13148c2ecf20Sopenharmony_ci} 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci/* 13178c2ecf20Sopenharmony_ci * Get the value of a smart attribute 13188c2ecf20Sopenharmony_ci * 13198c2ecf20Sopenharmony_ci * @port pointer to the port structure 13208c2ecf20Sopenharmony_ci * @id attribute number 13218c2ecf20Sopenharmony_ci * @attrib pointer to return attrib information corresponding to @id 13228c2ecf20Sopenharmony_ci * 13238c2ecf20Sopenharmony_ci * return value 13248c2ecf20Sopenharmony_ci * -EINVAL NULL buffer passed or unsupported attribute @id. 13258c2ecf20Sopenharmony_ci * -EPERM Identify data not valid, SMART not supported or not enabled 13268c2ecf20Sopenharmony_ci */ 13278c2ecf20Sopenharmony_cistatic int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, 13288c2ecf20Sopenharmony_ci struct smart_attr *attrib) 13298c2ecf20Sopenharmony_ci{ 13308c2ecf20Sopenharmony_ci int rv, i; 13318c2ecf20Sopenharmony_ci struct smart_attr *pattr; 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci if (!attrib) 13348c2ecf20Sopenharmony_ci return -EINVAL; 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci if (!port->identify_valid) { 13378c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "IDENTIFY DATA not valid\n"); 13388c2ecf20Sopenharmony_ci return -EPERM; 13398c2ecf20Sopenharmony_ci } 13408c2ecf20Sopenharmony_ci if (!(port->identify[82] & 0x1)) { 13418c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "SMART not supported\n"); 13428c2ecf20Sopenharmony_ci return -EPERM; 13438c2ecf20Sopenharmony_ci } 13448c2ecf20Sopenharmony_ci if (!(port->identify[85] & 0x1)) { 13458c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "SMART not enabled\n"); 13468c2ecf20Sopenharmony_ci return -EPERM; 13478c2ecf20Sopenharmony_ci } 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci memset(port->smart_buf, 0, ATA_SECT_SIZE); 13508c2ecf20Sopenharmony_ci rv = mtip_get_smart_data(port, port->smart_buf, port->smart_buf_dma); 13518c2ecf20Sopenharmony_ci if (rv) { 13528c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "Failed to ge SMART data\n"); 13538c2ecf20Sopenharmony_ci return rv; 13548c2ecf20Sopenharmony_ci } 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci pattr = (struct smart_attr *)(port->smart_buf + 2); 13578c2ecf20Sopenharmony_ci for (i = 0; i < 29; i++, pattr++) 13588c2ecf20Sopenharmony_ci if (pattr->attr_id == id) { 13598c2ecf20Sopenharmony_ci memcpy(attrib, pattr, sizeof(struct smart_attr)); 13608c2ecf20Sopenharmony_ci break; 13618c2ecf20Sopenharmony_ci } 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci if (i == 29) { 13648c2ecf20Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 13658c2ecf20Sopenharmony_ci "Query for invalid SMART attribute ID\n"); 13668c2ecf20Sopenharmony_ci rv = -EINVAL; 13678c2ecf20Sopenharmony_ci } 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_ci return rv; 13708c2ecf20Sopenharmony_ci} 13718c2ecf20Sopenharmony_ci 13728c2ecf20Sopenharmony_ci/* 13738c2ecf20Sopenharmony_ci * Get the drive capacity. 13748c2ecf20Sopenharmony_ci * 13758c2ecf20Sopenharmony_ci * @dd Pointer to the device data structure. 13768c2ecf20Sopenharmony_ci * @sectors Pointer to the variable that will receive the sector count. 13778c2ecf20Sopenharmony_ci * 13788c2ecf20Sopenharmony_ci * return value 13798c2ecf20Sopenharmony_ci * 1 Capacity was returned successfully. 13808c2ecf20Sopenharmony_ci * 0 The identify information is invalid. 13818c2ecf20Sopenharmony_ci */ 13828c2ecf20Sopenharmony_cistatic bool mtip_hw_get_capacity(struct driver_data *dd, sector_t *sectors) 13838c2ecf20Sopenharmony_ci{ 13848c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 13858c2ecf20Sopenharmony_ci u64 total, raw0, raw1, raw2, raw3; 13868c2ecf20Sopenharmony_ci raw0 = port->identify[100]; 13878c2ecf20Sopenharmony_ci raw1 = port->identify[101]; 13888c2ecf20Sopenharmony_ci raw2 = port->identify[102]; 13898c2ecf20Sopenharmony_ci raw3 = port->identify[103]; 13908c2ecf20Sopenharmony_ci total = raw0 | raw1<<16 | raw2<<32 | raw3<<48; 13918c2ecf20Sopenharmony_ci *sectors = total; 13928c2ecf20Sopenharmony_ci return (bool) !!port->identify_valid; 13938c2ecf20Sopenharmony_ci} 13948c2ecf20Sopenharmony_ci 13958c2ecf20Sopenharmony_ci/* 13968c2ecf20Sopenharmony_ci * Display the identify command data. 13978c2ecf20Sopenharmony_ci * 13988c2ecf20Sopenharmony_ci * @port Pointer to the port data structure. 13998c2ecf20Sopenharmony_ci * 14008c2ecf20Sopenharmony_ci * return value 14018c2ecf20Sopenharmony_ci * None 14028c2ecf20Sopenharmony_ci */ 14038c2ecf20Sopenharmony_cistatic void mtip_dump_identify(struct mtip_port *port) 14048c2ecf20Sopenharmony_ci{ 14058c2ecf20Sopenharmony_ci sector_t sectors; 14068c2ecf20Sopenharmony_ci unsigned short revid; 14078c2ecf20Sopenharmony_ci char cbuf[42]; 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (!port->identify_valid) 14108c2ecf20Sopenharmony_ci return; 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci strlcpy(cbuf, (char *)(port->identify+10), 21); 14138c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, 14148c2ecf20Sopenharmony_ci "Serial No.: %s\n", cbuf); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci strlcpy(cbuf, (char *)(port->identify+23), 9); 14178c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, 14188c2ecf20Sopenharmony_ci "Firmware Ver.: %s\n", cbuf); 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_ci strlcpy(cbuf, (char *)(port->identify+27), 41); 14218c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, "Model: %s\n", cbuf); 14228c2ecf20Sopenharmony_ci 14238c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, "Security: %04x %s\n", 14248c2ecf20Sopenharmony_ci port->identify[128], 14258c2ecf20Sopenharmony_ci port->identify[128] & 0x4 ? "(LOCKED)" : ""); 14268c2ecf20Sopenharmony_ci 14278c2ecf20Sopenharmony_ci if (mtip_hw_get_capacity(port->dd, §ors)) 14288c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, 14298c2ecf20Sopenharmony_ci "Capacity: %llu sectors (%llu MB)\n", 14308c2ecf20Sopenharmony_ci (u64)sectors, 14318c2ecf20Sopenharmony_ci ((u64)sectors) * ATA_SECT_SIZE >> 20); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci pci_read_config_word(port->dd->pdev, PCI_REVISION_ID, &revid); 14348c2ecf20Sopenharmony_ci switch (revid & 0xFF) { 14358c2ecf20Sopenharmony_ci case 0x1: 14368c2ecf20Sopenharmony_ci strlcpy(cbuf, "A0", 3); 14378c2ecf20Sopenharmony_ci break; 14388c2ecf20Sopenharmony_ci case 0x3: 14398c2ecf20Sopenharmony_ci strlcpy(cbuf, "A2", 3); 14408c2ecf20Sopenharmony_ci break; 14418c2ecf20Sopenharmony_ci default: 14428c2ecf20Sopenharmony_ci strlcpy(cbuf, "?", 2); 14438c2ecf20Sopenharmony_ci break; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci dev_info(&port->dd->pdev->dev, 14468c2ecf20Sopenharmony_ci "Card Type: %s\n", cbuf); 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_ci/* 14508c2ecf20Sopenharmony_ci * Map the commands scatter list into the command table. 14518c2ecf20Sopenharmony_ci * 14528c2ecf20Sopenharmony_ci * @command Pointer to the command. 14538c2ecf20Sopenharmony_ci * @nents Number of scatter list entries. 14548c2ecf20Sopenharmony_ci * 14558c2ecf20Sopenharmony_ci * return value 14568c2ecf20Sopenharmony_ci * None 14578c2ecf20Sopenharmony_ci */ 14588c2ecf20Sopenharmony_cistatic inline void fill_command_sg(struct driver_data *dd, 14598c2ecf20Sopenharmony_ci struct mtip_cmd *command, 14608c2ecf20Sopenharmony_ci int nents) 14618c2ecf20Sopenharmony_ci{ 14628c2ecf20Sopenharmony_ci int n; 14638c2ecf20Sopenharmony_ci unsigned int dma_len; 14648c2ecf20Sopenharmony_ci struct mtip_cmd_sg *command_sg; 14658c2ecf20Sopenharmony_ci struct scatterlist *sg; 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci command_sg = command->command + AHCI_CMD_TBL_HDR_SZ; 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci for_each_sg(command->sg, sg, nents, n) { 14708c2ecf20Sopenharmony_ci dma_len = sg_dma_len(sg); 14718c2ecf20Sopenharmony_ci if (dma_len > 0x400000) 14728c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 14738c2ecf20Sopenharmony_ci "DMA segment length truncated\n"); 14748c2ecf20Sopenharmony_ci command_sg->info = cpu_to_le32((dma_len-1) & 0x3FFFFF); 14758c2ecf20Sopenharmony_ci command_sg->dba = cpu_to_le32(sg_dma_address(sg)); 14768c2ecf20Sopenharmony_ci command_sg->dba_upper = 14778c2ecf20Sopenharmony_ci cpu_to_le32((sg_dma_address(sg) >> 16) >> 16); 14788c2ecf20Sopenharmony_ci command_sg++; 14798c2ecf20Sopenharmony_ci } 14808c2ecf20Sopenharmony_ci} 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci/* 14838c2ecf20Sopenharmony_ci * @brief Execute a drive command. 14848c2ecf20Sopenharmony_ci * 14858c2ecf20Sopenharmony_ci * return value 0 The command completed successfully. 14868c2ecf20Sopenharmony_ci * return value -1 An error occurred while executing the command. 14878c2ecf20Sopenharmony_ci */ 14888c2ecf20Sopenharmony_cistatic int exec_drive_task(struct mtip_port *port, u8 *command) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 14918c2ecf20Sopenharmony_ci struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG); 14928c2ecf20Sopenharmony_ci unsigned int to; 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* Build the FIS. */ 14958c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 14968c2ecf20Sopenharmony_ci fis.type = 0x27; 14978c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 14988c2ecf20Sopenharmony_ci fis.command = command[0]; 14998c2ecf20Sopenharmony_ci fis.features = command[1]; 15008c2ecf20Sopenharmony_ci fis.sect_count = command[2]; 15018c2ecf20Sopenharmony_ci fis.sector = command[3]; 15028c2ecf20Sopenharmony_ci fis.cyl_low = command[4]; 15038c2ecf20Sopenharmony_ci fis.cyl_hi = command[5]; 15048c2ecf20Sopenharmony_ci fis.device = command[6] & ~0x10; /* Clear the dev bit*/ 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &to, 0); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " %s: User Command: cmd %x, feat %x, nsect %x, sect %x, lcyl %x, hcyl %x, sel %x\n", 15098c2ecf20Sopenharmony_ci __func__, 15108c2ecf20Sopenharmony_ci command[0], 15118c2ecf20Sopenharmony_ci command[1], 15128c2ecf20Sopenharmony_ci command[2], 15138c2ecf20Sopenharmony_ci command[3], 15148c2ecf20Sopenharmony_ci command[4], 15158c2ecf20Sopenharmony_ci command[5], 15168c2ecf20Sopenharmony_ci command[6]); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* Execute the command. */ 15198c2ecf20Sopenharmony_ci if (mtip_exec_internal_command(port, 15208c2ecf20Sopenharmony_ci &fis, 15218c2ecf20Sopenharmony_ci 5, 15228c2ecf20Sopenharmony_ci 0, 15238c2ecf20Sopenharmony_ci 0, 15248c2ecf20Sopenharmony_ci 0, 15258c2ecf20Sopenharmony_ci to) < 0) { 15268c2ecf20Sopenharmony_ci return -1; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ci command[0] = reply->command; /* Status*/ 15308c2ecf20Sopenharmony_ci command[1] = reply->features; /* Error*/ 15318c2ecf20Sopenharmony_ci command[4] = reply->cyl_low; 15328c2ecf20Sopenharmony_ci command[5] = reply->cyl_hi; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " %s: Completion Status: stat %x, err %x , cyl_lo %x cyl_hi %x\n", 15358c2ecf20Sopenharmony_ci __func__, 15368c2ecf20Sopenharmony_ci command[0], 15378c2ecf20Sopenharmony_ci command[1], 15388c2ecf20Sopenharmony_ci command[4], 15398c2ecf20Sopenharmony_ci command[5]); 15408c2ecf20Sopenharmony_ci 15418c2ecf20Sopenharmony_ci return 0; 15428c2ecf20Sopenharmony_ci} 15438c2ecf20Sopenharmony_ci 15448c2ecf20Sopenharmony_ci/* 15458c2ecf20Sopenharmony_ci * @brief Execute a drive command. 15468c2ecf20Sopenharmony_ci * 15478c2ecf20Sopenharmony_ci * @param port Pointer to the port data structure. 15488c2ecf20Sopenharmony_ci * @param command Pointer to the user specified command parameters. 15498c2ecf20Sopenharmony_ci * @param user_buffer Pointer to the user space buffer where read sector 15508c2ecf20Sopenharmony_ci * data should be copied. 15518c2ecf20Sopenharmony_ci * 15528c2ecf20Sopenharmony_ci * return value 0 The command completed successfully. 15538c2ecf20Sopenharmony_ci * return value -EFAULT An error occurred while copying the completion 15548c2ecf20Sopenharmony_ci * data to the user space buffer. 15558c2ecf20Sopenharmony_ci * return value -1 An error occurred while executing the command. 15568c2ecf20Sopenharmony_ci */ 15578c2ecf20Sopenharmony_cistatic int exec_drive_command(struct mtip_port *port, u8 *command, 15588c2ecf20Sopenharmony_ci void __user *user_buffer) 15598c2ecf20Sopenharmony_ci{ 15608c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 15618c2ecf20Sopenharmony_ci struct host_to_dev_fis *reply; 15628c2ecf20Sopenharmony_ci u8 *buf = NULL; 15638c2ecf20Sopenharmony_ci dma_addr_t dma_addr = 0; 15648c2ecf20Sopenharmony_ci int rv = 0, xfer_sz = command[3]; 15658c2ecf20Sopenharmony_ci unsigned int to; 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci if (xfer_sz) { 15688c2ecf20Sopenharmony_ci if (!user_buffer) 15698c2ecf20Sopenharmony_ci return -EFAULT; 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci buf = dma_alloc_coherent(&port->dd->pdev->dev, 15728c2ecf20Sopenharmony_ci ATA_SECT_SIZE * xfer_sz, 15738c2ecf20Sopenharmony_ci &dma_addr, 15748c2ecf20Sopenharmony_ci GFP_KERNEL); 15758c2ecf20Sopenharmony_ci if (!buf) { 15768c2ecf20Sopenharmony_ci dev_err(&port->dd->pdev->dev, 15778c2ecf20Sopenharmony_ci "Memory allocation failed (%d bytes)\n", 15788c2ecf20Sopenharmony_ci ATA_SECT_SIZE * xfer_sz); 15798c2ecf20Sopenharmony_ci return -ENOMEM; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci } 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci /* Build the FIS. */ 15848c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 15858c2ecf20Sopenharmony_ci fis.type = 0x27; 15868c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 15878c2ecf20Sopenharmony_ci fis.command = command[0]; 15888c2ecf20Sopenharmony_ci fis.features = command[2]; 15898c2ecf20Sopenharmony_ci fis.sect_count = command[3]; 15908c2ecf20Sopenharmony_ci if (fis.command == ATA_CMD_SMART) { 15918c2ecf20Sopenharmony_ci fis.sector = command[1]; 15928c2ecf20Sopenharmony_ci fis.cyl_low = 0x4F; 15938c2ecf20Sopenharmony_ci fis.cyl_hi = 0xC2; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &to, 0); 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci if (xfer_sz) 15998c2ecf20Sopenharmony_ci reply = (port->rxfis + RX_FIS_PIO_SETUP); 16008c2ecf20Sopenharmony_ci else 16018c2ecf20Sopenharmony_ci reply = (port->rxfis + RX_FIS_D2H_REG); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 16048c2ecf20Sopenharmony_ci " %s: User Command: cmd %x, sect %x, " 16058c2ecf20Sopenharmony_ci "feat %x, sectcnt %x\n", 16068c2ecf20Sopenharmony_ci __func__, 16078c2ecf20Sopenharmony_ci command[0], 16088c2ecf20Sopenharmony_ci command[1], 16098c2ecf20Sopenharmony_ci command[2], 16108c2ecf20Sopenharmony_ci command[3]); 16118c2ecf20Sopenharmony_ci 16128c2ecf20Sopenharmony_ci /* Execute the command. */ 16138c2ecf20Sopenharmony_ci if (mtip_exec_internal_command(port, 16148c2ecf20Sopenharmony_ci &fis, 16158c2ecf20Sopenharmony_ci 5, 16168c2ecf20Sopenharmony_ci (xfer_sz ? dma_addr : 0), 16178c2ecf20Sopenharmony_ci (xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0), 16188c2ecf20Sopenharmony_ci 0, 16198c2ecf20Sopenharmony_ci to) 16208c2ecf20Sopenharmony_ci < 0) { 16218c2ecf20Sopenharmony_ci rv = -EFAULT; 16228c2ecf20Sopenharmony_ci goto exit_drive_command; 16238c2ecf20Sopenharmony_ci } 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_ci /* Collect the completion status. */ 16268c2ecf20Sopenharmony_ci command[0] = reply->command; /* Status*/ 16278c2ecf20Sopenharmony_ci command[1] = reply->features; /* Error*/ 16288c2ecf20Sopenharmony_ci command[2] = reply->sect_count; 16298c2ecf20Sopenharmony_ci 16308c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 16318c2ecf20Sopenharmony_ci " %s: Completion Status: stat %x, " 16328c2ecf20Sopenharmony_ci "err %x, nsect %x\n", 16338c2ecf20Sopenharmony_ci __func__, 16348c2ecf20Sopenharmony_ci command[0], 16358c2ecf20Sopenharmony_ci command[1], 16368c2ecf20Sopenharmony_ci command[2]); 16378c2ecf20Sopenharmony_ci 16388c2ecf20Sopenharmony_ci if (xfer_sz) { 16398c2ecf20Sopenharmony_ci if (copy_to_user(user_buffer, 16408c2ecf20Sopenharmony_ci buf, 16418c2ecf20Sopenharmony_ci ATA_SECT_SIZE * command[3])) { 16428c2ecf20Sopenharmony_ci rv = -EFAULT; 16438c2ecf20Sopenharmony_ci goto exit_drive_command; 16448c2ecf20Sopenharmony_ci } 16458c2ecf20Sopenharmony_ci } 16468c2ecf20Sopenharmony_ciexit_drive_command: 16478c2ecf20Sopenharmony_ci if (buf) 16488c2ecf20Sopenharmony_ci dma_free_coherent(&port->dd->pdev->dev, 16498c2ecf20Sopenharmony_ci ATA_SECT_SIZE * xfer_sz, buf, dma_addr); 16508c2ecf20Sopenharmony_ci return rv; 16518c2ecf20Sopenharmony_ci} 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci/* 16548c2ecf20Sopenharmony_ci * Indicates whether a command has a single sector payload. 16558c2ecf20Sopenharmony_ci * 16568c2ecf20Sopenharmony_ci * @command passed to the device to perform the certain event. 16578c2ecf20Sopenharmony_ci * @features passed to the device to perform the certain event. 16588c2ecf20Sopenharmony_ci * 16598c2ecf20Sopenharmony_ci * return value 16608c2ecf20Sopenharmony_ci * 1 command is one that always has a single sector payload, 16618c2ecf20Sopenharmony_ci * regardless of the value in the Sector Count field. 16628c2ecf20Sopenharmony_ci * 0 otherwise 16638c2ecf20Sopenharmony_ci * 16648c2ecf20Sopenharmony_ci */ 16658c2ecf20Sopenharmony_cistatic unsigned int implicit_sector(unsigned char command, 16668c2ecf20Sopenharmony_ci unsigned char features) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci unsigned int rv = 0; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci /* list of commands that have an implicit sector count of 1 */ 16718c2ecf20Sopenharmony_ci switch (command) { 16728c2ecf20Sopenharmony_ci case ATA_CMD_SEC_SET_PASS: 16738c2ecf20Sopenharmony_ci case ATA_CMD_SEC_UNLOCK: 16748c2ecf20Sopenharmony_ci case ATA_CMD_SEC_ERASE_PREP: 16758c2ecf20Sopenharmony_ci case ATA_CMD_SEC_ERASE_UNIT: 16768c2ecf20Sopenharmony_ci case ATA_CMD_SEC_FREEZE_LOCK: 16778c2ecf20Sopenharmony_ci case ATA_CMD_SEC_DISABLE_PASS: 16788c2ecf20Sopenharmony_ci case ATA_CMD_PMP_READ: 16798c2ecf20Sopenharmony_ci case ATA_CMD_PMP_WRITE: 16808c2ecf20Sopenharmony_ci rv = 1; 16818c2ecf20Sopenharmony_ci break; 16828c2ecf20Sopenharmony_ci case ATA_CMD_SET_MAX: 16838c2ecf20Sopenharmony_ci if (features == ATA_SET_MAX_UNLOCK) 16848c2ecf20Sopenharmony_ci rv = 1; 16858c2ecf20Sopenharmony_ci break; 16868c2ecf20Sopenharmony_ci case ATA_CMD_SMART: 16878c2ecf20Sopenharmony_ci if ((features == ATA_SMART_READ_VALUES) || 16888c2ecf20Sopenharmony_ci (features == ATA_SMART_READ_THRESHOLDS)) 16898c2ecf20Sopenharmony_ci rv = 1; 16908c2ecf20Sopenharmony_ci break; 16918c2ecf20Sopenharmony_ci case ATA_CMD_CONF_OVERLAY: 16928c2ecf20Sopenharmony_ci if ((features == ATA_DCO_IDENTIFY) || 16938c2ecf20Sopenharmony_ci (features == ATA_DCO_SET)) 16948c2ecf20Sopenharmony_ci rv = 1; 16958c2ecf20Sopenharmony_ci break; 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci return rv; 16988c2ecf20Sopenharmony_ci} 16998c2ecf20Sopenharmony_ci 17008c2ecf20Sopenharmony_ci/* 17018c2ecf20Sopenharmony_ci * Executes a taskfile 17028c2ecf20Sopenharmony_ci * See ide_taskfile_ioctl() for derivation 17038c2ecf20Sopenharmony_ci */ 17048c2ecf20Sopenharmony_cistatic int exec_drive_taskfile(struct driver_data *dd, 17058c2ecf20Sopenharmony_ci void __user *buf, 17068c2ecf20Sopenharmony_ci ide_task_request_t *req_task, 17078c2ecf20Sopenharmony_ci int outtotal) 17088c2ecf20Sopenharmony_ci{ 17098c2ecf20Sopenharmony_ci struct host_to_dev_fis fis; 17108c2ecf20Sopenharmony_ci struct host_to_dev_fis *reply; 17118c2ecf20Sopenharmony_ci u8 *outbuf = NULL; 17128c2ecf20Sopenharmony_ci u8 *inbuf = NULL; 17138c2ecf20Sopenharmony_ci dma_addr_t outbuf_dma = 0; 17148c2ecf20Sopenharmony_ci dma_addr_t inbuf_dma = 0; 17158c2ecf20Sopenharmony_ci dma_addr_t dma_buffer = 0; 17168c2ecf20Sopenharmony_ci int err = 0; 17178c2ecf20Sopenharmony_ci unsigned int taskin = 0; 17188c2ecf20Sopenharmony_ci unsigned int taskout = 0; 17198c2ecf20Sopenharmony_ci u8 nsect = 0; 17208c2ecf20Sopenharmony_ci unsigned int timeout; 17218c2ecf20Sopenharmony_ci unsigned int force_single_sector; 17228c2ecf20Sopenharmony_ci unsigned int transfer_size; 17238c2ecf20Sopenharmony_ci unsigned long task_file_data; 17248c2ecf20Sopenharmony_ci int intotal = outtotal + req_task->out_size; 17258c2ecf20Sopenharmony_ci int erasemode = 0; 17268c2ecf20Sopenharmony_ci 17278c2ecf20Sopenharmony_ci taskout = req_task->out_size; 17288c2ecf20Sopenharmony_ci taskin = req_task->in_size; 17298c2ecf20Sopenharmony_ci /* 130560 = 512 * 0xFF*/ 17308c2ecf20Sopenharmony_ci if (taskin > 130560 || taskout > 130560) 17318c2ecf20Sopenharmony_ci return -EINVAL; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci if (taskout) { 17348c2ecf20Sopenharmony_ci outbuf = memdup_user(buf + outtotal, taskout); 17358c2ecf20Sopenharmony_ci if (IS_ERR(outbuf)) 17368c2ecf20Sopenharmony_ci return PTR_ERR(outbuf); 17378c2ecf20Sopenharmony_ci 17388c2ecf20Sopenharmony_ci outbuf_dma = dma_map_single(&dd->pdev->dev, outbuf, 17398c2ecf20Sopenharmony_ci taskout, DMA_TO_DEVICE); 17408c2ecf20Sopenharmony_ci if (dma_mapping_error(&dd->pdev->dev, outbuf_dma)) { 17418c2ecf20Sopenharmony_ci err = -ENOMEM; 17428c2ecf20Sopenharmony_ci goto abort; 17438c2ecf20Sopenharmony_ci } 17448c2ecf20Sopenharmony_ci dma_buffer = outbuf_dma; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (taskin) { 17488c2ecf20Sopenharmony_ci inbuf = memdup_user(buf + intotal, taskin); 17498c2ecf20Sopenharmony_ci if (IS_ERR(inbuf)) { 17508c2ecf20Sopenharmony_ci err = PTR_ERR(inbuf); 17518c2ecf20Sopenharmony_ci inbuf = NULL; 17528c2ecf20Sopenharmony_ci goto abort; 17538c2ecf20Sopenharmony_ci } 17548c2ecf20Sopenharmony_ci inbuf_dma = dma_map_single(&dd->pdev->dev, inbuf, 17558c2ecf20Sopenharmony_ci taskin, DMA_FROM_DEVICE); 17568c2ecf20Sopenharmony_ci if (dma_mapping_error(&dd->pdev->dev, inbuf_dma)) { 17578c2ecf20Sopenharmony_ci err = -ENOMEM; 17588c2ecf20Sopenharmony_ci goto abort; 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci dma_buffer = inbuf_dma; 17618c2ecf20Sopenharmony_ci } 17628c2ecf20Sopenharmony_ci 17638c2ecf20Sopenharmony_ci /* only supports PIO and non-data commands from this ioctl. */ 17648c2ecf20Sopenharmony_ci switch (req_task->data_phase) { 17658c2ecf20Sopenharmony_ci case TASKFILE_OUT: 17668c2ecf20Sopenharmony_ci nsect = taskout / ATA_SECT_SIZE; 17678c2ecf20Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_PIO_SETUP); 17688c2ecf20Sopenharmony_ci break; 17698c2ecf20Sopenharmony_ci case TASKFILE_IN: 17708c2ecf20Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_PIO_SETUP); 17718c2ecf20Sopenharmony_ci break; 17728c2ecf20Sopenharmony_ci case TASKFILE_NO_DATA: 17738c2ecf20Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_D2H_REG); 17748c2ecf20Sopenharmony_ci break; 17758c2ecf20Sopenharmony_ci default: 17768c2ecf20Sopenharmony_ci err = -EINVAL; 17778c2ecf20Sopenharmony_ci goto abort; 17788c2ecf20Sopenharmony_ci } 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci /* Build the FIS. */ 17818c2ecf20Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci fis.type = 0x27; 17848c2ecf20Sopenharmony_ci fis.opts = 1 << 7; 17858c2ecf20Sopenharmony_ci fis.command = req_task->io_ports[7]; 17868c2ecf20Sopenharmony_ci fis.features = req_task->io_ports[1]; 17878c2ecf20Sopenharmony_ci fis.sect_count = req_task->io_ports[2]; 17888c2ecf20Sopenharmony_ci fis.lba_low = req_task->io_ports[3]; 17898c2ecf20Sopenharmony_ci fis.lba_mid = req_task->io_ports[4]; 17908c2ecf20Sopenharmony_ci fis.lba_hi = req_task->io_ports[5]; 17918c2ecf20Sopenharmony_ci /* Clear the dev bit*/ 17928c2ecf20Sopenharmony_ci fis.device = req_task->io_ports[6] & ~0x10; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci if ((req_task->in_flags.all == 0) && (req_task->out_flags.all & 1)) { 17958c2ecf20Sopenharmony_ci req_task->in_flags.all = 17968c2ecf20Sopenharmony_ci IDE_TASKFILE_STD_IN_FLAGS | 17978c2ecf20Sopenharmony_ci (IDE_HOB_STD_IN_FLAGS << 8); 17988c2ecf20Sopenharmony_ci fis.lba_low_ex = req_task->hob_ports[3]; 17998c2ecf20Sopenharmony_ci fis.lba_mid_ex = req_task->hob_ports[4]; 18008c2ecf20Sopenharmony_ci fis.lba_hi_ex = req_task->hob_ports[5]; 18018c2ecf20Sopenharmony_ci fis.features_ex = req_task->hob_ports[1]; 18028c2ecf20Sopenharmony_ci fis.sect_cnt_ex = req_task->hob_ports[2]; 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci } else { 18058c2ecf20Sopenharmony_ci req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; 18068c2ecf20Sopenharmony_ci } 18078c2ecf20Sopenharmony_ci 18088c2ecf20Sopenharmony_ci force_single_sector = implicit_sector(fis.command, fis.features); 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_ci if ((taskin || taskout) && (!fis.sect_count)) { 18118c2ecf20Sopenharmony_ci if (nsect) 18128c2ecf20Sopenharmony_ci fis.sect_count = nsect; 18138c2ecf20Sopenharmony_ci else { 18148c2ecf20Sopenharmony_ci if (!force_single_sector) { 18158c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 18168c2ecf20Sopenharmony_ci "data movement but " 18178c2ecf20Sopenharmony_ci "sect_count is 0\n"); 18188c2ecf20Sopenharmony_ci err = -EINVAL; 18198c2ecf20Sopenharmony_ci goto abort; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci } 18228c2ecf20Sopenharmony_ci } 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 18258c2ecf20Sopenharmony_ci " %s: cmd %x, feat %x, nsect %x," 18268c2ecf20Sopenharmony_ci " sect/lbal %x, lcyl/lbam %x, hcyl/lbah %x," 18278c2ecf20Sopenharmony_ci " head/dev %x\n", 18288c2ecf20Sopenharmony_ci __func__, 18298c2ecf20Sopenharmony_ci fis.command, 18308c2ecf20Sopenharmony_ci fis.features, 18318c2ecf20Sopenharmony_ci fis.sect_count, 18328c2ecf20Sopenharmony_ci fis.lba_low, 18338c2ecf20Sopenharmony_ci fis.lba_mid, 18348c2ecf20Sopenharmony_ci fis.lba_hi, 18358c2ecf20Sopenharmony_ci fis.device); 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci /* check for erase mode support during secure erase.*/ 18388c2ecf20Sopenharmony_ci if ((fis.command == ATA_CMD_SEC_ERASE_UNIT) && outbuf && 18398c2ecf20Sopenharmony_ci (outbuf[0] & MTIP_SEC_ERASE_MODE)) { 18408c2ecf20Sopenharmony_ci erasemode = 1; 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci 18438c2ecf20Sopenharmony_ci mtip_set_timeout(dd, &fis, &timeout, erasemode); 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci /* Determine the correct transfer size.*/ 18468c2ecf20Sopenharmony_ci if (force_single_sector) 18478c2ecf20Sopenharmony_ci transfer_size = ATA_SECT_SIZE; 18488c2ecf20Sopenharmony_ci else 18498c2ecf20Sopenharmony_ci transfer_size = ATA_SECT_SIZE * fis.sect_count; 18508c2ecf20Sopenharmony_ci 18518c2ecf20Sopenharmony_ci /* Execute the command.*/ 18528c2ecf20Sopenharmony_ci if (mtip_exec_internal_command(dd->port, 18538c2ecf20Sopenharmony_ci &fis, 18548c2ecf20Sopenharmony_ci 5, 18558c2ecf20Sopenharmony_ci dma_buffer, 18568c2ecf20Sopenharmony_ci transfer_size, 18578c2ecf20Sopenharmony_ci 0, 18588c2ecf20Sopenharmony_ci timeout) < 0) { 18598c2ecf20Sopenharmony_ci err = -EIO; 18608c2ecf20Sopenharmony_ci goto abort; 18618c2ecf20Sopenharmony_ci } 18628c2ecf20Sopenharmony_ci 18638c2ecf20Sopenharmony_ci task_file_data = readl(dd->port->mmio+PORT_TFDATA); 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if ((req_task->data_phase == TASKFILE_IN) && !(task_file_data & 1)) { 18668c2ecf20Sopenharmony_ci reply = dd->port->rxfis + RX_FIS_PIO_SETUP; 18678c2ecf20Sopenharmony_ci req_task->io_ports[7] = reply->control; 18688c2ecf20Sopenharmony_ci } else { 18698c2ecf20Sopenharmony_ci reply = dd->port->rxfis + RX_FIS_D2H_REG; 18708c2ecf20Sopenharmony_ci req_task->io_ports[7] = reply->command; 18718c2ecf20Sopenharmony_ci } 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci /* reclaim the DMA buffers.*/ 18748c2ecf20Sopenharmony_ci if (inbuf_dma) 18758c2ecf20Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, inbuf_dma, taskin, 18768c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 18778c2ecf20Sopenharmony_ci if (outbuf_dma) 18788c2ecf20Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, outbuf_dma, taskout, 18798c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 18808c2ecf20Sopenharmony_ci inbuf_dma = 0; 18818c2ecf20Sopenharmony_ci outbuf_dma = 0; 18828c2ecf20Sopenharmony_ci 18838c2ecf20Sopenharmony_ci /* return the ATA registers to the caller.*/ 18848c2ecf20Sopenharmony_ci req_task->io_ports[1] = reply->features; 18858c2ecf20Sopenharmony_ci req_task->io_ports[2] = reply->sect_count; 18868c2ecf20Sopenharmony_ci req_task->io_ports[3] = reply->lba_low; 18878c2ecf20Sopenharmony_ci req_task->io_ports[4] = reply->lba_mid; 18888c2ecf20Sopenharmony_ci req_task->io_ports[5] = reply->lba_hi; 18898c2ecf20Sopenharmony_ci req_task->io_ports[6] = reply->device; 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci if (req_task->out_flags.all & 1) { 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci req_task->hob_ports[3] = reply->lba_low_ex; 18948c2ecf20Sopenharmony_ci req_task->hob_ports[4] = reply->lba_mid_ex; 18958c2ecf20Sopenharmony_ci req_task->hob_ports[5] = reply->lba_hi_ex; 18968c2ecf20Sopenharmony_ci req_task->hob_ports[1] = reply->features_ex; 18978c2ecf20Sopenharmony_ci req_task->hob_ports[2] = reply->sect_cnt_ex; 18988c2ecf20Sopenharmony_ci } 18998c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 19008c2ecf20Sopenharmony_ci " %s: Completion: stat %x," 19018c2ecf20Sopenharmony_ci "err %x, sect_cnt %x, lbalo %x," 19028c2ecf20Sopenharmony_ci "lbamid %x, lbahi %x, dev %x\n", 19038c2ecf20Sopenharmony_ci __func__, 19048c2ecf20Sopenharmony_ci req_task->io_ports[7], 19058c2ecf20Sopenharmony_ci req_task->io_ports[1], 19068c2ecf20Sopenharmony_ci req_task->io_ports[2], 19078c2ecf20Sopenharmony_ci req_task->io_ports[3], 19088c2ecf20Sopenharmony_ci req_task->io_ports[4], 19098c2ecf20Sopenharmony_ci req_task->io_ports[5], 19108c2ecf20Sopenharmony_ci req_task->io_ports[6]); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci if (taskout) { 19138c2ecf20Sopenharmony_ci if (copy_to_user(buf + outtotal, outbuf, taskout)) { 19148c2ecf20Sopenharmony_ci err = -EFAULT; 19158c2ecf20Sopenharmony_ci goto abort; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci if (taskin) { 19198c2ecf20Sopenharmony_ci if (copy_to_user(buf + intotal, inbuf, taskin)) { 19208c2ecf20Sopenharmony_ci err = -EFAULT; 19218c2ecf20Sopenharmony_ci goto abort; 19228c2ecf20Sopenharmony_ci } 19238c2ecf20Sopenharmony_ci } 19248c2ecf20Sopenharmony_ciabort: 19258c2ecf20Sopenharmony_ci if (inbuf_dma) 19268c2ecf20Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, inbuf_dma, taskin, 19278c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 19288c2ecf20Sopenharmony_ci if (outbuf_dma) 19298c2ecf20Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, outbuf_dma, taskout, 19308c2ecf20Sopenharmony_ci DMA_TO_DEVICE); 19318c2ecf20Sopenharmony_ci kfree(outbuf); 19328c2ecf20Sopenharmony_ci kfree(inbuf); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci return err; 19358c2ecf20Sopenharmony_ci} 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci/* 19388c2ecf20Sopenharmony_ci * Handle IOCTL calls from the Block Layer. 19398c2ecf20Sopenharmony_ci * 19408c2ecf20Sopenharmony_ci * This function is called by the Block Layer when it receives an IOCTL 19418c2ecf20Sopenharmony_ci * command that it does not understand. If the IOCTL command is not supported 19428c2ecf20Sopenharmony_ci * this function returns -ENOTTY. 19438c2ecf20Sopenharmony_ci * 19448c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 19458c2ecf20Sopenharmony_ci * @cmd IOCTL command passed from the Block Layer. 19468c2ecf20Sopenharmony_ci * @arg IOCTL argument passed from the Block Layer. 19478c2ecf20Sopenharmony_ci * 19488c2ecf20Sopenharmony_ci * return value 19498c2ecf20Sopenharmony_ci * 0 The IOCTL completed successfully. 19508c2ecf20Sopenharmony_ci * -ENOTTY The specified command is not supported. 19518c2ecf20Sopenharmony_ci * -EFAULT An error occurred copying data to a user space buffer. 19528c2ecf20Sopenharmony_ci * -EIO An error occurred while executing the command. 19538c2ecf20Sopenharmony_ci */ 19548c2ecf20Sopenharmony_cistatic int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, 19558c2ecf20Sopenharmony_ci unsigned long arg) 19568c2ecf20Sopenharmony_ci{ 19578c2ecf20Sopenharmony_ci switch (cmd) { 19588c2ecf20Sopenharmony_ci case HDIO_GET_IDENTITY: 19598c2ecf20Sopenharmony_ci { 19608c2ecf20Sopenharmony_ci if (copy_to_user((void __user *)arg, dd->port->identify, 19618c2ecf20Sopenharmony_ci sizeof(u16) * ATA_ID_WORDS)) 19628c2ecf20Sopenharmony_ci return -EFAULT; 19638c2ecf20Sopenharmony_ci break; 19648c2ecf20Sopenharmony_ci } 19658c2ecf20Sopenharmony_ci case HDIO_DRIVE_CMD: 19668c2ecf20Sopenharmony_ci { 19678c2ecf20Sopenharmony_ci u8 drive_command[4]; 19688c2ecf20Sopenharmony_ci 19698c2ecf20Sopenharmony_ci /* Copy the user command info to our buffer. */ 19708c2ecf20Sopenharmony_ci if (copy_from_user(drive_command, 19718c2ecf20Sopenharmony_ci (void __user *) arg, 19728c2ecf20Sopenharmony_ci sizeof(drive_command))) 19738c2ecf20Sopenharmony_ci return -EFAULT; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci /* Execute the drive command. */ 19768c2ecf20Sopenharmony_ci if (exec_drive_command(dd->port, 19778c2ecf20Sopenharmony_ci drive_command, 19788c2ecf20Sopenharmony_ci (void __user *) (arg+4))) 19798c2ecf20Sopenharmony_ci return -EIO; 19808c2ecf20Sopenharmony_ci 19818c2ecf20Sopenharmony_ci /* Copy the status back to the users buffer. */ 19828c2ecf20Sopenharmony_ci if (copy_to_user((void __user *) arg, 19838c2ecf20Sopenharmony_ci drive_command, 19848c2ecf20Sopenharmony_ci sizeof(drive_command))) 19858c2ecf20Sopenharmony_ci return -EFAULT; 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci break; 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci case HDIO_DRIVE_TASK: 19908c2ecf20Sopenharmony_ci { 19918c2ecf20Sopenharmony_ci u8 drive_command[7]; 19928c2ecf20Sopenharmony_ci 19938c2ecf20Sopenharmony_ci /* Copy the user command info to our buffer. */ 19948c2ecf20Sopenharmony_ci if (copy_from_user(drive_command, 19958c2ecf20Sopenharmony_ci (void __user *) arg, 19968c2ecf20Sopenharmony_ci sizeof(drive_command))) 19978c2ecf20Sopenharmony_ci return -EFAULT; 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci /* Execute the drive command. */ 20008c2ecf20Sopenharmony_ci if (exec_drive_task(dd->port, drive_command)) 20018c2ecf20Sopenharmony_ci return -EIO; 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci /* Copy the status back to the users buffer. */ 20048c2ecf20Sopenharmony_ci if (copy_to_user((void __user *) arg, 20058c2ecf20Sopenharmony_ci drive_command, 20068c2ecf20Sopenharmony_ci sizeof(drive_command))) 20078c2ecf20Sopenharmony_ci return -EFAULT; 20088c2ecf20Sopenharmony_ci 20098c2ecf20Sopenharmony_ci break; 20108c2ecf20Sopenharmony_ci } 20118c2ecf20Sopenharmony_ci case HDIO_DRIVE_TASKFILE: { 20128c2ecf20Sopenharmony_ci ide_task_request_t req_task; 20138c2ecf20Sopenharmony_ci int ret, outtotal; 20148c2ecf20Sopenharmony_ci 20158c2ecf20Sopenharmony_ci if (copy_from_user(&req_task, (void __user *) arg, 20168c2ecf20Sopenharmony_ci sizeof(req_task))) 20178c2ecf20Sopenharmony_ci return -EFAULT; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci outtotal = sizeof(req_task); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci ret = exec_drive_taskfile(dd, (void __user *) arg, 20228c2ecf20Sopenharmony_ci &req_task, outtotal); 20238c2ecf20Sopenharmony_ci 20248c2ecf20Sopenharmony_ci if (copy_to_user((void __user *) arg, &req_task, 20258c2ecf20Sopenharmony_ci sizeof(req_task))) 20268c2ecf20Sopenharmony_ci return -EFAULT; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci return ret; 20298c2ecf20Sopenharmony_ci } 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci default: 20328c2ecf20Sopenharmony_ci return -EINVAL; 20338c2ecf20Sopenharmony_ci } 20348c2ecf20Sopenharmony_ci return 0; 20358c2ecf20Sopenharmony_ci} 20368c2ecf20Sopenharmony_ci 20378c2ecf20Sopenharmony_ci/* 20388c2ecf20Sopenharmony_ci * Submit an IO to the hw 20398c2ecf20Sopenharmony_ci * 20408c2ecf20Sopenharmony_ci * This function is called by the block layer to issue an io 20418c2ecf20Sopenharmony_ci * to the device. Upon completion, the callback function will 20428c2ecf20Sopenharmony_ci * be called with the data parameter passed as the callback data. 20438c2ecf20Sopenharmony_ci * 20448c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 20458c2ecf20Sopenharmony_ci * @start First sector to read. 20468c2ecf20Sopenharmony_ci * @nsect Number of sectors to read. 20478c2ecf20Sopenharmony_ci * @tag The tag of this read command. 20488c2ecf20Sopenharmony_ci * @callback Pointer to the function that should be called 20498c2ecf20Sopenharmony_ci * when the read completes. 20508c2ecf20Sopenharmony_ci * @data Callback data passed to the callback function 20518c2ecf20Sopenharmony_ci * when the read completes. 20528c2ecf20Sopenharmony_ci * @dir Direction (read or write) 20538c2ecf20Sopenharmony_ci * 20548c2ecf20Sopenharmony_ci * return value 20558c2ecf20Sopenharmony_ci * None 20568c2ecf20Sopenharmony_ci */ 20578c2ecf20Sopenharmony_cistatic void mtip_hw_submit_io(struct driver_data *dd, struct request *rq, 20588c2ecf20Sopenharmony_ci struct mtip_cmd *command, 20598c2ecf20Sopenharmony_ci struct blk_mq_hw_ctx *hctx) 20608c2ecf20Sopenharmony_ci{ 20618c2ecf20Sopenharmony_ci struct mtip_cmd_hdr *hdr = 20628c2ecf20Sopenharmony_ci dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag; 20638c2ecf20Sopenharmony_ci struct host_to_dev_fis *fis; 20648c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 20658c2ecf20Sopenharmony_ci int dma_dir = rq_data_dir(rq) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 20668c2ecf20Sopenharmony_ci u64 start = blk_rq_pos(rq); 20678c2ecf20Sopenharmony_ci unsigned int nsect = blk_rq_sectors(rq); 20688c2ecf20Sopenharmony_ci unsigned int nents; 20698c2ecf20Sopenharmony_ci 20708c2ecf20Sopenharmony_ci /* Map the scatter list for DMA access */ 20718c2ecf20Sopenharmony_ci nents = blk_rq_map_sg(hctx->queue, rq, command->sg); 20728c2ecf20Sopenharmony_ci nents = dma_map_sg(&dd->pdev->dev, command->sg, nents, dma_dir); 20738c2ecf20Sopenharmony_ci 20748c2ecf20Sopenharmony_ci prefetch(&port->flags); 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci command->scatter_ents = nents; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci /* 20798c2ecf20Sopenharmony_ci * The number of retries for this command before it is 20808c2ecf20Sopenharmony_ci * reported as a failure to the upper layers. 20818c2ecf20Sopenharmony_ci */ 20828c2ecf20Sopenharmony_ci command->retries = MTIP_MAX_RETRIES; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci /* Fill out fis */ 20858c2ecf20Sopenharmony_ci fis = command->command; 20868c2ecf20Sopenharmony_ci fis->type = 0x27; 20878c2ecf20Sopenharmony_ci fis->opts = 1 << 7; 20888c2ecf20Sopenharmony_ci if (dma_dir == DMA_FROM_DEVICE) 20898c2ecf20Sopenharmony_ci fis->command = ATA_CMD_FPDMA_READ; 20908c2ecf20Sopenharmony_ci else 20918c2ecf20Sopenharmony_ci fis->command = ATA_CMD_FPDMA_WRITE; 20928c2ecf20Sopenharmony_ci fis->lba_low = start & 0xFF; 20938c2ecf20Sopenharmony_ci fis->lba_mid = (start >> 8) & 0xFF; 20948c2ecf20Sopenharmony_ci fis->lba_hi = (start >> 16) & 0xFF; 20958c2ecf20Sopenharmony_ci fis->lba_low_ex = (start >> 24) & 0xFF; 20968c2ecf20Sopenharmony_ci fis->lba_mid_ex = (start >> 32) & 0xFF; 20978c2ecf20Sopenharmony_ci fis->lba_hi_ex = (start >> 40) & 0xFF; 20988c2ecf20Sopenharmony_ci fis->device = 1 << 6; 20998c2ecf20Sopenharmony_ci fis->features = nsect & 0xFF; 21008c2ecf20Sopenharmony_ci fis->features_ex = (nsect >> 8) & 0xFF; 21018c2ecf20Sopenharmony_ci fis->sect_count = ((rq->tag << 3) | (rq->tag >> 5)); 21028c2ecf20Sopenharmony_ci fis->sect_cnt_ex = 0; 21038c2ecf20Sopenharmony_ci fis->control = 0; 21048c2ecf20Sopenharmony_ci fis->res2 = 0; 21058c2ecf20Sopenharmony_ci fis->res3 = 0; 21068c2ecf20Sopenharmony_ci fill_command_sg(dd, command, nents); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci if (unlikely(command->unaligned)) 21098c2ecf20Sopenharmony_ci fis->device |= 1 << 7; 21108c2ecf20Sopenharmony_ci 21118c2ecf20Sopenharmony_ci /* Populate the command header */ 21128c2ecf20Sopenharmony_ci hdr->ctba = cpu_to_le32(command->command_dma & 0xFFFFFFFF); 21138c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_HOST_CAP_64, &dd->port->flags)) 21148c2ecf20Sopenharmony_ci hdr->ctbau = cpu_to_le32((command->command_dma >> 16) >> 16); 21158c2ecf20Sopenharmony_ci hdr->opts = cpu_to_le32((nents << 16) | 5 | AHCI_CMD_PREFETCH); 21168c2ecf20Sopenharmony_ci hdr->byte_count = 0; 21178c2ecf20Sopenharmony_ci 21188c2ecf20Sopenharmony_ci command->direction = dma_dir; 21198c2ecf20Sopenharmony_ci 21208c2ecf20Sopenharmony_ci /* 21218c2ecf20Sopenharmony_ci * To prevent this command from being issued 21228c2ecf20Sopenharmony_ci * if an internal command is in progress or error handling is active. 21238c2ecf20Sopenharmony_ci */ 21248c2ecf20Sopenharmony_ci if (unlikely(port->flags & MTIP_PF_PAUSE_IO)) { 21258c2ecf20Sopenharmony_ci set_bit(rq->tag, port->cmds_to_issue); 21268c2ecf20Sopenharmony_ci set_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); 21278c2ecf20Sopenharmony_ci return; 21288c2ecf20Sopenharmony_ci } 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci /* Issue the command to the hardware */ 21318c2ecf20Sopenharmony_ci mtip_issue_ncq_command(port, rq->tag); 21328c2ecf20Sopenharmony_ci} 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_ci/* 21358c2ecf20Sopenharmony_ci * Sysfs status dump. 21368c2ecf20Sopenharmony_ci * 21378c2ecf20Sopenharmony_ci * @dev Pointer to the device structure, passed by the kernrel. 21388c2ecf20Sopenharmony_ci * @attr Pointer to the device_attribute structure passed by the kernel. 21398c2ecf20Sopenharmony_ci * @buf Pointer to the char buffer that will receive the stats info. 21408c2ecf20Sopenharmony_ci * 21418c2ecf20Sopenharmony_ci * return value 21428c2ecf20Sopenharmony_ci * The size, in bytes, of the data copied into buf. 21438c2ecf20Sopenharmony_ci */ 21448c2ecf20Sopenharmony_cistatic ssize_t mtip_hw_show_status(struct device *dev, 21458c2ecf20Sopenharmony_ci struct device_attribute *attr, 21468c2ecf20Sopenharmony_ci char *buf) 21478c2ecf20Sopenharmony_ci{ 21488c2ecf20Sopenharmony_ci struct driver_data *dd = dev_to_disk(dev)->private_data; 21498c2ecf20Sopenharmony_ci int size = 0; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag)) 21528c2ecf20Sopenharmony_ci size += sprintf(buf, "%s", "thermal_shutdown\n"); 21538c2ecf20Sopenharmony_ci else if (test_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag)) 21548c2ecf20Sopenharmony_ci size += sprintf(buf, "%s", "write_protect\n"); 21558c2ecf20Sopenharmony_ci else 21568c2ecf20Sopenharmony_ci size += sprintf(buf, "%s", "online\n"); 21578c2ecf20Sopenharmony_ci 21588c2ecf20Sopenharmony_ci return size; 21598c2ecf20Sopenharmony_ci} 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_cistatic DEVICE_ATTR(status, 0444, mtip_hw_show_status, NULL); 21628c2ecf20Sopenharmony_ci 21638c2ecf20Sopenharmony_ci/* debugsfs entries */ 21648c2ecf20Sopenharmony_ci 21658c2ecf20Sopenharmony_cistatic ssize_t show_device_status(struct device_driver *drv, char *buf) 21668c2ecf20Sopenharmony_ci{ 21678c2ecf20Sopenharmony_ci int size = 0; 21688c2ecf20Sopenharmony_ci struct driver_data *dd, *tmp; 21698c2ecf20Sopenharmony_ci unsigned long flags; 21708c2ecf20Sopenharmony_ci char id_buf[42]; 21718c2ecf20Sopenharmony_ci u16 status = 0; 21728c2ecf20Sopenharmony_ci 21738c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_lock, flags); 21748c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "Devices Present:\n"); 21758c2ecf20Sopenharmony_ci list_for_each_entry_safe(dd, tmp, &online_list, online_list) { 21768c2ecf20Sopenharmony_ci if (dd->pdev) { 21778c2ecf20Sopenharmony_ci if (dd->port && 21788c2ecf20Sopenharmony_ci dd->port->identify && 21798c2ecf20Sopenharmony_ci dd->port->identify_valid) { 21808c2ecf20Sopenharmony_ci strlcpy(id_buf, 21818c2ecf20Sopenharmony_ci (char *) (dd->port->identify + 10), 21); 21828c2ecf20Sopenharmony_ci status = *(dd->port->identify + 141); 21838c2ecf20Sopenharmony_ci } else { 21848c2ecf20Sopenharmony_ci memset(id_buf, 0, 42); 21858c2ecf20Sopenharmony_ci status = 0; 21868c2ecf20Sopenharmony_ci } 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci if (dd->port && 21898c2ecf20Sopenharmony_ci test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags)) { 21908c2ecf20Sopenharmony_ci size += sprintf(&buf[size], 21918c2ecf20Sopenharmony_ci " device %s %s (ftl rebuild %d %%)\n", 21928c2ecf20Sopenharmony_ci dev_name(&dd->pdev->dev), 21938c2ecf20Sopenharmony_ci id_buf, 21948c2ecf20Sopenharmony_ci status); 21958c2ecf20Sopenharmony_ci } else { 21968c2ecf20Sopenharmony_ci size += sprintf(&buf[size], 21978c2ecf20Sopenharmony_ci " device %s %s\n", 21988c2ecf20Sopenharmony_ci dev_name(&dd->pdev->dev), 21998c2ecf20Sopenharmony_ci id_buf); 22008c2ecf20Sopenharmony_ci } 22018c2ecf20Sopenharmony_ci } 22028c2ecf20Sopenharmony_ci } 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "Devices Being Removed:\n"); 22058c2ecf20Sopenharmony_ci list_for_each_entry_safe(dd, tmp, &removing_list, remove_list) { 22068c2ecf20Sopenharmony_ci if (dd->pdev) { 22078c2ecf20Sopenharmony_ci if (dd->port && 22088c2ecf20Sopenharmony_ci dd->port->identify && 22098c2ecf20Sopenharmony_ci dd->port->identify_valid) { 22108c2ecf20Sopenharmony_ci strlcpy(id_buf, 22118c2ecf20Sopenharmony_ci (char *) (dd->port->identify+10), 21); 22128c2ecf20Sopenharmony_ci status = *(dd->port->identify + 141); 22138c2ecf20Sopenharmony_ci } else { 22148c2ecf20Sopenharmony_ci memset(id_buf, 0, 42); 22158c2ecf20Sopenharmony_ci status = 0; 22168c2ecf20Sopenharmony_ci } 22178c2ecf20Sopenharmony_ci 22188c2ecf20Sopenharmony_ci if (dd->port && 22198c2ecf20Sopenharmony_ci test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags)) { 22208c2ecf20Sopenharmony_ci size += sprintf(&buf[size], 22218c2ecf20Sopenharmony_ci " device %s %s (ftl rebuild %d %%)\n", 22228c2ecf20Sopenharmony_ci dev_name(&dd->pdev->dev), 22238c2ecf20Sopenharmony_ci id_buf, 22248c2ecf20Sopenharmony_ci status); 22258c2ecf20Sopenharmony_ci } else { 22268c2ecf20Sopenharmony_ci size += sprintf(&buf[size], 22278c2ecf20Sopenharmony_ci " device %s %s\n", 22288c2ecf20Sopenharmony_ci dev_name(&dd->pdev->dev), 22298c2ecf20Sopenharmony_ci id_buf); 22308c2ecf20Sopenharmony_ci } 22318c2ecf20Sopenharmony_ci } 22328c2ecf20Sopenharmony_ci } 22338c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_lock, flags); 22348c2ecf20Sopenharmony_ci 22358c2ecf20Sopenharmony_ci return size; 22368c2ecf20Sopenharmony_ci} 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_cistatic ssize_t mtip_hw_read_device_status(struct file *f, char __user *ubuf, 22398c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 22408c2ecf20Sopenharmony_ci{ 22418c2ecf20Sopenharmony_ci struct driver_data *dd = (struct driver_data *)f->private_data; 22428c2ecf20Sopenharmony_ci int size = *offset; 22438c2ecf20Sopenharmony_ci char *buf; 22448c2ecf20Sopenharmony_ci int rv = 0; 22458c2ecf20Sopenharmony_ci 22468c2ecf20Sopenharmony_ci if (!len || *offset) 22478c2ecf20Sopenharmony_ci return 0; 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci buf = kzalloc(MTIP_DFS_MAX_BUF_SIZE, GFP_KERNEL); 22508c2ecf20Sopenharmony_ci if (!buf) { 22518c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 22528c2ecf20Sopenharmony_ci "Memory allocation: status buffer\n"); 22538c2ecf20Sopenharmony_ci return -ENOMEM; 22548c2ecf20Sopenharmony_ci } 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci size += show_device_status(NULL, buf); 22578c2ecf20Sopenharmony_ci 22588c2ecf20Sopenharmony_ci *offset = size <= len ? size : len; 22598c2ecf20Sopenharmony_ci size = copy_to_user(ubuf, buf, *offset); 22608c2ecf20Sopenharmony_ci if (size) 22618c2ecf20Sopenharmony_ci rv = -EFAULT; 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_ci kfree(buf); 22648c2ecf20Sopenharmony_ci return rv ? rv : *offset; 22658c2ecf20Sopenharmony_ci} 22668c2ecf20Sopenharmony_ci 22678c2ecf20Sopenharmony_cistatic ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf, 22688c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 22698c2ecf20Sopenharmony_ci{ 22708c2ecf20Sopenharmony_ci struct driver_data *dd = (struct driver_data *)f->private_data; 22718c2ecf20Sopenharmony_ci char *buf; 22728c2ecf20Sopenharmony_ci u32 group_allocated; 22738c2ecf20Sopenharmony_ci int size = *offset; 22748c2ecf20Sopenharmony_ci int n, rv = 0; 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci if (!len || size) 22778c2ecf20Sopenharmony_ci return 0; 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci buf = kzalloc(MTIP_DFS_MAX_BUF_SIZE, GFP_KERNEL); 22808c2ecf20Sopenharmony_ci if (!buf) { 22818c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 22828c2ecf20Sopenharmony_ci "Memory allocation: register buffer\n"); 22838c2ecf20Sopenharmony_ci return -ENOMEM; 22848c2ecf20Sopenharmony_ci } 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "H/ S ACTive : [ 0x"); 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 22898c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 22908c2ecf20Sopenharmony_ci readl(dd->port->s_active[n])); 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 22938c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "H/ Command Issue : [ 0x"); 22948c2ecf20Sopenharmony_ci 22958c2ecf20Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 22968c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 22978c2ecf20Sopenharmony_ci readl(dd->port->cmd_issue[n])); 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 23008c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "H/ Completed : [ 0x"); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 23038c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 23048c2ecf20Sopenharmony_ci readl(dd->port->completed[n])); 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 23078c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "H/ PORT IRQ STAT : [ 0x%08X ]\n", 23088c2ecf20Sopenharmony_ci readl(dd->port->mmio + PORT_IRQ_STAT)); 23098c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "H/ HOST IRQ STAT : [ 0x%08X ]\n", 23108c2ecf20Sopenharmony_ci readl(dd->mmio + HOST_IRQ_STAT)); 23118c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "\n"); 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "L/ Commands in Q : [ 0x"); 23148c2ecf20Sopenharmony_ci 23158c2ecf20Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) { 23168c2ecf20Sopenharmony_ci if (sizeof(long) > sizeof(u32)) 23178c2ecf20Sopenharmony_ci group_allocated = 23188c2ecf20Sopenharmony_ci dd->port->cmds_to_issue[n/2] >> (32*(n&1)); 23198c2ecf20Sopenharmony_ci else 23208c2ecf20Sopenharmony_ci group_allocated = dd->port->cmds_to_issue[n]; 23218c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "%08X ", group_allocated); 23228c2ecf20Sopenharmony_ci } 23238c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci *offset = size <= len ? size : len; 23268c2ecf20Sopenharmony_ci size = copy_to_user(ubuf, buf, *offset); 23278c2ecf20Sopenharmony_ci if (size) 23288c2ecf20Sopenharmony_ci rv = -EFAULT; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci kfree(buf); 23318c2ecf20Sopenharmony_ci return rv ? rv : *offset; 23328c2ecf20Sopenharmony_ci} 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_cistatic ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf, 23358c2ecf20Sopenharmony_ci size_t len, loff_t *offset) 23368c2ecf20Sopenharmony_ci{ 23378c2ecf20Sopenharmony_ci struct driver_data *dd = (struct driver_data *)f->private_data; 23388c2ecf20Sopenharmony_ci char *buf; 23398c2ecf20Sopenharmony_ci int size = *offset; 23408c2ecf20Sopenharmony_ci int rv = 0; 23418c2ecf20Sopenharmony_ci 23428c2ecf20Sopenharmony_ci if (!len || size) 23438c2ecf20Sopenharmony_ci return 0; 23448c2ecf20Sopenharmony_ci 23458c2ecf20Sopenharmony_ci buf = kzalloc(MTIP_DFS_MAX_BUF_SIZE, GFP_KERNEL); 23468c2ecf20Sopenharmony_ci if (!buf) { 23478c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 23488c2ecf20Sopenharmony_ci "Memory allocation: flag buffer\n"); 23498c2ecf20Sopenharmony_ci return -ENOMEM; 23508c2ecf20Sopenharmony_ci } 23518c2ecf20Sopenharmony_ci 23528c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n", 23538c2ecf20Sopenharmony_ci dd->port->flags); 23548c2ecf20Sopenharmony_ci size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n", 23558c2ecf20Sopenharmony_ci dd->dd_flag); 23568c2ecf20Sopenharmony_ci 23578c2ecf20Sopenharmony_ci *offset = size <= len ? size : len; 23588c2ecf20Sopenharmony_ci size = copy_to_user(ubuf, buf, *offset); 23598c2ecf20Sopenharmony_ci if (size) 23608c2ecf20Sopenharmony_ci rv = -EFAULT; 23618c2ecf20Sopenharmony_ci 23628c2ecf20Sopenharmony_ci kfree(buf); 23638c2ecf20Sopenharmony_ci return rv ? rv : *offset; 23648c2ecf20Sopenharmony_ci} 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_cistatic const struct file_operations mtip_device_status_fops = { 23678c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 23688c2ecf20Sopenharmony_ci .open = simple_open, 23698c2ecf20Sopenharmony_ci .read = mtip_hw_read_device_status, 23708c2ecf20Sopenharmony_ci .llseek = no_llseek, 23718c2ecf20Sopenharmony_ci}; 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_cistatic const struct file_operations mtip_regs_fops = { 23748c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 23758c2ecf20Sopenharmony_ci .open = simple_open, 23768c2ecf20Sopenharmony_ci .read = mtip_hw_read_registers, 23778c2ecf20Sopenharmony_ci .llseek = no_llseek, 23788c2ecf20Sopenharmony_ci}; 23798c2ecf20Sopenharmony_ci 23808c2ecf20Sopenharmony_cistatic const struct file_operations mtip_flags_fops = { 23818c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 23828c2ecf20Sopenharmony_ci .open = simple_open, 23838c2ecf20Sopenharmony_ci .read = mtip_hw_read_flags, 23848c2ecf20Sopenharmony_ci .llseek = no_llseek, 23858c2ecf20Sopenharmony_ci}; 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci/* 23888c2ecf20Sopenharmony_ci * Create the sysfs related attributes. 23898c2ecf20Sopenharmony_ci * 23908c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 23918c2ecf20Sopenharmony_ci * @kobj Pointer to the kobj for the block device. 23928c2ecf20Sopenharmony_ci * 23938c2ecf20Sopenharmony_ci * return value 23948c2ecf20Sopenharmony_ci * 0 Operation completed successfully. 23958c2ecf20Sopenharmony_ci * -EINVAL Invalid parameter. 23968c2ecf20Sopenharmony_ci */ 23978c2ecf20Sopenharmony_cistatic int mtip_hw_sysfs_init(struct driver_data *dd, struct kobject *kobj) 23988c2ecf20Sopenharmony_ci{ 23998c2ecf20Sopenharmony_ci if (!kobj || !dd) 24008c2ecf20Sopenharmony_ci return -EINVAL; 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci if (sysfs_create_file(kobj, &dev_attr_status.attr)) 24038c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 24048c2ecf20Sopenharmony_ci "Error creating 'status' sysfs entry\n"); 24058c2ecf20Sopenharmony_ci return 0; 24068c2ecf20Sopenharmony_ci} 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci/* 24098c2ecf20Sopenharmony_ci * Remove the sysfs related attributes. 24108c2ecf20Sopenharmony_ci * 24118c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 24128c2ecf20Sopenharmony_ci * @kobj Pointer to the kobj for the block device. 24138c2ecf20Sopenharmony_ci * 24148c2ecf20Sopenharmony_ci * return value 24158c2ecf20Sopenharmony_ci * 0 Operation completed successfully. 24168c2ecf20Sopenharmony_ci * -EINVAL Invalid parameter. 24178c2ecf20Sopenharmony_ci */ 24188c2ecf20Sopenharmony_cistatic int mtip_hw_sysfs_exit(struct driver_data *dd, struct kobject *kobj) 24198c2ecf20Sopenharmony_ci{ 24208c2ecf20Sopenharmony_ci if (!kobj || !dd) 24218c2ecf20Sopenharmony_ci return -EINVAL; 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci sysfs_remove_file(kobj, &dev_attr_status.attr); 24248c2ecf20Sopenharmony_ci 24258c2ecf20Sopenharmony_ci return 0; 24268c2ecf20Sopenharmony_ci} 24278c2ecf20Sopenharmony_ci 24288c2ecf20Sopenharmony_cistatic int mtip_hw_debugfs_init(struct driver_data *dd) 24298c2ecf20Sopenharmony_ci{ 24308c2ecf20Sopenharmony_ci if (!dfs_parent) 24318c2ecf20Sopenharmony_ci return -1; 24328c2ecf20Sopenharmony_ci 24338c2ecf20Sopenharmony_ci dd->dfs_node = debugfs_create_dir(dd->disk->disk_name, dfs_parent); 24348c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dd->dfs_node)) { 24358c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 24368c2ecf20Sopenharmony_ci "Error creating node %s under debugfs\n", 24378c2ecf20Sopenharmony_ci dd->disk->disk_name); 24388c2ecf20Sopenharmony_ci dd->dfs_node = NULL; 24398c2ecf20Sopenharmony_ci return -1; 24408c2ecf20Sopenharmony_ci } 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci debugfs_create_file("flags", 0444, dd->dfs_node, dd, &mtip_flags_fops); 24438c2ecf20Sopenharmony_ci debugfs_create_file("registers", 0444, dd->dfs_node, dd, 24448c2ecf20Sopenharmony_ci &mtip_regs_fops); 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci return 0; 24478c2ecf20Sopenharmony_ci} 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_cistatic void mtip_hw_debugfs_exit(struct driver_data *dd) 24508c2ecf20Sopenharmony_ci{ 24518c2ecf20Sopenharmony_ci debugfs_remove_recursive(dd->dfs_node); 24528c2ecf20Sopenharmony_ci} 24538c2ecf20Sopenharmony_ci 24548c2ecf20Sopenharmony_ci/* 24558c2ecf20Sopenharmony_ci * Perform any init/resume time hardware setup 24568c2ecf20Sopenharmony_ci * 24578c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 24588c2ecf20Sopenharmony_ci * 24598c2ecf20Sopenharmony_ci * return value 24608c2ecf20Sopenharmony_ci * None 24618c2ecf20Sopenharmony_ci */ 24628c2ecf20Sopenharmony_cistatic inline void hba_setup(struct driver_data *dd) 24638c2ecf20Sopenharmony_ci{ 24648c2ecf20Sopenharmony_ci u32 hwdata; 24658c2ecf20Sopenharmony_ci hwdata = readl(dd->mmio + HOST_HSORG); 24668c2ecf20Sopenharmony_ci 24678c2ecf20Sopenharmony_ci /* interrupt bug workaround: use only 1 IS bit.*/ 24688c2ecf20Sopenharmony_ci writel(hwdata | 24698c2ecf20Sopenharmony_ci HSORG_DISABLE_SLOTGRP_INTR | 24708c2ecf20Sopenharmony_ci HSORG_DISABLE_SLOTGRP_PXIS, 24718c2ecf20Sopenharmony_ci dd->mmio + HOST_HSORG); 24728c2ecf20Sopenharmony_ci} 24738c2ecf20Sopenharmony_ci 24748c2ecf20Sopenharmony_cistatic int mtip_device_unaligned_constrained(struct driver_data *dd) 24758c2ecf20Sopenharmony_ci{ 24768c2ecf20Sopenharmony_ci return (dd->pdev->device == P420M_DEVICE_ID ? 1 : 0); 24778c2ecf20Sopenharmony_ci} 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci/* 24808c2ecf20Sopenharmony_ci * Detect the details of the product, and store anything needed 24818c2ecf20Sopenharmony_ci * into the driver data structure. This includes product type and 24828c2ecf20Sopenharmony_ci * version and number of slot groups. 24838c2ecf20Sopenharmony_ci * 24848c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 24858c2ecf20Sopenharmony_ci * 24868c2ecf20Sopenharmony_ci * return value 24878c2ecf20Sopenharmony_ci * None 24888c2ecf20Sopenharmony_ci */ 24898c2ecf20Sopenharmony_cistatic void mtip_detect_product(struct driver_data *dd) 24908c2ecf20Sopenharmony_ci{ 24918c2ecf20Sopenharmony_ci u32 hwdata; 24928c2ecf20Sopenharmony_ci unsigned int rev, slotgroups; 24938c2ecf20Sopenharmony_ci 24948c2ecf20Sopenharmony_ci /* 24958c2ecf20Sopenharmony_ci * HBA base + 0xFC [15:0] - vendor-specific hardware interface 24968c2ecf20Sopenharmony_ci * info register: 24978c2ecf20Sopenharmony_ci * [15:8] hardware/software interface rev# 24988c2ecf20Sopenharmony_ci * [ 3] asic-style interface 24998c2ecf20Sopenharmony_ci * [ 2:0] number of slot groups, minus 1 (only valid for asic-style). 25008c2ecf20Sopenharmony_ci */ 25018c2ecf20Sopenharmony_ci hwdata = readl(dd->mmio + HOST_HSORG); 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci dd->product_type = MTIP_PRODUCT_UNKNOWN; 25048c2ecf20Sopenharmony_ci dd->slot_groups = 1; 25058c2ecf20Sopenharmony_ci 25068c2ecf20Sopenharmony_ci if (hwdata & 0x8) { 25078c2ecf20Sopenharmony_ci dd->product_type = MTIP_PRODUCT_ASICFPGA; 25088c2ecf20Sopenharmony_ci rev = (hwdata & HSORG_HWREV) >> 8; 25098c2ecf20Sopenharmony_ci slotgroups = (hwdata & HSORG_SLOTGROUPS) + 1; 25108c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 25118c2ecf20Sopenharmony_ci "ASIC-FPGA design, HS rev 0x%x, " 25128c2ecf20Sopenharmony_ci "%i slot groups [%i slots]\n", 25138c2ecf20Sopenharmony_ci rev, 25148c2ecf20Sopenharmony_ci slotgroups, 25158c2ecf20Sopenharmony_ci slotgroups * 32); 25168c2ecf20Sopenharmony_ci 25178c2ecf20Sopenharmony_ci if (slotgroups > MTIP_MAX_SLOT_GROUPS) { 25188c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 25198c2ecf20Sopenharmony_ci "Warning: driver only supports " 25208c2ecf20Sopenharmony_ci "%i slot groups.\n", MTIP_MAX_SLOT_GROUPS); 25218c2ecf20Sopenharmony_ci slotgroups = MTIP_MAX_SLOT_GROUPS; 25228c2ecf20Sopenharmony_ci } 25238c2ecf20Sopenharmony_ci dd->slot_groups = slotgroups; 25248c2ecf20Sopenharmony_ci return; 25258c2ecf20Sopenharmony_ci } 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, "Unrecognized product id\n"); 25288c2ecf20Sopenharmony_ci} 25298c2ecf20Sopenharmony_ci 25308c2ecf20Sopenharmony_ci/* 25318c2ecf20Sopenharmony_ci * Blocking wait for FTL rebuild to complete 25328c2ecf20Sopenharmony_ci * 25338c2ecf20Sopenharmony_ci * @dd Pointer to the DRIVER_DATA structure. 25348c2ecf20Sopenharmony_ci * 25358c2ecf20Sopenharmony_ci * return value 25368c2ecf20Sopenharmony_ci * 0 FTL rebuild completed successfully 25378c2ecf20Sopenharmony_ci * -EFAULT FTL rebuild error/timeout/interruption 25388c2ecf20Sopenharmony_ci */ 25398c2ecf20Sopenharmony_cistatic int mtip_ftl_rebuild_poll(struct driver_data *dd) 25408c2ecf20Sopenharmony_ci{ 25418c2ecf20Sopenharmony_ci unsigned long timeout, cnt = 0, start; 25428c2ecf20Sopenharmony_ci 25438c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 25448c2ecf20Sopenharmony_ci "FTL rebuild in progress. Polling for completion.\n"); 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci start = jiffies; 25478c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(MTIP_FTL_REBUILD_TIMEOUT_MS); 25488c2ecf20Sopenharmony_ci 25498c2ecf20Sopenharmony_ci do { 25508c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 25518c2ecf20Sopenharmony_ci &dd->dd_flag))) 25528c2ecf20Sopenharmony_ci return -EFAULT; 25538c2ecf20Sopenharmony_ci if (mtip_check_surprise_removal(dd->pdev)) 25548c2ecf20Sopenharmony_ci return -EFAULT; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci if (mtip_get_identify(dd->port, NULL) < 0) 25578c2ecf20Sopenharmony_ci return -EFAULT; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci if (*(dd->port->identify + MTIP_FTL_REBUILD_OFFSET) == 25608c2ecf20Sopenharmony_ci MTIP_FTL_REBUILD_MAGIC) { 25618c2ecf20Sopenharmony_ci ssleep(1); 25628c2ecf20Sopenharmony_ci /* Print message every 3 minutes */ 25638c2ecf20Sopenharmony_ci if (cnt++ >= 180) { 25648c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 25658c2ecf20Sopenharmony_ci "FTL rebuild in progress (%d secs).\n", 25668c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 25678c2ecf20Sopenharmony_ci cnt = 0; 25688c2ecf20Sopenharmony_ci } 25698c2ecf20Sopenharmony_ci } else { 25708c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 25718c2ecf20Sopenharmony_ci "FTL rebuild complete (%d secs).\n", 25728c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 25738c2ecf20Sopenharmony_ci mtip_block_initialize(dd); 25748c2ecf20Sopenharmony_ci return 0; 25758c2ecf20Sopenharmony_ci } 25768c2ecf20Sopenharmony_ci } while (time_before(jiffies, timeout)); 25778c2ecf20Sopenharmony_ci 25788c2ecf20Sopenharmony_ci /* Check for timeout */ 25798c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 25808c2ecf20Sopenharmony_ci "Timed out waiting for FTL rebuild to complete (%d secs).\n", 25818c2ecf20Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 25828c2ecf20Sopenharmony_ci return -EFAULT; 25838c2ecf20Sopenharmony_ci} 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_cistatic void mtip_softirq_done_fn(struct request *rq) 25868c2ecf20Sopenharmony_ci{ 25878c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 25888c2ecf20Sopenharmony_ci struct driver_data *dd = rq->q->queuedata; 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_ci /* Unmap the DMA scatter list entries */ 25918c2ecf20Sopenharmony_ci dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents, 25928c2ecf20Sopenharmony_ci cmd->direction); 25938c2ecf20Sopenharmony_ci 25948c2ecf20Sopenharmony_ci if (unlikely(cmd->unaligned)) 25958c2ecf20Sopenharmony_ci atomic_inc(&dd->port->cmd_slot_unal); 25968c2ecf20Sopenharmony_ci 25978c2ecf20Sopenharmony_ci blk_mq_end_request(rq, cmd->status); 25988c2ecf20Sopenharmony_ci} 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_cistatic bool mtip_abort_cmd(struct request *req, void *data, bool reserved) 26018c2ecf20Sopenharmony_ci{ 26028c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(req); 26038c2ecf20Sopenharmony_ci struct driver_data *dd = data; 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " Aborting request, tag = %d\n", req->tag); 26068c2ecf20Sopenharmony_ci 26078c2ecf20Sopenharmony_ci clear_bit(req->tag, dd->port->cmds_to_issue); 26088c2ecf20Sopenharmony_ci cmd->status = BLK_STS_IOERR; 26098c2ecf20Sopenharmony_ci mtip_softirq_done_fn(req); 26108c2ecf20Sopenharmony_ci return true; 26118c2ecf20Sopenharmony_ci} 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_cistatic bool mtip_queue_cmd(struct request *req, void *data, bool reserved) 26148c2ecf20Sopenharmony_ci{ 26158c2ecf20Sopenharmony_ci struct driver_data *dd = data; 26168c2ecf20Sopenharmony_ci 26178c2ecf20Sopenharmony_ci set_bit(req->tag, dd->port->cmds_to_issue); 26188c2ecf20Sopenharmony_ci blk_abort_request(req); 26198c2ecf20Sopenharmony_ci return true; 26208c2ecf20Sopenharmony_ci} 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci/* 26238c2ecf20Sopenharmony_ci * service thread to issue queued commands 26248c2ecf20Sopenharmony_ci * 26258c2ecf20Sopenharmony_ci * @data Pointer to the driver data structure. 26268c2ecf20Sopenharmony_ci * 26278c2ecf20Sopenharmony_ci * return value 26288c2ecf20Sopenharmony_ci * 0 26298c2ecf20Sopenharmony_ci */ 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_cistatic int mtip_service_thread(void *data) 26328c2ecf20Sopenharmony_ci{ 26338c2ecf20Sopenharmony_ci struct driver_data *dd = (struct driver_data *)data; 26348c2ecf20Sopenharmony_ci unsigned long slot, slot_start, slot_wrap, to; 26358c2ecf20Sopenharmony_ci unsigned int num_cmd_slots = dd->slot_groups * 32; 26368c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci while (1) { 26398c2ecf20Sopenharmony_ci if (kthread_should_stop() || 26408c2ecf20Sopenharmony_ci test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) 26418c2ecf20Sopenharmony_ci goto st_out; 26428c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); 26438c2ecf20Sopenharmony_ci 26448c2ecf20Sopenharmony_ci /* 26458c2ecf20Sopenharmony_ci * the condition is to check neither an internal command is 26468c2ecf20Sopenharmony_ci * is in progress nor error handling is active 26478c2ecf20Sopenharmony_ci */ 26488c2ecf20Sopenharmony_ci wait_event_interruptible(port->svc_wait, (port->flags) && 26498c2ecf20Sopenharmony_ci (port->flags & MTIP_PF_SVC_THD_WORK)); 26508c2ecf20Sopenharmony_ci 26518c2ecf20Sopenharmony_ci if (kthread_should_stop() || 26528c2ecf20Sopenharmony_ci test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) 26538c2ecf20Sopenharmony_ci goto st_out; 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 26568c2ecf20Sopenharmony_ci &dd->dd_flag))) 26578c2ecf20Sopenharmony_ci goto st_out; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_cirestart_eh: 26628c2ecf20Sopenharmony_ci /* Demux bits: start with error handling */ 26638c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) { 26648c2ecf20Sopenharmony_ci mtip_handle_tfe(dd); 26658c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); 26668c2ecf20Sopenharmony_ci } 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) 26698c2ecf20Sopenharmony_ci goto restart_eh; 26708c2ecf20Sopenharmony_ci 26718c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_TO_ACTIVE_BIT, &port->flags)) { 26728c2ecf20Sopenharmony_ci to = jiffies + msecs_to_jiffies(5000); 26738c2ecf20Sopenharmony_ci 26748c2ecf20Sopenharmony_ci do { 26758c2ecf20Sopenharmony_ci mdelay(100); 26768c2ecf20Sopenharmony_ci } while (atomic_read(&dd->irq_workers_active) != 0 && 26778c2ecf20Sopenharmony_ci time_before(jiffies, to)); 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci if (atomic_read(&dd->irq_workers_active) != 0) 26808c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 26818c2ecf20Sopenharmony_ci "Completion workers still active!"); 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_ci blk_mq_quiesce_queue(dd->queue); 26848c2ecf20Sopenharmony_ci 26858c2ecf20Sopenharmony_ci blk_mq_tagset_busy_iter(&dd->tags, mtip_queue_cmd, dd); 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci set_bit(MTIP_PF_ISSUE_CMDS_BIT, &dd->port->flags); 26888c2ecf20Sopenharmony_ci 26898c2ecf20Sopenharmony_ci if (mtip_device_reset(dd)) 26908c2ecf20Sopenharmony_ci blk_mq_tagset_busy_iter(&dd->tags, 26918c2ecf20Sopenharmony_ci mtip_abort_cmd, dd); 26928c2ecf20Sopenharmony_ci 26938c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags); 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(dd->queue); 26968c2ecf20Sopenharmony_ci } 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { 26998c2ecf20Sopenharmony_ci slot = 1; 27008c2ecf20Sopenharmony_ci /* used to restrict the loop to one iteration */ 27018c2ecf20Sopenharmony_ci slot_start = num_cmd_slots; 27028c2ecf20Sopenharmony_ci slot_wrap = 0; 27038c2ecf20Sopenharmony_ci while (1) { 27048c2ecf20Sopenharmony_ci slot = find_next_bit(port->cmds_to_issue, 27058c2ecf20Sopenharmony_ci num_cmd_slots, slot); 27068c2ecf20Sopenharmony_ci if (slot_wrap == 1) { 27078c2ecf20Sopenharmony_ci if ((slot_start >= slot) || 27088c2ecf20Sopenharmony_ci (slot >= num_cmd_slots)) 27098c2ecf20Sopenharmony_ci break; 27108c2ecf20Sopenharmony_ci } 27118c2ecf20Sopenharmony_ci if (unlikely(slot_start == num_cmd_slots)) 27128c2ecf20Sopenharmony_ci slot_start = slot; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci if (unlikely(slot == num_cmd_slots)) { 27158c2ecf20Sopenharmony_ci slot = 1; 27168c2ecf20Sopenharmony_ci slot_wrap = 1; 27178c2ecf20Sopenharmony_ci continue; 27188c2ecf20Sopenharmony_ci } 27198c2ecf20Sopenharmony_ci 27208c2ecf20Sopenharmony_ci /* Issue the command to the hardware */ 27218c2ecf20Sopenharmony_ci mtip_issue_ncq_command(port, slot); 27228c2ecf20Sopenharmony_ci 27238c2ecf20Sopenharmony_ci clear_bit(slot, port->cmds_to_issue); 27248c2ecf20Sopenharmony_ci } 27258c2ecf20Sopenharmony_ci 27268c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); 27278c2ecf20Sopenharmony_ci } 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) { 27308c2ecf20Sopenharmony_ci if (mtip_ftl_rebuild_poll(dd) == 0) 27318c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_REBUILD_BIT, &port->flags); 27328c2ecf20Sopenharmony_ci } 27338c2ecf20Sopenharmony_ci } 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_cist_out: 27368c2ecf20Sopenharmony_ci return 0; 27378c2ecf20Sopenharmony_ci} 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci/* 27408c2ecf20Sopenharmony_ci * DMA region teardown 27418c2ecf20Sopenharmony_ci * 27428c2ecf20Sopenharmony_ci * @dd Pointer to driver_data structure 27438c2ecf20Sopenharmony_ci * 27448c2ecf20Sopenharmony_ci * return value 27458c2ecf20Sopenharmony_ci * None 27468c2ecf20Sopenharmony_ci */ 27478c2ecf20Sopenharmony_cistatic void mtip_dma_free(struct driver_data *dd) 27488c2ecf20Sopenharmony_ci{ 27498c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 27508c2ecf20Sopenharmony_ci 27518c2ecf20Sopenharmony_ci if (port->block1) 27528c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 27538c2ecf20Sopenharmony_ci port->block1, port->block1_dma); 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci if (port->command_list) { 27568c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, 27578c2ecf20Sopenharmony_ci port->command_list, port->command_list_dma); 27588c2ecf20Sopenharmony_ci } 27598c2ecf20Sopenharmony_ci} 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci/* 27628c2ecf20Sopenharmony_ci * DMA region setup 27638c2ecf20Sopenharmony_ci * 27648c2ecf20Sopenharmony_ci * @dd Pointer to driver_data structure 27658c2ecf20Sopenharmony_ci * 27668c2ecf20Sopenharmony_ci * return value 27678c2ecf20Sopenharmony_ci * -ENOMEM Not enough free DMA region space to initialize driver 27688c2ecf20Sopenharmony_ci */ 27698c2ecf20Sopenharmony_cistatic int mtip_dma_alloc(struct driver_data *dd) 27708c2ecf20Sopenharmony_ci{ 27718c2ecf20Sopenharmony_ci struct mtip_port *port = dd->port; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci /* Allocate dma memory for RX Fis, Identify, and Sector Bufffer */ 27748c2ecf20Sopenharmony_ci port->block1 = 27758c2ecf20Sopenharmony_ci dma_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 27768c2ecf20Sopenharmony_ci &port->block1_dma, GFP_KERNEL); 27778c2ecf20Sopenharmony_ci if (!port->block1) 27788c2ecf20Sopenharmony_ci return -ENOMEM; 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci /* Allocate dma memory for command list */ 27818c2ecf20Sopenharmony_ci port->command_list = 27828c2ecf20Sopenharmony_ci dma_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, 27838c2ecf20Sopenharmony_ci &port->command_list_dma, GFP_KERNEL); 27848c2ecf20Sopenharmony_ci if (!port->command_list) { 27858c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 27868c2ecf20Sopenharmony_ci port->block1, port->block1_dma); 27878c2ecf20Sopenharmony_ci port->block1 = NULL; 27888c2ecf20Sopenharmony_ci port->block1_dma = 0; 27898c2ecf20Sopenharmony_ci return -ENOMEM; 27908c2ecf20Sopenharmony_ci } 27918c2ecf20Sopenharmony_ci 27928c2ecf20Sopenharmony_ci /* Setup all pointers into first DMA region */ 27938c2ecf20Sopenharmony_ci port->rxfis = port->block1 + AHCI_RX_FIS_OFFSET; 27948c2ecf20Sopenharmony_ci port->rxfis_dma = port->block1_dma + AHCI_RX_FIS_OFFSET; 27958c2ecf20Sopenharmony_ci port->identify = port->block1 + AHCI_IDFY_OFFSET; 27968c2ecf20Sopenharmony_ci port->identify_dma = port->block1_dma + AHCI_IDFY_OFFSET; 27978c2ecf20Sopenharmony_ci port->log_buf = port->block1 + AHCI_SECTBUF_OFFSET; 27988c2ecf20Sopenharmony_ci port->log_buf_dma = port->block1_dma + AHCI_SECTBUF_OFFSET; 27998c2ecf20Sopenharmony_ci port->smart_buf = port->block1 + AHCI_SMARTBUF_OFFSET; 28008c2ecf20Sopenharmony_ci port->smart_buf_dma = port->block1_dma + AHCI_SMARTBUF_OFFSET; 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci return 0; 28038c2ecf20Sopenharmony_ci} 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_cistatic int mtip_hw_get_identify(struct driver_data *dd) 28068c2ecf20Sopenharmony_ci{ 28078c2ecf20Sopenharmony_ci struct smart_attr attr242; 28088c2ecf20Sopenharmony_ci unsigned char *buf; 28098c2ecf20Sopenharmony_ci int rv; 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci if (mtip_get_identify(dd->port, NULL) < 0) 28128c2ecf20Sopenharmony_ci return -EFAULT; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci if (*(dd->port->identify + MTIP_FTL_REBUILD_OFFSET) == 28158c2ecf20Sopenharmony_ci MTIP_FTL_REBUILD_MAGIC) { 28168c2ecf20Sopenharmony_ci set_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags); 28178c2ecf20Sopenharmony_ci return MTIP_FTL_REBUILD_MAGIC; 28188c2ecf20Sopenharmony_ci } 28198c2ecf20Sopenharmony_ci mtip_dump_identify(dd->port); 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci /* check write protect, over temp and rebuild statuses */ 28228c2ecf20Sopenharmony_ci rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, 28238c2ecf20Sopenharmony_ci dd->port->log_buf, 28248c2ecf20Sopenharmony_ci dd->port->log_buf_dma, 1); 28258c2ecf20Sopenharmony_ci if (rv) { 28268c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 28278c2ecf20Sopenharmony_ci "Error in READ LOG EXT (10h) command\n"); 28288c2ecf20Sopenharmony_ci /* non-critical error, don't fail the load */ 28298c2ecf20Sopenharmony_ci } else { 28308c2ecf20Sopenharmony_ci buf = (unsigned char *)dd->port->log_buf; 28318c2ecf20Sopenharmony_ci if (buf[259] & 0x1) { 28328c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 28338c2ecf20Sopenharmony_ci "Write protect bit is set.\n"); 28348c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); 28358c2ecf20Sopenharmony_ci } 28368c2ecf20Sopenharmony_ci if (buf[288] == 0xF7) { 28378c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 28388c2ecf20Sopenharmony_ci "Exceeded Tmax, drive in thermal shutdown.\n"); 28398c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); 28408c2ecf20Sopenharmony_ci } 28418c2ecf20Sopenharmony_ci if (buf[288] == 0xBF) { 28428c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 28438c2ecf20Sopenharmony_ci "Drive indicates rebuild has failed.\n"); 28448c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); 28458c2ecf20Sopenharmony_ci } 28468c2ecf20Sopenharmony_ci } 28478c2ecf20Sopenharmony_ci 28488c2ecf20Sopenharmony_ci /* get write protect progess */ 28498c2ecf20Sopenharmony_ci memset(&attr242, 0, sizeof(struct smart_attr)); 28508c2ecf20Sopenharmony_ci if (mtip_get_smart_attr(dd->port, 242, &attr242)) 28518c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 28528c2ecf20Sopenharmony_ci "Unable to check write protect progress\n"); 28538c2ecf20Sopenharmony_ci else 28548c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 28558c2ecf20Sopenharmony_ci "Write protect progress: %u%% (%u blocks)\n", 28568c2ecf20Sopenharmony_ci attr242.cur, le32_to_cpu(attr242.data)); 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci return rv; 28598c2ecf20Sopenharmony_ci} 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci/* 28628c2ecf20Sopenharmony_ci * Called once for each card. 28638c2ecf20Sopenharmony_ci * 28648c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 28658c2ecf20Sopenharmony_ci * 28668c2ecf20Sopenharmony_ci * return value 28678c2ecf20Sopenharmony_ci * 0 on success, else an error code. 28688c2ecf20Sopenharmony_ci */ 28698c2ecf20Sopenharmony_cistatic int mtip_hw_init(struct driver_data *dd) 28708c2ecf20Sopenharmony_ci{ 28718c2ecf20Sopenharmony_ci int i; 28728c2ecf20Sopenharmony_ci int rv; 28738c2ecf20Sopenharmony_ci unsigned long timeout, timetaken; 28748c2ecf20Sopenharmony_ci 28758c2ecf20Sopenharmony_ci dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR]; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci mtip_detect_product(dd); 28788c2ecf20Sopenharmony_ci if (dd->product_type == MTIP_PRODUCT_UNKNOWN) { 28798c2ecf20Sopenharmony_ci rv = -EIO; 28808c2ecf20Sopenharmony_ci goto out1; 28818c2ecf20Sopenharmony_ci } 28828c2ecf20Sopenharmony_ci 28838c2ecf20Sopenharmony_ci hba_setup(dd); 28848c2ecf20Sopenharmony_ci 28858c2ecf20Sopenharmony_ci dd->port = kzalloc_node(sizeof(struct mtip_port), GFP_KERNEL, 28868c2ecf20Sopenharmony_ci dd->numa_node); 28878c2ecf20Sopenharmony_ci if (!dd->port) { 28888c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 28898c2ecf20Sopenharmony_ci "Memory allocation: port structure\n"); 28908c2ecf20Sopenharmony_ci return -ENOMEM; 28918c2ecf20Sopenharmony_ci } 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci /* Continue workqueue setup */ 28948c2ecf20Sopenharmony_ci for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) 28958c2ecf20Sopenharmony_ci dd->work[i].port = dd->port; 28968c2ecf20Sopenharmony_ci 28978c2ecf20Sopenharmony_ci /* Enable unaligned IO constraints for some devices */ 28988c2ecf20Sopenharmony_ci if (mtip_device_unaligned_constrained(dd)) 28998c2ecf20Sopenharmony_ci dd->unal_qdepth = MTIP_MAX_UNALIGNED_SLOTS; 29008c2ecf20Sopenharmony_ci else 29018c2ecf20Sopenharmony_ci dd->unal_qdepth = 0; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci atomic_set(&dd->port->cmd_slot_unal, dd->unal_qdepth); 29048c2ecf20Sopenharmony_ci 29058c2ecf20Sopenharmony_ci /* Spinlock to prevent concurrent issue */ 29068c2ecf20Sopenharmony_ci for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) 29078c2ecf20Sopenharmony_ci spin_lock_init(&dd->port->cmd_issue_lock[i]); 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci /* Set the port mmio base address. */ 29108c2ecf20Sopenharmony_ci dd->port->mmio = dd->mmio + PORT_OFFSET; 29118c2ecf20Sopenharmony_ci dd->port->dd = dd; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci /* DMA allocations */ 29148c2ecf20Sopenharmony_ci rv = mtip_dma_alloc(dd); 29158c2ecf20Sopenharmony_ci if (rv < 0) 29168c2ecf20Sopenharmony_ci goto out1; 29178c2ecf20Sopenharmony_ci 29188c2ecf20Sopenharmony_ci /* Setup the pointers to the extended s_active and CI registers. */ 29198c2ecf20Sopenharmony_ci for (i = 0; i < dd->slot_groups; i++) { 29208c2ecf20Sopenharmony_ci dd->port->s_active[i] = 29218c2ecf20Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_SCR_ACT; 29228c2ecf20Sopenharmony_ci dd->port->cmd_issue[i] = 29238c2ecf20Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_COMMAND_ISSUE; 29248c2ecf20Sopenharmony_ci dd->port->completed[i] = 29258c2ecf20Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_SDBV; 29268c2ecf20Sopenharmony_ci } 29278c2ecf20Sopenharmony_ci 29288c2ecf20Sopenharmony_ci timetaken = jiffies; 29298c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(30000); 29308c2ecf20Sopenharmony_ci while (((readl(dd->port->mmio + PORT_SCR_STAT) & 0x0F) != 0x03) && 29318c2ecf20Sopenharmony_ci time_before(jiffies, timeout)) { 29328c2ecf20Sopenharmony_ci mdelay(100); 29338c2ecf20Sopenharmony_ci } 29348c2ecf20Sopenharmony_ci if (unlikely(mtip_check_surprise_removal(dd->pdev))) { 29358c2ecf20Sopenharmony_ci timetaken = jiffies - timetaken; 29368c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 29378c2ecf20Sopenharmony_ci "Surprise removal detected at %u ms\n", 29388c2ecf20Sopenharmony_ci jiffies_to_msecs(timetaken)); 29398c2ecf20Sopenharmony_ci rv = -ENODEV; 29408c2ecf20Sopenharmony_ci goto out2 ; 29418c2ecf20Sopenharmony_ci } 29428c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) { 29438c2ecf20Sopenharmony_ci timetaken = jiffies - timetaken; 29448c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 29458c2ecf20Sopenharmony_ci "Removal detected at %u ms\n", 29468c2ecf20Sopenharmony_ci jiffies_to_msecs(timetaken)); 29478c2ecf20Sopenharmony_ci rv = -EFAULT; 29488c2ecf20Sopenharmony_ci goto out2; 29498c2ecf20Sopenharmony_ci } 29508c2ecf20Sopenharmony_ci 29518c2ecf20Sopenharmony_ci /* Conditionally reset the HBA. */ 29528c2ecf20Sopenharmony_ci if (!(readl(dd->mmio + HOST_CAP) & HOST_CAP_NZDMA)) { 29538c2ecf20Sopenharmony_ci if (mtip_hba_reset(dd) < 0) { 29548c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 29558c2ecf20Sopenharmony_ci "Card did not reset within timeout\n"); 29568c2ecf20Sopenharmony_ci rv = -EIO; 29578c2ecf20Sopenharmony_ci goto out2; 29588c2ecf20Sopenharmony_ci } 29598c2ecf20Sopenharmony_ci } else { 29608c2ecf20Sopenharmony_ci /* Clear any pending interrupts on the HBA */ 29618c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_IRQ_STAT), 29628c2ecf20Sopenharmony_ci dd->mmio + HOST_IRQ_STAT); 29638c2ecf20Sopenharmony_ci } 29648c2ecf20Sopenharmony_ci 29658c2ecf20Sopenharmony_ci mtip_init_port(dd->port); 29668c2ecf20Sopenharmony_ci mtip_start_port(dd->port); 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_ci /* Setup the ISR and enable interrupts. */ 29698c2ecf20Sopenharmony_ci rv = request_irq(dd->pdev->irq, mtip_irq_handler, IRQF_SHARED, 29708c2ecf20Sopenharmony_ci dev_driver_string(&dd->pdev->dev), dd); 29718c2ecf20Sopenharmony_ci if (rv) { 29728c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 29738c2ecf20Sopenharmony_ci "Unable to allocate IRQ %d\n", dd->pdev->irq); 29748c2ecf20Sopenharmony_ci goto out2; 29758c2ecf20Sopenharmony_ci } 29768c2ecf20Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, get_cpu_mask(dd->isr_binding)); 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci /* Enable interrupts on the HBA. */ 29798c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 29808c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci init_waitqueue_head(&dd->port->svc_wait); 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) { 29858c2ecf20Sopenharmony_ci rv = -EFAULT; 29868c2ecf20Sopenharmony_ci goto out3; 29878c2ecf20Sopenharmony_ci } 29888c2ecf20Sopenharmony_ci 29898c2ecf20Sopenharmony_ci return rv; 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ciout3: 29928c2ecf20Sopenharmony_ci /* Disable interrupts on the HBA. */ 29938c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 29948c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci /* Release the IRQ. */ 29978c2ecf20Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, NULL); 29988c2ecf20Sopenharmony_ci free_irq(dd->pdev->irq, dd); 29998c2ecf20Sopenharmony_ci 30008c2ecf20Sopenharmony_ciout2: 30018c2ecf20Sopenharmony_ci mtip_deinit_port(dd->port); 30028c2ecf20Sopenharmony_ci mtip_dma_free(dd); 30038c2ecf20Sopenharmony_ci 30048c2ecf20Sopenharmony_ciout1: 30058c2ecf20Sopenharmony_ci /* Free the memory allocated for the for structure. */ 30068c2ecf20Sopenharmony_ci kfree(dd->port); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci return rv; 30098c2ecf20Sopenharmony_ci} 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_cistatic int mtip_standby_drive(struct driver_data *dd) 30128c2ecf20Sopenharmony_ci{ 30138c2ecf20Sopenharmony_ci int rv = 0; 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci if (dd->sr || !dd->port) 30168c2ecf20Sopenharmony_ci return -ENODEV; 30178c2ecf20Sopenharmony_ci /* 30188c2ecf20Sopenharmony_ci * Send standby immediate (E0h) to the drive so that it 30198c2ecf20Sopenharmony_ci * saves its state. 30208c2ecf20Sopenharmony_ci */ 30218c2ecf20Sopenharmony_ci if (!test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags) && 30228c2ecf20Sopenharmony_ci !test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag) && 30238c2ecf20Sopenharmony_ci !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) { 30248c2ecf20Sopenharmony_ci rv = mtip_standby_immediate(dd->port); 30258c2ecf20Sopenharmony_ci if (rv) 30268c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 30278c2ecf20Sopenharmony_ci "STANDBY IMMEDIATE failed\n"); 30288c2ecf20Sopenharmony_ci } 30298c2ecf20Sopenharmony_ci return rv; 30308c2ecf20Sopenharmony_ci} 30318c2ecf20Sopenharmony_ci 30328c2ecf20Sopenharmony_ci/* 30338c2ecf20Sopenharmony_ci * Called to deinitialize an interface. 30348c2ecf20Sopenharmony_ci * 30358c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 30368c2ecf20Sopenharmony_ci * 30378c2ecf20Sopenharmony_ci * return value 30388c2ecf20Sopenharmony_ci * 0 30398c2ecf20Sopenharmony_ci */ 30408c2ecf20Sopenharmony_cistatic int mtip_hw_exit(struct driver_data *dd) 30418c2ecf20Sopenharmony_ci{ 30428c2ecf20Sopenharmony_ci if (!dd->sr) { 30438c2ecf20Sopenharmony_ci /* de-initialize the port. */ 30448c2ecf20Sopenharmony_ci mtip_deinit_port(dd->port); 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci /* Disable interrupts on the HBA. */ 30478c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 30488c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 30498c2ecf20Sopenharmony_ci } 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci /* Release the IRQ. */ 30528c2ecf20Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, NULL); 30538c2ecf20Sopenharmony_ci free_irq(dd->pdev->irq, dd); 30548c2ecf20Sopenharmony_ci msleep(1000); 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci /* Free dma regions */ 30578c2ecf20Sopenharmony_ci mtip_dma_free(dd); 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_ci /* Free the memory allocated for the for structure. */ 30608c2ecf20Sopenharmony_ci kfree(dd->port); 30618c2ecf20Sopenharmony_ci dd->port = NULL; 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci return 0; 30648c2ecf20Sopenharmony_ci} 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci/* 30678c2ecf20Sopenharmony_ci * Issue a Standby Immediate command to the device. 30688c2ecf20Sopenharmony_ci * 30698c2ecf20Sopenharmony_ci * This function is called by the Block Layer just before the 30708c2ecf20Sopenharmony_ci * system powers off during a shutdown. 30718c2ecf20Sopenharmony_ci * 30728c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 30738c2ecf20Sopenharmony_ci * 30748c2ecf20Sopenharmony_ci * return value 30758c2ecf20Sopenharmony_ci * 0 30768c2ecf20Sopenharmony_ci */ 30778c2ecf20Sopenharmony_cistatic int mtip_hw_shutdown(struct driver_data *dd) 30788c2ecf20Sopenharmony_ci{ 30798c2ecf20Sopenharmony_ci /* 30808c2ecf20Sopenharmony_ci * Send standby immediate (E0h) to the drive so that it 30818c2ecf20Sopenharmony_ci * saves its state. 30828c2ecf20Sopenharmony_ci */ 30838c2ecf20Sopenharmony_ci mtip_standby_drive(dd); 30848c2ecf20Sopenharmony_ci 30858c2ecf20Sopenharmony_ci return 0; 30868c2ecf20Sopenharmony_ci} 30878c2ecf20Sopenharmony_ci 30888c2ecf20Sopenharmony_ci/* 30898c2ecf20Sopenharmony_ci * Suspend function 30908c2ecf20Sopenharmony_ci * 30918c2ecf20Sopenharmony_ci * This function is called by the Block Layer just before the 30928c2ecf20Sopenharmony_ci * system hibernates. 30938c2ecf20Sopenharmony_ci * 30948c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 30958c2ecf20Sopenharmony_ci * 30968c2ecf20Sopenharmony_ci * return value 30978c2ecf20Sopenharmony_ci * 0 Suspend was successful 30988c2ecf20Sopenharmony_ci * -EFAULT Suspend was not successful 30998c2ecf20Sopenharmony_ci */ 31008c2ecf20Sopenharmony_cistatic int mtip_hw_suspend(struct driver_data *dd) 31018c2ecf20Sopenharmony_ci{ 31028c2ecf20Sopenharmony_ci /* 31038c2ecf20Sopenharmony_ci * Send standby immediate (E0h) to the drive 31048c2ecf20Sopenharmony_ci * so that it saves its state. 31058c2ecf20Sopenharmony_ci */ 31068c2ecf20Sopenharmony_ci if (mtip_standby_drive(dd) != 0) { 31078c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 31088c2ecf20Sopenharmony_ci "Failed standby-immediate command\n"); 31098c2ecf20Sopenharmony_ci return -EFAULT; 31108c2ecf20Sopenharmony_ci } 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci /* Disable interrupts on the HBA.*/ 31138c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 31148c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 31158c2ecf20Sopenharmony_ci mtip_deinit_port(dd->port); 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_ci return 0; 31188c2ecf20Sopenharmony_ci} 31198c2ecf20Sopenharmony_ci 31208c2ecf20Sopenharmony_ci/* 31218c2ecf20Sopenharmony_ci * Resume function 31228c2ecf20Sopenharmony_ci * 31238c2ecf20Sopenharmony_ci * This function is called by the Block Layer as the 31248c2ecf20Sopenharmony_ci * system resumes. 31258c2ecf20Sopenharmony_ci * 31268c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 31278c2ecf20Sopenharmony_ci * 31288c2ecf20Sopenharmony_ci * return value 31298c2ecf20Sopenharmony_ci * 0 Resume was successful 31308c2ecf20Sopenharmony_ci * -EFAULT Resume was not successful 31318c2ecf20Sopenharmony_ci */ 31328c2ecf20Sopenharmony_cistatic int mtip_hw_resume(struct driver_data *dd) 31338c2ecf20Sopenharmony_ci{ 31348c2ecf20Sopenharmony_ci /* Perform any needed hardware setup steps */ 31358c2ecf20Sopenharmony_ci hba_setup(dd); 31368c2ecf20Sopenharmony_ci 31378c2ecf20Sopenharmony_ci /* Reset the HBA */ 31388c2ecf20Sopenharmony_ci if (mtip_hba_reset(dd) != 0) { 31398c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 31408c2ecf20Sopenharmony_ci "Unable to reset the HBA\n"); 31418c2ecf20Sopenharmony_ci return -EFAULT; 31428c2ecf20Sopenharmony_ci } 31438c2ecf20Sopenharmony_ci 31448c2ecf20Sopenharmony_ci /* 31458c2ecf20Sopenharmony_ci * Enable the port, DMA engine, and FIS reception specific 31468c2ecf20Sopenharmony_ci * h/w in controller. 31478c2ecf20Sopenharmony_ci */ 31488c2ecf20Sopenharmony_ci mtip_init_port(dd->port); 31498c2ecf20Sopenharmony_ci mtip_start_port(dd->port); 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci /* Enable interrupts on the HBA.*/ 31528c2ecf20Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 31538c2ecf20Sopenharmony_ci dd->mmio + HOST_CTL); 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci return 0; 31568c2ecf20Sopenharmony_ci} 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci/* 31598c2ecf20Sopenharmony_ci * Helper function for reusing disk name 31608c2ecf20Sopenharmony_ci * upon hot insertion. 31618c2ecf20Sopenharmony_ci */ 31628c2ecf20Sopenharmony_cistatic int rssd_disk_name_format(char *prefix, 31638c2ecf20Sopenharmony_ci int index, 31648c2ecf20Sopenharmony_ci char *buf, 31658c2ecf20Sopenharmony_ci int buflen) 31668c2ecf20Sopenharmony_ci{ 31678c2ecf20Sopenharmony_ci const int base = 'z' - 'a' + 1; 31688c2ecf20Sopenharmony_ci char *begin = buf + strlen(prefix); 31698c2ecf20Sopenharmony_ci char *end = buf + buflen; 31708c2ecf20Sopenharmony_ci char *p; 31718c2ecf20Sopenharmony_ci int unit; 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci p = end - 1; 31748c2ecf20Sopenharmony_ci *p = '\0'; 31758c2ecf20Sopenharmony_ci unit = base; 31768c2ecf20Sopenharmony_ci do { 31778c2ecf20Sopenharmony_ci if (p == begin) 31788c2ecf20Sopenharmony_ci return -EINVAL; 31798c2ecf20Sopenharmony_ci *--p = 'a' + (index % unit); 31808c2ecf20Sopenharmony_ci index = (index / unit) - 1; 31818c2ecf20Sopenharmony_ci } while (index >= 0); 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci memmove(begin, p, end - p); 31848c2ecf20Sopenharmony_ci memcpy(buf, prefix, strlen(prefix)); 31858c2ecf20Sopenharmony_ci 31868c2ecf20Sopenharmony_ci return 0; 31878c2ecf20Sopenharmony_ci} 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci/* 31908c2ecf20Sopenharmony_ci * Block layer IOCTL handler. 31918c2ecf20Sopenharmony_ci * 31928c2ecf20Sopenharmony_ci * @dev Pointer to the block_device structure. 31938c2ecf20Sopenharmony_ci * @mode ignored 31948c2ecf20Sopenharmony_ci * @cmd IOCTL command passed from the user application. 31958c2ecf20Sopenharmony_ci * @arg Argument passed from the user application. 31968c2ecf20Sopenharmony_ci * 31978c2ecf20Sopenharmony_ci * return value 31988c2ecf20Sopenharmony_ci * 0 IOCTL completed successfully. 31998c2ecf20Sopenharmony_ci * -ENOTTY IOCTL not supported or invalid driver data 32008c2ecf20Sopenharmony_ci * structure pointer. 32018c2ecf20Sopenharmony_ci */ 32028c2ecf20Sopenharmony_cistatic int mtip_block_ioctl(struct block_device *dev, 32038c2ecf20Sopenharmony_ci fmode_t mode, 32048c2ecf20Sopenharmony_ci unsigned cmd, 32058c2ecf20Sopenharmony_ci unsigned long arg) 32068c2ecf20Sopenharmony_ci{ 32078c2ecf20Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 32108c2ecf20Sopenharmony_ci return -EACCES; 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci if (!dd) 32138c2ecf20Sopenharmony_ci return -ENOTTY; 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) 32168c2ecf20Sopenharmony_ci return -ENOTTY; 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_ci switch (cmd) { 32198c2ecf20Sopenharmony_ci case BLKFLSBUF: 32208c2ecf20Sopenharmony_ci return -ENOTTY; 32218c2ecf20Sopenharmony_ci default: 32228c2ecf20Sopenharmony_ci return mtip_hw_ioctl(dd, cmd, arg); 32238c2ecf20Sopenharmony_ci } 32248c2ecf20Sopenharmony_ci} 32258c2ecf20Sopenharmony_ci 32268c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 32278c2ecf20Sopenharmony_ci/* 32288c2ecf20Sopenharmony_ci * Block layer compat IOCTL handler. 32298c2ecf20Sopenharmony_ci * 32308c2ecf20Sopenharmony_ci * @dev Pointer to the block_device structure. 32318c2ecf20Sopenharmony_ci * @mode ignored 32328c2ecf20Sopenharmony_ci * @cmd IOCTL command passed from the user application. 32338c2ecf20Sopenharmony_ci * @arg Argument passed from the user application. 32348c2ecf20Sopenharmony_ci * 32358c2ecf20Sopenharmony_ci * return value 32368c2ecf20Sopenharmony_ci * 0 IOCTL completed successfully. 32378c2ecf20Sopenharmony_ci * -ENOTTY IOCTL not supported or invalid driver data 32388c2ecf20Sopenharmony_ci * structure pointer. 32398c2ecf20Sopenharmony_ci */ 32408c2ecf20Sopenharmony_cistatic int mtip_block_compat_ioctl(struct block_device *dev, 32418c2ecf20Sopenharmony_ci fmode_t mode, 32428c2ecf20Sopenharmony_ci unsigned cmd, 32438c2ecf20Sopenharmony_ci unsigned long arg) 32448c2ecf20Sopenharmony_ci{ 32458c2ecf20Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 32488c2ecf20Sopenharmony_ci return -EACCES; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci if (!dd) 32518c2ecf20Sopenharmony_ci return -ENOTTY; 32528c2ecf20Sopenharmony_ci 32538c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) 32548c2ecf20Sopenharmony_ci return -ENOTTY; 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci switch (cmd) { 32578c2ecf20Sopenharmony_ci case BLKFLSBUF: 32588c2ecf20Sopenharmony_ci return -ENOTTY; 32598c2ecf20Sopenharmony_ci case HDIO_DRIVE_TASKFILE: { 32608c2ecf20Sopenharmony_ci struct mtip_compat_ide_task_request_s __user *compat_req_task; 32618c2ecf20Sopenharmony_ci ide_task_request_t req_task; 32628c2ecf20Sopenharmony_ci int compat_tasksize, outtotal, ret; 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci compat_tasksize = 32658c2ecf20Sopenharmony_ci sizeof(struct mtip_compat_ide_task_request_s); 32668c2ecf20Sopenharmony_ci 32678c2ecf20Sopenharmony_ci compat_req_task = 32688c2ecf20Sopenharmony_ci (struct mtip_compat_ide_task_request_s __user *) arg; 32698c2ecf20Sopenharmony_ci 32708c2ecf20Sopenharmony_ci if (copy_from_user(&req_task, (void __user *) arg, 32718c2ecf20Sopenharmony_ci compat_tasksize - (2 * sizeof(compat_long_t)))) 32728c2ecf20Sopenharmony_ci return -EFAULT; 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci if (get_user(req_task.out_size, &compat_req_task->out_size)) 32758c2ecf20Sopenharmony_ci return -EFAULT; 32768c2ecf20Sopenharmony_ci 32778c2ecf20Sopenharmony_ci if (get_user(req_task.in_size, &compat_req_task->in_size)) 32788c2ecf20Sopenharmony_ci return -EFAULT; 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci outtotal = sizeof(struct mtip_compat_ide_task_request_s); 32818c2ecf20Sopenharmony_ci 32828c2ecf20Sopenharmony_ci ret = exec_drive_taskfile(dd, (void __user *) arg, 32838c2ecf20Sopenharmony_ci &req_task, outtotal); 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci if (copy_to_user((void __user *) arg, &req_task, 32868c2ecf20Sopenharmony_ci compat_tasksize - 32878c2ecf20Sopenharmony_ci (2 * sizeof(compat_long_t)))) 32888c2ecf20Sopenharmony_ci return -EFAULT; 32898c2ecf20Sopenharmony_ci 32908c2ecf20Sopenharmony_ci if (put_user(req_task.out_size, &compat_req_task->out_size)) 32918c2ecf20Sopenharmony_ci return -EFAULT; 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ci if (put_user(req_task.in_size, &compat_req_task->in_size)) 32948c2ecf20Sopenharmony_ci return -EFAULT; 32958c2ecf20Sopenharmony_ci 32968c2ecf20Sopenharmony_ci return ret; 32978c2ecf20Sopenharmony_ci } 32988c2ecf20Sopenharmony_ci default: 32998c2ecf20Sopenharmony_ci return mtip_hw_ioctl(dd, cmd, arg); 33008c2ecf20Sopenharmony_ci } 33018c2ecf20Sopenharmony_ci} 33028c2ecf20Sopenharmony_ci#endif 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci/* 33058c2ecf20Sopenharmony_ci * Obtain the geometry of the device. 33068c2ecf20Sopenharmony_ci * 33078c2ecf20Sopenharmony_ci * You may think that this function is obsolete, but some applications, 33088c2ecf20Sopenharmony_ci * fdisk for example still used CHS values. This function describes the 33098c2ecf20Sopenharmony_ci * device as having 224 heads and 56 sectors per cylinder. These values are 33108c2ecf20Sopenharmony_ci * chosen so that each cylinder is aligned on a 4KB boundary. Since a 33118c2ecf20Sopenharmony_ci * partition is described in terms of a start and end cylinder this means 33128c2ecf20Sopenharmony_ci * that each partition is also 4KB aligned. Non-aligned partitions adversely 33138c2ecf20Sopenharmony_ci * affects performance. 33148c2ecf20Sopenharmony_ci * 33158c2ecf20Sopenharmony_ci * @dev Pointer to the block_device strucutre. 33168c2ecf20Sopenharmony_ci * @geo Pointer to a hd_geometry structure. 33178c2ecf20Sopenharmony_ci * 33188c2ecf20Sopenharmony_ci * return value 33198c2ecf20Sopenharmony_ci * 0 Operation completed successfully. 33208c2ecf20Sopenharmony_ci * -ENOTTY An error occurred while reading the drive capacity. 33218c2ecf20Sopenharmony_ci */ 33228c2ecf20Sopenharmony_cistatic int mtip_block_getgeo(struct block_device *dev, 33238c2ecf20Sopenharmony_ci struct hd_geometry *geo) 33248c2ecf20Sopenharmony_ci{ 33258c2ecf20Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 33268c2ecf20Sopenharmony_ci sector_t capacity; 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci if (!dd) 33298c2ecf20Sopenharmony_ci return -ENOTTY; 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci if (!(mtip_hw_get_capacity(dd, &capacity))) { 33328c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 33338c2ecf20Sopenharmony_ci "Could not get drive capacity.\n"); 33348c2ecf20Sopenharmony_ci return -ENOTTY; 33358c2ecf20Sopenharmony_ci } 33368c2ecf20Sopenharmony_ci 33378c2ecf20Sopenharmony_ci geo->heads = 224; 33388c2ecf20Sopenharmony_ci geo->sectors = 56; 33398c2ecf20Sopenharmony_ci sector_div(capacity, (geo->heads * geo->sectors)); 33408c2ecf20Sopenharmony_ci geo->cylinders = capacity; 33418c2ecf20Sopenharmony_ci return 0; 33428c2ecf20Sopenharmony_ci} 33438c2ecf20Sopenharmony_ci 33448c2ecf20Sopenharmony_cistatic int mtip_block_open(struct block_device *dev, fmode_t mode) 33458c2ecf20Sopenharmony_ci{ 33468c2ecf20Sopenharmony_ci struct driver_data *dd; 33478c2ecf20Sopenharmony_ci 33488c2ecf20Sopenharmony_ci if (dev && dev->bd_disk) { 33498c2ecf20Sopenharmony_ci dd = (struct driver_data *) dev->bd_disk->private_data; 33508c2ecf20Sopenharmony_ci 33518c2ecf20Sopenharmony_ci if (dd) { 33528c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVAL_BIT, 33538c2ecf20Sopenharmony_ci &dd->dd_flag)) { 33548c2ecf20Sopenharmony_ci return -ENODEV; 33558c2ecf20Sopenharmony_ci } 33568c2ecf20Sopenharmony_ci return 0; 33578c2ecf20Sopenharmony_ci } 33588c2ecf20Sopenharmony_ci } 33598c2ecf20Sopenharmony_ci return -ENODEV; 33608c2ecf20Sopenharmony_ci} 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_cistatic void mtip_block_release(struct gendisk *disk, fmode_t mode) 33638c2ecf20Sopenharmony_ci{ 33648c2ecf20Sopenharmony_ci} 33658c2ecf20Sopenharmony_ci 33668c2ecf20Sopenharmony_ci/* 33678c2ecf20Sopenharmony_ci * Block device operation function. 33688c2ecf20Sopenharmony_ci * 33698c2ecf20Sopenharmony_ci * This structure contains pointers to the functions required by the block 33708c2ecf20Sopenharmony_ci * layer. 33718c2ecf20Sopenharmony_ci */ 33728c2ecf20Sopenharmony_cistatic const struct block_device_operations mtip_block_ops = { 33738c2ecf20Sopenharmony_ci .open = mtip_block_open, 33748c2ecf20Sopenharmony_ci .release = mtip_block_release, 33758c2ecf20Sopenharmony_ci .ioctl = mtip_block_ioctl, 33768c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 33778c2ecf20Sopenharmony_ci .compat_ioctl = mtip_block_compat_ioctl, 33788c2ecf20Sopenharmony_ci#endif 33798c2ecf20Sopenharmony_ci .getgeo = mtip_block_getgeo, 33808c2ecf20Sopenharmony_ci .owner = THIS_MODULE 33818c2ecf20Sopenharmony_ci}; 33828c2ecf20Sopenharmony_ci 33838c2ecf20Sopenharmony_cistatic inline bool is_se_active(struct driver_data *dd) 33848c2ecf20Sopenharmony_ci{ 33858c2ecf20Sopenharmony_ci if (unlikely(test_bit(MTIP_PF_SE_ACTIVE_BIT, &dd->port->flags))) { 33868c2ecf20Sopenharmony_ci if (dd->port->ic_pause_timer) { 33878c2ecf20Sopenharmony_ci unsigned long to = dd->port->ic_pause_timer + 33888c2ecf20Sopenharmony_ci msecs_to_jiffies(1000); 33898c2ecf20Sopenharmony_ci if (time_after(jiffies, to)) { 33908c2ecf20Sopenharmony_ci clear_bit(MTIP_PF_SE_ACTIVE_BIT, 33918c2ecf20Sopenharmony_ci &dd->port->flags); 33928c2ecf20Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag); 33938c2ecf20Sopenharmony_ci dd->port->ic_pause_timer = 0; 33948c2ecf20Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 33958c2ecf20Sopenharmony_ci return false; 33968c2ecf20Sopenharmony_ci } 33978c2ecf20Sopenharmony_ci } 33988c2ecf20Sopenharmony_ci return true; 33998c2ecf20Sopenharmony_ci } 34008c2ecf20Sopenharmony_ci return false; 34018c2ecf20Sopenharmony_ci} 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_cistatic inline bool is_stopped(struct driver_data *dd, struct request *rq) 34048c2ecf20Sopenharmony_ci{ 34058c2ecf20Sopenharmony_ci if (likely(!(dd->dd_flag & MTIP_DDF_STOP_IO))) 34068c2ecf20Sopenharmony_ci return false; 34078c2ecf20Sopenharmony_ci 34088c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) 34098c2ecf20Sopenharmony_ci return true; 34108c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag)) 34118c2ecf20Sopenharmony_ci return true; 34128c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag) && 34138c2ecf20Sopenharmony_ci rq_data_dir(rq)) 34148c2ecf20Sopenharmony_ci return true; 34158c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) 34168c2ecf20Sopenharmony_ci return true; 34178c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag)) 34188c2ecf20Sopenharmony_ci return true; 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci return false; 34218c2ecf20Sopenharmony_ci} 34228c2ecf20Sopenharmony_ci 34238c2ecf20Sopenharmony_cistatic bool mtip_check_unal_depth(struct blk_mq_hw_ctx *hctx, 34248c2ecf20Sopenharmony_ci struct request *rq) 34258c2ecf20Sopenharmony_ci{ 34268c2ecf20Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 34278c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 34288c2ecf20Sopenharmony_ci 34298c2ecf20Sopenharmony_ci if (rq_data_dir(rq) == READ || !dd->unal_qdepth) 34308c2ecf20Sopenharmony_ci return false; 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci /* 34338c2ecf20Sopenharmony_ci * If unaligned depth must be limited on this controller, mark it 34348c2ecf20Sopenharmony_ci * as unaligned if the IO isn't on a 4k boundary (start of length). 34358c2ecf20Sopenharmony_ci */ 34368c2ecf20Sopenharmony_ci if (blk_rq_sectors(rq) <= 64) { 34378c2ecf20Sopenharmony_ci if ((blk_rq_pos(rq) & 7) || (blk_rq_sectors(rq) & 7)) 34388c2ecf20Sopenharmony_ci cmd->unaligned = 1; 34398c2ecf20Sopenharmony_ci } 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci if (cmd->unaligned && atomic_dec_if_positive(&dd->port->cmd_slot_unal) >= 0) 34428c2ecf20Sopenharmony_ci return true; 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci return false; 34458c2ecf20Sopenharmony_ci} 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_cistatic blk_status_t mtip_issue_reserved_cmd(struct blk_mq_hw_ctx *hctx, 34488c2ecf20Sopenharmony_ci struct request *rq) 34498c2ecf20Sopenharmony_ci{ 34508c2ecf20Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 34518c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 34528c2ecf20Sopenharmony_ci struct mtip_int_cmd *icmd = cmd->icmd; 34538c2ecf20Sopenharmony_ci struct mtip_cmd_hdr *hdr = 34548c2ecf20Sopenharmony_ci dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag; 34558c2ecf20Sopenharmony_ci struct mtip_cmd_sg *command_sg; 34568c2ecf20Sopenharmony_ci 34578c2ecf20Sopenharmony_ci if (mtip_commands_active(dd->port)) 34588c2ecf20Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_ci hdr->ctba = cpu_to_le32(cmd->command_dma & 0xFFFFFFFF); 34618c2ecf20Sopenharmony_ci if (test_bit(MTIP_PF_HOST_CAP_64, &dd->port->flags)) 34628c2ecf20Sopenharmony_ci hdr->ctbau = cpu_to_le32((cmd->command_dma >> 16) >> 16); 34638c2ecf20Sopenharmony_ci /* Populate the SG list */ 34648c2ecf20Sopenharmony_ci hdr->opts = cpu_to_le32(icmd->opts | icmd->fis_len); 34658c2ecf20Sopenharmony_ci if (icmd->buf_len) { 34668c2ecf20Sopenharmony_ci command_sg = cmd->command + AHCI_CMD_TBL_HDR_SZ; 34678c2ecf20Sopenharmony_ci 34688c2ecf20Sopenharmony_ci command_sg->info = cpu_to_le32((icmd->buf_len-1) & 0x3FFFFF); 34698c2ecf20Sopenharmony_ci command_sg->dba = cpu_to_le32(icmd->buffer & 0xFFFFFFFF); 34708c2ecf20Sopenharmony_ci command_sg->dba_upper = 34718c2ecf20Sopenharmony_ci cpu_to_le32((icmd->buffer >> 16) >> 16); 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci hdr->opts |= cpu_to_le32((1 << 16)); 34748c2ecf20Sopenharmony_ci } 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_ci /* Populate the command header */ 34778c2ecf20Sopenharmony_ci hdr->byte_count = 0; 34788c2ecf20Sopenharmony_ci 34798c2ecf20Sopenharmony_ci blk_mq_start_request(rq); 34808c2ecf20Sopenharmony_ci mtip_issue_non_ncq_command(dd->port, rq->tag); 34818c2ecf20Sopenharmony_ci return 0; 34828c2ecf20Sopenharmony_ci} 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_cistatic blk_status_t mtip_queue_rq(struct blk_mq_hw_ctx *hctx, 34858c2ecf20Sopenharmony_ci const struct blk_mq_queue_data *bd) 34868c2ecf20Sopenharmony_ci{ 34878c2ecf20Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 34888c2ecf20Sopenharmony_ci struct request *rq = bd->rq; 34898c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 34908c2ecf20Sopenharmony_ci 34918c2ecf20Sopenharmony_ci if (blk_rq_is_passthrough(rq)) 34928c2ecf20Sopenharmony_ci return mtip_issue_reserved_cmd(hctx, rq); 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci if (unlikely(mtip_check_unal_depth(hctx, rq))) 34958c2ecf20Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 34968c2ecf20Sopenharmony_ci 34978c2ecf20Sopenharmony_ci if (is_se_active(dd) || is_stopped(dd, rq)) 34988c2ecf20Sopenharmony_ci return BLK_STS_IOERR; 34998c2ecf20Sopenharmony_ci 35008c2ecf20Sopenharmony_ci blk_mq_start_request(rq); 35018c2ecf20Sopenharmony_ci 35028c2ecf20Sopenharmony_ci mtip_hw_submit_io(dd, rq, cmd, hctx); 35038c2ecf20Sopenharmony_ci return BLK_STS_OK; 35048c2ecf20Sopenharmony_ci} 35058c2ecf20Sopenharmony_ci 35068c2ecf20Sopenharmony_cistatic void mtip_free_cmd(struct blk_mq_tag_set *set, struct request *rq, 35078c2ecf20Sopenharmony_ci unsigned int hctx_idx) 35088c2ecf20Sopenharmony_ci{ 35098c2ecf20Sopenharmony_ci struct driver_data *dd = set->driver_data; 35108c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 35118c2ecf20Sopenharmony_ci 35128c2ecf20Sopenharmony_ci if (!cmd->command) 35138c2ecf20Sopenharmony_ci return; 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, cmd->command, 35168c2ecf20Sopenharmony_ci cmd->command_dma); 35178c2ecf20Sopenharmony_ci} 35188c2ecf20Sopenharmony_ci 35198c2ecf20Sopenharmony_cistatic int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq, 35208c2ecf20Sopenharmony_ci unsigned int hctx_idx, unsigned int numa_node) 35218c2ecf20Sopenharmony_ci{ 35228c2ecf20Sopenharmony_ci struct driver_data *dd = set->driver_data; 35238c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci cmd->command = dma_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, 35268c2ecf20Sopenharmony_ci &cmd->command_dma, GFP_KERNEL); 35278c2ecf20Sopenharmony_ci if (!cmd->command) 35288c2ecf20Sopenharmony_ci return -ENOMEM; 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci sg_init_table(cmd->sg, MTIP_MAX_SG); 35318c2ecf20Sopenharmony_ci return 0; 35328c2ecf20Sopenharmony_ci} 35338c2ecf20Sopenharmony_ci 35348c2ecf20Sopenharmony_cistatic enum blk_eh_timer_return mtip_cmd_timeout(struct request *req, 35358c2ecf20Sopenharmony_ci bool reserved) 35368c2ecf20Sopenharmony_ci{ 35378c2ecf20Sopenharmony_ci struct driver_data *dd = req->q->queuedata; 35388c2ecf20Sopenharmony_ci 35398c2ecf20Sopenharmony_ci if (reserved) { 35408c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(req); 35418c2ecf20Sopenharmony_ci 35428c2ecf20Sopenharmony_ci cmd->status = BLK_STS_TIMEOUT; 35438c2ecf20Sopenharmony_ci blk_mq_complete_request(req); 35448c2ecf20Sopenharmony_ci return BLK_EH_DONE; 35458c2ecf20Sopenharmony_ci } 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci if (test_bit(req->tag, dd->port->cmds_to_issue)) 35488c2ecf20Sopenharmony_ci goto exit_handler; 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci if (test_and_set_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags)) 35518c2ecf20Sopenharmony_ci goto exit_handler; 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 35548c2ecf20Sopenharmony_ciexit_handler: 35558c2ecf20Sopenharmony_ci return BLK_EH_RESET_TIMER; 35568c2ecf20Sopenharmony_ci} 35578c2ecf20Sopenharmony_ci 35588c2ecf20Sopenharmony_cistatic const struct blk_mq_ops mtip_mq_ops = { 35598c2ecf20Sopenharmony_ci .queue_rq = mtip_queue_rq, 35608c2ecf20Sopenharmony_ci .init_request = mtip_init_cmd, 35618c2ecf20Sopenharmony_ci .exit_request = mtip_free_cmd, 35628c2ecf20Sopenharmony_ci .complete = mtip_softirq_done_fn, 35638c2ecf20Sopenharmony_ci .timeout = mtip_cmd_timeout, 35648c2ecf20Sopenharmony_ci}; 35658c2ecf20Sopenharmony_ci 35668c2ecf20Sopenharmony_ci/* 35678c2ecf20Sopenharmony_ci * Block layer initialization function. 35688c2ecf20Sopenharmony_ci * 35698c2ecf20Sopenharmony_ci * This function is called once by the PCI layer for each P320 35708c2ecf20Sopenharmony_ci * device that is connected to the system. 35718c2ecf20Sopenharmony_ci * 35728c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 35738c2ecf20Sopenharmony_ci * 35748c2ecf20Sopenharmony_ci * return value 35758c2ecf20Sopenharmony_ci * 0 on success else an error code. 35768c2ecf20Sopenharmony_ci */ 35778c2ecf20Sopenharmony_cistatic int mtip_block_initialize(struct driver_data *dd) 35788c2ecf20Sopenharmony_ci{ 35798c2ecf20Sopenharmony_ci int rv = 0, wait_for_rebuild = 0; 35808c2ecf20Sopenharmony_ci sector_t capacity; 35818c2ecf20Sopenharmony_ci unsigned int index = 0; 35828c2ecf20Sopenharmony_ci struct kobject *kobj; 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci if (dd->disk) 35858c2ecf20Sopenharmony_ci goto skip_create_disk; /* hw init done, before rebuild */ 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci if (mtip_hw_init(dd)) { 35888c2ecf20Sopenharmony_ci rv = -EINVAL; 35898c2ecf20Sopenharmony_ci goto protocol_init_error; 35908c2ecf20Sopenharmony_ci } 35918c2ecf20Sopenharmony_ci 35928c2ecf20Sopenharmony_ci dd->disk = alloc_disk_node(MTIP_MAX_MINORS, dd->numa_node); 35938c2ecf20Sopenharmony_ci if (dd->disk == NULL) { 35948c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 35958c2ecf20Sopenharmony_ci "Unable to allocate gendisk structure\n"); 35968c2ecf20Sopenharmony_ci rv = -EINVAL; 35978c2ecf20Sopenharmony_ci goto alloc_disk_error; 35988c2ecf20Sopenharmony_ci } 35998c2ecf20Sopenharmony_ci 36008c2ecf20Sopenharmony_ci rv = ida_alloc(&rssd_index_ida, GFP_KERNEL); 36018c2ecf20Sopenharmony_ci if (rv < 0) 36028c2ecf20Sopenharmony_ci goto ida_get_error; 36038c2ecf20Sopenharmony_ci index = rv; 36048c2ecf20Sopenharmony_ci 36058c2ecf20Sopenharmony_ci rv = rssd_disk_name_format("rssd", 36068c2ecf20Sopenharmony_ci index, 36078c2ecf20Sopenharmony_ci dd->disk->disk_name, 36088c2ecf20Sopenharmony_ci DISK_NAME_LEN); 36098c2ecf20Sopenharmony_ci if (rv) 36108c2ecf20Sopenharmony_ci goto disk_index_error; 36118c2ecf20Sopenharmony_ci 36128c2ecf20Sopenharmony_ci dd->disk->major = dd->major; 36138c2ecf20Sopenharmony_ci dd->disk->first_minor = index * MTIP_MAX_MINORS; 36148c2ecf20Sopenharmony_ci dd->disk->minors = MTIP_MAX_MINORS; 36158c2ecf20Sopenharmony_ci dd->disk->fops = &mtip_block_ops; 36168c2ecf20Sopenharmony_ci dd->disk->private_data = dd; 36178c2ecf20Sopenharmony_ci dd->index = index; 36188c2ecf20Sopenharmony_ci 36198c2ecf20Sopenharmony_ci mtip_hw_debugfs_init(dd); 36208c2ecf20Sopenharmony_ci 36218c2ecf20Sopenharmony_ci memset(&dd->tags, 0, sizeof(dd->tags)); 36228c2ecf20Sopenharmony_ci dd->tags.ops = &mtip_mq_ops; 36238c2ecf20Sopenharmony_ci dd->tags.nr_hw_queues = 1; 36248c2ecf20Sopenharmony_ci dd->tags.queue_depth = MTIP_MAX_COMMAND_SLOTS; 36258c2ecf20Sopenharmony_ci dd->tags.reserved_tags = 1; 36268c2ecf20Sopenharmony_ci dd->tags.cmd_size = sizeof(struct mtip_cmd); 36278c2ecf20Sopenharmony_ci dd->tags.numa_node = dd->numa_node; 36288c2ecf20Sopenharmony_ci dd->tags.flags = BLK_MQ_F_SHOULD_MERGE; 36298c2ecf20Sopenharmony_ci dd->tags.driver_data = dd; 36308c2ecf20Sopenharmony_ci dd->tags.timeout = MTIP_NCQ_CMD_TIMEOUT_MS; 36318c2ecf20Sopenharmony_ci 36328c2ecf20Sopenharmony_ci rv = blk_mq_alloc_tag_set(&dd->tags); 36338c2ecf20Sopenharmony_ci if (rv) { 36348c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 36358c2ecf20Sopenharmony_ci "Unable to allocate request queue\n"); 36368c2ecf20Sopenharmony_ci goto block_queue_alloc_tag_error; 36378c2ecf20Sopenharmony_ci } 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci /* Allocate the request queue. */ 36408c2ecf20Sopenharmony_ci dd->queue = blk_mq_init_queue(&dd->tags); 36418c2ecf20Sopenharmony_ci if (IS_ERR(dd->queue)) { 36428c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 36438c2ecf20Sopenharmony_ci "Unable to allocate request queue\n"); 36448c2ecf20Sopenharmony_ci rv = -ENOMEM; 36458c2ecf20Sopenharmony_ci goto block_queue_alloc_init_error; 36468c2ecf20Sopenharmony_ci } 36478c2ecf20Sopenharmony_ci 36488c2ecf20Sopenharmony_ci dd->disk->queue = dd->queue; 36498c2ecf20Sopenharmony_ci dd->queue->queuedata = dd; 36508c2ecf20Sopenharmony_ci 36518c2ecf20Sopenharmony_ciskip_create_disk: 36528c2ecf20Sopenharmony_ci /* Initialize the protocol layer. */ 36538c2ecf20Sopenharmony_ci wait_for_rebuild = mtip_hw_get_identify(dd); 36548c2ecf20Sopenharmony_ci if (wait_for_rebuild < 0) { 36558c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, 36568c2ecf20Sopenharmony_ci "Protocol layer initialization failed\n"); 36578c2ecf20Sopenharmony_ci rv = -EINVAL; 36588c2ecf20Sopenharmony_ci goto init_hw_cmds_error; 36598c2ecf20Sopenharmony_ci } 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci /* 36628c2ecf20Sopenharmony_ci * if rebuild pending, start the service thread, and delay the block 36638c2ecf20Sopenharmony_ci * queue creation and device_add_disk() 36648c2ecf20Sopenharmony_ci */ 36658c2ecf20Sopenharmony_ci if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) 36668c2ecf20Sopenharmony_ci goto start_service_thread; 36678c2ecf20Sopenharmony_ci 36688c2ecf20Sopenharmony_ci /* Set device limits. */ 36698c2ecf20Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, dd->queue); 36708c2ecf20Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, dd->queue); 36718c2ecf20Sopenharmony_ci blk_queue_max_segments(dd->queue, MTIP_MAX_SG); 36728c2ecf20Sopenharmony_ci blk_queue_physical_block_size(dd->queue, 4096); 36738c2ecf20Sopenharmony_ci blk_queue_max_hw_sectors(dd->queue, 0xffff); 36748c2ecf20Sopenharmony_ci blk_queue_max_segment_size(dd->queue, 0x400000); 36758c2ecf20Sopenharmony_ci dma_set_max_seg_size(&dd->pdev->dev, 0x400000); 36768c2ecf20Sopenharmony_ci blk_queue_io_min(dd->queue, 4096); 36778c2ecf20Sopenharmony_ci 36788c2ecf20Sopenharmony_ci /* Set the capacity of the device in 512 byte sectors. */ 36798c2ecf20Sopenharmony_ci if (!(mtip_hw_get_capacity(dd, &capacity))) { 36808c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 36818c2ecf20Sopenharmony_ci "Could not read drive capacity\n"); 36828c2ecf20Sopenharmony_ci rv = -EIO; 36838c2ecf20Sopenharmony_ci goto read_capacity_error; 36848c2ecf20Sopenharmony_ci } 36858c2ecf20Sopenharmony_ci set_capacity(dd->disk, capacity); 36868c2ecf20Sopenharmony_ci 36878c2ecf20Sopenharmony_ci /* Enable the block device and add it to /dev */ 36888c2ecf20Sopenharmony_ci device_add_disk(&dd->pdev->dev, dd->disk, NULL); 36898c2ecf20Sopenharmony_ci 36908c2ecf20Sopenharmony_ci dd->bdev = bdget_disk(dd->disk, 0); 36918c2ecf20Sopenharmony_ci /* 36928c2ecf20Sopenharmony_ci * Now that the disk is active, initialize any sysfs attributes 36938c2ecf20Sopenharmony_ci * managed by the protocol layer. 36948c2ecf20Sopenharmony_ci */ 36958c2ecf20Sopenharmony_ci kobj = kobject_get(&disk_to_dev(dd->disk)->kobj); 36968c2ecf20Sopenharmony_ci if (kobj) { 36978c2ecf20Sopenharmony_ci mtip_hw_sysfs_init(dd, kobj); 36988c2ecf20Sopenharmony_ci kobject_put(kobj); 36998c2ecf20Sopenharmony_ci } 37008c2ecf20Sopenharmony_ci 37018c2ecf20Sopenharmony_ci if (dd->mtip_svc_handler) { 37028c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); 37038c2ecf20Sopenharmony_ci return rv; /* service thread created for handling rebuild */ 37048c2ecf20Sopenharmony_ci } 37058c2ecf20Sopenharmony_ci 37068c2ecf20Sopenharmony_cistart_service_thread: 37078c2ecf20Sopenharmony_ci dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread, 37088c2ecf20Sopenharmony_ci dd, dd->numa_node, 37098c2ecf20Sopenharmony_ci "mtip_svc_thd_%02d", index); 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci if (IS_ERR(dd->mtip_svc_handler)) { 37128c2ecf20Sopenharmony_ci dev_err(&dd->pdev->dev, "service thread failed to start\n"); 37138c2ecf20Sopenharmony_ci dd->mtip_svc_handler = NULL; 37148c2ecf20Sopenharmony_ci rv = -EFAULT; 37158c2ecf20Sopenharmony_ci goto kthread_run_error; 37168c2ecf20Sopenharmony_ci } 37178c2ecf20Sopenharmony_ci wake_up_process(dd->mtip_svc_handler); 37188c2ecf20Sopenharmony_ci if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) 37198c2ecf20Sopenharmony_ci rv = wait_for_rebuild; 37208c2ecf20Sopenharmony_ci 37218c2ecf20Sopenharmony_ci return rv; 37228c2ecf20Sopenharmony_ci 37238c2ecf20Sopenharmony_cikthread_run_error: 37248c2ecf20Sopenharmony_ci bdput(dd->bdev); 37258c2ecf20Sopenharmony_ci dd->bdev = NULL; 37268c2ecf20Sopenharmony_ci 37278c2ecf20Sopenharmony_ci /* Delete our gendisk. This also removes the device from /dev */ 37288c2ecf20Sopenharmony_ci del_gendisk(dd->disk); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ciread_capacity_error: 37318c2ecf20Sopenharmony_ciinit_hw_cmds_error: 37328c2ecf20Sopenharmony_ci blk_cleanup_queue(dd->queue); 37338c2ecf20Sopenharmony_ciblock_queue_alloc_init_error: 37348c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 37358c2ecf20Sopenharmony_ciblock_queue_alloc_tag_error: 37368c2ecf20Sopenharmony_ci mtip_hw_debugfs_exit(dd); 37378c2ecf20Sopenharmony_cidisk_index_error: 37388c2ecf20Sopenharmony_ci ida_free(&rssd_index_ida, index); 37398c2ecf20Sopenharmony_ci 37408c2ecf20Sopenharmony_ciida_get_error: 37418c2ecf20Sopenharmony_ci put_disk(dd->disk); 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_cialloc_disk_error: 37448c2ecf20Sopenharmony_ci mtip_hw_exit(dd); /* De-initialize the protocol layer. */ 37458c2ecf20Sopenharmony_ci 37468c2ecf20Sopenharmony_ciprotocol_init_error: 37478c2ecf20Sopenharmony_ci return rv; 37488c2ecf20Sopenharmony_ci} 37498c2ecf20Sopenharmony_ci 37508c2ecf20Sopenharmony_cistatic bool mtip_no_dev_cleanup(struct request *rq, void *data, bool reserv) 37518c2ecf20Sopenharmony_ci{ 37528c2ecf20Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ci cmd->status = BLK_STS_IOERR; 37558c2ecf20Sopenharmony_ci blk_mq_complete_request(rq); 37568c2ecf20Sopenharmony_ci return true; 37578c2ecf20Sopenharmony_ci} 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci/* 37608c2ecf20Sopenharmony_ci * Block layer deinitialization function. 37618c2ecf20Sopenharmony_ci * 37628c2ecf20Sopenharmony_ci * Called by the PCI layer as each P320 device is removed. 37638c2ecf20Sopenharmony_ci * 37648c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 37658c2ecf20Sopenharmony_ci * 37668c2ecf20Sopenharmony_ci * return value 37678c2ecf20Sopenharmony_ci * 0 37688c2ecf20Sopenharmony_ci */ 37698c2ecf20Sopenharmony_cistatic int mtip_block_remove(struct driver_data *dd) 37708c2ecf20Sopenharmony_ci{ 37718c2ecf20Sopenharmony_ci struct kobject *kobj; 37728c2ecf20Sopenharmony_ci 37738c2ecf20Sopenharmony_ci mtip_hw_debugfs_exit(dd); 37748c2ecf20Sopenharmony_ci 37758c2ecf20Sopenharmony_ci if (dd->mtip_svc_handler) { 37768c2ecf20Sopenharmony_ci set_bit(MTIP_PF_SVC_THD_STOP_BIT, &dd->port->flags); 37778c2ecf20Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 37788c2ecf20Sopenharmony_ci kthread_stop(dd->mtip_svc_handler); 37798c2ecf20Sopenharmony_ci } 37808c2ecf20Sopenharmony_ci 37818c2ecf20Sopenharmony_ci /* Clean up the sysfs attributes, if created */ 37828c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) { 37838c2ecf20Sopenharmony_ci kobj = kobject_get(&disk_to_dev(dd->disk)->kobj); 37848c2ecf20Sopenharmony_ci if (kobj) { 37858c2ecf20Sopenharmony_ci mtip_hw_sysfs_exit(dd, kobj); 37868c2ecf20Sopenharmony_ci kobject_put(kobj); 37878c2ecf20Sopenharmony_ci } 37888c2ecf20Sopenharmony_ci } 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci if (!dd->sr) { 37918c2ecf20Sopenharmony_ci /* 37928c2ecf20Sopenharmony_ci * Explicitly wait here for IOs to quiesce, 37938c2ecf20Sopenharmony_ci * as mtip_standby_drive usually won't wait for IOs. 37948c2ecf20Sopenharmony_ci */ 37958c2ecf20Sopenharmony_ci if (!mtip_quiesce_io(dd->port, MTIP_QUIESCE_IO_TIMEOUT_MS)) 37968c2ecf20Sopenharmony_ci mtip_standby_drive(dd); 37978c2ecf20Sopenharmony_ci } 37988c2ecf20Sopenharmony_ci else 37998c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, "device %s surprise removal\n", 38008c2ecf20Sopenharmony_ci dd->disk->disk_name); 38018c2ecf20Sopenharmony_ci 38028c2ecf20Sopenharmony_ci blk_freeze_queue_start(dd->queue); 38038c2ecf20Sopenharmony_ci blk_mq_quiesce_queue(dd->queue); 38048c2ecf20Sopenharmony_ci blk_mq_tagset_busy_iter(&dd->tags, mtip_no_dev_cleanup, dd); 38058c2ecf20Sopenharmony_ci blk_mq_unquiesce_queue(dd->queue); 38068c2ecf20Sopenharmony_ci 38078c2ecf20Sopenharmony_ci /* 38088c2ecf20Sopenharmony_ci * Delete our gendisk structure. This also removes the device 38098c2ecf20Sopenharmony_ci * from /dev 38108c2ecf20Sopenharmony_ci */ 38118c2ecf20Sopenharmony_ci if (dd->bdev) { 38128c2ecf20Sopenharmony_ci bdput(dd->bdev); 38138c2ecf20Sopenharmony_ci dd->bdev = NULL; 38148c2ecf20Sopenharmony_ci } 38158c2ecf20Sopenharmony_ci if (dd->disk) { 38168c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) 38178c2ecf20Sopenharmony_ci del_gendisk(dd->disk); 38188c2ecf20Sopenharmony_ci if (dd->disk->queue) { 38198c2ecf20Sopenharmony_ci blk_cleanup_queue(dd->queue); 38208c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 38218c2ecf20Sopenharmony_ci dd->queue = NULL; 38228c2ecf20Sopenharmony_ci } 38238c2ecf20Sopenharmony_ci put_disk(dd->disk); 38248c2ecf20Sopenharmony_ci } 38258c2ecf20Sopenharmony_ci dd->disk = NULL; 38268c2ecf20Sopenharmony_ci 38278c2ecf20Sopenharmony_ci ida_free(&rssd_index_ida, dd->index); 38288c2ecf20Sopenharmony_ci 38298c2ecf20Sopenharmony_ci /* De-initialize the protocol layer. */ 38308c2ecf20Sopenharmony_ci mtip_hw_exit(dd); 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci return 0; 38338c2ecf20Sopenharmony_ci} 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci/* 38368c2ecf20Sopenharmony_ci * Function called by the PCI layer when just before the 38378c2ecf20Sopenharmony_ci * machine shuts down. 38388c2ecf20Sopenharmony_ci * 38398c2ecf20Sopenharmony_ci * If a protocol layer shutdown function is present it will be called 38408c2ecf20Sopenharmony_ci * by this function. 38418c2ecf20Sopenharmony_ci * 38428c2ecf20Sopenharmony_ci * @dd Pointer to the driver data structure. 38438c2ecf20Sopenharmony_ci * 38448c2ecf20Sopenharmony_ci * return value 38458c2ecf20Sopenharmony_ci * 0 38468c2ecf20Sopenharmony_ci */ 38478c2ecf20Sopenharmony_cistatic int mtip_block_shutdown(struct driver_data *dd) 38488c2ecf20Sopenharmony_ci{ 38498c2ecf20Sopenharmony_ci mtip_hw_shutdown(dd); 38508c2ecf20Sopenharmony_ci 38518c2ecf20Sopenharmony_ci /* Delete our gendisk structure, and cleanup the blk queue. */ 38528c2ecf20Sopenharmony_ci if (dd->disk) { 38538c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 38548c2ecf20Sopenharmony_ci "Shutting down %s ...\n", dd->disk->disk_name); 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) 38578c2ecf20Sopenharmony_ci del_gendisk(dd->disk); 38588c2ecf20Sopenharmony_ci if (dd->disk->queue) { 38598c2ecf20Sopenharmony_ci blk_cleanup_queue(dd->queue); 38608c2ecf20Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 38618c2ecf20Sopenharmony_ci } 38628c2ecf20Sopenharmony_ci put_disk(dd->disk); 38638c2ecf20Sopenharmony_ci dd->disk = NULL; 38648c2ecf20Sopenharmony_ci dd->queue = NULL; 38658c2ecf20Sopenharmony_ci } 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci ida_free(&rssd_index_ida, dd->index); 38688c2ecf20Sopenharmony_ci return 0; 38698c2ecf20Sopenharmony_ci} 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_cistatic int mtip_block_suspend(struct driver_data *dd) 38728c2ecf20Sopenharmony_ci{ 38738c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 38748c2ecf20Sopenharmony_ci "Suspending %s ...\n", dd->disk->disk_name); 38758c2ecf20Sopenharmony_ci mtip_hw_suspend(dd); 38768c2ecf20Sopenharmony_ci return 0; 38778c2ecf20Sopenharmony_ci} 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_cistatic int mtip_block_resume(struct driver_data *dd) 38808c2ecf20Sopenharmony_ci{ 38818c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, "Resuming %s ...\n", 38828c2ecf20Sopenharmony_ci dd->disk->disk_name); 38838c2ecf20Sopenharmony_ci mtip_hw_resume(dd); 38848c2ecf20Sopenharmony_ci return 0; 38858c2ecf20Sopenharmony_ci} 38868c2ecf20Sopenharmony_ci 38878c2ecf20Sopenharmony_cistatic void drop_cpu(int cpu) 38888c2ecf20Sopenharmony_ci{ 38898c2ecf20Sopenharmony_ci cpu_use[cpu]--; 38908c2ecf20Sopenharmony_ci} 38918c2ecf20Sopenharmony_ci 38928c2ecf20Sopenharmony_cistatic int get_least_used_cpu_on_node(int node) 38938c2ecf20Sopenharmony_ci{ 38948c2ecf20Sopenharmony_ci int cpu, least_used_cpu, least_cnt; 38958c2ecf20Sopenharmony_ci const struct cpumask *node_mask; 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci node_mask = cpumask_of_node(node); 38988c2ecf20Sopenharmony_ci least_used_cpu = cpumask_first(node_mask); 38998c2ecf20Sopenharmony_ci least_cnt = cpu_use[least_used_cpu]; 39008c2ecf20Sopenharmony_ci cpu = least_used_cpu; 39018c2ecf20Sopenharmony_ci 39028c2ecf20Sopenharmony_ci for_each_cpu(cpu, node_mask) { 39038c2ecf20Sopenharmony_ci if (cpu_use[cpu] < least_cnt) { 39048c2ecf20Sopenharmony_ci least_used_cpu = cpu; 39058c2ecf20Sopenharmony_ci least_cnt = cpu_use[cpu]; 39068c2ecf20Sopenharmony_ci } 39078c2ecf20Sopenharmony_ci } 39088c2ecf20Sopenharmony_ci cpu_use[least_used_cpu]++; 39098c2ecf20Sopenharmony_ci return least_used_cpu; 39108c2ecf20Sopenharmony_ci} 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci/* Helper for selecting a node in round robin mode */ 39138c2ecf20Sopenharmony_cistatic inline int mtip_get_next_rr_node(void) 39148c2ecf20Sopenharmony_ci{ 39158c2ecf20Sopenharmony_ci static int next_node = NUMA_NO_NODE; 39168c2ecf20Sopenharmony_ci 39178c2ecf20Sopenharmony_ci if (next_node == NUMA_NO_NODE) { 39188c2ecf20Sopenharmony_ci next_node = first_online_node; 39198c2ecf20Sopenharmony_ci return next_node; 39208c2ecf20Sopenharmony_ci } 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci next_node = next_online_node(next_node); 39238c2ecf20Sopenharmony_ci if (next_node == MAX_NUMNODES) 39248c2ecf20Sopenharmony_ci next_node = first_online_node; 39258c2ecf20Sopenharmony_ci return next_node; 39268c2ecf20Sopenharmony_ci} 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(0); 39298c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(1); 39308c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(2); 39318c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(3); 39328c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(4); 39338c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(5); 39348c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(6); 39358c2ecf20Sopenharmony_cistatic DEFINE_HANDLER(7); 39368c2ecf20Sopenharmony_ci 39378c2ecf20Sopenharmony_cistatic void mtip_disable_link_opts(struct driver_data *dd, struct pci_dev *pdev) 39388c2ecf20Sopenharmony_ci{ 39398c2ecf20Sopenharmony_ci int pos; 39408c2ecf20Sopenharmony_ci unsigned short pcie_dev_ctrl; 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_ci pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); 39438c2ecf20Sopenharmony_ci if (pos) { 39448c2ecf20Sopenharmony_ci pci_read_config_word(pdev, 39458c2ecf20Sopenharmony_ci pos + PCI_EXP_DEVCTL, 39468c2ecf20Sopenharmony_ci &pcie_dev_ctrl); 39478c2ecf20Sopenharmony_ci if (pcie_dev_ctrl & (1 << 11) || 39488c2ecf20Sopenharmony_ci pcie_dev_ctrl & (1 << 4)) { 39498c2ecf20Sopenharmony_ci dev_info(&dd->pdev->dev, 39508c2ecf20Sopenharmony_ci "Disabling ERO/No-Snoop on bridge device %04x:%04x\n", 39518c2ecf20Sopenharmony_ci pdev->vendor, pdev->device); 39528c2ecf20Sopenharmony_ci pcie_dev_ctrl &= ~(PCI_EXP_DEVCTL_NOSNOOP_EN | 39538c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL_RELAX_EN); 39548c2ecf20Sopenharmony_ci pci_write_config_word(pdev, 39558c2ecf20Sopenharmony_ci pos + PCI_EXP_DEVCTL, 39568c2ecf20Sopenharmony_ci pcie_dev_ctrl); 39578c2ecf20Sopenharmony_ci } 39588c2ecf20Sopenharmony_ci } 39598c2ecf20Sopenharmony_ci} 39608c2ecf20Sopenharmony_ci 39618c2ecf20Sopenharmony_cistatic void mtip_fix_ero_nosnoop(struct driver_data *dd, struct pci_dev *pdev) 39628c2ecf20Sopenharmony_ci{ 39638c2ecf20Sopenharmony_ci /* 39648c2ecf20Sopenharmony_ci * This workaround is specific to AMD/ATI chipset with a PCI upstream 39658c2ecf20Sopenharmony_ci * device with device id 0x5aXX 39668c2ecf20Sopenharmony_ci */ 39678c2ecf20Sopenharmony_ci if (pdev->bus && pdev->bus->self) { 39688c2ecf20Sopenharmony_ci if (pdev->bus->self->vendor == PCI_VENDOR_ID_ATI && 39698c2ecf20Sopenharmony_ci ((pdev->bus->self->device & 0xff00) == 0x5a00)) { 39708c2ecf20Sopenharmony_ci mtip_disable_link_opts(dd, pdev->bus->self); 39718c2ecf20Sopenharmony_ci } else { 39728c2ecf20Sopenharmony_ci /* Check further up the topology */ 39738c2ecf20Sopenharmony_ci struct pci_dev *parent_dev = pdev->bus->self; 39748c2ecf20Sopenharmony_ci if (parent_dev->bus && 39758c2ecf20Sopenharmony_ci parent_dev->bus->parent && 39768c2ecf20Sopenharmony_ci parent_dev->bus->parent->self && 39778c2ecf20Sopenharmony_ci parent_dev->bus->parent->self->vendor == 39788c2ecf20Sopenharmony_ci PCI_VENDOR_ID_ATI && 39798c2ecf20Sopenharmony_ci (parent_dev->bus->parent->self->device & 39808c2ecf20Sopenharmony_ci 0xff00) == 0x5a00) { 39818c2ecf20Sopenharmony_ci mtip_disable_link_opts(dd, 39828c2ecf20Sopenharmony_ci parent_dev->bus->parent->self); 39838c2ecf20Sopenharmony_ci } 39848c2ecf20Sopenharmony_ci } 39858c2ecf20Sopenharmony_ci } 39868c2ecf20Sopenharmony_ci} 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci/* 39898c2ecf20Sopenharmony_ci * Called for each supported PCI device detected. 39908c2ecf20Sopenharmony_ci * 39918c2ecf20Sopenharmony_ci * This function allocates the private data structure, enables the 39928c2ecf20Sopenharmony_ci * PCI device and then calls the block layer initialization function. 39938c2ecf20Sopenharmony_ci * 39948c2ecf20Sopenharmony_ci * return value 39958c2ecf20Sopenharmony_ci * 0 on success else an error code. 39968c2ecf20Sopenharmony_ci */ 39978c2ecf20Sopenharmony_cistatic int mtip_pci_probe(struct pci_dev *pdev, 39988c2ecf20Sopenharmony_ci const struct pci_device_id *ent) 39998c2ecf20Sopenharmony_ci{ 40008c2ecf20Sopenharmony_ci int rv = 0; 40018c2ecf20Sopenharmony_ci struct driver_data *dd = NULL; 40028c2ecf20Sopenharmony_ci char cpu_list[256]; 40038c2ecf20Sopenharmony_ci const struct cpumask *node_mask; 40048c2ecf20Sopenharmony_ci int cpu, i = 0, j = 0; 40058c2ecf20Sopenharmony_ci int my_node = NUMA_NO_NODE; 40068c2ecf20Sopenharmony_ci unsigned long flags; 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci /* Allocate memory for this devices private data. */ 40098c2ecf20Sopenharmony_ci my_node = pcibus_to_node(pdev->bus); 40108c2ecf20Sopenharmony_ci if (my_node != NUMA_NO_NODE) { 40118c2ecf20Sopenharmony_ci if (!node_online(my_node)) 40128c2ecf20Sopenharmony_ci my_node = mtip_get_next_rr_node(); 40138c2ecf20Sopenharmony_ci } else { 40148c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Kernel not reporting proximity, choosing a node\n"); 40158c2ecf20Sopenharmony_ci my_node = mtip_get_next_rr_node(); 40168c2ecf20Sopenharmony_ci } 40178c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "NUMA node %d (closest: %d,%d, probe on %d:%d)\n", 40188c2ecf20Sopenharmony_ci my_node, pcibus_to_node(pdev->bus), dev_to_node(&pdev->dev), 40198c2ecf20Sopenharmony_ci cpu_to_node(raw_smp_processor_id()), raw_smp_processor_id()); 40208c2ecf20Sopenharmony_ci 40218c2ecf20Sopenharmony_ci dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node); 40228c2ecf20Sopenharmony_ci if (dd == NULL) { 40238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 40248c2ecf20Sopenharmony_ci "Unable to allocate memory for driver data\n"); 40258c2ecf20Sopenharmony_ci return -ENOMEM; 40268c2ecf20Sopenharmony_ci } 40278c2ecf20Sopenharmony_ci 40288c2ecf20Sopenharmony_ci /* Attach the private data to this PCI device. */ 40298c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, dd); 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_ci rv = pcim_enable_device(pdev); 40328c2ecf20Sopenharmony_ci if (rv < 0) { 40338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable device\n"); 40348c2ecf20Sopenharmony_ci goto iomap_err; 40358c2ecf20Sopenharmony_ci } 40368c2ecf20Sopenharmony_ci 40378c2ecf20Sopenharmony_ci /* Map BAR5 to memory. */ 40388c2ecf20Sopenharmony_ci rv = pcim_iomap_regions(pdev, 1 << MTIP_ABAR, MTIP_DRV_NAME); 40398c2ecf20Sopenharmony_ci if (rv < 0) { 40408c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to map regions\n"); 40418c2ecf20Sopenharmony_ci goto iomap_err; 40428c2ecf20Sopenharmony_ci } 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_ci rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 40458c2ecf20Sopenharmony_ci if (rv) { 40468c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "64-bit DMA enable failed\n"); 40478c2ecf20Sopenharmony_ci goto setmask_err; 40488c2ecf20Sopenharmony_ci } 40498c2ecf20Sopenharmony_ci 40508c2ecf20Sopenharmony_ci /* Copy the info we may need later into the private data structure. */ 40518c2ecf20Sopenharmony_ci dd->major = mtip_major; 40528c2ecf20Sopenharmony_ci dd->instance = instance; 40538c2ecf20Sopenharmony_ci dd->pdev = pdev; 40548c2ecf20Sopenharmony_ci dd->numa_node = my_node; 40558c2ecf20Sopenharmony_ci 40568c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dd->online_list); 40578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&dd->remove_list); 40588c2ecf20Sopenharmony_ci 40598c2ecf20Sopenharmony_ci memset(dd->workq_name, 0, 32); 40608c2ecf20Sopenharmony_ci snprintf(dd->workq_name, 31, "mtipq%d", dd->instance); 40618c2ecf20Sopenharmony_ci 40628c2ecf20Sopenharmony_ci dd->isr_workq = create_workqueue(dd->workq_name); 40638c2ecf20Sopenharmony_ci if (!dd->isr_workq) { 40648c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance); 40658c2ecf20Sopenharmony_ci rv = -ENOMEM; 40668c2ecf20Sopenharmony_ci goto setmask_err; 40678c2ecf20Sopenharmony_ci } 40688c2ecf20Sopenharmony_ci 40698c2ecf20Sopenharmony_ci memset(cpu_list, 0, sizeof(cpu_list)); 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci node_mask = cpumask_of_node(dd->numa_node); 40728c2ecf20Sopenharmony_ci if (!cpumask_empty(node_mask)) { 40738c2ecf20Sopenharmony_ci for_each_cpu(cpu, node_mask) 40748c2ecf20Sopenharmony_ci { 40758c2ecf20Sopenharmony_ci snprintf(&cpu_list[j], 256 - j, "%d ", cpu); 40768c2ecf20Sopenharmony_ci j = strlen(cpu_list); 40778c2ecf20Sopenharmony_ci } 40788c2ecf20Sopenharmony_ci 40798c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Node %d on package %d has %d cpu(s): %s\n", 40808c2ecf20Sopenharmony_ci dd->numa_node, 40818c2ecf20Sopenharmony_ci topology_physical_package_id(cpumask_first(node_mask)), 40828c2ecf20Sopenharmony_ci nr_cpus_node(dd->numa_node), 40838c2ecf20Sopenharmony_ci cpu_list); 40848c2ecf20Sopenharmony_ci } else 40858c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "mtip32xx: node_mask empty\n"); 40868c2ecf20Sopenharmony_ci 40878c2ecf20Sopenharmony_ci dd->isr_binding = get_least_used_cpu_on_node(dd->numa_node); 40888c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Initial IRQ binding node:cpu %d:%d\n", 40898c2ecf20Sopenharmony_ci cpu_to_node(dd->isr_binding), dd->isr_binding); 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci /* first worker context always runs in ISR */ 40928c2ecf20Sopenharmony_ci dd->work[0].cpu_binding = dd->isr_binding; 40938c2ecf20Sopenharmony_ci dd->work[1].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); 40948c2ecf20Sopenharmony_ci dd->work[2].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); 40958c2ecf20Sopenharmony_ci dd->work[3].cpu_binding = dd->work[0].cpu_binding; 40968c2ecf20Sopenharmony_ci dd->work[4].cpu_binding = dd->work[1].cpu_binding; 40978c2ecf20Sopenharmony_ci dd->work[5].cpu_binding = dd->work[2].cpu_binding; 40988c2ecf20Sopenharmony_ci dd->work[6].cpu_binding = dd->work[2].cpu_binding; 40998c2ecf20Sopenharmony_ci dd->work[7].cpu_binding = dd->work[1].cpu_binding; 41008c2ecf20Sopenharmony_ci 41018c2ecf20Sopenharmony_ci /* Log the bindings */ 41028c2ecf20Sopenharmony_ci for_each_present_cpu(cpu) { 41038c2ecf20Sopenharmony_ci memset(cpu_list, 0, sizeof(cpu_list)); 41048c2ecf20Sopenharmony_ci for (i = 0, j = 0; i < MTIP_MAX_SLOT_GROUPS; i++) { 41058c2ecf20Sopenharmony_ci if (dd->work[i].cpu_binding == cpu) { 41068c2ecf20Sopenharmony_ci snprintf(&cpu_list[j], 256 - j, "%d ", i); 41078c2ecf20Sopenharmony_ci j = strlen(cpu_list); 41088c2ecf20Sopenharmony_ci } 41098c2ecf20Sopenharmony_ci } 41108c2ecf20Sopenharmony_ci if (j) 41118c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "CPU %d: WQs %s\n", cpu, cpu_list); 41128c2ecf20Sopenharmony_ci } 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[0].work, mtip_workq_sdbf0); 41158c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[1].work, mtip_workq_sdbf1); 41168c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[2].work, mtip_workq_sdbf2); 41178c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[3].work, mtip_workq_sdbf3); 41188c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[4].work, mtip_workq_sdbf4); 41198c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[5].work, mtip_workq_sdbf5); 41208c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[6].work, mtip_workq_sdbf6); 41218c2ecf20Sopenharmony_ci INIT_WORK(&dd->work[7].work, mtip_workq_sdbf7); 41228c2ecf20Sopenharmony_ci 41238c2ecf20Sopenharmony_ci pci_set_master(pdev); 41248c2ecf20Sopenharmony_ci rv = pci_enable_msi(pdev); 41258c2ecf20Sopenharmony_ci if (rv) { 41268c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 41278c2ecf20Sopenharmony_ci "Unable to enable MSI interrupt.\n"); 41288c2ecf20Sopenharmony_ci goto msi_initialize_err; 41298c2ecf20Sopenharmony_ci } 41308c2ecf20Sopenharmony_ci 41318c2ecf20Sopenharmony_ci mtip_fix_ero_nosnoop(dd, pdev); 41328c2ecf20Sopenharmony_ci 41338c2ecf20Sopenharmony_ci /* Initialize the block layer. */ 41348c2ecf20Sopenharmony_ci rv = mtip_block_initialize(dd); 41358c2ecf20Sopenharmony_ci if (rv < 0) { 41368c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 41378c2ecf20Sopenharmony_ci "Unable to initialize block layer\n"); 41388c2ecf20Sopenharmony_ci goto block_initialize_err; 41398c2ecf20Sopenharmony_ci } 41408c2ecf20Sopenharmony_ci 41418c2ecf20Sopenharmony_ci /* 41428c2ecf20Sopenharmony_ci * Increment the instance count so that each device has a unique 41438c2ecf20Sopenharmony_ci * instance number. 41448c2ecf20Sopenharmony_ci */ 41458c2ecf20Sopenharmony_ci instance++; 41468c2ecf20Sopenharmony_ci if (rv != MTIP_FTL_REBUILD_MAGIC) 41478c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); 41488c2ecf20Sopenharmony_ci else 41498c2ecf20Sopenharmony_ci rv = 0; /* device in rebuild state, return 0 from probe */ 41508c2ecf20Sopenharmony_ci 41518c2ecf20Sopenharmony_ci /* Add to online list even if in ftl rebuild */ 41528c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_lock, flags); 41538c2ecf20Sopenharmony_ci list_add(&dd->online_list, &online_list); 41548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_lock, flags); 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci goto done; 41578c2ecf20Sopenharmony_ci 41588c2ecf20Sopenharmony_ciblock_initialize_err: 41598c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 41608c2ecf20Sopenharmony_ci 41618c2ecf20Sopenharmony_cimsi_initialize_err: 41628c2ecf20Sopenharmony_ci if (dd->isr_workq) { 41638c2ecf20Sopenharmony_ci flush_workqueue(dd->isr_workq); 41648c2ecf20Sopenharmony_ci destroy_workqueue(dd->isr_workq); 41658c2ecf20Sopenharmony_ci drop_cpu(dd->work[0].cpu_binding); 41668c2ecf20Sopenharmony_ci drop_cpu(dd->work[1].cpu_binding); 41678c2ecf20Sopenharmony_ci drop_cpu(dd->work[2].cpu_binding); 41688c2ecf20Sopenharmony_ci } 41698c2ecf20Sopenharmony_cisetmask_err: 41708c2ecf20Sopenharmony_ci pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); 41718c2ecf20Sopenharmony_ci 41728c2ecf20Sopenharmony_ciiomap_err: 41738c2ecf20Sopenharmony_ci kfree(dd); 41748c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 41758c2ecf20Sopenharmony_ci return rv; 41768c2ecf20Sopenharmony_cidone: 41778c2ecf20Sopenharmony_ci return rv; 41788c2ecf20Sopenharmony_ci} 41798c2ecf20Sopenharmony_ci 41808c2ecf20Sopenharmony_ci/* 41818c2ecf20Sopenharmony_ci * Called for each probed device when the device is removed or the 41828c2ecf20Sopenharmony_ci * driver is unloaded. 41838c2ecf20Sopenharmony_ci * 41848c2ecf20Sopenharmony_ci * return value 41858c2ecf20Sopenharmony_ci * None 41868c2ecf20Sopenharmony_ci */ 41878c2ecf20Sopenharmony_cistatic void mtip_pci_remove(struct pci_dev *pdev) 41888c2ecf20Sopenharmony_ci{ 41898c2ecf20Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 41908c2ecf20Sopenharmony_ci unsigned long flags, to; 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_REMOVAL_BIT, &dd->dd_flag); 41938c2ecf20Sopenharmony_ci 41948c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_lock, flags); 41958c2ecf20Sopenharmony_ci list_del_init(&dd->online_list); 41968c2ecf20Sopenharmony_ci list_add(&dd->remove_list, &removing_list); 41978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_lock, flags); 41988c2ecf20Sopenharmony_ci 41998c2ecf20Sopenharmony_ci mtip_check_surprise_removal(pdev); 42008c2ecf20Sopenharmony_ci synchronize_irq(dd->pdev->irq); 42018c2ecf20Sopenharmony_ci 42028c2ecf20Sopenharmony_ci /* Spin until workers are done */ 42038c2ecf20Sopenharmony_ci to = jiffies + msecs_to_jiffies(4000); 42048c2ecf20Sopenharmony_ci do { 42058c2ecf20Sopenharmony_ci msleep(20); 42068c2ecf20Sopenharmony_ci } while (atomic_read(&dd->irq_workers_active) != 0 && 42078c2ecf20Sopenharmony_ci time_before(jiffies, to)); 42088c2ecf20Sopenharmony_ci 42098c2ecf20Sopenharmony_ci if (!dd->sr) 42108c2ecf20Sopenharmony_ci fsync_bdev(dd->bdev); 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci if (atomic_read(&dd->irq_workers_active) != 0) { 42138c2ecf20Sopenharmony_ci dev_warn(&dd->pdev->dev, 42148c2ecf20Sopenharmony_ci "Completion workers still active!\n"); 42158c2ecf20Sopenharmony_ci } 42168c2ecf20Sopenharmony_ci 42178c2ecf20Sopenharmony_ci blk_set_queue_dying(dd->queue); 42188c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag); 42198c2ecf20Sopenharmony_ci 42208c2ecf20Sopenharmony_ci /* Clean up the block layer. */ 42218c2ecf20Sopenharmony_ci mtip_block_remove(dd); 42228c2ecf20Sopenharmony_ci 42238c2ecf20Sopenharmony_ci if (dd->isr_workq) { 42248c2ecf20Sopenharmony_ci flush_workqueue(dd->isr_workq); 42258c2ecf20Sopenharmony_ci destroy_workqueue(dd->isr_workq); 42268c2ecf20Sopenharmony_ci drop_cpu(dd->work[0].cpu_binding); 42278c2ecf20Sopenharmony_ci drop_cpu(dd->work[1].cpu_binding); 42288c2ecf20Sopenharmony_ci drop_cpu(dd->work[2].cpu_binding); 42298c2ecf20Sopenharmony_ci } 42308c2ecf20Sopenharmony_ci 42318c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci spin_lock_irqsave(&dev_lock, flags); 42348c2ecf20Sopenharmony_ci list_del_init(&dd->remove_list); 42358c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&dev_lock, flags); 42368c2ecf20Sopenharmony_ci 42378c2ecf20Sopenharmony_ci kfree(dd); 42388c2ecf20Sopenharmony_ci 42398c2ecf20Sopenharmony_ci pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); 42408c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 42418c2ecf20Sopenharmony_ci} 42428c2ecf20Sopenharmony_ci 42438c2ecf20Sopenharmony_ci/* 42448c2ecf20Sopenharmony_ci * Called for each probed device when the device is suspended. 42458c2ecf20Sopenharmony_ci * 42468c2ecf20Sopenharmony_ci * return value 42478c2ecf20Sopenharmony_ci * 0 Success 42488c2ecf20Sopenharmony_ci * <0 Error 42498c2ecf20Sopenharmony_ci */ 42508c2ecf20Sopenharmony_cistatic int mtip_pci_suspend(struct pci_dev *pdev, pm_message_t mesg) 42518c2ecf20Sopenharmony_ci{ 42528c2ecf20Sopenharmony_ci int rv = 0; 42538c2ecf20Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 42548c2ecf20Sopenharmony_ci 42558c2ecf20Sopenharmony_ci if (!dd) { 42568c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 42578c2ecf20Sopenharmony_ci "Driver private datastructure is NULL\n"); 42588c2ecf20Sopenharmony_ci return -EFAULT; 42598c2ecf20Sopenharmony_ci } 42608c2ecf20Sopenharmony_ci 42618c2ecf20Sopenharmony_ci set_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); 42628c2ecf20Sopenharmony_ci 42638c2ecf20Sopenharmony_ci /* Disable ports & interrupts then send standby immediate */ 42648c2ecf20Sopenharmony_ci rv = mtip_block_suspend(dd); 42658c2ecf20Sopenharmony_ci if (rv < 0) { 42668c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 42678c2ecf20Sopenharmony_ci "Failed to suspend controller\n"); 42688c2ecf20Sopenharmony_ci return rv; 42698c2ecf20Sopenharmony_ci } 42708c2ecf20Sopenharmony_ci 42718c2ecf20Sopenharmony_ci /* 42728c2ecf20Sopenharmony_ci * Save the pci config space to pdev structure & 42738c2ecf20Sopenharmony_ci * disable the device 42748c2ecf20Sopenharmony_ci */ 42758c2ecf20Sopenharmony_ci pci_save_state(pdev); 42768c2ecf20Sopenharmony_ci pci_disable_device(pdev); 42778c2ecf20Sopenharmony_ci 42788c2ecf20Sopenharmony_ci /* Move to Low power state*/ 42798c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D3hot); 42808c2ecf20Sopenharmony_ci 42818c2ecf20Sopenharmony_ci return rv; 42828c2ecf20Sopenharmony_ci} 42838c2ecf20Sopenharmony_ci 42848c2ecf20Sopenharmony_ci/* 42858c2ecf20Sopenharmony_ci * Called for each probed device when the device is resumed. 42868c2ecf20Sopenharmony_ci * 42878c2ecf20Sopenharmony_ci * return value 42888c2ecf20Sopenharmony_ci * 0 Success 42898c2ecf20Sopenharmony_ci * <0 Error 42908c2ecf20Sopenharmony_ci */ 42918c2ecf20Sopenharmony_cistatic int mtip_pci_resume(struct pci_dev *pdev) 42928c2ecf20Sopenharmony_ci{ 42938c2ecf20Sopenharmony_ci int rv = 0; 42948c2ecf20Sopenharmony_ci struct driver_data *dd; 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_ci dd = pci_get_drvdata(pdev); 42978c2ecf20Sopenharmony_ci if (!dd) { 42988c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 42998c2ecf20Sopenharmony_ci "Driver private datastructure is NULL\n"); 43008c2ecf20Sopenharmony_ci return -EFAULT; 43018c2ecf20Sopenharmony_ci } 43028c2ecf20Sopenharmony_ci 43038c2ecf20Sopenharmony_ci /* Move the device to active State */ 43048c2ecf20Sopenharmony_ci pci_set_power_state(pdev, PCI_D0); 43058c2ecf20Sopenharmony_ci 43068c2ecf20Sopenharmony_ci /* Restore PCI configuration space */ 43078c2ecf20Sopenharmony_ci pci_restore_state(pdev); 43088c2ecf20Sopenharmony_ci 43098c2ecf20Sopenharmony_ci /* Enable the PCI device*/ 43108c2ecf20Sopenharmony_ci rv = pcim_enable_device(pdev); 43118c2ecf20Sopenharmony_ci if (rv < 0) { 43128c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 43138c2ecf20Sopenharmony_ci "Failed to enable card during resume\n"); 43148c2ecf20Sopenharmony_ci goto err; 43158c2ecf20Sopenharmony_ci } 43168c2ecf20Sopenharmony_ci pci_set_master(pdev); 43178c2ecf20Sopenharmony_ci 43188c2ecf20Sopenharmony_ci /* 43198c2ecf20Sopenharmony_ci * Calls hbaReset, initPort, & startPort function 43208c2ecf20Sopenharmony_ci * then enables interrupts 43218c2ecf20Sopenharmony_ci */ 43228c2ecf20Sopenharmony_ci rv = mtip_block_resume(dd); 43238c2ecf20Sopenharmony_ci if (rv < 0) 43248c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Unable to resume\n"); 43258c2ecf20Sopenharmony_ci 43268c2ecf20Sopenharmony_cierr: 43278c2ecf20Sopenharmony_ci clear_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); 43288c2ecf20Sopenharmony_ci 43298c2ecf20Sopenharmony_ci return rv; 43308c2ecf20Sopenharmony_ci} 43318c2ecf20Sopenharmony_ci 43328c2ecf20Sopenharmony_ci/* 43338c2ecf20Sopenharmony_ci * Shutdown routine 43348c2ecf20Sopenharmony_ci * 43358c2ecf20Sopenharmony_ci * return value 43368c2ecf20Sopenharmony_ci * None 43378c2ecf20Sopenharmony_ci */ 43388c2ecf20Sopenharmony_cistatic void mtip_pci_shutdown(struct pci_dev *pdev) 43398c2ecf20Sopenharmony_ci{ 43408c2ecf20Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 43418c2ecf20Sopenharmony_ci if (dd) 43428c2ecf20Sopenharmony_ci mtip_block_shutdown(dd); 43438c2ecf20Sopenharmony_ci} 43448c2ecf20Sopenharmony_ci 43458c2ecf20Sopenharmony_ci/* Table of device ids supported by this driver. */ 43468c2ecf20Sopenharmony_cistatic const struct pci_device_id mtip_pci_tbl[] = { 43478c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) }, 43488c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) }, 43498c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) }, 43508c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) }, 43518c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) }, 43528c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) }, 43538c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) }, 43548c2ecf20Sopenharmony_ci { 0 } 43558c2ecf20Sopenharmony_ci}; 43568c2ecf20Sopenharmony_ci 43578c2ecf20Sopenharmony_ci/* Structure that describes the PCI driver functions. */ 43588c2ecf20Sopenharmony_cistatic struct pci_driver mtip_pci_driver = { 43598c2ecf20Sopenharmony_ci .name = MTIP_DRV_NAME, 43608c2ecf20Sopenharmony_ci .id_table = mtip_pci_tbl, 43618c2ecf20Sopenharmony_ci .probe = mtip_pci_probe, 43628c2ecf20Sopenharmony_ci .remove = mtip_pci_remove, 43638c2ecf20Sopenharmony_ci .suspend = mtip_pci_suspend, 43648c2ecf20Sopenharmony_ci .resume = mtip_pci_resume, 43658c2ecf20Sopenharmony_ci .shutdown = mtip_pci_shutdown, 43668c2ecf20Sopenharmony_ci}; 43678c2ecf20Sopenharmony_ci 43688c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mtip_pci_tbl); 43698c2ecf20Sopenharmony_ci 43708c2ecf20Sopenharmony_ci/* 43718c2ecf20Sopenharmony_ci * Module initialization function. 43728c2ecf20Sopenharmony_ci * 43738c2ecf20Sopenharmony_ci * Called once when the module is loaded. This function allocates a major 43748c2ecf20Sopenharmony_ci * block device number to the Cyclone devices and registers the PCI layer 43758c2ecf20Sopenharmony_ci * of the driver. 43768c2ecf20Sopenharmony_ci * 43778c2ecf20Sopenharmony_ci * Return value 43788c2ecf20Sopenharmony_ci * 0 on success else error code. 43798c2ecf20Sopenharmony_ci */ 43808c2ecf20Sopenharmony_cistatic int __init mtip_init(void) 43818c2ecf20Sopenharmony_ci{ 43828c2ecf20Sopenharmony_ci int error; 43838c2ecf20Sopenharmony_ci 43848c2ecf20Sopenharmony_ci pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n"); 43858c2ecf20Sopenharmony_ci 43868c2ecf20Sopenharmony_ci spin_lock_init(&dev_lock); 43878c2ecf20Sopenharmony_ci 43888c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&online_list); 43898c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&removing_list); 43908c2ecf20Sopenharmony_ci 43918c2ecf20Sopenharmony_ci /* Allocate a major block device number to use with this driver. */ 43928c2ecf20Sopenharmony_ci error = register_blkdev(0, MTIP_DRV_NAME); 43938c2ecf20Sopenharmony_ci if (error <= 0) { 43948c2ecf20Sopenharmony_ci pr_err("Unable to register block device (%d)\n", 43958c2ecf20Sopenharmony_ci error); 43968c2ecf20Sopenharmony_ci return -EBUSY; 43978c2ecf20Sopenharmony_ci } 43988c2ecf20Sopenharmony_ci mtip_major = error; 43998c2ecf20Sopenharmony_ci 44008c2ecf20Sopenharmony_ci dfs_parent = debugfs_create_dir("rssd", NULL); 44018c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dfs_parent)) { 44028c2ecf20Sopenharmony_ci pr_warn("Error creating debugfs parent\n"); 44038c2ecf20Sopenharmony_ci dfs_parent = NULL; 44048c2ecf20Sopenharmony_ci } 44058c2ecf20Sopenharmony_ci if (dfs_parent) { 44068c2ecf20Sopenharmony_ci dfs_device_status = debugfs_create_file("device_status", 44078c2ecf20Sopenharmony_ci 0444, dfs_parent, NULL, 44088c2ecf20Sopenharmony_ci &mtip_device_status_fops); 44098c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(dfs_device_status)) { 44108c2ecf20Sopenharmony_ci pr_err("Error creating device_status node\n"); 44118c2ecf20Sopenharmony_ci dfs_device_status = NULL; 44128c2ecf20Sopenharmony_ci } 44138c2ecf20Sopenharmony_ci } 44148c2ecf20Sopenharmony_ci 44158c2ecf20Sopenharmony_ci /* Register our PCI operations. */ 44168c2ecf20Sopenharmony_ci error = pci_register_driver(&mtip_pci_driver); 44178c2ecf20Sopenharmony_ci if (error) { 44188c2ecf20Sopenharmony_ci debugfs_remove(dfs_parent); 44198c2ecf20Sopenharmony_ci unregister_blkdev(mtip_major, MTIP_DRV_NAME); 44208c2ecf20Sopenharmony_ci } 44218c2ecf20Sopenharmony_ci 44228c2ecf20Sopenharmony_ci return error; 44238c2ecf20Sopenharmony_ci} 44248c2ecf20Sopenharmony_ci 44258c2ecf20Sopenharmony_ci/* 44268c2ecf20Sopenharmony_ci * Module de-initialization function. 44278c2ecf20Sopenharmony_ci * 44288c2ecf20Sopenharmony_ci * Called once when the module is unloaded. This function deallocates 44298c2ecf20Sopenharmony_ci * the major block device number allocated by mtip_init() and 44308c2ecf20Sopenharmony_ci * unregisters the PCI layer of the driver. 44318c2ecf20Sopenharmony_ci * 44328c2ecf20Sopenharmony_ci * Return value 44338c2ecf20Sopenharmony_ci * none 44348c2ecf20Sopenharmony_ci */ 44358c2ecf20Sopenharmony_cistatic void __exit mtip_exit(void) 44368c2ecf20Sopenharmony_ci{ 44378c2ecf20Sopenharmony_ci /* Release the allocated major block device number. */ 44388c2ecf20Sopenharmony_ci unregister_blkdev(mtip_major, MTIP_DRV_NAME); 44398c2ecf20Sopenharmony_ci 44408c2ecf20Sopenharmony_ci /* Unregister the PCI driver. */ 44418c2ecf20Sopenharmony_ci pci_unregister_driver(&mtip_pci_driver); 44428c2ecf20Sopenharmony_ci 44438c2ecf20Sopenharmony_ci debugfs_remove_recursive(dfs_parent); 44448c2ecf20Sopenharmony_ci} 44458c2ecf20Sopenharmony_ci 44468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Micron Technology, Inc"); 44478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Micron RealSSD PCIe Block Driver"); 44488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 44498c2ecf20Sopenharmony_ciMODULE_VERSION(MTIP_DRV_VERSION); 44508c2ecf20Sopenharmony_ci 44518c2ecf20Sopenharmony_cimodule_init(mtip_init); 44528c2ecf20Sopenharmony_cimodule_exit(mtip_exit); 4453