diff mbox series

[v2,3/5] usb: typec: ucsi: ccg: enable runtime pm support

Message ID 20190520183750.2932-4-ajayg@nvidia.com (mailing list archive)
State Superseded
Headers show
Series usb: typec: ucsi: ccg: add runtime pm support | expand

Commit Message

Ajay Gupta May 20, 2019, 6:37 p.m. UTC
From: Ajay Gupta <ajayg@nvidia.com>

The change enables runtime pm support to UCSI CCG driver.
ucsi_send_command() is used in resume path and so exported
ucsi_send_command() symbol in ucsi.c for modular build.

Signed-off-by: Ajay Gupta <ajayg@nvidia.com>
---
Changes from v1->v2 : None

 drivers/usb/typec/ucsi/ucsi.c     |  1 +
 drivers/usb/typec/ucsi/ucsi_ccg.c | 60 +++++++++++++++++++++++++++++++
 2 files changed, 61 insertions(+)

Comments

Heikki Krogerus May 21, 2019, 1:37 p.m. UTC | #1
Hi,

On Mon, May 20, 2019 at 11:37:48AM -0700, Ajay Gupta wrote:
> +static int ucsi_ccg_resume(struct device *dev)
> +{
> +	struct i2c_client *client = to_i2c_client(dev);
> +	struct ucsi_ccg *uc = i2c_get_clientdata(client);
> +	struct ucsi *ucsi = uc->ucsi;
> +	struct ucsi_control c;
> +	int ret;
> +
> +	/* restore UCSI notification enable mask */
> +	UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
> +	ret = ucsi_send_command(ucsi, &c, NULL, 0);
> +	if (ret < 0) {
> +		dev_err(uc->dev, "%s: failed to set notification enable - %d\n",
> +			__func__, ret);
> +	}
> +	return 0;
> +}

I would prefer that we did this for all methods in ucsi.c, not just
ccgx. Could you add resume callback to struct ucsi_ppm, and then call
it here.

