/* * 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_CLIENT_H #define _SYS_I2C_CLIENT_H /* * I2C client device driver interface */ #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif typedef struct i2c_client i2c_client_t; typedef struct i2c_reg_hdl i2c_reg_hdl_t; typedef struct i2c_txn i2c_txn_t; /* * Initialize a new I2C client that refers to the specified dev_info_t's * register property to target a specific address. Each client is independent. * Note, these calls all assume dip is the caller's dip. If it is not the * caller's dip, then the caller must ensure that they have a hold on * dev_info_t. Currently, this is not meant to be used as an LDI style * interface. */ extern i2c_errno_t i2c_client_init(dev_info_t *, uint32_t, i2c_client_t **); extern void i2c_client_destroy(i2c_client_t *); /* * Some devices may need access to addresses that they don't exclusively own or * are missing from their reg[] property. This provides a means to get access to * this address. For an exclusive address, no one must have it already. For a * shared address, it is claimable if it is free or it is already owned by * another instance of the calling driver. Multiple drivers cannot own a shared * address. If the address is already in reg[], then this acts just like * i2c_client_init(). */ typedef enum { /* * If specified, indicates that the address is shared across multiple * devices. If not specified, this address will be unique to the device. */ I2C_CLAIM_F_SHARED = 1 << 0 } i2c_claim_flags_t; extern i2c_errno_t i2c_client_claim_addr(dev_info_t *, const i2c_addr_t *, i2c_claim_flags_t, i2c_client_t **); /* * Obtain the address that corresponds to a client. */ extern const i2c_addr_t *i2c_client_addr(i2c_client_t *); /* * There are many times when multiple operations need to be performed on the bus * in a row without intervening traffic. Control of the bus is represented by an * 'i2c_txn_t'. This is passed along to all I/O operations to indicate and track * that the bus is owned. Similarly, this also blocks other operations on the * controller / bus such as the getting and setting of properties. These holds * are not intended to be long-lived due to the nature of them. * * Operations are not required to have an explicit hold in this way. If one * passes NULL for the 'i2c_txn_t *' argument to any of the I/O functions, then * the operation will take and release a bus hold for the duration of its * operation as only one request can be active on the bus at any time. */ typedef enum { I2C_BUS_LOCK_F_NONBLOCK = 1 << 0 } i2c_bus_lock_flags_t; extern i2c_errno_t i2c_bus_lock(i2c_client_t *, i2c_bus_lock_flags_t, i2c_txn_t **); extern void i2c_bus_unlock(i2c_txn_t *); /* * Current version of the register access structure. The register access * functions are intended to be a way to more easily access standard device * registers. */ #define I2C_REG_ACC_ATTR_V0 0 typedef struct i2c_reg_acc_attr { /* * Specifies the current version of the attribute structure. This should * be I2C_REG_ACC_ATTR_V0. */ uint16_t i2cacc_version; /* * Set as zero for now. */ uint16_t i2cacc_flags; /* * Indicates the length in bytes of the addresses and registers. */ uint8_t i2cacc_addr_len; uint8_t i2cacc_reg_len; /* * These are the standard DDI swapping attributes (e.g. * DDI_NEVERSWAP_ACC). These can be left at their default for a 1 byte * value. For 2 byte values this must be set to either to the BE or LE * access. */ uint8_t i2cacc_addr_endian; uint8_t i2cacc_reg_endian; /* * The maximum valid address (inclusive) on the device. */ uint64_t i2cacc_addr_max; } i2c_reg_acc_attr_t; extern i2c_errno_t i2c_reg_handle_init(i2c_client_t *, const i2c_reg_acc_attr_t *, i2c_reg_hdl_t **); extern void i2c_reg_handle_destroy(i2c_reg_hdl_t *); /* * Read and write device registers from the requested address. This will read * the specified number of registers from the device and assumes that i2c * addresses auto-increment. The size is the size of the data buffer. It must be * an even multiple of the number of registers that one wishes to read or write. */ extern bool i2c_reg_get(i2c_txn_t *, i2c_reg_hdl_t *, uint64_t, void *, uint32_t, i2c_error_t *); extern bool i2c_reg_put(i2c_txn_t *, i2c_reg_hdl_t *, uint64_t, const void *, uint32_t, i2c_error_t *); /* * Operations to get at the maximums that a client can read, write, or otherwise * perform. Currently we have plumbed through the ones that drivers have needed * to date. As most things are encouraged to use the register interface. When * they don't they are using small 1-2 byte SMBus read and write APIs that are * easy to translate. We should feel free to add ways to get at the underlying * controller limits if we find it useful. */ extern uint32_t i2c_reg_max_read(i2c_reg_hdl_t *); extern uint32_t i2c_reg_max_write(i2c_reg_hdl_t *); /* * These are SMBus aliases for devices where that makes sense versus the general * register interface. More can be added here based on driver need. */ extern bool smbus_client_send_byte(i2c_txn_t *, i2c_client_t *, uint8_t, i2c_error_t *); extern bool smbus_client_write_u8(i2c_txn_t *, i2c_client_t *, uint8_t, uint8_t, i2c_error_t *); extern bool smbus_client_write_u16(i2c_txn_t *, i2c_client_t *, uint8_t, uint16_t, i2c_error_t *); extern bool smbus_client_recv_byte(i2c_txn_t *, i2c_client_t *, uint8_t *, i2c_error_t *); extern bool smbus_client_read_u8(i2c_txn_t *, i2c_client_t *, uint8_t, uint8_t *, i2c_error_t *); extern bool smbus_client_read_u16(i2c_txn_t *, i2c_client_t *, uint8_t, uint16_t *, i2c_error_t *); extern const char *i2c_client_errtostr(i2c_client_t *, i2c_errno_t); extern const char *i2c_client_ctrl_errtostr(i2c_client_t *, i2c_ctrl_error_t); /* * This is a conveience routine for ksensor creation. */ extern int i2c_client_ksensor_create_scalar(i2c_client_t *, uint64_t, const ksensor_ops_t *, void *, const char *, id_t *); #ifdef __cplusplus } #endif #endif /* _SYS_I2C_CLIENT_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_CONTROLLER_H #define _SYS_I2C_CONTROLLER_H /* * This file contains the definitions that I2C, I3C, and SMBus controller * drivers should use to interface with the system's i2c stack. */ #include #include #ifdef __cplusplus extern "C" { #endif /* * Current version of the interface expected by the i2c controller provider. */ #define I2C_CTRL_PROVIDER_V0 0 #define I2C_CTRL_PROVIDER I2C_CTRL_PROVIDER_V0 typedef struct i2c_prop_info i2c_prop_info_t; typedef struct i2c_ctrl_ops { bool (*i2c_port_name_f)(void *, uint32_t, char *, size_t); void (*i2c_io_smbus_f)(void *, uint32_t, smbus_req_t *); void (*i2c_io_i2c_f)(void *, uint32_t, i2c_req_t *); i2c_errno_t (*i2c_prop_info_f)(void *, i2c_prop_t, i2c_prop_info_t *); i2c_errno_t (*i2c_prop_get_f)(void *, i2c_prop_t, void *, size_t); i2c_errno_t (*i2c_prop_set_f)(void *, i2c_prop_t, const void *, size_t); } i2c_ctrl_ops_t; typedef struct i2c_ctrl_register { uint32_t ic_vers; i2c_ctrl_type_t ic_type; const char *ic_name; uint32_t ic_nports; dev_info_t *ic_dip; void *ic_drv; const i2c_ctrl_ops_t *ic_ops; } i2c_ctrl_register_t; /* * Opaque structures for registration handles. */ typedef struct i2c_ctrl_hdl i2c_ctrl_hdl_t; typedef enum { I2C_CTRL_REG_E_OK = 0, I2C_CTRL_REG_E_BAD_VERS, I2C_CTRL_REG_E_NULL_ARG, I2C_CTRL_REG_E_BAD_OPS, I2C_CTRL_REG_E_NEED_PORT_NAME_FUNC, I2C_CTRL_REG_E_NEED_PROP_GET_FUNC, I2C_CTRL_REG_E_NEED_PROP_INFO_FUNC, I2C_CTRL_REG_E_BAD_CTRL_TYPE, I2C_CTRL_REG_E_UNSUP_CTRL_TYPE, I2C_CTRL_REG_E_BAD_DIP, I2C_CTRL_REG_E_BAD_NPORTS, I2C_CTRL_REG_E_BAD_NAME, I2C_CTRL_REG_E_INTERNAL, I2C_CTRL_REG_E_BAD_MOD_TYPE, I2C_CTRL_REG_E_NEXUS, I2C_CTRL_REG_E_NOT_UNIQUE, I2C_CTRL_REG_E_REQ_PROP, I2C_CTLR_REG_E_BAD_PROP_VAL } i2c_ctrl_reg_error_t; extern void i2c_ctrl_mod_init(struct dev_ops *); extern void i2c_ctrl_mod_fini(struct dev_ops *); extern i2c_ctrl_reg_error_t i2c_ctrl_register_alloc(uint32_t, i2c_ctrl_register_t **); extern void i2c_ctrl_register_free(i2c_ctrl_register_t *); extern i2c_ctrl_reg_error_t i2c_ctrl_register(const i2c_ctrl_register_t *, i2c_ctrl_hdl_t **); extern i2c_ctrl_reg_error_t i2c_ctrl_unregister(i2c_ctrl_hdl_t *); /* * The i2c_ctrl_port_name_portno() function is the default controller port * naming function that names the port after the controller. For systems where * ther isn't a complex I/O Mux element or some other naming case, this should * be what they use. */ extern bool i2c_ctrl_port_name_portno(void *, uint32_t, char *, size_t); /* * Various functions that can be called to set the results of I/O commands. */ extern void i2c_ctrl_io_success(i2c_error_t *); extern void i2c_ctrl_io_error(i2c_error_t *, i2c_errno_t, i2c_ctrl_error_t); /* * Property information interfaces. */ extern void i2c_prop_info_set_perm(i2c_prop_info_t *, i2c_prop_perm_t); extern void i2c_prop_info_set_def_u32(i2c_prop_info_t *, uint32_t); extern void i2c_prop_info_set_range_u32(i2c_prop_info_t *, uint32_t, uint32_t); extern void i2c_prop_info_set_pos_bit32(i2c_prop_info_t *, uint32_t); /* * Ways for a driver to discover timeout parameters to use. */ typedef enum { /* * This is the amount of time that a given I/O command should take * before the request is timed out. */ I2C_CTRL_TO_IO, /* * This is the amount of time to busy-wait while polling the controller * for I/O to advance. */ I2C_CTRL_TO_POLL_CTRL, /* * This is the amount of time to wait for the bus to be clear for use * before beginning a transaction. */ I2C_CTRL_TO_BUS_ACT, /* * This is the amount of time to wait for an abort to complete. */ I2C_CTRL_TO_ABORT } i2c_ctrl_timeout_t; extern uint32_t i2c_ctrl_timeout_count(i2c_ctrl_hdl_t *, i2c_ctrl_timeout_t); extern uint32_t i2c_ctrl_timeout_delay_us(i2c_ctrl_hdl_t *, i2c_ctrl_timeout_t); #ifdef __cplusplus } #endif #endif /* _SYS_I2C_CONTROLLER_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 , * , or depending on the type of device they * are. Userland should generally use or if it's the * implementation of libi2c. */ #include #include #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 */ /* * 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_IOCTL_H #define _SYS_I2C_IOCTL_H /* * Userland i2c requests. */ #include #include #ifdef __cplusplus extern "C" { #endif /* * This is a private property to aid nexus type discovery. */ #define I2C_NEXUS_TYPE_PROP "i2c-nexus-type" #define I2C_NEXUS_TYPE_PORT "port" #define I2C_NEXUS_TYPE_CTRL "controller" #define I2C_NEXUS_TYPE_MUX "mux" /* * Base user ioctl type. */ #define UI2C_IOCTL (('i' << 24) | ('2' << 16) | ('c' << 8)) #define UI2C_IOCTL_CTRL_NPROPS (UI2C_IOCTL | 0x00) typedef struct ui2c_ctrl_nprops { uint16_t ucp_nstd; uint16_t ucp_npriv; } ui2c_ctrl_nprops_t; #define UI2C_IOCTL_CTRL_PROP_INFO (UI2C_IOCTL | 0x01) /* * Get information about a property. */ typedef struct ui2c_prop_info { i2c_error_t upi_error; uint32_t upi_prop; uint32_t upi_type; uint32_t upi_perm; uint32_t upi_def_len; uint32_t upi_pos_len; char upi_name[I2C_PROP_NAME_MAX]; uint8_t upi_def[I2C_PROP_SIZE_MAX]; uint8_t upi_pos[I2C_PROP_SIZE_MAX]; } ui2c_prop_info_t; #define UI2C_IOCTL_CTRL_PROP_GET (UI2C_IOCTL | 0x02) #define UI2C_IOCTL_CTRL_PROP_SET (UI2C_IOCTL | 0x03) /* * Get and set a property. up_size is used to indicate how many bytes in * up_value are valid on a property set and is filled in on a property get. */ typedef struct ui2c_prop { i2c_error_t up_error; uint32_t up_prop; uint32_t up_size; uint8_t up_value[I2C_PROP_SIZE_MAX]; } ui2c_prop_t; /* * These ioctls are used to add and remove an i2c device from the system. The * device will be added or removed directly under the port that the ioctl is * performed upon, this may be a controller or a mux. When adding a device, the * address must by unique within the port and any upstream ports. See * uts/common/io/i2c/i2cnex/i2cnex_addr.c for more background. * * When adding a structure, the nvlist is used. When removing it, we just pass * an address in using the second structure. The nvlist is due to the assumption * that things are going to get more complex eventually and need to include * things like properties. Either way, the intent is that this is private to * libi2c and it can change. */ #define UI2C_IOCTL_DEVICE_ADD (UI2C_IOCTL | 0x04) #define UI2C_IOCTL_DEVICE_REMOVE (UI2C_IOCTL | 0x05) #define UI2C_IOCTL_NVL_MAX_SIZE 0x4000 /* 16 KiB */ #define UI2C_IOCTL_NVL_ADDR "address" /* uint16_t */ #define UI2C_IOCTL_NVL_TYPE "type" /* uint16_t */ #define UI2C_IOCTL_NVL_NAME "name" /* string */ #define UI2C_IOCTL_NVL_COMPAT "compat" /* string[] (optional) */ #define UI2C_IOCTL_NVL_NCOMPAT_MAX 32 /* max compat[] length */ typedef struct ui2c_dev { i2c_error_t uda_error; uintptr_t uda_nvl; size_t uda_nvl_len; } ui2c_dev_add_t; typedef struct ui2c_dev_rem { i2c_error_t udr_error; i2c_addr_t udr_addr; } ui2c_dev_rem_t; /* * These commands are used to issue a request. Today, these can target any port. * If the port is downstream of a mux, the mux will be set up appropriately to * reach that port. These use the shared i2c_req_t and smbus_req_t found in * . * * In the future, we may want to have this work on a device. If we did, it would * be the same thing, but constrain the address in question to one that a * device actually had from the kernel's perspective. */ #define UI2C_IOCTL_I2C_REQ (UI2C_IOCTL | 0x06) #define UI2C_IOCTL_SMBUS_REQ (UI2C_IOCTL | 0x07) /* * Get information about a port and the addresses that exist underneath it. */ #define UI2C_IOCTL_PORT_INFO (UI2C_IOCTL | 0x08) typedef struct ui2c_port_addr_info { bool pai_downstream; uint8_t pai_ndevs; major_t pai_major; uint16_t pai_pad; } ui2c_port_addr_info_t; typedef struct ui2c_port_info { i2c_error_t upo_error; uint32_t upo_portno; uint32_t upo_ndevs; uint32_t upo_ndevs_ds; ui2c_port_addr_info_t upo_7b[1 << 7]; } ui2c_port_info_t; /* * Get information about about a device and the kinds of addresses it uses. */ #define UI2C_IOCTL_DEV_INFO (UI2C_IOCTL | 0x09) typedef enum ui2c_dev_flags { UI2C_DEV_F_MUX = 1 << 0 } ui2c_dev_flags_t; typedef struct ui2c_dev_info { i2c_error_t udi_error; i2c_addr_t udi_primary; uint32_t udi_flags; uint8_t udi_7b[1 << 7]; } ui2c_dev_info_t; /* * Get information about a mux. */ #define UI2C_IOCTL_MUX_INFO (UI2C_IOCTL | 0x0a) typedef struct ui2c_mux_info { i2c_error_t umi_error; uint32_t umi_nports; } ui2c_mux_info_t; #if defined(_KERNEL) && defined(_SYSCALL32) #pragma pack(4) typedef struct { i2c_error_t uda_error; uintptr32_t uda_nvl; size32_t uda_nvl_len; } ui2c_dev_add32_t; #pragma pack() /* pack(4) */ #endif /* _KERNEL && _SYSCALL32 */ #ifdef __cplusplus } #endif #endif /* _SYS_I2C_IOCTL_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_MUX_H #define _SYS_I2C_MUX_H /* * This header should be used by device drivers that implement an I2C mux of * some kind, regardless of whether they are controlled in-band with I2C, * out-of-band, or through some other means. */ #include #include #include #include #ifdef __cplusplus extern "C" { #endif #define I2C_MUX_PROVIDER_V0 0 #define I2C_MUX_PROVIDER I2C_MUX_PROVIDER_V0 #define I2C_MUX_PORT_ALL UINT32_MAX typedef struct i2c_mux_ops { /* * Name the specified port on the mux. */ bool (*mux_port_name_f)(void *, uint32_t, char *, size_t); /* * Enable the specified port. The flags argument is reserved for future * use. Only one segment may be enabled at a time. If one is already * active, this should replace that with the new one. */ bool (*mux_port_enable_f)(void *, i2c_txn_t *, uint32_t, uint32_t, i2c_error_t *); /* * Disable the specified port. The flags argument is reserved for future * use. Today the framework will only ever send I2C_MUX_PORT_ALL. That * is, all ports should be disabled and the device should not send * anything. */ bool (*mux_port_disable_f)(void *, i2c_txn_t *, uint32_t, uint32_t, i2c_error_t *); } i2c_mux_ops_t; typedef struct i2c_mux_regiser { uint32_t mr_vers; uint32_t mr_nports; dev_info_t *mr_dip; void *mr_drv; const i2c_mux_ops_t *mr_ops; } i2c_mux_register_t; typedef struct i2c_mux_hdl i2c_mux_hdl_t; typedef enum { I2C_MUX_REG_E_OK = 0, I2C_MUX_REG_E_BAD_VERS, I2C_MUX_REG_E_BAD_PORTS, I2C_MUX_REG_E_BAD_DEVI, I2C_MUX_REG_E_BAD_DEVI_BUS, I2C_MUX_REG_E_BAD_OPS, I2C_MUX_REG_E_UNSUP_DEVI, I2C_MUX_REG_E_EXISTS, I2C_MUX_REG_E_NEXUS, I2C_MUX_REG_E_BUSY } i2c_mux_reg_error_t; extern void i2c_mux_mod_init(struct dev_ops *); extern void i2c_mux_mod_fini(struct dev_ops *); extern i2c_mux_reg_error_t i2c_mux_register_alloc(uint32_t, i2c_mux_register_t **); extern void i2c_mux_register_free(i2c_mux_register_t *); extern i2c_mux_reg_error_t i2c_mux_register(const i2c_mux_register_t *, i2c_mux_hdl_t **); extern i2c_mux_reg_error_t i2c_mux_unregister(i2c_mux_hdl_t *); /* * The i2c_mux_port_name_portno() function is a default port naming function * that names the port after the port number. The first function is zeros based, * the second is 1s based. It is recommended that you match the device datasheet * for downstream port/bus naming. */ extern bool i2c_mux_port_name_portno(void *, uint32_t, char *, size_t); extern bool i2c_mux_port_name_portno_1s(void *, uint32_t, char *, size_t); /* * Error functions that muxes can use. */ extern bool i2c_io_error(i2c_error_t *, i2c_errno_t, i2c_ctrl_error_t); #ifdef __cplusplus } #endif #endif /* _SYS_I2C_MUX_H */