Commit 4debfe409b6e550032bfef9733e9f6f7c5613617

Authored by Alessandro Rubini
Committed by Greg Kroah-Hartman
1 parent 6007b1bd0f

FMC: add a char-device mezzanine driver

This driver exports the memory area associated with the mezzanine card
as a misc device, so users can access registers.

Signed-off-by: Alessandro Rubini <rubini@gnudd.com>
Acked-by: Juan David Gonzalez Cobas <dcobas@cern.ch>
Acked-by: Emilio G. Cota <cota@braap.org>
Acked-by: Samuel Iglesias Gonsalvez <siglesias@igalia.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 5 changed files with 274 additions and 1 deletions Side-by-side Diff

Documentation/fmc/00-INDEX
... ... @@ -32,5 +32,8 @@
32 32 - about drivers/fmc/fmc-trivial.ko
33 33  
34 34 fmc-write-eeprom.txt
35   - - about drivers/fmc/fmc-write-eeprom.ko
  35 + - about drivers/fmc/fmc-write-eeprom.ko
  36 +
  37 +fmc-chardev.txt
  38 + - about drivers/fmc/fmc-chardev.ko
Documentation/fmc/fmc-chardev.txt
  1 +fmc-chardev
  2 +===========
  3 +
  4 +This is a simple generic driver, that allows user access by means of a
  5 +character device (actually, one for each mezzanine it takes hold of).
  6 +
  7 +The char device is created as a misc device. Its name in /dev (as
  8 +created by udev) is the same name as the underlying FMC device. Thus,
  9 +the name can be a silly fmc-0000 look-alike if the device has no
  10 +identifiers nor bus_id, a more specific fmc-0400 if the device has a
  11 +bus-specific address but no associated name, or something like
  12 +fdelay-0400 if the FMC core can rely on both a mezzanine name and a bus
  13 +address.
  14 +
  15 +Currently the driver only supports read and write: you can lseek to the
  16 +desired address and read or write a register.
  17 +
  18 +The driver assumes all registers are 32-bit in size, and only accepts a
  19 +single read or write per system call. However, as a result of Unix read
  20 +and write semantics, users can simply fread or fwrite bigger areas in
  21 +order to dump or store bigger memory areas.
  22 +
  23 +There is currently no support for mmap, user-space interrupt management
  24 +and DMA buffers. They may be added in later versions, if the need
  25 +arises.
  26 +
  27 +The example below shows raw access to a SPEC card programmed with its
  28 +golden FPGA file, that features an SDB structure at offset 256 - i.e.
  29 +64 words. The mezzanine's EEPROM in this case is not programmed, so the
  30 +default name is fmc-<bus><devfn>, and there are two cards in the system:
  31 +
  32 + spusa.root# insmod fmc-chardev.ko
  33 + [ 1073.339332] spec 0000:02:00.0: Driver has no ID: matches all
  34 + [ 1073.345051] spec 0000:02:00.0: Created misc device "fmc-0200"
  35 + [ 1073.350821] spec 0000:04:00.0: Driver has no ID: matches all
  36 + [ 1073.356525] spec 0000:04:00.0: Created misc device "fmc-0400"
  37 + spusa.root# ls -l /dev/fmc*
  38 + crw------- 1 root root 10, 58 Nov 20 19:23 /dev/fmc-0200
  39 + crw------- 1 root root 10, 57 Nov 20 19:23 /dev/fmc-0400
  40 + spusa.root# dd bs=4 skip=64 count=1 if=/dev/fmc-0200 2> /dev/null | od -t x1z
  41 + 0000000 2d 42 44 53 >-BDS<
  42 + 0000004
  43 +
  44 +The simple program tools/fmc-mem in this package can access an FMC char
  45 +device and read or write a word or a whole area. Actually, the program
  46 +is not specific to FMC at all, it just uses lseek, read and write.
  47 +
  48 +Its first argument is the device name, the second the offset, the third
  49 +(if any) the value to write and the optional last argument that must
  50 +begin with "+" is the number of bytes to read or write. In case of
  51 +repeated reading data is written to stdout; repeated writes read from
  52 +stdin and the value argument is ignored.
  53 +
  54 +The following examples show reading the SDB magic number and the first
  55 +SDB record from a SPEC device programmed with its golden image:
  56 +
  57 + spusa.root# ./fmc-mem /dev/fmc-0200 100
  58 + 5344422d
  59 + spusa.root# ./fmc-mem /dev/fmc-0200 100 +40 | od -Ax -t x1z
  60 + 000000 2d 42 44 53 00 01 02 00 00 00 00 00 00 00 00 00 >-BDS............<
  61 + 000010 00 00 00 00 ff 01 00 00 00 00 00 00 51 06 00 00 >............Q...<
  62 + 000020 c9 42 a5 e6 02 00 00 00 11 05 12 20 2d 34 42 57 >.B......... -4BW<
  63 + 000030 73 6f 72 43 72 61 62 73 49 53 47 2d 00 20 20 20 >sorCrabsISG-. <
  64 + 000040
