1310645Sgonzo/*- 2310645Sgonzo * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3310645Sgonzo * All rights reserved. 4310645Sgonzo * 5310645Sgonzo * Redistribution and use in source and binary forms, with or without 6310645Sgonzo * modification, are permitted provided that the following conditions 7310645Sgonzo * are met: 8310645Sgonzo * 1. Redistributions of source code must retain the above copyright 9310645Sgonzo * notice, this list of conditions and the following disclaimer. 10310645Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 11310645Sgonzo * notice, this list of conditions and the following disclaimer in the 12310645Sgonzo * documentation and/or other materials provided with the distribution. 13310645Sgonzo * 14310645Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15310645Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16310645Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17310645Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18310645Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19310645Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20310645Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21310645Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22310645Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23310645Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24310645Sgonzo * SUCH DAMAGE. 25310645Sgonzo */ 26310645Sgonzo 27310645Sgonzo#include <sys/cdefs.h> 28310645Sgonzo__FBSDID("$FreeBSD: stable/11/sys/dev/intel/spi.c 338693 2018-09-15 13:07:43Z kib $"); 29310645Sgonzo 30310645Sgonzo#include "opt_acpi.h" 31310645Sgonzo 32310645Sgonzo#include <sys/param.h> 33310645Sgonzo#include <sys/bus.h> 34310645Sgonzo#include <sys/kernel.h> 35310645Sgonzo#include <sys/module.h> 36310645Sgonzo#include <sys/proc.h> 37310645Sgonzo#include <sys/rman.h> 38310645Sgonzo 39310645Sgonzo#include <machine/bus.h> 40310645Sgonzo#include <machine/resource.h> 41310645Sgonzo 42310645Sgonzo#include <dev/spibus/spi.h> 43310645Sgonzo#include <dev/spibus/spibusvar.h> 44310645Sgonzo 45310645Sgonzo#include <contrib/dev/acpica/include/acpi.h> 46310645Sgonzo#include <contrib/dev/acpica/include/accommon.h> 47310645Sgonzo 48310645Sgonzo#include <dev/acpica/acpivar.h> 49310645Sgonzo 50310645Sgonzo#include "spibus_if.h" 51310645Sgonzo 52310645Sgonzo/** 53310645Sgonzo * Macros for driver mutex locking 54310645Sgonzo */ 55310645Sgonzo#define INTELSPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 56310645Sgonzo#define INTELSPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 57310645Sgonzo#define INTELSPI_LOCK_INIT(_sc) \ 58310645Sgonzo mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ 59310645Sgonzo "intelspi", MTX_DEF) 60310645Sgonzo#define INTELSPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) 61310645Sgonzo#define INTELSPI_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 62310645Sgonzo#define INTELSPI_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) 63310645Sgonzo 64310645Sgonzo#define INTELSPI_WRITE(_sc, _off, _val) \ 65310645Sgonzo bus_write_4((_sc)->sc_mem_res, (_off), (_val)) 66310645Sgonzo#define INTELSPI_READ(_sc, _off) \ 67310645Sgonzo bus_read_4((_sc)->sc_mem_res, (_off)) 68310645Sgonzo 69310645Sgonzo#define INTELSPI_BUSY 0x1 70310645Sgonzo#define TX_FIFO_THRESHOLD 2 71310645Sgonzo#define RX_FIFO_THRESHOLD 2 72310645Sgonzo#define CLOCK_DIV_10MHZ 5 73310645Sgonzo#define DATA_SIZE_8BITS 8 74310645Sgonzo 75310645Sgonzo#define CS_LOW 0 76310645Sgonzo#define CS_HIGH 1 77310645Sgonzo 78310645Sgonzo#define INTELSPI_SSPREG_SSCR0 0x0 79310645Sgonzo#define SSCR0_SCR(n) (((n) - 1) << 8) 80310645Sgonzo#define SSCR0_SSE (1 << 7) 81310645Sgonzo#define SSCR0_FRF_SPI (0 << 4) 82310645Sgonzo#define SSCR0_DSS(n) (((n) - 1) << 0) 83310645Sgonzo#define INTELSPI_SSPREG_SSCR1 0x4 84310645Sgonzo#define SSCR1_TINTE (1 << 19) 85310645Sgonzo#define SSCR1_RFT(n) (((n) - 1) << 10) 86310645Sgonzo#define SSCR1_RFT_MASK (0xf << 10) 87310645Sgonzo#define SSCR1_TFT(n) (((n) - 1) << 6) 88310645Sgonzo#define SSCR1_SPI_SPH (1 << 4) 89310645Sgonzo#define SSCR1_SPI_SPO (1 << 3) 90310645Sgonzo#define SSCR1_MODE_MASK (SSCR1_SPI_SPO | SSCR1_SPI_SPH) 91310645Sgonzo#define SSCR1_MODE_0 (0) 92310645Sgonzo#define SSCR1_MODE_1 (SSCR1_SPI_SPH) 93310645Sgonzo#define SSCR1_MODE_2 (SSCR1_SPI_SPO) 94310645Sgonzo#define SSCR1_MODE_3 (SSCR1_SPI_SPO | SSCR1_SPI_SPH) 95310645Sgonzo#define SSCR1_TIE (1 << 1) 96310645Sgonzo#define SSCR1_RIE (1 << 0) 97310645Sgonzo#define INTELSPI_SSPREG_SSSR 0x8 98310645Sgonzo#define SSSR_RFL_MASK (0xf << 12) 99310645Sgonzo#define SSSR_TFL_MASK (0xf << 8) 100310645Sgonzo#define SSSR_RNE (1 << 3) 101310645Sgonzo#define SSSR_TNF (1 << 2) 102310645Sgonzo#define INTELSPI_SSPREG_SSITR 0xC 103310645Sgonzo#define INTELSPI_SSPREG_SSDR 0x10 104310645Sgonzo#define INTELSPI_SSPREG_SSTO 0x28 105310645Sgonzo#define INTELSPI_SSPREG_SSPSP 0x2C 106310645Sgonzo#define INTELSPI_SSPREG_SSTSA 0x30 107310645Sgonzo#define INTELSPI_SSPREG_SSRSA 0x34 108310645Sgonzo#define INTELSPI_SSPREG_SSTSS 0x38 109310645Sgonzo#define INTELSPI_SSPREG_SSACD 0x3C 110310645Sgonzo#define INTELSPI_SSPREG_ITF 0x40 111310645Sgonzo#define INTELSPI_SSPREG_SITF 0x44 112310645Sgonzo#define INTELSPI_SSPREG_SIRF 0x48 113310645Sgonzo#define INTELSPI_SSPREG_PRV_CLOCK_PARAMS 0x400 114310645Sgonzo#define INTELSPI_SSPREG_RESETS 0x404 115310645Sgonzo#define INTELSPI_SSPREG_GENERAL 0x408 116310645Sgonzo#define INTELSPI_SSPREG_SSP_REG 0x40C 117310645Sgonzo#define INTELSPI_SSPREG_SPI_CS_CTRL 0x418 118310645Sgonzo#define SPI_CS_CTRL_CS_MASK (3) 119310645Sgonzo#define SPI_CS_CTRL_SW_MODE (1 << 0) 120310645Sgonzo#define SPI_CS_CTRL_HW_MODE (1 << 0) 121310645Sgonzo#define SPI_CS_CTRL_CS_HIGH (1 << 1) 122310645Sgonzo#define SPI_CS_CTRL_CS_LOW (0 << 1) 123310645Sgonzo 124310645Sgonzostruct intelspi_softc { 125310645Sgonzo ACPI_HANDLE sc_handle; 126310645Sgonzo device_t sc_dev; 127310645Sgonzo struct mtx sc_mtx; 128310645Sgonzo int sc_mem_rid; 129310645Sgonzo struct resource *sc_mem_res; 130310645Sgonzo int sc_irq_rid; 131310645Sgonzo struct resource *sc_irq_res; 132310645Sgonzo void *sc_irq_ih; 133310645Sgonzo struct spi_command *sc_cmd; 134310645Sgonzo uint32_t sc_len; 135310645Sgonzo uint32_t sc_read; 136310645Sgonzo uint32_t sc_flags; 137310645Sgonzo uint32_t sc_written; 138310645Sgonzo}; 139310645Sgonzo 140310645Sgonzostatic int intelspi_probe(device_t dev); 141310645Sgonzostatic int intelspi_attach(device_t dev); 142310645Sgonzostatic int intelspi_detach(device_t dev); 143310645Sgonzostatic void intelspi_intr(void *); 144310645Sgonzo 145310645Sgonzostatic int 146310645Sgonzointelspi_txfifo_full(struct intelspi_softc *sc) 147310645Sgonzo{ 148310645Sgonzo uint32_t sssr; 149310645Sgonzo 150310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 151310645Sgonzo 152310645Sgonzo sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 153310645Sgonzo if (sssr & SSSR_TNF) 154310645Sgonzo return (0); 155310645Sgonzo 156310645Sgonzo return (1); 157310645Sgonzo} 158310645Sgonzo 159310645Sgonzostatic int 160310645Sgonzointelspi_rxfifo_empty(struct intelspi_softc *sc) 161310645Sgonzo{ 162310645Sgonzo uint32_t sssr; 163310645Sgonzo 164310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 165310645Sgonzo 166310645Sgonzo sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 167310645Sgonzo if (sssr & SSSR_RNE) 168310645Sgonzo return (0); 169310645Sgonzo else 170310645Sgonzo return (1); 171310645Sgonzo} 172310645Sgonzo 173310645Sgonzostatic void 174310645Sgonzointelspi_fill_tx_fifo(struct intelspi_softc *sc) 175310645Sgonzo{ 176310645Sgonzo struct spi_command *cmd; 177310645Sgonzo uint32_t written; 178310645Sgonzo uint8_t *data; 179310645Sgonzo 180310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 181310645Sgonzo 182310645Sgonzo cmd = sc->sc_cmd; 183310645Sgonzo while (sc->sc_written < sc->sc_len && 184310645Sgonzo !intelspi_txfifo_full(sc)) { 185310645Sgonzo data = (uint8_t *)cmd->tx_cmd; 186310645Sgonzo written = sc->sc_written++; 187310645Sgonzo 188310645Sgonzo if (written >= cmd->tx_cmd_sz) { 189310645Sgonzo data = (uint8_t *)cmd->tx_data; 190310645Sgonzo written -= cmd->tx_cmd_sz; 191310645Sgonzo } 192310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSDR, data[written]); 193310645Sgonzo } 194310645Sgonzo} 195310645Sgonzo 196310645Sgonzostatic void 197310645Sgonzointelspi_drain_rx_fifo(struct intelspi_softc *sc) 198310645Sgonzo{ 199310645Sgonzo struct spi_command *cmd; 200310645Sgonzo uint32_t read; 201310645Sgonzo uint8_t *data; 202310645Sgonzo 203310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 204310645Sgonzo 205310645Sgonzo cmd = sc->sc_cmd; 206310645Sgonzo while (sc->sc_read < sc->sc_len && 207310645Sgonzo !intelspi_rxfifo_empty(sc)) { 208310645Sgonzo data = (uint8_t *)cmd->rx_cmd; 209310645Sgonzo read = sc->sc_read++; 210310645Sgonzo if (read >= cmd->rx_cmd_sz) { 211310645Sgonzo data = (uint8_t *)cmd->rx_data; 212310645Sgonzo read -= cmd->rx_cmd_sz; 213310645Sgonzo } 214310645Sgonzo data[read] = INTELSPI_READ(sc, INTELSPI_SSPREG_SSDR) & 0xff; 215310645Sgonzo } 216310645Sgonzo} 217310645Sgonzo 218310645Sgonzostatic int 219310645Sgonzointelspi_transaction_done(struct intelspi_softc *sc) 220310645Sgonzo{ 221310645Sgonzo int txfifo_empty; 222310645Sgonzo uint32_t sssr; 223310645Sgonzo 224310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 225310645Sgonzo 226310645Sgonzo if (sc->sc_written != sc->sc_len || 227310645Sgonzo sc->sc_read != sc->sc_len) 228310645Sgonzo return (0); 229310645Sgonzo 230310645Sgonzo sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 231310645Sgonzo txfifo_empty = ((sssr & SSSR_TFL_MASK) == 0) && 232310645Sgonzo (sssr & SSSR_TNF); 233310645Sgonzo 234310645Sgonzo if (txfifo_empty && !(sssr & SSSR_RNE)) 235310645Sgonzo return (1); 236310645Sgonzo 237310645Sgonzo return (0); 238310645Sgonzo} 239310645Sgonzo 240310645Sgonzostatic int 241310645Sgonzointelspi_transact(struct intelspi_softc *sc) 242310645Sgonzo{ 243310645Sgonzo 244310645Sgonzo INTELSPI_ASSERT_LOCKED(sc); 245310645Sgonzo 246310645Sgonzo /* TX - Fill up the FIFO. */ 247310645Sgonzo intelspi_fill_tx_fifo(sc); 248310645Sgonzo 249310645Sgonzo /* RX - Drain the FIFO. */ 250310645Sgonzo intelspi_drain_rx_fifo(sc); 251310645Sgonzo 252310645Sgonzo /* Check for end of transfer. */ 253310645Sgonzo return intelspi_transaction_done(sc); 254310645Sgonzo} 255310645Sgonzo 256310645Sgonzostatic void 257310645Sgonzointelspi_intr(void *arg) 258310645Sgonzo{ 259310645Sgonzo struct intelspi_softc *sc; 260310645Sgonzo uint32_t reg; 261310645Sgonzo 262310645Sgonzo sc = (struct intelspi_softc *)arg; 263310645Sgonzo 264310645Sgonzo INTELSPI_LOCK(sc); 265310645Sgonzo if ((sc->sc_flags & INTELSPI_BUSY) == 0) { 266310645Sgonzo INTELSPI_UNLOCK(sc); 267310645Sgonzo return; 268310645Sgonzo } 269310645Sgonzo 270310645Sgonzo /* Check if SSP if off */ 271310645Sgonzo reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 272310645Sgonzo if (reg == 0xffffffffU) { 273310645Sgonzo INTELSPI_UNLOCK(sc); 274310645Sgonzo return; 275310645Sgonzo } 276310645Sgonzo 277310645Sgonzo /* Check for end of transfer. */ 278310645Sgonzo if (intelspi_transact(sc)) { 279310645Sgonzo /* Disable interrupts */ 280310645Sgonzo reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 281310645Sgonzo reg &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 282310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg); 283310645Sgonzo wakeup(sc->sc_dev); 284310645Sgonzo } 285310645Sgonzo 286310645Sgonzo INTELSPI_UNLOCK(sc); 287310645Sgonzo} 288310645Sgonzo 289310645Sgonzostatic void 290310645Sgonzointelspi_init(struct intelspi_softc *sc) 291310645Sgonzo{ 292310645Sgonzo uint32_t reg; 293310645Sgonzo 294310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0); 295310645Sgonzo 296310645Sgonzo /* Manual CS control */ 297310645Sgonzo reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL); 298310645Sgonzo reg &= ~(SPI_CS_CTRL_CS_MASK); 299310645Sgonzo reg |= (SPI_CS_CTRL_SW_MODE | SPI_CS_CTRL_CS_HIGH); 300310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg); 301310645Sgonzo 302310645Sgonzo /* Set TX/RX FIFO IRQ threshold levels */ 303310645Sgonzo reg = SSCR1_TFT(TX_FIFO_THRESHOLD) | SSCR1_RFT(RX_FIFO_THRESHOLD); 304310645Sgonzo /* 305310645Sgonzo * Set SPI mode. This should be part of transaction or sysctl 306310645Sgonzo */ 307310645Sgonzo reg |= SSCR1_MODE_0; 308310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg); 309310645Sgonzo 310310645Sgonzo /* 311310645Sgonzo * Parent clock on Minowboard Turbot is 50MHz 312310645Sgonzo * divide it by 5 to set to more or less reasonable 313310645Sgonzo * value. But this should be part of transaction config 314310645Sgonzo * or sysctl 315310645Sgonzo */ 316310645Sgonzo reg = SSCR0_SCR(CLOCK_DIV_10MHZ); 317310645Sgonzo /* Put SSP in SPI mode */ 318310645Sgonzo reg |= SSCR0_FRF_SPI; 319310645Sgonzo /* Data size */ 320310645Sgonzo reg |= SSCR0_DSS(DATA_SIZE_8BITS); 321310645Sgonzo /* Enable SSP */ 322310645Sgonzo reg |= SSCR0_SSE; 323310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, reg); 324310645Sgonzo} 325310645Sgonzo 326310645Sgonzostatic void 327310645Sgonzointelspi_set_cs(struct intelspi_softc *sc, int level) 328310645Sgonzo{ 329310645Sgonzo uint32_t reg; 330310645Sgonzo 331310645Sgonzo reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL); 332310645Sgonzo reg &= ~(SPI_CS_CTRL_CS_MASK); 333310645Sgonzo reg |= SPI_CS_CTRL_SW_MODE; 334310645Sgonzo 335310645Sgonzo if (level == CS_HIGH) 336310645Sgonzo reg |= SPI_CS_CTRL_CS_HIGH; 337310645Sgonzo else 338310645Sgonzo reg |= SPI_CS_CTRL_CS_LOW; 339310645Sgonzo 340310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg); 341310645Sgonzo} 342310645Sgonzo 343310645Sgonzostatic int 344310645Sgonzointelspi_transfer(device_t dev, device_t child, struct spi_command *cmd) 345310645Sgonzo{ 346310645Sgonzo struct intelspi_softc *sc; 347310645Sgonzo int err; 348310645Sgonzo uint32_t sscr1; 349310645Sgonzo 350310645Sgonzo sc = device_get_softc(dev); 351310645Sgonzo err = 0; 352310645Sgonzo 353310645Sgonzo KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 354310645Sgonzo ("TX/RX command sizes should be equal")); 355310645Sgonzo KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 356310645Sgonzo ("TX/RX data sizes should be equal")); 357310645Sgonzo 358310645Sgonzo INTELSPI_LOCK(sc); 359310645Sgonzo 360310645Sgonzo /* If the controller is in use wait until it is available. */ 361310645Sgonzo while (sc->sc_flags & INTELSPI_BUSY) { 362310645Sgonzo err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0); 363310645Sgonzo if (err == EINTR) { 364310645Sgonzo INTELSPI_UNLOCK(sc); 365310645Sgonzo return (err); 366310645Sgonzo } 367310645Sgonzo } 368310645Sgonzo 369310645Sgonzo /* Now we have control over SPI controller. */ 370310645Sgonzo sc->sc_flags = INTELSPI_BUSY; 371310645Sgonzo 372310645Sgonzo /* Save a pointer to the SPI command. */ 373310645Sgonzo sc->sc_cmd = cmd; 374310645Sgonzo sc->sc_read = 0; 375310645Sgonzo sc->sc_written = 0; 376310645Sgonzo sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; 377310645Sgonzo 378310645Sgonzo /* Enable CS */ 379310645Sgonzo intelspi_set_cs(sc, CS_LOW); 380310645Sgonzo /* Transfer as much as possible to FIFOs */ 381310645Sgonzo if (!intelspi_transact(sc)) { 382310645Sgonzo /* If FIFO is not large enough - enable interrupts */ 383310645Sgonzo sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 384310645Sgonzo sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 385310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); 386310645Sgonzo 387310645Sgonzo /* and wait for transaction to complete */ 388310645Sgonzo err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2); 389310645Sgonzo } 390310645Sgonzo 391310645Sgonzo /* de-asser CS */ 392310645Sgonzo intelspi_set_cs(sc, CS_HIGH); 393310645Sgonzo 394310645Sgonzo /* Clear transaction details */ 395310645Sgonzo sc->sc_cmd = NULL; 396310645Sgonzo sc->sc_read = 0; 397310645Sgonzo sc->sc_written = 0; 398310645Sgonzo sc->sc_len = 0; 399310645Sgonzo 400310645Sgonzo /* Make sure the SPI engine and interrupts are disabled. */ 401310645Sgonzo sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 402310645Sgonzo sscr1 &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 403310645Sgonzo INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); 404310645Sgonzo 405310645Sgonzo /* Release the controller and wakeup the next thread waiting for it. */ 406310645Sgonzo sc->sc_flags = 0; 407310645Sgonzo wakeup_one(dev); 408310645Sgonzo INTELSPI_UNLOCK(sc); 409310645Sgonzo 410310645Sgonzo /* 411310645Sgonzo * Check for transfer timeout. The SPI controller doesn't 412310645Sgonzo * return errors. 413310645Sgonzo */ 414310645Sgonzo if (err == EWOULDBLOCK) { 415310645Sgonzo device_printf(sc->sc_dev, "transfer timeout\n"); 416310645Sgonzo err = EIO; 417310645Sgonzo } 418310645Sgonzo 419310645Sgonzo return (err); 420310645Sgonzo} 421310645Sgonzo 422310645Sgonzostatic int 423310645Sgonzointelspi_probe(device_t dev) 424310645Sgonzo{ 425310645Sgonzo static char *gpio_ids[] = { "80860F0E", NULL }; 426310645Sgonzo 427310645Sgonzo if (acpi_disabled("spi") || 428310645Sgonzo ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids) == NULL) 429310645Sgonzo return (ENXIO); 430310645Sgonzo 431310645Sgonzo device_set_desc(dev, "Intel SPI Controller"); 432310645Sgonzo return (0); 433310645Sgonzo} 434310645Sgonzo 435310645Sgonzostatic int 436310645Sgonzointelspi_attach(device_t dev) 437310645Sgonzo{ 438310645Sgonzo struct intelspi_softc *sc; 439310645Sgonzo 440310645Sgonzo sc = device_get_softc(dev); 441310645Sgonzo sc->sc_dev = dev; 442310645Sgonzo sc->sc_handle = acpi_get_handle(dev); 443310645Sgonzo 444310645Sgonzo INTELSPI_LOCK_INIT(sc); 445310645Sgonzo 446310645Sgonzo sc->sc_mem_rid = 0; 447310645Sgonzo sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, 448310645Sgonzo SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE); 449310645Sgonzo if (sc->sc_mem_res == NULL) { 450310645Sgonzo device_printf(dev, "can't allocate memory resource\n"); 451310645Sgonzo goto error; 452310645Sgonzo } 453310645Sgonzo 454310645Sgonzo sc->sc_irq_rid = 0; 455310645Sgonzo sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev, 456310645Sgonzo SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE); 457310645Sgonzo if (sc->sc_irq_res == NULL) { 458310645Sgonzo device_printf(dev, "can't allocate IRQ resource\n"); 459310645Sgonzo goto error; 460310645Sgonzo } 461310645Sgonzo 462310645Sgonzo /* Hook up our interrupt handler. */ 463310645Sgonzo if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 464310645Sgonzo NULL, intelspi_intr, sc, &sc->sc_irq_ih)) { 465310645Sgonzo device_printf(dev, "cannot setup the interrupt handler\n"); 466310645Sgonzo goto error; 467310645Sgonzo } 468310645Sgonzo 469310645Sgonzo intelspi_init(sc); 470310645Sgonzo 471310645Sgonzo device_add_child(dev, "spibus", -1); 472310645Sgonzo 473310645Sgonzo return (bus_generic_attach(dev)); 474310645Sgonzo 475310645Sgonzoerror: 476310645Sgonzo INTELSPI_LOCK_DESTROY(sc); 477310645Sgonzo 478310645Sgonzo if (sc->sc_mem_res != NULL) 479310645Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 480310645Sgonzo sc->sc_mem_rid, sc->sc_mem_res); 481310645Sgonzo 482310645Sgonzo if (sc->sc_irq_res != NULL) 483338678Smarkj bus_release_resource(dev, SYS_RES_IRQ, 484310645Sgonzo sc->sc_irq_rid, sc->sc_irq_res); 485310645Sgonzo 486310645Sgonzo return (ENXIO); 487310645Sgonzo} 488310645Sgonzo 489310645Sgonzostatic int 490310645Sgonzointelspi_detach(device_t dev) 491310645Sgonzo{ 492310645Sgonzo struct intelspi_softc *sc; 493310645Sgonzo 494310645Sgonzo sc = device_get_softc(dev); 495310645Sgonzo 496310645Sgonzo INTELSPI_LOCK_DESTROY(sc); 497310645Sgonzo 498310645Sgonzo if (sc->sc_irq_ih) 499310645Sgonzo bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih); 500310645Sgonzo 501310645Sgonzo if (sc->sc_mem_res != NULL) 502310645Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 503310645Sgonzo sc->sc_mem_rid, sc->sc_mem_res); 504310645Sgonzo 505310645Sgonzo if (sc->sc_irq_res != NULL) 506338678Smarkj bus_release_resource(dev, SYS_RES_IRQ, 507310645Sgonzo sc->sc_irq_rid, sc->sc_irq_res); 508310645Sgonzo 509338693Skib return (bus_generic_detach(dev)); 510310645Sgonzo} 511310645Sgonzo 512310645Sgonzostatic device_method_t intelspi_methods[] = { 513310645Sgonzo /* Device interface */ 514310645Sgonzo DEVMETHOD(device_probe, intelspi_probe), 515310645Sgonzo DEVMETHOD(device_attach, intelspi_attach), 516310645Sgonzo DEVMETHOD(device_detach, intelspi_detach), 517310645Sgonzo 518310645Sgonzo /* SPI interface */ 519310645Sgonzo DEVMETHOD(spibus_transfer, intelspi_transfer), 520310645Sgonzo 521310645Sgonzo DEVMETHOD_END 522310645Sgonzo}; 523310645Sgonzo 524310645Sgonzostatic driver_t intelspi_driver = { 525310645Sgonzo "spi", 526310645Sgonzo intelspi_methods, 527310645Sgonzo sizeof(struct intelspi_softc), 528310645Sgonzo}; 529310645Sgonzo 530310645Sgonzostatic devclass_t intelspi_devclass; 531310645SgonzoDRIVER_MODULE(intelspi, acpi, intelspi_driver, intelspi_devclass, 0, 0); 532310645SgonzoMODULE_DEPEND(intelspi, acpi, 1, 1, 1); 533310645SgonzoMODULE_DEPEND(intelspi, spibus, 1, 1, 1); 534