summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRich Felker <dalias@libc.org>2016-04-03 05:12:45 +0000
committerRich Felker <dalias@libc.org>2016-04-03 05:12:45 +0000
commitbaeab9654dcfe9f8e83adf469efc274511c41d61 (patch)
tree67bffd30f9a6f534515b5daddaee118036dd1c0d
parent44c9b877d9d632f4448263084c510c0e4ad48ad7 (diff)
downloadlinux-sh-baeab9654dcfe9f8e83adf469efc274511c41d61.tar.gz
spi: add J-Core bitbang driver
Sloppy, not ready for upstream, and not working on SMP.
-rw-r--r--drivers/spi/Kconfig5
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-jcore-bitbang.c251
3 files changed, 257 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 9d8c84bb1544..6bbdb5bca189 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -285,6 +285,11 @@ config SPI_IMX
This enables using the Freescale i.MX SPI controllers in master
mode.
+config SPI_JCORE_BITBANG
+ tristate "J-Core bitbanging SPI Master"
+ depends on OF
+ select SPI_BITBANG
+
config SPI_LM70_LLP
tristate "Parallel port adapter for LM70 eval board (DEVELOPMENT)"
depends on PARPORT
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index fbb255c5a608..76ce48bbe04a 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o
obj-$(CONFIG_SPI_GPIO) += spi-gpio.o
obj-$(CONFIG_SPI_IMG_SPFI) += spi-img-spfi.o
obj-$(CONFIG_SPI_IMX) += spi-imx.o
+obj-$(CONFIG_SPI_JCORE_BITBANG) += spi-jcore-bitbang.o
obj-$(CONFIG_SPI_LM70_LLP) += spi-lm70llp.o
obj-$(CONFIG_SPI_LP8841_RTC) += spi-lp8841-rtc.o
obj-$(CONFIG_SPI_MESON_SPIFC) += spi-meson-spifc.o
diff --git a/drivers/spi/spi-jcore-bitbang.c b/drivers/spi/spi-jcore-bitbang.c
new file mode 100644
index 000000000000..28936a9329bd
--- /dev/null
+++ b/drivers/spi/spi-jcore-bitbang.c
@@ -0,0 +1,251 @@
+/*
+ * SEI SoftCore SPI controller driver
+ *
+ * Copyright (C) 2012 SEI Inc.
+ * by Oleksandr G Zhadan
+ *
+ */
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_bitbang.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#define DRV_NAME "jcore_bitbang_spi"
+
+#define MAX_SPI_SPEED 12500000 /* 12.5 MHz */
+
+#define CTRL_REG 0x0
+#define STAT_REG 0x0
+#define DATA_REG 0x4
+
+#define SPI_NOCHIP_CS 0
+#define SPI_FLASH_CS 1
+#define SPI_CONF_CS 2
+#define SPI_SD_CS 2
+#define SPI_CODEC_CS 3
+
+#define SEI_SPI_CTRL_ACS 0x01
+#define SEI_SPI_CTRL_XMIT 0x02
+#define SEI_SPI_STAT_BUSY 0x02
+#define SEI_SPI_CTRL_CCS 0x04
+#define SEI_SPI_CTRL_LOOP 0x08
+#define SEI_SPI_CTRL_DCS 0x10
+
+#define SEI_SPI_WAIT_RDY_MAX_LOOP 2000000 /* in usec */
+
+static unsigned get_sr(void)
+{
+ unsigned sr;
+ __asm__ __volatile__("stc sr,%0" : "=r"(sr));
+ return sr;
+}
+
+struct sei_spi {
+ /* bitbang has to be first */
+ struct spi_bitbang bitbang;
+
+ void __iomem *base;
+ int len;
+ int count;
+ volatile unsigned int ctrlReg;
+ unsigned int csReg;
+ unsigned int speedReg;
+
+ /* data buffers */
+ const unsigned char *tx;
+ unsigned char *rx;
+};
+
+static void sei_spi_wait_till_ready(struct sei_spi *hw, int timeout)
+{
+ while (timeout--) {
+ hw->ctrlReg = readl(hw->base + STAT_REG);
+ if (!(hw->ctrlReg & SEI_SPI_STAT_BUSY))
+ return;
+ cpu_relax();
+ }
+ pr_err("%s: Timeout..\n", __func__);
+}
+
+static void hw_txbyte(struct sei_spi *hw, unsigned char val)
+{
+ sei_spi_wait_till_ready(hw, SEI_SPI_WAIT_RDY_MAX_LOOP);
+ writel(val, hw->base + DATA_REG);
+ writel(hw->csReg | hw->speedReg | SEI_SPI_CTRL_XMIT, hw->base + CTRL_REG);
+}
+
+static unsigned char hw_rxbyte(struct sei_spi *hw)
+{
+ sei_spi_wait_till_ready(hw, SEI_SPI_WAIT_RDY_MAX_LOOP);
+ return (unsigned char)readl(hw->base + DATA_REG);
+}
+
+static void sei_spi_chipsel(struct spi_device *spi, int value)
+{
+ struct sei_spi *hw = spi_master_get_devdata(spi->master);
+
+// pr_info("%s: CS=%d cpu=%d\n", __func__, value, smp_processor_id());
+ pr_debug("%s: CS=%d\n", __func__, value);
+
+ sei_spi_wait_till_ready(hw, SEI_SPI_WAIT_RDY_MAX_LOOP);
+
+ switch (value) {
+ default:
+ case SPI_NOCHIP_CS:
+ hw->csReg = SEI_SPI_CTRL_ACS;
+ break;
+ case SPI_FLASH_CS:
+ hw->csReg = 0;
+ break;
+ case SPI_SD_CS:
+ hw->csReg = SEI_SPI_CTRL_ACS | SEI_SPI_CTRL_CCS;
+ break;
+ case SPI_CODEC_CS:
+ hw->csReg = SEI_SPI_CTRL_ACS | SEI_SPI_CTRL_DCS;
+ break;
+ }
+
+ writel(hw->csReg | hw->speedReg, hw->base + CTRL_REG);
+}
+
+static void sei_spi_baudrate(struct sei_spi *hw, int speed)
+{
+ hw->speedReg = ((MAX_SPI_SPEED / speed) - 1) << 27;
+ pr_debug("%s: speed=%d pre=0x%x\n", __func__, speed, hw->speedReg);
+// pr_info("%s: speed=%d pre=0x%x\n", __func__, speed, hw->speedReg);
+}
+
+static int sei_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t)
+{
+ return 0;
+}
+
+static int sei_spi_setup(struct spi_device *spi)
+{
+ return 0;
+}
+
+static int sei_spi_txrx(struct spi_device *spi, struct spi_transfer *t)
+{
+ struct sei_spi *hw = spi_master_get_devdata(spi->master);
+ unsigned char rxByte, txByte;
+
+// pr_info("%s: TXRX cpu=%d\n", __func__, smp_processor_id());
+
+ hw->tx = t->tx_buf;
+ hw->rx = t->rx_buf;
+ hw->len = t->len;
+
+ for (hw->count = 0; hw->count < hw->len; hw->count++) {
+ if (hw->tx)
+ txByte = hw->tx[hw->count];
+ else
+ txByte = 0;
+ hw_txbyte(hw, txByte);
+ rxByte = hw_rxbyte(hw);
+ if (hw->rx)
+ hw->rx[hw->count] = rxByte;
+ }
+ return hw->count;
+}
+
+static int sei_spi_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct sei_spi *hw;
+ struct spi_master *master;
+ struct resource *res;
+ int err = -ENODEV;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(struct sei_spi));
+ if (!master)
+ return err;
+
+ /* setup the master state. */
+ master->bus_num = -1; //pdev->id;
+ master->num_chipselect = 2;
+ master->mode_bits = SPI_MODE_3;
+ master->setup = sei_spi_setup;
+ master->transfer = NULL;
+ master->dev.of_node = pdev->dev.of_node;
+
+ hw = spi_master_get_devdata(master);
+ platform_set_drvdata(pdev, hw);
+
+ /* setup the state for the bitbang driver */
+ hw->bitbang.master = spi_master_get(master);
+ if (!hw->bitbang.master)
+ return err;
+
+ hw->bitbang.setup_transfer = sei_spi_setupxfer;
+ hw->bitbang.chipselect = sei_spi_chipsel;
+ hw->bitbang.txrx_bufs = sei_spi_txrx;
+
+ /* find and map our resources */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ goto exit_busy;
+ if (!devm_request_mem_region
+ (&pdev->dev, res->start, resource_size(res), pdev->name))
+ goto exit_busy;
+ hw->base =
+ devm_ioremap_nocache(&pdev->dev, res->start, resource_size(res));
+ if (!hw->base)
+ goto exit_busy;
+
+ sei_spi_baudrate(hw, 400000);
+
+ pdev->dev.dma_mask = 0;
+ /* register our spi controller */
+ err = spi_bitbang_start(&hw->bitbang);
+ if (err)
+ goto exit;
+ dev_info(&pdev->dev, "base %p, noirq\n", hw->base);
+
+ return 0;
+
+exit_busy:
+ err = -EBUSY;
+exit:
+ platform_set_drvdata(pdev, NULL);
+ spi_master_put(master);
+ return err;
+}
+
+static int sei_spi_remove(struct platform_device *dev)
+{
+ struct sei_spi *hw = platform_get_drvdata(dev);
+ struct spi_master *master = hw->bitbang.master;
+
+ spi_bitbang_stop(&hw->bitbang);
+ platform_set_drvdata(dev, NULL);
+ spi_master_put(master);
+ return 0;
+}
+
+static const struct of_device_id jcore_bitbang_of_match[] = {
+ { .compatible = "jcore,soc-spi-0.1" },
+ {},
+};
+
+static struct platform_driver jcore_bitbang_driver = {
+ .probe = sei_spi_probe,
+ .remove = sei_spi_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = NULL,
+ .of_match_table = jcore_bitbang_of_match,
+ },
+};
+
+module_platform_driver(jcore_bitbang_driver);
+
+MODULE_DESCRIPTION("J-Core bitbang SPI driver");
+MODULE_AUTHOR("Oleksandr G Zhadan <ozhmail@gmail.com>");
+MODULE_ALIAS("platform:" DRV_NAME);