... ... @@ -40,5 +40,13 @@
40 40 its binary and the function carrier->reprogram to actually do it.
41 41 It is useful when the mezzanines are produced.
42 42  
  43 +config FMC_CHARDEV
  44 + tristate "FMC mezzanine driver that registers a char device"
  45 + help
  46 + This driver matches every mezzanine device and allows user
  47 + space to read and write registers using a char device. It
  48 + can be used to write user-space drivers, or just get
  49 + aquainted with a mezzanine before writing its specific driver.
  50 +
43 51 endif # FMC
drivers/fmc/Makefile
... ... @@ -10,4 +10,5 @@
10 10 obj-$(CONFIG_FMC_FAKEDEV) += fmc-fakedev.o
11 11 obj-$(CONFIG_FMC_TRIVIAL) += fmc-trivial.o
12 12 obj-$(CONFIG_FMC_WRITE_EEPROM) += fmc-write-eeprom.o
  13 +obj-$(CONFIG_FMC_CHARDEV) += fmc-chardev.o
drivers/fmc/fmc-chardev.c
  1 +/*
  2 + * Copyright (C) 2012 CERN (www.cern.ch)
  3 + * Author: Alessandro Rubini <rubini@gnudd.com>
  4 + *
  5 + * Released according to the GNU GPL, version 2 or any later version.
  6 + *
  7 + * This work is part of the White Rabbit project, a research effort led
  8 + * by CERN, the European Institute for Nuclear Research.
  9 + */
  10 +#include <linux/module.h>
  11 +#include <linux/init.h>
  12 +#include <linux/list.h>
  13 +#include <linux/slab.h>
  14 +#include <linux/fs.h>
  15 +#include <linux/miscdevice.h>
  16 +#include <linux/spinlock.h>
  17 +#include <linux/fmc.h>
  18 +#include <linux/uaccess.h>
  19 +
  20 +static LIST_HEAD(fc_devices);
  21 +static DEFINE_SPINLOCK(fc_lock);
  22 +
  23 +struct fc_instance {
  24 + struct list_head list;
  25 + struct fmc_device *fmc;
  26 + struct miscdevice misc;
  27 +};
  28 +
  29 +/* at open time, we must identify our device */
  30 +static int fc_open(struct inode *ino, struct file *f)
  31 +{
  32 + struct fmc_device *fmc;
  33 + struct fc_instance *fc;
  34 + int minor = iminor(ino);
  35 +
  36 + list_for_each_entry(fc, &fc_devices, list)
  37 + if (fc->misc.minor == minor)
  38 + break;
  39 + if (fc->misc.minor != minor)
  40 + return -ENODEV;
  41 + fmc = fc->fmc;
  42 + if (try_module_get(fmc->owner) == 0)
  43 + return -ENODEV;
  44 +
  45 + f->private_data = fmc;
  46 + return 0;
  47 +}
  48 +
  49 +static int fc_release(struct inode *ino, struct file *f)
  50 +{
  51 + struct fmc_device *fmc = f->private_data;
  52 + module_put(fmc->owner);
  53 + return 0;
  54 +}
  55 +
  56 +/* read and write are simple after the default llseek has been used */
  57 +static ssize_t fc_read(struct file *f, char __user *buf, size_t count,
  58 + loff_t *offp)
  59 +{
  60 + struct fmc_device *fmc = f->private_data;
  61 + unsigned long addr;
  62 + uint32_t val;
  63 +
  64 + if (count < sizeof(val))
  65 + return -EINVAL;
  66 + count = sizeof(val);
  67 +
  68 + addr = *offp;
  69 + if (addr > fmc->memlen)
  70 + return -ESPIPE; /* Illegal seek */
  71 + val = fmc_readl(fmc, addr);
  72 + if (copy_to_user(buf, &val, count))
  73 + return -EFAULT;
  74 + *offp += count;
  75 + return count;
  76 +}
  77 +
  78 +static ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
  79 + loff_t *offp)
  80 +{
  81 + struct fmc_device *fmc = f->private_data;
  82 + unsigned long addr;
  83 + uint32_t val;
  84 +
  85 + if (count < sizeof(val))
  86 + return -EINVAL;
  87 + count = sizeof(val);
  88 +
  89 + addr = *offp;
  90 + if (addr > fmc->memlen)
  91 + return -ESPIPE; /* Illegal seek */
  92 + if (copy_from_user(&val, buf, count))
  93 + return -EFAULT;
  94 + fmc_writel(fmc, val, addr);
  95 + *offp += count;
  96 + return count;
  97 +}
  98 +
  99 +static const struct file_operations fc_fops = {
  100 + .owner = THIS_MODULE,
  101 + .open = fc_open,
  102 + .release = fc_release,
  103 + .llseek = generic_file_llseek,
  104 + .read = fc_read,
  105 + .write = fc_write,
  106 +};
  107 +
  108 +
  109 +/* Device part .. */
  110 +static int fc_probe(struct fmc_device *fmc);
  111 +static int fc_remove(struct fmc_device *fmc);
  112 +
  113 +static struct fmc_driver fc_drv = {
  114 + .version = FMC_VERSION,
  115 + .driver.name = KBUILD_MODNAME,
  116 + .probe = fc_probe,
  117 + .remove = fc_remove,
  118 + /* no table: we want to match everything */
  119 +};
  120 +
  121 +/* We accept the generic busid parameter */
  122 +FMC_PARAM_BUSID(fc_drv);
  123 +
  124 +/* probe and remove must allocate and release a misc device */
  125 +static int fc_probe(struct fmc_device *fmc)
  126 +{
  127 + int ret;
  128 + int index = 0;
  129 +
  130 + struct fc_instance *fc;
  131 +
  132 + if (fmc->op->validate)
  133 + index = fmc->op->validate(fmc, &fc_drv);
  134 + if (index < 0)
  135 + return -EINVAL; /* not our device: invalid */
  136 +
  137 + /* Create a char device: we want to create it anew */
  138 + fc = kzalloc(sizeof(*fc), GFP_KERNEL);
  139 + fc->fmc = fmc;
  140 + fc->misc.minor = MISC_DYNAMIC_MINOR;
  141 + fc->misc.fops = &fc_fops;
  142 + fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
  143 +
  144 + spin_lock(&fc_lock);
  145 + ret = misc_register(&fc->misc);
  146 + if (ret < 0) {
  147 + kfree(fc->misc.name);
  148 + kfree(fc);
  149 + } else {
  150 + list_add(&fc->list, &fc_devices);
  151 + }
  152 + spin_unlock(&fc_lock);
  153 + dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
  154 + fc->misc.name);
  155 + return ret;
  156 +}
  157 +
  158 +static int fc_remove(struct fmc_device *fmc)
  159 +{
  160 + struct fc_instance *fc;
  161 +
  162 + list_for_each_entry(fc, &fc_devices, list)
  163 + if (fc->fmc == fmc)
  164 + break;
  165 + if (fc->fmc != fmc) {
  166 + dev_err(&fmc->dev, "remove called but not found\n");
  167 + return -ENODEV;
  168 + }
  169 +
  170 + spin_lock(&fc_lock);
  171 + list_del(&fc->list);
  172 + misc_deregister(&fc->misc);
  173 + kfree(fc->misc.name);
  174 + kfree(fc);
  175 + spin_unlock(&fc_lock);
  176 +
  177 + return 0;
  178 +}
  179 +
  180 +
  181 +static int fc_init(void)
  182 +{
  183 + int ret;
  184 +
  185 + ret = fmc_driver_register(&fc_drv);
  186 + return ret;
  187 +}
  188 +
  189 +static void fc_exit(void)
  190 +{
  191 + fmc_driver_unregister(&fc_drv);
  192 +}
  193 +
  194 +module_init(fc_init);
  195 +module_exit(fc_exit);
  196 +
  197 +MODULE_LICENSE("GPL");