lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite for Android: free password hash cracker in your pocket
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-Id: <E1ZEEtz-0000pe-7R@luxik.cdi.cz>
Date:	Sun, 12 Jul 2015 11:20:49 +0200
From:	Martin Devera <devik@....cz>
To:	unlisted-recipients:; (no To-header on input)
Subject: [PATCH 1/2] Add NXP LPC32XX SPI driver

This SPI device is found at least on NXP LPC32XX ARM
family. It has 64 entry FIFO and is quite fast when
using only IRQ.
The driver uses generic SPI and OF frameworks to
minimize its size.
It is tested in HW (SPI flash with JFFS2).
---
 .../devicetree/bindings/spi/spi_lpc32xx.txt        |   32 +++
 drivers/spi/Kconfig                                |    8 +
 drivers/spi/Makefile                               |    1 +
 drivers/spi/spi-lpc32xx.c                          |  265 ++++++++++++++++++++
 4 files changed, 306 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/spi/spi_lpc32xx.txt
 create mode 100644 drivers/spi/spi-lpc32xx.c

diff --git a/Documentation/devicetree/bindings/spi/spi_lpc32xx.txt b/Documentation/devicetree/bindings/spi/spi_lpc32xx.txt
new file mode 100644
index 0000000..aef86f4
--- /dev/null
+++ b/Documentation/devicetree/bindings/spi/spi_lpc32xx.txt
@@ -0,0 +1,32 @@
+NXP LPC32XX SPI controller
+
+Required properties:
+- compatible : "nxp,lpc3220-spi"
+- reg : Offset and length of the register set for the device
+- interrupts : Should contain SPI controller interrupt
+- cs-gpios : should specify GPIOs used for chipselects.
+  The gpios will be referred to as reg = <index> in the SPI child nodes.
+
+SPI slave nodes must be children of the SPI master node and can
+contain the following properties.
+
+	spi-max-frequency = <hz>;
+	spi-cpol;
+	spi-cpha;
+
+Example:
+		spi1: spi@...88000 {
+			status="okay";
+			interrupts = <55 0>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			num-cs = <1>;
+			cs-gpios = <&gpio 3 5 1>;
+			m25p80@1 {
+				compatible = "st,m25p80";
+				reg = <0>;
+				spi-max-frequency = <1000000>;
+				spi-cpol;
+				spi-cpha;
+			};
+		};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 72b0590..373f013 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -375,6 +375,14 @@ config SPI_ORION
 	help
 	  This enables using the SPI master controller on the Orion chips.
 
+config SPI_LPC32XX
+	tristate "NXP LPC32XX SPI controller"
+	depends on ARCH_LPC32XX
+	help
+	  This selects SPI controller found on NXP LPC32XX SoC. There
+	  are also ARM AMBA PL022 SSP controllers, but NXP's SPI one
+	  has 64 entry FIFOs as opossed to 8 entry in SSP.
+
 config SPI_PL022
 	tristate "ARM AMBA PL022 SSP controller"
 	depends on ARM_AMBA
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d8cbf65..62a09bd 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -44,6 +44,7 @@ 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_LM70_LLP)		+= spi-lm70llp.o
+obj-$(CONFIG_SPI_LPC32XX)		+= spi-lpc32xx.o
 obj-$(CONFIG_SPI_MESON_SPIFC)		+= spi-meson-spifc.o
 obj-$(CONFIG_SPI_MPC512x_PSC)		+= spi-mpc512x-psc.o
 obj-$(CONFIG_SPI_MPC52xx_PSC)		+= spi-mpc52xx-psc.o
