From: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
To: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>,
barebox@lists.infradead.org
Subject: [PATCH v2 04/14] spi: Add SPI GPIO bitbang driver
Date: Mon, 20 Apr 2015 22:11:16 +0200 [thread overview]
Message-ID: <1429560686-11391-5-git-send-email-sebastian.hesselbarth@gmail.com> (raw)
In-Reply-To: <1429560686-11391-1-git-send-email-sebastian.hesselbarth@gmail.com>
This adds a driver for SPI master by GPIO pins.
Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
---
Cc: barebox@lists.infradead.org
Cc: Ezequiel Garcia <ezequiel.garcia@free-electrons.com>
Cc: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
---
drivers/spi/Kconfig | 4 +
drivers/spi/Makefile | 1 +
drivers/spi/gpio_spi.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++
include/spi/spi_gpio.h | 36 ++++++++
4 files changed, 281 insertions(+)
create mode 100644 drivers/spi/gpio_spi.c
create mode 100644 include/spi/spi_gpio.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index e4fa6a2d94bb..738b88ea5ed7 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -18,6 +18,10 @@ config DRIVER_SPI_ATMEL
bool "Atmel (AT91) SPI Master driver"
depends on ARCH_AT91
+config DRIVER_SPI_GPIO
+ bool "GPIO SPI Master driver"
+ depends on GPIOLIB
+
config DRIVER_SPI_IMX
bool "i.MX SPI Master driver"
depends on ARCH_IMX
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 7469479c31fc..74819665856e 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -1,5 +1,6 @@
obj-$(CONFIG_SPI) += spi.o
obj-$(CONFIG_DRIVER_SPI_ATH79) += ath79_spi.o
+obj-$(CONFIG_DRIVER_SPI_GPIO) += gpio_spi.o
obj-$(CONFIG_DRIVER_SPI_IMX) += imx_spi.o
obj-$(CONFIG_DRIVER_SPI_MVEBU) += mvebu_spi.o
obj-$(CONFIG_DRIVER_SPI_MXS) += mxs_spi.o
diff --git a/drivers/spi/gpio_spi.c b/drivers/spi/gpio_spi.c
new file mode 100644
index 000000000000..4ea545a2823a
--- /dev/null
+++ b/drivers/spi/gpio_spi.c
@@ -0,0 +1,240 @@
+/*
+ * SPI master driver using generic bitbanged GPIO
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * Based on Linux driver
+ * Copyright (C) 2006,2008 David Brownell
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <driver.h>
+#include <errno.h>
+#include <gpio.h>
+#include <init.h>
+#include <io.h>
+#include <malloc.h>
+#include <of_gpio.h>
+#include <spi/spi.h>
+#include <spi/spi_gpio.h>
+
+struct gpio_spi {
+ struct spi_master master;
+ struct gpio_spi_pdata *data;
+};
+
+#define priv_from_spi_device(s) container_of(s->master, struct gpio_spi, master)
+
+static inline void setsck(const struct spi_device *spi, int is_on)
+{
+ struct gpio_spi *priv = priv_from_spi_device(spi);
+ gpio_set_value(priv->data->sck, is_on);
+}
+
+static inline void setmosi(const struct spi_device *spi, int is_on)
+{
+ struct gpio_spi *priv = priv_from_spi_device(spi);
+ if (!gpio_is_valid(priv->data->mosi))
+ return;
+ gpio_set_value(priv->data->mosi, is_on);
+}
+
+static inline int getmiso(const struct spi_device *spi)
+{
+ struct gpio_spi *priv = priv_from_spi_device(spi);
+ if (!gpio_is_valid(priv->data->miso))
+ return 1;
+ return !!gpio_get_value(priv->data->miso);
+}
+
+#define spidelay(nsecs) do { } while (0)
+
+#include "spi-bitbang-txrx.h"
+
+static int gpio_spi_set_cs(struct spi_device *spi, bool en)
+{
+ struct gpio_spi *priv = priv_from_spi_device(spi);
+
+ if (!gpio_is_valid(priv->data->cs[spi->chip_select]))
+ return 0;
+
+ gpio_set_value(priv->data->cs[spi->chip_select],
+ (spi->mode & SPI_CS_HIGH) ? en : !en);
+
+ return 0;
+}
+
+static inline u32 gpio_spi_txrx_word(struct spi_device *spi, unsigned nsecs,
+ u32 word, u8 bits)
+{
+ int cpol = !!(spi->mode & SPI_CPOL);
+ if (spi->mode & SPI_CPHA)
+ return bitbang_txrx_be_cpha1(spi, nsecs, cpol, word, bits);
+ else
+ return bitbang_txrx_be_cpha0(spi, nsecs, cpol, word, bits);
+}
+
+static int gpio_spi_transfer_one(struct spi_device *spi, struct spi_transfer *t)
+{
+ bool read = (t->rx_buf) ? true : false;
+ u32 word = 0;
+ int n;
+
+ for (n = 0; n < t->len; n++) {
+ if (!read)
+ word = ((const u8 *)t->tx_buf)[n];
+ word = gpio_spi_txrx_word(spi, 0, word, 8);
+ if (read)
+ ((u8 *)t->rx_buf)[n] = word & 0xff;
+ }
+
+ return 0;
+}
+
+static int gpio_spi_transfer(struct spi_device *spi, struct spi_message *msg)
+{
+ struct spi_transfer *t;
+ int ret;
+
+ ret = gpio_spi_set_cs(spi, true);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(t, &msg->transfers, transfer_list) {
+ ret = gpio_spi_transfer_one(spi, t);
+ if (ret)
+ return ret;
+ msg->actual_length += t->len;
+ }
+
+ ret = gpio_spi_set_cs(spi, false);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int gpio_spi_setup(struct spi_device *spi)
+{
+ if (spi->bits_per_word != 8) {
+ dev_err(spi->master->dev, "master does not support %d bits per word\n",
+ spi->bits_per_word);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int gpio_spi_of_probe(struct device_d *dev)
+{
+ struct device_node *np = dev->device_node;
+ struct gpio_spi_pdata *pdata;
+ int n, sck;
+
+ if (!IS_ENABLED(CONFIG_OFDEVICE) || dev->platform_data)
+ return 0;
+
+ sck = of_get_named_gpio(np, "gpio-sck", 0);
+ if (sck == -EPROBE_DEFER)
+ return sck;
+ if (!gpio_is_valid(sck)) {
+ dev_err(dev, "missing mandatory SCK gpio\n");
+ return sck;
+ }
+
+ pdata = xzalloc(sizeof(*pdata));
+ pdata->sck = sck;
+ pdata->num_cs = MAX_CHIPSELECT;
+
+ pdata->miso = of_get_named_gpio(np, "gpio-miso", 0);
+ if (!gpio_is_valid(pdata->miso))
+ pdata->miso = SPI_GPIO_NO_MISO;
+
+ pdata->mosi = of_get_named_gpio(np, "gpio-mosi", 0);
+ if (!gpio_is_valid(pdata->mosi))
+ pdata->mosi = SPI_GPIO_NO_MOSI;
+
+ for (n = 0; n < MAX_CHIPSELECT; n++) {
+ pdata->cs[n] = of_get_named_gpio(np, "cs-gpios", n);
+ if (!gpio_is_valid(pdata->cs[n]))
+ pdata->cs[n] = SPI_GPIO_NO_CS;
+ }
+
+ dev->platform_data = pdata;
+
+ return 0;
+}
+
+static int gpio_spi_probe(struct device_d *dev)
+{
+ struct gpio_spi *priv;
+ struct gpio_spi_pdata *pdata;
+ struct spi_master *master;
+ int n, ret;
+
+ ret = gpio_spi_of_probe(dev);
+ if (ret)
+ return ret;
+ pdata = dev->platform_data;
+
+ ret = gpio_request_one(pdata->sck, GPIOF_DIR_OUT, "spi-sck");
+ if (ret)
+ return ret;
+
+ if (pdata->miso != SPI_GPIO_NO_MISO) {
+ ret = gpio_request_one(pdata->miso, GPIOF_DIR_IN, "spi-miso");
+ if (ret)
+ return ret;
+ }
+
+ if (pdata->mosi != SPI_GPIO_NO_MOSI) {
+ ret = gpio_request_one(pdata->mosi, GPIOF_DIR_OUT, "spi-mosi");
+ if (ret)
+ return ret;
+ }
+
+ for (n = 0; n < pdata->num_cs; n++) {
+ char *cs_name;
+
+ if (!gpio_is_valid(pdata->cs[n]))
+ continue;
+
+ cs_name = asprintf("spi-cs%d", n);
+ ret = gpio_request_one(pdata->cs[n], GPIOF_DIR_OUT, cs_name);
+ if (ret)
+ return ret;
+ }
+
+ priv = xzalloc(sizeof(*priv));
+ priv->data = pdata;
+ master = &priv->master;
+ master->dev = dev;
+ master->bus_num = dev->id;
+ master->setup = gpio_spi_setup;
+ master->transfer = gpio_spi_transfer;
+ master->num_chipselect = priv->data->num_cs;
+
+ return spi_register_master(&priv->master);
+}
+
+static struct of_device_id __maybe_unused gpio_spi_dt_ids[] = {
+ { .compatible = "spi-gpio", },
+ { }
+};
+
+static struct driver_d gpio_spi_driver = {
+ .name = "gpio-spi",
+ .probe = gpio_spi_probe,
+ .of_compatible = DRV_OF_COMPAT(gpio_spi_dt_ids),
+};
+device_platform_driver(gpio_spi_driver);
diff --git a/include/spi/spi_gpio.h b/include/spi/spi_gpio.h
new file mode 100644
index 000000000000..841c934f6ead
--- /dev/null
+++ b/include/spi/spi_gpio.h
@@ -0,0 +1,36 @@
+/*
+ * SPI master driver using generic bitbanged GPIO
+ *
+ * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
+ *
+ * Based on Linux driver
+ * Copyright (C) 2006,2008 David Brownell
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __SPI_GPIO_H
+#define __SPI_GPIO_H
+
+#define MAX_CHIPSELECT 4
+#define SPI_GPIO_NO_CS (-1)
+#define SPI_GPIO_NO_MISO (-1)
+#define SPI_GPIO_NO_MOSI (-1)
+
+struct gpio_spi_pdata {
+ int sck;
+ int mosi;
+ int miso;
+ int cs[MAX_CHIPSELECT];
+ int num_cs;
+};
+
+#endif
--
2.1.0
_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox
next prev parent reply other threads:[~2015-04-20 20:12 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-20 20:11 [PATCH v2 00/14] Add support for Lenovo ix4-300d NAS Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 01/14] net: phy: Support Marvell 88E1318S PHY Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 02/14] gpio: Add driver for 74x164 compatible shift-registers Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 03/14] spi: ath79: move spidelay from spi-bitbang-txrx Sebastian Hesselbarth
2015-04-20 20:11 ` Sebastian Hesselbarth [this message]
2015-04-20 20:11 ` [PATCH v2 05/14] bus: mvebu-mbus: Remove coherency attribute Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 06/14] bus: mvebu-mbus: Drop device reference Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 07/14] bus: mvebu-mbus: Convert mbus platform driver to direct driver Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 08/14] ARM: mvebu: Move PCIe register defines to socid.h Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 09/14] ARM: mvebu: armada-xp: Fixup broken MV78230-A0 SoC ID Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 10/14] ARM: mvebu: armada-xp: Limit PUP access to Armada XP Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 11/14] ARM: mvebu: armada-xp: Use MBUS_ERR_PROP_EN define Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 12/14] ARM: mvebu: armada-xp: Sort boards and images alphabetically Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 13/14] ARM: mvebu: armada-xp: Add Lenovo Iomega ix4-300d Sebastian Hesselbarth
2015-04-20 20:11 ` [PATCH v2 14/14] ARM: Add Lenovo ix4 and related drivers to mvebu_defconfig Sebastian Hesselbarth
2015-04-23 5:57 ` [PATCH v2 00/14] Add support for Lenovo ix4-300d NAS Sascha Hauer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1429560686-11391-5-git-send-email-sebastian.hesselbarth@gmail.com \
--to=sebastian.hesselbarth@gmail.com \
--cc=barebox@lists.infradead.org \
--cc=thomas.petazzoni@free-electrons.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox