linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
@ 2019-04-09 14:42 Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem Sven Van Asbroeck
                   ` (8 more replies)
  0 siblings, 9 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

This patch:
  1. adds a Fieldbus subsystem
  2. adds support for the HMS Industrial Networks AB Profinet card.

1. Fieldbus subsystem
---------------------

Fieldbus device (client) adapters allow data exchange with a PLC aka.
"Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
They are typically used when a Linux device wants to expose itself
as an actuator, motor, console light, switch, etc. over the fieldbus.
The framework is designed to provide a generic interface to Fieldbus
Devices from both the Linux Kernel and the userspace.

2. Add support for HMS Profinet Card
------------------------------------

Profinet is an industry technical standard for data communication over
Industrial Ethernet, designed for collecting data from, and controlling,
equipment in industrial systems, with a particular strength in delivering data
under tight time constraints (on the order of 1ms or less).

The profinet card itself is connected to the system via an industrial bus
called 'anybus'.

I have followed the bus driver/client driver pattern, and created an anybus
bus driver, plus a client driver for the profinet card.

In case this patch set gets (eventually) accepted, drivers for other anybus
client cards may follow: flnet, cc-link, ...

The anybus slot on the host is located on an 'anybus controller', which is
custom h/w designed by arcx. It exposes a dual anybus host implementation,
plus a power readout unrelated to the anybus.

v10:
	add a TODO file on how to take this patch set out of staging
	fix linker errors on defconfigs without HAS_IOMEM or REGULATOR

v9:
	Moved everything to staging/ so new userspace ABI can still be tweaked
	Suggested cross-posting to linux-netdev to widen audience
	Greg KH: call ida_destroy() on module exits
	replace arcx anybus controller's reset spinlock with mutex
		(no need for spinlock as never run in atomic context)
		udelay() can now be replaced with usleep_range()
	harmonized email addresses across patchset
	various whitespace changes to silence checkpatch.pl --strict

v8:
	allow fieldbus core to be built as a module (define as tristate).
	use IS_ENABLED() in fieldbus_dev.h to conditionally compile dummy functions.
	anybuss_core.ko uses symbols from fieldbus_dev.ko. Use "top level kbuild
		file" method to make this work.
		See Documentation/kbuild/modules.txt, section 6.3
	Confirm licence choice: GPL-v2 only. no changes.

v7:
	use %zu printf formatter to display size_t

v6:
	Randy Dunlap:
	correctly/consistently indent Kconfig
	spelling/punctuation improvements

	Greg KH:
	suggestion: simplify /sys/class/fieldbus_dev/fieldbus_devX/ -> /sys/class/fieldbus_dev/X/
		can't simplify: create_device() also determines the name of the
		associated char device. simplification would result in a char device
		named /dev/X ?
	applied above suggestion to arcx-controller driver, this works ok as it does not
		have associated char device
	class name left as-is (fieldbus_dev) to differentiate between
		"fieldbus controller" and "fieldbus device" in the future
	makefile clean-up
	sysfs attributes: snprintf -> sprintf
		except card_name, as it's externally provided and could potentially
		be > PAGE_SIZE
	enabled/online sysfs attributes converted to "0/1" values
		using kstrtobool()

v5:
	Greg KH:
		fix licence nit
		consistent use of e-mail address
		replaced raw sysfs attribute with kobject_uevent() call
		keep __ATTRIBUTE_GROUPS because we need is_visible field
	Rob Herring:
		controller devicetree entry now requires multiple address
			regions and interrupts
		controller child node(s) now mapped to child devices by
			slot/host position (using devicetree reg = <>)
		architectural overhaul to align closely to existing pci arch:

				pci			anybus			
--------------------------------------------------------------------------------
Common bus code (spec),		pci/pci_driver.c	anybus/dev_core.c
  calls bus_register()		no of compatible	no of compatible
--------------------------------------------------------------------------------
Device on bus, calls
  XXX_client_driver_register()	hwmon/k8temp.c		fieldbus/hms-profinet.c
--------------------------------------------------------------------------------
Controller, silicon driver	pci/pcie-tango.c	anybus/arcx-anybus.c
				platform_driver calls	platform_driver calls
				pci_host_probe()	anybus_host_common_probe()

v4:
	general
		create fieldbus_dev subsystem, with standardized 'simple'
			userspace interface (sysfs + cdev)
		remove redundant __packed keywords
		use __be16/32 types wherever values are explicitly big-endian
	hms-profinet:
		remove configuration code, and uapi headers: not supported (yet)
			when registering as a fieldbus_dev.
	anybuss-host:
		use struct kref to reference-count tasks
		replace busy loops with usleep_range() loop after 10 tries
		use threaded irq so time_before_eq(jiffies, timeout) will
			continue to work
		allow client devices to be assigned a devicetree node,
			in the same way as pci/mmc/...

v3:
	devicetree-bindings
		adding the vendor prefix is now a separate commit
	anybus-bridge:
		moved misc driver to drivers/bus/
		converted of_gpio_* to gpiod_* abstractions
		can power readout is now a fixed-voltage regulator
	anybuss-host:
		converted refcounts from atomic_t to refcount_t
		fixed potential use-after-free
	hms-profinet:
		applied minor kernel build robot suggestion

v2:
	added architecture overview comments to host driver
	completely reworked anybus-bridge driver, it becomes a reset controller
	anybuss-host driver now needs devicetree entry, link to reset controller
	I will hold off on kernel-doc until the overall architecture gets
		more validation / approval
	fixed Kconfig, comment-style, document ioctl magic numbers
	removed redundant pwm dependency
	renamed enable-gpios to reset-gpios
	stop driving reset-gpio after unloading driver
	use interrupt-parent / interrupts method to describe interrupts
		in the devicetree
	convert references 'i.MX WEIM parallel bus' to 'parallel bus'
	replace devicetree functions with more generic platform_get_resource()
							platform_get_irq()
	added device unique data to add_device_randomness()

v1:
	first shot

Sven Van Asbroeck (7):
  fieldbus_dev: add Fieldbus Device subsystem.
  anybus-s: support HMS Anybus-S bus
  anybus-s: support the Arcx anybus controller
  dt-bindings: anybus-controller: document devicetree binding
  dt-bindings: Add vendor prefix for arcx / Archronix
  fieldbus_dev: support HMS Profinet IRT industrial controller
  staging: fieldbus_dev: add TODO

 Documentation/ABI/testing/fieldbus-dev-cdev   |   31 +
 .../ABI/testing/sysfs-class-fieldbus-dev      |   62 +
 .../fieldbus/arcx,anybus-controller.txt       |   71 +
 .../devicetree/bindings/vendor-prefixes.txt   |    1 +
 Documentation/fieldbus_dev/fieldbus_dev.txt   |   66 +
 drivers/staging/Kconfig                       |    2 +
 drivers/staging/Makefile                      |    1 +
 drivers/staging/fieldbus/Kconfig              |   18 +
 drivers/staging/fieldbus/Makefile             |    7 +
 drivers/staging/fieldbus/TODO                 |    5 +
 drivers/staging/fieldbus/anybuss/Kconfig      |   39 +
 drivers/staging/fieldbus/anybuss/Makefile     |   10 +
 .../staging/fieldbus/anybuss/arcx-anybus.c    |  398 +++++
 .../staging/fieldbus/anybuss/hms-profinet.c   |  224 +++
 drivers/staging/fieldbus/anybuss/host.c       | 1459 +++++++++++++++++
 drivers/staging/fieldbus/dev_core.c           |  349 ++++
 include/linux/anybuss-client.h                |  102 ++
 include/linux/anybuss-controller.h            |   47 +
 include/linux/fieldbus_dev.h                  |  106 ++
 19 files changed, 2998 insertions(+)
 create mode 100644 Documentation/ABI/testing/fieldbus-dev-cdev
 create mode 100644 Documentation/ABI/testing/sysfs-class-fieldbus-dev
 create mode 100644 Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt
 create mode 100644 Documentation/fieldbus_dev/fieldbus_dev.txt
 create mode 100644 drivers/staging/fieldbus/Kconfig
 create mode 100644 drivers/staging/fieldbus/Makefile
 create mode 100644 drivers/staging/fieldbus/TODO
 create mode 100644 drivers/staging/fieldbus/anybuss/Kconfig
 create mode 100644 drivers/staging/fieldbus/anybuss/Makefile
 create mode 100644 drivers/staging/fieldbus/anybuss/arcx-anybus.c
 create mode 100644 drivers/staging/fieldbus/anybuss/hms-profinet.c
 create mode 100644 drivers/staging/fieldbus/anybuss/host.c
 create mode 100644 drivers/staging/fieldbus/dev_core.c
 create mode 100644 include/linux/anybuss-client.h
 create mode 100644 include/linux/anybuss-controller.h
 create mode 100644 include/linux/fieldbus_dev.h

-- 
2.17.1


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

* [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem.
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-16 11:13   ` Greg KH
  2019-04-09 14:42 ` [PATCH v10 2/7] anybus-s: support HMS Anybus-S bus Sven Van Asbroeck
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

Fieldbus device (client) adapters allow data exchange with a PLC aka.
"Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)

They are typically used when a Linux device wants to expose itself
as an actuator, motor, console light, switch, etc. over the fieldbus.

This framework is designed to provide a generic interface to Fieldbus
Devices from both the Linux Kernel and the userspace.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 Documentation/ABI/testing/fieldbus-dev-cdev   |  31 ++
 .../ABI/testing/sysfs-class-fieldbus-dev      |  62 ++++
 Documentation/fieldbus_dev/fieldbus_dev.txt   |  66 ++++
 drivers/staging/Kconfig                       |   2 +
 drivers/staging/Makefile                      |   1 +
 drivers/staging/fieldbus/Kconfig              |  19 +
 drivers/staging/fieldbus/Makefile             |   9 +
 drivers/staging/fieldbus/dev_core.c           | 349 ++++++++++++++++++
 include/linux/fieldbus_dev.h                  | 106 ++++++
 9 files changed, 645 insertions(+)
 create mode 100644 Documentation/ABI/testing/fieldbus-dev-cdev
 create mode 100644 Documentation/ABI/testing/sysfs-class-fieldbus-dev
 create mode 100644 Documentation/fieldbus_dev/fieldbus_dev.txt
 create mode 100644 drivers/staging/fieldbus/Kconfig
 create mode 100644 drivers/staging/fieldbus/Makefile
 create mode 100644 drivers/staging/fieldbus/dev_core.c
 create mode 100644 include/linux/fieldbus_dev.h

diff --git a/Documentation/ABI/testing/fieldbus-dev-cdev b/Documentation/ABI/testing/fieldbus-dev-cdev
new file mode 100644
index 000000000000..45f631ea32a6
--- /dev/null
+++ b/Documentation/ABI/testing/fieldbus-dev-cdev
@@ -0,0 +1,31 @@
+What:		/dev/fieldbus_devX
+Date:		December 2018
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		The cdev interface to drivers for Fieldbus Device Memory
+			(aka. Process Memory).
+
+		The following file operations are supported:
+
+		open(2)
+		Create an I/O context associated with the file descriptor.
+
+		read(2)
+		Read from Process Memory's "read area".
+		Clears POLLERR | POLLPRI from the file descriptor.
+
+		write(2)
+		Write to Process Memory's "write area".
+
+		poll(2), select(2), epoll_wait(2) etc.
+		When a "Process Memory Read Area Changed" event occurs,
+		POLLERR | POLLPRI will be set on the file descriptor.
+		Note that POLLIN | POLLOUT events are always set, because the
+		process memory area is always readable and writable.
+
+		close(2)
+		Free up the I/O context that was associated
+		with the file descriptor.
+
+Users:		TBD
diff --git a/Documentation/ABI/testing/sysfs-class-fieldbus-dev b/Documentation/ABI/testing/sysfs-class-fieldbus-dev
new file mode 100644
index 000000000000..439f14d33c3b
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-fieldbus-dev
@@ -0,0 +1,62 @@
+What:		/sys/class/fieldbus_dev/fieldbus_devX/card_name
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		Human-readable name of the Fieldbus Device.
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/fieldbus_type
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		The type of fieldbus implemented by this device.
+		Possible values:
+			'unknown'
+			'profinet'
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/fieldbus_id
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		The unique fieldbus id associated with this device.
+		The exact format of this id is fieldbus type dependent, e.g.
+		a mac address for profinet.
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/read_area_size
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		The size, in bytes, of the Process Memory read area.
+		Note: this area is accessible by reading from the associated
+			character device (/dev/fieldbus_devX).
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/write_area_size
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		The size, in bytes, of the Process Memory write area.
+		Note: this area is accessible by writing to the associated
+			character device (/dev/fieldbus_devX)
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/online
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		Whether the fieldbus is online or offline.
+		Possible values:
+			'1' meaning 'online'
+			'0' meaning 'offline'
+		Note: an uevent is generated when this property changes.
+
+What:		/sys/class/fieldbus_dev/fieldbus_devX/enabled
+KernelVersion:	5.1 (staging)
+Contact:	Sven Van Asbroeck <TheSven73@gmail.com>
+Description:
+		Whether the device is enabled (power on) or
+			disabled (power off).
+		Possible values:
+			'1' meaning enabled
+			'0' meaning disabled
+		Normally a r/o property, but optionally r/w:
+		Writing '1' enables the device (power on) with default
+			settings.
+		Writing '0' disables the card (power off).
diff --git a/Documentation/fieldbus_dev/fieldbus_dev.txt b/Documentation/fieldbus_dev/fieldbus_dev.txt
new file mode 100644
index 000000000000..56af3f650fa3
--- /dev/null
+++ b/Documentation/fieldbus_dev/fieldbus_dev.txt
@@ -0,0 +1,66 @@
+                       Fieldbus-Device Subsystem
+               ============================================
+
+Part 0 - What is a Fieldbus Device ?
+------------------------------------
+
+Fieldbus is the name of a family of industrial computer network protocols used
+for real-time distributed control, standardized as IEC 61158.
+
+A complex automated industrial system -- such as manufacturing assembly line --
+usually needs a distributed control system -- an organized hierarchy of
+controller systems -- to function. In this hierarchy, there is usually a
+Human Machine Interface (HMI) at the top, where an operator can monitor or
+operate the system. This is typically linked to a middle layer of programmable
+logic controllers (PLC) via a non-time-critical communications system
+(e.g. Ethernet). At the bottom of the control chain is the fieldbus that links
+the PLCs to the components that actually do the work, such as sensors,
+actuators, electric motors, console lights, switches, valves and contactors.
+
+(Source: Wikipedia)
+
+A "Fieldbus Device" is such an actuator, motor, console light, switch, ...
+controlled via the Fieldbus by a PLC aka "Fieldbus Controller".
+
+Communication between PLC and device typically happens via process data memory,
+separated into input and output areas. The Fieldbus then cyclically transfers
+the PLC's output area to the device's input area, and vice versa.
+
+Part I - Why do we need this subsystem?
+---------------------------------------
+
+Fieldbus device (client) adapters are commercially available. They allow data
+exchange with a PLC aka "Fieldbus Controller" via process data memory.
+
+They are typically used when a Linux device wants to expose itself as an
+actuator, motor, console light, switch, etc. over the fieldbus.
+
+The purpose of this subsystem is:
+a) present a general, standardized, extensible API/ABI to userspace; and
+b) present a convenient interface to drivers.
+
+Part II - How can drivers use the subsystem?
+--------------------------------------------
+
+Any driver that wants to register as a Fieldbus Device should allocate and
+populate a 'struct fieldbus_dev' (from include/linux/fieldbus_dev.h).
+Registration then happens by calling fieldbus_dev_register().
+
+Part III - How can userspace use the subsystem?
+-----------------------------------------------
+
+Fieldbus protocols and adapters are diverse and varied. However, they share
+a limited few common behaviours and properties. This allows us to define
+a simple interface consisting of a character device and a set of sysfs files:
+
+See:
+Documentation/ABI/testing/sysfs-class-fieldbus-dev
+Documentation/ABI/testing/fieldbus-dev-cdev
+
+Note that this simple interface does not provide a way to modify adapter
+configuration settings. It is therefore useful only for adapters that get their
+configuration settings some other way, e.g. non-volatile memory on the adapter,
+through the network, ...
+
+At a later phase, this simple interface can easily co-exist with a future
+(netlink-based ?) configuration settings interface.
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 62951e836cbc..da58314732e1 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -122,4 +122,6 @@ source "drivers/staging/axis-fifo/Kconfig"
 
 source "drivers/staging/erofs/Kconfig"
 
+source "drivers/staging/fieldbus/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index d1b17ddcd354..ded12bdeb1a6 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -51,3 +51,4 @@ obj-$(CONFIG_SOC_MT7621)	+= mt7621-dts/
 obj-$(CONFIG_STAGING_GASKET_FRAMEWORK)	+= gasket/
 obj-$(CONFIG_XIL_AXIS_FIFO)	+= axis-fifo/
 obj-$(CONFIG_EROFS_FS)		+= erofs/
+obj-$(CONFIG_FIELDBUS_DEV)     += fieldbus/
diff --git a/drivers/staging/fieldbus/Kconfig b/drivers/staging/fieldbus/Kconfig
new file mode 100644
index 000000000000..b1d071cb096e
--- /dev/null
+++ b/drivers/staging/fieldbus/Kconfig
@@ -0,0 +1,19 @@
+menuconfig FIELDBUS_DEV
+	tristate "Fieldbus Device Support"
+	help
+	  Support for Fieldbus Device Adapters.
+
+	  Fieldbus device (client) adapters allow data exchange with a PLC aka.
+	  "Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
+
+	  They are typically used when a Linux device wants to expose itself
+	  as an actuator, motor, console light, switch, etc. over the fieldbus.
+
+	  This framework is designed to provide a generic interface to Fieldbus
+	  Devices from both the Linux Kernel and the userspace.
+
+	  If unsure, say no.
+
+if FIELDBUS_DEV
+
+endif
diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile
new file mode 100644
index 000000000000..23877def5803
--- /dev/null
+++ b/drivers/staging/fieldbus/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for fieldbus_dev drivers.
+#
+
+obj-$(CONFIG_FIELDBUS_DEV)	+= fieldbus_dev.o
+fieldbus_dev-y			:= dev_core.o
+
+# Devices
diff --git a/drivers/staging/fieldbus/dev_core.c b/drivers/staging/fieldbus/dev_core.c
new file mode 100644
index 000000000000..3f2368266848
--- /dev/null
+++ b/drivers/staging/fieldbus/dev_core.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Fieldbus Device Driver Core
+ *
+ */
+
+#include <linux/fieldbus_dev.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/idr.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+
+/* Maximum number of fieldbus devices */
+#define MAX_FIELDBUSES		32
+
+/* the dev_t structure to store the dynamically allocated fieldbus devices */
+static dev_t fieldbus_devt;
+static DEFINE_IDA(fieldbus_ida);
+static DEFINE_MUTEX(fieldbus_mtx);
+
+static const char ctrl_enabled[] = "enabled";
+static const char ctrl_disabled[] = "disabled";
+
+static ssize_t online_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", !!fb->online);
+}
+static DEVICE_ATTR_RO(online);
+
+static ssize_t enabled_show(struct device *dev, struct device_attribute *attr,
+			    char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	if (!fb->enable_get)
+		return -EINVAL;
+	return sprintf(buf, "%d\n", !!fb->enable_get(fb));
+}
+
+static ssize_t enabled_store(struct device *dev, struct device_attribute *attr,
+			     const char *buf, size_t n)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+	bool value;
+	int ret;
+
+	if (!fb->simple_enable_set)
+		return -ENOTSUPP;
+	ret = kstrtobool(buf, &value);
+	if (ret)
+		return ret;
+	ret = fb->simple_enable_set(fb, value);
+	if (ret < 0)
+		return ret;
+	return n;
+}
+static DEVICE_ATTR_RW(enabled);
+
+static ssize_t card_name_show(struct device *dev, struct device_attribute *attr,
+			      char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	/*
+	 * card_name was provided by child driver, could potentially be long.
+	 * protect against buffer overrun.
+	 */
+	return snprintf(buf, PAGE_SIZE, "%s\n", fb->card_name);
+}
+static DEVICE_ATTR_RO(card_name);
+
+static ssize_t read_area_size_show(struct device *dev,
+				   struct device_attribute *attr, char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%zu\n", fb->read_area_sz);
+}
+static DEVICE_ATTR_RO(read_area_size);
+
+static ssize_t write_area_size_show(struct device *dev,
+				    struct device_attribute *attr, char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%zu\n", fb->write_area_sz);
+}
+static DEVICE_ATTR_RO(write_area_size);
+
+static ssize_t fieldbus_id_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+
+	return fb->fieldbus_id_get(fb, buf, PAGE_SIZE);
+}
+static DEVICE_ATTR_RO(fieldbus_id);
+
+static ssize_t fieldbus_type_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+	const char *t;
+
+	switch (fb->fieldbus_type) {
+	case FIELDBUS_DEV_TYPE_PROFINET:
+		t = "profinet";
+		break;
+	default:
+		t = "unknown";
+		break;
+	}
+
+	return sprintf(buf, "%s\n", t);
+}
+static DEVICE_ATTR_RO(fieldbus_type);
+
+static struct attribute *fieldbus_attrs[] = {
+	&dev_attr_enabled.attr,
+	&dev_attr_card_name.attr,
+	&dev_attr_fieldbus_id.attr,
+	&dev_attr_read_area_size.attr,
+	&dev_attr_write_area_size.attr,
+	&dev_attr_online.attr,
+	&dev_attr_fieldbus_type.attr,
+	NULL,
+};
+
+static umode_t fieldbus_is_visible(struct kobject *kobj, struct attribute *attr,
+				   int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct fieldbus_dev *fb = dev_get_drvdata(dev);
+	umode_t mode = attr->mode;
+
+	if (attr == &dev_attr_enabled.attr) {
+		mode = 0;
+		if (fb->enable_get)
+			mode |= 0444;
+		if (fb->simple_enable_set)
+			mode |= 0200;
+	}
+
+	return mode;
+}
+
+static const struct attribute_group fieldbus_group = {
+	.attrs = fieldbus_attrs,
+	.is_visible = fieldbus_is_visible,
+};
+__ATTRIBUTE_GROUPS(fieldbus);
+
+static struct class fieldbus_class = {
+	.name =		"fieldbus_dev",
+	.owner =	THIS_MODULE,
+	.dev_groups =	fieldbus_groups,
+};
+
+struct fb_open_file {
+	struct fieldbus_dev *fbdev;
+	int dc_event;
+};
+
+static int fieldbus_open(struct inode *inode, struct file *filp)
+{
+	struct fb_open_file *of;
+	struct fieldbus_dev *fbdev = container_of(inode->i_cdev,
+						struct fieldbus_dev,
+						cdev);
+
+	of = kzalloc(sizeof(*of), GFP_KERNEL);
+	if (!of)
+		return -ENOMEM;
+	of->fbdev = fbdev;
+	filp->private_data = of;
+	return 0;
+}
+
+static int fieldbus_release(struct inode *node, struct file *filp)
+{
+	struct fb_open_file *of = filp->private_data;
+
+	kfree(of);
+	return 0;
+}
+
+static ssize_t fieldbus_read(struct file *filp, char __user *buf, size_t size,
+			     loff_t *offset)
+{
+	struct fb_open_file *of = filp->private_data;
+	struct fieldbus_dev *fbdev = of->fbdev;
+
+	of->dc_event = fbdev->dc_event;
+	return fbdev->read_area(fbdev, buf, size, offset);
+}
+
+static ssize_t fieldbus_write(struct file *filp, const char __user *buf,
+			      size_t size, loff_t *offset)
+{
+	struct fb_open_file *of = filp->private_data;
+	struct fieldbus_dev *fbdev = of->fbdev;
+
+	return fbdev->write_area(fbdev, buf, size, offset);
+}
+
+static unsigned int fieldbus_poll(struct file *filp, poll_table *wait)
+{
+	struct fb_open_file *of = filp->private_data;
+	struct fieldbus_dev *fbdev = of->fbdev;
+	unsigned int mask = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM;
+
+	poll_wait(filp, &fbdev->dc_wq, wait);
+	/* data changed ? */
+	if (fbdev->dc_event != of->dc_event)
+		mask |= POLLPRI | POLLERR;
+	return mask;
+}
+
+static const struct file_operations fieldbus_fops = {
+	.open		= fieldbus_open,
+	.release	= fieldbus_release,
+	.read		= fieldbus_read,
+	.write		= fieldbus_write,
+	.poll		= fieldbus_poll,
+	.llseek		= generic_file_llseek,
+	.owner		= THIS_MODULE,
+};
+
+void fieldbus_dev_area_updated(struct fieldbus_dev *fb)
+{
+	fb->dc_event++;
+	wake_up_all(&fb->dc_wq);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_area_updated);
+
+void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online)
+{
+	fb->online = online;
+	kobject_uevent(&fb->dev->kobj, KOBJ_CHANGE);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_online_changed);
+
+static void __fieldbus_dev_unregister(struct fieldbus_dev *fb)
+{
+	if (!fb)
+		return;
+	device_destroy(&fieldbus_class, fb->cdev.dev);
+	cdev_del(&fb->cdev);
+	ida_simple_remove(&fieldbus_ida, fb->id);
+}
+
+void fieldbus_dev_unregister(struct fieldbus_dev *fb)
+{
+	mutex_lock(&fieldbus_mtx);
+	__fieldbus_dev_unregister(fb);
+	mutex_unlock(&fieldbus_mtx);
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_unregister);
+
+static int __fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+	dev_t devno;
+	int err;
+
+	if (!fb)
+		return -EINVAL;
+	if (!fb->read_area || !fb->write_area || !fb->fieldbus_id_get)
+		return -EINVAL;
+	fb->id = ida_simple_get(&fieldbus_ida, 0, MAX_FIELDBUSES, GFP_KERNEL);
+	if (fb->id < 0)
+		return fb->id;
+	devno = MKDEV(MAJOR(fieldbus_devt), fb->id);
+	init_waitqueue_head(&fb->dc_wq);
+	cdev_init(&fb->cdev, &fieldbus_fops);
+	err = cdev_add(&fb->cdev, devno, 1);
+	if (err) {
+		pr_err("fieldbus_dev%d unable to add device %d:%d\n",
+		       fb->id, MAJOR(fieldbus_devt), fb->id);
+		goto err_cdev;
+	}
+	fb->dev = device_create(&fieldbus_class, fb->parent, devno, fb,
+				"fieldbus_dev%d", fb->id);
+	if (IS_ERR(fb->dev)) {
+		err = PTR_ERR(fb->dev);
+		goto err_dev_create;
+	}
+	return 0;
+
+err_dev_create:
+	cdev_del(&fb->cdev);
+err_cdev:
+	ida_simple_remove(&fieldbus_ida, fb->id);
+	return err;
+}
+
+int fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+	int err;
+
+	mutex_lock(&fieldbus_mtx);
+	err = __fieldbus_dev_register(fb);
+	mutex_unlock(&fieldbus_mtx);
+
+	return err;
+}
+EXPORT_SYMBOL_GPL(fieldbus_dev_register);
+
+static int __init fieldbus_init(void)
+{
+	int err;
+
+	err = class_register(&fieldbus_class);
+	if (err < 0) {
+		pr_err("fieldbus_dev: could not register class\n");
+		return err;
+	}
+	err = alloc_chrdev_region(&fieldbus_devt, 0,
+				  MAX_FIELDBUSES, "fieldbus_dev");
+	if (err < 0) {
+		pr_err("fieldbus_dev: unable to allocate char dev region\n");
+		goto err_alloc;
+	}
+	return 0;
+
+err_alloc:
+	class_unregister(&fieldbus_class);
+	return err;
+}
+
+static void __exit fieldbus_exit(void)
+{
+	unregister_chrdev_region(fieldbus_devt, MAX_FIELDBUSES);
+	class_unregister(&fieldbus_class);
+	ida_destroy(&fieldbus_ida);
+}
+
+subsys_initcall(fieldbus_init);
+module_exit(fieldbus_exit);
+
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_AUTHOR("Jonathan Stiles <jonathans@arcx.com>");
+MODULE_DESCRIPTION("Fieldbus Device Driver Core");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/fieldbus_dev.h b/include/linux/fieldbus_dev.h
new file mode 100644
index 000000000000..415dcab35e2b
--- /dev/null
+++ b/include/linux/fieldbus_dev.h
@@ -0,0 +1,106 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Fieldbus Device Driver Core
+ *
+ */
+
+#ifndef __FIELDBUS_DEV_H
+#define __FIELDBUS_DEV_H
+
+#include <linux/cdev.h>
+#include <linux/wait.h>
+
+enum fieldbus_dev_type {
+	FIELDBUS_DEV_TYPE_UNKNOWN = 0,
+	FIELDBUS_DEV_TYPE_PROFINET,
+};
+
+/**
+ * struct fieldbus_dev - Fieldbus device
+ * @read_area:		[DRIVER] function to read the process data area of the
+ *				 device. same parameters/return values as
+ *				 the read function in struct file_operations
+ * @write_area:		[DRIVER] function to write to the process data area of
+ *				 the device. same parameters/return values as
+ *				 the write function in struct file_operations
+ * @write_area_sz	[DRIVER] size of the writable process data area
+ * @read_area_sz	[DRIVER] size of the readable process data area
+ * @card_name		[DRIVER] name of the card, e.g. "ACME Inc. profinet"
+ * @fieldbus_type	[DRIVER] fieldbus type of this device, e.g.
+ *					FIELDBUS_DEV_TYPE_PROFINET
+ * @enable_get		[DRIVER] function which returns true if the card
+ *				 is enabled, false otherwise
+ * @fieldbus_id_get	[DRIVER] function to retrieve the unique fieldbus id
+ *				 by which this device can be identified;
+ *				 return value follows the snprintf convention
+ * @simple_enable_set	[DRIVER] (optional) function to enable the device
+ *				 according to its default settings
+ * @parent		[DRIVER] (optional) the device's parent device
+ */
+struct fieldbus_dev {
+	ssize_t (*read_area)(struct fieldbus_dev *fbdev, char __user *buf,
+			size_t size, loff_t *offset);
+	ssize_t (*write_area)(struct fieldbus_dev *fbdev,
+			const char __user *buf, size_t size, loff_t *offset);
+	size_t write_area_sz, read_area_sz;
+	const char *card_name;
+	enum fieldbus_dev_type fieldbus_type;
+	bool (*enable_get)(struct fieldbus_dev *fbdev);
+	int (*fieldbus_id_get)(struct fieldbus_dev *fbdev, char *buf,
+					size_t max_size);
+	int (*simple_enable_set)(struct fieldbus_dev *fbdev, bool enable);
+	struct device *parent;
+
+	/* private data */
+	int id;
+	struct cdev cdev;
+	struct device *dev;
+	int dc_event;
+	wait_queue_head_t dc_wq;
+	bool online;
+};
+
+#if IS_ENABLED(CONFIG_FIELDBUS_DEV)
+
+/**
+ * fieldbus_dev_unregister()
+ *	- unregister a previously registered fieldbus device
+ * @fb:		Device structure previously registered
+ **/
+void fieldbus_dev_unregister(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_register()
+ *	- register a device with the fieldbus device subsystem
+ * @fb:		Device structure filled by the device driver
+ **/
+int __must_check fieldbus_dev_register(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_area_updated()
+ *	- notify the subsystem that an external fieldbus controller updated
+ *			the process data area
+ * @fb:		Device structure
+ **/
+void fieldbus_dev_area_updated(struct fieldbus_dev *fb);
+
+/**
+ * fieldbus_dev_online_changed()
+ *	- notify the subsystem that the fieldbus online status changed
+ * @fb:		Device structure
+ **/
+void fieldbus_dev_online_changed(struct fieldbus_dev *fb, bool online);
+
+#else /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
+
+static inline void fieldbus_dev_unregister(struct fieldbus_dev *fb) {}
+static inline int __must_check fieldbus_dev_register(struct fieldbus_dev *fb)
+{
+	return -ENOTSUPP;
+}
+static inline void fieldbus_dev_area_updated(struct fieldbus_dev *fb) {}
+static inline void fieldbus_dev_online_changed(struct fieldbus_dev *fb,
+					       bool online) {}
+
+#endif /* IS_ENABLED(CONFIG_FIELDBUS_DEV) */
+#endif /* __FIELDBUS_DEV_H */
-- 
2.17.1


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

* [PATCH v10 2/7] anybus-s: support HMS Anybus-S bus
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 3/7] anybus-s: support the Arcx anybus controller Sven Van Asbroeck
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

The Anybus-S/Anybus-M is a series of interchangeable fieldbus communication
modules featuring on board memory and processing power. All software and
hardware functionality required to communicate on the fieldbus is
incorporated in the module itself, allowing the application to focus on
other tasks.

Typical applications are frequency inverters, HMI and visualization
devices, instruments, scales, robotics, PLC’s and intelligent measuring
devices.

Official documentation:
https://www.anybus.com/docs/librariesprovider7/default-document-library/
manuals-design-guides/hms-hmsi-27-275.pdf

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 drivers/staging/fieldbus/Kconfig          |    3 +-
 drivers/staging/fieldbus/Makefile         |    3 +-
 drivers/staging/fieldbus/anybuss/Kconfig  |    9 +
 drivers/staging/fieldbus/anybuss/Makefile |    7 +
 drivers/staging/fieldbus/anybuss/host.c   | 1459 +++++++++++++++++++++
 include/linux/anybuss-client.h            |  102 ++
 include/linux/anybuss-controller.h        |   47 +
 7 files changed, 1626 insertions(+), 4 deletions(-)
 create mode 100644 drivers/staging/fieldbus/anybuss/Kconfig
 create mode 100644 drivers/staging/fieldbus/anybuss/Makefile
 create mode 100644 drivers/staging/fieldbus/anybuss/host.c
 create mode 100644 include/linux/anybuss-client.h
 create mode 100644 include/linux/anybuss-controller.h

diff --git a/drivers/staging/fieldbus/Kconfig b/drivers/staging/fieldbus/Kconfig
index b1d071cb096e..e5e28e52c59b 100644
--- a/drivers/staging/fieldbus/Kconfig
+++ b/drivers/staging/fieldbus/Kconfig
@@ -14,6 +14,5 @@ menuconfig FIELDBUS_DEV
 
 	  If unsure, say no.
 
-if FIELDBUS_DEV
+source "drivers/staging/fieldbus/anybuss/Kconfig"
 
-endif
diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile
index 23877def5803..c21056bdb3d2 100644
--- a/drivers/staging/fieldbus/Makefile
+++ b/drivers/staging/fieldbus/Makefile
@@ -3,7 +3,6 @@
 # Makefile for fieldbus_dev drivers.
 #
 
-obj-$(CONFIG_FIELDBUS_DEV)	+= fieldbus_dev.o
+obj-$(CONFIG_FIELDBUS_DEV)	+= fieldbus_dev.o anybuss/
 fieldbus_dev-y			:= dev_core.o
 
-# Devices
diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig
new file mode 100644
index 000000000000..5b495f25c11e
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/Kconfig
@@ -0,0 +1,9 @@
+config HMS_ANYBUSS_BUS
+	tristate "HMS Anybus-S Bus Support"
+	select REGMAP
+	depends on OF && FIELDBUS_DEV
+	help
+	  Driver for the HMS Industrial Networks Anybus-S bus.
+	  You can attach a single Anybus-S compatible card to it, which
+	  typically provides fieldbus and industrial ethernet
+	  functionality.
diff --git a/drivers/staging/fieldbus/anybuss/Makefile b/drivers/staging/fieldbus/anybuss/Makefile
new file mode 100644
index 000000000000..236579aa7270
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for anybuss drivers.
+#
+
+obj-$(CONFIG_HMS_ANYBUSS_BUS)	+= anybuss_core.o
+anybuss_core-y			+= host.o
diff --git a/drivers/staging/fieldbus/anybuss/host.c b/drivers/staging/fieldbus/anybuss/host.c
new file mode 100644
index 000000000000..e642e8365c34
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/host.c
@@ -0,0 +1,1459 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMS Anybus-S Host Driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+/*
+ * Architecture Overview
+ * =====================
+ * This driver (running on the CPU/SoC) and the Anybus-S card communicate
+ * by reading and writing data to/from the Anybus-S Dual-Port RAM (dpram).
+ * This is memory connected to both the SoC and Anybus-S card, which both sides
+ * can access freely and concurrently.
+ *
+ * Synchronization happens by means of two registers located in the dpram:
+ * IND_AB: written exclusively by the Anybus card; and
+ * IND_AP: written exclusively by this driver.
+ *
+ * Communication happens using one of the following mechanisms:
+ * 1. reserve, read/write, release dpram memory areas:
+ *	using an IND_AB/IND_AP protocol, the driver is able to reserve certain
+ *	memory areas. no dpram memory can be read or written except if reserved.
+ *	(with a few limited exceptions)
+ * 2. send and receive data structures via a shared mailbox:
+ *	using an IND_AB/IND_AP protocol, the driver and Anybus card are able to
+ *	exchange commands and responses using a shared mailbox.
+ * 3. receive software interrupts:
+ *	using an IND_AB/IND_AP protocol, the Anybus card is able to notify the
+ *	driver of certain events such as: bus online/offline, data available.
+ *	note that software interrupt event bits are located in a memory area
+ *	which must be reserved before it can be accessed.
+ *
+ * The manual[1] is silent on whether these mechanisms can happen concurrently,
+ * or how they should be synchronized. However, section 13 (Driver Example)
+ * provides the following suggestion for developing a driver:
+ * a) an interrupt handler which updates global variables;
+ * b) a continuously-running task handling area requests (1 above)
+ * c) a continuously-running task handling mailbox requests (2 above)
+ * The example conspicuously leaves out software interrupts (3 above), which
+ * is the thorniest issue to get right (see below).
+ *
+ * The naive, straightforward way to implement this would be:
+ * - create an isr which updates shared variables;
+ * - create a work_struct which handles software interrupts on a queue;
+ * - create a function which does reserve/update/unlock in a loop;
+ * - create a function which does mailbox send/receive in a loop;
+ * - call the above functions from the driver's read/write/ioctl;
+ * - synchronize using mutexes/spinlocks:
+ *	+ only one area request at a time
+ *	+ only one mailbox request at a time
+ *	+ protect AB_IND, AB_IND against data hazards (e.g. read-after-write)
+ *
+ * Unfortunately, the presence of the software interrupt causes subtle yet
+ * considerable synchronization issues; especially problematic is the
+ * requirement to reserve/release the area which contains the status bits.
+ *
+ * The driver architecture presented here sidesteps these synchronization issues
+ * by accessing the dpram from a single kernel thread only. User-space throws
+ * "tasks" (i.e. 1, 2 above) into a task queue, waits for their completion,
+ * and the kernel thread runs them to completion.
+ *
+ * Each task has a task_function, which is called/run by the queue thread.
+ * That function communicates with the Anybus card, and returns either
+ * 0 (OK), a negative error code (error), or -EINPROGRESS (waiting).
+ * On OK or error, the queue thread completes and dequeues the task,
+ * which also releases the user space thread which may still be waiting for it.
+ * On -EINPROGRESS (waiting), the queue thread will leave the task on the queue,
+ * and revisit (call again) whenever an interrupt event comes in.
+ *
+ * Each task has a state machine, which is run by calling its task_function.
+ * It ensures that the task will go through its various stages over time,
+ * returning -EINPROGRESS if it wants to wait for an event to happen.
+ *
+ * Note that according to the manual's driver example, the following operations
+ * may run independent of each other:
+ * - area reserve/read/write/release	(point 1 above)
+ * - mailbox operations			(point 2 above)
+ * - switching power on/off
+ *
+ * To allow them to run independently, each operation class gets its own queue.
+ *
+ * Userspace processes A, B, C, D post tasks to the appropriate queue,
+ * and wait for task completion:
+ *
+ *	process A	B	C	D
+ *		|	|	|	|
+ *		v	v	v	v
+ *	|<-----	========================================
+ *	|		|	   |		|
+ *	|		v	   v		v-------<-------+
+ *	|	+--------------------------------------+	|
+ *	|	| power q     | mbox q    | area q     |	|
+ *	|	|------------|------------|------------|	|
+ *	|	| task       | task       | task       |	|
+ *	|	| task       | task       | task       |	|
+ *	|	| task wait  | task wait  | task wait  |	|
+ *	|	+--------------------------------------+	|
+ *	|		^	   ^		^		|
+ *	|		|	   |		|		^
+ *	|	+--------------------------------------+	|
+ *	|	|	     queue thread	       |	|
+ *	|	|--------------------------------------|	|
+ *	|	| single-threaded:		       |	|
+ *	|	| loop:				       |	|
+ *	v	|   for each queue:		       |	|
+ *	|	|     run task state machine	       |	|
+ *	|	|     if task waiting:		       |	|
+ *	|	|       leave on queue		       |	|
+ *	|	|     if task done:		       |	|
+ *	|	|       complete task, remove from q   |	|
+ *	|	|   if software irq event bits set:    |	|
+ *	|	|     notify userspace		       |	|
+ *	|	|     post clear event bits task------>|>-------+
+ *	|	|   wait for IND_AB changed event OR   |
+ *	|	|            task added event	  OR   |
+ *	|	|	     timeout		       |
+ *	|	| end loop			       |
+ *	|	+--------------------------------------+
+ *	|	+		wake up		       +
+ *	|	+--------------------------------------+
+ *	|		^			^
+ *	|		|			|
+ *	+-------->-------			|
+ *						|
+ *		+--------------------------------------+
+ *		|	interrupt service routine      |
+ *		|--------------------------------------|
+ *		| wake up queue thread on IND_AB change|
+ *		+--------------------------------------+
+ *
+ * Note that the Anybus interrupt is dual-purpose:
+ * - after a reset, triggered when the card becomes ready;
+ * - during normal operation, triggered when AB_IND changes.
+ * This is why the interrupt service routine doesn't just wake up the
+ * queue thread, but also completes the card_boot completion.
+ *
+ * [1] https://www.anybus.com/docs/librariesprovider7/default-document-library/
+ *	manuals-design-guides/hms-hmsi-27-275.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/atomic.h>
+#include <linux/kthread.h>
+#include <linux/kfifo.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/random.h>
+#include <linux/kref.h>
+#include <linux/of_address.h>
+
+#include <linux/anybuss-client.h>
+#include <linux/anybuss-controller.h>
+
+#define DPRAM_SIZE		0x800
+#define MAX_MBOX_MSG_SZ		0x0FF
+#define TIMEOUT			(HZ * 2)
+#define MAX_DATA_AREA_SZ	0x200
+#define MAX_FBCTRL_AREA_SZ	0x1BE
+
+#define REG_BOOTLOADER_V	0x7C0
+#define REG_API_V		0x7C2
+#define REG_FIELDBUS_V		0x7C4
+#define REG_SERIAL_NO		0x7C6
+#define REG_FIELDBUS_TYPE	0x7CC
+#define REG_MODULE_SW_V		0x7CE
+#define REG_IND_AB		0x7FF
+#define REG_IND_AP		0x7FE
+#define REG_EVENT_CAUSE		0x7ED
+#define MBOX_IN_AREA		0x400
+#define MBOX_OUT_AREA		0x520
+#define DATA_IN_AREA		0x000
+#define DATA_OUT_AREA		0x200
+#define FBCTRL_AREA		0x640
+
+#define EVENT_CAUSE_DC          0x01
+#define EVENT_CAUSE_FBOF        0x02
+#define EVENT_CAUSE_FBON        0x04
+
+#define IND_AB_UPDATED		0x08
+#define IND_AX_MIN		0x80
+#define IND_AX_MOUT		0x40
+#define IND_AX_IN		0x04
+#define IND_AX_OUT		0x02
+#define IND_AX_FBCTRL		0x01
+#define IND_AP_LOCK		0x08
+#define IND_AP_ACTION		0x10
+#define IND_AX_EVNT		0x20
+#define IND_AP_ABITS		(IND_AX_IN | IND_AX_OUT | \
+					IND_AX_FBCTRL | \
+					IND_AP_ACTION | IND_AP_LOCK)
+
+#define INFO_TYPE_FB		0x0002
+#define INFO_TYPE_APP		0x0001
+#define INFO_COMMAND		0x4000
+
+#define OP_MODE_FBFC		0x0002
+#define OP_MODE_FBS		0x0004
+#define OP_MODE_CD		0x0200
+
+#define CMD_START_INIT		0x0001
+#define CMD_ANYBUS_INIT		0x0002
+#define CMD_END_INIT		0x0003
+
+/*
+ * ---------------------------------------------------------------
+ * Anybus mailbox messages - definitions
+ * ---------------------------------------------------------------
+ * note that we're depending on the layout of these structures being
+ * exactly as advertised.
+ */
+
+struct anybus_mbox_hdr {
+	__be16 id;
+	__be16 info;
+	__be16 cmd_num;
+	__be16 data_size;
+	__be16 frame_count;
+	__be16 frame_num;
+	__be16 offset_high;
+	__be16 offset_low;
+	__be16 extended[8];
+};
+
+struct msg_anybus_init {
+	__be16 input_io_len;
+	__be16 input_dpram_len;
+	__be16 input_total_len;
+	__be16 output_io_len;
+	__be16 output_dpram_len;
+	__be16 output_total_len;
+	__be16 op_mode;
+	__be16 notif_config;
+	__be16 wd_val;
+};
+
+/* ------------- ref counted tasks ------------- */
+
+struct ab_task;
+typedef int (*ab_task_fn_t)(struct anybuss_host *cd,
+					struct ab_task *t);
+typedef void (*ab_done_fn_t)(struct anybuss_host *cd);
+
+struct area_priv {
+	bool is_write;
+	u16 flags;
+	u16 addr;
+	size_t count;
+	u8 buf[MAX_DATA_AREA_SZ];
+};
+
+struct mbox_priv {
+	struct anybus_mbox_hdr hdr;
+	size_t msg_out_sz;
+	size_t msg_in_sz;
+	u8 msg[MAX_MBOX_MSG_SZ];
+};
+
+struct ab_task {
+	struct kmem_cache	*cache;
+	struct kref		refcount;
+	ab_task_fn_t		task_fn;
+	ab_done_fn_t		done_fn;
+	int			result;
+	struct completion	done;
+	unsigned long		start_jiffies;
+	union {
+		struct area_priv area_pd;
+		struct mbox_priv mbox_pd;
+	};
+};
+
+static struct ab_task *ab_task_create_get(struct kmem_cache *cache,
+					  ab_task_fn_t task_fn)
+{
+	struct ab_task *t;
+
+	t = kmem_cache_alloc(cache, GFP_KERNEL);
+	if (!t)
+		return NULL;
+	t->cache = cache;
+	kref_init(&t->refcount);
+	t->task_fn = task_fn;
+	t->done_fn = NULL;
+	t->result = 0;
+	init_completion(&t->done);
+	return t;
+}
+
+static void __ab_task_destroy(struct kref *refcount)
+{
+	struct ab_task *t = container_of(refcount, struct ab_task, refcount);
+	struct kmem_cache *cache = t->cache;
+
+	kmem_cache_free(cache, t);
+}
+
+static void ab_task_put(struct ab_task *t)
+{
+	kref_put(&t->refcount, __ab_task_destroy);
+}
+
+static struct ab_task *__ab_task_get(struct ab_task *t)
+{
+	kref_get(&t->refcount);
+	return t;
+}
+
+static void __ab_task_finish(struct ab_task *t, struct anybuss_host *cd)
+{
+	if (t->done_fn)
+		t->done_fn(cd);
+	complete(&t->done);
+}
+
+static void
+ab_task_dequeue_finish_put(struct kfifo *q, struct anybuss_host *cd)
+{
+	int ret;
+	struct ab_task *t;
+
+	ret = kfifo_out(q, &t, sizeof(t));
+	WARN_ON(!ret);
+	__ab_task_finish(t, cd);
+	ab_task_put(t);
+}
+
+static int
+ab_task_enqueue(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
+		wait_queue_head_t *wq)
+{
+	int ret;
+
+	t->start_jiffies = jiffies;
+	__ab_task_get(t);
+	ret = kfifo_in_spinlocked(q, &t, sizeof(t), slock);
+	if (!ret) {
+		ab_task_put(t);
+		return -ENOMEM;
+	}
+	wake_up(wq);
+	return 0;
+}
+
+static int
+ab_task_enqueue_wait(struct ab_task *t, struct kfifo *q, spinlock_t *slock,
+		     wait_queue_head_t *wq)
+{
+	int ret;
+
+	ret = ab_task_enqueue(t, q, slock, wq);
+	if (ret)
+		return ret;
+	ret = wait_for_completion_interruptible(&t->done);
+	if (ret)
+		return ret;
+	return t->result;
+}
+
+/* ------------------------ anybus hardware ------------------------ */
+
+struct anybuss_host {
+	struct device *dev;
+	struct anybuss_client *client;
+	void (*reset)(struct device *dev, bool assert);
+	struct regmap *regmap;
+	int irq;
+	int host_idx;
+	struct task_struct *qthread;
+	wait_queue_head_t wq;
+	struct completion card_boot;
+	atomic_t ind_ab;
+	spinlock_t qlock; /* protects IN side of powerq, mboxq, areaq */
+	struct kmem_cache *qcache;
+	struct kfifo qs[3];
+	struct kfifo *powerq;
+	struct kfifo *mboxq;
+	struct kfifo *areaq;
+	bool power_on;
+	bool softint_pending;
+};
+
+static void reset_assert(struct anybuss_host *cd)
+{
+	cd->reset(cd->dev, true);
+}
+
+static void reset_deassert(struct anybuss_host *cd)
+{
+	cd->reset(cd->dev, false);
+}
+
+static int test_dpram(struct regmap *regmap)
+{
+	int i;
+	unsigned int val;
+
+	for (i = 0; i < DPRAM_SIZE; i++)
+		regmap_write(regmap, i, (u8)i);
+	for (i = 0; i < DPRAM_SIZE; i++) {
+		regmap_read(regmap, i, &val);
+		if ((u8)val != (u8)i)
+			return -EIO;
+	}
+	return 0;
+}
+
+static int read_ind_ab(struct regmap *regmap)
+{
+	unsigned long timeout = jiffies + HZ / 2;
+	unsigned int a, b, i = 0;
+
+	while (time_before_eq(jiffies, timeout)) {
+		regmap_read(regmap, REG_IND_AB, &a);
+		regmap_read(regmap, REG_IND_AB, &b);
+		if (likely(a == b))
+			return (int)a;
+		if (i < 10) {
+			cpu_relax();
+			i++;
+		} else {
+			usleep_range(500, 1000);
+		}
+	}
+	WARN(1, "IND_AB register not stable");
+	return -ETIMEDOUT;
+}
+
+static int write_ind_ap(struct regmap *regmap, unsigned int ind_ap)
+{
+	unsigned long timeout = jiffies + HZ / 2;
+	unsigned int v, i = 0;
+
+	while (time_before_eq(jiffies, timeout)) {
+		regmap_write(regmap, REG_IND_AP, ind_ap);
+		regmap_read(regmap, REG_IND_AP, &v);
+		if (likely(ind_ap == v))
+			return 0;
+		if (i < 10) {
+			cpu_relax();
+			i++;
+		} else {
+			usleep_range(500, 1000);
+		}
+	}
+	WARN(1, "IND_AP register not stable");
+	return -ETIMEDOUT;
+}
+
+static irqreturn_t irq_handler(int irq, void *data)
+{
+	struct anybuss_host *cd = data;
+	int ind_ab;
+
+	/*
+	 * irq handler needs exclusive access to the IND_AB register,
+	 * because the act of reading the register acks the interrupt.
+	 *
+	 * store the register value in cd->ind_ab (an atomic_t), so that the
+	 * queue thread is able to read it without causing an interrupt ack
+	 * side-effect (and without spuriously acking an interrupt).
+	 */
+	ind_ab = read_ind_ab(cd->regmap);
+	if (ind_ab < 0)
+		return IRQ_NONE;
+	atomic_set(&cd->ind_ab, ind_ab);
+	complete(&cd->card_boot);
+	wake_up(&cd->wq);
+	return IRQ_HANDLED;
+}
+
+/* ------------------------ power on/off tasks --------------------- */
+
+static int task_fn_power_off(struct anybuss_host *cd,
+			     struct ab_task *t)
+{
+	struct anybuss_client *client = cd->client;
+
+	if (!cd->power_on)
+		return 0;
+	disable_irq(cd->irq);
+	reset_assert(cd);
+	atomic_set(&cd->ind_ab, IND_AB_UPDATED);
+	if (client->on_online_changed)
+		client->on_online_changed(client, false);
+	cd->power_on = false;
+	return 0;
+}
+
+static int task_fn_power_on_2(struct anybuss_host *cd,
+			      struct ab_task *t)
+{
+	if (completion_done(&cd->card_boot)) {
+		cd->power_on = true;
+		return 0;
+	}
+	if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
+		disable_irq(cd->irq);
+		reset_assert(cd);
+		dev_err(cd->dev, "power on timed out");
+		return -ETIMEDOUT;
+	}
+	return -EINPROGRESS;
+}
+
+static int task_fn_power_on(struct anybuss_host *cd,
+			    struct ab_task *t)
+{
+	unsigned int dummy;
+
+	if (cd->power_on)
+		return 0;
+	/*
+	 * anybus docs: prevent false 'init done' interrupt by
+	 * doing a dummy read of IND_AB register while in reset.
+	 */
+	regmap_read(cd->regmap, REG_IND_AB, &dummy);
+	reinit_completion(&cd->card_boot);
+	enable_irq(cd->irq);
+	reset_deassert(cd);
+	t->task_fn = task_fn_power_on_2;
+	return -EINPROGRESS;
+}
+
+int anybuss_set_power(struct anybuss_client *client, bool power_on)
+{
+	struct anybuss_host *cd = client->host;
+	struct ab_task *t;
+	int err;
+
+	t = ab_task_create_get(cd->qcache, power_on ?
+				task_fn_power_on : task_fn_power_off);
+	if (!t)
+		return -ENOMEM;
+	err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+	ab_task_put(t);
+	return err;
+}
+EXPORT_SYMBOL_GPL(anybuss_set_power);
+
+/* ---------------------------- area tasks ------------------------ */
+
+static int task_fn_area_3(struct anybuss_host *cd, struct ab_task *t)
+{
+	struct area_priv *pd = &t->area_pd;
+
+	if (!cd->power_on)
+		return -EIO;
+	if (atomic_read(&cd->ind_ab) & pd->flags) {
+		/* area not released yet */
+		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+			return -ETIMEDOUT;
+		return -EINPROGRESS;
+	}
+	return 0;
+}
+
+static int task_fn_area_2(struct anybuss_host *cd, struct ab_task *t)
+{
+	struct area_priv *pd = &t->area_pd;
+	unsigned int ind_ap;
+	int ret;
+
+	if (!cd->power_on)
+		return -EIO;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	if (!(atomic_read(&cd->ind_ab) & pd->flags)) {
+		/* we don't own the area yet */
+		if (time_after(jiffies, t->start_jiffies + TIMEOUT)) {
+			dev_warn(cd->dev, "timeout waiting for area");
+			dump_stack();
+			return -ETIMEDOUT;
+		}
+		return -EINPROGRESS;
+	}
+	/* we own the area, do what we're here to do */
+	if (pd->is_write)
+		regmap_bulk_write(cd->regmap, pd->addr, pd->buf,
+				  pd->count);
+	else
+		regmap_bulk_read(cd->regmap, pd->addr, pd->buf,
+				 pd->count);
+	/* ask to release the area, must use unlocked release */
+	ind_ap &= ~IND_AP_ABITS;
+	ind_ap |= pd->flags;
+	ret = write_ind_ap(cd->regmap, ind_ap);
+	if (ret)
+		return ret;
+	t->task_fn = task_fn_area_3;
+	return -EINPROGRESS;
+}
+
+static int task_fn_area(struct anybuss_host *cd, struct ab_task *t)
+{
+	struct area_priv *pd = &t->area_pd;
+	unsigned int ind_ap;
+	int ret;
+
+	if (!cd->power_on)
+		return -EIO;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	/* ask to take the area */
+	ind_ap &= ~IND_AP_ABITS;
+	ind_ap |= pd->flags | IND_AP_ACTION | IND_AP_LOCK;
+	ret = write_ind_ap(cd->regmap, ind_ap);
+	if (ret)
+		return ret;
+	t->task_fn = task_fn_area_2;
+	return -EINPROGRESS;
+}
+
+static struct ab_task *
+create_area_reader(struct kmem_cache *qcache, u16 flags, u16 addr,
+		   size_t count)
+{
+	struct ab_task *t;
+	struct area_priv *ap;
+
+	t = ab_task_create_get(qcache, task_fn_area);
+	if (!t)
+		return NULL;
+	ap = &t->area_pd;
+	ap->flags = flags;
+	ap->addr = addr;
+	ap->is_write = false;
+	ap->count = count;
+	return t;
+}
+
+static struct ab_task *
+create_area_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
+		   const void *buf, size_t count)
+{
+	struct ab_task *t;
+	struct area_priv *ap;
+
+	t = ab_task_create_get(qcache, task_fn_area);
+	if (!t)
+		return NULL;
+	ap = &t->area_pd;
+	ap->flags = flags;
+	ap->addr = addr;
+	ap->is_write = true;
+	ap->count = count;
+	memcpy(ap->buf, buf, count);
+	return t;
+}
+
+static struct ab_task *
+create_area_user_writer(struct kmem_cache *qcache, u16 flags, u16 addr,
+			const void __user *buf, size_t count)
+{
+	struct ab_task *t;
+	struct area_priv *ap;
+
+	t = ab_task_create_get(qcache, task_fn_area);
+	if (!t)
+		return ERR_PTR(-ENOMEM);
+	ap = &t->area_pd;
+	ap->flags = flags;
+	ap->addr = addr;
+	ap->is_write = true;
+	ap->count = count;
+	if (copy_from_user(ap->buf, buf, count)) {
+		ab_task_put(t);
+		return ERR_PTR(-EFAULT);
+	}
+	return t;
+}
+
+static bool area_range_ok(u16 addr, size_t count, u16 area_start,
+			  size_t area_sz)
+{
+	u16 area_end_ex = area_start + area_sz;
+	u16 addr_end_ex;
+
+	if (addr < area_start)
+		return false;
+	if (addr >= area_end_ex)
+		return false;
+	addr_end_ex = addr + count;
+	if (addr_end_ex > area_end_ex)
+		return false;
+	return true;
+}
+
+/* -------------------------- mailbox tasks ----------------------- */
+
+static int task_fn_mbox_2(struct anybuss_host *cd, struct ab_task *t)
+{
+	struct mbox_priv *pd = &t->mbox_pd;
+	unsigned int ind_ap;
+
+	if (!cd->power_on)
+		return -EIO;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	if (((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MOUT) == 0) {
+		/* output message not here */
+		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+			return -ETIMEDOUT;
+		return -EINPROGRESS;
+	}
+	/* grab the returned header and msg */
+	regmap_bulk_read(cd->regmap, MBOX_OUT_AREA, &pd->hdr,
+			 sizeof(pd->hdr));
+	regmap_bulk_read(cd->regmap, MBOX_OUT_AREA + sizeof(pd->hdr),
+			 pd->msg, pd->msg_in_sz);
+	/* tell anybus we've consumed the message */
+	ind_ap ^= IND_AX_MOUT;
+	return write_ind_ap(cd->regmap, ind_ap);
+}
+
+static int task_fn_mbox(struct anybuss_host *cd, struct ab_task *t)
+{
+	struct mbox_priv *pd = &t->mbox_pd;
+	unsigned int ind_ap;
+	int ret;
+
+	if (!cd->power_on)
+		return -EIO;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	if ((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_MIN) {
+		/* mbox input area busy */
+		if (time_after(jiffies, t->start_jiffies + TIMEOUT))
+			return -ETIMEDOUT;
+		return -EINPROGRESS;
+	}
+	/* write the header and msg to input area */
+	regmap_bulk_write(cd->regmap, MBOX_IN_AREA, &pd->hdr,
+			  sizeof(pd->hdr));
+	regmap_bulk_write(cd->regmap, MBOX_IN_AREA + sizeof(pd->hdr),
+			  pd->msg, pd->msg_out_sz);
+	/* tell anybus we gave it a message */
+	ind_ap ^= IND_AX_MIN;
+	ret = write_ind_ap(cd->regmap, ind_ap);
+	if (ret)
+		return ret;
+	t->start_jiffies = jiffies;
+	t->task_fn = task_fn_mbox_2;
+	return -EINPROGRESS;
+}
+
+static void log_invalid_other(struct device *dev,
+			      struct anybus_mbox_hdr *hdr)
+{
+	size_t ext_offs = ARRAY_SIZE(hdr->extended) - 1;
+	u16 code = be16_to_cpu(hdr->extended[ext_offs]);
+
+	dev_err(dev, "   Invalid other: [0x%02X]", code);
+}
+
+static const char * const EMSGS[] = {
+	"Invalid Message ID",
+	"Invalid Message Type",
+	"Invalid Command",
+	"Invalid Data Size",
+	"Message Header Malformed (offset 008h)",
+	"Message Header Malformed (offset 00Ah)",
+	"Message Header Malformed (offset 00Ch - 00Dh)",
+	"Invalid Address",
+	"Invalid Response",
+	"Flash Config Error",
+};
+
+static int mbox_cmd_err(struct device *dev, struct mbox_priv *mpriv)
+{
+	int i;
+	u8 ecode;
+	struct anybus_mbox_hdr *hdr = &mpriv->hdr;
+	u16 info = be16_to_cpu(hdr->info);
+	u8 *phdr = (u8 *)hdr;
+	u8 *pmsg = mpriv->msg;
+
+	if (!(info & 0x8000))
+		return 0;
+	ecode = (info >> 8) & 0x0F;
+	dev_err(dev, "mailbox command failed:");
+	if (ecode == 0x0F)
+		log_invalid_other(dev, hdr);
+	else if (ecode < ARRAY_SIZE(EMSGS))
+		dev_err(dev, "   Error code: %s (0x%02X)",
+			EMSGS[ecode], ecode);
+	else
+		dev_err(dev, "   Error code: 0x%02X\n", ecode);
+	dev_err(dev, "Failed command:");
+	dev_err(dev, "Message Header:");
+	for (i = 0; i < sizeof(mpriv->hdr); i += 2)
+		dev_err(dev, "%02X%02X", phdr[i], phdr[i + 1]);
+	dev_err(dev, "Message Data:");
+	for (i = 0; i < mpriv->msg_in_sz; i += 2)
+		dev_err(dev, "%02X%02X", pmsg[i], pmsg[i + 1]);
+	dev_err(dev, "Stack dump:");
+	dump_stack();
+	return -EIO;
+}
+
+static int _anybus_mbox_cmd(struct anybuss_host *cd,
+			    u16 cmd_num, bool is_fb_cmd,
+				const void *msg_out, size_t msg_out_sz,
+				void *msg_in, size_t msg_in_sz,
+				const void *ext, size_t ext_sz)
+{
+	struct ab_task *t;
+	struct mbox_priv *pd;
+	struct anybus_mbox_hdr *h;
+	size_t msg_sz = max(msg_in_sz, msg_out_sz);
+	u16 info;
+	int err;
+
+	if (msg_sz > MAX_MBOX_MSG_SZ)
+		return -EINVAL;
+	if (ext && ext_sz > sizeof(h->extended))
+		return -EINVAL;
+	t = ab_task_create_get(cd->qcache, task_fn_mbox);
+	if (!t)
+		return -ENOMEM;
+	pd = &t->mbox_pd;
+	h = &pd->hdr;
+	info = is_fb_cmd ? INFO_TYPE_FB : INFO_TYPE_APP;
+	/*
+	 * prevent uninitialized memory in the header from being sent
+	 * across the anybus
+	 */
+	memset(h, 0, sizeof(*h));
+	h->info = cpu_to_be16(info | INFO_COMMAND);
+	h->cmd_num = cpu_to_be16(cmd_num);
+	h->data_size = cpu_to_be16(msg_out_sz);
+	h->frame_count = cpu_to_be16(1);
+	h->frame_num = cpu_to_be16(1);
+	h->offset_high = cpu_to_be16(0);
+	h->offset_low = cpu_to_be16(0);
+	if (ext)
+		memcpy(h->extended, ext, ext_sz);
+	memcpy(pd->msg, msg_out, msg_out_sz);
+	pd->msg_out_sz = msg_out_sz;
+	pd->msg_in_sz = msg_in_sz;
+	err = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+	if (err)
+		goto out;
+	/*
+	 * mailbox mechanism worked ok, but maybe the mbox response
+	 * contains an error ?
+	 */
+	err = mbox_cmd_err(cd->dev, pd);
+	if (err)
+		goto out;
+	memcpy(msg_in, pd->msg, msg_in_sz);
+out:
+	ab_task_put(t);
+	return err;
+}
+
+/* ------------------------ anybus queues ------------------------ */
+
+static void process_q(struct anybuss_host *cd, struct kfifo *q)
+{
+	struct ab_task *t;
+	int ret;
+
+	ret = kfifo_out_peek(q, &t, sizeof(t));
+	if (!ret)
+		return;
+	t->result = t->task_fn(cd, t);
+	if (t->result != -EINPROGRESS)
+		ab_task_dequeue_finish_put(q, cd);
+}
+
+static bool qs_have_work(struct kfifo *qs, size_t num)
+{
+	size_t i;
+	struct ab_task *t;
+	int ret;
+
+	for (i = 0; i < num; i++, qs++) {
+		ret = kfifo_out_peek(qs, &t, sizeof(t));
+		if (ret && (t->result != -EINPROGRESS))
+			return true;
+	}
+	return false;
+}
+
+static void process_qs(struct anybuss_host *cd)
+{
+	size_t i;
+	struct kfifo *qs = cd->qs;
+	size_t nqs = ARRAY_SIZE(cd->qs);
+
+	for (i = 0; i < nqs; i++, qs++)
+		process_q(cd, qs);
+}
+
+static void softint_ack(struct anybuss_host *cd)
+{
+	unsigned int ind_ap;
+
+	cd->softint_pending = false;
+	if (!cd->power_on)
+		return;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	ind_ap &= ~IND_AX_EVNT;
+	ind_ap |= atomic_read(&cd->ind_ab) & IND_AX_EVNT;
+	write_ind_ap(cd->regmap, ind_ap);
+}
+
+static void process_softint(struct anybuss_host *cd)
+{
+	struct anybuss_client *client = cd->client;
+	static const u8 zero;
+	int ret;
+	unsigned int ind_ap, ev;
+	struct ab_task *t;
+
+	if (!cd->power_on)
+		return;
+	if (cd->softint_pending)
+		return;
+	regmap_read(cd->regmap, REG_IND_AP, &ind_ap);
+	if (!((atomic_read(&cd->ind_ab) ^ ind_ap) & IND_AX_EVNT))
+		return;
+	/* process software interrupt */
+	regmap_read(cd->regmap, REG_EVENT_CAUSE, &ev);
+	if (ev & EVENT_CAUSE_FBON) {
+		if (client->on_online_changed)
+			client->on_online_changed(client, true);
+		dev_dbg(cd->dev, "Fieldbus ON");
+	}
+	if (ev & EVENT_CAUSE_FBOF) {
+		if (client->on_online_changed)
+			client->on_online_changed(client, false);
+		dev_dbg(cd->dev, "Fieldbus OFF");
+	}
+	if (ev & EVENT_CAUSE_DC) {
+		if (client->on_area_updated)
+			client->on_area_updated(client);
+		dev_dbg(cd->dev, "Fieldbus data changed");
+	}
+	/*
+	 * reset the event cause bits.
+	 * this must be done while owning the fbctrl area, so we'll
+	 * enqueue a task to do that.
+	 */
+	t = create_area_writer(cd->qcache, IND_AX_FBCTRL,
+			       REG_EVENT_CAUSE, &zero, sizeof(zero));
+	if (!t) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	t->done_fn = softint_ack;
+	ret = ab_task_enqueue(t, cd->powerq, &cd->qlock, &cd->wq);
+	ab_task_put(t);
+	cd->softint_pending = true;
+out:
+	WARN_ON(ret);
+	if (ret)
+		softint_ack(cd);
+}
+
+static int qthread_fn(void *data)
+{
+	struct anybuss_host *cd = data;
+	struct kfifo *qs = cd->qs;
+	size_t nqs = ARRAY_SIZE(cd->qs);
+	unsigned int ind_ab;
+
+	/*
+	 * this kernel thread has exclusive access to the anybus's memory.
+	 * only exception: the IND_AB register, which is accessed exclusively
+	 * by the interrupt service routine (ISR). This thread must not touch
+	 * the IND_AB register, but it does require access to its value.
+	 *
+	 * the interrupt service routine stores the register's value in
+	 * cd->ind_ab (an atomic_t), where we may safely access it, with the
+	 * understanding that it can be modified by the ISR at any time.
+	 */
+
+	while (!kthread_should_stop()) {
+		/*
+		 * make a local copy of IND_AB, so we can go around the loop
+		 * again in case it changed while processing queues and softint.
+		 */
+		ind_ab = atomic_read(&cd->ind_ab);
+		process_qs(cd);
+		process_softint(cd);
+		wait_event_timeout(cd->wq,
+				   (atomic_read(&cd->ind_ab) != ind_ab) ||
+				qs_have_work(qs, nqs) ||
+				kthread_should_stop(),
+			HZ);
+		/*
+		 * time out so even 'stuck' tasks will run eventually,
+		 * and can time out.
+		 */
+	}
+
+	return 0;
+}
+
+/* ------------------------ anybus exports ------------------------ */
+
+int anybuss_start_init(struct anybuss_client *client,
+		       const struct anybuss_memcfg *cfg)
+{
+	int ret;
+	u16 op_mode;
+	struct anybuss_host *cd = client->host;
+	struct msg_anybus_init msg = {
+		.input_io_len = cpu_to_be16(cfg->input_io),
+		.input_dpram_len = cpu_to_be16(cfg->input_dpram),
+		.input_total_len = cpu_to_be16(cfg->input_total),
+		.output_io_len = cpu_to_be16(cfg->output_io),
+		.output_dpram_len = cpu_to_be16(cfg->output_dpram),
+		.output_total_len = cpu_to_be16(cfg->output_total),
+		.notif_config = cpu_to_be16(0x000F),
+		.wd_val = cpu_to_be16(0),
+	};
+
+	switch (cfg->offl_mode) {
+	case AB_OFFL_MODE_CLEAR:
+		op_mode = 0;
+		break;
+	case AB_OFFL_MODE_FREEZE:
+		op_mode = OP_MODE_FBFC;
+		break;
+	case AB_OFFL_MODE_SET:
+		op_mode = OP_MODE_FBS;
+		break;
+	default:
+		return -EINVAL;
+	}
+	msg.op_mode = cpu_to_be16(op_mode | OP_MODE_CD);
+	ret = _anybus_mbox_cmd(cd, CMD_START_INIT, false, NULL, 0,
+			       NULL, 0, NULL, 0);
+	if (ret)
+		return ret;
+	return _anybus_mbox_cmd(cd, CMD_ANYBUS_INIT, false,
+			&msg, sizeof(msg), NULL, 0, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_start_init);
+
+int anybuss_finish_init(struct anybuss_client *client)
+{
+	struct anybuss_host *cd = client->host;
+
+	return _anybus_mbox_cmd(cd, CMD_END_INIT, false, NULL, 0,
+					NULL, 0, NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_finish_init);
+
+int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
+			void *buf, size_t count)
+{
+	struct anybuss_host *cd = client->host;
+	struct ab_task *t;
+	int ret;
+
+	if (count == 0)
+		return 0;
+	if (!area_range_ok(addr, count, FBCTRL_AREA,
+			   MAX_FBCTRL_AREA_SZ))
+		return -EFAULT;
+	t = create_area_reader(cd->qcache, IND_AX_FBCTRL, addr, count);
+	if (!t)
+		return -ENOMEM;
+	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+	if (ret)
+		goto out;
+	memcpy(buf, t->area_pd.buf, count);
+out:
+	ab_task_put(t);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(anybuss_read_fbctrl);
+
+int anybuss_write_input(struct anybuss_client *client,
+			const char __user *buf, size_t size,
+				loff_t *offset)
+{
+	ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
+	struct anybuss_host *cd = client->host;
+	struct ab_task *t;
+	int ret;
+
+	if (len <= 0)
+		return 0;
+	t = create_area_user_writer(cd->qcache, IND_AX_IN,
+				    DATA_IN_AREA + *offset, buf, len);
+	if (IS_ERR(t))
+		return PTR_ERR(t);
+	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+	ab_task_put(t);
+	if (ret)
+		return ret;
+	/* success */
+	*offset += len;
+	return len;
+}
+EXPORT_SYMBOL_GPL(anybuss_write_input);
+
+int anybuss_read_output(struct anybuss_client *client,
+			char __user *buf, size_t size,
+				loff_t *offset)
+{
+	ssize_t len = min_t(loff_t, MAX_DATA_AREA_SZ - *offset, size);
+	struct anybuss_host *cd = client->host;
+	struct ab_task *t;
+	int ret;
+
+	if (len <= 0)
+		return 0;
+	t = create_area_reader(cd->qcache, IND_AX_OUT,
+			       DATA_OUT_AREA + *offset, len);
+	if (!t)
+		return -ENOMEM;
+	ret = ab_task_enqueue_wait(t, cd->powerq, &cd->qlock, &cd->wq);
+	if (ret)
+		goto out;
+	if (copy_to_user(buf, t->area_pd.buf, len))
+		ret = -EFAULT;
+out:
+	ab_task_put(t);
+	if (ret)
+		return ret;
+	/* success */
+	*offset += len;
+	return len;
+}
+EXPORT_SYMBOL_GPL(anybuss_read_output);
+
+int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
+		     const void *buf, size_t count)
+{
+	struct anybuss_host *cd = client->host;
+
+	return _anybus_mbox_cmd(cd, cmd_num, true, buf, count, NULL, 0,
+					NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_send_msg);
+
+int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
+		     const void *buf, size_t count)
+{
+	struct anybuss_host *cd = client->host;
+
+	return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, NULL, 0,
+					buf, count);
+}
+EXPORT_SYMBOL_GPL(anybuss_send_ext);
+
+int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
+		     void *buf, size_t count)
+{
+	struct anybuss_host *cd = client->host;
+
+	return _anybus_mbox_cmd(cd, cmd_num, true, NULL, 0, buf, count,
+					NULL, 0);
+}
+EXPORT_SYMBOL_GPL(anybuss_recv_msg);
+
+/* ------------------------ bus functions ------------------------ */
+
+static int anybus_bus_match(struct device *dev,
+			    struct device_driver *drv)
+{
+	struct anybuss_client_driver *adrv =
+		to_anybuss_client_driver(drv);
+	struct anybuss_client *adev =
+		to_anybuss_client(dev);
+
+	return adrv->fieldbus_type == adev->fieldbus_type;
+}
+
+static int anybus_bus_probe(struct device *dev)
+{
+	struct anybuss_client_driver *adrv =
+		to_anybuss_client_driver(dev->driver);
+	struct anybuss_client *adev =
+		to_anybuss_client(dev);
+
+	if (!adrv->probe)
+		return -ENODEV;
+	return adrv->probe(adev);
+}
+
+static int anybus_bus_remove(struct device *dev)
+{
+	struct anybuss_client_driver *adrv =
+		to_anybuss_client_driver(dev->driver);
+
+	if (adrv->remove)
+		return adrv->remove(to_anybuss_client(dev));
+	return 0;
+}
+
+static struct bus_type anybus_bus = {
+	.name		= "anybuss",
+	.match		= anybus_bus_match,
+	.probe		= anybus_bus_probe,
+	.remove		= anybus_bus_remove,
+};
+
+int anybuss_client_driver_register(struct anybuss_client_driver *drv)
+{
+	drv->driver.bus = &anybus_bus;
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(anybuss_client_driver_register);
+
+void anybuss_client_driver_unregister(struct anybuss_client_driver *drv)
+{
+	return driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(anybuss_client_driver_unregister);
+
+static void client_device_release(struct device *dev)
+{
+	kfree(to_anybuss_client(dev));
+}
+
+static int taskq_alloc(struct device *dev, struct kfifo *q)
+{
+	void *buf;
+	size_t size = 64 * sizeof(struct ab_task *);
+
+	buf = devm_kzalloc(dev, size, GFP_KERNEL);
+	if (!buf)
+		return -EIO;
+	return kfifo_init(q, buf, size);
+}
+
+static int anybus_of_get_host_idx(struct device_node *np)
+{
+	const __be32 *host_idx;
+
+	host_idx = of_get_address(np, 0, NULL, NULL);
+	if (!host_idx)
+		return -ENOENT;
+	return __be32_to_cpu(*host_idx);
+}
+
+static struct device_node *
+anybus_of_find_child_device(struct device *dev, int host_idx)
+{
+	struct device_node *node;
+
+	if (!dev || !dev->of_node)
+		return NULL;
+	for_each_child_of_node(dev->of_node, node) {
+		if (anybus_of_get_host_idx(node) == host_idx)
+			return node;
+	}
+	return NULL;
+}
+
+struct anybuss_host * __must_check
+anybuss_host_common_probe(struct device *dev,
+			  const struct anybuss_ops *ops)
+{
+	int ret, i;
+	u8 val[4];
+	u16 fieldbus_type;
+	struct anybuss_host *cd;
+
+	cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
+	if (!cd)
+		return ERR_PTR(-ENOMEM);
+	cd->dev = dev;
+	cd->host_idx = ops->host_idx;
+	init_completion(&cd->card_boot);
+	init_waitqueue_head(&cd->wq);
+	for (i = 0; i < ARRAY_SIZE(cd->qs); i++) {
+		ret = taskq_alloc(dev, &cd->qs[i]);
+		if (ret)
+			return ERR_PTR(ret);
+	}
+	if (WARN_ON(ARRAY_SIZE(cd->qs) < 3))
+		return ERR_PTR(-EINVAL);
+	cd->powerq = &cd->qs[0];
+	cd->mboxq = &cd->qs[1];
+	cd->areaq = &cd->qs[2];
+	cd->reset = ops->reset;
+	if (!cd->reset)
+		return ERR_PTR(-EINVAL);
+	cd->regmap = ops->regmap;
+	if (!cd->regmap)
+		return ERR_PTR(-EINVAL);
+	spin_lock_init(&cd->qlock);
+	cd->qcache = kmem_cache_create(dev_name(dev),
+				       sizeof(struct ab_task), 0, 0, NULL);
+	if (!cd->qcache)
+		return ERR_PTR(-ENOMEM);
+	cd->irq = ops->irq;
+	if (cd->irq <= 0) {
+		ret = -EINVAL;
+		goto err_qcache;
+	}
+	/*
+	 * use a dpram test to check if a card is present, this is only
+	 * possible while in reset.
+	 */
+	reset_assert(cd);
+	if (test_dpram(cd->regmap)) {
+		dev_err(dev, "no Anybus-S card in slot");
+		ret = -ENODEV;
+		goto err_qcache;
+	}
+	ret = devm_request_threaded_irq(dev, cd->irq, NULL, irq_handler,
+					IRQF_ONESHOT, dev_name(dev), cd);
+	if (ret) {
+		dev_err(dev, "could not request irq");
+		goto err_qcache;
+	}
+	/*
+	 * startup sequence:
+	 *   perform dummy IND_AB read to prevent false 'init done' irq
+	 *     (already done by test_dpram() above)
+	 *   release reset
+	 *   wait for first interrupt
+	 *   interrupt came in: ready to go !
+	 */
+	reset_deassert(cd);
+	ret = wait_for_completion_timeout(&cd->card_boot, TIMEOUT);
+	if (ret == 0)
+		ret = -ETIMEDOUT;
+	if (ret < 0)
+		goto err_reset;
+	/*
+	 * according to the anybus docs, we're allowed to read these
+	 * without handshaking / reserving the area
+	 */
+	dev_info(dev, "Anybus-S card detected");
+	regmap_bulk_read(cd->regmap, REG_BOOTLOADER_V, val, 2);
+	dev_info(dev, "Bootloader version: %02X%02X",
+		 val[0], val[1]);
+	regmap_bulk_read(cd->regmap, REG_API_V, val, 2);
+	dev_info(dev, "API version: %02X%02X", val[0], val[1]);
+	regmap_bulk_read(cd->regmap, REG_FIELDBUS_V, val, 2);
+	dev_info(dev, "Fieldbus version: %02X%02X", val[0], val[1]);
+	regmap_bulk_read(cd->regmap, REG_SERIAL_NO, val, 4);
+	dev_info(dev, "Serial number: %02X%02X%02X%02X",
+		 val[0], val[1], val[2], val[3]);
+	add_device_randomness(&val, 4);
+	regmap_bulk_read(cd->regmap, REG_FIELDBUS_TYPE, &fieldbus_type,
+			 sizeof(fieldbus_type));
+	fieldbus_type = be16_to_cpu(fieldbus_type);
+	dev_info(dev, "Fieldbus type: %04X", fieldbus_type);
+	regmap_bulk_read(cd->regmap, REG_MODULE_SW_V, val, 2);
+	dev_info(dev, "Module SW version: %02X%02X",
+		 val[0], val[1]);
+	/* put card back reset until a client driver releases it */
+	disable_irq(cd->irq);
+	reset_assert(cd);
+	atomic_set(&cd->ind_ab, IND_AB_UPDATED);
+	/* fire up the queue thread */
+	cd->qthread = kthread_run(qthread_fn, cd, dev_name(dev));
+	if (IS_ERR(cd->qthread)) {
+		dev_err(dev, "could not create kthread");
+		ret = PTR_ERR(cd->qthread);
+		goto err_reset;
+	}
+	/*
+	 * now advertise that we've detected a client device (card).
+	 * the bus infrastructure will match it to a client driver.
+	 */
+	cd->client = kzalloc(sizeof(*cd->client), GFP_KERNEL);
+	if (!cd->client) {
+		ret = -ENOMEM;
+		goto err_kthread;
+	}
+	cd->client->fieldbus_type = fieldbus_type;
+	cd->client->host = cd;
+	cd->client->dev.bus = &anybus_bus;
+	cd->client->dev.parent = dev;
+	cd->client->dev.release = client_device_release;
+	cd->client->dev.of_node =
+		anybus_of_find_child_device(dev, cd->host_idx);
+	dev_set_name(&cd->client->dev, "anybuss.card%d", cd->host_idx);
+	ret = device_register(&cd->client->dev);
+	if (ret)
+		goto err_device;
+	return cd;
+err_device:
+	device_unregister(&cd->client->dev);
+err_kthread:
+	kthread_stop(cd->qthread);
+err_reset:
+	reset_assert(cd);
+err_qcache:
+	kmem_cache_destroy(cd->qcache);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(anybuss_host_common_probe);
+
+void anybuss_host_common_remove(struct anybuss_host *host)
+{
+	struct anybuss_host *cd = host;
+
+	device_unregister(&cd->client->dev);
+	kthread_stop(cd->qthread);
+	reset_assert(cd);
+	kmem_cache_destroy(cd->qcache);
+}
+EXPORT_SYMBOL_GPL(anybuss_host_common_remove);
+
+static void host_release(struct device *dev, void *res)
+{
+	struct anybuss_host **dr = res;
+
+	anybuss_host_common_remove(*dr);
+}
+
+struct anybuss_host * __must_check
+devm_anybuss_host_common_probe(struct device *dev,
+			       const struct anybuss_ops *ops)
+{
+	struct anybuss_host **dr;
+	struct anybuss_host *host;
+
+	dr = devres_alloc(host_release, sizeof(struct anybuss_host *),
+			  GFP_KERNEL);
+	if (!dr)
+		return ERR_PTR(-ENOMEM);
+
+	host = anybuss_host_common_probe(dev, ops);
+	if (IS_ERR(host)) {
+		devres_free(dr);
+		return host;
+	}
+	*dr = host;
+	devres_add(dev, dr);
+	return host;
+}
+EXPORT_SYMBOL_GPL(devm_anybuss_host_common_probe);
+
+static int __init anybus_init(void)
+{
+	int ret;
+
+	ret = bus_register(&anybus_bus);
+	if (ret)
+		pr_err("could not register Anybus-S bus: %d\n", ret);
+	return ret;
+}
+module_init(anybus_init);
+
+static void __exit anybus_exit(void)
+{
+	bus_unregister(&anybus_bus);
+}
+module_exit(anybus_exit);
+
+MODULE_DESCRIPTION("HMS Anybus-S Host Driver");
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/anybuss-client.h b/include/linux/anybuss-client.h
new file mode 100644
index 000000000000..2e48fb8f0209
--- /dev/null
+++ b/include/linux/anybuss-client.h
@@ -0,0 +1,102 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Anybus-S client adapter definitions
+ *
+ * Copyright 2018 Arcx Inc
+ */
+
+#ifndef __LINUX_ANYBUSS_CLIENT_H__
+#define __LINUX_ANYBUSS_CLIENT_H__
+
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/poll.h>
+
+struct anybuss_host;
+
+struct anybuss_client {
+	struct device dev;
+	struct anybuss_host *host;
+	u16 fieldbus_type;
+	/*
+	 * these can be optionally set by the client to receive event
+	 * notifications from the host.
+	 */
+	void (*on_area_updated)(struct anybuss_client *client);
+	void (*on_online_changed)(struct anybuss_client *client, bool online);
+};
+
+struct anybuss_client_driver {
+	struct device_driver driver;
+	int (*probe)(struct anybuss_client *adev);
+	int (*remove)(struct anybuss_client *adev);
+	u16 fieldbus_type;
+};
+
+int anybuss_client_driver_register(struct anybuss_client_driver *drv);
+void anybuss_client_driver_unregister(struct anybuss_client_driver *drv);
+
+static inline struct anybuss_client *to_anybuss_client(struct device *dev)
+{
+	return container_of(dev, struct anybuss_client, dev);
+}
+
+static inline struct anybuss_client_driver *
+to_anybuss_client_driver(struct device_driver *drv)
+{
+	return container_of(drv, struct anybuss_client_driver, driver);
+}
+
+static inline void *
+anybuss_get_drvdata(const struct anybuss_client *client)
+{
+	return dev_get_drvdata(&client->dev);
+}
+
+static inline void
+anybuss_set_drvdata(struct anybuss_client *client, void *data)
+{
+	dev_set_drvdata(&client->dev, data);
+}
+
+int anybuss_set_power(struct anybuss_client *client, bool power_on);
+
+enum anybuss_offl_mode {
+	AB_OFFL_MODE_CLEAR = 0,
+	AB_OFFL_MODE_FREEZE,
+	AB_OFFL_MODE_SET
+};
+
+struct anybuss_memcfg {
+	u16 input_io;
+	u16 input_dpram;
+	u16 input_total;
+
+	u16 output_io;
+	u16 output_dpram;
+	u16 output_total;
+
+	enum anybuss_offl_mode offl_mode;
+};
+
+int anybuss_start_init(struct anybuss_client *client,
+		       const struct anybuss_memcfg *cfg);
+int anybuss_finish_init(struct anybuss_client *client);
+int anybuss_read_fbctrl(struct anybuss_client *client, u16 addr,
+			void *buf, size_t count);
+int anybuss_send_msg(struct anybuss_client *client, u16 cmd_num,
+		     const void *buf, size_t count);
+int anybuss_send_ext(struct anybuss_client *client, u16 cmd_num,
+		     const void *buf, size_t count);
+int anybuss_recv_msg(struct anybuss_client *client, u16 cmd_num,
+		     void *buf, size_t count);
+
+/* these help clients make a struct file_operations */
+int anybuss_write_input(struct anybuss_client *client,
+			const char __user *buf, size_t size,
+				loff_t *offset);
+int anybuss_read_output(struct anybuss_client *client,
+			char __user *buf, size_t size,
+				loff_t *offset);
+
+#endif /* __LINUX_ANYBUSS_CLIENT_H__ */
diff --git a/include/linux/anybuss-controller.h b/include/linux/anybuss-controller.h
new file mode 100644
index 000000000000..02fa0749043b
--- /dev/null
+++ b/include/linux/anybuss-controller.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Anybus-S controller definitions
+ *
+ * Copyright 2018 Arcx Inc
+ */
+
+#ifndef __LINUX_ANYBUSS_CONTROLLER_H__
+#define __LINUX_ANYBUSS_CONTROLLER_H__
+
+#include <linux/device.h>
+#include <linux/regmap.h>
+
+/*
+ * To instantiate an Anybus-S host, a controller should provide the following:
+ * - a reset function which resets the attached card;
+ * - a regmap which provides access to the attached card's dpram;
+ * - the irq of the attached card
+ */
+/**
+ * struct anybuss_ops - Controller resources to instantiate an Anybus-S host
+ *
+ * @reset:	asserts/deasserts the anybus card's reset line.
+ * @regmap:	provides access to the card's dual-port RAM area.
+ * @irq:	number of the interrupt connected to the card's interrupt line.
+ * @host_idx:	for multi-host controllers, the host index:
+ *		0 for the first host on the controller, 1 for the second, etc.
+ */
+struct anybuss_ops {
+	void (*reset)(struct device *dev, bool assert);
+	struct regmap *regmap;
+	int irq;
+	int host_idx;
+};
+
+struct anybuss_host;
+
+struct anybuss_host * __must_check
+anybuss_host_common_probe(struct device *dev,
+			  const struct anybuss_ops *ops);
+void anybuss_host_common_remove(struct anybuss_host *host);
+
+struct anybuss_host * __must_check
+devm_anybuss_host_common_probe(struct device *dev,
+			       const struct anybuss_ops *ops);
+
+#endif /* __LINUX_ANYBUSS_CONTROLLER_H__ */
-- 
2.17.1


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

* [PATCH v10 3/7] anybus-s: support the Arcx anybus controller
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 2/7] anybus-s: support HMS Anybus-S bus Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding Sven Van Asbroeck
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

Add a driver for the Arcx anybus controller.

This device implements two Anybus-S hosts (buses),
and connects to the SoC via a parallel memory bus.
There is also a CAN power readout, unrelated to the Anybus,
modelled as a regulator.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 drivers/staging/fieldbus/Makefile             |   1 -
 drivers/staging/fieldbus/anybuss/Kconfig      |  14 +
 drivers/staging/fieldbus/anybuss/Makefile     |   2 +
 .../staging/fieldbus/anybuss/arcx-anybus.c    | 398 ++++++++++++++++++
 4 files changed, 414 insertions(+), 1 deletion(-)
 create mode 100644 drivers/staging/fieldbus/anybuss/arcx-anybus.c

diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile
index c21056bdb3d2..bdf645d41344 100644
--- a/drivers/staging/fieldbus/Makefile
+++ b/drivers/staging/fieldbus/Makefile
@@ -5,4 +5,3 @@
 
 obj-$(CONFIG_FIELDBUS_DEV)	+= fieldbus_dev.o anybuss/
 fieldbus_dev-y			:= dev_core.o
-
diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig
index 5b495f25c11e..e257f8a517bb 100644
--- a/drivers/staging/fieldbus/anybuss/Kconfig
+++ b/drivers/staging/fieldbus/anybuss/Kconfig
@@ -7,3 +7,17 @@ config HMS_ANYBUSS_BUS
 	  You can attach a single Anybus-S compatible card to it, which
 	  typically provides fieldbus and industrial ethernet
 	  functionality.
+
+if HMS_ANYBUSS_BUS
+
+config ARCX_ANYBUS_CONTROLLER
+	tristate "Arcx Anybus-S Controller"
+	depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR
+	help
+	  Select this to get support for the Arcx Anybus controller.
+	  It connects to the SoC via a parallel memory bus, and
+	  embeds up to two Anybus-S buses (slots).
+	  There is also a CAN power readout, unrelated to the Anybus,
+	  modelled as a regulator.
+
+endif
diff --git a/drivers/staging/fieldbus/anybuss/Makefile b/drivers/staging/fieldbus/anybuss/Makefile
index 236579aa7270..7198b080926b 100644
--- a/drivers/staging/fieldbus/anybuss/Makefile
+++ b/drivers/staging/fieldbus/anybuss/Makefile
@@ -5,3 +5,5 @@
 
 obj-$(CONFIG_HMS_ANYBUSS_BUS)	+= anybuss_core.o
 anybuss_core-y			+= host.o
+
+obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o
diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c
new file mode 100644
index 000000000000..b21b8cf9cc0f
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/arcx-anybus.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Arcx Anybus-S Controller driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/idr.h>
+#include <linux/mutex.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regmap.h>
+
+#include <linux/anybuss-controller.h>
+
+#define CPLD_STATUS1		0x80
+#define CPLD_CONTROL		0x80
+#define CPLD_CONTROL_CRST	0x40
+#define CPLD_CONTROL_RST1	0x04
+#define CPLD_CONTROL_RST2	0x80
+#define CPLD_STATUS1_AB		0x02
+#define CPLD_STATUS1_CAN_POWER	0x01
+#define CPLD_DESIGN_LO		0x81
+#define CPLD_DESIGN_HI		0x82
+#define CPLD_CAP		0x83
+#define CPLD_CAP_COMPAT		0x01
+#define CPLD_CAP_SEP_RESETS	0x02
+
+struct controller_priv {
+	struct device *class_dev;
+	bool common_reset;
+	struct gpio_desc *reset_gpiod;
+	void __iomem *cpld_base;
+	struct mutex ctrl_lock; /* protects CONTROL register */
+	u8 control_reg;
+	char version[3];
+	u16 design_no;
+};
+
+static void do_reset(struct controller_priv *cd, u8 rst_bit, bool reset)
+{
+	mutex_lock(&cd->ctrl_lock);
+	/*
+	 * CPLD_CONTROL is write-only, so cache its value in
+	 * cd->control_reg
+	 */
+	if (reset)
+		cd->control_reg &= ~rst_bit;
+	else
+		cd->control_reg |= rst_bit;
+	writeb(cd->control_reg, cd->cpld_base + CPLD_CONTROL);
+	/*
+	 * h/w work-around:
+	 * the hardware is 'too fast', so a reset followed by an immediate
+	 * not-reset will _not_ change the anybus reset line in any way,
+	 * losing the reset. to prevent this from happening, introduce
+	 * a minimum reset duration.
+	 * Verified minimum safe duration required using a scope
+	 * on 14-June-2018: 100 us.
+	 */
+	if (reset)
+		usleep_range(100, 200);
+	mutex_unlock(&cd->ctrl_lock);
+}
+
+static int anybuss_reset(struct controller_priv *cd,
+			 unsigned long id, bool reset)
+{
+	if (id >= 2)
+		return -EINVAL;
+	if (cd->common_reset)
+		do_reset(cd, CPLD_CONTROL_CRST, reset);
+	else
+		do_reset(cd, id ? CPLD_CONTROL_RST2 : CPLD_CONTROL_RST1, reset);
+	return 0;
+}
+
+static void export_reset_0(struct device *dev, bool assert)
+{
+	struct controller_priv *cd = dev_get_drvdata(dev);
+
+	anybuss_reset(cd, 0, assert);
+}
+
+static void export_reset_1(struct device *dev, bool assert)
+{
+	struct controller_priv *cd = dev_get_drvdata(dev);
+
+	anybuss_reset(cd, 1, assert);
+}
+
+/*
+ * parallel bus limitation:
+ *
+ * the anybus is 8-bit wide. we can't assume that the hardware will translate
+ * word accesses on the parallel bus to multiple byte-accesses on the anybus.
+ *
+ * the imx WEIM bus does not provide this type of translation.
+ *
+ * to be safe, we will limit parallel bus accesses to a single byte
+ * at a time for now.
+ */
+
+static int read_reg_bus(void *context, unsigned int reg,
+			unsigned int *val)
+{
+	void __iomem *base = context;
+
+	*val = readb(base + reg);
+	return 0;
+}
+
+static int write_reg_bus(void *context, unsigned int reg,
+			 unsigned int val)
+{
+	void __iomem *base = context;
+
+	writeb(val, base + reg);
+	return 0;
+}
+
+static struct regmap *create_parallel_regmap(struct platform_device *pdev,
+					     int idx)
+{
+	struct regmap_config regmap_cfg = {
+		.reg_bits = 11,
+		.val_bits = 8,
+		/*
+		 * single-byte parallel bus accesses are atomic, so don't
+		 * require any synchronization.
+		 */
+		.disable_locking = true,
+		.reg_read = read_reg_bus,
+		.reg_write = write_reg_bus,
+	};
+	struct resource *res;
+	void __iomem *base;
+	struct device *dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, idx + 1);
+	if (resource_size(res) < (1 << regmap_cfg.reg_bits))
+		return ERR_PTR(-EINVAL);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return ERR_CAST(base);
+	return devm_regmap_init(dev, NULL, base, &regmap_cfg);
+}
+
+static struct anybuss_host *
+create_anybus_host(struct platform_device *pdev, int idx)
+{
+	struct anybuss_ops ops = {};
+
+	switch (idx) {
+	case 0:
+		ops.reset = export_reset_0;
+		break;
+	case 1:
+		ops.reset = export_reset_1;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+	ops.host_idx = idx;
+	ops.regmap = create_parallel_regmap(pdev, idx);
+	if (IS_ERR(ops.regmap))
+		return ERR_CAST(ops.regmap);
+	ops.irq = platform_get_irq(pdev, idx);
+	if (ops.irq <= 0)
+		return ERR_PTR(-EINVAL);
+	return devm_anybuss_host_common_probe(&pdev->dev, &ops);
+}
+
+static ssize_t version_show(struct device *dev,
+			    struct device_attribute *attr, char *buf)
+{
+	struct controller_priv *cd = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%s\n", cd->version);
+}
+static DEVICE_ATTR_RO(version);
+
+static ssize_t design_number_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct controller_priv *cd = dev_get_drvdata(dev);
+
+	return sprintf(buf, "%d\n", cd->design_no);
+}
+static DEVICE_ATTR_RO(design_number);
+
+static struct attribute *controller_attributes[] = {
+	&dev_attr_version.attr,
+	&dev_attr_design_number.attr,
+	NULL,
+};
+
+static struct attribute_group controller_attribute_group = {
+	.attrs = controller_attributes,
+};
+
+static const struct attribute_group *controller_attribute_groups[] = {
+	&controller_attribute_group,
+	NULL,
+};
+
+static void controller_device_release(struct device *dev)
+{
+	kfree(dev);
+}
+
+static int can_power_is_enabled(struct regulator_dev *rdev)
+{
+	struct controller_priv *cd = rdev_get_drvdata(rdev);
+
+	return !(readb(cd->cpld_base + CPLD_STATUS1) & CPLD_STATUS1_CAN_POWER);
+}
+
+static struct regulator_ops can_power_ops = {
+	.is_enabled = can_power_is_enabled,
+};
+
+static const struct regulator_desc can_power_desc = {
+	.name = "regulator-can-power",
+	.id = -1,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+	.ops = &can_power_ops,
+};
+
+static struct class *controller_class;
+static DEFINE_IDA(controller_index_ida);
+
+static int controller_probe(struct platform_device *pdev)
+{
+	struct controller_priv *cd;
+	struct device *dev = &pdev->dev;
+	struct regulator_config config = { };
+	struct regulator_dev *regulator;
+	int err, id;
+	struct resource *res;
+	struct anybuss_host *host;
+	u8 status1, cap;
+
+	cd = devm_kzalloc(dev, sizeof(*cd), GFP_KERNEL);
+	if (!cd)
+		return -ENOMEM;
+	dev_set_drvdata(dev, cd);
+	mutex_init(&cd->ctrl_lock);
+	cd->reset_gpiod = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+	if (IS_ERR(cd->reset_gpiod))
+		return PTR_ERR(cd->reset_gpiod);
+
+	/* CPLD control memory, sits at index 0 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cd->cpld_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(cd->cpld_base)) {
+		dev_err(dev,
+			"failed to map cpld base address\n");
+		err = PTR_ERR(cd->cpld_base);
+		goto out_reset;
+	}
+
+	/* identify cpld */
+	status1 = readb(cd->cpld_base + CPLD_STATUS1);
+	cd->design_no = (readb(cd->cpld_base + CPLD_DESIGN_HI) << 8) |
+				readb(cd->cpld_base + CPLD_DESIGN_LO);
+	snprintf(cd->version, sizeof(cd->version), "%c%d",
+		 'A' + ((status1 >> 5) & 0x7),
+		 (status1 >> 2) & 0x7);
+	dev_info(dev, "design number %d, revision %s\n",
+		 cd->design_no,
+		cd->version);
+	cap = readb(cd->cpld_base + CPLD_CAP);
+	if (!(cap & CPLD_CAP_COMPAT)) {
+		dev_err(dev, "unsupported controller [cap=0x%02X]", cap);
+		err = -ENODEV;
+		goto out_reset;
+	}
+
+	if (status1 & CPLD_STATUS1_AB) {
+		dev_info(dev, "has anybus-S slot(s)");
+		cd->common_reset = !(cap & CPLD_CAP_SEP_RESETS);
+		dev_info(dev, "supports %s", cd->common_reset ?
+			"a common reset" : "separate resets");
+		for (id = 0; id < 2; id++) {
+			host = create_anybus_host(pdev, id);
+			if (!IS_ERR(host))
+				continue;
+			err = PTR_ERR(host);
+			/* -ENODEV is fine, it just means no card detected */
+			if (err != -ENODEV)
+				goto out_reset;
+		}
+	}
+
+	id = ida_simple_get(&controller_index_ida, 0, 0, GFP_KERNEL);
+	if (id < 0) {
+		err = id;
+		goto out_reset;
+	}
+	/* export can power readout as a regulator */
+	config.dev = dev;
+	config.driver_data = cd;
+	regulator = devm_regulator_register(dev, &can_power_desc, &config);
+	if (IS_ERR(regulator)) {
+		err = PTR_ERR(regulator);
+		goto out_reset;
+	}
+	/* make controller info visible to userspace */
+	cd->class_dev = kzalloc(sizeof(*cd->class_dev), GFP_KERNEL);
+	if (!cd->class_dev) {
+		err = -ENOMEM;
+		goto out_ida;
+	}
+	cd->class_dev->class = controller_class;
+	cd->class_dev->groups = controller_attribute_groups;
+	cd->class_dev->parent = dev;
+	cd->class_dev->id = id;
+	cd->class_dev->release = controller_device_release;
+	dev_set_name(cd->class_dev, "%d", cd->class_dev->id);
+	dev_set_drvdata(cd->class_dev, cd);
+	err = device_register(cd->class_dev);
+	if (err)
+		goto out_dev;
+	return 0;
+out_dev:
+	put_device(cd->class_dev);
+out_ida:
+	ida_simple_remove(&controller_index_ida, id);
+out_reset:
+	gpiod_set_value_cansleep(cd->reset_gpiod, 1);
+	return err;
+}
+
+static int controller_remove(struct platform_device *pdev)
+{
+	struct controller_priv *cd = platform_get_drvdata(pdev);
+	int id = cd->class_dev->id;
+
+	device_unregister(cd->class_dev);
+	ida_simple_remove(&controller_index_ida, id);
+	gpiod_set_value_cansleep(cd->reset_gpiod, 1);
+	return 0;
+}
+
+static const struct of_device_id controller_of_match[] = {
+	{ .compatible = "arcx,anybus-controller" },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, controller_of_match);
+
+static struct platform_driver controller_driver = {
+	.probe = controller_probe,
+	.remove = controller_remove,
+	.driver		= {
+		.name   = "arcx-anybus-controller",
+		.of_match_table	= of_match_ptr(controller_of_match),
+	},
+};
+
+static int __init controller_init(void)
+{
+	int err;
+
+	controller_class = class_create(THIS_MODULE, "arcx_anybus_controller");
+	if (IS_ERR(controller_class))
+		return PTR_ERR(controller_class);
+	err = platform_driver_register(&controller_driver);
+	if (err)
+		class_destroy(controller_class);
+
+	return err;
+}
+
+static void __exit controller_exit(void)
+{
+	platform_driver_unregister(&controller_driver);
+	class_destroy(controller_class);
+	ida_destroy(&controller_index_ida);
+}
+
+module_init(controller_init);
+module_exit(controller_exit);
+
+MODULE_DESCRIPTION("Arcx Anybus-S Controller driver");
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


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

* [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (2 preceding siblings ...)
  2019-04-09 14:42 ` [PATCH v10 3/7] anybus-s: support the Arcx anybus controller Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-09 15:50   ` Rob Herring
  2019-04-09 14:42 ` [PATCH v10 5/7] dt-bindings: Add vendor prefix for arcx / Archronix Sven Van Asbroeck
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

