diff mbox series

[v2,4/6] drivers: qcom: rpmh-rsc: Add RSC power domain support

Message ID 20190823081703.17325-5-mkshah@codeaurora.org (mailing list archive)
State Not Applicable, archived
Headers show
Series Add RSC power domain support | expand

Commit Message

Maulik Shah Aug. 23, 2019, 8:17 a.m. UTC
Add RSC power domain support. RSC is top level power domain in
hireachical CPU LPM modes. Once the rsc domain is down flush all
cached sleep and wake votes from controller.

Signed-off-by: Maulik Shah <mkshah@codeaurora.org>
---
 drivers/soc/qcom/rpmh-internal.h |  2 +
 drivers/soc/qcom/rpmh-rsc.c      | 84 ++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+)

Comments

Stephen Boyd Sept. 5, 2019, 5:32 p.m. UTC | #1
Quoting Maulik Shah (2019-08-23 01:17:01)
> diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
> index e278fc11fe5c..884b39599e8f 100644
> --- a/drivers/soc/qcom/rpmh-rsc.c
> +++ b/drivers/soc/qcom/rpmh-rsc.c
> @@ -498,6 +498,32 @@ static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
>         return ret;
>  }
>  
> +/**
> + *  rpmh_rsc_ctrlr_is_idle: Check if any of the AMCs are busy.
> + *
> + *  @drv: The controller
> + *
> + *  Returns false if the TCSes are engaged in handling requests,

Please use kernel-doc style for returns here.

> + *  True if controller is idle.
> + */
> +static bool rpmh_rsc_ctrlr_is_idle(struct rsc_drv *drv)
> +{
> +       int m;
> +       struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS);
> +       bool ret = true;
> +
> +       spin_lock(&drv->lock);

I think these need to be irqsave/restore still.

> +       for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
> +               if (!tcs_is_free(drv, m)) {

This snippet is from tcs_invalidate(). Please collapse it into some sort
of function or macro like for_each_tcs().

> +                       ret = false;
> +                       break;
> +               }
> +       }
> +       spin_unlock(&drv->lock);
> +
> +       return ret;
> +}
> +
>  /**
>   * rpmh_rsc_write_ctrl_data: Write request to the controller
>   *
> @@ -521,6 +547,53 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
>         return tcs_ctrl_write(drv, msg);
>  }
>  
> +static int rpmh_domain_power_off(struct generic_pm_domain *rsc_pd)
> +{
> +       struct rsc_drv *drv = container_of(rsc_pd, struct rsc_drv, rsc_pd);
> +
> +       /*
> +        * RPMh domain can not be powered off when there is pending ACK for
> +        * ACTIVE_TCS request. Exit when controller is busy.
> +        */
> +

Nitpick: Remove this extra newline.

