* [PATCH 1/8] i2c: i801: improve interrupt handler
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
@ 2022-04-15 16:54 ` Heiner Kallweit
2022-06-07 12:34 ` Jean Delvare
2022-04-15 16:55 ` [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ Heiner Kallweit
` (6 subsequent siblings)
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:54 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
Not sure if it can happen, but better play safe: If SMBHSTSTS_BYTE_DONE
and an error flag is set, then don't trust the result and skip calling
i801_isr_byte_done(). In addition clear status bit SMBHSTSTS_BYTE_DONE
in the main interrupt handler, this allows to simplify the code a
little.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 25 ++++++++-----------------
1 file changed, 8 insertions(+), 17 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index ff706349b..c481f121d 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -556,9 +556,6 @@ static void i801_isr_byte_done(struct i801_priv *priv)
/* Write next byte, except for IRQ after last byte */
outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
}
-
- /* Clear BYTE_DONE to continue with next byte */
- outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
}
static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
@@ -588,7 +585,6 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
* BUS_ERR - SMI# transaction collision
* FAILED - transaction was canceled due to a KILL request
* When any of these occur, update ->status and signal completion.
- * ->status must be cleared before kicking off the next transaction.
*
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
* occurs for each byte of a byte-by-byte to prepare the next byte.
@@ -613,23 +609,18 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
}
status = inb_p(SMBHSTSTS(priv));
- if (status & SMBHSTSTS_BYTE_DONE)
+ if ((status & SMBHSTSTS_BYTE_DONE) && !(status & STATUS_ERROR_FLAGS))
i801_isr_byte_done(priv);
/*
- * Clear remaining IRQ sources: Completion of last command, errors
- * and the SMB_ALERT signal. SMB_ALERT status is set after signal
- * assertion independently of the interrupt generation being blocked
- * or not so clear it always when the status is set.
- */
- status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS | SMBHSTSTS_SMBALERT_STS;
- if (status)
- outb_p(status, SMBHSTSTS(priv));
- status &= ~SMBHSTSTS_SMBALERT_STS; /* SMB_ALERT not reported */
- /*
- * Report transaction result.
- * ->status must be cleared before the next transaction is started.
+ * Clear IRQ sources: SMB_ALERT status is set after signal assertion
+ * independently of the interrupt generation being blocked or not
+ * so clear it always when the status is set.
*/
+ status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS;
+ outb_p(status, SMBHSTSTS(priv));
+
+ status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
if (status) {
priv->status = status;
complete(&priv->done);
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 1/8] i2c: i801: improve interrupt handler
2022-04-15 16:54 ` [PATCH 1/8] i2c: i801: improve interrupt handler Heiner Kallweit
@ 2022-06-07 12:34 ` Jean Delvare
2022-12-15 22:15 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-07 12:34 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:54:32 +0200, Heiner Kallweit wrote:
> Not sure if it can happen, but better play safe: If SMBHSTSTS_BYTE_DONE
> and an error flag is set, then don't trust the result and skip calling
> i801_isr_byte_done(). In addition clear status bit SMBHSTSTS_BYTE_DONE
> in the main interrupt handler, this allows to simplify the code a
> little.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 25 ++++++++-----------------
> 1 file changed, 8 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index ff706349b..c481f121d 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -556,9 +556,6 @@ static void i801_isr_byte_done(struct i801_priv *priv)
> /* Write next byte, except for IRQ after last byte */
> outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
> }
> -
> - /* Clear BYTE_DONE to continue with next byte */
> - outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
> }
>
> static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
> @@ -588,7 +585,6 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
> * BUS_ERR - SMI# transaction collision
> * FAILED - transaction was canceled due to a KILL request
> * When any of these occur, update ->status and signal completion.
> - * ->status must be cleared before kicking off the next transaction.
> *
> * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
> * occurs for each byte of a byte-by-byte to prepare the next byte.
> @@ -613,23 +609,18 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
> }
>
> status = inb_p(SMBHSTSTS(priv));
> - if (status & SMBHSTSTS_BYTE_DONE)
> + if ((status & SMBHSTSTS_BYTE_DONE) && !(status & STATUS_ERROR_FLAGS))
Isn't this better written
if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE)
? At least my compiler generates smaller binary code.
> i801_isr_byte_done(priv);
>
> /*
> - * Clear remaining IRQ sources: Completion of last command, errors
> - * and the SMB_ALERT signal. SMB_ALERT status is set after signal
> - * assertion independently of the interrupt generation being blocked
> - * or not so clear it always when the status is set.
> - */
> - status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS | SMBHSTSTS_SMBALERT_STS;
> - if (status)
> - outb_p(status, SMBHSTSTS(priv));
> - status &= ~SMBHSTSTS_SMBALERT_STS; /* SMB_ALERT not reported */
> - /*
> - * Report transaction result.
> - * ->status must be cleared before the next transaction is started.
> + * Clear IRQ sources: SMB_ALERT status is set after signal assertion
> + * independently of the interrupt generation being blocked or not
> + * so clear it always when the status is set.
> */
> + status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS;
> + outb_p(status, SMBHSTSTS(priv));
You are making the call to outb_p() unconditional. Is this under the
assumption that at least one of the bits must be set, so the condition
was always met?
> +
> + status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
> if (status) {
> priv->status = status;
> complete(&priv->done);
Tested OK on my system, looks good overall, nice simplification.
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 1/8] i2c: i801: improve interrupt handler
2022-06-07 12:34 ` Jean Delvare
@ 2022-12-15 22:15 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-15 22:15 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 07.06.2022 14:34, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:54:32 +0200, Heiner Kallweit wrote:
>> Not sure if it can happen, but better play safe: If SMBHSTSTS_BYTE_DONE
>> and an error flag is set, then don't trust the result and skip calling
>> i801_isr_byte_done(). In addition clear status bit SMBHSTSTS_BYTE_DONE
>> in the main interrupt handler, this allows to simplify the code a
>> little.
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 25 ++++++++-----------------
>> 1 file changed, 8 insertions(+), 17 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index ff706349b..c481f121d 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -556,9 +556,6 @@ static void i801_isr_byte_done(struct i801_priv *priv)
>> /* Write next byte, except for IRQ after last byte */
>> outb_p(priv->data[++priv->count], SMBBLKDAT(priv));
>> }
>> -
>> - /* Clear BYTE_DONE to continue with next byte */
>> - outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
>> }
>>
>> static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
>> @@ -588,7 +585,6 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
>> * BUS_ERR - SMI# transaction collision
>> * FAILED - transaction was canceled due to a KILL request
>> * When any of these occur, update ->status and signal completion.
>> - * ->status must be cleared before kicking off the next transaction.
>> *
>> * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
>> * occurs for each byte of a byte-by-byte to prepare the next byte.
>> @@ -613,23 +609,18 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
>> }
>>
>> status = inb_p(SMBHSTSTS(priv));
>> - if (status & SMBHSTSTS_BYTE_DONE)
>> + if ((status & SMBHSTSTS_BYTE_DONE) && !(status & STATUS_ERROR_FLAGS))
>
> Isn't this better written
>
> if ((status & (SMBHSTSTS_BYTE_DONE | STATUS_ERROR_FLAGS)) == SMBHSTSTS_BYTE_DONE)
>
> ? At least my compiler generates smaller binary code.
>
OK. Meanwhile I prefer readability over saving few bytes, but here IMO
the readability doesn't suffer.
>> i801_isr_byte_done(priv);
>>
>> /*
>> - * Clear remaining IRQ sources: Completion of last command, errors
>> - * and the SMB_ALERT signal. SMB_ALERT status is set after signal
>> - * assertion independently of the interrupt generation being blocked
>> - * or not so clear it always when the status is set.
>> - */
>> - status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS | SMBHSTSTS_SMBALERT_STS;
>> - if (status)
>> - outb_p(status, SMBHSTSTS(priv));
>> - status &= ~SMBHSTSTS_SMBALERT_STS; /* SMB_ALERT not reported */
>> - /*
>> - * Report transaction result.
>> - * ->status must be cleared before the next transaction is started.
>> + * Clear IRQ sources: SMB_ALERT status is set after signal assertion
>> + * independently of the interrupt generation being blocked or not
>> + * so clear it always when the status is set.
>> */
>> + status &= STATUS_FLAGS | SMBHSTSTS_SMBALERT_STS;
>> + outb_p(status, SMBHSTSTS(priv));
>
> You are making the call to outb_p() unconditional. Is this under the
> assumption that at least one of the bits must be set, so the condition
> was always met?
>
So far we exclude here the case where SMBHSTSTS is written in
i801_isr_byte_done(). The patch allows to write SMBHSTSTS here only,
so we don't need the condition any longer.
>> +
>> + status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
>> if (status) {
>> priv->status = status;
>> complete(&priv->done);
>
> Tested OK on my system, looks good overall, nice simplification.
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
2022-04-15 16:54 ` [PATCH 1/8] i2c: i801: improve interrupt handler Heiner Kallweit
@ 2022-04-15 16:55 ` Heiner Kallweit
2022-06-07 12:48 ` Jean Delvare
2022-04-15 16:55 ` [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER Heiner Kallweit
` (5 subsequent siblings)
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:55 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
Host notification uses an interrupt, therefore it makes sense only if
interrupts are enabled. Make this dependency explicit by removing
FEATURE_HOST_NOTIFY if FEATURE_IRQ isn't set.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index c481f121d..eccdc7035 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -1756,6 +1756,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
dev_info(&dev->dev, "SMBus using %s\n",
priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
+ if (!(priv->features & FEATURE_IRQ))
+ priv->features &= ~FEATURE_HOST_NOTIFY;
+
i801_add_tco(priv);
snprintf(priv->adapter.name, sizeof(priv->adapter.name),
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ
2022-04-15 16:55 ` [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ Heiner Kallweit
@ 2022-06-07 12:48 ` Jean Delvare
2022-12-16 20:23 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-07 12:48 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:55:10 +0200, Heiner Kallweit wrote:
> Host notification uses an interrupt, therefore it makes sense only if
> interrupts are enabled.
It would be nice to have this comment in the code itself.
> Make this dependency explicit by removing
> FEATURE_HOST_NOTIFY if FEATURE_IRQ isn't set.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index c481f121d..eccdc7035 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -1756,6 +1756,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
> dev_info(&dev->dev, "SMBus using %s\n",
> priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
>
> + if (!(priv->features & FEATURE_IRQ))
> + priv->features &= ~FEATURE_HOST_NOTIFY;
Earlier in this function, there's an action which depends on the
FEATURE_HOST_NOTIFY flag being set. While this will only result in a
useless action and wouldn't cause a bug as far as I can see, wouldn't
it be cleaner to move that piece of code *after* the check you're
adding?
> +
> i801_add_tco(priv);
>
> snprintf(priv->adapter.name, sizeof(priv->adapter.name),
Looks good, tested OK on my system (non-regression only, I'm not using
the Host Notify feature).
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ
2022-06-07 12:48 ` Jean Delvare
@ 2022-12-16 20:23 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-16 20:23 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 07.06.2022 14:48, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:55:10 +0200, Heiner Kallweit wrote:
>> Host notification uses an interrupt, therefore it makes sense only if
>> interrupts are enabled.
>
> It would be nice to have this comment in the code itself.
>
OK
>> Make this dependency explicit by removing
>> FEATURE_HOST_NOTIFY if FEATURE_IRQ isn't set.
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index c481f121d..eccdc7035 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -1756,6 +1756,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
>> dev_info(&dev->dev, "SMBus using %s\n",
>> priv->features & FEATURE_IRQ ? "PCI interrupt" : "polling");
>>
>> + if (!(priv->features & FEATURE_IRQ))
>> + priv->features &= ~FEATURE_HOST_NOTIFY;
>
> Earlier in this function, there's an action which depends on the
> FEATURE_HOST_NOTIFY flag being set. While this will only result in a
> useless action and wouldn't cause a bug as far as I can see, wouldn't
> it be cleaner to move that piece of code *after* the check you're
> adding?
>
Yes, this would be better. Will be part of v2.
>> +
>> i801_add_tco(priv);
>>
>> snprintf(priv->adapter.name, sizeof(priv->adapter.name),
>
> Looks good, tested OK on my system (non-regression only, I'm not using
> the Host Notify feature).
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
2022-04-15 16:54 ` [PATCH 1/8] i2c: i801: improve interrupt handler Heiner Kallweit
2022-04-15 16:55 ` [PATCH 2/8] i2c: i801: make FEATURE_HOST_NOTIFY dependent on FEATURE_IRQ Heiner Kallweit
@ 2022-04-15 16:55 ` Heiner Kallweit
2022-06-07 14:13 ` Jean Delvare
2022-04-15 16:56 ` [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions Heiner Kallweit
` (4 subsequent siblings)
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:55 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
According to the datasheet the block process call requires block
buffer mode. The user may disable block buffer mode by module
parameter disable_features, in such a case we have to clear
FEATURE_BLOCK_PROC.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index eccdc7035..1d8182901 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -1675,6 +1675,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
priv->features &= ~disable_features;
+ if (!(priv->features & FEATURE_BLOCK_BUFFER))
+ priv->features &= ~FEATURE_BLOCK_PROC;
+
err = pcim_enable_device(dev);
if (err) {
dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER
2022-04-15 16:55 ` [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER Heiner Kallweit
@ 2022-06-07 14:13 ` Jean Delvare
2022-12-16 20:57 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-07 14:13 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:55:46 +0200, Heiner Kallweit wrote:
> According to the datasheet the block process call requires block
> buffer mode. The user may disable block buffer mode by module
> parameter disable_features, in such a case we have to clear
> FEATURE_BLOCK_PROC.
In which datasheet are you seeing this? Can you point me to the
specific section and/or quote the statement? I can't find it in the
datasheet I'm looking at (ICH9, Intel document 316972-002) but it is
huge and I may just be missing it.
Also, same request as previous patch, I'd like a comment in the code,
so that developers don't have to read the git log to figure out why this
piece of code is there.
Furthermore, as far as I can see, the FEATURE_BLOCK_PROC flag only
affects the value returned by i801_func(). i801_access() does not
verify whether this flag is set before processing a command where size
== I2C_SMBUS_BLOCK_PROC_CALL. I think it should? Otherwise your fix is
only partial (will work if the device driver calls .functionality as it
is supposed to, will fail with - I suppose - unpredictable results if
the device driver calls .smbus_xfer directly).
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 3 +++
> 1 file changed, 3 insertions(+)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index eccdc7035..1d8182901 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -1675,6 +1675,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
> }
> priv->features &= ~disable_features;
>
> + if (!(priv->features & FEATURE_BLOCK_BUFFER))
> + priv->features &= ~FEATURE_BLOCK_PROC;
> +
> err = pcim_enable_device(dev);
> if (err) {
> dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
Thanks,
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER
2022-06-07 14:13 ` Jean Delvare
@ 2022-12-16 20:57 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-16 20:57 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 07.06.2022 16:13, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:55:46 +0200, Heiner Kallweit wrote:
>> According to the datasheet the block process call requires block
>> buffer mode. The user may disable block buffer mode by module
>> parameter disable_features, in such a case we have to clear
>> FEATURE_BLOCK_PROC.
>
> In which datasheet are you seeing this? Can you point me to the
> specific section and/or quote the statement? I can't find it in the
> datasheet I'm looking at (ICH9, Intel document 316972-002) but it is
> huge and I may just be missing it.
>
I used the following datasheet:
Intel 9 Series Chipset Family PCH
330550-002
On page 211 the block process call is described.
There's a note: E32B bit in the Auxiliary Control register must be set when using this protocol.
The same note can be found on page 663.
> Also, same request as previous patch, I'd like a comment in the code,
> so that developers don't have to read the git log to figure out why this
> piece of code is there.
>
OK
> Furthermore, as far as I can see, the FEATURE_BLOCK_PROC flag only
> affects the value returned by i801_func(). i801_access() does not
> verify whether this flag is set before processing a command where size
> == I2C_SMBUS_BLOCK_PROC_CALL. I think it should? Otherwise your fix is
> only partial (will work if the device driver calls .functionality as it
> is supposed to, will fail with - I suppose - unpredictable results if
> the device driver calls .smbus_xfer directly).
>
If FEATURE_BLOCK_PROC isn't set then we would call
i801_block_transaction_byte_by_byte() according to the following code
in i801_block_transaction().
if ((priv->features & FEATURE_BLOCK_BUFFER) &&
command != I2C_SMBUS_I2C_BLOCK_DATA)
result = i801_block_transaction_by_block(priv, data,
read_write,
command);
else
result = i801_block_transaction_byte_by_byte(priv, data,
read_write,
command);
And i801_block_transaction_byte_by_byte() immediately returns
-EOPNOTSUPP in case of command == I2C_SMBUS_BLOCK_PROC_CALL.
Having said that the requested check is there, it's just executed
a little bit later.
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 3 +++
>> 1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index eccdc7035..1d8182901 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -1675,6 +1675,9 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
>> }
>> priv->features &= ~disable_features;
>>
>> + if (!(priv->features & FEATURE_BLOCK_BUFFER))
>> + priv->features &= ~FEATURE_BLOCK_PROC;
>> +
>> err = pcim_enable_device(dev);
>> if (err) {
>> dev_err(&dev->dev, "Failed to enable SMBus PCI device (%d)\n",
>
> Thanks,
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
` (2 preceding siblings ...)
2022-04-15 16:55 ` [PATCH 3/8] i2c: i801: make FEATURE_BLOCK_PROC dependent on FEATURE_BLOCK_BUFFER Heiner Kallweit
@ 2022-04-15 16:56 ` Heiner Kallweit
2022-06-07 14:24 ` Jean Delvare
2022-04-15 16:57 ` [PATCH 5/8] i2c: i801: add helper i801_set_hstadd() Heiner Kallweit
` (3 subsequent siblings)
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:56 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
According to the datasheets interrupt mode and i2c block read are
supported on all chip versions. Therefore set both feature flags for
all chip versions.
Note: Don't remove the two feature flags as such (at least for now),
so that in case of a problem users can use the disable_features
module parameter to disable a problematic feature.
Patch is based solely on the datasheets. I don't have old enough
test hw, therefore the patch is compile-tested only.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 140 +++++++++++++++++-----------------
1 file changed, 69 insertions(+), 71 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 1d8182901..a9737f14d 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -12,70 +12,70 @@
/*
* Supports the following Intel I/O Controller Hubs (ICH):
*
- * I/O Block I2C
- * region SMBus Block proc. block
- * Chip name PCI ID size PEC buffer call read
- * ---------------------------------------------------------------------------
- * 82801AA (ICH) 0x2413 16 no no no no
- * 82801AB (ICH0) 0x2423 16 no no no no
- * 82801BA (ICH2) 0x2443 16 no no no no
- * 82801CA (ICH3) 0x2483 32 soft no no no
- * 82801DB (ICH4) 0x24c3 32 hard yes no no
- * 82801E (ICH5) 0x24d3 32 hard yes yes yes
- * 6300ESB 0x25a4 32 hard yes yes yes
- * 82801F (ICH6) 0x266a 32 hard yes yes yes
- * 6310ESB/6320ESB 0x269b 32 hard yes yes yes
- * 82801G (ICH7) 0x27da 32 hard yes yes yes
- * 82801H (ICH8) 0x283e 32 hard yes yes yes
- * 82801I (ICH9) 0x2930 32 hard yes yes yes
- * EP80579 (Tolapai) 0x5032 32 hard yes yes yes
- * ICH10 0x3a30 32 hard yes yes yes
- * ICH10 0x3a60 32 hard yes yes yes
- * 5/3400 Series (PCH) 0x3b30 32 hard yes yes yes
- * 6 Series (PCH) 0x1c22 32 hard yes yes yes
- * Patsburg (PCH) 0x1d22 32 hard yes yes yes
- * Patsburg (PCH) IDF 0x1d70 32 hard yes yes yes
- * Patsburg (PCH) IDF 0x1d71 32 hard yes yes yes
- * Patsburg (PCH) IDF 0x1d72 32 hard yes yes yes
- * DH89xxCC (PCH) 0x2330 32 hard yes yes yes
- * Panther Point (PCH) 0x1e22 32 hard yes yes yes
- * Lynx Point (PCH) 0x8c22 32 hard yes yes yes
- * Lynx Point-LP (PCH) 0x9c22 32 hard yes yes yes
- * Avoton (SOC) 0x1f3c 32 hard yes yes yes
- * Wellsburg (PCH) 0x8d22 32 hard yes yes yes
- * Wellsburg (PCH) MS 0x8d7d 32 hard yes yes yes
- * Wellsburg (PCH) MS 0x8d7e 32 hard yes yes yes
- * Wellsburg (PCH) MS 0x8d7f 32 hard yes yes yes
- * Coleto Creek (PCH) 0x23b0 32 hard yes yes yes
- * Wildcat Point (PCH) 0x8ca2 32 hard yes yes yes
- * Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes yes
- * BayTrail (SOC) 0x0f12 32 hard yes yes yes
- * Braswell (SOC) 0x2292 32 hard yes yes yes
- * Sunrise Point-H (PCH) 0xa123 32 hard yes yes yes
- * Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes yes
- * DNV (SOC) 0x19df 32 hard yes yes yes
- * Emmitsburg (PCH) 0x1bc9 32 hard yes yes yes
- * Broxton (SOC) 0x5ad4 32 hard yes yes yes
- * Lewisburg (PCH) 0xa1a3 32 hard yes yes yes
- * Lewisburg Supersku (PCH) 0xa223 32 hard yes yes yes
- * Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes yes
- * Gemini Lake (SOC) 0x31d4 32 hard yes yes yes
- * Cannon Lake-H (PCH) 0xa323 32 hard yes yes yes
- * Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes yes
- * Cedar Fork (PCH) 0x18df 32 hard yes yes yes
- * Ice Lake-LP (PCH) 0x34a3 32 hard yes yes yes
- * Ice Lake-N (PCH) 0x38a3 32 hard yes yes yes
- * Comet Lake (PCH) 0x02a3 32 hard yes yes yes
- * Comet Lake-H (PCH) 0x06a3 32 hard yes yes yes
- * Elkhart Lake (PCH) 0x4b23 32 hard yes yes yes
- * Tiger Lake-LP (PCH) 0xa0a3 32 hard yes yes yes
- * Tiger Lake-H (PCH) 0x43a3 32 hard yes yes yes
- * Jasper Lake (SOC) 0x4da3 32 hard yes yes yes
- * Comet Lake-V (PCH) 0xa3a3 32 hard yes yes yes
- * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes yes
- * Alder Lake-P (PCH) 0x51a3 32 hard yes yes yes
- * Alder Lake-M (PCH) 0x54a3 32 hard yes yes yes
- * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes yes
+ * I/O Block
+ * region SMBus Block proc.
+ * Chip name PCI ID size PEC buffer call
+ * -------------------------------------------------------------------
+ * 82801AA (ICH) 0x2413 16 no no no
+ * 82801AB (ICH0) 0x2423 16 no no no
+ * 82801BA (ICH2) 0x2443 16 no no no
+ * 82801CA (ICH3) 0x2483 32 soft no no
+ * 82801DB (ICH4) 0x24c3 32 hard yes no
+ * 82801E (ICH5) 0x24d3 32 hard yes yes
+ * 6300ESB 0x25a4 32 hard yes yes
+ * 82801F (ICH6) 0x266a 32 hard yes yes
+ * 6310ESB/6320ESB 0x269b 32 hard yes yes
+ * 82801G (ICH7) 0x27da 32 hard yes yes
+ * 82801H (ICH8) 0x283e 32 hard yes yes
+ * 82801I (ICH9) 0x2930 32 hard yes yes
+ * EP80579 (Tolapai) 0x5032 32 hard yes yes
+ * ICH10 0x3a30 32 hard yes yes
+ * ICH10 0x3a60 32 hard yes yes
+ * 5/3400 Series (PCH) 0x3b30 32 hard yes yes
+ * 6 Series (PCH) 0x1c22 32 hard yes yes
+ * Patsburg (PCH) 0x1d22 32 hard yes yes
+ * Patsburg (PCH) IDF 0x1d70 32 hard yes yes
+ * Patsburg (PCH) IDF 0x1d71 32 hard yes yes
+ * Patsburg (PCH) IDF 0x1d72 32 hard yes yes
+ * DH89xxCC (PCH) 0x2330 32 hard yes yes
+ * Panther Point (PCH) 0x1e22 32 hard yes yes
+ * Lynx Point (PCH) 0x8c22 32 hard yes yes
+ * Lynx Point-LP (PCH) 0x9c22 32 hard yes yes
+ * Avoton (SOC) 0x1f3c 32 hard yes yes
+ * Wellsburg (PCH) 0x8d22 32 hard yes yes
+ * Wellsburg (PCH) MS 0x8d7d 32 hard yes yes
+ * Wellsburg (PCH) MS 0x8d7e 32 hard yes yes
+ * Wellsburg (PCH) MS 0x8d7f 32 hard yes yes
+ * Coleto Creek (PCH) 0x23b0 32 hard yes yes
+ * Wildcat Point (PCH) 0x8ca2 32 hard yes yes
+ * Wildcat Point-LP (PCH) 0x9ca2 32 hard yes yes
+ * BayTrail (SOC) 0x0f12 32 hard yes yes
+ * Braswell (SOC) 0x2292 32 hard yes yes
+ * Sunrise Point-H (PCH) 0xa123 32 hard yes yes
+ * Sunrise Point-LP (PCH) 0x9d23 32 hard yes yes
+ * DNV (SOC) 0x19df 32 hard yes yes
+ * Emmitsburg (PCH) 0x1bc9 32 hard yes yes
+ * Broxton (SOC) 0x5ad4 32 hard yes yes
+ * Lewisburg (PCH) 0xa1a3 32 hard yes yes
+ * Lewisburg Supersku (PCH) 0xa223 32 hard yes yes
+ * Kaby Lake PCH-H (PCH) 0xa2a3 32 hard yes yes
+ * Gemini Lake (SOC) 0x31d4 32 hard yes yes
+ * Cannon Lake-H (PCH) 0xa323 32 hard yes yes
+ * Cannon Lake-LP (PCH) 0x9da3 32 hard yes yes
+ * Cedar Fork (PCH) 0x18df 32 hard yes yes
+ * Ice Lake-LP (PCH) 0x34a3 32 hard yes yes
+ * Ice Lake-N (PCH) 0x38a3 32 hard yes yes
+ * Comet Lake (PCH) 0x02a3 32 hard yes yes
+ * Comet Lake-H (PCH) 0x06a3 32 hard yes yes
+ * Elkhart Lake (PCH) 0x4b23 32 hard yes yes
+ * Tiger Lake-LP (PCH) 0xa0a3 32 hard yes yes
+ * Tiger Lake-H (PCH) 0x43a3 32 hard yes yes
+ * Jasper Lake (SOC) 0x4da3 32 hard yes yes
+ * Comet Lake-V (PCH) 0xa3a3 32 hard yes yes
+ * Alder Lake-S (PCH) 0x7aa3 32 hard yes yes
+ * Alder Lake-P (PCH) 0x51a3 32 hard yes yes
+ * Alder Lake-M (PCH) 0x54a3 32 hard yes yes
+ * Raptor Lake-S (PCH) 0x7a23 32 hard yes yes
*
* Features supported by this driver:
* Software PEC no
@@ -168,7 +168,7 @@
#define I801_WORD_DATA 0x0C
#define I801_PROC_CALL 0x10
#define I801_BLOCK_DATA 0x14
-#define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */
+#define I801_I2C_BLOCK_DATA 0x18
#define I801_BLOCK_PROC_CALL 0x1C
/* I801 Host Control register bits */
@@ -973,11 +973,8 @@ static const struct i2c_algorithm smbus_algorithm = {
.functionality = i801_func,
};
-#define FEATURES_ICH5 (FEATURE_BLOCK_PROC | FEATURE_I2C_BLOCK_READ | \
- FEATURE_IRQ | FEATURE_SMBUS_PEC | \
- FEATURE_BLOCK_BUFFER | FEATURE_HOST_NOTIFY)
-#define FEATURES_ICH4 (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER | \
- FEATURE_HOST_NOTIFY)
+#define FEATURES_ICH4 (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER | FEATURE_HOST_NOTIFY)
+#define FEATURES_ICH5 (FEATURES_ICH4 | FEATURE_BLOCK_PROC)
static const struct pci_device_id i801_ids[] = {
{ PCI_DEVICE_DATA(INTEL, 82801AA_3, 0) },
@@ -1665,7 +1662,8 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
mutex_init(&priv->acpi_lock);
priv->pci_dev = dev;
- priv->features = id->driver_data;
+ priv->features = FEATURE_IRQ | FEATURE_I2C_BLOCK_READ;
+ priv->features |= id->driver_data;
/* Disable features on user request */
for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) {
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions
2022-04-15 16:56 ` [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions Heiner Kallweit
@ 2022-06-07 14:24 ` Jean Delvare
2022-06-13 17:08 ` Jean Delvare
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-07 14:24 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:56:30 +0200, Heiner Kallweit wrote:
> According to the datasheets interrupt mode and i2c block read are
> supported on all chip versions. Therefore set both feature flags for
> all chip versions.
While the datasheets do match your claims (I checked the 82801CAM aka
ICH3-M datasheet), I have a hard time believing we would have made the
feature device-dependent without a good reason (and I have vague
memories that there was a problem, although I can't find any proof of
that).
So I'll try to resurrect my old ICH3-M-based laptop and test these
changes on it. If I manage to get a Linux distribution to install on
that 20-year-old system...
> Note: Don't remove the two feature flags as such (at least for now),
> so that in case of a problem users can use the disable_features
> module parameter to disable a problematic feature.
Agreed.
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions
2022-06-07 14:24 ` Jean Delvare
@ 2022-06-13 17:08 ` Jean Delvare
2022-06-14 12:59 ` Jean Delvare
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-13 17:08 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
I was able to resurrect my Sony Vaio GR214EP laptop to test this patch
on an Intel 82801CAM (ICH3-M) chipset. And as I suspected, it does not
work. I get the following error in the kernel log:
[13563.411101] i801_smbus 0000:00:1f.3: SMBus using PCI interrupt
[13565.434712] irq 9: nobody cared (try booting with the "irqpoll" option)
[13565.434728] Pid: 321, comm: udevd Tainted: G O 3.4.63-2.44-default #1
[13565.434734] Call Trace:
[13565.434773] [<c0205349>] try_stack_unwind+0x199/0x1b0
[13565.434793] [<c02041c7>] dump_trace+0x47/0xf0
[13565.434808] [<c02053ab>] show_trace_log_lvl+0x4b/0x60
[13565.434820] [<c02053d8>] show_trace+0x18/0x20
[13565.434837] [<c0682e81>] dump_stack+0x6d/0x72
[13565.434855] [<c02adad1>] __report_bad_irq+0x21/0xc0
[13565.434870] [<c02adef1>] note_interrupt+0x181/0x1d0
[13565.434887] [<c02abe9e>] handle_irq_event_percpu+0x9e/0x1d0
[13565.434901] [<c02abffe>] handle_irq_event+0x2e/0x50
[13565.434915] [<c02ae3e6>] handle_level_irq+0x56/0x90
[13565.434928] [<c0204168>] handle_irq+0x88/0xa0
[13565.434939] handlers:
[13565.434949] [<c04bbf6c>] acpi_irq
[13565.435027] [<e0baad00>] usb_hcd_irq
[13565.435054] [<e0baad00>] usb_hcd_irq
[13565.435079] [<e0baad00>] usb_hcd_irq
[13565.435193] [<e0d6fb80>] radeon_driver_irq_handler_kms
[13565.435206] [<e0af5c60>] yenta_interrupt
[13565.435214] [<e0af5c60>] yenta_interrupt
[13565.435227] [<e0b514c0>] irq_handler
[13565.435238] [<e0aeec90>] snd_intel8x0m_interrupt
[13565.435251] [<e0c514a0>] snd_intel8x0_interrupt
[13565.435264] [<e0ab4060>] e100_intr
[13565.435278] [<e09465d0>] i801_isr
[13565.435283] Disabling IRQ #9
[13565.437114] i801_smbus 0000:00:1f.3: Transaction timeout
[13565.437125] i801_smbus 0000:00:1f.3: Terminating the current operation
[13565.439189] i801_smbus 0000:00:1f.3: Failed terminating the transaction
If it matters, and as seen in the list of handlers above, IRQ9 is
heavily shared on this laptop (ACPI, USB, graphics, PCMCIA, sound,
network and SMBus).
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions
2022-06-13 17:08 ` Jean Delvare
@ 2022-06-14 12:59 ` Jean Delvare
2022-12-16 21:36 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-14 12:59 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Mon, 13 Jun 2022 19:08:41 +0200, Jean Delvare wrote:
> I was able to resurrect my Sony Vaio GR214EP laptop to test this patch
> on an Intel 82801CAM (ICH3-M) chipset. And as I suspected, it does not
> work. I get the following error in the kernel log:
>
> [13563.411101] i801_smbus 0000:00:1f.3: SMBus using PCI interrupt
> [13565.434712] irq 9: nobody cared (try booting with the "irqpoll" option)
And now it's all coming back to me. The reason why we did not enable
interrupts on chipsets older than the ICH5 is because they lack bit
INTS in PCI register PCISTS. When the IRQ line is shared, we don't know
whether the interrupt was caused by our device or by another device.
Specifically, the following piece of code fails (it returns IRQ_NONE
unconditionally):
static irqreturn_t i801_isr(int irq, void *dev_id)
{
(...)
/* Confirm this is our interrupt */
pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
if (!(pcists & PCI_STATUS_INTERRUPT))
return IRQ_NONE;
I tried replacing the code above by a check on SMBHSTSTS instead:
status = inb_p(SMBHSTSTS(priv));
if (!(status & STATUS_FLAGS))
return IRQ_NONE;
It seems to work, however my testing is limited and I don't know how
reliable that would be if the other devices sharing the interrupt line
were used heavily at the same time (the laptop is idling in text mode
at the moment so the fact that the interrupt line is heavily shared
does not really get exercised).
Then I loaded the driver with interrupts disabled
(disable_features=0x10) to see if I2C block read was working. It is NOT
working, as can be seen from the following dumps from the memory SPD
EEPROM:
linux-w6i1:/home/jdelvare/src/i2c-i801 # i2cdump -y 4 0x50 b
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 80 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 ??????@.?uT.??.?
10: 8f 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 ?????.??`..???,
20: 15 08 15 08 00 00 00 00 00 00 00 00 00 00 00 00 ????............
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 b9 ..............??
40: 7f da 00 00 00 00 00 00 53 53 50 31 33 33 2d 30 ??......SSP133-0
50: 36 34 33 32 33 4a 00 00 00 00 00 00 00 01 02 00 64323J.......??.
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 cd ..............d?
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
linux-w6i1:/home/jdelvare/src/i2c-i801 # i2cdump -y 4 0x50 i
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
10: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
20: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
30: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
40: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
50: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
60: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
70: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
80: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
90: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
a0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
b0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
c0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
d0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
e0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
f0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
As you can see, the requested offset of the I2C block read (0x00, then
0x20, then 0x40 etc.) is being ignored, and instead every I2C block
read starts at EEPROM offset 0x01.
If you compare the datasheets of the ICH5 (Intel doc 252516-001) and
ICH3-M (Intel doc 290716-001), section "I2C Read", you will see that
the description of the command is different. The format described for
the ICH5 (table 114 in the datasheet) does match what the Linux i2c
stack calls an I2C block read, while the format described for the
ICH3-M (table 5-87 in the datasheet) does NOT. Apparently the original
implementation was aimed at specific devices using 10-bit I2C
addressing. As no other SMBus host device implemented that, we did not
even allocate an SMBus command constant to it (and the fact that Intel
changed the format in later hardware iterations proves that this was the
right thing to do).
Looking at the ICH3-M implementation of the "I2C Block Read" transfer,
I feel very lucky that I did not trash my memory SPD EEPROM while
running the command. Because the format really starts with a WRITE of 2
bytes to the EEPROM before reading from it.
So at least the I2C block read part of the patch is never going to
work. The interrupt part could work if we change the test as described
above, however I would question the relevance of making that change
considering that it is no longer the obvious clean-up you originally
proposed, and would then impact recent hardware too. I would hate to
introduce a regression for the sole purpose of enabling interrupts on
20-year old hardware which I doubt many people are still using.
I am available to perform any test you want me to on this old laptop.
As long as it runs...
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions
2022-06-14 12:59 ` Jean Delvare
@ 2022-12-16 21:36 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-16 21:36 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 14.06.2022 14:59, Jean Delvare wrote:
> Hi Heiner,
>
> On Mon, 13 Jun 2022 19:08:41 +0200, Jean Delvare wrote:
>> I was able to resurrect my Sony Vaio GR214EP laptop to test this patch
>> on an Intel 82801CAM (ICH3-M) chipset. And as I suspected, it does not
>> work. I get the following error in the kernel log:
>>
>> [13563.411101] i801_smbus 0000:00:1f.3: SMBus using PCI interrupt
>> [13565.434712] irq 9: nobody cared (try booting with the "irqpoll" option)
>
> And now it's all coming back to me. The reason why we did not enable
> interrupts on chipsets older than the ICH5 is because they lack bit
> INTS in PCI register PCISTS. When the IRQ line is shared, we don't know
> whether the interrupt was caused by our device or by another device.
> Specifically, the following piece of code fails (it returns IRQ_NONE
> unconditionally):
>
> static irqreturn_t i801_isr(int irq, void *dev_id)
> {
> (...)
> /* Confirm this is our interrupt */
> pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
> if (!(pcists & PCI_STATUS_INTERRUPT))
> return IRQ_NONE;
>
> I tried replacing the code above by a check on SMBHSTSTS instead:
>
> status = inb_p(SMBHSTSTS(priv));
> if (!(status & STATUS_FLAGS))
> return IRQ_NONE;
>
> It seems to work, however my testing is limited and I don't know how
> reliable that would be if the other devices sharing the interrupt line
> were used heavily at the same time (the laptop is idling in text mode
> at the moment so the fact that the interrupt line is heavily shared
> does not really get exercised).
>
> Then I loaded the driver with interrupts disabled
> (disable_features=0x10) to see if I2C block read was working. It is NOT
> working, as can be seen from the following dumps from the memory SPD
> EEPROM:
>
> linux-w6i1:/home/jdelvare/src/i2c-i801 # i2cdump -y 4 0x50 b
> 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
> 00: 80 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 ??????@.?uT.??.?
> 10: 8f 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 ?????.??`..???,
> 20: 15 08 15 08 00 00 00 00 00 00 00 00 00 00 00 00 ????............
> 30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 b9 ..............??
> 40: 7f da 00 00 00 00 00 00 53 53 50 31 33 33 2d 30 ??......SSP133-0
> 50: 36 34 33 32 33 4a 00 00 00 00 00 00 00 01 02 00 64323J.......??.
> 60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
> 70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 64 cd ..............d?
> 80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> 90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
> f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
>
> linux-w6i1:/home/jdelvare/src/i2c-i801 # i2cdump -y 4 0x50 i
> 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
> 00: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> 10: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> 20: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> 30: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> 40: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> 50: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> 60: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> 70: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> 80: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> 90: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> a0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> b0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> c0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> d0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
> e0: 08 04 0d 09 02 40 00 01 75 54 00 82 10 00 01 8f ?????@.?uT.??.??
> f0: 04 06 01 01 00 0e a0 60 00 00 14 0f 14 2c 20 15 ????.??`..???, ?
>
> As you can see, the requested offset of the I2C block read (0x00, then
> 0x20, then 0x40 etc.) is being ignored, and instead every I2C block
> read starts at EEPROM offset 0x01.
>
> If you compare the datasheets of the ICH5 (Intel doc 252516-001) and
> ICH3-M (Intel doc 290716-001), section "I2C Read", you will see that
> the description of the command is different. The format described for
> the ICH5 (table 114 in the datasheet) does match what the Linux i2c
> stack calls an I2C block read, while the format described for the
> ICH3-M (table 5-87 in the datasheet) does NOT. Apparently the original
> implementation was aimed at specific devices using 10-bit I2C
> addressing. As no other SMBus host device implemented that, we did not
> even allocate an SMBus command constant to it (and the fact that Intel
> changed the format in later hardware iterations proves that this was the
> right thing to do).
>
> Looking at the ICH3-M implementation of the "I2C Block Read" transfer,
> I feel very lucky that I did not trash my memory SPD EEPROM while
> running the command. Because the format really starts with a WRITE of 2
> bytes to the EEPROM before reading from it.
>
> So at least the I2C block read part of the patch is never going to
> work. The interrupt part could work if we change the test as described
> above, however I would question the relevance of making that change
> considering that it is no longer the obvious clean-up you originally
> proposed, and would then impact recent hardware too. I would hate to
> introduce a regression for the sole purpose of enabling interrupts on
> 20-year old hardware which I doubt many people are still using.
>
> I am available to perform any test you want me to on this old laptop.
> As long as it runs...
>
Thanks for testing! So I'll drop this patch from the series.
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 5/8] i2c: i801: add helper i801_set_hstadd()
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
` (3 preceding siblings ...)
2022-04-15 16:56 ` [PATCH 4/8] i2c: i801: enable FEATURE_IRQ and FEATURE_I2C_BLOCK_READ on all chip versions Heiner Kallweit
@ 2022-04-15 16:57 ` Heiner Kallweit
2022-06-09 13:53 ` Jean Delvare
2022-04-15 16:58 ` [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction() Heiner Kallweit
` (2 subsequent siblings)
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:57 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
Factor out setting SMBHSTADD to a helper. The current code makes the
assumption that constant I2C_SMBUS_READ has bit 0 set, that's not ideal.
Therefore let the new helper explicitly check for I2C_SMBUS_READ.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 41 ++++++++++++++++++-----------------
1 file changed, 21 insertions(+), 20 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index a9737f14d..bf77f8640 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -771,6 +771,14 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
return result;
}
+static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
+{
+ addr <<= 1;
+ if (read_write == I2C_SMBUS_READ)
+ addr |= 0x01;
+ outb_p(addr, SMBHSTADD(priv));
+}
+
/* Return negative errno on error. */
static s32 i801_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
@@ -795,28 +803,24 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
switch (size) {
case I2C_SMBUS_QUICK:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, read_write);
xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, read_write);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD(priv));
xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, read_write);
outb_p(command, SMBHSTCMD(priv));
if (read_write == I2C_SMBUS_WRITE)
outb_p(data->byte, SMBHSTDAT0(priv));
xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, read_write);
outb_p(command, SMBHSTCMD(priv));
if (read_write == I2C_SMBUS_WRITE) {
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
@@ -825,7 +829,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
xact = I801_WORD_DATA;
break;
case I2C_SMBUS_PROC_CALL:
- outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
outb_p(command, SMBHSTCMD(priv));
outb_p(data->word & 0xff, SMBHSTDAT0(priv));
outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
@@ -833,8 +837,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_DATA:
- outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
- SMBHSTADD(priv));
+ i801_set_hstadd(priv, addr, read_write);
outb_p(command, SMBHSTCMD(priv));
block = 1;
break;
@@ -845,10 +848,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
* However if SPD Write Disable is set (Lynx Point and later),
* the read will fail if we don't set the R/#W bit.
*/
- outb_p(((addr & 0x7f) << 1) |
- ((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
- (read_write & 0x01) : 0),
- SMBHSTADD(priv));
+ if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
+ i801_set_hstadd(priv, addr, read_write);
+ else
+ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
+
if (read_write == I2C_SMBUS_READ) {
/* NB: page 240 of ICH5 datasheet also shows
* that DATA1 is the cmd field when reading */
@@ -858,11 +862,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
block = 1;
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
- /*
- * Bit 0 of the slave address register always indicate a write
- * command.
- */
- outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
+ /* Needs to be flagged as write transaction */
+ i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
outb_p(command, SMBHSTCMD(priv));
block = 1;
break;
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 5/8] i2c: i801: add helper i801_set_hstadd()
2022-04-15 16:57 ` [PATCH 5/8] i2c: i801: add helper i801_set_hstadd() Heiner Kallweit
@ 2022-06-09 13:53 ` Jean Delvare
2022-12-16 21:37 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-09 13:53 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:57:21 +0200, Heiner Kallweit wrote:
> Factor out setting SMBHSTADD to a helper. The current code makes the
> assumption that constant I2C_SMBUS_READ has bit 0 set, that's not ideal.
This isn't an "assumption". The values of I2C_SMBUS_WRITE and
I2C_SMBUS_READ were chosen to match the bit position and values in the
I2C protocol. Maybe it should have been made clearer by defining them
as hexadecimal values instead of decimal values. Nevertheless, I find
it unfortunate to not use this fact to optimize the code, see below.
> Therefore let the new helper explicitly check for I2C_SMBUS_READ.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 41 ++++++++++++++++++-----------------
> 1 file changed, 21 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index a9737f14d..bf77f8640 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -771,6 +771,14 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
> return result;
> }
>
> +static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
> +{
> + addr <<= 1;
> + if (read_write == I2C_SMBUS_READ)
> + addr |= 0x01;
> + outb_p(addr, SMBHSTADD(priv));
This can be written:
outb_p((addr << 1) | read_write, SMBHSTADD(priv));
Net result -48 bytes of (x86_64) binary code. That's basically what the
original code was doing, minus the useless masking.
> +}
> +
> /* Return negative errno on error. */
> static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> unsigned short flags, char read_write, u8 command,
> @@ -795,28 +803,24 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>
> switch (size) {
> case I2C_SMBUS_QUICK:
> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
> - SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, read_write);
> xact = I801_QUICK;
> break;
> case I2C_SMBUS_BYTE:
> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
> - SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, read_write);
> if (read_write == I2C_SMBUS_WRITE)
> outb_p(command, SMBHSTCMD(priv));
> xact = I801_BYTE;
> break;
> case I2C_SMBUS_BYTE_DATA:
> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
> - SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, read_write);
> outb_p(command, SMBHSTCMD(priv));
> if (read_write == I2C_SMBUS_WRITE)
> outb_p(data->byte, SMBHSTDAT0(priv));
> xact = I801_BYTE_DATA;
> break;
> case I2C_SMBUS_WORD_DATA:
> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
> - SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, read_write);
> outb_p(command, SMBHSTCMD(priv));
> if (read_write == I2C_SMBUS_WRITE) {
> outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> @@ -825,7 +829,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> xact = I801_WORD_DATA;
> break;
> case I2C_SMBUS_PROC_CALL:
> - outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
> outb_p(command, SMBHSTCMD(priv));
> outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
> @@ -833,8 +837,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> read_write = I2C_SMBUS_READ;
> break;
> case I2C_SMBUS_BLOCK_DATA:
> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
> - SMBHSTADD(priv));
> + i801_set_hstadd(priv, addr, read_write);
> outb_p(command, SMBHSTCMD(priv));
> block = 1;
> break;
> @@ -845,10 +848,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> * However if SPD Write Disable is set (Lynx Point and later),
> * the read will fail if we don't set the R/#W bit.
> */
> - outb_p(((addr & 0x7f) << 1) |
> - ((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
> - (read_write & 0x01) : 0),
> - SMBHSTADD(priv));
> + if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
> + i801_set_hstadd(priv, addr, read_write);
> + else
> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
Preserving the use of the ternary operator makes the generated binary
smaller once again:
i801_set_hstadd(priv, addr,
(priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
read_write : I2C_SMBUS_WRITE);
Net result -11 bytes of (x86_64) binary code.
> +
> if (read_write == I2C_SMBUS_READ) {
> /* NB: page 240 of ICH5 datasheet also shows
> * that DATA1 is the cmd field when reading */
> @@ -858,11 +862,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> block = 1;
> break;
> case I2C_SMBUS_BLOCK_PROC_CALL:
> - /*
> - * Bit 0 of the slave address register always indicate a write
> - * command.
> - */
> - outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
> + /* Needs to be flagged as write transaction */
> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
> outb_p(command, SMBHSTCMD(priv));
> block = 1;
> break;
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 5/8] i2c: i801: add helper i801_set_hstadd()
2022-06-09 13:53 ` Jean Delvare
@ 2022-12-16 21:37 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-16 21:37 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 09.06.2022 15:53, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:57:21 +0200, Heiner Kallweit wrote:
>> Factor out setting SMBHSTADD to a helper. The current code makes the
>> assumption that constant I2C_SMBUS_READ has bit 0 set, that's not ideal.
>
> This isn't an "assumption". The values of I2C_SMBUS_WRITE and
> I2C_SMBUS_READ were chosen to match the bit position and values in the
> I2C protocol. Maybe it should have been made clearer by defining them
> as hexadecimal values instead of decimal values. Nevertheless, I find
> it unfortunate to not use this fact to optimize the code, see below.
>
>> Therefore let the new helper explicitly check for I2C_SMBUS_READ.
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 41 ++++++++++++++++++-----------------
>> 1 file changed, 21 insertions(+), 20 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index a9737f14d..bf77f8640 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -771,6 +771,14 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
>> return result;
>> }
>>
>> +static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
>> +{
>> + addr <<= 1;
>> + if (read_write == I2C_SMBUS_READ)
>> + addr |= 0x01;
>> + outb_p(addr, SMBHSTADD(priv));
>
> This can be written:
>
> outb_p((addr << 1) | read_write, SMBHSTADD(priv));
>
> Net result -48 bytes of (x86_64) binary code. That's basically what the
> original code was doing, minus the useless masking.
>
OK
>> +}
>> +
>> /* Return negative errno on error. */
>> static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> unsigned short flags, char read_write, u8 command,
>> @@ -795,28 +803,24 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>>
>> switch (size) {
>> case I2C_SMBUS_QUICK:
>> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
>> - SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, read_write);
>> xact = I801_QUICK;
>> break;
>> case I2C_SMBUS_BYTE:
>> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
>> - SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, read_write);
>> if (read_write == I2C_SMBUS_WRITE)
>> outb_p(command, SMBHSTCMD(priv));
>> xact = I801_BYTE;
>> break;
>> case I2C_SMBUS_BYTE_DATA:
>> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
>> - SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, read_write);
>> outb_p(command, SMBHSTCMD(priv));
>> if (read_write == I2C_SMBUS_WRITE)
>> outb_p(data->byte, SMBHSTDAT0(priv));
>> xact = I801_BYTE_DATA;
>> break;
>> case I2C_SMBUS_WORD_DATA:
>> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
>> - SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, read_write);
>> outb_p(command, SMBHSTCMD(priv));
>> if (read_write == I2C_SMBUS_WRITE) {
>> outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> @@ -825,7 +829,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> xact = I801_WORD_DATA;
>> break;
>> case I2C_SMBUS_PROC_CALL:
>> - outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
>> outb_p(command, SMBHSTCMD(priv));
>> outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
>> @@ -833,8 +837,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> read_write = I2C_SMBUS_READ;
>> break;
>> case I2C_SMBUS_BLOCK_DATA:
>> - outb_p(((addr & 0x7f) << 1) | (read_write & 0x01),
>> - SMBHSTADD(priv));
>> + i801_set_hstadd(priv, addr, read_write);
>> outb_p(command, SMBHSTCMD(priv));
>> block = 1;
>> break;
>> @@ -845,10 +848,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> * However if SPD Write Disable is set (Lynx Point and later),
>> * the read will fail if we don't set the R/#W bit.
>> */
>> - outb_p(((addr & 0x7f) << 1) |
>> - ((priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
>> - (read_write & 0x01) : 0),
>> - SMBHSTADD(priv));
>> + if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
>> + i801_set_hstadd(priv, addr, read_write);
>> + else
>> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
>
> Preserving the use of the ternary operator makes the generated binary
> smaller once again:
>
> i801_set_hstadd(priv, addr,
> (priv->original_hstcfg & SMBHSTCFG_SPD_WD) ?
> read_write : I2C_SMBUS_WRITE);
>
> Net result -11 bytes of (x86_64) binary code.
>
OK
>> +
>> if (read_write == I2C_SMBUS_READ) {
>> /* NB: page 240 of ICH5 datasheet also shows
>> * that DATA1 is the cmd field when reading */
>> @@ -858,11 +862,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> block = 1;
>> break;
>> case I2C_SMBUS_BLOCK_PROC_CALL:
>> - /*
>> - * Bit 0 of the slave address register always indicate a write
>> - * command.
>> - */
>> - outb_p((addr & 0x7f) << 1, SMBHSTADD(priv));
>> + /* Needs to be flagged as write transaction */
>> + i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
>> outb_p(command, SMBHSTCMD(priv));
>> block = 1;
>> break;
>
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction()
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
` (4 preceding siblings ...)
2022-04-15 16:57 ` [PATCH 5/8] i2c: i801: add helper i801_set_hstadd() Heiner Kallweit
@ 2022-04-15 16:58 ` Heiner Kallweit
2022-06-10 11:03 ` Jean Delvare
2022-04-15 16:58 ` [PATCH 7/8] i2c: i801: call i801_check_pre() from i801_access() Heiner Kallweit
2022-04-15 16:59 ` [PATCH 8/8] i2c: i801: call i801_check_post() " Heiner Kallweit
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:58 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
This patch factors out non-block pre/post processing to a new function
i801_single_transaction(), complementing existing function
i801_block_transaction(). This makes i801_access() better readable.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 95 +++++++++++++++++++++--------------
1 file changed, 58 insertions(+), 37 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index bf77f8640..8c2245f38 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -771,6 +771,62 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
return result;
}
+/* Single transaction function */
+static int i801_single_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ char read_write, int command)
+{
+ int xact, ret;
+
+ switch (command) {
+ case I2C_SMBUS_QUICK:
+ xact = I801_QUICK;
+ break;
+ case I2C_SMBUS_BYTE:
+ xact = I801_BYTE;
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ if (read_write == I2C_SMBUS_WRITE)
+ outb_p(data->byte, SMBHSTDAT0(priv));
+ xact = I801_BYTE_DATA;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ if (read_write == I2C_SMBUS_WRITE) {
+ outb_p(data->word & 0xff, SMBHSTDAT0(priv));
+ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
+ }
+ xact = I801_WORD_DATA;
+ break;
+ case I2C_SMBUS_PROC_CALL:
+ outb_p(data->word & 0xff, SMBHSTDAT0(priv));
+ outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
+ xact = I801_PROC_CALL;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = i801_transaction(priv, xact);
+
+ if (ret || read_write == I2C_SMBUS_WRITE)
+ return ret;
+
+ switch (command) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+ data->byte = inb_p(SMBHSTDAT0(priv));
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ case I2C_SMBUS_PROC_CALL:
+ data->word = inb_p(SMBHSTDAT0(priv)) +
+ (inb_p(SMBHSTDAT1(priv)) << 8);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
{
addr <<= 1;
@@ -784,9 +840,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write, u8 command,
int size, union i2c_smbus_data *data)
{
- int hwpec;
- int block = 0;
- int ret, xact;
+ int hwpec, ret, block = 0;
struct i801_priv *priv = i2c_get_adapdata(adap);
mutex_lock(&priv->acpi_lock);
@@ -804,36 +858,23 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
switch (size) {
case I2C_SMBUS_QUICK:
i801_set_hstadd(priv, addr, read_write);
- xact = I801_QUICK;
break;
case I2C_SMBUS_BYTE:
i801_set_hstadd(priv, addr, read_write);
if (read_write == I2C_SMBUS_WRITE)
outb_p(command, SMBHSTCMD(priv));
- xact = I801_BYTE;
break;
case I2C_SMBUS_BYTE_DATA:
i801_set_hstadd(priv, addr, read_write);
outb_p(command, SMBHSTCMD(priv));
- if (read_write == I2C_SMBUS_WRITE)
- outb_p(data->byte, SMBHSTDAT0(priv));
- xact = I801_BYTE_DATA;
break;
case I2C_SMBUS_WORD_DATA:
i801_set_hstadd(priv, addr, read_write);
outb_p(command, SMBHSTCMD(priv));
- if (read_write == I2C_SMBUS_WRITE) {
- outb_p(data->word & 0xff, SMBHSTDAT0(priv));
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
- }
- xact = I801_WORD_DATA;
break;
case I2C_SMBUS_PROC_CALL:
i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
outb_p(command, SMBHSTCMD(priv));
- outb_p(data->word & 0xff, SMBHSTDAT0(priv));
- outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
- xact = I801_PROC_CALL;
read_write = I2C_SMBUS_READ;
break;
case I2C_SMBUS_BLOCK_DATA:
@@ -883,7 +924,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
if (block)
ret = i801_block_transaction(priv, data, read_write, size);
else
- ret = i801_transaction(priv, xact);
+ ret = i801_single_transaction(priv, data, read_write, size);
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. Turn off
@@ -891,26 +932,6 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
if (hwpec || block)
outb_p(inb_p(SMBAUXCTL(priv)) &
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
-
- if (block)
- goto out;
- if (ret)
- goto out;
- if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
- goto out;
-
- switch (xact) {
- case I801_BYTE: /* Result put in SMBHSTDAT0 */
- case I801_BYTE_DATA:
- data->byte = inb_p(SMBHSTDAT0(priv));
- break;
- case I801_WORD_DATA:
- case I801_PROC_CALL:
- data->word = inb_p(SMBHSTDAT0(priv)) +
- (inb_p(SMBHSTDAT1(priv)) << 8);
- break;
- }
-
out:
/*
* Unlock the SMBus device for use by BIOS/ACPI,
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction()
2022-04-15 16:58 ` [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction() Heiner Kallweit
@ 2022-06-10 11:03 ` Jean Delvare
2022-12-17 17:07 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-10 11:03 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:58:03 +0200, Heiner Kallweit wrote:
> This patch factors out non-block pre/post processing to a new function
> i801_single_transaction(), complementing existing function
> i801_block_transaction(). This makes i801_access() better readable.
I like the idea, but I have objections about some implementation
details, see below.
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 95 +++++++++++++++++++++--------------
> 1 file changed, 58 insertions(+), 37 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index bf77f8640..8c2245f38 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -771,6 +771,62 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
> return result;
> }
>
> +/* Single transaction function */
The term "single transaction" is a bit misleading. Block transactions
are also single transactions, in the sense that there's one start
condition at the beginning and one stop condition at the end. I'd
rather call non-block transactions "single value transactions" or
"simple transactions".
> +static int i801_single_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
> + char read_write, int command)
> +{
> + int xact, ret;
> +
> + switch (command) {
> + case I2C_SMBUS_QUICK:
> + xact = I801_QUICK;
> + break;
> + case I2C_SMBUS_BYTE:
> + xact = I801_BYTE;
> + break;
Previous 2 lines are indented with spaces instead of tabs.
> + case I2C_SMBUS_BYTE_DATA:
> + if (read_write == I2C_SMBUS_WRITE)
> + outb_p(data->byte, SMBHSTDAT0(priv));
> + xact = I801_BYTE_DATA;
> + break;
> + case I2C_SMBUS_WORD_DATA:
> + if (read_write == I2C_SMBUS_WRITE) {
> + outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
> + }
> + xact = I801_WORD_DATA;
> + break;
> + case I2C_SMBUS_PROC_CALL:
> + outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
> + xact = I801_PROC_CALL;
> + break;
> + default:
> + return -EOPNOTSUPP;
That's never going to happen.
Generally speaking, I'm worried about having the same switch/case
construct here that we already have in i801_access. Looks to me like we
are doing half of the work here and the other half there and I fail to
see the rationale for splitting the work like that. I mean, I see how
it solves the asymmetry between the block and non-block code paths, but
the result doesn't look appealing. From a performance perspective it's
questionable too.
What prevents us from doing all the work on either side? Maybe we
should move more code into i801_single_transaction (possibly in a
subsequent patch)?
> + }
> +
> + ret = i801_transaction(priv, xact);
> +
Traditionally no blank line here.
> + if (ret || read_write == I2C_SMBUS_WRITE)
> + return ret;
> +
> + switch (command) {
> + case I2C_SMBUS_BYTE:
> + case I2C_SMBUS_BYTE_DATA:
> + data->byte = inb_p(SMBHSTDAT0(priv));
> + break;
> + case I2C_SMBUS_WORD_DATA:
> + case I2C_SMBUS_PROC_CALL:
> + data->word = inb_p(SMBHSTDAT0(priv)) +
> + (inb_p(SMBHSTDAT1(priv)) << 8);
> + break;
> + default:
> + break;
Default case is not needed.
> + }
> +
> + return 0;
> +}
> +
> static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
> {
> addr <<= 1;
> @@ -784,9 +840,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> unsigned short flags, char read_write, u8 command,
> int size, union i2c_smbus_data *data)
> {
> - int hwpec;
> - int block = 0;
> - int ret, xact;
> + int hwpec, ret, block = 0;
> struct i801_priv *priv = i2c_get_adapdata(adap);
>
> mutex_lock(&priv->acpi_lock);
> @@ -804,36 +858,23 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> switch (size) {
> case I2C_SMBUS_QUICK:
> i801_set_hstadd(priv, addr, read_write);
> - xact = I801_QUICK;
> break;
> case I2C_SMBUS_BYTE:
> i801_set_hstadd(priv, addr, read_write);
> if (read_write == I2C_SMBUS_WRITE)
> outb_p(command, SMBHSTCMD(priv));
> - xact = I801_BYTE;
> break;
> case I2C_SMBUS_BYTE_DATA:
> i801_set_hstadd(priv, addr, read_write);
> outb_p(command, SMBHSTCMD(priv));
> - if (read_write == I2C_SMBUS_WRITE)
> - outb_p(data->byte, SMBHSTDAT0(priv));
> - xact = I801_BYTE_DATA;
> break;
> case I2C_SMBUS_WORD_DATA:
> i801_set_hstadd(priv, addr, read_write);
> outb_p(command, SMBHSTCMD(priv));
> - if (read_write == I2C_SMBUS_WRITE) {
> - outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
> - }
> - xact = I801_WORD_DATA;
> break;
> case I2C_SMBUS_PROC_CALL:
> i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
> outb_p(command, SMBHSTCMD(priv));
> - outb_p(data->word & 0xff, SMBHSTDAT0(priv));
> - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
> - xact = I801_PROC_CALL;
> read_write = I2C_SMBUS_READ;
> break;
> case I2C_SMBUS_BLOCK_DATA:
> @@ -883,7 +924,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> if (block)
> ret = i801_block_transaction(priv, data, read_write, size);
> else
> - ret = i801_transaction(priv, xact);
> + ret = i801_single_transaction(priv, data, read_write, size);
>
> /* Some BIOSes don't like it when PEC is enabled at reboot or resume
> time, so we forcibly disable it after every transaction. Turn off
> @@ -891,26 +932,6 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
> if (hwpec || block)
> outb_p(inb_p(SMBAUXCTL(priv)) &
> ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
> -
> - if (block)
> - goto out;
> - if (ret)
> - goto out;
> - if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
> - goto out;
> -
> - switch (xact) {
> - case I801_BYTE: /* Result put in SMBHSTDAT0 */
> - case I801_BYTE_DATA:
> - data->byte = inb_p(SMBHSTDAT0(priv));
> - break;
> - case I801_WORD_DATA:
> - case I801_PROC_CALL:
> - data->word = inb_p(SMBHSTDAT0(priv)) +
> - (inb_p(SMBHSTDAT1(priv)) << 8);
> - break;
> - }
> -
> out:
> /*
> * Unlock the SMBus device for use by BIOS/ACPI,
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction()
2022-06-10 11:03 ` Jean Delvare
@ 2022-12-17 17:07 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-17 17:07 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 10.06.2022 13:03, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:58:03 +0200, Heiner Kallweit wrote:
>> This patch factors out non-block pre/post processing to a new function
>> i801_single_transaction(), complementing existing function
>> i801_block_transaction(). This makes i801_access() better readable.
>
> I like the idea, but I have objections about some implementation
> details, see below.
>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 95 +++++++++++++++++++++--------------
>> 1 file changed, 58 insertions(+), 37 deletions(-)
>>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index bf77f8640..8c2245f38 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -771,6 +771,62 @@ static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *
>> return result;
>> }
>>
>> +/* Single transaction function */
>
> The term "single transaction" is a bit misleading. Block transactions
> are also single transactions, in the sense that there's one start
> condition at the beginning and one stop condition at the end. I'd
> rather call non-block transactions "single value transactions" or
> "simple transactions".
>
OK
>> +static int i801_single_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
>> + char read_write, int command)
>> +{
>> + int xact, ret;
>> +
>> + switch (command) {
>> + case I2C_SMBUS_QUICK:
>> + xact = I801_QUICK;
>> + break;
>> + case I2C_SMBUS_BYTE:
>> + xact = I801_BYTE;
>> + break;
>
> Previous 2 lines are indented with spaces instead of tabs.
>
OK
>> + case I2C_SMBUS_BYTE_DATA:
>> + if (read_write == I2C_SMBUS_WRITE)
>> + outb_p(data->byte, SMBHSTDAT0(priv));
>> + xact = I801_BYTE_DATA;
>> + break;
>> + case I2C_SMBUS_WORD_DATA:
>> + if (read_write == I2C_SMBUS_WRITE) {
>> + outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
>> + }
>> + xact = I801_WORD_DATA;
>> + break;
>> + case I2C_SMBUS_PROC_CALL:
>> + outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
>> + xact = I801_PROC_CALL;
>> + break;
>> + default:
>> + return -EOPNOTSUPP;
>
> That's never going to happen.
>
> Generally speaking, I'm worried about having the same switch/case
> construct here that we already have in i801_access. Looks to me like we
> are doing half of the work here and the other half there and I fail to
> see the rationale for splitting the work like that. I mean, I see how
> it solves the asymmetry between the block and non-block code paths, but
> the result doesn't look appealing. From a performance perspective it's
> questionable too.
>
> What prevents us from doing all the work on either side? Maybe we
> should move more code into i801_single_transaction (possibly in a
> subsequent patch)?
>
Makes sense. Ill add this in v2.
>> + }
>> +
>> + ret = i801_transaction(priv, xact);
>> +
>
> Traditionally no blank line here.
>
OK
>> + if (ret || read_write == I2C_SMBUS_WRITE)
>> + return ret;
>> +
>> + switch (command) {
>> + case I2C_SMBUS_BYTE:
>> + case I2C_SMBUS_BYTE_DATA:
>> + data->byte = inb_p(SMBHSTDAT0(priv));
>> + break;
>> + case I2C_SMBUS_WORD_DATA:
>> + case I2C_SMBUS_PROC_CALL:
>> + data->word = inb_p(SMBHSTDAT0(priv)) +
>> + (inb_p(SMBHSTDAT1(priv)) << 8);
>> + break;
>> + default:
>> + break;
>
> Default case is not needed.
>
OK
>> + }
>> +
>> + return 0;
>> +}
>> +
>> static void i801_set_hstadd(struct i801_priv *priv, u8 addr, char read_write)
>> {
>> addr <<= 1;
>> @@ -784,9 +840,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> unsigned short flags, char read_write, u8 command,
>> int size, union i2c_smbus_data *data)
>> {
>> - int hwpec;
>> - int block = 0;
>> - int ret, xact;
>> + int hwpec, ret, block = 0;
>> struct i801_priv *priv = i2c_get_adapdata(adap);
>>
>> mutex_lock(&priv->acpi_lock);
>> @@ -804,36 +858,23 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> switch (size) {
>> case I2C_SMBUS_QUICK:
>> i801_set_hstadd(priv, addr, read_write);
>> - xact = I801_QUICK;
>> break;
>> case I2C_SMBUS_BYTE:
>> i801_set_hstadd(priv, addr, read_write);
>> if (read_write == I2C_SMBUS_WRITE)
>> outb_p(command, SMBHSTCMD(priv));
>> - xact = I801_BYTE;
>> break;
>> case I2C_SMBUS_BYTE_DATA:
>> i801_set_hstadd(priv, addr, read_write);
>> outb_p(command, SMBHSTCMD(priv));
>> - if (read_write == I2C_SMBUS_WRITE)
>> - outb_p(data->byte, SMBHSTDAT0(priv));
>> - xact = I801_BYTE_DATA;
>> break;
>> case I2C_SMBUS_WORD_DATA:
>> i801_set_hstadd(priv, addr, read_write);
>> outb_p(command, SMBHSTCMD(priv));
>> - if (read_write == I2C_SMBUS_WRITE) {
>> - outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
>> - }
>> - xact = I801_WORD_DATA;
>> break;
>> case I2C_SMBUS_PROC_CALL:
>> i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
>> outb_p(command, SMBHSTCMD(priv));
>> - outb_p(data->word & 0xff, SMBHSTDAT0(priv));
>> - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1(priv));
>> - xact = I801_PROC_CALL;
>> read_write = I2C_SMBUS_READ;
>> break;
>> case I2C_SMBUS_BLOCK_DATA:
>> @@ -883,7 +924,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> if (block)
>> ret = i801_block_transaction(priv, data, read_write, size);
>> else
>> - ret = i801_transaction(priv, xact);
>> + ret = i801_single_transaction(priv, data, read_write, size);
>>
>> /* Some BIOSes don't like it when PEC is enabled at reboot or resume
>> time, so we forcibly disable it after every transaction. Turn off
>> @@ -891,26 +932,6 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>> if (hwpec || block)
>> outb_p(inb_p(SMBAUXCTL(priv)) &
>> ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
>> -
>> - if (block)
>> - goto out;
>> - if (ret)
>> - goto out;
>> - if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
>> - goto out;
>> -
>> - switch (xact) {
>> - case I801_BYTE: /* Result put in SMBHSTDAT0 */
>> - case I801_BYTE_DATA:
>> - data->byte = inb_p(SMBHSTDAT0(priv));
>> - break;
>> - case I801_WORD_DATA:
>> - case I801_PROC_CALL:
>> - data->word = inb_p(SMBHSTDAT0(priv)) +
>> - (inb_p(SMBHSTDAT1(priv)) << 8);
>> - break;
>> - }
>> -
>> out:
>> /*
>> * Unlock the SMBus device for use by BIOS/ACPI,
>
>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 7/8] i2c: i801: call i801_check_pre() from i801_access()
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
` (5 preceding siblings ...)
2022-04-15 16:58 ` [PATCH 6/8] i2c: i801: add i801_single_transaction(), complementing i801_block_transaction() Heiner Kallweit
@ 2022-04-15 16:58 ` Heiner Kallweit
2022-06-10 13:52 ` Jean Delvare
2022-04-15 16:59 ` [PATCH 8/8] i2c: i801: call i801_check_post() " Heiner Kallweit
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:58 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
This avoids code duplication, in a next step we'll call
i801_check_post() from i801_access() as well.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 8c2245f38..9061333f2 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -460,10 +460,6 @@ static int i801_transaction(struct i801_priv *priv, int xact)
unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
- status = i801_check_pre(priv);
- if (status < 0)
- return status;
-
if (priv->features & FEATURE_IRQ) {
reinit_completion(&priv->done);
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
@@ -647,10 +643,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
if (command == I2C_SMBUS_BLOCK_PROC_CALL)
return -EOPNOTSUPP;
- status = i801_check_pre(priv);
- if (status < 0)
- return status;
-
len = data->block[0];
if (read_write == I2C_SMBUS_WRITE) {
@@ -851,6 +843,10 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
pm_runtime_get_sync(&priv->pci_dev->dev);
+ ret = i801_check_pre(priv);
+ if (ret)
+ goto out;
+
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
&& size != I2C_SMBUS_QUICK
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 7/8] i2c: i801: call i801_check_pre() from i801_access()
2022-04-15 16:58 ` [PATCH 7/8] i2c: i801: call i801_check_pre() from i801_access() Heiner Kallweit
@ 2022-06-10 13:52 ` Jean Delvare
0 siblings, 0 replies; 26+ messages in thread
From: Jean Delvare @ 2022-06-10 13:52 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:58:40 +0200, Heiner Kallweit wrote:
> This avoids code duplication, in a next step we'll call
> i801_check_post() from i801_access() as well.
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 12 ++++--------
> 1 file changed, 4 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index 8c2245f38..9061333f2 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -460,10 +460,6 @@ static int i801_transaction(struct i801_priv *priv, int xact)
> unsigned long result;
> const struct i2c_adapter *adap = &priv->adapter;
>
> - status = i801_check_pre(priv);
> - if (status < 0)
> - return status;
> -
> if (priv->features & FEATURE_IRQ) {
> reinit_completion(&priv->done);
> outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
> @@ -647,10 +643,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
> if (command == I2C_SMBUS_BLOCK_PROC_CALL)
> return -EOPNOTSUPP;
>
> - status = i801_check_pre(priv);
> - if (status < 0)
> - return status;
> -
> len = data->block[0];
>
> if (read_write == I2C_SMBUS_WRITE) {
> @@ -851,6 +843,10 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
>
> pm_runtime_get_sync(&priv->pci_dev->dev);
>
> + ret = i801_check_pre(priv);
> + if (ret)
> + goto out;
> +
> hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
> && size != I2C_SMBUS_QUICK
> && size != I2C_SMBUS_I2C_BLOCK_DATA;
Makes sense, thanks for the clean-up.
Reviewed-by: Jean Delvare <jdelvare@suse.de>
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH 8/8] i2c: i801: call i801_check_post() from i801_access()
2022-04-15 16:53 [PATCH 0/8] i2c: i801: Series with minor improvements Heiner Kallweit
` (6 preceding siblings ...)
2022-04-15 16:58 ` [PATCH 7/8] i2c: i801: call i801_check_pre() from i801_access() Heiner Kallweit
@ 2022-04-15 16:59 ` Heiner Kallweit
2022-06-10 14:31 ` Jean Delvare
7 siblings, 1 reply; 26+ messages in thread
From: Heiner Kallweit @ 2022-04-15 16:59 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
Avoid code duplication by calling i801_check_post() from i801_access().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 9061333f2..ecec7a3a8 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -432,7 +432,7 @@ static int i801_wait_intr(struct i801_priv *priv)
busy = status & SMBHSTSTS_HOST_BUSY;
status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
if (!busy && status)
- return status;
+ return status & STATUS_ERROR_FLAGS;
} while (time_is_after_eq_jiffies(timeout));
return -ETIMEDOUT;
@@ -456,7 +456,6 @@ static int i801_wait_byte_done(struct i801_priv *priv)
static int i801_transaction(struct i801_priv *priv, int xact)
{
- int status;
unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
@@ -465,13 +464,12 @@ static int i801_transaction(struct i801_priv *priv, int xact)
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
SMBHSTCNT(priv));
result = wait_for_completion_timeout(&priv->done, adap->timeout);
- return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
+ return result ? priv->status : -ETIMEDOUT;
}
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
- status = i801_wait_intr(priv);
- return i801_check_post(priv, status);
+ return i801_wait_intr(priv);
}
static int i801_block_transaction_by_block(struct i801_priv *priv,
@@ -618,7 +616,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
if (status) {
- priv->status = status;
+ priv->status = status & STATUS_ERROR_FLAGS;
complete(&priv->done);
}
@@ -668,7 +666,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
reinit_completion(&priv->done);
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
result = wait_for_completion_timeout(&priv->done, adap->timeout);
- return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
+ return result ? priv->status : -ETIMEDOUT;
}
for (i = 1; i <= len; i++) {
@@ -682,7 +680,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
status = i801_wait_byte_done(priv);
if (status)
- goto exit;
+ return status;
if (i == 1 && read_write == I2C_SMBUS_READ
&& command != I2C_SMBUS_I2C_BLOCK_DATA) {
@@ -712,9 +710,7 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv));
}
- status = i801_wait_intr(priv);
-exit:
- return i801_check_post(priv, status);
+ return i801_wait_intr(priv);
}
/* Block transaction function */
@@ -922,6 +918,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
else
ret = i801_single_transaction(priv, data, read_write, size);
+ ret = i801_check_post(priv, ret);
+
/* Some BIOSes don't like it when PEC is enabled at reboot or resume
time, so we forcibly disable it after every transaction. Turn off
E32B for the same reason. */
--
2.35.3
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH 8/8] i2c: i801: call i801_check_post() from i801_access()
2022-04-15 16:59 ` [PATCH 8/8] i2c: i801: call i801_check_post() " Heiner Kallweit
@ 2022-06-10 14:31 ` Jean Delvare
2022-12-17 17:21 ` Heiner Kallweit
0 siblings, 1 reply; 26+ messages in thread
From: Jean Delvare @ 2022-06-10 14:31 UTC (permalink / raw)
To: Heiner Kallweit; +Cc: linux-i2c
Hi Heiner,
On Fri, 15 Apr 2022 18:59:46 +0200, Heiner Kallweit wrote:
> Avoid code duplication by calling i801_check_post() from i801_access().
>
> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
> ---
> drivers/i2c/busses/i2c-i801.c | 20 +++++++++-----------
> 1 file changed, 9 insertions(+), 11 deletions(-)
Overall I like the idea. I only have one question to make sure I'm not
missing something.
> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
> index 9061333f2..ecec7a3a8 100644
> --- a/drivers/i2c/busses/i2c-i801.c
> +++ b/drivers/i2c/busses/i2c-i801.c
> @@ -432,7 +432,7 @@ static int i801_wait_intr(struct i801_priv *priv)
> busy = status & SMBHSTSTS_HOST_BUSY;
> status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
> if (!busy && status)
> - return status;
> + return status & STATUS_ERROR_FLAGS;
> } while (time_is_after_eq_jiffies(timeout));
Do I understand correctly that this change isn't really related to the
rest of the patch, and could have been done independently?
You are filtering out SMBHSTSTS_INTR simply because i801_check_post()
will never check it anyway, right? If so, I wonder if that's really
something we want to do, as ultimately this adds code with no
functional benefit just to be "cleaner". But please correct me if I'm
wrong.
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH 8/8] i2c: i801: call i801_check_post() from i801_access()
2022-06-10 14:31 ` Jean Delvare
@ 2022-12-17 17:21 ` Heiner Kallweit
0 siblings, 0 replies; 26+ messages in thread
From: Heiner Kallweit @ 2022-12-17 17:21 UTC (permalink / raw)
To: Jean Delvare; +Cc: linux-i2c
On 10.06.2022 16:31, Jean Delvare wrote:
> Hi Heiner,
>
> On Fri, 15 Apr 2022 18:59:46 +0200, Heiner Kallweit wrote:
>> Avoid code duplication by calling i801_check_post() from i801_access().
>>
>> Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
>> ---
>> drivers/i2c/busses/i2c-i801.c | 20 +++++++++-----------
>> 1 file changed, 9 insertions(+), 11 deletions(-)
>
> Overall I like the idea. I only have one question to make sure I'm not
> missing something.
>
>> diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
>> index 9061333f2..ecec7a3a8 100644
>> --- a/drivers/i2c/busses/i2c-i801.c
>> +++ b/drivers/i2c/busses/i2c-i801.c
>> @@ -432,7 +432,7 @@ static int i801_wait_intr(struct i801_priv *priv)
>> busy = status & SMBHSTSTS_HOST_BUSY;
>> status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
>> if (!busy && status)
>> - return status;
>> + return status & STATUS_ERROR_FLAGS;
>> } while (time_is_after_eq_jiffies(timeout));
>
> Do I understand correctly that this change isn't really related to the
> rest of the patch, and could have been done independently?
>
> You are filtering out SMBHSTSTS_INTR simply because i801_check_post()
> will never check it anyway, right? If so, I wonder if that's really
> something we want to do, as ultimately this adds code with no
> functional benefit just to be "cleaner". But please correct me if I'm
> wrong.
>
Reason is that in few places we check whether return value of
i801_wait_intr() is zero, this would fail if not filtering out SMBHSTSTS_INTR.
Example:
i801_transaction() returns the return value of i801_wait_intr() now.
And in i801_block_transaction_by_block() we check whether return value of
i801_transaction() is zero.
^ permalink raw reply [flat|nested] 26+ messages in thread