Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

i2c: testunit: on errors, repeat NACK until STOP

This backend requests a NACK from the controller driver when it detects
an error. If that request gets ignored from some reason, subsequent
accesses will wrongly be handled OK. To fix this, an error now changes
the state machine, so the backend will report NACK until a STOP
condition has been detected. This make the driver more robust against
controllers which will sadly apply the NACK not to the current byte but
the next one.

Fixes: a8335c64c5f0 ("i2c: add slave testunit driver")
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>

+15 -4
+15 -4
drivers/i2c/i2c-slave-testunit.c
··· 38 38 39 39 enum testunit_flags { 40 40 TU_FLAG_IN_PROCESS, 41 + TU_FLAG_NACK, 41 42 }; 42 43 43 44 struct testunit_data { ··· 91 90 92 91 switch (event) { 93 92 case I2C_SLAVE_WRITE_REQUESTED: 94 - if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) 95 - return -EBUSY; 93 + if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) { 94 + ret = -EBUSY; 95 + break; 96 + } 96 97 97 98 memset(tu->regs, 0, TU_NUM_REGS); 98 99 tu->reg_idx = 0; ··· 102 99 break; 103 100 104 101 case I2C_SLAVE_WRITE_RECEIVED: 105 - if (test_bit(TU_FLAG_IN_PROCESS, &tu->flags)) 106 - return -EBUSY; 102 + if (test_bit(TU_FLAG_IN_PROCESS | TU_FLAG_NACK, &tu->flags)) { 103 + ret = -EBUSY; 104 + break; 105 + } 107 106 108 107 if (tu->reg_idx < TU_NUM_REGS) 109 108 tu->regs[tu->reg_idx] = *val; ··· 134 129 * here because we still need them in the workqueue! 135 130 */ 136 131 tu->reg_idx = 0; 132 + 133 + clear_bit(TU_FLAG_NACK, &tu->flags); 137 134 break; 138 135 139 136 case I2C_SLAVE_READ_PROCESSED: ··· 157 150 tu->regs[TU_REG_CMD] : 0; 158 151 break; 159 152 } 153 + 154 + /* If an error occurred somewhen, we NACK everything until next STOP */ 155 + if (ret) 156 + set_bit(TU_FLAG_NACK, &tu->flags); 160 157 161 158 return ret; 162 159 }