root/usr/src/uts/common/sys/i2c/i2c.h
/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2025 Oxide Computer Company
 */

#ifndef _SYS_I2C_I2C_H
#define _SYS_I2C_I2C_H

/*
 * General i2c definitions that should be shared between both userland and the
 * kernel. Kernel device drivers include <sys/i2c/controller.h>,
 * <sys/i2c/mux.h>, or <sys/i2c/client.h> depending on the type of device they
 * are. Userland should generally use <libi2c.h> or <sys/i2c/ui2c.h> if it's the
 * implementation of libi2c.
 */

#include <sys/stdint.h>
#include <sys/stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 * Different allowed values for I2C and SMBus speeds. In the future, we'll
 * determine how I3C based options like different supported clock rates and
 * SDR/DDR fit in here.
 */
typedef enum {
        /*
         * 100 kHz Standard operation.
         */
        I2C_SPEED_STD   = 1 << 0,
        /*
         * 400 kHz Fast-mode operation.
         */
        I2C_SPEED_FAST  = 1 << 1,
        /*
         * 1000 MHz Fast-mode plus operation.
         */
        I2C_SPEED_FPLUS = 1 << 2,
        /*
         * 3400 MHz High-speed mode operation.
         */
        I2C_SPEED_HIGH  = 1 << 3,
        /*
         * 5000 MHz Ultra-Fast mode operation.
         */
        I2C_SPEED_ULTRA = 1 << 4
} i2c_speed_t;

/*
 * Different types of controllers.
 */
typedef enum {
        I2C_CTRL_TYPE_I2C = 1,
        I2C_CTRL_TYPE_I3C,
        I2C_CTRL_TYPE_SMBUS
} i2c_ctrl_type_t;

/*
 * This represents the series of errors that can be generated by the various I2C
 * APIs. These are grouped into several different units that cover behavior
 * specific to the core (impacting everything) to properties, user-specific
 * behavior, kernel driver clients, etc.
 */
typedef enum {
        I2C_CORE_E_OK   = 0,
        /*
         * Indicates that the controller I/O failed. The reason is specified in
         * the controller error.
         */
        I2C_CORE_E_CONTROLLER,
        /*
         * The following pair indicate that a given address class or value for
         * an address within a valid address class are wrong.
         */
        I2C_CORE_E_BAD_ADDR_TYPE,
        I2C_CORE_E_BAD_ADDR,
        /*
         * This indicates that the requested address type, while valid, is not
         * supported. For example, trying to send to a 10-bit address on a
         * controller that does not support it.
         */
        I2C_CORE_E_UNSUP_ADDR_TYPE,
        /*
         * Indicates that the address in question is reserved by a corresponding
         * specification.
         */
        I2C_CORE_E_ADDR_RSVD,
        /*
         * Indicates that the address in question is already in use and
         * therefore cannot be used.
         */
        I2C_CORE_E_ADDR_IN_USE,
        /*
         * Indicates that the address could be used, but it has exceeded the
         * per-address usage count. This usually represents a place where the
         * kernel can be improved.
         */
        I2C_CORE_E_ADDR_REFCNT,
        /*
         * Indicates that there is no device with the specified address.
         */
        I2C_CORE_E_UNKNOWN_ADDR,
        /*
         * Indicates that the request type can't be translated to something the
         * underlying controller actually supports. For example, this would
         * cover trying to translate certain kinds of I2C requests to an SMBus
         * controller that supports limited types of operations or vice versa.
         */
        I2C_CORE_E_CANT_XLATE_REQ,
        /*
         * This indicates that a request had neither a read nor a write and
         * therefore cannot continue. This constraint on I/O may be lifted in
         * the future.
         */
        I2C_CORE_E_NEED_READ_OR_WRITE,
        /*
         * These indicate that invalid flags values, an invalid read length, or
         * invalid write length was encountered.
         */
        I2C_CORE_E_BAD_I2C_REQ_FLAGS,
        I2C_CORE_E_BAD_I2C_REQ_READ_LEN,
        I2C_CORE_E_BAD_I2C_REQ_WRITE_LEN,
        /*
         * These indicate similar situations in the SMBus request field.
         */
        I2C_CORE_E_BAD_SMBUS_REQ_FLAGS,
        I2C_CORE_E_BAD_SMBUS_READ_LEN,
        I2C_CORE_E_BAD_SMBUS_WRITE_LEN,
        I2C_CORE_E_BAD_SMBUS_OP,
        /*
         * Indicates that the controller does not support the requested SMBus
         * operation.
         */
        I2C_CORE_E_UNSUP_SMBUS_OP,
        /*
         * Indicates that the controller is already owned by someone and the
         * caller asked not to block.
         */
        I2C_CORE_E_LOCK_WOULD_BLOCK,
        /*
         * Indicates that the caller took a signal while waiting to acquire the
         * controller.
         */
        I2C_CORE_E_LOCK_WAIT_SIGNAL,
        /*
         * Indicates that a passed in nvlist was larger than the maximum value.
         */
        I2C_IOCTL_E_NVL_TOO_BIG = 0x1000,
        /*
         * Indicates that the nvlist was not parseable.
         */
        I2C_IOCTL_E_NVL_INVALID,
        /*
         * Indicates that the nvlist contained missing keys, keys that we don't
         * know how to handle, and keys that are the wrong type. If this gets
         * much more complex, the interface should change to the kgpio error
         * style where there is an additional nvlist there.
         */
        I2C_IOCTL_E_NVL_KEY_MISSING,
        I2C_IOCTL_E_NVL_KEY_UNKNOWN,
        I2C_IOCTL_E_NVL_KEY_BAD_TYPE,
        /*
         * Indicates that a pointer to user data inside of an ioctl (not the
         * overall ioctl itself) was not valid and generated a fault.
         */
        I2C_IOCTL_E_BAD_USER_DATA,
        /*
         * Indicates that there was no kernel memory available for the request.
         */
        I2C_IOCTL_E_NO_KERN_MEM,
        /*
         * Indicates that a string that is being used for a device name or
         * compatible array contains illegal characters or is too long.
         */
        I2C_IOCTL_E_BAD_DEV_NAME,
        /*
         * Indicates that the length of the compatible range is longer than the
         * system will allow to be set.
         */
        I2C_IOCTL_E_COMPAT_LEN_RANGE,
        /*
         * Indicates that something went wrong with trying to deal with nexus
         * related operations on a child.
         */
        I2C_IOCTL_E_NEXUS,
        /*
         * Indicates that a nexus operations was attempted while trying to hold
         * a bus lock.
         */
        I2C_IOCTL_E_NO_BUS_LOCK_NEXUS,
        /*
         * Indicates that an ioctl operation could not be started because the
         * client in question already has one in flight that requires the
         * controller lock.
         */
        I2C_IOCTL_E_IN_PROGRESS,
        /*
         * Indicates that the passed dev_info_t does not correspond to an i2c
         * device.
         */
        I2C_CLIENT_E_BAD_DIP = 0x2000,
        /*
         * Indicates that the regs[] index is invalid for the device.
         */
        I2C_CLIENT_E_BAD_REG_IDX,
        /*
         * Indicates that the specific set of flags are invalid.
         */
        I2C_CLIENT_E_BAD_CLAIM_FLAGS,
        I2C_CLIENT_E_BAD_IO_FLAGS,
        I2C_CLIENT_E_BAD_LOCK_FLAGS,
        /*
         * Indicates that the caller was interrupted while trying to get access
         * to the client for I/O.
         */
        I2C_CLIENT_E_SIGNAL,
        /*
         * Thee indicate that there are invalid values in the various register
         * access attributes.
         */
        I2C_CLIENT_E_BAD_REG_ATTR_VERS,
        I2C_CLIENT_E_BAD_REG_ATTR_FLAGS,
        I2C_CLIENT_E_BAD_REG_ATTR_RLEN,
        I2C_CLIENT_E_BAD_REG_ATTR_ALEN,
        I2C_CLIENT_E_BAD_REG_ATTR_ENDIAN,
        I2C_CLIENT_E_BAD_REG_ATTR_MAX,
        /*
         * Indicates that while the register attributes are supported, the
         * underlying hardware cannot support them.
         */
        I2C_CLIENT_E_REG_ALEN_UNSUP_BY_CTRL,
        /*
         * These indicate that I2C register operations were passed invalid
         * values or that these values would lead to an overflow.
         */
        I2C_CLIENT_E_BAD_REG_ADDR,
        I2C_CLIENT_E_BAD_REG_COUNT,
        I2C_CLIENT_E_REG_ADDR_OVERFLOW,
        I2C_CLIENT_E_REG_IO_TOO_LARGE,
        /*
         * Indicates that the size in bytes is a partial number of registers.
         */
        I2C_CLIENT_E_PARTIAL_REG,
        /*
         * Indicates that the client has tried to claim a shared address, but
         * they actually own the address directly.
         */
        I2C_CLIENT_E_CLAIM_OWNED_ADDR,
        /*
         * These two indicate that the property is unsupported by the device or
         * a property ID that is unknown by the system.
         */
        I2C_PROP_E_UNSUP = 0x3000,
        I2C_PROP_E_UNKNOWN,
        /*
         * Indicates that the property can't be written to because it is
         * read-only.
         */
        I2C_PROP_E_READ_ONLY,
        /*
         * Indicates that the property buffer is too small. The second indicates
         * that the property buffer is too large and doesn't make sense for the
         * property. The latter is only relevant when setting a property. The
         * former can be triggered in all property interfaces.
         */
        I2C_PROP_E_SMALL_BUF,
        I2C_PROP_E_TOO_BIG_BUF,
        /*
         * Indicates that the property value is invalid.
         */
        I2C_PROP_E_BAD_VAL,
        /*
         * Indicates that the controller doesn't support setting properties.
         */
        I2C_PROP_E_SET_UNSUP,
        /*
         * Indicates that the mux flag is unknown.
         */
        I2C_MUX_E_BAD_FLAG = 0x4000
} i2c_errno_t;

/*
 * These represent errors that the controller generates and/or detects while
 * attempting to perform I/O. Some controllers provide relatively rich
 * diagnostics as to what went wrong. Others, provide only generic classes of
 * errors. Try to use the most specific error possible.
 */
typedef enum {
        I2C_CTRL_E_OK = 0,
        /*
         * This is a generic class that represents something happened internal
         * to the controller. In general, this should be used sparingly and only
         * for something that only makes semantic sense on a single controller.
         * This should not be a property of something related to I2C/SMBus/I3C
         * directly.
         */
        I2C_CTRL_E_INTERNAL,
        /*
         * This is a variant of the above that indicates a driver programming
         * error violated a controller condition.
         */
        I2C_CTRL_E_DRIVER,
        /*
         * Indicates that the controller was given a type of command that it
         * does not support. For example, an SMBus 1.0 controller given an SMBus
         * 2.0 command. The framework generally is the only tying that will
         * return this error as drivers should not have to encounter them.
         */
        I2C_CTRL_E_UNSUP_CMD,
        /*
         * Indicates that prior to trying to perform the operation, the
         * controller detected that the bus was busy and after a timeout was
         * unable to get control of the bus.
         */
        I2C_CTRL_E_BUS_BUSY,
        /*
         * This error indicates that there was a no acknowledgement condition.
         * This should be used both for 7-bit and 10-bit cases. Similarly, for
         * now this should also be used for cases where no one acknowledges a
         * general call.
         */
        I2C_CTRL_E_ADDR_NACK,
        /*
         * This is a variant on the address NACK. This is used when the address
         * has been acknowledged, but subsequent data has not been. Some devices
         * may not be able to make the distinction. If they cannot, use the
         * catch-all NACK below.
         */
        I2C_CTRL_E_DATA_NACK,
        /*
         * This is a case where no NACK occurred, but the controller cannot be
         * more specific as to where in the process it occurred.
         */
        I2C_CTRL_E_NACK,
        /*
         * Indicates that the controller lost arbitration on the bus. A common
         * cause for this is a data collision.
         */
        I2C_CTRL_E_ARB_LOST,
        /*
         * Indicates that a device incorrectly issued an ACK when it was not
         * expected in the operation.
         */
        I2C_CTRL_E_BAD_ACK,
        /*
         * Indicates that the controller failed to successfully finish
         * transmitting the command within the default request timeout. This
         * results in the controller aborting the command. Callers cannot assume
         * anything about the number of bytes that made it out onto the bus.
         */
        I2C_CTRL_E_REQ_TO,
        /*
         * Indicates that an SMBus device returned a block read length that
         * could not be supported by the device or is illegal according to the
         * specification. For example, a 0 byte length or a length exceeding 32
         * bytes for an SMBus 2.0 controller.
         */
        I2C_CTRL_E_BAD_SMBUS_RLEN,
        /*
         * Indicates that an SMBus device held the clock low for too long to
         * effectively trime out the transaction. This is different from the
         * request timeout as it indicates something the target did.
         */
        I2C_CTRL_E_SMBUS_CLOCK_LOW
} i2c_ctrl_error_t;

