From: joshua stein Subject: intel lpss spi driver To: tech@openbsd.org Date: Wed, 12 Nov 2025 08:54:39 -0600 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 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +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 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#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 + * + * 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 +#include +#include +#include + +#include "acpi.h" +#if NACPI > 0 +#include +#include +#include +#include +#endif + +#include + +#include + +/* #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 #include #include +#include #include #include #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 #include #include +#include #include -/* 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 + * + * 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 +#include +#include +#include + +#include +#include +#include +#include + +#include + +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 + * + * 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_ */