> +       if (!rpmh_rsc_ctrlr_is_idle(drv))
> +               return -EBUSY;
> +
> +       return rpmh_flush(&drv->client);
> +}
> +
> +static int rpmh_probe_power_domain(struct platform_device *pdev,
> +                                  struct rsc_drv *drv)
> +{
> +       int ret;
> +       struct generic_pm_domain *rsc_pd = &drv->rsc_pd;
> +       struct device_node *dn = pdev->dev.of_node;
> +
> +       rsc_pd->name = kasprintf(GFP_KERNEL, "%s", dn->name);

Maybe use devm_kasprintf?

> +       if (!rsc_pd->name)
> +               return -ENOMEM;
> +
> +       rsc_pd->name = kbasename(rsc_pd->name);
> +       rsc_pd->power_off = rpmh_domain_power_off;
> +       rsc_pd->flags |= GENPD_FLAG_IRQ_SAFE;
> +
> +       ret = pm_genpd_init(rsc_pd, NULL, false);
> +       if (ret)
> +               goto free_name;
> +
> +       ret = of_genpd_add_provider_simple(dn, rsc_pd);
> +       if (ret)
> +               goto remove_pd;
> +
> +       return ret;
> +
> +remove_pd:
> +       pm_genpd_remove(rsc_pd);
> +free_name:
> +       kfree(rsc_pd->name);

And then drop this one?

> +       return ret;
> +}
> +
>  static int rpmh_probe_tcs_config(struct platform_device *pdev,
>                                  struct rsc_drv *drv)
>  {
> @@ -650,6 +723,17 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
>         if (ret)
>                 return ret;
>  
> +       /*
> +        * Power domain is not required for controllers that support 'solver'
> +        * mode where they can be in autonomous mode executing low power mode
> +        * to power down.
> +        */
> +       if (of_property_read_bool(dn, "#power-domain-cells")) {
> +               ret = rpmh_probe_power_domain(pdev, drv);
> +               if (ret)
> +                       return ret;
> +       }
> +
>         spin_lock_init(&drv->lock);
>         bitmap_zero(drv->tcs_in_use, MAX_TCS_NR);

What happens if it fails later on? The genpd provider is still sitting
around and needs to be removed on probe failure later on in this
function. It would be nicer if there wasn't another function to probe
the power domain and it was just inlined here actually. That way we
don't have to wonder about what's going on across two blocks of code.
Maulik Shah Feb. 3, 2020, 1:11 p.m. UTC | #2
On 9/5/2019 11:02 PM, Stephen Boyd wrote:
> Quoting Maulik Shah (2019-08-23 01:17:01)
>> diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
>> index e278fc11fe5c..884b39599e8f 100644
>> --- a/drivers/soc/qcom/rpmh-rsc.c
>> +++ b/drivers/soc/qcom/rpmh-rsc.c
>> @@ -498,6 +498,32 @@ static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
>>          return ret;
>>   }
>>   
>> +/**
>> + *  rpmh_rsc_ctrlr_is_idle: Check if any of the AMCs are busy.
>> + *
>> + *  @drv: The controller
>> + *
>> + *  Returns false if the TCSes are engaged in handling requests,
> Please use kernel-doc style for returns here.
Done.
>> + *  True if controller is idle.
>> + */
>> +static bool rpmh_rsc_ctrlr_is_idle(struct rsc_drv *drv)
>> +{
>> +       int m;
>> +       struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS);
>> +       bool ret = true;
>> +
>> +       spin_lock(&drv->lock);
> I think these need to be irqsave/restore still.
Done.
>> +       for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
>> +               if (!tcs_is_free(drv, m)) {
> This snippet is from tcs_invalidate(). Please collapse it into some sort
> of function or macro like for_each_tcs().
Keeping it as it is, the snippet is actually little different from 
tcs_invalidate.
>> +                       ret = false;
>> +                       break;
>> +               }
>> +       }
>> +       spin_unlock(&drv->lock);
>> +
>> +       return ret;
>> +}
>> +
>>   /**
>>    * rpmh_rsc_write_ctrl_data: Write request to the controller
>>    *
>> @@ -521,6 +547,53 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
>>          return tcs_ctrl_write(drv, msg);
>>   }
>>   
>> +static int rpmh_domain_power_off(struct generic_pm_domain *rsc_pd)
>> +{
>> +       struct rsc_drv *drv = container_of(rsc_pd, struct rsc_drv, rsc_pd);
>> +
>> +       /*
>> +        * RPMh domain can not be powered off when there is pending ACK for
>> +        * ACTIVE_TCS request. Exit when controller is busy.
>> +        */
>> +
> Nitpick: Remove this extra newline.
Done.
>> +       if (!rpmh_rsc_ctrlr_is_idle(drv))
>> +               return -EBUSY;
>> +
>> +       return rpmh_flush(&drv->client);
>> +}
>> +
>> +static int rpmh_probe_power_domain(struct platform_device *pdev,
>> +                                  struct rsc_drv *drv)
>> +{
>> +       int ret;
>> +       struct generic_pm_domain *rsc_pd = &drv->rsc_pd;
>> +       struct device_node *dn = pdev->dev.of_node;
>> +
>> +       rsc_pd->name = kasprintf(GFP_KERNEL, "%s", dn->name);
> Maybe use devm_kasprintf?
Done.
>> +       if (!rsc_pd->name)
>> +               return -ENOMEM;
>> +
>> +       rsc_pd->name = kbasename(rsc_pd->name);
>> +       rsc_pd->power_off = rpmh_domain_power_off;
>> +       rsc_pd->flags |= GENPD_FLAG_IRQ_SAFE;
>> +
>> +       ret = pm_genpd_init(rsc_pd, NULL, false);
>> +       if (ret)
>> +               goto free_name;
>> +
>> +       ret = of_genpd_add_provider_simple(dn, rsc_pd);
>> +       if (ret)
>> +               goto remove_pd;
>> +
>> +       return ret;
>> +
>> +remove_pd:
>> +       pm_genpd_remove(rsc_pd);
>> +free_name:
>> +       kfree(rsc_pd->name);
> And then drop this one?
Done.
>> +       return ret;
>> +}
>> +
>>   static int rpmh_probe_tcs_config(struct platform_device *pdev,
>>                                   struct rsc_drv *drv)
>>   {
>> @@ -650,6 +723,17 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
>>          if (ret)
>>                  return ret;
>>   
>> +       /*
>> +        * Power domain is not required for controllers that support 'solver'
>> +        * mode where they can be in autonomous mode executing low power mode
>> +        * to power down.
>> +        */
>> +       if (of_property_read_bool(dn, "#power-domain-cells")) {
>> +               ret = rpmh_probe_power_domain(pdev, drv);
>> +               if (ret)
>> +                       return ret;
>> +       }
>> +
>>          spin_lock_init(&drv->lock);
>>          bitmap_zero(drv->tcs_in_use, MAX_TCS_NR);
> What happens if it fails later on? The genpd provider is still sitting
> around and needs to be removed on probe failure later on in this
> function. It would be nicer if there wasn't another function to probe
> the power domain and it was just inlined here actually. That way we
> don't have to wonder about what's going on across two blocks of code.

Thanks for pointing this.  Moved it at the end of probe to avoid this.

>
diff mbox series

Patch

diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h
index 6eec32b97f83..3736c148effc 100644
--- a/drivers/soc/qcom/rpmh-internal.h
+++ b/drivers/soc/qcom/rpmh-internal.h
@@ -8,6 +8,7 @@ 
 #define __RPM_INTERNAL_H__
 
 #include <linux/bitmap.h>
+#include <linux/pm_domain.h>
 #include <soc/qcom/tcs.h>
 
 #define TCS_TYPE_NR			4
@@ -102,6 +103,7 @@  struct rsc_drv {
 	DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR);
 	spinlock_t lock;
 	struct rpmh_ctrlr client;
+	struct generic_pm_domain rsc_pd;
 };
 
 int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg);
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index e278fc11fe5c..884b39599e8f 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -498,6 +498,32 @@  static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg)
 	return ret;
 }
 
+/**
+ *  rpmh_rsc_ctrlr_is_idle: Check if any of the AMCs are busy.
+ *
+ *  @drv: The controller
+ *
+ *  Returns false if the TCSes are engaged in handling requests,
+ *  True if controller is idle.
+ */
+static bool rpmh_rsc_ctrlr_is_idle(struct rsc_drv *drv)
+{
+	int m;
+	struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS);
+	bool ret = true;
+
+	spin_lock(&drv->lock);
+	for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) {
+		if (!tcs_is_free(drv, m)) {
+			ret = false;
+			break;
+		}
+	}
+	spin_unlock(&drv->lock);
+
+	return ret;
+}
+
 /**
  * rpmh_rsc_write_ctrl_data: Write request to the controller
  *
@@ -521,6 +547,53 @@  int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg)
 	return tcs_ctrl_write(drv, msg);
 }
 
+static int rpmh_domain_power_off(struct generic_pm_domain *rsc_pd)
+{
+	struct rsc_drv *drv = container_of(rsc_pd, struct rsc_drv, rsc_pd);
+
+	/*
+	 * RPMh domain can not be powered off when there is pending ACK for
+	 * ACTIVE_TCS request. Exit when controller is busy.
+	 */
+
+	if (!rpmh_rsc_ctrlr_is_idle(drv))
+		return -EBUSY;
+
+	return rpmh_flush(&drv->client);
+}
+
+static int rpmh_probe_power_domain(struct platform_device *pdev,
+				   struct rsc_drv *drv)
+{
+	int ret;
+	struct generic_pm_domain *rsc_pd = &drv->rsc_pd;
+	struct device_node *dn = pdev->dev.of_node;
+
+	rsc_pd->name = kasprintf(GFP_KERNEL, "%s", dn->name);
+	if (!rsc_pd->name)
+		return -ENOMEM;
+
+	rsc_pd->name = kbasename(rsc_pd->name);
+	rsc_pd->power_off = rpmh_domain_power_off;
+	rsc_pd->flags |= GENPD_FLAG_IRQ_SAFE;
+
+	ret = pm_genpd_init(rsc_pd, NULL, false);
+	if (ret)
+		goto free_name;
+
+	ret = of_genpd_add_provider_simple(dn, rsc_pd);
+	if (ret)
+		goto remove_pd;
+
+	return ret;
+
+remove_pd:
+	pm_genpd_remove(rsc_pd);
+free_name:
+	kfree(rsc_pd->name);
+	return ret;
+}
+
 static int rpmh_probe_tcs_config(struct platform_device *pdev,
 				 struct rsc_drv *drv)
 {
@@ -650,6 +723,17 @@  static int rpmh_rsc_probe(struct platform_device *pdev)
 	if (ret)
 		return ret;
 
+	/*
+	 * Power domain is not required for controllers that support 'solver'
+	 * mode where they can be in autonomous mode executing low power mode
+	 * to power down.
+	 */
+	if (of_property_read_bool(dn, "#power-domain-cells")) {
+		ret = rpmh_probe_power_domain(pdev, drv);
+		if (ret)
+			return ret;
+	}
+
 	spin_lock_init(&drv->lock);
 	bitmap_zero(drv->tcs_in_use, MAX_TCS_NR);