summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSheshagiri Shenoy <sshenoy@nvidia.com>2011-03-17 16:47:11 -0700
committerVarun Colbert <vcolbert@nvidia.com>2011-04-01 19:29:16 -0700
commit9d0892e0a0a1570e866ef7aaf1c19fb8356e5ff2 (patch)
tree5d9bef6c11a6caa85dda69542747fe9c988bba40
parent8e70c9ce2c7caaac96b643daa7adb007515ac68b (diff)
ARM: tegra: spi: slave spi driver bug fixes
- the way spi slave driver waits for fifo to be full is fixed. - rx and tx fifo fill/drain logic fixed. - added makefile entry and kconfig entry to enable conditional compiling. bug 785523 Change-Id: Ib01381704aa2bd519a1146a9cd8efe75646efd6f Reviewed-on: http://git-master/r/23416 Reviewed-by: Varun Colbert <vcolbert@nvidia.com> Tested-by: Varun Colbert <vcolbert@nvidia.com>
-rw-r--r--arch/arm/mach-tegra/Kconfig7
-rw-r--r--arch/arm/mach-tegra/Makefile2
-rwxr-xr-xarch/arm/mach-tegra/spi_tegra_slave.c101
3 files changed, 101 insertions, 9 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 5d0bd6afe765..97419f957321 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -68,6 +68,13 @@ config TEGRA_SYSTEM_DMA
Adds system DMA functionality for NVIDIA Tegra SoCs, used by
several Tegra device drivers
+config TEGRA_SPI_SLAVE
+ tristate "Nvidia Tegra SPI slave driver"
+ depends on ARCH_TEGRA
+ select TEGRA_SYSTEM_DMA
+ help
+ SPI slave driver for NVIDIA Tegra SoCs
+
config TEGRA_PWM
tristate "Enable PWM driver"
select HAVE_PWM
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 970c431da188..fe45f5c3fb89 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -53,6 +53,8 @@ obj-$(CONFIG_TEGRA_MC_PROFILE) += tegra2_mc.o
obj-y += nv/
+obj-${CONFIG_TEGRA_SPI_SLAVE} += spi_tegra_slave.o
+
obj-${CONFIG_MACH_HARMONY} += board-harmony.o
obj-${CONFIG_MACH_HARMONY} += board-harmony-pinmux.o
obj-${CONFIG_MACH_HARMONY} += board-harmony-panel.o
diff --git a/arch/arm/mach-tegra/spi_tegra_slave.c b/arch/arm/mach-tegra/spi_tegra_slave.c
index 21fca5bf0a61..eddae51202cc 100755
--- a/arch/arm/mach-tegra/spi_tegra_slave.c
+++ b/arch/arm/mach-tegra/spi_tegra_slave.c
@@ -35,6 +35,9 @@
#include <linux/spi/spi.h>
#include <mach/dma.h>
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#endif
#include <mach/spi.h>
#include <mach/clk.h>
@@ -135,6 +138,7 @@
#define SLINK_TX_FIFO 0x100
#define SLINK_RX_FIFO 0x180
+#define SLINK_FIFO_DEPTH 0x20
static const unsigned long spi_tegra_req_sels[] = {
TEGRA_DMA_REQ_SEL_SL2B1,
@@ -143,7 +147,7 @@ static const unsigned long spi_tegra_req_sels[] = {
TEGRA_DMA_REQ_SEL_SL2B4,
};
-#define BB_LEN 2048
+#define BB_LEN (16384)
#define TX_FIFO_EMPTY_COUNT_MAX SLINK_TX_FIFO_EMPTY_COUNT(0x20)
#define RX_FIFO_FULL_COUNT_ZERO SLINK_RX_FIFO_FULL_COUNT(0)
@@ -193,6 +197,7 @@ struct spi_tegra_data {
bool abort_happen;
u8 g_bits_per_word;
+ struct dentry *debugfs;
};
static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
@@ -219,7 +224,7 @@ static void spi_tegra_clear_status(struct spi_tegra_data *tspi)
if (val & SLINK_ERR) {
val_write |= SLINK_ERR;
- printk("%s ERROR bit set 0x%lx \n", __func__, val);
+ pr_err("%s ERROR bit set 0x%lx\n", __func__, val);
if (val & SLINK_TX_OVF)
val_write |= SLINK_TX_OVF;
if (val & SLINK_RX_OVF)
@@ -240,6 +245,7 @@ static void spi_tegra_go(struct spi_tegra_data *tspi)
{
unsigned long val;
unsigned long test_val;
+ unsigned unused_fifo_size;
wmb();
@@ -263,9 +269,12 @@ static void spi_tegra_go(struct spi_tegra_data *tspi)
/*
* TRM 24.1.1.7 wait for the FIFO to be full
*/
- test_val = spi_tegra_readl(tspi, SLINK_STATUS);
- while (!(test_val & SLINK_TX_FULL))
- test_val = spi_tegra_readl(tspi, SLINK_STATUS);
+ test_val = spi_tegra_readl(tspi, SLINK_STATUS2);
+ unused_fifo_size = (tspi->tx_dma_req.size/4) >= SLINK_FIFO_DEPTH ?
+ 0 :
+ SLINK_FIFO_DEPTH - (tspi->tx_dma_req.size/4);
+ while (SLINK_TX_FIFO_EMPTY_COUNT(test_val) != (unused_fifo_size))
+ test_val = spi_tegra_readl(tspi, SLINK_STATUS2);
spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
}
@@ -290,7 +299,8 @@ static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
val = 0;
for (j = 0; j < tspi->cur_bytes_per_word; j++)
- val |= tx_buf[i + j] << j * 8;
+ val |=
+ tx_buf[i + j] << (tspi->cur_bytes_per_word - j - 1) * 8;
tspi->tx_bb[i / tspi->cur_bytes_per_word] = val;
}
@@ -315,7 +325,8 @@ static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi,
for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
val = tspi->rx_bb[i / tspi->cur_bytes_per_word];
for (j = 0; j < tspi->cur_bytes_per_word; j++)
- rx_buf[i + j] = (val >> (j * 8)) & 0xff;
+ rx_buf[i + j] =
+ (val >> (tspi->cur_bytes_per_word - j - 1) * 8) & 0xff;
}
}
@@ -361,7 +372,7 @@ static void spi_tegra_start_transfer(struct spi_device *spi,
spi_tegra_clear_status(tspi);
val = spi_tegra_readl(tspi, SLINK_COMMAND2);
- val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN;
+ val &= ~(SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN);
if (t->rx_buf)
val |= SLINK_RXEN;
if (t->tx_buf)
@@ -617,6 +628,73 @@ static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+static int spi_show_regs_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define SPI_TEGRA_BUFSIZE 512
+static ssize_t spi_show_regs(struct file *file, char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct spi_tegra_data *tspi;
+ char *buf;
+ u32 len = 0;
+ ssize_t ret;
+ unsigned long flags;
+
+ tspi = file->private_data;
+ buf = kzalloc(SPI_TEGRA_BUFSIZE, GFP_KERNEL);
+ if (!buf)
+ return 0;
+
+ spin_lock_irqsave(&tspi->lock, flags);
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SPI registers:\n");
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SLINK_STATUS 0x%lx\n", spi_tegra_readl(tspi, SLINK_STATUS));
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SLINK_STATUS2 0x%lx\n", spi_tegra_readl(tspi, SLINK_STATUS2));
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SLINK_COMMAND 0x%lx\n", spi_tegra_readl(tspi, SLINK_COMMAND));
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SLINK_COMMAND2 0x%lx\n", spi_tegra_readl(tspi, SLINK_COMMAND2));
+ len += snprintf(buf + len, SPI_TEGRA_BUFSIZE - len,
+ "SLINK_DMA_CTL 0x%lx\n", spi_tegra_readl(tspi, SLINK_DMA_CTL));
+ spin_unlock_irqrestore(&tspi->lock, flags);
+ ret = simple_read_from_buffer(user_buf, count, ppos, buf, len);
+ kfree(buf);
+ return ret;
+}
+
+static const struct file_operations tegra_spi_regs_ops = {
+ .owner = THIS_MODULE,
+ .open = spi_show_regs_open,
+ .read = spi_show_regs,
+};
+
+static int tegra_spi_debugfs_init(struct spi_tegra_data *tspi)
+{
+ char name[20] = {0};
+
+ sprintf(name, "%s.%d", tspi->pdev->name, tspi->pdev->id);
+ tspi->debugfs = debugfs_create_dir(name, NULL);
+ if (!tspi->debugfs)
+ return -ENOMEM;
+ debugfs_create_file("registers", S_IFREG | S_IRUGO,
+ tspi->debugfs, (void *)tspi, &tegra_spi_regs_ops);
+ return 0;
+}
+
+static void tegra_spi_debugfs_remove(struct spi_tegra_data *tspi)
+{
+ if (tspi->debugfs)
+ debugfs_remove_recursive(tspi->debugfs);
+}
+#endif
+
static int __init spi_tegra_probe(struct platform_device *pdev)
{
struct spi_master *master;
@@ -732,6 +810,9 @@ static int __init spi_tegra_probe(struct platform_device *pdev)
tspi->tx_dma_req.dev = tspi;
ret = spi_register_master(master);
+#ifdef CONFIG_DEBUG_FS
+ tegra_spi_debugfs_init(tspi);
+#endif
if (ret < 0)
goto err7;
@@ -766,7 +847,9 @@ static int __devexit spi_tegra_remove(struct platform_device *pdev)
master = dev_get_drvdata(&pdev->dev);
tspi = spi_master_get_devdata(master);
-
+#ifdef CONFIG_DEBUG_FS
+ tegra_spi_debugfs_remove(tspi);
+#endif
tegra_dma_free_channel(tspi->rx_dma);
dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,