linux-spi.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] Simple driver for Xilinx SPI controler.
@ 2007-06-06 14:05 Andrei Konovalov
  2007-06-06 16:03 ` Stephen Neuendorffer
       [not found] ` <4666BF47.8080103-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
  0 siblings, 2 replies; 15+ messages in thread
From: Andrei Konovalov @ 2007-06-06 14:05 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

Only master mode is supported.
No support for multiple masters. Slave mode is not supported either.
Not using level 1 drivers from EDK.

The reason not to use EDK is basically this driver being a simple one:
- SPI level 1 driver from EDK supports much more then needed for this
   simple version of the SPI driver (slave mode, multiple masters etc).
   And leaving just single master case alone is not possible without
   changing the level 1 driver.
- SPI level 1 driver from EDK manipulates chip selects on its own.
   This makes it impossible to use the spi_bitbang.

The driver has been tested with Xilinx ML300 reference design.

The patch to enable SPI for Xilinx ML300 board (EDK reference design)
will be sent to linuxppc-embedded list shortly.

Would be nice to get this driver into mainline.
Reviews and comments are welcome.

Thanks,
Andrei

Signed-off-by: Yuri Frolov <yfrolov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
Signed-off-by: Andrei Konovalov <akonovalov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
---
  arch/ppc/syslib/virtex_devices.h |    6 +
  drivers/spi/Kconfig              |    9 +
  drivers/spi/Makefile             |    1 +
  drivers/spi/xilinx_spi.c         |  447 ++++++++++++++++++++++++++++++++++++++
  4 files changed, 463 insertions(+), 0 deletions(-)
  create mode 100644 drivers/spi/xilinx_spi.c

diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h
index b49dc61..9ab3152 100644
--- a/arch/ppc/syslib/virtex_devices.h
+++ b/arch/ppc/syslib/virtex_devices.h
@@ -45,4 +45,10 @@ struct xtemac_platform_data {
  	u8 mac_addr[6];
  };

+/* SPI Controller IP */
+struct xspi_platform_data {
+	s16 bus_num;
+	u16 num_chipselect;
+};
+
  #endif  /* __ASM_VIRTEX_DEVICES_H__ */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 5e3f748..851d651 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -156,6 +156,15 @@ config SPI_S3C24XX_GPIO
  	  GPIO lines to provide the SPI bus. This can be used where
  	  the inbuilt hardware cannot provide the transfer mode, or
  	  where the board is using non hardware connected pins.
+
+config SPI_XILINX
+	tristate "Xilinx SPI controller"
+	depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL
+	select SPI_BITBANG
+	help
+	  This enables using the SPI controller IP from Xilinx EDK in master
+	  mode. See the DS464, "OPB Serial Peripheral Interface (SPI) (v1.00e)"
+	  Product Specification document for the hardware details.
  #
  # Add new SPI master controllers in alphabetical order above this line
  #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5788d86..a2412bd 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
  obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
  obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
  obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
+obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
  # 	... add above this line ...

  # SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
new file mode 100644
index 0000000..e5f149f
--- /dev/null
+++ b/drivers/spi/xilinx_spi.c
@@ -0,0 +1,447 @@
+/*
+ * xilinx_spi.c
+ *
+ * Xilinx SPI controler driver (master mode only)
+ *
+ * Author: MontaVista Software, Inc.
+ *         source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org
+ *
+ * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2.  This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <asm/io.h>
+#include <syslib/virtex_devices.h>
+
+#define XILINX_SPI_NAME "xspi"
+
+/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
+ * Product Specification", DS464
+ */
+#define XSPI_CR_OFFSET		0x62	/* 16-bit Control Register */
+
+#define XSPI_CR_ENABLE		0x02
+#define XSPI_CR_MASTER_MODE	0x04
+#define XSPI_CR_CPOL		0x08
+#define XSPI_CR_CPHA		0x10
+#define XSPI_CR_MODE_MASK	(XSPI_CR_CPHA | XSPI_CR_CPOL)
+#define XSPI_CR_TXFIFO_RESET	0x20
+#define XSPI_CR_RXFIFO_RESET	0x40
+#define XSPI_CR_MANUAL_SSELECT	0x80
+#define XSPI_CR_TRANS_INHIBIT	0x100
+
+#define XSPI_SR_OFFSET		0x67	/* 8-bit Status Register */
+
+#define XSPI_SR_RX_EMPTY_MASK	0x01	/* Receive FIFO is empty */
+#define XSPI_SR_RX_FULL_MASK	0x02	/* Receive FIFO is full */
+#define XSPI_SR_TX_EMPTY_MASK	0x04	/* Transmit FIFO is empty */
+#define XSPI_SR_TX_FULL_MASK	0x08	/* Transmit FIFO is full */
+#define XSPI_SR_MODE_FAULT_MASK	0x10	/* Mode fault error */
+
+#define XSPI_TXD_OFFSET		0x6b	/* 8-bit Data Transmit Register */
+#define XSPI_RXD_OFFSET		0x6f	/* 8-bit Data Receive Register */
+
+#define XSPI_SSR_OFFSET		0x70	/* 32-bit Slave Select Register */
+
+/* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414
+ * IPIF registers are 32 bit
+ */
+#define XIPIF_V123B_DGIER_OFFSET	0x1c	/* IPIF global int enable reg */
+#define XIPIF_V123B_GINTR_ENABLE	0x80000000
+
+#define XIPIF_V123B_IISR_OFFSET		0x20	/* IPIF interrupt status reg */
+#define XIPIF_V123B_IIER_OFFSET		0x28	/* IPIF interrupt enable reg */
+
+#define XSPI_INTR_MODE_FAULT		0x01	/* Mode fault error */
+#define XSPI_INTR_SLAVE_MODE_FAULT	0x02	/* Selected as slave while
+                                                * disabled */
+#define XSPI_INTR_TX_EMPTY		0x04	/* TxFIFO is empty */
+#define XSPI_INTR_TX_UNDERRUN		0x08	/* TxFIFO was underrun */
+#define XSPI_INTR_RX_FULL		0x10	/* RxFIFO is full */
+#define XSPI_INTR_RX_OVERRUN		0x20	/* RxFIFO was overrun */
+
+#define XIPIF_V123B_RESETR_OFFSET	0x40	/* IPIF reset register */
+#define XIPIF_V123B_RESET_MASK		0x0a	/* the value to write */
+
+/* Simple macros to get the code more readable */
+#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
+#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
+#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
+#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))
+
+struct xilinx_spi {
+	/* bitbang has to be first */
+	struct spi_bitbang bitbang;
+	struct completion done;
+
+	u32		regs_phys;	/* phys. address of the control registers */
+	void __iomem	*regs;		/* virt. address of the control registers */
+
+	u32 irq;
+
+	u8 *rx_ptr;		/* pointer in the Tx buffer */
+	const u8 *tx_ptr;	/* pointer in the Rx buffer */
+	int remaining_bytes;	/* the number of bytes left to transfer */
+};
+
+static void xspi_abort_transfer(u8 __iomem *regs_base)
+{
+	/* Deselect the slave on the SPI bus */
+	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+
+	/* Terminate transmit in progress (if any) and Reset the FIFOs */
+	xspi_out16(regs_base + XSPI_CR_OFFSET,
+		   xspi_in16(regs_base + XSPI_CR_OFFSET)
+		   | XSPI_CR_TRANS_INHIBIT | XSPI_CR_TXFIFO_RESET
+		   | XSPI_CR_RXFIFO_RESET);
+}
+
+static void xspi_init_hw(u8 __iomem *regs_base)
+{
+	/* Reset the SPI device */
+	xspi_out32(regs_base + XIPIF_V123B_RESETR_OFFSET,
+		   XIPIF_V123B_RESET_MASK);
+	/* Disable all the interrupts just in case */
+	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+	/* Enable the global IPIF interrupt */
+	xspi_out32(regs_base + XIPIF_V123B_DGIER_OFFSET,
+		   XIPIF_V123B_GINTR_ENABLE);
+	/* Deselect the slave on the SPI bus */
+	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	/* Disable the transmitter, enable Manual Slave Select Assertion,
+	 * put SPI controller into master mode, and enable it */
+	xspi_out16(regs_base + XSPI_CR_OFFSET,
+		   XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
+		   | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+}
+
+static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
+{
+	struct xilinx_spi *xspi;
+	u8 __iomem *regs_base;
+
+	xspi = spi_master_get_devdata(spi->master);
+	regs_base = xspi->regs;
+
+	if (is_on == BITBANG_CS_INACTIVE) {
+		/* Deselect the slave on the SPI bus */
+		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	} else if (is_on == BITBANG_CS_ACTIVE) {
+		/* Set the SPI clock phase and polarity */
+		u16 cr = xspi_in16(regs_base + XSPI_CR_OFFSET)
+			 & ~XSPI_CR_MODE_MASK;
+		if (spi->mode & SPI_CPHA)
+			cr |= XSPI_CR_CPHA;
+		if (spi->mode & SPI_CPOL)
+			cr |= XSPI_CR_CPOL;
+		xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+
+		/* We do not check spi->max_speed_hz here as the SPI clock
+		 * frequency is not software programmable (the IP block design
+		 * parameter)
+		 */
+
+		/* Activate the chip select */
+		xspi_out32(regs_base + XSPI_SSR_OFFSET,
+			   ~(0x0001 << spi->chip_select));
+	}
+}
+
+/* spi_bitbang requires custom setup_transfer() to be defined if there is a
+ * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
+ * supports just 8 bits per word, and SPI clock can't be changed in software.
+ * Check for 8 bits per word; speed_hz checking could be added if the SPI
+ * clock information is available. Chip select delay calculations could be
+ * added here as soon as bitbang_work() can be made aware of the delay value.
+ */
+static int xilinx_spi_setup_transfer(struct spi_device *spi,
+				     struct spi_transfer *t)
+{
+	u8 bits_per_word;
+
+	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
+	if (bits_per_word != 8)
+		return -EINVAL;
+
+	return 0;
+}
+
+
+static int xilinx_spi_setup(struct spi_device *spi)
+{
+	struct spi_bitbang *bitbang;
+	struct xilinx_spi *xspi;
+	int retval;
+
+	xspi = spi_master_get_devdata(spi->master);
+	bitbang = &xspi->bitbang;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	retval = xilinx_spi_setup_transfer(spi, NULL);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+		__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
+		spi->bits_per_word, 0);
+
+	return 0;
+}
+
+static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct xilinx_spi *xspi;
+	u8 __iomem *regs_base;
+	u32 ipif_ier;
+	u16 cr;
+	u8 sr;
+
+	/* We get here with transmitter inhibited */
+
+	xspi = spi_master_get_devdata(spi->master);
+	regs_base = xspi->regs;
+
+	xspi->tx_ptr = t->tx_buf;
+	xspi->rx_ptr = t->rx_buf;
+	xspi->remaining_bytes = t->len;
+	INIT_COMPLETION(xspi->done);
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	sr = in_8(regs_base + XSPI_SR_OFFSET);
+	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
+		if (xspi->tx_ptr) {
+			out_8(regs_base + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
+		} else {
+			out_8(regs_base + XSPI_TXD_OFFSET, 0);
+		}
+		xspi->remaining_bytes--;
+		sr = in_8(regs_base + XSPI_SR_OFFSET);
+	}
+
+	/* Enable the transmit empty interrupt, which we use to determine
+	 * progress on the transmission.
+	 */
+	ipif_ier = xspi_in32(regs_base + XIPIF_V123B_IIER_OFFSET);
+	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET,
+		   ipif_ier | XSPI_INTR_TX_EMPTY);
+
+	/* Start the transfer by not inhibiting the transmitter any longer */
+	cr = xspi_in16(regs_base + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
+	xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+
+	wait_for_completion(&xspi->done);
+
+	/* Disable the transmit empty interrupt */
+	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+
+	return t->len - xspi->remaining_bytes;
+}
+
+
+/* This driver supports single master mode only. Hence Tx FIFO Empty
+ * is the only interrupt we care about.
+ * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode
+ * Fault are not to happen.
+ */
+static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
+{
+	struct xilinx_spi *xspi;
+	u8 __iomem *regs_base;
+	u32 ipif_isr;
+
+	xspi = (struct xilinx_spi *) dev_id;
+	regs_base = xspi->regs;
+
+     	/* Get the IPIF inetrrupts, and clear them immediately */
+	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
+	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+
+	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
+		u16 cr;
+		u8 sr;
+
+		/* A transmit has just completed. Process received data and
+		 * check for more data to transmit. Always inhibit the
+		 * transmitter while the Isr refills the transmit register/FIFO,
+		 * or make sure it is stopped if we're done.
+	         */
+		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
+		xspi_out16(regs_base + XSPI_CR_OFFSET,
+			   cr | XSPI_CR_TRANS_INHIBIT);
+
+		/* Read out all the data from the Rx FIFO */
+		sr = in_8(regs_base + XSPI_SR_OFFSET);
+		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
+			u8 data;
+
+			data = in_8(regs_base + XSPI_RXD_OFFSET);
+			if (xspi->rx_ptr) {
+				*xspi->rx_ptr++ = data;
+			}
+			sr = in_8(regs_base + XSPI_SR_OFFSET);
+		}
+
+	        /* See if there is more data to send */
+		if (xspi->remaining_bytes > 0) {
+			/* sr content is valid here; no need for io_8() */
+			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
+				&& xspi->remaining_bytes > 0) {
+				if (xspi->tx_ptr) {
+					out_8(regs_base + XSPI_TXD_OFFSET,
+					      *xspi->tx_ptr++);
+				} else {
+					out_8(regs_base + XSPI_TXD_OFFSET, 0);
+				}
+				xspi->remaining_bytes--;
+				sr = in_8(regs_base + XSPI_SR_OFFSET);
+			}
+			/* Start the transfer by not inhibiting the
+			 * transmitter any longer
+			 */
+			xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+		} else {
+			/* No more data to send.
+			 * Indicate the transfer is completed.
+			 */
+			complete(&xspi->done);
+		}
+	} else {
+		/* spurious interrupt */
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __init xilinx_spi_probe(struct platform_device *dev)
+{
+	int ret = 0;
+	struct spi_master *master;
+	struct xilinx_spi *xspi;
+	struct xspi_platform_data *pdata;
+	struct resource *r;
+
+	/* Get resources(memory, IRQ) associated with the device */
+	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
+
+	if (master == NULL) {
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(dev, master);
+	pdata = dev->dev.platform_data;
+
+	if (pdata == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	xspi = spi_master_get_devdata(master);
+	xspi->bitbang.master = spi_master_get(master);
+	xspi->bitbang.chipselect = xilinx_spi_chipselect;
+	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
+	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
+	xspi->bitbang.master->setup = xilinx_spi_setup;
+	init_completion(&xspi->done);
+
+	xspi->regs = ioremap(r->start, r->end - r->start + 1);
+	if (xspi->regs == NULL) {
+		ret = -ENOMEM;
+		goto put_master;
+	}
+
+	xspi->irq = platform_get_irq(dev, 0);
+	if (xspi->irq < 0) {
+		ret = -ENXIO;
+		goto unmap_io;
+	}
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
+
+	/* SPI controller initializations */
+	xspi_init_hw(xspi->regs);
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+	if (ret != 0)
+		goto unmap_io;
+
+	ret = spi_bitbang_start(&xspi->bitbang);
+	if (ret != 0) {
+		printk(KERN_ALERT "spi_btbang_start FAILED\n");
+		goto free_irq;
+	}
+
+	printk(KERN_INFO "%s: at 0x%08X mapped to 0x%08X, irq=%d\n",
+	       dev->dev.bus_id, r->start, (u32)xspi->regs, xspi->irq);
+
+	return ret;
+
+free_irq:
+        free_irq(xspi->irq, xspi);
+unmap_io:
+	iounmap(xspi->regs);
+put_master:
+        spi_master_put(master);
+	return ret;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+	struct xilinx_spi *xspi;
+	struct spi_master *master;
+
+	master = platform_get_drvdata(dev);
+	xspi = spi_master_get_devdata(master);
+
+	spi_bitbang_stop(&xspi->bitbang);
+	xspi_abort_transfer(xspi->regs);
+	free_irq(xspi->irq, xspi);
+	iounmap(xspi->regs);
+	platform_set_drvdata(dev, 0);
+	spi_master_put(xspi->bitbang.master);
+
+	return 0;
+}
+
+static struct platform_driver xilinx_spi_driver = {
+	.probe	= xilinx_spi_probe,
+	.remove	= __devexit_p(xilinx_spi_remove),
+	.driver = {
+		   .name = XILINX_SPI_NAME,
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init xilinx_spi_init(void)
+{
+	return platform_driver_register(&xilinx_spi_driver);
+}
+
+static void __exit xilinx_spi_exit(void)
+{
+	platform_driver_unregister(&xilinx_spi_driver);
+}
+
+module_init(xilinx_spi_init);
+module_exit(xilinx_spi_exit);
+
+MODULE_AUTHOR("MontaVista Software, Inc. <source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("Xilinx SPI driver");
+MODULE_LICENSE("GPL");
-- 
1.5.1.1



-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* RE: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 14:05 [PATCH] Simple driver for Xilinx SPI controler Andrei Konovalov
@ 2007-06-06 16:03 ` Stephen Neuendorffer
  2007-06-06 17:57   ` Andrei Konovalov
       [not found] ` <4666BF47.8080103-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Stephen Neuendorffer @ 2007-06-06 16:03 UTC (permalink / raw)
  To: Andrei Konovalov, spi-devel-general; +Cc: linuxppc-embedded


In order to anticipate future Microblaze support with most of these
drivers,
we've been moving towards having all of the Kconfig dependencies on
XILINX_EDK,
rather than XILINX_VIRTEX...

Steve Neuendorffer

> -----Original Message-----
> From: 
> linuxppc-embedded-bounces+stephen=neuendorffer.name@ozlabs.org
>  
> [mailto:linuxppc-embedded-bounces+stephen=neuendorffer.name@oz
labs.org] On Behalf Of Andrei Konovalov
> Sent: Wednesday, June 06, 2007 7:06 AM
> To: spi-devel-general@lists.sourceforge.net
> Cc: linuxppc-embedded@ozlabs.org
> Subject: [PATCH] Simple driver for Xilinx SPI controler.
> 
> +config SPI_XILINX
> +	tristate "Xilinx SPI controller"
> +	depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL
> +	select SPI_BITBANG
> +	help
> +	  This enables using the SPI controller IP from Xilinx 
> EDK in master
> +	  mode. See the DS464, "OPB Serial Peripheral Interface 
> (SPI) (v1.00e)"
> +	  Product Specification document for the hardware details.
>   #
>   # Add new SPI master controllers in alphabetical order 
> above this line
>   #
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile 
> index 5788d86..a2412bd 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC)		
> += mpc52xx_psc_spi.o
>   obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
>   obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
>   obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
> +obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
>   # 	... add above this line ...
> 
>   # SPI protocol drivers (device/link on bus) diff --git 
> a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c new 
> file mode 100644 index 0000000..e5f149f
> --- /dev/null
> +++ b/drivers/spi/xilinx_spi.c
> @@ -0,0 +1,447 @@
> +/*
> + * xilinx_spi.c
> + *
> + * Xilinx SPI controler driver (master mode only)
> + *
> + * Author: MontaVista Software, Inc.
> + *         source@mvista.com
> + *
> + * 2002-2007 (c) MontaVista Software, Inc.  This file is 
> licensed under 
> +the
> + * terms of the GNU General Public License version 2.  This 
> program is 
> +licensed
> + * "as is" without any warranty of any kind, whether express 
> or implied.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/platform_device.h>
> +#include <linux/spi/spi.h>
> +#include <linux/spi/spi_bitbang.h>
> +
> +#include <asm/io.h>
> +#include <syslib/virtex_devices.h>
> +
> +#define XILINX_SPI_NAME "xspi"
> +
> +/* Register definitions as per "OPB Serial Peripheral 
> Interface (SPI) 
> +(v1.00e)
> + * Product Specification", DS464
> + */
> +#define XSPI_CR_OFFSET		0x62	/* 16-bit 
> Control Register */
> +
> +#define XSPI_CR_ENABLE		0x02
> +#define XSPI_CR_MASTER_MODE	0x04
> +#define XSPI_CR_CPOL		0x08
> +#define XSPI_CR_CPHA		0x10
> +#define XSPI_CR_MODE_MASK	(XSPI_CR_CPHA | XSPI_CR_CPOL)
> +#define XSPI_CR_TXFIFO_RESET	0x20
> +#define XSPI_CR_RXFIFO_RESET	0x40
> +#define XSPI_CR_MANUAL_SSELECT	0x80
> +#define XSPI_CR_TRANS_INHIBIT	0x100
> +
> +#define XSPI_SR_OFFSET		0x67	/* 8-bit Status 
> Register */
> +
> +#define XSPI_SR_RX_EMPTY_MASK	0x01	/* Receive FIFO 
> is empty */
> +#define XSPI_SR_RX_FULL_MASK	0x02	/* Receive FIFO is full */
> +#define XSPI_SR_TX_EMPTY_MASK	0x04	/* Transmit 
> FIFO is empty */
> +#define XSPI_SR_TX_FULL_MASK	0x08	/* Transmit FIFO is full */
> +#define XSPI_SR_MODE_FAULT_MASK	0x10	/* Mode fault error */
> +
> +#define XSPI_TXD_OFFSET		0x6b	/* 8-bit Data 
> Transmit Register */
> +#define XSPI_RXD_OFFSET		0x6f	/* 8-bit Data 
> Receive Register */
> +
> +#define XSPI_SSR_OFFSET		0x70	/* 32-bit Slave 
> Select Register */
> +
> +/* Register definitions as per "OPB IPIF (v3.01c) Product 
> +Specification", DS414
> + * IPIF registers are 32 bit
> + */
> +#define XIPIF_V123B_DGIER_OFFSET	0x1c	/* IPIF global 
> int enable reg */
> +#define XIPIF_V123B_GINTR_ENABLE	0x80000000
> +
> +#define XIPIF_V123B_IISR_OFFSET		0x20	/* IPIF 
> interrupt status reg */
> +#define XIPIF_V123B_IIER_OFFSET		0x28	/* IPIF 
> interrupt enable reg */
> +
> +#define XSPI_INTR_MODE_FAULT		0x01	/* Mode fault error */
> +#define XSPI_INTR_SLAVE_MODE_FAULT	0x02	/* Selected as 
> slave while
> +                                                * disabled */
> +#define XSPI_INTR_TX_EMPTY		0x04	/* TxFIFO is empty */
> +#define XSPI_INTR_TX_UNDERRUN		0x08	/* 
> TxFIFO was underrun */
> +#define XSPI_INTR_RX_FULL		0x10	/* RxFIFO is full */
> +#define XSPI_INTR_RX_OVERRUN		0x20	/* RxFIFO was overrun */
> +
> +#define XIPIF_V123B_RESETR_OFFSET	0x40	/* IPIF reset 
> register */
> +#define XIPIF_V123B_RESET_MASK		0x0a	/* the 
> value to write */
> +
> +/* Simple macros to get the code more readable */
> +#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
> +#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
> +#define xspi_out16(addr, value)	out_be16((u16 __iomem 
> *)(addr), (value))
> +#define xspi_out32(addr, value)	out_be32((u32 __iomem 
> *)(addr), (value))
> +
> +struct xilinx_spi {
> +	/* bitbang has to be first */
> +	struct spi_bitbang bitbang;
> +	struct completion done;
> +
> +	u32		regs_phys;	/* phys. address of the 
> control registers */
> +	void __iomem	*regs;		/* virt. address of the 
> control registers */
> +
> +	u32 irq;
> +
> +	u8 *rx_ptr;		/* pointer in the Tx buffer */
> +	const u8 *tx_ptr;	/* pointer in the Rx buffer */
> +	int remaining_bytes;	/* the number of bytes left to 
> transfer */
> +};
> +
> +static void xspi_abort_transfer(u8 __iomem *regs_base) {
> +	/* Deselect the slave on the SPI bus */
> +	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> +
> +	/* Terminate transmit in progress (if any) and Reset 
> the FIFOs */
> +	xspi_out16(regs_base + XSPI_CR_OFFSET,
> +		   xspi_in16(regs_base + XSPI_CR_OFFSET)
> +		   | XSPI_CR_TRANS_INHIBIT | XSPI_CR_TXFIFO_RESET
> +		   | XSPI_CR_RXFIFO_RESET);
> +}
> +
> +static void xspi_init_hw(u8 __iomem *regs_base) {
> +	/* Reset the SPI device */
> +	xspi_out32(regs_base + XIPIF_V123B_RESETR_OFFSET,
> +		   XIPIF_V123B_RESET_MASK);
> +	/* Disable all the interrupts just in case */
> +	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
> +	/* Enable the global IPIF interrupt */
> +	xspi_out32(regs_base + XIPIF_V123B_DGIER_OFFSET,
> +		   XIPIF_V123B_GINTR_ENABLE);
> +	/* Deselect the slave on the SPI bus */
> +	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> +	/* Disable the transmitter, enable Manual Slave Select 
> Assertion,
> +	 * put SPI controller into master mode, and enable it */
> +	xspi_out16(regs_base + XSPI_CR_OFFSET,
> +		   XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
> +		   | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE); }
> +
> +static void xilinx_spi_chipselect(struct spi_device *spi, 
> int is_on) {
> +	struct xilinx_spi *xspi;
> +	u8 __iomem *regs_base;
> +
> +	xspi = spi_master_get_devdata(spi->master);
> +	regs_base = xspi->regs;
> +
> +	if (is_on == BITBANG_CS_INACTIVE) {
> +		/* Deselect the slave on the SPI bus */
> +		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> +	} else if (is_on == BITBANG_CS_ACTIVE) {
> +		/* Set the SPI clock phase and polarity */
> +		u16 cr = xspi_in16(regs_base + XSPI_CR_OFFSET)
> +			 & ~XSPI_CR_MODE_MASK;
> +		if (spi->mode & SPI_CPHA)
> +			cr |= XSPI_CR_CPHA;
> +		if (spi->mode & SPI_CPOL)
> +			cr |= XSPI_CR_CPOL;
> +		xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
> +
> +		/* We do not check spi->max_speed_hz here as 
> the SPI clock
> +		 * frequency is not software programmable (the 
> IP block design
> +		 * parameter)
> +		 */
> +
> +		/* Activate the chip select */
> +		xspi_out32(regs_base + XSPI_SSR_OFFSET,
> +			   ~(0x0001 << spi->chip_select));
> +	}
> +}
> +
> +/* spi_bitbang requires custom setup_transfer() to be 
> defined if there 
> +is a
> + * custom txrx_bufs(). We have nothing to setup here as the SPI IP 
> +block
> + * supports just 8 bits per word, and SPI clock can't be 
> changed in software.
> + * Check for 8 bits per word; speed_hz checking could be 
> added if the 
> +SPI
> + * clock information is available. Chip select delay 
> calculations could 
> +be
> + * added here as soon as bitbang_work() can be made aware of 
> the delay value.
> + */
> +static int xilinx_spi_setup_transfer(struct spi_device *spi,
> +				     struct spi_transfer *t)
> +{
> +	u8 bits_per_word;
> +
> +	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
> +	if (bits_per_word != 8)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +
> +static int xilinx_spi_setup(struct spi_device *spi) {
> +	struct spi_bitbang *bitbang;
> +	struct xilinx_spi *xspi;
> +	int retval;
> +
> +	xspi = spi_master_get_devdata(spi->master);
> +	bitbang = &xspi->bitbang;
> +
> +	if (!spi->bits_per_word)
> +		spi->bits_per_word = 8;
> +
> +	retval = xilinx_spi_setup_transfer(spi, NULL);
> +	if (retval < 0)
> +		return retval;
> +
> +	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
> +		__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
> +		spi->bits_per_word, 0);
> +
> +	return 0;
> +}
> +
> +static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct 
> +spi_transfer *t) {
> +	struct xilinx_spi *xspi;
> +	u8 __iomem *regs_base;
> +	u32 ipif_ier;
> +	u16 cr;
> +	u8 sr;
> +
> +	/* We get here with transmitter inhibited */
> +
> +	xspi = spi_master_get_devdata(spi->master);
> +	regs_base = xspi->regs;
> +
> +	xspi->tx_ptr = t->tx_buf;
> +	xspi->rx_ptr = t->rx_buf;
> +	xspi->remaining_bytes = t->len;
> +	INIT_COMPLETION(xspi->done);
> +
> +	/* Fill the Tx FIFO with as many bytes as possible */
> +	sr = in_8(regs_base + XSPI_SR_OFFSET);
> +	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && 
> xspi->remaining_bytes > 0) {
> +		if (xspi->tx_ptr) {
> +			out_8(regs_base + XSPI_TXD_OFFSET, 
> *xspi->tx_ptr++);
> +		} else {
> +			out_8(regs_base + XSPI_TXD_OFFSET, 0);
> +		}
> +		xspi->remaining_bytes--;
> +		sr = in_8(regs_base + XSPI_SR_OFFSET);
> +	}
> +
> +	/* Enable the transmit empty interrupt, which we use to 
> determine
> +	 * progress on the transmission.
> +	 */
> +	ipif_ier = xspi_in32(regs_base + XIPIF_V123B_IIER_OFFSET);
> +	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET,
> +		   ipif_ier | XSPI_INTR_TX_EMPTY);
> +
> +	/* Start the transfer by not inhibiting the transmitter 
> any longer */
> +	cr = xspi_in16(regs_base + XSPI_CR_OFFSET) & 
> ~XSPI_CR_TRANS_INHIBIT;
> +	xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
> +
> +	wait_for_completion(&xspi->done);
> +
> +	/* Disable the transmit empty interrupt */
> +	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, ipif_ier);
> +
> +	return t->len - xspi->remaining_bytes; }
> +
> +
> +/* This driver supports single master mode only. Hence Tx FIFO Empty
> + * is the only interrupt we care about.
> + * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, 
> and Slave 
> +Mode
> + * Fault are not to happen.
> + */
> +static irqreturn_t xilinx_spi_irq(int irq, void *dev_id) {
> +	struct xilinx_spi *xspi;
> +	u8 __iomem *regs_base;
> +	u32 ipif_isr;
> +
> +	xspi = (struct xilinx_spi *) dev_id;
> +	regs_base = xspi->regs;
> +
> +     	/* Get the IPIF inetrrupts, and clear them 
> immediately */
> +	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
> +	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
> +
> +	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission 
> completed */
> +		u16 cr;
> +		u8 sr;
> +
> +		/* A transmit has just completed. Process 
> received data and
> +		 * check for more data to transmit. Always inhibit the
> +		 * transmitter while the Isr refills the 
> transmit register/FIFO,
> +		 * or make sure it is stopped if we're done.
> +	         */
> +		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
> +		xspi_out16(regs_base + XSPI_CR_OFFSET,
> +			   cr | XSPI_CR_TRANS_INHIBIT);
> +
> +		/* Read out all the data from the Rx FIFO */
> +		sr = in_8(regs_base + XSPI_SR_OFFSET);
> +		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
> +			u8 data;
> +
> +			data = in_8(regs_base + XSPI_RXD_OFFSET);
> +			if (xspi->rx_ptr) {
> +				*xspi->rx_ptr++ = data;
> +			}
> +			sr = in_8(regs_base + XSPI_SR_OFFSET);
> +		}
> +
> +	        /* See if there is more data to send */
> +		if (xspi->remaining_bytes > 0) {
> +			/* sr content is valid here; no need 
> for io_8() */
> +			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
> +				&& xspi->remaining_bytes > 0) {
> +				if (xspi->tx_ptr) {
> +					out_8(regs_base + 
> XSPI_TXD_OFFSET,
> +					      *xspi->tx_ptr++);
> +				} else {
> +					out_8(regs_base + 
> XSPI_TXD_OFFSET, 0);
> +				}
> +				xspi->remaining_bytes--;
> +				sr = in_8(regs_base + XSPI_SR_OFFSET);
> +			}
> +			/* Start the transfer by not inhibiting the
> +			 * transmitter any longer
> +			 */
> +			xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
> +		} else {
> +			/* No more data to send.
> +			 * Indicate the transfer is completed.
> +			 */
> +			complete(&xspi->done);
> +		}
> +	} else {
> +		/* spurious interrupt */
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int __init xilinx_spi_probe(struct platform_device *dev) {
> +	int ret = 0;
> +	struct spi_master *master;
> +	struct xilinx_spi *xspi;
> +	struct xspi_platform_data *pdata;
> +	struct resource *r;
> +
> +	/* Get resources(memory, IRQ) associated with the device */
> +	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
> +
> +	if (master == NULL) {
> +		return -ENOMEM;
> +	}
> +
> +	platform_set_drvdata(dev, master);
> +	pdata = dev->dev.platform_data;
> +
> +	if (pdata == NULL) {
> +		ret = -ENODEV;
> +		goto put_master;
> +	}
> +
> +	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
> +	if (r == NULL) {
> +		ret = -ENODEV;
> +		goto put_master;
> +	}
> +
> +	xspi = spi_master_get_devdata(master);
> +	xspi->bitbang.master = spi_master_get(master);
> +	xspi->bitbang.chipselect = xilinx_spi_chipselect;
> +	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
> +	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
> +	xspi->bitbang.master->setup = xilinx_spi_setup;
> +	init_completion(&xspi->done);
> +
> +	xspi->regs = ioremap(r->start, r->end - r->start + 1);
> +	if (xspi->regs == NULL) {
> +		ret = -ENOMEM;
> +		goto put_master;
> +	}
> +
> +	xspi->irq = platform_get_irq(dev, 0);
> +	if (xspi->irq < 0) {
> +		ret = -ENXIO;
> +		goto unmap_io;
> +	}
> +
> +	master->bus_num = pdata->bus_num;
> +	master->num_chipselect = pdata->num_chipselect;
> +
> +	/* SPI controller initializations */
> +	xspi_init_hw(xspi->regs);
> +
> +	/* Register for SPI Interrupt */
> +	ret = request_irq(xspi->irq, xilinx_spi_irq, 0, 
> XILINX_SPI_NAME, xspi);
> +	if (ret != 0)
> +		goto unmap_io;
> +
> +	ret = spi_bitbang_start(&xspi->bitbang);
> +	if (ret != 0) {
> +		printk(KERN_ALERT "spi_btbang_start FAILED\n");
> +		goto free_irq;
> +	}
> +
> +	printk(KERN_INFO "%s: at 0x%08X mapped to 0x%08X, irq=%d\n",
> +	       dev->dev.bus_id, r->start, (u32)xspi->regs, xspi->irq);
> +
> +	return ret;
> +
> +free_irq:
> +        free_irq(xspi->irq, xspi);
> +unmap_io:
> +	iounmap(xspi->regs);
> +put_master:
> +        spi_master_put(master);
> +	return ret;
> +}
> +
> +static int __devexit xilinx_spi_remove(struct platform_device *dev) {
> +	struct xilinx_spi *xspi;
> +	struct spi_master *master;
> +
> +	master = platform_get_drvdata(dev);
> +	xspi = spi_master_get_devdata(master);
> +
> +	spi_bitbang_stop(&xspi->bitbang);
> +	xspi_abort_transfer(xspi->regs);
> +	free_irq(xspi->irq, xspi);
> +	iounmap(xspi->regs);
> +	platform_set_drvdata(dev, 0);
> +	spi_master_put(xspi->bitbang.master);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver xilinx_spi_driver = {
> +	.probe	= xilinx_spi_probe,
> +	.remove	= __devexit_p(xilinx_spi_remove),
> +	.driver = {
> +		   .name = XILINX_SPI_NAME,
> +		   .owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init xilinx_spi_init(void) {
> +	return platform_driver_register(&xilinx_spi_driver);
> +}
> +
> +static void __exit xilinx_spi_exit(void) {
> +	platform_driver_unregister(&xilinx_spi_driver);
> +}
> +
> +module_init(xilinx_spi_init);
> +module_exit(xilinx_spi_exit);
> +
> +MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>"); 
> +MODULE_DESCRIPTION("Xilinx SPI driver"); MODULE_LICENSE("GPL");
> --
> 1.5.1.1
> 
> 
> _______________________________________________
> Linuxppc-embedded mailing list
> Linuxppc-embedded@ozlabs.org
> https://ozlabs.org/mailman/listinfo/linuxppc-embedded
> 
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 16:03 ` Stephen Neuendorffer
@ 2007-06-06 17:57   ` Andrei Konovalov
  2007-06-06 18:06     ` Stephen Neuendorffer
  0 siblings, 1 reply; 15+ messages in thread
From: Andrei Konovalov @ 2007-06-06 17:57 UTC (permalink / raw)
  To: Stephen Neuendorffer; +Cc: spi-devel-general, linuxppc-embedded

Hi Steve,

Stephen Neuendorffer wrote:
> In order to anticipate future Microblaze support with most of these
> drivers,
> we've been moving towards having all of the Kconfig dependencies on
> XILINX_EDK,

There is no XILINX_EDK in the ko tree.

> rather than XILINX_VIRTEX...

Using XILINX_VIRTEX seems to be a common thing:

> -------- Original Message --------
> Subject: [RFC] Xilinx SystemACE device driver
> Date: Sat, 14 Apr 2007 19:23:14 -0600
> From: Grant Likely <grant.likely@secretlab.ca>
> To: linuxppc-embedded@ozlabs.org, Andrei Konovalov <akonovalov@ru.mvista.com>,	Peter Korsgaard <jacmet@sunsite.dk>,	Rick Moleres <Rick.Moleres@xilinx.com>, Stefan Roese <sr@denx.de>
> 
> Add support for block device access to the Xilinx SystemACE Compact
> flash interface
<snip>
> diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
> index 17ee97f..08ad23c 100644
> --- a/drivers/block/Kconfig
> +++ b/drivers/block/Kconfig
> @@ -453,6 +453,12 @@ config ATA_OVER_ETH
>  	This driver provides Support for ATA over Ethernet block
>  	devices like the Coraid EtherDrive (R) Storage Blade.
>  
> +config XILINX_SYSACE
> +	tristate "Xilinx SystemACE support"
> +	depends on XILINX_VIRTEX
> +	help
> +	  Include support for the Xilinx SystemACE CompactFlash interface
> +

BTW the SystemACE has been successfully tested with ppc440 based
board. So XILINX_VIRTEX doesn't imply ppc405.

On the other side,
UART Lite driver (in the ko tree) doesn't depend on both XILINX_EDK and XILINX_VIRTEX

Couldn't Microblaze port use XILINX_VIRTEX too?
IMHO XILINX_VIRTEX is more general name for something inside the Virtex chip (ppc405
core, Microblaze softcore, IP blocks from EDK or elsewhere).

I've recently added XILINX_EDK to linux-xilinx-26.git repository at source.mvista.com,
but to include the level 1 drivers (aka OS independent drivers) from EDK. Haven't
planned to use XILINX_EDK for drivers not using EDK code.
Here is the relevant snippet from arch/ppc/platforms/4xx/Kconfig
(from linux-xilinx-26.git @ source.mvista.com, ko doesn't have XILINX_EDK and the
rest below):

===8<=====================================
config XILINX_VIRTEX_II_PRO
	bool
	select XILINX_VIRTEX

config XILINX_VIRTEX_4_FX
	bool
	select XILINX_VIRTEX

config XILINX_VIRTEX
	bool

# The options selected by EDK based drivers. Not visible from [menu]config.
config XILINX_EDK
	bool
config XILINX_IPIF_V123B
	bool
config XILINX_FIFO_V200A
	bool
config XILINX_DMA_V300A
	bool
===8<=====================================

If we do something like
   config XILINX_MICROBLAZE
	bool
	select XILINX_VIRTEX
in arch/microblaze/Kconfig
would it work?


Thanks,
Andrei

^ permalink raw reply	[flat|nested] 15+ messages in thread

* RE: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 17:57   ` Andrei Konovalov
@ 2007-06-06 18:06     ` Stephen Neuendorffer
  2007-06-06 19:00       ` Grant Likely
  0 siblings, 1 reply; 15+ messages in thread
From: Stephen Neuendorffer @ 2007-06-06 18:06 UTC (permalink / raw)
  To: Andrei Konovalov
  Cc: spi-devel-general, Wolfgang Reissnegger, linuxppc-embedded


Yes, except that microblazes systems run in non-virtex FPGAs...

It may be that XILINX_EDK is not the right thing either, but it
seemed like the easiest way to have a superset of powerpc and microblaze
systems

What I'm trying to do is (in concert with Grant's recent configuration
changes)
having an easy way for a new board configuration to select the Kconfig
options for
all of the Xilinx drivers.  Namely:

config XILINX_ML300
	bool "Xilinx-ML300"
	select XILINX_VIRTEX_II_PRO
	select EMBEDDEDBOOT
	help
	  This option enables support for the Xilinx ML300 evaluation
board.

config XILINX_VIRTEX_II_PRO
	bool
	select XILINX_VIRTEX

config XILINX_EDK
	bool
	depends on XILINX_VIRTEX || XILINX_MICROBLAZE
	default y

config XILINX_GPIO
	tristate "Xilinx OPB GPIO Support"
	depends on XILINX_EDK
	help
	This option enables support for Xilinx GPIO.


It seems like I'm using XILINX_EDK to mean something different than you
are.
Perhaps it sould be instead:

config XILINX_DRIVERS
	bool
	depends on XILINX_VIRTEX || XILINX_MICROBLAZE
	default y

config XILINX_GPIO_EDK
	tristate "Xilinx EDK-based OPB GPIO Support"
	depends on XILINX_DRIVERS
	select XILINX_EDK
	help
	This option enables support for Xilinx GPIO using EDK OS
independent driver.

config XILINX_GPIO
	tristate "Xilinx OPB GPIO Support"
	depends on XILINX_DRIVERS
	help
	This option enables support for Xilinx GPIO.

Would this work for you?

Steve

> -----Original Message-----
> From: Andrei Konovalov [mailto:akonovalov@ru.mvista.com] 
> Sent: Wednesday, June 06, 2007 10:58 AM
> To: Stephen Neuendorffer
> Cc: spi-devel-general@lists.sourceforge.net; 
> linuxppc-embedded@ozlabs.org; Wolfgang Reissnegger
> Subject: Re: [PATCH] Simple driver for Xilinx SPI controler.
> 
> Hi Steve,
> 
> Stephen Neuendorffer wrote:
> > In order to anticipate future Microblaze support with most of these 
> > drivers, we've been moving towards having all of the Kconfig 
> > dependencies on XILINX_EDK,
> 
> There is no XILINX_EDK in the ko tree.
> 
> > rather than XILINX_VIRTEX...
> 
> Using XILINX_VIRTEX seems to be a common thing:
> 
> > -------- Original Message --------
> > Subject: [RFC] Xilinx SystemACE device driver
> > Date: Sat, 14 Apr 2007 19:23:14 -0600
> > From: Grant Likely <grant.likely@secretlab.ca>
> > To: linuxppc-embedded@ozlabs.org, Andrei Konovalov 
> <akonovalov@ru.mvista.com>,	Peter Korsgaard 
> <jacmet@sunsite.dk>,	Rick Moleres <Rick.Moleres@xilinx.com>, 
> Stefan Roese <sr@denx.de>
> > 
> > Add support for block device access to the Xilinx SystemACE Compact 
> > flash interface
> <snip>
> > diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 
> > 17ee97f..08ad23c 100644
> > --- a/drivers/block/Kconfig
> > +++ b/drivers/block/Kconfig
> > @@ -453,6 +453,12 @@ config ATA_OVER_ETH
> >  	This driver provides Support for ATA over Ethernet block
> >  	devices like the Coraid EtherDrive (R) Storage Blade.
> >  
> > +config XILINX_SYSACE
> > +	tristate "Xilinx SystemACE support"
> > +	depends on XILINX_VIRTEX
> > +	help
> > +	  Include support for the Xilinx SystemACE CompactFlash 
> interface
> > +
> 
> BTW the SystemACE has been successfully tested with ppc440 
> based board. So XILINX_VIRTEX doesn't imply ppc405.
> 
> On the other side,
> UART Lite driver (in the ko tree) doesn't depend on both 
> XILINX_EDK and XILINX_VIRTEX
> 
> Couldn't Microblaze port use XILINX_VIRTEX too?
> IMHO XILINX_VIRTEX is more general name for something inside 
> the Virtex chip (ppc405 core, Microblaze softcore, IP blocks 
> from EDK or elsewhere).
> 
> I've recently added XILINX_EDK to linux-xilinx-26.git 
> repository at source.mvista.com, but to include the level 1 
> drivers (aka OS independent drivers) from EDK. Haven't 
> planned to use XILINX_EDK for drivers not using EDK code.
> Here is the relevant snippet from 
> arch/ppc/platforms/4xx/Kconfig (from linux-xilinx-26.git @ 
> source.mvista.com, ko doesn't have XILINX_EDK and the rest below):
> 
> ===8<=====================================
> config XILINX_VIRTEX_II_PRO
> 	bool
> 	select XILINX_VIRTEX
> 
> config XILINX_VIRTEX_4_FX
> 	bool
> 	select XILINX_VIRTEX
> 
> config XILINX_VIRTEX
> 	bool
> 
> # The options selected by EDK based drivers. Not visible from 
> [menu]config.
> config XILINX_EDK
> 	bool
> config XILINX_IPIF_V123B
> 	bool
> config XILINX_FIFO_V200A
> 	bool
> config XILINX_DMA_V300A
> 	bool
> ===8<=====================================
> 
> If we do something like
>    config XILINX_MICROBLAZE
> 	bool
> 	select XILINX_VIRTEX
> in arch/microblaze/Kconfig
> would it work?
> 
> 
> Thanks,
> Andrei
> 
> 
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 18:06     ` Stephen Neuendorffer
@ 2007-06-06 19:00       ` Grant Likely
       [not found]         ` <fa686aa40706061200u1141fcc9k73b7cc80c07240d1-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  2007-06-06 20:49         ` Wolfgang Reissnegger
  0 siblings, 2 replies; 15+ messages in thread
From: Grant Likely @ 2007-06-06 19:00 UTC (permalink / raw)
  To: Stephen Neuendorffer
  Cc: spi-devel-general, Andrei Konovalov, Wolfgang Reissnegger,
	linuxppc-embedded

On 6/6/07, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
>
> Yes, except that microblazes systems run in non-virtex FPGAs...
>
> It may be that XILINX_EDK is not the right thing either, but it
> seemed like the easiest way to have a superset of powerpc and microblaze
> systems
>
> What I'm trying to do is (in concert with Grant's recent configuration
> changes)
> having an easy way for a new board configuration to select the Kconfig
> options for
> all of the Xilinx drivers.  Namely:
>
> config XILINX_ML300
>         bool "Xilinx-ML300"
>         select XILINX_VIRTEX_II_PRO
>         select EMBEDDEDBOOT
>         help
>           This option enables support for the Xilinx ML300 evaluation
> board.
>
> config XILINX_VIRTEX_II_PRO
>         bool
>         select XILINX_VIRTEX
>
> config XILINX_EDK
>         bool
>         depends on XILINX_VIRTEX || XILINX_MICROBLAZE
>         default y

I don't have any problem with adding XILINX_EDK (or whatever), but
think particular layout is back-assward.  Rather than XILINX_EDK
depending on XILINX_VIRTEX/MICROBLAZE, XILINX_VIRTEX/MICROBLAZE should
'select' XILINX_EDK and you need to drop the 'default y' line.
Otherwise the XILINX_EDK option shows up in non-edk config files.

>
> config XILINX_GPIO
>         tristate "Xilinx OPB GPIO Support"
>         depends on XILINX_EDK
>         help
>         This option enables support for Xilinx GPIO.
>
>
> It seems like I'm using XILINX_EDK to mean something different than you
> are.
> Perhaps it sould be instead:
>
> config XILINX_DRIVERS
>         bool
>         depends on XILINX_VIRTEX || XILINX_MICROBLAZE
>         default y

Hmmm, yes.  XILINX_DRIVERS is probably better.

>
> config XILINX_GPIO_EDK
>         tristate "Xilinx EDK-based OPB GPIO Support"
>         depends on XILINX_DRIVERS
>         select XILINX_EDK
>         help
>         This option enables support for Xilinx GPIO using EDK OS
> independent driver.

Why still have XILINX_EDK?  I thought you meant replace XILINX_EDK
with XILINX_DRIVERS.

Cheers,
g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found]         ` <fa686aa40706061200u1141fcc9k73b7cc80c07240d1-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2007-06-06 19:06           ` Stephen Neuendorffer
  2007-06-06 19:55             ` Grant Likely
  0 siblings, 1 reply; 15+ messages in thread
From: Stephen Neuendorffer @ 2007-06-06 19:06 UTC (permalink / raw)
  To: Grant Likely
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	Andrei Konovalov, Wolfgang Reissnegger,
	linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A


> > config XILINX_EDK
> >         bool
> >         depends on XILINX_VIRTEX || XILINX_MICROBLAZE
> >         default y
> 
> I don't have any problem with adding XILINX_EDK (or 
> whatever), but think particular layout is back-assward.  
> Rather than XILINX_EDK depending on XILINX_VIRTEX/MICROBLAZE, 
> XILINX_VIRTEX/MICROBLAZE should 'select' XILINX_EDK and you 
> need to drop the 'default y' line.
> Otherwise the XILINX_EDK option shows up in non-edk config files.

Good point...
 
> Why still have XILINX_EDK?  I thought you meant replace 
> XILINX_EDK with XILINX_DRIVERS.

Yes, that's what I was suggesting.
But I think that Andrei still wants to use XILINX_EDK to pull in the
stuff in drivers/xilinx_common.

# The Xilinx OS common code 
obj-$(CONFIG_XILINX_EDK) += xbasic_types.o xpacket_fifo_l_v2_00_a.o \
			    xpacket_fifo_v2_00_a.o xversion.o \
			    xdma_channel.o xdma_channel_sg.o

Which you wouldn't want if you were able to build a kernel completely
from 'non-edk' drivers.

Steve



-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 19:06           ` Stephen Neuendorffer
@ 2007-06-06 19:55             ` Grant Likely
  0 siblings, 0 replies; 15+ messages in thread
From: Grant Likely @ 2007-06-06 19:55 UTC (permalink / raw)
  To: Stephen Neuendorffer
  Cc: spi-devel-general, Andrei Konovalov, Wolfgang Reissnegger,
	linuxppc-embedded

On 6/6/07, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
> > Why still have XILINX_EDK?  I thought you meant replace
> > XILINX_EDK with XILINX_DRIVERS.
>
> Yes, that's what I was suggesting.
> But I think that Andrei still wants to use XILINX_EDK to pull in the
> stuff in drivers/xilinx_common.
>
> # The Xilinx OS common code
> obj-$(CONFIG_XILINX_EDK) += xbasic_types.o xpacket_fifo_l_v2_00_a.o \
>                             xpacket_fifo_v2_00_a.o xversion.o \
>                             xdma_channel.o xdma_channel_sg.o
>
> Which you wouldn't want if you were able to build a kernel completely
> from 'non-edk' drivers.

Fair enough, but that's only an issue for vendor trees.  None of this
code will be going into mainline.  Any of it that does get into good
shape should probably go under drivers/misc, and have the drivers
select only the pieces that are needed (with finer-grain CONFIG_
items).  A blanked CONFIG_XILINX_EDK for random stuff is not a good
idea.

Cheers,
g.

>
> Steve
>
>
>


-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 19:00       ` Grant Likely
       [not found]         ` <fa686aa40706061200u1141fcc9k73b7cc80c07240d1-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2007-06-06 20:49         ` Wolfgang Reissnegger
  2007-06-06 20:55           ` Grant Likely
  1 sibling, 1 reply; 15+ messages in thread
From: Wolfgang Reissnegger @ 2007-06-06 20:49 UTC (permalink / raw)
  To: Grant Likely
  Cc: spi-devel-general, Stephen Neuendorffer, linuxppc-embedded,
	Andrei Konovalov, Wolfgang Reissnegger

Grant,

I'm in the process of merging your virtex-dev tree into our
linux-2.6-xlnx tree. I'll be taking care of the necessary changes. They
will show up on the Xilinx git repository soon.

Thanks,
   Wolfgang

Grant Likely wrote:
> On 6/6/07, Stephen Neuendorffer <stephen.neuendorffer@xilinx.com> wrote:
>>
>> Yes, except that microblazes systems run in non-virtex FPGAs...
>>
>> It may be that XILINX_EDK is not the right thing either, but it
>> seemed like the easiest way to have a superset of powerpc and microblaze
>> systems
>>
>> What I'm trying to do is (in concert with Grant's recent configuration
>> changes)
>> having an easy way for a new board configuration to select the Kconfig
>> options for
>> all of the Xilinx drivers.  Namely:
>>
>> config XILINX_ML300
>>         bool "Xilinx-ML300"
>>         select XILINX_VIRTEX_II_PRO
>>         select EMBEDDEDBOOT
>>         help
>>           This option enables support for the Xilinx ML300 evaluation
>> board.
>>
>> config XILINX_VIRTEX_II_PRO
>>         bool
>>         select XILINX_VIRTEX
>>
>> config XILINX_EDK
>>         bool
>>         depends on XILINX_VIRTEX || XILINX_MICROBLAZE
>>         default y
> 
> I don't have any problem with adding XILINX_EDK (or whatever), but
> think particular layout is back-assward.  Rather than XILINX_EDK
> depending on XILINX_VIRTEX/MICROBLAZE, XILINX_VIRTEX/MICROBLAZE should
> 'select' XILINX_EDK and you need to drop the 'default y' line.
> Otherwise the XILINX_EDK option shows up in non-edk config files.
> 
>>
>> config XILINX_GPIO
>>         tristate "Xilinx OPB GPIO Support"
>>         depends on XILINX_EDK
>>         help
>>         This option enables support for Xilinx GPIO.
>>
>>
>> It seems like I'm using XILINX_EDK to mean something different than you
>> are.
>> Perhaps it sould be instead:
>>
>> config XILINX_DRIVERS
>>         bool
>>         depends on XILINX_VIRTEX || XILINX_MICROBLAZE
>>         default y
> 
> Hmmm, yes.  XILINX_DRIVERS is probably better.
> 
>>
>> config XILINX_GPIO_EDK
>>         tristate "Xilinx EDK-based OPB GPIO Support"
>>         depends on XILINX_DRIVERS
>>         select XILINX_EDK
>>         help
>>         This option enables support for Xilinx GPIO using EDK OS
>> independent driver.
> 
> Why still have XILINX_EDK?  I thought you meant replace XILINX_EDK
> with XILINX_DRIVERS.
> 
> Cheers,
> g.
> 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-06 20:49         ` Wolfgang Reissnegger
@ 2007-06-06 20:55           ` Grant Likely
  0 siblings, 0 replies; 15+ messages in thread
From: Grant Likely @ 2007-06-06 20:55 UTC (permalink / raw)
  To: Wolfgang Reissnegger
  Cc: spi-devel-general, Stephen Neuendorffer, linuxppc-embedded,
	Andrei Konovalov, Wolfgang Reissnegger

On 6/6/07, Wolfgang Reissnegger <wolfgang.reissnegger@xilinx.com> wrote:
> Grant,
>
> I'm in the process of merging your virtex-dev tree into our
> linux-2.6-xlnx tree. I'll be taking care of the necessary changes. They
> will show up on the Xilinx git repository soon.

Don't forget to send patches to the list for review so we can get this
stuff into mainline.

g.

-- 
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
grant.likely@secretlab.ca
(403) 399-0195

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found] ` <4666BF47.8080103-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
@ 2007-06-07  5:09   ` David Brownell
  2007-06-07 18:39     ` [spi-devel-general] " Andrei Konovalov
       [not found]     ` <200706062209.09731.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 2 replies; 15+ messages in thread
From: David Brownell @ 2007-06-07  5:09 UTC (permalink / raw)
  To: Andrei Konovalov
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

On Wednesday 06 June 2007, Andrei Konovalov wrote:
> 
> Would be nice to get this driver into mainline.
> Reviews and comments are welcome.

I'll ignore the Kconfig flamage ... ;)


> --- /dev/null
> +++ b/drivers/spi/xilinx_spi.c
> @@ -0,0 +1,447 @@
> +/*
> + * xilinx_spi.c
> + *
> + * Xilinx SPI controler driver (master mode only)
> + *
> + * Author: MontaVista Software, Inc.
> + *         source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org
> + *
> + * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
> + * terms of the GNU General Public License version 2.  This program is licensed
> + * "as is" without any warranty of any kind, whether express or implied.
> + */
> +
> +
> +/* Simple macros to get the code more readable */
> +#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
> +#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
> +#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
> +#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))

I'm rather used to seeing I/O addressses passed around as "void __iomem *"
so those sorts of cast are not needed...  :)


> +
> +static void xspi_abort_transfer(u8 __iomem *regs_base)
> +{

You should not need an abort primitive.  This is called only
in the remove-controller path.  By the time it's called,
every child spi_device on this bus segment should have been
removed ... which means any spi_driver attached to that
device has already returned from its remove() method, which
in turn means that there will be no spi_message objects in
flight from any of those drivers.


> +static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
> +{
> +	struct xilinx_spi *xspi;
> +	u8 __iomem *regs_base;
> +
> +	xspi = spi_master_get_devdata(spi->master);
> +	regs_base = xspi->regs;
> +
> +	if (is_on == BITBANG_CS_INACTIVE) {
> +		/* Deselect the slave on the SPI bus */
> +		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);

I take it you can't support SPI_CS_HIGH??


> +/* spi_bitbang requires custom setup_transfer() to be defined if there is a
> + * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
> + * supports just 8 bits per word, and SPI clock can't be changed in software.
> + * Check for 8 bits per word; speed_hz checking could be added if the SPI
> + * clock information is available. Chip select delay calculations could be
> + * added here as soon as bitbang_work() can be made aware of the delay value.
> + */
> +static int xilinx_spi_setup_transfer(struct spi_device *spi,
> +				     struct spi_transfer *t)
> +{
> +	u8 bits_per_word;
> +
> +	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
> +	if (bits_per_word != 8)
> +		return -EINVAL;

Speed checking *SHOULD* be added; the clock info can be platform data.

It would make trouble if a driver needed to turn the clock down to,
say, 400 KHz for some operation, and the controller said "yes" but
kept it at 25 MHz or whatever.  It's OK if the driver is told that
400 KHz can't happen though -- it's possible to recover from that.

(Although in practice it's best to have the transfer method do
the error checking, so that messages that will fail do so before
they are allowed to enter the I/O queue.)

ISTR you may need to delegate to the default method here too, but
it's been a while since I poked at that level and the issue might
not apply to this particular driver config.



> +
> +	return 0;
> +}
> +
> +
> +static int xilinx_spi_setup(struct spi_device *spi)
> +{
> +	struct spi_bitbang *bitbang;
> +	struct xilinx_spi *xspi;
> +	int retval;
> +
> +	xspi = spi_master_get_devdata(spi->master);
> +	bitbang = &xspi->bitbang;

You need to verify ALL the input parameters.  In particular,
mask spi->mode against all the values this driver recognizes
and supports.  If you don't support SPI_LSB_FIRST it's a bug
if setup() succeeds after setting that.  Same thing with all
other bits defined today (SPI_3WIRE, SPI_CS_HIGH) and in the
future... 

For an example, see the atmel_spi.c driver and MODEBITS.
There's a patch in MM that makes all the SPI controller
drivers have analagous tests ... and for bitbang there's
a patch adding spi_bitbang.mode flags to declare any
extra spi->mode flags that should be accepted.


> +	...
> +	
> +static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
> +{
> +	struct xilinx_spi *xspi;
> +	u8 __iomem *regs_base;
> +	u32 ipif_isr;
> +
> +	xspi = (struct xilinx_spi *) dev_id;
> +	regs_base = xspi->regs;
> +
> +     	/* Get the IPIF inetrrupts, and clear them immediately */

Spell checkers will tell you this is "interrupts" ... ;)


> +	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
> +	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
> +
> +	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
> +		u16 cr;
> +		u8 sr;
> +
> +		/* A transmit has just completed. Process received data and
> +		 * check for more data to transmit. Always inhibit the
> +		 * transmitter while the Isr refills the transmit register/FIFO,
> +		 * or make sure it is stopped if we're done.
> +	         */
> +		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
> +		xspi_out16(regs_base + XSPI_CR_OFFSET,
> +			   cr | XSPI_CR_TRANS_INHIBIT);
> +
> +		/* Read out all the data from the Rx FIFO */
> +		sr = in_8(regs_base + XSPI_SR_OFFSET);
> +		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
> +			u8 data;
> +
> +			data = in_8(regs_base + XSPI_RXD_OFFSET);
> +			if (xspi->rx_ptr) {
> +				*xspi->rx_ptr++ = data;
> +			}
> +			sr = in_8(regs_base + XSPI_SR_OFFSET);
> +		}
> +
> +	        /* See if there is more data to send */
> +		if (xspi->remaining_bytes > 0) {
> +			/* sr content is valid here; no need for io_8() */
> +			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
> +				&& xspi->remaining_bytes > 0) {
> +				if (xspi->tx_ptr) {
> +					out_8(regs_base + XSPI_TXD_OFFSET,
> +					      *xspi->tx_ptr++);
> +				} else {
> +					out_8(regs_base + XSPI_TXD_OFFSET, 0);
> +				}

This duplicates the loop in txrx_bufs(); that's bad style.
Have one routine holding the shared code.


> +static int __init xilinx_spi_probe(struct platform_device *dev)
> +{
> +	int ret = 0;
> +	struct spi_master *master;
> +	struct xilinx_spi *xspi;
> +	struct xspi_platform_data *pdata;
> +	struct resource *r;
> +
> +	/* Get resources(memory, IRQ) associated with the device */
> +	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
> +
> +	if (master == NULL) {
> +		return -ENOMEM;
> +	}
> +
> +	platform_set_drvdata(dev, master);
> +	pdata = dev->dev.platform_data;
> +
> +	if (pdata == NULL) {
> +		ret = -ENODEV;
> +		goto put_master;
> +	}
> +
> +	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
> +	if (r == NULL) {
> +		ret = -ENODEV;
> +		goto put_master;
> +	}
> +
> +	xspi = spi_master_get_devdata(master);
> +	xspi->bitbang.master = spi_master_get(master);
> +	xspi->bitbang.chipselect = xilinx_spi_chipselect;
> +	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
> +	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
> +	xspi->bitbang.master->setup = xilinx_spi_setup;
> +	init_completion(&xspi->done);
> +
> +	xspi->regs = ioremap(r->start, r->end - r->start + 1);

Strictly speaking a request_region() should precede the ioremap,
but a lot of folk don't bother.  However, lacking that I'd put
the request_irq() earlier, since that will be the only resource
providing any guard against another driver sharing the hardware.


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [spi-devel-general] [PATCH] Simple driver for Xilinx SPI controler.
  2007-06-07  5:09   ` David Brownell
@ 2007-06-07 18:39     ` Andrei Konovalov
       [not found]       ` <466850FB.5030505-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
       [not found]     ` <200706062209.09731.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  1 sibling, 1 reply; 15+ messages in thread
From: Andrei Konovalov @ 2007-06-07 18:39 UTC (permalink / raw)
  To: David Brownell; +Cc: spi-devel-general, linuxppc-embedded

Hi David,

Thanks for reviewing this stuff!

David Brownell wrote:
> On Wednesday 06 June 2007, Andrei Konovalov wrote:
>> Would be nice to get this driver into mainline.
>> Reviews and comments are welcome.
> 
> I'll ignore the Kconfig flamage ... ;)
> 
> 
>> --- /dev/null
>> +++ b/drivers/spi/xilinx_spi.c
>> @@ -0,0 +1,447 @@
>> +/*
>> + * xilinx_spi.c
>> + *
>> + * Xilinx SPI controler driver (master mode only)
>> + *
>> + * Author: MontaVista Software, Inc.
>> + *         source@mvista.com
>> + *
>> + * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
>> + * terms of the GNU General Public License version 2.  This program is licensed
>> + * "as is" without any warranty of any kind, whether express or implied.
>> + */
>> +
>> +
>> +/* Simple macros to get the code more readable */
>> +#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
>> +#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
>> +#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
>> +#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))
> 
> I'm rather used to seeing I/O addressses passed around as "void __iomem *"
> so those sorts of cast are not needed...  :)

I've decided to make the base_address (u8 __iomem *) to make it easier to
add the register offsets to the base. Like that:

static void xspi_init_hw(u8 __iomem *regs_base)
{
	/* Reset the SPI device */
	xspi_out32(regs_base + XIPIF_V123B_RESETR_OFFSET,
		   XIPIF_V123B_RESET_MASK);
...

Without the cast, out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET, XIPIF_V123B_RESET_MASK)
produces a warning.

Another solution could be

#define XSPI_REG(base, offset) (void *)((base) + (offset))
and
	/* Reset the SPI device */
	out_be32(XSPI_REG(regs_base, XIPIF_V123B_RESETR_OFFSET),
	         XIPIF_V123B_RESET_MASK);

Is it better?

>> +
>> +static void xspi_abort_transfer(u8 __iomem *regs_base)
>> +{
> 
> You should not need an abort primitive.  This is called only
> in the remove-controller path.  By the time it's called,
> every child spi_device on this bus segment should have been
> removed ... which means any spi_driver attached to that
> device has already returned from its remove() method, which
> in turn means that there will be no spi_message objects in
> flight from any of those drivers.

I am paranoid enough not to rely 100% on the spi_device's and the spi_bitbang
doing all that :)
Agreed, xspi_abort_transfer is bad name here. But I still would like to
leave these two writes:

+	/* Deselect the slave on the SPI bus */
+	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	/* Disable the transmitter */
+	xspi_out16(regs_base + XSPI_CR_OFFSET,
+		   XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT);

in xilinx_spi_remove(). Just in case :)

>> +static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
>> +{
>> +	struct xilinx_spi *xspi;
>> +	u8 __iomem *regs_base;
>> +
>> +	xspi = spi_master_get_devdata(spi->master);
>> +	regs_base = xspi->regs;
>> +
>> +	if (is_on == BITBANG_CS_INACTIVE) {
>> +		/* Deselect the slave on the SPI bus */
>> +		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> 
> I take it you can't support SPI_CS_HIGH??

There is no clear indication in the SPI controller docs that SPI_CS_HIGH
would work. I suspect it would as we don't use the ability of the controller
to generate the chip selects automatically (the auto mode doesn't support SPI_CS_HIGH).
But it is more safe to advertise SPI_CS_HIGH is not supported.

>> +/* spi_bitbang requires custom setup_transfer() to be defined if there is a
>> + * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
>> + * supports just 8 bits per word, and SPI clock can't be changed in software.
>> + * Check for 8 bits per word; speed_hz checking could be added if the SPI
>> + * clock information is available. Chip select delay calculations could be
>> + * added here as soon as bitbang_work() can be made aware of the delay value.
>> + */
>> +static int xilinx_spi_setup_transfer(struct spi_device *spi,
>> +				     struct spi_transfer *t)
>> +{
>> +	u8 bits_per_word;
>> +
>> +	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
>> +	if (bits_per_word != 8)
>> +		return -EINVAL;
> 
> Speed checking *SHOULD* be added; the clock info can be platform data.
> 
> It would make trouble if a driver needed to turn the clock down to,
> say, 400 KHz for some operation, and the controller said "yes" but
> kept it at 25 MHz or whatever.  It's OK if the driver is told that
> 400 KHz can't happen though -- it's possible to recover from that.

OK

> (Although in practice it's best to have the transfer method do
> the error checking, so that messages that will fail do so before
> they are allowed to enter the I/O queue.)
> 
> ISTR you may need to delegate to the default method here too, but
> it's been a while since I poked at that level and the issue might
> not apply to this particular driver config.
> 
> 
> 
>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int xilinx_spi_setup(struct spi_device *spi)
>> +{
>> +	struct spi_bitbang *bitbang;
>> +	struct xilinx_spi *xspi;
>> +	int retval;
>> +
>> +	xspi = spi_master_get_devdata(spi->master);
>> +	bitbang = &xspi->bitbang;
> 
> You need to verify ALL the input parameters.  In particular,
> mask spi->mode against all the values this driver recognizes
> and supports.  If you don't support SPI_LSB_FIRST it's a bug
> if setup() succeeds after setting that.  Same thing with all
> other bits defined today (SPI_3WIRE, SPI_CS_HIGH) and in the
> future... 
> 
> For an example, see the atmel_spi.c driver and MODEBITS.
> There's a patch in MM that makes all the SPI controller
> drivers have analagous tests ... and for bitbang there's
> a patch adding spi_bitbang.mode flags to declare any
> extra spi->mode flags that should be accepted.

OK. Thanks.

>> +	...
>> +	
>> +static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
>> +{
>> +	struct xilinx_spi *xspi;
>> +	u8 __iomem *regs_base;
>> +	u32 ipif_isr;
>> +
>> +	xspi = (struct xilinx_spi *) dev_id;
>> +	regs_base = xspi->regs;
>> +
>> +     	/* Get the IPIF inetrrupts, and clear them immediately */
> 
> Spell checkers will tell you this is "interrupts" ... ;)

Oops...

>> +	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
>> +	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
>> +
>> +	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
>> +		u16 cr;
>> +		u8 sr;
>> +
>> +		/* A transmit has just completed. Process received data and
>> +		 * check for more data to transmit. Always inhibit the
>> +		 * transmitter while the Isr refills the transmit register/FIFO,
>> +		 * or make sure it is stopped if we're done.
>> +	         */
>> +		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
>> +		xspi_out16(regs_base + XSPI_CR_OFFSET,
>> +			   cr | XSPI_CR_TRANS_INHIBIT);
>> +
>> +		/* Read out all the data from the Rx FIFO */
>> +		sr = in_8(regs_base + XSPI_SR_OFFSET);
>> +		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
>> +			u8 data;
>> +
>> +			data = in_8(regs_base + XSPI_RXD_OFFSET);
>> +			if (xspi->rx_ptr) {
>> +				*xspi->rx_ptr++ = data;
>> +			}
>> +			sr = in_8(regs_base + XSPI_SR_OFFSET);
>> +		}
>> +
>> +	        /* See if there is more data to send */
>> +		if (xspi->remaining_bytes > 0) {
>> +			/* sr content is valid here; no need for io_8() */
>> +			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
>> +				&& xspi->remaining_bytes > 0) {
>> +				if (xspi->tx_ptr) {
>> +					out_8(regs_base + XSPI_TXD_OFFSET,
>> +					      *xspi->tx_ptr++);
>> +				} else {
>> +					out_8(regs_base + XSPI_TXD_OFFSET, 0);
>> +				}
> 
> This duplicates the loop in txrx_bufs(); that's bad style.
> Have one routine holding the shared code.

OK

>> +static int __init xilinx_spi_probe(struct platform_device *dev)
>> +{
>> +	int ret = 0;
>> +	struct spi_master *master;
>> +	struct xilinx_spi *xspi;
>> +	struct xspi_platform_data *pdata;
>> +	struct resource *r;
>> +
>> +	/* Get resources(memory, IRQ) associated with the device */
>> +	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
>> +
>> +	if (master == NULL) {
>> +		return -ENOMEM;
>> +	}
>> +
>> +	platform_set_drvdata(dev, master);
>> +	pdata = dev->dev.platform_data;
>> +
>> +	if (pdata == NULL) {
>> +		ret = -ENODEV;
>> +		goto put_master;
>> +	}
>> +
>> +	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
>> +	if (r == NULL) {
>> +		ret = -ENODEV;
>> +		goto put_master;
>> +	}
>> +
>> +	xspi = spi_master_get_devdata(master);
>> +	xspi->bitbang.master = spi_master_get(master);
>> +	xspi->bitbang.chipselect = xilinx_spi_chipselect;
>> +	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
>> +	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
>> +	xspi->bitbang.master->setup = xilinx_spi_setup;
>> +	init_completion(&xspi->done);
>> +
>> +	xspi->regs = ioremap(r->start, r->end - r->start + 1);
> 
> Strictly speaking a request_region() should precede the ioremap,
> but a lot of folk don't bother.  However, lacking that I'd put
> the request_irq() earlier, since that will be the only resource
> providing any guard against another driver sharing the hardware.

Will add request_region(). I was confused by platform_device_register()
doing something with the resources (contrary to the "plain" device_register()).


Thanks,
Andrei

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found]       ` <466850FB.5030505-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
@ 2007-06-07 19:21         ` David Brownell
  0 siblings, 0 replies; 15+ messages in thread
From: David Brownell @ 2007-06-07 19:21 UTC (permalink / raw)
  To: Andrei Konovalov
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

On Thursday 07 June 2007, Andrei Konovalov wrote:

> >> +/* Simple macros to get the code more readable */
> >> +#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
> >> +#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
> >> +#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
> >> +#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))
> > 
> > I'm rather used to seeing I/O addressses passed around as "void __iomem *"
> > so those sorts of cast are not needed...  :)
> 
> I've decided to make the base_address (u8 __iomem *) to make it easier to
> add the register offsets to the base. Like that:

Adding register offsets works with "void __iomem *" too ...


> > You should not need an abort primitive.  ...
> 
> I am paranoid enough not to rely 100% on the spi_device's and the spi_bitbang
> doing all that :)
> Agreed, xspi_abort_transfer is bad name here. But I still would like to
> leave these two writes:
> 
> +	/* Deselect the slave on the SPI bus */
> +	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> +	/* Disable the transmitter */
> +	xspi_out16(regs_base + XSPI_CR_OFFSET,
> +		   XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT);
> 
> in xilinx_spi_remove(). Just in case :)

What happens when you add a BUG_ON to catch the device still being active?

Really, you need to rely on the rest of the system working correctly.



> > I take it you can't support SPI_CS_HIGH??
> 
> There is no clear indication in the SPI controller docs that SPI_CS_HIGH
> would work. I suspect it would as we don't use the ability of the controller
> to generate the chip selects automatically (the auto mode doesn't support SPI_CS_HIGH).
> But it is more safe to advertise SPI_CS_HIGH is not supported.

If you don't explicitly support it, it's unlikely to work.  :)

- Dave


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found]     ` <200706062209.09731.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-06-09 16:58       ` Andrei Konovalov
       [not found]         ` <466ADC4B.4050806-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: Andrei Konovalov @ 2007-06-09 16:58 UTC (permalink / raw)
  To: David Brownell
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

[-- Attachment #1: Type: text/plain, Size: 19810 bytes --]

Hi David,

In the bottom there is the interdiff against the previous version of the patch.
The new patch is attached to this message.

Thanks,
Andrei

David Brownell wrote:
> On Wednesday 06 June 2007, Andrei Konovalov wrote:
>> Would be nice to get this driver into mainline.
>> Reviews and comments are welcome.
> 
> I'll ignore the Kconfig flamage ... ;)

I've left untouched XILINX_VIRTEX in

+config SPI_XILINX
+       tristate "Xilinx SPI controller"
+       depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL

as in the current kernel there is no other symbol to "depend on".
If / when someone comes with a separate patch to add XILINX_DRIVERS
or whatever suitable this "depends on" line can be revisited.

>> --- /dev/null
>> +++ b/drivers/spi/xilinx_spi.c
>> @@ -0,0 +1,447 @@
>> +/*
>> + * xilinx_spi.c
>> + *
>> + * Xilinx SPI controler driver (master mode only)
>> + *
>> + * Author: MontaVista Software, Inc.
>> + *         source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org
>> + *
>> + * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
>> + * terms of the GNU General Public License version 2.  This program is licensed
>> + * "as is" without any warranty of any kind, whether express or implied.
>> + */
>> +
>> +
>> +/* Simple macros to get the code more readable */
>> +#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
>> +#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
>> +#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
>> +#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))
> 
> I'm rather used to seeing I/O addressses passed around as "void __iomem *"
> so those sorts of cast are not needed...  :)

The macros has been removed, using "void __iomem *" now.

>> +
>> +static void xspi_abort_transfer(u8 __iomem *regs_base)
>> +{
> 
> You should not need an abort primitive.  This is called only
> in the remove-controller path.  By the time it's called,
> every child spi_device on this bus segment should have been
> removed ... which means any spi_driver attached to that
> device has already returned from its remove() method, which
> in turn means that there will be no spi_message objects in
> flight from any of those drivers.

xspi_abort_transfer() removed

>> +static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
>> +{
>> +	struct xilinx_spi *xspi;
>> +	u8 __iomem *regs_base;
>> +
>> +	xspi = spi_master_get_devdata(spi->master);
>> +	regs_base = xspi->regs;
>> +
>> +	if (is_on == BITBANG_CS_INACTIVE) {
>> +		/* Deselect the slave on the SPI bus */
>> +		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
> 
> I take it you can't support SPI_CS_HIGH??

Yes.

>> +/* spi_bitbang requires custom setup_transfer() to be defined if there is a
>> + * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
>> + * supports just 8 bits per word, and SPI clock can't be changed in software.
>> + * Check for 8 bits per word; speed_hz checking could be added if the SPI
>> + * clock information is available. Chip select delay calculations could be
>> + * added here as soon as bitbang_work() can be made aware of the delay value.
>> + */
>> +static int xilinx_spi_setup_transfer(struct spi_device *spi,
>> +				     struct spi_transfer *t)
>> +{
>> +	u8 bits_per_word;
>> +
>> +	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
>> +	if (bits_per_word != 8)
>> +		return -EINVAL;
> 
> Speed checking *SHOULD* be added; the clock info can be platform data.

Speed checking has be added.

The current problem is that EDK (the tool to create the FPGA "image"
thus configuring what devices are in it) doesn't put the SPI clock
information into any *.c or *.h file it generates. I've tried to
address that in the "enable SPI driver for ML300" patch, but this
doesn't affect the driver patch.

> (Although in practice it's best to have the transfer method do
> the error checking, so that messages that will fail do so before
> they are allowed to enter the I/O queue.)
> 
> ISTR you may need to delegate to the default method here too, but
> it's been a while since I poked at that level and the issue might
> not apply to this particular driver config.

As far as I can tell, for the drivers relying on spi_bitbabg
the setup_transfer() method is the right place for the speed
checking.

>> +
>> +	return 0;
>> +}
>> +
>> +
>> +static int xilinx_spi_setup(struct spi_device *spi)
>> +{
>> +	struct spi_bitbang *bitbang;
>> +	struct xilinx_spi *xspi;
>> +	int retval;
>> +
>> +	xspi = spi_master_get_devdata(spi->master);
>> +	bitbang = &xspi->bitbang;
> 
> You need to verify ALL the input parameters.  In particular,
> mask spi->mode against all the values this driver recognizes
> and supports.  If you don't support SPI_LSB_FIRST it's a bug
> if setup() succeeds after setting that.  Same thing with all
> other bits defined today (SPI_3WIRE, SPI_CS_HIGH) and in the
> future... 

Done

>> +	...
>> +	
>> +static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
>> +{
>> +	struct xilinx_spi *xspi;
>> +	u8 __iomem *regs_base;
>> +	u32 ipif_isr;
>> +
>> +	xspi = (struct xilinx_spi *) dev_id;
>> +	regs_base = xspi->regs;
>> +
>> +     	/* Get the IPIF inetrrupts, and clear them immediately */
> 
> Spell checkers will tell you this is "interrupts" ... ;)

Fixed

>> +	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
>> +	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
>> +
>> +	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
>> +		u16 cr;
>> +		u8 sr;
>> +
>> +		/* A transmit has just completed. Process received data and
>> +		 * check for more data to transmit. Always inhibit the
>> +		 * transmitter while the Isr refills the transmit register/FIFO,
>> +		 * or make sure it is stopped if we're done.
>> +	         */
>> +		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
>> +		xspi_out16(regs_base + XSPI_CR_OFFSET,
>> +			   cr | XSPI_CR_TRANS_INHIBIT);
>> +
>> +		/* Read out all the data from the Rx FIFO */
>> +		sr = in_8(regs_base + XSPI_SR_OFFSET);
>> +		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
>> +			u8 data;
>> +
>> +			data = in_8(regs_base + XSPI_RXD_OFFSET);
>> +			if (xspi->rx_ptr) {
>> +				*xspi->rx_ptr++ = data;
>> +			}
>> +			sr = in_8(regs_base + XSPI_SR_OFFSET);
>> +		}
>> +
>> +	        /* See if there is more data to send */
>> +		if (xspi->remaining_bytes > 0) {
>> +			/* sr content is valid here; no need for io_8() */
>> +			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
>> +				&& xspi->remaining_bytes > 0) {
>> +				if (xspi->tx_ptr) {
>> +					out_8(regs_base + XSPI_TXD_OFFSET,
>> +					      *xspi->tx_ptr++);
>> +				} else {
>> +					out_8(regs_base + XSPI_TXD_OFFSET, 0);
>> +				}
> 
> This duplicates the loop in txrx_bufs(); that's bad style.
> Have one routine holding the shared code.

Moved into separate function.

>> +static int __init xilinx_spi_probe(struct platform_device *dev)
>> +{
>> +	int ret = 0;
>> +	struct spi_master *master;
>> +	struct xilinx_spi *xspi;
>> +	struct xspi_platform_data *pdata;
>> +	struct resource *r;
>> +
>> +	/* Get resources(memory, IRQ) associated with the device */
>> +	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
>> +
>> +	if (master == NULL) {
>> +		return -ENOMEM;
>> +	}
>> +
>> +	platform_set_drvdata(dev, master);
>> +	pdata = dev->dev.platform_data;
>> +
>> +	if (pdata == NULL) {
>> +		ret = -ENODEV;
>> +		goto put_master;
>> +	}
>> +
>> +	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
>> +	if (r == NULL) {
>> +		ret = -ENODEV;
>> +		goto put_master;
>> +	}
>> +
>> +	xspi = spi_master_get_devdata(master);
>> +	xspi->bitbang.master = spi_master_get(master);
>> +	xspi->bitbang.chipselect = xilinx_spi_chipselect;
>> +	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
>> +	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
>> +	xspi->bitbang.master->setup = xilinx_spi_setup;
>> +	init_completion(&xspi->done);
>> +
>> +	xspi->regs = ioremap(r->start, r->end - r->start + 1);
> 
> Strictly speaking a request_region() should precede the ioremap,
> but a lot of folk don't bother.  However, lacking that I'd put
> the request_irq() earlier, since that will be the only resource
> providing any guard against another driver sharing the hardware.

request_region() added

Here is the interdiff against the previous version of the patch:

diff -u b/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h
--- b/arch/ppc/syslib/virtex_devices.h
+++ b/arch/ppc/syslib/virtex_devices.h
@@ -49,6 +49,7 @@
  struct xspi_platform_data {
  	s16 bus_num;
  	u16 num_chipselect;
+	u32 speed_hz;
  };

  #endif  /* __ASM_VIRTEX_DEVICES_H__ */
diff -u b/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
--- b/drivers/spi/xilinx_spi.c
+++ b/drivers/spi/xilinx_spi.c
@@ -71,12 +71,6 @@
  #define XIPIF_V123B_RESETR_OFFSET	0x40	/* IPIF reset register */
  #define XIPIF_V123B_RESET_MASK		0x0a	/* the value to write */

-/* Simple macros to get the code more readable */
-#define xspi_in16(addr)		in_be16((u16 __iomem *)(addr))
-#define xspi_in32(addr)		in_be32((u32 __iomem *)(addr))
-#define xspi_out16(addr, value)	out_be16((u16 __iomem *)(addr), (value))
-#define xspi_out32(addr, value)	out_be32((u32 __iomem *)(addr), (value))
-
  struct xilinx_spi {
  	/* bitbang has to be first */
  	struct spi_bitbang bitbang;
@@ -87,62 +81,48 @@

  	u32 irq;

+	u32 speed_hz;		/* SCK has a fixed frequency of speed_hz Hz */
+
  	u8 *rx_ptr;		/* pointer in the Tx buffer */
  	const u8 *tx_ptr;	/* pointer in the Rx buffer */
  	int remaining_bytes;	/* the number of bytes left to transfer */
  };

-static void xspi_abort_transfer(u8 __iomem *regs_base)
-{
-	/* Deselect the slave on the SPI bus */
-	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
-
-	/* Terminate transmit in progress (if any) and Reset the FIFOs */
-	xspi_out16(regs_base + XSPI_CR_OFFSET,
-		   xspi_in16(regs_base + XSPI_CR_OFFSET)
-		   | XSPI_CR_TRANS_INHIBIT | XSPI_CR_TXFIFO_RESET
-		   | XSPI_CR_RXFIFO_RESET);
-}
-
-static void xspi_init_hw(u8 __iomem *regs_base)
+static void xspi_init_hw(void __iomem *regs_base)
  {
  	/* Reset the SPI device */
-	xspi_out32(regs_base + XIPIF_V123B_RESETR_OFFSET,
-		   XIPIF_V123B_RESET_MASK);
+	out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
+		 XIPIF_V123B_RESET_MASK);
  	/* Disable all the interrupts just in case */
-	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+	out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
  	/* Enable the global IPIF interrupt */
-	xspi_out32(regs_base + XIPIF_V123B_DGIER_OFFSET,
-		   XIPIF_V123B_GINTR_ENABLE);
+	out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
+		 XIPIF_V123B_GINTR_ENABLE);
  	/* Deselect the slave on the SPI bus */
-	xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
  	/* Disable the transmitter, enable Manual Slave Select Assertion,
  	 * put SPI controller into master mode, and enable it */
-	xspi_out16(regs_base + XSPI_CR_OFFSET,
-		   XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
-		   | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+	out_be16(regs_base + XSPI_CR_OFFSET,
+		 XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
+		 | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
  }

  static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
  {
-	struct xilinx_spi *xspi;
-	u8 __iomem *regs_base;
-
-	xspi = spi_master_get_devdata(spi->master);
-	regs_base = xspi->regs;
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);

  	if (is_on == BITBANG_CS_INACTIVE) {
  		/* Deselect the slave on the SPI bus */
-		xspi_out32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+		out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
  	} else if (is_on == BITBANG_CS_ACTIVE) {
  		/* Set the SPI clock phase and polarity */
-		u16 cr = xspi_in16(regs_base + XSPI_CR_OFFSET)
+		u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
  			 & ~XSPI_CR_MODE_MASK;
  		if (spi->mode & SPI_CPHA)
  			cr |= XSPI_CR_CPHA;
  		if (spi->mode & SPI_CPOL)
  			cr |= XSPI_CR_CPOL;
-		xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+		out_be16(xspi->regs + XSPI_CR_OFFSET, cr);

  		/* We do not check spi->max_speed_hz here as the SPI clock
  		 * frequency is not software programmable (the IP block design
@@ -150,30 +130,43 @@
  		 */

  		/* Activate the chip select */
-		xspi_out32(regs_base + XSPI_SSR_OFFSET,
-			   ~(0x0001 << spi->chip_select));
+		out_be32(xspi->regs + XSPI_SSR_OFFSET,
+			 ~(0x0001 << spi->chip_select));
  	}
  }

  /* spi_bitbang requires custom setup_transfer() to be defined if there is a
   * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
   * supports just 8 bits per word, and SPI clock can't be changed in software.
- * Check for 8 bits per word; speed_hz checking could be added if the SPI
- * clock information is available. Chip select delay calculations could be
+ * Check for 8 bits per word. Chip select delay calculations could be
   * added here as soon as bitbang_work() can be made aware of the delay value.
   */
  static int xilinx_spi_setup_transfer(struct spi_device *spi,
  				     struct spi_transfer *t)
  {
  	u8 bits_per_word;
+	u32 hz;
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);

  	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
-	if (bits_per_word != 8)
+	hz = (t) ? t->speed_hz : spi->max_speed_hz;
+	if (bits_per_word != 8) {
+		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+			__FUNCTION__, bits_per_word);
  		return -EINVAL;
+	}
+
+	if (hz && xspi->speed_hz > hz) {
+		dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
+			__FUNCTION__, hz);
+		return -EINVAL;
+	}

  	return 0;
  }

+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)

  static int xilinx_spi_setup(struct spi_device *spi)
  {
@@ -186,63 +179,70 @@

  	if (!spi->bits_per_word)
  		spi->bits_per_word = 8;
+	
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+			__FUNCTION__, spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}

  	retval = xilinx_spi_setup_transfer(spi, NULL);
  	if (retval < 0)
  		return retval;

  	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
-		__FUNCTION__, spi->mode & (SPI_CPOL | SPI_CPHA),
-		spi->bits_per_word, 0);
+		__FUNCTION__, spi->mode & MODEBITS, spi->bits_per_word, 0);

  	return 0;
  }

+static inline void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
+{
+	u8 sr;
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
+		if (xspi->tx_ptr) {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
+		} else {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
+		}
+		xspi->remaining_bytes--;
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	}
+}
+
  static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
  {
-	struct xilinx_spi *xspi;
-	u8 __iomem *regs_base;
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
  	u32 ipif_ier;
  	u16 cr;
-	u8 sr;

  	/* We get here with transmitter inhibited */

-	xspi = spi_master_get_devdata(spi->master);
-	regs_base = xspi->regs;
-
  	xspi->tx_ptr = t->tx_buf;
  	xspi->rx_ptr = t->rx_buf;
  	xspi->remaining_bytes = t->len;
  	INIT_COMPLETION(xspi->done);

-	/* Fill the Tx FIFO with as many bytes as possible */
-	sr = in_8(regs_base + XSPI_SR_OFFSET);
-	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
-		if (xspi->tx_ptr) {
-			out_8(regs_base + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
-		} else {
-			out_8(regs_base + XSPI_TXD_OFFSET, 0);
-		}
-		xspi->remaining_bytes--;
-		sr = in_8(regs_base + XSPI_SR_OFFSET);
-	}
+	xilinx_spi_fill_tx_fifo(xspi);

  	/* Enable the transmit empty interrupt, which we use to determine
  	 * progress on the transmission.
  	 */
-	ipif_ier = xspi_in32(regs_base + XIPIF_V123B_IIER_OFFSET);
-	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET,
-		   ipif_ier | XSPI_INTR_TX_EMPTY);
+	ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
+		 ipif_ier | XSPI_INTR_TX_EMPTY);

  	/* Start the transfer by not inhibiting the transmitter any longer */
-	cr = xspi_in16(regs_base + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
-	xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+	cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
+	out_be16(xspi->regs + XSPI_CR_OFFSET, cr);

  	wait_for_completion(&xspi->done);

  	/* Disable the transmit empty interrupt */
-	xspi_out32(regs_base + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);

  	return t->len - xspi->remaining_bytes;
  }
@@ -255,16 +255,12 @@
   */
  static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
  {
-	struct xilinx_spi *xspi;
-	u8 __iomem *regs_base;
+	struct xilinx_spi *xspi = dev_id;
  	u32 ipif_isr;

-	xspi = (struct xilinx_spi *) dev_id;
-	regs_base = xspi->regs;
-
-     	/* Get the IPIF inetrrupts, and clear them immediately */
-	ipif_isr = xspi_in32(regs_base + XIPIF_V123B_IISR_OFFSET);
-	xspi_out32(regs_base + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+     	/* Get the IPIF interrupts, and clear them immediately */
+	ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);

  	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
  		u16 cr;
@@ -275,40 +271,29 @@
  		 * transmitter while the Isr refills the transmit register/FIFO,
  		 * or make sure it is stopped if we're done.
  	         */
-		cr = xspi_in16(regs_base + XSPI_CR_OFFSET);
-		xspi_out16(regs_base + XSPI_CR_OFFSET,
-			   cr | XSPI_CR_TRANS_INHIBIT);
+		cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
+		out_be16(xspi->regs + XSPI_CR_OFFSET,
+			 cr | XSPI_CR_TRANS_INHIBIT);

  		/* Read out all the data from the Rx FIFO */
-		sr = in_8(regs_base + XSPI_SR_OFFSET);
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
  		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
  			u8 data;

-			data = in_8(regs_base + XSPI_RXD_OFFSET);
+			data = in_8(xspi->regs + XSPI_RXD_OFFSET);
  			if (xspi->rx_ptr) {
  				*xspi->rx_ptr++ = data;
  			}
-			sr = in_8(regs_base + XSPI_SR_OFFSET);
+			sr = in_8(xspi->regs + XSPI_SR_OFFSET);
  		}

  	        /* See if there is more data to send */
  		if (xspi->remaining_bytes > 0) {
-			/* sr content is valid here; no need for io_8() */
-			while ((sr & XSPI_SR_TX_FULL_MASK) == 0
-				&& xspi->remaining_bytes > 0) {
-				if (xspi->tx_ptr) {
-					out_8(regs_base + XSPI_TXD_OFFSET,
-					      *xspi->tx_ptr++);
-				} else {
-					out_8(regs_base + XSPI_TXD_OFFSET, 0);
-				}
-				xspi->remaining_bytes--;
-				sr = in_8(regs_base + XSPI_SR_OFFSET);
-			}
+			xilinx_spi_fill_tx_fifo(xspi);
  			/* Start the transfer by not inhibiting the
  			 * transmitter any longer
  			 */
-			xspi_out16(regs_base + XSPI_CR_OFFSET, cr);
+			out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
  		} else {
  			/* No more data to send.
  			 * Indicate the transfer is completed.
@@ -359,6 +344,12 @@
  	xspi->bitbang.master->setup = xilinx_spi_setup;
  	init_completion(&xspi->done);

+	if (!request_mem_region(r->start,
+			r->end - r->start + 1, XILINX_SPI_NAME)) {
+		ret = -ENXIO;
+		goto put_master;
+	}
+	
  	xspi->regs = ioremap(r->start, r->end - r->start + 1);
  	if (xspi->regs == NULL) {
  		ret = -ENOMEM;
@@ -373,6 +364,7 @@

  	master->bus_num = pdata->bus_num;
  	master->num_chipselect = pdata->num_chipselect;
+	xspi->speed_hz = pdata->speed_hz;

  	/* SPI controller initializations */
  	xspi_init_hw(xspi->regs);
@@ -411,7 +403,6 @@
  	xspi = spi_master_get_devdata(master);

  	spi_bitbang_stop(&xspi->bitbang);
-	xspi_abort_transfer(xspi->regs);
  	free_irq(xspi->irq, xspi);
  	iounmap(xspi->regs);
  	platform_set_drvdata(dev, 0);


[-- Attachment #2: xilinx-spi-driver.ko.patch --]
[-- Type: text/x-patch, Size: 15511 bytes --]

Simple driver for Xilinx SPI controler.

From: Andrei Konovalov <ank-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>

Only master mode is supported. No support for multiple masters.
Slave mode is not supported either.
Not using level 1 drivers from EDK.

Signed-off-by: Yuri Frolov <yfrolov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
Signed-off-by: Andrei Konovalov <akonovalov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>

---
---

 arch/ppc/syslib/virtex_devices.h |    7 +
 drivers/spi/Kconfig              |    9 +
 drivers/spi/Makefile             |    1 
 drivers/spi/xilinx_spi.c         |  438 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 455 insertions(+), 0 deletions(-)

diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h
index b49dc61..420a634 100644
--- a/arch/ppc/syslib/virtex_devices.h
+++ b/arch/ppc/syslib/virtex_devices.h
@@ -31,4 +31,11 @@ struct xtemac_platform_data {
  */
 int virtex_device_fixup(struct platform_device *dev);
 
+/* SPI Controller IP */
+struct xspi_platform_data {
+	s16 bus_num;
+	u16 num_chipselect;
+	u32 speed_hz;
+};
+
 #endif  /* __ASM_VIRTEX_DEVICES_H__ */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 5e3f748..851d651 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -156,6 +156,15 @@ config SPI_S3C24XX_GPIO
 	  GPIO lines to provide the SPI bus. This can be used where
 	  the inbuilt hardware cannot provide the transfer mode, or
 	  where the board is using non hardware connected pins.
+
+config SPI_XILINX
+	tristate "Xilinx SPI controller"
+	depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL
+	select SPI_BITBANG
+	help
+	  This enables using the SPI controller IP from Xilinx EDK in master
+	  mode. See the DS464, "OPB Serial Peripheral Interface (SPI) (v1.00e)"
+	  Product Specification document for the hardware details.
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5788d86..a2412bd 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
+obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
 # 	... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
new file mode 100644
index 0000000..a0e96d7
--- /dev/null
+++ b/drivers/spi/xilinx_spi.c
@@ -0,0 +1,438 @@
+/*
+ * xilinx_spi.c
+ *
+ * Xilinx SPI controler driver (master mode only)
+ *
+ * Author: MontaVista Software, Inc.
+ *         source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org
+ *
+ * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2.  This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+
+#include <asm/io.h>
+#include <syslib/virtex_devices.h>
+
+#define XILINX_SPI_NAME "xspi"
+
+/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
+ * Product Specification", DS464
+ */
+#define XSPI_CR_OFFSET		0x62	/* 16-bit Control Register */
+
+#define XSPI_CR_ENABLE		0x02
+#define XSPI_CR_MASTER_MODE	0x04
+#define XSPI_CR_CPOL		0x08
+#define XSPI_CR_CPHA		0x10
+#define XSPI_CR_MODE_MASK	(XSPI_CR_CPHA | XSPI_CR_CPOL)
+#define XSPI_CR_TXFIFO_RESET	0x20
+#define XSPI_CR_RXFIFO_RESET	0x40
+#define XSPI_CR_MANUAL_SSELECT	0x80
+#define XSPI_CR_TRANS_INHIBIT	0x100
+
+#define XSPI_SR_OFFSET		0x67	/* 8-bit Status Register */
+
+#define XSPI_SR_RX_EMPTY_MASK	0x01	/* Receive FIFO is empty */
+#define XSPI_SR_RX_FULL_MASK	0x02	/* Receive FIFO is full */
+#define XSPI_SR_TX_EMPTY_MASK	0x04	/* Transmit FIFO is empty */
+#define XSPI_SR_TX_FULL_MASK	0x08	/* Transmit FIFO is full */
+#define XSPI_SR_MODE_FAULT_MASK	0x10	/* Mode fault error */
+
+#define XSPI_TXD_OFFSET		0x6b	/* 8-bit Data Transmit Register */
+#define XSPI_RXD_OFFSET		0x6f	/* 8-bit Data Receive Register */
+
+#define XSPI_SSR_OFFSET		0x70	/* 32-bit Slave Select Register */
+
+/* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414
+ * IPIF registers are 32 bit
+ */
+#define XIPIF_V123B_DGIER_OFFSET	0x1c	/* IPIF global int enable reg */
+#define XIPIF_V123B_GINTR_ENABLE	0x80000000
+
+#define XIPIF_V123B_IISR_OFFSET		0x20	/* IPIF interrupt status reg */
+#define XIPIF_V123B_IIER_OFFSET		0x28	/* IPIF interrupt enable reg */
+
+#define XSPI_INTR_MODE_FAULT		0x01	/* Mode fault error */
+#define XSPI_INTR_SLAVE_MODE_FAULT	0x02	/* Selected as slave while
+                                                * disabled */
+#define XSPI_INTR_TX_EMPTY		0x04	/* TxFIFO is empty */
+#define XSPI_INTR_TX_UNDERRUN		0x08	/* TxFIFO was underrun */
+#define XSPI_INTR_RX_FULL		0x10	/* RxFIFO is full */
+#define XSPI_INTR_RX_OVERRUN		0x20	/* RxFIFO was overrun */
+
+#define XIPIF_V123B_RESETR_OFFSET	0x40	/* IPIF reset register */
+#define XIPIF_V123B_RESET_MASK		0x0a	/* the value to write */
+
+struct xilinx_spi {
+	/* bitbang has to be first */
+	struct spi_bitbang bitbang;
+	struct completion done;
+
+	u32		regs_phys;	/* phys. address of the control registers */
+	void __iomem	*regs;		/* virt. address of the control registers */
+
+	u32 irq;
+
+	u32 speed_hz;		/* SCK has a fixed frequency of speed_hz Hz */
+
+	u8 *rx_ptr;		/* pointer in the Tx buffer */
+	const u8 *tx_ptr;	/* pointer in the Rx buffer */
+	int remaining_bytes;	/* the number of bytes left to transfer */
+};
+
+static void xspi_init_hw(void __iomem *regs_base)
+{
+	/* Reset the SPI device */
+	out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
+		 XIPIF_V123B_RESET_MASK);
+	/* Disable all the interrupts just in case */
+	out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+	/* Enable the global IPIF interrupt */
+	out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
+		 XIPIF_V123B_GINTR_ENABLE);
+	/* Deselect the slave on the SPI bus */
+	out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	/* Disable the transmitter, enable Manual Slave Select Assertion,
+	 * put SPI controller into master mode, and enable it */
+	out_be16(regs_base + XSPI_CR_OFFSET,
+		 XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
+		 | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+}
+
+static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
+{
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+
+	if (is_on == BITBANG_CS_INACTIVE) {
+		/* Deselect the slave on the SPI bus */
+		out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
+	} else if (is_on == BITBANG_CS_ACTIVE) {
+		/* Set the SPI clock phase and polarity */
+		u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
+			 & ~XSPI_CR_MODE_MASK;
+		if (spi->mode & SPI_CPHA)
+			cr |= XSPI_CR_CPHA;
+		if (spi->mode & SPI_CPOL)
+			cr |= XSPI_CR_CPOL;
+		out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+
+		/* We do not check spi->max_speed_hz here as the SPI clock
+		 * frequency is not software programmable (the IP block design
+		 * parameter)
+		 */
+
+		/* Activate the chip select */
+		out_be32(xspi->regs + XSPI_SSR_OFFSET,
+			 ~(0x0001 << spi->chip_select));
+	}
+}
+
+/* spi_bitbang requires custom setup_transfer() to be defined if there is a
+ * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
+ * supports just 8 bits per word, and SPI clock can't be changed in software.
+ * Check for 8 bits per word. Chip select delay calculations could be
+ * added here as soon as bitbang_work() can be made aware of the delay value.
+ */
+static int xilinx_spi_setup_transfer(struct spi_device *spi,
+				     struct spi_transfer *t)
+{
+	u8 bits_per_word;
+	u32 hz;
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+
+	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
+	hz = (t) ? t->speed_hz : spi->max_speed_hz;
+	if (bits_per_word != 8) {
+		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+			__FUNCTION__, bits_per_word);
+		return -EINVAL;
+	}
+
+	if (hz && xspi->speed_hz > hz) {
+		dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
+			__FUNCTION__, hz);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int xilinx_spi_setup(struct spi_device *spi)
+{
+	struct spi_bitbang *bitbang;
+	struct xilinx_spi *xspi;
+	int retval;
+
+	xspi = spi_master_get_devdata(spi->master);
+	bitbang = &xspi->bitbang;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+	
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+			__FUNCTION__, spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}
+
+	retval = xilinx_spi_setup_transfer(spi, NULL);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+		__FUNCTION__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+	return 0;
+}
+
+static inline void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
+{
+	u8 sr;
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
+		if (xspi->tx_ptr) {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
+		} else {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
+		}
+		xspi->remaining_bytes--;
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	}
+}
+
+static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+	u32 ipif_ier;
+	u16 cr;
+
+	/* We get here with transmitter inhibited */
+
+	xspi->tx_ptr = t->tx_buf;
+	xspi->rx_ptr = t->rx_buf;
+	xspi->remaining_bytes = t->len;
+	INIT_COMPLETION(xspi->done);
+
+	xilinx_spi_fill_tx_fifo(xspi);
+
+	/* Enable the transmit empty interrupt, which we use to determine
+	 * progress on the transmission.
+	 */
+	ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
+		 ipif_ier | XSPI_INTR_TX_EMPTY);
+
+	/* Start the transfer by not inhibiting the transmitter any longer */
+	cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
+	out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+
+	wait_for_completion(&xspi->done);
+
+	/* Disable the transmit empty interrupt */
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+
+	return t->len - xspi->remaining_bytes;
+}
+
+
+/* This driver supports single master mode only. Hence Tx FIFO Empty
+ * is the only interrupt we care about.
+ * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode
+ * Fault are not to happen.
+ */
+static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
+{
+	struct xilinx_spi *xspi = dev_id;
+	u32 ipif_isr;
+
+     	/* Get the IPIF interrupts, and clear them immediately */
+	ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+
+	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
+		u16 cr;
+		u8 sr;
+
+		/* A transmit has just completed. Process received data and
+		 * check for more data to transmit. Always inhibit the
+		 * transmitter while the Isr refills the transmit register/FIFO,
+		 * or make sure it is stopped if we're done.
+	         */
+		cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
+		out_be16(xspi->regs + XSPI_CR_OFFSET,
+			 cr | XSPI_CR_TRANS_INHIBIT);
+
+		/* Read out all the data from the Rx FIFO */
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
+			u8 data;
+
+			data = in_8(xspi->regs + XSPI_RXD_OFFSET);
+			if (xspi->rx_ptr) {
+				*xspi->rx_ptr++ = data;
+			}
+			sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+		}
+
+	        /* See if there is more data to send */
+		if (xspi->remaining_bytes > 0) {
+			xilinx_spi_fill_tx_fifo(xspi);
+			/* Start the transfer by not inhibiting the
+			 * transmitter any longer
+			 */
+			out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+		} else {
+			/* No more data to send.
+			 * Indicate the transfer is completed.
+			 */
+			complete(&xspi->done);
+		}
+	} else {
+		/* spurious interrupt */
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __init xilinx_spi_probe(struct platform_device *dev)
+{
+	int ret = 0;
+	struct spi_master *master;
+	struct xilinx_spi *xspi;
+	struct xspi_platform_data *pdata;
+	struct resource *r;
+
+	/* Get resources(memory, IRQ) associated with the device */
+	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
+
+	if (master == NULL) {
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(dev, master);
+	pdata = dev->dev.platform_data;
+
+	if (pdata == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	xspi = spi_master_get_devdata(master);
+	xspi->bitbang.master = spi_master_get(master);
+	xspi->bitbang.chipselect = xilinx_spi_chipselect;
+	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
+	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
+	xspi->bitbang.master->setup = xilinx_spi_setup;
+	init_completion(&xspi->done);
+
+	if (!request_mem_region(r->start, 
+			r->end - r->start + 1, XILINX_SPI_NAME)) {
+		ret = -ENXIO;
+		goto put_master;
+	}
+	
+	xspi->regs = ioremap(r->start, r->end - r->start + 1);
+	if (xspi->regs == NULL) {
+		ret = -ENOMEM;
+		goto put_master;
+	}
+
+	xspi->irq = platform_get_irq(dev, 0);
+	if (xspi->irq < 0) {
+		ret = -ENXIO;
+		goto unmap_io;
+	}
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
+	xspi->speed_hz = pdata->speed_hz;
+
+	/* SPI controller initializations */
+	xspi_init_hw(xspi->regs);
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+	if (ret != 0)
+		goto unmap_io;
+
+	ret = spi_bitbang_start(&xspi->bitbang);
+	if (ret != 0) {
+		printk(KERN_ALERT "spi_btbang_start FAILED\n");
+		goto free_irq;
+	}
+
+	printk(KERN_INFO "%s: at 0x%08X mapped to 0x%08X, irq=%d\n",
+	       dev->dev.bus_id, r->start, (u32)xspi->regs, xspi->irq);
+
+	return ret;
+
+free_irq:
+        free_irq(xspi->irq, xspi);
+unmap_io:
+	iounmap(xspi->regs);
+put_master:
+        spi_master_put(master);
+	return ret;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+	struct xilinx_spi *xspi;
+	struct spi_master *master;
+
+	master = platform_get_drvdata(dev);
+	xspi = spi_master_get_devdata(master);
+
+	spi_bitbang_stop(&xspi->bitbang);
+	free_irq(xspi->irq, xspi);
+	iounmap(xspi->regs);
+	platform_set_drvdata(dev, 0);
+	spi_master_put(xspi->bitbang.master);
+
+	return 0;
+}
+
+static struct platform_driver xilinx_spi_driver = {
+	.probe	= xilinx_spi_probe,
+	.remove	= __devexit_p(xilinx_spi_remove),
+	.driver = {
+		   .name = XILINX_SPI_NAME,
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init xilinx_spi_init(void)
+{
+	return platform_driver_register(&xilinx_spi_driver);
+}
+
+static void __exit xilinx_spi_exit(void)
+{
+	platform_driver_unregister(&xilinx_spi_driver);
+}
+
+module_init(xilinx_spi_init);
+module_exit(xilinx_spi_exit);
+
+MODULE_AUTHOR("MontaVista Software, Inc. <source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("Xilinx SPI driver");
+MODULE_LICENSE("GPL");

[-- Attachment #3: Type: text/plain, Size: 286 bytes --]

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

[-- Attachment #4: Type: text/plain, Size: 210 bytes --]

_______________________________________________
spi-devel-general mailing list
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

^ permalink raw reply related	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found]         ` <466ADC4B.4050806-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
@ 2007-06-12 16:28           ` David Brownell
       [not found]             ` <200706120928.09176.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
  0 siblings, 1 reply; 15+ messages in thread
From: David Brownell @ 2007-06-12 16:28 UTC (permalink / raw)
  To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
  Cc: Andrei Konovalov, linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

On Saturday 09 June 2007, Andrei Konovalov wrote:
> The new patch is attached to this message.

Would you send a version that doesn't produce so many
warnings when run against scripts/checkpatch.pl ??

The issues seem to be mostly whitespace and linelength,
so that should be easy.

- Dave

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

^ permalink raw reply	[flat|nested] 15+ messages in thread

* Re: [PATCH] Simple driver for Xilinx SPI controler.
       [not found]             ` <200706120928.09176.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
@ 2007-06-13 14:55               ` Andrei Konovalov
  0 siblings, 0 replies; 15+ messages in thread
From: Andrei Konovalov @ 2007-06-13 14:55 UTC (permalink / raw)
  To: David Brownell
  Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
	linuxppc-embedded-mnsaURCQ41sdnm+yROfE0A

[-- Attachment #1: Type: text/plain, Size: 509 bytes --]

David Brownell wrote:
> On Saturday 09 June 2007, Andrei Konovalov wrote:
>> The new patch is attached to this message.
> 
> Would you send a version that doesn't produce so many
> warnings when run against scripts/checkpatch.pl ??
> 
> The issues seem to be mostly whitespace and linelength,
> so that should be easy.
> 
> - Dave


Here is the updated patch (attached).
The changes are just
- replace "#include <asm/io.h>" with "#include <linux/io.h>"
- whitespace and linelength mods

Thanks again,
Andrei


[-- Attachment #2: xilinx-spi-driver.patch --]
[-- Type: text/x-patch, Size: 15448 bytes --]

Simple driver for Xilinx SPI controler.

From: Andrei Konovalov <ank-bi+AKbBUZKY6gyzm1THtWbp2dZbC/Bob@public.gmane.org>

Only master mode is supported. No support for multiple masters.
Slave mode is not supported either.
Not using level 1 drivers from EDK.

Signed-off-by: Yuri Frolov <yfrolov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
Signed-off-by: Andrei Konovalov <akonovalov-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
---

 arch/ppc/syslib/virtex_devices.h |    7 +
 drivers/spi/Kconfig              |    9 +
 drivers/spi/Makefile             |    1 
 drivers/spi/xilinx_spi.c         |  438 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 455 insertions(+), 0 deletions(-)

diff --git a/arch/ppc/syslib/virtex_devices.h b/arch/ppc/syslib/virtex_devices.h
index 3d4be14..9f38d92 100644
--- a/arch/ppc/syslib/virtex_devices.h
+++ b/arch/ppc/syslib/virtex_devices.h
@@ -31,4 +31,11 @@ void __init virtex_early_serial_map(void);
  */
 int virtex_device_fixup(struct platform_device *dev);
 
+/* SPI Controller IP */
+struct xspi_platform_data {
+	s16 bus_num;
+	u16 num_chipselect;
+	u32 speed_hz;
+};
+
 #endif  /* __ASM_VIRTEX_DEVICES_H__ */
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 5e3f748..851d651 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -156,6 +156,15 @@ config SPI_S3C24XX_GPIO
 	  GPIO lines to provide the SPI bus. This can be used where
 	  the inbuilt hardware cannot provide the transfer mode, or
 	  where the board is using non hardware connected pins.
+
+config SPI_XILINX
+	tristate "Xilinx SPI controller"
+	depends on SPI_MASTER && XILINX_VIRTEX && EXPERIMENTAL
+	select SPI_BITBANG
+	help
+	  This enables using the SPI controller IP from Xilinx EDK in master
+	  mode. See the DS464, "OPB Serial Peripheral Interface (SPI) (v1.00e)"
+	  Product Specification document for the hardware details.
 #
 # Add new SPI master controllers in alphabetical order above this line
 #
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 5788d86..a2412bd 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
 obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
+obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
 # 	... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
diff --git a/drivers/spi/xilinx_spi.c b/drivers/spi/xilinx_spi.c
new file mode 100644
index 0000000..b09f7c6
--- /dev/null
+++ b/drivers/spi/xilinx_spi.c
@@ -0,0 +1,438 @@
+/*
+ * xilinx_spi.c
+ *
+ * Xilinx SPI controler driver (master mode only)
+ *
+ * Author: MontaVista Software, Inc.
+ *         source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org
+ *
+ * 2002-2007 (c) MontaVista Software, Inc.  This file is licensed under the
+ * terms of the GNU General Public License version 2.  This program is licensed
+ * "as is" without any warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/io.h>
+
+#include <syslib/virtex_devices.h>
+
+#define XILINX_SPI_NAME "xspi"
+
+/* Register definitions as per "OPB Serial Peripheral Interface (SPI) (v1.00e)
+ * Product Specification", DS464
+ */
+#define XSPI_CR_OFFSET		0x62	/* 16-bit Control Register */
+
+#define XSPI_CR_ENABLE		0x02
+#define XSPI_CR_MASTER_MODE	0x04
+#define XSPI_CR_CPOL		0x08
+#define XSPI_CR_CPHA		0x10
+#define XSPI_CR_MODE_MASK	(XSPI_CR_CPHA | XSPI_CR_CPOL)
+#define XSPI_CR_TXFIFO_RESET	0x20
+#define XSPI_CR_RXFIFO_RESET	0x40
+#define XSPI_CR_MANUAL_SSELECT	0x80
+#define XSPI_CR_TRANS_INHIBIT	0x100
+
+#define XSPI_SR_OFFSET		0x67	/* 8-bit Status Register */
+
+#define XSPI_SR_RX_EMPTY_MASK	0x01	/* Receive FIFO is empty */
+#define XSPI_SR_RX_FULL_MASK	0x02	/* Receive FIFO is full */
+#define XSPI_SR_TX_EMPTY_MASK	0x04	/* Transmit FIFO is empty */
+#define XSPI_SR_TX_FULL_MASK	0x08	/* Transmit FIFO is full */
+#define XSPI_SR_MODE_FAULT_MASK	0x10	/* Mode fault error */
+
+#define XSPI_TXD_OFFSET		0x6b	/* 8-bit Data Transmit Register */
+#define XSPI_RXD_OFFSET		0x6f	/* 8-bit Data Receive Register */
+
+#define XSPI_SSR_OFFSET		0x70	/* 32-bit Slave Select Register */
+
+/* Register definitions as per "OPB IPIF (v3.01c) Product Specification", DS414
+ * IPIF registers are 32 bit
+ */
+#define XIPIF_V123B_DGIER_OFFSET	0x1c	/* IPIF global int enable reg */
+#define XIPIF_V123B_GINTR_ENABLE	0x80000000
+
+#define XIPIF_V123B_IISR_OFFSET		0x20	/* IPIF interrupt status reg */
+#define XIPIF_V123B_IIER_OFFSET		0x28	/* IPIF interrupt enable reg */
+
+#define XSPI_INTR_MODE_FAULT		0x01	/* Mode fault error */
+#define XSPI_INTR_SLAVE_MODE_FAULT	0x02	/* Selected as slave while
+						 * disabled */
+#define XSPI_INTR_TX_EMPTY		0x04	/* TxFIFO is empty */
+#define XSPI_INTR_TX_UNDERRUN		0x08	/* TxFIFO was underrun */
+#define XSPI_INTR_RX_FULL		0x10	/* RxFIFO is full */
+#define XSPI_INTR_RX_OVERRUN		0x20	/* RxFIFO was overrun */
+
+#define XIPIF_V123B_RESETR_OFFSET	0x40	/* IPIF reset register */
+#define XIPIF_V123B_RESET_MASK		0x0a	/* the value to write */
+
+struct xilinx_spi {
+	/* bitbang has to be first */
+	struct spi_bitbang bitbang;
+	struct completion done;
+
+	u32		regs_phys;  /* phys. address of the control registers */
+	void __iomem	*regs;	    /* virt. address of the control registers */
+
+	u32 irq;
+
+	u32 speed_hz;		/* SCK has a fixed frequency of speed_hz Hz */
+
+	u8 *rx_ptr;		/* pointer in the Tx buffer */
+	const u8 *tx_ptr;	/* pointer in the Rx buffer */
+	int remaining_bytes;	/* the number of bytes left to transfer */
+};
+
+static void xspi_init_hw(void __iomem *regs_base)
+{
+	/* Reset the SPI device */
+	out_be32(regs_base + XIPIF_V123B_RESETR_OFFSET,
+		 XIPIF_V123B_RESET_MASK);
+	/* Disable all the interrupts just in case */
+	out_be32(regs_base + XIPIF_V123B_IIER_OFFSET, 0);
+	/* Enable the global IPIF interrupt */
+	out_be32(regs_base + XIPIF_V123B_DGIER_OFFSET,
+		 XIPIF_V123B_GINTR_ENABLE);
+	/* Deselect the slave on the SPI bus */
+	out_be32(regs_base + XSPI_SSR_OFFSET, 0xffff);
+	/* Disable the transmitter, enable Manual Slave Select Assertion,
+	 * put SPI controller into master mode, and enable it */
+	out_be16(regs_base + XSPI_CR_OFFSET,
+		 XSPI_CR_TRANS_INHIBIT | XSPI_CR_MANUAL_SSELECT
+		 | XSPI_CR_MASTER_MODE | XSPI_CR_ENABLE);
+}
+
+static void xilinx_spi_chipselect(struct spi_device *spi, int is_on)
+{
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+
+	if (is_on == BITBANG_CS_INACTIVE) {
+		/* Deselect the slave on the SPI bus */
+		out_be32(xspi->regs + XSPI_SSR_OFFSET, 0xffff);
+	} else if (is_on == BITBANG_CS_ACTIVE) {
+		/* Set the SPI clock phase and polarity */
+		u16 cr = in_be16(xspi->regs + XSPI_CR_OFFSET)
+			 & ~XSPI_CR_MODE_MASK;
+		if (spi->mode & SPI_CPHA)
+			cr |= XSPI_CR_CPHA;
+		if (spi->mode & SPI_CPOL)
+			cr |= XSPI_CR_CPOL;
+		out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+
+		/* We do not check spi->max_speed_hz here as the SPI clock
+		 * frequency is not software programmable (the IP block design
+		 * parameter)
+		 */
+
+		/* Activate the chip select */
+		out_be32(xspi->regs + XSPI_SSR_OFFSET,
+			 ~(0x0001 << spi->chip_select));
+	}
+}
+
+/* spi_bitbang requires custom setup_transfer() to be defined if there is a
+ * custom txrx_bufs(). We have nothing to setup here as the SPI IP block
+ * supports just 8 bits per word, and SPI clock can't be changed in software.
+ * Check for 8 bits per word. Chip select delay calculations could be
+ * added here as soon as bitbang_work() can be made aware of the delay value.
+ */
+static int xilinx_spi_setup_transfer(struct spi_device *spi,
+				     struct spi_transfer *t)
+{
+	u8 bits_per_word;
+	u32 hz;
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+
+	bits_per_word = (t) ? t->bits_per_word : spi->bits_per_word;
+	hz = (t) ? t->speed_hz : spi->max_speed_hz;
+	if (bits_per_word != 8) {
+		dev_err(&spi->dev, "%s, unsupported bits_per_word=%d\n",
+			__FUNCTION__, bits_per_word);
+		return -EINVAL;
+	}
+
+	if (hz && xspi->speed_hz > hz) {
+		dev_err(&spi->dev, "%s, unsupported clock rate %uHz\n",
+			__FUNCTION__, hz);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/* the spi->mode bits understood by this driver: */
+#define MODEBITS (SPI_CPOL | SPI_CPHA)
+
+static int xilinx_spi_setup(struct spi_device *spi)
+{
+	struct spi_bitbang *bitbang;
+	struct xilinx_spi *xspi;
+	int retval;
+
+	xspi = spi_master_get_devdata(spi->master);
+	bitbang = &xspi->bitbang;
+
+	if (!spi->bits_per_word)
+		spi->bits_per_word = 8;
+
+	if (spi->mode & ~MODEBITS) {
+		dev_err(&spi->dev, "%s, unsupported mode bits %x\n",
+			__FUNCTION__, spi->mode & ~MODEBITS);
+		return -EINVAL;
+	}
+
+	retval = xilinx_spi_setup_transfer(spi, NULL);
+	if (retval < 0)
+		return retval;
+
+	dev_dbg(&spi->dev, "%s, mode %d, %u bits/w, %u nsec/bit\n",
+		__FUNCTION__, spi->mode & MODEBITS, spi->bits_per_word, 0);
+
+	return 0;
+}
+
+static inline void xilinx_spi_fill_tx_fifo(struct xilinx_spi *xspi)
+{
+	u8 sr;
+
+	/* Fill the Tx FIFO with as many bytes as possible */
+	sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	while ((sr & XSPI_SR_TX_FULL_MASK) == 0 && xspi->remaining_bytes > 0) {
+		if (xspi->tx_ptr) {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, *xspi->tx_ptr++);
+		} else {
+			out_8(xspi->regs + XSPI_TXD_OFFSET, 0);
+		}
+		xspi->remaining_bytes--;
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+	}
+}
+
+static int xilinx_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
+{
+	struct xilinx_spi *xspi = spi_master_get_devdata(spi->master);
+	u32 ipif_ier;
+	u16 cr;
+
+	/* We get here with transmitter inhibited */
+
+	xspi->tx_ptr = t->tx_buf;
+	xspi->rx_ptr = t->rx_buf;
+	xspi->remaining_bytes = t->len;
+	INIT_COMPLETION(xspi->done);
+
+	xilinx_spi_fill_tx_fifo(xspi);
+
+	/* Enable the transmit empty interrupt, which we use to determine
+	 * progress on the transmission.
+	 */
+	ipif_ier = in_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET,
+		 ipif_ier | XSPI_INTR_TX_EMPTY);
+
+	/* Start the transfer by not inhibiting the transmitter any longer */
+	cr = in_be16(xspi->regs + XSPI_CR_OFFSET) & ~XSPI_CR_TRANS_INHIBIT;
+	out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+
+	wait_for_completion(&xspi->done);
+
+	/* Disable the transmit empty interrupt */
+	out_be32(xspi->regs + XIPIF_V123B_IIER_OFFSET, ipif_ier);
+
+	return t->len - xspi->remaining_bytes;
+}
+
+
+/* This driver supports single master mode only. Hence Tx FIFO Empty
+ * is the only interrupt we care about.
+ * Receive FIFO Overrun, Transmit FIFO Underrun, Mode Fault, and Slave Mode
+ * Fault are not to happen.
+ */
+static irqreturn_t xilinx_spi_irq(int irq, void *dev_id)
+{
+	struct xilinx_spi *xspi = dev_id;
+	u32 ipif_isr;
+
+	/* Get the IPIF interrupts, and clear them immediately */
+	ipif_isr = in_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET);
+	out_be32(xspi->regs + XIPIF_V123B_IISR_OFFSET, ipif_isr);
+
+	if (ipif_isr & XSPI_INTR_TX_EMPTY) {	/* Transmission completed */
+		u16 cr;
+		u8 sr;
+
+		/* A transmit has just completed. Process received data and
+		 * check for more data to transmit. Always inhibit the
+		 * transmitter while the Isr refills the transmit register/FIFO,
+		 * or make sure it is stopped if we're done.
+		 */
+		cr = in_be16(xspi->regs + XSPI_CR_OFFSET);
+		out_be16(xspi->regs + XSPI_CR_OFFSET,
+			 cr | XSPI_CR_TRANS_INHIBIT);
+
+		/* Read out all the data from the Rx FIFO */
+		sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+		while ((sr & XSPI_SR_RX_EMPTY_MASK) == 0) {
+			u8 data;
+
+			data = in_8(xspi->regs + XSPI_RXD_OFFSET);
+			if (xspi->rx_ptr) {
+				*xspi->rx_ptr++ = data;
+			}
+			sr = in_8(xspi->regs + XSPI_SR_OFFSET);
+		}
+
+		/* See if there is more data to send */
+		if (xspi->remaining_bytes > 0) {
+			xilinx_spi_fill_tx_fifo(xspi);
+			/* Start the transfer by not inhibiting the
+			 * transmitter any longer
+			 */
+			out_be16(xspi->regs + XSPI_CR_OFFSET, cr);
+		} else {
+			/* No more data to send.
+			 * Indicate the transfer is completed.
+			 */
+			complete(&xspi->done);
+		}
+	} else {
+		/* spurious interrupt */
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __init xilinx_spi_probe(struct platform_device *dev)
+{
+	int ret = 0;
+	struct spi_master *master;
+	struct xilinx_spi *xspi;
+	struct xspi_platform_data *pdata;
+	struct resource *r;
+
+	/* Get resources(memory, IRQ) associated with the device */
+	master = spi_alloc_master(&dev->dev, sizeof(struct xilinx_spi));
+
+	if (master == NULL) {
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(dev, master);
+	pdata = dev->dev.platform_data;
+
+	if (pdata == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	r = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		ret = -ENODEV;
+		goto put_master;
+	}
+
+	xspi = spi_master_get_devdata(master);
+	xspi->bitbang.master = spi_master_get(master);
+	xspi->bitbang.chipselect = xilinx_spi_chipselect;
+	xspi->bitbang.setup_transfer = xilinx_spi_setup_transfer;
+	xspi->bitbang.txrx_bufs = xilinx_spi_txrx_bufs;
+	xspi->bitbang.master->setup = xilinx_spi_setup;
+	init_completion(&xspi->done);
+
+	if (!request_mem_region(r->start,
+			r->end - r->start + 1, XILINX_SPI_NAME)) {
+		ret = -ENXIO;
+		goto put_master;
+	}
+
+	xspi->regs = ioremap(r->start, r->end - r->start + 1);
+	if (xspi->regs == NULL) {
+		ret = -ENOMEM;
+		goto put_master;
+	}
+
+	xspi->irq = platform_get_irq(dev, 0);
+	if (xspi->irq < 0) {
+		ret = -ENXIO;
+		goto unmap_io;
+	}
+
+	master->bus_num = pdata->bus_num;
+	master->num_chipselect = pdata->num_chipselect;
+	xspi->speed_hz = pdata->speed_hz;
+
+	/* SPI controller initializations */
+	xspi_init_hw(xspi->regs);
+
+	/* Register for SPI Interrupt */
+	ret = request_irq(xspi->irq, xilinx_spi_irq, 0, XILINX_SPI_NAME, xspi);
+	if (ret != 0)
+		goto unmap_io;
+
+	ret = spi_bitbang_start(&xspi->bitbang);
+	if (ret != 0) {
+		printk(KERN_ALERT "spi_btbang_start FAILED\n");
+		goto free_irq;
+	}
+
+	printk(KERN_INFO "%s: at 0x%08X mapped to 0x%08X, irq=%d\n",
+	       dev->dev.bus_id, r->start, (u32)xspi->regs, xspi->irq);
+
+	return ret;
+
+free_irq:
+	free_irq(xspi->irq, xspi);
+unmap_io:
+	iounmap(xspi->regs);
+put_master:
+	spi_master_put(master);
+	return ret;
+}
+
+static int __devexit xilinx_spi_remove(struct platform_device *dev)
+{
+	struct xilinx_spi *xspi;
+	struct spi_master *master;
+
+	master = platform_get_drvdata(dev);
+	xspi = spi_master_get_devdata(master);
+
+	spi_bitbang_stop(&xspi->bitbang);
+	free_irq(xspi->irq, xspi);
+	iounmap(xspi->regs);
+	platform_set_drvdata(dev, 0);
+	spi_master_put(xspi->bitbang.master);
+
+	return 0;
+}
+
+static struct platform_driver xilinx_spi_driver = {
+	.probe	= xilinx_spi_probe,
+	.remove	= __devexit_p(xilinx_spi_remove),
+	.driver = {
+		   .name = XILINX_SPI_NAME,
+		   .owner = THIS_MODULE,
+	},
+};
+
+static int __init xilinx_spi_init(void)
+{
+	return platform_driver_register(&xilinx_spi_driver);
+}
+
+static void __exit xilinx_spi_exit(void)
+{
+	platform_driver_unregister(&xilinx_spi_driver);
+}
+
+module_init(xilinx_spi_init);
+module_exit(xilinx_spi_exit);
+
+MODULE_AUTHOR("MontaVista Software, Inc. <source-Igf4POYTYCDQT0dZR+AlfA@public.gmane.org>");
+MODULE_DESCRIPTION("Xilinx SPI driver");
+MODULE_LICENSE("GPL");

[-- Attachment #3: Type: text/plain, Size: 286 bytes --]

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/

[-- Attachment #4: Type: text/plain, Size: 210 bytes --]

_______________________________________________
spi-devel-general mailing list
spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/spi-devel-general

^ permalink raw reply related	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2007-06-13 14:55 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2007-06-06 14:05 [PATCH] Simple driver for Xilinx SPI controler Andrei Konovalov
2007-06-06 16:03 ` Stephen Neuendorffer
2007-06-06 17:57   ` Andrei Konovalov
2007-06-06 18:06     ` Stephen Neuendorffer
2007-06-06 19:00       ` Grant Likely
     [not found]         ` <fa686aa40706061200u1141fcc9k73b7cc80c07240d1-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2007-06-06 19:06           ` Stephen Neuendorffer
2007-06-06 19:55             ` Grant Likely
2007-06-06 20:49         ` Wolfgang Reissnegger
2007-06-06 20:55           ` Grant Likely
     [not found] ` <4666BF47.8080103-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
2007-06-07  5:09   ` David Brownell
2007-06-07 18:39     ` [spi-devel-general] " Andrei Konovalov
     [not found]       ` <466850FB.5030505-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
2007-06-07 19:21         ` David Brownell
     [not found]     ` <200706062209.09731.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-06-09 16:58       ` Andrei Konovalov
     [not found]         ` <466ADC4B.4050806-hkdhdckH98+B+jHODAdFcQ@public.gmane.org>
2007-06-12 16:28           ` David Brownell
     [not found]             ` <200706120928.09176.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2007-06-13 14:55               ` Andrei Konovalov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).