14debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/*
24debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Copyright (C) 2012 CERN (www.cern.ch)
34debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Author: Alessandro Rubini <rubini@gnudd.com>
44debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini *
54debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * Released according to the GNU GPL, version 2 or any later version.
64debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini *
74debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * This work is part of the White Rabbit project, a research effort led
84debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini * by CERN, the European Institute for Nuclear Research.
94debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini */
104debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/module.h>
114debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/init.h>
124debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/list.h>
134debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/slab.h>
144debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/fs.h>
154debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/miscdevice.h>
164debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/spinlock.h>
174debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/fmc.h>
184debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini#include <linux/uaccess.h>
194debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
204debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic LIST_HEAD(fc_devices);
214debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic DEFINE_SPINLOCK(fc_lock);
224debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
234debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistruct fc_instance {
244debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct list_head list;
254debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fmc_device *fmc;
264debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct miscdevice misc;
274debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini};
284debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
294debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* at open time, we must identify our device */
304debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_open(struct inode *ino, struct file *f)
314debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
324debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fmc_device *fmc;
334debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fc_instance *fc;
344debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	int minor = iminor(ino);
354debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
364debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	list_for_each_entry(fc, &fc_devices, list)
374debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		if (fc->misc.minor == minor)
384debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini			break;
394debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (fc->misc.minor != minor)
404debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -ENODEV;
414debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fmc = fc->fmc;
424debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (try_module_get(fmc->owner) == 0)
434debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -ENODEV;
444debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
454debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	f->private_data = fmc;
464debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return 0;
474debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
484debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
494debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_release(struct inode *ino, struct file *f)
504debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
514debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fmc_device *fmc = f->private_data;
524debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	module_put(fmc->owner);
534debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return 0;
544debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
554debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
564debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* read and write are simple after the default llseek has been used */
574debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic ssize_t fc_read(struct file *f, char __user *buf, size_t count,
584debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		       loff_t *offp)
594debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
604debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fmc_device *fmc = f->private_data;
614debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	unsigned long addr;
624debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	uint32_t val;
634debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
644debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (count < sizeof(val))
654debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -EINVAL;
664debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	count = sizeof(val);
674debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
684debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	addr = *offp;
694debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (addr > fmc->memlen)
704debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -ESPIPE; /* Illegal seek */
714debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	val = fmc_readl(fmc, addr);
724debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (copy_to_user(buf, &val, count))
734debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -EFAULT;
744debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	*offp += count;
754debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return count;
764debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
774debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
784debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic ssize_t fc_write(struct file *f, const char __user *buf, size_t count,
794debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini			loff_t *offp)
804debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
814debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fmc_device *fmc = f->private_data;
824debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	unsigned long addr;
834debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	uint32_t val;
844debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
854debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (count < sizeof(val))
864debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -EINVAL;
874debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	count = sizeof(val);
884debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
894debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	addr = *offp;
904debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (addr > fmc->memlen)
914debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -ESPIPE; /* Illegal seek */
924debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (copy_from_user(&val, buf, count))
934debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -EFAULT;
944debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fmc_writel(fmc, val, addr);
954debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	*offp += count;
964debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return count;
974debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
984debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
994debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic const struct file_operations fc_fops = {
1004debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.owner = THIS_MODULE,
1014debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.open = fc_open,
1024debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.release = fc_release,
1034debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.llseek = generic_file_llseek,
1044debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.read = fc_read,
1054debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.write = fc_write,
1064debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini};
1074debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1084debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1094debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* Device part .. */
1104debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_probe(struct fmc_device *fmc);
1114debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_remove(struct fmc_device *fmc);
1124debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1134debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic struct fmc_driver fc_drv = {
1144debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.version = FMC_VERSION,
1154debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.driver.name = KBUILD_MODNAME,
1164debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.probe = fc_probe,
1174debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	.remove = fc_remove,
1184debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	/* no table: we want to match everything */
1194debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini};
1204debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1214debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* We accept the generic busid parameter */
1224debfe409b6e550032bfef9733e9f6f7c5613617Alessandro RubiniFMC_PARAM_BUSID(fc_drv);
1234debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1244debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini/* probe and remove must allocate and release a misc device */
1254debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_probe(struct fmc_device *fmc)
1264debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
1274debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	int ret;
1284debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	int index = 0;
1294debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1304debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fc_instance *fc;
1314debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1324debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (fmc->op->validate)
1334debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		index = fmc->op->validate(fmc, &fc_drv);
1344debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (index < 0)
1354debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -EINVAL; /* not our device: invalid */
1364debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1374debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	/* Create a char device: we want to create it anew */
1384debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
1394640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	if (!fc)
1404640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter		return -ENOMEM;
1414debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fc->fmc = fmc;
1424debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fc->misc.minor = MISC_DYNAMIC_MINOR;
1434debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fc->misc.fops = &fc_fops;
1444debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fc->misc.name = kstrdup(dev_name(&fmc->dev), GFP_KERNEL);
1454debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1464debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	ret = misc_register(&fc->misc);
1474640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	if (ret < 0)
148783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini		goto out;
149783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini	spin_lock(&fc_lock);
1504640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	list_add(&fc->list, &fc_devices);
1514debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	spin_unlock(&fc_lock);
1524debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	dev_info(&fc->fmc->dev, "Created misc device \"%s\"\n",
1534debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		 fc->misc.name);
1544640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	return 0;
1554640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter
156783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubiniout:
1574640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	kfree(fc->misc.name);
1584640a3f2bff64b808bdedadcddf882aa4606f374Dan Carpenter	kfree(fc);
1594debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return ret;
1604debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
1614debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1624debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_remove(struct fmc_device *fmc)
1634debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
1644debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	struct fc_instance *fc;
1654debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1664debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	list_for_each_entry(fc, &fc_devices, list)
1674debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		if (fc->fmc == fmc)
1684debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini			break;
1694debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	if (fc->fmc != fmc) {
1704debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		dev_err(&fmc->dev, "remove called but not found\n");
1714debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini		return -ENODEV;
1724debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	}
1734debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1744debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	spin_lock(&fc_lock);
1754debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	list_del(&fc->list);
176783c2fb1b8ba2f057a26f881aa452d4aa8e4b3f4Alessandro Rubini	spin_unlock(&fc_lock);
1774debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	misc_deregister(&fc->misc);
1784debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	kfree(fc->misc.name);
1794debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	kfree(fc);
1804debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1814debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return 0;
1824debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
1834debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1844debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1854debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic int fc_init(void)
1864debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
1874debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	int ret;
1884debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1894debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	ret = fmc_driver_register(&fc_drv);
1904debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	return ret;
1914debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
1924debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1934debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinistatic void fc_exit(void)
1944debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini{
1954debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini	fmc_driver_unregister(&fc_drv);
1964debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini}
1974debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
1984debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinimodule_init(fc_init);
1994debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubinimodule_exit(fc_exit);
2004debfe409b6e550032bfef9733e9f6f7c5613617Alessandro Rubini
2014debfe409b6e550032bfef9733e9f6f7c5613617Alessandro RubiniMODULE_LICENSE("GPL");
202