diff mbox

[V4] i2c: imx: add runtime pm support to improve the performance

Message ID 1440469235-2328-1-git-send-email-b54642@freescale.com
State Superseded
Headers show

Commit Message

Gao Pan Aug. 25, 2015, 2:20 a.m. UTC
In our former i2c driver, i2c clk is enabled and disabled in
xfer function, which contributes to power saving. However,
the clk enable process brings a busy wait delay until the core
is stable. As a result, the performance is sacrificed.

To weigh the power consumption and i2c bus performance, runtime
pm is the good solution for it. The clk is enabled when a i2c
transfer starts, and disabled after a specifically defined delay.

Without the patch the test case (many eeprom reads) executes with approx:
real 1m7.735s
user 0m0.488s
sys 0m20.040s

With the patch the same test case (many eeprom reads) executes with approx:
real 0m54.241s
user 0m0.440s
sys 0m5.920s

Signed-off-by: Fugang Duan <B38611@freescale.com>
Signed-off-by: Gao Pan <b54642@freescale.com>
[wsa: fixed some indentation]
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
---
V2:
As Uwe Kleine-König's suggestion, the version do below changes:
 -call clk_prepare_enable in probe to avoid never enabling clock
  if CONFIG_PM is disabled
 -enable clock before request IRQ in probe
 -remove the pm staff in i2c_imx_isr

V3:
 -pm_runtime_get_sync returns < 0 as error

V4:
 -add pm_runtime_set_active before pm_runtime_enable
 -replace pm_runtime_put_autosuspend with pm_runtime_autosuspend
  in probe
 -add error disposal when i2c_add_numbered_adapter fails

 drivers/i2c/busses/i2c-imx.c | 79 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 68 insertions(+), 11 deletions(-)

Comments

