linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC 0/3] add Inter Chip Communication framework
@ 2016-11-09  6:04 fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 1/3] icc: add add SSI32 transport driver fixed-term.Oleksij.Rempel
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: fixed-term.Oleksij.Rempel @ 2016-11-09  6:04 UTC (permalink / raw)
  To: linux-kernel, linux-soc, dirk.behme, mkl; +Cc: Oleksij Rempel

From: Oleksij Rempel <linux@rempel-privat.de>

Hallo all,

i'm working on upstreamable version of ICC (Inter Chip Communication)
framework. It is used to provide kernel level abstraction for functionality
located in neighbor SoC or MCU.
The idea is not really different from existing driver/hsi, but currently it
seems to be painful to rework it to fit our needs.
The structure of ICC is fallowing:
- transport level: currently only SSI32 (based on SPI) is provided.
   I can imagine that HSI (drivers/hsi) and HTC (ath9k-htc) can be placed
   here too.
- icc bus: provides an upstraction level for icc clients
- icc clients. Drivers for: char device, watchdog, rtc, CAN, gpio.

Current patchset I a request for comments, before I'll start to bug you will
patch reviews und PRs.
Please let me know if i'm going to right or wrong direction.

Oleksij Rempel (3):
  icc: add add SSI32 transport driver
  watchdog: add icc watchdog driver
  char: add icc_char device

 drivers/Kconfig                   |   2 +
 drivers/Makefile                  |   1 +
 drivers/char/Kconfig              |   9 ++
 drivers/char/Makefile             |   1 +
 drivers/char/icc_char.c           | 280 +++++++++++++++++++++++++++++++++++
 drivers/icc/Kconfig               |  13 ++
 drivers/icc/Makefile              |   5 +
 drivers/icc/icc_bus.c             | 204 ++++++++++++++++++++++++++
 drivers/icc/ssi32/Kconfig         |  12 ++
 drivers/icc/ssi32/Makefile        |   6 +
 drivers/icc/ssi32/ssi32_prot.c    | 256 ++++++++++++++++++++++++++++++++
 drivers/icc/ssi32/ssi32_prot.h    |  94 ++++++++++++
 drivers/icc/ssi32/ssi32_spi_hif.c | 300 ++++++++++++++++++++++++++++++++++++++
 drivers/icc/ssi32/ssi32_spi_hif.h |  40 +++++
 drivers/watchdog/Kconfig          |   8 +
 drivers/watchdog/Makefile         |   1 +
 drivers/watchdog/icc_wdt.c        | 185 +++++++++++++++++++++++
 include/linux/icc/icc.h           |  88 +++++++++++
 include/linux/icc/ssi32.h         |  32 ++++
 19 files changed, 1537 insertions(+)
 create mode 100644 drivers/char/icc_char.c
 create mode 100644 drivers/icc/Kconfig
 create mode 100644 drivers/icc/Makefile
 create mode 100644 drivers/icc/icc_bus.c
 create mode 100644 drivers/icc/ssi32/Kconfig
 create mode 100644 drivers/icc/ssi32/Makefile
 create mode 100644 drivers/icc/ssi32/ssi32_prot.c
 create mode 100644 drivers/icc/ssi32/ssi32_prot.h
 create mode 100644 drivers/icc/ssi32/ssi32_spi_hif.c
 create mode 100644 drivers/icc/ssi32/ssi32_spi_hif.h
 create mode 100644 drivers/watchdog/icc_wdt.c
 create mode 100644 include/linux/icc/icc.h
 create mode 100644 include/linux/icc/ssi32.h

-- 
1.9.1

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [RFC 1/3] icc: add add SSI32 transport driver
  2016-11-09  6:04 [RFC 0/3] add Inter Chip Communication framework fixed-term.Oleksij.Rempel
