git: 43f2fdb20510 - stable/13 - linuxkpi: Add `i2c_adapter_quirks` support
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Thu, 16 Feb 2023 11:56:24 UTC
The branch stable/13 has been updated by dumbbell (ports committer):
URL: https://cgit.FreeBSD.org/src/commit/?id=43f2fdb205104dae32326d074367aa0eb251f8ec
commit 43f2fdb205104dae32326d074367aa0eb251f8ec
Author: Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
AuthorDate: 2023-02-08 17:57:32 +0000
Commit: Jean-Sébastien Pédron <dumbbell@FreeBSD.org>
CommitDate: 2023-02-16 11:55:19 +0000
linuxkpi: Add `i2c_adapter_quirks` support
While here, also declare `I2C_CLASS_HWMON`.
Reviewed by: manu
Approved by: manu
Differential Revision: https://reviews.freebsd.org/D38535
(cherry picked from commit 83276e1f952d720c23092ea17e95c03fef8476b7)
---
sys/compat/linuxkpi/common/include/linux/i2c.h | 25 ++++++
sys/compat/linuxkpi/common/src/linux_i2c.c | 113 +++++++++++++++++++++++++
2 files changed, 138 insertions(+)
diff --git a/sys/compat/linuxkpi/common/include/linux/i2c.h b/sys/compat/linuxkpi/common/include/linux/i2c.h
index 365ab893fdfd..f24d282586f6 100644
--- a/sys/compat/linuxkpi/common/include/linux/i2c.h
+++ b/sys/compat/linuxkpi/common/include/linux/i2c.h
@@ -46,6 +46,7 @@
#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL 0
#define I2C_FUNC_10BIT_ADDR 0
+#define I2C_CLASS_HWMON 0x1
#define I2C_CLASS_DDC 0x8
#define I2C_CLASS_SPD 0x80
@@ -58,6 +59,7 @@ struct i2c_adapter {
const struct i2c_lock_operations *lock_ops;
const struct i2c_algorithm *algo;
+ const struct i2c_adapter_quirks *quirks;
void *algo_data;
int retries;
@@ -82,6 +84,29 @@ struct i2c_lock_operations {
void (*unlock_bus)(struct i2c_adapter *, unsigned int);
};
+struct i2c_adapter_quirks {
+ uint64_t flags;
+ int max_num_msgs;
+ uint16_t max_write_len;
+ uint16_t max_read_len;
+ uint16_t max_comb_1st_msg_len;
+ uint16_t max_comb_2nd_msg_len;
+};
+
+#define I2C_AQ_COMB BIT(0)
+#define I2C_AQ_COMB_WRITE_FIRST BIT(1)
+#define I2C_AQ_COMB_READ_SECOND BIT(2)
+#define I2C_AQ_COMB_SAME_ADDR BIT(3)
+#define I2C_AQ_COMB_WRITE_THEN_READ \
+ (I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
+ I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
+#define I2C_AQ_NO_CLK_STRETCH BIT(4)
+#define I2C_AQ_NO_ZERO_LEN_READ BIT(5)
+#define I2C_AQ_NO_ZERO_LEN_WRITE BIT(6)
+#define I2C_AQ_NO_ZERO_LEN \
+ (I2C_AQ_NO_ZERO_LEN_READ | I2C_AQ_NO_ZERO_LEN_WRITE)
+#define I2C_AQ_NO_REP_START BIT(7)
+
int lkpi_i2c_add_adapter(struct i2c_adapter *adapter);
int lkpi_i2c_del_adapter(struct i2c_adapter *adapter);
diff --git a/sys/compat/linuxkpi/common/src/linux_i2c.c b/sys/compat/linuxkpi/common/src/linux_i2c.c
index a94ed630ab73..c01c88a1fafa 100644
--- a/sys/compat/linuxkpi/common/src/linux_i2c.c
+++ b/sys/compat/linuxkpi/common/src/linux_i2c.c
@@ -169,6 +169,116 @@ lkpi_i2c_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
return (0);
}
+static int i2c_check_for_quirks(struct i2c_adapter *adapter,
+ struct iic_msg *msgs, uint32_t nmsgs)
+{
+ const struct i2c_adapter_quirks *quirks;
+ device_t dev;
+ int i, max_nmsgs;
+ bool check_len;
+
+ dev = adapter->dev.parent->bsddev;
+ quirks = adapter->quirks;
+ if (quirks == NULL)
+ return (0);
+
+ check_len = true;
+ max_nmsgs = quirks->max_num_msgs;
+
+ if (quirks->flags & I2C_AQ_COMB) {
+ max_nmsgs = 2;
+
+ if (nmsgs == 2) {
+ if (quirks->flags & I2C_AQ_COMB_WRITE_FIRST &&
+ msgs[0].flags & IIC_M_RD) {
+ device_printf(dev,
+ "Error: "
+ "first combined message must be write\n");
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_COMB_READ_SECOND &&
+ !(msgs[1].flags & IIC_M_RD)) {
+ device_printf(dev,
+ "Error: "
+ "second combined message must be read\n");
+ return (EOPNOTSUPP);
+ }
+
+ if (quirks->flags & I2C_AQ_COMB_SAME_ADDR &&
+ msgs[0].slave != msgs[1].slave) {
+ device_printf(dev,
+ "Error: "
+ "combined message must be use the same "
+ "address\n");
+ return (EOPNOTSUPP);
+ }
+
+ if (quirks->max_comb_1st_msg_len &&
+ msgs[0].len > quirks->max_comb_1st_msg_len) {
+ device_printf(dev,
+ "Error: "
+ "message too long: %hu > %hu max\n",
+ msgs[0].len,
+ quirks->max_comb_1st_msg_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->max_comb_2nd_msg_len &&
+ msgs[1].len > quirks->max_comb_2nd_msg_len) {
+ device_printf(dev,
+ "Error: "
+ "message too long: %hu > %hu max\n",
+ msgs[1].len,
+ quirks->max_comb_2nd_msg_len);
+ return (EOPNOTSUPP);
+ }
+
+ check_len = false;
+ }
+ }
+
+ if (max_nmsgs && nmsgs > max_nmsgs) {
+ device_printf(dev,
+ "Error: too many messages: %d > %d max\n",
+ nmsgs, max_nmsgs);
+ return (EOPNOTSUPP);
+ }
+
+ for (i = 0; i < nmsgs; i++) {
+ if (msgs[i].flags & IIC_M_RD) {
+ if (check_len && quirks->max_read_len &&
+ msgs[i].len > quirks->max_read_len) {
+ device_printf(dev,
+ "Error: "
+ "message %d too long: %hu > %hu max\n",
+ i, msgs[i].len, quirks->max_read_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_NO_ZERO_LEN_READ &&
+ msgs[i].len == 0) {
+ device_printf(dev,
+ "Error: message %d of length 0\n", i);
+ return (EOPNOTSUPP);
+ }
+ } else {
+ if (check_len && quirks->max_write_len &&
+ msgs[i].len > quirks->max_write_len) {
+ device_printf(dev,
+ "Message %d too long: %hu > %hu max\n",
+ i, msgs[i].len, quirks->max_write_len);
+ return (EOPNOTSUPP);
+ }
+ if (quirks->flags & I2C_AQ_NO_ZERO_LEN_WRITE &&
+ msgs[i].len == 0) {
+ device_printf(dev,
+ "Error: message %d of length 0\n", i);
+ return (EOPNOTSUPP);
+ }
+ }
+ }
+
+ return (0);
+}
+
static int
lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{
@@ -179,6 +289,9 @@ lkpi_i2c_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
sc = device_get_softc(dev);
if (sc->adapter == NULL)
return (ENXIO);
+ ret = i2c_check_for_quirks(sc->adapter, msgs, nmsgs);
+ if (ret != 0)
+ return (ret);
linux_set_current(curthread);
linux_msgs = malloc(sizeof(struct i2c_msg) * nmsgs,