* [RFC PATCH 0/3] new bus type siox
@ 2016-03-11 21:52 Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX Uwe Kleine-König
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Uwe Kleine-König @ 2016-03-11 21:52 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman; +Cc: kernel
Hello,
this is a patch set for a new bus type called SIOX created by the German
company Eckelmann. This is a bus with four data lines. The signales DIN (from
devices to master) and DOUT (from master to devices) are daisy chained through
all devices. DLD and DCLK are shared. A pulse on DCLK pushs bits on the DIN and
DOUT chains in the respective direction. A pulse on DLD makes the devices
sample their input and output stated from/to DIN and DOUT.
There are still some open issues, at least:
- the bus logic is still broken somehow. When a bus with two devices is
instantiated and the siox-bus-gpio module is unloaded, the bus master
(siox-0) disappears, but the two devices (siox-0-0 and siox-0-1)
don't.
- no device tree binding doc for bus driver
- the devices should be polled each 25 ms, otherwise a per-device
watchdog resets the device. I'm currently using a workqueue for that,
and on a loaded machine sometimes the watchdog triggers. So maybe I
need a different approach here? But I didn't debug that yet, so this
is just FYI.
- some mechanism (I thought about an uevent) to report that a watchdog
triggered would be nice.
I asked a few questions in #kernelnewbies to solve the first issue, Greg
asked for a code drop, so here it is.
Thanks for your time and interest,
Uwe
Uwe Kleine-König (3):
siox: new driver/bus framework for Eckelmann SIOX
siox: add gpio bus driver
gpio: new driver to work with a 8x12 siox
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/gpio/Kconfig | 5 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-siox.c | 280 +++++++++++++++++++++
drivers/siox/Kconfig | 9 +
drivers/siox/Makefile | 2 +
drivers/siox/siox-bus-gpio.c | 168 +++++++++++++
drivers/siox/siox-core.c | 572 +++++++++++++++++++++++++++++++++++++++++++
drivers/siox/siox.h | 48 ++++
include/linux/siox.h | 53 ++++
11 files changed, 1141 insertions(+)
create mode 100644 drivers/gpio/gpio-siox.c
create mode 100644 drivers/siox/Kconfig
create mode 100644 drivers/siox/Makefile
create mode 100644 drivers/siox/siox-bus-gpio.c
create mode 100644 drivers/siox/siox-core.c
create mode 100644 drivers/siox/siox.h
create mode 100644 include/linux/siox.h
--
2.7.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX
2016-03-11 21:52 [RFC PATCH 0/3] new bus type siox Uwe Kleine-König
@ 2016-03-11 21:52 ` Uwe Kleine-König
2016-03-11 22:20 ` Greg Kroah-Hartman
2016-03-11 21:52 ` [RFC PATCH 2/3] siox: add gpio bus driver Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 3/3] gpio: new driver to work with a 8x12 siox Uwe Kleine-König
2 siblings, 1 reply; 7+ messages in thread
From: Uwe Kleine-König @ 2016-03-11 21:52 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman; +Cc: kernel
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/siox/Kconfig | 2 +
drivers/siox/Makefile | 1 +
drivers/siox/siox-core.c | 572 +++++++++++++++++++++++++++++++++++++++++++++++
drivers/siox/siox.h | 48 ++++
include/linux/siox.h | 53 +++++
7 files changed, 679 insertions(+)
create mode 100644 drivers/siox/Kconfig
create mode 100644 drivers/siox/Makefile
create mode 100644 drivers/siox/siox-core.c
create mode 100644 drivers/siox/siox.h
create mode 100644 include/linux/siox.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 6e973b8e3a3b..c2fbf214f8a3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -184,4 +184,6 @@ source "drivers/android/Kconfig"
source "drivers/nvdimm/Kconfig"
+source "drivers/siox/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index b64b49f6e01b..dd7b3cd1fd72 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,3 +165,4 @@ obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-$(CONFIG_ANDROID) += android/
+obj-$(CONFIG_SIOX) += siox/
diff --git a/drivers/siox/Kconfig b/drivers/siox/Kconfig
new file mode 100644
index 000000000000..471fbc009521
--- /dev/null
+++ b/drivers/siox/Kconfig
@@ -0,0 +1,2 @@
+menuconfig SIOX
+ tristate "Eckelmann SIOX Support"
diff --git a/drivers/siox/Makefile b/drivers/siox/Makefile
new file mode 100644
index 000000000000..d55cb5e08868
--- /dev/null
+++ b/drivers/siox/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SIOX) += siox-core.o
diff --git a/drivers/siox/siox-core.c b/drivers/siox/siox-core.c
new file mode 100644
index 000000000000..71c6b18f86ad
--- /dev/null
+++ b/drivers/siox/siox-core.c
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include "siox.h"
+
+struct workqueue_struct *wqueue;
+static bool siox_is_registered;
+
+void siox_master_lock(struct siox_master *smaster)
+{
+ mutex_lock(&smaster->lock);
+}
+
+void siox_master_unlock(struct siox_master *smaster)
+{
+ mutex_unlock(&smaster->lock);
+}
+
+struct siox_device *siox_device_add(struct siox_master *smaster);
+void siox_device_remove(struct siox_master *smaster);
+
+static void __siox_poll(struct siox_master *smaster)
+{
+ struct siox_device *sdevice;
+ size_t i = 0;
+ u8 prevstatus = smaster->status;
+
+ if (++smaster->status > 0x0d)
+ smaster->status = 0;
+
+ memset(smaster->buf, 0, smaster->setbuf_len);
+
+ list_for_each_entry_reverse(sdevice, &smaster->devices, node) {
+ struct siox_driver *sdriver =
+ sdevice->dev.driver ? to_siox_driver(sdevice->dev.driver) : NULL;
+
+ if (sdriver)
+ sdriver->set_data(sdevice, smaster->status,
+ &smaster->buf[i + 1]);
+
+ smaster->buf[i] = smaster->status;
+
+ i += sdevice->inbytes;
+ }
+
+ BUG_ON(i != smaster->setbuf_len);
+
+ smaster->pushpull(smaster, smaster->setbuf_len, smaster->buf,
+ smaster->getbuf_len,
+ smaster->buf + smaster->setbuf_len);
+
+ list_for_each_entry(sdevice, &smaster->devices, node) {
+ struct siox_driver *sdriver =
+ sdevice->dev.driver ? to_siox_driver(sdevice->dev.driver) : NULL;
+ u8 sdev_status = smaster->buf[i + sdevice->outbytes - 1];
+
+ /*
+ * bits 4:2 of status sample the respective bit in the status
+ * byte written in the previous cycle. Mask them out
+ * accordingly such that a set bit there indicates an error.
+ */
+ sdev_status ^= ~prevstatus & 0xe;
+ sdevice->status = sdev_status;
+
+ /*
+ * XXX trigger events for watchdog, changed jumper and misread
+ * counter. Should the bus stop to poll in these cases?
+ */
+
+ if (sdriver)
+ sdriver->get_data(sdevice, &smaster->buf[i]);
+
+ i += sdevice->outbytes;
+ }
+
+ if (smaster->active)
+ queue_delayed_work(wqueue, &smaster->poll, smaster->poll_interval);
+}
+
+static void siox_poll(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct siox_master *smaster = container_of(dwork, struct siox_master, poll);
+
+ siox_master_lock(smaster);
+ if (likely(smaster->active))
+ __siox_poll(smaster);
+ siox_master_unlock(smaster);
+}
+
+static int __siox_start(struct siox_master *smaster)
+{
+ dev_dbg(&smaster->dev, "%s\n", __func__);
+
+ if (!(smaster->setbuf_len + smaster->getbuf_len))
+ return -ENODEV;
+
+ if (!smaster->buf)
+ return -ENOMEM;
+
+ smaster->active = 1;
+
+ __siox_poll(smaster);
+
+ return 0;
+}
+
+static int siox_start(struct siox_master *smaster)
+{
+ int ret;
+
+ siox_master_lock(smaster);
+ ret = __siox_start(smaster);
+ siox_master_unlock(smaster);
+
+ return ret;
+}
+
+static int __siox_stop(struct siox_master *smaster)
+{
+ dev_dbg(&smaster->dev, "%s\n", __func__);
+ smaster->active = 0;
+ cancel_delayed_work(&smaster->poll);
+ return 0;
+}
+
+static int siox_stop(struct siox_master *smaster)
+{
+ int ret;
+
+ siox_master_lock(smaster);
+ ret = __siox_stop(smaster);
+ siox_master_unlock(smaster);
+
+ return ret;
+}
+
+static ssize_t type_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_device *sdev = to_siox_device(dev);
+
+ return sprintf(buf, "%s\n", sdev->type);
+}
+
+static DEVICE_ATTR_RO(type);
+
+static ssize_t inbytes_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_device *sdev = to_siox_device(dev);
+
+ return sprintf(buf, "%zu\n", sdev->inbytes);
+}
+
+static DEVICE_ATTR_RO(inbytes);
+
+static ssize_t outbytes_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_device *sdev = to_siox_device(dev);
+
+ return sprintf(buf, "%zu\n", sdev->outbytes);
+}
+
+static DEVICE_ATTR_RO(outbytes);
+
+static ssize_t status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_device *sdev = to_siox_device(dev);
+ u8 status;
+
+ siox_master_lock(sdev->smaster);
+
+ status = sdev->status;
+
+ siox_master_unlock(sdev->smaster);
+
+ return sprintf(buf, "%hhu\n", status);
+}
+
+static DEVICE_ATTR_RO(status);
+
+static struct attribute *siox_device_attrs[] = {
+ &dev_attr_type.attr,
+ &dev_attr_inbytes.attr,
+ &dev_attr_outbytes.attr,
+ &dev_attr_status.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(siox_device);
+
+static void siox_device_release(struct device *dev)
+{
+ struct siox_device *sdevice = to_siox_device(dev);
+
+ kfree(sdevice);
+}
+
+static struct device_type siox_device_type = {
+ .groups = siox_device_groups,
+ .release = siox_device_release,
+};
+
+static int siox_match(struct device *dev, struct device_driver *drv)
+{
+ if (dev->type != &siox_device_type)
+ return 0;
+
+ /* up to now there is only a single driver so keeping this simple */
+ return 1;
+}
+
+static struct bus_type siox_bus_type = {
+ .name = "siox",
+ .match = siox_match,
+};
+
+static int siox_driver_probe(struct device *dev)
+{
+ struct siox_driver *sdriver = to_siox_driver(dev->driver);
+ struct siox_device *sdevice = to_siox_device(dev);
+ int ret;
+
+ ret = sdriver->probe(sdevice);
+ return ret;
+}
+
+static int siox_driver_remove(struct device *dev)
+{
+ struct siox_driver *sdriver =
+ container_of(dev->driver, struct siox_driver, driver);
+ struct siox_device *sdevice = to_siox_device(dev);
+ int ret;
+
+ ret = sdriver->remove(sdevice);
+ return ret;
+}
+
+static void siox_driver_shutdown(struct device *dev)
+{
+ struct siox_driver *sdriver =
+ container_of(dev->driver, struct siox_driver, driver);
+ struct siox_device *sdevice = to_siox_device(dev);
+
+ sdriver->shutdown(sdevice);
+}
+
+static ssize_t poll_interval_ns_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+
+ return sprintf(buf, "%lld\n", jiffies_to_nsecs(smaster->poll_interval));
+}
+
+static ssize_t poll_interval_ns_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+ int ret;
+ u64 val;
+
+ siox_master_lock(smaster);
+
+ ret = kstrtou64(buf, 0, &val);
+ if (ret)
+ goto out_unlock;
+
+ smaster->poll_interval = nsecs_to_jiffies(val);
+
+out_unlock:
+ siox_master_unlock(smaster);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(poll_interval_ns);
+
+static ssize_t active_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+
+ return sprintf(buf, "%d\n", smaster->active);
+}
+
+static ssize_t active_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+ int ret;
+ int active;
+
+ ret = kstrtoint(buf, 0, &active);
+ if (ret < 0)
+ return ret;
+
+ if (!active == !smaster->active)
+ /* no change */
+ return count;
+
+ if (active)
+ ret = siox_start(smaster);
+ else
+ ret = siox_stop(smaster);
+
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+static DEVICE_ATTR_RW(active);
+
+static ssize_t device_add_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+ int ret;
+ char type[20] = "";
+ size_t inbytes = 0, outbytes = 0;
+
+ ret = sscanf(buf, "%20s %zu %zu", type, &inbytes, &outbytes);
+ if (ret != 3 || strcmp(type, "siox-12x8") ||
+ inbytes != 2 || outbytes != 4)
+ return -EINVAL;
+
+ siox_device_add(smaster);
+
+ return count;
+}
+
+static DEVICE_ATTR_WO(device_add);
+
+static ssize_t device_remove_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+
+ /* XXX? require to write <type> <inbytes> <outbytes> */
+ siox_device_remove(smaster);
+
+ return count;
+}
+
+static DEVICE_ATTR_WO(device_remove);
+
+static struct attribute *siox_master_attrs[] = {
+ &dev_attr_poll_interval_ns.attr,
+ &dev_attr_active.attr,
+ &dev_attr_device_add.attr,
+ &dev_attr_device_remove.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(siox_master);
+
+static void siox_master_release(struct device *dev)
+{
+ struct siox_master *smaster = to_siox_master(dev);
+
+ __siox_stop(smaster);
+}
+
+static struct device_type siox_master_type = {
+ .groups = siox_master_groups,
+ .release = siox_master_release,
+};
+
+int __siox_register_master(struct siox_master *smaster)
+{
+ int ret;
+
+ if (!siox_is_registered)
+ return -EPROBE_DEFER;
+
+ if (!smaster->pushpull)
+ return -EINVAL;
+
+ dev_set_name(&smaster->dev, "siox-%d", smaster->busno);
+ smaster->dev.bus = &siox_bus_type;
+ smaster->dev.type = &siox_master_type;
+
+ if (!smaster->poll_interval)
+ smaster->poll_interval = DIV_ROUND_UP(HZ, 40);
+
+ mutex_init(&smaster->lock);
+ INIT_LIST_HEAD(&smaster->devices);
+ INIT_DELAYED_WORK(&smaster->poll, siox_poll);
+
+ ret = device_register(&smaster->dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__siox_register_master);
+
+void siox_unregister_master(struct siox_master *smaster)
+{
+ device_unregister(&smaster->dev);
+}
+EXPORT_SYMBOL_GPL(siox_unregister_master);
+
+struct siox_device *siox_device_add(struct siox_master *smaster)
+{
+ struct siox_device *sdevice;
+ int ret;
+
+ sdevice = kzalloc(sizeof(*sdevice), GFP_KERNEL);
+ if (!sdevice)
+ return NULL;
+
+ sdevice->type = "siox-12x8";
+ sdevice->inbytes = 2;
+ sdevice->outbytes = 4;
+
+ sdevice->smaster = smaster;
+ sdevice->dev.parent = &smaster->dev;
+ sdevice->dev.bus = &siox_bus_type;
+ sdevice->dev.type = &siox_device_type;
+
+ siox_master_lock(smaster);
+
+ dev_set_name(&sdevice->dev, "siox-%d-%d",
+ smaster->busno, smaster->num_devices);
+
+ ret = device_register(&sdevice->dev);
+ if (ret) {
+ dev_err(&smaster->dev, "failed to register device: %d\n", ret);
+
+ goto err_device_register;
+ }
+
+ smaster->num_devices++;
+ list_add_tail(&sdevice->node, &smaster->devices);
+
+ smaster->setbuf_len += sdevice->inbytes;
+ smaster->getbuf_len += sdevice->outbytes;
+
+ if (smaster->buf_len < smaster->setbuf_len + smaster->getbuf_len) {
+ smaster->buf_len = smaster->setbuf_len + smaster->getbuf_len;
+ smaster->buf = krealloc(smaster->buf,
+ smaster->buf_len, GFP_KERNEL);
+ if (!smaster->buf) {
+ dev_err(&smaster->dev, "failed to realloc buffer to %zu\n",
+ smaster->buf_len);
+ if (smaster->active)
+ __siox_stop(smaster);
+ }
+ }
+
+ siox_master_unlock(smaster);
+
+ return sdevice;
+
+err_device_register:
+ siox_master_unlock(smaster);
+
+ kfree(sdevice);
+
+ return ERR_PTR(ret);
+}
+
+void siox_device_remove(struct siox_master *smaster)
+{
+ struct siox_device *sdevice;
+
+ siox_master_lock(smaster);
+
+ if (!smaster->num_devices) {
+ siox_master_unlock(smaster);
+ return;
+ }
+
+ sdevice = container_of(smaster->devices.prev, struct siox_device, node);
+ list_del(&sdevice->node);
+ smaster->num_devices--;
+
+ smaster->setbuf_len -= sdevice->inbytes;
+ smaster->getbuf_len -= sdevice->outbytes;
+
+ if (!smaster->num_devices)
+ __siox_stop(smaster);
+
+ siox_master_unlock(smaster);
+
+ device_unregister(&sdevice->dev);
+}
+
+int __siox_driver_register(struct siox_driver *sdriver, struct module *owner)
+{
+ int ret;
+
+ if (unlikely(!siox_is_registered))
+ return -EPROBE_DEFER;
+
+ if (!sdriver->set_data && !sdriver->get_data) {
+ pr_err("Driver %s doesn't provide needed callbacks\n",
+ sdriver->driver.name);
+ return -EINVAL;
+ }
+
+ sdriver->driver.owner = owner;
+ sdriver->driver.bus = &siox_bus_type;
+
+ if (sdriver->probe)
+ sdriver->driver.probe = siox_driver_probe;
+ if (sdriver->remove)
+ sdriver->driver.remove = siox_driver_remove;
+ if (sdriver->probe)
+ sdriver->driver.shutdown = siox_driver_shutdown;
+
+ ret = driver_register(&sdriver->driver);
+ if (ret)
+ pr_err("Failed to register siox driver %s (%d)\n",
+ sdriver->driver.name, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(__siox_driver_register);
+
+static int __init siox_init(void)
+{
+ int ret;
+
+ ret = bus_register(&siox_bus_type);
+ if (ret) {
+ pr_err("Registration of SIOX bus type failed: %d\n", ret);
+ return ret;
+ }
+
+ wqueue = create_singlethread_workqueue("siox");
+ if (!wqueue) {
+ pr_err("Creation of siox workqueue failed\n");
+ bus_unregister(&siox_bus_type);
+ return -ENOMEM;
+ }
+
+ siox_is_registered = true;
+
+ return 0;
+}
+subsys_initcall(siox_init);
+
+static void __exit siox_exit(void)
+{
+ flush_workqueue(wqueue);
+ destroy_workqueue(wqueue);
+ bus_unregister(&siox_bus_type);
+}
+module_exit(siox_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("Eckelmann SIOX driver core");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/siox/siox.h b/drivers/siox/siox.h
new file mode 100644
index 000000000000..9f0a8bc1f499
--- /dev/null
+++ b/drivers/siox/siox.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/workqueue.h>
+
+#include <linux/siox.h>
+
+#define to_siox_master(_dev) container_of((_dev), struct siox_master, dev)
+struct siox_master {
+ /* these fields should be initialized by the driver */
+ int busno;
+ int (*pushpull)(struct siox_master *smaster,
+ size_t setbuf_len, const u8 setbuf[],
+ size_t getbuf_len, u8 getbuf[]);
+
+ /* might be initialized by the driver, if 0 it is set to HZ / 40 */
+ unsigned long poll_interval; /* in jiffies */
+
+ /* framework private stuff */
+ struct mutex lock;
+ bool active;
+ struct module *owner;
+ struct device dev;
+ unsigned num_devices;
+ struct list_head devices;
+
+ size_t setbuf_len, getbuf_len;
+ size_t buf_len;
+ u8 *buf;
+ u8 status;
+
+ struct delayed_work poll;
+};
+
+int __siox_register_master(struct siox_master *smaster);
+
+static inline int siox_register_master(struct siox_master *smaster)
+{
+ smaster->owner = THIS_MODULE;
+ return __siox_register_master(smaster);
+}
+
+void siox_unregister_master(struct siox_master *smaster);
diff --git a/include/linux/siox.h b/include/linux/siox.h
new file mode 100644
index 000000000000..4ca0ab264be8
--- /dev/null
+++ b/include/linux/siox.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/device.h>
+
+#define to_siox_device(_dev) container_of((_dev), struct siox_device, dev)
+struct siox_device {
+ struct list_head node; /* node in smaster->devices */
+ struct siox_master *smaster;
+ struct device dev;
+
+ char *type;
+ size_t inbytes;
+ size_t outbytes;
+
+ u8 status;
+};
+
+#define to_siox_driver(_drv) container_of((_drv), struct siox_driver, driver)
+struct siox_driver {
+ int (*probe)(struct siox_device *);
+ int (*remove)(struct siox_device *);
+ void (*shutdown)(struct siox_device *);
+
+ /*
+ * buf is big enough to hold sdev->inbytes - 1 bytes, the status byte
+ * is in the scope of the framework.
+ */
+ int (*set_data)(struct siox_device *, u8 status, u8 buf[]);
+ /*
+ * buf is big enough to hold sdev->outbytes - 1 bytes, the status byte
+ * is in the scope of the framework
+ */
+ int (*get_data)(struct siox_device *, const u8 buf[]);
+
+ struct device_driver driver;
+};
+
+int __siox_driver_register(struct siox_driver *sdriver, struct module *owner);
+static inline int siox_driver_register(struct siox_driver *sdriver)
+{
+ return __siox_driver_register(sdriver, THIS_MODULE);
+}
+
+static inline void siox_driver_unregister(struct siox_driver *sdriver)
+{
+ return driver_unregister(&sdriver->driver);
+}
--
2.7.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 2/3] siox: add gpio bus driver
2016-03-11 21:52 [RFC PATCH 0/3] new bus type siox Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX Uwe Kleine-König
@ 2016-03-11 21:52 ` Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 3/3] gpio: new driver to work with a 8x12 siox Uwe Kleine-König
2 siblings, 0 replies; 7+ messages in thread
From: Uwe Kleine-König @ 2016-03-11 21:52 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman; +Cc: kernel
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/siox/Kconfig | 7 ++
drivers/siox/Makefile | 1 +
drivers/siox/siox-bus-gpio.c | 168 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 176 insertions(+)
create mode 100644 drivers/siox/siox-bus-gpio.c
diff --git a/drivers/siox/Kconfig b/drivers/siox/Kconfig
index 471fbc009521..7bd28527203f 100644
--- a/drivers/siox/Kconfig
+++ b/drivers/siox/Kconfig
@@ -1,2 +1,9 @@
menuconfig SIOX
tristate "Eckelmann SIOX Support"
+
+if SIOX
+
+config SIOX_BUS_GPIO
+ tristate "SIOX GPIO bus driver"
+
+endif
diff --git a/drivers/siox/Makefile b/drivers/siox/Makefile
index d55cb5e08868..a956f65206d5 100644
--- a/drivers/siox/Makefile
+++ b/drivers/siox/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_SIOX) += siox-core.o
+obj-$(CONFIG_SIOX_BUS_GPIO) += siox-bus-gpio.o
diff --git a/drivers/siox/siox-bus-gpio.c b/drivers/siox/siox-bus-gpio.c
new file mode 100644
index 000000000000..141640dea77b
--- /dev/null
+++ b/drivers/siox/siox-bus-gpio.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <linux/delay.h>
+
+#include "siox.h"
+
+#define DRIVER_NAME "siox-gpio"
+
+struct siox_gpio_ddata {
+ struct gpio_desc *din;
+ struct gpio_desc *dout;
+ struct gpio_desc *dclk;
+ struct gpio_desc *dld;
+
+ struct siox_master master;
+};
+
+unsigned int siox_clkhigh_ns = 1000;
+unsigned int siox_loadhigh_ns;
+unsigned int siox_bytegap_ns;
+
+static int siox_gpio_pushpull(struct siox_master *smaster,
+ size_t setbuf_len, const u8 setbuf[],
+ size_t getbuf_len, u8 getbuf[])
+{
+ struct siox_gpio_ddata *ddata = container_of(smaster, struct siox_gpio_ddata, master);
+ size_t i;
+ size_t cycles = max(setbuf_len, getbuf_len);
+
+ /* reset data and clock */
+ gpiod_set_value_cansleep(ddata->dout, 0);
+ gpiod_set_value_cansleep(ddata->dclk, 0);
+
+ gpiod_set_value_cansleep(ddata->dld, 1);
+ ndelay(siox_loadhigh_ns);
+ gpiod_set_value_cansleep(ddata->dld, 0);
+
+ for (i = 0; i < cycles; ++i) {
+ u8 set = 0, get = 0;
+ size_t j;
+
+ if (i >= cycles - setbuf_len)
+ set = setbuf[i - (cycles - setbuf_len)];
+
+ for (j = 0; j < 8; ++j) {
+ get <<= 1;
+ if (gpiod_get_value_cansleep(ddata->din))
+ get |= 1;
+
+ /* DOUT is logically inverted */
+ gpiod_set_value_cansleep(ddata->dout, !(set & 0x80));
+ set <<= 1;
+
+ gpiod_set_value_cansleep(ddata->dclk, 1);
+ ndelay(siox_clkhigh_ns);
+ gpiod_set_value_cansleep(ddata->dclk, 0);
+ }
+
+ if (i < getbuf_len)
+ getbuf[i] = get;
+
+ ndelay(siox_bytegap_ns);
+ }
+
+ gpiod_set_value_cansleep(ddata->dld, 1);
+ ndelay(siox_loadhigh_ns);
+ gpiod_set_value_cansleep(ddata->dld, 0);
+
+ /*
+ * Resetting dout isn't necessary protocol wise, but it makes the
+ * signals more pretty because the dout level is deterministic between
+ * cycles. Note that this only affects dout between the master and the
+ * first siox device. dout for the later devices depend on the output of
+ * the previous siox device.
+ */
+ gpiod_set_value_cansleep(ddata->dout, 0);
+
+ return 0;
+}
+
+static int siox_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct siox_gpio_ddata *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata) {
+ dev_err(dev, "Failed to allocate memory for driver data\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, ddata);
+
+ ddata->din = devm_gpiod_get(dev, "din", GPIOD_IN);
+ if (IS_ERR(ddata->din)) {
+ ret = PTR_ERR(ddata->din);
+ dev_err(dev, "Failed to get %s GPIO: %d\n", "din", ret);
+ return ret;
+ }
+
+ ddata->dout = devm_gpiod_get(dev, "dout", GPIOD_OUT_LOW);
+ if (IS_ERR(ddata->dout)) {
+ ret = PTR_ERR(ddata->dout);
+ dev_err(dev, "Failed to get %s GPIO: %d\n", "dout", ret);
+ return ret;
+ }
+
+ ddata->dclk = devm_gpiod_get(dev, "dclk", GPIOD_OUT_LOW);
+ if (IS_ERR(ddata->dclk)) {
+ ret = PTR_ERR(ddata->dclk);
+ dev_err(dev, "Failed to get %s GPIO: %d\n", "dclk", ret);
+ return ret;
+ }
+
+ ddata->dld = devm_gpiod_get(dev, "dld", GPIOD_OUT_LOW);
+ if (IS_ERR(ddata->dld)) {
+ ret = PTR_ERR(ddata->dld);
+ dev_err(dev, "Failed to get %s GPIO: %d\n", "dld", ret);
+ return ret;
+ }
+
+ ddata->master.dev.parent = dev;
+
+ ddata->master.pushpull = siox_gpio_pushpull;
+
+ return siox_register_master(&ddata->master);
+}
+
+static int siox_gpio_remove(struct platform_device *pdev)
+{
+ struct siox_gpio_ddata *ddata = platform_get_drvdata(pdev);
+
+ siox_unregister_master(&ddata->master);
+
+ return 0;
+}
+
+static const struct of_device_id siox_gpio_dt_ids[] = {
+ { .compatible = "eckelmann,siox-gpio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, siox_gpio_dt_ids);
+
+static struct platform_driver siox_gpio_driver = {
+ .probe = siox_gpio_probe,
+ .remove = siox_gpio_remove,
+
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = siox_gpio_dt_ids,
+ },
+};
+module_platform_driver(siox_gpio_driver);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRIVER_NAME);
--
2.7.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [RFC PATCH 3/3] gpio: new driver to work with a 8x12 siox
2016-03-11 21:52 [RFC PATCH 0/3] new bus type siox Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 2/3] siox: add gpio bus driver Uwe Kleine-König
@ 2016-03-11 21:52 ` Uwe Kleine-König
2 siblings, 0 replies; 7+ messages in thread
From: Uwe Kleine-König @ 2016-03-11 21:52 UTC (permalink / raw)
To: linux-kernel, Greg Kroah-Hartman; +Cc: kernel
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
drivers/gpio/Kconfig | 5 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio-siox.c | 280 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 286 insertions(+)
create mode 100644 drivers/gpio/gpio-siox.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 8f1fe739c985..c9cee1fb60aa 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -404,6 +404,11 @@ config GPIO_SCH311X
To compile this driver as a module, choose M here: the module will
be called gpio-sch311x.
+config GPIO_SIOX
+ tristate "SIOX GPIO support"
+ depends on SIOX
+ select GPIOLIB_IRQCHIP
+
config GPIO_SPEAR_SPICS
bool "ST SPEAr13xx SPI Chip Select as GPIO support"
depends on PLAT_SPEAR
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index f82cd678ce08..29d79f858003 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -93,6 +93,7 @@ obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o
obj-$(CONFIG_ARCH_TEGRA) += gpio-tegra.o
obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o
obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o
+obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_TPS6586X) += gpio-tps6586x.o
obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c
new file mode 100644
index 000000000000..bbc824390520
--- /dev/null
+++ b/drivers/gpio/gpio-siox.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2015 Pengutronix, Uwe Kleine-König <kernel@pengutronix.de>
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License version 2 as published by the
+ * Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/siox.h>
+#include <linux/gpio/driver.h>
+#include <linux/of.h>
+
+struct gpio_siox_ddata {
+ struct gpio_chip gchip;
+ struct irq_chip ichip;
+ struct mutex lock;
+ u32 irq_enable;
+ u32 irq_status;
+ u32 irq_type[20];
+ u8 setdata[1];
+ u8 getdata[3];
+};
+
+/* XXX
+ * should set block until data is shifted out?
+ * should get block until new data is shifted in?
+ */
+static int gpio_siox_set_data(struct siox_device *sdevice, u8 status, u8 buf[])
+{
+ struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
+
+ mutex_lock(&ddata->lock);
+ buf[0] = ddata->setdata[0];
+ mutex_unlock(&ddata->lock);
+
+ dev_vdbg(&sdevice->dev, "%s: status=0x%02hhx data=[%02hhx]\n",
+ __func__, status, buf[0]);
+
+ return 0;
+}
+
+static int gpio_siox_get_data(struct siox_device *sdevice, const u8 buf[])
+{
+ struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
+ size_t offset;
+
+ mutex_lock(&ddata->lock);
+
+ for (offset = 0; offset < 12; ++offset) {
+ unsigned bitpos = 11 - offset;
+ unsigned gpiolevel = buf[bitpos / 8] & (1 << bitpos % 8);
+ unsigned prev_gpiolevel = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
+
+ if (gpiolevel) {
+ if ((ddata->irq_type[offset] & IRQ_TYPE_LEVEL_HIGH) ||
+ ((ddata->irq_type[offset] & IRQ_TYPE_EDGE_RISING) && !prev_gpiolevel)) {
+ dev_info(&sdevice->dev, "trigger %zu\n", offset);
+ ddata->irq_status |= 1 << offset;
+ }
+ } else {
+ if ((ddata->irq_type[offset] & IRQ_TYPE_LEVEL_LOW) ||
+ ((ddata->irq_type[offset] & IRQ_TYPE_EDGE_FALLING) && prev_gpiolevel))
+ ddata->irq_status |= 1 << offset;
+ }
+
+ if ((ddata->irq_status & ddata->irq_enable & (1 << offset))) {
+ /*
+ * Conceptually handle_nested_irq should call the flow
+ * handler of the irq chip. But it doesn't, so we have
+ * to clean the irq_status here.
+ */
+ unsigned int irq = irq_find_mapping(ddata->gchip.irqdomain, offset);
+
+ dev_info(&sdevice->dev, "handle %u (%zu)\n", irq, offset);
+ handle_nested_irq(irq);
+ ddata->irq_status &= ~(1 << offset);
+ }
+ }
+
+ ddata->getdata[0] = buf[0];
+ ddata->getdata[1] = buf[1];
+ ddata->getdata[2] = buf[2];
+
+ mutex_unlock(&ddata->lock);
+
+ dev_vdbg(&sdevice->dev, "%s: data=[%02hhx %02hhx %02hhx]\n",
+ __func__, buf[0], buf[1], buf[2]);
+
+ return 0;
+}
+
+static void gpio_siox_irq_ack(struct irq_data *d)
+{
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
+ struct gpio_siox_ddata *ddata =
+ container_of(ic, struct gpio_siox_ddata, ichip);
+
+ pr_info("%s: %lu %x\n", __func__, d->hwirq, ddata->irq_status);
+ ddata->irq_status &= ~(1 << d->hwirq);
+}
+
+static void gpio_siox_irq_mask(struct irq_data *d)
+{
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
+ struct gpio_siox_ddata *ddata =
+ container_of(ic, struct gpio_siox_ddata, ichip);
+
+ pr_info("%s: %lu %x\n", __func__, d->hwirq, ddata->irq_enable);
+ ddata->irq_enable &= ~(1 << d->hwirq);
+}
+
+static void gpio_siox_irq_unmask(struct irq_data *d)
+{
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
+ struct gpio_siox_ddata *ddata =
+ container_of(ic, struct gpio_siox_ddata, ichip);
+
+ pr_info("%s: %lu %x\n", __func__, d->hwirq, ddata->irq_enable);
+ ddata->irq_enable |= 1 << d->hwirq;
+}
+
+static int gpio_siox_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct irq_chip *ic = irq_data_get_irq_chip(d);
+ struct gpio_siox_ddata *ddata =
+ container_of(ic, struct gpio_siox_ddata, ichip);
+
+ mutex_lock(&ddata->lock);
+ ddata->irq_type[d->hwirq] = type;
+ mutex_unlock(&ddata->lock);
+
+ return 0;
+}
+
+static int gpio_siox_get(struct gpio_chip *chip, unsigned offset)
+{
+ struct gpio_siox_ddata *ddata =
+ container_of(chip, struct gpio_siox_ddata, gchip);
+ int ret;
+
+ mutex_lock(&ddata->lock);
+
+ if (offset >= 12) {
+ unsigned bitpos = 19 - offset;
+ ret = ddata->setdata[0] & (1 << bitpos);
+ } else {
+ unsigned bitpos = 11 - offset;
+ ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
+ }
+
+ mutex_unlock(&ddata->lock);
+
+ return ret;
+}
+
+static void gpio_siox_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ struct gpio_siox_ddata *ddata =
+ container_of(chip, struct gpio_siox_ddata, gchip);
+ u8 mask = 1 << (19 - offset);
+
+ mutex_lock(&ddata->lock);
+
+ if (value)
+ ddata->setdata[0] |= mask;
+ else
+ ddata->setdata[0] &= ~mask;
+
+ mutex_unlock(&ddata->lock);
+}
+
+static int gpio_siox_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset >= 12)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int gpio_siox_direction_output(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ if (offset < 12)
+ return -EINVAL;
+
+ gpio_siox_set(chip, offset, value);
+ return 0;
+}
+
+static int gpio_siox_get_direction(struct gpio_chip *chip, unsigned offset)
+{
+ if (offset < 12)
+ return 1; /* input */
+ else
+ return 0; /* output */
+}
+
+static int gpio_siox_probe(struct siox_device *sdevice)
+{
+ struct gpio_siox_ddata *ddata;
+ int ret;
+
+ ddata = devm_kzalloc(&sdevice->dev, sizeof(*ddata), GFP_KERNEL);
+ if (!ddata) {
+ dev_info(&sdevice->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ dev_set_drvdata(&sdevice->dev, ddata);
+
+ mutex_init(&ddata->lock);
+
+ ddata->gchip.base = -1;
+ ddata->gchip.can_sleep = 1;
+ ddata->gchip.dev = &sdevice->dev;
+ ddata->gchip.owner = THIS_MODULE;
+ ddata->gchip.get = gpio_siox_get;
+ ddata->gchip.set = gpio_siox_set;
+ ddata->gchip.direction_input = gpio_siox_direction_input;
+ ddata->gchip.direction_output = gpio_siox_direction_output;
+ ddata->gchip.get_direction = gpio_siox_get_direction;
+ ddata->gchip.ngpio = 20;
+
+ ddata->ichip.name = "siox-gpio";
+ ddata->ichip.irq_ack = gpio_siox_irq_ack;
+ ddata->ichip.irq_mask = gpio_siox_irq_mask;
+ ddata->ichip.irq_unmask = gpio_siox_irq_unmask;
+ ddata->ichip.irq_set_type = gpio_siox_irq_set_type;
+
+ ret = gpiochip_add(&ddata->gchip);
+ if (ret) {
+ dev_err(&sdevice->dev, "Failed to register gpio chip (%d)\n", ret);
+ goto err_gpiochip;
+ }
+
+ ret = gpiochip_irqchip_add(&ddata->gchip, &ddata->ichip,
+ 0, handle_level_irq, IRQ_TYPE_EDGE_RISING);
+ if (ret) {
+ dev_err(&sdevice->dev, "Failed to register irq chip (%d)\n", ret);
+err_gpiochip:
+ gpiochip_remove(&ddata->gchip);
+ }
+ return ret;
+}
+
+static int gpio_siox_remove(struct siox_device *sdevice)
+{
+ struct gpio_siox_ddata *ddata = dev_get_drvdata(&sdevice->dev);
+
+ gpiochip_remove(&ddata->gchip);
+ return 0;
+}
+
+static struct siox_driver gpio_siox_driver = {
+ .probe = gpio_siox_probe,
+ .remove = gpio_siox_remove,
+ .set_data = gpio_siox_set_data,
+ .get_data = gpio_siox_get_data,
+ .driver = {
+ .name = "gpio-siox",
+ },
+};
+
+static int __init gpio_siox_init(void)
+{
+ pr_info("%s\n", __func__);
+ return siox_driver_register(&gpio_siox_driver);
+}
+module_init(gpio_siox_init);
+
+static void __exit gpio_siox_exit(void)
+{
+ siox_driver_unregister(&gpio_siox_driver);
+}
+module_exit(gpio_siox_exit);
+
+MODULE_AUTHOR("Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de>");
+MODULE_DESCRIPTION("SIOX gpio driver");
+MODULE_LICENSE("GPL v2");
--
2.7.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX
2016-03-11 21:52 ` [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX Uwe Kleine-König
@ 2016-03-11 22:20 ` Greg Kroah-Hartman
2016-03-12 13:43 ` Uwe Kleine-König
0 siblings, 1 reply; 7+ messages in thread
From: Greg Kroah-Hartman @ 2016-03-11 22:20 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: linux-kernel, kernel
On Fri, Mar 11, 2016 at 10:52:12PM +0100, Uwe Kleine-König wrote:
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> ---
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/siox/Kconfig | 2 +
> drivers/siox/Makefile | 1 +
> drivers/siox/siox-core.c | 572 +++++++++++++++++++++++++++++++++++++++++++++++
> drivers/siox/siox.h | 48 ++++
> include/linux/siox.h | 53 +++++
> 7 files changed, 679 insertions(+)
> create mode 100644 drivers/siox/Kconfig
> create mode 100644 drivers/siox/Makefile
> create mode 100644 drivers/siox/siox-core.c
> create mode 100644 drivers/siox/siox.h
> create mode 100644 include/linux/siox.h
What's the relationship between your "master" and "regular" devices
here? I think that's getting things confused, you are unregistering
your 'master' yet the children it controls don't go away because you
never unregister them. You need to fix this heirachy up a bit, and
maybe just drop the 'master' logic?
thanks,
greg k-h
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX
2016-03-11 22:20 ` Greg Kroah-Hartman
@ 2016-03-12 13:43 ` Uwe Kleine-König
2016-03-13 3:42 ` Greg Kroah-Hartman
0 siblings, 1 reply; 7+ messages in thread
From: Uwe Kleine-König @ 2016-03-12 13:43 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-kernel, kernel
Hello Greg,
On Fri, Mar 11, 2016 at 02:20:39PM -0800, Greg Kroah-Hartman wrote:
> On Fri, Mar 11, 2016 at 10:52:12PM +0100, Uwe Kleine-König wrote:
> > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > ---
> > drivers/Kconfig | 2 +
> > drivers/Makefile | 1 +
> > drivers/siox/Kconfig | 2 +
> > drivers/siox/Makefile | 1 +
> > drivers/siox/siox-core.c | 572 +++++++++++++++++++++++++++++++++++++++++++++++
> > drivers/siox/siox.h | 48 ++++
> > include/linux/siox.h | 53 +++++
> > 7 files changed, 679 insertions(+)
> > create mode 100644 drivers/siox/Kconfig
> > create mode 100644 drivers/siox/Makefile
> > create mode 100644 drivers/siox/siox-core.c
> > create mode 100644 drivers/siox/siox.h
> > create mode 100644 include/linux/siox.h
>
> What's the relationship between your "master" and "regular" devices
> here? I think that's getting things confused, you are unregistering
> your 'master' yet the children it controls don't go away because you
> never unregister them. You need to fix this heirachy up a bit, and
> maybe just drop the 'master' logic?
A master is the bus controller (i.e. it drives DOUT, DCLK and DLD). A
device is a consumer and provides DIN to the master.
The topology looks as follows:
,------->--DCLK-->---------------+----------------------.
^ v v
,--------. ,----------------------. ,------
| | | ,--------------. | |
| |--->--DOUT-->---|->-|shift register|->-|--->---|
| | | `--------------' | |
| master | | device | | device
| | | ,--------------. | |
| |---<--DIN---<---|-<-|shift register|-<-|---<---|
| | | `--------------' | |
`--------' `----------------------' `------
v ^ ^
`----------DLD-------------------+----------------------'
So there are two chains of shift registers, one for letting the master
write to the devices, and the other to let the master read from the
devices. DCLK is the clock to shift both chains by a single bit. An edge
on DLD is used to make the devices load the inputs and sample the
outputs.
I think I need the following:
void siox_unregister_master(struct siox_master *smaster)
{
for (each siox_device d of smaster)
siox_device_remove(d);
device_unregister(&smaster->dev);
}
But the locking scheme that is necessary isn't obvious for me. For
example I must prevent that after the for loop is done a new device is
registered. Do I need to prevent this explicitly (e.g. by doing
something like:
lock(smaster);
smaster->is_going_away = true;
for (each siox_device d of smaster)
siox_device_remove(d);
unlock(smaster);
device_unregister(&smaster->dev);
and let registering new devices fail if smaster->is_going_away is true?)
Or is there something that the kobject stuff/driver core can help me
with?
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX
2016-03-12 13:43 ` Uwe Kleine-König
@ 2016-03-13 3:42 ` Greg Kroah-Hartman
0 siblings, 0 replies; 7+ messages in thread
From: Greg Kroah-Hartman @ 2016-03-13 3:42 UTC (permalink / raw)
To: Uwe Kleine-König; +Cc: linux-kernel, kernel
On Sat, Mar 12, 2016 at 02:43:21PM +0100, Uwe Kleine-König wrote:
> Hello Greg,
>
> On Fri, Mar 11, 2016 at 02:20:39PM -0800, Greg Kroah-Hartman wrote:
> > On Fri, Mar 11, 2016 at 10:52:12PM +0100, Uwe Kleine-König wrote:
> > > Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
> > > ---
> > > drivers/Kconfig | 2 +
> > > drivers/Makefile | 1 +
> > > drivers/siox/Kconfig | 2 +
> > > drivers/siox/Makefile | 1 +
> > > drivers/siox/siox-core.c | 572 +++++++++++++++++++++++++++++++++++++++++++++++
> > > drivers/siox/siox.h | 48 ++++
> > > include/linux/siox.h | 53 +++++
> > > 7 files changed, 679 insertions(+)
> > > create mode 100644 drivers/siox/Kconfig
> > > create mode 100644 drivers/siox/Makefile
> > > create mode 100644 drivers/siox/siox-core.c
> > > create mode 100644 drivers/siox/siox.h
> > > create mode 100644 include/linux/siox.h
> >
> > What's the relationship between your "master" and "regular" devices
> > here? I think that's getting things confused, you are unregistering
> > your 'master' yet the children it controls don't go away because you
> > never unregister them. You need to fix this heirachy up a bit, and
> > maybe just drop the 'master' logic?
>
> A master is the bus controller (i.e. it drives DOUT, DCLK and DLD). A
> device is a consumer and provides DIN to the master.
>
> The topology looks as follows:
>
> ,------->--DCLK-->---------------+----------------------.
> ^ v v
> ,--------. ,----------------------. ,------
> | | | ,--------------. | |
> | |--->--DOUT-->---|->-|shift register|->-|--->---|
> | | | `--------------' | |
> | master | | device | | device
> | | | ,--------------. | |
> | |---<--DIN---<---|-<-|shift register|-<-|---<---|
> | | | `--------------' | |
> `--------' `----------------------' `------
> v ^ ^
> `----------DLD-------------------+----------------------'
>
> So there are two chains of shift registers, one for letting the master
> write to the devices, and the other to let the master read from the
> devices. DCLK is the clock to shift both chains by a single bit. An edge
> on DLD is used to make the devices load the inputs and sample the
> outputs.
>
> I think I need the following:
>
> void siox_unregister_master(struct siox_master *smaster)
> {
> for (each siox_device d of smaster)
> siox_device_remove(d);
>
> device_unregister(&smaster->dev);
> }
Yes, you have to do this, as that's how USB and other busses do it when
their "controllers" go away.
> But the locking scheme that is necessary isn't obvious for me. For
> example I must prevent that after the for loop is done a new device is
> registered. Do I need to prevent this explicitly (e.g. by doing
> something like:
>
> lock(smaster);
> smaster->is_going_away = true;
> for (each siox_device d of smaster)
> siox_device_remove(d);
> unlock(smaster);
>
> device_unregister(&smaster->dev);
>
> and let registering new devices fail if smaster->is_going_away is true?)
> Or is there something that the kobject stuff/driver core can help me
> with?
Look at what happens when you remove a USB host controller with the
locking there, it shouldn't be that hard.
thanks,
greg k-h
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2016-03-13 3:47 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-11 21:52 [RFC PATCH 0/3] new bus type siox Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 1/3] siox: new driver/bus framework for Eckelmann SIOX Uwe Kleine-König
2016-03-11 22:20 ` Greg Kroah-Hartman
2016-03-12 13:43 ` Uwe Kleine-König
2016-03-13 3:42 ` Greg Kroah-Hartman
2016-03-11 21:52 ` [RFC PATCH 2/3] siox: add gpio bus driver Uwe Kleine-König
2016-03-11 21:52 ` [RFC PATCH 3/3] gpio: new driver to work with a 8x12 siox Uwe Kleine-König
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®