Heiner Kallweit Aug. 25, 2015, 6:37 a.m. UTC | #1
Am 25.08.2015 um 04:20 schrieb Gao Pan:
> In our former i2c driver, i2c clk is enabled and disabled in
> xfer function, which contributes to power saving. However,
> the clk enable process brings a busy wait delay until the core
> is stable. As a result, the performance is sacrificed.
> 
> To weigh the power consumption and i2c bus performance, runtime
> pm is the good solution for it. The clk is enabled when a i2c
> transfer starts, and disabled after a specifically defined delay.
> 
> Without the patch the test case (many eeprom reads) executes with approx:
> real 1m7.735s
> user 0m0.488s
> sys 0m20.040s
> 
> With the patch the same test case (many eeprom reads) executes with approx:
> real 0m54.241s
> user 0m0.440s
> sys 0m5.920s
> 
> Signed-off-by: Fugang Duan <B38611@freescale.com>
> Signed-off-by: Gao Pan <b54642@freescale.com>
> [wsa: fixed some indentation]
> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
> ---
> V2:
> As Uwe Kleine-König's suggestion, the version do below changes:
>  -call clk_prepare_enable in probe to avoid never enabling clock
>   if CONFIG_PM is disabled
>  -enable clock before request IRQ in probe
>  -remove the pm staff in i2c_imx_isr
> 
> V3:
>  -pm_runtime_get_sync returns < 0 as error
> 
> V4:
>  -add pm_runtime_set_active before pm_runtime_enable
>  -replace pm_runtime_put_autosuspend with pm_runtime_autosuspend
>   in probe
>  -add error disposal when i2c_add_numbered_adapter fails
> 
>  drivers/i2c/busses/i2c-imx.c | 79 ++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 68 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
> index 785aa67..7c63047 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c
> @@ -53,6 +53,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/sched.h>
>  #include <linux/slab.h>
> +#include <linux/pm_runtime.h>
>  
>  /** Defines ********************************************************************
>  *******************************************************************************/
> @@ -118,6 +119,8 @@
>  #define I2CR_IEN_OPCODE_0	0x0
>  #define I2CR_IEN_OPCODE_1	I2CR_IEN
>  
> +#define I2C_PM_TIMEOUT		10 /* ms */
> +
>  /** Variables ******************************************************************
>  *******************************************************************************/
>  
> @@ -520,9 +523,6 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
>  
>  	i2c_imx_set_clk(i2c_imx);
>  
> -	result = clk_prepare_enable(i2c_imx->clk);
> -	if (result)
> -		return result;
>  	imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
>  	/* Enable I2C controller */
>  	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
> @@ -575,7 +575,6 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
>  	/* Disable I2C controller */
>  	temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
>  	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
> -	clk_disable_unprepare(i2c_imx->clk);
>  }
>  
>  static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
> @@ -894,6 +893,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
>  
>  	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
>  
> +	result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
> +	if (result < 0)
> +		goto out;
> +
>  	/* Start I2C transfer */
>  	result = i2c_imx_start(i2c_imx);
>  	if (result)
> @@ -950,6 +953,10 @@ fail0:
>  	/* Stop I2C transfer */
>  	i2c_imx_stop(i2c_imx);
>  
> +out:
> +	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
> +	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
> +
>  	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
>  		(result < 0) ? "error" : "success msg",
>  			(result < 0) ? result : num);
> @@ -1020,9 +1027,10 @@ static int i2c_imx_probe(struct platform_device *pdev)
>  
>  	ret = clk_prepare_enable(i2c_imx->clk);
>  	if (ret) {
> -		dev_err(&pdev->dev, "can't enable I2C clock\n");
> +		dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
>  		return ret;
>  	}
> +
>  	/* Request IRQ */
>  	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
>  				pdev->name, i2c_imx);
> @@ -1037,6 +1045,14 @@ static int i2c_imx_probe(struct platform_device *pdev)
>  	/* Set up adapter data */
>  	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
>  
> +	/* Set up platform driver data */
> +	platform_set_drvdata(pdev, i2c_imx);
> +
> +	pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
> +	pm_runtime_use_autosuspend(&pdev->dev);
> +	pm_runtime_set_active(&pdev->dev);
> +	pm_runtime_enable(&pdev->dev);
> +
>  	/* Set up clock divider */
>  	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
>  	ret = of_property_read_u32(pdev->dev.of_node,
> @@ -1053,12 +1069,13 @@ static int i2c_imx_probe(struct platform_device *pdev)
>  	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
>  	if (ret < 0) {
>  		dev_err(&pdev->dev, "registration failed\n");
> +		pm_runtime_mark_last_busy(&pdev->dev);
> +		pm_runtime_autosuspend(&pdev->dev);
It would be more consistent with the general practice not to clean up here
but to define a new error label, goto there and do the clean-up there.
And if adding the adapter fails most likely you don't want to autosuspend
but clean up and disable runtime PM.
>  		goto clk_disable;
>  	}
>  
> -	/* Set up platform driver data */
> -	platform_set_drvdata(pdev, i2c_imx);
> -	clk_disable_unprepare(i2c_imx->clk);
> +	pm_runtime_mark_last_busy(&pdev->dev);
> +	pm_runtime_autosuspend(&pdev->dev);
>From enabling runtime PM to here you don't hold a reference so theoretically
it might happen that the device gets suspended in-between.
I'm not able to say whether this is an actual potential issue here.
However to be on the safe side it wouldn't hurt to do a pm_runtime_get_xx
when enabling PM and use pm_runtime_put_autosuspend here.

Regards, Heiner
>  
>  	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
>  	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
> @@ -1079,6 +1096,11 @@ clk_disable:
>  static int i2c_imx_remove(struct platform_device *pdev)
>  {
>  	struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
> +	int ret;
> +
> +	ret = pm_runtime_get_sync(&pdev->dev);
> +	if (ret < 0)
> +		return ret;
>  
>  	/* remove adapter */
>  	dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
> @@ -1093,17 +1115,52 @@ static int i2c_imx_remove(struct platform_device *pdev)
>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
>  
> +	pm_runtime_put_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +
>  	return 0;
>  }
>  
> +#ifdef CONFIG_PM
> +static int i2c_imx_runtime_suspend(struct device *dev)
> +{
> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
> +
> +	clk_disable_unprepare(i2c_imx->clk);
> +
> +	return 0;
> +}
> +
> +static int i2c_imx_runtime_resume(struct device *dev)
> +{
> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
> +	int ret;
> +
> +	ret = clk_prepare_enable(i2c_imx->clk);
> +	if (ret)
> +		dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
> +
> +	return ret;
> +}
> +
> +static const struct dev_pm_ops i2c_imx_pm_ops = {
> +	SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
> +			   i2c_imx_runtime_resume, NULL)
> +};
> +#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
> +#else
> +#define I2C_IMX_PM_OPS NULL
> +#endif /* CONFIG_PM */
> +
>  static struct platform_driver i2c_imx_driver = {
>  	.probe = i2c_imx_probe,
>  	.remove = i2c_imx_remove,
> -	.driver	= {
> -		.name	= DRIVER_NAME,
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.pm = I2C_IMX_PM_OPS,
>  		.of_match_table = i2c_imx_dt_ids,
>  	},
> -	.id_table	= imx_i2c_devtype,
> +	.id_table = imx_i2c_devtype,
>  };
>  
>  static int __init i2c_adap_imx_init(void)
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gao Pandy Aug. 25, 2015, 11:09 a.m. UTC | #2
RnJvbTogSGVpbmVyIEthbGx3ZWl0IDxtYWlsdG86aGthbGx3ZWl0MUBnbWFpbC5jb20+IFNlbnQ6
IFR1ZXNkYXksIEF1Z3VzdCAyNSwgMjAxNSAyOjM3IFBNDQo+IFRvOiBHYW8gUGFuLUI1NDY0Mjsg
d3NhQHRoZS1kcmVhbXMuZGUNCj4gQ2M6IGxpbnV4LWkyY0B2Z2VyLmtlcm5lbC5vcmc7IExpIEZy
YW5rLUIyMDU5NjsgRHVhbiBGdWdhbmctQjM4NjExOw0KPiB1LmtsZWluZS1rb2VuaWdAcGVuZ3V0
cm9uaXguZGU7IGtlcm5lbEBwZW5ndXRyb25peC5kZQ0KPiBTdWJqZWN0OiBSZTogW1BhdGNoIFY0
XSBpMmM6IGlteDogYWRkIHJ1bnRpbWUgcG0gc3VwcG9ydCB0byBpbXByb3ZlIHRoZQ0KPiBwZXJm
b3JtYW5jZQ0KPiANCj4gQW0gMjUuMDguMjAxNSB1bSAwNDoyMCBzY2hyaWViIEdhbyBQYW46DQo+
ID4gSW4gb3VyIGZvcm1lciBpMmMgZHJpdmVyLCBpMmMgY2xrIGlzIGVuYWJsZWQgYW5kIGRpc2Fi
bGVkIGluIHhmZXINCj4gPiBmdW5jdGlvbiwgd2hpY2ggY29udHJpYnV0ZXMgdG8gcG93ZXIgc2F2
aW5nLiBIb3dldmVyLCB0aGUgY2xrIGVuYWJsZQ0KPiA+IHByb2Nlc3MgYnJpbmdzIGEgYnVzeSB3
YWl0IGRlbGF5IHVudGlsIHRoZSBjb3JlIGlzIHN0YWJsZS4gQXMgYQ0KPiA+IHJlc3VsdCwgdGhl
IHBlcmZvcm1hbmNlIGlzIHNhY3JpZmljZWQuDQo+ID4NCj4gPiBUbyB3ZWlnaCB0aGUgcG93ZXIg
Y29uc3VtcHRpb24gYW5kIGkyYyBidXMgcGVyZm9ybWFuY2UsIHJ1bnRpbWUgcG0gaXMNCj4gPiB0
aGUgZ29vZCBzb2x1dGlvbiBmb3IgaXQuIFRoZSBjbGsgaXMgZW5hYmxlZCB3aGVuIGEgaTJjIHRy
YW5zZmVyDQo+ID4gc3RhcnRzLCBhbmQgZGlzYWJsZWQgYWZ0ZXIgYSBzcGVjaWZpY2FsbHkgZGVm
aW5lZCBkZWxheS4NCj4gPg0KPiA+IFdpdGhvdXQgdGhlIHBhdGNoIHRoZSB0ZXN0IGNhc2UgKG1h
bnkgZWVwcm9tIHJlYWRzKSBleGVjdXRlcyB3aXRoDQo+IGFwcHJveDoNCj4gPiByZWFsIDFtNy43
MzVzDQo+ID4gdXNlciAwbTAuNDg4cw0KPiA+IHN5cyAwbTIwLjA0MHMNCj4gPg0KPiA+IFdpdGgg
dGhlIHBhdGNoIHRoZSBzYW1lIHRlc3QgY2FzZSAobWFueSBlZXByb20gcmVhZHMpIGV4ZWN1dGVz
IHdpdGgNCj4gYXBwcm94Og0KPiA+IHJlYWwgMG01NC4yNDFzDQo+ID4gdXNlciAwbTAuNDQwcw0K
PiA+IHN5cyAwbTUuOTIwcw0KPiA+DQo+ID4gU2lnbmVkLW9mZi1ieTogRnVnYW5nIER1YW4gPEIz
ODYxMUBmcmVlc2NhbGUuY29tPg0KPiA+IFNpZ25lZC1vZmYtYnk6IEdhbyBQYW4gPGI1NDY0MkBm
cmVlc2NhbGUuY29tPg0KPiA+IFt3c2E6IGZpeGVkIHNvbWUgaW5kZW50YXRpb25dDQo+ID4gU2ln
bmVkLW9mZi1ieTogV29sZnJhbSBTYW5nIDx3c2FAdGhlLWRyZWFtcy5kZT4NCj4gPiAtLS0NCj4g
PiBWMjoNCj4gPiBBcyBVd2UgS2xlaW5lLUvDtm5pZydzIHN1Z2dlc3Rpb24sIHRoZSB2ZXJzaW9u
IGRvIGJlbG93IGNoYW5nZXM6DQo+ID4gIC1jYWxsIGNsa19wcmVwYXJlX2VuYWJsZSBpbiBwcm9i
ZSB0byBhdm9pZCBuZXZlciBlbmFibGluZyBjbG9jaw0KPiA+ICAgaWYgQ09ORklHX1BNIGlzIGRp
c2FibGVkDQo+ID4gIC1lbmFibGUgY2xvY2sgYmVmb3JlIHJlcXVlc3QgSVJRIGluIHByb2JlICAt
cmVtb3ZlIHRoZSBwbSBzdGFmZiBpbg0KPiA+IGkyY19pbXhfaXNyDQo+ID4NCj4gPiBWMzoNCj4g
PiAgLXBtX3J1bnRpbWVfZ2V0X3N5bmMgcmV0dXJucyA8IDAgYXMgZXJyb3INCj4gPg0KPiA+IFY0
Og0KPiA+ICAtYWRkIHBtX3J1bnRpbWVfc2V0X2FjdGl2ZSBiZWZvcmUgcG1fcnVudGltZV9lbmFi
bGUgIC1yZXBsYWNlDQo+ID4gcG1fcnVudGltZV9wdXRfYXV0b3N1c3BlbmQgd2l0aCBwbV9ydW50
aW1lX2F1dG9zdXNwZW5kDQo+ID4gICBpbiBwcm9iZQ0KPiA+ICAtYWRkIGVycm9yIGRpc3Bvc2Fs
IHdoZW4gaTJjX2FkZF9udW1iZXJlZF9hZGFwdGVyIGZhaWxzDQo+ID4NCj4gPiAgZHJpdmVycy9p
MmMvYnVzc2VzL2kyYy1pbXguYyB8IDc5DQo+ID4gKysrKysrKysrKysrKysrKysrKysrKysrKysr
KysrKysrKysrKystLS0tLS0NCj4gPiAgMSBmaWxlIGNoYW5nZWQsIDY4IGluc2VydGlvbnMoKyks
IDExIGRlbGV0aW9ucygtKQ0KPiA+DQo+ID4gZGlmZiAtLWdpdCBhL2RyaXZlcnMvaTJjL2J1c3Nl
cy9pMmMtaW14LmMNCj4gPiBiL2RyaXZlcnMvaTJjL2J1c3Nlcy9pMmMtaW14LmMgaW5kZXggNzg1
YWE2Ny4uN2M2MzA0NyAxMDA2NDQNCj4gPiAtLS0gYS9kcml2ZXJzL2kyYy9idXNzZXMvaTJjLWlt
eC5jDQo+ID4gKysrIGIvZHJpdmVycy9pMmMvYnVzc2VzL2kyYy1pbXguYw0KPiA+IEBAIC01Myw2
ICs1Myw3IEBADQo+ID4gICNpbmNsdWRlIDxsaW51eC9wbGF0Zm9ybV9kZXZpY2UuaD4NCj4gPiAg
I2luY2x1ZGUgPGxpbnV4L3NjaGVkLmg+DQo+ID4gICNpbmNsdWRlIDxsaW51eC9zbGFiLmg+DQo+
ID4gKyNpbmNsdWRlIDxsaW51eC9wbV9ydW50aW1lLmg+DQo+ID4NCj4gPiAgLyoqIERlZmluZXMN
Cj4gPiAqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq
KioqKioqKioqKioqKioqKg0KPiA+DQo+ID4gKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq
KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KPiA+ICoqKioqKioqKi8N
Cj4gPiBAQCAtMTE4LDYgKzExOSw4IEBADQo+ID4gICNkZWZpbmUgSTJDUl9JRU5fT1BDT0RFXzAJ
MHgwDQo+ID4gICNkZWZpbmUgSTJDUl9JRU5fT1BDT0RFXzEJSTJDUl9JRU4NCj4gPg0KPiA+ICsj
ZGVmaW5lIEkyQ19QTV9USU1FT1VUCQkxMCAvKiBtcyAqLw0KPiA+ICsNCj4gPiAgLyoqIFZhcmlh
Ymxlcw0KPiA+ICoqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq
KioqKioqKioqKioqKioqKioqKg0KPiA+DQo+ID4gKioqKioqKioqKioqKioqKioqKioqKioqKioq
KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKg0KPiA+ICoqKioqKioq
Ki8NCj4gPg0KPiA+IEBAIC01MjAsOSArNTIzLDYgQEAgc3RhdGljIGludCBpMmNfaW14X3N0YXJ0
KHN0cnVjdCBpbXhfaTJjX3N0cnVjdA0KPiA+ICppMmNfaW14KQ0KPiA+DQo+ID4gIAlpMmNfaW14
X3NldF9jbGsoaTJjX2lteCk7DQo+ID4NCj4gPiAtCXJlc3VsdCA9IGNsa19wcmVwYXJlX2VuYWJs
ZShpMmNfaW14LT5jbGspOw0KPiA+IC0JaWYgKHJlc3VsdCkNCj4gPiAtCQlyZXR1cm4gcmVzdWx0
Ow0KPiA+ICAJaW14X2kyY193cml0ZV9yZWcoaTJjX2lteC0+aWZkciwgaTJjX2lteCwgSU1YX0ky
Q19JRkRSKTsNCj4gPiAgCS8qIEVuYWJsZSBJMkMgY29udHJvbGxlciAqLw0KPiA+ICAJaW14X2ky
Y193cml0ZV9yZWcoaTJjX2lteC0+aHdkYXRhLT5pMnNyX2Nscl9vcGNvZGUsIGkyY19pbXgsDQo+
ID4gSU1YX0kyQ19JMlNSKTsgQEAgLTU3NSw3ICs1NzUsNiBAQCBzdGF0aWMgdm9pZCBpMmNfaW14
X3N0b3Aoc3RydWN0DQo+IGlteF9pMmNfc3RydWN0ICppMmNfaW14KQ0KPiA+ICAJLyogRGlzYWJs
ZSBJMkMgY29udHJvbGxlciAqLw0KPiA+ICAJdGVtcCA9IGkyY19pbXgtPmh3ZGF0YS0+aTJjcl9p
ZW5fb3Bjb2RlIF4gSTJDUl9JRU4sDQo+ID4gIAlpbXhfaTJjX3dyaXRlX3JlZyh0ZW1wLCBpMmNf
aW14LCBJTVhfSTJDX0kyQ1IpOw0KPiA+IC0JY2xrX2Rpc2FibGVfdW5wcmVwYXJlKGkyY19pbXgt
PmNsayk7DQo+ID4gIH0NCj4gPg0KPiA+ICBzdGF0aWMgaXJxcmV0dXJuX3QgaTJjX2lteF9pc3Io
aW50IGlycSwgdm9pZCAqZGV2X2lkKSBAQCAtODk0LDYNCj4gPiArODkzLDEwIEBAIHN0YXRpYyBp
bnQgaTJjX2lteF94ZmVyKHN0cnVjdCBpMmNfYWRhcHRlciAqYWRhcHRlciwNCj4gPg0KPiA+ICAJ
ZGV2X2RiZygmaTJjX2lteC0+YWRhcHRlci5kZXYsICI8JXM+XG4iLCBfX2Z1bmNfXyk7DQo+ID4N
Cj4gPiArCXJlc3VsdCA9IHBtX3J1bnRpbWVfZ2V0X3N5bmMoaTJjX2lteC0+YWRhcHRlci5kZXYu
cGFyZW50KTsNCj4gPiArCWlmIChyZXN1bHQgPCAwKQ0KPiA+ICsJCWdvdG8gb3V0Ow0KPiA+ICsN
Cj4gPiAgCS8qIFN0YXJ0IEkyQyB0cmFuc2ZlciAqLw0KPiA+ICAJcmVzdWx0ID0gaTJjX2lteF9z
dGFydChpMmNfaW14KTsNCj4gPiAgCWlmIChyZXN1bHQpDQo+ID4gQEAgLTk1MCw2ICs5NTMsMTAg
QEAgZmFpbDA6DQo+ID4gIAkvKiBTdG9wIEkyQyB0cmFuc2ZlciAqLw0KPiA+ICAJaTJjX2lteF9z
dG9wKGkyY19pbXgpOw0KPiA+DQo+ID4gK291dDoNCj4gPiArCXBtX3J1bnRpbWVfbWFya19sYXN0
X2J1c3koaTJjX2lteC0+YWRhcHRlci5kZXYucGFyZW50KTsNCj4gPiArCXBtX3J1bnRpbWVfcHV0
X2F1dG9zdXNwZW5kKGkyY19pbXgtPmFkYXB0ZXIuZGV2LnBhcmVudCk7DQo+ID4gKw0KPiA+ICAJ
ZGV2X2RiZygmaTJjX2lteC0+YWRhcHRlci5kZXYsICI8JXM+IGV4aXQgd2l0aDogJXM6ICVkXG4i
LCBfX2Z1bmNfXywNCj4gPiAgCQkocmVzdWx0IDwgMCkgPyAiZXJyb3IiIDogInN1Y2Nlc3MgbXNn
IiwNCj4gPiAgCQkJKHJlc3VsdCA8IDApID8gcmVzdWx0IDogbnVtKTsNCj4gPiBAQCAtMTAyMCw5
ICsxMDI3LDEwIEBAIHN0YXRpYyBpbnQgaTJjX2lteF9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2
aWNlDQo+ID4gKnBkZXYpDQo+ID4NCj4gPiAgCXJldCA9IGNsa19wcmVwYXJlX2VuYWJsZShpMmNf
aW14LT5jbGspOw0KPiA+ICAJaWYgKHJldCkgew0KPiA+IC0JCWRldl9lcnIoJnBkZXYtPmRldiwg
ImNhbid0IGVuYWJsZSBJMkMgY2xvY2tcbiIpOw0KPiA+ICsJCWRldl9lcnIoJnBkZXYtPmRldiwg
ImNhbid0IGVuYWJsZSBJMkMgY2xvY2ssIHJldD0lZFxuIiwgcmV0KTsNCj4gPiAgCQlyZXR1cm4g
cmV0Ow0KPiA+ICAJfQ0KPiA+ICsNCj4gPiAgCS8qIFJlcXVlc3QgSVJRICovDQo+ID4gIAlyZXQg
PSBkZXZtX3JlcXVlc3RfaXJxKCZwZGV2LT5kZXYsIGlycSwgaTJjX2lteF9pc3IsIDAsDQo+ID4g
IAkJCQlwZGV2LT5uYW1lLCBpMmNfaW14KTsNCj4gPiBAQCAtMTAzNyw2ICsxMDQ1LDE0IEBAIHN0
YXRpYyBpbnQgaTJjX2lteF9wcm9iZShzdHJ1Y3QgcGxhdGZvcm1fZGV2aWNlDQo+ICpwZGV2KQ0K
PiA+ICAJLyogU2V0IHVwIGFkYXB0ZXIgZGF0YSAqLw0KPiA+ICAJaTJjX3NldF9hZGFwZGF0YSgm
aTJjX2lteC0+YWRhcHRlciwgaTJjX2lteCk7DQo+ID4NCj4gPiArCS8qIFNldCB1cCBwbGF0Zm9y
bSBkcml2ZXIgZGF0YSAqLw0KPiA+ICsJcGxhdGZvcm1fc2V0X2RydmRhdGEocGRldiwgaTJjX2lt
eCk7DQo+ID4gKw0KPiA+ICsJcG1fcnVudGltZV9zZXRfYXV0b3N1c3BlbmRfZGVsYXkoJnBkZXYt
PmRldiwgSTJDX1BNX1RJTUVPVVQpOw0KPiA+ICsJcG1fcnVudGltZV91c2VfYXV0b3N1c3BlbmQo
JnBkZXYtPmRldik7DQo+ID4gKwlwbV9ydW50aW1lX3NldF9hY3RpdmUoJnBkZXYtPmRldik7DQo+
ID4gKwlwbV9ydW50aW1lX2VuYWJsZSgmcGRldi0+ZGV2KTsNCj4gPiArDQo+ID4gIAkvKiBTZXQg
dXAgY2xvY2sgZGl2aWRlciAqLw0KPiA+ICAJaTJjX2lteC0+Yml0cmF0ZSA9IElNWF9JMkNfQklU
X1JBVEU7DQo+ID4gIAlyZXQgPSBvZl9wcm9wZXJ0eV9yZWFkX3UzMihwZGV2LT5kZXYub2Zfbm9k
ZSwNCj4gPiBAQCAtMTA1MywxMiArMTA2OSwxMyBAQCBzdGF0aWMgaW50IGkyY19pbXhfcHJvYmUo
c3RydWN0IHBsYXRmb3JtX2RldmljZQ0KPiAqcGRldikNCj4gPiAgCXJldCA9IGkyY19hZGRfbnVt
YmVyZWRfYWRhcHRlcigmaTJjX2lteC0+YWRhcHRlcik7DQo+ID4gIAlpZiAocmV0IDwgMCkgew0K
PiA+ICAJCWRldl9lcnIoJnBkZXYtPmRldiwgInJlZ2lzdHJhdGlvbiBmYWlsZWRcbiIpOw0KPiA+
ICsJCXBtX3J1bnRpbWVfbWFya19sYXN0X2J1c3koJnBkZXYtPmRldik7DQo+ID4gKwkJcG1fcnVu
dGltZV9hdXRvc3VzcGVuZCgmcGRldi0+ZGV2KTsNCj4gSXQgd291bGQgYmUgbW9yZSBjb25zaXN0
ZW50IHdpdGggdGhlIGdlbmVyYWwgcHJhY3RpY2Ugbm90IHRvIGNsZWFuIHVwDQo+IGhlcmUgYnV0
IHRvIGRlZmluZSBhIG5ldyBlcnJvciBsYWJlbCwgZ290byB0aGVyZSBhbmQgZG8gdGhlIGNsZWFu
LXVwDQo+IHRoZXJlLg0KPiBBbmQgaWYgYWRkaW5nIHRoZSBhZGFwdGVyIGZhaWxzIG1vc3QgbGlr
ZWx5IHlvdSBkb24ndCB3YW50IHRvIGF1dG9zdXNwZW5kDQo+IGJ1dCBjbGVhbiB1cCBhbmQgZGlz
YWJsZSBydW50aW1lIFBNLg0KIA0KVGhhbmsgeW91LCB3aWxsIGNoYW5nZSBpdCBpbiBuZXh0IHZl
cnNpb24uDQoNCj4gPiAgCQlnb3RvIGNsa19kaXNhYmxlOw0KPiA+ICAJfQ0KPiA+DQo+ID4gLQkv
KiBTZXQgdXAgcGxhdGZvcm0gZHJpdmVyIGRhdGEgKi8NCj4gPiAtCXBsYXRmb3JtX3NldF9kcnZk
YXRhKHBkZXYsIGkyY19pbXgpOw0KPiA+IC0JY2xrX2Rpc2FibGVfdW5wcmVwYXJlKGkyY19pbXgt
PmNsayk7DQo+ID4gKwlwbV9ydW50aW1lX21hcmtfbGFzdF9idXN5KCZwZGV2LT5kZXYpOw0KPiA+
ICsJcG1fcnVudGltZV9hdXRvc3VzcGVuZCgmcGRldi0+ZGV2KTsNCj4gPkZyb20gZW5hYmxpbmcg
cnVudGltZSBQTSB0byBoZXJlIHlvdSBkb24ndCBob2xkIGEgcmVmZXJlbmNlIHNvDQo+ID50aGVv
cmV0aWNhbGx5DQo+IGl0IG1pZ2h0IGhhcHBlbiB0aGF0IHRoZSBkZXZpY2UgZ2V0cyBzdXNwZW5k
ZWQgaW4tYmV0d2Vlbi4NCj4gSSdtIG5vdCBhYmxlIHRvIHNheSB3aGV0aGVyIHRoaXMgaXMgYW4g
YWN0dWFsIHBvdGVudGlhbCBpc3N1ZSBoZXJlLg0KPiBIb3dldmVyIHRvIGJlIG9uIHRoZSBzYWZl
IHNpZGUgaXQgd291bGRuJ3QgaHVydCB0byBkbyBhIHBtX3J1bnRpbWVfZ2V0X3h4DQo+IHdoZW4g
ZW5hYmxpbmcgUE0gYW5kIHVzZSBwbV9ydW50aW1lX3B1dF9hdXRvc3VzcGVuZCBoZXJlLg0KDQpU
aGFua3MuIElmIEkgdXNlIHBtX3J1bnRpbWVfZ2V0X3h4IHdoZW4gZW5hYmxpbmcgUE0sIGNsa19w
cmVwYXJlX2VuYWJsZSB3aWxsIGJlIGNhbGxlZCBhZ2Fpbi4gDQpIb3dldmVyLCBpdCBoYXMgYmVl
biBjYWxsZWQgYmVmb3JlLiBJdCdzIGtpbmQgb2YgY29kZSByZWR1bmRhbmN5IHRvIHVzZSBwbV9y
dW50aW1lX2dldF94eC4NCg0KPiBSZWdhcmRzLCBIZWluZXINCj4gPg0KPiA+ICAJZGV2X2RiZygm
aTJjX2lteC0+YWRhcHRlci5kZXYsICJjbGFpbWVkIGlycSAlZFxuIiwgaXJxKTsNCj4gPiAgCWRl
dl9kYmcoJmkyY19pbXgtPmFkYXB0ZXIuZGV2LCAiZGV2aWNlIHJlc291cmNlczogJXBSXG4iLCBy
ZXMpOyBAQA0KPiA+IC0xMDc5LDYgKzEwOTYsMTEgQEAgY2xrX2Rpc2FibGU6DQo+ID4gIHN0YXRp
YyBpbnQgaTJjX2lteF9yZW1vdmUoc3RydWN0IHBsYXRmb3JtX2RldmljZSAqcGRldikgIHsNCj4g
PiAgCXN0cnVjdCBpbXhfaTJjX3N0cnVjdCAqaTJjX2lteCA9IHBsYXRmb3JtX2dldF9kcnZkYXRh
KHBkZXYpOw0KPiA+ICsJaW50IHJldDsNCj4gPiArDQo+ID4gKwlyZXQgPSBwbV9ydW50aW1lX2dl
dF9zeW5jKCZwZGV2LT5kZXYpOw0KPiA+ICsJaWYgKHJldCA8IDApDQo+ID4gKwkJcmV0dXJuIHJl
dDsNCj4gPg0KPiA+ICAJLyogcmVtb3ZlIGFkYXB0ZXIgKi8NCj4gPiAgCWRldl9kYmcoJmkyY19p
bXgtPmFkYXB0ZXIuZGV2LCAiYWRhcHRlciByZW1vdmVkXG4iKTsgQEAgLTEwOTMsMTcNCj4gPiAr
MTExNSw1MiBAQCBzdGF0aWMgaW50IGkyY19pbXhfcmVtb3ZlKHN0cnVjdCBwbGF0Zm9ybV9kZXZp
Y2UgKnBkZXYpDQo+ID4gIAlpbXhfaTJjX3dyaXRlX3JlZygwLCBpMmNfaW14LCBJTVhfSTJDX0ky
Q1IpOw0KPiA+ICAJaW14X2kyY193cml0ZV9yZWcoMCwgaTJjX2lteCwgSU1YX0kyQ19JMlNSKTsN
Cj4gPg0KPiA+ICsJcG1fcnVudGltZV9wdXRfc3luYygmcGRldi0+ZGV2KTsNCj4gPiArCXBtX3J1
bnRpbWVfZGlzYWJsZSgmcGRldi0+ZGV2KTsNCj4gPiArDQo+ID4gIAlyZXR1cm4gMDsNCj4gPiAg
fQ0KPiA+DQo+ID4gKyNpZmRlZiBDT05GSUdfUE0NCj4gPiArc3RhdGljIGludCBpMmNfaW14X3J1
bnRpbWVfc3VzcGVuZChzdHJ1Y3QgZGV2aWNlICpkZXYpIHsNCj4gPiArCXN0cnVjdCBpbXhfaTJj
X3N0cnVjdCAqaTJjX2lteCAgPSBkZXZfZ2V0X2RydmRhdGEoZGV2KTsNCj4gPiArDQo+ID4gKwlj
bGtfZGlzYWJsZV91bnByZXBhcmUoaTJjX2lteC0+Y2xrKTsNCj4gPiArDQo+ID4gKwlyZXR1cm4g
MDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3RhdGljIGludCBpMmNfaW14X3J1bnRpbWVfcmVzdW1l
KHN0cnVjdCBkZXZpY2UgKmRldikgew0KPiA+ICsJc3RydWN0IGlteF9pMmNfc3RydWN0ICppMmNf
aW14ICA9IGRldl9nZXRfZHJ2ZGF0YShkZXYpOw0KPiA+ICsJaW50IHJldDsNCj4gPiArDQo+ID4g
KwlyZXQgPSBjbGtfcHJlcGFyZV9lbmFibGUoaTJjX2lteC0+Y2xrKTsNCj4gPiArCWlmIChyZXQp
DQo+ID4gKwkJZGV2X2VycihkZXYsICJjYW4ndCBlbmFibGUgSTJDIGNsb2NrLCByZXQ9JWRcbiIs
IHJldCk7DQo+ID4gKw0KPiA+ICsJcmV0dXJuIHJldDsNCj4gPiArfQ0KPiA+ICsNCj4gPiArc3Rh
dGljIGNvbnN0IHN0cnVjdCBkZXZfcG1fb3BzIGkyY19pbXhfcG1fb3BzID0gew0KPiA+ICsJU0VU
X1JVTlRJTUVfUE1fT1BTKGkyY19pbXhfcnVudGltZV9zdXNwZW5kLA0KPiA+ICsJCQkgICBpMmNf
aW14X3J1bnRpbWVfcmVzdW1lLCBOVUxMKQ0KPiA+ICt9Ow0KPiA+ICsjZGVmaW5lIEkyQ19JTVhf
UE1fT1BTICgmaTJjX2lteF9wbV9vcHMpICNlbHNlICNkZWZpbmUgSTJDX0lNWF9QTV9PUFMNCj4g
PiArTlVMTCAjZW5kaWYgLyogQ09ORklHX1BNICovDQo+ID4gKw0KPiA+ICBzdGF0aWMgc3RydWN0
IHBsYXRmb3JtX2RyaXZlciBpMmNfaW14X2RyaXZlciA9IHsNCj4gPiAgCS5wcm9iZSA9IGkyY19p
bXhfcHJvYmUsDQo+ID4gIAkucmVtb3ZlID0gaTJjX2lteF9yZW1vdmUsDQo+ID4gLQkuZHJpdmVy
CT0gew0KPiA+IC0JCS5uYW1lCT0gRFJJVkVSX05BTUUsDQo+ID4gKwkuZHJpdmVyID0gew0KPiA+
ICsJCS5uYW1lID0gRFJJVkVSX05BTUUsDQo+ID4gKwkJLnBtID0gSTJDX0lNWF9QTV9PUFMsDQo+
ID4gIAkJLm9mX21hdGNoX3RhYmxlID0gaTJjX2lteF9kdF9pZHMsDQo+ID4gIAl9LA0KPiA+IC0J
LmlkX3RhYmxlCT0gaW14X2kyY19kZXZ0eXBlLA0KPiA+ICsJLmlkX3RhYmxlID0gaW14X2kyY19k
ZXZ0eXBlLA0KPiA+ICB9Ow0KPiA+DQo+ID4gIHN0YXRpYyBpbnQgX19pbml0IGkyY19hZGFwX2lt
eF9pbml0KHZvaWQpDQo+ID4NCg0K
--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gao Pandy Aug. 25, 2015, 11:13 a.m. UTC | #3
> From: linux-i2c-owner@vger.kernel.org [mailto:linux-i2c-

> owner@vger.kernel.org] On Behalf Of Gao Pandy

> Sent: Tuesday, August 25, 2015 7:09 PM

> To: Heiner Kallweit; wsa@the-dreams.de

> Cc: linux-i2c@vger.kernel.org; Li Frank-B20596; Duan Fugang-B38611;

> u.kleine-koenig@pengutronix.de; kernel@pengutronix.de

> Subject: RE: [Patch V4] i2c: imx: add runtime pm support to improve the

> performance

> 

> From: Heiner Kallweit <mailto:hkallweit1@gmail.com> Sent: Tuesday, August

> 25, 2015 2:37 PM

> > To: Gao Pan-B54642; wsa@the-dreams.de

> > Cc: linux-i2c@vger.kernel.org; Li Frank-B20596; Duan Fugang-B38611;

> > u.kleine-koenig@pengutronix.de; kernel@pengutronix.de

> > Subject: Re: [Patch V4] i2c: imx: add runtime pm support to improve

> > the performance

> >

> > Am 25.08.2015 um 04:20 schrieb Gao Pan:

> > > In our former i2c driver, i2c clk is enabled and disabled in xfer

> > > function, which contributes to power saving. However, the clk enable

> > > process brings a busy wait delay until the core is stable. As a

> > > result, the performance is sacrificed.

> > >

> > > To weigh the power consumption and i2c bus performance, runtime pm

