All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
@ 2023-01-18  6:15 Haotien Hsu
  2023-01-19 12:28 ` Greg Kroah-Hartman
  0 siblings, 1 reply; 7+ messages in thread
From: Haotien Hsu @ 2023-01-18  6:15 UTC (permalink / raw)
  To: Heikki Krogerus, Greg Kroah-Hartman
  Cc: Sing-Han Chen, Sanket Goswami, Wayne Chang, Uwe Kleine-König,
	Jonathan Hunter, linux-usb, linux-kernel, Haotien Hsu

From: Sing-Han Chen <singhanc@nvidia.com>

For the CCGx, when the OPM field in the INTR_REG is cleared, then the
CCI data in the PPM is reset.

To align with the CCGx UCSI interface guide, this patch updates the
driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
When a new command is sent, the driver will clear the old CCI and
MESSAGE_IN copy.

Finally, clear UCSI_READ_INT before calling complete() to ensure that
the ucsi_ccg_sync_write() would wait for the interrupt handling to
complete.
It prevents the driver from resetting CCI prematurely.

Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
---
V1->V2
- Fix uninitialized symbol 'cci'
v2->v3
- Remove misusing Reported-by tags
v3->v4
- Add comments for op_lock
---
 drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
 1 file changed, 83 insertions(+), 7 deletions(-)

diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
index eab3012e1b01..532813a32cc1 100644
--- a/drivers/usb/typec/ucsi/ucsi_ccg.c
+++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
@@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
 	bool checked;
 } __packed;
 
+#define CCGX_MESSAGE_IN_MAX 4
+struct op_region {
+	u32 cci;
+	u32 message_in[CCGX_MESSAGE_IN_MAX];
+};
+
 struct ucsi_ccg {
 	struct device *dev;
 	struct ucsi *ucsi;
@@ -222,6 +228,13 @@ struct ucsi_ccg {
 	bool has_multiple_dp;
 	struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
 	struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
+
+	/*
+	 * This spinlock protects op_data which includes CCI and MESSAGE_IN that
+	 * will be updated in ISR
+	 */
+	spinlock_t op_lock;
+	struct op_region op_data;
 };
 
 static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
@@ -305,12 +318,57 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
 	return 0;
 }
 
+static void ccg_op_region_read(struct ucsi_ccg *uc, unsigned int offset,
+		void *val, size_t val_len)
+{
+	struct op_region *data = &uc->op_data;
+
+	spin_lock(&uc->op_lock);
+	if (offset == UCSI_CCI)
+		memcpy(val, &data->cci, val_len);
+	else if (offset == UCSI_MESSAGE_IN)
+		memcpy(val, &data->message_in, val_len);
+	spin_unlock(&uc->op_lock);
+}
+
+static void ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
+{
+	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
+	struct op_region *data = &uc->op_data;
+	u32 message_in[CCGX_MESSAGE_IN_MAX];
+
+	if (UCSI_CCI_LENGTH(cci))
+		if (ccg_read(uc, reg, (void *)&message_in,
+					sizeof(message_in))) {
+			dev_err(uc->dev, "failed to read MESSAGE_IN\n");
+			return;
+		}
+
+	spin_lock(&uc->op_lock);
+	memcpy(&data->cci, &cci, sizeof(cci));
+	if (UCSI_CCI_LENGTH(cci))
+		memcpy(&data->message_in, &message_in, sizeof(message_in));
+	spin_unlock(&uc->op_lock);
+}
+
+static void ccg_op_region_clean(struct ucsi_ccg *uc)
+{
+	struct op_region *data = &uc->op_data;
+
+	spin_lock(&uc->op_lock);
+	memset(&data->cci, 0, sizeof(data->cci));
+	memset(&data->message_in, 0, sizeof(data->message_in));
+	spin_unlock(&uc->op_lock);
+}
+
 static int ucsi_ccg_init(struct ucsi_ccg *uc)
 {
 	unsigned int count = 10;
 	u8 data;
 	int status;
 
+	spin_lock_init(&uc->op_lock);
+
 	data = CCGX_RAB_UCSI_CONTROL_STOP;
 	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
 	if (status < 0)
@@ -520,9 +578,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
 	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
 	struct ucsi_capability *cap;
 	struct ucsi_altmode *alt;
-	int ret;
+	int ret = 0;
+
+	if ((offset == UCSI_CCI) || (offset == UCSI_MESSAGE_IN))
+		ccg_op_region_read(uc, offset, val, val_len);
+	else
+		ret = ccg_read(uc, reg, val, val_len);
 
-	ret = ccg_read(uc, reg, val, val_len);
 	if (ret)
 		return ret;
 
@@ -559,9 +621,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
 static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
 				const void *val, size_t val_len)
 {
+	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
 	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
 
-	return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
+	if (offset == UCSI_CONTROL)
+		ccg_op_region_clean(uc);
+
+	return ccg_write(uc, reg, val, val_len);
 }
 
 static int ucsi_ccg_sync_write(struct ucsi *ucsi, unsigned int offset,
@@ -616,12 +682,17 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
 	struct ucsi_ccg *uc = data;
 	u8 intr_reg;
 	u32 cci;
-	int ret;
+	int ret = 0;
 
 	ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
 	if (ret)
 		return ret;
 
+	if (!intr_reg)
+		return IRQ_HANDLED;
+	else if (!(intr_reg & UCSI_READ_INT))
+		goto err_clear_irq;
+
 	ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
 	if (ret)
 		goto err_clear_irq;
@@ -629,13 +700,18 @@ static irqreturn_t ccg_irq_handler(int irq, void *data)
 	if (UCSI_CCI_CONNECTOR(cci))
 		ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
 
-	if (test_bit(DEV_CMD_PENDING, &uc->flags) &&
-	    cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
-		complete(&uc->complete);
+	/* As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN
+	 * to the OpRegion before clear the UCSI interrupt
+	 */
+	ccg_op_region_update(uc, cci);
 
 err_clear_irq:
 	ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
 
+	if (!ret && test_bit(DEV_CMD_PENDING, &uc->flags) &&
+	    cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
+		complete(&uc->complete);
+
 	return IRQ_HANDLED;
 }
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
  2023-01-18  6:15 [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling Haotien Hsu
@ 2023-01-19 12:28 ` Greg Kroah-Hartman
  2023-01-31  6:29   ` Haotien Hsu
  2023-02-21 16:40   ` Jon Hunter
  0 siblings, 2 replies; 7+ messages in thread
