From 835d5df175c057191e3c9fe2ad31c8546404e854 Mon Sep 17 00:00:00 2001 From: matejcik Date: Mon, 3 Jun 2019 17:02:20 +0200 Subject: [PATCH] core: reset i2c bus when detected busy (cherry picked from commit bf793848451a7a6ef1cb53e3040805d1fde7df5f) --- core/embed/trezorhal/touch_t.h | 96 +++++++++++++++++++++++++++++----- 1 file changed, 84 insertions(+), 12 deletions(-) diff --git a/core/embed/trezorhal/touch_t.h b/core/embed/trezorhal/touch_t.h index 4283b542fd..51aae97909 100644 --- a/core/embed/trezorhal/touch_t.h +++ b/core/embed/trezorhal/touch_t.h @@ -91,16 +91,11 @@ void HAL_I2C_MspDeInit(I2C_HandleTypeDef *hi2c) { __HAL_RCC_I2C1_CLK_DISABLE(); } -void touch_power_on(void) { +static void _i2c_init(void) { if (i2c_handle.Instance) { return; } - // turn on CTP circuitry - touch_active_pin_state(); - HAL_Delay(50); - - // I2C device interface configuration i2c_handle.Instance = I2C1; i2c_handle.Init.ClockSpeed = 400000; i2c_handle.Init.DutyCycle = I2C_DUTYCYCLE_16_9; @@ -115,6 +110,84 @@ void touch_power_on(void) { ensure(secfalse, NULL); return; } +} + +static void _i2c_deinit(void) { + if (i2c_handle.Instance) { + HAL_I2C_DeInit(&i2c_handle); + i2c_handle.Instance = NULL; + } +} + +static void _i2c_ensure_pin(uint16_t GPIO_Pin, GPIO_PinState PinState) { + HAL_GPIO_WritePin(GPIOB, GPIO_Pin, PinState); + while (HAL_GPIO_ReadPin(GPIOB, GPIO_Pin) != PinState) + ; +} + +// I2C cycle described in section 2.9.7 of STM CD00288116 Errata sheet +// +// https://www.st.com/content/ccc/resource/technical/document/errata_sheet/7f/05/b0/bc/34/2f/4c/21/CD00288116.pdf/files/CD00288116.pdf/jcr:content/translations/en.CD00288116.pdf + +static void _i2c_cycle(void) { + // PIN6 is SCL, PIN7 is SDA + + // 1. Disable I2C peripheral + _i2c_deinit(); + + // 2. Configure SCL/SDA as GPIO OUTPUT Open Drain + GPIO_InitTypeDef GPIO_InitStructure; + GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + HAL_Delay(50); + + // 3. Check SCL and SDA High level + _i2c_ensure_pin(GPIO_PIN_6, GPIO_PIN_SET); + _i2c_ensure_pin(GPIO_PIN_7, GPIO_PIN_SET); + // 4+5. Check SDA Low level + _i2c_ensure_pin(GPIO_PIN_7, GPIO_PIN_RESET); + // 6+7. Check SCL Low level + _i2c_ensure_pin(GPIO_PIN_6, GPIO_PIN_RESET); + // 8+9. Check SCL High level + _i2c_ensure_pin(GPIO_PIN_6, GPIO_PIN_SET); + // 10+11. Check SDA High level + _i2c_ensure_pin(GPIO_PIN_7, GPIO_PIN_SET); + + // 12. Configure SCL/SDA as Alternate function Open-Drain + GPIO_InitStructure.Mode = GPIO_MODE_AF_OD; + GPIO_InitStructure.Pull = GPIO_NOPULL; + GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW; + GPIO_InitStructure.Alternate = GPIO_AF4_I2C1; + GPIO_InitStructure.Pin = GPIO_PIN_6 | GPIO_PIN_7; + HAL_GPIO_Init(GPIOB, &GPIO_InitStructure); + HAL_Delay(50); + + // 13. Set SWRST bit in I2Cx_CR1 register + __HAL_RCC_I2C1_FORCE_RESET(); + HAL_Delay(50); + + // 14. Clear SWRST bit in I2Cx_CR1 register + __HAL_RCC_I2C1_RELEASE_RESET(); + + // 15. Enable the I2C peripheral + _i2c_init(); + HAL_Delay(10); +} + +void touch_power_on(void) { + if (i2c_handle.Instance) { + return; + } + + // turn on CTP circuitry + touch_active_pin_state(); + HAL_Delay(50); + + // I2C device interface configuration + _i2c_init(); // set register 0xA4 G_MODE to interrupt polling mode (0x00). basically, CTPM // keeps this input line (to PC4) low while a finger is on the screen. @@ -129,10 +202,7 @@ void touch_power_on(void) { } void touch_power_off(void) { - if (i2c_handle.Instance) { - HAL_I2C_DeInit(&i2c_handle); - i2c_handle.Instance = NULL; - } + _i2c_deinit(); // turn off CTP circuitry HAL_Delay(50); touch_default_pin_state(); @@ -175,8 +245,10 @@ uint32_t touch_read(void) { } uint8_t outgoing[] = {0x00}; // start reading from address 0x00 - if (HAL_OK != HAL_I2C_Master_Transmit(&i2c_handle, TOUCH_ADDRESS, outgoing, - sizeof(outgoing), 1)) { + int result = HAL_I2C_Master_Transmit(&i2c_handle, TOUCH_ADDRESS, outgoing, + sizeof(outgoing), 1); + if (result != HAL_OK) { + if (result == HAL_BUSY) _i2c_cycle(); return 0; }