@ 2016-11-09  6:04 ` fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 2/3] watchdog: add icc watchdog driver fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 3/3] char: add icc_char device fixed-term.Oleksij.Rempel
  2 siblings, 0 replies; 4+ messages in thread
From: fixed-term.Oleksij.Rempel @ 2016-11-09  6:04 UTC (permalink / raw)
  To: linux-kernel, linux-soc, dirk.behme, mkl; +Cc: Oleksij Rempel, Oleksij Rempel

From: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>

Add driver for Inter-Chip communication based on SSI32 protocol.

SSI32 is Synchronous Serial Interface optimised for 32bit alignment.

It is an Transport protocol based on SPI used for industrie devices
designed with fallowing points in mind:
- implementation with widely available and cheap technology - SPI.
- error detection and response within 1 milli seccond.
- fixed size messages.
- Acknowledgment between applications.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>
---
 drivers/Kconfig                   |   2 +
 drivers/Makefile                  |   1 +
 drivers/icc/Kconfig               |  13 ++
 drivers/icc/Makefile              |   5 +
 drivers/icc/icc_bus.c             | 204 ++++++++++++++++++++++++++
 drivers/icc/ssi32/Kconfig         |  12 ++
 drivers/icc/ssi32/Makefile        |   6 +
 drivers/icc/ssi32/ssi32_prot.c    | 256 ++++++++++++++++++++++++++++++++
 drivers/icc/ssi32/ssi32_prot.h    |  94 ++++++++++++
 drivers/icc/ssi32/ssi32_spi_hif.c | 300 ++++++++++++++++++++++++++++++++++++++
 drivers/icc/ssi32/ssi32_spi_hif.h |  40 +++++
 include/linux/icc/icc.h           |  88 +++++++++++
 include/linux/icc/ssi32.h         |  32 ++++
 13 files changed, 1053 insertions(+)
 create mode 100644 drivers/icc/Kconfig
 create mode 100644 drivers/icc/Makefile
 create mode 100644 drivers/icc/icc_bus.c
 create mode 100644 drivers/icc/ssi32/Kconfig
 create mode 100644 drivers/icc/ssi32/Makefile
 create mode 100644 drivers/icc/ssi32/ssi32_prot.c
 create mode 100644 drivers/icc/ssi32/ssi32_prot.h
 create mode 100644 drivers/icc/ssi32/ssi32_spi_hif.c
 create mode 100644 drivers/icc/ssi32/ssi32_spi_hif.h
 create mode 100644 include/linux/icc/icc.h
 create mode 100644 include/linux/icc/ssi32.h

diff --git a/drivers/Kconfig b/drivers/Kconfig
index c0cc96b..acc9b08 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -56,6 +56,8 @@ source "drivers/spmi/Kconfig"
 
 source "drivers/hsi/Kconfig"
 
+source "drivers/icc/Kconfig"
+
 source "drivers/pps/Kconfig"
 
 source "drivers/ptp/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da..6656a1e 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -75,6 +75,7 @@ obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_SPMI)		+= spmi/
 obj-y				+= hsi/
+obj-y				+= icc/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
 obj-$(CONFIG_FUSION)		+= message/
diff --git a/drivers/icc/Kconfig b/drivers/icc/Kconfig
new file mode 100644
index 0000000..33b6422
--- /dev/null
+++ b/drivers/icc/Kconfig
@@ -0,0 +1,13 @@
+#
+# ICC driver configuration
+#
+menuconfig ICC
+	tristate "ICC support"
+	---help---
+	  The "Inter-Chip Communication" support
+
+if ICC
+
+source "drivers/icc/ssi32/Kconfig"
+
+endif # ICC
diff --git a/drivers/icc/Makefile b/drivers/icc/Makefile
new file mode 100644
index 0000000..c3662c0
--- /dev/null
+++ b/drivers/icc/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for ICC
+#
+obj-$(CONFIG_ICC) 	+= icc_bus.o
+obj-y			+= ssi32/
diff --git a/drivers/icc/icc_bus.c b/drivers/icc/icc_bus.c
new file mode 100644
index 0000000..dceb47c
--- /dev/null
+++ b/drivers/icc/icc_bus.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *	 Oleksij Rempel
+ *		<fixed-term.Oleksij.Rempel@de.bosch.com>
+ *		<linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/compiler.h>
+#include <linux/list.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/notifier.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/icc/icc.h>
+
+static struct class *icc_class;
+
+static ssize_t modalias_show(struct device *dev,
+			     struct device_attribute *a __maybe_unused,
+			     char *buf)
+{
+	return sprintf(buf, "icc:%s\n", dev_name(dev));
+}
+static DEVICE_ATTR_RO(modalias);
+
+static struct attribute *icc_bus_dev_attrs[] = {
+	&dev_attr_modalias.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(icc_bus_dev);
+
+static int icc_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	add_uevent_var(env, "MODALIAS=icc:%s", dev_name(dev));
+
+	return 0;
+}
+
+static int icc_bus_match(struct device *dev, struct device_driver *drv)
+{
+	if (of_driver_match_device(dev, drv))
+		return true;
+
+	if (strcmp(dev_name(dev), drv->name) == 0)
+		return true;
+
+	return false;
+}
+
+static struct bus_type icc_bus_type = {
+	.name           = "icc",
+	.dev_groups	= icc_bus_dev_groups,
+	.match		= icc_bus_match,
+	.uevent		= icc_bus_uevent,
+};
+
+static int icc_drv_probe(struct device *dev)
+{
+	const struct icc_driver *icc_drv = to_icc_driver(dev->driver);
+	struct icc_device *iccd = to_icc_device(dev);
+
+	return icc_drv->probe(iccd);
+}
+
+static int icc_drv_remove(struct device *dev)
+{
+	const struct icc_driver *icc_drv = to_icc_driver(dev->driver);
+	struct icc_device *iccd = to_icc_device(dev);
+
+	return icc_drv->remove(iccd);
+}
+
+static void icc_drv_shutdown(struct device *dev)
+{
+	const struct icc_driver *icc_drv = to_icc_driver(dev->driver);
+	struct icc_device *iccd = to_icc_device(dev);
+
+	icc_drv->shutdown(iccd);
+}
+
+int icc_register_driver(struct icc_driver *icc_drv)
+{
+	icc_drv->driver.bus = &icc_bus_type;
+
+	if (icc_drv->probe)
+		icc_drv->driver.probe = icc_drv_probe;
+	if (icc_drv->remove)
+		icc_drv->driver.remove = icc_drv_remove;
+	if (icc_drv->shutdown)
+		icc_drv->driver.shutdown = icc_drv_shutdown;
+
+	return driver_register(&icc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(icc_register_driver);
+
+void icc_unregister_driver(struct icc_driver *icc_drv)
+{
+	return driver_unregister(&icc_drv->driver);
+}
+EXPORT_SYMBOL_GPL(icc_unregister_driver);
+
+static void icc_lun_release(struct device *dev)
+{
+}
+
+static int icc_add_lun_from_dt(struct icc_master *iccm,
+				 struct device_node *np)
+{
+	struct icc_device *iccd;
+	u32 reg;
+	int ret;
+
+	ret = of_property_read_u32(np, "reg", &reg);
+	if (ret) {
+		dev_warn(iccm->dev, "can't parse \"reg\" for %s\n",
+			 np->full_name);
+		return ret;
+	}
+
+	if (reg > ICC_MAX_LUNS) {
+		dev_warn(iccm->dev, "reg is more then %i\n",
+			 ICC_MAX_LUNS);
+		return -EINVAL;
+	}
+
+	iccd = &iccm->lun[reg];
+
+	if (iccd->configured) {
+		dev_warn(iccm->dev, "lun %i is already configured\n",
+			 reg);
+		return -EINVAL;
+	}
+
+	/* TODO: set some usaable name */
+	dev_set_name(&iccd->dev, "%s:lun.%i:%s", dev_name(iccm->dev),
+		     reg, np->name);
+
+	iccd->dev.bus = &icc_bus_type;
+	iccd->dev.parent = iccm->dev;
+	iccd->dev.release = icc_lun_release;
+	iccd->dev.of_node = np;
+	iccd->iccm = iccm;
+	iccd->lun_number = reg;
+
+	/* TODO use device_create? to move major/minor registration here? */
+	ret = device_register(&iccd->dev);
+	if (ret < 0) {
+		dev_warn(iccm->dev, "filed to registr lun %i\n", reg);
+		put_device(&iccd->dev);
+	}
+
+	iccd->configured = true;
+
+	return 0;
+}
+
+static void icc_add_luns_from_dt(struct icc_master *iccm)
+{
+	struct device_node *np = iccm->dev->of_node;
+	struct device_node *child;
+
+	for_each_available_child_of_node(np, child)
+		icc_add_lun_from_dt(iccm, child);
+
+}
+
+void icc_add_luns(struct icc_master *iccm)
+{
+	iccm->icc_class = icc_class;
+	icc_add_luns_from_dt(iccm);
+}
+EXPORT_SYMBOL_GPL(icc_add_luns);
+
+static int __init icc_bus_init(void)
+{
+	int ret;
+
+	icc_class = class_create(THIS_MODULE, "icc");
+	if (IS_ERR(icc_class)) {
+		ret = PTR_ERR(icc_class);
+		return ret;
+	}
+
+	return bus_register(&icc_bus_type);
+}
+postcore_initcall(icc_bus_init);
+
+static void __exit icc_bus_exit(void)
+{
+	class_destroy(icc_class);
+	bus_unregister(&icc_bus_type);
+}
+module_exit(icc_bus_exit);
+
+MODULE_AUTHOR("Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>");
+MODULE_DESCRIPTION("ICC framework");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/icc/ssi32/Kconfig b/drivers/icc/ssi32/Kconfig
new file mode 100644
index 0000000..e22c322
--- /dev/null
+++ b/drivers/icc/ssi32/Kconfig
@@ -0,0 +1,12 @@
+#
+# Incnet protocol
+#
+
+config ICC_SSI32
+	tristate "SSI32 (EXPERIMENTAL)"
+	depends on ICC
+	depends on OF
+	depends on SPI
+	---help---
+	  To compile this driver as a module, choose M here: the module
+	  will be called SSI32.
diff --git a/drivers/icc/ssi32/Makefile b/drivers/icc/ssi32/Makefile
new file mode 100644
index 0000000..5fdeaed
--- /dev/null
+++ b/drivers/icc/ssi32/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for SSI32 support code.
+#
+
+icc_ssi32-y := ssi32_spi_hif.o ssi32_prot.o
+obj-$(CONFIG_ICC_SSI32) += icc_ssi32.o
diff --git a/drivers/icc/ssi32/ssi32_prot.c b/drivers/icc/ssi32/ssi32_prot.c
new file mode 100644
index 0000000..31ee5fb
--- /dev/null
+++ b/drivers/icc/ssi32/ssi32_prot.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *	 Oleksij Rempel
+ *		<fixed-term.Oleksij.Rempel@de.bosch.com>
+ *		<linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/pm_runtime.h>
+#include <linux/icc/ssi32.h>
+#include <linux/icc/icc.h>
+
+#include "ssi32_spi_hif.h"
+
+/* Proper name? alloc? */
+int ssi32_trf_init(struct ssi32_prot_priv *ssi32pp,
+		   struct ssi32_prot_trf *trf, u8 lun, size_t data_size)
+{
+	struct ssi32_prot_hdr *hdr;
+
+	trf->buf = devm_kzalloc(ssi32pp->dev, ssi32pp->trf_size,
+				GFP_KERNEL);
+	if (!trf->buf)
+		return -ENOMEM;
+
+	trf->hdr = trf->buf;
+	trf->data = trf->buf + SSI32_HDR_SIZE;
+	trf->data_size = data_size;
+	trf->size = data_size + SSI32_HDR_SIZE;
+
+	hdr = trf->hdr;
+	hdr->lun = lun;
+
+	return 0;
+}
+
+void ssi32_trf_free(struct ssi32_prot_priv *ssi32pp,
+		    struct ssi32_prot_trf *trf)
+{
+	devm_kfree(ssi32pp->dev, trf->buf);
+}
+
+static unsigned char ssi32_chksum(void *data, size_t len)
+{
+	u32 sum = 0;
+	unsigned int i;
+	u8 *buf = data;
+
+	for (i = 0; i < len; i++)
+		sum += buf[i];
+	/* return 2's complement */
+	return (u8)((~sum) + 1);
+}
+
+static void *ssi32_setup_tx_buf(struct ssi32_prot_priv *ssi32pp,
+				struct ssi32_prot_trf *tx_trf)
+{
+	struct ssi32_prot_hdr *tx_hdr = tx_trf->hdr;
+
+	if (unlikely(tx_trf->size > ssi32pp->trf_size)) {
+		dev_warn(ssi32pp->dev, "tx size is to big: %i > %i\n",
+			 tx_trf->size, ssi32pp->trf_size);
+		return NULL;
+	}
+
+	tx_hdr->ctrl = SSI32_CTRL_DEFAULT;
+	tx_hdr->dlc = tx_trf->data_size;
+
+	if (!tx_hdr->dlc)
+		tx_hdr->ctrl |= SSI32_CTRL_NO_MSG;
+	else if (tx_trf->data_size < tx_trf->size)
+		memset(tx_trf->data + tx_trf->data_size, 0,
+		       tx_trf->size - tx_trf->data_size);
+
+	tx_hdr->chk = 0;
+	tx_hdr->chk = ssi32_chksum(tx_trf->buf, tx_trf->size);
+
+	return tx_trf->buf;
+}
+
+static void ssi32_setup_tx_ack(struct ssi32_prot_ack *ack, u32 err)
+{
+	memset(ack, 0, sizeof(*ack));
+	ack->tx_ack = SSI32_ACK_DEFAULT;
+
+	if (!(err & SSI32_RX_ERR_MASK)) {
+		ack->tx_ack |= SSI32_ACK_POS_ACK;
+		return;
+	}
+
+	if (err & SSI32_RX_CHK_ERR)
+		ack->tx_ack |= SSI32_ACK_CHK_ERR;
+	if (err & SSI32_RX_SEQ_ERR)
+		ack->tx_ack |= SSI32_ACK_SEQ_ERR;
+	if (err & SSI32_RX_SYN_ERR)
+		ack->tx_ack |= SSI32_ACK_CHK_ERR;
+	if (err & SSI32_RX_XOFF_ERR)
+		ack->tx_ack |= SSI32_ACK_XOFF;
+}
+
+static int ssi32_process_ack(struct ssi32_prot_ack *ack)
+{
+	return 0;
+}
+
+static u32 ssi32_process_rx(struct ssi32_prot_priv *ssi32pp)
+{
+	struct ssi32_prot_hdr *rx_hdr = ssi32pp->rx_buf;
+	struct icc_master *iccm = &ssi32pp->iccm;
+	struct icc_device *iccd;
+	size_t rx_size;
+	int ret;
+
+	rx_size = rx_hdr->dlc + SSI32_HDR_SIZE;
+	if (rx_size > ssi32pp->trf_size) {
+		dev_dbg(ssi32pp->dev, "rx size is wrong %i\n", rx_size);
+		return SSI32_RX_CHK_ERR;
+	}
+
+	if (ssi32_chksum(ssi32pp->rx_buf, rx_size)) {
+		dev_dbg(ssi32pp->dev, "rx chksum err\n");
+		return SSI32_RX_CHK_ERR;
+	}
+
+	/* got dummy message */
+	if (rx_hdr->lun == SSI32_MAX_LUNS)
+		return 0;
+
+	iccd = &iccm->lun[rx_hdr->lun];
+	if (!iccd->rx_cb)
+		return SSI32_RX_XOFF_ERR;
+
+	ret = iccd->rx_cb(iccd, ssi32pp->rx_buf + SSI32_HDR_SIZE,
+			   rx_hdr->dlc);
+
+	/* what is better error here? */
+	if (ret == -EBUSY)
+		return SSI32_RX_CHK_ERR;
+
+	return ret;
+}
+
+int ssi32_prot_shift(struct ssi32_prot_priv *ssi32pp,
+		     struct ssi32_prot_trf *tx_trf)
+{
+	void *tx_buf = NULL;
+	u32 err = 0;
+	int ret;
+
+	ssi32_hif_idle(ssi32pp->hif, false);
+
+	if (!tx_trf)
+		tx_trf = &ssi32pp->tx_dummy_trf;
+
+	if (tx_trf) {
+		tx_buf = ssi32_setup_tx_buf(ssi32pp, tx_trf);
+		if (!tx_buf)
+			return -ENOMEM;
+	}
+
+	ret = ssi32_shift_one_msg(ssi32pp->hif, tx_buf,
+				  ssi32pp->rx_buf, ssi32pp->trf_size);
+	if (ret)
+		return ret;
+
+	err = ssi32_process_rx(ssi32pp);
+
+	ssi32_setup_tx_ack(&ssi32pp->tx_ack, err);
+
+	ret = ssi32_shift_one_msg(ssi32pp->hif, &ssi32pp->tx_ack,
+				  &ssi32pp->rx_ack, SSI32_ACK_SIZE);
+	if (ret)
+		return ret;
+
+	ret = ssi32_process_ack(&ssi32pp->rx_ack);
+
+	ssi32_hif_idle(ssi32pp->hif, true);
+
+	return ret;
+}
+
+static int ssi32_trf_alloc(struct icc_master *iccm,
+		    struct icc_trf *tx_trf,
+		    u8 lun, size_t data_size)
+{
+	struct ssi32_prot_priv *ssi32pp =
+		container_of(iccm, struct ssi32_prot_priv, iccm);
+
+	/* TODO currently struct icc_trf is identical to struct ssi32_prot_trf */
+	return ssi32_trf_init(ssi32pp, (struct ssi32_prot_trf *)tx_trf, lun, data_size);
+}
+
+static int ssi32_trf_xmit(struct icc_master *iccm,
+		   struct icc_trf *tx_trf)
+{
+	struct ssi32_prot_priv *ssi32pp =
+		container_of(iccm, struct ssi32_prot_priv, iccm);
+	int ret;
+
+	mutex_lock(&ssi32pp->lock);
+	/* currently struct icc_trf is identical to struct ssi32_prot_trf */
+	ret = ssi32_prot_shift(ssi32pp, (struct ssi32_prot_trf *)tx_trf);
+	mutex_unlock(&ssi32pp->lock);
+
+	return ret;
+}
+
+int ssi32_prot_init(struct ssi32_spi_hif_priv *priv)
+{
+	struct spi_device *spi = priv->spi;
+	struct ssi32_prot_priv *ssi32pp = &priv->ssi32pp;
+	struct icc_master *iccm = &ssi32pp->iccm;
+	int ret;
+
+	iccm->dev = &spi->dev;
+	ssi32pp->dev = &spi->dev;
+	ssi32pp->hif = priv;
+
+	mutex_init(&ssi32pp->lock);
+
+	ssi32pp->trf_size = SSI32_MAX_TRANSFER_SIZE;
+	iccm->max_data_size = ssi32pp->trf_size - SSI32_HDR_SIZE;
+	ssi32pp->rx_buf = devm_kzalloc(&spi->dev, ssi32pp->trf_size * 2,
+				       GFP_KERNEL);
+	iccm->trf_xmit = ssi32_trf_xmit;
+	iccm->trf_alloc = ssi32_trf_alloc;
+
+	ret = ssi32_trf_init(ssi32pp, &ssi32pp->tx_dummy_trf,
+			     SSI32_MAX_LUNS, 0);
+	if (ret)
+		return ret;
+
+	icc_add_luns(iccm);
+
+	ssi32_hif_idle(ssi32pp->hif, true);
+
+	return 0;
+}
+
+void ssi32_prot_uninit(struct ssi32_spi_hif_priv *priv)
+{
+}
diff --git a/drivers/icc/ssi32/ssi32_prot.h b/drivers/icc/ssi32/ssi32_prot.h
new file mode 100644
index 0000000..fc39449
--- /dev/null
+++ b/drivers/icc/ssi32/ssi32_prot.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *	 Oleksij Rempel
+ *		<fixed-term.Oleksij.Rempel@de.bosch.com>
+ *		<linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef SSI32_PROT_H
+#define SSI32_PROT_H
+
+#include <linux/icc/ssi32.h>
+#include <linux/icc/icc.h>
+
+#define SSI32_MAX_TRANSFER_SIZE	128
+
+#define SSI32_CTRL_FIXED2	BIT(7)
+#define SSI32_CTRL_VER		BIT(6)
+#define SSI32_CTRL_FIXED1	BIT(5)
+/* no message */
+#define SSI32_CTRL_NO_MSG	BIT(4)
+#define SSI32_CTRL_SN		BIT(3)
+/* retry message */
+#define SSI32_CTRL_RTR		BIT(2)
+/* end of message */
+#define SSI32_CTRL_EOM		BIT(1)
+/* start of message */
+#define SSI32_CTRL_SOM		BIT(0)
+
+
+#define SSI32_CTRL_DEFAULT (SSI32_CTRL_VER | SSI32_CTRL_SOM | SSI32_CTRL_EOM)
+
+#define SSI32_RX_CHK_ERR	BIT(3)
+#define SSI32_RX_SEQ_ERR	BIT(2)
+#define SSI32_RX_SYN_ERR	BIT(1)
+#define SSI32_RX_XOFF_ERR	BIT(0)
+#define SSI32_RX_ERR_MASK	(SSI32_RX_XOFF_ERR | SSI32_RX_SYN_ERR \
+				 | SSI32_RX_SEQ_ERR | SSI32_RX_CHK_ERR)
+
+#define SSI32_ACK_RESERVED3	BIT(7)
+#define SSI32_ACK_RESERVED2	BIT(6)
+#define SSI32_ACK_PARITY	BIT(5)
+#define SSI32_ACK_POS_ACK	BIT(4)
+#define SSI32_ACK_XOFF		BIT(3)
+#define SSI32_ACK_RESERVED1	BIT(2)
+#define SSI32_ACK_SEQ_ERR	BIT(1)
+#define SSI32_ACK_CHK_ERR	BIT(0)
+
+#define SSI32_ACK_DEFAULT (SSI32_ACK_RESERVED2 | SSI32_ACK_RESERVED3)
+#define SSI_ACKBLOCK_CHKMASK (0xffffff00 | (SSI32_ACK_RESERVED1 \
+			      | SSI32_ACK_RESERVED2 | SSI32_ACK_RESERVED3))
+
+struct ssi32_prot_test {
+	struct mutex            lock;   /* protect from simultaneous accesses */
+	struct device		*dev;
+	struct task_struct      *task;
+	wait_queue_head_t       wait;
+
+	struct completion       input;
+	atomic_t                active_rq;
+	atomic_t                buffs;
+	struct ssi32_prot_trf	trf;
+
+	bool			dir_in;
+	size_t			data_size;
+};
+
+/* ssi32 protocol struct */
+struct ssi32_prot_priv {
+	struct icc_master	iccm;
+	struct ssi32_spi_hif_priv	*hif;
+	struct mutex            lock;   /* protect from simultaneous accesses */
+	struct device		*dev;
+	struct task_struct      *task;
+	wait_queue_head_t       wait;
+
+	struct completion       input;
+	atomic_t                active_rq;
+
+	struct ssi32_prot_ack	tx_ack;
+	struct ssi32_prot_ack	rx_ack;
+
+	size_t			trf_size;
+	void			*rx_buf;
+	struct ssi32_prot_trf	tx_dummy_trf;
+
+	struct ssi32_prot_test	test;
+};
+
+int ssi32_prot_shift(struct ssi32_prot_priv *ssi32pp,
+		     struct ssi32_prot_trf *tx_trf);
+#endif /* SSI32_PROT_H */
diff --git a/drivers/icc/ssi32/ssi32_spi_hif.c b/drivers/icc/ssi32/ssi32_spi_hif.c
new file mode 100644
index 0000000..33fea82
--- /dev/null
+++ b/drivers/icc/ssi32/ssi32_spi_hif.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *	 Oleksij Rempel
+ *		<fixed-term.Oleksij.Rempel@de.bosch.com>
+ *		<linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/pm_runtime.h>
+
+#include "ssi32_spi_hif.h"
+
+#define DRIVER_NAME "ssi32_spi_hif"
+
+static struct dentry *debugfs_root;
+
+static irqreturn_t spi_fc_rq_thread(int irq, void *dev_id)
+{
+	struct ssi32_spi_hif_priv *priv = (struct ssi32_spi_hif_priv *)dev_id;
+	struct ssi32_prot_priv *ssi32pp = &priv->ssi32pp;
+
+	mutex_lock(&ssi32pp->lock);
+	/*
+	 * slave requeste a transver, it can be processed
+	 * if we wish to TX some thing or do RX only transaction.
+	 */
+	if (atomic_read(&priv->active_rq))
+		ssi32_prot_shift(ssi32pp, NULL);
+	mutex_unlock(&ssi32pp->lock);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t spi_fc_rq(int irq, void *dev_id)
+{
+	struct ssi32_spi_hif_priv *priv = (struct ssi32_spi_hif_priv *)dev_id;
+	int fc_active;
+
+	fc_active = gpiod_get_value(priv->fc_gpio);
+	if (atomic_read(&priv->idle) && fc_active) {
+		atomic_set(&priv->active_rq, 1);
+		return IRQ_WAKE_THREAD;
+	}
+
+	complete(&priv->fc_complete);
+
+	return IRQ_HANDLED;
+}
+
+void ssi32_hif_idle(struct ssi32_spi_hif_priv *priv, bool idle)
+{
+
+	atomic_set(&priv->active_rq, !idle);
+	atomic_set(&priv->idle, idle);
+
+	if (idle && gpiod_get_value(priv->fc_gpio)) {
+			atomic_set(&priv->active_rq, 1);
+			irq_wake_thread(priv->fc_irq, priv);
+	}
+}
+
+/* Wait for flow control */
+static inline int ssi32_hif_wait_fc(struct ssi32_spi_hif_priv *priv,
+				    bool fc_enabled)
+{
+	int ret, val;
+	int count = 8;
+
+	/* try to poll the FC line. It will allow us to speed up the response */
+	while (count) {
+		/*
+		 * the switch from SLAVE_FC to SLAVE_REQ can be wary short.
+		 * make sure we didn't missed it.
+		 */
+		if (try_wait_for_completion(&priv->fc_complete))
+			return 0;
+
+		/*
+		 * Some rounds of polling for SLAVE_FC are still faster then
+		 * wait_for_completion.
+		 */
+		val = gpiod_get_value(priv->fc_gpio);
+		if (val == fc_enabled)
+			return 0;
+		count--;
+		usleep_range(10, 20);
+	}
+
+	/* now we are getting slow */
+	ret  = wait_for_completion_interruptible_timeout(&priv->fc_complete,
+							 msecs_to_jiffies(20));
+	if (!ret)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+int ssi32_shift_one_msg(struct ssi32_spi_hif_priv *priv,
+			void *tx_buf, void *rx_buf, size_t len)
+{
+	struct spi_device *spi = priv->spi;
+	struct spi_message m;
+	int ret = 0;
+	struct spi_transfer t = {
+		.len = 0,
+		.cs_change = 1,
+	};
+
+	/* prepare dummy message */
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	spi_bus_lock(spi->master);
+
+	/* send dummy to trigger CS */
+	reinit_completion(&priv->fc_complete);
+	spi_sync_locked(spi, &m);
+	ret = ssi32_hif_wait_fc(priv, true);
+	if (ret) {
+		/* recover the CS line and unlock the bus */
+		t.cs_change = 0;
+		spi_sync_locked(spi, &m);
+		dev_warn(&spi->dev, "FC failed, not Ready\n");
+		spi_bus_unlock(spi->master);
+		return ret;
+	}
+
+	/* prepare actual message */
+	t.tx_buf = tx_buf;
+	t.rx_buf = rx_buf;
+	t.len = len;
+	t.delay_usecs = 0;
+	t.cs_change = 0;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	/*
+	 * Using sync variant. according to my test, async is slower:
+	 * sync - 1m2.203s for 100000 packets
+	 * async - 1m14.416s for 100000 packets
+	 */
+	reinit_completion(&priv->fc_complete);
+	spi_sync_locked(spi, &m);
+	ret = ssi32_hif_wait_fc(priv, false);
+	if (ret)
+		dev_warn(&spi->dev, "FC failed, no ACK\n");
+	spi_bus_unlock(spi->master);
+	return ret;
+}
+
+/* send dummy to trigger CS */
+static void ssi32_shift_dummy_msg(struct ssi32_spi_hif_priv *priv)
+{
+	struct spi_device *spi = priv->spi;
+	struct spi_message m;
+	struct spi_transfer t = {
+		.len = 0,
+		.cs_change = 0,
+	};
+
+	reinit_completion(&priv->fc_complete);
+	/* prepare dummy message */
+	spi_message_init(&m);
+	spi_message_add_tail(&t, &m);
+
+	spi_bus_lock(spi->master);
+
+	spi_sync_locked(spi, &m);
+
+	spi_bus_unlock(spi->master);
+}
+
+static int ssi32_spi_hif_probe(struct spi_device *spi)
+{
+	struct ssi32_spi_hif_priv *priv;
+	int ret;
+
+	spi->bits_per_word = 8;
+
+	ret = spi_setup(spi);
+	if (ret < 0)
+		return ret;
+
+	priv = devm_kzalloc(&spi->dev, sizeof(struct ssi32_spi_hif_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	mutex_init(&priv->lock);
+	init_completion(&priv->fc_complete);
+	init_completion(&priv->input);
+	init_waitqueue_head(&priv->wait);
+
+	spi_set_drvdata(spi, priv);
+
+	priv->spi = spi;
+	spi->master->rt = 1;
+
+	priv->fc_gpio = devm_gpiod_get(&spi->dev, "fc", GPIOD_IN);
+	if (IS_ERR(priv->fc_gpio)) {
+		ret = PTR_ERR(priv->fc_gpio);
+		dev_err(&spi->dev, "Failed to request FC GPIO: %d\n", ret);
+		return ret;
+	}
+
+	priv->fc_irq = gpiod_to_irq(priv->fc_gpio);
+	if (priv->fc_irq < 0) {
+		dev_err(&spi->dev, "Can't registr IRQ for GPIO\n");
+		return priv->fc_irq;
+	}
+
+	ret = devm_request_threaded_irq(&spi->dev, priv->fc_irq, spi_fc_rq,
+					spi_fc_rq_thread, IRQF_TRIGGER_RISING
+					| IRQF_TRIGGER_FALLING,
+					dev_name(&spi->dev), priv);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to request FC IRQ\n");
+		return ret;
+	}
+
+	priv->debugfs = debugfs_create_dir(dev_name(&spi->dev), debugfs_root);
+
+	pm_runtime_forbid(spi->master->dev.parent);
+	/*
+	 * Shift a dummy packet to prepare the SPI Master. For example
+	 * set CS to proper state. FIXME: should it be fixed in SPI framework?
+	 */
+	ssi32_shift_dummy_msg(priv);
+
+	return ssi32_prot_init(priv);
+}
+
+static int ssi32_spi_hif_remove(struct spi_device *spi)
+{
+	struct ssi32_spi_hif_priv *priv = spi_get_drvdata(spi);
+
+	ssi32_prot_uninit(priv);
+
+	pm_runtime_allow(spi->master->dev.parent);
+	if (!priv)
+		return -ENODEV;
+
+	mutex_destroy(&priv->lock);
+
+	return 0;
+}
+
+static const struct of_device_id ssi32_spi_hif_match[] = {
+	{
+		.compatible = "rbcm,icc-ssi32",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, ssi32_spi_hif_match);
+
+static struct spi_driver ssi32_spi_hif_driver = {
+	.driver = {
+		.name		= DRIVER_NAME,
+		.of_match_table = of_match_ptr(ssi32_spi_hif_match),
+	},
+	.probe		= ssi32_spi_hif_probe,
+	.remove		= ssi32_spi_hif_remove,
+};
+
+static int __init ssi32_spi_hif_init(void)
+{
+	debugfs_root = debugfs_create_dir(DRIVER_NAME, NULL);
+	if (IS_ERR_OR_NULL(debugfs_root)) {
+		pr_err("%s: Filed to create debufs entry.\n", DRIVER_NAME);
+		return -ENOMEM;
+	}
+
+	return spi_register_driver(&ssi32_spi_hif_driver);
+}
+subsys_initcall(ssi32_spi_hif_init);
+
+static void __exit ssi32_spi_hif_exit(void)
+{
+	debugfs_remove_recursive(debugfs_root);
+	spi_unregister_driver(&ssi32_spi_hif_driver);
+}
+module_exit(ssi32_spi_hif_exit);
+
+MODULE_AUTHOR("Oleksij Rempel <linux@rempel-privat.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/icc/ssi32/ssi32_spi_hif.h b/drivers/icc/ssi32/ssi32_spi_hif.h
new file mode 100644
index 0000000..bf51a58
--- /dev/null
+++ b/drivers/icc/ssi32/ssi32_spi_hif.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *	 Oleksij Rempel
+ *		<fixed-term.Oleksij.Rempel@de.bosch.com>
+ *		<linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#ifndef SSI32_SPI_HIF_H
+#define SSI32_SPI_HIF_H
+
+#include "ssi32_prot.h"
+
+struct ssi32_spi_hif_priv {
+	struct mutex            lock;   /* protect from simultaneous accesses */
+	u8                      port_config;
+	struct spi_device       *spi;
+	struct gpio_desc        *fc_gpio;
+	int			fc_irq;
+	struct dentry           *debugfs;
+	struct completion       fc_complete;
+	struct completion       input;
+	atomic_t                active_rq;
+	atomic_t                idle;
+	struct task_struct      *task;
+	wait_queue_head_t       wait;
+
+	struct ssi32_prot_priv	ssi32pp;
+};
+
+void ssi32_hif_idle(struct ssi32_spi_hif_priv *priv, bool idle);
+int ssi32_shift_one_msg(struct ssi32_spi_hif_priv *priv,
+			void *tx_buf, void *rx_buf, size_t len);
+
+int ssi32_prot_init(struct ssi32_spi_hif_priv *priv);
+void ssi32_prot_uninit(struct ssi32_spi_hif_priv *priv);
+
+#endif /* SSI32_H */
diff --git a/include/linux/icc/icc.h b/include/linux/icc/icc.h
new file mode 100644
index 0000000..e0b93e6
--- /dev/null
+++ b/include/linux/icc/icc.h
@@ -0,0 +1,88 @@
+#ifndef _ICC_CORE_H_
+#define _ICC_CORE_H_
+
+#include <linux/device.h>
+
+#define ICC_MAX_LUNS 255
+
+struct icc_trf;
+struct icc_master;
+
+struct icc_device {
+	struct device           dev;
+	struct icc_master	*iccm;
+	void                    *data;
+	bool                    configured;
+	unsigned int		lun_number;
+
+	int			(*rx_cb)(struct icc_device *, void *, size_t);
+};
+
+struct icc_master {
+	struct device           *dev;
+	struct class		*icc_class;
+	int			max_data_size;
+
+	int (*trf_alloc)(struct icc_master *, struct icc_trf *, u8, size_t);
+	int (*trf_xmit)(struct icc_master *, struct icc_trf *);
+
+	struct icc_device	lun[ICC_MAX_LUNS];
+};
+
+struct icc_driver {
+	struct device_driver	driver;
+	int			(*probe)(struct icc_device *);
+	int			(*remove)(struct icc_device *);
+	void			(*shutdown)(struct icc_device *);
+};
+
+struct icc_trf {
+	void		*buf;
+	void		*hdr;
+	void		*data;
+	size_t		size;
+	size_t		data_size;
+};
+
+static inline struct icc_device *to_icc_device(struct device *dev)
+{
+	return dev ? container_of(dev, struct icc_device, dev) : NULL;
+}
+
+static inline struct icc_driver *to_icc_driver(struct device_driver *drv)
+{
+	return drv ? container_of(drv, struct icc_driver, driver) : NULL;
+}
+
+static inline void icc_set_drvdata(struct icc_device *iccd, void *data)
+{
+	dev_set_drvdata(&iccd->dev, data);
+}
+
+static inline void *icc_get_drvdata(struct icc_device *iccd)
+{
+	return dev_get_drvdata(&iccd->dev);
+}
+
+static inline void icc_set_rxcb(struct icc_device *iccd,
+		int (*rx_cb)(struct icc_device *, void *, size_t))
+{
+	iccd->rx_cb = rx_cb;
+}
+
+static inline int icc_trf_alloc(struct icc_master *iccm,
+		struct icc_trf *trf, u8 lun, size_t size)
+{
+	return iccm->trf_alloc(iccm, trf, lun, size);
+}
+static inline int icc_trf_xmit(struct icc_master *iccm,
+		struct icc_trf *trf)
+{
+	return iccm->trf_xmit(iccm, trf);
+}
+
+int icc_register_driver(struct icc_driver *);
+void icc_unregister_driver(struct icc_driver *);
+void icc_add_luns(struct icc_master *iccm);
+
+#endif /* _ICC_CORE_H_ */
diff --git a/include/linux/icc/ssi32.h b/include/linux/icc/ssi32.h
new file mode 100644
index 0000000..8015c21e
--- /dev/null
+++ b/include/linux/icc/ssi32.h
@@ -0,0 +1,32 @@
+#ifndef _SSI32_CORE_H_
+#define _SSI32_CORE_H_
+
+#include <linux/device.h>
+
+#define SSI32_MAX_LUNS		255
+
+#define SSI32_HDR_SIZE          4
+struct ssi32_prot_hdr {
+	u8 ctrl;
+	u8 lun;
+	u8 dlc;
+	u8 chk;
+} __packed;
+
+#define SSI32_ACK_SIZE          4
+struct ssi32_prot_ack {
+	u8 tx_ack;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct ssi32_prot_trf {
+	void                    *buf;
+	struct ssi32_prot_hdr   *hdr;
+	void                    *data;
+	size_t                  size;
+	size_t                  data_size;
+};
+
+#endif /* _SSI32_H_ */
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC 2/3] watchdog: add icc watchdog driver
  2016-11-09  6:04 [RFC 0/3] add Inter Chip Communication framework fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 1/3] icc: add add SSI32 transport driver fixed-term.Oleksij.Rempel
@ 2016-11-09  6:04 ` fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 3/3] char: add icc_char device fixed-term.Oleksij.Rempel
  2 siblings, 0 replies; 4+ messages in thread
From: fixed-term.Oleksij.Rempel @ 2016-11-09  6:04 UTC (permalink / raw)
  To: linux-kernel, linux-soc, dirk.behme, mkl; +Cc: Oleksij Rempel, Oleksij Rempel

From: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>

this driver is needed to ping over ICC bus a watchdog.

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>
---
 drivers/watchdog/Kconfig   |   8 ++
 drivers/watchdog/Makefile  |   1 +
 drivers/watchdog/icc_wdt.c | 185 +++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 194 insertions(+)
 create mode 100644 drivers/watchdog/icc_wdt.c

diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
index 16f2023..7e52835 100644
--- a/drivers/watchdog/Kconfig
+++ b/drivers/watchdog/Kconfig
@@ -104,6 +104,14 @@ config GPIO_WATCHDOG
 	  If you say yes here you get support for watchdog device
 	  controlled through GPIO-line.
 
+config ICC_WATCHDOG
+	tristate "ICC watchdog"
+	depends on ICC
+	select WATCHDOG_CORE
+	help
+	  If you say yes here you get support for watchdog device
+	  controlled through ICC bus.
+
 config MENF21BMC_WATCHDOG
 	tristate "MEN 14F021P00 BMC Watchdog"
 	depends on MFD_MENF21BMC
diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
index 5c19294..40451cc 100644
--- a/drivers/watchdog/Makefile
+++ b/drivers/watchdog/Makefile
@@ -181,6 +181,7 @@ obj-$(CONFIG_DA9052_WATCHDOG) += da9052_wdt.o
 obj-$(CONFIG_DA9055_WATCHDOG) += da9055_wdt.o
 obj-$(CONFIG_DA9063_WATCHDOG) += da9063_wdt.o
 obj-$(CONFIG_GPIO_WATCHDOG)	+= gpio_wdt.o
