162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for the Micron P320 SSD 462306a36Sopenharmony_ci * Copyright (C) 2011 Micron Technology, Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Portions of this code were derived from works subjected to the 762306a36Sopenharmony_ci * following copyright: 862306a36Sopenharmony_ci * Copyright (C) 2009 Integrated Device Technology, Inc. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/pci.h> 1262306a36Sopenharmony_ci#include <linux/interrupt.h> 1362306a36Sopenharmony_ci#include <linux/ata.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/hdreg.h> 1662306a36Sopenharmony_ci#include <linux/uaccess.h> 1762306a36Sopenharmony_ci#include <linux/random.h> 1862306a36Sopenharmony_ci#include <linux/smp.h> 1962306a36Sopenharmony_ci#include <linux/compat.h> 2062306a36Sopenharmony_ci#include <linux/fs.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/blkdev.h> 2362306a36Sopenharmony_ci#include <linux/blk-mq.h> 2462306a36Sopenharmony_ci#include <linux/bio.h> 2562306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2662306a36Sopenharmony_ci#include <linux/idr.h> 2762306a36Sopenharmony_ci#include <linux/kthread.h> 2862306a36Sopenharmony_ci#include <../drivers/ata/ahci.h> 2962306a36Sopenharmony_ci#include <linux/export.h> 3062306a36Sopenharmony_ci#include <linux/debugfs.h> 3162306a36Sopenharmony_ci#include <linux/prefetch.h> 3262306a36Sopenharmony_ci#include <linux/numa.h> 3362306a36Sopenharmony_ci#include "mtip32xx.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#define HW_CMD_SLOT_SZ (MTIP_MAX_COMMAND_SLOTS * 32) 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci/* DMA region containing RX Fis, Identify, RLE10, and SMART buffers */ 3862306a36Sopenharmony_ci#define AHCI_RX_FIS_SZ 0x100 3962306a36Sopenharmony_ci#define AHCI_RX_FIS_OFFSET 0x0 4062306a36Sopenharmony_ci#define AHCI_IDFY_SZ ATA_SECT_SIZE 4162306a36Sopenharmony_ci#define AHCI_IDFY_OFFSET 0x400 4262306a36Sopenharmony_ci#define AHCI_SECTBUF_SZ ATA_SECT_SIZE 4362306a36Sopenharmony_ci#define AHCI_SECTBUF_OFFSET 0x800 4462306a36Sopenharmony_ci#define AHCI_SMARTBUF_SZ ATA_SECT_SIZE 4562306a36Sopenharmony_ci#define AHCI_SMARTBUF_OFFSET 0xC00 4662306a36Sopenharmony_ci/* 0x100 + 0x200 + 0x200 + 0x200 is smaller than 4k but we pad it out */ 4762306a36Sopenharmony_ci#define BLOCK_DMA_ALLOC_SZ 4096 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci/* DMA region containing command table (should be 8192 bytes) */ 5062306a36Sopenharmony_ci#define AHCI_CMD_SLOT_SZ sizeof(struct mtip_cmd_hdr) 5162306a36Sopenharmony_ci#define AHCI_CMD_TBL_SZ (MTIP_MAX_COMMAND_SLOTS * AHCI_CMD_SLOT_SZ) 5262306a36Sopenharmony_ci#define AHCI_CMD_TBL_OFFSET 0x0 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* DMA region per command (contains header and SGL) */ 5562306a36Sopenharmony_ci#define AHCI_CMD_TBL_HDR_SZ 0x80 5662306a36Sopenharmony_ci#define AHCI_CMD_TBL_HDR_OFFSET 0x0 5762306a36Sopenharmony_ci#define AHCI_CMD_TBL_SGL_SZ (MTIP_MAX_SG * sizeof(struct mtip_cmd_sg)) 5862306a36Sopenharmony_ci#define AHCI_CMD_TBL_SGL_OFFSET AHCI_CMD_TBL_HDR_SZ 5962306a36Sopenharmony_ci#define CMD_DMA_ALLOC_SZ (AHCI_CMD_TBL_SGL_SZ + AHCI_CMD_TBL_HDR_SZ) 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define HOST_CAP_NZDMA (1 << 19) 6362306a36Sopenharmony_ci#define HOST_HSORG 0xFC 6462306a36Sopenharmony_ci#define HSORG_DISABLE_SLOTGRP_INTR (1<<24) 6562306a36Sopenharmony_ci#define HSORG_DISABLE_SLOTGRP_PXIS (1<<16) 6662306a36Sopenharmony_ci#define HSORG_HWREV 0xFF00 6762306a36Sopenharmony_ci#define HSORG_STYLE 0x8 6862306a36Sopenharmony_ci#define HSORG_SLOTGROUPS 0x7 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define PORT_COMMAND_ISSUE 0x38 7162306a36Sopenharmony_ci#define PORT_SDBV 0x7C 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define PORT_OFFSET 0x100 7462306a36Sopenharmony_ci#define PORT_MEM_SIZE 0x80 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define PORT_IRQ_ERR \ 7762306a36Sopenharmony_ci (PORT_IRQ_HBUS_ERR | PORT_IRQ_IF_ERR | PORT_IRQ_CONNECT | \ 7862306a36Sopenharmony_ci PORT_IRQ_PHYRDY | PORT_IRQ_UNK_FIS | PORT_IRQ_BAD_PMP | \ 7962306a36Sopenharmony_ci PORT_IRQ_TF_ERR | PORT_IRQ_HBUS_DATA_ERR | PORT_IRQ_IF_NONFATAL | \ 8062306a36Sopenharmony_ci PORT_IRQ_OVERFLOW) 8162306a36Sopenharmony_ci#define PORT_IRQ_LEGACY \ 8262306a36Sopenharmony_ci (PORT_IRQ_PIOS_FIS | PORT_IRQ_D2H_REG_FIS) 8362306a36Sopenharmony_ci#define PORT_IRQ_HANDLED \ 8462306a36Sopenharmony_ci (PORT_IRQ_SDB_FIS | PORT_IRQ_LEGACY | \ 8562306a36Sopenharmony_ci PORT_IRQ_TF_ERR | PORT_IRQ_IF_ERR | \ 8662306a36Sopenharmony_ci PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY) 8762306a36Sopenharmony_ci#define DEF_PORT_IRQ \ 8862306a36Sopenharmony_ci (PORT_IRQ_ERR | PORT_IRQ_LEGACY | PORT_IRQ_SDB_FIS) 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* product numbers */ 9162306a36Sopenharmony_ci#define MTIP_PRODUCT_UNKNOWN 0x00 9262306a36Sopenharmony_ci#define MTIP_PRODUCT_ASICFPGA 0x11 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* Device instance number, incremented each time a device is probed. */ 9562306a36Sopenharmony_cistatic int instance; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/* 9862306a36Sopenharmony_ci * Global variable used to hold the major block device number 9962306a36Sopenharmony_ci * allocated in mtip_init(). 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistatic int mtip_major; 10262306a36Sopenharmony_cistatic struct dentry *dfs_parent; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic u32 cpu_use[NR_CPUS]; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistatic DEFINE_IDA(rssd_index_ida); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int mtip_block_initialize(struct driver_data *dd); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 11162306a36Sopenharmony_cistruct mtip_compat_ide_task_request_s { 11262306a36Sopenharmony_ci __u8 io_ports[8]; 11362306a36Sopenharmony_ci __u8 hob_ports[8]; 11462306a36Sopenharmony_ci ide_reg_valid_t out_flags; 11562306a36Sopenharmony_ci ide_reg_valid_t in_flags; 11662306a36Sopenharmony_ci int data_phase; 11762306a36Sopenharmony_ci int req_cmd; 11862306a36Sopenharmony_ci compat_ulong_t out_size; 11962306a36Sopenharmony_ci compat_ulong_t in_size; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci#endif 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* 12462306a36Sopenharmony_ci * This function check_for_surprise_removal is called 12562306a36Sopenharmony_ci * while card is removed from the system and it will 12662306a36Sopenharmony_ci * read the vendor id from the configuration space 12762306a36Sopenharmony_ci * 12862306a36Sopenharmony_ci * @pdev Pointer to the pci_dev structure. 12962306a36Sopenharmony_ci * 13062306a36Sopenharmony_ci * return value 13162306a36Sopenharmony_ci * true if device removed, else false 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_cistatic bool mtip_check_surprise_removal(struct driver_data *dd) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci u16 vendor_id = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (dd->sr) 13862306a36Sopenharmony_ci return true; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* Read the vendorID from the configuration space */ 14162306a36Sopenharmony_ci pci_read_config_word(dd->pdev, 0x00, &vendor_id); 14262306a36Sopenharmony_ci if (vendor_id == 0xFFFF) { 14362306a36Sopenharmony_ci dd->sr = true; 14462306a36Sopenharmony_ci if (dd->disk) 14562306a36Sopenharmony_ci blk_mark_disk_dead(dd->disk); 14662306a36Sopenharmony_ci return true; /* device removed */ 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return false; /* device present */ 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic struct mtip_cmd *mtip_cmd_from_tag(struct driver_data *dd, 15362306a36Sopenharmony_ci unsigned int tag) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return blk_mq_rq_to_pdu(blk_mq_tag_to_rq(dd->tags.tags[0], tag)); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci/* 15962306a36Sopenharmony_ci * Reset the HBA (without sleeping) 16062306a36Sopenharmony_ci * 16162306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 16262306a36Sopenharmony_ci * 16362306a36Sopenharmony_ci * return value 16462306a36Sopenharmony_ci * 0 The reset was successful. 16562306a36Sopenharmony_ci * -1 The HBA Reset bit did not clear. 16662306a36Sopenharmony_ci */ 16762306a36Sopenharmony_cistatic int mtip_hba_reset(struct driver_data *dd) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned long timeout; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Set the reset bit */ 17262306a36Sopenharmony_ci writel(HOST_RESET, dd->mmio + HOST_CTL); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Flush */ 17562306a36Sopenharmony_ci readl(dd->mmio + HOST_CTL); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* 17862306a36Sopenharmony_ci * Spin for up to 10 seconds waiting for reset acknowledgement. Spec 17962306a36Sopenharmony_ci * is 1 sec but in LUN failure conditions, up to 10 secs are required 18062306a36Sopenharmony_ci */ 18162306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(10000); 18262306a36Sopenharmony_ci do { 18362306a36Sopenharmony_ci mdelay(10); 18462306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) 18562306a36Sopenharmony_ci return -1; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci } while ((readl(dd->mmio + HOST_CTL) & HOST_RESET) 18862306a36Sopenharmony_ci && time_before(jiffies, timeout)); 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci if (readl(dd->mmio + HOST_CTL) & HOST_RESET) 19162306a36Sopenharmony_ci return -1; 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Issue a command to the hardware. 19862306a36Sopenharmony_ci * 19962306a36Sopenharmony_ci * Set the appropriate bit in the s_active and Command Issue hardware 20062306a36Sopenharmony_ci * registers, causing hardware command processing to begin. 20162306a36Sopenharmony_ci * 20262306a36Sopenharmony_ci * @port Pointer to the port structure. 20362306a36Sopenharmony_ci * @tag The tag of the command to be issued. 20462306a36Sopenharmony_ci * 20562306a36Sopenharmony_ci * return value 20662306a36Sopenharmony_ci * None 20762306a36Sopenharmony_ci */ 20862306a36Sopenharmony_cistatic inline void mtip_issue_ncq_command(struct mtip_port *port, int tag) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci int group = tag >> 5; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci /* guard SACT and CI registers */ 21362306a36Sopenharmony_ci spin_lock(&port->cmd_issue_lock[group]); 21462306a36Sopenharmony_ci writel((1 << MTIP_TAG_BIT(tag)), 21562306a36Sopenharmony_ci port->s_active[MTIP_TAG_INDEX(tag)]); 21662306a36Sopenharmony_ci writel((1 << MTIP_TAG_BIT(tag)), 21762306a36Sopenharmony_ci port->cmd_issue[MTIP_TAG_INDEX(tag)]); 21862306a36Sopenharmony_ci spin_unlock(&port->cmd_issue_lock[group]); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* 22262306a36Sopenharmony_ci * Enable/disable the reception of FIS 22362306a36Sopenharmony_ci * 22462306a36Sopenharmony_ci * @port Pointer to the port data structure 22562306a36Sopenharmony_ci * @enable 1 to enable, 0 to disable 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * return value 22862306a36Sopenharmony_ci * Previous state: 1 enabled, 0 disabled 22962306a36Sopenharmony_ci */ 23062306a36Sopenharmony_cistatic int mtip_enable_fis(struct mtip_port *port, int enable) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci u32 tmp; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci /* enable FIS reception */ 23562306a36Sopenharmony_ci tmp = readl(port->mmio + PORT_CMD); 23662306a36Sopenharmony_ci if (enable) 23762306a36Sopenharmony_ci writel(tmp | PORT_CMD_FIS_RX, port->mmio + PORT_CMD); 23862306a36Sopenharmony_ci else 23962306a36Sopenharmony_ci writel(tmp & ~PORT_CMD_FIS_RX, port->mmio + PORT_CMD); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* Flush */ 24262306a36Sopenharmony_ci readl(port->mmio + PORT_CMD); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci return (((tmp & PORT_CMD_FIS_RX) == PORT_CMD_FIS_RX)); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/* 24862306a36Sopenharmony_ci * Enable/disable the DMA engine 24962306a36Sopenharmony_ci * 25062306a36Sopenharmony_ci * @port Pointer to the port data structure 25162306a36Sopenharmony_ci * @enable 1 to enable, 0 to disable 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * return value 25462306a36Sopenharmony_ci * Previous state: 1 enabled, 0 disabled. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic int mtip_enable_engine(struct mtip_port *port, int enable) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci u32 tmp; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* enable FIS reception */ 26162306a36Sopenharmony_ci tmp = readl(port->mmio + PORT_CMD); 26262306a36Sopenharmony_ci if (enable) 26362306a36Sopenharmony_ci writel(tmp | PORT_CMD_START, port->mmio + PORT_CMD); 26462306a36Sopenharmony_ci else 26562306a36Sopenharmony_ci writel(tmp & ~PORT_CMD_START, port->mmio + PORT_CMD); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci readl(port->mmio + PORT_CMD); 26862306a36Sopenharmony_ci return (((tmp & PORT_CMD_START) == PORT_CMD_START)); 26962306a36Sopenharmony_ci} 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci/* 27262306a36Sopenharmony_ci * Enables the port DMA engine and FIS reception. 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci * return value 27562306a36Sopenharmony_ci * None 27662306a36Sopenharmony_ci */ 27762306a36Sopenharmony_cistatic inline void mtip_start_port(struct mtip_port *port) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci /* Enable FIS reception */ 28062306a36Sopenharmony_ci mtip_enable_fis(port, 1); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Enable the DMA engine */ 28362306a36Sopenharmony_ci mtip_enable_engine(port, 1); 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci/* 28762306a36Sopenharmony_ci * Deinitialize a port by disabling port interrupts, the DMA engine, 28862306a36Sopenharmony_ci * and FIS reception. 28962306a36Sopenharmony_ci * 29062306a36Sopenharmony_ci * @port Pointer to the port structure 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * return value 29362306a36Sopenharmony_ci * None 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_cistatic inline void mtip_deinit_port(struct mtip_port *port) 29662306a36Sopenharmony_ci{ 29762306a36Sopenharmony_ci /* Disable interrupts on this port */ 29862306a36Sopenharmony_ci writel(0, port->mmio + PORT_IRQ_MASK); 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* Disable the DMA engine */ 30162306a36Sopenharmony_ci mtip_enable_engine(port, 0); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Disable FIS reception */ 30462306a36Sopenharmony_ci mtip_enable_fis(port, 0); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/* 30862306a36Sopenharmony_ci * Initialize a port. 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * This function deinitializes the port by calling mtip_deinit_port() and 31162306a36Sopenharmony_ci * then initializes it by setting the command header and RX FIS addresses, 31262306a36Sopenharmony_ci * clearing the SError register and any pending port interrupts before 31362306a36Sopenharmony_ci * re-enabling the default set of port interrupts. 31462306a36Sopenharmony_ci * 31562306a36Sopenharmony_ci * @port Pointer to the port structure. 31662306a36Sopenharmony_ci * 31762306a36Sopenharmony_ci * return value 31862306a36Sopenharmony_ci * None 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_cistatic void mtip_init_port(struct mtip_port *port) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci int i; 32362306a36Sopenharmony_ci mtip_deinit_port(port); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Program the command list base and FIS base addresses */ 32662306a36Sopenharmony_ci if (readl(port->dd->mmio + HOST_CAP) & HOST_CAP_64) { 32762306a36Sopenharmony_ci writel((port->command_list_dma >> 16) >> 16, 32862306a36Sopenharmony_ci port->mmio + PORT_LST_ADDR_HI); 32962306a36Sopenharmony_ci writel((port->rxfis_dma >> 16) >> 16, 33062306a36Sopenharmony_ci port->mmio + PORT_FIS_ADDR_HI); 33162306a36Sopenharmony_ci set_bit(MTIP_PF_HOST_CAP_64, &port->flags); 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci writel(port->command_list_dma & 0xFFFFFFFF, 33562306a36Sopenharmony_ci port->mmio + PORT_LST_ADDR); 33662306a36Sopenharmony_ci writel(port->rxfis_dma & 0xFFFFFFFF, port->mmio + PORT_FIS_ADDR); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Clear SError */ 33962306a36Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_ERR), port->mmio + PORT_SCR_ERR); 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* reset the completed registers.*/ 34262306a36Sopenharmony_ci for (i = 0; i < port->dd->slot_groups; i++) 34362306a36Sopenharmony_ci writel(0xFFFFFFFF, port->completed[i]); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci /* Clear any pending interrupts for this port */ 34662306a36Sopenharmony_ci writel(readl(port->mmio + PORT_IRQ_STAT), port->mmio + PORT_IRQ_STAT); 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Clear any pending interrupts on the HBA. */ 34962306a36Sopenharmony_ci writel(readl(port->dd->mmio + HOST_IRQ_STAT), 35062306a36Sopenharmony_ci port->dd->mmio + HOST_IRQ_STAT); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci /* Enable port interrupts */ 35362306a36Sopenharmony_ci writel(DEF_PORT_IRQ, port->mmio + PORT_IRQ_MASK); 35462306a36Sopenharmony_ci} 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci/* 35762306a36Sopenharmony_ci * Restart a port 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * @port Pointer to the port data structure. 36062306a36Sopenharmony_ci * 36162306a36Sopenharmony_ci * return value 36262306a36Sopenharmony_ci * None 36362306a36Sopenharmony_ci */ 36462306a36Sopenharmony_cistatic void mtip_restart_port(struct mtip_port *port) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci unsigned long timeout; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Disable the DMA engine */ 36962306a36Sopenharmony_ci mtip_enable_engine(port, 0); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Chip quirk: wait up to 500ms for PxCMD.CR == 0 */ 37262306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 37362306a36Sopenharmony_ci while ((readl(port->mmio + PORT_CMD) & PORT_CMD_LIST_ON) 37462306a36Sopenharmony_ci && time_before(jiffies, timeout)) 37562306a36Sopenharmony_ci ; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 37862306a36Sopenharmony_ci return; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci /* 38162306a36Sopenharmony_ci * Chip quirk: escalate to hba reset if 38262306a36Sopenharmony_ci * PxCMD.CR not clear after 500 ms 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_ci if (readl(port->mmio + PORT_CMD) & PORT_CMD_LIST_ON) { 38562306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 38662306a36Sopenharmony_ci "PxCMD.CR not clear, escalating reset\n"); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (mtip_hba_reset(port->dd)) 38962306a36Sopenharmony_ci dev_err(&port->dd->pdev->dev, 39062306a36Sopenharmony_ci "HBA reset escalation failed.\n"); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* 30 ms delay before com reset to quiesce chip */ 39362306a36Sopenharmony_ci mdelay(30); 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "Issuing COM reset\n"); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci /* Set PxSCTL.DET */ 39962306a36Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_CTL) | 40062306a36Sopenharmony_ci 1, port->mmio + PORT_SCR_CTL); 40162306a36Sopenharmony_ci readl(port->mmio + PORT_SCR_CTL); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci /* Wait 1 ms to quiesce chip function */ 40462306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(1); 40562306a36Sopenharmony_ci while (time_before(jiffies, timeout)) 40662306a36Sopenharmony_ci ; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 40962306a36Sopenharmony_ci return; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci /* Clear PxSCTL.DET */ 41262306a36Sopenharmony_ci writel(readl(port->mmio + PORT_SCR_CTL) & ~1, 41362306a36Sopenharmony_ci port->mmio + PORT_SCR_CTL); 41462306a36Sopenharmony_ci readl(port->mmio + PORT_SCR_CTL); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Wait 500 ms for bit 0 of PORT_SCR_STS to be set */ 41762306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(500); 41862306a36Sopenharmony_ci while (((readl(port->mmio + PORT_SCR_STAT) & 0x01) == 0) 41962306a36Sopenharmony_ci && time_before(jiffies, timeout)) 42062306a36Sopenharmony_ci ; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 42362306a36Sopenharmony_ci return; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if ((readl(port->mmio + PORT_SCR_STAT) & 0x01) == 0) 42662306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 42762306a36Sopenharmony_ci "COM reset failed\n"); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci mtip_init_port(port); 43062306a36Sopenharmony_ci mtip_start_port(port); 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic int mtip_device_reset(struct driver_data *dd) 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci int rv = 0; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (mtip_check_surprise_removal(dd)) 43962306a36Sopenharmony_ci return 0; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (mtip_hba_reset(dd) < 0) 44262306a36Sopenharmony_ci rv = -EFAULT; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci mdelay(1); 44562306a36Sopenharmony_ci mtip_init_port(dd->port); 44662306a36Sopenharmony_ci mtip_start_port(dd->port); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Enable interrupts on the HBA. */ 44962306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 45062306a36Sopenharmony_ci dd->mmio + HOST_CTL); 45162306a36Sopenharmony_ci return rv; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ci/* 45562306a36Sopenharmony_ci * Helper function for tag logging 45662306a36Sopenharmony_ci */ 45762306a36Sopenharmony_cistatic void print_tags(struct driver_data *dd, 45862306a36Sopenharmony_ci char *msg, 45962306a36Sopenharmony_ci unsigned long *tagbits, 46062306a36Sopenharmony_ci int cnt) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci unsigned char tagmap[128]; 46362306a36Sopenharmony_ci int group, tagmap_len = 0; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci memset(tagmap, 0, sizeof(tagmap)); 46662306a36Sopenharmony_ci for (group = SLOTBITS_IN_LONGS; group > 0; group--) 46762306a36Sopenharmony_ci tagmap_len += sprintf(tagmap + tagmap_len, "%016lX ", 46862306a36Sopenharmony_ci tagbits[group-1]); 46962306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 47062306a36Sopenharmony_ci "%d command(s) %s: tagmap [%s]", cnt, msg, tagmap); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_cistatic int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, 47462306a36Sopenharmony_ci dma_addr_t buffer_dma, unsigned int sectors); 47562306a36Sopenharmony_cistatic int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, 47662306a36Sopenharmony_ci struct smart_attr *attrib); 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic void mtip_complete_command(struct mtip_cmd *cmd, blk_status_t status) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci struct request *req = blk_mq_rq_from_pdu(cmd); 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci cmd->status = status; 48362306a36Sopenharmony_ci if (likely(!blk_should_fake_timeout(req->q))) 48462306a36Sopenharmony_ci blk_mq_complete_request(req); 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci/* 48862306a36Sopenharmony_ci * Handle an error. 48962306a36Sopenharmony_ci * 49062306a36Sopenharmony_ci * @dd Pointer to the DRIVER_DATA structure. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * return value 49362306a36Sopenharmony_ci * None 49462306a36Sopenharmony_ci */ 49562306a36Sopenharmony_cistatic void mtip_handle_tfe(struct driver_data *dd) 49662306a36Sopenharmony_ci{ 49762306a36Sopenharmony_ci int group, tag, bit, reissue, rv; 49862306a36Sopenharmony_ci struct mtip_port *port; 49962306a36Sopenharmony_ci struct mtip_cmd *cmd; 50062306a36Sopenharmony_ci u32 completed; 50162306a36Sopenharmony_ci struct host_to_dev_fis *fis; 50262306a36Sopenharmony_ci unsigned long tagaccum[SLOTBITS_IN_LONGS]; 50362306a36Sopenharmony_ci unsigned int cmd_cnt = 0; 50462306a36Sopenharmony_ci unsigned char *buf; 50562306a36Sopenharmony_ci char *fail_reason = NULL; 50662306a36Sopenharmony_ci int fail_all_ncq_write = 0, fail_all_ncq_cmds = 0; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, "Taskfile error\n"); 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci port = dd->port; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags)) { 51362306a36Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); 51462306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " TFE for the internal command\n"); 51562306a36Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_IOERR); 51662306a36Sopenharmony_ci return; 51762306a36Sopenharmony_ci } 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* clear the tag accumulator */ 52062306a36Sopenharmony_ci memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* Loop through all the groups */ 52362306a36Sopenharmony_ci for (group = 0; group < dd->slot_groups; group++) { 52462306a36Sopenharmony_ci completed = readl(port->completed[group]); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, "g=%u, comp=%x\n", group, completed); 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci /* clear completed status register in the hardware.*/ 52962306a36Sopenharmony_ci writel(completed, port->completed[group]); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* Process successfully completed commands */ 53262306a36Sopenharmony_ci for (bit = 0; bit < 32 && completed; bit++) { 53362306a36Sopenharmony_ci if (!(completed & (1<<bit))) 53462306a36Sopenharmony_ci continue; 53562306a36Sopenharmony_ci tag = (group << 5) + bit; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* Skip the internal command slot */ 53862306a36Sopenharmony_ci if (tag == MTIP_TAG_INTERNAL) 53962306a36Sopenharmony_ci continue; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, tag); 54262306a36Sopenharmony_ci mtip_complete_command(cmd, 0); 54362306a36Sopenharmony_ci set_bit(tag, tagaccum); 54462306a36Sopenharmony_ci cmd_cnt++; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci print_tags(dd, "completed (TFE)", tagaccum, cmd_cnt); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Restart the port */ 55162306a36Sopenharmony_ci mdelay(20); 55262306a36Sopenharmony_ci mtip_restart_port(port); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Trying to determine the cause of the error */ 55562306a36Sopenharmony_ci rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, 55662306a36Sopenharmony_ci dd->port->log_buf, 55762306a36Sopenharmony_ci dd->port->log_buf_dma, 1); 55862306a36Sopenharmony_ci if (rv) { 55962306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 56062306a36Sopenharmony_ci "Error in READ LOG EXT (10h) command\n"); 56162306a36Sopenharmony_ci /* non-critical error, don't fail the load */ 56262306a36Sopenharmony_ci } else { 56362306a36Sopenharmony_ci buf = (unsigned char *)dd->port->log_buf; 56462306a36Sopenharmony_ci if (buf[259] & 0x1) { 56562306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 56662306a36Sopenharmony_ci "Write protect bit is set.\n"); 56762306a36Sopenharmony_ci set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); 56862306a36Sopenharmony_ci fail_all_ncq_write = 1; 56962306a36Sopenharmony_ci fail_reason = "write protect"; 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_ci if (buf[288] == 0xF7) { 57262306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 57362306a36Sopenharmony_ci "Exceeded Tmax, drive in thermal shutdown.\n"); 57462306a36Sopenharmony_ci set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); 57562306a36Sopenharmony_ci fail_all_ncq_cmds = 1; 57662306a36Sopenharmony_ci fail_reason = "thermal shutdown"; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci if (buf[288] == 0xBF) { 57962306a36Sopenharmony_ci set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); 58062306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 58162306a36Sopenharmony_ci "Drive indicates rebuild has failed. Secure erase required.\n"); 58262306a36Sopenharmony_ci fail_all_ncq_cmds = 1; 58362306a36Sopenharmony_ci fail_reason = "rebuild failed"; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci } 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci /* clear the tag accumulator */ 58862306a36Sopenharmony_ci memset(tagaccum, 0, SLOTBITS_IN_LONGS * sizeof(long)); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci /* Loop through all the groups */ 59162306a36Sopenharmony_ci for (group = 0; group < dd->slot_groups; group++) { 59262306a36Sopenharmony_ci for (bit = 0; bit < 32; bit++) { 59362306a36Sopenharmony_ci reissue = 1; 59462306a36Sopenharmony_ci tag = (group << 5) + bit; 59562306a36Sopenharmony_ci cmd = mtip_cmd_from_tag(dd, tag); 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci fis = (struct host_to_dev_fis *)cmd->command; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci /* Should re-issue? */ 60062306a36Sopenharmony_ci if (tag == MTIP_TAG_INTERNAL || 60162306a36Sopenharmony_ci fis->command == ATA_CMD_SET_FEATURES) 60262306a36Sopenharmony_ci reissue = 0; 60362306a36Sopenharmony_ci else { 60462306a36Sopenharmony_ci if (fail_all_ncq_cmds || 60562306a36Sopenharmony_ci (fail_all_ncq_write && 60662306a36Sopenharmony_ci fis->command == ATA_CMD_FPDMA_WRITE)) { 60762306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 60862306a36Sopenharmony_ci " Fail: %s w/tag %d [%s].\n", 60962306a36Sopenharmony_ci fis->command == ATA_CMD_FPDMA_WRITE ? 61062306a36Sopenharmony_ci "write" : "read", 61162306a36Sopenharmony_ci tag, 61262306a36Sopenharmony_ci fail_reason != NULL ? 61362306a36Sopenharmony_ci fail_reason : "unknown"); 61462306a36Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_MEDIUM); 61562306a36Sopenharmony_ci continue; 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci /* 62062306a36Sopenharmony_ci * First check if this command has 62162306a36Sopenharmony_ci * exceeded its retries. 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci if (reissue && (cmd->retries-- > 0)) { 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci set_bit(tag, tagaccum); 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci /* Re-issue the command. */ 62862306a36Sopenharmony_ci mtip_issue_ncq_command(port, tag); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci continue; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Retire a command that will not be reissued */ 63462306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 63562306a36Sopenharmony_ci "retiring tag %d\n", tag); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci mtip_complete_command(cmd, BLK_STS_IOERR); 63862306a36Sopenharmony_ci } 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci print_tags(dd, "reissued (TFE)", tagaccum, cmd_cnt); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci/* 64462306a36Sopenharmony_ci * Handle a set device bits interrupt 64562306a36Sopenharmony_ci */ 64662306a36Sopenharmony_cistatic inline void mtip_workq_sdbfx(struct mtip_port *port, int group, 64762306a36Sopenharmony_ci u32 completed) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct driver_data *dd = port->dd; 65062306a36Sopenharmony_ci int tag, bit; 65162306a36Sopenharmony_ci struct mtip_cmd *command; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci if (!completed) { 65462306a36Sopenharmony_ci WARN_ON_ONCE(!completed); 65562306a36Sopenharmony_ci return; 65662306a36Sopenharmony_ci } 65762306a36Sopenharmony_ci /* clear completed status register in the hardware.*/ 65862306a36Sopenharmony_ci writel(completed, port->completed[group]); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci /* Process completed commands. */ 66162306a36Sopenharmony_ci for (bit = 0; (bit < 32) && completed; bit++) { 66262306a36Sopenharmony_ci if (completed & 0x01) { 66362306a36Sopenharmony_ci tag = (group << 5) | bit; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci /* skip internal command slot. */ 66662306a36Sopenharmony_ci if (unlikely(tag == MTIP_TAG_INTERNAL)) 66762306a36Sopenharmony_ci continue; 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci command = mtip_cmd_from_tag(dd, tag); 67062306a36Sopenharmony_ci mtip_complete_command(command, 0); 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci completed >>= 1; 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci /* If last, re-enable interrupts */ 67662306a36Sopenharmony_ci if (atomic_dec_return(&dd->irq_workers_active) == 0) 67762306a36Sopenharmony_ci writel(0xffffffff, dd->mmio + HOST_IRQ_STAT); 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci/* 68162306a36Sopenharmony_ci * Process legacy pio and d2h interrupts 68262306a36Sopenharmony_ci */ 68362306a36Sopenharmony_cistatic inline void mtip_process_legacy(struct driver_data *dd, u32 port_stat) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci struct mtip_port *port = dd->port; 68662306a36Sopenharmony_ci struct mtip_cmd *cmd = mtip_cmd_from_tag(dd, MTIP_TAG_INTERNAL); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if (test_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags) && cmd) { 68962306a36Sopenharmony_ci int group = MTIP_TAG_INDEX(MTIP_TAG_INTERNAL); 69062306a36Sopenharmony_ci int status = readl(port->cmd_issue[group]); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci if (!(status & (1 << MTIP_TAG_BIT(MTIP_TAG_INTERNAL)))) 69362306a36Sopenharmony_ci mtip_complete_command(cmd, 0); 69462306a36Sopenharmony_ci } 69562306a36Sopenharmony_ci} 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci/* 69862306a36Sopenharmony_ci * Demux and handle errors 69962306a36Sopenharmony_ci */ 70062306a36Sopenharmony_cistatic inline void mtip_process_errors(struct driver_data *dd, u32 port_stat) 70162306a36Sopenharmony_ci{ 70262306a36Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_CONNECT)) { 70362306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 70462306a36Sopenharmony_ci "Clearing PxSERR.DIAG.x\n"); 70562306a36Sopenharmony_ci writel((1 << 26), dd->port->mmio + PORT_SCR_ERR); 70662306a36Sopenharmony_ci } 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_PHYRDY)) { 70962306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 71062306a36Sopenharmony_ci "Clearing PxSERR.DIAG.n\n"); 71162306a36Sopenharmony_ci writel((1 << 16), dd->port->mmio + PORT_SCR_ERR); 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (unlikely(port_stat & ~PORT_IRQ_HANDLED)) { 71562306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 71662306a36Sopenharmony_ci "Port stat errors %x unhandled\n", 71762306a36Sopenharmony_ci (port_stat & ~PORT_IRQ_HANDLED)); 71862306a36Sopenharmony_ci if (mtip_check_surprise_removal(dd)) 71962306a36Sopenharmony_ci return; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci if (likely(port_stat & (PORT_IRQ_TF_ERR | PORT_IRQ_IF_ERR))) { 72262306a36Sopenharmony_ci set_bit(MTIP_PF_EH_ACTIVE_BIT, &dd->port->flags); 72362306a36Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 72462306a36Sopenharmony_ci } 72562306a36Sopenharmony_ci} 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_cistatic inline irqreturn_t mtip_handle_irq(struct driver_data *data) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci struct driver_data *dd = (struct driver_data *) data; 73062306a36Sopenharmony_ci struct mtip_port *port = dd->port; 73162306a36Sopenharmony_ci u32 hba_stat, port_stat; 73262306a36Sopenharmony_ci int rv = IRQ_NONE; 73362306a36Sopenharmony_ci int do_irq_enable = 1, i, workers; 73462306a36Sopenharmony_ci struct mtip_work *twork; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci hba_stat = readl(dd->mmio + HOST_IRQ_STAT); 73762306a36Sopenharmony_ci if (hba_stat) { 73862306a36Sopenharmony_ci rv = IRQ_HANDLED; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* Acknowledge the interrupt status on the port.*/ 74162306a36Sopenharmony_ci port_stat = readl(port->mmio + PORT_IRQ_STAT); 74262306a36Sopenharmony_ci if (unlikely(port_stat == 0xFFFFFFFF)) { 74362306a36Sopenharmony_ci mtip_check_surprise_removal(dd); 74462306a36Sopenharmony_ci return IRQ_HANDLED; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci writel(port_stat, port->mmio + PORT_IRQ_STAT); 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci /* Demux port status */ 74962306a36Sopenharmony_ci if (likely(port_stat & PORT_IRQ_SDB_FIS)) { 75062306a36Sopenharmony_ci do_irq_enable = 0; 75162306a36Sopenharmony_ci WARN_ON_ONCE(atomic_read(&dd->irq_workers_active) != 0); 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci /* Start at 1: group zero is always local? */ 75462306a36Sopenharmony_ci for (i = 0, workers = 0; i < MTIP_MAX_SLOT_GROUPS; 75562306a36Sopenharmony_ci i++) { 75662306a36Sopenharmony_ci twork = &dd->work[i]; 75762306a36Sopenharmony_ci twork->completed = readl(port->completed[i]); 75862306a36Sopenharmony_ci if (twork->completed) 75962306a36Sopenharmony_ci workers++; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci atomic_set(&dd->irq_workers_active, workers); 76362306a36Sopenharmony_ci if (workers) { 76462306a36Sopenharmony_ci for (i = 1; i < MTIP_MAX_SLOT_GROUPS; i++) { 76562306a36Sopenharmony_ci twork = &dd->work[i]; 76662306a36Sopenharmony_ci if (twork->completed) 76762306a36Sopenharmony_ci queue_work_on( 76862306a36Sopenharmony_ci twork->cpu_binding, 76962306a36Sopenharmony_ci dd->isr_workq, 77062306a36Sopenharmony_ci &twork->work); 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci if (likely(dd->work[0].completed)) 77462306a36Sopenharmony_ci mtip_workq_sdbfx(port, 0, 77562306a36Sopenharmony_ci dd->work[0].completed); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci } else { 77862306a36Sopenharmony_ci /* 77962306a36Sopenharmony_ci * Chip quirk: SDB interrupt but nothing 78062306a36Sopenharmony_ci * to complete 78162306a36Sopenharmony_ci */ 78262306a36Sopenharmony_ci do_irq_enable = 1; 78362306a36Sopenharmony_ci } 78462306a36Sopenharmony_ci } 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_ERR)) { 78762306a36Sopenharmony_ci if (unlikely(mtip_check_surprise_removal(dd))) { 78862306a36Sopenharmony_ci /* don't proceed further */ 78962306a36Sopenharmony_ci return IRQ_HANDLED; 79062306a36Sopenharmony_ci } 79162306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 79262306a36Sopenharmony_ci &dd->dd_flag)) 79362306a36Sopenharmony_ci return rv; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci mtip_process_errors(dd, port_stat & PORT_IRQ_ERR); 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (unlikely(port_stat & PORT_IRQ_LEGACY)) 79962306a36Sopenharmony_ci mtip_process_legacy(dd, port_stat & PORT_IRQ_LEGACY); 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci /* acknowledge interrupt */ 80362306a36Sopenharmony_ci if (unlikely(do_irq_enable)) 80462306a36Sopenharmony_ci writel(hba_stat, dd->mmio + HOST_IRQ_STAT); 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return rv; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci/* 81062306a36Sopenharmony_ci * HBA interrupt subroutine. 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * @irq IRQ number. 81362306a36Sopenharmony_ci * @instance Pointer to the driver data structure. 81462306a36Sopenharmony_ci * 81562306a36Sopenharmony_ci * return value 81662306a36Sopenharmony_ci * IRQ_HANDLED A HBA interrupt was pending and handled. 81762306a36Sopenharmony_ci * IRQ_NONE This interrupt was not for the HBA. 81862306a36Sopenharmony_ci */ 81962306a36Sopenharmony_cistatic irqreturn_t mtip_irq_handler(int irq, void *instance) 82062306a36Sopenharmony_ci{ 82162306a36Sopenharmony_ci struct driver_data *dd = instance; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci return mtip_handle_irq(dd); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_cistatic void mtip_issue_non_ncq_command(struct mtip_port *port, int tag) 82762306a36Sopenharmony_ci{ 82862306a36Sopenharmony_ci writel(1 << MTIP_TAG_BIT(tag), port->cmd_issue[MTIP_TAG_INDEX(tag)]); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic bool mtip_pause_ncq(struct mtip_port *port, 83262306a36Sopenharmony_ci struct host_to_dev_fis *fis) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci unsigned long task_file_data; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci task_file_data = readl(port->mmio+PORT_TFDATA); 83762306a36Sopenharmony_ci if ((task_file_data & 1)) 83862306a36Sopenharmony_ci return false; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci if (fis->command == ATA_CMD_SEC_ERASE_PREP) { 84162306a36Sopenharmony_ci port->ic_pause_timer = jiffies; 84262306a36Sopenharmony_ci return true; 84362306a36Sopenharmony_ci } else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) && 84462306a36Sopenharmony_ci (fis->features == 0x03)) { 84562306a36Sopenharmony_ci set_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); 84662306a36Sopenharmony_ci port->ic_pause_timer = jiffies; 84762306a36Sopenharmony_ci return true; 84862306a36Sopenharmony_ci } else if ((fis->command == ATA_CMD_SEC_ERASE_UNIT) || 84962306a36Sopenharmony_ci ((fis->command == 0xFC) && 85062306a36Sopenharmony_ci (fis->features == 0x27 || fis->features == 0x72 || 85162306a36Sopenharmony_ci fis->features == 0x62 || fis->features == 0x26))) { 85262306a36Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 85362306a36Sopenharmony_ci clear_bit(MTIP_DDF_REBUILD_FAILED_BIT, &port->dd->dd_flag); 85462306a36Sopenharmony_ci /* Com reset after secure erase or lowlevel format */ 85562306a36Sopenharmony_ci mtip_restart_port(port); 85662306a36Sopenharmony_ci clear_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); 85762306a36Sopenharmony_ci return false; 85862306a36Sopenharmony_ci } 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci return false; 86162306a36Sopenharmony_ci} 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_cistatic bool mtip_commands_active(struct mtip_port *port) 86462306a36Sopenharmony_ci{ 86562306a36Sopenharmony_ci unsigned int active; 86662306a36Sopenharmony_ci unsigned int n; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci /* 86962306a36Sopenharmony_ci * Ignore s_active bit 0 of array element 0. 87062306a36Sopenharmony_ci * This bit will always be set 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_ci active = readl(port->s_active[0]) & 0xFFFFFFFE; 87362306a36Sopenharmony_ci for (n = 1; n < port->dd->slot_groups; n++) 87462306a36Sopenharmony_ci active |= readl(port->s_active[n]); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return active != 0; 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_ci/* 88062306a36Sopenharmony_ci * Wait for port to quiesce 88162306a36Sopenharmony_ci * 88262306a36Sopenharmony_ci * @port Pointer to port data structure 88362306a36Sopenharmony_ci * @timeout Max duration to wait (ms) 88462306a36Sopenharmony_ci * 88562306a36Sopenharmony_ci * return value 88662306a36Sopenharmony_ci * 0 Success 88762306a36Sopenharmony_ci * -EBUSY Commands still active 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_cistatic int mtip_quiesce_io(struct mtip_port *port, unsigned long timeout) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci unsigned long to; 89262306a36Sopenharmony_ci bool active = true; 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci blk_mq_quiesce_queue(port->dd->queue); 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci to = jiffies + msecs_to_jiffies(timeout); 89762306a36Sopenharmony_ci do { 89862306a36Sopenharmony_ci if (test_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags) && 89962306a36Sopenharmony_ci test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { 90062306a36Sopenharmony_ci msleep(20); 90162306a36Sopenharmony_ci continue; /* svc thd is actively issuing commands */ 90262306a36Sopenharmony_ci } 90362306a36Sopenharmony_ci 90462306a36Sopenharmony_ci msleep(100); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci if (mtip_check_surprise_removal(port->dd)) 90762306a36Sopenharmony_ci goto err_fault; 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci active = mtip_commands_active(port); 91062306a36Sopenharmony_ci if (!active) 91162306a36Sopenharmony_ci break; 91262306a36Sopenharmony_ci } while (time_before(jiffies, to)); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci blk_mq_unquiesce_queue(port->dd->queue); 91562306a36Sopenharmony_ci return active ? -EBUSY : 0; 91662306a36Sopenharmony_cierr_fault: 91762306a36Sopenharmony_ci blk_mq_unquiesce_queue(port->dd->queue); 91862306a36Sopenharmony_ci return -EFAULT; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistruct mtip_int_cmd { 92262306a36Sopenharmony_ci int fis_len; 92362306a36Sopenharmony_ci dma_addr_t buffer; 92462306a36Sopenharmony_ci int buf_len; 92562306a36Sopenharmony_ci u32 opts; 92662306a36Sopenharmony_ci}; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/* 92962306a36Sopenharmony_ci * Execute an internal command and wait for the completion. 93062306a36Sopenharmony_ci * 93162306a36Sopenharmony_ci * @port Pointer to the port data structure. 93262306a36Sopenharmony_ci * @fis Pointer to the FIS that describes the command. 93362306a36Sopenharmony_ci * @fis_len Length in WORDS of the FIS. 93462306a36Sopenharmony_ci * @buffer DMA accessible for command data. 93562306a36Sopenharmony_ci * @buf_len Length, in bytes, of the data buffer. 93662306a36Sopenharmony_ci * @opts Command header options, excluding the FIS length 93762306a36Sopenharmony_ci * and the number of PRD entries. 93862306a36Sopenharmony_ci * @timeout Time in ms to wait for the command to complete. 93962306a36Sopenharmony_ci * 94062306a36Sopenharmony_ci * return value 94162306a36Sopenharmony_ci * 0 Command completed successfully. 94262306a36Sopenharmony_ci * -EFAULT The buffer address is not correctly aligned. 94362306a36Sopenharmony_ci * -EBUSY Internal command or other IO in progress. 94462306a36Sopenharmony_ci * -EAGAIN Time out waiting for command to complete. 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_cistatic int mtip_exec_internal_command(struct mtip_port *port, 94762306a36Sopenharmony_ci struct host_to_dev_fis *fis, 94862306a36Sopenharmony_ci int fis_len, 94962306a36Sopenharmony_ci dma_addr_t buffer, 95062306a36Sopenharmony_ci int buf_len, 95162306a36Sopenharmony_ci u32 opts, 95262306a36Sopenharmony_ci unsigned long timeout) 95362306a36Sopenharmony_ci{ 95462306a36Sopenharmony_ci struct mtip_cmd *int_cmd; 95562306a36Sopenharmony_ci struct driver_data *dd = port->dd; 95662306a36Sopenharmony_ci struct request *rq; 95762306a36Sopenharmony_ci struct mtip_int_cmd icmd = { 95862306a36Sopenharmony_ci .fis_len = fis_len, 95962306a36Sopenharmony_ci .buffer = buffer, 96062306a36Sopenharmony_ci .buf_len = buf_len, 96162306a36Sopenharmony_ci .opts = opts 96262306a36Sopenharmony_ci }; 96362306a36Sopenharmony_ci int rv = 0; 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci /* Make sure the buffer is 8 byte aligned. This is asic specific. */ 96662306a36Sopenharmony_ci if (buffer & 0x00000007) { 96762306a36Sopenharmony_ci dev_err(&dd->pdev->dev, "SG buffer is not 8 byte aligned\n"); 96862306a36Sopenharmony_ci return -EFAULT; 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (mtip_check_surprise_removal(dd)) 97262306a36Sopenharmony_ci return -EFAULT; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci rq = blk_mq_alloc_request(dd->queue, REQ_OP_DRV_IN, BLK_MQ_REQ_RESERVED); 97562306a36Sopenharmony_ci if (IS_ERR(rq)) { 97662306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME "Unable to allocate tag for PIO cmd\n"); 97762306a36Sopenharmony_ci return -EFAULT; 97862306a36Sopenharmony_ci } 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci set_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (fis->command == ATA_CMD_SEC_ERASE_PREP) 98362306a36Sopenharmony_ci set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci clear_bit(MTIP_PF_DM_ACTIVE_BIT, &port->flags); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (fis->command != ATA_CMD_STANDBYNOW1) { 98862306a36Sopenharmony_ci /* wait for io to complete if non atomic */ 98962306a36Sopenharmony_ci if (mtip_quiesce_io(port, MTIP_QUIESCE_IO_TIMEOUT_MS) < 0) { 99062306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, "Failed to quiesce IO\n"); 99162306a36Sopenharmony_ci blk_mq_free_request(rq); 99262306a36Sopenharmony_ci clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 99362306a36Sopenharmony_ci wake_up_interruptible(&port->svc_wait); 99462306a36Sopenharmony_ci return -EBUSY; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Copy the command to the command table */ 99962306a36Sopenharmony_ci int_cmd = blk_mq_rq_to_pdu(rq); 100062306a36Sopenharmony_ci int_cmd->icmd = &icmd; 100162306a36Sopenharmony_ci memcpy(int_cmd->command, fis, fis_len*4); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci rq->timeout = timeout; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci /* insert request and run queue */ 100662306a36Sopenharmony_ci blk_execute_rq(rq, true); 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (int_cmd->status) { 100962306a36Sopenharmony_ci dev_err(&dd->pdev->dev, "Internal command [%02X] failed %d\n", 101062306a36Sopenharmony_ci fis->command, int_cmd->status); 101162306a36Sopenharmony_ci rv = -EIO; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if (mtip_check_surprise_removal(dd) || 101462306a36Sopenharmony_ci test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 101562306a36Sopenharmony_ci &dd->dd_flag)) { 101662306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 101762306a36Sopenharmony_ci "Internal command [%02X] wait returned due to SR\n", 101862306a36Sopenharmony_ci fis->command); 101962306a36Sopenharmony_ci rv = -ENXIO; 102062306a36Sopenharmony_ci goto exec_ic_exit; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci mtip_device_reset(dd); /* recover from timeout issue */ 102362306a36Sopenharmony_ci rv = -EAGAIN; 102462306a36Sopenharmony_ci goto exec_ic_exit; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (readl(port->cmd_issue[MTIP_TAG_INDEX(MTIP_TAG_INTERNAL)]) 102862306a36Sopenharmony_ci & (1 << MTIP_TAG_BIT(MTIP_TAG_INTERNAL))) { 102962306a36Sopenharmony_ci rv = -ENXIO; 103062306a36Sopenharmony_ci if (!test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) { 103162306a36Sopenharmony_ci mtip_device_reset(dd); 103262306a36Sopenharmony_ci rv = -EAGAIN; 103362306a36Sopenharmony_ci } 103462306a36Sopenharmony_ci } 103562306a36Sopenharmony_ciexec_ic_exit: 103662306a36Sopenharmony_ci /* Clear the allocated and active bits for the internal command. */ 103762306a36Sopenharmony_ci blk_mq_free_request(rq); 103862306a36Sopenharmony_ci clear_bit(MTIP_PF_IC_ACTIVE_BIT, &port->flags); 103962306a36Sopenharmony_ci if (rv >= 0 && mtip_pause_ncq(port, fis)) { 104062306a36Sopenharmony_ci /* NCQ paused */ 104162306a36Sopenharmony_ci return rv; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci wake_up_interruptible(&port->svc_wait); 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci return rv; 104662306a36Sopenharmony_ci} 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci/* 104962306a36Sopenharmony_ci * Byte-swap ATA ID strings. 105062306a36Sopenharmony_ci * 105162306a36Sopenharmony_ci * ATA identify data contains strings in byte-swapped 16-bit words. 105262306a36Sopenharmony_ci * They must be swapped (on all architectures) to be usable as C strings. 105362306a36Sopenharmony_ci * This function swaps bytes in-place. 105462306a36Sopenharmony_ci * 105562306a36Sopenharmony_ci * @buf The buffer location of the string 105662306a36Sopenharmony_ci * @len The number of bytes to swap 105762306a36Sopenharmony_ci * 105862306a36Sopenharmony_ci * return value 105962306a36Sopenharmony_ci * None 106062306a36Sopenharmony_ci */ 106162306a36Sopenharmony_cistatic inline void ata_swap_string(u16 *buf, unsigned int len) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci int i; 106462306a36Sopenharmony_ci for (i = 0; i < (len/2); i++) 106562306a36Sopenharmony_ci be16_to_cpus(&buf[i]); 106662306a36Sopenharmony_ci} 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_cistatic void mtip_set_timeout(struct driver_data *dd, 106962306a36Sopenharmony_ci struct host_to_dev_fis *fis, 107062306a36Sopenharmony_ci unsigned int *timeout, u8 erasemode) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci switch (fis->command) { 107362306a36Sopenharmony_ci case ATA_CMD_DOWNLOAD_MICRO: 107462306a36Sopenharmony_ci *timeout = 120000; /* 2 minutes */ 107562306a36Sopenharmony_ci break; 107662306a36Sopenharmony_ci case ATA_CMD_SEC_ERASE_UNIT: 107762306a36Sopenharmony_ci case 0xFC: 107862306a36Sopenharmony_ci if (erasemode) 107962306a36Sopenharmony_ci *timeout = ((*(dd->port->identify + 90) * 2) * 60000); 108062306a36Sopenharmony_ci else 108162306a36Sopenharmony_ci *timeout = ((*(dd->port->identify + 89) * 2) * 60000); 108262306a36Sopenharmony_ci break; 108362306a36Sopenharmony_ci case ATA_CMD_STANDBYNOW1: 108462306a36Sopenharmony_ci *timeout = 120000; /* 2 minutes */ 108562306a36Sopenharmony_ci break; 108662306a36Sopenharmony_ci case 0xF7: 108762306a36Sopenharmony_ci case 0xFA: 108862306a36Sopenharmony_ci *timeout = 60000; /* 60 seconds */ 108962306a36Sopenharmony_ci break; 109062306a36Sopenharmony_ci case ATA_CMD_SMART: 109162306a36Sopenharmony_ci *timeout = 15000; /* 15 seconds */ 109262306a36Sopenharmony_ci break; 109362306a36Sopenharmony_ci default: 109462306a36Sopenharmony_ci *timeout = MTIP_IOCTL_CMD_TIMEOUT_MS; 109562306a36Sopenharmony_ci break; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci} 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci/* 110062306a36Sopenharmony_ci * Request the device identity information. 110162306a36Sopenharmony_ci * 110262306a36Sopenharmony_ci * If a user space buffer is not specified, i.e. is NULL, the 110362306a36Sopenharmony_ci * identify information is still read from the drive and placed 110462306a36Sopenharmony_ci * into the identify data buffer (@e port->identify) in the 110562306a36Sopenharmony_ci * port data structure. 110662306a36Sopenharmony_ci * When the identify buffer contains valid identify information @e 110762306a36Sopenharmony_ci * port->identify_valid is non-zero. 110862306a36Sopenharmony_ci * 110962306a36Sopenharmony_ci * @port Pointer to the port structure. 111062306a36Sopenharmony_ci * @user_buffer A user space buffer where the identify data should be 111162306a36Sopenharmony_ci * copied. 111262306a36Sopenharmony_ci * 111362306a36Sopenharmony_ci * return value 111462306a36Sopenharmony_ci * 0 Command completed successfully. 111562306a36Sopenharmony_ci * -EFAULT An error occurred while coping data to the user buffer. 111662306a36Sopenharmony_ci * -1 Command failed. 111762306a36Sopenharmony_ci */ 111862306a36Sopenharmony_cistatic int mtip_get_identify(struct mtip_port *port, void __user *user_buffer) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci int rv = 0; 112162306a36Sopenharmony_ci struct host_to_dev_fis fis; 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &port->dd->dd_flag)) 112462306a36Sopenharmony_ci return -EFAULT; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci /* Build the FIS. */ 112762306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 112862306a36Sopenharmony_ci fis.type = 0x27; 112962306a36Sopenharmony_ci fis.opts = 1 << 7; 113062306a36Sopenharmony_ci fis.command = ATA_CMD_ID_ATA; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci /* Set the identify information as invalid. */ 113362306a36Sopenharmony_ci port->identify_valid = 0; 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci /* Clear the identify information. */ 113662306a36Sopenharmony_ci memset(port->identify, 0, sizeof(u16) * ATA_ID_WORDS); 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci /* Execute the command. */ 113962306a36Sopenharmony_ci if (mtip_exec_internal_command(port, 114062306a36Sopenharmony_ci &fis, 114162306a36Sopenharmony_ci 5, 114262306a36Sopenharmony_ci port->identify_dma, 114362306a36Sopenharmony_ci sizeof(u16) * ATA_ID_WORDS, 114462306a36Sopenharmony_ci 0, 114562306a36Sopenharmony_ci MTIP_INT_CMD_TIMEOUT_MS) 114662306a36Sopenharmony_ci < 0) { 114762306a36Sopenharmony_ci rv = -1; 114862306a36Sopenharmony_ci goto out; 114962306a36Sopenharmony_ci } 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_ci /* 115262306a36Sopenharmony_ci * Perform any necessary byte-swapping. Yes, the kernel does in fact 115362306a36Sopenharmony_ci * perform field-sensitive swapping on the string fields. 115462306a36Sopenharmony_ci * See the kernel use of ata_id_string() for proof of this. 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci#ifdef __LITTLE_ENDIAN 115762306a36Sopenharmony_ci ata_swap_string(port->identify + 27, 40); /* model string*/ 115862306a36Sopenharmony_ci ata_swap_string(port->identify + 23, 8); /* firmware string*/ 115962306a36Sopenharmony_ci ata_swap_string(port->identify + 10, 20); /* serial# string*/ 116062306a36Sopenharmony_ci#else 116162306a36Sopenharmony_ci { 116262306a36Sopenharmony_ci int i; 116362306a36Sopenharmony_ci for (i = 0; i < ATA_ID_WORDS; i++) 116462306a36Sopenharmony_ci port->identify[i] = le16_to_cpu(port->identify[i]); 116562306a36Sopenharmony_ci } 116662306a36Sopenharmony_ci#endif 116762306a36Sopenharmony_ci 116862306a36Sopenharmony_ci /* Check security locked state */ 116962306a36Sopenharmony_ci if (port->identify[128] & 0x4) 117062306a36Sopenharmony_ci set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 117162306a36Sopenharmony_ci else 117262306a36Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci /* Set the identify buffer as valid. */ 117562306a36Sopenharmony_ci port->identify_valid = 1; 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci if (user_buffer) { 117862306a36Sopenharmony_ci if (copy_to_user( 117962306a36Sopenharmony_ci user_buffer, 118062306a36Sopenharmony_ci port->identify, 118162306a36Sopenharmony_ci ATA_ID_WORDS * sizeof(u16))) { 118262306a36Sopenharmony_ci rv = -EFAULT; 118362306a36Sopenharmony_ci goto out; 118462306a36Sopenharmony_ci } 118562306a36Sopenharmony_ci } 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ciout: 118862306a36Sopenharmony_ci return rv; 118962306a36Sopenharmony_ci} 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci/* 119262306a36Sopenharmony_ci * Issue a standby immediate command to the device. 119362306a36Sopenharmony_ci * 119462306a36Sopenharmony_ci * @port Pointer to the port structure. 119562306a36Sopenharmony_ci * 119662306a36Sopenharmony_ci * return value 119762306a36Sopenharmony_ci * 0 Command was executed successfully. 119862306a36Sopenharmony_ci * -1 An error occurred while executing the command. 119962306a36Sopenharmony_ci */ 120062306a36Sopenharmony_cistatic int mtip_standby_immediate(struct mtip_port *port) 120162306a36Sopenharmony_ci{ 120262306a36Sopenharmony_ci int rv; 120362306a36Sopenharmony_ci struct host_to_dev_fis fis; 120462306a36Sopenharmony_ci unsigned long __maybe_unused start; 120562306a36Sopenharmony_ci unsigned int timeout; 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci /* Build the FIS. */ 120862306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 120962306a36Sopenharmony_ci fis.type = 0x27; 121062306a36Sopenharmony_ci fis.opts = 1 << 7; 121162306a36Sopenharmony_ci fis.command = ATA_CMD_STANDBYNOW1; 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &timeout, 0); 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_ci start = jiffies; 121662306a36Sopenharmony_ci rv = mtip_exec_internal_command(port, 121762306a36Sopenharmony_ci &fis, 121862306a36Sopenharmony_ci 5, 121962306a36Sopenharmony_ci 0, 122062306a36Sopenharmony_ci 0, 122162306a36Sopenharmony_ci 0, 122262306a36Sopenharmony_ci timeout); 122362306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME "Time taken to complete standby cmd: %d ms\n", 122462306a36Sopenharmony_ci jiffies_to_msecs(jiffies - start)); 122562306a36Sopenharmony_ci if (rv) 122662306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 122762306a36Sopenharmony_ci "STANDBY IMMEDIATE command failed.\n"); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci return rv; 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci/* 123362306a36Sopenharmony_ci * Issue a READ LOG EXT command to the device. 123462306a36Sopenharmony_ci * 123562306a36Sopenharmony_ci * @port pointer to the port structure. 123662306a36Sopenharmony_ci * @page page number to fetch 123762306a36Sopenharmony_ci * @buffer pointer to buffer 123862306a36Sopenharmony_ci * @buffer_dma dma address corresponding to @buffer 123962306a36Sopenharmony_ci * @sectors page length to fetch, in sectors 124062306a36Sopenharmony_ci * 124162306a36Sopenharmony_ci * return value 124262306a36Sopenharmony_ci * @rv return value from mtip_exec_internal_command() 124362306a36Sopenharmony_ci */ 124462306a36Sopenharmony_cistatic int mtip_read_log_page(struct mtip_port *port, u8 page, u16 *buffer, 124562306a36Sopenharmony_ci dma_addr_t buffer_dma, unsigned int sectors) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct host_to_dev_fis fis; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 125062306a36Sopenharmony_ci fis.type = 0x27; 125162306a36Sopenharmony_ci fis.opts = 1 << 7; 125262306a36Sopenharmony_ci fis.command = ATA_CMD_READ_LOG_EXT; 125362306a36Sopenharmony_ci fis.sect_count = sectors & 0xFF; 125462306a36Sopenharmony_ci fis.sect_cnt_ex = (sectors >> 8) & 0xFF; 125562306a36Sopenharmony_ci fis.lba_low = page; 125662306a36Sopenharmony_ci fis.lba_mid = 0; 125762306a36Sopenharmony_ci fis.device = ATA_DEVICE_OBS; 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci memset(buffer, 0, sectors * ATA_SECT_SIZE); 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci return mtip_exec_internal_command(port, 126262306a36Sopenharmony_ci &fis, 126362306a36Sopenharmony_ci 5, 126462306a36Sopenharmony_ci buffer_dma, 126562306a36Sopenharmony_ci sectors * ATA_SECT_SIZE, 126662306a36Sopenharmony_ci 0, 126762306a36Sopenharmony_ci MTIP_INT_CMD_TIMEOUT_MS); 126862306a36Sopenharmony_ci} 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci/* 127162306a36Sopenharmony_ci * Issue a SMART READ DATA command to the device. 127262306a36Sopenharmony_ci * 127362306a36Sopenharmony_ci * @port pointer to the port structure. 127462306a36Sopenharmony_ci * @buffer pointer to buffer 127562306a36Sopenharmony_ci * @buffer_dma dma address corresponding to @buffer 127662306a36Sopenharmony_ci * 127762306a36Sopenharmony_ci * return value 127862306a36Sopenharmony_ci * @rv return value from mtip_exec_internal_command() 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_cistatic int mtip_get_smart_data(struct mtip_port *port, u8 *buffer, 128162306a36Sopenharmony_ci dma_addr_t buffer_dma) 128262306a36Sopenharmony_ci{ 128362306a36Sopenharmony_ci struct host_to_dev_fis fis; 128462306a36Sopenharmony_ci 128562306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 128662306a36Sopenharmony_ci fis.type = 0x27; 128762306a36Sopenharmony_ci fis.opts = 1 << 7; 128862306a36Sopenharmony_ci fis.command = ATA_CMD_SMART; 128962306a36Sopenharmony_ci fis.features = 0xD0; 129062306a36Sopenharmony_ci fis.sect_count = 1; 129162306a36Sopenharmony_ci fis.lba_mid = 0x4F; 129262306a36Sopenharmony_ci fis.lba_hi = 0xC2; 129362306a36Sopenharmony_ci fis.device = ATA_DEVICE_OBS; 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci return mtip_exec_internal_command(port, 129662306a36Sopenharmony_ci &fis, 129762306a36Sopenharmony_ci 5, 129862306a36Sopenharmony_ci buffer_dma, 129962306a36Sopenharmony_ci ATA_SECT_SIZE, 130062306a36Sopenharmony_ci 0, 130162306a36Sopenharmony_ci 15000); 130262306a36Sopenharmony_ci} 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci/* 130562306a36Sopenharmony_ci * Get the value of a smart attribute 130662306a36Sopenharmony_ci * 130762306a36Sopenharmony_ci * @port pointer to the port structure 130862306a36Sopenharmony_ci * @id attribute number 130962306a36Sopenharmony_ci * @attrib pointer to return attrib information corresponding to @id 131062306a36Sopenharmony_ci * 131162306a36Sopenharmony_ci * return value 131262306a36Sopenharmony_ci * -EINVAL NULL buffer passed or unsupported attribute @id. 131362306a36Sopenharmony_ci * -EPERM Identify data not valid, SMART not supported or not enabled 131462306a36Sopenharmony_ci */ 131562306a36Sopenharmony_cistatic int mtip_get_smart_attr(struct mtip_port *port, unsigned int id, 131662306a36Sopenharmony_ci struct smart_attr *attrib) 131762306a36Sopenharmony_ci{ 131862306a36Sopenharmony_ci int rv, i; 131962306a36Sopenharmony_ci struct smart_attr *pattr; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (!attrib) 132262306a36Sopenharmony_ci return -EINVAL; 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci if (!port->identify_valid) { 132562306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "IDENTIFY DATA not valid\n"); 132662306a36Sopenharmony_ci return -EPERM; 132762306a36Sopenharmony_ci } 132862306a36Sopenharmony_ci if (!(port->identify[82] & 0x1)) { 132962306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "SMART not supported\n"); 133062306a36Sopenharmony_ci return -EPERM; 133162306a36Sopenharmony_ci } 133262306a36Sopenharmony_ci if (!(port->identify[85] & 0x1)) { 133362306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "SMART not enabled\n"); 133462306a36Sopenharmony_ci return -EPERM; 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci memset(port->smart_buf, 0, ATA_SECT_SIZE); 133862306a36Sopenharmony_ci rv = mtip_get_smart_data(port, port->smart_buf, port->smart_buf_dma); 133962306a36Sopenharmony_ci if (rv) { 134062306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, "Failed to ge SMART data\n"); 134162306a36Sopenharmony_ci return rv; 134262306a36Sopenharmony_ci } 134362306a36Sopenharmony_ci 134462306a36Sopenharmony_ci pattr = (struct smart_attr *)(port->smart_buf + 2); 134562306a36Sopenharmony_ci for (i = 0; i < 29; i++, pattr++) 134662306a36Sopenharmony_ci if (pattr->attr_id == id) { 134762306a36Sopenharmony_ci memcpy(attrib, pattr, sizeof(struct smart_attr)); 134862306a36Sopenharmony_ci break; 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (i == 29) { 135262306a36Sopenharmony_ci dev_warn(&port->dd->pdev->dev, 135362306a36Sopenharmony_ci "Query for invalid SMART attribute ID\n"); 135462306a36Sopenharmony_ci rv = -EINVAL; 135562306a36Sopenharmony_ci } 135662306a36Sopenharmony_ci 135762306a36Sopenharmony_ci return rv; 135862306a36Sopenharmony_ci} 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci/* 136162306a36Sopenharmony_ci * Get the drive capacity. 136262306a36Sopenharmony_ci * 136362306a36Sopenharmony_ci * @dd Pointer to the device data structure. 136462306a36Sopenharmony_ci * @sectors Pointer to the variable that will receive the sector count. 136562306a36Sopenharmony_ci * 136662306a36Sopenharmony_ci * return value 136762306a36Sopenharmony_ci * 1 Capacity was returned successfully. 136862306a36Sopenharmony_ci * 0 The identify information is invalid. 136962306a36Sopenharmony_ci */ 137062306a36Sopenharmony_cistatic bool mtip_hw_get_capacity(struct driver_data *dd, sector_t *sectors) 137162306a36Sopenharmony_ci{ 137262306a36Sopenharmony_ci struct mtip_port *port = dd->port; 137362306a36Sopenharmony_ci u64 total, raw0, raw1, raw2, raw3; 137462306a36Sopenharmony_ci raw0 = port->identify[100]; 137562306a36Sopenharmony_ci raw1 = port->identify[101]; 137662306a36Sopenharmony_ci raw2 = port->identify[102]; 137762306a36Sopenharmony_ci raw3 = port->identify[103]; 137862306a36Sopenharmony_ci total = raw0 | raw1<<16 | raw2<<32 | raw3<<48; 137962306a36Sopenharmony_ci *sectors = total; 138062306a36Sopenharmony_ci return (bool) !!port->identify_valid; 138162306a36Sopenharmony_ci} 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci/* 138462306a36Sopenharmony_ci * Display the identify command data. 138562306a36Sopenharmony_ci * 138662306a36Sopenharmony_ci * @port Pointer to the port data structure. 138762306a36Sopenharmony_ci * 138862306a36Sopenharmony_ci * return value 138962306a36Sopenharmony_ci * None 139062306a36Sopenharmony_ci */ 139162306a36Sopenharmony_cistatic void mtip_dump_identify(struct mtip_port *port) 139262306a36Sopenharmony_ci{ 139362306a36Sopenharmony_ci sector_t sectors; 139462306a36Sopenharmony_ci unsigned short revid; 139562306a36Sopenharmony_ci char cbuf[42]; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci if (!port->identify_valid) 139862306a36Sopenharmony_ci return; 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_ci strscpy(cbuf, (char *)(port->identify + 10), 21); 140162306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, 140262306a36Sopenharmony_ci "Serial No.: %s\n", cbuf); 140362306a36Sopenharmony_ci 140462306a36Sopenharmony_ci strscpy(cbuf, (char *)(port->identify + 23), 9); 140562306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, 140662306a36Sopenharmony_ci "Firmware Ver.: %s\n", cbuf); 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci strscpy(cbuf, (char *)(port->identify + 27), 41); 140962306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, "Model: %s\n", cbuf); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, "Security: %04x %s\n", 141262306a36Sopenharmony_ci port->identify[128], 141362306a36Sopenharmony_ci port->identify[128] & 0x4 ? "(LOCKED)" : ""); 141462306a36Sopenharmony_ci 141562306a36Sopenharmony_ci if (mtip_hw_get_capacity(port->dd, §ors)) 141662306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, 141762306a36Sopenharmony_ci "Capacity: %llu sectors (%llu MB)\n", 141862306a36Sopenharmony_ci (u64)sectors, 141962306a36Sopenharmony_ci ((u64)sectors) * ATA_SECT_SIZE >> 20); 142062306a36Sopenharmony_ci 142162306a36Sopenharmony_ci pci_read_config_word(port->dd->pdev, PCI_REVISION_ID, &revid); 142262306a36Sopenharmony_ci switch (revid & 0xFF) { 142362306a36Sopenharmony_ci case 0x1: 142462306a36Sopenharmony_ci strscpy(cbuf, "A0", 3); 142562306a36Sopenharmony_ci break; 142662306a36Sopenharmony_ci case 0x3: 142762306a36Sopenharmony_ci strscpy(cbuf, "A2", 3); 142862306a36Sopenharmony_ci break; 142962306a36Sopenharmony_ci default: 143062306a36Sopenharmony_ci strscpy(cbuf, "?", 2); 143162306a36Sopenharmony_ci break; 143262306a36Sopenharmony_ci } 143362306a36Sopenharmony_ci dev_info(&port->dd->pdev->dev, 143462306a36Sopenharmony_ci "Card Type: %s\n", cbuf); 143562306a36Sopenharmony_ci} 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci/* 143862306a36Sopenharmony_ci * Map the commands scatter list into the command table. 143962306a36Sopenharmony_ci * 144062306a36Sopenharmony_ci * @command Pointer to the command. 144162306a36Sopenharmony_ci * @nents Number of scatter list entries. 144262306a36Sopenharmony_ci * 144362306a36Sopenharmony_ci * return value 144462306a36Sopenharmony_ci * None 144562306a36Sopenharmony_ci */ 144662306a36Sopenharmony_cistatic inline void fill_command_sg(struct driver_data *dd, 144762306a36Sopenharmony_ci struct mtip_cmd *command, 144862306a36Sopenharmony_ci int nents) 144962306a36Sopenharmony_ci{ 145062306a36Sopenharmony_ci int n; 145162306a36Sopenharmony_ci unsigned int dma_len; 145262306a36Sopenharmony_ci struct mtip_cmd_sg *command_sg; 145362306a36Sopenharmony_ci struct scatterlist *sg; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci command_sg = command->command + AHCI_CMD_TBL_HDR_SZ; 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_ci for_each_sg(command->sg, sg, nents, n) { 145862306a36Sopenharmony_ci dma_len = sg_dma_len(sg); 145962306a36Sopenharmony_ci if (dma_len > 0x400000) 146062306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 146162306a36Sopenharmony_ci "DMA segment length truncated\n"); 146262306a36Sopenharmony_ci command_sg->info = cpu_to_le32((dma_len-1) & 0x3FFFFF); 146362306a36Sopenharmony_ci command_sg->dba = cpu_to_le32(sg_dma_address(sg)); 146462306a36Sopenharmony_ci command_sg->dba_upper = 146562306a36Sopenharmony_ci cpu_to_le32((sg_dma_address(sg) >> 16) >> 16); 146662306a36Sopenharmony_ci command_sg++; 146762306a36Sopenharmony_ci } 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci/* 147162306a36Sopenharmony_ci * @brief Execute a drive command. 147262306a36Sopenharmony_ci * 147362306a36Sopenharmony_ci * return value 0 The command completed successfully. 147462306a36Sopenharmony_ci * return value -1 An error occurred while executing the command. 147562306a36Sopenharmony_ci */ 147662306a36Sopenharmony_cistatic int exec_drive_task(struct mtip_port *port, u8 *command) 147762306a36Sopenharmony_ci{ 147862306a36Sopenharmony_ci struct host_to_dev_fis fis; 147962306a36Sopenharmony_ci struct host_to_dev_fis *reply = (port->rxfis + RX_FIS_D2H_REG); 148062306a36Sopenharmony_ci unsigned int to; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci /* Build the FIS. */ 148362306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 148462306a36Sopenharmony_ci fis.type = 0x27; 148562306a36Sopenharmony_ci fis.opts = 1 << 7; 148662306a36Sopenharmony_ci fis.command = command[0]; 148762306a36Sopenharmony_ci fis.features = command[1]; 148862306a36Sopenharmony_ci fis.sect_count = command[2]; 148962306a36Sopenharmony_ci fis.sector = command[3]; 149062306a36Sopenharmony_ci fis.cyl_low = command[4]; 149162306a36Sopenharmony_ci fis.cyl_hi = command[5]; 149262306a36Sopenharmony_ci fis.device = command[6] & ~0x10; /* Clear the dev bit*/ 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &to, 0); 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " %s: User Command: cmd %x, feat %x, nsect %x, sect %x, lcyl %x, hcyl %x, sel %x\n", 149762306a36Sopenharmony_ci __func__, 149862306a36Sopenharmony_ci command[0], 149962306a36Sopenharmony_ci command[1], 150062306a36Sopenharmony_ci command[2], 150162306a36Sopenharmony_ci command[3], 150262306a36Sopenharmony_ci command[4], 150362306a36Sopenharmony_ci command[5], 150462306a36Sopenharmony_ci command[6]); 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* Execute the command. */ 150762306a36Sopenharmony_ci if (mtip_exec_internal_command(port, 150862306a36Sopenharmony_ci &fis, 150962306a36Sopenharmony_ci 5, 151062306a36Sopenharmony_ci 0, 151162306a36Sopenharmony_ci 0, 151262306a36Sopenharmony_ci 0, 151362306a36Sopenharmony_ci to) < 0) { 151462306a36Sopenharmony_ci return -1; 151562306a36Sopenharmony_ci } 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci command[0] = reply->command; /* Status*/ 151862306a36Sopenharmony_ci command[1] = reply->features; /* Error*/ 151962306a36Sopenharmony_ci command[4] = reply->cyl_low; 152062306a36Sopenharmony_ci command[5] = reply->cyl_hi; 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " %s: Completion Status: stat %x, err %x , cyl_lo %x cyl_hi %x\n", 152362306a36Sopenharmony_ci __func__, 152462306a36Sopenharmony_ci command[0], 152562306a36Sopenharmony_ci command[1], 152662306a36Sopenharmony_ci command[4], 152762306a36Sopenharmony_ci command[5]); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci return 0; 153062306a36Sopenharmony_ci} 153162306a36Sopenharmony_ci 153262306a36Sopenharmony_ci/* 153362306a36Sopenharmony_ci * @brief Execute a drive command. 153462306a36Sopenharmony_ci * 153562306a36Sopenharmony_ci * @param port Pointer to the port data structure. 153662306a36Sopenharmony_ci * @param command Pointer to the user specified command parameters. 153762306a36Sopenharmony_ci * @param user_buffer Pointer to the user space buffer where read sector 153862306a36Sopenharmony_ci * data should be copied. 153962306a36Sopenharmony_ci * 154062306a36Sopenharmony_ci * return value 0 The command completed successfully. 154162306a36Sopenharmony_ci * return value -EFAULT An error occurred while copying the completion 154262306a36Sopenharmony_ci * data to the user space buffer. 154362306a36Sopenharmony_ci * return value -1 An error occurred while executing the command. 154462306a36Sopenharmony_ci */ 154562306a36Sopenharmony_cistatic int exec_drive_command(struct mtip_port *port, u8 *command, 154662306a36Sopenharmony_ci void __user *user_buffer) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct host_to_dev_fis fis; 154962306a36Sopenharmony_ci struct host_to_dev_fis *reply; 155062306a36Sopenharmony_ci u8 *buf = NULL; 155162306a36Sopenharmony_ci dma_addr_t dma_addr = 0; 155262306a36Sopenharmony_ci int rv = 0, xfer_sz = command[3]; 155362306a36Sopenharmony_ci unsigned int to; 155462306a36Sopenharmony_ci 155562306a36Sopenharmony_ci if (xfer_sz) { 155662306a36Sopenharmony_ci if (!user_buffer) 155762306a36Sopenharmony_ci return -EFAULT; 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci buf = dma_alloc_coherent(&port->dd->pdev->dev, 156062306a36Sopenharmony_ci ATA_SECT_SIZE * xfer_sz, 156162306a36Sopenharmony_ci &dma_addr, 156262306a36Sopenharmony_ci GFP_KERNEL); 156362306a36Sopenharmony_ci if (!buf) { 156462306a36Sopenharmony_ci dev_err(&port->dd->pdev->dev, 156562306a36Sopenharmony_ci "Memory allocation failed (%d bytes)\n", 156662306a36Sopenharmony_ci ATA_SECT_SIZE * xfer_sz); 156762306a36Sopenharmony_ci return -ENOMEM; 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* Build the FIS. */ 157262306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 157362306a36Sopenharmony_ci fis.type = 0x27; 157462306a36Sopenharmony_ci fis.opts = 1 << 7; 157562306a36Sopenharmony_ci fis.command = command[0]; 157662306a36Sopenharmony_ci fis.features = command[2]; 157762306a36Sopenharmony_ci fis.sect_count = command[3]; 157862306a36Sopenharmony_ci if (fis.command == ATA_CMD_SMART) { 157962306a36Sopenharmony_ci fis.sector = command[1]; 158062306a36Sopenharmony_ci fis.cyl_low = 0x4F; 158162306a36Sopenharmony_ci fis.cyl_hi = 0xC2; 158262306a36Sopenharmony_ci } 158362306a36Sopenharmony_ci 158462306a36Sopenharmony_ci mtip_set_timeout(port->dd, &fis, &to, 0); 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci if (xfer_sz) 158762306a36Sopenharmony_ci reply = (port->rxfis + RX_FIS_PIO_SETUP); 158862306a36Sopenharmony_ci else 158962306a36Sopenharmony_ci reply = (port->rxfis + RX_FIS_D2H_REG); 159062306a36Sopenharmony_ci 159162306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 159262306a36Sopenharmony_ci " %s: User Command: cmd %x, sect %x, " 159362306a36Sopenharmony_ci "feat %x, sectcnt %x\n", 159462306a36Sopenharmony_ci __func__, 159562306a36Sopenharmony_ci command[0], 159662306a36Sopenharmony_ci command[1], 159762306a36Sopenharmony_ci command[2], 159862306a36Sopenharmony_ci command[3]); 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci /* Execute the command. */ 160162306a36Sopenharmony_ci if (mtip_exec_internal_command(port, 160262306a36Sopenharmony_ci &fis, 160362306a36Sopenharmony_ci 5, 160462306a36Sopenharmony_ci (xfer_sz ? dma_addr : 0), 160562306a36Sopenharmony_ci (xfer_sz ? ATA_SECT_SIZE * xfer_sz : 0), 160662306a36Sopenharmony_ci 0, 160762306a36Sopenharmony_ci to) 160862306a36Sopenharmony_ci < 0) { 160962306a36Sopenharmony_ci rv = -EFAULT; 161062306a36Sopenharmony_ci goto exit_drive_command; 161162306a36Sopenharmony_ci } 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci /* Collect the completion status. */ 161462306a36Sopenharmony_ci command[0] = reply->command; /* Status*/ 161562306a36Sopenharmony_ci command[1] = reply->features; /* Error*/ 161662306a36Sopenharmony_ci command[2] = reply->sect_count; 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 161962306a36Sopenharmony_ci " %s: Completion Status: stat %x, " 162062306a36Sopenharmony_ci "err %x, nsect %x\n", 162162306a36Sopenharmony_ci __func__, 162262306a36Sopenharmony_ci command[0], 162362306a36Sopenharmony_ci command[1], 162462306a36Sopenharmony_ci command[2]); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci if (xfer_sz) { 162762306a36Sopenharmony_ci if (copy_to_user(user_buffer, 162862306a36Sopenharmony_ci buf, 162962306a36Sopenharmony_ci ATA_SECT_SIZE * command[3])) { 163062306a36Sopenharmony_ci rv = -EFAULT; 163162306a36Sopenharmony_ci goto exit_drive_command; 163262306a36Sopenharmony_ci } 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ciexit_drive_command: 163562306a36Sopenharmony_ci if (buf) 163662306a36Sopenharmony_ci dma_free_coherent(&port->dd->pdev->dev, 163762306a36Sopenharmony_ci ATA_SECT_SIZE * xfer_sz, buf, dma_addr); 163862306a36Sopenharmony_ci return rv; 163962306a36Sopenharmony_ci} 164062306a36Sopenharmony_ci 164162306a36Sopenharmony_ci/* 164262306a36Sopenharmony_ci * Indicates whether a command has a single sector payload. 164362306a36Sopenharmony_ci * 164462306a36Sopenharmony_ci * @command passed to the device to perform the certain event. 164562306a36Sopenharmony_ci * @features passed to the device to perform the certain event. 164662306a36Sopenharmony_ci * 164762306a36Sopenharmony_ci * return value 164862306a36Sopenharmony_ci * 1 command is one that always has a single sector payload, 164962306a36Sopenharmony_ci * regardless of the value in the Sector Count field. 165062306a36Sopenharmony_ci * 0 otherwise 165162306a36Sopenharmony_ci * 165262306a36Sopenharmony_ci */ 165362306a36Sopenharmony_cistatic unsigned int implicit_sector(unsigned char command, 165462306a36Sopenharmony_ci unsigned char features) 165562306a36Sopenharmony_ci{ 165662306a36Sopenharmony_ci unsigned int rv = 0; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci /* list of commands that have an implicit sector count of 1 */ 165962306a36Sopenharmony_ci switch (command) { 166062306a36Sopenharmony_ci case ATA_CMD_SEC_SET_PASS: 166162306a36Sopenharmony_ci case ATA_CMD_SEC_UNLOCK: 166262306a36Sopenharmony_ci case ATA_CMD_SEC_ERASE_PREP: 166362306a36Sopenharmony_ci case ATA_CMD_SEC_ERASE_UNIT: 166462306a36Sopenharmony_ci case ATA_CMD_SEC_FREEZE_LOCK: 166562306a36Sopenharmony_ci case ATA_CMD_SEC_DISABLE_PASS: 166662306a36Sopenharmony_ci case ATA_CMD_PMP_READ: 166762306a36Sopenharmony_ci case ATA_CMD_PMP_WRITE: 166862306a36Sopenharmony_ci rv = 1; 166962306a36Sopenharmony_ci break; 167062306a36Sopenharmony_ci case ATA_CMD_SET_MAX: 167162306a36Sopenharmony_ci if (features == ATA_SET_MAX_UNLOCK) 167262306a36Sopenharmony_ci rv = 1; 167362306a36Sopenharmony_ci break; 167462306a36Sopenharmony_ci case ATA_CMD_SMART: 167562306a36Sopenharmony_ci if ((features == ATA_SMART_READ_VALUES) || 167662306a36Sopenharmony_ci (features == ATA_SMART_READ_THRESHOLDS)) 167762306a36Sopenharmony_ci rv = 1; 167862306a36Sopenharmony_ci break; 167962306a36Sopenharmony_ci case ATA_CMD_CONF_OVERLAY: 168062306a36Sopenharmony_ci if ((features == ATA_DCO_IDENTIFY) || 168162306a36Sopenharmony_ci (features == ATA_DCO_SET)) 168262306a36Sopenharmony_ci rv = 1; 168362306a36Sopenharmony_ci break; 168462306a36Sopenharmony_ci } 168562306a36Sopenharmony_ci return rv; 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci/* 168962306a36Sopenharmony_ci * Executes a taskfile 169062306a36Sopenharmony_ci * See ide_taskfile_ioctl() for derivation 169162306a36Sopenharmony_ci */ 169262306a36Sopenharmony_cistatic int exec_drive_taskfile(struct driver_data *dd, 169362306a36Sopenharmony_ci void __user *buf, 169462306a36Sopenharmony_ci ide_task_request_t *req_task, 169562306a36Sopenharmony_ci int outtotal) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci struct host_to_dev_fis fis; 169862306a36Sopenharmony_ci struct host_to_dev_fis *reply; 169962306a36Sopenharmony_ci u8 *outbuf = NULL; 170062306a36Sopenharmony_ci u8 *inbuf = NULL; 170162306a36Sopenharmony_ci dma_addr_t outbuf_dma = 0; 170262306a36Sopenharmony_ci dma_addr_t inbuf_dma = 0; 170362306a36Sopenharmony_ci dma_addr_t dma_buffer = 0; 170462306a36Sopenharmony_ci int err = 0; 170562306a36Sopenharmony_ci unsigned int taskin = 0; 170662306a36Sopenharmony_ci unsigned int taskout = 0; 170762306a36Sopenharmony_ci u8 nsect = 0; 170862306a36Sopenharmony_ci unsigned int timeout; 170962306a36Sopenharmony_ci unsigned int force_single_sector; 171062306a36Sopenharmony_ci unsigned int transfer_size; 171162306a36Sopenharmony_ci unsigned long task_file_data; 171262306a36Sopenharmony_ci int intotal = outtotal + req_task->out_size; 171362306a36Sopenharmony_ci int erasemode = 0; 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci taskout = req_task->out_size; 171662306a36Sopenharmony_ci taskin = req_task->in_size; 171762306a36Sopenharmony_ci /* 130560 = 512 * 0xFF*/ 171862306a36Sopenharmony_ci if (taskin > 130560 || taskout > 130560) 171962306a36Sopenharmony_ci return -EINVAL; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (taskout) { 172262306a36Sopenharmony_ci outbuf = memdup_user(buf + outtotal, taskout); 172362306a36Sopenharmony_ci if (IS_ERR(outbuf)) 172462306a36Sopenharmony_ci return PTR_ERR(outbuf); 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci outbuf_dma = dma_map_single(&dd->pdev->dev, outbuf, 172762306a36Sopenharmony_ci taskout, DMA_TO_DEVICE); 172862306a36Sopenharmony_ci if (dma_mapping_error(&dd->pdev->dev, outbuf_dma)) { 172962306a36Sopenharmony_ci err = -ENOMEM; 173062306a36Sopenharmony_ci goto abort; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci dma_buffer = outbuf_dma; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci if (taskin) { 173662306a36Sopenharmony_ci inbuf = memdup_user(buf + intotal, taskin); 173762306a36Sopenharmony_ci if (IS_ERR(inbuf)) { 173862306a36Sopenharmony_ci err = PTR_ERR(inbuf); 173962306a36Sopenharmony_ci inbuf = NULL; 174062306a36Sopenharmony_ci goto abort; 174162306a36Sopenharmony_ci } 174262306a36Sopenharmony_ci inbuf_dma = dma_map_single(&dd->pdev->dev, inbuf, 174362306a36Sopenharmony_ci taskin, DMA_FROM_DEVICE); 174462306a36Sopenharmony_ci if (dma_mapping_error(&dd->pdev->dev, inbuf_dma)) { 174562306a36Sopenharmony_ci err = -ENOMEM; 174662306a36Sopenharmony_ci goto abort; 174762306a36Sopenharmony_ci } 174862306a36Sopenharmony_ci dma_buffer = inbuf_dma; 174962306a36Sopenharmony_ci } 175062306a36Sopenharmony_ci 175162306a36Sopenharmony_ci /* only supports PIO and non-data commands from this ioctl. */ 175262306a36Sopenharmony_ci switch (req_task->data_phase) { 175362306a36Sopenharmony_ci case TASKFILE_OUT: 175462306a36Sopenharmony_ci nsect = taskout / ATA_SECT_SIZE; 175562306a36Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_PIO_SETUP); 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci case TASKFILE_IN: 175862306a36Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_PIO_SETUP); 175962306a36Sopenharmony_ci break; 176062306a36Sopenharmony_ci case TASKFILE_NO_DATA: 176162306a36Sopenharmony_ci reply = (dd->port->rxfis + RX_FIS_D2H_REG); 176262306a36Sopenharmony_ci break; 176362306a36Sopenharmony_ci default: 176462306a36Sopenharmony_ci err = -EINVAL; 176562306a36Sopenharmony_ci goto abort; 176662306a36Sopenharmony_ci } 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci /* Build the FIS. */ 176962306a36Sopenharmony_ci memset(&fis, 0, sizeof(struct host_to_dev_fis)); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci fis.type = 0x27; 177262306a36Sopenharmony_ci fis.opts = 1 << 7; 177362306a36Sopenharmony_ci fis.command = req_task->io_ports[7]; 177462306a36Sopenharmony_ci fis.features = req_task->io_ports[1]; 177562306a36Sopenharmony_ci fis.sect_count = req_task->io_ports[2]; 177662306a36Sopenharmony_ci fis.lba_low = req_task->io_ports[3]; 177762306a36Sopenharmony_ci fis.lba_mid = req_task->io_ports[4]; 177862306a36Sopenharmony_ci fis.lba_hi = req_task->io_ports[5]; 177962306a36Sopenharmony_ci /* Clear the dev bit*/ 178062306a36Sopenharmony_ci fis.device = req_task->io_ports[6] & ~0x10; 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci if ((req_task->in_flags.all == 0) && (req_task->out_flags.all & 1)) { 178362306a36Sopenharmony_ci req_task->in_flags.all = 178462306a36Sopenharmony_ci IDE_TASKFILE_STD_IN_FLAGS | 178562306a36Sopenharmony_ci (IDE_HOB_STD_IN_FLAGS << 8); 178662306a36Sopenharmony_ci fis.lba_low_ex = req_task->hob_ports[3]; 178762306a36Sopenharmony_ci fis.lba_mid_ex = req_task->hob_ports[4]; 178862306a36Sopenharmony_ci fis.lba_hi_ex = req_task->hob_ports[5]; 178962306a36Sopenharmony_ci fis.features_ex = req_task->hob_ports[1]; 179062306a36Sopenharmony_ci fis.sect_cnt_ex = req_task->hob_ports[2]; 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci } else { 179362306a36Sopenharmony_ci req_task->in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; 179462306a36Sopenharmony_ci } 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci force_single_sector = implicit_sector(fis.command, fis.features); 179762306a36Sopenharmony_ci 179862306a36Sopenharmony_ci if ((taskin || taskout) && (!fis.sect_count)) { 179962306a36Sopenharmony_ci if (nsect) 180062306a36Sopenharmony_ci fis.sect_count = nsect; 180162306a36Sopenharmony_ci else { 180262306a36Sopenharmony_ci if (!force_single_sector) { 180362306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 180462306a36Sopenharmony_ci "data movement but " 180562306a36Sopenharmony_ci "sect_count is 0\n"); 180662306a36Sopenharmony_ci err = -EINVAL; 180762306a36Sopenharmony_ci goto abort; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci } 181062306a36Sopenharmony_ci } 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 181362306a36Sopenharmony_ci " %s: cmd %x, feat %x, nsect %x," 181462306a36Sopenharmony_ci " sect/lbal %x, lcyl/lbam %x, hcyl/lbah %x," 181562306a36Sopenharmony_ci " head/dev %x\n", 181662306a36Sopenharmony_ci __func__, 181762306a36Sopenharmony_ci fis.command, 181862306a36Sopenharmony_ci fis.features, 181962306a36Sopenharmony_ci fis.sect_count, 182062306a36Sopenharmony_ci fis.lba_low, 182162306a36Sopenharmony_ci fis.lba_mid, 182262306a36Sopenharmony_ci fis.lba_hi, 182362306a36Sopenharmony_ci fis.device); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci /* check for erase mode support during secure erase.*/ 182662306a36Sopenharmony_ci if ((fis.command == ATA_CMD_SEC_ERASE_UNIT) && outbuf && 182762306a36Sopenharmony_ci (outbuf[0] & MTIP_SEC_ERASE_MODE)) { 182862306a36Sopenharmony_ci erasemode = 1; 182962306a36Sopenharmony_ci } 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci mtip_set_timeout(dd, &fis, &timeout, erasemode); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* Determine the correct transfer size.*/ 183462306a36Sopenharmony_ci if (force_single_sector) 183562306a36Sopenharmony_ci transfer_size = ATA_SECT_SIZE; 183662306a36Sopenharmony_ci else 183762306a36Sopenharmony_ci transfer_size = ATA_SECT_SIZE * fis.sect_count; 183862306a36Sopenharmony_ci 183962306a36Sopenharmony_ci /* Execute the command.*/ 184062306a36Sopenharmony_ci if (mtip_exec_internal_command(dd->port, 184162306a36Sopenharmony_ci &fis, 184262306a36Sopenharmony_ci 5, 184362306a36Sopenharmony_ci dma_buffer, 184462306a36Sopenharmony_ci transfer_size, 184562306a36Sopenharmony_ci 0, 184662306a36Sopenharmony_ci timeout) < 0) { 184762306a36Sopenharmony_ci err = -EIO; 184862306a36Sopenharmony_ci goto abort; 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci task_file_data = readl(dd->port->mmio+PORT_TFDATA); 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci if ((req_task->data_phase == TASKFILE_IN) && !(task_file_data & 1)) { 185462306a36Sopenharmony_ci reply = dd->port->rxfis + RX_FIS_PIO_SETUP; 185562306a36Sopenharmony_ci req_task->io_ports[7] = reply->control; 185662306a36Sopenharmony_ci } else { 185762306a36Sopenharmony_ci reply = dd->port->rxfis + RX_FIS_D2H_REG; 185862306a36Sopenharmony_ci req_task->io_ports[7] = reply->command; 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci /* reclaim the DMA buffers.*/ 186262306a36Sopenharmony_ci if (inbuf_dma) 186362306a36Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, inbuf_dma, taskin, 186462306a36Sopenharmony_ci DMA_FROM_DEVICE); 186562306a36Sopenharmony_ci if (outbuf_dma) 186662306a36Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, outbuf_dma, taskout, 186762306a36Sopenharmony_ci DMA_TO_DEVICE); 186862306a36Sopenharmony_ci inbuf_dma = 0; 186962306a36Sopenharmony_ci outbuf_dma = 0; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci /* return the ATA registers to the caller.*/ 187262306a36Sopenharmony_ci req_task->io_ports[1] = reply->features; 187362306a36Sopenharmony_ci req_task->io_ports[2] = reply->sect_count; 187462306a36Sopenharmony_ci req_task->io_ports[3] = reply->lba_low; 187562306a36Sopenharmony_ci req_task->io_ports[4] = reply->lba_mid; 187662306a36Sopenharmony_ci req_task->io_ports[5] = reply->lba_hi; 187762306a36Sopenharmony_ci req_task->io_ports[6] = reply->device; 187862306a36Sopenharmony_ci 187962306a36Sopenharmony_ci if (req_task->out_flags.all & 1) { 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci req_task->hob_ports[3] = reply->lba_low_ex; 188262306a36Sopenharmony_ci req_task->hob_ports[4] = reply->lba_mid_ex; 188362306a36Sopenharmony_ci req_task->hob_ports[5] = reply->lba_hi_ex; 188462306a36Sopenharmony_ci req_task->hob_ports[1] = reply->features_ex; 188562306a36Sopenharmony_ci req_task->hob_ports[2] = reply->sect_cnt_ex; 188662306a36Sopenharmony_ci } 188762306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME 188862306a36Sopenharmony_ci " %s: Completion: stat %x," 188962306a36Sopenharmony_ci "err %x, sect_cnt %x, lbalo %x," 189062306a36Sopenharmony_ci "lbamid %x, lbahi %x, dev %x\n", 189162306a36Sopenharmony_ci __func__, 189262306a36Sopenharmony_ci req_task->io_ports[7], 189362306a36Sopenharmony_ci req_task->io_ports[1], 189462306a36Sopenharmony_ci req_task->io_ports[2], 189562306a36Sopenharmony_ci req_task->io_ports[3], 189662306a36Sopenharmony_ci req_task->io_ports[4], 189762306a36Sopenharmony_ci req_task->io_ports[5], 189862306a36Sopenharmony_ci req_task->io_ports[6]); 189962306a36Sopenharmony_ci 190062306a36Sopenharmony_ci if (taskout) { 190162306a36Sopenharmony_ci if (copy_to_user(buf + outtotal, outbuf, taskout)) { 190262306a36Sopenharmony_ci err = -EFAULT; 190362306a36Sopenharmony_ci goto abort; 190462306a36Sopenharmony_ci } 190562306a36Sopenharmony_ci } 190662306a36Sopenharmony_ci if (taskin) { 190762306a36Sopenharmony_ci if (copy_to_user(buf + intotal, inbuf, taskin)) { 190862306a36Sopenharmony_ci err = -EFAULT; 190962306a36Sopenharmony_ci goto abort; 191062306a36Sopenharmony_ci } 191162306a36Sopenharmony_ci } 191262306a36Sopenharmony_ciabort: 191362306a36Sopenharmony_ci if (inbuf_dma) 191462306a36Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, inbuf_dma, taskin, 191562306a36Sopenharmony_ci DMA_FROM_DEVICE); 191662306a36Sopenharmony_ci if (outbuf_dma) 191762306a36Sopenharmony_ci dma_unmap_single(&dd->pdev->dev, outbuf_dma, taskout, 191862306a36Sopenharmony_ci DMA_TO_DEVICE); 191962306a36Sopenharmony_ci kfree(outbuf); 192062306a36Sopenharmony_ci kfree(inbuf); 192162306a36Sopenharmony_ci 192262306a36Sopenharmony_ci return err; 192362306a36Sopenharmony_ci} 192462306a36Sopenharmony_ci 192562306a36Sopenharmony_ci/* 192662306a36Sopenharmony_ci * Handle IOCTL calls from the Block Layer. 192762306a36Sopenharmony_ci * 192862306a36Sopenharmony_ci * This function is called by the Block Layer when it receives an IOCTL 192962306a36Sopenharmony_ci * command that it does not understand. If the IOCTL command is not supported 193062306a36Sopenharmony_ci * this function returns -ENOTTY. 193162306a36Sopenharmony_ci * 193262306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 193362306a36Sopenharmony_ci * @cmd IOCTL command passed from the Block Layer. 193462306a36Sopenharmony_ci * @arg IOCTL argument passed from the Block Layer. 193562306a36Sopenharmony_ci * 193662306a36Sopenharmony_ci * return value 193762306a36Sopenharmony_ci * 0 The IOCTL completed successfully. 193862306a36Sopenharmony_ci * -ENOTTY The specified command is not supported. 193962306a36Sopenharmony_ci * -EFAULT An error occurred copying data to a user space buffer. 194062306a36Sopenharmony_ci * -EIO An error occurred while executing the command. 194162306a36Sopenharmony_ci */ 194262306a36Sopenharmony_cistatic int mtip_hw_ioctl(struct driver_data *dd, unsigned int cmd, 194362306a36Sopenharmony_ci unsigned long arg) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci switch (cmd) { 194662306a36Sopenharmony_ci case HDIO_GET_IDENTITY: 194762306a36Sopenharmony_ci { 194862306a36Sopenharmony_ci if (copy_to_user((void __user *)arg, dd->port->identify, 194962306a36Sopenharmony_ci sizeof(u16) * ATA_ID_WORDS)) 195062306a36Sopenharmony_ci return -EFAULT; 195162306a36Sopenharmony_ci break; 195262306a36Sopenharmony_ci } 195362306a36Sopenharmony_ci case HDIO_DRIVE_CMD: 195462306a36Sopenharmony_ci { 195562306a36Sopenharmony_ci u8 drive_command[4]; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci /* Copy the user command info to our buffer. */ 195862306a36Sopenharmony_ci if (copy_from_user(drive_command, 195962306a36Sopenharmony_ci (void __user *) arg, 196062306a36Sopenharmony_ci sizeof(drive_command))) 196162306a36Sopenharmony_ci return -EFAULT; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci /* Execute the drive command. */ 196462306a36Sopenharmony_ci if (exec_drive_command(dd->port, 196562306a36Sopenharmony_ci drive_command, 196662306a36Sopenharmony_ci (void __user *) (arg+4))) 196762306a36Sopenharmony_ci return -EIO; 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci /* Copy the status back to the users buffer. */ 197062306a36Sopenharmony_ci if (copy_to_user((void __user *) arg, 197162306a36Sopenharmony_ci drive_command, 197262306a36Sopenharmony_ci sizeof(drive_command))) 197362306a36Sopenharmony_ci return -EFAULT; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci break; 197662306a36Sopenharmony_ci } 197762306a36Sopenharmony_ci case HDIO_DRIVE_TASK: 197862306a36Sopenharmony_ci { 197962306a36Sopenharmony_ci u8 drive_command[7]; 198062306a36Sopenharmony_ci 198162306a36Sopenharmony_ci /* Copy the user command info to our buffer. */ 198262306a36Sopenharmony_ci if (copy_from_user(drive_command, 198362306a36Sopenharmony_ci (void __user *) arg, 198462306a36Sopenharmony_ci sizeof(drive_command))) 198562306a36Sopenharmony_ci return -EFAULT; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci /* Execute the drive command. */ 198862306a36Sopenharmony_ci if (exec_drive_task(dd->port, drive_command)) 198962306a36Sopenharmony_ci return -EIO; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* Copy the status back to the users buffer. */ 199262306a36Sopenharmony_ci if (copy_to_user((void __user *) arg, 199362306a36Sopenharmony_ci drive_command, 199462306a36Sopenharmony_ci sizeof(drive_command))) 199562306a36Sopenharmony_ci return -EFAULT; 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci break; 199862306a36Sopenharmony_ci } 199962306a36Sopenharmony_ci case HDIO_DRIVE_TASKFILE: { 200062306a36Sopenharmony_ci ide_task_request_t req_task; 200162306a36Sopenharmony_ci int ret, outtotal; 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_ci if (copy_from_user(&req_task, (void __user *) arg, 200462306a36Sopenharmony_ci sizeof(req_task))) 200562306a36Sopenharmony_ci return -EFAULT; 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci outtotal = sizeof(req_task); 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_ci ret = exec_drive_taskfile(dd, (void __user *) arg, 201062306a36Sopenharmony_ci &req_task, outtotal); 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci if (copy_to_user((void __user *) arg, &req_task, 201362306a36Sopenharmony_ci sizeof(req_task))) 201462306a36Sopenharmony_ci return -EFAULT; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci return ret; 201762306a36Sopenharmony_ci } 201862306a36Sopenharmony_ci 201962306a36Sopenharmony_ci default: 202062306a36Sopenharmony_ci return -EINVAL; 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci return 0; 202362306a36Sopenharmony_ci} 202462306a36Sopenharmony_ci 202562306a36Sopenharmony_ci/* 202662306a36Sopenharmony_ci * Submit an IO to the hw 202762306a36Sopenharmony_ci * 202862306a36Sopenharmony_ci * This function is called by the block layer to issue an io 202962306a36Sopenharmony_ci * to the device. Upon completion, the callback function will 203062306a36Sopenharmony_ci * be called with the data parameter passed as the callback data. 203162306a36Sopenharmony_ci * 203262306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 203362306a36Sopenharmony_ci * @start First sector to read. 203462306a36Sopenharmony_ci * @nsect Number of sectors to read. 203562306a36Sopenharmony_ci * @tag The tag of this read command. 203662306a36Sopenharmony_ci * @callback Pointer to the function that should be called 203762306a36Sopenharmony_ci * when the read completes. 203862306a36Sopenharmony_ci * @data Callback data passed to the callback function 203962306a36Sopenharmony_ci * when the read completes. 204062306a36Sopenharmony_ci * @dir Direction (read or write) 204162306a36Sopenharmony_ci * 204262306a36Sopenharmony_ci * return value 204362306a36Sopenharmony_ci * None 204462306a36Sopenharmony_ci */ 204562306a36Sopenharmony_cistatic void mtip_hw_submit_io(struct driver_data *dd, struct request *rq, 204662306a36Sopenharmony_ci struct mtip_cmd *command, 204762306a36Sopenharmony_ci struct blk_mq_hw_ctx *hctx) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct mtip_cmd_hdr *hdr = 205062306a36Sopenharmony_ci dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag; 205162306a36Sopenharmony_ci struct host_to_dev_fis *fis; 205262306a36Sopenharmony_ci struct mtip_port *port = dd->port; 205362306a36Sopenharmony_ci int dma_dir = rq_data_dir(rq) == READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE; 205462306a36Sopenharmony_ci u64 start = blk_rq_pos(rq); 205562306a36Sopenharmony_ci unsigned int nsect = blk_rq_sectors(rq); 205662306a36Sopenharmony_ci unsigned int nents; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* Map the scatter list for DMA access */ 205962306a36Sopenharmony_ci nents = blk_rq_map_sg(hctx->queue, rq, command->sg); 206062306a36Sopenharmony_ci nents = dma_map_sg(&dd->pdev->dev, command->sg, nents, dma_dir); 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci prefetch(&port->flags); 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci command->scatter_ents = nents; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci /* 206762306a36Sopenharmony_ci * The number of retries for this command before it is 206862306a36Sopenharmony_ci * reported as a failure to the upper layers. 206962306a36Sopenharmony_ci */ 207062306a36Sopenharmony_ci command->retries = MTIP_MAX_RETRIES; 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_ci /* Fill out fis */ 207362306a36Sopenharmony_ci fis = command->command; 207462306a36Sopenharmony_ci fis->type = 0x27; 207562306a36Sopenharmony_ci fis->opts = 1 << 7; 207662306a36Sopenharmony_ci if (dma_dir == DMA_FROM_DEVICE) 207762306a36Sopenharmony_ci fis->command = ATA_CMD_FPDMA_READ; 207862306a36Sopenharmony_ci else 207962306a36Sopenharmony_ci fis->command = ATA_CMD_FPDMA_WRITE; 208062306a36Sopenharmony_ci fis->lba_low = start & 0xFF; 208162306a36Sopenharmony_ci fis->lba_mid = (start >> 8) & 0xFF; 208262306a36Sopenharmony_ci fis->lba_hi = (start >> 16) & 0xFF; 208362306a36Sopenharmony_ci fis->lba_low_ex = (start >> 24) & 0xFF; 208462306a36Sopenharmony_ci fis->lba_mid_ex = (start >> 32) & 0xFF; 208562306a36Sopenharmony_ci fis->lba_hi_ex = (start >> 40) & 0xFF; 208662306a36Sopenharmony_ci fis->device = 1 << 6; 208762306a36Sopenharmony_ci fis->features = nsect & 0xFF; 208862306a36Sopenharmony_ci fis->features_ex = (nsect >> 8) & 0xFF; 208962306a36Sopenharmony_ci fis->sect_count = ((rq->tag << 3) | (rq->tag >> 5)); 209062306a36Sopenharmony_ci fis->sect_cnt_ex = 0; 209162306a36Sopenharmony_ci fis->control = 0; 209262306a36Sopenharmony_ci fis->res2 = 0; 209362306a36Sopenharmony_ci fis->res3 = 0; 209462306a36Sopenharmony_ci fill_command_sg(dd, command, nents); 209562306a36Sopenharmony_ci 209662306a36Sopenharmony_ci if (unlikely(command->unaligned)) 209762306a36Sopenharmony_ci fis->device |= 1 << 7; 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci /* Populate the command header */ 210062306a36Sopenharmony_ci hdr->ctba = cpu_to_le32(command->command_dma & 0xFFFFFFFF); 210162306a36Sopenharmony_ci if (test_bit(MTIP_PF_HOST_CAP_64, &dd->port->flags)) 210262306a36Sopenharmony_ci hdr->ctbau = cpu_to_le32((command->command_dma >> 16) >> 16); 210362306a36Sopenharmony_ci hdr->opts = cpu_to_le32((nents << 16) | 5 | AHCI_CMD_PREFETCH); 210462306a36Sopenharmony_ci hdr->byte_count = 0; 210562306a36Sopenharmony_ci 210662306a36Sopenharmony_ci command->direction = dma_dir; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci /* 210962306a36Sopenharmony_ci * To prevent this command from being issued 211062306a36Sopenharmony_ci * if an internal command is in progress or error handling is active. 211162306a36Sopenharmony_ci */ 211262306a36Sopenharmony_ci if (unlikely(port->flags & MTIP_PF_PAUSE_IO)) { 211362306a36Sopenharmony_ci set_bit(rq->tag, port->cmds_to_issue); 211462306a36Sopenharmony_ci set_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); 211562306a36Sopenharmony_ci return; 211662306a36Sopenharmony_ci } 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci /* Issue the command to the hardware */ 211962306a36Sopenharmony_ci mtip_issue_ncq_command(port, rq->tag); 212062306a36Sopenharmony_ci} 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci/* 212362306a36Sopenharmony_ci * Sysfs status dump. 212462306a36Sopenharmony_ci * 212562306a36Sopenharmony_ci * @dev Pointer to the device structure, passed by the kernrel. 212662306a36Sopenharmony_ci * @attr Pointer to the device_attribute structure passed by the kernel. 212762306a36Sopenharmony_ci * @buf Pointer to the char buffer that will receive the stats info. 212862306a36Sopenharmony_ci * 212962306a36Sopenharmony_ci * return value 213062306a36Sopenharmony_ci * The size, in bytes, of the data copied into buf. 213162306a36Sopenharmony_ci */ 213262306a36Sopenharmony_cistatic ssize_t mtip_hw_show_status(struct device *dev, 213362306a36Sopenharmony_ci struct device_attribute *attr, 213462306a36Sopenharmony_ci char *buf) 213562306a36Sopenharmony_ci{ 213662306a36Sopenharmony_ci struct driver_data *dd = dev_to_disk(dev)->private_data; 213762306a36Sopenharmony_ci int size = 0; 213862306a36Sopenharmony_ci 213962306a36Sopenharmony_ci if (test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag)) 214062306a36Sopenharmony_ci size += sprintf(buf, "%s", "thermal_shutdown\n"); 214162306a36Sopenharmony_ci else if (test_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag)) 214262306a36Sopenharmony_ci size += sprintf(buf, "%s", "write_protect\n"); 214362306a36Sopenharmony_ci else 214462306a36Sopenharmony_ci size += sprintf(buf, "%s", "online\n"); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci return size; 214762306a36Sopenharmony_ci} 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_cistatic DEVICE_ATTR(status, 0444, mtip_hw_show_status, NULL); 215062306a36Sopenharmony_ci 215162306a36Sopenharmony_cistatic struct attribute *mtip_disk_attrs[] = { 215262306a36Sopenharmony_ci &dev_attr_status.attr, 215362306a36Sopenharmony_ci NULL, 215462306a36Sopenharmony_ci}; 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_cistatic const struct attribute_group mtip_disk_attr_group = { 215762306a36Sopenharmony_ci .attrs = mtip_disk_attrs, 215862306a36Sopenharmony_ci}; 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_cistatic const struct attribute_group *mtip_disk_attr_groups[] = { 216162306a36Sopenharmony_ci &mtip_disk_attr_group, 216262306a36Sopenharmony_ci NULL, 216362306a36Sopenharmony_ci}; 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_cistatic ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf, 216662306a36Sopenharmony_ci size_t len, loff_t *offset) 216762306a36Sopenharmony_ci{ 216862306a36Sopenharmony_ci struct driver_data *dd = (struct driver_data *)f->private_data; 216962306a36Sopenharmony_ci char *buf; 217062306a36Sopenharmony_ci u32 group_allocated; 217162306a36Sopenharmony_ci int size = *offset; 217262306a36Sopenharmony_ci int n, rv = 0; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (!len || size) 217562306a36Sopenharmony_ci return 0; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci buf = kzalloc(MTIP_DFS_MAX_BUF_SIZE, GFP_KERNEL); 217862306a36Sopenharmony_ci if (!buf) 217962306a36Sopenharmony_ci return -ENOMEM; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci size += sprintf(&buf[size], "H/ S ACTive : [ 0x"); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 218462306a36Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 218562306a36Sopenharmony_ci readl(dd->port->s_active[n])); 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 218862306a36Sopenharmony_ci size += sprintf(&buf[size], "H/ Command Issue : [ 0x"); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 219162306a36Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 219262306a36Sopenharmony_ci readl(dd->port->cmd_issue[n])); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 219562306a36Sopenharmony_ci size += sprintf(&buf[size], "H/ Completed : [ 0x"); 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) 219862306a36Sopenharmony_ci size += sprintf(&buf[size], "%08X ", 219962306a36Sopenharmony_ci readl(dd->port->completed[n])); 220062306a36Sopenharmony_ci 220162306a36Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 220262306a36Sopenharmony_ci size += sprintf(&buf[size], "H/ PORT IRQ STAT : [ 0x%08X ]\n", 220362306a36Sopenharmony_ci readl(dd->port->mmio + PORT_IRQ_STAT)); 220462306a36Sopenharmony_ci size += sprintf(&buf[size], "H/ HOST IRQ STAT : [ 0x%08X ]\n", 220562306a36Sopenharmony_ci readl(dd->mmio + HOST_IRQ_STAT)); 220662306a36Sopenharmony_ci size += sprintf(&buf[size], "\n"); 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci size += sprintf(&buf[size], "L/ Commands in Q : [ 0x"); 220962306a36Sopenharmony_ci 221062306a36Sopenharmony_ci for (n = dd->slot_groups-1; n >= 0; n--) { 221162306a36Sopenharmony_ci if (sizeof(long) > sizeof(u32)) 221262306a36Sopenharmony_ci group_allocated = 221362306a36Sopenharmony_ci dd->port->cmds_to_issue[n/2] >> (32*(n&1)); 221462306a36Sopenharmony_ci else 221562306a36Sopenharmony_ci group_allocated = dd->port->cmds_to_issue[n]; 221662306a36Sopenharmony_ci size += sprintf(&buf[size], "%08X ", group_allocated); 221762306a36Sopenharmony_ci } 221862306a36Sopenharmony_ci size += sprintf(&buf[size], "]\n"); 221962306a36Sopenharmony_ci 222062306a36Sopenharmony_ci *offset = size <= len ? size : len; 222162306a36Sopenharmony_ci size = copy_to_user(ubuf, buf, *offset); 222262306a36Sopenharmony_ci if (size) 222362306a36Sopenharmony_ci rv = -EFAULT; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci kfree(buf); 222662306a36Sopenharmony_ci return rv ? rv : *offset; 222762306a36Sopenharmony_ci} 222862306a36Sopenharmony_ci 222962306a36Sopenharmony_cistatic ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf, 223062306a36Sopenharmony_ci size_t len, loff_t *offset) 223162306a36Sopenharmony_ci{ 223262306a36Sopenharmony_ci struct driver_data *dd = (struct driver_data *)f->private_data; 223362306a36Sopenharmony_ci char *buf; 223462306a36Sopenharmony_ci int size = *offset; 223562306a36Sopenharmony_ci int rv = 0; 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci if (!len || size) 223862306a36Sopenharmony_ci return 0; 223962306a36Sopenharmony_ci 224062306a36Sopenharmony_ci buf = kzalloc(MTIP_DFS_MAX_BUF_SIZE, GFP_KERNEL); 224162306a36Sopenharmony_ci if (!buf) 224262306a36Sopenharmony_ci return -ENOMEM; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n", 224562306a36Sopenharmony_ci dd->port->flags); 224662306a36Sopenharmony_ci size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n", 224762306a36Sopenharmony_ci dd->dd_flag); 224862306a36Sopenharmony_ci 224962306a36Sopenharmony_ci *offset = size <= len ? size : len; 225062306a36Sopenharmony_ci size = copy_to_user(ubuf, buf, *offset); 225162306a36Sopenharmony_ci if (size) 225262306a36Sopenharmony_ci rv = -EFAULT; 225362306a36Sopenharmony_ci 225462306a36Sopenharmony_ci kfree(buf); 225562306a36Sopenharmony_ci return rv ? rv : *offset; 225662306a36Sopenharmony_ci} 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_cistatic const struct file_operations mtip_regs_fops = { 225962306a36Sopenharmony_ci .owner = THIS_MODULE, 226062306a36Sopenharmony_ci .open = simple_open, 226162306a36Sopenharmony_ci .read = mtip_hw_read_registers, 226262306a36Sopenharmony_ci .llseek = no_llseek, 226362306a36Sopenharmony_ci}; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_cistatic const struct file_operations mtip_flags_fops = { 226662306a36Sopenharmony_ci .owner = THIS_MODULE, 226762306a36Sopenharmony_ci .open = simple_open, 226862306a36Sopenharmony_ci .read = mtip_hw_read_flags, 226962306a36Sopenharmony_ci .llseek = no_llseek, 227062306a36Sopenharmony_ci}; 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_cistatic int mtip_hw_debugfs_init(struct driver_data *dd) 227362306a36Sopenharmony_ci{ 227462306a36Sopenharmony_ci if (!dfs_parent) 227562306a36Sopenharmony_ci return -1; 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci dd->dfs_node = debugfs_create_dir(dd->disk->disk_name, dfs_parent); 227862306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dd->dfs_node)) { 227962306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 228062306a36Sopenharmony_ci "Error creating node %s under debugfs\n", 228162306a36Sopenharmony_ci dd->disk->disk_name); 228262306a36Sopenharmony_ci dd->dfs_node = NULL; 228362306a36Sopenharmony_ci return -1; 228462306a36Sopenharmony_ci } 228562306a36Sopenharmony_ci 228662306a36Sopenharmony_ci debugfs_create_file("flags", 0444, dd->dfs_node, dd, &mtip_flags_fops); 228762306a36Sopenharmony_ci debugfs_create_file("registers", 0444, dd->dfs_node, dd, 228862306a36Sopenharmony_ci &mtip_regs_fops); 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci return 0; 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic void mtip_hw_debugfs_exit(struct driver_data *dd) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci debugfs_remove_recursive(dd->dfs_node); 229662306a36Sopenharmony_ci} 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci/* 229962306a36Sopenharmony_ci * Perform any init/resume time hardware setup 230062306a36Sopenharmony_ci * 230162306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 230262306a36Sopenharmony_ci * 230362306a36Sopenharmony_ci * return value 230462306a36Sopenharmony_ci * None 230562306a36Sopenharmony_ci */ 230662306a36Sopenharmony_cistatic inline void hba_setup(struct driver_data *dd) 230762306a36Sopenharmony_ci{ 230862306a36Sopenharmony_ci u32 hwdata; 230962306a36Sopenharmony_ci hwdata = readl(dd->mmio + HOST_HSORG); 231062306a36Sopenharmony_ci 231162306a36Sopenharmony_ci /* interrupt bug workaround: use only 1 IS bit.*/ 231262306a36Sopenharmony_ci writel(hwdata | 231362306a36Sopenharmony_ci HSORG_DISABLE_SLOTGRP_INTR | 231462306a36Sopenharmony_ci HSORG_DISABLE_SLOTGRP_PXIS, 231562306a36Sopenharmony_ci dd->mmio + HOST_HSORG); 231662306a36Sopenharmony_ci} 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_cistatic int mtip_device_unaligned_constrained(struct driver_data *dd) 231962306a36Sopenharmony_ci{ 232062306a36Sopenharmony_ci return (dd->pdev->device == P420M_DEVICE_ID ? 1 : 0); 232162306a36Sopenharmony_ci} 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci/* 232462306a36Sopenharmony_ci * Detect the details of the product, and store anything needed 232562306a36Sopenharmony_ci * into the driver data structure. This includes product type and 232662306a36Sopenharmony_ci * version and number of slot groups. 232762306a36Sopenharmony_ci * 232862306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 232962306a36Sopenharmony_ci * 233062306a36Sopenharmony_ci * return value 233162306a36Sopenharmony_ci * None 233262306a36Sopenharmony_ci */ 233362306a36Sopenharmony_cistatic void mtip_detect_product(struct driver_data *dd) 233462306a36Sopenharmony_ci{ 233562306a36Sopenharmony_ci u32 hwdata; 233662306a36Sopenharmony_ci unsigned int rev, slotgroups; 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci /* 233962306a36Sopenharmony_ci * HBA base + 0xFC [15:0] - vendor-specific hardware interface 234062306a36Sopenharmony_ci * info register: 234162306a36Sopenharmony_ci * [15:8] hardware/software interface rev# 234262306a36Sopenharmony_ci * [ 3] asic-style interface 234362306a36Sopenharmony_ci * [ 2:0] number of slot groups, minus 1 (only valid for asic-style). 234462306a36Sopenharmony_ci */ 234562306a36Sopenharmony_ci hwdata = readl(dd->mmio + HOST_HSORG); 234662306a36Sopenharmony_ci 234762306a36Sopenharmony_ci dd->product_type = MTIP_PRODUCT_UNKNOWN; 234862306a36Sopenharmony_ci dd->slot_groups = 1; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci if (hwdata & 0x8) { 235162306a36Sopenharmony_ci dd->product_type = MTIP_PRODUCT_ASICFPGA; 235262306a36Sopenharmony_ci rev = (hwdata & HSORG_HWREV) >> 8; 235362306a36Sopenharmony_ci slotgroups = (hwdata & HSORG_SLOTGROUPS) + 1; 235462306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 235562306a36Sopenharmony_ci "ASIC-FPGA design, HS rev 0x%x, " 235662306a36Sopenharmony_ci "%i slot groups [%i slots]\n", 235762306a36Sopenharmony_ci rev, 235862306a36Sopenharmony_ci slotgroups, 235962306a36Sopenharmony_ci slotgroups * 32); 236062306a36Sopenharmony_ci 236162306a36Sopenharmony_ci if (slotgroups > MTIP_MAX_SLOT_GROUPS) { 236262306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 236362306a36Sopenharmony_ci "Warning: driver only supports " 236462306a36Sopenharmony_ci "%i slot groups.\n", MTIP_MAX_SLOT_GROUPS); 236562306a36Sopenharmony_ci slotgroups = MTIP_MAX_SLOT_GROUPS; 236662306a36Sopenharmony_ci } 236762306a36Sopenharmony_ci dd->slot_groups = slotgroups; 236862306a36Sopenharmony_ci return; 236962306a36Sopenharmony_ci } 237062306a36Sopenharmony_ci 237162306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, "Unrecognized product id\n"); 237262306a36Sopenharmony_ci} 237362306a36Sopenharmony_ci 237462306a36Sopenharmony_ci/* 237562306a36Sopenharmony_ci * Blocking wait for FTL rebuild to complete 237662306a36Sopenharmony_ci * 237762306a36Sopenharmony_ci * @dd Pointer to the DRIVER_DATA structure. 237862306a36Sopenharmony_ci * 237962306a36Sopenharmony_ci * return value 238062306a36Sopenharmony_ci * 0 FTL rebuild completed successfully 238162306a36Sopenharmony_ci * -EFAULT FTL rebuild error/timeout/interruption 238262306a36Sopenharmony_ci */ 238362306a36Sopenharmony_cistatic int mtip_ftl_rebuild_poll(struct driver_data *dd) 238462306a36Sopenharmony_ci{ 238562306a36Sopenharmony_ci unsigned long timeout, cnt = 0, start; 238662306a36Sopenharmony_ci 238762306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 238862306a36Sopenharmony_ci "FTL rebuild in progress. Polling for completion.\n"); 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci start = jiffies; 239162306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(MTIP_FTL_REBUILD_TIMEOUT_MS); 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ci do { 239462306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 239562306a36Sopenharmony_ci &dd->dd_flag))) 239662306a36Sopenharmony_ci return -EFAULT; 239762306a36Sopenharmony_ci if (mtip_check_surprise_removal(dd)) 239862306a36Sopenharmony_ci return -EFAULT; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci if (mtip_get_identify(dd->port, NULL) < 0) 240162306a36Sopenharmony_ci return -EFAULT; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci if (*(dd->port->identify + MTIP_FTL_REBUILD_OFFSET) == 240462306a36Sopenharmony_ci MTIP_FTL_REBUILD_MAGIC) { 240562306a36Sopenharmony_ci ssleep(1); 240662306a36Sopenharmony_ci /* Print message every 3 minutes */ 240762306a36Sopenharmony_ci if (cnt++ >= 180) { 240862306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 240962306a36Sopenharmony_ci "FTL rebuild in progress (%d secs).\n", 241062306a36Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 241162306a36Sopenharmony_ci cnt = 0; 241262306a36Sopenharmony_ci } 241362306a36Sopenharmony_ci } else { 241462306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 241562306a36Sopenharmony_ci "FTL rebuild complete (%d secs).\n", 241662306a36Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 241762306a36Sopenharmony_ci mtip_block_initialize(dd); 241862306a36Sopenharmony_ci return 0; 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci } while (time_before(jiffies, timeout)); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci /* Check for timeout */ 242362306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 242462306a36Sopenharmony_ci "Timed out waiting for FTL rebuild to complete (%d secs).\n", 242562306a36Sopenharmony_ci jiffies_to_msecs(jiffies - start) / 1000); 242662306a36Sopenharmony_ci return -EFAULT; 242762306a36Sopenharmony_ci} 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_cistatic void mtip_softirq_done_fn(struct request *rq) 243062306a36Sopenharmony_ci{ 243162306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 243262306a36Sopenharmony_ci struct driver_data *dd = rq->q->queuedata; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci /* Unmap the DMA scatter list entries */ 243562306a36Sopenharmony_ci dma_unmap_sg(&dd->pdev->dev, cmd->sg, cmd->scatter_ents, 243662306a36Sopenharmony_ci cmd->direction); 243762306a36Sopenharmony_ci 243862306a36Sopenharmony_ci if (unlikely(cmd->unaligned)) 243962306a36Sopenharmony_ci atomic_inc(&dd->port->cmd_slot_unal); 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci blk_mq_end_request(rq, cmd->status); 244262306a36Sopenharmony_ci} 244362306a36Sopenharmony_ci 244462306a36Sopenharmony_cistatic bool mtip_abort_cmd(struct request *req, void *data) 244562306a36Sopenharmony_ci{ 244662306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(req); 244762306a36Sopenharmony_ci struct driver_data *dd = data; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci dbg_printk(MTIP_DRV_NAME " Aborting request, tag = %d\n", req->tag); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci clear_bit(req->tag, dd->port->cmds_to_issue); 245262306a36Sopenharmony_ci cmd->status = BLK_STS_IOERR; 245362306a36Sopenharmony_ci mtip_softirq_done_fn(req); 245462306a36Sopenharmony_ci return true; 245562306a36Sopenharmony_ci} 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_cistatic bool mtip_queue_cmd(struct request *req, void *data) 245862306a36Sopenharmony_ci{ 245962306a36Sopenharmony_ci struct driver_data *dd = data; 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci set_bit(req->tag, dd->port->cmds_to_issue); 246262306a36Sopenharmony_ci blk_abort_request(req); 246362306a36Sopenharmony_ci return true; 246462306a36Sopenharmony_ci} 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci/* 246762306a36Sopenharmony_ci * service thread to issue queued commands 246862306a36Sopenharmony_ci * 246962306a36Sopenharmony_ci * @data Pointer to the driver data structure. 247062306a36Sopenharmony_ci * 247162306a36Sopenharmony_ci * return value 247262306a36Sopenharmony_ci * 0 247362306a36Sopenharmony_ci */ 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_cistatic int mtip_service_thread(void *data) 247662306a36Sopenharmony_ci{ 247762306a36Sopenharmony_ci struct driver_data *dd = (struct driver_data *)data; 247862306a36Sopenharmony_ci unsigned long slot, slot_start, slot_wrap, to; 247962306a36Sopenharmony_ci unsigned int num_cmd_slots = dd->slot_groups * 32; 248062306a36Sopenharmony_ci struct mtip_port *port = dd->port; 248162306a36Sopenharmony_ci 248262306a36Sopenharmony_ci while (1) { 248362306a36Sopenharmony_ci if (kthread_should_stop() || 248462306a36Sopenharmony_ci test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) 248562306a36Sopenharmony_ci goto st_out; 248662306a36Sopenharmony_ci clear_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci /* 248962306a36Sopenharmony_ci * the condition is to check neither an internal command is 249062306a36Sopenharmony_ci * is in progress nor error handling is active 249162306a36Sopenharmony_ci */ 249262306a36Sopenharmony_ci wait_event_interruptible(port->svc_wait, (port->flags) && 249362306a36Sopenharmony_ci (port->flags & MTIP_PF_SVC_THD_WORK)); 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci if (kthread_should_stop() || 249662306a36Sopenharmony_ci test_bit(MTIP_PF_SVC_THD_STOP_BIT, &port->flags)) 249762306a36Sopenharmony_ci goto st_out; 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, 250062306a36Sopenharmony_ci &dd->dd_flag))) 250162306a36Sopenharmony_ci goto st_out; 250262306a36Sopenharmony_ci 250362306a36Sopenharmony_ci set_bit(MTIP_PF_SVC_THD_ACTIVE_BIT, &port->flags); 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_cirestart_eh: 250662306a36Sopenharmony_ci /* Demux bits: start with error handling */ 250762306a36Sopenharmony_ci if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) { 250862306a36Sopenharmony_ci mtip_handle_tfe(dd); 250962306a36Sopenharmony_ci clear_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags); 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci 251262306a36Sopenharmony_ci if (test_bit(MTIP_PF_EH_ACTIVE_BIT, &port->flags)) 251362306a36Sopenharmony_ci goto restart_eh; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci if (test_bit(MTIP_PF_TO_ACTIVE_BIT, &port->flags)) { 251662306a36Sopenharmony_ci to = jiffies + msecs_to_jiffies(5000); 251762306a36Sopenharmony_ci 251862306a36Sopenharmony_ci do { 251962306a36Sopenharmony_ci mdelay(100); 252062306a36Sopenharmony_ci } while (atomic_read(&dd->irq_workers_active) != 0 && 252162306a36Sopenharmony_ci time_before(jiffies, to)); 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci if (atomic_read(&dd->irq_workers_active) != 0) 252462306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 252562306a36Sopenharmony_ci "Completion workers still active!"); 252662306a36Sopenharmony_ci 252762306a36Sopenharmony_ci blk_mq_quiesce_queue(dd->queue); 252862306a36Sopenharmony_ci 252962306a36Sopenharmony_ci blk_mq_tagset_busy_iter(&dd->tags, mtip_queue_cmd, dd); 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_ci set_bit(MTIP_PF_ISSUE_CMDS_BIT, &dd->port->flags); 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci if (mtip_device_reset(dd)) 253462306a36Sopenharmony_ci blk_mq_tagset_busy_iter(&dd->tags, 253562306a36Sopenharmony_ci mtip_abort_cmd, dd); 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci clear_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci blk_mq_unquiesce_queue(dd->queue); 254062306a36Sopenharmony_ci } 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci if (test_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags)) { 254362306a36Sopenharmony_ci slot = 1; 254462306a36Sopenharmony_ci /* used to restrict the loop to one iteration */ 254562306a36Sopenharmony_ci slot_start = num_cmd_slots; 254662306a36Sopenharmony_ci slot_wrap = 0; 254762306a36Sopenharmony_ci while (1) { 254862306a36Sopenharmony_ci slot = find_next_bit(port->cmds_to_issue, 254962306a36Sopenharmony_ci num_cmd_slots, slot); 255062306a36Sopenharmony_ci if (slot_wrap == 1) { 255162306a36Sopenharmony_ci if ((slot_start >= slot) || 255262306a36Sopenharmony_ci (slot >= num_cmd_slots)) 255362306a36Sopenharmony_ci break; 255462306a36Sopenharmony_ci } 255562306a36Sopenharmony_ci if (unlikely(slot_start == num_cmd_slots)) 255662306a36Sopenharmony_ci slot_start = slot; 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_ci if (unlikely(slot == num_cmd_slots)) { 255962306a36Sopenharmony_ci slot = 1; 256062306a36Sopenharmony_ci slot_wrap = 1; 256162306a36Sopenharmony_ci continue; 256262306a36Sopenharmony_ci } 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci /* Issue the command to the hardware */ 256562306a36Sopenharmony_ci mtip_issue_ncq_command(port, slot); 256662306a36Sopenharmony_ci 256762306a36Sopenharmony_ci clear_bit(slot, port->cmds_to_issue); 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci clear_bit(MTIP_PF_ISSUE_CMDS_BIT, &port->flags); 257162306a36Sopenharmony_ci } 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci if (test_bit(MTIP_PF_REBUILD_BIT, &port->flags)) { 257462306a36Sopenharmony_ci if (mtip_ftl_rebuild_poll(dd) == 0) 257562306a36Sopenharmony_ci clear_bit(MTIP_PF_REBUILD_BIT, &port->flags); 257662306a36Sopenharmony_ci } 257762306a36Sopenharmony_ci } 257862306a36Sopenharmony_ci 257962306a36Sopenharmony_cist_out: 258062306a36Sopenharmony_ci return 0; 258162306a36Sopenharmony_ci} 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci/* 258462306a36Sopenharmony_ci * DMA region teardown 258562306a36Sopenharmony_ci * 258662306a36Sopenharmony_ci * @dd Pointer to driver_data structure 258762306a36Sopenharmony_ci * 258862306a36Sopenharmony_ci * return value 258962306a36Sopenharmony_ci * None 259062306a36Sopenharmony_ci */ 259162306a36Sopenharmony_cistatic void mtip_dma_free(struct driver_data *dd) 259262306a36Sopenharmony_ci{ 259362306a36Sopenharmony_ci struct mtip_port *port = dd->port; 259462306a36Sopenharmony_ci 259562306a36Sopenharmony_ci if (port->block1) 259662306a36Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 259762306a36Sopenharmony_ci port->block1, port->block1_dma); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_ci if (port->command_list) { 260062306a36Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, 260162306a36Sopenharmony_ci port->command_list, port->command_list_dma); 260262306a36Sopenharmony_ci } 260362306a36Sopenharmony_ci} 260462306a36Sopenharmony_ci 260562306a36Sopenharmony_ci/* 260662306a36Sopenharmony_ci * DMA region setup 260762306a36Sopenharmony_ci * 260862306a36Sopenharmony_ci * @dd Pointer to driver_data structure 260962306a36Sopenharmony_ci * 261062306a36Sopenharmony_ci * return value 261162306a36Sopenharmony_ci * -ENOMEM Not enough free DMA region space to initialize driver 261262306a36Sopenharmony_ci */ 261362306a36Sopenharmony_cistatic int mtip_dma_alloc(struct driver_data *dd) 261462306a36Sopenharmony_ci{ 261562306a36Sopenharmony_ci struct mtip_port *port = dd->port; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci /* Allocate dma memory for RX Fis, Identify, and Sector Buffer */ 261862306a36Sopenharmony_ci port->block1 = 261962306a36Sopenharmony_ci dma_alloc_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 262062306a36Sopenharmony_ci &port->block1_dma, GFP_KERNEL); 262162306a36Sopenharmony_ci if (!port->block1) 262262306a36Sopenharmony_ci return -ENOMEM; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci /* Allocate dma memory for command list */ 262562306a36Sopenharmony_ci port->command_list = 262662306a36Sopenharmony_ci dma_alloc_coherent(&dd->pdev->dev, AHCI_CMD_TBL_SZ, 262762306a36Sopenharmony_ci &port->command_list_dma, GFP_KERNEL); 262862306a36Sopenharmony_ci if (!port->command_list) { 262962306a36Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, BLOCK_DMA_ALLOC_SZ, 263062306a36Sopenharmony_ci port->block1, port->block1_dma); 263162306a36Sopenharmony_ci port->block1 = NULL; 263262306a36Sopenharmony_ci port->block1_dma = 0; 263362306a36Sopenharmony_ci return -ENOMEM; 263462306a36Sopenharmony_ci } 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci /* Setup all pointers into first DMA region */ 263762306a36Sopenharmony_ci port->rxfis = port->block1 + AHCI_RX_FIS_OFFSET; 263862306a36Sopenharmony_ci port->rxfis_dma = port->block1_dma + AHCI_RX_FIS_OFFSET; 263962306a36Sopenharmony_ci port->identify = port->block1 + AHCI_IDFY_OFFSET; 264062306a36Sopenharmony_ci port->identify_dma = port->block1_dma + AHCI_IDFY_OFFSET; 264162306a36Sopenharmony_ci port->log_buf = port->block1 + AHCI_SECTBUF_OFFSET; 264262306a36Sopenharmony_ci port->log_buf_dma = port->block1_dma + AHCI_SECTBUF_OFFSET; 264362306a36Sopenharmony_ci port->smart_buf = port->block1 + AHCI_SMARTBUF_OFFSET; 264462306a36Sopenharmony_ci port->smart_buf_dma = port->block1_dma + AHCI_SMARTBUF_OFFSET; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci return 0; 264762306a36Sopenharmony_ci} 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_cistatic int mtip_hw_get_identify(struct driver_data *dd) 265062306a36Sopenharmony_ci{ 265162306a36Sopenharmony_ci struct smart_attr attr242; 265262306a36Sopenharmony_ci unsigned char *buf; 265362306a36Sopenharmony_ci int rv; 265462306a36Sopenharmony_ci 265562306a36Sopenharmony_ci if (mtip_get_identify(dd->port, NULL) < 0) 265662306a36Sopenharmony_ci return -EFAULT; 265762306a36Sopenharmony_ci 265862306a36Sopenharmony_ci if (*(dd->port->identify + MTIP_FTL_REBUILD_OFFSET) == 265962306a36Sopenharmony_ci MTIP_FTL_REBUILD_MAGIC) { 266062306a36Sopenharmony_ci set_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags); 266162306a36Sopenharmony_ci return MTIP_FTL_REBUILD_MAGIC; 266262306a36Sopenharmony_ci } 266362306a36Sopenharmony_ci mtip_dump_identify(dd->port); 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci /* check write protect, over temp and rebuild statuses */ 266662306a36Sopenharmony_ci rv = mtip_read_log_page(dd->port, ATA_LOG_SATA_NCQ, 266762306a36Sopenharmony_ci dd->port->log_buf, 266862306a36Sopenharmony_ci dd->port->log_buf_dma, 1); 266962306a36Sopenharmony_ci if (rv) { 267062306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 267162306a36Sopenharmony_ci "Error in READ LOG EXT (10h) command\n"); 267262306a36Sopenharmony_ci /* non-critical error, don't fail the load */ 267362306a36Sopenharmony_ci } else { 267462306a36Sopenharmony_ci buf = (unsigned char *)dd->port->log_buf; 267562306a36Sopenharmony_ci if (buf[259] & 0x1) { 267662306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 267762306a36Sopenharmony_ci "Write protect bit is set.\n"); 267862306a36Sopenharmony_ci set_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag); 267962306a36Sopenharmony_ci } 268062306a36Sopenharmony_ci if (buf[288] == 0xF7) { 268162306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 268262306a36Sopenharmony_ci "Exceeded Tmax, drive in thermal shutdown.\n"); 268362306a36Sopenharmony_ci set_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag); 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci if (buf[288] == 0xBF) { 268662306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 268762306a36Sopenharmony_ci "Drive indicates rebuild has failed.\n"); 268862306a36Sopenharmony_ci set_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag); 268962306a36Sopenharmony_ci } 269062306a36Sopenharmony_ci } 269162306a36Sopenharmony_ci 269262306a36Sopenharmony_ci /* get write protect progess */ 269362306a36Sopenharmony_ci memset(&attr242, 0, sizeof(struct smart_attr)); 269462306a36Sopenharmony_ci if (mtip_get_smart_attr(dd->port, 242, &attr242)) 269562306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 269662306a36Sopenharmony_ci "Unable to check write protect progress\n"); 269762306a36Sopenharmony_ci else 269862306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 269962306a36Sopenharmony_ci "Write protect progress: %u%% (%u blocks)\n", 270062306a36Sopenharmony_ci attr242.cur, le32_to_cpu(attr242.data)); 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci return rv; 270362306a36Sopenharmony_ci} 270462306a36Sopenharmony_ci 270562306a36Sopenharmony_ci/* 270662306a36Sopenharmony_ci * Called once for each card. 270762306a36Sopenharmony_ci * 270862306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 270962306a36Sopenharmony_ci * 271062306a36Sopenharmony_ci * return value 271162306a36Sopenharmony_ci * 0 on success, else an error code. 271262306a36Sopenharmony_ci */ 271362306a36Sopenharmony_cistatic int mtip_hw_init(struct driver_data *dd) 271462306a36Sopenharmony_ci{ 271562306a36Sopenharmony_ci int i; 271662306a36Sopenharmony_ci int rv; 271762306a36Sopenharmony_ci unsigned long timeout, timetaken; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci dd->mmio = pcim_iomap_table(dd->pdev)[MTIP_ABAR]; 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci mtip_detect_product(dd); 272262306a36Sopenharmony_ci if (dd->product_type == MTIP_PRODUCT_UNKNOWN) { 272362306a36Sopenharmony_ci rv = -EIO; 272462306a36Sopenharmony_ci goto out1; 272562306a36Sopenharmony_ci } 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci hba_setup(dd); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci dd->port = kzalloc_node(sizeof(struct mtip_port), GFP_KERNEL, 273062306a36Sopenharmony_ci dd->numa_node); 273162306a36Sopenharmony_ci if (!dd->port) 273262306a36Sopenharmony_ci return -ENOMEM; 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_ci /* Continue workqueue setup */ 273562306a36Sopenharmony_ci for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) 273662306a36Sopenharmony_ci dd->work[i].port = dd->port; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci /* Enable unaligned IO constraints for some devices */ 273962306a36Sopenharmony_ci if (mtip_device_unaligned_constrained(dd)) 274062306a36Sopenharmony_ci dd->unal_qdepth = MTIP_MAX_UNALIGNED_SLOTS; 274162306a36Sopenharmony_ci else 274262306a36Sopenharmony_ci dd->unal_qdepth = 0; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci atomic_set(&dd->port->cmd_slot_unal, dd->unal_qdepth); 274562306a36Sopenharmony_ci 274662306a36Sopenharmony_ci /* Spinlock to prevent concurrent issue */ 274762306a36Sopenharmony_ci for (i = 0; i < MTIP_MAX_SLOT_GROUPS; i++) 274862306a36Sopenharmony_ci spin_lock_init(&dd->port->cmd_issue_lock[i]); 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci /* Set the port mmio base address. */ 275162306a36Sopenharmony_ci dd->port->mmio = dd->mmio + PORT_OFFSET; 275262306a36Sopenharmony_ci dd->port->dd = dd; 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci /* DMA allocations */ 275562306a36Sopenharmony_ci rv = mtip_dma_alloc(dd); 275662306a36Sopenharmony_ci if (rv < 0) 275762306a36Sopenharmony_ci goto out1; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci /* Setup the pointers to the extended s_active and CI registers. */ 276062306a36Sopenharmony_ci for (i = 0; i < dd->slot_groups; i++) { 276162306a36Sopenharmony_ci dd->port->s_active[i] = 276262306a36Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_SCR_ACT; 276362306a36Sopenharmony_ci dd->port->cmd_issue[i] = 276462306a36Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_COMMAND_ISSUE; 276562306a36Sopenharmony_ci dd->port->completed[i] = 276662306a36Sopenharmony_ci dd->port->mmio + i*0x80 + PORT_SDBV; 276762306a36Sopenharmony_ci } 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci timetaken = jiffies; 277062306a36Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(30000); 277162306a36Sopenharmony_ci while (((readl(dd->port->mmio + PORT_SCR_STAT) & 0x0F) != 0x03) && 277262306a36Sopenharmony_ci time_before(jiffies, timeout)) { 277362306a36Sopenharmony_ci mdelay(100); 277462306a36Sopenharmony_ci } 277562306a36Sopenharmony_ci if (unlikely(mtip_check_surprise_removal(dd))) { 277662306a36Sopenharmony_ci timetaken = jiffies - timetaken; 277762306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 277862306a36Sopenharmony_ci "Surprise removal detected at %u ms\n", 277962306a36Sopenharmony_ci jiffies_to_msecs(timetaken)); 278062306a36Sopenharmony_ci rv = -ENODEV; 278162306a36Sopenharmony_ci goto out2 ; 278262306a36Sopenharmony_ci } 278362306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) { 278462306a36Sopenharmony_ci timetaken = jiffies - timetaken; 278562306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 278662306a36Sopenharmony_ci "Removal detected at %u ms\n", 278762306a36Sopenharmony_ci jiffies_to_msecs(timetaken)); 278862306a36Sopenharmony_ci rv = -EFAULT; 278962306a36Sopenharmony_ci goto out2; 279062306a36Sopenharmony_ci } 279162306a36Sopenharmony_ci 279262306a36Sopenharmony_ci /* Conditionally reset the HBA. */ 279362306a36Sopenharmony_ci if (!(readl(dd->mmio + HOST_CAP) & HOST_CAP_NZDMA)) { 279462306a36Sopenharmony_ci if (mtip_hba_reset(dd) < 0) { 279562306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 279662306a36Sopenharmony_ci "Card did not reset within timeout\n"); 279762306a36Sopenharmony_ci rv = -EIO; 279862306a36Sopenharmony_ci goto out2; 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci } else { 280162306a36Sopenharmony_ci /* Clear any pending interrupts on the HBA */ 280262306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_IRQ_STAT), 280362306a36Sopenharmony_ci dd->mmio + HOST_IRQ_STAT); 280462306a36Sopenharmony_ci } 280562306a36Sopenharmony_ci 280662306a36Sopenharmony_ci mtip_init_port(dd->port); 280762306a36Sopenharmony_ci mtip_start_port(dd->port); 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci /* Setup the ISR and enable interrupts. */ 281062306a36Sopenharmony_ci rv = request_irq(dd->pdev->irq, mtip_irq_handler, IRQF_SHARED, 281162306a36Sopenharmony_ci dev_driver_string(&dd->pdev->dev), dd); 281262306a36Sopenharmony_ci if (rv) { 281362306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 281462306a36Sopenharmony_ci "Unable to allocate IRQ %d\n", dd->pdev->irq); 281562306a36Sopenharmony_ci goto out2; 281662306a36Sopenharmony_ci } 281762306a36Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, get_cpu_mask(dd->isr_binding)); 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ci /* Enable interrupts on the HBA. */ 282062306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 282162306a36Sopenharmony_ci dd->mmio + HOST_CTL); 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci init_waitqueue_head(&dd->port->svc_wait); 282462306a36Sopenharmony_ci 282562306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) { 282662306a36Sopenharmony_ci rv = -EFAULT; 282762306a36Sopenharmony_ci goto out3; 282862306a36Sopenharmony_ci } 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci return rv; 283162306a36Sopenharmony_ci 283262306a36Sopenharmony_ciout3: 283362306a36Sopenharmony_ci /* Disable interrupts on the HBA. */ 283462306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 283562306a36Sopenharmony_ci dd->mmio + HOST_CTL); 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_ci /* Release the IRQ. */ 283862306a36Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, NULL); 283962306a36Sopenharmony_ci free_irq(dd->pdev->irq, dd); 284062306a36Sopenharmony_ci 284162306a36Sopenharmony_ciout2: 284262306a36Sopenharmony_ci mtip_deinit_port(dd->port); 284362306a36Sopenharmony_ci mtip_dma_free(dd); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ciout1: 284662306a36Sopenharmony_ci /* Free the memory allocated for the for structure. */ 284762306a36Sopenharmony_ci kfree(dd->port); 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci return rv; 285062306a36Sopenharmony_ci} 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_cistatic int mtip_standby_drive(struct driver_data *dd) 285362306a36Sopenharmony_ci{ 285462306a36Sopenharmony_ci int rv = 0; 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci if (dd->sr || !dd->port) 285762306a36Sopenharmony_ci return -ENODEV; 285862306a36Sopenharmony_ci /* 285962306a36Sopenharmony_ci * Send standby immediate (E0h) to the drive so that it 286062306a36Sopenharmony_ci * saves its state. 286162306a36Sopenharmony_ci */ 286262306a36Sopenharmony_ci if (!test_bit(MTIP_PF_REBUILD_BIT, &dd->port->flags) && 286362306a36Sopenharmony_ci !test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag) && 286462306a36Sopenharmony_ci !test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) { 286562306a36Sopenharmony_ci rv = mtip_standby_immediate(dd->port); 286662306a36Sopenharmony_ci if (rv) 286762306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 286862306a36Sopenharmony_ci "STANDBY IMMEDIATE failed\n"); 286962306a36Sopenharmony_ci } 287062306a36Sopenharmony_ci return rv; 287162306a36Sopenharmony_ci} 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci/* 287462306a36Sopenharmony_ci * Called to deinitialize an interface. 287562306a36Sopenharmony_ci * 287662306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 287762306a36Sopenharmony_ci * 287862306a36Sopenharmony_ci * return value 287962306a36Sopenharmony_ci * 0 288062306a36Sopenharmony_ci */ 288162306a36Sopenharmony_cistatic int mtip_hw_exit(struct driver_data *dd) 288262306a36Sopenharmony_ci{ 288362306a36Sopenharmony_ci if (!dd->sr) { 288462306a36Sopenharmony_ci /* de-initialize the port. */ 288562306a36Sopenharmony_ci mtip_deinit_port(dd->port); 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_ci /* Disable interrupts on the HBA. */ 288862306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 288962306a36Sopenharmony_ci dd->mmio + HOST_CTL); 289062306a36Sopenharmony_ci } 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci /* Release the IRQ. */ 289362306a36Sopenharmony_ci irq_set_affinity_hint(dd->pdev->irq, NULL); 289462306a36Sopenharmony_ci free_irq(dd->pdev->irq, dd); 289562306a36Sopenharmony_ci msleep(1000); 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_ci /* Free dma regions */ 289862306a36Sopenharmony_ci mtip_dma_free(dd); 289962306a36Sopenharmony_ci 290062306a36Sopenharmony_ci /* Free the memory allocated for the for structure. */ 290162306a36Sopenharmony_ci kfree(dd->port); 290262306a36Sopenharmony_ci dd->port = NULL; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci return 0; 290562306a36Sopenharmony_ci} 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci/* 290862306a36Sopenharmony_ci * Issue a Standby Immediate command to the device. 290962306a36Sopenharmony_ci * 291062306a36Sopenharmony_ci * This function is called by the Block Layer just before the 291162306a36Sopenharmony_ci * system powers off during a shutdown. 291262306a36Sopenharmony_ci * 291362306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 291462306a36Sopenharmony_ci * 291562306a36Sopenharmony_ci * return value 291662306a36Sopenharmony_ci * 0 291762306a36Sopenharmony_ci */ 291862306a36Sopenharmony_cistatic int mtip_hw_shutdown(struct driver_data *dd) 291962306a36Sopenharmony_ci{ 292062306a36Sopenharmony_ci /* 292162306a36Sopenharmony_ci * Send standby immediate (E0h) to the drive so that it 292262306a36Sopenharmony_ci * saves its state. 292362306a36Sopenharmony_ci */ 292462306a36Sopenharmony_ci mtip_standby_drive(dd); 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci return 0; 292762306a36Sopenharmony_ci} 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci/* 293062306a36Sopenharmony_ci * Suspend function 293162306a36Sopenharmony_ci * 293262306a36Sopenharmony_ci * This function is called by the Block Layer just before the 293362306a36Sopenharmony_ci * system hibernates. 293462306a36Sopenharmony_ci * 293562306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 293662306a36Sopenharmony_ci * 293762306a36Sopenharmony_ci * return value 293862306a36Sopenharmony_ci * 0 Suspend was successful 293962306a36Sopenharmony_ci * -EFAULT Suspend was not successful 294062306a36Sopenharmony_ci */ 294162306a36Sopenharmony_cistatic int mtip_hw_suspend(struct driver_data *dd) 294262306a36Sopenharmony_ci{ 294362306a36Sopenharmony_ci /* 294462306a36Sopenharmony_ci * Send standby immediate (E0h) to the drive 294562306a36Sopenharmony_ci * so that it saves its state. 294662306a36Sopenharmony_ci */ 294762306a36Sopenharmony_ci if (mtip_standby_drive(dd) != 0) { 294862306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 294962306a36Sopenharmony_ci "Failed standby-immediate command\n"); 295062306a36Sopenharmony_ci return -EFAULT; 295162306a36Sopenharmony_ci } 295262306a36Sopenharmony_ci 295362306a36Sopenharmony_ci /* Disable interrupts on the HBA.*/ 295462306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) & ~HOST_IRQ_EN, 295562306a36Sopenharmony_ci dd->mmio + HOST_CTL); 295662306a36Sopenharmony_ci mtip_deinit_port(dd->port); 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci return 0; 295962306a36Sopenharmony_ci} 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci/* 296262306a36Sopenharmony_ci * Resume function 296362306a36Sopenharmony_ci * 296462306a36Sopenharmony_ci * This function is called by the Block Layer as the 296562306a36Sopenharmony_ci * system resumes. 296662306a36Sopenharmony_ci * 296762306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 296862306a36Sopenharmony_ci * 296962306a36Sopenharmony_ci * return value 297062306a36Sopenharmony_ci * 0 Resume was successful 297162306a36Sopenharmony_ci * -EFAULT Resume was not successful 297262306a36Sopenharmony_ci */ 297362306a36Sopenharmony_cistatic int mtip_hw_resume(struct driver_data *dd) 297462306a36Sopenharmony_ci{ 297562306a36Sopenharmony_ci /* Perform any needed hardware setup steps */ 297662306a36Sopenharmony_ci hba_setup(dd); 297762306a36Sopenharmony_ci 297862306a36Sopenharmony_ci /* Reset the HBA */ 297962306a36Sopenharmony_ci if (mtip_hba_reset(dd) != 0) { 298062306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 298162306a36Sopenharmony_ci "Unable to reset the HBA\n"); 298262306a36Sopenharmony_ci return -EFAULT; 298362306a36Sopenharmony_ci } 298462306a36Sopenharmony_ci 298562306a36Sopenharmony_ci /* 298662306a36Sopenharmony_ci * Enable the port, DMA engine, and FIS reception specific 298762306a36Sopenharmony_ci * h/w in controller. 298862306a36Sopenharmony_ci */ 298962306a36Sopenharmony_ci mtip_init_port(dd->port); 299062306a36Sopenharmony_ci mtip_start_port(dd->port); 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci /* Enable interrupts on the HBA.*/ 299362306a36Sopenharmony_ci writel(readl(dd->mmio + HOST_CTL) | HOST_IRQ_EN, 299462306a36Sopenharmony_ci dd->mmio + HOST_CTL); 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci return 0; 299762306a36Sopenharmony_ci} 299862306a36Sopenharmony_ci 299962306a36Sopenharmony_ci/* 300062306a36Sopenharmony_ci * Helper function for reusing disk name 300162306a36Sopenharmony_ci * upon hot insertion. 300262306a36Sopenharmony_ci */ 300362306a36Sopenharmony_cistatic int rssd_disk_name_format(char *prefix, 300462306a36Sopenharmony_ci int index, 300562306a36Sopenharmony_ci char *buf, 300662306a36Sopenharmony_ci int buflen) 300762306a36Sopenharmony_ci{ 300862306a36Sopenharmony_ci const int base = 'z' - 'a' + 1; 300962306a36Sopenharmony_ci char *begin = buf + strlen(prefix); 301062306a36Sopenharmony_ci char *end = buf + buflen; 301162306a36Sopenharmony_ci char *p; 301262306a36Sopenharmony_ci int unit; 301362306a36Sopenharmony_ci 301462306a36Sopenharmony_ci p = end - 1; 301562306a36Sopenharmony_ci *p = '\0'; 301662306a36Sopenharmony_ci unit = base; 301762306a36Sopenharmony_ci do { 301862306a36Sopenharmony_ci if (p == begin) 301962306a36Sopenharmony_ci return -EINVAL; 302062306a36Sopenharmony_ci *--p = 'a' + (index % unit); 302162306a36Sopenharmony_ci index = (index / unit) - 1; 302262306a36Sopenharmony_ci } while (index >= 0); 302362306a36Sopenharmony_ci 302462306a36Sopenharmony_ci memmove(begin, p, end - p); 302562306a36Sopenharmony_ci memcpy(buf, prefix, strlen(prefix)); 302662306a36Sopenharmony_ci 302762306a36Sopenharmony_ci return 0; 302862306a36Sopenharmony_ci} 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_ci/* 303162306a36Sopenharmony_ci * Block layer IOCTL handler. 303262306a36Sopenharmony_ci * 303362306a36Sopenharmony_ci * @dev Pointer to the block_device structure. 303462306a36Sopenharmony_ci * @mode ignored 303562306a36Sopenharmony_ci * @cmd IOCTL command passed from the user application. 303662306a36Sopenharmony_ci * @arg Argument passed from the user application. 303762306a36Sopenharmony_ci * 303862306a36Sopenharmony_ci * return value 303962306a36Sopenharmony_ci * 0 IOCTL completed successfully. 304062306a36Sopenharmony_ci * -ENOTTY IOCTL not supported or invalid driver data 304162306a36Sopenharmony_ci * structure pointer. 304262306a36Sopenharmony_ci */ 304362306a36Sopenharmony_cistatic int mtip_block_ioctl(struct block_device *dev, 304462306a36Sopenharmony_ci blk_mode_t mode, 304562306a36Sopenharmony_ci unsigned cmd, 304662306a36Sopenharmony_ci unsigned long arg) 304762306a36Sopenharmony_ci{ 304862306a36Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 304962306a36Sopenharmony_ci 305062306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 305162306a36Sopenharmony_ci return -EACCES; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci if (!dd) 305462306a36Sopenharmony_ci return -ENOTTY; 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) 305762306a36Sopenharmony_ci return -ENOTTY; 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci switch (cmd) { 306062306a36Sopenharmony_ci case BLKFLSBUF: 306162306a36Sopenharmony_ci return -ENOTTY; 306262306a36Sopenharmony_ci default: 306362306a36Sopenharmony_ci return mtip_hw_ioctl(dd, cmd, arg); 306462306a36Sopenharmony_ci } 306562306a36Sopenharmony_ci} 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 306862306a36Sopenharmony_ci/* 306962306a36Sopenharmony_ci * Block layer compat IOCTL handler. 307062306a36Sopenharmony_ci * 307162306a36Sopenharmony_ci * @dev Pointer to the block_device structure. 307262306a36Sopenharmony_ci * @mode ignored 307362306a36Sopenharmony_ci * @cmd IOCTL command passed from the user application. 307462306a36Sopenharmony_ci * @arg Argument passed from the user application. 307562306a36Sopenharmony_ci * 307662306a36Sopenharmony_ci * return value 307762306a36Sopenharmony_ci * 0 IOCTL completed successfully. 307862306a36Sopenharmony_ci * -ENOTTY IOCTL not supported or invalid driver data 307962306a36Sopenharmony_ci * structure pointer. 308062306a36Sopenharmony_ci */ 308162306a36Sopenharmony_cistatic int mtip_block_compat_ioctl(struct block_device *dev, 308262306a36Sopenharmony_ci blk_mode_t mode, 308362306a36Sopenharmony_ci unsigned cmd, 308462306a36Sopenharmony_ci unsigned long arg) 308562306a36Sopenharmony_ci{ 308662306a36Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 308762306a36Sopenharmony_ci 308862306a36Sopenharmony_ci if (!capable(CAP_SYS_ADMIN)) 308962306a36Sopenharmony_ci return -EACCES; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci if (!dd) 309262306a36Sopenharmony_ci return -ENOTTY; 309362306a36Sopenharmony_ci 309462306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag))) 309562306a36Sopenharmony_ci return -ENOTTY; 309662306a36Sopenharmony_ci 309762306a36Sopenharmony_ci switch (cmd) { 309862306a36Sopenharmony_ci case BLKFLSBUF: 309962306a36Sopenharmony_ci return -ENOTTY; 310062306a36Sopenharmony_ci case HDIO_DRIVE_TASKFILE: { 310162306a36Sopenharmony_ci struct mtip_compat_ide_task_request_s __user *compat_req_task; 310262306a36Sopenharmony_ci ide_task_request_t req_task; 310362306a36Sopenharmony_ci int compat_tasksize, outtotal, ret; 310462306a36Sopenharmony_ci 310562306a36Sopenharmony_ci compat_tasksize = 310662306a36Sopenharmony_ci sizeof(struct mtip_compat_ide_task_request_s); 310762306a36Sopenharmony_ci 310862306a36Sopenharmony_ci compat_req_task = 310962306a36Sopenharmony_ci (struct mtip_compat_ide_task_request_s __user *) arg; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci if (copy_from_user(&req_task, (void __user *) arg, 311262306a36Sopenharmony_ci compat_tasksize - (2 * sizeof(compat_long_t)))) 311362306a36Sopenharmony_ci return -EFAULT; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci if (get_user(req_task.out_size, &compat_req_task->out_size)) 311662306a36Sopenharmony_ci return -EFAULT; 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_ci if (get_user(req_task.in_size, &compat_req_task->in_size)) 311962306a36Sopenharmony_ci return -EFAULT; 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci outtotal = sizeof(struct mtip_compat_ide_task_request_s); 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci ret = exec_drive_taskfile(dd, (void __user *) arg, 312462306a36Sopenharmony_ci &req_task, outtotal); 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ci if (copy_to_user((void __user *) arg, &req_task, 312762306a36Sopenharmony_ci compat_tasksize - 312862306a36Sopenharmony_ci (2 * sizeof(compat_long_t)))) 312962306a36Sopenharmony_ci return -EFAULT; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci if (put_user(req_task.out_size, &compat_req_task->out_size)) 313262306a36Sopenharmony_ci return -EFAULT; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci if (put_user(req_task.in_size, &compat_req_task->in_size)) 313562306a36Sopenharmony_ci return -EFAULT; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci return ret; 313862306a36Sopenharmony_ci } 313962306a36Sopenharmony_ci default: 314062306a36Sopenharmony_ci return mtip_hw_ioctl(dd, cmd, arg); 314162306a36Sopenharmony_ci } 314262306a36Sopenharmony_ci} 314362306a36Sopenharmony_ci#endif 314462306a36Sopenharmony_ci 314562306a36Sopenharmony_ci/* 314662306a36Sopenharmony_ci * Obtain the geometry of the device. 314762306a36Sopenharmony_ci * 314862306a36Sopenharmony_ci * You may think that this function is obsolete, but some applications, 314962306a36Sopenharmony_ci * fdisk for example still used CHS values. This function describes the 315062306a36Sopenharmony_ci * device as having 224 heads and 56 sectors per cylinder. These values are 315162306a36Sopenharmony_ci * chosen so that each cylinder is aligned on a 4KB boundary. Since a 315262306a36Sopenharmony_ci * partition is described in terms of a start and end cylinder this means 315362306a36Sopenharmony_ci * that each partition is also 4KB aligned. Non-aligned partitions adversely 315462306a36Sopenharmony_ci * affects performance. 315562306a36Sopenharmony_ci * 315662306a36Sopenharmony_ci * @dev Pointer to the block_device strucutre. 315762306a36Sopenharmony_ci * @geo Pointer to a hd_geometry structure. 315862306a36Sopenharmony_ci * 315962306a36Sopenharmony_ci * return value 316062306a36Sopenharmony_ci * 0 Operation completed successfully. 316162306a36Sopenharmony_ci * -ENOTTY An error occurred while reading the drive capacity. 316262306a36Sopenharmony_ci */ 316362306a36Sopenharmony_cistatic int mtip_block_getgeo(struct block_device *dev, 316462306a36Sopenharmony_ci struct hd_geometry *geo) 316562306a36Sopenharmony_ci{ 316662306a36Sopenharmony_ci struct driver_data *dd = dev->bd_disk->private_data; 316762306a36Sopenharmony_ci sector_t capacity; 316862306a36Sopenharmony_ci 316962306a36Sopenharmony_ci if (!dd) 317062306a36Sopenharmony_ci return -ENOTTY; 317162306a36Sopenharmony_ci 317262306a36Sopenharmony_ci if (!(mtip_hw_get_capacity(dd, &capacity))) { 317362306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 317462306a36Sopenharmony_ci "Could not get drive capacity.\n"); 317562306a36Sopenharmony_ci return -ENOTTY; 317662306a36Sopenharmony_ci } 317762306a36Sopenharmony_ci 317862306a36Sopenharmony_ci geo->heads = 224; 317962306a36Sopenharmony_ci geo->sectors = 56; 318062306a36Sopenharmony_ci sector_div(capacity, (geo->heads * geo->sectors)); 318162306a36Sopenharmony_ci geo->cylinders = capacity; 318262306a36Sopenharmony_ci return 0; 318362306a36Sopenharmony_ci} 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_cistatic void mtip_block_free_disk(struct gendisk *disk) 318662306a36Sopenharmony_ci{ 318762306a36Sopenharmony_ci struct driver_data *dd = disk->private_data; 318862306a36Sopenharmony_ci 318962306a36Sopenharmony_ci ida_free(&rssd_index_ida, dd->index); 319062306a36Sopenharmony_ci kfree(dd); 319162306a36Sopenharmony_ci} 319262306a36Sopenharmony_ci 319362306a36Sopenharmony_ci/* 319462306a36Sopenharmony_ci * Block device operation function. 319562306a36Sopenharmony_ci * 319662306a36Sopenharmony_ci * This structure contains pointers to the functions required by the block 319762306a36Sopenharmony_ci * layer. 319862306a36Sopenharmony_ci */ 319962306a36Sopenharmony_cistatic const struct block_device_operations mtip_block_ops = { 320062306a36Sopenharmony_ci .ioctl = mtip_block_ioctl, 320162306a36Sopenharmony_ci#ifdef CONFIG_COMPAT 320262306a36Sopenharmony_ci .compat_ioctl = mtip_block_compat_ioctl, 320362306a36Sopenharmony_ci#endif 320462306a36Sopenharmony_ci .getgeo = mtip_block_getgeo, 320562306a36Sopenharmony_ci .free_disk = mtip_block_free_disk, 320662306a36Sopenharmony_ci .owner = THIS_MODULE 320762306a36Sopenharmony_ci}; 320862306a36Sopenharmony_ci 320962306a36Sopenharmony_cistatic inline bool is_se_active(struct driver_data *dd) 321062306a36Sopenharmony_ci{ 321162306a36Sopenharmony_ci if (unlikely(test_bit(MTIP_PF_SE_ACTIVE_BIT, &dd->port->flags))) { 321262306a36Sopenharmony_ci if (dd->port->ic_pause_timer) { 321362306a36Sopenharmony_ci unsigned long to = dd->port->ic_pause_timer + 321462306a36Sopenharmony_ci msecs_to_jiffies(1000); 321562306a36Sopenharmony_ci if (time_after(jiffies, to)) { 321662306a36Sopenharmony_ci clear_bit(MTIP_PF_SE_ACTIVE_BIT, 321762306a36Sopenharmony_ci &dd->port->flags); 321862306a36Sopenharmony_ci clear_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag); 321962306a36Sopenharmony_ci dd->port->ic_pause_timer = 0; 322062306a36Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 322162306a36Sopenharmony_ci return false; 322262306a36Sopenharmony_ci } 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci return true; 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci return false; 322762306a36Sopenharmony_ci} 322862306a36Sopenharmony_ci 322962306a36Sopenharmony_cistatic inline bool is_stopped(struct driver_data *dd, struct request *rq) 323062306a36Sopenharmony_ci{ 323162306a36Sopenharmony_ci if (likely(!(dd->dd_flag & MTIP_DDF_STOP_IO))) 323262306a36Sopenharmony_ci return false; 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag)) 323562306a36Sopenharmony_ci return true; 323662306a36Sopenharmony_ci if (test_bit(MTIP_DDF_OVER_TEMP_BIT, &dd->dd_flag)) 323762306a36Sopenharmony_ci return true; 323862306a36Sopenharmony_ci if (test_bit(MTIP_DDF_WRITE_PROTECT_BIT, &dd->dd_flag) && 323962306a36Sopenharmony_ci rq_data_dir(rq)) 324062306a36Sopenharmony_ci return true; 324162306a36Sopenharmony_ci if (test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag)) 324262306a36Sopenharmony_ci return true; 324362306a36Sopenharmony_ci if (test_bit(MTIP_DDF_REBUILD_FAILED_BIT, &dd->dd_flag)) 324462306a36Sopenharmony_ci return true; 324562306a36Sopenharmony_ci 324662306a36Sopenharmony_ci return false; 324762306a36Sopenharmony_ci} 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_cistatic bool mtip_check_unal_depth(struct blk_mq_hw_ctx *hctx, 325062306a36Sopenharmony_ci struct request *rq) 325162306a36Sopenharmony_ci{ 325262306a36Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 325362306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 325462306a36Sopenharmony_ci 325562306a36Sopenharmony_ci if (rq_data_dir(rq) == READ || !dd->unal_qdepth) 325662306a36Sopenharmony_ci return false; 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci /* 325962306a36Sopenharmony_ci * If unaligned depth must be limited on this controller, mark it 326062306a36Sopenharmony_ci * as unaligned if the IO isn't on a 4k boundary (start of length). 326162306a36Sopenharmony_ci */ 326262306a36Sopenharmony_ci if (blk_rq_sectors(rq) <= 64) { 326362306a36Sopenharmony_ci if ((blk_rq_pos(rq) & 7) || (blk_rq_sectors(rq) & 7)) 326462306a36Sopenharmony_ci cmd->unaligned = 1; 326562306a36Sopenharmony_ci } 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci if (cmd->unaligned && atomic_dec_if_positive(&dd->port->cmd_slot_unal) >= 0) 326862306a36Sopenharmony_ci return true; 326962306a36Sopenharmony_ci 327062306a36Sopenharmony_ci return false; 327162306a36Sopenharmony_ci} 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_cistatic blk_status_t mtip_issue_reserved_cmd(struct blk_mq_hw_ctx *hctx, 327462306a36Sopenharmony_ci struct request *rq) 327562306a36Sopenharmony_ci{ 327662306a36Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 327762306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 327862306a36Sopenharmony_ci struct mtip_int_cmd *icmd = cmd->icmd; 327962306a36Sopenharmony_ci struct mtip_cmd_hdr *hdr = 328062306a36Sopenharmony_ci dd->port->command_list + sizeof(struct mtip_cmd_hdr) * rq->tag; 328162306a36Sopenharmony_ci struct mtip_cmd_sg *command_sg; 328262306a36Sopenharmony_ci 328362306a36Sopenharmony_ci if (mtip_commands_active(dd->port)) 328462306a36Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci hdr->ctba = cpu_to_le32(cmd->command_dma & 0xFFFFFFFF); 328762306a36Sopenharmony_ci if (test_bit(MTIP_PF_HOST_CAP_64, &dd->port->flags)) 328862306a36Sopenharmony_ci hdr->ctbau = cpu_to_le32((cmd->command_dma >> 16) >> 16); 328962306a36Sopenharmony_ci /* Populate the SG list */ 329062306a36Sopenharmony_ci hdr->opts = cpu_to_le32(icmd->opts | icmd->fis_len); 329162306a36Sopenharmony_ci if (icmd->buf_len) { 329262306a36Sopenharmony_ci command_sg = cmd->command + AHCI_CMD_TBL_HDR_SZ; 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci command_sg->info = cpu_to_le32((icmd->buf_len-1) & 0x3FFFFF); 329562306a36Sopenharmony_ci command_sg->dba = cpu_to_le32(icmd->buffer & 0xFFFFFFFF); 329662306a36Sopenharmony_ci command_sg->dba_upper = 329762306a36Sopenharmony_ci cpu_to_le32((icmd->buffer >> 16) >> 16); 329862306a36Sopenharmony_ci 329962306a36Sopenharmony_ci hdr->opts |= cpu_to_le32((1 << 16)); 330062306a36Sopenharmony_ci } 330162306a36Sopenharmony_ci 330262306a36Sopenharmony_ci /* Populate the command header */ 330362306a36Sopenharmony_ci hdr->byte_count = 0; 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci blk_mq_start_request(rq); 330662306a36Sopenharmony_ci mtip_issue_non_ncq_command(dd->port, rq->tag); 330762306a36Sopenharmony_ci return 0; 330862306a36Sopenharmony_ci} 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_cistatic blk_status_t mtip_queue_rq(struct blk_mq_hw_ctx *hctx, 331162306a36Sopenharmony_ci const struct blk_mq_queue_data *bd) 331262306a36Sopenharmony_ci{ 331362306a36Sopenharmony_ci struct driver_data *dd = hctx->queue->queuedata; 331462306a36Sopenharmony_ci struct request *rq = bd->rq; 331562306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci if (blk_rq_is_passthrough(rq)) 331862306a36Sopenharmony_ci return mtip_issue_reserved_cmd(hctx, rq); 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci if (unlikely(mtip_check_unal_depth(hctx, rq))) 332162306a36Sopenharmony_ci return BLK_STS_DEV_RESOURCE; 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci if (is_se_active(dd) || is_stopped(dd, rq)) 332462306a36Sopenharmony_ci return BLK_STS_IOERR; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci blk_mq_start_request(rq); 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci mtip_hw_submit_io(dd, rq, cmd, hctx); 332962306a36Sopenharmony_ci return BLK_STS_OK; 333062306a36Sopenharmony_ci} 333162306a36Sopenharmony_ci 333262306a36Sopenharmony_cistatic void mtip_free_cmd(struct blk_mq_tag_set *set, struct request *rq, 333362306a36Sopenharmony_ci unsigned int hctx_idx) 333462306a36Sopenharmony_ci{ 333562306a36Sopenharmony_ci struct driver_data *dd = set->driver_data; 333662306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_ci if (!cmd->command) 333962306a36Sopenharmony_ci return; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci dma_free_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, cmd->command, 334262306a36Sopenharmony_ci cmd->command_dma); 334362306a36Sopenharmony_ci} 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_cistatic int mtip_init_cmd(struct blk_mq_tag_set *set, struct request *rq, 334662306a36Sopenharmony_ci unsigned int hctx_idx, unsigned int numa_node) 334762306a36Sopenharmony_ci{ 334862306a36Sopenharmony_ci struct driver_data *dd = set->driver_data; 334962306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci cmd->command = dma_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, 335262306a36Sopenharmony_ci &cmd->command_dma, GFP_KERNEL); 335362306a36Sopenharmony_ci if (!cmd->command) 335462306a36Sopenharmony_ci return -ENOMEM; 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci sg_init_table(cmd->sg, MTIP_MAX_SG); 335762306a36Sopenharmony_ci return 0; 335862306a36Sopenharmony_ci} 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_cistatic enum blk_eh_timer_return mtip_cmd_timeout(struct request *req) 336162306a36Sopenharmony_ci{ 336262306a36Sopenharmony_ci struct driver_data *dd = req->q->queuedata; 336362306a36Sopenharmony_ci 336462306a36Sopenharmony_ci if (blk_mq_is_reserved_rq(req)) { 336562306a36Sopenharmony_ci struct mtip_cmd *cmd = blk_mq_rq_to_pdu(req); 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci cmd->status = BLK_STS_TIMEOUT; 336862306a36Sopenharmony_ci blk_mq_complete_request(req); 336962306a36Sopenharmony_ci return BLK_EH_DONE; 337062306a36Sopenharmony_ci } 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci if (test_bit(req->tag, dd->port->cmds_to_issue)) 337362306a36Sopenharmony_ci goto exit_handler; 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci if (test_and_set_bit(MTIP_PF_TO_ACTIVE_BIT, &dd->port->flags)) 337662306a36Sopenharmony_ci goto exit_handler; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 337962306a36Sopenharmony_ciexit_handler: 338062306a36Sopenharmony_ci return BLK_EH_RESET_TIMER; 338162306a36Sopenharmony_ci} 338262306a36Sopenharmony_ci 338362306a36Sopenharmony_cistatic const struct blk_mq_ops mtip_mq_ops = { 338462306a36Sopenharmony_ci .queue_rq = mtip_queue_rq, 338562306a36Sopenharmony_ci .init_request = mtip_init_cmd, 338662306a36Sopenharmony_ci .exit_request = mtip_free_cmd, 338762306a36Sopenharmony_ci .complete = mtip_softirq_done_fn, 338862306a36Sopenharmony_ci .timeout = mtip_cmd_timeout, 338962306a36Sopenharmony_ci}; 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_ci/* 339262306a36Sopenharmony_ci * Block layer initialization function. 339362306a36Sopenharmony_ci * 339462306a36Sopenharmony_ci * This function is called once by the PCI layer for each P320 339562306a36Sopenharmony_ci * device that is connected to the system. 339662306a36Sopenharmony_ci * 339762306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 339862306a36Sopenharmony_ci * 339962306a36Sopenharmony_ci * return value 340062306a36Sopenharmony_ci * 0 on success else an error code. 340162306a36Sopenharmony_ci */ 340262306a36Sopenharmony_cistatic int mtip_block_initialize(struct driver_data *dd) 340362306a36Sopenharmony_ci{ 340462306a36Sopenharmony_ci int rv = 0, wait_for_rebuild = 0; 340562306a36Sopenharmony_ci sector_t capacity; 340662306a36Sopenharmony_ci unsigned int index = 0; 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_ci if (dd->disk) 340962306a36Sopenharmony_ci goto skip_create_disk; /* hw init done, before rebuild */ 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if (mtip_hw_init(dd)) { 341262306a36Sopenharmony_ci rv = -EINVAL; 341362306a36Sopenharmony_ci goto protocol_init_error; 341462306a36Sopenharmony_ci } 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci memset(&dd->tags, 0, sizeof(dd->tags)); 341762306a36Sopenharmony_ci dd->tags.ops = &mtip_mq_ops; 341862306a36Sopenharmony_ci dd->tags.nr_hw_queues = 1; 341962306a36Sopenharmony_ci dd->tags.queue_depth = MTIP_MAX_COMMAND_SLOTS; 342062306a36Sopenharmony_ci dd->tags.reserved_tags = 1; 342162306a36Sopenharmony_ci dd->tags.cmd_size = sizeof(struct mtip_cmd); 342262306a36Sopenharmony_ci dd->tags.numa_node = dd->numa_node; 342362306a36Sopenharmony_ci dd->tags.flags = BLK_MQ_F_SHOULD_MERGE; 342462306a36Sopenharmony_ci dd->tags.driver_data = dd; 342562306a36Sopenharmony_ci dd->tags.timeout = MTIP_NCQ_CMD_TIMEOUT_MS; 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci rv = blk_mq_alloc_tag_set(&dd->tags); 342862306a36Sopenharmony_ci if (rv) { 342962306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 343062306a36Sopenharmony_ci "Unable to allocate request queue\n"); 343162306a36Sopenharmony_ci goto block_queue_alloc_tag_error; 343262306a36Sopenharmony_ci } 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci dd->disk = blk_mq_alloc_disk(&dd->tags, dd); 343562306a36Sopenharmony_ci if (IS_ERR(dd->disk)) { 343662306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 343762306a36Sopenharmony_ci "Unable to allocate request queue\n"); 343862306a36Sopenharmony_ci rv = -ENOMEM; 343962306a36Sopenharmony_ci goto block_queue_alloc_init_error; 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci dd->queue = dd->disk->queue; 344262306a36Sopenharmony_ci 344362306a36Sopenharmony_ci rv = ida_alloc(&rssd_index_ida, GFP_KERNEL); 344462306a36Sopenharmony_ci if (rv < 0) 344562306a36Sopenharmony_ci goto ida_get_error; 344662306a36Sopenharmony_ci index = rv; 344762306a36Sopenharmony_ci 344862306a36Sopenharmony_ci rv = rssd_disk_name_format("rssd", 344962306a36Sopenharmony_ci index, 345062306a36Sopenharmony_ci dd->disk->disk_name, 345162306a36Sopenharmony_ci DISK_NAME_LEN); 345262306a36Sopenharmony_ci if (rv) 345362306a36Sopenharmony_ci goto disk_index_error; 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci dd->disk->major = dd->major; 345662306a36Sopenharmony_ci dd->disk->first_minor = index * MTIP_MAX_MINORS; 345762306a36Sopenharmony_ci dd->disk->minors = MTIP_MAX_MINORS; 345862306a36Sopenharmony_ci dd->disk->fops = &mtip_block_ops; 345962306a36Sopenharmony_ci dd->disk->private_data = dd; 346062306a36Sopenharmony_ci dd->index = index; 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci mtip_hw_debugfs_init(dd); 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_ciskip_create_disk: 346562306a36Sopenharmony_ci /* Initialize the protocol layer. */ 346662306a36Sopenharmony_ci wait_for_rebuild = mtip_hw_get_identify(dd); 346762306a36Sopenharmony_ci if (wait_for_rebuild < 0) { 346862306a36Sopenharmony_ci dev_err(&dd->pdev->dev, 346962306a36Sopenharmony_ci "Protocol layer initialization failed\n"); 347062306a36Sopenharmony_ci rv = -EINVAL; 347162306a36Sopenharmony_ci goto init_hw_cmds_error; 347262306a36Sopenharmony_ci } 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci /* 347562306a36Sopenharmony_ci * if rebuild pending, start the service thread, and delay the block 347662306a36Sopenharmony_ci * queue creation and device_add_disk() 347762306a36Sopenharmony_ci */ 347862306a36Sopenharmony_ci if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) 347962306a36Sopenharmony_ci goto start_service_thread; 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci /* Set device limits. */ 348262306a36Sopenharmony_ci blk_queue_flag_set(QUEUE_FLAG_NONROT, dd->queue); 348362306a36Sopenharmony_ci blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, dd->queue); 348462306a36Sopenharmony_ci blk_queue_max_segments(dd->queue, MTIP_MAX_SG); 348562306a36Sopenharmony_ci blk_queue_physical_block_size(dd->queue, 4096); 348662306a36Sopenharmony_ci blk_queue_max_hw_sectors(dd->queue, 0xffff); 348762306a36Sopenharmony_ci blk_queue_max_segment_size(dd->queue, 0x400000); 348862306a36Sopenharmony_ci dma_set_max_seg_size(&dd->pdev->dev, 0x400000); 348962306a36Sopenharmony_ci blk_queue_io_min(dd->queue, 4096); 349062306a36Sopenharmony_ci 349162306a36Sopenharmony_ci /* Set the capacity of the device in 512 byte sectors. */ 349262306a36Sopenharmony_ci if (!(mtip_hw_get_capacity(dd, &capacity))) { 349362306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 349462306a36Sopenharmony_ci "Could not read drive capacity\n"); 349562306a36Sopenharmony_ci rv = -EIO; 349662306a36Sopenharmony_ci goto read_capacity_error; 349762306a36Sopenharmony_ci } 349862306a36Sopenharmony_ci set_capacity(dd->disk, capacity); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci /* Enable the block device and add it to /dev */ 350162306a36Sopenharmony_ci rv = device_add_disk(&dd->pdev->dev, dd->disk, mtip_disk_attr_groups); 350262306a36Sopenharmony_ci if (rv) 350362306a36Sopenharmony_ci goto read_capacity_error; 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci if (dd->mtip_svc_handler) { 350662306a36Sopenharmony_ci set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); 350762306a36Sopenharmony_ci return rv; /* service thread created for handling rebuild */ 350862306a36Sopenharmony_ci } 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_cistart_service_thread: 351162306a36Sopenharmony_ci dd->mtip_svc_handler = kthread_create_on_node(mtip_service_thread, 351262306a36Sopenharmony_ci dd, dd->numa_node, 351362306a36Sopenharmony_ci "mtip_svc_thd_%02d", index); 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_ci if (IS_ERR(dd->mtip_svc_handler)) { 351662306a36Sopenharmony_ci dev_err(&dd->pdev->dev, "service thread failed to start\n"); 351762306a36Sopenharmony_ci dd->mtip_svc_handler = NULL; 351862306a36Sopenharmony_ci rv = -EFAULT; 351962306a36Sopenharmony_ci goto kthread_run_error; 352062306a36Sopenharmony_ci } 352162306a36Sopenharmony_ci wake_up_process(dd->mtip_svc_handler); 352262306a36Sopenharmony_ci if (wait_for_rebuild == MTIP_FTL_REBUILD_MAGIC) 352362306a36Sopenharmony_ci rv = wait_for_rebuild; 352462306a36Sopenharmony_ci 352562306a36Sopenharmony_ci return rv; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_cikthread_run_error: 352862306a36Sopenharmony_ci /* Delete our gendisk. This also removes the device from /dev */ 352962306a36Sopenharmony_ci del_gendisk(dd->disk); 353062306a36Sopenharmony_ciread_capacity_error: 353162306a36Sopenharmony_ciinit_hw_cmds_error: 353262306a36Sopenharmony_ci mtip_hw_debugfs_exit(dd); 353362306a36Sopenharmony_cidisk_index_error: 353462306a36Sopenharmony_ci ida_free(&rssd_index_ida, index); 353562306a36Sopenharmony_ciida_get_error: 353662306a36Sopenharmony_ci put_disk(dd->disk); 353762306a36Sopenharmony_ciblock_queue_alloc_init_error: 353862306a36Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 353962306a36Sopenharmony_ciblock_queue_alloc_tag_error: 354062306a36Sopenharmony_ci mtip_hw_exit(dd); /* De-initialize the protocol layer. */ 354162306a36Sopenharmony_ciprotocol_init_error: 354262306a36Sopenharmony_ci return rv; 354362306a36Sopenharmony_ci} 354462306a36Sopenharmony_ci 354562306a36Sopenharmony_ci/* 354662306a36Sopenharmony_ci * Function called by the PCI layer when just before the 354762306a36Sopenharmony_ci * machine shuts down. 354862306a36Sopenharmony_ci * 354962306a36Sopenharmony_ci * If a protocol layer shutdown function is present it will be called 355062306a36Sopenharmony_ci * by this function. 355162306a36Sopenharmony_ci * 355262306a36Sopenharmony_ci * @dd Pointer to the driver data structure. 355362306a36Sopenharmony_ci * 355462306a36Sopenharmony_ci * return value 355562306a36Sopenharmony_ci * 0 355662306a36Sopenharmony_ci */ 355762306a36Sopenharmony_cistatic int mtip_block_shutdown(struct driver_data *dd) 355862306a36Sopenharmony_ci{ 355962306a36Sopenharmony_ci mtip_hw_shutdown(dd); 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 356262306a36Sopenharmony_ci "Shutting down %s ...\n", dd->disk->disk_name); 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) 356562306a36Sopenharmony_ci del_gendisk(dd->disk); 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 356862306a36Sopenharmony_ci put_disk(dd->disk); 356962306a36Sopenharmony_ci return 0; 357062306a36Sopenharmony_ci} 357162306a36Sopenharmony_ci 357262306a36Sopenharmony_cistatic int mtip_block_suspend(struct driver_data *dd) 357362306a36Sopenharmony_ci{ 357462306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 357562306a36Sopenharmony_ci "Suspending %s ...\n", dd->disk->disk_name); 357662306a36Sopenharmony_ci mtip_hw_suspend(dd); 357762306a36Sopenharmony_ci return 0; 357862306a36Sopenharmony_ci} 357962306a36Sopenharmony_ci 358062306a36Sopenharmony_cistatic int mtip_block_resume(struct driver_data *dd) 358162306a36Sopenharmony_ci{ 358262306a36Sopenharmony_ci dev_info(&dd->pdev->dev, "Resuming %s ...\n", 358362306a36Sopenharmony_ci dd->disk->disk_name); 358462306a36Sopenharmony_ci mtip_hw_resume(dd); 358562306a36Sopenharmony_ci return 0; 358662306a36Sopenharmony_ci} 358762306a36Sopenharmony_ci 358862306a36Sopenharmony_cistatic void drop_cpu(int cpu) 358962306a36Sopenharmony_ci{ 359062306a36Sopenharmony_ci cpu_use[cpu]--; 359162306a36Sopenharmony_ci} 359262306a36Sopenharmony_ci 359362306a36Sopenharmony_cistatic int get_least_used_cpu_on_node(int node) 359462306a36Sopenharmony_ci{ 359562306a36Sopenharmony_ci int cpu, least_used_cpu, least_cnt; 359662306a36Sopenharmony_ci const struct cpumask *node_mask; 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci node_mask = cpumask_of_node(node); 359962306a36Sopenharmony_ci least_used_cpu = cpumask_first(node_mask); 360062306a36Sopenharmony_ci least_cnt = cpu_use[least_used_cpu]; 360162306a36Sopenharmony_ci cpu = least_used_cpu; 360262306a36Sopenharmony_ci 360362306a36Sopenharmony_ci for_each_cpu(cpu, node_mask) { 360462306a36Sopenharmony_ci if (cpu_use[cpu] < least_cnt) { 360562306a36Sopenharmony_ci least_used_cpu = cpu; 360662306a36Sopenharmony_ci least_cnt = cpu_use[cpu]; 360762306a36Sopenharmony_ci } 360862306a36Sopenharmony_ci } 360962306a36Sopenharmony_ci cpu_use[least_used_cpu]++; 361062306a36Sopenharmony_ci return least_used_cpu; 361162306a36Sopenharmony_ci} 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci/* Helper for selecting a node in round robin mode */ 361462306a36Sopenharmony_cistatic inline int mtip_get_next_rr_node(void) 361562306a36Sopenharmony_ci{ 361662306a36Sopenharmony_ci static int next_node = NUMA_NO_NODE; 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_ci if (next_node == NUMA_NO_NODE) { 361962306a36Sopenharmony_ci next_node = first_online_node; 362062306a36Sopenharmony_ci return next_node; 362162306a36Sopenharmony_ci } 362262306a36Sopenharmony_ci 362362306a36Sopenharmony_ci next_node = next_online_node(next_node); 362462306a36Sopenharmony_ci if (next_node == MAX_NUMNODES) 362562306a36Sopenharmony_ci next_node = first_online_node; 362662306a36Sopenharmony_ci return next_node; 362762306a36Sopenharmony_ci} 362862306a36Sopenharmony_ci 362962306a36Sopenharmony_cistatic DEFINE_HANDLER(0); 363062306a36Sopenharmony_cistatic DEFINE_HANDLER(1); 363162306a36Sopenharmony_cistatic DEFINE_HANDLER(2); 363262306a36Sopenharmony_cistatic DEFINE_HANDLER(3); 363362306a36Sopenharmony_cistatic DEFINE_HANDLER(4); 363462306a36Sopenharmony_cistatic DEFINE_HANDLER(5); 363562306a36Sopenharmony_cistatic DEFINE_HANDLER(6); 363662306a36Sopenharmony_cistatic DEFINE_HANDLER(7); 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_cistatic void mtip_disable_link_opts(struct driver_data *dd, struct pci_dev *pdev) 363962306a36Sopenharmony_ci{ 364062306a36Sopenharmony_ci unsigned short pcie_dev_ctrl; 364162306a36Sopenharmony_ci 364262306a36Sopenharmony_ci if (pci_is_pcie(pdev)) { 364362306a36Sopenharmony_ci pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &pcie_dev_ctrl); 364462306a36Sopenharmony_ci if (pcie_dev_ctrl & PCI_EXP_DEVCTL_NOSNOOP_EN || 364562306a36Sopenharmony_ci pcie_dev_ctrl & PCI_EXP_DEVCTL_RELAX_EN) { 364662306a36Sopenharmony_ci dev_info(&dd->pdev->dev, 364762306a36Sopenharmony_ci "Disabling ERO/No-Snoop on bridge device %04x:%04x\n", 364862306a36Sopenharmony_ci pdev->vendor, pdev->device); 364962306a36Sopenharmony_ci pcie_dev_ctrl &= ~(PCI_EXP_DEVCTL_NOSNOOP_EN | 365062306a36Sopenharmony_ci PCI_EXP_DEVCTL_RELAX_EN); 365162306a36Sopenharmony_ci pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, 365262306a36Sopenharmony_ci pcie_dev_ctrl); 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci } 365562306a36Sopenharmony_ci} 365662306a36Sopenharmony_ci 365762306a36Sopenharmony_cistatic void mtip_fix_ero_nosnoop(struct driver_data *dd, struct pci_dev *pdev) 365862306a36Sopenharmony_ci{ 365962306a36Sopenharmony_ci /* 366062306a36Sopenharmony_ci * This workaround is specific to AMD/ATI chipset with a PCI upstream 366162306a36Sopenharmony_ci * device with device id 0x5aXX 366262306a36Sopenharmony_ci */ 366362306a36Sopenharmony_ci if (pdev->bus && pdev->bus->self) { 366462306a36Sopenharmony_ci if (pdev->bus->self->vendor == PCI_VENDOR_ID_ATI && 366562306a36Sopenharmony_ci ((pdev->bus->self->device & 0xff00) == 0x5a00)) { 366662306a36Sopenharmony_ci mtip_disable_link_opts(dd, pdev->bus->self); 366762306a36Sopenharmony_ci } else { 366862306a36Sopenharmony_ci /* Check further up the topology */ 366962306a36Sopenharmony_ci struct pci_dev *parent_dev = pdev->bus->self; 367062306a36Sopenharmony_ci if (parent_dev->bus && 367162306a36Sopenharmony_ci parent_dev->bus->parent && 367262306a36Sopenharmony_ci parent_dev->bus->parent->self && 367362306a36Sopenharmony_ci parent_dev->bus->parent->self->vendor == 367462306a36Sopenharmony_ci PCI_VENDOR_ID_ATI && 367562306a36Sopenharmony_ci (parent_dev->bus->parent->self->device & 367662306a36Sopenharmony_ci 0xff00) == 0x5a00) { 367762306a36Sopenharmony_ci mtip_disable_link_opts(dd, 367862306a36Sopenharmony_ci parent_dev->bus->parent->self); 367962306a36Sopenharmony_ci } 368062306a36Sopenharmony_ci } 368162306a36Sopenharmony_ci } 368262306a36Sopenharmony_ci} 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_ci/* 368562306a36Sopenharmony_ci * Called for each supported PCI device detected. 368662306a36Sopenharmony_ci * 368762306a36Sopenharmony_ci * This function allocates the private data structure, enables the 368862306a36Sopenharmony_ci * PCI device and then calls the block layer initialization function. 368962306a36Sopenharmony_ci * 369062306a36Sopenharmony_ci * return value 369162306a36Sopenharmony_ci * 0 on success else an error code. 369262306a36Sopenharmony_ci */ 369362306a36Sopenharmony_cistatic int mtip_pci_probe(struct pci_dev *pdev, 369462306a36Sopenharmony_ci const struct pci_device_id *ent) 369562306a36Sopenharmony_ci{ 369662306a36Sopenharmony_ci int rv = 0; 369762306a36Sopenharmony_ci struct driver_data *dd = NULL; 369862306a36Sopenharmony_ci char cpu_list[256]; 369962306a36Sopenharmony_ci const struct cpumask *node_mask; 370062306a36Sopenharmony_ci int cpu, i = 0, j = 0; 370162306a36Sopenharmony_ci int my_node = NUMA_NO_NODE; 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_ci /* Allocate memory for this devices private data. */ 370462306a36Sopenharmony_ci my_node = pcibus_to_node(pdev->bus); 370562306a36Sopenharmony_ci if (my_node != NUMA_NO_NODE) { 370662306a36Sopenharmony_ci if (!node_online(my_node)) 370762306a36Sopenharmony_ci my_node = mtip_get_next_rr_node(); 370862306a36Sopenharmony_ci } else { 370962306a36Sopenharmony_ci dev_info(&pdev->dev, "Kernel not reporting proximity, choosing a node\n"); 371062306a36Sopenharmony_ci my_node = mtip_get_next_rr_node(); 371162306a36Sopenharmony_ci } 371262306a36Sopenharmony_ci dev_info(&pdev->dev, "NUMA node %d (closest: %d,%d, probe on %d:%d)\n", 371362306a36Sopenharmony_ci my_node, pcibus_to_node(pdev->bus), dev_to_node(&pdev->dev), 371462306a36Sopenharmony_ci cpu_to_node(raw_smp_processor_id()), raw_smp_processor_id()); 371562306a36Sopenharmony_ci 371662306a36Sopenharmony_ci dd = kzalloc_node(sizeof(struct driver_data), GFP_KERNEL, my_node); 371762306a36Sopenharmony_ci if (!dd) 371862306a36Sopenharmony_ci return -ENOMEM; 371962306a36Sopenharmony_ci 372062306a36Sopenharmony_ci /* Attach the private data to this PCI device. */ 372162306a36Sopenharmony_ci pci_set_drvdata(pdev, dd); 372262306a36Sopenharmony_ci 372362306a36Sopenharmony_ci rv = pcim_enable_device(pdev); 372462306a36Sopenharmony_ci if (rv < 0) { 372562306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to enable device\n"); 372662306a36Sopenharmony_ci goto iomap_err; 372762306a36Sopenharmony_ci } 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci /* Map BAR5 to memory. */ 373062306a36Sopenharmony_ci rv = pcim_iomap_regions(pdev, 1 << MTIP_ABAR, MTIP_DRV_NAME); 373162306a36Sopenharmony_ci if (rv < 0) { 373262306a36Sopenharmony_ci dev_err(&pdev->dev, "Unable to map regions\n"); 373362306a36Sopenharmony_ci goto iomap_err; 373462306a36Sopenharmony_ci } 373562306a36Sopenharmony_ci 373662306a36Sopenharmony_ci rv = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 373762306a36Sopenharmony_ci if (rv) { 373862306a36Sopenharmony_ci dev_warn(&pdev->dev, "64-bit DMA enable failed\n"); 373962306a36Sopenharmony_ci goto setmask_err; 374062306a36Sopenharmony_ci } 374162306a36Sopenharmony_ci 374262306a36Sopenharmony_ci /* Copy the info we may need later into the private data structure. */ 374362306a36Sopenharmony_ci dd->major = mtip_major; 374462306a36Sopenharmony_ci dd->instance = instance; 374562306a36Sopenharmony_ci dd->pdev = pdev; 374662306a36Sopenharmony_ci dd->numa_node = my_node; 374762306a36Sopenharmony_ci 374862306a36Sopenharmony_ci memset(dd->workq_name, 0, 32); 374962306a36Sopenharmony_ci snprintf(dd->workq_name, 31, "mtipq%d", dd->instance); 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci dd->isr_workq = create_workqueue(dd->workq_name); 375262306a36Sopenharmony_ci if (!dd->isr_workq) { 375362306a36Sopenharmony_ci dev_warn(&pdev->dev, "Can't create wq %d\n", dd->instance); 375462306a36Sopenharmony_ci rv = -ENOMEM; 375562306a36Sopenharmony_ci goto setmask_err; 375662306a36Sopenharmony_ci } 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_ci memset(cpu_list, 0, sizeof(cpu_list)); 375962306a36Sopenharmony_ci 376062306a36Sopenharmony_ci node_mask = cpumask_of_node(dd->numa_node); 376162306a36Sopenharmony_ci if (!cpumask_empty(node_mask)) { 376262306a36Sopenharmony_ci for_each_cpu(cpu, node_mask) 376362306a36Sopenharmony_ci { 376462306a36Sopenharmony_ci snprintf(&cpu_list[j], 256 - j, "%d ", cpu); 376562306a36Sopenharmony_ci j = strlen(cpu_list); 376662306a36Sopenharmony_ci } 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci dev_info(&pdev->dev, "Node %d on package %d has %d cpu(s): %s\n", 376962306a36Sopenharmony_ci dd->numa_node, 377062306a36Sopenharmony_ci topology_physical_package_id(cpumask_first(node_mask)), 377162306a36Sopenharmony_ci nr_cpus_node(dd->numa_node), 377262306a36Sopenharmony_ci cpu_list); 377362306a36Sopenharmony_ci } else 377462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "mtip32xx: node_mask empty\n"); 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci dd->isr_binding = get_least_used_cpu_on_node(dd->numa_node); 377762306a36Sopenharmony_ci dev_info(&pdev->dev, "Initial IRQ binding node:cpu %d:%d\n", 377862306a36Sopenharmony_ci cpu_to_node(dd->isr_binding), dd->isr_binding); 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci /* first worker context always runs in ISR */ 378162306a36Sopenharmony_ci dd->work[0].cpu_binding = dd->isr_binding; 378262306a36Sopenharmony_ci dd->work[1].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); 378362306a36Sopenharmony_ci dd->work[2].cpu_binding = get_least_used_cpu_on_node(dd->numa_node); 378462306a36Sopenharmony_ci dd->work[3].cpu_binding = dd->work[0].cpu_binding; 378562306a36Sopenharmony_ci dd->work[4].cpu_binding = dd->work[1].cpu_binding; 378662306a36Sopenharmony_ci dd->work[5].cpu_binding = dd->work[2].cpu_binding; 378762306a36Sopenharmony_ci dd->work[6].cpu_binding = dd->work[2].cpu_binding; 378862306a36Sopenharmony_ci dd->work[7].cpu_binding = dd->work[1].cpu_binding; 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci /* Log the bindings */ 379162306a36Sopenharmony_ci for_each_present_cpu(cpu) { 379262306a36Sopenharmony_ci memset(cpu_list, 0, sizeof(cpu_list)); 379362306a36Sopenharmony_ci for (i = 0, j = 0; i < MTIP_MAX_SLOT_GROUPS; i++) { 379462306a36Sopenharmony_ci if (dd->work[i].cpu_binding == cpu) { 379562306a36Sopenharmony_ci snprintf(&cpu_list[j], 256 - j, "%d ", i); 379662306a36Sopenharmony_ci j = strlen(cpu_list); 379762306a36Sopenharmony_ci } 379862306a36Sopenharmony_ci } 379962306a36Sopenharmony_ci if (j) 380062306a36Sopenharmony_ci dev_info(&pdev->dev, "CPU %d: WQs %s\n", cpu, cpu_list); 380162306a36Sopenharmony_ci } 380262306a36Sopenharmony_ci 380362306a36Sopenharmony_ci INIT_WORK(&dd->work[0].work, mtip_workq_sdbf0); 380462306a36Sopenharmony_ci INIT_WORK(&dd->work[1].work, mtip_workq_sdbf1); 380562306a36Sopenharmony_ci INIT_WORK(&dd->work[2].work, mtip_workq_sdbf2); 380662306a36Sopenharmony_ci INIT_WORK(&dd->work[3].work, mtip_workq_sdbf3); 380762306a36Sopenharmony_ci INIT_WORK(&dd->work[4].work, mtip_workq_sdbf4); 380862306a36Sopenharmony_ci INIT_WORK(&dd->work[5].work, mtip_workq_sdbf5); 380962306a36Sopenharmony_ci INIT_WORK(&dd->work[6].work, mtip_workq_sdbf6); 381062306a36Sopenharmony_ci INIT_WORK(&dd->work[7].work, mtip_workq_sdbf7); 381162306a36Sopenharmony_ci 381262306a36Sopenharmony_ci pci_set_master(pdev); 381362306a36Sopenharmony_ci rv = pci_enable_msi(pdev); 381462306a36Sopenharmony_ci if (rv) { 381562306a36Sopenharmony_ci dev_warn(&pdev->dev, 381662306a36Sopenharmony_ci "Unable to enable MSI interrupt.\n"); 381762306a36Sopenharmony_ci goto msi_initialize_err; 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci mtip_fix_ero_nosnoop(dd, pdev); 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci /* Initialize the block layer. */ 382362306a36Sopenharmony_ci rv = mtip_block_initialize(dd); 382462306a36Sopenharmony_ci if (rv < 0) { 382562306a36Sopenharmony_ci dev_err(&pdev->dev, 382662306a36Sopenharmony_ci "Unable to initialize block layer\n"); 382762306a36Sopenharmony_ci goto block_initialize_err; 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci /* 383162306a36Sopenharmony_ci * Increment the instance count so that each device has a unique 383262306a36Sopenharmony_ci * instance number. 383362306a36Sopenharmony_ci */ 383462306a36Sopenharmony_ci instance++; 383562306a36Sopenharmony_ci if (rv != MTIP_FTL_REBUILD_MAGIC) 383662306a36Sopenharmony_ci set_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag); 383762306a36Sopenharmony_ci else 383862306a36Sopenharmony_ci rv = 0; /* device in rebuild state, return 0 from probe */ 383962306a36Sopenharmony_ci 384062306a36Sopenharmony_ci goto done; 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_ciblock_initialize_err: 384362306a36Sopenharmony_ci pci_disable_msi(pdev); 384462306a36Sopenharmony_ci 384562306a36Sopenharmony_cimsi_initialize_err: 384662306a36Sopenharmony_ci if (dd->isr_workq) { 384762306a36Sopenharmony_ci destroy_workqueue(dd->isr_workq); 384862306a36Sopenharmony_ci drop_cpu(dd->work[0].cpu_binding); 384962306a36Sopenharmony_ci drop_cpu(dd->work[1].cpu_binding); 385062306a36Sopenharmony_ci drop_cpu(dd->work[2].cpu_binding); 385162306a36Sopenharmony_ci } 385262306a36Sopenharmony_cisetmask_err: 385362306a36Sopenharmony_ci pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ciiomap_err: 385662306a36Sopenharmony_ci kfree(dd); 385762306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 385862306a36Sopenharmony_ci return rv; 385962306a36Sopenharmony_cidone: 386062306a36Sopenharmony_ci return rv; 386162306a36Sopenharmony_ci} 386262306a36Sopenharmony_ci 386362306a36Sopenharmony_ci/* 386462306a36Sopenharmony_ci * Called for each probed device when the device is removed or the 386562306a36Sopenharmony_ci * driver is unloaded. 386662306a36Sopenharmony_ci * 386762306a36Sopenharmony_ci * return value 386862306a36Sopenharmony_ci * None 386962306a36Sopenharmony_ci */ 387062306a36Sopenharmony_cistatic void mtip_pci_remove(struct pci_dev *pdev) 387162306a36Sopenharmony_ci{ 387262306a36Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 387362306a36Sopenharmony_ci unsigned long to; 387462306a36Sopenharmony_ci 387562306a36Sopenharmony_ci mtip_check_surprise_removal(dd); 387662306a36Sopenharmony_ci synchronize_irq(dd->pdev->irq); 387762306a36Sopenharmony_ci 387862306a36Sopenharmony_ci /* Spin until workers are done */ 387962306a36Sopenharmony_ci to = jiffies + msecs_to_jiffies(4000); 388062306a36Sopenharmony_ci do { 388162306a36Sopenharmony_ci msleep(20); 388262306a36Sopenharmony_ci } while (atomic_read(&dd->irq_workers_active) != 0 && 388362306a36Sopenharmony_ci time_before(jiffies, to)); 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci if (atomic_read(&dd->irq_workers_active) != 0) { 388662306a36Sopenharmony_ci dev_warn(&dd->pdev->dev, 388762306a36Sopenharmony_ci "Completion workers still active!\n"); 388862306a36Sopenharmony_ci } 388962306a36Sopenharmony_ci 389062306a36Sopenharmony_ci set_bit(MTIP_DDF_REMOVE_PENDING_BIT, &dd->dd_flag); 389162306a36Sopenharmony_ci 389262306a36Sopenharmony_ci if (test_bit(MTIP_DDF_INIT_DONE_BIT, &dd->dd_flag)) 389362306a36Sopenharmony_ci del_gendisk(dd->disk); 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci mtip_hw_debugfs_exit(dd); 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci if (dd->mtip_svc_handler) { 389862306a36Sopenharmony_ci set_bit(MTIP_PF_SVC_THD_STOP_BIT, &dd->port->flags); 389962306a36Sopenharmony_ci wake_up_interruptible(&dd->port->svc_wait); 390062306a36Sopenharmony_ci kthread_stop(dd->mtip_svc_handler); 390162306a36Sopenharmony_ci } 390262306a36Sopenharmony_ci 390362306a36Sopenharmony_ci if (!dd->sr) { 390462306a36Sopenharmony_ci /* 390562306a36Sopenharmony_ci * Explicitly wait here for IOs to quiesce, 390662306a36Sopenharmony_ci * as mtip_standby_drive usually won't wait for IOs. 390762306a36Sopenharmony_ci */ 390862306a36Sopenharmony_ci if (!mtip_quiesce_io(dd->port, MTIP_QUIESCE_IO_TIMEOUT_MS)) 390962306a36Sopenharmony_ci mtip_standby_drive(dd); 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci else 391262306a36Sopenharmony_ci dev_info(&dd->pdev->dev, "device %s surprise removal\n", 391362306a36Sopenharmony_ci dd->disk->disk_name); 391462306a36Sopenharmony_ci 391562306a36Sopenharmony_ci blk_mq_free_tag_set(&dd->tags); 391662306a36Sopenharmony_ci 391762306a36Sopenharmony_ci /* De-initialize the protocol layer. */ 391862306a36Sopenharmony_ci mtip_hw_exit(dd); 391962306a36Sopenharmony_ci 392062306a36Sopenharmony_ci if (dd->isr_workq) { 392162306a36Sopenharmony_ci destroy_workqueue(dd->isr_workq); 392262306a36Sopenharmony_ci drop_cpu(dd->work[0].cpu_binding); 392362306a36Sopenharmony_ci drop_cpu(dd->work[1].cpu_binding); 392462306a36Sopenharmony_ci drop_cpu(dd->work[2].cpu_binding); 392562306a36Sopenharmony_ci } 392662306a36Sopenharmony_ci 392762306a36Sopenharmony_ci pci_disable_msi(pdev); 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci pcim_iounmap_regions(pdev, 1 << MTIP_ABAR); 393062306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci put_disk(dd->disk); 393362306a36Sopenharmony_ci} 393462306a36Sopenharmony_ci 393562306a36Sopenharmony_ci/* 393662306a36Sopenharmony_ci * Called for each probed device when the device is suspended. 393762306a36Sopenharmony_ci * 393862306a36Sopenharmony_ci * return value 393962306a36Sopenharmony_ci * 0 Success 394062306a36Sopenharmony_ci * <0 Error 394162306a36Sopenharmony_ci */ 394262306a36Sopenharmony_cistatic int __maybe_unused mtip_pci_suspend(struct device *dev) 394362306a36Sopenharmony_ci{ 394462306a36Sopenharmony_ci int rv = 0; 394562306a36Sopenharmony_ci struct driver_data *dd = dev_get_drvdata(dev); 394662306a36Sopenharmony_ci 394762306a36Sopenharmony_ci set_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci /* Disable ports & interrupts then send standby immediate */ 395062306a36Sopenharmony_ci rv = mtip_block_suspend(dd); 395162306a36Sopenharmony_ci if (rv < 0) 395262306a36Sopenharmony_ci dev_err(dev, "Failed to suspend controller\n"); 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci return rv; 395562306a36Sopenharmony_ci} 395662306a36Sopenharmony_ci 395762306a36Sopenharmony_ci/* 395862306a36Sopenharmony_ci * Called for each probed device when the device is resumed. 395962306a36Sopenharmony_ci * 396062306a36Sopenharmony_ci * return value 396162306a36Sopenharmony_ci * 0 Success 396262306a36Sopenharmony_ci * <0 Error 396362306a36Sopenharmony_ci */ 396462306a36Sopenharmony_cistatic int __maybe_unused mtip_pci_resume(struct device *dev) 396562306a36Sopenharmony_ci{ 396662306a36Sopenharmony_ci int rv = 0; 396762306a36Sopenharmony_ci struct driver_data *dd = dev_get_drvdata(dev); 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci /* 397062306a36Sopenharmony_ci * Calls hbaReset, initPort, & startPort function 397162306a36Sopenharmony_ci * then enables interrupts 397262306a36Sopenharmony_ci */ 397362306a36Sopenharmony_ci rv = mtip_block_resume(dd); 397462306a36Sopenharmony_ci if (rv < 0) 397562306a36Sopenharmony_ci dev_err(dev, "Unable to resume\n"); 397662306a36Sopenharmony_ci 397762306a36Sopenharmony_ci clear_bit(MTIP_DDF_RESUME_BIT, &dd->dd_flag); 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_ci return rv; 398062306a36Sopenharmony_ci} 398162306a36Sopenharmony_ci 398262306a36Sopenharmony_ci/* 398362306a36Sopenharmony_ci * Shutdown routine 398462306a36Sopenharmony_ci * 398562306a36Sopenharmony_ci * return value 398662306a36Sopenharmony_ci * None 398762306a36Sopenharmony_ci */ 398862306a36Sopenharmony_cistatic void mtip_pci_shutdown(struct pci_dev *pdev) 398962306a36Sopenharmony_ci{ 399062306a36Sopenharmony_ci struct driver_data *dd = pci_get_drvdata(pdev); 399162306a36Sopenharmony_ci if (dd) 399262306a36Sopenharmony_ci mtip_block_shutdown(dd); 399362306a36Sopenharmony_ci} 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci/* Table of device ids supported by this driver. */ 399662306a36Sopenharmony_cistatic const struct pci_device_id mtip_pci_tbl[] = { 399762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) }, 399862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) }, 399962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) }, 400062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) }, 400162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) }, 400262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) }, 400362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) }, 400462306a36Sopenharmony_ci { 0 } 400562306a36Sopenharmony_ci}; 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mtip_pci_pm_ops, mtip_pci_suspend, mtip_pci_resume); 400862306a36Sopenharmony_ci 400962306a36Sopenharmony_ci/* Structure that describes the PCI driver functions. */ 401062306a36Sopenharmony_cistatic struct pci_driver mtip_pci_driver = { 401162306a36Sopenharmony_ci .name = MTIP_DRV_NAME, 401262306a36Sopenharmony_ci .id_table = mtip_pci_tbl, 401362306a36Sopenharmony_ci .probe = mtip_pci_probe, 401462306a36Sopenharmony_ci .remove = mtip_pci_remove, 401562306a36Sopenharmony_ci .driver.pm = &mtip_pci_pm_ops, 401662306a36Sopenharmony_ci .shutdown = mtip_pci_shutdown, 401762306a36Sopenharmony_ci}; 401862306a36Sopenharmony_ci 401962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, mtip_pci_tbl); 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci/* 402262306a36Sopenharmony_ci * Module initialization function. 402362306a36Sopenharmony_ci * 402462306a36Sopenharmony_ci * Called once when the module is loaded. This function allocates a major 402562306a36Sopenharmony_ci * block device number to the Cyclone devices and registers the PCI layer 402662306a36Sopenharmony_ci * of the driver. 402762306a36Sopenharmony_ci * 402862306a36Sopenharmony_ci * Return value 402962306a36Sopenharmony_ci * 0 on success else error code. 403062306a36Sopenharmony_ci */ 403162306a36Sopenharmony_cistatic int __init mtip_init(void) 403262306a36Sopenharmony_ci{ 403362306a36Sopenharmony_ci int error; 403462306a36Sopenharmony_ci 403562306a36Sopenharmony_ci pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n"); 403662306a36Sopenharmony_ci 403762306a36Sopenharmony_ci /* Allocate a major block device number to use with this driver. */ 403862306a36Sopenharmony_ci error = register_blkdev(0, MTIP_DRV_NAME); 403962306a36Sopenharmony_ci if (error <= 0) { 404062306a36Sopenharmony_ci pr_err("Unable to register block device (%d)\n", 404162306a36Sopenharmony_ci error); 404262306a36Sopenharmony_ci return -EBUSY; 404362306a36Sopenharmony_ci } 404462306a36Sopenharmony_ci mtip_major = error; 404562306a36Sopenharmony_ci 404662306a36Sopenharmony_ci dfs_parent = debugfs_create_dir("rssd", NULL); 404762306a36Sopenharmony_ci if (IS_ERR_OR_NULL(dfs_parent)) { 404862306a36Sopenharmony_ci pr_warn("Error creating debugfs parent\n"); 404962306a36Sopenharmony_ci dfs_parent = NULL; 405062306a36Sopenharmony_ci } 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci /* Register our PCI operations. */ 405362306a36Sopenharmony_ci error = pci_register_driver(&mtip_pci_driver); 405462306a36Sopenharmony_ci if (error) { 405562306a36Sopenharmony_ci debugfs_remove(dfs_parent); 405662306a36Sopenharmony_ci unregister_blkdev(mtip_major, MTIP_DRV_NAME); 405762306a36Sopenharmony_ci } 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci return error; 406062306a36Sopenharmony_ci} 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci/* 406362306a36Sopenharmony_ci * Module de-initialization function. 406462306a36Sopenharmony_ci * 406562306a36Sopenharmony_ci * Called once when the module is unloaded. This function deallocates 406662306a36Sopenharmony_ci * the major block device number allocated by mtip_init() and 406762306a36Sopenharmony_ci * unregisters the PCI layer of the driver. 406862306a36Sopenharmony_ci * 406962306a36Sopenharmony_ci * Return value 407062306a36Sopenharmony_ci * none 407162306a36Sopenharmony_ci */ 407262306a36Sopenharmony_cistatic void __exit mtip_exit(void) 407362306a36Sopenharmony_ci{ 407462306a36Sopenharmony_ci /* Release the allocated major block device number. */ 407562306a36Sopenharmony_ci unregister_blkdev(mtip_major, MTIP_DRV_NAME); 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_ci /* Unregister the PCI driver. */ 407862306a36Sopenharmony_ci pci_unregister_driver(&mtip_pci_driver); 407962306a36Sopenharmony_ci 408062306a36Sopenharmony_ci debugfs_remove_recursive(dfs_parent); 408162306a36Sopenharmony_ci} 408262306a36Sopenharmony_ci 408362306a36Sopenharmony_ciMODULE_AUTHOR("Micron Technology, Inc"); 408462306a36Sopenharmony_ciMODULE_DESCRIPTION("Micron RealSSD PCIe Block Driver"); 408562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 408662306a36Sopenharmony_ciMODULE_VERSION(MTIP_DRV_VERSION); 408762306a36Sopenharmony_ci 408862306a36Sopenharmony_cimodule_init(mtip_init); 408962306a36Sopenharmony_cimodule_exit(mtip_exit); 4090