jcs's openbsd hax
openbsd
1/* $OpenBSD: ispi.c,v 1.1 2025/11/14 01:55:07 jcs Exp $ */
2/*
3 * Intel LPSS SPI controller
4 *
5 * Copyright (c) 2015-2019 joshua stein <jcs@openbsd.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <sys/param.h>
21#include <sys/systm.h>
22#include <sys/kernel.h>
23#include <sys/kthread.h>
24
25#include <dev/acpi/acpireg.h>
26#include <dev/acpi/acpivar.h>
27#include <dev/acpi/acpidev.h>
28#include <dev/acpi/amltypes.h>
29#include <dev/acpi/dsdt.h>
30
31#include <dev/spi/spivar.h>
32
33#include <dev/pci/lpssreg.h>
34#include <dev/ic/ispivar.h>
35
36#define SSCR0 0x0 /* SSP Control Register 0 */
37#define SSCR0_EDSS_0 (0 << 20)
38#define SSCR0_EDSS_1 (1 << 20)
39#define SSCR0_SCR_SHIFT (8)
40#define SSCR0_SCR_MASK (0xFFF)
41#define SSCR0_SSE (1 << 7)
42#define SSCR0_ECS_ON_CHIP (0 << 6)
43#define SSCR0_FRF_MOTOROLA (0 << 4)
44#define SSCR0_DSS_SHIFT (0)
45#define SSCR0_DSS_MASK (0xF)
46#define SSCR1 0x4 /* SSP Control Register 1 */
47#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */
48#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */
49#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */
50#define SSCR1_SPO_LOW (0 << 3) /* Motorola SPI SSPSCLK polarity setting */
51#define SSCR1_SPO_HIGH (1 << 3)
52#define SSCR1_SPH_FIRST (0 << 4) /* Motorola SPI SSPSCLK phase setting */
53#define SSCR1_SPH_SECOND (1 << 4)
54#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */
55#define SSCR1_IFS_LOW (0 << 16)
56#define SSCR1_IFS_HIGH (1 << 16)
57#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interrupt Enable */
58#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */
59#define SSCR1_RX_THRESH_DEF 8
60#define SSCR1_RX_THRESH(x) (((x) - 1) << 10)
61#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
62#define SSCR1_TX_THRESH_DEF 8
63#define SSCR1_TX_THRESH(x) (((x) - 1) << 6)
64#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */
65#define SSSR 0x8 /* SSP Status Register */
66#define SSSR_TUR (1 << 21) /* Tx FIFO underrun */
67#define SSSR_TINT (1 << 19) /* Rx Time-out interrupt */
68#define SSSR_PINT (1 << 18) /* Peripheral trailing byte interrupt */
69#define SSSR_ROR (1 << 7) /* Rx FIFO Overrun */
70#define SSSR_BSY (1 << 4) /* SSP Busy */
71#define SSSR_RNE (1 << 3) /* Receive FIFO not empty */
72#define SSSR_TNF (1 << 2) /* Transmit FIFO not full */
73#define SSDR 0x10 /* SSP Data Register */
74#define SSTO 0x28 /* SSP Time out */
75#define SSPSP 0x2C /* SSP Programmable Serial Protocol */
76#define SSITF 0x44 /* SPI Transmit FIFO */
77#define SSITF_LEVEL_SHIFT (16)
78#define SSITF_LEVEL_MASK (0x3f)
79#define SSITF_TX_LO_THRESH(x) (((x) - 1) << 8)
80#define SSITF_TX_HI_THRESH(x) ((x) - 1)
81#define SSIRF 0x48 /* SPI Receive FIFO */
82#define SSIRF_LEVEL_SHIFT (8)
83#define SSIRF_LEVEL_MASK (0x3f)
84#define SSIRF_RX_THRESH(x) ((x) - 1)
85
86void ispi_cs_change(struct ispi_softc *, int);
87uint32_t ispi_lpss_read(struct ispi_softc *, int);
88void ispi_lpss_write(struct ispi_softc *, int, uint32_t);
89int ispi_rx_fifo_empty(struct ispi_softc *);
90int ispi_tx_fifo_full(struct ispi_softc *);
91
92struct cfdriver ispi_cd = {
93 NULL, "ispi", DV_DULL
94};
95
96int
97ispi_activate(struct device *self, int act)
98{
99 return config_activate_children(self, act);
100}
101
102int
103ispi_spi_print(void *aux, const char *pnp)
104{
105 struct spi_attach_args *sa = aux;
106
107 if (pnp != NULL)
108 printf("\"%s\" at %s", sa->sa_name, pnp);
109
110 return UNCONF;
111}
112
113int
114ispi_acquire_bus(void *cookie, int flags)
115{
116 struct ispi_softc *sc = cookie;
117
118 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
119
120 rw_enter(&sc->sc_buslock, RW_WRITE);
121 ispi_cs_change(sc, 1);
122
123 return 0;
124}
125
126void
127ispi_release_bus(void *cookie, int flags)
128{
129 struct ispi_softc *sc = cookie;
130
131 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
132
133 ispi_cs_change(sc, 0);
134 rw_exit(&sc->sc_buslock);
135}
136
137void
138ispi_init(struct ispi_softc *sc)
139{
140 uint32_t csctrl;
141
142 if (!sc->sc_rx_threshold)
143 sc->sc_rx_threshold = SSCR1_RX_THRESH_DEF;
144 if (!sc->sc_tx_threshold)
145 sc->sc_tx_threshold = SSCR1_TX_THRESH_DEF;
146
147 ispi_write(sc, SSCR0, 0);
148 ispi_write(sc, SSCR1, 0);
149 ispi_write(sc, SSTO, 0);
150 ispi_write(sc, SSPSP, 0);
151
152 /* lpss: enable software chip select control */
153 csctrl = ispi_lpss_read(sc, sc->sc_reg_cs_ctrl);
154 csctrl &= ~(LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH);
155 csctrl |= LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH;
156 ispi_lpss_write(sc, sc->sc_reg_cs_ctrl, csctrl);
157
158 ispi_cs_change(sc, 0);
159}
160
161uint32_t
162ispi_read(struct ispi_softc *sc, int reg)
163{
164 uint32_t val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
165 DPRINTF(("%s: %s(0x%x) = 0x%x\n", sc->sc_dev.dv_xname, __func__, reg,
166 val));
167 return val;
168}
169
170void
171ispi_write(struct ispi_softc *sc, int reg, uint32_t val)
172{
173 DPRINTF(("%s: %s(0x%x, 0x%x)\n", sc->sc_dev.dv_xname, __func__, reg,
174 val));
175 bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
176}
177
178uint32_t
179ispi_lpss_read(struct ispi_softc *sc, int reg)
180{
181 return ispi_read(sc, sc->sc_lpss_reg_offset + reg);
182}
183
184void
185ispi_lpss_write(struct ispi_softc *sc, int reg, uint32_t val)
186{
187 ispi_write(sc, sc->sc_lpss_reg_offset + reg, val);
188}
189
190void
191ispi_config(void *cookie, struct spi_config *conf)
192{
193 struct ispi_softc *sc = cookie;
194 uint32_t sscr0, sscr1;
195 unsigned int rate;
196 int rx_threshold, tx_threshold;
197
198 DPRINTF(("%s: %s: ssp clk %ld sc_freq %d\n", sc->sc_dev.dv_xname,
199 __func__, sc->sc_ssp_clk, conf->sc_freq));
200
201 rate = min(sc->sc_ssp_clk, conf->sc_freq);
202 if (rate <= 1)
203 rate = sc->sc_ssp_clk;
204
205 sscr0 = ((sc->sc_ssp_clk / rate) - 1) & 0xfff;
206 sscr0 |= SSCR0_FRF_MOTOROLA;
207 sscr0 |= (conf->sc_bpw - 1);
208 sscr0 |= SSCR0_SSE;
209 sscr0 |= (conf->sc_bpw > 16 ? SSCR0_EDSS_1 : SSCR0_EDSS_0);
210
211 ispi_clear_status(sc);
212
213 rx_threshold = SSIRF_RX_THRESH(sc->sc_rx_threshold);
214 tx_threshold = SSITF_TX_LO_THRESH(sc->sc_tx_threshold) |
215 SSITF_TX_HI_THRESH(sc->sc_tx_threshold_hi);
216
217 if ((ispi_read(sc, SSIRF) & 0xff) != rx_threshold)
218 ispi_write(sc, SSIRF, rx_threshold);
219
220 if ((ispi_read(sc, SSITF) & 0xffff) != tx_threshold)
221 ispi_write(sc, SSITF, tx_threshold);
222
223 ispi_write(sc, SSCR0, sscr0 & ~SSCR0_SSE);
224
225 ispi_write(sc, SSTO, 1000); /* timeout */
226
227 sscr1 = (SSCR1_RX_THRESH(sc->sc_rx_threshold) & SSCR1_RFT) |
228 (SSCR1_TX_THRESH(sc->sc_tx_threshold) & SSCR1_TFT);
229
230#if 0
231 /* enable interrupts */
232 sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
233#endif
234 ispi_write(sc, SSCR1, sscr1);
235
236 /* restart SSP */
237 ispi_write(sc, SSCR0, sscr0);
238 ispi_write(sc, SSCR1, sscr1);
239}
240
241int
242ispi_flush(struct ispi_softc *sc)
243{
244 int tries = 500;
245
246 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
247
248 do {
249 while (!ispi_rx_fifo_empty(sc))
250 ispi_read(sc, SSDR);
251 } while ((ispi_read(sc, SSSR) & SSSR_BSY) && --tries);
252
253 DPRINTF(("%s: flushed with %d left\n", sc->sc_dev.dv_xname, tries));
254
255 ispi_write(sc, SSSR, SSSR_ROR);
256
257 return (tries != 0);
258}
259
260void
261ispi_cs_change(struct ispi_softc *sc, int cs_assert)
262{
263 uint32_t t;
264 int tries = 500;
265
266 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
267
268 if (!cs_assert) {
269 /* wait until idle */
270 while ((ispi_read(sc, SSSR) & SSSR_BSY) && --tries)
271 ;
272 }
273
274 t = ispi_lpss_read(sc, sc->sc_reg_cs_ctrl);
275 if (cs_assert)
276 t &= ~LPSS_CS_CONTROL_CS_HIGH;
277 else
278 t |= LPSS_CS_CONTROL_CS_HIGH;
279 ispi_lpss_write(sc, sc->sc_reg_cs_ctrl, t);
280
281 DELAY(10);
282}
283
284int
285ispi_transfer(void *cookie, char *out, char *in, int len, int flags)
286{
287 struct ispi_softc *sc = cookie;
288 int s = spltty();
289
290 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
291
292 sc->sc_ridx = sc->sc_widx = 0;
293
294 /* drain input buffer */
295 ispi_flush(sc);
296
297 while (sc->sc_ridx < len || sc->sc_widx < len) {
298 while (!ispi_rx_fifo_empty(sc) && sc->sc_ridx < len) {
299 if (in)
300 in[sc->sc_ridx] = ispi_read(sc, SSDR) & 0xff;
301 else
302 ispi_read(sc, SSDR);
303
304 sc->sc_ridx++;
305 }
306
307 while (!ispi_tx_fifo_full(sc) && sc->sc_widx < len) {
308 if (out)
309 ispi_write(sc, SSDR, out[sc->sc_widx]);
310 else
311 ispi_write(sc, SSDR, 0);
312
313 sc->sc_widx++;
314 }
315 }
316
317 DPRINTF(("%s: %s: done transmitting %s %d\n", sc->sc_dev.dv_xname,
318 __func__, (out ? "out" : "in"), len));
319
320 splx(s);
321 return 0;
322}
323
324int
325ispi_status(struct ispi_softc *sc)
326{
327 return ispi_read(sc, SSSR);
328}
329
330void
331ispi_clear_status(struct ispi_softc *sc)
332{
333 ispi_write(sc, SSSR, SSSR_TUR | SSSR_TINT | SSSR_PINT | SSSR_ROR);
334}
335
336int
337ispi_rx_fifo_empty(struct ispi_softc *sc)
338{
339 return !(ispi_status(sc) & SSSR_RNE);
340}
341
342int
343ispi_tx_fifo_full(struct ispi_softc *sc)
344{
345 return !(ispi_status(sc) & SSSR_TNF);
346}
347
348int
349ispi_rx_fifo_overrun(struct ispi_softc *sc)
350{
351 if (ispi_status(sc) & SSSR_ROR) {
352 printf("%s: %s\n", sc->sc_dev.dv_xname, __func__);
353 return 1;
354 }
355
356 return 0;
357}
358
359int
360ispi_intr(void *arg)
361{
362#ifdef ISPI_DEBUG
363 struct ispi_softc *sc = arg;
364 DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
365#endif
366 return 1;
367}