Commit f9065e5e authored by Alessandro Rubini's avatar Alessandro Rubini

initial fmc-core.c and header

parent cb416764
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
......
/*
* 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");
/*
* 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__ */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment