git: 1f40866feb21 - main - intelspi: add PCI attachment (Lynx/Wildcat/Sunrise Point)

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Mon, 24 Apr 2023 09:43:07 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=1f40866feb2135a4cf764a07b1b90a8a3398ff0a

commit 1f40866feb2135a4cf764a07b1b90a8a3398ff0a
Author:     Val Packett <val@packett.cool>
AuthorDate: 2023-04-24 09:41:52 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2023-04-24 09:41:52 +0000

    intelspi: add PCI attachment (Lynx/Wildcat/Sunrise Point)
    
    Also adds fixups and cleanups:
    
    - apply the child's mode/speed
    - implement suspend/resume support
    - use RF_SHAREABLE interrupts
    - use bus_delayed_attach_children since the transfer can use interrupts
    - add support for newly added spibus features (cs_delay and flags)
    
    Operation tested on Broadwell (Wildcat Point) MacBookPro12,1.
    Attachment also tested on Kaby Lake (Sunrise Point) Pixelbook.
    
    Reviewed by:    wulf
    MFC after:      1 month
    Differential revision:  https://reviews.freebsd.org/D29249
---
 sys/conf/files.x86            |   2 +
 sys/dev/intel/spi.c           | 241 +++++++++++++++++++++++-------------------
 sys/dev/intel/spi.h           |  99 +++++++++++++++++
 sys/dev/intel/spi_acpi.c      | 111 +++++++++++++++++++
 sys/dev/intel/spi_pci.c       | 138 ++++++++++++++++++++++++
 sys/modules/intelspi/Makefile |   4 +-
 6 files changed, 485 insertions(+), 110 deletions(-)

diff --git a/sys/conf/files.x86 b/sys/conf/files.x86
index 2e7ce6c00b3b..f80638f98f95 100644
--- a/sys/conf/files.x86
+++ b/sys/conf/files.x86
@@ -145,6 +145,8 @@ dev/imcsmb/imcsmb.c		optional	imcsmb
 dev/imcsmb/imcsmb_pci.c		optional	imcsmb pci
 dev/intel/pchtherm.c		optional	pchtherm
 dev/intel/spi.c			optional	intelspi
+dev/intel/spi_pci.c			optional	intelspi pci
+dev/intel/spi_acpi.c			optional	intelspi acpi
 dev/io/iodev.c			optional	io
 dev/iommu/busdma_iommu.c	optional	acpi iommu pci
 dev/iommu/iommu_gas.c		optional	acpi iommu pci
diff --git a/sys/dev/intel/spi.c b/sys/dev/intel/spi.c
index 676530234382..497c2606a1c9 100644
--- a/sys/dev/intel/spi.c
+++ b/sys/dev/intel/spi.c
@@ -1,4 +1,6 @@
 /*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
  * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
  * All rights reserved.
  *
@@ -24,9 +26,6 @@
  * SUCH DAMAGE.
  */
 
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
 #include "opt_acpi.h"
 
 #include <sys/param.h>
@@ -42,12 +41,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/spibus/spi.h>
 #include <dev/spibus/spibusvar.h>
 
-#include <contrib/dev/acpica/include/acpi.h>
-#include <contrib/dev/acpica/include/accommon.h>
-
-#include <dev/acpica/acpivar.h>
-
-#include "spibus_if.h"
+#include <dev/intel/spi.h>
 
 /**
  *	Macros for driver mutex locking
@@ -71,12 +65,13 @@ __FBSDID("$FreeBSD$");
 #define	RX_FIFO_THRESHOLD	2
 #define	CLOCK_DIV_10MHZ		5
 #define	DATA_SIZE_8BITS		8
+#define	MAX_CLOCK_RATE		50000000
 
 #define	CS_LOW		0
 #define	CS_HIGH		1
 
 #define	INTELSPI_SSPREG_SSCR0	 	0x0
-#define	 SSCR0_SCR(n)				(((n) - 1) << 8)
+#define	 SSCR0_SCR(n)				((((n) - 1) & 0xfff) << 8)
 #define	 SSCR0_SSE				(1 << 7)
 #define	 SSCR0_FRF_SPI				(0 << 4)
 #define	 SSCR0_DSS(n)				(((n) - 1) << 0)
@@ -88,10 +83,6 @@ __FBSDID("$FreeBSD$");
 #define	 SSCR1_SPI_SPH				(1 << 4)
 #define	 SSCR1_SPI_SPO				(1 << 3)
 #define	 SSCR1_MODE_MASK				(SSCR1_SPI_SPO | SSCR1_SPI_SPH)
-#define	 SSCR1_MODE_0				(0)
-#define	 SSCR1_MODE_1				(SSCR1_SPI_SPH)
-#define	 SSCR1_MODE_2				(SSCR1_SPI_SPO)
-#define	 SSCR1_MODE_3				(SSCR1_SPI_SPO | SSCR1_SPI_SPH)
 #define	 SSCR1_TIE				(1 << 1)
 #define	 SSCR1_RIE				(1 << 0)
 #define	INTELSPI_SSPREG_SSSR	 	0x8
@@ -110,36 +101,14 @@ __FBSDID("$FreeBSD$");
 #define	INTELSPI_SSPREG_ITF	 	0x40
 #define	INTELSPI_SSPREG_SITF	 	0x44
 #define	INTELSPI_SSPREG_SIRF	 	0x48
-#define	INTELSPI_SSPREG_PRV_CLOCK_PARAMS	0x400
-#define	INTELSPI_SSPREG_RESETS	 	0x404
-#define	INTELSPI_SSPREG_GENERAL	 	0x408
-#define	INTELSPI_SSPREG_SSP_REG	 	0x40C
-#define	INTELSPI_SSPREG_SPI_CS_CTRL	 0x418
+#define SPI_CS_CTRL(sc) \
+	(intelspi_infos[sc->sc_vers].reg_lpss_base + \
+	 intelspi_infos[sc->sc_vers].reg_cs_ctrl)
 #define	 SPI_CS_CTRL_CS_MASK			(3)
 #define	 SPI_CS_CTRL_SW_MODE			(1 << 0)
 #define	 SPI_CS_CTRL_HW_MODE			(1 << 0)
 #define	 SPI_CS_CTRL_CS_HIGH			(1 << 1)
-#define	 SPI_CS_CTRL_CS_LOW			(0 << 1)
-
-struct intelspi_softc {
-	ACPI_HANDLE		sc_handle;
-	device_t		sc_dev;
-	struct mtx		sc_mtx;
-	int			sc_mem_rid;
-	struct resource		*sc_mem_res;
-	int			sc_irq_rid;
-	struct resource		*sc_irq_res;
-	void			*sc_irq_ih;
-	struct spi_command	*sc_cmd;
-	uint32_t		sc_len;
-	uint32_t		sc_read;
-	uint32_t		sc_flags;
-	uint32_t		sc_written;
-};
-
-static int	intelspi_probe(device_t dev);
-static int	intelspi_attach(device_t dev);
-static int	intelspi_detach(device_t dev);
+
 static void	intelspi_intr(void *);
 
 static int
@@ -294,25 +263,15 @@ intelspi_init(struct intelspi_softc *sc)
 	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0);
 
 	/* Manual CS control */
-	reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL);
+	reg = INTELSPI_READ(sc, SPI_CS_CTRL(sc));
 	reg &= ~(SPI_CS_CTRL_CS_MASK);
 	reg |= (SPI_CS_CTRL_SW_MODE | SPI_CS_CTRL_CS_HIGH);
-	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg);
+	INTELSPI_WRITE(sc, SPI_CS_CTRL(sc), reg);
 
 	/* Set TX/RX FIFO IRQ threshold levels */
 	reg = SSCR1_TFT(TX_FIFO_THRESHOLD) | SSCR1_RFT(RX_FIFO_THRESHOLD);
-	/*
-	 * Set SPI mode. This should be part of transaction or sysctl
-	 */
-	reg |= SSCR1_MODE_0;
 	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, reg);
 
-	/*
-	 * Parent clock on Minowboard Turbot is 50MHz
-	 * divide it by 5 to set to more or less reasonable
-	 * value. But this should be part of transaction config
-	 * or sysctl
-	 */
 	reg = SSCR0_SCR(CLOCK_DIV_10MHZ);
 	/* Put SSP in SPI mode */
 	reg |= SSCR0_FRF_SPI;
@@ -328,24 +287,23 @@ intelspi_set_cs(struct intelspi_softc *sc, int level)
 {
 	uint32_t reg;
 
-	reg = INTELSPI_READ(sc, INTELSPI_SSPREG_SPI_CS_CTRL);
+	reg = INTELSPI_READ(sc, SPI_CS_CTRL(sc));
 	reg &= ~(SPI_CS_CTRL_CS_MASK);
 	reg |= SPI_CS_CTRL_SW_MODE;
 
 	if (level == CS_HIGH)
 		reg |= SPI_CS_CTRL_CS_HIGH;
-	else
-		reg |= SPI_CS_CTRL_CS_LOW;
-		
-	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SPI_CS_CTRL, reg);
+
+	INTELSPI_WRITE(sc, SPI_CS_CTRL(sc), reg);
 }
 
-static int
+int
 intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 {
 	struct intelspi_softc *sc;
-	int err;
-	uint32_t sscr1;
+	int err, poll_limit;
+	uint32_t sscr0, sscr1, mode, clock, cs_delay;
+	bool restart = false;
 
 	sc = device_get_softc(dev);
 	err = 0;
@@ -359,6 +317,8 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 
 	/* If the controller is in use wait until it is available. */
 	while (sc->sc_flags & INTELSPI_BUSY) {
+		if ((cmd->flags & SPI_FLAG_NO_SLEEP) == SPI_FLAG_NO_SLEEP)
+			return (EBUSY);
 		err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", 0);
 		if (err == EINTR) {
 			INTELSPI_UNLOCK(sc);
@@ -369,6 +329,45 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 	/* Now we have control over SPI controller. */
 	sc->sc_flags = INTELSPI_BUSY;
 
+	/* Configure the clock rate and SPI mode. */
+	spibus_get_clock(child, &clock);
+	spibus_get_mode(child, &mode);
+
+	if (clock != sc->sc_clock || mode != sc->sc_mode) {
+		sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0);
+		sscr0 &= ~SSCR0_SSE;
+		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0);
+		restart = true;
+	}
+
+	if (clock != sc->sc_clock) {
+		sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0);
+		sscr0 &= ~SSCR0_SCR(0xfff);
+		if (clock == 0)
+			sscr0 |= SSCR0_SCR(CLOCK_DIV_10MHZ);
+		else
+			sscr0 |= SSCR0_SCR(howmany(MAX_CLOCK_RATE, min(MAX_CLOCK_RATE, clock)));
+		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0);
+		sc->sc_clock = clock;
+	}
+
+	if (mode != sc->sc_mode) {
+		sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
+		sscr1 &= ~SSCR1_MODE_MASK;
+		if (mode & SPIBUS_MODE_CPHA)
+			sscr1 |= SSCR1_SPI_SPH;
+		if (mode & SPIBUS_MODE_CPOL)
+			sscr1 |= SSCR1_SPI_SPO;
+		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1);
+		sc->sc_mode = mode;
+	}
+
+	if (restart) {
+		sscr0 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR0);
+		sscr0 |= SSCR0_SSE;
+		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, sscr0);
+	}
+
 	/* Save a pointer to the SPI command. */
 	sc->sc_cmd = cmd;
 	sc->sc_read = 0;
@@ -377,19 +376,36 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 
 	/* Enable CS */
 	intelspi_set_cs(sc, CS_LOW);
-	/* Transfer as much as possible to FIFOs */
-	if (!intelspi_transact(sc)) {
-		/* If FIFO is not large enough - enable interrupts */
-		sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
-		sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
-		INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1);
 
