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:
parent
3c08673438
commit
1f40866feb
@ -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
|
||||
|
@ -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
99
sys/dev/intel/spi.h
Normal 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
111
sys/dev/intel/spi_acpi.c
Normal 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
138
sys/dev/intel/spi_pci.c
Normal 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));
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user