/* * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include "ulp_riscv_i2c.h" #include "esp_check.h" #include "soc/rtc_i2c_reg.h" #include "soc/rtc_i2c_struct.h" #include "soc/rtc_io_struct.h" #include "soc/sens_reg.h" #include "soc/clk_tree_defs.h" #include "hal/i2c_ll.h" #include "driver/rtc_io.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" static const char *RTCI2C_TAG = "ulp_riscv_i2c"; #define I2C_CTRL_SLAVE_ADDR_MASK (0xFF << 0) #define I2C_CTRL_SLAVE_REG_ADDR_MASK (0xFF << 11) #define I2C_CTRL_MASTER_TX_DATA_MASK (0xFF << 19) #if CONFIG_IDF_TARGET_ESP32S3 #define ULP_I2C_CMD_RESTART 0 /*!i2c_pin_cfg.sda_io_num; gpio_num_t scl_io_num = cfg->i2c_pin_cfg.scl_io_num; bool sda_pullup_en = cfg->i2c_pin_cfg.sda_pullup_en; bool scl_pullup_en = cfg->i2c_pin_cfg.scl_pullup_en; /* Verify that the I2C GPIOs are valid */ ESP_RETURN_ON_ERROR(i2c_gpio_is_cfg_valid(sda_io_num, scl_io_num), RTCI2C_TAG, "RTC I2C GPIO config invalid"); /* Initialize SDA Pin */ ESP_RETURN_ON_ERROR(i2c_configure_io(sda_io_num, sda_pullup_en), RTCI2C_TAG, "RTC I2C SDA pin config failed"); /* Initialize SCL Pin */ ESP_RETURN_ON_ERROR(i2c_configure_io(scl_io_num, scl_pullup_en), RTCI2C_TAG, "RTC I2C SCL pin config failed"); /* Route SDA IO signal to the RTC subsystem */ rtc_io_dev->touch_pad[sda_io_num].mux_sel = 1; /* Route SCL IO signal to the RTC subsystem */ rtc_io_dev->touch_pad[scl_io_num].mux_sel = 1; /* Select RTC I2C function for SDA pin */ rtc_io_dev->touch_pad[sda_io_num].fun_sel = 3; /* Select RTC I2C function for SCL pin */ rtc_io_dev->touch_pad[scl_io_num].fun_sel = 3; /* Map the SDA and SCL signals to the RTC I2C controller */ if (sda_io_num == GPIO_NUM_1) { rtc_io_dev->sar_i2c_io.sda_sel = 0; } else { rtc_io_dev->sar_i2c_io.sda_sel = 1; } if (scl_io_num == GPIO_NUM_0) { rtc_io_dev->sar_i2c_io.scl_sel = 0; } else { rtc_io_dev->sar_i2c_io.scl_sel = 1; } return ESP_OK; } static esp_err_t i2c_set_timing(const ulp_riscv_i2c_cfg_t *cfg) { /* Convert all timing parameters from micro-seconds to period in RTC_FAST_CLK cycles. * RTC_FAST_CLK = 8.5 MHz for esp32s2 and 17.5 MHz for esp32s3. * The following calculations approximate the period for each parameter. */ uint32_t scl_low_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_low_period); uint32_t scl_high_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_high_period); uint32_t sda_duty_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.sda_duty_period); uint32_t scl_start_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_start_period); uint32_t scl_stop_period = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.scl_stop_period); uint32_t i2c_trans_timeout = MICROSEC_TO_RTC_FAST_CLK(cfg->i2c_timing_cfg.i2c_trans_timeout); uint32_t setup_time_start = (cfg->i2c_timing_cfg.scl_high_period + cfg->i2c_timing_cfg.sda_duty_period); uint32_t hold_time_start = (cfg->i2c_timing_cfg.scl_start_period - cfg->i2c_timing_cfg.sda_duty_period); uint32_t setup_time_data = (cfg->i2c_timing_cfg.scl_low_period - cfg->i2c_timing_cfg.sda_duty_period); /* Verify timing constraints */ ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_low_period > 1.3, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low period cannot be less than 1.3 micro seconds"); ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_high_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL high period cannot be less than 0.6 micro seconds"); ESP_RETURN_ON_FALSE((float)setup_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); ESP_RETURN_ON_FALSE((float)hold_time_start > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be less than 0.6 micro seconds"); ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.scl_stop_period > 0.6, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Setup time cannot be less than 0.6 micro seconds"); ESP_RETURN_ON_FALSE((float)cfg->i2c_timing_cfg.sda_duty_period < 3.45, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data hold time cannot be greater than 3.45 micro seconds"); ESP_RETURN_ON_FALSE((float)(setup_time_data * 1000) > 250, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "Data setup time cannot be less than 250 nano seconds"); /* Verify filtering constrains * * I2C may have glitches on the transition edge, so the edge will be filtered in the design, * which will also affect the value of the timing parameter register. * Therefore, the following filtering constraints must be followed: */ ESP_RETURN_ON_FALSE(scl_stop_period > scl_high_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL Stop period cannot be greater than SCL high period"); ESP_RETURN_ON_FALSE(sda_duty_period < scl_low_period, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period cannot be less than the SCL low period"); ESP_RETURN_ON_FALSE(scl_start_period > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL start period must be greater than 8 RTC_FAST_CLK cycles"); ESP_RETURN_ON_FALSE((scl_low_period + scl_high_period - sda_duty_period) > 8, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SCL low + SCL high - SDA duty must be greater than 8 RTC_FAST_CLK cycles"); /* Verify SDA duty num constraints */ ESP_RETURN_ON_FALSE(sda_duty_period > 14, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "SDA duty period must be greater than 14 RTC_FAST_CLK cycles"); /* Set the RTC I2C timing parameters */ #if CONFIG_IDF_TARGET_ESP32S2 i2c_dev->scl_low.val = scl_low_period; // SCL low period i2c_dev->scl_high.val = scl_high_period; // SCL high period i2c_dev->sda_duty.val = sda_duty_period; // SDA duty cycle i2c_dev->scl_start_period.val = scl_start_period; // Wait time after START condition i2c_dev->scl_stop_period.val = scl_stop_period; // Wait time before END condition i2c_dev->timeout.val = i2c_trans_timeout; // I2C transaction timeout #elif CONFIG_IDF_TARGET_ESP32S3 i2c_dev->i2c_scl_low.val = scl_low_period; // SCL low period i2c_dev->i2c_scl_high.val = scl_high_period; // SCL high period i2c_dev->i2c_sda_duty.val = sda_duty_period; // SDA duty cycle i2c_dev->i2c_scl_start_period.val = scl_start_period; // Wait time after START condition i2c_dev->i2c_scl_stop_period.val = scl_stop_period; // Wait time before END condition i2c_dev->i2c_to.val = i2c_trans_timeout; // I2C transaction timeout #endif // CONFIG_IDF_TARGET_ESP32S2 return ESP_OK; } /* * The RTC I2C controller follows the I2C command registers to perform read/write operations. * The cmd registers have the following format: * * 31 30:14 13:11 10 9 8 7:0 * |----------|----------|---------|---------|----------|------------|---------| * | CMD_DONE | Reserved | OPCODE |ACK Value|ACK Expect|ACK Check En|Byte Num | * |----------|----------|---------|---------|----------|------------|---------| */ static void ulp_riscv_i2c_format_cmd(uint32_t cmd_idx, uint8_t op_code, uint8_t ack_val, uint8_t ack_expected, uint8_t ack_check_en, uint8_t byte_num) { #if CONFIG_IDF_TARGET_ESP32S2 /* Reset cmd register */ i2c_dev->command[cmd_idx].val = 0; /* Write new command to cmd register */ i2c_dev->command[cmd_idx].done = 0; // CMD Done i2c_dev->command[cmd_idx].op_code = op_code; // Opcode i2c_dev->command[cmd_idx].ack_val = ack_val; // ACK bit sent by I2C controller during READ. // Ignored during RSTART, STOP, END and WRITE cmds. i2c_dev->command[cmd_idx].ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE. // Ignored during RSTART, STOP, END and READ cmds. i2c_dev->command[cmd_idx].ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the // slave device matches the ACK expected bit during WRITE. // Ignored during RSTART, STOP, END and READ cmds. i2c_dev->command[cmd_idx].byte_num = byte_num; // Byte Num #elif CONFIG_IDF_TARGET_ESP32S3 /* Reset cmd register */ i2c_dev->i2c_cmd[cmd_idx].val = 0; /* Write new command to cmd register */ i2c_dev->i2c_cmd[cmd_idx].i2c_command_done = 0; // CMD Done i2c_dev->i2c_cmd[cmd_idx].i2c_op_code = op_code; // Opcode i2c_dev->i2c_cmd[cmd_idx].i2c_ack_val = ack_val; // ACK bit sent by I2C controller during READ. // Ignored during RSTART, STOP, END and WRITE cmds. i2c_dev->i2c_cmd[cmd_idx].i2c_ack_exp = ack_expected; // ACK bit expected by I2C controller during WRITE. // Ignored during RSTART, STOP, END and READ cmds. i2c_dev->i2c_cmd[cmd_idx].i2c_ack_en = ack_check_en; // I2C controller verifies that the ACK bit sent by the // slave device matches the ACK expected bit during WRITE. // Ignored during RSTART, STOP, END and READ cmds. i2c_dev->i2c_cmd[cmd_idx].i2c_byte_num = byte_num; // Byte Num #endif // CONFIG_IDF_TARGET_ESP32S2 } void ulp_riscv_i2c_master_set_slave_addr(uint8_t slave_addr) { CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_ADDR_MASK); SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_addr, 0); } void ulp_riscv_i2c_master_set_slave_reg_addr(uint8_t slave_reg_addr) { CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_SLAVE_REG_ADDR_MASK); SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, slave_reg_addr, 11); } /* * I2C transactions when master reads one byte of data from the slave device: * * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------| * | Master | START | SAD + W | | SUB | | SR | SAD + R | | | NACK | STOP | * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------| * | Slave | | | ACK | | ACK | | | ACK | DATA | | | * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------| * * I2C transactions when master reads multiple bytes of data from the slave device: * * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------| * | Master | START | SAD + W | | SUB | | SR | SAD + R | | | ACK | | NACK | STOP | * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------| * | Slave | | | ACK | | ACK | | | ACK | DATA | | DATA | | | * |--------|--------|---------|--------|--------|--------|--------|---------|--------|--------|--------|--------|--------|--------| */ void ulp_riscv_i2c_master_read_from_device(uint8_t *data_rd, size_t size) { uint32_t i = 0; uint32_t cmd_idx = 0; if (size == 0) { // Quietly return return; } /* By default, RTC I2C controller is hard wired to use CMD2 register onwards for read operations */ cmd_idx = 2; /* Write slave addr */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2); /* Repeated START */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_RESTART, 0, 0, 0, 0); /* Write slave register addr */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 1); if (size > 1) { /* Read n - 1 bytes */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 0, 0, 1, size - 1); } /* Read last byte + NACK */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_READ, 1, 1, 1, 1); /* STOP */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0); /* Configure the RTC I2C controller in read mode */ SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 0, 27); /* Enable Rx data interrupt */ SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_RX_DATA_INT_ENA); /* Start RTC I2C transmission */ SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE); SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); for (i = 0; i < size; i++) { /* Poll for RTC I2C Rx Data interrupt bit to be set */ while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_RX_DATA_INT_ST)) { /* Minimal delay to avoid hogging the CPU */ vTaskDelay(1); } /* Read the data * * Unfortunately, the RTC I2C has no fifo buffer to help us with reading and storing * multiple bytes of data. Therefore, we need to read one byte at a time and clear the * Rx interrupt to get ready for the next byte. */ #if CONFIG_IDF_TARGET_ESP32S2 data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_RDATA); #elif CONFIG_IDF_TARGET_ESP32S3 data_rd[i] = REG_GET_FIELD(RTC_I2C_DATA_REG, RTC_I2C_I2C_RDATA); #endif // CONFIG_IDF_TARGET_ESP32S2 /* Clear the Rx data interrupt bit */ SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_RX_DATA_INT_CLR); } /* Clear the RTC I2C transmission bits */ CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE); CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); } /* * I2C transactions when master writes one byte of data to the slave device: * * |--------|--------|---------|--------|--------|--------|--------|--------|--------| * | Master | START | SAD + W | | SUB | | DATA | | STOP | * |--------|--------|---------|--------|--------|--------|--------|--------|--------| * | Slave | | | ACK | | ACK | | ACK | | * |--------|--------|---------|--------|--------|--------|--------|--------|--------| * * I2C transactions when master writes multiple bytes of data to the slave device: * * |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------| * | Master | START | SAD + W | | SUB | | DATA | | DATA | | STOP | * |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------| * | Slave | | | ACK | | ACK | | ACK | | ACK | | * |--------|--------|---------|--------|--------|--------|--------|--------|--------|--------|--------| */ void ulp_riscv_i2c_master_write_to_device(uint8_t *data_wr, size_t size) { uint32_t i = 0; uint32_t cmd_idx = 0; if (size == 0) { // Quietly return return; } /* By default, RTC I2C controller is hard wired to use CMD0 and CMD1 registers for write operations */ cmd_idx = 0; /* Write slave addr + reg addr + data */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_WRITE, 0, 0, 1, 2 + size); /* Stop */ ulp_riscv_i2c_format_cmd(cmd_idx++, ULP_I2C_CMD_STOP, 0, 0, 0, 0); /* Configure the RTC I2C controller in write mode */ SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0x1, 1, 27); /* Enable Tx data interrupt */ SET_PERI_REG_MASK(RTC_I2C_INT_ENA_REG, RTC_I2C_TX_DATA_INT_ENA); for (i = 0; i < size; i++) { /* Write the data to be transmitted */ CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, I2C_CTRL_MASTER_TX_DATA_MASK); SET_PERI_REG_BITS(SENS_SAR_I2C_CTRL_REG, 0xFF, data_wr[i], 19); if (i == 0) { /* Start RTC I2C transmission. (Needn't do it for every byte) */ SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE); SET_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); } /* Poll for RTC I2C Tx Data interrupt bit to be set */ while (!REG_GET_FIELD(RTC_I2C_INT_ST_REG, RTC_I2C_TX_DATA_INT_ST)) { /* Minimal delay to avoid hogging the CPU */ vTaskDelay(1); } /* Clear the Tx data interrupt bit */ SET_PERI_REG_MASK(RTC_I2C_INT_CLR_REG, RTC_I2C_TX_DATA_INT_CLR); } /* Clear the RTC I2C transmission bits */ CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START_FORCE); CLEAR_PERI_REG_MASK(SENS_SAR_I2C_CTRL_REG, SENS_SAR_I2C_START); } esp_err_t ulp_riscv_i2c_master_init(const ulp_riscv_i2c_cfg_t *cfg) { /* Clear any stale config registers */ WRITE_PERI_REG(RTC_I2C_CTRL_REG, 0); WRITE_PERI_REG(SENS_SAR_I2C_CTRL_REG, 0); /* Reset RTC I2C */ #if CONFIG_IDF_TARGET_ESP32S2 i2c_dev->ctrl.i2c_reset = 1; esp_rom_delay_us(20); i2c_dev->ctrl.i2c_reset = 0; #elif CONFIG_IDF_TARGET_ESP32S3 SET_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET); i2c_dev->i2c_ctrl.i2c_i2c_reset = 1; esp_rom_delay_us(20); i2c_dev->i2c_ctrl.i2c_i2c_reset = 0; CLEAR_PERI_REG_MASK(SENS_SAR_PERI_RESET_CONF_REG, SENS_RTC_I2C_RESET); #endif // CONFIG_IDF_TARGET_ESP32S2 /* Verify that the input cfg param is valid */ ESP_RETURN_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, RTCI2C_TAG, "RTC I2C configuration is NULL"); /* Configure RTC I2C GPIOs */ ESP_RETURN_ON_ERROR(i2c_set_pin(cfg), RTCI2C_TAG, "Failed to configure RTC I2C GPIOs"); #if CONFIG_IDF_TARGET_ESP32S2 /* Configure the RTC I2C controller in master mode */ i2c_dev->ctrl.ms_mode = 1; /* Enable RTC I2C Clock gate */ i2c_dev->ctrl.i2c_ctrl_clk_gate_en = 1; #elif CONFIG_IDF_TARGET_ESP32S3 /* For esp32s3, we need to enable the rtc_i2c clock gate before accessing rtc i2c registers */ SET_PERI_REG_MASK(SENS_SAR_PERI_CLK_GATE_CONF_REG, SENS_RTC_I2C_CLK_EN); /* Configure the RTC I2C controller in master mode */ i2c_dev->i2c_ctrl.i2c_ms_mode = 1; /* Enable RTC I2C Clock gate */ i2c_dev->i2c_ctrl.i2c_i2c_ctrl_clk_gate_en = 1; #endif // CONFIG_IDF_TARGET_ESP32S2 /* Configure RTC I2C timing paramters */ ESP_RETURN_ON_ERROR(i2c_set_timing(cfg), RTCI2C_TAG, "Failed to configure RTC I2C timing"); return ESP_OK; }