2012-02-29 10:41:50 +04:00
/*
2013-02-25 20:14:33 +04:00
* Universal Flash Storage Host controller driver Core
2012-02-29 10:41:50 +04:00
*
* This code is based on drivers / scsi / ufs / ufshcd . c
2013-02-25 20:14:32 +04:00
* Copyright ( C ) 2011 - 2013 Samsung India Software Operations
2012-02-29 10:41:50 +04:00
*
2013-02-25 20:14:32 +04:00
* Authors :
* Santosh Yaraganavi < santosh . sy @ samsung . com >
* Vinayak Holikatti < h . vinayak @ samsung . com >
2012-02-29 10:41:50 +04:00
*
* 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 .
2013-02-25 20:14:32 +04:00
* See the COPYING file in the top - level directory or visit
* < http : //www.gnu.org/licenses/gpl-2.0.html>
2012-02-29 10:41:50 +04:00
*
* 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 .
*
2013-02-25 20:14:32 +04:00
* 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 .
2012-02-29 10:41:50 +04:00
*/
2013-06-26 21:09:29 +04:00
# include <linux/async.h>
2013-02-25 20:14:33 +04:00
# include "ufshcd.h"
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:27 +04:00
# define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\
UTP_TASK_REQ_COMPL | \
UFSHCD_ERROR_MASK )
2013-06-26 21:09:29 +04:00
/* UIC command timeout, unit: ms */
# define UIC_CMD_TIMEOUT 500
2013-06-26 21:09:27 +04:00
2013-07-29 23:05:57 +04:00
/* NOP OUT retries waiting for NOP IN response */
# define NOP_OUT_RETRIES 10
/* Timeout after 30 msecs if NOP OUT hangs without response */
# define NOP_OUT_TIMEOUT 30 /* msecs */
2013-07-29 23:05:58 +04:00
/* Query request retries */
# define QUERY_REQ_RETRIES 10
/* Query request timeout */
# define QUERY_REQ_TIMEOUT 30 /* msec */
/* Expose the flag value from utp_upiu_query.value */
# define MASK_QUERY_UPIU_FLAG_LOC 0xFF
2012-02-29 10:41:50 +04:00
enum {
UFSHCD_MAX_CHANNEL = 0 ,
UFSHCD_MAX_ID = 1 ,
UFSHCD_MAX_LUNS = 8 ,
UFSHCD_CMD_PER_LUN = 32 ,
UFSHCD_CAN_QUEUE = 32 ,
} ;
/* 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 ,
} ;
2013-07-29 23:05:57 +04:00
/*
* ufshcd_wait_for_register - wait for register value to change
* @ hba - per - adapter interface
* @ reg - mmio register offset
* @ mask - mask to apply to read register value
* @ val - wait condition
* @ interval_us - polling interval in microsecs
* @ timeout_ms - timeout in millisecs
*
* Returns - ETIMEDOUT on error , zero on success
*/
static int ufshcd_wait_for_register ( struct ufs_hba * hba , u32 reg , u32 mask ,
u32 val , unsigned long interval_us , unsigned long timeout_ms )
{
int err = 0 ;
unsigned long timeout = jiffies + msecs_to_jiffies ( timeout_ms ) ;
/* ignore bits that we don't intend to wait on */
val = val & mask ;
while ( ( ufshcd_readl ( hba , reg ) & mask ) ! = val ) {
/* wakeup within 50us of expiry */
usleep_range ( interval_us , interval_us + 50 ) ;
if ( time_after ( jiffies , timeout ) ) {
if ( ( ufshcd_readl ( hba , reg ) & mask ) ! = val )
err = - ETIMEDOUT ;
break ;
}
}
return err ;
}
2013-06-26 21:09:27 +04:00
/**
* ufshcd_get_intr_mask - Get the interrupt bit mask
* @ hba - Pointer to adapter instance
*
* Returns interrupt bit mask per version
*/
static inline u32 ufshcd_get_intr_mask ( struct ufs_hba * hba )
{
if ( hba - > ufs_version = = UFSHCI_VERSION_10 )
return INTERRUPT_MASK_ALL_VER_10 ;
else
return INTERRUPT_MASK_ALL_VER_11 ;
}
2012-02-29 10:41:50 +04:00
/**
* 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 )
{
2013-06-26 21:09:26 +04:00
return ufshcd_readl ( hba , REG_UFS_VERSION ) ;
2012-02-29 10:41:50 +04:00
}
/**
* ufshcd_is_device_present - Check if any device connected to
* the host controller
* @ reg_hcs - host controller status register value
*
2012-07-10 18:09:23 +04:00
* Returns 1 if device present , 0 if no device detected
2012-02-29 10:41:50 +04:00
*/
static inline int ufshcd_is_device_present ( u32 reg_hcs )
{
2012-07-10 18:09:23 +04:00
return ( DEVICE_PRESENT & reg_hcs ) ? 1 : 0 ;
2012-02-29 10:41:50 +04:00
}
/**
* 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_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_get_tm_free_slot - get a free slot for task management request
* @ hba : per adapter instance
*
* Returns maximum number of task management request slots in case of
* task management queue full or returns the free slot number
*/
static inline int ufshcd_get_tm_free_slot ( struct ufs_hba * hba )
{
return find_first_zero_bit ( & hba - > outstanding_tasks , hba - > nutmrs ) ;
}
/**
* ufshcd_utrl_clear - Clear a bit in UTRLCLR register
* @ hba : per adapter instance
* @ pos : position of the bit to be cleared
*/
static inline void ufshcd_utrl_clear ( struct ufs_hba * hba , u32 pos )
{
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , ~ ( 1 < < pos ) , REG_UTP_TRANSFER_REQ_LIST_CLEAR ) ;
2012-02-29 10:41:50 +04:00
}
/**
* 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 )
{
2013-06-26 21:09:26 +04:00
return ufshcd_readl ( hba , REG_UIC_COMMAND_ARG_2 ) &
2012-02-29 10:41:50 +04:00
MASK_UIC_COMMAND_RESULT ;
}
/**
2013-07-29 23:05:57 +04:00
* ufshcd_get_req_rsp - returns the TR response transaction type
2012-02-29 10:41:50 +04:00
* @ ucd_rsp_ptr : pointer to response UPIU
*/
static inline int
2013-07-29 23:05:57 +04:00
ufshcd_get_req_rsp ( struct utp_upiu_rsp * ucd_rsp_ptr )
2012-02-29 10:41:50 +04:00
{
2013-07-29 23:05:57 +04:00
return be32_to_cpu ( ucd_rsp_ptr - > header . dword_0 ) > > 24 ;
2012-02-29 10:41:50 +04:00
}
/**
* 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 ;
}
2013-07-29 23:05:59 +04:00
/**
* ufshcd_is_exception_event - Check if the device raised an exception event
* @ ucd_rsp_ptr : pointer to response UPIU
*
* The function checks if the device raised an exception event indicated in
* the Device Information field of response UPIU .
*
* Returns true if exception is raised , false otherwise .
*/
static inline bool ufshcd_is_exception_event ( struct utp_upiu_rsp * ucd_rsp_ptr )
{
return be32_to_cpu ( ucd_rsp_ptr - > header . dword_2 ) &
MASK_RSP_EXCEPTION_EVENT ? true : false ;
}
2012-02-29 10:41:50 +04:00
/**
* 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 :
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , INT_AGGR_ENABLE |
INT_AGGR_COUNTER_AND_TIMER_RESET ,
REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL ) ;
2012-02-29 10:41:50 +04:00
break ;
case INT_AGGR_CONFIG :
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , INT_AGGR_ENABLE | INT_AGGR_PARAM_WRITE |
INT_AGGR_COUNTER_THRESHOLD_VALUE |
INT_AGGR_TIMEOUT_VALUE ,
REG_UTP_TRANSFER_REQ_INT_AGG_CONTROL ) ;
2012-02-29 10:41:50 +04:00
break ;
}
}
/**
* ufshcd_enable_run_stop_reg - Enable run - stop registers ,
* When run - stop registers are set to 1 , it indicates the
* host controller that it can process the requests
* @ hba : per adapter instance
*/
static void ufshcd_enable_run_stop_reg ( struct ufs_hba * hba )
{
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , UTP_TASK_REQ_LIST_RUN_STOP_BIT ,
REG_UTP_TASK_REQ_LIST_RUN_STOP ) ;
ufshcd_writel ( hba , UTP_TRANSFER_REQ_LIST_RUN_STOP_BIT ,
REG_UTP_TRANSFER_REQ_LIST_RUN_STOP ) ;
2012-02-29 10:41:50 +04:00
}
/**
* ufshcd_hba_start - Start controller initialization sequence
* @ hba : per adapter instance
*/
static inline void ufshcd_hba_start ( struct ufs_hba * hba )
{
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , CONTROLLER_ENABLE , REG_CONTROLLER_ENABLE ) ;
2012-02-29 10:41:50 +04:00
}
/**
* ufshcd_is_hba_active - Get controller state
* @ hba : per adapter instance
*
* Returns zero if controller is active , 1 otherwise
*/
static inline int ufshcd_is_hba_active ( struct ufs_hba * hba )
{
2013-06-26 21:09:26 +04:00
return ( ufshcd_readl ( hba , REG_CONTROLLER_ENABLE ) & 0x1 ) ? 0 : 1 ;
2012-02-29 10:41:50 +04:00
}
/**
* 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 )
{
__set_bit ( task_tag , & hba - > outstanding_reqs ) ;
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , 1 < < task_tag , REG_UTP_TRANSFER_REQ_DOOR_BELL ) ;
2012-02-29 10:41:50 +04:00
}
/**
* 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 ) {
2013-07-29 23:05:57 +04:00
len = be16_to_cpu ( lrbp - > ucd_rsp_ptr - > sr . sense_data_len ) ;
2012-02-29 10:41:50 +04:00
memcpy ( lrbp - > sense_buffer ,
2013-07-29 23:05:57 +04:00
lrbp - > ucd_rsp_ptr - > sr . sense_data ,
2012-02-29 10:41:50 +04:00
min_t ( int , len , SCSI_SENSE_BUFFERSIZE ) ) ;
}
}
2013-07-29 23:05:58 +04:00
/**
* ufshcd_query_to_cpu ( ) - formats the buffer to native cpu endian
* @ response : upiu query response to convert
*/
static inline void ufshcd_query_to_cpu ( struct utp_upiu_query * response )
{
response - > length = be16_to_cpu ( response - > length ) ;
response - > value = be32_to_cpu ( response - > value ) ;
}
/**
* ufshcd_query_to_be ( ) - formats the buffer to big endian
* @ request : upiu query request to convert
*/
static inline void ufshcd_query_to_be ( struct utp_upiu_query * request )
{
request - > length = cpu_to_be16 ( request - > length ) ;
request - > value = cpu_to_be32 ( request - > value ) ;
}
/**
* ufshcd_copy_query_response ( ) - Copy the Query Response and the data
* descriptor
* @ hba : per adapter instance
* @ lrb - pointer to local reference block
*/
static
void ufshcd_copy_query_response ( struct ufs_hba * hba , struct ufshcd_lrb * lrbp )
{
struct ufs_query_res * query_res = & hba - > dev_cmd . query . response ;
/* Get the UPIU response */
query_res - > response = ufshcd_get_rsp_upiu_result ( lrbp - > ucd_rsp_ptr ) > >
UPIU_RSP_CODE_OFFSET ;
memcpy ( & query_res - > upiu_res , & lrbp - > ucd_rsp_ptr - > qr , QUERY_OSF_SIZE ) ;
ufshcd_query_to_cpu ( & query_res - > upiu_res ) ;
/* Get the descriptor */
if ( lrbp - > ucd_rsp_ptr - > qr . opcode = = UPIU_QUERY_OPCODE_READ_DESC ) {
u8 * descp = ( u8 * ) & lrbp - > ucd_rsp_ptr +
GENERAL_UPIU_REQUEST_SIZE ;
u16 len ;
/* data segment length */
len = be32_to_cpu ( lrbp - > ucd_rsp_ptr - > header . dword_2 ) &
MASK_QUERY_DATA_SEG_LEN ;
memcpy ( hba - > dev_cmd . query . descriptor , descp ,
min_t ( u16 , len , QUERY_DESC_MAX_SIZE ) ) ;
}
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_hba_capabilities - Read controller capabilities
* @ hba : per adapter instance
*/
static inline void ufshcd_hba_capabilities ( struct ufs_hba * hba )
{
2013-06-26 21:09:26 +04:00
hba - > capabilities = ufshcd_readl ( hba , REG_CONTROLLER_CAPABILITIES ) ;
2012-02-29 10:41:50 +04:00
/* nutrs and nutmrs are 0 based values */
hba - > nutrs = ( hba - > capabilities & MASK_TRANSFER_REQUESTS_SLOTS ) + 1 ;
hba - > nutmrs =
( ( hba - > capabilities & MASK_TASK_MANAGEMENT_REQUEST_SLOTS ) > > 16 ) + 1 ;
}
/**
2013-06-26 21:09:29 +04:00
* ufshcd_ready_for_uic_cmd - Check if controller is ready
* to accept UIC commands
2012-02-29 10:41:50 +04:00
* @ hba : per adapter instance
2013-06-26 21:09:29 +04:00
* Return true on success , else false
*/
static inline bool ufshcd_ready_for_uic_cmd ( struct ufs_hba * hba )
{
if ( ufshcd_readl ( hba , REG_CONTROLLER_STATUS ) & UIC_COMMAND_READY )
return true ;
else
return false ;
}
/**
* ufshcd_dispatch_uic_cmd - Dispatch UIC commands to unipro layers
* @ hba : per adapter instance
* @ uic_cmd : UIC command
*
* Mutex must be held .
2012-02-29 10:41:50 +04:00
*/
static inline void
2013-06-26 21:09:29 +04:00
ufshcd_dispatch_uic_cmd ( struct ufs_hba * hba , struct uic_command * uic_cmd )
2012-02-29 10:41:50 +04:00
{
2013-06-26 21:09:29 +04:00
WARN_ON ( hba - > active_uic_cmd ) ;
hba - > active_uic_cmd = uic_cmd ;
2012-02-29 10:41:50 +04:00
/* Write Args */
2013-06-26 21:09:29 +04:00
ufshcd_writel ( hba , uic_cmd - > argument1 , REG_UIC_COMMAND_ARG_1 ) ;
ufshcd_writel ( hba , uic_cmd - > argument2 , REG_UIC_COMMAND_ARG_2 ) ;
ufshcd_writel ( hba , uic_cmd - > argument3 , REG_UIC_COMMAND_ARG_3 ) ;
2012-02-29 10:41:50 +04:00
/* Write UIC Cmd */
2013-06-26 21:09:29 +04:00
ufshcd_writel ( hba , uic_cmd - > command & COMMAND_OPCODE_MASK ,
2013-06-26 21:09:26 +04:00
REG_UIC_COMMAND ) ;
2012-02-29 10:41:50 +04:00
}
2013-06-26 21:09:29 +04:00
/**
* ufshcd_wait_for_uic_cmd - Wait complectioin of UIC command
* @ hba : per adapter instance
* @ uic_command : UIC command
*
* Must be called with mutex held .
* Returns 0 only if success .
*/
static int
ufshcd_wait_for_uic_cmd ( struct ufs_hba * hba , struct uic_command * uic_cmd )
{
int ret ;
unsigned long flags ;
if ( wait_for_completion_timeout ( & uic_cmd - > done ,
msecs_to_jiffies ( UIC_CMD_TIMEOUT ) ) )
ret = uic_cmd - > argument2 & MASK_UIC_COMMAND_RESULT ;
else
ret = - ETIMEDOUT ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
hba - > active_uic_cmd = NULL ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
return ret ;
}
/**
* __ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
* @ hba : per adapter instance
* @ uic_cmd : UIC command
*
* Identical to ufshcd_send_uic_cmd ( ) expect mutex . Must be called
* with mutex held .
* Returns 0 only if success .
*/
static int
__ufshcd_send_uic_cmd ( struct ufs_hba * hba , struct uic_command * uic_cmd )
{
int ret ;
unsigned long flags ;
if ( ! ufshcd_ready_for_uic_cmd ( hba ) ) {
dev_err ( hba - > dev ,
" Controller not ready to accept UIC commands \n " ) ;
return - EIO ;
}
init_completion ( & uic_cmd - > done ) ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
ufshcd_dispatch_uic_cmd ( hba , uic_cmd ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
ret = ufshcd_wait_for_uic_cmd ( hba , uic_cmd ) ;
return ret ;
}
/**
* ufshcd_send_uic_cmd - Send UIC commands and retrieve the result
* @ hba : per adapter instance
* @ uic_cmd : UIC command
*
* Returns 0 only if success .
*/
static int
ufshcd_send_uic_cmd ( struct ufs_hba * hba , struct uic_command * uic_cmd )
{
int ret ;
mutex_lock ( & hba - > uic_cmd_mutex ) ;
ret = __ufshcd_send_uic_cmd ( hba , uic_cmd ) ;
mutex_unlock ( & hba - > uic_cmd_mutex ) ;
return ret ;
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_map_sg - Map scatter - gather list to prdt
* @ lrbp - pointer to local reference block
*
* Returns 0 in case of success , non - zero value in case of failure
*/
static int 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 ) ;
if ( sg_segments < 0 )
return sg_segments ;
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 ( lower_32_bits ( sg - > dma_address ) ) ;
prd_table [ i ] . upper_addr =
cpu_to_le32 ( upper_32_bits ( sg - > dma_address ) ) ;
}
} else {
lrbp - > utr_descriptor_ptr - > prd_table_length = 0 ;
}
return 0 ;
}
/**
2013-06-26 21:09:27 +04:00
* ufshcd_enable_intr - enable interrupts
2012-02-29 10:41:50 +04:00
* @ hba : per adapter instance
2013-06-26 21:09:27 +04:00
* @ intrs : interrupt bits
2012-02-29 10:41:50 +04:00
*/
2013-06-26 21:09:27 +04:00
static void ufshcd_enable_intr ( struct ufs_hba * hba , u32 intrs )
2012-02-29 10:41:50 +04:00
{
2013-06-26 21:09:27 +04:00
u32 set = ufshcd_readl ( hba , REG_INTERRUPT_ENABLE ) ;
if ( hba - > ufs_version = = UFSHCI_VERSION_10 ) {
u32 rw ;
rw = set & INTERRUPT_MASK_RW_VER_10 ;
set = rw | ( ( set ^ intrs ) & intrs ) ;
} else {
set | = intrs ;
}
ufshcd_writel ( hba , set , REG_INTERRUPT_ENABLE ) ;
}
/**
* ufshcd_disable_intr - disable interrupts
* @ hba : per adapter instance
* @ intrs : interrupt bits
*/
static void ufshcd_disable_intr ( struct ufs_hba * hba , u32 intrs )
{
u32 set = ufshcd_readl ( hba , REG_INTERRUPT_ENABLE ) ;
if ( hba - > ufs_version = = UFSHCI_VERSION_10 ) {
u32 rw ;
rw = ( set & INTERRUPT_MASK_RW_VER_10 ) &
~ ( intrs & INTERRUPT_MASK_RW_VER_10 ) ;
set = rw | ( ( set & intrs ) & ~ INTERRUPT_MASK_RW_VER_10 ) ;
} else {
set & = ~ intrs ;
2012-02-29 10:41:50 +04:00
}
2013-06-26 21:09:27 +04:00
ufshcd_writel ( hba , set , REG_INTERRUPT_ENABLE ) ;
2012-02-29 10:41:50 +04:00
}
2013-07-29 23:05:57 +04:00
/**
* ufshcd_prepare_req_desc_hdr ( ) - Fills the requests header
* descriptor according to request
* @ lrbp : pointer to local reference block
* @ upiu_flags : flags required in the header
* @ cmd_dir : requests data direction
*/
static void ufshcd_prepare_req_desc_hdr ( struct ufshcd_lrb * lrbp ,
u32 * upiu_flags , enum dma_data_direction cmd_dir )
{
struct utp_transfer_req_desc * req_desc = lrbp - > utr_descriptor_ptr ;
u32 data_direction ;
u32 dword_0 ;
if ( cmd_dir = = DMA_FROM_DEVICE ) {
data_direction = UTP_DEVICE_TO_HOST ;
* upiu_flags = UPIU_CMD_FLAGS_READ ;
} else if ( cmd_dir = = DMA_TO_DEVICE ) {
data_direction = UTP_HOST_TO_DEVICE ;
* upiu_flags = UPIU_CMD_FLAGS_WRITE ;
} else {
data_direction = UTP_NO_DATA_TRANSFER ;
* upiu_flags = UPIU_CMD_FLAGS_NONE ;
}
dword_0 = data_direction | ( lrbp - > command_type
< < UPIU_COMMAND_TYPE_OFFSET ) ;
if ( lrbp - > intr_cmd )
dword_0 | = UTP_REQ_DESC_INT_CMD ;
/* Transfer request descriptor header fields */
req_desc - > header . dword_0 = cpu_to_le32 ( dword_0 ) ;
/*
* 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 ) ;
}
/**
* ufshcd_prepare_utp_scsi_cmd_upiu ( ) - fills the utp_transfer_req_desc ,
* for scsi commands
* @ lrbp - local reference block pointer
* @ upiu_flags - flags
*/
static
void ufshcd_prepare_utp_scsi_cmd_upiu ( struct ufshcd_lrb * lrbp , u32 upiu_flags )
{
struct utp_upiu_req * ucd_req_ptr = lrbp - > ucd_req_ptr ;
/* command descriptor fields */
ucd_req_ptr - > header . dword_0 = UPIU_HEADER_DWORD (
UPIU_TRANSACTION_COMMAND , upiu_flags ,
lrbp - > lun , lrbp - > task_tag ) ;
ucd_req_ptr - > header . dword_1 = UPIU_HEADER_DWORD (
UPIU_COMMAND_SET_TYPE_SCSI , 0 , 0 , 0 ) ;
/* Total EHS length and Data segment length will be zero */
ucd_req_ptr - > header . dword_2 = 0 ;
ucd_req_ptr - > sc . exp_data_transfer_len =
cpu_to_be32 ( lrbp - > cmd - > sdb . length ) ;
memcpy ( ucd_req_ptr - > sc . cdb , lrbp - > cmd - > cmnd ,
( min_t ( unsigned short , lrbp - > cmd - > cmd_len , MAX_CDB_SIZE ) ) ) ;
}
2013-07-29 23:05:58 +04:00
/**
* ufshcd_prepare_utp_query_req_upiu ( ) - fills the utp_transfer_req_desc ,
* for query requsts
* @ hba : UFS hba
* @ lrbp : local reference block pointer
* @ upiu_flags : flags
*/
static void ufshcd_prepare_utp_query_req_upiu ( struct ufs_hba * hba ,
struct ufshcd_lrb * lrbp , u32 upiu_flags )
{
struct utp_upiu_req * ucd_req_ptr = lrbp - > ucd_req_ptr ;
struct ufs_query * query = & hba - > dev_cmd . query ;
u16 len = query - > request . upiu_req . length ;
u8 * descp = ( u8 * ) lrbp - > ucd_req_ptr + GENERAL_UPIU_REQUEST_SIZE ;
/* Query request header */
ucd_req_ptr - > header . dword_0 = UPIU_HEADER_DWORD (
UPIU_TRANSACTION_QUERY_REQ , upiu_flags ,
lrbp - > lun , lrbp - > task_tag ) ;
ucd_req_ptr - > header . dword_1 = UPIU_HEADER_DWORD (
0 , query - > request . query_func , 0 , 0 ) ;
/* Data segment length */
ucd_req_ptr - > header . dword_2 = UPIU_HEADER_DWORD (
0 , 0 , len > > 8 , ( u8 ) len ) ;
/* Copy the Query Request buffer as is */
memcpy ( & ucd_req_ptr - > qr , & query - > request . upiu_req ,
QUERY_OSF_SIZE ) ;
ufshcd_query_to_be ( & ucd_req_ptr - > qr ) ;
/* Copy the Descriptor */
if ( ( len > 0 ) & & ( query - > request . upiu_req . opcode = =
UPIU_QUERY_OPCODE_WRITE_DESC ) ) {
memcpy ( descp , query - > descriptor ,
min_t ( u16 , len , QUERY_DESC_MAX_SIZE ) ) ;
}
}
2013-07-29 23:05:57 +04:00
static inline void ufshcd_prepare_utp_nop_upiu ( struct ufshcd_lrb * lrbp )
{
struct utp_upiu_req * ucd_req_ptr = lrbp - > ucd_req_ptr ;
memset ( ucd_req_ptr , 0 , sizeof ( struct utp_upiu_req ) ) ;
/* command descriptor fields */
ucd_req_ptr - > header . dword_0 =
UPIU_HEADER_DWORD (
UPIU_TRANSACTION_NOP_OUT , 0 , 0 , lrbp - > task_tag ) ;
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_compose_upiu - form UFS Protocol Information Unit ( UPIU )
2013-07-29 23:05:57 +04:00
* @ hba - per adapter instance
2012-02-29 10:41:50 +04:00
* @ lrb - pointer to local reference block
*/
2013-07-29 23:05:57 +04:00
static int ufshcd_compose_upiu ( struct ufs_hba * hba , struct ufshcd_lrb * lrbp )
2012-02-29 10:41:50 +04:00
{
u32 upiu_flags ;
2013-07-29 23:05:57 +04:00
int ret = 0 ;
2012-02-29 10:41:50 +04:00
switch ( lrbp - > command_type ) {
case UTP_CMD_TYPE_SCSI :
2013-07-29 23:05:57 +04:00
if ( likely ( lrbp - > cmd ) ) {
ufshcd_prepare_req_desc_hdr ( lrbp , & upiu_flags ,
lrbp - > cmd - > sc_data_direction ) ;
ufshcd_prepare_utp_scsi_cmd_upiu ( lrbp , upiu_flags ) ;
2012-02-29 10:41:50 +04:00
} else {
2013-07-29 23:05:57 +04:00
ret = - EINVAL ;
2012-02-29 10:41:50 +04:00
}
break ;
case UTP_CMD_TYPE_DEV_MANAGE :
2013-07-29 23:05:57 +04:00
ufshcd_prepare_req_desc_hdr ( lrbp , & upiu_flags , DMA_NONE ) ;
2013-07-29 23:05:58 +04:00
if ( hba - > dev_cmd . type = = DEV_CMD_TYPE_QUERY )
ufshcd_prepare_utp_query_req_upiu (
hba , lrbp , upiu_flags ) ;
else if ( hba - > dev_cmd . type = = DEV_CMD_TYPE_NOP )
2013-07-29 23:05:57 +04:00
ufshcd_prepare_utp_nop_upiu ( lrbp ) ;
else
ret = - EINVAL ;
2012-02-29 10:41:50 +04:00
break ;
case UTP_CMD_TYPE_UFS :
/* For UFS native command implementation */
2013-07-29 23:05:57 +04:00
ret = - ENOTSUPP ;
dev_err ( hba - > dev , " %s: UFS native command are not supported \n " ,
__func__ ) ;
break ;
default :
ret = - ENOTSUPP ;
dev_err ( hba - > dev , " %s: unknown command type: 0x%x \n " ,
__func__ , lrbp - > command_type ) ;
2012-02-29 10:41:50 +04:00
break ;
} /* end of switch */
2013-07-29 23:05:57 +04:00
return ret ;
2012-02-29 10:41:50 +04:00
}
/**
* ufshcd_queuecommand - main entry point for SCSI requests
* @ cmd : command from SCSI Midlayer
* @ done : call back function
*
* Returns 0 for success , non - zero 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 ;
int err = 0 ;
hba = shost_priv ( host ) ;
tag = cmd - > request - > tag ;
if ( hba - > ufshcd_state ! = UFSHCD_STATE_OPERATIONAL ) {
err = SCSI_MLQUEUE_HOST_BUSY ;
goto out ;
}
2013-07-29 23:05:57 +04:00
/* acquire the tag to make sure device cmds don't use it */
if ( test_and_set_bit_lock ( tag , & hba - > lrb_in_use ) ) {
/*
* Dev manage command in progress , requeue the command .
* Requeuing the command helps in cases where the request * may *
* find different tag instead of waiting for dev manage command
* completion .
*/
err = SCSI_MLQUEUE_HOST_BUSY ;
goto out ;
}
2012-02-29 10:41:50 +04:00
lrbp = & hba - > lrb [ tag ] ;
2013-07-29 23:05:57 +04:00
WARN_ON ( lrbp - > cmd ) ;
2012-02-29 10:41:50 +04:00
lrbp - > cmd = cmd ;
lrbp - > sense_bufflen = SCSI_SENSE_BUFFERSIZE ;
lrbp - > sense_buffer = cmd - > sense_buffer ;
lrbp - > task_tag = tag ;
lrbp - > lun = cmd - > device - > lun ;
2013-07-29 23:05:57 +04:00
lrbp - > intr_cmd = false ;
2012-02-29 10:41:50 +04:00
lrbp - > command_type = UTP_CMD_TYPE_SCSI ;
/* form UPIU before issuing the command */
2013-07-29 23:05:57 +04:00
ufshcd_compose_upiu ( hba , lrbp ) ;
2012-02-29 10:41:50 +04:00
err = ufshcd_map_sg ( lrbp ) ;
2013-07-29 23:05:57 +04:00
if ( err ) {
lrbp - > cmd = NULL ;
clear_bit_unlock ( tag , & hba - > lrb_in_use ) ;
2012-02-29 10:41:50 +04:00
goto out ;
2013-07-29 23:05:57 +04:00
}
2012-02-29 10:41:50 +04:00
/* issue command to the controller */
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
ufshcd_send_command ( hba , tag ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
out :
return err ;
}
2013-07-29 23:05:57 +04:00
static int ufshcd_compose_dev_cmd ( struct ufs_hba * hba ,
struct ufshcd_lrb * lrbp , enum dev_cmd_type cmd_type , int tag )
{
lrbp - > cmd = NULL ;
lrbp - > sense_bufflen = 0 ;
lrbp - > sense_buffer = NULL ;
lrbp - > task_tag = tag ;
lrbp - > lun = 0 ; /* device management cmd is not specific to any LUN */
lrbp - > command_type = UTP_CMD_TYPE_DEV_MANAGE ;
lrbp - > intr_cmd = true ; /* No interrupt aggregation */
hba - > dev_cmd . type = cmd_type ;
return ufshcd_compose_upiu ( hba , lrbp ) ;
}
static int
ufshcd_clear_cmd ( struct ufs_hba * hba , int tag )
{
int err = 0 ;
unsigned long flags ;
u32 mask = 1 < < tag ;
/* clear outstanding transaction before retry */
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
ufshcd_utrl_clear ( hba , tag ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
/*
* wait for for h / w to clear corresponding bit in door - bell .
* max . wait is 1 sec .
*/
err = ufshcd_wait_for_register ( hba ,
REG_UTP_TRANSFER_REQ_DOOR_BELL ,
mask , ~ mask , 1000 , 1000 ) ;
return err ;
}
/**
* ufshcd_dev_cmd_completion ( ) - handles device management command responses
* @ hba : per adapter instance
* @ lrbp : pointer to local reference block
*/
static int
ufshcd_dev_cmd_completion ( struct ufs_hba * hba , struct ufshcd_lrb * lrbp )
{
int resp ;
int err = 0 ;
resp = ufshcd_get_req_rsp ( lrbp - > ucd_rsp_ptr ) ;
switch ( resp ) {
case UPIU_TRANSACTION_NOP_IN :
if ( hba - > dev_cmd . type ! = DEV_CMD_TYPE_NOP ) {
err = - EINVAL ;
dev_err ( hba - > dev , " %s: unexpected response %x \n " ,
__func__ , resp ) ;
}
break ;
2013-07-29 23:05:58 +04:00
case UPIU_TRANSACTION_QUERY_RSP :
ufshcd_copy_query_response ( hba , lrbp ) ;
break ;
2013-07-29 23:05:57 +04:00
case UPIU_TRANSACTION_REJECT_UPIU :
/* TODO: handle Reject UPIU Response */
err = - EPERM ;
dev_err ( hba - > dev , " %s: Reject UPIU not fully implemented \n " ,
__func__ ) ;
break ;
default :
err = - EINVAL ;
dev_err ( hba - > dev , " %s: Invalid device management cmd response: %x \n " ,
__func__ , resp ) ;
break ;
}
return err ;
}
static int ufshcd_wait_for_dev_cmd ( struct ufs_hba * hba ,
struct ufshcd_lrb * lrbp , int max_timeout )
{
int err = 0 ;
unsigned long time_left ;
unsigned long flags ;
time_left = wait_for_completion_timeout ( hba - > dev_cmd . complete ,
msecs_to_jiffies ( max_timeout ) ) ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
hba - > dev_cmd . complete = NULL ;
if ( likely ( time_left ) ) {
err = ufshcd_get_tr_ocs ( lrbp ) ;
if ( ! err )
err = ufshcd_dev_cmd_completion ( hba , lrbp ) ;
}
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
if ( ! time_left ) {
err = - ETIMEDOUT ;
if ( ! ufshcd_clear_cmd ( hba , lrbp - > task_tag ) )
/* sucessfully cleared the command, retry if needed */
err = - EAGAIN ;
}
return err ;
}
/**
* ufshcd_get_dev_cmd_tag - Get device management command tag
* @ hba : per - adapter instance
* @ tag : pointer to variable with available slot value
*
* Get a free slot and lock it until device management command
* completes .
*
* Returns false if free slot is unavailable for locking , else
* return true with tag value in @ tag .
*/
static bool ufshcd_get_dev_cmd_tag ( struct ufs_hba * hba , int * tag_out )
{
int tag ;
bool ret = false ;
unsigned long tmp ;
if ( ! tag_out )
goto out ;
do {
tmp = ~ hba - > lrb_in_use ;
tag = find_last_bit ( & tmp , hba - > nutrs ) ;
if ( tag > = hba - > nutrs )
goto out ;
} while ( test_and_set_bit_lock ( tag , & hba - > lrb_in_use ) ) ;
* tag_out = tag ;
ret = true ;
out :
return ret ;
}
static inline void ufshcd_put_dev_cmd_tag ( struct ufs_hba * hba , int tag )
{
clear_bit_unlock ( tag , & hba - > lrb_in_use ) ;
}
/**
* ufshcd_exec_dev_cmd - API for sending device management requests
* @ hba - UFS hba
* @ cmd_type - specifies the type ( NOP , Query . . . )
* @ timeout - time in seconds
*
2013-07-29 23:05:58 +04:00
* NOTE : Since there is only one available tag for device management commands ,
* it is expected you hold the hba - > dev_cmd . lock mutex .
2013-07-29 23:05:57 +04:00
*/
static int ufshcd_exec_dev_cmd ( struct ufs_hba * hba ,
enum dev_cmd_type cmd_type , int timeout )
{
struct ufshcd_lrb * lrbp ;
int err ;
int tag ;
struct completion wait ;
unsigned long flags ;
/*
* Get free slot , sleep if slots are unavailable .
* Even though we use wait_event ( ) which sleeps indefinitely ,
* the maximum wait time is bounded by SCSI request timeout .
*/
wait_event ( hba - > dev_cmd . tag_wq , ufshcd_get_dev_cmd_tag ( hba , & tag ) ) ;
init_completion ( & wait ) ;
lrbp = & hba - > lrb [ tag ] ;
WARN_ON ( lrbp - > cmd ) ;
err = ufshcd_compose_dev_cmd ( hba , lrbp , cmd_type , tag ) ;
if ( unlikely ( err ) )
goto out_put_tag ;
hba - > dev_cmd . complete = & wait ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
ufshcd_send_command ( hba , tag ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
err = ufshcd_wait_for_dev_cmd ( hba , lrbp , timeout ) ;
out_put_tag :
ufshcd_put_dev_cmd_tag ( hba , tag ) ;
wake_up ( & hba - > dev_cmd . tag_wq ) ;
return err ;
}
2013-07-29 23:05:58 +04:00
/**
* ufshcd_query_flag ( ) - API function for sending flag query requests
* hba : per - adapter instance
* query_opcode : flag query to perform
* idn : flag idn to access
* flag_res : the flag value after the query request completes
*
* Returns 0 for success , non - zero in case of failure
*/
static int ufshcd_query_flag ( struct ufs_hba * hba , enum query_opcode opcode ,
enum flag_idn idn , bool * flag_res )
{
struct ufs_query_req * request ;
struct ufs_query_res * response ;
int err ;
BUG_ON ( ! hba ) ;
mutex_lock ( & hba - > dev_cmd . lock ) ;
request = & hba - > dev_cmd . query . request ;
response = & hba - > dev_cmd . query . response ;
memset ( request , 0 , sizeof ( struct ufs_query_req ) ) ;
memset ( response , 0 , sizeof ( struct ufs_query_res ) ) ;
switch ( opcode ) {
case UPIU_QUERY_OPCODE_SET_FLAG :
case UPIU_QUERY_OPCODE_CLEAR_FLAG :
case UPIU_QUERY_OPCODE_TOGGLE_FLAG :
request - > query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST ;
break ;
case UPIU_QUERY_OPCODE_READ_FLAG :
request - > query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST ;
if ( ! flag_res ) {
/* No dummy reads */
dev_err ( hba - > dev , " %s: Invalid argument for read request \n " ,
__func__ ) ;
err = - EINVAL ;
goto out_unlock ;
}
break ;
default :
dev_err ( hba - > dev ,
" %s: Expected query flag opcode but got = %d \n " ,
__func__ , opcode ) ;
err = - EINVAL ;
goto out_unlock ;
}
request - > upiu_req . opcode = opcode ;
request - > upiu_req . idn = idn ;
/* Send query request */
err = ufshcd_exec_dev_cmd ( hba , DEV_CMD_TYPE_QUERY ,
QUERY_REQ_TIMEOUT ) ;
if ( err ) {
dev_err ( hba - > dev ,
" %s: Sending flag query for idn %d failed, err = %d \n " ,
__func__ , idn , err ) ;
goto out_unlock ;
}
if ( flag_res )
* flag_res = ( response - > upiu_res . value &
MASK_QUERY_UPIU_FLAG_LOC ) & 0x1 ;
out_unlock :
mutex_unlock ( & hba - > dev_cmd . lock ) ;
return err ;
}
2013-07-29 23:05:59 +04:00
/**
* ufshcd_query_attr - API function for sending attribute requests
* hba : per - adapter instance
* opcode : attribute opcode
* idn : attribute idn to access
* index : index field
* selector : selector field
* attr_val : the attribute value after the query request completes
*
* Returns 0 for success , non - zero in case of failure
*/
int ufshcd_query_attr ( struct ufs_hba * hba , enum query_opcode opcode ,
enum attr_idn idn , u8 index , u8 selector , u32 * attr_val )
{
struct ufs_query_req * request ;
struct ufs_query_res * response ;
int err ;
BUG_ON ( ! hba ) ;
if ( ! attr_val ) {
dev_err ( hba - > dev , " %s: attribute value required for opcode 0x%x \n " ,
__func__ , opcode ) ;
err = - EINVAL ;
goto out ;
}
mutex_lock ( & hba - > dev_cmd . lock ) ;
request = & hba - > dev_cmd . query . request ;
response = & hba - > dev_cmd . query . response ;
memset ( request , 0 , sizeof ( struct ufs_query_req ) ) ;
memset ( response , 0 , sizeof ( struct ufs_query_res ) ) ;
switch ( opcode ) {
case UPIU_QUERY_OPCODE_WRITE_ATTR :
request - > query_func = UPIU_QUERY_FUNC_STANDARD_WRITE_REQUEST ;
request - > upiu_req . value = * attr_val ;
break ;
case UPIU_QUERY_OPCODE_READ_ATTR :
request - > query_func = UPIU_QUERY_FUNC_STANDARD_READ_REQUEST ;
break ;
default :
dev_err ( hba - > dev , " %s: Expected query attr opcode but got = 0x%.2x \n " ,
__func__ , opcode ) ;
err = - EINVAL ;
goto out_unlock ;
}
request - > upiu_req . opcode = opcode ;
request - > upiu_req . idn = idn ;
request - > upiu_req . index = index ;
request - > upiu_req . selector = selector ;
/* Send query request */
err = ufshcd_exec_dev_cmd ( hba , DEV_CMD_TYPE_QUERY ,
QUERY_REQ_TIMEOUT ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: opcode 0x%.2x for idn %d failed, err = %d \n " ,
__func__ , opcode , idn , err ) ;
goto out_unlock ;
}
* attr_val = response - > upiu_res . value ;
out_unlock :
mutex_unlock ( & hba - > dev_cmd . lock ) ;
out :
return err ;
}
2012-02-29 10:41:50 +04:00
/**
* 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. Allocate DMA memory for UTP Transfer Request Descriptor List ( UTRDL ) .
* 3. Allocate DMA memory for UTP Task Management Request Descriptor List
* ( UTMRDL )
* 4. Allocate 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 )
{
size_t utmrdl_size , utrdl_size , ucdl_size ;
/* Allocate memory for UTP command descriptors */
ucdl_size = ( sizeof ( struct utp_transfer_cmd_desc ) * hba - > nutrs ) ;
2013-06-27 08:31:54 +04:00
hba - > ucdl_base_addr = dmam_alloc_coherent ( hba - > dev ,
ucdl_size ,
& hba - > ucdl_dma_addr ,
GFP_KERNEL ) ;
2012-02-29 10:41:50 +04:00
/*
* UFSHCI requires UTP command descriptor to be 128 byte aligned .
* make sure hba - > ucdl_dma_addr is aligned to PAGE_SIZE
* if hba - > ucdl_dma_addr is aligned to PAGE_SIZE , then it will
* be aligned to 128 bytes as well
*/
if ( ! hba - > ucdl_base_addr | |
WARN_ON ( hba - > ucdl_dma_addr & ( PAGE_SIZE - 1 ) ) ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Command Descriptor Memory allocation failed \n " ) ;
goto out ;
}
/*
* Allocate memory for UTP Transfer descriptors
* UFSHCI requires 1024 byte alignment of UTRD
*/
utrdl_size = ( sizeof ( struct utp_transfer_req_desc ) * hba - > nutrs ) ;
2013-06-27 08:31:54 +04:00
hba - > utrdl_base_addr = dmam_alloc_coherent ( hba - > dev ,
utrdl_size ,
& hba - > utrdl_dma_addr ,
GFP_KERNEL ) ;
2012-02-29 10:41:50 +04:00
if ( ! hba - > utrdl_base_addr | |
WARN_ON ( hba - > utrdl_dma_addr & ( PAGE_SIZE - 1 ) ) ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Transfer Descriptor Memory allocation failed \n " ) ;
goto out ;
}
/*
* Allocate memory for UTP Task Management descriptors
* UFSHCI requires 1024 byte alignment of UTMRD
*/
utmrdl_size = sizeof ( struct utp_task_req_desc ) * hba - > nutmrs ;
2013-06-27 08:31:54 +04:00
hba - > utmrdl_base_addr = dmam_alloc_coherent ( hba - > dev ,
utmrdl_size ,
& hba - > utmrdl_dma_addr ,
GFP_KERNEL ) ;
2012-02-29 10:41:50 +04:00
if ( ! hba - > utmrdl_base_addr | |
WARN_ON ( hba - > utmrdl_dma_addr & ( PAGE_SIZE - 1 ) ) ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Task Management Descriptor Memory allocation failed \n " ) ;
goto out ;
}
/* Allocate memory for local reference block */
2013-06-27 08:31:54 +04:00
hba - > lrb = devm_kzalloc ( hba - > dev ,
hba - > nutrs * sizeof ( struct ufshcd_lrb ) ,
GFP_KERNEL ) ;
2012-02-29 10:41:50 +04:00
if ( ! hba - > lrb ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " LRB Memory allocation failed \n " ) ;
2012-02-29 10:41:50 +04:00
goto out ;
}
return 0 ;
out :
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 = hba - > utrdl_base_addr ;
cmd_descp = hba - > ucdl_base_addr ;
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 ;
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 ( lower_32_bits ( cmd_desc_element_addr ) ) ;
utrdlp [ i ] . command_desc_base_addr_hi =
cpu_to_le32 ( upper_32_bits ( cmd_desc_element_addr ) ) ;
/* Response upiu and prdt offset should be in double words */
utrdlp [ i ] . response_upiu_offset =
cpu_to_le16 ( ( response_offset > > 2 ) ) ;
utrdlp [ i ] . prd_table_offset =
cpu_to_le16 ( ( prdt_offset > > 2 ) ) ;
utrdlp [ i ] . response_upiu_length =
2013-06-26 21:09:30 +04:00
cpu_to_le16 ( ALIGNED_UPIU_SIZE > > 2 ) ;
2012-02-29 10:41:50 +04:00
hba - > lrb [ i ] . utr_descriptor_ptr = ( utrdlp + i ) ;
2013-07-29 23:05:57 +04:00
hba - > lrb [ i ] . ucd_req_ptr =
( struct utp_upiu_req * ) ( cmd_descp + i ) ;
2012-02-29 10:41:50 +04:00
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 initialize 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 )
{
2013-06-26 21:09:29 +04:00
struct uic_command uic_cmd = { 0 } ;
int ret ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:29 +04:00
uic_cmd . command = UIC_CMD_DME_LINK_STARTUP ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:29 +04:00
ret = ufshcd_send_uic_cmd ( hba , & uic_cmd ) ;
if ( ret )
dev_err ( hba - > dev ,
" dme-link-startup: error code %d \n " , ret ) ;
return ret ;
2012-02-29 10:41:50 +04:00
}
2013-07-29 23:05:58 +04:00
/**
* ufshcd_complete_dev_init ( ) - checks device readiness
* hba : per - adapter instance
*
* Set fDeviceInit flag and poll until device toggles it .
*/
static int ufshcd_complete_dev_init ( struct ufs_hba * hba )
{
int i , retries , err = 0 ;
bool flag_res = 1 ;
for ( retries = QUERY_REQ_RETRIES ; retries > 0 ; retries - - ) {
/* Set the fDeviceInit flag */
err = ufshcd_query_flag ( hba , UPIU_QUERY_OPCODE_SET_FLAG ,
QUERY_FLAG_IDN_FDEVICEINIT , NULL ) ;
if ( ! err | | err = = - ETIMEDOUT )
break ;
dev_dbg ( hba - > dev , " %s: error %d retrying \n " , __func__ , err ) ;
}
if ( err ) {
dev_err ( hba - > dev ,
" %s setting fDeviceInit flag failed with error %d \n " ,
__func__ , err ) ;
goto out ;
}
/* poll for max. 100 iterations for fDeviceInit flag to clear */
for ( i = 0 ; i < 100 & & ! err & & flag_res ; i + + ) {
for ( retries = QUERY_REQ_RETRIES ; retries > 0 ; retries - - ) {
err = ufshcd_query_flag ( hba ,
UPIU_QUERY_OPCODE_READ_FLAG ,
QUERY_FLAG_IDN_FDEVICEINIT , & flag_res ) ;
if ( ! err | | err = = - ETIMEDOUT )
break ;
dev_dbg ( hba - > dev , " %s: error %d retrying \n " , __func__ ,
err ) ;
}
}
if ( err )
dev_err ( hba - > dev ,
" %s reading fDeviceInit flag failed with error %d \n " ,
__func__ , err ) ;
else if ( flag_res )
dev_err ( hba - > dev ,
" %s fDeviceInit was not cleared by the device \n " ,
__func__ ) ;
out :
return err ;
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_make_hba_operational - Make UFS controller operational
* @ hba : per adapter instance
*
* To bring UFS host controller to operational state ,
* 1. Check if device is present
2013-06-26 21:09:29 +04:00
* 2. Enable required interrupts
* 3. Configure interrupt aggregation
* 4. Program UTRL and UTMRL base addres
* 5. Configure run - stop - registers
2012-02-29 10:41:50 +04:00
*
* Returns 0 on success , non - zero value on failure
*/
static int ufshcd_make_hba_operational ( struct ufs_hba * hba )
{
int err = 0 ;
u32 reg ;
/* check if device present */
2013-06-26 21:09:26 +04:00
reg = ufshcd_readl ( hba , REG_CONTROLLER_STATUS ) ;
2012-07-10 18:09:23 +04:00
if ( ! ufshcd_is_device_present ( reg ) ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " cc: Device not present \n " ) ;
2012-02-29 10:41:50 +04:00
err = - ENXIO ;
goto out ;
}
2013-06-26 21:09:29 +04:00
/* Enable required interrupts */
ufshcd_enable_intr ( hba , UFSHCD_ENABLE_INTRS ) ;
/* Configure interrupt aggregation */
ufshcd_config_int_aggr ( hba , INT_AGGR_CONFIG ) ;
/* Configure UTRL and UTMRL base address registers */
ufshcd_writel ( hba , lower_32_bits ( hba - > utrdl_dma_addr ) ,
REG_UTP_TRANSFER_REQ_LIST_BASE_L ) ;
ufshcd_writel ( hba , upper_32_bits ( hba - > utrdl_dma_addr ) ,
REG_UTP_TRANSFER_REQ_LIST_BASE_H ) ;
ufshcd_writel ( hba , lower_32_bits ( hba - > utmrdl_dma_addr ) ,
REG_UTP_TASK_REQ_LIST_BASE_L ) ;
ufshcd_writel ( hba , upper_32_bits ( hba - > utmrdl_dma_addr ) ,
REG_UTP_TASK_REQ_LIST_BASE_H ) ;
2012-02-29 10:41:50 +04:00
/*
* UCRDY , UTMRLDY and UTRLRDY bits must be 1
* DEI , HEI bits must be 0
*/
if ( ! ( ufshcd_get_lists_status ( reg ) ) ) {
ufshcd_enable_run_stop_reg ( hba ) ;
} else {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Host controller not ready to process requests " ) ;
err = - EIO ;
goto out ;
}
if ( hba - > ufshcd_state = = UFSHCD_STATE_RESET )
scsi_unblock_requests ( hba - > host ) ;
hba - > ufshcd_state = UFSHCD_STATE_OPERATIONAL ;
2013-06-26 21:09:29 +04:00
2012-02-29 10:41:50 +04:00
out :
return err ;
}
/**
* ufshcd_hba_enable - initialize the controller
* @ hba : per adapter instance
*
* The controller resets itself and controller firmware initialization
* sequence kicks off . When controller is ready it will set
* the Host Controller Enable bit to 1.
*
* Returns 0 on success , non - zero value on failure
*/
static int ufshcd_hba_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 .
*/
if ( ! ufshcd_is_hba_active ( hba ) ) {
/* change controller state to "reset state" */
ufshcd_hba_stop ( hba ) ;
/*
* This delay is based on the testing done with UFS host
* controller FPGA . The delay can be changed based on the
* host controller used .
*/
msleep ( 5 ) ;
}
/* start controller initialization sequence */
ufshcd_hba_start ( hba ) ;
/*
* To initialize a UFS host controller HCE bit must be set to 1.
* During initialization the HCE bit value changes from 1 - > 0 - > 1.
* When the host controller completes initialization sequence
* it sets the value of HCE bit to 1. The same HCE bit is read back
* to check if the controller has completed initialization sequence .
* So without this delay the value HCE = 1 , set in the previous
* instruction might be read back .
* This delay can be changed based on the controller .
*/
msleep ( 1 ) ;
/* wait for the host controller to complete initialization */
retry = 10 ;
while ( ufshcd_is_hba_active ( hba ) ) {
if ( retry ) {
retry - - ;
} else {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Controller enable failed \n " ) ;
return - EIO ;
}
msleep ( 5 ) ;
}
return 0 ;
}
/**
2013-06-26 21:09:29 +04:00
* ufshcd_link_startup - Initialize unipro link startup
2012-02-29 10:41:50 +04:00
* @ hba : per adapter instance
*
2013-06-26 21:09:29 +04:00
* Returns 0 for success , non - zero in case of failure
2012-02-29 10:41:50 +04:00
*/
2013-06-26 21:09:29 +04:00
static int ufshcd_link_startup ( struct ufs_hba * hba )
2012-02-29 10:41:50 +04:00
{
2013-06-26 21:09:29 +04:00
int ret ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:29 +04:00
/* enable UIC related interrupts */
ufshcd_enable_intr ( hba , UIC_COMMAND_COMPL ) ;
ret = ufshcd_dme_link_startup ( hba ) ;
if ( ret )
goto out ;
ret = ufshcd_make_hba_operational ( hba ) ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:29 +04:00
out :
if ( ret )
dev_err ( hba - > dev , " link startup failed %d \n " , ret ) ;
return ret ;
2012-02-29 10:41:50 +04:00
}
2013-07-29 23:05:57 +04:00
/**
* ufshcd_verify_dev_init ( ) - Verify device initialization
* @ hba : per - adapter instance
*
* Send NOP OUT UPIU and wait for NOP IN response to check whether the
* device Transport Protocol ( UTP ) layer is ready after a reset .
* If the UTP layer at the device side is not initialized , it may
* not respond with NOP IN UPIU within timeout of % NOP_OUT_TIMEOUT
* and we retry sending NOP OUT for % NOP_OUT_RETRIES iterations .
*/
static int ufshcd_verify_dev_init ( struct ufs_hba * hba )
{
int err = 0 ;
int retries ;
mutex_lock ( & hba - > dev_cmd . lock ) ;
for ( retries = NOP_OUT_RETRIES ; retries > 0 ; retries - - ) {
err = ufshcd_exec_dev_cmd ( hba , DEV_CMD_TYPE_NOP ,
NOP_OUT_TIMEOUT ) ;
if ( ! err | | err = = - ETIMEDOUT )
break ;
dev_dbg ( hba - > dev , " %s: error %d retrying \n " , __func__ , err ) ;
}
mutex_unlock ( & hba - > dev_cmd . lock ) ;
if ( err )
dev_err ( hba - > dev , " %s: NOP OUT failed %d \n " , __func__ , err ) ;
return err ;
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_do_reset - reset the host controller
* @ hba : per adapter instance
*
* Returns SUCCESS / FAILED
*/
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 */
ufshcd_hba_stop ( hba ) ;
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
/* abort outstanding commands */
for ( tag = 0 ; tag < hba - > nutrs ; tag + + ) {
if ( test_bit ( tag , & hba - > outstanding_reqs ) ) {
lrbp = & hba - > lrb [ tag ] ;
2013-07-29 23:05:57 +04:00
if ( lrbp - > cmd ) {
scsi_dma_unmap ( lrbp - > cmd ) ;
lrbp - > cmd - > result = DID_RESET < < 16 ;
lrbp - > cmd - > scsi_done ( lrbp - > cmd ) ;
lrbp - > cmd = NULL ;
clear_bit_unlock ( tag , & hba - > lrb_in_use ) ;
}
2012-02-29 10:41:50 +04:00
}
}
2013-07-29 23:05:57 +04:00
/* complete device management command */
if ( hba - > dev_cmd . complete )
complete ( hba - > dev_cmd . complete ) ;
2012-02-29 10:41:50 +04:00
/* clear outstanding request/task bit maps */
hba - > outstanding_reqs = 0 ;
hba - > outstanding_tasks = 0 ;
2013-06-26 21:09:29 +04:00
/* Host controller enable */
if ( ufshcd_hba_enable ( hba ) ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Reset: Controller initialization failed \n " ) ;
return FAILED ;
}
2013-06-26 21:09:29 +04:00
if ( ufshcd_link_startup ( hba ) ) {
dev_err ( hba - > dev ,
" Reset: Link start-up failed \n " ) ;
return FAILED ;
}
2012-02-29 10:41:50 +04:00
return SUCCESS ;
}
/**
* ufshcd_slave_alloc - handle initial SCSI device configurations
* @ sdev : pointer to SCSI device
*
* Returns success
*/
static int ufshcd_slave_alloc ( struct scsi_device * sdev )
{
struct ufs_hba * hba ;
hba = shost_priv ( sdev - > host ) ;
sdev - > tagged_supported = 1 ;
/* Mode sense(6) is not supported by UFS, so use Mode sense(10) */
sdev - > use_10_for_ms = 1 ;
scsi_set_tag_type ( sdev , MSG_SIMPLE_TAG ) ;
/*
* Inform SCSI Midlayer that the LUN queue depth is same as the
* controller queue depth . If a LUN queue depth is less than the
* controller queue depth and if the LUN reports
* SAM_STAT_TASK_SET_FULL , the LUN queue depth will be adjusted
* with scsi_adjust_queue_depth .
*/
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 = shost_priv ( sdev - > host ) ;
scsi_deactivate_tcq ( sdev , hba - > nutrs ) ;
}
/**
* ufshcd_task_req_compl - handle task management request completion
* @ hba : per adapter instance
* @ index : index of the completed request
*
* Returns SUCCESS / FAILED
*/
static int ufshcd_task_req_compl ( struct ufs_hba * hba , u32 index )
{
struct utp_task_req_desc * task_req_descp ;
struct utp_upiu_task_rsp * task_rsp_upiup ;
unsigned long flags ;
int ocs_value ;
int task_result ;
spin_lock_irqsave ( hba - > host - > host_lock , flags ) ;
/* Clear completed tasks from outstanding_tasks */
__clear_bit ( index , & hba - > outstanding_tasks ) ;
task_req_descp = hba - > utmrdl_base_addr ;
ocs_value = ufshcd_get_tmr_ocs ( & task_req_descp [ index ] ) ;
if ( ocs_value = = OCS_SUCCESS ) {
task_rsp_upiup = ( struct utp_upiu_task_rsp * )
task_req_descp [ index ] . task_rsp_upiu ;
task_result = be32_to_cpu ( task_rsp_upiup - > header . dword_1 ) ;
task_result = ( ( task_result & MASK_TASK_RESPONSE ) > > 8 ) ;
2012-04-19 10:16:22 +04:00
if ( task_result ! = UPIU_TASK_MANAGEMENT_FUNC_COMPL & &
2012-02-29 10:41:50 +04:00
task_result ! = UPIU_TASK_MANAGEMENT_FUNC_SUCCEEDED )
task_result = FAILED ;
2012-07-10 19:11:54 +04:00
else
task_result = SUCCESS ;
2012-02-29 10:41:50 +04:00
} else {
task_result = FAILED ;
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" trc: Invalid ocs = %x \n " , ocs_value ) ;
}
spin_unlock_irqrestore ( hba - > host - > host_lock , flags ) ;
return task_result ;
}
/**
* ufshcd_adjust_lun_qdepth - Update LUN queue depth if device responds with
* SAM_STAT_TASK_SET_FULL SCSI command status .
* @ cmd : pointer to SCSI command
*/
static void ufshcd_adjust_lun_qdepth ( struct scsi_cmnd * cmd )
{
struct ufs_hba * hba ;
int i ;
int lun_qdepth = 0 ;
hba = shost_priv ( cmd - > device - > host ) ;
/*
* LUN queue depth can be obtained by counting outstanding commands
* on the LUN .
*/
for ( i = 0 ; i < hba - > nutrs ; i + + ) {
if ( test_bit ( i , & hba - > outstanding_reqs ) ) {
/*
* Check if the outstanding command belongs
* to the LUN which reported SAM_STAT_TASK_SET_FULL .
*/
if ( cmd - > device - > lun = = hba - > lrb [ i ] . lun )
lun_qdepth + + ;
}
}
/*
* LUN queue depth will be total outstanding commands , except the
* command for which the LUN reported SAM_STAT_TASK_SET_FULL .
*/
scsi_adjust_queue_depth ( cmd - > device , MSG_SIMPLE_TAG , lun_qdepth - 1 ) ;
}
/**
* 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 | = DID_OK < < 16 |
COMMAND_COMPLETE < < 8 |
SAM_STAT_CHECK_CONDITION ;
ufshcd_copy_sense_data ( lrbp ) ;
break ;
case SAM_STAT_BUSY :
result | = SAM_STAT_BUSY ;
break ;
case SAM_STAT_TASK_SET_FULL :
/*
* If a LUN reports SAM_STAT_TASK_SET_FULL , then the LUN queue
* depth needs to be adjusted to the exact number of
* outstanding commands the LUN can handle at any given time .
*/
ufshcd_adjust_lun_qdepth ( lrbp - > cmd ) ;
result | = SAM_STAT_TASK_SET_FULL ;
break ;
case SAM_STAT_TASK_ABORTED :
result | = SAM_STAT_TASK_ABORTED ;
break ;
default :
result | = DID_ERROR < < 16 ;
break ;
} /* end of switch */
return result ;
}
/**
* ufshcd_transfer_rsp_status - Get overall status of the response
* @ hba : per adapter instance
* @ 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 ufs_hba * hba , 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 :
2013-07-29 23:05:57 +04:00
result = ufshcd_get_req_rsp ( lrbp - > ucd_rsp_ptr ) ;
2012-02-29 10:41:50 +04:00
2013-07-29 23:05:57 +04:00
switch ( result ) {
case UPIU_TRANSACTION_RESPONSE :
/*
* 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 ) ;
2013-07-29 23:05:59 +04:00
if ( ufshcd_is_exception_event ( lrbp - > ucd_rsp_ptr ) )
schedule_work ( & hba - > eeh_work ) ;
2013-07-29 23:05:57 +04:00
break ;
case UPIU_TRANSACTION_REJECT_UPIU :
/* TODO: handle Reject UPIU Response */
result = DID_ERROR < < 16 ;
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2013-07-29 23:05:57 +04:00
" Reject UPIU not fully implemented \n " ) ;
break ;
default :
result = DID_ERROR < < 16 ;
dev_err ( hba - > dev ,
" Unexpected request response code = %x \n " ,
result ) ;
2012-02-29 10:41:50 +04:00
break ;
}
break ;
case OCS_ABORTED :
result | = DID_ABORT < < 16 ;
break ;
case OCS_INVALID_CMD_TABLE_ATTR :
case OCS_INVALID_PRDT_ATTR :
case OCS_MISMATCH_DATA_BUF_SIZE :
case OCS_MISMATCH_RESP_UPIU_SIZE :
case OCS_PEER_COMM_FAILURE :
case OCS_FATAL_ERROR :
default :
result | = DID_ERROR < < 16 ;
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" OCS error from controller = %x \n " , ocs ) ;
break ;
} /* end of switch */
return result ;
}
2013-06-26 21:09:29 +04:00
/**
* ufshcd_uic_cmd_compl - handle completion of uic command
* @ hba : per adapter instance
*/
static void ufshcd_uic_cmd_compl ( struct ufs_hba * hba )
{
if ( hba - > active_uic_cmd ) {
hba - > active_uic_cmd - > argument2 | =
ufshcd_get_uic_cmd_result ( hba ) ;
complete ( & hba - > active_uic_cmd - > done ) ;
}
}
2012-02-29 10:41:50 +04:00
/**
* ufshcd_transfer_req_compl - handle SCSI and query command completion
* @ hba : per adapter instance
*/
static void ufshcd_transfer_req_compl ( struct ufs_hba * hba )
{
2013-07-29 23:05:57 +04:00
struct ufshcd_lrb * lrbp ;
struct scsi_cmnd * cmd ;
2012-02-29 10:41:50 +04:00
unsigned long completed_reqs ;
u32 tr_doorbell ;
int result ;
int index ;
2013-07-29 23:05:57 +04:00
bool int_aggr_reset = false ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:26 +04:00
tr_doorbell = ufshcd_readl ( hba , REG_UTP_TRANSFER_REQ_DOOR_BELL ) ;
2012-02-29 10:41:50 +04:00
completed_reqs = tr_doorbell ^ hba - > outstanding_reqs ;
for ( index = 0 ; index < hba - > nutrs ; index + + ) {
if ( test_bit ( index , & completed_reqs ) ) {
2013-07-29 23:05:57 +04:00
lrbp = & hba - > lrb [ index ] ;
cmd = lrbp - > cmd ;
/*
* Don ' t skip resetting interrupt aggregation counters
* if a regular command is present .
*/
int_aggr_reset | = ! lrbp - > intr_cmd ;
2012-02-29 10:41:50 +04:00
2013-07-29 23:05:57 +04:00
if ( cmd ) {
result = ufshcd_transfer_rsp_status ( hba , lrbp ) ;
scsi_dma_unmap ( cmd ) ;
cmd - > result = result ;
2012-02-29 10:41:50 +04:00
/* Mark completed command as NULL in LRB */
2013-07-29 23:05:57 +04:00
lrbp - > cmd = NULL ;
clear_bit_unlock ( index , & hba - > lrb_in_use ) ;
/* Do not touch lrbp after scsi done */
cmd - > scsi_done ( cmd ) ;
} else if ( lrbp - > command_type = =
UTP_CMD_TYPE_DEV_MANAGE ) {
if ( hba - > dev_cmd . complete )
complete ( hba - > dev_cmd . complete ) ;
2012-02-29 10:41:50 +04:00
}
} /* end of if */
} /* end of for */
/* clear corresponding bits of completed commands */
hba - > outstanding_reqs ^ = completed_reqs ;
2013-07-29 23:05:57 +04:00
/* we might have free'd some tags above */
wake_up ( & hba - > dev_cmd . tag_wq ) ;
2012-02-29 10:41:50 +04:00
/* Reset interrupt aggregation counters */
2013-07-29 23:05:57 +04:00
if ( int_aggr_reset )
ufshcd_config_int_aggr ( hba , INT_AGGR_RESET ) ;
2012-02-29 10:41:50 +04:00
}
2013-07-29 23:05:59 +04:00
/**
* ufshcd_disable_ee - disable exception event
* @ hba : per - adapter instance
* @ mask : exception event to disable
*
* Disables exception event in the device so that the EVENT_ALERT
* bit is not set .
*
* Returns zero on success , non - zero error value on failure .
*/
static int ufshcd_disable_ee ( struct ufs_hba * hba , u16 mask )
{
int err = 0 ;
u32 val ;
if ( ! ( hba - > ee_ctrl_mask & mask ) )
goto out ;
val = hba - > ee_ctrl_mask & ~ mask ;
val & = 0xFFFF ; /* 2 bytes */
err = ufshcd_query_attr ( hba , UPIU_QUERY_OPCODE_WRITE_ATTR ,
QUERY_ATTR_IDN_EE_CONTROL , 0 , 0 , & val ) ;
if ( ! err )
hba - > ee_ctrl_mask & = ~ mask ;
out :
return err ;
}
/**
* ufshcd_enable_ee - enable exception event
* @ hba : per - adapter instance
* @ mask : exception event to enable
*
* Enable corresponding exception event in the device to allow
* device to alert host in critical scenarios .
*
* Returns zero on success , non - zero error value on failure .
*/
static int ufshcd_enable_ee ( struct ufs_hba * hba , u16 mask )
{
int err = 0 ;
u32 val ;
if ( hba - > ee_ctrl_mask & mask )
goto out ;
val = hba - > ee_ctrl_mask | mask ;
val & = 0xFFFF ; /* 2 bytes */
err = ufshcd_query_attr ( hba , UPIU_QUERY_OPCODE_WRITE_ATTR ,
QUERY_ATTR_IDN_EE_CONTROL , 0 , 0 , & val ) ;
if ( ! err )
hba - > ee_ctrl_mask | = mask ;
out :
return err ;
}
/**
* ufshcd_enable_auto_bkops - Allow device managed BKOPS
* @ hba : per - adapter instance
*
* Allow device to manage background operations on its own . Enabling
* this might lead to inconsistent latencies during normal data transfers
* as the device is allowed to manage its own way of handling background
* operations .
*
* Returns zero on success , non - zero on failure .
*/
static int ufshcd_enable_auto_bkops ( struct ufs_hba * hba )
{
int err = 0 ;
if ( hba - > auto_bkops_enabled )
goto out ;
err = ufshcd_query_flag ( hba , UPIU_QUERY_OPCODE_SET_FLAG ,
QUERY_FLAG_IDN_BKOPS_EN , NULL ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: failed to enable bkops %d \n " ,
__func__ , err ) ;
goto out ;
}
hba - > auto_bkops_enabled = true ;
/* No need of URGENT_BKOPS exception from the device */
err = ufshcd_disable_ee ( hba , MASK_EE_URGENT_BKOPS ) ;
if ( err )
dev_err ( hba - > dev , " %s: failed to disable exception event %d \n " ,
__func__ , err ) ;
out :
return err ;
}
/**
* ufshcd_disable_auto_bkops - block device in doing background operations
* @ hba : per - adapter instance
*
* Disabling background operations improves command response latency but
* has drawback of device moving into critical state where the device is
* not - operable . Make sure to call ufshcd_enable_auto_bkops ( ) whenever the
* host is idle so that BKOPS are managed effectively without any negative
* impacts .
*
* Returns zero on success , non - zero on failure .
*/
static int ufshcd_disable_auto_bkops ( struct ufs_hba * hba )
{
int err = 0 ;
if ( ! hba - > auto_bkops_enabled )
goto out ;
/*
* If host assisted BKOPs is to be enabled , make sure
* urgent bkops exception is allowed .
*/
err = ufshcd_enable_ee ( hba , MASK_EE_URGENT_BKOPS ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: failed to enable exception event %d \n " ,
__func__ , err ) ;
goto out ;
}
err = ufshcd_query_flag ( hba , UPIU_QUERY_OPCODE_CLEAR_FLAG ,
QUERY_FLAG_IDN_BKOPS_EN , NULL ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: failed to disable bkops %d \n " ,
__func__ , err ) ;
ufshcd_disable_ee ( hba , MASK_EE_URGENT_BKOPS ) ;
goto out ;
}
hba - > auto_bkops_enabled = false ;
out :
return err ;
}
/**
* ufshcd_force_reset_auto_bkops - force enable of auto bkops
* @ hba : per adapter instance
*
* After a device reset the device may toggle the BKOPS_EN flag
* to default value . The s / w tracking variables should be updated
* as well . Do this by forcing enable of auto bkops .
*/
static void ufshcd_force_reset_auto_bkops ( struct ufs_hba * hba )
{
hba - > auto_bkops_enabled = false ;
hba - > ee_ctrl_mask | = MASK_EE_URGENT_BKOPS ;
ufshcd_enable_auto_bkops ( hba ) ;
}
static inline int ufshcd_get_bkops_status ( struct ufs_hba * hba , u32 * status )
{
return ufshcd_query_attr ( hba , UPIU_QUERY_OPCODE_READ_ATTR ,
QUERY_ATTR_IDN_BKOPS_STATUS , 0 , 0 , status ) ;
}
/**
* ufshcd_urgent_bkops - handle urgent bkops exception event
* @ hba : per - adapter instance
*
* Enable fBackgroundOpsEn flag in the device to permit background
* operations .
*/
static int ufshcd_urgent_bkops ( struct ufs_hba * hba )
{
int err ;
u32 status = 0 ;
err = ufshcd_get_bkops_status ( hba , & status ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: failed to get BKOPS status %d \n " ,
__func__ , err ) ;
goto out ;
}
status = status & 0xF ;
/* handle only if status indicates performance impact or critical */
if ( status > = BKOPS_STATUS_PERF_IMPACT )
err = ufshcd_enable_auto_bkops ( hba ) ;
out :
return err ;
}
static inline int ufshcd_get_ee_status ( struct ufs_hba * hba , u32 * status )
{
return ufshcd_query_attr ( hba , UPIU_QUERY_OPCODE_READ_ATTR ,
QUERY_ATTR_IDN_EE_STATUS , 0 , 0 , status ) ;
}
/**
* ufshcd_exception_event_handler - handle exceptions raised by device
* @ work : pointer to work data
*
* Read bExceptionEventStatus attribute from the device and handle the
* exception event accordingly .
*/
static void ufshcd_exception_event_handler ( struct work_struct * work )
{
struct ufs_hba * hba ;
int err ;
u32 status = 0 ;
hba = container_of ( work , struct ufs_hba , eeh_work ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_get_sync ( hba - > dev ) ;
2013-07-29 23:05:59 +04:00
err = ufshcd_get_ee_status ( hba , & status ) ;
if ( err ) {
dev_err ( hba - > dev , " %s: failed to get exception status %d \n " ,
__func__ , err ) ;
goto out ;
}
status & = hba - > ee_ctrl_mask ;
if ( status & MASK_EE_URGENT_BKOPS ) {
err = ufshcd_urgent_bkops ( hba ) ;
if ( err )
dev_err ( hba - > dev , " %s: failed to handle urgent bkops %d \n " ,
__func__ , err ) ;
}
out :
2013-07-29 23:06:00 +04:00
pm_runtime_put_sync ( hba - > dev ) ;
2013-07-29 23:05:59 +04:00
return ;
}
2012-02-29 10:41:50 +04:00
/**
* 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 ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_get_sync ( hba - > dev ) ;
2012-02-29 10:41:50 +04:00
/* check if reset is already in progress */
if ( hba - > ufshcd_state ! = UFSHCD_STATE_RESET )
ufshcd_do_reset ( hba ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_put_sync ( hba - > dev ) ;
2012-02-29 10:41:50 +04:00
}
/**
* 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 ) {
2013-06-26 21:09:33 +04:00
reg = ufshcd_readl ( hba , REG_UIC_ERROR_CODE_DATA_LINK_LAYER ) ;
2012-02-29 10:41:50 +04:00
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_tmc_handler - handle task management function completion
* @ hba : per adapter instance
*/
static void ufshcd_tmc_handler ( struct ufs_hba * hba )
{
u32 tm_doorbell ;
2013-06-26 21:09:26 +04:00
tm_doorbell = ufshcd_readl ( hba , REG_UTP_TASK_REQ_DOOR_BELL ) ;
2012-02-29 10:41:50 +04:00
hba - > tm_condition = tm_doorbell ^ hba - > outstanding_tasks ;
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
*/
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 )
2013-06-26 21:09:29 +04:00
ufshcd_uic_cmd_compl ( hba ) ;
2012-02-29 10:41:50 +04:00
if ( intr_status & UTP_TASK_REQ_COMPL )
ufshcd_tmc_handler ( hba ) ;
if ( intr_status & UTP_TRANSFER_REQ_COMPL )
ufshcd_transfer_req_compl ( hba ) ;
}
/**
* 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 )
{
u32 intr_status ;
irqreturn_t retval = IRQ_NONE ;
struct ufs_hba * hba = __hba ;
spin_lock ( hba - > host - > host_lock ) ;
2013-06-26 21:09:26 +04:00
intr_status = ufshcd_readl ( hba , REG_INTERRUPT_STATUS ) ;
2012-02-29 10:41:50 +04:00
if ( intr_status ) {
2013-06-26 21:09:28 +04:00
ufshcd_writel ( hba , intr_status , REG_INTERRUPT_STATUS ) ;
2012-02-29 10:41:50 +04:00
ufshcd_sl_intr ( hba , intr_status ) ;
retval = IRQ_HANDLED ;
}
spin_unlock ( hba - > host - > host_lock ) ;
return retval ;
}
/**
* ufshcd_issue_tm_cmd - issues task management commands to controller
* @ hba : per adapter instance
* @ lrbp : pointer to local reference block
*
* Returns SUCCESS / FAILED
*/
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 free_slot = 0 ;
int err ;
host = hba - > host ;
spin_lock_irqsave ( host - > host_lock , flags ) ;
/* If task management queue is full */
free_slot = ufshcd_get_tm_free_slot ( hba ) ;
if ( free_slot > = hba - > nutmrs ) {
spin_unlock_irqrestore ( host - > host_lock , flags ) ;
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " Task management queue full \n " ) ;
2012-02-29 10:41:50 +04:00
err = FAILED ;
goto out ;
}
task_req_descp = hba - > utmrdl_base_addr ;
task_req_descp + = free_slot ;
/* Configure task request descriptor */
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 =
2013-07-29 23:05:57 +04:00
UPIU_HEADER_DWORD ( UPIU_TRANSACTION_TASK_REQ , 0 ,
lrbp - > lun , lrbp - > task_tag ) ;
2012-02-29 10:41:50 +04:00
task_req_upiup - > header . dword_1 =
2013-07-29 23:05:57 +04:00
UPIU_HEADER_DWORD ( 0 , tm_function , 0 , 0 ) ;
2012-02-29 10:41:50 +04:00
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 */
__set_bit ( free_slot , & hba - > outstanding_tasks ) ;
2013-06-26 21:09:26 +04:00
ufshcd_writel ( hba , 1 < < free_slot , REG_UTP_TASK_REQ_DOOR_BELL ) ;
2012-02-29 10:41:50 +04:00
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 ,
( test_bit ( free_slot ,
& hba - > tm_condition ) ! = 0 ) ,
60 * HZ ) ;
if ( ! err ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev ,
2012-02-29 10:41:50 +04:00
" Task management command timed-out \n " ) ;
err = FAILED ;
goto out ;
}
clear_bit ( free_slot , & hba - > tm_condition ) ;
2012-07-10 19:11:54 +04:00
err = ufshcd_task_req_compl ( hba , free_slot ) ;
2012-02-29 10:41:50 +04:00
out :
return err ;
}
/**
* ufshcd_device_reset - reset device and abort all the pending commands
* @ cmd : SCSI command pointer
*
* Returns SUCCESS / FAILED
*/
static int ufshcd_device_reset ( struct scsi_cmnd * cmd )
{
struct Scsi_Host * host ;
struct ufs_hba * hba ;
unsigned int tag ;
u32 pos ;
int err ;
host = cmd - > device - > host ;
hba = shost_priv ( host ) ;
tag = cmd - > request - > tag ;
err = ufshcd_issue_tm_cmd ( hba , & hba - > lrb [ tag ] , UFS_LOGICAL_RESET ) ;
2012-07-10 19:11:54 +04:00
if ( err = = FAILED )
2012-02-29 10:41:50 +04:00
goto out ;
for ( pos = 0 ; pos < hba - > nutrs ; pos + + ) {
if ( test_bit ( pos , & hba - > outstanding_reqs ) & &
( hba - > lrb [ tag ] . lun = = hba - > lrb [ pos ] . lun ) ) {
/* clear the respective UTRLCLR register bit */
ufshcd_utrl_clear ( hba , pos ) ;
clear_bit ( pos , & hba - > outstanding_reqs ) ;
if ( hba - > lrb [ pos ] . cmd ) {
scsi_dma_unmap ( hba - > lrb [ pos ] . cmd ) ;
hba - > lrb [ pos ] . cmd - > result =
2013-07-29 23:05:57 +04:00
DID_ABORT < < 16 ;
2012-02-29 10:41:50 +04:00
hba - > lrb [ pos ] . cmd - > scsi_done ( cmd ) ;
hba - > lrb [ pos ] . cmd = NULL ;
2013-07-29 23:05:57 +04:00
clear_bit_unlock ( pos , & hba - > lrb_in_use ) ;
wake_up ( & hba - > dev_cmd . tag_wq ) ;
2012-02-29 10:41:50 +04:00
}
}
} /* end of for */
out :
return err ;
}
/**
* ufshcd_host_reset - Main reset function registered with scsi layer
* @ cmd : SCSI command pointer
*
* Returns SUCCESS / FAILED
*/
static int ufshcd_host_reset ( struct scsi_cmnd * cmd )
{
struct ufs_hba * hba ;
hba = shost_priv ( cmd - > device - > host ) ;
if ( hba - > ufshcd_state = = UFSHCD_STATE_RESET )
return SUCCESS ;
2012-07-10 19:11:54 +04:00
return ufshcd_do_reset ( hba ) ;
2012-02-29 10:41:50 +04:00
}
/**
* ufshcd_abort - abort a specific command
* @ cmd : SCSI command pointer
*
* Returns SUCCESS / FAILED
*/
static int ufshcd_abort ( struct scsi_cmnd * cmd )
{
struct Scsi_Host * host ;
struct ufs_hba * hba ;
unsigned long flags ;
unsigned int tag ;
int err ;
host = cmd - > device - > host ;
hba = shost_priv ( host ) ;
tag = cmd - > request - > tag ;
spin_lock_irqsave ( host - > host_lock , flags ) ;
/* check if command is still pending */
if ( ! ( test_bit ( tag , & hba - > outstanding_reqs ) ) ) {
err = FAILED ;
spin_unlock_irqrestore ( host - > host_lock , flags ) ;
goto out ;
}
spin_unlock_irqrestore ( host - > host_lock , flags ) ;
err = ufshcd_issue_tm_cmd ( hba , & hba - > lrb [ tag ] , UFS_ABORT_TASK ) ;
2012-07-10 19:11:54 +04:00
if ( err = = FAILED )
2012-02-29 10:41:50 +04:00
goto out ;
scsi_dma_unmap ( cmd ) ;
spin_lock_irqsave ( host - > host_lock , flags ) ;
/* clear the respective UTRLCLR register bit */
ufshcd_utrl_clear ( hba , tag ) ;
__clear_bit ( tag , & hba - > outstanding_reqs ) ;
hba - > lrb [ tag ] . cmd = NULL ;
spin_unlock_irqrestore ( host - > host_lock , flags ) ;
2013-07-29 23:05:57 +04:00
clear_bit_unlock ( tag , & hba - > lrb_in_use ) ;
wake_up ( & hba - > dev_cmd . tag_wq ) ;
2012-02-29 10:41:50 +04:00
out :
return err ;
}
2013-06-26 21:09:29 +04:00
/**
* ufshcd_async_scan - asynchronous execution for link startup
* @ data : data pointer to pass to this function
* @ cookie : cookie data
*/
static void ufshcd_async_scan ( void * data , async_cookie_t cookie )
{
struct ufs_hba * hba = ( struct ufs_hba * ) data ;
int ret ;
ret = ufshcd_link_startup ( hba ) ;
2013-07-29 23:05:57 +04:00
if ( ret )
goto out ;
ret = ufshcd_verify_dev_init ( hba ) ;
if ( ret )
goto out ;
2013-07-29 23:05:58 +04:00
ret = ufshcd_complete_dev_init ( hba ) ;
if ( ret )
goto out ;
2013-07-29 23:05:57 +04:00
2013-07-29 23:05:59 +04:00
ufshcd_force_reset_auto_bkops ( hba ) ;
2013-07-29 23:05:57 +04:00
scsi_scan_host ( hba - > host ) ;
2013-07-29 23:06:00 +04:00
pm_runtime_put_sync ( hba - > dev ) ;
2013-07-29 23:05:57 +04:00
out :
return ;
2013-06-26 21:09:29 +04:00
}
2012-02-29 10:41:50 +04:00
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 ,
. 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 ,
. can_queue = UFSHCD_CAN_QUEUE ,
} ;
/**
* ufshcd_suspend - suspend power management function
2013-02-25 20:14:32 +04:00
* @ hba : per adapter instance
2012-02-29 10:41:50 +04:00
* @ state : power state
*
* Returns - ENOSYS
*/
2013-02-25 20:14:32 +04:00
int ufshcd_suspend ( struct ufs_hba * hba , pm_message_t state )
2012-02-29 10:41:50 +04:00
{
/*
* TODO :
* 1. Block SCSI requests from SCSI midlayer
* 2. Change the internal driver state to non operational
* 3. Set UTRLRSR and UTMRLRSR bits to zero
* 4. Wait until outstanding commands are completed
* 5. Set HCE to zero to send the UFS host controller to reset state
*/
return - ENOSYS ;
}
2013-02-25 20:14:32 +04:00
EXPORT_SYMBOL_GPL ( ufshcd_suspend ) ;
2012-02-29 10:41:50 +04:00
/**
* ufshcd_resume - resume power management function
2013-02-25 20:14:32 +04:00
* @ hba : per adapter instance
2012-02-29 10:41:50 +04:00
*
* Returns - ENOSYS
*/
2013-02-25 20:14:32 +04:00
int ufshcd_resume ( struct ufs_hba * hba )
2012-02-29 10:41:50 +04:00
{
/*
* TODO :
* 1. Set HCE to 1 , to start the UFS host controller
* initialization process
* 2. Set UTRLRSR and UTMRLRSR bits to 1
* 3. Change the internal driver state to operational
* 4. Unblock SCSI requests from SCSI midlayer
*/
return - ENOSYS ;
}
2013-02-25 20:14:32 +04:00
EXPORT_SYMBOL_GPL ( ufshcd_resume ) ;
2013-07-29 23:05:59 +04:00
int ufshcd_runtime_suspend ( struct ufs_hba * hba )
{
if ( ! hba )
return 0 ;
/*
* The device is idle with no requests in the queue ,
* allow background operations .
*/
return ufshcd_enable_auto_bkops ( hba ) ;
}
EXPORT_SYMBOL ( ufshcd_runtime_suspend ) ;
int ufshcd_runtime_resume ( struct ufs_hba * hba )
{
if ( ! hba )
return 0 ;
return ufshcd_disable_auto_bkops ( hba ) ;
}
EXPORT_SYMBOL ( ufshcd_runtime_resume ) ;
int ufshcd_runtime_idle ( struct ufs_hba * hba )
{
return 0 ;
}
EXPORT_SYMBOL ( ufshcd_runtime_idle ) ;
2012-02-29 10:41:50 +04:00
/**
2013-02-25 20:14:32 +04:00
* ufshcd_remove - de - allocate SCSI host and host memory space
2012-02-29 10:41:50 +04:00
* data structure memory
2013-02-25 20:14:32 +04:00
* @ hba - per adapter instance
2012-02-29 10:41:50 +04:00
*/
2013-02-25 20:14:32 +04:00
void ufshcd_remove ( struct ufs_hba * hba )
2012-02-29 10:41:50 +04:00
{
2013-07-29 23:06:03 +04:00
scsi_remove_host ( hba - > host ) ;
2012-02-29 10:41:50 +04:00
/* disable interrupts */
2013-06-26 21:09:27 +04:00
ufshcd_disable_intr ( hba , hba - > intr_mask ) ;
2012-02-29 10:41:50 +04:00
ufshcd_hba_stop ( hba ) ;
scsi_host_put ( hba - > host ) ;
2013-02-25 20:14:32 +04:00
}
EXPORT_SYMBOL_GPL ( ufshcd_remove ) ;
2012-02-29 10:41:50 +04:00
/**
2013-02-25 20:14:32 +04:00
* ufshcd_init - Driver initialization routine
* @ dev : pointer to device handle
* @ hba_handle : driver private handle
* @ mmio_base : base register address
* @ irq : Interrupt line of device
2012-02-29 10:41:50 +04:00
* Returns 0 on success , non - zero value on failure
*/
2013-02-25 20:14:32 +04:00
int ufshcd_init ( struct device * dev , struct ufs_hba * * hba_handle ,
void __iomem * mmio_base , unsigned int irq )
2012-02-29 10:41:50 +04:00
{
struct Scsi_Host * host ;
struct ufs_hba * hba ;
int err ;
2013-02-25 20:14:32 +04:00
if ( ! dev ) {
dev_err ( dev ,
" Invalid memory reference for dev is NULL \n " ) ;
err = - ENODEV ;
2012-02-29 10:41:50 +04:00
goto out_error ;
}
2013-02-25 20:14:32 +04:00
if ( ! mmio_base ) {
dev_err ( dev ,
" Invalid memory reference for mmio_base is NULL \n " ) ;
err = - ENODEV ;
goto out_error ;
}
2012-02-29 10:41:50 +04:00
host = scsi_host_alloc ( & ufshcd_driver_template ,
sizeof ( struct ufs_hba ) ) ;
if ( ! host ) {
2013-02-25 20:14:32 +04:00
dev_err ( dev , " scsi_host_alloc failed \n " ) ;
2012-02-29 10:41:50 +04:00
err = - ENOMEM ;
2013-02-25 20:14:32 +04:00
goto out_error ;
2012-02-29 10:41:50 +04:00
}
hba = shost_priv ( host ) ;
hba - > host = host ;
2013-02-25 20:14:32 +04:00
hba - > dev = dev ;
hba - > mmio_base = mmio_base ;
hba - > irq = irq ;
2012-02-29 10:41:50 +04:00
/* Read capabilities registers */
ufshcd_hba_capabilities ( hba ) ;
/* Get UFS version supported by the controller */
hba - > ufs_version = ufshcd_get_ufs_version ( hba ) ;
2013-06-26 21:09:27 +04:00
/* Get Interrupt bit mask per version */
hba - > intr_mask = ufshcd_get_intr_mask ( hba ) ;
2012-02-29 10:41:50 +04:00
/* Allocate memory for host memory space */
err = ufshcd_memory_alloc ( hba ) ;
if ( err ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " Memory allocation failed \n " ) ;
goto out_disable ;
2012-02-29 10:41:50 +04:00
}
/* Configure LRB */
ufshcd_host_memory_configure ( hba ) ;
host - > can_queue = hba - > nutrs ;
host - > cmd_per_lun = 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 ;
host - > max_cmd_len = MAX_CDB_SIZE ;
/* Initailize wait queue for task management */
init_waitqueue_head ( & hba - > ufshcd_tm_wait_queue ) ;
/* Initialize work queues */
INIT_WORK ( & hba - > feh_workq , ufshcd_fatal_err_handler ) ;
2013-07-29 23:05:59 +04:00
INIT_WORK ( & hba - > eeh_work , ufshcd_exception_event_handler ) ;
2012-02-29 10:41:50 +04:00
2013-06-26 21:09:29 +04:00
/* Initialize UIC command mutex */
mutex_init ( & hba - > uic_cmd_mutex ) ;
2013-07-29 23:05:57 +04:00
/* Initialize mutex for device management commands */
mutex_init ( & hba - > dev_cmd . lock ) ;
/* Initialize device management tag acquire wait queue */
init_waitqueue_head ( & hba - > dev_cmd . tag_wq ) ;
2012-02-29 10:41:50 +04:00
/* IRQ registration */
2013-06-27 08:31:54 +04:00
err = devm_request_irq ( dev , irq , ufshcd_intr , IRQF_SHARED , UFSHCD , hba ) ;
2012-02-29 10:41:50 +04:00
if ( err ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " request irq failed \n " ) ;
2013-06-27 08:31:54 +04:00
goto out_disable ;
2012-02-29 10:41:50 +04:00
}
/* Enable SCSI tag mapping */
err = scsi_init_shared_tag_map ( host , host - > can_queue ) ;
if ( err ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " init shared queue failed \n " ) ;
2013-06-27 08:31:54 +04:00
goto out_disable ;
2012-02-29 10:41:50 +04:00
}
2013-02-25 20:14:32 +04:00
err = scsi_add_host ( host , hba - > dev ) ;
2012-02-29 10:41:50 +04:00
if ( err ) {
2013-02-25 20:14:32 +04:00
dev_err ( hba - > dev , " scsi_add_host failed \n " ) ;
2013-06-27 08:31:54 +04:00
goto out_disable ;
2012-02-29 10:41:50 +04:00
}
2013-06-26 21:09:29 +04:00
/* Host controller enable */
err = ufshcd_hba_enable ( hba ) ;
2012-02-29 10:41:50 +04:00
if ( err ) {
2013-06-26 21:09:29 +04:00
dev_err ( hba - > dev , " Host controller enable failed \n " ) ;
2013-02-25 20:14:32 +04:00
goto out_remove_scsi_host ;
2012-02-29 10:41:50 +04:00
}
2013-06-26 21:09:29 +04:00
2013-02-25 20:14:32 +04:00
* hba_handle = hba ;
2012-02-29 10:41:50 +04:00
2013-07-29 23:06:00 +04:00
/* Hold auto suspend until async scan completes */
pm_runtime_get_sync ( dev ) ;
2013-06-26 21:09:29 +04:00
async_schedule ( ufshcd_async_scan , hba ) ;
2012-02-29 10:41:50 +04:00
return 0 ;
2013-02-25 20:14:32 +04:00
out_remove_scsi_host :
scsi_remove_host ( hba - > host ) ;
out_disable :
scsi_host_put ( host ) ;
out_error :
return err ;
}
EXPORT_SYMBOL_GPL ( ufshcd_init ) ;
MODULE_AUTHOR ( " Santosh Yaragnavi <santosh.sy@samsung.com> " ) ;
MODULE_AUTHOR ( " Vinayak Holikatti <h.vinayak@samsung.com> " ) ;
2013-02-25 20:14:33 +04:00
MODULE_DESCRIPTION ( " Generic UFS host controller driver Core " ) ;
2012-02-29 10:41:50 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( UFSHCD_DRIVER_VERSION ) ;