svn commit: r297395 - in head/sys/arm/ti: . am335x

Luiz Otavio O Souza loos at FreeBSD.org
Tue Mar 29 19:11:05 UTC 2016


Author: loos
Date: Tue Mar 29 19:11:04 2016
New Revision: 297395
URL: https://svnweb.freebsd.org/changeset/base/297395

Log:
  Add the SPI driver for am335x.
  
  This driver works in PIO mode for now, interrupts are available only when
  FIFO is enabled.  The FIFO cannot be used with arbitrary sizes which defeat
  its general use.
  
  At some point we can add DMA transfers where the FIFO can be more useful.
  
  Tested on uBMC (microBMC) and BBB.
  
  Sponsored by:	Rubicon Communications (Netgate)

Added:
  head/sys/arm/ti/ti_spi.c   (contents, props changed)
  head/sys/arm/ti/ti_spireg.h   (contents, props changed)
  head/sys/arm/ti/ti_spivar.h   (contents, props changed)
Modified:
  head/sys/arm/ti/am335x/am335x_prcm.c
  head/sys/arm/ti/files.ti
  head/sys/arm/ti/ti_hwmods.c
  head/sys/arm/ti/ti_prcm.h

Modified: head/sys/arm/ti/am335x/am335x_prcm.c
==============================================================================
--- head/sys/arm/ti/am335x/am335x_prcm.c	Tue Mar 29 16:07:51 2016	(r297394)
+++ head/sys/arm/ti/am335x/am335x_prcm.c	Tue Mar 29 19:11:04 2016	(r297395)
@@ -64,6 +64,8 @@ __FBSDID("$FreeBSD$");
 #define CM_PER_MMC0_CLKCTRL		(CM_PER + 0x03C)
 #define CM_PER_I2C2_CLKCTRL		(CM_PER + 0x044)
 #define CM_PER_I2C1_CLKCTRL		(CM_PER + 0x048)
+#define CM_PER_SPI0_CLKCTRL		(CM_PER + 0x04C)
+#define CM_PER_SPI1_CLKCTRL		(CM_PER + 0x050)
 #define CM_PER_UART1_CLKCTRL		(CM_PER + 0x06C)
 #define CM_PER_UART2_CLKCTRL		(CM_PER + 0x070)
 #define CM_PER_UART3_CLKCTRL		(CM_PER + 0x074)
@@ -274,6 +276,10 @@ struct ti_clock_dev ti_am335x_clk_devmap
 	AM335X_GENERIC_CLOCK_DEV(I2C2_CLK),
 	AM335X_GENERIC_CLOCK_DEV(I2C3_CLK),
 
+	/* McSPI we use hwmods as reference, not units in spec */
+	AM335X_GENERIC_CLOCK_DEV(SPI0_CLK),
+	AM335X_GENERIC_CLOCK_DEV(SPI1_CLK),
+
 	/* TSC_ADC */
 	AM335X_GENERIC_CLOCK_DEV(TSC_ADC_CLK),
 
@@ -356,6 +362,10 @@ static struct am335x_clk_details g_am335
 	_CLK_DETAIL(I2C2_CLK, CM_PER_I2C1_CLKCTRL, 0),
 	_CLK_DETAIL(I2C3_CLK, CM_PER_I2C2_CLKCTRL, 0),
 
+	/* McSPI modules, hwmods start with spi0 */
+	_CLK_DETAIL(SPI0_CLK, CM_PER_SPI0_CLKCTRL, 0),
+	_CLK_DETAIL(SPI1_CLK, CM_PER_SPI1_CLKCTRL, 0),
+
 	/* TSC_ADC module */
 	_CLK_DETAIL(TSC_ADC_CLK, CM_WKUP_ADC_TSC_CLKCTRL, 0),
 

Modified: head/sys/arm/ti/files.ti
==============================================================================
--- head/sys/arm/ti/files.ti	Tue Mar 29 16:07:51 2016	(r297394)
+++ head/sys/arm/ti/files.ti	Tue Mar 29 19:11:04 2016	(r297395)
@@ -18,6 +18,7 @@ arm/ti/ti_gpio.c				optional	gpio
 arm/ti/ti_gpio_if.m				optional	gpio
 arm/ti/ti_i2c.c					optional	ti_i2c
 arm/ti/ti_sdhci.c	 			optional	sdhci
+arm/ti/ti_spi.c		 			optional	ti_spi
 
 dev/uart/uart_dev_ti8250.c			optional	uart
 dev/uart/uart_dev_ns8250.c			optional	uart

Modified: head/sys/arm/ti/ti_hwmods.c
==============================================================================
--- head/sys/arm/ti/ti_hwmods.c	Tue Mar 29 16:07:51 2016	(r297394)
+++ head/sys/arm/ti/ti_hwmods.c	Tue Mar 29 19:11:04 2016	(r297395)
@@ -76,6 +76,9 @@ struct hwmod ti_hwmods[] = {
 	{"epwmss1",	PWMSS1_CLK},
 	{"epwmss2",	PWMSS2_CLK},
 
+	{"spi0",	SPI0_CLK},
+	{"spi1",	SPI1_CLK},
+
 	{"timer1",	TIMER1_CLK},
 	{"timer2",	TIMER2_CLK},
 	{"timer3",	TIMER3_CLK},

Modified: head/sys/arm/ti/ti_prcm.h
==============================================================================
--- head/sys/arm/ti/ti_prcm.h	Tue Mar 29 16:07:51 2016	(r297394)
+++ head/sys/arm/ti/ti_prcm.h	Tue Mar 29 19:11:04 2016	(r297395)
@@ -158,6 +158,10 @@ typedef enum {
 
 	/* RTC module */
 	RTC_CLK = 1900,
+
+	/* McSPI */
+	SPI0_CLK = 2000,
+	SPI1_CLK,
 } clk_ident_t;
 
 /*

Added: head/sys/arm/ti/ti_spi.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/ti/ti_spi.c	Tue Mar 29 19:11:04 2016	(r297395)
@@ -0,0 +1,582 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/rman.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/sysctl.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <machine/intr.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <arm/ti/ti_prcm.h>
+#include <arm/ti/ti_hwmods.h>
+#include <arm/ti/ti_spireg.h>
+#include <arm/ti/ti_spivar.h>
+
+#include "spibus_if.h"
+
+static void ti_spi_intr(void *);
+static int ti_spi_detach(device_t);
+
+#undef TI_SPI_DEBUG
+#ifdef TI_SPI_DEBUG
+#define	IRQSTATUSBITS							\
+	"\020\1TX0_EMPTY\2TX0_UNDERFLOW\3RX0_FULL\4RX0_OVERFLOW"	\
+	"\5TX1_EMPTY\6TX1_UNDERFLOW\7RX1_FULL\11TX2_EMPTY"		\
+	"\12TX1_UNDERFLOW\13RX2_FULL\15TX3_EMPTY\16TX3_UNDERFLOW"	\
+	"\17RX3_FULL\22EOW"
+#define	CONFBITS							\
+	"\020\1PHA\2POL\7EPOL\17DMAW\20DMAR\21DPE0\22DPE1\23IS"		\
+	"\24TURBO\25FORCE\30SBE\31SBPOL\34FFEW\35FFER\36CLKG"
+#define	STATBITS							\
+	"\020\1RXS\2TXS\3EOT\4TXFFE\5TXFFF\6RXFFE\7RXFFFF"
+#define	MODULCTRLBITS							\
+	"\020\1SINGLE\2NOSPIEN\3SLAVE\4SYST\10MOA\11FDAA"
+#define	CTRLBITS							\
+	"\020\1ENABLED"
+
+static void
+ti_spi_printr(device_t dev)
+{
+	int clk, conf, ctrl, div, i, j, wl;
+	struct ti_spi_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+	reg = TI_SPI_READ(sc, MCSPI_SYSCONFIG);
+	device_printf(dev, "SYSCONFIG: %#x\n", reg);
+	reg = TI_SPI_READ(sc, MCSPI_SYSSTATUS);
+	device_printf(dev, "SYSSTATUS: %#x\n", reg);
+	reg = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
+	device_printf(dev, "IRQSTATUS: 0x%b\n", reg, IRQSTATUSBITS);
+	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+	device_printf(dev, "IRQENABLE: 0x%b\n", reg, IRQSTATUSBITS);
+	reg = TI_SPI_READ(sc, MCSPI_MODULCTRL);
+	device_printf(dev, "MODULCTRL: 0x%b\n", reg, MODULCTRLBITS);
+	for (i = 0; i < sc->sc_numcs; i++) {
+		ctrl = TI_SPI_READ(sc, MCSPI_CTRL_CH(i));
+		conf = TI_SPI_READ(sc, MCSPI_CONF_CH(i));
+		device_printf(dev, "CH%dCONF: 0x%b\n", i, conf, CONFBITS);
+		if (conf & MCSPI_CONF_CLKG) {
+			div = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
+			div |= ((ctrl >> MCSPI_CTRL_EXTCLK_SHIFT) & MCSPI_CTRL_EXTCLK_MSK) << 4;
+		} else {
+			div = 1;
+			j = (conf >> MCSPI_CONF_CLK_SHIFT) & MCSPI_CONF_CLK_MSK;
+			while (j-- > 0)
+				div <<= 1;
+		}
+		clk = TI_SPI_GCLK / div;
+		wl = ((conf >> MCSPI_CONF_WL_SHIFT) & MCSPI_CONF_WL_MSK) + 1;
+		device_printf(dev, "wordlen: %-2d clock: %d\n", wl, clk);
+		reg = TI_SPI_READ(sc, MCSPI_STAT_CH(i));
+		device_printf(dev, "CH%dSTAT: 0x%b\n", i, reg, STATBITS);
+		device_printf(dev, "CH%dCTRL: 0x%b\n", i, ctrl, CTRLBITS);
+	}
+	reg = TI_SPI_READ(sc, MCSPI_XFERLEVEL);
+	device_printf(dev, "XFERLEVEL: %#x\n", reg);
+}
+#endif
+
+static void
+ti_spi_set_clock(struct ti_spi_softc *sc, int ch, int freq)
+{
+	uint32_t clkdiv, conf, div, extclk, reg;
+
+	clkdiv = TI_SPI_GCLK / freq;
+	if (clkdiv > MCSPI_EXTCLK_MSK) {
+		extclk = 0;
+		clkdiv = 0;
+		div = 1;
+		while (TI_SPI_GCLK / div > freq && clkdiv <= 0xf) {
+			clkdiv++;
+			div <<= 1;
+		}
+		conf = clkdiv << MCSPI_CONF_CLK_SHIFT;
+	} else {
+		extclk = clkdiv >> 4;
+		clkdiv &= MCSPI_CONF_CLK_MSK;
+		conf = MCSPI_CONF_CLKG | clkdiv << MCSPI_CONF_CLK_SHIFT;
+	}
+
+	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(ch));
+	reg &= ~(MCSPI_CTRL_EXTCLK_MSK << MCSPI_CTRL_EXTCLK_SHIFT);
+	reg |= extclk << MCSPI_CTRL_EXTCLK_SHIFT;
+	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(ch), reg);
+
+	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(ch));
+	reg &= ~(MCSPI_CONF_CLKG | MCSPI_CONF_CLK_MSK << MCSPI_CONF_CLK_SHIFT);
+	TI_SPI_WRITE(sc, MCSPI_CONF_CH(ch), reg | conf);
+}
+
+static int
+ti_spi_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+	if (!ofw_bus_is_compatible(dev, "ti,omap4-mcspi"))
+		return (ENXIO);
+
+	device_set_desc(dev, "TI McSPI controller");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+ti_spi_attach(device_t dev)
+{
+	int clk_id, err, i, rid, timeout;
+	struct ti_spi_softc *sc;
+	uint32_t rev;
+
+	sc = device_get_softc(dev);
+	sc->sc_dev = dev;
+
+	/*
+	 * Get the MMCHS device id from FDT.  If it's not there use the newbus
+	 * unit number (which will work as long as the devices are in order and
+	 * none are skipped in the fdt).  Note that this is a property we made
+	 * up and added in freebsd, it doesn't exist in the published bindings.
+	 */
+	clk_id = ti_hwmods_get_clock(dev);
+	if (clk_id == INVALID_CLK_IDENT) {
+		device_printf(dev,
+		    "failed to get clock based on hwmods property\n");
+		return (EINVAL);
+	}
+
+	/* Activate the McSPI module. */
+	err = ti_prcm_clk_enable(clk_id);
+	if (err) {
+		device_printf(dev, "Error: failed to activate source clock\n");
+		return (err);
+	}
+
+	/* Get the number of available channels. */
+	if ((OF_getencprop(ofw_bus_get_node(dev), "ti,spi-num-cs",
+	    &sc->sc_numcs, sizeof(sc->sc_numcs))) <= 0) {
+		sc->sc_numcs = 2;
+	}
+
+	rid = 0;
+	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+	    RF_ACTIVE);
+	if (!sc->sc_mem_res) {
+		device_printf(dev, "cannot allocate memory window\n");
+		return (ENXIO);
+	}
+
+	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
+	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
+
+	rid = 0;
+	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
+	    RF_ACTIVE);
+	if (!sc->sc_irq_res) {
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+		device_printf(dev, "cannot allocate interrupt\n");
+		return (ENXIO);
+	}
+
+	/* Hook up our interrupt handler. */
+	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, ti_spi_intr, sc, &sc->sc_intrhand)) {
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+		device_printf(dev, "cannot setup the interrupt handler\n");
+		return (ENXIO);
+	}
+
+	mtx_init(&sc->sc_mtx, "ti_spi", NULL, MTX_DEF);
+
+	/* Issue a softreset to the controller */
+	TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
+	timeout = 1000;
+	while (!(TI_SPI_READ(sc, MCSPI_SYSSTATUS) &
+	    MCSPI_SYSSTATUS_RESETDONE)) {
+		if (--timeout == 0) {
+			device_printf(dev,
+			    "Error: Controller reset operation timed out\n");
+			ti_spi_detach(dev);
+			return (ENXIO);
+		}
+		DELAY(100);
+	}
+
+	/* Print the McSPI module revision. */
+	rev = TI_SPI_READ(sc, MCSPI_REVISION);
+	device_printf(dev,
+	    "scheme: %#x func: %#x rtl: %d rev: %d.%d custom rev: %d\n",
+	    (rev >> MCSPI_REVISION_SCHEME_SHIFT) & MCSPI_REVISION_SCHEME_MSK,
+	    (rev >> MCSPI_REVISION_FUNC_SHIFT) & MCSPI_REVISION_FUNC_MSK,
+	    (rev >> MCSPI_REVISION_RTL_SHIFT) & MCSPI_REVISION_RTL_MSK,
+	    (rev >> MCSPI_REVISION_MAJOR_SHIFT) & MCSPI_REVISION_MAJOR_MSK,
+	    (rev >> MCSPI_REVISION_MINOR_SHIFT) & MCSPI_REVISION_MINOR_MSK,
+	    (rev >> MCSPI_REVISION_CUSTOM_SHIFT) & MCSPI_REVISION_CUSTOM_MSK);
+
+	/* Set Master mode, single channel. */
+	TI_SPI_WRITE(sc, MCSPI_MODULCTRL, MCSPI_MODULCTRL_SINGLE);
+
+	/* Clear pending interrupts and disable interrupts. */
+	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0x0);
+	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
+
+	for (i = 0; i < sc->sc_numcs; i++) {
+		/*
+		 * Default to SPI mode 0, CS active low, 8 bits word length and
+		 * 500kHz clock.
+		 */
+		TI_SPI_WRITE(sc, MCSPI_CONF_CH(i),
+		    MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL |
+		    (8 - 1) << MCSPI_CONF_WL_SHIFT);
+		/* Set initial clock - 500kHz. */
+		ti_spi_set_clock(sc, i, 500000);
+	}
+
+#ifdef	TI_SPI_DEBUG
+	ti_spi_printr(dev);
+#endif
+
+	device_add_child(dev, "spibus", -1);
+
+	return (bus_generic_attach(dev));
+}
+
+static int
+ti_spi_detach(device_t dev)
+{
+	struct ti_spi_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	/* Clear pending interrupts and disable interrupts. */
+	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, 0);
+	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xffff);
+
+	/* Reset controller. */
+	TI_SPI_WRITE(sc, MCSPI_SYSCONFIG, MCSPI_SYSCONFIG_SOFTRESET);
+
+	bus_generic_detach(dev);
+
+	mtx_destroy(&sc->sc_mtx);
+	if (sc->sc_intrhand)
+		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
+	if (sc->sc_irq_res)
+		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
+	if (sc->sc_mem_res)
+		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
+
+	return (0);
+}
+
+static int
+ti_spi_fill_fifo(struct ti_spi_softc *sc)
+{
+	int bytes, timeout;
+	struct spi_command *cmd;
+	uint32_t written;
+	uint8_t *data;
+
+	cmd = sc->sc_cmd;
+	bytes = min(sc->sc_len - sc->sc_written, sc->sc_fifolvl);
+	while (bytes-- > 0) {
+		data = (uint8_t *)cmd->tx_cmd;
+		written = sc->sc_written++;
+		if (written >= cmd->tx_cmd_sz) {
+			data = (uint8_t *)cmd->tx_data;
+			written -= cmd->tx_cmd_sz;
+		}
+		if (sc->sc_fifolvl == 1) {
+			/* FIFO disabled. */
+			timeout = 1000;
+			while (--timeout > 0 && (TI_SPI_READ(sc,
+			    MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_TXS) == 0) {
+				DELAY(1);
+			}
+			if (timeout == 0)
+				return (-1);
+		}
+		TI_SPI_WRITE(sc, MCSPI_TX_CH(sc->sc_cs), data[written]);
+	}
+
+	return (0);
+}
+
+static int
+ti_spi_drain_fifo(struct ti_spi_softc *sc)
+{
+	int bytes, timeout;
+	struct spi_command *cmd;
+	uint32_t read;
+	uint8_t *data;
+
+	cmd = sc->sc_cmd;
+	bytes = min(sc->sc_len - sc->sc_read, sc->sc_fifolvl);
+	while (bytes-- > 0) {
+		data = (uint8_t *)cmd->rx_cmd;
+		read = sc->sc_read++;
+		if (read >= cmd->rx_cmd_sz) {
+			data = (uint8_t *)cmd->rx_data;
+			read -= cmd->rx_cmd_sz;
+		}
+		if (sc->sc_fifolvl == 1) {
+			/* FIFO disabled. */
+			timeout = 1000;
+			while (--timeout > 0 && (TI_SPI_READ(sc,
+			    MCSPI_STAT_CH(sc->sc_cs)) & MCSPI_STAT_RXS) == 0) {
+				DELAY(1);
+			}
+			if (timeout == 0)
+				return (-1);
+		}
+		data[read] = TI_SPI_READ(sc, MCSPI_RX_CH(sc->sc_cs));
+	}
+
+	return (0);
+}
+
+static void
+ti_spi_intr(void *arg)
+{
+	int eow;
+	struct ti_spi_softc *sc;
+	uint32_t status;
+
+	eow = 0;
+	sc = (struct ti_spi_softc *)arg;
+	TI_SPI_LOCK(sc);
+	status = TI_SPI_READ(sc, MCSPI_IRQSTATUS);
+
+	/*
+	 * No new TX_empty or RX_full event will be asserted while the CPU has
+	 * not performed the number of writes or reads defined by
+	 * MCSPI_XFERLEVEL[AEL] and MCSPI_XFERLEVEL[AFL].  It is responsibility
+	 * of CPU perform the right number of writes and reads.
+	 */
+	if (status & MCSPI_IRQ_TX0_EMPTY)
+		ti_spi_fill_fifo(sc);
+	if (status & MCSPI_IRQ_RX0_FULL)
+		ti_spi_drain_fifo(sc);
+
+	if (status & MCSPI_IRQ_EOW)
+		eow = 1;
+		
+	/* Clear interrupt status. */
+	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, status);
+
+	/* Check for end of transfer. */
+	if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) {
+		sc->sc_flags |= TI_SPI_DONE;
+		wakeup(sc->sc_dev);
+	}
+
+	TI_SPI_UNLOCK(sc);
+}
+
+static int
+ti_spi_pio_transfer(struct ti_spi_softc *sc)
+{
+
+	while (sc->sc_len - sc->sc_written > 0) {
+		if (ti_spi_fill_fifo(sc) == -1)
+			return (EIO);
+		if (ti_spi_drain_fifo(sc) == -1)
+			return (EIO);
+	}
+
+	return (0);
+}
+
+static int
+ti_spi_gcd(int a, int b)
+{
+	int m;
+
+	while ((m = a % b) != 0) {
+		a = b;
+		b = m;
+	}
+
+	return (b);
+}
+
+static int
+ti_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+	int cs, err;
+	struct ti_spi_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 
+	    ("TX/RX command sizes should be equal"));
+	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 
+	    ("TX/RX data sizes should be equal"));
+
+	/* Get the proper chip select for this child. */
+	spibus_get_cs(child, &cs);
+	if (cs < 0 || cs > sc->sc_numcs) {
+		device_printf(dev, "Invalid chip select %d requested by %s\n",
+		    cs, device_get_nameunit(child));
+		return (EINVAL);
+	}
+
+	TI_SPI_LOCK(sc);
+
+	/* If the controller is in use wait until it is available. */
+	while (sc->sc_flags & TI_SPI_BUSY)
+		mtx_sleep(dev, &sc->sc_mtx, 0, "ti_spi", 0);
+
+	/* Now we have control over SPI controller. */
+	sc->sc_flags = TI_SPI_BUSY;
+
+	/* Save the SPI command data. */
+	sc->sc_cs = cs;
+	sc->sc_cmd = cmd;
+	sc->sc_read = 0;
+	sc->sc_written = 0;
+	sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
+	sc->sc_fifolvl = ti_spi_gcd(sc->sc_len, TI_SPI_FIFOSZ);
+	if (sc->sc_fifolvl < 2 || sc->sc_len > 0xffff)
+		sc->sc_fifolvl = 1;	/* FIFO disabled. */
+	/* Disable FIFO for now. */
+	sc->sc_fifolvl = 1;
+
+	/* Use a safe clock - 500kHz. */
+	ti_spi_set_clock(sc, sc->sc_cs, 500000);
+
+	/* Disable the FIFO. */
+	TI_SPI_WRITE(sc, MCSPI_XFERLEVEL, 0);
+
+	/* 8 bits word, d0 miso, d1 mosi, mode 0 and CS active low. */
+	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+	reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW | MCSPI_CONF_SBPOL |
+	    MCSPI_CONF_SBE | MCSPI_CONF_TURBO | MCSPI_CONF_IS |
+	    MCSPI_CONF_DPE1 | MCSPI_CONF_DPE0 | MCSPI_CONF_DMAR |
+	    MCSPI_CONF_DMAW | MCSPI_CONF_EPOL);
+	reg |= MCSPI_CONF_DPE0 | MCSPI_CONF_EPOL | MCSPI_CONF_WL8BITS;
+	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+#if 0
+	/* Enable channel interrupts. */
+	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+	reg |= 0xf;
+	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
+#endif
+
+	/* Start the transfer. */
+	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
+	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg | MCSPI_CTRL_ENABLE);
+
+	/* Force CS on. */
+	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg |= MCSPI_CONF_FORCE);
+
+	err = 0;
+	if (sc->sc_fifolvl == 1)
+		err = ti_spi_pio_transfer(sc);
+
+	/* Force CS off. */
+	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+	reg &= ~MCSPI_CONF_FORCE;
+	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+	/* Disable IRQs. */
+	reg = TI_SPI_READ(sc, MCSPI_IRQENABLE);
+	reg &= ~0xf;
+	TI_SPI_WRITE(sc, MCSPI_IRQENABLE, reg);
+	TI_SPI_WRITE(sc, MCSPI_IRQSTATUS, 0xf);
+
+	/* Disable the SPI channel. */
+	reg = TI_SPI_READ(sc, MCSPI_CTRL_CH(sc->sc_cs));
+	reg &= ~MCSPI_CTRL_ENABLE;
+	TI_SPI_WRITE(sc, MCSPI_CTRL_CH(sc->sc_cs), reg);
+
+	/* Disable FIFO. */
+	reg = TI_SPI_READ(sc, MCSPI_CONF_CH(sc->sc_cs));
+	reg &= ~(MCSPI_CONF_FFER | MCSPI_CONF_FFEW);
+	TI_SPI_WRITE(sc, MCSPI_CONF_CH(sc->sc_cs), reg);
+
+	/* Release the controller and wakeup the next thread waiting for it. */
+	sc->sc_flags = 0;
+	wakeup_one(dev);
+	TI_SPI_UNLOCK(sc);
+
+	return (err);
+}
+
+static phandle_t
+ti_spi_get_node(device_t bus, device_t dev)
+{
+
+	/* Share controller node with spibus. */
+	return (ofw_bus_get_node(bus));
+}
+
+static device_method_t ti_spi_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		ti_spi_probe),
+	DEVMETHOD(device_attach,	ti_spi_attach),
+	DEVMETHOD(device_detach,	ti_spi_detach),
+
+	/* SPI interface */
+	DEVMETHOD(spibus_transfer,	ti_spi_transfer),
+
+	/* ofw_bus interface */
+	DEVMETHOD(ofw_bus_get_node,	ti_spi_get_node),
+
+	DEVMETHOD_END
+};
+
+static devclass_t ti_spi_devclass;
+
+static driver_t ti_spi_driver = {
+	"spi",
+	ti_spi_methods,
+	sizeof(struct ti_spi_softc),
+};
+
+DRIVER_MODULE(ti_spi, simplebus, ti_spi_driver, ti_spi_devclass, 0, 0);

Added: head/sys/arm/ti/ti_spireg.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/ti/ti_spireg.h	Tue Mar 29 19:11:04 2016	(r297395)
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_TI_SPIREG_H_
+#define	_TI_SPIREG_H_
+
+#define	TI_SPI_GCLK			48000000U
+#define	TI_SPI_FIFOSZ			32
+#define	MCSPI_REVISION			0x0
+#define	 MCSPI_REVISION_SCHEME_SHIFT	30
+#define	 MCSPI_REVISION_SCHEME_MSK	0x3
+#define	 MCSPI_REVISION_FUNC_SHIFT	16
+#define	 MCSPI_REVISION_FUNC_MSK	0xfff
+#define	 MCSPI_REVISION_RTL_SHIFT	11
+#define	 MCSPI_REVISION_RTL_MSK		0x1f
+#define	 MCSPI_REVISION_MAJOR_SHIFT	8
+#define	 MCSPI_REVISION_MAJOR_MSK	0x7
+#define	 MCSPI_REVISION_CUSTOM_SHIFT	6
+#define	 MCSPI_REVISION_CUSTOM_MSK	0x3
+#define	 MCSPI_REVISION_MINOR_SHIFT	0
+#define	 MCSPI_REVISION_MINOR_MSK	0x3f
+#define	MCSPI_SYSCONFIG			0x110
+#define	 MCSPI_SYSCONFIG_SOFTRESET	(1 << 1)
+#define	MCSPI_SYSSTATUS			0x114
+#define	 MCSPI_SYSSTATUS_RESETDONE	(1 << 0)
+#define	MCSPI_MODULCTRL			0x128
+#define	 MCSPI_MODULCTRL_SLAVE		(1 << 2)
+#define	 MCSPI_MODULCTRL_SINGLE		(1 << 0)
+#define	MCSPI_IRQSTATUS			0x118
+#define	MCSPI_IRQENABLE			0x11c
+#define	 MCSPI_IRQ_EOW			(1 << 17)
+#define	 MCSPI_IRQ_RX0_OVERFLOW		(1 << 3)
+#define	 MCSPI_IRQ_RX0_FULL		(1 << 2)
+#define	 MCSPI_IRQ_TX0_UNDERFLOW	(1 << 1)
+#define	 MCSPI_IRQ_TX0_EMPTY		(1 << 0)
+#define	MCSPI_CONF_CH(_c)		(0x12c + 0x14 * (_c))
+#define	 MCSPI_CONF_CLKG		(1 << 29)
+#define	 MCSPI_CONF_FFER		(1 << 28)
+#define	 MCSPI_CONF_FFEW		(1 << 27)
+#define	 MCSPI_CONF_SBPOL		(1 << 24)
+#define	 MCSPI_CONF_SBE			(1 << 23)
+#define	 MCSPI_CONF_FORCE		(1 << 20)
+#define	 MCSPI_CONF_TURBO		(1 << 19)
+#define	 MCSPI_CONF_IS			(1 << 18)
+#define	 MCSPI_CONF_DPE1		(1 << 17)
+#define	 MCSPI_CONF_DPE0		(1 << 16)
+#define	 MCSPI_CONF_DMAR		(1 << 15)
+#define	 MCSPI_CONF_DMAW		(1 << 14)
+#define	 MCSPI_CONF_WL_MSK		0x1f
+#define	 MCSPI_CONF_WL_SHIFT		7
+#define	 MCSPI_CONF_WL8BITS		(7 << MCSPI_CONF_WL_SHIFT)
+#define	 MCSPI_CONF_EPOL		(1 << 6)
+#define	 MCSPI_CONF_CLK_MSK		0xf
+#define	 MCSPI_CONF_CLK_SHIFT		2
+#define	 MCSPI_CONF_POL			(1 << 1)
+#define	 MCSPI_CONF_PHA			(1 << 0)
+#define	MCSPI_STAT_CH(_c)		(0x130 + 0x14 * (_c))
+#define	 MCSPI_STAT_TXFFF		(1 << 4)
+#define	 MCSPI_STAT_TXS			(1 << 1)
+#define	 MCSPI_STAT_RXS			(1 << 0)
+#define	MCSPI_CTRL_CH(_c)		(0x134 + 0x14 * (_c))
+#define	 MCSPI_EXTCLK_MSK		0xfff
+#define	 MCSPI_CTRL_EXTCLK_MSK		0xff
+#define	 MCSPI_CTRL_EXTCLK_SHIFT	8
+#define	 MCSPI_CTRL_ENABLE		(1 << 0)
+#define	MCSPI_TX_CH(_c)			(0x138 + 0x14 * (_c))
+#define	MCSPI_RX_CH(_c)			(0x13c + 0x14 * (_c))
+#define	MCSPI_XFERLEVEL			0x17c
+#define	 MCSPI_XFERLEVEL_AFL(_a)	(((_a) >> 8) & 0xff)
+#define	 MCSPI_XFERLEVEL_AEL(_a)	(((_a) >> 0) & 0xff)
+
+#endif	/* _TI_SPIREG_H_ */

