[PATCH] SPI: add support for PNX/SPI controller

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,
inlined is a pretty much _development_ version of the Philips SPI controller support we're working with on some Philips ARM targets (PNX0106 and PNX4008, namely) based on spi_bitbang library from David Brownell. It's quite a notable thing that this buggy and in some way complicated SPI controller fits well in this bitbang framework! :)
This patch also contains dumb EEPROM driver for the onboard EEPROM behind the SPI bus. This driver was used by us for controller driver testing, mainly.

Signed-off-by: Vitaly Wool <[email protected]>
Signed-off-by: Dmitry Pervusin <[email protected]>

 drivers/spi/Kconfig                |    8
 drivers/spi/Makefile               |    2
 drivers/spi/eeprom.c               |  127 ++++++++++++
 drivers/spi/spi_pnx.c              |  365 ++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_pnx.h              |   60 ++++++
 include/asm-arm/arch-pnx4008/spi.h |  370 +++++++++++++++++++++++++++++++++++++
 6 files changed, 932 insertions(+)

Index: linux-2.6/drivers/spi/Kconfig
===================================================================
--- linux-2.6.orig/drivers/spi/Kconfig
+++ linux-2.6/drivers/spi/Kconfig
@@ -95,6 +95,14 @@ config SPI_BUTTERFLY
 # Add new SPI master controllers in alphabetical order above this line
 #
 
+config SPI_PNX
+	tristate "PNX SPI bus support"
+	depends on ARCH_PNX4008 && SPI_BITBANG
+
+config SPI_EEPROM
+	tristate "SPI EEPROM"
+	depends on SPI
+
 
 #
 # There are lots of SPI device types, with sensors and memory
Index: linux-2.6/drivers/spi/Makefile
===================================================================
--- linux-2.6.orig/drivers/spi/Makefile
+++ linux-2.6/drivers/spi/Makefile
@@ -13,9 +13,11 @@ obj-$(CONFIG_SPI_MASTER)		+= spi.o
 # SPI master controller drivers (bus)
 obj-$(CONFIG_SPI_BITBANG)		+= spi_bitbang.o
 obj-$(CONFIG_SPI_BUTTERFLY)		+= spi_butterfly.o
+obj-$(CONFIG_SPI_PNX)			+= spi_pnx.o
 # 	... add above this line ...
 
 # SPI protocol drivers (device/link on bus)
+obj-$(CONFIG_SPI_EEPROM)		+= eeprom.o
 # 	... add above this line ...
 
 # SPI slave controller drivers (upstream link)
Index: linux-2.6/drivers/spi/eeprom.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/spi/eeprom.c
@@ -0,0 +1,127 @@
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+
+#define EEPROM_SIZE		256
+#define DRIVER_NAME		"EEPROM"
+#define READ_BUFF_SIZE 160
+
+static int __init spi_eeprom_init(void);
+static void __exit spi_eeprom_cleanup(void);
+
+static int spiee_read_block (struct device *d, void *block)
+{
+	struct spi_device *spi = to_spi_device(d);
+	char cmd[2];
+	struct spi_transfer t[2] = {
+		[0] = {
+			.tx_buf = cmd,
+			.len = sizeof cmd,
+		},
+		[1] = {
+			.rx_buf = block,
+			.len = 256,
+		},
+	};
+	DECLARE_SPI_MESSAGE(m);
+
+	cmd[ 0 ] = 0x03;
+	cmd[ 1 ] = 0x00;
+	list_add_tail(&t[0].link, &m.transfers);
+	list_add_tail(&t[1].link, &m.transfers);
+
+	spi_sync(spi, &m);
+	return 256;
+}
+static ssize_t blk_show (struct device *d, struct device_attribute *attr, char *text )
+{
+	char *rdbuff = kmalloc (256, SLAB_KERNEL);
+	char line1[80],line2[80];
+	char item1[5], item2[5];
+	int bytes, i, x, blen;
+
+	blen = spiee_read_block (d, rdbuff);
+
+	bytes = 0;
+
+	strcpy(text, "");
+	for (i = 0; i < blen; i += 8) {
+		strcpy(line1, "");
+		strcpy(line2, "" );
+		for (x = i; x < i + 8; x++) {
+			if (x > blen) {
+				sprintf(item1, "   ");
+				sprintf(item2, " " );
+			} else {
+				sprintf(item1, "%02x ", rdbuff[x]);
+				if (isprint(rdbuff[x])) {
+					sprintf(item2, "%c", rdbuff[x]);
+				} else {
+					sprintf(item2, ".");
+				}
+			}
+			strcat(line1, item1);
+			strcat(line2, item2);
+		}
+
+		strcat(text, line1);
+		strcat(text, "|  " );
+		strcat(text, line2);
+		strcat(text, "\n" );
+
+		bytes += (strlen (line1 ) + strlen(line2) + 4);
+	}
+
+	kfree (rdbuff);
+
+	return bytes + 1;
+}
+
+static DEVICE_ATTR(blk, S_IRUGO, blk_show, NULL );
+
+
+static int spiee_probe(struct spi_device *this_dev)
+{
+	device_create_file(&this_dev->dev, &dev_attr_blk);
+	return 0;
+}
+
+static int spiee_remove(struct spi_device *this_dev)
+{
+	device_remove_file(&this_dev->dev, &dev_attr_blk);
+	return 0;
+}
+
+static struct spi_driver eeprom_driver = {
+	.driver = {
+		   .name	= DRIVER_NAME,
+		   .bus		= &spi_bus_type,
+		   .owner	= THIS_MODULE,
+	},
+	.probe = spiee_probe,
+	.remove = __devexit_p(spiee_remove),
+};
+
+static int __init spi_eeprom_init(void)
+{
+	int rc = spi_register_driver(&eeprom_driver);
+	printk("%s: %d\n", __FUNCTION__, rc);
+	return rc;
+}
+static void __exit spi_eeprom_cleanup(void)
+{
+	spi_unregister_driver(&eeprom_driver);
+}
+
+module_init(spi_eeprom_init);
+module_exit(spi_eeprom_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <[email protected]>");
+MODULE_DESCRIPTION("SPI EEPROM driver");
Index: linux-2.6/drivers/spi/spi_pnx.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/spi/spi_pnx.c
@@ -0,0 +1,365 @@
+/*
+ * drivers/spi/spi-pnx.c
+ *
+ * SPI support for PNX 010x/4008 boards.
+ *
+ * Author: dmitry pervushin <[email protected]>
+ * Based on Dennis Kovalev's <[email protected]> bus driver for pnx010x
+ *
+ * 2004 (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/config.h>
+#include <linux/version.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <asm/io.h>
+
+#include "spi_pnx.h"
+
+#define lock_device( dev )	/* down( &dev->sem ); */
+#define unlock_device( dev )	/* up( &dev->sem );   */
+
+void spipnx_dma_handler (int channel, int cause, void* context, struct pt_regs* pt_regs )
+{
+	struct spi_pnx_data *dd = context;
+
+	if (cause & DMA_TC_INT)
+		complete(&dd->threshold);
+}
+
+int spipnx_request_hardware(struct spi_pnx_data *dd,
+			    struct platform_device *dev)
+{
+	int err = 0;
+	struct resource *rsrc;
+
+	lock_device(&dev->dev);
+	rsrc = platform_get_resource(dev, IORESOURCE_MEM, 0);
+	if (rsrc) {
+		dd->iostart = rsrc->start;
+		dd->ioend = rsrc->end;
+		dd->spi_regs = ioremap(dd->iostart, SZ_4K);
+		/*
+		if (!request_mem_region
+		    (dd->iostart, dd->ioend - dd->iostart + 1, dev->dev.bus_id))
+			err = -ENODEV;
+		else
+		*/
+			dd->io_flags |= IORESOURCE_MEM;
+	}
+	dd->clk = spipnx_clk_init(dev);
+	if (IS_ERR(dd->clk)) {
+		err = PTR_ERR(dd->clk);
+		goto out;
+	}
+
+	spipnx_clk_start(dd->clk);
+
+	dd->irq = platform_get_irq(dev, 0);
+	if (dd->irq != NO_IRQ) {
+		dd->spi_regs->ier = 0;
+		dd->spi_regs->stat = 0;
+	}
+
+	rsrc = platform_get_resource(dev, IORESOURCE_DMA, 0);
+	if (rsrc) {
+		dd->slave_nr = rsrc->start;
+		err = spipnx_request_dma(&dd->dma_channel, dev->dev.bus_id, spipnx_dma_handler, dd);
+		if (!err)
+			dd->io_flags |= IORESOURCE_DMA;
+	}
+out:
+	unlock_device(&dev.dev);
+	return err;
+}
+
+static void spipnx_free_hardware(struct spi_pnx_data *dd,
+				 struct platform_device *dev)
+{
+	lock_device(&dev->dev);
+
+	spipnx_clk_stop(dd->clk);
+
+	if (dd->io_flags & IORESOURCE_IRQ) {
+		free_irq(dd->irq, dev->dev.bus_id);
+		dd->io_flags &= ~IORESOURCE_IRQ;
+	}
+	if (dd->io_flags & IORESOURCE_MEM) {
+		/*
+		release_mem_region((unsigned long)dd->iostart,
+				   dd->ioend - dd->iostart + 1);
+		*/
+		dd->io_flags &= ~IORESOURCE_MEM;
+	}
+	if (dd->io_flags & IORESOURCE_DMA) {
+		spipnx_release_dma(&dd->dma_channel);
+		dd->io_flags &= ~IORESOURCE_DMA;
+	}
+
+	spipnx_clk_free(dd->clk);
+
+#ifdef DEBUG
+	if (dd->io_flags)
+		printk(KERN_ERR
+		       "Oops, dd_ioflags for driver data %p is still 0x%x!\n",
+		       dd, dd->io_flags);
+#endif
+	unlock_device(&dev->dev);
+}
+
+static void
+spi_pnx_chipselect(struct spi_device *spi, int is_on)
+{
+	spipnx_arch_cs(spi, is_on);
+}
+
+static int spi_pnx_xfer(struct spi_device *spidev, struct spi_transfer *t)
+{
+	u8* dat;
+	int len;
+	struct device *dev;
+	struct spi_pnx_data *dd;
+	vhblas_spiregs* regs;
+	int i;
+	u32 stat, fc, con, timeout;
+	struct spipnx_dma_transfer_t params;
+
+	dev = &spidev->dev;
+	dd = dev_get_drvdata(spidev->master->cdev.dev);
+	regs = dd->spi_regs;
+	len = t->len;
+
+	/* REVISIT */
+	con = regs->con;
+	regs->global = SPI_GLOBAL_BLRES_SPI | SPI_GLOBAL_SPI_ON;
+	udelay(10);
+	regs->global = SPI_GLOBAL_SPI_ON;
+	for (timeout = 10000; timeout >= 0; --timeout)
+		if (dd->spi_regs->global & SPIPNX_GLOBAL_SPI_ON)
+			break;
+	con |= (7<<9);
+	con &= ~(SPIPNX_CON_RXTX);
+	regs->con = con;
+
+	if (spidev->max_speed_hz)
+		spi_pnx_set_clock_rate(spidev->max_speed_hz / 1000, regs);
+
+	regs->stat |= 0x100;
+	regs->con |= SPI_CON_SPI_BIDIR | SPI_CON_SPI_BHALT | SPI_CON_SPI_BPOL |
+		SPI_CON_THR | SPI_CON_MS;
+
+	if (t->tx_buf) {
+		dat = (u8 *)t->tx_buf;
+		regs->con |= SPIPNX_CON_RXTX;
+		regs->ier |= SPIPNX_IER_EOT;
+		regs->con &= ~SPIPNX_CON_SHIFT_OFF;
+		regs->frm = len;
+
+		if (dd->dma_mode && len >= FIFO_CHUNK_SIZE) {
+			void *dmasafe = NULL;
+			if (t->tx_dma)
+				params.dma_buffer = t->tx_dma;
+			else {
+				dmasafe = kmalloc(len, SLAB_KERNEL);
+				if (!dmasafe) {
+					len = 0;
+					goto out;
+				}
+				params.dma_buffer = dma_map_single(dev->parent, dmasafe, len, DMA_TO_DEVICE);
+				memcpy(dmasafe, dat, len);
+			}
+			params.saved_ll = NULL;
+			params.saved_ll_dma = 0;
+
+			spipnx_setup_dma(dd->iostart, dd->slave_nr, dd->dma_channel,
+				 DMA_MODE_WRITE, &params, len);
+			init_completion (&dd->threshold);
+			spipnx_start_dma(dd->dma_channel);
+			wait_for_completion (&dd->threshold);
+			spipnx_stop_dma(dd->dma_channel, &params);
+			if (dmasafe) {
+				dma_unmap_single(dev->parent, params.dma_buffer, len, DMA_TO_DEVICE);
+				kfree(dmasafe);
+			}
+		} else {
+			regs->con &= ~SPIPNX_CON_SHIFT_OFF;
+			for (i = 0; i < len ; i ++ ) {
+				while (regs->stat & 0x04)
+		       			continue;
+				regs->dat = *dat;
+				dat ++;
+			}
+		}
+		while ((regs->stat & SPIPNX_STAT_EOT) == 0)
+			continue;
+	}
+
+	if (t->rx_buf) {
+		int stopped;
+		u8 c;
+		void *dmasafe = NULL;
+
+		dat = t->rx_buf;
+		regs->con &= ~SPIPNX_CON_RXTX;
+		regs->ier |= SPIPNX_IER_EOT;
+		regs->con &= ~SPIPNX_CON_SHIFT_OFF;
+		regs->frm = len;
+
+		regs->dat = 0;  /* kick off read */
+
+		if (dd->dma_mode && len > FIFO_CHUNK_SIZE - 1) {
+			if (t->rx_dma)
+				params.dma_buffer = t->rx_dma;
+			else {
+				dmasafe = kmalloc(len, SLAB_KERNEL);
+				if (!dmasafe) {
+					len = 0;
+					goto out;
+				}
+				params.dma_buffer = dma_map_single(dev->parent, dmasafe, len - (FIFO_CHUNK_SIZE - 1), DMA_FROM_DEVICE);
+			}
+			params.saved_ll = NULL;
+			params.saved_ll_dma = 0;
+
+			spipnx_setup_dma(dd->iostart, dd->slave_nr, dd->dma_channel,
+				 DMA_MODE_READ, &params, len - (FIFO_CHUNK_SIZE - 1));
+			init_completion (&dd->threshold);
+			spipnx_start_dma(dd->dma_channel);
+			wait_for_completion (&dd->threshold);
+			spipnx_stop_dma(dd->dma_channel, &params);
+			if (dmasafe) {
+				memcpy(dat, dmasafe, len - (FIFO_CHUNK_SIZE - 1));
+				dma_unmap_single(dev->parent, params.dma_buffer, len - (FIFO_CHUNK_SIZE - 1), DMA_FROM_DEVICE);
+				kfree(dmasafe);
+			}
+			dat = dat + len - (FIFO_CHUNK_SIZE - 1);
+			len = FIFO_CHUNK_SIZE - 1;
+		}
+
+               	stopped = 0;
+                stat = 0;
+       	        for(i = 0; i < len; ) {
+			unsigned long irq_flags;
+			local_irq_save(irq_flags);
+
+			stat = dd->spi_regs->stat;;
+			fc = dd->spi_regs->frm;
+			/* has hardware finished ? */
+			if(fc == 0) {
+				regs->con |= SPIPNX_CON_SHIFT_OFF;
+				stopped = 1;
+			}
+			/* FIFO not empty and hardware not about to finish */
+			if((!(stat & SPI_STAT_SPI_BE)) && ((fc > 4) || (stopped ))) {
+				*dat++= c = dd->spi_regs->dat;
+				i++;
+			}
+			if((stat & SPI_STAT_SPI_BF) && (!stopped)) {
+				*dat++= c = dd->spi_regs->dat;
+				i++;
+			}
+			local_irq_restore(irq_flags);
+		 }
+	}
+out:
+	return len;
+}
+
+static int spi_pnx_setup(struct spi_device *spi)
+{
+	return 0;
+}
+
+static int spi_pnx_probe(struct device *device)
+{
+	struct spi_master	*master;
+	struct spi_pnx_data	*data;
+	struct spi_bitbang	*pnx;
+	int rc = 0;
+
+	printk("spi probe called\n");
+	master = spi_alloc_master(device, sizeof *data);
+	if (!master) {
+		rc = -ENOMEM;
+		goto out;
+	}
+	master->setup = &spi_pnx_setup;
+	master->bus_num = to_platform_device(device)->id;
+
+	pnx = spi_master_get_devdata(master);
+	pnx->master = master;
+	pnx->chipselect = &spi_pnx_chipselect;
+	pnx->txrx_bufs = &spi_pnx_xfer;
+	rc = spi_bitbang_start(pnx);
+	if (rc < 0)
+		goto out;
+
+	data = kzalloc(sizeof *data, GFP_KERNEL);
+	if (!data) {
+		spi_bitbang_stop(pnx);
+		kfree(master);
+		rc = -ENOMEM;
+		goto out;
+	}
+	data->dma_channel = -1;
+	data->slave_nr = -1;
+	data->irq = NO_IRQ;
+	data->master = master;
+	data->dma_mode = 1;
+	dev_set_drvdata(device, data);
+
+	rc = spipnx_request_hardware(data, to_platform_device(device));
+	if (rc < 0) {
+		spi_bitbang_stop(pnx);
+		kfree(master);
+		kfree(data);
+		goto out;
+	}
+out:
+	return rc;
+}
+
+static int spi_pnx_remove(struct device *device)
+{
+	struct spi_pnx_data *data = dev_get_drvdata(device);
+	struct spi_bitbang *pnx;
+
+	pnx = spi_master_get_devdata(data->master);
+
+	spipnx_free_hardware(data, to_platform_device(device));
+	spi_bitbang_stop(pnx);
+	kfree(data->master);
+	kfree(data);
+	return 0;
+}
+
+
+static struct device_driver spi_pnx_driver = {
+	.name 		= "spipnx",
+	.bus		= &platform_bus_type,
+	.probe		= spi_pnx_probe,
+	.remove		= spi_pnx_remove,
+};
+
+static int __init spi_pnx_init(void)
+{
+	return driver_register(&spi_pnx_driver);
+}
+
+static void __exit spi_pnx_cleanup(void)
+{
+	driver_unregister(&spi_pnx_driver);
+}
+
+module_init(spi_pnx_init);
+module_exit(spi_pnx_cleanup);
Index: linux-2.6/include/asm-arm/arch-pnx4008/spi.h
===================================================================
--- /dev/null
+++ linux-2.6/include/asm-arm/arch-pnx4008/spi.h
@@ -0,0 +1,370 @@
+/*
+ * include/asm-arm/arch-pnx4008/spi.h
+ *
+ * Author: Vitaly Wool <[email protected]>
+ *
+ * PNX4008-specific tweaks for SPI block
+ *
+ * 2005 (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.
+ */
+#ifndef __ASM_ARCH_SPI_H__
+#define __ASM_ARCH_SPI_H__
+
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include <asm/dma.h>
+
+#include <asm/arch/dma.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/pm.h>
+#include <asm/arch/gpio.h>
+
+#include <asm/hardware/clock.h>
+
+#define SPI_ADR_OFFSET_GLOBAL              0x0000	/* R/W - Global control register */
+#define SPI_ADR_OFFSET_CON                 0x0004	/* R/W - Control register */
+#define SPI_ADR_OFFSET_FRM                 0x0008	/* R/W - Frame count register */
+#define SPI_ADR_OFFSET_IER                 0x000C	/* R/W - Interrupt enable register */
+#define SPI_ADR_OFFSET_STAT                0x0010	/* R/W - Status register */
+#define SPI_ADR_OFFSET_DAT                 0x0014	/* R/W - Data register */
+#define SPI_ADR_OFFSET_DAT_MSK             0x0018	/*  W  - Data register for using mask mode */
+#define SPI_ADR_OFFSET_MASK                0x001C	/* R/W - Mask register */
+#define SPI_ADR_OFFSET_ADDR                0x0020	/* R/W - Address register */
+#define SPI_ADR_OFFSET_TIMER_CTRL_REG      0x0400	/* R/W - IRQ timer control register */
+#define SPI_ADR_OFFSET_TIMER_COUNT_REG     0x0404	/* R/W - Timed interrupt period register */
+#define SPI_ADR_OFFSET_TIMER_STATUS_REG    0x0408	/* R;R/C - RxDepth and interrupt status register */
+
+/* Bit definitions for SPI_GLOBAL register */
+#define SPI_GLOBAL_BLRES_SPI               0x00000002	/*  R/W - Software reset, active high */
+#define SPI_GLOBAL_SPI_ON                  0x00000001	/* R/W - SPI interface on */
+
+/* Bit definitions for SPI_CON register */
+#define SPI_CON_SPI_BIDIR                  0x00800000	/* R/W - SPI data in bidir. mux */
+#define SPI_CON_SPI_BHALT                  0x00400000	/* R/W - Halt control */
+#define SPI_CON_SPI_BPOL                   0x00200000	/* R/W - Busy signal active polarity */
+#define SPI_CON_SPI_SHFT                   0x00100000	/* R/W - Data shifting enable in mask mode */
+#define SPI_CON_SPI_MSB                    0x00080000	/* R/W - Transfer LSB or MSB first */
+#define SPI_CON_SPI_EN                     0x00040000	/* R/W - SPULSE signal enable */
+#define SPI_CON_SPI_MODE                   0x00030000	/* R/W - SCK polarity and phase modes */
+#define SPI_CON_SPI_MODE3                  0x00030000	/* R/W - SCK polarity and phase mode 3 */
+#define SPI_CON_SPI_MODE2                  0x00020000	/* R/W - SCK polarity and phase mode 2 */
+#define SPI_CON_SPI_MODE1                  0x00010000	/* R/W - SCK polarity and phase mode 1 */
+#define SPI_CON_SPI_MODE0                  0x00000000	/* R/W - SCK polarity and phase mode 0 */
+#define SPI_CON_RxTx                       0x00008000	/* R/W - Transfer direction */
+#define SPI_CON_THR                        0x00004000	/* R/W - Threshold selection */
+#define SPI_CON_SHIFT_OFF                  0x00002000	/* R/W - Inhibits generation of clock pulses on sck_spi pin */
+#define SPI_CON_BITNUM                     0x00001E00	/* R/W - No of bits to tx or rx in one block transfer */
+#define SPI_CON_CS_EN                      0x00000100	/* R/W - Disable use of CS in slave mode */
+#define SPI_CON_MS                         0x00000080	/* R/W - Selection of master or slave mode */
+#define SPI_CON_RATE                       0x0000007F	/* R/W - Transmit/receive rate */
+
+#define SPI_CON_RATE_0                     0x00
+#define SPI_CON_RATE_1                     0x01
+#define SPI_CON_RATE_2                     0x02
+#define SPI_CON_RATE_3                     0x03
+#define SPI_CON_RATE_4                     0x04
+#define SPI_CON_RATE_13                     0x13
+
+/* Bit definitions for SPI_FRM register */
+#define SPI_FRM_SPIF                       0x0000FFFF	/* R/W - Number of frames to be transfered */
+
+/* Bit definitions for SPI_IER register */
+#define SPI_IER_SPI_INTCS                  0x00000004	/* R/W - SPI CS level change interrupt enable  */
+#define SPI_IER_SPI_INTEOT                 0x00000002	/* R/W - End of transfer interrupt enable */
+#define SPI_IER_SPI_INTTHR                 0x00000001	/* R/W - FIFO threshold interrupt enable */
+
+/* Bit definitions for SPI_STAT register */
+#define SPI_STAT_SPI_INTCLR                0x00000100	/* R/WC - Clear interrupt */
+#define SPI_STAT_SPI_EOT                   0x00000080	/* R/W - End of transfer */
+#define SPI_STAT_SPI_BSY_SL                0x00000040	/* R/W - Status of the input pin spi_busy */
+#define SPI_STAT_SPI_CSL                   0x00000020	/* R/W - Indication of the edge on SPI_CS that caused an int.  */
+#define SPI_STAT_SPI_CSS                   0x00000010	/* R/W - Level of SPI_CS has changed in slave mode */
+#define SPI_STAT_SPI_BUSY                  0x00000008	/* R/W - A data transfer is ongoing  */
+#define SPI_STAT_SPI_BF                    0x00000004	/* R/W - FIFO is full */
+#define SPI_STAT_SPI_THR                   0x00000002	/* R/W - No of entries in Tx/Rx FIFO */
+#define SPI_STAT_SPI_BE                    0x00000001	/* R/W - FIFO is empty */
+
+/* Bit definitions for SPI_DAT register */
+#define SPI_DAT_SPID                       0x0000FFFF	/* R/W - SPI data bits  */
+
+/* Bit definitions for SPI_DAT_MSK register */
+#define SPI_DAT_MSK_SPIDM                  0x0000FFFF	/*  W  - SPI data bits to send using the masking mode */
+
+/* Bit definitions for SPI_MASK register */
+#define SPI_MASK_SPIMSK                    0x0000FFFF	/* R/W - Masking bits used for validating data bits */
+
+/* Bit definitions for SPI_ADDR register */
+#define SPI_ADDR_SPIAD                     0x0000FFFF	/* R/W - Address bits to add to the data bits */
+
+/* Bit definitions for SPI_TIMER_CTRL_REG register */
+#define SPI_TIMER_CTRL_REG_TIRQE           0x00000004	/* R/W - Timer interrupt enable */
+#define SPI_TIMER_CTRL_REG_PIRQE           0x00000002	/* R/W - Peripheral interrupt enable */
+#define SPI_TIMER_CTRL_REG_MODE            0x00000001	/* R/W - Mode */
+
+/* Bit definitions for SPI_TIMER_COUNT_REG register */
+#define SPI_TIMER_COUNT_REG                0x0000FFFF	/* R/W - Timed interrupt period */
+
+/* Bit definitions for SPI_TIMER_STATUS_REG register */
+#define SPI_TIMER_STATUS_REG_INT_STAT      0x00008000	/* R/C - Interrupt status */
+#define SPI_TIMER_STATUS_REG_FIFO_DEPTH    0x0000007F	/*  R  - FIFO depth value (for debug purpose) */
+
+#define SPIPNX_STAT_EOT SPI_STAT_SPI_EOT
+#define SPIPNX_STAT_THR SPI_STAT_SPI_THR
+#define SPIPNX_IER_EOT SPI_IER_SPI_INTEOT
+#define SPIPNX_IER_THR SPI_IER_SPI_INTTHR
+#define SPIPNX_STAT_CLRINT SPI_STAT_SPI_INTCLR
+
+#define SPIPNX_GLOBAL_RESET_SPI SPI_GLOBAL_BLRES_SPI
+#define SPIPNX_GLOBAL_SPI_ON 	SPI_GLOBAL_SPI_ON
+#define SPIPNX_CON_MS		SPI_CON_MS
+#define SPIPNX_CON_BIDIR	SPI_CON_SPI_BIDIR
+#define SPIPNX_CON_RXTX		SPI_CON_RxTx
+#define SPIPNX_CON_THR		SPI_CON_THR
+#define SPIPNX_CON_SHIFT_OFF	SPI_CON_SHIFT_OFF
+#define SPIPNX_DATA		SPI_ADR_OFFSET_DAT
+#define SPI_PNX4008_CLOCK_IN  	104000000
+#define SPI_PNX4008_CLOCK 	2670000
+#define SPIPNX_CLOCK ((((SPI_PNX4008_CLOCK_IN / SPI_PNX4008_CLOCK) - 2) / 2) & SPI_CON_RATE_MASK )
+#define SPIPNX_DATA_REG(s) ( (u32)(s) + SPIPNX_DATA )
+
+typedef volatile struct {
+	u32 global;		/* 0x000             */
+	u32 con;		/* 0x004             */
+	u32 frm;		/* 0x008             */
+	u32 ier;		/* 0x00C             */
+	u32 stat;		/* 0x010             */
+	u32 dat;		/* 0x014             */
+	u32 dat_msk;		/* 0x018             */
+	u32 mask;		/* 0x01C             */
+	u32 addr;		/* 0x020             */
+	u32 _d0[(SPI_ADR_OFFSET_TIMER_CTRL_REG -
+		 (SPI_ADR_OFFSET_ADDR + sizeof(u32))) / sizeof(u32)];
+	u32 timer_ctrl;		/* 0x400             */
+	u32 timer_count;	/* 0x404             */
+	u32 timer_status;	/* 0x408             */
+}
+vhblas_spiregs, *pvhblas_spiregs;
+
+#define SPI_CON_RATE_MASK			0x7f
+
+static void spipnx_arch_set_clock_rate( int clock_khz, int ref_clock_khz, vhblas_spiregs* regs )
+{
+	u32 spi_clock;
+
+	printk( "ref clock = %d, requested clock = %d\n", ref_clock_khz, clock_khz);
+	if(!clock_khz)
+		spi_clock = ref_clock_khz < 104000 ? 0 : 1;
+	else
+		spi_clock = (ref_clock_khz/1000) / (2*(clock_khz/1000) + 2);
+	regs->con &= ~SPI_CON_RATE_MASK;
+	printk("SPI clock = %x\n", spi_clock);
+	regs->con |= spi_clock;
+}
+
+static inline void spi_pnx_set_clock_rate(int saved_clock_khz, vhblas_spiregs *regs)
+{
+	struct clk *div_clk= clk_get(0, "hclk_ck");
+	struct clk *arm_clk = clk_get(0, "ck_pll4");
+	if (!IS_ERR(arm_clk) && !IS_ERR(div_clk))
+		spipnx_arch_set_clock_rate(
+			saved_clock_khz,
+			clk_get_rate(arm_clk) / clk_get_rate(div_clk),
+			regs);
+	if (!IS_ERR(arm_clk))
+		clk_put(arm_clk);
+	if (!IS_ERR(div_clk))
+		clk_put(div_clk);
+}
+
+static inline void spipnx_clk_stop(void *clk_data)
+{
+	struct clk *clk = (struct clk *)clk_data;
+	if (clk && !IS_ERR(clk)) {
+		clk_set_rate(clk, 0);
+		clk_put(clk);
+	}
+}
+
+static inline void spipnx_clk_start(void *clk_data)
+{
+	struct clk *clk = (struct clk *)clk_data;
+	if (clk && !IS_ERR(clk)) {
+		clk_set_rate(clk, 1);
+		clk_put(clk);
+	}
+}
+
+static inline void *spipnx_clk_init(struct platform_device *dev)
+{
+	struct clk *clk = clk_get(0, dev->id == 1 ? "spi0_ck" : "spi1_ck");
+	if (IS_ERR(clk))
+		printk("spi%d_ck pointer err\n", dev->id);
+	return clk;
+}
+
+static inline void spipnx_clk_free( void* clk )
+{
+	clk_put( clk );
+}
+
+static inline void spipnx_arch_cs(struct spi_device *spi, int is_on)
+{
+	switch (spi->chip_select)
+	{
+	case 0:
+		printk("setting pin %x to %d\n", GPO_02, !is_on);
+		pnx4008_gpio_write_pin(GPO_02, !is_on);
+		break;
+	case 1:
+		printk( "setting pin %x to %d\n", GPIO_03, !is_on);
+		pnx4008_gpio_write_pin(GPIO_03, !is_on);
+		break;
+	}
+}
+
+static inline int spipnx_request_dma(
+		int *dma_channel, char *name,
+		void (*handler)( int, int, void*, struct pt_regs*),
+		void *dma_context)
+{
+	int err;
+
+	if (*dma_channel != -1) {
+		err = *dma_channel;
+		goto out;
+	}
+	err = pnx4008_request_channel(name, -1, handler, dma_context);
+	if (err >= 0) {
+		*dma_channel = err;
+	}
+out:
+	return err < 0 ? err : 0;
+}
+
+static inline void spipnx_release_dma(int *dma_channel)
+{
+	if (*dma_channel >= 0) {
+		pnx4008_free_channel(*dma_channel);
+	}
+	*dma_channel = -1;
+}
+
+static inline void spipnx_start_dma(int dma_channel)
+{
+	pnx4008_dma_ch_enable(dma_channel);
+}
+
+struct spipnx_dma_transfer_t
+{
+	dma_addr_t dma_buffer;
+	void *saved_ll;
+	u32 saved_ll_dma;
+};
+
+static inline void spipnx_stop_dma(int dma_channel, void* dev_buf)
+{
+	struct spipnx_dma_transfer_t *buf = dev_buf;
+
+	pnx4008_dma_ch_disable(dma_channel);
+	if (buf->saved_ll) {
+		pnx4008_free_ll(buf->saved_ll_dma, buf->saved_ll);
+		buf->saved_ll = NULL;
+	}
+}
+
+static inline int spipnx_setup_dma(u32 iostart, int slave_nr, int dma_channel,
+				int mode, void* params, int len)
+{
+	int err = 0;
+	struct spipnx_dma_transfer_t *buff = params;
+
+	pnx4008_dma_config_t cfg;
+	pnx4008_dma_ch_config_t ch_cfg;
+	pnx4008_dma_ch_ctrl_t ch_ctrl;
+
+	memset(&cfg, 0, sizeof(cfg));
+
+	if (mode == DMA_MODE_READ) {
+		cfg.dest_addr = buff->dma_buffer; /* buff->dma_buffer; */
+		cfg.src_addr = (u32) SPIPNX_DATA_REG(iostart);
+		ch_cfg.flow_cntrl = FC_PER2MEM_DMA;
+		ch_cfg.src_per = slave_nr;
+		ch_cfg.dest_per = 0;
+		ch_ctrl.di = 1;
+		ch_ctrl.si = 0;
+	} else if (mode == DMA_MODE_WRITE) {
+		cfg.src_addr = buff->dma_buffer; /* buff->dma_buffer; */
+		cfg.dest_addr = (u32) SPIPNX_DATA_REG(iostart);
+		ch_cfg.flow_cntrl = FC_MEM2PER_DMA;
+		ch_cfg.dest_per = slave_nr;
+		ch_cfg.src_per = 0;
+		ch_ctrl.di = 0;
+		ch_ctrl.si = 1;
+	} else {
+		err = -EINVAL;
+	}
+
+	ch_cfg.halt = 0;
+	ch_cfg.active = 1;
+	ch_cfg.lock = 0;
+	ch_cfg.itc = 1;
+	ch_cfg.ie = 1;
+	ch_ctrl.tc_mask = 1;
+	ch_ctrl.cacheable = 0;
+	ch_ctrl.bufferable = 0;
+	ch_ctrl.priv_mode = 1;
+	ch_ctrl.dest_ahb1 = 0;
+	ch_ctrl.src_ahb1 = 0;
+	ch_ctrl.dwidth = WIDTH_BYTE;
+	ch_ctrl.swidth = WIDTH_BYTE;
+	ch_ctrl.dbsize = 1;
+	ch_ctrl.sbsize = 1;
+	ch_ctrl.tr_size = len;
+	if (0 > (err = pnx4008_dma_pack_config(&ch_cfg, &cfg.ch_cfg))) {
+		goto out;
+	}
+	if (0 > (err = pnx4008_dma_pack_control(&ch_ctrl, &cfg.ch_ctrl))) {
+		if ( err == -E2BIG ) {
+			printk( KERN_DEBUG"buffer is too large, splitting\n" );
+			pnx4008_dma_split_head_entry(&cfg, &ch_ctrl);
+			buff->saved_ll = cfg.ll;
+			buff->saved_ll_dma = cfg.ll_dma;
+			err = 0;
+		} else {
+			goto out;
+		}
+	}
+	err = pnx4008_config_channel(dma_channel, &cfg);
+out:
+	return err;
+}
+
+static inline void spipnx_start_event_init(int id)
+{
+	int se = id==0 ? SE_SPI1_DATAIN_INT : SE_SPI2_DATAIN_INT;
+
+	/*setup wakeup interrupt*/
+	start_int_set_rising_edge(se);
+	start_int_ack(se);
+	start_int_umask(se);
+}
+
+static inline void spipnx_start_event_deinit(int id)
+{
+	int se = id==0 ? SE_SPI1_DATAIN_INT : SE_SPI2_DATAIN_INT;
+
+	start_int_mask(se);
+}
+
+struct spipnx_wifi_params {
+	int gpio_cs_line;
+};
+
+#endif /* __ASM_ARCH_SPI_H___ */
+
Index: linux-2.6/drivers/spi/spi_pnx.h
===================================================================
--- /dev/null
+++ linux-2.6/drivers/spi/spi_pnx.h
@@ -0,0 +1,60 @@
+/*
+ * SPI support for Philips SPI bus on PNX's.
+ *
+ * Author: Dennis Kovalev <[email protected]>
+ *
+ * 2004 (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.
+ */
+
+#ifndef _SPI_PNX_BUS_DRIVER
+#define _SPI_PNX_BUS_DRIVER
+
+#include <linux/spi/spi.h>
+#include <asm/arch/platform.h>
+#include <asm/arch/dma.h>
+#include <asm/dma.h>
+#include <asm/arch/spi.h>
+
+/* structures */
+struct spi_pnx_data {
+	unsigned io_flags;
+
+	int irq;
+
+	int dma_mode;
+	int dma_channel;
+	int slave_nr;
+
+	u32 iostart, ioend;
+	vhblas_spiregs *spi_regs;
+
+	struct clk *clk;
+	int clk_id[4];
+
+	int state;
+	u32 saved_clock;
+
+	struct completion op_complete;
+	struct completion threshold;
+
+	int progress;
+	struct spi_master *master;
+
+	void (*control) (struct spi_device * dev, int code, u32 ctl);
+};
+
+#define SPIPNX_STATE_UNINITIALIZED 0
+#define SPIPNX_STATE_READY         1
+#define SPIPNX_STATE_SUSPENDED     2
+
+#define SPIPNX_IS_READY( bus )  ( (bus)->state == SPIPNX_STATE_READY )
+
+#define FIFO_CHUNK_SIZE		56
+
+#define SPI_ENDIAN_SWAP_NO                 0
+#define SPI_ENDIAN_SWAP_YES                1
+
+#endif				// __SPI_PNX_BUS_DRIVER
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux