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
This commit is contained in:
Val Packett 2023-04-24 12:41:52 +03:00 committed by Vladimir Kondratyev
parent 3c08673438
commit 1f40866feb
6 changed files with 484 additions and 109 deletions

View File

@ -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

View File

@ -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 @@ intelspi_attach(device_t dev)
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);
}
DRIVER_MODULE(intelspi, acpi, intelspi_driver, 0, 0);
MODULE_DEPEND(intelspi, acpi, 1, 1, 1);
MODULE_DEPEND(intelspi, spibus, 1, 1, 1);
/* Shutdown just in case */
INTELSPI_WRITE(sc, INTELSPI_SSPREG_SSCR0, 0);
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));
}

99
sys/dev/intel/spi.h Normal file
View File

@ -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

111
sys/dev/intel/spi_acpi.c Normal file
View File

@ -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);

138
sys/dev/intel/spi_pci.c Normal file
View File

@ -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));

View File

@ -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>