2012-02-02 04:58:04

by vinayak holikatti

[permalink] [raw]
Subject: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

From: Santosh Yaraganavi <[email protected]>

UFS is designed to be the most advanced specification for
both embedded and removable flash memory-based storage in mobile devices
such as smart phones and tablet computers. The UFS standard represents
an evolutionary progression of JEDEC standards in this field, and has been
specifically tailored for mobile applications and computing systems requiring
high performance and low power consumption. The initial data throughput for
UFS will be ~300 megabytes per second (MB/s), and the standard also supports
command queuing features to raise random read/write speeds.

To achieve the highest performance and most power efficient data
transport, UFS uses the leading industry interface standards to form its
Interconnect Layer: MIPI® Alliance’s M-PHY and UniProSM specifications.
UniPro is a comprehensive specification meant to act as a universal
chip-to-chip protocol, providing a common tunnel for other protocols.
The M-PHY interface is designed as the primary physical interface (PHY layer)
for the UniPro specification, and is a high speed serial interface targeting
up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
per lane.

MIPI’s M-PHY and UniPro specifications are optimized for mobile applications,
and are designed from the ground up for efficient power management in mobile
devices, including enabling efficient transitions between the active and power
save modes. Combined with a low active power level and a near-zero idle power
level, UFS offers the promise for significant reductions in device power
consumption.

The UFS standard adopts the well-known SCSI Architecture Model and command
protocols supporting multiple commands with command queuing features and
enabling a multi-thread programming paradigm. This differs from conventional
flash-based memory cards and embedded flash solutions which process one
command at a time, limiting random read/write access performance.
In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
specification will allow system designers greater flexibility by simplifying
the involvement of the host processor in the operation of the flash storage
subsystem. The UFS HCI specification and the adoption of SCSI will provide
a well-known software programming model and enable wider market adoption.

This patchset contains PCIe based UFS host controller driver which complies
to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).

This patch set is successfully applied on kernel version 3.3-rc2.

Santosh Yaraganavi (4):
[SCSI] ufshcd: UFS Host controller driver
[SCSI] ufshcd: UFS UTP Transfer requests handling
[SCSI] ufshcd: UFSHCI error handling
[SCSI] ufshcd: SCSI error handling

drivers/scsi/Kconfig | 1 +
drivers/scsi/Makefile | 1 +
drivers/scsi/ufs/Kconfig | 49 ++
drivers/scsi/ufs/Makefile | 2 +
drivers/scsi/ufs/ufs.h | 203 +++++
drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufshci.h | 360 +++++++++
7 files changed, 2570 insertions(+), 0 deletions(-)
create mode 100644 drivers/scsi/ufs/Kconfig
create mode 100644 drivers/scsi/ufs/Makefile
create mode 100644 drivers/scsi/ufs/ufs.h
create mode 100644 drivers/scsi/ufs/ufshcd.c
create mode 100644 drivers/scsi/ufs/ufshci.h

--
1.7.5.4


2012-02-02 04:58:17

by vinayak holikatti

[permalink] [raw]
Subject: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

From: Santosh Yaraganavi <[email protected]>

This patch adds support for Universal Flash Storage(UFS)
host controllers. The UFS host controller driver
includes host controller initialization method.

The Initialization process involves following steps:
- Initiate UFS Host Controller initialization process by writing
to Host controller enable register
- Configure UFS Host controller registers with host memory space
datastructure offsets.
- Unipro link startup procedure
- Check for connected device
- Configure UFS host controller to process requests
- Enable required interrupts
- Configure interrupt aggregation

Signed-off-by: Santosh Yaraganavi <[email protected]>
Signed-off-by: Vinayak Holikatti <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Reviewed-by: Saugata Das <[email protected]>
Reviewed-by: Vishak G <[email protected]>
Reviewed-by: Girish K S <[email protected]>
---
drivers/scsi/Kconfig | 1 +
drivers/scsi/Makefile | 1 +
drivers/scsi/ufs/Kconfig | 49 ++
drivers/scsi/ufs/Makefile | 2 +
drivers/scsi/ufs/ufs.h | 203 +++++++++
drivers/scsi/ufs/ufshcd.c | 1091 +++++++++++++++++++++++++++++++++++++++++++++
drivers/scsi/ufs/ufshci.h | 360 +++++++++++++++
7 files changed, 1707 insertions(+), 0 deletions(-)
create mode 100644 drivers/scsi/ufs/Kconfig
create mode 100644 drivers/scsi/ufs/Makefile
create mode 100644 drivers/scsi/ufs/ufs.h
create mode 100644 drivers/scsi/ufs/ufshcd.c
create mode 100644 drivers/scsi/ufs/ufshci.h

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 16570aa..477a91a 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -619,6 +619,7 @@ config SCSI_ARCMSR

source "drivers/scsi/megaraid/Kconfig.megaraid"
source "drivers/scsi/mpt2sas/Kconfig"
+source "drivers/scsi/ufs/Kconfig"

config SCSI_HPTIOP
tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 2b88749..c832974 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -108,6 +108,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) += megaraid.o
obj-$(CONFIG_MEGARAID_NEWGEN) += megaraid/
obj-$(CONFIG_MEGARAID_SAS) += megaraid/
obj-$(CONFIG_SCSI_MPT2SAS) += mpt2sas/
+obj-$(CONFIG_SCSI_UFSHCD) += ufs/
obj-$(CONFIG_SCSI_ACARD) += atp870u.o
obj-$(CONFIG_SCSI_SUNESP) += esp_scsi.o sun_esp.o
obj-$(CONFIG_SCSI_GDTH) += gdth.o
diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
new file mode 100644
index 0000000..8f27f9d
--- /dev/null
+++ b/drivers/scsi/ufs/Kconfig
@@ -0,0 +1,49 @@
+#
+# Kernel configuration file for the UFS Host Controller
+#
+# This code is based on drivers/scsi/ufs/Kconfig
+# Copyright (C) 2011 Samsung Samsung India Software Operations
+#
+# Santosh Yaraganavi <[email protected]>
+# Vinayak Holikatti <[email protected]>
+
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# NO WARRANTY
+# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+# solely responsible for determining the appropriateness of using and
+# distributing the Program and assumes all risks associated with its
+# exercise of rights under this Agreement, including but not limited to
+# the risks and costs of program errors, damage to or loss of data,
+# programs or equipment, and unavailability or interruption of operations.
+
+# DISCLAIMER OF LIABILITY
+# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+# USA.
+
+config SCSI_UFSHCD
+ tristate "Universal Flash Storage host controller driver"
+ depends on PCI && SCSI
+ ---help---
+ This is a generic driver which supports PCIe UFS Host controllers.
diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
new file mode 100644
index 0000000..adf7895
--- /dev/null
+++ b/drivers/scsi/ufs/Makefile
@@ -0,0 +1,2 @@
+# UFSHCD makefile
+obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
new file mode 100644
index 0000000..96b5cae
--- /dev/null
+++ b/drivers/scsi/ufs/ufs.h
@@ -0,0 +1,203 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufs.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <[email protected]>
+ * Vinayak Holikatti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _UFS_H
+#define _UFS_H
+
+#define TASK_REQ_UPIU_SIZE_DWORDS 8
+#define TASK_RSP_UPIU_SIZE_DWORDS 8
+
+#define MAX_CDB_SIZE 16
+#define ALIGNED_UPIU_SIZE 128
+
+#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
+ ((byte3 << 24) | (byte2 << 16) |\
+ (byte1 << 8) | (byte0))
+
+/*
+ * UFS Protocol Information Unit related definitions
+ */
+
+/* Task management functions */
+enum {
+ UFS_ABORT_TASK = 0x01,
+ UFS_ABORT_TASK_SET = 0x02,
+ UFS_CLEAR_TASK_SET = 0x04,
+ UFS_LOGICAL_RESET = 0x08,
+ UFS_QUERY_TASK = 0x80,
+ UFS_QUERY_TASK_SET = 0x81
+};
+
+/* UTP UPIU Transaction Codes Initiator to Target */
+enum {
+ UPIU_TRANSACTION_NOP_OUT = 0x00,
+ UPIU_TRANSACTION_COMMAND = 0x01,
+ UPIU_TRANSACTION_DATA_OUT = 0x02,
+ UPIU_TRANSACTION_TASK_REQ = 0x04,
+ UPIU_TRANSACTION_QUERY_REQ = 0x26
+};
+
+/* UTP UPIU Transaction Codes Target to Initiator */
+enum {
+ UPIU_TRANSACTION_NOP_IN = 0x20,
+ UPIU_TRANSACTION_RESPONSE = 0x21,
+ UPIU_TRANSACTION_DATA_IN = 0x22,
+ UPIU_TRANSACTION_TASK_RSP = 0x24,
+ UPIU_TRANSACTION_READY_XFER = 0x31,
+ UPIU_TRANSACTION_QUERY_RSP = 0x36
+};
+
+/* UPIU Read/Write flags */
+enum {
+ UPIU_CMD_FLAGS_READ = 0x40,
+ UPIU_CMD_FLAGS_WRITE = 0x20
+
+};
+
+/* UPIU Task Attributes */
+enum {
+ UPIU_TASK_ATTR_SIMPLE = 0x00,
+ UPIU_TASK_ATTR_ORDERED = 0x01,
+ UPIU_TASK_ATTR_HEADQ = 0x02,
+ UPIU_TASK_ATTR_ACA = 0x03
+};
+
+/* UTP QUERY Transaction Specific Fields OpCode */
+enum {
+ UPIU_QUERY_OPCODE_NOP = 0x0,
+ UPIU_QUERY_OPCODE_READ_DESC = 0x1,
+ UPIU_QUERY_OPCODE_WRITE_DESC = 0x2,
+ UPIU_QUERY_OPCODE_READ_ATTR = 0x3,
+ UPIU_QUERY_OPCODE_WRITE_ATTR = 0x4,
+ UPIU_QUERY_OPCODE_READ_FLAG = 0x5,
+ UPIU_QUERY_OPCODE_SET_FLAG = 0x6,
+ UPIU_QUERY_OPCODE_CLEAR_FLAG = 0x7,
+ UPIU_QUERY_OPCODE_TOGGLE_FLAG = 0x8
+};
+
+/* UTP Transfer Request Command Type (CT) */
+enum {
+ UPIU_COMMAND_SET_TYPE_SCSI = 0x0,
+ UPIU_COMMAND_SET_TYPE_UFS = 0x1,
+ UPIU_COMMAND_SET_TYPE_QUERY = 0x2
+};
+
+enum {
+ MASK_SCSI_STATUS = 0xFF,
+ MASK_TASK_RESPONSE = 0xFF00,
+ MASK_RSP_UPIU_RESULT = 0xFFFF
+};
+
+/**
+ * struct utp_upiu_header - UPIU header structure
+ * @dword_0: UPIU header DW-0
+ * @dword_1: UPIU header DW-1
+ * @dword_2: UPIU header DW-2
+ */
+struct utp_upiu_header {
+ u32 dword_0;
+ u32 dword_1;
+ u32 dword_2;
+};
+
+/**
+ * struct utp_upiu_cmd - Command UPIU structure
+ * @header: UPIU header structure DW-0 to DW-2
+ * @data_transfer_len: Data Transfer Length DW-3
+ * @cdb: Command Descriptor Block CDB DW-4 to DW-7
+ */
+struct utp_upiu_cmd {
+ struct utp_upiu_header header;
+ u32 exp_data_transfer_len;
+ u8 cdb[MAX_CDB_SIZE];
+};
+
+/**
+ * struct utp_upiu_rsp - Response UPIU structure
+ * @header: UPIU header DW-0 to DW-2
+ * @residual_transfer_count: Residual transfer count DW-3
+ * @reserved: Reserver DW-4 to DW-7
+ * @sense_data_len: Sense data length DW-8 U16
+ * @sense_data: Sense data field DW-8 to DW-12
+ */
+struct utp_upiu_rsp {
+ struct utp_upiu_header header;
+ u32 residual_transfer_count;
+ u32 reserved[4];
+ u16 sense_data_len;
+ u8 sense_data[18];
+};
+
+/**
+ * struct utp_upiu_task_req - Task request UPIU structure
+ * @header - UPIU header structure DW0 to DW-2
+ * @input_param1: Input param 1 DW-3
+ * @input_param2: Input param 2 DW-4
+ * @input_param3: Input param 3 DW-5
+ * @reserved: Reserver DW-6 to DW-7
+ */
+struct utp_upiu_task_req {
+ struct utp_upiu_header header;
+ u32 input_param1;
+ u32 input_param2;
+ u32 input_param3;
+ u32 reserved[2];
+};
+
+/**
+ * struct utp_upiu_task_rsp - Task Management Response UPIU structure
+ * @header: UPIU header structure DW0-DW-2
+ * @output_param1: Ouput param 1 DW3
+ * @output_param2: Output param 2 DW4
+ * @reserved: Reserver DW-5 to DW-7
+ */
+struct utp_upiu_task_rsp {
+ struct utp_upiu_header header;
+ u32 output_param1;
+ u32 output_param2;
+ u32 reserved[3];
+};
+
+#endif /* End of Header */
diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
new file mode 100644
index 0000000..c82eeea
--- /dev/null
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -0,0 +1,1091 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshcd.c
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <[email protected]>
+ * Vinayak Holikatti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+
+#include <asm/irq.h>
+#include <asm/byteorder.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_dbg.h>
+
+#include "ufs.h"
+#include "ufshci.h"
+
+#define UFSHCD "ufshcd"
+#define UFSHCD_DRIVER_VERSION "0.1"
+
+#ifndef NULL
+#define NULL 0
+#endif /* NULL */
+
+#define BYTES_TO_DWORDS(p) (p >> 2)
+#define UFSHCD_MMIO_BASE (hba->mmio_base)
+
+enum {
+ UFSHCD_MAX_CHANNEL = 1,
+ UFSHCD_MAX_ID = 1,
+ UFSHCD_MAX_LUNS = 8,
+ UFSHCD_CAN_QUEUE = 32,
+ BYTES_128 = 128,
+ BYTES_1024 = 1024
+};
+
+/* UFSHCD states */
+enum {
+ UFSHCD_STATE_OPERATIONAL,
+ UFSHCD_STATE_RESET,
+ UFSHCD_STATE_ERROR
+};
+
+/* Interrupt configuration options */
+enum {
+ UFSHCD_INT_DISABLE,
+ UFSHCD_INT_ENABLE,
+ UFSHCD_INT_CLEAR
+};
+
+/* Interrupt aggregation options */
+enum {
+ INT_AGGR_RESET,
+ INT_AGGR_CONFIG
+};
+
+/**
+ * struct uic_command - UIC command structure
+ * @command: UIC command
+ * @argument1: UIC command argument 1
+ * @argument2: UIC command argument 2
+ * @argument3: UIC command argument 3
+ * @cmd_active: Indicate if UIC command is outstanding
+ * @result: UIC command result
+ * @callback: routine to be called when UIC command completes
+ */
+struct uic_command {
+ u32 command;
+ u32 argument1;
+ u32 argument2;
+ u32 argument3;
+ int cmd_active;
+ int result;
+};
+
+/**
+ * struct ufs_hba - per adapter private structure
+ * @mmio_base: UFSHCI base register address
+ * @ucdl_virt_addr: UFS Command Descriptor virtual address
+ * @utrdl_virt_addr: UTP Transfer Request Descriptor virtual address
+ * @utmrdl_virt_addr: UTP Task Management Descriptor virtual address
+ * @utrdl_virt_addr_aligned: UTRD Aligned vitual address
+ * @utmrdl_virt_addr_aligned: UTMRD Aligned virtual address
+ * @ucdl_size: Memory size of UCD command block
+ * @utrdl_size: Memory size of UTRDL block
+ * @utmrdl_size: Memory size of UTMRDL block
+ * @ucdl_dma_addr: UFS Command Descriptor DMA address
+ * @utrdl_dma_addr: UTRDL DMA address
+ * @utmrdl_dma_addr: UTMRDL DMA address
+ * @utrdl_dma_addr_aligned: UTRDL aligned DMA address
+ * @utmrdl_dma_addr_aligned: UTMRDL aligned DMA address
+ * @ucdl_dma_addr_aligned: UCD aligned DMA address
+ * @dma_size:
+ * @host: Scsi_Host instance of the driver
+ * @pdev: PCI device handle
+ * @lrb: local reference block
+ * @capabilities: UFS Controller Capabilities
+ * @nutrs: Transfer Request Queue depth supported by controller
+ * @nutmrs: Task Management Queue depth supported by controller
+ * @active_uic_cmd: handle of active UIC command
+ * @ufshcd_state: UFSHCD states
+ * @int_enable_mask: Interrupt Mask Bits
+ * @uic_workq: Work queue for UIC completion handling
+ */
+struct ufs_hba {
+ void __iomem *mmio_base;
+
+ /* Virtual memory reference */
+ void *ucdl_virt_addr;
+ void *utrdl_virt_addr;
+ void *utmrdl_virt_addr;
+ void *utrdl_virt_addr_aligned;
+ void *utmrdl_virt_addr_aligned;
+ void *ucdl_virt_addr_aligned;
+
+ size_t ucdl_size;
+ size_t utrdl_size;
+ size_t utmrdl_size;
+
+ /* DMA memory reference */
+ dma_addr_t ucdl_dma_addr;
+ dma_addr_t utrdl_dma_addr;
+ dma_addr_t utmrdl_dma_addr;
+ dma_addr_t utrdl_dma_addr_aligned;
+ dma_addr_t utmrdl_dma_addr_aligned;
+ dma_addr_t ucdl_dma_addr_aligned;
+
+ size_t dma_size;
+
+ struct Scsi_Host *host;
+ struct pci_dev *pdev;
+
+ struct ufshcd_lrb *lrb;
+
+ u32 capabilities;
+ int nutrs;
+ int nutmrs;
+ u32 ufs_version;
+
+ struct uic_command active_uic_cmd;
+
+ u32 ufshcd_state;
+ u32 int_enable_mask;
+
+ /* Work Queues */
+ struct work_struct uic_workq;
+};
+
+/**
+ * struct ufshcd_lrb - command control block
+ * @utr_descriptor_ptr: UTRD address of the command
+ * @ucd_cmd_ptr: UCD address of the command
+ * @ucd_rsp_ptr: Response UPIU address for this command
+ * @ucd_prdt_ptr: PRDT address of the command
+ */
+struct ufshcd_lrb {
+ struct utp_transfer_req_desc *utr_descriptor_ptr;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
+ struct utp_upiu_rsp *ucd_rsp_ptr;
+ struct ufshcd_sg_entry *ucd_prdt_ptr;
+};
+
+/**
+ * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
+ * @hba - Pointer to adapter instance
+ *
+ * Returns UFSHCI version supported by the controller
+ */
+static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
+{
+ return readl(UFSHCD_MMIO_BASE + REG_UFS_VERSION);
+}
+
+/**
+ * ufshcd_is_device_present - Check if any device connected to
+ * the host controller
+ * @reg_hcs - host controller status register value
+ *
+ * Returns 0 if device present, non-zeo if no device detected
+ */
+static inline int ufshcd_is_device_present(u32 reg_hcs)
+{
+ return (DEVICE_PRESENT & reg_hcs) ? 0 : -1;
+}
+
+/**
+ * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
+ * @reg: Register value of host controller status
+ *
+ * Returns integer, 0 on Success and positive value if failed
+ */
+static inline int ufshcd_get_lists_status(u32 reg)
+{
+ /*
+ * The mask 0xFF is for the following HCS register bits
+ * Bit Description
+ * 0 Device Present
+ * 1 UTRLRDY
+ * 2 UTMRLRDY
+ * 3 UCRDY
+ * 4 HEI
+ * 5 DEI
+ * 6-7 reserved
+ */
+ return (((reg) & (0xFF)) >> 1) ^ (0x07);
+}
+
+/**
+ * ufshcd_get_uic_cmd_result - Get the UIC command result
+ * @hba: Pointer to adapter instance
+ *
+ * This function gets the result of UIC command completion
+ * Returns 0 on success, non zero value on error
+ */
+static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
+{
+ return readl(UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2) &
+ MASK_UIC_COMMAND_RESULT;
+}
+
+/**
+ * ufshcd_free_hba_memory - Free allocated memory for LRB request
+ * and task lists
+ * @hba: Pointer to adapter instance
+ */
+static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
+{
+ kfree(hba->lrb);
+ hba->lrb = NULL;
+
+ if (hba->utmrdl_virt_addr_aligned) {
+ dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
+ hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
+ hba->utmrdl_virt_addr = NULL;
+ hba->utmrdl_virt_addr_aligned = NULL;
+ }
+
+ if (hba->utrdl_virt_addr_aligned) {
+ dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
+ hba->utrdl_virt_addr, hba->utrdl_dma_addr);
+ hba->utrdl_virt_addr = NULL;
+ hba->utrdl_virt_addr_aligned = NULL;
+ }
+
+ if (hba->ucdl_virt_addr_aligned) {
+ dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
+ hba->ucdl_virt_addr, hba->ucdl_dma_addr);
+ hba->ucdl_virt_addr = NULL;
+ hba->ucdl_virt_addr_aligned = NULL;
+ }
+}
+
+/**
+ * ufshcd_config_int_aggr - Configure interrupt aggregation values
+ * currently there is no use case where we want to configure
+ * interrupt aggregation dynamically. So to configure interrupt
+ * aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
+ * INT_AGGR_TIMEOUT_VALUE are used.
+ * @hba: per adapter instance
+ * @option: Interrupt aggregation option
+ */
+static inline void
+ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
+{
+ switch (option) {
+ case INT_AGGR_RESET:
+ writel((INT_AGGR_ENABLE |
+ INT_AGGR_COUNTER_AND_TIMER_RESET),
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ break;
+ case INT_AGGR_CONFIG:
+ writel((INT_AGGR_ENABLE |
+ INT_AGGR_PARAM_WRITE |
+ INT_AGGR_COUNTER_THRESHOLD_VALUE |
+ INT_AGGR_TIMEOUT_VALUE),
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
+ break;
+ }
+}
+
+/**
+ * ufshcd_hba_stop - put the controller in reset state
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_stop(struct ufs_hba *hba)
+{
+ writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
+}
+
+/**
+ * ufshcd_hba_capabilities - Read controller capabilities
+ * @hba: per adapter instance
+ */
+static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
+{
+ u32 capabilities;
+
+ capabilities =
+ readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_CAPABILITIES);
+ hba->capabilities = capabilities;
+
+ /* nutrs and nutmrs are 0 based values */
+ hba->nutrs = (capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
+ hba->nutmrs =
+ ((capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
+}
+
+/**
+ * ufshcd_send_uic_command - Send UIC commands to unipro layers
+ * @hba: per adapter instance
+ * @uic_command: UIC command
+ */
+static inline void
+ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
+{
+ /* Clear interrupt status register */
+ writel((readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS)),
+ (UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
+
+ /* Write Args */
+ writel(uic_cmnd->argument1,
+ (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_1));
+ writel(uic_cmnd->argument2,
+ (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2));
+ writel(uic_cmnd->argument3,
+ (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_3));
+
+ /* Write UIC Cmd */
+ writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
+ (UFSHCD_MMIO_BASE + REG_UIC_COMMAND));
+}
+
+/**
+ * ufshcd_int_config - enable/disable interrupts
+ * @hba: per adapter instance
+ * @option: interrupt option
+ */
+static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
+{
+ switch (option) {
+ case UFSHCD_INT_ENABLE:
+ writel(hba->int_enable_mask,
+ (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
+ break;
+ case UFSHCD_INT_DISABLE:
+ if (UFSHCI_VERSION_10 == hba->ufs_version)
+ writel(readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE),
+ (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
+ else
+ writel(0, (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
+ break;
+ default:
+ dev_err(&hba->pdev->dev, "Invalid interrupt option\n");
+ break;
+ } /* end of switch */
+}
+
+/**
+ * ufshcd_memory_alloc - allocate memory for host memory space data structures
+ * @hba: per adapter instance
+ *
+ * 1) Allocate DMA memory for Command Descriptor array
+ * Each command descriptor consist of Command UPIU, Response UPIU and PRDT
+ * 2) Align allocated command descriptor address to 128 byte align.
+ * 3) Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
+ * 4) Align UTRDL address to 1KB (UFSHCI spec)
+ * 5) Allocate DMA memory for UTP Task Management Request Descriptor List
+ * (UTMRDL)
+ * 6) Align UTMRDL address to 1KB (UFSHCI spec)
+ * 7) Allocate the memory for local reference block(lrb).
+ *
+ * Returns 0 for success, non-zero in case of failure
+ */
+static int ufshcd_memory_alloc(struct ufs_hba *hba)
+{
+ /*
+ * Allocate memory for UTP command descriptors.
+ * UFSHCI requires 128 byte alignement of UCD and
+ * 64 byte alignement for PRDT. So allocating extra 128 bytes
+ */
+ hba->ucdl_size =
+ (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs) + BYTES_128;
+ hba->ucdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
+ hba->ucdl_size,
+ &hba->ucdl_dma_addr,
+ GFP_KERNEL);
+ if (NULL == hba->ucdl_virt_addr) {
+ dev_err(&hba->pdev->dev,
+ "Command Descriptor Memory allocation failed\n");
+ goto ucd_fail;
+ }
+
+ /* Align UCD to 128 bytes */
+ hba->ucdl_virt_addr_aligned =
+ (void *) ALIGN((unsigned long) hba->ucdl_virt_addr, BYTES_128);
+ hba->ucdl_dma_addr_aligned = ALIGN(hba->ucdl_dma_addr, BYTES_128);
+
+ /*
+ * Allocate memory for UTP Transfer descriptors.
+ * UFSHCI requires 1kb alignement of UTRD. So allocating
+ * extra 1024 bytes
+ */
+ hba->utrdl_size =
+ (sizeof(struct utp_transfer_req_desc) * hba->nutrs) + BYTES_1024;
+ hba->utrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
+ hba->utrdl_size,
+ &hba->utrdl_dma_addr,
+ GFP_KERNEL);
+ if (NULL == hba->utrdl_virt_addr) {
+ dev_err(&hba->pdev->dev,
+ "Transfer Descriptor Memory allocation failed\n");
+ goto utrd_fail;
+ }
+
+ /* alignement UTRD to 1kb */
+ hba->utrdl_virt_addr_aligned =
+ (void *) ALIGN((unsigned long) hba->utrdl_virt_addr, BYTES_1024);
+ hba->utrdl_dma_addr_aligned = ALIGN(hba->utrdl_dma_addr, BYTES_1024);
+
+ /*
+ * Allocate memory for UTP Task Management descriptors
+ * UFSHCI requires 1kb alignement of UTMRD. So allocating
+ * extra 1024 bytes
+ */
+ hba->utmrdl_size =
+ sizeof(struct utp_task_req_desc) * hba->nutmrs + BYTES_1024;
+ hba->utmrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
+ hba->utmrdl_size,
+ &hba->utmrdl_dma_addr,
+ GFP_KERNEL);
+ if (NULL == hba->utmrdl_virt_addr) {
+ dev_err(&hba->pdev->dev,
+ "Task Management Descriptor Memory allocation failed\n");
+ goto utmrd_fail;
+ }
+
+ /* alignement UTMRD to 1kb */
+ hba->utmrdl_virt_addr_aligned =
+ (void *) ALIGN((unsigned long) hba->utmrdl_virt_addr, BYTES_1024);
+ hba->utmrdl_dma_addr_aligned = ALIGN(hba->utmrdl_dma_addr, BYTES_1024);
+
+ /* Allocate memory for local reference block */
+ hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
+ if (NULL == hba->lrb) {
+ dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
+ goto lrb_fail;
+ }
+
+ return 0;
+
+lrb_fail:
+ dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
+ hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
+ hba->utmrdl_virt_addr = NULL;
+ hba->utmrdl_virt_addr_aligned = NULL;
+utmrd_fail:
+ dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
+ hba->utrdl_virt_addr, hba->utrdl_dma_addr);
+ hba->utrdl_virt_addr = NULL;
+ hba->utrdl_virt_addr_aligned = NULL;
+utrd_fail:
+ dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
+ hba->ucdl_virt_addr, hba->ucdl_dma_addr);
+ hba->ucdl_virt_addr = NULL;
+ hba->ucdl_virt_addr_aligned = NULL;
+ucd_fail:
+ return -ENOMEM;
+}
+
+/**
+ * ufshcd_host_memory_configure - configure local reference block with
+ * memory offsets
+ * @hba: per adapter instance
+ *
+ * Configure Host memory space
+ * 1) Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
+ * address.
+ * 2) Update each UTRD with Response UPIU offset, Response UPIU length
+ * and PRDT offset.
+ * 3) Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
+ * into local reference block.
+ */
+static void ufshcd_host_memory_configure(struct ufs_hba *hba)
+{
+ struct utp_transfer_cmd_desc *cmd_descp;
+ struct utp_transfer_req_desc *utrdlp;
+ dma_addr_t cmd_desc_dma_addr;
+ dma_addr_t cmd_desc_element_addr;
+ u16 response_offset;
+ u16 prdt_offset;
+ int cmd_desc_size;
+ int i;
+
+ utrdlp = (struct utp_transfer_req_desc *)hba->utrdl_virt_addr_aligned;
+ cmd_descp =
+ (struct utp_transfer_cmd_desc *)hba->ucdl_virt_addr_aligned;
+
+ response_offset =
+ offsetof(struct utp_transfer_cmd_desc, response_upiu);
+ prdt_offset =
+ offsetof(struct utp_transfer_cmd_desc, prd_table);
+
+ cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
+ cmd_desc_dma_addr = hba->ucdl_dma_addr_aligned;
+
+ for (i = 0; i < hba->nutrs; i++) {
+ /* Configure UTRD with command descriptor base address */
+ cmd_desc_element_addr =
+ (cmd_desc_dma_addr + (cmd_desc_size * i));
+ utrdlp[i].command_desc_base_addr_lo =
+ cpu_to_le32(cmd_desc_element_addr);
+ utrdlp[i].command_desc_base_addr_hi =
+ cpu_to_le32(cmd_desc_element_addr >> 32);
+
+ /* Response upiu and prdt offset should be in double words */
+ utrdlp[i].response_upiu_offset =
+ cpu_to_le16(BYTES_TO_DWORDS(response_offset));
+ utrdlp[i].prd_table_offset =
+ cpu_to_le16(BYTES_TO_DWORDS(prdt_offset));
+ utrdlp[i].response_upiu_length =
+ cpu_to_le16(ALIGNED_UPIU_SIZE);
+
+ hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
+ hba->lrb[i].ucd_cmd_ptr =
+ (struct utp_upiu_cmd *)(cmd_descp + i);
+ hba->lrb[i].ucd_rsp_ptr =
+ (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
+ hba->lrb[i].ucd_prdt_ptr =
+ (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
+ }
+}
+
+/**
+ * ufshcd_dme_link_startup - Notify Unipro to perform link startup
+ * @hba: per adapter instance
+ *
+ * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
+ * in order to intitialize the Unipro link startup procedure.
+ * Once the Unipro links are up, the device connected to the controller
+ * is detected.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_dme_link_startup(struct ufs_hba *hba)
+{
+ struct uic_command *uic_cmd;
+ unsigned long flags;
+
+ /* check if controller is ready to accept UIC commands */
+ if (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS)) &
+ UIC_COMMAND_READY) == 0x0) {
+ dev_err(&hba->pdev->dev,
+ "Controller not ready"
+ " to accept UIC commands\n");
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ uic_cmd = &hba->active_uic_cmd;
+ uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
+ uic_cmd->argument1 = 0;
+ uic_cmd->argument2 = 0;
+ uic_cmd->argument3 = 0;
+
+ /* Enable UIC related interrupts */
+ hba->int_enable_mask |= UIC_COMMAND_COMPL;
+ ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+ /* sending UIC commands to controller */
+ ufshcd_send_uic_command(hba, uic_cmd);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ return 0;
+}
+
+/**
+ * ufshcd_make_hba_operational - Make UFS controller operatinal
+ * @hba: per adapter instance
+ *
+ * To bring UFS host controller to operational state,
+ * 1. Check if device is present
+ * 2. Configure run-stop-registers
+ * 3. Enable required interrupts
+ * 4. Configure interrupt aggregation
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_make_hba_operational(struct ufs_hba *hba)
+{
+ u32 reg;
+
+ /* check if device present */
+ reg = readl((UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS));
+ if (ufshcd_is_device_present(reg)) {
+ dev_err(&hba->pdev->dev, "cc: Device not present\n");
+ return -EINVAL;
+ }
+
+ /*
+ * UCRDY, UTMRLDY and UTRLRDY bits must be 1
+ * DEI, HEI bits must be 0
+ */
+ if (!(ufshcd_get_lists_status(reg))) {
+ writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TASK_REQ_LIST_RUN_STOP));
+ writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
+ } else {
+ dev_err(&hba->pdev->dev,
+ "Host controller not ready to process requests");
+ return -EINVAL;
+ }
+
+ /* Enable required interrupts */
+ hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
+ UIC_ERROR |
+ UTP_TASK_REQ_COMPL |
+ DEVICE_FATAL_ERROR |
+ CONTROLLER_FATAL_ERROR |
+ SYSTEM_BUS_FATAL_ERROR);
+ ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
+
+ /* Configure interrupt aggregation */
+ ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
+
+ hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
+
+ return 0;
+}
+
+/**
+ * ufshcd_controller_enable - initialize the controller
+ * @hba: per adapter instance
+ *
+ * The controller resets its self and controller firmware start of day is
+ * kickes off. When controller is ready it will set the Host Controller
+ * Status bit to 1.
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_controller_enable(struct ufs_hba *hba)
+{
+ int retry;
+
+ /*
+ * msleep of 1 and 5 used in this function might result in msleep(20),
+ * but it was necessary to send the UFS FPGA to reset mode during
+ * development and testing of this driver. msleep can be changed to
+ * mdelay and retry count can be reduced based on the controller.
+ */
+
+ /* change controller state to "reset state" */
+ writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
+ msleep(5);
+
+ writel(CONTROLLER_ENABLE,
+ (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
+ msleep(1);
+
+ /* wait for the host controller to complete initialization */
+ retry = 10;
+ while (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE)) &
+ CONTROLLER_ENABLE) != 0x1) {
+ if (retry) {
+ retry--;
+ } else {
+ dev_err(&hba->pdev->dev,
+ "Controller enable failed\n");
+ return -EINVAL;
+ }
+ msleep(5);
+ }
+ return 0;
+}
+
+/**
+ * ufshcd_initialize_hba - start the initialization process
+ * @hba: per adapter instance
+ *
+ * Initialize the Controller
+ * 1) Enable the controller via ufshcd_controller_enable.
+ * 2) Program the Transfer Request List Address with the starting address of
+ * UTRDL.
+ *
+ * 3) Program the Task Management Request List Address with starting address
+ * of UTMRDL.
+ *
+ * Returns 0 on success, non-zero value on failure.
+ */
+static int ufshcd_initialize_hba(struct ufs_hba *hba)
+{
+ if (ufshcd_controller_enable(hba))
+ return -1;
+
+ /* Configure TR/TM address registers */
+ writel(hba->utrdl_dma_addr_aligned,
+ (UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
+ writel((hba->utrdl_dma_addr_aligned >> 32),
+ (UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
+ writel(hba->utmrdl_dma_addr_aligned,
+ (UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_L));
+ writel((hba->utmrdl_dma_addr_aligned >> 32),
+ (UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_H));
+
+ /* Initialize unipro link startup procedure */
+ return ufshcd_dme_link_startup(hba);
+}
+
+/**
+ * ufshcd_uic_cc_handler - handle UIC command completion
+ * @work: pointer to a work queue structure
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static void ufshcd_uic_cc_handler (struct work_struct *work)
+{
+ struct ufs_hba *hba;
+
+ hba = container_of(work, struct ufs_hba, uic_workq);
+
+ if ((UIC_CMD_DME_LINK_STARTUP == hba->active_uic_cmd.command) &&
+ !(ufshcd_get_uic_cmd_result(hba))) {
+
+ if (ufshcd_make_hba_operational(hba))
+ dev_err(&hba->pdev->dev,
+ "cc: hba not operational state\n");
+ return;
+ }
+}
+
+/**
+ * ufshcd_sl_intr - Interrupt service routine
+ * @hba: per adapter instance
+ * @intr_status: contains interrupts generated by the controller
+ */
+static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
+{
+ if (intr_status & UIC_COMMAND_COMPL)
+ schedule_work(&hba->uic_workq);
+}
+
+/**
+ * ufshcd_intr - Main interrupt service routine
+ * @irq: irq number
+ * @__hba: pointer to adapter instance
+ *
+ * Returns IRQ_HANDLED - If interrupt is valid
+ * IRQ_NONE - If invalid interrupt
+ */
+static irqreturn_t ufshcd_intr(int irq, void *__hba)
+{
+ unsigned long flags;
+ u32 intr_status;
+ irqreturn_t retval = IRQ_NONE;
+ struct ufs_hba *hba = __hba;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ intr_status = readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS);
+
+ if (intr_status) {
+ ufshcd_sl_intr(hba, intr_status);
+
+ /* If UFSHCI 1.0 then clear interrupt status register */
+ if (UFSHCI_VERSION_10 == hba->ufs_version)
+ writel(intr_status,
+ (UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
+ retval = IRQ_HANDLED;
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+ return retval;
+}
+
+static struct scsi_host_template ufshcd_driver_template = {
+ .module = THIS_MODULE,
+ .name = UFSHCD,
+ .proc_name = UFSHCD,
+ .this_id = -1,
+};
+
+/**
+ * ufshcd_shutdown - main funciton to put the controller in reset state
+ * @pdev: pointer to PCI device handle
+ */
+static void ufshcd_shutdown(struct pci_dev *pdev)
+{
+ ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
+}
+
+#ifdef CONFIG_PM
+/**
+ * ufshcd_suspend - suspend power management function
+ * @pdev: pointer to PCI device handle
+ * @state: power state
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ return -ENOSYS;
+}
+
+/**
+ * ufshcd_resume - resume power management function
+ * @pdev: pointer to PCI device handle
+ *
+ * Returns -ENOSYS
+ */
+static int ufshcd_resume(struct pci_dev *pdev)
+{
+ return -ENOSYS;
+}
+#endif /* CONFIG_PM */
+
+/**
+ * ufshcd_hba_free - free allocated memory for
+ * host memory space data structures
+ * @hba: per adapter instance
+ */
+static void ufshcd_hba_free(struct ufs_hba *hba)
+{
+ iounmap(UFSHCD_MMIO_BASE);
+ ufshcd_free_hba_memory(hba);
+ pci_release_regions(hba->pdev);
+}
+
+/**
+ * ufshcd_remove - deallocate PCI/SCSI host and host memory space
+ * data structure memory
+ * @pdev - pointer to PCI handle
+ */
+static void ufshcd_remove(struct pci_dev *pdev)
+{
+ struct ufs_hba *hba = pci_get_drvdata(pdev);
+
+ /* disable interrupts */
+ ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+ free_irq(pdev->irq, hba);
+
+ ufshcd_hba_stop(hba);
+ ufshcd_hba_free(hba);
+
+ scsi_remove_host(hba->host);
+ scsi_host_put(hba->host);
+ pci_set_drvdata(pdev, NULL);
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+}
+
+/**
+ * ufshcd_set_dma_mask - Set dma addressing
+ * @pdev: PCI device struct
+ *
+ * Returns 0 for success, non-zero for failure
+ */
+static int ufshcd_set_dma_mask(struct pci_dev *pdev)
+{
+ int err;
+
+ do {
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
+ if (!err) {
+ err = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(64));
+ break;
+ }
+ err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
+ if (!err)
+ err = pci_set_consistent_dma_mask(pdev,
+ DMA_BIT_MASK(32));
+ } while (0);
+
+ return err;
+}
+
+/**
+ * ufshcd_probe - probe routine of the driver
+ * @pdev: pointer to PCI device handle
+ * @id: PCI device id
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int __devinit
+ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ int ufs_hba_len;
+ int err;
+
+ ufs_hba_len = sizeof(struct ufs_hba);
+ err = pci_enable_device(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "pci_enable_device failed\n");
+ goto out_error;
+ }
+
+ pci_set_master(pdev);
+
+ host = scsi_host_alloc(&ufshcd_driver_template, ufs_hba_len);
+ if (!host) {
+ dev_err(&pdev->dev, "scsi_host_alloc failed\n");
+ err = -ENOMEM;
+ goto out_disable;
+ }
+ hba = (struct ufs_hba *)host->hostdata;
+
+ err = pci_request_regions(pdev, UFSHCD);
+ if (err < 0) {
+ dev_err(&pdev->dev, "request regions failed\n");
+ goto out_disable;
+ }
+
+ hba->mmio_base = pci_ioremap_bar(pdev, 0);
+ if (!hba->mmio_base) {
+ dev_err(&pdev->dev, "memory map failed\n");
+ err = -ENOMEM;
+ goto out_release_regions;
+ }
+
+ err = ufshcd_set_dma_mask(pdev);
+ if (err) {
+ dev_err(&pdev->dev, "set dma mask failed\n");
+ goto out_iounmap;
+ }
+
+ hba->host = host;
+ hba->pdev = pdev;
+
+ /* Read capabilities registers */
+ ufshcd_hba_capabilities(hba);
+
+ /* Get UFS version supported by the controller */
+ hba->ufs_version = ufshcd_get_ufs_version(hba);
+
+ /* Allocate memory for host memory space */
+ err = ufshcd_memory_alloc(hba);
+ if (err) {
+ dev_err(&pdev->dev, "Memory allocation failed\n");
+ goto out_iounmap;
+ }
+
+ /* Configure LRB */
+ ufshcd_host_memory_configure(hba);
+
+ host->can_queue = hba->nutrs;
+ host->max_id = UFSHCD_MAX_ID;
+ host->max_lun = UFSHCD_MAX_LUNS;
+ host->max_channel = UFSHCD_MAX_CHANNEL;
+ host->unique_id = host->host_no;
+
+ /* Initialize work queues */
+ INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
+
+ /* IRQ registration */
+ err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
+ if (err) {
+ dev_err(&pdev->dev, "request irq failed\n");
+ goto out_lrb_free;
+ }
+
+ pci_set_drvdata(pdev, hba);
+
+ err = scsi_add_host(host, &pdev->dev);
+ if (err) {
+ dev_err(&pdev->dev, "scsi_add_host failed\n");
+ goto out_free_irq;
+ }
+
+ /* Initialization routine */
+ err = ufshcd_initialize_hba(hba);
+ if (err) {
+ dev_err(&pdev->dev, "Initialization failed\n");
+ goto out_free_irq;
+ }
+
+ return 0;
+
+out_free_irq:
+ free_irq(pdev->irq, hba);
+out_lrb_free:
+ ufshcd_free_hba_memory(hba);
+out_iounmap:
+ iounmap(hba->mmio_base);
+out_release_regions:
+ pci_release_regions(pdev);
+out_disable:
+ scsi_host_put(host);
+ pci_clear_master(pdev);
+ pci_disable_device(pdev);
+out_error:
+ return err;
+}
+
+static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
+ { 0x144D, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { } /* terminate list */
+};
+
+MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
+
+static struct pci_driver ufshcd_pci_driver = {
+ .name = UFSHCD,
+ .id_table = ufshcd_pci_tbl,
+ .probe = ufshcd_probe,
+ .remove = __devexit_p(ufshcd_remove),
+ .shutdown = ufshcd_shutdown,
+#ifdef CONFIG_PM
+ .suspend = ufshcd_suspend,
+ .resume = ufshcd_resume,
+#endif
+};
+
+/**
+ * ufshcd_init - Driver registration routine
+ */
+static int __init ufshcd_init(void)
+{
+ return pci_register_driver(&ufshcd_pci_driver);
+}
+module_init(ufshcd_init);
+
+/**
+ * ufshcd_exit - Driver exit clean-up routine
+ */
+static void __exit ufshcd_exit(void)
+{
+ pci_unregister_driver(&ufshcd_pci_driver);
+}
+module_exit(ufshcd_exit);
+
+
+MODULE_AUTHOR("Santosh Yaragnavi, Vinayak Holikatti");
+MODULE_DESCRIPTION("Generic UFS host controller driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(UFSHCD_DRIVER_VERSION);
diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
new file mode 100644
index 0000000..f8701b7
--- /dev/null
+++ b/drivers/scsi/ufs/ufshci.h
@@ -0,0 +1,360 @@
+/*
+ * Universal Flash Storage Host controller driver
+ *
+ * This code is based on drivers/scsi/ufs/ufshci.h
+ * Copyright (C) 2011-2012 Samsung India Software Operations
+ *
+ * Santosh Yaraganavi <[email protected]>
+ * Vinayak Holikatti <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * NO WARRANTY
+ * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
+ * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
+ * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
+ * solely responsible for determining the appropriateness of using and
+ * distributing the Program and assumes all risks associated with its
+ * exercise of rights under this Agreement, including but not limited to
+ * the risks and costs of program errors, damage to or loss of data,
+ * programs or equipment, and unavailability or interruption of operations.
+
+ * DISCLAIMER OF LIABILITY
+ * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
+ * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _UFSHCI_H
+#define _UFSHCI_H
+
+/* UFSHCI Registers */
+enum {
+ REG_CONTROLLER_CAPABILITIES = 0x00,
+ REG_UFS_VERSION = 0x08,
+ REG_CONTROLLER_DEV_ID = 0x10,
+ REG_CONTROLLER_PROD_ID = 0x14,
+ REG_INTERRUPT_STATUS = 0x20,
+ REG_INTERRUPT_ENABLE = 0x24,
+ REG_CONTROLLER_STATUS = 0x30,
+ REG_CONTROLLER_ENABLE = 0x34,
+ REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER = 0x38,
+ REG_UIC_ERROR_CODE_DATA_LINK_LAYER = 0x3C,
+ REG_UIC_ERROR_CODE_NETWORK_LAYER = 0x40,
+ REG_UIC_ERROR_CODE_TRANSPORT_LAYER = 0x44,
+ REG_UIC_ERROR_CODE_DME = 0x48,
+ REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL = 0x4C,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_L = 0x50,
+ REG_UTP_TRANSFER_REQ_LIST_BASE_H = 0x54,
+ REG_UTP_TRANSFER_REQ_DOOR_BELL = 0x58,
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR = 0x5C,
+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP = 0x60,
+ REG_UTP_TASK_REQ_LIST_BASE_L = 0x70,
+ REG_UTP_TASK_REQ_LIST_BASE_H = 0x74,
+ REG_UTP_TASK_REQ_DOOR_BELL = 0x78,
+ REG_UTP_TASK_REQ_LIST_CLEAR = 0x7C,
+ REG_UTP_TASK_REQ_LIST_RUN_STOP = 0x80,
+ REG_UIC_COMMAND = 0x90,
+ REG_UIC_COMMAND_ARG_1 = 0x94,
+ REG_UIC_COMMAND_ARG_2 = 0x98,
+ REG_UIC_COMMAND_ARG_3 = 0x9C
+};
+
+/* Controller capability masks */
+enum {
+ MASK_TRANSFER_REQUESTS_SLOTS = 0x0000001F,
+ MASK_TASK_MANAGEMENT_REQUEST_SLOTS = 0x00070000,
+ MASK_64_ADDRESSING_SUPPORT = 0x01000000,
+ MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
+ MASK_UIC_DME_TEST_MODE_SUPPORT = 0x04000000
+};
+
+/* UFS Version 08h */
+#define MINOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 0)
+#define MAJOR_VERSION_NUM_MASK UFS_MASK(0xFFFF, 16)
+
+/* Controller UFSHCI version */
+enum {
+ UFSHCI_VERSION_10 = 0x00010000,
+ UFSHCI_VERSION_11 = 0x00010100
+};
+
+/*
+ * HCDDID - Host Controller Identification Descriptor
+ * - Device ID and Device Class 10h
+ */
+#define DEVICE_CLASS UFS_MASK(0xFFFF, 0)
+#define DEVICE_ID UFS_MASK(0xFF, 24)
+
+/*
+ * HCPMID - Host Controller Identification Descriptor
+ * - Product/Manufacturer ID 14h
+ */
+#define MANUFACTURE_ID_MASK UFS_MASK(0xFFFF, 0)
+#define PRODUCT_ID_MASK UFS_MASK(0xFFFF, 16)
+
+#define UFS_BIT(x) (1L << (x))
+
+#define UTP_TRANSFER_REQ_COMPL UFS_BIT(0)
+#define UIC_DME_END_PT_RESET UFS_BIT(1)
+#define UIC_ERROR UFS_BIT(2)
+#define UIC_TEST_MODE UFS_BIT(3)
+#define UIC_POWER_MODE UFS_BIT(4)
+#define UIC_HIBERNATE_EXIT UFS_BIT(5)
+#define UIC_HIBERNATE_ENTER UFS_BIT(6)
+#define UIC_LINK_LOST UFS_BIT(7)
+#define UIC_LINK_STARTUP UFS_BIT(8)
+#define UTP_TASK_REQ_COMPL UFS_BIT(9)
+#define UIC_COMMAND_COMPL UFS_BIT(10)
+#define DEVICE_FATAL_ERROR UFS_BIT(11)
+#define CONTROLLER_FATAL_ERROR UFS_BIT(16)
+#define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17)
+
+#define UFSHCD_ERROR_MASK (UIC_ERROR |\
+ DEVICE_FATAL_ERROR |\
+ CONTROLLER_FATAL_ERROR |\
+ SYSTEM_BUS_FATAL_ERROR)
+
+#define INT_FATAL_ERRORS (DEVICE_FATAL_ERROR |\
+ CONTROLLER_FATAL_ERROR |\
+ SYSTEM_BUS_FATAL_ERROR)
+
+/* HCS - Host Controller Status 30h */
+#define DEVICE_PRESENT UFS_BIT(0)
+#define UTP_TRANSFER_REQ_LIST_READY UFS_BIT(1)
+#define UTP_TASK_REQ_LIST_READY UFS_BIT(2)
+#define UIC_COMMAND_READY UFS_BIT(3)
+#define HOST_ERROR_INDICATOR UFS_BIT(4)
+#define DEVICE_ERROR_INDICATOR UFS_BIT(5)
+#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8)
+
+/* HCE - Host Controller Enable 34h */
+#define CONTROLLER_ENABLE UFS_BIT(0)
+
+/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
+#define UIC_PHY_ADAPTER_LAYER_ERROR UFS_BIT(31)
+#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK 0x1F
+
+/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
+#define UIC_DATA_LINK_LAYER_ERROR UFS_BIT(31)
+#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK 0x7FFF
+#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT 0x2000
+
+/* UECN - Host UIC Error Code Network Layer 40h */
+#define UIC_NETWORK_LAYER_ERROR UFS_BIT(31)
+#define UIC_NETWORK_LAYER_ERROR_CODE_MASK 0x7
+
+/* UECT - Host UIC Error Code Transport Layer 44h */
+#define UIC_TRANSPORT_LAYER_ERROR UFS_BIT(31)
+#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK 0x7F
+
+/* UECDME - Host UIC Error Code DME 48h */
+#define UIC_DME_ERROR UFS_BIT(31)
+#define UIC_DME_ERROR_CODE_MASK 0x1
+
+#define INT_AGGR_TIMEOUT_VAL_MASK 0xFF
+#define INT_AGGR_COUNTER_THRESHOLD_MASK UFS_MASK(0x1F, 8)
+#define INT_AGGR_COUNTER_AND_TIMER_RESET UFS_BIT(16)
+#define INT_AGGR_STATUS_BIT UFS_BIT(20)
+#define INT_AGGR_PARAM_WRITE UFS_BIT(24)
+#define INT_AGGR_ENABLE UFS_BIT(31)
+
+/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
+#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
+
+/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
+#define UTP_TASK_REQ_LIST_RUN_STOP_BIT UFS_BIT(0)
+
+/* UICCMD - UIC Command */
+#define COMMAND_OPCODE_MASK 0xFF
+#define GEN_SELECTOR_INDEX_MASK 0xFFFF
+
+#define MIB_ATTRIBUTE_MASK UFS_MASK(0xFFFF, 16)
+#define RESET_LEVEL 0xFF
+
+#define ATTR_SET_TYPE_MASK UFS_MASK(0xFF, 16)
+#define CONFIG_RESULT_CODE_MASK 0xFF
+#define GENERIC_ERROR_CODE_MASK 0xFF
+
+/* UIC Commands */
+enum {
+ UIC_CMD_DME_GET = 0x01,
+ UIC_CMD_DME_SET = 0x02,
+ UIC_CMD_DME_PEER_GET = 0x03,
+ UIC_CMD_DME_PEER_SET = 0x04,
+ UIC_CMD_DME_POWERON = 0x10,
+ UIC_CMD_DME_POWEROFF = 0x11,
+ UIC_CMD_DME_ENABLE = 0x12,
+ UIC_CMD_DME_RESET = 0x14,
+ UIC_CMD_DME_END_PT_RST = 0x15,
+ UIC_CMD_DME_LINK_STARTUP = 0x16,
+ UIC_CMD_DME_HIBER_ENTER = 0x17,
+ UIC_CMD_DME_HIBER_EXIT = 0x18,
+ UIC_CMD_DME_TEST_MODE = 0x1A
+};
+
+/* UIC Config result code / Generic error code */
+enum {
+ UIC_CMD_RESULT_SUCCESS = 0x00,
+ UIC_CMD_RESULT_INVALID_ATTR = 0x01,
+ UIC_CMD_RESULT_FAILURE = 0x01,
+ UIC_CMD_RESULT_INVALID_ATTR_VALUE = 0x02,
+ UIC_CMD_RESULT_READ_ONLY_ATTR = 0x03,
+ UIC_CMD_RESULT_WRITE_ONLY_ATTR = 0x04,
+ UIC_CMD_RESULT_BAD_INDEX = 0x05,
+ UIC_CMD_RESULT_LOCKED_ATTR = 0x06,
+ UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX = 0x07,
+ UIC_CMD_RESULT_PEER_COMM_FAILURE = 0x08,
+ UIC_CMD_RESULT_BUSY = 0x09,
+ UIC_CMD_RESULT_DME_FAILURE = 0x0A
+};
+
+#define MASK_UIC_COMMAND_RESULT 0xFF
+
+#define INT_AGGR_COUNTER_THRESHOLD_VALUE (0x1F << 8)
+#define INT_AGGR_TIMEOUT_VALUE (0x02)
+
+/*
+ * Request Descriptor Definitions
+ */
+
+/* Transfer request command type */
+enum {
+ UTP_CMD_TYPE_SCSI = 0x0,
+ UTP_CMD_TYPE_UFS = 0x1,
+ UTP_CMD_TYPE_DEV_MANAGE = 0x2
+};
+
+enum {
+ UTP_SCSI_COMMAND = 0x00000000,
+ UTP_NATIVE_UFS_COMMAND = 0x10000000,
+ UTP_DEVICE_MANAGEMENT_FUNCTION = 0x20000000,
+ UTP_REQ_DESC_INT_CMD = 0x01000000
+};
+
+/* UTP Transfer Request Data Direction (DD) */
+enum {
+ UTP_NO_DATA_TRANSFER = 0x00000000,
+ UTP_HOST_TO_DEVICE = 0x02000000,
+ UTP_DEVICE_TO_HOST = 0x04000000
+};
+
+/* Overall command status values */
+enum {
+ OCS_SUCCESS = 0x0,
+ OCS_INVALID_CMD_TABLE_ATTR = 0x1,
+ OCS_INVALID_PRDT_ATTR = 0x2,
+ OCS_MISMATCH_DATA_BUF_SIZE = 0x3,
+ OCS_MISMATCH_RESP_UPIU_SIZE = 0x4,
+ OCS_PEER_COMM_FAILURE = 0x5,
+ OCS_ABORTED = 0x6,
+ OCS_FATAL_ERROR = 0x7,
+ OCS_INVALID_COMMAND_STATUS = 0x0F,
+ MASK_OCS = 0x0F
+};
+
+/**
+ * struct ufshcd_sg_entry - UFSHCI PRD Entry
+ * @base_addr: Lower 32bit physical address DW-0
+ * @upper_addr: Upper 32bit physical address DW-1
+ * @reserved: Reserved for future use DW-2
+ * @size: size of physical segment DW-3
+ */
+struct ufshcd_sg_entry {
+ u32 base_addr;
+ u32 upper_addr;
+ u32 reserved;
+ u32 size;
+};
+
+/**
+ * struct utp_transfer_cmd_desc - UFS Commad Descriptor structure
+ * @command_upiu: Command UPIU Frame address
+ * @response_upiu: Response UPIU Frame address
+ * @prd_table: Physcial Region Descriptor
+ */
+struct utp_transfer_cmd_desc {
+ u8 command_upiu[ALIGNED_UPIU_SIZE];
+ u8 response_upiu[ALIGNED_UPIU_SIZE];
+ struct ufshcd_sg_entry prd_table[SG_ALL];
+};
+
+/**
+ * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
+ * @dword0: Descriptor Header DW0
+ * @dword1: Descriptor Header DW1
+ * @dword2: Descriptor Header DW2
+ * @dword3: Descriptor Header DW3
+ */
+struct request_desc_header {
+ u32 dword_0;
+ u32 dword_1;
+ u32 dword_2;
+ u32 dword_3;
+};
+
+/**
+ * struct utp_transfer_req_desc - UTRD structure
+ * @header: UTRD header DW-0 to DW-3
+ * @command_desc_base_addr_lo: UCD base address low DW-4
+ * @command_desc_base_addr_hi: UCD base address high DW-5
+ * @response_upiu_length: response UPIU length DW-6
+ * @response_upiu_offset: response UPIU offset DW-6
+ * @prd_table_length: Physical region descriptor length DW-7
+ * @prd_table_offset: Physical region descriptor offset DW-7
+ */
+struct utp_transfer_req_desc {
+
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-5*/
+ u32 command_desc_base_addr_lo;
+ u32 command_desc_base_addr_hi;
+
+ /* DW 6 */
+ u16 response_upiu_length;
+ u16 response_upiu_offset;
+
+ /* DW 7 */
+ u16 prd_table_length;
+ u16 prd_table_offset;
+};
+
+/**
+ * struct utp_task_req_desc - UTMRD structure
+ * @header: UTMRD header DW-0 to DW-3
+ * @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
+ * @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
+ */
+struct utp_task_req_desc {
+
+ /* DW 0-3 */
+ struct request_desc_header header;
+
+ /* DW 4-11 */
+ u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
+
+ /* DW 12-19 */
+ u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
+};
+
+#endif /* End of Header */
--
1.7.5.4

2012-02-02 04:58:28

by vinayak holikatti

[permalink] [raw]
Subject: [PATCH 3/4] [SCSI] ufshcd: UFSHCI error handling

From: Santosh Yaraganavi <[email protected]>

UFSHCI error handling includes support for:
- UFS host controller errors
- System bus errors
- Unipro errors

Signed-off-by: Santosh Yaraganavi <[email protected]>
Signed-off-by: Vinayak Holikatti <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Reviewed-by: Saugata Das <[email protected]>
Reviewed-by: Vishak G <[email protected]>
Reviewed-by: Girish K S <[email protected]>
---
drivers/scsi/ufs/ufshcd.c | 104 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 104 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 23d758b..1bfae84 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -64,6 +64,7 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>

#include "ufs.h"
#include "ufshci.h"
@@ -153,9 +154,13 @@ struct uic_command {
* @nutrs: Transfer Request Queue depth supported by controller
* @nutmrs: Task Management Queue depth supported by controller
* @active_uic_cmd: handle of active UIC command
+ * @ufshcd_tm_wait_queue: wait queue for task management
+ * @tm_condition: array of condition varibles for task management
* @ufshcd_state: UFSHCD states
* @int_enable_mask: Interrupt Mask Bits
* @uic_workq: Work queue for UIC completion handling
+ * @feh_workq: Work queue for fatal controller error handling
+ * @errors: HBA errors
*/
struct ufs_hba {
void __iomem *mmio_base;
@@ -201,6 +206,10 @@ struct ufs_hba {

/* Work Queues */
struct work_struct uic_workq;
+ struct work_struct feh_workq;
+
+ /* HBA Errors */
+ u32 errors;
};

/**
@@ -924,6 +933,9 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
/* Configure interrupt aggregation */
ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);

+ if (UFSHCD_STATE_RESET == hba->ufshcd_state)
+ scsi_unblock_requests(hba->host);
+
hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
scsi_scan_host(hba->host);

@@ -1009,6 +1021,53 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
}

/**
+ * ufshcd_do_reset - reset the host controller
+ * @hba: per adapter instance
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_do_reset(struct ufs_hba *hba)
+{
+ struct ufshcd_lrb *lrbp;
+ unsigned long flags;
+ int tag;
+
+ /* block commands from midlayer */
+ scsi_block_requests(hba->host);
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+ hba->ufshcd_state = UFSHCD_STATE_RESET;
+
+ /* send controller to reset state */
+ writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
+
+ /* abort outstanding commands */
+ for (tag = 0; tag < hba->host->can_queue; tag++) {
+ lrbp = &hba->lrb[tag];
+
+ /* if lrbp->cmd is not NULL, the command is outstanding */
+ if (lrbp->cmd) {
+ scsi_dma_unmap(lrbp->cmd);
+ lrbp->cmd->result = DID_RESET << 16;
+ lrbp->cmd->scsi_done(lrbp->cmd);
+ lrbp->cmd = NULL;
+ }
+ }
+
+ /* disable all the interrupts */
+ ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ /* start the initialization process */
+ if (ufshcd_initialize_hba(hba)) {
+ dev_err(&hba->pdev->dev,
+ "Reset: Controller initialization failed\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/**
* ufshcd_slave_alloc - handle initial scsi devie configurations
* @sdev: pointer to scsi device
*
@@ -1215,12 +1274,56 @@ static void ufshcd_uic_cc_handler (struct work_struct *work)
}

/**
+ * ufshcd_fatal_err_handler - handle fatal errors
+ * @hba: per adapter instance
+ */
+static void ufshcd_fatal_err_handler(struct work_struct *work)
+{
+ struct ufs_hba *hba;
+ hba = container_of(work, struct ufs_hba, feh_workq);
+
+ /* check if reset is already in progress */
+ if (UFSHCD_STATE_RESET != hba->ufshcd_state)
+ ufshcd_do_reset(hba);
+}
+
+/**
+ * ufshcd_err_handler - Check for fatal errors
+ * @work: pointer to a work queue structure
+ */
+static void ufshcd_err_handler(struct ufs_hba *hba)
+{
+ u32 reg;
+
+ if (hba->errors & INT_FATAL_ERRORS)
+ goto fatal_eh;
+
+ if (hba->errors & UIC_ERROR) {
+
+ reg = readl(UFSHCD_MMIO_BASE +
+ REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER);
+ if (reg & UIC_DATA_LINK_LAYER_ERROR_PA_INIT)
+ goto fatal_eh;
+ }
+
+ return;
+
+fatal_eh:
+ hba->ufshcd_state = UFSHCD_STATE_ERROR;
+ schedule_work(&hba->feh_workq);
+}
+
+/**
* ufshcd_sl_intr - Interrupt service routine
* @hba: per adapter instance
* @intr_status: contains interrupts generated by the controller
*/
static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
{
+ hba->errors = UFSHCD_ERROR_MASK & intr_status;
+ if (hba->errors)
+ ufshcd_err_handler(hba);
+
if (intr_status & UIC_COMMAND_COMPL)
schedule_work(&hba->uic_workq);

@@ -1446,6 +1549,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)

/* Initialize work queues */
INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
+ INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);

/* IRQ registration */
err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
--
1.7.5.4

2012-02-02 04:58:43

by vinayak holikatti

[permalink] [raw]
Subject: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

From: Santosh Yaraganavi <[email protected]>

UFSHCD SCSI error handling includes following implementations,
- Abort task
- Device reset
- Host reset

Signed-off-by: Santosh Yaraganavi <[email protected]>
Signed-off-by: Vinayak Holikatti <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Reviewed-by: Saugata Das <[email protected]>
Reviewed-by: Vishak G <[email protected]>
Reviewed-by: Girish K S <[email protected]>
---
drivers/scsi/ufs/ufshcd.c | 312 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 312 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index 1bfae84..7589dd1 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -76,6 +76,8 @@
#define NULL 0
#endif /* NULL */

+#define UFSHCD_MAX_TM_SLOTS 0xFF
+
#define BYTES_TO_DWORDS(p) (p >> 2)
#define UFSHCD_MMIO_BASE (hba->mmio_base)

@@ -83,6 +85,7 @@ enum {
UFSHCD_MAX_CHANNEL = 1,
UFSHCD_MAX_ID = 1,
UFSHCD_MAX_LUNS = 8,
+ UFSHCD_MAX_TM_CMDS = 8,
UFSHCD_CMD_PER_LUN = 16,
UFSHCD_CAN_QUEUE = 32,
BYTES_128 = 128,
@@ -149,6 +152,7 @@ struct uic_command {
* @host: Scsi_Host instance of the driver
* @pdev: PCI device handle
* @lrb: local reference block
+ * @outstanding_tasks: Bits representing outstanding task requests
* @outstanding_reqs: Bits representing outstanding transfer requests
* @capabilities: UFS Controller Capabilities
* @nutrs: Transfer Request Queue depth supported by controller
@@ -192,6 +196,7 @@ struct ufs_hba {

struct ufshcd_lrb *lrb;

+ u32 outstanding_tasks;
u32 outstanding_reqs;

u32 capabilities;
@@ -200,6 +205,8 @@ struct ufs_hba {
u32 ufs_version;

struct uic_command active_uic_cmd;
+ wait_queue_head_t ufshcd_tm_wait_queue;
+ u8 tm_condition[UFSHCD_MAX_TM_CMDS];

u32 ufshcd_state;
u32 int_enable_mask;
@@ -278,6 +285,30 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
}

/**
+ * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
+ * @task_req_descp: pointer to utp_task_req_desc structure
+ *
+ * This function is used to get the OCS field from UTMRD
+ * Returns the OCS field in the UTMRD
+ */
+static inline int
+ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
+{
+ return task_req_descp->header.dword_2 & MASK_OCS;
+}
+
+/**
+ * ufshcd_is_tmq_full - checks if the task management slots are full
+ * @outstanding_tasks: contains the task management doorbell value
+ *
+ * Returns 1 if a free slot available, 0 if task slots are full
+ */
+static inline int ufshcd_is_tmq_full(u32 outstanding_tasks)
+{
+ return (UFSHCD_MAX_TM_SLOTS == outstanding_tasks) ? 0 : 1;
+}
+
+/**
* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
* @reg: Register value of host controller status
*
@@ -1098,6 +1129,45 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
}

/**
+ * ufshcd_task_req_compl - handle task management request completion
+ * @hba: per adapter instance
+ * @index: index of the completed request
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
+{
+ struct utp_upiu_task_rsp *task_rsp_upiup;
+ struct utp_task_req_desc *task_req_descp;
+ unsigned long flags;
+ int ocs_value;
+ int task_response = -1;
+
+ spin_lock_irqsave(hba->host->host_lock, flags);
+
+ /* clear completed tasks from outstanding_tasks */
+ hba->outstanding_tasks ^= index;
+
+ task_req_descp =
+ (struct utp_task_req_desc *)hba->utmrdl_virt_addr_aligned;
+ ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
+
+ if (OCS_SUCCESS == ocs_value) {
+
+ task_rsp_upiup = (struct utp_upiu_task_rsp *)
+ task_req_descp[index].task_rsp_upiu;
+ task_response = be32_to_cpu(task_rsp_upiup->header.dword_1);
+ task_response = ((task_response & MASK_TASK_RESPONSE) >> 8);
+
+ /* clear task response */
+ memset(task_rsp_upiup, 0, sizeof(struct utp_upiu_task_rsp));
+ }
+ spin_unlock_irqrestore(hba->host->host_lock, flags);
+
+ return task_response;
+}
+
+/**
* ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
* @lrb: pointer to local reference block of completed command
* @scsi_status: SCSI command status
@@ -1314,6 +1384,31 @@ fatal_eh:
}

/**
+ * ufshcd_tmc_handler - handle task management function completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_tmc_handler(struct ufs_hba *hba)
+{
+ u32 completed_reqs;
+ u32 tm_doorbell;
+ int i;
+
+ tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
+ completed_reqs = tm_doorbell ^ hba->outstanding_tasks;
+ for (i = 0; i < hba->nutmrs; i++) {
+ if (completed_reqs & (1 << i)) {
+
+ /*
+ * Change the default value 0xFF to 0 to indicate
+ * completion of respective task management command
+ */
+ hba->tm_condition[i] = 0;
+ wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
+ }
+ }
+}
+
+/**
* ufshcd_sl_intr - Interrupt service routine
* @hba: per adapter instance
* @intr_status: contains interrupts generated by the controller
@@ -1327,6 +1422,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
if (intr_status & UIC_COMMAND_COMPL)
schedule_work(&hba->uic_workq);

+ if (intr_status & UTP_TASK_REQ_COMPL)
+ ufshcd_tmc_handler(hba);
+
if (intr_status & UTP_TRANSFER_REQ_COMPL)
ufshcd_transfer_req_compl(hba);
}
@@ -1362,6 +1460,209 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
return retval;
}

+/**
+ * ufshcd_issue_tm_cmd - issues task management commands to controller
+ * @hba: per adapter instance
+ * @lrbp: pointer to local reference block
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int
+ufshcd_issue_tm_cmd(struct ufs_hba *hba,
+ struct ufshcd_lrb *lrbp,
+ u8 tm_function)
+{
+ struct utp_task_req_desc *task_req_descp;
+ struct utp_upiu_task_req *task_req_upiup;
+ struct Scsi_Host *host;
+ unsigned long flags;
+ int i;
+ int free_slot = 0;
+ int err = -1;
+
+ host = hba->host;
+
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* If task management queue is full */
+ if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
+ dev_err(&hba->pdev->dev, "Task management queue full\n");
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return err;
+ }
+
+ for (i = 0; i < hba->nutmrs; i++) {
+ if (!(hba->outstanding_tasks & (1 << i))) {
+ free_slot = i;
+ break;
+ }
+ }
+
+ /* Configure task request descriptor */
+ task_req_descp =
+ (struct utp_task_req_desc *) hba->utmrdl_virt_addr_aligned;
+ task_req_descp += free_slot;
+
+ memset(task_req_descp, 0, sizeof(struct utp_task_req_desc));
+ task_req_descp->header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
+ task_req_descp->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+ /* Configure task request UPIU */
+ task_req_upiup =
+ (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
+ task_req_upiup->header.dword_0 =
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
+ lrbp->lun, lrbp->task_tag));
+ task_req_upiup->header.dword_1 =
+ cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
+
+ task_req_upiup->input_param1 = lrbp->lun;
+ task_req_upiup->input_param1 =
+ cpu_to_be32(task_req_upiup->input_param1);
+ task_req_upiup->input_param2 = lrbp->task_tag;
+ task_req_upiup->input_param2 =
+ cpu_to_be32(task_req_upiup->input_param2);
+
+ /* send command to the controller */
+ hba->outstanding_tasks |= (1 << free_slot);
+ writel((1 << free_slot),
+ (UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_DOOR_BELL));
+
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ /* wait until the task management command is completed */
+ err = wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
+ (hba->tm_condition[free_slot] != 0),
+ 60 * HZ);
+ hba->tm_condition[free_slot] = 0xFF;
+ if (!err) {
+ dev_err(&hba->pdev->dev,
+ "Task management command timed-out\n");
+ return err;
+ }
+ return ufshcd_task_req_compl(hba, free_slot);
+}
+
+/**
+ * ufshcd_device_reset - reset device and abort all the pending commands
+ * @cmd: SCSI command pointer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_device_reset(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ int reset_lun;
+ unsigned int tag;
+ u32 i;
+ u32 pos; /* pos: represents a bit in a register */
+ int err = -1;
+
+ host = cmd->device->host;
+ hba = (struct ufs_hba *)host->hostdata;
+ tag = cmd->request->tag;
+
+ if ((hba->lrb[tag].cmd == cmd))
+ reset_lun = hba->lrb[tag].lun;
+ else
+ goto out;
+
+ err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
+ if (!err) {
+ spin_lock_irqsave(host->host_lock, flags);
+ for (i = 0; i < hba->nutrs; i++) {
+ pos = (1 << i);
+ if ((pos & hba->outstanding_reqs) &&
+ (reset_lun == hba->lrb[i].lun)) {
+
+ /* clear the respective UTRLCLR bit */
+ writel(~pos, (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+
+ hba->outstanding_reqs ^= pos;
+
+ if (hba->lrb[i].cmd) {
+ scsi_dma_unmap(hba->lrb[i].cmd);
+ hba->lrb[i].cmd->result =
+ DID_ABORT << 16;
+ hba->lrb[i].cmd->scsi_done(cmd);
+ hba->lrb[i].cmd = NULL;
+ }
+ }
+ } /* end of for */
+ spin_unlock_irqrestore(host->host_lock, flags);
+ } /*end of if */
+out:
+ return err;
+}
+
+/**
+ * ufshcd_host_reset - Main reset function registered with scsi layer
+ * @cmd: SCSI command pointer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_host_reset(struct scsi_cmnd *cmd)
+{
+ struct ufs_hba *hba = NULL;
+
+ hba = (struct ufs_hba *) &cmd->device->host->hostdata[0];
+
+ if (UFSHCD_STATE_RESET == hba->ufshcd_state)
+ return 0;
+
+ return ufshcd_do_reset(hba) ? -1 : 0;
+}
+
+/**
+ * ufshcd_abort - abort a specific command
+ * @cmd: SCSI command pointer
+ *
+ * Returns 0 on success, non-zero value on failure
+ */
+static int ufshcd_abort(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ unsigned int tag;
+ unsigned int pos;
+ int err;
+
+ host = cmd->device->host;
+ hba = (struct ufs_hba *) host->hostdata;
+ tag = cmd->request->tag;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ pos = (1 << tag);
+
+ /* check if command is still pending */
+ if (!(hba->outstanding_reqs & pos)) {
+ err = -1;
+ spin_unlock_irqrestore(host->host_lock, flags);
+ goto out;
+ }
+
+ err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
+ if (!err) {
+ spin_lock_irqsave(host->host_lock, flags);
+ scsi_dma_unmap(cmd);
+
+ /* clear the respective UTRLCLR bit */
+ writel(~pos,
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+ hba->outstanding_reqs &= ~pos;
+ hba->lrb[tag].cmd = NULL;
+ spin_unlock_irqrestore(host->host_lock, flags);
+ }
+out:
+ return err;
+}
+
static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name = UFSHCD,
@@ -1369,6 +1670,9 @@ static struct scsi_host_template ufshcd_driver_template = {
.queuecommand = ufshcd_queuecommand,
.slave_alloc = ufshcd_slave_alloc,
.slave_destroy = ufshcd_slave_destroy,
+ .eh_abort_handler = ufshcd_abort,
+ .eh_device_reset_handler = ufshcd_device_reset,
+ .eh_host_reset_handler = ufshcd_host_reset,
.this_id = -1,
.sg_tablesize = SG_ALL,
.cmd_per_lun = UFSHCD_CMD_PER_LUN,
@@ -1483,6 +1787,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
struct Scsi_Host *host;
struct ufs_hba *hba;
int ufs_hba_len;
+ int index;
int err;

ufs_hba_len = sizeof(struct ufs_hba);
@@ -1547,6 +1852,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
host->unique_id = host->host_no;
host->max_cmd_len = MAX_CDB_SIZE;

+ /* Initailze wait queue for task management */
+ init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
+
+ /* Initially assign invalid value to tm_condition */
+ for (index = 0; index < hba->nutmrs; index++)
+ hba->tm_condition[index] = 0xFF;
+
/* Initialize work queues */
INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
--
1.7.5.4

2012-02-02 04:59:38

by vinayak holikatti

[permalink] [raw]
Subject: [PATCH 2/4] [SCSI] ufshcd: UFS UTP Transfer requests handling

From: Santosh Yaraganavi <[email protected]>

This patch adds support for Transfer request handling.

ufshcd includes following implementations:
- SCSI queuecommand
- Compose UPIU(UFS Protocol information unit)
- Issue commands to UFS host controller
- Handle completed commands

Signed-off-by: Santosh Yaraganavi <[email protected]>
Signed-off-by: Vinayak Holikatti <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
Reviewed-by: Saugata Das <[email protected]>
Reviewed-by: Vishak G <[email protected]>
Reviewed-by: Girish K S <[email protected]>
---
drivers/scsi/ufs/ufshcd.c | 447 +++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 447 insertions(+), 0 deletions(-)

diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
index c82eeea..23d758b 100644
--- a/drivers/scsi/ufs/ufshcd.c
+++ b/drivers/scsi/ufs/ufshcd.c
@@ -62,6 +62,7 @@
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>

#include "ufs.h"
@@ -81,6 +82,7 @@ enum {
UFSHCD_MAX_CHANNEL = 1,
UFSHCD_MAX_ID = 1,
UFSHCD_MAX_LUNS = 8,
+ UFSHCD_CMD_PER_LUN = 16,
UFSHCD_CAN_QUEUE = 32,
BYTES_128 = 128,
BYTES_1024 = 1024
@@ -146,6 +148,7 @@ struct uic_command {
* @host: Scsi_Host instance of the driver
* @pdev: PCI device handle
* @lrb: local reference block
+ * @outstanding_reqs: Bits representing outstanding transfer requests
* @capabilities: UFS Controller Capabilities
* @nutrs: Transfer Request Queue depth supported by controller
* @nutmrs: Task Management Queue depth supported by controller
@@ -184,6 +187,8 @@ struct ufs_hba {

struct ufshcd_lrb *lrb;

+ u32 outstanding_reqs;
+
u32 capabilities;
int nutrs;
int nutmrs;
@@ -204,12 +209,28 @@ struct ufs_hba {
* @ucd_cmd_ptr: UCD address of the command
* @ucd_rsp_ptr: Response UPIU address for this command
* @ucd_prdt_ptr: PRDT address of the command
+ * @cmd: pointer to scsi command
+ * @sense_buffer: pointer sense buffer address of the scsi command
+ * @sense_bufflen: Length of the sense buffer
+ * @scsi_status: SCSI status of the command
+ * @command_type: SCSI, UFS, Query.
+ * @task_tag: Task tag of the command
+ * @lun: LUN of the command
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
struct utp_upiu_cmd *ucd_cmd_ptr;
struct utp_upiu_rsp *ucd_rsp_ptr;
struct ufshcd_sg_entry *ucd_prdt_ptr;
+
+ struct scsi_cmnd *cmd;
+ u8 *sense_buffer;
+ unsigned int sense_bufflen;
+ int scsi_status;
+
+ int command_type;
+ int task_tag;
+ int lun;
};

/**
@@ -236,6 +257,18 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
}

/**
+ * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
+ * @lrb: pointer to local command reference block
+ *
+ * This function is used to get the OCS field from UTRD
+ * Returns the OCS field in the UTRD
+ */
+static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
+{
+ return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
+}
+
+/**
* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
* @reg: Register value of host controller status
*
@@ -303,6 +336,36 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
}

/**
+ * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function checks the response UPIU for valid transaction type in
+ * response field
+ * Returns 0 on success, non-zero on failure
+ */
+static inline int
+ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+ return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
+ UPIU_TRANSACTION_RESPONSE) ? 0 :
+ (DID_ERROR << 16 |
+ COMMAND_COMPLETE << 8);
+}
+
+/**
+ * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
+ * @ucd_rsp_ptr: pointer to response UPIU
+ *
+ * This function gets the response status and scsi_status from response UPIU
+ * Returns the response result code.
+ */
+static inline int
+ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
+{
+ return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
+}
+
+/**
* ufshcd_config_int_aggr - Configure interrupt aggregation values
* currently there is no use case where we want to configure
* interrupt aggregation dynamically. So to configure interrupt
@@ -342,6 +405,34 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
}

/**
+ * ufshcd_send_command - Send SCSI or device management commands
+ * @hba: per adapter instance
+ * @task_tag: Task tag of the command
+ */
+static inline
+void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
+{
+ hba->outstanding_reqs |= (1 << task_tag);
+ writel((1 << task_tag),
+ (UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_DOOR_BELL));
+}
+
+/**
+ * ufshcd_copy_sense_data - Copy sense data in case of check condition
+ * @lrb - pointer to local reference block
+ */
+static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
+{
+ int len;
+ if (lrbp->sense_buffer != NULL) {
+ len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
+ memcpy(lrbp->sense_buffer,
+ lrbp->ucd_rsp_ptr->sense_data,
+ min_t(int, len, SCSI_SENSE_BUFFERSIZE));
+ }
+}
+
+/**
* ufshcd_hba_capabilities - Read controller capabilities
* @hba: per adapter instance
*/
@@ -385,6 +476,42 @@ ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
}

/**
+ * ufshcd_map_sg - Map scatter-gather list to prdt
+ * @lrbp - pointer to local reference block
+ */
+static void ufshcd_map_sg(struct ufshcd_lrb *lrbp)
+{
+ struct ufshcd_sg_entry *prd_table;
+ struct scatterlist *sg;
+ struct scsi_cmnd *cmd;
+ int sg_segments;
+ int i;
+
+ cmd = lrbp->cmd;
+ sg_segments = scsi_dma_map(cmd);
+
+ BUG_ON(sg_segments < 0);
+
+ if (sg_segments) {
+ lrbp->utr_descriptor_ptr->prd_table_length =
+ cpu_to_le16((u16) (sg_segments));
+
+ prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
+
+ scsi_for_each_sg(cmd, sg, sg_segments, i) {
+ prd_table[i].size =
+ cpu_to_le32(((u32) sg_dma_len(sg))-1);
+ prd_table[i].base_addr =
+ cpu_to_le32(sg->dma_address);
+ prd_table[i].upper_addr =
+ cpu_to_le32((sg->dma_address >> 32));
+ }
+ } else {
+ lrbp->utr_descriptor_ptr->prd_table_length = 0;
+ }
+}
+
+/**
* ufshcd_int_config - enable/disable interrupts
* @hba: per adapter instance
* @option: interrupt option
@@ -410,6 +537,124 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
}

/**
+ * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
+ * @hba: per adapter instance
+ * @lrb - pointer to local reference block
+ */
+static void ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
+{
+ struct utp_transfer_req_desc *req_desc;
+ struct utp_upiu_cmd *ucd_cmd_ptr;
+ u32 data_direction;
+ u32 upiu_flags;
+
+ ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
+ req_desc = lrbp->utr_descriptor_ptr;
+
+ memset(ucd_cmd_ptr, 0, sizeof(struct utp_upiu_cmd));
+
+ switch (lrbp->command_type) {
+ case UTP_CMD_TYPE_SCSI:
+ if (DMA_FROM_DEVICE == lrbp->cmd->sc_data_direction) {
+ data_direction = UTP_DEVICE_TO_HOST;
+ upiu_flags = UPIU_CMD_FLAGS_READ;
+ } else if (DMA_TO_DEVICE == lrbp->cmd->sc_data_direction) {
+ data_direction = UTP_HOST_TO_DEVICE;
+ upiu_flags = UPIU_CMD_FLAGS_WRITE;
+ } else {
+ data_direction = UTP_NO_DATA_TRANSFER;
+ upiu_flags = 0;
+ }
+
+ /*
+ * Transfer request desc header fields
+ * set interrupt bit if interrupt aggregation is disabled
+ */
+ req_desc->header.dword_0 = cpu_to_le32(data_direction |
+ UTP_SCSI_COMMAND);
+
+ /*
+ * assigning invalid value for command status. Controller
+ * updates OCS on command completion, with the command
+ * status
+ */
+ req_desc->header.dword_2 =
+ cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
+
+ /* command descriptor fields */
+ ucd_cmd_ptr->exp_data_transfer_len =
+ cpu_to_be32(lrbp->cmd->transfersize);
+ memcpy(ucd_cmd_ptr->cdb, lrbp->cmd->cmnd,
+ (min_t(unsigned short,
+ lrbp->cmd->cmd_len,
+ (unsigned short)MAX_CDB_SIZE)));
+
+ ucd_cmd_ptr->header.dword_0 =
+ cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
+ upiu_flags,
+ lrbp->lun,
+ lrbp->task_tag));
+ ucd_cmd_ptr->header.dword_1 =
+ cpu_to_be32(
+ UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
+ 0,
+ 0,
+ 0));
+
+ /* set command response to zero */
+ memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
+ case UTP_CMD_TYPE_DEV_MANAGE:
+ /* For query function implementation */
+ break;
+ case UTP_CMD_TYPE_UFS:
+ /* For UFS native command implementation */
+ break;
+ } /* end of switch, command_type */
+}
+
+/**
+ * ufshcd_queuecommand - main entry point for SCSI requests
+ * @cmd: command from SCSI Midlayer
+ * @done: call back function
+ *
+ * Retruns 0 for success, SCSI Midlayer specific error in case of failure
+ */
+static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+ struct ufshcd_lrb *lrbp;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ int tag;
+
+ hba = (struct ufs_hba *) &host->hostdata[0];
+
+ tag = cmd->request->tag;
+
+ if (UFSHCD_STATE_OPERATIONAL != hba->ufshcd_state)
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+ lrbp = &hba->lrb[tag];
+
+ lrbp->cmd = cmd;
+ lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
+ lrbp->sense_buffer = cmd->sense_buffer;
+ lrbp->task_tag = tag;
+ lrbp->lun = cmd->device->lun;
+
+ lrbp->command_type = UTP_CMD_TYPE_SCSI;
+
+ /* form UPIU before issuing the command */
+ ufshcd_compose_upiu(hba, lrbp);
+ ufshcd_map_sg(lrbp);
+
+ /* issue command to the controller */
+ spin_lock_irqsave(host->host_lock, flags);
+ ufshcd_send_command(hba, tag);
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return 0;
+}
+
+/**
* ufshcd_memory_alloc - allocate memory for host memory space data structures
* @hba: per adapter instance
*
@@ -680,6 +925,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);

hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
+ scsi_scan_host(hba->host);

return 0;
}
@@ -763,6 +1009,190 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
}

/**
+ * ufshcd_slave_alloc - handle initial scsi devie configurations
+ * @sdev: pointer to scsi device
+ *
+ * Returns success
+ */
+static int ufshcd_slave_alloc(struct scsi_device *sdev)
+{
+ struct ufs_hba *hba;
+
+ hba = (struct ufs_hba *)sdev->host->hostdata;
+ sdev->tagged_supported = 1;
+ scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
+ scsi_activate_tcq(sdev, hba->nutrs);
+
+ return 0;
+}
+
+/**
+ * ufshcd_slave_destroy - remove scsi device configurations
+ * @sdev: pointer to scsi device
+ */
+static void ufshcd_slave_destroy(struct scsi_device *sdev)
+{
+ struct ufs_hba *hba;
+
+ hba = (struct ufs_hba *)sdev->host->hostdata;
+ scsi_deactivate_tcq(sdev, hba->nutrs);
+}
+
+/**
+ * ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
+ * @lrb: pointer to local reference block of completed command
+ * @scsi_status: SCSI command status
+ *
+ * Returns value base on SCSI command status
+ */
+static inline int
+ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
+{
+ int result = 0;
+
+ switch (scsi_status) {
+ case SAM_STAT_GOOD:
+ result |= DID_OK << 16 |
+ COMMAND_COMPLETE << 8 |
+ SAM_STAT_GOOD;
+ break;
+ case SAM_STAT_CHECK_CONDITION:
+ result |= DRIVER_SENSE << 24 |
+ COMMAND_COMPLETE << 8 |
+ SAM_STAT_CHECK_CONDITION;
+ ufshcd_copy_sense_data(lrbp);
+ break;
+ case SAM_STAT_BUSY:
+ result |= DID_BUS_BUSY << 16 |
+ SAM_STAT_BUSY;
+ break;
+ case SAM_STAT_TASK_SET_FULL:
+ result |= DID_OK << 16 |
+ COMMAND_COMPLETE << 8 |
+ SAM_STAT_TASK_SET_FULL;
+ break;
+ case SAM_STAT_TASK_ABORTED:
+ result |= DID_ABORT << 16 |
+ ABORT_TASK << 8 |
+ SAM_STAT_TASK_ABORTED;
+ default:
+ result |= DID_ERROR << 16;
+ break;
+ } /* end of switch */
+
+ return result;
+}
+
+/**
+ * ufshcd_transfer_rsp_status - Get overall status of the response
+ * @lrb: pointer to local reference block of completed command
+ *
+ * Returns result of the command to notify SCSI midlayer
+ */
+static inline int ufshcd_transfer_rsp_status(struct ufshcd_lrb *lrbp)
+{
+ int result = 0;
+ int scsi_status;
+ int ocs;
+
+ /* overall command status of utrd */
+ ocs = ufshcd_get_tr_ocs(lrbp);
+
+ switch (ocs) {
+ case OCS_SUCCESS:
+
+ /* check if the returned transfer response is valid */
+ result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
+ if (result) {
+ pr_err(UFSHCD ":Invalid Response\n");
+ break;
+ }
+
+ /*
+ * get the response UPIU result to extract
+ * the SCSI command status
+ */
+ result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
+
+ /*
+ * get the result based on SCSI status response
+ * to notify the SCSI midlayer of the command status
+ */
+ scsi_status = result & MASK_SCSI_STATUS;
+ result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
+ break;
+ case OCS_INVALID_CMD_TABLE_ATTR:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case OCS_INVALID_PRDT_ATTR:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case OCS_MISMATCH_DATA_BUF_SIZE:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case OCS_MISMATCH_RESP_UPIU_SIZE:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case OCS_PEER_COMM_FAILURE:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ case OCS_ABORTED:
+ result |= DID_ABORT << 16 | ABORT_TASK << 8;
+ break;
+ case OCS_FATAL_ERROR:
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ break;
+ default:
+ /* should not come here */
+ result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
+ pr_err(UFSHCD ":Invalid OCS from controller\n");
+ break;
+ } /* end of switch */
+
+ return result;
+}
+
+/**
+ * ufshcd_transfer_req_compl - handle SCSI and query command completion
+ * @hba: per adapter instance
+ */
+static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
+{
+ struct ufshcd_lrb *lrb;
+ u32 completed_reqs;
+ u32 tr_doorbell;
+ int result;
+ int index;
+
+ lrb = hba->lrb;
+ tr_doorbell =
+ readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
+ completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
+
+ /* clear completed commands from outstanding_reqs */
+ hba->outstanding_reqs ^= completed_reqs;
+
+ for (index = 0; index < hba->nutrs; index++) {
+ if (completed_reqs & (1 << index)) {
+
+ result = ufshcd_transfer_rsp_status(&lrb[index]);
+
+ if (NULL != lrb[index].cmd) {
+ scsi_dma_unmap(lrb[index].cmd);
+ lrb[index].cmd->result = result;
+ lrb[index].cmd->scsi_done(lrb[index].cmd);
+
+ /* Mark completed command as NULL in LRB */
+ lrb[index].cmd = NULL;
+ }
+ } /* end of if */
+ } /* end of for */
+
+ /* Reset interrupt aggregation counters */
+ ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
+}
+
+/**
* ufshcd_uic_cc_handler - handle UIC command completion
* @work: pointer to a work queue structure
*
@@ -793,6 +1223,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
{
if (intr_status & UIC_COMMAND_COMPL)
schedule_work(&hba->uic_workq);
+
+ if (intr_status & UTP_TRANSFER_REQ_COMPL)
+ ufshcd_transfer_req_compl(hba);
}

/**
@@ -830,7 +1263,13 @@ static struct scsi_host_template ufshcd_driver_template = {
.module = THIS_MODULE,
.name = UFSHCD,
.proc_name = UFSHCD,
+ .queuecommand = ufshcd_queuecommand,
+ .slave_alloc = ufshcd_slave_alloc,
+ .slave_destroy = ufshcd_slave_destroy,
.this_id = -1,
+ .sg_tablesize = SG_ALL,
+ .cmd_per_lun = UFSHCD_CMD_PER_LUN,
+ .can_queue = UFSHCD_CAN_QUEUE
};

/**
@@ -1003,6 +1442,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
host->max_lun = UFSHCD_MAX_LUNS;
host->max_channel = UFSHCD_MAX_CHANNEL;
host->unique_id = host->host_no;
+ host->max_cmd_len = MAX_CDB_SIZE;

/* Initialize work queues */
INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
@@ -1014,6 +1454,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
goto out_lrb_free;
}

+ /* Enable scsi tag mapping */
+ err = scsi_init_shared_tag_map(host, host->can_queue);
+ if (err) {
+ dev_err(&pdev->dev, "init shared queue failed\n");
+ goto out_free_irq;
+ }
+
pci_set_drvdata(pdev, hba);

err = scsi_add_host(host, &pdev->dev);
--
1.7.5.4

2012-02-03 15:19:18

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On Thursday 02 February 2012, Vinayak Holikatti wrote:
> From: Santosh Yaraganavi <[email protected]>
>
> This patch adds support for Universal Flash Storage(UFS)
> host controllers. The UFS host controller driver
> includes host controller initialization method.
>
> The Initialization process involves following steps:
> - Initiate UFS Host Controller initialization process by writing
> to Host controller enable register
> - Configure UFS Host controller registers with host memory space
> datastructure offsets.
> - Unipro link startup procedure
> - Check for connected device
> - Configure UFS host controller to process requests
> - Enable required interrupts
> - Configure interrupt aggregation
>
> Signed-off-by: Santosh Yaraganavi <[email protected]>
> Signed-off-by: Vinayak Holikatti <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Reviewed-by: Saugata Das <[email protected]>
> Reviewed-by: Vishak G <[email protected]>
> Reviewed-by: Girish K S <[email protected]>

Thanks for posting this here. Note that while I did review the patches
in private email, I did not reply with a "Reviewed-by" tag, so you should
not add it yourself. In particular, I had made some additional comments
about the ufshcd_memory_alloc() function that have not been addressed.

Getting the code changed will certainly not be a problem, but please
be careful with assigning those tags in the future.

The only major thing that I see missing is a review from James or
someone else who is familiar with other scsi device drivers. Saugata
and I have (in private) commented on a a number of more general issues
and the comments were addressed before this patch set got sent out.

Unless there are important concerns from the SCSI side, I believe this
is going to be ready to get merged very soon, after the usual nitpicking
is done ;-)

Speaking of nitpicking:

>--- /dev/null
>+++ b/drivers/scsi/ufs/Kconfig
>+
>+#ifndef NULL
>+#define NULL 0
>+#endif /* NULL */

Please remove this #define, NULL is defined in <linux/stddef.h>

>+#define BYTES_TO_DWORDS(p) (p >> 2)
>+#define UFSHCD_MMIO_BASE (hba->mmio_base)

Better remove these macros, too: The are clearly longer than the
expanded text, and less clear.

>+struct ufs_hba {
>+ /* Virtual memory reference */
>+ void *ucdl_virt_addr;
>+ void *utrdl_virt_addr;
>+ void *utmrdl_virt_addr;
>+ void *utrdl_virt_addr_aligned;
>+ void *utmrdl_virt_addr_aligned;
>+ void *ucdl_virt_addr_aligned;
>+
>+ size_t ucdl_size;
>+ size_t utrdl_size;
>+ size_t utmrdl_size;
>+
>+ /* DMA memory reference */
>+ dma_addr_t ucdl_dma_addr;
>+ dma_addr_t utrdl_dma_addr;
>+ dma_addr_t utmrdl_dma_addr;
>+ dma_addr_t utrdl_dma_addr_aligned;
>+ dma_addr_t utmrdl_dma_addr_aligned;
>+ dma_addr_t ucdl_dma_addr_aligned;

You can remove most of these members by simplifying the allocation:

- remove the _aligned variables and use WARN_ON to test that
the allocated buffers are aligned (they always are)
- remove the sizes because they are computed from the number of
descriptors
- if possible, remove the _dma_addr members by referencing them only
in the ufshcd_host_memory_configure() function that can get merged
into ufshcd_memory_alloc()
- while you're here, change the type of the *_virt_addr to
struct utp_task_req_desc/utp_transfer_req_desc/utp_transfer_req_cmd_desc
and remove the _virt_addr postfix.

>+ if (NULL == hba->ucdl_virt_addr) {

Here and in many other places, it's better to use the common kernel style

if (!hba->ucdl_virt_addr) {

to check the validity of an object.

>+static int ufshcd_make_hba_operational(struct ufs_hba *hba)
>+{
>+ u32 reg;
>+
>+ /* check if device present */
>+ reg = readl((UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS));
>+ if (ufshcd_is_device_present(reg)) {
>+ dev_err(&hba->pdev->dev, "cc: Device not present\n");
>+ return -EINVAL;
>+ }
>+
>+ /*
>+ * UCRDY, UTMRLDY and UTRLRDY bits must be 1
>+ * DEI, HEI bits must be 0
>+ */
>+ if (!(ufshcd_get_lists_status(reg))) {
>+ writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
>+ (UFSHCD_MMIO_BASE +
>+ REG_UTP_TASK_REQ_LIST_RUN_STOP));
>+ writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
>+ (UFSHCD_MMIO_BASE +
>+ REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
>+ } else {
>+ dev_err(&hba->pdev->dev,
>+ "Host controller not ready to process requests");
>+ return -EINVAL;
>+ }

I guess ENXIO or EIO would be more fitting here than EINVAL, because you
are not referring to incorrect user input.

>+#ifdef CONFIG_PM
>+/**
>+ * ufshcd_suspend - suspend power management function
>+ * @pdev: pointer to PCI device handle
>+ * @state: power state
>+ *
>+ * Returns -ENOSYS
>+ */
>+static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
>+{
>+ return -ENOSYS;
>+}
>+
>+/**
>+ * ufshcd_resume - resume power management function
>+ * @pdev: pointer to PCI device handle
>+ *
>+ * Returns -ENOSYS
>+ */
>+static int ufshcd_resume(struct pci_dev *pdev)
>+{
>+ return -ENOSYS;
>+}
>+#endif /* CONFIG_PM */

These look wrong. Are you planning to fill them in a later patch? If not,
it's probably better to just remove these functions.

Arnd

2012-02-04 06:58:51

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On Fri, Feb 3, 2012 at 8:49 PM, Arnd Bergmann <[email protected]> wrote:
> On Thursday 02 February 2012, Vinayak Holikatti wrote:
>> From: Santosh Yaraganavi <[email protected]>
>>
>> This patch adds support for Universal Flash Storage(UFS)
>> host controllers. The UFS host controller driver
>> includes host controller initialization method.
>>
>> The Initialization process involves following steps:
>> ?- Initiate UFS Host Controller initialization process by writing
>> ? ?to Host controller enable register
>> ?- Configure UFS Host controller registers with host memory space
>> ? ?datastructure offsets.
>> ?- Unipro link startup procedure
>> ?- Check for connected device
>> ?- Configure UFS host controller to process requests
>> ?- Enable required interrupts
>> ?- Configure interrupt aggregation
>>
>> Signed-off-by: Santosh Yaraganavi <[email protected]>
>> Signed-off-by: Vinayak Holikatti <[email protected]>
>> Reviewed-by: Arnd Bergmann <[email protected]>
>> Reviewed-by: Saugata Das <[email protected]>
>> Reviewed-by: Vishak G <[email protected]>
>> Reviewed-by: Girish K S <[email protected]>
>
> Thanks for posting this here. Note that while I did review the patches
> in private email, I did not reply with a "Reviewed-by" tag, so you should
> not add it yourself.

Sure, we'll keep in mind.

>In particular, I had made some additional comments
> about the ufshcd_memory_alloc() function that have not been addressed.
>
> Getting the code changed will certainly not be a problem, but please
> be careful with assigning those tags in the future.
>
> The only major thing that I see missing is a review from James or
> someone else who is familiar with other scsi device drivers. Saugata
> and I have (in private) commented on a a number of more general issues
> and the comments were addressed before this patch set got sent out.
>
> Unless there are important concerns from the SCSI side, I believe this
> is going to be ready to get merged very soon, after the usual nitpicking
> is done ;-)
>
> Speaking of nitpicking:
>
>>--- /dev/null
>>+++ b/drivers/scsi/ufs/Kconfig
>>+
>>+#ifndef NULL
>>+#define NULL 0
>>+#endif ?/* NULL */
>
> Please remove this #define, NULL is defined in <linux/stddef.h>
>
>>+#define BYTES_TO_DWORDS(p) ? ? (p >> 2)
>>+#define UFSHCD_MMIO_BASE ? ? ? (hba->mmio_base)
>
> Better remove these macros, too: The are clearly longer than the
> expanded text, and less clear.
>

Ok, we'll do so.

>>+struct ufs_hba {
>>+ ? ? ? /* Virtual memory reference */
>>+ ? ? ? void *ucdl_virt_addr;
>>+ ? ? ? void *utrdl_virt_addr;
>>+ ? ? ? void *utmrdl_virt_addr;
>>+ ? ? ? void *utrdl_virt_addr_aligned;
>>+ ? ? ? void *utmrdl_virt_addr_aligned;
>>+ ? ? ? void *ucdl_virt_addr_aligned;
>>+
>>+ ? ? ? size_t ucdl_size;
>>+ ? ? ? size_t utrdl_size;
>>+ ? ? ? size_t utmrdl_size;
>>+
>>+ ? ? ? /* DMA memory reference */
>>+ ? ? ? dma_addr_t ucdl_dma_addr;
>>+ ? ? ? dma_addr_t utrdl_dma_addr;
>>+ ? ? ? dma_addr_t utmrdl_dma_addr;
>>+ ? ? ? dma_addr_t utrdl_dma_addr_aligned;
>>+ ? ? ? dma_addr_t utmrdl_dma_addr_aligned;
>>+ ? ? ? dma_addr_t ucdl_dma_addr_aligned;
>
> You can remove most of these members by simplifying the allocation:
>
> - remove the _aligned variables and use WARN_ON to test that
> ?the allocated buffers are aligned (they always are)
> - remove the sizes because they are computed from the number of
> ?descriptors
> - if possible, remove the _dma_addr members by referencing them only
> ?in the ufshcd_host_memory_configure() function that can get merged
> ?into ufshcd_memory_alloc()
> - while you're here, change the type of the *_virt_addr to
> ?struct utp_task_req_desc/utp_transfer_req_desc/utp_transfer_req_cmd_desc
> ?and remove the _virt_addr postfix.
>

I tried,
if( !ucdl_dma_addr || WARN_ON(ucdl_dma_addr & PAGE_SIZE))
pr_err("Memory allocation failed\n");
but I was getting "memory allocation failed error".

Since we need ucdl 128 byte aligned, utrdl and utmrdl 1kb aligned,
Currently I'm testing with the following code,

if( !ucdl_dma_addr || WARN_ON(ucdl_dma_addr & (128 - 1))
pr_err("Memory allocation failed\n");

if( !utrdl_dma_addr || WARN_ON(utrdl_dma_addr & (1024 - 1))
pr_err("Memory allocation failed\n");

and
if( !utmrdl_dma_addr || WARN_ON(utmrdl_dma_addr & (1024 - 1))
pr_err("Memory allocation failed\n");

also I'll make changes to the other things you pointed out.

>>+ ? ? ? if (NULL == hba->ucdl_virt_addr) {
>
> Here and in many other places, it's better to use the common kernel style
>
> ? ? ? ?if (!hba->ucdl_virt_addr) {
>
> to check the validity of an object.
>

ok, we'll do so.

>>+static int ufshcd_make_hba_operational(struct ufs_hba *hba)
>>+{
>>+ ? ? ? u32 reg;
>>+
>>+ ? ? ? /* check if device present */
>>+ ? ? ? reg = readl((UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS));
>>+ ? ? ? if (ufshcd_is_device_present(reg)) {
>>+ ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "cc: Device not present\n");
>>+ ? ? ? ? ? ? ? return -EINVAL;
>>+ ? ? ? }
>>+
>>+ ? ? ? /*
>>+ ? ? ? ?* UCRDY, UTMRLDY and UTRLRDY bits must be 1
>>+ ? ? ? ?* DEI, HEI bits must be 0
>>+ ? ? ? ?*/
>>+ ? ? ? if (!(ufshcd_get_lists_status(reg))) {
>>+ ? ? ? ? ? ? ? writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
>>+ ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
>>+ ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TASK_REQ_LIST_RUN_STOP));
>>+ ? ? ? ? ? ? ? writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
>>+ ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
>>+ ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
>>+ ? ? ? } else {
>>+ ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>>+ ? ? ? ? ? ? ? ? ? ? ? "Host controller not ready to process requests");
>>+ ? ? ? ? ? ? ? return -EINVAL;
>>+ ? ? ? }
>
> I guess ENXIO or EIO would be more fitting here than EINVAL, because you
> are not referring to incorrect user input.
>

ok, we'll update accordingly.

>>+#ifdef CONFIG_PM
>>+/**
>>+ * ufshcd_suspend - suspend power management function
>>+ * @pdev: pointer to PCI device handle
>>+ * @state: power state
>>+ *
>>+ * Returns -ENOSYS
>>+ */
>>+static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
>>+{
>>+ ? ? ? return -ENOSYS;
>>+}
>>+
>>+/**
>>+ * ufshcd_resume - resume power management function
>>+ * @pdev: pointer to PCI device handle
>>+ *
>>+ * Returns -ENOSYS
>>+ */
>>+static int ufshcd_resume(struct pci_dev *pdev)
>>+{
>>+ ? ? ? return -ENOSYS;
>>+}
>>+#endif /* CONFIG_PM */
>
> These look wrong. Are you planning to fill them in a later patch? If not,
> it's probably better to just remove these functions.
>

Yes, We'll implement power management in the next patch.
linux/Documentation/SubmittingDrivers suggested to define
.suspend and .resume methods returning -ENOSYS, if not yet implemented.
So it was added.

Thanks for your comments. Please let us know if you have any comments
on the other patches as well.

> ? ? ? ?Arnd

--
~Santosh

2012-02-05 07:20:51

by Namjae Jeon

[permalink] [raw]
Subject: Re: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

2012/2/2 Vinayak Holikatti <[email protected]>:
> From: Santosh Yaraganavi <[email protected]>
>
> UFS is designed to be the most advanced specification for
> both embedded and removable flash memory-based storage in mobile devices
> such as smart phones and tablet computers.  The UFS standard represents
> an evolutionary progression of JEDEC standards in this field, and has been
> specifically tailored for mobile applications and computing systems requiring
> high performance and low power consumption.  The initial data throughput for
> UFS will be ~300 megabytes per second (MB/s), and the standard also supports
> command queuing features to raise random read/write speeds.
>
> To achieve the highest performance and most power efficient data
> transport, UFS uses the leading industry interface standards to form its
> Interconnect Layer: MIPI® Alliance’s M-PHY and UniProSM  specifications.
> UniPro is a comprehensive specification meant to act as a universal
> chip-to-chip protocol, providing a common tunnel for other protocols.
> The M-PHY interface is designed as the primary physical interface (PHY layer)
> for the UniPro specification, and is a high speed serial interface targeting
> up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
> per lane.
>
> MIPI’s M-PHY and UniPro specifications are optimized for mobile applications,
> and are designed from the ground up for efficient power management in mobile
> devices, including enabling efficient transitions between the active and power
> save modes. Combined with a low active power level and a near-zero idle power
> level, UFS offers the promise for significant reductions in device power
> consumption.
>
> The UFS standard adopts the well-known SCSI Architecture Model and command
> protocols supporting multiple commands with command queuing features and
> enabling a multi-thread programming paradigm. This differs from conventional
> flash-based memory cards and embedded flash solutions which process one
> command at a time, limiting random read/write access performance.
> In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
> specification will allow system designers greater flexibility by simplifying
> the involvement of the host processor in the operation of the flash storage
> subsystem. The UFS HCI specification and the adoption of SCSI will provide
> a well-known software programming model and enable wider market adoption.
>
> This patchset contains PCIe based UFS host controller driver which complies
> to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
> The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).
>
> This patch set is successfully applied on kernel version 3.3-rc2.
>
> Santosh Yaraganavi (4):
>  [SCSI] ufshcd: UFS Host controller driver
>  [SCSI] ufshcd: UFS UTP Transfer requests handling
>  [SCSI] ufshcd: UFSHCI error handling
>  [SCSI] ufshcd: SCSI error handling
>
>  drivers/scsi/Kconfig      |    1 +
>  drivers/scsi/Makefile     |    1 +
>  drivers/scsi/ufs/Kconfig  |   49 ++
>  drivers/scsi/ufs/Makefile |    2 +
>  drivers/scsi/ufs/ufs.h    |  203 +++++
>  drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
>  drivers/scsi/ufs/ufshci.h |  360 +++++++++
>  7 files changed, 2570 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/scsi/ufs/Kconfig
>  create mode 100644 drivers/scsi/ufs/Makefile
>  create mode 100644 drivers/scsi/ufs/ufs.h
>  create mode 100644 drivers/scsi/ufs/ufshcd.c
>  create mode 100644 drivers/scsi/ufs/ufshci.h
>
> --
> 1.7.5.4
Hi.
I have been waiting for ufs contribution.
Unfortunately I don't have real target supported ufs(maybe only you
have it). I can not debug and run this code, So just review only code
with specification.
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

2012-02-05 07:37:09

by Namjae Jeon

[permalink] [raw]
Subject: Re: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

> +
> +/**
> + * ufshcd_abort - abort a specific command
> + * @cmd: SCSI command pointer
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_abort(struct scsi_cmnd *cmd)
> +{
> +       struct Scsi_Host *host;
> +       struct ufs_hba *hba;
> +       unsigned long flags;
> +       unsigned int tag;
> +       unsigned int pos;
> +       int err;
> +
> +       host = cmd->device->host;
> +       hba = (struct ufs_hba *) host->hostdata;
> +       tag = cmd->request->tag;
> +
> +       spin_lock_irqsave(host->host_lock, flags);
> +       pos = (1 << tag);
> +
> +       /* check if command is still pending */
> +       if (!(hba->outstanding_reqs & pos)) {
> +               err = -1;
> +               spin_unlock_irqrestore(host->host_lock, flags);
> +               goto out;
> +       }
> +
> +       err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
Hi.
You called spin_lock_irqsave in ufshcd_issue_tm_cmd() without
spin_unlock_irqrestore.

> +       if (!err) {
> +               spin_lock_irqsave(host->host_lock, flags);
This case is same also.
Thanks.
> +               scsi_dma_unmap(cmd);
> +
> +               /* clear the respective UTRLCLR bit */
> +               writel(~pos,
> +                       (UFSHCD_MMIO_BASE +
> +                        REG_UTP_TRANSFER_REQ_LIST_CLEAR));
> +               hba->outstanding_reqs &= ~pos;
> +               hba->lrb[tag].cmd = NULL;
> +               spin_unlock_irqrestore(host->host_lock, flags);
> +       }
> +out:
> +       return err;
> +}
> +

2012-02-05 09:18:11

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

On Sun, Feb 5, 2012 at 1:07 PM, Namjae Jeon <[email protected]> wrote:
>> +
>> +/**
>> + * ufshcd_abort - abort a specific command
>> + * @cmd: SCSI command pointer
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_abort(struct scsi_cmnd *cmd)
>> +{
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? struct ufs_hba *hba;
>> + ? ? ? unsigned long flags;
>> + ? ? ? unsigned int tag;
>> + ? ? ? unsigned int pos;
>> + ? ? ? int err;
>> +
>> + ? ? ? host = cmd->device->host;
>> + ? ? ? hba = (struct ufs_hba *) host->hostdata;
>> + ? ? ? tag = cmd->request->tag;
>> +
>> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> + ? ? ? pos = (1 << tag);
>> +
>> + ? ? ? /* check if command is still pending */
>> + ? ? ? if (!(hba->outstanding_reqs & pos)) {
>> + ? ? ? ? ? ? ? err = -1;
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? ? ? ? ? goto out;
>> + ? ? ? }
>> +
>> + ? ? ? err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
> Hi.
> You called spin_lock_irqsave in ufshcd_issue_tm_cmd() without
> spin_unlock_irqrestore.

Thanks for reviewing the code. We'll correct it.
Please let us know your comments on the other patches too.

>
>> + ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? spin_lock_irqsave(host->host_lock, flags);
> This case is same also.
> Thanks.
>> + ? ? ? ? ? ? ? scsi_dma_unmap(cmd);
>> +
>> + ? ? ? ? ? ? ? /* clear the respective UTRLCLR bit */
>> + ? ? ? ? ? ? ? writel(~pos,
>> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_LIST_CLEAR));
>> + ? ? ? ? ? ? ? hba->outstanding_reqs &= ~pos;
>> + ? ? ? ? ? ? ? hba->lrb[tag].cmd = NULL;
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? }
>> +out:
>> + ? ? ? return err;
>> +}
>> +

--
~Santosh

2012-02-05 12:51:29

by Namjae Jeon

[permalink] [raw]
Subject: Re: [PATCH 2/4] [SCSI] ufshcd: UFS UTP Transfer requests handling

2012/2/2 Vinayak Holikatti <[email protected]>:
> From: Santosh Yaraganavi <[email protected]>
>
> This patch adds support for Transfer request handling.
>
> ufshcd includes following implementations:
>  - SCSI queuecommand
>  - Compose UPIU(UFS Protocol information unit)
>  - Issue commands to UFS host controller
>  - Handle completed commands
>
> Signed-off-by: Santosh Yaraganavi <[email protected]>
> Signed-off-by: Vinayak Holikatti <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Reviewed-by: Saugata Das <[email protected]>
> Reviewed-by: Vishak G <[email protected]>
> Reviewed-by: Girish K S <[email protected]>
> ---
>  drivers/scsi/ufs/ufshcd.c |  447 +++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 447 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index c82eeea..23d758b 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -62,6 +62,7 @@
>  #include <scsi/scsi.h>
>  #include <scsi/scsi_cmnd.h>
>  #include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
>  #include <scsi/scsi_dbg.h>
>
>  #include "ufs.h"
> @@ -81,6 +82,7 @@ enum {
>        UFSHCD_MAX_CHANNEL      = 1,
>        UFSHCD_MAX_ID           = 1,
>        UFSHCD_MAX_LUNS         = 8,
> +       UFSHCD_CMD_PER_LUN      = 16,
>        UFSHCD_CAN_QUEUE        = 32,
>        BYTES_128               = 128,
>        BYTES_1024              = 1024
> @@ -146,6 +148,7 @@ struct uic_command {
>  * @host: Scsi_Host instance of the driver
>  * @pdev: PCI device handle
>  * @lrb: local reference block
> + * @outstanding_reqs: Bits representing outstanding transfer requests
>  * @capabilities: UFS Controller Capabilities
>  * @nutrs: Transfer Request Queue depth supported by controller
>  * @nutmrs: Task Management Queue depth supported by controller
> @@ -184,6 +187,8 @@ struct ufs_hba {
>
>        struct ufshcd_lrb *lrb;
>
> +       u32 outstanding_reqs;
> +
>        u32 capabilities;
>        int nutrs;
>        int nutmrs;
> @@ -204,12 +209,28 @@ struct ufs_hba {
>  * @ucd_cmd_ptr: UCD address of the command
>  * @ucd_rsp_ptr: Response UPIU address for this command
>  * @ucd_prdt_ptr: PRDT address of the command
> + * @cmd: pointer to scsi command
> + * @sense_buffer: pointer sense buffer address of the scsi command
> + * @sense_bufflen: Length of the sense buffer
> + * @scsi_status: SCSI status of the command
> + * @command_type: SCSI, UFS, Query.
> + * @task_tag: Task tag of the command
> + * @lun: LUN of the command
>  */
>  struct ufshcd_lrb {
>        struct utp_transfer_req_desc *utr_descriptor_ptr;
>        struct utp_upiu_cmd *ucd_cmd_ptr;
>        struct utp_upiu_rsp *ucd_rsp_ptr;
>        struct ufshcd_sg_entry *ucd_prdt_ptr;
> +
> +       struct scsi_cmnd *cmd;
> +       u8 *sense_buffer;
> +       unsigned int sense_bufflen;
> +       int scsi_status;
> +
> +       int command_type;
> +       int task_tag;
> +       int lun;
>  };
>
>  /**
> @@ -236,6 +257,18 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
>  }
>
>  /**
> + * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
> + * @lrb: pointer to local command reference block
> + *
> + * This function is used to get the OCS field from UTRD
> + * Returns the OCS field in the UTRD
> + */
> +static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
> +{
> +       return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
> +}
> +
> +/**
>  * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
>  * @reg: Register value of host controller status
>  *
> @@ -303,6 +336,36 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
>  }
>
>  /**
> + * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
> + * @ucd_rsp_ptr: pointer to response UPIU
> + *
> + * This function checks the response UPIU for valid transaction type in
> + * response field
> + * Returns 0 on success, non-zero on failure
> + */
> +static inline int
> +ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
> +{
> +       return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
> +                UPIU_TRANSACTION_RESPONSE) ? 0 :
> +                                             (DID_ERROR << 16 |
> +                                              COMMAND_COMPLETE << 8);
> +}
> +
> +/**
> + * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
> + * @ucd_rsp_ptr: pointer to response UPIU
> + *
> + * This function gets the response status and scsi_status from response UPIU
> + * Returns the response result code.
> + */
> +static inline int
> +ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
> +{
> +       return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
> +}
> +
> +/**
>  * ufshcd_config_int_aggr - Configure interrupt aggregation values
>  *             currently there is no use case where we want to configure
>  *             interrupt aggregation dynamically. So to configure interrupt
> @@ -342,6 +405,34 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
>  }
>
>  /**
> + * ufshcd_send_command - Send SCSI or device management commands
> + * @hba: per adapter instance
> + * @task_tag: Task tag of the command
> + */
> +static inline
> +void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
> +{
> +       hba->outstanding_reqs |= (1 << task_tag);
> +       writel((1 << task_tag),
> +              (UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_DOOR_BELL));
> +}
> +
> +/**
> + * ufshcd_copy_sense_data - Copy sense data in case of check condition
> + * @lrb - pointer to local reference block
> + */
> +static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
> +{
> +       int len;
> +       if (lrbp->sense_buffer != NULL) {
> +               len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
> +               memcpy(lrbp->sense_buffer,
> +                       lrbp->ucd_rsp_ptr->sense_data,
> +                       min_t(int, len, SCSI_SENSE_BUFFERSIZE));
> +       }
> +}
> +
> +/**
>  * ufshcd_hba_capabilities - Read controller capabilities
>  * @hba: per adapter instance
>  */
> @@ -385,6 +476,42 @@ ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
>  }
>
>  /**
> + * ufshcd_map_sg - Map scatter-gather list to prdt
> + * @lrbp - pointer to local reference block
> + */
> +static void ufshcd_map_sg(struct ufshcd_lrb *lrbp)
> +{
> +       struct ufshcd_sg_entry *prd_table;
> +       struct scatterlist *sg;
> +       struct scsi_cmnd *cmd;
> +       int sg_segments;
> +       int i;
> +
> +       cmd = lrbp->cmd;
> +       sg_segments = scsi_dma_map(cmd);
> +
> +       BUG_ON(sg_segments < 0);
> +
> +       if (sg_segments) {
> +               lrbp->utr_descriptor_ptr->prd_table_length =
> +                                       cpu_to_le16((u16) (sg_segments));
> +
> +               prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
> +
> +               scsi_for_each_sg(cmd, sg, sg_segments, i) {
> +                       prd_table[i].size  =
> +                               cpu_to_le32(((u32) sg_dma_len(sg))-1);
> +                       prd_table[i].base_addr =
> +                               cpu_to_le32(sg->dma_address);
> +                       prd_table[i].upper_addr =
> +                               cpu_to_le32((sg->dma_address >> 32));
> +               }
> +       } else {
> +               lrbp->utr_descriptor_ptr->prd_table_length = 0;
> +       }
> +}
> +
> +/**
>  * ufshcd_int_config - enable/disable interrupts
>  * @hba: per adapter instance
>  * @option: interrupt option
> @@ -410,6 +537,124 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
>  }
>
>  /**
> + * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
> + * @hba: per adapter instance
> + * @lrb - pointer to local reference block
> + */
> +static void ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
> +{
> +       struct utp_transfer_req_desc *req_desc;
> +       struct utp_upiu_cmd *ucd_cmd_ptr;
> +       u32 data_direction;
> +       u32 upiu_flags;
> +
> +       ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
> +       req_desc = lrbp->utr_descriptor_ptr;
> +
> +       memset(ucd_cmd_ptr, 0, sizeof(struct utp_upiu_cmd));
> +
> +       switch (lrbp->command_type) {
> +       case UTP_CMD_TYPE_SCSI:
> +               if (DMA_FROM_DEVICE == lrbp->cmd->sc_data_direction) {
> +                       data_direction = UTP_DEVICE_TO_HOST;
> +                       upiu_flags = UPIU_CMD_FLAGS_READ;
> +               } else if (DMA_TO_DEVICE == lrbp->cmd->sc_data_direction) {
> +                       data_direction = UTP_HOST_TO_DEVICE;
> +                       upiu_flags = UPIU_CMD_FLAGS_WRITE;
> +               } else {
> +                       data_direction = UTP_NO_DATA_TRANSFER;
> +                       upiu_flags = 0;
I think that we can add some enum value for like UTP_NO_DATA_TRANSFER.
> +               }
> +
> +               /*
> +                * Transfer request desc header fields
> +                * set interrupt bit if interrupt aggregation is disabled
> +                */
> +               req_desc->header.dword_0 = cpu_to_le32(data_direction |
> +                                                      UTP_SCSI_COMMAND);
> +
> +               /*
> +                * assigning invalid value for command status. Controller
> +                * updates OCS on command completion, with the command
> +                * status
> +                */
> +               req_desc->header.dword_2 =
> +                       cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
> +
> +               /* command descriptor fields */
> +               ucd_cmd_ptr->exp_data_transfer_len =
> +                       cpu_to_be32(lrbp->cmd->transfersize);
> +               memcpy(ucd_cmd_ptr->cdb, lrbp->cmd->cmnd,
> +                     (min_t(unsigned short,
> +                            lrbp->cmd->cmd_len,
> +                            (unsigned short)MAX_CDB_SIZE)));
Is there any reason type casting for MAX_CDB_SIZE ?

> +
> +               ucd_cmd_ptr->header.dword_0 =
> +                       cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
> +                                                     upiu_flags,
> +                                                     lrbp->lun,
> +                                                     lrbp->task_tag));
> +               ucd_cmd_ptr->header.dword_1 =
> +                       cpu_to_be32(
> +                               UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
> +                                                 0,
> +                                                 0,
> +                                                 0));
> +
> +               /* set command response to zero */
> +               memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
Why did you set ucd_rsp_ptr to zero afte adding value ?
And plz add break here.
> +       case UTP_CMD_TYPE_DEV_MANAGE:
> +               /* For query function implementation */
> +               break;
> +       case UTP_CMD_TYPE_UFS:
> +               /* For UFS native command implementation */
> +               break;
> +       } /* end of switch, command_type */
> +}
> +
> +/**
> + * ufshcd_queuecommand - main entry point for SCSI requests
> + * @cmd: command from SCSI Midlayer
> + * @done: call back function
> + *
> + * Retruns 0 for success, SCSI Midlayer specific error in case of failure
> + */
> +static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
> +{
> +       struct ufshcd_lrb *lrbp;
> +       struct ufs_hba *hba;
> +       unsigned long flags;
> +       int tag;
> +
> +       hba = (struct ufs_hba *) &host->hostdata[0];
> +
> +       tag = cmd->request->tag;
> +
> +       if (UFSHCD_STATE_OPERATIONAL != hba->ufshcd_state)
> +               return SCSI_MLQUEUE_HOST_BUSY;
> +
> +       lrbp = &hba->lrb[tag];
> +
> +       lrbp->cmd = cmd;
> +       lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
> +       lrbp->sense_buffer = cmd->sense_buffer;
> +       lrbp->task_tag = tag;
> +       lrbp->lun = cmd->device->lun;
> +
> +       lrbp->command_type = UTP_CMD_TYPE_SCSI;
> +
> +       /* form UPIU before issuing the command */
> +       ufshcd_compose_upiu(hba, lrbp);
> +       ufshcd_map_sg(lrbp);
> +
> +       /* issue command to the controller */
> +       spin_lock_irqsave(host->host_lock, flags);
> +       ufshcd_send_command(hba, tag);
> +       spin_unlock_irqrestore(host->host_lock, flags);
> +       return 0;
> +}
> +
> +/**
>  * ufshcd_memory_alloc - allocate memory for host memory space data structures
>  * @hba: per adapter instance
>  *
> @@ -680,6 +925,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
>        ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
>
>        hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
> +       scsi_scan_host(hba->host);
>
>        return 0;
>  }
> @@ -763,6 +1009,190 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
>  }
>
>  /**
> + * ufshcd_slave_alloc - handle initial scsi devie configurations
> + * @sdev: pointer to scsi device
> + *
> + * Returns success
> + */
> +static int ufshcd_slave_alloc(struct scsi_device *sdev)
> +{
> +       struct ufs_hba *hba;
> +
> +       hba = (struct ufs_hba *)sdev->host->hostdata;
> +       sdev->tagged_supported = 1;
> +       scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
> +       scsi_activate_tcq(sdev, hba->nutrs);
> +
> +       return 0;
> +}
> +
> +/**
> + * ufshcd_slave_destroy - remove scsi device configurations
> + * @sdev: pointer to scsi device
> + */
> +static void ufshcd_slave_destroy(struct scsi_device *sdev)
> +{
> +       struct ufs_hba *hba;
> +
> +       hba = (struct ufs_hba *)sdev->host->hostdata;
> +       scsi_deactivate_tcq(sdev, hba->nutrs);
> +}
> +
> +/**
> + * ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
> + * @lrb: pointer to local reference block of completed command
> + * @scsi_status: SCSI command status
> + *
> + * Returns value base on SCSI command status
> + */
> +static inline int
> +ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
> +{
> +       int result = 0;
> +
> +       switch (scsi_status) {
> +       case SAM_STAT_GOOD:
> +               result |= DID_OK << 16 |
> +                         COMMAND_COMPLETE << 8 |
> +                         SAM_STAT_GOOD;
> +               break;
> +       case SAM_STAT_CHECK_CONDITION:
> +               result |= DRIVER_SENSE << 24 |
> +                         COMMAND_COMPLETE << 8 |
> +                         SAM_STAT_CHECK_CONDITION;
> +               ufshcd_copy_sense_data(lrbp);
> +               break;
> +       case SAM_STAT_BUSY:
> +               result |= DID_BUS_BUSY << 16 |
> +                         SAM_STAT_BUSY;
> +               break;
> +       case SAM_STAT_TASK_SET_FULL:
> +               result |= DID_OK << 16 |
> +                         COMMAND_COMPLETE << 8 |
> +                         SAM_STAT_TASK_SET_FULL;
> +               break;
> +       case SAM_STAT_TASK_ABORTED:
> +               result |=  DID_ABORT << 16 |
> +                          ABORT_TASK << 8 |
> +                          SAM_STAT_TASK_ABORTED;
plz add break here also.
> +       default:
> +               result |= DID_ERROR << 16;
> +               break;
> +       } /* end of switch */
> +
> +       return result;
> +}
> +
> +/**
> + * ufshcd_transfer_rsp_status - Get overall status of the response
> + * @lrb: pointer to local reference block of completed command
> + *
> + * Returns result of the command to notify SCSI midlayer
> + */
> +static inline int ufshcd_transfer_rsp_status(struct ufshcd_lrb *lrbp)
> +{
> +       int result = 0;
> +       int scsi_status;
> +       int ocs;
> +
> +       /* overall command status of utrd */
> +       ocs = ufshcd_get_tr_ocs(lrbp);
> +
> +       switch (ocs) {
> +       case OCS_SUCCESS:
> +
> +               /* check if the returned transfer response is valid */
> +               result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
> +               if (result) {
> +                       pr_err(UFSHCD ":Invalid Response\n");
> +                       break;
> +               }
> +
> +               /*
> +                * get the response UPIU result to extract
> +                * the SCSI command status
> +                */
> +               result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
> +
> +               /*
> +                * get the result based on SCSI status response
> +                * to notify the SCSI midlayer of the command status
> +                */
> +               scsi_status = result & MASK_SCSI_STATUS;
> +               result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
> +               break;
> +       case OCS_INVALID_CMD_TABLE_ATTR:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       case OCS_INVALID_PRDT_ATTR:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       case OCS_MISMATCH_DATA_BUF_SIZE:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       case OCS_MISMATCH_RESP_UPIU_SIZE:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       case OCS_PEER_COMM_FAILURE:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       case OCS_ABORTED:
> +               result |= DID_ABORT << 16 | ABORT_TASK << 8;
> +               break;
> +       case OCS_FATAL_ERROR:
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               break;
> +       default:
> +               /* should not come here */
> +               result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> +               pr_err(UFSHCD ":Invalid OCS from controller\n");
> +               break;
> +       } /* end of switch */
> +
> +       return result;
> +}
> +
> +/**
> + * ufshcd_transfer_req_compl - handle SCSI and query command completion
> + * @hba: per adapter instance
> + */
> +static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
> +{
> +       struct ufshcd_lrb *lrb;
> +       u32 completed_reqs;
> +       u32 tr_doorbell;
> +       int result;
> +       int index;
> +
> +       lrb = hba->lrb;
> +       tr_doorbell =
> +               readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
> +       completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
> +
> +       /* clear completed commands from outstanding_reqs */
> +       hba->outstanding_reqs ^= completed_reqs;
> +
> +       for (index = 0; index < hba->nutrs; index++) {
> +               if (completed_reqs & (1 << index)) {
> +
> +                       result = ufshcd_transfer_rsp_status(&lrb[index]);
> +
> +                       if (NULL != lrb[index].cmd) {
> +                               scsi_dma_unmap(lrb[index].cmd);
> +                               lrb[index].cmd->result = result;
> +                               lrb[index].cmd->scsi_done(lrb[index].cmd);
> +
> +                               /* Mark completed command as NULL in LRB */
> +                               lrb[index].cmd = NULL;
> +                       }
> +               } /* end of if */
> +       } /* end of for */
> +
> +       /* Reset interrupt aggregation counters */
> +       ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
> +}
> +
> +/**
>  * ufshcd_uic_cc_handler - handle UIC command completion
>  * @work: pointer to a work queue structure
>  *
> @@ -793,6 +1223,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>  {
>        if (intr_status & UIC_COMMAND_COMPL)
>                schedule_work(&hba->uic_workq);
> +
> +       if (intr_status & UTP_TRANSFER_REQ_COMPL)
> +               ufshcd_transfer_req_compl(hba);
>  }
>
>  /**
> @@ -830,7 +1263,13 @@ static struct scsi_host_template ufshcd_driver_template = {
>        .module                 = THIS_MODULE,
>        .name                   = UFSHCD,
>        .proc_name              = UFSHCD,
> +       .queuecommand           = ufshcd_queuecommand,
> +       .slave_alloc            = ufshcd_slave_alloc,
> +       .slave_destroy          = ufshcd_slave_destroy,
>        .this_id                = -1,
> +       .sg_tablesize           = SG_ALL,
> +       .cmd_per_lun            = UFSHCD_CMD_PER_LUN,
> +       .can_queue              = UFSHCD_CAN_QUEUE
>  };
>
>  /**
> @@ -1003,6 +1442,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>        host->max_lun = UFSHCD_MAX_LUNS;
>        host->max_channel = UFSHCD_MAX_CHANNEL;
>        host->unique_id = host->host_no;
> +       host->max_cmd_len = MAX_CDB_SIZE;
>
>        /* Initialize work queues */
>        INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
> @@ -1014,6 +1454,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>                goto out_lrb_free;
>        }
>
> +       /* Enable scsi tag mapping */
> +       err = scsi_init_shared_tag_map(host, host->can_queue);
> +       if (err) {
> +               dev_err(&pdev->dev, "init shared queue failed\n");
> +               goto out_free_irq;
> +       }
> +
>        pci_set_drvdata(pdev, hba);
>
>        err = scsi_add_host(host, &pdev->dev);
> --
> 1.7.5.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
> the body of a message to [email protected]
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

2012-02-05 14:36:28

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 2/4] [SCSI] ufshcd: UFS UTP Transfer requests handling

On Sun, Feb 5, 2012 at 6:21 PM, Namjae Jeon <[email protected]> wrote:
> 2012/2/2 Vinayak Holikatti <[email protected]>:
>> From: Santosh Yaraganavi <[email protected]>
>>
>> This patch adds support for Transfer request handling.
>>
>> ufshcd includes following implementations:
>> ?- SCSI queuecommand
>> ?- Compose UPIU(UFS Protocol information unit)
>> ?- Issue commands to UFS host controller
>> ?- Handle completed commands
>>
>> Signed-off-by: Santosh Yaraganavi <[email protected]>
>> Signed-off-by: Vinayak Holikatti <[email protected]>
>> Reviewed-by: Arnd Bergmann <[email protected]>
>> Reviewed-by: Saugata Das <[email protected]>
>> Reviewed-by: Vishak G <[email protected]>
>> Reviewed-by: Girish K S <[email protected]>
>> ---
>> ?drivers/scsi/ufs/ufshcd.c | ?447 +++++++++++++++++++++++++++++++++++++++++++++
>> ?1 files changed, 447 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>> index c82eeea..23d758b 100644
>> --- a/drivers/scsi/ufs/ufshcd.c
>> +++ b/drivers/scsi/ufs/ufshcd.c
>> @@ -62,6 +62,7 @@
>> ?#include <scsi/scsi.h>
>> ?#include <scsi/scsi_cmnd.h>
>> ?#include <scsi/scsi_host.h>
>> +#include <scsi/scsi_tcq.h>
>> ?#include <scsi/scsi_dbg.h>
>>
>> ?#include "ufs.h"
>> @@ -81,6 +82,7 @@ enum {
>> ? ? ? ?UFSHCD_MAX_CHANNEL ? ? ?= 1,
>> ? ? ? ?UFSHCD_MAX_ID ? ? ? ? ? = 1,
>> ? ? ? ?UFSHCD_MAX_LUNS ? ? ? ? = 8,
>> + ? ? ? UFSHCD_CMD_PER_LUN ? ? ?= 16,
>> ? ? ? ?UFSHCD_CAN_QUEUE ? ? ? ?= 32,
>> ? ? ? ?BYTES_128 ? ? ? ? ? ? ? = 128,
>> ? ? ? ?BYTES_1024 ? ? ? ? ? ? ?= 1024
>> @@ -146,6 +148,7 @@ struct uic_command {
>> ?* @host: Scsi_Host instance of the driver
>> ?* @pdev: PCI device handle
>> ?* @lrb: local reference block
>> + * @outstanding_reqs: Bits representing outstanding transfer requests
>> ?* @capabilities: UFS Controller Capabilities
>> ?* @nutrs: Transfer Request Queue depth supported by controller
>> ?* @nutmrs: Task Management Queue depth supported by controller
>> @@ -184,6 +187,8 @@ struct ufs_hba {
>>
>> ? ? ? ?struct ufshcd_lrb *lrb;
>>
>> + ? ? ? u32 outstanding_reqs;
>> +
>> ? ? ? ?u32 capabilities;
>> ? ? ? ?int nutrs;
>> ? ? ? ?int nutmrs;
>> @@ -204,12 +209,28 @@ struct ufs_hba {
>> ?* @ucd_cmd_ptr: UCD address of the command
>> ?* @ucd_rsp_ptr: Response UPIU address for this command
>> ?* @ucd_prdt_ptr: PRDT address of the command
>> + * @cmd: pointer to scsi command
>> + * @sense_buffer: pointer sense buffer address of the scsi command
>> + * @sense_bufflen: Length of the sense buffer
>> + * @scsi_status: SCSI status of the command
>> + * @command_type: SCSI, UFS, Query.
>> + * @task_tag: Task tag of the command
>> + * @lun: LUN of the command
>> ?*/
>> ?struct ufshcd_lrb {
>> ? ? ? ?struct utp_transfer_req_desc *utr_descriptor_ptr;
>> ? ? ? ?struct utp_upiu_cmd *ucd_cmd_ptr;
>> ? ? ? ?struct utp_upiu_rsp *ucd_rsp_ptr;
>> ? ? ? ?struct ufshcd_sg_entry *ucd_prdt_ptr;
>> +
>> + ? ? ? struct scsi_cmnd *cmd;
>> + ? ? ? u8 *sense_buffer;
>> + ? ? ? unsigned int sense_bufflen;
>> + ? ? ? int scsi_status;
>> +
>> + ? ? ? int command_type;
>> + ? ? ? int task_tag;
>> + ? ? ? int lun;
>> ?};
>>
>> ?/**
>> @@ -236,6 +257,18 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
>> ?}
>>
>> ?/**
>> + * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
>> + * @lrb: pointer to local command reference block
>> + *
>> + * This function is used to get the OCS field from UTRD
>> + * Returns the OCS field in the UTRD
>> + */
>> +static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
>> +{
>> + ? ? ? return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
>> +}
>> +
>> +/**
>> ?* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
>> ?* @reg: Register value of host controller status
>> ?*
>> @@ -303,6 +336,36 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
>> ?}
>>
>> ?/**
>> + * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
>> + * @ucd_rsp_ptr: pointer to response UPIU
>> + *
>> + * This function checks the response UPIU for valid transaction type in
>> + * response field
>> + * Returns 0 on success, non-zero on failure
>> + */
>> +static inline int
>> +ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
>> +{
>> + ? ? ? return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
>> + ? ? ? ? ? ? ? ?UPIU_TRANSACTION_RESPONSE) ? 0 :
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (DID_ERROR << 16 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?COMMAND_COMPLETE << 8);
>> +}
>> +
>> +/**
>> + * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
>> + * @ucd_rsp_ptr: pointer to response UPIU
>> + *
>> + * This function gets the response status and scsi_status from response UPIU
>> + * Returns the response result code.
>> + */
>> +static inline int
>> +ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
>> +{
>> + ? ? ? return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
>> +}
>> +
>> +/**
>> ?* ufshcd_config_int_aggr - Configure interrupt aggregation values
>> ?* ? ? ? ? ? ? currently there is no use case where we want to configure
>> ?* ? ? ? ? ? ? interrupt aggregation dynamically. So to configure interrupt
>> @@ -342,6 +405,34 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
>> ?}
>>
>> ?/**
>> + * ufshcd_send_command - Send SCSI or device management commands
>> + * @hba: per adapter instance
>> + * @task_tag: Task tag of the command
>> + */
>> +static inline
>> +void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
>> +{
>> + ? ? ? hba->outstanding_reqs |= (1 << task_tag);
>> + ? ? ? writel((1 << task_tag),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_DOOR_BELL));
>> +}
>> +
>> +/**
>> + * ufshcd_copy_sense_data - Copy sense data in case of check condition
>> + * @lrb - pointer to local reference block
>> + */
>> +static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
>> +{
>> + ? ? ? int len;
>> + ? ? ? if (lrbp->sense_buffer != NULL) {
>> + ? ? ? ? ? ? ? len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
>> + ? ? ? ? ? ? ? memcpy(lrbp->sense_buffer,
>> + ? ? ? ? ? ? ? ? ? ? ? lrbp->ucd_rsp_ptr->sense_data,
>> + ? ? ? ? ? ? ? ? ? ? ? min_t(int, len, SCSI_SENSE_BUFFERSIZE));
>> + ? ? ? }
>> +}
>> +
>> +/**
>> ?* ufshcd_hba_capabilities - Read controller capabilities
>> ?* @hba: per adapter instance
>> ?*/
>> @@ -385,6 +476,42 @@ ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
>> ?}
>>
>> ?/**
>> + * ufshcd_map_sg - Map scatter-gather list to prdt
>> + * @lrbp - pointer to local reference block
>> + */
>> +static void ufshcd_map_sg(struct ufshcd_lrb *lrbp)
>> +{
>> + ? ? ? struct ufshcd_sg_entry *prd_table;
>> + ? ? ? struct scatterlist *sg;
>> + ? ? ? struct scsi_cmnd *cmd;
>> + ? ? ? int sg_segments;
>> + ? ? ? int i;
>> +
>> + ? ? ? cmd = lrbp->cmd;
>> + ? ? ? sg_segments = scsi_dma_map(cmd);
>> +
>> + ? ? ? BUG_ON(sg_segments < 0);
>> +
>> + ? ? ? if (sg_segments) {
>> + ? ? ? ? ? ? ? lrbp->utr_descriptor_ptr->prd_table_length =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16((u16) (sg_segments));
>> +
>> + ? ? ? ? ? ? ? prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
>> +
>> + ? ? ? ? ? ? ? scsi_for_each_sg(cmd, sg, sg_segments, i) {
>> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].size ?=
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(((u32) sg_dma_len(sg))-1);
>> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].base_addr =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(sg->dma_address);
>> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].upper_addr =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32((sg->dma_address >> 32));
>> + ? ? ? ? ? ? ? }
>> + ? ? ? } else {
>> + ? ? ? ? ? ? ? lrbp->utr_descriptor_ptr->prd_table_length = 0;
>> + ? ? ? }
>> +}
>> +
>> +/**
>> ?* ufshcd_int_config - enable/disable interrupts
>> ?* @hba: per adapter instance
>> ?* @option: interrupt option
>> @@ -410,6 +537,124 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
>> ?}
>>
>> ?/**
>> + * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
>> + * @hba: per adapter instance
>> + * @lrb - pointer to local reference block
>> + */
>> +static void ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
>> +{
>> + ? ? ? struct utp_transfer_req_desc *req_desc;
>> + ? ? ? struct utp_upiu_cmd *ucd_cmd_ptr;
>> + ? ? ? u32 data_direction;
>> + ? ? ? u32 upiu_flags;
>> +
>> + ? ? ? ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
>> + ? ? ? req_desc = lrbp->utr_descriptor_ptr;
>> +
>> + ? ? ? memset(ucd_cmd_ptr, 0, sizeof(struct utp_upiu_cmd));
>> +
>> + ? ? ? switch (lrbp->command_type) {
>> + ? ? ? case UTP_CMD_TYPE_SCSI:
>> + ? ? ? ? ? ? ? if (DMA_FROM_DEVICE == lrbp->cmd->sc_data_direction) {
>> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_DEVICE_TO_HOST;
>> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = UPIU_CMD_FLAGS_READ;
>> + ? ? ? ? ? ? ? } else if (DMA_TO_DEVICE == lrbp->cmd->sc_data_direction) {
>> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_HOST_TO_DEVICE;
>> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = UPIU_CMD_FLAGS_WRITE;
>> + ? ? ? ? ? ? ? } else {
>> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_NO_DATA_TRANSFER;
>> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = 0;
> I think that we can add some enum value for like UTP_NO_DATA_TRANSFER.

We'll add a comment.

>> + ? ? ? ? ? ? ? }
>> +
>> + ? ? ? ? ? ? ? /*
>> + ? ? ? ? ? ? ? ?* Transfer request desc header fields
>> + ? ? ? ? ? ? ? ?* set interrupt bit if interrupt aggregation is disabled
>> + ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? req_desc->header.dword_0 = cpu_to_le32(data_direction |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UTP_SCSI_COMMAND);
>> +
>> + ? ? ? ? ? ? ? /*
>> + ? ? ? ? ? ? ? ?* assigning invalid value for command status. Controller
>> + ? ? ? ? ? ? ? ?* updates OCS on command completion, with the command
>> + ? ? ? ? ? ? ? ?* status
>> + ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? req_desc->header.dword_2 =
>> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
>> +
>> + ? ? ? ? ? ? ? /* command descriptor fields */
>> + ? ? ? ? ? ? ? ucd_cmd_ptr->exp_data_transfer_len =
>> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(lrbp->cmd->transfersize);
>> + ? ? ? ? ? ? ? memcpy(ucd_cmd_ptr->cdb, lrbp->cmd->cmnd,
>> + ? ? ? ? ? ? ? ? ? ? (min_t(unsigned short,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?lrbp->cmd->cmd_len,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?(unsigned short)MAX_CDB_SIZE)));
> Is there any reason type casting for MAX_CDB_SIZE ?

No, we'll remove the type casting.

>
>> +
>> + ? ? ? ? ? ? ? ucd_cmd_ptr->header.dword_0 =
>> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? upiu_flags,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->lun,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->task_tag));
>> + ? ? ? ? ? ? ? ucd_cmd_ptr->header.dword_1 =
>> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0));
>> +
>> + ? ? ? ? ? ? ? /* set command response to zero */
>> + ? ? ? ? ? ? ? memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));
> Why did you set ucd_rsp_ptr to zero afte adding value ?

We are assigning values to ucd_cmd_ptr(command) and memset is being
done on ucd_rsp_ptr(response).

> And plz add break here.

ok, we'll update it.

>> + ? ? ? case UTP_CMD_TYPE_DEV_MANAGE:
>> + ? ? ? ? ? ? ? /* For query function implementation */
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case UTP_CMD_TYPE_UFS:
>> + ? ? ? ? ? ? ? /* For UFS native command implementation */
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? } /* end of switch, command_type */
>> +}
>> +
>> +/**
>> + * ufshcd_queuecommand - main entry point for SCSI requests
>> + * @cmd: command from SCSI Midlayer
>> + * @done: call back function
>> + *
>> + * Retruns 0 for success, SCSI Midlayer specific error in case of failure
>> + */
>> +static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
>> +{
>> + ? ? ? struct ufshcd_lrb *lrbp;
>> + ? ? ? struct ufs_hba *hba;
>> + ? ? ? unsigned long flags;
>> + ? ? ? int tag;
>> +
>> + ? ? ? hba = (struct ufs_hba *) &host->hostdata[0];
>> +
>> + ? ? ? tag = cmd->request->tag;
>> +
>> + ? ? ? if (UFSHCD_STATE_OPERATIONAL != hba->ufshcd_state)
>> + ? ? ? ? ? ? ? return SCSI_MLQUEUE_HOST_BUSY;
>> +
>> + ? ? ? lrbp = &hba->lrb[tag];
>> +
>> + ? ? ? lrbp->cmd = cmd;
>> + ? ? ? lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
>> + ? ? ? lrbp->sense_buffer = cmd->sense_buffer;
>> + ? ? ? lrbp->task_tag = tag;
>> + ? ? ? lrbp->lun = cmd->device->lun;
>> +
>> + ? ? ? lrbp->command_type = UTP_CMD_TYPE_SCSI;
>> +
>> + ? ? ? /* form UPIU before issuing the command */
>> + ? ? ? ufshcd_compose_upiu(hba, lrbp);
>> + ? ? ? ufshcd_map_sg(lrbp);
>> +
>> + ? ? ? /* issue command to the controller */
>> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> + ? ? ? ufshcd_send_command(hba, tag);
>> + ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? return 0;
>> +}
>> +
>> +/**
>> ?* ufshcd_memory_alloc - allocate memory for host memory space data structures
>> ?* @hba: per adapter instance
>> ?*
>> @@ -680,6 +925,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
>> ? ? ? ?ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
>>
>> ? ? ? ?hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
>> + ? ? ? scsi_scan_host(hba->host);
>>
>> ? ? ? ?return 0;
>> ?}
>> @@ -763,6 +1009,190 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
>> ?}
>>
>> ?/**
>> + * ufshcd_slave_alloc - handle initial scsi devie configurations
>> + * @sdev: pointer to scsi device
>> + *
>> + * Returns success
>> + */
>> +static int ufshcd_slave_alloc(struct scsi_device *sdev)
>> +{
>> + ? ? ? struct ufs_hba *hba;
>> +
>> + ? ? ? hba = (struct ufs_hba *)sdev->host->hostdata;
>> + ? ? ? sdev->tagged_supported = 1;
>> + ? ? ? scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
>> + ? ? ? scsi_activate_tcq(sdev, hba->nutrs);
>> +
>> + ? ? ? return 0;
>> +}
>> +
>> +/**
>> + * ufshcd_slave_destroy - remove scsi device configurations
>> + * @sdev: pointer to scsi device
>> + */
>> +static void ufshcd_slave_destroy(struct scsi_device *sdev)
>> +{
>> + ? ? ? struct ufs_hba *hba;
>> +
>> + ? ? ? hba = (struct ufs_hba *)sdev->host->hostdata;
>> + ? ? ? scsi_deactivate_tcq(sdev, hba->nutrs);
>> +}
>> +
>> +/**
>> + * ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
>> + * @lrb: pointer to local reference block of completed command
>> + * @scsi_status: SCSI command status
>> + *
>> + * Returns value base on SCSI command status
>> + */
>> +static inline int
>> +ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
>> +{
>> + ? ? ? int result = 0;
>> +
>> + ? ? ? switch (scsi_status) {
>> + ? ? ? case SAM_STAT_GOOD:
>> + ? ? ? ? ? ? ? result |= DID_OK << 16 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_GOOD;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case SAM_STAT_CHECK_CONDITION:
>> + ? ? ? ? ? ? ? result |= DRIVER_SENSE << 24 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_CHECK_CONDITION;
>> + ? ? ? ? ? ? ? ufshcd_copy_sense_data(lrbp);
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case SAM_STAT_BUSY:
>> + ? ? ? ? ? ? ? result |= DID_BUS_BUSY << 16 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_BUSY;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case SAM_STAT_TASK_SET_FULL:
>> + ? ? ? ? ? ? ? result |= DID_OK << 16 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_TASK_SET_FULL;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case SAM_STAT_TASK_ABORTED:
>> + ? ? ? ? ? ? ? result |= ?DID_ABORT << 16 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ?ABORT_TASK << 8 |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ?SAM_STAT_TASK_ABORTED;
> plz add break here also.

ok, we'll update it.

>> + ? ? ? default:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? } /* end of switch */
>> +
>> + ? ? ? return result;
>> +}
>> +
>> +/**
>> + * ufshcd_transfer_rsp_status - Get overall status of the response
>> + * @lrb: pointer to local reference block of completed command
>> + *
>> + * Returns result of the command to notify SCSI midlayer
>> + */
>> +static inline int ufshcd_transfer_rsp_status(struct ufshcd_lrb *lrbp)
>> +{
>> + ? ? ? int result = 0;
>> + ? ? ? int scsi_status;
>> + ? ? ? int ocs;
>> +
>> + ? ? ? /* overall command status of utrd */
>> + ? ? ? ocs = ufshcd_get_tr_ocs(lrbp);
>> +
>> + ? ? ? switch (ocs) {
>> + ? ? ? case OCS_SUCCESS:
>> +
>> + ? ? ? ? ? ? ? /* check if the returned transfer response is valid */
>> + ? ? ? ? ? ? ? result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
>> + ? ? ? ? ? ? ? if (result) {
>> + ? ? ? ? ? ? ? ? ? ? ? pr_err(UFSHCD ":Invalid Response\n");
>> + ? ? ? ? ? ? ? ? ? ? ? break;
>> + ? ? ? ? ? ? ? }
>> +
>> + ? ? ? ? ? ? ? /*
>> + ? ? ? ? ? ? ? ?* get the response UPIU result to extract
>> + ? ? ? ? ? ? ? ?* the SCSI command status
>> + ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
>> +
>> + ? ? ? ? ? ? ? /*
>> + ? ? ? ? ? ? ? ?* get the result based on SCSI status response
>> + ? ? ? ? ? ? ? ?* to notify the SCSI midlayer of the command status
>> + ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? scsi_status = result & MASK_SCSI_STATUS;
>> + ? ? ? ? ? ? ? result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_INVALID_CMD_TABLE_ATTR:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_INVALID_PRDT_ATTR:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_MISMATCH_DATA_BUF_SIZE:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_MISMATCH_RESP_UPIU_SIZE:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_PEER_COMM_FAILURE:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_ABORTED:
>> + ? ? ? ? ? ? ? result |= DID_ABORT << 16 | ABORT_TASK << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case OCS_FATAL_ERROR:
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? default:
>> + ? ? ? ? ? ? ? /* should not come here */
>> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
>> + ? ? ? ? ? ? ? pr_err(UFSHCD ":Invalid OCS from controller\n");
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? } /* end of switch */
>> +
>> + ? ? ? return result;
>> +}
>> +
>> +/**
>> + * ufshcd_transfer_req_compl - handle SCSI and query command completion
>> + * @hba: per adapter instance
>> + */
>> +static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
>> +{
>> + ? ? ? struct ufshcd_lrb *lrb;
>> + ? ? ? u32 completed_reqs;
>> + ? ? ? u32 tr_doorbell;
>> + ? ? ? int result;
>> + ? ? ? int index;
>> +
>> + ? ? ? lrb = hba->lrb;
>> + ? ? ? tr_doorbell =
>> + ? ? ? ? ? ? ? readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
>> + ? ? ? completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
>> +
>> + ? ? ? /* clear completed commands from outstanding_reqs */
>> + ? ? ? hba->outstanding_reqs ^= completed_reqs;
>> +
>> + ? ? ? for (index = 0; index < hba->nutrs; index++) {
>> + ? ? ? ? ? ? ? if (completed_reqs & (1 << index)) {
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? result = ufshcd_transfer_rsp_status(&lrb[index]);
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? if (NULL != lrb[index].cmd) {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? scsi_dma_unmap(lrb[index].cmd);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd->result = result;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd->scsi_done(lrb[index].cmd);
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* Mark completed command as NULL in LRB */
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd = NULL;
>> + ? ? ? ? ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ? } /* end of if */
>> + ? ? ? } /* end of for */
>> +
>> + ? ? ? /* Reset interrupt aggregation counters */
>> + ? ? ? ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
>> +}
>> +
>> +/**
>> ?* ufshcd_uic_cc_handler - handle UIC command completion
>> ?* @work: pointer to a work queue structure
>> ?*
>> @@ -793,6 +1223,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>> ?{
>> ? ? ? ?if (intr_status & UIC_COMMAND_COMPL)
>> ? ? ? ? ? ? ? ?schedule_work(&hba->uic_workq);
>> +
>> + ? ? ? if (intr_status & UTP_TRANSFER_REQ_COMPL)
>> + ? ? ? ? ? ? ? ufshcd_transfer_req_compl(hba);
>> ?}
>>
>> ?/**
>> @@ -830,7 +1263,13 @@ static struct scsi_host_template ufshcd_driver_template = {
>> ? ? ? ?.module ? ? ? ? ? ? ? ? = THIS_MODULE,
>> ? ? ? ?.name ? ? ? ? ? ? ? ? ? = UFSHCD,
>> ? ? ? ?.proc_name ? ? ? ? ? ? ?= UFSHCD,
>> + ? ? ? .queuecommand ? ? ? ? ? = ufshcd_queuecommand,
>> + ? ? ? .slave_alloc ? ? ? ? ? ?= ufshcd_slave_alloc,
>> + ? ? ? .slave_destroy ? ? ? ? ?= ufshcd_slave_destroy,
>> ? ? ? ?.this_id ? ? ? ? ? ? ? ?= -1,
>> + ? ? ? .sg_tablesize ? ? ? ? ? = SG_ALL,
>> + ? ? ? .cmd_per_lun ? ? ? ? ? ?= UFSHCD_CMD_PER_LUN,
>> + ? ? ? .can_queue ? ? ? ? ? ? ?= UFSHCD_CAN_QUEUE
>> ?};
>>
>> ?/**
>> @@ -1003,6 +1442,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> ? ? ? ?host->max_lun = UFSHCD_MAX_LUNS;
>> ? ? ? ?host->max_channel = UFSHCD_MAX_CHANNEL;
>> ? ? ? ?host->unique_id = host->host_no;
>> + ? ? ? host->max_cmd_len = MAX_CDB_SIZE;
>>
>> ? ? ? ?/* Initialize work queues */
>> ? ? ? ?INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
>> @@ -1014,6 +1454,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> ? ? ? ? ? ? ? ?goto out_lrb_free;
>> ? ? ? ?}
>>
>> + ? ? ? /* Enable scsi tag mapping */
>> + ? ? ? err = scsi_init_shared_tag_map(host, host->can_queue);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "init shared queue failed\n");
>> + ? ? ? ? ? ? ? goto out_free_irq;
>> + ? ? ? }
>> +
>> ? ? ? ?pci_set_drvdata(pdev, hba);
>>
>> ? ? ? ?err = scsi_add_host(host, &pdev->dev);
>> --
>> 1.7.5.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-scsi" in
>> the body of a message to [email protected]
>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html

Thanks,
~Santosh

2012-02-05 22:45:09

by Namjae Jeon

[permalink] [raw]
Subject: Re: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

2012/2/5 Namjae Jeon <[email protected]>:
> 2012/2/2 Vinayak Holikatti <[email protected]>:
>> From: Santosh Yaraganavi <[email protected]>
>>
>> UFS is designed to be the most advanced specification for
>> both embedded and removable flash memory-based storage in mobile devices
>> such as smart phones and tablet computers.  The UFS standard represents
>> an evolutionary progression of JEDEC standards in this field, and has been
>> specifically tailored for mobile applications and computing systems requiring
>> high performance and low power consumption.  The initial data throughput for
>> UFS will be ~300 megabytes per second (MB/s), and the standard also supports
>> command queuing features to raise random read/write speeds.
>>
>> To achieve the highest performance and most power efficient data
>> transport, UFS uses the leading industry interface standards to form its
>> Interconnect Layer: MIPI® Alliance’s M-PHY and UniProSM  specifications.
>> UniPro is a comprehensive specification meant to act as a universal
>> chip-to-chip protocol, providing a common tunnel for other protocols.
>> The M-PHY interface is designed as the primary physical interface (PHY layer)
>> for the UniPro specification, and is a high speed serial interface targeting
>> up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
>> per lane.
>>
>> MIPI’s M-PHY and UniPro specifications are optimized for mobile applications,
>> and are designed from the ground up for efficient power management in mobile
>> devices, including enabling efficient transitions between the active and power
>> save modes. Combined with a low active power level and a near-zero idle power
>> level, UFS offers the promise for significant reductions in device power
>> consumption.
>>
>> The UFS standard adopts the well-known SCSI Architecture Model and command
>> protocols supporting multiple commands with command queuing features and
>> enabling a multi-thread programming paradigm. This differs from conventional
>> flash-based memory cards and embedded flash solutions which process one
>> command at a time, limiting random read/write access performance.
>> In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
>> specification will allow system designers greater flexibility by simplifying
>> the involvement of the host processor in the operation of the flash storage
>> subsystem. The UFS HCI specification and the adoption of SCSI will provide
>> a well-known software programming model and enable wider market adoption.
>>
>> This patchset contains PCIe based UFS host controller driver which complies
>> to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
>> The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).
>>
>> This patch set is successfully applied on kernel version 3.3-rc2.
>>
>> Santosh Yaraganavi (4):
>>  [SCSI] ufshcd: UFS Host controller driver
>>  [SCSI] ufshcd: UFS UTP Transfer requests handling
>>  [SCSI] ufshcd: UFSHCI error handling
>>  [SCSI] ufshcd: SCSI error handling
>>
>>  drivers/scsi/Kconfig      |    1 +
>>  drivers/scsi/Makefile     |    1 +
>>  drivers/scsi/ufs/Kconfig  |   49 ++
>>  drivers/scsi/ufs/Makefile |    2 +
>>  drivers/scsi/ufs/ufs.h    |  203 +++++
>>  drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
>>  drivers/scsi/ufs/ufshci.h |  360 +++++++++
>>  7 files changed, 2570 insertions(+), 0 deletions(-)
>>  create mode 100644 drivers/scsi/ufs/Kconfig
>>  create mode 100644 drivers/scsi/ufs/Makefile
>>  create mode 100644 drivers/scsi/ufs/ufs.h
>>  create mode 100644 drivers/scsi/ufs/ufshcd.c
>>  create mode 100644 drivers/scsi/ufs/ufshci.h
>>
>> --
>> 1.7.5.4
> Hi.
> I have been waiting for ufs contribution.
> Unfortunately I don't have real target supported ufs(maybe only you
> have it). I can not debug and run this code, So just review only code
> with specification.
>>
Hi. Santosh.
Would you share performance measurement result (read/write seq,ran) ?
Thanks.
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to [email protected]
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at  http://www.tux.org/lkml/

2012-02-06 07:16:19

by Amit Sahrawat

[permalink] [raw]
Subject: Re: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

Hi,

In function:
+static int ufshcd_abort(struct scsi_cmnd *cmd)
+{...
- int err;
+ int err = -1;
...
+ spin_lock_irqsave(host->host_lock, flags);
+ pos = (1 << tag);
+
+ /* check if command is still pending */
+ if (!(hba->outstanding_reqs & pos)) {
- err = -1;
- spin_unlock_irqrestore(host->host_lock, flags);
+ goto out;
+ }
...
...
+out:
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return err;
+}
this will also take of matching out
spin_lock_irqsave()->spin_unlock_irqrestore() before exiting the
function.

In function:
+static int
+ufshcd_issue_tm_cmd(struct ufs_hba *hba,
+ struct ufshcd_lrb *lrbp,
+ u8 tm_function)
...
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* If task management queue is full */
+ if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
+ dev_err(&hba->pdev->dev, "Task management queue full\n");
I think this will map to a Buffered printf - which can potentially
result in BUG: Schedule while atomic()...
+ spin_unlock_irqrestore(host->host_lock, flags);
+ return err;
+ }
So, it could be like this:
+ spin_lock_irqsave(host->host_lock, flags);
+
+ /* If task management queue is full */
+ if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
+ spin_unlock_irqrestore(host->host_lock, flags);
+ dev_err(&hba->pdev->dev, "Task management queue full\n");
+ return err;
+ }

Thanks & Regards,
Amit Sahrawat


On Thu, Feb 2, 2012 at 10:27 AM, Vinayak Holikatti
<[email protected]> wrote:
> From: Santosh Yaraganavi <[email protected]>
>
> UFSHCD SCSI error handling includes following implementations,
> ?- Abort task
> ?- Device reset
> ?- Host reset
>
> Signed-off-by: Santosh Yaraganavi <[email protected]>
> Signed-off-by: Vinayak Holikatti <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Reviewed-by: Saugata Das <[email protected]>
> Reviewed-by: Vishak G <[email protected]>
> Reviewed-by: Girish K S <[email protected]>
> ---
> ?drivers/scsi/ufs/ufshcd.c | ?312 +++++++++++++++++++++++++++++++++++++++++++++
> ?1 files changed, 312 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index 1bfae84..7589dd1 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -76,6 +76,8 @@
> ?#define NULL 0
> ?#endif ?/* NULL */
>
> +#define UFSHCD_MAX_TM_SLOTS ? ?0xFF
> +
> ?#define BYTES_TO_DWORDS(p) ? ? (p >> 2)
> ?#define UFSHCD_MMIO_BASE ? ? ? (hba->mmio_base)
>
> @@ -83,6 +85,7 @@ enum {
> ? ? ? ?UFSHCD_MAX_CHANNEL ? ? ?= 1,
> ? ? ? ?UFSHCD_MAX_ID ? ? ? ? ? = 1,
> ? ? ? ?UFSHCD_MAX_LUNS ? ? ? ? = 8,
> + ? ? ? UFSHCD_MAX_TM_CMDS ? ? ?= 8,
> ? ? ? ?UFSHCD_CMD_PER_LUN ? ? ?= 16,
> ? ? ? ?UFSHCD_CAN_QUEUE ? ? ? ?= 32,
> ? ? ? ?BYTES_128 ? ? ? ? ? ? ? = 128,
> @@ -149,6 +152,7 @@ struct uic_command {
> ?* @host: Scsi_Host instance of the driver
> ?* @pdev: PCI device handle
> ?* @lrb: local reference block
> + * @outstanding_tasks: Bits representing outstanding task requests
> ?* @outstanding_reqs: Bits representing outstanding transfer requests
> ?* @capabilities: UFS Controller Capabilities
> ?* @nutrs: Transfer Request Queue depth supported by controller
> @@ -192,6 +196,7 @@ struct ufs_hba {
>
> ? ? ? ?struct ufshcd_lrb *lrb;
>
> + ? ? ? u32 outstanding_tasks;
> ? ? ? ?u32 outstanding_reqs;
>
> ? ? ? ?u32 capabilities;
> @@ -200,6 +205,8 @@ struct ufs_hba {
> ? ? ? ?u32 ufs_version;
>
> ? ? ? ?struct uic_command active_uic_cmd;
> + ? ? ? wait_queue_head_t ufshcd_tm_wait_queue;
> + ? ? ? u8 tm_condition[UFSHCD_MAX_TM_CMDS];
>
> ? ? ? ?u32 ufshcd_state;
> ? ? ? ?u32 int_enable_mask;
> @@ -278,6 +285,30 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
> ?}
>
> ?/**
> + * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
> + * @task_req_descp: pointer to utp_task_req_desc structure
> + *
> + * This function is used to get the OCS field from UTMRD
> + * Returns the OCS field in the UTMRD
> + */
> +static inline int
> +ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
> +{
> + ? ? ? return task_req_descp->header.dword_2 & MASK_OCS;
> +}
> +
> +/**
> + * ufshcd_is_tmq_full - checks if the task management slots are full
> + * @outstanding_tasks: contains the task management doorbell value
> + *
> + * Returns 1 if a free slot available, 0 if task slots are full
> + */
> +static inline int ufshcd_is_tmq_full(u32 outstanding_tasks)
> +{
> + ? ? ? return (UFSHCD_MAX_TM_SLOTS == outstanding_tasks) ? 0 : 1;
> +}
> +
> +/**
> ?* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
> ?* @reg: Register value of host controller status
> ?*
> @@ -1098,6 +1129,45 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
> ?}
>
> ?/**
> + * ufshcd_task_req_compl - handle task management request completion
> + * @hba: per adapter instance
> + * @index: index of the completed request
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
> +{
> + ? ? ? struct utp_upiu_task_rsp *task_rsp_upiup;
> + ? ? ? struct utp_task_req_desc *task_req_descp;
> + ? ? ? unsigned long flags;
> + ? ? ? int ocs_value;
> + ? ? ? int task_response = -1;
> +
> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
> +
> + ? ? ? /* clear completed tasks from outstanding_tasks */
> + ? ? ? hba->outstanding_tasks ^= index;
> +
> + ? ? ? task_req_descp =
> + ? ? ? ? ? ? ? (struct utp_task_req_desc *)hba->utmrdl_virt_addr_aligned;
> + ? ? ? ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
> +
> + ? ? ? if (OCS_SUCCESS == ocs_value) {
> +
> + ? ? ? ? ? ? ? task_rsp_upiup = (struct utp_upiu_task_rsp *)
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?task_req_descp[index].task_rsp_upiu;
> + ? ? ? ? ? ? ? task_response = be32_to_cpu(task_rsp_upiup->header.dword_1);
> + ? ? ? ? ? ? ? task_response = ((task_response & MASK_TASK_RESPONSE) >> 8);
> +
> + ? ? ? ? ? ? ? /* clear task response */
> + ? ? ? ? ? ? ? memset(task_rsp_upiup, 0, sizeof(struct utp_upiu_task_rsp));
> + ? ? ? }
> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
> +
> + ? ? ? return task_response;
> +}
> +
> +/**
> ?* ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
> ?* @lrb: pointer to local reference block of completed command
> ?* @scsi_status: SCSI command status
> @@ -1314,6 +1384,31 @@ fatal_eh:
> ?}
>
> ?/**
> + * ufshcd_tmc_handler - handle task management function completion
> + * @hba: per adapter instance
> + */
> +static void ufshcd_tmc_handler(struct ufs_hba *hba)
> +{
> + ? ? ? u32 completed_reqs;
> + ? ? ? u32 tm_doorbell;
> + ? ? ? int i;
> +
> + ? ? ? tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
> + ? ? ? completed_reqs = tm_doorbell ^ hba->outstanding_tasks;
> + ? ? ? for (i = 0; i < hba->nutmrs; i++) {
> + ? ? ? ? ? ? ? if (completed_reqs & (1 << i)) {
> +
> + ? ? ? ? ? ? ? ? ? ? ? /*
> + ? ? ? ? ? ? ? ? ? ? ? ?* Change the default value 0xFF to 0 to indicate
> + ? ? ? ? ? ? ? ? ? ? ? ?* completion of respective task management command
> + ? ? ? ? ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? ? ? ? ? hba->tm_condition[i] = 0;
> + ? ? ? ? ? ? ? ? ? ? ? wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +}
> +
> +/**
> ?* ufshcd_sl_intr - Interrupt service routine
> ?* @hba: per adapter instance
> ?* @intr_status: contains interrupts generated by the controller
> @@ -1327,6 +1422,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
> ? ? ? ?if (intr_status & UIC_COMMAND_COMPL)
> ? ? ? ? ? ? ? ?schedule_work(&hba->uic_workq);
>
> + ? ? ? if (intr_status & UTP_TASK_REQ_COMPL)
> + ? ? ? ? ? ? ? ufshcd_tmc_handler(hba);
> +
> ? ? ? ?if (intr_status & UTP_TRANSFER_REQ_COMPL)
> ? ? ? ? ? ? ? ?ufshcd_transfer_req_compl(hba);
> ?}
> @@ -1362,6 +1460,209 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
> ? ? ? ?return retval;
> ?}
>
> +/**
> + * ufshcd_issue_tm_cmd - issues task management commands to controller
> + * @hba: per adapter instance
> + * @lrbp: pointer to local reference block
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int
> +ufshcd_issue_tm_cmd(struct ufs_hba *hba,
> + ? ? ? ? ? ? ? ? ? struct ufshcd_lrb *lrbp,
> + ? ? ? ? ? ? ? ? ? u8 tm_function)
> +{
> + ? ? ? struct utp_task_req_desc *task_req_descp;
> + ? ? ? struct utp_upiu_task_req *task_req_upiup;
> + ? ? ? struct Scsi_Host *host;
> + ? ? ? unsigned long flags;
> + ? ? ? int i;
> + ? ? ? int free_slot = 0;
> + ? ? ? int err = -1;
> +
> + ? ? ? host = hba->host;
> +
> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> +
> + ? ? ? /* If task management queue is full */
> + ? ? ? if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Task management queue full\n");
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? ? ? ? ? return err;
> + ? ? ? }
> +
> + ? ? ? for (i = 0; i < hba->nutmrs; i++) {
> + ? ? ? ? ? ? ? if (!(hba->outstanding_tasks & (1 << i))) {
> + ? ? ? ? ? ? ? ? ? ? ? free_slot = i;
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? /* Configure task request descriptor */
> + ? ? ? task_req_descp =
> + ? ? ? ? ? ? ? (struct utp_task_req_desc *) hba->utmrdl_virt_addr_aligned;
> + ? ? ? task_req_descp += free_slot;
> +
> + ? ? ? memset(task_req_descp, 0, sizeof(struct utp_task_req_desc));
> + ? ? ? task_req_descp->header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
> + ? ? ? task_req_descp->header.dword_2 =
> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
> +
> + ? ? ? /* Configure task request UPIU */
> + ? ? ? task_req_upiup =
> + ? ? ? ? ? ? ? (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
> + ? ? ? task_req_upiup->header.dword_0 =
> + ? ? ? ? ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->lun, lrbp->task_tag));
> + ? ? ? task_req_upiup->header.dword_1 =
> + ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
> +
> + ? ? ? task_req_upiup->input_param1 = lrbp->lun;
> + ? ? ? task_req_upiup->input_param1 =
> + ? ? ? ? ? ? ? cpu_to_be32(task_req_upiup->input_param1);
> + ? ? ? task_req_upiup->input_param2 = lrbp->task_tag;
> + ? ? ? task_req_upiup->input_param2 =
> + ? ? ? ? ? ? ? cpu_to_be32(task_req_upiup->input_param2);
> +
> + ? ? ? /* send command to the controller */
> + ? ? ? hba->outstanding_tasks |= (1 << free_slot);
> + ? ? ? writel((1 << free_slot),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_DOOR_BELL));
> +
> + ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> +
> + ? ? ? /* wait until the task management command is completed */
> + ? ? ? err = wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(hba->tm_condition[free_slot] != 0),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?60 * HZ);
> + ? ? ? hba->tm_condition[free_slot] = 0xFF;
> + ? ? ? if (!err) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Task management command timed-out\n");
> + ? ? ? ? ? ? ? return err;
> + ? ? ? }
> + ? ? ? return ufshcd_task_req_compl(hba, free_slot);
> +}
> +
> +/**
> + * ufshcd_device_reset - reset device and abort all the pending commands
> + * @cmd: SCSI command pointer
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_device_reset(struct scsi_cmnd *cmd)
> +{
> + ? ? ? struct Scsi_Host *host;
> + ? ? ? struct ufs_hba *hba;
> + ? ? ? unsigned long flags;
> + ? ? ? int reset_lun;
> + ? ? ? unsigned int tag;
> + ? ? ? u32 i;
> + ? ? ? u32 pos; /* pos: represents a bit in a register */
> + ? ? ? int err = -1;
> +
> + ? ? ? host = cmd->device->host;
> + ? ? ? hba = (struct ufs_hba *)host->hostdata;
> + ? ? ? tag = cmd->request->tag;
> +
> + ? ? ? if ((hba->lrb[tag].cmd == cmd))
> + ? ? ? ? ? ? ? reset_lun = hba->lrb[tag].lun;
> + ? ? ? else
> + ? ? ? ? ? ? ? goto out;
> +
> + ? ? ? err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
> + ? ? ? if (!err) {
> + ? ? ? ? ? ? ? spin_lock_irqsave(host->host_lock, flags);
> + ? ? ? ? ? ? ? for (i = 0; i < hba->nutrs; i++) {
> + ? ? ? ? ? ? ? ? ? ? ? pos = (1 << i);
> + ? ? ? ? ? ? ? ? ? ? ? if ((pos & hba->outstanding_reqs) &&
> + ? ? ? ? ? ? ? ? ? ? ? ? ? (reset_lun == hba->lrb[i].lun)) {
> +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* clear the respective UTRLCLR bit */
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? writel(~pos, (UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TRANSFER_REQ_LIST_CLEAR));
> +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->outstanding_reqs ^= pos;
> +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (hba->lrb[i].cmd) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? scsi_dma_unmap(hba->lrb[i].cmd);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd->result =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DID_ABORT << 16;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd->scsi_done(cmd);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd = NULL;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? } /* end of for */
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? } /*end of if */
> +out:
> + ? ? ? return err;
> +}
> +
> +/**
> + * ufshcd_host_reset - Main reset function registered with scsi layer
> + * @cmd: SCSI command pointer
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_host_reset(struct scsi_cmnd *cmd)
> +{
> + ? ? ? struct ufs_hba *hba = NULL;
> +
> + ? ? ? hba = (struct ufs_hba *) &cmd->device->host->hostdata[0];
> +
> + ? ? ? if (UFSHCD_STATE_RESET == hba->ufshcd_state)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? return ufshcd_do_reset(hba) ? -1 : 0;
> +}
> +
> +/**
> + * ufshcd_abort - abort a specific command
> + * @cmd: SCSI command pointer
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_abort(struct scsi_cmnd *cmd)
> +{
> + ? ? ? struct Scsi_Host *host;
> + ? ? ? struct ufs_hba *hba;
> + ? ? ? unsigned long flags;
> + ? ? ? unsigned int tag;
> + ? ? ? unsigned int pos;
> + ? ? ? int err;
> +
> + ? ? ? host = cmd->device->host;
> + ? ? ? hba = (struct ufs_hba *) host->hostdata;
> + ? ? ? tag = cmd->request->tag;
> +
> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> + ? ? ? pos = (1 << tag);
> +
> + ? ? ? /* check if command is still pending */
> + ? ? ? if (!(hba->outstanding_reqs & pos)) {
> + ? ? ? ? ? ? ? err = -1;
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? ? ? ? ? goto out;
> + ? ? ? }
> +
> + ? ? ? err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
> + ? ? ? if (!err) {
> + ? ? ? ? ? ? ? spin_lock_irqsave(host->host_lock, flags);
> + ? ? ? ? ? ? ? scsi_dma_unmap(cmd);
> +
> + ? ? ? ? ? ? ? /* clear the respective UTRLCLR bit */
> + ? ? ? ? ? ? ? writel(~pos,
> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_LIST_CLEAR));
> + ? ? ? ? ? ? ? hba->outstanding_reqs &= ~pos;
> + ? ? ? ? ? ? ? hba->lrb[tag].cmd = NULL;
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? }
> +out:
> + ? ? ? return err;
> +}
> +
> ?static struct scsi_host_template ufshcd_driver_template = {
> ? ? ? ?.module ? ? ? ? ? ? ? ? = THIS_MODULE,
> ? ? ? ?.name ? ? ? ? ? ? ? ? ? = UFSHCD,
> @@ -1369,6 +1670,9 @@ static struct scsi_host_template ufshcd_driver_template = {
> ? ? ? ?.queuecommand ? ? ? ? ? = ufshcd_queuecommand,
> ? ? ? ?.slave_alloc ? ? ? ? ? ?= ufshcd_slave_alloc,
> ? ? ? ?.slave_destroy ? ? ? ? ?= ufshcd_slave_destroy,
> + ? ? ? .eh_abort_handler ? ? ? = ufshcd_abort,
> + ? ? ? .eh_device_reset_handler = ufshcd_device_reset,
> + ? ? ? .eh_host_reset_handler ?= ufshcd_host_reset,
> ? ? ? ?.this_id ? ? ? ? ? ? ? ?= -1,
> ? ? ? ?.sg_tablesize ? ? ? ? ? = SG_ALL,
> ? ? ? ?.cmd_per_lun ? ? ? ? ? ?= UFSHCD_CMD_PER_LUN,
> @@ -1483,6 +1787,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> ? ? ? ?struct Scsi_Host *host;
> ? ? ? ?struct ufs_hba *hba;
> ? ? ? ?int ufs_hba_len;
> + ? ? ? int index;
> ? ? ? ?int err;
>
> ? ? ? ?ufs_hba_len = sizeof(struct ufs_hba);
> @@ -1547,6 +1852,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> ? ? ? ?host->unique_id = host->host_no;
> ? ? ? ?host->max_cmd_len = MAX_CDB_SIZE;
>
> + ? ? ? /* Initailze wait queue for task management */
> + ? ? ? init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
> +
> + ? ? ? /* Initially assign invalid value to tm_condition */
> + ? ? ? for (index = 0; index < hba->nutmrs; index++)
> + ? ? ? ? ? ? ? hba->tm_condition[index] = 0xFF;
> +
> ? ? ? ?/* Initialize work queues */
> ? ? ? ?INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
> ? ? ? ?INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
> --
> 1.7.5.4
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at ?http://www.tux.org/lkml/

2012-02-06 07:18:20

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

On Mon, Feb 6, 2012 at 4:15 AM, Namjae Jeon <[email protected]> wrote:
> 2012/2/5 Namjae Jeon <[email protected]>:
>> 2012/2/2 Vinayak Holikatti <[email protected]>:
>>> From: Santosh Yaraganavi <[email protected]>
>>>
>>> UFS is designed to be the most advanced specification for
>>> both embedded and removable flash memory-based storage in mobile devices
>>> such as smart phones and tablet computers. ?The UFS standard represents
>>> an evolutionary progression of JEDEC standards in this field, and has been
>>> specifically tailored for mobile applications and computing systems requiring
>>> high performance and low power consumption. ?The initial data throughput for
>>> UFS will be ~300 megabytes per second (MB/s), and the standard also supports
>>> command queuing features to raise random read/write speeds.
>>>
>>> To achieve the highest performance and most power efficient data
>>> transport, UFS uses the leading industry interface standards to form its
>>> Interconnect Layer: MIPI? Alliance?s M-PHY and UniProSM ?specifications.
>>> UniPro is a comprehensive specification meant to act as a universal
>>> chip-to-chip protocol, providing a common tunnel for other protocols.
>>> The M-PHY interface is designed as the primary physical interface (PHY layer)
>>> for the UniPro specification, and is a high speed serial interface targeting
>>> up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
>>> per lane.
>>>
>>> MIPI?s M-PHY and UniPro specifications are optimized for mobile applications,
>>> and are designed from the ground up for efficient power management in mobile
>>> devices, including enabling efficient transitions between the active and power
>>> save modes. Combined with a low active power level and a near-zero idle power
>>> level, UFS offers the promise for significant reductions in device power
>>> consumption.
>>>
>>> The UFS standard adopts the well-known SCSI Architecture Model and command
>>> protocols supporting multiple commands with command queuing features and
>>> enabling a multi-thread programming paradigm. This differs from conventional
>>> flash-based memory cards and embedded flash solutions which process one
>>> command at a time, limiting random read/write access performance.
>>> In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
>>> specification will allow system designers greater flexibility by simplifying
>>> the involvement of the host processor in the operation of the flash storage
>>> subsystem. The UFS HCI specification and the adoption of SCSI will provide
>>> a well-known software programming model and enable wider market adoption.
>>>
>>> This patchset contains PCIe based UFS host controller driver which complies
>>> to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
>>> The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).
>>>
>>> This patch set is successfully applied on kernel version 3.3-rc2.
>>>
>>> Santosh Yaraganavi (4):
>>> ?[SCSI] ufshcd: UFS Host controller driver
>>> ?[SCSI] ufshcd: UFS UTP Transfer requests handling
>>> ?[SCSI] ufshcd: UFSHCI error handling
>>> ?[SCSI] ufshcd: SCSI error handling
>>>
>>> ?drivers/scsi/Kconfig ? ? ?| ? ?1 +
>>> ?drivers/scsi/Makefile ? ? | ? ?1 +
>>> ?drivers/scsi/ufs/Kconfig ?| ? 49 ++
>>> ?drivers/scsi/ufs/Makefile | ? ?2 +
>>> ?drivers/scsi/ufs/ufs.h ? ?| ?203 +++++
>>> ?drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
>>> ?drivers/scsi/ufs/ufshci.h | ?360 +++++++++
>>> ?7 files changed, 2570 insertions(+), 0 deletions(-)
>>> ?create mode 100644 drivers/scsi/ufs/Kconfig
>>> ?create mode 100644 drivers/scsi/ufs/Makefile
>>> ?create mode 100644 drivers/scsi/ufs/ufs.h
>>> ?create mode 100644 drivers/scsi/ufs/ufshcd.c
>>> ?create mode 100644 drivers/scsi/ufs/ufshci.h
>>>
>>> --
>>> 1.7.5.4
>> Hi.
>> I have been waiting for ufs contribution.
>> Unfortunately I don't have real target supported ufs(maybe only you
>> have it). I can not debug and run this code, So just review only code
>> with specification.
>>>
> Hi. Santosh.
> Would you share performance measurement result (read/write seq,ran) ?
> Thanks.

Hi Namjae,

Currently UFS Driver is being tested on on UFS Controller FPGA and UFS
Device FPGA. So currently the main concentration is on functionality.
Performance on FPGA will not be the correct measurement.

>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>>> the body of a message to [email protected]
>>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at ?http://www.tux.org/lkml/

--
~Santosh

2012-02-06 19:00:31

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

On Mon, Feb 6, 2012 at 12:46 PM, Amit Sahrawat
<[email protected]> wrote:
> Hi,
>
> In function:
> +static int ufshcd_abort(struct scsi_cmnd *cmd)
> +{...
> - ? ? ? int err;
> + ? ? ? int err = -1;
> ...
> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> + ? ? ? pos = (1 << tag);
> +
> + ? ? ? /* check if command is still pending */
> + ? ? ? if (!(hba->outstanding_reqs & pos)) {
> - ? ? ? ? ? ? ? err = -1;
> - ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? ? ? ? ? goto out;
> + ? ? ? }
> ...
> ...
> +out:
> + ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? return err;
> +}
> this will also take of matching out
> spin_lock_irqsave()->spin_unlock_irqrestore() before exiting the
> function.

That will not work again.
In case "if (!(hba->outstanding_reqs & pos))" is false,
i.e if the command for which the abort is issued still exists,

ufshcd_issue_tm_cmd() will be called and if no error returned
by the function, the code after "out:" will be executed. Which will
try to "spin_unlock_irqrestore",
even though no lock has been acquired.

>
> In function:
> +static int
> +ufshcd_issue_tm_cmd(struct ufs_hba *hba,
> + ? ? ? ? ? ? ? ? ? struct ufshcd_lrb *lrbp,
> + ? ? ? ? ? ? ? ? ? u8 tm_function)
> ...
> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> +
> + ? ? ? /* If task management queue is full */
> + ? ? ? if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Task management queue full\n");
> I think this will map to a Buffered printf - which can potentially
> result in BUG: Schedule while atomic()...
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? ? ? ? ? return err;
> + ? ? ? }
> So, it could be like this:

ok, Thanks, I'll look into it.

> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> +
> + ? ? ? /* If task management queue is full */
> + ? ? ? if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Task management queue full\n");
> + ? ? ? ? ? ? ? return err;
> + ? ? ? }
>
> Thanks & Regards,
> Amit Sahrawat
>
>
> On Thu, Feb 2, 2012 at 10:27 AM, Vinayak Holikatti
> <[email protected]> wrote:
>> From: Santosh Yaraganavi <[email protected]>
>>
>> UFSHCD SCSI error handling includes following implementations,
>> ?- Abort task
>> ?- Device reset
>> ?- Host reset
>>
>> Signed-off-by: Santosh Yaraganavi <[email protected]>
>> Signed-off-by: Vinayak Holikatti <[email protected]>
>> Reviewed-by: Arnd Bergmann <[email protected]>
>> Reviewed-by: Saugata Das <[email protected]>
>> Reviewed-by: Vishak G <[email protected]>
>> Reviewed-by: Girish K S <[email protected]>
>> ---
>> ?drivers/scsi/ufs/ufshcd.c | ?312 +++++++++++++++++++++++++++++++++++++++++++++
>> ?1 files changed, 312 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>> index 1bfae84..7589dd1 100644
>> --- a/drivers/scsi/ufs/ufshcd.c
>> +++ b/drivers/scsi/ufs/ufshcd.c
>> @@ -76,6 +76,8 @@
>> ?#define NULL 0
>> ?#endif ?/* NULL */
>>
>> +#define UFSHCD_MAX_TM_SLOTS ? ?0xFF
>> +
>> ?#define BYTES_TO_DWORDS(p) ? ? (p >> 2)
>> ?#define UFSHCD_MMIO_BASE ? ? ? (hba->mmio_base)
>>
>> @@ -83,6 +85,7 @@ enum {
>> ? ? ? ?UFSHCD_MAX_CHANNEL ? ? ?= 1,
>> ? ? ? ?UFSHCD_MAX_ID ? ? ? ? ? = 1,
>> ? ? ? ?UFSHCD_MAX_LUNS ? ? ? ? = 8,
>> + ? ? ? UFSHCD_MAX_TM_CMDS ? ? ?= 8,
>> ? ? ? ?UFSHCD_CMD_PER_LUN ? ? ?= 16,
>> ? ? ? ?UFSHCD_CAN_QUEUE ? ? ? ?= 32,
>> ? ? ? ?BYTES_128 ? ? ? ? ? ? ? = 128,
>> @@ -149,6 +152,7 @@ struct uic_command {
>> ?* @host: Scsi_Host instance of the driver
>> ?* @pdev: PCI device handle
>> ?* @lrb: local reference block
>> + * @outstanding_tasks: Bits representing outstanding task requests
>> ?* @outstanding_reqs: Bits representing outstanding transfer requests
>> ?* @capabilities: UFS Controller Capabilities
>> ?* @nutrs: Transfer Request Queue depth supported by controller
>> @@ -192,6 +196,7 @@ struct ufs_hba {
>>
>> ? ? ? ?struct ufshcd_lrb *lrb;
>>
>> + ? ? ? u32 outstanding_tasks;
>> ? ? ? ?u32 outstanding_reqs;
>>
>> ? ? ? ?u32 capabilities;
>> @@ -200,6 +205,8 @@ struct ufs_hba {
>> ? ? ? ?u32 ufs_version;
>>
>> ? ? ? ?struct uic_command active_uic_cmd;
>> + ? ? ? wait_queue_head_t ufshcd_tm_wait_queue;
>> + ? ? ? u8 tm_condition[UFSHCD_MAX_TM_CMDS];
>>
>> ? ? ? ?u32 ufshcd_state;
>> ? ? ? ?u32 int_enable_mask;
>> @@ -278,6 +285,30 @@ static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
>> ?}
>>
>> ?/**
>> + * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
>> + * @task_req_descp: pointer to utp_task_req_desc structure
>> + *
>> + * This function is used to get the OCS field from UTMRD
>> + * Returns the OCS field in the UTMRD
>> + */
>> +static inline int
>> +ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
>> +{
>> + ? ? ? return task_req_descp->header.dword_2 & MASK_OCS;
>> +}
>> +
>> +/**
>> + * ufshcd_is_tmq_full - checks if the task management slots are full
>> + * @outstanding_tasks: contains the task management doorbell value
>> + *
>> + * Returns 1 if a free slot available, 0 if task slots are full
>> + */
>> +static inline int ufshcd_is_tmq_full(u32 outstanding_tasks)
>> +{
>> + ? ? ? return (UFSHCD_MAX_TM_SLOTS == outstanding_tasks) ? 0 : 1;
>> +}
>> +
>> +/**
>> ?* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
>> ?* @reg: Register value of host controller status
>> ?*
>> @@ -1098,6 +1129,45 @@ static void ufshcd_slave_destroy(struct scsi_device *sdev)
>> ?}
>>
>> ?/**
>> + * ufshcd_task_req_compl - handle task management request completion
>> + * @hba: per adapter instance
>> + * @index: index of the completed request
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
>> +{
>> + ? ? ? struct utp_upiu_task_rsp *task_rsp_upiup;
>> + ? ? ? struct utp_task_req_desc *task_req_descp;
>> + ? ? ? unsigned long flags;
>> + ? ? ? int ocs_value;
>> + ? ? ? int task_response = -1;
>> +
>> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
>> +
>> + ? ? ? /* clear completed tasks from outstanding_tasks */
>> + ? ? ? hba->outstanding_tasks ^= index;
>> +
>> + ? ? ? task_req_descp =
>> + ? ? ? ? ? ? ? (struct utp_task_req_desc *)hba->utmrdl_virt_addr_aligned;
>> + ? ? ? ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
>> +
>> + ? ? ? if (OCS_SUCCESS == ocs_value) {
>> +
>> + ? ? ? ? ? ? ? task_rsp_upiup = (struct utp_upiu_task_rsp *)
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?task_req_descp[index].task_rsp_upiu;
>> + ? ? ? ? ? ? ? task_response = be32_to_cpu(task_rsp_upiup->header.dword_1);
>> + ? ? ? ? ? ? ? task_response = ((task_response & MASK_TASK_RESPONSE) >> 8);
>> +
>> + ? ? ? ? ? ? ? /* clear task response */
>> + ? ? ? ? ? ? ? memset(task_rsp_upiup, 0, sizeof(struct utp_upiu_task_rsp));
>> + ? ? ? }
>> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
>> +
>> + ? ? ? return task_response;
>> +}
>> +
>> +/**
>> ?* ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
>> ?* @lrb: pointer to local reference block of completed command
>> ?* @scsi_status: SCSI command status
>> @@ -1314,6 +1384,31 @@ fatal_eh:
>> ?}
>>
>> ?/**
>> + * ufshcd_tmc_handler - handle task management function completion
>> + * @hba: per adapter instance
>> + */
>> +static void ufshcd_tmc_handler(struct ufs_hba *hba)
>> +{
>> + ? ? ? u32 completed_reqs;
>> + ? ? ? u32 tm_doorbell;
>> + ? ? ? int i;
>> +
>> + ? ? ? tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
>> + ? ? ? completed_reqs = tm_doorbell ^ hba->outstanding_tasks;
>> + ? ? ? for (i = 0; i < hba->nutmrs; i++) {
>> + ? ? ? ? ? ? ? if (completed_reqs & (1 << i)) {
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? /*
>> + ? ? ? ? ? ? ? ? ? ? ? ?* Change the default value 0xFF to 0 to indicate
>> + ? ? ? ? ? ? ? ? ? ? ? ?* completion of respective task management command
>> + ? ? ? ? ? ? ? ? ? ? ? ?*/
>> + ? ? ? ? ? ? ? ? ? ? ? hba->tm_condition[i] = 0;
>> + ? ? ? ? ? ? ? ? ? ? ? wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
>> + ? ? ? ? ? ? ? }
>> + ? ? ? }
>> +}
>> +
>> +/**
>> ?* ufshcd_sl_intr - Interrupt service routine
>> ?* @hba: per adapter instance
>> ?* @intr_status: contains interrupts generated by the controller
>> @@ -1327,6 +1422,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>> ? ? ? ?if (intr_status & UIC_COMMAND_COMPL)
>> ? ? ? ? ? ? ? ?schedule_work(&hba->uic_workq);
>>
>> + ? ? ? if (intr_status & UTP_TASK_REQ_COMPL)
>> + ? ? ? ? ? ? ? ufshcd_tmc_handler(hba);
>> +
>> ? ? ? ?if (intr_status & UTP_TRANSFER_REQ_COMPL)
>> ? ? ? ? ? ? ? ?ufshcd_transfer_req_compl(hba);
>> ?}
>> @@ -1362,6 +1460,209 @@ static irqreturn_t ufshcd_intr(int irq, void *__hba)
>> ? ? ? ?return retval;
>> ?}
>>
>> +/**
>> + * ufshcd_issue_tm_cmd - issues task management commands to controller
>> + * @hba: per adapter instance
>> + * @lrbp: pointer to local reference block
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int
>> +ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>> + ? ? ? ? ? ? ? ? ? struct ufshcd_lrb *lrbp,
>> + ? ? ? ? ? ? ? ? ? u8 tm_function)
>> +{
>> + ? ? ? struct utp_task_req_desc *task_req_descp;
>> + ? ? ? struct utp_upiu_task_req *task_req_upiup;
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? unsigned long flags;
>> + ? ? ? int i;
>> + ? ? ? int free_slot = 0;
>> + ? ? ? int err = -1;
>> +
>> + ? ? ? host = hba->host;
>> +
>> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> +
>> + ? ? ? /* If task management queue is full */
>> + ? ? ? if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Task management queue full\n");
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? ? ? ? ? return err;
>> + ? ? ? }
>> +
>> + ? ? ? for (i = 0; i < hba->nutmrs; i++) {
>> + ? ? ? ? ? ? ? if (!(hba->outstanding_tasks & (1 << i))) {
>> + ? ? ? ? ? ? ? ? ? ? ? free_slot = i;
>> + ? ? ? ? ? ? ? ? ? ? ? break;
>> + ? ? ? ? ? ? ? }
>> + ? ? ? }
>> +
>> + ? ? ? /* Configure task request descriptor */
>> + ? ? ? task_req_descp =
>> + ? ? ? ? ? ? ? (struct utp_task_req_desc *) hba->utmrdl_virt_addr_aligned;
>> + ? ? ? task_req_descp += free_slot;
>> +
>> + ? ? ? memset(task_req_descp, 0, sizeof(struct utp_task_req_desc));
>> + ? ? ? task_req_descp->header.dword_0 = cpu_to_le32(UTP_REQ_DESC_INT_CMD);
>> + ? ? ? task_req_descp->header.dword_2 =
>> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
>> +
>> + ? ? ? /* Configure task request UPIU */
>> + ? ? ? task_req_upiup =
>> + ? ? ? ? ? ? ? (struct utp_upiu_task_req *) task_req_descp->task_req_upiu;
>> + ? ? ? task_req_upiup->header.dword_0 =
>> + ? ? ? ? ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ, 0,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->lun, lrbp->task_tag));
>> + ? ? ? task_req_upiup->header.dword_1 =
>> + ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
>> +
>> + ? ? ? task_req_upiup->input_param1 = lrbp->lun;
>> + ? ? ? task_req_upiup->input_param1 =
>> + ? ? ? ? ? ? ? cpu_to_be32(task_req_upiup->input_param1);
>> + ? ? ? task_req_upiup->input_param2 = lrbp->task_tag;
>> + ? ? ? task_req_upiup->input_param2 =
>> + ? ? ? ? ? ? ? cpu_to_be32(task_req_upiup->input_param2);
>> +
>> + ? ? ? /* send command to the controller */
>> + ? ? ? hba->outstanding_tasks |= (1 << free_slot);
>> + ? ? ? writel((1 << free_slot),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_DOOR_BELL));
>> +
>> + ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> +
>> + ? ? ? /* wait until the task management command is completed */
>> + ? ? ? err = wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(hba->tm_condition[free_slot] != 0),
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?60 * HZ);
>> + ? ? ? hba->tm_condition[free_slot] = 0xFF;
>> + ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? "Task management command timed-out\n");
>> + ? ? ? ? ? ? ? return err;
>> + ? ? ? }
>> + ? ? ? return ufshcd_task_req_compl(hba, free_slot);
>> +}
>> +
>> +/**
>> + * ufshcd_device_reset - reset device and abort all the pending commands
>> + * @cmd: SCSI command pointer
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_device_reset(struct scsi_cmnd *cmd)
>> +{
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? struct ufs_hba *hba;
>> + ? ? ? unsigned long flags;
>> + ? ? ? int reset_lun;
>> + ? ? ? unsigned int tag;
>> + ? ? ? u32 i;
>> + ? ? ? u32 pos; /* pos: represents a bit in a register */
>> + ? ? ? int err = -1;
>> +
>> + ? ? ? host = cmd->device->host;
>> + ? ? ? hba = (struct ufs_hba *)host->hostdata;
>> + ? ? ? tag = cmd->request->tag;
>> +
>> + ? ? ? if ((hba->lrb[tag].cmd == cmd))
>> + ? ? ? ? ? ? ? reset_lun = hba->lrb[tag].lun;
>> + ? ? ? else
>> + ? ? ? ? ? ? ? goto out;
>> +
>> + ? ? ? err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_LOGICAL_RESET);
>> + ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> + ? ? ? ? ? ? ? for (i = 0; i < hba->nutrs; i++) {
>> + ? ? ? ? ? ? ? ? ? ? ? pos = (1 << i);
>> + ? ? ? ? ? ? ? ? ? ? ? if ((pos & hba->outstanding_reqs) &&
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? (reset_lun == hba->lrb[i].lun)) {
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* clear the respective UTRLCLR bit */
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? writel(~pos, (UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TRANSFER_REQ_LIST_CLEAR));
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->outstanding_reqs ^= pos;
>> +
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (hba->lrb[i].cmd) {
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? scsi_dma_unmap(hba->lrb[i].cmd);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd->result =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DID_ABORT << 16;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd->scsi_done(cmd);
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->lrb[i].cmd = NULL;
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ? } /* end of for */
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? } /*end of if */
>> +out:
>> + ? ? ? return err;
>> +}
>> +
>> +/**
>> + * ufshcd_host_reset - Main reset function registered with scsi layer
>> + * @cmd: SCSI command pointer
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_host_reset(struct scsi_cmnd *cmd)
>> +{
>> + ? ? ? struct ufs_hba *hba = NULL;
>> +
>> + ? ? ? hba = (struct ufs_hba *) &cmd->device->host->hostdata[0];
>> +
>> + ? ? ? if (UFSHCD_STATE_RESET == hba->ufshcd_state)
>> + ? ? ? ? ? ? ? return 0;
>> +
>> + ? ? ? return ufshcd_do_reset(hba) ? -1 : 0;
>> +}
>> +
>> +/**
>> + * ufshcd_abort - abort a specific command
>> + * @cmd: SCSI command pointer
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_abort(struct scsi_cmnd *cmd)
>> +{
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? struct ufs_hba *hba;
>> + ? ? ? unsigned long flags;
>> + ? ? ? unsigned int tag;
>> + ? ? ? unsigned int pos;
>> + ? ? ? int err;
>> +
>> + ? ? ? host = cmd->device->host;
>> + ? ? ? hba = (struct ufs_hba *) host->hostdata;
>> + ? ? ? tag = cmd->request->tag;
>> +
>> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> + ? ? ? pos = (1 << tag);
>> +
>> + ? ? ? /* check if command is still pending */
>> + ? ? ? if (!(hba->outstanding_reqs & pos)) {
>> + ? ? ? ? ? ? ? err = -1;
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? ? ? ? ? goto out;
>> + ? ? ? }
>> +
>> + ? ? ? err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
>> + ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? spin_lock_irqsave(host->host_lock, flags);
>> + ? ? ? ? ? ? ? scsi_dma_unmap(cmd);
>> +
>> + ? ? ? ? ? ? ? /* clear the respective UTRLCLR bit */
>> + ? ? ? ? ? ? ? writel(~pos,
>> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_LIST_CLEAR));
>> + ? ? ? ? ? ? ? hba->outstanding_reqs &= ~pos;
>> + ? ? ? ? ? ? ? hba->lrb[tag].cmd = NULL;
>> + ? ? ? ? ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
>> + ? ? ? }
>> +out:
>> + ? ? ? return err;
>> +}
>> +
>> ?static struct scsi_host_template ufshcd_driver_template = {
>> ? ? ? ?.module ? ? ? ? ? ? ? ? = THIS_MODULE,
>> ? ? ? ?.name ? ? ? ? ? ? ? ? ? = UFSHCD,
>> @@ -1369,6 +1670,9 @@ static struct scsi_host_template ufshcd_driver_template = {
>> ? ? ? ?.queuecommand ? ? ? ? ? = ufshcd_queuecommand,
>> ? ? ? ?.slave_alloc ? ? ? ? ? ?= ufshcd_slave_alloc,
>> ? ? ? ?.slave_destroy ? ? ? ? ?= ufshcd_slave_destroy,
>> + ? ? ? .eh_abort_handler ? ? ? = ufshcd_abort,
>> + ? ? ? .eh_device_reset_handler = ufshcd_device_reset,
>> + ? ? ? .eh_host_reset_handler ?= ufshcd_host_reset,
>> ? ? ? ?.this_id ? ? ? ? ? ? ? ?= -1,
>> ? ? ? ?.sg_tablesize ? ? ? ? ? = SG_ALL,
>> ? ? ? ?.cmd_per_lun ? ? ? ? ? ?= UFSHCD_CMD_PER_LUN,
>> @@ -1483,6 +1787,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> ? ? ? ?struct Scsi_Host *host;
>> ? ? ? ?struct ufs_hba *hba;
>> ? ? ? ?int ufs_hba_len;
>> + ? ? ? int index;
>> ? ? ? ?int err;
>>
>> ? ? ? ?ufs_hba_len = sizeof(struct ufs_hba);
>> @@ -1547,6 +1852,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> ? ? ? ?host->unique_id = host->host_no;
>> ? ? ? ?host->max_cmd_len = MAX_CDB_SIZE;
>>
>> + ? ? ? /* Initailze wait queue for task management */
>> + ? ? ? init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
>> +
>> + ? ? ? /* Initially assign invalid value to tm_condition */
>> + ? ? ? for (index = 0; index < hba->nutmrs; index++)
>> + ? ? ? ? ? ? ? hba->tm_condition[index] = 0xFF;
>> +
>> ? ? ? ?/* Initialize work queues */
>> ? ? ? ?INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
>> ? ? ? ?INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
>> --
>> 1.7.5.4
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>> the body of a message to [email protected]
>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>> Please read the FAQ at ?http://www.tux.org/lkml/



--
~Santosh

2012-02-07 07:17:00

by Felipe Balbi

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

Hi,

On Thu, Feb 02, 2012 at 10:27:26AM +0530, Vinayak Holikatti wrote:
> +/**
> + * ufshcd_uic_cc_handler - handle UIC command completion
> + * @work: pointer to a work queue structure
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static void ufshcd_uic_cc_handler (struct work_struct *work)
> +{
> + struct ufs_hba *hba;
> +
> + hba = container_of(work, struct ufs_hba, uic_workq);
> +
> + if ((UIC_CMD_DME_LINK_STARTUP == hba->active_uic_cmd.command) &&

this is so annoying. Please invert all these constructs:

if ((hcd->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) ....
and so on.

> + !(ufshcd_get_uic_cmd_result(hba))) {
> +
> + if (ufshcd_make_hba_operational(hba))
> + dev_err(&hba->pdev->dev,
> + "cc: hba not operational state\n");
> + return;
> + }
> +}
> +
> +/**
> + * ufshcd_sl_intr - Interrupt service routine
> + * @hba: per adapter instance
> + * @intr_status: contains interrupts generated by the controller
> + */
> +static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
> +{
> + if (intr_status & UIC_COMMAND_COMPL)
> + schedule_work(&hba->uic_workq);
> +}
> +
> +/**
> + * ufshcd_intr - Main interrupt service routine
> + * @irq: irq number
> + * @__hba: pointer to adapter instance
> + *
> + * Returns IRQ_HANDLED - If interrupt is valid
> + * IRQ_NONE - If invalid interrupt
> + */
> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
> +{
> + unsigned long flags;
> + u32 intr_status;
> + irqreturn_t retval = IRQ_NONE;
> + struct ufs_hba *hba = __hba;
> +
> + spin_lock_irqsave(hba->host->host_lock, flags);
> + intr_status = readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS);
> +
> + if (intr_status) {
> + ufshcd_sl_intr(hba, intr_status);
> +
> + /* If UFSHCI 1.0 then clear interrupt status register */
> + if (UFSHCI_VERSION_10 == hba->ufs_version)
> + writel(intr_status,
> + (UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
> + retval = IRQ_HANDLED;
> + }
> + spin_unlock_irqrestore(hba->host->host_lock, flags);
> + return retval;
> +}
> +
> +static struct scsi_host_template ufshcd_driver_template = {
> + .module = THIS_MODULE,
> + .name = UFSHCD,
> + .proc_name = UFSHCD,
> + .this_id = -1,
> +};
> +
> +/**
> + * ufshcd_shutdown - main funciton to put the controller in reset state
> + * @pdev: pointer to PCI device handle
> + */
> +static void ufshcd_shutdown(struct pci_dev *pdev)
> +{
> + ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
> +}
> +
> +#ifdef CONFIG_PM
> +/**
> + * ufshcd_suspend - suspend power management function
> + * @pdev: pointer to PCI device handle
> + * @state: power state
> + *
> + * Returns -ENOSYS
> + */
> +static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> + return -ENOSYS;
> +}
> +
> +/**
> + * ufshcd_resume - resume power management function
> + * @pdev: pointer to PCI device handle
> + *
> + * Returns -ENOSYS
> + */
> +static int ufshcd_resume(struct pci_dev *pdev)
> +{
> + return -ENOSYS;
> +}
> +#endif /* CONFIG_PM */
> +
> +/**
> + * ufshcd_hba_free - free allocated memory for
> + * host memory space data structures
> + * @hba: per adapter instance
> + */
> +static void ufshcd_hba_free(struct ufs_hba *hba)
> +{
> + iounmap(UFSHCD_MMIO_BASE);
> + ufshcd_free_hba_memory(hba);
> + pci_release_regions(hba->pdev);
> +}
> +
> +/**
> + * ufshcd_remove - deallocate PCI/SCSI host and host memory space
> + * data structure memory
> + * @pdev - pointer to PCI handle
> + */
> +static void ufshcd_remove(struct pci_dev *pdev)
> +{
> + struct ufs_hba *hba = pci_get_drvdata(pdev);
> +
> + /* disable interrupts */
> + ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
> + free_irq(pdev->irq, hba);
> +
> + ufshcd_hba_stop(hba);
> + ufshcd_hba_free(hba);
> +
> + scsi_remove_host(hba->host);
> + scsi_host_put(hba->host);
> + pci_set_drvdata(pdev, NULL);
> + pci_clear_master(pdev);
> + pci_disable_device(pdev);
> +}
> +
> +/**
> + * ufshcd_set_dma_mask - Set dma addressing
> + * @pdev: PCI device struct
> + *
> + * Returns 0 for success, non-zero for failure
> + */
> +static int ufshcd_set_dma_mask(struct pci_dev *pdev)
> +{
> + int err;
> +
> + do {
> + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
> + if (!err) {
> + err = pci_set_consistent_dma_mask(pdev,
> + DMA_BIT_MASK(64));
> + break;
> + }
> + err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
> + if (!err)
> + err = pci_set_consistent_dma_mask(pdev,
> + DMA_BIT_MASK(32));
> + } while (0);
> +
> + return err;
> +}
> +
> +/**
> + * ufshcd_probe - probe routine of the driver
> + * @pdev: pointer to PCI device handle
> + * @id: PCI device id
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int __devinit
> +ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> + struct Scsi_Host *host;
> + struct ufs_hba *hba;
> + int ufs_hba_len;
> + int err;
> +
> + ufs_hba_len = sizeof(struct ufs_hba);
> + err = pci_enable_device(pdev);
> + if (err) {
> + dev_err(&pdev->dev, "pci_enable_device failed\n");
> + goto out_error;
> + }
> +
> + pci_set_master(pdev);
> +
> + host = scsi_host_alloc(&ufshcd_driver_template, ufs_hba_len);
> + if (!host) {
> + dev_err(&pdev->dev, "scsi_host_alloc failed\n");
> + err = -ENOMEM;
> + goto out_disable;
> + }
> + hba = (struct ufs_hba *)host->hostdata;
> +
> + err = pci_request_regions(pdev, UFSHCD);
> + if (err < 0) {
> + dev_err(&pdev->dev, "request regions failed\n");
> + goto out_disable;
> + }
> +
> + hba->mmio_base = pci_ioremap_bar(pdev, 0);
> + if (!hba->mmio_base) {
> + dev_err(&pdev->dev, "memory map failed\n");
> + err = -ENOMEM;
> + goto out_release_regions;
> + }
> +
> + err = ufshcd_set_dma_mask(pdev);
> + if (err) {
> + dev_err(&pdev->dev, "set dma mask failed\n");
> + goto out_iounmap;
> + }
> +
> + hba->host = host;
> + hba->pdev = pdev;
> +
> + /* Read capabilities registers */
> + ufshcd_hba_capabilities(hba);
> +
> + /* Get UFS version supported by the controller */
> + hba->ufs_version = ufshcd_get_ufs_version(hba);
> +
> + /* Allocate memory for host memory space */
> + err = ufshcd_memory_alloc(hba);
> + if (err) {
> + dev_err(&pdev->dev, "Memory allocation failed\n");
> + goto out_iounmap;
> + }
> +
> + /* Configure LRB */
> + ufshcd_host_memory_configure(hba);
> +
> + host->can_queue = hba->nutrs;
> + host->max_id = UFSHCD_MAX_ID;
> + host->max_lun = UFSHCD_MAX_LUNS;
> + host->max_channel = UFSHCD_MAX_CHANNEL;
> + host->unique_id = host->host_no;
> +
> + /* Initialize work queues */
> + INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
> +
> + /* IRQ registration */
> + err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);

make this a threaded IRQ and remove your workqueues and tasklets.

> + if (err) {
> + dev_err(&pdev->dev, "request irq failed\n");
> + goto out_lrb_free;
> + }
> +
> + pci_set_drvdata(pdev, hba);

would also be cool if you would abstract PCI out of this driver, so it's
easier to be re-used. See how I did it with drivers/usb/dwc3/core.c
drivers/usb/dwc3/dwc3-pci.c and drivers/usb/dwc3/dwc3-omap.c

--
balbi


Attachments:
(No filename) (6.91 kB)
signature.asc (836.00 B)
Digital signature
Download all attachments

2012-02-07 15:10:35

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On Tue, Feb 7, 2012 at 12:46 PM, Felipe Balbi <[email protected]> wrote:
> Hi,
>
> On Thu, Feb 02, 2012 at 10:27:26AM +0530, Vinayak Holikatti wrote:
>> +/**
>> + * ufshcd_uic_cc_handler - handle UIC command completion
>> + * @work: pointer to a work queue structure
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static void ufshcd_uic_cc_handler (struct work_struct *work)
>> +{
>> + ? ? struct ufs_hba *hba;
>> +
>> + ? ? hba = container_of(work, struct ufs_hba, uic_workq);
>> +
>> + ? ? if ((UIC_CMD_DME_LINK_STARTUP == hba->active_uic_cmd.command) &&
>
> this is so annoying. Please invert all these constructs:
>
> if ((hcd->active_uic_cmd.command == UIC_CMD_DME_LINK_STARTUP) ....
> and so on.
>
ok, I'll change it.

>> + ? ? ? ? !(ufshcd_get_uic_cmd_result(hba))) {
>> +
>> + ? ? ? ? ? ? if (ufshcd_make_hba_operational(hba))
>> + ? ? ? ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? "cc: hba not operational state\n");
>> + ? ? ? ? ? ? return;
>> + ? ? }
>> +}
>> +
>> +/**
>> + * ufshcd_sl_intr - Interrupt service routine
>> + * @hba: per adapter instance
>> + * @intr_status: contains interrupts generated by the controller
>> + */
>> +static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>> +{
>> + ? ? if (intr_status & UIC_COMMAND_COMPL)
>> + ? ? ? ? ? ? schedule_work(&hba->uic_workq);
>> +}
>> +
>> +/**
>> + * ufshcd_intr - Main interrupt service routine
>> + * @irq: irq number
>> + * @__hba: pointer to adapter instance
>> + *
>> + * Returns IRQ_HANDLED - If interrupt is valid
>> + * ? ? ? ? ? IRQ_NONE - If invalid interrupt
>> + */
>> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
>> +{
>> + ? ? unsigned long flags;
>> + ? ? u32 intr_status;
>> + ? ? irqreturn_t retval = IRQ_NONE;
>> + ? ? struct ufs_hba *hba = __hba;
>> +
>> + ? ? spin_lock_irqsave(hba->host->host_lock, flags);
>> + ? ? intr_status = readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS);
>> +
>> + ? ? if (intr_status) {
>> + ? ? ? ? ? ? ufshcd_sl_intr(hba, intr_status);
>> +
>> + ? ? ? ? ? ? /* If UFSHCI 1.0 then clear interrupt status register */
>> + ? ? ? ? ? ? if (UFSHCI_VERSION_10 == hba->ufs_version)
>> + ? ? ? ? ? ? ? ? ? ? writel(intr_status,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
>> + ? ? ? ? ? ? retval = IRQ_HANDLED;
>> + ? ? }
>> + ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
>> + ? ? return retval;
>> +}
>> +
>> +static struct scsi_host_template ufshcd_driver_template = {
>> + ? ? .module ? ? ? ? ? ? ? ? = THIS_MODULE,
>> + ? ? .name ? ? ? ? ? ? ? ? ? = UFSHCD,
>> + ? ? .proc_name ? ? ? ? ? ? ?= UFSHCD,
>> + ? ? .this_id ? ? ? ? ? ? ? ?= -1,
>> +};
>> +
>> +/**
>> + * ufshcd_shutdown - main funciton to put the controller in reset state
>> + * @pdev: pointer to PCI device handle
>> + */
>> +static void ufshcd_shutdown(struct pci_dev *pdev)
>> +{
>> + ? ? ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +/**
>> + * ufshcd_suspend - suspend power management function
>> + * @pdev: pointer to PCI device handle
>> + * @state: power state
>> + *
>> + * Returns -ENOSYS
>> + */
>> +static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
>> +{
>> + ? ? return -ENOSYS;
>> +}
>> +
>> +/**
>> + * ufshcd_resume - resume power management function
>> + * @pdev: pointer to PCI device handle
>> + *
>> + * Returns -ENOSYS
>> + */
>> +static int ufshcd_resume(struct pci_dev *pdev)
>> +{
>> + ? ? return -ENOSYS;
>> +}
>> +#endif /* CONFIG_PM */
>> +
>> +/**
>> + * ufshcd_hba_free - free allocated memory for
>> + * ? ? ? ? ? ? ? ? ? host memory space data structures
>> + * @hba: per adapter instance
>> + */
>> +static void ufshcd_hba_free(struct ufs_hba *hba)
>> +{
>> + ? ? iounmap(UFSHCD_MMIO_BASE);
>> + ? ? ufshcd_free_hba_memory(hba);
>> + ? ? pci_release_regions(hba->pdev);
>> +}
>> +
>> +/**
>> + * ufshcd_remove - deallocate PCI/SCSI host and host memory space
>> + * ? ? ? ? ? data structure memory
>> + * @pdev - pointer to PCI handle
>> + */
>> +static void ufshcd_remove(struct pci_dev *pdev)
>> +{
>> + ? ? struct ufs_hba *hba = pci_get_drvdata(pdev);
>> +
>> + ? ? /* disable interrupts */
>> + ? ? ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
>> + ? ? free_irq(pdev->irq, hba);
>> +
>> + ? ? ufshcd_hba_stop(hba);
>> + ? ? ufshcd_hba_free(hba);
>> +
>> + ? ? scsi_remove_host(hba->host);
>> + ? ? scsi_host_put(hba->host);
>> + ? ? pci_set_drvdata(pdev, NULL);
>> + ? ? pci_clear_master(pdev);
>> + ? ? pci_disable_device(pdev);
>> +}
>> +
>> +/**
>> + * ufshcd_set_dma_mask - Set dma addressing
>> + * @pdev: PCI device struct
>> + *
>> + * Returns 0 for success, non-zero for failure
>> + */
>> +static int ufshcd_set_dma_mask(struct pci_dev *pdev)
>> +{
>> + ? ? int err;
>> +
>> + ? ? do {
>> + ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
>> + ? ? ? ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(64));
>> + ? ? ? ? ? ? ? ? ? ? break;
>> + ? ? ? ? ? ? }
>> + ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
>> + ? ? ? ? ? ? if (!err)
>> + ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(32));
>> + ? ? } while (0);
>> +
>> + ? ? return err;
>> +}
>> +
>> +/**
>> + * ufshcd_probe - probe routine of the driver
>> + * @pdev: pointer to PCI device handle
>> + * @id: PCI device id
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int __devinit
>> +ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> +{
>> + ? ? struct Scsi_Host *host;
>> + ? ? struct ufs_hba *hba;
>> + ? ? int ufs_hba_len;
>> + ? ? int err;
>> +
>> + ? ? ufs_hba_len = sizeof(struct ufs_hba);
>> + ? ? err = pci_enable_device(pdev);
>> + ? ? if (err) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "pci_enable_device failed\n");
>> + ? ? ? ? ? ? goto out_error;
>> + ? ? }
>> +
>> + ? ? pci_set_master(pdev);
>> +
>> + ? ? host = scsi_host_alloc(&ufshcd_driver_template, ufs_hba_len);
>> + ? ? if (!host) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "scsi_host_alloc failed\n");
>> + ? ? ? ? ? ? err = -ENOMEM;
>> + ? ? ? ? ? ? goto out_disable;
>> + ? ? }
>> + ? ? hba = (struct ufs_hba *)host->hostdata;
>> +
>> + ? ? err = pci_request_regions(pdev, UFSHCD);
>> + ? ? if (err < 0) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "request regions failed\n");
>> + ? ? ? ? ? ? goto out_disable;
>> + ? ? }
>> +
>> + ? ? hba->mmio_base = pci_ioremap_bar(pdev, 0);
>> + ? ? if (!hba->mmio_base) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "memory map failed\n");
>> + ? ? ? ? ? ? err = -ENOMEM;
>> + ? ? ? ? ? ? goto out_release_regions;
>> + ? ? }
>> +
>> + ? ? err = ufshcd_set_dma_mask(pdev);
>> + ? ? if (err) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "set dma mask failed\n");
>> + ? ? ? ? ? ? goto out_iounmap;
>> + ? ? }
>> +
>> + ? ? hba->host = host;
>> + ? ? hba->pdev = pdev;
>> +
>> + ? ? /* Read capabilities registers */
>> + ? ? ufshcd_hba_capabilities(hba);
>> +
>> + ? ? /* Get UFS version supported by the controller */
>> + ? ? hba->ufs_version = ufshcd_get_ufs_version(hba);
>> +
>> + ? ? /* Allocate memory for host memory space */
>> + ? ? err = ufshcd_memory_alloc(hba);
>> + ? ? if (err) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "Memory allocation failed\n");
>> + ? ? ? ? ? ? goto out_iounmap;
>> + ? ? }
>> +
>> + ? ? /* Configure LRB */
>> + ? ? ufshcd_host_memory_configure(hba);
>> +
>> + ? ? host->can_queue = hba->nutrs;
>> + ? ? host->max_id = UFSHCD_MAX_ID;
>> + ? ? host->max_lun = UFSHCD_MAX_LUNS;
>> + ? ? host->max_channel = UFSHCD_MAX_CHANNEL;
>> + ? ? host->unique_id = host->host_no;
>> +
>> + ? ? /* Initialize work queues */
>> + ? ? INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
>> +
>> + ? ? /* IRQ registration */
>> + ? ? err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
>
> make this a threaded IRQ and remove your workqueues and tasklets.
>

ok, I'll look into it.

>> + ? ? if (err) {
>> + ? ? ? ? ? ? dev_err(&pdev->dev, "request irq failed\n");
>> + ? ? ? ? ? ? goto out_lrb_free;
>> + ? ? }
>> +
>> + ? ? pci_set_drvdata(pdev, hba);
>
> would also be cool if you would abstract PCI out of this driver, so it's
> easier to be re-used. See how I did it with drivers/usb/dwc3/core.c
> drivers/usb/dwc3/dwc3-pci.c and drivers/usb/dwc3/dwc3-omap.c
>

ok, we'll send it as a separate patch.

> --
> balbi

Thanks,
~Santosh

2012-02-08 19:48:59

by Girish K S

[permalink] [raw]
Subject: Re: [PATCH 2/4] [SCSI] ufshcd: UFS UTP Transfer requests handling

On 2 February 2012 10:27, Vinayak Holikatti <[email protected]> wrote:
> From: Santosh Yaraganavi <[email protected]>
>
> This patch adds support for Transfer request handling.
>
> ufshcd includes following implementations:
> ?- SCSI queuecommand
> ?- Compose UPIU(UFS Protocol information unit)
> ?- Issue commands to UFS host controller
> ?- Handle completed commands
>
> Signed-off-by: Santosh Yaraganavi <[email protected]>
> Signed-off-by: Vinayak Holikatti <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Reviewed-by: Saugata Das <[email protected]>
> Reviewed-by: Vishak G <[email protected]>
> Reviewed-by: Girish K S <[email protected]>
> ---
> ?drivers/scsi/ufs/ufshcd.c | ?447 +++++++++++++++++++++++++++++++++++++++++++++
> ?1 files changed, 447 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> index c82eeea..23d758b 100644
> --- a/drivers/scsi/ufs/ufshcd.c
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -62,6 +62,7 @@
> ?#include <scsi/scsi.h>
> ?#include <scsi/scsi_cmnd.h>
> ?#include <scsi/scsi_host.h>
> +#include <scsi/scsi_tcq.h>
> ?#include <scsi/scsi_dbg.h>
>
> ?#include "ufs.h"
> @@ -81,6 +82,7 @@ enum {
> ? ? ? ?UFSHCD_MAX_CHANNEL ? ? ?= 1,
> ? ? ? ?UFSHCD_MAX_ID ? ? ? ? ? = 1,
> ? ? ? ?UFSHCD_MAX_LUNS ? ? ? ? = 8,
> + ? ? ? UFSHCD_CMD_PER_LUN ? ? ?= 16,
> ? ? ? ?UFSHCD_CAN_QUEUE ? ? ? ?= 32,
> ? ? ? ?BYTES_128 ? ? ? ? ? ? ? = 128,
> ? ? ? ?BYTES_1024 ? ? ? ? ? ? ?= 1024
> @@ -146,6 +148,7 @@ struct uic_command {
> ?* @host: Scsi_Host instance of the driver
> ?* @pdev: PCI device handle
> ?* @lrb: local reference block
> + * @outstanding_reqs: Bits representing outstanding transfer requests
> ?* @capabilities: UFS Controller Capabilities
> ?* @nutrs: Transfer Request Queue depth supported by controller
> ?* @nutmrs: Task Management Queue depth supported by controller
> @@ -184,6 +187,8 @@ struct ufs_hba {
>
> ? ? ? ?struct ufshcd_lrb *lrb;
>
> + ? ? ? u32 outstanding_reqs;
> +
> ? ? ? ?u32 capabilities;
> ? ? ? ?int nutrs;
> ? ? ? ?int nutmrs;
> @@ -204,12 +209,28 @@ struct ufs_hba {
> ?* @ucd_cmd_ptr: UCD address of the command
> ?* @ucd_rsp_ptr: Response UPIU address for this command
> ?* @ucd_prdt_ptr: PRDT address of the command
> + * @cmd: pointer to scsi command
> + * @sense_buffer: pointer sense buffer address of the scsi command
> + * @sense_bufflen: Length of the sense buffer
> + * @scsi_status: SCSI status of the command
> + * @command_type: SCSI, UFS, Query.
> + * @task_tag: Task tag of the command
> + * @lun: LUN of the command
> ?*/
> ?struct ufshcd_lrb {
> ? ? ? ?struct utp_transfer_req_desc *utr_descriptor_ptr;
> ? ? ? ?struct utp_upiu_cmd *ucd_cmd_ptr;
> ? ? ? ?struct utp_upiu_rsp *ucd_rsp_ptr;
> ? ? ? ?struct ufshcd_sg_entry *ucd_prdt_ptr;
> +
> + ? ? ? struct scsi_cmnd *cmd;
> + ? ? ? u8 *sense_buffer;
> + ? ? ? unsigned int sense_bufflen;
> + ? ? ? int scsi_status;
> +
> + ? ? ? int command_type;
> + ? ? ? int task_tag;
> + ? ? ? int lun;
> ?};
>
> ?/**
> @@ -236,6 +257,18 @@ static inline int ufshcd_is_device_present(u32 reg_hcs)
> ?}
>
> ?/**
> + * ufshcd_get_tr_ocs - Get the UTRD Overall Command Status
> + * @lrb: pointer to local command reference block
> + *
> + * This function is used to get the OCS field from UTRD
> + * Returns the OCS field in the UTRD
> + */
> +static inline int ufshcd_get_tr_ocs(struct ufshcd_lrb *lrbp)
> +{
> + ? ? ? return lrbp->utr_descriptor_ptr->header.dword_2 & MASK_OCS;
> +}
> +
> +/**
> ?* ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
> ?* @reg: Register value of host controller status
> ?*
> @@ -303,6 +336,36 @@ static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
> ?}
>
> ?/**
> + * ufshcd_is_valid_req_rsp - checks if controller TR response is valid
> + * @ucd_rsp_ptr: pointer to response UPIU
> + *
> + * This function checks the response UPIU for valid transaction type in
> + * response field
> + * Returns 0 on success, non-zero on failure
> + */
> +static inline int
> +ufshcd_is_valid_req_rsp(struct utp_upiu_rsp *ucd_rsp_ptr)
> +{
> + ? ? ? return ((be32_to_cpu(ucd_rsp_ptr->header.dword_0) >> 24) ==
> + ? ? ? ? ? ? ? ?UPIU_TRANSACTION_RESPONSE) ? 0 :
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (DID_ERROR << 16 |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?COMMAND_COMPLETE << 8);
> +}
> +
> +/**
> + * ufshcd_get_rsp_upiu_result - Get the result from response UPIU
> + * @ucd_rsp_ptr: pointer to response UPIU
> + *
> + * This function gets the response status and scsi_status from response UPIU
> + * Returns the response result code.
> + */
> +static inline int
> +ufshcd_get_rsp_upiu_result(struct utp_upiu_rsp *ucd_rsp_ptr)
> +{
> + ? ? ? return be32_to_cpu(ucd_rsp_ptr->header.dword_1) & MASK_RSP_UPIU_RESULT;
> +}
> +
> +/**
> ?* ufshcd_config_int_aggr - Configure interrupt aggregation values
> ?* ? ? ? ? ? ? currently there is no use case where we want to configure
> ?* ? ? ? ? ? ? interrupt aggregation dynamically. So to configure interrupt
> @@ -342,6 +405,34 @@ static inline void ufshcd_hba_stop(struct ufs_hba *hba)
> ?}
>
> ?/**
> + * ufshcd_send_command - Send SCSI or device management commands
> + * @hba: per adapter instance
> + * @task_tag: Task tag of the command
> + */
> +static inline
> +void ufshcd_send_command(struct ufs_hba *hba, unsigned int task_tag)
> +{
> + ? ? ? hba->outstanding_reqs |= (1 << task_tag);
> + ? ? ? writel((1 << task_tag),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_DOOR_BELL));
> +}
> +
> +/**
> + * ufshcd_copy_sense_data - Copy sense data in case of check condition
> + * @lrb - pointer to local reference block
> + */
> +static inline void ufshcd_copy_sense_data(struct ufshcd_lrb *lrbp)
> +{
> + ? ? ? int len;
> + ? ? ? if (lrbp->sense_buffer != NULL) {
> + ? ? ? ? ? ? ? len = be16_to_cpu(lrbp->ucd_rsp_ptr->sense_data_len);
> + ? ? ? ? ? ? ? memcpy(lrbp->sense_buffer,
> + ? ? ? ? ? ? ? ? ? ? ? lrbp->ucd_rsp_ptr->sense_data,
> + ? ? ? ? ? ? ? ? ? ? ? min_t(int, len, SCSI_SENSE_BUFFERSIZE));
> + ? ? ? }
> +}
> +
> +/**
> ?* ufshcd_hba_capabilities - Read controller capabilities
> ?* @hba: per adapter instance
> ?*/
> @@ -385,6 +476,42 @@ ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
> ?}
>
> ?/**
> + * ufshcd_map_sg - Map scatter-gather list to prdt
> + * @lrbp - pointer to local reference block
> + */
> +static void ufshcd_map_sg(struct ufshcd_lrb *lrbp)
> +{
> + ? ? ? struct ufshcd_sg_entry *prd_table;
> + ? ? ? struct scatterlist *sg;
> + ? ? ? struct scsi_cmnd *cmd;
> + ? ? ? int sg_segments;
> + ? ? ? int i;
> +
> + ? ? ? cmd = lrbp->cmd;
> + ? ? ? sg_segments = scsi_dma_map(cmd);
> +
> + ? ? ? BUG_ON(sg_segments < 0);
> +
> + ? ? ? if (sg_segments) {
> + ? ? ? ? ? ? ? lrbp->utr_descriptor_ptr->prd_table_length =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16((u16) (sg_segments));
> +
> + ? ? ? ? ? ? ? prd_table = (struct ufshcd_sg_entry *)lrbp->ucd_prdt_ptr;
> +
> + ? ? ? ? ? ? ? scsi_for_each_sg(cmd, sg, sg_segments, i) {
> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].size ?=
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(((u32) sg_dma_len(sg))-1);
> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].base_addr =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(sg->dma_address);
> + ? ? ? ? ? ? ? ? ? ? ? prd_table[i].upper_addr =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32((sg->dma_address >> 32));
> + ? ? ? ? ? ? ? }
> + ? ? ? } else {
> + ? ? ? ? ? ? ? lrbp->utr_descriptor_ptr->prd_table_length = 0;
> + ? ? ? }
> +}
> +
> +/**
> ?* ufshcd_int_config - enable/disable interrupts
> ?* @hba: per adapter instance
> ?* @option: interrupt option
> @@ -410,6 +537,124 @@ static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
> ?}
>
> ?/**
> + * ufshcd_compose_upiu - form UFS Protocol Information Unit(UPIU)
> + * @hba: per adapter instance
> + * @lrb - pointer to local reference block
> + */
> +static void ufshcd_compose_upiu(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
> +{
> + ? ? ? struct utp_transfer_req_desc *req_desc;
> + ? ? ? struct utp_upiu_cmd *ucd_cmd_ptr;
> + ? ? ? u32 data_direction;
> + ? ? ? u32 upiu_flags;
> +
> + ? ? ? ucd_cmd_ptr = lrbp->ucd_cmd_ptr;
> + ? ? ? req_desc = lrbp->utr_descriptor_ptr;
> +
> + ? ? ? memset(ucd_cmd_ptr, 0, sizeof(struct utp_upiu_cmd));
> +
> + ? ? ? switch (lrbp->command_type) {
> + ? ? ? case UTP_CMD_TYPE_SCSI:
> + ? ? ? ? ? ? ? if (DMA_FROM_DEVICE == lrbp->cmd->sc_data_direction) {
> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_DEVICE_TO_HOST;
> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = UPIU_CMD_FLAGS_READ;

For a read operation the Spec mentions that flag.F bit should be set
to inform the target that current upiu is the final upiu.

> + ? ? ? ? ? ? ? } else if (DMA_TO_DEVICE == lrbp->cmd->sc_data_direction) {
> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_HOST_TO_DEVICE;
> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = UPIU_CMD_FLAGS_WRITE;

During write at some point of time flag.F bit has to be set to tell
the target about the last data out upiu.

> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? data_direction = UTP_NO_DATA_TRANSFER;
> + ? ? ? ? ? ? ? ? ? ? ? upiu_flags = 0;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? /*
> + ? ? ? ? ? ? ? ?* Transfer request desc header fields
> + ? ? ? ? ? ? ? ?* set interrupt bit if interrupt aggregation is disabled
> + ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? req_desc->header.dword_0 = cpu_to_le32(data_direction |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UTP_SCSI_COMMAND);
> +
> + ? ? ? ? ? ? ? /*
> + ? ? ? ? ? ? ? ?* assigning invalid value for command status. Controller
> + ? ? ? ? ? ? ? ?* updates OCS on command completion, with the command
> + ? ? ? ? ? ? ? ?* status
> + ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? req_desc->header.dword_2 =
> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
> +
> + ? ? ? ? ? ? ? /* command descriptor fields */
> + ? ? ? ? ? ? ? ucd_cmd_ptr->exp_data_transfer_len =
> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(lrbp->cmd->transfersize);

updating expected data transfer length can be ignored if flag.R/W is not set

> + ? ? ? ? ? ? ? memcpy(ucd_cmd_ptr->cdb, lrbp->cmd->cmnd,
> + ? ? ? ? ? ? ? ? ? ? (min_t(unsigned short,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?lrbp->cmd->cmd_len,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ?(unsigned short)MAX_CDB_SIZE)));

casting is unnecessary as the macro min_t itself will assign this
value to a unsigned integer

> +
> + ? ? ? ? ? ? ? ucd_cmd_ptr->header.dword_0 =
> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_COMMAND,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? upiu_flags,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->lun,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrbp->task_tag));
> + ? ? ? ? ? ? ? ucd_cmd_ptr->header.dword_1 =
> + ? ? ? ? ? ? ? ? ? ? ? cpu_to_be32(
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? UPIU_HEADER_DWORD(UPIU_COMMAND_SET_TYPE_SCSI,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0));

the macro UPIU_COMMAND_SET_TYPE_SCSI is unnecessary. the
lrbp->command_type can be used. One can be deleted
> +
> + ? ? ? ? ? ? ? /* set command response to zero */
> + ? ? ? ? ? ? ? memset(lrbp->ucd_rsp_ptr, 0, sizeof(struct utp_upiu_rsp));

The break is missing here. crucial once the below case is populated
with valid dev management info

> + ? ? ? case UTP_CMD_TYPE_DEV_MANAGE:
> + ? ? ? ? ? ? ? /* For query function implementation */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case UTP_CMD_TYPE_UFS:
> + ? ? ? ? ? ? ? /* For UFS native command implementation */
> + ? ? ? ? ? ? ? break;
> + ? ? ? } /* end of switch, command_type */
> +}
> +
> +/**
> + * ufshcd_queuecommand - main entry point for SCSI requests
> + * @cmd: command from SCSI Midlayer
> + * @done: call back function
> + *
> + * Retruns 0 for success, SCSI Midlayer specific error in case of failure
> + */
> +static int ufshcd_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
> +{
> + ? ? ? struct ufshcd_lrb *lrbp;
> + ? ? ? struct ufs_hba *hba;
> + ? ? ? unsigned long flags;
> + ? ? ? int tag;
> +
> + ? ? ? hba = (struct ufs_hba *) &host->hostdata[0];
> +
> + ? ? ? tag = cmd->request->tag;
> +
> + ? ? ? if (UFSHCD_STATE_OPERATIONAL != hba->ufshcd_state)
> + ? ? ? ? ? ? ? return SCSI_MLQUEUE_HOST_BUSY;
> +
> + ? ? ? lrbp = &hba->lrb[tag];
> +
> + ? ? ? lrbp->cmd = cmd;
> + ? ? ? lrbp->sense_bufflen = SCSI_SENSE_BUFFERSIZE;
> + ? ? ? lrbp->sense_buffer = cmd->sense_buffer;
> + ? ? ? lrbp->task_tag = tag;
> + ? ? ? lrbp->lun = cmd->device->lun;
> +
> + ? ? ? lrbp->command_type = UTP_CMD_TYPE_SCSI;
> +
> + ? ? ? /* form UPIU before issuing the command */
> + ? ? ? ufshcd_compose_upiu(hba, lrbp);
> + ? ? ? ufshcd_map_sg(lrbp);
> +
> + ? ? ? /* issue command to the controller */
> + ? ? ? spin_lock_irqsave(host->host_lock, flags);
> + ? ? ? ufshcd_send_command(hba, tag);
> + ? ? ? spin_unlock_irqrestore(host->host_lock, flags);
> + ? ? ? return 0;
> +}
> +
> +/**
> ?* ufshcd_memory_alloc - allocate memory for host memory space data structures
> ?* @hba: per adapter instance
> ?*
> @@ -680,6 +925,7 @@ static int ufshcd_make_hba_operational(struct ufs_hba *hba)
> ? ? ? ?ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
>
> ? ? ? ?hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
> + ? ? ? scsi_scan_host(hba->host);
>
> ? ? ? ?return 0;
> ?}
> @@ -763,6 +1009,190 @@ static int ufshcd_initialize_hba(struct ufs_hba *hba)
> ?}
>
> ?/**
> + * ufshcd_slave_alloc - handle initial scsi devie configurations
> + * @sdev: pointer to scsi device
> + *
> + * Returns success
> + */
> +static int ufshcd_slave_alloc(struct scsi_device *sdev)
> +{
> + ? ? ? struct ufs_hba *hba;
> +
> + ? ? ? hba = (struct ufs_hba *)sdev->host->hostdata;
> + ? ? ? sdev->tagged_supported = 1;
> + ? ? ? scsi_set_tag_type(sdev, MSG_SIMPLE_TAG);
> + ? ? ? scsi_activate_tcq(sdev, hba->nutrs);
> +
> + ? ? ? return 0;
> +}
> +
> +/**
> + * ufshcd_slave_destroy - remove scsi device configurations
> + * @sdev: pointer to scsi device
> + */
> +static void ufshcd_slave_destroy(struct scsi_device *sdev)
> +{
> + ? ? ? struct ufs_hba *hba;
> +
> + ? ? ? hba = (struct ufs_hba *)sdev->host->hostdata;
> + ? ? ? scsi_deactivate_tcq(sdev, hba->nutrs);
> +}
> +
> +/**
> + * ufshcd_scsi_cmd_status - Update SCSI command result based on scsi status
> + * @lrb: pointer to local reference block of completed command
> + * @scsi_status: SCSI command status
> + *
> + * Returns value base on SCSI command status
> + */
> +static inline int
> +ufshcd_scsi_cmd_status(struct ufshcd_lrb *lrbp, int scsi_status)
> +{
> + ? ? ? int result = 0;
> +
> + ? ? ? switch (scsi_status) {
> + ? ? ? case SAM_STAT_GOOD:
> + ? ? ? ? ? ? ? result |= DID_OK << 16 |
> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_GOOD;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case SAM_STAT_CHECK_CONDITION:
> + ? ? ? ? ? ? ? result |= DRIVER_SENSE << 24 |
> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_CHECK_CONDITION;
> + ? ? ? ? ? ? ? ufshcd_copy_sense_data(lrbp);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case SAM_STAT_BUSY:
> + ? ? ? ? ? ? ? result |= DID_BUS_BUSY << 16 |
> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_BUSY;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case SAM_STAT_TASK_SET_FULL:
> + ? ? ? ? ? ? ? result |= DID_OK << 16 |
> + ? ? ? ? ? ? ? ? ? ? ? ? COMMAND_COMPLETE << 8 |
> + ? ? ? ? ? ? ? ? ? ? ? ? SAM_STAT_TASK_SET_FULL;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case SAM_STAT_TASK_ABORTED:
> + ? ? ? ? ? ? ? result |= ?DID_ABORT << 16 |
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ABORT_TASK << 8 |
> + ? ? ? ? ? ? ? ? ? ? ? ? ?SAM_STAT_TASK_ABORTED;
> + ? ? ? default:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16;
> + ? ? ? ? ? ? ? break;
> + ? ? ? } /* end of switch */
> +
> + ? ? ? return result;
> +}
> +
> +/**
> + * ufshcd_transfer_rsp_status - Get overall status of the response
> + * @lrb: pointer to local reference block of completed command
> + *
> + * Returns result of the command to notify SCSI midlayer
> + */
> +static inline int ufshcd_transfer_rsp_status(struct ufshcd_lrb *lrbp)
> +{
> + ? ? ? int result = 0;
> + ? ? ? int scsi_status;
> + ? ? ? int ocs;
> +
> + ? ? ? /* overall command status of utrd */
> + ? ? ? ocs = ufshcd_get_tr_ocs(lrbp);
> +
> + ? ? ? switch (ocs) {
> + ? ? ? case OCS_SUCCESS:
> +
> + ? ? ? ? ? ? ? /* check if the returned transfer response is valid */
> + ? ? ? ? ? ? ? result = ufshcd_is_valid_req_rsp(lrbp->ucd_rsp_ptr);
> + ? ? ? ? ? ? ? if (result) {
> + ? ? ? ? ? ? ? ? ? ? ? pr_err(UFSHCD ":Invalid Response\n");
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? /*
> + ? ? ? ? ? ? ? ?* get the response UPIU result to extract
> + ? ? ? ? ? ? ? ?* the SCSI command status
> + ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? result = ufshcd_get_rsp_upiu_result(lrbp->ucd_rsp_ptr);
> +
> + ? ? ? ? ? ? ? /*
> + ? ? ? ? ? ? ? ?* get the result based on SCSI status response
> + ? ? ? ? ? ? ? ?* to notify the SCSI midlayer of the command status
> + ? ? ? ? ? ? ? ?*/
> + ? ? ? ? ? ? ? scsi_status = result & MASK_SCSI_STATUS;
> + ? ? ? ? ? ? ? result = ufshcd_scsi_cmd_status(lrbp, scsi_status);
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_INVALID_CMD_TABLE_ATTR:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_INVALID_PRDT_ATTR:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_MISMATCH_DATA_BUF_SIZE:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_MISMATCH_RESP_UPIU_SIZE:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_PEER_COMM_FAILURE:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_ABORTED:
> + ? ? ? ? ? ? ? result |= DID_ABORT << 16 | ABORT_TASK << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? case OCS_FATAL_ERROR:
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? /* should not come here */
> + ? ? ? ? ? ? ? result |= DID_ERROR << 16 | COMMAND_COMPLETE << 8;
> + ? ? ? ? ? ? ? pr_err(UFSHCD ":Invalid OCS from controller\n");
> + ? ? ? ? ? ? ? break;
> + ? ? ? } /* end of switch */
> +
> + ? ? ? return result;
> +}
> +
> +/**
> + * ufshcd_transfer_req_compl - handle SCSI and query command completion
> + * @hba: per adapter instance
> + */
> +static void ufshcd_transfer_req_compl(struct ufs_hba *hba)
> +{
> + ? ? ? struct ufshcd_lrb *lrb;
> + ? ? ? u32 completed_reqs;
> + ? ? ? u32 tr_doorbell;
> + ? ? ? int result;
> + ? ? ? int index;
> +
> + ? ? ? lrb = hba->lrb;
> + ? ? ? tr_doorbell =
> + ? ? ? ? ? ? ? readl(hba->mmio_base + REG_UTP_TRANSFER_REQ_DOOR_BELL);
> + ? ? ? completed_reqs = tr_doorbell ^ hba->outstanding_reqs;
> +
> + ? ? ? /* clear completed commands from outstanding_reqs */
> + ? ? ? hba->outstanding_reqs ^= completed_reqs;
> +
> + ? ? ? for (index = 0; index < hba->nutrs; index++) {
> + ? ? ? ? ? ? ? if (completed_reqs & (1 << index)) {
> +
> + ? ? ? ? ? ? ? ? ? ? ? result = ufshcd_transfer_rsp_status(&lrb[index]);
> +
> + ? ? ? ? ? ? ? ? ? ? ? if (NULL != lrb[index].cmd) {
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? scsi_dma_unmap(lrb[index].cmd);
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd->result = result;
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd->scsi_done(lrb[index].cmd);
> +
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* Mark completed command as NULL in LRB */
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? lrb[index].cmd = NULL;
> + ? ? ? ? ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? } /* end of if */
> + ? ? ? } /* end of for */
> +
> + ? ? ? /* Reset interrupt aggregation counters */
> + ? ? ? ufshcd_config_int_aggr(hba, INT_AGGR_RESET);
> +}
> +
> +/**
> ?* ufshcd_uic_cc_handler - handle UIC command completion
> ?* @work: pointer to a work queue structure
> ?*
> @@ -793,6 +1223,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
> ?{
> ? ? ? ?if (intr_status & UIC_COMMAND_COMPL)
> ? ? ? ? ? ? ? ?schedule_work(&hba->uic_workq);
> +
> + ? ? ? if (intr_status & UTP_TRANSFER_REQ_COMPL)
> + ? ? ? ? ? ? ? ufshcd_transfer_req_compl(hba);
> ?}
>
> ?/**
> @@ -830,7 +1263,13 @@ static struct scsi_host_template ufshcd_driver_template = {
> ? ? ? ?.module ? ? ? ? ? ? ? ? = THIS_MODULE,
> ? ? ? ?.name ? ? ? ? ? ? ? ? ? = UFSHCD,
> ? ? ? ?.proc_name ? ? ? ? ? ? ?= UFSHCD,
> + ? ? ? .queuecommand ? ? ? ? ? = ufshcd_queuecommand,
> + ? ? ? .slave_alloc ? ? ? ? ? ?= ufshcd_slave_alloc,
> + ? ? ? .slave_destroy ? ? ? ? ?= ufshcd_slave_destroy,
> ? ? ? ?.this_id ? ? ? ? ? ? ? ?= -1,
> + ? ? ? .sg_tablesize ? ? ? ? ? = SG_ALL,
> + ? ? ? .cmd_per_lun ? ? ? ? ? ?= UFSHCD_CMD_PER_LUN,
> + ? ? ? .can_queue ? ? ? ? ? ? ?= UFSHCD_CAN_QUEUE
> ?};
>
> ?/**
> @@ -1003,6 +1442,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> ? ? ? ?host->max_lun = UFSHCD_MAX_LUNS;
> ? ? ? ?host->max_channel = UFSHCD_MAX_CHANNEL;
> ? ? ? ?host->unique_id = host->host_no;
> + ? ? ? host->max_cmd_len = MAX_CDB_SIZE;
>
> ? ? ? ?/* Initialize work queues */
> ? ? ? ?INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
> @@ -1014,6 +1454,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> ? ? ? ? ? ? ? ?goto out_lrb_free;
> ? ? ? ?}
>
> + ? ? ? /* Enable scsi tag mapping */
> + ? ? ? err = scsi_init_shared_tag_map(host, host->can_queue);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "init shared queue failed\n");
> + ? ? ? ? ? ? ? goto out_free_irq;
> + ? ? ? }
> +
> ? ? ? ?pci_set_drvdata(pdev, hba);
>
> ? ? ? ?err = scsi_add_host(host, &pdev->dev);
> --
> 1.7.5.4
>

2012-02-09 04:34:22

by Namjae Jeon

[permalink] [raw]
Subject: Re: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

2012/2/6 Santosh Y <[email protected]>:
> On Mon, Feb 6, 2012 at 4:15 AM, Namjae Jeon <[email protected]> wrote:
>> 2012/2/5 Namjae Jeon <[email protected]>:
>>> 2012/2/2 Vinayak Holikatti <[email protected]>:
>>>> From: Santosh Yaraganavi <[email protected]>
>>>>
>>>> UFS is designed to be the most advanced specification for
>>>> both embedded and removable flash memory-based storage in mobile devices
>>>> such as smart phones and tablet computers.  The UFS standard represents
>>>> an evolutionary progression of JEDEC standards in this field, and has been
>>>> specifically tailored for mobile applications and computing systems requiring
>>>> high performance and low power consumption.  The initial data throughput for
>>>> UFS will be ~300 megabytes per second (MB/s), and the standard also supports
>>>> command queuing features to raise random read/write speeds.
>>>>
>>>> To achieve the highest performance and most power efficient data
>>>> transport, UFS uses the leading industry interface standards to form its
>>>> Interconnect Layer: MIPI® Alliance’s M-PHY and UniProSM  specifications.
>>>> UniPro is a comprehensive specification meant to act as a universal
>>>> chip-to-chip protocol, providing a common tunnel for other protocols.
>>>> The M-PHY interface is designed as the primary physical interface (PHY layer)
>>>> for the UniPro specification, and is a high speed serial interface targeting
>>>> up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
>>>> per lane.
>>>>
>>>> MIPI’s M-PHY and UniPro specifications are optimized for mobile applications,
>>>> and are designed from the ground up for efficient power management in mobile
>>>> devices, including enabling efficient transitions between the active and power
>>>> save modes. Combined with a low active power level and a near-zero idle power
>>>> level, UFS offers the promise for significant reductions in device power
>>>> consumption.
>>>>
>>>> The UFS standard adopts the well-known SCSI Architecture Model and command
>>>> protocols supporting multiple commands with command queuing features and
>>>> enabling a multi-thread programming paradigm. This differs from conventional
>>>> flash-based memory cards and embedded flash solutions which process one
>>>> command at a time, limiting random read/write access performance.
>>>> In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
>>>> specification will allow system designers greater flexibility by simplifying
>>>> the involvement of the host processor in the operation of the flash storage
>>>> subsystem. The UFS HCI specification and the adoption of SCSI will provide
>>>> a well-known software programming model and enable wider market adoption.
>>>>
>>>> This patchset contains PCIe based UFS host controller driver which complies
>>>> to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
>>>> The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).
>>>>
>>>> This patch set is successfully applied on kernel version 3.3-rc2.
>>>>
>>>> Santosh Yaraganavi (4):
>>>>  [SCSI] ufshcd: UFS Host controller driver
>>>>  [SCSI] ufshcd: UFS UTP Transfer requests handling
>>>>  [SCSI] ufshcd: UFSHCI error handling
>>>>  [SCSI] ufshcd: SCSI error handling
>>>>
>>>>  drivers/scsi/Kconfig      |    1 +
>>>>  drivers/scsi/Makefile     |    1 +
>>>>  drivers/scsi/ufs/Kconfig  |   49 ++
>>>>  drivers/scsi/ufs/Makefile |    2 +
>>>>  drivers/scsi/ufs/ufs.h    |  203 +++++
>>>>  drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
>>>>  drivers/scsi/ufs/ufshci.h |  360 +++++++++
>>>>  7 files changed, 2570 insertions(+), 0 deletions(-)
>>>>  create mode 100644 drivers/scsi/ufs/Kconfig
>>>>  create mode 100644 drivers/scsi/ufs/Makefile
>>>>  create mode 100644 drivers/scsi/ufs/ufs.h
>>>>  create mode 100644 drivers/scsi/ufs/ufshcd.c
>>>>  create mode 100644 drivers/scsi/ufs/ufshci.h
>>>>
>>>> --
>>>> 1.7.5.4
>>> Hi.
>>> I have been waiting for ufs contribution.
>>> Unfortunately I don't have real target supported ufs(maybe only you
>>> have it). I can not debug and run this code, So just review only code
>>> with specification.
>>>>
>> Hi. Santosh.
>> Would you share performance measurement result (read/write seq,ran) ?
>> Thanks.
>
> Hi Namjae,
>
> Currently UFS Driver is being tested on on UFS Controller FPGA and UFS
> Device FPGA. So currently the main concentration is on functionality.
> Performance on FPGA will not be the correct measurement.
Hi. Santosh.
If possible, I hope that you share performance to us when you post new
patch next time.
And Personally I am expecting good random I/O speed on UFS.
Thanks.
>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>>>> the body of a message to [email protected]
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>> Please read the FAQ at  http://www.tux.org/lkml/
>
> --
> ~Santosh

2012-02-09 07:54:09

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 0/4] [SCSI] ufshcd: UFS Host Controller Driver

On Thu, Feb 9, 2012 at 10:04 AM, Namjae Jeon <[email protected]> wrote:
> 2012/2/6 Santosh Y <[email protected]>:
>> On Mon, Feb 6, 2012 at 4:15 AM, Namjae Jeon <[email protected]> wrote:
>>> 2012/2/5 Namjae Jeon <[email protected]>:
>>>> 2012/2/2 Vinayak Holikatti <[email protected]>:
>>>>> From: Santosh Yaraganavi <[email protected]>
>>>>>
>>>>> UFS is designed to be the most advanced specification for
>>>>> both embedded and removable flash memory-based storage in mobile devices
>>>>> such as smart phones and tablet computers. ?The UFS standard represents
>>>>> an evolutionary progression of JEDEC standards in this field, and has been
>>>>> specifically tailored for mobile applications and computing systems requiring
>>>>> high performance and low power consumption. ?The initial data throughput for
>>>>> UFS will be ~300 megabytes per second (MB/s), and the standard also supports
>>>>> command queuing features to raise random read/write speeds.
>>>>>
>>>>> To achieve the highest performance and most power efficient data
>>>>> transport, UFS uses the leading industry interface standards to form its
>>>>> Interconnect Layer: MIPI? Alliance?s M-PHY and UniProSM ?specifications.
>>>>> UniPro is a comprehensive specification meant to act as a universal
>>>>> chip-to-chip protocol, providing a common tunnel for other protocols.
>>>>> The M-PHY interface is designed as the primary physical interface (PHY layer)
>>>>> for the UniPro specification, and is a high speed serial interface targeting
>>>>> up to 2.9 gigabits per second (Gbps) per lane with up-scalability to 5.8Gbps
>>>>> per lane.
>>>>>
>>>>> MIPI?s M-PHY and UniPro specifications are optimized for mobile applications,
>>>>> and are designed from the ground up for efficient power management in mobile
>>>>> devices, including enabling efficient transitions between the active and power
>>>>> save modes. Combined with a low active power level and a near-zero idle power
>>>>> level, UFS offers the promise for significant reductions in device power
>>>>> consumption.
>>>>>
>>>>> The UFS standard adopts the well-known SCSI Architecture Model and command
>>>>> protocols supporting multiple commands with command queuing features and
>>>>> enabling a multi-thread programming paradigm. This differs from conventional
>>>>> flash-based memory cards and embedded flash solutions which process one
>>>>> command at a time, limiting random read/write access performance.
>>>>> In addition, a forthcoming complementary UFS Host Controller Interface (HCI)
>>>>> specification will allow system designers greater flexibility by simplifying
>>>>> the involvement of the host processor in the operation of the flash storage
>>>>> subsystem. The UFS HCI specification and the adoption of SCSI will provide
>>>>> a well-known software programming model and enable wider market adoption.
>>>>>
>>>>> This patchset contains PCIe based UFS host controller driver which complies
>>>>> to UFSHCI 1.0 and 1.1. The driver is based on Linux SCSI framework.
>>>>> The driver is tested with UFS Host controller(FPGA) and UFS device(FPGA).
>>>>>
>>>>> This patch set is successfully applied on kernel version 3.3-rc2.
>>>>>
>>>>> Santosh Yaraganavi (4):
>>>>> ?[SCSI] ufshcd: UFS Host controller driver
>>>>> ?[SCSI] ufshcd: UFS UTP Transfer requests handling
>>>>> ?[SCSI] ufshcd: UFSHCI error handling
>>>>> ?[SCSI] ufshcd: SCSI error handling
>>>>>
>>>>> ?drivers/scsi/Kconfig ? ? ?| ? ?1 +
>>>>> ?drivers/scsi/Makefile ? ? | ? ?1 +
>>>>> ?drivers/scsi/ufs/Kconfig ?| ? 49 ++
>>>>> ?drivers/scsi/ufs/Makefile | ? ?2 +
>>>>> ?drivers/scsi/ufs/ufs.h ? ?| ?203 +++++
>>>>> ?drivers/scsi/ufs/ufshcd.c | 1954 +++++++++++++++++++++++++++++++++++++++++++++
>>>>> ?drivers/scsi/ufs/ufshci.h | ?360 +++++++++
>>>>> ?7 files changed, 2570 insertions(+), 0 deletions(-)
>>>>> ?create mode 100644 drivers/scsi/ufs/Kconfig
>>>>> ?create mode 100644 drivers/scsi/ufs/Makefile
>>>>> ?create mode 100644 drivers/scsi/ufs/ufs.h
>>>>> ?create mode 100644 drivers/scsi/ufs/ufshcd.c
>>>>> ?create mode 100644 drivers/scsi/ufs/ufshci.h
>>>>>
>>>>> --
>>>>> 1.7.5.4
>>>> Hi.
>>>> I have been waiting for ufs contribution.
>>>> Unfortunately I don't have real target supported ufs(maybe only you
>>>> have it). I can not debug and run this code, So just review only code
>>>> with specification.
>>>>>
>>> Hi. Santosh.
>>> Would you share performance measurement result (read/write seq,ran) ?
>>> Thanks.
>>
>> Hi Namjae,
>>
>> Currently UFS Driver is being tested on on UFS Controller FPGA and UFS
>> Device FPGA. So currently the main concentration is on functionality.
>> Performance on FPGA will not be the correct measurement.
> Hi. Santosh.
> If possible, I hope that you share performance to us when you post new
> patch next time.
> And Personally I am expecting good random I/O speed on UFS.
> Thanks.

Hi Namjae,
As soon as the hardware is available, we'll share the performance results.

>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
>>>>> the body of a message to [email protected]
>>>>> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>>>>> Please read the FAQ at ?http://www.tux.org/lkml/
>>
>> --
>> ~Santosh



--
~Santosh

2012-02-09 19:15:45

by Girish K S

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On 2 February 2012 10:27, Vinayak Holikatti <[email protected]> wrote:
> From: Santosh Yaraganavi <[email protected]>
>
> This patch adds support for Universal Flash Storage(UFS)
> host controllers. The UFS host controller driver
> includes host controller initialization method.
>
> The Initialization process involves following steps:
> ?- Initiate UFS Host Controller initialization process by writing
> ? to Host controller enable register
> ?- Configure UFS Host controller registers with host memory space
> ? datastructure offsets.
> ?- Unipro link startup procedure
> ?- Check for connected device
> ?- Configure UFS host controller to process requests
> ?- Enable required interrupts
> ?- Configure interrupt aggregation
>
> Signed-off-by: Santosh Yaraganavi <[email protected]>
> Signed-off-by: Vinayak Holikatti <[email protected]>
> Reviewed-by: Arnd Bergmann <[email protected]>
> Reviewed-by: Saugata Das <[email protected]>
> Reviewed-by: Vishak G <[email protected]>
> Reviewed-by: Girish K S <[email protected]>
> ---
> ?drivers/scsi/Kconfig ? ? ?| ? ?1 +
> ?drivers/scsi/Makefile ? ? | ? ?1 +
> ?drivers/scsi/ufs/Kconfig ?| ? 49 ++
> ?drivers/scsi/ufs/Makefile | ? ?2 +
> ?drivers/scsi/ufs/ufs.h ? ?| ?203 +++++++++
> ?drivers/scsi/ufs/ufshcd.c | 1091 +++++++++++++++++++++++++++++++++++++++++++++
> ?drivers/scsi/ufs/ufshci.h | ?360 +++++++++++++++
> ?7 files changed, 1707 insertions(+), 0 deletions(-)
> ?create mode 100644 drivers/scsi/ufs/Kconfig
> ?create mode 100644 drivers/scsi/ufs/Makefile
> ?create mode 100644 drivers/scsi/ufs/ufs.h
> ?create mode 100644 drivers/scsi/ufs/ufshcd.c
> ?create mode 100644 drivers/scsi/ufs/ufshci.h
>
> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
> index 16570aa..477a91a 100644
> --- a/drivers/scsi/Kconfig
> +++ b/drivers/scsi/Kconfig
> @@ -619,6 +619,7 @@ config SCSI_ARCMSR
>
> ?source "drivers/scsi/megaraid/Kconfig.megaraid"
> ?source "drivers/scsi/mpt2sas/Kconfig"
> +source "drivers/scsi/ufs/Kconfig"
>
> ?config SCSI_HPTIOP
> ? ? ? ?tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
> diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
> index 2b88749..c832974 100644
> --- a/drivers/scsi/Makefile
> +++ b/drivers/scsi/Makefile
> @@ -108,6 +108,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) ? ? ? += megaraid.o
> ?obj-$(CONFIG_MEGARAID_NEWGEN) ?+= megaraid/
> ?obj-$(CONFIG_MEGARAID_SAS) ? ? += megaraid/
> ?obj-$(CONFIG_SCSI_MPT2SAS) ? ? += mpt2sas/
> +obj-$(CONFIG_SCSI_UFSHCD) ? ? ?+= ufs/
> ?obj-$(CONFIG_SCSI_ACARD) ? ? ? += atp870u.o
> ?obj-$(CONFIG_SCSI_SUNESP) ? ? ?+= esp_scsi.o ? sun_esp.o
> ?obj-$(CONFIG_SCSI_GDTH) ? ? ? ? ? ? ? ?+= gdth.o
> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
> new file mode 100644
> index 0000000..8f27f9d
> --- /dev/null
> +++ b/drivers/scsi/ufs/Kconfig
> @@ -0,0 +1,49 @@
> +#
> +# Kernel configuration file for the UFS Host Controller
> +#
> +# This code is based on drivers/scsi/ufs/Kconfig
> +# Copyright (C) 2011 ?Samsung Samsung India Software Operations
> +#
> +# Santosh Yaraganavi <[email protected]>
> +# Vinayak Holikatti <[email protected]>
> +
> +# This program is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU General Public License
> +# as published by the Free Software Foundation; either version 2
> +# of the License, or (at your option) any later version.
> +
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
> +# GNU General Public License for more details.
> +
> +# NO WARRANTY
> +# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
> +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
> +# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
> +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
> +# solely responsible for determining the appropriateness of using and
> +# distributing the Program and assumes all risks associated with its
> +# exercise of rights under this Agreement, including but not limited to
> +# the risks and costs of program errors, damage to or loss of data,
> +# programs or equipment, and unavailability or interruption of operations.
> +
> +# DISCLAIMER OF LIABILITY
> +# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
> +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> +# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
> +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
> +# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
> +# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
> +
> +# You should have received a copy of the GNU General Public License
> +# along with this program; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
> +# USA.
> +
> +config SCSI_UFSHCD
> + ? ? ? tristate "Universal Flash Storage host controller driver"
> + ? ? ? depends on PCI && SCSI
> + ? ? ? ---help---
> + ? ? ? This is a generic driver which supports PCIe UFS Host controllers.
> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
> new file mode 100644
> index 0000000..adf7895
> --- /dev/null
> +++ b/drivers/scsi/ufs/Makefile
> @@ -0,0 +1,2 @@
> +# UFSHCD makefile
> +obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
> diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
> new file mode 100644
> index 0000000..96b5cae
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufs.h
> @@ -0,0 +1,203 @@
> +/*
> + * Universal Flash Storage Host controller driver
> + *
> + * This code is based on drivers/scsi/ufs/ufs.h
> + * Copyright (C) 2011-2012 Samsung India Software Operations
> + *
> + * Santosh Yaraganavi <[email protected]>
> + * Vinayak Holikatti <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
> + * GNU General Public License for more details.
> + *
> + * NO WARRANTY
> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
> + * solely responsible for determining the appropriateness of using and
> + * distributing the Program and assumes all risks associated with its
> + * exercise of rights under this Agreement, including but not limited to
> + * the risks and costs of program errors, damage to or loss of data,
> + * programs or equipment, and unavailability or interruption of operations.
> +
> + * DISCLAIMER OF LIABILITY
> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
> + * USA.
> + */
> +
> +#ifndef _UFS_H
> +#define _UFS_H
> +
> +#define TASK_REQ_UPIU_SIZE_DWORDS ? ? ?8
> +#define TASK_RSP_UPIU_SIZE_DWORDS ? ? ?8
> +
> +#define MAX_CDB_SIZE ? ? ? ? ? 16
> +#define ALIGNED_UPIU_SIZE ? ? ?128
> +
> +#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
> + ? ? ? ? ? ? ? ? ? ? ? ((byte3 << 24) | (byte2 << 16) |\
> + ? ? ? ? ? ? ? ? ? ? ? ?(byte1 << 8) | (byte0))
> +
> +/*
> + * UFS Protocol Information Unit related definitions
> + */
> +
> +/* Task management functions */
> +enum {
> + ? ? ? UFS_ABORT_TASK ? ? ? ? ?= 0x01,
> + ? ? ? UFS_ABORT_TASK_SET ? ? ?= 0x02,
> + ? ? ? UFS_CLEAR_TASK_SET ? ? ?= 0x04,
> + ? ? ? UFS_LOGICAL_RESET ? ? ? = 0x08,
> + ? ? ? UFS_QUERY_TASK ? ? ? ? ?= 0x80,
> + ? ? ? UFS_QUERY_TASK_SET ? ? ?= 0x81
> +};
> +
> +/* UTP UPIU Transaction Codes Initiator to Target */
> +enum {
> + ? ? ? UPIU_TRANSACTION_NOP_OUT ? ? ? ?= 0x00,
> + ? ? ? UPIU_TRANSACTION_COMMAND ? ? ? ?= 0x01,
> + ? ? ? UPIU_TRANSACTION_DATA_OUT ? ? ? = 0x02,
> + ? ? ? UPIU_TRANSACTION_TASK_REQ ? ? ? = 0x04,
> + ? ? ? UPIU_TRANSACTION_QUERY_REQ ? ? ?= 0x26
> +};
> +
> +/* UTP UPIU Transaction Codes Target to Initiator */
> +enum {
> + ? ? ? UPIU_TRANSACTION_NOP_IN ? ? ? ? = 0x20,
> + ? ? ? UPIU_TRANSACTION_RESPONSE ? ? ? = 0x21,
> + ? ? ? UPIU_TRANSACTION_DATA_IN ? ? ? ?= 0x22,
> + ? ? ? UPIU_TRANSACTION_TASK_RSP ? ? ? = 0x24,
> + ? ? ? UPIU_TRANSACTION_READY_XFER ? ? = 0x31,
> + ? ? ? UPIU_TRANSACTION_QUERY_RSP ? ? ?= 0x36
> +};
> +
> +/* UPIU Read/Write flags */
> +enum {
> + ? ? ? UPIU_CMD_FLAGS_READ ? ? = 0x40,
> + ? ? ? UPIU_CMD_FLAGS_WRITE ? ?= 0x20
> +
> +};
> +
> +/* UPIU Task Attributes */
> +enum {
> + ? ? ? UPIU_TASK_ATTR_SIMPLE ? = 0x00,
> + ? ? ? UPIU_TASK_ATTR_ORDERED ?= 0x01,
> + ? ? ? UPIU_TASK_ATTR_HEADQ ? ?= 0x02,
> + ? ? ? UPIU_TASK_ATTR_ACA ? ? ?= 0x03
> +};
> +
> +/* UTP QUERY Transaction Specific Fields OpCode */
> +enum {
> + ? ? ? UPIU_QUERY_OPCODE_NOP ? ? ? ? ? = 0x0,
> + ? ? ? UPIU_QUERY_OPCODE_READ_DESC ? ? = 0x1,
> + ? ? ? UPIU_QUERY_OPCODE_WRITE_DESC ? ?= 0x2,
> + ? ? ? UPIU_QUERY_OPCODE_READ_ATTR ? ? = 0x3,
> + ? ? ? UPIU_QUERY_OPCODE_WRITE_ATTR ? ?= 0x4,
> + ? ? ? UPIU_QUERY_OPCODE_READ_FLAG ? ? = 0x5,
> + ? ? ? UPIU_QUERY_OPCODE_SET_FLAG ? ? ?= 0x6,
> + ? ? ? UPIU_QUERY_OPCODE_CLEAR_FLAG ? ?= 0x7,
> + ? ? ? UPIU_QUERY_OPCODE_TOGGLE_FLAG ? = 0x8
> +};
> +
> +/* UTP Transfer Request Command Type (CT) */
> +enum {
> + ? ? ? UPIU_COMMAND_SET_TYPE_SCSI ? ? ?= 0x0,
> + ? ? ? UPIU_COMMAND_SET_TYPE_UFS ? ? ? = 0x1,
> + ? ? ? UPIU_COMMAND_SET_TYPE_QUERY ? ? = 0x2
> +};
> +
> +enum {
> + ? ? ? MASK_SCSI_STATUS ? ? ? ?= 0xFF,
> + ? ? ? MASK_TASK_RESPONSE ? ? ?= 0xFF00,
> + ? ? ? MASK_RSP_UPIU_RESULT ? ?= 0xFFFF
> +};
> +
> +/**
> + * struct utp_upiu_header - UPIU header structure
> + * @dword_0: UPIU header DW-0
> + * @dword_1: UPIU header DW-1
> + * @dword_2: UPIU header DW-2
> + */
> +struct utp_upiu_header {
> + ? ? ? u32 dword_0;
> + ? ? ? u32 dword_1;
> + ? ? ? u32 dword_2;
> +};
> +
> +/**
> + * struct utp_upiu_cmd - Command UPIU structure
> + * @header: UPIU header structure DW-0 to DW-2
> + * @data_transfer_len: Data Transfer Length DW-3
> + * @cdb: Command Descriptor Block CDB DW-4 to DW-7
> + */
> +struct utp_upiu_cmd {
> + ? ? ? struct utp_upiu_header header;
> + ? ? ? u32 exp_data_transfer_len;
> + ? ? ? u8 cdb[MAX_CDB_SIZE];
> +};
> +
> +/**
> + * struct utp_upiu_rsp - Response UPIU structure
> + * @header: UPIU header DW-0 to DW-2
> + * @residual_transfer_count: Residual transfer count DW-3
> + * @reserved: Reserver DW-4 to DW-7
> + * @sense_data_len: Sense data length DW-8 U16
> + * @sense_data: Sense data field DW-8 to DW-12
> + */
> +struct utp_upiu_rsp {
> + ? ? ? struct utp_upiu_header header;
> + ? ? ? u32 residual_transfer_count;
> + ? ? ? u32 reserved[4];
> + ? ? ? u16 sense_data_len;
> + ? ? ? u8 sense_data[18];
> +};
> +
> +/**
> + * struct utp_upiu_task_req - Task request UPIU structure
> + * @header - UPIU header structure DW0 to DW-2
> + * @input_param1: Input param 1 DW-3
> + * @input_param2: Input param 2 DW-4
> + * @input_param3: Input param 3 DW-5
> + * @reserved: Reserver DW-6 to DW-7
> + */
> +struct utp_upiu_task_req {
> + ? ? ? struct utp_upiu_header header;
> + ? ? ? u32 input_param1;
> + ? ? ? u32 input_param2;
> + ? ? ? u32 input_param3;
> + ? ? ? u32 reserved[2];
> +};
> +
> +/**
> + * struct utp_upiu_task_rsp - Task Management Response UPIU structure
> + * @header: UPIU header structure DW0-DW-2
> + * @output_param1: Ouput param 1 DW3
> + * @output_param2: Output param 2 DW4
> + * @reserved: Reserver DW-5 to DW-7
> + */
> +struct utp_upiu_task_rsp {
> + ? ? ? struct utp_upiu_header header;
> + ? ? ? u32 output_param1;
> + ? ? ? u32 output_param2;
> + ? ? ? u32 reserved[3];
> +};
> +
> +#endif /* End of Header */
> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
> new file mode 100644
> index 0000000..c82eeea
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufshcd.c
> @@ -0,0 +1,1091 @@
> +/*
> + * Universal Flash Storage Host controller driver
> + *
> + * This code is based on drivers/scsi/ufs/ufshcd.c
> + * Copyright (C) 2011-2012 Samsung India Software Operations
> + *
> + * Santosh Yaraganavi <[email protected]>
> + * Vinayak Holikatti <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
> + * GNU General Public License for more details.
> + *
> + * NO WARRANTY
> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
> + * solely responsible for determining the appropriateness of using and
> + * distributing the Program and assumes all risks associated with its
> + * exercise of rights under this Agreement, including but not limited to
> + * the risks and costs of program errors, damage to or loss of data,
> + * programs or equipment, and unavailability or interruption of operations.
> +
> + * DISCLAIMER OF LIABILITY
> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
> + * USA.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/pci.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
> +#include <linux/errno.h>
> +#include <linux/types.h>
> +#include <linux/wait.h>
> +
> +#include <asm/irq.h>
> +#include <asm/byteorder.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_cmnd.h>
> +#include <scsi/scsi_host.h>
> +#include <scsi/scsi_dbg.h>
> +
> +#include "ufs.h"
> +#include "ufshci.h"
> +
> +#define UFSHCD "ufshcd"
> +#define UFSHCD_DRIVER_VERSION "0.1"
> +
> +#ifndef NULL
> +#define NULL 0
> +#endif ?/* NULL */
> +
> +#define BYTES_TO_DWORDS(p) ? ? (p >> 2)
> +#define UFSHCD_MMIO_BASE ? ? ? (hba->mmio_base)
> +
> +enum {
> + ? ? ? UFSHCD_MAX_CHANNEL ? ? ?= 1,
> + ? ? ? UFSHCD_MAX_ID ? ? ? ? ? = 1,
> + ? ? ? UFSHCD_MAX_LUNS ? ? ? ? = 8,
> + ? ? ? UFSHCD_CAN_QUEUE ? ? ? ?= 32,
> + ? ? ? BYTES_128 ? ? ? ? ? ? ? = 128,
> + ? ? ? BYTES_1024 ? ? ? ? ? ? ?= 1024
> +};
> +
> +/* UFSHCD states */
> +enum {
> + ? ? ? UFSHCD_STATE_OPERATIONAL,
> + ? ? ? UFSHCD_STATE_RESET,
> + ? ? ? UFSHCD_STATE_ERROR
> +};
> +
> +/* Interrupt configuration options */
> +enum {
> + ? ? ? UFSHCD_INT_DISABLE,
> + ? ? ? UFSHCD_INT_ENABLE,
> + ? ? ? UFSHCD_INT_CLEAR
> +};
> +
> +/* Interrupt aggregation options */
> +enum {
> + ? ? ? INT_AGGR_RESET,
> + ? ? ? INT_AGGR_CONFIG
> +};
> +
> +/**
> + * struct uic_command - UIC command structure
> + * @command: UIC command
> + * @argument1: UIC command argument 1
> + * @argument2: UIC command argument 2
> + * @argument3: UIC command argument 3
> + * @cmd_active: Indicate if UIC command is outstanding
> + * @result: UIC command result
> + * @callback: routine to be called when UIC command completes
> + */
> +struct uic_command {
> + ? ? ? u32 command;
> + ? ? ? u32 argument1;
> + ? ? ? u32 argument2;
> + ? ? ? u32 argument3;
> + ? ? ? int cmd_active;
> + ? ? ? int result;
> +};
> +
> +/**
> + * struct ufs_hba - per adapter private structure
> + * @mmio_base: UFSHCI base register address
> + * @ucdl_virt_addr: UFS Command Descriptor virtual address
> + * @utrdl_virt_addr: UTP Transfer Request Descriptor virtual address
> + * @utmrdl_virt_addr: UTP Task Management Descriptor virtual address
> + * @utrdl_virt_addr_aligned: UTRD Aligned vitual address
> + * @utmrdl_virt_addr_aligned: UTMRD Aligned virtual address
> + * @ucdl_size: Memory size of UCD command block
> + * @utrdl_size: Memory size of UTRDL block
> + * @utmrdl_size: Memory size of UTMRDL block
> + * @ucdl_dma_addr: UFS Command Descriptor DMA address
> + * @utrdl_dma_addr: UTRDL DMA address
> + * @utmrdl_dma_addr: UTMRDL DMA address
> + * @utrdl_dma_addr_aligned: UTRDL aligned DMA address
> + * @utmrdl_dma_addr_aligned: UTMRDL aligned DMA address
> + * @ucdl_dma_addr_aligned: UCD aligned DMA address
> + * @dma_size:
> + * @host: Scsi_Host instance of the driver
> + * @pdev: PCI device handle
> + * @lrb: local reference block
> + * @capabilities: UFS Controller Capabilities
> + * @nutrs: Transfer Request Queue depth supported by controller
> + * @nutmrs: Task Management Queue depth supported by controller
> + * @active_uic_cmd: handle of active UIC command
> + * @ufshcd_state: UFSHCD states
> + * @int_enable_mask: Interrupt Mask Bits
> + * @uic_workq: Work queue for UIC completion handling
> + */
> +struct ufs_hba {
> + ? ? ? void __iomem *mmio_base;
> +
> + ? ? ? /* Virtual memory reference */
> + ? ? ? void *ucdl_virt_addr;
> + ? ? ? void *utrdl_virt_addr;
> + ? ? ? void *utmrdl_virt_addr;
> + ? ? ? void *utrdl_virt_addr_aligned;
> + ? ? ? void *utmrdl_virt_addr_aligned;
> + ? ? ? void *ucdl_virt_addr_aligned;
> +
> + ? ? ? size_t ucdl_size;
> + ? ? ? size_t utrdl_size;
> + ? ? ? size_t utmrdl_size;
> +
> + ? ? ? /* DMA memory reference */
> + ? ? ? dma_addr_t ucdl_dma_addr;
> + ? ? ? dma_addr_t utrdl_dma_addr;
> + ? ? ? dma_addr_t utmrdl_dma_addr;
> + ? ? ? dma_addr_t utrdl_dma_addr_aligned;
> + ? ? ? dma_addr_t utmrdl_dma_addr_aligned;
> + ? ? ? dma_addr_t ucdl_dma_addr_aligned;
> +
> + ? ? ? size_t dma_size;
> +
> + ? ? ? struct Scsi_Host *host;
> + ? ? ? struct pci_dev *pdev;
> +
> + ? ? ? struct ufshcd_lrb *lrb;
> +
> + ? ? ? u32 capabilities;
> + ? ? ? int nutrs;
> + ? ? ? int nutmrs;
> + ? ? ? u32 ufs_version;
> +
> + ? ? ? struct uic_command active_uic_cmd;
> +
> + ? ? ? u32 ufshcd_state;
> + ? ? ? u32 int_enable_mask;
> +
> + ? ? ? /* Work Queues */
> + ? ? ? struct work_struct uic_workq;
> +};
> +
> +/**
> + * struct ufshcd_lrb - command control block
> + * @utr_descriptor_ptr: UTRD address of the command
> + * @ucd_cmd_ptr: UCD address of the command
> + * @ucd_rsp_ptr: Response UPIU address for this command
> + * @ucd_prdt_ptr: PRDT address of the command
> + */
> +struct ufshcd_lrb {
> + ? ? ? struct utp_transfer_req_desc *utr_descriptor_ptr;
> + ? ? ? struct utp_upiu_cmd *ucd_cmd_ptr;
> + ? ? ? struct utp_upiu_rsp *ucd_rsp_ptr;
> + ? ? ? struct ufshcd_sg_entry *ucd_prdt_ptr;
> +};
> +
> +/**
> + * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
> + * @hba - Pointer to adapter instance
> + *
> + * Returns UFSHCI version supported by the controller
> + */
> +static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
> +{
> + ? ? ? return readl(UFSHCD_MMIO_BASE + REG_UFS_VERSION);
> +}
> +
> +/**
> + * ufshcd_is_device_present - Check if any device connected to
> + * ? ? ? ? ? ? ? ? ? ? ? ? ? the host controller
> + * @reg_hcs - host controller status register value
> + *
> + * Returns 0 if device present, non-zeo if no device detected
> + */
> +static inline int ufshcd_is_device_present(u32 reg_hcs)
> +{
> + ? ? ? return (DEVICE_PRESENT & reg_hcs) ? 0 : -1;
> +}
> +
> +/**
> + * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
> + * @reg: Register value of host controller status
> + *
> + * Returns integer, 0 on Success and positive value if failed
> + */
> +static inline int ufshcd_get_lists_status(u32 reg)
> +{
> + ? ? ? /*
> + ? ? ? ?* The mask 0xFF is for the following HCS register bits
> + ? ? ? ?* Bit ? ? ? ? ?Description
> + ? ? ? ?* ?0 ? ? ? ? ? Device Present
> + ? ? ? ?* ?1 ? ? ? ? ? UTRLRDY
> + ? ? ? ?* ?2 ? ? ? ? ? UTMRLRDY
> + ? ? ? ?* ?3 ? ? ? ? ? UCRDY
> + ? ? ? ?* ?4 ? ? ? ? ? HEI
> + ? ? ? ?* ?5 ? ? ? ? ? DEI
> + ? ? ? ?* 6-7 ? ? ? ? ?reserved
> + ? ? ? ?*/
> + ? ? ? return (((reg) & (0xFF)) >> 1) ^ (0x07);
> +}
> +
> +/**
> + * ufshcd_get_uic_cmd_result - Get the UIC command result
> + * @hba: Pointer to adapter instance
> + *
> + * This function gets the result of UIC command completion
> + * Returns 0 on success, non zero value on error
> + */
> +static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
> +{
> + ? ? ? return readl(UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2) &
> + ? ? ? ? ? ? ?MASK_UIC_COMMAND_RESULT;
> +}
> +
> +/**
> + * ufshcd_free_hba_memory - Free allocated memory for LRB request
> + * ? ? ? ? ? ? ? ? ? ? ? ? and task lists
> + * @hba: Pointer to adapter instance
> + */
> +static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
> +{
> + ? ? ? kfree(hba->lrb);
> + ? ? ? hba->lrb = NULL;
> +
> + ? ? ? if (hba->utmrdl_virt_addr_aligned) {
> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
> + ? ? ? ? ? ? ? hba->utmrdl_virt_addr = NULL;
> + ? ? ? ? ? ? ? hba->utmrdl_virt_addr_aligned = NULL;
> + ? ? ? }
> +
> + ? ? ? if (hba->utrdl_virt_addr_aligned) {
> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_virt_addr, hba->utrdl_dma_addr);
> + ? ? ? ? ? ? ? hba->utrdl_virt_addr = NULL;
> + ? ? ? ? ? ? ? hba->utrdl_virt_addr_aligned = NULL;
> + ? ? ? }
> +
> + ? ? ? if (hba->ucdl_virt_addr_aligned) {
> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->ucdl_virt_addr, hba->ucdl_dma_addr);
> + ? ? ? ? ? ? ? hba->ucdl_virt_addr = NULL;
> + ? ? ? ? ? ? ? hba->ucdl_virt_addr_aligned = NULL;
> + ? ? ? }
> +}
> +
> +/**
> + * ufshcd_config_int_aggr - Configure interrupt aggregation values
> + * ? ? ? ? ? ? currently there is no use case where we want to configure
> + * ? ? ? ? ? ? interrupt aggregation dynamically. So to configure interrupt
> + * ? ? ? ? ? ? aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
> + * ? ? ? ? ? ? INT_AGGR_TIMEOUT_VALUE are used.
> + * @hba: per adapter instance
> + * @option: Interrupt aggregation option
> + */
> +static inline void
> +ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
> +{
> + ? ? ? switch (option) {
> + ? ? ? case INT_AGGR_RESET:
> + ? ? ? ? ? ? ? writel((INT_AGGR_ENABLE |
> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_COUNTER_AND_TIMER_RESET),
> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
> + ? ? ? ? ? ? ? break;
> + ? ? ? case INT_AGGR_CONFIG:
> + ? ? ? ? ? ? ? writel((INT_AGGR_ENABLE |
> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_PARAM_WRITE |
> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_COUNTER_THRESHOLD_VALUE |
> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_TIMEOUT_VALUE),
> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +}
> +
> +/**
> + * ufshcd_hba_stop - put the controller in reset state
> + * @hba: per adapter instance
> + */
> +static inline void ufshcd_hba_stop(struct ufs_hba *hba)
> +{
> + ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
> +}
> +
> +/**
> + * ufshcd_hba_capabilities - Read controller capabilities
> + * @hba: per adapter instance
> + */
> +static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
> +{
> + ? ? ? u32 capabilities;
> +
> + ? ? ? capabilities =
> + ? ? ? ? ? ? ? readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_CAPABILITIES);
> + ? ? ? hba->capabilities = capabilities;
> +
> + ? ? ? /* nutrs and nutmrs are 0 based values */
> + ? ? ? hba->nutrs = (capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
> + ? ? ? hba->nutmrs =
> + ? ? ? ((capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
> +}
> +
> +/**
> + * ufshcd_send_uic_command - Send UIC commands to unipro layers
> + * @hba: per adapter instance
> + * @uic_command: UIC command
> + */
> +static inline void
> +ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
> +{
> + ? ? ? /* Clear interrupt status register */
> + ? ? ? writel((readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS)),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
> +
> + ? ? ? /* Write Args */
> + ? ? ? writel(uic_cmnd->argument1,
> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_1));
> + ? ? ? writel(uic_cmnd->argument2,
> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2));
> + ? ? ? writel(uic_cmnd->argument3,
> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_3));
> +
> + ? ? ? /* Write UIC Cmd */
> + ? ? ? writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UIC_COMMAND));
> +}
> +
> +/**
> + * ufshcd_int_config - enable/disable interrupts
> + * @hba: per adapter instance
> + * @option: interrupt option
> + */
> +static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
> +{
> + ? ? ? switch (option) {
> + ? ? ? case UFSHCD_INT_ENABLE:
> + ? ? ? ? ? ? ? writel(hba->int_enable_mask,
> + ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
> + ? ? ? ? ? ? ? break;
> + ? ? ? case UFSHCD_INT_DISABLE:
> + ? ? ? ? ? ? ? if (UFSHCI_VERSION_10 == hba->ufs_version)
> + ? ? ? ? ? ? ? ? ? ? ? writel(readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE),
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
> + ? ? ? ? ? ? ? else
> + ? ? ? ? ? ? ? ? ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Invalid interrupt option\n");
> + ? ? ? ? ? ? ? break;
> + ? ? ? } /* end of switch */
> +}
> +
> +/**
> + * ufshcd_memory_alloc - allocate memory for host memory space data structures
> + * @hba: per adapter instance
> + *
> + * 1) Allocate DMA memory for Command Descriptor array
> + * ? ? Each command descriptor consist of Command UPIU, Response UPIU and PRDT
> + * 2) Align allocated command descriptor address to 128 byte align.
> + * 3) Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
> + * 4) Align UTRDL address to 1KB (UFSHCI spec)
> + * 5) Allocate DMA memory for UTP Task Management Request Descriptor List
> + * ? ? (UTMRDL)
> + * 6) Align UTMRDL address to 1KB (UFSHCI spec)
> + * 7) Allocate the memory for local reference block(lrb).
> + *
> + * Returns 0 for success, non-zero in case of failure
> + */
> +static int ufshcd_memory_alloc(struct ufs_hba *hba)
> +{
> + ? ? ? /*
> + ? ? ? ?* Allocate memory for UTP command descriptors.
> + ? ? ? ?* UFSHCI requires 128 byte alignement of UCD and
> + ? ? ? ?* 64 byte alignement for PRDT. So allocating extra 128 bytes
> + ? ? ? ?*/
> + ? ? ? hba->ucdl_size =
> + ? ? ? (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs) + BYTES_128;
> + ? ? ? hba->ucdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?hba->ucdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&hba->ucdl_dma_addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GFP_KERNEL);
> + ? ? ? if (NULL == hba->ucdl_virt_addr) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Command Descriptor Memory allocation failed\n");
> + ? ? ? ? ? ? ? goto ucd_fail;
> + ? ? ? }
> +
> + ? ? ? /* Align UCD to 128 bytes */
> + ? ? ? hba->ucdl_virt_addr_aligned =
> + ? ? ? (void *) ALIGN((unsigned long) hba->ucdl_virt_addr, BYTES_128);
> + ? ? ? hba->ucdl_dma_addr_aligned = ALIGN(hba->ucdl_dma_addr, BYTES_128);
> +
> + ? ? ? /*
> + ? ? ? ?* Allocate memory for UTP Transfer descriptors.
> + ? ? ? ?* UFSHCI requires 1kb alignement of UTRD. So allocating
> + ? ? ? ?* extra 1024 bytes
> + ? ? ? ?*/
> + ? ? ? hba->utrdl_size =
> + ? ? ? (sizeof(struct utp_transfer_req_desc) * hba->nutrs) + BYTES_1024;
> + ? ? ? hba->utrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &hba->utrdl_dma_addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
> + ? ? ? if (NULL == hba->utrdl_virt_addr) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Transfer Descriptor Memory allocation failed\n");
> + ? ? ? ? ? ? ? goto utrd_fail;
> + ? ? ? }
> +
> + ? ? ? /* alignement UTRD to 1kb */
> + ? ? ? hba->utrdl_virt_addr_aligned =
> + ? ? ? (void *) ALIGN((unsigned long) hba->utrdl_virt_addr, BYTES_1024);
> + ? ? ? hba->utrdl_dma_addr_aligned = ALIGN(hba->utrdl_dma_addr, BYTES_1024);
> +
> + ? ? ? /*
> + ? ? ? ?* Allocate memory for UTP Task Management descriptors
> + ? ? ? ?* UFSHCI requires 1kb alignement of UTMRD. So allocating
> + ? ? ? ?* extra 1024 bytes
> + ? ? ? ?*/
> + ? ? ? hba->utmrdl_size =
> + ? ? ? sizeof(struct utp_task_req_desc) * hba->nutmrs + BYTES_1024;
> + ? ? ? hba->utmrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?hba->utmrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&hba->utmrdl_dma_addr,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GFP_KERNEL);
> + ? ? ? if (NULL == hba->utmrdl_virt_addr) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? "Task Management Descriptor Memory allocation failed\n");
> + ? ? ? ? ? ? ? goto utmrd_fail;
> + ? ? ? }
> +
> + ? ? ? /* alignement UTMRD to 1kb */
> + ? ? ? hba->utmrdl_virt_addr_aligned =
> + ? ? ? (void *) ALIGN((unsigned long) hba->utmrdl_virt_addr, BYTES_1024);
> + ? ? ? hba->utmrdl_dma_addr_aligned = ALIGN(hba->utmrdl_dma_addr, BYTES_1024);
> +
> + ? ? ? /* Allocate memory for local reference block */
> + ? ? ? hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
> + ? ? ? if (NULL == hba->lrb) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
> + ? ? ? ? ? ? ? goto lrb_fail;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +
> +lrb_fail:
> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
> + ? ? ? hba->utmrdl_virt_addr = NULL;
> + ? ? ? hba->utmrdl_virt_addr_aligned = NULL;
> +utmrd_fail:
> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_virt_addr, hba->utrdl_dma_addr);
> + ? ? ? hba->utrdl_virt_addr = NULL;
> + ? ? ? hba->utrdl_virt_addr_aligned = NULL;
> +utrd_fail:
> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
> + ? ? ? ? ? ? ? ? ? ? ? ? hba->ucdl_virt_addr, hba->ucdl_dma_addr);
> + ? ? ? hba->ucdl_virt_addr = NULL;
> + ? ? ? hba->ucdl_virt_addr_aligned = NULL;
> +ucd_fail:
> + ? ? ? return -ENOMEM;
> +}
> +
> +/**
> + * ufshcd_host_memory_configure - configure local reference block with
> + * ? ? ? ? ? ? ? ? ? ? ? ? ? ? memory offsets
> + * @hba: per adapter instance
> + *
> + * Configure Host memory space
> + * 1) Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
> + * address.
> + * 2) Update each UTRD with Response UPIU offset, Response UPIU length
> + * and PRDT offset.
> + * 3) Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
> + * into local reference block.
> + */
> +static void ufshcd_host_memory_configure(struct ufs_hba *hba)
> +{
> + ? ? ? struct utp_transfer_cmd_desc *cmd_descp;
> + ? ? ? struct utp_transfer_req_desc *utrdlp;
> + ? ? ? dma_addr_t cmd_desc_dma_addr;
> + ? ? ? dma_addr_t cmd_desc_element_addr;
> + ? ? ? u16 response_offset;
> + ? ? ? u16 prdt_offset;
> + ? ? ? int cmd_desc_size;
> + ? ? ? int i;
> +
> + ? ? ? utrdlp = (struct utp_transfer_req_desc *)hba->utrdl_virt_addr_aligned;
> + ? ? ? cmd_descp =
> + ? ? ? ? ? ? ? (struct utp_transfer_cmd_desc *)hba->ucdl_virt_addr_aligned;
> +
> + ? ? ? response_offset =
> + ? ? ? ? ? ? ? offsetof(struct utp_transfer_cmd_desc, response_upiu);
> + ? ? ? prdt_offset =
> + ? ? ? ? ? ? ? offsetof(struct utp_transfer_cmd_desc, prd_table);
> +
> + ? ? ? cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
> + ? ? ? cmd_desc_dma_addr = hba->ucdl_dma_addr_aligned;
> +
> + ? ? ? for (i = 0; i < hba->nutrs; i++) {
> + ? ? ? ? ? ? ? /* Configure UTRD with command descriptor base address */
> + ? ? ? ? ? ? ? cmd_desc_element_addr =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (cmd_desc_dma_addr + (cmd_desc_size * i));
> + ? ? ? ? ? ? ? utrdlp[i].command_desc_base_addr_lo =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(cmd_desc_element_addr);
> + ? ? ? ? ? ? ? utrdlp[i].command_desc_base_addr_hi =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(cmd_desc_element_addr >> 32);
> +
> + ? ? ? ? ? ? ? /* Response upiu and prdt offset should be in double words */
> + ? ? ? ? ? ? ? utrdlp[i].response_upiu_offset =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(BYTES_TO_DWORDS(response_offset));
> + ? ? ? ? ? ? ? utrdlp[i].prd_table_offset =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(BYTES_TO_DWORDS(prdt_offset));
> + ? ? ? ? ? ? ? utrdlp[i].response_upiu_length =
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(ALIGNED_UPIU_SIZE);
> +
> + ? ? ? ? ? ? ? hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
> + ? ? ? ? ? ? ? hba->lrb[i].ucd_cmd_ptr =
> + ? ? ? ? ? ? ? ? ? ? ? (struct utp_upiu_cmd *)(cmd_descp + i);
> + ? ? ? ? ? ? ? hba->lrb[i].ucd_rsp_ptr =
> + ? ? ? ? ? ? ? ? ? ? ? (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
> + ? ? ? ? ? ? ? hba->lrb[i].ucd_prdt_ptr =
> + ? ? ? ? ? ? ? ? ? ? ? (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
> + ? ? ? }
> +}
> +
> +/**
> + * ufshcd_dme_link_startup - Notify Unipro to perform link startup
> + * @hba: per adapter instance
> + *
> + * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
> + * in order to intitialize the Unipro link startup procedure.
> + * Once the Unipro links are up, the device connected to the controller
> + * is detected.
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_dme_link_startup(struct ufs_hba *hba)
> +{
> + ? ? ? struct uic_command *uic_cmd;
> + ? ? ? unsigned long flags;
> +
> + ? ? ? /* check if controller is ready to accept UIC commands */
> + ? ? ? if (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS)) &
> + ? ? ? ? ? UIC_COMMAND_READY) == 0x0) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Controller not ready"
> + ? ? ? ? ? ? ? ? ? ? ? " to accept UIC commands\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
> + ? ? ? uic_cmd = &hba->active_uic_cmd;
> + ? ? ? uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
> + ? ? ? uic_cmd->argument1 = 0;
> + ? ? ? uic_cmd->argument2 = 0;
> + ? ? ? uic_cmd->argument3 = 0;
> +
> + ? ? ? /* Enable UIC related interrupts */
> + ? ? ? hba->int_enable_mask |= UIC_COMMAND_COMPL;
> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
> +
> + ? ? ? /* sending UIC commands to controller */
> + ? ? ? ufshcd_send_uic_command(hba, uic_cmd);
> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
> +
> + ? ? ? return 0;
> +}
> +
> +/**
> + * ufshcd_make_hba_operational - Make UFS controller operatinal
> + * @hba: per adapter instance
> + *
> + * To bring UFS host controller to operational state,
> + * 1. Check if device is present
> + * 2. Configure run-stop-registers
> + * 3. Enable required interrupts
> + * 4. Configure interrupt aggregation
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_make_hba_operational(struct ufs_hba *hba)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? /* check if device present */
> + ? ? ? reg = readl((UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS));
> + ? ? ? if (ufshcd_is_device_present(reg)) {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "cc: Device not present\n");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /*
> + ? ? ? ?* UCRDY, UTMRLDY and UTRLRDY bits must be 1
> + ? ? ? ?* DEI, HEI bits must be 0
> + ? ? ? ?*/
> + ? ? ? if (!(ufshcd_get_lists_status(reg))) {
> + ? ? ? ? ? ? ? writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
> + ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TASK_REQ_LIST_RUN_STOP));
> + ? ? ? ? ? ? ? writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
> + ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
> + ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
> + ? ? ? } else {
> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? "Host controller not ready to process requests");
> + ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? }
> +
> + ? ? ? /* Enable required interrupts */
> + ? ? ? hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UIC_ERROR |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UTP_TASK_REQ_COMPL |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DEVICE_FATAL_ERROR |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONTROLLER_FATAL_ERROR |
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SYSTEM_BUS_FATAL_ERROR);
> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_ENABLE);

UFS host controller specification Section 7.2.1, step 11, mentions
that the aggregation control register should be set if run/stop bit is
not enabled.
But In this case the run/ stop bit is set above before configuring the
aggregation register. Please check for the same in other places from
where it is called.

> +
> + ? ? ? /* Configure interrupt aggregation */
> + ? ? ? ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
> +
> + ? ? ? hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
> +
> + ? ? ? return 0;
> +}
> +
> +/**
> + * ufshcd_controller_enable - initialize the controller
> + * @hba: per adapter instance
> + *
> + * The controller resets its self and controller firmware start of day is
> + * kickes off. When controller is ready it will set the Host Controller
> + * Status bit to 1.
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int ufshcd_controller_enable(struct ufs_hba *hba)
> +{
> + ? ? ? int retry;
> +
> + ? ? ? /*
> + ? ? ? ?* msleep of 1 and 5 used in this function might result in msleep(20),
> + ? ? ? ?* but it was necessary to send the UFS FPGA to reset mode during
> + ? ? ? ?* development and testing of this driver. msleep can be changed to
> + ? ? ? ?* mdelay and retry count can be reduced based on the controller.
> + ? ? ? ?*/
> +
> + ? ? ? /* change controller state to "reset state" */
> + ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
> + ? ? ? msleep(5);
> +
> + ? ? ? writel(CONTROLLER_ENABLE,
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
> + ? ? ? msleep(1);
> +
> + ? ? ? /* wait for the host controller to complete initialization */
> + ? ? ? retry = 10;
> + ? ? ? while (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE)) &
> + ? ? ? ? ? ? ?CONTROLLER_ENABLE) != 0x1) {
> + ? ? ? ? ? ? ? if (retry) {
> + ? ? ? ? ? ? ? ? ? ? ? retry--;
> + ? ? ? ? ? ? ? } else {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Controller enable failed\n");
> + ? ? ? ? ? ? ? ? ? ? ? return -EINVAL;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? msleep(5);
> + ? ? ? }
> + ? ? ? return 0;
> +}
> +
> +/**
> + * ufshcd_initialize_hba - start the initialization process
> + * @hba: per adapter instance
> + *
> + * Initialize the Controller
> + * 1) Enable the controller via ufshcd_controller_enable.
> + * 2) Program the Transfer Request List Address with the starting address of
> + * UTRDL.
> + *
> + * 3) Program the Task Management Request List Address with starting address
> + * of UTMRDL.
> + *
> + * Returns 0 on success, non-zero value on failure.
> + */
> +static int ufshcd_initialize_hba(struct ufs_hba *hba)
> +{
> + ? ? ? if (ufshcd_controller_enable(hba))
> + ? ? ? ? ? ? ? return -1;
> +
> + ? ? ? /* Configure TR/TM address registers */
> + ? ? ? writel(hba->utrdl_dma_addr_aligned,
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
> + ? ? ? writel((hba->utrdl_dma_addr_aligned >> 32),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
> + ? ? ? writel(hba->utmrdl_dma_addr_aligned,
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_L));
> + ? ? ? writel((hba->utmrdl_dma_addr_aligned >> 32),
> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_H));
> +
> + ? ? ? /* Initialize unipro link startup procedure */
> + ? ? ? return ufshcd_dme_link_startup(hba);
> +}
> +
> +/**
> + * ufshcd_uic_cc_handler - handle UIC command completion
> + * @work: pointer to a work queue structure
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static void ufshcd_uic_cc_handler (struct work_struct *work)
> +{
> + ? ? ? struct ufs_hba *hba;
> +
> + ? ? ? hba = container_of(work, struct ufs_hba, uic_workq);
> +
> + ? ? ? if ((UIC_CMD_DME_LINK_STARTUP == hba->active_uic_cmd.command) &&
> + ? ? ? ? ? !(ufshcd_get_uic_cmd_result(hba))) {
> +
> + ? ? ? ? ? ? ? if (ufshcd_make_hba_operational(hba))
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "cc: hba not operational state\n");
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +}
> +
> +/**
> + * ufshcd_sl_intr - Interrupt service routine
> + * @hba: per adapter instance
> + * @intr_status: contains interrupts generated by the controller
> + */
> +static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
> +{
> + ? ? ? if (intr_status & UIC_COMMAND_COMPL)
> + ? ? ? ? ? ? ? schedule_work(&hba->uic_workq);
> +}
> +
> +/**
> + * ufshcd_intr - Main interrupt service routine
> + * @irq: irq number
> + * @__hba: pointer to adapter instance
> + *
> + * Returns IRQ_HANDLED - If interrupt is valid
> + * ? ? ? ? ? ? IRQ_NONE - If invalid interrupt
> + */
> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
> +{
> + ? ? ? unsigned long flags;
> + ? ? ? u32 intr_status;
> + ? ? ? irqreturn_t retval = IRQ_NONE;
> + ? ? ? struct ufs_hba *hba = __hba;
> +
> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
> + ? ? ? intr_status = readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS);
> +
> + ? ? ? if (intr_status) {
> + ? ? ? ? ? ? ? ufshcd_sl_intr(hba, intr_status);
> +
> + ? ? ? ? ? ? ? /* If UFSHCI 1.0 then clear interrupt status register */
> + ? ? ? ? ? ? ? if (UFSHCI_VERSION_10 == hba->ufs_version)
> + ? ? ? ? ? ? ? ? ? ? ? writel(intr_status,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
> + ? ? ? ? ? ? ? retval = IRQ_HANDLED;
> + ? ? ? }
> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
> + ? ? ? return retval;
> +}
> +
> +static struct scsi_host_template ufshcd_driver_template = {
> + ? ? ? .module ? ? ? ? ? ? ? ? = THIS_MODULE,
> + ? ? ? .name ? ? ? ? ? ? ? ? ? = UFSHCD,
> + ? ? ? .proc_name ? ? ? ? ? ? ?= UFSHCD,
> + ? ? ? .this_id ? ? ? ? ? ? ? ?= -1,
> +};
> +
> +/**
> + * ufshcd_shutdown - main funciton to put the controller in reset state
> + * @pdev: pointer to PCI device handle
> + */
> +static void ufshcd_shutdown(struct pci_dev *pdev)
> +{
> + ? ? ? ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
> +}
> +
> +#ifdef CONFIG_PM
> +/**
> + * ufshcd_suspend - suspend power management function
> + * @pdev: pointer to PCI device handle
> + * @state: power state
> + *
> + * Returns -ENOSYS
> + */
> +static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
> +{
> + ? ? ? return -ENOSYS;
> +}
> +
> +/**
> + * ufshcd_resume - resume power management function
> + * @pdev: pointer to PCI device handle
> + *
> + * Returns -ENOSYS
> + */
> +static int ufshcd_resume(struct pci_dev *pdev)
> +{
> + ? ? ? return -ENOSYS;
> +}
> +#endif /* CONFIG_PM */
> +
> +/**
> + * ufshcd_hba_free - free allocated memory for
> + * ? ? ? ? ? ? ? ? ? ? host memory space data structures
> + * @hba: per adapter instance
> + */
> +static void ufshcd_hba_free(struct ufs_hba *hba)
> +{
> + ? ? ? iounmap(UFSHCD_MMIO_BASE);
> + ? ? ? ufshcd_free_hba_memory(hba);
> + ? ? ? pci_release_regions(hba->pdev);
> +}
> +
> +/**
> + * ufshcd_remove - deallocate PCI/SCSI host and host memory space
> + * ? ? ? ? ? ? data structure memory
> + * @pdev - pointer to PCI handle
> + */
> +static void ufshcd_remove(struct pci_dev *pdev)
> +{
> + ? ? ? struct ufs_hba *hba = pci_get_drvdata(pdev);
> +
> + ? ? ? /* disable interrupts */
> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
> + ? ? ? free_irq(pdev->irq, hba);
> +
> + ? ? ? ufshcd_hba_stop(hba);
> + ? ? ? ufshcd_hba_free(hba);
> +
> + ? ? ? scsi_remove_host(hba->host);
> + ? ? ? scsi_host_put(hba->host);
> + ? ? ? pci_set_drvdata(pdev, NULL);
> + ? ? ? pci_clear_master(pdev);
> + ? ? ? pci_disable_device(pdev);
> +}
> +
> +/**
> + * ufshcd_set_dma_mask - Set dma addressing
> + * @pdev: PCI device struct
> + *
> + * Returns 0 for success, non-zero for failure
> + */
> +static int ufshcd_set_dma_mask(struct pci_dev *pdev)
> +{
> + ? ? ? int err;
> +
> + ? ? ? do {
> + ? ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
> + ? ? ? ? ? ? ? if (!err) {
> + ? ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(64));
> + ? ? ? ? ? ? ? ? ? ? ? break;
> + ? ? ? ? ? ? ? }
> + ? ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
> + ? ? ? ? ? ? ? if (!err)
> + ? ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(32));
> + ? ? ? } while (0);
> +
> + ? ? ? return err;
> +}
> +
> +/**
> + * ufshcd_probe - probe routine of the driver
> + * @pdev: pointer to PCI device handle
> + * @id: PCI device id
> + *
> + * Returns 0 on success, non-zero value on failure
> + */
> +static int __devinit
> +ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> +{
> + ? ? ? struct Scsi_Host *host;
> + ? ? ? struct ufs_hba *hba;
> + ? ? ? int ufs_hba_len;
> + ? ? ? int err;
> +
> + ? ? ? ufs_hba_len = sizeof(struct ufs_hba);
> + ? ? ? err = pci_enable_device(pdev);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "pci_enable_device failed\n");
> + ? ? ? ? ? ? ? goto out_error;
> + ? ? ? }
> +
> + ? ? ? pci_set_master(pdev);
> +
> + ? ? ? host = scsi_host_alloc(&ufshcd_driver_template, ufs_hba_len);
> + ? ? ? if (!host) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "scsi_host_alloc failed\n");
> + ? ? ? ? ? ? ? err = -ENOMEM;
> + ? ? ? ? ? ? ? goto out_disable;
> + ? ? ? }
> + ? ? ? hba = (struct ufs_hba *)host->hostdata;
> +
> + ? ? ? err = pci_request_regions(pdev, UFSHCD);
> + ? ? ? if (err < 0) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request regions failed\n");
> + ? ? ? ? ? ? ? goto out_disable;
> + ? ? ? }
> +
> + ? ? ? hba->mmio_base = pci_ioremap_bar(pdev, 0);
> + ? ? ? if (!hba->mmio_base) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "memory map failed\n");
> + ? ? ? ? ? ? ? err = -ENOMEM;
> + ? ? ? ? ? ? ? goto out_release_regions;
> + ? ? ? }
> +
> + ? ? ? err = ufshcd_set_dma_mask(pdev);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "set dma mask failed\n");
> + ? ? ? ? ? ? ? goto out_iounmap;
> + ? ? ? }
> +
> + ? ? ? hba->host = host;
> + ? ? ? hba->pdev = pdev;
> +
> + ? ? ? /* Read capabilities registers */
> + ? ? ? ufshcd_hba_capabilities(hba);
> +
> + ? ? ? /* Get UFS version supported by the controller */
> + ? ? ? hba->ufs_version = ufshcd_get_ufs_version(hba);
> +
> + ? ? ? /* Allocate memory for host memory space */
> + ? ? ? err = ufshcd_memory_alloc(hba);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Memory allocation failed\n");
> + ? ? ? ? ? ? ? goto out_iounmap;
> + ? ? ? }
> +
> + ? ? ? /* Configure LRB */
> + ? ? ? ufshcd_host_memory_configure(hba);
> +
> + ? ? ? host->can_queue = hba->nutrs;
> + ? ? ? host->max_id = UFSHCD_MAX_ID;
> + ? ? ? host->max_lun = UFSHCD_MAX_LUNS;
> + ? ? ? host->max_channel = UFSHCD_MAX_CHANNEL;
> + ? ? ? host->unique_id = host->host_no;
> +
> + ? ? ? /* Initialize work queues */
> + ? ? ? INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
> +
> + ? ? ? /* IRQ registration */
> + ? ? ? err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request irq failed\n");
> + ? ? ? ? ? ? ? goto out_lrb_free;
> + ? ? ? }
> +
> + ? ? ? pci_set_drvdata(pdev, hba);
> +
> + ? ? ? err = scsi_add_host(host, &pdev->dev);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "scsi_add_host failed\n");
> + ? ? ? ? ? ? ? goto out_free_irq;
> + ? ? ? }
> +
> + ? ? ? /* Initialization routine */
> + ? ? ? err = ufshcd_initialize_hba(hba);
> + ? ? ? if (err) {
> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Initialization failed\n");
> + ? ? ? ? ? ? ? goto out_free_irq;
> + ? ? ? }
> +
> + ? ? ? return 0;
> +
> +out_free_irq:
> + ? ? ? free_irq(pdev->irq, hba);
> +out_lrb_free:
> + ? ? ? ufshcd_free_hba_memory(hba);
> +out_iounmap:
> + ? ? ? iounmap(hba->mmio_base);
> +out_release_regions:
> + ? ? ? pci_release_regions(pdev);
> +out_disable:
> + ? ? ? scsi_host_put(host);
> + ? ? ? pci_clear_master(pdev);
> + ? ? ? pci_disable_device(pdev);
> +out_error:
> + ? ? ? return err;
> +}
> +
> +static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
> + ? ? ? { 0x144D, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
> + ? ? ? { } ? ? /* terminate list */
> +};
> +
> +MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
> +
> +static struct pci_driver ufshcd_pci_driver = {
> + ? ? ? .name = UFSHCD,
> + ? ? ? .id_table = ufshcd_pci_tbl,
> + ? ? ? .probe = ufshcd_probe,
> + ? ? ? .remove = __devexit_p(ufshcd_remove),
> + ? ? ? .shutdown = ufshcd_shutdown,
> +#ifdef CONFIG_PM
> + ? ? ? .suspend = ufshcd_suspend,
> + ? ? ? .resume = ufshcd_resume,
> +#endif
> +};
> +
> +/**
> + * ufshcd_init - Driver registration routine
> + */
> +static int __init ufshcd_init(void)
> +{
> + ? ? ? return pci_register_driver(&ufshcd_pci_driver);
> +}
> +module_init(ufshcd_init);
> +
> +/**
> + * ufshcd_exit - Driver exit clean-up routine
> + */
> +static void __exit ufshcd_exit(void)
> +{
> + ? ? ? pci_unregister_driver(&ufshcd_pci_driver);
> +}
> +module_exit(ufshcd_exit);
> +
> +
> +MODULE_AUTHOR("Santosh Yaragnavi, Vinayak Holikatti");
> +MODULE_DESCRIPTION("Generic UFS host controller driver");
> +MODULE_LICENSE("GPL");
> +MODULE_VERSION(UFSHCD_DRIVER_VERSION);
> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
> new file mode 100644
> index 0000000..f8701b7
> --- /dev/null
> +++ b/drivers/scsi/ufs/ufshci.h
> @@ -0,0 +1,360 @@
> +/*
> + * Universal Flash Storage Host controller driver
> + *
> + * This code is based on drivers/scsi/ufs/ufshci.h
> + * Copyright (C) 2011-2012 Samsung India Software Operations
> + *
> + * Santosh Yaraganavi <[email protected]>
> + * Vinayak Holikatti <[email protected]>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version 2
> + * of the License, or (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
> + * GNU General Public License for more details.
> + *
> + * NO WARRANTY
> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
> + * solely responsible for determining the appropriateness of using and
> + * distributing the Program and assumes all risks associated with its
> + * exercise of rights under this Agreement, including but not limited to
> + * the risks and costs of program errors, damage to or loss of data,
> + * programs or equipment, and unavailability or interruption of operations.
> +
> + * DISCLAIMER OF LIABILITY
> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
> +
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
> + * USA.
> + */
> +
> +#ifndef _UFSHCI_H
> +#define _UFSHCI_H
> +
> +/* UFSHCI Registers */
> +enum {
> + ? ? ? REG_CONTROLLER_CAPABILITIES ? ? ? ? ? ? = 0x00,
> + ? ? ? REG_UFS_VERSION ? ? ? ? ? ? ? ? ? ? ? ? = 0x08,
> + ? ? ? REG_CONTROLLER_DEV_ID ? ? ? ? ? ? ? ? ? = 0x10,
> + ? ? ? REG_CONTROLLER_PROD_ID ? ? ? ? ? ? ? ? ?= 0x14,
> + ? ? ? REG_INTERRUPT_STATUS ? ? ? ? ? ? ? ? ? ?= 0x20,
> + ? ? ? REG_INTERRUPT_ENABLE ? ? ? ? ? ? ? ? ? ?= 0x24,
> + ? ? ? REG_CONTROLLER_STATUS ? ? ? ? ? ? ? ? ? = 0x30,
> + ? ? ? REG_CONTROLLER_ENABLE ? ? ? ? ? ? ? ? ? = 0x34,
> + ? ? ? REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER ? ?= 0x38,
> + ? ? ? REG_UIC_ERROR_CODE_DATA_LINK_LAYER ? ? ?= 0x3C,
> + ? ? ? REG_UIC_ERROR_CODE_NETWORK_LAYER ? ? ? ?= 0x40,
> + ? ? ? REG_UIC_ERROR_CODE_TRANSPORT_LAYER ? ? ?= 0x44,
> + ? ? ? REG_UIC_ERROR_CODE_DME ? ? ? ? ? ? ? ? ?= 0x48,
> + ? ? ? REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL ? ?= 0x4C,
> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_BASE_L ? ? ? ?= 0x50,
> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_BASE_H ? ? ? ?= 0x54,
> + ? ? ? REG_UTP_TRANSFER_REQ_DOOR_BELL ? ? ? ? ?= 0x58,
> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_CLEAR ? ? ? ? = 0x5C,
> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_RUN_STOP ? ? ?= 0x60,
> + ? ? ? REG_UTP_TASK_REQ_LIST_BASE_L ? ? ? ? ? ?= 0x70,
> + ? ? ? REG_UTP_TASK_REQ_LIST_BASE_H ? ? ? ? ? ?= 0x74,
> + ? ? ? REG_UTP_TASK_REQ_DOOR_BELL ? ? ? ? ? ? ?= 0x78,
> + ? ? ? REG_UTP_TASK_REQ_LIST_CLEAR ? ? ? ? ? ? = 0x7C,
> + ? ? ? REG_UTP_TASK_REQ_LIST_RUN_STOP ? ? ? ? ?= 0x80,
> + ? ? ? REG_UIC_COMMAND ? ? ? ? ? ? ? ? ? ? ? ? = 0x90,
> + ? ? ? REG_UIC_COMMAND_ARG_1 ? ? ? ? ? ? ? ? ? = 0x94,
> + ? ? ? REG_UIC_COMMAND_ARG_2 ? ? ? ? ? ? ? ? ? = 0x98,
> + ? ? ? REG_UIC_COMMAND_ARG_3 ? ? ? ? ? ? ? ? ? = 0x9C
> +};
> +
> +/* Controller capability masks */
> +enum {
> + ? ? ? MASK_TRANSFER_REQUESTS_SLOTS ? ? ? ? ? ?= 0x0000001F,
> + ? ? ? MASK_TASK_MANAGEMENT_REQUEST_SLOTS ? ? ?= 0x00070000,
> + ? ? ? MASK_64_ADDRESSING_SUPPORT ? ? ? ? ? ? ?= 0x01000000,
> + ? ? ? MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
> + ? ? ? MASK_UIC_DME_TEST_MODE_SUPPORT ? ? ? ? ?= 0x04000000
> +};
> +
> +/* UFS Version 08h */
> +#define MINOR_VERSION_NUM_MASK ? ? ? ? UFS_MASK(0xFFFF, 0)
> +#define MAJOR_VERSION_NUM_MASK ? ? ? ? UFS_MASK(0xFFFF, 16)
> +
> +/* Controller UFSHCI version */
> +enum {
> + ? ? ? UFSHCI_VERSION_10 = 0x00010000,
> + ? ? ? UFSHCI_VERSION_11 = 0x00010100
> +};
> +
> +/*
> + * HCDDID - Host Controller Identification Descriptor
> + * ? ? ? - Device ID and Device Class 10h
> + */
> +#define DEVICE_CLASS ? UFS_MASK(0xFFFF, 0)
> +#define DEVICE_ID ? ? ?UFS_MASK(0xFF, 24)
> +
> +/*
> + * HCPMID - Host Controller Identification Descriptor
> + * ? ? ? - Product/Manufacturer ID ?14h
> + */
> +#define MANUFACTURE_ID_MASK ? ?UFS_MASK(0xFFFF, 0)
> +#define PRODUCT_ID_MASK ? ? ? ? ? ? ? ?UFS_MASK(0xFFFF, 16)
> +
> +#define UFS_BIT(x) ? ? (1L << (x))
> +
> +#define UTP_TRANSFER_REQ_COMPL ? ? ? ? ? ? ? ? UFS_BIT(0)
> +#define UIC_DME_END_PT_RESET ? ? ? ? ? ? ? ? ? UFS_BIT(1)
> +#define UIC_ERROR ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(2)
> +#define UIC_TEST_MODE ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(3)
> +#define UIC_POWER_MODE ? ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(4)
> +#define UIC_HIBERNATE_EXIT ? ? ? ? ? ? ? ? ? ? UFS_BIT(5)
> +#define UIC_HIBERNATE_ENTER ? ? ? ? ? ? ? ? ? ?UFS_BIT(6)
> +#define UIC_LINK_LOST ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(7)
> +#define UIC_LINK_STARTUP ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(8)
> +#define UTP_TASK_REQ_COMPL ? ? ? ? ? ? ? ? ? ? UFS_BIT(9)
> +#define UIC_COMMAND_COMPL ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(10)
> +#define DEVICE_FATAL_ERROR ? ? ? ? ? ? ? ? ? ? UFS_BIT(11)
> +#define CONTROLLER_FATAL_ERROR ? ? ? ? ? ? ? ? UFS_BIT(16)
> +#define SYSTEM_BUS_FATAL_ERROR ? ? ? ? ? ? ? ? UFS_BIT(17)
> +
> +#define UFSHCD_ERROR_MASK ? ? ?(UIC_ERROR |\
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DEVICE_FATAL_ERROR |\
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONTROLLER_FATAL_ERROR |\
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SYSTEM_BUS_FATAL_ERROR)
> +
> +#define INT_FATAL_ERRORS ? ? ? (DEVICE_FATAL_ERROR |\
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONTROLLER_FATAL_ERROR |\
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SYSTEM_BUS_FATAL_ERROR)
> +
> +/* HCS - Host Controller Status 30h */
> +#define DEVICE_PRESENT ? ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(0)
> +#define UTP_TRANSFER_REQ_LIST_READY ? ? ? ? ? ?UFS_BIT(1)
> +#define UTP_TASK_REQ_LIST_READY ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(2)
> +#define UIC_COMMAND_READY ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(3)
> +#define HOST_ERROR_INDICATOR ? ? ? ? ? ? ? ? ? UFS_BIT(4)
> +#define DEVICE_ERROR_INDICATOR ? ? ? ? ? ? ? ? UFS_BIT(5)
> +#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK ?UFS_MASK(0x7, 8)
> +
> +/* HCE - Host Controller Enable 34h */
> +#define CONTROLLER_ENABLE ? ? ?UFS_BIT(0)
> +
> +/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
> +#define UIC_PHY_ADAPTER_LAYER_ERROR ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
> +#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK ? ? ? ? ?0x1F
> +
> +/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
> +#define UIC_DATA_LINK_LAYER_ERROR ? ? ? ? ? ? ?UFS_BIT(31)
> +#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK ? ?0x7FFF
> +#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT ? ? ?0x2000
> +
> +/* UECN - Host UIC Error Code Network Layer 40h */
> +#define UIC_NETWORK_LAYER_ERROR ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
> +#define UIC_NETWORK_LAYER_ERROR_CODE_MASK ? ? ?0x7
> +
> +/* UECT - Host UIC Error Code Transport Layer 44h */
> +#define UIC_TRANSPORT_LAYER_ERROR ? ? ? ? ? ? ?UFS_BIT(31)
> +#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK ? ?0x7F
> +
> +/* UECDME - Host UIC Error Code DME 48h */
> +#define UIC_DME_ERROR ? ? ? ? ? ? ? ? ?UFS_BIT(31)
> +#define UIC_DME_ERROR_CODE_MASK ? ? ? ? ? ? ? ?0x1
> +
> +#define INT_AGGR_TIMEOUT_VAL_MASK ? ? ? ? ? ? ?0xFF
> +#define INT_AGGR_COUNTER_THRESHOLD_MASK ? ? ? ? ? ? ? ?UFS_MASK(0x1F, 8)
> +#define INT_AGGR_COUNTER_AND_TIMER_RESET ? ? ? UFS_BIT(16)
> +#define INT_AGGR_STATUS_BIT ? ? ? ? ? ? ? ? ? ?UFS_BIT(20)
> +#define INT_AGGR_PARAM_WRITE ? ? ? ? ? ? ? ? ? UFS_BIT(24)
> +#define INT_AGGR_ENABLE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
> +
> +/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
> +#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT ? ? UFS_BIT(0)
> +
> +/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
> +#define UTP_TASK_REQ_LIST_RUN_STOP_BIT ? ? ? ? UFS_BIT(0)
> +
> +/* UICCMD - UIC Command */
> +#define COMMAND_OPCODE_MASK ? ? ? ? ? ?0xFF
> +#define GEN_SELECTOR_INDEX_MASK ? ? ? ? ? ? ? ?0xFFFF
> +
> +#define MIB_ATTRIBUTE_MASK ? ? ? ? ? ? UFS_MASK(0xFFFF, 16)
> +#define RESET_LEVEL ? ? ? ? ? ? ? ? ? ?0xFF
> +
> +#define ATTR_SET_TYPE_MASK ? ? ? ? ? ? UFS_MASK(0xFF, 16)
> +#define CONFIG_RESULT_CODE_MASK ? ? ? ? ? ? ? ?0xFF
> +#define GENERIC_ERROR_CODE_MASK ? ? ? ? ? ? ? ?0xFF
> +
> +/* UIC Commands */
> +enum {
> + ? ? ? UIC_CMD_DME_GET ? ? ? ? ? ? ? ? = 0x01,
> + ? ? ? UIC_CMD_DME_SET ? ? ? ? ? ? ? ? = 0x02,
> + ? ? ? UIC_CMD_DME_PEER_GET ? ? ? ? ? ?= 0x03,
> + ? ? ? UIC_CMD_DME_PEER_SET ? ? ? ? ? ?= 0x04,
> + ? ? ? UIC_CMD_DME_POWERON ? ? ? ? ? ? = 0x10,
> + ? ? ? UIC_CMD_DME_POWEROFF ? ? ? ? ? ?= 0x11,
> + ? ? ? UIC_CMD_DME_ENABLE ? ? ? ? ? ? ?= 0x12,
> + ? ? ? UIC_CMD_DME_RESET ? ? ? ? ? ? ? = 0x14,
> + ? ? ? UIC_CMD_DME_END_PT_RST ? ? ? ? ?= 0x15,
> + ? ? ? UIC_CMD_DME_LINK_STARTUP ? ? ? ?= 0x16,
> + ? ? ? UIC_CMD_DME_HIBER_ENTER ? ? ? ? = 0x17,
> + ? ? ? UIC_CMD_DME_HIBER_EXIT ? ? ? ? ?= 0x18,
> + ? ? ? UIC_CMD_DME_TEST_MODE ? ? ? ? ? = 0x1A
> +};
> +
> +/* UIC Config result code / Generic error code */
> +enum {
> + ? ? ? UIC_CMD_RESULT_SUCCESS ? ? ? ? ? ? ? ? ?= 0x00,
> + ? ? ? UIC_CMD_RESULT_INVALID_ATTR ? ? ? ? ? ? = 0x01,
> + ? ? ? UIC_CMD_RESULT_FAILURE ? ? ? ? ? ? ? ? ?= 0x01,
> + ? ? ? UIC_CMD_RESULT_INVALID_ATTR_VALUE ? ? ? = 0x02,
> + ? ? ? UIC_CMD_RESULT_READ_ONLY_ATTR ? ? ? ? ? = 0x03,
> + ? ? ? UIC_CMD_RESULT_WRITE_ONLY_ATTR ? ? ? ? ?= 0x04,
> + ? ? ? UIC_CMD_RESULT_BAD_INDEX ? ? ? ? ? ? ? ?= 0x05,
> + ? ? ? UIC_CMD_RESULT_LOCKED_ATTR ? ? ? ? ? ? ?= 0x06,
> + ? ? ? UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX ? = 0x07,
> + ? ? ? UIC_CMD_RESULT_PEER_COMM_FAILURE ? ? ? ?= 0x08,
> + ? ? ? UIC_CMD_RESULT_BUSY ? ? ? ? ? ? ? ? ? ? = 0x09,
> + ? ? ? UIC_CMD_RESULT_DME_FAILURE ? ? ? ? ? ? ?= 0x0A
> +};
> +
> +#define MASK_UIC_COMMAND_RESULT ? ? ? ? ? ? ? ? ? ? ? ?0xFF
> +
> +#define INT_AGGR_COUNTER_THRESHOLD_VALUE ? ? ? (0x1F << 8)
> +#define INT_AGGR_TIMEOUT_VALUE ? ? ? ? ? ? ? ? (0x02)
> +
> +/*
> + * Request Descriptor Definitions
> + */
> +
> +/* Transfer request command type */
> +enum {
> + ? ? ? UTP_CMD_TYPE_SCSI ? ? ? ? ? ? ? = 0x0,
> + ? ? ? UTP_CMD_TYPE_UFS ? ? ? ? ? ? ? ?= 0x1,
> + ? ? ? UTP_CMD_TYPE_DEV_MANAGE ? ? ? ? = 0x2
> +};
> +
> +enum {
> + ? ? ? UTP_SCSI_COMMAND ? ? ? ? ? ? ? ?= 0x00000000,
> + ? ? ? UTP_NATIVE_UFS_COMMAND ? ? ? ? ?= 0x10000000,
> + ? ? ? UTP_DEVICE_MANAGEMENT_FUNCTION ?= 0x20000000,
> + ? ? ? UTP_REQ_DESC_INT_CMD ? ? ? ? ? ?= 0x01000000
> +};
> +
> +/* UTP Transfer Request Data Direction (DD) */
> +enum {
> + ? ? ? UTP_NO_DATA_TRANSFER ? ?= 0x00000000,
> + ? ? ? UTP_HOST_TO_DEVICE ? ? ?= 0x02000000,
> + ? ? ? UTP_DEVICE_TO_HOST ? ? ?= 0x04000000
> +};
> +
> +/* Overall command status values */
> +enum {
> + ? ? ? OCS_SUCCESS ? ? ? ? ? ? ? ? ? ? = 0x0,
> + ? ? ? OCS_INVALID_CMD_TABLE_ATTR ? ? ?= 0x1,
> + ? ? ? OCS_INVALID_PRDT_ATTR ? ? ? ? ? = 0x2,
> + ? ? ? OCS_MISMATCH_DATA_BUF_SIZE ? ? ?= 0x3,
> + ? ? ? OCS_MISMATCH_RESP_UPIU_SIZE ? ? = 0x4,
> + ? ? ? OCS_PEER_COMM_FAILURE ? ? ? ? ? = 0x5,
> + ? ? ? OCS_ABORTED ? ? ? ? ? ? ? ? ? ? = 0x6,
> + ? ? ? OCS_FATAL_ERROR ? ? ? ? ? ? ? ? = 0x7,
> + ? ? ? OCS_INVALID_COMMAND_STATUS ? ? ?= 0x0F,
> + ? ? ? MASK_OCS ? ? ? ? ? ? ? ? ? ? ? ?= 0x0F
> +};
> +
> +/**
> + * struct ufshcd_sg_entry - UFSHCI PRD Entry
> + * @base_addr: Lower 32bit physical address DW-0
> + * @upper_addr: Upper 32bit physical address DW-1
> + * @reserved: Reserved for future use DW-2
> + * @size: size of physical segment DW-3
> + */
> +struct ufshcd_sg_entry {
> + ? ? ? u32 ? ?base_addr;
> + ? ? ? u32 ? ?upper_addr;
> + ? ? ? u32 ? ?reserved;
> + ? ? ? u32 ? ?size;
> +};
> +
> +/**
> + * struct utp_transfer_cmd_desc - UFS Commad Descriptor structure
> + * @command_upiu: Command UPIU Frame address
> + * @response_upiu: Response UPIU Frame address
> + * @prd_table: Physcial Region Descriptor
> + */
> +struct utp_transfer_cmd_desc {
> + ? ? ? u8 command_upiu[ALIGNED_UPIU_SIZE];
> + ? ? ? u8 response_upiu[ALIGNED_UPIU_SIZE];
> + ? ? ? struct ufshcd_sg_entry ? ?prd_table[SG_ALL];
> +};
> +
> +/**
> + * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
> + * @dword0: Descriptor Header DW0
> + * @dword1: Descriptor Header DW1
> + * @dword2: Descriptor Header DW2
> + * @dword3: Descriptor Header DW3
> + */
> +struct request_desc_header {
> + ? ? ? u32 dword_0;
> + ? ? ? u32 dword_1;
> + ? ? ? u32 dword_2;
> + ? ? ? u32 dword_3;
> +};
> +
> +/**
> + * struct utp_transfer_req_desc - UTRD structure
> + * @header: UTRD header DW-0 to DW-3
> + * @command_desc_base_addr_lo: UCD base address low DW-4
> + * @command_desc_base_addr_hi: UCD base address high DW-5
> + * @response_upiu_length: response UPIU length DW-6
> + * @response_upiu_offset: response UPIU offset DW-6
> + * @prd_table_length: Physical region descriptor length DW-7
> + * @prd_table_offset: Physical region descriptor offset DW-7
> + */
> +struct utp_transfer_req_desc {
> +
> + ? ? ? /* DW 0-3 */
> + ? ? ? struct request_desc_header header;
> +
> + ? ? ? /* DW 4-5*/
> + ? ? ? u32 ?command_desc_base_addr_lo;
> + ? ? ? u32 ?command_desc_base_addr_hi;
> +
> + ? ? ? /* DW 6 */
> + ? ? ? u16 ?response_upiu_length;
> + ? ? ? u16 ?response_upiu_offset;
> +
> + ? ? ? /* DW 7 */
> + ? ? ? u16 ?prd_table_length;
> + ? ? ? u16 ?prd_table_offset;
> +};
> +
> +/**
> + * struct utp_task_req_desc - UTMRD structure
> + * @header: UTMRD header DW-0 to DW-3
> + * @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
> + * @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
> + */
> +struct utp_task_req_desc {
> +
> + ? ? ? /* DW 0-3 */
> + ? ? ? struct request_desc_header header;
> +
> + ? ? ? /* DW 4-11 */
> + ? ? ? u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
> +
> + ? ? ? /* DW 12-19 */
> + ? ? ? u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
> +};
> +
> +#endif /* End of Header */
> --
> 1.7.5.4
>

2012-02-10 07:07:09

by Santosh Y

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On Fri, Feb 10, 2012 at 12:45 AM, Girish K S
<[email protected]> wrote:
> On 2 February 2012 10:27, Vinayak Holikatti <[email protected]> wrote:
>> From: Santosh Yaraganavi <[email protected]>
>>
>> This patch adds support for Universal Flash Storage(UFS)
>> host controllers. The UFS host controller driver
>> includes host controller initialization method.
>>
>> The Initialization process involves following steps:
>> ?- Initiate UFS Host Controller initialization process by writing
>> ? to Host controller enable register
>> ?- Configure UFS Host controller registers with host memory space
>> ? datastructure offsets.
>> ?- Unipro link startup procedure
>> ?- Check for connected device
>> ?- Configure UFS host controller to process requests
>> ?- Enable required interrupts
>> ?- Configure interrupt aggregation
>>
>> Signed-off-by: Santosh Yaraganavi <[email protected]>
>> Signed-off-by: Vinayak Holikatti <[email protected]>
>> Reviewed-by: Arnd Bergmann <[email protected]>
>> Reviewed-by: Saugata Das <[email protected]>
>> Reviewed-by: Vishak G <[email protected]>
>> Reviewed-by: Girish K S <[email protected]>
>> ---
>> ?drivers/scsi/Kconfig ? ? ?| ? ?1 +
>> ?drivers/scsi/Makefile ? ? | ? ?1 +
>> ?drivers/scsi/ufs/Kconfig ?| ? 49 ++
>> ?drivers/scsi/ufs/Makefile | ? ?2 +
>> ?drivers/scsi/ufs/ufs.h ? ?| ?203 +++++++++
>> ?drivers/scsi/ufs/ufshcd.c | 1091 +++++++++++++++++++++++++++++++++++++++++++++
>> ?drivers/scsi/ufs/ufshci.h | ?360 +++++++++++++++
>> ?7 files changed, 1707 insertions(+), 0 deletions(-)
>> ?create mode 100644 drivers/scsi/ufs/Kconfig
>> ?create mode 100644 drivers/scsi/ufs/Makefile
>> ?create mode 100644 drivers/scsi/ufs/ufs.h
>> ?create mode 100644 drivers/scsi/ufs/ufshcd.c
>> ?create mode 100644 drivers/scsi/ufs/ufshci.h
>>
>> diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
>> index 16570aa..477a91a 100644
>> --- a/drivers/scsi/Kconfig
>> +++ b/drivers/scsi/Kconfig
>> @@ -619,6 +619,7 @@ config SCSI_ARCMSR
>>
>> ?source "drivers/scsi/megaraid/Kconfig.megaraid"
>> ?source "drivers/scsi/mpt2sas/Kconfig"
>> +source "drivers/scsi/ufs/Kconfig"
>>
>> ?config SCSI_HPTIOP
>> ? ? ? ?tristate "HighPoint RocketRAID 3xxx/4xxx Controller support"
>> diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
>> index 2b88749..c832974 100644
>> --- a/drivers/scsi/Makefile
>> +++ b/drivers/scsi/Makefile
>> @@ -108,6 +108,7 @@ obj-$(CONFIG_MEGARAID_LEGACY) ? ? ? += megaraid.o
>> ?obj-$(CONFIG_MEGARAID_NEWGEN) ?+= megaraid/
>> ?obj-$(CONFIG_MEGARAID_SAS) ? ? += megaraid/
>> ?obj-$(CONFIG_SCSI_MPT2SAS) ? ? += mpt2sas/
>> +obj-$(CONFIG_SCSI_UFSHCD) ? ? ?+= ufs/
>> ?obj-$(CONFIG_SCSI_ACARD) ? ? ? += atp870u.o
>> ?obj-$(CONFIG_SCSI_SUNESP) ? ? ?+= esp_scsi.o ? sun_esp.o
>> ?obj-$(CONFIG_SCSI_GDTH) ? ? ? ? ? ? ? ?+= gdth.o
>> diff --git a/drivers/scsi/ufs/Kconfig b/drivers/scsi/ufs/Kconfig
>> new file mode 100644
>> index 0000000..8f27f9d
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/Kconfig
>> @@ -0,0 +1,49 @@
>> +#
>> +# Kernel configuration file for the UFS Host Controller
>> +#
>> +# This code is based on drivers/scsi/ufs/Kconfig
>> +# Copyright (C) 2011 ?Samsung Samsung India Software Operations
>> +#
>> +# Santosh Yaraganavi <[email protected]>
>> +# Vinayak Holikatti <[email protected]>
>> +
>> +# This program is free software; you can redistribute it and/or
>> +# modify it under the terms of the GNU General Public License
>> +# as published by the Free Software Foundation; either version 2
>> +# of the License, or (at your option) any later version.
>> +
>> +# This program is distributed in the hope that it will be useful,
>> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
>> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> +# GNU General Public License for more details.
>> +
>> +# NO WARRANTY
>> +# THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
>> +# CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
>> +# LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
>> +# MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
>> +# solely responsible for determining the appropriateness of using and
>> +# distributing the Program and assumes all risks associated with its
>> +# exercise of rights under this Agreement, including but not limited to
>> +# the risks and costs of program errors, damage to or loss of data,
>> +# programs or equipment, and unavailability or interruption of operations.
>> +
>> +# DISCLAIMER OF LIABILITY
>> +# NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
>> +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
>> +# DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
>> +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
>> +# TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>> +# USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
>> +# HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
>> +
>> +# You should have received a copy of the GNU General Public License
>> +# along with this program; if not, write to the Free Software
>> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
>> +# USA.
>> +
>> +config SCSI_UFSHCD
>> + ? ? ? tristate "Universal Flash Storage host controller driver"
>> + ? ? ? depends on PCI && SCSI
>> + ? ? ? ---help---
>> + ? ? ? This is a generic driver which supports PCIe UFS Host controllers.
>> diff --git a/drivers/scsi/ufs/Makefile b/drivers/scsi/ufs/Makefile
>> new file mode 100644
>> index 0000000..adf7895
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/Makefile
>> @@ -0,0 +1,2 @@
>> +# UFSHCD makefile
>> +obj-$(CONFIG_SCSI_UFSHCD) += ufshcd.o
>> diff --git a/drivers/scsi/ufs/ufs.h b/drivers/scsi/ufs/ufs.h
>> new file mode 100644
>> index 0000000..96b5cae
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/ufs.h
>> @@ -0,0 +1,203 @@
>> +/*
>> + * Universal Flash Storage Host controller driver
>> + *
>> + * This code is based on drivers/scsi/ufs/ufs.h
>> + * Copyright (C) 2011-2012 Samsung India Software Operations
>> + *
>> + * Santosh Yaraganavi <[email protected]>
>> + * Vinayak Holikatti <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> + * GNU General Public License for more details.
>> + *
>> + * NO WARRANTY
>> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
>> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
>> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
>> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
>> + * solely responsible for determining the appropriateness of using and
>> + * distributing the Program and assumes all risks associated with its
>> + * exercise of rights under this Agreement, including but not limited to
>> + * the risks and costs of program errors, damage to or loss of data,
>> + * programs or equipment, and unavailability or interruption of operations.
>> +
>> + * DISCLAIMER OF LIABILITY
>> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
>> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
>> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
>> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
>> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
>> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
>> +
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
>> + * USA.
>> + */
>> +
>> +#ifndef _UFS_H
>> +#define _UFS_H
>> +
>> +#define TASK_REQ_UPIU_SIZE_DWORDS ? ? ?8
>> +#define TASK_RSP_UPIU_SIZE_DWORDS ? ? ?8
>> +
>> +#define MAX_CDB_SIZE ? ? ? ? ? 16
>> +#define ALIGNED_UPIU_SIZE ? ? ?128
>> +
>> +#define UPIU_HEADER_DWORD(byte3, byte2, byte1, byte0)\
>> + ? ? ? ? ? ? ? ? ? ? ? ((byte3 << 24) | (byte2 << 16) |\
>> + ? ? ? ? ? ? ? ? ? ? ? ?(byte1 << 8) | (byte0))
>> +
>> +/*
>> + * UFS Protocol Information Unit related definitions
>> + */
>> +
>> +/* Task management functions */
>> +enum {
>> + ? ? ? UFS_ABORT_TASK ? ? ? ? ?= 0x01,
>> + ? ? ? UFS_ABORT_TASK_SET ? ? ?= 0x02,
>> + ? ? ? UFS_CLEAR_TASK_SET ? ? ?= 0x04,
>> + ? ? ? UFS_LOGICAL_RESET ? ? ? = 0x08,
>> + ? ? ? UFS_QUERY_TASK ? ? ? ? ?= 0x80,
>> + ? ? ? UFS_QUERY_TASK_SET ? ? ?= 0x81
>> +};
>> +
>> +/* UTP UPIU Transaction Codes Initiator to Target */
>> +enum {
>> + ? ? ? UPIU_TRANSACTION_NOP_OUT ? ? ? ?= 0x00,
>> + ? ? ? UPIU_TRANSACTION_COMMAND ? ? ? ?= 0x01,
>> + ? ? ? UPIU_TRANSACTION_DATA_OUT ? ? ? = 0x02,
>> + ? ? ? UPIU_TRANSACTION_TASK_REQ ? ? ? = 0x04,
>> + ? ? ? UPIU_TRANSACTION_QUERY_REQ ? ? ?= 0x26
>> +};
>> +
>> +/* UTP UPIU Transaction Codes Target to Initiator */
>> +enum {
>> + ? ? ? UPIU_TRANSACTION_NOP_IN ? ? ? ? = 0x20,
>> + ? ? ? UPIU_TRANSACTION_RESPONSE ? ? ? = 0x21,
>> + ? ? ? UPIU_TRANSACTION_DATA_IN ? ? ? ?= 0x22,
>> + ? ? ? UPIU_TRANSACTION_TASK_RSP ? ? ? = 0x24,
>> + ? ? ? UPIU_TRANSACTION_READY_XFER ? ? = 0x31,
>> + ? ? ? UPIU_TRANSACTION_QUERY_RSP ? ? ?= 0x36
>> +};
>> +
>> +/* UPIU Read/Write flags */
>> +enum {
>> + ? ? ? UPIU_CMD_FLAGS_READ ? ? = 0x40,
>> + ? ? ? UPIU_CMD_FLAGS_WRITE ? ?= 0x20
>> +
>> +};
>> +
>> +/* UPIU Task Attributes */
>> +enum {
>> + ? ? ? UPIU_TASK_ATTR_SIMPLE ? = 0x00,
>> + ? ? ? UPIU_TASK_ATTR_ORDERED ?= 0x01,
>> + ? ? ? UPIU_TASK_ATTR_HEADQ ? ?= 0x02,
>> + ? ? ? UPIU_TASK_ATTR_ACA ? ? ?= 0x03
>> +};
>> +
>> +/* UTP QUERY Transaction Specific Fields OpCode */
>> +enum {
>> + ? ? ? UPIU_QUERY_OPCODE_NOP ? ? ? ? ? = 0x0,
>> + ? ? ? UPIU_QUERY_OPCODE_READ_DESC ? ? = 0x1,
>> + ? ? ? UPIU_QUERY_OPCODE_WRITE_DESC ? ?= 0x2,
>> + ? ? ? UPIU_QUERY_OPCODE_READ_ATTR ? ? = 0x3,
>> + ? ? ? UPIU_QUERY_OPCODE_WRITE_ATTR ? ?= 0x4,
>> + ? ? ? UPIU_QUERY_OPCODE_READ_FLAG ? ? = 0x5,
>> + ? ? ? UPIU_QUERY_OPCODE_SET_FLAG ? ? ?= 0x6,
>> + ? ? ? UPIU_QUERY_OPCODE_CLEAR_FLAG ? ?= 0x7,
>> + ? ? ? UPIU_QUERY_OPCODE_TOGGLE_FLAG ? = 0x8
>> +};
>> +
>> +/* UTP Transfer Request Command Type (CT) */
>> +enum {
>> + ? ? ? UPIU_COMMAND_SET_TYPE_SCSI ? ? ?= 0x0,
>> + ? ? ? UPIU_COMMAND_SET_TYPE_UFS ? ? ? = 0x1,
>> + ? ? ? UPIU_COMMAND_SET_TYPE_QUERY ? ? = 0x2
>> +};
>> +
>> +enum {
>> + ? ? ? MASK_SCSI_STATUS ? ? ? ?= 0xFF,
>> + ? ? ? MASK_TASK_RESPONSE ? ? ?= 0xFF00,
>> + ? ? ? MASK_RSP_UPIU_RESULT ? ?= 0xFFFF
>> +};
>> +
>> +/**
>> + * struct utp_upiu_header - UPIU header structure
>> + * @dword_0: UPIU header DW-0
>> + * @dword_1: UPIU header DW-1
>> + * @dword_2: UPIU header DW-2
>> + */
>> +struct utp_upiu_header {
>> + ? ? ? u32 dword_0;
>> + ? ? ? u32 dword_1;
>> + ? ? ? u32 dword_2;
>> +};
>> +
>> +/**
>> + * struct utp_upiu_cmd - Command UPIU structure
>> + * @header: UPIU header structure DW-0 to DW-2
>> + * @data_transfer_len: Data Transfer Length DW-3
>> + * @cdb: Command Descriptor Block CDB DW-4 to DW-7
>> + */
>> +struct utp_upiu_cmd {
>> + ? ? ? struct utp_upiu_header header;
>> + ? ? ? u32 exp_data_transfer_len;
>> + ? ? ? u8 cdb[MAX_CDB_SIZE];
>> +};
>> +
>> +/**
>> + * struct utp_upiu_rsp - Response UPIU structure
>> + * @header: UPIU header DW-0 to DW-2
>> + * @residual_transfer_count: Residual transfer count DW-3
>> + * @reserved: Reserver DW-4 to DW-7
>> + * @sense_data_len: Sense data length DW-8 U16
>> + * @sense_data: Sense data field DW-8 to DW-12
>> + */
>> +struct utp_upiu_rsp {
>> + ? ? ? struct utp_upiu_header header;
>> + ? ? ? u32 residual_transfer_count;
>> + ? ? ? u32 reserved[4];
>> + ? ? ? u16 sense_data_len;
>> + ? ? ? u8 sense_data[18];
>> +};
>> +
>> +/**
>> + * struct utp_upiu_task_req - Task request UPIU structure
>> + * @header - UPIU header structure DW0 to DW-2
>> + * @input_param1: Input param 1 DW-3
>> + * @input_param2: Input param 2 DW-4
>> + * @input_param3: Input param 3 DW-5
>> + * @reserved: Reserver DW-6 to DW-7
>> + */
>> +struct utp_upiu_task_req {
>> + ? ? ? struct utp_upiu_header header;
>> + ? ? ? u32 input_param1;
>> + ? ? ? u32 input_param2;
>> + ? ? ? u32 input_param3;
>> + ? ? ? u32 reserved[2];
>> +};
>> +
>> +/**
>> + * struct utp_upiu_task_rsp - Task Management Response UPIU structure
>> + * @header: UPIU header structure DW0-DW-2
>> + * @output_param1: Ouput param 1 DW3
>> + * @output_param2: Output param 2 DW4
>> + * @reserved: Reserver DW-5 to DW-7
>> + */
>> +struct utp_upiu_task_rsp {
>> + ? ? ? struct utp_upiu_header header;
>> + ? ? ? u32 output_param1;
>> + ? ? ? u32 output_param2;
>> + ? ? ? u32 reserved[3];
>> +};
>> +
>> +#endif /* End of Header */
>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>> new file mode 100644
>> index 0000000..c82eeea
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/ufshcd.c
>> @@ -0,0 +1,1091 @@
>> +/*
>> + * Universal Flash Storage Host controller driver
>> + *
>> + * This code is based on drivers/scsi/ufs/ufshcd.c
>> + * Copyright (C) 2011-2012 Samsung India Software Operations
>> + *
>> + * Santosh Yaraganavi <[email protected]>
>> + * Vinayak Holikatti <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> + * GNU General Public License for more details.
>> + *
>> + * NO WARRANTY
>> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
>> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
>> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
>> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
>> + * solely responsible for determining the appropriateness of using and
>> + * distributing the Program and assumes all risks associated with its
>> + * exercise of rights under this Agreement, including but not limited to
>> + * the risks and costs of program errors, damage to or loss of data,
>> + * programs or equipment, and unavailability or interruption of operations.
>> +
>> + * DISCLAIMER OF LIABILITY
>> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
>> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
>> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
>> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
>> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
>> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
>> +
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
>> + * USA.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/init.h>
>> +#include <linux/pci.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/workqueue.h>
>> +#include <linux/errno.h>
>> +#include <linux/types.h>
>> +#include <linux/wait.h>
>> +
>> +#include <asm/irq.h>
>> +#include <asm/byteorder.h>
>> +#include <scsi/scsi.h>
>> +#include <scsi/scsi_cmnd.h>
>> +#include <scsi/scsi_host.h>
>> +#include <scsi/scsi_dbg.h>
>> +
>> +#include "ufs.h"
>> +#include "ufshci.h"
>> +
>> +#define UFSHCD "ufshcd"
>> +#define UFSHCD_DRIVER_VERSION "0.1"
>> +
>> +#ifndef NULL
>> +#define NULL 0
>> +#endif ?/* NULL */
>> +
>> +#define BYTES_TO_DWORDS(p) ? ? (p >> 2)
>> +#define UFSHCD_MMIO_BASE ? ? ? (hba->mmio_base)
>> +
>> +enum {
>> + ? ? ? UFSHCD_MAX_CHANNEL ? ? ?= 1,
>> + ? ? ? UFSHCD_MAX_ID ? ? ? ? ? = 1,
>> + ? ? ? UFSHCD_MAX_LUNS ? ? ? ? = 8,
>> + ? ? ? UFSHCD_CAN_QUEUE ? ? ? ?= 32,
>> + ? ? ? BYTES_128 ? ? ? ? ? ? ? = 128,
>> + ? ? ? BYTES_1024 ? ? ? ? ? ? ?= 1024
>> +};
>> +
>> +/* UFSHCD states */
>> +enum {
>> + ? ? ? UFSHCD_STATE_OPERATIONAL,
>> + ? ? ? UFSHCD_STATE_RESET,
>> + ? ? ? UFSHCD_STATE_ERROR
>> +};
>> +
>> +/* Interrupt configuration options */
>> +enum {
>> + ? ? ? UFSHCD_INT_DISABLE,
>> + ? ? ? UFSHCD_INT_ENABLE,
>> + ? ? ? UFSHCD_INT_CLEAR
>> +};
>> +
>> +/* Interrupt aggregation options */
>> +enum {
>> + ? ? ? INT_AGGR_RESET,
>> + ? ? ? INT_AGGR_CONFIG
>> +};
>> +
>> +/**
>> + * struct uic_command - UIC command structure
>> + * @command: UIC command
>> + * @argument1: UIC command argument 1
>> + * @argument2: UIC command argument 2
>> + * @argument3: UIC command argument 3
>> + * @cmd_active: Indicate if UIC command is outstanding
>> + * @result: UIC command result
>> + * @callback: routine to be called when UIC command completes
>> + */
>> +struct uic_command {
>> + ? ? ? u32 command;
>> + ? ? ? u32 argument1;
>> + ? ? ? u32 argument2;
>> + ? ? ? u32 argument3;
>> + ? ? ? int cmd_active;
>> + ? ? ? int result;
>> +};
>> +
>> +/**
>> + * struct ufs_hba - per adapter private structure
>> + * @mmio_base: UFSHCI base register address
>> + * @ucdl_virt_addr: UFS Command Descriptor virtual address
>> + * @utrdl_virt_addr: UTP Transfer Request Descriptor virtual address
>> + * @utmrdl_virt_addr: UTP Task Management Descriptor virtual address
>> + * @utrdl_virt_addr_aligned: UTRD Aligned vitual address
>> + * @utmrdl_virt_addr_aligned: UTMRD Aligned virtual address
>> + * @ucdl_size: Memory size of UCD command block
>> + * @utrdl_size: Memory size of UTRDL block
>> + * @utmrdl_size: Memory size of UTMRDL block
>> + * @ucdl_dma_addr: UFS Command Descriptor DMA address
>> + * @utrdl_dma_addr: UTRDL DMA address
>> + * @utmrdl_dma_addr: UTMRDL DMA address
>> + * @utrdl_dma_addr_aligned: UTRDL aligned DMA address
>> + * @utmrdl_dma_addr_aligned: UTMRDL aligned DMA address
>> + * @ucdl_dma_addr_aligned: UCD aligned DMA address
>> + * @dma_size:
>> + * @host: Scsi_Host instance of the driver
>> + * @pdev: PCI device handle
>> + * @lrb: local reference block
>> + * @capabilities: UFS Controller Capabilities
>> + * @nutrs: Transfer Request Queue depth supported by controller
>> + * @nutmrs: Task Management Queue depth supported by controller
>> + * @active_uic_cmd: handle of active UIC command
>> + * @ufshcd_state: UFSHCD states
>> + * @int_enable_mask: Interrupt Mask Bits
>> + * @uic_workq: Work queue for UIC completion handling
>> + */
>> +struct ufs_hba {
>> + ? ? ? void __iomem *mmio_base;
>> +
>> + ? ? ? /* Virtual memory reference */
>> + ? ? ? void *ucdl_virt_addr;
>> + ? ? ? void *utrdl_virt_addr;
>> + ? ? ? void *utmrdl_virt_addr;
>> + ? ? ? void *utrdl_virt_addr_aligned;
>> + ? ? ? void *utmrdl_virt_addr_aligned;
>> + ? ? ? void *ucdl_virt_addr_aligned;
>> +
>> + ? ? ? size_t ucdl_size;
>> + ? ? ? size_t utrdl_size;
>> + ? ? ? size_t utmrdl_size;
>> +
>> + ? ? ? /* DMA memory reference */
>> + ? ? ? dma_addr_t ucdl_dma_addr;
>> + ? ? ? dma_addr_t utrdl_dma_addr;
>> + ? ? ? dma_addr_t utmrdl_dma_addr;
>> + ? ? ? dma_addr_t utrdl_dma_addr_aligned;
>> + ? ? ? dma_addr_t utmrdl_dma_addr_aligned;
>> + ? ? ? dma_addr_t ucdl_dma_addr_aligned;
>> +
>> + ? ? ? size_t dma_size;
>> +
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? struct pci_dev *pdev;
>> +
>> + ? ? ? struct ufshcd_lrb *lrb;
>> +
>> + ? ? ? u32 capabilities;
>> + ? ? ? int nutrs;
>> + ? ? ? int nutmrs;
>> + ? ? ? u32 ufs_version;
>> +
>> + ? ? ? struct uic_command active_uic_cmd;
>> +
>> + ? ? ? u32 ufshcd_state;
>> + ? ? ? u32 int_enable_mask;
>> +
>> + ? ? ? /* Work Queues */
>> + ? ? ? struct work_struct uic_workq;
>> +};
>> +
>> +/**
>> + * struct ufshcd_lrb - command control block
>> + * @utr_descriptor_ptr: UTRD address of the command
>> + * @ucd_cmd_ptr: UCD address of the command
>> + * @ucd_rsp_ptr: Response UPIU address for this command
>> + * @ucd_prdt_ptr: PRDT address of the command
>> + */
>> +struct ufshcd_lrb {
>> + ? ? ? struct utp_transfer_req_desc *utr_descriptor_ptr;
>> + ? ? ? struct utp_upiu_cmd *ucd_cmd_ptr;
>> + ? ? ? struct utp_upiu_rsp *ucd_rsp_ptr;
>> + ? ? ? struct ufshcd_sg_entry *ucd_prdt_ptr;
>> +};
>> +
>> +/**
>> + * ufshcd_get_ufs_version - Get the UFS version supported by the HBA
>> + * @hba - Pointer to adapter instance
>> + *
>> + * Returns UFSHCI version supported by the controller
>> + */
>> +static inline u32 ufshcd_get_ufs_version(struct ufs_hba *hba)
>> +{
>> + ? ? ? return readl(UFSHCD_MMIO_BASE + REG_UFS_VERSION);
>> +}
>> +
>> +/**
>> + * ufshcd_is_device_present - Check if any device connected to
>> + * ? ? ? ? ? ? ? ? ? ? ? ? ? the host controller
>> + * @reg_hcs - host controller status register value
>> + *
>> + * Returns 0 if device present, non-zeo if no device detected
>> + */
>> +static inline int ufshcd_is_device_present(u32 reg_hcs)
>> +{
>> + ? ? ? return (DEVICE_PRESENT & reg_hcs) ? 0 : -1;
>> +}
>> +
>> +/**
>> + * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
>> + * @reg: Register value of host controller status
>> + *
>> + * Returns integer, 0 on Success and positive value if failed
>> + */
>> +static inline int ufshcd_get_lists_status(u32 reg)
>> +{
>> + ? ? ? /*
>> + ? ? ? ?* The mask 0xFF is for the following HCS register bits
>> + ? ? ? ?* Bit ? ? ? ? ?Description
>> + ? ? ? ?* ?0 ? ? ? ? ? Device Present
>> + ? ? ? ?* ?1 ? ? ? ? ? UTRLRDY
>> + ? ? ? ?* ?2 ? ? ? ? ? UTMRLRDY
>> + ? ? ? ?* ?3 ? ? ? ? ? UCRDY
>> + ? ? ? ?* ?4 ? ? ? ? ? HEI
>> + ? ? ? ?* ?5 ? ? ? ? ? DEI
>> + ? ? ? ?* 6-7 ? ? ? ? ?reserved
>> + ? ? ? ?*/
>> + ? ? ? return (((reg) & (0xFF)) >> 1) ^ (0x07);
>> +}
>> +
>> +/**
>> + * ufshcd_get_uic_cmd_result - Get the UIC command result
>> + * @hba: Pointer to adapter instance
>> + *
>> + * This function gets the result of UIC command completion
>> + * Returns 0 on success, non zero value on error
>> + */
>> +static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba)
>> +{
>> + ? ? ? return readl(UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2) &
>> + ? ? ? ? ? ? ?MASK_UIC_COMMAND_RESULT;
>> +}
>> +
>> +/**
>> + * ufshcd_free_hba_memory - Free allocated memory for LRB request
>> + * ? ? ? ? ? ? ? ? ? ? ? ? and task lists
>> + * @hba: Pointer to adapter instance
>> + */
>> +static inline void ufshcd_free_hba_memory(struct ufs_hba *hba)
>> +{
>> + ? ? ? kfree(hba->lrb);
>> + ? ? ? hba->lrb = NULL;
>> +
>> + ? ? ? if (hba->utmrdl_virt_addr_aligned) {
>> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
>> + ? ? ? ? ? ? ? hba->utmrdl_virt_addr = NULL;
>> + ? ? ? ? ? ? ? hba->utmrdl_virt_addr_aligned = NULL;
>> + ? ? ? }
>> +
>> + ? ? ? if (hba->utrdl_virt_addr_aligned) {
>> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_virt_addr, hba->utrdl_dma_addr);
>> + ? ? ? ? ? ? ? hba->utrdl_virt_addr = NULL;
>> + ? ? ? ? ? ? ? hba->utrdl_virt_addr_aligned = NULL;
>> + ? ? ? }
>> +
>> + ? ? ? if (hba->ucdl_virt_addr_aligned) {
>> + ? ? ? ? ? ? ? dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->ucdl_virt_addr, hba->ucdl_dma_addr);
>> + ? ? ? ? ? ? ? hba->ucdl_virt_addr = NULL;
>> + ? ? ? ? ? ? ? hba->ucdl_virt_addr_aligned = NULL;
>> + ? ? ? }
>> +}
>> +
>> +/**
>> + * ufshcd_config_int_aggr - Configure interrupt aggregation values
>> + * ? ? ? ? ? ? currently there is no use case where we want to configure
>> + * ? ? ? ? ? ? interrupt aggregation dynamically. So to configure interrupt
>> + * ? ? ? ? ? ? aggregation, #define INT_AGGR_COUNTER_THRESHOLD_VALUE and
>> + * ? ? ? ? ? ? INT_AGGR_TIMEOUT_VALUE are used.
>> + * @hba: per adapter instance
>> + * @option: Interrupt aggregation option
>> + */
>> +static inline void
>> +ufshcd_config_int_aggr(struct ufs_hba *hba, int option)
>> +{
>> + ? ? ? switch (option) {
>> + ? ? ? case INT_AGGR_RESET:
>> + ? ? ? ? ? ? ? writel((INT_AGGR_ENABLE |
>> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_COUNTER_AND_TIMER_RESET),
>> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case INT_AGGR_CONFIG:
>> + ? ? ? ? ? ? ? writel((INT_AGGR_ENABLE |
>> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_PARAM_WRITE |
>> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_COUNTER_THRESHOLD_VALUE |
>> + ? ? ? ? ? ? ? ? ? ? ? INT_AGGR_TIMEOUT_VALUE),
>> + ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? ?REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL));
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? }
>> +}
>> +
>> +/**
>> + * ufshcd_hba_stop - put the controller in reset state
>> + * @hba: per adapter instance
>> + */
>> +static inline void ufshcd_hba_stop(struct ufs_hba *hba)
>> +{
>> + ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
>> +}
>> +
>> +/**
>> + * ufshcd_hba_capabilities - Read controller capabilities
>> + * @hba: per adapter instance
>> + */
>> +static inline void ufshcd_hba_capabilities(struct ufs_hba *hba)
>> +{
>> + ? ? ? u32 capabilities;
>> +
>> + ? ? ? capabilities =
>> + ? ? ? ? ? ? ? readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_CAPABILITIES);
>> + ? ? ? hba->capabilities = capabilities;
>> +
>> + ? ? ? /* nutrs and nutmrs are 0 based values */
>> + ? ? ? hba->nutrs = (capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1;
>> + ? ? ? hba->nutmrs =
>> + ? ? ? ((capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS) >> 16) + 1;
>> +}
>> +
>> +/**
>> + * ufshcd_send_uic_command - Send UIC commands to unipro layers
>> + * @hba: per adapter instance
>> + * @uic_command: UIC command
>> + */
>> +static inline void
>> +ufshcd_send_uic_command(struct ufs_hba *hba, struct uic_command *uic_cmnd)
>> +{
>> + ? ? ? /* Clear interrupt status register */
>> + ? ? ? writel((readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS)),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
>> +
>> + ? ? ? /* Write Args */
>> + ? ? ? writel(uic_cmnd->argument1,
>> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_1));
>> + ? ? ? writel(uic_cmnd->argument2,
>> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_2));
>> + ? ? ? writel(uic_cmnd->argument3,
>> + ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_UIC_COMMAND_ARG_3));
>> +
>> + ? ? ? /* Write UIC Cmd */
>> + ? ? ? writel((uic_cmnd->command & COMMAND_OPCODE_MASK),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UIC_COMMAND));
>> +}
>> +
>> +/**
>> + * ufshcd_int_config - enable/disable interrupts
>> + * @hba: per adapter instance
>> + * @option: interrupt option
>> + */
>> +static void ufshcd_int_config(struct ufs_hba *hba, u32 option)
>> +{
>> + ? ? ? switch (option) {
>> + ? ? ? case UFSHCD_INT_ENABLE:
>> + ? ? ? ? ? ? ? writel(hba->int_enable_mask,
>> + ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? case UFSHCD_INT_DISABLE:
>> + ? ? ? ? ? ? ? if (UFSHCI_VERSION_10 == hba->ufs_version)
>> + ? ? ? ? ? ? ? ? ? ? ? writel(readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE),
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
>> + ? ? ? ? ? ? ? else
>> + ? ? ? ? ? ? ? ? ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_INTERRUPT_ENABLE));
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? default:
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "Invalid interrupt option\n");
>> + ? ? ? ? ? ? ? break;
>> + ? ? ? } /* end of switch */
>> +}
>> +
>> +/**
>> + * ufshcd_memory_alloc - allocate memory for host memory space data structures
>> + * @hba: per adapter instance
>> + *
>> + * 1) Allocate DMA memory for Command Descriptor array
>> + * ? ? Each command descriptor consist of Command UPIU, Response UPIU and PRDT
>> + * 2) Align allocated command descriptor address to 128 byte align.
>> + * 3) Allocate DMA memory for UTP Transfer Request Descriptor List (UTRDL).
>> + * 4) Align UTRDL address to 1KB (UFSHCI spec)
>> + * 5) Allocate DMA memory for UTP Task Management Request Descriptor List
>> + * ? ? (UTMRDL)
>> + * 6) Align UTMRDL address to 1KB (UFSHCI spec)
>> + * 7) Allocate the memory for local reference block(lrb).
>> + *
>> + * Returns 0 for success, non-zero in case of failure
>> + */
>> +static int ufshcd_memory_alloc(struct ufs_hba *hba)
>> +{
>> + ? ? ? /*
>> + ? ? ? ?* Allocate memory for UTP command descriptors.
>> + ? ? ? ?* UFSHCI requires 128 byte alignement of UCD and
>> + ? ? ? ?* 64 byte alignement for PRDT. So allocating extra 128 bytes
>> + ? ? ? ?*/
>> + ? ? ? hba->ucdl_size =
>> + ? ? ? (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs) + BYTES_128;
>> + ? ? ? hba->ucdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?hba->ucdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&hba->ucdl_dma_addr,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GFP_KERNEL);
>> + ? ? ? if (NULL == hba->ucdl_virt_addr) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? "Command Descriptor Memory allocation failed\n");
>> + ? ? ? ? ? ? ? goto ucd_fail;
>> + ? ? ? }
>> +
>> + ? ? ? /* Align UCD to 128 bytes */
>> + ? ? ? hba->ucdl_virt_addr_aligned =
>> + ? ? ? (void *) ALIGN((unsigned long) hba->ucdl_virt_addr, BYTES_128);
>> + ? ? ? hba->ucdl_dma_addr_aligned = ALIGN(hba->ucdl_dma_addr, BYTES_128);
>> +
>> + ? ? ? /*
>> + ? ? ? ?* Allocate memory for UTP Transfer descriptors.
>> + ? ? ? ?* UFSHCI requires 1kb alignement of UTRD. So allocating
>> + ? ? ? ?* extra 1024 bytes
>> + ? ? ? ?*/
>> + ? ? ? hba->utrdl_size =
>> + ? ? ? (sizeof(struct utp_transfer_req_desc) * hba->nutrs) + BYTES_1024;
>> + ? ? ? hba->utrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? &hba->utrdl_dma_addr,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? GFP_KERNEL);
>> + ? ? ? if (NULL == hba->utrdl_virt_addr) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? "Transfer Descriptor Memory allocation failed\n");
>> + ? ? ? ? ? ? ? goto utrd_fail;
>> + ? ? ? }
>> +
>> + ? ? ? /* alignement UTRD to 1kb */
>> + ? ? ? hba->utrdl_virt_addr_aligned =
>> + ? ? ? (void *) ALIGN((unsigned long) hba->utrdl_virt_addr, BYTES_1024);
>> + ? ? ? hba->utrdl_dma_addr_aligned = ALIGN(hba->utrdl_dma_addr, BYTES_1024);
>> +
>> + ? ? ? /*
>> + ? ? ? ?* Allocate memory for UTP Task Management descriptors
>> + ? ? ? ?* UFSHCI requires 1kb alignement of UTMRD. So allocating
>> + ? ? ? ?* extra 1024 bytes
>> + ? ? ? ?*/
>> + ? ? ? hba->utmrdl_size =
>> + ? ? ? sizeof(struct utp_task_req_desc) * hba->nutmrs + BYTES_1024;
>> + ? ? ? hba->utmrdl_virt_addr = dma_alloc_coherent(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?hba->utmrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?&hba->utmrdl_dma_addr,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?GFP_KERNEL);
>> + ? ? ? if (NULL == hba->utmrdl_virt_addr) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? "Task Management Descriptor Memory allocation failed\n");
>> + ? ? ? ? ? ? ? goto utmrd_fail;
>> + ? ? ? }
>> +
>> + ? ? ? /* alignement UTMRD to 1kb */
>> + ? ? ? hba->utmrdl_virt_addr_aligned =
>> + ? ? ? (void *) ALIGN((unsigned long) hba->utmrdl_virt_addr, BYTES_1024);
>> + ? ? ? hba->utmrdl_dma_addr_aligned = ALIGN(hba->utmrdl_dma_addr, BYTES_1024);
>> +
>> + ? ? ? /* Allocate memory for local reference block */
>> + ? ? ? hba->lrb = kcalloc(hba->nutrs, sizeof(struct ufshcd_lrb), GFP_KERNEL);
>> + ? ? ? if (NULL == hba->lrb) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "LRB Memory allocation failed\n");
>> + ? ? ? ? ? ? ? goto lrb_fail;
>> + ? ? ? }
>> +
>> + ? ? ? return 0;
>> +
>> +lrb_fail:
>> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utmrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? hba->utmrdl_virt_addr, hba->utmrdl_dma_addr);
>> + ? ? ? hba->utmrdl_virt_addr = NULL;
>> + ? ? ? hba->utmrdl_virt_addr_aligned = NULL;
>> +utmrd_fail:
>> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->utrdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? hba->utrdl_virt_addr, hba->utrdl_dma_addr);
>> + ? ? ? hba->utrdl_virt_addr = NULL;
>> + ? ? ? hba->utrdl_virt_addr_aligned = NULL;
>> +utrd_fail:
>> + ? ? ? dma_free_coherent(&hba->pdev->dev, hba->ucdl_size,
>> + ? ? ? ? ? ? ? ? ? ? ? ? hba->ucdl_virt_addr, hba->ucdl_dma_addr);
>> + ? ? ? hba->ucdl_virt_addr = NULL;
>> + ? ? ? hba->ucdl_virt_addr_aligned = NULL;
>> +ucd_fail:
>> + ? ? ? return -ENOMEM;
>> +}
>> +
>> +/**
>> + * ufshcd_host_memory_configure - configure local reference block with
>> + * ? ? ? ? ? ? ? ? ? ? ? ? ? ? memory offsets
>> + * @hba: per adapter instance
>> + *
>> + * Configure Host memory space
>> + * 1) Update Corresponding UTRD.UCDBA and UTRD.UCDBAU with UCD DMA
>> + * address.
>> + * 2) Update each UTRD with Response UPIU offset, Response UPIU length
>> + * and PRDT offset.
>> + * 3) Save the corresponding addresses of UTRD, UCD.CMD, UCD.RSP and UCD.PRDT
>> + * into local reference block.
>> + */
>> +static void ufshcd_host_memory_configure(struct ufs_hba *hba)
>> +{
>> + ? ? ? struct utp_transfer_cmd_desc *cmd_descp;
>> + ? ? ? struct utp_transfer_req_desc *utrdlp;
>> + ? ? ? dma_addr_t cmd_desc_dma_addr;
>> + ? ? ? dma_addr_t cmd_desc_element_addr;
>> + ? ? ? u16 response_offset;
>> + ? ? ? u16 prdt_offset;
>> + ? ? ? int cmd_desc_size;
>> + ? ? ? int i;
>> +
>> + ? ? ? utrdlp = (struct utp_transfer_req_desc *)hba->utrdl_virt_addr_aligned;
>> + ? ? ? cmd_descp =
>> + ? ? ? ? ? ? ? (struct utp_transfer_cmd_desc *)hba->ucdl_virt_addr_aligned;
>> +
>> + ? ? ? response_offset =
>> + ? ? ? ? ? ? ? offsetof(struct utp_transfer_cmd_desc, response_upiu);
>> + ? ? ? prdt_offset =
>> + ? ? ? ? ? ? ? offsetof(struct utp_transfer_cmd_desc, prd_table);
>> +
>> + ? ? ? cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
>> + ? ? ? cmd_desc_dma_addr = hba->ucdl_dma_addr_aligned;
>> +
>> + ? ? ? for (i = 0; i < hba->nutrs; i++) {
>> + ? ? ? ? ? ? ? /* Configure UTRD with command descriptor base address */
>> + ? ? ? ? ? ? ? cmd_desc_element_addr =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? (cmd_desc_dma_addr + (cmd_desc_size * i));
>> + ? ? ? ? ? ? ? utrdlp[i].command_desc_base_addr_lo =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(cmd_desc_element_addr);
>> + ? ? ? ? ? ? ? utrdlp[i].command_desc_base_addr_hi =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le32(cmd_desc_element_addr >> 32);
>> +
>> + ? ? ? ? ? ? ? /* Response upiu and prdt offset should be in double words */
>> + ? ? ? ? ? ? ? utrdlp[i].response_upiu_offset =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(BYTES_TO_DWORDS(response_offset));
>> + ? ? ? ? ? ? ? utrdlp[i].prd_table_offset =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(BYTES_TO_DWORDS(prdt_offset));
>> + ? ? ? ? ? ? ? utrdlp[i].response_upiu_length =
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cpu_to_le16(ALIGNED_UPIU_SIZE);
>> +
>> + ? ? ? ? ? ? ? hba->lrb[i].utr_descriptor_ptr = (utrdlp + i);
>> + ? ? ? ? ? ? ? hba->lrb[i].ucd_cmd_ptr =
>> + ? ? ? ? ? ? ? ? ? ? ? (struct utp_upiu_cmd *)(cmd_descp + i);
>> + ? ? ? ? ? ? ? hba->lrb[i].ucd_rsp_ptr =
>> + ? ? ? ? ? ? ? ? ? ? ? (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
>> + ? ? ? ? ? ? ? hba->lrb[i].ucd_prdt_ptr =
>> + ? ? ? ? ? ? ? ? ? ? ? (struct ufshcd_sg_entry *)cmd_descp[i].prd_table;
>> + ? ? ? }
>> +}
>> +
>> +/**
>> + * ufshcd_dme_link_startup - Notify Unipro to perform link startup
>> + * @hba: per adapter instance
>> + *
>> + * UIC_CMD_DME_LINK_STARTUP command must be issued to Unipro layer,
>> + * in order to intitialize the Unipro link startup procedure.
>> + * Once the Unipro links are up, the device connected to the controller
>> + * is detected.
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_dme_link_startup(struct ufs_hba *hba)
>> +{
>> + ? ? ? struct uic_command *uic_cmd;
>> + ? ? ? unsigned long flags;
>> +
>> + ? ? ? /* check if controller is ready to accept UIC commands */
>> + ? ? ? if (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS)) &
>> + ? ? ? ? ? UIC_COMMAND_READY) == 0x0) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? "Controller not ready"
>> + ? ? ? ? ? ? ? ? ? ? ? " to accept UIC commands\n");
>> + ? ? ? ? ? ? ? return -EINVAL;
>> + ? ? ? }
>> +
>> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
>> + ? ? ? uic_cmd = &hba->active_uic_cmd;
>> + ? ? ? uic_cmd->command = UIC_CMD_DME_LINK_STARTUP;
>> + ? ? ? uic_cmd->argument1 = 0;
>> + ? ? ? uic_cmd->argument2 = 0;
>> + ? ? ? uic_cmd->argument3 = 0;
>> +
>> + ? ? ? /* Enable UIC related interrupts */
>> + ? ? ? hba->int_enable_mask |= UIC_COMMAND_COMPL;
>> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
>> +
>> + ? ? ? /* sending UIC commands to controller */
>> + ? ? ? ufshcd_send_uic_command(hba, uic_cmd);
>> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
>> +
>> + ? ? ? return 0;
>> +}
>> +
>> +/**
>> + * ufshcd_make_hba_operational - Make UFS controller operatinal
>> + * @hba: per adapter instance
>> + *
>> + * To bring UFS host controller to operational state,
>> + * 1. Check if device is present
>> + * 2. Configure run-stop-registers
>> + * 3. Enable required interrupts
>> + * 4. Configure interrupt aggregation
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_make_hba_operational(struct ufs_hba *hba)
>> +{
>> + ? ? ? u32 reg;
>> +
>> + ? ? ? /* check if device present */
>> + ? ? ? reg = readl((UFSHCD_MMIO_BASE + REG_CONTROLLER_STATUS));
>> + ? ? ? if (ufshcd_is_device_present(reg)) {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev, "cc: Device not present\n");
>> + ? ? ? ? ? ? ? return -EINVAL;
>> + ? ? ? }
>> +
>> + ? ? ? /*
>> + ? ? ? ?* UCRDY, UTMRLDY and UTRLRDY bits must be 1
>> + ? ? ? ?* DEI, HEI bits must be 0
>> + ? ? ? ?*/
>> + ? ? ? if (!(ufshcd_get_lists_status(reg))) {
>> + ? ? ? ? ? ? ? writel(UTP_TASK_REQ_LIST_RUN_STOP_BIT,
>> + ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TASK_REQ_LIST_RUN_STOP));
>> + ? ? ? ? ? ? ? writel(UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT,
>> + ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE +
>> + ? ? ? ? ? ? ? ? ? ? ? REG_UTP_TRANSFER_REQ_LIST_RUN_STOP));
>> + ? ? ? } else {
>> + ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? "Host controller not ready to process requests");
>> + ? ? ? ? ? ? ? return -EINVAL;
>> + ? ? ? }
>> +
>> + ? ? ? /* Enable required interrupts */
>> + ? ? ? hba->int_enable_mask |= (UTP_TRANSFER_REQ_COMPL |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UIC_ERROR |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UTP_TASK_REQ_COMPL |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?DEVICE_FATAL_ERROR |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?CONTROLLER_FATAL_ERROR |
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?SYSTEM_BUS_FATAL_ERROR);
>> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_ENABLE);
>
> UFS host controller specification Section 7.2.1, step 11, ?mentions
> that the aggregation control register should be set if run/stop bit is
> not enabled.
> But In this case the run/ stop bit is set above before configuring the
> aggregation register. Please check for the same in other places from
> where it is called.
>

The spec mentions, (Section 7.2.1, step 11)
"UTRIACR initialization may be executed at any time when the Run/Stop
register (UTRLRSR)
is not enabled or when no requests are outstanding."

At this point in the code, during execution, there will not be any
outstanding requests.

>> +
>> + ? ? ? /* Configure interrupt aggregation */
>> + ? ? ? ufshcd_config_int_aggr(hba, INT_AGGR_CONFIG);
>> +
>> + ? ? ? hba->ufshcd_state = UFSHCD_STATE_OPERATIONAL;
>> +
>> + ? ? ? return 0;
>> +}
>> +
>> +/**
>> + * ufshcd_controller_enable - initialize the controller
>> + * @hba: per adapter instance
>> + *
>> + * The controller resets its self and controller firmware start of day is
>> + * kickes off. When controller is ready it will set the Host Controller
>> + * Status bit to 1.
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int ufshcd_controller_enable(struct ufs_hba *hba)
>> +{
>> + ? ? ? int retry;
>> +
>> + ? ? ? /*
>> + ? ? ? ?* msleep of 1 and 5 used in this function might result in msleep(20),
>> + ? ? ? ?* but it was necessary to send the UFS FPGA to reset mode during
>> + ? ? ? ?* development and testing of this driver. msleep can be changed to
>> + ? ? ? ?* mdelay and retry count can be reduced based on the controller.
>> + ? ? ? ?*/
>> +
>> + ? ? ? /* change controller state to "reset state" */
>> + ? ? ? writel(0, (UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
>> + ? ? ? msleep(5);
>> +
>> + ? ? ? writel(CONTROLLER_ENABLE,
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE));
>> + ? ? ? msleep(1);
>> +
>> + ? ? ? /* wait for the host controller to complete initialization */
>> + ? ? ? retry = 10;
>> + ? ? ? while (((readl(UFSHCD_MMIO_BASE + REG_CONTROLLER_ENABLE)) &
>> + ? ? ? ? ? ? ?CONTROLLER_ENABLE) != 0x1) {
>> + ? ? ? ? ? ? ? if (retry) {
>> + ? ? ? ? ? ? ? ? ? ? ? retry--;
>> + ? ? ? ? ? ? ? } else {
>> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Controller enable failed\n");
>> + ? ? ? ? ? ? ? ? ? ? ? return -EINVAL;
>> + ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ? msleep(5);
>> + ? ? ? }
>> + ? ? ? return 0;
>> +}
>> +
>> +/**
>> + * ufshcd_initialize_hba - start the initialization process
>> + * @hba: per adapter instance
>> + *
>> + * Initialize the Controller
>> + * 1) Enable the controller via ufshcd_controller_enable.
>> + * 2) Program the Transfer Request List Address with the starting address of
>> + * UTRDL.
>> + *
>> + * 3) Program the Task Management Request List Address with starting address
>> + * of UTMRDL.
>> + *
>> + * Returns 0 on success, non-zero value on failure.
>> + */
>> +static int ufshcd_initialize_hba(struct ufs_hba *hba)
>> +{
>> + ? ? ? if (ufshcd_controller_enable(hba))
>> + ? ? ? ? ? ? ? return -1;
>> +
>> + ? ? ? /* Configure TR/TM address registers */
>> + ? ? ? writel(hba->utrdl_dma_addr_aligned,
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_L));
>> + ? ? ? writel((hba->utrdl_dma_addr_aligned >> 32),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TRANSFER_REQ_LIST_BASE_H));
>> + ? ? ? writel(hba->utmrdl_dma_addr_aligned,
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_L));
>> + ? ? ? writel((hba->utmrdl_dma_addr_aligned >> 32),
>> + ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_LIST_BASE_H));
>> +
>> + ? ? ? /* Initialize unipro link startup procedure */
>> + ? ? ? return ufshcd_dme_link_startup(hba);
>> +}
>> +
>> +/**
>> + * ufshcd_uic_cc_handler - handle UIC command completion
>> + * @work: pointer to a work queue structure
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static void ufshcd_uic_cc_handler (struct work_struct *work)
>> +{
>> + ? ? ? struct ufs_hba *hba;
>> +
>> + ? ? ? hba = container_of(work, struct ufs_hba, uic_workq);
>> +
>> + ? ? ? if ((UIC_CMD_DME_LINK_STARTUP == hba->active_uic_cmd.command) &&
>> + ? ? ? ? ? !(ufshcd_get_uic_cmd_result(hba))) {
>> +
>> + ? ? ? ? ? ? ? if (ufshcd_make_hba_operational(hba))
>> + ? ? ? ? ? ? ? ? ? ? ? dev_err(&hba->pdev->dev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "cc: hba not operational state\n");
>> + ? ? ? ? ? ? ? return;
>> + ? ? ? }
>> +}
>> +
>> +/**
>> + * ufshcd_sl_intr - Interrupt service routine
>> + * @hba: per adapter instance
>> + * @intr_status: contains interrupts generated by the controller
>> + */
>> +static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status)
>> +{
>> + ? ? ? if (intr_status & UIC_COMMAND_COMPL)
>> + ? ? ? ? ? ? ? schedule_work(&hba->uic_workq);
>> +}
>> +
>> +/**
>> + * ufshcd_intr - Main interrupt service routine
>> + * @irq: irq number
>> + * @__hba: pointer to adapter instance
>> + *
>> + * Returns IRQ_HANDLED - If interrupt is valid
>> + * ? ? ? ? ? ? IRQ_NONE - If invalid interrupt
>> + */
>> +static irqreturn_t ufshcd_intr(int irq, void *__hba)
>> +{
>> + ? ? ? unsigned long flags;
>> + ? ? ? u32 intr_status;
>> + ? ? ? irqreturn_t retval = IRQ_NONE;
>> + ? ? ? struct ufs_hba *hba = __hba;
>> +
>> + ? ? ? spin_lock_irqsave(hba->host->host_lock, flags);
>> + ? ? ? intr_status = readl(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS);
>> +
>> + ? ? ? if (intr_status) {
>> + ? ? ? ? ? ? ? ufshcd_sl_intr(hba, intr_status);
>> +
>> + ? ? ? ? ? ? ? /* If UFSHCI 1.0 then clear interrupt status register */
>> + ? ? ? ? ? ? ? if (UFSHCI_VERSION_10 == hba->ufs_version)
>> + ? ? ? ? ? ? ? ? ? ? ? writel(intr_status,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?(UFSHCD_MMIO_BASE + REG_INTERRUPT_STATUS));
>> + ? ? ? ? ? ? ? retval = IRQ_HANDLED;
>> + ? ? ? }
>> + ? ? ? spin_unlock_irqrestore(hba->host->host_lock, flags);
>> + ? ? ? return retval;
>> +}
>> +
>> +static struct scsi_host_template ufshcd_driver_template = {
>> + ? ? ? .module ? ? ? ? ? ? ? ? = THIS_MODULE,
>> + ? ? ? .name ? ? ? ? ? ? ? ? ? = UFSHCD,
>> + ? ? ? .proc_name ? ? ? ? ? ? ?= UFSHCD,
>> + ? ? ? .this_id ? ? ? ? ? ? ? ?= -1,
>> +};
>> +
>> +/**
>> + * ufshcd_shutdown - main funciton to put the controller in reset state
>> + * @pdev: pointer to PCI device handle
>> + */
>> +static void ufshcd_shutdown(struct pci_dev *pdev)
>> +{
>> + ? ? ? ufshcd_hba_stop((struct ufs_hba *)pci_get_drvdata(pdev));
>> +}
>> +
>> +#ifdef CONFIG_PM
>> +/**
>> + * ufshcd_suspend - suspend power management function
>> + * @pdev: pointer to PCI device handle
>> + * @state: power state
>> + *
>> + * Returns -ENOSYS
>> + */
>> +static int ufshcd_suspend(struct pci_dev *pdev, pm_message_t state)
>> +{
>> + ? ? ? return -ENOSYS;
>> +}
>> +
>> +/**
>> + * ufshcd_resume - resume power management function
>> + * @pdev: pointer to PCI device handle
>> + *
>> + * Returns -ENOSYS
>> + */
>> +static int ufshcd_resume(struct pci_dev *pdev)
>> +{
>> + ? ? ? return -ENOSYS;
>> +}
>> +#endif /* CONFIG_PM */
>> +
>> +/**
>> + * ufshcd_hba_free - free allocated memory for
>> + * ? ? ? ? ? ? ? ? ? ? host memory space data structures
>> + * @hba: per adapter instance
>> + */
>> +static void ufshcd_hba_free(struct ufs_hba *hba)
>> +{
>> + ? ? ? iounmap(UFSHCD_MMIO_BASE);
>> + ? ? ? ufshcd_free_hba_memory(hba);
>> + ? ? ? pci_release_regions(hba->pdev);
>> +}
>> +
>> +/**
>> + * ufshcd_remove - deallocate PCI/SCSI host and host memory space
>> + * ? ? ? ? ? ? data structure memory
>> + * @pdev - pointer to PCI handle
>> + */
>> +static void ufshcd_remove(struct pci_dev *pdev)
>> +{
>> + ? ? ? struct ufs_hba *hba = pci_get_drvdata(pdev);
>> +
>> + ? ? ? /* disable interrupts */
>> + ? ? ? ufshcd_int_config(hba, UFSHCD_INT_DISABLE);
>> + ? ? ? free_irq(pdev->irq, hba);
>> +
>> + ? ? ? ufshcd_hba_stop(hba);
>> + ? ? ? ufshcd_hba_free(hba);
>> +
>> + ? ? ? scsi_remove_host(hba->host);
>> + ? ? ? scsi_host_put(hba->host);
>> + ? ? ? pci_set_drvdata(pdev, NULL);
>> + ? ? ? pci_clear_master(pdev);
>> + ? ? ? pci_disable_device(pdev);
>> +}
>> +
>> +/**
>> + * ufshcd_set_dma_mask - Set dma addressing
>> + * @pdev: PCI device struct
>> + *
>> + * Returns 0 for success, non-zero for failure
>> + */
>> +static int ufshcd_set_dma_mask(struct pci_dev *pdev)
>> +{
>> + ? ? ? int err;
>> +
>> + ? ? ? do {
>> + ? ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
>> + ? ? ? ? ? ? ? if (!err) {
>> + ? ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(64));
>> + ? ? ? ? ? ? ? ? ? ? ? break;
>> + ? ? ? ? ? ? ? }
>> + ? ? ? ? ? ? ? err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32));
>> + ? ? ? ? ? ? ? if (!err)
>> + ? ? ? ? ? ? ? ? ? ? ? err = pci_set_consistent_dma_mask(pdev,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DMA_BIT_MASK(32));
>> + ? ? ? } while (0);
>> +
>> + ? ? ? return err;
>> +}
>> +
>> +/**
>> + * ufshcd_probe - probe routine of the driver
>> + * @pdev: pointer to PCI device handle
>> + * @id: PCI device id
>> + *
>> + * Returns 0 on success, non-zero value on failure
>> + */
>> +static int __devinit
>> +ufshcd_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>> +{
>> + ? ? ? struct Scsi_Host *host;
>> + ? ? ? struct ufs_hba *hba;
>> + ? ? ? int ufs_hba_len;
>> + ? ? ? int err;
>> +
>> + ? ? ? ufs_hba_len = sizeof(struct ufs_hba);
>> + ? ? ? err = pci_enable_device(pdev);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "pci_enable_device failed\n");
>> + ? ? ? ? ? ? ? goto out_error;
>> + ? ? ? }
>> +
>> + ? ? ? pci_set_master(pdev);
>> +
>> + ? ? ? host = scsi_host_alloc(&ufshcd_driver_template, ufs_hba_len);
>> + ? ? ? if (!host) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "scsi_host_alloc failed\n");
>> + ? ? ? ? ? ? ? err = -ENOMEM;
>> + ? ? ? ? ? ? ? goto out_disable;
>> + ? ? ? }
>> + ? ? ? hba = (struct ufs_hba *)host->hostdata;
>> +
>> + ? ? ? err = pci_request_regions(pdev, UFSHCD);
>> + ? ? ? if (err < 0) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request regions failed\n");
>> + ? ? ? ? ? ? ? goto out_disable;
>> + ? ? ? }
>> +
>> + ? ? ? hba->mmio_base = pci_ioremap_bar(pdev, 0);
>> + ? ? ? if (!hba->mmio_base) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "memory map failed\n");
>> + ? ? ? ? ? ? ? err = -ENOMEM;
>> + ? ? ? ? ? ? ? goto out_release_regions;
>> + ? ? ? }
>> +
>> + ? ? ? err = ufshcd_set_dma_mask(pdev);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "set dma mask failed\n");
>> + ? ? ? ? ? ? ? goto out_iounmap;
>> + ? ? ? }
>> +
>> + ? ? ? hba->host = host;
>> + ? ? ? hba->pdev = pdev;
>> +
>> + ? ? ? /* Read capabilities registers */
>> + ? ? ? ufshcd_hba_capabilities(hba);
>> +
>> + ? ? ? /* Get UFS version supported by the controller */
>> + ? ? ? hba->ufs_version = ufshcd_get_ufs_version(hba);
>> +
>> + ? ? ? /* Allocate memory for host memory space */
>> + ? ? ? err = ufshcd_memory_alloc(hba);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Memory allocation failed\n");
>> + ? ? ? ? ? ? ? goto out_iounmap;
>> + ? ? ? }
>> +
>> + ? ? ? /* Configure LRB */
>> + ? ? ? ufshcd_host_memory_configure(hba);
>> +
>> + ? ? ? host->can_queue = hba->nutrs;
>> + ? ? ? host->max_id = UFSHCD_MAX_ID;
>> + ? ? ? host->max_lun = UFSHCD_MAX_LUNS;
>> + ? ? ? host->max_channel = UFSHCD_MAX_CHANNEL;
>> + ? ? ? host->unique_id = host->host_no;
>> +
>> + ? ? ? /* Initialize work queues */
>> + ? ? ? INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
>> +
>> + ? ? ? /* IRQ registration */
>> + ? ? ? err = request_irq(pdev->irq, ufshcd_intr, IRQF_SHARED, UFSHCD, hba);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "request irq failed\n");
>> + ? ? ? ? ? ? ? goto out_lrb_free;
>> + ? ? ? }
>> +
>> + ? ? ? pci_set_drvdata(pdev, hba);
>> +
>> + ? ? ? err = scsi_add_host(host, &pdev->dev);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "scsi_add_host failed\n");
>> + ? ? ? ? ? ? ? goto out_free_irq;
>> + ? ? ? }
>> +
>> + ? ? ? /* Initialization routine */
>> + ? ? ? err = ufshcd_initialize_hba(hba);
>> + ? ? ? if (err) {
>> + ? ? ? ? ? ? ? dev_err(&pdev->dev, "Initialization failed\n");
>> + ? ? ? ? ? ? ? goto out_free_irq;
>> + ? ? ? }
>> +
>> + ? ? ? return 0;
>> +
>> +out_free_irq:
>> + ? ? ? free_irq(pdev->irq, hba);
>> +out_lrb_free:
>> + ? ? ? ufshcd_free_hba_memory(hba);
>> +out_iounmap:
>> + ? ? ? iounmap(hba->mmio_base);
>> +out_release_regions:
>> + ? ? ? pci_release_regions(pdev);
>> +out_disable:
>> + ? ? ? scsi_host_put(host);
>> + ? ? ? pci_clear_master(pdev);
>> + ? ? ? pci_disable_device(pdev);
>> +out_error:
>> + ? ? ? return err;
>> +}
>> +
>> +static DEFINE_PCI_DEVICE_TABLE(ufshcd_pci_tbl) = {
>> + ? ? ? { 0x144D, 0xC00C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
>> + ? ? ? { } ? ? /* terminate list */
>> +};
>> +
>> +MODULE_DEVICE_TABLE(pci, ufshcd_pci_tbl);
>> +
>> +static struct pci_driver ufshcd_pci_driver = {
>> + ? ? ? .name = UFSHCD,
>> + ? ? ? .id_table = ufshcd_pci_tbl,
>> + ? ? ? .probe = ufshcd_probe,
>> + ? ? ? .remove = __devexit_p(ufshcd_remove),
>> + ? ? ? .shutdown = ufshcd_shutdown,
>> +#ifdef CONFIG_PM
>> + ? ? ? .suspend = ufshcd_suspend,
>> + ? ? ? .resume = ufshcd_resume,
>> +#endif
>> +};
>> +
>> +/**
>> + * ufshcd_init - Driver registration routine
>> + */
>> +static int __init ufshcd_init(void)
>> +{
>> + ? ? ? return pci_register_driver(&ufshcd_pci_driver);
>> +}
>> +module_init(ufshcd_init);
>> +
>> +/**
>> + * ufshcd_exit - Driver exit clean-up routine
>> + */
>> +static void __exit ufshcd_exit(void)
>> +{
>> + ? ? ? pci_unregister_driver(&ufshcd_pci_driver);
>> +}
>> +module_exit(ufshcd_exit);
>> +
>> +
>> +MODULE_AUTHOR("Santosh Yaragnavi, Vinayak Holikatti");
>> +MODULE_DESCRIPTION("Generic UFS host controller driver");
>> +MODULE_LICENSE("GPL");
>> +MODULE_VERSION(UFSHCD_DRIVER_VERSION);
>> diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h
>> new file mode 100644
>> index 0000000..f8701b7
>> --- /dev/null
>> +++ b/drivers/scsi/ufs/ufshci.h
>> @@ -0,0 +1,360 @@
>> +/*
>> + * Universal Flash Storage Host controller driver
>> + *
>> + * This code is based on drivers/scsi/ufs/ufshci.h
>> + * Copyright (C) 2011-2012 Samsung India Software Operations
>> + *
>> + * Santosh Yaraganavi <[email protected]>
>> + * Vinayak Holikatti <[email protected]>
>> + *
>> + * This program is free software; you can redistribute it and/or
>> + * modify it under the terms of the GNU General Public License
>> + * as published by the Free Software Foundation; either version 2
>> + * of the License, or (at your option) any later version.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
>> + * GNU General Public License for more details.
>> + *
>> + * NO WARRANTY
>> + * THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
>> + * CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
>> + * LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
>> + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
>> + * solely responsible for determining the appropriateness of using and
>> + * distributing the Program and assumes all risks associated with its
>> + * exercise of rights under this Agreement, including but not limited to
>> + * the risks and costs of program errors, damage to or loss of data,
>> + * programs or equipment, and unavailability or interruption of operations.
>> +
>> + * DISCLAIMER OF LIABILITY
>> + * NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
>> + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
>> + * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
>> + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
>> + * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
>> + * USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
>> + * HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
>> +
>> + * You should have received a copy of the GNU General Public License
>> + * along with this program; if not, write to the Free Software
>> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA ?02110-1301,
>> + * USA.
>> + */
>> +
>> +#ifndef _UFSHCI_H
>> +#define _UFSHCI_H
>> +
>> +/* UFSHCI Registers */
>> +enum {
>> + ? ? ? REG_CONTROLLER_CAPABILITIES ? ? ? ? ? ? = 0x00,
>> + ? ? ? REG_UFS_VERSION ? ? ? ? ? ? ? ? ? ? ? ? = 0x08,
>> + ? ? ? REG_CONTROLLER_DEV_ID ? ? ? ? ? ? ? ? ? = 0x10,
>> + ? ? ? REG_CONTROLLER_PROD_ID ? ? ? ? ? ? ? ? ?= 0x14,
>> + ? ? ? REG_INTERRUPT_STATUS ? ? ? ? ? ? ? ? ? ?= 0x20,
>> + ? ? ? REG_INTERRUPT_ENABLE ? ? ? ? ? ? ? ? ? ?= 0x24,
>> + ? ? ? REG_CONTROLLER_STATUS ? ? ? ? ? ? ? ? ? = 0x30,
>> + ? ? ? REG_CONTROLLER_ENABLE ? ? ? ? ? ? ? ? ? = 0x34,
>> + ? ? ? REG_UIC_ERROR_CODE_PHY_ADAPTER_LAYER ? ?= 0x38,
>> + ? ? ? REG_UIC_ERROR_CODE_DATA_LINK_LAYER ? ? ?= 0x3C,
>> + ? ? ? REG_UIC_ERROR_CODE_NETWORK_LAYER ? ? ? ?= 0x40,
>> + ? ? ? REG_UIC_ERROR_CODE_TRANSPORT_LAYER ? ? ?= 0x44,
>> + ? ? ? REG_UIC_ERROR_CODE_DME ? ? ? ? ? ? ? ? ?= 0x48,
>> + ? ? ? REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL ? ?= 0x4C,
>> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_BASE_L ? ? ? ?= 0x50,
>> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_BASE_H ? ? ? ?= 0x54,
>> + ? ? ? REG_UTP_TRANSFER_REQ_DOOR_BELL ? ? ? ? ?= 0x58,
>> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_CLEAR ? ? ? ? = 0x5C,
>> + ? ? ? REG_UTP_TRANSFER_REQ_LIST_RUN_STOP ? ? ?= 0x60,
>> + ? ? ? REG_UTP_TASK_REQ_LIST_BASE_L ? ? ? ? ? ?= 0x70,
>> + ? ? ? REG_UTP_TASK_REQ_LIST_BASE_H ? ? ? ? ? ?= 0x74,
>> + ? ? ? REG_UTP_TASK_REQ_DOOR_BELL ? ? ? ? ? ? ?= 0x78,
>> + ? ? ? REG_UTP_TASK_REQ_LIST_CLEAR ? ? ? ? ? ? = 0x7C,
>> + ? ? ? REG_UTP_TASK_REQ_LIST_RUN_STOP ? ? ? ? ?= 0x80,
>> + ? ? ? REG_UIC_COMMAND ? ? ? ? ? ? ? ? ? ? ? ? = 0x90,
>> + ? ? ? REG_UIC_COMMAND_ARG_1 ? ? ? ? ? ? ? ? ? = 0x94,
>> + ? ? ? REG_UIC_COMMAND_ARG_2 ? ? ? ? ? ? ? ? ? = 0x98,
>> + ? ? ? REG_UIC_COMMAND_ARG_3 ? ? ? ? ? ? ? ? ? = 0x9C
>> +};
>> +
>> +/* Controller capability masks */
>> +enum {
>> + ? ? ? MASK_TRANSFER_REQUESTS_SLOTS ? ? ? ? ? ?= 0x0000001F,
>> + ? ? ? MASK_TASK_MANAGEMENT_REQUEST_SLOTS ? ? ?= 0x00070000,
>> + ? ? ? MASK_64_ADDRESSING_SUPPORT ? ? ? ? ? ? ?= 0x01000000,
>> + ? ? ? MASK_OUT_OF_ORDER_DATA_DELIVERY_SUPPORT = 0x02000000,
>> + ? ? ? MASK_UIC_DME_TEST_MODE_SUPPORT ? ? ? ? ?= 0x04000000
>> +};
>> +
>> +/* UFS Version 08h */
>> +#define MINOR_VERSION_NUM_MASK ? ? ? ? UFS_MASK(0xFFFF, 0)
>> +#define MAJOR_VERSION_NUM_MASK ? ? ? ? UFS_MASK(0xFFFF, 16)
>> +
>> +/* Controller UFSHCI version */
>> +enum {
>> + ? ? ? UFSHCI_VERSION_10 = 0x00010000,
>> + ? ? ? UFSHCI_VERSION_11 = 0x00010100
>> +};
>> +
>> +/*
>> + * HCDDID - Host Controller Identification Descriptor
>> + * ? ? ? - Device ID and Device Class 10h
>> + */
>> +#define DEVICE_CLASS ? UFS_MASK(0xFFFF, 0)
>> +#define DEVICE_ID ? ? ?UFS_MASK(0xFF, 24)
>> +
>> +/*
>> + * HCPMID - Host Controller Identification Descriptor
>> + * ? ? ? - Product/Manufacturer ID ?14h
>> + */
>> +#define MANUFACTURE_ID_MASK ? ?UFS_MASK(0xFFFF, 0)
>> +#define PRODUCT_ID_MASK ? ? ? ? ? ? ? ?UFS_MASK(0xFFFF, 16)
>> +
>> +#define UFS_BIT(x) ? ? (1L << (x))
>> +
>> +#define UTP_TRANSFER_REQ_COMPL ? ? ? ? ? ? ? ? UFS_BIT(0)
>> +#define UIC_DME_END_PT_RESET ? ? ? ? ? ? ? ? ? UFS_BIT(1)
>> +#define UIC_ERROR ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(2)
>> +#define UIC_TEST_MODE ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(3)
>> +#define UIC_POWER_MODE ? ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(4)
>> +#define UIC_HIBERNATE_EXIT ? ? ? ? ? ? ? ? ? ? UFS_BIT(5)
>> +#define UIC_HIBERNATE_ENTER ? ? ? ? ? ? ? ? ? ?UFS_BIT(6)
>> +#define UIC_LINK_LOST ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(7)
>> +#define UIC_LINK_STARTUP ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(8)
>> +#define UTP_TASK_REQ_COMPL ? ? ? ? ? ? ? ? ? ? UFS_BIT(9)
>> +#define UIC_COMMAND_COMPL ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(10)
>> +#define DEVICE_FATAL_ERROR ? ? ? ? ? ? ? ? ? ? UFS_BIT(11)
>> +#define CONTROLLER_FATAL_ERROR ? ? ? ? ? ? ? ? UFS_BIT(16)
>> +#define SYSTEM_BUS_FATAL_ERROR ? ? ? ? ? ? ? ? UFS_BIT(17)
>> +
>> +#define UFSHCD_ERROR_MASK ? ? ?(UIC_ERROR |\
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? DEVICE_FATAL_ERROR |\
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONTROLLER_FATAL_ERROR |\
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SYSTEM_BUS_FATAL_ERROR)
>> +
>> +#define INT_FATAL_ERRORS ? ? ? (DEVICE_FATAL_ERROR |\
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CONTROLLER_FATAL_ERROR |\
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? SYSTEM_BUS_FATAL_ERROR)
>> +
>> +/* HCS - Host Controller Status 30h */
>> +#define DEVICE_PRESENT ? ? ? ? ? ? ? ? ? ? ? ? UFS_BIT(0)
>> +#define UTP_TRANSFER_REQ_LIST_READY ? ? ? ? ? ?UFS_BIT(1)
>> +#define UTP_TASK_REQ_LIST_READY ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(2)
>> +#define UIC_COMMAND_READY ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(3)
>> +#define HOST_ERROR_INDICATOR ? ? ? ? ? ? ? ? ? UFS_BIT(4)
>> +#define DEVICE_ERROR_INDICATOR ? ? ? ? ? ? ? ? UFS_BIT(5)
>> +#define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK ?UFS_MASK(0x7, 8)
>> +
>> +/* HCE - Host Controller Enable 34h */
>> +#define CONTROLLER_ENABLE ? ? ?UFS_BIT(0)
>> +
>> +/* UECPA - Host UIC Error Code PHY Adapter Layer 38h */
>> +#define UIC_PHY_ADAPTER_LAYER_ERROR ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
>> +#define UIC_PHY_ADAPTER_LAYER_ERROR_CODE_MASK ? ? ? ? ?0x1F
>> +
>> +/* UECDL - Host UIC Error Code Data Link Layer 3Ch */
>> +#define UIC_DATA_LINK_LAYER_ERROR ? ? ? ? ? ? ?UFS_BIT(31)
>> +#define UIC_DATA_LINK_LAYER_ERROR_CODE_MASK ? ?0x7FFF
>> +#define UIC_DATA_LINK_LAYER_ERROR_PA_INIT ? ? ?0x2000
>> +
>> +/* UECN - Host UIC Error Code Network Layer 40h */
>> +#define UIC_NETWORK_LAYER_ERROR ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
>> +#define UIC_NETWORK_LAYER_ERROR_CODE_MASK ? ? ?0x7
>> +
>> +/* UECT - Host UIC Error Code Transport Layer 44h */
>> +#define UIC_TRANSPORT_LAYER_ERROR ? ? ? ? ? ? ?UFS_BIT(31)
>> +#define UIC_TRANSPORT_LAYER_ERROR_CODE_MASK ? ?0x7F
>> +
>> +/* UECDME - Host UIC Error Code DME 48h */
>> +#define UIC_DME_ERROR ? ? ? ? ? ? ? ? ?UFS_BIT(31)
>> +#define UIC_DME_ERROR_CODE_MASK ? ? ? ? ? ? ? ?0x1
>> +
>> +#define INT_AGGR_TIMEOUT_VAL_MASK ? ? ? ? ? ? ?0xFF
>> +#define INT_AGGR_COUNTER_THRESHOLD_MASK ? ? ? ? ? ? ? ?UFS_MASK(0x1F, 8)
>> +#define INT_AGGR_COUNTER_AND_TIMER_RESET ? ? ? UFS_BIT(16)
>> +#define INT_AGGR_STATUS_BIT ? ? ? ? ? ? ? ? ? ?UFS_BIT(20)
>> +#define INT_AGGR_PARAM_WRITE ? ? ? ? ? ? ? ? ? UFS_BIT(24)
>> +#define INT_AGGR_ENABLE ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UFS_BIT(31)
>> +
>> +/* UTRLRSR - UTP Transfer Request Run-Stop Register 60h */
>> +#define UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT ? ? UFS_BIT(0)
>> +
>> +/* UTMRLRSR - UTP Task Management Request Run-Stop Register 80h */
>> +#define UTP_TASK_REQ_LIST_RUN_STOP_BIT ? ? ? ? UFS_BIT(0)
>> +
>> +/* UICCMD - UIC Command */
>> +#define COMMAND_OPCODE_MASK ? ? ? ? ? ?0xFF
>> +#define GEN_SELECTOR_INDEX_MASK ? ? ? ? ? ? ? ?0xFFFF
>> +
>> +#define MIB_ATTRIBUTE_MASK ? ? ? ? ? ? UFS_MASK(0xFFFF, 16)
>> +#define RESET_LEVEL ? ? ? ? ? ? ? ? ? ?0xFF
>> +
>> +#define ATTR_SET_TYPE_MASK ? ? ? ? ? ? UFS_MASK(0xFF, 16)
>> +#define CONFIG_RESULT_CODE_MASK ? ? ? ? ? ? ? ?0xFF
>> +#define GENERIC_ERROR_CODE_MASK ? ? ? ? ? ? ? ?0xFF
>> +
>> +/* UIC Commands */
>> +enum {
>> + ? ? ? UIC_CMD_DME_GET ? ? ? ? ? ? ? ? = 0x01,
>> + ? ? ? UIC_CMD_DME_SET ? ? ? ? ? ? ? ? = 0x02,
>> + ? ? ? UIC_CMD_DME_PEER_GET ? ? ? ? ? ?= 0x03,
>> + ? ? ? UIC_CMD_DME_PEER_SET ? ? ? ? ? ?= 0x04,
>> + ? ? ? UIC_CMD_DME_POWERON ? ? ? ? ? ? = 0x10,
>> + ? ? ? UIC_CMD_DME_POWEROFF ? ? ? ? ? ?= 0x11,
>> + ? ? ? UIC_CMD_DME_ENABLE ? ? ? ? ? ? ?= 0x12,
>> + ? ? ? UIC_CMD_DME_RESET ? ? ? ? ? ? ? = 0x14,
>> + ? ? ? UIC_CMD_DME_END_PT_RST ? ? ? ? ?= 0x15,
>> + ? ? ? UIC_CMD_DME_LINK_STARTUP ? ? ? ?= 0x16,
>> + ? ? ? UIC_CMD_DME_HIBER_ENTER ? ? ? ? = 0x17,
>> + ? ? ? UIC_CMD_DME_HIBER_EXIT ? ? ? ? ?= 0x18,
>> + ? ? ? UIC_CMD_DME_TEST_MODE ? ? ? ? ? = 0x1A
>> +};
>> +
>> +/* UIC Config result code / Generic error code */
>> +enum {
>> + ? ? ? UIC_CMD_RESULT_SUCCESS ? ? ? ? ? ? ? ? ?= 0x00,
>> + ? ? ? UIC_CMD_RESULT_INVALID_ATTR ? ? ? ? ? ? = 0x01,
>> + ? ? ? UIC_CMD_RESULT_FAILURE ? ? ? ? ? ? ? ? ?= 0x01,
>> + ? ? ? UIC_CMD_RESULT_INVALID_ATTR_VALUE ? ? ? = 0x02,
>> + ? ? ? UIC_CMD_RESULT_READ_ONLY_ATTR ? ? ? ? ? = 0x03,
>> + ? ? ? UIC_CMD_RESULT_WRITE_ONLY_ATTR ? ? ? ? ?= 0x04,
>> + ? ? ? UIC_CMD_RESULT_BAD_INDEX ? ? ? ? ? ? ? ?= 0x05,
>> + ? ? ? UIC_CMD_RESULT_LOCKED_ATTR ? ? ? ? ? ? ?= 0x06,
>> + ? ? ? UIC_CMD_RESULT_BAD_TEST_FEATURE_INDEX ? = 0x07,
>> + ? ? ? UIC_CMD_RESULT_PEER_COMM_FAILURE ? ? ? ?= 0x08,
>> + ? ? ? UIC_CMD_RESULT_BUSY ? ? ? ? ? ? ? ? ? ? = 0x09,
>> + ? ? ? UIC_CMD_RESULT_DME_FAILURE ? ? ? ? ? ? ?= 0x0A
>> +};
>> +
>> +#define MASK_UIC_COMMAND_RESULT ? ? ? ? ? ? ? ? ? ? ? ?0xFF
>> +
>> +#define INT_AGGR_COUNTER_THRESHOLD_VALUE ? ? ? (0x1F << 8)
>> +#define INT_AGGR_TIMEOUT_VALUE ? ? ? ? ? ? ? ? (0x02)
>> +
>> +/*
>> + * Request Descriptor Definitions
>> + */
>> +
>> +/* Transfer request command type */
>> +enum {
>> + ? ? ? UTP_CMD_TYPE_SCSI ? ? ? ? ? ? ? = 0x0,
>> + ? ? ? UTP_CMD_TYPE_UFS ? ? ? ? ? ? ? ?= 0x1,
>> + ? ? ? UTP_CMD_TYPE_DEV_MANAGE ? ? ? ? = 0x2
>> +};
>> +
>> +enum {
>> + ? ? ? UTP_SCSI_COMMAND ? ? ? ? ? ? ? ?= 0x00000000,
>> + ? ? ? UTP_NATIVE_UFS_COMMAND ? ? ? ? ?= 0x10000000,
>> + ? ? ? UTP_DEVICE_MANAGEMENT_FUNCTION ?= 0x20000000,
>> + ? ? ? UTP_REQ_DESC_INT_CMD ? ? ? ? ? ?= 0x01000000
>> +};
>> +
>> +/* UTP Transfer Request Data Direction (DD) */
>> +enum {
>> + ? ? ? UTP_NO_DATA_TRANSFER ? ?= 0x00000000,
>> + ? ? ? UTP_HOST_TO_DEVICE ? ? ?= 0x02000000,
>> + ? ? ? UTP_DEVICE_TO_HOST ? ? ?= 0x04000000
>> +};
>> +
>> +/* Overall command status values */
>> +enum {
>> + ? ? ? OCS_SUCCESS ? ? ? ? ? ? ? ? ? ? = 0x0,
>> + ? ? ? OCS_INVALID_CMD_TABLE_ATTR ? ? ?= 0x1,
>> + ? ? ? OCS_INVALID_PRDT_ATTR ? ? ? ? ? = 0x2,
>> + ? ? ? OCS_MISMATCH_DATA_BUF_SIZE ? ? ?= 0x3,
>> + ? ? ? OCS_MISMATCH_RESP_UPIU_SIZE ? ? = 0x4,
>> + ? ? ? OCS_PEER_COMM_FAILURE ? ? ? ? ? = 0x5,
>> + ? ? ? OCS_ABORTED ? ? ? ? ? ? ? ? ? ? = 0x6,
>> + ? ? ? OCS_FATAL_ERROR ? ? ? ? ? ? ? ? = 0x7,
>> + ? ? ? OCS_INVALID_COMMAND_STATUS ? ? ?= 0x0F,
>> + ? ? ? MASK_OCS ? ? ? ? ? ? ? ? ? ? ? ?= 0x0F
>> +};
>> +
>> +/**
>> + * struct ufshcd_sg_entry - UFSHCI PRD Entry
>> + * @base_addr: Lower 32bit physical address DW-0
>> + * @upper_addr: Upper 32bit physical address DW-1
>> + * @reserved: Reserved for future use DW-2
>> + * @size: size of physical segment DW-3
>> + */
>> +struct ufshcd_sg_entry {
>> + ? ? ? u32 ? ?base_addr;
>> + ? ? ? u32 ? ?upper_addr;
>> + ? ? ? u32 ? ?reserved;
>> + ? ? ? u32 ? ?size;
>> +};
>> +
>> +/**
>> + * struct utp_transfer_cmd_desc - UFS Commad Descriptor structure
>> + * @command_upiu: Command UPIU Frame address
>> + * @response_upiu: Response UPIU Frame address
>> + * @prd_table: Physcial Region Descriptor
>> + */
>> +struct utp_transfer_cmd_desc {
>> + ? ? ? u8 command_upiu[ALIGNED_UPIU_SIZE];
>> + ? ? ? u8 response_upiu[ALIGNED_UPIU_SIZE];
>> + ? ? ? struct ufshcd_sg_entry ? ?prd_table[SG_ALL];
>> +};
>> +
>> +/**
>> + * struct request_desc_header - Descriptor Header common to both UTRD and UTMRD
>> + * @dword0: Descriptor Header DW0
>> + * @dword1: Descriptor Header DW1
>> + * @dword2: Descriptor Header DW2
>> + * @dword3: Descriptor Header DW3
>> + */
>> +struct request_desc_header {
>> + ? ? ? u32 dword_0;
>> + ? ? ? u32 dword_1;
>> + ? ? ? u32 dword_2;
>> + ? ? ? u32 dword_3;
>> +};
>> +
>> +/**
>> + * struct utp_transfer_req_desc - UTRD structure
>> + * @header: UTRD header DW-0 to DW-3
>> + * @command_desc_base_addr_lo: UCD base address low DW-4
>> + * @command_desc_base_addr_hi: UCD base address high DW-5
>> + * @response_upiu_length: response UPIU length DW-6
>> + * @response_upiu_offset: response UPIU offset DW-6
>> + * @prd_table_length: Physical region descriptor length DW-7
>> + * @prd_table_offset: Physical region descriptor offset DW-7
>> + */
>> +struct utp_transfer_req_desc {
>> +
>> + ? ? ? /* DW 0-3 */
>> + ? ? ? struct request_desc_header header;
>> +
>> + ? ? ? /* DW 4-5*/
>> + ? ? ? u32 ?command_desc_base_addr_lo;
>> + ? ? ? u32 ?command_desc_base_addr_hi;
>> +
>> + ? ? ? /* DW 6 */
>> + ? ? ? u16 ?response_upiu_length;
>> + ? ? ? u16 ?response_upiu_offset;
>> +
>> + ? ? ? /* DW 7 */
>> + ? ? ? u16 ?prd_table_length;
>> + ? ? ? u16 ?prd_table_offset;
>> +};
>> +
>> +/**
>> + * struct utp_task_req_desc - UTMRD structure
>> + * @header: UTMRD header DW-0 to DW-3
>> + * @task_req_upiu: Pointer to task request UPIU DW-4 to DW-11
>> + * @task_rsp_upiu: Pointer to task response UPIU DW12 to DW-19
>> + */
>> +struct utp_task_req_desc {
>> +
>> + ? ? ? /* DW 0-3 */
>> + ? ? ? struct request_desc_header header;
>> +
>> + ? ? ? /* DW 4-11 */
>> + ? ? ? u32 task_req_upiu[TASK_REQ_UPIU_SIZE_DWORDS];
>> +
>> + ? ? ? /* DW 12-19 */
>> + ? ? ? u32 task_rsp_upiu[TASK_RSP_UPIU_SIZE_DWORDS];
>> +};
>> +
>> +#endif /* End of Header */
>> --
>> 1.7.5.4
>>



--
~Santosh

2012-02-11 17:39:29

by Amit Sahrawat

[permalink] [raw]
Subject: Re: [PATCH 4/4] [SCSI] ufshcd: SCSI error handling

Hi Santosh,
Below is the original function in your patch:
+static int ufshcd_abort(struct scsi_cmnd *cmd)
+{
+ struct Scsi_Host *host;
+ struct ufs_hba *hba;
+ unsigned long flags;
+ unsigned int tag;
+ unsigned int pos;
+ int err;
+
+ host = cmd->device->host;
+ hba = (struct ufs_hba *) host->hostdata;
+ tag = cmd->request->tag;
+
+ spin_lock_irqsave(host->host_lock, flags);
+ pos = (1 << tag);
+
+ /* check if command is still pending */
+ if (!(hba->outstanding_reqs & pos)) {
+ err = -1;
+ spin_unlock_irqrestore(host->host_lock, flags);
at this place, you are taking care of unlocking - irqrestore - in case
of error. which is ok.
+ goto out;
+ }
+
+ err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
+ if (!err) {
+ spin_lock_irqsave(host->host_lock, flags);
+ scsi_dma_unmap(cmd);
+
+ /* clear the respective UTRLCLR bit */
+ writel(~pos,
+ (UFSHCD_MMIO_BASE +
+ REG_UTP_TRANSFER_REQ_LIST_CLEAR));
+ hba->outstanding_reqs &= ~pos;
+ hba->lrb[tag].cmd = NULL;
+ spin_unlock_irqrestore(host->host_lock, flags);
this unlocking - irqrestore - will work for your irqsave inside this
'if' - there is no return inside this
+ }
+out:
+ return err;
At this point, there is no unlocking-restore for spin_lock_irqsave
done at the entry of this function
+}
So, the changes I suggested will take care of handling the error
condition as well as the conditions for 'ufshcd_issue_tm_cmd'.

Please let me know in case I am missing something.

Regards,
Amit Sahrawat

On 2/7/12, Santosh Y <[email protected]> wrote:
> On Mon, Feb 6, 2012 at 12:46 PM, Amit Sahrawat
> <[email protected]> wrote:
>> Hi,
>>
>> In function:
>> +static int ufshcd_abort(struct scsi_cmnd *cmd)
>> +{...
>> - int err;
>> + int err = -1;
>> ...
>> + spin_lock_irqsave(host->host_lock, flags);
>> + pos = (1 << tag);
>> +
>> + /* check if command is still pending */
>> + if (!(hba->outstanding_reqs & pos)) {
>> - err = -1;
>> - spin_unlock_irqrestore(host->host_lock, flags);
>> + goto out;
>> + }
>> ...
>> ...
>> +out:
>> + spin_unlock_irqrestore(host->host_lock, flags);
>> + return err;
>> +}
>> this will also take of matching out
>> spin_lock_irqsave()->spin_unlock_irqrestore() before exiting the
>> function.
>
> That will not work again.
> In case "if (!(hba->outstanding_reqs & pos))" is false,
> i.e if the command for which the abort is issued still exists,
>
> ufshcd_issue_tm_cmd() will be called and if no error returned
> by the function, the code after "out:" will be executed. Which will
> try to "spin_unlock_irqrestore",
> even though no lock has been acquired.
I think you are just trying to disable interrupts and disable
preemtion - with spin_lock_irqsave/spin_unlock_irqsave
if you are looking to acquire lock - then may be
spin_lock_irq/spin_unlock_irq - will work.
>
>>
>> In function:
>> +static int
>> +ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>> + struct ufshcd_lrb *lrbp,
>> + u8 tm_function)
>> ...
>> + spin_lock_irqsave(host->host_lock, flags);
>> +
>> + /* If task management queue is full */
>> + if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
>> + dev_err(&hba->pdev->dev, "Task management queue full\n");
>> I think this will map to a Buffered printf - which can potentially
>> result in BUG: Schedule while atomic()...
>> + spin_unlock_irqrestore(host->host_lock, flags);
>> + return err;
>> + }
>> So, it could be like this:
>
> ok, Thanks, I'll look into it.
>
>> + spin_lock_irqsave(host->host_lock, flags);
>> +
>> + /* If task management queue is full */
>> + if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
>> + spin_unlock_irqrestore(host->host_lock, flags);
>> + dev_err(&hba->pdev->dev, "Task management queue full\n");
>> + return err;
>> + }
>>
>> Thanks & Regards,
>> Amit Sahrawat
>>
>>
>> On Thu, Feb 2, 2012 at 10:27 AM, Vinayak Holikatti
>> <[email protected]> wrote:
>>> From: Santosh Yaraganavi <[email protected]>
>>>
>>> UFSHCD SCSI error handling includes following implementations,
>>> - Abort task
>>> - Device reset
>>> - Host reset
>>>
>>> Signed-off-by: Santosh Yaraganavi <[email protected]>
>>> Signed-off-by: Vinayak Holikatti <[email protected]>
>>> Reviewed-by: Arnd Bergmann <[email protected]>
>>> Reviewed-by: Saugata Das <[email protected]>
>>> Reviewed-by: Vishak G <[email protected]>
>>> Reviewed-by: Girish K S <[email protected]>
>>> ---
>>> drivers/scsi/ufs/ufshcd.c | 312
>>> +++++++++++++++++++++++++++++++++++++++++++++
>>> 1 files changed, 312 insertions(+), 0 deletions(-)
>>>
>>> diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c
>>> index 1bfae84..7589dd1 100644
>>> --- a/drivers/scsi/ufs/ufshcd.c
>>> +++ b/drivers/scsi/ufs/ufshcd.c
>>> @@ -76,6 +76,8 @@
>>> #define NULL 0
>>> #endif /* NULL */
>>>
>>> +#define UFSHCD_MAX_TM_SLOTS 0xFF
>>> +
>>> #define BYTES_TO_DWORDS(p) (p >> 2)
>>> #define UFSHCD_MMIO_BASE (hba->mmio_base)
>>>
>>> @@ -83,6 +85,7 @@ enum {
>>> UFSHCD_MAX_CHANNEL = 1,
>>> UFSHCD_MAX_ID = 1,
>>> UFSHCD_MAX_LUNS = 8,
>>> + UFSHCD_MAX_TM_CMDS = 8,
>>> UFSHCD_CMD_PER_LUN = 16,
>>> UFSHCD_CAN_QUEUE = 32,
>>> BYTES_128 = 128,
>>> @@ -149,6 +152,7 @@ struct uic_command {
>>> * @host: Scsi_Host instance of the driver
>>> * @pdev: PCI device handle
>>> * @lrb: local reference block
>>> + * @outstanding_tasks: Bits representing outstanding task requests
>>> * @outstanding_reqs: Bits representing outstanding transfer requests
>>> * @capabilities: UFS Controller Capabilities
>>> * @nutrs: Transfer Request Queue depth supported by controller
>>> @@ -192,6 +196,7 @@ struct ufs_hba {
>>>
>>> struct ufshcd_lrb *lrb;
>>>
>>> + u32 outstanding_tasks;
>>> u32 outstanding_reqs;
>>>
>>> u32 capabilities;
>>> @@ -200,6 +205,8 @@ struct ufs_hba {
>>> u32 ufs_version;
>>>
>>> struct uic_command active_uic_cmd;
>>> + wait_queue_head_t ufshcd_tm_wait_queue;
>>> + u8 tm_condition[UFSHCD_MAX_TM_CMDS];
>>>
>>> u32 ufshcd_state;
>>> u32 int_enable_mask;
>>> @@ -278,6 +285,30 @@ static inline int ufshcd_get_tr_ocs(struct
>>> ufshcd_lrb *lrbp)
>>> }
>>>
>>> /**
>>> + * ufshcd_get_tmr_ocs - Get the UTMRD Overall Command Status
>>> + * @task_req_descp: pointer to utp_task_req_desc structure
>>> + *
>>> + * This function is used to get the OCS field from UTMRD
>>> + * Returns the OCS field in the UTMRD
>>> + */
>>> +static inline int
>>> +ufshcd_get_tmr_ocs(struct utp_task_req_desc *task_req_descp)
>>> +{
>>> + return task_req_descp->header.dword_2 & MASK_OCS;
>>> +}
>>> +
>>> +/**
>>> + * ufshcd_is_tmq_full - checks if the task management slots are full
>>> + * @outstanding_tasks: contains the task management doorbell value
>>> + *
>>> + * Returns 1 if a free slot available, 0 if task slots are full
>>> + */
>>> +static inline int ufshcd_is_tmq_full(u32 outstanding_tasks)
>>> +{
>>> + return (UFSHCD_MAX_TM_SLOTS == outstanding_tasks) ? 0 : 1;
>>> +}
>>> +
>>> +/**
>>> * ufshcd_get_lists_status - Check UCRDY, UTRLRDY and UTMRLRDY
>>> * @reg: Register value of host controller status
>>> *
>>> @@ -1098,6 +1129,45 @@ static void ufshcd_slave_destroy(struct
>>> scsi_device *sdev)
>>> }
>>>
>>> /**
>>> + * ufshcd_task_req_compl - handle task management request completion
>>> + * @hba: per adapter instance
>>> + * @index: index of the completed request
>>> + *
>>> + * Returns 0 on success, non-zero value on failure
>>> + */
>>> +static int ufshcd_task_req_compl(struct ufs_hba *hba, u32 index)
>>> +{
>>> + struct utp_upiu_task_rsp *task_rsp_upiup;
>>> + struct utp_task_req_desc *task_req_descp;
>>> + unsigned long flags;
>>> + int ocs_value;
>>> + int task_response = -1;
>>> +
>>> + spin_lock_irqsave(hba->host->host_lock, flags);
>>> +
>>> + /* clear completed tasks from outstanding_tasks */
>>> + hba->outstanding_tasks ^= index;
>>> +
>>> + task_req_descp =
>>> + (struct utp_task_req_desc
>>> *)hba->utmrdl_virt_addr_aligned;
>>> + ocs_value = ufshcd_get_tmr_ocs(&task_req_descp[index]);
>>> +
>>> + if (OCS_SUCCESS == ocs_value) {
>>> +
>>> + task_rsp_upiup = (struct utp_upiu_task_rsp *)
>>> + task_req_descp[index].task_rsp_upiu;
>>> + task_response =
>>> be32_to_cpu(task_rsp_upiup->header.dword_1);
>>> + task_response = ((task_response & MASK_TASK_RESPONSE) >>
>>> 8);
>>> +
>>> + /* clear task response */
>>> + memset(task_rsp_upiup, 0, sizeof(struct
>>> utp_upiu_task_rsp));
>>> + }
>>> + spin_unlock_irqrestore(hba->host->host_lock, flags);
>>> +
>>> + return task_response;
>>> +}
>>> +
>>> +/**
>>> * ufshcd_scsi_cmd_status - Update SCSI command result based on scsi
>>> status
>>> * @lrb: pointer to local reference block of completed command
>>> * @scsi_status: SCSI command status
>>> @@ -1314,6 +1384,31 @@ fatal_eh:
>>> }
>>>
>>> /**
>>> + * ufshcd_tmc_handler - handle task management function completion
>>> + * @hba: per adapter instance
>>> + */
>>> +static void ufshcd_tmc_handler(struct ufs_hba *hba)
>>> +{
>>> + u32 completed_reqs;
>>> + u32 tm_doorbell;
>>> + int i;
>>> +
>>> + tm_doorbell = readl(hba->mmio_base + REG_UTP_TASK_REQ_DOOR_BELL);
>>> + completed_reqs = tm_doorbell ^ hba->outstanding_tasks;
>>> + for (i = 0; i < hba->nutmrs; i++) {
>>> + if (completed_reqs & (1 << i)) {
>>> +
>>> + /*
>>> + * Change the default value 0xFF to 0 to indicate
>>> + * completion of respective task management
>>> command
>>> + */
>>> + hba->tm_condition[i] = 0;
>>> +
>>> wake_up_interruptible(&hba->ufshcd_tm_wait_queue);
>>> + }
>>> + }
>>> +}
>>> +
>>> +/**
>>> * ufshcd_sl_intr - Interrupt service routine
>>> * @hba: per adapter instance
>>> * @intr_status: contains interrupts generated by the controller
>>> @@ -1327,6 +1422,9 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32
>>> intr_status)
>>> if (intr_status & UIC_COMMAND_COMPL)
>>> schedule_work(&hba->uic_workq);
>>>
>>> + if (intr_status & UTP_TASK_REQ_COMPL)
>>> + ufshcd_tmc_handler(hba);
>>> +
>>> if (intr_status & UTP_TRANSFER_REQ_COMPL)
>>> ufshcd_transfer_req_compl(hba);
>>> }
>>> @@ -1362,6 +1460,209 @@ static irqreturn_t ufshcd_intr(int irq, void
>>> *__hba)
>>> return retval;
>>> }
>>>
>>> +/**
>>> + * ufshcd_issue_tm_cmd - issues task management commands to controller
>>> + * @hba: per adapter instance
>>> + * @lrbp: pointer to local reference block
>>> + *
>>> + * Returns 0 on success, non-zero value on failure
>>> + */
>>> +static int
>>> +ufshcd_issue_tm_cmd(struct ufs_hba *hba,
>>> + struct ufshcd_lrb *lrbp,
>>> + u8 tm_function)
>>> +{
>>> + struct utp_task_req_desc *task_req_descp;
>>> + struct utp_upiu_task_req *task_req_upiup;
>>> + struct Scsi_Host *host;
>>> + unsigned long flags;
>>> + int i;
>>> + int free_slot = 0;
>>> + int err = -1;
>>> +
>>> + host = hba->host;
>>> +
>>> + spin_lock_irqsave(host->host_lock, flags);
>>> +
>>> + /* If task management queue is full */
>>> + if (!ufshcd_is_tmq_full(hba->outstanding_tasks)) {
>>> + dev_err(&hba->pdev->dev, "Task management queue full\n");
>>> + spin_unlock_irqrestore(host->host_lock, flags);
>>> + return err;
>>> + }
>>> +
>>> + for (i = 0; i < hba->nutmrs; i++) {
>>> + if (!(hba->outstanding_tasks & (1 << i))) {
>>> + free_slot = i;
>>> + break;
>>> + }
>>> + }
>>> +
>>> + /* Configure task request descriptor */
>>> + task_req_descp =
>>> + (struct utp_task_req_desc *)
>>> hba->utmrdl_virt_addr_aligned;
>>> + task_req_descp += free_slot;
>>> +
>>> + memset(task_req_descp, 0, sizeof(struct utp_task_req_desc));
>>> + task_req_descp->header.dword_0 =
>>> cpu_to_le32(UTP_REQ_DESC_INT_CMD);
>>> + task_req_descp->header.dword_2 =
>>> + cpu_to_le32(OCS_INVALID_COMMAND_STATUS);
>>> +
>>> + /* Configure task request UPIU */
>>> + task_req_upiup =
>>> + (struct utp_upiu_task_req *)
>>> task_req_descp->task_req_upiu;
>>> + task_req_upiup->header.dword_0 =
>>> + cpu_to_be32(UPIU_HEADER_DWORD(UPIU_TRANSACTION_TASK_REQ,
>>> 0,
>>> + lrbp->lun,
>>> lrbp->task_tag));
>>> + task_req_upiup->header.dword_1 =
>>> + cpu_to_be32(UPIU_HEADER_DWORD(0, tm_function, 0, 0));
>>> +
>>> + task_req_upiup->input_param1 = lrbp->lun;
>>> + task_req_upiup->input_param1 =
>>> + cpu_to_be32(task_req_upiup->input_param1);
>>> + task_req_upiup->input_param2 = lrbp->task_tag;
>>> + task_req_upiup->input_param2 =
>>> + cpu_to_be32(task_req_upiup->input_param2);
>>> +
>>> + /* send command to the controller */
>>> + hba->outstanding_tasks |= (1 << free_slot);
>>> + writel((1 << free_slot),
>>> + (UFSHCD_MMIO_BASE + REG_UTP_TASK_REQ_DOOR_BELL));
>>> +
>>> + spin_unlock_irqrestore(host->host_lock, flags);
>>> +
>>> + /* wait until the task management command is completed */
>>> + err = wait_event_interruptible_timeout(hba->ufshcd_tm_wait_queue,
>>> + (hba->tm_condition[free_slot] !=
>>> 0),
>>> + 60 * HZ);
>>> + hba->tm_condition[free_slot] = 0xFF;
>>> + if (!err) {
>>> + dev_err(&hba->pdev->dev,
>>> + "Task management command timed-out\n");
>>> + return err;
>>> + }
>>> + return ufshcd_task_req_compl(hba, free_slot);
>>> +}
>>> +
>>> +/**
>>> + * ufshcd_device_reset - reset device and abort all the pending commands
>>> + * @cmd: SCSI command pointer
>>> + *
>>> + * Returns 0 on success, non-zero value on failure
>>> + */
>>> +static int ufshcd_device_reset(struct scsi_cmnd *cmd)
>>> +{
>>> + struct Scsi_Host *host;
>>> + struct ufs_hba *hba;
>>> + unsigned long flags;
>>> + int reset_lun;
>>> + unsigned int tag;
>>> + u32 i;
>>> + u32 pos; /* pos: represents a bit in a register */
>>> + int err = -1;
>>> +
>>> + host = cmd->device->host;
>>> + hba = (struct ufs_hba *)host->hostdata;
>>> + tag = cmd->request->tag;
>>> +
>>> + if ((hba->lrb[tag].cmd == cmd))
>>> + reset_lun = hba->lrb[tag].lun;
>>> + else
>>> + goto out;
>>> +
>>> + err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag],
>>> UFS_LOGICAL_RESET);
>>> + if (!err) {
>>> + spin_lock_irqsave(host->host_lock, flags);
>>> + for (i = 0; i < hba->nutrs; i++) {
>>> + pos = (1 << i);
>>> + if ((pos & hba->outstanding_reqs) &&
>>> + (reset_lun == hba->lrb[i].lun)) {
>>> +
>>> + /* clear the respective UTRLCLR bit */
>>> + writel(~pos, (UFSHCD_MMIO_BASE +
>>> + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
>>> +
>>> + hba->outstanding_reqs ^= pos;
>>> +
>>> + if (hba->lrb[i].cmd) {
>>> + scsi_dma_unmap(hba->lrb[i].cmd);
>>> + hba->lrb[i].cmd->result =
>>> + DID_ABORT << 16;
>>> + hba->lrb[i].cmd->scsi_done(cmd);
>>> + hba->lrb[i].cmd = NULL;
>>> + }
>>> + }
>>> + } /* end of for */
>>> + spin_unlock_irqrestore(host->host_lock, flags);
>>> + } /*end of if */
>>> +out:
>>> + return err;
>>> +}
>>> +
>>> +/**
>>> + * ufshcd_host_reset - Main reset function registered with scsi layer
>>> + * @cmd: SCSI command pointer
>>> + *
>>> + * Returns 0 on success, non-zero value on failure
>>> + */
>>> +static int ufshcd_host_reset(struct scsi_cmnd *cmd)
>>> +{
>>> + struct ufs_hba *hba = NULL;
>>> +
>>> + hba = (struct ufs_hba *) &cmd->device->host->hostdata[0];
>>> +
>>> + if (UFSHCD_STATE_RESET == hba->ufshcd_state)
>>> + return 0;
>>> +
>>> + return ufshcd_do_reset(hba) ? -1 : 0;
>>> +}
>>> +
>>> +/**
>>> + * ufshcd_abort - abort a specific command
>>> + * @cmd: SCSI command pointer
>>> + *
>>> + * Returns 0 on success, non-zero value on failure
>>> + */
>>> +static int ufshcd_abort(struct scsi_cmnd *cmd)
>>> +{
>>> + struct Scsi_Host *host;
>>> + struct ufs_hba *hba;
>>> + unsigned long flags;
>>> + unsigned int tag;
>>> + unsigned int pos;
>>> + int err;
>>> +
>>> + host = cmd->device->host;
>>> + hba = (struct ufs_hba *) host->hostdata;
>>> + tag = cmd->request->tag;
>>> +
>>> + spin_lock_irqsave(host->host_lock, flags);
>>> + pos = (1 << tag);
>>> +
>>> + /* check if command is still pending */
>>> + if (!(hba->outstanding_reqs & pos)) {
>>> + err = -1;
>>> + spin_unlock_irqrestore(host->host_lock, flags);
>>> + goto out;
>>> + }
>>> +
>>> + err = ufshcd_issue_tm_cmd(hba, &hba->lrb[tag], UFS_ABORT_TASK);
>>> + if (!err) {
>>> + spin_lock_irqsave(host->host_lock, flags);
>>> + scsi_dma_unmap(cmd);
>>> +
>>> + /* clear the respective UTRLCLR bit */
>>> + writel(~pos,
>>> + (UFSHCD_MMIO_BASE +
>>> + REG_UTP_TRANSFER_REQ_LIST_CLEAR));
>>> + hba->outstanding_reqs &= ~pos;
>>> + hba->lrb[tag].cmd = NULL;
>>> + spin_unlock_irqrestore(host->host_lock, flags);
>>> + }
>>> +out:
>>> + return err;
>>> +}
>>> +
>>> static struct scsi_host_template ufshcd_driver_template = {
>>> .module = THIS_MODULE,
>>> .name = UFSHCD,
>>> @@ -1369,6 +1670,9 @@ static struct scsi_host_template
>>> ufshcd_driver_template = {
>>> .queuecommand = ufshcd_queuecommand,
>>> .slave_alloc = ufshcd_slave_alloc,
>>> .slave_destroy = ufshcd_slave_destroy,
>>> + .eh_abort_handler = ufshcd_abort,
>>> + .eh_device_reset_handler = ufshcd_device_reset,
>>> + .eh_host_reset_handler = ufshcd_host_reset,
>>> .this_id = -1,
>>> .sg_tablesize = SG_ALL,
>>> .cmd_per_lun = UFSHCD_CMD_PER_LUN,
>>> @@ -1483,6 +1787,7 @@ ufshcd_probe(struct pci_dev *pdev, const struct
>>> pci_device_id *id)
>>> struct Scsi_Host *host;
>>> struct ufs_hba *hba;
>>> int ufs_hba_len;
>>> + int index;
>>> int err;
>>>
>>> ufs_hba_len = sizeof(struct ufs_hba);
>>> @@ -1547,6 +1852,13 @@ ufshcd_probe(struct pci_dev *pdev, const struct
>>> pci_device_id *id)
>>> host->unique_id = host->host_no;
>>> host->max_cmd_len = MAX_CDB_SIZE;
>>>
>>> + /* Initailze wait queue for task management */
>>> + init_waitqueue_head(&hba->ufshcd_tm_wait_queue);
>>> +
>>> + /* Initially assign invalid value to tm_condition */
>>> + for (index = 0; index < hba->nutmrs; index++)
>>> + hba->tm_condition[index] = 0xFF;
>>> +
>>> /* Initialize work queues */
>>> INIT_WORK(&hba->uic_workq, ufshcd_uic_cc_handler);
>>> INIT_WORK(&hba->feh_workq, ufshcd_fatal_err_handler);
>>> --
>>> 1.7.5.4
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-kernel"
>>> in
>>> the body of a message to [email protected]
>>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>> Please read the FAQ at http://www.tux.org/lkml/
>
>
>
> --
> ~Santosh
>

2012-02-11 19:52:24

by Arnd Bergmann

[permalink] [raw]
Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver

On Friday 10 February 2012, Santosh Y wrote:
> > UFS host controller specification Section 7.2.1, step 11, mentions
> > that the aggregation control register should be set if run/stop bit is
> > not enabled.
> > But In this case the run/ stop bit is set above before configuring the
> > aggregation register. Please check for the same in other places from
> > where it is called.
> >
>
> The spec mentions, (Section 7.2.1, step 11)
> "UTRIACR initialization may be executed at any time when the Run/Stop
> register (UTRLRSR)
> is not enabled or when no requests are outstanding."
>
> At this point in the code, during execution, there will not be any
> outstanding requests.

One meta-comment: When you reply on a long email like this, please
make sure you only quote the minimum amount of lines from the original
email to give enough context for your comment. Otherwise it becomes
much more time consuming to read your reply and readers might miss
your contribution in the middle of a long quote.

Arnd