lkml.org 
[lkml]   [2015]   [Jul]   [28]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
/
Date
From
SubjectRe: [RFC] Generic VME UIO
Hi Dmitry,

On 22/07/15 19:09, Dmitry Kalinkin wrote:
> Linux kernel has supported VME bus since 2009. The support comes in a form
> of kernel driver API that is backed by a couple drivers for PCI-VME
> bridges. There is also a vme_user driver that provides a generic userpsace
> interface to do data transfer and generate interrupts on the bus. Due to
> specifics of the VME bus, this interface doesn't fit into the UIO
> framework. The other useful feature is to be able to receive VME interrupts
> in the userspace can, on the other hand, benefit from reusing UIO.
>
> VME bus offers seven interrupt request lines IRQ1-IRQ7 corresponding to
> seven interrupt levels. In the event of interrupt, the interrupt handler
> board is to prioritize interrupts in accordance to their levels. The
> interrupt handler then takes ownership over the data bus to perform an
> interrupt acknowledge cycle where it supplies an interrupt level to be
> acknowledged. When multiple interrupters are producing interrupt at the
> same level, only one interrupt gets acknowledged based on interrupters
> position in IACKIN/IACKOUT daisy- chain. The response of the interrupter
> to a relevant interrupt will contain a 8, 16 or 32 bit interrupt vector
> (also called STATUS/ID). After the interrupt acknowledge cycle, the
> interrupter is to remove it's interrupt request from the bus. Such
> standartized scheme is called "Release On AcKnowledge" (ROAK).
>
> Like PCI, VME has it's own corner case where interrupt request is removed
> on VME device register access. VME specification acknowledges this
> behaviour and calls it "Release On Register Access" (RORA) and requires
> RORA devices to still provide interrupt vector value in acknowledge cycles.
> I'm not aware how widespread the RORA devices are.
>

From memory all the hardware I've seen is RORA, but from talking to
you, it seems you've had the opposite experience, so I suspect it might
be quite market dependent.

> The driver below provides a generic userspace interface to handle ROAK VME
> device interrupts.
>
> The user is to enable interrupt vectors through a sysfs interface. For
> example, enabling handler for interrupt vector 0x6b at the level 1 will
> look like:
>
> echo 1 > /sys/bus/vme/devices/vme_uio.0-0/irq/1/6b/enabled
>
> A separate UIO device is created for each handler. Waiting for event can be
> done as:
>
> dd if=/dev/uio0 bs=4 count=1
>
> In response for this RFC I would like to hear your comments or suggestions
> on the proposed sysfs interface, about the idea in general. Some tips on
> how to better handle kobject cleanup are also very welcome.
>

Looks like a good start to me, please provide some documentation under
Documentation/ before submitting (and please clearly state that it will
only work for ROAK hardware!).

