Merge branch 'x86-mm-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-block.git] / drivers / scsi / ufs / ufshcd.h
index 8365ad437aa61a3fd1d0f32d33d5330ca755776a..58ecdff5065c27d2dc3b6c563f641675799435dc 100644 (file)
@@ -96,6 +96,54 @@ struct uic_command {
        struct completion done;
 };
 
+/* Used to differentiate the power management options */
+enum ufs_pm_op {
+       UFS_RUNTIME_PM,
+       UFS_SYSTEM_PM,
+       UFS_SHUTDOWN_PM,
+};
+
+#define ufshcd_is_runtime_pm(op) ((op) == UFS_RUNTIME_PM)
+#define ufshcd_is_system_pm(op) ((op) == UFS_SYSTEM_PM)
+#define ufshcd_is_shutdown_pm(op) ((op) == UFS_SHUTDOWN_PM)
+
+/* Host <-> Device UniPro Link state */
+enum uic_link_state {
+       UIC_LINK_OFF_STATE      = 0, /* Link powered down or disabled */
+       UIC_LINK_ACTIVE_STATE   = 1, /* Link is in Fast/Slow/Sleep state */
+       UIC_LINK_HIBERN8_STATE  = 2, /* Link is in Hibernate state */
+};
+
+#define ufshcd_is_link_off(hba) ((hba)->uic_link_state == UIC_LINK_OFF_STATE)
+#define ufshcd_is_link_active(hba) ((hba)->uic_link_state == \
+                                   UIC_LINK_ACTIVE_STATE)
+#define ufshcd_is_link_hibern8(hba) ((hba)->uic_link_state == \
+                                   UIC_LINK_HIBERN8_STATE)
+#define ufshcd_set_link_off(hba) ((hba)->uic_link_state = UIC_LINK_OFF_STATE)
+#define ufshcd_set_link_active(hba) ((hba)->uic_link_state = \
+                                   UIC_LINK_ACTIVE_STATE)
+#define ufshcd_set_link_hibern8(hba) ((hba)->uic_link_state = \
+                                   UIC_LINK_HIBERN8_STATE)
+
+/*
+ * UFS Power management levels.
+ * Each level is in increasing order of power savings.
+ */
+enum ufs_pm_level {
+       UFS_PM_LVL_0, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE */
+       UFS_PM_LVL_1, /* UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE */
+       UFS_PM_LVL_2, /* UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE */
+       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_MAX
+};
+
+struct ufs_pm_lvl_states {
+       enum ufs_dev_pwr_mode dev_state;
+       enum uic_link_state link_state;
+};
+
 /**
  * struct ufshcd_lrb - local reference block
  * @utr_descriptor_ptr: UTRD address of the command
@@ -124,7 +172,7 @@ struct ufshcd_lrb {
 
        int command_type;
        int task_tag;
-       unsigned int lun;
+       u8 lun; /* UPIU LUN id field is only 8-bit wide */
        bool intr_cmd;
 };
 