+obj-$(CONFIG_ICC_WATCHDOG)	+= icc_wdt.o
 obj-$(CONFIG_WM831X_WATCHDOG) += wm831x_wdt.o
 obj-$(CONFIG_WM8350_WATCHDOG) += wm8350_wdt.o
 obj-$(CONFIG_MAX63XX_WATCHDOG) += max63xx_wdt.o
diff --git a/drivers/watchdog/icc_wdt.c b/drivers/watchdog/icc_wdt.c
new file mode 100644
index 0000000..a89dc15
--- /dev/null
+++ b/drivers/watchdog/icc_wdt.c
@@ -0,0 +1,185 @@
+/*
+ * Driver for RBCM INC Gen3 Watchdog.
+ *   RBCM - Robert Bosch Car Multimedia GmbH
+ *   INC - Inter node communication.
+ *   Gen3 - Generation 3 products.
+ * Short: rig3 :)
+ *
+ * Copyright (C) Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *       Oleksij Rempel
+ *              <fixed-term.Oleksij.Rempel@de.bosch.com>
+ *              <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/reset.h>
+#include <linux/watchdog.h>
+#include <linux/icc/icc.h>
+
+struct icc_wdt_priv {
+	struct device		*dev;
+	struct icc_device	*iccd;
+	struct watchdog_device	wdd;
+
+	struct icc_trf	trf;
+	bool			check_pong;
+	atomic_t		pong;
+};
+
+static int icc_wdt_feed(struct watchdog_device *wdd)
+{
+	struct icc_wdt_priv *priv = watchdog_get_drvdata(wdd);
+	struct icc_device *iccd = priv->iccd;
+
+	if (priv->check_pong && atomic_read(&priv->pong))
+		dev_warn_ratelimited(priv->dev, "making ping without getting pong\n");
+
+	atomic_set(&priv->pong, 1);
+
+	return icc_trf_xmit(iccd->iccm, &priv->trf);
+}
+
+static int icc_wdt_enable(struct watchdog_device *wdd)
+{
+	return 0;
+}
+
+static int icc_wdt_stop(struct watchdog_device *wdd)
+{
+	return 0;
+}
+
+static const struct watchdog_info icc_wdt_ident = {
+	.identity         =	"RBCM, Inc Gen3 WatchDog",
+};
+
+static struct watchdog_ops icc_wdt_ops = {
+	.owner		= THIS_MODULE,
+	.start		= icc_wdt_enable,
+	.stop		= icc_wdt_stop,
+	.ping		= icc_wdt_feed,
+};
+
+static int icc_wdt_rx_cb(struct icc_device *iccd, void *rx_buf, size_t size)
+{
+	struct icc_wdt_priv *priv = icc_get_drvdata(iccd);
+
+	atomic_set(&priv->pong, 0);
+
+	return 0;
+}
+
+static int icc_wdt_int_trf(struct icc_wdt_priv *priv)
+{
+	struct icc_device *iccd = priv->iccd;
+	struct icc_trf *trf = &priv->trf;
+
+	u8 *buf;
+	int ret;
+
+	ret = icc_trf_alloc(iccd->iccm, trf, 9, 1);
+	if (ret)
+		return ret;
+
+	buf = trf->data;
+	buf[0] = 0x62;
+
+	return 0;
+}
+
+static int icc_wdt_probe(struct icc_device *iccd)
+{
+	struct device *dev = &iccd->dev;
+	struct icc_wdt_priv *priv;
+	struct watchdog_device *wdd;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(struct icc_wdt_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	priv->iccd = iccd;
+
+	wdd = &priv->wdd;
+	wdd->info = &icc_wdt_ident;
+	wdd->ops = &icc_wdt_ops;
+	wdd->min_timeout = 1;
+	wdd->max_timeout = 10;
+	wdd->parent = dev;
+
+	icc_set_drvdata(iccd, priv);
+	icc_set_rxcb(iccd, icc_wdt_rx_cb);
+	watchdog_set_drvdata(wdd, priv);
+
+	/*
+	 * If 'timeout-sec' unspecified in devicetree, assume a 30 second
+	 * default, unless the max timeout is less than 30 seconds, then use
+	 * the max instead.
+	 */
+	watchdog_init_timeout(wdd, 10, dev);
+
+	ret = watchdog_register_device(wdd);
+	if (ret)
+		return ret;
+
+	ret = icc_wdt_int_trf(priv);
+	if (ret)
+		return ret;
+
+	dev_info(dev, "Watchdog enabled\n");
+	return 0;
+}
+
+static int icc_wdt_remove(struct icc_device *iccd)
+{
+	struct icc_wdt_priv *priv = icc_get_drvdata(iccd);
+
+	watchdog_unregister_device(&priv->wdd);
+
+	return 0;
+}
+
+static const struct of_device_id icc_wdt_of_match[] = {
+	{ .compatible = "rbcm,inc-wdt-gen3" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, icc_wdt_of_match);
+
+static struct icc_driver icc_wdt_driver = {
+	.driver = {
+		.name = "inc-wdt-gen3",
+		.owner = THIS_MODULE,
+		.of_match_table	= icc_wdt_of_match,
+	},
+	.probe = icc_wdt_probe,
+	.remove = icc_wdt_remove,
+};
+
+static int __init icc_wdt_init(void)
+{
+	return icc_register_driver(&icc_wdt_driver);
+}
+module_init(icc_wdt_init);
+
+static void __exit icc_wdt_exit(void)
+{
+	icc_unregister_driver(&icc_wdt_driver);
+}
+module_exit(icc_wdt_exit);
+
+MODULE_DESCRIPTION("ICC WatchDog Driver");
+MODULE_AUTHOR("Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>");
+MODULE_LICENSE("GPL");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [RFC 3/3] char: add icc_char device
  2016-11-09  6:04 [RFC 0/3] add Inter Chip Communication framework fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 1/3] icc: add add SSI32 transport driver fixed-term.Oleksij.Rempel
  2016-11-09  6:04 ` [RFC 2/3] watchdog: add icc watchdog driver fixed-term.Oleksij.Rempel
@ 2016-11-09  6:04 ` fixed-term.Oleksij.Rempel
  2 siblings, 0 replies; 4+ messages in thread
