* Re: [PATCH 1/1] device core: Add flag to autoremove device link on supplier unbind
From: Rafael J. Wysocki @ 2018-05-30 10:59 UTC (permalink / raw)
To: Vivek Gautam
Cc: gregkh, lukas, dmitry.torokhov, aspriel, robin.murphy,
linux-kernel, linux-pm
In-Reply-To: <20180530095704.5788-1-vivek.gautam@codeaurora.org>
On 5/30/2018 11:57 AM, Vivek Gautam wrote:
> When using the device links without the consumers or
> suppliers maintaining pointers to these links, a flag can
> help in autoremoving the links on supplier driver unbind.
> We remove these links only when the supplier's link to its
> consumers has gone in DL_STATE_SUPPLIER_UNBIND state.
>
> Signed-off-by: Vivek Gautam <vivek.gautam@codeaurora.org>
> Suggested-by: Lukas Wunner <lukas@wunner.de>
> ---
>
> Lukas, as suggested in the thread [1] this change adds additional flag
> to autoremove device links on supplier unbind.
> For arm-smmu, we want to _not_ keep references to the device links
> added between arm-smmu, and consumer devices.
> Robin also pointed to [2] the need to autoremove the device link on
> supplier unbind rather than consumer unbind.
Please CC device links patches to linux-pm.
> [1] https://lkml.org/lkml/2018/3/14/390
> [2] https://lkml.org/lkml/2018/5/21/381
>
> drivers/base/core.c | 10 ++++++++++
> include/linux/device.h | 2 ++
> 2 files changed, 12 insertions(+)
>
> diff --git a/drivers/base/core.c b/drivers/base/core.c
> index b610816eb887..52c7222bb3c4 100644
> --- a/drivers/base/core.c
> +++ b/drivers/base/core.c
> @@ -490,6 +490,16 @@ void device_links_driver_cleanup(struct device *dev)
>
> WARN_ON(link->flags & DL_FLAG_AUTOREMOVE);
> WARN_ON(link->status != DL_STATE_SUPPLIER_UNBIND);
> +
> + /*
> + * autoremove the links between this @dev and its consumer
> + * devices that are not active, i.e. where the link state
> + * has moved to DL_STATE_SUPPLIER_UNBIND.
> + */
> + if (link->status == DL_STATE_SUPPLIER_UNBIND &&
> + link->flags & DL_FLAG_AUTOREMOVE_S)
> + kref_put(&link->kref, __device_link_del);
> +
> WRITE_ONCE(link->status, DL_STATE_DORMANT);
> }
>
> diff --git a/include/linux/device.h b/include/linux/device.h
> index 477956990f5e..6033bf58453d 100644
> --- a/include/linux/device.h
> +++ b/include/linux/device.h
> @@ -779,11 +779,13 @@ enum device_link_state {
> * AUTOREMOVE: Remove this link automatically on consumer driver unbind.
> * PM_RUNTIME: If set, the runtime PM framework will use this link.
> * RPM_ACTIVE: Run pm_runtime_get_sync() on the supplier during link creation.
> + * AUTOREMOVE_S: Remove this link automatically on supplier driver unbind.
> */
> #define DL_FLAG_STATELESS BIT(0)
> #define DL_FLAG_AUTOREMOVE BIT(1)
> #define DL_FLAG_PM_RUNTIME BIT(2)
> #define DL_FLAG_RPM_ACTIVE BIT(3)
> +#define DL_FLAG_AUTOREMOVE_S BIT(4)
Couldn't you invent a better name for this one?
>
> /**
> * struct device_link - Device link representation.
^ permalink raw reply
* [PATCH RESEND] serdev: add controller runtime PM support
From: Johan Hovold @ 2018-05-30 10:50 UTC (permalink / raw)
To: Greg Kroah-Hartman, Rob Herring
Cc: Sebastian Reichel, Tony Lindgren, H. Nikolaus Schaller,
Andreas Kemnade, Mark Rutland, Arnd Bergmann, Pavel Machek,
linux-kernel, linux-serial, linux-pm, Johan Hovold
Add support for controller runtime power management to serdev core. This
is needed to allow slave drivers to manage the runtime PM state of the
underlying serial controller when its driver, in turn, implements more
aggressive runtime power management (e.g. using autosuspend).
For some applications, for example, where loss off initial data after a
remote-wakeup event is acceptable or where rx is not used at all,
aggressive serial controller runtime PM may be used without further
involvement of the slave driver. But when this is not the case, the
slave driver must be able to indicate when incoming data is expected in
order to avoid data loss.
To facilitate the common case, where the serial controller power state
is active whenever the port is open (which is the case with just about
every serial driver), and where data loss is not acceptable and cannot
even be prevented by explicit controller runtime power management, an
RPM reference is taken in serdev open and put again at close. This
reference can later be balanced by any serdev driver which wants and/or
can handle aggressive controller runtime PM.
Note that the .ignore_children flag is set for the serdev controller to
allow the underlying hardware to idle when no I/O is expected, regardless
of the slave device RPM state.
Acked-by: Tony Lindgren <tony@atomide.com>
Reviewed-by: Sebastian Reichel <sebastian.reichel@collabora.co.uk>
Signed-off-by: Johan Hovold <johan@kernel.org>
---
Hi Rob and Greg,
This is a resend of the serdev controller runtime PM patch, which you
haven't commented on yet (possibly due to the following extensive
discussions on how to generalise the aggressive OMAP serial runtime PM
implementation).
This patch works with what we have today, regardless of how we end up
configuring the serial controller (active) runtime PM behaviour
(currently done through sysfs for OMAP), which is a separate issue.
No changes in this resend, besides me adding Tony's and Sebastian's ack
and reviewed-by tags and dropping the second patch which only served as
an example of how to use this in a serdev driver.
Johan
drivers/tty/serdev/core.c | 33 ++++++++++++++++++++++++++++++---
1 file changed, 30 insertions(+), 3 deletions(-)
diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c
index df93b727e984..e5e84303faca 100644
--- a/drivers/tty/serdev/core.c
+++ b/drivers/tty/serdev/core.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/serdev.h>
#include <linux/slab.h>
@@ -143,11 +144,28 @@ EXPORT_SYMBOL_GPL(serdev_device_remove);
int serdev_device_open(struct serdev_device *serdev)
{
struct serdev_controller *ctrl = serdev->ctrl;
+ int ret;
if (!ctrl || !ctrl->ops->open)
return -EINVAL;
- return ctrl->ops->open(ctrl);
+ ret = ctrl->ops->open(ctrl);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(&ctrl->dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(&ctrl->dev);
+ goto err_close;
+ }
+
+ return 0;
+
+err_close:
+ if (ctrl->ops->close)
+ ctrl->ops->close(ctrl);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(serdev_device_open);
@@ -158,6 +176,8 @@ void serdev_device_close(struct serdev_device *serdev)
if (!ctrl || !ctrl->ops->close)
return;
+ pm_runtime_put(&ctrl->dev);
+
ctrl->ops->close(ctrl);
}
EXPORT_SYMBOL_GPL(serdev_device_close);
@@ -416,6 +436,9 @@ struct serdev_controller *serdev_controller_alloc(struct device *parent,
dev_set_name(&ctrl->dev, "serial%d", id);
+ pm_runtime_no_callbacks(&ctrl->dev);
+ pm_suspend_ignore_children(&ctrl->dev, true);
+
dev_dbg(&ctrl->dev, "allocated controller 0x%p id %d\n", ctrl, id);
return ctrl;
@@ -547,20 +570,23 @@ int serdev_controller_add(struct serdev_controller *ctrl)
if (ret)
return ret;
+ pm_runtime_enable(&ctrl->dev);
+
ret_of = of_serdev_register_devices(ctrl);
ret_acpi = acpi_serdev_register_devices(ctrl);
if (ret_of && ret_acpi) {
dev_dbg(&ctrl->dev, "no devices registered: of:%d acpi:%d\n",
ret_of, ret_acpi);
ret = -ENODEV;
- goto out_dev_del;
+ goto err_rpm_disable;
}
dev_dbg(&ctrl->dev, "serdev%d registered: dev:%p\n",
ctrl->nr, &ctrl->dev);
return 0;
-out_dev_del:
+err_rpm_disable:
+ pm_runtime_disable(&ctrl->dev);
device_del(&ctrl->dev);
return ret;
};
@@ -591,6 +617,7 @@ void serdev_controller_remove(struct serdev_controller *ctrl)
dummy = device_for_each_child(&ctrl->dev, NULL,
serdev_remove_device);
+ pm_runtime_disable(&ctrl->dev);
device_del(&ctrl->dev);
}
EXPORT_SYMBOL_GPL(serdev_controller_remove);
--
2.17.0
^ permalink raw reply related
* [PATCH] OPP: Allow same OPP table to be used for multiple genpd
From: Viresh Kumar @ 2018-05-30 10:09 UTC (permalink / raw)
To: Rafael Wysocki, Viresh Kumar, Nishanth Menon, Stephen Boyd
Cc: Viresh Kumar, linux-pm, Vincent Guittot, Rajendra Nayak,
linux-kernel
The OPP binding says:
Property: operating-points-v2
...
This can contain more than one phandle for power domain
providers that provide multiple power domains. That is, one
phandle for each power domain. If only one phandle is available,
then the same OPP table will be used for all power domains
provided by the power domain provider.
But the OPP core isn't allowing the same OPP table to be used for
multiple domains. Update dev_pm_opp_of_add_table_indexed() to allow
that.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Tested-by: Rajendra Nayak <rnayak@codeaurora.org>
---
drivers/opp/of.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index 6d15f05bfc28..7af0ddec936b 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -554,11 +554,24 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_add_table);
int dev_pm_opp_of_add_table_indexed(struct device *dev, int index)
{
struct device_node *opp_np;
- int ret;
+ int ret, count;
+again:
opp_np = _opp_of_get_opp_desc_node(dev->of_node, index);
- if (!opp_np)
+ if (!opp_np) {
+ /*
+ * If only one phandle is present, then the same OPP table
+ * applies for all index requests.
+ */
+ count = of_count_phandle_with_args(dev->of_node,
+ "operating-points-v2", NULL);
+ if (count == 1 && index) {
+ index = 0;
+ goto again;
+ }
+
return -ENODEV;
+ }
ret = _of_add_opp_table_v2(dev, opp_np);
of_node_put(opp_np);
--
2.15.0.194.g9af6a3dea062
^ permalink raw reply related
* Re: [PATCH v2 0/9] PM / Domains: Add support for multi PM domains per device
From: Rafael J. Wysocki @ 2018-05-30 9:30 UTC (permalink / raw)
To: Ulf Hansson
Cc: Rafael J . Wysocki, Linux PM, Greg Kroah-Hartman, Jon Hunter,
Geert Uytterhoeven, Todor Tomov, Rajendra Nayak, Viresh Kumar,
Vincent Guittot, Kevin Hilman, Linux Kernel Mailing List,
Linux ARM, linux-tegra
In-Reply-To: <20180529100421.31022-1-ulf.hansson@linaro.org>
On Tue, May 29, 2018 at 12:04 PM, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> Changes in v2:
> - Addressed comments from Geert around DT doc.
> - Addressed comments from Jon around clarification of how to use this
> and changes to returned error codes.
> - Fixed build error in case CONFIG_PM was unset.
>
> There are devices that are partitioned across multiple PM domains. Currently
> these can't be supported well by the available PM infrastructures we have in
> the kernel. This series is an attempt to address this.
>
> The interesting parts happens from patch 5 an onwards, including a minor DT
> update to the existing power-domain bindings, the 4 earlier are just trivial
> clean-ups of some related code in genpd, which I happened to stumble over.
>
> Some additional background:
>
> One existing case where devices are partitioned across multiple PM domains, is
> the Nvida Tegra 124/210 X-USB subsystem. A while ago Jon Hunter (Nvidia) sent a
> series, trying to address these issues, however this is a new approach, while
> it re-uses the same concepts from DT point of view.
>
> The Tegra 124/210 X-USB subsystem contains of a host controller and a device
> controller. Each controller have its own independent PM domain, but are being
> partitioned across another shared PM domain for the USB super-speed logic.
>
> Currently to make the drivers work, either the related PM domains needs to stay
> powered on always or the PM domain topology needs to be in-correctly modelled
> through sub-domains. In both cases PM domains may be powered on while they
> don't need to be, so in the end this means - wasting power -.
>
> As stated above, this series intends to address these problem from a PM
> infrastructure point of view. More details are available in each changelog.
>
> It should be noted that this series has been tested on HW, however only by using
> a home-cooked test PM domain driver for genpd and together with a test driver.
> This allowed me to play with PM domain (genpd), runtime PM and device links.
>
> Any further deployment for real use cases are greatly appreciated. I am happy to
> to help, if needed!
>
> Kind regards
> Ulf Hansson
>
>
> Ulf Hansson (9):
> PM / Domains: Drop extern declarations of functions in pm_domain.h
> PM / Domains: Drop __pm_genpd_add_device()
> PM / Domains: Drop genpd as in-param for pm_genpd_remove_device()
> PM / Domains: Drop unused parameter in genpd_allocate_dev_data()
> PM / Domains: dt: Allow power-domain property to be a list of
> specifiers
> PM / Domains: Don't attach devices in genpd with multi PM domains
> PM / Domains: Split genpd_dev_pm_attach()
> PM / Domains: Add support for multi PM domains per device to genpd
> PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM
> domains
>
> .../bindings/power/power_domain.txt | 19 ++-
> drivers/base/power/common.c | 39 ++++-
> drivers/base/power/domain.c | 155 ++++++++++++++----
> drivers/gpu/drm/amd/amdgpu/amdgpu_acp.c | 2 +-
> include/linux/pm_domain.h | 79 ++++-----
> 5 files changed, 216 insertions(+), 78 deletions(-)
I can take patches [1-4/9] from this series for 4.18.
I need an ACK from Rob on the bindings change.
The rest of the series doesn't seem to be ready yet.
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH v2 9/9] PM / Domains: Add dev_pm_domain_attach_by_id() to manage multi PM domains
From: Jon Hunter @ 2018-05-30 9:19 UTC (permalink / raw)
To: Ulf Hansson, Rafael J . Wysocki, linux-pm
Cc: Greg Kroah-Hartman, Geert Uytterhoeven, Todor Tomov,
Rajendra Nayak, Viresh Kumar, Vincent Guittot, Kevin Hilman,
linux-kernel, linux-arm-kernel, linux-tegra
In-Reply-To: <20180529100421.31022-10-ulf.hansson@linaro.org>
Hi Ulf,
On 29/05/18 11:04, Ulf Hansson wrote:
> The existing dev_pm_domain_attach() function, allows a single PM domain to
> be attached per device. To be able to support devices that are partitioned
> across multiple PM domains, let's introduce a new interface,
> dev_pm_domain_attach_by_id().
>
> The dev_pm_domain_attach_by_id() returns a new allocated struct device with
> the corresponding attached PM domain. This enables for example a driver to
> operate on the new device from a power management point of view. The driver
> may then also benefit from using the received device, to set up so called
> device-links towards its original device. Depending on the situation, these
> links may then be dynamically changed.
I have given this series a go with Tegra updating the XHCI driver to make
use of these new APIs. Good news it does appear to work fine for Tegra,
however, initially when looking at the device_link_add() API ...
/**
* device_link_add - Create a link between two devices.
* @consumer: Consumer end of the link.
* @supplier: Supplier end of the link.
* @flags: Link flags.
... I had assumed that the 'consumer' device would be the actual XHCI
device (in the case of Tegra) and the 'supplier' device would be the new
genpd device. However, this did not work and I got the following WARN on
boot ...
[ 2.050929] ---[ end trace eff0b5265e530c92 ]---
[ 2.055567] WARNING: CPU: 2 PID: 1 at drivers/base/core.c:446 device_links_driver_bound+0xc0/0xd0
[ 2.064422] Modules linked in:
[ 2.067471] CPU: 2 PID: 1 Comm: swapper/0 Tainted: G W 4.17.0-rc7-next-20180529-00011-g4faf0dc0ebf3-dirty #32
[ 2.078667] Hardware name: Google Pixel C (DT)
[ 2.083101] pstate: 80000005 (Nzcv daif -PAN -UAO)
[ 2.087881] pc : device_links_driver_bound+0xc0/0xd0
[ 2.092832] lr : device_links_driver_bound+0x20/0xd0
Switching the Tegra XHCI device to be the 'supplier' and genpd device to
be the 'consumer' does work, but is this correct? Seems to be opposite to
what I expected. Maybe I am missing something?
> The new interface is typically called by drivers during their probe phase,
> in case they manages devices which uses multiple PM domains. If that is the
> case, the driver also becomes responsible of managing the detaching of the
> PM domains, which typically should be done at the remove phase. Detaching
> is done by calling the existing dev_pm_domain_detach() function and for
> each of the received devices from dev_pm_domain_attach_by_id().
>
> Note, currently its only genpd that supports multiple PM domains per
> device, but dev_pm_domain_attach_by_id() can easily by extended to cover
> other PM domain types, if/when needed.
>
> Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
> ---
>
> Changes in v2:
> - Fixed comments from Jon. Clarified function descriptions/changelog and
> return ERR_PTR(-EEXIST) in case a PM domain is already assigned.
> - Fix build error when CONFIG_PM is unset.
>
> ---
> drivers/base/power/common.c | 43 ++++++++++++++++++++++++++++++++++---
> include/linux/pm_domain.h | 7 ++++++
> 2 files changed, 47 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
> index 7ae62b6355b8..5e5ea0c239de 100644
> --- a/drivers/base/power/common.c
> +++ b/drivers/base/power/common.c
> @@ -116,14 +116,51 @@ int dev_pm_domain_attach(struct device *dev, bool power_on)
> }
> EXPORT_SYMBOL_GPL(dev_pm_domain_attach);
>
> +/**
> + * dev_pm_domain_attach_by_id - Attach a device to one of its PM domains.
> + * @dev: Device to attach.
Nit ... I still don't think this is the device we are attaching to, but the
device the PM domains are associated with. IOW we are using this device to
lookup the PM domains.
> + * @index: The index of the PM domain.
> + *
> + * As @dev may only be attached to a single PM domain, the backend PM domain
> + * provider creates a virtual device to attach instead. If attachment succeeds,
> + * the ->detach() callback in the struct dev_pm_domain are assigned by the
> + * corresponding backend attach function, as to deal with detaching of the
> + * created virtual device.
> + *
> + * This function should typically be invoked by a driver during the probe phase,
> + * in case its device requires power management through multiple PM domains. The
> + * driver may benefit from using the received device, to configure device-links
> + * towards its original device. Depending on the use-case and if needed, the
> + * links may be dynamically changed by the driver, which allows it to control
> + * the power to the PM domains independently from each other.
> + *
> + * Callers must ensure proper synchronization of this function with power
> + * management callbacks.
> + *
> + * Returns the virtual created device when successfully attached to its PM
> + * domain, NULL in case @dev don't need a PM domain, else an ERR_PTR().
> + * Note that, to detach the returned virtual device, the driver shall call
> + * dev_pm_domain_detach() on it, typically during the remove phase.
> + */
> +struct device *dev_pm_domain_attach_by_id(struct device *dev,
> + unsigned int index)
> +{
> + if (dev->pm_domain)
> + return ERR_PTR(-EEXIST);
> +
> + return genpd_dev_pm_attach_by_id(dev, index);
> +}
> +EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_id);
> +
> /**
> * dev_pm_domain_detach - Detach a device from its PM domain.
> * @dev: Device to detach.
> * @power_off: Used to indicate whether we should power off the device.
> *
> - * This functions will reverse the actions from dev_pm_domain_attach() and thus
> - * try to detach the @dev from its PM domain. Typically it should be invoked
> - * from subsystem level code during the remove phase.
> + * This functions will reverse the actions from dev_pm_domain_attach() and
> + * dev_pm_domain_attach_by_id(), thus it detaches @dev from its PM domain.
> + * Typically it should be invoked during the remove phase, either from
> + * subsystem level code or from drivers.
> *
> * Callers must ensure proper synchronization of this function with power
> * management callbacks.
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 82458e8e2e01..9206a4fef9ac 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -299,6 +299,8 @@ struct generic_pm_domain *of_genpd_remove_last(struct device_node *np)
>
> #ifdef CONFIG_PM
> int dev_pm_domain_attach(struct device *dev, bool power_on);
> +struct device *dev_pm_domain_attach_by_id(struct device *dev,
> + unsigned int index);
> void dev_pm_domain_detach(struct device *dev, bool power_off);
> void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
> #else
> @@ -306,6 +308,11 @@ static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
> {
> return 0;
> }
> +static inline struct device *dev_pm_domain_attach_by_id(struct device *dev,
> + unsigned int index)
> +{
> + return NULL;
> +}
> static inline void dev_pm_domain_detach(struct device *dev, bool power_off) {}
> static inline void dev_pm_domain_set(struct device *dev,
> struct dev_pm_domain *pd) {}
>
My only other comments on this series are ...
1. I think it would be nice to have an dev_pm_domain_attach_by_name() and
that the DT binding has a 'power-domain-names' property.
2. I am wondering if there could be value in having a
dev_pm_domain_attach_link_all() helper which would attach and link all
PM domains at once.
Cheers
Jon
--
nvpublic
^ permalink raw reply
* Re: [PATCH v14 0/2] Kryo CPU scaling driver
From: Viresh Kumar @ 2018-05-30 9:15 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Ilia Lin, Viresh Kumar, Nishanth Menon, Stephen Boyd, Rob Herring,
Mark Rutland, Rafael J. Wysocki, Linux PM,
devicetree@vger.kernel.org, Linux Kernel Mailing List
In-Reply-To: <CAJZ5v0gwtu-ptPskMeiXqWcRLhaGvvFVRyi-kX=nw_s6if7XGg@mail.gmail.com>
On 30-05-18, 10:08, Rafael J. Wysocki wrote:
> On Fri, May 25, 2018 at 2:07 PM, Ilia Lin <ilialin@codeaurora.org> wrote:
> > [v14]
> > * Addressed comment from Sudeep about DT compatible
> > * Added MAINTAINERS entry
>
> This causes a build issue to occur in my bleeding-edge branch.
>
> Does it depend on anything new in arm-soc?
He already sent a v15 and the changelog suggest he fixed that build
issue.
--
viresh
^ permalink raw reply
* Re: [PATCH v4 11/27] x86/power/64: Adapt assembly for PIE support
From: Rafael J. Wysocki @ 2018-05-30 8:20 UTC (permalink / raw)
To: Thomas Garnier
Cc: Kernel Hardening, Rafael J. Wysocki, Pavel Machek,
Thomas Gleixner, Ingo Molnar, H. Peter Anvin,
the arch/x86 maintainers, Linux PM, Linux Kernel Mailing List
In-Reply-To: <20180529221625.33541-12-thgarnie@google.com>
On Wed, May 30, 2018 at 12:15 AM, Thomas Garnier <thgarnie@google.com> wrote:
> Change the assembly code to use only relative references of symbols for the
> kernel to be PIE compatible.
>
> Position Independent Executable (PIE) support will allow to extend the
> KASLR randomization range 0xffffffff80000000.
>
> Signed-off-by: Thomas Garnier <thgarnie@google.com>
> Acked-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
> arch/x86/power/hibernate_asm_64.S | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/arch/x86/power/hibernate_asm_64.S b/arch/x86/power/hibernate_asm_64.S
> index ce8da3a0412c..6fdd7bbc3c33 100644
> --- a/arch/x86/power/hibernate_asm_64.S
> +++ b/arch/x86/power/hibernate_asm_64.S
> @@ -24,7 +24,7 @@
> #include <asm/frame.h>
>
> ENTRY(swsusp_arch_suspend)
> - movq $saved_context, %rax
> + leaq saved_context(%rip), %rax
> movq %rsp, pt_regs_sp(%rax)
> movq %rbp, pt_regs_bp(%rax)
> movq %rsi, pt_regs_si(%rax)
> @@ -115,7 +115,7 @@ ENTRY(restore_registers)
> movq %rax, %cr4; # turn PGE back on
>
> /* We don't restore %rax, it must be 0 anyway */
> - movq $saved_context, %rax
> + leaq saved_context(%rip), %rax
> movq pt_regs_sp(%rax), %rsp
> movq pt_regs_bp(%rax), %rbp
> movq pt_regs_si(%rax), %rsi
> --
> 2.17.0.921.gf22659ad46-goog
>
^ permalink raw reply
* Re: [PATCH v4 09/27] x86/acpi: Adapt assembly for PIE support
From: Rafael J. Wysocki @ 2018-05-30 8:19 UTC (permalink / raw)
To: Thomas Garnier
Cc: Kernel Hardening, Rafael J. Wysocki, Len Brown, Pavel Machek,
Thomas Gleixner, Ingo Molnar, H. Peter Anvin,
the arch/x86 maintainers, Linux PM, Linux Kernel Mailing List
In-Reply-To: <20180529221625.33541-10-thgarnie@google.com>
On Wed, May 30, 2018 at 12:15 AM, Thomas Garnier <thgarnie@google.com> wrote:
> Change the assembly code to use only relative references of symbols for the
> kernel to be PIE compatible.
>
> Position Independent Executable (PIE) support will allow to extend the
> KASLR randomization range 0xffffffff80000000.
>
> Signed-off-by: Thomas Garnier <thgarnie@google.com>
> Acked-by: Pavel Machek <pavel@ucw.cz>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
> ---
> arch/x86/kernel/acpi/wakeup_64.S | 31 ++++++++++++++++---------------
> 1 file changed, 16 insertions(+), 15 deletions(-)
>
> diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S
> index 50b8ed0317a3..472659c0f811 100644
> --- a/arch/x86/kernel/acpi/wakeup_64.S
> +++ b/arch/x86/kernel/acpi/wakeup_64.S
> @@ -14,7 +14,7 @@
> * Hooray, we are in Long 64-bit mode (but still running in low memory)
> */
> ENTRY(wakeup_long64)
> - movq saved_magic, %rax
> + movq saved_magic(%rip), %rax
> movq $0x123456789abcdef0, %rdx
> cmpq %rdx, %rax
> jne bogus_64_magic
> @@ -25,14 +25,14 @@ ENTRY(wakeup_long64)
> movw %ax, %es
> movw %ax, %fs
> movw %ax, %gs
> - movq saved_rsp, %rsp
> + movq saved_rsp(%rip), %rsp
>
> - movq saved_rbx, %rbx
> - movq saved_rdi, %rdi
> - movq saved_rsi, %rsi
> - movq saved_rbp, %rbp
> + movq saved_rbx(%rip), %rbx
> + movq saved_rdi(%rip), %rdi
> + movq saved_rsi(%rip), %rsi
> + movq saved_rbp(%rip), %rbp
>
> - movq saved_rip, %rax
> + movq saved_rip(%rip), %rax
> jmp *%rax
> ENDPROC(wakeup_long64)
>
> @@ -45,7 +45,7 @@ ENTRY(do_suspend_lowlevel)
> xorl %eax, %eax
> call save_processor_state
>
> - movq $saved_context, %rax
> + leaq saved_context(%rip), %rax
> movq %rsp, pt_regs_sp(%rax)
> movq %rbp, pt_regs_bp(%rax)
> movq %rsi, pt_regs_si(%rax)
> @@ -64,13 +64,14 @@ ENTRY(do_suspend_lowlevel)
> pushfq
> popq pt_regs_flags(%rax)
>
> - movq $.Lresume_point, saved_rip(%rip)
> + leaq .Lresume_point(%rip), %rax
> + movq %rax, saved_rip(%rip)
>
> - movq %rsp, saved_rsp
> - movq %rbp, saved_rbp
> - movq %rbx, saved_rbx
> - movq %rdi, saved_rdi
> - movq %rsi, saved_rsi
> + movq %rsp, saved_rsp(%rip)
> + movq %rbp, saved_rbp(%rip)
> + movq %rbx, saved_rbx(%rip)
> + movq %rdi, saved_rdi(%rip)
> + movq %rsi, saved_rsi(%rip)
>
> addq $8, %rsp
> movl $3, %edi
> @@ -82,7 +83,7 @@ ENTRY(do_suspend_lowlevel)
> .align 4
> .Lresume_point:
> /* We don't restore %rax, it must be 0 anyway */
> - movq $saved_context, %rax
> + leaq saved_context(%rip), %rax
> movq saved_context_cr4(%rax), %rbx
> movq %rbx, %cr4
> movq saved_context_cr3(%rax), %rbx
> --
> 2.17.0.921.gf22659ad46-goog
>
^ permalink raw reply
* Re: [PATCH v14 0/2] Kryo CPU scaling driver
From: Rafael J. Wysocki @ 2018-05-30 8:08 UTC (permalink / raw)
To: Ilia Lin
Cc: Viresh Kumar, Nishanth Menon, Stephen Boyd, Rob Herring,
Mark Rutland, Rafael J. Wysocki, Linux PM,
devicetree@vger.kernel.org, Linux Kernel Mailing List
In-Reply-To: <1527250067-1256-1-git-send-email-ilialin@codeaurora.org>
On Fri, May 25, 2018 at 2:07 PM, Ilia Lin <ilialin@codeaurora.org> wrote:
> [v14]
> * Addressed comment from Sudeep about DT compatible
> * Added MAINTAINERS entry
This causes a build issue to occur in my bleeding-edge branch.
Does it depend on anything new in arm-soc?
Thanks,
Rafael
^ permalink raw reply
* Re: [PATCH 09/11] misc: throttler: Add core support for non-thermal throttling
From: Chanwoo Choi @ 2018-05-30 8:08 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: MyungJoo Ham, Kyungmin Park, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson
In-Reply-To: <20180529205748.GJ168650@google.com>
Hi,
On 2018년 05월 30일 05:57, Matthias Kaehlcke wrote:
> On Mon, May 28, 2018 at 04:32:37PM +0900, Chanwoo Choi wrote:
>
>> IMHO, you better to split out the devfreq patches from
>> 'throttler' patch set. Because I'm not sure throttler is either
>> necessary or not.
>>
>> After finishing the review of 'throttler' patches without devfreq handling,
>> it would be better for you to send devfreq patches separately.
>
> I could certainly try to get 'throttler' with only cpufreq support
> merged, but that would kind of defeat the purpose.
>
> I first sent a RFC patch for the devfreq policy notifiers
> (https://patchwork.kernel.org/patch/10401999/) to get an idea if this
> is a reasonable path to pursue. In response you asked about "real code
> and patches" and here it is :)
>
> For my use case throttler is not really useful without devfreq
> support. In this sense I prefer to know 'early' if there are any
> blocking issues, rather then making the effort to get a limited
> version of the driver merged, and then learn that I wasted my own and
> the reviewers time because it is a dead end.
I'm never force to you. Just my opinion is how to make the patches
including the new concept. Thanks for your explanation why you send
the patch set with devfreq.
>
>> On 2018년 05월 26일 05:30, Matthias Kaehlcke wrote:
>>> The purpose of the throttler is to provide support for non-thermal
>>> throttling. Throttling is triggered by external event, e.g. the
>>> detection of a high battery discharge current, close to the OCP limit
>>> of the battery. The throttler is only in charge of the throttling, not
>>> the monitoring, which is done by another (possibly platform specific)
>>> driver.
>>>
>>> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
>>> ---
>>> drivers/misc/Kconfig | 1 +
>>> drivers/misc/Makefile | 1 +
>>> drivers/misc/throttler/Kconfig | 13 ++
>>> drivers/misc/throttler/Makefile | 1 +
>>> drivers/misc/throttler/core.c | 373 ++++++++++++++++++++++++++++++++
>>> include/linux/throttler.h | 10 +
>>> 6 files changed, 399 insertions(+)
>>> create mode 100644 drivers/misc/throttler/Kconfig
>>> create mode 100644 drivers/misc/throttler/Makefile
>>> create mode 100644 drivers/misc/throttler/core.c
>>> create mode 100644 include/linux/throttler.h
>>>
>>> diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
>>> index 5d713008749b..691d9625d83c 100644
>>> --- a/drivers/misc/Kconfig
>>> +++ b/drivers/misc/Kconfig
>>> @@ -513,4 +513,5 @@ source "drivers/misc/echo/Kconfig"
>>> source "drivers/misc/cxl/Kconfig"
>>> source "drivers/misc/ocxl/Kconfig"
>>> source "drivers/misc/cardreader/Kconfig"
>>> +source "drivers/misc/throttler/Kconfig"
>>> endmenu
>>> diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
>>> index 20be70c3f118..01a1714dd2ad 100644
>>> --- a/drivers/misc/Makefile
>>> +++ b/drivers/misc/Makefile
>>> @@ -57,3 +57,4 @@ obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o
>>> obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o
>>> obj-$(CONFIG_OCXL) += ocxl/
>>> obj-$(CONFIG_MISC_RTSX) += cardreader/
>>> +obj-y += throttler/
>>> diff --git a/drivers/misc/throttler/Kconfig b/drivers/misc/throttler/Kconfig
>>> new file mode 100644
>>> index 000000000000..ef8388f6bc0a
>>> --- /dev/null
>>> +++ b/drivers/misc/throttler/Kconfig
>>> @@ -0,0 +1,13 @@
>>> +menuconfig THROTTLER
>>> + bool "Throttler support"
>>> + default n
>>> + depends on OF
>>> + select CPU_FREQ
>>> + select PM_DEVFREQ
>>> + help
>>> + This option enables core support for non-thermal throttling of CPUs
>>> + and devfreq devices.
>>> +
>>> + Note that you also need a event monitor module usually called
>>> + *_throttler.
>>> +
>>> diff --git a/drivers/misc/throttler/Makefile b/drivers/misc/throttler/Makefile
>>> new file mode 100644
>>> index 000000000000..c8d920cee315
>>> --- /dev/null
>>> +++ b/drivers/misc/throttler/Makefile
>>> @@ -0,0 +1 @@
>>> +obj-$(CONFIG_THROTTLER) += core.o
>>> diff --git a/drivers/misc/throttler/core.c b/drivers/misc/throttler/core.c
>>> new file mode 100644
>>> index 000000000000..c058d03212b8
>>> --- /dev/null
>>> +++ b/drivers/misc/throttler/core.c
>>> @@ -0,0 +1,373 @@
>>> +/*
>>> + * Core code for non-thermal throttling
>>> + *
>>> + * Copyright (C) 2018 Google, Inc.
>>> + *
>>> + * 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/cpufreq.h>
>>> +#include <linux/devfreq.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/notifier.h>
>>> +#include <linux/of.h>
>>> +#include <linux/of_platform.h>
>>> +#include <linux/platform_device.h>
>>> +
>>> +/*
>>> + * Non-thermal throttling: throttling of system components in response to
>>> + * external events (e.g. high battery discharge current).
>>> + *
>>> + * The throttler supports throttling through cpufreq and devfreq. Multiple
>>> + * levels of throttling can be configured. At level 0 no throttling is
>>> + * active on behalf of the throttler, for values > 0 throttling is typically
>>> + * configured to be increasingly aggressive with each level.
>>> + * The number of throttling levels is not limited by the throttler (though
>>> + * it is likely limited by the throttling devices). It is not necessary to
>>> + * configure the same number of levels for all throttling devices. If the
>>> + * requested throttling level for a device is higher than the maximum level
>>> + * of the device the throttler will sleect the maximum throttling level of
>>> + * the device.
>>> + *
>>> + * Non-thermal throttling is split in two parts:
>>> + *
>>> + * - throttler core
>>> + * - parses the thermal policy
>>> + * - applies throttling settings for a requested level of throttling
>>> + *
>>> + * - event monitor driver
>>> + * - monitors the events that trigger throttling
>>> + * - determines the throttling level (often limited to on/off)
>>> + * - requests throttler core to apply throttling settings
>>> + *
>>> + * It is possible for a system to have more than one throttler and the
>>> + * throttlers may make use of the same throttling devices, in case of
>>> + * conflicting settings for a device the more aggressive values will be
>>> + * applied.
>>> + *
>>> + */
>>> +
>>> +struct thrcfg {
>>> + uint32_t *freqs;
>>> + int num_levels;
>>> +};
>>> +
>>> +struct cpufreq_thrdev {
>>> + uint32_t cpu;
>>> + struct thrcfg cfg;
>>> +};
>>> +
>>> +struct devfreq_thrdev {
>>> + struct devfreq *devfreq;
>>> + struct thrcfg cfg;
>>> + struct throttler *thr;
>>> + struct notifier_block nb;
>>> +};
>>> +
>>> +struct __thr_cpufreq {
>>> + struct cpufreq_thrdev *devs;
>>> + int ndevs;
>>> + struct notifier_block nb;
>>> +};
>>> +
>>> +struct __thr_devfreq {
>>> + struct devfreq_thrdev *devs;
>>> + int ndevs;
>>> +};
>>> +
>>> +struct throttler {
>>> + struct device *dev;
>>> + int level;
>>> + struct __thr_cpufreq cpufreq;
>>> + struct __thr_devfreq devfreq;
>>> +};
>>> +
>>> +static unsigned long thr_get_throttling_freq(struct thrcfg *cfg, int level)
>>> +{
>>> + if (level == 0 ) {
>>> + WARN(true, "level == 0");
>>> + return 0;
>>> + }
>>> +
>>> + if (level <= cfg->num_levels)
>>> + return cfg->freqs[level - 1];
>>> + else
>>> + return cfg->freqs[cfg->num_levels - 1];
>>> +}
>>> +
>>> +static int thr_cpufreq_event(struct notifier_block *nb,
>>> + unsigned long event, void *data)
>>> +{
>>> + struct throttler *thr =
>>> + container_of(nb, struct throttler, cpufreq.nb);
>>> + struct cpufreq_policy *policy = data;
>>> + struct cpufreq_thrdev *ctd;
>>> + int i;
>>> +
>>> + if ((event != CPUFREQ_ADJUST) || (thr->level == 0))
>>> + return NOTIFY_DONE;
>>> +
>>> + for (i = 0; i < thr->cpufreq.ndevs; i++) {
>>> + ctd = &thr->cpufreq.devs[i];
>>> +
>>> + if (ctd->cpu == policy->cpu) {
>>> + unsigned long clamp_freq =
>>> + thr_get_throttling_freq(&ctd->cfg, thr->level);
>>> + if (clamp_freq < policy->max) {
>>> + cpufreq_verify_within_limits(policy, 0, clamp_freq);
>>> + }
>>> + }
>>> + }
>>> +
>>> + return NOTIFY_DONE;
>>> +}
>>> +
>>> +static int thr_devfreq_event(struct notifier_block *nb,
>>> + unsigned long event, void *data)
>>> +{
>>> + struct devfreq_thrdev *dtd =
>>> + container_of(nb, struct devfreq_thrdev, nb);
>>> + struct throttler *thr = dtd->thr;
>>> + struct devfreq_policy *policy = data;
>>> + unsigned long clamp_freq;
>>> +
>>> + if ((event != DEVFREQ_ADJUST) || (thr->level == 0))
>>> + return NOTIFY_DONE;
>>> +
>>> + clamp_freq = thr_get_throttling_freq(&dtd->cfg, thr->level);
>>> + if (clamp_freq < policy->max)
>>> + devfreq_verify_within_limits(policy, 0, clamp_freq);
>>> +
>>> + return NOTIFY_DONE;
>>> +}
>>> +
>>> +static void thr_cpufreq_update_policy(struct throttler *thr)
>>> +{
>>> + int i;
>>> +
>>> + for (i = 0; i < thr->cpufreq.ndevs; i++) {
>>> + struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
>>> + struct cpufreq_policy *policy = cpufreq_cpu_get(ctd->cpu);
>>> +
>>> + if (!policy) {
>>> + dev_warn(thr->dev, "CPU%d does have no cpufreq policy!\n", ctd->cpu);
>>> + continue;
>>> + }
>>> +
>>> + cpufreq_update_policy(ctd->cpu);
>>> + cpufreq_cpu_put(policy);
>>> + }
>>> +}
>>> +
>>> +static int thr_parse_thrcfg(struct throttler *thr,
>>> + struct device_node *np, struct thrcfg *cfg) {
>>> + int err;
>>> +
>>> + cfg->num_levels =
>>> + of_property_count_u32_elems(np, "throttling-frequencies");
>>> + if (cfg->num_levels < 0) {
>>> + pr_err("%s: failed to determine number of throttling frequencies\n",
>>> + np->full_name);
>>> + return cfg->num_levels;
>>> + }
>>> +
>>> + cfg->freqs = devm_kzalloc(thr->dev,
>>> + cfg->num_levels * sizeof(u32), GFP_KERNEL);
>>> + if (!cfg->freqs)
>>> + return -ENOMEM;
>>> +
>>> + err = of_property_read_u32_array(np, "throttling-frequencies",
>>> + cfg->freqs, cfg->num_levels);
>>> + if (err) {
>>> + pr_err("%s: failed to read throttling frequencies\n", np->full_name);
>>> + return err;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static struct devfreq *thr_find_devfreq_dev(struct throttler *thr,
>>> + struct device_node *np_df) {
>>> + struct device_node *node;
>>> + struct platform_device *pdev;
>>> +
>>> + node = of_parse_phandle(np_df, "device", 0);
>>> + if (!node) {
>>> + pr_err("%s: failed to get devfreq parent device\n",
>>> + np_df->full_name);
>>> + return ERR_PTR(-EINVAL);
>>> + }
>>> +
>>> + pdev = of_find_device_by_node(node);
>>> + if (!pdev) {
>>> + pr_err("%s: could not find devfreq parent device\n",
>>> + node->full_name);
>>> + return ERR_PTR(-EINVAL);
>>> + }
>>> +
>>> + return dev_to_devfreq(&pdev->dev);
>>> +}
>>> +
>>> +static int thr_parse_dt(struct throttler *thr, struct device_node *np)
>>> +{
>>> + struct device_node *node, *child;
>>> + int err, i;
>>> +
>>> + node = of_get_child_by_name(np, "cpufreq");
>>> + if (node) {
>>> + thr->cpufreq.ndevs = of_get_child_count(node);
>>> + thr->cpufreq.devs = devm_kzalloc(thr->dev,
>>> + sizeof(*thr->cpufreq.devs) * thr->cpufreq.ndevs,
>>> + GFP_KERNEL);
>>> +
>>> + i = 0;
>>> + for_each_child_of_node(node, child) {
>>> + struct cpufreq_thrdev *ctd = &thr->cpufreq.devs[i];
>>> +
>>> + err = of_property_read_u32(child, "cpu", &ctd->cpu);
>>> + if (err) {
>>> + pr_err("%s: failed to read CPU id\n", child->full_name);
>>> + return err;
>>> + }
>>> +
>>> + err = thr_parse_thrcfg(thr, child, &ctd->cfg);
>>> + if (err)
>>> + return err;
>>> +
>>> + i++;
>>> + }
>>> + }
>>> +
>>> + node = of_get_child_by_name(np, "devfreq");
>>> + if (node) {
>>> + thr->devfreq.ndevs = of_get_child_count(node);
>>> + thr->devfreq.devs = devm_kzalloc(thr->dev,
>>> + sizeof(*thr->devfreq.devs) * thr->devfreq.ndevs,
>>> + GFP_KERNEL);
>>> +
>>> + i = 0;
>>> + for_each_child_of_node(node, child) {
>>> + struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
>>> +
>>> + dtd->thr = thr;
>>> +
>>> + dtd->devfreq = thr_find_devfreq_dev(thr, child);
>>> + if (IS_ERR(dtd->devfreq))
>>> + return PTR_ERR(dtd->devfreq);
>>> +
>>> + err = thr_parse_thrcfg(thr, child, &dtd->cfg);
>>> + if (err)
>>> + return err;
>>> +
>>> + i++;
>>> + }
>>> + }
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static void thr_update_devfreq(struct devfreq *devfreq)
>>> +{
>>> + mutex_lock(&devfreq->lock);
>>> + update_devfreq(devfreq);
>>> + mutex_unlock(&devfreq->lock);
>>> +}
>>> +
>>> +void throttler_set_level(struct throttler *thr, int level)
>>> +{
>>> + int i;
>>> +
>>> + if (level == thr->level)
>>> + return;
>>> +
>>> + dev_dbg(thr->dev, "throttling level: %d\n", level);
>>> + thr->level = level;
>>> +
>>> + if (thr->cpufreq.ndevs > 0)
>>> + thr_cpufreq_update_policy(thr);
>>> +
>>> + if (thr->devfreq.ndevs > 0)
>>> + for (i = 0; i < thr->devfreq.ndevs; i++)
>>> + thr_update_devfreq(thr->devfreq.devs[i].devfreq);
>>> +}
>>> +EXPORT_SYMBOL_GPL(throttler_set_level);
>>> +
>>> +struct throttler *throttler_setup(struct device *dev)
>>> +{
>>> + struct throttler *thr;
>>> + struct device_node *np = dev->of_node;
>>> + int err, i;
>>> +
>>> + if (!np)
>>> + /* should never happen */
>>> + return ERR_PTR(-EINVAL);
>>> +
>>> + thr = devm_kzalloc(dev, sizeof(*thr), GFP_KERNEL);
>>> + if (!thr)
>>> + return ERR_PTR(-ENOMEM);
>>> +
>>> + thr->dev = dev;
>>> +
>>> + err = thr_parse_dt(thr, np);
>>> + if (err)
>>> + return ERR_PTR(err);
>>> +
>>> + if (thr->cpufreq.ndevs > 0) {
>>> + thr->cpufreq.nb.notifier_call = thr_cpufreq_event;
>>> + err = cpufreq_register_notifier(&thr->cpufreq.nb,
>>> + CPUFREQ_POLICY_NOTIFIER);
>>> + if (err < 0) {
>>> + dev_err(dev, "failed to register cpufreq notifier\n");
>>> + return ERR_PTR(err);
>>> + }
>>> + }
>>> +
>>> + for (i = 0; i < thr->devfreq.ndevs; i++) {
>>> + struct devfreq_thrdev *dtd = &thr->devfreq.devs[i];
>>> +
>>> + dtd->nb.notifier_call = thr_devfreq_event;
>>> + err = devm_devfreq_register_notifier(dev, dtd->devfreq,
>>> + &dtd->nb, DEVFREQ_POLICY_NOTIFIER);
>>> + if (err < 0) {
>>> + dev_err(dev, "failed to register devfreq notifier\n");
>>> + goto err_cpufreq_unregister;
>>> + }
>>> + }
>>> +
>>> + return thr;
>>> +
>>> +err_cpufreq_unregister:
>>> + if (thr->cpufreq.ndevs > 0)
>>> + cpufreq_unregister_notifier(&thr->cpufreq.nb,
>>> + CPUFREQ_POLICY_NOTIFIER);
>>> +
>>> + return ERR_PTR(err);
>>> +}
>>> +EXPORT_SYMBOL_GPL(throttler_setup);
>>> +
>>> +void throttler_teardown(struct throttler *thr)
>>> +{
>>> + int i;
>>> +
>>> + thr->level = 0;
>>> +
>>> + if (thr->cpufreq.ndevs > 0) {
>>> + thr_cpufreq_update_policy(thr);
>>> +
>>> + cpufreq_unregister_notifier(&thr->cpufreq.nb,
>>> + CPUFREQ_POLICY_NOTIFIER);
>>> + }
>>> +
>>> + if (thr->devfreq.ndevs > 0)
>>> + for (i = 0; i < thr->devfreq.ndevs; i++)
>>> + thr_update_devfreq(thr->devfreq.devs[i].devfreq);
>>> +}
>>> +EXPORT_SYMBOL_GPL(throttler_teardown);
>>> diff --git a/include/linux/throttler.h b/include/linux/throttler.h
>>> new file mode 100644
>>> index 000000000000..cab8c466da4b
>>> --- /dev/null
>>> +++ b/include/linux/throttler.h
>>> @@ -0,0 +1,10 @@
>>> +#ifndef __LINUX_THROTTLER_H__
>>> +#define __LINUX_THROTTLER_H__
>>> +
>>> +struct throttler;
>>> +
>>> +extern struct throttler *throttler_setup(struct device *dev);
>>> +extern void throttler_teardown(struct throttler *thr);
>>> +extern void throttler_set_level(struct throttler *thr, int level);
>>> +
>>> +#endif /* __LINUX_THROTTLER_H__ */
>>>
>
>
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH 02/11] PM / devfreq: Fix handling of min/max_freq == 0
From: Chanwoo Choi @ 2018-05-30 8:04 UTC (permalink / raw)
To: Matthias Kaehlcke
Cc: MyungJoo Ham, Kyungmin Park, Arnd Bergmann, Greg Kroah-Hartman,
Rob Herring, Mark Rutland, linux-pm, devicetree, linux-kernel,
Brian Norris, Douglas Anderson, Ørjan Eide,
John Einar Reitan
In-Reply-To: <20180529185758.GG168650@google.com>
Hi,
On 2018년 05월 30일 03:57, Matthias Kaehlcke wrote:
> On Mon, May 28, 2018 at 03:37:47PM +0900, Chanwoo Choi wrote:
>> Hi,
>>
>> On 2018년 05월 26일 05:30, Matthias Kaehlcke wrote:
>>> Commit ab8f58ad72c4 ("PM / devfreq: Set min/max_freq when adding the
>>> devfreq device") initializes df->min/max_freq with the min/max OPP when
>>> the device is added. Later commit f1d981eaecf8 ("PM / devfreq: Use the
>>> available min/max frequency") adds df->scaling_min/max_freq and the
>>> following to the frequency adjustment code:
>>>
>>> max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
>>>
>>> With the current handling of min/max_freq this is incorrect:
>>>
>>> Even though df->max_freq is now initialized to a value != 0 user space
>>> can still set it to 0, in this case max_freq would be 0 instead of
>>> df->scaling_max_freq as intended. In consequence the frequency adjustment
>>> is not performed:
>>>
>>> if (max_freq && freq > max_freq) {
>>> freq = max_freq;
>>>
>>> To fix this set df->min/max freq to the min/max OPP in max/max_freq_store,
>>> when the user passes a value of 0. This also prevents df->max_freq from
>>> being set below the min OPP when df->min_freq is 0, and similar for
>>> min_freq. Since it is now guaranteed that df->min/max_freq can't be 0 the
>>> checks for this case can be removed.
>>>
>>> Fixes: f1d981eaecf8 ("PM / devfreq: Use the available min/max frequency")
>>> Signed-off-by: Matthias Kaehlcke <mka@chromium.org>
>>> ---
>>> drivers/devfreq/devfreq.c | 30 ++++++++++++++++++------------
>>> 1 file changed, 18 insertions(+), 12 deletions(-)
>>>
>>> diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c
>>> index 0057ef5b0a98..67da4e7b486b 100644
>>> --- a/drivers/devfreq/devfreq.c
>>> +++ b/drivers/devfreq/devfreq.c
>>> @@ -283,11 +283,11 @@ int update_devfreq(struct devfreq *devfreq)
>>> max_freq = MIN(devfreq->scaling_max_freq, devfreq->max_freq);
>>> min_freq = MAX(devfreq->scaling_min_freq, devfreq->min_freq);
>>>
>>> - if (min_freq && freq < min_freq) {
>>> + if (freq < min_freq) {
>>> freq = min_freq;
>>> flags &= ~DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use GLB */
>>> }
>>> - if (max_freq && freq > max_freq) {
>>> + if (freq > max_freq) {
>>> freq = max_freq;
>>> flags |= DEVFREQ_FLAG_LEAST_UPPER_BOUND; /* Use LUB */
>>> }
>>> @@ -1123,17 +1123,20 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr,
>>> struct devfreq *df = to_devfreq(dev);
>>> unsigned long value;
>>> int ret;
>>> - unsigned long max;
>>>
>>> ret = sscanf(buf, "%lu", &value);
>>> if (ret != 1)
>>> return -EINVAL;
>>>
>>> mutex_lock(&df->lock);
>>> - max = df->max_freq;
>>> - if (value && max && value > max) {
>>> - ret = -EINVAL;
>>> - goto unlock;
>>> +
>>> + if (value) {
>>> + if (value > df->max_freq) {
>>> + ret = -EINVAL;
>>> + goto unlock;
>>> + }
>>> + } else {
>>> + value = df->profile->freq_table[df->profile->max_state - 1];
>>> }
>>
>> If you want to prevent that df->min_freq is zero,
>> you should reinitialize 'value' as following.
>> Because freq_table must be in ascending order.
>> value = df->profile->freq_table[0];
>
> Thanks for pointing this out!
>
> The devfreq device I tested with (a Mali GPU) uses descending order
> for some reason, and I assumed that's the usual order.
>
> https://chromium.googlesource.com/chromiumos/third_party/kernel/+/chromeos-4.4/drivers/gpu/arm/midgard/backend/gpu/mali_kbase_devfreq.c#208
>
> It seems the ordering doesn't have any impact beyond this patch. If
> the order isn't mandatory for drivers that set up their own freq_table
> we should probably support both cases to be safe.
Prior to that 'freq_table' is optional. So, patch[1] initialize the 'freq_table'
by using OPP interface if 'freq_table' is NULL.
[1] commit 0ec09ac2cebe ("PM / devfreq: Set the freq_table of devfreq device")
Current devfreq recommend the ascending order for 'freq_table'.
But, as you know, it might be not enough to support them.
I agree that we should support the both cases (ascending or descending order).
Maybe, it might be not proper to access the freq_table[] directly
because we don't know the ordering style of 'freq_table'
if 'freq_table' is made by devfreq user instead of devfreq core.
>
>>> @@ -1158,17 +1161,20 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr,
>>> struct devfreq *df = to_devfreq(dev);
>>> unsigned long value;
>>> int ret;
>>> - unsigned long min;
>>>
>>> ret = sscanf(buf, "%lu", &value);
>>> if (ret != 1)
>>> return -EINVAL;
>>>
>>> mutex_lock(&df->lock);
>>> - min = df->min_freq;
>>> - if (value && min && value < min) {
>>> - ret = -EINVAL;
>>> - goto unlock;
>>> +
>>> + if (!value) {
>>> + value = df->profile->freq_table[0];
>>
>> ditto.
>> value = df->profile->freq_table[df->profile->max_state - 1];
>>
>>> + } else {
>>> + if (value < df->min_freq) {
>>> + ret = -EINVAL;
>>> + goto unlock;
>>> + }
>>> }
>>>
>>> df->max_freq = value;
>>>
>>
>> Actually, min_freq_store() and max_freq_store() are very similar.
>> But, this patch changed the order of conditional statement as following:
>> If there is no special reason, you better to keep the same format
>> for the readability.
>>
>>
>> min_freq_store()
>> if (value) {
>> ...
>> } else {
>> value = df->profile->freq_table[df->profile->max_state - 1];
>> }
>>
>>
>> max_freq_store()
>> if (!value) {
>> value = df->profile->freq_table[0];
>> } else {
>> ...
>>
>
> Agreed, better use the same format, I'll update it in the next revision.
>
>
>
--
Best Regards,
Chanwoo Choi
Samsung Electronics
^ permalink raw reply
* Re: [PATCH V2] cpufreq: reinitialize new policy min/max when writing scaling_(max|min)_freq
From: Wangtao (Kevin, Kirin) @ 2018-05-30 8:03 UTC (permalink / raw)
To: Viresh Kumar; +Cc: rjw, linux-pm, linux-kernel, gengyanping, sunzhaosheng
In-Reply-To: <20180529102638.ikmw2xjf523sf4kf@vireshk-i7>
在 2018/5/29 18:26, Viresh Kumar 写道:
> On 26-05-18, 15:16, Kevin Wangtao wrote:
>> consider such situation, current user_policy.min is 1000000,
>> current user_policy.max is 1200000, in cpufreq_set_policy,
>> other driver may update policy.min to 1200000, policy.max to
>> 1300000. After that, If we input "echo 1300000 > scaling_min_freq",
>> then user_policy.min will be 1300000, and user_policy.max is
>> still 1200000, because the input value is checked with policy.max
>> not user_policy.max. if we get all related cpus offline and
>> online again, it will cause cpufreq_init_policy fail because
>> user_policy.min is higher than user_policy.max.
>>
>> The solution is when user space tries to write scaling_(max|min)_freq,
>> the min/max of new_policy should be reinitialized with min/max
>> of user_policy, like what cpufreq_update_policy does.
>>
>> Signed-off-by: Kevin Wangtao <kevin.wangtao@hisilicon.com>
>> ---
>> drivers/cpufreq/cpufreq.c | 2 ++
>> 1 file changed, 2 insertions(+)
>>
>> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
>> index b79c532..82123a1 100644
>> --- a/drivers/cpufreq/cpufreq.c
>> +++ b/drivers/cpufreq/cpufreq.c
>> @@ -697,6 +697,8 @@ static ssize_t store_##file_name \
>> struct cpufreq_policy new_policy; \
>> \
>> memcpy(&new_policy, policy, sizeof(*policy)); \
>
> Maybe add a comment here on why this is required ?
OK
>
>> + new_policy.min = policy->user_policy.min; \
>> + new_policy.max = policy->user_policy.max; \
>> \
>> ret = sscanf(buf, "%u", &new_policy.object); \
>> if (ret != 1) \
>
> Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
>
^ permalink raw reply
* [PATCH V3] cpufreq: reinitialize new policy min/max when writing scaling_(max|min)_freq
From: Kevin Wangtao @ 2018-05-30 7:56 UTC (permalink / raw)
To: rjw, viresh.kumar
Cc: linux-pm, linux-kernel, gengyanping, sunzhaosheng, Kevin Wangtao
In-Reply-To: <1527319008-66663-1-git-send-email-kevin.wangtao@hisilicon.com>
consider such situation, current user_policy.min is 1000000,
current user_policy.max is 1200000, in cpufreq_set_policy,
other driver may update policy.min to 1200000, policy.max to
1300000. After that, If we input "echo 1300000 > scaling_min_freq",
then user_policy.min will be 1300000, and user_policy.max is
still 1200000, because the input value is checked with policy.max
not user_policy.max. if we get all related cpus offline and
online again, it will cause cpufreq_init_policy fail because
user_policy.min is higher than user_policy.max.
The solution is when user space tries to write scaling_(max|min)_freq,
the min/max of new_policy should be reinitialized with min/max
of user_policy, like what cpufreq_update_policy does.
Signed-off-by: Kevin Wangtao <kevin.wangtao@hisilicon.com>
---
drivers/cpufreq/cpufreq.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index b79c532..a970113 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -697,6 +697,9 @@ static ssize_t store_##file_name \
struct cpufreq_policy new_policy; \
\
memcpy(&new_policy, policy, sizeof(*policy)); \
+ /* Initialized with user_policy to keep consistency */ \
+ new_policy.min = policy->user_policy.min; \
+ new_policy.max = policy->user_policy.max; \
\
ret = sscanf(buf, "%u", &new_policy.object); \
if (ret != 1) \
--
2.8.1
^ permalink raw reply related
* Re: [PATCH 2/2] cpufreq: Use static SRCU initializer
From: Viresh Kumar @ 2018-05-30 4:50 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: linux-pm, linux-kernel, Rafael J. Wysocki, Paul E. McKenney,
Thomas Gleixner
In-Reply-To: <20180525101958.13277-3-bigeasy@linutronix.de>
On 25-05-18, 12:19, Sebastian Andrzej Siewior wrote:
> Use the static SRCU initializer for `cpufreq_transition_notifier_list'.
> This avoids the init_cpufreq_transition_notifier_list() initcall. Its
> only purpose is to initialize the SRCU notifier once during boot and set
> another variable which is used as an indicator whether the init was
> perfromed before cpufreq_register_notifier() was used.
>
> Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
> CC: Viresh Kumar <viresh.kumar@linaro.org>
> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
> ---
> drivers/cpufreq/cpufreq.c | 13 +------------
> 1 file changed, 1 insertion(+), 12 deletions(-)
>
> diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
> index 075d18f6ba7a..3f7b502cfb1e 100644
> --- a/drivers/cpufreq/cpufreq.c
> +++ b/drivers/cpufreq/cpufreq.c
> @@ -89,16 +89,7 @@ static void cpufreq_governor_limits(struct cpufreq_policy *policy);
> * The mutex locks both lists.
> */
> static BLOCKING_NOTIFIER_HEAD(cpufreq_policy_notifier_list);
> -static struct srcu_notifier_head cpufreq_transition_notifier_list;
> -
> -static bool init_cpufreq_transition_notifier_list_called;
> -static int __init init_cpufreq_transition_notifier_list(void)
> -{
> - srcu_init_notifier_head(&cpufreq_transition_notifier_list);
> - init_cpufreq_transition_notifier_list_called = true;
> - return 0;
> -}
> -pure_initcall(init_cpufreq_transition_notifier_list);
> +SRCU_NOTIFIER_HEAD_STATIC(cpufreq_transition_notifier_list);
>
> static int off __read_mostly;
> static int cpufreq_disabled(void)
> @@ -1764,8 +1755,6 @@ int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list)
> if (cpufreq_disabled())
> return -EINVAL;
>
> - WARN_ON(!init_cpufreq_transition_notifier_list_called);
> -
> switch (list) {
> case CPUFREQ_TRANSITION_NOTIFIER:
> mutex_lock(&cpufreq_fast_switch_lock);
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
--
viresh
^ permalink raw reply
* [PATCH 3/3] power: supply: cros: add property to detect connected ports
From: Fabien Parent @ 2018-05-30 3:17 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: linux-pm, linux-kernel, eballetbo, gpain, Fabien Parent
In-Reply-To: <20180530031704.18597-1-fparent@baylibre.com>
When a port is connected but acting as a source, its 'online' and
'status' properties are identical to a port that is not connected. This
makes it tedious for userspace to know for sure whether a port is
connected or not.
This commit adds a new property 'present' to reflect whether a port
is connected or not.
Signed-off-by: Fabien Parent <fparent@baylibre.com>
---
drivers/power/supply/cros_usbpd-charger.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c
index 808688a6586c..d44ab35670ab 100644
--- a/drivers/power/supply/cros_usbpd-charger.c
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -32,6 +32,7 @@ struct port_data {
struct power_supply_desc psy_desc;
int psy_usb_type;
int psy_online;
+ int psy_present;
int psy_status;
int psy_current_max;
int psy_voltage_max_design;
@@ -54,6 +55,7 @@ struct charger_data {
static enum power_supply_property cros_usbpd_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
@@ -65,6 +67,7 @@ static enum power_supply_property cros_usbpd_charger_props[] = {
static enum power_supply_property cros_usbpd_dedicated_charger_props[] = {
POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
};
@@ -205,18 +208,22 @@ static int cros_usbpd_charger_get_power_info(struct port_data *port)
case USB_PD_PORT_POWER_DISCONNECTED:
port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
port->psy_online = 0;
+ port->psy_present = 0;
break;
case USB_PD_PORT_POWER_SOURCE:
port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
port->psy_online = 0;
+ port->psy_present = 1;
break;
case USB_PD_PORT_POWER_SINK:
port->psy_status = POWER_SUPPLY_STATUS_CHARGING;
port->psy_online = 1;
+ port->psy_present = 1;
break;
case USB_PD_PORT_POWER_SINK_NOT_CHARGING:
port->psy_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
port->psy_online = 1;
+ port->psy_present = 1;
break;
default:
dev_err(dev, "Unknown role %d\n", resp.role);
@@ -362,6 +369,7 @@ static int cros_usbpd_charger_get_prop(struct power_supply *psy,
*/
if (ec_device->mkbp_event_supported || port->psy_online)
break;
+ case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
@@ -380,6 +388,9 @@ static int cros_usbpd_charger_get_prop(struct power_supply *psy,
case POWER_SUPPLY_PROP_ONLINE:
val->intval = port->psy_online;
break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = port->psy_present;
+ break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = port->psy_status;
break;
--
2.17.0
^ permalink raw reply related
* [PATCH 2/3] power: supply: cros: add support for dedicated port
From: Fabien Parent @ 2018-05-30 3:17 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: linux-pm, linux-kernel, eballetbo, gpain, Fabien Parent
In-Reply-To: <20180530031704.18597-1-fparent@baylibre.com>
ChromeOS devices can have one optional dedicated port.
The Dedicated port is unique and similar to the USB PD ports
except that it doesn't support as many properties.
The presence of a dedicated port is determined from whether the
EC's charger port count is equal to 'number of USB PD port' + 1.
The dedicated port ID is always the last valid port ID.
This commit keeps compatibility with Embedded Controllers that do not
support the new EC_CMD_CHARGE_PORT_COUNT command by setting
the number of charger port to be equal to the number of USB PD port
when this command fails.
Signed-off-by: Fabien Parent <fparent@baylibre.com>
---
drivers/power/supply/cros_usbpd-charger.c | 115 +++++++++++++++++++---
1 file changed, 101 insertions(+), 14 deletions(-)
diff --git a/drivers/power/supply/cros_usbpd-charger.c b/drivers/power/supply/cros_usbpd-charger.c
index 3a0c96fd1bc1..808688a6586c 100644
--- a/drivers/power/supply/cros_usbpd-charger.c
+++ b/drivers/power/supply/cros_usbpd-charger.c
@@ -12,8 +12,12 @@
#include <linux/power_supply.h>
#include <linux/slab.h>
-#define CHARGER_DIR_NAME "CROS_USBPD_CHARGER%d"
-#define CHARGER_DIR_NAME_LENGTH sizeof(CHARGER_DIR_NAME)
+#define CHARGER_USBPD_DIR_NAME "CROS_USBPD_CHARGER%d"
+#define CHARGER_DEDICATED_DIR_NAME "CROS_DEDICATED_CHARGER"
+#define CHARGER_DIR_NAME_LENGTH (sizeof(CHARGER_USBPD_DIR_NAME) >= \
+ sizeof(CHARGER_DEDICATED_DIR_NAME) ? \
+ sizeof(CHARGER_USBPD_DIR_NAME) : \
+ sizeof(CHARGER_DEDICATED_DIR_NAME))
#define CHARGER_CACHE_UPDATE_DELAY msecs_to_jiffies(500)
#define CHARGER_MANUFACTURER_MODEL_LENGTH 32
@@ -42,6 +46,7 @@ struct charger_data {
struct cros_ec_dev *ec_dev;
struct cros_ec_device *ec_device;
int num_charger_ports;
+ int num_usbpd_ports;
int num_registered_psy;
struct port_data *ports[EC_USB_PD_MAX_PORTS];
struct notifier_block notifier;
@@ -58,6 +63,12 @@ static enum power_supply_property cros_usbpd_charger_props[] = {
POWER_SUPPLY_PROP_USB_TYPE
};
+static enum power_supply_property cros_usbpd_dedicated_charger_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = {
POWER_SUPPLY_USB_TYPE_UNKNOWN,
POWER_SUPPLY_USB_TYPE_SDP,
@@ -69,6 +80,11 @@ static enum power_supply_usb_type cros_usbpd_charger_usb_types[] = {
POWER_SUPPLY_USB_TYPE_APPLE_BRICK_ID
};
+static bool cros_usbpd_charger_port_is_dedicated(struct port_data *port)
+{
+ return port->port_number >= port->charger->num_usbpd_ports;
+}
+
static int cros_usbpd_charger_ec_command(struct charger_data *charger,
unsigned int version,
unsigned int command,
@@ -102,6 +118,23 @@ static int cros_usbpd_charger_ec_command(struct charger_data *charger,
}
static int cros_usbpd_charger_get_num_ports(struct charger_data *charger)
+{
+ struct ec_response_charge_port_count resp;
+ int ret;
+
+ ret = cros_usbpd_charger_ec_command(charger, 0,
+ EC_CMD_CHARGE_PORT_COUNT,
+ NULL, 0, &resp, sizeof(resp));
+ if (ret < 0) {
+ dev_err(charger->dev,
+ "Unable to get the number of ports (err:0x%x)\n", ret);
+ return ret;
+ }
+
+ return resp.port_count;
+}
+
+static int cros_usbpd_charger_get_usbpd_num_ports(struct charger_data *charger)
{
struct ec_response_usb_pd_ports resp;
int ret;
@@ -246,7 +279,10 @@ static int cros_usbpd_charger_get_power_info(struct port_data *port)
port->psy_usb_type = POWER_SUPPLY_USB_TYPE_SDP;
}
- port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
+ if (cros_usbpd_charger_port_is_dedicated(port))
+ port->psy_desc.type = POWER_SUPPLY_TYPE_MAINS;
+ else
+ port->psy_desc.type = POWER_SUPPLY_TYPE_USB;
dev_dbg(dev,
"Port %d: type=%d vmax=%d vnow=%d cmax=%d clim=%d pmax=%d\n",
@@ -281,7 +317,8 @@ static int cros_usbpd_charger_get_port_status(struct port_data *port,
if (ret < 0)
return ret;
- ret = cros_usbpd_charger_get_discovery_info(port);
+ if (!cros_usbpd_charger_port_is_dedicated(port))
+ ret = cros_usbpd_charger_get_discovery_info(port);
port->last_update = jiffies;
return ret;
@@ -425,17 +462,56 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
platform_set_drvdata(pd, charger);
+ /*
+ * We need to know the number of USB PD ports in order to know whether
+ * there is a dedicated port. The dedicated port will always be
+ * after the USB PD ports, and there should be only one.
+ */
+ charger->num_usbpd_ports =
+ cros_usbpd_charger_get_usbpd_num_ports(charger);
+ if (charger->num_usbpd_ports <= 0) {
+ /*
+ * This can happen on a system that doesn't support USB PD.
+ * Log a message, but no need to warn.
+ */
+ dev_info(dev, "No USB PD charging ports found\n");
+ }
+
charger->num_charger_ports = cros_usbpd_charger_get_num_ports(charger);
- if (charger->num_charger_ports <= 0) {
+ if (charger->num_charger_ports < 0) {
/*
* This can happen on a system that doesn't support USB PD.
* Log a message, but no need to warn.
+ * Older ECs do not support the above command, in that case
+ * let's set up the number of charger ports equal to the number
+ * of USB PD ports
+ */
+ dev_info(dev, "Could not get charger port count\n");
+ charger->num_charger_ports = charger->num_usbpd_ports;
+ }
+
+ if (charger->num_charger_ports <= 0) {
+ /*
+ * This can happen on a system that doesn't support USB PD and
+ * doesn't have a dedicated port.
+ * Log a message, but no need to warn.
*/
dev_info(dev, "No charging ports found\n");
ret = -ENODEV;
goto fail_nowarn;
}
+ /*
+ * Sanity checks on the number of ports:
+ * there should be at most 1 dedicated port
+ */
+ if (charger->num_charger_ports < charger->num_usbpd_ports ||
+ charger->num_charger_ports > (charger->num_usbpd_ports + 1)) {
+ dev_err(dev, "Unexpected number of charge port count\n");
+ ret = -EPROTO;
+ goto fail_nowarn;
+ }
+
for (i = 0; i < charger->num_charger_ports; i++) {
struct power_supply_config psy_cfg = {};
@@ -447,22 +523,33 @@ static int cros_usbpd_charger_probe(struct platform_device *pd)
port->charger = charger;
port->port_number = i;
- sprintf(port->name, CHARGER_DIR_NAME, i);
psy_desc = &port->psy_desc;
- psy_desc->name = port->name;
- psy_desc->type = POWER_SUPPLY_TYPE_USB;
psy_desc->get_property = cros_usbpd_charger_get_prop;
psy_desc->external_power_changed =
cros_usbpd_charger_power_changed;
- psy_desc->properties = cros_usbpd_charger_props;
- psy_desc->num_properties =
- ARRAY_SIZE(cros_usbpd_charger_props);
- psy_desc->usb_types = cros_usbpd_charger_usb_types;
- psy_desc->num_usb_types =
- ARRAY_SIZE(cros_usbpd_charger_usb_types);
psy_cfg.drv_data = port;
+ if (cros_usbpd_charger_port_is_dedicated(port)) {
+ sprintf(port->name, CHARGER_DEDICATED_DIR_NAME);
+ psy_desc->type = POWER_SUPPLY_TYPE_MAINS;
+ psy_desc->properties =
+ cros_usbpd_dedicated_charger_props;
+ psy_desc->num_properties =
+ ARRAY_SIZE(cros_usbpd_dedicated_charger_props);
+ } else {
+ sprintf(port->name, CHARGER_USBPD_DIR_NAME, i);
+ psy_desc->type = POWER_SUPPLY_TYPE_USB;
+ psy_desc->properties = cros_usbpd_charger_props;
+ psy_desc->num_properties =
+ ARRAY_SIZE(cros_usbpd_charger_props);
+ psy_desc->usb_types = cros_usbpd_charger_usb_types;
+ psy_desc->num_usb_types =
+ ARRAY_SIZE(cros_usbpd_charger_usb_types);
+ }
+
+ psy_desc->name = port->name;
+
psy = devm_power_supply_register_no_ws(dev, psy_desc,
&psy_cfg);
if (IS_ERR(psy)) {
--
2.17.0
^ permalink raw reply related
* [PATCH 1/3] mfd: cros: add charger port count command definition
From: Fabien Parent @ 2018-05-30 3:17 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: linux-pm, linux-kernel, eballetbo, gpain, Fabien Parent
In-Reply-To: <20180530031704.18597-1-fparent@baylibre.com>
A new more command has been added to the ChromeOS embedded controller
that allows to get the number of charger port count. Unlike
EC_CMD_USB_PD_PORTS, this new command also includes the dedicated
port if present.
This command will be used to expose the dedicated charger port
in the ChromeOS charger driver.
Signed-off-by: Fabien Parent <fparent@baylibre.com>
---
include/linux/mfd/cros_ec_commands.h | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/include/linux/mfd/cros_ec_commands.h b/include/linux/mfd/cros_ec_commands.h
index 0d926492ac3a..e3187f8bdb7e 100644
--- a/include/linux/mfd/cros_ec_commands.h
+++ b/include/linux/mfd/cros_ec_commands.h
@@ -3005,6 +3005,16 @@ struct ec_params_usb_pd_info_request {
uint8_t port;
} __packed;
+/*
+ * This command will return the number of USB PD charge port + the number
+ * of dedicated port present.
+ * EC_CMD_USB_PD_PORTS does NOT include the dedicated ports
+ */
+#define EC_CMD_CHARGE_PORT_COUNT 0x0105
+struct ec_response_charge_port_count {
+ uint8_t port_count;
+} __packed;
+
/* Read USB-PD Device discovery info */
#define EC_CMD_USB_PD_DISCOVERY 0x0113
struct ec_params_usb_pd_discovery_entry {
--
2.17.0
^ permalink raw reply related
* [PATCH 0/3] power: supply: cros: add support for dedicated port and expose connected ports
From: Fabien Parent @ 2018-05-30 3:17 UTC (permalink / raw)
To: Sebastian Reichel; +Cc: linux-pm, linux-kernel, eballetbo, gpain, Fabien Parent
Dear all,
This patch series adds support for an optional dedicated port
to the ChromeOS power supply driver and adds a new property that expose
when a power supply is connected. The series was tested on ChromeOS "Fizz"
hardware.
This patch series depends on the following patch serie which adds
the ChromeOS power supply driver:
* https://lkml.org/lkml/2018/5/2/585 [PATCH v4 0/3] mfd/power: cros_ec: add
support for USBPD charger driver
The ChromeOS power supply driver also depends on the following patches to be
applied:
* https://lkml.org/lkml/2018/4/23/602 ([PATCH v8 0/6] typec: tcpm: Add
sink side support for PPS)
* https://lkml.org/lkml/2018/4/18/229 ([RESEND PATCH v5 4/7] mfd:
cros_ec_dev: Register cros-ec-rtc driver as a subdevice.)
Best Regards,
Fabien
Fabien Parent (3):
mfd: cros: add charger port count command definition
power: supply: cros: add support for dedicated port
power: supply: cros: add property to detect connected ports
drivers/power/supply/cros_usbpd-charger.c | 129 +++++++++++++++++++---
include/linux/mfd/cros_ec_commands.h | 10 ++
2 files changed, 124 insertions(+), 15 deletions(-)
--
2.17.0
^ permalink raw reply
* [PATCH v15 2/2] dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
From: Ilia Lin @ 2018-05-30 2:39 UTC (permalink / raw)
To: ilia.lin, vireshk, nm, sboyd, robh, mark.rutland, rjw
Cc: linux-pm, devicetree, linux-kernel, ilialin
In-Reply-To: <1527647969-15716-1-git-send-email-ilialin@codeaurora.org>
The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
to provide the OPP framework with required information.
This is used to determine the voltage and frequency value for each OPP of
operating-points-v2 table when it is parsed by the OPP framework.
This change adds documentation for the DT bindings.
The "operating-points-v2-kryo-cpu" DT extends the "operating-points-v2"
with following parameters:
- nvmem-cells (NVMEM area containig the speedbin information)
- opp-supported-hw: A single 32 bit bitmap value,
representing compatible HW:
0: MSM8996 V3, speedbin 0
1: MSM8996 V3, speedbin 1
2: MSM8996 V3, speedbin 2
3: unused
4: MSM8996 SG, speedbin 0
5: MSM8996 SG, speedbin 1
6: MSM8996 SG, speedbin 2
7-31: unused
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
Tested-by: Amit Kucheria <amit.kucheria@linaro.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
---
.../devicetree/bindings/opp/kryo-cpufreq.txt | 680 +++++++++++++++++++++
1 file changed, 680 insertions(+)
create mode 100644 Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
diff --git a/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
new file mode 100644
index 0000000..c2127b9
--- /dev/null
+++ b/Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
@@ -0,0 +1,680 @@
+Qualcomm Technologies, Inc. KRYO CPUFreq and OPP bindings
+===================================
+
+In Certain Qualcomm Technologies, Inc. SoCs like apq8096 and msm8996
+that have KRYO processors, the CPU ferequencies subset and voltage value
+of each OPP varies based on the silicon variant in use.
+Qualcomm Technologies, Inc. Process Voltage Scaling Tables
+defines the voltage and frequency value based on the msm-id in SMEM
+and speedbin blown in the efuse combination.
+The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
+to provide the OPP framework with required information (existing HW bitmap).
+This is used to determine the voltage and frequency value for each OPP of
+operating-points-v2 table when it is parsed by the OPP framework.
+
+Required properties:
+--------------------
+In 'cpus' nodes:
+- operating-points-v2: Phandle to the operating-points-v2 table to use.
+
+In 'operating-points-v2' table:
+- compatible: Should be
+ - 'operating-points-v2-kryo-cpu' for apq8096 and msm8996.
+- nvmem-cells: A phandle pointing to a nvmem-cells node representing the
+ efuse registers that has information about the
+ speedbin that is used to select the right frequency/voltage
+ value pair.
+ Please refer the for nvmem-cells
+ bindings Documentation/devicetree/bindings/nvmem/nvmem.txt
+ and also examples below.
+
+In every OPP node:
+- opp-supported-hw: A single 32 bit bitmap value, representing compatible HW.
+ Bitmap:
+ 0: MSM8996 V3, speedbin 0
+ 1: MSM8996 V3, speedbin 1
+ 2: MSM8996 V3, speedbin 2
+ 3: unused
+ 4: MSM8996 SG, speedbin 0
+ 5: MSM8996 SG, speedbin 1
+ 6: MSM8996 SG, speedbin 2
+ 7-31: unused
+
+Example 1:
+---------
+
+ cpus {
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ CPU0: cpu@0 {
+ device_type = "cpu";
+ compatible = "qcom,kryo";
+ reg = <0x0 0x0>;
+ enable-method = "psci";
+ clocks = <&kryocc 0>;
+ cpu-supply = <&pm8994_s11_saw>;
+ operating-points-v2 = <&cluster0_opp>;
+ #cooling-cells = <2>;
+ next-level-cache = <&L2_0>;
+ L2_0: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ };
+ };
+
+ CPU1: cpu@1 {
+ device_type = "cpu";
+ compatible = "qcom,kryo";
+ reg = <0x0 0x1>;
+ enable-method = "psci";
+ clocks = <&kryocc 0>;
+ cpu-supply = <&pm8994_s11_saw>;
+ operating-points-v2 = <&cluster0_opp>;
+ #cooling-cells = <2>;
+ next-level-cache = <&L2_0>;
+ };
+
+ CPU2: cpu@100 {
+ device_type = "cpu";
+ compatible = "qcom,kryo";
+ reg = <0x0 0x100>;
+ enable-method = "psci";
+ clocks = <&kryocc 1>;
+ cpu-supply = <&pm8994_s11_saw>;
+ operating-points-v2 = <&cluster1_opp>;
+ #cooling-cells = <2>;
+ next-level-cache = <&L2_1>;
+ L2_1: l2-cache {
+ compatible = "cache";
+ cache-level = <2>;
+ };
+ };
+
+ CPU3: cpu@101 {
+ device_type = "cpu";
+ compatible = "qcom,kryo";
+ reg = <0x0 0x101>;
+ enable-method = "psci";
+ clocks = <&kryocc 1>;
+ cpu-supply = <&pm8994_s11_saw>;
+ operating-points-v2 = <&cluster1_opp>;
+ #cooling-cells = <2>;
+ next-level-cache = <&L2_1>;
+ };
+
+ cpu-map {
+ cluster0 {
+ core0 {
+ cpu = <&CPU0>;
+ };
+
+ core1 {
+ cpu = <&CPU1>;
+ };
+ };
+
+ cluster1 {
+ core0 {
+ cpu = <&CPU2>;
+ };
+
+ core1 {
+ cpu = <&CPU3>;
+ };
+ };
+ };
+ };
+
+ cluster0_opp: opp_table0 {
+ compatible = "operating-points-v2-kryo-cpu";
+ nvmem-cells = <&speedbin_efuse>;
+ opp-shared;
+
+ opp-307200000 {
+ opp-hz = /bits/ 64 <307200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x77>;
+ clock-latency-ns = <200000>;
+ };
+ opp-384000000 {
+ opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-422400000 {
+ opp-hz = /bits/ 64 <422400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-460800000 {
+ opp-hz = /bits/ 64 <460800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-480000000 {
+ opp-hz = /bits/ 64 <480000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-537600000 {
+ opp-hz = /bits/ 64 <537600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-556800000 {
+ opp-hz = /bits/ 64 <556800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-614400000 {
+ opp-hz = /bits/ 64 <614400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-652800000 {
+ opp-hz = /bits/ 64 <652800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-691200000 {
+ opp-hz = /bits/ 64 <691200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-729600000 {
+ opp-hz = /bits/ 64 <729600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-768000000 {
+ opp-hz = /bits/ 64 <768000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-844800000 {
+ opp-hz = /bits/ 64 <844800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x77>;
+ clock-latency-ns = <200000>;
+ };
+ opp-902400000 {
+ opp-hz = /bits/ 64 <902400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-960000000 {
+ opp-hz = /bits/ 64 <960000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-979200000 {
+ opp-hz = /bits/ 64 <979200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1036800000 {
+ opp-hz = /bits/ 64 <1036800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1056000000 {
+ opp-hz = /bits/ 64 <1056000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1113600000 {
+ opp-hz = /bits/ 64 <1113600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1132800000 {
+ opp-hz = /bits/ 64 <1132800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1190400000 {
+ opp-hz = /bits/ 64 <1190400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1209600000 {
+ opp-hz = /bits/ 64 <1209600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1228800000 {
+ opp-hz = /bits/ 64 <1228800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1286400000 {
+ opp-hz = /bits/ 64 <1286400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1324800000 {
+ opp-hz = /bits/ 64 <1324800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x5>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1363200000 {
+ opp-hz = /bits/ 64 <1363200000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x72>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1401600000 {
+ opp-hz = /bits/ 64 <1401600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x5>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1440000000 {
+ opp-hz = /bits/ 64 <1440000000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1478400000 {
+ opp-hz = /bits/ 64 <1478400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1497600000 {
+ opp-hz = /bits/ 64 <1497600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x4>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1516800000 {
+ opp-hz = /bits/ 64 <1516800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1593600000 {
+ opp-hz = /bits/ 64 <1593600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x71>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1996800000 {
+ opp-hz = /bits/ 64 <1996800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x20>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2188800000 {
+ opp-hz = /bits/ 64 <2188800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x10>;
+ clock-latency-ns = <200000>;
+ };
+ };
+
+ cluster1_opp: opp_table1 {
+ compatible = "operating-points-v2-kryo-cpu";
+ nvmem-cells = <&speedbin_efuse>;
+ opp-shared;
+
+ opp-307200000 {
+ opp-hz = /bits/ 64 <307200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x77>;
+ clock-latency-ns = <200000>;
+ };
+ opp-384000000 {
+ opp-hz = /bits/ 64 <384000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-403200000 {
+ opp-hz = /bits/ 64 <403200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-460800000 {
+ opp-hz = /bits/ 64 <460800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-480000000 {
+ opp-hz = /bits/ 64 <480000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-537600000 {
+ opp-hz = /bits/ 64 <537600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-556800000 {
+ opp-hz = /bits/ 64 <556800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-614400000 {
+ opp-hz = /bits/ 64 <614400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-652800000 {
+ opp-hz = /bits/ 64 <652800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-691200000 {
+ opp-hz = /bits/ 64 <691200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-729600000 {
+ opp-hz = /bits/ 64 <729600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-748800000 {
+ opp-hz = /bits/ 64 <748800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-806400000 {
+ opp-hz = /bits/ 64 <806400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-825600000 {
+ opp-hz = /bits/ 64 <825600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-883200000 {
+ opp-hz = /bits/ 64 <883200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-902400000 {
+ opp-hz = /bits/ 64 <902400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-940800000 {
+ opp-hz = /bits/ 64 <940800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-979200000 {
+ opp-hz = /bits/ 64 <979200000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1036800000 {
+ opp-hz = /bits/ 64 <1036800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1056000000 {
+ opp-hz = /bits/ 64 <1056000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1113600000 {
+ opp-hz = /bits/ 64 <1113600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1132800000 {
+ opp-hz = /bits/ 64 <1132800000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1190400000 {
+ opp-hz = /bits/ 64 <1190400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1209600000 {
+ opp-hz = /bits/ 64 <1209600000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1248000000 {
+ opp-hz = /bits/ 64 <1248000000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1286400000 {
+ opp-hz = /bits/ 64 <1286400000>;
+ opp-microvolt = <905000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1324800000 {
+ opp-hz = /bits/ 64 <1324800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1363200000 {
+ opp-hz = /bits/ 64 <1363200000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1401600000 {
+ opp-hz = /bits/ 64 <1401600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1440000000 {
+ opp-hz = /bits/ 64 <1440000000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1478400000 {
+ opp-hz = /bits/ 64 <1478400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1516800000 {
+ opp-hz = /bits/ 64 <1516800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1555200000 {
+ opp-hz = /bits/ 64 <1555200000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1593600000 {
+ opp-hz = /bits/ 64 <1593600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1632000000 {
+ opp-hz = /bits/ 64 <1632000000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1670400000 {
+ opp-hz = /bits/ 64 <1670400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1708800000 {
+ opp-hz = /bits/ 64 <1708800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1747200000 {
+ opp-hz = /bits/ 64 <1747200000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x70>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1785600000 {
+ opp-hz = /bits/ 64 <1785600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x7>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1804800000 {
+ opp-hz = /bits/ 64 <1804800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x6>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1824000000 {
+ opp-hz = /bits/ 64 <1824000000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x71>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1900800000 {
+ opp-hz = /bits/ 64 <1900800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x74>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1920000000 {
+ opp-hz = /bits/ 64 <1920000000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1977600000 {
+ opp-hz = /bits/ 64 <1977600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x30>;
+ clock-latency-ns = <200000>;
+ };
+ opp-1996800000 {
+ opp-hz = /bits/ 64 <1996800000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2054400000 {
+ opp-hz = /bits/ 64 <2054400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x30>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2073600000 {
+ opp-hz = /bits/ 64 <2073600000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x1>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2150400000 {
+ opp-hz = /bits/ 64 <2150400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x31>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2246400000 {
+ opp-hz = /bits/ 64 <2246400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x10>;
+ clock-latency-ns = <200000>;
+ };
+ opp-2342400000 {
+ opp-hz = /bits/ 64 <2342400000>;
+ opp-microvolt = <1140000 905000 1140000>;
+ opp-supported-hw = <0x10>;
+ clock-latency-ns = <200000>;
+ };
+ };
+
+....
+
+reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+....
+ smem_mem: smem-mem@86000000 {
+ reg = <0x0 0x86000000 0x0 0x200000>;
+ no-map;
+ };
+....
+};
+
+smem {
+ compatible = "qcom,smem";
+ memory-region = <&smem_mem>;
+ hwlocks = <&tcsr_mutex 3>;
+};
+
+soc {
+....
+ qfprom: qfprom@74000 {
+ compatible = "qcom,qfprom";
+ reg = <0x00074000 0x8ff>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ....
+ speedbin_efuse: speedbin@133 {
+ reg = <0x133 0x1>;
+ bits = <5 3>;
+ };
+ };
+};
--
1.9.1
^ permalink raw reply related
* [PATCH v15 1/2] cpufreq: Add Kryo CPU scaling driver
From: Ilia Lin @ 2018-05-30 2:39 UTC (permalink / raw)
To: ilia.lin, vireshk, nm, sboyd, robh, mark.rutland, rjw
Cc: linux-pm, devicetree, linux-kernel, ilialin
In-Reply-To: <1527647969-15716-1-git-send-email-ilialin@codeaurora.org>
In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
the CPU frequency subset and voltage value of each OPP varies
based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
defines the voltage and frequency value based on the msm-id in SMEM
and speedbin blown in the efuse combination.
The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
to provide the OPP framework with required information.
This is used to determine the voltage and frequency value for each OPP of
operating-points-v2 table when it is parsed by the OPP framework.
Signed-off-by: Ilia Lin <ilialin@codeaurora.org>
Acked-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Amit Kucheria <amit.kucheria@linaro.org>
Tested-by: Amit Kucheria <amit.kucheria@linaro.org>
---
MAINTAINERS | 7 ++
drivers/cpufreq/Kconfig.arm | 11 ++
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
drivers/cpufreq/qcom-cpufreq-kryo.c | 212 +++++++++++++++++++++++++++++++++++
5 files changed, 234 insertions(+)
create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
diff --git a/MAINTAINERS b/MAINTAINERS
index ba0adcb..648e0c8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11687,6 +11687,13 @@ F: Documentation/devicetree/bindings/media/qcom,camss.txt
F: Documentation/media/v4l-drivers/qcom_camss.rst
F: drivers/media/platform/qcom/camss-8x16/
+QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
+M: Ilia Lin <ilia.lin@gmail.com>
+L: linux-pm@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
+F: drivers/cpufreq/qcom-cpufreq-kryo.c
+
QUALCOMM EMAC GIGABIT ETHERNET DRIVER
M: Timur Tabi <timur@codeaurora.org>
L: netdev@vger.kernel.org
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index de55c7d..86e87bb 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -124,6 +124,17 @@ config ARM_OMAP2PLUS_CPUFREQ
depends on ARCH_OMAP2PLUS
default ARCH_OMAP2PLUS
+config ARM_QCOM_CPUFREQ_KRYO
+ bool "Qualcomm Kryo based CPUFreq"
+ depends on ARM64
+ depends on QCOM_QFPROM
+ depends on QCOM_SMEM
+ select PM_OPP
+ help
+ This adds the CPUFreq driver for Qualcomm Kryo SoC based boards.
+
+ If in doubt, say N.
+
config ARM_S3C_CPUFREQ
bool
help
diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile
index 8d24ade..fb4a2ec 100644
--- a/drivers/cpufreq/Makefile
+++ b/drivers/cpufreq/Makefile
@@ -65,6 +65,7 @@ obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx) += pxa3xx-cpufreq.o
+obj-$(CONFIG_ARM_QCOM_CPUFREQ_KRYO) += qcom-cpufreq-kryo.o
obj-$(CONFIG_ARM_S3C2410_CPUFREQ) += s3c2410-cpufreq.o
obj-$(CONFIG_ARM_S3C2412_CPUFREQ) += s3c2412-cpufreq.o
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 3b585e4..77d6ab8 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -118,6 +118,9 @@
{ .compatible = "nvidia,tegra124", },
+ { .compatible = "qcom,apq8096", },
+ { .compatible = "qcom,msm8996", },
+
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },
diff --git a/drivers/cpufreq/qcom-cpufreq-kryo.c b/drivers/cpufreq/qcom-cpufreq-kryo.c
new file mode 100644
index 0000000..d049fe4
--- /dev/null
+++ b/drivers/cpufreq/qcom-cpufreq-kryo.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+/*
+ * In Certain QCOM SoCs like apq8096 and msm8996 that have KRYO processors,
+ * the CPU frequency subset and voltage value of each OPP varies
+ * based on the silicon variant in use. Qualcomm Process Voltage Scaling Tables
+ * defines the voltage and frequency value based on the msm-id in SMEM
+ * and speedbin blown in the efuse combination.
+ * The qcom-cpufreq-kryo driver reads the msm-id and efuse value from the SoC
+ * to provide the OPP framework with required information.
+ * This is used to determine the voltage and frequency value for each OPP of
+ * operating-points-v2 table when it is parsed by the OPP framework.
+ */
+
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/soc/qcom/smem.h>
+
+#define MSM_ID_SMEM 137
+
+enum _msm_id {
+ MSM8996V3 = 0xF6ul,
+ APQ8096V3 = 0x123ul,
+ MSM8996SG = 0x131ul,
+ APQ8096SG = 0x138ul,
+};
+
+enum _msm8996_version {
+ MSM8996_V3,
+ MSM8996_SG,
+ NUM_OF_MSM8996_VERSIONS,
+};
+
+static enum _msm8996_version __init qcom_cpufreq_kryo_get_msm_id(void)
+{
+ size_t len;
+ u32 *msm_id;
+ enum _msm8996_version version;
+
+ msm_id = qcom_smem_get(QCOM_SMEM_HOST_ANY, MSM_ID_SMEM, &len);
+ if (IS_ERR(msm_id))
+ return NUM_OF_MSM8996_VERSIONS;
+
+ /* The first 4 bytes are format, next to them is the actual msm-id */
+ msm_id++;
+
+ switch ((enum _msm_id)*msm_id) {
+ case MSM8996V3:
+ case APQ8096V3:
+ version = MSM8996_V3;
+ break;
+ case MSM8996SG:
+ case APQ8096SG:
+ version = MSM8996_SG;
+ break;
+ default:
+ version = NUM_OF_MSM8996_VERSIONS;
+ }
+
+ return version;
+}
+
+static int qcom_cpufreq_kryo_probe(struct platform_device *pdev)
+{
+ struct opp_table *opp_tables[NR_CPUS] = {0};
+ struct platform_device *cpufreq_dt_pdev;
+ enum _msm8996_version msm8996_version;
+ struct nvmem_cell *speedbin_nvmem;
+ struct device_node *np;
+ struct device *cpu_dev;
+ unsigned cpu;
+ u8 *speedbin;
+ u32 versions;
+ size_t len;
+ int ret;
+
+ cpu_dev = get_cpu_device(0);
+ if (NULL == cpu_dev)
+ ret = -ENODEV;
+
+ msm8996_version = qcom_cpufreq_kryo_get_msm_id();
+ if (NUM_OF_MSM8996_VERSIONS == msm8996_version) {
+ dev_err(cpu_dev, "Not Snapdragon 820/821!");
+ return -ENODEV;
+ }
+
+ np = dev_pm_opp_of_get_opp_desc_node(cpu_dev);
+ if (IS_ERR(np))
+ return PTR_ERR(np);
+
+ ret = of_device_is_compatible(np, "operating-points-v2-kryo-cpu");
+ if (!ret) {
+ of_node_put(np);
+ return -ENOENT;
+ }
+
+ speedbin_nvmem = of_nvmem_cell_get(np, NULL);
+ of_node_put(np);
+ if (IS_ERR(speedbin_nvmem)) {
+ dev_err(cpu_dev, "Could not get nvmem cell: %ld\n",
+ PTR_ERR(speedbin_nvmem));
+ return PTR_ERR(speedbin_nvmem);
+ }
+
+ speedbin = nvmem_cell_read(speedbin_nvmem, &len);
+ nvmem_cell_put(speedbin_nvmem);
+
+ switch (msm8996_version) {
+ case MSM8996_V3:
+ versions = 1 << (unsigned int)(*speedbin);
+ break;
+ case MSM8996_SG:
+ versions = 1 << ((unsigned int)(*speedbin) + 4);
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ for_each_possible_cpu(cpu) {
+ cpu_dev = get_cpu_device(cpu);
+ if (NULL == cpu_dev) {
+ ret = -ENODEV;
+ goto free_opp;
+ }
+
+ opp_tables[cpu] = dev_pm_opp_set_supported_hw(cpu_dev,
+ &versions, 1);
+ if (IS_ERR(opp_tables[cpu])) {
+ ret = PTR_ERR(opp_tables[cpu]);
+ dev_err(cpu_dev, "Failed to set supported hardware\n");
+ goto free_opp;
+ }
+ }
+
+ cpufreq_dt_pdev = platform_device_register_simple("cpufreq-dt", -1,
+ NULL, 0);
+ if (!IS_ERR(cpufreq_dt_pdev))
+ return 0;
+
+ ret = PTR_ERR(cpufreq_dt_pdev);
+ dev_err(cpu_dev, "Failed to register platform device\n");
+
+free_opp:
+ for_each_possible_cpu(cpu) {
+ if (IS_ERR_OR_NULL(opp_tables[cpu]))
+ break;
+ dev_pm_opp_put_supported_hw(opp_tables[cpu]);
+ }
+
+ return ret;
+}
+
+static struct platform_driver qcom_cpufreq_kryo_driver = {
+ .probe = qcom_cpufreq_kryo_probe,
+ .driver = {
+ .name = "qcom-cpufreq-kryo",
+ },
+};
+
+static const struct of_device_id qcom_cpufreq_kryo_match_list[] __initconst = {
+ { .compatible = "qcom,apq8096", },
+ { .compatible = "qcom,msm8996", },
+};
+
+/*
+ * Since the driver depends on smem and nvmem drivers, which may
+ * return EPROBE_DEFER, all the real activity is done in the probe,
+ * which may be defered as well. The init here is only registering
+ * the driver and the platform device.
+ */
+static int __init qcom_cpufreq_kryo_init(void)
+{
+ struct device_node *np = of_find_node_by_path("/");
+ const struct of_device_id *match;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ match = of_match_node(qcom_cpufreq_kryo_match_list, np);
+ of_node_put(np);
+ if (!match)
+ return -ENODEV;
+
+ ret = platform_driver_register(&qcom_cpufreq_kryo_driver);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = PTR_ERR_OR_ZERO(platform_device_register_simple(
+ "qcom-cpufreq-kryo", -1, NULL, 0));
+ if (0 == ret)
+ return 0;
+
+ platform_driver_unregister(&qcom_cpufreq_kryo_driver);
+ return ret;
+}
+module_init(qcom_cpufreq_kryo_init);
+
+MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Kryo CPUfreq driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
^ permalink raw reply related
* [PATCH v15 0/2] Kryo CPU scaling driver
From: Ilia Lin @ 2018-05-30 2:39 UTC (permalink / raw)
To: ilia.lin, vireshk, nm, sboyd, robh, mark.rutland, rjw
Cc: linux-pm, devicetree, linux-kernel, ilialin
[v15]
* Addressed the kbuild error
[v14]
* Addressed comment from Sudeep about DT compatible
* Added MAINTAINERS entry
[v13]
* Addressed comment from Sudeep about DT compatible check on init
[v12]
* Addressed comments from Sudeep and Viresh about the single init
[v11]
* Addressed comment from Russel about device_node reference
* Addressed comment from Sudeep about the late_initcall
* Transformed init into probe to take care of deferals
[v10]
* Split the series into domains
* Addressed comments from Viresh and Sudeep about logical CPU numbering.
The qcom-cpufreq-kryo driver is aimed to support different SOC versions.
The driver reads eFuse information and chooses the required OPP subset
by passing the OPP supported-hw parameter.
The series depends on the series from Viresh:
https://patchwork.kernel.org/patch/10418139/
The previous spin was here:
https://patchwork.kernel.org/patch/10427315/
Ilia Lin (2):
cpufreq: Add Kryo CPU scaling driver
dt-bindings: cpufreq: Document operating-points-v2-kryo-cpu
.../devicetree/bindings/opp/kryo-cpufreq.txt | 680 +++++++++++++++++++++
MAINTAINERS | 7 +
drivers/cpufreq/Kconfig.arm | 11 +
drivers/cpufreq/Makefile | 1 +
drivers/cpufreq/cpufreq-dt-platdev.c | 3 +
drivers/cpufreq/qcom-cpufreq-kryo.c | 212 +++++++
6 files changed, 914 insertions(+)
create mode 100644 Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
create mode 100644 drivers/cpufreq/qcom-cpufreq-kryo.c
--
1.9.1
^ permalink raw reply
* Re: [RFC/RFT] [PATCH v2 4/6] cpufreq: intel_pstate: HWP boost performance on IO wakeup
From: Pandruvada, Srinivas @ 2018-05-29 22:24 UTC (permalink / raw)
To: rafael@kernel.org
Cc: linux-kernel@vger.kernel.org, peterz@infradead.org,
linux-pm@vger.kernel.org, juri.lelli@redhat.com,
rjw@rjwysocki.net, viresh.kumar@linaro.org,
mgorman@techsingularity.net, lenb@kernel.org
In-Reply-To: <CAJZ5v0iG+jNC77KCHNUAt5tAtrtgryz2U4P1s_d9vB0HzLyOFw@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 1936 bytes --]
On Tue, 2018-05-29 at 09:44 +0200, Rafael J. Wysocki wrote:
> On Thu, May 24, 2018 at 3:47 AM, Srinivas Pandruvada
> <srinivas.pandruvada@linux.intel.com> wrote:
[...]
> >
> > + cpu->sample.time = time;
> > + io_flag = test_and_clear_bit(SCHED_CPUFREQ_IOWAIT, &cpu-
> > >sched_flags);
>
> I don't think you need to use bit ops here.
Agree. This is not required here for just IO boost support.
>
> _update_util() runs under rq->lock for the target CPU, so it will not
> run concurrently on two different CPUs for the same target anyway.
>
> > + if (io_flag) {
> > + bool do_io = false;
> > +
> > + /*
> > + * Set iowait_boost flag and update time. Since IO
> > WAIT flag
> > + * is set all the time, we can't just conclude that
> > there is
> > + * some IO bound activity is scheduled on this CPU
> > with just
> > + * one occurrence. If we receive at least two in
> > two
> > + * consecutive ticks, then we treat as boost
> > candidate.
> > + */
> > + if (time_before64(time, cpu->last_io_update + 2 *
> > TICK_NSEC))
> > + do_io = true;
> > +
> > + cpu->last_io_update = time;
> > +
> > + if (do_io)
> > + intel_pstate_hwp_boost_up(cpu);
>
> But what happens if user space wants to update the limits while
> boosting is in effect? Shouldn't it take hwp_boost_min into account
> then?
User request has always higher priority. User min will be taken into
account as the boost min is updated under the the update util call
back, not just one time.
Thanks,
Srinivas
[-- Attachment #2: smime.p7s --]
[-- Type: application/x-pkcs7-signature, Size: 3290 bytes --]
^ permalink raw reply
* Re: [RFC/RFT] [PATCH v2 3/6] cpufreq: intel_pstate: Add update_util_hook for HWP
From: Srinivas Pandruvada @ 2018-05-29 22:17 UTC (permalink / raw)
To: Rafael J. Wysocki
Cc: Len Brown, Rafael J. Wysocki, Peter Zijlstra, Mel Gorman,
Linux PM, Linux Kernel Mailing List, Juri Lelli, Viresh Kumar
In-Reply-To: <CAJZ5v0hHD-qH45bnw91w+ky=rnrTW4yTYMdizU47S8Y8Avqc_g@mail.gmail.com>
On Tue, 2018-05-29 at 09:37 +0200, Rafael J. Wysocki wrote:
> On Thu, May 24, 2018 at 3:47 AM, Srinivas Pandruvada
> <srinivas.pandruvada@linux.intel.com> wrote:
> > When HWP dynamic boost is active then set the HWP specific update
> > util
> > hook.
> >
> > Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel
> > .com>
>
> Splitting this patch out of the next one is sort of artificial.
I will merge to the patch where the hwp_boost is getting used.
Thanks,
Srinivas
>
> > ---
> > drivers/cpufreq/intel_pstate.c | 21 +++++++++++++++++----
> > 1 file changed, 17 insertions(+), 4 deletions(-)
> >
> > diff --git a/drivers/cpufreq/intel_pstate.c
> > b/drivers/cpufreq/intel_pstate.c
> > index 6ad46e07cad6..382160570b5f 100644
> > --- a/drivers/cpufreq/intel_pstate.c
> > +++ b/drivers/cpufreq/intel_pstate.c
> > @@ -291,6 +291,7 @@ static struct pstate_funcs pstate_funcs
> > __read_mostly;
> >
> > static int hwp_active __read_mostly;
> > static bool per_cpu_limits __read_mostly;
> > +static bool hwp_boost __read_mostly;
>
> Because of this, among other things.
>
> >
> > static struct cpufreq_driver *intel_pstate_driver __read_mostly;
> >
> > @@ -1461,6 +1462,11 @@ static inline bool
> > intel_pstate_hwp_boost_down(struct cpudata *cpu)
> > return false;
> > }
> >
> > +static inline void intel_pstate_update_util_hwp(struct
> > update_util_data *data,
> > + u64 time, unsigned
> > int flags)
> > +{
> > +}
> > +
> > static inline void intel_pstate_calc_avg_perf(struct cpudata *cpu)
> > {
> > struct sample *sample = &cpu->sample;
> > @@ -1764,7 +1770,7 @@ static void
> > intel_pstate_set_update_util_hook(unsigned int cpu_num)
> > {
> > struct cpudata *cpu = all_cpu_data[cpu_num];
> >
> > - if (hwp_active)
> > + if (hwp_active && !hwp_boost)
> > return;
> >
> > if (cpu->update_util_set)
> > @@ -1772,8 +1778,12 @@ static void
> > intel_pstate_set_update_util_hook(unsigned int cpu_num)
> >
> > /* Prevent intel_pstate_update_util() from using stale
> > data. */
> > cpu->sample.time = 0;
> > - cpufreq_add_update_util_hook(cpu_num, &cpu->update_util,
> > - intel_pstate_update_util);
> > + if (hwp_active)
> > + cpufreq_add_update_util_hook(cpu_num, &cpu-
> > >update_util,
> > + intel_pstate_update_ut
> > il_hwp);
> > + else
> > + cpufreq_add_update_util_hook(cpu_num, &cpu-
> > >update_util,
> > + intel_pstate_update_ut
> > il);
> > cpu->update_util_set = true;
> > }
> >
> > @@ -1885,8 +1895,11 @@ static int intel_pstate_set_policy(struct
> > cpufreq_policy *policy)
> > intel_pstate_set_update_util_hook(policy->cpu);
> > }
> >
> > - if (hwp_active)
> > + if (hwp_active) {
> > + if (!hwp_boost)
> > + intel_pstate_clear_update_util_hook(policy-
> > >cpu);
> > intel_pstate_hwp_set(policy->cpu);
> > + }
> >
> > mutex_unlock(&intel_pstate_limits_lock);
> >
> > --
> > 2.13.6
> >
^ permalink raw reply
* [PATCH v4 11/27] x86/power/64: Adapt assembly for PIE support
From: Thomas Garnier @ 2018-05-29 22:15 UTC (permalink / raw)
To: kernel-hardening
Cc: Thomas Garnier, Rafael J. Wysocki, Pavel Machek, Thomas Gleixner,
Ingo Molnar, H. Peter Anvin, x86, linux-pm, linux-kernel
In-Reply-To: <20180529221625.33541-1-thgarnie@google.com>
Change the assembly code to use only relative references of symbols for the
kernel to be PIE compatible.
Position Independent Executable (PIE) support will allow to extend the
KASLR randomization range 0xffffffff80000000.
Signed-off-by: Thomas Garnier <thgarnie@google.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
arch/x86/power/hibernate_asm_64.S | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/power/hibernate_asm_64.S b/arch/x86/power/hibernate_asm_64.S
index ce8da3a0412c..6fdd7bbc3c33 100644
--- a/arch/x86/power/hibernate_asm_64.S
+++ b/arch/x86/power/hibernate_asm_64.S
@@ -24,7 +24,7 @@
#include <asm/frame.h>
ENTRY(swsusp_arch_suspend)
- movq $saved_context, %rax
+ leaq saved_context(%rip), %rax
movq %rsp, pt_regs_sp(%rax)
movq %rbp, pt_regs_bp(%rax)
movq %rsi, pt_regs_si(%rax)
@@ -115,7 +115,7 @@ ENTRY(restore_registers)
movq %rax, %cr4; # turn PGE back on
/* We don't restore %rax, it must be 0 anyway */
- movq $saved_context, %rax
+ leaq saved_context(%rip), %rax
movq pt_regs_sp(%rax), %rsp
movq pt_regs_bp(%rax), %rbp
movq pt_regs_si(%rax), %rsi
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
* [PATCH v4 09/27] x86/acpi: Adapt assembly for PIE support
From: Thomas Garnier @ 2018-05-29 22:15 UTC (permalink / raw)
To: kernel-hardening
Cc: Thomas Garnier, Rafael J. Wysocki, Len Brown, Pavel Machek,
Thomas Gleixner, Ingo Molnar, H. Peter Anvin, x86, linux-pm,
linux-kernel
In-Reply-To: <20180529221625.33541-1-thgarnie@google.com>
Change the assembly code to use only relative references of symbols for the
kernel to be PIE compatible.
Position Independent Executable (PIE) support will allow to extend the
KASLR randomization range 0xffffffff80000000.
Signed-off-by: Thomas Garnier <thgarnie@google.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
---
arch/x86/kernel/acpi/wakeup_64.S | 31 ++++++++++++++++---------------
1 file changed, 16 insertions(+), 15 deletions(-)
diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S
index 50b8ed0317a3..472659c0f811 100644
--- a/arch/x86/kernel/acpi/wakeup_64.S
+++ b/arch/x86/kernel/acpi/wakeup_64.S
@@ -14,7 +14,7 @@
* Hooray, we are in Long 64-bit mode (but still running in low memory)
*/
ENTRY(wakeup_long64)
- movq saved_magic, %rax
+ movq saved_magic(%rip), %rax
movq $0x123456789abcdef0, %rdx
cmpq %rdx, %rax
jne bogus_64_magic
@@ -25,14 +25,14 @@ ENTRY(wakeup_long64)
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
- movq saved_rsp, %rsp
+ movq saved_rsp(%rip), %rsp
- movq saved_rbx, %rbx
- movq saved_rdi, %rdi
- movq saved_rsi, %rsi
- movq saved_rbp, %rbp
+ movq saved_rbx(%rip), %rbx
+ movq saved_rdi(%rip), %rdi
+ movq saved_rsi(%rip), %rsi
+ movq saved_rbp(%rip), %rbp
- movq saved_rip, %rax
+ movq saved_rip(%rip), %rax
jmp *%rax
ENDPROC(wakeup_long64)
@@ -45,7 +45,7 @@ ENTRY(do_suspend_lowlevel)
xorl %eax, %eax
call save_processor_state
- movq $saved_context, %rax
+ leaq saved_context(%rip), %rax
movq %rsp, pt_regs_sp(%rax)
movq %rbp, pt_regs_bp(%rax)
movq %rsi, pt_regs_si(%rax)
@@ -64,13 +64,14 @@ ENTRY(do_suspend_lowlevel)
pushfq
popq pt_regs_flags(%rax)
- movq $.Lresume_point, saved_rip(%rip)
+ leaq .Lresume_point(%rip), %rax
+ movq %rax, saved_rip(%rip)
- movq %rsp, saved_rsp
- movq %rbp, saved_rbp
- movq %rbx, saved_rbx
- movq %rdi, saved_rdi
- movq %rsi, saved_rsi
+ movq %rsp, saved_rsp(%rip)
+ movq %rbp, saved_rbp(%rip)
+ movq %rbx, saved_rbx(%rip)
+ movq %rdi, saved_rdi(%rip)
+ movq %rsi, saved_rsi(%rip)
addq $8, %rsp
movl $3, %edi
@@ -82,7 +83,7 @@ ENTRY(do_suspend_lowlevel)
.align 4
.Lresume_point:
/* We don't restore %rax, it must be 0 anyway */
- movq $saved_context, %rax
+ leaq saved_context(%rip), %rax
movq saved_context_cr4(%rax), %rbx
movq %rbx, %cr4
movq saved_context_cr3(%rax), %rbx
--
2.17.0.921.gf22659ad46-goog
^ permalink raw reply related
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