Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1671421 > unrolled thread
| Started by | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| First post | 2017-06-21 09:20 +0200 |
| Last post | 2017-06-21 09:20 +0200 |
| Articles | 6 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH V8 0/6] PM / Domains: Power domain performance states Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
[NOT-FOR-MERGE V8 6/6] remoteproc: qcom: q6v5: Vote for proxy powerdomain performance state Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
[NOT-FOR-MERGE V8 4/6] soc: qcom: rpmpd: Add support for get/set performance state Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
[NOT-FOR-MERGE V8 3/6] soc: qcom: rpmpd: Add a powerdomain driver to model cx/mx powerdomains Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
[PATCH V8 1/6] PM / Domains: Add support to select performance-state of domains Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
[PATCH V8 2/6] PM / OPP: Support updating performance state of device's power domains Viresh Kumar <viresh.kumar@linaro.org> - 2017-06-21 09:20 +0200
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [PATCH V8 0/6] PM / Domains: Power domain performance states |
| Message-ID | <tUI5I-31b-11@gated-at.bofh.it> |
Hi,
Some platforms have the capability to configure the performance state of
their power domains. The process of configuring the performance state is
pretty much platform dependent and we may need to work with a wide range
of configurables. For some platforms, like Qcom, it can be a positive
integer value alone, while in other cases it can be voltage levels, etc.
The power-domain framework until now was only designed for the idle
state management of the device and this needs to change in order to
reuse the power-domain framework for active state management of the
devices.
The first patch updates the genpd framework to supply new APIs to
support active state management and the second patch uses them from the
OPP core.
Rest of the patches [3-6/6] are included just to show how user drivers
would end up using the new APIs and these patches aren't there to get
merged and are marked clearly like that.
The ideal way is still to get the relation between device and domain
states via the DT instead of platform code, but that can be done
incrementally later once we have some users for it upstream.
This is currently tested by:
- me by hacking the kernel a bit with virtual power-domains for the dual
A15 exynos platform. I have also tested the complex cases where the
device's parent power domain doesn't have set_performance_state()
callback set, but parents of that domains have it. Lockdep configs
were enabled for these tests.
- Rajendra Nayak, on msm8996 platform (Qcom) with MMC controller.
Thanks Rajendra for helping me testing this out.
Pushed here as well:
https://git.linaro.org/people/viresh.kumar/linux.git/log/?h=opp/genpd-performance-state
Rebased on: 4.12-rc6 + some OPP core changes [1] and [2].
V7->V8:
- Ulf helped a lot in reviewing V7 and pointed out couple of issues,
specially in locking while dealing with a hierarchy of power domains.
- All those locking issues are sorted out now, even for the complex
cases.
- genpd_lookup_dev() is used in pm_genpd_has_performance_state() to make
sure we have a valid genpd available for the device.
- Validation of performance state callbacks isn't done anymore in
pm_genpd_init() as it gets called very early and the binding of
subdomains to their parent domains happens later. This is handled in
pm_genpd_has_performance_state() now, which is called from user
drivers.
- User driver changes (not to be merged) are included for the first time
here, to demonstrate how changes would look finally.
V6->V7:
- Almost a rewrite, only two patches against 9 in earlier version.
- No bindings updated now and domain's performance state aren't passed
via DT for now (until we know how users are going to use it).
- We also skipped the QoS framework completely and new APIs are provided
directly by genpd.
V5->V6:
- Use freq/voltage in OPP table as it is for power domain and don't
create "domain-performance-level" property
- Create new "power-domain-opp" property for the devices.
- Take care of domain providers that provide multiple domains and extend
"operating-points-v2" property to contain a list of phandles
- Update code according to those bindings.
V4->V5:
- Only 3 patches were resent and 2 of them are Acked from Ulf.
V3->V4:
- Use OPP table for genpd devices as well.
- Add struct device to genpd, in order to reuse OPP infrastructure.
- Based over: https://marc.info/?l=linux-kernel&m=148972988002317&w=2
- Fixed examples in DT document to have voltage in target,min,max order.
V2->V3:
- Based over latest pm/linux-next
- Bindings and code are merged together
- Lots of updates in bindings
- the performance-states node is present within the power-domain now,
instead of its phandle.
- performance-level property is replaced by "reg".
- domain-performance-state property of the consumers contain an
integer value now instead of phandle.
- Lots of updates to the code as well
- Patch "PM / QOS: Add default case to the switch" is merged with
other patches and the code is changed a bit as well.
- Don't pass 'type' to dev_pm_qos_add_notifier(), rather handle all
notifiers with a single list. A new patch is added for that.
- The OPP framework patch can be applied now and has proper SoB from
me.
- Dropped "PM / domain: Save/restore performance state at runtime
suspend/resume".
- Drop all WARN().
- Tested-by Rajendra nayak.
V1->V2:
- Based over latest pm/linux-next
- It is mostly a resend of what is sent earlier as this series hasn't
got any reviews so far and Rafael suggested that its better I resend
it.
- Only the 4/6 patch got an update, which was shared earlier as reply to
V1 as well. It has got several fixes for taking care of power domain
hierarchy, etc.
--
viresh
[1] https://marc.info/?l=linux-kernel&m=149499607030364&w=2
[2] https://marc.info/?l=linux-kernel&m=149795317123259&w=2
Rajendra Nayak (4):
soc: qcom: rpmpd: Add a powerdomain driver to model cx/mx powerdomains
soc: qcom: rpmpd: Add support for get/set performance state
mmc: sdhci-msm: Adapt the driver to use OPPs to set clocks/performance
state
remoteproc: qcom: q6v5: Vote for proxy powerdomain performance state
Viresh Kumar (2):
PM / Domains: Add support to select performance-state of domains
PM / OPP: Support updating performance state of device's power domains
.../devicetree/bindings/power/qcom,rpmpd.txt | 10 +
arch/arm64/boot/dts/qcom/msm8996.dtsi | 39 ++
drivers/base/power/domain.c | 223 +++++++++++
drivers/base/power/opp/core.c | 48 ++-
drivers/base/power/opp/opp.h | 2 +
drivers/clk/qcom/gcc-msm8996.c | 8 +-
drivers/mmc/host/sdhci-msm.c | 39 +-
drivers/remoteproc/qcom_q6v5_pil.c | 20 +-
drivers/soc/qcom/Kconfig | 9 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/rpmpd.c | 412 +++++++++++++++++++++
include/linux/pm_domain.h | 22 ++
12 files changed, 811 insertions(+), 22 deletions(-)
create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmpd.txt
create mode 100644 drivers/soc/qcom/rpmpd.c
--
2.13.0.71.gd7076ec9c9cb
[toc] | [next] | [standalone]
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [NOT-FOR-MERGE V8 6/6] remoteproc: qcom: q6v5: Vote for proxy powerdomain performance state |
| Message-ID | <tUI5J-31b-19@gated-at.bofh.it> |
| In reply to | #1671421 |
From: Rajendra Nayak <rnayak@codeaurora.org>
THIS IS TEST CODE, SHOULDN'T BE MERGED.
This patch just demonstrates the usage of pm_genpd_update_performance_state()
api in cases where users need to set performance state of a powerdomain without
having to do it via the OPP framework.
q6v5 remoteproc driver needs to proxy vote for performance states of multiple
powerdomains (but we currently only demonstate how it can be done for
one powerdomain, as there is no way to associate multiple powerdomains
to a device at this time) while it loads the firmware, and then releases
the vote, once the firmware is up and can vote for itself.
This is not a functional patch since rpmpd driver only supports msm8996
and there is no msm8996 support in the q6v5 remoteproc driver at this
point in mainline.
This patch is not tested as well.
NOT-signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
NOT-signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/remoteproc/qcom_q6v5_pil.c | 20 +++++++++++++-------
drivers/soc/qcom/rpmpd.c | 5 +++++
2 files changed, 18 insertions(+), 7 deletions(-)
diff --git a/drivers/remoteproc/qcom_q6v5_pil.c b/drivers/remoteproc/qcom_q6v5_pil.c
index 8fd697a3cf8f..47c9dad98ee8 100644
--- a/drivers/remoteproc/qcom_q6v5_pil.c
+++ b/drivers/remoteproc/qcom_q6v5_pil.c
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <linux/pm_domain.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/mfd/syscon.h>
@@ -152,6 +153,8 @@ struct q6v5 {
void *mpss_region;
size_t mpss_size;
+ bool has_perf_state;
+
struct qcom_rproc_subdev smd_subdev;
};
@@ -603,11 +606,12 @@ static int q6v5_start(struct rproc *rproc)
struct q6v5 *qproc = (struct q6v5 *)rproc->priv;
int ret;
- ret = q6v5_regulator_enable(qproc, qproc->proxy_regs,
- qproc->proxy_reg_count);
- if (ret) {
- dev_err(qproc->dev, "failed to enable proxy supplies\n");
- return ret;
+ if (qproc->has_perf_state) {
+ ret = pm_genpd_update_performance_state(qproc->dev, INT_MAX);
+ if (ret) {
+ dev_err(qproc->dev, "Failed to set performance state.\n");
+ return ret;
+ }
}
ret = q6v5_clk_enable(qproc->dev, qproc->proxy_clks,
@@ -671,8 +675,8 @@ static int q6v5_start(struct rproc *rproc)
q6v5_clk_disable(qproc->dev, qproc->proxy_clks,
qproc->proxy_clk_count);
- q6v5_regulator_disable(qproc, qproc->proxy_regs,
- qproc->proxy_reg_count);
+ if (qproc->has_perf_state)
+ pm_genpd_update_performance_state(qproc->dev, 0);
return 0;
@@ -1043,6 +1047,8 @@ static int q6v5_probe(struct platform_device *pdev)
if (ret)
goto free_rproc;
+ qproc->has_perf_state = pm_genpd_has_performance_state(&qproc->dev);
+
return 0;
free_rproc:
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c
index 7ef81429c5c5..b1af1442ebe9 100644
--- a/drivers/soc/qcom/rpmpd.c
+++ b/drivers/soc/qcom/rpmpd.c
@@ -145,6 +145,7 @@ enum msm8996_devices {
UFS,
PCIE,
USB3,
+ Q6V5_PIL,
};
static struct rpmpd_freq_map msm8996_rpmpd_freq_map[] = {
@@ -171,6 +172,10 @@ static struct rpmpd_freq_map msm8996_rpmpd_freq_map[] = {
.freq[LOW] = 120000000,
.freq[NOMINAL] = 150000000,
},
+ [Q6V5_PIL] = {
+ .pd = &msm8996_vddcx,
+ .freq[HIGH] = INT_MAX,
+ },
};
static const struct of_device_id rpmpd_performance_table[] = {
--
2.13.0.71.gd7076ec9c9cb
[toc] | [prev] | [next] | [standalone]
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [NOT-FOR-MERGE V8 4/6] soc: qcom: rpmpd: Add support for get/set performance state |
| Message-ID | <tUI5J-31b-35@gated-at.bofh.it> |
| In reply to | #1671421 |
From: Rajendra Nayak <rnayak@codeaurora.org>
THIS IS TEST CODE, SHOULDN'T BE MERGED.
With genpd now expecting powerdomain drivers supporting performance
state to support get/set performance state callbacks, add support for it
in the rpmpd driver.
NOT-signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
NOT-signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/soc/qcom/rpmpd.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c
index d34d9c363815..7ef81429c5c5 100644
--- a/drivers/soc/qcom/rpmpd.c
+++ b/drivers/soc/qcom/rpmpd.c
@@ -101,6 +101,20 @@ struct rpmpd_desc {
size_t num_pds;
};
+enum rpmpd_levels {
+ NONE,
+ LOWER, /* SVS2 */
+ LOW, /* SVS */
+ NOMINAL, /* NOMINAL */
+ HIGH, /* Turbo */
+ MAX_LEVEL,
+};
+
+struct rpmpd_freq_map {
+ struct rpmpd *pd;
+ unsigned long freq[MAX_LEVEL];
+};
+
static DEFINE_MUTEX(rpmpd_lock);
/* msm8996 RPM powerdomains */
@@ -126,6 +140,47 @@ static const struct rpmpd_desc msm8996_desc = {
.num_pds = ARRAY_SIZE(msm8996_rpmpds),
};
+enum msm8996_devices {
+ SDHCI,
+ UFS,
+ PCIE,
+ USB3,
+};
+
+static struct rpmpd_freq_map msm8996_rpmpd_freq_map[] = {
+ [SDHCI] = {
+ .pd = &msm8996_vddcx,
+ .freq[LOWER] = 19200000,
+ .freq[LOW] = 200000000,
+ .freq[NOMINAL] = 400000000,
+ },
+ [UFS] = {
+ .pd = &msm8996_vddcx,
+ .freq[LOWER] = 19200000,
+ .freq[LOW] = 100000000,
+ .freq[NOMINAL] = 200000000,
+ .freq[HIGH] = 240000000,
+ },
+ [PCIE] = {
+ .pd = &msm8996_vddcx,
+ .freq[LOWER] = 1011000,
+ },
+ [USB3] = {
+ .pd = &msm8996_vddcx,
+ .freq[LOWER] = 60000000,
+ .freq[LOW] = 120000000,
+ .freq[NOMINAL] = 150000000,
+ },
+};
+
+static const struct of_device_id rpmpd_performance_table[] = {
+ { .compatible = "qcom,sdhci-msm-v4", .data = (void *)SDHCI },
+ { .compatible = "qcom,ufshc", .data = (void *)UFS },
+ { .compatible = "qcom,pcie-msm8996", .data = (void *)PCIE },
+ { .compatible = "qcom,dwc3", .data = (void *)USB3 },
+ { }
+};
+
static const struct of_device_id rpmpd_match_table[] = {
{ .compatible = "qcom,rpmpd-msm8996", .data = &msm8996_desc },
{ }
@@ -230,6 +285,49 @@ static int rpmpd_power_off(struct generic_pm_domain *domain)
return ret;
}
+static int rpmpd_set_performance(struct generic_pm_domain *domain,
+ unsigned int state)
+{
+ int ret = 0;
+ struct rpmpd *pd = domain_to_rpmpd(domain);
+
+ mutex_lock(&rpmpd_lock);
+
+ pd->corner = state;
+
+ if (!pd->enabled && (pd->key != KEY_FLOOR_CORNER))
+ goto out;
+
+ ret = rpmpd_aggregate_corner(pd);
+
+out:
+ mutex_unlock(&rpmpd_lock);
+
+ return ret;
+}
+
+
+static int rpmpd_get_performance(struct device *dev, unsigned long rate)
+{
+ int i;
+ unsigned long index;
+ const struct of_device_id *id;
+
+ if (!rate)
+ return 0;
+
+ id = of_match_device(rpmpd_performance_table, dev);
+ if (!id)
+ return -EINVAL;
+
+ index = (unsigned long)id->data;
+ for (i = 0; i < MAX_LEVEL; i++)
+ if (msm8996_rpmpd_freq_map[index].freq[i] >= rate)
+ return i;
+
+ return MAX_LEVEL;
+}
+
static int rpmpd_probe(struct platform_device *pdev)
{
int i;
@@ -267,6 +365,8 @@ static int rpmpd_probe(struct platform_device *pdev)
rpmpds[i]->rpm = rpm;
rpmpds[i]->pd.power_off = rpmpd_power_off;
rpmpds[i]->pd.power_on = rpmpd_power_on;
+ rpmpds[i]->pd.set_performance_state = rpmpd_set_performance;
+ rpmpds[i]->pd.get_performance_state = rpmpd_get_performance;
pm_genpd_init(&rpmpds[i]->pd, NULL, true);
data->domains[i] = &rpmpds[i]->pd;
--
2.13.0.71.gd7076ec9c9cb
[toc] | [prev] | [next] | [standalone]
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [NOT-FOR-MERGE V8 3/6] soc: qcom: rpmpd: Add a powerdomain driver to model cx/mx powerdomains |
| Message-ID | <tUI5J-31b-33@gated-at.bofh.it> |
| In reply to | #1671421 |
From: Rajendra Nayak <rnayak@codeaurora.org>
THIS IS TEST CODE, SHOULDN'T BE MERGED.
The cx/mx powerdomains just pass the performance state set by the
consumers to the RPM (Remote Power manager) which then takes care
of setting the appropriate voltage on the corresponding rails to
meet the performance needs.
Add data for all powerdomains on msm8996.
NOT-signed-off-by: Rajendra Nayak <rnayak@codeaurora.org>
NOT-signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
.../devicetree/bindings/power/qcom,rpmpd.txt | 10 +
arch/arm64/boot/dts/qcom/msm8996.dtsi | 5 +
drivers/soc/qcom/Kconfig | 9 +
drivers/soc/qcom/Makefile | 1 +
drivers/soc/qcom/rpmpd.c | 307 +++++++++++++++++++++
5 files changed, 332 insertions(+)
create mode 100644 Documentation/devicetree/bindings/power/qcom,rpmpd.txt
create mode 100644 drivers/soc/qcom/rpmpd.c
diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.txt b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt
new file mode 100644
index 000000000000..8b48ce57a563
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.txt
@@ -0,0 +1,10 @@
+Qualcomm RPM Powerdomains
+
+* For RPM powerdomains, we communicate a performance state to RPM
+which then translates it into a corresponding voltage on a rail
+
+Required Properties:
+ - compatible: Should be one of the following
+ * qcom,rpmpd-msm8996: RPM Powerdomain for the msm8996 family of SoC
+ - power-domain-cells: number of cells in power domain specifier
+ must be 1.
diff --git a/arch/arm64/boot/dts/qcom/msm8996.dtsi b/arch/arm64/boot/dts/qcom/msm8996.dtsi
index 9bc9c857a000..1fec06285f38 100644
--- a/arch/arm64/boot/dts/qcom/msm8996.dtsi
+++ b/arch/arm64/boot/dts/qcom/msm8996.dtsi
@@ -412,6 +412,11 @@
status = "disabled";
};
+ rpmpd: qcom,rpmpd {
+ compatible = "qcom,rpmpd-msm8996", "qcom,rpmpd";
+ #power-domain-cells = <1>;
+ };
+
sdhc2: sdhci@74a4900 {
status = "disabled";
compatible = "qcom,sdhci-msm-v4";
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 9fca977ef18d..3892148be3c3 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -24,6 +24,15 @@ config QCOM_PM
modes. It interface with various system drivers to put the cores in
low power modes.
+config QCOM_RPMPD
+ tristate "Qualcomm RPM Powerdomain driver"
+ depends on MFD_QCOM_RPM && QCOM_SMD_RPM
+ help
+ QCOM RPM powerdomain driver to support powerdomain with
+ performance states. The driver communicates a performance state
+ value to RPM which then translates it into corresponding voltage
+ for the voltage rail.
+
config QCOM_SMEM
tristate "Qualcomm Shared Memory Manager (SMEM)"
depends on ARCH_QCOM
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 414f0de274fa..f7c3edfa6de8 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o
obj-$(CONFIG_QCOM_SMP2P) += smp2p.o
obj-$(CONFIG_QCOM_SMSM) += smsm.o
obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
+obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o
diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c
new file mode 100644
index 000000000000..d34d9c363815
--- /dev/null
+++ b/drivers/soc/qcom/rpmpd.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm_domain.h>
+#include <linux/mfd/qcom_rpm.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/soc/qcom/smd-rpm.h>
+
+#include <dt-bindings/mfd/qcom-rpm.h>
+
+#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd)
+
+/* Resource types */
+#define RPMPD_SMPA 0x61706d73
+#define RPMPD_LDOA 0x616f646c
+
+/* Operation Keys */
+#define KEY_CORNER 0x6e726f63 /* corn */
+#define KEY_ENABLE 0x6e657773 /* swen */
+#define KEY_FLOOR_CORNER 0x636676 /* vfc */
+
+#define DEFINE_RPMPD_CORN_SMPA(_platform, _name, _active, r_id) \
+ static struct rpmpd _platform##_##_active; \
+ static struct rpmpd _platform##_##_name = { \
+ .pd = { .name = #_name, }, \
+ .peer = &_platform##_##_active, \
+ .res_type = RPMPD_SMPA, \
+ .res_id = r_id, \
+ .key = KEY_CORNER, \
+ }; \
+ static struct rpmpd _platform##_##_active = { \
+ .pd = { .name = #_active, }, \
+ .peer = &_platform##_##_name, \
+ .active_only = true, \
+ .res_type = RPMPD_SMPA, \
+ .res_id = r_id, \
+ .key = KEY_CORNER, \
+ }
+
+#define DEFINE_RPMPD_CORN_LDOA(_platform, _name, r_id) \
+ static struct rpmpd _platform##_##_name = { \
+ .pd = { .name = #_name, }, \
+ .res_type = RPMPD_LDOA, \
+ .res_id = r_id, \
+ .key = KEY_CORNER, \
+ }
+
+#define DEFINE_RPMPD_VFC(_platform, _name, r_id, r_type) \
+ static struct rpmpd _platform##_##_name = { \
+ .pd = { .name = #_name, }, \
+ .res_type = r_type, \
+ .res_id = r_id, \
+ .key = KEY_FLOOR_CORNER, \
+ }
+
+#define DEFINE_RPMPD_VFC_SMPA(_platform, _name, r_id) \
+ DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_SMPA)
+
+#define DEFINE_RPMPD_VFC_LDOA(_platform, _name, r_id) \
+ DEFINE_RPMPD_VFC(_platform, _name, r_id, RPMPD_LDOA)
+
+struct rpmpd_req {
+ __le32 key;
+ __le32 nbytes;
+ __le32 value;
+};
+
+struct rpmpd {
+ struct generic_pm_domain pd;
+ struct rpmpd *peer;
+ const bool active_only;
+ unsigned long corner;
+ bool enabled;
+ const char *res_name;
+ const int res_type;
+ const int res_id;
+ struct qcom_smd_rpm *rpm;
+ __le32 key;
+};
+
+struct rpmpd_desc {
+ struct rpmpd **rpmpds;
+ size_t num_pds;
+};
+
+static DEFINE_MUTEX(rpmpd_lock);
+
+/* msm8996 RPM powerdomains */
+DEFINE_RPMPD_CORN_SMPA(msm8996, vddcx, vddcx_ao, 1);
+DEFINE_RPMPD_CORN_SMPA(msm8996, vddmx, vddmx_ao, 2);
+DEFINE_RPMPD_CORN_LDOA(msm8996, vddsscx, 26);
+
+DEFINE_RPMPD_VFC_SMPA(msm8996, vddcx_vfc, 1);
+DEFINE_RPMPD_VFC_LDOA(msm8996, vddsscx_vfc, 26);
+
+static struct rpmpd *msm8996_rpmpds[] = {
+ [0] = &msm8996_vddcx,
+ [1] = &msm8996_vddcx_ao,
+ [2] = &msm8996_vddcx_vfc,
+ [3] = &msm8996_vddmx,
+ [4] = &msm8996_vddmx_ao,
+ [5] = &msm8996_vddsscx,
+ [6] = &msm8996_vddsscx_vfc,
+};
+
+static const struct rpmpd_desc msm8996_desc = {
+ .rpmpds = msm8996_rpmpds,
+ .num_pds = ARRAY_SIZE(msm8996_rpmpds),
+};
+
+static const struct of_device_id rpmpd_match_table[] = {
+ { .compatible = "qcom,rpmpd-msm8996", .data = &msm8996_desc },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rpmpd_match_table);
+
+static int rpmpd_send_enable(struct rpmpd *pd, bool enable)
+{
+ struct rpmpd_req req = {
+ .key = KEY_ENABLE,
+ .nbytes = cpu_to_le32(sizeof(u32)),
+ .value = cpu_to_le32(enable),
+ };
+
+ return qcom_rpm_smd_write(pd->rpm, QCOM_RPM_ACTIVE_STATE, pd->res_type,
+ pd->res_id, &req, sizeof(req));
+}
+
+static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner)
+{
+ struct rpmpd_req req = {
+ .key = pd->key,
+ .nbytes = cpu_to_le32(sizeof(u32)),
+ .value = cpu_to_le32(corner),
+ };
+
+ return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id,
+ &req, sizeof(req));
+};
+
+static void to_active_sleep(struct rpmpd *pd, unsigned long corner,
+ unsigned long *active, unsigned long *sleep)
+{
+ *active = corner;
+
+ if (pd->active_only)
+ *sleep = 0;
+ else
+ *sleep = *active;
+}
+
+static int rpmpd_aggregate_corner(struct rpmpd *pd)
+{
+ int ret;
+ struct rpmpd *peer = pd->peer;
+ unsigned long active_corner, sleep_corner;
+ unsigned long this_corner = 0, this_sleep_corner = 0;
+ unsigned long peer_corner = 0, peer_sleep_corner = 0;
+
+ to_active_sleep(pd, pd->corner, &this_corner, &this_sleep_corner);
+
+ if (peer && peer->enabled)
+ to_active_sleep(peer, peer->corner, &peer_corner,
+ &peer_sleep_corner);
+
+ active_corner = max(this_corner, peer_corner);
+
+ ret = rpmpd_send_corner(pd, QCOM_RPM_ACTIVE_STATE, active_corner);
+ if (ret)
+ return ret;
+
+ sleep_corner = max(this_sleep_corner, peer_sleep_corner);
+
+ return rpmpd_send_corner(pd, QCOM_RPM_SLEEP_STATE, sleep_corner);
+}
+
+static int rpmpd_power_on(struct generic_pm_domain *domain)
+{
+ int ret;
+ struct rpmpd *pd = domain_to_rpmpd(domain);
+
+ mutex_lock(&rpmpd_lock);
+
+ ret = rpmpd_send_enable(pd, true);
+ if (ret)
+ goto out;
+
+ pd->enabled = true;
+
+ if (pd->corner)
+ ret = rpmpd_aggregate_corner(pd);
+
+out:
+ mutex_unlock(&rpmpd_lock);
+
+ return ret;
+}
+
+static int rpmpd_power_off(struct generic_pm_domain *domain)
+{
+ int ret;
+ struct rpmpd *pd = domain_to_rpmpd(domain);
+
+ mutex_lock(&rpmpd_lock);
+
+ ret = rpmpd_send_enable(pd, false);
+ if (!ret)
+ pd->enabled = false;
+
+ mutex_unlock(&rpmpd_lock);
+
+ return ret;
+}
+
+static int rpmpd_probe(struct platform_device *pdev)
+{
+ int i;
+ size_t num;
+ struct genpd_onecell_data *data;
+ struct qcom_smd_rpm *rpm;
+ struct rpmpd **rpmpds;
+ const struct rpmpd_desc *desc;
+
+ rpm = dev_get_drvdata(pdev->dev.parent);
+ if (!rpm) {
+ dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n");
+ return -ENODEV;
+ }
+
+ desc = of_device_get_match_data(&pdev->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmpds = desc->rpmpds;
+ num = desc->num_pds;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains),
+ GFP_KERNEL);
+ data->num_domains = num;
+
+ for (i = 0; i < num; i++) {
+ if (!rpmpds[i])
+ continue;
+
+ rpmpds[i]->rpm = rpm;
+ rpmpds[i]->pd.power_off = rpmpd_power_off;
+ rpmpds[i]->pd.power_on = rpmpd_power_on;
+ pm_genpd_init(&rpmpds[i]->pd, NULL, true);
+
+ data->domains[i] = &rpmpds[i]->pd;
+ }
+
+ return of_genpd_add_provider_onecell(pdev->dev.of_node, data);
+}
+
+static int rpmpd_remove(struct platform_device *pdev)
+{
+ of_genpd_del_provider(pdev->dev.of_node);
+ return 0;
+}
+
+static struct platform_driver rpmpd_driver = {
+ .driver = {
+ .name = "qcom-rpmpd",
+ .of_match_table = rpmpd_match_table,
+ },
+ .probe = rpmpd_probe,
+ .remove = rpmpd_remove,
+};
+
+static int __init rpmpd_init(void)
+{
+ return platform_driver_register(&rpmpd_driver);
+}
+core_initcall(rpmpd_init);
+
+static void __exit rpmpd_exit(void)
+{
+ platform_driver_unregister(&rpmpd_driver);
+}
+module_exit(rpmpd_exit);
+
+MODULE_DESCRIPTION("Qualcomm RPM Power Domain Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-rpmpd");
--
2.13.0.71.gd7076ec9c9cb
[toc] | [prev] | [next] | [standalone]
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [PATCH V8 1/6] PM / Domains: Add support to select performance-state of domains |
| Message-ID | <tUI5K-31b-41@gated-at.bofh.it> |
| In reply to | #1671421 |
Some platforms have the capability to configure the performance state of
their Power Domains. The performance levels are identified by positive
integer values, a lower value represents lower performance state.
This patch adds a new genpd API: pm_genpd_update_performance_state().
The caller passes the affected device and the frequency representing its
next DVFS state.
The power domains get two new callbacks:
- get_performance_state(): This is called by the genpd core to retrieve
the performance state (integer value) corresponding to a target
frequency for the device. This state is used by the genpd core to find
the highest requested state by all the devices powered by a domain.
- set_performance_state(): The highest state retrieved from above
interface is then passed to this callback to finally program the
performance state of the power domain.
The power domains can avoid supplying these callbacks, if they don't
support setting performance-states.
A power domain may have only get_performance_state() callback, if it
doesn't have the capability of changing the performance state itself but
someone in its parent hierarchy has.
A power domain may have only set_performance_state(), if it doesn't have
any direct devices below it but subdomains. And so the
get_performance_state() will never get called from the core.
The more common case would be to have both the callbacks set.
Another API, pm_genpd_has_performance_state(), is also added to let
other parts of the kernel check if the power domain of a device supports
performance-states or not. This could have been done from
pm_genpd_update_performance_state() as well, but that routine gets
called every time we do DVFS for the device and it wouldn't be optimal
in that case.
Note that, the performance level as returned by
->get_performance_state() for the parent domain of a device is used for
all domains in parent hierarchy.
Tested-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/base/power/domain.c | 223 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/pm_domain.h | 22 +++++
2 files changed, 245 insertions(+)
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 71c95ad808d5..d506be9ff1f7 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -466,6 +466,229 @@ static int genpd_dev_pm_qos_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
+/*
+ * Returns true if anyone in genpd's parent hierarchy has
+ * set_performance_state() set.
+ */
+static bool genpd_has_set_performance_state(struct generic_pm_domain *genpd)
+{
+ struct gpd_link *link;
+
+ if (genpd->set_performance_state)
+ return true;
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ if (genpd_has_set_performance_state(link->master))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * pm_genpd_has_performance_state - Checks if power domain does performance
+ * state management.
+ *
+ * @dev: Device whose power domain is getting inquired.
+ *
+ * This must be called by the user drivers, before they start calling
+ * pm_genpd_update_performance_state(), to guarantee that all dependencies are
+ * met and the device's genpd supports performance states.
+ *
+ * It is assumed that the user driver guarantees that the genpd wouldn't be
+ * detached while this routine is getting called.
+ *
+ * Returns "true" if device's genpd supports performance states, "false"
+ * otherwise.
+ */
+bool pm_genpd_has_performance_state(struct device *dev)
+{
+ struct generic_pm_domain *genpd = genpd_lookup_dev(dev);
+
+ /* The parent domain must have set get_performance_state() */
+ if (!IS_ERR(genpd) && genpd->get_performance_state) {
+ if (genpd_has_set_performance_state(genpd))
+ return true;
+
+ /*
+ * A genpd with ->get_performance_state() callback must also
+ * allow setting performance state.
+ */
+ dev_err(dev, "genpd doesn't support setting performance state\n");
+ }
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_has_performance_state);
+
+/*
+ * Re-evaluate performance state of a power domain. Finds the highest requested
+ * performance state by the devices and subdomains within the power domain and
+ * then tries to change its performance state. If the power domain doesn't have
+ * a set_performance_state() callback, then we move the request to its parent
+ * power domain.
+ *
+ * Locking: Access (or update) to device's "pd_data->performance_state" field
+ * happens only with parent domain's lock held. Subdomains have their
+ * "genpd->performance_state" protected with their own lock (and they are the
+ * only user of this field) and their per-parent-domain
+ * "link->performance_state" field is protected with individual parent power
+ * domain's lock and is only accessed/updated with that lock held.
+ */
+static int genpd_update_performance_state(struct generic_pm_domain *genpd,
+ int depth)
+{
+ struct generic_pm_domain_data *pd_data;
+ struct generic_pm_domain *master;
+ struct pm_domain_data *pdd;
+ unsigned int state = 0, prev;
+ struct gpd_link *link;
+ int ret;
+
+ /* Traverse all devices within the domain */
+ list_for_each_entry(pdd, &genpd->dev_list, list_node) {
+ pd_data = to_gpd_data(pdd);
+
+ if (pd_data->performance_state > state)
+ state = pd_data->performance_state;
+ }
+
+ /* Traverse all subdomains within the domain */
+ list_for_each_entry(link, &genpd->master_links, master_node) {
+ if (link->performance_state > state)
+ state = link->performance_state;
+ }
+
+ if (genpd->performance_state == state)
+ return 0;
+
+ if (genpd->set_performance_state) {
+ ret = genpd->set_performance_state(genpd, state);
+ if (!ret)
+ genpd->performance_state = state;
+
+ return ret;
+ }
+
+ /*
+ * Not all domains support updating performance state. Move on to their
+ * parent domains in that case.
+ */
+ prev = genpd->performance_state;
+
+ list_for_each_entry(link, &genpd->slave_links, slave_node) {
+ master = link->master;
+
+ genpd_lock_nested(master, depth + 1);
+
+ link->performance_state = state;
+ ret = genpd_update_performance_state(master, depth + 1);
+ if (ret)
+ link->performance_state = prev;
+
+ genpd_unlock(master);
+
+ if (ret)
+ goto err;
+ }
+
+ /*
+ * The parent domains are updated now, lets get genpd performance_state
+ * in sync with those.
+ */
+ genpd->performance_state = state;
+ return 0;
+
+err:
+ list_for_each_entry_continue_reverse(link, &genpd->slave_links,
+ slave_node) {
+ master = link->master;
+
+ genpd_lock_nested(master, depth + 1);
+ link->performance_state = prev;
+ if (genpd_update_performance_state(master, depth + 1))
+ pr_err("%s: Failed to roll back to %d performance state\n",
+ genpd->name, prev);
+ genpd_unlock(master);
+ }
+
+ return ret;
+}
+
+static int __dev_update_performance_state(struct device *dev, int state)
+{
+ struct generic_pm_domain_data *gpd_data;
+ int ret;
+
+ spin_lock_irq(&dev->power.lock);
+
+ if (!dev->power.subsys_data || !dev->power.subsys_data->domain_data) {
+ ret = -ENODEV;
+ goto unlock;
+ }
+
+ gpd_data = to_gpd_data(dev->power.subsys_data->domain_data);
+
+ ret = gpd_data->performance_state;
+ gpd_data->performance_state = state;
+
+unlock:
+ spin_unlock_irq(&dev->power.lock);
+
+ return ret;
+}
+
+/**
+ * pm_genpd_update_performance_state - Update performance state of device's
+ * parent power domain for the target frequency for the device.
+ *
+ * @dev: Device for which the performance-state needs to be adjusted.
+ * @rate: Device's next frequency. This can be set as 0 when the device doesn't
+ * have any performance state constraints left (And so the device wouldn't
+ * participate anymore to find the target performance state of the genpd).
+ *
+ * This must be called by the user drivers (as many times as they want) only
+ * after pm_genpd_has_performance_state() is called (only once) and that
+ * returned "true".
+ *
+ * It is assumed that the user driver guarantees that the genpd wouldn't be
+ * detached while this routine is getting called.
+ *
+ * Returns 0 on success and negative error values on failures.
+ */
+int pm_genpd_update_performance_state(struct device *dev, unsigned long rate)
+{
+ struct generic_pm_domain *genpd = dev_to_genpd(dev);
+ int ret, state;
+
+ if (IS_ERR(genpd))
+ return -ENODEV;
+
+ genpd_lock(genpd);
+
+ state = genpd->get_performance_state(dev, rate);
+ if (state < 0) {
+ ret = state;
+ goto unlock;
+ }
+
+ state = __dev_update_performance_state(dev, state);
+ if (state < 0) {
+ ret = state;
+ goto unlock;
+ }
+
+ ret = genpd_update_performance_state(genpd, 0);
+ if (ret)
+ __dev_update_performance_state(dev, state);
+
+unlock:
+ genpd_unlock(genpd);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(pm_genpd_update_performance_state);
+
/**
* genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
* @work: Work structure used for scheduling the execution of this function.
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index b7803a251044..bf90177208a2 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -63,8 +63,12 @@ struct generic_pm_domain {
unsigned int device_count; /* Number of devices */
unsigned int suspended_count; /* System suspend device counter */
unsigned int prepared_count; /* Suspend counter of prepared devices */
+ unsigned int performance_state; /* Max requested performance state */
int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain);
+ int (*get_performance_state)(struct device *dev, unsigned long rate);
+ int (*set_performance_state)(struct generic_pm_domain *domain,
+ unsigned int state);
struct gpd_dev_ops dev_ops;
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
bool max_off_time_changed;
@@ -99,6 +103,9 @@ struct gpd_link {
struct list_head master_node;
struct generic_pm_domain *slave;
struct list_head slave_node;
+
+ /* Sub-domain's per-parent domain performance state */
+ unsigned int performance_state;
};
struct gpd_timing_data {
@@ -118,6 +125,7 @@ struct generic_pm_domain_data {
struct pm_domain_data base;
struct gpd_timing_data td;
struct notifier_block nb;
+ unsigned int performance_state;
void *data;
};
@@ -148,6 +156,9 @@ extern int pm_genpd_remove(struct generic_pm_domain *genpd);
extern struct dev_power_governor simple_qos_governor;
extern struct dev_power_governor pm_domain_always_on_gov;
+extern bool pm_genpd_has_performance_state(struct device *dev);
+extern int pm_genpd_update_performance_state(struct device *dev,
+ unsigned long rate);
#else
static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
@@ -185,6 +196,17 @@ static inline int pm_genpd_remove(struct generic_pm_domain *genpd)
return -ENOTSUPP;
}
+static inline bool pm_genpd_has_performance_state(struct device *dev)
+{
+ return false;
+}
+
+static inline int pm_genpd_update_performance_state(struct device *dev,
+ unsigned long rate)
+{
+ return -ENOTSUPP;
+}
+
#define simple_qos_governor (*(struct dev_power_governor *)(NULL))
#define pm_domain_always_on_gov (*(struct dev_power_governor *)(NULL))
#endif
--
2.13.0.71.gd7076ec9c9cb
[toc] | [prev] | [next] | [standalone]
| From | Viresh Kumar <viresh.kumar@linaro.org> |
|---|---|
| Date | 2017-06-21 09:20 +0200 |
| Subject | [PATCH V8 2/6] PM / OPP: Support updating performance state of device's power domains |
| Message-ID | <tUI5K-31b-45@gated-at.bofh.it> |
| In reply to | #1671421 |
The genpd framework now provides an API to request device's power
domain to update its performance state based on a particular target
frequency for the device.
Use that interface from the OPP core for devices whose power domains
support performance states.
Note that the current implementation is restricted to the case where the
device doesn't have separate regulators for itself. We shouldn't
over engineer the code before we have real use case for them. We can
always come back and add more code to support such cases later on.
Tested-by: Rajendra Nayak <rnayak@codeaurora.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
drivers/base/power/opp/core.c | 48 ++++++++++++++++++++++++++++++++++++++++++-
drivers/base/power/opp/opp.h | 2 ++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/drivers/base/power/opp/core.c b/drivers/base/power/opp/core.c
index a8cc14fd8ae4..ef623afcc5fd 100644
--- a/drivers/base/power/opp/core.c
+++ b/drivers/base/power/opp/core.c
@@ -19,6 +19,7 @@
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/export.h>
+#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
#include "opp.h"
@@ -535,6 +536,42 @@ _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
return ret;
}
+static inline int
+_generic_set_opp_domain(struct device *dev, struct clk *clk,
+ unsigned long old_freq, unsigned long freq)
+{
+ int ret;
+
+ /* Scaling up? Scale domain performance state before frequency */
+ if (freq > old_freq) {
+ ret = pm_genpd_update_performance_state(dev, freq);
+ if (ret)
+ return ret;
+ }
+
+ ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+ if (ret)
+ goto restore_domain_state;
+
+ /* Scaling down? Scale domain performance state after frequency */
+ if (freq < old_freq) {
+ ret = pm_genpd_update_performance_state(dev, freq);
+ if (ret)
+ goto restore_freq;
+ }
+
+ return 0;
+
+restore_freq:
+ if (_generic_set_opp_clk_only(dev, clk, freq, old_freq))
+ dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
+ __func__, old_freq);
+restore_domain_state:
+ pm_genpd_update_performance_state(dev, old_freq);
+
+ return ret;
+}
+
static int _generic_set_opp_regulator(const struct opp_table *opp_table,
struct device *dev,
unsigned long old_freq,
@@ -653,7 +690,14 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
/* Only frequency scaling */
if (!opp_table->regulators) {
- ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
+ /*
+ * We don't support devices with both regulator and
+ * domain performance-state for now.
+ */
+ if (opp_table->genpd_performance_state)
+ ret = _generic_set_opp_domain(dev, clk, old_freq, freq);
+ else
+ ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
} else if (!opp_table->set_opp) {
ret = _generic_set_opp_regulator(opp_table, dev, old_freq, freq,
IS_ERR(old_opp) ? NULL : old_opp->supplies,
@@ -755,6 +799,8 @@ static struct opp_table *_allocate_opp_table(struct device *dev)
ret);
}
+ opp_table->genpd_performance_state = pm_genpd_has_performance_state(dev);
+
BLOCKING_INIT_NOTIFIER_HEAD(&opp_table->head);
INIT_LIST_HEAD(&opp_table->opp_list);
mutex_init(&opp_table->lock);
diff --git a/drivers/base/power/opp/opp.h b/drivers/base/power/opp/opp.h
index 166eef990599..1efa253e1934 100644
--- a/drivers/base/power/opp/opp.h
+++ b/drivers/base/power/opp/opp.h
@@ -135,6 +135,7 @@ enum opp_table_access {
* @clk: Device's clock handle
* @regulators: Supply regulators
* @regulator_count: Number of power supply regulators
+ * @genpd_performance_state: Device's power domain support performance state.
* @set_opp: Platform specific set_opp callback
* @set_opp_data: Data to be passed to set_opp callback
* @dentry: debugfs dentry pointer of the real device directory (not links).
@@ -170,6 +171,7 @@ struct opp_table {
struct clk *clk;
struct regulator **regulators;
unsigned int regulator_count;
+ bool genpd_performance_state;
int (*set_opp)(struct dev_pm_set_opp_data *data);
struct dev_pm_set_opp_data *set_opp_data;
--
2.13.0.71.gd7076ec9c9cb
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web