From: fixed-term.Oleksij.Rempel @ 2016-11-09  6:04 UTC (permalink / raw)
  To: linux-kernel, linux-soc, dirk.behme, mkl; +Cc: Oleksij Rempel, Oleksij Rempel

From: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>

This is simple interface to talk over SSI32 bus.

For testing fallwoing commands can be used:
while true; do cat /dev/iccch015 | hexdump; done &
while true; do echo 1 > /dev/iccch015; done

Signed-off-by: Oleksij Rempel <linux@rempel-privat.de>
Signed-off-by: Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>
---
 drivers/char/Kconfig    |   9 ++
 drivers/char/Makefile   |   1 +
 drivers/char/icc_char.c | 280 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 290 insertions(+)
 create mode 100644 drivers/char/icc_char.c

diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index a4af822..e610173 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -611,5 +611,14 @@ config TILE_SROM
 
 source "drivers/char/xillybus/Kconfig"
 
+config ICC_CHAR
+	tristate "ICC char interface"
+	depends on ICC
+	default y
+	help
+	  If you say yes here you get char device communication over
+	  ICC bus.
+
+
 endmenu
 
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index d06cde26..0439daa 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -62,3 +62,4 @@ js-rtc-y = rtc.o
 
 obj-$(CONFIG_TILE_SROM)		+= tile-srom.o
 obj-$(CONFIG_XILLYBUS)		+= xillybus/
