jcs's openbsd hax
openbsd
at jcs 367 lines 9.5 kB view raw
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}