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