* [PATCH v3 2/5] i2c: i801: Add helper i801_check_and_clear_pec_error
2024-02-02 7:00 [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Heiner Kallweit
2024-02-02 7:01 ` [PATCH v3 1/5] i2c: i801: Define FEATURES_ICH5 as an extension of FEATURES_ICH4 Heiner Kallweit
@ 2024-02-02 7:02 ` Heiner Kallweit
2024-02-02 7:02 ` [PATCH v3 3/5] i2c: i801: Split i801_block_transaction Heiner Kallweit
` (3 subsequent siblings)
5 siblings, 0 replies; 10+ messages in thread
From: Heiner Kallweit @ 2024-02-02 7:02 UTC (permalink / raw)
To: Jean Delvare, Andi Shyti; +Cc: linux-i2c@vger.kernel.org, Andy Shevchenko
Avoid code duplication and factor out checking and clearing PEC error
bit to new helper i801_check_and_clear_pec_error().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
Reviewed-by: Andi Shyti <andi.shyti@kernel.org>
---
drivers/i2c/busses/i2c-i801.c | 38 ++++++++++++++++++++++-------------
1 file changed, 24 insertions(+), 14 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 44ae6326d..156bace92 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -328,11 +328,27 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n"
"\t\t 0x10 don't use interrupts\n"
"\t\t 0x20 disable SMBus Host Notify ");
+static int i801_check_and_clear_pec_error(struct i801_priv *priv)
+{
+ u8 status;
+
+ if (!(priv->features & FEATURE_SMBUS_PEC))
+ return 0;
+
+ status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
+ if (status) {
+ outb_p(status, SMBAUXSTS(priv));
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
/* Make sure the SMBus host is ready to start transmitting.
Return 0 if it is, -EBUSY if it is not. */
static int i801_check_pre(struct i801_priv *priv)
{
- int status;
+ int status, result;
status = inb_p(SMBHSTSTS(priv));
if (status & SMBHSTSTS_HOST_BUSY) {
@@ -353,13 +369,9 @@ static int i801_check_pre(struct i801_priv *priv)
* the hardware was already in this state when the driver
* started.
*/
- if (priv->features & FEATURE_SMBUS_PEC) {
- status = inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE;
- if (status) {
- pci_dbg(priv->pci_dev, "Clearing aux status flags (%02x)\n", status);
- outb_p(status, SMBAUXSTS(priv));
- }
- }
+ result = i801_check_and_clear_pec_error(priv);
+ if (result)
+ pci_dbg(priv->pci_dev, "Clearing aux status flag CRCE\n");
return 0;
}
@@ -408,14 +420,12 @@ static int i801_check_post(struct i801_priv *priv, int status)
* bit is harmless as long as it's cleared before
* the next operation.
*/
- if ((priv->features & FEATURE_SMBUS_PEC) &&
- (inb_p(SMBAUXSTS(priv)) & SMBAUXSTS_CRCE)) {
- outb_p(SMBAUXSTS_CRCE, SMBAUXSTS(priv));
- result = -EBADMSG;
- dev_dbg(&priv->pci_dev->dev, "PEC error\n");
+ result = i801_check_and_clear_pec_error(priv);
+ if (result) {
+ pci_dbg(priv->pci_dev, "PEC error\n");
} else {
result = -ENXIO;
- dev_dbg(&priv->pci_dev->dev, "No response\n");
+ pci_dbg(priv->pci_dev, "No response\n");
}
}
if (status & SMBHSTSTS_BUS_ERR) {
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 3/5] i2c: i801: Split i801_block_transaction
2024-02-02 7:00 [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Heiner Kallweit
2024-02-02 7:01 ` [PATCH v3 1/5] i2c: i801: Define FEATURES_ICH5 as an extension of FEATURES_ICH4 Heiner Kallweit
2024-02-02 7:02 ` [PATCH v3 2/5] i2c: i801: Add helper i801_check_and_clear_pec_error Heiner Kallweit
@ 2024-02-02 7:02 ` Heiner Kallweit
2024-02-08 17:03 ` Andi Shyti
2024-02-02 7:04 ` [PATCH v3 4/5] i2c: i801: Add SMBUS_LEN_SENTINEL Heiner Kallweit
` (2 subsequent siblings)
5 siblings, 1 reply; 10+ messages in thread
From: Heiner Kallweit @ 2024-02-02 7:02 UTC (permalink / raw)
To: Jean Delvare, Andi Shyti; +Cc: linux-i2c@vger.kernel.org, Andy Shevchenko
i2c and smbus block transaction handling have little in common,
therefore split this function to improve code readability.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
drivers/i2c/busses/i2c-i801.c | 112 +++++++++++++++-------------------
1 file changed, 50 insertions(+), 62 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 156bace92..24eb187db 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -802,77 +802,65 @@ static int i801_simple_transaction(struct i801_priv *priv, union i2c_smbus_data
return 0;
}
-/* Block transaction function */
-static int i801_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
- u8 addr, u8 hstcmd, char read_write, int command)
+static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ u8 addr, u8 hstcmd, char read_write, int command)
{
- int result = 0;
- unsigned char hostc;
-
if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA)
data->block[0] = I2C_SMBUS_BLOCK_MAX;
else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
- switch (command) {
- case I2C_SMBUS_BLOCK_DATA:
- i801_set_hstadd(priv, addr, read_write);
- outb_p(hstcmd, SMBHSTCMD(priv));
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- /*
- * NB: page 240 of ICH5 datasheet shows that the R/#W
- * bit should be cleared here, even when reading.
- * However if SPD Write Disable is set (Lynx Point and later),
- * the read will fail if we don't set the R/#W bit.
- */
- i801_set_hstadd(priv, addr,
- priv->original_hstcfg & SMBHSTCFG_SPD_WD ?
- read_write : 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
- */
- outb_p(hstcmd, SMBHSTDAT1(priv));
- } else
- outb_p(hstcmd, SMBHSTCMD(priv));
-
- if (read_write == I2C_SMBUS_WRITE) {
- /* set I2C_EN bit in configuration register */
- pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
- pci_write_config_byte(priv->pci_dev, SMBHSTCFG,
- hostc | SMBHSTCFG_I2C_EN);
- } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
- dev_err(&priv->pci_dev->dev,
- "I2C block read is unsupported!\n");
- return -EOPNOTSUPP;
- }
- break;
- case I2C_SMBUS_BLOCK_PROC_CALL:
+ if (command == I2C_SMBUS_BLOCK_PROC_CALL)
/* Needs to be flagged as write transaction */
i801_set_hstadd(priv, addr, I2C_SMBUS_WRITE);
+ else
+ i801_set_hstadd(priv, addr, read_write);
+ outb_p(hstcmd, SMBHSTCMD(priv));
+
+ if (priv->features & FEATURE_BLOCK_BUFFER)
+ return i801_block_transaction_by_block(priv, data, read_write, command);
+ else
+ return i801_block_transaction_byte_by_byte(priv, data, read_write, command);
+}
+
+static int i801_i2c_block_transaction(struct i801_priv *priv, union i2c_smbus_data *data,
+ u8 addr, u8 hstcmd, char read_write, int command)
+{
+ int result;
+ u8 hostc;
+
+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
+ return -EPROTO;
+ /*
+ * NB: page 240 of ICH5 datasheet shows that the R/#W bit should be cleared here,
+ * even when reading. However if SPD Write Disable is set (Lynx Point and later),
+ * the read will fail if we don't set the R/#W bit.
+ */
+ i801_set_hstadd(priv, addr,
+ priv->original_hstcfg & SMBHSTCFG_SPD_WD ? read_write : I2C_SMBUS_WRITE);
+
+ /* NB: page 240 of ICH5 datasheet shows that DATA1 is the cmd field when reading */
+ if (read_write == I2C_SMBUS_READ)
+ outb_p(hstcmd, SMBHSTDAT1(priv));
+ else
outb_p(hstcmd, SMBHSTCMD(priv));
- break;
+
+ if (read_write == I2C_SMBUS_WRITE) {
+ /* set I2C_EN bit in configuration register */
+ pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &hostc);
+ pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc | SMBHSTCFG_I2C_EN);
+ } else if (!(priv->features & FEATURE_I2C_BLOCK_READ)) {
+ pci_err(priv->pci_dev, "I2C block read is unsupported!\n");
+ return -EOPNOTSUPP;
}
- /* Experience has shown that the block buffer can only be used for
- SMBus (not I2C) block transactions, even though the datasheet
- doesn't mention this limitation. */
- 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);
+ /* Block buffer isn't supported for I2C block transactions */
+ result = i801_block_transaction_byte_by_byte(priv, data, read_write, command);
- if (command == I2C_SMBUS_I2C_BLOCK_DATA
- && read_write == I2C_SMBUS_WRITE) {
- /* restore saved configuration register value */
+ /* restore saved configuration register value */
+ if (read_write == I2C_SMBUS_WRITE)
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hostc);
- }
+
return result;
}
@@ -903,10 +891,10 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
outb_p(inb_p(SMBAUXCTL(priv)) & (~SMBAUXCTL_CRC),
SMBAUXCTL(priv));
- if (size == I2C_SMBUS_BLOCK_DATA ||
- size == I2C_SMBUS_I2C_BLOCK_DATA ||
- size == I2C_SMBUS_BLOCK_PROC_CALL)
- ret = i801_block_transaction(priv, data, addr, command, read_write, size);
+ if (size == I2C_SMBUS_BLOCK_DATA || size == I2C_SMBUS_BLOCK_PROC_CALL)
+ ret = i801_smbus_block_transaction(priv, data, addr, command, read_write, size);
+ else if (size == I2C_SMBUS_I2C_BLOCK_DATA)
+ ret = i801_i2c_block_transaction(priv, data, addr, command, read_write, size);
else
ret = i801_simple_transaction(priv, data, addr, command, read_write, size);
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 4/5] i2c: i801: Add SMBUS_LEN_SENTINEL
2024-02-02 7:00 [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Heiner Kallweit
` (2 preceding siblings ...)
2024-02-02 7:02 ` [PATCH v3 3/5] i2c: i801: Split i801_block_transaction Heiner Kallweit
@ 2024-02-02 7:04 ` Heiner Kallweit
2024-02-08 17:03 ` Andi Shyti
2024-02-02 7:06 ` [PATCH v3 5/5] i2c: i801: Add helper i801_get_block_len Heiner Kallweit
2024-02-08 22:33 ` [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Andi Shyti
5 siblings, 1 reply; 10+ messages in thread
From: Heiner Kallweit @ 2024-02-02 7:04 UTC (permalink / raw)
To: Jean Delvare, Andi Shyti; +Cc: linux-i2c@vger.kernel.org, Andy Shevchenko
Add a sentinel length value that is used to check whether we should
read and use the length value provided by the slave device.
This simplifies the currently used checks.
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v2:
- add comments
v3:
- fix comment style
---
drivers/i2c/busses/i2c-i801.c | 22 ++++++++++++++++------
1 file changed, 16 insertions(+), 6 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 24eb187db..15d251288 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -205,6 +205,8 @@
#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | \
STATUS_ERROR_FLAGS)
+#define SMBUS_LEN_SENTINEL (I2C_SMBUS_BLOCK_MAX + 1)
+
/* Older devices have their ID defined in <linux/pci_ids.h> */
#define PCI_DEVICE_ID_INTEL_COMETLAKE_SMBUS 0x02a3
#define PCI_DEVICE_ID_INTEL_COMETLAKE_H_SMBUS 0x06a3
@@ -541,9 +543,12 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
static void i801_isr_byte_done(struct i801_priv *priv)
{
if (priv->is_read) {
- /* For SMBus block reads, length is received with first byte */
- if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) &&
- (priv->count == 0)) {
+ /*
+ * At transfer start i801_smbus_block_transaction() marks
+ * the block length as invalid. Check for this sentinel value
+ * and read the block length from SMBHSTDAT0.
+ */
+ if (priv->len == SMBUS_LEN_SENTINEL) {
priv->len = inb_p(SMBHSTDAT0(priv));
if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) {
dev_err(&priv->pci_dev->dev,
@@ -698,8 +703,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
if (status)
return status;
- if (i == 1 && read_write == I2C_SMBUS_READ
- && command != I2C_SMBUS_I2C_BLOCK_DATA) {
+ /*
+ * At transfer start i801_smbus_block_transaction() marks
+ * the block length as invalid. Check for this sentinel value
+ * and read the block length from SMBHSTDAT0.
+ */
+ if (len == SMBUS_LEN_SENTINEL) {
len = inb_p(SMBHSTDAT0(priv));
if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
dev_err(&priv->pci_dev->dev,
@@ -806,7 +815,8 @@ static int i801_smbus_block_transaction(struct i801_priv *priv, union i2c_smbus_
u8 addr, u8 hstcmd, char read_write, int command)
{
if (read_write == I2C_SMBUS_READ && command == I2C_SMBUS_BLOCK_DATA)
- data->block[0] = I2C_SMBUS_BLOCK_MAX;
+ /* Mark block length as invalid */
+ data->block[0] = SMBUS_LEN_SENTINEL;
else if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX)
return -EPROTO;
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* [PATCH v3 5/5] i2c: i801: Add helper i801_get_block_len
2024-02-02 7:00 [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Heiner Kallweit
` (3 preceding siblings ...)
2024-02-02 7:04 ` [PATCH v3 4/5] i2c: i801: Add SMBUS_LEN_SENTINEL Heiner Kallweit
@ 2024-02-02 7:06 ` Heiner Kallweit
2024-02-08 17:05 ` Andi Shyti
2024-02-08 22:33 ` [PATCH v3 0/5] i2c: i801: collection of further improvements / refactorings Andi Shyti
5 siblings, 1 reply; 10+ messages in thread
From: Heiner Kallweit @ 2024-02-02 7:06 UTC (permalink / raw)
To: Jean Delvare, Andi Shyti; +Cc: linux-i2c@vger.kernel.org, Andy Shevchenko
Avoid code duplication and factor out retrieving and checking the
block length value to new helper i801_get_block_len().
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
---
v2:
- in the error case go to label out instead of directly bailing
out in i801_block_transaction_by_block()
---
drivers/i2c/busses/i2c-i801.c | 35 ++++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 15d251288..918c794c7 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -330,6 +330,18 @@ MODULE_PARM_DESC(disable_features, "Disable selected driver features:\n"
"\t\t 0x10 don't use interrupts\n"
"\t\t 0x20 disable SMBus Host Notify ");
+static int i801_get_block_len(struct i801_priv *priv)
+{
+ u8 len = inb_p(SMBHSTDAT0(priv));
+
+ if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
+ pci_err(priv->pci_dev, "Illegal SMBus block read size %u\n", len);
+ return -EPROTO;
+ }
+
+ return len;
+}
+
static int i801_check_and_clear_pec_error(struct i801_priv *priv)
{
u8 status;
@@ -525,12 +537,11 @@ static int i801_block_transaction_by_block(struct i801_priv *priv,
if (read_write == I2C_SMBUS_READ ||
command == I2C_SMBUS_BLOCK_PROC_CALL) {
- len = inb_p(SMBHSTDAT0(priv));
- if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
- status = -EPROTO;
+ status = i801_get_block_len(priv);
+ if (status < 0)
goto out;
- }
+ len = status;
data->block[0] = len;
for (i = 0; i < len; i++)
data->block[i + 1] = inb_p(SMBBLKDAT(priv));
@@ -549,14 +560,11 @@ static void i801_isr_byte_done(struct i801_priv *priv)
* and read the block length from SMBHSTDAT0.
*/
if (priv->len == SMBUS_LEN_SENTINEL) {
- priv->len = inb_p(SMBHSTDAT0(priv));
- if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&priv->pci_dev->dev,
- "Illegal SMBus block read size %d\n",
- priv->len);
+ priv->len = i801_get_block_len(priv);
+ if (priv->len < 0)
/* FIXME: Recover */
priv->len = I2C_SMBUS_BLOCK_MAX;
- }
+
priv->data[-1] = priv->len;
}
@@ -709,11 +717,8 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
* and read the block length from SMBHSTDAT0.
*/
if (len == SMBUS_LEN_SENTINEL) {
- len = inb_p(SMBHSTDAT0(priv));
- if (len < 1 || len > I2C_SMBUS_BLOCK_MAX) {
- dev_err(&priv->pci_dev->dev,
- "Illegal SMBus block read size %d\n",
- len);
+ len = i801_get_block_len(priv);
+ if (len < 0) {
/* Recover */
while (inb_p(SMBHSTSTS(priv)) &
SMBHSTSTS_HOST_BUSY)
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread