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
intelspi_txfifo_full(struct intelspi_softc * sc)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
intelspi_rxfifo_empty(struct intelspi_softc * sc)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
intelspi_fill_tx_fifo(struct intelspi_softc * sc)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
intelspi_drain_rx_fifo(struct intelspi_softc * sc)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
intelspi_transaction_done(struct intelspi_softc * sc)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
intelspi_transact(struct intelspi_softc * sc)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
intelspi_intr(void * arg)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
intelspi_init(struct intelspi_softc * sc)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
intelspi_set_cs(struct intelspi_softc * sc,int level)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
intelspi_transfer(device_t dev,device_t child,struct spi_command * cmd)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
intelspi_attach(device_t dev)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
intelspi_detach(device_t dev)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
intelspi_suspend(device_t dev)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
intelspi_resume(device_t dev)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