* [PATCH v1 03/11] PM / devfreq: tegra30: Handle possible round-rate error
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
The EMC clock rate rounding technically could fail, hence let's handle
the error cases properly.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 5e2b133babdd..5e606ae3f238 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -592,8 +592,8 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
struct tegra_devfreq_device *dev;
struct tegra_devfreq *tegra;
struct devfreq *devfreq;
- unsigned long rate;
unsigned int i;
+ long rate;
int err;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
@@ -650,8 +650,14 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
reset_control_deassert(tegra->reset);
- tegra->max_freq = clk_round_rate(tegra->emc_clock, ULONG_MAX) / KHZ;
+ rate = clk_round_rate(tegra->emc_clock, ULONG_MAX);
+ if (rate < 0) {
+ dev_err(&pdev->dev, "Failed to round clock rate: %ld\n", rate);
+ return rate;
+ }
+
tegra->cur_freq = clk_get_rate(tegra->emc_clock) / KHZ;
+ tegra->max_freq = rate / KHZ;
for (i = 0; i < ARRAY_SIZE(actmon_device_configs); i++) {
dev = tegra->devices + i;
@@ -662,6 +668,13 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
for (rate = 0; rate <= tegra->max_freq * KHZ; rate++) {
rate = clk_round_rate(tegra->emc_clock, rate);
+ if (rate < 0) {
+ dev_err(&pdev->dev,
+ "Failed to round clock rate: %ld\n", rate);
+ err = rate;
+ goto remove_opps;
+ }
+
err = dev_pm_opp_add(&pdev->dev, rate, 0);
if (err) {
dev_err(&pdev->dev, "Failed to add OPP: %d\n", err);
--
2.22.0
^ permalink raw reply related
* [PATCH v1 06/11] PM / devfreq: tegra30: Reset boosting on startup
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
Governor could be stopped while boosting is active. We have assumption
that everything is reset on governor's restart, including the boosting
value, which was missed.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 813b0f490b90..fc278f2f1b62 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -544,6 +544,9 @@ static void tegra_actmon_configure_device(struct tegra_devfreq *tegra,
{
u32 val = 0, target_freq;
+ /* we don't want boosting on restart */
+ dev->boost_freq = 0;
+
target_freq = clk_get_rate(tegra->emc_clock) / KHZ;
dev->avg_count = target_freq * ACTMON_SAMPLING_PERIOD;
device_writel(dev, dev->avg_count, ACTMON_DEV_INIT_AVG);
--
2.22.0
^ permalink raw reply related
* [PATCH v1 02/11] PM / devfreq: tegra30: Keep interrupt disabled while governor is stopped
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
There is no real need to keep interrupt always-enabled, will be nicer
to keep it disabled where appropriate.
Suggested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 43 ++++++++++++++++---------------
1 file changed, 22 insertions(+), 21 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index a27300f40b0b..5e2b133babdd 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -11,6 +11,7 @@
#include <linux/devfreq.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
@@ -416,8 +417,6 @@ static void tegra_actmon_start(struct tegra_devfreq *tegra)
{
unsigned int i;
- disable_irq(tegra->irq);
-
actmon_writel(tegra, ACTMON_SAMPLING_PERIOD - 1,
ACTMON_GLB_PERIOD_CTRL);
@@ -442,8 +441,6 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
}
actmon_write_barrier(tegra);
-
- enable_irq(tegra->irq);
}
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
@@ -552,6 +549,12 @@ static int tegra_governor_event_handler(struct devfreq *devfreq,
{
struct tegra_devfreq *tegra = dev_get_drvdata(devfreq->dev.parent);
+ /*
+ * Couple device with the governor early as it is needed at
+ * the moment of governor's start (used by ISR).
+ */
+ tegra->devfreq = devfreq;
+
switch (event) {
case DEVFREQ_GOV_START:
devfreq_monitor_start(devfreq);
@@ -586,10 +589,11 @@ static struct devfreq_governor tegra_devfreq_governor = {
static int tegra_devfreq_probe(struct platform_device *pdev)
{
- struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev;
- unsigned int i;
+ struct tegra_devfreq *tegra;
+ struct devfreq *devfreq;
unsigned long rate;
+ unsigned int i;
int err;
tegra = devm_kzalloc(&pdev->dev, sizeof(*tegra), GFP_KERNEL);
@@ -625,6 +629,16 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra->irq = err;
+ irq_set_status_flags(tegra->irq, IRQ_NOAUTOEN);
+
+ err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL,
+ actmon_thread_isr, IRQF_ONESHOT,
+ "tegra-devfreq", tegra);
+ if (err) {
+ dev_err(&pdev->dev, "Interrupt request failed: %d\n", err);
+ return err;
+ }
+
reset_control_assert(tegra->reset);
err = clk_prepare_enable(tegra->clock);
@@ -672,28 +686,15 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
}
tegra_devfreq_profile.initial_freq = clk_get_rate(tegra->emc_clock);
- tegra->devfreq = devfreq_add_device(&pdev->dev,
- &tegra_devfreq_profile,
- "tegra_actmon",
- NULL);
+ devfreq = devfreq_add_device(&pdev->dev, &tegra_devfreq_profile,
+ "tegra_actmon", NULL);
if (IS_ERR(tegra->devfreq)) {
err = PTR_ERR(tegra->devfreq);
goto remove_governor;
}
- err = devm_request_threaded_irq(&pdev->dev, tegra->irq, NULL,
- actmon_thread_isr, IRQF_ONESHOT,
- "tegra-devfreq", tegra);
- if (err) {
- dev_err(&pdev->dev, "Interrupt request failed: %d\n", err);
- goto remove_devfreq;
- }
-
return 0;
-remove_devfreq:
- devfreq_remove_device(tegra->devfreq);
-
remove_governor:
devfreq_remove_governor(&tegra_devfreq_governor);
--
2.22.0
^ permalink raw reply related
* [PATCH v1 04/11] PM / devfreq: tegra30: Drop write-barrier
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
There is no need in a write-barrier now, given that interrupt masking is
handled by CPU's GIC now. Hence we know exactly that interrupt won't fire
after stopping the devfreq's governor. In other cases we don't care about
potential buffering of the writes to hardware and thus there is no need to
stall CPU.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 14 --------------
1 file changed, 14 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 5e606ae3f238..4be7858c33bc 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -230,12 +230,6 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
ACTMON_DEV_LOWER_WMARK);
}
-static void actmon_write_barrier(struct tegra_devfreq *tegra)
-{
- /* ensure the update has reached the ACTMON */
- readl(tegra->regs + ACTMON_GLB_STATUS);
-}
-
static void actmon_isr_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
@@ -287,8 +281,6 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,
device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
-
- actmon_write_barrier(tegra);
}
static unsigned long actmon_cpu_to_emc_rate(struct tegra_devfreq *tegra,
@@ -376,8 +368,6 @@ static int tegra_actmon_rate_notify_cb(struct notifier_block *nb,
tegra_devfreq_update_wmark(tegra, dev);
}
- actmon_write_barrier(tegra);
-
return NOTIFY_OK;
}
@@ -423,8 +413,6 @@ static void tegra_actmon_start(struct tegra_devfreq *tegra)
for (i = 0; i < ARRAY_SIZE(tegra->devices); i++)
tegra_actmon_configure_device(tegra, &tegra->devices[i]);
- actmon_write_barrier(tegra);
-
enable_irq(tegra->irq);
}
@@ -439,8 +427,6 @@ static void tegra_actmon_stop(struct tegra_devfreq *tegra)
device_writel(&tegra->devices[i], ACTMON_INTR_STATUS_CLEAR,
ACTMON_DEV_INTR_STATUS);
}
-
- actmon_write_barrier(tegra);
}
static int tegra_devfreq_target(struct device *dev, unsigned long *freq,
--
2.22.0
^ permalink raw reply related
* [PATCH v1 10/11] PM / devfreq: tegra30: Add debug messages
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
Add debug messages to know about what's happening in hardware and how
driver reacts.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 33 +++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index 168bfe78e525..452bc10d2d72 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -277,6 +277,8 @@ static void tegra_actmon_get_lower_upper(struct tegra_devfreq *tegra,
unsigned long *lower,
unsigned long *upper)
{
+ struct device *ddev = tegra->devfreq->dev.parent;
+ u32 offset = dev->config->offset;
unsigned long target_freq, min;
target_freq = count / ACTMON_SAMPLING_PERIOD * KHZ;
@@ -312,6 +314,9 @@ static void tegra_actmon_get_lower_upper(struct tegra_devfreq *tegra,
else
*upper = tegra_actmon_upper_freq(tegra, target_freq);
+ dev_dbg(ddev, "%03x: target_freq %lu lower freq %lu upper freq %lu\n",
+ offset, target_freq, *lower, *upper);
+
*lower /= KHZ;
*upper /= KHZ;
@@ -384,12 +389,32 @@ static void tegra_devfreq_update_wmark(struct tegra_devfreq *tegra,
device_writel(dev, count + delta, ACTMON_DEV_UPPER_WMARK);
}
+static void actmon_device_debug(struct tegra_devfreq *tegra,
+ struct tegra_devfreq_device *dev,
+ const char *prefix)
+{
+ dev_dbg(tegra->devfreq->dev.parent,
+ "%03x: %s: 0x%08x 0x%08x a %u %u %u c %u %u %u b %lu cpu %u\n",
+ dev->config->offset, prefix,
+ device_readl(dev, ACTMON_DEV_INTR_STATUS),
+ device_readl(dev, ACTMON_DEV_CTRL),
+ device_readl(dev, ACTMON_DEV_AVG_COUNT),
+ device_readl(dev, ACTMON_DEV_AVG_LOWER_WMARK),
+ device_readl(dev, ACTMON_DEV_AVG_UPPER_WMARK),
+ device_readl(dev, ACTMON_DEV_COUNT),
+ device_readl(dev, ACTMON_DEV_LOWER_WMARK),
+ device_readl(dev, ACTMON_DEV_UPPER_WMARK),
+ dev->boost_freq, cpufreq_get(0));
+}
+
static void actmon_isr_device(struct tegra_devfreq *tegra,
struct tegra_devfreq_device *dev)
{
u32 intr_status, dev_ctrl, avg_intr_mask;
bool low_activity = true;
+ actmon_device_debug(tegra, dev, "isr+");
+
dev->avg_count = device_readl(dev, ACTMON_DEV_AVG_COUNT);
intr_status = device_readl(dev, ACTMON_DEV_INTR_STATUS);
dev_ctrl = device_readl(dev, ACTMON_DEV_CTRL);
@@ -451,6 +476,8 @@ static void actmon_isr_device(struct tegra_devfreq *tegra,
device_writel(dev, dev_ctrl, ACTMON_DEV_CTRL);
device_writel(dev, ACTMON_INTR_STATUS_CLEAR, ACTMON_DEV_INTR_STATUS);
+
+ actmon_device_debug(tegra, dev, "isr-");
}
static unsigned long actmon_update_target(struct tegra_devfreq *tegra,
@@ -737,6 +764,7 @@ static struct devfreq_dev_profile tegra_devfreq_profile = {
static int tegra_governor_get_target(struct devfreq *devfreq,
unsigned long *freq)
{
+ struct device *ddev = devfreq->dev.parent;
struct devfreq_dev_status *stat;
struct tegra_devfreq *tegra;
struct tegra_devfreq_device *dev;
@@ -759,6 +787,11 @@ static int tegra_governor_get_target(struct devfreq *devfreq,
dev_target_freq = actmon_update_target(tegra, dev);
target_freq = max(target_freq, dev_target_freq);
+
+ dev_dbg(ddev, "%03x: upd: dev_target_freq %lu\n",
+ dev->config->offset, dev_target_freq);
+
+ actmon_device_debug(tegra, dev, "upd");
}
*freq = target_freq * KHZ;
--
2.22.0
^ permalink raw reply related
* [PATCH v1 01/11] PM / devfreq: tegra30: Change irq type to unsigned int
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
IRQ numbers are always positive, hence the corresponding variable should
be unsigned to keep types consistent. This is a minor change that cleans
up code a tad more.
Suggested-by: Thierry Reding <thierry.reding@gmail.com>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/devfreq/tegra30-devfreq.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/devfreq/tegra30-devfreq.c b/drivers/devfreq/tegra30-devfreq.c
index a6ba75f4106d..a27300f40b0b 100644
--- a/drivers/devfreq/tegra30-devfreq.c
+++ b/drivers/devfreq/tegra30-devfreq.c
@@ -160,7 +160,7 @@ struct tegra_devfreq {
struct tegra_devfreq_device devices[ARRAY_SIZE(actmon_device_configs)];
- int irq;
+ unsigned int irq;
};
struct tegra_actmon_emc_ratio {
@@ -618,12 +618,12 @@ static int tegra_devfreq_probe(struct platform_device *pdev)
return PTR_ERR(tegra->emc_clock);
}
- tegra->irq = platform_get_irq(pdev, 0);
- if (tegra->irq < 0) {
- err = tegra->irq;
+ err = platform_get_irq(pdev, 0);
+ if (err < 0) {
dev_err(&pdev->dev, "Failed to get IRQ: %d\n", err);
return err;
}
+ tegra->irq = err;
reset_control_assert(tegra->reset);
--
2.22.0
^ permalink raw reply related
* Re: Alternatives to /sys/kernel/debug/wakeup_sources
From: Tri Vo @ 2019-06-24 1:48 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Joel Fernandes, Rafael J. Wysocki, Sandeep Patil, Viresh Kumar,
Hridya Valsaraju, Linux PM, Cc: Android Kernel,
Greg Kroah-Hartman, LKML
In-Reply-To: <CAJZ5v0gvzCx-7qS9qkxB=sGKjQJKMR7yCc21f=_vqrbZxMSWNg@mail.gmail.com>
On Wed, Jun 19, 2019 at 1:35 AM Rafael J. Wysocki <rafael@kernel.org> wrote:
>
> On Wed, Jun 19, 2019 at 1:52 AM Joel Fernandes <joelaf@google.com> wrote:
> >
> > On Tue, Jun 18, 2019 at 7:15 PM Tri Vo <trong@android.com> wrote:
> > [snip]
> > > > > > >
> > > > > > > Android userspace reading wakeup_sources is not ideal because:
> > > > > > > - Debugfs API is not stable, i.e. Android tools built on top of it are
> > > > > > > not guaranteed to be backward/forward compatible.
> > > > > > > - This file requires debugfs to be mounted, which itself is
> > > > > > > undesirable for security reasons.
> > > > > > >
> > > > > > > To address these problems, we want to contribute a way to expose these
> > > > > > > statistics that doesn't depend on debugfs.
> > > > > > >
> > > > > > > Some initial thoughts/questions: Should we expose the stats in sysfs?
> > > > > > > Or maybe implement eBPF-based solution? What do you think?
> > > > >
> > > > > We are going through Android's out-of-tree kernel dependencies along with
> > > > > userspace APIs that are not necessarily considered "stable and forever
> > > > > supported" upstream. The debugfs dependencies showed up on our radar as a
> > > > > result and so we are wondering if we should worry about changes in debugfs
> > > > > interface and hence the question(s) below.
> > > > >
> > > > > So, can we rely on /d/wakeup_sources to be considered a userspace API and
> > > > > hence maintained stable as we do for other /proc and /sys entries?
> > > > >
> > > > > If yes, then we will go ahead and add tests for this in LTP or
> > > > > somewhere else suitable.
> > > >
> > > > No, debugfs is not ABI.
> > > >
> > > > > If no, then we would love to hear suggestions for any changes that need to be
> > > > > made or we simply just move the debugfs entry into somewhere like
> > > > > /sys/power/ ?
> > > >
> > > > No, moving that entire file from debugfs into sysfs is not an option either.
> > > >
> > > > The statistics for the wakeup sources associated with devices are already there
> > > > under /sys/devices/.../power/ , but I guess you want all wakeup sources?
> > > >
> > > > That would require adding a kobject to struct wakeup_source and exposing
> > > > all of the statistics as separate attributes under it. In which case it would be
> > > > good to replace the existing wakeup statistics under /sys/devices/.../power/
> > > > with symbolic links to the attributes under the wakeup_source kobject.
> > >
> > > Thanks for your input, Rafael! Your suggestion makes sense. I'll work
> > > on a patch for this.
> >
> > Does that entail making each wake up source, a new sysfs node under a
> > particular device, and then adding stats under that new node?
>
> Not under a device, because there are wakeup source objects without
> associated devices.
>
> It is conceivable to have a "wakeup_sources" directory under
> /sys/power/ and sysfs nodes for all wakeup sources in there.
>
> Then, instead of exposing wakeup statistics directly under
> /sys/devices/.../power/, there can be symbolic links from there to the
> new wakeup source nodes under "wakeup_sources" (so as to avoid
> exposing the same data in two different places in sysfs, which may be
> confusing).
This may be a dumb question. Is it appropriate to make symbolic links
in sysfs from one attribute to another attribute? For example,
/sys/devices/.../power/wakeup_count ->
/sys/power/wakeup_sources/.../wakeup_count.
I only see kobject to kobject symlinks around. And I don't think we
can make /sys/devices/.../power/ directory a symlink to where our new
wakeup stats will be, since the former contains attributes other than
wakeup ones.
Thanks!
^ permalink raw reply
* [PATCH v1 00/11] More improvements for Tegra30 devfreq driver
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
Hello,
This series is a followup to [1] which addresses some additional review
comments that were made by Thierry Reding to [1] and makes several
important changes to the driver, fixing excessive interrupts activity.
In the end I'm proposing myself as a maintainer for the driver.
[1] https://lore.kernel.org/lkml/0fb50eb1-a173-1756-6889-2526a10ac707@gmail.com/T/
Dmitry Osipenko (11):
PM / devfreq: tegra30: Change irq type to unsigned int
PM / devfreq: tegra30: Keep interrupt disabled while governor is
stopped
PM / devfreq: tegra30: Handle possible round-rate error
PM / devfreq: tegra30: Drop write-barrier
PM / devfreq: tegra30: Rework frequency management logic
PM / devfreq: tegra30: Reset boosting on startup
PM / devfreq: tegra30: Reset boosting if clock rate changed
PM / devfreq: tegra30: Stop de-boosting once it's finished
PM / devfreq: tegra30: Don't enable consecutive-down interrupt on
startup
PM / devfreq: tegra30: Add debug messages
PM / devfreq: tegra30: Add Dmitry as maintainer
MAINTAINERS | 8 +
drivers/devfreq/tegra30-devfreq.c | 542 ++++++++++++++++++++++--------
2 files changed, 409 insertions(+), 141 deletions(-)
--
2.22.0
^ permalink raw reply
* [PATCH v1 11/11] PM / devfreq: tegra30: Add Dmitry as maintainer
From: Dmitry Osipenko @ 2019-06-23 21:46 UTC (permalink / raw)
To: Thierry Reding, MyungJoo Ham, Kyungmin Park, Chanwoo Choi,
Jonathan Hunter, Tomeu Vizoso
Cc: linux-pm, linux-tegra, linux-kernel
In-Reply-To: <20190623214658.11680-1-digetx@gmail.com>
I was contributing to the NVIDIA Tegra30+ devfreq driver recently and want
to help keep it working and evolving in the future.
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
MAINTAINERS | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 6c203278700f..ac347278f1fc 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10214,6 +10214,14 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
S: Maintained
F: drivers/devfreq/tegra20-devfreq.c
+MEMORY FREQUENCY SCALING DRIVER FOR NVIDIA TEGRA30-TEGRA210
+M: Dmitry Osipenko <digetx@gmail.com>
+L: linux-pm@vger.kernel.org
+L: linux-tegra@vger.kernel.org
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/mzx/devfreq.git
+S: Maintained
+F: drivers/devfreq/tegra30-devfreq.c
+
MEMORY MANAGEMENT
L: linux-mm@kvack.org
W: http://www.linux-mm.org
--
2.22.0
^ permalink raw reply related
* [PATCH v1] OPP: Fix crashing when current OPP has unsupportable voltage
From: Dmitry Osipenko @ 2019-06-23 17:50 UTC (permalink / raw)
To: Viresh Kumar, Nishanth Menon, Stephen Boyd, Marc Dietrich
Cc: linux-pm, linux-tegra, linux-kernel
Fix NULL dereference caused by a typo in the code. In particular it
happens when CPU is running on a frequency which has unsupportable voltage
(by regulator) defined in the OPP table and a custom set_opp() callback is
being used. The problem was spotted during of testing of upcoming update
for the NVIDIA Tegra CPUFreq driver.
Cc: stable <stable@vger.kernel.org>
Fixes: 7e535993fa4f ("OPP: Separate out custom OPP handler specific code")
Reported-by: Marc Dietrich <marvin24@gmx.de>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
---
drivers/opp/core.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 9fda9a0ec016..89ec6aa220cf 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -685,7 +685,7 @@ static int _set_opp_custom(const struct opp_table *opp_table,
data->old_opp.rate = old_freq;
size = sizeof(*old_supply) * opp_table->regulator_count;
- if (IS_ERR(old_supply))
+ if (!old_supply)
memset(data->old_opp.supplies, 0, size);
else
memcpy(data->old_opp.supplies, old_supply, size);
--
2.22.0
^ permalink raw reply related
* Re: [PATCH v4 00/16] NVIDIA Tegra devfreq improvements and Tegra20/30 support
From: Dmitry Osipenko @ 2019-06-23 17:17 UTC (permalink / raw)
To: Chanwoo Choi, MyungJoo Ham
Cc: Thierry Reding, Jonathan Hunter, Kyungmin Park, Tomeu Vizoso,
linux-pm, linux-tegra, linux-kernel
In-Reply-To: <5c2a7c32-a98c-3930-14ae-beb0241908d0@gmail.com>
05.06.2019 2:09, Dmitry Osipenko пишет:
> 04.06.2019 3:49, Chanwoo Choi пишет:
>> On 19. 6. 4. 오전 1:52, Dmitry Osipenko wrote:
>>> 03.05.2019 3:52, Dmitry Osipenko пишет:
>>>> 03.05.2019 3:31, Chanwoo Choi пишет:
>>>>> Hi Dmitry,
>>>>>
>>>>> On 19. 5. 2. 오전 8:37, Dmitry Osipenko wrote:
>>>>>> Changelog:
>>>>>>
>>>>>> v4: Addressed all review comments that were made by Chanwoo Choi to v3:
>>>>>>
>>>>>> - changed the driver removal order to match the probe exactly
>>>>>> - added clarifying comment for 1/8 ratio to the Tegra20 driver
>>>>>>
>>>>>> Chanwoo, please also note that the clk patch that should fix
>>>>>> compilation problem that was reported the kbuild-test-robot is already
>>>>>> applied and available in the recent linux-next.
>>>>>
>>>>> I knew that Stephen picked up your path about clock.
>>>>
>>>> Hi Chanwoo,
>>>>
>>>> Okay, good. Thank you very much for reviewing this series! I assume it's
>>>> too late now for v5.2, but it should be good to go for v5.3.
>>>>
>>>
>>> Hello Chanwoo,
>>>
>>> Will be nice to see the patches in the linux-next before they'll hit mainline. We have tested that
>>> everything works fine on a selective devices, but won't hurt to get some extra testing beforehand.
>>> AFAIK, at least NVIDIA people are regularly testing -next on theirs dev boards. Please note that
>>> this not very important, so don't bother if there is some hurdle with pushing to the tracking branch
>>> for now. Also please let me know if you're expecting to see some ACK's on the patches, I'm sure
>>> we'll be able to work out that with Thierry and Jon if necessary.
>>>
>>>
>>
>> Hi Dmitry,
>> I think that it is enough for applying to mainline branch.
>> The devfreq.git is maintained by Myungjoo. He will be merged or
>> reviewed if there are th remained review point.
>
> Thank you very much!
>
>>
>> Hi Myungjoo,
>> I reviewed the Dmitry's patches from v1 to v4 patches.
>> And then I tested them on my testing branch[1] for catching
>> the build warning and error. In result, it is clean.
>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/linux.git/log/?h=devfreq-testing
>>
>> Please review or apply these patches for v5.3.
>>
>
> Hello Myungjoo,
>
> I think this patchset should be completed now. Thierry has some extra
> comments to the patches, but seems nothing critical so far and all the
> concerns could be addressed in a follow-up series. Please let me know if
> you're fine with this, I can re-spin v5 as well if necessary.
>
Hello Chanwoo,
It looks like Myungjoo is inactive at the moment. Do you know if he'll
be back to the time of the merge window opening or you'll be curating
the pull request for 5.3 this time?
Secondly, I'll send a few more patches on top of this series, addressing
Thierry's comments and making more improvements. Please let me know if
this causes any problems and I should re-spin the whole series.
^ permalink raw reply
* [PATCH v4 00/11] add thermal driver for h6
From: Yangtao Li @ 2019-06-23 16:41 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
This patchset add support for H3 and H6 thermal sensor.
BTY, do a cleanup in thermal makfile.
Yangtao Li (11):
thermal: sun8i: add thermal driver for h6
dt-bindings: thermal: add binding document for h6 thermal controller
thermal: fix indentation in makefile
thermal: sun8i: get ths sensor number from device compatible
thermal: sun8i: rework for sun8i_ths_get_temp()
thermal: sun8i: get ths init func from device compatible
thermal: sun8i: rework for ths irq handler func
thermal: sun8i: support ahb clocks
thermal: sun8i: rework for ths calibrate func
dt-bindings: thermal: add binding document for h3 thermal controller
thermal: sun8i: add thermal driver for h3
.../bindings/thermal/sun8i-thermal.yaml | 94 +++
MAINTAINERS | 7 +
drivers/thermal/Kconfig | 14 +
drivers/thermal/Makefile | 9 +-
drivers/thermal/sun8i_thermal.c | 534 ++++++++++++++++++
5 files changed, 654 insertions(+), 4 deletions(-)
create mode 100644 Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
create mode 100644 drivers/thermal/sun8i_thermal.c
---
v4:
-add h3 support
-fix yaml file
---
2.17.1
^ permalink raw reply
* [PATCH v4 02/11] dt-bindings: thermal: add binding document for h6 thermal controller
From: Yangtao Li @ 2019-06-23 16:41 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
This patch adds binding document for allwinner h6 thermal controller.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
.../bindings/thermal/sun8i-thermal.yaml | 71 +++++++++++++++++++
1 file changed, 71 insertions(+)
create mode 100644 Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
diff --git a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
new file mode 100644
index 000000000000..2c5acc61ed03
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/thermal/sun8i-thermal.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner SUN8I Thermal Controller Device Tree Bindings
+
+maintainers:
+ - Yangtao Li <tiny.windzz@gmail.com>
+
+description: |-
+ This describes the device tree binding for the Allwinner thermal
+ controller which measures the on-SoC temperatures.
+
+properties:
+ compatible:
+ enum:
+ - allwinner,sun50i-h6-ths
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ resets:
+ maxItems: 1
+
+ clocks:
+ minItems: 1
+ maxItems: 1
+
+ clock-names:
+ const: bus
+
+ '#thermal-sensor-cells':
+ const: 1
+
+ nvmem-cells:
+ items:
+ - description: ths calibrate data
+
+ nvmem-cell-names:
+ items:
+ - const: calib
+
+required:
+ - compatible
+ - reg
+ - reset
+ - clocks
+ - clock-names
+ - interrupts
+ - '#thermal-sensor-cells'
+
+examples:
+ - |
+ ths: ths@5070400 {
+ compatible = "allwinner,sun50i-h6-ths";
+ reg = <0x05070400 0x100>;
+ clocks = <&ccu CLK_BUS_THS>;
+ clock-names = "bus";
+ resets = <&ccu RST_BUS_THS>;
+ interrupts = <GIC_SPI 15 IRQ_TYPE_LEVEL_HIGH>;
+ nvmem-cells = <&tsen_calib>;
+ nvmem-cell-names = "calib";
+ #thermal-sensor-cells = <1>;
+ };
+
+...
--
2.17.1
^ permalink raw reply related
* [PATCH v4 03/11] thermal: fix indentation in makefile
From: Yangtao Li @ 2019-06-23 16:41 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
To unify code style.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/Makefile | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index fa6f8b206281..d7eafb5ef8ef 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -5,7 +5,7 @@
obj-$(CONFIG_THERMAL) += thermal_sys.o
thermal_sys-y += thermal_core.o thermal_sysfs.o \
- thermal_helpers.o
+ thermal_helpers.o
# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON) += thermal_hwmon.o
@@ -25,11 +25,11 @@ thermal_sys-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
thermal_sys-$(CONFIG_CLOCK_THERMAL) += clock_cooling.o
# devfreq cooling
-thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
+thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
# platform thermal drivers
obj-y += broadcom/
-obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
+obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
@@ -50,7 +50,7 @@ obj-$(CONFIG_TI_SOC_THERMAL) += ti-soc-thermal/
obj-y += st/
obj-$(CONFIG_QCOM_TSENS) += qcom/
obj-y += tegra/
-obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
+obj-$(CONFIG_HISI_THERMAL) += hisi_thermal.o
obj-$(CONFIG_MTK_THERMAL) += mtk_thermal.o
obj-$(CONFIG_GENERIC_ADC_THERMAL) += thermal-generic-adc.o
obj-$(CONFIG_ZX2967_THERMAL) += zx2967_thermal.o
--
2.17.1
^ permalink raw reply related
* [PATCH v4 04/11] thermal: sun8i: get ths sensor number from device compatible
From: Yangtao Li @ 2019-06-23 16:41 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
For different socs, the number of ths sensors is different.
So we need to do some work in order to support more soc.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 28 ++++++++++++++++++++--------
1 file changed, 20 insertions(+), 8 deletions(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index d6918c62682b..c37e1c51a543 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -22,7 +22,6 @@
#define MAX_SENSOR_NUM 4
-#define SUN50I_H6_SENSOR_NUM 2
#define SUN50I_H6_OFFSET -2794
#define SUN50I_H6_SCALE -67
@@ -57,7 +56,12 @@ struct tsensor {
int id;
};
+struct ths_thermal_chip {
+ int sensor_num;
+};
+
struct ths_device {
+ const struct ths_thermal_chip *chip;
struct device *dev;
struct regmap *regmap;
struct reset_control *reset;
@@ -117,7 +121,7 @@ static irqreturn_t sun50i_h6_irq_thread(int irq, void *data)
regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
- for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+ for (i = 0; i < tmdev->chip->sensor_num; i++) {
if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
/* clear data irq pending */
@@ -167,7 +171,7 @@ static int sun50i_ths_calibrate(struct ths_device *tmdev)
goto out;
}
- if (!caldata[0] || callen < 2 + 2 * SUN50I_H6_SENSOR_NUM) {
+ if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num) {
ret = -EINVAL;
goto out_free;
}
@@ -190,7 +194,7 @@ static int sun50i_ths_calibrate(struct ths_device *tmdev)
*/
ft_temp = caldata[0] & FT_TEMP_MASK;
- for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+ for (i = 0; i < tmdev->chip->sensor_num; i++) {
int reg = (int)caldata[i + 1];
int sensor_temp = sun8i_ths_reg2temp(tmdev, reg);
int delta, cdata, calib_offest;
@@ -303,10 +307,10 @@ static int sun50i_thermal_init(struct ths_device *tmdev)
regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
SUN50I_H6_THS_PC_TEMP_PERIOD(58));
/* enable sensor */
- val = GENMASK(SUN50I_H6_SENSOR_NUM - 1, 0);
+ val = GENMASK(tmdev->chip->sensor_num - 1, 0);
regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val);
/* thermal data interrupt enable */
- val = GENMASK(SUN50I_H6_SENSOR_NUM - 1, 0);
+ val = GENMASK(tmdev->chip->sensor_num - 1, 0);
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
return 0;
@@ -317,7 +321,7 @@ static int sun8i_ths_register(struct ths_device *tmdev)
struct thermal_zone_device *tzd;
int i;
- for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+ for (i = 0; i < tmdev->chip->sensor_num; i++) {
tmdev->sensor[i].tmdev = tmdev;
tmdev->sensor[i].id = i;
tmdev->sensor[i].tzd =
@@ -343,6 +347,10 @@ static int sun8i_ths_probe(struct platform_device *pdev)
return -ENOMEM;
tmdev->dev = dev;
+ tmdev->chip = of_device_get_match_data(&pdev->dev);
+ if (!tmdev->chip)
+ return -EINVAL;
+
platform_set_drvdata(pdev, tmdev);
ret = sun8i_ths_resource_init(tmdev);
@@ -385,8 +393,12 @@ static int sun8i_ths_remove(struct platform_device *pdev)
return 0;
}
+static const struct ths_thermal_chip sun50i_h6_ths = {
+ .sensor_num = 2,
+};
+
static const struct of_device_id of_ths_match[] = {
- { .compatible = "allwinner,sun50i-h6-ths"},
+ { .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, of_ths_match);
--
2.17.1
^ permalink raw reply related
* [PATCH v4 05/11] thermal: sun8i: rework for sun8i_ths_get_temp()
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
For different socs, the way they get and calculate the
temperature is roughly the same. So get the difference
from device compatible.
Difference point:
1) temperature calculation formula parameters
2) ths data register start address
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 17 +++++++++++------
1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index c37e1c51a543..e473a5651436 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -22,9 +22,6 @@
#define MAX_SENSOR_NUM 4
-#define SUN50I_H6_OFFSET -2794
-#define SUN50I_H6_SCALE -67
-
#define FT_TEMP_MASK GENMASK(11, 0)
#define TEMP_CALIB_MASK GENMASK(11, 0)
#define TEMP_TO_REG 672
@@ -58,6 +55,10 @@ struct tsensor {
struct ths_thermal_chip {
int sensor_num;
+ int offset;
+ int scale;
+ int ft_deviation;
+ int temp_data_base;
};
struct ths_device {
@@ -73,7 +74,7 @@ struct ths_device {
static int sun8i_ths_reg2temp(struct ths_device *tmdev,
int reg)
{
- return (reg + SUN50I_H6_OFFSET) * SUN50I_H6_SCALE;
+ return (reg + tmdev->chip->offset) * tmdev->chip->scale;
}
static int sun8i_ths_get_temp(void *data, int *temp)
@@ -82,7 +83,7 @@ static int sun8i_ths_get_temp(void *data, int *temp)
struct ths_device *tmdev = s->tmdev;
int val;
- regmap_read(tmdev->regmap, SUN50I_H6_THS_TEMP_DATA +
+ regmap_read(tmdev->regmap, tmdev->chip->temp_data_base +
0x4 * s->id, &val);
/* ths have no data yet */
@@ -98,7 +99,7 @@ static int sun8i_ths_get_temp(void *data, int *temp)
* temperature above is also used when the sensor is calibrated. If
* do this, the correct calibration formula is hard to know.
*/
- *temp += SUN50I_H6_FT_DEVIATION;
+ *temp += tmdev->chip->ft_deviation;
return 0;
}
@@ -395,6 +396,10 @@ static int sun8i_ths_remove(struct platform_device *pdev)
static const struct ths_thermal_chip sun50i_h6_ths = {
.sensor_num = 2,
+ .offset = -2794,
+ .scale = -67,
+ .ft_deviation = SUN50I_H6_FT_DEVIATION,
+ .temp_data_base = SUN50I_H6_THS_TEMP_DATA,
};
static const struct of_device_id of_ths_match[] = {
--
2.17.1
^ permalink raw reply related
* [PATCH v4 06/11] thermal: sun8i: get ths init func from device compatible
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
There are some differences in register initialization for
different socs. So we get different initialization functions
from device compatible.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index e473a5651436..59acbbac76e4 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -59,6 +59,7 @@ struct ths_thermal_chip {
int scale;
int ft_deviation;
int temp_data_base;
+ int (*init)(struct ths_device *tmdev);
};
struct ths_device {
@@ -362,7 +363,7 @@ static int sun8i_ths_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- ret = sun50i_thermal_init(tmdev);
+ ret = tmdev->chip->init(tmdev);
if (ret)
return ret;
@@ -400,6 +401,7 @@ static const struct ths_thermal_chip sun50i_h6_ths = {
.scale = -67,
.ft_deviation = SUN50I_H6_FT_DEVIATION,
.temp_data_base = SUN50I_H6_THS_TEMP_DATA,
+ .init = sun50i_thermal_init,
};
static const struct of_device_id of_ths_match[] = {
--
2.17.1
^ permalink raw reply related
* [PATCH v4 08/11] thermal: sun8i: support ahb clocks
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
H3 has extra clock, so introduce something in ths_thermal_chip/ths_device
and adds the process of the clock.
This is pre-work for supprt it.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index ed1c19bb27cf..04f53ffb6a14 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -54,6 +54,7 @@ struct tsensor {
};
struct ths_thermal_chip {
+ bool has_ahb_clk;
int sensor_num;
int offset;
int scale;
@@ -69,6 +70,7 @@ struct ths_device {
struct regmap *regmap;
struct reset_control *reset;
struct clk *bus_clk;
+ struct clk *ahb_clk;
struct tsensor sensor[MAX_SENSOR_NUM];
};
@@ -280,6 +282,12 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
if (IS_ERR(tmdev->bus_clk))
return PTR_ERR(tmdev->bus_clk);
+ if (tmdev->chip->has_ahb_clk) {
+ tmdev->ahb_clk = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(tmdev->ahb_clk))
+ return PTR_ERR(tmdev->ahb_clk);
+ }
+
ret = reset_control_deassert(tmdev->reset);
if (ret)
return ret;
@@ -288,12 +296,18 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
if (ret)
goto assert_reset;
- ret = sun50i_ths_calibrate(tmdev);
+ ret = clk_prepare_enable(tmdev->ahb_clk);
if (ret)
goto bus_disable;
+ ret = sun50i_ths_calibrate(tmdev);
+ if (ret)
+ goto ahb_disable;
+
return 0;
+ahb_disable:
+ clk_disable_unprepare(tmdev->ahb_clk);
bus_disable:
clk_disable_unprepare(tmdev->bus_clk);
assert_reset:
@@ -401,6 +415,7 @@ static int sun8i_ths_remove(struct platform_device *pdev)
{
struct ths_device *tmdev = platform_get_drvdata(pdev);
+ clk_disable_unprepare(tmdev->ahb_clk);
clk_disable_unprepare(tmdev->bus_clk);
reset_control_assert(tmdev->reset);
--
2.17.1
^ permalink raw reply related
* [PATCH v4 09/11] thermal: sun8i: rework for ths calibrate func
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
Here, we do something to prepare for the subsequent
support of multiple platforms.
1) rename sun50i_ths_calibrate to sun8i_ths_calibrate, because
this function should be suitable for all platforms now.
2) introduce calibrate callback to mask calibration method
differences.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 86 ++++++++++++++++++---------------
1 file changed, 48 insertions(+), 38 deletions(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index 04f53ffb6a14..260b24340f5b 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -60,6 +60,8 @@ struct ths_thermal_chip {
int scale;
int ft_deviation;
int temp_data_base;
+ int (*calibrate)(struct ths_device *tmdev,
+ u16 *caldata, int callen);
int (*init)(struct ths_device *tmdev);
int (*irq_ack)(struct ths_device *tmdev);
};
@@ -152,45 +154,14 @@ static irqreturn_t sun8i_irq_thread(int irq, void *data)
return IRQ_HANDLED;
}
-static int sun50i_ths_calibrate(struct ths_device *tmdev)
+static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
+ u16 *caldata, int callen)
{
- struct nvmem_cell *calcell;
struct device *dev = tmdev->dev;
- u16 *caldata;
- size_t callen;
- int ft_temp;
- int i, ret = 0;
-
- calcell = devm_nvmem_cell_get(dev, "calib");
- if (IS_ERR(calcell)) {
- if (PTR_ERR(calcell) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- /*
- * Even if the external calibration data stored in sid is
- * not accessible, the THS hardware can still work, although
- * the data won't be so accurate.
- *
- * The default value of calibration register is 0x800 for
- * every sensor, and the calibration value is usually 0x7xx
- * or 0x8xx, so they won't be away from the default value
- * for a lot.
- *
- * So here we do not return error if the calibartion data is
- * not available, except the probe needs deferring.
- */
- goto out;
- }
+ int i, ft_temp;
- caldata = nvmem_cell_read(calcell, &callen);
- if (IS_ERR(caldata)) {
- ret = PTR_ERR(caldata);
- goto out;
- }
-
- if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num) {
- ret = -EINVAL;
- goto out_free;
- }
+ if (!caldata[0] || callen < 2 + 2 * tmdev->chip->sensor_num)
+ return -EINVAL;
/*
* efuse layout:
@@ -251,7 +222,45 @@ static int sun50i_ths_calibrate(struct ths_device *tmdev)
}
}
-out_free:
+ return 0;
+}
+
+static int sun8i_ths_calibrate(struct ths_device *tmdev)
+{
+ struct nvmem_cell *calcell;
+ struct device *dev = tmdev->dev;
+ u16 *caldata;
+ size_t callen;
+ int ret = 0;
+
+ calcell = devm_nvmem_cell_get(dev, "calib");
+ if (IS_ERR(calcell)) {
+ if (PTR_ERR(calcell) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ /*
+ * Even if the external calibration data stored in sid is
+ * not accessible, the THS hardware can still work, although
+ * the data won't be so accurate.
+ *
+ * The default value of calibration register is 0x800 for
+ * every sensor, and the calibration value is usually 0x7xx
+ * or 0x8xx, so they won't be away from the default value
+ * for a lot.
+ *
+ * So here we do not return error if the calibartion data is
+ * not available, except the probe needs deferring.
+ */
+ goto out;
+ }
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ ret = PTR_ERR(caldata);
+ goto out;
+ }
+
+ tmdev->chip->calibrate(tmdev, caldata, callen);
+
kfree(caldata);
out:
return ret;
@@ -300,7 +309,7 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
if (ret)
goto bus_disable;
- ret = sun50i_ths_calibrate(tmdev);
+ ret = sun8i_ths_calibrate(tmdev);
if (ret)
goto ahb_disable;
@@ -428,6 +437,7 @@ static const struct ths_thermal_chip sun50i_h6_ths = {
.scale = -67,
.ft_deviation = SUN50I_H6_FT_DEVIATION,
.temp_data_base = SUN50I_H6_THS_TEMP_DATA,
+ .calibrate = sun50i_h6_ths_calibrate,
.init = sun50i_thermal_init,
.irq_ack = sun50i_h6_irq_ack,
};
--
2.17.1
^ permalink raw reply related
* [PATCH v4 10/11] dt-bindings: thermal: add binding document for h3 thermal controller
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
This patch adds binding document for allwinner h3 thermal controller.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
.../bindings/thermal/sun8i-thermal.yaml | 29 +++++++++++++++++--
1 file changed, 26 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
index 2c5acc61ed03..1eaf68b5dd5a 100644
--- a/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
+++ b/Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
@@ -16,6 +16,7 @@ description: |-
properties:
compatible:
enum:
+ - allwinner,sun8i-h3-ths
- allwinner,sun50i-h6-ths
reg:
@@ -29,13 +30,22 @@ properties:
clocks:
minItems: 1
- maxItems: 1
+ maxItems: 2
clock-names:
- const: bus
+ items:
+ - const: bus
+ - const: ahb
'#thermal-sensor-cells':
- const: 1
+ enum: [ 0, 1 ]
+ description: |
+ Definition depends on soc version:
+
+ For "allwinner,sun8i-h3-ths",
+ value must be 0.
+ For "allwinner,sun50i-h6-ths",
+ value must be 1.
nvmem-cells:
items:
@@ -55,6 +65,19 @@ required:
- '#thermal-sensor-cells'
examples:
+ - |
+ ths: ths@1c25000 {
+ compatible = "allwinner,sun8i-h3-ths";
+ reg = <0x01c25000 0x400>;
+ clocks = <&ccu CLK_BUS_THS>, <&ccu CLK_THS>;
+ clock-names = "bus", "ahb";
+ resets = <&ccu RST_BUS_THS>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ nvmem-cells = <&tsen_calib>;
+ nvmem-cell-names = "calib";
+ #thermal-sensor-cells = <0>;
+ };
+
- |
ths: ths@5070400 {
compatible = "allwinner,sun50i-h6-ths";
--
2.17.1
^ permalink raw reply related
* [PATCH v4 11/11] thermal: sun8i: add thermal driver for h3
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
This patch adds the support for allwinner h3 thermal sensor.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 72 +++++++++++++++++++++++++++++++++
1 file changed, 72 insertions(+)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index 260b24340f5b..c8ee291f3b17 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -27,6 +27,14 @@
#define TEMP_TO_REG 672
#define CALIBRATE_DEFAULT 0x800
+#define SUN8I_THS_CTRL0 0x00
+#define SUN8I_THS_CTRL2 0x40
+#define SUN8I_THS_IC 0x44
+#define SUN8I_THS_IS 0x48
+#define SUN8I_THS_MFC 0x70
+#define SUN8I_THS_TEMP_CALIB 0x74
+#define SUN8I_THS_TEMP_DATA 0x80
+
#define SUN50I_THS_CTRL0 0x00
#define SUN50I_H6_THS_ENABLE 0x04
#define SUN50I_H6_THS_PC 0x08
@@ -36,6 +44,9 @@
#define SUN50I_H6_THS_TEMP_CALIB 0xa0
#define SUN50I_H6_THS_TEMP_DATA 0xc0
+#define SUN8I_THS_CTRL0_T_ACQ0(x) (GENMASK(15, 0) & (x))
+#define SUN8I_THS_CTRL2_T_ACQ1(x) ((GENMASK(15, 0) & (x)) << 16)
+
#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16)
#define SUN50I_THS_FILTER_EN BIT(2)
#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
@@ -121,6 +132,21 @@ static const struct regmap_config config = {
.fast_io = true,
};
+static int sun8i_h3_irq_ack(struct ths_device *tmdev)
+{
+ int state, ret = 0;
+
+ regmap_read(tmdev->regmap, SUN8I_THS_IS, &state);
+
+ if (state & BIT(8)) {
+ regmap_write(tmdev->regmap, SUN8I_THS_IS,
+ BIT(8));
+ ret |= BIT(1);
+ }
+
+ return ret;
+}
+
static int sun50i_h6_irq_ack(struct ths_device *tmdev)
{
int i, state, ret = 0;
@@ -154,6 +180,14 @@ static irqreturn_t sun8i_irq_thread(int irq, void *data)
return IRQ_HANDLED;
}
+static int sun8i_h3_ths_calibrate(struct ths_device *tmdev,
+ u16 *caldata, int callen)
+{
+ regmap_write(tmdev->regmap, SUN8I_THS_TEMP_CALIB, *caldata);
+
+ return 0;
+}
+
static int sun50i_h6_ths_calibrate(struct ths_device *tmdev,
u16 *caldata, int callen)
{
@@ -325,6 +359,32 @@ static int sun8i_ths_resource_init(struct ths_device *tmdev)
return ret;
}
+static int sun8i_h3_thermal_init(struct ths_device *tmdev)
+{
+ /* average over 4 samples */
+ regmap_write(tmdev->regmap, SUN8I_THS_MFC,
+ SUN50I_THS_FILTER_EN |
+ SUN50I_THS_FILTER_TYPE(1));
+ /*
+ * period = (x + 1) * 4096 / clkin; ~10ms
+ * enable data interrupt
+ */
+ regmap_write(tmdev->regmap, SUN8I_THS_IC,
+ SUN50I_H6_THS_PC_TEMP_PERIOD(58) | BIT(8));
+ /*
+ * clkin = 24MHz
+ * T acquire = clkin / (x + 1)
+ * = 20us
+ * enable sensor
+ */
+ regmap_write(tmdev->regmap, SUN8I_THS_CTRL0,
+ SUN8I_THS_CTRL0_T_ACQ0(479));
+ regmap_write(tmdev->regmap, SUN8I_THS_CTRL2,
+ SUN8I_THS_CTRL2_T_ACQ1(479) | BIT(0));
+
+ return 0;
+}
+
static int sun50i_thermal_init(struct ths_device *tmdev)
{
int val;
@@ -431,6 +491,17 @@ static int sun8i_ths_remove(struct platform_device *pdev)
return 0;
}
+static const struct ths_thermal_chip sun8i_h3_ths = {
+ .sensor_num = 1,
+ .offset = -1794,
+ .scale = -121,
+ .has_ahb_clk = true,
+ .temp_data_base = SUN8I_THS_TEMP_DATA,
+ .calibrate = sun8i_h3_ths_calibrate,
+ .init = sun8i_h3_thermal_init,
+ .irq_ack = sun8i_h3_irq_ack,
+};
+
static const struct ths_thermal_chip sun50i_h6_ths = {
.sensor_num = 2,
.offset = -2794,
@@ -443,6 +514,7 @@ static const struct ths_thermal_chip sun50i_h6_ths = {
};
static const struct of_device_id of_ths_match[] = {
+ { .compatible = "allwinner,sun8i-h3-ths", .data = &sun8i_h3_ths },
{ .compatible = "allwinner,sun50i-h6-ths", .data = &sun50i_h6_ths },
{ /* sentinel */ },
};
--
2.17.1
^ permalink raw reply related
* [PATCH v4 07/11] thermal: sun8i: rework for ths irq handler func
From: Yangtao Li @ 2019-06-23 16:42 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
Here, we do something to prepare for the subsequent
support of multiple platforms.
1) rename sun50i_h6_irq_thread to sun8i_irq_thread, because
this function should be suitable for all platforms.
2) introduce irq_ack callback to mask interrupt register
differences.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
drivers/thermal/sun8i_thermal.c | 27 ++++++++++++++++++++-------
1 file changed, 20 insertions(+), 7 deletions(-)
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
index 59acbbac76e4..ed1c19bb27cf 100644
--- a/drivers/thermal/sun8i_thermal.c
+++ b/drivers/thermal/sun8i_thermal.c
@@ -60,6 +60,7 @@ struct ths_thermal_chip {
int ft_deviation;
int temp_data_base;
int (*init)(struct ths_device *tmdev);
+ int (*irq_ack)(struct ths_device *tmdev);
};
struct ths_device {
@@ -116,23 +117,34 @@ static const struct regmap_config config = {
.fast_io = true,
};
-static irqreturn_t sun50i_h6_irq_thread(int irq, void *data)
+static int sun50i_h6_irq_ack(struct ths_device *tmdev)
{
- struct ths_device *tmdev = data;
- int i, state;
+ int i, state, ret = 0;
regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
for (i = 0; i < tmdev->chip->sensor_num; i++) {
-
if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
- /* clear data irq pending */
regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
SUN50I_H6_THS_DATA_IRQ_STS(i));
+ ret |= BIT(i);
+ }
+ }
+
+ return ret;
+}
+static irqreturn_t sun8i_irq_thread(int irq, void *data)
+{
+ struct ths_device *tmdev = data;
+ int i, state;
+
+ state = tmdev->chip->irq_ack(tmdev);
+
+ for (i = 0; i < tmdev->chip->sensor_num; i++) {
+ if (state & BIT(i))
thermal_zone_device_update(tmdev->sensor[i].tzd,
THERMAL_EVENT_UNSPECIFIED);
- }
}
return IRQ_HANDLED;
@@ -377,7 +389,7 @@ static int sun8i_ths_probe(struct platform_device *pdev)
* the end.
*/
ret = devm_request_threaded_irq(dev, irq, NULL,
- sun50i_h6_irq_thread,
+ sun8i_irq_thread,
IRQF_ONESHOT, "ths", tmdev);
if (ret)
return ret;
@@ -402,6 +414,7 @@ static const struct ths_thermal_chip sun50i_h6_ths = {
.ft_deviation = SUN50I_H6_FT_DEVIATION,
.temp_data_base = SUN50I_H6_THS_TEMP_DATA,
.init = sun50i_thermal_init,
+ .irq_ack = sun50i_h6_irq_ack,
};
static const struct of_device_id of_ths_match[] = {
--
2.17.1
^ permalink raw reply related
* [PATCH v4 01/11] thermal: sun8i: add thermal driver for h6
From: Yangtao Li @ 2019-06-23 16:41 UTC (permalink / raw)
To: rui.zhang, edubezval, daniel.lezcano, robh+dt, mark.rutland,
maxime.ripard, wens, davem, gregkh, mchehab+samsung,
linus.walleij, nicolas.ferre, paulmck
Cc: linux-pm, devicetree, linux-arm-kernel, linux-kernel, Yangtao Li
In-Reply-To: <20190623164206.7467-1-tiny.windzz@gmail.com>
This patch adds the support for allwinner thermal sensor, within
allwinner SoC. It will register sensors for thermal framework
and use device tree to bind cooling device.
Signed-off-by: Yangtao Li <tiny.windzz@gmail.com>
---
MAINTAINERS | 7 +
drivers/thermal/Kconfig | 14 ++
drivers/thermal/Makefile | 1 +
drivers/thermal/sun8i_thermal.c | 405 ++++++++++++++++++++++++++++++++
4 files changed, 427 insertions(+)
create mode 100644 drivers/thermal/sun8i_thermal.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 36a84614d6c3..67e7fcfaded2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -674,6 +674,13 @@ L: linux-crypto@vger.kernel.org
S: Maintained
F: drivers/crypto/sunxi-ss/
+ALLWINNER THERMAL DRIVER
+M: Yangtao Li <tiny.windzz@gmail.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/thermal/sun8i-thermal.yaml
+F: drivers/thermal/sun8i_thermal.c
+
ALLWINNER VPU DRIVER
M: Maxime Ripard <maxime.ripard@bootlin.com>
M: Paul Kocialkowski <paul.kocialkowski@bootlin.com>
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index 9966364a6deb..f8b73b32b92d 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -262,6 +262,20 @@ config SPEAR_THERMAL
Enable this to plug the SPEAr thermal sensor driver into the Linux
thermal framework.
+config SUN8I_THERMAL
+ tristate "Allwinner sun8i thermal driver"
+ depends on ARCH_SUNXI || COMPILE_TEST
+ depends on HAS_IOMEM
+ depends on NVMEM
+ depends on OF
+ depends on RESET_CONTROLLER
+ help
+ Support for the sun8i thermal sensor driver into the Linux thermal
+ framework.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sun8i-thermal.
+
config ROCKCHIP_THERMAL
tristate "Rockchip thermal driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 74a37c7f847a..fa6f8b206281 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -31,6 +31,7 @@ thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o
obj-y += broadcom/
obj-$(CONFIG_THERMAL_MMIO) += thermal_mmio.o
obj-$(CONFIG_SPEAR_THERMAL) += spear_thermal.o
+obj-$(CONFIG_SUN8I_THERMAL) += sun8i_thermal.o
obj-$(CONFIG_ROCKCHIP_THERMAL) += rockchip_thermal.o
obj-$(CONFIG_RCAR_THERMAL) += rcar_thermal.o
obj-$(CONFIG_RCAR_GEN3_THERMAL) += rcar_gen3_thermal.o
diff --git a/drivers/thermal/sun8i_thermal.c b/drivers/thermal/sun8i_thermal.c
new file mode 100644
index 000000000000..d6918c62682b
--- /dev/null
+++ b/drivers/thermal/sun8i_thermal.c
@@ -0,0 +1,405 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Thermal sensor driver for Allwinner SOC
+ * Copyright (C) 2019 Yangtao Li
+ *
+ * Based on the work of Icenowy Zheng <icenowy@aosc.io>
+ * Based on the work of Ondrej Jirman <megous@megous.com>
+ * Based on the work of Josef Gajdusek <atx@atx.name>
+ */
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/thermal.h>
+
+#define MAX_SENSOR_NUM 4
+
+#define SUN50I_H6_SENSOR_NUM 2
+#define SUN50I_H6_OFFSET -2794
+#define SUN50I_H6_SCALE -67
+
+#define FT_TEMP_MASK GENMASK(11, 0)
+#define TEMP_CALIB_MASK GENMASK(11, 0)
+#define TEMP_TO_REG 672
+#define CALIBRATE_DEFAULT 0x800
+
+#define SUN50I_THS_CTRL0 0x00
+#define SUN50I_H6_THS_ENABLE 0x04
+#define SUN50I_H6_THS_PC 0x08
+#define SUN50I_H6_THS_DIC 0x10
+#define SUN50I_H6_THS_DIS 0x20
+#define SUN50I_H6_THS_MFC 0x30
+#define SUN50I_H6_THS_TEMP_CALIB 0xa0
+#define SUN50I_H6_THS_TEMP_DATA 0xc0
+
+#define SUN50I_THS_CTRL0_T_ACQ(x) ((GENMASK(15, 0) & (x)) << 16)
+#define SUN50I_THS_FILTER_EN BIT(2)
+#define SUN50I_THS_FILTER_TYPE(x) (GENMASK(1, 0) & (x))
+#define SUN50I_H6_THS_PC_TEMP_PERIOD(x) ((GENMASK(19, 0) & (x)) << 12)
+#define SUN50I_H6_THS_DATA_IRQ_STS(x) BIT(x)
+
+/* millidegree celsius */
+#define SUN50I_H6_FT_DEVIATION 7000
+
+struct ths_device;
+
+struct tsensor {
+ struct ths_device *tmdev;
+ struct thermal_zone_device *tzd;
+ int id;
+};
+
+struct ths_device {
+ struct device *dev;
+ struct regmap *regmap;
+ struct reset_control *reset;
+ struct clk *bus_clk;
+ struct tsensor sensor[MAX_SENSOR_NUM];
+};
+
+/* Temp Unit: millidegree Celsius */
+static int sun8i_ths_reg2temp(struct ths_device *tmdev,
+ int reg)
+{
+ return (reg + SUN50I_H6_OFFSET) * SUN50I_H6_SCALE;
+}
+
+static int sun8i_ths_get_temp(void *data, int *temp)
+{
+ struct tsensor *s = data;
+ struct ths_device *tmdev = s->tmdev;
+ int val;
+
+ regmap_read(tmdev->regmap, SUN50I_H6_THS_TEMP_DATA +
+ 0x4 * s->id, &val);
+
+ /* ths have no data yet */
+ if (!val)
+ return -EBUSY;
+
+ *temp = sun8i_ths_reg2temp(tmdev, val);
+ /*
+ * XX - According to the original sdk, there are some platforms(rarely)
+ * that add a fixed offset value after calculating the temperature
+ * value. We can't simply put it on the formula for calculating the
+ * temperature above, because the formula for calculating the
+ * temperature above is also used when the sensor is calibrated. If
+ * do this, the correct calibration formula is hard to know.
+ */
+ *temp += SUN50I_H6_FT_DEVIATION;
+
+ return 0;
+}
+
+static const struct thermal_zone_of_device_ops ths_ops = {
+ .get_temp = sun8i_ths_get_temp,
+};
+
+static const struct regmap_config config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+};
+
+static irqreturn_t sun50i_h6_irq_thread(int irq, void *data)
+{
+ struct ths_device *tmdev = data;
+ int i, state;
+
+ regmap_read(tmdev->regmap, SUN50I_H6_THS_DIS, &state);
+
+ for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+
+ if (state & SUN50I_H6_THS_DATA_IRQ_STS(i)) {
+ /* clear data irq pending */
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_DIS,
+ SUN50I_H6_THS_DATA_IRQ_STS(i));
+
+ thermal_zone_device_update(tmdev->sensor[i].tzd,
+ THERMAL_EVENT_UNSPECIFIED);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int sun50i_ths_calibrate(struct ths_device *tmdev)
+{
+ struct nvmem_cell *calcell;
+ struct device *dev = tmdev->dev;
+ u16 *caldata;
+ size_t callen;
+ int ft_temp;
+ int i, ret = 0;
+
+ calcell = devm_nvmem_cell_get(dev, "calib");
+ if (IS_ERR(calcell)) {
+ if (PTR_ERR(calcell) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ /*
+ * Even if the external calibration data stored in sid is
+ * not accessible, the THS hardware can still work, although
+ * the data won't be so accurate.
+ *
+ * The default value of calibration register is 0x800 for
+ * every sensor, and the calibration value is usually 0x7xx
+ * or 0x8xx, so they won't be away from the default value
+ * for a lot.
+ *
+ * So here we do not return error if the calibartion data is
+ * not available, except the probe needs deferring.
+ */
+ goto out;
+ }
+
+ caldata = nvmem_cell_read(calcell, &callen);
+ if (IS_ERR(caldata)) {
+ ret = PTR_ERR(caldata);
+ goto out;
+ }
+
+ if (!caldata[0] || callen < 2 + 2 * SUN50I_H6_SENSOR_NUM) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+
+ /*
+ * efuse layout:
+ *
+ * 0 11 16 32
+ * +-------+-------+-------+
+ * |temp| |sensor0|sensor1|
+ * +-------+-------+-------+
+ *
+ * The calibration data on the H6 is the ambient temperature and
+ * sensor values that are filled during the factory test stage.
+ *
+ * The unit of stored FT temperature is 0.1 degreee celusis.
+ * Through the stored ambient temperature and the data read
+ * by the sensor, after a certain calculation, the calibration
+ * value to be compensated can be obtained.
+ */
+ ft_temp = caldata[0] & FT_TEMP_MASK;
+
+ for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+ int reg = (int)caldata[i + 1];
+ int sensor_temp = sun8i_ths_reg2temp(tmdev, reg);
+ int delta, cdata, calib_offest;
+
+ /*
+ * To calculate the calibration value:
+ *
+ * X(in Celsius) = Ts - ft_temp
+ * delta = X * 10000 / TEMP_TO_REG
+ * cdata = CALIBRATE_DEFAULT - delta
+ *
+ * cdata: calibration value
+ */
+ delta = (sensor_temp - ft_temp * 100) * 10 / TEMP_TO_REG;
+ cdata = CALIBRATE_DEFAULT - delta;
+ if (cdata & ~TEMP_CALIB_MASK) {
+ /*
+ * Calibration value more than 12-bit, but calibration
+ * register is 12-bit. In this case, ths hardware can
+ * still work without calibration, although the data
+ * won't be so accurate.
+ */
+ dev_warn(dev, "sensor%d is not calibrated.\n", i);
+
+ continue;
+ }
+
+ calib_offest = SUN50I_H6_THS_TEMP_CALIB + (i / 2) * 0x4;
+
+ if (i % 2) {
+ int val;
+
+ regmap_read(tmdev->regmap, calib_offest, &val);
+ val = (val & TEMP_CALIB_MASK) | (cdata << 16);
+ regmap_write(tmdev->regmap, calib_offest, val);
+ } else {
+ regmap_write(tmdev->regmap, calib_offest, cdata);
+ }
+ }
+
+out_free:
+ kfree(caldata);
+out:
+ return ret;
+}
+
+static int sun8i_ths_resource_init(struct ths_device *tmdev)
+{
+ struct device *dev = tmdev->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *mem;
+ void __iomem *base;
+ int ret;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(dev, mem);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ tmdev->regmap = devm_regmap_init_mmio(dev, base, &config);
+ if (IS_ERR(tmdev->regmap))
+ return PTR_ERR(tmdev->regmap);
+
+ tmdev->reset = devm_reset_control_get(dev, 0);
+ if (IS_ERR(tmdev->reset))
+ return PTR_ERR(tmdev->reset);
+
+ tmdev->bus_clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(tmdev->bus_clk))
+ return PTR_ERR(tmdev->bus_clk);
+
+ ret = reset_control_deassert(tmdev->reset);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(tmdev->bus_clk);
+ if (ret)
+ goto assert_reset;
+
+ ret = sun50i_ths_calibrate(tmdev);
+ if (ret)
+ goto bus_disable;
+
+ return 0;
+
+bus_disable:
+ clk_disable_unprepare(tmdev->bus_clk);
+assert_reset:
+ reset_control_assert(tmdev->reset);
+
+ return ret;
+}
+
+static int sun50i_thermal_init(struct ths_device *tmdev)
+{
+ int val;
+
+ /*
+ * clkin = 24MHz
+ * T acquire = clkin / (x + 1)
+ * = 20us
+ */
+ regmap_write(tmdev->regmap, SUN50I_THS_CTRL0,
+ SUN50I_THS_CTRL0_T_ACQ(479));
+ /* average over 4 samples */
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_MFC,
+ SUN50I_THS_FILTER_EN |
+ SUN50I_THS_FILTER_TYPE(1));
+ /* period = (x + 1) * 4096 / clkin; ~10ms */
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_PC,
+ SUN50I_H6_THS_PC_TEMP_PERIOD(58));
+ /* enable sensor */
+ val = GENMASK(SUN50I_H6_SENSOR_NUM - 1, 0);
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_ENABLE, val);
+ /* thermal data interrupt enable */
+ val = GENMASK(SUN50I_H6_SENSOR_NUM - 1, 0);
+ regmap_write(tmdev->regmap, SUN50I_H6_THS_DIC, val);
+
+ return 0;
+}
+
+static int sun8i_ths_register(struct ths_device *tmdev)
+{
+ struct thermal_zone_device *tzd;
+ int i;
+
+ for (i = 0; i < SUN50I_H6_SENSOR_NUM; i++) {
+ tmdev->sensor[i].tmdev = tmdev;
+ tmdev->sensor[i].id = i;
+ tmdev->sensor[i].tzd =
+ devm_thermal_zone_of_sensor_register(tmdev->dev,
+ i,
+ &tmdev->sensor[i],
+ &ths_ops);
+ if (IS_ERR(tmdev->sensor[i].tzd))
+ return PTR_ERR(tzd);
+ }
+
+ return 0;
+}
+
+static int sun8i_ths_probe(struct platform_device *pdev)
+{
+ struct ths_device *tmdev;
+ struct device *dev = &pdev->dev;
+ int ret, irq;
+
+ tmdev = devm_kzalloc(dev, sizeof(*tmdev), GFP_KERNEL);
+ if (!tmdev)
+ return -ENOMEM;
+
+ tmdev->dev = dev;
+ platform_set_drvdata(pdev, tmdev);
+
+ ret = sun8i_ths_resource_init(tmdev);
+ if (ret)
+ return ret;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ ret = sun50i_thermal_init(tmdev);
+ if (ret)
+ return ret;
+
+ ret = sun8i_ths_register(tmdev);
+ if (ret)
+ return ret;
+
+ /*
+ * Avoid entering the interrupt handler, the thermal device is not
+ * registered yet, we deffer the registration of the interrupt to
+ * the end.
+ */
+ ret = devm_request_threaded_irq(dev, irq, NULL,
+ sun50i_h6_irq_thread,
+ IRQF_ONESHOT, "ths", tmdev);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static int sun8i_ths_remove(struct platform_device *pdev)
+{
+ struct ths_device *tmdev = platform_get_drvdata(pdev);
+
+ clk_disable_unprepare(tmdev->bus_clk);
+ reset_control_assert(tmdev->reset);
+
+ return 0;
+}
+
+static const struct of_device_id of_ths_match[] = {
+ { .compatible = "allwinner,sun50i-h6-ths"},
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, of_ths_match);
+
+static struct platform_driver ths_driver = {
+ .probe = sun8i_ths_probe,
+ .remove = sun8i_ths_remove,
+ .driver = {
+ .name = "sun8i-thermal",
+ .of_match_table = of_ths_match,
+ },
+};
+module_platform_driver(ths_driver);
+
+MODULE_DESCRIPTION("Thermal sensor driver for Allwinner SOC");
+MODULE_LICENSE("GPL v2");
--
2.17.1
^ permalink raw reply related
* Re: [PATCH v1 2/3] OPP: Add function to look up required OPP's for a given OPP
From: Saravana Kannan @ 2019-06-23 6:07 UTC (permalink / raw)
To: cwchoi00
Cc: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki,
Android Kernel Team, Linux PM list, linux-kernel
In-Reply-To: <CAGTfZH2jK8s=5d_R7=kbUsPwE6s3fmz2_srZVyr32EU5qcB07Q@mail.gmail.com>
On Sat, Jun 22, 2019 at 9:28 PM Chanwoo Choi <cwchoi00@gmail.com> wrote:
>
> Hi,
>
> 2019년 6월 23일 (일) 오전 6:42, Saravana Kannan <saravanak@google.com>님이 작성:
> >
> > On Sat, Jun 22, 2019 at 4:50 AM Chanwoo Choi <cwchoi00@gmail.com> wrote:
> > >
> > > Hi,
> > >
> > > Absolutely, I like this approach. I think that it is necessary to make
> > > the connection
> > > between frequencies of devices.
> >
> > Happy to hear that.
> >
> > > But, I have a question on below.
> > >
> > > 2019년 6월 22일 (토) 오전 9:35, Saravana Kannan <saravanak@google.com>님이 작성:
> > > >
> > > > Add a function that allows looking up required OPPs given a source OPP
> > > > table, destination OPP table and the source OPP.
> > > >
> > > > Signed-off-by: Saravana Kannan <saravanak@google.com>
> > > > ---
> > > > drivers/opp/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++
> > > > include/linux/pm_opp.h | 11 +++++++++
> > > > 2 files changed, 65 insertions(+)
> > > >
> > > > diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> > > > index 74c7bdc6f463..4f7870bffbf8 100644
> > > > --- a/drivers/opp/core.c
> > > > +++ b/drivers/opp/core.c
> > > > @@ -1830,6 +1830,60 @@ void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
> > > > dev_err(virt_dev, "Failed to find required device entry\n");
> > > > }
> > > >
> > > > +/**
> > > > + * dev_pm_opp_xlate_opp() - Find required OPP for src_table OPP.
> > > > + * @src_table: OPP table which has dst_table as one of its required OPP table.
> > > > + * @dst_table: Required OPP table of the src_table.
> > > > + * @pstate: OPP of the src_table.
> > > > + *
> > > > + * This function returns the OPP (present in @dst_table) pointed out by the
> > > > + * "required-opps" property of the OPP (present in @src_table).
> > > > + *
> > > > + * The callers are required to call dev_pm_opp_put() for the returned OPP after
> > > > + * use.
> > > > + *
> > > > + * Return: destination table OPP on success, otherwise NULL on errors.
> > > > + */
> > > > +struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
> > > > + struct opp_table *dst_table,
> > > > + struct dev_pm_opp *src_opp)
> > > > +{
> > > > + struct dev_pm_opp *opp, *dest_opp = NULL;
> > > > + int i;
> > > > +
> > > > + if (!src_table || !dst_table || !src_opp)
> > > > + return NULL;
> > > > +
> > > > + for (i = 0; i < src_table->required_opp_count; i++) {
> > > > + if (src_table->required_opp_tables[i]->np == dst_table->np)
> > > > + break;
> > > > + }
> > > > +
> > > > + if (unlikely(i == src_table->required_opp_count)) {
> > > > + pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
> > > > + __func__, src_table, dst_table);
> > > > + return NULL;
> > > > + }
> > > > +
> > > > + mutex_lock(&src_table->lock);
> > > > +
> > > > + list_for_each_entry(opp, &src_table->opp_list, node) {
> > > > + if (opp == src_opp) {
> > > > + dest_opp = opp->required_opps[i];
> > >
> > > Correct me if I am wrong. This patch assume that 'i' index is same on between
> > > [1] and [2]. But in order to guarantee this assumption, all OPP entries
> > > in the same opp_table have to have the same number of 'required-opps' properties
> > > and keep the sequence among 'required-opps' entries.
> > >
> > > [1] src_table->required_opp_tables[i]->np
> > > [2] opp->required_opps[I];
> > >
> > > For example, three OPP entries in the 'parent_bus_opp'
> > > have the different sequence of 'required-opps' and the different
> > > number of 'required-opps'. Is it no problem?
> > >
> > > parent_bus_opp: opp_table {
> > > compatible = "operating-points-v2";
> > >
> > > opp2 {
> > > opp-hz = /bits/ 64 <200000>;
> > > required-opps = <&child_bus_a_opp2>, <&child_bus_b_opp2>,
> > > <&child_bus_c_opp2>;
> > > };
> > >
> > > opp1 {
> > > opp-hz = /bits/ 64 <200000>;
> > > // change the sequence between child_bus_b_opp2 and child_bus_c_opp2
> > > required-opps = <&child_bus_a_opp2>, <&child_bus_c_opp2>,
> > > <&child_bus_b_opp2>
> > > };
> > >
> > > opp0 {
> > > opp-hz = /bits/ 64 <200000>;
> > > // missing 'child_bus_a_opp2'
> > > required-opps = <&child_bus_c_opp2>, <&child_bus_b_opp2>
> > > };
> > >
> > > }
> > >
> >
> > I get your question. If I'm not mistaken the OPP framework DT parsing
> > code makes the assumption that the required-opps list has the phandles
> > in the same order for each "row" in the OPP table. It actually only
> > looks at the first OPP entry to figure out the list of required OPP
> > tables.
>
> Thanks for description. It is the limitation of 'required-opps' until now.
>
> >
> > Technically one can write code to deal with random order of the
> > required-opp list, but doesn't seem like that's worth it because
> > there's no need to have that order all mixed up in DT. And even if
> > someone wants to add support for that, I don't think improving the DT
> > parsing to handle random order would be part of this patch series.
>
> I understand the existing ' required-opps' only consider the same sequence
> of entries which are included in the same OPP table.
>
> One more thing, 'required-opps' properties doesn't support
> the other OPP enters of the different OPP table. Is it right of 'required-opps'?
Not sure I fully understand the question.
> Except for the random order, just each OPP might will requires
> the different 'required-opps' of different OPP table. Even if it is
> not related to random order, I think that this approach cannot
> support them.
>
> For example as following:
> - opp2 used the OPP entries of 'child_bus_A' and 'child_bus_B' opp-table.
> - opp1 used the OPP entries of 'child_bus_C' and 'child_bus_D' opp-table.
>
> parent_bus_opp: opp_table {
> compatible = "operating-points-v2";
>
> opp2 {
> opp-hz = /bits/ 64 <200000>;
I'm guessing this is a typo and let's assume you meant to sat 400000
> required-opps = <&child_bus_A_opp2>, <&child_bus_B_opp2>;
> };
>
> opp1 {
> opp-hz = /bits/ 64 <200000>;
> required-opps = <&child_bus_C_opp0>, <&child_bus_D_opp0>;
> };
> };
Is this a real use case? If it is, in reality parent_bus_opp_table
always has requirements on all 4 children bus, just that opp1 is okay
with the lowest frequency for some of the children?
So, in this example, you just need to always list all 4 child OPPs for
each parent OPP. And some of the children OPP values might not change
when going from one parent OPP to another.
-Saravana
^ permalink raw reply
* Re: [PATCH v1 2/3] OPP: Add function to look up required OPP's for a given OPP
From: Chanwoo Choi @ 2019-06-23 4:27 UTC (permalink / raw)
To: Saravana Kannan
Cc: MyungJoo Ham, Kyungmin Park, Chanwoo Choi, Viresh Kumar,
Nishanth Menon, Stephen Boyd, Rafael J. Wysocki,
Android Kernel Team, Linux PM list, linux-kernel
In-Reply-To: <CAGETcx9Gi24bng_PCqc6=9S584va4hRc4HHZtBLevKHgYGSNDA@mail.gmail.com>
Hi,
2019년 6월 23일 (일) 오전 6:42, Saravana Kannan <saravanak@google.com>님이 작성:
>
> On Sat, Jun 22, 2019 at 4:50 AM Chanwoo Choi <cwchoi00@gmail.com> wrote:
> >
> > Hi,
> >
> > Absolutely, I like this approach. I think that it is necessary to make
> > the connection
> > between frequencies of devices.
>
> Happy to hear that.
>
> > But, I have a question on below.
> >
> > 2019년 6월 22일 (토) 오전 9:35, Saravana Kannan <saravanak@google.com>님이 작성:
> > >
> > > Add a function that allows looking up required OPPs given a source OPP
> > > table, destination OPP table and the source OPP.
> > >
> > > Signed-off-by: Saravana Kannan <saravanak@google.com>
> > > ---
> > > drivers/opp/core.c | 54 ++++++++++++++++++++++++++++++++++++++++++
> > > include/linux/pm_opp.h | 11 +++++++++
> > > 2 files changed, 65 insertions(+)
> > >
> > > diff --git a/drivers/opp/core.c b/drivers/opp/core.c
> > > index 74c7bdc6f463..4f7870bffbf8 100644
> > > --- a/drivers/opp/core.c
> > > +++ b/drivers/opp/core.c
> > > @@ -1830,6 +1830,60 @@ void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
> > > dev_err(virt_dev, "Failed to find required device entry\n");
> > > }
> > >
> > > +/**
> > > + * dev_pm_opp_xlate_opp() - Find required OPP for src_table OPP.
> > > + * @src_table: OPP table which has dst_table as one of its required OPP table.
> > > + * @dst_table: Required OPP table of the src_table.
> > > + * @pstate: OPP of the src_table.
> > > + *
> > > + * This function returns the OPP (present in @dst_table) pointed out by the
> > > + * "required-opps" property of the OPP (present in @src_table).
> > > + *
> > > + * The callers are required to call dev_pm_opp_put() for the returned OPP after
> > > + * use.
> > > + *
> > > + * Return: destination table OPP on success, otherwise NULL on errors.
> > > + */
> > > +struct dev_pm_opp *dev_pm_opp_xlate_opp(struct opp_table *src_table,
> > > + struct opp_table *dst_table,
> > > + struct dev_pm_opp *src_opp)
> > > +{
> > > + struct dev_pm_opp *opp, *dest_opp = NULL;
> > > + int i;
> > > +
> > > + if (!src_table || !dst_table || !src_opp)
> > > + return NULL;
> > > +
> > > + for (i = 0; i < src_table->required_opp_count; i++) {
> > > + if (src_table->required_opp_tables[i]->np == dst_table->np)
> > > + break;
> > > + }
> > > +
> > > + if (unlikely(i == src_table->required_opp_count)) {
> > > + pr_err("%s: Couldn't find matching OPP table (%p: %p)\n",
> > > + __func__, src_table, dst_table);
> > > + return NULL;
> > > + }
> > > +
> > > + mutex_lock(&src_table->lock);
> > > +
> > > + list_for_each_entry(opp, &src_table->opp_list, node) {
> > > + if (opp == src_opp) {
> > > + dest_opp = opp->required_opps[i];
> >
> > Correct me if I am wrong. This patch assume that 'i' index is same on between
> > [1] and [2]. But in order to guarantee this assumption, all OPP entries
> > in the same opp_table have to have the same number of 'required-opps' properties
> > and keep the sequence among 'required-opps' entries.
> >
> > [1] src_table->required_opp_tables[i]->np
> > [2] opp->required_opps[I];
> >
> > For example, three OPP entries in the 'parent_bus_opp'
> > have the different sequence of 'required-opps' and the different
> > number of 'required-opps'. Is it no problem?
> >
> > parent_bus_opp: opp_table {
> > compatible = "operating-points-v2";
> >
> > opp2 {
> > opp-hz = /bits/ 64 <200000>;
> > required-opps = <&child_bus_a_opp2>, <&child_bus_b_opp2>,
> > <&child_bus_c_opp2>;
> > };
> >
> > opp1 {
> > opp-hz = /bits/ 64 <200000>;
> > // change the sequence between child_bus_b_opp2 and child_bus_c_opp2
> > required-opps = <&child_bus_a_opp2>, <&child_bus_c_opp2>,
> > <&child_bus_b_opp2>
> > };
> >
> > opp0 {
> > opp-hz = /bits/ 64 <200000>;
> > // missing 'child_bus_a_opp2'
> > required-opps = <&child_bus_c_opp2>, <&child_bus_b_opp2>
> > };
> >
> > }
> >
>
> I get your question. If I'm not mistaken the OPP framework DT parsing
> code makes the assumption that the required-opps list has the phandles
> in the same order for each "row" in the OPP table. It actually only
> looks at the first OPP entry to figure out the list of required OPP
> tables.
Thanks for description. It is the limitation of 'required-opps' until now.
>
> Technically one can write code to deal with random order of the
> required-opp list, but doesn't seem like that's worth it because
> there's no need to have that order all mixed up in DT. And even if
> someone wants to add support for that, I don't think improving the DT
> parsing to handle random order would be part of this patch series.
I understand the existing ' required-opps' only consider the same sequence
of entries which are included in the same OPP table.
One more thing, 'required-opps' properties doesn't support
the other OPP enters of the different OPP table. Is it right of 'required-opps'?
Except for the random order, just each OPP might will requires
the different 'required-opps' of different OPP table. Even if it is
not related to random order, I think that this approach cannot
support them.
For example as following:
- opp2 used the OPP entries of 'child_bus_A' and 'child_bus_B' opp-table.
- opp1 used the OPP entries of 'child_bus_C' and 'child_bus_D' opp-table.
parent_bus_opp: opp_table {
compatible = "operating-points-v2";
opp2 {
opp-hz = /bits/ 64 <200000>;
required-opps = <&child_bus_A_opp2>, <&child_bus_B_opp2>;
};
opp1 {
opp-hz = /bits/ 64 <200000>;
required-opps = <&child_bus_C_opp0>, <&child_bus_D_opp0>;
};
};
--
Best Regards,
Chanwoo Choi
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox