Add support to parse clk scaling dt entries in mmc
driver.

Signed-off-by: Sujit Reddy Thumma <sthu...@codeaurora.org>
Signed-off-by: Talel Shenhar <tat...@codeaurora.org>
Signed-off-by: Sahitya Tummala <stumm...@codeaurora.org>
Signed-off-by: Asutosh Das <asuto...@codeaurora.org>
Signed-off-by: Ritesh Harjani <rite...@codeaurora.org>
Signed-off-by: Veerabhadrarao Badiganti <vbadi...@codeaurora.org>
Signed-off-by: Bao D. Nguyen <nguy...@codeaurora.org>
Signed-off-by: Can Guo <c...@codeaurora.org>
Signed-off-by: Sayali Lokhande <saya...@codeaurora.org>
Co-Developed-by: Ram Prakash Gupta <rampr...@codeaurora.org>
Signed-off-by: Ram Prakash Gupta <rampr...@codeaurora.org>
---
 drivers/mmc/core/host.c  | 63 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/card.h |  7 +++++
 include/linux/mmc/host.h | 66 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 136 insertions(+)

diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
index 105b7a7..672f2d6 100644
--- a/drivers/mmc/core/host.c
+++ b/drivers/mmc/core/host.c
@@ -161,6 +161,44 @@ static void mmc_retune_timer(struct timer_list *t)
        mmc_retune_needed(host);
 }
 
+static int mmc_dt_get_array(struct device *dev, const char *prop_name,
+                               u32 **out, int *len, u32 size)
+{
+       int ret = 0;
+       struct device_node *np = dev->of_node;
+       size_t sz;
+       u32 *arr = NULL;
+
+       if (!of_get_property(np, prop_name, len)) {
+               ret = -EINVAL;
+               goto out;
+       }
+       sz = *len = *len / sizeof(*arr);
+       if (sz <= 0 || (size > 0 && (sz > size))) {
+               dev_err(dev, "%s invalid size\n", prop_name);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       arr = devm_kcalloc(dev, sz, sizeof(*arr), GFP_KERNEL);
+       if (!arr) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       ret = of_property_read_u32_array(np, prop_name, arr, sz);
+       if (ret < 0) {
+               dev_err(dev, "%s failed reading array %d\n", prop_name, ret);
+               goto out;
+       }
+       *out = arr;
+out:
+       if (ret)
+               *len = 0;
+       return ret;
+
+}
+
 /**
  *     mmc_of_parse() - parse host's device-tree node
  *     @host: host whose node should be parsed.
@@ -173,10 +211,12 @@ static void mmc_retune_timer(struct timer_list *t)
 int mmc_of_parse(struct mmc_host *host)
 {
        struct device *dev = host->parent;
+       struct device_node *np = dev->of_node;
        u32 bus_width, drv_type, cd_debounce_delay_ms;
        int ret;
        bool cd_cap_invert, cd_gpio_invert = false;
        bool ro_cap_invert, ro_gpio_invert = false;
+       const char *lower_bus_speed = NULL;
 
        if (!dev || !dev_fwnode(dev))
                return 0;
@@ -340,6 +380,29 @@ int mmc_of_parse(struct mmc_host *host)
        device_property_read_u32(dev, "post-power-on-delay-ms",
                                 &host->ios.power_delay_ms);
 
+       if (mmc_dt_get_array(dev, "devfreq,freq-table",
+                               &host->clk_scaling.pltfm_freq_table,
+                               &host->clk_scaling.pltfm_freq_table_sz, 0))
+               pr_debug("%s: no clock scaling frequencies were supplied\n",
+                               dev_name(dev));
+       else if (!host->clk_scaling.pltfm_freq_table ||
+                       host->clk_scaling.pltfm_freq_table_sz)
+               dev_info(dev, "bad dts clock scaling frequencies\n");
+
+       /*
+        * Few hosts can support DDR52 mode at the same lower
+        * system voltage corner as high-speed mode. In such
+        * cases, it is always better to put it in DDR
+        * mode which will improve the performance
+        * without any power impact.
+        */
+       if (!of_property_read_string(np, "scaling-lower-bus-speed-mode",
+                       &lower_bus_speed)) {
+               if (!strncmp(lower_bus_speed, "DDR52", strlen(lower_bus_speed)))
+                       host->clk_scaling.lower_bus_speed_mode |=
+                               MMC_SCALING_LOWER_DDR52_MODE;
+       }
+
        return mmc_pwrseq_alloc(host);
 }
 
diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h
index 9b6336a..8b577a1 100644
--- a/include/linux/mmc/card.h
+++ b/include/linux/mmc/card.h
@@ -244,6 +244,12 @@ struct mmc_card {
        struct mmc_host         *host;          /* the host this device belongs 
to */
        struct device           dev;            /* the device */
        u32                     ocr;            /* the current OCR setting */
+       unsigned long           clk_scaling_lowest;     /* lowest scaleable
+                                                        * frequency
+                                                        */
+       unsigned long           clk_scaling_highest;    /* highest scaleable
+                                                        * frequency
+                                                        */
        unsigned int            rca;            /* relative card address of 
device */
        unsigned int            type;           /* card type */
 #define MMC_TYPE_MMC           0               /* MMC card */
@@ -306,6 +312,7 @@ struct mmc_card {
        struct dentry           *debugfs_root;
        struct mmc_part part[MMC_NUM_PHY_PARTITION]; /* physical partitions */
        unsigned int    nr_parts;
+       unsigned int            part_curr;
 
        unsigned int            bouncesz;       /* Bounce buffer size */
        struct workqueue_struct *complete_wq;   /* Private workqueue */
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index ba70338..bfb47d6 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -9,6 +9,7 @@
 
 #include <linux/sched.h>
 #include <linux/device.h>
+#include <linux/devfreq.h>
 #include <linux/fault-inject.h>
 
 #include <linux/mmc/core.h>
@@ -268,9 +269,72 @@ struct mmc_ctx {
        struct task_struct *task;
 };
 
+enum dev_state {
+       DEV_SUSPENDING = 1,
+       DEV_SUSPENDED,
+       DEV_RESUMED,
+};
+
+enum mmc_load {
+       MMC_LOAD_HIGH,
+       MMC_LOAD_LOW,
+};
+
+/**
+ * struct mmc_devfeq_clk_scaling - main context for MMC clock scaling logic
+ *
+ * @lock: spinlock to protect statistics
+ * @devfreq: struct that represent mmc-host as a client for devfreq
+ * @devfreq_profile: MMC device profile, mostly polling interval and callbacks
+ * @ondemand_gov_data: struct supplied to ondemmand governor (thresholds)
+ * @state: load state, can be HIGH or LOW. used to notify mmc_host_ops callback
+ * @start_busy: timestamped armed once a data request is started
+ * @measure_interval_start: timestamped armed once a measure interval started
+ * @devfreq_abort: flag to sync between different contexts relevant to devfreq
+ * @skip_clk_scale_freq_update: flag that enable/disable frequency change
+ * @freq_table_sz: table size of frequencies supplied to devfreq
+ * @freq_table: frequencies table supplied to devfreq
+ * @curr_freq: current frequency
+ * @polling_delay_ms: polling interval for status collection used by devfreq
+ * @upthreshold: up-threshold supplied to ondemand governor
+ * @downthreshold: down-threshold supplied to ondemand governor
+ * @need_freq_change: flag indicating if a frequency change is required
+ * @is_busy_started: flag indicating if a request is handled by the HW
+ * @enable: flag indicating if the clock scaling logic is enabled for this host
+ * @is_suspended: to make devfreq request queued when mmc is suspened
+ */
+struct mmc_devfeq_clk_scaling {
+       spinlock_t      lock;
+       struct          devfreq *devfreq;
+       struct          devfreq_dev_profile devfreq_profile;
+       struct          devfreq_simple_ondemand_data ondemand_gov_data;
+       enum mmc_load   state;
+       ktime_t         start_busy;
+       ktime_t         measure_interval_start;
+       atomic_t        devfreq_abort;
+       bool            skip_clk_scale_freq_update;
+       int             freq_table_sz;
+       int             pltfm_freq_table_sz;
+       u32             *freq_table;
+       u32             *pltfm_freq_table;
+       unsigned long   total_busy_time_us;
+       unsigned long   target_freq;
+       unsigned long   curr_freq;
+       unsigned long   polling_delay_ms;
+       unsigned int    upthreshold;
+       unsigned int    downthreshold;
+       unsigned int    lower_bus_speed_mode;
+#define MMC_SCALING_LOWER_DDR52_MODE   1
+       bool            need_freq_change;
+       bool            is_busy_started;
+       bool            enable;
+       bool            is_suspended;
+};
+
 struct mmc_host {
        struct device           *parent;
        struct device           class_dev;
+       struct mmc_devfeq_clk_scaling   clk_scaling;
        int                     index;
        const struct mmc_host_ops *ops;
        struct mmc_pwrseq       *pwrseq;
@@ -369,6 +433,7 @@ struct mmc_host {
 #define MMC_CAP2_CQE_DCMD      (1 << 24)       /* CQE can issue a direct 
command */
 #define MMC_CAP2_AVOID_3_3V    (1 << 25)       /* Host must negotiate down 
from 3.3V */
 #define MMC_CAP2_MERGE_CAPABLE (1 << 26)       /* Host can merge a segment 
over the segment size */
+#define MMC_CAP2_CLK_SCALE      (1 << 27)       /* Allow dynamic clk scaling */
 
        int                     fixed_drv_type; /* fixed driver type for 
non-removable media */
 
@@ -462,6 +527,7 @@ struct mmc_host {
        bool                    cqe_enabled;
        bool                    cqe_on;
 
+       atomic_t active_reqs;
        unsigned long           private[0] ____cacheline_aligned;
 };
 
-- 
1.9.1

Reply via email to