summaryrefslogtreecommitdiff
path: root/drivers/scsi/ufs/ufshcd.h
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/ufs/ufshcd.h')
-rw-r--r--drivers/scsi/ufs/ufshcd.h151
1 files changed, 97 insertions, 54 deletions
diff --git a/drivers/scsi/ufs/ufshcd.h b/drivers/scsi/ufs/ufshcd.h
index e0f00a42371c..f8c2467dc014 100644
--- a/drivers/scsi/ufs/ufshcd.h
+++ b/drivers/scsi/ufs/ufshcd.h
@@ -58,6 +58,29 @@ enum dev_cmd_type {
DEV_CMD_TYPE_QUERY = 0x1,
};
+enum ufs_event_type {
+ /* uic specific errors */
+ UFS_EVT_PA_ERR = 0,
+ UFS_EVT_DL_ERR,
+ UFS_EVT_NL_ERR,
+ UFS_EVT_TL_ERR,
+ UFS_EVT_DME_ERR,
+
+ /* fatal errors */
+ UFS_EVT_AUTO_HIBERN8_ERR,
+ UFS_EVT_FATAL_ERR,
+ UFS_EVT_LINK_STARTUP_FAIL,
+ UFS_EVT_RESUME_ERR,
+ UFS_EVT_SUSPEND_ERR,
+
+ /* abnormal events */
+ UFS_EVT_DEV_RESET,
+ UFS_EVT_HOST_RESET,
+ UFS_EVT_ABORT,
+
+ UFS_EVT_CNT,
+};
+
/**
* struct uic_command - UIC command structure
* @command: UIC command
@@ -116,16 +139,22 @@ enum uic_link_state {
((h)->curr_dev_pwr_mode = UFS_SLEEP_PWR_MODE)
#define ufshcd_set_ufs_dev_poweroff(h) \
((h)->curr_dev_pwr_mode = UFS_POWERDOWN_PWR_MODE)
+#define ufshcd_set_ufs_dev_deepsleep(h) \
+ ((h)->curr_dev_pwr_mode = UFS_DEEPSLEEP_PWR_MODE)
#define ufshcd_is_ufs_dev_active(h) \
((h)->curr_dev_pwr_mode == UFS_ACTIVE_PWR_MODE)
#define ufshcd_is_ufs_dev_sleep(h) \
((h)->curr_dev_pwr_mode == UFS_SLEEP_PWR_MODE)
#define ufshcd_is_ufs_dev_poweroff(h) \
((h)->curr_dev_pwr_mode == UFS_POWERDOWN_PWR_MODE)
+#define ufshcd_is_ufs_dev_deepsleep(h) \
+ ((h)->curr_dev_pwr_mode == UFS_DEEPSLEEP_PWR_MODE)
/*
* UFS Power management levels.
- * Each level is in increasing order of power savings.
+ * Each level is in increasing order of power savings, except DeepSleep
+ * which is lower than PowerDown with power on but not PowerDown with
+ * power off.
*/
enum ufs_pm_level {
UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
@@ -134,6 +163,7 @@ enum ufs_pm_level {
UFS_PM_LVL_3, /* UFS_SLEEP_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_4, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_HIBERN8_STATE */
UFS_PM_LVL_5, /* UFS_POWERDOWN_PWR_MODE, UIC_LINK_OFF_STATE */
+ UFS_PM_LVL_6, /* UFS_DEEPSLEEP_PWR_MODE, UIC_LINK_OFF_STATE */
UFS_PM_LVL_MAX
};
@@ -165,6 +195,7 @@ struct ufs_pm_lvl_states {
* @crypto_key_slot: the key slot to use for inline crypto (-1 if none)
* @data_unit_num: the data unit number for the first block for inline crypto
* @req_abort_skip: skip request abort task flag
+ * @in_use: indicates that this lrb is still in use
*/
struct ufshcd_lrb {
struct utp_transfer_req_desc *utr_descriptor_ptr;
@@ -194,6 +225,7 @@ struct ufshcd_lrb {
#endif
bool req_abort_skip;
+ bool in_use;
};
/**
@@ -229,6 +261,8 @@ struct ufs_dev_cmd {
* @max_freq: maximum frequency supported by the clock
* @min_freq: min frequency that can be used for clock scaling
* @curr_freq: indicates the current frequency that it is set to
+ * @keep_link_active: indicates that the clk should not be disabled if
+ link is active
* @enabled: variable to check against multiple enable/disable
*/
struct ufs_clk_info {
@@ -238,6 +272,7 @@ struct ufs_clk_info {
u32 max_freq;
u32 min_freq;
u32 curr_freq;
+ bool keep_link_active;
bool enabled;
};
@@ -269,7 +304,6 @@ struct ufs_pwr_mode_info {
* @get_ufs_hci_version: called to get UFS HCI version
* @clk_scale_notify: notifies that clks are scaled up/down
* @setup_clocks: called before touching any of the controller registers
- * @setup_regulators: called before accessing the host controller
* @hce_enable_notify: called before and after HCE enable bit is set to allow
* variant specific Uni-Pro initialization.
* @link_startup_notify: called before and after Link startup is carried out
@@ -289,6 +323,7 @@ struct ufs_pwr_mode_info {
* @phy_initialization: used to initialize phys
* @device_reset: called to issue a reset pulse on the UFS device
* @program_key: program or evict an inline encryption key
+ * @event_notify: called to notify important events
*/
struct ufs_hba_variant_ops {
const char *name;
@@ -299,7 +334,6 @@ struct ufs_hba_variant_ops {
enum ufs_notify_change_status);
int (*setup_clocks)(struct ufs_hba *, bool,
enum ufs_notify_change_status);
- int (*setup_regulators)(struct ufs_hba *, bool);
int (*hce_enable_notify)(struct ufs_hba *,
enum ufs_notify_change_status);
int (*link_startup_notify)(struct ufs_hba *,
@@ -318,12 +352,14 @@ struct ufs_hba_variant_ops {
int (*resume)(struct ufs_hba *, enum ufs_pm_op);
void (*dbg_register_dump)(struct ufs_hba *hba);
int (*phy_initialization)(struct ufs_hba *);
- void (*device_reset)(struct ufs_hba *hba);
+ int (*device_reset)(struct ufs_hba *hba);
void (*config_scaling_param)(struct ufs_hba *hba,
struct devfreq_dev_profile *profile,
void *data);
int (*program_key)(struct ufs_hba *hba,
const union ufs_crypto_cfg_entry *cfg, int slot);
+ void (*event_notify)(struct ufs_hba *hba,
+ enum ufs_event_type evt, void *data);
};
/* clock gating state */
@@ -382,6 +418,7 @@ struct ufs_saved_pwr_info {
* @workq: workqueue to schedule devfreq suspend/resume work
* @suspend_work: worker to suspend devfreq
* @resume_work: worker to resume devfreq
+ * @min_gear: lowest HS gear to scale down to
* @is_allowed: tracks if scaling is currently allowed or not
* @is_busy_started: tracks if busy period has started or not
* @is_suspended: tracks if devfreq is suspended or not
@@ -396,22 +433,23 @@ struct ufs_clk_scaling {
struct workqueue_struct *workq;
struct work_struct suspend_work;
struct work_struct resume_work;
+ u32 min_gear;
bool is_allowed;
bool is_busy_started;
bool is_suspended;
};
-#define UFS_ERR_REG_HIST_LENGTH 8
+#define UFS_EVENT_HIST_LENGTH 8
/**
- * struct ufs_err_reg_hist - keeps history of errors
+ * struct ufs_event_hist - keeps history of errors
* @pos: index to indicate cyclic buffer position
* @reg: cyclic buffer for registers value
* @tstamp: cyclic buffer for time stamp
*/
-struct ufs_err_reg_hist {
+struct ufs_event_hist {
int pos;
- u32 reg[UFS_ERR_REG_HIST_LENGTH];
- ktime_t tstamp[UFS_ERR_REG_HIST_LENGTH];
+ u32 val[UFS_EVENT_HIST_LENGTH];
+ ktime_t tstamp[UFS_EVENT_HIST_LENGTH];
};
/**
@@ -422,19 +460,6 @@ struct ufs_err_reg_hist {
* reset this after link-startup.
* @last_hibern8_exit_tstamp: Set time after the hibern8 exit.
* Clear after the first successful command completion.
- * @pa_err: tracks pa-uic errors
- * @dl_err: tracks dl-uic errors
- * @nl_err: tracks nl-uic errors
- * @tl_err: tracks tl-uic errors
- * @dme_err: tracks dme errors
- * @auto_hibern8_err: tracks auto-hibernate errors
- * @fatal_err: tracks fatal errors
- * @linkup_err: tracks link-startup errors
- * @resume_err: tracks resume errors
- * @suspend_err: tracks suspend errors
- * @dev_reset: tracks device reset events
- * @host_reset: tracks host reset events
- * @tsk_abort: tracks task abort events
*/
struct ufs_stats {
u32 last_intr_status;
@@ -442,25 +467,7 @@ struct ufs_stats {
u32 hibern8_exit_cnt;
ktime_t last_hibern8_exit_tstamp;
-
- /* uic specific errors */
- struct ufs_err_reg_hist pa_err;
- struct ufs_err_reg_hist dl_err;
- struct ufs_err_reg_hist nl_err;
- struct ufs_err_reg_hist tl_err;
- struct ufs_err_reg_hist dme_err;
-
- /* fatal errors */
- struct ufs_err_reg_hist auto_hibern8_err;
- struct ufs_err_reg_hist fatal_err;
- struct ufs_err_reg_hist link_startup_err;
- struct ufs_err_reg_hist resume_err;
- struct ufs_err_reg_hist suspend_err;
-
- /* abnormal events */
- struct ufs_err_reg_hist dev_reset;
- struct ufs_err_reg_hist host_reset;
- struct ufs_err_reg_hist task_abort;
+ struct ufs_event_hist event[UFS_EVT_CNT];
};
enum ufshcd_quirks {
@@ -594,6 +601,21 @@ enum ufshcd_caps {
* inline crypto engine, if it is present
*/
UFSHCD_CAP_CRYPTO = 1 << 8,
+
+ /*
+ * This capability allows the controller regulators to be put into
+ * lpm mode aggressively during clock gating.
+ * This would increase power savings.
+ */
+ UFSHCD_CAP_AGGR_POWER_COLLAPSE = 1 << 9,
+
+ /*
+ * This capability allows the host controller driver to use DeepSleep,
+ * if it is supported by the UFS device. The host controller driver must
+ * support device hardware reset via the hba->device_reset() callback,
+ * in order to exit DeepSleep state.
+ */
+ UFSHCD_CAP_DEEPSLEEP = 1 << 10,
};
struct ufs_hba_variant_params {
@@ -683,6 +705,7 @@ struct ufs_hba {
* "UFS device" W-LU.
*/
struct scsi_device *sdev_ufs_device;
+ struct scsi_device *sdev_rpmb;
enum ufs_dev_pwr_mode curr_dev_pwr_mode;
enum uic_link_state uic_link_state;
@@ -730,6 +753,7 @@ struct ufs_hba {
u32 intr_mask;
u16 ee_ctrl_mask;
bool is_powered;
+ struct semaphore eh_sem;
/* Work Queues */
struct workqueue_struct *eh_wq;
@@ -831,6 +855,12 @@ return true;
#endif
}
+static inline bool ufshcd_can_aggressive_pc(struct ufs_hba *hba)
+{
+ return !!(ufshcd_is_link_hibern8(hba) &&
+ (hba->caps & UFSHCD_CAP_AGGR_POWER_COLLAPSE));
+}
+
static inline bool ufshcd_is_auto_hibern8_supported(struct ufs_hba *hba)
{
return (hba->capabilities & MASK_AUTO_HIBERN8_SUPPORT) &&
@@ -882,8 +912,7 @@ int ufshcd_wait_for_register(struct ufs_hba *hba, u32 reg, u32 mask,
u32 val, unsigned long interval_us,
unsigned long timeout_ms);
void ufshcd_parse_dev_ref_clk_freq(struct ufs_hba *hba, struct clk *refclk);
-void ufshcd_update_reg_hist(struct ufs_err_reg_hist *reg_hist,
- u32 reg);
+void ufshcd_update_evt_hist(struct ufs_hba *hba, u32 id, u32 val);
static inline void check_upiu_size(void)
{
@@ -930,6 +959,9 @@ extern int ufshcd_runtime_idle(struct ufs_hba *hba);
extern int ufshcd_system_suspend(struct ufs_hba *hba);
extern int ufshcd_system_resume(struct ufs_hba *hba);
extern int ufshcd_shutdown(struct ufs_hba *hba);
+extern int ufshcd_dme_configure_adapt(struct ufs_hba *hba,
+ int agreed_gear,
+ int adapt_val);
extern int ufshcd_dme_set_attr(struct ufs_hba *hba, u32 attr_sel,
u8 attr_set, u32 mib_val, u8 peer);
extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel,
@@ -1076,6 +1108,14 @@ static inline int ufshcd_vops_clk_scale_notify(struct ufs_hba *hba,
return 0;
}
+static inline void ufshcd_vops_event_notify(struct ufs_hba *hba,
+ enum ufs_event_type evt,
+ void *data)
+{
+ if (hba->vops && hba->vops->event_notify)
+ hba->vops->event_notify(hba, evt, data);
+}
+
static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on,
enum ufs_notify_change_status status)
{
@@ -1084,14 +1124,6 @@ static inline int ufshcd_vops_setup_clocks(struct ufs_hba *hba, bool on,
return 0;
}
-static inline int ufshcd_vops_setup_regulators(struct ufs_hba *hba, bool status)
-{
- if (hba->vops && hba->vops->setup_regulators)
- return hba->vops->setup_regulators(hba, status);
-
- return 0;
-}
-
static inline int ufshcd_vops_hce_enable_notify(struct ufs_hba *hba,
bool status)
{
@@ -1109,6 +1141,14 @@ static inline int ufshcd_vops_link_startup_notify(struct ufs_hba *hba,
return 0;
}
+static inline int ufshcd_vops_phy_initialization(struct ufs_hba *hba)
+{
+ if (hba->vops && hba->vops->phy_initialization)
+ return hba->vops->phy_initialization(hba);
+
+ return 0;
+}
+
static inline int ufshcd_vops_pwr_change_notify(struct ufs_hba *hba,
bool status,
struct ufs_pa_layer_attr *dev_max_params,
@@ -1181,9 +1221,12 @@ static inline void ufshcd_vops_dbg_register_dump(struct ufs_hba *hba)
static inline void ufshcd_vops_device_reset(struct ufs_hba *hba)
{
if (hba->vops && hba->vops->device_reset) {
- hba->vops->device_reset(hba);
- ufshcd_set_ufs_dev_active(hba);
- ufshcd_update_reg_hist(&hba->ufs_stats.dev_reset, 0);
+ int err = hba->vops->device_reset(hba);
+
+ if (!err)
+ ufshcd_set_ufs_dev_active(hba);
+ if (err != -EOPNOTSUPP)
+ ufshcd_update_evt_hist(hba, UFS_EVT_DEV_RESET, err);
}
}