1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2025 Oxide Computer Company
14 */
15
16 /*
17 * This file contains logic for getting information about and manipulating
18 * properties.
19 */
20
21 #include <sys/sunddi.h>
22 #include <sys/debug.h>
23 #include <sys/sysmacros.h>
24
25 #include "i2cnex.h"
26
27 typedef struct i2c_prop_table {
28 const char *ipt_name;
29 i2c_prop_type_t ipt_type;
30 i2c_prop_type_t ipt_pos;
31 } i2c_prop_table_t;
32
33 static const i2c_prop_table_t i2c_prop_table[] = {
34 [I2C_PROP_BUS_SPEED] = {
35 .ipt_name = "speed",
36 .ipt_type = I2C_PROP_TYPE_U32,
37 .ipt_pos = I2C_PROP_TYPE_BIT32
38 },
39 [I2C_PROP_NPORTS] = {
40 .ipt_name = "ports",
41 .ipt_type = I2C_PROP_TYPE_U32,
42 .ipt_pos = I2C_PROP_TYPE_U32,
43 },
44 [I2C_PROP_TYPE] = {
45 .ipt_name = "type",
46 .ipt_type = I2C_PROP_TYPE_U32,
47 .ipt_pos = I2C_PROP_TYPE_U32,
48 },
49 [SMBUS_PROP_SUP_OPS] = {
50 .ipt_name = "smbus-ops",
51 .ipt_type = I2C_PROP_TYPE_BIT32,
52 .ipt_pos = I2C_PROP_TYPE_BIT32,
53 },
54 [I2C_PROP_MAX_READ] = {
55 .ipt_name = "i2c-max-read",
56 .ipt_type = I2C_PROP_TYPE_U32,
57 .ipt_pos = I2C_PROP_TYPE_U32,
58 },
59 [I2C_PROP_MAX_WRITE] = {
60 .ipt_name = "i2c-max-write",
61 .ipt_type = I2C_PROP_TYPE_U32,
62 .ipt_pos = I2C_PROP_TYPE_U32,
63 },
64 [SMBUS_PROP_MAX_BLOCK] = {
65 .ipt_name = "smbus-max-block",
66 .ipt_type = I2C_PROP_TYPE_U32,
67 .ipt_pos = I2C_PROP_TYPE_U32,
68 },
69 [I2C_PROP_STD_SCL_HIGH] = {
70 .ipt_name = "clock-Thigh-std",
71 .ipt_type = I2C_PROP_TYPE_U32,
72 .ipt_pos = I2C_PROP_TYPE_U32,
73 },
74 [I2C_PROP_STD_SCL_LOW] = {
75 .ipt_name = "clock-Tlow-std",
76 .ipt_type = I2C_PROP_TYPE_U32,
77 .ipt_pos = I2C_PROP_TYPE_U32,
78 },
79 [I2C_PROP_FAST_SCL_HIGH] = {
80 .ipt_name = "clock-Thigh-fast",
81 .ipt_type = I2C_PROP_TYPE_U32,
82 .ipt_pos = I2C_PROP_TYPE_U32,
83 },
84 [I2C_PROP_FAST_SCL_LOW] = {
85 .ipt_name = "clock-Tlow-fast",
86 .ipt_type = I2C_PROP_TYPE_U32,
87 .ipt_pos = I2C_PROP_TYPE_U32,
88 },
89 [I2C_PROP_HIGH_SCL_HIGH] = {
90 .ipt_name = "clock-Thigh-high",
91 .ipt_type = I2C_PROP_TYPE_U32,
92 .ipt_pos = I2C_PROP_TYPE_U32,
93 },
94 [I2C_PROP_HIGH_SCL_LOW] = {
95 .ipt_name = "clock-Tlow-high",
96 .ipt_type = I2C_PROP_TYPE_U32,
97 .ipt_pos = I2C_PROP_TYPE_U32,
98 }
99 };
100
101 uint16_t
i2c_prop_nstd(void)102 i2c_prop_nstd(void)
103 {
104 CTASSERT(ARRAY_SIZE(i2c_prop_table) < UINT16_MAX);
105 return ((uint16_t)ARRAY_SIZE(i2c_prop_table));
106 }
107
108 static const i2c_prop_table_t *
i2c_prop_find(i2c_prop_t prop)109 i2c_prop_find(i2c_prop_t prop)
110 {
111 if (prop >= ARRAY_SIZE(i2c_prop_table)) {
112 return (NULL);
113 }
114
115 VERIFY3P(i2c_prop_table[prop].ipt_name, !=, NULL);
116 return (&i2c_prop_table[prop]);
117 }
118
119 bool
i2c_prop_info(i2c_ctrl_t * ctrl,ui2c_prop_info_t * info)120 i2c_prop_info(i2c_ctrl_t *ctrl, ui2c_prop_info_t *info)
121 {
122 i2c_errno_t ret;
123 const i2c_prop_table_t *table;
124 i2c_prop_t propid = info->upi_prop;
125 i2c_prop_range_t *range;
126
127 bzero(info, sizeof (ui2c_prop_info_t));
128 info->upi_prop = propid;
129 table = i2c_prop_find(propid);
130 if (table == NULL) {
131 return (i2c_error(&info->upi_error, I2C_PROP_E_UNKNOWN, 0));
132 }
133
134 (void) strlcpy(info->upi_name, table->ipt_name,
135 sizeof (info->upi_name));
136 info->upi_type = table->ipt_type;
137 info->upi_pos_len = sizeof (i2c_prop_range_t);
138 info->upi_perm = I2C_PROP_PERM_RO;
139 range = (i2c_prop_range_t *)info->upi_pos;
140 range->ipr_type = table->ipt_pos;
141
142 switch (info->upi_prop) {
143 case I2C_PROP_NPORTS:
144 case I2C_PROP_TYPE:
145 /*
146 * These properties are handled by the framework. There's
147 * nothing to do for these as we already set them to read only
148 * just above. there is no default value and there is no
149 * possible range.
150 */
151 return (true);
152 default:
153 break;
154 }
155
156 ret = ctrl->ic_ops->i2c_prop_info_f(ctrl->ic_drv, propid,
157 (i2c_prop_info_t *)info);
158 if (ret != I2C_CORE_E_OK) {
159 return (i2c_error(&info->upi_error, ret, 0));
160 }
161
162 return (true);
163 }
164
165 bool
i2c_prop_get(i2c_ctrl_t * ctrl,i2c_prop_t prop,void * buf,uint32_t * buflen,i2c_error_t * err)166 i2c_prop_get(i2c_ctrl_t *ctrl, i2c_prop_t prop, void *buf,
167 uint32_t *buflen, i2c_error_t *err)
168 {
169 i2c_errno_t ret;
170 size_t osize = *buflen;
171 const i2c_prop_table_t *table;
172
173 table = i2c_prop_find(prop);
174 if (table == NULL) {
175 return (i2c_error(err, I2C_PROP_E_UNKNOWN, 0));
176 }
177
178 switch (table->ipt_type) {
179 case I2C_PROP_TYPE_U32:
180 case I2C_PROP_TYPE_BIT32:
181 *buflen = sizeof (uint32_t);
182 if (osize < sizeof (uint32_t)) {
183 return (i2c_error(err, I2C_PROP_E_SMALL_BUF, 0));
184 }
185 break;
186 }
187
188
189 switch (prop) {
190 case I2C_PROP_NPORTS:
191 bcopy(&ctrl->ic_nports, buf, sizeof (uint32_t));
192 return (true);
193 case I2C_PROP_TYPE:
194 bcopy(&ctrl->ic_type, buf, sizeof (uint32_t));
195 return (true);
196 default:
197 break;
198 }
199
200 ret = ctrl->ic_ops->i2c_prop_get_f(ctrl->ic_drv, prop,
201 buf, *buflen);
202 if (ret != I2C_CORE_E_OK) {
203 return (i2c_error(err, ret, 0));
204 }
205
206 return (true);
207 }
208
209 bool
i2c_prop_set(i2c_txn_t * txn,i2c_ctrl_t * ctrl,i2c_prop_t prop,const void * buf,uint32_t buflen,i2c_error_t * err)210 i2c_prop_set(i2c_txn_t *txn, i2c_ctrl_t *ctrl, i2c_prop_t prop, const void *buf,
211 uint32_t buflen, i2c_error_t *err)
212 {
213 i2c_errno_t ret;
214 const i2c_prop_table_t *table;
215
216 VERIFY(i2c_txn_held(txn));
217 VERIFY3P(txn->txn_ctrl, ==, ctrl);
218
219 table = i2c_prop_find(prop);
220 if (table == NULL) {
221 return (i2c_error(err, I2C_PROP_E_UNKNOWN, 0));
222 }
223
224 switch (table->ipt_type) {
225 case I2C_PROP_TYPE_U32:
226 case I2C_PROP_TYPE_BIT32:
227 if (buflen < sizeof (uint32_t)) {
228 return (i2c_error(err, I2C_PROP_E_SMALL_BUF, 0));
229 } else if (buflen != sizeof (uint32_t)) {
230 return (i2c_error(err, I2C_PROP_E_TOO_BIG_BUF, 0));
231 }
232 break;
233 }
234
235 if (ctrl->ic_ops->i2c_prop_set_f == NULL) {
236 return (i2c_error(err, I2C_PROP_E_SET_UNSUP, 0));
237 }
238
239 /*
240 * Enforce that specific properties are read-only.
241 */
242 switch (prop) {
243 case I2C_PROP_NPORTS:
244 case I2C_PROP_TYPE:
245 case SMBUS_PROP_SUP_OPS:
246 case I2C_PROP_MAX_READ:
247 case I2C_PROP_MAX_WRITE:
248 case SMBUS_PROP_MAX_BLOCK:
249 return (i2c_error(err, I2C_PROP_E_READ_ONLY, 0));
250 default:
251 break;
252 }
253
254 ret = ctrl->ic_ops->i2c_prop_set_f(ctrl->ic_drv, prop,
255 buf, buflen);
256 if (ret != I2C_CORE_E_OK) {
257 return (i2c_error(err, ret, 0));
258 }
259
260 return (true);
261 }
262
263 void
i2c_prop_info_set_perm(i2c_prop_info_t * pi,i2c_prop_perm_t perm)264 i2c_prop_info_set_perm(i2c_prop_info_t *pi, i2c_prop_perm_t perm)
265 {
266 ui2c_prop_info_t *info = (ui2c_prop_info_t *)pi;
267
268 VERIFY(perm == I2C_PROP_PERM_RO || perm == I2C_PROP_PERM_RW);
269 info->upi_perm = perm;
270 }
271
272 void
i2c_prop_info_set_def_u32(i2c_prop_info_t * pi,uint32_t val)273 i2c_prop_info_set_def_u32(i2c_prop_info_t *pi, uint32_t val)
274 {
275 ui2c_prop_info_t *info = (ui2c_prop_info_t *)pi;
276
277 VERIFY(info->upi_type == I2C_PROP_TYPE_U32 ||
278 info->upi_type == I2C_PROP_TYPE_BIT32);
279 info->upi_def_len = sizeof (uint32_t);
280 bcopy(&val, info->upi_def, sizeof (uint32_t));
281 }
282
283 void
i2c_prop_info_set_range_u32(i2c_prop_info_t * pi,uint32_t min,uint32_t max)284 i2c_prop_info_set_range_u32(i2c_prop_info_t *pi, uint32_t min, uint32_t max)
285 {
286 ui2c_prop_info_t *info = (ui2c_prop_info_t *)pi;
287 i2c_prop_range_t *range;
288
289 VERIFY(info->upi_type == I2C_PROP_TYPE_U32);
290 VERIFY3U(min, <=, max);
291 if (info->upi_pos_len + sizeof (i2c_prop_u32_range_t) >
292 sizeof (info->upi_pos)) {
293 return;
294 }
295
296 info->upi_pos_len += sizeof (i2c_prop_u32_range_t);
297 range = (i2c_prop_range_t *)info->upi_pos;
298 VERIFY(range->ipr_type == I2C_PROP_TYPE_U32);
299 range->ipr_range[range->ipr_count].ipvr_u32.ipur_min = min;
300 range->ipr_range[range->ipr_count].ipvr_u32.ipur_max = max;
301 range->ipr_count++;
302 }
303
304 void
i2c_prop_info_set_pos_bit32(i2c_prop_info_t * pi,uint32_t val)305 i2c_prop_info_set_pos_bit32(i2c_prop_info_t *pi, uint32_t val)
306 {
307 ui2c_prop_info_t *info = (ui2c_prop_info_t *)pi;
308 i2c_prop_range_t *range;
309
310 VERIFY(info->upi_type == I2C_PROP_TYPE_U32 ||
311 info->upi_type == I2C_PROP_TYPE_BIT32);
312 CTASSERT(sizeof (i2c_prop_range_t) + sizeof (uint32_t) <
313 sizeof (info->upi_pos));
314 info->upi_pos_len = sizeof (i2c_prop_range_t) + sizeof (uint32_t);
315 range = (i2c_prop_range_t *)info->upi_pos;
316 VERIFY(range->ipr_type == I2C_PROP_TYPE_BIT32);
317 range->ipr_count = 1;
318 range->ipr_range[0].ipvr_bit32 = val;
319 }
320
321 const char *
i2c_prop_name(i2c_prop_t prop)322 i2c_prop_name(i2c_prop_t prop)
323 {
324 const i2c_prop_table_t *table = i2c_prop_find(prop);
325 if (table == NULL) {
326 return (NULL);
327 }
328
329 return (table->ipt_name);
330 }
331