This patch adds devicetree binding documentation for the
Arcx anybus controller.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 .../fieldbus/arcx,anybus-controller.txt       | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt

diff --git a/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt b/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt
new file mode 100644
index 000000000000..b1f9474f36d5
--- /dev/null
+++ b/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt
@@ -0,0 +1,71 @@
+* Arcx Anybus-S controller
+
+This chip communicates with the SoC over a parallel bus. It is
+expected that its Device Tree node is specified as the child of a node
+corresponding to the parallel bus used for communication.
+
+Required properties:
+--------------------
+
+  - compatible : The following chip-specific string:
+        "arcx,anybus-controller"
+
+  - reg : three areas:
+	index 0: bus memory area where the cpld registers are located.
+	index 1: bus memory area of the first  host's dual-port ram.
+	index 2: bus memory area of the second host's dual-port ram.
+
+  - reset-gpios : the GPIO pin connected to the reset line of the controller.
+
+  - interrupts : two interrupts:
+		index 0: interrupt connected to the first  host
+		index 1: interrupt connected to the second host
+	Generic interrupt client node bindings are described in
+	interrupt-controller/interrupts.txt
+
+Optional: use of subnodes
+-------------------------
+
+The card connected to a host may need additional properties. These can be
+specified in subnodes to the controller node.
+
+The subnodes are identified by the standard 'reg' property. Which information
+exactly can be specified depends on the bindings for the function driver
+for the subnode.
+
+Required controller node properties when using subnodes:
+- #address-cells: should be one.
+- #size-cells: should be zero.
+
+Required subnode properties:
+- reg: Must contain the host index of the card this subnode describes:
+		<0>	for the first  host on the controller
+		<1>	for the second host on the controller
+	Note that only a single card can be plugged into a host, so the host
+	index uniquely describes the card location.
+
+Example of usage:
+-----------------
+
+This example places the bridge on top of the i.MX WEIM parallel bus, see:
+Documentation/devicetree/bindings/bus/imx-weim.txt
+
+&weim {
+	controller@0,0 {
+		compatible = "arcx,anybus-controller";
+		reg = <0 0 0x100>, <0 0x400000 0x800>, <1 0x400000 0x800>;
+		reset-gpios = <&gpio5 2 GPIO_ACTIVE_HIGH>;
+		interrupt-parent = <&gpio1>;
+		interrupts = <1 IRQ_TYPE_LEVEL_LOW>, <5 IRQ_TYPE_LEVEL_LOW>;
+		/* fsl,weim-cs-timing is a i.MX WEIM bus specific property */
+		fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100
+				0x00000000 0xa0000240 0x00000000>;
+		/* optional subnode for a card plugged into the first host */
+		#address-cells = <1>;
+		#size-cells = <0>;
+		card@0 {
+			reg = <0>;
+			/* card specific properties go here */
+		};
+	};
+};
-- 
2.17.1


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