diff --git a/drivers/spi/spi-lpc32xx.c b/drivers/spi/spi-lpc32xx.c
new file mode 100644
index 0000000..5c6b1ca
--- /dev/null
+++ b/drivers/spi/spi-lpc32xx.c
@@ -0,0 +1,265 @@
+/*
+ *  LPC32XX SPI bus driver
+ *
+ *  Copyright (C) 2015 Martin Devera <devik@....cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/of_gpio.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME	"spi-lpc32xx"
+
+#define _BIT(n) (1<<(n))
+#define SPI_GLOB_RST        _BIT(1)     /* SPI interfase sw reset */
+#define SPI_GLOB_ENABLE     _BIT(0)     /* SPI interface enable */
+
+#define SPI_CON_UNIDIR      _BIT(23)    /* DATIO pin dir control */
+#define SPI_CON_BHALT       _BIT(22)    /* Busy halt control */
+#define SPI_CON_BPOL        _BIT(21)    /* Busy line polarity */
+#define SPI_CON_MSB         _BIT(19)    /* MSB/LSB control */
+#define SPI_CON_CPOL        _BIT(17)    /* CPOL control*/
+#define SPI_CON_CPHA        _BIT(16)    /* CPHA control*/
+#define SPI_CON_MODE00      0           /* mode = 00 */
+#define SPI_CON_MODE01      _BIT(16)    /* mode = 01 */
+#define SPI_CON_MODE10      _BIT(17)    /* mode = 10 */
+#define SPI_CON_MODE11      _SBF(16,0x3)/* mode = 11 */
+#define SPI_CON_RXTX        _BIT(15)    /* Tx/Rx control */
+#define SPI_CON_THR         _BIT(14)    /* FIFO threshold control */
+#define SPI_CON_SHIFT_OFF   _BIT(13)    /* SPI clock control */
+#define SPI_CON_BITNUM(n)   _SBF(9,((n-1)&0xF)) /* number of bits ctr */
+#define SPI_CON_MS          _BIT(7)     /* Master mode control */
+#define SPI_CON_RATE(n)     (n & 0x7F)  /* Transfer rate control */
+
+#define SPI_IER_INTEOT      _BIT(1)     /* End of transfer int en */
+#define SPI_IER_INTTHR      _BIT(0)     /* FIFO threshold int en */
+
+#define SPI_STAT_INTCLR     _BIT(8)     /* SPI interrupt clear */
+#define SPI_STAT_EOT        _BIT(7)     /* SPI End of Transfer flag */
+#define SPI_STAT_BUSYLEV    _BIT(6)     /* SPI BUSY level */
+#define SPI_STAT_SHIFTACT   _BIT(3)     /* Shift active flag */
+#define SPI_STAT_BF         _BIT(2)     /* FIFO full int flag */
+#define SPI_STAT_THR        _BIT(1)     /* FIFO threshold int flag */
+#define SPI_STAT_BE         _BIT(0)     /* FIFO empty int flag */
+#define SYNCIO_FRMLEN(x)	((x) << 8)
+#define SYNCIO_TXFRMEN		(1 << 14)
+
+#define SPI_GLOBAL(r)	(r + 0x00)
+#define SPI_CON(r)	(r + 0x04)
+#define SPI_FRM(r)	(r + 0x08)
+#define SPI_IER(r)	(r + 0x0c)
+#define SPI_STAT(r)	(r + 0x10)
+#define SPI_DAT(r)	(r + 0x14)
+
+struct spi_lpc32xx_data {
+	void __iomem		*syncio;
+	struct clk		*spi_clk;
+
+	u8			*buf;
+	unsigned int		bpw;
+	unsigned int		mode;
+	int			len;
+	int			is_rd;
+	unsigned long		rate;	/* master clock rate */
+};
+
+static int spi_lpc32xx_setup(struct spi_device *spi)
+{
+	/* We are expect that SPI-device is not selected */
+	//gpio_direction_output(spi->cs_gpio, !(spi->mode & SPI_CS_HIGH));
+//
+	printk("#### Setup spi\n");
+	return 0;
+}
+
+static int spi_lpc32xx_start(struct spi_lpc32xx_data *hw)
+{
+	int sz,mode;
+	sz = hw->len;
+	if (sz > 64) sz = 64;
+
+	mode = hw->mode;
+	mode |= (hw->bpw-1)<<9;
+//	printk("in spi_lpc32xx_start sz=%d buf=%p isrd=%d mode=%X io=%p stat=%X\n",
+//			sz,hw->buf,hw->is_rd,mode,hw->syncio,readl(SPI_STAT(hw->syncio)));
+	writel(mode,SPI_CON(hw->syncio));
+	writel(sz,SPI_FRM(hw->syncio));
+	writel(SPI_IER_INTEOT,SPI_IER(hw->syncio));
+	if (hw->is_rd) {
+		readl(SPI_DAT(hw->syncio)); /* start reading */
+	} else {
+		while (sz-- > 0) { /* fill FIFO */
+			writel(*(hw->buf++),SPI_DAT(hw->syncio)); 
+		}
+	}
+	return 0;
+}
+
+static int spi_lpc32xx_transfer_one(struct spi_master *master,
+				     struct spi_device *spi,
+				     struct spi_transfer *xfer)
+{
+	struct spi_lpc32xx_data *hw = spi_master_get_devdata(master);
+	unsigned long div;
+
+	div = 1;
+	if (xfer->speed_hz <= spi->max_speed_hz && xfer->speed_hz > 100) 
+		div = hw->rate / xfer->speed_hz;
+	if (div < 1) div = 1;
+	if (div > 0x7f) div = 0x7f;
+
+	hw->mode = ((spi->mode&3)<<16) | SPI_CON_THR | SPI_CON_MS | (div-1);
+
+	if (!(spi->mode & SPI_3WIRE))
+		hw->mode |= SPI_CON_UNIDIR;
+
+	if (spi->mode & SPI_LSB_FIRST)
+		hw->mode |= SPI_CON_MSB;
+
+	hw->len = xfer->len;
+	hw->bpw = xfer->bits_per_word;
+	hw->buf = (u8 *)(xfer->tx_buf ? xfer->tx_buf : xfer->rx_buf);
+	hw->is_rd = xfer->tx_buf ? 0 : 1;
+	if (!hw->is_rd) hw->mode |= SPI_CON_RXTX; else 
+			/* only read FIFO, don't cause another shifting */
+			hw->mode |= SPI_CON_SHIFT_OFF;
+
+	spi_lpc32xx_start(hw);
+
+	return 1;
+}
+
+static irqreturn_t spi_lpc32xx_isr(int irq, void *dev_id)
+{
+	struct spi_master *master = dev_id;
+	struct spi_lpc32xx_data *hw = spi_master_get_devdata(master);
+
+	int sz,i;
+	sz = hw->len;
+	if (sz > 64) sz = 64;
+#if 0
+	printk("in spi_lpc32xx_isr sz=%d buf=%p isrd=%d stat=%X\n",
+			sz,hw->buf,hw->is_rd,readl(SPI_STAT(hw->syncio)));
+#endif
+
+	if (hw->is_rd) {
+		writel(0,SPI_FRM(hw->syncio));
+		/* FIFO is full of sz data */
+		for (i=0;i<sz;i++) {
+			*(hw->buf++) = readl(SPI_DAT(hw->syncio));
+		}
+	} else 
+		hw->buf += sz; /* data were sent */
+	hw->len -= sz;
+	writel(SPI_STAT_INTCLR,SPI_STAT(hw->syncio));
+		
+	if (hw->len > 0) 
+		spi_lpc32xx_start(hw);
+	else
+		spi_finalize_current_transfer(master);
+
+	return IRQ_HANDLED;
+}
+
+static int spi_lpc32xx_probe(struct platform_device *pdev)
+{
+	struct spi_lpc32xx_data *hw;
+	struct device_node *np = pdev->dev.of_node;
+	struct spi_master *master;
+	struct resource *res;
+	int irq, ret;
+
+	if (!np) {
+		dev_err(&pdev->dev, "no DT node defined\n");
+		return -EINVAL;
+	}
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	master = spi_alloc_master(&pdev->dev, sizeof(*hw));
+	if (!master)
+		return -ENOMEM;
+
+	master->bus_num = pdev->id;
+	master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST | SPI_3WIRE;
+	master->bits_per_word_mask =  SPI_BPW_RANGE_MASK(1, 8);
+	master->setup = spi_lpc32xx_setup;
+	master->transfer_one = spi_lpc32xx_transfer_one;
+	master->flags = SPI_MASTER_HALF_DUPLEX;
+	master->dev.of_node = pdev->dev.of_node;
+
+	hw = spi_master_get_devdata(master);
+
+	hw->spi_clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(hw->spi_clk)) {
+		ret = PTR_ERR(hw->spi_clk);
+		goto err_out;
+	}
+	clk_enable(hw->spi_clk);
+	hw->rate = clk_get_rate(hw->spi_clk)/2;
+	master->max_speed_hz = hw->rate;
+	master->min_speed_hz = hw->rate/128;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hw->syncio = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(hw->syncio)) {
+		ret = PTR_ERR(hw->syncio);
+		goto err_out;
+	}
+
+	/* Clear possible pending interrupt */
+	writel(SPI_GLOB_ENABLE, SPI_GLOBAL(hw->syncio));
+	writel(SPI_GLOB_ENABLE|SPI_GLOB_RST, SPI_GLOBAL(hw->syncio));
+	writel(SPI_GLOB_ENABLE, SPI_GLOBAL(hw->syncio));
+
+	ret = devm_request_irq(&pdev->dev, irq, spi_lpc32xx_isr, 0,
+			       dev_name(&pdev->dev), master);
+	if (ret)
+		goto err_out;
+
+	ret = devm_spi_register_master(&pdev->dev, master);
+	if (!ret) {
+		dev_info(&pdev->dev,
+			 "SPI bus driver initialized. Master clock %u Hz\n",
+			 master->max_speed_hz);
+		return 0;
+	}
+
+	dev_err(&pdev->dev, "Failed to register master\n");
+
+err_out:
+	spi_master_put(master);
+
+	return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id lpc_spi_match[] = {
+	{ .compatible = "nxp,lpc3220-spi" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, lpc_spi_match);
+#endif
+
+static struct platform_driver lpc32xx_spi_driver = {
+	.driver	= {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(lpc_spi_match),
+	},
+	.probe	= spi_lpc32xx_probe,
+};
+module_platform_driver(lpc32xx_spi_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Martin Devera <devik@....cz>");
+MODULE_DESCRIPTION("LPC32XX SPI bus driver");
+MODULE_ALIAS("platform:" DRIVER_NAME);
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