Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alessandro Rubini and committed by
Greg Kroah-Hartman
4debfe40 6007b1bd

+274 -1
+4 -1
Documentation/fmc/00-INDEX
··· 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
+64
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
+8
drivers/fmc/Kconfig
··· 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
+1
drivers/fmc/Makefile
··· 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
+197
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");