[PATCH 5/5] scsi: ufs: add dme operations

Seungwon Jeon
2013-04-24T16:06:45Z

Add uic command operations including DME_XXX series.

Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
---
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