diff mbox

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

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

Commit Message

Gao Pan Sept. 9, 2015, 2:12 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>
---
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

V5:
 -clean up and disable runtime PM when i2c_add_numbered_adapter fails
 -use pm_runtime_get and pm_runtime_put_autosuspend in probe

V6:
 -disable the clock manually and set the state to suspended explicitly with
  pm_runtime_set_suspended

V7:
 -manually disabling the clock and use pm_runtime_put_noidle in the remove callback

 drivers/i2c/busses/i2c-imx.c | 90 ++++++++++++++++++++++++++++++++++++++------
 1 file changed, 78 insertions(+), 12 deletions(-)

Comments

Gao Pandy Sept. 18, 2015, 7:41 a.m. UTC | #1
From: Gao Pan <mailto:b54642@freescale.com> Sent: Wednesday, September 09, 2015 10:12 AM

> To: wsa@the-dreams.de

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

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

> hkallweit1@gmail.com

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

> performance

> 

> 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>

> ---

> 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

> 

> V5:

>  -clean up and disable runtime PM when i2c_add_numbered_adapter fails  -

> use pm_runtime_get and pm_runtime_put_autosuspend in probe

> 

> V6:

>  -disable the clock manually and set the state to suspended explicitly

> with

>   pm_runtime_set_suspended

> 

> V7:

>  -manually disabling the clock and use pm_runtime_put_noidle in the

> remove callback

> 

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

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

>  1 file changed, 78 insertions(+), 12 deletions(-)

> 

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

> index 785aa67..d7c823b 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,18 @@ 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);

> +

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

> +	if (ret < 0)

> +		goto rpm_disable;

> +

>  	/* Set up clock divider */

>  	i2c_imx->bitrate = IMX_I2C_BIT_RATE;

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

> @@ -1053,12 +1073,11 @@ 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");

> -		goto clk_disable;

> +		goto rpm_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_put_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); @@

> -1071,6 +1090,11 @@ static int i2c_imx_probe(struct platform_device *pdev)

> 

>  	return 0;   /* Return OK */

> 

> +rpm_disable:

> +	pm_runtime_put_noidle(&pdev->dev);

> +	pm_runtime_disable(&pdev->dev);

> +	pm_runtime_set_suspended(&pdev->dev);

> +

>  clk_disable:

>  	clk_disable_unprepare(i2c_imx->clk);

>  	return ret;

> @@ -1079,6 +1103,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

> +1122,54 @@ 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);

> 

> +	clk_disable_unprepare(i2c_imx->clk);

> +

> +	pm_runtime_put_noidle(&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)

> --

> 1.9.1
Uwe Kleine-König Sept. 18, 2015, 8:13 a.m. UTC | #2
Hello,

On Wed, Sep 09, 2015 at 10:12:19AM +0800, Gao Pan wrote:
>  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);
> +

I doubt that it's correct to call these two functions if
pm_runtime_get_sync failed. Shouldn't the out label be after the two
calls?

Other than that I assume you tested your patch with and without CONFIG_PM?

Best regards
Uwe
Gao Pandy Sept. 18, 2015, 9:21 a.m. UTC | #3
From: Uwe Kleine-König <mailto:u.kleine-koenig@pengutronix.de> Sent: Friday, September 18, 2015 4:14 PM
> To: Gao Pan-B54642
> Cc: wsa@the-dreams.de; Li Frank-B20596; Duan Fugang-B38611; linux-
> i2c@vger.kernel.org; kernel@pengutronix.de; hkallweit1@gmail.com
> Subject: Re: [Patch V7] i2c: imx: add runtime pm support to improve the
> performance
> 
> Hello,
> 
> On Wed, Sep 09, 2015 at 10:12:19AM +0800, Gao Pan wrote:
> >  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);
> > +
> 
> I doubt that it's correct to call these two functions if
> pm_runtime_get_sync failed. Shouldn't the out label be after the two
> calls?
 
Thanks, will change it in next version.
 
> Other than that I assume you tested your patch with and without CONFIG_PM?

Thanks, I have tested the patch. The function is ok without CONFIG_PM.
The patch uses clk_prepare_enable in the probe. Even if CONFIG_PM is disabled, it still can work.
 
> Best regards
> Uwe
> 
> --
> Pengutronix e.K.                           | Uwe Kleine-König
> |
> Industrial Linux Solutions                 | http://www.pengutronix.de/
--
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
diff mbox

Patch

diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c
index 785aa67..d7c823b 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,18 @@  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);
+
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0)
+		goto rpm_disable;
+
 	/* Set up clock divider */
 	i2c_imx->bitrate = IMX_I2C_BIT_RATE;
 	ret = of_property_read_u32(pdev->dev.of_node,
@@ -1053,12 +1073,11 @@  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");
-		goto clk_disable;
+		goto rpm_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_put_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);
@@ -1071,6 +1090,11 @@  static int i2c_imx_probe(struct platform_device *pdev)
 
 	return 0;   /* Return OK */
 
+rpm_disable:
+	pm_runtime_put_noidle(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+
 clk_disable:
 	clk_disable_unprepare(i2c_imx->clk);
 	return ret;
@@ -1079,6 +1103,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 +1122,54 @@  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);
 
+	clk_disable_unprepare(i2c_imx->clk);
+
+	pm_runtime_put_noidle(&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)