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

Subhash Jadavani
2013-05-02T12:09:01Z

On 4/24/2013 9:36 PM, Seungwon Jeon wrote:
> 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_

I don't see a need to add this file as part of this patch as there are
no users of the same in this patch. So its better to remove from here
and add it as separate patch when their real use arises.

> +
> +/*
> + * 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);
> +}
> +
> +

Please remove one extra line space above.

> +/**
> + * 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,