[PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage

Mukesh Kumar Savaliya posted 1 patch 1 week ago
drivers/i2c/busses/i2c-qcom-geni.c | 43 +++++++++++++++++++++---------
1 file changed, 31 insertions(+), 12 deletions(-)
[PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage
Posted by Mukesh Kumar Savaliya 1 week ago
pm_runtime_get_sync() function fails during PM early resume and returns
-EACCES because runtime PM for the device is disabled at the early stage
causing i2c transfer to fail. Make changes to serve transfer with forced
resume.

Few i2c clients like PCI OR touch may request i2c transfers during early
resume stage. Any i2c client can keep transfer request very early resume
stage like noirq phase of PM. To serve the transfer, register an interrupt
with IRQF_EARLY_RESUME and IRQF_NO_SUSPEND flags to avoid timeout of
transfer when IRQ is not enabled during early stage.

pm_runtime_get_sync() fails when runtime PM is not enabled, then use
pm_runtime_force_resume() and serve the transfer.

Co-developed-by: Viken Dadhaniya <quic_vdadhani@quicinc.com>
Signed-off-by: Viken Dadhaniya <quic_vdadhani@quicinc.com>
Signed-off-by: Mukesh Kumar Savaliya <quic_msavaliy@quicinc.com>
---

Link to V2: https://lore.kernel.org/lkml/202410132233.P25W2vKq-lkp@intel.com/T/

 v2 -> v3:
 - Updated exact usecase and scenario in the commit log description.
 - Removed bulleted points from technical description, added details in free flow.
 - Used pm_runtime_force_resume/suspend() instead customized local implementation.
 - Added debug log after pm_runtime_force_suspend().

---
Link to V1: https://patches.linaro.org/project/linux-i2c/patch/20240328123743.1713696-1-quic_msavaliy@quicinc.com/

 v1 -> v2:
 - Changed gi2c->se.dev to dev during dev_dbg() calls.
 - Addressed review comments from Andi and Bjorn.
 - Returned 0 instead garbage inside geni_i2c_force_resume().
 - Added comments explaining forced resume transfer when runtime PM
   remains disabled.
---

 drivers/i2c/busses/i2c-qcom-geni.c | 43 +++++++++++++++++++++---------
 1 file changed, 31 insertions(+), 12 deletions(-)

diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c
index 7a22e1f46e60..4fe5fa4532ca 100644
--- a/drivers/i2c/busses/i2c-qcom-geni.c
+++ b/drivers/i2c/busses/i2c-qcom-geni.c
@@ -695,17 +695,27 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
 			 int num)
 {
 	struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
+	struct device *dev = gi2c->se.dev;
 	int ret;
 
 	gi2c->err = 0;
 	reinit_completion(&gi2c->done);
-	ret = pm_runtime_get_sync(gi2c->se.dev);
-	if (ret < 0) {
-		dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
-		pm_runtime_put_noidle(gi2c->se.dev);
-		/* Set device in suspended since resume failed */
-		pm_runtime_set_suspended(gi2c->se.dev);
-		return ret;
+	/* Serve I2C transfer by forced resume if Runtime PM is enbled or not */
+	if (!pm_runtime_enabled(dev) && gi2c->suspended) {
+		dev_dbg(dev, "Runtime PM is disabled hence force resume, pm_usage_count: %d\n",
+			atomic_read(&dev->power.usage_count));
+		ret = pm_runtime_force_resume(dev);
+		if (ret)
+			return ret;
+	} else {
+		ret = pm_runtime_get_sync(gi2c->se.dev);
+		if (ret < 0) {
+			dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
+			pm_runtime_put_noidle(gi2c->se.dev);
+			/* Set device in suspended since resume failed */
+			pm_runtime_set_suspended(gi2c->se.dev);
+			return ret;
+		}
 	}
 
 	qcom_geni_i2c_conf(gi2c);
@@ -715,8 +725,18 @@ static int geni_i2c_xfer(struct i2c_adapter *adap,
 	else
 		ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
 
-	pm_runtime_mark_last_busy(gi2c->se.dev);
-	pm_runtime_put_autosuspend(gi2c->se.dev);
+	/* if Runtime PM is disabled, do force_suspend() else autosuspend the driver */
+	if (!pm_runtime_enabled(dev) && !gi2c->suspended) {
+		ret = pm_runtime_force_suspend(dev);
+		dev_dbg(dev, "Runtime PM is disabled hence force suspend, pm_usage_count: %d\n",
+			atomic_read(&dev->power.usage_count));
+		if (ret)
+			return ret;
+	} else {
+		pm_runtime_mark_last_busy(gi2c->se.dev);
+		pm_runtime_put_autosuspend(gi2c->se.dev);
+	}
+
 	gi2c->cur = NULL;
 	gi2c->err = 0;
 	return ret;
@@ -833,9 +853,8 @@ static int geni_i2c_probe(struct platform_device *pdev)
 	init_completion(&gi2c->done);
 	spin_lock_init(&gi2c->lock);
 	platform_set_drvdata(pdev, gi2c);
-
-	/* Keep interrupts disabled initially to allow for low-power modes */
-	ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq, IRQF_NO_AUTOEN,
+	ret = devm_request_irq(dev, gi2c->irq, geni_i2c_irq,
+			       IRQF_NO_AUTOEN | IRQF_EARLY_RESUME | IRQF_NO_SUSPEND,
 			       dev_name(dev), gi2c);
 	if (ret) {
 		dev_err(dev, "Request_irq failed:%d: err:%d\n",
-- 
2.25.1
Re: [PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage
Posted by kernel test robot 4 days, 16 hours ago
Hi Mukesh,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20241120]
[also build test ERROR on linus/master]
[cannot apply to v6.12 v6.12-rc7 v6.12-rc6 v6.12]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mukesh-Kumar-Savaliya/i2c-i2c-qcom-geni-Serve-transfer-during-early-resume-stage/20241121-124240
base:   next-20241120
patch link:    https://lore.kernel.org/r/20241119143031.3331753-1-quic_msavaliy%40quicinc.com
patch subject: [PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage
config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20241122/202411222036.5u0o0jkd-lkp@intel.com/config)
compiler: m68k-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241122/202411222036.5u0o0jkd-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411222036.5u0o0jkd-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from include/linux/printk.h:610,
                    from include/asm-generic/bug.h:22,
                    from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:13,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/m68k/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:79,
                    from include/linux/spinlock.h:56,
                    from include/linux/mmzone.h:8,
                    from include/linux/gfp.h:7,
                    from include/linux/slab.h:16,
                    from include/linux/resource_ext.h:11,
                    from include/linux/acpi.h:13,
                    from drivers/i2c/busses/i2c-qcom-geni.c:4:
   drivers/i2c/busses/i2c-qcom-geni.c: In function 'geni_i2c_xfer':
>> drivers/i2c/busses/i2c-qcom-geni.c:706:48: error: 'struct dev_pm_info' has no member named 'usage_count'
     706 |                         atomic_read(&dev->power.usage_count));
         |                                                ^
   include/linux/dynamic_debug.h:224:29: note: in definition of macro '__dynamic_func_call_cls'
     224 |                 func(&id, ##__VA_ARGS__);                       \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:250:9: note: in expansion of macro '_dynamic_func_call_cls'
     250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:273:9: note: in expansion of macro '_dynamic_func_call'
     273 |         _dynamic_func_call(fmt, __dynamic_dev_dbg,              \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
     165 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   drivers/i2c/busses/i2c-qcom-geni.c:705:17: note: in expansion of macro 'dev_dbg'
     705 |                 dev_dbg(dev, "Runtime PM is disabled hence force resume, pm_usage_count: %d\n",
         |                 ^~~~~~~
   drivers/i2c/busses/i2c-qcom-geni.c:732:48: error: 'struct dev_pm_info' has no member named 'usage_count'
     732 |                         atomic_read(&dev->power.usage_count));
         |                                                ^
   include/linux/dynamic_debug.h:224:29: note: in definition of macro '__dynamic_func_call_cls'
     224 |                 func(&id, ##__VA_ARGS__);                       \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:250:9: note: in expansion of macro '_dynamic_func_call_cls'
     250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:273:9: note: in expansion of macro '_dynamic_func_call'
     273 |         _dynamic_func_call(fmt, __dynamic_dev_dbg,              \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:165:9: note: in expansion of macro 'dynamic_dev_dbg'
     165 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   drivers/i2c/busses/i2c-qcom-geni.c:731:17: note: in expansion of macro 'dev_dbg'
     731 |                 dev_dbg(dev, "Runtime PM is disabled hence force suspend, pm_usage_count: %d\n",
         |                 ^~~~~~~


vim +706 drivers/i2c/busses/i2c-qcom-geni.c

   692	
   693	static int geni_i2c_xfer(struct i2c_adapter *adap,
   694				 struct i2c_msg msgs[],
   695				 int num)
   696	{
   697		struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
   698		struct device *dev = gi2c->se.dev;
   699		int ret;
   700	
   701		gi2c->err = 0;
   702		reinit_completion(&gi2c->done);
   703		/* Serve I2C transfer by forced resume if Runtime PM is enbled or not */
   704		if (!pm_runtime_enabled(dev) && gi2c->suspended) {
   705			dev_dbg(dev, "Runtime PM is disabled hence force resume, pm_usage_count: %d\n",
 > 706				atomic_read(&dev->power.usage_count));
   707			ret = pm_runtime_force_resume(dev);
   708			if (ret)
   709				return ret;
   710		} else {
   711			ret = pm_runtime_get_sync(gi2c->se.dev);
   712			if (ret < 0) {
   713				dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
   714				pm_runtime_put_noidle(gi2c->se.dev);
   715				/* Set device in suspended since resume failed */
   716				pm_runtime_set_suspended(gi2c->se.dev);
   717				return ret;
   718			}
   719		}
   720	
   721		qcom_geni_i2c_conf(gi2c);
   722	
   723		if (gi2c->gpi_mode)
   724			ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
   725		else
   726			ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
   727	
   728		/* if Runtime PM is disabled, do force_suspend() else autosuspend the driver */
   729		if (!pm_runtime_enabled(dev) && !gi2c->suspended) {
   730			ret = pm_runtime_force_suspend(dev);
   731			dev_dbg(dev, "Runtime PM is disabled hence force suspend, pm_usage_count: %d\n",
   732				atomic_read(&dev->power.usage_count));
   733			if (ret)
   734				return ret;
   735		} else {
   736			pm_runtime_mark_last_busy(gi2c->se.dev);
   737			pm_runtime_put_autosuspend(gi2c->se.dev);
   738		}
   739	
   740		gi2c->cur = NULL;
   741		gi2c->err = 0;
   742		return ret;
   743	}
   744	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Re: [PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage
Posted by kernel test robot 4 days, 20 hours ago
Hi Mukesh,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20241120]
[also build test ERROR on linus/master]
[cannot apply to v6.12 v6.12-rc7 v6.12-rc6 v6.12]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Mukesh-Kumar-Savaliya/i2c-i2c-qcom-geni-Serve-transfer-during-early-resume-stage/20241121-124240
base:   next-20241120
patch link:    https://lore.kernel.org/r/20241119143031.3331753-1-quic_msavaliy%40quicinc.com
patch subject: [PATCH v3] i2c: i2c-qcom-geni: Serve transfer during early resume stage
config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20241122/202411221541.mTqJTK6J-lkp@intel.com/config)
compiler: clang version 20.0.0git (https://github.com/llvm/llvm-project 592c0fe55f6d9a811028b5f3507be91458ab2713)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241122/202411221541.mTqJTK6J-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411221541.mTqJTK6J-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/i2c/busses/i2c-qcom-geni.c:6:
   In file included from include/linux/dmaengine.h:12:
   In file included from include/linux/scatterlist.h:8:
   In file included from include/linux/mm.h:2223:
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
>> drivers/i2c/busses/i2c-qcom-geni.c:706:28: error: no member named 'usage_count' in 'struct dev_pm_info'
     706 |                         atomic_read(&dev->power.usage_count));
         |                                      ~~~~~~~~~~ ^
   include/linux/dev_printk.h:165:39: note: expanded from macro 'dev_dbg'
     165 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                                              ^~~~~~~~~~~
   include/linux/dynamic_debug.h:274:19: note: expanded from macro 'dynamic_dev_dbg'
     274 |                            dev, fmt, ##__VA_ARGS__)
         |                                        ^~~~~~~~~~~
   include/linux/dynamic_debug.h:250:59: note: expanded from macro '_dynamic_func_call'
     250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
         |                                                                  ^~~~~~~~~~~
   include/linux/dynamic_debug.h:248:65: note: expanded from macro '_dynamic_func_call_cls'
     248 |         __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
         |                                                                        ^~~~~~~~~~~
   include/linux/dynamic_debug.h:224:15: note: expanded from macro '__dynamic_func_call_cls'
     224 |                 func(&id, ##__VA_ARGS__);                       \
         |                             ^~~~~~~~~~~
   drivers/i2c/busses/i2c-qcom-geni.c:732:28: error: no member named 'usage_count' in 'struct dev_pm_info'
     732 |                         atomic_read(&dev->power.usage_count));
         |                                      ~~~~~~~~~~ ^
   include/linux/dev_printk.h:165:39: note: expanded from macro 'dev_dbg'
     165 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                                              ^~~~~~~~~~~
   include/linux/dynamic_debug.h:274:19: note: expanded from macro 'dynamic_dev_dbg'
     274 |                            dev, fmt, ##__VA_ARGS__)
         |                                        ^~~~~~~~~~~
   include/linux/dynamic_debug.h:250:59: note: expanded from macro '_dynamic_func_call'
     250 |         _dynamic_func_call_cls(_DPRINTK_CLASS_DFLT, fmt, func, ##__VA_ARGS__)
         |                                                                  ^~~~~~~~~~~
   include/linux/dynamic_debug.h:248:65: note: expanded from macro '_dynamic_func_call_cls'
     248 |         __dynamic_func_call_cls(__UNIQUE_ID(ddebug), cls, fmt, func, ##__VA_ARGS__)
         |                                                                        ^~~~~~~~~~~
   include/linux/dynamic_debug.h:224:15: note: expanded from macro '__dynamic_func_call_cls'
     224 |                 func(&id, ##__VA_ARGS__);                       \
         |                             ^~~~~~~~~~~
   1 warning and 2 errors generated.


vim +706 drivers/i2c/busses/i2c-qcom-geni.c

   692	
   693	static int geni_i2c_xfer(struct i2c_adapter *adap,
   694				 struct i2c_msg msgs[],
   695				 int num)
   696	{
   697		struct geni_i2c_dev *gi2c = i2c_get_adapdata(adap);
   698		struct device *dev = gi2c->se.dev;
   699		int ret;
   700	
   701		gi2c->err = 0;
   702		reinit_completion(&gi2c->done);
   703		/* Serve I2C transfer by forced resume if Runtime PM is enbled or not */
   704		if (!pm_runtime_enabled(dev) && gi2c->suspended) {
   705			dev_dbg(dev, "Runtime PM is disabled hence force resume, pm_usage_count: %d\n",
 > 706				atomic_read(&dev->power.usage_count));
   707			ret = pm_runtime_force_resume(dev);
   708			if (ret)
   709				return ret;
   710		} else {
   711			ret = pm_runtime_get_sync(gi2c->se.dev);
   712			if (ret < 0) {
   713				dev_err(gi2c->se.dev, "error turning SE resources:%d\n", ret);
   714				pm_runtime_put_noidle(gi2c->se.dev);
   715				/* Set device in suspended since resume failed */
   716				pm_runtime_set_suspended(gi2c->se.dev);
   717				return ret;
   718			}
   719		}
   720	
   721		qcom_geni_i2c_conf(gi2c);
   722	
   723		if (gi2c->gpi_mode)
   724			ret = geni_i2c_gpi_xfer(gi2c, msgs, num);
   725		else
   726			ret = geni_i2c_fifo_xfer(gi2c, msgs, num);
   727	
   728		/* if Runtime PM is disabled, do force_suspend() else autosuspend the driver */
   729		if (!pm_runtime_enabled(dev) && !gi2c->suspended) {
   730			ret = pm_runtime_force_suspend(dev);
   731			dev_dbg(dev, "Runtime PM is disabled hence force suspend, pm_usage_count: %d\n",
   732				atomic_read(&dev->power.usage_count));
   733			if (ret)
   734				return ret;
   735		} else {
   736			pm_runtime_mark_last_busy(gi2c->se.dev);
   737			pm_runtime_put_autosuspend(gi2c->se.dev);
   738		}
   739	
   740		gi2c->cur = NULL;
   741		gi2c->err = 0;
   742		return ret;
   743	}
   744	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki