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