What changed, and why it matters
This commit is a routine hardware-driver refactor for the vibration motor (haptic feedback) in Trezor devices. It renames the driver, adds support for a second chip model, fixes how closed-loop vs open-loop motor control is configured, and improves error handling. There is no direct evidence in the commit that this fixes an exploitable security vulnerability; it appears to be a product-quality and maintainability improvement.
Treat as a normal firmware improvement. No urgent security action is indicated by the commit content alone. If auditing, verify that the new closed-loop parameters match the actuator datasheet and that error return values from haptic_init are handled appropriately in production code (the prodtest stub currently has an empty if-block).
Security signals we found
Driver configuration correction for closed-loop/open-loop haptic actuator operation
Addition of error-handling return codes (ts_t) to haptic API
Removal of haptic_test() production-test function, replaced by haptic_play_custom
No evidence of memory corruption, authentication bypass, or cryptographic weakness in the diff
Evidence from the diff
The change replaces the drv2625 haptic driver with a new drv262x driver that supports both DRV2624 and DRV2625 controllers. It corrects actuator configuration (notably the T3W1 ld0625bc actuator was previously set to closed-loop mode while the old code only applied open-loop parameters), adds custom waveform playback for DRV2624, introduces a ts_t error-handling return convention, and removes the haptic_test() helper. The diff shows no buffer overflows, missing bounds checks, or cryptographic changes. The only security-relevant aspect is that misconfigured hardware drivers could theoretically affect device reliability or side-channel behavior, but the commit itself does not frame this as a security fix.
Changed components
core/embed/io/haptic/drv262x driverTrezor T3T1 revE haptic configurationTrezor T3W1 revA/B/C haptic configurationcore/embed/projects/prodtest haptic initializationcore/embed/rtl error_handlingInspect captured patch +1423 / −767
diff --git a/core/embed/io/haptic/drv2625/actuators/ld0625bc.h b/core/embed/io/haptic/drv2625/actuators/ld0625bc.h
deleted file mode 100644
index ea4e69f01..000000000
--- a/core/embed/io/haptic/drv2625/actuators/ld0625bc.h
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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/>.
- */
-
-#pragma once
-
-#define ACTUATOR_LRA
-#define ACTUATOR_CLOSED_LOOP
-
-#define ACTUATOR_FREQUENCY (260)
-#define ACTUATOR_VOLTAGE (0.7)
-
-#define ACTUATOR_LRA_PERIOD ((int)((1000000 / ACTUATOR_FREQUENCY) / 24.615))
-
-// open-loop mode
-// V = 21.32 * 10^(-3) * OD_CLAMP * sqrt(1 - fLRA * 800 * 10^(-6))
-#define ACTUATOR_OD_CLAMP (37)
-
-// closed-loop mode
-// V = (20.58 * 10^(-3) * RATED_VOLTAGE) / sqrt (1 - (4 * tSAMPLE_TIME + 300 *
-// 10^(-6)) * fLRA) where tSAMPLE_TIME = 300us, by default
-#define ACTUATOR_RATED_VOLTAGE (27)
diff --git a/core/embed/io/haptic/drv2625/actuators/vg1040003d.h b/core/embed/io/haptic/drv2625/actuators/vg1040003d.h
deleted file mode 100644
index 038ca6f69..000000000
--- a/core/embed/io/haptic/drv2625/actuators/vg1040003d.h
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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/>.
- */
-
-#pragma once
-
-#define ACTUATOR_LRA
-#define ACTUATOR_OPEN_LOOP
-
-#define ACTUATOR_LRA_PERIOD (239)
-#define ACTUATOR_OD_CLAMP (126)
diff --git a/core/embed/io/haptic/drv2625/drv2625.c b/core/embed/io/haptic/drv2625/drv2625.c
deleted file mode 100644
index e547046b7..000000000
--- a/core/embed/io/haptic/drv2625/drv2625.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- * 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 <trezor_bsp.h>
-#include <trezor_rtl.h>
-
-#include <io/haptic.h>
-#include <io/i2c_bus.h>
-#include <sys/systick.h>
-
-#include "drv2625.h"
-
-#ifdef KERNEL_MODE
-
-// Maximum amplitude of the vibration effect
-// (DRV2625 supports 7-bit amplitude)
-#define MAX_AMPLITUDE 127
-// Amplitude of the vibration effect used for production test
-#define PRODTEST_EFFECT_AMPLITUDE 127
-// Amplitude of the button press effect
-#define PRESS_EFFECT_AMPLITUDE 25
-// Duration of the button press effect
-#define PRESS_EFFECT_DURATION 10
-// Amplitude of the bootloader entry effect
-#define BOOTLOADER_ENTRY_EFFECT_AMPLITUDE 100
-// Duration of the bootloader entry effect
-#define BOOTLOADER_ENTRY_EFFECT_DURATION 300
-// Amplitude of the power on effect
-#define POWER_ON_EFFECT_AMPLITUDE 50
-// Duration of the power on effect
-#define POWER_ON_EFFECT_DURATION 50
-
-// Actuator configuration
-#include HAPTIC_ACTUATOR
-
-#if defined ACTUATOR_CLOSED_LOOP
-#define LIB_SEL 0x00
-#define LOOP_SEL 0x00
-#elif defined ACTUATOR_OPEN_LOOP
-#define LIB_SEL DRV2625_REG_LIBRARY_OPENLOOP
-#define LOOP_SEL DRV2625_REG_LRAERM_OPENLOOP
-#else
-#error "Must define either CLOSED_LOOP or OPEN_LOOP"
-#endif
-
-#if defined ACTUATOR_LRA
-#define LRA_ERM_SEL DRV2625_REG_LRAERM_LRA
-#elif defined ACTUATOR_ERM
-#define LRA_ERM_SEL 0x00
-#else
-#error "Must define either LRA or ERM"
-#endif
-
-// Driver state
-typedef struct {
- // Set if driver is initialized
- bool initialized;
- // I2c bus where the touch controller is connected
- i2c_bus_t *i2c_bus;
- // Set if driver is enabled
- bool enabled;
- // Set to if real-time playing is activated.
- // This prevents the repeated set of `DRV2625_REG_MODE` register
- // which would otherwise stop all playback.
- bool playing_rtp;
-
-} haptic_driver_t;
-
-// Haptic driver instance
-static haptic_driver_t g_haptic_driver = {
- .initialized = false,
-};
-
-static bool drv2625_set_reg(i2c_bus_t *bus, uint8_t addr, uint8_t value) {
- i2c_op_t ops[] = {
- {
- .flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
- .size = 2,
- .data = {addr, value},
- },
- };
-
- i2c_packet_t pkt = {
- .address = DRV2625_I2C_ADDRESS,
- .op_count = ARRAY_LENGTH(ops),
- .ops = ops,
- };
-
- if (I2C_STATUS_OK != i2c_bus_submit_and_wait(bus, &pkt)) {
- return false;
- }
-
- return true;
-}
-
-bool haptic_init(void) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (driver->initialized) {
- return true;
- }
-
- memset(driver, 0, sizeof(haptic_driver_t));
-
- GPIO_InitTypeDef GPIO_InitStructure = {0};
-
-#ifdef DRV2625_RESET_PIN
- DRV2625_RESET_CLK_ENA();
- GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
- GPIO_InitStructure.Pull = GPIO_NOPULL;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStructure.Pin = DRV2625_RESET_PIN;
- HAL_GPIO_WritePin(DRV2625_RESET_PORT, DRV2625_RESET_PIN, GPIO_PIN_RESET);
- HAL_GPIO_Init(DRV2625_RESET_PORT, &GPIO_InitStructure);
- systick_delay_ms(1);
- HAL_GPIO_WritePin(DRV2625_RESET_PORT, DRV2625_RESET_PIN, GPIO_PIN_SET);
- systick_delay_ms(1);
-#endif
-
- DRV2625_TRIG_CLK_ENA();
- GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
- GPIO_InitStructure.Pull = GPIO_PULLDOWN;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStructure.Pin = DRV2625_TRIG_PIN;
- GPIO_InitStructure.Alternate = DRV2625_TRIG_AF;
- HAL_GPIO_Init(DRV2625_TRIG_PORT, &GPIO_InitStructure);
-
- driver->i2c_bus = i2c_bus_open(DRV2625_I2C_INSTANCE);
- if (driver->i2c_bus == NULL) {
- goto cleanup;
- }
-
- // select library
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_LIBRARY,
- LIB_SEL | DRV2625_REG_LIBRARY_GAIN_25)) {
- goto cleanup;
- }
-
- if (!drv2625_set_reg(
- driver->i2c_bus, DRV2625_REG_LRAERM,
- LRA_ERM_SEL | LOOP_SEL | DRV2625_REG_LRAERM_AUTO_BRK_OL)) {
- goto cleanup;
- }
-
-#ifdef ACTUATOR_OPEN_LOOP
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_OD_CLAMP,
- ACTUATOR_OD_CLAMP)) {
- goto cleanup;
- }
-#elif defined ACTUATOR_CLOSED_LOOP
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_RATED_VOLTAGE,
- ACTUATOR_RATED_VOLTAGE)) {
- goto cleanup;
- }
-#endif
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_LRA_WAVE_SHAPE,
- DRV2625_REG_LRA_WAVE_SHAPE_SINE)) {
- goto cleanup;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_OL_LRA_PERIOD_LO,
- ACTUATOR_LRA_PERIOD & 0xFF)) {
- goto cleanup;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_OL_LRA_PERIOD_HI,
- ACTUATOR_LRA_PERIOD >> 8)) {
- goto cleanup;
- }
-
- DRV2625_TRIG_TIM_FORCE_RESET();
- DRV2625_TRIG_TIM_RELEASE_RESET();
- DRV2625_TRIG_TIM_CLK_ENA();
-
- TIM_HandleTypeDef TIM_Handle = {0};
- TIM_Handle.State = HAL_TIM_STATE_RESET;
- TIM_Handle.Instance = DRV2625_TRIG_TIM;
- TIM_Handle.Init.Period = 0;
- TIM_Handle.Init.Prescaler = SystemCoreClock / 10000;
- TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
- TIM_Handle.Init.RepetitionCounter = 0;
- HAL_TIM_OnePulse_Init(&TIM_Handle, TIM_OPMODE_SINGLE);
-
- TIM_OnePulse_InitTypeDef TIM_OP_InitStructure = {0};
- TIM_OP_InitStructure.OCMode = TIM_OCMODE_PWM2;
- TIM_OP_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
- TIM_OP_InitStructure.Pulse = 1;
- TIM_OP_InitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
- HAL_TIM_OnePulse_ConfigChannel(&TIM_Handle, &TIM_OP_InitStructure,
- TIM_CHANNEL_1, TIM_CHANNEL_2);
-
- HAL_TIM_OC_Start(&TIM_Handle, TIM_CHANNEL_1);
-
- DRV2625_TRIG_TIM->BDTR |= TIM_BDTR_MOE;
-
- driver->initialized = true;
- driver->enabled = true;
-
- return true;
-
-cleanup:
- haptic_deinit();
- return false;
-}
-
-void haptic_deinit(void) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- i2c_bus_close(driver->i2c_bus);
-
- GPIO_InitTypeDef GPIO_InitStructure = {0};
-
-#ifdef DRV2625_RESET_PIN
- // External pull-down on NRST pin ensures that the DRV2625 goes into
- // shutdown mode when the reset GPIO is deinitialized.
- GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStructure.Pull = GPIO_NOPULL;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStructure.Pin = DRV2625_RESET_PIN;
- HAL_GPIO_Init(DRV2625_RESET_PORT, &GPIO_InitStructure);
-#endif
-
- GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
- GPIO_InitStructure.Pull = GPIO_NOPULL;
- GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
- GPIO_InitStructure.Pin = DRV2625_TRIG_PIN;
- HAL_GPIO_Init(DRV2625_TRIG_PORT, &GPIO_InitStructure);
-
- DRV2625_TRIG_TIM_FORCE_RESET();
- DRV2625_TRIG_TIM_RELEASE_RESET();
- DRV2625_TRIG_TIM_CLK_DIS();
-
- memset(driver, 0, sizeof(haptic_driver_t));
-}
-
-void haptic_set_enabled(bool enabled) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return;
- }
-
- driver->enabled = enabled;
-}
-
-bool haptic_get_enabled(void) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return false;
- }
-
- return driver->enabled;
-}
-
-static bool haptic_play_rtp(int8_t amplitude, uint16_t duration_ms) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return false;
- }
-
- if (!driver->playing_rtp) {
- if (!drv2625_set_reg(
- driver->i2c_bus, DRV2625_REG_MODE,
- DRV2625_REG_MODE_RTP | DRV2625_REG_MODE_TRGFUNC_ENABLE)) {
- return false;
- }
-
- driver->playing_rtp = true;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_RTP, (uint8_t)amplitude)) {
- return false;
- }
-
- if (duration_ms > 6500) {
- duration_ms = 6500;
- }
- if (duration_ms == 0) {
- return true;
- }
-
- DRV2625_TRIG_TIM->CNT = 1;
- DRV2625_TRIG_TIM->CCR1 = 1;
- DRV2625_TRIG_TIM->ARR = duration_ms * 10;
- DRV2625_TRIG_TIM->CR1 |= TIM_CR1_CEN;
-
- return true;
-}
-
-static bool haptic_play_lib(drv2625_lib_effect_t effect) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return false;
- }
-
- driver->playing_rtp = false;
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_MODE,
- DRV2625_REG_MODE_WAVEFORM)) {
- return false;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_WAVESEQ1, effect)) {
- return false;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_WAVESEQ2, 0)) {
- return false;
- }
-
- if (!drv2625_set_reg(driver->i2c_bus, DRV2625_REG_GO, DRV2625_REG_GO_GO)) {
- return false;
- }
-
- return true;
-}
-
-bool haptic_play(haptic_effect_t effect) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return false;
- }
-
- if (!driver->enabled) {
- return true;
- }
-
- switch (effect) {
- case HAPTIC_BUTTON_PRESS:
- return haptic_play_rtp(PRESS_EFFECT_AMPLITUDE, PRESS_EFFECT_DURATION);
- break;
- case HAPTIC_HOLD_TO_CONFIRM:
- return haptic_play_lib(DOUBLE_CLICK_60);
- break;
- case HAPTIC_BOOTLOADER_ENTRY:
- return haptic_play_rtp(BOOTLOADER_ENTRY_EFFECT_AMPLITUDE,
- BOOTLOADER_ENTRY_EFFECT_DURATION);
- break;
- case HAPTIC_POWER_ON:
- return haptic_play_rtp(POWER_ON_EFFECT_AMPLITUDE,
- POWER_ON_EFFECT_DURATION);
- default:
- break;
- }
-
- return false;
-}
-
-bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) {
- haptic_driver_t *driver = &g_haptic_driver;
-
- if (!driver->initialized) {
- return false;
- }
-
- if (!driver->enabled) {
- return true;
- }
- if (amplitude_pct < 0) {
- amplitude_pct = 0;
- } else if (amplitude_pct > 100) {
- amplitude_pct = 100;
- }
-
- return haptic_play_rtp((int8_t)((amplitude_pct * MAX_AMPLITUDE) / 100),
- duration_ms);
-}
-
-bool haptic_test(uint16_t duration_ms) {
- return haptic_play_rtp(PRODTEST_EFFECT_AMPLITUDE, duration_ms);
-}
-
-#endif // KERNEL_MODE
diff --git a/core/embed/io/haptic/drv2625/drv2625.h b/core/embed/io/haptic/drv2625/drv2625.h
deleted file mode 100644
index 2c63a9715..000000000
--- a/core/embed/io/haptic/drv2625/drv2625.h
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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/>.
- */
-
-#pragma once
-
-// I2C address of the DRV2625 on the I2C bus.
-#define DRV2625_I2C_ADDRESS 0x5A
-
-// ------------------------------------------------------------
-// DRV2625 registers
-// ------------------------------------------------------------
-
-#define DRV2625_REG_CHIPID 0x00
-#define DRV2625_REG_STATUS 0x01
-#define DRV2625_REG_MODE 0x07
-#define DRV2625_REG_MODE_RTP 0
-#define DRV2625_REG_MODE_WAVEFORM 0x01
-#define DRV2625_REG_MODE_DIAG 0x02
-#define DRV2625_REG_MODE_AUTOCAL 0x03
-#define DRV2625_REG_MODE_TRGFUNC_PULSE 0x00
-#define DRV2625_REG_MODE_TRGFUNC_ENABLE 0x04
-#define DRV2625_REG_MODE_TRGFUNC_INTERRUPT 0x08
-
-#define DRV2625_REG_LRAERM 0x08
-#define DRV2625_REG_LRAERM_LRA 0x80
-#define DRV2625_REG_LRAERM_OPENLOOP 0x40
-#define DRV2625_REG_LRAERM_AUTO_BRK_OL 0x10
-#define DRV2625_REG_LRAERM_AUTO_BRK_STBY 0x08
-
-#define DRV2625_REG_LIBRARY 0x0D ///< Waveform library selection register
-#define DRV2625_REG_LIBRARY_OPENLOOP 0x40
-#define DRV2625_REG_LIBRARY_GAIN_100 0x00
-#define DRV2625_REG_LIBRARY_GAIN_75 0x01
-#define DRV2625_REG_LIBRARY_GAIN_50 0x02
-#define DRV2625_REG_LIBRARY_GAIN_25 0x03
-
-#define DRV2625_REG_RTP 0x0E ///< RTP input register
-
-#define DRV2625_REG_WAVESEQ1 0x0F ///< Waveform sequence register 1
-#define DRV2625_REG_WAVESEQ2 0x10 ///< Waveform sequence register 2
-#define DRV2625_REG_WAVESEQ3 0x11 ///< Waveform sequence register 3
-#define DRV2625_REG_WAVESEQ4 0x12 ///< Waveform sequence register 4
-#define DRV2625_REG_WAVESEQ5 0x13 ///< Waveform sequence register 5
-#define DRV2625_REG_WAVESEQ6 0x14 ///< Waveform sequence register 6
-#define DRV2625_REG_WAVESEQ7 0x15 ///< Waveform sequence register 7
-#define DRV2625_REG_WAVESEQ8 0x16 ///< Waveform sequence register 8
-
-#define DRV2625_REG_GO 0x0C ///< Go register
-#define DRV2625_REG_GO_GO 0x01
-
-#define DRV2625_REG_OD_CLAMP 0x20
-#define DRV2625_REG_RATED_VOLTAGE 0x20
-
-#define DRV2625_REG_LRA_WAVE_SHAPE 0x2C
-#define DRV2625_REG_LRA_WAVE_SHAPE_SINE 0x01
-
-#define DRV2625_REG_OL_LRA_PERIOD_LO 0x2F
-#define DRV2625_REG_OL_LRA_PERIOD_HI 0x2E
-
-// ------------------------------------------------------------
-// DRV2625 effect types
-// ------------------------------------------------------------
-
-typedef enum {
- STRONG_CLICK_100 = 1,
- STRONG_CLICK_60 = 2,
- STRONG_CLICK_30 = 3,
- SHARP_CLICK_100 = 4,
- SHARP_CLICK_60 = 5,
- SHARP_CLICK_30 = 6,
- SOFT_BUMP_100 = 7,
- SOFT_BUMP_60 = 8,
- SOFT_BUMP_30 = 9,
- DOUBLE_CLICK_100 = 10,
- DOUBLE_CLICK_60 = 11,
- TRIPLE_CLICK_100 = 12,
- SOFT_FUZZ_60 = 13,
- STRONG_BUZZ_100 = 14,
- ALERT_750MS_100 = 15,
- ALERT_1000MS_100 = 16,
- STRONG_CLICK_1_100 = 17,
- STRONG_CLICK_2_80 = 18,
- STRONG_CLICK_3_60 = 19,
- STRONG_CLICK_4_30 = 20,
- MEDIUM_CLICK_1_100 = 21,
- MEDIUM_CLICK_2_80 = 22,
- MEDIUM_CLICK_3_60 = 23,
- SHARP_TICK_1_100 = 24,
- SHARP_TICK_2_80 = 25,
- SHARP_TICK_3_60 = 26,
- SHORT_DOUBLE_CLICK_STRONG_1_100 = 27,
- SHORT_DOUBLE_CLICK_STRONG_2_80 = 28,
- SHORT_DOUBLE_CLICK_STRONG_3_60 = 29,
- SHORT_DOUBLE_CLICK_STRONG_4_30 = 30,
- SHORT_DOUBLE_CLICK_MEDIUM_1_100 = 31,
- SHORT_DOUBLE_CLICK_MEDIUM_2_80 = 32,
- SHORT_DOUBLE_CLICK_MEDIUM_3_60 = 33,
- SHORT_DOUBLE_SHARP_TICK_1_100 = 34,
- SHORT_DOUBLE_SHARP_TICK_2_80 = 35,
- SHORT_DOUBLE_SHARP_TICK_3_60 = 36,
- LONG_DOUBLE_SHARP_TICK_STRONG_1_100 = 37,
- LONG_DOUBLE_SHARP_TICK_STRONG_2_80 = 38,
- LONG_DOUBLE_SHARP_TICK_STRONG_3_60 = 39,
- LONG_DOUBLE_SHARP_TICK_STRONG_4_30 = 40,
- LONG_DOUBLE_SHARP_TICK_MEDIUM_1_100 = 41,
- LONG_DOUBLE_SHARP_TICK_MEDIUM_2_80 = 42,
- LONG_DOUBLE_SHARP_TICK_MEDIUM_3_60 = 43,
- LONG_DOUBLE_SHARP_TICK_1_100 = 44,
- LONG_DOUBLE_SHARP_TICK_2_80 = 45,
- LONG_DOUBLE_SHARP_TICK_3_60 = 46,
- BUZZ_1_100 = 47,
- BUZZ_2_80 = 48,
- BUZZ_3_60 = 49,
- BUZZ_4_40 = 50,
- BUZZ_5_20 = 51,
- PULSING_STRONG_1_100 = 52,
- PULSING_STRONG_2_60 = 53,
- PULSING_MEDIUM_1_100 = 54,
- PULSING_MEDIUM_2_60 = 55,
- PULSING_SHARP_1_100 = 56,
- PULSING_SHARP_2_60 = 57,
- TRANSITION_CLICK_1_100 = 58,
- TRANSITION_CLICK_2_80 = 59,
- TRANSITION_CLICK_3_60 = 60,
- TRANSITION_CLICK_4_40 = 61,
- TRANSITION_CLICK_5_20 = 62,
- TRANSITION_CLICK_6_10 = 63,
- TRANSITION_HUM_1_100 = 64,
- TRANSITION_HUM_2_80 = 65,
- TRANSITION_HUM_3_60 = 66,
- TRANSITION_HUM_4_40 = 67,
- TRANSITION_HUM_5_20 = 68,
- TRANSITION_HUM_6_10 = 69,
- TRANSITION_RAMP_DOWN_LONG_SMOOTH_1 = 70,
- TRANSITION_RAMP_DOWN_LONG_SMOOTH_2 = 71,
- TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1 = 72,
- TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2 = 73,
- TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1 = 74,
- TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2 = 75,
- TRANSITION_RAMP_DOWN_LONG_SHARP_1 = 76,
- TRANSITION_RAMP_DOWN_LONG_SHARP_2 = 77,
- TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1 = 78,
- TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2 = 79,
- TRANSITION_RAMP_DOWN_SHORT_SHARP_1 = 80,
- TRANSITION_RAMP_DOWN_SHORT_SHARP_2 = 81,
- TRANSITION_RAMP_UP_LONG_SMOOTH_1 = 82,
- TRANSITION_RAMP_UP_LONG_SMOOTH_2 = 83,
- TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1 = 84,
- TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2 = 85,
- TRANSITION_RAMP_UP_SHORT_SMOOTH_1 = 86,
- TRANSITION_RAMP_UP_SHORT_SMOOTH_2 = 87,
- TRANSITION_RAMP_UP_LONG_SHARP_1 = 88,
- TRANSITION_RAMP_UP_LONG_SHARP_2 = 89,
- TRANSITION_RAMP_UP_MEDIUM_SHARP_1 = 90,
- TRANSITION_RAMP_UP_MEDIUM_SHARP_2 = 91,
- TRANSITION_RAMP_UP_SHORT_SHARP_1 = 92,
- TRANSITION_RAMP_UP_SHORT_SHARP_2 = 93,
- TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_50 = 94,
- TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_50 = 95,
- TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_50 = 96,
- TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_50 = 97,
- TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_50 = 98,
- TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_50 = 99,
- TRANSITION_RAMP_DOWN_LONG_SHARP_1_50 = 100,
- TRANSITION_RAMP_DOWN_LONG_SHARP_2_50 = 101,
- TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_50 = 102,
- TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_50 = 103,
- TRANSITION_RAMP_DOWN_SHORT_SHARP_1_50 = 104,
- TRANSITION_RAMP_DOWN_SHORT_SHARP_2_50 = 105,
- TRANSITION_RAMP_UP_LONG_SMOOTH_1_50 = 106,
- TRANSITION_RAMP_UP_LONG_SMOOTH_2_50 = 107,
- TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_50 = 108,
- TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_50 = 109,
- TRANSITION_RAMP_UP_SHORT_SMOOTH_1_50 = 110,
- TRANSITION_RAMP_UP_SHORT_SMOOTH_2_50 = 111,
- TRANSITION_RAMP_UP_LONG_SHARP_1_50 = 112,
- TRANSITION_RAMP_UP_LONG_SHARP_2_50 = 113,
- TRANSITION_RAMP_UP_MEDIUM_SHARP_1_50 = 114,
- TRANSITION_RAMP_UP_MEDIUM_SHARP_2_50 = 115,
- TRANSITION_RAMP_UP_SHORT_SHARP_1_50 = 116,
- TRANSITION_RAMP_UP_SHORT_SHARP_2_50 = 117,
- LONG_BUZZ_FROM_PROGRAMMATIC_STOPPING = 118,
- SMOOTH_HUM_1_100 = 119,
- SMOOTH_HUM_2_80 = 120,
- SMOOTH_HUM_3_60 = 121,
- SMOOTH_HUM_4_40 = 122,
- SMOOTH_HUM_5_20 = 123,
-} drv2625_lib_effect_t;
diff --git a/core/embed/io/haptic/drv262x/actuators/ld0625bc.h b/core/embed/io/haptic/drv262x/actuators/ld0625bc.h
new file mode 100644
index 000000000..8a3ca08d7
--- /dev/null
+++ b/core/embed/io/haptic/drv262x/actuators/ld0625bc.h
@@ -0,0 +1,49 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+/* Actuator type: ACTUATOR_LRA or ACTUATOR_ERM */
+#define ACTUATOR_LRA
+
+/* Actuator control mode: ACTUATOR_CLOSED_LOOP or ACTUATOR_OPEN_LOOP */
+#define ACTUATOR_CLOSED_LOOP
+
+// ACTUATOR_LRA_PERIOD = 1/(fLRA * 24.615*10^{-6})
+#define ACTUATOR_LRA_PERIOD (156)
+
+// V = (20.58 * 10^(-3) * RATED_VOLTAGE) / sqrt (1 - (4 * tSAMPLE_TIME + 300 *
+// 10^(-6)) * fLRA) where tSAMPLE_TIME = 300us, by default
+#define ACTUATOR_RATED_VOLTAGE (27)
+
+// V = 21.32 * 10^(-3) * OD_CLAMP * sqrt(1 - fLRA * 800 * 10^(-6))
+// Note: For ld0625bc this value was increased in order to achieve higher
+// accelaration / breaking capability.
+#define ACTUATOR_OD_CLAMP (150)
+
+#define ACTUATOR_FB_BRK_FACTOR 3
+#define ACTUATOR_LOOP_GAIN 1
+#define ACTUATOR_BEMF_GAIN 2
+
+// DRIVE_TIME ~= (0.5 * (1000/ACTUATOR_FREQUENCY_HZ) - 0.5) / 0.1
+#define ACTUATOR_DRIVE_TIME 16
+#define ACTUATOR_IDISS_TIME 1
+#define ACTUATOR_BLANK_TIME 1
+#define ACTUATOR_SAMPLE_TIME 3
+#define ACTUATOR_ZC_DET_TIME 0
diff --git a/core/embed/io/haptic/drv262x/actuators/vg1040003d.h b/core/embed/io/haptic/drv262x/actuators/vg1040003d.h
new file mode 100644
index 000000000..72bae0046
--- /dev/null
+++ b/core/embed/io/haptic/drv262x/actuators/vg1040003d.h
@@ -0,0 +1,47 @@
+/*
+ * 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/>.
+ */
+
+#pragma once
+
+/* Actuator type: ACTUATOR_LRA or ACTUATOR_ERM */
+#define ACTUATOR_LRA
+
+/* Actuator control mode: ACTUATOR_CLOSED_LOOP or ACTUATOR_OPEN_LOOP */
+#define ACTUATOR_OPEN_LOOP
+
+// ACTUATOR_LRA_PERIOD = 1/(fLRA * 24.615*10^{-6})
+#define ACTUATOR_LRA_PERIOD (239)
+
+// V = (20.58 * 10^(-3) * RATED_VOLTAGE) / sqrt (1 - (4 * tSAMPLE_TIME + 300 *
+// 10^(-6)) * fLRA) where tSAMPLE_TIME = 300us, by default
+#define ACTUATOR_RATED_VOLTAGE (27)
+
+// V = 21.32 * 10^(-3) * OD_CLAMP * sqrt(1 - fLRA * 800 * 10^(-6))
+#define ACTUATOR_OD_CLAMP (126)
+
+#define ACTUATOR_FB_BRK_FACTOR 3
+#define ACTUATOR_LOOP_GAIN 1
+#define ACTUATOR_BEMF_GAIN 2
+
+// DRIVE_TIME ~= (0.5 * (1000/ACTUATOR_FREQUENCY_HZ) - 0.5) / 0.1
+#define ACTUATOR_DRIVE_TIME 16
+#define ACTUATOR_IDISS_TIME 1
+#define ACTUATOR_BLANK_TIME 1
+#define ACTUATOR_SAMPLE_TIME 3
+#define ACTUATOR_ZC_DET_TIME 0
diff --git a/core/embed/io/haptic/drv262x/drv262x.c b/core/embed/io/haptic/drv262x/drv262x.c
new file mode 100644
index 000000000..223028b37
--- /dev/null
+++ b/core/embed/io/haptic/drv262x/drv262x.c
@@ -0,0 +1,708 @@
+/*
+ * 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 <math.h>
+#include <trezor_bsp.h>
+#include <trezor_rtl.h>
+
+#include <io/haptic.h>
+#include <io/i2c_bus.h>
+#include <sys/systick.h>
+
+#include "drv262x.h"
+
+// Actuator configuration
+#include DRV262X_ACTUATOR
+
+#ifdef KERNEL_MODE
+
+// Maximum amplitude of the vibration effect
+// (DRV2625 supports 7-bit amplitude)
+#define MAX_AMPLITUDE 127
+// Amplitude of the button press effect
+#define PRESS_EFFECT_AMPLITUDE 25
+// Duration of the button press effect
+#define PRESS_EFFECT_DURATION 10
+// Amplitude of the bootloader entry effect
+#define BOOTLOADER_ENTRY_EFFECT_AMPLITUDE 100
+// Duration of the bootloader entry effect
+#define BOOTLOADER_ENTRY_EFFECT_DURATION 300
+// Amplitude of the power on effect
+#define POWER_ON_EFFECT_AMPLITUDE 50
+// Duration of the power on effect
+#define POWER_ON_EFFECT_DURATION 50
+
+#if !defined(ACTUATOR_LRA) && !defined(ACTUATOR_ERM)
+#error "Actuator type (ACTUATOR_LRA or ACTUATOR_ERM) not defined"
+#endif
+
+#if defined(ACTUATOR_LRA) && defined(ACTUATOR_ERM)
+#error "Both ACTUATOR_LRA and ACTUATOR_ERM defined, only one must be defined"
+#endif
+
+#if !defined(ACTUATOR_CLOSED_LOOP) && !defined(ACTUATOR_OPEN_LOOP)
+#error \
+ "Actuator control mode (ACTUATOR_CLOSED_LOOP or ACTUATOR_OPEN_LOOP) not defined"
+#endif
+
+#if !defined(ACTUATOR_RATED_VOLTAGE) || ACTUATOR_RATED_VOLTAGE > 255
+#error "ACTUATOR_RATED_VOLTAGE must be defined and <= 255"
+#endif
+
+#if !defined(ACTUATOR_OD_CLAMP) || ACTUATOR_OD_CLAMP > 255
+#error "ACTUATOR_OD_CLAMP must be defined and <= 255"
+#endif
+
+#if !defined(HAPTIC_CHIP_DRV2624) && !defined(HAPTIC_CHIP_DRV2625)
+#error "HAPTIC_CHIP_DRV2624 or HAPTIC_CHIP_DRV2625 must be defined"
+#endif
+
+// Driver state
+typedef struct {
+ // Set if driver is initialized
+ bool initialized;
+
+ // I2c bus where the touch controller is connected
+ i2c_bus_t *i2c_bus;
+
+ // Set if driver is enabled
+ bool enabled;
+
+ // Set to if real-time playing is activated.
+ // This prevents the repeated set of `DRV2625_REG_MODE` register
+ // which would otherwise stop all playback.
+ bool rtp_mode;
+} drv262x_driver_t;
+
+// Haptic driver instance
+static drv262x_driver_t g_drv262x_driver = {
+ .initialized = false,
+};
+
+static ts_t drv262x_read_reg(i2c_bus_t *bus, uint8_t addr, uint8_t *value) {
+ TSH_DECLARE;
+
+ i2c_op_t ops[] = {
+ {
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
+ .size = 1,
+ .data = {addr},
+ },
+ {
+ .flags = I2C_FLAG_RX,
+ .size = sizeof(*value),
+ .ptr = value,
+ },
+ };
+
+ i2c_packet_t pkt = {
+ .address = DRV262X_I2C_ADDRESS,
+ .op_count = ARRAY_LENGTH(ops),
+ .ops = ops,
+ };
+
+ TSH_CHECK(i2c_bus_submit_and_wait(bus, &pkt) == I2C_STATUS_OK, TS_EIO);
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv262x_set_reg(i2c_bus_t *bus, uint8_t addr, uint8_t value) {
+ TSH_DECLARE;
+
+ i2c_op_t ops[] = {
+ {
+ .flags = I2C_FLAG_TX | I2C_FLAG_EMBED,
+ .size = 2,
+ .data = {addr, value},
+ },
+ };
+
+ i2c_packet_t pkt = {
+ .address = DRV262X_I2C_ADDRESS,
+ .op_count = ARRAY_LENGTH(ops),
+ .ops = ops,
+ };
+
+ TSH_CHECK(i2c_bus_submit_and_wait(bus, &pkt) == I2C_STATUS_OK, TS_EIO);
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv262x_reg_mask_modify(i2c_bus_t *bus, uint8_t addr,
+ uint8_t clear_mask, uint8_t set_mask) {
+ TSH_DECLARE;
+ ts_t status;
+
+ uint8_t reg;
+ status = drv262x_read_reg(bus, addr, ®);
+ TSH_CHECK_OK(status);
+
+ reg &= ~clear_mask;
+ reg |= set_mask;
+
+ status = drv262x_set_reg(bus, addr, reg);
+ TSH_CHECK_OK(status);
+
+cleanup:
+ TSH_RETURN;
+}
+
+#ifdef HAPTIC_CHIP_DRV2624
+
+#define DRV2624_LIB_MAX_SEQ_LEN 15
+#define DRV2624_LIB_MAX_WAVEFORMS 20
+#define DRV2624_RAM_SIZE 1024
+
+// DRV2624 custom waveform definition
+typedef struct {
+ uint8_t sequence[DRV2624_LIB_MAX_SEQ_LEN];
+ uint8_t time[DRV2624_LIB_MAX_SEQ_LEN];
+ uint8_t length;
+ uint8_t repeat; // 0-single run, 2-three runs, 7(max)-infinite runs.
+ bool linear_ramp;
+ bool short_timing; // true = 1ms units, false = 5ms units
+} drv2624_waveform_t;
+
+// List of DRV2624 registered custom waveforms
+typedef struct {
+ drv2624_waveform_t *waveforms[DRV2624_LIB_MAX_WAVEFORMS];
+ uint8_t registered_waveforms;
+} drv2624_waveform_list_t;
+
+// Sharp btn click effect waveform
+drv2624_waveform_t sharp_btn_click_effect = {
+ .sequence = {45, 63, 55, 120, 15, 100, 8, 90, 3, 0, 0, 0, 0, 0, 0},
+ .time = {3, 2, 3, 1, 4, 2, 5, 3, 8, 0, 0, 0, 0, 0, 0},
+ .length = 9,
+ .repeat = 0,
+ .linear_ramp = false,
+ .short_timing = true,
+};
+
+static drv2624_waveform_list_t g_waveform_list = {.registered_waveforms = 0};
+
+static ts_t drv2624_register_waveform(drv2624_waveform_list_t *list,
+ drv2624_waveform_t *waveform) {
+ TSH_DECLARE;
+
+ TSH_CHECK_ARG(waveform->length != 0 &&
+ waveform->length <= DRV2624_LIB_MAX_SEQ_LEN);
+
+ TSH_CHECK_ARG(waveform->repeat <= 7);
+
+ list->waveforms[list->registered_waveforms] = waveform;
+ list->registered_waveforms++;
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv2624_load_ram(drv2624_waveform_list_t *wave_list) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ /**
+ * Set RAM address pointer to 0x0000 and write RAM revision byte 0x00.
+ * RAM address pointer auto-increments after each write to DRV2624_RAM_DATA
+ * so from now on, we can just write the data without setting the address
+ * again.
+ */
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_ADDR_H, 0x00);
+ TSH_CHECK_OK(status);
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_ADDR_L, 0x00);
+ TSH_CHECK_OK(status);
+
+ // set RAM revision byte 0x00
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA, 0x00);
+ TSH_CHECK_OK(status);
+
+ uint16_t waveform_data_start_address =
+ 0x0001 + wave_list->registered_waveforms * 3;
+
+ uint16_t addr_pointer = waveform_data_start_address;
+ TSH_CHECK(waveform_data_start_address + 2 * DRV2624_LIB_MAX_SEQ_LEN <
+ DRV2624_RAM_SIZE,
+ TS_ENOMEM);
+
+ // RAM Header
+ for (int i = 0; i < wave_list->registered_waveforms; i++) {
+ drv2624_waveform_t *wav = wave_list->waveforms[i];
+
+ status = drv262x_set_reg(
+ drv->i2c_bus, DRV2624_RAM_DATA,
+ (addr_pointer >> 8) & 0xFF); // Start address upper byte
+ TSH_CHECK_OK(status);
+
+ status =
+ drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA,
+ (addr_pointer) & 0xFF); // Start address lower byte
+ TSH_CHECK_OK(status);
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA,
+ ((wav->length * 2) & 0x3F) | (wav->repeat << 6));
+ TSH_CHECK_OK(status);
+
+ addr_pointer += wav->length * 2;
+ TSH_CHECK(addr_pointer + DRV2624_LIB_MAX_SEQ_LEN <= DRV2624_RAM_SIZE,
+ TS_ENOMEM);
+ }
+
+ // Copy waveform data
+ addr_pointer = waveform_data_start_address;
+ for (int i = 0; i < wave_list->registered_waveforms; i++) {
+ drv2624_waveform_t *wav = wave_list->waveforms[i];
+
+ for (int j = 0; j < wav->length; j++) {
+ if (wav->linear_ramp) {
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA,
+ (wav->sequence[j] & 0x7F) | (1 << 7));
+ } else {
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA,
+ (wav->sequence[j] & 0x7F));
+ }
+ TSH_CHECK_OK(status);
+
+ status = drv262x_set_reg(drv->i2c_bus, DRV2624_RAM_DATA, wav->time[j]);
+ TSH_CHECK_OK(status);
+ }
+
+ addr_pointer += wav->length * 2;
+ TSH_CHECK(addr_pointer <= DRV2624_RAM_SIZE, TS_ENOMEM);
+ }
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv2624_waveform_configuration(void) {
+ TSH_DECLARE;
+ ts_t status;
+
+ drv2624_waveform_list_t *wave_list = &g_waveform_list;
+
+ // Clear waveform list
+ memset(wave_list, 0, sizeof(drv2624_waveform_list_t));
+
+ // Register haptic waveforms, waveforms are assigned IDs based on the order
+ // of registration starting from 1.
+
+ TSH_CHECK_OK(
+ drv2624_register_waveform(wave_list, &sharp_btn_click_effect)); // ID:1
+
+ /** Add more waveforms here .. */
+ // TSH_CHECK_OK(drv2624_register_waveform(wave_list, &waveform2));
+
+ status = drv2624_load_ram(wave_list);
+ TSH_CHECK_OK(status);
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv2624_play_waveform(uint8_t waveform_id) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ TSH_CHECK_ARG(waveform_id > 0 &&
+ waveform_id <= g_waveform_list.registered_waveforms);
+
+ drv->rtp_mode = false;
+
+ // Set driver to waveform mode
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R7, DRV262X_R7_MODE_MASK,
+ (DRV262X_R7_MODE_WAVEFORM << DRV262X_R7_MODE_POS) & DRV262X_R7_MODE_MASK);
+ TSH_CHECK_OK(status);
+
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R7, DRV262X_R7_TRIG_PIN_FUNC_MASK,
+ (DRV262X_R7_TRIG_PIN_FUNC_INT << DRV262X_R7_TRIG_PIN_FUNC_POS) &
+ DRV262X_R7_TRIG_PIN_FUNC_MASK);
+ TSH_CHECK_OK(status);
+
+ // DRV2424 could play waveforms from RAM with different timing resolution
+ // set the timing according to waveform settings.
+ if (g_waveform_list.waveforms[waveform_id - 1]->short_timing) {
+ status = drv262x_reg_mask_modify(drv->i2c_bus, DRV262X_RD,
+ DRV262X_RD_PLAYBACK_INTERVAL_MASK,
+ 0x1 << DRV262X_RD_PLAYBACK_INTERVAL_POS);
+ TSH_CHECK_OK(status);
+
+ } else {
+ status = drv262x_reg_mask_modify(drv->i2c_bus, DRV262X_RD,
+ DRV262X_RD_PLAYBACK_INTERVAL_MASK,
+ 0x0 << DRV262X_RD_PLAYBACK_INTERVAL_POS);
+ TSH_CHECK_OK(status);
+ }
+
+ // Set first waveform slot
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_RF, waveform_id);
+ TSH_CHECK_OK(status);
+
+ // Make sure that the second slot is empty (end of sequence)
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_R10, 0);
+ TSH_CHECK_OK(status);
+
+ // Start playback with GO bit
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_RC, DRV262X_RC_GO_MASK);
+ TSH_CHECK_OK(status);
+
+cleanup:
+ TSH_RETURN;
+}
+
+#endif // HAPTIC_CHIP_DRV2624
+
+static ts_t drv262x_actuator_configuration() {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ uint8_t reg_mask = DRV262X_R8_AUTO_BRK_INT_O_STBY_MASK;
+
+#ifdef ACTUATOR_LRA
+ reg_mask |= DRV262X_R8_LRA_ERM_MASK; // Set LRA acuator
+#endif
+
+#ifdef ACTUATOR_OPEN_LOOP
+ reg_mask |= DRV262X_R8_CONTROL_LOOP_MASK; // Open loop control
+#endif
+
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_R8, reg_mask);
+ TSH_CHECK_OK(status);
+
+ // Set RATED_VOLTAGE
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_R1F, ACTUATOR_RATED_VOLTAGE);
+ TSH_CHECK_OK(status);
+
+ // SET OD_CLAMP
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_R20, ACTUATOR_OD_CLAMP);
+ TSH_CHECK_OK(status);
+
+#ifdef ACTUATOR_OPEN_LOOP
+
+ status =
+ drv262x_set_reg(drv->i2c_bus, DRV262X_R2F, ACTUATOR_LRA_PERIOD & 0xFF);
+ TSH_CHECK_OK(status);
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_R2E, ACTUATOR_LRA_PERIOD >> 8);
+ TSH_CHECK_OK(status);
+
+ // Set sin-wave driving shape
+ status = drv262x_reg_mask_modify(drv->i2c_bus, DRV262X_R2C,
+ DRV262X_R2C_LRA_WAVE_SHAPE_MASK,
+ DRV262X_R2C_LRA_WAVE_SHAPE_MASK);
+ TSH_CHECK_OK(status);
+
+#endif
+
+ // Set FB_BREAK_FACTOR, LOOP_GAIN, BEMF_GAIN
+ status = drv262x_set_reg(
+ drv->i2c_bus, DRV262X_R23,
+ ((ACTUATOR_FB_BRK_FACTOR << DRV262X_R23_FB_BREAK_FACTOR_POS) &
+ DRV262X_R23_FB_BREAK_FACTOR_MASK) |
+ ((ACTUATOR_LOOP_GAIN << DRV262X_R23_LOOP_GAIN_POS) &
+ DRV262X_R23_LOOP_GAIN_MASK) |
+ ((ACTUATOR_BEMF_GAIN << DRV262X_R23_BEMF_GAIN_POS) &
+ DRV262X_R23_BEMF_GAIN_MASK));
+ TSH_CHECK_OK(status);
+
+ // Set DRIVE_TIME
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R27, DRV262X_R27_DRIVE_TIME_MASK,
+ (ACTUATOR_DRIVE_TIME << DRV262X_R27_DRIVE_TIME_POS) &
+ DRV262X_R27_DRIVE_TIME_MASK);
+ TSH_CHECK_OK(status);
+
+ // Set BLANKING_TIME, IDISS_TIME
+ status =
+ drv262x_set_reg(drv->i2c_bus, DRV262X_R28,
+ ((ACTUATOR_IDISS_TIME << DRV262X_R28_IDISS_TIME_POS) &
+ DRV262X_R28_IDISS_TIME_MASK) |
+ ((ACTUATOR_BLANK_TIME << DRV262X_R28_BLANK_TIME_POS) &
+ DRV262X_R28_BLANK_TIME_MASK));
+ TSH_CHECK_OK(status);
+
+ // Set ZC_DET_TIME, SAMPLE_TIME
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R29,
+ DRV262X_R29_ZC_DET_TIME_MASK | DRV262X_R29_SAMPLE_TIME_MASK,
+ ((ACTUATOR_ZC_DET_TIME << DRV262X_R29_ZC_DET_TIME_POS) &
+ DRV262X_R29_ZC_DET_TIME_MASK) |
+ ((ACTUATOR_SAMPLE_TIME << DRV262X_R29_SAMPLE_TIME_POS) &
+ DRV262X_R29_SAMPLE_TIME_MASK));
+ TSH_CHECK_OK(status);
+
+#ifdef HAPTIC_CHIP_DRV2624
+ // DRV2624 do not have a predefined waveform library, but instead it has a
+ // dedicated 1KB RAM which could be filled with custom waveforms data.
+ status = drv2624_waveform_configuration();
+ TSH_CHECK_OK(status);
+#endif
+
+cleanup:
+ TSH_RETURN;
+}
+
+static ts_t drv262x_play_rtp(int8_t amplitude, uint16_t duration_ms) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ if (!drv->rtp_mode) {
+ // Switch to RTP mode
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R7, DRV262X_R7_MODE_MASK,
+ (DRV262X_R7_MODE_RTP << DRV262X_R7_MODE_POS) & DRV262X_R7_MODE_MASK);
+ TSH_CHECK_OK(status);
+
+ status = drv262x_reg_mask_modify(
+ drv->i2c_bus, DRV262X_R7, DRV262X_R7_TRIG_PIN_FUNC_MASK,
+ (DRV262X_R7_TRIG_PIN_FUNC_EXT_TRIG << DRV262X_R7_TRIG_PIN_FUNC_POS) &
+ DRV262X_R7_TRIG_PIN_FUNC_MASK);
+ TSH_CHECK_OK(status);
+
+ drv->rtp_mode = true;
+ }
+
+ // Set RTP amplitude
+ status = drv262x_set_reg(drv->i2c_bus, DRV262X_RE, (uint8_t)amplitude);
+ TSH_CHECK_OK(status);
+
+ duration_ms = MIN(duration_ms, 6500);
+
+ if (duration_ms > 0) {
+ DRV262X_TRIG_TIM->CNT = 1;
+ DRV262X_TRIG_TIM->CCR1 = 1;
+ DRV262X_TRIG_TIM->ARR = duration_ms * 10;
+ DRV262X_TRIG_TIM->CR1 |= TIM_CR1_CEN;
+ }
+
+cleanup:
+ TSH_RETURN;
+}
+
+ts_t haptic_init(void) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ if (drv->initialized) {
+ return TS_OK;
+ }
+
+ TSH_DECLARE;
+ ts_t status;
+
+ memset(drv, 0, sizeof(drv262x_driver_t));
+
+ GPIO_InitTypeDef GPIO_InitStructure = {0};
+
+#ifdef DRV262X_RESET_PIN
+ DRV262X_RESET_CLK_ENA();
+ GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;
+ GPIO_InitStructure.Pull = GPIO_NOPULL;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
+ GPIO_InitStructure.Pin = DRV262X_RESET_PIN;
+ HAL_GPIO_WritePin(DRV262X_RESET_PORT, DRV262X_RESET_PIN, GPIO_PIN_RESET);
+ HAL_GPIO_Init(DRV262X_RESET_PORT, &GPIO_InitStructure);
+ systick_delay_ms(1);
+ HAL_GPIO_WritePin(DRV262X_RESET_PORT, DRV262X_RESET_PIN, GPIO_PIN_SET);
+ systick_delay_ms(1);
+#endif
+
+ DRV262X_TRIG_CLK_ENA();
+ GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
+ GPIO_InitStructure.Pull = GPIO_PULLDOWN;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
+ GPIO_InitStructure.Pin = DRV262X_TRIG_PIN;
+ GPIO_InitStructure.Alternate = DRV262X_TRIG_AF;
+ HAL_GPIO_Init(DRV262X_TRIG_PORT, &GPIO_InitStructure);
+
+ drv->i2c_bus = i2c_bus_open(DRV262X_I2C_INSTANCE);
+ TSH_CHECK(drv->i2c_bus != NULL, TS_EIO);
+
+ // Read haptic driver model and revision
+ uint8_t reg_value;
+ status = drv262x_read_reg(drv->i2c_bus, DRV262X_R0, ®_value);
+ TSH_CHECK_OK(status);
+
+#if defined(HAPTIC_CHIP_DRV2624)
+ TSH_CHECK((reg_value >> 4) == 0x0, TS_EINVAL);
+#elif defined(HAPTIC_CHIP_DRV2625)
+ TSH_CHECK((reg_value >> 4) == 0x1, TS_EINVAL);
+#endif
+
+ status = drv262x_actuator_configuration();
+ TSH_CHECK_OK(status);
+
+ DRV262X_TRIG_TIM_FORCE_RESET();
+ DRV262X_TRIG_TIM_RELEASE_RESET();
+ DRV262X_TRIG_TIM_CLK_ENA();
+
+ TIM_HandleTypeDef TIM_Handle = {0};
+ TIM_Handle.State = HAL_TIM_STATE_RESET;
+ TIM_Handle.Instance = DRV262X_TRIG_TIM;
+ TIM_Handle.Init.Period = 0;
+ TIM_Handle.Init.Prescaler = SystemCoreClock / 10000;
+ TIM_Handle.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
+ TIM_Handle.Init.CounterMode = TIM_COUNTERMODE_UP;
+ TIM_Handle.Init.RepetitionCounter = 0;
+ status =
+ hal_status_to_ts(HAL_TIM_OnePulse_Init(&TIM_Handle, TIM_OPMODE_SINGLE));
+ TSH_CHECK_OK(status);
+
+ TIM_OnePulse_InitTypeDef TIM_OP_InitStructure = {0};
+ TIM_OP_InitStructure.OCMode = TIM_OCMODE_PWM2;
+ TIM_OP_InitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;
+ TIM_OP_InitStructure.Pulse = 1;
+ TIM_OP_InitStructure.OCNPolarity = TIM_OCNPOLARITY_HIGH;
+ status = hal_status_to_ts(HAL_TIM_OnePulse_ConfigChannel(
+ &TIM_Handle, &TIM_OP_InitStructure, TIM_CHANNEL_1, TIM_CHANNEL_2));
+ TSH_CHECK_OK(status);
+
+ status = hal_status_to_ts(HAL_TIM_OC_Start(&TIM_Handle, TIM_CHANNEL_1));
+ TSH_CHECK_OK(status);
+
+ DRV262X_TRIG_TIM->BDTR |= TIM_BDTR_MOE;
+
+ drv->initialized = true;
+ drv->enabled = true;
+
+ TSH_RETURN;
+
+cleanup:
+ haptic_deinit();
+ TSH_RETURN;
+}
+
+void haptic_deinit(void) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ i2c_bus_close(drv->i2c_bus);
+
+ GPIO_InitTypeDef GPIO_InitStructure = {0};
+
+#ifdef DRV262X_RESET_PIN
+ // External pull-down on NRST pin ensures that the DRV262X goes into
+ // shutdown mode when the reset GPIO is deinitialized.
+ GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStructure.Pull = GPIO_NOPULL;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
+ GPIO_InitStructure.Pin = DRV262X_RESET_PIN;
+ HAL_GPIO_Init(DRV262X_RESET_PORT, &GPIO_InitStructure);
+#endif
+
+ GPIO_InitStructure.Mode = GPIO_MODE_ANALOG;
+ GPIO_InitStructure.Pull = GPIO_NOPULL;
+ GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_LOW;
+ GPIO_InitStructure.Pin = DRV262X_TRIG_PIN;
+ HAL_GPIO_Init(DRV262X_TRIG_PORT, &GPIO_InitStructure);
+
+ DRV262X_TRIG_TIM_FORCE_RESET();
+ DRV262X_TRIG_TIM_RELEASE_RESET();
+ DRV262X_TRIG_TIM_CLK_DIS();
+
+ memset(drv, 0, sizeof(drv262x_driver_t));
+}
+
+ts_t haptic_set_enabled(bool enabled) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ if (!drv->initialized) {
+ return TS_ENOINIT;
+ }
+
+ drv->enabled = enabled;
+
+ return TS_OK;
+}
+
+bool haptic_get_enabled(void) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ if (!drv->initialized) {
+ return false;
+ }
+
+ return drv->enabled;
+}
+
+ts_t haptic_play(haptic_effect_t effect) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ TSH_CHECK(drv->initialized, TS_ENOINIT);
+ TSH_CHECK(drv->enabled, TS_ENOEN);
+
+ switch (effect) {
+ case HAPTIC_BUTTON_PRESS:
+
+#if defined(HAPTIC_CHIP_DRV2624)
+ status = drv2624_play_waveform(1); // Sharp button click effect
+ TSH_CHECK_OK(status);
+#elif defined(HAPTIC_CHIP_DRV2625)
+ status = drv262x_play_rtp(PRESS_EFFECT_AMPLITUDE, PRESS_EFFECT_DURATION);
+ TSH_CHECK_OK(status);
+#endif
+ break;
+ case HAPTIC_BOOTLOADER_ENTRY:
+ status = drv262x_play_rtp(BOOTLOADER_ENTRY_EFFECT_AMPLITUDE,
+ BOOTLOADER_ENTRY_EFFECT_DURATION);
+ TSH_CHECK_OK(status);
+ break;
+ case HAPTIC_POWER_ON:
+ status =
+ drv262x_play_rtp(POWER_ON_EFFECT_AMPLITUDE, POWER_ON_EFFECT_DURATION);
+ TSH_CHECK_OK(status);
+ default:
+ break;
+ }
+
+cleanup:
+ TSH_RETURN;
+}
+
+ts_t haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms) {
+ drv262x_driver_t *drv = &g_drv262x_driver;
+
+ TSH_DECLARE;
+ ts_t status;
+
+ TSH_CHECK(drv->initialized, TS_ENOINIT);
+ TSH_CHECK(drv->enabled, TS_ENOEN);
+
+ // Clamp amplitude percentage to 0-100%
+ amplitude_pct = MIN(MAX(amplitude_pct, 0), 100);
+
+ status = drv262x_play_rtp((int8_t)((amplitude_pct * MAX_AMPLITUDE) / 100),
+ duration_ms);
+ TSH_CHECK_OK(status);
+
+cleanup:
+ TSH_RETURN;
+}
+
+#endif // KERNEL_MODE
diff --git a/core/embed/io/haptic/drv262x/drv262x.h b/core/embed/io/haptic/drv262x/drv262x.h
new file mode 100644
index 000000000..1e8bc9838
--- /dev/null
+++ b/core/embed/io/haptic/drv262x/drv262x.h
@@ -0,0 +1,501 @@
+/*
+ * 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.p
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+// I2C address of the DRV262X on the I2C bus.
+#define DRV262X_I2C_ADDRESS 0x5A
+
+// ------------------------------------------------------------
+// DRV262X registers
+// ------------------------------------------------------------
+
+// Register 0x00
+#define DRV262X_R0 0x00
+#define DRV262X_R0_CHIPID_POS 4
+#define DRV262X_R0_CHIPID_MASK 0xF << DRV2625_R0_CHIPID_POS
+#define DRV262X_R0_REV_POS 0
+#define DRV262X_R0_REV_MASK 0xF << DRV2625_R0_REV_POS
+
+// Register 0x01
+#define DRV262X_R1 0x01
+#define DRV262X_R1_OC_DETECT_POS 0
+#define DRV262X_R1_OC_DETECT_MASK 0x1 << DRV2625_R1_OC_DETECT_POS
+#define DRV262X_R1_OVER_TEMP_POS 1
+#define DRV262X_R1_OVER_TEMP_MASK 0x1 << DRV2625_R1_OVER_TEMP_POS
+#define DRV262X_R1_UVLO_POS 2
+#define DRV262X_R1_UVLO_MASK 0x1 << DRV2625_R1_UVLO_POS
+#define DRV262X_R1_PROCESS_DONE_POS 3
+#define DRV262X_R1_PROCESS_DONE_MASK 0x1 << DRV2625_R1_PROCESS_DONE_POS
+#define DRV262X_R1_PROG_ERROR_POS 4
+#define DRV262X_R1_PROG_ERROR_MASK 0x1 << DRV2625_R1_PROG_ERROR_POS
+#define DRV262X_R1_DIAG_RESULT_POS 7
+#define DRV262X_R1_DIAG_RESULT_MASK 0x1 << DRV2625_R1_DIAG_RESULT_POS
+
+// Register 0x02
+#define DRV262X_R2 0x02
+#define DRV262X_R2_INTZ_POS 0
+#define DRV262X_R2_INTZ_MASK 0x1 << DRV262X_R2_INTZ_POS
+
+// Register 0x03
+#define DRV262X_R3 0x03
+#define DRV262X_R3_DIAG_RESULT_POS 0
+#define DRV262X_R3_DIAG_RESULT_MASK 0xFF << DRV262X_R3_DIAG_RESULT_POS
+
+// Register 0x04
+#define DRV262X_R4 0x04
+#define DRV262X_R4_VBAT_POS 0
+#define DRV262X_R4_VBAT_MASK 0xFF << DRV262X_R4_VBAT_POS
+
+// Register 0x05
+#define DRV262X_R5_LRA_PERIOD 0x05
+#define DRV262X_R5_LRA_PERIOD_MSB_POS 0
+#define DRV262X_R5_LRA_PERIOD_MSB_MASK 0x3 << DRV262X_LRA_PERIOD_MSB_POS
+
+// Register 0x06
+#define DRV262X_R6_LRA_PERIOD_LSB 0x06
+#define DRV262X_R6_LRA_PERIOD_LSB_POS 0
+#define DRV262X_R6_LRA_PERIOD_LSB_MASK 0xFF << DRV262X_LRA_PERIOD_LSB_POS
+
+// Register 0x7
+#define DRV262X_R7 0x07
+#define DRV262X_R7_MODE_POS 0
+#define DRV262X_R7_MODE_MASK 0x3 << DRV262X_R7_MODE_POS
+#define DRV262X_R7_TRIG_PIN_FUNC_POS 2
+#define DRV262X_R7_TRIG_PIN_FUNC_MASK 0x3 << DRV262X_R7_TRIG_PIN_FUNC_POS
+#define DRV262X_R7_LINEREF_COMP_SEL_POS 4
+#define DRV262X_R7_LINEREF_COMP_SEL_MASK 0x3 << DRV262X_R7_LINEREF_COMP_SEL_POS
+#define DRV262X_R7_LRA_PERIOD_AVG_DIS_POS 6
+#define DRV262X_R7_LRA_PERIOD_AVG_DIS_MASK \
+ 0x1 << DRV262X_R7_LRA_PERIOD_AVG_DIS_POS
+#define DRV262X_R7_I2C_BCAST_EN_POS 7
+#define DRV262X_R7_I2C_BCAST_EN_MASK 0x1 << DRV262X_R7_I2C_BCAST_EN_POS
+
+// MODE configurations
+#define DRV262X_R7_MODE_RTP 0x0
+#define DRV262X_R7_MODE_WAVEFORM 0x1
+#define DRV262X_R7_MODE_DIAG 0x2
+#define DRV262X_R7_MODE_AUTOCAL 0x3
+
+// Trig Pin Func configurations
+#define DRV262X_R7_TRIG_PIN_FUNC_PULSE 0x0
+#define DRV262X_R7_TRIG_PIN_FUNC_EXT_TRIG 0x1
+#define DRV262X_R7_TRIG_PIN_FUNC_INT 0x2
+
+// Register 0x08
+#define DRV262X_R8 0x8
+#define DRV262X_R8_INPUT_SLOPE_CHECK_POS 2
+#define DRV262X_R8_INPUT_SLOPE_CHECK_MASK \
+ 0x1 << DRV262X_R8_INPUT_SLOPE_CHECK_POS
+#define DRV262X_R8_AUTO_BRK_INT_O_STBY_POS 3
+#define DRV262X_R8_AUTO_BRK_INT_O_STBY_MASK \
+ 0x1 << DRV262X_R8_AUTO_BRK_INT_O_STBY_POS
+#define DRV262X_R8_AUTO_BRK_OL_POS 4
+#define DRV262X_R8_AUTO_BRK_OL_MASK 0x1 << DRV262X_R8_AUTO_BRK_OL_POS
+#define DRV262X_R8_HYBRID_LOOP_POS 5
+#define DRV262X_R8_HYBRID_LOOP_MASK 0x1 << DRV262X_R8_HYBRID_LOOP_POS
+#define DRV262X_R8_CONTROL_LOOP_POS 6
+#define DRV262X_R8_CONTROL_LOOP_MASK 0x1 << DRV262X_R8_CONTROL_LOOP_POS
+#define DRV262X_R8_LRA_ERM_POS 7
+#define DRV262X_R8_LRA_ERM_MASK 0x1 << DRV262X_R8_LRA_ERM_POS
+
+// Register 0x09
+#define DRV262X_R9 0x09
+#define DRV262X_R9_UVLO_THRES_POS 0
+#define DRV262X_R9_UVLO_THRES_MASK 0x3 << DRV262X_R9_UVLO_THRES_POS
+#define DRV262X_R9_BAT_LIFE_EXT_LVL_EN_POS 2
+#define DRV262X_R9_BAT_LIFE_EXT_LVL_EN_MASK \
+ 0x1 << DRV262X_R9_BAT_LIFE_EXT_LVL_EN_POS
+
+// Register 0x0A
+#define DRV262X_RA 0x0A
+#define DRV262X_RA_BAT_LIFE_EXT_LVL1_POS 0
+#define DRV262X_RA_BAT_LIFE_EXT_LVL1_MASK \
+ 0xFF << DRV262X_RA_BAT_LIFE_EXT_LVL1_POS
+
+// Register 0x0B
+#define DRV262X_RB 0x0B
+#define DRV262X_RB_BAT_LIFE_EXT_LVL2_POS 0
+#define DRV262X_RB_BAT_LIFE_EXT_LVL2_MASK \
+ 0xFF << DRV262X_RB_BAT_LIFE_EXT_LVL2_POS
+
+// Register 0x0C
+#define DRV262X_RC 0x0C
+#define DRV262X_RC_GO_POS 0
+#define DRV262X_RC_GO_MASK 0x1 << DRV262X_RC_GO_POS
+
+// Register 0x0D
+#define DRV262X_RD 0x0D
+#define DRV262X_RD_DIAG_MEM_GAIN_POS 0
+#define DRV262X_RD_DIAG_MEM_GAIN_MASK 0x3 << DRV262X_RD_DIAG_MEM_GAIN_POS
+#define DRV262X_RD_PLAYBACK_INTERVAL_POS 5
+#define DRV262X_RD_PLAYBACK_INTERVAL_MASK \
+ 0x3 << DRV262X_RD_PLAYBACK_INTERVAL_POS
+#define DRV262X_RD_LIB_SEL_POS 6
+#define DRV262X_RD_LIB_SEL_MASK 0x1 << DRV262X_RD_LIB_SEL_POS
+#define DRV262X_RD_LIB_ENABLE_POS 7
+#define DRV262X_RD_LIB_ENABLE_MASK 0x1 << DRV262X_RD_LIB_ENABLE_POS
+
+// Register 0x0E
+#define DRV262X_RE 0x0E
+#define DRV262X_REG_RTP_POS 0
+#define DRV262X_REG_RTP_MASK 0xFF << DRV262X_REG_RTP_POS
+
+// Register 0x0F
+#define DRV262X_RF 0x0F
+#define DRV262X_RF_WAV_FRM_SEQ1_POS 0
+#define DRV262X_RF_WAV_FRM_SEQ1_MASK 0x7F << DRV262X_RF_WAV_FRM_SEQ1_POS
+#define DRV262X_RF_WAIT1_POS 7
+#define DRV262X_RF_WAIT1_MASK 0x1 << DRV262X_RF_WAIT1_POS
+
+// Register 0x10
+#define DRV262X_R10 0x10
+#define DRV262X_R10_WAV_FRM_SEQ2_POS 0
+#define DRV262X_R10_WAV_FRM_SEQ2_MASK 0x7F << DRV262X_R10_WAV_FRM_SEQ2_POS
+#define DRV262X_R10_WAIT2_POS 7
+#define DRV262X_R10_WAIT2_MASK 0x1 << DRV262X_R10_WAIT2_POS
+
+// Register 0x11
+#define DRV262X_R11 0x11
+#define DRV262X_R11_WAV_FRM_SEQ3_POS 0
+#define DRV262X_R11_WAV_FRM_SEQ3_MASK 0x7F << DRV262X_R11_WAV_FRM_SEQ3_POS
+#define DRV262X_R11_WAIT3_POS 7
+#define DRV262X_R11_WAIT3_MASK 0x1 << DRV262X_R11_WAIT3_POS
+
+// Register 0x12
+#define DRV262X_R12 0x12
+#define DRV262X_R12_WAV_FRM_SEQ4_POS 0
+#define DRV262X_R12_WAV_FRM_SEQ4_MASK 0x7F << DRV262X_R12_WAV_FRM_SEQ4_POS
+#define DRV262X_R12_WAIT4_POS 7
+#define DRV262X_R12_WAIT4_MASK 0x1 << DRV262X_R12_WAIT4_POS
+
+// Register 0x13
+#define DRV262X_R13 0x13
+#define DRV262X_R13_WAV_FRM_SEQ5_POS 0
+#define DRV262X_R13_WAV_FRM_SEQ5_MASK 0x7F << DRV262X_R13_WAV_FRM_SEQ5_POS
+#define DRV262X_R13_WAIT5_POS 7
+#define DRV262X_R13_WAIT5_MASK 0x1 << DRV262X_R13_WAIT5_POS
+
+// Register 0x14
+#define DRV262X_R14 0x14
+#define DRV262X_R14_WAV_FRM_SEQ6_POS 0
+#define DRV262X_R14_WAV_FRM_SEQ6_MASK 0x7F << DRV262X_R14_WAV_FRM_SEQ6_POS
+#define DRV262X_R14_WAIT6_POS 7
+#define DRV262X_R14_WAIT6_MASK 0x1 << DRV262X_R14_WAIT6_POS
+
+// Register 0x15
+#define DRV262X_R15 0x15
+#define DRV262X_R15_WAV_FRM_SEQ7_POS 0
+#define DRV262X_R15_WAV_FRM_SEQ7_MASK 0x7F << DRV262X_R15_WAV_FRM_SEQ7_POS
+#define DRV262X_R15_WAIT7_POS 7
+#define DRV262X_R15_WAIT7_MASK 0x1 << DRV262X_R15_WAIT7_POS
+
+// Register 0x16
+#define DRV262X_R16 0x16
+#define DRV262X_R16_WAV_FRM_SEQ8_POS 0
+#define DRV262X_R16_WAV_FRM_SEQ8_MASK 0x7F << DRV262X_R16_WAV_FRM_SEQ8_POS
+#define DRV262X_R16_WAIT8_POS 7
+#define DRV262X_R16_WAIT8_MASK 0x1 << DRV262X_R16_WAIT8_POS
+
+// Register 0x17
+#define DRV262X_R17 0x17
+#define DRV262X_R17_WAW1_SEQ_LOOP_POS 0
+#define DRV262X_R17_WAW1_SEQ_LOOP_MASK 0x3 << DRV262X_R17_WAW1_SEQ_LOOP_POS
+#define DRV262X_R17_WAW2_SEQ_LOOP_POS 2
+#define DRV262X_R17_WAW2_SEQ_LOOP_MASK 0x3 << DRV262X_R17_WAW2_SEQ_LOOP_POS
+#define DRV262X_R17_WAW3_SEQ_LOOP_POS 4
+#define DRV262X_R17_WAW3_SEQ_LOOP_MASK 0x3 << DRV262X_R17_WAW3_SEQ_LOOP_POS
+#define DRV262X_R17_WAW4_SEQ_LOOP_POS 6
+#define DRV262X_R17_WAW4_SEQ_LOOP_MASK 0x3 << DRV262X_R17_WAW4_SEQ_LOOP_POS
+
+// Register 0x18
+#define DRV262X_R18 0x18
+#define DRV262X_R18_WAW5_SEQ_LOOP_POS 0
+#define DRV262X_R18_WAW5_SEQ_LOOP_MASK 0x3 << DRV262X_R18_WAW5_SEQ_LOOP_POS
+#define DRV262X_R18_WAW6_SEQ_LOOP_POS 2
+#define DRV262X_R18_WAW6_SEQ_LOOP_MASK 0x3 << DRV262X_R18_WAW6_SEQ_LOOP_POS
+#define DRV262X_R18_WAW7_SEQ_LOOP_POS 4
+#define DRV262X_R18_WAW7_SEQ_LOOP_MASK 0x3 << DRV262X_R18_WAW7_SEQ_LOOP_POS
+#define DRV262X_R18_WAW8_SEQ_LOOP_POS 6
+#define DRV262X_R18_WAW8_SEQ_LOOP_MASK 0x3 << DRV262X_R18_WAW8_SEQ_LOOP_POS
+
+// Register 0x19
+#define DRV262X_R19 0x19
+#define DRV262X_R19_WAW_SEQ_MAIN_LOOP_POS 0
+#define DRV262X_R19_WAW_SEQ_MAIN_LOOP_MASK \
+ 0x7 << DRV262X_R19_WAW_SEQ_MAIN_LOOP_POS
+
+// Register 0x1A
+#define DRV262X_R1A 0x1A
+#define DRV262X_R1A_ODT_POS 0
+#define DRV262X_R1A_ODT_MASK 0xFF << DRV262X_R1A_ODT_POS
+
+// Register 0x1B
+#define DRV262X_R1B 0x1B
+#define DRV262X_R1B_SPT_POS 0
+#define DRV262X_R1B_SPT_MASK 0xFF << DRV262X_R1B_SPT_POS
+
+// Register 0x1C
+#define DRV262X_R1C 0x1C
+#define DRV262X_R1C_SNT_POS 0
+#define DRV262X_R1C_SNT_MASK 0xFF << DRV262X_R1C_SNT_POS
+
+// Register 0x1D
+#define DRV262X_R1D 0x1D
+#define DRV262X_R1D_BRT_POS 0
+#define DRV262X_R1D_BRT_MASK 0xFF << DRV262X_R1D_BRT_POS
+
+// Register 0x1F
+#define DRV262X_R1F 0x1F
+#define DRV262X_R1F_RATED_VOLTAGE_POS 0
+#define DRV262X_R1F_RATED_VOLTAGE_MASK 0xFF << DRV262X_R1F_RATED_VOLTAGE_POS
+
+// Register 0x20
+#define DRV262X_R20 0x20
+#define DRV262X_R20_OD_CLAMP_POS 0
+#define DRV262X_R20_OD_CLAMP_MASK 0xFF << DRV262X_R20_OD_CLAMP_POS
+
+// Register 0x21
+#define DRV262X_R21 0x21
+#define DRV262X_R21_A_CAL_COMP_POS 0
+#define DRV262X_R21_A_CAL_COMP_MASK 0xFF << DRV262X_R21_A_CAL_COMP_POS
+
+// Register 0x22
+#define DRV262X_R22 0x22
+#define DRV262X_R22_A_CAL_BEMF_POS 0
+#define DRV262X_R22_A_CAL_BEMF_MASK 0xFF << DRV262X_R22_A_CAL_BEMF_POS
+
+// Register 0x23
+#define DRV262X_R23 0x23
+#define DRV262X_R23_BEMF_GAIN_POS 0
+#define DRV262X_R23_BEMF_GAIN_MASK 0x3 << DRV262X_R23_BEMF_GAIN_POS
+#define DRV262X_R23_LOOP_GAIN_POS 2
+#define DRV262X_R23_LOOP_GAIN_MASK 0x3 << DRV262X_R23_LOOP_GAIN_POS
+#define DRV262X_R23_FB_BREAK_FACTOR_POS 4
+#define DRV262X_R23_FB_BREAK_FACTOR_MASK 0x7 << DRV262X_R23_FB_BREAK_FACTOR_POS
+#define DRV262X_R23_NG_THRESH_POS 7
+#define DRV262X_R23_NG_THRESH_MASK 0x1 << DRV262X_R23_NG_THRESH_POS
+
+// Register 0x24
+#define DRV262X_R24 0x24
+#define DRV262X_R24_RATED_VOLTAGE_CLAMP_POS 0
+#define DRV262X_R24_RATED_VOLTAGE_CLAMP_MASK \
+ 0xFF << DRV262X_R24_RATED_VOLTAGE_CLAMP_POS
+
+// Register 0x25
+#define DRV262X_R25 0x25
+#define DRV262X_R25_OD_CLAMP_LVL1_POS 0
+#define DRV262X_R25_OD_CLAMP_LVL1_MASK 0xFF << DRV262X_R25_OD_CLAMP_LVL1_POS
+
+// Register 0x26
+#define DRV262X_R26 0x26
+#define DRV262X_R26_OD_CLAMP_LVL2_POS 0
+#define DRV262X_R26_OD_CLAMP_LVL2_MASK 0xFF << DRV262X_R26_OD_CLAMP_LVL2_POS
+
+// Register 0x27
+#define DRV262X_R27 0x27
+#define DRV262X_R27_DRIVE_TIME_POS 0
+#define DRV262X_R27_DRIVE_TIME_MASK 0x1F << DRV262X_R27_DRIVE_TIME_POS
+
+// Register 0x28
+#define DRV262X_R28 0x28
+#define DRV262X_R28_IDISS_TIME_POS 0
+#define DRV262X_R28_IDISS_TIME_MASK 0x0F << DRV262X_R28_IDISS_TIME_POS
+#define DRV262X_R28_BLANK_TIME_POS 4
+#define DRV262X_R28_BLANK_TIME_MASK 0x0F << DRV262X_R28_BLANK_TIME_POS
+
+// Register 0x29
+#define DRV262X_R29 0x29
+#define DRV262X_R29_ZC_DET_TIME_POS 0
+#define DRV262X_R29_ZC_DET_TIME_MASK 0x3 << DRV262X_R29_ZC_DET_TIME_POS
+#define DRV262X_R29_SAMPLE_TIME_POS 2
+#define DRV262X_R29_SAMPLE_TIME_MASK 0x3 << DRV262X_R29_SAMPLE_TIME_POS
+#define DRV262X_R29_OC_CLAMP_TIME_POS 4
+#define DRV262X_R29_OC_CLAMP_TIME_MASK 0x3 << DRV262X_R29_OC_CLAMP_TIME_POS
+
+// Register 0x2A
+#define DRV262X_R2A 0x2A
+#define DRV262X_R2A_AUTO_CAL_TIME_POS 0
+#define DRV262X_R2A_AUTO_CAL_TIME_MASK 0x3 << DRV262X_R2A_AUTO_CAL_TIME_POS
+
+// Register 0x2C
+#define DRV262X_R2C 0x2C
+#define DRV262X_R2C_LRA_WAVE_SHAPE_POS 0
+#define DRV262X_R2C_LRA_WAVE_SHAPE_MASK 0x1 << DRV262X_R2C_LRA_WAVE_SHAPE_POS
+#define DRV262X_R2C_AUTO_OL_CNT_POS 5
+#define DRV262X_R2C_AUTO_OL_CNT_MASK 0x3 << DRV262X_R2C_AUTO_OL_CNT_POS
+#define DRV262X_R2C_LRA_AUTO_OPEN_LOOP_POS 7
+#define DRV262X_R2C_LRA_AUTO_OPEN_LOOP_MASK \
+ 0x1 << DRV262X_R2C_LRA_AUTO_OPEN_LOOP_POS
+
+// Register 0x2E
+#define DRV262X_R2E 0x2E
+#define DRV262X_R2E_OL_LRA_PERIOD_HI_POS 0x2E
+#define DRV262X_R2E_OL_LRA_PERIOD_HI_MASK \
+ 0x03 << DRV262X_R2E_OL_LRA_PERIOD_HI_POS
+
+// Register 0x2F
+#define DRV262X_R2F 0x2F
+#define DRV262X_R2F_OL_LRA_PERIOD_LO_POS 0
+#define DRV262X_R2F_OL_LRA_PERIOD_LO_MASK \
+ 0xFF << DRV262X_R2F_OL_LRA_PERIOD_LO_POS
+
+// Register 0x30
+#define DRV262X_R30 0x30
+#define DRV262X_R30_CURRENT_K_POS 0
+#define DRV262X_R30_CURRENT_K_MASK 0xFF << DRV262X_R30_CURRENT_K_POS
+
+#ifdef HAPTIC_CHIP_DRV2624
+
+// DRV2624 RAM register address definitions
+#define DRV2624_RAM_ADDR_H 0xFD
+#define DRV2624_RAM_ADDR_L 0xFE
+#define DRV2624_RAM_DATA 0xFF
+
+#endif
+
+// ------------------------------------------------------------
+// DRV2625 effect types
+// ------------------------------------------------------------
+
+typedef enum {
+ STRONG_CLICK_100 = 1,
+ STRONG_CLICK_60 = 2,
+ STRONG_CLICK_30 = 3,
+ SHARP_CLICK_100 = 4,
+ SHARP_CLICK_60 = 5,
+ SHARP_CLICK_30 = 6,
+ SOFT_BUMP_100 = 7,
+ SOFT_BUMP_60 = 8,
+ SOFT_BUMP_30 = 9,
+ DOUBLE_CLICK_100 = 10,
+ DOUBLE_CLICK_60 = 11,
+ TRIPLE_CLICK_100 = 12,
+ SOFT_FUZZ_60 = 13,
+ STRONG_BUZZ_100 = 14,
+ ALERT_750MS_100 = 15,
+ ALERT_1000MS_100 = 16,
+ STRONG_CLICK_1_100 = 17,
+ STRONG_CLICK_2_80 = 18,
+ STRONG_CLICK_3_60 = 19,
+ STRONG_CLICK_4_30 = 20,
+ MEDIUM_CLICK_1_100 = 21,
+ MEDIUM_CLICK_2_80 = 22,
+ MEDIUM_CLICK_3_60 = 23,
+ SHARP_TICK_1_100 = 24,
+ SHARP_TICK_2_80 = 25,
+ SHARP_TICK_3_60 = 26,
+ SHORT_DOUBLE_CLICK_STRONG_1_100 = 27,
+ SHORT_DOUBLE_CLICK_STRONG_2_80 = 28,
+ SHORT_DOUBLE_CLICK_STRONG_3_60 = 29,
+ SHORT_DOUBLE_CLICK_STRONG_4_30 = 30,
+ SHORT_DOUBLE_CLICK_MEDIUM_1_100 = 31,
+ SHORT_DOUBLE_CLICK_MEDIUM_2_80 = 32,
+ SHORT_DOUBLE_CLICK_MEDIUM_3_60 = 33,
+ SHORT_DOUBLE_SHARP_TICK_1_100 = 34,
+ SHORT_DOUBLE_SHARP_TICK_2_80 = 35,
+ SHORT_DOUBLE_SHARP_TICK_3_60 = 36,
+ LONG_DOUBLE_SHARP_TICK_STRONG_1_100 = 37,
+ LONG_DOUBLE_SHARP_TICK_STRONG_2_80 = 38,
+ LONG_DOUBLE_SHARP_TICK_STRONG_3_60 = 39,
+ LONG_DOUBLE_SHARP_TICK_STRONG_4_30 = 40,
+ LONG_DOUBLE_SHARP_TICK_MEDIUM_1_100 = 41,
+ LONG_DOUBLE_SHARP_TICK_MEDIUM_2_80 = 42,
+ LONG_DOUBLE_SHARP_TICK_MEDIUM_3_60 = 43,
+ LONG_DOUBLE_SHARP_TICK_1_100 = 44,
+ LONG_DOUBLE_SHARP_TICK_2_80 = 45,
+ LONG_DOUBLE_SHARP_TICK_3_60 = 46,
+ BUZZ_1_100 = 47,
+ BUZZ_2_80 = 48,
+ BUZZ_3_60 = 49,
+ BUZZ_4_40 = 50,
+ BUZZ_5_20 = 51,
+ PULSING_STRONG_1_100 = 52,
+ PULSING_STRONG_2_60 = 53,
+ PULSING_MEDIUM_1_100 = 54,
+ PULSING_MEDIUM_2_60 = 55,
+ PULSING_SHARP_1_100 = 56,
+ PULSING_SHARP_2_60 = 57,
+ TRANSITION_CLICK_1_100 = 58,
+ TRANSITION_CLICK_2_80 = 59,
+ TRANSITION_CLICK_3_60 = 60,
+ TRANSITION_CLICK_4_40 = 61,
+ TRANSITION_CLICK_5_20 = 62,
+ TRANSITION_CLICK_6_10 = 63,
+ TRANSITION_HUM_1_100 = 64,
+ TRANSITION_HUM_2_80 = 65,
+ TRANSITION_HUM_3_60 = 66,
+ TRANSITION_HUM_4_40 = 67,
+ TRANSITION_HUM_5_20 = 68,
+ TRANSITION_HUM_6_10 = 69,
+ TRANSITION_RAMP_DOWN_LONG_SMOOTH_1 = 70,
+ TRANSITION_RAMP_DOWN_LONG_SMOOTH_2 = 71,
+ TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1 = 72,
+ TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2 = 73,
+ TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1 = 74,
+ TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2 = 75,
+ TRANSITION_RAMP_DOWN_LONG_SHARP_1 = 76,
+ TRANSITION_RAMP_DOWN_LONG_SHARP_2 = 77,
+ TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1 = 78,
+ TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2 = 79,
+ TRANSITION_RAMP_DOWN_SHORT_SHARP_1 = 80,
+ TRANSITION_RAMP_DOWN_SHORT_SHARP_2 = 81,
+ TRANSITION_RAMP_UP_LONG_SMOOTH_1 = 82,
+ TRANSITION_RAMP_UP_LONG_SMOOTH_2 = 83,
+ TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1 = 84,
+ TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2 = 85,
+ TRANSITION_RAMP_UP_SHORT_SMOOTH_1 = 86,
+ TRANSITION_RAMP_UP_SHORT_SMOOTH_2 = 87,
+ TRANSITION_RAMP_UP_LONG_SHARP_1 = 88,
+ TRANSITION_RAMP_UP_LONG_SHARP_2 = 89,
+ TRANSITION_RAMP_UP_MEDIUM_SHARP_1 = 90,
+ TRANSITION_RAMP_UP_MEDIUM_SHARP_2 = 91,
+ TRANSITION_RAMP_UP_SHORT_SHARP_1 = 92,
+ TRANSITION_RAMP_UP_SHORT_SHARP_2 = 93,
+ TRANSITION_RAMP_DOWN_LONG_SMOOTH_1_50 = 94,
+ TRANSITION_RAMP_DOWN_LONG_SMOOTH_2_50 = 95,
+ TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_1_50 = 96,
+ TRANSITION_RAMP_DOWN_MEDIUM_SMOOTH_2_50 = 97,
+ TRANSITION_RAMP_DOWN_SHORT_SMOOTH_1_50 = 98,
+ TRANSITION_RAMP_DOWN_SHORT_SMOOTH_2_50 = 99,
+ TRANSITION_RAMP_DOWN_LONG_SHARP_1_50 = 100,
+ TRANSITION_RAMP_DOWN_LONG_SHARP_2_50 = 101,
+ TRANSITION_RAMP_DOWN_MEDIUM_SHARP_1_50 = 102,
+ TRANSITION_RAMP_DOWN_MEDIUM_SHARP_2_50 = 103,
+ TRANSITION_RAMP_DOWN_SHORT_SHARP_1_50 = 104,
+ TRANSITION_RAMP_DOWN_SHORT_SHARP_2_50 = 105,
+ TRANSITION_RAMP_UP_LONG_SMOOTH_1_50 = 106,
+ TRANSITION_RAMP_UP_LONG_SMOOTH_2_50 = 107,
+ TRANSITION_RAMP_UP_MEDIUM_SMOOTH_1_50 = 108,
+ TRANSITION_RAMP_UP_MEDIUM_SMOOTH_2_50 = 109,
+ TRANSITION_RAMP_UP_SHORT_SMOOTH_1_50 = 110,
+ TRANSITION_RAMP_UP_SHORT_SMOOTH_2_50 = 111,
+ TRANSITION_RAMP_UP_LONG_SHARP_1_50 = 112,
+ TRANSITION_RAMP_UP_LONG_SHARP_2_50 = 113,
+ TRANSITION_RAMP_UP_MEDIUM_SHARP_1_50 = 114,
+ TRANSITION_RAMP_UP_MEDIUM_SHARP_2_50 = 115,
+ TRANSITION_RAMP_UP_SHORT_SHARP_1_50 = 116,
+ TRANSITION_RAMP_UP_SHORT_SHARP_2_50 = 117,
+ LONG_BUZZ_FROM_PROGRAMMATIC_STOPPING = 118,
+ SMOOTH_HUM_1_100 = 119,
+ SMOOTH_HUM_2_80 = 120,
+ SMOOTH_HUM_3_60 = 121,
+ SMOOTH_HUM_4_40 = 122,
+ SMOOTH_HUM_5_20 = 123,
+} drv2625_lib_effect_t;
diff --git a/core/embed/io/haptic/inc/io/haptic.h b/core/embed/io/haptic/inc/io/haptic.h
index f24a11632..6e3b40944 100644
--- a/core/embed/io/haptic/inc/io/haptic.h
+++ b/core/embed/io/haptic/inc/io/haptic.h
@@ -34,58 +34,59 @@ typedef enum {
#ifdef KERNEL_MODE
-// Initializes the haptic driver
-//
-// The function initializes the GPIO pins and the hardware
-// peripherals used by the haptic driver.
-//
-// Returns `true` if the initialization was successful.
-bool haptic_init(void);
+/**
+ * @brief Initializes the haptic driver
+ *
+ * The function initializes the GPIO pins and the hardware
+ * peripherals used by the haptic driver.
+ *
+ * @return TS_OK if the initialization was successful
+ */
+ts_t __wur haptic_init(void);
-// Deinitializes the haptic driver
-//
-// The function deinitializes the hardware peripherals used by the
-// haptic driver so the device can be eventually put into a low-power mode.
+/**
+ * @brief Deinitializes the haptic driver
+ *
+ * The function deinitializes the hardware peripherals used by the
+ * haptic driver.
+ */
void haptic_deinit(void);
#endif // KERNEL_MODE
-// Enables or disables the haptic driver
-//
-// When the driver is disabled, it does not play any haptic effects
-// and potentially can put the controller into a low-power mode.
-//
-// The driver is enabled by default (after initialization).
-void haptic_set_enabled(bool enabled);
+/**
+ * @brief Enables/disables the haptic driver
+ *
+ * @param enabled If `true`, enables the haptic driver; if `false`, disables it
+ * @return TS_OK if the operation was successful
+ */
+ts_t haptic_set_enabled(bool enabled);
-// Returns `true` if haptic driver is enabled
+/**
+ * @brief Gets tha haptic driver enable state
+ *
+ * @return true if the haptic driver is enabled
+ */
bool haptic_get_enabled(void);
-// Tests the haptic driver, playing at maximum amplitude for the given duration
-//
-// This function is used during production testing to verify that the haptic
-// motor is working correctly.
-//
-// Returns `true` if the test effect was successfully started.
-bool haptic_test(uint16_t duration_ms);
-
-// Plays one of haptic effects
-//
-// The function stops playing any currently running effect and
-// starts playing the specified effect.
-//
-// Returns `true` if the effect was successfully started.
-bool haptic_play(haptic_effect_t effect);
+/**
+ * @brief Plays selected haptic effect
+ *
+ * The function stops playing any currently running effect and
+ * starts playing the specified effect.
+ *
+ * @return TS_OK if the effect was successfully started
+ */
+ts_t haptic_play(haptic_effect_t effect);
-// Starts the haptic motor with a specified amplitude (in percent) for a
-// specified duration (in milliseconds).
-//
-// The function stops playing any currently running effect and
-// starts playing the specified effect.
-//
-// The function can be invoked repeatedly during the specified duration
-// (`duration_ms`) to modify the amplitude dynamically, allowing
-// the creation of customized haptic effects.
-//
-// Returns `true` if the effect was successfully started.
-bool haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms);
+/**
+ * @brief Resonates the haptic motor with specified amplitude (in percent) and
+ * duration (in milliseconds)
+ *
+ * The function can be invoked repeatedly during the specified duration
+ * (`duration_ms`) to modify the amplitude dynamically, allowing
+ * the creation of customized haptic effects.
+ *
+ * @return TS_OK if the effect was successfully started
+ */
+ts_t haptic_play_custom(int8_t amplitude_pct, uint16_t duration_ms);
diff --git a/core/embed/models/T3T1/boards/trezor_t3t1_revE.h b/core/embed/models/T3T1/boards/trezor_t3t1_revE.h
index 1860a1b88..126a9b1d9 100644
--- a/core/embed/models/T3T1/boards/trezor_t3t1_revE.h
+++ b/core/embed/models/T3T1/boards/trezor_t3t1_revE.h
@@ -94,18 +94,18 @@
#define TOUCH_ON_PORT GPIOB
#define TOUCH_ON_PIN GPIO_PIN_0
-#define DRV2625_I2C_INSTANCE 1
-#define DRV2625_TRIG_PIN GPIO_PIN_8
-#define DRV2625_TRIG_PORT GPIOB
-#define DRV2625_TRIG_CLK_ENA __HAL_RCC_GPIOB_CLK_ENABLE
-#define DRV2625_TRIG_AF GPIO_AF14_TIM16
-#define DRV2625_TRIG_TIM TIM16
-#define DRV2625_TRIG_TIM_CLK_ENA __HAL_RCC_TIM16_CLK_ENABLE
-#define DRV2625_TRIG_TIM_CLK_DIS __HAL_RCC_TIM16_CLK_DISABLE
-#define DRV2625_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM16_FORCE_RESET
-#define DRV2625_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM16_RELEASE_RESET
-
-#define HAPTIC_ACTUATOR "actuators/vg1040003d.h"
+#define HAPTIC_CHIP_DRV2625
+#define DRV262X_I2C_INSTANCE 1
+#define DRV262X_ACTUATOR "actuators/vg1040003d.h"
+#define DRV262X_TRIG_PIN GPIO_PIN_8
+#define DRV262X_TRIG_PORT GPIOB
+#define DRV262X_TRIG_CLK_ENA __HAL_RCC_GPIOB_CLK_ENABLE
+#define DRV262X_TRIG_AF GPIO_AF14_TIM16
+#define DRV262X_TRIG_TIM TIM16
+#define DRV262X_TRIG_TIM_CLK_ENA __HAL_RCC_TIM16_CLK_ENABLE
+#define DRV262X_TRIG_TIM_CLK_DIS __HAL_RCC_TIM16_CLK_DISABLE
+#define DRV262X_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM16_FORCE_RESET
+#define DRV262X_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM16_RELEASE_RESET
#define OPTIGA_I2C_INSTANCE 2
#define OPTIGA_RST_PORT GPIOB
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
index 8d156c1cf..cb0c514eb 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revA.h
@@ -131,20 +131,21 @@
#define TOUCH_INT_PORT GPIOC
#define TOUCH_INT_PIN GPIO_PIN_3
-#define DRV2625_I2C_INSTANCE 2
-#define HAPTIC_ACTUATOR "actuators/ld0625bc.h"
-#define DRV2625_TRIG_PIN GPIO_PIN_2
-#define DRV2625_TRIG_PORT GPIOA
-#define DRV2625_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
-#define DRV2625_TRIG_AF GPIO_AF14_TIM15
-#define DRV2625_TRIG_TIM TIM15
-#define DRV2625_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
-#define DRV2625_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
-#define DRV2625_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
-#define DRV2625_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
-#define DRV2625_RESET_PIN GPIO_PIN_3
-#define DRV2625_RESET_PORT GPIOA
-#define DRV2625_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define HAPTIC_CHIP_DRV2624
+#define DRV262X_I2C_INSTANCE 2
+#define DRV262X_ACTUATOR "actuators/ld0625bc.h"
+#define DRV262X_TRIG_PIN GPIO_PIN_2
+#define DRV262X_TRIG_PORT GPIOA
+#define DRV262X_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define DRV262X_TRIG_AF GPIO_AF14_TIM15
+#define DRV262X_TRIG_TIM TIM15
+#define DRV262X_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
+#define DRV262X_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
+#define DRV262X_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
+#define DRV262X_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
+#define DRV262X_RESET_PIN GPIO_PIN_3
+#define DRV262X_RESET_PORT GPIOA
+#define DRV262X_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
#define OPTIGA_I2C_INSTANCE 3
#define OPTIGA_RST_PORT GPIOD
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
index f78d1f377..a4b7aeab5 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revB.h
@@ -131,20 +131,21 @@
#define TOUCH_INT_PORT GPIOC
#define TOUCH_INT_PIN GPIO_PIN_3
-#define DRV2625_I2C_INSTANCE 2
-#define HAPTIC_ACTUATOR "actuators/ld0625bc.h"
-#define DRV2625_TRIG_PIN GPIO_PIN_2
-#define DRV2625_TRIG_PORT GPIOA
-#define DRV2625_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
-#define DRV2625_TRIG_AF GPIO_AF14_TIM15
-#define DRV2625_TRIG_TIM TIM15
-#define DRV2625_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
-#define DRV2625_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
-#define DRV2625_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
-#define DRV2625_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
-#define DRV2625_RESET_PIN GPIO_PIN_3
-#define DRV2625_RESET_PORT GPIOA
-#define DRV2625_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define HAPTIC_CHIP_DRV2624
+#define DRV262X_I2C_INSTANCE 2
+#define DRV262X_ACTUATOR "actuators/ld0625bc.h"
+#define DRV262X_TRIG_PIN GPIO_PIN_2
+#define DRV262X_TRIG_PORT GPIOA
+#define DRV262X_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define DRV262X_TRIG_AF GPIO_AF14_TIM15
+#define DRV262X_TRIG_TIM TIM15
+#define DRV262X_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
+#define DRV262X_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
+#define DRV262X_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
+#define DRV262X_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
+#define DRV262X_RESET_PIN GPIO_PIN_3
+#define DRV262X_RESET_PORT GPIOA
+#define DRV262X_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
#define OPTIGA_I2C_INSTANCE 3
#define OPTIGA_RST_PORT GPIOD
diff --git a/core/embed/models/T3W1/boards/trezor_t3w1_revC.h b/core/embed/models/T3W1/boards/trezor_t3w1_revC.h
index 854fd3f91..3728b9480 100644
--- a/core/embed/models/T3W1/boards/trezor_t3w1_revC.h
+++ b/core/embed/models/T3W1/boards/trezor_t3w1_revC.h
@@ -149,20 +149,21 @@
#define TOUCH_INT_PORT GPIOC
#define TOUCH_INT_PIN GPIO_PIN_3
-#define DRV2625_I2C_INSTANCE 2
-#define HAPTIC_ACTUATOR "actuators/ld0625bc.h"
-#define DRV2625_TRIG_PIN GPIO_PIN_2
-#define DRV2625_TRIG_PORT GPIOA
-#define DRV2625_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
-#define DRV2625_TRIG_AF GPIO_AF14_TIM15
-#define DRV2625_TRIG_TIM TIM15
-#define DRV2625_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
-#define DRV2625_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
-#define DRV2625_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
-#define DRV2625_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
-#define DRV2625_RESET_PIN GPIO_PIN_3
-#define DRV2625_RESET_PORT GPIOA
-#define DRV2625_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define HAPTIC_CHIP_DRV2624
+#define DRV262X_I2C_INSTANCE 2
+#define DRV262X_ACTUATOR "actuators/ld0625bc.h"
+#define DRV262X_TRIG_PIN GPIO_PIN_2
+#define DRV262X_TRIG_PORT GPIOA
+#define DRV262X_TRIG_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
+#define DRV262X_TRIG_AF GPIO_AF14_TIM15
+#define DRV262X_TRIG_TIM TIM15
+#define DRV262X_TRIG_TIM_CLK_ENA __HAL_RCC_TIM15_CLK_ENABLE
+#define DRV262X_TRIG_TIM_CLK_DIS __HAL_RCC_TIM15_CLK_DISABLE
+#define DRV262X_TRIG_TIM_FORCE_RESET __HAL_RCC_TIM15_FORCE_RESET
+#define DRV262X_TRIG_TIM_RELEASE_RESET __HAL_RCC_TIM15_RELEASE_RESET
+#define DRV262X_RESET_PIN GPIO_PIN_3
+#define DRV262X_RESET_PORT GPIOA
+#define DRV262X_RESET_CLK_ENA __HAL_RCC_GPIOA_CLK_ENABLE
#define OPTIGA_I2C_INSTANCE 3
#define OPTIGA_RST_PORT GPIOD
diff --git a/core/embed/projects/prodtest/main.c b/core/embed/projects/prodtest/main.c
index 2dd8cd1c1..595e58a7d 100644
--- a/core/embed/projects/prodtest/main.c
+++ b/core/embed/projects/prodtest/main.c
@@ -182,7 +182,10 @@ static void drivers_init(void) {
sbu_init();
#endif
#ifdef USE_HAPTIC
- haptic_init();
+ ts_t status;
+ status = haptic_init();
+ if (ts_error(status)) {
+ }
#endif
#ifdef USE_RGB_LED
rgb_led_init();
diff --git a/core/embed/rtl/error_handling.c b/core/embed/rtl/error_handling.c
index 23fc32e9c..90d928f36 100644
--- a/core/embed/rtl/error_handling.c
+++ b/core/embed/rtl/error_handling.c
@@ -51,6 +51,11 @@ const char *ts_string(ts_t status) {
return "EIO";
} else if (ts_eq(status, TS_EBADMSG)) {
return "EBADMSG";
+ // Trezor-specific error codes
+ } else if (ts_eq(status, TS_ENOINIT)) {
+ return "ENOINIT";
+ } else if (ts_eq(status, TS_ENOEN)) {
+ return "ENOEN";
} else {
return "?ERROR";
}
diff --git a/core/embed/rtl/inc/rtl/error_handling.h b/core/embed/rtl/inc/rtl/error_handling.h
index 57cbe2022..1cc8a1c66 100644
--- a/core/embed/rtl/inc/rtl/error_handling.h
+++ b/core/embed/rtl/inc/rtl/error_handling.h
@@ -47,6 +47,7 @@ typedef struct {
/** List of Trezor-specific error codes with offset from 2000 to avoid mixing
* with standard errno codes */
#define TS_ENOINIT ts_make(2000) /* Not initialized */
+#define TS_ENOEN ts_make(2001) /* Not enabled */
/**
* Extracts the code integer value from status structure.
diff --git a/core/site_scons/models/T3T1/trezor_t3t1_revE.py b/core/site_scons/models/T3T1/trezor_t3t1_revE.py
index 839a62147..d5b46b4f6 100644
--- a/core/site_scons/models/T3T1/trezor_t3t1_revE.py
+++ b/core/site_scons/models/T3T1/trezor_t3t1_revE.py
@@ -83,7 +83,7 @@ def configure(
if "haptic" in features_wanted:
sources += [
- "embed/io/haptic/drv2625/drv2625.c",
+ "embed/io/haptic/drv262x/drv262x.c",
]
paths += ["embed/io/haptic/inc"]
features_available.append("haptic")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revA.py b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
index 0d3fb2d3b..a572fed06 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revA.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revA.py
@@ -116,7 +116,7 @@ def configure(
if "haptic" in features_wanted:
sources += [
- "embed/io/haptic/drv2625/drv2625.c",
+ "embed/io/haptic/drv262x/drv262x.c",
]
paths += ["embed/io/haptic/inc"]
features_available.append("haptic")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revB.py b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
index 5f5241298..4108709ab 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revB.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revB.py
@@ -118,7 +118,7 @@ def configure(
if "haptic" in features_wanted:
sources += [
- "embed/io/haptic/drv2625/drv2625.c",
+ "embed/io/haptic/drv262x/drv262x.c",
]
paths += ["embed/io/haptic/inc"]
features_available.append("haptic")
diff --git a/core/site_scons/models/T3W1/trezor_t3w1_revC.py b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
index 9e0145816..0aed85e3f 100644
--- a/core/site_scons/models/T3W1/trezor_t3w1_revC.py
+++ b/core/site_scons/models/T3W1/trezor_t3w1_revC.py
@@ -117,7 +117,7 @@ def configure(
if "haptic" in features_wanted:
sources += [
- "embed/io/haptic/drv2625/drv2625.c",
+ "embed/io/haptic/drv262x/drv262x.c",
]
paths += ["embed/io/haptic/inc"]
features_available.append("haptic")
Why this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.