feat(core/embed): introduce non-blocking i2c drivers

[no changelog]
tychovrahe/coresplit/merged
cepetr 3 weeks ago
parent a67149efff
commit bd95ec5bdf

@ -24,7 +24,13 @@
#define I2C_INSTANCE_0_SCL_PORT GPIOA
#define I2C_INSTANCE_0_SCL_PIN GPIO_PIN_8
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOA_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_FLG RCC_APB1RSTR_I2C3RST
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR_I2C3RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C3_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C3_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C3_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C3_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 0
#define TOUCH_I2C_INSTANCE 0

@ -30,6 +30,11 @@
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOH_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR2
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR2_I2C5RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C5_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C5_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C5_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C5_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 0
#define TOUCH_I2C_INSTANCE 0

@ -50,7 +50,13 @@
#define I2C_INSTANCE_0_SCL_PORT GPIOB
#define I2C_INSTANCE_0_SCL_PIN GPIO_PIN_10
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_FLG RCC_APB1RSTR_I2C2RST
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR_I2C2RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C2_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C2_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C2_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C2_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 50 // Optiga requires 50us guard time
#define OPTIGA_I2C_INSTANCE 0
#define OPTIGA_RST_PORT GPIOD

@ -42,7 +42,13 @@
#define I2C_INSTANCE_0_SCL_PORT GPIOB
#define I2C_INSTANCE_0_SCL_PIN GPIO_PIN_6
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_FLG RCC_APB1RSTR_I2C1RST
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR_I2C1RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C1_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C1_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C1_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C1_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 0
#define TOUCH_SENSITIVITY 0x06
#define TOUCH_I2C_INSTANCE 0

@ -54,6 +54,11 @@
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOG_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR1
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR1_I2C1RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C1_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C1_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C1_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C1_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 50 // Optiga requires 50us guard time
#define OPTIGA_I2C_INSTANCE 0
#define OPTIGA_RST_PORT GPIOE

@ -50,6 +50,11 @@
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR1
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR1_I2C1RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C1_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C1_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C1_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C1_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 0
#define I2C_INSTANCE_1 I2C2
#define I2C_INSTANCE_1_CLK_EN __HAL_RCC_I2C2_CLK_ENABLE
@ -63,6 +68,11 @@
#define I2C_INSTANCE_1_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_1_RESET_REG &RCC->APB1RSTR1
#define I2C_INSTANCE_1_RESET_BIT RCC_APB1RSTR1_I2C2RST
#define I2C_INSTANCE_1_EV_IRQHandler I2C2_EV_IRQHandler
#define I2C_INSTANCE_1_ER_IRQHandler I2C2_ER_IRQHandler
#define I2C_INSTANCE_1_EV_IRQn I2C2_EV_IRQn
#define I2C_INSTANCE_1_ER_IRQn I2C2_ER_IRQn
#define I2C_INSTANCE_1_GUARD_TIME 0
#define I2C_INSTANCE_2 I2C3
#define I2C_INSTANCE_2_CLK_EN __HAL_RCC_I2C3_CLK_ENABLE
@ -76,6 +86,11 @@
#define I2C_INSTANCE_2_SCL_CLK_EN __HAL_RCC_GPIOC_CLK_ENABLE
#define I2C_INSTANCE_2_RESET_REG &RCC->APB3RSTR
#define I2C_INSTANCE_2_RESET_BIT RCC_APB3RSTR_I2C3RST
#define I2C_INSTANCE_2_EV_IRQHandler I2C3_EV_IRQHandler
#define I2C_INSTANCE_2_ER_IRQHandler I2C3_ER_IRQHandler
#define I2C_INSTANCE_2_EV_IRQn I2C3_EV_IRQn
#define I2C_INSTANCE_2_ER_IRQn I2C3_ER_IRQn
#define I2C_INSTANCE_2_GUARD_TIME 50 // Optiga requires 50us guard time
#define TOUCH_PANEL_LX154A2422CPT23 1
#define TOUCH_SENSITIVITY 0x40

@ -51,6 +51,11 @@
#define I2C_INSTANCE_0_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_0_RESET_REG &RCC->APB1RSTR1
#define I2C_INSTANCE_0_RESET_BIT RCC_APB1RSTR1_I2C1RST
#define I2C_INSTANCE_0_EV_IRQHandler I2C1_EV_IRQHandler
#define I2C_INSTANCE_0_ER_IRQHandler I2C1_ER_IRQHandler
#define I2C_INSTANCE_0_EV_IRQn I2C1_EV_IRQn
#define I2C_INSTANCE_0_ER_IRQn I2C1_ER_IRQn
#define I2C_INSTANCE_0_GUARD_TIME 0
#define I2C_INSTANCE_1 I2C2
#define I2C_INSTANCE_1_CLK_EN __HAL_RCC_I2C2_CLK_ENABLE
@ -64,6 +69,11 @@
#define I2C_INSTANCE_1_SCL_CLK_EN __HAL_RCC_GPIOB_CLK_ENABLE
#define I2C_INSTANCE_1_RESET_REG &RCC->APB1RSTR1
#define I2C_INSTANCE_1_RESET_BIT RCC_APB1RSTR1_I2C2RST
#define I2C_INSTANCE_1_EV_IRQHandler I2C2_EV_IRQHandler
#define I2C_INSTANCE_1_ER_IRQHandler I2C2_ER_IRQHandler
#define I2C_INSTANCE_1_EV_IRQn I2C2_EV_IRQn
#define I2C_INSTANCE_1_ER_IRQn I2C2_ER_IRQn
#define I2C_INSTANCE_1_GUARD_TIME 50 // Optiga requires 50us guard time
#define TOUCH_SENSITIVITY 0x40
#define TOUCH_I2C_INSTANCE 0

@ -0,0 +1,185 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TREZORHAL_I2C_BUS_H
#define TREZORHAL_I2C_BUS_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
// I2C bus abstraction
typedef struct i2c_bus i2c_bus_t;
// I2C packet (series of I2C operations)
typedef struct i2c_packet i2c_packet_t;
// I2C operation (single transfer)
typedef struct i2c_op i2c_op_t;
// Completion callback
typedef void (*i2c_callback_t)(void* context, i2c_packet_t* packet);
// I2C packet status
typedef enum {
I2C_STATUS_OK = 0, // Packet completed successfully
I2C_STATUS_PENDING = 1, // Packet is pending
I2C_STATUS_INVARG = 2, // Invalid packet/op parameters
I2C_STATUS_BUSY = 3, // Bus is busy
I2C_STATUS_TIMEOUT = 4, // Timeout occurred
I2C_STATUS_NACK = 5, // Device did not acknowledge
I2C_STATUS_ERROR = 6, // General error
I2C_STATUS_ABORTED = 7, // Packet was aborted
} i2c_status_t;
struct i2c_packet {
// Next packet in the driver queue
i2c_packet_t* next;
// I2C device address (7-bit address)
uint8_t address;
// Extra timeout (in milliseconds) added to the default timeout
// to finish each operation
uint16_t timeout;
// I2C_STATUS_xxx
i2c_status_t status;
// Number of operations
uint8_t op_count;
// Pointer to array of operations
i2c_op_t* ops;
// Completion callback function
i2c_callback_t callback;
// Callback context (user provided data)
void* context;
};
// I2C operation flags
#define I2C_FLAG_START 0x0001 // Generate START condition before the operation
#define I2C_FLAG_STOP 0x0002 // Generate STOP after the operation
#define I2C_FLAG_TX 0x0004 // Transmit data
#define I2C_FLAG_RX 0x0008 // Receive data
#define I2C_FLAG_EMBED 0x0010 // Embedded data (no reference)
// I2C operation flags constraints:
// 1) I2C_FLAG_TX | I2C_FLAG_RX is not allowed
// 2) if I2C_FLAG_EMBED is set, size must be <= 4
struct i2c_op {
// I2C_FLAG_xxx
uint16_t flags;
// Number of bytes to transfer
uint16_t size;
// Data to read or write
union {
// Pointer to data (I2C_FLAG_EMBED is not set)
void* ptr;
// Embedded data (I2C_FLAG_EMBED is set)
uint8_t data[4];
};
};
// Acquires I2C bus reference by index (0..2 according to the model)
//
// Returns NULL if bus is not available or can't be initialized.
//
// If the bus was not acquired before, it will be initialized.
i2c_bus_t* i2c_bus_open(uint8_t bus_index);
// Closes I2C bus handle
//
// After releasing the last bus reference, the bus will be deinitialized.
void i2c_bus_close(i2c_bus_t* bus);
// Submits I2C packet to the bus
//
// After submitting the packet, the packet status will be set to
// I2C_STATUS_PENDING until the packet is completed.
//
// The caller must not modify the packet (or data pointed by the packet)
// until the packet is completed (callback is called or status
// is not I2C_STATUS_PENDING).
//
// Returns:
// I2C_STATUS_OK -- packet was successfully submitted
i2c_status_t i2c_bus_submit(i2c_bus_t* bus, i2c_packet_t* packet);
// Aborts pending or queue packet
//
// Immediately after calling this function, the packet status will be
// set to I2C_STATUS_ABORTED and I2C driver will not access the packet anymore.
//
// If the packet is already completed, it does nothing.
// If the packet is queued it will be removed from the queue.
// If the packet is pending, it will be aborted.
// In any case completion callback will not be called.
void i2c_bus_abort(i2c_bus_t* bus, i2c_packet_t* packet);
// Returns I2C packet status
//
// If the packet is not completed yet, it returns I2C_STATUS_PENDING.
i2c_status_t i2c_packet_status(i2c_packet_t* packet);
// Waits until I2C packet is completed and returns its final status
i2c_status_t i2c_packet_wait(i2c_packet_t* packet);
// Helper function to submit and wait for the packet
static inline i2c_status_t i2c_bus_submit_and_wait(i2c_bus_t* bus,
i2c_packet_t* packet) {
i2c_status_t status = i2c_bus_submit(bus, packet);
if (status == I2C_STATUS_OK) {
status = i2c_packet_wait(packet);
}
return status;
}
/*
void example() {
i2c_bus_t* bus = i2c_bus_open(DEVICE_I2C_INSTANCE);
static uint8_t data_out;
static i2c_op_t ops[] = {
{
.flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
.size = 1,
.data = {0x01},
},
{
.flags = I2C_FLAG_RX,
.size = sizeof(data_out),
.ptr = &data_out,
},
};
static i2c_packet_t pkt = {
.callback = NULL,
.context = NULL,
.address = DEVICE_I2C_ADDRESS,
.op_count = ARRAY_LENGTH(ops),
.ops = ops,
};
status = i2c_bus_submit(bus, &pkt);
status = i2c_packet_wait(&pkt);
i2c_bus_close(&bus);
}
*/
#endif // TREZORHAL_I2C_BUS_H

@ -25,7 +25,7 @@ i2c_instance_t i2c_defs[I2C_COUNT] = {
.SclPin = I2C_INSTANCE_0_SCL_PIN,
.SdaPin = I2C_INSTANCE_0_SDA_PIN,
.PinAF = I2C_INSTANCE_0_PIN_AF,
.Reset = I2C_INSTANCE_0_RESET_FLG,
.Reset = I2C_INSTANCE_0_RESET_BIT,
},
#ifdef I2C_INSTANCE_1
{
@ -35,7 +35,7 @@ i2c_instance_t i2c_defs[I2C_COUNT] = {
.SclPin = I2C_INSTANCE_1_SCL_PIN,
.SdaPin = I2C_INSTANCE_1_SDA_PIN,
.PinAF = I2C_INSTANCE_1_PIN_AF,
.Reset = I2C_INSTANCE_1_RESET_FLG,
.Reset = I2C_INSTANCE_1_RESET_BIT,
},
#endif

@ -0,0 +1,886 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include STM32_HAL_H
#include TREZOR_BOARD
#include <string.h>
#include "common.h"
#include "i2c_bus.h"
#include "irq.h"
#include "systimer.h"
// I2C bus hardware definition
typedef struct {
// I2C controller registers
I2C_TypeDef* regs;
// SCL pin GPIO port
GPIO_TypeDef* scl_port;
// SDA pin GPIO port
GPIO_TypeDef* sda_port;
// SCL pin number
uint16_t scl_pin;
// SDA pin number
uint16_t sda_pin;
// Alternate function for SCL and SDA pins
uint8_t pin_af;
// Register for I2C controller reset
volatile uint32_t* reset_reg;
// Reset bit specific for this I2C controller
uint32_t reset_bit;
// I2C event IRQ number
uint32_t ev_irq;
// I2C error IRQ number
uint32_t er_irq;
// Guard time [us] between STOP and START condition.
// If zero, the guard time is not used.
uint16_t guard_time;
} i2c_bus_def_t;
// I2C bus hardware definitions
static const i2c_bus_def_t g_i2c_bus_def[I2C_COUNT] = {
{
.regs = I2C_INSTANCE_0,
.scl_port = I2C_INSTANCE_0_SCL_PORT,
.sda_port = I2C_INSTANCE_0_SDA_PORT,
.scl_pin = I2C_INSTANCE_0_SCL_PIN,
.sda_pin = I2C_INSTANCE_0_SDA_PIN,
.pin_af = I2C_INSTANCE_0_PIN_AF,
.reset_reg = I2C_INSTANCE_0_RESET_REG,
.reset_bit = I2C_INSTANCE_0_RESET_BIT,
.ev_irq = I2C_INSTANCE_0_EV_IRQn,
.er_irq = I2C_INSTANCE_0_ER_IRQn,
.guard_time = I2C_INSTANCE_0_GUARD_TIME,
},
#ifdef I2C_INSTANCE_1
{
.regs = I2C_INSTANCE_1,
.scl_port = I2C_INSTANCE_1_SCL_PORT,
.sda_port = I2C_INSTANCE_1_SDA_PORT,
.scl_pin = I2C_INSTANCE_1_SCL_PIN,
.sda_pin = I2C_INSTANCE_1_SDA_PIN,
.pin_af = I2C_INSTANCE_1_PIN_AF,
.reset_reg = I2C_INSTANCE_1_RESET_REG,
.reset_bit = I2C_INSTANCE_1_RESET_BIT,
.ev_irq = I2C_INSTANCE_1_EV_IRQn,
.er_irq = I2C_INSTANCE_1_ER_IRQn,
.guard_time = I2C_INSTANCE_1_GUARD_TIME,
},
#endif
#ifdef I2C_INSTANCE_2
{
.regs = I2C_INSTANCE_2,
.scl_port = I2C_INSTANCE_2_SCL_PORT,
.sda_port = I2C_INSTANCE_2_SDA_PORT,
.scl_pin = I2C_INSTANCE_2_SCL_PIN,
.sda_pin = I2C_INSTANCE_2_SDA_PIN,
.pin_af = I2C_INSTANCE_2_PIN_AF,
.reset_reg = I2C_INSTANCE_2_RESET_REG,
.reset_bit = I2C_INSTANCE_2_RESET_BIT,
.ev_irq = I2C_INSTANCE_2_EV_IRQn,
.er_irq = I2C_INSTANCE_2_ER_IRQn,
.guard_time = I2C_INSTANCE_2_GUARD_TIME,
},
#endif
};
struct i2c_bus {
// Number of references to the bus
// (0 means the bus is not initialized)
uint32_t refcount;
// Hardware definition
const i2c_bus_def_t* def;
// Timer for timeout handling
systimer_t* timer;
// Head of the packet queue
// (this packet is currently being processed)
i2c_packet_t* queue_head;
// Tail of the packet queue
// (this packet is the last in the queue)
i2c_packet_t* queue_tail;
// Next operation index in the current packet
// == 0 => no operation is being processed
// == queue_head->op_count => no more operations
int next_op;
// Current operation address byte
uint8_t addr_byte;
// Points to the data buffer of the current operation
uint8_t* buff_ptr;
// Remaining number of bytes of the buffer to transfer
uint16_t buff_size;
// Remaining number of bytes of the current operation
// (if the transfer is split into multiple operations it
// may be different from buff_size)
uint16_t transfer_size;
// For case of split transfer, points to the next operation
// that is part of the current transfer
int transfer_op;
// Set if the STOP condition is requested after the current operation
// when data transfer is completed.
bool stop_requested;
// Set if pending transaction is being aborted
bool abort_pending;
// Flag indicating that the completion callback is being executed
bool callback_executed;
// The last time [us] the STOP condition was issued
uint64_t stop_time;
};
// I2C bus driver instances
static i2c_bus_t g_i2c_bus_driver[I2C_COUNT] = {0};
// Check if the I2C bus pointer is valid
static inline bool i2c_bus_ptr_valid(i2c_bus_t* bus) {
if (bus >= &g_i2c_bus_driver[0] && bus < &g_i2c_bus_driver[I2C_COUNT]) {
uintptr_t offset = (uintptr_t)bus - (uintptr_t)&g_i2c_bus_driver[0];
if (offset % sizeof(i2c_bus_t) == 0) {
return bus->refcount > 0;
}
}
return false;
}
// Using calculation from STM32CubeMX
// PCLKx as source, assumed 160MHz
// Fast mode, freq = 400kHz, Rise time = 250ns, Fall time = 100ns
// Fast mode, freq = 200kHz, Rise time = 250ns, Fall time = 100ns
// SCLH and SCLL are manually modified to achieve more symmetric clock
#define I2C_TIMING_400000_Hz 0x30D22728
#define I2C_TIMING_200000_Hz 0x30D2595A
#define I2C_TIMING I2C_TIMING_200000_Hz
// We expect the I2C bus to be running at 100kHz
// and max response time of the device is 1000us
#define I2C_BUS_CHAR_TIMEOUT 110 // us
#define I2C_BUS_OP_TIMEOUT 1000 // us
#define I2C_BUS_TIMEOUT(n) \
((I2C_BUS_CHAR_TIMEOUT * 2 + I2C_BUS_OP_TIMEOUT + 999) / 1000)
// forward declarations
static void i2c_bus_timer_callback(void* context);
static void i2c_bus_head_continue(i2c_bus_t* bus);
static void i2c_bus_reset(i2c_bus_t* bus) {
const i2c_bus_def_t* def = bus->def;
// Reset I2C peripheral
*def->reset_reg |= def->reset_bit;
*def->reset_reg &= ~def->reset_bit;
I2C_TypeDef* regs = def->regs;
// Configure I2C peripheral
uint32_t pclk_hz = HAL_RCC_GetPCLK1Freq();
uint32_t pclk_mhz = I2C_FREQRANGE(pclk_hz);
uint32_t i2c_speed_hz = 200000;
regs->CR1 = 0;
regs->TRISE = I2C_RISE_TIME(pclk_mhz, i2c_speed_hz);
regs->CR2 = pclk_mhz;
regs->CCR = I2C_SPEED(pclk_hz, i2c_speed_hz, I2C_DUTYCYCLE_16_9);
regs->FLTR = 0;
regs->OAR1 = 0;
regs->OAR2 = 0;
regs->CR1 |= I2C_CR1_PE;
}
static bool i2c_bus_init(i2c_bus_t* bus, int bus_index) {
memset(bus, 0, sizeof(i2c_bus_t));
const i2c_bus_def_t* def = &g_i2c_bus_def[bus_index];
bus->def = def;
switch (bus_index) {
case 0:
// enable I2C clock
I2C_INSTANCE_0_CLK_EN();
I2C_INSTANCE_0_SCL_CLK_EN();
I2C_INSTANCE_0_SDA_CLK_EN();
break;
#ifdef I2C_INSTANCE_1
case 1:
I2C_INSTANCE_1_CLK_EN();
I2C_INSTANCE_1_SCL_CLK_EN();
I2C_INSTANCE_1_SDA_CLK_EN();
break;
#endif
#ifdef I2C_INSTANCE_2
case 2:
I2C_INSTANCE_2_CLK_EN();
I2C_INSTANCE_2_SCL_CLK_EN();
I2C_INSTANCE_2_SDA_CLK_EN();
break;
#endif
default:
return false;
}
GPIO_InitTypeDef GPIO_InitStructure = {0};
// Configure SDA and SCL as open-drain output
// and connect to the I2C peripheral
GPIO_InitStructure.Mode = GPIO_MODE_AF_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->scl_pin;
HAL_GPIO_Init(def->scl_port, &GPIO_InitStructure);
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->sda_pin;
HAL_GPIO_Init(def->sda_port, &GPIO_InitStructure);
i2c_bus_reset(bus);
NVIC_SetPriority(def->ev_irq, IRQ_PRI_NORMAL);
NVIC_SetPriority(def->er_irq, IRQ_PRI_NORMAL);
NVIC_EnableIRQ(def->ev_irq);
NVIC_EnableIRQ(def->er_irq);
bus->timer = systimer_create(i2c_bus_timer_callback, bus);
if (bus->timer == NULL) {
return false;
}
return true;
}
static void i2c_bus_deinit(i2c_bus_t* bus) {
const i2c_bus_def_t* def = bus->def;
systimer_delete(bus->timer);
NVIC_DisableIRQ(def->ev_irq);
NVIC_DisableIRQ(def->er_irq);
I2C_TypeDef* regs = def->regs;
// Disable I2C peripheral
regs->CR1 = 0;
// Reset I2C peripheral
*def->reset_reg |= def->reset_bit;
*def->reset_reg &= ~def->reset_bit;
}
i2c_bus_t* i2c_bus_open(uint8_t bus_index) {
if (bus_index >= I2C_COUNT) {
return NULL;
}
i2c_bus_t* bus = &g_i2c_bus_driver[bus_index];
if (bus->refcount == 0) {
if (!i2c_bus_init(bus, bus_index)) {
i2c_bus_deinit(bus);
return NULL;
}
}
++bus->refcount;
return bus;
}
void i2c_bus_close(i2c_bus_t* bus) {
if (i2c_bus_ptr_valid(bus)) {
// Bus reference is invalid or not initialized
return;
}
if (bus->refcount > 0) {
if (--bus->refcount == 0) {
i2c_bus_deinit(bus);
}
}
}
i2c_status_t i2c_packet_status(i2c_packet_t* packet) {
uint32_t irq_state = disable_irq();
i2c_status_t status = packet->status;
enable_irq(irq_state);
return status;
}
i2c_status_t i2c_packet_wait(i2c_packet_t* packet) {
while (true) {
i2c_status_t status = i2c_packet_status(packet);
if (status != I2C_STATUS_PENDING) {
return status;
}
// Enter sleep mode and wait for any interrupt
__WFI();
}
}
// Invokes the packet completion callback
static inline void i2c_bus_invoke_callback(i2c_bus_t* bus, i2c_packet_t* packet,
i2c_status_t status) {
packet->status = status;
if (packet->callback) {
bus->callback_executed = true;
packet->callback(packet->context, packet);
bus->callback_executed = false;
}
}
// Appends the packet to the end of the queue
// Returns true if the queue was empty before
// Expects disabled IRQ or calling from IRQ context
static inline bool i2c_bus_add_packet(i2c_bus_t* bus, i2c_packet_t* packet) {
if (bus->queue_tail == NULL) {
bus->queue_head = packet;
bus->queue_tail = packet;
return true;
} else {
bus->queue_tail->next = packet;
bus->queue_tail = packet;
return false;
}
}
// Removes the packet from the queue (if present)
// Returns true if the removed we removed head of the queue
// Expects disabled IRQ or calling from IRQ context
static inline bool i2c_bus_remove_packet(i2c_bus_t* bus, i2c_packet_t* packet) {
if (packet == bus->queue_head) {
// Remove head of the queue
bus->queue_head = packet->next;
// If the removed paacket was also the tail, reset the tail
if (bus->queue_tail == packet) {
bus->queue_tail = NULL;
}
packet->next = NULL;
return true;
}
// Remove from the middle or tail of the queue
i2c_packet_t* p = bus->queue_head;
while (p->next != NULL && p->next != packet) {
p = p->next;
}
if (p->next == packet) {
// The packet found in the queue, remove it
p->next = packet->next;
// Update the tail if necessary
if (bus->queue_tail == packet) {
bus->queue_tail = p;
}
packet->next = NULL;
}
return false;
}
i2c_status_t i2c_bus_submit(i2c_bus_t* bus, i2c_packet_t* packet) {
if (!i2c_bus_ptr_valid(bus) || packet == NULL) {
// Invalid bus or packet
return I2C_STATUS_ERROR;
}
if (packet->next != NULL) {
// Packet is already queued
return I2C_STATUS_ERROR;
}
packet->status = I2C_STATUS_PENDING;
// Insert packet into the queue
uint32_t irq_state = disable_irq();
if (i2c_bus_add_packet(bus, packet)) {
// The queue was empty, start the operation
if (!bus->callback_executed && !bus->abort_pending) {
i2c_bus_head_continue(bus);
}
}
enable_irq(irq_state);
return I2C_STATUS_OK;
}
void i2c_bus_abort(i2c_bus_t* bus, i2c_packet_t* packet) {
if (!i2c_bus_ptr_valid(bus) || packet == NULL) {
// Invalid bus or packet
return;
}
uint32_t irq_state = disable_irq();
if (packet->status == I2C_STATUS_PENDING) {
if (i2c_bus_remove_packet(bus, packet) && bus->next_op > 0) {
// The packet was being processed
// Reset internal state
bus->next_op = 0;
bus->buff_ptr = NULL;
bus->buff_size = 0;
bus->transfer_size = 0;
bus->transfer_op = 0;
// Inform interrupt handler about pending abort
bus->abort_pending = true;
bus->stop_requested = true;
// Abort operation may fail if the bus is busy or noisy
// so we need to set a timeout.
systimer_set(bus->timer, I2C_BUS_TIMEOUT(2));
}
packet->status = I2C_STATUS_ABORTED;
}
enable_irq(irq_state);
}
// Completes the current packet by removing it from the queue
// an invoking the completion callback
//
// Must be called with IRQ disabled or from IRQ context
// Expects the operation is finished
static void i2c_bus_head_complete(i2c_bus_t* bus, i2c_status_t status) {
i2c_packet_t* packet = bus->queue_head;
if (packet != NULL) {
// Remove packet from the queue
i2c_bus_remove_packet(bus, packet);
// Reset internal state
bus->next_op = 0;
bus->buff_ptr = NULL;
bus->buff_size = 0;
bus->transfer_size = 0;
bus->transfer_op = 0;
bus->abort_pending = false;
systimer_unset(bus->timer);
// Invoke the completion callback
i2c_bus_invoke_callback(bus, packet, status);
}
}
// Starts the next operation in the packet by
// programming the I2C controller
//
// Must be called with IRQ disabled or from IRQ context
// Expects no other operation is being processed
static void i2c_bus_head_continue(i2c_bus_t* bus) {
I2C_TypeDef* regs = bus->def->regs;
if (bus->stop_requested) {
// Issue STOP condition
regs->CR1 |= I2C_CR1_STOP;
if (bus->def->guard_time > 0) {
bus->stop_time = systick_us();
}
bus->stop_requested = false;
}
if (bus->abort_pending) {
systimer_unset(bus->timer);
bus->abort_pending = false;
}
uint32_t cr1 = regs->CR1;
cr1 &= ~(I2C_CR1_POS | I2C_CR1_ACK | I2C_CR1_STOP | I2C_CR1_START);
uint32_t cr2 = regs->CR2;
cr2 &= ~(I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN);
if (bus->queue_head != NULL) {
i2c_packet_t* packet = bus->queue_head;
if (bus->next_op < packet->op_count) {
i2c_op_t* op = &packet->ops[bus->next_op++];
// Get data ptr and data length
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
// Calculate transfer size
bus->transfer_size = bus->buff_size;
bus->transfer_op = bus->next_op;
// Include following operations in the transfer if:
// 1) We are not processing the last operation
// 2) STOP condition is not requested in the current operation
// 3) START condition is not requested in the next operation
// 4) The next operation has the same direction
while ((bus->next_op != packet->op_count) &&
((op->flags & I2C_FLAG_STOP) == 0) &&
(((op + 1)->flags & I2C_FLAG_START) == 0) &&
(((op + 1)->flags & I2C_FLAG_TX) == (op->flags & I2C_FLAG_TX))) {
// Move to the next operation
op = &packet->ops[bus->next_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->transfer_size += MIN(op->size, sizeof(op->data));
} else {
bus->transfer_size += op->size;
}
}
// STOP conditition:
// 1) if it is explicitly requested
// 2) if it is the last operation in the packet
bus->stop_requested = ((op->flags & I2C_FLAG_STOP) != 0) ||
(bus->next_op == packet->op_count);
// Calculate address byte
bus->addr_byte = packet->address << 1;
// ACK, POS, ITBUFEN flags are set based on the operation
if (bus->transfer_size > 0) {
if (op->flags & I2C_FLAG_TX) {
cr2 |= I2C_CR2_ITBUFEN;
} else if (op->flags & I2C_FLAG_RX) {
bus->addr_byte |= 1; // Set RW bit to 1 (READ)
if (bus->transfer_size == 1) {
cr2 |= I2C_CR2_ITBUFEN;
} else if (bus->transfer_size == 2) {
cr1 |= I2C_CR1_POS;
} else if (bus->transfer_size == 3) {
cr1 |= I2C_CR1_ACK;
} else if (bus->transfer_size > 3) {
cr2 |= I2C_CR2_ITBUFEN;
cr1 |= I2C_CR1_ACK;
}
}
}
// Enable event and error interrupts
cr2 |= I2C_CR2_ITEVTEN | I2C_CR2_ITERREN;
// Generate start condition
// (this also clears all status flags)
cr1 |= I2C_CR1_START;
// Each operation has its own timeout calculated
// based on the number of bytes to transfer and the bus speed +
// expected operation overhead
systimer_set(bus->timer,
I2C_BUS_TIMEOUT(bus->transfer_size) + packet->timeout);
// Guard time between operations STOP and START condition
if (bus->def->guard_time > 0) {
// Add 5us as a safety margin since the stop_time was set before the
// STOP condition was issued
uint16_t guard_time = bus->def->guard_time + 5;
while (systick_us() - bus->stop_time < guard_time)
;
}
}
// Clear BTF flag
(void)regs->DR;
}
regs->CR1 = cr1;
regs->CR2 = cr2;
}
// Timer callback handling I2C bus timeout
static void i2c_bus_timer_callback(void* context) {
i2c_bus_t* bus = (i2c_bus_t*)context;
if (bus->abort_pending) {
// This may be cause by the bus being busy/noisy.
// Reset I2C Controller
i2c_bus_reset(bus);
// Start the next packet
i2c_bus_head_continue(bus);
} else {
// Timeout during normal operation occurred
i2c_packet_t* packet = bus->queue_head;
if (packet != NULL) {
// Determine the status based on the current bus state
I2C_TypeDef* regs = bus->def->regs;
i2c_status_t status;
if ((regs->CR1 & I2C_CR1_START) && (regs->SR2 & I2C_SR2_BUSY)) {
// START condition was issued but the bus is still busy
status = I2C_STATUS_BUSY;
} else {
status = I2C_STATUS_TIMEOUT;
}
// Abort pending packet
i2c_bus_abort(bus, packet);
// Invoke the completion callback
i2c_bus_invoke_callback(bus, packet, status);
}
}
}
static uint8_t i2c_bus_read_buff(i2c_bus_t* bus) {
if (bus->transfer_size > 0) {
while (bus->buff_size == 0 && bus->transfer_op < bus->next_op) {
i2c_op_t* op = &bus->queue_head->ops[bus->transfer_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
}
--bus->transfer_size;
if (bus->buff_size > 0) {
--bus->buff_size;
return *bus->buff_ptr++;
}
}
return 0;
}
static void i2c_bus_write_buff(i2c_bus_t* bus, uint8_t data) {
if (bus->transfer_size > 0) {
while (bus->buff_size == 0 && bus->transfer_op < bus->next_op) {
i2c_op_t* op = &bus->queue_head->ops[bus->transfer_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
}
--bus->transfer_size;
if (bus->buff_size > 0) {
*bus->buff_ptr++ = data;
--bus->buff_size;
}
}
}
// I2C bus event interrupt handler
static void i2c_bus_ev_handler(i2c_bus_t* bus) {
I2C_TypeDef* regs = bus->def->regs;
uint32_t sr1 = regs->SR1;
if (sr1 & I2C_SR1_SB) {
// START condition generated
// Send the address byte
regs->DR = bus->addr_byte;
// Operation cannot be aborted at this point.
// We need to wait for ADDR flag.
} else if (sr1 & I2C_SR1_ADDR) {
// Address sent and ACKed by the slave
// By reading SR2 we clear ADDR flag and start the data transfer
regs->SR2;
if (bus->abort_pending) {
// Only TX operation can be aborted at this point
// For RX operation, we need to wait for the first byte
if ((bus->addr_byte & 1) == 0) {
// Issue STOP condition and start the next packet
i2c_bus_head_continue(bus);
}
} else if (bus->transfer_size == 0) {
// Operation contains only address without any data
if (bus->next_op == bus->queue_head->op_count) {
i2c_bus_head_complete(bus, I2C_STATUS_OK);
}
i2c_bus_head_continue(bus);
}
} else if ((bus->addr_byte & 1) == 0) {
// Data transmit phase
if (bus->abort_pending) {
// Issue STOP condition and start the next packet
i2c_bus_head_continue(bus);
} else if ((sr1 & I2C_SR1_TXE) && (regs->CR2 & I2C_CR2_ITBUFEN)) {
// I2C controller transmit buffer is empty.
// The interrupt flag is cleared by writing the DR register.
if (bus->transfer_size > 0) {
// Send the next byte
regs->DR = i2c_bus_read_buff(bus);
if (bus->transfer_size == 0) {
// All data bytes were transmitted
// Disable RXNE interrupt and wait for BTF
regs->CR2 &= ~I2C_CR2_ITBUFEN;
}
}
} else if (sr1 & I2C_SR1_BTF) {
if (bus->transfer_size == 0) {
// All data bytes were shifted out
if (bus->next_op == bus->queue_head->op_count) {
// Last operation in the packet
i2c_bus_head_complete(bus, I2C_STATUS_OK);
}
i2c_bus_head_continue(bus);
}
}
} else { // Data receive phase
if (bus->abort_pending) {
regs->CR1 &= ~(I2C_CR1_ACK | I2C_CR1_POS);
(void)regs->DR;
// Issue STOP condition and start the next packet
i2c_bus_head_continue(bus);
} else if ((sr1 & I2C_SR1_RXNE) && (regs->CR2 & I2C_CR2_ITBUFEN)) {
uint8_t received_byte = regs->DR;
if (bus->transfer_size > 0) {
// Receive the next byte
i2c_bus_write_buff(bus, received_byte);
if (bus->transfer_size == 3) {
// 3 bytes left to receive
// Disable RXNE interrupt and wait for BTF
regs->CR2 &= ~I2C_CR2_ITBUFEN;
} else if (bus->transfer_size == 0) {
// All data bytes were received
// We get here only in case of 1 byte transfers
if (bus->next_op == bus->queue_head->op_count) {
// Last operation in the packet
i2c_bus_head_complete(bus, I2C_STATUS_OK);
}
i2c_bus_head_continue(bus);
}
}
} else if (sr1 & I2C_SR1_BTF) {
if (bus->transfer_size == 3) {
// 3 bytes left to receive
regs->CR1 &= ~I2C_CR1_ACK;
i2c_bus_write_buff(bus, regs->DR);
} else if (bus->transfer_size == 2) {
// 2 left bytes are already in DR a shift register
if (bus->stop_requested) {
// Issue STOP condition before reading the 2 last bytes
regs->CR1 |= I2C_CR1_STOP;
if (bus->def->guard_time > 0) {
bus->stop_time = systick_us();
}
bus->stop_requested = false;
}
i2c_bus_write_buff(bus, regs->DR);
i2c_bus_write_buff(bus, regs->DR);
if (bus->next_op == bus->queue_head->op_count) {
i2c_bus_head_complete(bus, I2C_STATUS_OK);
}
i2c_bus_head_continue(bus);
}
}
}
}
// I2C bus error interrupt handler
static void i2c_bus_er_handler(i2c_bus_t* bus) {
I2C_TypeDef* regs = bus->def->regs;
uint32_t sr1 = regs->SR1;
// Clear error flags
regs->SR1 &= ~(I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR);
if (sr1 & I2C_SR1_AF) {
// NACK received
if (bus->abort_pending) {
// Start the next packet
i2c_bus_head_continue(bus);
} else if (bus->next_op > 0) {
// Complete packet with error
i2c_bus_head_complete(bus, I2C_STATUS_NACK);
// Issue stop condition and start the next packet
bus->stop_requested = true;
i2c_bus_head_continue(bus);
} else {
// Invalid state
}
}
if (sr1 & I2C_SR1_ARLO) {
if (bus->abort_pending) {
// Packet aborted or invalid state
// Start the next packet
bus->stop_requested = false;
i2c_bus_head_continue(bus);
} else if (bus->next_op > 0) {
// Arbitration lost, complete packet with error
i2c_bus_head_complete(bus, I2C_STATUS_ERROR);
// Start the next packet
bus->stop_requested = false;
i2c_bus_head_continue(bus);
}
}
if (sr1 & I2C_SR1_BERR) {
// Bus error
// Ignore and continue with pending operation
}
}
// Interrupt handlers
#ifdef I2C_INSTANCE_0
void I2C_INSTANCE_0_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[0]);
}
void I2C_INSTANCE_0_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[0]);
}
#endif
#ifdef I2C_INSTANCE_1
void I2C_INSTANCE_1_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[1]);
}
void I2C_INSTANCE_1_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[1]);
}
#endif
#ifdef I2C_INSTANCE_2
void I2C_INSTANCE_2_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[2]);
}
void I2C_INSTANCE_2_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[2]);
}
#endif

@ -29,7 +29,7 @@
//
// Consider different implementation (i.e. priority queue
// using binary heap if MAX_SYSTIMERS exceeds 10 or more)
#define MAX_SYSTIMERS 4
#define MAX_SYSTIMERS 8
// User timer instance
struct systimer {

@ -0,0 +1,887 @@
/*
* This file is part of the Trezor project, https://trezor.io/
*
* Copyright (c) SatoshiLabs
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include STM32_HAL_H
#include TREZOR_BOARD
#include <string.h>
#include "common.h"
#include "i2c_bus.h"
#include "irq.h"
#include "systimer.h"
// I2C bus hardware definition
typedef struct {
// I2C controller registers
I2C_TypeDef* regs;
// SCL pin GPIO port
GPIO_TypeDef* scl_port;
// SDA pin GPIO port
GPIO_TypeDef* sda_port;
// SCL pin number
uint16_t scl_pin;
// SDA pin number
uint16_t sda_pin;
// Alternate function for SCL and SDA pins
uint8_t pin_af;
// Register for I2C controller reset
volatile uint32_t* reset_reg;
// Reset bit specific for this I2C controller
uint32_t reset_bit;
// I2C event IRQ number
uint32_t ev_irq;
// I2C error IRQ number
uint32_t er_irq;
// Guard time [us] between STOP and START condition.
// If zero, the guard time is not used.
uint16_t guard_time;
} i2c_bus_def_t;
// I2C bus hardware definitions
static const i2c_bus_def_t g_i2c_bus_def[I2C_COUNT] = {
{
.regs = I2C_INSTANCE_0,
.scl_port = I2C_INSTANCE_0_SCL_PORT,
.sda_port = I2C_INSTANCE_0_SDA_PORT,
.scl_pin = I2C_INSTANCE_0_SCL_PIN,
.sda_pin = I2C_INSTANCE_0_SDA_PIN,
.pin_af = I2C_INSTANCE_0_PIN_AF,
.reset_reg = I2C_INSTANCE_0_RESET_REG,
.reset_bit = I2C_INSTANCE_0_RESET_BIT,
.ev_irq = I2C_INSTANCE_0_EV_IRQn,
.er_irq = I2C_INSTANCE_0_ER_IRQn,
.guard_time = I2C_INSTANCE_0_GUARD_TIME,
},
#ifdef I2C_INSTANCE_1
{
.regs = I2C_INSTANCE_1,
.scl_port = I2C_INSTANCE_1_SCL_PORT,
.sda_port = I2C_INSTANCE_1_SDA_PORT,
.scl_pin = I2C_INSTANCE_1_SCL_PIN,
.sda_pin = I2C_INSTANCE_1_SDA_PIN,
.pin_af = I2C_INSTANCE_1_PIN_AF,
.reset_reg = I2C_INSTANCE_1_RESET_REG,
.reset_bit = I2C_INSTANCE_1_RESET_BIT,
.ev_irq = I2C_INSTANCE_1_EV_IRQn,
.er_irq = I2C_INSTANCE_1_ER_IRQn,
.guard_time = I2C_INSTANCE_1_GUARD_TIME,
},
#endif
#ifdef I2C_INSTANCE_2
{
.regs = I2C_INSTANCE_2,
.scl_port = I2C_INSTANCE_2_SCL_PORT,
.sda_port = I2C_INSTANCE_2_SDA_PORT,
.scl_pin = I2C_INSTANCE_2_SCL_PIN,
.sda_pin = I2C_INSTANCE_2_SDA_PIN,
.pin_af = I2C_INSTANCE_2_PIN_AF,
.reset_reg = I2C_INSTANCE_2_RESET_REG,
.reset_bit = I2C_INSTANCE_2_RESET_BIT,
.ev_irq = I2C_INSTANCE_2_EV_IRQn,
.er_irq = I2C_INSTANCE_2_ER_IRQn,
.guard_time = I2C_INSTANCE_2_GUARD_TIME,
},
#endif
};
struct i2c_bus {
// Number of references to the bus
// (0 means the bus is not initialized)
uint32_t refcount;
// Hardware definition
const i2c_bus_def_t* def;
// Timer for timeout handling
systimer_t* timer;
// Head of the packet queue
// (this packet is currently being processed)
i2c_packet_t* queue_head;
// Tail of the packet queue
// (this packet is the last in the queue)
i2c_packet_t* queue_tail;
// Next operation index in the current packet
// == 0 => no operation is being processed
// == queue_head->op_count => no more operations
int next_op;
// Points to the data buffer of the current operation
uint8_t* buff_ptr;
// Remaining number of bytes of the buffer to transfer
uint16_t buff_size;
// Remaining number of bytes of the current operation
// (if the transfer is split into multiple operations it
// may be different from buff_size)
uint16_t transfer_size;
// For case of split transfer, points to the next operation
// that is part of the current transfer
int transfer_op;
// Set if the STOP condition is requested after the current operation
// when data transfer is completed.
bool stop_requested;
// Set if pending transaction is being aborted
bool abort_pending;
// Set if NACK was detected
bool nack;
// Data for clearing TXIS interrupt flag
// during an invalid or abort state
uint8_t dummy_data;
// Flag indicating that the completion callback is being executed
bool callback_executed;
// The last time [us] the STOP condition was issued
uint64_t stop_time;
};
// I2C bus driver instances
static i2c_bus_t g_i2c_bus_driver[I2C_COUNT] = {0};
// Check if the I2C bus pointer is valid
static inline bool i2c_bus_ptr_valid(i2c_bus_t* bus) {
if (bus >= &g_i2c_bus_driver[0] && bus < &g_i2c_bus_driver[I2C_COUNT]) {
uintptr_t offset = (uintptr_t)bus - (uintptr_t)&g_i2c_bus_driver[0];
if (offset % sizeof(i2c_bus_t) == 0) {
return bus->refcount > 0;
}
}
return false;
}
// Using calculation from STM32CubeMX
// PCLKx as source, assumed 160MHz
// Fast mode, freq = 400kHz, Rise time = 250ns, Fall time = 100ns
// Fast mode, freq = 200kHz, Rise time = 250ns, Fall time = 100ns
// SCLH and SCLL are manually modified to achieve more symmetric clock
#define I2C_TIMING_400000_Hz 0x30D22728
#define I2C_TIMING_200000_Hz 0x30D2595A
#define I2C_TIMING I2C_TIMING_200000_Hz
// We expect the I2C bus to be running at 100kHz
// and max response time of the device is 1000us
#define I2C_BUS_CHAR_TIMEOUT 110 // us
#define I2C_BUS_OP_TIMEOUT 1000 // us
#define I2C_BUS_TIMEOUT(n) \
((I2C_BUS_CHAR_TIMEOUT * 2 + I2C_BUS_OP_TIMEOUT + 999) / 1000)
// forward declarations
static void i2c_bus_timer_callback(void* context);
static void i2c_bus_head_continue(i2c_bus_t* bus);
static void i2c_bus_unlock(i2c_bus_t* bus) {
const i2c_bus_def_t* def = bus->def;
GPIO_InitTypeDef GPIO_InitStructure = {0};
// Set SDA and SCL high
HAL_GPIO_WritePin(def->sda_port, def->sda_pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(def->scl_port, def->scl_pin, GPIO_PIN_SET);
// Configure SDA and SCL as open-drain output
// and connect to the I2C peripheral
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->scl_pin;
HAL_GPIO_Init(def->scl_port, &GPIO_InitStructure);
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->sda_pin;
HAL_GPIO_Init(def->sda_port, &GPIO_InitStructure);
uint32_t clock_count = 16;
while ((HAL_GPIO_ReadPin(def->sda_port, def->sda_pin) == GPIO_PIN_RESET) &&
(clock_count-- > 0)) {
// Clock SCL
HAL_GPIO_WritePin(def->scl_port, def->scl_pin, GPIO_PIN_RESET);
systick_delay_us(10);
HAL_GPIO_WritePin(def->scl_port, def->scl_pin, GPIO_PIN_SET);
systick_delay_us(10);
}
}
static bool i2c_bus_init(i2c_bus_t* bus, int bus_index) {
memset(bus, 0, sizeof(i2c_bus_t));
const i2c_bus_def_t* def = &g_i2c_bus_def[bus_index];
bus->def = def;
switch (bus_index) {
case 0:
// enable I2C clock
I2C_INSTANCE_0_CLK_EN();
I2C_INSTANCE_0_SCL_CLK_EN();
I2C_INSTANCE_0_SDA_CLK_EN();
break;
#ifdef I2C_INSTANCE_1
case 1:
I2C_INSTANCE_1_CLK_EN();
I2C_INSTANCE_1_SCL_CLK_EN();
I2C_INSTANCE_1_SDA_CLK_EN();
break;
#endif
#ifdef I2C_INSTANCE_2
case 2:
I2C_INSTANCE_2_CLK_EN();
I2C_INSTANCE_2_SCL_CLK_EN();
I2C_INSTANCE_2_SDA_CLK_EN();
break;
#endif
default:
return false;
}
// Unlocks potentialy locked I2C bus by
// generating 9 clock pulses on SCL while SDA is low
i2c_bus_unlock(bus);
GPIO_InitTypeDef GPIO_InitStructure = {0};
// Configure SDA and SCL as open-drain output
// and connect to the I2C peripheral
GPIO_InitStructure.Mode = GPIO_MODE_AF_OD;
GPIO_InitStructure.Pull = GPIO_NOPULL;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->scl_pin;
HAL_GPIO_Init(def->scl_port, &GPIO_InitStructure);
GPIO_InitStructure.Alternate = def->pin_af;
GPIO_InitStructure.Pin = def->sda_pin;
HAL_GPIO_Init(def->sda_port, &GPIO_InitStructure);
// Reset I2C peripheral
*def->reset_reg |= def->reset_bit;
*def->reset_reg &= ~def->reset_bit;
I2C_TypeDef* regs = def->regs;
// Configure I2C peripheral
regs->CR1 = 0;
regs->TIMINGR = I2C_TIMING;
regs->CR2 = 0;
regs->OAR1 = 0;
regs->OAR2 = 0;
regs->CR1 |= I2C_CR1_PE;
// Configure I2C interrupts
regs->CR1 |= I2C_CR1_ERRIE | I2C_CR1_NACKIE | I2C_CR1_STOPIE | I2C_CR1_TCIE |
I2C_CR1_RXIE | I2C_CR1_TXIE;
NVIC_SetPriority(def->ev_irq, IRQ_PRI_NORMAL);
NVIC_SetPriority(def->er_irq, IRQ_PRI_NORMAL);
NVIC_EnableIRQ(def->ev_irq);
NVIC_EnableIRQ(def->er_irq);
bus->timer = systimer_create(i2c_bus_timer_callback, bus);
if (bus->timer == NULL) {
return false;
}
return true;
}
static void i2c_bus_deinit(i2c_bus_t* bus) {
const i2c_bus_def_t* def = bus->def;
systimer_delete(bus->timer);
NVIC_DisableIRQ(def->ev_irq);
NVIC_DisableIRQ(def->er_irq);
I2C_TypeDef* regs = def->regs;
// Disable I2C peripheral
regs->CR1 = 0;
// Reset I2C peripheral
*def->reset_reg |= def->reset_bit;
*def->reset_reg &= ~def->reset_bit;
}
i2c_bus_t* i2c_bus_open(uint8_t bus_index) {
if (bus_index >= I2C_COUNT) {
return NULL;
}
i2c_bus_t* bus = &g_i2c_bus_driver[bus_index];
if (bus->refcount == 0) {
if (!i2c_bus_init(bus, bus_index)) {
i2c_bus_deinit(bus);
return NULL;
}
}
++bus->refcount;
return bus;
}
void i2c_bus_close(i2c_bus_t* bus) {
if (!i2c_bus_ptr_valid(bus)) {
return;
}
if (bus->refcount > 0) {
if (--bus->refcount == 0) {
i2c_bus_deinit(bus);
}
}
}
i2c_status_t i2c_packet_status(i2c_packet_t* packet) {
uint32_t irq_state = disable_irq();
i2c_status_t status = packet->status;
enable_irq(irq_state);
return status;
}
i2c_status_t i2c_packet_wait(i2c_packet_t* packet) {
while (true) {
i2c_status_t status = i2c_packet_status(packet);
if (status != I2C_STATUS_PENDING) {
return status;
}
// Enter sleep mode and wait for any interrupt
__WFI();
}
}
static uint8_t i2c_bus_read_buff(i2c_bus_t* bus) {
if (bus->transfer_size > 0) {
while (bus->buff_size == 0 && bus->transfer_op < bus->next_op) {
i2c_op_t* op = &bus->queue_head->ops[bus->transfer_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
}
--bus->transfer_size;
if (bus->buff_size > 0) {
--bus->buff_size;
return *bus->buff_ptr++;
}
}
return 0;
}
static void i2c_bus_write_buff(i2c_bus_t* bus, uint8_t data) {
if (bus->transfer_size > 0) {
while (bus->buff_size == 0 && bus->transfer_op < bus->next_op) {
i2c_op_t* op = &bus->queue_head->ops[bus->transfer_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
}
--bus->transfer_size;
if (bus->buff_size > 0) {
*bus->buff_ptr++ = data;
--bus->buff_size;
}
}
}
// Invokes the packet completion callback
static inline void i2c_bus_invoke_callback(i2c_bus_t* bus, i2c_packet_t* packet,
i2c_status_t status) {
packet->status = status;
if (packet->callback) {
bus->callback_executed = true;
packet->callback(packet->context, packet);
bus->callback_executed = false;
}
}
// Appends the packet to the end of the queue
// Returns true if the queue was empty before
// Expects disabled IRQ or calling from IRQ context
static inline bool i2c_bus_add_packet(i2c_bus_t* bus, i2c_packet_t* packet) {
if (bus->queue_tail == NULL) {
bus->queue_head = packet;
bus->queue_tail = packet;
return true;
} else {
bus->queue_tail->next = packet;
bus->queue_tail = packet;
return false;
}
}
// Removes the packet from the queue (if present)
// Returns true if the removed we removed head of the queue
// Expects disabled IRQ or calling from IRQ context
static inline bool i2c_bus_remove_packet(i2c_bus_t* bus, i2c_packet_t* packet) {
if (packet == bus->queue_head) {
// Remove head of the queue
bus->queue_head = packet->next;
// If the removed paacket was also the tail, reset the tail
if (bus->queue_tail == packet) {
bus->queue_tail = NULL;
}
packet->next = NULL;
return true;
}
// Remove from the middle or tail of the queue
i2c_packet_t* p = bus->queue_head;
while (p->next != NULL && p->next != packet) {
p = p->next;
}
if (p->next == packet) {
// The packet found in the queue, remove it
p->next = packet->next;
// Update the tail if necessary
if (bus->queue_tail == packet) {
bus->queue_tail = p;
}
packet->next = NULL;
}
return false;
}
i2c_status_t i2c_bus_submit(i2c_bus_t* bus, i2c_packet_t* packet) {
if (!i2c_bus_ptr_valid(bus) || packet == NULL) {
// Invalid bus or packet
return I2C_STATUS_ERROR;
}
if (packet->next != NULL) {
// Packet is already queued
return I2C_STATUS_ERROR;
}
packet->status = I2C_STATUS_PENDING;
// Insert packet into the queue
uint32_t irq_state = disable_irq();
if (i2c_bus_add_packet(bus, packet)) {
// The queue was empty, start the operation
if (!bus->callback_executed && !bus->abort_pending) {
i2c_bus_head_continue(bus);
}
}
enable_irq(irq_state);
return I2C_STATUS_OK;
}
void i2c_bus_abort(i2c_bus_t* bus, i2c_packet_t* packet) {
if (!i2c_bus_ptr_valid(bus) || packet == NULL) {
// Invalid bus or packet
return;
}
uint32_t irq_state = disable_irq();
if (packet->status == I2C_STATUS_PENDING) {
if (i2c_bus_remove_packet(bus, packet) && bus->next_op > 0) {
// The packet was being processed
if (bus->transfer_size > 0) {
bus->dummy_data = i2c_bus_read_buff(bus);
}
// Reset internal state
bus->next_op = 0;
bus->buff_ptr = NULL;
bus->buff_size = 0;
bus->transfer_size = 0;
bus->transfer_op = 0;
bus->stop_requested = false;
// Inform interrupt handler about pending abort
bus->abort_pending = true;
// Abort operation may fail if the bus is busy or noisy
// so we need to set a timeout.
systimer_set(bus->timer, I2C_BUS_TIMEOUT(2));
}
packet->status = I2C_STATUS_ABORTED;
}
enable_irq(irq_state);
}
// Completes the current packet by removing it from the queue
// an invoking the completion callback
//
// Must be called with IRQ disabled or from IRQ context
// Expects the operation is finished
static void i2c_bus_head_complete(i2c_bus_t* bus, i2c_status_t status) {
i2c_packet_t* packet = bus->queue_head;
if (packet != NULL) {
// Remove packet from the queue
i2c_bus_remove_packet(bus, packet);
// Reset internal state
bus->next_op = 0;
bus->buff_ptr = NULL;
bus->buff_size = 0;
bus->transfer_size = 0;
bus->transfer_op = 0;
bus->stop_requested = false;
bus->abort_pending = false;
systimer_unset(bus->timer);
// Invoke the completion callback
i2c_bus_invoke_callback(bus, packet, status);
}
}
// Starts the next operation in the packet by
// programming the I2C controller
//
// Must be called with IRQ disabled or from IRQ context
// Expects no other operation is being processed
static void i2c_bus_head_continue(i2c_bus_t* bus) {
if (bus->abort_pending) {
systimer_unset(bus->timer);
bus->abort_pending = false;
}
if (bus->queue_head != NULL) {
i2c_packet_t* packet = bus->queue_head;
if (bus->next_op < packet->op_count) {
i2c_op_t* op = &packet->ops[bus->next_op++];
I2C_TypeDef* regs = bus->def->regs;
uint32_t cr2 = regs->CR2;
cr2 &= ~(I2C_CR2_SADD | I2C_CR2_NBYTES | I2C_CR2_RELOAD |
I2C_CR2_AUTOEND | I2C_CR2_RD_WRN | I2C_CR2_SADD_Msk);
// Set device address
cr2 |= ((packet->address & 0x7F) << 1) << I2C_CR2_SADD_Pos;
// Get data ptr and its length
if (op->flags & I2C_FLAG_EMBED) {
bus->buff_ptr = op->data;
bus->buff_size = MIN(op->size, sizeof(op->data));
} else {
bus->buff_ptr = op->ptr;
bus->buff_size = op->size;
}
// Calculate transfer size
bus->transfer_size = bus->buff_size;
bus->transfer_op = bus->next_op;
// Include following operations in the transfer if:
// 1) We are not processing the last operation
// 2) STOP condition is not requested in the current operation
// 3) START condition is not requested in the next operation
// 4) The next operation has the same direction
while ((bus->next_op != packet->op_count) &&
((op->flags & I2C_FLAG_STOP) == 0) &&
(((op + 1)->flags & I2C_FLAG_START) == 0) &&
(((op + 1)->flags & I2C_FLAG_TX) == (op->flags & I2C_FLAG_TX))) {
// Move to the next operation
op = &packet->ops[bus->next_op++];
if (op->flags & I2C_FLAG_EMBED) {
bus->transfer_size += MIN(op->size, sizeof(op->data));
} else {
bus->transfer_size += op->size;
}
}
if (bus->transfer_size > 0) {
// I2C controller can handle only 255 bytes at once
// More data will be handled by the TCR interrupt
cr2 |= MIN(255, bus->transfer_size) << I2C_CR2_NBYTES_Pos;
if (bus->transfer_size > 255) {
cr2 |= I2C_CR2_RELOAD;
}
if (op->flags & I2C_FLAG_TX) {
// Transmitting has priority over receive.
// Flush TXDR register possibly filled by some previous
// invalid operation or abort.
regs->ISR = I2C_ISR_TXE;
} else if (op->flags & I2C_FLAG_RX) {
// Receive data from the device
cr2 |= I2C_CR2_RD_WRN;
}
}
// STOP conditition:
// 1) if it is explicitly requested
// 2) if it is the last operation in the packet
bus->stop_requested = ((op->flags & I2C_FLAG_STOP) != 0) ||
(bus->next_op == packet->op_count);
bus->nack = false;
// START condition
cr2 |= I2C_CR2_START;
// Guard time between operations STOP and START condition
if (bus->def->guard_time > 0) {
while (systick_us() - bus->stop_time < bus->def->guard_time)
;
}
regs->CR2 = cr2;
// Each operation has its own timeout calculated
// based on the number of bytes to transfer and the bus speed +
// expected operation overhead
systimer_set(bus->timer,
I2C_BUS_TIMEOUT(bus->transfer_size) + packet->timeout);
}
}
}
// Timer callback handling I2C bus timeout
static void i2c_bus_timer_callback(void* context) {
i2c_bus_t* bus = (i2c_bus_t*)context;
if (bus->abort_pending) {
// Packet abort was not completed in time (STOPF was not detected)
// This may be cause by the bus being busy/noisy.
I2C_TypeDef* regs = bus->def->regs;
// Reset the I2C controller
regs->CR1 &= ~I2C_CR1_PE;
regs->CR1 |= I2C_CR1_PE;
// Continue with the next packet
i2c_bus_head_continue(bus);
} else {
// Timeout during normal operation occurred
i2c_packet_t* packet = bus->queue_head;
if (packet != NULL) {
// Determine the status based on the current bus state
I2C_TypeDef* regs = bus->def->regs;
i2c_status_t status;
if ((regs->CR2 & I2C_CR2_START) && (regs->ISR & I2C_ISR_BUSY)) {
// START condition was issued but the bus is still busy
status = I2C_STATUS_BUSY;
} else {
status = I2C_STATUS_TIMEOUT;
}
// Abort pending packet
i2c_bus_abort(bus, packet);
// Invoke the completion callback
i2c_bus_invoke_callback(bus, packet, status);
}
}
}
// I2C bus event interrupt handler
static void i2c_bus_ev_handler(i2c_bus_t* bus) {
I2C_TypeDef* regs = bus->def->regs;
uint32_t isr = regs->ISR;
if (isr & I2C_ISR_RXNE) {
// I2C controller receive buffer is not empty.
// The interrupt flag is cleared by reading the RXDR register.
uint8_t received_byte = regs->RXDR;
if (bus->next_op > 0 && bus->transfer_size > 0) {
i2c_bus_write_buff(bus, received_byte);
} else if (bus->abort_pending) {
regs->CR2 |= I2C_CR2_STOP;
} else {
// Invalid state, ignore
}
}
if (isr & I2C_ISR_TXIS) {
// I2C controller transmit buffer is empty.
// The interrupt flag is cleared by writing the TXDR register.
if (bus->next_op > 0 && bus->transfer_size > 0) {
regs->TXDR = i2c_bus_read_buff(bus);
} else {
regs->TXDR = bus->dummy_data;
if (bus->abort_pending) {
regs->CR2 |= I2C_CR2_STOP;
} else {
// Invalid state, ignore
}
}
}
if (isr & I2C_ISR_TCR) {
// Data transfer is partially completed and RELOAD is required
if (bus->abort_pending) {
// Packet is being aborted, issue STOP condition
regs->CR2 &= ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD);
regs->CR2 |= I2C_CR2_STOP;
} else if (bus->transfer_size > 0) {
// There are still some bytes left in the current operation buffer
uint32_t cr2 = regs->CR2 & ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD);
cr2 |= MIN(bus->transfer_size, 255) << I2C_CR2_NBYTES_Pos;
if (bus->transfer_size > 255) {
// Set RELOAD if we the remaining data is still over
// the 255 bytes limit
cr2 |= I2C_CR2_RELOAD;
}
regs->CR2 = cr2;
} else if (bus->queue_head != NULL) {
// Data transfer is split between two or more operations,
// continues in the next operation
i2c_bus_head_continue(bus);
} else {
// Invalid state, clear the TCR flag
regs->CR2 &= ~(I2C_CR2_NBYTES | I2C_CR2_RELOAD);
regs->CR2 |= I2C_CR2_STOP;
}
}
if (isr & I2C_ISR_TC) {
// Trasfer complete
if (bus->stop_requested || bus->abort_pending) {
// Issue stop condition and wait for ISR_STOPF flag
regs->CR2 |= I2C_CR2_STOP;
} else if (bus->queue_head != NULL) {
// Continue with the next operation
i2c_bus_head_continue(bus);
} else {
// Invalid state, clear the TC flag
regs->CR2 |= I2C_CR2_STOP;
}
}
if (isr & I2C_ISR_NACKF) {
// Clear the NACKF flag
regs->ICR = I2C_ICR_NACKCF;
bus->nack = true;
// STOP condition is automatically generated
// by the hardware and the STOPF is set later.
}
if (isr & I2C_ISR_STOPF) {
// Clear the STOPF flag
regs->ICR = I2C_ICR_STOPCF;
if (bus->def->guard_time > 0) {
bus->stop_time = systick_us();
}
if (bus->next_op > 0 && bus->next_op == bus->queue_head->op_count) {
// Last operation in the packet
i2c_bus_head_complete(bus, bus->nack ? I2C_STATUS_NACK : I2C_STATUS_OK);
}
// Continue with the next operation
// or complete the pending packet and move to the next
i2c_bus_head_continue(bus);
}
}
// I2C bus error interrupt handler
static void i2c_bus_er_handler(i2c_bus_t* bus) {
I2C_TypeDef* regs = bus->def->regs;
uint32_t isr = regs->ISR;
// Clear error flags
regs->ICR = I2C_ICR_BERRCF | I2C_ICR_ARLOCF | I2C_ICR_OVRCF;
if (isr & I2C_ISR_BERR) {
// Bus error
// Ignore and continue with pending operation
}
if (isr & I2C_ISR_ARLO) {
if (bus->next_op > 0) {
// Arbitration lost, complete packet with error
i2c_bus_head_complete(bus, I2C_STATUS_ERROR);
// Start the next packet
i2c_bus_head_continue(bus);
} else {
// Packet aborted or invalid state
}
}
if (isr & I2C_ISR_OVR) {
// This should not happed in master mode
}
}
// Interrupt handlers
#ifdef I2C_INSTANCE_0
void I2C_INSTANCE_0_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[0]);
}
void I2C_INSTANCE_0_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[0]);
}
#endif
#ifdef I2C_INSTANCE_1
void I2C_INSTANCE_1_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[1]);
}
void I2C_INSTANCE_1_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[1]);
}
#endif
#ifdef I2C_INSTANCE_2
void I2C_INSTANCE_2_EV_IRQHandler(void) {
i2c_bus_ev_handler(&g_i2c_bus_driver[2]);
}
void I2C_INSTANCE_2_ER_IRQHandler(void) {
i2c_bus_er_handler(&g_i2c_bus_driver[2]);
}
#endif

@ -37,6 +37,10 @@ def configure(
"embed/models/D001/model_D001_layout.c",
]
sources += [
"embed/trezorhal/stm32f4/i2c_bus.c"
]
if "new_rendering" in features_wanted:
sources += [
"embed/trezorhal/xdisplay_legacy.c",

@ -44,6 +44,10 @@ def configure(
"embed/models/D002/model_D002_layout.c",
]
sources += [
"embed/trezorhal/stm32u5/i2c_bus.c"
]
if "new_rendering" in features_wanted:
sources += [
"embed/trezorhal/xdisplay_legacy.c",

@ -48,7 +48,10 @@ def configure(
else:
sources += [f"embed/trezorhal/stm32f4/displays/{display}"]
sources += ["embed/trezorhal/stm32f4/i2c.c"]
sources += [
"embed/trezorhal/stm32f4/i2c.c",
"embed/trezorhal/stm32f4/i2c_bus.c",
]
if "input" in features_wanted:
sources += ["embed/trezorhal/stm32f4/button.c"]

@ -42,6 +42,11 @@ def configure(
"embed/models/T2T1/model_T2T1_layout.c",
"embed/models/T2T1/compat_settings.c",
]
sources += [
"embed/trezorhal/stm32f4/i2c_bus.c"
]
if "new_rendering" in features_wanted:
sources += ["embed/trezorhal/xdisplay_legacy.c"]
sources += ["embed/trezorhal/stm32f4/xdisplay/st-7789/display_nofb.c"]

@ -43,6 +43,10 @@ def configure(
"embed/models/T3B1/model_T3B1_layout.c",
]
sources += [
"embed/trezorhal/stm32u5/i2c_bus.c",
]
if "new_rendering" in features_wanted:
sources += ["embed/trezorhal/xdisplay_legacy.c"]
sources += ["embed/trezorhal/stm32u5/xdisplay/vg-2864/display_driver.c"]

@ -47,6 +47,10 @@ def configure(
"embed/models/T3T1/model_T3T1_layout.c",
]
sources += [
"embed/trezorhal/stm32u5/i2c_bus.c"
]
if "new_rendering" in features_wanted:
sources += ["embed/trezorhal/xdisplay_legacy.c"]
sources += ["embed/trezorhal/stm32u5/xdisplay/st-7789/display_fb.c"]

@ -46,7 +46,10 @@ def configure(
sources += [
"embed/models/T3T1/model_T3T1_layout.c",
]
sources += [f"embed/trezorhal/stm32u5/displays/{display}"]
sources += [
f"embed/trezorhal/stm32u5/displays/{display}",
"embed/trezorhal/stm32u5/i2c_bus.c"
]
if "new_rendering" in features_wanted:
sources += ["embed/trezorhal/xdisplay_legacy.c"]

Loading…
Cancel
Save