xref: /illumos-gate/usr/src/uts/common/io/i2c/mux/ltc430x/ltc430x.c (revision a3ebb524df668b0fc3a38f33d0049380f5f11ec1)
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  * Device driver for the LTC4306 4-channel mux and its 2-channel variant the
18  * LTC4305.
19  *
20  * The two devieces are generally register compatible. The main difference is
21  * that several of the bits are reserved the LTC4305 that correspond to
22  * downstream busses 3/4 (the datasheet is ones based). In addition, the
23  * LTC4306 also supports two GPIOs.
24  */
25 
26 #include <sys/modctl.h>
27 #include <sys/conf.h>
28 #include <sys/devops.h>
29 #include <sys/ddi.h>
30 #include <sys/sunddi.h>
31 #include <sys/sysmacros.h>
32 #include <sys/bitext.h>
33 
34 #include <sys/i2c/mux.h>
35 #include <sys/i2c/client.h>
36 #include <sys/gpio/kgpio_provider.h>
37 #include <sys/gpio/ltc4306.h>
38 
39 /*
40  * LTC4306 registers. Note, we've made up the register names as the datasheet
41  * just calls them registers 0-4.
42  *
43  * Register 0 is the status register. It contains information about the overall
44  * device and alerts.
45  */
46 #define	LTC430X_R_STS	0
47 #define	LTC430X_R_STS_GET_DS_CON(r)	bitx8(r, 7, 7)
48 #define	LTC430X_R_STS_GET_ALERT(r, a)	bitx8(r, 6 - a, 6 - a)
49 #define	LTC430X_R_STS_GET_FAIL_CONN(r)	bitx8(r, 2, 2)
50 #define	LTC430X_R_STS_GET_TO_LATCH(r)	bitx8(r, 1, 1)
51 #define	LTC430X_R_STS_GET_TO_CUR(r)	bitx8(r, 0, 0)
52 
53 /*
54  * Register 1 contains information about the rise time time accelerator and the
55  * GPIO input / output values.
56  */
57 #define	LTC430X_R_GPIO	1
58 #define	LTC430X_R_GPIO_SET_US_ACCEL(r, v)	bitset8(r, 7, 7, v)
59 #define	LTC430X_R_GPIO_SET_DS_ACCEL(r, v)	bitset8(r, 6, 6, v)
60 #define	LTC430X_R_GPIO_SET_OUTPUT(r, idx, v)	bitset8(r, 5 - idx, 5 - idx, v)
61 #define	LTC430X_R_GPIO_GET_OUTPUT(r, idx)	bitx8(r, 5 - idx, 5 - idx)
62 #define	LTC430X_R_GPIO_GET_INPUT(r, idx)	bitx8(r, 1 - idx, 1 - idx)
63 
64 /*
65  * Register 2 controls the GPIO input and output type and mode. In addition, it
66  * has timeout and connection controls.
67  */
68 #define	LTC430X_R_CFG	2
69 #define	LTC430X_R_CFG_SET_GPIO_DIR(r, idx, v)	bitset8(r, 7 - idx, 7 - idx, v)
70 #define	LTC430X_R_CFG_GET_GPIO_DIR(r, idx)	bitx8(r, 7 - idx, 7 - idx)
71 #define	LTC430X_R_CFG_GPIO_DIR_OUTPUT	0
72 #define	LTC430X_R_CFG_GPIO_DIR_INPUT	1
73 #define	LTC430X_R_CFG_SET_CONN_REQ(r, v)	bitset8(r, 5, 5)
74 #define	LTC430X_R_CFG_SET_GPIO_TYPE(r, idx, v)	bitset8(r, 4 - idx, 4 - idx, v)
75 #define	LTC430X_R_CFG_GET_GPIO_TYPE(r, idx)	bitx8(r, 4 - idx, 4 - idx)
76 #define	LTC430X_R_CFG_GPIO_OPEN_DRAIN	0
77 #define	LTC430X_R_CFG_GPIO_PUSH_PULL	1
78 #define	LTC430X_R_CFG_SET_MASS_WRITE(r, v)	bitset8(r, 2, 2)
79 #define	LTC430X_R_CFG_SET_TIMEOUT(r, v)		bitset8(r, 1, 0)
80 #define	LTC430X_R_CFG_TIMEOUT_NONE	0
81 #define	LTC430X_R_CFG_TIMEOUT_30MS	1
82 #define	LTC430X_R_CFG_TIMEOUT_15MS	2
83 #define	LTC430X_R_CFG_TIMEOUT_7P5MS	3
84 
85 /*
86  * Register three controls the actual switch enable and disable, as well as the
87  * current state of the bus.
88  */
89 #define	LTC430X_R_SWITCH	3
90 #define	LTC430X_R_SWITCH_SET_SWITCH(r, idx, v)	bitset8(r, 7 - idx, 7 - idx, v)
91 #define	LTC430X_R_SWITCH_DISCON		0
92 #define	LTC430X_R_SWITCH_CON		1
93 #define	LTC430X_R_SWITCH_GET_STATUS(r, idx)	bitx8(r, 3 - idx, 3 - idx)
94 
95 static const i2c_reg_acc_attr_t ltc430x_reg_attr = {
96 	.i2cacc_version = I2C_REG_ACC_ATTR_V0,
97 	.i2cacc_addr_len = 1,
98 	.i2cacc_reg_len = 1,
99 	.i2cacc_addr_max = LTC430X_R_SWITCH
100 };
101 
102 typedef struct {
103 	const char *li_name;
104 	const char *li_compat;
105 	uint32_t li_nports;
106 	uint32_t li_ngpios;
107 } ltc430x_ident_t;
108 
109 static const ltc430x_ident_t ltc430x_idents[] = {
110 	{ "ltc4305", "lltc,ltc4305", 2, 0 },
111 	{ "ltc4306", "lltc,ltc4306", 4, 2 }
112 };
113 
114 typedef struct ltc4306 {
115 	dev_info_t *ltc_dip;
116 	const ltc430x_ident_t *ltc_ident;
117 	i2c_client_t *ltc_client;
118 	i2c_reg_hdl_t *ltc_regs;
119 	i2c_mux_hdl_t *ltc_mux;
120 } ltc430x_t;
121 
122 static bool
ltc430x_port_enable(void * arg,i2c_txn_t * txn,uint32_t port,uint32_t flags,i2c_error_t * err)123 ltc430x_port_enable(void *arg, i2c_txn_t *txn, uint32_t port, uint32_t flags,
124     i2c_error_t *err)
125 {
126 	uint8_t val;
127 	ltc430x_t *ltc = arg;
128 
129 	if (flags != 0) {
130 		return (i2c_io_error(err, I2C_MUX_E_BAD_FLAG, 0));
131 	}
132 
133 	/*
134 	 * The framework promises us that we're only getting valid ports.
135 	 */
136 	VERIFY3U(port, !=, I2C_MUX_PORT_ALL);
137 	VERIFY3U(port, <, ltc->ltc_ident->li_nports);
138 	val = LTC430X_R_SWITCH_SET_SWITCH(0, port, LTC430X_R_SWITCH_CON);
139 
140 	if (!i2c_reg_put(txn, ltc->ltc_regs, LTC430X_R_SWITCH, &val,
141 	    sizeof (val), err)) {
142 		return (false);
143 	}
144 
145 	return (true);
146 }
147 
148 static bool
ltc430x_port_disable(void * arg,i2c_txn_t * txn,uint32_t port,uint32_t flags,i2c_error_t * err)149 ltc430x_port_disable(void *arg, i2c_txn_t *txn, uint32_t port, uint32_t flags,
150     i2c_error_t *err)
151 {
152 	uint8_t val = 0;
153 	ltc430x_t *ltc = arg;
154 
155 	if (flags != 0) {
156 		return (i2c_io_error(err, I2C_MUX_E_BAD_FLAG, 0));
157 	}
158 
159 	ASSERT3U(port, ==, I2C_MUX_PORT_ALL);
160 	for (uint8_t i = 0; i < ltc->ltc_ident->li_nports; i++) {
161 		val = LTC430X_R_SWITCH_SET_SWITCH(val, i,
162 		    LTC430X_R_SWITCH_DISCON);
163 	}
164 
165 	if (!i2c_reg_put(txn, ltc->ltc_regs, LTC430X_R_SWITCH, &val,
166 	    sizeof (val), err)) {
167 		return (false);
168 	}
169 
170 	return (true);
171 }
172 
173 static const i2c_mux_ops_t ltc430x_mux_ops = {
174 	.mux_port_name_f = i2c_mux_port_name_portno_1s,
175 	.mux_port_enable_f = ltc430x_port_enable,
176 	.mux_port_disable_f = ltc430x_port_disable
177 };
178 
179 /*
180  * The LTC4306 only has two GPIOs which the datasheet calls GPIO1 and GPIO2.
181  * These will map to our two GPIO IDs.
182  */
183 static const char *ltc430x_gpio_names[2] = { "GPIO1", "GPIO2" };
184 
185 static int
ltc430x_op_name2id(void * arg,const char * name,uint32_t * idp)186 ltc430x_op_name2id(void *arg, const char *name, uint32_t *idp)
187 {
188 	for (size_t i = 0; i < ARRAY_SIZE(ltc430x_gpio_names); i++) {
189 		if (strcmp(ltc430x_gpio_names[i], name) == 0) {
190 			*idp = i;
191 			return (0);
192 		}
193 	}
194 
195 	return (ENOENT);
196 }
197 
198 static int
ltc430x_gpio_regs_get(ltc430x_t * ltc,i2c_txn_t ** txnp,uint8_t * gpiop,uint8_t * cfgp)199 ltc430x_gpio_regs_get(ltc430x_t *ltc, i2c_txn_t **txnp, uint8_t *gpiop,
200     uint8_t *cfgp)
201 {
202 	i2c_error_t err;
203 
204 	err.i2c_error = i2c_bus_lock(ltc->ltc_client, 0, txnp);
205 	if (err.i2c_error == I2C_CORE_E_LOCK_WAIT_SIGNAL) {
206 		return (EINTR);
207 	} else if (err.i2c_error != I2C_CORE_E_OK) {
208 		dev_err(ltc->ltc_dip, CE_WARN, "!unexpected i2c error while "
209 		    "attempting to take bus lock: 0x%x", err.i2c_error);
210 		return (EIO);
211 	}
212 
213 	if (!i2c_reg_get(*txnp, ltc->ltc_regs, LTC430X_R_GPIO, gpiop,
214 	    sizeof (uint8_t), &err)) {
215 		i2c_bus_unlock(*txnp);
216 		*txnp = NULL;
217 		dev_err(ltc->ltc_dip, CE_WARN, "!failed to read GPIO "
218 		    "register: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
219 		return (EIO);
220 	}
221 
222 	if (!i2c_reg_get(*txnp, ltc->ltc_regs, LTC430X_R_CFG, cfgp,
223 	    sizeof (uint8_t), &err)) {
224 		i2c_bus_unlock(*txnp);
225 		*txnp = NULL;
226 		dev_err(ltc->ltc_dip, CE_WARN, "!failed to read CFG "
227 		    "register: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
228 		return (EIO);
229 	}
230 
231 	return (0);
232 }
233 
234 /*
235  * Write GPIO output register values. The hardware requires that these are a
236  * series of one byte writes. We always write the GPIO register ahead of the
237  * configuration register so that new output values are set ahead of changing
238  * whether the register is an input or output.
239  */
240 static int
ltc430x_gpio_regs_put(ltc430x_t * ltc,i2c_txn_t * txn,uint8_t gpio,uint8_t ngpio,uint8_t cfg,uint8_t ncfg)241 ltc430x_gpio_regs_put(ltc430x_t *ltc, i2c_txn_t *txn, uint8_t gpio,
242     uint8_t ngpio, uint8_t cfg, uint8_t ncfg)
243 {
244 	i2c_error_t err;
245 
246 	if (gpio != ngpio && !i2c_reg_put(txn, ltc->ltc_regs, LTC430X_R_GPIO,
247 	    &gpio, sizeof (gpio), &err)) {
248 		dev_err(ltc->ltc_dip, CE_WARN, "!failed to write GPIO "
249 		    "register: 0x%x/0x%x", err.i2c_error,
250 		    err.i2c_ctrl);
251 		return (EIO);
252 	}
253 
254 	if (cfg != ncfg && !i2c_reg_put(txn, ltc->ltc_regs, LTC430X_R_CFG,
255 	    &cfg, sizeof (cfg), &err)) {
256 		dev_err(ltc->ltc_dip, CE_WARN, "!failed to write CFG"
257 		    "register: 0x%x/0x%x", err.i2c_error,
258 		    err.i2c_ctrl);
259 		return (EIO);
260 	};
261 
262 	return (0);
263 }
264 
265 static void
ltc430x_gpio_attr_get_name(uint32_t gpio_id,nvlist_t * nvl,nvlist_t * meta,uint8_t gpio,uint8_t cfg)266 ltc430x_gpio_attr_get_name(uint32_t gpio_id, nvlist_t *nvl, nvlist_t *meta,
267     uint8_t gpio, uint8_t cfg)
268 {
269 	VERIFY3U(gpio_id, <, ARRAY_SIZE(ltc430x_gpio_names));
270 
271 	kgpio_nvl_attr_fill_str(nvl, meta, KGPIO_ATTR_NAME,
272 	    ltc430x_gpio_names[gpio_id], 0, NULL, KGPIO_PROT_RO);
273 }
274 
275 static void
ltc430x_gpio_attr_get_input(uint32_t gpio_id,nvlist_t * nvl,nvlist_t * meta,uint8_t gpio,uint8_t cfg)276 ltc430x_gpio_attr_get_input(uint32_t gpio_id, nvlist_t *nvl, nvlist_t *meta,
277     uint8_t gpio, uint8_t cfg)
278 {
279 	ltc4306_gpio_input_t input;
280 	uint32_t input_pos[2] = { LTC4306_GPIO_INPUT_LOW,
281 	    LTC4306_GPIO_INPUT_HIGH };
282 
283 	if (LTC430X_R_GPIO_GET_INPUT(gpio, gpio_id) == 0) {
284 		input = LTC4306_GPIO_INPUT_LOW;
285 	} else {
286 		input = LTC4306_GPIO_INPUT_HIGH;
287 	}
288 	kgpio_nvl_attr_fill_u32(nvl, meta, LTC4306_GPIO_ATTR_INPUT, input,
289 	    ARRAY_SIZE(input_pos), input_pos, KGPIO_PROT_RO);
290 }
291 
292 static void
ltc430x_gpio_attr_get_output(uint32_t gpio_id,nvlist_t * nvl,nvlist_t * meta,uint8_t gpio,uint8_t cfg)293 ltc430x_gpio_attr_get_output(uint32_t gpio_id, nvlist_t *nvl, nvlist_t *meta,
294     uint8_t gpio, uint8_t cfg)
295 {
296 	ltc4306_gpio_output_t output;
297 	uint32_t output_pos[3] = { LTC4306_GPIO_OUTPUT_DISABLED,
298 	    LTC4306_GPIO_OUTPUT_LOW, LTC4306_GPIO_OUTPUT_HIGH };
299 
300 	if (LTC430X_R_CFG_GET_GPIO_DIR(cfg, gpio_id) ==
301 	    LTC430X_R_CFG_GPIO_DIR_INPUT) {
302 		output = LTC4306_GPIO_OUTPUT_DISABLED;
303 	} else if (LTC430X_R_GPIO_GET_OUTPUT(gpio, gpio_id) == 0) {
304 		output = LTC4306_GPIO_OUTPUT_LOW;
305 	} else {
306 		output = LTC4306_GPIO_OUTPUT_HIGH;
307 	}
308 	kgpio_nvl_attr_fill_u32(nvl, meta, LTC4306_GPIO_ATTR_OUTPUT, output,
309 	    ARRAY_SIZE(output_pos), output_pos, KGPIO_PROT_RW);
310 }
311 
312 static bool
ltc430x_gpio_attr_set_ro(uint32_t gpio_id,nvpair_t * pair,nvlist_t * errs,uint8_t * gpiop,uint8_t * cfgp)313 ltc430x_gpio_attr_set_ro(uint32_t gpio_id, nvpair_t *pair, nvlist_t *errs,
314     uint8_t *gpiop, uint8_t *cfgp)
315 {
316 	const char *name = nvpair_name(pair);
317 	fnvlist_add_uint32(errs, name, (uint32_t)KGPIO_ATTR_ERR_ATTR_RO);
318 	return (false);
319 }
320 
321 static void
ltc430x_gpio_attr_get_output_mode(uint32_t gpio_id,nvlist_t * nvl,nvlist_t * meta,uint8_t gpio,uint8_t cfg)322 ltc430x_gpio_attr_get_output_mode(uint32_t gpio_id, nvlist_t *nvl,
323     nvlist_t *meta, uint8_t gpio, uint8_t cfg)
324 {
325 	ltc4306_gpio_output_mode_t mode;
326 	uint32_t mode_pos[2] = { LTC4306_GPIO_OUTPUT_MODE_PUSH_PULL,
327 	    LTC4306_GPIO_OUTPUT_MODE_OPEN_DRAIN };
328 
329 	if (LTC430X_R_CFG_GET_GPIO_TYPE(cfg, gpio_id) ==
330 	    LTC430X_R_CFG_GPIO_OPEN_DRAIN) {
331 		mode = LTC4306_GPIO_OUTPUT_MODE_OPEN_DRAIN;
332 	} else {
333 		mode = LTC4306_GPIO_OUTPUT_MODE_PUSH_PULL;
334 	}
335 	kgpio_nvl_attr_fill_u32(nvl, meta, LTC4306_GPIO_ATTR_OUTPUT_MODE, mode,
336 	    ARRAY_SIZE(mode_pos), mode_pos, KGPIO_PROT_RW);
337 }
338 
339 static bool
ltc430x_gpio_attr_set_output(uint32_t gpio_id,nvpair_t * pair,nvlist_t * errs,uint8_t * gpiop,uint8_t * cfgp)340 ltc430x_gpio_attr_set_output(uint32_t gpio_id, nvpair_t *pair, nvlist_t *errs,
341     uint8_t *gpiop, uint8_t *cfgp)
342 {
343 	uint32_t val;
344 
345 	if (nvpair_value_uint32(pair, &val) != 0) {
346 		fnvlist_add_uint32(errs, nvpair_name(pair),
347 		    (uint32_t)KGPIO_ATTR_ERR_BAD_TYPE);
348 		return (false);
349 	}
350 
351 	switch (val) {
352 	case LTC4306_GPIO_OUTPUT_DISABLED:
353 		*cfgp = LTC430X_R_CFG_SET_GPIO_DIR(*cfgp, gpio_id,
354 		    LTC430X_R_CFG_GPIO_DIR_INPUT);
355 		break;
356 	case LTC4306_GPIO_OUTPUT_LOW:
357 		*gpiop = LTC430X_R_GPIO_SET_OUTPUT(*gpiop, gpio_id, 0);
358 		*cfgp = LTC430X_R_CFG_SET_GPIO_DIR(*cfgp, gpio_id,
359 		    LTC430X_R_CFG_GPIO_DIR_OUTPUT);
360 		break;
361 	case LTC4306_GPIO_OUTPUT_HIGH:
362 		*gpiop = LTC430X_R_GPIO_SET_OUTPUT(*gpiop, gpio_id, 1);
363 		*cfgp = LTC430X_R_CFG_SET_GPIO_DIR(*cfgp, gpio_id,
364 		    LTC430X_R_CFG_GPIO_DIR_OUTPUT);
365 		break;
366 	default:
367 		fnvlist_add_uint32(errs, nvpair_name(pair),
368 		    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_VAL);
369 		return (false);
370 	}
371 
372 	return (true);
373 }
374 
375 static bool
ltc430x_gpio_attr_set_output_mode(uint32_t gpio_id,nvpair_t * pair,nvlist_t * errs,uint8_t * gpiop,uint8_t * cfgp)376 ltc430x_gpio_attr_set_output_mode(uint32_t gpio_id, nvpair_t *pair,
377     nvlist_t *errs, uint8_t *gpiop, uint8_t *cfgp)
378 {
379 	uint32_t val;
380 
381 	if (nvpair_value_uint32(pair, &val) != 0) {
382 		fnvlist_add_uint32(errs, nvpair_name(pair),
383 		    (uint32_t)KGPIO_ATTR_ERR_BAD_TYPE);
384 		return (false);
385 	}
386 
387 	switch (val) {
388 	case LTC4306_GPIO_OUTPUT_MODE_PUSH_PULL:
389 		*cfgp = LTC430X_R_CFG_SET_GPIO_TYPE(*cfgp, gpio_id,
390 		    LTC430X_R_CFG_GPIO_PUSH_PULL);
391 		break;
392 	case LTC4306_GPIO_OUTPUT_MODE_OPEN_DRAIN:
393 		*cfgp = LTC430X_R_CFG_SET_GPIO_TYPE(*cfgp, gpio_id,
394 		    LTC430X_R_CFG_GPIO_OPEN_DRAIN);
395 		break;
396 	default:
397 		fnvlist_add_uint32(errs, nvpair_name(pair),
398 		    (uint32_t)KGPIO_ATTR_ERR_UNKNOWN_VAL);
399 		return (false);
400 	}
401 
402 	return (true);
403 }
404 
405 typedef void (*ltc430x_gpio_attr_get_f)(uint32_t, nvlist_t *, nvlist_t *,
406     uint8_t, uint8_t);
407 typedef bool (*ltc430x_gpio_attr_set_f)(uint32_t, nvpair_t *, nvlist_t *,
408     uint8_t *, uint8_t *);
409 
410 typedef struct {
411 	const char *lgat_attr;
412 	ltc430x_gpio_attr_get_f lgat_get;
413 	ltc430x_gpio_attr_set_f lgat_set;
414 } ltc430x_gpio_attr_table_t;
415 
416 static const ltc430x_gpio_attr_table_t ltc430x_gpio_attrs[] = {
417 	{ KGPIO_ATTR_NAME, ltc430x_gpio_attr_get_name,
418 	    ltc430x_gpio_attr_set_ro },
419 	{ LTC4306_GPIO_ATTR_INPUT, ltc430x_gpio_attr_get_input,
420 	    ltc430x_gpio_attr_set_ro },
421 	{ LTC4306_GPIO_ATTR_OUTPUT, ltc430x_gpio_attr_get_output,
422 	    ltc430x_gpio_attr_set_output },
423 	{ LTC4306_GPIO_ATTR_OUTPUT_MODE, ltc430x_gpio_attr_get_output_mode,
424 	    ltc430x_gpio_attr_set_output_mode },
425 };
426 
427 static int
ltc430x_op_attr_get(void * arg,uint32_t gpio_id,nvlist_t * nvl)428 ltc430x_op_attr_get(void *arg, uint32_t gpio_id, nvlist_t *nvl)
429 {
430 	int ret;
431 	i2c_txn_t *txn;
432 	uint8_t gpio, cfg;
433 	ltc430x_t *ltc = arg;
434 
435 	if ((ret = ltc430x_gpio_regs_get(ltc, &txn, &gpio, &cfg)) != 0) {
436 		return (ret);
437 	}
438 
439 	nvlist_t *meta = fnvlist_alloc();
440 	for (size_t i = 0; i < ARRAY_SIZE(ltc430x_gpio_attrs); i++) {
441 		ltc430x_gpio_attrs[i].lgat_get(gpio_id, nvl, meta, gpio, cfg);
442 	}
443 
444 	fnvlist_add_nvlist(nvl, KGPIO_ATTR_META, meta);
445 	fnvlist_free(meta);
446 
447 	i2c_bus_unlock(txn);
448 	return (0);
449 }
450 static int
ltc430x_op_attr_set(void * arg,uint32_t gpio_id,nvlist_t * nvl,nvlist_t * errs)451 ltc430x_op_attr_set(void *arg, uint32_t gpio_id, nvlist_t *nvl, nvlist_t *errs)
452 {
453 	int ret;
454 	i2c_txn_t *txn;
455 	uint8_t gpio, cfg, ngpio, ncfg;
456 	ltc430x_t *ltc = arg;
457 	bool valid = true;
458 
459 	if ((ret = ltc430x_gpio_regs_get(ltc, &txn, &gpio, &cfg)) != 0) {
460 		return (ret);
461 	}
462 
463 	ngpio = gpio;
464 	ncfg = cfg;
465 	for (nvpair_t *nvpair = nvlist_next_nvpair(nvl, NULL); nvpair != NULL;
466 	    nvpair = nvlist_next_nvpair(nvl, nvpair)) {
467 		const char *name = nvpair_name(nvpair);
468 
469 		for (size_t i = 0; i < ARRAY_SIZE(ltc430x_gpio_attrs); i++) {
470 			if (strcmp(name, ltc430x_gpio_attrs[i].lgat_attr) != 0)
471 				continue;
472 
473 			if (!ltc430x_gpio_attrs[i].lgat_set(gpio_id, nvpair,
474 			    errs, &ngpio, &ncfg)) {
475 				valid = false;
476 			}
477 		}
478 	}
479 
480 	if (valid) {
481 		ret = ltc430x_gpio_regs_put(ltc, txn, gpio, ngpio, cfg, ncfg);
482 	} else {
483 		ret = EINVAL;
484 	}
485 	i2c_bus_unlock(txn);
486 	return (ret);
487 }
488 
489 static int
ltc430x_op_cap(void * arg,uint32_t gpio_id,dpio_caps_t * caps)490 ltc430x_op_cap(void *arg, uint32_t gpio_id, dpio_caps_t *caps)
491 {
492 	return (DPIO_C_READ | DPIO_C_WRITE);
493 }
494 
495 static int
ltc430x_op_dpio_input(void * arg,uint32_t gpio_id,dpio_input_t * input)496 ltc430x_op_dpio_input(void *arg, uint32_t gpio_id, dpio_input_t *input)
497 {
498 	i2c_error_t err;
499 	uint8_t val;
500 	ltc430x_t *ltc = arg;
501 
502 	if (!i2c_reg_get(NULL, ltc->ltc_regs, LTC430X_R_GPIO, &val,
503 	    sizeof (val), &err)) {
504 		dev_err(ltc->ltc_dip, CE_WARN, "!failed to read GPIO and CFG "
505 		    "registers: 0x%x/0x%x", err.i2c_error, err.i2c_ctrl);
506 		return (EIO);
507 	}
508 
509 	if (LTC430X_R_GPIO_GET_INPUT(val, gpio_id) == 0) {
510 		*input = DPIO_INPUT_LOW;
511 	} else {
512 		*input = DPIO_INPUT_HIGH;
513 	}
514 
515 	return (0);
516 }
517 
518 static int
ltc430x_op_dpio_output_state(void * arg,uint32_t gpio_id,dpio_output_t * output)519 ltc430x_op_dpio_output_state(void *arg, uint32_t gpio_id,
520     dpio_output_t *output)
521 {
522 	int ret;
523 	i2c_txn_t *txn;
524 	uint8_t gpio, cfg;
525 	ltc430x_t *ltc = arg;
526 
527 	if ((ret = ltc430x_gpio_regs_get(ltc, &txn, &gpio, &cfg)) != 0) {
528 		return (ret);
529 	}
530 
531 	if (LTC430X_R_CFG_GET_GPIO_DIR(cfg, gpio_id) ==
532 	    LTC430X_R_CFG_GPIO_DIR_INPUT) {
533 		*output = DPIO_OUTPUT_DISABLE;
534 	} else if (LTC430X_R_GPIO_GET_OUTPUT(gpio, gpio_id) == 0) {
535 		*output = DPIO_OUTPUT_LOW;
536 	} else {
537 		*output = DPIO_OUTPUT_HIGH;
538 	}
539 
540 	i2c_bus_unlock(txn);
541 	return (0);
542 }
543 
544 static int
ltc430x_op_dpio_output(void * arg,uint32_t gpio_id,dpio_output_t output)545 ltc430x_op_dpio_output(void *arg, uint32_t gpio_id, dpio_output_t output)
546 {
547 	int ret;
548 	i2c_txn_t *txn;
549 	uint8_t gpio, cfg, ngpio, ncfg;
550 	ltc430x_t *ltc = arg;
551 
552 	if ((ret = ltc430x_gpio_regs_get(ltc, &txn, &gpio, &cfg)) != 0) {
553 		return (ret);
554 	}
555 
556 	switch (output) {
557 	case DPIO_OUTPUT_LOW:
558 		ngpio = LTC430X_R_GPIO_SET_OUTPUT(gpio, gpio_id, 0);
559 		ncfg = LTC430X_R_CFG_SET_GPIO_DIR(cfg, gpio_id,
560 		    LTC430X_R_CFG_GPIO_DIR_OUTPUT);
561 		break;
562 	case DPIO_OUTPUT_HIGH:
563 		ngpio = LTC430X_R_GPIO_SET_OUTPUT(gpio, gpio_id, 1);
564 		ncfg = LTC430X_R_CFG_SET_GPIO_DIR(cfg, gpio_id,
565 		    LTC430X_R_CFG_GPIO_DIR_OUTPUT);
566 		break;
567 	case DPIO_OUTPUT_DISABLE:
568 		ngpio = gpio;
569 		ncfg = LTC430X_R_CFG_SET_GPIO_DIR(cfg, gpio_id,
570 		    LTC430X_R_CFG_GPIO_DIR_INPUT);
571 		break;
572 	default:
573 		ret = EINVAL;
574 		goto out;
575 	}
576 
577 	ret = ltc430x_gpio_regs_put(ltc, txn, gpio, ngpio, cfg, ncfg);
578 out:
579 	i2c_bus_unlock(txn);
580 	return (ret);
581 }
582 
583 static const kgpio_ops_t ltc430x_gpio_ops = {
584 	.kgo_name2id = ltc430x_op_name2id,
585 	.kgo_get = ltc430x_op_attr_get,
586 	.kgo_set = ltc430x_op_attr_set,
587 	.kgo_cap = ltc430x_op_cap,
588 	.kgo_input = ltc430x_op_dpio_input,
589 	.kgo_output_state = ltc430x_op_dpio_output_state,
590 	.kgo_output = ltc430x_op_dpio_output
591 };
592 
593 static bool
ltc430x_identify(ltc430x_t * ltc)594 ltc430x_identify(ltc430x_t *ltc)
595 {
596 	const char *bind = ddi_binding_name(ltc->ltc_dip);
597 	const char *name = ddi_node_name(ltc->ltc_dip);
598 
599 	for (size_t i = 0; i < ARRAY_SIZE(ltc430x_idents); i++) {
600 		if (strcmp(bind, ltc430x_idents[i].li_name) == 0 ||
601 		    strcmp(bind, ltc430x_idents[i].li_compat) == 0 ||
602 		    strcmp(name, ltc430x_idents[i].li_name) == 0 ||
603 		    strcmp(name, ltc430x_idents[i].li_compat) == 0) {
604 			ltc->ltc_ident = &ltc430x_idents[i];
605 			return (true);
606 		}
607 	}
608 
609 
610 	dev_err(ltc->ltc_dip, CE_WARN, "failed to match against node name %s "
611 	    "and binding name %s", name, bind);
612 	return (false);
613 }
614 
615 static bool
ltc430x_i2c_init(ltc430x_t * ltc)616 ltc430x_i2c_init(ltc430x_t *ltc)
617 {
618 	i2c_errno_t err;
619 
620 	if ((err = i2c_client_init(ltc->ltc_dip, 0, &ltc->ltc_client)) !=
621 	    I2C_CORE_E_OK) {
622 		dev_err(ltc->ltc_dip, CE_WARN, "failed to create i2c client: "
623 		    "0x%x", err);
624 		return (false);
625 	}
626 
627 	if ((err = i2c_reg_handle_init(ltc->ltc_client, &ltc430x_reg_attr,
628 	    &ltc->ltc_regs)) != I2C_CORE_E_OK) {
629 		dev_err(ltc->ltc_dip, CE_WARN, "failed to create register "
630 		    "handle: %s (0x%x)", i2c_client_errtostr(ltc->ltc_client,
631 		    err), err);
632 		return (false);
633 	}
634 
635 	return (true);
636 }
637 
638 static bool
ltc430x_mux_init(ltc430x_t * ltc)639 ltc430x_mux_init(ltc430x_t *ltc)
640 {
641 	i2c_mux_reg_error_t ret;
642 	i2c_mux_register_t *regp;
643 
644 	ret = i2c_mux_register_alloc(I2C_MUX_PROVIDER, &regp);
645 	if (ret != I2C_MUX_REG_E_OK) {
646 		dev_err(ltc->ltc_dip, CE_WARN, "failed to get mux reister "
647 		    "structure: 0x%x", ret);
648 		return (false);
649 	}
650 
651 	regp->mr_nports = ltc->ltc_ident->li_nports;
652 	regp->mr_dip = ltc->ltc_dip;
653 	regp->mr_drv = ltc;
654 	regp->mr_ops = &ltc430x_mux_ops;
655 
656 	ret = i2c_mux_register(regp, &ltc->ltc_mux);
657 	i2c_mux_register_free(regp);
658 	if (ret != I2C_MUX_REG_E_OK) {
659 		dev_err(ltc->ltc_dip, CE_WARN, "failed to register with i2c "
660 		    "mux framework: 0x%x", ret);
661 		return (false);
662 	}
663 
664 	return (true);
665 }
666 
667 static bool
ltc430x_gpio_fini(ltc430x_t * ltc)668 ltc430x_gpio_fini(ltc430x_t *ltc)
669 {
670 	int ret;
671 
672 	if (ltc->ltc_ident->li_ngpios == 0) {
673 		return (true);
674 	}
675 
676 	if ((ret = kgpio_unregister(ltc->ltc_dip)) != 0) {
677 		dev_err(ltc->ltc_dip, CE_WARN, "failed to unregister from "
678 		    "gpio framework: 0x%x", ret);
679 		return (false);
680 	}
681 
682 	return (true);
683 }
684 
685 static bool
ltc430x_gpio_init(ltc430x_t * ltc)686 ltc430x_gpio_init(ltc430x_t *ltc)
687 {
688 	int ret;
689 
690 	/*
691 	 * There's nothing to register with if this doesn't actually exist.
692 	 */
693 	if (ltc->ltc_ident->li_ngpios == 0) {
694 		return (true);
695 	}
696 
697 	if ((ret = kgpio_register(ltc->ltc_dip, &ltc430x_gpio_ops, ltc,
698 	    ltc->ltc_ident->li_ngpios)) != 0) {
699 		dev_err(ltc->ltc_dip, CE_WARN, "failed to register with gpio "
700 		    "framework: 0x%x", ret);
701 		return (false);
702 	}
703 
704 	return (true);
705 }
706 
707 static void
ltc430x_cleanup(ltc430x_t * ltc)708 ltc430x_cleanup(ltc430x_t *ltc)
709 {
710 	i2c_reg_handle_destroy(ltc->ltc_regs);
711 	i2c_client_destroy(ltc->ltc_client);
712 	ddi_set_driver_private(ltc->ltc_dip, NULL);
713 	ltc->ltc_dip = NULL;
714 	kmem_free(ltc, sizeof (ltc430x_t));
715 }
716 
717 int
ltc430x_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)718 ltc430x_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
719 {
720 	ltc430x_t *ltc;
721 
722 	switch (cmd) {
723 	case DDI_ATTACH:
724 		break;
725 	case DDI_RESUME:
726 		return (DDI_SUCCESS);
727 	default:
728 		return (DDI_FAILURE);
729 	}
730 
731 	ltc = kmem_zalloc(sizeof (ltc430x_t), KM_SLEEP);
732 	ltc->ltc_dip = dip;
733 	ddi_set_driver_private(dip, ltc);
734 
735 	if (!ltc430x_identify(ltc))
736 		goto cleanup;
737 
738 	if (!ltc430x_i2c_init(ltc))
739 		goto cleanup;
740 
741 	if (!ltc430x_mux_init(ltc))
742 		goto cleanup;
743 
744 	if (!ltc430x_gpio_init(ltc))
745 		goto cleanup;
746 
747 	return (DDI_SUCCESS);
748 
749 cleanup:
750 	ltc430x_cleanup(ltc);
751 	return (DDI_FAILURE);
752 }
753 
754 int
ltc430x_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)755 ltc430x_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
756 {
757 	ltc430x_t *ltc;
758 
759 	switch (cmd) {
760 	case DDI_DETACH:
761 		break;
762 	case DDI_SUSPEND:
763 		return (DDI_SUCCESS);
764 	default:
765 		return (DDI_FAILURE);
766 	}
767 
768 	ltc = ddi_get_driver_private(dip);
769 	if (ltc == NULL) {
770 		dev_err(dip, CE_WARN, "asked to detach, but missing private "
771 		    "data");
772 		return (DDI_FAILURE);
773 	}
774 	VERIFY3P(ltc->ltc_dip, ==, dip);
775 
776 	if (!ltc430x_gpio_fini(ltc)) {
777 		return (DDI_FAILURE);
778 	}
779 
780 	if (i2c_mux_unregister(ltc->ltc_mux) != I2C_MUX_REG_E_OK) {
781 		/*
782 		 * We're not actually detaching, so try to register with the
783 		 * kgpio provider again.
784 		 */
785 		(void) ltc430x_gpio_init(ltc);
786 		return (DDI_FAILURE);
787 	}
788 
789 	ltc430x_cleanup(ltc);
790 
791 	return (DDI_SUCCESS);
792 }
793 
794 static struct dev_ops ltc430x_dev_ops = {
795 	.devo_rev = DEVO_REV,
796 	.devo_refcnt = 0,
797 	.devo_identify = nulldev,
798 	.devo_probe = nulldev,
799 	.devo_attach = ltc430x_attach,
800 	.devo_detach = ltc430x_detach,
801 	.devo_reset = nodev,
802 	.devo_quiesce = ddi_quiesce_not_needed
803 };
804 
805 static struct modldrv ltc430x_modldrv = {
806 	.drv_modops = &mod_driverops,
807 	.drv_linkinfo = "LTC4305/6 I2C Mux",
808 	.drv_dev_ops = &ltc430x_dev_ops
809 };
810 
811 static struct modlinkage ltc430x_modlinkage = {
812 	.ml_rev = MODREV_1,
813 	.ml_linkage = { &ltc430x_modldrv, NULL }
814 };
815 
816 
817 int
_init(void)818 _init(void)
819 {
820 	int ret;
821 
822 	i2c_mux_mod_init(&ltc430x_dev_ops);
823 	if ((ret = mod_install(&ltc430x_modlinkage)) != 0) {
824 		i2c_mux_mod_fini(&ltc430x_dev_ops);
825 	}
826 
827 	return (ret);
828 }
829 
830 int
_info(struct modinfo * modinfop)831 _info(struct modinfo *modinfop)
832 {
833 	return (mod_info(&ltc430x_modlinkage, modinfop));
834 }
835 
836 int
_fini(void)837 _fini(void)
838 {
839 	int ret;
840 
841 	if ((ret = mod_remove(&ltc430x_modlinkage)) == 0) {
842 		i2c_mux_mod_fini(&ltc430x_dev_ops);
843 	}
844 
845 	return (ret);
846 }
847