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