> +static int ucsi_ccg_runtime_suspend(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int ucsi_ccg_runtime_resume(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static int ucsi_ccg_runtime_idle(struct device *dev)
> +{
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops ucsi_ccg_pm = {
> +	.suspend = ucsi_ccg_suspend,
> +	.resume = ucsi_ccg_resume,
> +	.runtime_suspend = ucsi_ccg_runtime_suspend,
> +	.runtime_resume = ucsi_ccg_runtime_resume,
> +	.runtime_idle = ucsi_ccg_runtime_idle,
> +};
> +
>  static struct i2c_driver ucsi_ccg_driver = {
>  	.driver = {
>  		.name = "ucsi_ccg",
> +		.pm = &ucsi_ccg_pm,
>  	},
>  	.probe = ucsi_ccg_probe,
>  	.remove = ucsi_ccg_remove,

thanks,
Heikki Krogerus May 21, 2019, 1:49 p.m. UTC | #2
On Tue, May 21, 2019 at 04:37:27PM +0300, Heikki Krogerus wrote:
> Hi,
> 
> On Mon, May 20, 2019 at 11:37:48AM -0700, Ajay Gupta wrote:
> > +static int ucsi_ccg_resume(struct device *dev)
> > +{
> > +	struct i2c_client *client = to_i2c_client(dev);
> > +	struct ucsi_ccg *uc = i2c_get_clientdata(client);
> > +	struct ucsi *ucsi = uc->ucsi;
> > +	struct ucsi_control c;
> > +	int ret;
> > +
> > +	/* restore UCSI notification enable mask */
> > +	UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
> > +	ret = ucsi_send_command(ucsi, &c, NULL, 0);
> > +	if (ret < 0) {
> > +		dev_err(uc->dev, "%s: failed to set notification enable - %d\n",
> > +			__func__, ret);
> > +	}
> > +	return 0;
> > +}
> 
> I would prefer that we did this for all methods in ucsi.c, not just
> ccgx. Could you add resume callback to struct ucsi_ppm, and then call
> it here.
> 
> > +static int ucsi_ccg_runtime_suspend(struct device *dev)
> > +{
> > +	return 0;
> > +}
> > +
> > +static int ucsi_ccg_runtime_resume(struct device *dev)
> > +{
> > +	return 0;
> > +}
> > +
> > +static int ucsi_ccg_runtime_idle(struct device *dev)
> > +{
> > +	return 0;
> > +}

Oh yes, and do you really need to supply all of those stubs?

thanks,
Ajay Gupta May 21, 2019, 5:44 p.m. UTC | #3
Hi Heikki

> > +static int ucsi_ccg_resume(struct device *dev) {
> > +	struct i2c_client *client = to_i2c_client(dev);
> > +	struct ucsi_ccg *uc = i2c_get_clientdata(client);
> > +	struct ucsi *ucsi = uc->ucsi;
> > +	struct ucsi_control c;
> > +	int ret;
> > +
> > +	/* restore UCSI notification enable mask */
> > +	UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
> > +	ret = ucsi_send_command(ucsi, &c, NULL, 0);
> > +	if (ret < 0) {
> > +		dev_err(uc->dev, "%s: failed to set notification enable - %d\n",
> > +			__func__, ret);
> > +	}
> > +	return 0;
> > +}
> 
> I would prefer that we did this for all methods in ucsi.c, not just ccgx. Could you
> add resume callback to struct ucsi_ppm, and then call it here.
struct ucsi_ppm currently have .sync() and .cmd() callback which is implemented by
ucsi_ccg and ucsi_acpi and invoked by usci.c. 

Is it okay to add a callback in this structure and implement inside ucsi.c and invoke
from ucsi_ccg and ucsi_acpi? OR we can just add a function in ucsi.c and export it
and use it from ucsi_ccg and ucsi_acpi?

> 
> > +static int ucsi_ccg_runtime_suspend(struct device *dev) {
> > +	return 0;
> > +}
> > +
> > +static int ucsi_ccg_runtime_resume(struct device *dev) {
> > +	return 0;
> > +}
> > +
> > +static int ucsi_ccg_runtime_idle(struct device *dev) {
> > +	return 0;
> > +}

> > Oh yes, and do you really need to supply all of those stubs?
We can drop ucsi_ccg_runtime_idle() but we do need
ucsi_ccg_runtime_suspend() and ucsi_ccg_runtime_resume() for
runtime pm functionality.

Thanks
Ajay
> nvpublic
> > +
> > +static const struct dev_pm_ops ucsi_ccg_pm = {
> > +	.suspend = ucsi_ccg_suspend,
> > +	.resume = ucsi_ccg_resume,
> > +	.runtime_suspend = ucsi_ccg_runtime_suspend,
> > +	.runtime_resume = ucsi_ccg_runtime_resume,
> > +	.runtime_idle = ucsi_ccg_runtime_idle, };
> > +
> >  static struct i2c_driver ucsi_ccg_driver = {
> >  	.driver = {
> >  		.name = "ucsi_ccg",
> > +		.pm = &ucsi_ccg_pm,
> >  	},
> >  	.probe = ucsi_ccg_probe,
> >  	.remove = ucsi_ccg_remove,
> 
> thanks,
> 
> --
> heikki
Heikki Krogerus May 22, 2019, 11:12 a.m. UTC | #4
On Tue, May 21, 2019 at 05:44:50PM +0000, Ajay Gupta wrote:
> Hi Heikki
> 
> > > +static int ucsi_ccg_resume(struct device *dev) {
> > > +	struct i2c_client *client = to_i2c_client(dev);
> > > +	struct ucsi_ccg *uc = i2c_get_clientdata(client);
> > > +	struct ucsi *ucsi = uc->ucsi;
> > > +	struct ucsi_control c;
> > > +	int ret;
> > > +
> > > +	/* restore UCSI notification enable mask */
> > > +	UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
> > > +	ret = ucsi_send_command(ucsi, &c, NULL, 0);
> > > +	if (ret < 0) {
> > > +		dev_err(uc->dev, "%s: failed to set notification enable - %d\n",
> > > +			__func__, ret);
> > > +	}
> > > +	return 0;
> > > +}
> > 
> > I would prefer that we did this for all methods in ucsi.c, not just ccgx. Could you
> > add resume callback to struct ucsi_ppm, and then call it here.
> struct ucsi_ppm currently have .sync() and .cmd() callback which is implemented by
> ucsi_ccg and ucsi_acpi and invoked by usci.c. 
> 
> Is it okay to add a callback in this structure and implement inside ucsi.c and invoke
> from ucsi_ccg and ucsi_acpi? OR we can just add a function in ucsi.c and export it
> and use it from ucsi_ccg and ucsi_acpi?

Right! Export the function. Sorry.

thanks,
diff mbox series

Patch

diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
index 7850b851cecd..e9454134d399 100644
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -206,6 +206,7 @@  int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
 
 	return ret;
 }
+EXPORT_SYMBOL_GPL(ucsi_send_command);
 
 /* -------------------------------------------------------------------------- */
 
diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index 9d46aa9e4e35..cc7094ecda2d 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -14,6 +14,8 @@ 
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
 
 #include <asm/unaligned.h>
 #include "ucsi.h"
@@ -210,6 +212,7 @@  static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
 	if (quirks && quirks->max_read_len)
 		max_read_len = quirks->max_read_len;
 
+	pm_runtime_get_sync(uc->dev);
 	while (rem_len > 0) {
 		msgs[1].buf = &data[len - rem_len];
 		rlen = min_t(u16, rem_len, max_read_len);
@@ -218,12 +221,14 @@  static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
 		status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 		if (status < 0) {
 			dev_err(uc->dev, "i2c_transfer failed %d\n", status);
+			pm_runtime_put_sync(uc->dev);
 			return status;
 		}
 		rab += rlen;
 		rem_len -= rlen;
 	}
 
+	pm_runtime_put_sync(uc->dev);
 	return 0;
 }
 
@@ -249,13 +254,16 @@  static int ccg_write(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
 	msgs[0].len = len + sizeof(rab);
 	msgs[0].buf = buf;
 
+	pm_runtime_get_sync(uc->dev);
 	status = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
 	if (status < 0) {
 		dev_err(uc->dev, "i2c_transfer failed %d\n", status);
+		pm_runtime_put_sync(uc->dev);
 		kfree(buf);
 		return status;
 	}
 
+	pm_runtime_put_sync(uc->dev);
 	kfree(buf);
 	return 0;
 }
@@ -1134,6 +1142,10 @@  static int ucsi_ccg_probe(struct i2c_client *client,
 	if (status)
 		dev_err(uc->dev, "cannot create sysfs group: %d\n", status);
 
+	pm_runtime_set_active(uc->dev);
+	pm_runtime_enable(uc->dev);
+	pm_runtime_idle(uc->dev);
+
 	return 0;
 }
 
@@ -1143,6 +1155,7 @@  static int ucsi_ccg_remove(struct i2c_client *client)
 
 	cancel_work_sync(&uc->work);
 	ucsi_unregister_ppm(uc->ucsi);
+	pm_runtime_disable(uc->dev);
 	free_irq(uc->irq, uc);
 	sysfs_remove_group(&uc->dev->kobj, &ucsi_ccg_attr_group);
 
@@ -1155,9 +1168,56 @@  static const struct i2c_device_id ucsi_ccg_device_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id);
 
+static int ucsi_ccg_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ucsi_ccg_resume(struct device *dev)
+{
+	struct i2c_client *client = to_i2c_client(dev);
+	struct ucsi_ccg *uc = i2c_get_clientdata(client);
+	struct ucsi *ucsi = uc->ucsi;
+	struct ucsi_control c;
+	int ret;
+
+	/* restore UCSI notification enable mask */
+	UCSI_CMD_SET_NTFY_ENABLE(c, UCSI_ENABLE_NTFY_ALL);
+	ret = ucsi_send_command(ucsi, &c, NULL, 0);
+	if (ret < 0) {
+		dev_err(uc->dev, "%s: failed to set notification enable - %d\n",
+			__func__, ret);
+	}
+	return 0;
+}
+
+static int ucsi_ccg_runtime_suspend(struct device *dev)
+{
+	return 0;
+}
+
+static int ucsi_ccg_runtime_resume(struct device *dev)
+{
+	return 0;
+}
+
+static int ucsi_ccg_runtime_idle(struct device *dev)
+{
+	return 0;
+}
+
+static const struct dev_pm_ops ucsi_ccg_pm = {
+	.suspend = ucsi_ccg_suspend,
+	.resume = ucsi_ccg_resume,
+	.runtime_suspend = ucsi_ccg_runtime_suspend,
+	.runtime_resume = ucsi_ccg_runtime_resume,
+	.runtime_idle = ucsi_ccg_runtime_idle,
+};
+
 static struct i2c_driver ucsi_ccg_driver = {
 	.driver = {
 		.name = "ucsi_ccg",
+		.pm = &ucsi_ccg_pm,
 	},
 	.probe = ucsi_ccg_probe,
 	.remove = ucsi_ccg_remove,