+obj-$(CONFIG_ICC_CHAR)		+= icc_char.o
diff --git a/drivers/char/icc_char.c b/drivers/char/icc_char.c
new file mode 100644
index 0000000..a4005c7
--- /dev/null
+++ b/drivers/char/icc_char.c
@@ -0,0 +1,280 @@
+/*
+ * Char device for ICC bus.
+ *
+ * Copyright (C) Robert Bosch Car Multimedia GmbH
+ * Authors:
+ *       Oleksij Rempel
+ *              <fixed-term.Oleksij.Rempel@de.bosch.com>
+ *              <linux@rempel-privat.de>
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/cdev.h>
+#include <linux/fs.h>
+#include <linux/icc/icc.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/proc_fs.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include <asm/uaccess.h>
+
+static unsigned int icc_ch_major;
+
+struct icc_ch_priv {
+	struct device		*dev;
+	struct icc_device	*iccd;
+	struct cdev		cdev;
+
+
+	struct icc_trf	trf;
+	void			*rx_buf;
+	size_t			rx_buf_size;
+	struct mutex            rx_buf_lock;
+	wait_queue_head_t	rx_wq;
+};
+
+static int icc_ch_int_trf(struct icc_ch_priv *priv)
+{
+	struct icc_device *iccd = priv->iccd;
+	struct icc_master *iccm = iccd->iccm;
+	struct icc_trf *trf = &priv->trf;
+	int ret;
+
+	ret = icc_trf_alloc(iccm, trf, iccd->lun_number,
+				iccm->max_data_size);
+	if (ret)
+		return ret;
+
+	priv->rx_buf = devm_kzalloc(priv->dev, iccm->max_data_size,
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static ssize_t icc_ch_fop_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct icc_ch_priv *priv = file->private_data;
+	struct icc_device *iccd = priv->iccd;
+	struct icc_master *iccm = iccd->iccm;
+	int ret;
+
+	if (count > iccm->max_data_size)
+		return -EINVAL;
+
+	count = min(count, (size_t)iccm->max_data_size);
+	if (copy_from_user(priv->trf.data, buf, count))
+		return -EFAULT;
+
+	priv->trf.data_size = count;
+
+	ret = icc_trf_xmit(iccm, &priv->trf);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static int icc_ch_wait(struct icc_ch_priv *priv) {
+	return wait_event_interruptible(priv->rx_wq, priv->rx_buf_size);
+}
+
+static void icc_ch_wake(struct icc_ch_priv *priv) {
+	wake_up_interruptible(&priv->rx_wq);
+}
+
+static ssize_t icc_ch_fop_read(struct file *file, char *buf,
+			  size_t count, loff_t *ppos)
+{
+	struct icc_ch_priv *priv = file->private_data;
+	size_t ret = 0;
+
+	if (*ppos != 0)
+		return 0;
+
+	ret = icc_ch_wait(priv);
+	if (ret)
+		return ret;
+
+	mutex_lock(&priv->rx_buf_lock);
+
+	if (!priv->rx_buf_size) {
+		ret = 0;
+		goto done;
+	}
+
+	if (count < priv->rx_buf_size) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (copy_to_user(buf, priv->rx_buf, priv->rx_buf_size)) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = *ppos = priv->rx_buf_size;
+	priv->rx_buf_size = 0;
+
+	mutex_unlock(&priv->rx_buf_lock);
+
+done:
+	return ret;
+}
+
+static int icc_ch_fop_open(struct inode *ino, struct file *file)
+{
+	file->private_data =
+		container_of(ino->i_cdev, struct icc_ch_priv, cdev);
+
+	return 0;
+}
+
+static const struct file_operations icc_ch_fops = {
+	.owner		= THIS_MODULE,
+	.open		= icc_ch_fop_open,
+	.read		= icc_ch_fop_read,
+	.write		= icc_ch_fop_write,
+};
+
+static int icc_ch_rx_cb(struct icc_device *iccd, void *rx_buf, size_t size)
+{
+	struct icc_ch_priv *priv = icc_get_drvdata(iccd);
+
+	/*
+	 * if we can't transfer it no, drop it
+	 * allow other do the job
+	 */
+	if (!mutex_trylock(&priv->rx_buf_lock))
+		return -EBUSY;
+
+	/*
+	 * TODO: we should decide what kind of logic do we use.
+	 * For example for GPS it would make no match sense to
+	 * keep old and not valid data.
+	 * For now, let's be save and keep it.
+	 */
+	if (priv->rx_buf_size) {
+		mutex_unlock(&priv->rx_buf_lock);
+		return -EBUSY;
+	}
+
+	memcpy(priv->rx_buf, rx_buf, size);
+	priv->rx_buf_size = size;
+
+	mutex_unlock(&priv->rx_buf_lock);
+	icc_ch_wake(priv);
+
+	return 0;
+}
+
+static int icc_ch_probe(struct icc_device *iccd)
+{
+	struct icc_master *iccm = iccd->iccm;
+	struct device *dev = &iccd->dev;
+	struct device *dev1;
+	struct icc_ch_priv *priv;
+	int ret;
+
+	priv = devm_kzalloc(dev, sizeof(struct icc_ch_priv),
+			    GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->dev = dev;
+	priv->iccd = iccd;
+	mutex_init(&priv->rx_buf_lock);
+	init_waitqueue_head(&priv->rx_wq);
+
+	icc_set_drvdata(iccd, priv);
+	icc_set_rxcb(iccd, icc_ch_rx_cb);
+
+	ret = icc_ch_int_trf(priv);
+	if (ret)
+		return ret;
+
+	cdev_init(&priv->cdev, &icc_ch_fops);
+	priv->cdev.owner = THIS_MODULE;
+	ret = cdev_add(&priv->cdev, MKDEV(icc_ch_major, iccd->lun_number), 1);
+	if (ret) {
+		dev_err(dev, "filed to add cdev\n");
+		return ret;
+	}
+
+	dev1 = device_create(iccm->icc_class, dev,
+		      MKDEV(icc_ch_major, iccd->lun_number),
+		      priv, "iccch%03d", iccd->lun_number);
+
+	if (IS_ERR(dev1)) {
+		dev_info(dev, "ICC chardev filed: %i\n", PTR_ERR(dev1));
+		return PTR_ERR(dev1);
+	}
+
+	dev_info(dev, "ICC chardev enabled\n");
+	return 0;
+}
+
+static int icc_ch_remove(struct icc_device *iccd)
+{
+	struct icc_master *iccm = iccd->iccm;
+
+	device_destroy(iccm->icc_class,
+		       MKDEV(icc_ch_major, iccd->lun_number));
+	return 0;
+}
+
+static const struct of_device_id icc_ch_of_match[] = {
+	{ .compatible = "rbcm,ssi32-char-v1" },
+	{ .compatible = "rbcm,icc-char-v1" },
+	{},
+};
+MODULE_DEVICE_TABLE(of, icc_ch_of_match);
+
+static struct icc_driver icc_ch_driver = {
+	.driver = {
+		.name = "icc-ch",
+		.owner = THIS_MODULE,
+		.of_match_table	= icc_ch_of_match,
+	},
+	.probe = icc_ch_probe,
+	.remove = icc_ch_remove,
+};
+
+static int __init icc_ch_init(void)
+{
+	static dev_t dev;
+	int ret;
+
+	ret = alloc_chrdev_region(&dev, 0, ICC_MAX_LUNS, "iccch");
+	if (ret)
+		pr_err("Unable to register ICC chdev region\n");
+
+	icc_ch_major = MAJOR(dev);
+
+	ret = icc_register_driver(&icc_ch_driver);
+	if (ret)
+		pr_err("Unable to register ICC chdev\n");
+
+	return ret;
+}
+
+module_init(icc_ch_init);
+
+static void __exit
+icc_ch_exit(void)
+{
+	icc_unregister_driver(&icc_ch_driver);
+	unregister_chrdev_region(MKDEV(icc_ch_major, 0), ICC_MAX_LUNS);
+}
+
+module_exit(icc_ch_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Oleksij Rempel <fixed-term.Oleksij.Rempel@de.bosch.com>");
+MODULE_DESCRIPTION("ICC chardev");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2016-11-09  6:05 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-09  6:04 [RFC 0/3] add Inter Chip Communication framework fixed-term.Oleksij.Rempel
2016-11-09  6:04 ` [RFC 1/3] icc: add add SSI32 transport driver fixed-term.Oleksij.Rempel
2016-11-09  6:04 ` [RFC 2/3] watchdog: add icc watchdog driver fixed-term.Oleksij.Rempel
2016-11-09  6:04 ` [RFC 3/3] char: add icc_char device fixed-term.Oleksij.Rempel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).