Tested-by: Maya Erez <merez@xxxxxxxxxxxxxx> > 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 > -- Maya Erez QUALCOMM ISRAEL, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation -- 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