typedef struct {
        i2c_errno_t i2c_error;
        i2c_ctrl_error_t i2c_ctrl;
} i2c_error_t;

/*
 * General maximum length for an i2c related name, including the NUL.
 */
#define I2C_NAME_MAX    32

typedef enum i2c_addr_type {
        I2C_ADDR_7BIT = 0,
        I2C_ADDR_10BIT
} i2c_addr_type_t;

/*
 * This represents the general form of our i2c addresses and what the nexus will
 * give client devices and drivers in the form of regs[].
 */
typedef struct i2c_addr {
        uint16_t ia_type;
        uint16_t ia_addr;
} i2c_addr_t;

/*
 * This indicates where an address came from.
 */
typedef enum i2c_addr_source {
        /*
         * Indicates that this came from the devices reg[] array. It was either
         * put there as part of discovering information about the system by the
         * platform (e.g. ACPI, device tree, etc.) or based on a user's specific
         * device creation.
         */
        I2C_ADDR_SOURCE_REG = 1,
        /*
         * Indicates that the address was one that the driver claimed.
         */
        I2C_ADDR_SOURCE_CLAIMED,
        /*
         * Indicates that the address was one that the driver claimed and is
         * shared across multiple instances.
         */
        I2C_ADDR_SOURCE_SHARED
} i2c_addr_source_t;

/*
 * Well-known and other special addresses. I2C reserves several addresses for
 * special purposes. SMBus has a more extensive of nominal assignments, but
 * things definitely stomp in that space.
 */
typedef enum {
        I2C_RSVD_ADDR_GEN_CALL  = 0x00,
        I2C_RSVD_ADDR_C_BUS     = 0x01,
        I2C_RSVD_ADDR_DIFF_BUS  = 0x02,
        I2C_RSVD_ADDR_FUTURE    = 0x03,
        I2C_RSVD_ADDR_HS_0      = 0x04,
        I2C_RSVD_ADDR_HS_1      = 0x05,
        I2C_RSVD_ADDR_HS_2      = 0x06,
        I2C_RSVD_ADDR_HS_3      = 0x07,
        I2C_RSVD_ADDR_10B_0     = 0x78,
        I2C_RSVD_ADDR_10B_1     = 0x79,
        I2C_RSVD_ADDR_10B_2     = 0x7a,
        I2C_RSVD_ADDR_10B_3     = 0x7b,
        I2C_RSVD_ADDR_DID_0     = 0x7c,
        I2C_RSVD_ADDR_DID_1     = 0x7d,
        I2C_RSVD_ADDR_DID_2     = 0x7e,
        I2C_RSVD_ADDR_DID_3     = 0x7f
} i2c_rsvd_addr_t;

/*
 * SMBus 2.0 controllers have a maximum byte size of 32 bytes. SMBus 3.0
 * increases that to a maximum of 255 bytes. As such we size our maximum request
 * sizes at 256 bytes in our structures.
 */
