umcpmio_subr.c revision 1.3 1 /* $NetBSD: umcpmio_subr.c,v 1.3 2025/03/25 20:38:27 riastradh Exp $ */
2
3 /*
4 * Copyright (c) 2024 Brad Spencer <brad (at) anduin.eldar.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/cdefs.h>
20 __KERNEL_RCSID(0, "$NetBSD: umcpmio_subr.c,v 1.3 2025/03/25 20:38:27 riastradh Exp $");
21
22 #ifdef _KERNEL_OPT
23 #include "opt_usb.h"
24 #endif
25
26 #include <sys/param.h>
27 #include <sys/types.h>
28
29 #include <sys/conf.h>
30 #include <sys/device.h>
31 #include <sys/file.h>
32 #include <sys/kauth.h>
33 #include <sys/kernel.h>
34 #include <sys/kmem.h>
35 #include <sys/lwp.h>
36 #include <sys/sysctl.h>
37 #include <sys/systm.h>
38 #include <sys/tty.h>
39 #include <sys/vnode.h>
40
41 #include <dev/hid/hid.h>
42
43 #include <dev/usb/uhidev.h>
44 #include <dev/usb/usb.h>
45 #include <dev/usb/usbdevs.h>
46 #include <dev/usb/usbdi.h>
47 #include <dev/usb/usbdi_util.h>
48 #include <dev/usb/usbhid.h>
49
50 #include <dev/usb/umcpmio.h>
51 #include <dev/usb/umcpmio_subr.h>
52 #include <dev/usb/umcpmio_hid_reports.h>
53
54 int umcpmio_send_report(struct umcpmio_softc *, uint8_t *, size_t, uint8_t *,
55 int);
56
57 #define UMCPMIO_DEBUG 1
58 #ifdef UMCPMIO_DEBUG
59 #define DPRINTF(x) do { if (umcpmiodebug) printf x; } while (0)
60 #define DPRINTFN(n, x) do { if (umcpmiodebug > (n)) printf x; } while (0)
61 extern int umcpmiodebug;
62 #else
63 #define DPRINTF(x) __nothing
64 #define DPRINTFN(n,x) __nothing
65 #endif
66
67 /* Handy functions that do a bunch of things for the main driver code */
68
69 int
70 umcpmio_get_status(struct umcpmio_softc *sc,
71 struct mcp2221_status_res *res, bool takemutex)
72 {
73 struct mcp2221_status_req req;
74 int err = 0;
75
76 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
77 req.cmd = MCP2221_CMD_STATUS;
78
79 if (takemutex)
80 mutex_enter(&sc->sc_action_mutex);
81 err = umcpmio_send_report(sc,
82 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
83 (uint8_t *)res, sc->sc_cv_wait);
84 if (takemutex)
85 mutex_exit(&sc->sc_action_mutex);
86
87 return err;
88 }
89
90 void
91 umcpmio_set_i2c_speed(struct mcp2221_status_req *req, int flags)
92 {
93 int i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
94
95 if (flags & I2C_F_SPEED)
96 i2cbaud = 400000;
97
98 req->set_i2c_speed = MCP2221_I2C_SET_SPEED;
99 if (i2cbaud <= 0)
100 i2cbaud = MCP2221_DEFAULT_I2C_SPEED;
101
102 /*
103 * Everyone and their brother seems to store the I2C divider like this,
104 * so do likewise
105 */
106 req->i2c_clock_divider = (MCP2221_INTERNAL_CLOCK / i2cbaud) - 3;
107 }
108
109 int
110 umcpmio_put_status(struct umcpmio_softc *sc,
111 struct mcp2221_status_req *req, struct mcp2221_status_res *res,
112 bool takemutex)
113 {
114 int err = 0;
115
116 req->cmd = MCP2221_CMD_STATUS;
117
118 if (takemutex)
119 mutex_enter(&sc->sc_action_mutex);
120 err = umcpmio_send_report(sc,
121 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
122 (uint8_t *)res, sc->sc_cv_wait);
123 if (takemutex)
124 mutex_exit(&sc->sc_action_mutex);
125
126 return err;
127 }
128
129 int
130 umcpmio_set_i2c_speed_one(struct umcpmio_softc *sc,
131 int flags, bool takemutex)
132 {
133 int err = 0;
134 struct mcp2221_status_req req;
135 struct mcp2221_status_res res;
136
137 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
138 umcpmio_set_i2c_speed(&req, flags);
139 err = umcpmio_put_status(sc, &req, &res, takemutex);
140 if (err)
141 goto out;
142 if (res.set_i2c_speed == MCP2221_I2C_SPEED_BUSY)
143 err = EBUSY;
144 out:
145 return err;
146 }
147
148 int
149 umcpmio_get_sram(struct umcpmio_softc *sc,
150 struct mcp2221_get_sram_res *res, bool takemutex)
151 {
152 struct mcp2221_get_sram_req req;
153 int err = 0;
154
155 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
156 req.cmd = MCP2221_CMD_GET_SRAM;
157
158 if (takemutex)
159 mutex_enter(&sc->sc_action_mutex);
160 err = umcpmio_send_report(sc,
161 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
162 (uint8_t *)res, sc->sc_cv_wait);
163 if (takemutex)
164 mutex_exit(&sc->sc_action_mutex);
165
166 return err;
167 }
168
169 int
170 umcpmio_put_sram(struct umcpmio_softc *sc,
171 struct mcp2221_set_sram_req *req, struct mcp2221_set_sram_res *res,
172 bool takemutex)
173 {
174 int err = 0;
175
176 req->cmd = MCP2221_CMD_SET_SRAM;
177
178 if (takemutex)
179 mutex_enter(&sc->sc_action_mutex);
180 err = umcpmio_send_report(sc,
181 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
182 (uint8_t *)res, sc->sc_cv_wait);
183 if (takemutex)
184 mutex_exit(&sc->sc_action_mutex);
185
186 return err;
187 }
188
189 /* We call the dedicated function ALT3 everywhere */
190
191 uint32_t
192 umcpmio_sram_gpio_to_flags(uint8_t gp_setting)
193 {
194 uint32_t r = 0;
195
196 switch (gp_setting & MCP2221_SRAM_PIN_TYPE_MASK) {
197 case MCP2221_SRAM_PIN_IS_DED:
198 r |= GPIO_PIN_ALT3;
199 break;
200 case MCP2221_SRAM_PIN_IS_ALT0:
201 r |= GPIO_PIN_ALT0;
202 break;
203 case MCP2221_SRAM_PIN_IS_ALT1:
204 r |= GPIO_PIN_ALT1;
205 break;
206 case MCP2221_SRAM_PIN_IS_ALT2:
207 r |= GPIO_PIN_ALT2;
208 break;
209 case MCP2221_SRAM_PIN_IS_GPIO:
210 default:
211 if ((gp_setting & MCP2221_SRAM_GPIO_TYPE_MASK) ==
212 MCP2221_SRAM_GPIO_INPUT)
213 r |= GPIO_PIN_INPUT;
214 else
215 r |= GPIO_PIN_OUTPUT;
216 break;
217 }
218
219 return r;
220 }
221
222 void
223 umcpmio_set_gpio_value_sram(struct mcp2221_set_sram_req *req, int pin,
224 bool value)
225 {
226 uint8_t *alter = NULL;
227 uint8_t *newvalue = NULL;
228
229 if (pin >= 0 && pin < MCP2221_NPINS) {
230 switch (pin) {
231 case 0:
232 alter = &req->alter_gpio_config;
233 newvalue = &req->gp0_settings;
234 break;
235 case 1:
236 alter = &req->alter_gpio_config;
237 newvalue = &req->gp1_settings;
238 break;
239 case 2:
240 alter = &req->alter_gpio_config;
241 newvalue = &req->gp2_settings;
242 break;
243 case 3:
244 alter = &req->alter_gpio_config;
245 newvalue = &req->gp3_settings;
246 break;
247 default:
248 break;
249 }
250
251 if (alter != NULL) {
252 *alter = MCP2221_SRAM_ALTER_GPIO;
253 if (value)
254 *newvalue |= MCP2221_SRAM_GPIO_HIGH;
255 else
256 *newvalue &= ~MCP2221_SRAM_GPIO_HIGH;
257 }
258 }
259 }
260
261 void
262 umcpmio_set_gpio_dir_sram(struct mcp2221_set_sram_req *req, int pin, int flags)
263 {
264 uint8_t *alter = NULL;
265 uint8_t *newvalue = NULL;
266
267 if (pin >= 0 && pin < MCP2221_NPINS) {
268 switch (pin) {
269 case 0:
270 alter = &req->alter_gpio_config;
271 newvalue = &req->gp0_settings;
272 break;
273 case 1:
274 alter = &req->alter_gpio_config;
275 newvalue = &req->gp1_settings;
276 break;
277 case 2:
278 alter = &req->alter_gpio_config;
279 newvalue = &req->gp2_settings;
280 break;
281 case 3:
282 alter = &req->alter_gpio_config;
283 newvalue = &req->gp3_settings;
284 break;
285 default:
286 break;
287 }
288
289 if (alter != NULL) {
290 *alter = MCP2221_SRAM_ALTER_GPIO;
291 if (flags & GPIO_PIN_INPUT)
292 *newvalue |= MCP2221_SRAM_GPIO_INPUT;
293 else
294 *newvalue &= ~MCP2221_SRAM_GPIO_INPUT;
295 }
296 }
297 }
298
299 void
300 umcpmio_set_gpio_designation_sram(struct mcp2221_set_sram_req *req, int pin,
301 int flags)
302 {
303 uint8_t *alter = NULL;
304 uint8_t *newvalue = NULL;
305 uint32_t altmask =
306 GPIO_PIN_ALT0 | GPIO_PIN_ALT1 | GPIO_PIN_ALT2 | GPIO_PIN_ALT3;
307
308 if (pin >= 0 && pin < MCP2221_NPINS) {
309 switch (pin) {
310 case 0:
311 alter = &req->alter_gpio_config;
312 newvalue = &req->gp0_settings;
313 break;
314 case 1:
315 alter = &req->alter_gpio_config;
316 newvalue = &req->gp1_settings;
317 break;
318 case 2:
319 alter = &req->alter_gpio_config;
320 newvalue = &req->gp2_settings;
321 break;
322 case 3:
323 alter = &req->alter_gpio_config;
324 newvalue = &req->gp3_settings;
325 break;
326 default:
327 break;
328 }
329
330 if (alter != NULL) {
331 int nv = *newvalue;
332
333 *alter = MCP2221_SRAM_ALTER_GPIO;
334 nv &= 0xF8;
335
336 if (flags & (GPIO_PIN_OUTPUT|GPIO_PIN_INPUT)) {
337 nv |= MCP2221_SRAM_PIN_IS_GPIO;
338 } else {
339 switch (flags & altmask) {
340 case GPIO_PIN_ALT0:
341 nv |= MCP2221_SRAM_PIN_IS_ALT0;
342 break;
343 case GPIO_PIN_ALT1:
344 nv |= MCP2221_SRAM_PIN_IS_ALT1;
345 break;
346 case GPIO_PIN_ALT2:
347 nv |= MCP2221_SRAM_PIN_IS_ALT2;
348 break;
349 /*
350 * ALT3 will always be used as
351 * the dedicated function
352 * specific to the pin. Not
353 * all of the pins will have
354 * the alt functions below #3.
355 */
356 case GPIO_PIN_ALT3:
357 nv |= MCP2221_SRAM_PIN_IS_DED;
358 break;
359 default:
360 break;
361 }
362 }
363 *newvalue = nv;
364 }
365 }
366 }
367
368 void
369 umcpmio_set_gpio_irq_sram(struct mcp2221_set_sram_req *req, int irqmode)
370 {
371 req->alter_gpio_config = MCP2221_SRAM_ALTER_GPIO;
372
373 if (irqmode & (GPIO_INTR_POS_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
374 req->irq_config |= MCP2221_SRAM_ALTER_IRQ |
375 MCP2221_SRAM_ALTER_POS_EDGE |
376 MCP2221_SRAM_ENABLE_POS_EDGE |
377 MCP2221_SRAM_CLEAR_IRQ;
378 }
379 if (irqmode & (GPIO_INTR_NEG_EDGE | GPIO_INTR_DOUBLE_EDGE)) {
380 req->irq_config |= MCP2221_SRAM_ALTER_IRQ |
381 MCP2221_SRAM_ALTER_NEG_EDGE |
382 MCP2221_SRAM_ENABLE_NEG_EDGE |
383 MCP2221_SRAM_CLEAR_IRQ;
384 }
385
386 if (req->irq_config != 0) {
387 req->gp1_settings = MCP2221_SRAM_PIN_IS_ALT2;
388 } else {
389 req->irq_config = MCP2221_SRAM_ALTER_IRQ |
390 MCP2221_SRAM_CLEAR_IRQ;
391 req->gp1_settings = MCP2221_SRAM_PIN_IS_GPIO |
392 MCP2221_SRAM_GPIO_INPUT;
393 }
394 }
395
396 /*
397 * It is unfortunate that the GET and PUT requests are not symertric. That is,
398 * the bits sort of line up but not quite between a GET and PUT.
399 */
400
401 static struct umcpmio_mapping_put umcpmio_vref_puts[] = {
402 {
403 .tname = "4.096V",
404 .mask = 0x06 | 0x01,
405 },
406 {
407 .tname = "2.048V",
408 .mask = 0x04 | 0x01,
409 },
410 {
411 .tname = "1.024V",
412 .mask = 0x02 | 0x01,
413 },
414 {
415 .tname = "OFF",
416 .mask = 0x00 | 0x01,
417 },
418 {
419 .tname = "VDD",
420 .mask = 0x00,
421 }
422 };
423
424 void
425 umcpmio_set_dac_vref(struct mcp2221_set_sram_req *req, char *newvref)
426 {
427 int i;
428
429 for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
430 if (strncmp(newvref, umcpmio_vref_puts[i].tname,
431 UMCPMIO_VREF_NAME) == 0) {
432 break;
433 }
434 }
435
436 if (i == __arraycount(umcpmio_vref_puts))
437 return;
438
439 req->dac_voltage_reference |= umcpmio_vref_puts[i].mask |
440 MCP2221_SRAM_CHANGE_DAC_VREF;
441 }
442
443 int
444 umcpmio_set_dac_vref_one(struct umcpmio_softc *sc, char *newvref,
445 bool takemutex)
446 {
447 struct mcp2221_set_sram_req req;
448 struct mcp2221_set_sram_res res;
449 int err = 0;
450
451 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
452 umcpmio_set_dac_vref(&req, newvref);
453 err = umcpmio_put_sram(sc, &req, &res, takemutex);
454
455 return err;
456 }
457
458 void
459 umcpmio_set_dac_value(struct mcp2221_set_sram_req *req, uint8_t newvalue)
460 {
461 req->set_dac_output_value |= (newvalue & MCP2221_SRAM_DAC_VALUE_MASK) |
462 MCP2221_SRAM_CHANGE_DAC_VREF;
463 }
464
465 int
466 umcpmio_set_dac_value_one(struct umcpmio_softc *sc, uint8_t newvalue,
467 bool takemutex)
468 {
469 struct mcp2221_set_sram_req req;
470 struct mcp2221_set_sram_res res;
471 int err = 0;
472
473 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
474 umcpmio_set_dac_value(&req, newvalue);
475 err = umcpmio_put_sram(sc, &req, &res, takemutex);
476
477 return err;
478 }
479
480 void
481 umcpmio_set_adc_vref(struct mcp2221_set_sram_req *req, char *newvref)
482 {
483 int i;
484
485 for (i = 0; i < __arraycount(umcpmio_vref_puts); i++) {
486 if (strncmp(newvref, umcpmio_vref_puts[i].tname,
487 UMCPMIO_VREF_NAME) == 0) {
488 break;
489 }
490 }
491
492 if (i == __arraycount(umcpmio_vref_puts))
493 return;
494
495 req->adc_voltage_reference |= umcpmio_vref_puts[i].mask |
496 MCP2221_SRAM_CHANGE_ADC_VREF;
497 }
498
499 int
500 umcpmio_set_adc_vref_one(struct umcpmio_softc *sc, char *newvref,
501 bool takemutex)
502 {
503 struct mcp2221_set_sram_req req;
504 struct mcp2221_set_sram_res res;
505 int err = 0;
506
507 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
508 umcpmio_set_adc_vref(&req, newvref);
509 err = umcpmio_put_sram(sc, &req, &res, takemutex);
510
511 return err;
512 }
513
514 static struct umcpmio_mapping_put umcpmio_dc_puts[] = {
515 {
516 .tname = "75%",
517 .mask = MCP2221_SRAM_GPIO_CLOCK_DC_75,
518 },
519 {
520 .tname = "50%",
521 .mask = MCP2221_SRAM_GPIO_CLOCK_DC_50,
522 },
523 {
524 .tname = "25%",
525 .mask = MCP2221_SRAM_GPIO_CLOCK_DC_25,
526 },
527 {
528 .tname = "0%",
529 .mask = MCP2221_SRAM_GPIO_CLOCK_DC_0,
530 }
531 };
532
533 void
534 umcpmio_set_gpioclock_dc(struct mcp2221_set_sram_req *req, char *new_dc)
535 {
536 int i;
537
538 for (i = 0; i < __arraycount(umcpmio_dc_puts); i++) {
539 if (strncmp(new_dc, umcpmio_dc_puts[i].tname,
540 UMCPMIO_VREF_NAME) == 0) {
541 break;
542 }
543 }
544
545 if (i == __arraycount(umcpmio_dc_puts))
546 return;
547
548 req->clock_output_divider |= umcpmio_dc_puts[i].mask;
549 }
550
551 int
552 umcpmio_set_gpioclock_dc_one(struct umcpmio_softc *sc, char *new_dutycycle,
553 bool takemutex)
554 {
555 struct mcp2221_get_sram_res current_sram_res;
556 struct mcp2221_set_sram_req req;
557 struct mcp2221_set_sram_res res;
558 int err = 0;
559
560 err = umcpmio_get_sram(sc, ¤t_sram_res, takemutex);
561 if (err)
562 goto out;
563
564 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
565 umcpmio_set_gpioclock_dc(&req, new_dutycycle);
566 DPRINTF(("umcpmio_set_gpioclock_dc_one:"
567 " req.clock_output_divider=%02x, current mask=%02x\n",
568 req.clock_output_divider,
569 (current_sram_res.clock_divider &
570 MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
571 req.clock_output_divider |=
572 (current_sram_res.clock_divider &
573 MCP2221_SRAM_GPIO_CLOCK_CD_MASK) |
574 MCP2221_SRAM_GPIO_CHANGE_DCCD;
575 DPRINTF(("umcpmio_set_gpioclock_dc_one:"
576 " SET req.clock_output_divider=%02x\n",
577 req.clock_output_divider));
578 err = umcpmio_put_sram(sc, &req, &res, takemutex);
579 out:
580 return err;
581 }
582
583 static struct umcpmio_mapping_put umcpmio_cd_puts[] = {
584 {
585 .tname = "375kHz",
586 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
587 },
588 {
589 .tname = "750kHz",
590 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
591 },
592 {
593 .tname = "1.5MHz",
594 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
595 },
596 {
597 .tname = "3MHz",
598 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
599 },
600 {
601 .tname = "6MHz",
602 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
603 },
604 {
605 .tname = "12MHz",
606 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
607 },
608 {
609 .tname = "24MHz",
610 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
611 }
612 };
613
614 void
615 umcpmio_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
616 {
617 int i;
618
619 for (i = 0; i < __arraycount(umcpmio_cd_puts); i++) {
620 if (strncmp(new_cd, umcpmio_cd_puts[i].tname,
621 UMCPMIO_CD_NAME) == 0) {
622 break;
623 }
624 }
625
626 if (i == __arraycount(umcpmio_cd_puts))
627 return;
628
629 req->clock_output_divider |= umcpmio_cd_puts[i].mask;
630 }
631
632 int
633 umcpmio_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider,
634 bool takemutex)
635 {
636 struct mcp2221_get_sram_res current_sram_res;
637 struct mcp2221_set_sram_req req;
638 struct mcp2221_set_sram_res res;
639 int err = 0;
640
641 err = umcpmio_get_sram(sc, ¤t_sram_res, takemutex);
642 if (err)
643 goto out;
644
645 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
646 umcpmio_set_gpioclock_cd(&req, new_clockdivider);
647 DPRINTF(("umcpmio_set_gpioclock_cd_one:"
648 " req.clock_output_divider=%02x, current mask=%02x\n",
649 req.clock_output_divider,
650 (current_sram_res.clock_divider &
651 MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
652 req.clock_output_divider |=
653 (current_sram_res.clock_divider &
654 MCP2221_SRAM_GPIO_CLOCK_DC_MASK) |
655 MCP2221_SRAM_GPIO_CHANGE_DCCD;
656 DPRINTF(("umcpmio_set_gpioclock_cd_one:"
657 " SET req.clock_output_divider=%02x\n",
658 req.clock_output_divider));
659 err = umcpmio_put_sram(sc, &req, &res, takemutex);
660 out:
661 return err;
662 }
663
664 int
665 umcpmio_get_gpio_cfg(struct umcpmio_softc *sc,
666 struct mcp2221_get_gpio_cfg_res *res, bool takemutex)
667 {
668 struct mcp2221_get_gpio_cfg_req req;
669 int err = 0;
670
671 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
672 req.cmd = MCP2221_CMD_GET_GPIO_CFG;
673
674 if (takemutex)
675 mutex_enter(&sc->sc_action_mutex);
676 err = umcpmio_send_report(sc,
677 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
678 (uint8_t *)res, sc->sc_cv_wait);
679 if (takemutex)
680 mutex_exit(&sc->sc_action_mutex);
681
682 return err;
683 }
684
685 int
686 umcpmio_put_gpio_cfg(struct umcpmio_softc *sc,
687 struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res,
688 bool takemutex)
689 {
690 int err = 0;
691
692 req->cmd = MCP2221_CMD_SET_GPIO_CFG;
693
694 if (takemutex)
695 mutex_enter(&sc->sc_action_mutex);
696 err = umcpmio_send_report(sc,
697 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
698 (uint8_t *)res, sc->sc_cv_wait);
699 if (takemutex)
700 mutex_exit(&sc->sc_action_mutex);
701
702 return err;
703 }
704
705 /* So... if the pin isn't set to GPIO, just call the output LOW */
706
707 int
708 umcpmio_get_gpio_value(struct umcpmio_softc *sc,
709 int pin, bool takemutex)
710 {
711 struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
712 int err = 0;
713 int r = GPIO_PIN_LOW;
714
715 err = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, takemutex);
716 if (err)
717 goto out;
718
719 if (get_gpio_cfg_res.cmd != MCP2221_CMD_GET_GPIO_CFG ||
720 get_gpio_cfg_res.completion != MCP2221_CMD_COMPLETE_OK) {
721 device_printf(sc->sc_dev, "umcpmio_get_gpio_value:"
722 " wrong command or error: %02x %02x\n",
723 get_gpio_cfg_res.cmd,
724 get_gpio_cfg_res.completion);
725 goto out;
726 }
727
728 switch (pin) {
729 case 0:
730 if (get_gpio_cfg_res.gp0_pin_value !=
731 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
732 if (get_gpio_cfg_res.gp0_pin_value == 0x01)
733 r = GPIO_PIN_HIGH;
734 break;
735 case 1:
736 if (get_gpio_cfg_res.gp1_pin_value !=
737 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
738 if (get_gpio_cfg_res.gp1_pin_value == 0x01)
739 r = GPIO_PIN_HIGH;
740 break;
741 case 2:
742 if (get_gpio_cfg_res.gp2_pin_value !=
743 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
744 if (get_gpio_cfg_res.gp2_pin_value == 0x01)
745 r = GPIO_PIN_HIGH;
746 break;
747 case 3:
748 if (get_gpio_cfg_res.gp3_pin_value !=
749 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
750 if (get_gpio_cfg_res.gp3_pin_value == 0x01)
751 r = GPIO_PIN_HIGH;
752 break;
753 default:
754 break;
755 }
756 out:
757 return r;
758 }
759
760 void
761 umcpmio_set_gpio_value(struct mcp2221_set_gpio_cfg_req *req,
762 int pin, bool value)
763 {
764 uint8_t *alter = NULL;
765 uint8_t *newvalue = NULL;
766
767 if (pin < 0 || pin >= MCP2221_NPINS)
768 return;
769
770 switch (pin) {
771 case 0:
772 alter = &req->alter_gp0_value;
773 newvalue = &req->new_gp0_value;
774 break;
775 case 1:
776 alter = &req->alter_gp1_value;
777 newvalue = &req->new_gp1_value;
778 break;
779 case 2:
780 alter = &req->alter_gp2_value;
781 newvalue = &req->new_gp2_value;
782 break;
783 case 3:
784 alter = &req->alter_gp3_value;
785 newvalue = &req->new_gp3_value;
786 break;
787 default:
788 return;
789 }
790
791 *alter = MCP2221_GPIO_CFG_ALTER;
792 *newvalue = 0;
793 if (value)
794 *newvalue = 1;
795 }
796
797 int
798 umcpmio_set_gpio_value_one(struct umcpmio_softc *sc,
799 int pin, bool value, bool takemutex)
800 {
801 int err = 0;
802 struct mcp2221_set_gpio_cfg_req req;
803 struct mcp2221_set_gpio_cfg_res res;
804
805 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
806 umcpmio_set_gpio_value(&req, pin, value);
807 err = umcpmio_put_gpio_cfg(sc, &req, &res, takemutex);
808 if (err)
809 goto out;
810 if (res.cmd != MCP2221_CMD_SET_GPIO_CFG ||
811 res.completion != MCP2221_CMD_COMPLETE_OK) {
812 err = EIO;
813 device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:"
814 " not the command desired, or error: %02x %02x\n",
815 res.cmd,
816 res.completion);
817 }
818 out:
819 return err;
820 }
821
822 int
823 umcpmio_get_flash(struct umcpmio_softc *sc, uint8_t subcode,
824 struct mcp2221_get_flash_res *res, bool takemutex)
825 {
826 struct mcp2221_get_flash_req req;
827 int err = 0;
828
829 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
830 req.cmd = MCP2221_CMD_GET_FLASH;
831
832 if (subcode < MCP2221_FLASH_SUBCODE_CS ||
833 subcode > MCP2221_FLASH_SUBCODE_CHIPSN)
834 return EINVAL;
835
836 req.subcode = subcode;
837
838 if (takemutex)
839 mutex_enter(&sc->sc_action_mutex);
840 err = umcpmio_send_report(sc,
841 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
842 (uint8_t *)res, sc->sc_cv_wait);
843 if (takemutex)
844 mutex_exit(&sc->sc_action_mutex);
845
846 return err;
847 }
848
849 int
850 umcpmio_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req,
851 struct mcp2221_put_flash_res *res, bool takemutex)
852 {
853 int err = 0;
854
855 req->cmd = MCP2221_CMD_SET_FLASH;
856
857 if (req->subcode < MCP2221_FLASH_SUBCODE_CS ||
858 req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) {
859 DPRINTF(("umcpmio_put_flash: subcode out of range:"
860 " subcode=%d\n",
861 req->subcode));
862 return EINVAL;
863 }
864
865 if (takemutex)
866 mutex_enter(&sc->sc_action_mutex);
867 err = umcpmio_send_report(sc,
868 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
869 (uint8_t *)res, sc->sc_cv_wait);
870 if (takemutex)
871 mutex_exit(&sc->sc_action_mutex);
872
873 return err;
874 }
875