> > > is the good solution for it. The clk is enabled when a i2c transfer

> > > starts, and disabled after a specifically defined delay.

> > >

> > > Without the patch the test case (many eeprom reads) executes with

> > approx:

> > > real 1m7.735s

> > > user 0m0.488s

> > > sys 0m20.040s

> > >

> > > With the patch the same test case (many eeprom reads) executes with

> > approx:

> > > real 0m54.241s

> > > user 0m0.440s

> > > sys 0m5.920s

> > >

> > > Signed-off-by: Fugang Duan <B38611@freescale.com>

> > > Signed-off-by: Gao Pan <b54642@freescale.com>

> > > [wsa: fixed some indentation]

> > > Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

> > > ---

> > > V2:

> > > As Uwe Kleine-König's suggestion, the version do below changes:

> > >  -call clk_prepare_enable in probe to avoid never enabling clock

> > >   if CONFIG_PM is disabled

> > >  -enable clock before request IRQ in probe  -remove the pm staff in

> > > i2c_imx_isr

> > >

> > > V3:

> > >  -pm_runtime_get_sync returns < 0 as error

> > >

> > > V4:

> > >  -add pm_runtime_set_active before pm_runtime_enable  -replace

> > > pm_runtime_put_autosuspend with pm_runtime_autosuspend