#define SMBUS_V2_MAX_BLOCK      32
#define SMBUS_V3_MAX_BLOCK      255
#define I2C_REQ_MAX             256

typedef enum smbus_op {
        SMBUS_OP_QUICK_COMMAND,
        SMBUS_OP_SEND_BYTE,
        SMBUS_OP_RECV_BYTE,
        SMBUS_OP_WRITE_BYTE,
        SMBUS_OP_READ_BYTE,
        SMBUS_OP_WRITE_WORD,
        SMBUS_OP_READ_WORD,
        SMBUS_OP_PROCESS_CALL,
        SMBUS_OP_WRITE_BLOCK,
        SMBUS_OP_READ_BLOCK,
        /* Added in SMBUS 2.0 */
        SMBUS_OP_HOST_NOTIFY,
        SMBUS_OP_BLOCK_PROCESS_CALL,
        /* Added in SMBUS 3.x */
        SMBUS_OP_WRITE_U32,
        SMBUS_OP_READ_U32,
        SMBUS_OP_WRITE_U64,
        SMBUS_OP_READ_U64,
        /* I2C Compatibility */
        SMBUS_OP_I2C_WRITE_BLOCK,
        SMBUS_OP_I2C_READ_BLOCK
} smbus_op_t;

typedef enum {
        /*
         * Indicates that regardless of whether or not interrupts are supported,
         * this request should be polled.
         */
        I2C_IO_REQ_F_POLL               = 1 << 0,
        /*
         * Indicates that this zero-byte I/O quick command is a write. If this
         * flag is not set then a quick command is a read.
         */
        I2C_IO_REQ_F_QUICK_WRITE        = 1 << 1,
} i2c_req_flags_t;

typedef struct smbus_req {
        i2c_error_t smbr_error;
        smbus_op_t smbr_op;
        i2c_req_flags_t smbr_flags;
        i2c_addr_t smbr_addr;
        uint16_t smbr_wlen;
        uint16_t smbr_rlen;
        uint8_t smbr_cmd;
        uint8_t smbr_wdata[I2C_REQ_MAX];
        uint8_t smbr_rdata[I2C_REQ_MAX];
} smbus_req_t;

typedef struct i2c_req {
        i2c_error_t ir_error;
        i2c_req_flags_t ir_flags;
        i2c_addr_t ir_addr;
        uint16_t ir_wlen;
        uint16_t ir_rlen;
        uint8_t ir_wdata[I2C_REQ_MAX];
        uint8_t ir_rdata[I2C_REQ_MAX];
} i2c_req_t;

typedef enum i2c_prop_type {
        /*
         * A property that is a standard, scalar uint32_t.
         */
        I2C_PROP_TYPE_U32,
        /*
         * A property that fits in a uint32_t, but represents a bitfield of
         * values.
         */
        I2C_PROP_TYPE_BIT32
} i2c_prop_type_t;

typedef enum i2c_prop_perm {
        I2C_PROP_PERM_RO,
        I2C_PROP_PERM_RW
} i2c_prop_perm_t;

typedef struct i2c_prop_u32_range {
        uint32_t ipur_min;
        uint32_t ipur_max;
} i2c_prop_u32_range_t;

typedef union i2c_prop_val_range {
        uint32_t ipvr_bit32;
        i2c_prop_u32_range_t ipvr_u32;
} i2c_prop_val_range_t;

typedef struct i2c_prop_range {
        uint32_t ipr_count;
        i2c_prop_type_t ipr_type;
        i2c_prop_val_range_t ipr_range[];
} i2c_prop_range_t;

/*
 * This enumeration contains a list of the properties that are supported.
 *
 * In earlier designs we had an initial set of properties for setup and hold
 * time related aspects, but controllers don't really have a uniform design
 * here. Some offer different values for RX and TX. Some offer only a single
 * value.
 */