* [PATCH v10 5/7] dt-bindings: Add vendor prefix for arcx / Archronix
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (3 preceding siblings ...)
  2019-04-09 14:42 ` [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 6/7] fieldbus_dev: support HMS Profinet IRT industrial controller Sven Van Asbroeck
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

arcx Inc. is an engineering company which provides advanced
embedded systems and consulting services.

Archronix is a technology design and product engineering firm
specializing in hardware control systems and enabling software.
Clients include OEM's in the transportation, aerospace,
medical and commercial sectors.

Websites:
	http://www.arcx.com/
	http://www.archronix.com/

Reviewed-by: Rob Herring <robh@kernel.org>
Reviewed-by: Andreas Färber <afaerber@suse.de>
Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 8162b0eb4b50..f3c81407675e 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -36,6 +36,7 @@ aptina	Aptina Imaging
 arasan	Arasan Chip Systems
 archermind ArcherMind Technology (Nanjing) Co., Ltd.
 arctic	Arctic Sand
+arcx	arcx Inc. / Archronix Inc.
 aries	Aries Embedded GmbH
 arm	ARM Ltd.
 armadeus	ARMadeus Systems SARL
-- 
2.17.1


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

* [PATCH v10 6/7] fieldbus_dev: support HMS Profinet IRT industrial controller
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (4 preceding siblings ...)
  2019-04-09 14:42 ` [PATCH v10 5/7] dt-bindings: Add vendor prefix for arcx / Archronix Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-09 14:42 ` [PATCH v10 7/7] staging: fieldbus_dev: add TODO Sven Van Asbroeck
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

The Anybus-S PROFINET IRT communication module provides instant integration
to any Ethernet based LAN via SMTP, FTP, HTTP as well as PROFINET and
Modbus-TCP. Additional protocols can be implemented on top of TCP/IP
or UDP using the transparent socket interface.

Official documentation:
https://www.anybus.com/docs/librariesprovider7/default-document-library
/manuals-design-guides/hms-hmsi-168-52.pdf

This implementation is an Anybus-S client driver, designed to be
instantiated by the Anybus-S bus driver when it discovers the Profinet
card.

If loaded successfully, the driver registers itself as a fieldbus_dev,
and userspace can access it through the fieldbus_dev interface.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 drivers/staging/fieldbus/anybuss/Kconfig      |  16 ++
 drivers/staging/fieldbus/anybuss/Makefile     |   1 +
 .../staging/fieldbus/anybuss/hms-profinet.c   | 224 ++++++++++++++++++
 3 files changed, 241 insertions(+)
 create mode 100644 drivers/staging/fieldbus/anybuss/hms-profinet.c

diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig
index e257f8a517bb..41f241c73826 100644
--- a/drivers/staging/fieldbus/anybuss/Kconfig
+++ b/drivers/staging/fieldbus/anybuss/Kconfig
@@ -20,4 +20,20 @@ config ARCX_ANYBUS_CONTROLLER
 	  There is also a CAN power readout, unrelated to the Anybus,
 	  modelled as a regulator.
 
+config HMS_PROFINET
+	tristate "HMS Profinet IRT Controller (Anybus-S)"
+	depends on FIELDBUS_DEV && HMS_ANYBUSS_BUS
+	help
+	  If you say yes here you get support for the HMS Industrial
+	  Networks Profinet IRT Controller.
+
+	  It will be registered with the kernel as a fieldbus_dev,
+	  so userspace can interact with it via the fieldbus_dev userspace
+	  interface(s).
+
+	  This driver can also be built as a module. If so, the module
+	  will be called hms-profinet.
+
+	  If unsure, say N.
+
 endif
diff --git a/drivers/staging/fieldbus/anybuss/Makefile b/drivers/staging/fieldbus/anybuss/Makefile
index 7198b080926b..3ad3dcc6be56 100644
--- a/drivers/staging/fieldbus/anybuss/Makefile
+++ b/drivers/staging/fieldbus/anybuss/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_HMS_ANYBUSS_BUS)	+= anybuss_core.o
 anybuss_core-y			+= host.o
 
 obj-$(CONFIG_ARCX_ANYBUS_CONTROLLER) += arcx-anybus.o
+obj-$(CONFIG_HMS_PROFINET)	+= hms-profinet.o
diff --git a/drivers/staging/fieldbus/anybuss/hms-profinet.c b/drivers/staging/fieldbus/anybuss/hms-profinet.c
new file mode 100644
index 000000000000..89eae3251a69
--- /dev/null
+++ b/drivers/staging/fieldbus/anybuss/hms-profinet.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * HMS Profinet Client Driver
+ *
+ * Copyright (C) 2018 Arcx Inc
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fieldbus_dev.h>
+#include <linux/anybuss-client.h>
+
+#define PROFI_DPRAM_SIZE	512
+
+/*
+ * ---------------------------------------------------------------
+ * Anybus Profinet mailbox messages - definitions
+ * ---------------------------------------------------------------
+ * note that we're depending on the layout of these structures being
+ * exactly as advertised.
+ */
+
+struct msg_mac_addr {
+	u8 addr[6];
+};
+
+struct profi_priv {
+	struct fieldbus_dev fbdev;
+	struct anybuss_client *client;
+	struct mutex enable_lock; /* serializes card enable */
+	bool power_on;
+};
+
+static ssize_t
+profi_read_area(struct fieldbus_dev *fbdev, char __user *buf, size_t size,
+		loff_t *offset)
+{
+	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+	return anybuss_read_output(priv->client, buf, size, offset);
+}
+
+static ssize_t
+profi_write_area(struct fieldbus_dev *fbdev, const char __user *buf,
+		 size_t size, loff_t *offset)
+{
+	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+	return anybuss_write_input(priv->client, buf, size, offset);
+}
+
+static int profi_id_get(struct fieldbus_dev *fbdev, char *buf,
+			size_t max_size)
+{
+	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+	struct msg_mac_addr response;
+	int ret;
+
+	ret = anybuss_recv_msg(priv->client, 0x0010, &response,
+			       sizeof(response));
+	if (ret < 0)
+		return ret;
+	return snprintf(buf, max_size, "%02X:%02X:%02X:%02X:%02X:%02X\n",
+		response.addr[0], response.addr[1],
+		response.addr[2], response.addr[3],
+		response.addr[4], response.addr[5]);
+}
+
+static bool profi_enable_get(struct fieldbus_dev *fbdev)
+{
+	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+	bool power_on;
+
+	mutex_lock(&priv->enable_lock);
+	power_on = priv->power_on;
+	mutex_unlock(&priv->enable_lock);
+
+	return power_on;
+}
+
+static int __profi_enable(struct profi_priv *priv)
+{
+	int ret;
+	struct anybuss_client *client = priv->client;
+	/* Initialization Sequence, Generic Anybus Mode */
+	const struct anybuss_memcfg mem_cfg = {
+		.input_io = 220,
+		.input_dpram = PROFI_DPRAM_SIZE,
+		.input_total = PROFI_DPRAM_SIZE,
+		.output_io = 220,
+		.output_dpram = PROFI_DPRAM_SIZE,
+		.output_total = PROFI_DPRAM_SIZE,
+		.offl_mode = AB_OFFL_MODE_CLEAR,
+	};
+
+	/*
+	 * switch anybus off then on, this ensures we can do a complete
+	 * configuration cycle in case anybus was already on.
+	 */
+	anybuss_set_power(client, false);
+	ret = anybuss_set_power(client, true);
+	if (ret)
+		goto err;
+	ret = anybuss_start_init(client, &mem_cfg);
+	if (ret)
+		goto err;
+	ret = anybuss_finish_init(client);
+	if (ret)
+		goto err;
+	priv->power_on = true;
+	return 0;
+
+err:
+	anybuss_set_power(client, false);
+	priv->power_on = false;
+	return ret;
+}
+
+static int __profi_disable(struct profi_priv *priv)
+{
+	struct anybuss_client *client = priv->client;
+
+	anybuss_set_power(client, false);
+	priv->power_on = false;
+	return 0;
+}
+
+static int profi_simple_enable(struct fieldbus_dev *fbdev, bool enable)
+{
+	int ret;
+	struct profi_priv *priv = container_of(fbdev, struct profi_priv, fbdev);
+
+	mutex_lock(&priv->enable_lock);
+	if (enable)
+		ret = __profi_enable(priv);
+	else
+		ret = __profi_disable(priv);
+	mutex_unlock(&priv->enable_lock);
+
+	return ret;
+}
+
+static void profi_on_area_updated(struct anybuss_client *client)
+{
+	struct profi_priv *priv = anybuss_get_drvdata(client);
+
+	fieldbus_dev_area_updated(&priv->fbdev);
+}
+
+static void profi_on_online_changed(struct anybuss_client *client, bool online)
+{
+	struct profi_priv *priv = anybuss_get_drvdata(client);
+
+	fieldbus_dev_online_changed(&priv->fbdev, online);
+}
+
+static int profinet_probe(struct anybuss_client *client)
+{
+	struct profi_priv *priv;
+	struct device *dev = &client->dev;
+	int err;
+
+	client->on_area_updated = profi_on_area_updated;
+	client->on_online_changed = profi_on_online_changed;
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+	mutex_init(&priv->enable_lock);
+	priv->client = client;
+	priv->fbdev.read_area_sz = PROFI_DPRAM_SIZE;
+	priv->fbdev.write_area_sz = PROFI_DPRAM_SIZE;
+	priv->fbdev.card_name = "HMS Profinet IRT (Anybus-S)";
+	priv->fbdev.fieldbus_type = FIELDBUS_DEV_TYPE_PROFINET;
+	priv->fbdev.read_area = profi_read_area;
+	priv->fbdev.write_area = profi_write_area;
+	priv->fbdev.fieldbus_id_get = profi_id_get;
+	priv->fbdev.enable_get = profi_enable_get;
+	priv->fbdev.simple_enable_set = profi_simple_enable;
+	priv->fbdev.parent = dev;
+	err = fieldbus_dev_register(&priv->fbdev);
+	if (err < 0)
+		return err;
+	dev_info(dev, "card detected, registered as %s",
+		 dev_name(priv->fbdev.dev));
+	anybuss_set_drvdata(client, priv);
+
+	return 0;
+}
+
+static int profinet_remove(struct anybuss_client *client)
+{
+	struct profi_priv *priv = anybuss_get_drvdata(client);
+
+	fieldbus_dev_unregister(&priv->fbdev);
+	return 0;
+}
+
+static struct anybuss_client_driver profinet_driver = {
+	.probe = profinet_probe,
+	.remove = profinet_remove,
+	.driver		= {
+		.name   = "hms-profinet",
+		.owner	= THIS_MODULE,
+	},
+	.fieldbus_type = 0x0089,
+};
+
+static int __init profinet_init(void)
+{
+	return anybuss_client_driver_register(&profinet_driver);
+}
+module_init(profinet_init);
+
+static void __exit profinet_exit(void)
+{
+	return anybuss_client_driver_unregister(&profinet_driver);
+}
+module_exit(profinet_exit);
+
+MODULE_AUTHOR("Sven Van Asbroeck <TheSven73@gmail.com>");
+MODULE_DESCRIPTION("HMS Profinet IRT Driver (Anybus-S)");
+MODULE_LICENSE("GPL v2");
-- 
2.17.1


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

* [PATCH v10 7/7] staging: fieldbus_dev: add TODO
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (5 preceding siblings ...)
  2019-04-09 14:42 ` [PATCH v10 6/7] fieldbus_dev: support HMS Profinet IRT industrial controller Sven Van Asbroeck
