diff options
-rw-r--r-- | drivers/i2c/i2c-core-base.c | 6 | ||||
-rw-r--r-- | drivers/i2c/i2c-core-smbus.c | 18 | ||||
-rw-r--r-- | drivers/i2c/i2c-core.h | 7 | ||||
-rw-r--r-- | include/linux/i2c.h | 15 |
4 files changed, 36 insertions, 10 deletions
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c index ad14f38a67e4..4e6300dc2c4e 100644 --- a/drivers/i2c/i2c-core-base.c +++ b/drivers/i2c/i2c-core-base.c @@ -1890,7 +1890,11 @@ int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (ret = 0, try = 0; try <= adap->retries; try++) { - ret = adap->algo->master_xfer(adap, msgs, num); + if (i2c_in_atomic_xfer_mode() && adap->algo->master_xfer_atomic) + ret = adap->algo->master_xfer_atomic(adap, msgs, num); + else + ret = adap->algo->master_xfer(adap, msgs, num); + if (ret != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adap->timeout)) diff --git a/drivers/i2c/i2c-core-smbus.c b/drivers/i2c/i2c-core-smbus.c index 357e083e8f45..fdb0fb9fb9aa 100644 --- a/drivers/i2c/i2c-core-smbus.c +++ b/drivers/i2c/i2c-core-smbus.c @@ -548,6 +548,9 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data) { + int (*xfer_func)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); unsigned long orig_jiffies; int try; s32 res; @@ -562,13 +565,20 @@ s32 __i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; - if (adapter->algo->smbus_xfer) { + xfer_func = adapter->algo->smbus_xfer; + if (i2c_in_atomic_xfer_mode()) { + if (adapter->algo->smbus_xfer_atomic) + xfer_func = adapter->algo->smbus_xfer_atomic; + else if (adapter->algo->master_xfer_atomic) + xfer_func = NULL; /* fallback to I2C emulation */ + } + + if (xfer_func) { /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (res = 0, try = 0; try <= adapter->retries; try++) { - res = adapter->algo->smbus_xfer(adapter, addr, flags, - read_write, command, - protocol, data); + res = xfer_func(adapter, addr, flags, read_write, + command, protocol, data); if (res != -EAGAIN) break; if (time_after(jiffies, diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h index deea47c576e5..f9d0c417b5a5 100644 --- a/drivers/i2c/i2c-core.h +++ b/drivers/i2c/i2c-core.h @@ -43,10 +43,13 @@ static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap) { int ret = 0; - if (i2c_in_atomic_xfer_mode()) + if (i2c_in_atomic_xfer_mode()) { + WARN(!adap->algo->master_xfer_atomic && !adap->algo->smbus_xfer_atomic, + "No atomic I2C transfer handler for '%s'\n", dev_name(&adap->dev)); ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT) ? 0 : -EAGAIN; - else + } else { i2c_lock_bus(adap, I2C_LOCK_SEGMENT); + } return ret; } diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 758a6db864c9..03755d4c9229 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -499,9 +499,13 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info, * @master_xfer: Issue a set of i2c transactions to the given I2C adapter * defined by the msgs array, with num messages available to transfer via * the adapter specified by adap. + * @master_xfer_atomic: same as @master_xfer. Yet, only using atomic context + * so e.g. PMICs can be accessed very late before shutdown. Optional. * @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this * is not present, then the bus layer will try and convert the SMBus calls * into I2C transfers instead. + * @smbus_xfer_atomic: same as @smbus_xfer. Yet, only using atomic context + * so e.g. PMICs can be accessed very late before shutdown. Optional. * @functionality: Return the flags that this algorithm/adapter pair supports * from the I2C_FUNC_* flags. * @reg_slave: Register given client to I2C slave mode of this adapter @@ -512,9 +516,9 @@ i2c_register_board_info(int busnum, struct i2c_board_info const *info, * be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584 * to name two of the most common. * - * The return codes from the @master_xfer field should indicate the type of - * error code that occurred during the transfer, as documented in the kernel - * Documentation file Documentation/i2c/fault-codes. + * The return codes from the @master_xfer{_atomic} fields should indicate the + * type of error code that occurred during the transfer, as documented in the + * Kernel Documentation file Documentation/i2c/fault-codes. */ struct i2c_algorithm { /* @@ -528,9 +532,14 @@ struct i2c_algorithm { */ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); + int (*master_xfer_atomic)(struct i2c_adapter *adap, + struct i2c_msg *msgs, int num); int (*smbus_xfer)(struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data); + int (*smbus_xfer_atomic)(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); /* To determine what the adapter supports */ u32 (*functionality)(struct i2c_adapter *adap); |