i2c: core: introduce callbacks for atomic transfers

We had the request to access devices very late when interrupts are not
available anymore multiple times now. Mostly to prepare shutdown or
reboot. Allow adapters to specify a specific callback for this case.
Note that we fall back to the generic {master|smbus}_xfer callback if
this new atomic one is not present. This is intentional to preserve the
previous behaviour and avoid regressions. Because there are drivers not
using interrupts or because it might have worked "accidently" before.

Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Reviewed-by Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Tested-by: Stefan Lengfeld <contact@stefanchrist.eu>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
Wolfram Sang
2019-04-03 14:40:10 +02:00
committed by Wolfram Sang
parent 83c42212d2
commit 63b96983a5
4 changed files with 36 additions and 10 deletions

View File

@@ -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,