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