From f9065e5ea9d2324a41c04671a6bd2f1062a6c201 Mon Sep 17 00:00:00 2001
From: Alessandro Rubini <rubini@gnudd.com>
Date: Tue, 17 Jul 2012 01:02:13 +0200
Subject: [PATCH] initial fmc-core.c and header

---
 kernel/Makefile                    |   7 +-
 kernel/fmc-core.c                  | 137 +++++++++++++++++++++++++++++
 kernel/include/linux/fmc.h         |  57 ++++++++++++
 kernel/{spec-wr-nic.c => wr-nic.c} |   0
 4 files changed, 199 insertions(+), 2 deletions(-)
 create mode 100644 kernel/fmc-core.c
 create mode 100644 kernel/include/linux/fmc.h
 rename kernel/{spec-wr-nic.c => wr-nic.c} (100%)

diff --git a/kernel/Makefile b/kernel/Makefile
index 806c602..554bd63 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -1,8 +1,11 @@
 
 LINUX ?= /lib/modules/$(shell uname -r)/build
 
-obj-m = spec.o
-obj-m += spec-wr-nic.o
+ccflags-y = -I$M/include
+
+obj-m = fmc-core.o
+obj-m += spec.o
+obj-m += wr-nic.o
 
 spec-objs = spec-core.o loader-ll.o
 
diff --git a/kernel/fmc-core.c b/kernel/fmc-core.c
new file mode 100644
index 0000000..b8705fb
--- /dev/null
+++ b/kernel/fmc-core.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/fmc.h>
+#include "spec.h"
+
+static int fmc_match(struct device *dev, struct device_driver *drv)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(drv);
+	struct fmc_device *fdev = to_fmc_device(dev);
+	const struct fmc_device_id *t = fdrv->id_table;
+
+	/* Currently, return 1 every time, until we define policies */
+	return 1;
+}
+
+static int fmc_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct fmc_device *fdev = to_fmc_device(dev);
+
+	/* FIXME: The MODALIAS */
+	add_uevent_var(env, "MODALIAS=%s", "fmc");
+	return 0;
+}
+
+static int fmc_probe(struct device *dev)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
+	struct fmc_device *fdev = to_fmc_device(dev);
+
+	return fdrv->probe(fdev);
+}
+
+static int fmc_remove(struct device *dev)
+{
+	struct fmc_driver *fdrv = to_fmc_driver(dev->driver);
+	struct fmc_device *fdev = to_fmc_device(dev);
+
+	return fdrv->remove(fdev);
+}
+
+static void fmc_shutdown(struct device *dev)
+{
+	/* not implemented but mandatory */
+}
+
+static struct bus_type fmc_bus_type = {
+	.name = "fmc",
+	.match = fmc_match,
+	.uevent = fmc_uevent,
+	.probe = fmc_probe,
+	.remove = fmc_remove,
+	.shutdown = fmc_shutdown,
+};
+
+/* Every device must have a release method: provide a default */
+static void __fmc_release(struct device *dev){ }
+
+/* This is needed as parent for our devices and dir in sysfs */
+struct device fmc_bus = {
+	.release = __fmc_release,
+	.init_name = "fmc",
+};
+
+/* Functions for client modules */
+int fmc_driver_register(struct fmc_driver *drv)
+{
+	drv->driver.bus = &fmc_bus_type;
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(fmc_driver_register);
+
+void fmc_driver_unregister(struct fmc_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(fmc_driver_unregister);
+
+int fmc_device_register(struct fmc_device *fdev)
+{
+	device_initialize(&fdev->dev);
+	if (!fdev->dev.release)
+		fdev->dev.release = __fmc_release;
+	if (!fdev->dev.parent)
+		fdev->dev.parent = &fmc_bus;
+	fdev->dev.bus = &fmc_bus_type;
+	{
+		static int i;
+
+		/* FIXME: the name */
+		dev_set_name(&fdev->dev, "fmc-%04x", i++);
+	}
+	return device_add(&fdev->dev);
+}
+EXPORT_SYMBOL(fmc_device_register);
+
+void fmc_device_unregister(struct fmc_device *fdev)
+{
+	device_del(&fdev->dev);
+	put_device(&fdev->dev);
+}
+EXPORT_SYMBOL(fmc_device_unregister);
+
+/* Init and exit are trivial */
+static int fmc_init(void)
+{
+	int err;
+
+	err = device_register(&fmc_bus);
+	if (err)
+		return err;
+	err = bus_register(&fmc_bus_type);
+	if (err)
+		device_unregister(&fmc_bus);
+	return err;
+}
+
+static void fmc_exit(void)
+{
+	bus_unregister(&fmc_bus_type);
+	device_unregister(&fmc_bus);
+}
+
+module_init(fmc_init);
+module_exit(fmc_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/kernel/include/linux/fmc.h b/kernel/include/linux/fmc.h
new file mode 100644
index 0000000..a91cc6c
--- /dev/null
+++ b/kernel/include/linux/fmc.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2012 CERN (www.cern.ch)
+ * Author: Alessandro Rubini <rubini@gnudd.com>
+ *
+ * Released according to the GNU GPL, version 2 or any later version.
+ *
+ * This work is part of the White Rabbit project, a research effort led
+ * by CERN, the European Institute for Nuclear Research.
+ */
+#ifndef __LINUX_FMC_H__
+#define __LINUX_FMC_H__
+
+struct fmc_device;
+struct fmc_driver;
+
+struct fmc_device_id {
+	/* FIXME: the device ID must be defined according to eeprom contents */
+	uint64_t unique_id;
+};
+
+/* The driver is a pretty simple thing */
+struct fmc_driver {
+	struct device_driver driver;
+	int (*probe)(struct fmc_device *);
+	int (*remove)(struct fmc_device *);
+	const struct fmc_device_id *id_table;
+
+};
+#define to_fmc_driver(x) container_of((x), struct fmc_driver, driver)
+
+/* To be carrier-independent, we need to abstract hardware access */
+struct fmc_operations {
+	uint32_t (*readl)(struct fmc_device *d, int offset);
+	uint32_t (*writel)(struct fmc_device *d, int offset, uint32_t value);
+	void (*irq_ack)(struct fmc_device *d);
+};
+
+/* The device reports all information needed to access hw */
+struct fmc_device {
+	struct fmc_device_id id;
+	struct fmc_operations *op;
+	int irq;
+	int eeprom_len;
+	uint8_t *eeprom;
+	char *carrier_name;
+	void *carrier_data;
+	__iomem void *base;
+	struct device dev;
+};
+#define to_fmc_device(x) container_of((x), struct fmc_device, dev)
+
+extern int fmc_driver_register(struct fmc_driver *drv);
+extern void fmc_driver_unregister(struct fmc_driver *drv);
+extern int fmc_device_register(struct fmc_device *tdev);
+extern void fmc_device_unregister(struct fmc_device *tdev);
+
+#endif /* __LINUX_FMC_H__ */
diff --git a/kernel/spec-wr-nic.c b/kernel/wr-nic.c
similarity index 100%
rename from kernel/spec-wr-nic.c
rename to kernel/wr-nic.c
-- 
GitLab