@ 2019-04-09 14:42 ` Sven Van Asbroeck
  2019-04-15 17:41 ` [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Enrico Weigelt, metux IT consult
  2019-04-25  8:26 ` Lee Jones
  8 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 14:42 UTC (permalink / raw)
  To: robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

Add a TODO file which describes what needs to be done in order to
get the fieldbus_dev subsystem out of staging.

Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
---
 drivers/staging/fieldbus/TODO | 5 +++++
 1 file changed, 5 insertions(+)
 create mode 100644 drivers/staging/fieldbus/TODO

diff --git a/drivers/staging/fieldbus/TODO b/drivers/staging/fieldbus/TODO
new file mode 100644
index 000000000000..6d6626af4ec7
--- /dev/null
+++ b/drivers/staging/fieldbus/TODO
@@ -0,0 +1,5 @@
+TODO:
+-Get more people/drivers to use the Fieldbus userspace ABI. It requires
+ verification/sign-off by multiple users.
+
+Contact: Sven Van Asbroeck <TheSven73@gmail.com>
-- 
2.17.1


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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 14:42 ` [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding Sven Van Asbroeck
@ 2019-04-09 15:50   ` Rob Herring
  2019-04-09 17:16     ` Sven Van Asbroeck
  0 siblings, 1 reply; 34+ messages in thread
From: Rob Herring @ 2019-04-09 15:50 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: Linus Walleij, Lee Jones, Mark Rutland, Andreas Färber,
	Thierry Reding, David Lechner, Noralf Trønnes, Johan Hovold,
	Michal Simek, Michal Vokáč,
	Arnd Bergmann, Greg Kroah-Hartman, John Garry,
	Geert Uytterhoeven, Robin Murphy, Paul Gortmaker,
	Sebastien Bourdelin, Icenowy Zheng, Stuart Yoder, Jan Kiszka,
	Maxime Ripard, linux-kernel, netdev

On Tue, Apr 9, 2019 at 9:43 AM Sven Van Asbroeck <thesven73@gmail.com> wrote:
>
> This patch adds devicetree binding documentation for the
> Arcx anybus controller.
>
> Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
> ---
>  .../fieldbus/arcx,anybus-controller.txt       | 71 +++++++++++++++++++
>  1 file changed, 71 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt

DT patches don't get in my queue if you don't CC DT list.

Rob

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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 15:50   ` Rob Herring
@ 2019-04-09 17:16     ` Sven Van Asbroeck
  2019-04-09 17:31       ` Sven Van Asbroeck
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 17:16 UTC (permalink / raw)
  To: Rob Herring; +Cc: Greg Kroah-Hartman, linux-kernel, netdev

On Tue, Apr 9, 2019 at 11:51 AM Rob Herring <robh+dt@kernel.org> wrote:
>
> DT patches don't get in my queue if you don't CC DT list.
>

My apologies for the confusion. You already offered your tag to an
identical commit in v3:
https://lkml.org/lkml/2018/11/6/66

Am I ok to add it, given that nothing has changed in this particular commit?

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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 17:16     ` Sven Van Asbroeck
@ 2019-04-09 17:31       ` Sven Van Asbroeck
  2019-04-09 21:09         ` Rob Herring
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-09 17:31 UTC (permalink / raw)
  To: Rob Herring; +Cc: Greg Kroah-Hartman, linux-kernel, netdev

On Tue, Apr 9, 2019 at 1:16 PM Sven Van Asbroeck <thesven73@gmail.com> wrote:
>
> My apologies for the confusion. You already offered your tag to an
> identical commit in v3:
> https://lkml.org/lkml/2018/11/6/66
>
> Am I ok to add it, given that nothing has changed in this particular commit?

I added a link to the wrong version. The current patch is identical to
v5, where you
said it looks fine, but the series itself still has discussion.
https://lkml.org/lkml/2018/12/7/600

Should I re-post to the DT list?

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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 17:31       ` Sven Van Asbroeck
@ 2019-04-09 21:09         ` Rob Herring
  2019-04-10 13:11           ` Sven Van Asbroeck
  0 siblings, 1 reply; 34+ messages in thread
From: Rob Herring @ 2019-04-09 21:09 UTC (permalink / raw)
  To: Sven Van Asbroeck; +Cc: Greg Kroah-Hartman, linux-kernel, netdev

On Tue, Apr 9, 2019 at 12:32 PM Sven Van Asbroeck <thesven73@gmail.com> wrote:
>
> On Tue, Apr 9, 2019 at 1:16 PM Sven Van Asbroeck <thesven73@gmail.com> wrote:
> >
> > My apologies for the confusion. You already offered your tag to an
> > identical commit in v3:
> > https://lkml.org/lkml/2018/11/6/66
> >
> > Am I ok to add it, given that nothing has changed in this particular commit?
>
> I added a link to the wrong version. The current patch is identical to
> v5, where you
> said it looks fine, but the series itself still has discussion.
> https://lkml.org/lkml/2018/12/7/600
>
> Should I re-post to the DT list?

No. If you send again, then add my R-by if it hasn't changed.

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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-09 21:09         ` Rob Herring
@ 2019-04-10 13:11           ` Sven Van Asbroeck
  2019-04-16 11:11             ` Greg KH
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-10 13:11 UTC (permalink / raw)
  To: Rob Herring, Greg KH; +Cc: linux-kernel, netdev

On Tue, Apr 9, 2019 at 5:10 PM Rob Herring <robh+dt@kernel.org> wrote:
>
> No. If you send again, then add my R-by if it hasn't changed.

Thank you.

Greg, are you able to apply Rob's R-by tag if you merge this set as-is,
or would you like me to repost as v11 ?

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (6 preceding siblings ...)
  2019-04-09 14:42 ` [PATCH v10 7/7] staging: fieldbus_dev: add TODO Sven Van Asbroeck
@ 2019-04-15 17:41 ` Enrico Weigelt, metux IT consult
  2019-04-15 18:31   ` Sven Van Asbroeck
  2019-04-25  8:26 ` Lee Jones
  8 siblings, 1 reply; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-15 17:41 UTC (permalink / raw)
  To: Sven Van Asbroeck, robh+dt, linus.walleij
  Cc: lee.jones, mark.rutland, afaerber, treding, david, noralf, johan,
	monstr, michal.vokac, arnd, gregkh, john.garry, geert+renesas,
	robin.murphy, paul.gortmaker, sebastien.bourdelin, icenowy,
	stuyoder, jan.kiszka, maxime.ripard, linux-kernel, netdev

On 09.04.19 16:42, Sven Van Asbroeck wrote:

Hi,

> Fieldbus device (client) adapters allow data exchange with a PLC aka.
> "Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)

AFAIK, "fieldbus" is a bit broader term than just the typical IEC61158-
style remote memory stuff. At least that's the terminology my clients
tend to use (eg. they also call some homebbrewn rs485-based protocols
"fieldbus").

Maybe it would be better calling it "IEC-61158" instead of "fieldbus" ?


By the way: can this device be opened/used by several processes
simultaniously ?


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-15 17:41 ` [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Enrico Weigelt, metux IT consult
@ 2019-04-15 18:31   ` Sven Van Asbroeck
  2019-04-16 16:49     ` Enrico Weigelt, metux IT consult
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-15 18:31 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

Hi Enrico,

Thanks for your input, appreciate it !

On Mon, Apr 15, 2019 at 1:41 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
> AFAIK, "fieldbus" is a bit broader term than just the typical IEC61158-
> style remote memory stuff. At least that's the terminology my clients
> tend to use (eg. they also call some homebbrewn rs485-based protocols
> "fieldbus").
>
> Maybe it would be better calling it "IEC-61158" instead of "fieldbus" ?
>

Yes, we are certainly open to that, if it is more correct and/or better
accepted by users.

>
> By the way: can this device be opened/used by several processes
> simultaniously ?
>

Yes. You can open as many handles to the device as you like,
they will all share the fieldbus memory. When the remote
fieldbus memory changes, that event will be broadcast to
all open handles, via poll/select.

Of course if your application is monitoring the fieldbus memory
in more than one thread/process, you may need to guard
against race conditions. There is no guarantee which
thread/process will react to the change first.

Sven

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

* Re: [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding
  2019-04-10 13:11           ` Sven Van Asbroeck
@ 2019-04-16 11:11             ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2019-04-16 11:11 UTC (permalink / raw)
  To: Sven Van Asbroeck; +Cc: Rob Herring, linux-kernel, netdev

On Wed, Apr 10, 2019 at 09:11:03AM -0400, Sven Van Asbroeck wrote:
> On Tue, Apr 9, 2019 at 5:10 PM Rob Herring <robh+dt@kernel.org> wrote:
> >
> > No. If you send again, then add my R-by if it hasn't changed.
> 
> Thank you.
> 
> Greg, are you able to apply Rob's R-by tag if you merge this set as-is,
> or would you like me to repost as v11 ?

I can add it, thanks.

greg k-h

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

* Re: [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem.
  2019-04-09 14:42 ` [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem Sven Van Asbroeck
@ 2019-04-16 11:13   ` Greg KH
  0 siblings, 0 replies; 34+ messages in thread
From: Greg KH @ 2019-04-16 11:13 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: robh+dt, linus.walleij, lee.jones, mark.rutland, afaerber,
	treding, david, noralf, johan, monstr, michal.vokac, arnd,
	john.garry, geert+renesas, robin.murphy, paul.gortmaker,
	sebastien.bourdelin, icenowy, stuyoder, jan.kiszka,
	maxime.ripard, linux-kernel, netdev

On Tue, Apr 09, 2019 at 10:42:44AM -0400, Sven Van Asbroeck wrote:
> Fieldbus device (client) adapters allow data exchange with a PLC aka.
> "Fieldbus Controller" over a fieldbus (Profinet, FLNet, etc.)
> 
> They are typically used when a Linux device wants to expose itself
> as an actuator, motor, console light, switch, etc. over the fieldbus.
> 
> This framework is designed to provide a generic interface to Fieldbus
> Devices from both the Linux Kernel and the userspace.
> 
> Signed-off-by: Sven Van Asbroeck <TheSven73@gmail.com>
> ---
>  Documentation/ABI/testing/fieldbus-dev-cdev   |  31 ++
>  .../ABI/testing/sysfs-class-fieldbus-dev      |  62 ++++
>  Documentation/fieldbus_dev/fieldbus_dev.txt   |  66 ++++
>  drivers/staging/Kconfig                       |   2 +
>  drivers/staging/Makefile                      |   1 +
>  drivers/staging/fieldbus/Kconfig              |  19 +
>  drivers/staging/fieldbus/Makefile             |   9 +
>  drivers/staging/fieldbus/dev_core.c           | 349 ++++++++++++++++++
>  include/linux/fieldbus_dev.h                  | 106 ++++++

Please keep all staging code self-contained within your drivers/staging/
directory.

For the dt stuff, as that has been acked, it can go into the "real"
locations.  But for the include and sysfs documentation stuff, that all
needs to stay within your directory.

Can you respin this series based on that and resend?

thanks,

greg k-h

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-15 18:31   ` Sven Van Asbroeck
@ 2019-04-16 16:49     ` Enrico Weigelt, metux IT consult
  2019-04-16 18:56       ` Sven Van Asbroeck
  2019-04-17 17:04       ` Andreas Färber
  0 siblings, 2 replies; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-16 16:49 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 15.04.19 20:31, Sven Van Asbroeck wrote:

Hi,

>> Maybe it would be better calling it "IEC-61158" instead of "fieldbus" ?>>> > Yes, we are certainly open to that, if it is more correct and/or
better> accepted by users.
Thanks, I'd really appreciate that :)

Maybe I'm a bit beaurocratic here, but I really believe that precise
naming is important, eg. for avoiding potential conflicts w/ different
fieldbus classes (eg. mvb) that might come in the future.

> Yes. You can open as many handles to the device as you like,
> they will all share the fieldbus memory. When the remote
> fieldbus memory changes, that event will be broadcast to
> all open handles, via poll/select.

Great. When I read your first mail, I got reminded on the old legacy
canbus chardevs (before cansocket came in) that was single-user only.

By the way: any special reason for doing this via device instead of
socket (like we have w/ can) ?

I'm, personally, pretty undecided which way is better. Device nodes give
us easy access control via fs permissions, while socket allows
firewalling.


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 16:49     ` Enrico Weigelt, metux IT consult
@ 2019-04-16 18:56       ` Sven Van Asbroeck
  2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
  2019-04-17 17:04       ` Andreas Färber
  1 sibling, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-16 18:56 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On Tue, Apr 16, 2019 at 12:49 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
>
> Maybe I'm a bit beaurocratic here, but I really believe that precise
> naming is important, eg. for avoiding potential conflicts w/ different
> fieldbus classes (eg. mvb) that might come in the future.

I named the subsystem 'fieldbus_dev' because it handles fieldbus
devices, not controllers. Granted, it's in the drivers/staging/fieldbus
directory, but that's because people objected to the verbosity of
fieldbus_dev. In the C code, it's all fieldbus_dev.

>
> By the way: any special reason for doing this via device instead of
> socket (like we have w/ can) ?
>

A fieldbus_dev device communicates with its controller through a section
of shared memory (process memory). It just felt more logical to model
this as a device which acts like a file. When you read/write the file,
you read/write the process memory underneath.

Sven

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 18:56       ` Sven Van Asbroeck
@ 2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
  2019-04-16 21:49           ` Sven Van Asbroeck
                             ` (2 more replies)
  0 siblings, 3 replies; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-16 21:18 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 16.04.19 20:56, Sven Van Asbroeck wrote:

>> By the way: any special reason for doing this via device instead of
>> socket (like we have w/ can) ?
>>
> 
> A fieldbus_dev device communicates with its controller through a section
> of shared memory (process memory). It just felt more logical to model
> this as a device which acts like a file. When you read/write the file,
> you read/write the process memory underneath.

Yeah, that sounds logical. By the way: could we also mmap() that device?

Okay that's just a purely academical idea here, but I'm curious whether
that would make a notable difference in performance.

Yet another question: does each fieldbus_dev instance talk to exactly
one plc process memory, or can there be many ?

Having separate device nodes per plc process memory seems a good idea
for finer access control (via chown+friends).


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
@ 2019-04-16 21:49           ` Sven Van Asbroeck
  2019-04-16 22:12           ` Sven Van Asbroeck
  2019-04-17 14:37           ` Sven Van Asbroeck
  2 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-16 21:49 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On Tue, Apr 16, 2019 at 5:21 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
> Yeah, that sounds logical. By the way: could we also mmap() that device?

AFAIK mmap only makes sense if you have actual device memory, accessible
through some memory controller. The process memory on anybus-s devices
is accessed through many layers of indirection, which includes a handshaking/
synchronization protocol over anybus-s.

In addition, you'd lose timing information about writes. The current API blocks
until the write to the remote PLC's process memory has been
successfully completed.

Perhaps if someone ever adds a fieldbus_dev driver for a device with mappable
process memory, we can consider mmap() as an optional extension ?

>
> Okay that's just a purely academical idea here, but I'm curious whether
> that would make a notable difference in performance.

Perhaps on a device with memory-mappable process memory.
But it would have to be an incredibly fast fieldbus device for the
difference between read()/write() and mmap() to matter...

>
> Yet another question: does each fieldbus_dev instance talk to exactly
> one plc process memory, or can there be many ?
>
> Having separate device nodes per plc process memory seems a good idea
> for finer access control (via chown+friends).
>

Good question ! I'll have to get back to you on that...

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
  2019-04-16 21:49           ` Sven Van Asbroeck
@ 2019-04-16 22:12           ` Sven Van Asbroeck
  2019-04-18  8:32             ` Enrico Weigelt, metux IT consult
  2019-04-17 14:37           ` Sven Van Asbroeck
  2 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-16 22:12 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On Tue, Apr 16, 2019 at 5:21 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
>
> Yet another question: does each fieldbus_dev instance talk to exactly
> one plc process memory, or can there be many ?
>
> Having separate device nodes per plc process memory seems a good idea
> for finer access control (via chown+friends).
>

PS do you have a specific fieldbus device in mind which could be added
to Linux? Would it support multiple plc process memories?

Sven

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
  2019-04-16 21:49           ` Sven Van Asbroeck
  2019-04-16 22:12           ` Sven Van Asbroeck
@ 2019-04-17 14:37           ` Sven Van Asbroeck
  2 siblings, 0 replies; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-17 14:37 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On Tue, Apr 16, 2019 at 5:21 PM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
> Yet another question: does each fieldbus_dev instance talk to exactly
> one plc process memory, or can there be many ?

I'm by no means a fieldbus expert, so I had a little chat with one of
the fieldbus
people in the company here.

AFAIK if multiple plcs connect to the same device, all of them 'see' the same
process memory for that device. Any mechanism to prevent these plcs from
racing with each other is implemented in the application software. Could
be handshaking, could be each plc dealing with a separate section of
the process memory.

So for a fieldbus *device* (which is what this subsystem deals with) there can
only be a single process memory. So we have a single devnode per device.

A fieldbus *controller* would have one process memory per device it connects to.
But this subsystem does not deal with controllers.

This is my understanding, but I'd appreciate your input if I have overlooked
something.

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 16:49     ` Enrico Weigelt, metux IT consult
  2019-04-16 18:56       ` Sven Van Asbroeck
@ 2019-04-17 17:04       ` Andreas Färber
  2019-04-17 20:13         ` Enrico Weigelt, metux IT consult
  1 sibling, 1 reply; 34+ messages in thread
From: Andreas Färber @ 2019-04-17 17:04 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Sven Van Asbroeck, Rob Herring, Linus Walleij, Lee Jones,
	mark.rutland, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

Hi Enrico,

Am 16.04.19 um 18:49 schrieb Enrico Weigelt, metux IT consult:
> On 15.04.19 20:31, Sven Van Asbroeck wrote:
>>> Maybe it would be better calling it "IEC-61158" instead of "fieldbus" ?>>> > Yes, we are certainly open to that, if it is more correct and/or
> better> accepted by users.
> Thanks, I'd really appreciate that :)
> 
> Maybe I'm a bit beaurocratic here, but I really believe that precise
> naming is important, eg. for avoiding potential conflicts w/ different
> fieldbus classes (eg. mvb) that might come in the future.

I somewhat see your point, but I would not have recognized iec61158 as
something relevant to my hardware, whereas fieldbus is understandable.

If you see specific conflicts or differences, please explain them
instead of just throwing around bus names. :) Then we can more easily
discuss whether to make changes to this framework or whether we indeed
need some fieldbus/iec61158/ subdirectory. For your RS-485 I don't see a
conflict as that'll just go via tty/serial/ and optionally serdev, no?
However, I'd be curious how I/O Link might relate to this, it seems to
have no public specifications.

> By the way: any special reason for doing this via device instead of
> socket (like we have w/ can) ?
> 
> I'm, personally, pretty undecided which way is better. Device nodes give
> us easy access control via fs permissions, while socket allows
> firewalling.

While I do like sockets, they seem more useful for packet-based
communication, which may be an implementation detail of fieldbus_dev
drivers, but AFAIU that's unrelated to Sven's memory-focused subsystem
representing a view of the data received, which may be different from
the last packet received. Also, when a packet is received via socket, it
gets dequeued, whereas you'll want to access the device's memory without
restrictions.

Cheers,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-17 17:04       ` Andreas Färber
@ 2019-04-17 20:13         ` Enrico Weigelt, metux IT consult
  0 siblings, 0 replies; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-17 20:13 UTC (permalink / raw)
  To: Andreas Färber
  Cc: Sven Van Asbroeck, Rob Herring, Linus Walleij, Lee Jones,
	mark.rutland, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 17.04.19 19:04, Andreas Färber wrote:

Hi,

> If you see specific conflicts or differences, please explain them> instead of just throwing around bus names. :) Then we can more easily>
discuss whether to make changes to this framework or whether we indeed>
need some fieldbus/iec61158/ subdirectory.

For example, MVB semantically is more similar to can/canopen or even
SNMP than to iec61158. T-bus is logically more a typically network
interface (similar to 802.3 or HDLC), WTB is actually HDCL via RS485.
KNX has similarities to canopen or SNMP. DALI is somewhat similar
to I2C.

Ergo: there're various fieldbus protocols with entirely different
concepts. Distributed process memory like in iec61158 is just one
of them.

> For your RS-485 I don't see > conflict as that'll just go via tty/serial/ and optionally serdev, no?

That's just layer 0/1. Ontop of that there're various protocols.
Some folks do some ASCII-based protocol ontop of that (eg. elevators,
water plants, etc), others put in HDLC (eg. WTB), some do completely
weird things ;-)

