Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 11 Jun 2014 10:03:06 +0000 (UTC)
From:      Ruslan Bukin <br@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r267354 - in head/sys/arm: conf freescale/vybrid
Message-ID:  <201406111003.s5BA36gt079670@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: br
Date: Wed Jun 11 10:03:06 2014
New Revision: 267354
URL: http://svnweb.freebsd.org/changeset/base/267354

Log:
  Add a driver for Serial Peripheral Interface (SPI).

Added:
  head/sys/arm/freescale/vybrid/vf_spi.c   (contents, props changed)
Modified:
  head/sys/arm/conf/VYBRID
  head/sys/arm/freescale/vybrid/files.vybrid

Modified: head/sys/arm/conf/VYBRID
==============================================================================
--- head/sys/arm/conf/VYBRID	Wed Jun 11 09:31:09 2014	(r267353)
+++ head/sys/arm/conf/VYBRID	Wed Jun 11 10:03:06 2014	(r267354)
@@ -141,6 +141,10 @@ device		bpf			# Berkeley packet filter
 
 device		sound
 
+# SPI
+device		spibus
+device		vf_spi
+
 # Framebuffer
 device		vt
 device		kbdmux

Modified: head/sys/arm/freescale/vybrid/files.vybrid
==============================================================================
--- head/sys/arm/freescale/vybrid/files.vybrid	Wed Jun 11 09:31:09 2014	(r267353)
+++ head/sys/arm/freescale/vybrid/files.vybrid	Wed Jun 11 10:03:06 2014	(r267354)
@@ -31,4 +31,5 @@ arm/freescale/vybrid/vf_ehci.c			optiona
 arm/freescale/vybrid/vf_gpio.c			optional	gpio
 arm/freescale/vybrid/vf_uart.c			optional	uart
 arm/freescale/vybrid/vf_sai.c			optional	sound
+arm/freescale/vybrid/vf_spi.c			optional	vf_spi
 dev/ffec/if_ffec.c				optional	ffec

