diff options
19 files changed, 0 insertions, 2981 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index b609f40e1420..e4c51d40d42b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21930,17 +21930,6 @@ L: linux-media@vger.kernel.org S: Maintained F: drivers/staging/media/atomisp/ -STAGING - FIELDBUS SUBSYSTEM -M: Sven Van Asbroeck <TheSven73@gmail.com> -S: Maintained -F: drivers/staging/fieldbus/* -F: drivers/staging/fieldbus/Documentation/ - -STAGING - HMS ANYBUS-S BUS -M: Sven Van Asbroeck <TheSven73@gmail.com> -S: Maintained -F: drivers/staging/fieldbus/anybuss/ - STAGING - INDUSTRIAL IO M: Jonathan Cameron <jic23@kernel.org> L: linux-iio@vger.kernel.org diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 4018f95a31bc..075e775d3868 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -46,8 +46,6 @@ source "drivers/staging/vc04_services/Kconfig" source "drivers/staging/axis-fifo/Kconfig" -source "drivers/staging/fieldbus/Kconfig" - source "drivers/staging/vme_user/Kconfig" source "drivers/staging/gpib/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index f29d66da02eb..e681e403509c 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -13,5 +13,4 @@ obj-$(CONFIG_MOST) += most/ obj-$(CONFIG_GREYBUS) += greybus/ obj-$(CONFIG_BCM2835_VCHIQ) += vc04_services/ obj-$(CONFIG_XIL_AXIS_FIFO) += axis-fifo/ -obj-$(CONFIG_FIELDBUS_DEV) += fieldbus/ obj-$(CONFIG_GPIB) += gpib/ diff --git a/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev b/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev deleted file mode 100644 index 45f631ea32a6..000000000000 --- a/drivers/staging/fieldbus/Documentation/ABI/fieldbus-dev-cdev +++ /dev/null @@ -1,31 +0,0 @@ -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/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev b/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev deleted file mode 100644 index 439f14d33c3b..000000000000 --- a/drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev +++ /dev/null @@ -1,62 +0,0 @@ -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/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt b/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt deleted file mode 100644 index f34a95611645..000000000000 --- a/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt +++ /dev/null @@ -1,71 +0,0 @@ -* 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/memory-controllers/fsl/fsl,imx-weim.yaml - -&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 */ - }; - }; -}; diff --git a/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt b/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt deleted file mode 100644 index 89fb8e14676f..000000000000 --- a/drivers/staging/fieldbus/Documentation/fieldbus_dev.txt +++ /dev/null @@ -1,66 +0,0 @@ - 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: -drivers/staging/fieldbus/Documentation/ABI/sysfs-class-fieldbus-dev -drivers/staging/fieldbus/Documentation/ABI/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/fieldbus/Kconfig b/drivers/staging/fieldbus/Kconfig deleted file mode 100644 index b0b865acccfb..000000000000 --- a/drivers/staging/fieldbus/Kconfig +++ /dev/null @@ -1,19 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -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. - -source "drivers/staging/fieldbus/anybuss/Kconfig" - diff --git a/drivers/staging/fieldbus/Makefile b/drivers/staging/fieldbus/Makefile deleted file mode 100644 index bdf645d41344..000000000000 --- a/drivers/staging/fieldbus/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for fieldbus_dev drivers. -# - -obj-$(CONFIG_FIELDBUS_DEV) += fieldbus_dev.o anybuss/ -fieldbus_dev-y := dev_core.o diff --git a/drivers/staging/fieldbus/TODO b/drivers/staging/fieldbus/TODO deleted file mode 100644 index 6d6626af4ec7..000000000000 --- a/drivers/staging/fieldbus/TODO +++ /dev/null @@ -1,5 +0,0 @@ -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> diff --git a/drivers/staging/fieldbus/anybuss/Kconfig b/drivers/staging/fieldbus/anybuss/Kconfig deleted file mode 100644 index 635a0a7b7dd2..000000000000 --- a/drivers/staging/fieldbus/anybuss/Kconfig +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -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. - -if HMS_ANYBUSS_BUS - -config ARCX_ANYBUS_CONTROLLER - tristate "Arcx Anybus-S Controller" - depends on OF && GPIOLIB && HAS_IOMEM && REGULATOR - select REGMAP_MMIO - 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. - -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 deleted file mode 100644 index 3ad3dcc6be56..000000000000 --- a/drivers/staging/fieldbus/anybuss/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for anybuss drivers. -# - -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/anybuss-client.h b/drivers/staging/fieldbus/anybuss/anybuss-client.h deleted file mode 100644 index c21c4bebfb84..000000000000 --- a/drivers/staging/fieldbus/anybuss/anybuss-client.h +++ /dev/null @@ -1,95 +0,0 @@ -/* 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> - -/* move to <linux/fieldbus_dev.h> when taking this out of staging */ -#include "../fieldbus_dev.h" - -struct anybuss_host; - -struct anybuss_client { - struct device dev; - struct anybuss_host *host; - __be16 anybus_id; - /* - * 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); - void (*remove)(struct anybuss_client *adev); - u16 anybus_id; -}; - -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); -} - -#define to_anybuss_client_driver(__drv) container_of_const(__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); - -struct anybuss_memcfg { - u16 input_io; - u16 input_dpram; - u16 input_total; - - u16 output_io; - u16 output_dpram; - u16 output_total; - - enum fieldbus_dev_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/drivers/staging/fieldbus/anybuss/anybuss-controller.h b/drivers/staging/fieldbus/anybuss/anybuss-controller.h deleted file mode 100644 index 02fa0749043b..000000000000 --- a/drivers/staging/fieldbus/anybuss/anybuss-controller.h +++ /dev/null @@ -1,47 +0,0 @@ -/* 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__ */ diff --git a/drivers/staging/fieldbus/anybuss/arcx-anybus.c b/drivers/staging/fieldbus/anybuss/arcx-anybus.c deleted file mode 100644 index cda0d2cf84c5..000000000000 --- a/drivers/staging/fieldbus/anybuss/arcx-anybus.c +++ /dev/null @@ -1,379 +0,0 @@ -// 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> - -/* move to <linux/anybuss-controller.h> when taking this out of staging */ -#include "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 const struct regmap_config arcx_regmap_cfg = { - .reg_bits = 16, - .val_bits = 8, - .max_register = 0x7ff, - .use_single_read = true, - .use_single_write = true, - /* - * single-byte parallel bus accesses are atomic, so don't - * require any synchronization. - */ - .disable_locking = true, -}; - -static struct regmap *create_parallel_regmap(struct platform_device *pdev, - int idx) -{ - void __iomem *base; - struct device *dev = &pdev->dev; - - base = devm_platform_ioremap_resource(pdev, idx + 1); - if (IS_ERR(base)) - return ERR_CAST(base); - return devm_regmap_init_mmio(dev, base, &arcx_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(ops.irq); - 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 const 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 const 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 const struct class controller_class = { - .name = "arcx_anybus_controller", -}; - -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 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 */ - cd->cpld_base = devm_platform_ioremap_resource(pdev, 0); - 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_alloc(&controller_index_ida, 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_ida; - } - /* 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_free(&controller_index_ida, id); -out_reset: - gpiod_set_value_cansleep(cd->reset_gpiod, 1); - return err; -} - -static void 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_free(&controller_index_ida, id); - gpiod_set_value_cansleep(cd->reset_gpiod, 1); -} - -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 = controller_of_match, - }, -}; - -static int __init controller_init(void) -{ - int err; - - err = class_register(&controller_class); - if (err) - return err; - err = platform_driver_register(&controller_driver); - if (err) - class_unregister(&controller_class); - - return err; -} - -static void __exit controller_exit(void) -{ - platform_driver_unregister(&controller_driver); - class_unregister(&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"); diff --git a/drivers/staging/fieldbus/anybuss/hms-profinet.c b/drivers/staging/fieldbus/anybuss/hms-profinet.c deleted file mode 100644 index e691736a53f1..000000000000 --- a/drivers/staging/fieldbus/anybuss/hms-profinet.c +++ /dev/null @@ -1,224 +0,0 @@ -// 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> - -/* move to <linux/fieldbus_dev.h> when taking this out of staging */ -#include "../fieldbus_dev.h" - -/* move to <linux/anybuss-client.h> when taking this out of staging */ -#include "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, "%pM\n", response.addr); -} - -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 = FIELDBUS_DEV_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 void profinet_remove(struct anybuss_client *client) -{ - struct profi_priv *priv = anybuss_get_drvdata(client); - - fieldbus_dev_unregister(&priv->fbdev); -} - -static struct anybuss_client_driver profinet_driver = { - .probe = profinet_probe, - .remove = profinet_remove, - .driver = { - .name = "hms-profinet", - .owner = THIS_MODULE, - }, - .anybus_id = 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"); diff --git a/drivers/staging/fieldbus/anybuss/host.c b/drivers/staging/fieldbus/anybuss/host.c deleted file mode 100644 index 4f2b2fce92ee..000000000000 --- a/drivers/staging/fieldbus/anybuss/host.c +++ /dev/null @@ -1,1452 +0,0 @@ -// 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> - -/* move to <linux/anybuss-*.h> when taking this out of staging */ -#include "anybuss-client.h" -#include "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 FIELDBUS_DEV_OFFL_MODE_CLEAR: - op_mode = 0; - break; - case FIELDBUS_DEV_OFFL_MODE_FREEZE: - op_mode = OP_MODE_FBFC; - break; - case FIELDBUS_DEV_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, - const struct device_driver *drv) -{ - const struct anybuss_client_driver *adrv = - to_anybuss_client_driver(drv); - struct anybuss_client *adev = - to_anybuss_client(dev); - - return adrv->anybus_id == be16_to_cpu(adev->anybus_id); -} - -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); - - return adrv->probe(adev); -} - -static void anybus_bus_remove(struct device *dev) -{ - struct anybuss_client_driver *adrv = - to_anybuss_client_driver(dev->driver); - - if (adrv->remove) - adrv->remove(to_anybuss_client(dev)); -} - -static const 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) -{ - if (!drv->probe) - return -ENODEV; - - 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]; - __be16 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: - * a) perform dummy IND_AB read to prevent false 'init done' irq - * (already done by test_dpram() above) - * b) release reset - * c) wait for first interrupt - * d) interrupt came in: ready to go ! - */ - reset_deassert(cd); - if (!wait_for_completion_timeout(&cd->card_boot, TIMEOUT)) { - ret = -ETIMEDOUT; - 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)); - dev_info(dev, "Fieldbus type: %04X", be16_to_cpu(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->anybus_id = 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: - put_device(&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(void *res) -{ - anybuss_host_common_remove(res); -} - -struct anybuss_host * __must_check -devm_anybuss_host_common_probe(struct device *dev, - const struct anybuss_ops *ops) -{ - struct anybuss_host *host; - int ret; - - host = anybuss_host_common_probe(dev, ops); - if (IS_ERR(host)) - return host; - - ret = devm_add_action_or_reset(dev, host_release, host); - if (ret) - return ERR_PTR(ret); - - 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/drivers/staging/fieldbus/dev_core.c b/drivers/staging/fieldbus/dev_core.c deleted file mode 100644 index 0053ebd91442..000000000000 --- a/drivers/staging/fieldbus/dev_core.c +++ /dev/null @@ -1,344 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Fieldbus Device Driver Core - * - */ - -#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> - -/* move to <linux/fieldbus_dev.h> when taking this out of staging */ -#include "fieldbus_dev.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 ssize_t online_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct fieldbus_dev *fb = dev_get_drvdata(dev); - - return sysfs_emit(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 sysfs_emit(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. */ - return sysfs_emit(buf, "%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 sysfs_emit(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 sysfs_emit(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 sysfs_emit(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 = kobj_to_dev(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 const struct class fieldbus_class = { - .name = "fieldbus_dev", - .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 __poll_t fieldbus_poll(struct file *filp, poll_table *wait) -{ - struct fb_open_file *of = filp->private_data; - struct fieldbus_dev *fbdev = of->fbdev; - __poll_t mask = EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; - - poll_wait(filp, &fbdev->dc_wq, wait); - /* data changed ? */ - if (fbdev->dc_event != of->dc_event) - mask |= EPOLLPRI | EPOLLERR; - 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_free(&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_alloc_max(&fieldbus_ida, MAX_FIELDBUSES - 1, 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_free(&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/drivers/staging/fieldbus/fieldbus_dev.h b/drivers/staging/fieldbus/fieldbus_dev.h deleted file mode 100644 index 301dca3b8d71..000000000000 --- a/drivers/staging/fieldbus/fieldbus_dev.h +++ /dev/null @@ -1,114 +0,0 @@ -/* 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, -}; - -enum fieldbus_dev_offl_mode { - FIELDBUS_DEV_OFFL_MODE_CLEAR = 0, - FIELDBUS_DEV_OFFL_MODE_FREEZE, - FIELDBUS_DEV_OFFL_MODE_SET -}; - -/** - * 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 */ |