> However, I'd be curious how I/O Link might relate to this, it seems to> have no public specifications.
https://io-link.com/de/Download/Download.php
https://io-link.com/share/Downloads/Spec-Interface/IOL-Interface-Spec_10002_V112_Jul13.pdf

> While I do like sockets, they seem more useful for packet-based> communication, which may be an implementation detail of fieldbus_dev>
drivers, but AFAIU that's unrelated to Sven's memory-focused subsystem>
representing a view of the data received, which may be different from>
the last packet received. Also, when a packet is received via socket,
it> gets dequeued, whereas you'll want to access the device's memory
without> restrictions.
okay, if the device always represents the current process memory, w/o
showing the actual communication, then Sven's approach makes sense.


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-16 22:12           ` Sven Van Asbroeck
@ 2019-04-18  8:32             ` Enrico Weigelt, metux IT consult
  2019-04-18 17:34               ` Sven Van Asbroeck
  0 siblings, 1 reply; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-18  8:32 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: Rob Herring, Linus Walleij, Lee Jones, mark.rutland,
	Andreas Färber, treding, David Lechner, noralf, johan,
	Michal Simek, michal.vokac, Arnd Bergmann, Greg KH, john.garry,
	geert+renesas, robin.murphy, Paul Gortmaker, sebastien.bourdelin,
	icenowy, Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 17.04.19 00:12, Sven Van Asbroeck wrote:

> PS do you have a specific fieldbus device in mind which could be added
> to Linux? 

I actually had some Duagon MVB controllers (Ionia) in the pipeline.
That's stalled for quite some time, as my customer doesn't need the
MVB communication yet, for now just the machine as generic Linux host
w/ ADCs+DIOs.

Can't tell when exactly the MVB issue comes up again, or whether
the device will be connected to WTB instead. But I'm sure either one
will come in a few years.

> Would it support multiple plc process memories?

Not quite. More like SNMP. Theoretically, (parts of) that could be
somehow mapped into a logical process memory, but handling async
signals could be tricky (at least we'd need a mtime for each field
and an change notification mechanism).


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-18  8:32             ` Enrico Weigelt, metux IT consult
@ 2019-04-18 17:34               ` Sven Van Asbroeck
  2019-04-24  9:40                 ` Enrico Weigelt, metux IT consult
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-18 17:34 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult
  Cc: Linus Walleij, Lee Jones, mark.rutland, Andreas Färber,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