Added: head/sys/arm/ti/ti_spivar.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/ti/ti_spivar.h	Tue Mar 29 19:11:04 2016	(r297395)
@@ -0,0 +1,71 @@
+/*-
+ * Copyright (c) 2016 Rubicon Communications, LLC (Netgate)
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	_TI_SPIVAR_H_
+#define	_TI_SPIVAR_H_
+
+struct ti_spi_softc {
+	bus_space_tag_t		sc_bst;
+	bus_space_handle_t	sc_bsh;
+	device_t		sc_dev;
+	int			sc_numcs;
+	struct mtx		sc_mtx;
+	struct resource		*sc_mem_res;
+	struct resource		*sc_irq_res;
+	struct {
+		int		cs;
+		int		fifolvl;
+		struct spi_command	*cmd;
+		uint32_t	len;
+		uint32_t	read;
+		uint32_t	written;
+	} xfer;
+	uint32_t		sc_flags;
+	void			*sc_intrhand;
+#define	sc_cs			xfer.cs
+#define	sc_fifolvl		xfer.fifolvl
+#define	sc_cmd			xfer.cmd
+#define	sc_len			xfer.len
+#define	sc_read			xfer.read
+#define	sc_written		xfer.written
+};
+
+#define	TI_SPI_BUSY		0x1
+#define	TI_SPI_DONE		0x2
+
+#define TI_SPI_WRITE(_sc, _off, _val)		\
+    bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
+#define TI_SPI_READ(_sc, _off)			\
+    bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
+
+#define TI_SPI_LOCK(_sc)			\
+    mtx_lock(&(_sc)->sc_mtx)
+#define TI_SPI_UNLOCK(_sc)			\
+    mtx_unlock(&(_sc)->sc_mtx)
+
+#endif	/* _TI_SPIVAR_H_ */


More information about the svn-src-all mailing list