Download raw body.
intel lpss spi driver
Needed for an upcoming driver...
diff --git sys/arch/amd64/conf/GENERIC sys/arch/amd64/conf/GENERIC
index d30c1d85139..d119b9d45b0 100644
--- sys/arch/amd64/conf/GENERIC
+++ sys/arch/amd64/conf/GENERIC
@@ -206,6 +206,9 @@ wskbd* at icc? mux 1
skgpio0 at isa? port 0x680 # Soekris net6501 GPIO and LEDs
gpio* at skgpio?
+ispi* at acpi? # Intel LPSS SPI controller
+ispi* at pci?
+
#option PCMCIAVERBOSE
# PCI PCMCIA controllers
diff --git sys/conf/files sys/conf/files
index 1390f7d29c3..88454c9271c 100644
--- sys/conf/files
+++ sys/conf/files
@@ -20,6 +20,7 @@ define radiobus {}
define i2cbus {}
define gpiobus {}
define onewirebus {}
+define spi {}
define video {}
define intrmap {}
define fdt {[early = 0]}
@@ -566,6 +567,10 @@ device qcscm
device ufshci: scsi
file dev/ic/ufshci.c ufshci needs-flag
+# Intel LPSS SPI controller
+device ispi: spi
+file dev/ic/ispi.c ispi
+
# legitimate pseudo-devices
pseudo-device vnd: disk
pseudo-device rd: disk
diff --git sys/dev/acpi/acpi.c sys/dev/acpi/acpi.c
index 7a57236436a..4d41dff3b40 100644
--- sys/dev/acpi/acpi.c
+++ sys/dev/acpi/acpi.c
@@ -62,7 +62,6 @@ struct pool acpiwqpool;
#define ACPIEN_RETRIES 15
-struct aml_node *acpi_pci_match(struct device *, struct pci_attach_args *);
pcireg_t acpi_pci_min_powerstate(pci_chipset_tag_t, pcitag_t);
void acpi_pci_set_powerstate(pci_chipset_tag_t, pcitag_t, int, int);
int acpi_pci_notify(struct aml_node *, int, void *);
diff --git sys/dev/acpi/acpivar.h sys/dev/acpi/acpivar.h
index 819a4a5f4e8..f683efc5690 100644
--- sys/dev/acpi/acpivar.h
+++ sys/dev/acpi/acpivar.h
@@ -314,6 +314,8 @@ int acpi_bus_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
bus_space_handle_t *);
void acpi_bus_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+struct aml_node *acpi_pci_match(struct device *, struct pci_attach_args *);
+
struct bios_attach_args;
int acpi_probe(struct device *, struct cfdata *, struct bios_attach_args *);
u_int acpi_checksum(const void *, size_t);
diff --git sys/dev/acpi/files.acpi sys/dev/acpi/files.acpi
index 4c465a0d661..6fc08faca3f 100644
--- sys/dev/acpi/files.acpi
+++ sys/dev/acpi/files.acpi
@@ -212,6 +212,10 @@ file dev/acpi/dwgpio.c dwgpio
attach dwiic at acpi with dwiic_acpi
file dev/acpi/dwiic_acpi.c dwiic_acpi
+# Intel LPSS SPI controller
+attach ispi at acpi with ispi_acpi
+file dev/acpi/ispi_acpi.c ispi_acpi
+
# Chromebook keyboard backlight
device acpicbkbd
attach acpicbkbd at acpi
diff --git sys/dev/acpi/ispi_acpi.c sys/dev/acpi/ispi_acpi.c
new file mode 100644
index 00000000000..345bffa0d7e
--- /dev/null
+++ sys/dev/acpi/ispi_acpi.c
@@ -0,0 +1,116 @@
+/* $OpenBSD$ */
+/*
+ * Intel LPSS SPI controller
+ * ACPI attachment
+ *
+ * Copyright (c) 2015-2019 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include <dev/ic/ispivar.h>
+
+int ispi_acpi_match(struct device *, void *, void *);
+void ispi_acpi_attach(struct device *, struct device *, void *);
+
+int ispi_activate(struct device *, int);
+void ispi_acpi_bus_scan(struct ispi_softc *);
+
+int ispi_configure(void *, int, int, int);
+void ispi_start(struct ispi_softc *);
+void ispi_send(struct ispi_softc *);
+void ispi_recv(struct ispi_softc *);
+
+struct cfattach ispi_acpi_ca = {
+ sizeof(struct ispi_softc),
+ ispi_acpi_match,
+ ispi_acpi_attach,
+ NULL,
+ ispi_activate,
+};
+
+const char *ispi_acpi_hids[] = {
+ "INT33C0",
+ "INT33C1",
+ "INT3430",
+ "INT3431",
+ NULL
+};
+
+int
+ispi_acpi_match(struct device *parent, void *match, void *aux)
+{
+ struct acpi_attach_args *aaa = aux;
+ struct cfdata *cf = match;
+
+ return acpi_matchhids(aaa, ispi_acpi_hids, cf->cf_driver->cd_name);
+}
+
+void
+ispi_acpi_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ispi_softc *sc = (struct ispi_softc *)self;
+ struct acpi_attach_args *aa = aux;
+
+ sc->sc_acpi = (struct acpi_softc *)parent;
+ sc->sc_devnode = aa->aaa_node;
+ rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);
+
+ printf(": %s", sc->sc_devnode->name);
+
+ ispi_init(sc);
+}
+
+void
+ispi_acpi_bus_scan(struct ispi_softc *sc)
+{
+ aml_find_node(sc->sc_devnode, "_HID", ispi_acpi_found_hid, sc);
+}
+
+int
+ispi_acpi_found_hid(struct aml_node *node, void *arg)
+{
+ struct ispi_softc *sc = (struct ispi_softc *)arg;
+ int64_t sta;
+ char cdev[32], dev[32];
+
+ if (node->parent == sc->sc_devnode)
+ return 0;
+
+ if (acpi_parsehid(node, arg, cdev, dev, sizeof(cdev)) != 0)
+ return 0;
+
+ sta = acpi_getsta(acpi_softc, node->parent);
+ if ((sta & STA_PRESENT) == 0)
+ return 0;
+
+ DPRINTF(("%s: found HID %s at %s\n", sc->sc_dev.dv_xname, dev,
+ aml_nodename(node)));
+
+ acpi_attach_deps(acpi_softc, node->parent);
+
+ /* TODO */
+
+ return 0;
+}
diff --git sys/dev/ic/ispi.c sys/dev/ic/ispi.c
new file mode 100644
index 00000000000..a8aa1e2af65
--- /dev/null
+++ sys/dev/ic/ispi.c
@@ -0,0 +1,368 @@
+/* $OpenBSD$ */
+/*
+ * Intel LPSS SPI controller
+ *
+ * Copyright (c) 2015-2019 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+
+#include <dev/acpi/acpireg.h>
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+
+#include <dev/spi/spivar.h>
+
+#include <dev/pci/lpssreg.h>
+#include <dev/ic/ispivar.h>
+
+#define SSCR0 0x0 /* SSP Control Register 0 */
+#define SSCR0_EDSS_0 (0 << 20)
+#define SSCR0_EDSS_1 (1 << 20)
+#define SSCR0_SCR_SHIFT (8)
+#define SSCR0_SCR_MASK (0xFFF)
+#define SSCR0_SSE (1 << 7)
+#define SSCR0_ECS_ON_CHIP (0 << 6)
+#define SSCR0_FRF_MOTOROLA (0 << 4)
+#define SSCR0_DSS_SHIFT (0)
+#define SSCR0_DSS_MASK (0xF)
+#define SSCR1 0x4 /* SSP Control Register 1 */
+#define SSCR1_RIE (1 << 0) /* Receive FIFO Interrupt Enable */
+#define SSCR1_TIE (1 << 1) /* Transmit FIFO Interrupt Enable */
+#define SSCR1_LBM (1 << 2) /* Loop-Back Mode */
+#define SSCR1_SPO_LOW (0 << 3) /* Motorola SPI SSPSCLK polarity setting */
+#define SSCR1_SPO_HIGH (1 << 3)
+#define SSCR1_SPH_FIRST (0 << 4) /* Motorola SPI SSPSCLK phase setting */
+#define SSCR1_SPH_SECOND (1 << 4)
+#define SSCR1_MWDS (1 << 5) /* Microwire Transmit Data Size */
+#define SSCR1_IFS_LOW (0 << 16)
+#define SSCR1_IFS_HIGH (1 << 16)
+#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interrupt Enable */
+#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */
+#define SSCR1_RX_THRESH_DEF 8
+#define SSCR1_RX_THRESH(x) (((x) - 1) << 10)
+#define SSCR1_RFT (0x00003c00) /* Receive FIFO Threshold (mask) */
+#define SSCR1_TX_THRESH_DEF 8
+#define SSCR1_TX_THRESH(x) (((x) - 1) << 6)
+#define SSCR1_TFT (0x000003c0) /* Transmit FIFO Threshold (mask) */
+#define SSSR 0x8 /* SSP Status Register */
+#define SSSR_TUR (1 << 21) /* Tx FIFO underrun */
+#define SSSR_TINT (1 << 19) /* Rx Time-out interrupt */
+#define SSSR_PINT (1 << 18) /* Peripheral trailing byte interrupt */
+#define SSSR_ROR (1 << 7) /* Rx FIFO Overrun */
+#define SSSR_BSY (1 << 4) /* SSP Busy */
+#define SSSR_RNE (1 << 3) /* Receive FIFO not empty */
+#define SSSR_TNF (1 << 2) /* Transmit FIFO not full */
+#define SSDR 0x10 /* SSP Data Register */
+#define SSTO 0x28 /* SSP Time out */
+#define SSPSP 0x2C /* SSP Programmable Serial Protocol */
+#define SSITF 0x44 /* SPI Transmit FIFO */
+#define SSITF_LEVEL_SHIFT (16)
+#define SSITF_LEVEL_MASK (0x3f)
+#define SSITF_TX_LO_THRESH(x) (((x) - 1) << 8)
+#define SSITF_TX_HI_THRESH(x) ((x) - 1)
+#define SSIRF 0x48 /* SPI Receive FIFO */
+#define SSIRF_LEVEL_SHIFT (8)
+#define SSIRF_LEVEL_MASK (0x3f)
+#define SSIRF_RX_THRESH(x) ((x) - 1)
+
+void ispi_cs_change(struct ispi_softc *, int);
+uint32_t ispi_lpss_read(struct ispi_softc *, int);
+void ispi_lpss_write(struct ispi_softc *, int, uint32_t);
+int ispi_rx_fifo_empty(struct ispi_softc *);
+int ispi_tx_fifo_full(struct ispi_softc *);
+
+struct cfdriver ispi_cd = {
+ NULL, "ispi", DV_DULL
+};
+
+int
+ispi_activate(struct device *self, int act)
+{
+ config_activate_children(self, act);
+ return 0;
+}
+
+int
+ispi_spi_print(void *aux, const char *pnp)
+{
+ struct spi_attach_args *sa = aux;
+
+ if (pnp != NULL)
+ printf("\"%s\" at %s", sa->sa_name, pnp);
+
+ return UNCONF;
+}
+
+int
+ispi_acquire_bus(void *cookie, int flags)
+{
+ struct ispi_softc *sc = cookie;
+
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+
+ rw_enter(&sc->sc_buslock, RW_WRITE);
+ ispi_cs_change(sc, 1);
+
+ return 0;
+}
+
+void
+ispi_release_bus(void *cookie, int flags)
+{
+ struct ispi_softc *sc = cookie;
+
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+
+ ispi_cs_change(sc, 0);
+ rw_exit(&sc->sc_buslock);
+}
+
+void
+ispi_init(struct ispi_softc *sc)
+{
+ uint32_t csctrl;
+
+ if (!sc->sc_rx_threshold)
+ sc->sc_rx_threshold = SSCR1_RX_THRESH_DEF;
+ if (!sc->sc_tx_threshold)
+ sc->sc_tx_threshold = SSCR1_TX_THRESH_DEF;
+
+ ispi_write(sc, SSCR0, 0);
+ ispi_write(sc, SSCR1, 0);
+ ispi_write(sc, SSTO, 0);
+ ispi_write(sc, SSPSP, 0);
+
+ /* lpss: enable software chip select control */
+ csctrl = ispi_lpss_read(sc, sc->sc_reg_cs_ctrl);
+ csctrl &= ~(LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH);
+ csctrl |= LPSS_CS_CONTROL_SW_MODE | LPSS_CS_CONTROL_CS_HIGH;
+ ispi_lpss_write(sc, sc->sc_reg_cs_ctrl, csctrl);
+
+ ispi_cs_change(sc, 0);
+}
+
+uint32_t
+ispi_read(struct ispi_softc *sc, int reg)
+{
+ uint32_t val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
+ DPRINTF(("%s: %s(0x%x) = 0x%x\n", sc->sc_dev.dv_xname, __func__, reg,
+ val));
+ return val;
+}
+
+void
+ispi_write(struct ispi_softc *sc, int reg, uint32_t val)
+{
+ DPRINTF(("%s: %s(0x%x, 0x%x)\n", sc->sc_dev.dv_xname, __func__, reg,
+ val));
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
+}
+
+uint32_t
+ispi_lpss_read(struct ispi_softc *sc, int reg)
+{
+ return ispi_read(sc, sc->sc_lpss_reg_offset + reg);
+}
+
+void
+ispi_lpss_write(struct ispi_softc *sc, int reg, uint32_t val)
+{
+ ispi_write(sc, sc->sc_lpss_reg_offset + reg, val);
+}
+
+void
+ispi_config(void *cookie, struct spi_config *conf)
+{
+ struct ispi_softc *sc = cookie;
+ uint32_t sscr0, sscr1;
+ unsigned int rate;
+ int rx_threshold, tx_threshold;
+
+ DPRINTF(("%s: %s: ssp clk %ld sc_freq %d\n", sc->sc_dev.dv_xname,
+ __func__, sc->sc_ssp_clk, conf->sc_freq));
+
+ rate = min(sc->sc_ssp_clk, conf->sc_freq);
+ if (rate <= 1)
+ rate = sc->sc_ssp_clk;
+
+ sscr0 = ((sc->sc_ssp_clk / rate) - 1) & 0xfff;
+ sscr0 |= SSCR0_FRF_MOTOROLA;
+ sscr0 |= (conf->sc_bpw - 1);
+ sscr0 |= SSCR0_SSE;
+ sscr0 |= (conf->sc_bpw > 16 ? SSCR0_EDSS_1 : SSCR0_EDSS_0);
+
+ ispi_clear_status(sc);
+
+ rx_threshold = SSIRF_RX_THRESH(sc->sc_rx_threshold);
+ tx_threshold = SSITF_TX_LO_THRESH(sc->sc_tx_threshold) |
+ SSITF_TX_HI_THRESH(sc->sc_tx_threshold_hi);
+
+ if ((ispi_read(sc, SSIRF) & 0xff) != rx_threshold)
+ ispi_write(sc, SSIRF, rx_threshold);
+
+ if ((ispi_read(sc, SSITF) & 0xffff) != tx_threshold)
+ ispi_write(sc, SSITF, tx_threshold);
+
+ ispi_write(sc, SSCR0, sscr0 & ~SSCR0_SSE);
+
+ ispi_write(sc, SSTO, 1000); /* timeout */
+
+ sscr1 = (SSCR1_RX_THRESH(sc->sc_rx_threshold) & SSCR1_RFT) |
+ (SSCR1_TX_THRESH(sc->sc_tx_threshold) & SSCR1_TFT);
+
+#if 0
+ /* enable interrupts */
+ sscr1 |= (SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE);
+#endif
+ ispi_write(sc, SSCR1, sscr1);
+
+ /* restart SSP */
+ ispi_write(sc, SSCR0, sscr0);
+ ispi_write(sc, SSCR1, sscr1);
+}
+
+int
+ispi_flush(struct ispi_softc *sc)
+{
+ int tries = 500;
+
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+
+ do {
+ while (!ispi_rx_fifo_empty(sc))
+ ispi_read(sc, SSDR);
+ } while ((ispi_read(sc, SSSR) & SSSR_BSY) && --tries);
+
+ DPRINTF(("%s: flushed with %d left\n", sc->sc_dev.dv_xname, tries));
+
+ ispi_write(sc, SSSR, SSSR_ROR);
+
+ return (tries != 0);
+}
+
+void
+ispi_cs_change(struct ispi_softc *sc, int cs_assert)
+{
+ uint32_t t;
+ int tries = 500;
+
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+
+ if (!cs_assert) {
+ /* wait until idle */
+ while ((ispi_read(sc, SSSR) & SSSR_BSY) && --tries)
+ ;
+ }
+
+ t = ispi_lpss_read(sc, sc->sc_reg_cs_ctrl);
+ if (cs_assert)
+ t &= ~LPSS_CS_CONTROL_CS_HIGH;
+ else
+ t |= LPSS_CS_CONTROL_CS_HIGH;
+ ispi_lpss_write(sc, sc->sc_reg_cs_ctrl, t);
+
+ DELAY(10);
+}
+
+int
+ispi_transfer(void *cookie, char *out, char *in, int len, int flags)
+{
+ struct ispi_softc *sc = cookie;
+ int s = spltty();
+
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+
+ sc->sc_ridx = sc->sc_widx = 0;
+
+ /* drain input buffer */
+ ispi_flush(sc);
+
+ while (sc->sc_ridx < len || sc->sc_widx < len) {
+ while (!ispi_rx_fifo_empty(sc) && sc->sc_ridx < len) {
+ if (in)
+ in[sc->sc_ridx] = ispi_read(sc, SSDR) & 0xff;
+ else
+ ispi_read(sc, SSDR);
+
+ sc->sc_ridx++;
+ }
+
+ while (!ispi_tx_fifo_full(sc) && sc->sc_widx < len) {
+ if (out)
+ ispi_write(sc, SSDR, out[sc->sc_widx]);
+ else
+ ispi_write(sc, SSDR, 0);
+
+ sc->sc_widx++;
+ }
+ }
+
+ DPRINTF(("%s: %s: done transmitting %s %d\n", sc->sc_dev.dv_xname,
+ __func__, (out ? "out" : "in"), len));
+
+ splx(s);
+ return 0;
+}
+
+int
+ispi_status(struct ispi_softc *sc)
+{
+ return ispi_read(sc, SSSR);
+}
+
+void
+ispi_clear_status(struct ispi_softc *sc)
+{
+ ispi_write(sc, SSSR, SSSR_TUR | SSSR_TINT | SSSR_PINT | SSSR_ROR);
+}
+
+int
+ispi_rx_fifo_empty(struct ispi_softc *sc)
+{
+ return !(ispi_status(sc) & SSSR_RNE);
+}
+
+int
+ispi_tx_fifo_full(struct ispi_softc *sc)
+{
+ return !(ispi_status(sc) & SSSR_TNF);
+}
+
+int
+ispi_rx_fifo_overrun(struct ispi_softc *sc)
+{
+ if (ispi_status(sc) & SSSR_ROR) {
+ printf("%s: %s\n", sc->sc_dev.dv_xname, __func__);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ispi_intr(void *arg)
+{
+#ifdef ISPI_DEBUG
+ struct ispi_softc *sc = arg;
+ DPRINTF(("%s: %s\n", sc->sc_dev.dv_xname, __func__));
+#endif
+ return 1;
+}
diff --git sys/dev/ic/ispivar.h sys/dev/ic/ispivar.h
new file mode 100644
index 00000000000..2d9ae17b2f1
--- /dev/null
+++ sys/dev/ic/ispivar.h
@@ -0,0 +1,101 @@
+/* $OpenBSD$ */
+/*
+ * Intel LPSS SPI controller
+ *
+ * Copyright (c) 2015-2018 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+
+#include "acpi.h"
+#if NACPI > 0
+#include <dev/acpi/acpivar.h>
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/amltypes.h>
+#include <dev/acpi/dsdt.h>
+#endif
+
+#include <dev/pci/pcivar.h>
+
+#include <dev/spi/spivar.h>
+
+/* #define ISPI_DEBUG */
+
+#ifdef ISPI_DEBUG
+#define DPRINTF(x) printf x
+#else
+#define DPRINTF(x)
+#endif
+
+struct ispi_softc {
+ struct device sc_dev;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+
+ void *sc_ih;
+ struct pci_attach_args sc_paa;
+ long sc_ssp_clk;
+ int sc_lpss_reg_offset;
+ int sc_reg_cs_ctrl;
+ int sc_rx_threshold;
+ int sc_tx_threshold;
+ int sc_tx_threshold_hi;
+
+ struct spi_controller sc_spi_tag;
+ struct rwlock sc_buslock;
+ struct spi_config sc_spi_conf;
+
+ int sc_ridx;
+ int sc_widx;
+
+#if NACPI > 0
+ struct acpi_softc *sc_acpi;
+ struct aml_node *sc_devnode;
+#endif
+ u_int32_t sc_caps;
+};
+
+void ispi_init(struct ispi_softc *sc);
+
+int ispi_match(struct device *, void *, void *);
+void ispi_attach(struct device *, struct device *, void *);
+int ispi_activate(struct device *, int);
+int ispi_spi_print(void *aux, const char *pnp);
+
+void ispi_write(struct ispi_softc *sc, int reg, uint32_t val);
+uint32_t ispi_read(struct ispi_softc *sc, int reg);
+
+#if NACPI > 0
+int ispi_acpi_found_hid(struct aml_node *node, void *arg);
+#endif
+
+void ispi_config(void *, struct spi_config *);
+int ispi_acquire_bus(void *, int);
+void ispi_release_bus(void *, int);
+int ispi_transfer(void *, char *, char *, int, int);
+void ispi_start(struct ispi_softc *);
+void ispi_send(struct ispi_softc *);
+void ispi_recv(struct ispi_softc *);
+
+int ispi_intr(void *);
+int ispi_status(struct ispi_softc *);
+int ispi_flush(struct ispi_softc *);
+void ispi_clear_status(struct ispi_softc *);
+int ispi_rx_fifo_empty(struct ispi_softc *);
+int ispi_rx_fifo_overrun(struct ispi_softc *);
diff --git sys/dev/pci/com_pci.c sys/dev/pci/com_pci.c
index e0cd1ed4d14..9461de4b86d 100644
--- sys/dev/pci/com_pci.c
+++ sys/dev/pci/com_pci.c
@@ -22,44 +22,13 @@
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+#include <dev/pci/lpssreg.h>
#include <dev/ic/comreg.h>
#include <dev/ic/comvar.h>
#define com_usr 31 /* Synopsys DesignWare UART */
-/* Intel Low Power Subsystem */
-#define LPSS_CLK 0x200
-#define LPSS_CLK_GATE (1 << 0)
-#define LPSS_CLK_MDIV_SHIFT 1
-#define LPSS_CLK_MDIV_MASK 0x3fff
-#define LPSS_CLK_NDIV_SHIFT 16
-#define LPSS_CLK_NDIV_MASK 0x3fff
-#define LPSS_CLK_UPDATE (1U << 31)
-#define LPSS_RESETS 0x204
-#define LPSS_RESETS_FUNC (3 << 0)
-#define LPSS_RESETS_IDMA (1 << 2)
-#define LPSS_ACTIVELTR 0x210
-#define LPSS_IDLELTR 0x214
-#define LPSS_LTR_VALUE_MASK (0x3ff << 0)
-#define LPSS_LTR_SCALE_MASK (0x3 << 10)
-#define LPSS_LTR_SCALE_1US (2 << 10)
-#define LPSS_LTR_SCALE_32US (3 << 10)
-#define LPSS_LTR_REQ (1 << 15)
-#define LPSS_SSP 0x220
-#define LPSS_SSP_DIS_DMA_FIN (1 << 0)
-#define LPSS_REMAP_ADDR 0x240
-#define LPSS_CAPS 0x2fc
-#define LPSS_CAPS_TYPE_I2C (0 << 4)
-#define LPSS_CAPS_TYPE_UART (1 << 4)
-#define LPSS_CAPS_TYPE_SPI (2 << 4)
-#define LPSS_CAPS_TYPE_MASK (0xf << 4)
-#define LPSS_CAPS_NO_IDMA (1 << 8)
-
-#define LPSS_REG_OFF 0x200
-#define LPSS_REG_SIZE 0x100
-#define LPSS_REG_NUM (LPSS_REG_SIZE / sizeof(uint32_t))
-
#define HREAD4(sc, reg) \
(bus_space_read_4((sc)->sc.sc_iot, (sc)->sc.sc_ioh, (reg)))
#define HWRITE4(sc, reg, val) \
@@ -135,7 +104,8 @@ com_pci_attach(struct device *parent, struct device *self, void *aux)
*/
if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_INTEL) {
caps = HREAD4(sc, LPSS_CAPS);
- if ((caps & LPSS_CAPS_TYPE_MASK) != LPSS_CAPS_TYPE_UART) {
+ if (((caps & LPSS_CAPS_TYPE_MASK) >> LPSS_CAPS_TYPE_SHIFT) !=
+ LPSS_CAPS_TYPE_UART) {
bus_space_unmap(sc->sc.sc_iot, sc->sc.sc_ioh,
sc->sc_ios);
printf(": not a UART\n");
diff --git sys/dev/pci/dwiic_pci.c sys/dev/pci/dwiic_pci.c
index b51aa211991..0bd675c6dfa 100644
--- sys/dev/pci/dwiic_pci.c
+++ sys/dev/pci/dwiic_pci.c
@@ -24,20 +24,10 @@
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
+#include <dev/pci/lpssreg.h>
#include <dev/ic/dwiicvar.h>
-/* 13.3: I2C Additional Registers Summary */
-#define LPSS_RESETS 0x204
-#define LPSS_RESETS_I2C (1 << 0) | (1 << 1)
-#define LPSS_RESETS_IDMA (1 << 2)
-#define LPSS_ACTIVELTR 0x210
-#define LPSS_IDLELTR 0x214
-#define LPSS_CAPS 0x2fc
-#define LPSS_CAPS_NO_IDMA (1 << 8)
-#define LPSS_CAPS_TYPE_SHIFT 4
-#define LPSS_CAPS_TYPE_MASK (0xf << LPSS_CAPS_TYPE_SHIFT)
-
int dwiic_pci_match(struct device *, void *, void *);
void dwiic_pci_attach(struct device *, struct device *, void *);
int dwiic_pci_activate(struct device *, int);
@@ -232,7 +222,7 @@ dwiic_pci_attach(struct device *parent, struct device *self, void *aux)
/* un-reset - page 958 */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS,
- (LPSS_RESETS_I2C | LPSS_RESETS_IDMA));
+ (LPSS_RESETS_FUNC | LPSS_RESETS_IDMA));
#if NACPI > 0
/* fetch timing parameters from ACPI, if possible */
@@ -308,7 +298,7 @@ dwiic_pci_activate(struct device *self, int act)
case DVACT_RESUME:
DELAY(10000); /* 10 msec */
bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS,
- (LPSS_RESETS_I2C | LPSS_RESETS_IDMA));
+ (LPSS_RESETS_FUNC | LPSS_RESETS_IDMA));
DELAY(10000); /* 10 msec */
break;
}
diff --git sys/dev/pci/files.pci sys/dev/pci/files.pci
index c0c71355de8..16279cd6e33 100644
--- sys/dev/pci/files.pci
+++ sys/dev/pci/files.pci
@@ -897,5 +897,9 @@ device nhi
attach nhi at pci
file dev/pci/nhi.c nhi
+# Intel LPSS SPI controller
+attach ispi at pci with ispi_pci
+file dev/pci/ispi_pci.c ispi_pci
+
include "dev/pci/files.agp"
include "dev/pci/drm/files.drm"
diff --git sys/dev/pci/ispi_pci.c sys/dev/pci/ispi_pci.c
new file mode 100644
index 00000000000..2c3872964f4
--- /dev/null
+++ sys/dev/pci/ispi_pci.c
@@ -0,0 +1,157 @@
+/* $OpenBSD$ */
+/*
+ * Intel LPSS SPI controller
+ * PCI attachment
+ *
+ * Copyright (c) 2015-2018 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/lpssreg.h>
+
+#include <dev/ic/ispivar.h>
+
+int ispi_pci_match(struct device *, void *, void *);
+void ispi_pci_attach(struct device *, struct device *, void *);
+int ispi_pci_activate(struct device *, int);
+void ispi_pci_bus_scan(struct ispi_softc *);
+
+struct cfattach ispi_pci_ca = {
+ sizeof(struct ispi_softc),
+ ispi_pci_match,
+ ispi_pci_attach,
+ NULL,
+ ispi_pci_activate,
+};
+
+const struct pci_matchid ispi_pci_ids[] = {
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_100SERIES_LP_SPI_3 },
+};
+
+int
+ispi_pci_match(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid(aux, ispi_pci_ids, nitems(ispi_pci_ids)));
+}
+
+void
+ispi_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct ispi_softc *sc = (struct ispi_softc *)self;
+ struct pci_attach_args *pa = aux;
+ bus_size_t iosize;
+ pci_intr_handle_t ih;
+ const char *intrstr = NULL;
+ uint8_t type;
+
+ memcpy(&sc->sc_paa, pa, sizeof(sc->sc_paa));
+
+ pci_set_powerstate(pa->pa_pc, pa->pa_tag, PCI_PMCSR_STATE_D0);
+
+ if (pci_mapreg_map(pa, PCI_MAPREG_START, PCI_MAPREG_MEM_TYPE_64BIT, 0,
+ &sc->sc_iot, &sc->sc_ioh, NULL, &iosize, 0)) {
+ printf(": can't map mem space\n");
+ return;
+ }
+
+ sc->sc_caps = bus_space_read_4(sc->sc_iot, sc->sc_ioh, LPSS_CAPS);
+ type = (sc->sc_caps & LPSS_CAPS_TYPE_MASK) >> LPSS_CAPS_TYPE_SHIFT;
+ if (type != LPSS_CAPS_TYPE_SPI) {
+ printf(": type %d not supported\n", type);
+ return;
+ }
+
+ switch (PCI_PRODUCT(pa->pa_id)) {
+ case PCI_PRODUCT_INTEL_100SERIES_LP_SPI_3:
+ /* SPT */
+ sc->sc_lpss_reg_offset = 0x200;
+ sc->sc_reg_cs_ctrl = 0x24;
+ sc->sc_rx_threshold = 1;
+ sc->sc_tx_threshold = 32;
+ sc->sc_tx_threshold_hi = 56;
+ sc->sc_ssp_clk = 9600000;
+ break;
+ default:
+ printf(": unknown parameters\n");
+ return;
+ }
+
+ /* un-reset - page 958 */
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS,
+ (LPSS_RESETS_FUNC | LPSS_RESETS_IDMA));
+
+ /* install interrupt handler */
+ if (pci_intr_map(&sc->sc_paa, &ih) == 0) {
+ intrstr = pci_intr_string(sc->sc_paa.pa_pc, ih);
+ sc->sc_ih = pci_intr_establish(sc->sc_paa.pa_pc, ih, IPL_BIO,
+ ispi_intr, sc, sc->sc_dev.dv_xname);
+ if (sc->sc_ih != NULL) {
+ printf(": %s", intrstr);
+ }
+ }
+
+ printf("\n");
+
+ rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);
+
+ ispi_init(sc);
+
+ /* setup spi controller */
+ sc->sc_spi_tag.sc_cookie = sc;
+ sc->sc_spi_tag.sc_config = ispi_config;
+ sc->sc_spi_tag.sc_transfer = ispi_transfer;
+ sc->sc_spi_tag.sc_acquire_bus = ispi_acquire_bus;
+ sc->sc_spi_tag.sc_release_bus = ispi_release_bus;
+
+ ispi_pci_bus_scan(sc);
+}
+
+int
+ispi_pci_activate(struct device *self, int act)
+{
+ struct ispi_softc *sc = (struct ispi_softc *)self;
+
+ switch (act) {
+ case DVACT_WAKEUP:
+ bus_space_write_4(sc->sc_iot, sc->sc_ioh, LPSS_RESETS,
+ (LPSS_RESETS_FUNC | LPSS_RESETS_IDMA));
+ break;
+ }
+
+ ispi_activate(self, act);
+
+ return 0;
+}
+
+void
+ispi_pci_bus_scan(struct ispi_softc *sc)
+{
+#if NACPI > 0
+ struct aml_node *node = acpi_pci_match((struct device *)sc, &sc->sc_paa);
+
+ if (node == NULL)
+ return;
+
+ sc->sc_devnode = node;
+ aml_find_node(node, "_HID", ispi_acpi_found_hid, sc);
+#endif
+}
diff --git sys/dev/pci/lpssreg.h sys/dev/pci/lpssreg.h
new file mode 100644
index 00000000000..66d2f0b7b18
--- /dev/null
+++ sys/dev/pci/lpssreg.h
@@ -0,0 +1,48 @@
+/* $OpenBSD$ */
+/*
+ * Intel Low Power Subsystem
+ *
+ * Copyright (c) 2015-2017 joshua stein <jcs@openbsd.org>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* 13.3: I2C Additional Registers Summary */
+#define LPSS_CLK 0x200
+#define LPSS_CLK_GATE (1 << 0)
+#define LPSS_CLK_MDIV_SHIFT 1
+#define LPSS_CLK_MDIV_MASK 0x3fff
+#define LPSS_CLK_NDIV_SHIFT 16
+#define LPSS_CLK_NDIV_MASK 0x3fff
+#define LPSS_CLK_UPDATE (1U << 31)
+#define LPSS_RESETS 0x204
+#define LPSS_RESETS_FUNC (1 << 0) | (1 << 1)
+#define LPSS_RESETS_IDMA (1 << 2)
+#define LPSS_ACTIVELTR 0x210
+#define LPSS_IDLELTR 0x214
+#define LPSS_REMAP_ADDR 0x240
+#define LPSS_CAPS 0x2fc
+#define LPSS_CAPS_NO_IDMA (1 << 8)
+#define LPSS_CAPS_TYPE_SHIFT 4
+#define LPSS_CAPS_TYPE_MASK (0xf << LPSS_CAPS_TYPE_SHIFT)
+#define LPSS_CAPS_TYPE_I2C 0
+#define LPSS_CAPS_TYPE_UART 1
+#define LPSS_CAPS_TYPE_SPI 2
+#define LPSS_CAPS_CS_EN_SHIFT 9
+#define LPSS_CAPS_CS_EN_MASK (0xf << LPSS_CAPS_CS_EN_SHIFT)
+#define LPSS_CS_CONTROL_SW_MODE (1 << 0)
+#define LPSS_CS_CONTROL_CS_HIGH (1 << 1)
+
+#define LPSS_REG_OFF 0x200
+#define LPSS_REG_SIZE 0x100
+#define LPSS_REG_NUM (LPSS_REG_SIZE / sizeof(uint32_t))
diff --git sys/dev/spi/spivar.h sys/dev/spi/spivar.h
index c56b840a3b6..f00c6c57938 100644
--- sys/dev/spi/spivar.h
+++ sys/dev/spi/spivar.h
@@ -15,6 +15,9 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#ifndef _DEV_SPI_SPIVAR_H_
+#define _DEV_SPI_SPIVAR_H_
+
struct spi_config {
int sc_cs;
int sc_flags;
@@ -54,3 +57,5 @@ struct spi_attach_args {
(*(sc)->sc_acquire_bus)((sc)->sc_cookie, (flags))
#define spi_release_bus(sc, flags) \
(*(sc)->sc_release_bus)((sc)->sc_cookie, (flags))
+
+#endif /* _DEV_SPI_SPIVAR_H_ */
intel lpss spi driver