From 083b42068a284765c5dd18c2f04ed542d157ffc9 Mon Sep 17 00:00:00 2001 From: Stefan Kerkmann Date: Tue, 26 Jul 2022 21:40:14 +0200 Subject: Chibios: Stop I2C peripheral on transaction error (#17798) From the ChibiOS HAL I2C driver pages: After a timeout the driver must be stopped and restarted because the bus is in an uncertain state. This commit does that stopping explicitly on any error that occurred, not only timeouts. As all the i2c functions restart the peripheral if necessary it is safe to do so. Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com> Co-authored-by: Dasky <32983009+daskygit@users.noreply.github.com> --- platforms/chibios/drivers/i2c_master.c | 39 +++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'platforms/chibios/drivers') diff --git a/platforms/chibios/drivers/i2c_master.c b/platforms/chibios/drivers/i2c_master.c index 21e064b1dc..bf8f1ee236 100644 --- a/platforms/chibios/drivers/i2c_master.c +++ b/platforms/chibios/drivers/i2c_master.c @@ -107,16 +107,25 @@ static const I2CConfig i2cconfig = { #endif }; -static i2c_status_t chibios_to_qmk(const msg_t* status) { - switch (*status) { - case I2C_NO_ERROR: - return I2C_STATUS_SUCCESS; - case I2C_TIMEOUT: - return I2C_STATUS_TIMEOUT; - // I2C_BUS_ERROR, I2C_ARBITRATION_LOST, I2C_ACK_FAILURE, I2C_OVERRUN, I2C_PEC_ERROR, I2C_SMB_ALERT - default: - return I2C_STATUS_ERROR; +/** + * @brief Handles any I2C error condition by stopping the I2C peripheral and + * aborting any ongoing transactions. Furthermore ChibiOS status codes are + * converted into QMK codes. + * + * @param status ChibiOS specific I2C status code + * @return i2c_status_t QMK specific I2C status code + */ +static i2c_status_t i2c_epilogue(const msg_t status) { + if (status == I2C_NO_ERROR) { + return I2C_STATUS_SUCCESS; } + + // From ChibiOS HAL: "After a timeout the driver must be stopped and + // restarted because the bus is in an uncertain state." We also issue that + // hard stop in case of any error. + i2c_stop(); + + return status == I2C_TIMEOUT ? I2C_STATUS_TIMEOUT : I2C_STATUS_ERROR; } __attribute__((weak)) void i2c_init(void) { @@ -149,14 +158,14 @@ i2c_status_t i2c_transmit(uint8_t address, const uint8_t* data, uint16_t length, i2c_address = address; i2cStart(&I2C_DRIVER, &i2cconfig); msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, 0, 0, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } i2c_status_t i2c_receive(uint8_t address, uint8_t* data, uint16_t length, uint16_t timeout) { i2c_address = address; i2cStart(&I2C_DRIVER, &i2cconfig); msg_t status = i2cMasterReceiveTimeout(&I2C_DRIVER, (i2c_address >> 1), data, length, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { @@ -170,7 +179,7 @@ i2c_status_t i2c_writeReg(uint8_t devaddr, uint8_t regaddr, const uint8_t* data, complete_packet[0] = regaddr; msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 1, 0, 0, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* data, uint16_t length, uint16_t timeout) { @@ -185,14 +194,14 @@ i2c_status_t i2c_writeReg16(uint8_t devaddr, uint16_t regaddr, const uint8_t* da complete_packet[1] = regaddr & 0xFF; msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), complete_packet, length + 2, 0, 0, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } i2c_status_t i2c_readReg(uint8_t devaddr, uint8_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { i2c_address = devaddr; i2cStart(&I2C_DRIVER, &i2cconfig); msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), ®addr, 1, data, length, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uint16_t length, uint16_t timeout) { @@ -200,7 +209,7 @@ i2c_status_t i2c_readReg16(uint8_t devaddr, uint16_t regaddr, uint8_t* data, uin i2cStart(&I2C_DRIVER, &i2cconfig); uint8_t register_packet[2] = {regaddr >> 8, regaddr & 0xFF}; msg_t status = i2cMasterTransmitTimeout(&I2C_DRIVER, (i2c_address >> 1), register_packet, 2, data, length, TIME_MS2I(timeout)); - return chibios_to_qmk(&status); + return i2c_epilogue(status); } void i2c_stop(void) { -- cgit v1.2.3