From e36c9c00188458d4843d68cae947e64d99259b0e Mon Sep 17 00:00:00 2001 From: Bruno Moreira-Guedes Date: Thu, 21 Apr 2022 11:59:34 -0300 Subject: staging: vme: Move 'vme/devices/' to 'vme_user/' In ("Staging: VME: move VME drivers out of staging") the vme code, board and bridge drivers were moved out of the staging tree, remaining only the VME user device driver. Since this driver is the only one remaining in staging, such multi-level structure confuses more than helps. The current structure is as follows: - drivers/staging/vme/ Makefile devices/ Kconfig Makefile vme_user.c vme_user.h The top-level Makefile has the only function of calling another Makefile into the devices/ subdirectory. This latter only compiles the vme_user driver, since there is no other in the staging tree. This patch removes the unnecessary Makefile from the 'vme/' dir, move the contents of 'vme/devices' into the 'vme/' dir, and renames it to 'vme_user/' (the driver name), allowing a straightforward understanding of this driver's contents. Furthermore, it updates the MAINTAINERS file to properly reflect the new paths. Signed-off-by: Bruno Moreira-Guedes Link: https://lore.kernel.org/r/2cd7de9a426c443a5ea618682d605ecfd751d798.1650544175.git.codeagain@codeagain.dev Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 +- drivers/staging/Kconfig | 3 +- drivers/staging/Makefile | 2 +- drivers/staging/vme/Makefile | 2 - drivers/staging/vme/devices/Kconfig | 13 - drivers/staging/vme/devices/Makefile | 6 - drivers/staging/vme/devices/vme_user.c | 780 --------------------------------- drivers/staging/vme/devices/vme_user.h | 57 --- drivers/staging/vme_user/Kconfig | 13 + drivers/staging/vme_user/Makefile | 6 + drivers/staging/vme_user/vme_user.c | 780 +++++++++++++++++++++++++++++++++ drivers/staging/vme_user/vme_user.h | 57 +++ 12 files changed, 859 insertions(+), 862 deletions(-) delete mode 100644 drivers/staging/vme/Makefile delete mode 100644 drivers/staging/vme/devices/Kconfig delete mode 100644 drivers/staging/vme/devices/Makefile delete mode 100644 drivers/staging/vme/devices/vme_user.c delete mode 100644 drivers/staging/vme/devices/vme_user.h create mode 100644 drivers/staging/vme_user/Kconfig create mode 100644 drivers/staging/vme_user/Makefile create mode 100644 drivers/staging/vme_user/vme_user.c create mode 100644 drivers/staging/vme_user/vme_user.h diff --git a/MAINTAINERS b/MAINTAINERS index a62da6d0f943..0a635f2ae5b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -21002,7 +21002,7 @@ L: linux-kernel@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git F: Documentation/driver-api/vme.rst -F: drivers/staging/vme/ +F: drivers/staging/vme_user/ F: drivers/vme/ F: include/linux/vme* diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 42dcb9b7cb60..0a993c47273e 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -84,7 +84,6 @@ source "drivers/staging/fieldbus/Kconfig" source "drivers/staging/qlge/Kconfig" - -source "drivers/staging/vme/devices/Kconfig" +source "drivers/staging/vme_user/Kconfig" endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a17a2aeaceba..2800ab9b2d1d 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -14,7 +14,7 @@ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ obj-$(CONFIG_OCTEON_USB) += octeon-usb/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ -obj-$(CONFIG_VME_BUS) += vme/ +obj-$(CONFIG_VME_BUS) += vme_user/ obj-$(CONFIG_IIO) += iio/ obj-$(CONFIG_FB_SM750) += sm750fb/ obj-$(CONFIG_USB_EMXX) += emxx_udc/ diff --git a/drivers/staging/vme/Makefile b/drivers/staging/vme/Makefile deleted file mode 100644 index cf2f686ccffe..000000000000 --- a/drivers/staging/vme/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-y += devices/ diff --git a/drivers/staging/vme/devices/Kconfig b/drivers/staging/vme/devices/Kconfig deleted file mode 100644 index e8b4461bf27f..000000000000 --- a/drivers/staging/vme/devices/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -comment "VME Device Drivers" - -config VME_USER - tristate "VME user space access driver" - depends on STAGING && VME_BUS - help - If you say Y here you want to be able to access a limited number of - VME windows in a manner at least semi-compatible with the interface - provided with the original driver at . - - To compile this driver as a module, choose M here. The module will - be called vme_user. If unsure, say N. diff --git a/drivers/staging/vme/devices/Makefile b/drivers/staging/vme/devices/Makefile deleted file mode 100644 index 5380115139b0..000000000000 --- a/drivers/staging/vme/devices/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# -# Makefile for the VME device drivers. -# - -obj-$(CONFIG_VME_USER) += vme_user.o diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c deleted file mode 100644 index 859af797630c..000000000000 --- a/drivers/staging/vme/devices/vme_user.c +++ /dev/null @@ -1,780 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * VMEbus User access driver - * - * Author: Martyn Welch - * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. - * - * Based on work by: - * Tom Armistead and Ajit Prem - * Copyright 2004 Motorola Inc. - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "vme_user.h" - -static const char driver_name[] = "vme_user"; - -static int bus[VME_USER_BUS_MAX]; -static unsigned int bus_num; - -/* Currently Documentation/admin-guide/devices.rst defines the - * following for VME: - * - * 221 char VME bus - * 0 = /dev/bus/vme/m0 First master image - * 1 = /dev/bus/vme/m1 Second master image - * 2 = /dev/bus/vme/m2 Third master image - * 3 = /dev/bus/vme/m3 Fourth master image - * 4 = /dev/bus/vme/s0 First slave image - * 5 = /dev/bus/vme/s1 Second slave image - * 6 = /dev/bus/vme/s2 Third slave image - * 7 = /dev/bus/vme/s3 Fourth slave image - * 8 = /dev/bus/vme/ctl Control - * - * It is expected that all VME bus drivers will use the - * same interface. For interface documentation see - * http://www.vmelinux.org/. - * - * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't - * even support the tsi148 chipset (which has 8 master and 8 slave windows). - * We'll run with this for now as far as possible, however it probably makes - * sense to get rid of the old mappings and just do everything dynamically. - * - * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as - * defined above and try to support at least some of the interface from - * http://www.vmelinux.org/ as an alternative the driver can be written - * providing a saner interface later. - * - * The vmelinux.org driver never supported slave images, the devices reserved - * for slaves were repurposed to support all 8 master images on the UniverseII! - * We shall support 4 masters and 4 slaves with this driver. - */ -#define VME_MAJOR 221 /* VME Major Device Number */ -#define VME_DEVS 9 /* Number of dev entries */ - -#define MASTER_MINOR 0 -#define MASTER_MAX 3 -#define SLAVE_MINOR 4 -#define SLAVE_MAX 7 -#define CONTROL_MINOR 8 - -#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */ - -/* - * Structure to handle image related parameters. - */ -struct image_desc { - void *kern_buf; /* Buffer address in kernel space */ - dma_addr_t pci_buf; /* Buffer address in PCI address space */ - unsigned long long size_buf; /* Buffer size */ - struct mutex mutex; /* Mutex for locking image */ - struct device *device; /* Sysfs device */ - struct vme_resource *resource; /* VME resource */ - int mmap_count; /* Number of current mmap's */ -}; - -static struct image_desc image[VME_DEVS]; - -static struct cdev *vme_user_cdev; /* Character device */ -static struct class *vme_user_sysfs_class; /* Sysfs class */ -static struct vme_dev *vme_user_bridge; /* Pointer to user device */ - -static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR, - MASTER_MINOR, MASTER_MINOR, - SLAVE_MINOR, SLAVE_MINOR, - SLAVE_MINOR, SLAVE_MINOR, - CONTROL_MINOR - }; - -struct vme_user_vma_priv { - unsigned int minor; - refcount_t refcnt; -}; - -static ssize_t resource_to_user(int minor, char __user *buf, size_t count, - loff_t *ppos) -{ - ssize_t copied = 0; - - if (count > image[minor].size_buf) - count = image[minor].size_buf; - - copied = vme_master_read(image[minor].resource, image[minor].kern_buf, - count, *ppos); - if (copied < 0) - return (int)copied; - - if (copy_to_user(buf, image[minor].kern_buf, (unsigned long)copied)) - return -EFAULT; - - return copied; -} - -static ssize_t resource_from_user(unsigned int minor, const char __user *buf, - size_t count, loff_t *ppos) -{ - if (count > image[minor].size_buf) - count = image[minor].size_buf; - - if (copy_from_user(image[minor].kern_buf, buf, (unsigned long)count)) - return -EFAULT; - - return vme_master_write(image[minor].resource, image[minor].kern_buf, - count, *ppos); -} - -static ssize_t buffer_to_user(unsigned int minor, char __user *buf, - size_t count, loff_t *ppos) -{ - void *image_ptr; - - image_ptr = image[minor].kern_buf + *ppos; - if (copy_to_user(buf, image_ptr, (unsigned long)count)) - return -EFAULT; - - return count; -} - -static ssize_t buffer_from_user(unsigned int minor, const char __user *buf, - size_t count, loff_t *ppos) -{ - void *image_ptr; - - image_ptr = image[minor].kern_buf + *ppos; - if (copy_from_user(image_ptr, buf, (unsigned long)count)) - return -EFAULT; - - return count; -} - -static ssize_t vme_user_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - unsigned int minor = iminor(file_inode(file)); - ssize_t retval; - size_t image_size; - - if (minor == CONTROL_MINOR) - return 0; - - mutex_lock(&image[minor].mutex); - - /* XXX Do we *really* want this helper - we can use vme_*_get ? */ - image_size = vme_get_size(image[minor].resource); - - /* Ensure we are starting at a valid location */ - if ((*ppos < 0) || (*ppos > (image_size - 1))) { - mutex_unlock(&image[minor].mutex); - return 0; - } - - /* Ensure not reading past end of the image */ - if (*ppos + count > image_size) - count = image_size - *ppos; - - switch (type[minor]) { - case MASTER_MINOR: - retval = resource_to_user(minor, buf, count, ppos); - break; - case SLAVE_MINOR: - retval = buffer_to_user(minor, buf, count, ppos); - break; - default: - retval = -EINVAL; - } - - mutex_unlock(&image[minor].mutex); - if (retval > 0) - *ppos += retval; - - return retval; -} - -static ssize_t vme_user_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - unsigned int minor = iminor(file_inode(file)); - ssize_t retval; - size_t image_size; - - if (minor == CONTROL_MINOR) - return 0; - - mutex_lock(&image[minor].mutex); - - image_size = vme_get_size(image[minor].resource); - - /* Ensure we are starting at a valid location */ - if ((*ppos < 0) || (*ppos > (image_size - 1))) { - mutex_unlock(&image[minor].mutex); - return 0; - } - - /* Ensure not reading past end of the image */ - if (*ppos + count > image_size) - count = image_size - *ppos; - - switch (type[minor]) { - case MASTER_MINOR: - retval = resource_from_user(minor, buf, count, ppos); - break; - case SLAVE_MINOR: - retval = buffer_from_user(minor, buf, count, ppos); - break; - default: - retval = -EINVAL; - } - - mutex_unlock(&image[minor].mutex); - - if (retval > 0) - *ppos += retval; - - return retval; -} - -static loff_t vme_user_llseek(struct file *file, loff_t off, int whence) -{ - unsigned int minor = iminor(file_inode(file)); - size_t image_size; - loff_t res; - - switch (type[minor]) { - case MASTER_MINOR: - case SLAVE_MINOR: - mutex_lock(&image[minor].mutex); - image_size = vme_get_size(image[minor].resource); - res = fixed_size_llseek(file, off, whence, image_size); - mutex_unlock(&image[minor].mutex); - return res; - } - - return -EINVAL; -} - -/* - * The ioctls provided by the old VME access method (the one at vmelinux.org) - * are most certainly wrong as the effectively push the registers layout - * through to user space. Given that the VME core can handle multiple bridges, - * with different register layouts this is most certainly not the way to go. - * - * We aren't using the structures defined in the Motorola driver either - these - * are also quite low level, however we should use the definitions that have - * already been defined. - */ -static int vme_user_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct vme_master master; - struct vme_slave slave; - struct vme_irq_id irq_req; - unsigned long copied; - unsigned int minor = iminor(inode); - int retval; - dma_addr_t pci_addr; - void __user *argp = (void __user *)arg; - - switch (type[minor]) { - case CONTROL_MINOR: - switch (cmd) { - case VME_IRQ_GEN: - copied = copy_from_user(&irq_req, argp, - sizeof(irq_req)); - if (copied) { - pr_warn("Partial copy from userspace\n"); - return -EFAULT; - } - - return vme_irq_generate(vme_user_bridge, - irq_req.level, - irq_req.statid); - } - break; - case MASTER_MINOR: - switch (cmd) { - case VME_GET_MASTER: - memset(&master, 0, sizeof(master)); - - /* XXX We do not want to push aspace, cycle and width - * to userspace as they are - */ - retval = vme_master_get(image[minor].resource, - &master.enable, - &master.vme_addr, - &master.size, &master.aspace, - &master.cycle, &master.dwidth); - - copied = copy_to_user(argp, &master, - sizeof(master)); - if (copied) { - pr_warn("Partial copy to userspace\n"); - return -EFAULT; - } - - return retval; - - case VME_SET_MASTER: - - if (image[minor].mmap_count != 0) { - pr_warn("Can't adjust mapped window\n"); - return -EPERM; - } - - copied = copy_from_user(&master, argp, sizeof(master)); - if (copied) { - pr_warn("Partial copy from userspace\n"); - return -EFAULT; - } - - /* XXX We do not want to push aspace, cycle and width - * to userspace as they are - */ - return vme_master_set(image[minor].resource, - master.enable, master.vme_addr, master.size, - master.aspace, master.cycle, master.dwidth); - - break; - } - break; - case SLAVE_MINOR: - switch (cmd) { - case VME_GET_SLAVE: - memset(&slave, 0, sizeof(slave)); - - /* XXX We do not want to push aspace, cycle and width - * to userspace as they are - */ - retval = vme_slave_get(image[minor].resource, - &slave.enable, &slave.vme_addr, - &slave.size, &pci_addr, - &slave.aspace, &slave.cycle); - - copied = copy_to_user(argp, &slave, - sizeof(slave)); - if (copied) { - pr_warn("Partial copy to userspace\n"); - return -EFAULT; - } - - return retval; - - case VME_SET_SLAVE: - - copied = copy_from_user(&slave, argp, sizeof(slave)); - if (copied) { - pr_warn("Partial copy from userspace\n"); - return -EFAULT; - } - - /* XXX We do not want to push aspace, cycle and width - * to userspace as they are - */ - return vme_slave_set(image[minor].resource, - slave.enable, slave.vme_addr, slave.size, - image[minor].pci_buf, slave.aspace, - slave.cycle); - - break; - } - break; - } - - return -EINVAL; -} - -static long -vme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int ret; - struct inode *inode = file_inode(file); - unsigned int minor = iminor(inode); - - mutex_lock(&image[minor].mutex); - ret = vme_user_ioctl(inode, file, cmd, arg); - mutex_unlock(&image[minor].mutex); - - return ret; -} - -static void vme_user_vm_open(struct vm_area_struct *vma) -{ - struct vme_user_vma_priv *vma_priv = vma->vm_private_data; - - refcount_inc(&vma_priv->refcnt); -} - -static void vme_user_vm_close(struct vm_area_struct *vma) -{ - struct vme_user_vma_priv *vma_priv = vma->vm_private_data; - unsigned int minor = vma_priv->minor; - - if (!refcount_dec_and_test(&vma_priv->refcnt)) - return; - - mutex_lock(&image[minor].mutex); - image[minor].mmap_count--; - mutex_unlock(&image[minor].mutex); - - kfree(vma_priv); -} - -static const struct vm_operations_struct vme_user_vm_ops = { - .open = vme_user_vm_open, - .close = vme_user_vm_close, -}; - -static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma) -{ - int err; - struct vme_user_vma_priv *vma_priv; - - mutex_lock(&image[minor].mutex); - - err = vme_master_mmap(image[minor].resource, vma); - if (err) { - mutex_unlock(&image[minor].mutex); - return err; - } - - vma_priv = kmalloc(sizeof(*vma_priv), GFP_KERNEL); - if (!vma_priv) { - mutex_unlock(&image[minor].mutex); - return -ENOMEM; - } - - vma_priv->minor = minor; - refcount_set(&vma_priv->refcnt, 1); - vma->vm_ops = &vme_user_vm_ops; - vma->vm_private_data = vma_priv; - - image[minor].mmap_count++; - - mutex_unlock(&image[minor].mutex); - - return 0; -} - -static int vme_user_mmap(struct file *file, struct vm_area_struct *vma) -{ - unsigned int minor = iminor(file_inode(file)); - - if (type[minor] == MASTER_MINOR) - return vme_user_master_mmap(minor, vma); - - return -ENODEV; -} - -static const struct file_operations vme_user_fops = { - .read = vme_user_read, - .write = vme_user_write, - .llseek = vme_user_llseek, - .unlocked_ioctl = vme_user_unlocked_ioctl, - .compat_ioctl = compat_ptr_ioctl, - .mmap = vme_user_mmap, -}; - -static int vme_user_match(struct vme_dev *vdev) -{ - int i; - - int cur_bus = vme_bus_num(vdev); - int cur_slot = vme_slot_num(vdev); - - for (i = 0; i < bus_num; i++) - if ((cur_bus == bus[i]) && (cur_slot == vdev->num)) - return 1; - - return 0; -} - -/* - * In this simple access driver, the old behaviour is being preserved as much - * as practical. We will therefore reserve the buffers and request the images - * here so that we don't have to do it later. - */ -static int vme_user_probe(struct vme_dev *vdev) -{ - int i, err; - char *name; - - /* Save pointer to the bridge device */ - if (vme_user_bridge) { - dev_err(&vdev->dev, "Driver can only be loaded for 1 device\n"); - err = -EINVAL; - goto err_dev; - } - vme_user_bridge = vdev; - - /* Initialise descriptors */ - for (i = 0; i < VME_DEVS; i++) { - image[i].kern_buf = NULL; - image[i].pci_buf = 0; - mutex_init(&image[i].mutex); - image[i].device = NULL; - image[i].resource = NULL; - } - - /* Assign major and minor numbers for the driver */ - err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS, - driver_name); - if (err) { - dev_warn(&vdev->dev, "Error getting Major Number %d for driver.\n", - VME_MAJOR); - goto err_region; - } - - /* Register the driver as a char device */ - vme_user_cdev = cdev_alloc(); - if (!vme_user_cdev) { - err = -ENOMEM; - goto err_char; - } - vme_user_cdev->ops = &vme_user_fops; - vme_user_cdev->owner = THIS_MODULE; - err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS); - if (err) - goto err_class; - - /* Request slave resources and allocate buffers (128kB wide) */ - for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { - /* XXX Need to properly request attributes */ - /* For ca91cx42 bridge there are only two slave windows - * supporting A16 addressing, so we request A24 supported - * by all windows. - */ - image[i].resource = vme_slave_request(vme_user_bridge, - VME_A24, VME_SCT); - if (!image[i].resource) { - dev_warn(&vdev->dev, - "Unable to allocate slave resource\n"); - err = -ENOMEM; - goto err_slave; - } - image[i].size_buf = PCI_BUF_SIZE; - image[i].kern_buf = vme_alloc_consistent(image[i].resource, - image[i].size_buf, - &image[i].pci_buf); - if (!image[i].kern_buf) { - dev_warn(&vdev->dev, - "Unable to allocate memory for buffer\n"); - image[i].pci_buf = 0; - vme_slave_free(image[i].resource); - err = -ENOMEM; - goto err_slave; - } - } - - /* - * Request master resources allocate page sized buffers for small - * reads and writes - */ - for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { - /* XXX Need to properly request attributes */ - image[i].resource = vme_master_request(vme_user_bridge, - VME_A32, VME_SCT, - VME_D32); - if (!image[i].resource) { - dev_warn(&vdev->dev, - "Unable to allocate master resource\n"); - err = -ENOMEM; - goto err_master; - } - image[i].size_buf = PCI_BUF_SIZE; - image[i].kern_buf = kmalloc(image[i].size_buf, GFP_KERNEL); - if (!image[i].kern_buf) { - err = -ENOMEM; - vme_master_free(image[i].resource); - goto err_master; - } - } - - /* Create sysfs entries - on udev systems this creates the dev files */ - vme_user_sysfs_class = class_create(THIS_MODULE, driver_name); - if (IS_ERR(vme_user_sysfs_class)) { - dev_err(&vdev->dev, "Error creating vme_user class.\n"); - err = PTR_ERR(vme_user_sysfs_class); - goto err_master; - } - - /* Add sysfs Entries */ - for (i = 0; i < VME_DEVS; i++) { - int num; - - switch (type[i]) { - case MASTER_MINOR: - name = "bus/vme/m%d"; - break; - case CONTROL_MINOR: - name = "bus/vme/ctl"; - break; - case SLAVE_MINOR: - name = "bus/vme/s%d"; - break; - default: - err = -EINVAL; - goto err_sysfs; - } - - num = (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i; - image[i].device = device_create(vme_user_sysfs_class, NULL, - MKDEV(VME_MAJOR, i), NULL, - name, num); - if (IS_ERR(image[i].device)) { - dev_info(&vdev->dev, "Error creating sysfs device\n"); - err = PTR_ERR(image[i].device); - goto err_sysfs; - } - } - - return 0; - -err_sysfs: - while (i > 0) { - i--; - device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); - } - class_destroy(vme_user_sysfs_class); - - /* Ensure counter set correctly to unalloc all master windows */ - i = MASTER_MAX + 1; -err_master: - while (i > MASTER_MINOR) { - i--; - kfree(image[i].kern_buf); - vme_master_free(image[i].resource); - } - - /* - * Ensure counter set correctly to unalloc all slave windows and buffers - */ - i = SLAVE_MAX + 1; -err_slave: - while (i > SLAVE_MINOR) { - i--; - vme_free_consistent(image[i].resource, image[i].size_buf, - image[i].kern_buf, image[i].pci_buf); - vme_slave_free(image[i].resource); - } -err_class: - cdev_del(vme_user_cdev); -err_char: - unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); -err_region: -err_dev: - return err; -} - -static void vme_user_remove(struct vme_dev *dev) -{ - int i; - - /* Remove sysfs Entries */ - for (i = 0; i < VME_DEVS; i++) { - mutex_destroy(&image[i].mutex); - device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); - } - class_destroy(vme_user_sysfs_class); - - for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { - kfree(image[i].kern_buf); - vme_master_free(image[i].resource); - } - - for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { - vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0); - vme_free_consistent(image[i].resource, image[i].size_buf, - image[i].kern_buf, image[i].pci_buf); - vme_slave_free(image[i].resource); - } - - /* Unregister device driver */ - cdev_del(vme_user_cdev); - - /* Unregister the major and minor device numbers */ - unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); -} - -static struct vme_driver vme_user_driver = { - .name = driver_name, - .match = vme_user_match, - .probe = vme_user_probe, - .remove = vme_user_remove, -}; - -static int __init vme_user_init(void) -{ - int retval = 0; - - pr_info("VME User Space Access Driver\n"); - - if (bus_num == 0) { - pr_err("No cards, skipping registration\n"); - retval = -ENODEV; - goto err_nocard; - } - - /* Let's start by supporting one bus, we can support more than one - * in future revisions if that ever becomes necessary. - */ - if (bus_num > VME_USER_BUS_MAX) { - pr_err("Driver only able to handle %d buses\n", - VME_USER_BUS_MAX); - bus_num = VME_USER_BUS_MAX; - } - - /* - * Here we just register the maximum number of devices we can and - * leave vme_user_match() to allow only 1 to go through to probe(). - * This way, if we later want to allow multiple user access devices, - * we just change the code in vme_user_match(). - */ - retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS); - if (retval) - goto err_reg; - - return retval; - -err_reg: -err_nocard: - return retval; -} - -static void __exit vme_user_exit(void) -{ - vme_unregister_driver(&vme_user_driver); -} - -MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected"); -module_param_array(bus, int, &bus_num, 0000); - -MODULE_DESCRIPTION("VME User Space Access Driver"); -MODULE_AUTHOR("Martyn Welch "); -MODULE_LICENSE("GPL"); - -module_init(vme_user_init); -module_exit(vme_user_exit); diff --git a/drivers/staging/vme/devices/vme_user.h b/drivers/staging/vme/devices/vme_user.h deleted file mode 100644 index 19ecb05781cc..000000000000 --- a/drivers/staging/vme/devices/vme_user.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _VME_USER_H_ -#define _VME_USER_H_ - -#define VME_USER_BUS_MAX 1 - -/* - * VMEbus Master Window Configuration Structure - */ -struct vme_master { - __u32 enable; /* State of Window */ - __u64 vme_addr; /* Starting Address on the VMEbus */ - __u64 size; /* Window Size */ - __u32 aspace; /* Address Space */ - __u32 cycle; /* Cycle properties */ - __u32 dwidth; /* Maximum Data Width */ -#if 0 - char prefetchenable; /* Prefetch Read Enable State */ - int prefetchsize; /* Prefetch Read Size (Cache Lines) */ - char wrpostenable; /* Write Post State */ -#endif -} __packed; - -/* - * IOCTL Commands and structures - */ - -/* Magic number for use in ioctls */ -#define VME_IOC_MAGIC 0xAE - -/* VMEbus Slave Window Configuration Structure */ -struct vme_slave { - __u32 enable; /* State of Window */ - __u64 vme_addr; /* Starting Address on the VMEbus */ - __u64 size; /* Window Size */ - __u32 aspace; /* Address Space */ - __u32 cycle; /* Cycle properties */ -#if 0 - char wrpostenable; /* Write Post State */ - char rmwlock; /* Lock PCI during RMW Cycles */ - char data64bitcapable; /* non-VMEbus capable of 64-bit Data */ -#endif -} __packed; - -struct vme_irq_id { - __u8 level; - __u8 statid; -}; - -#define VME_GET_SLAVE _IOR(VME_IOC_MAGIC, 1, struct vme_slave) -#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 2, struct vme_slave) -#define VME_GET_MASTER _IOR(VME_IOC_MAGIC, 3, struct vme_master) -#define VME_SET_MASTER _IOW(VME_IOC_MAGIC, 4, struct vme_master) -#define VME_IRQ_GEN _IOW(VME_IOC_MAGIC, 5, struct vme_irq_id) - -#endif /* _VME_USER_H_ */ - diff --git a/drivers/staging/vme_user/Kconfig b/drivers/staging/vme_user/Kconfig new file mode 100644 index 000000000000..e8b4461bf27f --- /dev/null +++ b/drivers/staging/vme_user/Kconfig @@ -0,0 +1,13 @@ +# SPDX-License-Identifier: GPL-2.0 +comment "VME Device Drivers" + +config VME_USER + tristate "VME user space access driver" + depends on STAGING && VME_BUS + help + If you say Y here you want to be able to access a limited number of + VME windows in a manner at least semi-compatible with the interface + provided with the original driver at . + + To compile this driver as a module, choose M here. The module will + be called vme_user. If unsure, say N. diff --git a/drivers/staging/vme_user/Makefile b/drivers/staging/vme_user/Makefile new file mode 100644 index 000000000000..5380115139b0 --- /dev/null +++ b/drivers/staging/vme_user/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the VME device drivers. +# + +obj-$(CONFIG_VME_USER) += vme_user.o diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c new file mode 100644 index 000000000000..859af797630c --- /dev/null +++ b/drivers/staging/vme_user/vme_user.c @@ -0,0 +1,780 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * VMEbus User access driver + * + * Author: Martyn Welch + * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. + * + * Based on work by: + * Tom Armistead and Ajit Prem + * Copyright 2004 Motorola Inc. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "vme_user.h" + +static const char driver_name[] = "vme_user"; + +static int bus[VME_USER_BUS_MAX]; +static unsigned int bus_num; + +/* Currently Documentation/admin-guide/devices.rst defines the + * following for VME: + * + * 221 char VME bus + * 0 = /dev/bus/vme/m0 First master image + * 1 = /dev/bus/vme/m1 Second master image + * 2 = /dev/bus/vme/m2 Third master image + * 3 = /dev/bus/vme/m3 Fourth master image + * 4 = /dev/bus/vme/s0 First slave image + * 5 = /dev/bus/vme/s1 Second slave image + * 6 = /dev/bus/vme/s2 Third slave image + * 7 = /dev/bus/vme/s3 Fourth slave image + * 8 = /dev/bus/vme/ctl Control + * + * It is expected that all VME bus drivers will use the + * same interface. For interface documentation see + * http://www.vmelinux.org/. + * + * However the VME driver at http://www.vmelinux.org/ is rather old and doesn't + * even support the tsi148 chipset (which has 8 master and 8 slave windows). + * We'll run with this for now as far as possible, however it probably makes + * sense to get rid of the old mappings and just do everything dynamically. + * + * So for now, we'll restrict the driver to providing 4 masters and 4 slaves as + * defined above and try to support at least some of the interface from + * http://www.vmelinux.org/ as an alternative the driver can be written + * providing a saner interface later. + * + * The vmelinux.org driver never supported slave images, the devices reserved + * for slaves were repurposed to support all 8 master images on the UniverseII! + * We shall support 4 masters and 4 slaves with this driver. + */ +#define VME_MAJOR 221 /* VME Major Device Number */ +#define VME_DEVS 9 /* Number of dev entries */ + +#define MASTER_MINOR 0 +#define MASTER_MAX 3 +#define SLAVE_MINOR 4 +#define SLAVE_MAX 7 +#define CONTROL_MINOR 8 + +#define PCI_BUF_SIZE 0x20000 /* Size of one slave image buffer */ + +/* + * Structure to handle image related parameters. + */ +struct image_desc { + void *kern_buf; /* Buffer address in kernel space */ + dma_addr_t pci_buf; /* Buffer address in PCI address space */ + unsigned long long size_buf; /* Buffer size */ + struct mutex mutex; /* Mutex for locking image */ + struct device *device; /* Sysfs device */ + struct vme_resource *resource; /* VME resource */ + int mmap_count; /* Number of current mmap's */ +}; + +static struct image_desc image[VME_DEVS]; + +static struct cdev *vme_user_cdev; /* Character device */ +static struct class *vme_user_sysfs_class; /* Sysfs class */ +static struct vme_dev *vme_user_bridge; /* Pointer to user device */ + +static const int type[VME_DEVS] = { MASTER_MINOR, MASTER_MINOR, + MASTER_MINOR, MASTER_MINOR, + SLAVE_MINOR, SLAVE_MINOR, + SLAVE_MINOR, SLAVE_MINOR, + CONTROL_MINOR + }; + +struct vme_user_vma_priv { + unsigned int minor; + refcount_t refcnt; +}; + +static ssize_t resource_to_user(int minor, char __user *buf, size_t count, + loff_t *ppos) +{ + ssize_t copied = 0; + + if (count > image[minor].size_buf) + count = image[minor].size_buf; + + copied = vme_master_read(image[minor].resource, image[minor].kern_buf, + count, *ppos); + if (copied < 0) + return (int)copied; + + if (copy_to_user(buf, image[minor].kern_buf, (unsigned long)copied)) + return -EFAULT; + + return copied; +} + +static ssize_t resource_from_user(unsigned int minor, const char __user *buf, + size_t count, loff_t *ppos) +{ + if (count > image[minor].size_buf) + count = image[minor].size_buf; + + if (copy_from_user(image[minor].kern_buf, buf, (unsigned long)count)) + return -EFAULT; + + return vme_master_write(image[minor].resource, image[minor].kern_buf, + count, *ppos); +} + +static ssize_t buffer_to_user(unsigned int minor, char __user *buf, + size_t count, loff_t *ppos) +{ + void *image_ptr; + + image_ptr = image[minor].kern_buf + *ppos; + if (copy_to_user(buf, image_ptr, (unsigned long)count)) + return -EFAULT; + + return count; +} + +static ssize_t buffer_from_user(unsigned int minor, const char __user *buf, + size_t count, loff_t *ppos) +{ + void *image_ptr; + + image_ptr = image[minor].kern_buf + *ppos; + if (copy_from_user(image_ptr, buf, (unsigned long)count)) + return -EFAULT; + + return count; +} + +static ssize_t vme_user_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + unsigned int minor = iminor(file_inode(file)); + ssize_t retval; + size_t image_size; + + if (minor == CONTROL_MINOR) + return 0; + + mutex_lock(&image[minor].mutex); + + /* XXX Do we *really* want this helper - we can use vme_*_get ? */ + image_size = vme_get_size(image[minor].resource); + + /* Ensure we are starting at a valid location */ + if ((*ppos < 0) || (*ppos > (image_size - 1))) { + mutex_unlock(&image[minor].mutex); + return 0; + } + + /* Ensure not reading past end of the image */ + if (*ppos + count > image_size) + count = image_size - *ppos; + + switch (type[minor]) { + case MASTER_MINOR: + retval = resource_to_user(minor, buf, count, ppos); + break; + case SLAVE_MINOR: + retval = buffer_to_user(minor, buf, count, ppos); + break; + default: + retval = -EINVAL; + } + + mutex_unlock(&image[minor].mutex); + if (retval > 0) + *ppos += retval; + + return retval; +} + +static ssize_t vme_user_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + unsigned int minor = iminor(file_inode(file)); + ssize_t retval; + size_t image_size; + + if (minor == CONTROL_MINOR) + return 0; + + mutex_lock(&image[minor].mutex); + + image_size = vme_get_size(image[minor].resource); + + /* Ensure we are starting at a valid location */ + if ((*ppos < 0) || (*ppos > (image_size - 1))) { + mutex_unlock(&image[minor].mutex); + return 0; + } + + /* Ensure not reading past end of the image */ + if (*ppos + count > image_size) + count = image_size - *ppos; + + switch (type[minor]) { + case MASTER_MINOR: + retval = resource_from_user(minor, buf, count, ppos); + break; + case SLAVE_MINOR: + retval = buffer_from_user(minor, buf, count, ppos); + break; + default: + retval = -EINVAL; + } + + mutex_unlock(&image[minor].mutex); + + if (retval > 0) + *ppos += retval; + + return retval; +} + +static loff_t vme_user_llseek(struct file *file, loff_t off, int whence) +{ + unsigned int minor = iminor(file_inode(file)); + size_t image_size; + loff_t res; + + switch (type[minor]) { + case MASTER_MINOR: + case SLAVE_MINOR: + mutex_lock(&image[minor].mutex); + image_size = vme_get_size(image[minor].resource); + res = fixed_size_llseek(file, off, whence, image_size); + mutex_unlock(&image[minor].mutex); + return res; + } + + return -EINVAL; +} + +/* + * The ioctls provided by the old VME access method (the one at vmelinux.org) + * are most certainly wrong as the effectively push the registers layout + * through to user space. Given that the VME core can handle multiple bridges, + * with different register layouts this is most certainly not the way to go. + * + * We aren't using the structures defined in the Motorola driver either - these + * are also quite low level, however we should use the definitions that have + * already been defined. + */ +static int vme_user_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct vme_master master; + struct vme_slave slave; + struct vme_irq_id irq_req; + unsigned long copied; + unsigned int minor = iminor(inode); + int retval; + dma_addr_t pci_addr; + void __user *argp = (void __user *)arg; + + switch (type[minor]) { + case CONTROL_MINOR: + switch (cmd) { + case VME_IRQ_GEN: + copied = copy_from_user(&irq_req, argp, + sizeof(irq_req)); + if (copied) { + pr_warn("Partial copy from userspace\n"); + return -EFAULT; + } + + return vme_irq_generate(vme_user_bridge, + irq_req.level, + irq_req.statid); + } + break; + case MASTER_MINOR: + switch (cmd) { + case VME_GET_MASTER: + memset(&master, 0, sizeof(master)); + + /* XXX We do not want to push aspace, cycle and width + * to userspace as they are + */ + retval = vme_master_get(image[minor].resource, + &master.enable, + &master.vme_addr, + &master.size, &master.aspace, + &master.cycle, &master.dwidth); + + copied = copy_to_user(argp, &master, + sizeof(master)); + if (copied) { + pr_warn("Partial copy to userspace\n"); + return -EFAULT; + } + + return retval; + + case VME_SET_MASTER: + + if (image[minor].mmap_count != 0) { + pr_warn("Can't adjust mapped window\n"); + return -EPERM; + } + + copied = copy_from_user(&master, argp, sizeof(master)); + if (copied) { + pr_warn("Partial copy from userspace\n"); + return -EFAULT; + } + + /* XXX We do not want to push aspace, cycle and width + * to userspace as they are + */ + return vme_master_set(image[minor].resource, + master.enable, master.vme_addr, master.size, + master.aspace, master.cycle, master.dwidth); + + break; + } + break; + case SLAVE_MINOR: + switch (cmd) { + case VME_GET_SLAVE: + memset(&slave, 0, sizeof(slave)); + + /* XXX We do not want to push aspace, cycle and width + * to userspace as they are + */ + retval = vme_slave_get(image[minor].resource, + &slave.enable, &slave.vme_addr, + &slave.size, &pci_addr, + &slave.aspace, &slave.cycle); + + copied = copy_to_user(argp, &slave, + sizeof(slave)); + if (copied) { + pr_warn("Partial copy to userspace\n"); + return -EFAULT; + } + + return retval; + + case VME_SET_SLAVE: + + copied = copy_from_user(&slave, argp, sizeof(slave)); + if (copied) { + pr_warn("Partial copy from userspace\n"); + return -EFAULT; + } + + /* XXX We do not want to push aspace, cycle and width + * to userspace as they are + */ + return vme_slave_set(image[minor].resource, + slave.enable, slave.vme_addr, slave.size, + image[minor].pci_buf, slave.aspace, + slave.cycle); + + break; + } + break; + } + + return -EINVAL; +} + +static long +vme_user_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int ret; + struct inode *inode = file_inode(file); + unsigned int minor = iminor(inode); + + mutex_lock(&image[minor].mutex); + ret = vme_user_ioctl(inode, file, cmd, arg); + mutex_unlock(&image[minor].mutex); + + return ret; +} + +static void vme_user_vm_open(struct vm_area_struct *vma) +{ + struct vme_user_vma_priv *vma_priv = vma->vm_private_data; + + refcount_inc(&vma_priv->refcnt); +} + +static void vme_user_vm_close(struct vm_area_struct *vma) +{ + struct vme_user_vma_priv *vma_priv = vma->vm_private_data; + unsigned int minor = vma_priv->minor; + + if (!refcount_dec_and_test(&vma_priv->refcnt)) + return; + + mutex_lock(&image[minor].mutex); + image[minor].mmap_count--; + mutex_unlock(&image[minor].mutex); + + kfree(vma_priv); +} + +static const struct vm_operations_struct vme_user_vm_ops = { + .open = vme_user_vm_open, + .close = vme_user_vm_close, +}; + +static int vme_user_master_mmap(unsigned int minor, struct vm_area_struct *vma) +{ + int err; + struct vme_user_vma_priv *vma_priv; + + mutex_lock(&image[minor].mutex); + + err = vme_master_mmap(image[minor].resource, vma); + if (err) { + mutex_unlock(&image[minor].mutex); + return err; + } + + vma_priv = kmalloc(sizeof(*vma_priv), GFP_KERNEL); + if (!vma_priv) { + mutex_unlock(&image[minor].mutex); + return -ENOMEM; + } + + vma_priv->minor = minor; + refcount_set(&vma_priv->refcnt, 1); + vma->vm_ops = &vme_user_vm_ops; + vma->vm_private_data = vma_priv; + + image[minor].mmap_count++; + + mutex_unlock(&image[minor].mutex); + + return 0; +} + +static int vme_user_mmap(struct file *file, struct vm_area_struct *vma) +{ + unsigned int minor = iminor(file_inode(file)); + + if (type[minor] == MASTER_MINOR) + return vme_user_master_mmap(minor, vma); + + return -ENODEV; +} + +static const struct file_operations vme_user_fops = { + .read = vme_user_read, + .write = vme_user_write, + .llseek = vme_user_llseek, + .unlocked_ioctl = vme_user_unlocked_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .mmap = vme_user_mmap, +}; + +static int vme_user_match(struct vme_dev *vdev) +{ + int i; + + int cur_bus = vme_bus_num(vdev); + int cur_slot = vme_slot_num(vdev); + + for (i = 0; i < bus_num; i++) + if ((cur_bus == bus[i]) && (cur_slot == vdev->num)) + return 1; + + return 0; +} + +/* + * In this simple access driver, the old behaviour is being preserved as much + * as practical. We will therefore reserve the buffers and request the images + * here so that we don't have to do it later. + */ +static int vme_user_probe(struct vme_dev *vdev) +{ + int i, err; + char *name; + + /* Save pointer to the bridge device */ + if (vme_user_bridge) { + dev_err(&vdev->dev, "Driver can only be loaded for 1 device\n"); + err = -EINVAL; + goto err_dev; + } + vme_user_bridge = vdev; + + /* Initialise descriptors */ + for (i = 0; i < VME_DEVS; i++) { + image[i].kern_buf = NULL; + image[i].pci_buf = 0; + mutex_init(&image[i].mutex); + image[i].device = NULL; + image[i].resource = NULL; + } + + /* Assign major and minor numbers for the driver */ + err = register_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS, + driver_name); + if (err) { + dev_warn(&vdev->dev, "Error getting Major Number %d for driver.\n", + VME_MAJOR); + goto err_region; + } + + /* Register the driver as a char device */ + vme_user_cdev = cdev_alloc(); + if (!vme_user_cdev) { + err = -ENOMEM; + goto err_char; + } + vme_user_cdev->ops = &vme_user_fops; + vme_user_cdev->owner = THIS_MODULE; + err = cdev_add(vme_user_cdev, MKDEV(VME_MAJOR, 0), VME_DEVS); + if (err) + goto err_class; + + /* Request slave resources and allocate buffers (128kB wide) */ + for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { + /* XXX Need to properly request attributes */ + /* For ca91cx42 bridge there are only two slave windows + * supporting A16 addressing, so we request A24 supported + * by all windows. + */ + image[i].resource = vme_slave_request(vme_user_bridge, + VME_A24, VME_SCT); + if (!image[i].resource) { + dev_warn(&vdev->dev, + "Unable to allocate slave resource\n"); + err = -ENOMEM; + goto err_slave; + } + image[i].size_buf = PCI_BUF_SIZE; + image[i].kern_buf = vme_alloc_consistent(image[i].resource, + image[i].size_buf, + &image[i].pci_buf); + if (!image[i].kern_buf) { + dev_warn(&vdev->dev, + "Unable to allocate memory for buffer\n"); + image[i].pci_buf = 0; + vme_slave_free(image[i].resource); + err = -ENOMEM; + goto err_slave; + } + } + + /* + * Request master resources allocate page sized buffers for small + * reads and writes + */ + for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { + /* XXX Need to properly request attributes */ + image[i].resource = vme_master_request(vme_user_bridge, + VME_A32, VME_SCT, + VME_D32); + if (!image[i].resource) { + dev_warn(&vdev->dev, + "Unable to allocate master resource\n"); + err = -ENOMEM; + goto err_master; + } + image[i].size_buf = PCI_BUF_SIZE; + image[i].kern_buf = kmalloc(image[i].size_buf, GFP_KERNEL); + if (!image[i].kern_buf) { + err = -ENOMEM; + vme_master_free(image[i].resource); + goto err_master; + } + } + + /* Create sysfs entries - on udev systems this creates the dev files */ + vme_user_sysfs_class = class_create(THIS_MODULE, driver_name); + if (IS_ERR(vme_user_sysfs_class)) { + dev_err(&vdev->dev, "Error creating vme_user class.\n"); + err = PTR_ERR(vme_user_sysfs_class); + goto err_master; + } + + /* Add sysfs Entries */ + for (i = 0; i < VME_DEVS; i++) { + int num; + + switch (type[i]) { + case MASTER_MINOR: + name = "bus/vme/m%d"; + break; + case CONTROL_MINOR: + name = "bus/vme/ctl"; + break; + case SLAVE_MINOR: + name = "bus/vme/s%d"; + break; + default: + err = -EINVAL; + goto err_sysfs; + } + + num = (type[i] == SLAVE_MINOR) ? i - (MASTER_MAX + 1) : i; + image[i].device = device_create(vme_user_sysfs_class, NULL, + MKDEV(VME_MAJOR, i), NULL, + name, num); + if (IS_ERR(image[i].device)) { + dev_info(&vdev->dev, "Error creating sysfs device\n"); + err = PTR_ERR(image[i].device); + goto err_sysfs; + } + } + + return 0; + +err_sysfs: + while (i > 0) { + i--; + device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); + } + class_destroy(vme_user_sysfs_class); + + /* Ensure counter set correctly to unalloc all master windows */ + i = MASTER_MAX + 1; +err_master: + while (i > MASTER_MINOR) { + i--; + kfree(image[i].kern_buf); + vme_master_free(image[i].resource); + } + + /* + * Ensure counter set correctly to unalloc all slave windows and buffers + */ + i = SLAVE_MAX + 1; +err_slave: + while (i > SLAVE_MINOR) { + i--; + vme_free_consistent(image[i].resource, image[i].size_buf, + image[i].kern_buf, image[i].pci_buf); + vme_slave_free(image[i].resource); + } +err_class: + cdev_del(vme_user_cdev); +err_char: + unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); +err_region: +err_dev: + return err; +} + +static void vme_user_remove(struct vme_dev *dev) +{ + int i; + + /* Remove sysfs Entries */ + for (i = 0; i < VME_DEVS; i++) { + mutex_destroy(&image[i].mutex); + device_destroy(vme_user_sysfs_class, MKDEV(VME_MAJOR, i)); + } + class_destroy(vme_user_sysfs_class); + + for (i = MASTER_MINOR; i < (MASTER_MAX + 1); i++) { + kfree(image[i].kern_buf); + vme_master_free(image[i].resource); + } + + for (i = SLAVE_MINOR; i < (SLAVE_MAX + 1); i++) { + vme_slave_set(image[i].resource, 0, 0, 0, 0, VME_A32, 0); + vme_free_consistent(image[i].resource, image[i].size_buf, + image[i].kern_buf, image[i].pci_buf); + vme_slave_free(image[i].resource); + } + + /* Unregister device driver */ + cdev_del(vme_user_cdev); + + /* Unregister the major and minor device numbers */ + unregister_chrdev_region(MKDEV(VME_MAJOR, 0), VME_DEVS); +} + +static struct vme_driver vme_user_driver = { + .name = driver_name, + .match = vme_user_match, + .probe = vme_user_probe, + .remove = vme_user_remove, +}; + +static int __init vme_user_init(void) +{ + int retval = 0; + + pr_info("VME User Space Access Driver\n"); + + if (bus_num == 0) { + pr_err("No cards, skipping registration\n"); + retval = -ENODEV; + goto err_nocard; + } + + /* Let's start by supporting one bus, we can support more than one + * in future revisions if that ever becomes necessary. + */ + if (bus_num > VME_USER_BUS_MAX) { + pr_err("Driver only able to handle %d buses\n", + VME_USER_BUS_MAX); + bus_num = VME_USER_BUS_MAX; + } + + /* + * Here we just register the maximum number of devices we can and + * leave vme_user_match() to allow only 1 to go through to probe(). + * This way, if we later want to allow multiple user access devices, + * we just change the code in vme_user_match(). + */ + retval = vme_register_driver(&vme_user_driver, VME_MAX_SLOTS); + if (retval) + goto err_reg; + + return retval; + +err_reg: +err_nocard: + return retval; +} + +static void __exit vme_user_exit(void) +{ + vme_unregister_driver(&vme_user_driver); +} + +MODULE_PARM_DESC(bus, "Enumeration of VMEbus to which the driver is connected"); +module_param_array(bus, int, &bus_num, 0000); + +MODULE_DESCRIPTION("VME User Space Access Driver"); +MODULE_AUTHOR("Martyn Welch "); +MODULE_LICENSE("GPL"); + +module_init(vme_user_init); +module_exit(vme_user_exit); diff --git a/drivers/staging/vme_user/vme_user.h b/drivers/staging/vme_user/vme_user.h new file mode 100644 index 000000000000..19ecb05781cc --- /dev/null +++ b/drivers/staging/vme_user/vme_user.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _VME_USER_H_ +#define _VME_USER_H_ + +#define VME_USER_BUS_MAX 1 + +/* + * VMEbus Master Window Configuration Structure + */ +struct vme_master { + __u32 enable; /* State of Window */ + __u64 vme_addr; /* Starting Address on the VMEbus */ + __u64 size; /* Window Size */ + __u32 aspace; /* Address Space */ + __u32 cycle; /* Cycle properties */ + __u32 dwidth; /* Maximum Data Width */ +#if 0 + char prefetchenable; /* Prefetch Read Enable State */ + int prefetchsize; /* Prefetch Read Size (Cache Lines) */ + char wrpostenable; /* Write Post State */ +#endif +} __packed; + +/* + * IOCTL Commands and structures + */ + +/* Magic number for use in ioctls */ +#define VME_IOC_MAGIC 0xAE + +/* VMEbus Slave Window Configuration Structure */ +struct vme_slave { + __u32 enable; /* State of Window */ + __u64 vme_addr; /* Starting Address on the VMEbus */ + __u64 size; /* Window Size */ + __u32 aspace; /* Address Space */ + __u32 cycle; /* Cycle properties */ +#if 0 + char wrpostenable; /* Write Post State */ + char rmwlock; /* Lock PCI during RMW Cycles */ + char data64bitcapable; /* non-VMEbus capable of 64-bit Data */ +#endif +} __packed; + +struct vme_irq_id { + __u8 level; + __u8 statid; +}; + +#define VME_GET_SLAVE _IOR(VME_IOC_MAGIC, 1, struct vme_slave) +#define VME_SET_SLAVE _IOW(VME_IOC_MAGIC, 2, struct vme_slave) +#define VME_GET_MASTER _IOR(VME_IOC_MAGIC, 3, struct vme_master) +#define VME_SET_MASTER _IOW(VME_IOC_MAGIC, 4, struct vme_master) +#define VME_IRQ_GEN _IOW(VME_IOC_MAGIC, 5, struct vme_irq_id) + +#endif /* _VME_USER_H_ */ + -- cgit