typedef enum i2c_prop {
        /*
         * This is a uint32_t that is covered by the i2c_speed_t.
         */
        I2C_PROP_BUS_SPEED      = 0,
        /*
         * This is a uint32_t that indicates the number of ports the device has.
         */
        I2C_PROP_NPORTS,
        /*
         * This indicates the controller's type, which is covered by the
         * i2c_ctrl_type_t.
         */
        I2C_PROP_TYPE,
        /*
         * SMBus operations that are supported by the controller. This is a
         * uint32_t that covers smbus_prop_op_t.
         */
        SMBUS_PROP_SUP_OPS,
        /*
         * Maximum sizes that a controller can support for performing different
         * kinds of I/O. We expect that most I2C controllers will only support a
         *
         * single read/write buffer. For SMBus controllers, they should specify
         * the maximum block size. This covers block reads, writes, and calls.
         * If a controller supports an I2C mode with a different maximum, then
         * it can additionally specify the I2c properties.
         */
        I2C_PROP_MAX_READ,
        I2C_PROP_MAX_WRITE,
        SMBUS_PROP_MAX_BLOCK,
        /*
         * Properties for different timing parameters in I2C devices. These are
         * all uint32_t values generally in clock cycles.
         */
        I2C_PROP_STD_SCL_HIGH,
        I2C_PROP_STD_SCL_LOW,
        I2C_PROP_FAST_SCL_HIGH,
        I2C_PROP_FAST_SCL_LOW,
        I2C_PROP_HIGH_SCL_HIGH,
        I2C_PROP_HIGH_SCL_LOW
} i2c_prop_t;

typedef enum smbus_prop_op {
        SMBUS_PROP_OP_QUICK_COMMAND = 1 << SMBUS_OP_QUICK_COMMAND,
        SMBUS_PROP_OP_SEND_BYTE = 1 << SMBUS_OP_SEND_BYTE,
        SMBUS_PROP_OP_RECV_BYTE = 1 << SMBUS_OP_RECV_BYTE,
        SMBUS_PROP_OP_WRITE_BYTE = 1 << SMBUS_OP_WRITE_BYTE,
        SMBUS_PROP_OP_READ_BYTE = 1 << SMBUS_OP_READ_BYTE,
        SMBUS_PROP_OP_WRITE_WORD = 1 << SMBUS_OP_WRITE_WORD,
        SMBUS_PROP_OP_READ_WORD = 1 << SMBUS_OP_READ_WORD,
        SMBUS_PROP_OP_PROCESS_CALL = 1 << SMBUS_OP_PROCESS_CALL,
        SMBUS_PROP_OP_WRITE_BLOCK = 1 << SMBUS_OP_WRITE_BLOCK,
        SMBUS_PROP_OP_READ_BLOCK = 1 << SMBUS_OP_READ_BLOCK,
        SMBUS_PROP_OP_HOST_NOTIFY = 1 << SMBUS_OP_HOST_NOTIFY,
        SMBUS_PROP_OP_BLOCK_PROCESS_CALL = 1 << SMBUS_OP_BLOCK_PROCESS_CALL,
        SMBUS_PROP_OP_WRITE_U32 = 1 << SMBUS_OP_WRITE_U32,
        SMBUS_PROP_OP_READ_U32 = 1 << SMBUS_OP_READ_U32,
        SMBUS_PROP_OP_WRITE_U64 = 1 << SMBUS_OP_WRITE_U64,
        SMBUS_PROP_OP_READ_U64 = 1 << SMBUS_OP_READ_U64,
        SMBUS_PROP_OP_I2C_WRITE_BLOCK = 1 << SMBUS_OP_I2C_WRITE_BLOCK,
        SMBUS_PROP_OP_I2C_READ_BLOCK = 1 << SMBUS_OP_I2C_READ_BLOCK
} smbus_prop_op_t;

/*
 * The size in bytes of the maximum property name (including the NUL) and the
 * largest data size.
 */
#define I2C_PROP_NAME_MAX       32
#define I2C_PROP_SIZE_MAX       256

#ifdef __cplusplus
}
#endif

#endif /* _SYS_I2C_I2C_H */