mmc: sdhci-msm: add PM QoS properties for IRQ and cpu group voting
Add the necessary device tree properties and parsing in the driver to support PM QoS voting for IRQ and CPU groups for CMDQ / legacy modes. Change-Id: I1a94978ca66823d2ce78ee230cf36b4ebb72e6d8 Signed-off-by: Gilad Broner <gbroner@codeaurora.org> [subhashj@codeaurora.org: fixed trivial merge conflicts] Signed-off-by: Subhash Jadavani <subhashj@codeaurora.org>
This commit is contained in:
parent
fd6903e1eb
commit
8a67fa5b17
4 changed files with 219 additions and 0 deletions
|
@ -45,6 +45,26 @@ Optional Properties:
|
||||||
the driver will construct one based on the card
|
the driver will construct one based on the card
|
||||||
supported max and min frequencies.
|
supported max and min frequencies.
|
||||||
The frequencies must be ordered from lowest to highest.
|
The frequencies must be ordered from lowest to highest.
|
||||||
|
- qcom,pm-qos-irq-type - the PM QoS request type to be used for IRQ voting.
|
||||||
|
Can be either "affine_cores" or "affine_irq". If not specified, will default
|
||||||
|
to "affine_cores". Use "affine_irq" setting in case an IRQ balancer is active,
|
||||||
|
and IRQ affinity changes during runtime.
|
||||||
|
- qcom,pm-qos-irq-cpu - specifies the CPU for which IRQ voting shall be done.
|
||||||
|
If "affine_cores" was specified for property 'qcom,pm-qos-irq-type'
|
||||||
|
then this property must be defined, and is not relevant otherwise.
|
||||||
|
- qcom,pm-qos-irq-latency - a tuple defining two latency values with which
|
||||||
|
PM QoS IRQ voting shall be done. The first value is the latecy to be used
|
||||||
|
when load is high (performance mode) and the second is for low loads
|
||||||
|
(power saving mode).
|
||||||
|
- qcom,pm-qos-cpu-groups - defines cpu groups mapping.
|
||||||
|
Each cell represnets a group, which is a cpu bitmask defining which cpus belong
|
||||||
|
to that group.
|
||||||
|
- qcom,pm-qos-<mode>-latency-us - where <mode> is either "cmdq" or "legacy".
|
||||||
|
An array of latency value tuples, each tuple corresponding to a cpu group in the order
|
||||||
|
defined in property 'qcom,pm-qos-cpu-groups'. The first value is the latecy to be used
|
||||||
|
when load is high (performance mode) and the second is for low loads
|
||||||
|
(power saving mode). These values will be used for cpu group voting for
|
||||||
|
command-queueing mode or legacy respectively.
|
||||||
|
|
||||||
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
|
In the following, <supply> can be vdd (flash core voltage) or vdd-io (I/O voltage).
|
||||||
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
|
- qcom,<supply>-always-on - specifies whether supply should be kept "on" always.
|
||||||
|
@ -122,6 +142,13 @@ Example:
|
||||||
<&msmgpio 36 0>, /* DATA2 */
|
<&msmgpio 36 0>, /* DATA2 */
|
||||||
<&msmgpio 35 0>; /* DATA3 */
|
<&msmgpio 35 0>; /* DATA3 */
|
||||||
qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
|
qcom,gpio-names = "CLK", "CMD", "DAT0", "DAT1", "DAT2", "DAT3";
|
||||||
|
|
||||||
|
qcom,pm-qos-irq-type = "affine_cores";
|
||||||
|
qcom,pm-qos-irq-cpu = <0>;
|
||||||
|
qcom,pm-qos-irq-latency = <500 100>;
|
||||||
|
qcom,pm-qos-cpu-groups = <0x03 0x0c>;
|
||||||
|
qcom,pm-qos-cmdq-latency-us = <50 100>, <50 100>;
|
||||||
|
qcom,pm-qos-legacy-latency-us = <50 100>, <50 100>;
|
||||||
};
|
};
|
||||||
|
|
||||||
sdhc_2: qcom,sdhc@f98a4900 {
|
sdhc_2: qcom,sdhc@f98a4900 {
|
||||||
|
@ -145,4 +172,7 @@ Example:
|
||||||
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
|
qcom,pad-pull-off = <0x0 0x3 0x3>; /* no-pull, pull-up, pull-up */
|
||||||
qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
|
qcom,pad-drv-on = <0x7 0x4 0x4>; /* 16mA, 10mA, 10mA */
|
||||||
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
|
qcom,pad-drv-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */
|
||||||
|
|
||||||
|
qcom,pm-qos-irq-type = "affine_irq";
|
||||||
|
qcom,pm-qos-irq-latency = <120 200>;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1424,6 +1424,169 @@ out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_pm_qos_parse_irq(struct device *dev,
|
||||||
|
struct sdhci_msm_pltfm_data *pdata)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
const char *str;
|
||||||
|
u32 cpu;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pdata->pm_qos_data.irq_valid = false;
|
||||||
|
pdata->pm_qos_data.irq_req_type = PM_QOS_REQ_AFFINE_CORES;
|
||||||
|
if (!of_property_read_string(np, "qcom,pm-qos-irq-type", &str) &&
|
||||||
|
!strcmp(str, "affine_irq")) {
|
||||||
|
pdata->pm_qos_data.irq_req_type = PM_QOS_REQ_AFFINE_IRQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* must specify cpu for "affine_cores" type */
|
||||||
|
if (pdata->pm_qos_data.irq_req_type == PM_QOS_REQ_AFFINE_CORES) {
|
||||||
|
pdata->pm_qos_data.irq_cpu = -1;
|
||||||
|
ret = of_property_read_u32(np, "qcom,pm-qos-irq-cpu", &cpu);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "%s: error %d reading irq cpu\n", __func__,
|
||||||
|
ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (cpu < 0 || cpu >= num_possible_cpus()) {
|
||||||
|
dev_err(dev, "%s: invalid irq cpu %d (NR_CPUS=%d)\n",
|
||||||
|
__func__, cpu, num_possible_cpus());
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pdata->pm_qos_data.irq_cpu = cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_count_u32_elems(np, "qcom,pm-qos-irq-latency") !=
|
||||||
|
SDHCI_POWER_POLICY_NUM) {
|
||||||
|
dev_err(dev, "%s: could not read %d values for 'qcom,pm-qos-irq-latency'\n",
|
||||||
|
__func__, SDHCI_POWER_POLICY_NUM);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < SDHCI_POWER_POLICY_NUM; i++)
|
||||||
|
of_property_read_u32_index(np, "qcom,pm-qos-irq-latency", i,
|
||||||
|
&pdata->pm_qos_data.irq_latency.latency[i]);
|
||||||
|
|
||||||
|
pdata->pm_qos_data.irq_valid = true;
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_pm_qos_parse_cpu_groups(struct device *dev,
|
||||||
|
struct sdhci_msm_pltfm_data *pdata)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
u32 mask;
|
||||||
|
int nr_groups;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Read cpu group mapping */
|
||||||
|
nr_groups = of_property_count_u32_elems(np, "qcom,pm-qos-cpu-groups");
|
||||||
|
if (nr_groups <= 0) {
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
pdata->pm_qos_data.cpu_group_map.nr_groups = nr_groups;
|
||||||
|
pdata->pm_qos_data.cpu_group_map.mask =
|
||||||
|
kcalloc(nr_groups, sizeof(cpumask_t), GFP_KERNEL);
|
||||||
|
if (!pdata->pm_qos_data.cpu_group_map.mask) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nr_groups; i++) {
|
||||||
|
of_property_read_u32_index(np, "qcom,pm-qos-cpu-groups",
|
||||||
|
i, &mask);
|
||||||
|
|
||||||
|
pdata->pm_qos_data.cpu_group_map.mask[i].bits[0] = mask;
|
||||||
|
if (!cpumask_subset(&pdata->pm_qos_data.cpu_group_map.mask[i],
|
||||||
|
cpu_possible_mask)) {
|
||||||
|
dev_err(dev, "%s: invalid mask 0x%x of cpu group #%d\n",
|
||||||
|
__func__, mask, i);
|
||||||
|
ret = -EINVAL;
|
||||||
|
goto free_res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
free_res:
|
||||||
|
kfree(pdata->pm_qos_data.cpu_group_map.mask);
|
||||||
|
out:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int sdhci_msm_pm_qos_parse_latency(struct device *dev, const char *name,
|
||||||
|
int nr_groups, struct sdhci_msm_pm_qos_latency **latency)
|
||||||
|
{
|
||||||
|
struct device_node *np = dev->of_node;
|
||||||
|
struct sdhci_msm_pm_qos_latency *values;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
int group;
|
||||||
|
int cfg;
|
||||||
|
|
||||||
|
ret = of_property_count_u32_elems(np, name);
|
||||||
|
if (ret > 0 && ret != SDHCI_POWER_POLICY_NUM * nr_groups) {
|
||||||
|
dev_err(dev, "%s: invalid number of values for property %s: expected=%d actual=%d\n",
|
||||||
|
__func__, name, SDHCI_POWER_POLICY_NUM * nr_groups,
|
||||||
|
ret);
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
values = kcalloc(nr_groups, sizeof(struct sdhci_msm_pm_qos_latency),
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!values)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (i = 0; i < SDHCI_POWER_POLICY_NUM * nr_groups; i++) {
|
||||||
|
group = i / SDHCI_POWER_POLICY_NUM;
|
||||||
|
cfg = i % SDHCI_POWER_POLICY_NUM;
|
||||||
|
of_property_read_u32_index(np, name, i,
|
||||||
|
&(values[group].latency[cfg]));
|
||||||
|
}
|
||||||
|
|
||||||
|
*latency = values;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdhci_msm_pm_qos_parse(struct device *dev,
|
||||||
|
struct sdhci_msm_pltfm_data *pdata)
|
||||||
|
{
|
||||||
|
if (sdhci_msm_pm_qos_parse_irq(dev, pdata))
|
||||||
|
dev_notice(dev, "%s: PM QoS voting for IRQ will be disabled\n",
|
||||||
|
__func__);
|
||||||
|
|
||||||
|
if (!sdhci_msm_pm_qos_parse_cpu_groups(dev, pdata)) {
|
||||||
|
pdata->pm_qos_data.cmdq_valid =
|
||||||
|
!sdhci_msm_pm_qos_parse_latency(dev,
|
||||||
|
"qcom,pm-qos-cmdq-latency-us",
|
||||||
|
pdata->pm_qos_data.cpu_group_map.nr_groups,
|
||||||
|
&pdata->pm_qos_data.cmdq_latency);
|
||||||
|
pdata->pm_qos_data.legacy_valid =
|
||||||
|
!sdhci_msm_pm_qos_parse_latency(dev,
|
||||||
|
"qcom,pm-qos-legacy-latency-us",
|
||||||
|
pdata->pm_qos_data.cpu_group_map.nr_groups,
|
||||||
|
&pdata->pm_qos_data.latency);
|
||||||
|
if (!pdata->pm_qos_data.cmdq_valid &&
|
||||||
|
!pdata->pm_qos_data.legacy_valid) {
|
||||||
|
/* clean-up previously allocated arrays */
|
||||||
|
kfree(pdata->pm_qos_data.latency);
|
||||||
|
kfree(pdata->pm_qos_data.cmdq_latency);
|
||||||
|
dev_err(dev, "%s: invalid PM QoS latency values. Voting for cpu group will be disabled\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dev_notice(dev, "%s: PM QoS voting for cpu group will be disabled\n",
|
||||||
|
__func__);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Parse platform data */
|
/* Parse platform data */
|
||||||
static
|
static
|
||||||
struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
|
struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
|
||||||
|
@ -1565,6 +1728,8 @@ struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev,
|
||||||
if (of_property_read_bool(np, "qcom,wakeup-on-idle"))
|
if (of_property_read_bool(np, "qcom,wakeup-on-idle"))
|
||||||
msm_host->mmc->wakeup_on_idle = true;
|
msm_host->mmc->wakeup_on_idle = true;
|
||||||
|
|
||||||
|
sdhci_msm_pm_qos_parse(dev, pdata);
|
||||||
|
|
||||||
return pdata;
|
return pdata;
|
||||||
out:
|
out:
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#define __SDHCI_MSM_H__
|
#define __SDHCI_MSM_H__
|
||||||
|
|
||||||
#include <linux/mmc/mmc.h>
|
#include <linux/mmc/mmc.h>
|
||||||
|
#include <linux/pm_qos.h>
|
||||||
#include "sdhci-pltfm.h"
|
#include "sdhci-pltfm.h"
|
||||||
|
|
||||||
/* This structure keeps information per regulator */
|
/* This structure keeps information per regulator */
|
||||||
|
@ -83,6 +84,27 @@ struct sdhci_msm_bus_voting_data {
|
||||||
unsigned int bw_vecs_size;
|
unsigned int bw_vecs_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_cpu_group_map {
|
||||||
|
int nr_groups;
|
||||||
|
cpumask_t *mask;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pm_qos_latency {
|
||||||
|
s32 latency[SDHCI_POWER_POLICY_NUM];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sdhci_msm_pm_qos_data {
|
||||||
|
struct sdhci_msm_cpu_group_map cpu_group_map;
|
||||||
|
enum pm_qos_req_type irq_req_type;
|
||||||
|
int irq_cpu;
|
||||||
|
struct sdhci_msm_pm_qos_latency irq_latency;
|
||||||
|
struct sdhci_msm_pm_qos_latency *cmdq_latency;
|
||||||
|
struct sdhci_msm_pm_qos_latency *latency;
|
||||||
|
bool irq_valid;
|
||||||
|
bool cmdq_valid;
|
||||||
|
bool legacy_valid;
|
||||||
|
};
|
||||||
|
|
||||||
struct sdhci_msm_pltfm_data {
|
struct sdhci_msm_pltfm_data {
|
||||||
/* Supported UHS-I Modes */
|
/* Supported UHS-I Modes */
|
||||||
u32 caps;
|
u32 caps;
|
||||||
|
@ -106,6 +128,7 @@ struct sdhci_msm_pltfm_data {
|
||||||
unsigned char sup_ice_clk_cnt;
|
unsigned char sup_ice_clk_cnt;
|
||||||
u32 ice_clk_max;
|
u32 ice_clk_max;
|
||||||
u32 ice_clk_min;
|
u32 ice_clk_min;
|
||||||
|
struct sdhci_msm_pm_qos_data pm_qos_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_msm_bus_vote {
|
struct sdhci_msm_bus_vote {
|
||||||
|
|
|
@ -328,6 +328,7 @@ enum sdhci_cookie {
|
||||||
enum sdhci_power_policy {
|
enum sdhci_power_policy {
|
||||||
SDHCI_PERFORMANCE_MODE,
|
SDHCI_PERFORMANCE_MODE,
|
||||||
SDHCI_POWER_SAVE_MODE,
|
SDHCI_POWER_SAVE_MODE,
|
||||||
|
SDHCI_POWER_POLICY_NUM /* Always keep this one last */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sdhci_host {
|
struct sdhci_host {
|
||||||
|
|
Loading…
Add table
Reference in a new issue