xref: /illumos-gate/usr/src/uts/common/io/i2c/nexus/i2cnex_props.c (revision 32002227574cf0a435dc03de622191ca53724f0a)
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