Message ID | 20240328074131.2839871-3-quic_sibis@quicinc.com |
---|---|
State | New |
Headers | show |
Series | firmware: arm_scmi: Register and handle limits change notification | expand |
On 3/28/24 07:41, Sibi Sankar wrote: > Register for limit change notifications if supported and use the throttled > frequency from the notification to apply HW pressure. > > Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> > --- > > v4: > * Use a interim variable to show the khz calc. [Lukasz] > * Use driver_data to pass on the handle and scmi_dev instead of using > global variables. Dropped Lukasz's Rb due to adding these minor > changes. > > drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ > 1 file changed, 44 insertions(+) > > diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c > index 3b4f6bfb2f4c..d946b7a08258 100644 > --- a/drivers/cpufreq/scmi-cpufreq.c > +++ b/drivers/cpufreq/scmi-cpufreq.c > @@ -21,11 +21,18 @@ > #include <linux/types.h> > #include <linux/units.h> > > +struct scmi_cpufreq_driver_data { > + struct scmi_device *sdev; > + const struct scmi_handle *handle; > +}; > + > struct scmi_data { > int domain_id; > int nr_opp; > struct device *cpu_dev; > + struct cpufreq_policy *policy; > cpumask_var_t opp_shared_cpus; > + struct notifier_block limit_notify_nb; > }; > > static struct scmi_protocol_handle *ph; > @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { > NULL, > }; > > +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) > +{ > + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); > + struct scmi_perf_limits_report *limit_notify = data; > + struct cpufreq_policy *policy = priv->policy; > + unsigned int limit_freq_khz; > + > + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; > + > + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); > + > + cpufreq_update_pressure(policy); > + > + return NOTIFY_OK; > +} > + > static int scmi_cpufreq_init(struct cpufreq_policy *policy) > { > int ret, nr_opp, domain; > @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > struct device *cpu_dev; > struct scmi_data *priv; > struct cpufreq_frequency_table *freq_table; > + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); > > cpu_dev = get_cpu_device(policy->cpu); > if (!cpu_dev) { > @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > } > } > > + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; > + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, > + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, > + &domain, > + &priv->limit_notify_nb); > + if (ret) > + dev_warn(cpu_dev, > + "failed to register for limits change notifier for domain %d\n", domain); > + > + priv->policy = policy; > + > return 0; > > out_free_opp: > @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) > int ret; > struct device *dev = &sdev->dev; > const struct scmi_handle *handle; > + struct scmi_cpufreq_driver_data *data; > > handle = sdev->handle; > > if (!handle) > return -ENODEV; > > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->sdev = sdev; > + data->handle = handle; > + scmi_cpufreq_driver.driver_data = data; > + > perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); > if (IS_ERR(perf_ops)) > return PTR_ERR(perf_ops); LGTM, Reviewed-by: Lukasz Luba <lukasz.luba@arm.com>
On Thu, Mar 28, 2024 at 01:11:31PM +0530, Sibi Sankar wrote: > Register for limit change notifications if supported and use the throttled > frequency from the notification to apply HW pressure. > Hi Sibi, a bit late on this, sorry. Just a couple of nitpicks down below. > Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> > --- > > v4: > * Use a interim variable to show the khz calc. [Lukasz] > * Use driver_data to pass on the handle and scmi_dev instead of using > global variables. Dropped Lukasz's Rb due to adding these minor > changes. > > drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ > 1 file changed, 44 insertions(+) > > diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c > index 3b4f6bfb2f4c..d946b7a08258 100644 > --- a/drivers/cpufreq/scmi-cpufreq.c > +++ b/drivers/cpufreq/scmi-cpufreq.c > @@ -21,11 +21,18 @@ > #include <linux/types.h> > #include <linux/units.h> > > +struct scmi_cpufreq_driver_data { > + struct scmi_device *sdev; > + const struct scmi_handle *handle; > +}; > + > struct scmi_data { > int domain_id; > int nr_opp; > struct device *cpu_dev; > + struct cpufreq_policy *policy; > cpumask_var_t opp_shared_cpus; > + struct notifier_block limit_notify_nb; > }; > > static struct scmi_protocol_handle *ph; > @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { > NULL, > }; > > +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) > +{ > + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); > + struct scmi_perf_limits_report *limit_notify = data; > + struct cpufreq_policy *policy = priv->policy; > + unsigned int limit_freq_khz; > + > + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; > + > + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); > + > + cpufreq_update_pressure(policy); > + > + return NOTIFY_OK; > +} > + > static int scmi_cpufreq_init(struct cpufreq_policy *policy) > { > int ret, nr_opp, domain; > @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > struct device *cpu_dev; > struct scmi_data *priv; > struct cpufreq_frequency_table *freq_table; > + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); > > cpu_dev = get_cpu_device(policy->cpu); > if (!cpu_dev) { > @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > } > } > > + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; > + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, > + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, > + &domain, > + &priv->limit_notify_nb); > + if (ret) > + dev_warn(cpu_dev, or &data->sdev->dev which refers to this driver ? which is more informational ? no strong opinion just a question... > + "failed to register for limits change notifier for domain %d\n", domain); > + > + priv->policy = policy; > + > return 0; > > out_free_opp: > @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) > int ret; > struct device *dev = &sdev->dev; > const struct scmi_handle *handle; > + struct scmi_cpufreq_driver_data *data; > > handle = sdev->handle; ^^^ .... > > if (!handle) > return -ENODEV; > > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->sdev = sdev; > + data->handle = handle; ^^^ ... you dont need to pass around handle AND sdev really since you can access the handle from sdev. > + scmi_cpufreq_driver.driver_data = data; This is slightly better, but, as said, does not solve the multi-instance issue... ...the scmi cpufreq driver remains a driver that works only if instantiated (probed) once, given how the CPUFreq core handles cpufreq_driver registration itself... ...just a note about something to work on in the future...NOT a concern for this series. In general, LGTM. Thanks, Cristian
On Wed, May 01, 2024 at 09:21:30AM +0100, Cristian Marussi wrote: > On Thu, Mar 28, 2024 at 01:11:31PM +0530, Sibi Sankar wrote: > > Register for limit change notifications if supported and use the throttled > > frequency from the notification to apply HW pressure. > > > > Hi Sibi, > > a bit late on this, sorry. > ...forgot the tag :P > Just a couple of nitpicks down below. [snip] > In general, > > LGTM. > Reviewed-by: Cristian Marussi <cristian.marussi@arm.com> Thanks, Cristian
On 5/1/24 13:51, Cristian Marussi wrote: > On Thu, Mar 28, 2024 at 01:11:31PM +0530, Sibi Sankar wrote: >> Register for limit change notifications if supported and use the throttled >> frequency from the notification to apply HW pressure. >> > > Hi Sibi, > > a bit late on this, sorry. > > Just a couple of nitpicks down below. > >> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> >> --- >> >> v4: >> * Use a interim variable to show the khz calc. [Lukasz] >> * Use driver_data to pass on the handle and scmi_dev instead of using >> global variables. Dropped Lukasz's Rb due to adding these minor >> changes. >> >> drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ >> 1 file changed, 44 insertions(+) >> >> diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c >> index 3b4f6bfb2f4c..d946b7a08258 100644 >> --- a/drivers/cpufreq/scmi-cpufreq.c >> +++ b/drivers/cpufreq/scmi-cpufreq.c >> @@ -21,11 +21,18 @@ >> #include <linux/types.h> >> #include <linux/units.h> >> >> +struct scmi_cpufreq_driver_data { >> + struct scmi_device *sdev; >> + const struct scmi_handle *handle; >> +}; >> + >> struct scmi_data { >> int domain_id; >> int nr_opp; >> struct device *cpu_dev; >> + struct cpufreq_policy *policy; >> cpumask_var_t opp_shared_cpus; >> + struct notifier_block limit_notify_nb; >> }; >> >> static struct scmi_protocol_handle *ph; >> @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { >> NULL, >> }; >> >> +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) >> +{ >> + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); >> + struct scmi_perf_limits_report *limit_notify = data; >> + struct cpufreq_policy *policy = priv->policy; >> + unsigned int limit_freq_khz; >> + >> + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; >> + >> + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); >> + >> + cpufreq_update_pressure(policy); >> + >> + return NOTIFY_OK; >> +} >> + >> static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> { >> int ret, nr_opp, domain; >> @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> struct device *cpu_dev; >> struct scmi_data *priv; >> struct cpufreq_frequency_table *freq_table; >> + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); >> >> cpu_dev = get_cpu_device(policy->cpu); >> if (!cpu_dev) { >> @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> } >> } >> >> + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; >> + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, >> + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, >> + &domain, >> + &priv->limit_notify_nb); >> + if (ret) >> + dev_warn(cpu_dev, > > or &data->sdev->dev which refers to this driver ? which is more informational ? no strong opinion just a question... Pointing to the driver is better given that we already pass on domain info. > >> + "failed to register for limits change notifier for domain %d\n", domain); >> + >> + priv->policy = policy; >> + >> return 0; >> >> out_free_opp: >> @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) >> int ret; >> struct device *dev = &sdev->dev; >> const struct scmi_handle *handle; >> + struct scmi_cpufreq_driver_data *data; >> >> handle = sdev->handle; > > ^^^ .... >> >> if (!handle) >> return -ENODEV; >> >> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); >> + if (!data) >> + return -ENOMEM; >> + >> + data->sdev = sdev; >> + data->handle = handle; > > ^^^ ... you dont need to pass around handle AND sdev really > since you can access the handle from sdev. > >> + scmi_cpufreq_driver.driver_data = data; Ack setting sdev as driver data would suffice. Will fix it in the next re-spin. -Sibi > > This is slightly better, but, as said, does not solve the multi-instance issue... > ...the scmi cpufreq driver remains a driver that works only if instantiated (probed) > once, given how the CPUFreq core handles cpufreq_driver registration itself... > > ...just a note about something to work on in the future...NOT a concern for this series. > > In general, > > LGTM. > > Thanks, > Cristian >
Hi Sibi, On Thu, 28 Mar 2024 at 08:42, Sibi Sankar <quic_sibis@quicinc.com> wrote: > > Register for limit change notifications if supported and use the throttled > frequency from the notification to apply HW pressure. > > Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> > --- > > v4: > * Use a interim variable to show the khz calc. [Lukasz] > * Use driver_data to pass on the handle and scmi_dev instead of using > global variables. Dropped Lukasz's Rb due to adding these minor > changes. > > drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ > 1 file changed, 44 insertions(+) > > diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c > index 3b4f6bfb2f4c..d946b7a08258 100644 > --- a/drivers/cpufreq/scmi-cpufreq.c > +++ b/drivers/cpufreq/scmi-cpufreq.c > @@ -21,11 +21,18 @@ > #include <linux/types.h> > #include <linux/units.h> > > +struct scmi_cpufreq_driver_data { > + struct scmi_device *sdev; > + const struct scmi_handle *handle; > +}; > + > struct scmi_data { > int domain_id; > int nr_opp; > struct device *cpu_dev; > + struct cpufreq_policy *policy; > cpumask_var_t opp_shared_cpus; > + struct notifier_block limit_notify_nb; > }; > > static struct scmi_protocol_handle *ph; > @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { > NULL, > }; > > +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) > +{ > + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); > + struct scmi_perf_limits_report *limit_notify = data; > + struct cpufreq_policy *policy = priv->policy; > + unsigned int limit_freq_khz; > + > + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; > + > + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); > + > + cpufreq_update_pressure(policy); I noticed your patch while looking for other things in the archive but I don't think this is the right way to do it. cpufreq_update_pressure() aims to set to the scheduler the aggregation of all cappings set to cpufreq through the pm_qos and freq_qos_add_request(). Calling this function directly in scmi notification callback will overwrite the pm_qos aggregation. And at the opposite, any update of a pm_qos constraint will overwrite scmi notification. Instead you should better set a pm_qos constraint like others > + > + return NOTIFY_OK; > +} > + > static int scmi_cpufreq_init(struct cpufreq_policy *policy) > { > int ret, nr_opp, domain; > @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > struct device *cpu_dev; > struct scmi_data *priv; > struct cpufreq_frequency_table *freq_table; > + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); > > cpu_dev = get_cpu_device(policy->cpu); > if (!cpu_dev) { > @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) > } > } > > + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; > + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, > + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, > + &domain, > + &priv->limit_notify_nb); > + if (ret) > + dev_warn(cpu_dev, > + "failed to register for limits change notifier for domain %d\n", domain); > + > + priv->policy = policy; > + > return 0; > > out_free_opp: > @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) > int ret; > struct device *dev = &sdev->dev; > const struct scmi_handle *handle; > + struct scmi_cpufreq_driver_data *data; > > handle = sdev->handle; > > if (!handle) > return -ENODEV; > > + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); > + if (!data) > + return -ENOMEM; > + > + data->sdev = sdev; > + data->handle = handle; > + scmi_cpufreq_driver.driver_data = data; > + > perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); > if (IS_ERR(perf_ops)) > return PTR_ERR(perf_ops); > -- > 2.34.1 > >
On 5/28/24 14:38, Vincent Guittot wrote: > Hi Sibi, > Hey Vincent, Thanks for taking time to review the series :) > On Thu, 28 Mar 2024 at 08:42, Sibi Sankar <quic_sibis@quicinc.com> wrote: >> >> Register for limit change notifications if supported and use the throttled >> frequency from the notification to apply HW pressure. >> >> Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> >> --- >> >> v4: >> * Use a interim variable to show the khz calc. [Lukasz] >> * Use driver_data to pass on the handle and scmi_dev instead of using >> global variables. Dropped Lukasz's Rb due to adding these minor >> changes. >> >> drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ >> 1 file changed, 44 insertions(+) >> >> diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c >> index 3b4f6bfb2f4c..d946b7a08258 100644 >> --- a/drivers/cpufreq/scmi-cpufreq.c >> +++ b/drivers/cpufreq/scmi-cpufreq.c >> @@ -21,11 +21,18 @@ >> #include <linux/types.h> >> #include <linux/units.h> >> >> +struct scmi_cpufreq_driver_data { >> + struct scmi_device *sdev; >> + const struct scmi_handle *handle; >> +}; >> + >> struct scmi_data { >> int domain_id; >> int nr_opp; >> struct device *cpu_dev; >> + struct cpufreq_policy *policy; >> cpumask_var_t opp_shared_cpus; >> + struct notifier_block limit_notify_nb; >> }; >> >> static struct scmi_protocol_handle *ph; >> @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { >> NULL, >> }; >> >> +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) >> +{ >> + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); >> + struct scmi_perf_limits_report *limit_notify = data; >> + struct cpufreq_policy *policy = priv->policy; >> + unsigned int limit_freq_khz; >> + >> + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; >> + >> + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); >> + >> + cpufreq_update_pressure(policy); > > I noticed your patch while looking for other things in the archive but > I don't think this is the right way to do it. > > cpufreq_update_pressure() aims to set to the scheduler the aggregation > of all cappings set to cpufreq through the pm_qos and > freq_qos_add_request(). Calling this function directly in scmi > notification callback will overwrite the pm_qos aggregation. And at > the opposite, any update of a pm_qos constraint will overwrite scmi > notification. Instead you should better set a pm_qos constraint like > others Sure, I'll drop update_pressue and use the freq_qos_update_request to update the policy->max_freq_req with the new policy->max. -Sibi > >> + >> + return NOTIFY_OK; >> +} >> + >> static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> { >> int ret, nr_opp, domain; >> @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> struct device *cpu_dev; >> struct scmi_data *priv; >> struct cpufreq_frequency_table *freq_table; >> + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); >> >> cpu_dev = get_cpu_device(policy->cpu); >> if (!cpu_dev) { >> @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) >> } >> } >> >> + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; >> + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, >> + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, >> + &domain, >> + &priv->limit_notify_nb); >> + if (ret) >> + dev_warn(cpu_dev, >> + "failed to register for limits change notifier for domain %d\n", domain); >> + >> + priv->policy = policy; >> + >> return 0; >> >> out_free_opp: >> @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) >> int ret; >> struct device *dev = &sdev->dev; >> const struct scmi_handle *handle; >> + struct scmi_cpufreq_driver_data *data; >> >> handle = sdev->handle; >> >> if (!handle) >> return -ENODEV; >> >> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); >> + if (!data) >> + return -ENOMEM; >> + >> + data->sdev = sdev; >> + data->handle = handle; >> + scmi_cpufreq_driver.driver_data = data; >> + >> perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); >> if (IS_ERR(perf_ops)) >> return PTR_ERR(perf_ops); >> -- >> 2.34.1 >> >>
diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index 3b4f6bfb2f4c..d946b7a08258 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -21,11 +21,18 @@ #include <linux/types.h> #include <linux/units.h> +struct scmi_cpufreq_driver_data { + struct scmi_device *sdev; + const struct scmi_handle *handle; +}; + struct scmi_data { int domain_id; int nr_opp; struct device *cpu_dev; + struct cpufreq_policy *policy; cpumask_var_t opp_shared_cpus; + struct notifier_block limit_notify_nb; }; static struct scmi_protocol_handle *ph; @@ -174,6 +181,22 @@ static struct freq_attr *scmi_cpufreq_hw_attr[] = { NULL, }; +static int scmi_limit_notify_cb(struct notifier_block *nb, unsigned long event, void *data) +{ + struct scmi_data *priv = container_of(nb, struct scmi_data, limit_notify_nb); + struct scmi_perf_limits_report *limit_notify = data; + struct cpufreq_policy *policy = priv->policy; + unsigned int limit_freq_khz; + + limit_freq_khz = limit_notify->range_max_freq / HZ_PER_KHZ; + + policy->max = clamp(limit_freq_khz, policy->cpuinfo.min_freq, policy->cpuinfo.max_freq); + + cpufreq_update_pressure(policy); + + return NOTIFY_OK; +} + static int scmi_cpufreq_init(struct cpufreq_policy *policy) { int ret, nr_opp, domain; @@ -181,6 +204,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) struct device *cpu_dev; struct scmi_data *priv; struct cpufreq_frequency_table *freq_table; + struct scmi_cpufreq_driver_data *data = cpufreq_get_driver_data(); cpu_dev = get_cpu_device(policy->cpu); if (!cpu_dev) { @@ -294,6 +318,17 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) } } + priv->limit_notify_nb.notifier_call = scmi_limit_notify_cb; + ret = data->handle->notify_ops->devm_event_notifier_register(data->sdev, SCMI_PROTOCOL_PERF, + SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED, + &domain, + &priv->limit_notify_nb); + if (ret) + dev_warn(cpu_dev, + "failed to register for limits change notifier for domain %d\n", domain); + + priv->policy = policy; + return 0; out_free_opp: @@ -366,12 +401,21 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev) int ret; struct device *dev = &sdev->dev; const struct scmi_handle *handle; + struct scmi_cpufreq_driver_data *data; handle = sdev->handle; if (!handle) return -ENODEV; + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->sdev = sdev; + data->handle = handle; + scmi_cpufreq_driver.driver_data = data; + perf_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_PERF, &ph); if (IS_ERR(perf_ops)) return PTR_ERR(perf_ops);
Register for limit change notifications if supported and use the throttled frequency from the notification to apply HW pressure. Signed-off-by: Sibi Sankar <quic_sibis@quicinc.com> --- v4: * Use a interim variable to show the khz calc. [Lukasz] * Use driver_data to pass on the handle and scmi_dev instead of using global variables. Dropped Lukasz's Rb due to adding these minor changes. drivers/cpufreq/scmi-cpufreq.c | 44 ++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+)