From: Greg Kroah-Hartman @ 2023-01-19 12:28 UTC (permalink / raw)
  To: Haotien Hsu
  Cc: Heikki Krogerus, Sing-Han Chen, Sanket Goswami, Wayne Chang,
	Uwe Kleine-König, Jonathan Hunter, linux-usb, linux-kernel

On Wed, Jan 18, 2023 at 02:15:23PM +0800, Haotien Hsu wrote:
> From: Sing-Han Chen <singhanc@nvidia.com>
> 
> For the CCGx, when the OPM field in the INTR_REG is cleared, then the
> CCI data in the PPM is reset.
> 
> To align with the CCGx UCSI interface guide, this patch updates the
> driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
> When a new command is sent, the driver will clear the old CCI and
> MESSAGE_IN copy.
> 
> Finally, clear UCSI_READ_INT before calling complete() to ensure that
> the ucsi_ccg_sync_write() would wait for the interrupt handling to
> complete.
> It prevents the driver from resetting CCI prematurely.
> 
> Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
> Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
> ---
> V1->V2
> - Fix uninitialized symbol 'cci'
> v2->v3
> - Remove misusing Reported-by tags
> v3->v4
> - Add comments for op_lock
> ---
>  drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
>  1 file changed, 83 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> index eab3012e1b01..532813a32cc1 100644
> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> @@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
>  	bool checked;
>  } __packed;
>  
> +#define CCGX_MESSAGE_IN_MAX 4
> +struct op_region {
> +	u32 cci;

This is coming from hardware so you have to specify the endian-ness of
it, right?

> +	u32 message_in[CCGX_MESSAGE_IN_MAX];

Same here.

> +};
> +
>  struct ucsi_ccg {
>  	struct device *dev;
>  	struct ucsi *ucsi;
> @@ -222,6 +228,13 @@ struct ucsi_ccg {
>  	bool has_multiple_dp;
>  	struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
>  	struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
> +
> +	/*
> +	 * This spinlock protects op_data which includes CCI and MESSAGE_IN that
> +	 * will be updated in ISR
> +	 */
> +	spinlock_t op_lock;
> +	struct op_region op_data;
>  };
>  
>  static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
> @@ -305,12 +318,57 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
>  	return 0;
>  }
>  
> +static void ccg_op_region_read(struct ucsi_ccg *uc, unsigned int offset,
> +		void *val, size_t val_len)
> +{
> +	struct op_region *data = &uc->op_data;
> +
> +	spin_lock(&uc->op_lock);
> +	if (offset == UCSI_CCI)
> +		memcpy(val, &data->cci, val_len);
> +	else if (offset == UCSI_MESSAGE_IN)
> +		memcpy(val, &data->message_in, val_len);

What happens if the offset is neither of these?

You seem to be only calling this if that value is set correctly, but
this seems very fragile.  You are also only calling this in one place,
so why is this a function at all?  Just do the copy under the lock as
needed in the calling location instead.

> +	spin_unlock(&uc->op_lock);
> +}
> +
> +static void ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
> +{
> +	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
> +	struct op_region *data = &uc->op_data;
> +	u32 message_in[CCGX_MESSAGE_IN_MAX];

Are you sure you can put this big of a buffer on the stack?

> +
> +	if (UCSI_CCI_LENGTH(cci))
> +		if (ccg_read(uc, reg, (void *)&message_in,
> +					sizeof(message_in))) {

Are you allowed to copy in into stack memory?  This ends up being an i2c
message, right?  Can that be transferred into non-dma-able memory?

> +			dev_err(uc->dev, "failed to read MESSAGE_IN\n");

Why can you not fail this function?  You are throwing away the error
here, that's not good.

> +			return;
> +		}
> +
> +	spin_lock(&uc->op_lock);
> +	memcpy(&data->cci, &cci, sizeof(cci));

Perhaps just:
	data->cci = cci;
as this is only a 32bit value.

> +	if (UCSI_CCI_LENGTH(cci))
> +		memcpy(&data->message_in, &message_in, sizeof(message_in));
> +	spin_unlock(&uc->op_lock);
> +}
> +
> +static void ccg_op_region_clean(struct ucsi_ccg *uc)
> +{
> +	struct op_region *data = &uc->op_data;
> +
> +	spin_lock(&uc->op_lock);
> +	memset(&data->cci, 0, sizeof(data->cci));

	data->cci = 0;

> +	memset(&data->message_in, 0, sizeof(data->message_in));

Or better yet, do it all at once:
	memset(&data, 0, sizeof(*data));

> +	spin_unlock(&uc->op_lock);

But why do you need to do this at all?  Why "clean" the whole buffer
out, why not just set cci to 0 and be done with it?

Or why even clean this out at all, what happens if you do not?

> +}
> +
>  static int ucsi_ccg_init(struct ucsi_ccg *uc)
>  {
>  	unsigned int count = 10;
>  	u8 data;
>  	int status;
>  
> +	spin_lock_init(&uc->op_lock);
> +
>  	data = CCGX_RAB_UCSI_CONTROL_STOP;
>  	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
>  	if (status < 0)
> @@ -520,9 +578,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>  	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>  	struct ucsi_capability *cap;
>  	struct ucsi_altmode *alt;
> -	int ret;
> +	int ret = 0;
> +
> +	if ((offset == UCSI_CCI) || (offset == UCSI_MESSAGE_IN))
> +		ccg_op_region_read(uc, offset, val, val_len);
> +	else
> +		ret = ccg_read(uc, reg, val, val_len);
>  
> -	ret = ccg_read(uc, reg, val, val_len);
>  	if (ret)
>  		return ret;
>  
> @@ -559,9 +621,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>  static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
>  				const void *val, size_t val_len)
>  {
> +	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
>  	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>  
> -	return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
> +	if (offset == UCSI_CONTROL)
> +		ccg_op_region_clean(uc);

Why is this needed?  You have not documented it the need for this.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
@ 2023-01-20 21:21 kernel test robot
  0 siblings, 0 replies; 7+ messages in thread
From: kernel test robot @ 2023-01-20 21:21 UTC (permalink / raw)
  To: oe-kbuild; +Cc: lkp, Dan Carpenter

BCC: lkp@intel.com
CC: oe-kbuild-all@lists.linux.dev
In-Reply-To: <20230118061523.1537992-1-haotienh@nvidia.com>
References: <20230118061523.1537992-1-haotienh@nvidia.com>
TO: Haotien Hsu <haotienh@nvidia.com>
TO: Heikki Krogerus <heikki.krogerus@linux.intel.com>
TO: "Greg Kroah-Hartman" <gregkh@linuxfoundation.org>
CC: "Sing-Han Chen" <singhanc@nvidia.com>
CC: Sanket Goswami <Sanket.Goswami@amd.com>
CC: Wayne Chang <waynec@nvidia.com>
CC: "Uwe Kleine-König" <u.kleine-koenig@pengutronix.de>
CC: Jonathan Hunter <jonathanh@nvidia.com>
CC: linux-usb@vger.kernel.org
CC: linux-kernel@vger.kernel.org
CC: Haotien Hsu <haotienh@nvidia.com>

Hi Haotien,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on usb/usb-next usb/usb-linus linus/master v6.2-rc4 next-20230120]
[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/Haotien-Hsu/ucsi_ccg-Refine-the-UCSI-Interrupt-handling/20230118-143008
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
patch link:    https://lore.kernel.org/r/20230118061523.1537992-1-haotienh%40nvidia.com
patch subject: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
:::::: branch date: 3 days ago
:::::: commit date: 3 days ago
config: ia64-randconfig-m041-20230119 (https://download.01.org/0day-ci/archive/20230121/202301210542.DHRnbwpr-lkp@intel.com/config)
compiler: ia64-linux-gcc (GCC) 12.1.0

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <error27@gmail.com>

smatch warnings:
drivers/usb/typec/ucsi/ucsi_ccg.c:712 ccg_irq_handler() error: uninitialized symbol 'cci'.

vim +/cci +712 drivers/usb/typec/ucsi/ucsi_ccg.c

247c554a14aa16 Ajay Gupta      2018-10-26  678  
247c554a14aa16 Ajay Gupta      2018-10-26  679  static irqreturn_t ccg_irq_handler(int irq, void *data)
247c554a14aa16 Ajay Gupta      2018-10-26  680  {
e32fd989ac1c45 Heikki Krogerus 2019-11-04  681  	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_CCI);
247c554a14aa16 Ajay Gupta      2018-10-26  682  	struct ucsi_ccg *uc = data;
e32fd989ac1c45 Heikki Krogerus 2019-11-04  683  	u8 intr_reg;
e32fd989ac1c45 Heikki Krogerus 2019-11-04  684  	u32 cci;
d0e0298af0ae5b Sing-Han Chen   2023-01-18  685  	int ret = 0;
e32fd989ac1c45 Heikki Krogerus 2019-11-04  686  
e32fd989ac1c45 Heikki Krogerus 2019-11-04  687  	ret = ccg_read(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
e32fd989ac1c45 Heikki Krogerus 2019-11-04  688  	if (ret)
e32fd989ac1c45 Heikki Krogerus 2019-11-04  689  		return ret;
e32fd989ac1c45 Heikki Krogerus 2019-11-04  690  
d0e0298af0ae5b Sing-Han Chen   2023-01-18  691  	if (!intr_reg)
d0e0298af0ae5b Sing-Han Chen   2023-01-18  692  		return IRQ_HANDLED;
d0e0298af0ae5b Sing-Han Chen   2023-01-18  693  	else if (!(intr_reg & UCSI_READ_INT))
d0e0298af0ae5b Sing-Han Chen   2023-01-18  694  		goto err_clear_irq;
d0e0298af0ae5b Sing-Han Chen   2023-01-18  695  
e32fd989ac1c45 Heikki Krogerus 2019-11-04  696  	ret = ccg_read(uc, reg, (void *)&cci, sizeof(cci));
e32fd989ac1c45 Heikki Krogerus 2019-11-04  697  	if (ret)
e32fd989ac1c45 Heikki Krogerus 2019-11-04  698  		goto err_clear_irq;
e32fd989ac1c45 Heikki Krogerus 2019-11-04  699  
e32fd989ac1c45 Heikki Krogerus 2019-11-04  700  	if (UCSI_CCI_CONNECTOR(cci))
e32fd989ac1c45 Heikki Krogerus 2019-11-04  701  		ucsi_connector_change(uc->ucsi, UCSI_CCI_CONNECTOR(cci));
247c554a14aa16 Ajay Gupta      2018-10-26  702  
d0e0298af0ae5b Sing-Han Chen   2023-01-18  703  	/* As per CCGx UCSI interface guide, copy CCI and MESSAGE_IN
d0e0298af0ae5b Sing-Han Chen   2023-01-18  704  	 * to the OpRegion before clear the UCSI interrupt
d0e0298af0ae5b Sing-Han Chen   2023-01-18  705  	 */
d0e0298af0ae5b Sing-Han Chen   2023-01-18  706  	ccg_op_region_update(uc, cci);
e32fd989ac1c45 Heikki Krogerus 2019-11-04  707  
e32fd989ac1c45 Heikki Krogerus 2019-11-04  708  err_clear_irq:
e32fd989ac1c45 Heikki Krogerus 2019-11-04  709  	ccg_write(uc, CCGX_RAB_INTR_REG, &intr_reg, sizeof(intr_reg));
247c554a14aa16 Ajay Gupta      2018-10-26  710  
d0e0298af0ae5b Sing-Han Chen   2023-01-18  711  	if (!ret && test_bit(DEV_CMD_PENDING, &uc->flags) &&
d0e0298af0ae5b Sing-Han Chen   2023-01-18 @712  	    cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE))
d0e0298af0ae5b Sing-Han Chen   2023-01-18  713  		complete(&uc->complete);
d0e0298af0ae5b Sing-Han Chen   2023-01-18  714  
247c554a14aa16 Ajay Gupta      2018-10-26  715  	return IRQ_HANDLED;
247c554a14aa16 Ajay Gupta      2018-10-26  716  }
247c554a14aa16 Ajay Gupta      2018-10-26  717  

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

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
  2023-01-19 12:28 ` Greg Kroah-Hartman
@ 2023-01-31  6:29   ` Haotien Hsu
  2023-01-31  6:40     ` Greg Kroah-Hartman
  2023-02-21 16:40   ` Jon Hunter
  1 sibling, 1 reply; 7+ messages in thread
From: Haotien Hsu @ 2023-01-31  6:29 UTC (permalink / raw)
  To: Greg Kroah-Hartman
  Cc: Heikki Krogerus, Sing-Han Chen, Sanket Goswami, Wayne Chang,
	Uwe Kleine-König, Jonathan Hunter, linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org

On 1/19/23 20:28, Greg Kroah-Hartman wrote:
> External email: Use caution opening links or attachments
> 
> 
> On Wed, Jan 18, 2023 at 02:15:23PM +0800, Haotien Hsu wrote:
>> From: Sing-Han Chen <singhanc@nvidia.com>
>>
>> For the CCGx, when the OPM field in the INTR_REG is cleared, then the
>> CCI data in the PPM is reset.
>>
>> To align with the CCGx UCSI interface guide, this patch updates the
>> driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
>> When a new command is sent, the driver will clear the old CCI and
>> MESSAGE_IN copy.
>>
>> Finally, clear UCSI_READ_INT before calling complete() to ensure that
>> the ucsi_ccg_sync_write() would wait for the interrupt handling to
>> complete.
>> It prevents the driver from resetting CCI prematurely.
>>
>> Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
>> Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
>> ---
>> V1->V2
>> - Fix uninitialized symbol 'cci'
>> v2->v3
>> - Remove misusing Reported-by tags
>> v3->v4
>> - Add comments for op_lock
>> ---
>>   drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
>>   1 file changed, 83 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
>> index eab3012e1b01..532813a32cc1 100644
>> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
>> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
>> @@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
>>        bool checked;
>>   } __packed;
>>
>> +#define CCGX_MESSAGE_IN_MAX 4
>> +struct op_region {
>> +     u32 cci;
> 
> This is coming from hardware so you have to specify the endian-ness of
> it, right?


Yes.
According to CCGX's guide, CCI and MESSAGE_IN are accessed as registers.

> 
>> +     u32 message_in[CCGX_MESSAGE_IN_MAX];
> 
> Same here.
> 
>> +};
>> +
>>   struct ucsi_ccg {
>>        struct device *dev;
>>        struct ucsi *ucsi;
>> @@ -222,6 +228,13 @@ struct ucsi_ccg {
>>        bool has_multiple_dp;
>>        struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
>>        struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
>> +
>> +     /*
>> +      * This spinlock protects op_data which includes CCI and MESSAGE_IN that
>> +      * will be updated in ISR
>> +      */
>> +     spinlock_t op_lock;
>> +     struct op_region op_data;
>>   };
>>
>>   static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
>> @@ -305,12 +318,57 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
>>        return 0;
>>   }
>>
>> +static void ccg_op_region_read(struct ucsi_ccg *uc, unsigned int offset,
>> +             void *val, size_t val_len)
>> +{
>> +     struct op_region *data = &uc->op_data;
>> +
>> +     spin_lock(&uc->op_lock);
>> +     if (offset == UCSI_CCI)
>> +             memcpy(val, &data->cci, val_len);
>> +     else if (offset == UCSI_MESSAGE_IN)
>> +             memcpy(val, &data->message_in, val_len);
> 
> What happens if the offset is neither of these?
> 
> You seem to be only calling this if that value is set correctly, but
> this seems very fragile.  You are also only calling this in one place,
> so why is this a function at all?  Just do the copy under the lock as
> needed in the calling location instead.
> 


I will move these codes inline.

>> +     spin_unlock(&uc->op_lock);
>> +}
>> +
>> +static void ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
>> +{
>> +     u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
>> +     struct op_region *data = &uc->op_data;
>> +     u32 message_in[CCGX_MESSAGE_IN_MAX];
> 
> Are you sure you can put this big of a buffer on the stack?
> 


I assume 16 bytes are okay to put on the stack.
Please let me know if you think this size is not practical to put on the 
stack.

>> +
>> +     if (UCSI_CCI_LENGTH(cci))
>> +             if (ccg_read(uc, reg, (void *)&message_in,
>> +                                     sizeof(message_in))) {
> 
> Are you allowed to copy in into stack memory?  This ends up being an i2c
> message, right?  Can that be transferred into non-dma-able memory?
> 


Yes, it works.

>> +                     dev_err(uc->dev, "failed to read MESSAGE_IN\n");
> 
> Why can you not fail this function?  You are throwing away the error
> here, that's not good.
>


I will update it to return errors.

>> +                     return;
>> +             }
>> +
>> +     spin_lock(&uc->op_lock);
>> +     memcpy(&data->cci, &cci, sizeof(cci));
> 
> Perhaps just:
>          data->cci = cci;
> as this is only a 32bit value.
>


True.
>> +     if (UCSI_CCI_LENGTH(cci))
>> +             memcpy(&data->message_in, &message_in, sizeof(message_in));
>> +     spin_unlock(&uc->op_lock);
>> +}
>> +
>> +static void ccg_op_region_clean(struct ucsi_ccg *uc)
>> +{
>> +     struct op_region *data = &uc->op_data;
>> +
>> +     spin_lock(&uc->op_lock);
>> +     memset(&data->cci, 0, sizeof(data->cci));
> 
>          data->cci = 0;
> 
>> +     memset(&data->message_in, 0, sizeof(data->message_in));
> 
> Or better yet, do it all at once:
>          memset(&data, 0, sizeof(*data));


That looks better, thanks.

> 
>> +     spin_unlock(&uc->op_lock);
> 
> But why do you need to do this at all?  Why "clean" the whole buffer
> out, why not just set cci to 0 and be done with it?
> 
> Or why even clean this out at all, what happens if you do not?
> 


It only be called in ucsi_ccg_async_write(), and I will move it there as 
inline.
The reason to clean the whole op_data is that UCSI may read MESSAGE_IN 
after writing UCSI_CONTROL, so clear it to avoid callers getting wrong data.

>> +}
>> +
>>   static int ucsi_ccg_init(struct ucsi_ccg *uc)
>>   {
>>        unsigned int count = 10;
>>        u8 data;
>>        int status;
>>
>> +     spin_lock_init(&uc->op_lock);
>> +
>>        data = CCGX_RAB_UCSI_CONTROL_STOP;
>>        status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
>>        if (status < 0)
>> @@ -520,9 +578,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>>        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>>        struct ucsi_capability *cap;
>>        struct ucsi_altmode *alt;
>> -     int ret;
>> +     int ret = 0;
>> +
>> +     if ((offset == UCSI_CCI) || (offset == UCSI_MESSAGE_IN))
>> +             ccg_op_region_read(uc, offset, val, val_len);
>> +     else
>> +             ret = ccg_read(uc, reg, val, val_len);
>>
>> -     ret = ccg_read(uc, reg, val, val_len);
>>        if (ret)
>>                return ret;
>>
>> @@ -559,9 +621,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>>   static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
>>                                const void *val, size_t val_len)
>>   {
>> +     struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
>>        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>>
>> -     return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
>> +     if (offset == UCSI_CONTROL)
>> +             ccg_op_region_clean(uc);
> 
> Why is this needed?  You have not documented it the need for this.
> 


The reason is described as above and I will add comments for it.

> thanks,
> 
> greg k-h
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
  2023-01-31  6:29   ` Haotien Hsu
@ 2023-01-31  6:40     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 7+ messages in thread
From: Greg Kroah-Hartman @ 2023-01-31  6:40 UTC (permalink / raw)
  To: Haotien Hsu
  Cc: Heikki Krogerus, Sing-Han Chen, Sanket Goswami, Wayne Chang,
	Uwe Kleine-König, Jonathan Hunter, linux-usb@vger.kernel.org,
	linux-kernel@vger.kernel.org

On Tue, Jan 31, 2023 at 06:29:59AM +0000, Haotien Hsu wrote:
> On 1/19/23 20:28, Greg Kroah-Hartman wrote:
> > External email: Use caution opening links or attachments
> > 
> > 
> > On Wed, Jan 18, 2023 at 02:15:23PM +0800, Haotien Hsu wrote:
> >> From: Sing-Han Chen <singhanc@nvidia.com>
> >>
> >> For the CCGx, when the OPM field in the INTR_REG is cleared, then the
> >> CCI data in the PPM is reset.
> >>
> >> To align with the CCGx UCSI interface guide, this patch updates the
> >> driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
> >> When a new command is sent, the driver will clear the old CCI and
> >> MESSAGE_IN copy.
> >>
> >> Finally, clear UCSI_READ_INT before calling complete() to ensure that
> >> the ucsi_ccg_sync_write() would wait for the interrupt handling to
> >> complete.
> >> It prevents the driver from resetting CCI prematurely.
> >>
> >> Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
> >> Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
> >> ---
> >> V1->V2
> >> - Fix uninitialized symbol 'cci'
> >> v2->v3
> >> - Remove misusing Reported-by tags
> >> v3->v4
> >> - Add comments for op_lock
> >> ---
> >>   drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
> >>   1 file changed, 83 insertions(+), 7 deletions(-)
> >>
> >> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> >> index eab3012e1b01..532813a32cc1 100644
> >> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> >> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> >> @@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
> >>        bool checked;
> >>   } __packed;
> >>
> >> +#define CCGX_MESSAGE_IN_MAX 4
> >> +struct op_region {
> >> +     u32 cci;
> > 
> > This is coming from hardware so you have to specify the endian-ness of
> > it, right?
> 
> 
> Yes.
> According to CCGX's guide, CCI and MESSAGE_IN are accessed as registers.

So please specify the endianness of the registers.

> >> +static void ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
> >> +{
> >> +     u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
> >> +     struct op_region *data = &uc->op_data;
> >> +     u32 message_in[CCGX_MESSAGE_IN_MAX];
> > 
> > Are you sure you can put this big of a buffer on the stack?
> > 
> 
> 
> I assume 16 bytes are okay to put on the stack.
> Please let me know if you think this size is not practical to put on the 
> stack.

Why do you want it on the stack?  Is it going to be used as DMA memory?
If so, it can NOT be on the stack.

> >> +
> >> +     if (UCSI_CCI_LENGTH(cci))
> >> +             if (ccg_read(uc, reg, (void *)&message_in,
> >> +                                     sizeof(message_in))) {
> > 
> > Are you allowed to copy in into stack memory?  This ends up being an i2c
> > message, right?  Can that be transferred into non-dma-able memory?
> > 
> 
> 
> Yes, it works.

How was this tested?  On a system that requires i2c messages to be in
DMA?

> >> +                     return;
> >> +             }
> >> +
> >> +     spin_lock(&uc->op_lock);
> >> +     memcpy(&data->cci, &cci, sizeof(cci));
> > 
> > Perhaps just:
> >          data->cci = cci;
> > as this is only a 32bit value.
> >
> 
> 
> True.
> >> +     if (UCSI_CCI_LENGTH(cci))
> >> +             memcpy(&data->message_in, &message_in, sizeof(message_in));
> >> +     spin_unlock(&uc->op_lock);
> >> +}
> >> +
> >> +static void ccg_op_region_clean(struct ucsi_ccg *uc)
> >> +{
> >> +     struct op_region *data = &uc->op_data;
> >> +
> >> +     spin_lock(&uc->op_lock);
> >> +     memset(&data->cci, 0, sizeof(data->cci));
> > 
> >          data->cci = 0;
> > 
> >> +     memset(&data->message_in, 0, sizeof(data->message_in));
> > 
> > Or better yet, do it all at once:
> >          memset(&data, 0, sizeof(*data));
> 
> 
> That looks better, thanks.
> 
> > 
> >> +     spin_unlock(&uc->op_lock);
> > 
> > But why do you need to do this at all?  Why "clean" the whole buffer
> > out, why not just set cci to 0 and be done with it?
> > 
> > Or why even clean this out at all, what happens if you do not?
> > 
> 
> 
> It only be called in ucsi_ccg_async_write(), and I will move it there as 
> inline.
> The reason to clean the whole op_data is that UCSI may read MESSAGE_IN 
> after writing UCSI_CONTROL, so clear it to avoid callers getting wrong data.

How could a caller get the wrong data?  It's what they asked for.  I'm
confused.

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
  2023-01-19 12:28 ` Greg Kroah-Hartman
  2023-01-31  6:29   ` Haotien Hsu
@ 2023-02-21 16:40   ` Jon Hunter
  2023-02-21 16:59     ` Greg Kroah-Hartman
  1 sibling, 1 reply; 7+ messages in thread
From: Jon Hunter @ 2023-02-21 16:40 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Haotien Hsu
  Cc: Heikki Krogerus, Sing-Han Chen, Sanket Goswami, Wayne Chang,
	Uwe Kleine-König, linux-usb, linux-kernel

Hi Greg,

On 19/01/2023 12:28, Greg Kroah-Hartman wrote:
> On Wed, Jan 18, 2023 at 02:15:23PM +0800, Haotien Hsu wrote:
>> From: Sing-Han Chen <singhanc@nvidia.com>
>>
>> For the CCGx, when the OPM field in the INTR_REG is cleared, then the
>> CCI data in the PPM is reset.
>>
>> To align with the CCGx UCSI interface guide, this patch updates the
>> driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
>> When a new command is sent, the driver will clear the old CCI and
>> MESSAGE_IN copy.
>>
>> Finally, clear UCSI_READ_INT before calling complete() to ensure that
>> the ucsi_ccg_sync_write() would wait for the interrupt handling to
>> complete.
>> It prevents the driver from resetting CCI prematurely.
>>
>> Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
>> Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
>> ---
>> V1->V2
>> - Fix uninitialized symbol 'cci'
>> v2->v3
>> - Remove misusing Reported-by tags
>> v3->v4
>> - Add comments for op_lock
>> ---
>>   drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
>>   1 file changed, 83 insertions(+), 7 deletions(-)
>>
>> diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
>> index eab3012e1b01..532813a32cc1 100644
>> --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
>> +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
>> @@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
>>   	bool checked;
>>   } __packed;
>>   
>> +#define CCGX_MESSAGE_IN_MAX 4
>> +struct op_region {
>> +	u32 cci;
> 
> This is coming from hardware so you have to specify the endian-ness of
> it, right?

The current driver reads the 'cci' state in the ccg_irq_handler and here 
we just pass a variable of type u32 for storing the state. We are just 
adding variable of the same type to save the state. This value is 
returned to the ucsi layer which does not specify the endian-ness 
either. I guess this driver like many assume little endian. What is the 
guidance here? Should we be adding __le32 here even if the upper layers 
don't?

>> +	u32 message_in[CCGX_MESSAGE_IN_MAX];
> 
> Same here.
> 
>> +};
>> +
>>   struct ucsi_ccg {
>>   	struct device *dev;
>>   	struct ucsi *ucsi;
>> @@ -222,6 +228,13 @@ struct ucsi_ccg {
>>   	bool has_multiple_dp;
>>   	struct ucsi_ccg_altmode orig[UCSI_MAX_ALTMODES];
>>   	struct ucsi_ccg_altmode updated[UCSI_MAX_ALTMODES];
>> +
>> +	/*
>> +	 * This spinlock protects op_data which includes CCI and MESSAGE_IN that
>> +	 * will be updated in ISR
>> +	 */
>> +	spinlock_t op_lock;
>> +	struct op_region op_data;
>>   };
>>   
>>   static int ccg_read(struct ucsi_ccg *uc, u16 rab, u8 *data, u32 len)
>> @@ -305,12 +318,57 @@ static int ccg_write(struct ucsi_ccg *uc, u16 rab, const u8 *data, u32 len)
>>   	return 0;
>>   }
>>   
>> +static void ccg_op_region_read(struct ucsi_ccg *uc, unsigned int offset,
>> +		void *val, size_t val_len)
>> +{
>> +	struct op_region *data = &uc->op_data;
>> +
>> +	spin_lock(&uc->op_lock);
>> +	if (offset == UCSI_CCI)
>> +		memcpy(val, &data->cci, val_len);
>> +	else if (offset == UCSI_MESSAGE_IN)
>> +		memcpy(val, &data->message_in, val_len);
> 
> What happens if the offset is neither of these?

Looking at where this is called, currently only these offsets are passed 
to this function. However, I am wondering if we really need this 
function and if we just don't collapse this into ucsi_ccg_read() so we 
have ...

        if (offset == UCSI_CCI) {
                spin_lock(&uc->op_lock);
                memcpy(val, &uc->op_data.cci, val_len);
                spin_unlock(&uc->op_lock);
        } else if (offset == UCSI_MESSAGE_IN) {
                spin_lock(&uc->op_lock);
                memcpy(val, &uc->op_data.message_in, val_len);
                spin_unlock(&uc->op_lock);
        } else {
                ret = ccg_read(uc, reg, val, val_len);
        }

> You seem to be only calling this if that value is set correctly, but
> this seems very fragile.  You are also only calling this in one place,
> so why is this a function at all?  Just do the copy under the lock as
> needed in the calling location instead.
> 
>> +	spin_unlock(&uc->op_lock);
>> +}
>> +
>> +static void ccg_op_region_update(struct ucsi_ccg *uc, u32 cci)
>> +{
>> +	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(UCSI_MESSAGE_IN);
>> +	struct op_region *data = &uc->op_data;
>> +	u32 message_in[CCGX_MESSAGE_IN_MAX];
> 
> Are you sure you can put this big of a buffer on the stack?

It is 16 bytes total and so we did not think it was too big.

>> +
>> +	if (UCSI_CCI_LENGTH(cci))
>> +		if (ccg_read(uc, reg, (void *)&message_in,
>> +					sizeof(message_in))) {
> 
> Are you allowed to copy in into stack memory?  This ends up being an i2c
> message, right?  Can that be transferred into non-dma-able memory?

Yes the existing callers of ccg_read() are also using buffers on the 
stack for reading the data into.

>> +			dev_err(uc->dev, "failed to read MESSAGE_IN\n");
> 
> Why can you not fail this function?  You are throwing away the error
> here, that's not good.

Agree. We can take a look at this.

>> +			return;
>> +		}
>> +
>> +	spin_lock(&uc->op_lock);
>> +	memcpy(&data->cci, &cci, sizeof(cci));
> 
> Perhaps just:
> 	data->cci = cci;
> as this is only a 32bit value.

Agree.

>> +	if (UCSI_CCI_LENGTH(cci))
>> +		memcpy(&data->message_in, &message_in, sizeof(message_in));
>> +	spin_unlock(&uc->op_lock);
>> +}
>> +
>> +static void ccg_op_region_clean(struct ucsi_ccg *uc)
>> +{
>> +	struct op_region *data = &uc->op_data;
>> +
>> +	spin_lock(&uc->op_lock);
>> +	memset(&data->cci, 0, sizeof(data->cci));
> 
> 	data->cci = 0;
> 
>> +	memset(&data->message_in, 0, sizeof(data->message_in));
> 
> Or better yet, do it all at once:
> 	memset(&data, 0, sizeof(*data));
> 
>> +	spin_unlock(&uc->op_lock);
> 
> But why do you need to do this at all?  Why "clean" the whole buffer
> out, why not just set cci to 0 and be done with it?
> 
> Or why even clean this out at all, what happens if you do not?


I have been taking a look at this. If we don't clean the variable and 
buffer, then the previous state could be incorrectly read again after 
the next command has been sent.

Without this fix we occasionally see timeout errors such as ...

    ucsi_ccg 2-0008: error -ETIMEDOUT: PPM init failed (-110)


I tried not doing this at all, but then we see these timeout issues are 
still seen.

>> +}
>> +
>>   static int ucsi_ccg_init(struct ucsi_ccg *uc)
>>   {
>>   	unsigned int count = 10;
>>   	u8 data;
>>   	int status;
>>   
>> +	spin_lock_init(&uc->op_lock);
>> +
>>   	data = CCGX_RAB_UCSI_CONTROL_STOP;
>>   	status = ccg_write(uc, CCGX_RAB_UCSI_CONTROL, &data, sizeof(data));
>>   	if (status < 0)
>> @@ -520,9 +578,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>>   	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>>   	struct ucsi_capability *cap;
>>   	struct ucsi_altmode *alt;
>> -	int ret;
>> +	int ret = 0;
>> +
>> +	if ((offset == UCSI_CCI) || (offset == UCSI_MESSAGE_IN))
>> +		ccg_op_region_read(uc, offset, val, val_len);
>> +	else
>> +		ret = ccg_read(uc, reg, val, val_len);
>>   
>> -	ret = ccg_read(uc, reg, val, val_len);
>>   	if (ret)
>>   		return ret;
>>   
>> @@ -559,9 +621,13 @@ static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
>>   static int ucsi_ccg_async_write(struct ucsi *ucsi, unsigned int offset,
>>   				const void *val, size_t val_len)
>>   {
>> +	struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
>>   	u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
>>   
>> -	return ccg_write(ucsi_get_drvdata(ucsi), reg, val, val_len);
>> +	if (offset == UCSI_CONTROL)
>> +		ccg_op_region_clean(uc);
> 
> Why is this needed?  You have not documented it the need for this.

When we send a new command we need to clear out the previous state, if 
we don't we are seeing those timeouts. When we issue the next control 
command we are expecting a new state and so it does make sense to clear 
it here.

In general, I think we can improve this patch and add some more 
comments. I will work with Haotien and Sing-Han on this.

Cheers
Jon

-- 
nvpublic

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling
  2023-02-21 16:40   ` Jon Hunter
@ 2023-02-21 16:59     ` Greg Kroah-Hartman
  0 siblings, 0 replies; 7+ messages in thread
From: Greg Kroah-Hartman @ 2023-02-21 16:59 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Haotien Hsu, Heikki Krogerus, Sing-Han Chen, Sanket Goswami,
	Wayne Chang, Uwe Kleine-König, linux-usb, linux-kernel

On Tue, Feb 21, 2023 at 04:40:24PM +0000, Jon Hunter wrote:
> Hi Greg,
> 
> On 19/01/2023 12:28, Greg Kroah-Hartman wrote:
> > On Wed, Jan 18, 2023 at 02:15:23PM +0800, Haotien Hsu wrote:
> > > From: Sing-Han Chen <singhanc@nvidia.com>
> > > 
> > > For the CCGx, when the OPM field in the INTR_REG is cleared, then the
> > > CCI data in the PPM is reset.
> > > 
> > > To align with the CCGx UCSI interface guide, this patch updates the
> > > driver to copy CCI and MESSAGE_IN before clearing UCSI interrupt.
> > > When a new command is sent, the driver will clear the old CCI and
> > > MESSAGE_IN copy.
> > > 
> > > Finally, clear UCSI_READ_INT before calling complete() to ensure that
> > > the ucsi_ccg_sync_write() would wait for the interrupt handling to
> > > complete.
> > > It prevents the driver from resetting CCI prematurely.
> > > 
> > > Signed-off-by: Sing-Han Chen <singhanc@nvidia.com>
> > > Signed-off-by: Haotien Hsu <haotienh@nvidia.com>
> > > ---
> > > V1->V2
> > > - Fix uninitialized symbol 'cci'
> > > v2->v3
> > > - Remove misusing Reported-by tags
> > > v3->v4
> > > - Add comments for op_lock
> > > ---
> > >   drivers/usb/typec/ucsi/ucsi_ccg.c | 90 ++++++++++++++++++++++++++++---
> > >   1 file changed, 83 insertions(+), 7 deletions(-)
> > > 
> > > diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > > index eab3012e1b01..532813a32cc1 100644
> > > --- a/drivers/usb/typec/ucsi/ucsi_ccg.c
> > > +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c
> > > @@ -192,6 +192,12 @@ struct ucsi_ccg_altmode {
> > >   	bool checked;
> > >   } __packed;
> > > +#define CCGX_MESSAGE_IN_MAX 4
> > > +struct op_region {
> > > +	u32 cci;
> > 
> > This is coming from hardware so you have to specify the endian-ness of
> > it, right?
> 
> The current driver reads the 'cci' state in the ccg_irq_handler and here we
> just pass a variable of type u32 for storing the state. We are just adding
> variable of the same type to save the state. This value is returned to the
> ucsi layer which does not specify the endian-ness either. I guess this
> driver like many assume little endian. What is the guidance here? Should we
> be adding __le32 here even if the upper layers don't?

Yes, set what you are reading from the hardware, and then do the proper
transformation to the cpu native types for the upper layers where
needed.

> > Or why even clean this out at all, what happens if you do not?
> 
> 
> I have been taking a look at this. If we don't clean the variable and
> buffer, then the previous state could be incorrectly read again after the
> next command has been sent.
> 
> Without this fix we occasionally see timeout errors such as ...
> 
>    ucsi_ccg 2-0008: error -ETIMEDOUT: PPM init failed (-110)
> 
> 
> I tried not doing this at all, but then we see these timeout issues are
> still seen.

Then that means someone is not properly handling errors, and assuming
that whatever data is in the buffer is correct?  Try fixing that bug :)

See my other comments about not handling errors, perhaps that is where
the problem is.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2023-02-21 17:01 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2023-01-18  6:15 [PATCH v4] ucsi_ccg: Refine the UCSI Interrupt handling Haotien Hsu
2023-01-19 12:28 ` Greg Kroah-Hartman
2023-01-31  6:29   ` Haotien Hsu
2023-01-31  6:40     ` Greg Kroah-Hartman
2023-02-21 16:40   ` Jon Hunter
2023-02-21 16:59     ` Greg Kroah-Hartman
  -- strict thread matches above, loose matches on Subject: below --
2023-01-20 21:21 kernel test robot

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.