/* * Universal Flash Storage Host controller driver * * This code is based on drivers/scsi/ufs/ufshcd.h * Copyright (C) 2011-2013 Samsung India Software Operations * * Authors: * Santosh Yaraganavi <santosh.sy@samsung.com> * Vinayak Holikatti <h.vinayak@samsung.com> * * 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. * See the COPYING file in the top-level directory or visit * <http://www.gnu.org/licenses/gpl-2.0.html> * * 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. * * This program is provided "AS IS" and "WITH ALL FAULTS" and * without warranty of any kind. You are solely responsible for * determining the appropriateness of using and distributing * the program and assume all risks associated with your exercise * of rights with respect to the program, including but not limited * to infringement of third party rights, the risks and costs of * program errors, damage to or loss of data, programs or equipment, * and unavailability or interruption of operations. Under no * circumstances will the contributor of this Program be liable for * any damages of any kind arising from your use or distribution of * this program. */ #ifndef _UFSHCD_H #define _UFSHCD_H #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.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 <linux/bitops.h> #include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/completion.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_tcq.h> #include <scsi/scsi_dbg.h> #include <scsi/scsi_eh.h> #include "ufs.h" #include "ufshci.h" #define UFSHCD "ufshcd" #define UFSHCD_DRIVER_VERSION "0.2" enum dev_cmd_type { DEV_CMD_TYPE_NOP = 0x0, DEV_CMD_TYPE_QUERY = 0x1, }; /** * 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 * @done: UIC command completion */ struct uic_command { u32 command; u32 argument1; u32 argument2; u32 argument3; int cmd_active; int result; struct completion done; }; /** * struct ufshcd_lrb - local reference block * @utr_descriptor_ptr: UTRD address of the command * @ucd_req_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 to 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 * @intr_cmd: Interrupt command (doesn't participate in interrupt aggregation) */ struct ufshcd_lrb { struct utp_transfer_req_desc *utr_descriptor_ptr; struct utp_upiu_req *ucd_req_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; unsigned int lun; bool intr_cmd; }; /** * struct ufs_query - holds relevent data structures for query request * @request: request upiu and function * @descriptor: buffer for sending/receiving descriptor * @response: response upiu and response */ struct ufs_query { struct ufs_query_req request; u8 *descriptor; struct ufs_query_res response; }; /** * struct ufs_dev_cmd - all assosiated fields with device management commands * @type: device management command type - Query, NOP OUT * @lock: lock to allow one command at a time * @complete: internal commands completion * @tag_wq: wait queue until free command slot is available */ struct ufs_dev_cmd { enum dev_cmd_type type; struct mutex lock; struct completion *complete; wait_queue_head_t tag_wq; struct ufs_query query; }; /** * struct ufs_hba - per adapter private structure * @mmio_base: UFSHCI base register address * @ucdl_base_addr: UFS Command Descriptor base address * @utrdl_base_addr: UTP Transfer Request Descriptor base address * @utmrdl_base_addr: UTP Task Management Descriptor base address * @ucdl_dma_addr: UFS Command Descriptor DMA address * @utrdl_dma_addr: UTRDL DMA address * @utmrdl_dma_addr: UTMRDL DMA address * @host: Scsi_Host instance of the driver * @dev: device handle * @lrb: local reference block * @lrb_in_use: lrb in use * @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 * @nutmrs: Task Management Queue depth supported by controller * @ufs_version: UFS Version to which controller complies * @irq: Irq number of the controller * @active_uic_cmd: handle of active UIC command * @uic_cmd_mutex: mutex for uic command * @ufshcd_tm_wait_queue: wait queue for task management * @pwr_done: completion for power mode change * @tm_condition: condition variable for task management * @ufshcd_state: UFSHCD states * @intr_mask: Interrupt Mask Bits * @ee_ctrl_mask: Exception event control mask * @feh_workq: Work queue for fatal controller error handling * @eeh_work: Worker to handle exception events * @errors: HBA errors * @dev_cmd: ufs device management command information * @auto_bkops_enabled: to track whether bkops is enabled in device */ struct ufs_hba { void __iomem *mmio_base; /* Virtual memory reference */ struct utp_transfer_cmd_desc *ucdl_base_addr; struct utp_transfer_req_desc *utrdl_base_addr; struct utp_task_req_desc *utmrdl_base_addr; /* DMA memory reference */ dma_addr_t ucdl_dma_addr; dma_addr_t utrdl_dma_addr; dma_addr_t utmrdl_dma_addr; struct Scsi_Host *host; struct device *dev; struct ufshcd_lrb *lrb; unsigned long lrb_in_use; unsigned long outstanding_tasks; unsigned long outstanding_reqs; u32 capabilities; int nutrs; int nutmrs; u32 ufs_version; unsigned int irq; struct uic_command *active_uic_cmd; struct mutex uic_cmd_mutex; wait_queue_head_t ufshcd_tm_wait_queue; unsigned long tm_condition; struct completion *pwr_done; u32 ufshcd_state; u32 intr_mask; u16 ee_ctrl_mask; /* Work Queues */ struct work_struct feh_workq; struct work_struct eeh_work; /* HBA Errors */ u32 errors; /* Device management request data */ struct ufs_dev_cmd dev_cmd; bool auto_bkops_enabled; }; #define ufshcd_writel(hba, val, reg) \ writel((val), (hba)->mmio_base + (reg)) #define ufshcd_readl(hba, reg) \ readl((hba)->mmio_base + (reg)) int ufshcd_init(struct device *, struct ufs_hba ** , void __iomem * , unsigned int); void ufshcd_remove(struct ufs_hba *); /** * ufshcd_hba_stop - Send controller to reset state * @hba: per adapter instance */ static inline void ufshcd_hba_stop(struct ufs_hba *hba) { ufshcd_writel(hba, CONTROLLER_DISABLE, REG_CONTROLLER_ENABLE); } static inline void check_upiu_size(void) { BUILD_BUG_ON(ALIGNED_UPIU_SIZE < GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE); } extern int ufshcd_runtime_suspend(struct ufs_hba *hba); extern int ufshcd_runtime_resume(struct ufs_hba *hba); extern int ufshcd_runtime_idle(struct ufs_hba *hba); extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel, u8 attr_set, u32 mib_val, u8 peer); extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 #define DME_PEER 1 #define ATTR_SET_NOR 0 /* NORMAL */ #define ATTR_SET_ST 1 /* STATIC */ static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel, u32 mib_val) { return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR, mib_val, DME_LOCAL); } static inline int ufshcd_dme_st_set(struct ufs_hba *hba, u32 attr_sel, u32 mib_val) { return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST, mib_val, DME_LOCAL); } static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel, u32 mib_val) { return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_NOR, mib_val, DME_PEER); } static inline int ufshcd_dme_peer_st_set(struct ufs_hba *hba, u32 attr_sel, u32 mib_val) { return ufshcd_dme_set_attr(hba, attr_sel, ATTR_SET_ST, mib_val, DME_PEER); } static inline int ufshcd_dme_get(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val) { return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_LOCAL); } static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val) { return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER); } #endif /* End of Header */