Hi Enrico,

On Thu, Apr 18, 2019 at 4:34 AM Enrico Weigelt, metux IT consult
<lkml@metux.net> wrote:
>
> On 17.04.19 00:12, Sven Van Asbroeck wrote:
>
> > PS do you have a specific fieldbus device in mind which could be added
> > to Linux?
>
> I actually had some Duagon MVB controllers (Ionia) in the pipeline.
> That's stalled for quite some time, as my customer doesn't need the
> MVB communication yet, for now just the machine as generic Linux host
> w/ ADCs+DIOs.
>

If you have a fieldbus device you want to add to mainline Linux, and
you wish to piggy-back onto the fieldbus_dev subsystem, then we can
discuss fieldbus API changes/additions/improvements during the
patch review stage.

It's my experience that good userspace APIs evolve from practical
requirements, not top-down theoretical design. The staging/ location
allows us to take an evolutionary approach.

Sven

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-18 17:34               ` Sven Van Asbroeck
@ 2019-04-24  9:40                 ` Enrico Weigelt, metux IT consult
  2019-04-24 10:26                   ` Oliver Hartkopp
  0 siblings, 1 reply; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-24  9:40 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: Linus Walleij, Lee Jones, mark.rutland, Andreas Färber,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 18.04.19 19:34, Sven Van Asbroeck wrote:

> If you have a fieldbus device you want to add to mainline Linux, and> you wish to piggy-back onto the fieldbus_dev subsystem, then we can>
discuss fieldbus API changes/additions/improvements during the> patch
review stage.
With those cases, piggy-backing wouldn't make much sense, as their
semantics is pretty different.

My whole point here was just that it shouldn't be called "fieldbus",
but iec61158 instead.


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-24  9:40                 ` Enrico Weigelt, metux IT consult
@ 2019-04-24 10:26                   ` Oliver Hartkopp
  2019-04-24 11:00                     ` Andreas Färber
  0 siblings, 1 reply; 34+ messages in thread
From: Oliver Hartkopp @ 2019-04-24 10:26 UTC (permalink / raw)
  To: Enrico Weigelt, metux IT consult, Sven Van Asbroeck
  Cc: Linus Walleij, Lee Jones, mark.rutland, Andreas Färber,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

Thanks Enrico!

On 24.04.19 11:40, Enrico Weigelt, metux IT consult wrote:
> On 18.04.19 19:34, Sven Van Asbroeck wrote:
> 
>> If you have a fieldbus device you want to add to mainline Linux, and> you wish to piggy-back onto the fieldbus_dev subsystem, then we can>
> discuss fieldbus API changes/additions/improvements during the> patch
> review stage.
> With those cases, piggy-backing wouldn't make much sense, as their
> semantics is pretty different.
> 
> My whole point here was just that it shouldn't be called "fieldbus",
> but iec61158 instead.
> 

Full ACK!

The Controller Area Network also belongs to the class of field busses 
and has its own networking subsystem in linux/net/can.

So using a 'class' of communication protocols as naming scheme doesn't 
fit IMHO.

Best regards,
Oliver

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-24 10:26                   ` Oliver Hartkopp
@ 2019-04-24 11:00                     ` Andreas Färber
  2019-04-24 11:48                       ` Oliver Hartkopp
  0 siblings, 1 reply; 34+ messages in thread
From: Andreas Färber @ 2019-04-24 11:00 UTC (permalink / raw)
  To: Oliver Hartkopp, Enrico Weigelt, metux IT consult
  Cc: Sven Van Asbroeck, Linus Walleij, Lee Jones, mark.rutland,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

Am 24.04.19 um 12:26 schrieb Oliver Hartkopp:
> Thanks Enrico!
> 
> On 24.04.19 11:40, Enrico Weigelt, metux IT consult wrote:
>> On 18.04.19 19:34, Sven Van Asbroeck wrote:
>>
>>> If you have a fieldbus device you want to add to mainline Linux, and>
>>> you wish to piggy-back onto the fieldbus_dev subsystem, then we can>
>> discuss fieldbus API changes/additions/improvements during the> patch
>> review stage.
>> With those cases, piggy-backing wouldn't make much sense, as their
>> semantics is pretty different.
>>
>> My whole point here was just that it shouldn't be called "fieldbus",
>> but iec61158 instead.
>>
> 
> Full ACK!
> 
> The Controller Area Network also belongs to the class of field busses
> and has its own networking subsystem in linux/net/can.
> 
> So using a 'class' of communication protocols as naming scheme doesn't
> fit IMHO.

And - again - NACK. Calling a subsystem just iec61158 is going to hide
what it is and stand in the way of development of this niche system. I
asked Enrico to come up with a better naming proposal such as having
iec61158 as subfolder to human-readable fieldbus, but I did not see him
coming up with any such new proposals apart from repeating this name.

Also please read Sven's comment again: It you don't like the current
naming you'll need to post follow-up patches, as v11 of this subsystem
has been merged into staging. No complaint about piggy-backing on v10 is
going to change that fact now!

https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fieldbus?h=staging-next

And since we're at it, Enrico's response to me just threw around a bunch
of acronyms instead of explaining which ones have an _actual_ conflict
with this subsystem - my point precisely was that if they use sockets or
ttys then there's no real conflict apart from lots of things classifying
as "fieldbus".

Regards,
Andreas

-- 
SUSE Linux GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Felix Imendörffer, Mary Higgins, Sri Rasiah
HRB 21284 (AG Nürnberg)

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-24 11:00                     ` Andreas Färber
@ 2019-04-24 11:48                       ` Oliver Hartkopp
  2019-04-24 15:10                         ` Sven Van Asbroeck
  0 siblings, 1 reply; 34+ messages in thread