-		/* and wait for transaction to complete */
-		err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2);
+	/* Wait the CS delay */
+	spibus_get_cs_delay(child, &cs_delay);
+	DELAY(cs_delay);
+
+	/* Transfer as much as possible to FIFOs */
+	if ((cmd->flags & SPI_FLAG_NO_SLEEP) == SPI_FLAG_NO_SLEEP) {
+		/* We cannot wait with mtx_sleep if we're called from e.g. an ithread */
+		poll_limit = 2000;
+		while (!intelspi_transact(sc) && poll_limit-- > 0)
+			DELAY(1000);
+		if (poll_limit == 0) {
+			device_printf(dev, "polling was stuck, transaction not finished\n");
+			err = EIO;
+		}
+	} else {
+		if (!intelspi_transact(sc)) {
+			/* If FIFO is not large enough - enable interrupts */
+			sscr1 = INTELSPI_READ(sc, INTELSPI_SSPREG_SSCR1);
+			sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
+			INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR1, sscr1);
+
+			/* and wait for transaction to complete */
+			err = mtx_sleep(dev, &sc->sc_mtx, 0, "intelspi", hz * 2);
+		}
 	}
 
-	/* de-asser CS */
-	intelspi_set_cs(sc, CS_HIGH);
+	/* De-assert CS */
+	if ((cmd->flags & SPI_FLAG_KEEP_CS) == 0)
+		intelspi_set_cs(sc, CS_HIGH);
 
 	/* Clear transaction details */
 	sc->sc_cmd = NULL;
@@ -419,32 +435,16 @@ intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
 	return (err);
 }
 
-static int
-intelspi_probe(device_t dev)
-{
-	static char *gpio_ids[] = { "80860F0E", NULL };
-	int rv;
-	
-	if (acpi_disabled("spi") )
-		return (ENXIO);
-	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, gpio_ids, NULL);
-	if (rv <= 0)
-		device_set_desc(dev, "Intel SPI Controller");
-	return (rv);
-}
-
-static int
+int
 intelspi_attach(device_t dev)
 {
 	struct intelspi_softc	*sc;
 
 	sc = device_get_softc(dev);
 	sc->sc_dev = dev;
-	sc->sc_handle = acpi_get_handle(dev);
 
 	INTELSPI_LOCK_INIT(sc);
 
-	sc->sc_mem_rid = 0;
 	sc->sc_mem_res = bus_alloc_resource_any(sc->sc_dev,
 	    SYS_RES_MEMORY, &sc->sc_mem_rid, RF_ACTIVE);
 	if (sc->sc_mem_res == NULL) {
@@ -452,9 +452,8 @@ intelspi_attach(device_t dev)
 		goto error;
 	}
 
-	sc->sc_irq_rid = 0;
 	sc->sc_irq_res = bus_alloc_resource_any(sc->sc_dev,
-	    SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE);
+	    SYS_RES_IRQ, &sc->sc_irq_rid, RF_ACTIVE | RF_SHAREABLE);
 	if (sc->sc_irq_res == NULL) {
 		device_printf(dev, "can't allocate IRQ resource\n");
 		goto error;
@@ -471,7 +470,7 @@ intelspi_attach(device_t dev)
 
 	device_add_child(dev, "spibus", -1);
 
-	return (bus_generic_attach(dev));
+	return (bus_delayed_attach_children(dev));
 
 error:
 	INTELSPI_LOCK_DESTROY(sc);
@@ -487,7 +486,7 @@ error:
 	return (ENXIO);
 }
 
-static int
+int
 intelspi_detach(device_t dev)
 {
 	struct intelspi_softc	*sc;
@@ -510,24 +509,50 @@ intelspi_detach(device_t dev)
 	return (bus_generic_detach(dev));
 }
 
