1d786719dSOleksandr Tymoshenko /*- 21f40866fSVal Packett * SPDX-License-Identifier: BSD-2-Clause 31f40866fSVal Packett * 4d786719dSOleksandr Tymoshenko * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 5d786719dSOleksandr Tymoshenko * All rights reserved. 6d786719dSOleksandr Tymoshenko * 7d786719dSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 8d786719dSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 9d786719dSOleksandr Tymoshenko * are met: 10d786719dSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 11d786719dSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 12d786719dSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 13d786719dSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 14d786719dSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 15d786719dSOleksandr Tymoshenko * 16d786719dSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17d786719dSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18d786719dSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19d786719dSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20d786719dSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21d786719dSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22d786719dSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23d786719dSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24d786719dSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25d786719dSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26d786719dSOleksandr Tymoshenko * SUCH DAMAGE. 27d786719dSOleksandr Tymoshenko */ 28d786719dSOleksandr Tymoshenko 29d786719dSOleksandr Tymoshenko #include "opt_acpi.h" 30d786719dSOleksandr Tymoshenko 31d786719dSOleksandr Tymoshenko #include <sys/param.h> 32d786719dSOleksandr Tymoshenko #include <sys/bus.h> 335adcec04SVladimir Kondratyev #include <sys/kdb.h> 34d786719dSOleksandr Tymoshenko #include <sys/kernel.h> 35d786719dSOleksandr Tymoshenko #include <sys/module.h> 36d786719dSOleksandr Tymoshenko #include <sys/proc.h> 37d786719dSOleksandr Tymoshenko #include <sys/rman.h> 38d786719dSOleksandr Tymoshenko 39d786719dSOleksandr Tymoshenko #include <machine/bus.h> 40d786719dSOleksandr Tymoshenko #include <machine/resource.h> 41d786719dSOleksandr Tymoshenko 42d786719dSOleksandr Tymoshenko #include <dev/spibus/spi.h> 43d786719dSOleksandr Tymoshenko #include <dev/spibus/spibusvar.h> 44d786719dSOleksandr Tymoshenko 451f40866fSVal Packett #include <dev/intel/spi.h> 46d786719dSOleksandr Tymoshenko 47d786719dSOleksandr Tymoshenko /** 48d786719dSOleksandr Tymoshenko * Macros for driver mutex locking 49d786719dSOleksandr Tymoshenko */ 505adcec04SVladimir Kondratyev #define INTELSPI_IN_POLLING_MODE() (SCHEDULER_STOPPED() || kdb_active) 515adcec04SVladimir Kondratyev #define INTELSPI_LOCK(_sc) do { \ 525adcec04SVladimir Kondratyev if(!INTELSPI_IN_POLLING_MODE()) \ 535adcec04SVladimir Kondratyev mtx_lock(&(_sc)->sc_mtx); \ 545adcec04SVladimir Kondratyev } while (0) 555adcec04SVladimir Kondratyev #define INTELSPI_UNLOCK(_sc) do { \ 565adcec04SVladimir Kondratyev if(!INTELSPI_IN_POLLING_MODE()) \ 575adcec04SVladimir Kondratyev mtx_unlock(&(_sc)->sc_mtx); \ 585adcec04SVladimir Kondratyev } while (0) 59d786719dSOleksandr Tymoshenko #define INTELSPI_LOCK_INIT(_sc) \ 60d786719dSOleksandr Tymoshenko mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ 61d786719dSOleksandr Tymoshenko "intelspi", MTX_DEF) 62d786719dSOleksandr Tymoshenko #define INTELSPI_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) 635adcec04SVladimir Kondratyev #define INTELSPI_ASSERT_LOCKED(_sc) do { \ 645adcec04SVladimir Kondratyev if(!INTELSPI_IN_POLLING_MODE()) \ 655adcec04SVladimir Kondratyev mtx_assert(&(_sc)->sc_mtx, MA_OWNED); \ 665adcec04SVladimir Kondratyev } while (0) 675adcec04SVladimir Kondratyev #define INTELSPI_ASSERT_UNLOCKED(_sc) do { \ 685adcec04SVladimir Kondratyev if(!INTELSPI_IN_POLLING_MODE()) \ 695adcec04SVladimir Kondratyev mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED);\ 705adcec04SVladimir Kondratyev } while (0) 71d786719dSOleksandr Tymoshenko 72d786719dSOleksandr Tymoshenko #define INTELSPI_WRITE(_sc, _off, _val) \ 73d786719dSOleksandr Tymoshenko bus_write_4((_sc)->sc_mem_res, (_off), (_val)) 74d786719dSOleksandr Tymoshenko #define INTELSPI_READ(_sc, _off) \ 75d786719dSOleksandr Tymoshenko bus_read_4((_sc)->sc_mem_res, (_off)) 76d786719dSOleksandr Tymoshenko 77d786719dSOleksandr Tymoshenko #define INTELSPI_BUSY 0x1 78d786719dSOleksandr Tymoshenko #define TX_FIFO_THRESHOLD 2 79d786719dSOleksandr Tymoshenko #define RX_FIFO_THRESHOLD 2 80d786719dSOleksandr Tymoshenko #define CLOCK_DIV_10MHZ 5 81d786719dSOleksandr Tymoshenko #define DATA_SIZE_8BITS 8 821f40866fSVal Packett #define MAX_CLOCK_RATE 50000000 83d786719dSOleksandr Tymoshenko 84d786719dSOleksandr Tymoshenko #define CS_LOW 0 85d786719dSOleksandr Tymoshenko #define CS_HIGH 1 86d786719dSOleksandr Tymoshenko 87d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSCR0 0x0 881f40866fSVal Packett #define SSCR0_SCR(n) ((((n) - 1) & 0xfff) << 8) 89d786719dSOleksandr Tymoshenko #define SSCR0_SSE (1 << 7) 90d786719dSOleksandr Tymoshenko #define SSCR0_FRF_SPI (0 << 4) 91d786719dSOleksandr Tymoshenko #define SSCR0_DSS(n) (((n) - 1) << 0) 92d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSCR1 0x4 93d786719dSOleksandr Tymoshenko #define SSCR1_TINTE (1 << 19) 94d786719dSOleksandr Tymoshenko #define SSCR1_RFT(n) (((n) - 1) << 10) 95d786719dSOleksandr Tymoshenko #define SSCR1_RFT_MASK (0xf << 10) 96d786719dSOleksandr Tymoshenko #define SSCR1_TFT(n) (((n) - 1) << 6) 97d786719dSOleksandr Tymoshenko #define SSCR1_SPI_SPH (1 << 4) 98d786719dSOleksandr Tymoshenko #define SSCR1_SPI_SPO (1 << 3) 99d786719dSOleksandr Tymoshenko #define SSCR1_MODE_MASK (SSCR1_SPI_SPO | SSCR1_SPI_SPH) 100d786719dSOleksandr Tymoshenko #define SSCR1_TIE (1 << 1) 101d786719dSOleksandr Tymoshenko #define SSCR1_RIE (1 << 0) 102d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSSR 0x8 103d786719dSOleksandr Tymoshenko #define SSSR_RFL_MASK (0xf << 12) 104d786719dSOleksandr Tymoshenko #define SSSR_TFL_MASK (0xf << 8) 105d786719dSOleksandr Tymoshenko #define SSSR_RNE (1 << 3) 106d786719dSOleksandr Tymoshenko #define SSSR_TNF (1 << 2) 107d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSITR 0xC 108d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSDR 0x10 109d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSTO 0x28 110d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSPSP 0x2C 111d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSTSA 0x30 112d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSRSA 0x34 113d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSTSS 0x38 114d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SSACD 0x3C 115d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_ITF 0x40 116d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SITF 0x44 117d786719dSOleksandr Tymoshenko #define INTELSPI_SSPREG_SIRF 0x48 1181f40866fSVal Packett #define SPI_CS_CTRL(sc) \ 1191f40866fSVal Packett (intelspi_infos[sc->sc_vers].reg_lpss_base + \ 1201f40866fSVal Packett intelspi_infos[sc->sc_vers].reg_cs_ctrl) 121d786719dSOleksandr Tymoshenko #define SPI_CS_CTRL_CS_MASK (3) 122d786719dSOleksandr Tymoshenko #define SPI_CS_CTRL_SW_MODE (1 << 0) 123d786719dSOleksandr Tymoshenko #define SPI_CS_CTRL_HW_MODE (1 << 0) 124d786719dSOleksandr Tymoshenko #define SPI_CS_CTRL_CS_HIGH (1 << 1) 125d786719dSOleksandr Tymoshenko 126b109946dSVladimir Kondratyev #define INTELSPI_RESETS 0x204 127b109946dSVladimir Kondratyev #define INTELSPI_RESET_HOST (1 << 0) | (1 << 1) 128b109946dSVladimir Kondratyev #define INTELSPI_RESET_DMA (1 << 2) 129b109946dSVladimir Kondratyev 130f56dbe7aSVladimir Kondratyev /* Same order as intelspi_vers */ 131f56dbe7aSVladimir Kondratyev static const struct intelspi_info { 132f56dbe7aSVladimir Kondratyev uint32_t reg_lpss_base; 133f56dbe7aSVladimir Kondratyev uint32_t reg_cs_ctrl; 134f56dbe7aSVladimir Kondratyev } intelspi_infos[] = { 135f56dbe7aSVladimir Kondratyev [SPI_BAYTRAIL] = { 136f56dbe7aSVladimir Kondratyev .reg_lpss_base = 0x400, 137f56dbe7aSVladimir Kondratyev .reg_cs_ctrl = 0x18, 138f56dbe7aSVladimir Kondratyev }, 139f56dbe7aSVladimir Kondratyev [SPI_BRASWELL] = { 140f56dbe7aSVladimir Kondratyev .reg_lpss_base = 0x400, 141f56dbe7aSVladimir Kondratyev .reg_cs_ctrl = 0x18, 142f56dbe7aSVladimir Kondratyev }, 143f56dbe7aSVladimir Kondratyev [SPI_LYNXPOINT] = { 144f56dbe7aSVladimir Kondratyev .reg_lpss_base = 0x800, 145f56dbe7aSVladimir Kondratyev .reg_cs_ctrl = 0x18, 146f56dbe7aSVladimir Kondratyev }, 147f56dbe7aSVladimir Kondratyev [SPI_SUNRISEPOINT] = { 148f56dbe7aSVladimir Kondratyev .reg_lpss_base = 0x200, 149f56dbe7aSVladimir Kondratyev .reg_cs_ctrl = 0x24, 150f56dbe7aSVladimir Kondratyev }, 151f56dbe7aSVladimir Kondratyev }; 152f56dbe7aSVladimir Kondratyev 153d786719dSOleksandr Tymoshenko static void intelspi_intr(void *); 154d786719dSOleksandr Tymoshenko 155d786719dSOleksandr Tymoshenko static int 156d786719dSOleksandr Tymoshenko intelspi_txfifo_full(struct intelspi_softc *sc) 157d786719dSOleksandr Tymoshenko { 158d786719dSOleksandr Tymoshenko uint32_t sssr; 159d786719dSOleksandr Tymoshenko 160d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 161d786719dSOleksandr Tymoshenko 162d786719dSOleksandr Tymoshenko sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 163d786719dSOleksandr Tymoshenko if (sssr & SSSR_TNF) 164d786719dSOleksandr Tymoshenko return (0); 165d786719dSOleksandr Tymoshenko 166d786719dSOleksandr Tymoshenko return (1); 167d786719dSOleksandr Tymoshenko } 168d786719dSOleksandr Tymoshenko 169d786719dSOleksandr Tymoshenko static int 170d786719dSOleksandr Tymoshenko intelspi_rxfifo_empty(struct intelspi_softc *sc) 171d786719dSOleksandr Tymoshenko { 172d786719dSOleksandr Tymoshenko uint32_t sssr; 173d786719dSOleksandr Tymoshenko 174d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 175d786719dSOleksandr Tymoshenko 176d786719dSOleksandr Tymoshenko sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 177d786719dSOleksandr Tymoshenko if (sssr & SSSR_RNE) 178d786719dSOleksandr Tymoshenko return (0); 179d786719dSOleksandr Tymoshenko else 180d786719dSOleksandr Tymoshenko return (1); 181d786719dSOleksandr Tymoshenko } 182d786719dSOleksandr Tymoshenko 183d786719dSOleksandr Tymoshenko static void 184d786719dSOleksandr Tymoshenko intelspi_fill_tx_fifo(struct intelspi_softc *sc) 185d786719dSOleksandr Tymoshenko { 186d786719dSOleksandr Tymoshenko struct spi_command *cmd; 187d786719dSOleksandr Tymoshenko uint32_t written; 188d786719dSOleksandr Tymoshenko uint8_t *data; 189d786719dSOleksandr Tymoshenko 190d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 191d786719dSOleksandr Tymoshenko 192d786719dSOleksandr Tymoshenko cmd = sc->sc_cmd; 193d786719dSOleksandr Tymoshenko while (sc->sc_written < sc->sc_len && 194d786719dSOleksandr Tymoshenko !intelspi_txfifo_full(sc)) { 195d786719dSOleksandr Tymoshenko data = (uint8_t *)cmd->tx_cmd; 196d786719dSOleksandr Tymoshenko written = sc->sc_written++; 197d786719dSOleksandr Tymoshenko 198d786719dSOleksandr Tymoshenko if (written >= cmd->tx_cmd_sz) { 199d786719dSOleksandr Tymoshenko data = (uint8_t *)cmd->tx_data; 200d786719dSOleksandr Tymoshenko written -= cmd->tx_cmd_sz; 201d786719dSOleksandr Tymoshenko } 202d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSDR, data[written]); 203d786719dSOleksandr Tymoshenko } 204d786719dSOleksandr Tymoshenko } 205d786719dSOleksandr Tymoshenko 206d786719dSOleksandr Tymoshenko static void 207d786719dSOleksandr Tymoshenko intelspi_drain_rx_fifo(struct intelspi_softc *sc) 208d786719dSOleksandr Tymoshenko { 209d786719dSOleksandr Tymoshenko struct spi_command *cmd; 210d786719dSOleksandr Tymoshenko uint32_t read; 211d786719dSOleksandr Tymoshenko uint8_t *data; 212d786719dSOleksandr Tymoshenko 213d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 214d786719dSOleksandr Tymoshenko 215d786719dSOleksandr Tymoshenko cmd = sc->sc_cmd; 216d786719dSOleksandr Tymoshenko while (sc->sc_read < sc->sc_len && 217d786719dSOleksandr Tymoshenko !intelspi_rxfifo_empty(sc)) { 218d786719dSOleksandr Tymoshenko data = (uint8_t *)cmd->rx_cmd; 219d786719dSOleksandr Tymoshenko read = sc->sc_read++; 220d786719dSOleksandr Tymoshenko if (read >= cmd->rx_cmd_sz) { 221d786719dSOleksandr Tymoshenko data = (uint8_t *)cmd->rx_data; 222d786719dSOleksandr Tymoshenko read -= cmd->rx_cmd_sz; 223d786719dSOleksandr Tymoshenko } 224d786719dSOleksandr Tymoshenko data[read] = INTELSPI_READ(sc, INTELSPI_SSPREG_SSDR) & 0xff; 225d786719dSOleksandr Tymoshenko } 226d786719dSOleksandr Tymoshenko } 227d786719dSOleksandr Tymoshenko 228d786719dSOleksandr Tymoshenko static int 229d786719dSOleksandr Tymoshenko intelspi_transaction_done(struct intelspi_softc *sc) 230d786719dSOleksandr Tymoshenko { 231d786719dSOleksandr Tymoshenko int txfifo_empty; 232d786719dSOleksandr Tymoshenko uint32_t sssr; 233d786719dSOleksandr Tymoshenko 234d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 235d786719dSOleksandr Tymoshenko 236d786719dSOleksandr Tymoshenko if (sc->sc_written != sc->sc_len || 237d786719dSOleksandr Tymoshenko sc->sc_read != sc->sc_len) 238d786719dSOleksandr Tymoshenko return (0); 239d786719dSOleksandr Tymoshenko 240d786719dSOleksandr Tymoshenko sssr = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 241d786719dSOleksandr Tymoshenko txfifo_empty = ((sssr & SSSR_TFL_MASK) == 0) && 242d786719dSOleksandr Tymoshenko (sssr & SSSR_TNF); 243d786719dSOleksandr Tymoshenko 244d786719dSOleksandr Tymoshenko if (txfifo_empty && !(sssr & SSSR_RNE)) 245d786719dSOleksandr Tymoshenko return (1); 246d786719dSOleksandr Tymoshenko 247d786719dSOleksandr Tymoshenko return (0); 248d786719dSOleksandr Tymoshenko } 249d786719dSOleksandr Tymoshenko 250d786719dSOleksandr Tymoshenko static int 251d786719dSOleksandr Tymoshenko intelspi_transact(struct intelspi_softc *sc) 252d786719dSOleksandr Tymoshenko { 253d786719dSOleksandr Tymoshenko 254d786719dSOleksandr Tymoshenko INTELSPI_ASSERT_LOCKED(sc); 255d786719dSOleksandr Tymoshenko 256d786719dSOleksandr Tymoshenko /* TX - Fill up the FIFO. */ 257d786719dSOleksandr Tymoshenko intelspi_fill_tx_fifo(sc); 258d786719dSOleksandr Tymoshenko 259d786719dSOleksandr Tymoshenko /* RX - Drain the FIFO. */ 260d786719dSOleksandr Tymoshenko intelspi_drain_rx_fifo(sc); 261d786719dSOleksandr Tymoshenko 262d786719dSOleksandr Tymoshenko /* Check for end of transfer. */ 263d786719dSOleksandr Tymoshenko return intelspi_transaction_done(sc); 264d786719dSOleksandr Tymoshenko } 265d786719dSOleksandr Tymoshenko 266d786719dSOleksandr Tymoshenko static void 267d786719dSOleksandr Tymoshenko intelspi_intr(void *arg) 268d786719dSOleksandr Tymoshenko { 269d786719dSOleksandr Tymoshenko struct intelspi_softc *sc; 270d786719dSOleksandr Tymoshenko uint32_t reg; 271d786719dSOleksandr Tymoshenko 272d786719dSOleksandr Tymoshenko sc = (struct intelspi_softc *)arg; 273d786719dSOleksandr Tymoshenko 274d786719dSOleksandr Tymoshenko INTELSPI_LOCK(sc); 275d786719dSOleksandr Tymoshenko if ((sc->sc_flags & INTELSPI_BUSY) == 0) { 276d786719dSOleksandr Tymoshenko INTELSPI_UNLOCK(sc); 277d786719dSOleksandr Tymoshenko return; 278d786719dSOleksandr Tymoshenko } 279d786719dSOleksandr Tymoshenko 280d786719dSOleksandr Tymoshenko /* Check if SSP if off */ 281d786719dSOleksandr Tymoshenko reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSSR); 282d786719dSOleksandr Tymoshenko if (reg == 0xffffffffU) { 283d786719dSOleksandr Tymoshenko INTELSPI_UNLOCK(sc); 284d786719dSOleksandr Tymoshenko return; 285d786719dSOleksandr Tymoshenko } 286d786719dSOleksandr Tymoshenko 287d786719dSOleksandr Tymoshenko /* Check for end of transfer. */ 288d786719dSOleksandr Tymoshenko if (intelspi_transact(sc)) { 289d786719dSOleksandr Tymoshenko /* Disable interrupts */ 290d786719dSOleksandr Tymoshenko reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 291d786719dSOleksandr Tymoshenko reg &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 292d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg); 293d786719dSOleksandr Tymoshenko wakeup(sc->sc_dev); 294d786719dSOleksandr Tymoshenko } 295d786719dSOleksandr Tymoshenko 296d786719dSOleksandr Tymoshenko INTELSPI_UNLOCK(sc); 297d786719dSOleksandr Tymoshenko } 298d786719dSOleksandr Tymoshenko 299d786719dSOleksandr Tymoshenko static void 300d786719dSOleksandr Tymoshenko intelspi_init(struct intelspi_softc *sc) 301d786719dSOleksandr Tymoshenko { 302d786719dSOleksandr Tymoshenko uint32_t reg; 303d786719dSOleksandr Tymoshenko 304d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0); 305d786719dSOleksandr Tymoshenko 306d786719dSOleksandr Tymoshenko /* Manual CS control */ 3071f40866fSVal Packett reg = INTELSPI_READ(sc, SPI_CS_CTRL(sc)); 308d786719dSOleksandr Tymoshenko reg &= ~(SPI_CS_CTRL_CS_MASK); 309d786719dSOleksandr Tymoshenko reg |= (SPI_CS_CTRL_SW_MODE | SPI_CS_CTRL_CS_HIGH); 3101f40866fSVal Packett INTELSPI_WRITE(sc, SPI_CS_CTRL(sc), reg); 311d786719dSOleksandr Tymoshenko 312d786719dSOleksandr Tymoshenko /* Set TX/RX FIFO IRQ threshold levels */ 313d786719dSOleksandr Tymoshenko reg = SSCR1_TFT(TX_FIFO_THRESHOLD) | SSCR1_RFT(RX_FIFO_THRESHOLD); 314d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg); 315d786719dSOleksandr Tymoshenko 316d786719dSOleksandr Tymoshenko reg = SSCR0_SCR(CLOCK_DIV_10MHZ); 317d786719dSOleksandr Tymoshenko /* Put SSP in SPI mode */ 318d786719dSOleksandr Tymoshenko reg |= SSCR0_FRF_SPI; 319d786719dSOleksandr Tymoshenko /* Data size */ 320d786719dSOleksandr Tymoshenko reg |= SSCR0_DSS(DATA_SIZE_8BITS); 321d786719dSOleksandr Tymoshenko /* Enable SSP */ 322d786719dSOleksandr Tymoshenko reg |= SSCR0_SSE; 323d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, reg); 324d786719dSOleksandr Tymoshenko } 325d786719dSOleksandr Tymoshenko 326d786719dSOleksandr Tymoshenko static void 327d786719dSOleksandr Tymoshenko intelspi_set_cs(struct intelspi_softc *sc, int level) 328d786719dSOleksandr Tymoshenko { 329d786719dSOleksandr Tymoshenko uint32_t reg; 330d786719dSOleksandr Tymoshenko 3311f40866fSVal Packett reg = INTELSPI_READ(sc, SPI_CS_CTRL(sc)); 332d786719dSOleksandr Tymoshenko reg &= ~(SPI_CS_CTRL_CS_MASK); 333d786719dSOleksandr Tymoshenko reg |= SPI_CS_CTRL_SW_MODE; 334d786719dSOleksandr Tymoshenko 335d786719dSOleksandr Tymoshenko if (level == CS_HIGH) 336d786719dSOleksandr Tymoshenko reg |= SPI_CS_CTRL_CS_HIGH; 337d786719dSOleksandr Tymoshenko 3381f40866fSVal Packett INTELSPI_WRITE(sc, SPI_CS_CTRL(sc), reg); 339d786719dSOleksandr Tymoshenko } 340d786719dSOleksandr Tymoshenko 3411f40866fSVal Packett int 342d786719dSOleksandr Tymoshenko intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd) 343d786719dSOleksandr Tymoshenko { 344d786719dSOleksandr Tymoshenko struct intelspi_softc *sc; 3451f40866fSVal Packett int err, poll_limit; 3461f40866fSVal Packett uint32_t sscr0, sscr1, mode, clock, cs_delay; 3471f40866fSVal Packett bool restart = false; 348d786719dSOleksandr Tymoshenko 349d786719dSOleksandr Tymoshenko sc = device_get_softc(dev); 350d786719dSOleksandr Tymoshenko err = 0; 351d786719dSOleksandr Tymoshenko 352d786719dSOleksandr Tymoshenko KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 353d786719dSOleksandr Tymoshenko ("TX/RX command sizes should be equal")); 354d786719dSOleksandr Tymoshenko KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 355d786719dSOleksandr Tymoshenko ("TX/RX data sizes should be equal")); 356d786719dSOleksandr Tymoshenko 357d786719dSOleksandr Tymoshenko INTELSPI_LOCK(sc); 358d786719dSOleksandr Tymoshenko 3595adcec04SVladimir Kondratyev if (!INTELSPI_IN_POLLING_MODE()) { 360d786719dSOleksandr Tymoshenko /* If the controller is in use wait until it is available. */ 361d786719dSOleksandr Tymoshenko while (sc->sc_flags & INTELSPI_BUSY) { 3625adcec04SVladimir Kondratyev if ((cmd->flags & SPI_FLAG_NO_SLEEP) != 0) { 363d101156eSVladimir Kondratyev INTELSPI_UNLOCK(sc); 3641f40866fSVal Packett return (EBUSY); 365d101156eSVladimir Kondratyev } 366d786719dSOleksandr Tymoshenko err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0); 367d786719dSOleksandr Tymoshenko if (err == EINTR) { 368d786719dSOleksandr Tymoshenko INTELSPI_UNLOCK(sc); 369d786719dSOleksandr Tymoshenko return (err); 370d786719dSOleksandr Tymoshenko } 371d786719dSOleksandr Tymoshenko } 3725adcec04SVladimir Kondratyev } else { 3735adcec04SVladimir Kondratyev /* 3745adcec04SVladimir Kondratyev * Now we are in the middle of other transfer. Try to reset 3755adcec04SVladimir Kondratyev * controller state to get predictable context. 3765adcec04SVladimir Kondratyev */ 3775adcec04SVladimir Kondratyev if ((sc->sc_flags & INTELSPI_BUSY) != 0) 3785adcec04SVladimir Kondratyev intelspi_init(sc); 3795adcec04SVladimir Kondratyev } 380d786719dSOleksandr Tymoshenko 381d786719dSOleksandr Tymoshenko /* Now we have control over SPI controller. */ 382d786719dSOleksandr Tymoshenko sc->sc_flags = INTELSPI_BUSY; 383d786719dSOleksandr Tymoshenko 3841f40866fSVal Packett /* Configure the clock rate and SPI mode. */ 3851f40866fSVal Packett spibus_get_clock(child, &clock); 3861f40866fSVal Packett spibus_get_mode(child, &mode); 3871f40866fSVal Packett 3881f40866fSVal Packett if (clock != sc->sc_clock || mode != sc->sc_mode) { 3891f40866fSVal Packett sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0); 3901f40866fSVal Packett sscr0 &= ~SSCR0_SSE; 3911f40866fSVal Packett INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0); 3921f40866fSVal Packett restart = true; 3931f40866fSVal Packett } 3941f40866fSVal Packett 3951f40866fSVal Packett if (clock != sc->sc_clock) { 3961f40866fSVal Packett sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0); 3971f40866fSVal Packett sscr0 &= ~SSCR0_SCR(0xfff); 3981f40866fSVal Packett if (clock == 0) 3991f40866fSVal Packett sscr0 |= SSCR0_SCR(CLOCK_DIV_10MHZ); 4001f40866fSVal Packett else 4011f40866fSVal Packett sscr0 |= SSCR0_SCR(howmany(MAX_CLOCK_RATE, min(MAX_CLOCK_RATE, clock))); 4021f40866fSVal Packett INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0); 4031f40866fSVal Packett sc->sc_clock = clock; 4041f40866fSVal Packett } 4051f40866fSVal Packett 4061f40866fSVal Packett if (mode != sc->sc_mode) { 4071f40866fSVal Packett sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 4081f40866fSVal Packett sscr1 &= ~SSCR1_MODE_MASK; 4091f40866fSVal Packett if (mode & SPIBUS_MODE_CPHA) 4101f40866fSVal Packett sscr1 |= SSCR1_SPI_SPH; 4111f40866fSVal Packett if (mode & SPIBUS_MODE_CPOL) 4121f40866fSVal Packett sscr1 |= SSCR1_SPI_SPO; 4131f40866fSVal Packett INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); 4141f40866fSVal Packett sc->sc_mode = mode; 4151f40866fSVal Packett } 4161f40866fSVal Packett 4171f40866fSVal Packett if (restart) { 4181f40866fSVal Packett sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0); 4191f40866fSVal Packett sscr0 |= SSCR0_SSE; 4201f40866fSVal Packett INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0); 4211f40866fSVal Packett } 4221f40866fSVal Packett 423d786719dSOleksandr Tymoshenko /* Save a pointer to the SPI command. */ 424d786719dSOleksandr Tymoshenko sc->sc_cmd = cmd; 425d786719dSOleksandr Tymoshenko sc->sc_read = 0; 426d786719dSOleksandr Tymoshenko sc->sc_written = 0; 427d786719dSOleksandr Tymoshenko sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; 428d786719dSOleksandr Tymoshenko 429d786719dSOleksandr Tymoshenko /* Enable CS */ 430d786719dSOleksandr Tymoshenko intelspi_set_cs(sc, CS_LOW); 4311f40866fSVal Packett 4321f40866fSVal Packett /* Wait the CS delay */ 4331f40866fSVal Packett spibus_get_cs_delay(child, &cs_delay); 4341f40866fSVal Packett DELAY(cs_delay); 4351f40866fSVal Packett 436d786719dSOleksandr Tymoshenko /* Transfer as much as possible to FIFOs */ 4375adcec04SVladimir Kondratyev if ((cmd->flags & SPI_FLAG_NO_SLEEP) != 0 || 4385adcec04SVladimir Kondratyev INTELSPI_IN_POLLING_MODE() || cold) { 4391f40866fSVal Packett /* We cannot wait with mtx_sleep if we're called from e.g. an ithread */ 4401f40866fSVal Packett poll_limit = 2000; 4411f40866fSVal Packett while (!intelspi_transact(sc) && poll_limit-- > 0) 4421f40866fSVal Packett DELAY(1000); 4431f40866fSVal Packett if (poll_limit == 0) { 4441f40866fSVal Packett device_printf(dev, "polling was stuck, transaction not finished\n"); 4451f40866fSVal Packett err = EIO; 4461f40866fSVal Packett } 4471f40866fSVal Packett } else { 448d786719dSOleksandr Tymoshenko if (!intelspi_transact(sc)) { 449d786719dSOleksandr Tymoshenko /* If FIFO is not large enough - enable interrupts */ 450d786719dSOleksandr Tymoshenko sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 451d786719dSOleksandr Tymoshenko sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 452d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); 453d786719dSOleksandr Tymoshenko 454d786719dSOleksandr Tymoshenko /* and wait for transaction to complete */ 455d786719dSOleksandr Tymoshenko err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2); 456d786719dSOleksandr Tymoshenko } 4571f40866fSVal Packett } 458d786719dSOleksandr Tymoshenko 4591f40866fSVal Packett /* De-assert CS */ 4601f40866fSVal Packett if ((cmd->flags & SPI_FLAG_KEEP_CS) == 0) 461d786719dSOleksandr Tymoshenko intelspi_set_cs(sc, CS_HIGH); 462d786719dSOleksandr Tymoshenko 463d786719dSOleksandr Tymoshenko /* Clear transaction details */ 464d786719dSOleksandr Tymoshenko sc->sc_cmd = NULL; 465d786719dSOleksandr Tymoshenko sc->sc_read = 0; 466d786719dSOleksandr Tymoshenko sc->sc_written = 0; 467d786719dSOleksandr Tymoshenko sc->sc_len = 0; 468d786719dSOleksandr Tymoshenko 469d786719dSOleksandr Tymoshenko /* Make sure the SPI engine and interrupts are disabled. */ 470d786719dSOleksandr Tymoshenko sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1); 471d786719dSOleksandr Tymoshenko sscr1 &= ~(SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE); 472d786719dSOleksandr Tymoshenko INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1); 473d786719dSOleksandr Tymoshenko 474d786719dSOleksandr Tymoshenko /* Release the controller and wakeup the next thread waiting for it. */ 475d786719dSOleksandr Tymoshenko sc->sc_flags = 0; 4765adcec04SVladimir Kondratyev if (!INTELSPI_IN_POLLING_MODE()) 477d786719dSOleksandr Tymoshenko wakeup_one(dev); 478d786719dSOleksandr Tymoshenko INTELSPI_UNLOCK(sc); 479d786719dSOleksandr Tymoshenko 480d786719dSOleksandr Tymoshenko /* 481d786719dSOleksandr Tymoshenko * Check for transfer timeout. The SPI controller doesn't 482d786719dSOleksandr Tymoshenko * return errors. 483d786719dSOleksandr Tymoshenko */ 484d786719dSOleksandr Tymoshenko if (err == EWOULDBLOCK) { 485d786719dSOleksandr Tymoshenko device_printf(sc->sc_dev, "transfer timeout\n"); 486d786719dSOleksandr Tymoshenko err = EIO; 487d786719dSOleksandr Tymoshenko } 488d786719dSOleksandr Tymoshenko 489d786719dSOleksandr Tymoshenko return (err); 490d786719dSOleksandr Tymoshenko } 491d786719dSOleksandr Tymoshenko 4921f40866fSVal Packett int 493d786719dSOleksandr Tymoshenko intelspi_attach(device_t dev) 494d786719dSOleksandr Tymoshenko { 495d786719dSOleksandr Tymoshenko struct intelspi_softc *sc; 496d786719dSOleksandr Tymoshenko 497d786719dSOleksandr Tymoshenko sc = device_get_softc(dev); 498d786719dSOleksandr Tymoshenko sc->sc_dev = dev; 499d786719dSOleksandr Tymoshenko 500d786719dSOleksandr Tymoshenko INTELSPI_LOCK_INIT(sc); 501d786719dSOleksandr Tymoshenko 502d786719dSOleksandr Tymoshenko sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev, 503d786719dSOleksandr Tymoshenko SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE); 504d786719dSOleksandr Tymoshenko if (sc->sc_mem_res == NULL) { 505d786719dSOleksandr Tymoshenko device_printf(dev, "can't allocate memory resource\n"); 506d786719dSOleksandr Tymoshenko goto error; 507d786719dSOleksandr Tymoshenko } 508d786719dSOleksandr Tymoshenko 509b109946dSVladimir Kondratyev /* Release LPSS reset */ 510b109946dSVladimir Kondratyev if (sc->sc_vers == SPI_SUNRISEPOINT) 511b109946dSVladimir Kondratyev INTELSPI_WRITE(sc, INTELSPI_RESETS, 512b109946dSVladimir Kondratyev (INTELSPI_RESET_HOST | INTELSPI_RESET_DMA)); 513b109946dSVladimir Kondratyev 514d786719dSOleksandr Tymoshenko sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev, 5151f40866fSVal Packett SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE | RF_SHAREABLE); 516d786719dSOleksandr Tymoshenko if (sc->sc_irq_res == NULL) { 517d786719dSOleksandr Tymoshenko device_printf(dev, "can't allocate IRQ resource\n"); 518d786719dSOleksandr Tymoshenko goto error; 519d786719dSOleksandr Tymoshenko } 520d786719dSOleksandr Tymoshenko 521d786719dSOleksandr Tymoshenko /* Hook up our interrupt handler. */ 522d786719dSOleksandr Tymoshenko if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 523d786719dSOleksandr Tymoshenko NULL, intelspi_intr, sc, &sc->sc_irq_ih)) { 524d786719dSOleksandr Tymoshenko device_printf(dev, "cannot setup the interrupt handler\n"); 525d786719dSOleksandr Tymoshenko goto error; 526d786719dSOleksandr Tymoshenko } 527d786719dSOleksandr Tymoshenko 528d786719dSOleksandr Tymoshenko intelspi_init(sc); 529d786719dSOleksandr Tymoshenko 5305b56413dSWarner Losh device_add_child(dev, "spibus", DEVICE_UNIT_ANY); 531d786719dSOleksandr Tymoshenko 53234f5de82SJohn Baldwin bus_delayed_attach_children(dev); 53334f5de82SJohn Baldwin return (0); 534d786719dSOleksandr Tymoshenko 535d786719dSOleksandr Tymoshenko error: 536d786719dSOleksandr Tymoshenko INTELSPI_LOCK_DESTROY(sc); 537d786719dSOleksandr Tymoshenko 538d786719dSOleksandr Tymoshenko if (sc->sc_mem_res != NULL) 539d786719dSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 540d786719dSOleksandr Tymoshenko sc->sc_mem_rid, sc->sc_mem_res); 541d786719dSOleksandr Tymoshenko 542d786719dSOleksandr Tymoshenko if (sc->sc_irq_res != NULL) 5439ef958c7SMark Johnston bus_release_resource(dev, SYS_RES_IRQ, 544d786719dSOleksandr Tymoshenko sc->sc_irq_rid, sc->sc_irq_res); 545d786719dSOleksandr Tymoshenko 546d786719dSOleksandr Tymoshenko return (ENXIO); 547d786719dSOleksandr Tymoshenko } 548d786719dSOleksandr Tymoshenko 5491f40866fSVal Packett int 550d786719dSOleksandr Tymoshenko intelspi_detach(device_t dev) 551d786719dSOleksandr Tymoshenko { 552d786719dSOleksandr Tymoshenko struct intelspi_softc *sc; 553*3ddaf820SJohn Baldwin int error; 554d786719dSOleksandr Tymoshenko 555d786719dSOleksandr Tymoshenko sc = device_get_softc(dev); 556d786719dSOleksandr Tymoshenko 557*3ddaf820SJohn Baldwin error = bus_generic_detach(dev); 558*3ddaf820SJohn Baldwin if (error != 0) 559*3ddaf820SJohn Baldwin return (error); 560*3ddaf820SJohn Baldwin 561d786719dSOleksandr Tymoshenko INTELSPI_LOCK_DESTROY(sc); 562d786719dSOleksandr Tymoshenko 563d786719dSOleksandr Tymoshenko if (sc->sc_irq_ih) 564d786719dSOleksandr Tymoshenko bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_irq_ih); 565d786719dSOleksandr Tymoshenko 566d786719dSOleksandr Tymoshenko if (sc->sc_mem_res != NULL) 567d786719dSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 568d786719dSOleksandr Tymoshenko sc->sc_mem_rid, sc->sc_mem_res); 569d786719dSOleksandr Tymoshenko 570d786719dSOleksandr Tymoshenko if (sc->sc_irq_res != NULL) 5719ef958c7SMark Johnston bus_release_resource(dev, SYS_RES_IRQ, 572d786719dSOleksandr Tymoshenko sc->sc_irq_rid, sc->sc_irq_res); 573d786719dSOleksandr Tymoshenko 574*3ddaf820SJohn Baldwin return (0); 575d786719dSOleksandr Tymoshenko } 576d786719dSOleksandr Tymoshenko 5771f40866fSVal Packett int 5781f40866fSVal Packett intelspi_suspend(device_t dev) 5791f40866fSVal Packett { 5801f40866fSVal Packett struct intelspi_softc *sc; 5811f40866fSVal Packett int err, i; 582d786719dSOleksandr Tymoshenko 5831f40866fSVal Packett sc = device_get_softc(dev); 584d786719dSOleksandr Tymoshenko 5851f40866fSVal Packett err = bus_generic_suspend(dev); 5861f40866fSVal Packett if (err) 5871f40866fSVal Packett return (err); 588d786719dSOleksandr Tymoshenko 5891f40866fSVal Packett for (i = 0; i < 9; i++) { 5901f40866fSVal Packett unsigned long offset = i * sizeof(uint32_t); 5911f40866fSVal Packett sc->sc_regs[i] = INTELSPI_READ(sc, 5921f40866fSVal Packett intelspi_infos[sc->sc_vers].reg_lpss_base + offset); 5931f40866fSVal Packett } 594d786719dSOleksandr Tymoshenko 5951f40866fSVal Packett /* Shutdown just in case */ 5961f40866fSVal Packett INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0); 5971f40866fSVal Packett 5981f40866fSVal Packett return (0); 5991f40866fSVal Packett } 6001f40866fSVal Packett 6011f40866fSVal Packett int 6021f40866fSVal Packett intelspi_resume(device_t dev) 6031f40866fSVal Packett { 6041f40866fSVal Packett struct intelspi_softc *sc; 6051f40866fSVal Packett int i; 6061f40866fSVal Packett 6071f40866fSVal Packett sc = device_get_softc(dev); 6081f40866fSVal Packett 6091f40866fSVal Packett for (i = 0; i < 9; i++) { 6101f40866fSVal Packett unsigned long offset = i * sizeof(uint32_t); 6111f40866fSVal Packett INTELSPI_WRITE(sc, 6121f40866fSVal Packett intelspi_infos[sc->sc_vers].reg_lpss_base + offset, 6131f40866fSVal Packett sc->sc_regs[i]); 6141f40866fSVal Packett } 6151f40866fSVal Packett 6161f40866fSVal Packett intelspi_init(sc); 6171f40866fSVal Packett 6181f40866fSVal Packett /* Ensure the next transfer would reconfigure these */ 6191f40866fSVal Packett sc->sc_clock = 0; 6201f40866fSVal Packett sc->sc_mode = 0; 6211f40866fSVal Packett 6221f40866fSVal Packett return (bus_generic_resume(dev)); 6231f40866fSVal Packett } 624