> > >   in probe

> > >  -add error disposal when i2c_add_numbered_adapter fails

> > >

> > >  drivers/i2c/busses/i2c-imx.c | 79

> > > ++++++++++++++++++++++++++++++++++++++------

> > >  1 file changed, 68 insertions(+), 11 deletions(-)

> > >

> > > diff --git a/drivers/i2c/busses/i2c-imx.c

> > > b/drivers/i2c/busses/i2c-imx.c index 785aa67..7c63047 100644

> > > --- a/drivers/i2c/busses/i2c-imx.c

> > > +++ b/drivers/i2c/busses/i2c-imx.c

> > > @@ -53,6 +53,7 @@

> > >  #include <linux/platform_device.h>

> > >  #include <linux/sched.h>

> > >  #include <linux/slab.h>

> > > +#include <linux/pm_runtime.h>

> > >

> > >  /** Defines

> > > ********************************************************************

> > >

> > > ********************************************************************

> > > **

> > > *********/

> > > @@ -118,6 +119,8 @@

> > >  #define I2CR_IEN_OPCODE_0	0x0

> > >  #define I2CR_IEN_OPCODE_1	I2CR_IEN

> > >

> > > +#define I2C_PM_TIMEOUT		10 /* ms */

> > > +

> > >  /** Variables

> > > ******************************************************************

> > >

> > > ********************************************************************

> > > **

> > > *********/

> > >

> > > @@ -520,9 +523,6 @@ static int i2c_imx_start(struct imx_i2c_struct

> > > *i2c_imx)

> > >

> > >  	i2c_imx_set_clk(i2c_imx);

> > >

> > > -	result = clk_prepare_enable(i2c_imx->clk);

> > > -	if (result)

> > > -		return result;

> > >  	imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);

> > >  	/* Enable I2C controller */

> > >  	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,

> > > IMX_I2C_I2SR); @@ -575,7 +575,6 @@ static void i2c_imx_stop(struct

> > imx_i2c_struct *i2c_imx)

> > >  	/* Disable I2C controller */

> > >  	temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,

> > >  	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);

> > > -	clk_disable_unprepare(i2c_imx->clk);

> > >  }

> > >

> > >  static irqreturn_t i2c_imx_isr(int irq, void *dev_id) @@ -894,6

> > > +893,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,

> > >

> > >  	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

> > >

> > > +	result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);

> > > +	if (result < 0)

> > > +		goto out;

> > > +

> > >  	/* Start I2C transfer */

> > >  	result = i2c_imx_start(i2c_imx);

> > >  	if (result)

> > > @@ -950,6 +953,10 @@ fail0:

> > >  	/* Stop I2C transfer */

> > >  	i2c_imx_stop(i2c_imx);

> > >

> > > +out:

> > > +	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);

> > > +	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);

> > > +

> > >  	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,

> > >  		(result < 0) ? "error" : "success msg",

> > >  			(result < 0) ? result : num);

> > > @@ -1020,9 +1027,10 @@ static int i2c_imx_probe(struct

> > > platform_device

> > > *pdev)

> > >

> > >  	ret = clk_prepare_enable(i2c_imx->clk);

> > >  	if (ret) {

> > > -		dev_err(&pdev->dev, "can't enable I2C clock\n");

> > > +		dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);

> > >  		return ret;

> > >  	}

> > > +

> > >  	/* Request IRQ */

> > >  	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,

> > >  				pdev->name, i2c_imx);

> > > @@ -1037,6 +1045,14 @@ static int i2c_imx_probe(struct

> > > platform_device

> > *pdev)

> > >  	/* Set up adapter data */

> > >  	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

> > >

> > > +	/* Set up platform driver data */

> > > +	platform_set_drvdata(pdev, i2c_imx);

> > > +

> > > +	pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);

> > > +	pm_runtime_use_autosuspend(&pdev->dev);

> > > +	pm_runtime_set_active(&pdev->dev);

> > > +	pm_runtime_enable(&pdev->dev);

> > > +

> > >  	/* Set up clock divider */

> > >  	i2c_imx->bitrate = IMX_I2C_BIT_RATE;

> > >  	ret = of_property_read_u32(pdev->dev.of_node,

> > > @@ -1053,12 +1069,13 @@ static int i2c_imx_probe(struct

> > > platform_device

> > *pdev)

> > >  	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);

> > >  	if (ret < 0) {

> > >  		dev_err(&pdev->dev, "registration failed\n");

> > > +		pm_runtime_mark_last_busy(&pdev->dev);

> > > +		pm_runtime_autosuspend(&pdev->dev);

> > It would be more consistent with the general practice not to clean up

> > here but to define a new error label, goto there and do the clean-up

> > there.

> > And if adding the adapter fails most likely you don't want to

> > autosuspend but clean up and disable runtime PM.

> 

> Thank you, will change it in next version.

> 

> > >  		goto clk_disable;

> > >  	}

> > >

> > > -	/* Set up platform driver data */

> > > -	platform_set_drvdata(pdev, i2c_imx);

> > > -	clk_disable_unprepare(i2c_imx->clk);

> > > +	pm_runtime_mark_last_busy(&pdev->dev);

> > > +	pm_runtime_autosuspend(&pdev->dev);

> > >From enabling runtime PM to here you don't hold a reference so

> > >theoretically

> > it might happen that the device gets suspended in-between.

> > I'm not able to say whether this is an actual potential issue here.

> > However to be on the safe side it wouldn't hurt to do a

> > pm_runtime_get_xx when enabling PM and use pm_runtime_put_autosuspend

> here.

> 

> Thanks. If I use pm_runtime_get_xx when enabling PM, clk_prepare_enable

> will be called again.

> However, it has been called before. It's kind of code redundancy to use

> pm_runtime_get_xx.

> 

> > Regards, Heiner

> > >

> > >  	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);

> > >  	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); @@

> > > -1079,6 +1096,11 @@ clk_disable:

> > >  static int i2c_imx_remove(struct platform_device *pdev)  {

> > >  	struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);

> > > +	int ret;

> > > +

> > > +	ret = pm_runtime_get_sync(&pdev->dev);

> > > +	if (ret < 0)

> > > +		return ret;

> > >

> > >  	/* remove adapter */

> > >  	dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); @@ -1093,17

> > > +1115,52 @@ static int i2c_imx_remove(struct platform_device *pdev)

> > >  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);

> > >  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);

> > >

> > > +	pm_runtime_put_sync(&pdev->dev);

> > > +	pm_runtime_disable(&pdev->dev);

> > > +

> > >  	return 0;

> > >  }

> > >

> > > +#ifdef CONFIG_PM

> > > +static int i2c_imx_runtime_suspend(struct device *dev) {

> > > +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);

> > > +

> > > +	clk_disable_unprepare(i2c_imx->clk);

> > > +

> > > +	return 0;

> > > +}

> > > +

> > > +static int i2c_imx_runtime_resume(struct device *dev) {

> > > +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);

> > > +	int ret;

> > > +

> > > +	ret = clk_prepare_enable(i2c_imx->clk);

> > > +	if (ret)

> > > +		dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);

> > > +

> > > +	return ret;

> > > +}

> > > +

> > > +static const struct dev_pm_ops i2c_imx_pm_ops = {

> > > +	SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,

> > > +			   i2c_imx_runtime_resume, NULL) }; #define

> I2C_IMX_PM_OPS

> > > +(&i2c_imx_pm_ops) #else #define I2C_IMX_PM_OPS NULL #endif /*

> > > +CONFIG_PM */

> > > +

