From owner-svn-src-stable-11@freebsd.org Sat Mar 24 23:23:34 2018 Return-Path: Delivered-To: svn-src-stable-11@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 22024F5575C; Sat, 24 Mar 2018 23:23:34 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id B3F15858E9; Sat, 24 Mar 2018 23:23:33 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id AD19F15FEC; Sat, 24 Mar 2018 23:23:33 +0000 (UTC) (envelope-from ian@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w2ONNXnd036281; Sat, 24 Mar 2018 23:23:33 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w2ONNVMg036256; Sat, 24 Mar 2018 23:23:31 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201803242323.w2ONNVMg036256@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sat, 24 Mar 2018 23:23:31 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r331506 - in stable/11/sys: arm/at91 arm/broadcom/bcm2835 arm/freescale/imx arm/freescale/vybrid arm/lpc arm/samsung/exynos arm/ti dev/flash dev/spibus dev/xilinx mips/atheros mips/medi... X-SVN-Group: stable-11 X-SVN-Commit-Author: ian X-SVN-Commit-Paths: in stable/11/sys: arm/at91 arm/broadcom/bcm2835 arm/freescale/imx arm/freescale/vybrid arm/lpc arm/samsung/exynos arm/ti dev/flash dev/spibus dev/xilinx mips/atheros mips/mediatek mips/rt305x modules ... X-SVN-Commit-Revision: 331506 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-stable-11@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: SVN commit messages for only the 11-stable src tree List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 24 Mar 2018 23:23:34 -0000 Author: ian Date: Sat Mar 24 23:23:31 2018 New Revision: 331506 URL: https://svnweb.freebsd.org/changeset/base/331506 Log: MFC r310017, r310229, r312289, r327260, r329539, r329544-r329546, r329620, r329729, r329911, r329999 r310017: [spi] reformat message This commit corrects print of nomatch (newline was too early) Submitted by: Hiroki Mori Reviewed by: ray, loos, mizhka Differential Revision: https://reviews.freebsd.org/D8749 r310229: ofw_spi: Parse property for the SPI mode and CS polarity. As cs is stored in a uint32_t, use the last bit to store the active high flag as it's unlikely that we will have that much CS. Reviewed by: loos Differential Revision: https://reviews.freebsd.org/D8614 r312289: [spibus] small code refactoring Merge 3 sequential printf calls into one. Reported by: rpokala Reviewed by: rpokala, adrian Approved by: adrian (mentor) Differential Revision: https://reviews.freebsd.org/D8795 r327260: SPDX: fix wrong license ID tag in dev/spibus. r329539: Provide public declarations for ofw_spibus_driver and ofw_spibus_devclass so other drivers can refer to them in DRIVER_MODULE() decls. r329544: Add modules/spi as a gathering point for SPI-related modules, analagous to modules/i2c for i2c/iicbus modules. Build spibus as a module. r329545: Add ofw_bus_if.h to SRCS. r329546: Build at45d and mx25l SPI flash drivers as modules. r329620: Add missing MODULE_DEPENDS(). r329729: Remove some files that snuck in via cut and paste. Having these compiled into the module causes the kobj method descriptors to be resolved incorrectly (by the compile-time linker instead of the kernel linker), which then leads to hours of frustrating debugging. r329911: Add a functional detach() routine, to make things kldunload-friendly. r329999: Add a SPI driver for imx5 and imx6. It can be compiled into the kernel with "device imx_spi" or loaded as a module, which is also named "imx_spi". Added: stable/11/sys/arm/freescale/imx/imx_spi.c - copied unchanged from r329999, head/sys/arm/freescale/imx/imx_spi.c stable/11/sys/modules/imx/imx_spi/ - copied from r329999, head/sys/modules/imx/imx_spi/ stable/11/sys/modules/spi/ - copied from r329546, head/sys/modules/spi/ Modified: stable/11/sys/arm/at91/at91_spi.c stable/11/sys/arm/broadcom/bcm2835/bcm2835_spi.c stable/11/sys/arm/freescale/imx/files.imx5 stable/11/sys/arm/freescale/imx/files.imx6 stable/11/sys/arm/freescale/imx/imx51_ccm.c stable/11/sys/arm/freescale/imx/imx6_ccm.c stable/11/sys/arm/freescale/imx/imx6_ccmreg.h stable/11/sys/arm/freescale/imx/imx_ccmvar.h stable/11/sys/arm/freescale/vybrid/vf_spi.c stable/11/sys/arm/lpc/lpc_spi.c stable/11/sys/arm/samsung/exynos/exynos5_spi.c stable/11/sys/arm/ti/ti_spi.c stable/11/sys/dev/flash/at45d.c stable/11/sys/dev/flash/mx25l.c stable/11/sys/dev/spibus/ofw_spibus.c stable/11/sys/dev/spibus/spibusvar.h stable/11/sys/dev/xilinx/axi_quad_spi.c stable/11/sys/mips/atheros/ar71xx_spi.c stable/11/sys/mips/mediatek/mtk_spi_v1.c stable/11/sys/mips/mediatek/mtk_spi_v2.c stable/11/sys/mips/rt305x/rt305x_spi.c stable/11/sys/modules/Makefile stable/11/sys/modules/imx/Makefile stable/11/sys/modules/spi/at45d/Makefile stable/11/sys/modules/spi/mx25l/Makefile Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/arm/at91/at91_spi.c ============================================================================== --- stable/11/sys/arm/at91/at91_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/at91/at91_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -304,6 +304,8 @@ at91_spi_transfer(device_t dev, device_t child, struct /* get the proper chip select */ spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + sc = device_get_softc(dev); i = 0; Modified: stable/11/sys/arm/broadcom/bcm2835/bcm2835_spi.c ============================================================================== --- stable/11/sys/arm/broadcom/bcm2835/bcm2835_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/broadcom/bcm2835/bcm2835_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -436,6 +436,9 @@ bcm_spi_transfer(device_t dev, device_t child, struct /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); + + cs &= ~SPIBUS_CS_HIGH; + if (cs > 2) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, Modified: stable/11/sys/arm/freescale/imx/files.imx5 ============================================================================== --- stable/11/sys/arm/freescale/imx/files.imx5 Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/files.imx5 Sat Mar 24 23:23:31 2018 (r331506) @@ -52,3 +52,6 @@ dev/vt/hw/fb/vt_early_fb.c optional vt # Fast Ethernet Controller dev/ffec/if_ffec.c optional ffec +# SPI +arm/freescale/imx/imx_spi.c optional imx_spi + Modified: stable/11/sys/arm/freescale/imx/files.imx6 ============================================================================== --- stable/11/sys/arm/freescale/imx/files.imx6 Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/files.imx6 Sat Mar 24 23:23:31 2018 (r331506) @@ -22,6 +22,7 @@ arm/freescale/imx/imx_machdep.c standard arm/freescale/imx/imx_gpt.c optional imx_gpt arm/freescale/imx/imx_gpio.c optional gpio arm/freescale/imx/imx_i2c.c optional fsliic +arm/freescale/imx/imx_spi.c optional imx_spi arm/freescale/imx/imx6_sdma.c optional sdma arm/freescale/imx/imx6_audmux.c optional sound arm/freescale/imx/imx6_ssi.c optional sound Modified: stable/11/sys/arm/freescale/imx/imx51_ccm.c ============================================================================== --- stable/11/sys/arm/freescale/imx/imx51_ccm.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/imx51_ccm.c Sat Mar 24 23:23:31 2018 (r331506) @@ -622,6 +622,13 @@ imx_ccm_usbphy_enable(device_t dev) } uint32_t +imx_ccm_ecspi_hz(void) +{ + + return (imx51_get_clock(IMX51CLK_CSPI_CLK_ROOT)); +} + +uint32_t imx_ccm_ipg_hz(void) { @@ -655,3 +662,4 @@ imx_ccm_ahb_hz(void) return (imx51_get_clock(IMX51CLK_AHB_CLK_ROOT)); } + Modified: stable/11/sys/arm/freescale/imx/imx6_ccm.c ============================================================================== --- stable/11/sys/arm/freescale/imx/imx6_ccm.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/imx6_ccm.c Sat Mar 24 23:23:31 2018 (r331506) @@ -96,8 +96,9 @@ ccm_init_gates(struct ccm_softc *sc) reg = CCGR0_AIPS_TZ1 | CCGR0_AIPS_TZ2 | CCGR0_ABPHDMA; WR4(sc, CCM_CCGR0, reg); - /* enet, epit, gpt */ - reg = CCGR1_ENET | CCGR1_EPIT1 | CCGR1_GPT; + /* enet, epit, gpt, spi */ + reg = CCGR1_ENET | CCGR1_EPIT1 | CCGR1_GPT | CCGR1_ECSPI1 | + CCGR1_ECSPI2 | CCGR1_ECSPI3 | CCGR1_ECSPI4 | CCGR1_ECSPI5; WR4(sc, CCM_CCGR1, reg); /* ipmux & ipsync (bridges), iomux, i2c */ @@ -349,6 +350,13 @@ imx6_ccm_sata_enable(void) WR4(ccm_sc, CCM_ANALOG_PLL_ENET, v); return 0; +} + +uint32_t +imx_ccm_ecspi_hz(void) +{ + + return (60000000); } uint32_t Modified: stable/11/sys/arm/freescale/imx/imx6_ccmreg.h ============================================================================== --- stable/11/sys/arm/freescale/imx/imx6_ccmreg.h Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/imx6_ccmreg.h Sat Mar 24 23:23:31 2018 (r331506) @@ -80,9 +80,15 @@ #define CCGR0_AIPS_TZ2 (0x3 << 2) #define CCGR0_ABPHDMA (0x3 << 4) #define CCM_CCGR1 0x06C +#define CCGR1_ECSPI1 (0x3 << 0) +#define CCGR1_ECSPI2 (0x3 << 2) +#define CCGR1_ECSPI3 (0x3 << 4) +#define CCGR1_ECSPI4 (0x3 << 6) +#define CCGR1_ECSPI5 (0x3 << 8) #define CCGR1_ENET (0x3 << 10) #define CCGR1_EPIT1 (0x3 << 12) #define CCGR1_EPIT2 (0x3 << 14) +#define CCGR1_ESAI (0x3 << 16) #define CCGR1_GPT (0x3 << 20) #define CCGR1_GPT_SERIAL (0x3 << 22) #define CCM_CCGR2 0x070 Modified: stable/11/sys/arm/freescale/imx/imx_ccmvar.h ============================================================================== --- stable/11/sys/arm/freescale/imx/imx_ccmvar.h Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/imx/imx_ccmvar.h Sat Mar 24 23:23:31 2018 (r331506) @@ -43,6 +43,7 @@ * board setup code has to handle those things. */ +uint32_t imx_ccm_ecspi_hz(void); uint32_t imx_ccm_ipg_hz(void); uint32_t imx_ccm_perclk_hz(void); uint32_t imx_ccm_sdhci_hz(void); Copied: stable/11/sys/arm/freescale/imx/imx_spi.c (from r329999, head/sys/arm/freescale/imx/imx_spi.c) ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ stable/11/sys/arm/freescale/imx/imx_spi.c Sat Mar 24 23:23:31 2018 (r331506, copy of r329999, head/sys/arm/freescale/imx/imx_spi.c) @@ -0,0 +1,604 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2018 Ian Lepore + * 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. + */ + +#include +__FBSDID("$FreeBSD$"); + +/* + * Driver for imx Enhanced Configurable SPI; master-mode only. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "spibus_if.h" + +#define ECSPI_RXDATA 0x00 +#define ECSPI_TXDATA 0x04 +#define ECSPI_CTLREG 0x08 +#define CTLREG_BLEN_SHIFT 20 +#define CTLREG_BLEN_MASK 0x0fff +#define CTLREG_CSEL_SHIFT 18 +#define CTLREG_CSEL_MASK 0x03 +#define CTLREG_DRCTL_SHIFT 16 +#define CTLREG_DRCTL_MASK 0x03 +#define CTLREG_PREDIV_SHIFT 12 +#define CTLREG_PREDIV_MASK 0x0f +#define CTLREG_POSTDIV_SHIFT 8 +#define CTLREG_POSTDIV_MASK 0x0f +#define CTLREG_CMODE_SHIFT 4 +#define CTLREG_CMODE_MASK 0x0f +#define CTLREG_CMODES_MASTER (CTLREG_CMODE_MASK << CTLREG_CMODE_SHIFT) +#define CTLREG_SMC (1u << 3) +#define CTLREG_XCH (1u << 2) +#define CTLREG_HT (1u << 1) +#define CTLREG_EN (1u << 0) +#define ECSPI_CFGREG 0x0c +#define CFGREG_HTLEN_SHIFT 24 +#define CFGREG_SCLKCTL_SHIFT 20 +#define CFGREG_DATACTL_SHIFT 16 +#define CFGREG_SSPOL_SHIFT 12 +#define CFGREG_SSCTL_SHIFT 8 +#define CFGREG_SCLKPOL_SHIFT 4 +#define CFGREG_SCLKPHA_SHIFT 0 +#define CFGREG_MASK 0x0f /* all CFGREG fields are 4 bits */ +#define ECSPI_INTREG 0x10 +#define INTREG_TCEN (1u << 7) +#define INTREG_ROEN (1u << 6) +#define INTREG_RFEN (1u << 5) +#define INTREG_RDREN (1u << 4) +#define INTREG_RREN (1u << 3) +#define INTREG_TFEN (1u << 2) +#define INTREG_TDREN (1u << 1) +#define INTREG_TEEN (1u << 0) +#define ECSPI_DMAREG 0x14 +#define DMA_RX_THRESH_SHIFT 16 +#define DMA_RX_THRESH_MASK 0x3f +#define DMA_TX_THRESH_SHIFT 0 +#define DMA_TX_THRESH_MASK 0x3f +#define ECSPI_STATREG 0x18 +#define SREG_TC (1u << 7) +#define SREG_RO (1u << 6) +#define SREG_RF (1u << 5) +#define SREG_RDR (1u << 4) +#define SREG_RR (1u << 3) +#define SREG_TF (1u << 2) +#define SREG_TDR (1u << 1) +#define SREG_TE (1u << 0) +#define ECSPI_PERIODREG 0x1c +#define ECSPI_TESTREG 0x20 + +#define CS_MAX 4 /* Max number of chip selects. */ +#define CS_MASK 0x03 /* Mask flag bits out of chipsel. */ + +#define FIFO_SIZE 64 +#define FIFO_RXTHRESH 32 +#define FIFO_TXTHRESH 32 + +struct spi_softc { + device_t dev; + device_t spibus; + struct mtx mtx; + struct resource *memres; + struct resource *intres; + void *inthandle; + gpio_pin_t cspins[CS_MAX]; + u_int debug; + u_int basefreq; + uint32_t ctlreg; + uint32_t intreg; + uint32_t fifocnt; + uint8_t *rxbuf; + uint32_t rxidx; + uint32_t rxlen; + uint8_t *txbuf; + uint32_t txidx; + uint32_t txlen; +}; + +static struct ofw_compat_data compat_data[] = { + {"fsl,imx51-ecspi", true}, + {"fsl,imx53-ecspi", true}, + {"fsl,imx6dl-ecspi", true}, + {"fsl,imx6q-ecspi", true}, + {"fsl,imx6sx-ecspi", true}, + {"fsl,imx6ul-ecspi", true}, + {NULL, false} +}; + +static inline uint32_t +RD4(struct spi_softc *sc, bus_size_t offset) +{ + + return (bus_read_4(sc->memres, offset)); +} + +static inline void +WR4(struct spi_softc *sc, bus_size_t offset, uint32_t value) +{ + + bus_write_4(sc->memres, offset, value); +} + +static u_int +spi_calc_clockdiv(struct spi_softc *sc, u_int busfreq) +{ + u_int post, pre; + + /* Returning 0 effectively sets both dividers to 1. */ + if (sc->basefreq <= busfreq) + return (0); + + /* + * Brute-force this; all real-world bus speeds are going to be found on + * the 1st or 2nd time through this loop. + */ + for (post = 0; post < 16; ++post) { + pre = ((sc->basefreq >> post) / busfreq) - 1; + if (pre < 16) + break; + } + if (post == 16) { + /* The lowest we can go is ~115 Hz. */ + pre = 15; + post = 15; + } + + if (sc->debug >= 2) { + device_printf(sc->dev, + "base %u bus %u; pre %u, post %u; actual busfreq %u\n", + sc->basefreq, busfreq, pre, post, + (sc->basefreq / (pre + 1)) / (1 << post)); + } + + return (pre << CTLREG_PREDIV_SHIFT) | (post << CTLREG_POSTDIV_SHIFT); +} + +static void +spi_set_chipsel(struct spi_softc *sc, u_int cs, bool active) +{ + bool pinactive; + + /* + * This is kinda crazy... the gpio pins for chipsel are defined as + * active-high in the dts, but are supposed to be treated as active-low + * by this driver. So to turn on chipsel we have to invert the value + * passed to gpio_pin_set_active(). Then, to make it more fun, any + * slave can say its chipsel is active-high, so if that option is + * on, we have to invert the value again. + */ + pinactive = !active ^ (bool)(cs & SPIBUS_CS_HIGH); + + if (sc->debug >= 2) { + device_printf(sc->dev, "chipsel %u changed to %u\n", + (cs & ~SPIBUS_CS_HIGH), pinactive); + } + + /* + * Change the pin, then do a dummy read of its current state to ensure + * that the state change reaches the hardware before proceeding. + */ + gpio_pin_set_active(sc->cspins[cs & ~SPIBUS_CS_HIGH], pinactive); + gpio_pin_is_active(sc->cspins[cs & ~SPIBUS_CS_HIGH], &pinactive); +} + +static void +spi_hw_setup(struct spi_softc *sc, u_int cs, u_int mode, u_int freq) +{ + uint32_t reg; + + /* + * Set up control register, and write it first to bring the device out + * of reset. + */ + sc->ctlreg = CTLREG_EN | CTLREG_CMODES_MASTER | CTLREG_SMC; + sc->ctlreg |= spi_calc_clockdiv(sc, freq); + sc->ctlreg |= 7 << CTLREG_BLEN_SHIFT; /* XXX byte at a time */ + WR4(sc, ECSPI_CTLREG, sc->ctlreg); + + /* + * Set up the config register. Note that we do all transfers with the + * SPI hardware's chip-select set to zero. The actual chip select is + * handled with a gpio pin. + */ + reg = 0; + if (cs & SPIBUS_CS_HIGH) + reg |= 1u << CFGREG_SSPOL_SHIFT; + if (mode & SPIBUS_MODE_CPHA) + reg |= 1u << CFGREG_SCLKPHA_SHIFT; + if (mode & SPIBUS_MODE_CPOL) { + reg |= 1u << CFGREG_SCLKPOL_SHIFT; + reg |= 1u << CFGREG_SCLKCTL_SHIFT; + } + WR4(sc, ECSPI_CFGREG, reg); + + /* + * Set up the rx/tx FIFO interrupt thresholds. + */ + reg = (FIFO_RXTHRESH << DMA_RX_THRESH_SHIFT); + reg |= (FIFO_TXTHRESH << DMA_TX_THRESH_SHIFT); + WR4(sc, ECSPI_DMAREG, reg); + + /* + * Do a dummy read, to make sure the preceding writes reach the spi + * hardware before we assert any gpio chip select. + */ + (void)RD4(sc, ECSPI_CFGREG); +} + +static void +spi_empty_rxfifo(struct spi_softc *sc) +{ + + while (sc->rxidx < sc->rxlen && (RD4(sc, ECSPI_STATREG) & SREG_RR)) { + sc->rxbuf[sc->rxidx++] = (uint8_t)RD4(sc, ECSPI_RXDATA); + --sc->fifocnt; + } +} + +static void +spi_fill_txfifo(struct spi_softc *sc) +{ + + while (sc->txidx < sc->txlen && sc->fifocnt < FIFO_SIZE) { + WR4(sc, ECSPI_TXDATA, sc->txbuf[sc->txidx++]); + ++sc->fifocnt; + } + + /* + * If we're out of data, disable tx data ready (threshold) interrupts, + * and enable tx fifo empty interrupts. + */ + if (sc->txidx == sc->txlen) + sc->intreg = (sc->intreg & ~INTREG_TDREN) | INTREG_TEEN; +} + +static void +spi_intr(void *arg) +{ + struct spi_softc *sc = arg; + uint32_t intreg, status; + + mtx_lock(&sc->mtx); + + sc = arg; + intreg = sc->intreg; + status = RD4(sc, ECSPI_STATREG); + WR4(sc, ECSPI_STATREG, status); /* Clear w1c bits. */ + + /* + * If we get an overflow error, just signal that the transfer is done + * and wakeup the waiting thread, which will see that txidx != txlen and + * return an IO error to the caller. + */ + if (__predict_false(status & SREG_RO)) { + if (sc->debug || bootverbose) { + device_printf(sc->dev, "rxoverflow rxidx %u txidx %u\n", + sc->rxidx, sc->txidx); + } + sc->intreg = 0; + wakeup(sc); + mtx_unlock(&sc->mtx); + return; + } + + if (status & SREG_RR) + spi_empty_rxfifo(sc); + + if (status & SREG_TDR) + spi_fill_txfifo(sc); + + /* + * If we're out of bytes to send... + * - If Transfer Complete is set (shift register is empty) and we've + * received everything we expect, we're all done. + * - Else if Tx Fifo Empty is set, we need to stop waiting for that and + * switch to waiting for Transfer Complete (wait for shift register + * to empty out), and also for Receive Ready (last of incoming data). + */ + if (sc->txidx == sc->txlen) { + if ((status & SREG_TC) && sc->fifocnt == 0) { + sc->intreg = 0; + wakeup(sc); + } else if (status & SREG_TE) { + sc->intreg &= ~(sc->intreg & ~INTREG_TEEN); + sc->intreg |= INTREG_TCEN | INTREG_RREN; + } + } + + /* + * If interrupt flags changed, write the new flags to the hardware and + * do a dummy readback to ensure the changes reach the hardware before + * we exit the isr. + */ + if (sc->intreg != intreg) { + WR4(sc, ECSPI_INTREG, sc->intreg); + (void)RD4(sc, ECSPI_INTREG); + } + + if (sc->debug >= 3) { + device_printf(sc->dev, + "spi_intr, sreg 0x%08x intreg was 0x%08x now 0x%08x\n", + status, intreg, sc->intreg); + } + + mtx_unlock(&sc->mtx); +} + +static int +spi_xfer_buf(struct spi_softc *sc, void *rxbuf, void *txbuf, uint32_t len) +{ + int err; + + if (sc->debug >= 1) { + device_printf(sc->dev, + "spi_xfer_buf, rxbuf %p txbuf %p len %u\n", + rxbuf, txbuf, len); + } + + if (len == 0) + return (0); + + sc->rxbuf = rxbuf; + sc->rxlen = len; + sc->rxidx = 0; + sc->txbuf = txbuf; + sc->txlen = len; + sc->txidx = 0; + sc->intreg = INTREG_RDREN | INTREG_TDREN; + spi_fill_txfifo(sc); + + /* Enable interrupts last; spi_fill_txfifo() can change sc->intreg */ + WR4(sc, ECSPI_INTREG, sc->intreg); + + err = 0; + while (err == 0 && sc->intreg != 0) + err = msleep(sc, &sc->mtx, 0, "imxspi", 10 * hz); + + if (sc->rxidx != sc->rxlen || sc->txidx != sc->txlen) + err = EIO; + + return (err); +} + +static int +spi_transfer(device_t dev, device_t child, struct spi_command *cmd) +{ + struct spi_softc *sc = device_get_softc(dev); + uint32_t cs, mode, clock; + int err; + + spibus_get_cs(child, &cs); + spibus_get_clock(child, &clock); + spibus_get_mode(child, &mode); + + if (cs > CS_MAX || sc->cspins[cs] == NULL) { + if (sc->debug || bootverbose) + device_printf(sc->dev, "Invalid chip select %u\n", cs); + return (EINVAL); + } + + mtx_lock(&sc->mtx); + + if (sc->debug >= 1) { + device_printf(sc->dev, + "spi_transfer, cs 0x%x clock %u mode %u\n", + cs, clock, mode); + } + + /* Set up the hardware and select the device. */ + spi_hw_setup(sc, cs, mode, clock); + spi_set_chipsel(sc, cs, true); + + /* Transfer command then data bytes. */ + err = 0; + if (cmd->tx_cmd_sz > 0) + err = spi_xfer_buf(sc, cmd->rx_cmd, cmd->tx_cmd, + cmd->tx_cmd_sz); + if (cmd->tx_data_sz > 0 && err == 0) + err = spi_xfer_buf(sc, cmd->rx_data, cmd->tx_data, + cmd->tx_data_sz); + + /* Deselect the device, turn off (and reset) hardware. */ + spi_set_chipsel(sc, cs, false); + WR4(sc, ECSPI_CTLREG, 0); + + mtx_unlock(&sc->mtx); + + return (err); +} + +static phandle_t +spi_get_node(device_t bus, device_t dev) +{ + + /* + * Share our controller node with our spibus child; it instantiates + * devices by walking the children contained within our node. + */ + return ofw_bus_get_node(bus); +} + +static int +spi_detach(device_t dev) +{ + struct spi_softc *sc = device_get_softc(dev); + int idx; + + mtx_lock(&sc->mtx); + + bus_generic_detach(sc->dev); + if (sc->spibus != NULL) + device_delete_child(dev, sc->spibus); + + for (idx = 0; idx < nitems(sc->cspins); ++idx) { + if (sc->cspins[idx] != NULL) + gpio_pin_release(sc->cspins[idx]); + } + + if (sc->inthandle != NULL) + bus_teardown_intr(sc->dev, sc->intres, sc->inthandle); + if (sc->intres != NULL) + bus_release_resource(sc->dev, SYS_RES_IRQ, 0, sc->intres); + if (sc->memres != NULL) + bus_release_resource(sc->dev, SYS_RES_MEMORY, 0, sc->memres); + + mtx_unlock(&sc->mtx); + mtx_destroy(&sc->mtx); + + return (0); +} + +static int +spi_attach(device_t dev) +{ + struct spi_softc *sc = device_get_softc(dev); + phandle_t node; + int err, idx, rid; + + sc->dev = dev; + sc->basefreq = imx_ccm_ecspi_hz(); + + mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); + + /* Set up debug-enable sysctl. */ + SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), + OID_AUTO, "debug", CTLFLAG_RWTUN, &sc->debug, 0, + "Enable debug, higher values = more info"); + + /* Allocate mmio register access resources. */ + rid = 0; + sc->memres = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->memres == NULL) { + device_printf(sc->dev, "could not allocate registers\n"); + spi_detach(sc->dev); + return (ENXIO); + } + + /* Allocate interrupt resources and set up handler. */ + rid = 0; + sc->intres = bus_alloc_resource_any(sc->dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->intres == NULL) { + device_printf(sc->dev, "could not allocate interrupt\n"); + device_detach(sc->dev); + return (ENXIO); + } + err = bus_setup_intr(sc->dev, sc->intres, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, spi_intr, sc, &sc->inthandle); + if (err != 0) { + device_printf(sc->dev, "could not setup interrupt handler"); + device_detach(sc->dev); + return (ENXIO); + } + + /* Allocate gpio pins for configured chip selects. */ + node = ofw_bus_get_node(sc->dev); + for (err = 0, idx = 0; err == 0 && idx < nitems(sc->cspins); ++idx) { + err = gpio_pin_get_by_ofw_propidx(sc->dev, node, "cs-gpios", + idx, &sc->cspins[idx]); + if (err == 0) { + gpio_pin_setflags(sc->cspins[idx], GPIO_PIN_OUTPUT); + } else if (sc->debug >= 2) { + device_printf(sc->dev, + "cannot configure gpio for chip select %u\n", idx); + } + } + + /* + * Hardware init: put all channels into Master mode, turn off the enable + * bit (gates off clocks); we only enable the hardware while xfers run. + */ + WR4(sc, ECSPI_CTLREG, CTLREG_CMODES_MASTER); + + /* Attach the bus driver. */ + sc->spibus = device_add_child(dev, "spibus", -1); + return (bus_generic_attach(sc->dev)); +} + +static int +spi_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) + return (ENXIO); + + device_set_desc(dev, "i.MX ECSPI Master"); + return (BUS_PROBE_DEFAULT); +} + +static device_method_t spi_methods[] = { + DEVMETHOD(device_probe, spi_probe), + DEVMETHOD(device_attach, spi_attach), + DEVMETHOD(device_detach, spi_detach), + + /* spibus_if */ + DEVMETHOD(spibus_transfer, spi_transfer), + + /* ofw_bus_if */ + DEVMETHOD(ofw_bus_get_node, spi_get_node), + + DEVMETHOD_END +}; + +static driver_t spi_driver = { + "imx_spi", + spi_methods, + sizeof(struct spi_softc), +}; + +static devclass_t spi_devclass; + +DRIVER_MODULE(imx_spi, simplebus, spi_driver, spi_devclass, 0, 0); +DRIVER_MODULE(ofw_spibus, imx_spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0); +MODULE_DEPEND(imx_spi, ofw_spibus, 1, 1, 1); Modified: stable/11/sys/arm/freescale/vybrid/vf_spi.c ============================================================================== --- stable/11/sys/arm/freescale/vybrid/vf_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/freescale/vybrid/vf_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -263,6 +263,8 @@ spi_transfer(device_t dev, device_t child, struct spi_ /* get the proper chip select */ spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + /* Command */ spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); Modified: stable/11/sys/arm/lpc/lpc_spi.c ============================================================================== --- stable/11/sys/arm/lpc/lpc_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/lpc/lpc_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -149,6 +149,8 @@ lpc_spi_transfer(device_t dev, device_t child, struct spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + /* Set CS active */ lpc_gpio_set_state(child, cs, 0); Modified: stable/11/sys/arm/samsung/exynos/exynos5_spi.c ============================================================================== --- stable/11/sys/arm/samsung/exynos/exynos5_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/samsung/exynos/exynos5_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -204,6 +204,8 @@ spi_transfer(device_t dev, device_t child, struct spi_ /* get the proper chip select */ spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + /* Command */ spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); Modified: stable/11/sys/arm/ti/ti_spi.c ============================================================================== --- stable/11/sys/arm/ti/ti_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/arm/ti/ti_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -458,6 +458,9 @@ ti_spi_transfer(device_t dev, device_t child, struct s /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); + + cs &= ~SPIBUS_CS_HIGH; + if (cs > sc->sc_numcs) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, device_get_nameunit(child)); Modified: stable/11/sys/dev/flash/at45d.c ============================================================================== --- stable/11/sys/dev/flash/at45d.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/dev/flash/at45d.c Sat Mar 24 23:23:31 2018 (r331506) @@ -450,3 +450,4 @@ static driver_t at45d_driver = { }; DRIVER_MODULE(at45d, spibus, at45d_driver, at45d_devclass, NULL, NULL); +MODULE_DEPEND(at45d, spibus, 1, 1, 1); Modified: stable/11/sys/dev/flash/mx25l.c ============================================================================== --- stable/11/sys/dev/flash/mx25l.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/dev/flash/mx25l.c Sat Mar 24 23:23:31 2018 (r331506) @@ -89,8 +89,13 @@ struct mx25l_softc struct proc *sc_p; struct bio_queue_head sc_bio_queue; unsigned int sc_flags; + unsigned int sc_taskstate; }; +#define TSTATE_STOPPED 0 +#define TSTATE_STOPPING 1 +#define TSTATE_RUNNING 2 + #define M25PXX_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define M25PXX_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define M25PXX_LOCK_INIT(_sc) \ @@ -527,6 +532,8 @@ mx25l_attach(device_t dev) bioq_init(&sc->sc_bio_queue); kproc_create(&mx25l_task, sc, &sc->sc_p, 0, 0, "task: mx25l flash"); + sc->sc_taskstate = TSTATE_RUNNING; + device_printf(sc->sc_dev, "%s, sector %d bytes, %d sectors\n", ident->name, ident->sectorsize, ident->sectorcount); @@ -536,8 +543,33 @@ mx25l_attach(device_t dev) static int mx25l_detach(device_t dev) { + struct mx25l_softc *sc; + int err; - return (EIO); + sc = device_get_softc(dev); + err = 0; + + M25PXX_LOCK(sc); + if (sc->sc_taskstate == TSTATE_RUNNING) { + sc->sc_taskstate = TSTATE_STOPPING; + wakeup(sc); + while (err == 0 && sc->sc_taskstate != TSTATE_STOPPED) { + err = msleep(sc, &sc->sc_mtx, 0, "mx25dt", hz * 3); + if (err != 0) { + sc->sc_taskstate = TSTATE_RUNNING; + device_printf(dev, + "Failed to stop queue task\n"); + } + } + } + M25PXX_UNLOCK(sc); + + if (err == 0 && sc->sc_taskstate == TSTATE_STOPPED) { + disk_destroy(sc->sc_disk); + bioq_flush(&sc->sc_bio_queue, NULL, ENXIO); + M25PXX_LOCK_DESTROY(sc); + } + return (err); } static int @@ -605,9 +637,15 @@ mx25l_task(void *arg) dev = sc->sc_dev; M25PXX_LOCK(sc); do { + if (sc->sc_taskstate == TSTATE_STOPPING) { + sc->sc_taskstate = TSTATE_STOPPED; + M25PXX_UNLOCK(sc); + wakeup(sc); + kproc_exit(0); + } bp = bioq_first(&sc->sc_bio_queue); if (bp == NULL) - msleep(sc, &sc->sc_mtx, PRIBIO, "jobqueue", 0); + msleep(sc, &sc->sc_mtx, PRIBIO, "mx25jq", 0); } while (bp == NULL); bioq_remove(&sc->sc_bio_queue, bp); M25PXX_UNLOCK(sc); @@ -648,3 +686,4 @@ static driver_t mx25l_driver = { }; DRIVER_MODULE(mx25l, spibus, mx25l_driver, mx25l_devclass, 0, 0); +MODULE_DEPEND(mx25l, spibus, 1, 1, 1); Modified: stable/11/sys/dev/spibus/ofw_spibus.c ============================================================================== --- stable/11/sys/dev/spibus/ofw_spibus.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/dev/spibus/ofw_spibus.c Sat Mar 24 23:23:31 2018 (r331506) @@ -1,5 +1,5 @@ /*- - * SPDX-License-Identifier: BSD-4-Clause-FreeBSD + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2009, Nathan Whitehorn * Copyright (c) 2013 The FreeBSD Foundation @@ -82,6 +82,7 @@ ofw_spibus_attach(device_t dev) phandle_t child; pcell_t clock, paddr; device_t childdev; + uint32_t mode = SPIBUS_MODE_NONE; sc->dev = dev; @@ -105,6 +106,24 @@ ofw_spibus_attach(device_t dev) } /* + * Try to get the cpol/cpha mode + */ + if (OF_hasprop(child, "spi-cpol")) + mode = SPIBUS_MODE_CPOL; + if (OF_hasprop(child, "spi-cpha")) { + if (mode == SPIBUS_MODE_CPOL) + mode = SPIBUS_MODE_CPOL_CPHA; + else + mode = SPIBUS_MODE_CPHA; + } + + /* + * Try to get the CS polarity + */ + if (OF_hasprop(child, "spi-cs-high")) + paddr |= SPIBUS_CS_HIGH; + + /* * Get the maximum clock frequency for device, zero means * use the default bus speed. */ @@ -122,6 +141,7 @@ ofw_spibus_attach(device_t dev) continue; dinfo->opd_dinfo.cs = paddr; dinfo->opd_dinfo.clock = clock; + dinfo->opd_dinfo.mode = mode; if (ofw_bus_gen_setup_devinfo(&dinfo->opd_obdinfo, child) != 0) { free(dinfo, M_DEVBUF); @@ -194,10 +214,10 @@ static device_method_t ofw_spibus_methods[] = { DEVMETHOD_END }; -static devclass_t ofwspibus_devclass; +devclass_t ofw_spibus_devclass; DEFINE_CLASS_1(spibus, ofw_spibus_driver, ofw_spibus_methods, sizeof(struct spibus_softc), spibus_driver); -DRIVER_MODULE(ofw_spibus, spi, ofw_spibus_driver, ofwspibus_devclass, 0, 0); +DRIVER_MODULE(ofw_spibus, spi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0); MODULE_VERSION(ofw_spibus, 1); MODULE_DEPEND(ofw_spibus, spibus, 1, 1, 1); Modified: stable/11/sys/dev/spibus/spibusvar.h ============================================================================== --- stable/11/sys/dev/spibus/spibusvar.h Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/dev/spibus/spibusvar.h Sat Mar 24 23:23:31 2018 (r331506) @@ -46,6 +46,8 @@ struct spibus_ivar uint32_t clock; }; +#define SPIBUS_CS_HIGH (1U << 31) + enum { SPIBUS_IVAR_CS, /* chip select that we're on */ SPIBUS_IVAR_MODE, /* SPI mode (0-3) */ @@ -66,3 +68,5 @@ SPIBUS_ACCESSOR(clock, CLOCK, uint32_t) extern driver_t spibus_driver; extern devclass_t spibus_devclass; +extern driver_t ofw_spibus_driver; +extern devclass_t ofw_spibus_devclass; Modified: stable/11/sys/dev/xilinx/axi_quad_spi.c ============================================================================== --- stable/11/sys/dev/xilinx/axi_quad_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/dev/xilinx/axi_quad_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -193,6 +193,8 @@ spi_transfer(device_t dev, device_t child, struct spi_ /* get the proper chip select */ spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + /* Assert CS */ reg = READ4(sc, SPI_SSR); reg &= ~(1 << cs); Modified: stable/11/sys/mips/atheros/ar71xx_spi.c ============================================================================== --- stable/11/sys/mips/atheros/ar71xx_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/mips/atheros/ar71xx_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -214,6 +214,8 @@ ar71xx_spi_transfer(device_t dev, device_t child, stru spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + ar71xx_spi_chip_activate(sc, cs); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, Modified: stable/11/sys/mips/mediatek/mtk_spi_v1.c ============================================================================== --- stable/11/sys/mips/mediatek/mtk_spi_v1.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/mips/mediatek/mtk_spi_v1.c Sat Mar 24 23:23:31 2018 (r331506) @@ -231,6 +231,8 @@ mtk_spi_transfer(device_t dev, device_t child, struct spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + if (cs != 0) /* Only 1 CS */ return (ENXIO); Modified: stable/11/sys/mips/mediatek/mtk_spi_v2.c ============================================================================== --- stable/11/sys/mips/mediatek/mtk_spi_v2.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/mips/mediatek/mtk_spi_v2.c Sat Mar 24 23:23:31 2018 (r331506) @@ -236,6 +236,8 @@ mtk_spi_transfer(device_t dev, device_t child, struct spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + if (cs != 0) /* Only 1 CS */ return (ENXIO); Modified: stable/11/sys/mips/rt305x/rt305x_spi.c ============================================================================== --- stable/11/sys/mips/rt305x/rt305x_spi.c Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/mips/rt305x/rt305x_spi.c Sat Mar 24 23:23:31 2018 (r331506) @@ -226,6 +226,8 @@ rt305x_spi_transfer(device_t dev, device_t child, stru spibus_get_cs(child, &cs); + cs &= ~SPIBUS_CS_HIGH; + if (cs != 0) /* Only 1 CS */ return (ENXIO); Modified: stable/11/sys/modules/Makefile ============================================================================== --- stable/11/sys/modules/Makefile Sat Mar 24 23:07:10 2018 (r331505) +++ stable/11/sys/modules/Makefile Sat Mar 24 23:23:31 2018 (r331506) @@ -360,7 +360,7 @@ SUBDIR= \ snp \ sound \ *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***