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 = <c430x_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, <c->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, <c430x_reg_attr,
628 <c->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, ®p);
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 = <c430x_mux_ops;
655
656 ret = i2c_mux_register(regp, <c->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, <c430x_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 = <c430x_dev_ops
809 };
810
811 static struct modlinkage ltc430x_modlinkage = {
812 .ml_rev = MODREV_1,
813 .ml_linkage = { <c430x_modldrv, NULL }
814 };
815
816
817 int
_init(void)818 _init(void)
819 {
820 int ret;
821
822 i2c_mux_mod_init(<c430x_dev_ops);
823 if ((ret = mod_install(<c430x_modlinkage)) != 0) {
824 i2c_mux_mod_fini(<c430x_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(<c430x_modlinkage, modinfop));
834 }
835
836 int
_fini(void)837 _fini(void)
838 {
839 int ret;
840
841 if ((ret = mod_remove(<c430x_modlinkage)) == 0) {
842 i2c_mux_mod_fini(<c430x_dev_ops);
843 }
844
845 return (ret);
846 }
847