|
root / base / usr / src / uts / common / sys / i2c / client.h
client.h C 188 lines 6.5 KB
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
 * 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 <sys/devops.h>
#include <sys/stdint.h>
#include <sys/stdbool.h>
#include <sys/i2c/i2c.h>
#include <sys/sensors.h>

#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 */