Added: head/sys/arm/freescale/vybrid/vf_spi.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/freescale/vybrid/vf_spi.c	Wed Jun 11 10:03:06 2014	(r267354)
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
+ * 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.
+ */
+
+/*
+ * Vybrid Family Serial Peripheral Interface (SPI)
+ * Chapter 47, Vybrid Reference Manual, Rev. 5, 07/2013
+ */
+
+#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/malloc.h>
+#include <sys/rman.h>
+#include <sys/timeet.h>
+#include <sys/timetc.h>
+#include <sys/watchdog.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <machine/bus.h>
+#include <machine/fdt.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <arm/freescale/vybrid/vf_common.h>
+
+#define	SPI_FIFO_SIZE	4
+
+#define	SPI_MCR		0x00		/* Module Configuration */
+#define	 MCR_MSTR	(1 << 31)	/* Master/Slave Mode Select */
+#define	 MCR_CONT_SCKE	(1 << 30)	/* Continuous SCK Enable */
+#define	 MCR_FRZ	(1 << 27)	/* Freeze */
+#define	 MCR_PCSIS_S	16		/* Peripheral Chip Select */
+#define	 MCR_PCSIS_M	0x3f
+#define	 MCR_MDIS	(1 << 14)	/* Module Disable */
+#define	 MCR_CLR_TXF	(1 << 11)	/* Clear TX FIFO */
+#define	 MCR_CLR_RXF	(1 << 10)	/* Clear RX FIFO */
+#define	 MCR_HALT	(1 << 0)	/* Starts and stops SPI transfers */
+#define	SPI_TCR		0x08		/* Transfer Count */
+#define	SPI_CTAR0	0x0C		/* Clock and Transfer Attributes */
+#define	SPI_CTAR0_SLAVE	0x0C		/* Clock and Transfer Attributes */
+#define	SPI_CTAR1	0x10		/* Clock and Transfer Attributes */
+#define	SPI_CTAR2	0x14		/* Clock and Transfer Attributes */
+#define	SPI_CTAR3	0x18		/* Clock and Transfer Attributes */
+#define	 CTAR_FMSZ_M	0xf
+#define	 CTAR_FMSZ_S	27		/* Frame Size */
+#define	 CTAR_FMSZ_8	0x7		/* 8 bits */
+#define	 CTAR_CPOL	(1 << 26)	/* Clock Polarity */
+#define	 CTAR_CPHA	(1 << 25)	/* Clock Phase */
+#define	 CTAR_LSBFE	(1 << 24)	/* Less significant bit first */
+#define	 CTAR_PCSSCK_M	0x3
+#define	 CTAR_PCSSCK_S	22		/* PCS to SCK Delay Prescaler */
+#define	 CTAR_PBR_M	0x3
+#define	 CTAR_PBR_S	16		/* Baud Rate Prescaler */
+#define	 CTAR_PBR_7	0x3		/* Divide by 7 */
+#define	 CTAR_CSSCK_M	0xf
+#define	 CTAR_CSSCK_S	12		/* PCS to SCK Delay Scaler */
+#define	 CTAR_BR_M	0xf
+#define	 CTAR_BR_S	0		/* Baud Rate Scaler */
+#define	SPI_SR		0x2C		/* Status Register */
+#define	 SR_TCF		(1 << 31)	/* Transfer Complete Flag */
+#define	 SR_EOQF	(1 << 28)	/* End of Queue Flag */
+#define	 SR_TFFF	(1 << 25)	/* Transmit FIFO Fill Flag */
+#define	 SR_RFDF	(1 << 17)	/* Receive FIFO Drain Flag */
+#define	SPI_RSER	0x30		/* DMA/Interrupt Select */
+#define	 RSER_EOQF_RE	(1 << 28)	/* Finished Request Enable */
+#define	SPI_PUSHR	0x34		/* PUSH TX FIFO In Master Mode */
+#define	 PUSHR_CONT	(1 << 31)	/* Continuous Peripheral CS */
+#define	 PUSHR_EOQ	(1 << 27)	/* End Of Queue */
+#define	 PUSHR_CTCNT	(1 << 26)	/* Clear Transfer Counter */
+#define	 PUSHR_PCS_M	0x3f
+#define	 PUSHR_PCS_S	16		/* Select PCS signals */
+
+#define	SPI_PUSHR_SLAVE	0x34	/* PUSH TX FIFO Register In Slave Mode */
+#define	SPI_POPR	0x38	/* POP RX FIFO Register */
+#define	SPI_TXFR0	0x3C	/* Transmit FIFO Registers */
+#define	SPI_TXFR1	0x40
+#define	SPI_TXFR2	0x44
+#define	SPI_TXFR3	0x48
+#define	SPI_RXFR0	0x7C	/* Receive FIFO Registers */
+#define	SPI_RXFR1	0x80
+#define	SPI_RXFR2	0x84
+#define	SPI_RXFR3	0x88
+
+struct spi_softc {
+	struct resource		*res[2];
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+	void			*ih;
+};
+
+static struct resource_spec spi_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+static int
+spi_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "fsl,mvf600-spi"))
+		return (ENXIO);
+
+	device_set_desc(dev, "Vybrid Family Serial Peripheral Interface");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+spi_attach(device_t dev)
+{
+	struct spi_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	if (bus_alloc_resources(dev, spi_spec, sc->res)) {
+		device_printf(dev, "could not allocate resources\n");
+		return (ENXIO);
+	}
+
+	/* Memory interface */
+	sc->bst = rman_get_bustag(sc->res[0]);
+	sc->bsh = rman_get_bushandle(sc->res[0]);
+
+	reg = READ4(sc, SPI_MCR);
+	reg |= MCR_MSTR;
+	reg &= ~(MCR_CONT_SCKE | MCR_MDIS | MCR_FRZ);
+	reg &= ~(MCR_PCSIS_M << MCR_PCSIS_S);
+	reg |= (MCR_PCSIS_M << MCR_PCSIS_S);	/* PCS Active low */
+	reg |= (MCR_CLR_TXF | MCR_CLR_RXF);
+	WRITE4(sc, SPI_MCR, reg);
+
+	reg = READ4(sc, SPI_RSER);
+	reg |= RSER_EOQF_RE;
+	WRITE4(sc, SPI_RSER, reg);
+
+	reg = READ4(sc, SPI_MCR);
+	reg &= ~MCR_HALT;
+	WRITE4(sc, SPI_MCR, reg);
+
+	reg = READ4(sc, SPI_CTAR0);
+	reg &= ~(CTAR_FMSZ_M << CTAR_FMSZ_S);
+	reg |= (CTAR_FMSZ_8 << CTAR_FMSZ_S);
+	/*
+	 * TODO: calculate BR
+	 * SCK baud rate = ( fsys / PBR ) * (1 + DBR) / BR
+	 *
+	 * reg &= ~(CTAR_BR_M << CTAR_BR_S);
+	 */
+	reg &= ~CTAR_CPOL; /* Polarity */
+	reg |= CTAR_CPHA;
+	/*
+	 * Set LSB (Less significant bit first)
+	 * must be used for some applications, e.g. some LCDs
+	 */
+	reg |= CTAR_LSBFE;
+	WRITE4(sc, SPI_CTAR0, reg);
+
+	reg = READ4(sc, SPI_CTAR0);
+	reg &= ~(CTAR_PBR_M << CTAR_PBR_S);
+	reg |= (CTAR_PBR_7 << CTAR_PBR_S);
+	WRITE4(sc, SPI_CTAR0, reg);
+
+	device_add_child(dev, "spibus", 0);
+	return (bus_generic_attach(dev));
+}
+
+static int
+spi_txrx(struct spi_softc *sc, uint8_t *out_buf,
+    uint8_t *in_buf, int bufsz, int cs)
+{
+	uint32_t reg, wreg;
+	uint32_t txcnt;
+	uint32_t i;
+
+	txcnt = 0;
+
+	for (i = 0; i < bufsz; i++) {
+		txcnt++;
+		wreg = out_buf[i];
+		wreg |= PUSHR_CONT;
+		wreg |= (cs << PUSHR_PCS_S);
+		if (i == 0)
+			wreg |= PUSHR_CTCNT;
+		if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE)
+			wreg |= PUSHR_EOQ;
+		WRITE4(sc, SPI_PUSHR, wreg);
+
+		if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE) {
+			txcnt = 0;
+
+			/* Wait last entry in a queue to be transmitted */
+			while((READ4(sc, SPI_SR) & SR_EOQF) == 0)
+				continue;
+
+			reg = READ4(sc, SPI_SR);
+			reg |= (SR_TCF | SR_EOQF);
+			WRITE4(sc, SPI_SR, reg);
+		}
+
+		/* Wait until RX FIFO is empty */
+		while((READ4(sc, SPI_SR) & SR_RFDF) == 0)
+			continue;
+
+		in_buf[i] = READ1(sc, SPI_POPR);
+	}
+
+	return (0);
+}
+
+static int
+spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+	struct spi_softc *sc;
+	uint32_t cs;
+
+	sc = device_get_softc(dev);
+
+	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+	    ("%s: TX/RX command sizes should be equal", __func__));
+	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+	    ("%s: TX/RX data sizes should be equal", __func__));
+
+	/* get the proper chip select */
+	spibus_get_cs(child, &cs);
+
+	/* Command */
+	spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs);
+
+	/* Data */
+	spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs);
+
+	return (0);
+}
+
+static device_method_t spi_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		spi_probe),
+	DEVMETHOD(device_attach,	spi_attach),
+	/* SPI interface */
+	DEVMETHOD(spibus_transfer,	spi_transfer),
+	{ 0, 0 }
+};
+
+static driver_t spi_driver = {
+	"spi",
+	spi_methods,
+	sizeof(struct spi_softc),
+};
+
+static devclass_t spi_devclass;
+
+DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406111003.s5BA36gt079670>