> > >  static struct platform_driver i2c_imx_driver = {

> > >  	.probe = i2c_imx_probe,

> > >  	.remove = i2c_imx_remove,

> > > -	.driver	= {

> > > -		.name	= DRIVER_NAME,

> > > +	.driver = {

> > > +		.name = DRIVER_NAME,

> > > +		.pm = I2C_IMX_PM_OPS,

> > >  		.of_match_table = i2c_imx_dt_ids,

> > >  	},

> > > -	.id_table	= imx_i2c_devtype,

> > > +	.id_table = imx_i2c_devtype,

> > >  };

> > >

> > >  static int __init i2c_adap_imx_init(void)
Heiner Kallweit Aug. 25, 2015, 7:53 p.m. UTC | #4
Am 25.08.2015 um 13:09 schrieb Gao Pandy:
> From: Heiner Kallweit <mailto:hkallweit1@gmail.com> Sent: Tuesday, August 25, 2015 2:37 PM
>> To: Gao Pan-B54642; wsa@the-dreams.de
>> Cc: linux-i2c@vger.kernel.org; Li Frank-B20596; Duan Fugang-B38611;
>> u.kleine-koenig@pengutronix.de; kernel@pengutronix.de
>> Subject: Re: [Patch V4] i2c: imx: add runtime pm support to improve the
>> performance
>>
>> Am 25.08.2015 um 04:20 schrieb Gao Pan:
>>> In our former i2c driver, i2c clk is enabled and disabled in xfer
>>> function, which contributes to power saving. However, the clk enable
>>> process brings a busy wait delay until the core is stable. As a
>>> result, the performance is sacrificed.
>>>
>>> To weigh the power consumption and i2c bus performance, runtime pm is
>>> the good solution for it. The clk is enabled when a i2c transfer
>>> starts, and disabled after a specifically defined delay.
>>>
>>> Without the patch the test case (many eeprom reads) executes with
>> approx:
>>> real 1m7.735s
>>> user 0m0.488s
>>> sys 0m20.040s
>>>
>>> With the patch the same test case (many eeprom reads) executes with
>> approx:
>>> real 0m54.241s
>>> user 0m0.440s
>>> sys 0m5.920s
>>>
>>> Signed-off-by: Fugang Duan <B38611@freescale.com>
>>> Signed-off-by: Gao Pan <b54642@freescale.com>
>>> [wsa: fixed some indentation]
>>> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
>>> ---
>>> V2:
>>> As Uwe Kleine-König's suggestion, the version do below changes:
>>>  -call clk_prepare_enable in probe to avoid never enabling clock
>>>   if CONFIG_PM is disabled
>>>  -enable clock before request IRQ in probe  -remove the pm staff in
>>> i2c_imx_isr
>>>
>>> V3:
>>>  -pm_runtime_get_sync returns < 0 as error
>>>
>>> V4:
>>>  -add pm_runtime_set_active before pm_runtime_enable  -replace
>>> pm_runtime_put_autosuspend with pm_runtime_autosuspend
>>>   in probe
>>>  -add error disposal when i2c_add_numbered_adapter fails
>>>
>>>  drivers/i2c/busses/i2c-imx.c | 79
>>> ++++++++++++++++++++++++++++++++++++++------
>>>  1 file changed, 68 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/drivers/i2c/busses/i2c-imx.c
>>> b/drivers/i2c/busses/i2c-imx.c index 785aa67..7c63047 100644
>>> --- a/drivers/i2c/busses/i2c-imx.c
>>> +++ b/drivers/i2c/busses/i2c-imx.c
>>> @@ -53,6 +53,7 @@
>>>  #include <linux/platform_device.h>
>>>  #include <linux/sched.h>
>>>  #include <linux/slab.h>
>>> +#include <linux/pm_runtime.h>
>>>
>>>  /** Defines
>>> ********************************************************************
>>>
>>> **********************************************************************
>>> *********/
>>> @@ -118,6 +119,8 @@
>>>  #define I2CR_IEN_OPCODE_0	0x0
>>>  #define I2CR_IEN_OPCODE_1	I2CR_IEN
>>>
>>> +#define I2C_PM_TIMEOUT		10 /* ms */
>>> +
>>>  /** Variables
>>> ******************************************************************
>>>
>>> **********************************************************************
>>> *********/
>>>
>>> @@ -520,9 +523,6 @@ static int i2c_imx_start(struct imx_i2c_struct
>>> *i2c_imx)
>>>
>>>  	i2c_imx_set_clk(i2c_imx);
>>>
>>> -	result = clk_prepare_enable(i2c_imx->clk);
>>> -	if (result)
>>> -		return result;
>>>  	imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
>>>  	/* Enable I2C controller */
>>>  	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,
>>> IMX_I2C_I2SR); @@ -575,7 +575,6 @@ static void i2c_imx_stop(struct
>> imx_i2c_struct *i2c_imx)
>>>  	/* Disable I2C controller */
>>>  	temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
>>>  	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
>>> -	clk_disable_unprepare(i2c_imx->clk);
>>>  }
>>>
>>>  static irqreturn_t i2c_imx_isr(int irq, void *dev_id) @@ -894,6
>>> +893,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
>>>
>>>  	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
>>>
>>> +	result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
>>> +	if (result < 0)
>>> +		goto out;
>>> +
>>>  	/* Start I2C transfer */
>>>  	result = i2c_imx_start(i2c_imx);
>>>  	if (result)
>>> @@ -950,6 +953,10 @@ fail0:
>>>  	/* Stop I2C transfer */
>>>  	i2c_imx_stop(i2c_imx);
>>>
>>> +out:
>>> +	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
>>> +	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
>>> +
>>>  	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
>>>  		(result < 0) ? "error" : "success msg",
>>>  			(result < 0) ? result : num);
>>> @@ -1020,9 +1027,10 @@ static int i2c_imx_probe(struct platform_device
>>> *pdev)
>>>
>>>  	ret = clk_prepare_enable(i2c_imx->clk);
>>>  	if (ret) {
>>> -		dev_err(&pdev->dev, "can't enable I2C clock\n");
>>> +		dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
>>>  		return ret;
>>>  	}
>>> +
>>>  	/* Request IRQ */
>>>  	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
>>>  				pdev->name, i2c_imx);
>>> @@ -1037,6 +1045,14 @@ static int i2c_imx_probe(struct platform_device
>> *pdev)
>>>  	/* Set up adapter data */
>>>  	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
>>>
>>> +	/* Set up platform driver data */
>>> +	platform_set_drvdata(pdev, i2c_imx);
>>> +
>>> +	pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
>>> +	pm_runtime_use_autosuspend(&pdev->dev);
>>> +	pm_runtime_set_active(&pdev->dev);
>>> +	pm_runtime_enable(&pdev->dev);
>>> +
>>>  	/* Set up clock divider */
>>>  	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
>>>  	ret = of_property_read_u32(pdev->dev.of_node,
>>> @@ -1053,12 +1069,13 @@ static int i2c_imx_probe(struct platform_device
>> *pdev)
>>>  	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
>>>  	if (ret < 0) {
>>>  		dev_err(&pdev->dev, "registration failed\n");
>>> +		pm_runtime_mark_last_busy(&pdev->dev);
>>> +		pm_runtime_autosuspend(&pdev->dev);
>> It would be more consistent with the general practice not to clean up
>> here but to define a new error label, goto there and do the clean-up
>> there.
>> And if adding the adapter fails most likely you don't want to autosuspend
>> but clean up and disable runtime PM.
>  
> Thank you, will change it in next version.
> 
>>>  		goto clk_disable;
>>>  	}
>>>
>>> -	/* Set up platform driver data */
>>> -	platform_set_drvdata(pdev, i2c_imx);
>>> -	clk_disable_unprepare(i2c_imx->clk);
>>> +	pm_runtime_mark_last_busy(&pdev->dev);
>>> +	pm_runtime_autosuspend(&pdev->dev);
>> >From enabling runtime PM to here you don't hold a reference so
>>> theoretically
>> it might happen that the device gets suspended in-between.
>> I'm not able to say whether this is an actual potential issue here.
>> However to be on the safe side it wouldn't hurt to do a pm_runtime_get_xx
>> when enabling PM and use pm_runtime_put_autosuspend here.
> 
> Thanks. If I use pm_runtime_get_xx when enabling PM, clk_prepare_enable will be called again. 
> However, it has been called before. It's kind of code redundancy to use pm_runtime_get_xx.
No, it won't. This function checks whether the device is in state "active" and in this case
it doesn't call the resume callback and returns 1.
See also the source code of runtime PM in drivers/base/power/runtime.c

Rgds, Heiner
> 
>> Regards, Heiner
>>>
>>>  	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
>>>  	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); @@
>>> -1079,6 +1096,11 @@ clk_disable:
>>>  static int i2c_imx_remove(struct platform_device *pdev)  {
>>>  	struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
>>> +	int ret;
>>> +
>>> +	ret = pm_runtime_get_sync(&pdev->dev);
>>> +	if (ret < 0)
>>> +		return ret;
>>>
>>>  	/* remove adapter */
>>>  	dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); @@ -1093,17
>>> +1115,52 @@ static int i2c_imx_remove(struct platform_device *pdev)
>>>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
>>>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
>>>
>>> +	pm_runtime_put_sync(&pdev->dev);
>>> +	pm_runtime_disable(&pdev->dev);
>>> +
>>>  	return 0;
>>>  }
>>>
>>> +#ifdef CONFIG_PM
>>> +static int i2c_imx_runtime_suspend(struct device *dev) {
>>> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
>>> +
>>> +	clk_disable_unprepare(i2c_imx->clk);
>>> +
>>> +	return 0;
>>> +}
>>> +
>>> +static int i2c_imx_runtime_resume(struct device *dev) {
>>> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
>>> +	int ret;
>>> +
>>> +	ret = clk_prepare_enable(i2c_imx->clk);
>>> +	if (ret)
>>> +		dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
>>> +
>>> +	return ret;
>>> +}
>>> +
>>> +static const struct dev_pm_ops i2c_imx_pm_ops = {
>>> +	SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
>>> +			   i2c_imx_runtime_resume, NULL)
>>> +};
>>> +#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops) #else #define I2C_IMX_PM_OPS
>>> +NULL #endif /* CONFIG_PM */
>>> +
>>>  static struct platform_driver i2c_imx_driver = {
>>>  	.probe = i2c_imx_probe,
>>>  	.remove = i2c_imx_remove,
>>> -	.driver	= {
>>> -		.name	= DRIVER_NAME,
>>> +	.driver = {
>>> +		.name = DRIVER_NAME,
>>> +		.pm = I2C_IMX_PM_OPS,
>>>  		.of_match_table = i2c_imx_dt_ids,
>>>  	},
>>> -	.id_table	= imx_i2c_devtype,
>>> +	.id_table = imx_i2c_devtype,
>>>  };
>>>
>>>  static int __init i2c_adap_imx_init(void)
>>>
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-i2c" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Gao Pandy Aug. 26, 2015, 3:21 a.m. UTC | #5
From: Heiner Kallweit <mailto:hkallweit1@gmail.com> Sent: Wednesday, August 26, 2015 3:53 AM

> To: Gao Pan-B54642; wsa@the-dreams.de

> Cc: linux-i2c@vger.kernel.org; Li Frank-B20596; Duan Fugang-B38611;

> u.kleine-koenig@pengutronix.de; kernel@pengutronix.de

> Subject: Re: [Patch V4] i2c: imx: add runtime pm support to improve the

> performance

> 

> Am 25.08.2015 um 13:09 schrieb Gao Pandy:

> > From: Heiner Kallweit <mailto:hkallweit1@gmail.com> Sent: Tuesday,

> > August 25, 2015 2:37 PM

> >> To: Gao Pan-B54642; wsa@the-dreams.de

> >> Cc: linux-i2c@vger.kernel.org; Li Frank-B20596; Duan Fugang-B38611;

> >> u.kleine-koenig@pengutronix.de; kernel@pengutronix.de

> >> Subject: Re: [Patch V4] i2c: imx: add runtime pm support to improve

> >> the performance

> >>

> >> Am 25.08.2015 um 04:20 schrieb Gao Pan:

> >>> In our former i2c driver, i2c clk is enabled and disabled in xfer

> >>> function, which contributes to power saving. However, the clk enable

> >>> process brings a busy wait delay until the core is stable. As a

> >>> result, the performance is sacrificed.

> >>>

> >>> To weigh the power consumption and i2c bus performance, runtime pm

> >>> is the good solution for it. The clk is enabled when a i2c transfer

> >>> starts, and disabled after a specifically defined delay.

> >>>

> >>> Without the patch the test case (many eeprom reads) executes with

> >> approx:

> >>> real 1m7.735s

> >>> user 0m0.488s

> >>> sys 0m20.040s

> >>>

> >>> With the patch the same test case (many eeprom reads) executes with

> >> approx:

> >>> real 0m54.241s

> >>> user 0m0.440s

> >>> sys 0m5.920s

> >>>

> >>> Signed-off-by: Fugang Duan <B38611@freescale.com>

> >>> Signed-off-by: Gao Pan <b54642@freescale.com>

> >>> [wsa: fixed some indentation]

> >>> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>

> >>> ---

> >>> V2:

> >>> As Uwe Kleine-König's suggestion, the version do below changes:

> >>>  -call clk_prepare_enable in probe to avoid never enabling clock

> >>>   if CONFIG_PM is disabled

> >>>  -enable clock before request IRQ in probe  -remove the pm staff in

> >>> i2c_imx_isr

> >>>

> >>> V3:

> >>>  -pm_runtime_get_sync returns < 0 as error

> >>>

> >>> V4:

> >>>  -add pm_runtime_set_active before pm_runtime_enable  -replace

> >>> pm_runtime_put_autosuspend with pm_runtime_autosuspend

> >>>   in probe

> >>>  -add error disposal when i2c_add_numbered_adapter fails

> >>>

> >>>  drivers/i2c/busses/i2c-imx.c | 79

> >>> ++++++++++++++++++++++++++++++++++++++------

> >>>  1 file changed, 68 insertions(+), 11 deletions(-)

> >>>

> >>> diff --git a/drivers/i2c/busses/i2c-imx.c

> >>> b/drivers/i2c/busses/i2c-imx.c index 785aa67..7c63047 100644

> >>> --- a/drivers/i2c/busses/i2c-imx.c

> >>> +++ b/drivers/i2c/busses/i2c-imx.c

> >>> @@ -53,6 +53,7 @@

> >>>  #include <linux/platform_device.h>

> >>>  #include <linux/sched.h>

> >>>  #include <linux/slab.h>

> >>> +#include <linux/pm_runtime.h>

> >>>

> >>>  /** Defines

> >>> ********************************************************************

> >>>

> >>> ********************************************************************

> >>> **

> >>> *********/

> >>> @@ -118,6 +119,8 @@

> >>>  #define I2CR_IEN_OPCODE_0	0x0

> >>>  #define I2CR_IEN_OPCODE_1	I2CR_IEN

> >>>

> >>> +#define I2C_PM_TIMEOUT		10 /* ms */

> >>> +

> >>>  /** Variables

> >>> ******************************************************************

> >>>

> >>> ********************************************************************

> >>> **

> >>> *********/

> >>>

> >>> @@ -520,9 +523,6 @@ static int i2c_imx_start(struct imx_i2c_struct

> >>> *i2c_imx)

> >>>

> >>>  	i2c_imx_set_clk(i2c_imx);

> >>>

> >>> -	result = clk_prepare_enable(i2c_imx->clk);

> >>> -	if (result)

> >>> -		return result;

> >>>  	imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);

