Loading Documentation/devicetree/bindings/mmc/sdhci-msm.txt +30 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,26 @@ Optional Properties: the driver will construct one based on the card supported max and min frequencies. 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). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. Loading Loading @@ -122,6 +142,13 @@ Example: <&msmgpio 36 0>, /* DATA2 */ <&msmgpio 35 0>; /* DATA3 */ 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 { Loading @@ -145,4 +172,7 @@ Example: 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-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ qcom,pm-qos-irq-type = "affine_irq"; qcom,pm-qos-irq-latency = <120 200>; }; drivers/mmc/host/sdhci-msm.c +165 −0 Original line number Diff line number Diff line Loading @@ -1424,6 +1424,169 @@ out: 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 */ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, Loading Loading @@ -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")) msm_host->mmc->wakeup_on_idle = true; sdhci_msm_pm_qos_parse(dev, pdata); return pdata; out: return NULL; Loading drivers/mmc/host/sdhci-msm.h +23 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #define __SDHCI_MSM_H__ #include <linux/mmc/mmc.h> #include <linux/pm_qos.h> #include "sdhci-pltfm.h" /* This structure keeps information per regulator */ Loading Loading @@ -83,6 +84,27 @@ struct sdhci_msm_bus_voting_data { 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 { /* Supported UHS-I Modes */ u32 caps; Loading @@ -106,6 +128,7 @@ struct sdhci_msm_pltfm_data { unsigned char sup_ice_clk_cnt; u32 ice_clk_max; u32 ice_clk_min; struct sdhci_msm_pm_qos_data pm_qos_data; }; struct sdhci_msm_bus_vote { Loading include/linux/mmc/sdhci.h +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ struct sdhci_next { enum sdhci_power_policy { SDHCI_PERFORMANCE_MODE, SDHCI_POWER_SAVE_MODE, SDHCI_POWER_POLICY_NUM /* Always keep this one last */ }; struct sdhci_host { Loading Loading
Documentation/devicetree/bindings/mmc/sdhci-msm.txt +30 −0 Original line number Diff line number Diff line Loading @@ -45,6 +45,26 @@ Optional Properties: the driver will construct one based on the card supported max and min frequencies. 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). - qcom,<supply>-always-on - specifies whether supply should be kept "on" always. Loading Loading @@ -122,6 +142,13 @@ Example: <&msmgpio 36 0>, /* DATA2 */ <&msmgpio 35 0>; /* DATA3 */ 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 { Loading @@ -145,4 +172,7 @@ Example: 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-off = <0x0 0x0 0x0>; /* 2mA, 2mA, 2mA */ qcom,pm-qos-irq-type = "affine_irq"; qcom,pm-qos-irq-latency = <120 200>; };
drivers/mmc/host/sdhci-msm.c +165 −0 Original line number Diff line number Diff line Loading @@ -1424,6 +1424,169 @@ out: 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 */ static struct sdhci_msm_pltfm_data *sdhci_msm_populate_pdata(struct device *dev, Loading Loading @@ -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")) msm_host->mmc->wakeup_on_idle = true; sdhci_msm_pm_qos_parse(dev, pdata); return pdata; out: return NULL; Loading
drivers/mmc/host/sdhci-msm.h +23 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ #define __SDHCI_MSM_H__ #include <linux/mmc/mmc.h> #include <linux/pm_qos.h> #include "sdhci-pltfm.h" /* This structure keeps information per regulator */ Loading Loading @@ -83,6 +84,27 @@ struct sdhci_msm_bus_voting_data { 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 { /* Supported UHS-I Modes */ u32 caps; Loading @@ -106,6 +128,7 @@ struct sdhci_msm_pltfm_data { unsigned char sup_ice_clk_cnt; u32 ice_clk_max; u32 ice_clk_min; struct sdhci_msm_pm_qos_data pm_qos_data; }; struct sdhci_msm_bus_vote { Loading
include/linux/mmc/sdhci.h +1 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ struct sdhci_next { enum sdhci_power_policy { SDHCI_PERFORMANCE_MODE, SDHCI_POWER_SAVE_MODE, SDHCI_POWER_POLICY_NUM /* Always keep this one last */ }; struct sdhci_host { Loading