1 /* $NetBSD: spi.c,v 1.36 2025/09/14 16:00:04 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 Urbana-Champaign Independent Media Center. 5 * Copyright (c) 2006 Garrett D'Amore. 6 * All rights reserved. 7 * 8 * Portions of this code were written by Garrett D'Amore for the 9 * Champaign-Urbana Community Wireless Network Project. 10 * 11 * Redistribution and use in source and binary forms, with or 12 * without modification, are permitted provided that the following 13 * conditions are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer in the documentation and/or other materials provided 19 * with the distribution. 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgements: 22 * This product includes software developed by the Urbana-Champaign 23 * Independent Media Center. 24 * This product includes software developed by Garrett D'Amore. 25 * 4. Urbana-Champaign Independent Media Center's name and Garrett 26 * D'Amore's name may not be used to endorse or promote products 27 * derived from this software without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE URBANA-CHAMPAIGN INDEPENDENT 30 * MEDIA CENTER AND GARRETT D'AMORE ``AS IS'' AND ANY EXPRESS OR 31 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 32 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE URBANA-CHAMPAIGN INDEPENDENT 34 * MEDIA CENTER OR GARRETT D'AMORE BE LIABLE FOR ANY DIRECT, INDIRECT, 35 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 36 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 37 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 38 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 39 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 40 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 41 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 */ 43 44 #include "opt_fdt.h" /* XXX */ 45 46 #include <sys/cdefs.h> 47 __KERNEL_RCSID(0, "$NetBSD: spi.c,v 1.36 2025/09/14 16:00:04 thorpej Exp $"); 48 49 #include "locators.h" 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/device.h> 54 #include <sys/conf.h> 55 #include <sys/malloc.h> 56 #include <sys/mutex.h> 57 #include <sys/condvar.h> 58 #include <sys/errno.h> 59 60 #include <dev/spi/spivar.h> 61 #include <dev/spi/spi_io.h> 62 #include <dev/spi/spi_calls.h> 63 64 #ifdef FDT 65 #include <dev/fdt/fdt_spi.h> /* XXX */ 66 #include <dev/ofw/openfirm.h> /* XXX */ 67 #endif 68 69 #include "ioconf.h" 70 #include "locators.h" 71 72 struct spi_softc { 73 device_t sc_dev; 74 const struct spi_controller *sc_controller; 75 int sc_mode; 76 int sc_speed; 77 int sc_slave; 78 int sc_nslaves; 79 spi_handle_t sc_slaves; 80 kmutex_t sc_slave_state_lock; 81 kmutex_t sc_lock; 82 kcondvar_t sc_cv; 83 kmutex_t sc_dev_lock; 84 int sc_flags; 85 #define SPIC_BUSY 1 86 }; 87 88 static dev_type_open(spi_open); 89 static dev_type_close(spi_close); 90 static dev_type_ioctl(spi_ioctl); 91 92 const struct cdevsw spi_cdevsw = { 93 .d_open = spi_open, 94 .d_close = spi_close, 95 .d_read = noread, 96 .d_write = nowrite, 97 .d_ioctl = spi_ioctl, 98 .d_stop = nostop, 99 .d_tty = notty, 100 .d_poll = nopoll, 101 .d_mmap = nommap, 102 .d_kqfilter = nokqfilter, 103 .d_discard = nodiscard, 104 .d_flag = D_OTHER | D_MPSAFE 105 }; 106 107 /* 108 * SPI slave device. We have one of these per slave. 109 */ 110 struct spi_handle { 111 struct spi_softc *sh_sc; /* static */ 112 const struct spi_controller *sh_controller; /* static */ 113 int sh_slave; /* static */ 114 int sh_mode; /* locked by owning child */ 115 int sh_speed; /* locked by owning child */ 116 int sh_flags; /* vv slave_state_lock vv */ 117 #define SPIH_ATTACHED __BIT(0) 118 #define SPIH_DIRECT __BIT(1) 119 device_t sh_dev; /* ^^ slave_state_lock ^^ */ 120 }; 121 122 #define SPI_MAXDATA 4096 123 124 /* 125 * API for bus drivers. 126 */ 127 128 int 129 spibus_print(void *aux, const char *pnp) 130 { 131 132 if (pnp != NULL) 133 aprint_normal("spi at %s", pnp); 134 135 return (UNCONF); 136 } 137 138 139 static int 140 spi_match(device_t parent, cfdata_t cf, void *aux) 141 { 142 143 return 1; 144 } 145 146 static int 147 spi_print_direct(void *aux, const char *pnp) 148 { 149 struct spi_attach_args *sa = aux; 150 151 if (pnp != NULL) { 152 aprint_normal("%s%s%s%s at %s slave %d", 153 sa->sa_name ? sa->sa_name : "(unknown)", 154 sa->sa_clist ? " (" : "", 155 sa->sa_clist ? sa->sa_clist : "", 156 sa->sa_clist ? ")" : "", 157 pnp, sa->sa_handle->sh_slave); 158 } else { 159 aprint_normal(" slave %d", sa->sa_handle->sh_slave); 160 } 161 162 return UNCONF; 163 } 164 165 static int 166 spi_print(void *aux, const char *pnp) 167 { 168 struct spi_attach_args *sa = aux; 169 170 aprint_normal(" slave %d", sa->sa_handle->sh_slave); 171 172 return UNCONF; 173 } 174 175 static void 176 spi_attach_child(struct spi_softc *sc, struct spi_attach_args *sa, 177 int chip_select, cfdata_t cf) 178 { 179 spi_handle_t sh; 180 device_t newdev = NULL; 181 bool is_direct = cf == NULL; 182 const int skip_flags = is_direct ? SPIH_ATTACHED 183 : (SPIH_ATTACHED | SPIH_DIRECT); 184 const int claim_flags = skip_flags ^ SPIH_DIRECT; 185 int locs[SPICF_NLOCS] = { 0 }; 186 187 if (chip_select < 0 || 188 chip_select >= sc->sc_controller->sct_nslaves) { 189 return; 190 } 191 192 sh = &sc->sc_slaves[chip_select]; 193 194 mutex_enter(&sc->sc_slave_state_lock); 195 if (ISSET(sh->sh_flags, skip_flags)) { 196 mutex_exit(&sc->sc_slave_state_lock); 197 return; 198 } 199 200 /* Keep others off of this chip select. */ 201 SET(sh->sh_flags, claim_flags); 202 mutex_exit(&sc->sc_slave_state_lock); 203 204 locs[SPICF_SLAVE] = chip_select; 205 sa->sa_handle = sh; 206 207 if (is_direct) { 208 newdev = config_found(sc->sc_dev, sa, spi_print_direct, 209 CFARGS(.submatch = config_stdsubmatch, 210 .locators = locs, 211 .devhandle = sa->sa_devhandle)); 212 } else { 213 if (config_probe(sc->sc_dev, cf, &sa)) { 214 newdev = config_attach(sc->sc_dev, cf, &sa, spi_print, 215 CFARGS(.locators = locs)); 216 } 217 } 218 219 mutex_enter(&sc->sc_slave_state_lock); 220 if (newdev == NULL) { 221 /* 222 * Clear our claim on this chip select (yes, just 223 * the ATTACHED flag; we want to keep indirects off 224 * of chip selects for which there is a device tree 225 * node). 226 */ 227 CLR(sh->sh_flags, SPIH_ATTACHED); 228 } else { 229 /* Record the child for posterity. */ 230 sh->sh_dev = newdev; 231 } 232 mutex_exit(&sc->sc_slave_state_lock); 233 } 234 235 static int 236 spi_search(device_t parent, cfdata_t cf, const int *ldesc, void *aux) 237 { 238 struct spi_softc *sc = device_private(parent); 239 240 if (cf->cf_loc[SPICF_SLAVE] == SPICF_SLAVE_DEFAULT) { 241 /* No wildcards for indirect on SPI. */ 242 return 0; 243 } 244 245 struct spi_attach_args sa = { 0 }; 246 spi_attach_child(sc, &sa, cf->cf_loc[SPICF_SLAVE], cf); 247 248 return 0; 249 } 250 251 static bool 252 spi_enumerate_devices_callback(device_t self, 253 struct spi_enumerate_devices_args *args) 254 { 255 struct spi_softc *sc = device_private(self); 256 257 spi_attach_child(sc, args->sa, args->chip_select, NULL); 258 259 return true; /* keep enumerating */ 260 } 261 262 int 263 spi_compatible_match(const struct spi_attach_args *sa, 264 const struct device_compatible_entry *compats) 265 { 266 return device_compatible_match_strlist(sa->sa_clist, 267 sa->sa_clist_size, compats); 268 } 269 270 const struct device_compatible_entry * 271 spi_compatible_lookup(const struct spi_attach_args *sa, 272 const struct device_compatible_entry *compats) 273 { 274 return device_compatible_lookup_strlist(sa->sa_clist, 275 sa->sa_clist_size, compats); 276 } 277 278 bool 279 spi_use_direct_match(const struct spi_attach_args *sa, 280 const struct device_compatible_entry *compats, 281 int *match_resultp) 282 { 283 KASSERT(match_resultp != NULL); 284 285 if (sa->sa_clist != NULL && sa->sa_clist_size != 0) { 286 *match_resultp = spi_compatible_match(sa, compats); 287 return true; 288 } 289 290 return false; 291 } 292 293 /* 294 * API for device drivers. 295 * 296 * We provide wrapper routines to decouple the ABI for the SPI 297 * device drivers from the ABI for the SPI bus drivers. 298 */ 299 static void 300 spi_attach(device_t parent, device_t self, void *aux) 301 { 302 struct spi_softc *sc = device_private(self); 303 struct spibus_attach_args *sba = aux; 304 int i; 305 306 aprint_naive(": SPI bus\n"); 307 aprint_normal(": SPI bus\n"); 308 309 mutex_init(&sc->sc_dev_lock, MUTEX_DEFAULT, IPL_NONE); 310 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); 311 mutex_init(&sc->sc_slave_state_lock, MUTEX_DEFAULT, IPL_NONE); 312 cv_init(&sc->sc_cv, "spictl"); 313 314 sc->sc_dev = self; 315 sc->sc_controller = sba->sba_controller; 316 sc->sc_nslaves = sba->sba_controller->sct_nslaves; 317 /* allocate slave structures */ 318 sc->sc_slaves = malloc(sizeof(*sc->sc_slaves) * sc->sc_nslaves, 319 M_DEVBUF, M_WAITOK | M_ZERO); 320 321 sc->sc_speed = 0; 322 sc->sc_mode = -1; 323 sc->sc_slave = -1; 324 325 /* 326 * Initialize slave handles 327 */ 328 for (i = 0; i < sc->sc_nslaves; i++) { 329 sc->sc_slaves[i].sh_slave = i; 330 sc->sc_slaves[i].sh_sc = sc; 331 sc->sc_slaves[i].sh_controller = sc->sc_controller; 332 } 333 334 /* XXX Need a better way for this. */ 335 switch (devhandle_type(device_handle(sc->sc_dev))) { 336 #ifdef FDT 337 case DEVHANDLE_TYPE_OF: 338 fdtbus_register_spi_controller(self, sc->sc_controller); 339 break; 340 #endif /* FDT */ 341 default: 342 break; 343 } 344 345 /* 346 * Attempt to enumerate the devices on the bus using the 347 * platform device tree. 348 */ 349 struct spi_attach_args sa = { 0 }; 350 struct spi_enumerate_devices_args enumargs = { 351 .sa = &sa, 352 .callback = spi_enumerate_devices_callback, 353 }; 354 device_call(self, SPI_ENUMERATE_DEVICES(&enumargs)); 355 356 /* Then do any other devices the user may have manually wired */ 357 config_search(self, NULL, 358 CFARGS(.search = spi_search)); 359 } 360 361 static int 362 spi_open(dev_t dev, int flag, int fmt, lwp_t *l) 363 { 364 struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev)); 365 366 if (sc == NULL) 367 return ENXIO; 368 369 return 0; 370 } 371 372 static int 373 spi_close(dev_t dev, int flag, int fmt, lwp_t *l) 374 { 375 376 return 0; 377 } 378 379 static int 380 spi_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l) 381 { 382 struct spi_softc *sc = device_lookup_private(&spi_cd, minor(dev)); 383 spi_handle_t sh; 384 spi_ioctl_configure_t *sic; 385 spi_ioctl_transfer_t *sit; 386 uint8_t *sbuf, *rbuf; 387 int error; 388 389 if (sc == NULL) 390 return ENXIO; 391 392 mutex_enter(&sc->sc_dev_lock); 393 394 switch (cmd) { 395 case SPI_IOCTL_CONFIGURE: 396 sic = (spi_ioctl_configure_t *)data; 397 if (sic->sic_addr < 0 || sic->sic_addr >= sc->sc_nslaves) { 398 error = EINVAL; 399 break; 400 } 401 sh = &sc->sc_slaves[sic->sic_addr]; 402 error = spi_configure(sc->sc_dev, sh, sic->sic_mode, 403 sic->sic_speed); 404 break; 405 case SPI_IOCTL_TRANSFER: 406 sit = (spi_ioctl_transfer_t *)data; 407 if (sit->sit_addr < 0 || sit->sit_addr >= sc->sc_nslaves) { 408 error = EINVAL; 409 break; 410 } 411 if ((sit->sit_send && sit->sit_sendlen == 0) 412 || (sit->sit_recv && sit->sit_recvlen == 0)) { 413 error = EINVAL; 414 break; 415 } 416 sh = &sc->sc_slaves[sit->sit_addr]; 417 sbuf = rbuf = NULL; 418 error = 0; 419 if (sit->sit_send && sit->sit_sendlen <= SPI_MAXDATA) { 420 sbuf = malloc(sit->sit_sendlen, M_DEVBUF, M_WAITOK); 421 error = copyin(sit->sit_send, sbuf, sit->sit_sendlen); 422 } 423 if (sit->sit_recv && sit->sit_recvlen <= SPI_MAXDATA) { 424 rbuf = malloc(sit->sit_recvlen, M_DEVBUF, M_WAITOK); 425 } 426 if (error == 0) { 427 if (sbuf && rbuf) 428 error = spi_send_recv(sh, 429 sit->sit_sendlen, sbuf, 430 sit->sit_recvlen, rbuf); 431 else if (sbuf) 432 error = spi_send(sh, 433 sit->sit_sendlen, sbuf); 434 else if (rbuf) 435 error = spi_recv(sh, 436 sit->sit_recvlen, rbuf); 437 } 438 if (rbuf) { 439 if (error == 0) 440 error = copyout(rbuf, sit->sit_recv, 441 sit->sit_recvlen); 442 free(rbuf, M_DEVBUF); 443 } 444 if (sbuf) { 445 free(sbuf, M_DEVBUF); 446 } 447 break; 448 default: 449 error = ENODEV; 450 break; 451 } 452 453 mutex_exit(&sc->sc_dev_lock); 454 455 return error; 456 } 457 458 CFATTACH_DECL_NEW(spi, sizeof(struct spi_softc), 459 spi_match, spi_attach, NULL, NULL); 460 461 /* 462 * Configure. This should be the first thing that the SPI driver 463 * should do, to configure which mode (e.g. SPI_MODE_0, which is the 464 * same as Philips Microwire mode), and speed. If the bus driver 465 * cannot run fast enough, then it should just configure the fastest 466 * mode that it can support. If the bus driver cannot run slow 467 * enough, then the device is incompatible and an error should be 468 * returned. 469 */ 470 int 471 spi_configure(device_t dev, spi_handle_t sh, int mode, int speed) 472 { 473 struct spi_get_transfer_mode_args args = { 0 }; 474 int error; 475 476 /* 477 * Get transfer mode information from the platform device tree, if 478 * it exists. 479 */ 480 error = device_call(dev, SPI_GET_TRANSFER_MODE(&args)); 481 if (error) { 482 if (error != ENOTSUP) { 483 /* 484 * This error is fatal. Error message has already 485 * been displayed. 486 */ 487 return error; 488 } 489 } else { 490 /* 491 * If the device tree specifies clock phase shift or 492 * polarity inversion, override whatever the caller 493 * specified. 494 */ 495 if (args.mode != 0) { 496 aprint_debug_dev(dev, 497 "using SPI mode %u from device tree\n", 498 args.mode); 499 mode = args.mode; 500 } 501 502 /* 503 * If the device tree specifies the max clock frequency, 504 * override whatever the caller specified. 505 */ 506 if (args.max_frequency != 0) { 507 aprint_debug_dev(dev, 508 "using max-frequency %u Hz from device tree\n", 509 args.max_frequency); 510 speed = args.max_frequency; 511 } 512 513 /* XXX Handle the other transfer properties. */ 514 } 515 516 sh->sh_mode = mode; 517 sh->sh_speed = speed; 518 519 return 0; 520 } 521 522 /* 523 * Acquire controller 524 */ 525 static void 526 spi_acquire(spi_handle_t sh) 527 { 528 struct spi_softc *sc = sh->sh_sc; 529 530 mutex_enter(&sc->sc_lock); 531 while ((sc->sc_flags & SPIC_BUSY) != 0) 532 cv_wait(&sc->sc_cv, &sc->sc_lock); 533 sc->sc_flags |= SPIC_BUSY; 534 mutex_exit(&sc->sc_lock); 535 } 536 537 /* 538 * Release controller 539 */ 540 static void 541 spi_release(spi_handle_t sh) 542 { 543 struct spi_softc *sc = sh->sh_sc; 544 545 mutex_enter(&sc->sc_lock); 546 sc->sc_flags &= ~SPIC_BUSY; 547 cv_broadcast(&sc->sc_cv); 548 mutex_exit(&sc->sc_lock); 549 } 550 551 void 552 spi_transfer_init(struct spi_transfer *st) 553 { 554 555 mutex_init(&st->st_lock, MUTEX_DEFAULT, IPL_VM); 556 cv_init(&st->st_cv, "spixfr"); 557 558 st->st_flags = 0; 559 st->st_errno = 0; 560 st->st_done = NULL; 561 st->st_chunks = NULL; 562 st->st_private = NULL; 563 st->st_slave = -1; 564 } 565 566 void 567 spi_chunk_init(struct spi_chunk *chunk, int cnt, const uint8_t *wptr, 568 uint8_t *rptr) 569 { 570 571 chunk->chunk_write = chunk->chunk_wptr = wptr; 572 chunk->chunk_read = chunk->chunk_rptr = rptr; 573 chunk->chunk_rresid = chunk->chunk_wresid = chunk->chunk_count = cnt; 574 chunk->chunk_next = NULL; 575 } 576 577 void 578 spi_transfer_add(struct spi_transfer *st, struct spi_chunk *chunk) 579 { 580 struct spi_chunk **cpp; 581 582 /* this is an O(n) insert -- perhaps we should use a simpleq? */ 583 for (cpp = &st->st_chunks; *cpp; cpp = &(*cpp)->chunk_next); 584 *cpp = chunk; 585 } 586 587 int 588 spi_transfer(spi_handle_t sh, struct spi_transfer *st) 589 { 590 struct spi_softc *sc = sh->sh_sc; 591 const struct spi_controller *tag = sh->sh_controller; 592 struct spi_chunk *chunk; 593 int error; 594 595 /* 596 * Initialize "resid" counters and pointers, so that callers 597 * and bus drivers don't have to. 598 */ 599 for (chunk = st->st_chunks; chunk; chunk = chunk->chunk_next) { 600 chunk->chunk_wresid = chunk->chunk_rresid = chunk->chunk_count; 601 chunk->chunk_wptr = chunk->chunk_write; 602 chunk->chunk_rptr = chunk->chunk_read; 603 } 604 605 /* 606 * Match slave and parameters to handle 607 */ 608 st->st_slave = sh->sh_slave; 609 610 /* 611 * Reserve controller during transaction 612 */ 613 spi_acquire(sh); 614 615 st->st_spiprivate = (void *)sh; 616 617 /* 618 * Reconfigure controller 619 * 620 * XXX backends don't configure per-slave parameters 621 * Whenever we switch slaves or change mode or speed, we 622 * need to tell the backend. 623 */ 624 if (sc->sc_slave != sh->sh_slave 625 || sc->sc_mode != sh->sh_mode 626 || sc->sc_speed != sh->sh_speed) { 627 error = (*tag->sct_configure)(tag->sct_cookie, 628 sh->sh_slave, sh->sh_mode, sh->sh_speed); 629 if (error) 630 return error; 631 } 632 sc->sc_mode = sh->sh_mode; 633 sc->sc_speed = sh->sh_speed; 634 sc->sc_slave = sh->sh_slave; 635 636 error = (*tag->sct_transfer)(tag->sct_cookie, st); 637 638 return error; 639 } 640 641 void 642 spi_wait(struct spi_transfer *st) 643 { 644 spi_handle_t sh = st->st_spiprivate; 645 646 mutex_enter(&st->st_lock); 647 while (!(st->st_flags & SPI_F_DONE)) { 648 cv_wait(&st->st_cv, &st->st_lock); 649 } 650 mutex_exit(&st->st_lock); 651 cv_destroy(&st->st_cv); 652 mutex_destroy(&st->st_lock); 653 654 /* 655 * End transaction 656 */ 657 spi_release(sh); 658 } 659 660 void 661 spi_done(struct spi_transfer *st, int err) 662 { 663 664 mutex_enter(&st->st_lock); 665 if ((st->st_errno = err) != 0) { 666 st->st_flags |= SPI_F_ERROR; 667 } 668 st->st_flags |= SPI_F_DONE; 669 if (st->st_done != NULL) { 670 (*st->st_done)(st); 671 } else { 672 cv_broadcast(&st->st_cv); 673 } 674 mutex_exit(&st->st_lock); 675 } 676 677 /* 678 * Some convenience routines. These routines block until the work 679 * is done. 680 * 681 * spi_recv - receives data from the bus 682 * 683 * spi_send - sends data to the bus 684 * 685 * spi_send_recv - sends data to the bus, and then receives. Note that this is 686 * done synchronously, i.e. send a command and get the response. This is 687 * not full duplex. If you want full duplex, you can't use these convenience 688 * wrappers. 689 */ 690 int 691 spi_recv(spi_handle_t sh, int cnt, uint8_t *data) 692 { 693 struct spi_transfer trans; 694 struct spi_chunk chunk; 695 696 spi_transfer_init(&trans); 697 spi_chunk_init(&chunk, cnt, NULL, data); 698 spi_transfer_add(&trans, &chunk); 699 700 /* enqueue it and wait for it to complete */ 701 spi_transfer(sh, &trans); 702 spi_wait(&trans); 703 704 if (trans.st_flags & SPI_F_ERROR) 705 return trans.st_errno; 706 707 return 0; 708 } 709 710 int 711 spi_send(spi_handle_t sh, int cnt, const uint8_t *data) 712 { 713 struct spi_transfer trans; 714 struct spi_chunk chunk; 715 716 spi_transfer_init(&trans); 717 spi_chunk_init(&chunk, cnt, data, NULL); 718 spi_transfer_add(&trans, &chunk); 719 720 /* enqueue it and wait for it to complete */ 721 spi_transfer(sh, &trans); 722 spi_wait(&trans); 723 724 if (trans.st_flags & SPI_F_ERROR) 725 return trans.st_errno; 726 727 return 0; 728 } 729 730 int 731 spi_send_recv(spi_handle_t sh, int scnt, const uint8_t *snd, 732 int rcnt, uint8_t *rcv) 733 { 734 struct spi_transfer trans; 735 struct spi_chunk chunk1, chunk2; 736 737 spi_transfer_init(&trans); 738 spi_chunk_init(&chunk1, scnt, snd, NULL); 739 spi_chunk_init(&chunk2, rcnt, NULL, rcv); 740 spi_transfer_add(&trans, &chunk1); 741 spi_transfer_add(&trans, &chunk2); 742 743 /* enqueue it and wait for it to complete */ 744 spi_transfer(sh, &trans); 745 spi_wait(&trans); 746 747 if (trans.st_flags & SPI_F_ERROR) 748 return trans.st_errno; 749 750 return 0; 751 } 752