umcpmio_subr.c revision 1.2 1 /* $NetBSD: umcpmio_subr.c,v 1.2 2025/03/17 18:24:08 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.2 2025/03/17 18:24:08 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 if (res.set_i2c_speed == MCP2221_I2C_SPEED_BUSY)
142 err = EBUSY;
143 }
144
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 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
563 umcpmio_set_gpioclock_dc(&req, new_dutycycle);
564 DPRINTF(("umcpmio_set_gpioclock_dc_one:"
565 " req.clock_output_divider=%02x, current mask=%02x\n",
566 req.clock_output_divider,
567 (current_sram_res.clock_divider &
568 MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
569 req.clock_output_divider |=
570 (current_sram_res.clock_divider &
571 MCP2221_SRAM_GPIO_CLOCK_CD_MASK) |
572 MCP2221_SRAM_GPIO_CHANGE_DCCD;
573 DPRINTF(("umcpmio_set_gpioclock_dc_one:"
574 " SET req.clock_output_divider=%02x\n",
575 req.clock_output_divider));
576 err = umcpmio_put_sram(sc, &req, &res, takemutex);
577 }
578
579 return err;
580 }
581
582 static struct umcpmio_mapping_put umcpmio_cd_puts[] = {
583 {
584 .tname = "375kHz",
585 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_375KHZ,
586 },
587 {
588 .tname = "750kHz",
589 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_750KHZ,
590 },
591 {
592 .tname = "1.5MHz",
593 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_1P5MHZ,
594 },
595 {
596 .tname = "3MHz",
597 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_3MHZ,
598 },
599 {
600 .tname = "6MHz",
601 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_6MHZ,
602 },
603 {
604 .tname = "12MHz",
605 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_12MHZ,
606 },
607 {
608 .tname = "24MHz",
609 .mask = MCP2221_SRAM_GPIO_CLOCK_CD_24MHZ,
610 }
611 };
612
613 void
614 umcpmio_set_gpioclock_cd(struct mcp2221_set_sram_req *req, char *new_cd)
615 {
616 int i;
617
618 for (i = 0; i < __arraycount(umcpmio_cd_puts); i++) {
619 if (strncmp(new_cd, umcpmio_cd_puts[i].tname,
620 UMCPMIO_CD_NAME) == 0) {
621 break;
622 }
623 }
624
625 if (i == __arraycount(umcpmio_cd_puts))
626 return;
627
628 req->clock_output_divider |= umcpmio_cd_puts[i].mask;
629 }
630
631 int
632 umcpmio_set_gpioclock_cd_one(struct umcpmio_softc *sc, char *new_clockdivider,
633 bool takemutex)
634 {
635 struct mcp2221_get_sram_res current_sram_res;
636 struct mcp2221_set_sram_req req;
637 struct mcp2221_set_sram_res res;
638 int err = 0;
639
640 err = umcpmio_get_sram(sc, ¤t_sram_res, takemutex);
641 if (! err) {
642 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
643 umcpmio_set_gpioclock_cd(&req, new_clockdivider);
644 DPRINTF(("umcpmio_set_gpioclock_cd_one:"
645 " req.clock_output_divider=%02x, current mask=%02x\n",
646 req.clock_output_divider,
647 (current_sram_res.clock_divider &
648 MCP2221_SRAM_GPIO_CLOCK_CD_MASK)));
649 req.clock_output_divider |=
650 (current_sram_res.clock_divider &
651 MCP2221_SRAM_GPIO_CLOCK_DC_MASK) |
652 MCP2221_SRAM_GPIO_CHANGE_DCCD;
653 DPRINTF(("umcpmio_set_gpioclock_cd_one:"
654 " SET req.clock_output_divider=%02x\n",
655 req.clock_output_divider));
656 err = umcpmio_put_sram(sc, &req, &res, takemutex);
657 }
658
659 return err;
660 }
661
662 int
663 umcpmio_get_gpio_cfg(struct umcpmio_softc *sc,
664 struct mcp2221_get_gpio_cfg_res *res, bool takemutex)
665 {
666 struct mcp2221_get_gpio_cfg_req req;
667 int err = 0;
668
669 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
670 req.cmd = MCP2221_CMD_GET_GPIO_CFG;
671
672 if (takemutex)
673 mutex_enter(&sc->sc_action_mutex);
674 err = umcpmio_send_report(sc,
675 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
676 (uint8_t *)res, sc->sc_cv_wait);
677 if (takemutex)
678 mutex_exit(&sc->sc_action_mutex);
679
680 return err;
681 }
682
683 int
684 umcpmio_put_gpio_cfg(struct umcpmio_softc *sc,
685 struct mcp2221_set_gpio_cfg_req *req, struct mcp2221_set_gpio_cfg_res *res,
686 bool takemutex)
687 {
688 int err = 0;
689
690 req->cmd = MCP2221_CMD_SET_GPIO_CFG;
691
692 if (takemutex)
693 mutex_enter(&sc->sc_action_mutex);
694 err = umcpmio_send_report(sc,
695 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
696 (uint8_t *)res, sc->sc_cv_wait);
697 if (takemutex)
698 mutex_exit(&sc->sc_action_mutex);
699
700 return err;
701 }
702
703 /* So... if the pin isn't set to GPIO, just call the output LOW */
704
705 int
706 umcpmio_get_gpio_value(struct umcpmio_softc *sc,
707 int pin, bool takemutex)
708 {
709 struct mcp2221_get_gpio_cfg_res get_gpio_cfg_res;
710 int err = 0;
711 int r = GPIO_PIN_LOW;
712
713 err = umcpmio_get_gpio_cfg(sc, &get_gpio_cfg_res, takemutex);
714 if (! err) {
715 if (get_gpio_cfg_res.cmd == MCP2221_CMD_GET_GPIO_CFG &&
716 get_gpio_cfg_res.completion == MCP2221_CMD_COMPLETE_OK) {
717 switch (pin) {
718 case 0:
719 if (get_gpio_cfg_res.gp0_pin_value !=
720 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
721 if (get_gpio_cfg_res.gp0_pin_value ==
722 0x01)
723 r = GPIO_PIN_HIGH;
724 break;
725 case 1:
726 if (get_gpio_cfg_res.gp1_pin_value !=
727 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
728 if (get_gpio_cfg_res.gp1_pin_value ==
729 0x01)
730 r = GPIO_PIN_HIGH;
731 break;
732 case 2:
733 if (get_gpio_cfg_res.gp2_pin_value !=
734 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
735 if (get_gpio_cfg_res.gp2_pin_value ==
736 0x01)
737 r = GPIO_PIN_HIGH;
738 break;
739 case 3:
740 if (get_gpio_cfg_res.gp3_pin_value !=
741 MCP2221_GPIO_CFG_VALUE_NOT_GPIO)
742 if (get_gpio_cfg_res.gp3_pin_value ==
743 0x01)
744 r = GPIO_PIN_HIGH;
745 break;
746 default:
747 break;
748 }
749 } else {
750 device_printf(sc->sc_dev, "umcpmio_get_gpio_value:"
751 " wrong command or error: %02x %02x\n",
752 get_gpio_cfg_res.cmd,
753 get_gpio_cfg_res.completion);
754 }
755 }
756
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 switch (pin) {
769 case 0:
770 alter = &req->alter_gp0_value;
771 newvalue = &req->new_gp0_value;
772 break;
773 case 1:
774 alter = &req->alter_gp1_value;
775 newvalue = &req->new_gp1_value;
776 break;
777 case 2:
778 alter = &req->alter_gp2_value;
779 newvalue = &req->new_gp2_value;
780 break;
781 case 3:
782 alter = &req->alter_gp3_value;
783 newvalue = &req->new_gp3_value;
784 break;
785 default:
786 break;
787 }
788
789 if (alter != NULL) {
790 *alter = MCP2221_GPIO_CFG_ALTER;
791 *newvalue = 0;
792 if (value)
793 *newvalue = 1;
794 }
795 }
796 }
797
798 int
799 umcpmio_set_gpio_value_one(struct umcpmio_softc *sc,
800 int pin, bool value, bool takemutex)
801 {
802 int err = 0;
803 struct mcp2221_set_gpio_cfg_req req;
804 struct mcp2221_set_gpio_cfg_res res;
805
806 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
807 umcpmio_set_gpio_value(&req, pin, value);
808 err = umcpmio_put_gpio_cfg(sc, &req, &res, takemutex);
809 if (! err) {
810 if (res.cmd == MCP2221_CMD_SET_GPIO_CFG &&
811 res.completion == MCP2221_CMD_COMPLETE_OK) {
812 } else {
813 err = EIO;
814 device_printf(sc->sc_dev, "umcpmio_gpio_pin_write:"
815 " not the command desired, or error: %02x %02x\n",
816 res.cmd,
817 res.completion);
818 }
819 }
820
821 return err;
822 }
823
824 int
825 umcpmio_get_flash(struct umcpmio_softc *sc, uint8_t subcode,
826 struct mcp2221_get_flash_res *res, bool takemutex)
827 {
828 struct mcp2221_get_flash_req req;
829 int err = 0;
830
831 memset(&req, 0, MCP2221_REQ_BUFFER_SIZE);
832 req.cmd = MCP2221_CMD_GET_FLASH;
833
834 if (subcode < MCP2221_FLASH_SUBCODE_CS ||
835 subcode > MCP2221_FLASH_SUBCODE_CHIPSN)
836 return EINVAL;
837
838 req.subcode = subcode;
839
840 if (takemutex)
841 mutex_enter(&sc->sc_action_mutex);
842 err = umcpmio_send_report(sc,
843 (uint8_t *)&req, MCP2221_REQ_BUFFER_SIZE,
844 (uint8_t *)res, sc->sc_cv_wait);
845 if (takemutex)
846 mutex_exit(&sc->sc_action_mutex);
847
848 return err;
849 }
850
851 int
852 umcpmio_put_flash(struct umcpmio_softc *sc, struct mcp2221_put_flash_req *req,
853 struct mcp2221_put_flash_res *res, bool takemutex)
854 {
855 int err = 0;
856
857 req->cmd = MCP2221_CMD_SET_FLASH;
858
859 if (req->subcode < MCP2221_FLASH_SUBCODE_CS ||
860 req->subcode > MCP2221_FLASH_SUBCODE_CHIPSN) {
861 DPRINTF(("umcpmio_put_flash: subcode out of range:"
862 " subcode=%d\n",
863 req->subcode));
864 return EINVAL;
865 }
866
867 if (takemutex)
868 mutex_enter(&sc->sc_action_mutex);
869 err = umcpmio_send_report(sc,
870 (uint8_t *)req, MCP2221_REQ_BUFFER_SIZE,
871 (uint8_t *)res, sc->sc_cv_wait);
872 if (takemutex)
873 mutex_exit(&sc->sc_action_mutex);
874
875 return err;
876 }
877