Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752798AbbG1RDi (ORCPT ); Tue, 28 Jul 2015 13:03:38 -0400 Received: from mx0b-00176a03.pphosted.com ([67.231.157.48]:61075 "EHLO mx0b-00176a03.pphosted.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751420AbbG1RDg (ORCPT ); Tue, 28 Jul 2015 13:03:36 -0400 Message-ID: <55B7B5DB.6050105@ge.com> Date: Tue, 28 Jul 2015 18:03:23 +0100 From: Martyn Welch User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.7.0 MIME-Version: 1.0 To: Dmitry Kalinkin , , CC: Manohar Vanga , Greg Kroah-Hartman , "Hans J. Koch" , "Igor Alekseev" Subject: Re: [RFC] Generic VME UIO References: <1437588546-1855-1-git-send-email-dmitry.kalinkin@gmail.com> In-Reply-To: <1437588546-1855-1-git-send-email-dmitry.kalinkin@gmail.com> Content-Type: text/plain; charset="windows-1252"; format=flowed Content-Transfer-Encoding: 7bit X-Originating-IP: [3.159.212.193] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:,, definitions=2015-07-28_08:,, signatures=0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 spamscore=0 suspectscore=52 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1506180000 definitions=main-1507280257 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 10475 Lines: 322 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 > Cc: Igor Alekseev > > --- > 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 > +#include > +#include > +#include > +#include > + > +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 "); > 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 > + > #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 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/