> >>>  	/* Enable I2C controller */

> >>>  	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx,

> >>> IMX_I2C_I2SR); @@ -575,7 +575,6 @@ static void i2c_imx_stop(struct

> >> imx_i2c_struct *i2c_imx)

> >>>  	/* Disable I2C controller */

> >>>  	temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,

> >>>  	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);

> >>> -	clk_disable_unprepare(i2c_imx->clk);

> >>>  }

> >>>

> >>>  static irqreturn_t i2c_imx_isr(int irq, void *dev_id) @@ -894,6

> >>> +893,10 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,

> >>>

> >>>  	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);

> >>>

> >>> +	result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);

> >>> +	if (result < 0)

> >>> +		goto out;

> >>> +

> >>>  	/* Start I2C transfer */

> >>>  	result = i2c_imx_start(i2c_imx);

> >>>  	if (result)

> >>> @@ -950,6 +953,10 @@ fail0:

> >>>  	/* Stop I2C transfer */

> >>>  	i2c_imx_stop(i2c_imx);

> >>>

> >>> +out:

> >>> +	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);

> >>> +	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);

> >>> +

> >>>  	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,

> >>>  		(result < 0) ? "error" : "success msg",

> >>>  			(result < 0) ? result : num);

> >>> @@ -1020,9 +1027,10 @@ static int i2c_imx_probe(struct

> >>> platform_device

> >>> *pdev)

> >>>

> >>>  	ret = clk_prepare_enable(i2c_imx->clk);

> >>>  	if (ret) {

> >>> -		dev_err(&pdev->dev, "can't enable I2C clock\n");

> >>> +		dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);

> >>>  		return ret;

> >>>  	}

> >>> +

> >>>  	/* Request IRQ */

> >>>  	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,

> >>>  				pdev->name, i2c_imx);

> >>> @@ -1037,6 +1045,14 @@ static int i2c_imx_probe(struct

> >>> platform_device

> >> *pdev)

> >>>  	/* Set up adapter data */

> >>>  	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);

> >>>

> >>> +	/* Set up platform driver data */

> >>> +	platform_set_drvdata(pdev, i2c_imx);

> >>> +

> >>> +	pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);

> >>> +	pm_runtime_use_autosuspend(&pdev->dev);

> >>> +	pm_runtime_set_active(&pdev->dev);

> >>> +	pm_runtime_enable(&pdev->dev);

> >>> +

> >>>  	/* Set up clock divider */

> >>>  	i2c_imx->bitrate = IMX_I2C_BIT_RATE;

> >>>  	ret = of_property_read_u32(pdev->dev.of_node,

> >>> @@ -1053,12 +1069,13 @@ static int i2c_imx_probe(struct

> >>> platform_device

> >> *pdev)

> >>>  	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);

> >>>  	if (ret < 0) {

> >>>  		dev_err(&pdev->dev, "registration failed\n");

> >>> +		pm_runtime_mark_last_busy(&pdev->dev);

> >>> +		pm_runtime_autosuspend(&pdev->dev);

> >> It would be more consistent with the general practice not to clean up

> >> here but to define a new error label, goto there and do the clean-up

> >> there.

> >> And if adding the adapter fails most likely you don't want to

> >> autosuspend but clean up and disable runtime PM.

> >

> > Thank you, will change it in next version.

> >

> >>>  		goto clk_disable;

> >>>  	}

> >>>

> >>> -	/* Set up platform driver data */

> >>> -	platform_set_drvdata(pdev, i2c_imx);

> >>> -	clk_disable_unprepare(i2c_imx->clk);

