Add uic command operations including DME_XXX series. Signed-off-by: Seungwon Jeon <tgih.jun@xxxxxxxxxxx> --- drivers/scsi/ufs/ufs-attrs.h | 129 ++++++++++++++++++++++++ drivers/scsi/ufs/ufshcd.c | 220 +++++++++++++++++++++++++++++++++++++++++- drivers/scsi/ufs/ufshcd.h | 55 +++++++++++ drivers/scsi/ufs/ufshci.h | 19 ++++ 4 files changed, 422 insertions(+), 1 deletions(-) create mode 100644 drivers/scsi/ufs/ufs-attrs.h diff --git a/drivers/scsi/ufs/ufs-attrs.h b/drivers/scsi/ufs/ufs-attrs.h new file mode 100644 index 0000000..562bb49 --- /dev/null +++ b/drivers/scsi/ufs/ufs-attrs.h @@ -0,0 +1,129 @@ +/* + * drivers/scsi/ufs/ufs-attrs.h + * + * Copyright (C) 2013 Samsung Electronics Co., Ltd. + * + * 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. + */ + +#ifndef _UFS_ATTRS_H_ +#define _UFS_ATTRS_H_ + +/* + * PHY Adpater attributes + */ +#define PA_ACTIVETXDATALANES 0x1560 +#define PA_ACTIVERXDATALANES 0x1580 +#define PA_TXTRAILINGCLOCKS 0x1564 +#define PA_PHY_TYPE 0x1500 +#define PA_AVAILTXDATALANES 0x1520 +#define PA_AVAILRXDATALANES 0x1540 +#define PA_MINRXTRAILINGCLOCKS 0x1543 +#define PA_TXPWRSTATUS 0x1567 +#define PA_RXPWRSTATUS 0x1582 +#define PA_TXFORCECLOCK 0x1562 +#define PA_TXPWRMODE 0x1563 +#define PA_LEGACYDPHYESCDL 0x1570 +#define PA_MAXTXSPEEDFAST 0x1521 +#define PA_MAXTXSPEEDSLOW 0x1522 +#define PA_MAXRXSPEEDFAST 0x1541 +#define PA_MAXRXSPEEDSLOW 0x1542 +#define PA_TXLINKSTARTUPHS 0x1544 +#define PA_TXSPEEDFAST 0x1565 +#define PA_TXSPEEDSLOW 0x1566 +#define PA_REMOTEVERINFO 0x15A0 +#define PA_TXGEAR 0x1568 +#define PA_TXTERMINATION 0x1569 +#define PA_HSSERIES 0x156A +#define PA_PWRMODE 0x1571 +#define PA_RXGEAR 0x1583 +#define PA_RXTERMINATION 0x1584 +#define PA_MAXRXPWMGEAR 0x1586 +#define PA_MAXRXHSGEAR 0x1587 +#define PA_RXHSUNTERMCAP 0x15A5 +#define PA_RXLSTERMCAP 0x15A6 +#define PA_PACPREQTIMEOUT 0x1590 +#define PA_PACPREQEOBTIMEOUT 0x1591 +#define PA_LOCALVERINFO 0x15A9 +#define PA_TACTIVATE 0x15A8 +#define PA_PACPFRAMECOUNT 0x15C0 +#define PA_PACPERRORCOUNT 0x15C1 +#define PA_PHYTESTCONTROL 0x15C2 +#define PA_PWRMODEUSERDATA0 0x15B0 +#define PA_PWRMODEUSERDATA1 0x15B1 +#define PA_PWRMODEUSERDATA2 0x15B2 +#define PA_PWRMODEUSERDATA3 0x15B3 +#define PA_PWRMODEUSERDATA4 0x15B4 +#define PA_PWRMODEUSERDATA5 0x15B5 +#define PA_PWRMODEUSERDATA6 0x15B6 +#define PA_PWRMODEUSERDATA7 0x15B7 +#define PA_PWRMODEUSERDATA8 0x15B8 +#define PA_PWRMODEUSERDATA9 0x15B9 +#define PA_PWRMODEUSERDATA10 0x15BA +#define PA_PWRMODEUSERDATA11 0x15BB +#define PA_CONNECTEDTXDATALANE 0x1561 +#define PA_CONNECTEDRXDATALANE 0x1581 +#define PA_LOGICALLANEMAP 0x15A1 +#define PA_SLEEPNOCONFIGTIME 0x15A2 +#define PA_STALLNOCONFIGTIME 0x15A3 +#define PA_SAVECONFIGTIME 0x15A4 + +/* + * Data Link Layer Attributes + */ +#define DL_TC0TXFCTHRESHOLD 0x2040 +#define DL_FC0PROTTIMEOUTVAL 0x2041 +#define DL_TC0REPLAYTIMEOUTVAL 0x2042 +#define DL_AFC0REQTIMEOUTVAL 0x2043 +#define DL_AFC0CREDITTHRESHOLD 0x2044 +#define DL_TC0OUTACKTHRESHOLD 0x2045 +#define DL_TC1TXFCTHRESHOLD 0x2060 +#define DL_FC1PROTTIMEOUTVAL 0x2061 +#define DL_TC1REPLAYTIMEOUTVAL 0x2062 +#define DL_AFC1REQTIMEOUTVAL 0x2063 +#define DL_AFC1CREDITTHRESHOLD 0x2064 +#define DL_TC1OUTACKTHRESHOLD 0x2065 +#define DL_TXPREEMPTIONCAP 0x2000 +#define DL_TC0TXMAXSDUSIZE 0x2001 +#define DL_TC0RXINITCREDITVAL 0x2002 +#define DL_TC0TXBUFFERSIZE 0x2005 +#define DL_PEERTC0PRESENT 0x2046 +#define DL_PEERTC0RXINITCREVAL 0x2047 +#define DL_TC1TXMAXSDUSIZE 0x2003 +#define DL_TC1RXINITCREDITVAL 0x2004 +#define DL_TC1TXBUFFERSIZE 0x2006 +#define DL_PEERTC1PRESENT 0x2066 +#define DL_PEERTC1RXINITCREVAL 0x2067 + +/* + * Network Layer Attributes + */ +#define N_DEVICEID 0x3000 +#define N_DEVICEID_VALID 0x3001 +#define N_TC0TXMAXSDUSIZE 0x3020 +#define N_TC1TXMAXSDUSIZE 0x3021 + +/* + * Transport Layer Attributes + */ +#define T_NUMCPORTS 0x4000 +#define T_NUMTESTFEATURES 0x4001 +#define T_CONNECTIONSTATE 0x4020 +#define T_PEERDEVICEID 0x4021 +#define T_PEERCPORTID 0x4022 +#define T_TRAFFICCLASS 0x4023 +#define T_PROTOCOLID 0x4024 +#define T_CPORTFLAGS 0x4025 +#define T_TXTOKENVALUE 0x4026 +#define T_RXTOKENVALUE 0x4027 +#define T_LOCALBUFFERSPACE 0x4028 +#define T_PEERBUFFERSPACE 0x4029 +#define T_CREDITSTOSEND 0x402A +#define T_CPORTMODE 0x402B +#define T_TC0TXMAXSDUSIZE 0x4060 +#define T_TC1TXMAXSDUSIZE 0x4061 + +#endif /* _UFS_ATTRS_H_ */ diff --git a/drivers/scsi/ufs/ufshcd.c b/drivers/scsi/ufs/ufshcd.c index 76ff332..55575ea 100644 --- a/drivers/scsi/ufs/ufshcd.c +++ b/drivers/scsi/ufs/ufshcd.c @@ -37,6 +37,7 @@ #define UFSHCD_ENABLE_INTRS (UTP_TRANSFER_REQ_COMPL |\ UTP_TASK_REQ_COMPL |\ + UFSHCD_HIBERNATE_MASK |\ UFSHCD_ERROR_MASK) #define UIC_CMD_TIMEOUT 100 @@ -188,6 +189,31 @@ static inline int ufshcd_get_uic_cmd_result(struct ufs_hba *hba) } /** + * ufshcd_get_dme_attr_val - Get the value of attribute returned by UIC command + * @hba: Pointer to adapter instance + * + * This function gets UIC command argument3 + * Returns 0 on success, non zero value on error + */ +static inline u32 ufshcd_get_dme_attr_val(struct ufs_hba *hba) +{ + return ufshcd_readl(hba, REG_UIC_COMMAND_ARG_3); +} + + +/** + * ufshcd_get_upmcrs - Get the power mode change request status + * @hba: Pointer to adapter instance + * + * This function gets the UPMCRS field of HCS register + * Returns value of UPMCRS field + */ +static inline u8 ufshcd_get_upmcrs(struct ufs_hba *hba) +{ + return (ufshcd_readl(hba, REG_CONTROLLER_STATUS) >> 8) & 0x7; +} + +/** * ufshcd_free_hba_memory - Free allocated memory for LRB, request * and task lists * @hba: Pointer to adapter instance @@ -804,6 +830,195 @@ static int ufshcd_dme_link_startup(struct ufs_hba *hba) } /** + * ufshcd_dme_xxx_set - UIC command for DME_SET, DME_PEER_SET + * @hba: per adapter instance + * + * Returns 0 on success, non-zero value on failure + */ +int ufshcd_dme_xxx_set(struct ufs_hba *hba, u32 attr_sel, + u8 attr_set, u32 mib_val, u8 peer) +{ + struct uic_command *uic_cmd; + static const char *const action[] = { + "dme-set", "dme-peer-set" + }; + const char *set = action[!!peer]; + unsigned long flags; + int ret; + + if (!ufshcd_ready_uic_cmd(hba)) + return -EIO; + + spin_lock_irqsave(hba->host->host_lock, flags); + + /* form UIC command */ + uic_cmd = &hba->active_uic_cmd; + uic_cmd->command = peer ? + UIC_CMD_DME_PEER_SET : UIC_CMD_DME_SET; + uic_cmd->argument1 = attr_sel; + uic_cmd->argument2 = UIC_ARG_ATTR_SET(attr_set); + uic_cmd->argument3 = mib_val; + + /* dispatch UIC commands to controller */ + ufshcd_dispatch_uic_cmd(hba, uic_cmd); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + ret = ufshcd_wait_for_uic_cmd(hba); + dev_dbg(hba->dev, "%s: error code %d returned\n", set, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ufshcd_dme_xxx_set); + +/** + * ufshcd_dme_xxx_get - UIC command for DME_GET, DME_PEER_GET + * @hba: per adapter instance + * + * Returns 0 on success, non-zero value on failure + */ +int ufshcd_dme_xxx_get(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer) +{ + struct uic_command *uic_cmd; + static const char *const action[] = { + "dme-get", "dme-peer-get" + }; + const char *get = action[!!peer]; + unsigned long flags; + int ret; + + if (!ufshcd_ready_uic_cmd(hba)) + return -EIO; + + spin_lock_irqsave(hba->host->host_lock, flags); + + /* form UIC command */ + uic_cmd = &hba->active_uic_cmd; + uic_cmd->command = peer ? + UIC_CMD_DME_PEER_GET : UIC_CMD_DME_GET; + uic_cmd->argument1 = attr_sel; + uic_cmd->argument2 = 0; + uic_cmd->argument3 = 0; + + /* dispatch UIC commands to controller */ + ufshcd_dispatch_uic_cmd(hba, uic_cmd); + spin_unlock_irqrestore(hba->host->host_lock, flags); + + ret = ufshcd_wait_for_uic_cmd(hba); + if (mib_val) + *mib_val = ufshcd_get_dme_attr_val(hba); + + dev_dbg(hba->dev, "%s: error code %d returned\n", get, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ufshcd_dme_xxx_get); + +/** + * ufshcd_dme_power_xxx - UIC command for DME_POWERON, DME_POWEROFF + * @hba: per adapter instance + * + * Returns 0 on success, non-zero value on failure + */ +int ufshcd_dme_power_xxx(struct ufs_hba *hba, u8 on) +{ + struct uic_command *uic_cmd; + static const char *const action[] = { + "dme-power-on", + "dme-power-off", + }; + const char *power = action[!!on]; + unsigned long flags; + int ret; + + if (!ufshcd_ready_uic_cmd(hba)) + return -EIO; + + spin_lock_irqsave(hba->host->host_lock, flags); + + /* form UIC command */ + uic_cmd = &hba->active_uic_cmd; + uic_cmd->command = on ? + UIC_CMD_DME_POWERON : UIC_CMD_DME_POWEROFF; + uic_cmd->argument1 = 0; + uic_cmd->argument2 = 0; + uic_cmd->argument3 = 0; + + /* dispatch UIC commands to controller */ + ufshcd_dispatch_uic_cmd(hba, uic_cmd); + + spin_unlock_irqrestore(hba->host->host_lock, flags); + + ret = ufshcd_wait_for_uic_cmd(hba); + if (ret) + dev_err(hba->dev, "%s: error code %d returned\n", power, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(ufshcd_dme_power_xxx); + +/** + * ufshcd_dme_hibern8_xxx - UIC command for DME_HIBERNATE_ENTER, + * DME_HIBERNATE_EXIT + * @hba: per adapter instance + * + * Returns 0 on success, non-zero value on failure + */ +int ufshcd_dme_hibern8_xxx(struct ufs_hba *hba, u8 enter) +{ + struct uic_command *uic_cmd; + static const char *const action[] = { + "dme-hibernate-enter", + "dme-hibernate-exit" + }; + const char *hibern8 = action[!!enter]; + unsigned long flags; + u8 status; + int ret; + + if (!ufshcd_ready_uic_cmd(hba)) + return -EIO; + + spin_lock_irqsave(hba->host->host_lock, flags); + + /* form UIC command */ + uic_cmd = &hba->active_uic_cmd; + uic_cmd->command = enter ? + UIC_CMD_DME_HIBER_ENTER : UIC_CMD_DME_HIBER_EXIT; + uic_cmd->argument1 = 0; + uic_cmd->argument2 = 0; + uic_cmd->argument3 = 0; + + /* dispatch UIC commands to controller */ + ufshcd_dispatch_uic_cmd(hba, uic_cmd); + + spin_unlock_irqrestore(hba->host->host_lock, flags); + + ret= ufshcd_wait_for_uic_cmd(hba); + if (ret) { + dev_err(hba->dev, "%s: error code %d returned\n", hibern8, ret); + goto out; + } + + init_completion(&hba->hibern8_done); + + if (wait_for_completion_timeout(&hba->hibern8_done, + msecs_to_jiffies(UIC_CMD_TIMEOUT))) { + status = ufshcd_get_upmcrs(hba); + if (status != PWR_LOCAL) { + dev_err(hba->dev, "%s: failed, host upmcrs:%x\n", + hibern8, status); + ret = status; + } + } else { + dev_err(hba->dev, "%s: timeout\n", hibern8); + ret = -ETIMEDOUT; + } +out: + return ret; +} +EXPORT_SYMBOL_GPL(ufshcd_dme_hibern8_xxx); + +/** * ufshcd_make_hba_operational - Make UFS controller operational * @hba: per adapter instance * @@ -1238,6 +1453,9 @@ static void ufshcd_uic_cmd_compl(struct ufs_hba *hba, u32 intr_status) { if (intr_status & UIC_COMMAND_COMPL) complete(&hba->active_uic_cmd.done); + + if (intr_status & UFSHCD_HIBERNATE_MASK) + complete(&hba->hibern8_done); } /** @@ -1362,7 +1580,7 @@ static void ufshcd_sl_intr(struct ufs_hba *hba, u32 intr_status) if (hba->errors) ufshcd_err_handler(hba); - if (intr_status & UIC_COMMAND_COMPL) + if (intr_status & UFSHCD_UIC_MASK) ufshcd_uic_cmd_compl(hba, intr_status); if (intr_status & UTP_TASK_REQ_COMPL) diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h index 2fb4d94..5bf19f5 100644 --- a/drivers/scsi/ufs/ufshcd.h +++ b/drivers/scsi/ufs/ufshcd.h @@ -140,6 +140,7 @@ struct ufshcd_lrb { * @active_uic_cmd: handle of active UIC command * @ufshcd_tm_wait_queue: wait queue for task management * @tm_condition: condition variable for task management + * @hibern8_done: completion for hibernate * @ufshcd_state: UFSHCD states * @intr_mask: Interrupt Mask Bits * @link_startup_wq: Work queue for link start-up @@ -177,6 +178,8 @@ struct ufs_hba { wait_queue_head_t ufshcd_tm_wait_queue; unsigned long tm_condition; + struct completion hibern8_done; + u32 ufshcd_state; u32 intr_mask; @@ -197,4 +200,56 @@ void ufshcd_remove(struct ufs_hba *); #define ufshcd_readl(hba, reg) \ readl((hba)->mmio_base + (reg)) + +extern int ufshcd_dme_xxx_set(struct ufs_hba *hba, u32 attr_sel, + u8 attr_set, u32 mib_val, u8 peer); +extern int ufshcd_dme_xxx_get(struct ufs_hba *hba, u32 attr_sel, + u32 *mib_val, u8 peer); +extern int ufshcd_dme_power_xxx(struct ufs_hba *hba, u8 on); +extern int ufshcd_dme_hibern8_xxx(struct ufs_hba *hba, u8 enter); + +static inline int ufshcd_dme_set(struct ufs_hba *hba, u32 attr_sel, + u8 attr_set, u32 mib_val) +{ + return ufshcd_dme_xxx_set(hba, attr_sel, attr_set, mib_val, 0); +} + +static inline int ufshcd_dme_peer_set(struct ufs_hba *hba, u32 attr_sel, + u8 attr_set, u32 mib_val) +{ + return ufshcd_dme_xxx_set(hba, attr_sel, attr_set, mib_val, 1); +} + +static inline int ufshcd_dme_get(struct ufs_hba *hba, u32 attr_sel, + u32 *mib_val) +{ + return ufshcd_dme_xxx_get(hba, attr_sel, mib_val, 0); +} + +static inline int ufshcd_dme_peer_get(struct ufs_hba *hba, u32 attr_sel, + u32 *mib_val) +{ + return ufshcd_dme_xxx_get(hba, attr_sel, mib_val, 1); +} + +static inline int ufshcd_dme_power_on(struct ufs_hba *hba) +{ + return ufshcd_dme_power_xxx(hba, 1); +} + +static inline int ufshcd_dme_power_off(struct ufs_hba *hba) +{ + return ufshcd_dme_power_xxx(hba, 0); +} + +static inline int ufshcd_dme_hibern8_enter(struct ufs_hba *hba, u8 enter) +{ + return ufshcd_dme_hibern8_xxx(hba, 1); +} + +static inline int ufshcd_dme_hibern8_exit(struct ufs_hba *hba, u8 enter) +{ + return ufshcd_dme_hibern8_xxx(hba, 0); +} + #endif /* End of Header */ diff --git a/drivers/scsi/ufs/ufshci.h b/drivers/scsi/ufs/ufshci.h index d5c5f14..28ede2a 100644 --- a/drivers/scsi/ufs/ufshci.h +++ b/drivers/scsi/ufs/ufshci.h @@ -124,6 +124,12 @@ enum { #define CONTROLLER_FATAL_ERROR UFS_BIT(16) #define SYSTEM_BUS_FATAL_ERROR UFS_BIT(17) +#define UFSHCD_HIBERNATE_MASK (UIC_HIBERNATE_ENTER |\ + UIC_HIBERNATE_EXIT) + +#define UFSHCD_UIC_MASK (UIC_COMMAND_COMPL |\ + UFSHCD_HIBERNATE_MASK) + #define UFSHCD_ERROR_MASK (UIC_ERROR |\ DEVICE_FATAL_ERROR |\ CONTROLLER_FATAL_ERROR |\ @@ -142,6 +148,15 @@ enum { #define DEVICE_ERROR_INDICATOR UFS_BIT(5) #define UIC_POWER_MODE_CHANGE_REQ_STATUS_MASK UFS_MASK(0x7, 8) +enum { + PWR_OK = 0x0, + PWR_LOCAL = 0x01, + PWR_REMOTE = 0x02, + PWR_BUSY = 0x03, + PWR_ERROR_CAP = 0x04, + PWR_FATAL_ERROR = 0x05, +}; + /* HCE - Host Controller Enable 34h */ #define CONTROLLER_ENABLE UFS_BIT(0) #define CONTROLLER_DISABLE 0x0 @@ -191,6 +206,10 @@ enum { #define CONFIG_RESULT_CODE_MASK 0xFF #define GENERIC_ERROR_CODE_MASK 0xFF +#define UIC_ARG_MIB_SEL(attr, sel) (((attr & 0xFFFF) << 16) |\ + (sel & 0xFFFF)) +#define UIC_ARG_ATTR_SET(type) ((type & 0xFF) << 16) + /* UIC Commands */ enum { UIC_CMD_DME_GET = 0x01, -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html