Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1758393Ab2BITPp (ORCPT ); Thu, 9 Feb 2012 14:15:45 -0500 Received: from mail-qy0-f174.google.com ([209.85.216.174]:35387 "EHLO mail-qy0-f174.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1758336Ab2BITPl convert rfc822-to-8bit (ORCPT ); Thu, 9 Feb 2012 14:15:41 -0500 MIME-Version: 1.0 In-Reply-To: <1328158649-4137-2-git-send-email-vinholikatti@gmail.com> References: <1328158649-4137-1-git-send-email-vinholikatti@gmail.com> <1328158649-4137-2-git-send-email-vinholikatti@gmail.com> Date: Fri, 10 Feb 2012 00:45:39 +0530 Message-ID: Subject: Re: [PATCH 1/4] [SCSI] ufshcd: UFS Host controller driver From: Girish K S To: Vinayak Holikatti Cc: James.Bottomley@hansenpartnership.com, linux-scsi@vger.kernel.org, linux-kernel@vger.kernel.org, patches@linaro.org, linux-samsung-soc@vger.kernel.org, saugata.das@linaro.org, arnd@arndb.de, venkat@linaro.org, vishak.g@samsung.com, k.rajesh@samsung.com, yejin.moon@samsung.com, Santosh Yaraganavi , Sree kumar Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: 8BIT Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 66833 Lines: 1814 On 2 February 2012 10:27, Vinayak Holikatti wrote: > From: Santosh Yaraganavi > > 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 > Signed-off-by: Vinayak Holikatti > Reviewed-by: Arnd Bergmann > Reviewed-by: Saugata Das > Reviewed-by: Vishak G > Reviewed-by: Girish K S > --- > ?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 > +# Vinayak Holikatti > + > +# 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 > + * Vinayak Holikatti > + * > + * 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 > + * Vinayak Holikatti > + * > + * 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > + > +#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 > + * Vinayak Holikatti > + * > + * 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 > -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/