> The vme_uio driver is provided separately for the ease of review, it's code
> is intended for the merge into vme_user.
>
> Signed-off-by: Dmitry Kalinkin <dmitry.kalinkin@gmail.com>
> Cc: Igor Alekseev <igor.alekseev@itep.ru>
>
> ---
> drivers/staging/vme/devices/Kconfig | 10 +++
> drivers/staging/vme/devices/Makefile | 1 +
> drivers/staging/vme/devices/vme_uio.c | 158 ++++++++++++++++++++++++++++++++++
> drivers/vme/vme_bridge.h | 4 +-
> include/linux/vme.h | 3 +
> 5 files changed, 175 insertions(+), 1 deletion(-)
> create mode 100644 drivers/staging/vme/devices/vme_uio.c
>
> diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig
> index 1d2ff0c..0300226 100644
> --- a/drivers/staging/vme/devices/Kconfig
> +++ b/drivers/staging/vme/devices/Kconfig
> @@ -1,5 +1,15 @@
> comment "VME Device Drivers"
>
> +config VME_UIO
> + tristate "VME UIO user space access driver"
> + depends on STAGING && VME_BUS && UIO
> + help
> + Say Y here to include UIO interface to VME. This module currently
> + allows you to deliver VME interrupts to user space.
> +
> + To compile this driver as a module, choose M here. The module will
> + be called vme_uio. If unsure, say N.
> +
> config VME_USER
> tristate "VME user space access driver"
> depends on STAGING
> diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile
> index 172512c..c198004 100644
> --- a/drivers/staging/vme/devices/Makefile
> +++ b/drivers/staging/vme/devices/Makefile
> @@ -2,6 +2,7 @@
> # Makefile for the VME device drivers.
> #
>
> +obj-$(CONFIG_VME_UIO) += vme_uio.o
> obj-$(CONFIG_VME_USER) += vme_user.o
>
> vme_pio2-objs := vme_pio2_cntr.o vme_pio2_gpio.o vme_pio2_core.o
> diff --git a/drivers/staging/vme/devices/vme_uio.c b/drivers/staging/vme/devices/vme_uio.c
> new file mode 100644
> index 0000000..4c55a23
> --- /dev/null
> +++ b/drivers/staging/vme/devices/vme_uio.c
> @@ -0,0 +1,158 @@
> +#include <linux/device.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/uio_driver.h>
> +#include <linux/vme.h>
> +
> +static void vme_uio_int(int level, int status_id, void *priv)
> +{
> + struct uio_info *info = priv;
> +
> + uio_event_notify(info);
> +}
> +
> +struct int_sysfs_entry {
> + struct kobj_attribute kobj_attr;
> + struct vme_dev *vdev;
> + struct uio_info uio;
> + int level;
> + int statid;
> + int enabled;
> +};
> +
> +static ssize_t int_enabled_show(struct kobject *kobj,
> + struct kobj_attribute *attr, char *buf)
> +{
> + struct int_sysfs_entry *entry;
> +
> + entry = container_of(attr, struct int_sysfs_entry, kobj_attr);
> + return sprintf(buf, "%d\n", entry->enabled);
> +}
> +
> +static ssize_t int_enabled_store(struct kobject *kobj,
> + struct kobj_attribute *attr, const char *buf,
> + size_t count)
> +{
> + int enabled;
> + struct int_sysfs_entry *entry;
> + int ret;
> +
> + entry = container_of(attr, struct int_sysfs_entry, kobj_attr);
> +
> + ret = kstrtoint(buf, 0, &enabled);
> + if (ret)
> + return ret;
> + enabled = !!enabled;
> +
> + if (enabled == entry->enabled)
> + return count;
> +
> + if (enabled) {
> + ret = uio_register_device(&entry->vdev->dev, &entry->uio);
> + if (ret) {
> + enabled = 0;
> + return ret;
> + }
> +
> + ret = vme_irq_request(entry->vdev, entry->level, entry->statid,
> + vme_uio_int, &entry->uio);
> + if (ret) {

You need a uio_unregister_device() here, or create a exit path and jump
to it with goto (which could also be used for disabling an interrupt I
suppose).

> + enabled = 0;
> + return ret;
> + }
> + } else {
> + vme_irq_free(entry->vdev, entry->level, entry->statid);
> +
> + uio_unregister_device(&entry->uio);
> + }
> +
> + entry->enabled = enabled;
> +
> + return count;
> +}
> +
> +static struct kobj_attribute int_enabled_attribute =
> + __ATTR(enabled, 0644, int_enabled_show, int_enabled_store);
> +
> +static int vme_uio_match(struct vme_dev *vdev)
> +{
> + return 1;
> +}
> +
> +static int vme_uio_probe(struct vme_dev *vdev)
> +{
> + int ret, level, statid;
> +
> + int bus_num = vme_bus_num(vdev);
> +
> + struct kobject *kobj = kobject_create_and_add("irq", &vdev->dev.kobj);
> +
> + for (level = 1; level <= 7; level++) {
> + char *level_node_name = kasprintf(GFP_KERNEL, "%d", level);
> + struct kobject *level_node = kobject_create_and_add(
> + level_node_name, kobj);
> + if (!level_node)
> + return -ENOMEM;
> +
> + for (statid = 0; statid < VME_NUM_STATUSID; statid++) {
> + char *statid_node_name = kasprintf(GFP_KERNEL,
> + "%02x", statid);
> + struct kobject *statid_node;
> + struct int_sysfs_entry *entry;
> +
> + statid_node = kobject_create_and_add(statid_node_name,
> + level_node);
> + if (!statid_node)
> + return -ENOMEM;
> +
> + entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> + if (!entry)
> + return -ENOMEM;
> + entry->uio.name = kasprintf(GFP_KERNEL,
> + "vme_irq_%d_%d_%02x",
> + bus_num, level, statid);
> + entry->uio.version = "1";
> + entry->uio.irq = UIO_IRQ_CUSTOM;
> + entry->level = level;
> + entry->statid = statid;
> + entry->vdev = vdev;
> + entry->enabled = 0;
> + entry->kobj_attr = int_enabled_attribute;
> + ret = sysfs_create_file(statid_node,
> + &entry->kobj_attr.attr);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int vme_uio_remove(struct vme_dev *vdev)
> +{
> + /* XXX Cleanup here */
> + return 0;
> +}
> +
> +static struct vme_driver vme_uio_driver = {
> + .name = "vme_uio",
> + .match = vme_uio_match,
> + .probe = vme_uio_probe,
> + .remove = vme_uio_remove,
> +};
> +
> +static int __init vme_uio_init(void)
> +{
> + return vme_register_driver(&vme_uio_driver, 1);
> +}
> +
> +static void __exit vme_uio_exit(void)
> +{
> + vme_unregister_driver(&vme_uio_driver);
> +}
> +
> +module_init(vme_uio_init);
> +module_exit(vme_uio_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Dmitry Kalinkin <dmitry.kalinkin@gmail.com>");
> diff --git a/drivers/vme/vme_bridge.h b/drivers/vme/vme_bridge.h
> index 37d2fd7..a3ef63b 100644
> --- a/drivers/vme/vme_bridge.h
> +++ b/drivers/vme/vme_bridge.h
> @@ -1,6 +1,8 @@
> #ifndef _VME_BRIDGE_H_
> #define _VME_BRIDGE_H_
>
> +#include <linux/vme.h>
> +
> #define VME_CRCSR_BUF_SIZE (508*1024)
> /*
> * Resource structures
> @@ -88,7 +90,7 @@ struct vme_callback {
>
> struct vme_irq {
> int count;
> - struct vme_callback callback[256];
> + struct vme_callback callback[VME_NUM_STATUSID];
> };
>
> /* Allow 16 characters for name (including null character) */
> diff --git a/include/linux/vme.h b/include/linux/vme.h
> index c013135..71e4a6d 100644
> --- a/include/linux/vme.h
> +++ b/include/linux/vme.h
> @@ -81,6 +81,9 @@ struct vme_resource {
>
> extern struct bus_type vme_bus_type;
>
> +/* Number of VME interrupt vectors */
> +#define VME_NUM_STATUSID 256
> +
> /* VME_MAX_BRIDGES comes from the type of vme_bus_numbers */
> #define VME_MAX_BRIDGES (sizeof(unsigned int)*8)
> #define VME_MAX_SLOTS 32
>

--
Martyn Welch (Lead Software Engineer) | Registered in England and Wales
GE Intelligent Platforms | (3828642) at 100 Barbirolli Square
T +44(0)1327322748 | Manchester, M2 3AB
E martyn.welch@ge.com | VAT:GB 927559189


\
 
 \ /
  Last update: 2015-07-28 19:21    [W:0.055 / U:0.912 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site