From owner-svn-src-head@freebsd.org Sat Jun 23 23:44:37 2018 Return-Path: Delivered-To: svn-src-head@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 A3D11100AB8A; Sat, 23 Jun 2018 23:44:37 +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 4DE3796D27; Sat, 23 Jun 2018 23:44:37 +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 2EFA216B25; Sat, 23 Jun 2018 23:44:37 +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 w5NNib4E019943; Sat, 23 Jun 2018 23:44:37 GMT (envelope-from ian@FreeBSD.org) Received: (from ian@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w5NNibaq019942; Sat, 23 Jun 2018 23:44:37 GMT (envelope-from ian@FreeBSD.org) Message-Id: <201806232344.w5NNibaq019942@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: ian set sender to ian@FreeBSD.org using -f From: Ian Lepore Date: Sat, 23 Jun 2018 23:44:37 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r335594 - head/sys/arm/broadcom/bcm2835 X-SVN-Group: head X-SVN-Commit-Author: ian X-SVN-Commit-Paths: head/sys/arm/broadcom/bcm2835 X-SVN-Commit-Revision: 335594 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-head@freebsd.org X-Mailman-Version: 2.1.26 Precedence: list List-Id: SVN commit messages for the src tree for head/-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 23 Jun 2018 23:44:37 -0000 Author: ian Date: Sat Jun 23 23:44:36 2018 New Revision: 335594 URL: https://svnweb.freebsd.org/changeset/base/335594 Log: Retrieve the bus clock speed and mode (polarity/phase) from the child device and set up the hardware accordingly on each transfer. This replaces the old configuration done via sysctl, and allows both fdt configuration data and userland control via the spigen device to work. Submitted by: Bob Frazier Differential Revision: https://reviews.freebsd.org/D15031 Modified: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c Modified: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c ============================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spi.c Sat Jun 23 23:08:25 2018 (r335593) +++ head/sys/arm/broadcom/bcm2835/bcm2835_spi.c Sat Jun 23 23:44:36 2018 (r335594) @@ -132,17 +132,6 @@ bcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) if (error != 0 || req->newptr == NULL) return (error); - clk = SPI_CORE_CLK / clk; - if (clk <= 1) - clk = 2; - else if (clk % 2) - clk--; - if (clk > 0xffff) - clk = 0; - BCM_SPI_LOCK(sc); - BCM_SPI_WRITE(sc, SPI_CLK, clk); - BCM_SPI_UNLOCK(sc); - return (0); } @@ -163,12 +152,6 @@ bcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) if (error != 0 || req->newptr == NULL) return (error); - if (reg) - reg = bit; - BCM_SPI_LOCK(sc); - bcm_spi_modifyreg(sc, SPI_CS, bit, reg); - BCM_SPI_UNLOCK(sc); - return (0); } @@ -200,6 +183,13 @@ bcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); } +static int +bcm_spi_cspol2_proc(SYSCTL_HANDLER_ARGS) +{ + + return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL2)); +} + static void bcm_spi_sysctl_init(struct bcm_spi_softc *sc) { @@ -214,20 +204,23 @@ bcm_spi_sysctl_init(struct bcm_spi_softc *sc) tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", - CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", - CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", - CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", - CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", - CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); + SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol2", + CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), + bcm_spi_cspol2_proc, "IU", "SPI BUS chip select 2 polarity"); } static int @@ -303,9 +296,6 @@ bcm_spi_attach(device_t dev) */ BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); - /* Set the SPI clock to 500Khz. */ - BCM_SPI_WRITE(sc, SPI_CLK, SPI_CORE_CLK / 500000); - #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif @@ -412,7 +402,7 @@ static int bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct bcm_spi_softc *sc; - uint32_t cs; + uint32_t cs, mode, clock; int err; sc = device_get_softc(dev); @@ -422,21 +412,34 @@ bcm_spi_transfer(device_t dev, device_t child, struct KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); - /* Get the proper chip select for this child. */ + /* Get the bus speed, mode, and chip select for this child. */ + spibus_get_cs(child, &cs); + if ((cs & (~SPIBUS_CS_HIGH)) > 2) { + device_printf(dev, + "Invalid chip select %u requested by %s\n", cs, + device_get_nameunit(child)); + return (EINVAL); + } - cs &= ~SPIBUS_CS_HIGH; + spibus_get_clock(child, &clock); + if (clock == 0) { + device_printf(dev, + "Invalid clock %uHz requested by %s\n", clock, + device_get_nameunit(child)); + return (EINVAL); + } - if (cs > 2) { + spibus_get_mode(child, &mode); + if (mode > 3) { device_printf(dev, - "Invalid chip select %d requested by %s\n", cs, + "Invalid mode %u requested by %s\n", mode, device_get_nameunit(child)); return (EINVAL); } - BCM_SPI_LOCK(sc); - /* If the controller is in use wait until it is available. */ + BCM_SPI_LOCK(sc); while (sc->sc_flags & BCM_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); @@ -454,13 +457,63 @@ bcm_spi_transfer(device_t dev, device_t child, struct sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; +#ifdef BCM2835_SPI_USE_CS_HIGH /* TODO: for when behavior is correct */ /* + * Assign CS polarity first, while the CS indicates 'inactive'. + * This will need to set the correct polarity bit based on the 'cs', and + * the polarity bit will remain in this state, even after the transaction + * is complete. + */ + if((cs & ~SPIBUS_CS_HIGH) == 0) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL0, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL0 : 0)); + } + else if((cs & ~SPIBUS_CS_HIGH) == 1) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL1, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL1 : 0)); + } + else if((cs & ~SPIBUS_CS_HIGH) == 2) { + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CSPOL2, + ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL2 : 0)); + } +#endif + + /* + * Set the mode in 'SPI_CS' (clock phase and polarity bits). + * This must happen before CS output pin is active. + * Otherwise, you might glitch and drop the first bit. + */ + bcm_spi_modifyreg(sc, SPI_CS, + SPI_CS_CPOL | SPI_CS_CPHA, + ((mode & SPIBUS_MODE_CPHA) ? SPI_CS_CPHA : 0) | + ((mode & SPIBUS_MODE_CPOL) ? SPI_CS_CPOL : 0)); + + /* + * Set the clock divider in 'SPI_CLK - see 'bcm_spi_clock_proc()'. + */ + + /* calculate 'clock' as a divider value from freq */ + clock = SPI_CORE_CLK / clock; + if (clock <= 1) + clock = 2; + else if (clock % 2) + clock--; + if (clock > 0xffff) + clock = 0; + + BCM_SPI_WRITE(sc, SPI_CLK, clock); + + /* * Set the CS for this transaction, enable interrupts and announce * we're ready to tx. This will kick off the first interrupt. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, - cs | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); + (cs & (~SPIBUS_CS_HIGH)) | /* cs is the lower 2 bits of the reg */ + SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); @@ -478,7 +531,7 @@ bcm_spi_transfer(device_t dev, device_t child, struct * return errors. */ if (err == EWOULDBLOCK) { - device_printf(sc->sc_dev, "SPI error\n"); + device_printf(sc->sc_dev, "SPI error (timeout)\n"); err = EIO; }