blob: f753efc7beaa02a09a39e394dd7f8b65ec70a4e3 [file] [log] [blame]
// 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 <a href="/hw/ip/i2c/doc/">I2C</a> Device Interface Functions
*/
#include <stdbool.h>
#include <stdint.h>
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/dif/dif_warn_unused_result.h"
// Header Extern Guard (so header can be used from C and C++)
#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 dif_i2c_toggle {
/*
* The "enabled" state.
*/
kDifI2cToggleEnabled,
/**
* The "disabled" state.
*/
kDifI2cToggleDisabled,
} dif_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 dif_i2c_speed {
/**
* Standard speed, 100 kilobaud.
*/
kDifI2cSpeedStandard,
/**
* Fast speed, 400 kilobaud.
*/
kDifI2cSpeedFast,
/**
* Fast plus speed, 1 megabaud.
*/
kDifI2cSpeedFastPlus,
} dif_i2c_speed_t;
typedef struct dif_i2c_timing_params {
} dif_i2c_timing_params_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 `dif_i2c_compute_timing()`
*/
typedef struct dif_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.
*/
dif_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;
} dif_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 dif_i2c_params {
/**
* The base address for the I2C hardware registers.
*/
mmio_region_t base_addr;
} dif_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
* `dif_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 dif_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;
} dif_i2c_config_t;
/**
* A handle to I2C.
*
* This type should be treated as opaque by users.
*/
typedef struct dif_i2c { dif_i2c_params_t params; } dif_i2c_t;
/**
* The result of a I2C operation.
*/
typedef enum dif_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,
} dif_i2c_result_t;
/**
* Represents an I2C-related interrupt type.
*/
typedef enum dif_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,
} dif_i2c_irq_t;
/**
* A snapshot of the entablement state of the interrupts for I2C.
*
* This is an opaque type, to be used with the `dif_i2c_irq_disable_all()` and
* `dif_i2c_irq_restore_all()` functions.
*/
typedef uint32_t dif_i2c_irq_snapshot_t;
/**
* Represents a valid watermark level for one of the I2C FIFOs.
*/
typedef enum dif_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,
} dif_i2c_level_t;
/**
* Flags for a formatted I2C byte, used by the `dif_i2c_write_byte_raw()`
* function.
*/
typedef struct dif_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;
} dif_i2c_fmt_flags_t;
/**
* Available formatting codes for `dif_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 dif_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,
} dif_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 config Configuration values for producing timing parameters.
* @param[out] out I2C configuration to which to apply the computed parameters.
* @return The result of the operation.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_compute_timing(dif_i2c_timing_config_t timing_config,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_init(dif_i2c_params_t params, dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_configure(const dif_i2c_t *i2c,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_is_pending(const dif_i2c_t *i2c, dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_acknowledge(const dif_i2c_t *i2c,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_get_enabled(const dif_i2c_t *i2c,
dif_i2c_irq_t irq,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_set_enabled(const dif_i2c_t *i2c,
dif_i2c_irq_t irq,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_force(const dif_i2c_t *i2c, dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_disable_all(const dif_i2c_t *i2c,
dif_i2c_irq_snapshot_t *snapshot);
/**
* Restores interrupts from the given snapshot.
*
* This function can be used with `dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_irq_restore_all(
const dif_i2c_t *i2c, const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_reset_rx_fifo(const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_reset_fmt_fifo(const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_set_watermarks(const dif_i2c_t *i2c,
dif_i2c_level_t rx_level,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_host_set_enabled(const dif_i2c_t *i2c,
dif_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
* `dif_i2c_override_drive_pins()`.
*
* @param i2c An I2C handle.
* @param state The new toggle state for override mode.'
* @return The result of the operation.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_override_set_enabled(const dif_i2c_t *i2c,
dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_override_drive_pins(const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_override_sample_pins(const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_get_fifo_levels(const dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_read_byte(const dif_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 `dif_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_write_byte_raw(const dif_i2c_t *i2c, uint8_t byte,
dif_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 supress_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.
*/
DIF_WARN_UNUSED_RESULT
dif_i2c_result_t dif_i2c_write_byte(const dif_i2c_t *i2c, uint8_t byte,
dif_i2c_fmt_t code, bool suppress_nak_irq);
#ifdef __cplusplus
} // extern "C"
#endif // __cplusplus
#endif // OPENTITAN_SW_DEVICE_LIB_DIF_DIF_I2C_H_