@@ -161,6 +209,8 @@ struct ufs_dev_cmd {
  * @clk: clock node
  * @name: clock name
  * @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
  * @enabled: variable to check against multiple enable/disable
  */
 struct ufs_clk_info {
@@ -168,31 +218,100 @@ struct ufs_clk_info {
        struct clk *clk;
        const char *name;
        u32 max_freq;
+       u32 min_freq;
+       u32 curr_freq;
        bool enabled;
 };
 
 #define PRE_CHANGE      0
 #define POST_CHANGE     1
+
+struct ufs_pa_layer_attr {
+       u32 gear_rx;
+       u32 gear_tx;
+       u32 lane_rx;
+       u32 lane_tx;
+       u32 pwr_rx;
+       u32 pwr_tx;
+       u32 hs_rate;
+};
+
+struct ufs_pwr_mode_info {
+       bool is_valid;
+       struct ufs_pa_layer_attr info;
+};
+
 /**
  * struct ufs_hba_variant_ops - variant specific callbacks
  * @name: variant name
  * @init: called when the driver is initialized
  * @exit: called to cleanup everything done in init
+ * @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
  *                       to allow variant specific Uni-Pro initialization.
+ * @pwr_change_notify: called before and after a power mode change
+ *                     is carried out to allow vendor spesific capabilities
+ *                     to be set.
+ * @suspend: called during host controller PM callback
+ * @resume: called during host controller PM callback
  */
 struct ufs_hba_variant_ops {
        const char *name;
        int     (*init)(struct ufs_hba *);
        void    (*exit)(struct ufs_hba *);
+       void    (*clk_scale_notify)(struct ufs_hba *);
        int     (*setup_clocks)(struct ufs_hba *, bool);
        int     (*setup_regulators)(struct ufs_hba *, bool);
        int     (*hce_enable_notify)(struct ufs_hba *, bool);
        int     (*link_startup_notify)(struct ufs_hba *, bool);
+       int     (*pwr_change_notify)(struct ufs_hba *,
+                                       bool, struct ufs_pa_layer_attr *,
+                                       struct ufs_pa_layer_attr *);
+       int     (*suspend)(struct ufs_hba *, enum ufs_pm_op);
+       int     (*resume)(struct ufs_hba *, enum ufs_pm_op);
+};
+
+/* clock gating state  */
+enum clk_gating_state {
+       CLKS_OFF,
+       CLKS_ON,
+       REQ_CLKS_OFF,
+       REQ_CLKS_ON,
+};
+
+/**
+ * struct ufs_clk_gating - UFS clock gating related info
+ * @gate_work: worker to turn off clocks after some delay as specified in
+ * delay_ms
+ * @ungate_work: worker to turn on clocks that will be used in case of
+ * interrupt context
+ * @state: the current clocks state
+ * @delay_ms: gating delay in ms
+ * @is_suspended: clk gating is suspended when set to 1 which can be used
+ * during suspend/resume
+ * @delay_attr: sysfs attribute to control delay_attr
+ * @active_reqs: number of requests that are pending and should be waited for
+ * completion before gating clocks.
+ */
+struct ufs_clk_gating {
+       struct delayed_work gate_work;
+       struct work_struct ungate_work;
+       enum clk_gating_state state;
+       unsigned long delay_ms;
+       bool is_suspended;
+       struct device_attribute delay_attr;
+       int active_reqs;
+};
+
+struct ufs_clk_scaling {
+       ktime_t  busy_start_t;
+       bool is_busy_started;
+       unsigned long  tot_busy_t;
+       unsigned long window_start_t;
 };
 
 /**
@@ -250,6 +369,8 @@ struct ufs_init_prefetch {
  * @auto_bkops_enabled: to track whether bkops is enabled in device
  * @vreg_info: UFS device voltage regulator information
  * @clk_list_head: UFS host controller clocks list node head
+ * @pwr_info: holds current power mode
+ * @max_pwr_info: keeps the device max valid pwm
  */
 struct ufs_hba {
        void __iomem *mmio_base;
@@ -266,6 +387,21 @@ struct ufs_hba {
 
        struct Scsi_Host *host;
        struct device *dev;
+       /*
+        * This field is to keep a reference to "scsi_device" corresponding to
+        * "UFS device" W-LU.
+        */
+       struct scsi_device *sdev_ufs_device;
+       struct scsi_device *sdev_rpmb;
+       struct scsi_device *sdev_boot;
+
+       enum ufs_dev_pwr_mode curr_dev_pwr_mode;
+       enum uic_link_state uic_link_state;
+       /* Desired UFS power management level during runtime PM */
+       enum ufs_pm_level rpm_lvl;
+       /* Desired UFS power management level during system PM */
+       enum ufs_pm_level spm_lvl;
+       int pm_op_in_progress;
 
        struct ufshcd_lrb *lrb;
        unsigned long lrb_in_use;
@@ -280,16 +416,17 @@ struct ufs_hba {
        struct ufs_hba_variant_ops *vops;
        void *priv;
        unsigned int irq;
+       bool is_irq_enabled;
 
-       struct uic_command *active_uic_cmd;
-       struct mutex uic_cmd_mutex;
 
        wait_queue_head_t tm_wq;
        wait_queue_head_t tm_tag_wq;
        unsigned long tm_condition;
        unsigned long tm_slots_in_use;
 
-       struct completion *pwr_done;
+       struct uic_command *active_uic_cmd;
+       struct mutex uic_cmd_mutex;
+       struct completion *uic_async_done;
 
        u32 ufshcd_state;
        u32 eh_flags;
@@ -312,16 +449,74 @@ struct ufs_hba {
        /* Device management request data */
        struct ufs_dev_cmd dev_cmd;
 
+       /* Keeps information of the UFS device connected to this host */
+       struct ufs_dev_info dev_info;
        bool auto_bkops_enabled;
        struct ufs_vreg_info vreg_info;
        struct list_head clk_list_head;
+
+       bool wlun_dev_clr_ua;
+
+       struct ufs_pa_layer_attr pwr_info;
+       struct ufs_pwr_mode_info max_pwr_info;
+
+       struct ufs_clk_gating clk_gating;
+       /* Control to enable/disable host capabilities */
+       u32 caps;
+       /* Allow dynamic clk gating */
+#define UFSHCD_CAP_CLK_GATING  (1 << 0)
+       /* Allow hiberb8 with clk gating */
+#define UFSHCD_CAP_HIBERN8_WITH_CLK_GATING (1 << 1)
+       /* Allow dynamic clk scaling */
+#define UFSHCD_CAP_CLK_SCALING (1 << 2)
+       /* Allow auto bkops to enabled during runtime suspend */
+#define UFSHCD_CAP_AUTO_BKOPS_SUSPEND (1 << 3)
+
+       struct devfreq *devfreq;
+       struct ufs_clk_scaling clk_scaling;
+       bool is_sys_suspended;
 };
 
+/* Returns true if clocks can be gated. Otherwise false */
+static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
+{
+       return hba->caps & UFSHCD_CAP_CLK_GATING;
+}
+static inline bool ufshcd_can_hibern8_during_gating(struct ufs_hba *hba)
+{
+       return hba->caps & UFSHCD_CAP_HIBERN8_WITH_CLK_GATING;
+}
+static inline int ufshcd_is_clkscaling_enabled(struct ufs_hba *hba)
+{
+       return hba->caps & UFSHCD_CAP_CLK_SCALING;
+}
+static inline bool ufshcd_can_autobkops_during_suspend(struct ufs_hba *hba)
+{
+       return hba->caps & UFSHCD_CAP_AUTO_BKOPS_SUSPEND;
+}
+
 #define ufshcd_writel(hba, val, reg)   \
        writel((val), (hba)->mmio_base + (reg))
 #define ufshcd_readl(hba, reg) \
        readl((hba)->mmio_base + (reg))
 
+/**
+ * ufshcd_rmwl - read modify write into a register
+ * @hba - per adapter instance
+ * @mask - mask to apply on read value
+ * @val - actual value to write
+ * @reg - register address
+ */
+static inline void ufshcd_rmwl(struct ufs_hba *hba, u32 mask, u32 val, u32 reg)
+{
+       u32 tmp;
+
+       tmp = ufshcd_readl(hba, reg);
+       tmp &= ~mask;
+       tmp |= (val & mask);
+       ufshcd_writel(hba, tmp, reg);
+}
+
 int ufshcd_alloc_host(struct device *, struct ufs_hba **);
 int ufshcd_init(struct ufs_hba * , void __iomem * , unsigned int);
 void ufshcd_remove(struct ufs_hba *);
@@ -341,11 +536,12 @@ static inline void check_upiu_size(void)
                GENERAL_UPIU_REQUEST_SIZE + QUERY_DESC_MAX_SIZE);
 }
 
-extern int ufshcd_suspend(struct ufs_hba *hba, pm_message_t state);
-extern int ufshcd_resume(struct ufs_hba *hba);
 extern int ufshcd_runtime_suspend(struct ufs_hba *hba);
 extern int ufshcd_runtime_resume(struct ufs_hba *hba);
 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_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,
@@ -397,4 +593,6 @@ static inline int ufshcd_dme_peer_get(struct ufs_hba *hba,
        return ufshcd_dme_get_attr(hba, attr_sel, mib_val, DME_PEER);
 }
 
+int ufshcd_hold(struct ufs_hba *hba, bool async);
+void ufshcd_release(struct ufs_hba *hba);
 #endif /* End of Header */