From: Oliver Hartkopp @ 2019-04-24 11:48 UTC (permalink / raw)
  To: Andreas Färber, Enrico Weigelt, metux IT consult
  Cc: Sven Van Asbroeck, Linus Walleij, Lee Jones, mark.rutland,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 24.04.19 13:00, Andreas Färber wrote:
> Am 24.04.19 um 12:26 schrieb Oliver Hartkopp:

>> The Controller Area Network also belongs to the class of field busses
>> and has its own networking subsystem in linux/net/can.
>>
>> So using a 'class' of communication protocols as naming scheme doesn't
>> fit IMHO.
> 
> And - again - NACK. Calling a subsystem just iec61158 is going to hide
> what it is and stand in the way of development of this niche system. I
> asked Enrico to come up with a better naming proposal such as having
> iec61158 as subfolder to human-readable fieldbus, but I did not see him
> coming up with any such new proposals apart from repeating this name.
> 
> Also please read Sven's comment again: It you don't like the current
> naming you'll need to post follow-up patches, as v11 of this subsystem
> has been merged into staging. No complaint about piggy-backing on v10 is
> going to change that fact now!
> 
> https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fieldbus?h=staging-next
> 
> And since we're at it, Enrico's response to me just threw around a bunch
> of acronyms instead of explaining which ones have an _actual_ conflict
> with this subsystem - my point precisely was that if they use sockets or
> ttys then there's no real conflict apart from lots of things classifying
> as "fieldbus".

Ok, I either tried to get through the fieldbus code and the 
documentation from above.

I hopefully got it right by: You implemented a standardized chardev ABI 
that enables a data exchange between userspace and kernel, where other 
drivers can register 'from inside the kernel', that feel like being a 
fieldbus controller?!?

When checking out the Anybus website the first hit for serial fieldbus 
is https://www.anybus.com/technologies/fieldbus-serial/can-canopen .

CANopen is CAN based - and the CAN network drivers and CAN controllers 
definitely do not expose sysfs entries like

	/sys/class/fieldbus_dev/fieldbus_devX/write_area_size

How can this fit together for me and what would be the plan to cover 
CANopen or EtherCAT in the fieldbus subsystem? Does it need some kind of 
CANopen protocol driver that registers to your ABI and speaks to CAN 
interfaces on the other side?

If this would have been outlined more clearly I wouldn't have stumbled 
over the fieldbus naming scheme.

Regards,
Oliver

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-24 11:48                       ` Oliver Hartkopp
@ 2019-04-24 15:10                         ` Sven Van Asbroeck
  2019-04-29 13:54                           ` Enrico Weigelt, metux IT consult
  0 siblings, 1 reply; 34+ messages in thread
From: Sven Van Asbroeck @ 2019-04-24 15:10 UTC (permalink / raw)
  To: Oliver Hartkopp
  Cc: Andreas Färber, Enrico Weigelt, metux IT consult,
	Linus Walleij, Lee Jones, mark.rutland, treding, David Lechner,
	noralf, johan, Michal Simek, michal.vokac, Arnd Bergmann,
	Greg KH, john.garry, geert+renesas, robin.murphy, Paul Gortmaker,
	sebastien.bourdelin, icenowy, Stuart Yoder, J. Kiszka,
	maxime.ripard, Linux Kernel Mailing List, netdev

Hello Oliver, thank you for your input, appreciate it !

On Wed, Apr 24, 2019 at 7:50 AM Oliver Hartkopp <socketcan@hartkopp.net> wrote:
>
> I hopefully got it right by: You implemented a standardized chardev ABI
> that enables a data exchange between userspace and kernel, where other
> drivers can register 'from inside the kernel', that feel like being a
> fieldbus controller?!?

Close ! Devices that "feel like" a Fieldbus _device_ can register.

The subsystem is called fieldbus_dev "fieldbus device" because it
abstracts Linux fieldbus clients that want to expose themselves as
e.g. an actuator, motor, console light, switch, ...

The commit message fully describes this, and so does the
Documentation. Any improvements are welcome.
https://lkml.org/lkml/2019/4/16/817
https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging.git/tree/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt?h=staging-next

During one of the eleven review cycles, drivers/fieldbus_dev got
truncated to drivers/fieldbus because the reviewers felt that
_dev was redundant, given the lack of other fieldbus
subsystems.

This subsystem came about because I'm trying to mainline
support for three HMS cards: FLNet, Profinet and CC-Link.
These cards are not controllers, but slaves on the bus.
All three make heavy use of process memory. From the
get-go, the kernel reviewers made it clear that the userspace
cannot be specific and must be generic. So I crafted a generic
ABI that works for clients on process-memory based busses,
and called it 'fieldbus_dev', for lack of a better name.

I'm by no means a fieldbus expert. It seems that the term
'fieldbus' is much broader than these process-memory based
standards? I am open to any _concrete_ naming suggestions
that can get consensus.

I'm a bit confused by Wikipedia's entry for fieldbus.
It suggests that IEC 61158 and Fieldbus are
interchangeable?
https://en.wikipedia.org/wiki/Fieldbus

<quote>
Fieldbus is the name of a family of industrial computer
network protocols used for real-time distributed control,
standardized as IEC 61158.
</quote>

>
> How can this fit together for me and what would be the plan to cover
> CANopen or EtherCAT in the fieldbus subsystem? Does it need some kind of
> CANopen protocol driver that registers to your ABI and speaks to CAN
> interfaces on the other side?

Given that CAN/EtherCAT are not process memory based
(that I know of), the fieldbus_dev subsystem is probably
not a good fit.

Also no good for bus controllers, the subsystem deals
with slave devices only.

Sven

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
                   ` (7 preceding siblings ...)
  2019-04-15 17:41 ` [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Enrico Weigelt, metux IT consult
@ 2019-04-25  8:26 ` Lee Jones
  8 siblings, 0 replies; 34+ messages in thread
From: Lee Jones @ 2019-04-25  8:26 UTC (permalink / raw)
  To: Sven Van Asbroeck
  Cc: robh+dt, linus.walleij, mark.rutland, afaerber, treding, david,
	noralf, johan, monstr, michal.vokac, arnd, gregkh, john.garry,
	geert+renesas, robin.murphy, paul.gortmaker, sebastien.bourdelin,
	icenowy, stuyoder, jan.kiszka, maxime.ripard, linux-kernel,
	netdev

On Tue, 09 Apr 2019, Sven Van Asbroeck wrote:

> This patch:
>   1. adds a Fieldbus subsystem
>   2. adds support for the HMS Industrial Networks AB Profinet card.
> 
> 1. Fieldbus subsystem
> ---------------------

Not sure how I ended up on the review list for this set, but could you
please remove me.  Thank you.

-- 
Lee Jones [李琼斯]
Linaro Services Technical Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog

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

* Re: [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card
  2019-04-24 15:10                         ` Sven Van Asbroeck
@ 2019-04-29 13:54                           ` Enrico Weigelt, metux IT consult
  0 siblings, 0 replies; 34+ messages in thread
From: Enrico Weigelt, metux IT consult @ 2019-04-29 13:54 UTC (permalink / raw)
  To: Sven Van Asbroeck, Oliver Hartkopp
  Cc: Andreas Färber, Linus Walleij, Lee Jones, mark.rutland,
	treding, David Lechner, noralf, johan, Michal Simek,
	michal.vokac, Arnd Bergmann, Greg KH, john.garry, geert+renesas,
	robin.murphy, Paul Gortmaker, sebastien.bourdelin, icenowy,
	Stuart Yoder, J. Kiszka, maxime.ripard,
	Linux Kernel Mailing List, netdev

On 24.04.19 17:10, Sven Van Asbroeck wrote:

> The subsystem is called fieldbus_dev "fieldbus device" because it> abstracts Linux fieldbus clients that want to expose themselves as>
e.g. an actuator, motor, console light, switch, ...
Sounds a bit confusing. With that description, I'd expect highlevel
interfaces similar to LED, input, IIO, etc ... but you're actually
implementing an distributed process memory system. This in turn is
just a subset of the fieldbus world.

> During one of the eleven review cycles, drivers/fieldbus_dev got> truncated to drivers/fieldbus because the reviewers felt that> _dev
was redundant, given the lack of other fieldbus> subsystems.
There is at least one: CAN. Sometimes CAN is used in the IEC61158-way,
but also completely different, even both in combination.

> These cards are not controllers, but slaves on the bus.

Do they really implement the process memory part or just the lower
layer communications ?

> I'm by no means a fieldbus expert. It seems that the term> 'fieldbus' is much broader than these process-memory based> standards?

Yes, indeed.

> I am open to any _concrete_ naming suggestion
> that can get consensus.
Maybe IEC61158 ?

> I'm a bit confused by Wikipedia's entry for fieldbus.
> It suggests that IEC 61158 and Fieldbus are
> interchangeable?
> https://en.wikipedia.org/wiki/Fieldbus

That's wrong.

> <quote>
> Fieldbus is the name of a family of industrial computer
> network protocols used for real-time distributed control,
> standardized as IEC 61158.
> </quote>

IEC 61158 only standardizes one particular approach: the distributed
process memory.

> Given that CAN/EtherCAT are not process memory based
> (that I know of), the fieldbus_dev subsystem is probably
> not a good fit.

ACK. Neither are MVB+friends.


--mtx

-- 
Enrico Weigelt, metux IT consult
Free software and Linux embedded engineering
info@metux.net -- +49-151-27565287

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

end of thread, other threads:[~2019-04-29 13:56 UTC | newest]

Thread overview: 34+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-04-09 14:42 [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Sven Van Asbroeck
2019-04-09 14:42 ` [PATCH v10 1/7] fieldbus_dev: add Fieldbus Device subsystem Sven Van Asbroeck
2019-04-16 11:13   ` Greg KH
2019-04-09 14:42 ` [PATCH v10 2/7] anybus-s: support HMS Anybus-S bus Sven Van Asbroeck
2019-04-09 14:42 ` [PATCH v10 3/7] anybus-s: support the Arcx anybus controller Sven Van Asbroeck
2019-04-09 14:42 ` [PATCH v10 4/7] dt-bindings: anybus-controller: document devicetree binding Sven Van Asbroeck
2019-04-09 15:50   ` Rob Herring
2019-04-09 17:16     ` Sven Van Asbroeck
2019-04-09 17:31       ` Sven Van Asbroeck
2019-04-09 21:09         ` Rob Herring
2019-04-10 13:11           ` Sven Van Asbroeck
2019-04-16 11:11             ` Greg KH
2019-04-09 14:42 ` [PATCH v10 5/7] dt-bindings: Add vendor prefix for arcx / Archronix Sven Van Asbroeck
2019-04-09 14:42 ` [PATCH v10 6/7] fieldbus_dev: support HMS Profinet IRT industrial controller Sven Van Asbroeck
2019-04-09 14:42 ` [PATCH v10 7/7] staging: fieldbus_dev: add TODO Sven Van Asbroeck
2019-04-15 17:41 ` [PATCH v10 0/7] Add Fieldbus subsystem + support HMS Profinet card Enrico Weigelt, metux IT consult
2019-04-15 18:31   ` Sven Van Asbroeck
2019-04-16 16:49     ` Enrico Weigelt, metux IT consult
2019-04-16 18:56       ` Sven Van Asbroeck
2019-04-16 21:18         ` Enrico Weigelt, metux IT consult
2019-04-16 21:49           ` Sven Van Asbroeck
2019-04-16 22:12           ` Sven Van Asbroeck
2019-04-18  8:32             ` Enrico Weigelt, metux IT consult
2019-04-18 17:34               ` Sven Van Asbroeck
2019-04-24  9:40                 ` Enrico Weigelt, metux IT consult
2019-04-24 10:26                   ` Oliver Hartkopp
2019-04-24 11:00                     ` Andreas Färber
2019-04-24 11:48                       ` Oliver Hartkopp
2019-04-24 15:10                         ` Sven Van Asbroeck
2019-04-29 13:54                           ` Enrico Weigelt, metux IT consult
2019-04-17 14:37           ` Sven Van Asbroeck
2019-04-17 17:04       ` Andreas Färber
2019-04-17 20:13         ` Enrico Weigelt, metux IT consult
2019-04-25  8:26 ` Lee Jones

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).