-static device_method_t intelspi_methods[] = {
-	/* Device interface */
-	DEVMETHOD(device_probe, intelspi_probe),
-	DEVMETHOD(device_attach, intelspi_attach),
-	DEVMETHOD(device_detach, intelspi_detach),
+int
+intelspi_suspend(device_t dev)
+{
+	struct intelspi_softc        *sc;
+	int err, i;
 
-	/* SPI interface */
-	DEVMETHOD(spibus_transfer,	intelspi_transfer),
+	sc = device_get_softc(dev);
 
-	DEVMETHOD_END
-};
+	err = bus_generic_suspend(dev);
+	if (err)
+		return (err);
 
-static driver_t intelspi_driver = {
-	"spi",
-	intelspi_methods,
-	sizeof(struct intelspi_softc),
-};
+	for (i = 0; i < 9; i++) {
+		unsigned long offset = i * sizeof(uint32_t);
+		sc->sc_regs[i] = INTELSPI_READ(sc,
+		    intelspi_infos[sc->sc_vers].reg_lpss_base + offset);
+	}
+
+	/* Shutdown just in case */
+	INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0);
 
-DRIVER_MODULE(intelspi, acpi, intelspi_driver, 0, 0);
-MODULE_DEPEND(intelspi, acpi, 1, 1, 1);
-MODULE_DEPEND(intelspi, spibus, 1, 1, 1);
+	return (0);
+}
+
+int
+intelspi_resume(device_t dev)
+{
+	struct intelspi_softc   *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	for (i = 0; i < 9; i++) {
+		unsigned long offset = i * sizeof(uint32_t);
+		INTELSPI_WRITE(sc,
+		    intelspi_infos[sc->sc_vers].reg_lpss_base + offset,
+		    sc->sc_regs[i]);
+	}
+
+	intelspi_init(sc);
+
+	/* Ensure the next transfer would reconfigure these */
+	sc->sc_clock = 0;
+	sc->sc_mode = 0;
+
+	return (bus_generic_resume(dev));
+}
diff --git a/sys/dev/intel/spi.h b/sys/dev/intel/spi.h
new file mode 100644
index 000000000000..31b708572630
--- /dev/null
+++ b/sys/dev/intel/spi.h
@@ -0,0 +1,99 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _DEV_INTEL_SPI_H_
+#define _DEV_INTEL_SPI_H_
+
+#include <contrib/dev/acpica/include/acpi.h>
+#include <contrib/dev/acpica/include/accommon.h>
+
+#include <dev/acpica/acpivar.h>
+
+enum intelspi_vers {
+	SPI_BAYTRAIL,
+	SPI_BRASWELL,
+	SPI_LYNXPOINT,
+	SPI_SUNRISEPOINT,
+};
+
+/* Same order as intelspi_vers */
+static const struct intelspi_info {
+	const char *desc;
+	uint32_t reg_lpss_base;
+	uint32_t reg_cs_ctrl;
+} intelspi_infos[] = {
+	[SPI_BAYTRAIL] = {
+		.desc = "Intel Bay Trail SPI Controller",
+		.reg_lpss_base = 0x400,
+		.reg_cs_ctrl = 0x18,
+	},
+	[SPI_BRASWELL] = {
+		.desc = "Intel Braswell SPI Controller",
+		.reg_lpss_base = 0x400,
+		.reg_cs_ctrl = 0x18,
+	},
+	[SPI_LYNXPOINT] = {
+		.desc = "Intel Lynx Point / Wildcat Point SPI Controller",
+		.reg_lpss_base = 0x800,
+		.reg_cs_ctrl = 0x18,
+	},
+	[SPI_SUNRISEPOINT] = {
+		.desc = "Intel Sunrise Point SPI Controller",
+		.reg_lpss_base = 0x200,
+		.reg_cs_ctrl = 0x24,
+	},
+};
+
+struct intelspi_softc {
+	ACPI_HANDLE		sc_handle;
+	device_t		sc_dev;
+	enum intelspi_vers sc_vers;
+	struct mtx		sc_mtx;
+	int			sc_mem_rid;
+	struct resource		*sc_mem_res;
+	int			sc_irq_rid;
+	struct resource		*sc_irq_res;
+	void			*sc_irq_ih;
+	struct spi_command	*sc_cmd;
+	uint32_t		sc_len;
+	uint32_t		sc_read;
+	uint32_t		sc_flags;
+	uint32_t		sc_written;
+	uint32_t		sc_clock;
+	uint32_t		sc_mode;
+	/* LPSS private register storage for suspend-resume */
+	uint32_t		sc_regs[9];
+};
+
+int	intelspi_attach(device_t dev);
+int	intelspi_detach(device_t dev);
+int	intelspi_transfer(device_t dev, device_t child, struct spi_command *cmd);
+int	intelspi_suspend(device_t dev);
+int	intelspi_resume(device_t dev);
+
+#endif
diff --git a/sys/dev/intel/spi_acpi.c b/sys/dev/intel/spi_acpi.c
new file mode 100644
index 000000000000..015694f4a008
--- /dev/null
+++ b/sys/dev/intel/spi_acpi.c
@@ -0,0 +1,111 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_acpi.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+
+#include <dev/intel/spi.h>
+
+#include "spibus_if.h"
+
+static const struct intelspi_acpi_device {
+	const char *hid;
+	enum intelspi_vers vers;
+} intelspi_acpi_devices[] = {
+	{ "80860F0E", SPI_BAYTRAIL },
+	{ "8086228E", SPI_BRASWELL },
+};
+
+static char *intelspi_ids[] = { "80860F0E", "8086228E", NULL };
+
+static int
+intelspi_acpi_probe(device_t dev)
+{
+	struct intelspi_softc *sc = device_get_softc(dev);
+	char *hid;
+	int i;
+
+	if (acpi_disabled("spi"))
+		return (ENXIO);
+
+	if (ACPI_ID_PROBE(device_get_parent(dev), dev, intelspi_ids, &hid) > 0)
+		return (ENXIO);
+
+	for (i = 0; i < nitems(intelspi_acpi_devices); i++) {
+		if (strcmp(intelspi_acpi_devices[i].hid, hid) == 0) {
+			sc->sc_vers = intelspi_acpi_devices[i].vers;
+			sc->sc_handle = acpi_get_handle(dev);
+			device_set_desc(dev, intelspi_infos[sc->sc_vers].desc);
+			return (BUS_PROBE_DEFAULT);
+		}
+	}
+
+	return (ENXIO);
+}
+
+static int
+intelspi_acpi_attach(device_t dev)
+{
+	struct intelspi_softc *sc = device_get_softc(dev);
+
+	sc->sc_mem_rid = 0;
+	sc->sc_irq_rid = 0;
+
+	return (intelspi_attach(dev));
+}
+
+static device_method_t intelspi_acpi_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, intelspi_acpi_probe),
+	DEVMETHOD(device_attach, intelspi_acpi_attach),
+	DEVMETHOD(device_detach, intelspi_detach),
+	DEVMETHOD(device_suspend, intelspi_suspend),
+	DEVMETHOD(device_resume, intelspi_resume),
+
+	/* SPI interface */
+	DEVMETHOD(spibus_transfer, intelspi_transfer),
+
+	DEVMETHOD_END
+};
+
+static driver_t intelspi_acpi_driver = {
+	"spi",
+	intelspi_acpi_methods,
+	sizeof(struct intelspi_softc),
+};
+
+DRIVER_MODULE(intelspi, acpi, intelspi_acpi_driver, 0, 0);
+MODULE_DEPEND(intelspi, acpi, 1, 1, 1);
+MODULE_DEPEND(intelspi, spibus, 1, 1, 1);
+ACPI_PNP_INFO(intelspi_ids);
diff --git a/sys/dev/intel/spi_pci.c b/sys/dev/intel/spi_pci.c
new file mode 100644
index 000000000000..c55b5a12228e
--- /dev/null
+++ b/sys/dev/intel/spi_pci.c
@@ -0,0 +1,138 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Val Packett <val@packett.cool>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "opt_acpi.h"
+#include "opt_pci.h"
+
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+
+#include <dev/intel/spi.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include "spibus_if.h"
+
+static struct intelspi_pci_device {
+	uint32_t devid;
+	enum intelspi_vers vers;
+} intelspi_pci_devices[] = {
+	{ 0x9c658086, SPI_LYNXPOINT },
+	{ 0x9c668086, SPI_LYNXPOINT },
+	{ 0x9ce58086, SPI_LYNXPOINT },
+	{ 0x9ce68086, SPI_LYNXPOINT },
+	{ 0x9d298086, SPI_SUNRISEPOINT },
+	{ 0x9d2a8086, SPI_SUNRISEPOINT },
+	{ 0xa1298086, SPI_SUNRISEPOINT },
+	{ 0xa12a8086, SPI_SUNRISEPOINT },
+	{ 0xa2a98086, SPI_SUNRISEPOINT },
+	{ 0xa2aa8086, SPI_SUNRISEPOINT },
+	{ 0xa3a98086, SPI_SUNRISEPOINT },
+	{ 0xa3aa8086, SPI_SUNRISEPOINT },
+};
+
+static int
+intelspi_pci_probe(device_t dev)
+{
+	struct intelspi_softc *sc = device_get_softc(dev);
+	uint32_t devid = pci_get_devid(dev);
+	int i;
+
+	for (i = 0; i < nitems(intelspi_pci_devices); i++) {
+		if (intelspi_pci_devices[i].devid == devid) {
+			sc->sc_vers = intelspi_pci_devices[i].vers;
+			/* The PCI device is listed in ACPI too.
+			 * Not that we use the handle for anything... */
+			sc->sc_handle = acpi_get_handle(dev);
+			device_set_desc(dev, intelspi_infos[sc->sc_vers].desc);
+			return (BUS_PROBE_DEFAULT);
+		}
+	}
+
+	return (ENXIO);
+}
+
+static int
+intelspi_pci_attach(device_t dev)
+{
+	struct intelspi_softc *sc = device_get_softc(dev);
+
+	sc->sc_mem_rid = PCIR_BAR(0);
+	sc->sc_irq_rid = 0;
+	if (pci_alloc_msi(dev, &sc->sc_irq_rid)) {
+		device_printf(dev, "Using MSI\n");
+	}
+
+	return (intelspi_attach(dev));
+}
+
+static int
+intelspi_pci_detach(device_t dev)
+{
+	struct intelspi_softc *sc = device_get_softc(dev);
+	int err;
+
+	err = intelspi_detach(dev);
+	if (err)
+		return (err);
+
+	if (sc->sc_irq_rid != 0)
+		pci_release_msi(dev);
+
+	return (0);
+}
+
+static device_method_t intelspi_pci_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe, intelspi_pci_probe),
+	DEVMETHOD(device_attach, intelspi_pci_attach),
+	DEVMETHOD(device_detach, intelspi_pci_detach),
+	DEVMETHOD(device_suspend, intelspi_suspend),
+	DEVMETHOD(device_resume, intelspi_resume),
+
+	/* SPI interface */
+	DEVMETHOD(spibus_transfer, intelspi_transfer),
+
+	DEVMETHOD_END
+};
+
+static driver_t intelspi_pci_driver = {
+	"spi",
+	intelspi_pci_methods,
+	sizeof(struct intelspi_softc),
+};
+
+DRIVER_MODULE(intelspi, pci, intelspi_pci_driver, 0, 0);
+MODULE_DEPEND(intelspi, pci, 1, 1, 1);
+MODULE_DEPEND(intelspi, spibus, 1, 1, 1);
+MODULE_PNP_INFO("W32:vendor/device", pci, intelspi, intelspi_pci_devices,
+    nitems(intelspi_pci_devices));
diff --git a/sys/modules/intelspi/Makefile b/sys/modules/intelspi/Makefile
index b2bceee4b3f8..2220a0b87232 100644
--- a/sys/modules/intelspi/Makefile
+++ b/sys/modules/intelspi/Makefile
@@ -2,7 +2,7 @@
 
 .PATH:	${SRCTOP}/sys/dev/intel
 KMOD=	intelspi
-SRCS=	spi.c
-SRCS+=	acpi_if.h device_if.h bus_if.h opt_acpi.h spibus_if.h
+SRCS=	spi.c spi_acpi.c spi_pci.c
+SRCS+=	acpi_if.h pci_if.h device_if.h bus_if.h opt_acpi.h opt_pci.h spibus_if.h
 
 .include <bsd.kmod.mk>