> >>> +	pm_runtime_mark_last_busy(&pdev->dev);

> >>> +	pm_runtime_autosuspend(&pdev->dev);

> >> >From enabling runtime PM to here you don't hold a reference so

> >>> theoretically

> >> it might happen that the device gets suspended in-between.

> >> I'm not able to say whether this is an actual potential issue here.

> >> However to be on the safe side it wouldn't hurt to do a

> >> pm_runtime_get_xx when enabling PM and use pm_runtime_put_autosuspend

> here.

> >

> > Thanks. If I use pm_runtime_get_xx when enabling PM, clk_prepare_enable

> will be called again.

> > However, it has been called before. It's kind of code redundancy to use

> pm_runtime_get_xx.

> No, it won't. This function checks whether the device is in state

> "active" and in this case it doesn't call the resume callback and returns

> 1. See also the source code of runtime PM in drivers/base/power/runtime.c


Thank you, will change it in next version.

> Rgds, Heiner

> >

> >> Regards, Heiner

> >>>

> >>>  	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);

> >>>  	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res); @@

> >>> -1079,6 +1096,11 @@ clk_disable:

> >>>  static int i2c_imx_remove(struct platform_device *pdev)  {

> >>>  	struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);

> >>> +	int ret;

> >>> +

> >>> +	ret = pm_runtime_get_sync(&pdev->dev);

> >>> +	if (ret < 0)

> >>> +		return ret;

> >>>

> >>>  	/* remove adapter */

> >>>  	dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n"); @@ -1093,17

> >>> +1115,52 @@ static int i2c_imx_remove(struct platform_device *pdev)

> >>>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);

> >>>  	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);

> >>>

> >>> +	pm_runtime_put_sync(&pdev->dev);

> >>> +	pm_runtime_disable(&pdev->dev);

> >>> +

> >>>  	return 0;

> >>>  }

> >>>

> >>> +#ifdef CONFIG_PM

> >>> +static int i2c_imx_runtime_suspend(struct device *dev) {

> >>> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);

> >>> +

> >>> +	clk_disable_unprepare(i2c_imx->clk);

> >>> +

> >>> +	return 0;

> >>> +}

> >>> +

> >>> +static int i2c_imx_runtime_resume(struct device *dev) {

> >>> +	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);

> >>> +	int ret;

> >>> +

> >>> +	ret = clk_prepare_enable(i2c_imx->clk);

> >>> +	if (ret)

> >>> +		dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);

> >>> +

> >>> +	return ret;

> >>> +}

> >>> +

> >>> +static const struct dev_pm_ops i2c_imx_pm_ops = {

> >>> +	SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,

> >>> +			   i2c_imx_runtime_resume, NULL) }; #define

> I2C_IMX_PM_OPS

> >>> +(&i2c_imx_pm_ops) #else #define I2C_IMX_PM_OPS NULL #endif /*

> >>> +CONFIG_PM */

> >>> +

> >>>  static struct platform_driver i2c_imx_driver = {

> >>>  	.probe = i2c_imx_probe,

> >>>  	.remove = i2c_imx_remove,

> >>> -	.driver	= {

> >>> -		.name	= DRIVER_NAME,

> >>> +	.driver = {

> >>> +		.name = DRIVER_NAME,

> >>> +		.pm = I2C_IMX_PM_OPS,

> >>>  		.of_match_table = i2c_imx_dt_ids,

> >>>  	},

> >>> -	.id_table	= imx_i2c_devtype,

> >>> +	.id_table = imx_i2c_devtype,

> >>>  };

> >>>

> >>>  static int __init i2c_adap_imx_init(void)
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 785aa67..7c63047 100644
--- a/drivers/i2c/busses/i2c-imx.c
+++ b/drivers/i2c/busses/i2c-imx.c
@@ -53,6 +53,7 @@ 
 #include <linux/platform_device.h>
 #include <linux/sched.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
 
 /** Defines ********************************************************************
 *******************************************************************************/
@@ -118,6 +119,8 @@ 
 #define I2CR_IEN_OPCODE_0	0x0
 #define I2CR_IEN_OPCODE_1	I2CR_IEN
 
+#define I2C_PM_TIMEOUT		10 /* ms */
+
 /** Variables ******************************************************************
 *******************************************************************************/
 
@@ -520,9 +523,6 @@  static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
 
 	i2c_imx_set_clk(i2c_imx);
 
-	result = clk_prepare_enable(i2c_imx->clk);
-	if (result)
-		return result;
 	imx_i2c_write_reg(i2c_imx->ifdr, i2c_imx, IMX_I2C_IFDR);
 	/* Enable I2C controller */
 	imx_i2c_write_reg(i2c_imx->hwdata->i2sr_clr_opcode, i2c_imx, IMX_I2C_I2SR);
@@ -575,7 +575,6 @@  static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx)
 	/* Disable I2C controller */
 	temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN,
 	imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
-	clk_disable_unprepare(i2c_imx->clk);
 }
 
 static irqreturn_t i2c_imx_isr(int irq, void *dev_id)
@@ -894,6 +893,10 @@  static int i2c_imx_xfer(struct i2c_adapter *adapter,
 
 	dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
 
+	result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent);
+	if (result < 0)
+		goto out;
+
 	/* Start I2C transfer */
 	result = i2c_imx_start(i2c_imx);
 	if (result)
@@ -950,6 +953,10 @@  fail0:
 	/* Stop I2C transfer */
 	i2c_imx_stop(i2c_imx);
 
+out:
+	pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent);
+	pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent);
+
 	dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
 		(result < 0) ? "error" : "success msg",
 			(result < 0) ? result : num);
@@ -1020,9 +1027,10 @@  static int i2c_imx_probe(struct platform_device *pdev)
 
 	ret = clk_prepare_enable(i2c_imx->clk);
 	if (ret) {
-		dev_err(&pdev->dev, "can't enable I2C clock\n");
+		dev_err(&pdev->dev, "can't enable I2C clock, ret=%d\n", ret);
 		return ret;
 	}
+
 	/* Request IRQ */
 	ret = devm_request_irq(&pdev->dev, irq, i2c_imx_isr, 0,
 				pdev->name, i2c_imx);
@@ -1037,6 +1045,14 @@  static int i2c_imx_probe(struct platform_device *pdev)
 	/* Set up adapter data */
 	i2c_set_adapdata(&i2c_imx->adapter, i2c_imx);
 
+	/* Set up platform driver data */
+	platform_set_drvdata(pdev, i2c_imx);
+
+	pm_runtime_set_autosuspend_delay(&pdev->dev, I2C_PM_TIMEOUT);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
 	/* Set up clock divider */
 	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
 	ret = of_property_read_u32(pdev->dev.of_node,
@@ -1053,12 +1069,13 @@  static int i2c_imx_probe(struct platform_device *pdev)
 	ret = i2c_add_numbered_adapter(&i2c_imx->adapter);
 	if (ret < 0) {
 		dev_err(&pdev->dev, "registration failed\n");
+		pm_runtime_mark_last_busy(&pdev->dev);
+		pm_runtime_autosuspend(&pdev->dev);
 		goto clk_disable;
 	}
 
-	/* Set up platform driver data */
-	platform_set_drvdata(pdev, i2c_imx);
-	clk_disable_unprepare(i2c_imx->clk);
+	pm_runtime_mark_last_busy(&pdev->dev);
+	pm_runtime_autosuspend(&pdev->dev);
 
 	dev_dbg(&i2c_imx->adapter.dev, "claimed irq %d\n", irq);
 	dev_dbg(&i2c_imx->adapter.dev, "device resources: %pR\n", res);
@@ -1079,6 +1096,11 @@  clk_disable:
 static int i2c_imx_remove(struct platform_device *pdev)
 {
 	struct imx_i2c_struct *i2c_imx = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0)
+		return ret;
 
 	/* remove adapter */
 	dev_dbg(&i2c_imx->adapter.dev, "adapter removed\n");
@@ -1093,17 +1115,52 @@  static int i2c_imx_remove(struct platform_device *pdev)
 	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2CR);
 	imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
 
+	pm_runtime_put_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+
 	return 0;
 }
 
+#ifdef CONFIG_PM
+static int i2c_imx_runtime_suspend(struct device *dev)
+{
+	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(i2c_imx->clk);
+
+	return 0;
+}
+
+static int i2c_imx_runtime_resume(struct device *dev)
+{
+	struct imx_i2c_struct *i2c_imx  = dev_get_drvdata(dev);
+	int ret;
+
+	ret = clk_prepare_enable(i2c_imx->clk);
+	if (ret)
+		dev_err(dev, "can't enable I2C clock, ret=%d\n", ret);
+
+	return ret;
+}
+
+static const struct dev_pm_ops i2c_imx_pm_ops = {
+	SET_RUNTIME_PM_OPS(i2c_imx_runtime_suspend,
+			   i2c_imx_runtime_resume, NULL)
+};
+#define I2C_IMX_PM_OPS (&i2c_imx_pm_ops)
+#else
+#define I2C_IMX_PM_OPS NULL
+#endif /* CONFIG_PM */
+
 static struct platform_driver i2c_imx_driver = {
 	.probe = i2c_imx_probe,
 	.remove = i2c_imx_remove,
-	.driver	= {
-		.name	= DRIVER_NAME,
+	.driver = {
+		.name = DRIVER_NAME,
+		.pm = I2C_IMX_PM_OPS,
 		.of_match_table = i2c_imx_dt_ids,
 	},
-	.id_table	= imx_i2c_devtype,
+	.id_table = imx_i2c_devtype,
 };
 
 static int __init i2c_adap_imx_init(void)