diff --git a/sw/applications/I2C_test/main.c b/sw/applications/I2C_test/main.c new file mode 100644 index 000000000..c4a2ea026 --- /dev/null +++ b/sw/applications/I2C_test/main.c @@ -0,0 +1,471 @@ +#include +#include +#include "csr.h" +#include "hart.h" +#include "handler.h" +#include "core_v_mini_mcu.h" +#include "rv_plic.h" +#include "rv_plic_regs.h" +#include "gpio.h" +#include "pad_control.h" +#include "pad_control_regs.h" // Generated. +#include "x-heep.h" +#include "bitfield.h" + +#include "i2c.h" +#include "i2c_regs.h" + +#include "i2c_structs.h" + +// #include + +void test_config(); +void test_set_watermark(); +void test_reset(); +void test_host_enable(); +void test_override(); +void test_write_byte(); +// void test_read_byte(); +void test_irq_enable(); +void test_irq_snapshot_restore(); + +void print_FSM_status(); + +void test_read(); +void test_transmit(); + +void handler_irq_i2c_fmtOverflow(void) +{ + printf("Ecco!\n"); +} + +uint8_t plic_intr_flag = 0; + +int main() +{ + // printf("\n------------------------------------\n"); + // printf("\nStarting i2c test!\n"); + + test_config(); + test_reset(); + test_set_watermark(); + test_host_enable(); + // test_override(); + // test_write_byte(); + // test_read_byte(); + // test_irq_enable(); + // test_irq_snapshot_restore(); + + // print_FSM_status(); + + // test_read(); + test_transmit(); + + printf("Done!\n"); + + return 1; +} + + +void test_config() +{ + printf("\n\n>> Config...\n"); + + i2c_config_t config; + + i2c_timing_config_t time_config = { + .lowest_target_device_speed = 100000, + .clock_period_nanos = 1000000000, + .sda_rise_nanos = 500, + .sda_fall_nanos = 500, + .scl_period_nanos = 1500 + }; + + + i2c_compute_timing(time_config, &config); + + + /* This is the hardcoded version of doing it + it allows to directly see the numbers in the + register when printing */ + + // i2c_config_t config = { + // // TIMING 0 + // .scl_time_high_cycles = 1, + // .scl_time_low_cycles = 1, + + // // TIMING 1 + // .rise_cycles = 1, + // .fall_cycles = 1, + + // // TIMING 2 + // .start_signal_setup_cycles = 1, + // .start_signal_hold_cycles = 1, + + // // TIMING 3 + // .data_signal_setup_cycles = 1, + // .data_signal_hold_cycles = 1, + + // // TIMING 4 + // .stop_signal_setup_cycles = 1, + // .stop_signal_hold_cycles = 1 + // }; + + i2c_configure(config); + + // printf("TIMING0:\t%d\n", i2c_peri->TIMING0); + // printf("TIMING1:\t%d\n", i2c_peri->TIMING1); + // printf("TIMING2:\t%d\n", i2c_peri->TIMING2); + // printf("TIMING3:\t%d\n", i2c_peri->TIMING3); + // printf("TIMING4:\t%d\n", i2c_peri->TIMING4); +} + +void test_set_watermark() +{ + printf(">> Set watermark...\n"); + + i2c_level_t rx_level = kI2cLevel4Byte; + + i2c_level_t fmt_level = kI2cLevel16Byte; + + i2c_set_watermarks(rx_level, fmt_level); + + // printf("FIFO_CTRL:\t%d\n", i2c_peri->FIFO_CTRL); + // printf("\tRX_LVL:\t\t%d\n", bitfield_read(i2c_peri->FIFO_CTRL, 0x3, I2C_FIFO_CTRL_RXILVL_OFFSET)); + // printf("\tFMT_LVL:\t%d\n", bitfield_read(i2c_peri->FIFO_CTRL, 0x3, I2C_FIFO_CTRL_FMTILVL_OFFSET)); + +} + +void test_reset() +{ + printf(">> Reset...\n"); + + i2c_reset_rx_fifo(); + i2c_reset_fmt_fifo(); + i2c_reset_acq_fifo(); + i2c_reset_tx_fifo(); + + int32_t rx_rst = bitfield_read(i2c_peri->FIFO_CTRL, BIT_MASK_1, I2C_FIFO_CTRL_RXRST_BIT); + int32_t fmt_rst = bitfield_read(i2c_peri->FIFO_CTRL, BIT_MASK_1, I2C_FIFO_CTRL_FMTRST_BIT); + int32_t rx_lvl = bitfield_read(i2c_peri->FIFO_CTRL, 0x3, I2C_FIFO_CTRL_RXILVL_OFFSET); + int32_t fmt_lvl = bitfield_read(i2c_peri->FIFO_CTRL, 0x3, I2C_FIFO_CTRL_FMTILVL_OFFSET); + + // printf("\tFIFO_CTRL:\t%d\n", i2c_peri->FIFO_CTRL); + // printf("\tRx rst:\t\t%d\n", rx_rst); + // printf("\tFmt rst:\t%d\n", fmt_rst); + // printf("\tRX_LVL:\t\t%d\n", rx_lvl); + // printf("\tFMT_LVL:\t%d\n", fmt_lvl); +} + + +void test_host_enable(){ + + printf(">> Host enable\n"); + + i2c_result_t res = i2c_host_set_enabled(kI2cToggleEnabled); + if(res != kI2cOk){ + printf("ERROR (host_set_enabled)\n"); + } + +} + + + +void test_override() +{ + printf(">> Test override...\n"); + + /* This test is to check that I cannot + modify the pins if first I don't enable + the override mode */ + + // NOTE: this test is not properly asserted!!! + // The values in the registers are always written, but they are used by + // the HW only if the override mode is enabled (check it with gtkwave, it works!!) + i2c_override_set_enabled(kI2cToggleDisabled); + printf("\ten?:\t%d\n", bitfield_read(i2c_peri->OVRD, BIT_MASK_1, I2C_OVRD_TXOVRDEN_BIT)); + i2c_override_drive_pins(0, 1); + printf("\tOVRD:\t%d\n", i2c_peri->OVRD); + + /* This is the one in which I also enable the override */ + i2c_override_set_enabled(kI2cToggleEnabled); + printf("\ten?:\t%d\n", bitfield_read(i2c_peri->OVRD, BIT_MASK_1, I2C_OVRD_TXOVRDEN_BIT)); + i2c_override_drive_pins(1, 1); + printf("\tOVRD:\t%d\n", i2c_peri->OVRD); + +} + +void test_write_byte() +{ + printf(">> Write byte...\n"); + + i2c_fmt_t code = kI2cFmtStart; + uint8_t fmt_lvl = 0; + uint8_t rx_lvl = 0; + + i2c_get_fifo_levels(&fmt_lvl, &rx_lvl); + + printf("\tfmt_lvl:\t%d\n", fmt_lvl); + printf("\trx_lvl:\t\t%d\n", rx_lvl); + + i2c_result_t res = i2c_write_byte(42, code, false); + + if(res != kI2cOk){ + printf("\t--|| ERROR! ||--\n"); + } else { + printf("\tByte written!\n"); + } + + i2c_get_fifo_levels(&fmt_lvl, &rx_lvl); + + printf("\tfmt_lvl:\t%d\n", fmt_lvl); + printf("\trx_lvl:\t\t%d\n", rx_lvl); + + // Try resetting the FMT fifo + res = i2c_reset_fmt_fifo(); + if(res != kI2cOk){ + printf("\t--|| ERROR! ||--\n"); + } else { + printf("\tFMT fifo reset!\n"); + } + i2c_get_fifo_levels(&fmt_lvl, &rx_lvl); + printf("\tfmt_lvl:\t%d\n", fmt_lvl); + printf("\trx_lvl:\t\t%d\n", rx_lvl); + +} + + +void test_read_byte() +{ + printf(">> Read byte...\n"); + + uint8_t byte; + + i2c_read_byte(&byte); + + printf("Read byte:\t%d\n", byte); +} + + +void test_irq_enable() +{ + printf(">> Enable IRQ...\n"); + + // Interrupt type we want to test + i2c_irq_t irq = kI2cIrqFmtFifoOverflow; + + // SETUP PLIC + plic_result_t r = plic_Init(); + if(r != kPlicOk) { + printf("PLIC init failed!\n"); + return -1; + } + + r = plic_irq_set_priority(I2C_IRQ_ID_START + irq, 1); + if(r != kPlicOk){ + printf("Prio setting failed!\n"); + } + + r = plic_irq_set_enabled(I2C_IRQ_ID_START + irq, kPlicToggleEnabled); + if (r != kPlicOk) { + printf("Failed\n"); + return -1; + } + + // Enable interrupt on processor side + // Enable global interrupt for machine-level interrupts + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + // Set mie.MEIE bit to one to enable machine-level external interrupts + const uint32_t mask = 1 << 11; + CSR_SET_BITS(CSR_REG_MIE, mask); + // END SETUP PLIC + + i2c_toggle_t state = -1; + i2c_result_t res = 0; + + // Should be disabled + i2c_irq_get_enabled(irq, &state); + printf("\tIRQ [%d] en?\t%d\n", irq, state); + + // Enable the irq + res = i2c_irq_set_enabled(irq, kI2cToggleEnabled); + if (res != kI2cOk) + { + printf("\t--|| Enabling ERROR! ||--\n"); + } else { + printf("\tIRQ [%d] enabled!\n", irq); + } + + i2c_irq_get_enabled(irq, &state); + printf("\tIRQ [%d] en?\t%d\n", irq, state); + + // If a irq is pending + bool is_pending = false; + + /* + NB To make this work you must disable the interrupt. + This will cause the interrupt not to be serviced but the app + will not get stuck. + Easy way: in the plic disable the intr line just to test. + Add something like: + plic_irq_set_enabled(I2C_ID_START + irq, kPlicToggleDisabled); + + If you want to test the interrupt event keep the interrupt enabled and + overwrite the weak i2c-handler. The app will get stuck because + there is no way of deasserting the interrupt source (it's simulated + by writing into a register), so the irq_src_i of the PLIC will + stay high and the IRQ will be serviced infinitely. + */ + plic_irq_set_enabled(I2C_IRQ_ID_START + irq, kPlicToggleDisabled); + + plic_intr_flag = 0; + while (plic_intr_flag == 0){ + res = i2c_irq_force(irq); + + // wait_for_interrupt(); + plic_intr_flag = 1; + } + + res = i2c_irq_force(irq); + if (res == kI2cOk) + { + printf("\tForced!\n"); + } + else { + printf("\tForcing error!\n"); + } + + printf("\tTest:\t%d\n", i2c_peri->INTR_TEST); + + res = i2c_irq_is_pending(irq, &is_pending); + if(res != kI2cOk){ + printf("\t--|| is_pending ERROR! ||--\n"); + } + else { + printf("\tRead is_pending!\n"); + } + + printf("\tIs pending:\t%d\n", is_pending); + + res = i2c_irq_acknowledge(irq); + if(res != kI2cOk){ + printf("\t--|| ACK ERROR! ||--\n"); + } + else { + printf("\tIRQ acknowledged!\n"); + } + + res = i2c_irq_is_pending(irq, &is_pending); + if(res != kI2cOk){ + printf("\t--|| is_pending ERROR! ||--\n"); + } + else { + printf("\tRead is_pending!\n"); + } + + printf("\tIs pending:\t%d\n", is_pending); + +} + + +void test_irq_snapshot_restore() +{ + /* Here I test the restoring of the INTR enable states + after resetting them all! */ + + printf(">> Snapshot restore...\n"); + + i2c_result_t res = 0; + + i2c_irq_set_enabled(kI2cIrqRxWatermarkOverflow, kI2cToggleEnabled); + i2c_irq_set_enabled(kI2cIrqRxFifoOverflow, kI2cToggleEnabled); + i2c_irq_set_enabled(kI2cIrqSclInterference, kI2cToggleEnabled); + + printf("\tEnable reg:\t%d\n", i2c_peri->INTR_ENABLE); + + i2c_irq_snapshot_t snapshot = 0; + + res = i2c_irq_disable_all(&snapshot); + if(res != kI2cOk){ + printf("\t--|| disable_all ERROR! ||--\n"); + } else { + printf("\tDisabled all the IRQ\n"); + } + + printf("\tEnable reg:\t%d\n", i2c_peri->INTR_ENABLE); + printf("\tSnapshot:\t%d\n", snapshot); + + res = i2c_irq_restore_all(&snapshot); + if(res != kI2cOk){ + printf("\t--|| restore_all ERROR! ||--\n"); + } else { + printf("\tRestored the IRQ\n"); + } + + printf("\tEnable reg:\t%d\n", i2c_peri->INTR_ENABLE); +} + + +// void print_FSM_status() +// { +// i2c_fsm_t fsm = i2c_state; + +// printf("----------- FSM -----------\n"); +// printf("\tFMT_LVL:\t%d\n", i2c_state.fmt_fifo_level); +// printf("\tTX_LVL:\t%f\n", i2c_state.tx_fifo_level); +// printf("\tBYTES_RD:\t%d\n", i2c_state.bytes_read); +// printf("\tBYTES_WR:\t%d\n", i2c_state.bytes_written); +// printf("---------------------------\n"); +// } + + +void test_read(){ + + printf(">> Read...\n"); + + int8_t addr = 0x34; // hardcoded address of the audio sensor + + int32_t data = 42; + + i2c_read(&data, addr); + +} + + +void test_transmit(){ + + printf(">> Transmit...\n"); + + + uint32_t data_len = 5; + int8_t *data = (int8_t*)malloc(data_len * sizeof(int8_t)); + + for(uint16_t i=0; i +#include +#include "csr.h" +#include "hart.h" +#include "handler.h" +#include "core_v_mini_mcu.h" +#include "rv_plic.h" +#include "rv_plic_regs.h" +#include "gpio.h" +#include "pad_control.h" +#include "pad_control_regs.h" // Generated. +#include "x-heep.h" + +#include "rv_plic_structs.h" + + +void test_init(); +void test_setPrio(); +void test_setEnable(); +void test_getEnable(); +void test_triggerType(); +void test_complete(); + +// typedef struct app_rv_plic { +// irq_sources_t irq_src; +// } app_rv_plic_t; + +static app_rv_plic_t app_plic; + +// irq_sources_t irq_src; + + +void delay(){ + for(int i=0; i<10000000; i++){ + asm("nop"); + } +} + +// int8_t external_intr_flag = 0; + +/* +Notes: + - Ports 30 and 31 are connected in questasim testbench, but in the FPGA version they are connected to the EPFL programmer and should not be used + - Connect a cable between the two pins for the applicatio to work +*/ + +#ifdef TARGET_PYNQ_Z2 + #define GPIO_TB_OUT 8 + #define GPIO_TB_IN 9 + #define GPIO_INTR GPIO_INTR_9 + #pragma message ( "Connect a cable between GPIOs IN and OUT" ) + +#else + + #define GPIO_TB_OUT 30 + #define GPIO_TB_IN 31 + #define GPIO_INTR GPIO_INTR_31 + +#endif + + +void handler_irq_gpio(uint32_t id) { + printf("Interrupt serviced!"); + printf("Interrupt ID:\t%d\n", id); +} + +int main(){ + + printf("\nStarting...\n"); + + // TESTING INIT + test_init(); + // + + // test_bitfield(); + + pad_control_t pad_control; + pad_control.base_addr = mmio_region_from_addr((uintptr_t)PAD_CONTROL_START_ADDRESS); + + // In case GPIOs 30 and 31 are used: +#if GPIO_TB_OUT == 31 || GPIO_TB_IN == 31 + pad_control_set_mux(&pad_control, (ptrdiff_t)(PAD_CONTROL_PAD_MUX_I2C_SCL_REG_OFFSET), 1); +#endif + +#if GPIO_TB_OUT == 30|| GPIO_TB_IN == 30 + pad_control_set_mux(&pad_control, (ptrdiff_t)(PAD_CONTROL_PAD_MUX_I2C_SDA_REG_OFFSET), 1); +#endif + gpio_params_t gpio_params; + gpio_t gpio; + gpio_result_t gpio_res; + gpio_params.base_addr = mmio_region_from_addr((uintptr_t)GPIO_START_ADDRESS); + gpio_res = gpio_init(gpio_params, &gpio); + if (gpio_res != kGpioOk) { + printf("Failed\n;"); + return -1; + } + + irq_src = IRQ_GPIO_SRC; + + + test_setPrio(); + + test_setEnable(); + + // test_getEnable(); + + // test_triggerType(); + + // plic_target_set_threshold(4); + + + // Enable interrupt on processor side + // Enable global interrupt for machine-level interrupts + CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); + // Set mie.MEIE bit to one to enable machine-level external interrupts + const uint32_t mask = 1 << 11; + CSR_SET_BITS(CSR_REG_MIE, mask); + + gpio_res = gpio_output_set_enabled(&gpio, GPIO_TB_OUT, true); + if (gpio_res != kGpioOk) { + printf("Failed\n;"); + return -1; + } + gpio_write(&gpio, GPIO_TB_OUT, false); + + gpio_res = gpio_input_enabled(&gpio, GPIO_TB_IN, true); + if (gpio_res != kGpioOk) { + printf("Failed\n;"); + return -1; + } + + + gpio_res = gpio_irq_set_trigger(&gpio, GPIO_TB_IN, true, kGpioIrqTriggerEdgeRising); + if (gpio_res != kGpioOk) { + printf("Failed\n;"); + return -1; + } + + + plic_intr_flag = 0; + // printf("Write 1 to GPIO 30 and wait for interrupt...\n"); + while(plic_intr_flag==0) { + // gpio_write(&gpio, GPIO_TB_OUT, true); + wait_for_interrupt(); + } + printf("plic_intr_flag = %d\n", plic_intr_flag); + + printf("Done...\n"); + + return 0; +} + + +void test_init(){ + // printf("ext:\t%d\n", external_intr_flag); + plic_result_t r = plic_Init(); + // printf("ext:\t%d\n", external_intr_flag); + + if(r != kPlicOk) { + printf("PLIC init failed!\n"); + return -1; + } + + // To see all the priority registers + // for(int i=0; i<64; i++){ + // printf("PRIO%d:\t%d\n", i, *((&rv_plic_peri->PRIO0) + i)); + // } +} + + +void test_setPrio(){ + + plic_result_t plic_res = plic_irq_set_priority(GPIO_INTR, 1); + + if(plic_res != kPlicOk){ + printf("Prio setting failed!\n"); + } + + plic_res = plic_irq_set_priority(12, 5); + + if(plic_res != kPlicOk){ + printf("Prio setting failed!\n"); + } + + + // To see all the priority registers + // for(int i=0; i<64; i++){ + // printf("PRIO%d:\t%d\n", i, *((&rv_plic_peri->PRIO0) + i)); + // } +} + + +void test_setEnable(){ + plic_result_t plic_res = plic_irq_set_enabled(GPIO_INTR, kPlicToggleEnabled); + if (plic_res != kPlicOk) { + printf("Failed\n"); + return -1; + } + plic_toggle_t state; + plic_irq_get_enabled(GPIO_INTR, &state); +} + + +void test_getEnable(){ + + plic_result_t res; + plic_toggle_t state; + + printf("--------------------------\n"); + plic_irq_get_enabled(GPIO_INTR, &state); + printf("Enabled[%d]:\t%d\n\n", GPIO_INTR, state); + plic_irq_get_enabled(45, &state); + printf("Enabled[%d]:\t%d\n\n", 45, state); + plic_irq_get_enabled(22, &state); + printf("Enabled[%d]:\t%d\n\n", 22, state); + printf("--------------------------\n"); + +} + + +void test_triggerType(){ + plic_result_t res; + + plic_irq_set_trigger(GPIO_INTR, kPlicIrqTriggerEdge); +} \ No newline at end of file diff --git a/sw/applications/example/main.c b/sw/applications/example/main.c new file mode 100644 index 000000000..0bd8a4402 --- /dev/null +++ b/sw/applications/example/main.c @@ -0,0 +1,47 @@ +#include +#include +int main(int argc, char *argv[]) +{ + /* write something to stdout */ + printf("hello world!\n"); + float a = 1.3; + float b = 3.7; + float c = 0.0; + c = a * b; + printf("Result: %f\n", c); + return EXIT_SUCCESS; +} + + + + + + + + + + + + + + + + + + + +// #include + +// int main(){ + +// printf("Hello World!\n"); + +// float a = 10.0; +// float b = 5.0; + +// printf("Res:\t%f\n", (a * b) + a - 2.0); + +// printf("Ciao\n"); +// // make app PROJECT=epilepsy_C COMPILER=gcc ARCH=rv32imfc +// return 0; +// } \ No newline at end of file diff --git a/sw/device/lib/drivers/i2c/i2c.c b/sw/device/lib/drivers/i2c/i2c.c index ef41d320a..37aa66104 100644 --- a/sw/device/lib/drivers/i2c/i2c.c +++ b/sw/device/lib/drivers/i2c/i2c.c @@ -1,102 +1,218 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 +/* + ******************* +******************************* C SOURCE FILE ******************************* +** ******************* ** +** ** +** project : x-heep ** +** filename : i2c.c ** +** version : 1.0 ** +** date : 03/06/2023 ** +** ** +***************************************************************************** +** ** +** ** +***************************************************************************** + +*/ + +/***************************************************************************/ +/***************************************************************************/ + +/** +* @file i2c.c +* @date 17/04/2023 +* @brief This is the main file for the HAL of the I2C peripheral +* +* In this files there are definitions of low level HAL functions to interact +* with the registers of the I2C peripheral. +* The functionalities implemented allow to configure the peripheral with +* parameters for the FMT and RX fifos, enable or disable interrupts and write +* and read bytes. +*/ + + +/****************************************************************************/ +/** **/ +/* MODULES USED */ +/** **/ +/****************************************************************************/ #include "i2c.h" +#include "i2c_regs.h" +#include "i2c_structs.h" + #include "bitfield.h" -#include "i2c_regs.h" // Generated + +/****************************************************************************/ +/** **/ +/* DEFINITIONS AND MACROS */ +/** **/ +/****************************************************************************/ + +/** + * Array of handler functions. + * Each element contains the address of the function and can be directly called. + * The order of the handlers in the array is the same as the one of the IDs + * of the possible interrupt sources, so to make it easier to call + * the proper one. +*/ +handler_func_i2c_t i2c_handlers[] = {&handler_irq_i2c_fmtWatermarkUnderflow, + &handler_irq_i2c_rxWatermarkOverflow, + &handler_irq_i2c_fmtOverflow, + &handler_irq_i2c_rxOverflow, + &handler_irq_i2c_nak, + &handler_irq_i2c_sclInterference, + &handler_irq_i2c_sdaInteference, + &handler_irq_i2c_clockStretchTimeout, + &handler_irq_i2c_sdaUnstable, + &handler_irq_i2c_transComplete, + &handler_irq_i2c_txEmpty, + &handler_irq_i2c_txNonEmpty, + &handler_irq_i2c_txOverflow, + &handler_irq_i2c_acqOverflow, + &handler_irq_i2c_ackStop, + &handler_irq_i2c_hostTimeout + }; + +/****************************************************************************/ +/** **/ +/* TYPEDEFS AND STRUCTURES */ +/** **/ +/****************************************************************************/ + +/****************************************************************************/ +/** **/ +/* PROTOTYPES OF LOCAL FUNCTIONS */ +/** **/ +/****************************************************************************/ /** * Performs a 32-bit integer unsigned division, rounding up. The bottom * 16 bits of the result are then returned. - * - * As usual, a divisor of 0 is still Undefined Behavior. */ -static uint16_t round_up_divide(uint32_t a, uint32_t b) { - if (a == 0) { - return 0; - } +static uint16_t round_up_divide(uint32_t a, uint32_t b); - return ((a - 1) / b) + 1; -} /** * Computes default timing parameters for a particular I2C speed, given the * clock period, in nanoseconds. - * - * Returns an unspecified value for an invalid speed. + * + * \returns an unspecified value for an invalid speed. */ static i2c_config_t default_timing_for_speed(i2c_speed_t speed, - uint32_t clock_period_nanos) { - // NOTE: All constants below are lifted from Table 10 of the I2C spec. - // All literal values are given in nanoseconds; we don't bother putting - // these into constants since they are not used anywhere else. - switch (speed) { - case kDifI2cSpeedStandard: - return (i2c_config_t){ - .scl_time_high_cycles = round_up_divide(4000, clock_period_nanos), - .scl_time_low_cycles = round_up_divide(4700, clock_period_nanos), - .start_signal_setup_cycles = - round_up_divide(4700, clock_period_nanos), - .start_signal_hold_cycles = round_up_divide(4000, clock_period_nanos), - .data_signal_setup_cycles = round_up_divide(250, clock_period_nanos), - .data_signal_hold_cycles = 0, - .stop_signal_setup_cycles = round_up_divide(4000, clock_period_nanos), - .stop_signal_hold_cycles = round_up_divide(4700, clock_period_nanos), - }; - case kDifI2cSpeedFast: - return (i2c_config_t){ - .scl_time_high_cycles = round_up_divide(600, clock_period_nanos), - .scl_time_low_cycles = round_up_divide(1300, clock_period_nanos), - .start_signal_setup_cycles = round_up_divide(600, clock_period_nanos), - .start_signal_hold_cycles = round_up_divide(600, clock_period_nanos), - .data_signal_setup_cycles = round_up_divide(100, clock_period_nanos), - .data_signal_hold_cycles = 0, - .stop_signal_setup_cycles = round_up_divide(600, clock_period_nanos), - .stop_signal_hold_cycles = round_up_divide(1300, clock_period_nanos), - }; - case kDifI2cSpeedFastPlus: - return (i2c_config_t){ - .scl_time_high_cycles = round_up_divide(260, clock_period_nanos), - .scl_time_low_cycles = round_up_divide(500, clock_period_nanos), - .start_signal_setup_cycles = round_up_divide(260, clock_period_nanos), - .start_signal_hold_cycles = round_up_divide(260, clock_period_nanos), - .data_signal_setup_cycles = round_up_divide(50, clock_period_nanos), - .data_signal_hold_cycles = 0, - .stop_signal_setup_cycles = round_up_divide(260, clock_period_nanos), - .stop_signal_hold_cycles = round_up_divide(500, clock_period_nanos), - }; - default: - return (i2c_config_t){0}; - } + uint32_t clock_period_nanos); + +/****************************************************************************/ +/** **/ +/* EXPORTED VARIABLES */ +/** **/ +/****************************************************************************/ + +/****************************************************************************/ +/** **/ +/* GLOBAL VARIABLES */ +/** **/ +/****************************************************************************/ + +/****************************************************************************/ +/** **/ +/* EXPORTED FUNCTIONS */ +/** **/ +/****************************************************************************/ + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_fmtWatermarkUnderflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_rxWatermarkOverflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_fmtOverflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_rxOverflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_nak(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_sclInterference(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_sdaInteference(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_clockStretchTimeout(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_sdaUnstable(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_transComplete(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_txEmpty(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_txNonEmpty(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_txOverflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_acqOverflow(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_ackStop(void) { + +} + +__attribute__((weak, optimize("O0"))) void handler_irq_i2c_hostTimeout(void) { + +} + + +void handler_i2c(uint32_t id) +{ + // Call the proper handler basing on the ID. + i2c_handlers[id](); } -static const uint32_t kNanosPerKBaud = 1000000; // One million. i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, i2c_config_t *config) { if (config == NULL) { - return kDifI2cBadArg; + return kI2cBadArg; } uint32_t lowest_target_device_speed_khz; switch (timing_config.lowest_target_device_speed) { - case kDifI2cSpeedStandard: - lowest_target_device_speed_khz = 100; + case kI2cSpeedStandard: + lowest_target_device_speed_khz = STANDARD_MODE_SPEED; break; - case kDifI2cSpeedFast: - lowest_target_device_speed_khz = 400; + case kI2cSpeedFast: + lowest_target_device_speed_khz = FAST_MODE_SPEED; break; - case kDifI2cSpeedFastPlus: - lowest_target_device_speed_khz = 1000; + case kI2cSpeedFastPlus: + lowest_target_device_speed_khz = FAST_MODE_PLUS_SPEED; break; default: - return kDifI2cBadArg; + return kI2cBadArg; } - // This code follows the algorithm given in - // https://docs.opentitan.org/hw/ip/i2c/doc/index.html#initialization - *config = default_timing_for_speed(timing_config.lowest_target_device_speed, timing_config.clock_period_nanos); @@ -107,7 +223,7 @@ i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, uint32_t scl_period_nanos = timing_config.scl_period_nanos; uint32_t slowest_scl_period_nanos = - kNanosPerKBaud / lowest_target_device_speed_khz; + NANO_SEC_PER_KBAUD / lowest_target_device_speed_khz; if (scl_period_nanos < slowest_scl_period_nanos) { scl_period_nanos = slowest_scl_period_nanos; } @@ -122,476 +238,388 @@ i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, config->scl_time_high_cycles = lengthened_high_cycles; } - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_init(i2c_params_t params, i2c_t *i2c) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - - i2c->params = params; +i2c_result_t i2c_configure(i2c_config_t config) +{ + // Configure TIMING0 register + i2c_peri->TIMING0 = bitfield_write(i2c_peri->TIMING0, + I2C_TIMING0_THIGH_MASK, + I2C_TIMING0_THIGH_OFFSET, + config.scl_time_high_cycles); + i2c_peri->TIMING0 = bitfield_write(i2c_peri->TIMING0, + I2C_TIMING0_TLOW_MASK, + I2C_TIMING0_TLOW_OFFSET, + config.scl_time_low_cycles); + + // Configure TIMING1 register + i2c_peri->TIMING1 = bitfield_write(i2c_peri->TIMING1, + I2C_TIMING1_T_R_MASK, + I2C_TIMING1_T_R_OFFSET, + config.rise_cycles); + i2c_peri->TIMING1 = bitfield_write(i2c_peri->TIMING1, + I2C_TIMING1_T_F_MASK, + I2C_TIMING1_T_F_OFFSET, + config.fall_cycles); + + // Configure TIMING2 register + i2c_peri->TIMING2 = bitfield_write(i2c_peri->TIMING2, + I2C_TIMING2_TSU_STA_MASK, + I2C_TIMING2_TSU_STA_OFFSET, + config.start_signal_setup_cycles); + i2c_peri->TIMING2 = bitfield_write(i2c_peri->TIMING2, + I2C_TIMING2_THD_STA_MASK, + I2C_TIMING2_THD_STA_OFFSET, + config.start_signal_hold_cycles); + + // Configure TIMING3 register + i2c_peri->TIMING3 = bitfield_write(i2c_peri->TIMING3, + I2C_TIMING3_TSU_DAT_MASK, + I2C_TIMING3_TSU_DAT_OFFSET, + config.data_signal_setup_cycles); + i2c_peri->TIMING3 = bitfield_write(i2c_peri->TIMING3, + I2C_TIMING3_THD_DAT_MASK, + I2C_TIMING3_THD_DAT_OFFSET, + config.data_signal_hold_cycles); + + // Configure TIMING4 register + i2c_peri->TIMING4 = bitfield_write(i2c_peri->TIMING4, + I2C_TIMING4_TSU_STO_MASK, + I2C_TIMING4_TSU_STO_OFFSET, + config.stop_signal_setup_cycles); + i2c_peri->TIMING4 = bitfield_write(i2c_peri->TIMING4, + I2C_TIMING4_T_BUF_MASK, + I2C_TIMING4_T_BUF_OFFSET, + config.stop_signal_hold_cycles); - return kDifI2cOk; } -i2c_result_t i2c_configure(const i2c_t *i2c, - i2c_config_t config) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - - uint32_t timing0 = 0; - timing0 = bitfield_field32_write(timing0, I2C_TIMING0_THIGH_FIELD, - config.scl_time_high_cycles); - timing0 = bitfield_field32_write(timing0, I2C_TIMING0_TLOW_FIELD, - config.scl_time_low_cycles); - mmio_region_write32(i2c->params.base_addr, I2C_TIMING0_REG_OFFSET, timing0); - - uint32_t timing1 = 0; - timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_R_FIELD, - config.rise_cycles); - timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_F_FIELD, - config.fall_cycles); - mmio_region_write32(i2c->params.base_addr, I2C_TIMING1_REG_OFFSET, timing1); - - uint32_t timing2 = 0; - timing2 = bitfield_field32_write(timing2, I2C_TIMING2_TSU_STA_FIELD, - config.start_signal_setup_cycles); - timing2 = bitfield_field32_write(timing2, I2C_TIMING2_THD_STA_FIELD, - config.start_signal_hold_cycles); - mmio_region_write32(i2c->params.base_addr, I2C_TIMING2_REG_OFFSET, timing2); - - uint32_t timing3 = 0; - timing3 = bitfield_field32_write(timing3, I2C_TIMING3_TSU_DAT_FIELD, - config.data_signal_setup_cycles); - timing3 = bitfield_field32_write(timing3, I2C_TIMING3_THD_DAT_FIELD, - config.data_signal_hold_cycles); - mmio_region_write32(i2c->params.base_addr, I2C_TIMING3_REG_OFFSET, timing3); - - uint32_t timing4 = 0; - timing4 = bitfield_field32_write(timing4, I2C_TIMING4_TSU_STO_FIELD, - config.stop_signal_setup_cycles); - timing4 = bitfield_field32_write(timing4, I2C_TIMING4_T_BUF_FIELD, - config.stop_signal_hold_cycles); - mmio_region_write32(i2c->params.base_addr, I2C_TIMING4_REG_OFFSET, timing4); - - return kDifI2cOk; -} - -i2c_result_t i2c_reset_rx_fifo(const i2c_t *i2c) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - uint32_t reg = - mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); - reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_RXRST_BIT, true); - mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg); - return kDifI2cOk; -} +i2c_result_t i2c_irq_is_pending(i2c_irq_t irq, bool *is_pending) +{ + if (is_pending == NULL) { + return kI2cBadArg; + } -i2c_result_t i2c_reset_fmt_fifo(const i2c_t *i2c) { - if (i2c == NULL) { - return kDifI2cBadArg; + // Sanity check on the irq + if(irq < 0 || irq > kI2cNIrqTypes) + { + return kI2cBadArg; } - uint32_t reg = - mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); - reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_FMTRST_BIT, true); - mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg); + *is_pending = bitfield_read(i2c_peri->INTR_STATE, BIT_MASK_1, irq); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_set_watermarks(const i2c_t *i2c, - i2c_level_t rx_level, - i2c_level_t fmt_level) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - ptrdiff_t rx_level_value; - switch (rx_level) { - case kDifI2cLevel1Byte: - rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL1; - break; - case kDifI2cLevel4Byte: - rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL4; - break; - case kDifI2cLevel8Byte: - rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL8; - break; - case kDifI2cLevel16Byte: - rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL16; - break; - case kDifI2cLevel30Byte: - rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL30; - break; - default: - return kDifI2cBadArg; - } +i2c_result_t i2c_irq_acknowledge(i2c_irq_t irq) { - ptrdiff_t fmt_level_value; - switch (fmt_level) { - case kDifI2cLevel1Byte: - fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL1; - break; - case kDifI2cLevel4Byte: - fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL4; - break; - case kDifI2cLevel8Byte: - fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL8; - break; - case kDifI2cLevel16Byte: - fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL16; - break; - default: - return kDifI2cBadArg; + // Sanity check on the irq + if(irq < 0 || irq > kI2cNIrqTypes) + { + return kI2cBadArg; } - uint32_t ctrl_value = - mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); - ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_RXILVL_FIELD, - rx_level_value); - ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_FMTILVL_FIELD, - fmt_level_value); - mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, - ctrl_value); + i2c_peri->INTR_STATE = bitfield_write(0, + BIT_MASK_1, + irq, + true); - return kDifI2cOk; + return kI2cOk; } -static bool irq_index(i2c_irq_t irq, bitfield_bit32_index_t *bit_index) { - switch (irq) { - case kDifI2cIrqFmtWatermarkUnderflow: - *bit_index = I2C_INTR_COMMON_FMT_WATERMARK_BIT; - break; - case kDifI2cIrqRxWatermarkOverflow: - *bit_index = I2C_INTR_COMMON_RX_WATERMARK_BIT; - break; - case kDifI2cIrqFmtFifoOverflow: - *bit_index = I2C_INTR_COMMON_FMT_OVERFLOW_BIT; - break; - case kDifI2cIrqRxFifoOverflow: - *bit_index = I2C_INTR_COMMON_RX_OVERFLOW_BIT; - break; - case kDifI2cIrqNak: - *bit_index = I2C_INTR_COMMON_NAK_BIT; - break; - case kDifI2cIrqSclInterference: - *bit_index = I2C_INTR_COMMON_SCL_INTERFERENCE_BIT; - break; - case kDifI2cIrqSdaInterference: - *bit_index = I2C_INTR_COMMON_SDA_INTERFERENCE_BIT; - break; - case kDifI2cIrqClockStretchTimeout: - *bit_index = I2C_INTR_COMMON_STRETCH_TIMEOUT_BIT; - break; - case kDifI2cIrqSdaUnstable: - *bit_index = I2C_INTR_COMMON_SDA_UNSTABLE_BIT; - break; - default: - return false; - } - return true; -} -i2c_result_t i2c_irq_is_pending(const i2c_t *i2c, i2c_irq_t irq, - bool *is_pending) { - if (i2c == NULL || is_pending == NULL) { - return kDifI2cBadArg; - } +i2c_result_t i2c_irq_get_enabled(i2c_irq_t irq, i2c_toggle_t *state) +{ - bitfield_bit32_index_t index; - if (!irq_index(irq, &index)) { - return kDifI2cBadArg; + // Sanity check on the irq + if(irq < 0 || irq > kI2cNIrqTypes) + { + return kI2cBadArg; } - uint32_t reg = - mmio_region_read32(i2c->params.base_addr, I2C_INTR_STATE_REG_OFFSET); - *is_pending = bitfield_bit32_read(reg, index); + *state = bitfield_read(i2c_peri->INTR_ENABLE, BIT_MASK_1, irq); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_irq_acknowledge(const i2c_t *i2c, - i2c_irq_t irq) { - if (i2c == NULL) { - return kDifI2cBadArg; + +i2c_result_t i2c_irq_set_enabled(i2c_irq_t irq, i2c_toggle_t state) +{ + + if (state != kI2cToggleEnabled && state != kI2cToggleDisabled) + { + return kI2cBadArg; } - bitfield_bit32_index_t index; - if (!irq_index(irq, &index)) { - return kDifI2cBadArg; + // Sanity check on the irq + if(irq < 0 || irq > kI2cNIrqTypes) + { + return kI2cBadArg; } - uint32_t reg = bitfield_bit32_write(0, index, true); - mmio_region_write32(i2c->params.base_addr, I2C_INTR_STATE_REG_OFFSET, reg); + i2c_peri->INTR_ENABLE = bitfield_write(i2c_peri->INTR_ENABLE, + BIT_MASK_1, + irq, + state); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_irq_get_enabled(const i2c_t *i2c, - i2c_irq_t irq, - i2c_toggle_t *state) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - bitfield_bit32_index_t index; - if (!irq_index(irq, &index)) { - return kDifI2cBadArg; +i2c_result_t i2c_irq_force(i2c_irq_t irq) +{ + // Sanity check on the irq + if(irq < 0 || irq > kI2cNIrqTypes) + { + return kI2cBadArg; } - uint32_t reg = - mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); - bool is_enabled = bitfield_bit32_read(reg, index); - *state = is_enabled ? kDifI2cToggleEnabled : kDifI2cToggleDisabled; + i2c_peri->INTR_TEST = bitfield_write(0, + BIT_MASK_1, + irq, + true); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_irq_set_enabled(const i2c_t *i2c, - i2c_irq_t irq, - i2c_toggle_t state) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - bitfield_bit32_index_t index; - if (!irq_index(irq, &index)) { - return kDifI2cBadArg; - } +i2c_result_t i2c_irq_disable_all(i2c_irq_snapshot_t *snapshot) +{ - bool flag; - switch (state) { - case kDifI2cToggleEnabled: - flag = true; - break; - case kDifI2cToggleDisabled: - flag = false; - break; - default: - return kDifI2cBadArg; + if (snapshot != NULL) { + *snapshot = i2c_peri->INTR_ENABLE; } - uint32_t reg = - mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); - reg = bitfield_bit32_write(reg, index, flag); - mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, reg); + i2c_peri->INTR_ENABLE = 0; - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_irq_force(const i2c_t *i2c, i2c_irq_t irq) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - bitfield_bit32_index_t index; - if (!irq_index(irq, &index)) { - return kDifI2cBadArg; +i2c_result_t i2c_irq_restore_all(const i2c_irq_snapshot_t *snapshot) +{ + if (snapshot == NULL) { + return kI2cBadArg; } - uint32_t reg = bitfield_bit32_write(0, index, true); - mmio_region_write32(i2c->params.base_addr, I2C_INTR_TEST_REG_OFFSET, reg); + i2c_peri->INTR_ENABLE = *snapshot; - return kDifI2cOk; + return kI2cOk; } +i2c_result_t i2c_reset_rx_fifo() { -i2c_result_t i2c_irq_disable_all(const i2c_t *i2c, - i2c_irq_snapshot_t *snapshot) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - - if (snapshot != NULL) { - *snapshot = - mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); - } + i2c_peri->FIFO_CTRL = bitfield_write(i2c_peri->FIFO_CTRL, + BIT_MASK_1, + I2C_FIFO_CTRL_RXRST_BIT, + true); + return kI2cOk; +} - mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, 0); +i2c_result_t i2c_reset_fmt_fifo() { - return kDifI2cOk; + i2c_peri->FIFO_CTRL = bitfield_write(i2c_peri->FIFO_CTRL, + BIT_MASK_1, + I2C_FIFO_CTRL_FMTRST_BIT, + true); + return kI2cOk; } -i2c_result_t i2c_irq_restore_all( - const i2c_t *i2c, const i2c_irq_snapshot_t *snapshot) { - if (i2c == NULL || snapshot == NULL) { - return kDifI2cBadArg; - } - mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, - *snapshot); +i2c_result_t i2c_set_watermarks(i2c_level_t rx_level, i2c_level_t fmt_level) { - return kDifI2cOk; -} - -i2c_result_t i2c_host_set_enabled(const i2c_t *i2c, - i2c_toggle_t state) { - if (i2c == NULL) { - return kDifI2cBadArg; + // TODO: is it bad in these 2 cases to use directly the value of + // the level_t enum to avoid the case switch and to read the MACRO? + // The values are the same anyway + ptrdiff_t rx_level_value; + switch (rx_level) { + case kI2cLevel1Byte: + rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL1; + break; + case kI2cLevel4Byte: + rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL4; + break; + case kI2cLevel8Byte: + rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL8; + break; + case kI2cLevel16Byte: + rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL16; + break; + case kI2cLevel30Byte: + rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL30; + break; + default: + return kI2cBadArg; } - bool flag; - switch (state) { - case kDifI2cToggleEnabled: - flag = true; + ptrdiff_t fmt_level_value; + switch (fmt_level) { + case kI2cLevel1Byte: + fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL1; + break; + case kI2cLevel4Byte: + fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL4; break; - case kDifI2cToggleDisabled: - flag = false; + case kI2cLevel8Byte: + fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL8; + break; + case kI2cLevel16Byte: + fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL16; break; default: - return kDifI2cBadArg; + return kI2cBadArg; } - uint32_t reg = mmio_region_read32(i2c->params.base_addr, I2C_CTRL_REG_OFFSET); - reg = bitfield_bit32_write(reg, I2C_CTRL_ENABLEHOST_BIT, flag); - mmio_region_write32(i2c->params.base_addr, I2C_CTRL_REG_OFFSET, reg); + i2c_peri->FIFO_CTRL = bitfield_write(i2c_peri->FIFO_CTRL, + I2C_FIFO_CTRL_RXILVL_MASK, + I2C_FIFO_CTRL_RXILVL_OFFSET, + rx_level_value); + i2c_peri->FIFO_CTRL = bitfield_write(i2c_peri->FIFO_CTRL, + I2C_FIFO_CTRL_FMTILVL_MASK, + I2C_FIFO_CTRL_FMTILVL_OFFSET, + fmt_level_value); + - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_override_set_enabled(const i2c_t *i2c, - i2c_toggle_t state) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - bool flag; - switch (state) { - case kDifI2cToggleEnabled: - flag = true; - break; - case kDifI2cToggleDisabled: - flag = false; - break; - default: - return kDifI2cBadArg; +i2c_result_t i2c_host_set_enabled(i2c_toggle_t state) { + + if (state != kI2cToggleEnabled && state != kI2cToggleDisabled) + { + return kI2cBadArg; } - uint32_t reg = mmio_region_read32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET); - reg = bitfield_bit32_write(reg, I2C_OVRD_TXOVRDEN_BIT, flag); - mmio_region_write32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET, reg); + i2c_peri->CTRL = bitfield_write(i2c_peri->CTRL, + BIT_MASK_1, + I2C_CTRL_ENABLEHOST_BIT, + state); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_override_drive_pins(const i2c_t *i2c, bool scl, - bool sda) { - if (i2c == NULL) { - return kDifI2cBadArg; +i2c_result_t i2c_override_set_enabled(i2c_toggle_t state) { + + if (state != kI2cToggleEnabled && state != kI2cToggleDisabled) + { + return kI2cBadArg; } - uint32_t override_val = - mmio_region_read32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET); - override_val = bitfield_bit32_write(override_val, I2C_OVRD_SCLVAL_BIT, scl); - override_val = bitfield_bit32_write(override_val, I2C_OVRD_SDAVAL_BIT, sda); - mmio_region_write32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET, override_val); + i2c_peri->OVRD = bitfield_write(i2c_peri->OVRD, + BIT_MASK_1, + I2C_OVRD_TXOVRDEN_BIT, + state); - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_override_sample_pins(const i2c_t *i2c, - uint16_t *scl_samples, - uint16_t *sda_samples) { - if (i2c == NULL) { - return kDifI2cBadArg; - } +i2c_result_t i2c_override_drive_pins(bool scl, bool sda) { + + i2c_peri->OVRD = bitfield_write(i2c_peri->OVRD, + BIT_MASK_1, + I2C_OVRD_SCLVAL_BIT, + scl); + i2c_peri->OVRD = bitfield_write(i2c_peri->OVRD, + BIT_MASK_1, + I2C_OVRD_SDAVAL_BIT, + sda); + return kI2cOk; +} + +i2c_result_t i2c_override_sample_pins(uint16_t *scl_samples, + uint16_t *sda_samples) { - uint32_t samples = - mmio_region_read32(i2c->params.base_addr, I2C_VAL_REG_OFFSET); - if (scl_samples != NULL) { - *scl_samples = bitfield_field32_read(samples, I2C_VAL_SCL_RX_FIELD); + if (scl_samples != NULL) + { + bitfield_read(i2c_peri->VAL, I2C_VAL_SCL_RX_MASK, I2C_VAL_SCL_RX_OFFSET); } - if (sda_samples != NULL) { - *sda_samples = bitfield_field32_read(samples, I2C_VAL_SDA_RX_FIELD); + if (sda_samples != NULL) + { + bitfield_read(i2c_peri->VAL, I2C_VAL_SDA_RX_MASK, I2C_VAL_SDA_RX_OFFSET); } - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_get_fifo_levels(const i2c_t *i2c, - uint8_t *fmt_fifo_level, - uint8_t *rx_fifo_level) { - if (i2c == NULL) { - return kDifI2cBadArg; - } - uint32_t values = - mmio_region_read32(i2c->params.base_addr, I2C_FIFO_STATUS_REG_OFFSET); - if (fmt_fifo_level != NULL) { - *fmt_fifo_level = - bitfield_field32_read(values, I2C_FIFO_STATUS_FMTLVL_FIELD); +i2c_result_t i2c_get_fifo_levels(uint8_t *fmt_fifo_level, + uint8_t *rx_fifo_level) { + + if (fmt_fifo_level != NULL) + { + *fmt_fifo_level = bitfield_read(i2c_peri->FIFO_STATUS, + I2C_FIFO_STATUS_FMTLVL_MASK, + I2C_FIFO_STATUS_FMTLVL_OFFSET); } - if (rx_fifo_level != NULL) { - *rx_fifo_level = bitfield_field32_read(values, I2C_FIFO_STATUS_RXLVL_FIELD); + + if (rx_fifo_level != NULL) + { + *rx_fifo_level = bitfield_read(i2c_peri->FIFO_STATUS, + I2C_FIFO_STATUS_RXLVL_MASK, + I2C_FIFO_STATUS_RXLVL_OFFSET); } - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_read_byte(const i2c_t *i2c, uint8_t *byte) { - if (i2c == NULL) { - return kDifI2cBadArg; - } +i2c_result_t i2c_read_byte(uint8_t *byte) { - uint32_t values = - mmio_region_read32(i2c->params.base_addr, I2C_RDATA_REG_OFFSET); - if (byte != NULL) { - *byte = bitfield_field32_read(values, I2C_RDATA_RDATA_FIELD); + if (byte != NULL) + { + *byte = bitfield_read(i2c_peri->RDATA, I2C_RDATA_RDATA_MASK, I2C_RDATA_RDATA_OFFSET); } - return kDifI2cOk; + return kI2cOk; } -i2c_result_t i2c_write_byte_raw(const i2c_t *i2c, uint8_t byte, - i2c_fmt_flags_t flags) { - if (i2c == NULL) { - return kDifI2cBadArg; - } +i2c_result_t i2c_write_byte_raw(uint8_t byte, i2c_fmt_flags_t flags) { + // Validate that "write only" flags and "read only" flags are not set // simultaneously. bool has_write_flags = flags.start || flags.stop || flags.suppress_nak_irq; bool has_read_flags = flags.read || flags.read_cont; if (has_write_flags && has_read_flags) { - return kDifI2cBadArg; + return kI2cBadArg; } + // Also, read_cont requires read. if (flags.read_cont && !flags.read) { - return kDifI2cBadArg; - } - - uint32_t fmt_byte = 0; - fmt_byte = bitfield_field32_write(fmt_byte, I2C_FDATA_FBYTE_FIELD, byte); - fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_START_BIT, flags.start); - fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_STOP_BIT, flags.stop); - fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_READ_BIT, flags.read); - fmt_byte = - bitfield_bit32_write(fmt_byte, I2C_FDATA_RCONT_BIT, flags.read_cont); - fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_NAKOK_BIT, - flags.suppress_nak_irq); - mmio_region_write32(i2c->params.base_addr, I2C_FDATA_REG_OFFSET, fmt_byte); - - return kDifI2cOk; + return kI2cBadArg; + } + + /* + Here I cannot perform every bitfield write directly in the register (using the + `i2c_peri` struct) because each write counts as a single entry (so it would + be counted as 6 entries instead of 1). + */ + uint32_t fmt_entry = 0; + fmt_entry = i2c_peri->FDATA; + fmt_entry = bitfield_write(fmt_entry, I2C_FDATA_FBYTE_MASK, I2C_FDATA_FBYTE_OFFSET, byte); + fmt_entry = bitfield_write(fmt_entry, BIT_MASK_1, I2C_FDATA_START_BIT, flags.start); + fmt_entry = bitfield_write(fmt_entry, BIT_MASK_1, I2C_FDATA_STOP_BIT, flags.stop); + fmt_entry = bitfield_write(fmt_entry, BIT_MASK_1, I2C_FDATA_READ_BIT, flags.read); + fmt_entry = bitfield_write(fmt_entry, BIT_MASK_1, I2C_FDATA_RCONT_BIT, flags.read_cont); + fmt_entry = bitfield_write(fmt_entry, BIT_MASK_1, I2C_FDATA_NAKOK_BIT, flags.suppress_nak_irq); + i2c_peri->FDATA = fmt_entry; + + + return kI2cOk; } -i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, - i2c_fmt_t code, bool suppress_nak_irq) { - if (i2c == NULL) { - return kDifI2cBadArg; - } +i2c_result_t i2c_write_byte(uint8_t byte, i2c_fmt_t code, bool suppress_nak_irq) { // Validate that `suppress_nak_irq` has not been mixed with an Rx code. if (suppress_nak_irq) { switch (code) { - case kDifI2cFmtRx: - case kDifI2cFmtRxContinue: - case kDifI2cFmtRxStop: - return kDifI2cBadArg; + case kI2cFmtRx: + case kI2cFmtRxContinue: + case kI2cFmtRxStop: + return kI2cBadArg; default: break; } @@ -600,33 +628,114 @@ i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, // Convert the format code into flags. i2c_fmt_flags_t flags = {.suppress_nak_irq = suppress_nak_irq}; switch (code) { - case kDifI2cFmtStart: + case kI2cFmtStart: flags.start = true; break; - case kDifI2cFmtTx: + case kI2cFmtTx: break; - case kDifI2cFmtTxStop: + case kI2cFmtTxStop: flags.stop = true; break; - case kDifI2cFmtRx: + case kI2cFmtRx: flags.read = true; break; - case kDifI2cFmtRxContinue: + case kI2cFmtRxContinue: flags.read = true; flags.read_cont = true; break; - case kDifI2cFmtRxStop: + case kI2cFmtRxStop: flags.read = true; flags.stop = true; break; default: - return kDifI2cBadArg; + return kI2cBadArg; } - return i2c_write_byte_raw(i2c, byte, flags); + return i2c_write_byte_raw(byte, flags); } -__attribute__((weak, optimize("O0"))) void handler_irq_i2c(uint32_t id) -{ - // Replace this function with a non-weak implementation -} \ No newline at end of file + +/****************************************************************************/ +/** **/ +/* LOCAL FUNCTIONS */ +/** **/ +/****************************************************************************/ + + +uint16_t round_up_divide(uint32_t a, uint32_t b) { + if (a == 0) { + return 0; + } + + return ((a - 1) / b) + 1; +} + + +i2c_config_t default_timing_for_speed(i2c_speed_t speed, + uint32_t clock_period_nanos) { + // All values are given in nanoseconds + switch (speed) { + case kI2cSpeedStandard: + return (i2c_config_t){ + .scl_time_high_cycles = round_up_divide(T_HIGH_SCL_STANDARD, + clock_period_nanos), + .scl_time_low_cycles = round_up_divide(T_LOW_SCL_STANDARD, + clock_period_nanos), + .start_signal_setup_cycles = round_up_divide(T_START_SET_UP_STANDARD, + clock_period_nanos), + .start_signal_hold_cycles = round_up_divide(T_START_HOLD_STANDARD, + clock_period_nanos), + .data_signal_setup_cycles = round_up_divide(T_DATA_SET_UP_STANDARD, + clock_period_nanos), + .data_signal_hold_cycles = T_SIGNAL_HOLD_STANDARD, + .stop_signal_setup_cycles = round_up_divide(T_STOP_SET_UP_STANDARD, + clock_period_nanos), + .stop_signal_hold_cycles = round_up_divide(T_STOP_HOLD_STANDARD, + clock_period_nanos), + }; + case kI2cSpeedFast: + return (i2c_config_t){ + .scl_time_high_cycles = round_up_divide(T_HIGH_SCL_FAST, + clock_period_nanos), + .scl_time_low_cycles = round_up_divide(T_LOW_SCL_FAST, + clock_period_nanos), + .start_signal_setup_cycles = round_up_divide(T_START_SET_UP_FAST, + clock_period_nanos), + .start_signal_hold_cycles = round_up_divide(T_START_HOLD_FAST, + clock_period_nanos), + .data_signal_setup_cycles = round_up_divide(T_DATA_SET_UP_FAST, + clock_period_nanos), + .data_signal_hold_cycles = T_SIGNAL_HOLD_FAST, + .stop_signal_setup_cycles = round_up_divide(T_STOP_SET_UP_FAST, + clock_period_nanos), + .stop_signal_hold_cycles = round_up_divide(T_STOP_HOLD_FAST, + clock_period_nanos), + }; + case kI2cSpeedFastPlus: + return (i2c_config_t){ + .scl_time_high_cycles = round_up_divide(T_HIGH_SCL_FAST_PLUS, + clock_period_nanos), + .scl_time_low_cycles = round_up_divide(T_LOW_SCL_FAST_PLUS, + clock_period_nanos), + .start_signal_setup_cycles = round_up_divide(T_START_SET_UP_FAST_PLUS, + clock_period_nanos), + .start_signal_hold_cycles = round_up_divide(T_START_HOLD_FAST_PLUS, + clock_period_nanos), + .data_signal_setup_cycles = round_up_divide(T_DATA_SET_UP_FAST_PLUS, + clock_period_nanos), + .data_signal_hold_cycles = T_SIGNAL_HOLD_FAST_PLUS, + .stop_signal_setup_cycles = round_up_divide(T_STOP_SET_UP_FAST_PLUS, + clock_period_nanos), + .stop_signal_hold_cycles = round_up_divide(T_STOP_HOLD_FAST_PLUS, + clock_period_nanos), + }; + default: + return (i2c_config_t){0}; + } +} + +/****************************************************************************/ +/** **/ +/* EOF */ +/** **/ +/****************************************************************************/ diff --git a/sw/device/lib/drivers/i2c/i2c.h b/sw/device/lib/drivers/i2c/i2c.h index 6c1706d9f..cac6127a9 100644 --- a/sw/device/lib/drivers/i2c/i2c.h +++ b/sw/device/lib/drivers/i2c/i2c.h @@ -1,40 +1,155 @@ -// Copyright lowRISC contributors. -// Licensed under the Apache License, Version 2.0, see LICENSE for details. -// SPDX-License-Identifier: Apache-2.0 - -#ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ -#define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ - +/* + ******************* +******************************* H SOURCE FILE ******************************* +** ******************* ** +** ** +** project : x-heep ** +** filename : i2c.h ** +** version : 1.0 ** +** date : 03/06/2023 ** +** ** +***************************************************************************** +** ** +** ** +***************************************************************************** + +*/ + +/***************************************************************************/ +/***************************************************************************/ + +/** +* @file i2c.h +* @date 16/05/2023 +* @brief This is the main header file of the HAL for I2C peripheral +* +* In this files there are definitions of low level HAL functions to interact +* with the registers of the I2C peripheral. +* The functionalities implemented allow to configure the peripheral with +* parameters for the FMT and RX fifos, enable or disable interrupts and write +* and read bytes. +*/ + +#ifndef _I2C_H_ +#define _I2C_H_ + +/****************************************************************************/ +/** **/ +/** MODULES USED **/ +/** **/ +/****************************************************************************/ + +#include "inttypes.h" +#include "stddef.h" +#include "stdbool.h" + +/****************************************************************************/ +/** **/ +/** DEFINITIONS AND MACROS **/ +/** **/ +/****************************************************************************/ + +/** + * Maximum bandwidth supported expressed as [nano seconds * Baud] +*/ +#define NANO_SEC_PER_KBAUD 1000000 // One million + +/** + * Possible speed (expressed in kBaud) for the I2C devices +*/ +#define STANDARD_MODE_SPEED 100 +#define FAST_MODE_SPEED 400 +#define FAST_MODE_PLUS_SPEED 1000 + + +/** + * Speed constants for the Standard-speed mode +*/ +#define T_HIGH_SCL_STANDARD 4000 +#define T_LOW_SCL_STANDARD 4700 +#define T_START_SET_UP_STANDARD 4700 +#define T_START_HOLD_STANDARD 4000 +#define T_DATA_SET_UP_STANDARD 250 +#define T_SIGNAL_HOLD_STANDARD 0 +#define T_STOP_SET_UP_STANDARD 4000 +#define T_STOP_HOLD_STANDARD 4700 + + +/** + * Speed constants for the Fast-speed mode +*/ +#define T_HIGH_SCL_FAST 600 +#define T_LOW_SCL_FAST 1300 +#define T_START_SET_UP_FAST 600 +#define T_START_HOLD_FAST 600 +#define T_DATA_SET_UP_FAST 100 +#define T_SIGNAL_HOLD_FAST 0 +#define T_STOP_SET_UP_FAST 600 +#define T_STOP_HOLD_FAST 1300 + + +/** + * Speed constants for the Fast-plus-speed mode +*/ +#define T_HIGH_SCL_FAST_PLUS 260 +#define T_LOW_SCL_FAST_PLUS 500 +#define T_START_SET_UP_FAST_PLUS 260 +#define T_START_HOLD_FAST_PLUS 260 +#define T_DATA_SET_UP_FAST_PLUS 50 +#define T_SIGNAL_HOLD_FAST_PLUS 0 +#define T_STOP_SET_UP_FAST_PLUS 260 +#define T_STOP_HOLD_FAST_PLUS 500 + +/****************************************************************************/ +/** **/ +/** TYPEDEFS AND STRUCTURES **/ +/** **/ +/****************************************************************************/ + /** - * @file - * @brief I2C Device Interface Functions - */ - -#include -#include + * Pointer to a generic handler function for the I2C. + * This pointer is used to sequentially store different + * handlers for different I2C interrupt cases. +*/ +typedef void (*handler_func_i2c_t)(void); -#include "mmio.h" - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus /** - * A toggle state: enabled, or disabled. - * - * This enum may be used instead of a `bool` when describing an enabled/disabled - * state. - */ + * A toggle state: enabled ior disabled. +*/ typedef enum i2c_toggle { + /** + * The "disabled" state. + */ + kI2cToggleDisabled, /* * The "enabled" state. */ - kDifI2cToggleEnabled, + kI2cToggleEnabled +} i2c_toggle_t; + + +/** + * The result of a I2C operation. + */ +typedef enum i2c_result { + /** + * Indicates that the operation succeeded. + */ + kI2cOk, /** - * The "disabled" state. + * Indicates some unspecified failure. */ - kDifI2cToggleDisabled, -} i2c_toggle_t; + kI2cError, + /** + * Indicates that some parameter passed into a function failed a + * precondition. + * + * When this value is returned, no hardware operations occurred. + */ + kI2cBadArg, +} i2c_result_t; + /** * Represents a speed setting for an I2C component: standard, fast, and @@ -45,17 +160,18 @@ typedef enum i2c_speed { /** * Standard speed, 100 kilobaud. */ - kDifI2cSpeedStandard, + kI2cSpeedStandard, /** * Fast speed, 400 kilobaud. */ - kDifI2cSpeedFast, + kI2cSpeedFast, /** * Fast plus speed, 1 megabaud. */ - kDifI2cSpeedFastPlus, + kI2cSpeedFastPlus, } i2c_speed_t; + /** * Timing configuration parameters for I2C. * @@ -103,19 +219,6 @@ typedef struct i2c_timing_config { uint32_t scl_period_nanos; } i2c_timing_config_t; -/** - * Hardware instantiation parameters for I2C. - * - * This struct describes information about the underlying hardware that is - * not determined until the hardware design is used as part of a top-level - * design. - */ -typedef struct i2c_params { - /** - * The base address for the I2C hardware registers. - */ - mmio_region_t base_addr; -} i2c_params_t; /** * Runtime configuration for I2C. @@ -126,8 +229,7 @@ typedef struct i2c_params { * these values themselves if they prefer, so long as the I2C spec is * respected. * - * These values correspond to those in Table 10 of the I2C spec, and are given - * in units of input clock cycles. + * These values are given in units of input clock cycles. */ typedef struct i2c_config { uint16_t scl_time_high_cycles; @@ -139,91 +241,19 @@ typedef struct i2c_config { uint16_t data_signal_setup_cycles; uint16_t data_signal_hold_cycles; uint16_t stop_signal_setup_cycles; - /** - * This parameter is referred to in the I2C documents as the - * "bus free time". - */ uint16_t stop_signal_hold_cycles; } i2c_config_t; -/** - * A handle to I2C. - * - * This type should be treated as opaque by users. - */ -typedef struct i2c { i2c_params_t params; } i2c_t; - -/** - * The result of a I2C operation. - */ -typedef enum i2c_result { - /** - * Indicates that the operation succeeded. - */ - kDifI2cOk = 0, - /** - * Indicates some unspecified failure. - */ - kDifI2cError = 1, - /** - * Indicates that some parameter passed into a function failed a - * precondition. - * - * When this value is returned, no hardware operations occurred. - */ - kDifI2cBadArg = 2, -} i2c_result_t; - -/** - * Represents an I2C-related interrupt type. - */ -typedef enum i2c_irq { - /** - * Fired when the FMT FIFO underflows its watermark. - */ - kDifI2cIrqFmtWatermarkUnderflow = 0, - /** - * Fired when the RX FIFO overflows its watermark. - */ - kDifI2cIrqRxWatermarkOverflow, - /** - * Fired when the FMT FIFO overflows. - */ - kDifI2cIrqFmtFifoOverflow, - /** - * Fired when the RX FIFO overflows. - */ - kDifI2cIrqRxFifoOverflow, - /** - * Fired when there is no ACK in response to an address or data write. - */ - kDifI2cIrqNak, - /** - * Fired when the SCL line seems to have interference. - */ - kDifI2cIrqSclInterference, - /** - * Fired when the SDA line seems to have interference. - */ - kDifI2cIrqSdaInterference, - /** - * Fired when the target stretches the clock beyond the allowed period. - */ - kDifI2cIrqClockStretchTimeout, - /** - * Fired when the target does not maintain a stable SDA line. - */ - kDifI2cIrqSdaUnstable, -} i2c_irq_t; /** * A snapshot of the entablement state of the interrupts for I2C. * - * This is an opaque type, to be used with the `i2c_irq_disable_all()` and + * This is to be used with the `i2c_irq_disable_all()` and * `i2c_irq_restore_all()` functions. */ typedef uint32_t i2c_irq_snapshot_t; + /** * Represents a valid watermark level for one of the I2C FIFOs. */ @@ -231,27 +261,28 @@ typedef enum i2c_watermark_level { /** * A one-byte watermark. */ - kDifI2cLevel1Byte = 0, + kI2cLevel1Byte = 0, /** * A four-byte watermark. */ - kDifI2cLevel4Byte, + kI2cLevel4Byte, /** * An eight-byte watermark. */ - kDifI2cLevel8Byte, + kI2cLevel8Byte, /** * A sixteen-byte watermark. */ - kDifI2cLevel16Byte, + kI2cLevel16Byte, /** * A thirty-byte watermark. * * Note that this watermark is only supported for RX, and not for FMT. */ - kDifI2cLevel30Byte, + kI2cLevel30Byte, } i2c_level_t; + /** * Flags for a formatted I2C byte, used by the `i2c_write_byte_raw()` * function. @@ -283,22 +314,19 @@ typedef struct i2c_fmt_flags { bool read_cont; /** * By default, the hardware expects an ACK after every byte sent, and raises - * an exception (surfaced as the `kDifi2cIrqNak` interrupt). This flag - * disables that behavior. + * an exception. This flag disables that behavior. * * This flag cannot be set along with `read` or `read_cont`. */ bool suppress_nak_irq; } i2c_fmt_flags_t; + /** * Available formatting codes for `i2c_write_byte_raw()`. * * Each code describes how to interpret the `byte` parameter, referred to below * as "the byte". - * - * It is the caller's responsibility to observe the state transitions in the - * comments below. */ typedef enum i2c_fmt { /** @@ -308,21 +336,21 @@ typedef enum i2c_fmt { * * May be followed by any format code. */ - kDifI2cFmtStart, + kI2cFmtStart, /** * Transmit byte. This simply sends the byte. It may need to be used in * conjunction with `Start` to send a multi-byte target address. * * May be followed by any format code. */ - kDifI2cFmtTx, + kI2cFmtTx, /** * Transmit byte and stop. This sends the byte, and then sends a stop * signal, completing a transaction. * * Only `Start` may follow this code. */ - kDifI2cFmtTxStop, + kI2cFmtTxStop, /** * Request `n` bytes, where `n` is the byte interpreted as an unsigned * integer; a byte value of `0` will be interpreted as requesting `256` @@ -331,126 +359,286 @@ typedef enum i2c_fmt { * Only `Start` may follow this code (this code does not stop a transaction; * see `RxStop`). */ - kDifI2cFmtRx, + kI2cFmtRx, /** * Request `n` bytes, same as `Rx`, but ACK the last byte so that more data * can be requested. * * May be followed by `RxContinue`, `Rx`, or `RxStop`. */ - kDifI2cFmtRxContinue, + kI2cFmtRxContinue, /** * Request `n` bytes, same as `Rx`, but, after NAKing the last byte, send a * stop signal to end the transaction. * * Only `Start` may follow this code. */ - kDifI2cFmtRxStop, + kI2cFmtRxStop, } i2c_fmt_t; + /** - * Computes timing parameters for an I2C device and stores them in `config`. - * - * The values returned may be tweaked by callers that require finer control over - * some of the calculations, such as how the allocation of a lengthened SCL - * period. - * - * @param timing_config Configuration values for producing timing parameters. - * @param[out] config I2C configuration to which to apply the computed - * parameters. - * @return The result of the operation. + * Represents an I2C-related interrupt type. */ -i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, - i2c_config_t *config); +typedef enum i2c_irq { + /** + * Fired when the FMT FIFO underflows its watermark. + */ + kI2cIrqFmtWatermarkUnderflow = 0, + /** + * Fired when the RX FIFO overflows its watermark. + */ + kI2cIrqRxWatermarkOverflow, + /** + * Fired when the FMT FIFO overflows. + */ + kI2cIrqFmtFifoOverflow, + /** + * Fired when the RX FIFO overflows. + */ + kI2cIrqRxFifoOverflow, + /** + * Fired when there is no ACK in response to an address or data write. + */ + kI2cIrqNak, + /** + * Fired when the SCL line seems to have interference. + */ + kI2cIrqSclInterference, + /** + * Fired when the SDA line seems to have interference. + */ + kI2cIrqSdaInterference, + /** + * Fired when the target stretches the clock beyond the allowed period. + */ + kI2cIrqClockStretchTimeout, + /** + * Fired when the target does not maintain a stable SDA line. + */ + kI2cIrqSdaUnstable, + /** + * Fires when host issues a repeated START or terminates the transaction + * with a STOP. + */ + kI2cIrqTransComplete, + /** + * Fires when the target stretches the clock for a read command (TX FIFO empty) + */ + kI2cIrqTxEmpty, + /** + * Fires when the target stretches the clock for a read command (ACQ FIFO non empty) + */ + kI2cIrqTxNonEmpty, + /** + * Fires when the TX FIFO overflows + */ + kI2cIrqTxOverflow, + /** + * Fires when the ACQ FIFO is full + */ + kI2cIrqAcqOverflow, + /** + * Fires when a STOP is received without a preceding NACK (target mode) + */ + kI2cIrqAckStop, + /** + * Fires when the host stop sending the clock during a transaction (target mode) + */ + kI2cIrqHostTimeout, + + /** + * Helper variable used to keep track of the amount of different I2C-related + * interrupt requests there can be. + * Whenever a new IRQ is added after the last one, this variable should + * be changed as well in order to keep track of the correct number. + */ + kI2cNIrqTypes = kI2cIrqHostTimeout +} i2c_irq_t; + +/****************************************************************************/ +/** **/ +/** EXPORTED VARIABLES **/ +/** **/ +/****************************************************************************/ + +/****************************************************************************/ +/** **/ +/** EXPORTED FUNCTIONS **/ +/** **/ +/****************************************************************************/ + /** - * Creates a new handle for I2C. - * - * This function does not actuate the hardware. + * IRQ handler for when the FMT FIFO depth goes under the watermark +*/ +void handler_irq_i2c_fmtWatermarkUnderflow(void); + +/** + * IRQ handler for when the RX FIFO depth goes over the watermark +*/ +void handler_irq_i2c_rxWatermarkOverflow(void); + +/** + * IRQ handler for when the FMT FIFO overflows +*/ +void handler_irq_i2c_fmtOverflow(void); + +/** + * IRQ handler for when the RX FIFO overflows +*/ +void handler_irq_i2c_rxOverflow(void); + +/** + * IRQ handler for when there is no ACK response +*/ +void handler_irq_i2c_nak(void); + +/** + * IRQ handler for when the SCL line appears to have interference +*/ +void handler_irq_i2c_sclInterference(void); + +/** + * IRQ handler for when the SDA line appears to have interference +*/ +void handler_irq_i2c_sdaInteference(void); + +/** + * IRQ handler for when the target stretches the clock beyond the allower period +*/ +void handler_irq_i2c_clockStretchTimeout(void); + +/** + * IRQ handler for when the target doesn't keep the SDA line stable +*/ +void handler_irq_i2c_sdaUnstable(void); + +/** + * IRQ handler for when the host issues a repeated START or terminates the transaction + * with a STOP. +*/ +void handler_irq_i2c_transComplete(void); + +/** + * IRQ handler for when the target stretches the clock for a read command (TX FIFO empty) +*/ +void handler_irq_i2c_txEmpty(void); + +/** + * IRQ handler for when the target stretches the clock for a read command (ACQ FIFO non empty) +*/ +void handler_irq_i2c_txNonEmpty(void); + +/** + * IRQ handler for when the TX FIFO overflows +*/ +void handler_irq_i2c_txOverflow(void); + +/** + * IRQ handler for when the ACQ FIFO is full +*/ +void handler_irq_i2c_acqOverflow(void); + +/** + * IRQ handler for when a STOP is received without a preceding NACK (target mode) +*/ +void handler_irq_i2c_ackStop(void); + +/** + * IRQ handler for when the host stop sending the clock during a transaction (target mode) +*/ +void handler_irq_i2c_hostTimeout(void); + +/** + * Generic hanlder for the I2C interupts. + * Whenever the I2C generates an interrupt, this function will be called + * by the interrupt controller and the proper handler will be called, basing on the + * interrupt ID. * - * @param params Hardware instantiation parameters. - * @param[out] i2c Out param for the initialized handle. + * @param id An interrupt source ID +*/ +void handler_i2c(uint32_t id); + + +/** + * Computes timing parameters for an I2C device and store them in `config`. + * + * @param timing_config Configuration values for producing timing parameters. + * @param[out] config I2C configuration to which to apply the computed parameters. * @return The result of the operation. - */ -i2c_result_t i2c_init(i2c_params_t params, i2c_t *i2c); +*/ +i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, + i2c_config_t *config); + /** * Configures I2C with runtime information. * - * This function should need to be called once for the lifetime of `handle`. - * - * @param i2c An I2C handle. * @param config Runtime configuration parameters. * @return The result of the operation. */ -i2c_result_t i2c_configure(const i2c_t *i2c, - i2c_config_t config); +i2c_result_t i2c_configure(i2c_config_t config); + /** * Returns whether a particular interrupt is currently pending. * - * @param i2c An I2C handle. * @param irq An interrupt type. * @param[out] is_pending Out-param for whether the interrupt is pending. * @return The result of the operation. */ -i2c_result_t i2c_irq_is_pending(const i2c_t *i2c, i2c_irq_t irq, - bool *is_pending); +i2c_result_t i2c_irq_is_pending(i2c_irq_t irq, bool *is_pending); + /** * Acknowledges a particular interrupt, indicating to the hardware that it has * been successfully serviced. * - * @param i2c An I2C handle. * @param irq An interrupt type. * @return The result of the operation. */ -i2c_result_t i2c_irq_acknowledge(const i2c_t *i2c, - i2c_irq_t irq); +i2c_result_t i2c_irq_acknowledge(i2c_irq_t irq); + /** * Checks whether a particular interrupt is currently enabled or disabled. * - * @param i2c An I2C handle. * @param irq An interrupt type. - * @param[out] state Out-param toggle state of the interrupt. + * @param[out] state State of the interrupt. * @return The result of the operation. */ -i2c_result_t i2c_irq_get_enabled(const i2c_t *i2c, - i2c_irq_t irq, - i2c_toggle_t *state); +i2c_result_t i2c_irq_get_enabled(i2c_irq_t irq, i2c_toggle_t *state); + /** * Sets whether a particular interrupt is currently enabled or disabled. * - * @param i2c An I2C handle. * @param irq An interrupt type. * @param state The new toggle state for the interrupt. * @return The result of the operation. */ -i2c_result_t i2c_irq_set_enabled(const i2c_t *i2c, - i2c_irq_t irq, - i2c_toggle_t state); +i2c_result_t i2c_irq_set_enabled(i2c_irq_t irq, i2c_toggle_t state); + /** * Forces a particular interrupt, causing it to be serviced as if hardware had * asserted it. * - * @param i2c An I2C handle. * @param irq An interrupt type. * @return The result of the operation. */ -i2c_result_t i2c_irq_force(const i2c_t *i2c, i2c_irq_t irq); +i2c_result_t i2c_irq_force(i2c_irq_t irq); + /** * Disables all interrupts, optionally snapshotting all toggle state for later * restoration. * - * @param i2c An I2C handle. - * @param[out] snapshot Out-param for the snapshot; may be `NULL`. + * @param[out] snapshot Out-param for the snapshot. * @return The result of the operation. */ -i2c_result_t i2c_irq_disable_all(const i2c_t *i2c, - i2c_irq_snapshot_t *snapshot); +i2c_result_t i2c_irq_disable_all(i2c_irq_snapshot_t *snapshot); + /** * Restores interrupts from the given snapshot. @@ -458,159 +646,145 @@ i2c_result_t i2c_irq_disable_all(const i2c_t *i2c, * This function can be used with `i2c_irq_disable_all()` to temporary * interrupt save-and-restore. * - * @param i2c An I2C handle. * @param snapshot A snapshot to restore from. * @return The result of the operation. */ -i2c_result_t i2c_irq_restore_all( - const i2c_t *i2c, const i2c_irq_snapshot_t *snapshot); +i2c_result_t i2c_irq_restore_all(const i2c_irq_snapshot_t *snapshot); + /** * Resets the state of the RX FIFO, essentially dropping all received bytes. * - * @param i2c An I2c handle. * @return The result of the operation. */ -i2c_result_t i2c_reset_rx_fifo(const i2c_t *i2c); +i2c_result_t i2c_reset_rx_fifo(); + /** * Resets the state of the FMT FIFO, essentially dropping all scheduled * operations. * - * @param i2c An I2c handle. * @return The result of the operation. */ -i2c_result_t i2c_reset_fmt_fifo(const i2c_t *i2c); +i2c_result_t i2c_reset_fmt_fifo(); + /** - * Sets watermarks for for the RX and FMT FIFOs, which will fire the respective + * Sets watermarks for for the RX and FMT FIFOs, which will trigger the respective * interrupts when each fifo exceeds, or falls below, the set level. * * Note that the 30-byte level is only supported for the RX FIFO: trying to use * it with the FMT FIFO is an error. - * - * @param i2c An I2C handle. + * * @param rx_level The desired watermark level for the RX FIFO. * @param fmt_level The desired watermark level for the FMT FIFO. * @return The result of the operation. */ -i2c_result_t i2c_set_watermarks(const i2c_t *i2c, - i2c_level_t rx_level, - i2c_level_t fmt_level); +i2c_result_t i2c_set_watermarks(i2c_level_t rx_level, i2c_level_t fmt_level); + /** * Enables or disables the "Host I2C" functionality, effectively turning the * I2C device on or off. This function should be called to enable the device * once timings, interrupts, and watermarks are all configured. * - * @param i2c An I2C handle. * @param state The new toggle state for the host functionality. * @return The result of the operation. */ -i2c_result_t i2c_host_set_enabled(const i2c_t *i2c, - i2c_toggle_t state); +i2c_result_t i2c_host_set_enabled(i2c_toggle_t state); /** * Enables or disables the "override mode". In override mode, software is able * to directly control the driven values of the SCL and SDA lines using * `i2c_override_drive_pins()`. * - * @param i2c An I2C handle. * @param state The new toggle state for override mode.' * @return The result of the operation. */ -i2c_result_t i2c_override_set_enabled(const i2c_t *i2c, - i2c_toggle_t state); +i2c_result_t i2c_override_set_enabled(i2c_toggle_t state); + /** * Drives the SCL and SDA pins to the given values when "override mode" is * enabled. * - * @param i2c An I2C handle. * @param scl The value to drive SCL to. * @param sda The value to drive SDA to. * @return The result of the operation. */ -i2c_result_t i2c_override_drive_pins(const i2c_t *i2c, bool scl, - bool sda); +i2c_result_t i2c_override_drive_pins(bool scl, bool sda); + /** * Returns oversampling of the last 16 values of the SCL and SDA pins, with the * zeroth bit being the most recent. * - * @param i2c An I2C handle. - * @param[out] scl_samples SCL sample bits; may be `NULL`. - * @param[out] sda_samples SDA sample bits; may be `NULL`. + * @param[out] scl_samples SCL sample bits. + * @param[out] sda_samples SDA sample bits. * @return The result of the operation. */ -i2c_result_t i2c_override_sample_pins(const i2c_t *i2c, - uint16_t *scl_samples, - uint16_t *sda_samples); +i2c_result_t i2c_override_sample_pins(uint16_t *scl_samples, + uint16_t *sda_samples); + /** * Returns the current levels, i.e., number of entries, in the FMT and RX FIFOs. * These values represent the number of entries pending for send by hardware, * and entries pending for read by software, respectively. * - * @param i2c An I2C handle. - * @param[out] fmt_fifo_level The number of unsent FMT bytes; may be `NULL`. - * @param[out] rx_fifo_level The number of unread RX bytes; may be `NULL`. + * @param[out] fmt_fifo_level The number of unsent FMT bytes. + * @param[out] rx_fifo_level The number of unread RX bytes. * @return The result of the operation. */ -i2c_result_t i2c_get_fifo_levels(const i2c_t *i2c, - uint8_t *fmt_fifo_level, - uint8_t *rx_fifo_level); +i2c_result_t i2c_get_fifo_levels(uint8_t *fmt_fifo_level, + uint8_t *rx_fifo_level); + /** - * Pops an entry (a byte) off of the RX FIFO. Passing in `NULL` to the out-param - * will still trigger a byte pop. + * Pops an entry (a byte) off of the RX FIFO. * - * @param i2c An I2C handle. - * @param[out] byte The popped byte; may be `NULL`. + * @param[out] byte The popped byte. * @return The result of the operation. */ -i2c_result_t i2c_read_byte(const i2c_t *i2c, uint8_t *byte); +i2c_result_t i2c_read_byte(uint8_t *byte); + /** * Pushes a raw write entry onto the FMT FIFO, consisting of a byte and format * flags. This function can be called in sequence to enqueue an I2C * transmission. * - * Callers should prefer `i2c_write_byte()` instead, since that function - * provides clearer semantics. This function should only really be used for - * testing or troubleshooting a device. - * - * @param i2c An I2C handle. + * This function has to be preferred for testing and debugging. + * * @param byte The value to push onto the FIFO. * @param flags The flags to use for this write. * @return The result of the operation. */ -i2c_result_t i2c_write_byte_raw(const i2c_t *i2c, uint8_t byte, - i2c_fmt_flags_t flags); +i2c_result_t i2c_write_byte_raw(uint8_t byte, i2c_fmt_flags_t flags); + /** * Pushes a write entry onto the FMT FIFO, consisting of a byte and a format * code. This function can be called in sequence to enqueue an I2C * transmission. * - * @param i2c An I2C handle. * @param byte The value to push onto the FIFO. * @param code The code to use for this write. * @param suppress_nak_irq Whether to supress the NAK IRQ for this one byte. * May not be used in combination with `Rx` codes. * @return The result of the operation. */ -i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, - i2c_fmt_t code, bool suppress_nak_irq); - - -/** - * @brief Attends the plic interrupt. - */ -__attribute__((weak, optimize("O0"))) void handler_irq_i2c(uint32_t id); +i2c_result_t i2c_write_byte(uint8_t byte, i2c_fmt_t code, bool suppress_nak_irq); -#ifdef __cplusplus -} // extern "C" -#endif // __cplusplus +/****************************************************************************/ +/** **/ +/** INLINE FUNCTIONS **/ +/** **/ +/****************************************************************************/ -#endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ +#endif +/****************************************************************************/ +/** **/ +/** EOF **/ +/** **/ +/****************************************************************************/ diff --git a/sw/device/lib/drivers/i2c/i2c_old.c b/sw/device/lib/drivers/i2c/i2c_old.c new file mode 100644 index 000000000..ca6bb9bce --- /dev/null +++ b/sw/device/lib/drivers/i2c/i2c_old.c @@ -0,0 +1,627 @@ +// // Copyright lowRISC contributors. +// // Licensed under the Apache License, Version 2.0, see LICENSE for details. +// // SPDX-License-Identifier: Apache-2.0 + +// #include "i2c_old.h" + +// #include "bitfield.h" +// #include "i2c_regs.h" // Generated + +// /** +// * Performs a 32-bit integer unsigned division, rounding up. The bottom +// * 16 bits of the result are then returned. +// * +// * As usual, a divisor of 0 is still Undefined Behavior. +// */ +// static uint16_t round_up_divide(uint32_t a, uint32_t b) { +// if (a == 0) { +// return 0; +// } + +// return ((a - 1) / b) + 1; +// } + +// /** +// * Computes default timing parameters for a particular I2C speed, given the +// * clock period, in nanoseconds. +// * +// * Returns an unspecified value for an invalid speed. +// */ +// static i2c_config_t default_timing_for_speed(i2c_speed_t speed, +// uint32_t clock_period_nanos) { +// // NOTE: All constants below are lifted from Table 10 of the I2C spec. +// // All literal values are given in nanoseconds; we don't bother putting +// // these into constants since they are not used anywhere else. +// switch (speed) { +// case kDifI2cSpeedStandard: +// return (i2c_config_t){ +// .scl_time_high_cycles = round_up_divide(4000, clock_period_nanos), +// .scl_time_low_cycles = round_up_divide(4700, clock_period_nanos), +// .start_signal_setup_cycles = +// round_up_divide(4700, clock_period_nanos), +// .start_signal_hold_cycles = round_up_divide(4000, clock_period_nanos), +// .data_signal_setup_cycles = round_up_divide(250, clock_period_nanos), +// .data_signal_hold_cycles = 0, +// .stop_signal_setup_cycles = round_up_divide(4000, clock_period_nanos), +// .stop_signal_hold_cycles = round_up_divide(4700, clock_period_nanos), +// }; +// case kDifI2cSpeedFast: +// return (i2c_config_t){ +// .scl_time_high_cycles = round_up_divide(600, clock_period_nanos), +// .scl_time_low_cycles = round_up_divide(1300, clock_period_nanos), +// .start_signal_setup_cycles = round_up_divide(600, clock_period_nanos), +// .start_signal_hold_cycles = round_up_divide(600, clock_period_nanos), +// .data_signal_setup_cycles = round_up_divide(100, clock_period_nanos), +// .data_signal_hold_cycles = 0, +// .stop_signal_setup_cycles = round_up_divide(600, clock_period_nanos), +// .stop_signal_hold_cycles = round_up_divide(1300, clock_period_nanos), +// }; +// case kDifI2cSpeedFastPlus: +// return (i2c_config_t){ +// .scl_time_high_cycles = round_up_divide(260, clock_period_nanos), +// .scl_time_low_cycles = round_up_divide(500, clock_period_nanos), +// .start_signal_setup_cycles = round_up_divide(260, clock_period_nanos), +// .start_signal_hold_cycles = round_up_divide(260, clock_period_nanos), +// .data_signal_setup_cycles = round_up_divide(50, clock_period_nanos), +// .data_signal_hold_cycles = 0, +// .stop_signal_setup_cycles = round_up_divide(260, clock_period_nanos), +// .stop_signal_hold_cycles = round_up_divide(500, clock_period_nanos), +// }; +// default: +// return (i2c_config_t){0}; +// } +// } + +// static const uint32_t kNanosPerKBaud = 1000000; // One million. + +// i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, +// i2c_config_t *config) { +// if (config == NULL) { +// return kDifI2cBadArg; +// } +// uint32_t lowest_target_device_speed_khz; +// switch (timing_config.lowest_target_device_speed) { +// case kDifI2cSpeedStandard: +// lowest_target_device_speed_khz = 100; +// break; +// case kDifI2cSpeedFast: +// lowest_target_device_speed_khz = 400; +// break; +// case kDifI2cSpeedFastPlus: +// lowest_target_device_speed_khz = 1000; +// break; +// default: +// return kDifI2cBadArg; +// } + +// // This code follows the algorithm given in +// // https://docs.opentitan.org/hw/ip/i2c/doc/index.html#initialization + +// *config = default_timing_for_speed(timing_config.lowest_target_device_speed, +// timing_config.clock_period_nanos); + +// config->rise_cycles = round_up_divide(timing_config.sda_rise_nanos, +// timing_config.clock_period_nanos); +// config->fall_cycles = round_up_divide(timing_config.sda_fall_nanos, +// timing_config.clock_period_nanos); + +// uint32_t scl_period_nanos = timing_config.scl_period_nanos; +// uint32_t slowest_scl_period_nanos = +// kNanosPerKBaud / lowest_target_device_speed_khz; +// if (scl_period_nanos < slowest_scl_period_nanos) { +// scl_period_nanos = slowest_scl_period_nanos; +// } +// uint16_t scl_period_cycles = +// round_up_divide(scl_period_nanos, timing_config.clock_period_nanos); + +// // Lengthen the SCL high period to accommodate the desired SCL period. +// uint16_t lengthened_high_cycles = scl_period_cycles - +// config->scl_time_low_cycles - +// config->rise_cycles - config->fall_cycles; +// if (lengthened_high_cycles > config->scl_time_high_cycles) { +// config->scl_time_high_cycles = lengthened_high_cycles; +// } + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_init(i2c_params_t params, i2c_t *i2c) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// i2c->params = params; + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_configure(const i2c_t *i2c, +// i2c_config_t config) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t timing0 = 0; +// timing0 = bitfield_field32_write(timing0, I2C_TIMING0_THIGH_FIELD, +// config.scl_time_high_cycles); +// timing0 = bitfield_field32_write(timing0, I2C_TIMING0_TLOW_FIELD, +// config.scl_time_low_cycles); +// mmio_region_write32(i2c->params.base_addr, I2C_TIMING0_REG_OFFSET, timing0); + +// uint32_t timing1 = 0; +// timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_R_FIELD, +// config.rise_cycles); +// timing1 = bitfield_field32_write(timing1, I2C_TIMING1_T_F_FIELD, +// config.fall_cycles); +// mmio_region_write32(i2c->params.base_addr, I2C_TIMING1_REG_OFFSET, timing1); + +// uint32_t timing2 = 0; +// timing2 = bitfield_field32_write(timing2, I2C_TIMING2_TSU_STA_FIELD, +// config.start_signal_setup_cycles); +// timing2 = bitfield_field32_write(timing2, I2C_TIMING2_THD_STA_FIELD, +// config.start_signal_hold_cycles); +// mmio_region_write32(i2c->params.base_addr, I2C_TIMING2_REG_OFFSET, timing2); + +// uint32_t timing3 = 0; +// timing3 = bitfield_field32_write(timing3, I2C_TIMING3_TSU_DAT_FIELD, +// config.data_signal_setup_cycles); +// timing3 = bitfield_field32_write(timing3, I2C_TIMING3_THD_DAT_FIELD, +// config.data_signal_hold_cycles); +// mmio_region_write32(i2c->params.base_addr, I2C_TIMING3_REG_OFFSET, timing3); + +// uint32_t timing4 = 0; +// timing4 = bitfield_field32_write(timing4, I2C_TIMING4_TSU_STO_FIELD, +// config.stop_signal_setup_cycles); +// timing4 = bitfield_field32_write(timing4, I2C_TIMING4_T_BUF_FIELD, +// config.stop_signal_hold_cycles); +// mmio_region_write32(i2c->params.base_addr, I2C_TIMING4_REG_OFFSET, timing4); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_reset_rx_fifo(const i2c_t *i2c) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = +// mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); +// reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_RXRST_BIT, true); +// mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_reset_fmt_fifo(const i2c_t *i2c) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = +// mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); +// reg = bitfield_bit32_write(reg, I2C_FIFO_CTRL_FMTRST_BIT, true); +// mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_set_watermarks(const i2c_t *i2c, +// i2c_level_t rx_level, +// i2c_level_t fmt_level) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// ptrdiff_t rx_level_value; +// switch (rx_level) { +// case kDifI2cLevel1Byte: +// rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL1; +// break; +// case kDifI2cLevel4Byte: +// rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL4; +// break; +// case kDifI2cLevel8Byte: +// rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL8; +// break; +// case kDifI2cLevel16Byte: +// rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL16; +// break; +// case kDifI2cLevel30Byte: +// rx_level_value = I2C_FIFO_CTRL_RXILVL_VALUE_RXLVL30; +// break; +// default: +// return kDifI2cBadArg; +// } + +// ptrdiff_t fmt_level_value; +// switch (fmt_level) { +// case kDifI2cLevel1Byte: +// fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL1; +// break; +// case kDifI2cLevel4Byte: +// fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL4; +// break; +// case kDifI2cLevel8Byte: +// fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL8; +// break; +// case kDifI2cLevel16Byte: +// fmt_level_value = I2C_FIFO_CTRL_FMTILVL_VALUE_FMTLVL16; +// break; +// default: +// return kDifI2cBadArg; +// } + +// uint32_t ctrl_value = +// mmio_region_read32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET); +// ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_RXILVL_FIELD, +// rx_level_value); +// ctrl_value = bitfield_field32_write(ctrl_value, I2C_FIFO_CTRL_FMTILVL_FIELD, +// fmt_level_value); +// mmio_region_write32(i2c->params.base_addr, I2C_FIFO_CTRL_REG_OFFSET, +// ctrl_value); + +// return kDifI2cOk; +// } + +// static bool irq_index(i2c_irq_t irq, bitfield_bit32_index_t *bit_index) { +// switch (irq) { +// case kDifI2cIrqFmtWatermarkUnderflow: +// *bit_index = I2C_INTR_COMMON_FMT_WATERMARK_BIT; +// break; +// case kDifI2cIrqRxWatermarkOverflow: +// *bit_index = I2C_INTR_COMMON_RX_WATERMARK_BIT; +// break; +// case kDifI2cIrqFmtFifoOverflow: +// *bit_index = I2C_INTR_COMMON_FMT_OVERFLOW_BIT; +// break; +// case kDifI2cIrqRxFifoOverflow: +// *bit_index = I2C_INTR_COMMON_RX_OVERFLOW_BIT; +// break; +// case kDifI2cIrqNak: +// *bit_index = I2C_INTR_COMMON_NAK_BIT; +// break; +// case kDifI2cIrqSclInterference: +// *bit_index = I2C_INTR_COMMON_SCL_INTERFERENCE_BIT; +// break; +// case kDifI2cIrqSdaInterference: +// *bit_index = I2C_INTR_COMMON_SDA_INTERFERENCE_BIT; +// break; +// case kDifI2cIrqClockStretchTimeout: +// *bit_index = I2C_INTR_COMMON_STRETCH_TIMEOUT_BIT; +// break; +// case kDifI2cIrqSdaUnstable: +// *bit_index = I2C_INTR_COMMON_SDA_UNSTABLE_BIT; +// break; +// default: +// return false; +// } +// return true; +// } + +// i2c_result_t i2c_irq_is_pending(const i2c_t *i2c, i2c_irq_t irq, +// bool *is_pending) { +// if (i2c == NULL || is_pending == NULL) { +// return kDifI2cBadArg; +// } + +// bitfield_bit32_index_t index; +// if (!irq_index(irq, &index)) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = +// mmio_region_read32(i2c->params.base_addr, I2C_INTR_STATE_REG_OFFSET); +// *is_pending = bitfield_bit32_read(reg, index); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_acknowledge(const i2c_t *i2c, +// i2c_irq_t irq) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bitfield_bit32_index_t index; +// if (!irq_index(irq, &index)) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = bitfield_bit32_write(0, index, true); +// mmio_region_write32(i2c->params.base_addr, I2C_INTR_STATE_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_get_enabled(const i2c_t *i2c, +// i2c_irq_t irq, +// i2c_toggle_t *state) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bitfield_bit32_index_t index; +// if (!irq_index(irq, &index)) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = +// mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); +// bool is_enabled = bitfield_bit32_read(reg, index); +// *state = is_enabled ? kDifI2cToggleEnabled : kDifI2cToggleDisabled; + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_set_enabled(const i2c_t *i2c, +// i2c_irq_t irq, +// i2c_toggle_t state) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bitfield_bit32_index_t index; +// if (!irq_index(irq, &index)) { +// return kDifI2cBadArg; +// } + +// bool flag; +// switch (state) { +// case kDifI2cToggleEnabled: +// flag = true; +// break; +// case kDifI2cToggleDisabled: +// flag = false; +// break; +// default: +// return kDifI2cBadArg; +// } + +// uint32_t reg = +// mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); +// reg = bitfield_bit32_write(reg, index, flag); +// mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_force(const i2c_t *i2c, i2c_irq_t irq) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bitfield_bit32_index_t index; +// if (!irq_index(irq, &index)) { +// return kDifI2cBadArg; +// } + +// uint32_t reg = bitfield_bit32_write(0, index, true); +// mmio_region_write32(i2c->params.base_addr, I2C_INTR_TEST_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_disable_all(const i2c_t *i2c, +// i2c_irq_snapshot_t *snapshot) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// if (snapshot != NULL) { +// *snapshot = +// mmio_region_read32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET); +// } + +// mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, 0); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_irq_restore_all( +// const i2c_t *i2c, const i2c_irq_snapshot_t *snapshot) { +// if (i2c == NULL || snapshot == NULL) { +// return kDifI2cBadArg; +// } + +// mmio_region_write32(i2c->params.base_addr, I2C_INTR_ENABLE_REG_OFFSET, +// *snapshot); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_host_set_enabled(const i2c_t *i2c, +// i2c_toggle_t state) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bool flag; +// switch (state) { +// case kDifI2cToggleEnabled: +// flag = true; +// break; +// case kDifI2cToggleDisabled: +// flag = false; +// break; +// default: +// return kDifI2cBadArg; +// } + +// uint32_t reg = mmio_region_read32(i2c->params.base_addr, I2C_CTRL_REG_OFFSET); +// reg = bitfield_bit32_write(reg, I2C_CTRL_ENABLEHOST_BIT, flag); +// mmio_region_write32(i2c->params.base_addr, I2C_CTRL_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_override_set_enabled(const i2c_t *i2c, +// i2c_toggle_t state) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// bool flag; +// switch (state) { +// case kDifI2cToggleEnabled: +// flag = true; +// break; +// case kDifI2cToggleDisabled: +// flag = false; +// break; +// default: +// return kDifI2cBadArg; +// } + +// uint32_t reg = mmio_region_read32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET); +// reg = bitfield_bit32_write(reg, I2C_OVRD_TXOVRDEN_BIT, flag); +// mmio_region_write32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET, reg); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_override_drive_pins(const i2c_t *i2c, bool scl, +// bool sda) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t override_val = +// mmio_region_read32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET); +// override_val = bitfield_bit32_write(override_val, I2C_OVRD_SCLVAL_BIT, scl); +// override_val = bitfield_bit32_write(override_val, I2C_OVRD_SDAVAL_BIT, sda); +// mmio_region_write32(i2c->params.base_addr, I2C_OVRD_REG_OFFSET, override_val); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_override_sample_pins(const i2c_t *i2c, +// uint16_t *scl_samples, +// uint16_t *sda_samples) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t samples = +// mmio_region_read32(i2c->params.base_addr, I2C_VAL_REG_OFFSET); +// if (scl_samples != NULL) { +// *scl_samples = bitfield_field32_read(samples, I2C_VAL_SCL_RX_FIELD); +// } + +// if (sda_samples != NULL) { +// *sda_samples = bitfield_field32_read(samples, I2C_VAL_SDA_RX_FIELD); +// } + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_get_fifo_levels(const i2c_t *i2c, +// uint8_t *fmt_fifo_level, +// uint8_t *rx_fifo_level) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t values = +// mmio_region_read32(i2c->params.base_addr, I2C_FIFO_STATUS_REG_OFFSET); +// if (fmt_fifo_level != NULL) { +// *fmt_fifo_level = +// bitfield_field32_read(values, I2C_FIFO_STATUS_FMTLVL_FIELD); +// } +// if (rx_fifo_level != NULL) { +// *rx_fifo_level = bitfield_field32_read(values, I2C_FIFO_STATUS_RXLVL_FIELD); +// } + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_read_byte(const i2c_t *i2c, uint8_t *byte) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// uint32_t values = +// mmio_region_read32(i2c->params.base_addr, I2C_RDATA_REG_OFFSET); +// if (byte != NULL) { +// *byte = bitfield_field32_read(values, I2C_RDATA_RDATA_FIELD); +// } + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_write_byte_raw(const i2c_t *i2c, uint8_t byte, +// i2c_fmt_flags_t flags) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } +// // Validate that "write only" flags and "read only" flags are not set +// // simultaneously. +// bool has_write_flags = flags.start || flags.stop || flags.suppress_nak_irq; +// bool has_read_flags = flags.read || flags.read_cont; +// if (has_write_flags && has_read_flags) { +// return kDifI2cBadArg; +// } +// // Also, read_cont requires read. +// if (flags.read_cont && !flags.read) { +// return kDifI2cBadArg; +// } + +// uint32_t fmt_byte = 0; +// fmt_byte = bitfield_field32_write(fmt_byte, I2C_FDATA_FBYTE_FIELD, byte); +// fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_START_BIT, flags.start); +// fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_STOP_BIT, flags.stop); +// fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_READ_BIT, flags.read); +// fmt_byte = +// bitfield_bit32_write(fmt_byte, I2C_FDATA_RCONT_BIT, flags.read_cont); +// fmt_byte = bitfield_bit32_write(fmt_byte, I2C_FDATA_NAKOK_BIT, +// flags.suppress_nak_irq); +// mmio_region_write32(i2c->params.base_addr, I2C_FDATA_REG_OFFSET, fmt_byte); + +// return kDifI2cOk; +// } + +// i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, +// i2c_fmt_t code, bool suppress_nak_irq) { +// if (i2c == NULL) { +// return kDifI2cBadArg; +// } + +// // Validate that `suppress_nak_irq` has not been mixed with an Rx code. +// if (suppress_nak_irq) { +// switch (code) { +// case kDifI2cFmtRx: +// case kDifI2cFmtRxContinue: +// case kDifI2cFmtRxStop: +// return kDifI2cBadArg; +// default: +// break; +// } +// } + +// // Convert the format code into flags. +// i2c_fmt_flags_t flags = {.suppress_nak_irq = suppress_nak_irq}; +// switch (code) { +// case kDifI2cFmtStart: +// flags.start = true; +// break; +// case kDifI2cFmtTx: +// break; +// case kDifI2cFmtTxStop: +// flags.stop = true; +// break; +// case kDifI2cFmtRx: +// flags.read = true; +// break; +// case kDifI2cFmtRxContinue: +// flags.read = true; +// flags.read_cont = true; +// break; +// case kDifI2cFmtRxStop: +// flags.read = true; +// flags.stop = true; +// break; +// default: +// return kDifI2cBadArg; +// } + +// return i2c_write_byte_raw(i2c, byte, flags); +// } \ No newline at end of file diff --git a/sw/device/lib/drivers/i2c/i2c_old.h b/sw/device/lib/drivers/i2c/i2c_old.h new file mode 100644 index 000000000..a2e85f498 --- /dev/null +++ b/sw/device/lib/drivers/i2c/i2c_old.h @@ -0,0 +1,610 @@ +// // Copyright lowRISC contributors. +// // Licensed under the Apache License, Version 2.0, see LICENSE for details. +// // SPDX-License-Identifier: Apache-2.0 + +// #ifndef OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ +// #define OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ + +// /** +// * @file +// * @brief I2C Device Interface Functions +// */ + +// #include +// #include + +// #include "mmio.h" + +// #ifdef __cplusplus +// extern "C" { +// #endif // __cplusplus + +// /** +// * A toggle state: enabled, or disabled. +// * +// * This enum may be used instead of a `bool` when describing an enabled/disabled +// * state. +// */ +// typedef enum i2c_toggle { +// /* +// * The "enabled" state. +// */ +// kDifI2cToggleEnabled, +// /** +// * The "disabled" state. +// */ +// kDifI2cToggleDisabled, +// } i2c_toggle_t; + +// /** +// * Represents a speed setting for an I2C component: standard, fast, and +// * fast plus, corresponding to 100 kbaud, 400 kbaud, and 1 Mbaud, +// * respectively. +// */ +// typedef enum i2c_speed { +// /** +// * Standard speed, 100 kilobaud. +// */ +// kDifI2cSpeedStandard, +// /** +// * Fast speed, 400 kilobaud. +// */ +// kDifI2cSpeedFast, +// /** +// * Fast plus speed, 1 megabaud. +// */ +// kDifI2cSpeedFastPlus, +// } i2c_speed_t; + +// /** +// * Timing configuration parameters for I2C. +// * +// * While the I2C device requires ten parameters to describe its timing +// * configuration, the degrees of freedom of those parameters is constrained to +// * the ones in this struct. +// * +// * See `i2c_compute_timing()` +// */ +// typedef struct i2c_timing_config { +// /** +// * The lowest speed at which an I2C target connected to this host will +// * operate. +// * +// * In other words, this is the maximum speed at which the host can operate +// * without going over what the target devices can handle. +// */ +// i2c_speed_t lowest_target_device_speed; +// /** +// * The period of the clock driving this device, in nanoseconds. +// * +// * This value should not be zero, since it is used as a divisor for +// * division. +// */ +// uint32_t clock_period_nanos; +// /** +// * The expected time it takes for the I2C bus signal to rise, in nanoseconds. +// * +// * This value is dependent on properties of the hardware's interconnect, and +// * not under actual firmware control. +// */ +// uint32_t sda_rise_nanos; +// /** +// * The expected time for the bus signal to fall, similar to `sda_rise_nanos`. +// */ +// uint32_t sda_fall_nanos; +// /** +// * The desired period of the SCL line, in nanoseconds. +// * +// * Normally, this should just be `1'000'000 / lowest_target_device_speed`, +// * but the period may be larger if desired. +// * +// * Setting this value to zero will result in the minimum period being used. +// */ +// uint32_t scl_period_nanos; +// } i2c_timing_config_t; + +// /** +// * Hardware instantiation parameters for I2C. +// * +// * This struct describes information about the underlying hardware that is +// * not determined until the hardware design is used as part of a top-level +// * design. +// */ +// typedef struct i2c_params { +// /** +// * The base address for the I2C hardware registers. +// */ +// mmio_region_t base_addr; +// } i2c_params_t; + +// /** +// * Runtime configuration for I2C. +// * +// * This struct describes runtime timing parameters. Computing these values is +// * somewhat complicated, so these fields should be initialized using the +// * `i2c_compute_timing()` function. A caller is, however, free to compute +// * these values themselves if they prefer, so long as the I2C spec is +// * respected. +// * +// * These values correspond to those in Table 10 of the I2C spec, and are given +// * in units of input clock cycles. +// */ +// typedef struct i2c_config { +// uint16_t scl_time_high_cycles; +// uint16_t scl_time_low_cycles; +// uint16_t rise_cycles; +// uint16_t fall_cycles; +// uint16_t start_signal_setup_cycles; +// uint16_t start_signal_hold_cycles; +// uint16_t data_signal_setup_cycles; +// uint16_t data_signal_hold_cycles; +// uint16_t stop_signal_setup_cycles; +// /** +// * This parameter is referred to in the I2C documents as the +// * "bus free time". +// */ +// uint16_t stop_signal_hold_cycles; +// } i2c_config_t; + +// /** +// * A handle to I2C. +// * +// * This type should be treated as opaque by users. +// */ +// typedef struct i2c { i2c_params_t params; } i2c_t; + +// /** +// * The result of a I2C operation. +// */ +// typedef enum i2c_result { +// /** +// * Indicates that the operation succeeded. +// */ +// kDifI2cOk = 0, +// /** +// * Indicates some unspecified failure. +// */ +// kDifI2cError = 1, +// /** +// * Indicates that some parameter passed into a function failed a +// * precondition. +// * +// * When this value is returned, no hardware operations occurred. +// */ +// kDifI2cBadArg = 2, +// } i2c_result_t; + +// /** +// * Represents an I2C-related interrupt type. +// */ +// typedef enum i2c_irq { +// /** +// * Fired when the FMT FIFO underflows its watermark. +// */ +// kDifI2cIrqFmtWatermarkUnderflow = 0, +// /** +// * Fired when the RX FIFO overflows its watermark. +// */ +// kDifI2cIrqRxWatermarkOverflow, +// /** +// * Fired when the FMT FIFO overflows. +// */ +// kDifI2cIrqFmtFifoOverflow, +// /** +// * Fired when the RX FIFO overflows. +// */ +// kDifI2cIrqRxFifoOverflow, +// /** +// * Fired when there is no ACK in response to an address or data write. +// */ +// kDifI2cIrqNak, +// /** +// * Fired when the SCL line seems to have interference. +// */ +// kDifI2cIrqSclInterference, +// /** +// * Fired when the SDA line seems to have interference. +// */ +// kDifI2cIrqSdaInterference, +// /** +// * Fired when the target stretches the clock beyond the allowed period. +// */ +// kDifI2cIrqClockStretchTimeout, +// /** +// * Fired when the target does not maintain a stable SDA line. +// */ +// kDifI2cIrqSdaUnstable, +// } i2c_irq_t; + +// /** +// * A snapshot of the entablement state of the interrupts for I2C. +// * +// * This is an opaque type, to be used with the `i2c_irq_disable_all()` and +// * `i2c_irq_restore_all()` functions. +// */ +// typedef uint32_t i2c_irq_snapshot_t; + +// /** +// * Represents a valid watermark level for one of the I2C FIFOs. +// */ +// typedef enum i2c_watermark_level { +// /** +// * A one-byte watermark. +// */ +// kDifI2cLevel1Byte = 0, +// /** +// * A four-byte watermark. +// */ +// kDifI2cLevel4Byte, +// /** +// * An eight-byte watermark. +// */ +// kDifI2cLevel8Byte, +// /** +// * A sixteen-byte watermark. +// */ +// kDifI2cLevel16Byte, +// /** +// * A thirty-byte watermark. +// * +// * Note that this watermark is only supported for RX, and not for FMT. +// */ +// kDifI2cLevel30Byte, +// } i2c_level_t; + +// /** +// * Flags for a formatted I2C byte, used by the `i2c_write_byte_raw()` +// * function. +// */ +// typedef struct i2c_fmt_flags { +// /** +// * Causes a start signal to be sent before the byte. +// * +// * If a start has been issued during the current transaction, this will issue +// * a repeated start. +// */ +// bool start; +// /** +// * Causes a stop signal to be sent after the byte. +// * +// * This flag cannot be set when both `read` and `read_cont` are set. +// */ +// bool stop; +// /** +// * Causes the byte to be interpreted as an unsigned number of bytes to read +// * from the target; 0 is interpreted as 256. +// */ +// bool read; +// /** +// * Requires `read` to be set; if so, once the final byte associated with this +// * read is received, it will be acknowledged, allowing the read operation to +// * continue. +// */ +// bool read_cont; +// /** +// * By default, the hardware expects an ACK after every byte sent, and raises +// * an exception (surfaced as the `kDifi2cIrqNak` interrupt). This flag +// * disables that behavior. +// * +// * This flag cannot be set along with `read` or `read_cont`. +// */ +// bool suppress_nak_irq; +// } i2c_fmt_flags_t; + +// /** +// * Available formatting codes for `i2c_write_byte_raw()`. +// * +// * Each code describes how to interpret the `byte` parameter, referred to below +// * as "the byte". +// * +// * It is the caller's responsibility to observe the state transitions in the +// * comments below. +// */ +// typedef enum i2c_fmt { +// /** +// * Start a transaction. This sends a START signal followed by the byte. +// * The byte sent will form (potentially part of) the target address for the +// * transaction. +// * +// * May be followed by any format code. +// */ +// kDifI2cFmtStart, +// /** +// * Transmit byte. This simply sends the byte. It may need to be used in +// * conjunction with `Start` to send a multi-byte target address. +// * +// * May be followed by any format code. +// */ +// kDifI2cFmtTx, +// /** +// * Transmit byte and stop. This sends the byte, and then sends a stop +// * signal, completing a transaction. +// * +// * Only `Start` may follow this code. +// */ +// kDifI2cFmtTxStop, +// /** +// * Request `n` bytes, where `n` is the byte interpreted as an unsigned +// * integer; a byte value of `0` will be interpreted as requesting `256` +// * bytes. This will NAK the last byte. +// * +// * Only `Start` may follow this code (this code does not stop a transaction; +// * see `RxStop`). +// */ +// kDifI2cFmtRx, +// /** +// * Request `n` bytes, same as `Rx`, but ACK the last byte so that more data +// * can be requested. +// * +// * May be followed by `RxContinue`, `Rx`, or `RxStop`. +// */ +// kDifI2cFmtRxContinue, +// /** +// * Request `n` bytes, same as `Rx`, but, after NAKing the last byte, send a +// * stop signal to end the transaction. +// * +// * Only `Start` may follow this code. +// */ +// kDifI2cFmtRxStop, +// } i2c_fmt_t; + +// /** +// * Computes timing parameters for an I2C device and stores them in `config`. +// * +// * The values returned may be tweaked by callers that require finer control over +// * some of the calculations, such as how the allocation of a lengthened SCL +// * period. +// * +// * @param timing_config Configuration values for producing timing parameters. +// * @param[out] config I2C configuration to which to apply the computed +// * parameters. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_compute_timing(i2c_timing_config_t timing_config, +// i2c_config_t *config); +// /** +// * Creates a new handle for I2C. +// * +// * This function does not actuate the hardware. +// * +// * @param params Hardware instantiation parameters. +// * @param[out] i2c Out param for the initialized handle. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_init(i2c_params_t params, i2c_t *i2c); + +// /** +// * Configures I2C with runtime information. +// * +// * This function should need to be called once for the lifetime of `handle`. +// * +// * @param i2c An I2C handle. +// * @param config Runtime configuration parameters. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_configure(const i2c_t *i2c, +// i2c_config_t config); + +// /** +// * Returns whether a particular interrupt is currently pending. +// * +// * @param i2c An I2C handle. +// * @param irq An interrupt type. +// * @param[out] is_pending Out-param for whether the interrupt is pending. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_is_pending(const i2c_t *i2c, i2c_irq_t irq, +// bool *is_pending); + +// /** +// * Acknowledges a particular interrupt, indicating to the hardware that it has +// * been successfully serviced. +// * +// * @param i2c An I2C handle. +// * @param irq An interrupt type. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_acknowledge(const i2c_t *i2c, +// i2c_irq_t irq); + +// /** +// * Checks whether a particular interrupt is currently enabled or disabled. +// * +// * @param i2c An I2C handle. +// * @param irq An interrupt type. +// * @param[out] state Out-param toggle state of the interrupt. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_get_enabled(const i2c_t *i2c, +// i2c_irq_t irq, +// i2c_toggle_t *state); + +// /** +// * Sets whether a particular interrupt is currently enabled or disabled. +// * +// * @param i2c An I2C handle. +// * @param irq An interrupt type. +// * @param state The new toggle state for the interrupt. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_set_enabled(const i2c_t *i2c, +// i2c_irq_t irq, +// i2c_toggle_t state); + +// /** +// * Forces a particular interrupt, causing it to be serviced as if hardware had +// * asserted it. +// * +// * @param i2c An I2C handle. +// * @param irq An interrupt type. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_force(const i2c_t *i2c, i2c_irq_t irq); + +// /** +// * Disables all interrupts, optionally snapshotting all toggle state for later +// * restoration. +// * +// * @param i2c An I2C handle. +// * @param[out] snapshot Out-param for the snapshot; may be `NULL`. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_disable_all(const i2c_t *i2c, +// i2c_irq_snapshot_t *snapshot); + +// /** +// * Restores interrupts from the given snapshot. +// * +// * This function can be used with `i2c_irq_disable_all()` to temporary +// * interrupt save-and-restore. +// * +// * @param i2c An I2C handle. +// * @param snapshot A snapshot to restore from. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_irq_restore_all( +// const i2c_t *i2c, const i2c_irq_snapshot_t *snapshot); + +// /** +// * Resets the state of the RX FIFO, essentially dropping all received bytes. +// * +// * @param i2c An I2c handle. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_reset_rx_fifo(const i2c_t *i2c); + +// /** +// * Resets the state of the FMT FIFO, essentially dropping all scheduled +// * operations. +// * +// * @param i2c An I2c handle. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_reset_fmt_fifo(const i2c_t *i2c); + +// /** +// * Sets watermarks for for the RX and FMT FIFOs, which will fire the respective +// * interrupts when each fifo exceeds, or falls below, the set level. +// * +// * Note that the 30-byte level is only supported for the RX FIFO: trying to use +// * it with the FMT FIFO is an error. +// * +// * @param i2c An I2C handle. +// * @param rx_level The desired watermark level for the RX FIFO. +// * @param fmt_level The desired watermark level for the FMT FIFO. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_set_watermarks(const i2c_t *i2c, +// i2c_level_t rx_level, +// i2c_level_t fmt_level); + +// /** +// * Enables or disables the "Host I2C" functionality, effectively turning the +// * I2C device on or off. This function should be called to enable the device +// * once timings, interrupts, and watermarks are all configured. +// * +// * @param i2c An I2C handle. +// * @param state The new toggle state for the host functionality. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_host_set_enabled(const i2c_t *i2c, +// i2c_toggle_t state); + +// /** +// * Enables or disables the "override mode". In override mode, software is able +// * to directly control the driven values of the SCL and SDA lines using +// * `i2c_override_drive_pins()`. +// * +// * @param i2c An I2C handle. +// * @param state The new toggle state for override mode.' +// * @return The result of the operation. +// */ +// i2c_result_t i2c_override_set_enabled(const i2c_t *i2c, +// i2c_toggle_t state); + +// /** +// * Drives the SCL and SDA pins to the given values when "override mode" is +// * enabled. +// * +// * @param i2c An I2C handle. +// * @param scl The value to drive SCL to. +// * @param sda The value to drive SDA to. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_override_drive_pins(const i2c_t *i2c, bool scl, +// bool sda); + +// /** +// * Returns oversampling of the last 16 values of the SCL and SDA pins, with the +// * zeroth bit being the most recent. +// * +// * @param i2c An I2C handle. +// * @param[out] scl_samples SCL sample bits; may be `NULL`. +// * @param[out] sda_samples SDA sample bits; may be `NULL`. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_override_sample_pins(const i2c_t *i2c, +// uint16_t *scl_samples, +// uint16_t *sda_samples); + +// /** +// * Returns the current levels, i.e., number of entries, in the FMT and RX FIFOs. +// * These values represent the number of entries pending for send by hardware, +// * and entries pending for read by software, respectively. +// * +// * @param i2c An I2C handle. +// * @param[out] fmt_fifo_level The number of unsent FMT bytes; may be `NULL`. +// * @param[out] rx_fifo_level The number of unread RX bytes; may be `NULL`. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_get_fifo_levels(const i2c_t *i2c, +// uint8_t *fmt_fifo_level, +// uint8_t *rx_fifo_level); + +// /** +// * Pops an entry (a byte) off of the RX FIFO. Passing in `NULL` to the out-param +// * will still trigger a byte pop. +// * +// * @param i2c An I2C handle. +// * @param[out] byte The popped byte; may be `NULL`. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_read_byte(const i2c_t *i2c, uint8_t *byte); + +// /** +// * Pushes a raw write entry onto the FMT FIFO, consisting of a byte and format +// * flags. This function can be called in sequence to enqueue an I2C +// * transmission. +// * +// * Callers should prefer `i2c_write_byte()` instead, since that function +// * provides clearer semantics. This function should only really be used for +// * testing or troubleshooting a device. +// * +// * @param i2c An I2C handle. +// * @param byte The value to push onto the FIFO. +// * @param flags The flags to use for this write. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_write_byte_raw(const i2c_t *i2c, uint8_t byte, +// i2c_fmt_flags_t flags); + +// /** +// * Pushes a write entry onto the FMT FIFO, consisting of a byte and a format +// * code. This function can be called in sequence to enqueue an I2C +// * transmission. +// * +// * @param i2c An I2C handle. +// * @param byte The value to push onto the FIFO. +// * @param code The code to use for this write. +// * @param suppress_nak_irq Whether to supress the NAK IRQ for this one byte. +// * May not be used in combination with `Rx` codes. +// * @return The result of the operation. +// */ +// i2c_result_t i2c_write_byte(const i2c_t *i2c, uint8_t byte, +// i2c_fmt_t code, bool suppress_nak_irq); + +// #ifdef __cplusplus +// } // extern "C" +// #endif // __cplusplus + +// #endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_ \ No newline at end of file